diff --git a/.devcontainer/cortex-m-toolchain.sh b/.devcontainer/cortex-m-toolchain.sh index 10896abd08c00..56be006544f1c 100755 --- a/.devcontainer/cortex-m-toolchain.sh +++ b/.devcontainer/cortex-m-toolchain.sh @@ -14,10 +14,10 @@ echo -e "[cortex-m-toolchain.sh] downloading and installing gcc-arm-non-eabi too cd /workspaces wget -qO gcc-arm-none-eabi.tar.xz \ - https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi.tar.xz + https://developer.arm.com/-/media/Files/downloads/gnu/15.2.rel1/binrel/arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-eabi.tar.xz tar -xJf gcc-arm-none-eabi.tar.xz -ln -s arm-gnu-toolchain-14.2.rel1-x86_64-arm-none-eabi gcc-arm-none-eabi +ln -s arm-gnu-toolchain-15.2.rel1-x86_64-arm-none-eabi gcc-arm-none-eabi rm -f gcc-arm-none-eabi.tar.xz echo -e "[cortex-m-toolchain.sh] update PATH in environment" diff --git a/.gitattributes b/.gitattributes index 38bba729fc191..d226cebf5d81d 100644 --- a/.gitattributes +++ b/.gitattributes @@ -27,4 +27,5 @@ # These should also not be modified by git. tests/basics/string_cr_conversion.py -text tests/basics/string_crlf_conversion.py -text +tests/micropython/test_normalize_newlines.py.exp -text # CIRCUITPY-CHANGE: remove non-CircuitPython tests diff --git a/.github/actions/deps/external/action.yml b/.github/actions/deps/external/action.yml index 10aace1134ec5..81e73f871efc6 100644 --- a/.github/actions/deps/external/action.yml +++ b/.github/actions/deps/external/action.yml @@ -27,7 +27,7 @@ runs: uses: carlosperate/arm-none-eabi-gcc-action@v1 with: # When changing this update what Windows grabs too! - release: '14.2.Rel1' + release: '15.2.Rel1' # espressif - name: Get espressif toolchain @@ -56,6 +56,16 @@ runs: uses: ./.github/actions/deps/python with: action: ${{ inputs.action }} + - name: Set ESP-IDF constraints path + if: inputs.port == 'espressif' + run: python3 -u tools/ci_set_idf_constraint.py + shell: bash + - name: Install python dependencies - run: pip install -r requirements-dev.txt + run: | + if [ -n "${IDF_CONSTRAINT_FILE:-}" ] && [ -f "$IDF_CONSTRAINT_FILE" ]; then + pip install -c "$IDF_CONSTRAINT_FILE" -r requirements-dev.txt + else + pip install -r requirements-dev.txt + fi shell: bash diff --git a/.github/actions/deps/ports/broadcom/action.yml b/.github/actions/deps/ports/broadcom/action.yml index 9ad0e361bda1b..fc375aa10f74f 100644 --- a/.github/actions/deps/ports/broadcom/action.yml +++ b/.github/actions/deps/ports/broadcom/action.yml @@ -5,8 +5,8 @@ runs: steps: - name: Get broadcom toolchain run: | - wget --no-verbose https://adafruit-circuit-python.s3.amazonaws.com/arm-gnu-toolchain-14.2.rel1-x86_64-aarch64-none-elf.tar.xz - sudo tar -C /usr --strip-components=1 -xaf arm-gnu-toolchain-14.2.rel1-x86_64-aarch64-none-elf.tar.xz + wget --no-verbose https://adafruit-circuit-python.s3.amazonaws.com/arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-elf.tar.xz + sudo tar -C /usr --strip-components=1 -xaf arm-gnu-toolchain-15.2.rel1-x86_64-aarch64-none-elf.tar.xz sudo apt-get update sudo apt-get install -y mtools shell: bash diff --git a/.github/actions/deps/ports/espressif/action.yml b/.github/actions/deps/ports/espressif/action.yml index 25965eb7ef040..321a0fb2b3023 100644 --- a/.github/actions/deps/ports/espressif/action.yml +++ b/.github/actions/deps/ports/espressif/action.yml @@ -19,7 +19,7 @@ runs: shell: bash - name: Cache IDF submodules - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: | .git/modules/ports/espressif/esp-idf @@ -27,7 +27,7 @@ runs: key: submodules-idf-${{ steps.idf-commit.outputs.commit }} - name: Cache IDF tools - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ${{ env.IDF_TOOLS_PATH }} key: ${{ runner.os }}-${{ env.pythonLocation }}-tools-idf-${{ steps.idf-commit.outputs.commit }} diff --git a/.github/actions/deps/ports/nordic/action.yml b/.github/actions/deps/ports/nordic/action.yml index 5f08b17ca7a68..96754be9507c5 100644 --- a/.github/actions/deps/ports/nordic/action.yml +++ b/.github/actions/deps/ports/nordic/action.yml @@ -5,7 +5,7 @@ runs: steps: - name: Get nrfutil 7+ run: | - wget https://developer.nordicsemi.com/.pc-tools/nrfutil/x64-linux/nrfutil + wget https://files.nordicsemi.com/artifactory/swtools/external/nrfutil/executables/x86_64-unknown-linux-gnu/nrfutil chmod +x nrfutil ./nrfutil install nrf5sdk-tools mkdir -p $HOME/.local/bin diff --git a/.github/actions/deps/ports/zephyr-cp/action.yml b/.github/actions/deps/ports/zephyr-cp/action.yml index cfa1177598e4d..6c538e3fa573e 100644 --- a/.github/actions/deps/ports/zephyr-cp/action.yml +++ b/.github/actions/deps/ports/zephyr-cp/action.yml @@ -3,6 +3,19 @@ name: Fetch Zephyr port deps runs: using: composite steps: + - name: Get Linux build dependencies + if: runner.os == 'Linux' + run: | + echo "--- cpu model ---" + grep "model name" /proc/cpuinfo | head -1 + sudo dpkg --add-architecture i386 + sudo apt-get update + sudo apt-get install -y libusb-1.0-0-dev libudev-dev pkg-config mtools + # We have to hold python3 so the following install works. See https://github.com/actions/runner-images/issues/13803 + sudo apt-mark hold python3 + sudo apt-get install -y libsdl2-dev:i386 libsdl2-image-dev:i386 + echo "PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" >> $GITHUB_ENV + shell: bash - name: Setup Zephyr project uses: zephyrproject-rtos/action-zephyr-setup@v1 with: diff --git a/.github/actions/deps/python/action.yml b/.github/actions/deps/python/action.yml index da59b87b17a29..bc8b578c1473d 100644 --- a/.github/actions/deps/python/action.yml +++ b/.github/actions/deps/python/action.yml @@ -16,7 +16,7 @@ runs: - name: Cache python dependencies id: cache-python-deps if: inputs.action == 'cache' - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: .cp_tools key: ${{ runner.os }}-${{ env.pythonLocation }}-tools-cp-${{ hashFiles('requirements-dev.txt') }} @@ -24,7 +24,7 @@ runs: - name: Restore python dependencies id: restore-python-deps if: inputs.action == 'restore' - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: .cp_tools key: ${{ runner.os }}-${{ env.pythonLocation }}-tools-cp-${{ hashFiles('requirements-dev.txt') }} diff --git a/.github/actions/deps/submodules/action.yml b/.github/actions/deps/submodules/action.yml index eed83af41f4eb..5ec57b594c0da 100644 --- a/.github/actions/deps/submodules/action.yml +++ b/.github/actions/deps/submodules/action.yml @@ -48,7 +48,7 @@ runs: - name: Cache submodules if: ${{ inputs.action == 'cache' }} - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: ".git/modules/\n${{ join(fromJSON(steps.create-submodule-status.outputs.submodules), '\n') }}" key: submodules-common-${{ hashFiles('submodule_status') }} @@ -56,7 +56,7 @@ runs: - name: Restore submodules if: ${{ inputs.action == 'restore' }} - uses: actions/cache/restore@v4 + uses: actions/cache/restore@v5 with: path: ".git/modules/\n${{ join(fromJSON(steps.create-submodule-status.outputs.submodules), '\n') }}" key: submodules-common-${{ hashFiles('submodule_status') }} diff --git a/.github/actions/mpy_cross/action.yml b/.github/actions/mpy_cross/action.yml index 8839f790915cf..469b8b0763e95 100644 --- a/.github/actions/mpy_cross/action.yml +++ b/.github/actions/mpy_cross/action.yml @@ -16,7 +16,7 @@ runs: id: download-mpy-cross if: inputs.download == 'true' continue-on-error: true - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: mpy-cross path: mpy-cross/build @@ -36,7 +36,7 @@ runs: - name: Upload mpy-cross if: inputs.download == 'false' || steps.download-mpy-cross.outcome == 'failure' continue-on-error: true - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: mpy-cross path: mpy-cross/build/mpy-cross diff --git a/.github/workflows/build-board-custom.yml b/.github/workflows/build-board-custom.yml index bf18d7d725956..c17e1b13f9791 100644 --- a/.github/workflows/build-board-custom.yml +++ b/.github/workflows/build-board-custom.yml @@ -70,7 +70,7 @@ jobs: run: | > custom-build && git add custom-build - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Board to port @@ -124,7 +124,7 @@ jobs: run: make -j4 $FLAGS BOARD="$BOARD" DEBUG=$DEBUG TRANSLATION="$TRANSLATION" working-directory: ports/${{ steps.board-to-port.outputs.port }} - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ${{ inputs.board }}-${{ inputs.language }}-${{ inputs.version }}${{ inputs.flags != '' && '-custom' || '' }}${{ inputs.debug && '-debug' || '' }} path: ports/${{ steps.board-to-port.outputs.port }}/build-${{ inputs.board }}/firmware.* diff --git a/.github/workflows/build-boards.yml b/.github/workflows/build-boards.yml index 7e5156d40114a..6fbc5b6a08f15 100644 --- a/.github/workflows/build-boards.yml +++ b/.github/workflows/build-boards.yml @@ -29,7 +29,7 @@ jobs: board: ${{ fromJSON(inputs.boards) }} steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false @@ -37,7 +37,7 @@ jobs: persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x @@ -87,7 +87,7 @@ jobs: HEAD_COMMIT_MESSAGE: ${{ github.event.head_commit.message }} - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ${{ matrix.board }} path: bin/${{ matrix.board }} diff --git a/.github/workflows/build-mpy-cross.yml b/.github/workflows/build-mpy-cross.yml index 831ad3082275b..9e5c1cdfc4a8d 100644 --- a/.github/workflows/build-mpy-cross.yml +++ b/.github/workflows/build-mpy-cross.yml @@ -28,14 +28,14 @@ jobs: OS_static-raspbian: linux-raspbian steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules @@ -66,7 +66,7 @@ jobs: echo >> $GITHUB_ENV "OS=$OS" - name: Upload artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: mpy-cross.${{ env.EX }} path: mpy-cross/build-${{ matrix.mpy-cross }}/mpy-cross.${{ env.EX }} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0f837d793bddc..6a2329a411340 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,14 +28,14 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Duplicate USB VID/PID check @@ -107,21 +107,21 @@ jobs: cp-version: ${{ needs.scheduler.outputs.cp-version }} mpy-cross-mac: - runs-on: macos-13 + runs-on: macos-latest needs: scheduler if: needs.scheduler.outputs.ports != '{}' env: CP_VERSION: ${{ needs.scheduler.outputs.cp-version }} steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules @@ -131,33 +131,18 @@ jobs: gcc --version python3 --version msgfmt --version - - name: Build mpy-cross - run: make -C mpy-cross -j4 - - uses: actions/upload-artifact@v4 - with: - name: mpy-cross-macos-x64 - path: mpy-cross/build/mpy-cross - name: Build mpy-cross (arm64) run: make -C mpy-cross -j4 -f Makefile.m1 V=2 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: mpy-cross-macos-arm64 path: mpy-cross/build-arm64/mpy-cross-arm64 - - name: Make universal binary - run: lipo -create -output mpy-cross-macos-universal mpy-cross/build/mpy-cross mpy-cross/build-arm64/mpy-cross-arm64 - - name: Upload artifact - uses: actions/upload-artifact@v4 - with: - name: mpy-cross-macos-universal - path: mpy-cross-macos-universal - name: Upload to S3 if: >- (github.event_name == 'push' && github.ref == 'refs/heads/main' && github.repository_owner == 'adafruit') || (github.event_name == 'release' && (github.event.action == 'published' || github.event.action == 'rerequested')) run: | - [ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross-macos-universal s3://adafruit-circuit-python/bin/mpy-cross/macos/mpy-cross-macos-"${CP_VERSION}"-universal --no-progress --region us-east-1 [ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/build-arm64/mpy-cross-arm64 s3://adafruit-circuit-python/bin/mpy-cross/macos/mpy-cross-macos-"${CP_VERSION}"-arm64 --no-progress --region us-east-1 - [ -z "$AWS_ACCESS_KEY_ID" ] || aws s3 cp mpy-cross/build/mpy-cross s3://adafruit-circuit-python/bin/mpy-cross/macos/mpy-cross-macos-"${CP_VERSION}"-x64 --no-progress --region us-east-1 env: AWS_PAGER: '' AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} @@ -171,14 +156,14 @@ jobs: CP_VERSION: ${{ needs.scheduler.outputs.cp-version }} steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules @@ -192,20 +177,20 @@ jobs: pip install -r requirements-doc.txt - name: Build and Validate Stubs run: make check-stubs -j4 - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: stubs path: circuitpython-stubs/dist/* - name: Test Documentation Build (HTML) run: sphinx-build -E -W -b html -D version="$CP_VERSION" -D release="$CP_VERSION" . _build/html - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: docs-html path: _build/html - name: Test Documentation Build (LaTeX/PDF) run: | make latexpdf - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@v7 with: name: docs-latexpdf path: _build/latex @@ -261,7 +246,7 @@ jobs: python3 -c "import sys, locale; print(sys.getdefaultencoding(), locale.getpreferredencoding(False))" - name: Install dependencies run: | - wget --no-verbose -O gcc-arm.zip https://developer.arm.com/-/media/Files/downloads/gnu/14.2.rel1/binrel/arm-gnu-toolchain-14.2.rel1-mingw-w64-i686-arm-none-eabi.zip + wget --no-verbose -O gcc-arm.zip https://developer.arm.com/-/media/Files/downloads/gnu/15.2.rel1/binrel/arm-gnu-toolchain-15.2.rel1-mingw-w64-i686-arm-none-eabi.zip unzip -q -d /tmp/arm-gnu-toolchain gcc-arm.zip tar -C /tmp/arm-gnu-toolchain -cf - . | tar -C /usr/local -xf - # We could use a venv instead, but that requires entering the venv on each run step @@ -275,7 +260,7 @@ jobs: which python; python --version; python -c "import cascadetoml" which python3; python3 --version; python3 -c "import cascadetoml" - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false @@ -310,13 +295,13 @@ jobs: CP_VERSION: ${{ needs.scheduler.outputs.cp-version }} steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - - uses: actions/setup-python@v5 + - uses: actions/setup-python@v6 with: python-version: '3.13' - name: Set up Zephyr diff --git a/.github/workflows/bundle_cron.yml b/.github/workflows/bundle_cron.yml new file mode 100644 index 0000000000000..eefffaaa5b9ab --- /dev/null +++ b/.github/workflows/bundle_cron.yml @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: 2019 Michael Schroeder +# +# SPDX-License-Identifier: MIT + +name: Update Bundles + +on: + schedule: + - cron: 0 5 * * * + workflow_dispatch: + +jobs: + check-repo-owner: + # This job is so the entire workflow will end successfully and give some + # output to explain why it hasn't run on a non-Adafruit fork. + runs-on: ubuntu-latest + steps: + - name: repository + env: + OWNER_IS_ADAFRUIT: ${{ startswith(github.repository, 'adafruit/') }} + run: | + echo "This workflow will only run if Adafruit is the repository owner." + echo "Repository owner is Adafruit: $OWNER_IS_ADAFRUIT" + update-bundles: + runs-on: ubuntu-latest + # Only run the build on Adafruit's repository. Forks won't have the secrets. + # It's necessary to do this here, since 'schedule' events cannot (currently) + # be limited (they run on all forks' default branches). + if: startswith(github.repository, 'adafruit/') + steps: + - name: Set up Python 3.12 + uses: actions/setup-python@v6 + with: + python-version: 3.12 + - name: Load contributor cache + uses: actions/cache@v5 + with: + key: "contributor-cache" + path: "contributors.json" + - name: Versions + run: | + python3 --version + - uses: actions/checkout@v6 + with: + repository: 'adafruit/adabot' + submodules: true + - name: Install deps + run: | + pip install -r requirements.txt + - name: Run adabot.circuitpython_bundle + env: + ADABOT_EMAIL: ${{ secrets.ADABOT_EMAIL }} + ADABOT_GITHUB_USER: ${{ secrets.ADABOT_GITHUB_USER }} + ADABOT_GITHUB_ACCESS_TOKEN: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} + BIGQUERY_PRIVATE_KEY: ${{ secrets.BIGQUERY_PRIVATE_KEY }} + BIGQUERY_CLIENT_EMAIL: ${{ secrets.BIGQUERY_CLIENT_EMAIL }} + run: | + python3 -u -m adabot.circuitpython_bundle diff --git a/.github/workflows/create-website-pr.yml b/.github/workflows/create-website-pr.yml index 32c1792fa6c74..559a41e67e793 100644 --- a/.github/workflows/create-website-pr.yml +++ b/.github/workflows/create-website-pr.yml @@ -17,14 +17,14 @@ jobs: env: GITHUB_CONTEXT: ${{ toJson(github) }} - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules diff --git a/.github/workflows/learn_cron.yml b/.github/workflows/learn_cron.yml new file mode 100644 index 0000000000000..135089bfd5ef2 --- /dev/null +++ b/.github/workflows/learn_cron.yml @@ -0,0 +1,41 @@ +# SPDX-FileCopyrightText: 2021 Jeff Epler for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +name: Tag Learning System Guides + +on: + schedule: + - cron: 0 5 * * * + +jobs: + check-repo-owner: + # This job is so the entire workflow will end successfully and give some + # output to explain why it hasn't run on a non-Adafruit fork. + runs-on: ubuntu-latest + if: ${{ (github.repository_owner != 'adafruit') }} + steps: + - run: | + echo "This workflow is only intended to run in the adafruit fork" + + update-learn: + runs-on: ubuntu-latest + # Only run the build if the access token has been configured. This will be + # the case on Adafruit's repository. It's necessary to do this here, since + # 'schedule' events cannot (currently) be limited (they run on all forks' + # default branches). + if: ${{ (github.repository_owner == 'adafruit') }} + steps: + - uses: actions/checkout@v6 + with: + repository: ${{ github.repository_owner }}/Adafruit_Learning_System_Guides + token: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} + - name: Tag a release + env: + ADABOT_EMAIL: ${{ secrets.ADABOT_EMAIL }} + run: | + git config --global user.name adabot + git config --global user.email "$ADABOT_EMAIL" + TAG_NAME=`date +%Y%m%d` + git tag $TAG_NAME + git push origin $TAG_NAME diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 778270dc08c8f..21ae984e46870 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -17,14 +17,14 @@ jobs: runs-on: ubuntu-24.04 steps: - name: Set up repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: submodules: false show-progress: false fetch-depth: 1 persist-credentials: false - name: Set up python - uses: actions/setup-python@v5 + uses: actions/setup-python@v6 with: python-version: 3.x - name: Set up submodules @@ -42,7 +42,7 @@ jobs: run: git diff > ~/pre-commit.patch - name: Upload patch if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: patch path: ~/pre-commit.patch diff --git a/.github/workflows/reports_cron.yml b/.github/workflows/reports_cron.yml new file mode 100644 index 0000000000000..476ead95d256a --- /dev/null +++ b/.github/workflows/reports_cron.yml @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: 2019 Michael Schroeder +# +# SPDX-License-Identifier: MIT + +name: Run Daily Reports + +on: + schedule: + # The actor (github.actor) that runs the cron job may be the user who created the cron job + # initially. It does not appear to be settable via a secret or environment variable. + - cron: 15 5 * * * + workflow_dispatch: + + +jobs: + check-repo-owner: + # This job is so the entire workflow will end successfully and give some + # output to explain why it hasn't run on a non-Adafruit fork. + runs-on: ubuntu-latest + steps: + - name: repository + env: + OWNER_IS_ADAFRUIT: ${{ startswith(github.repository, 'adafruit/') }} + run: | + echo "This workflow will only run if Adafruit is the repository owner." + echo "Repository owner is Adafruit: $OWNER_IS_ADAFRUIT" + run-reports: + runs-on: ubuntu-latest + # Only run the build on Adafruit's repository. Forks won't have the secrets. + # It's necessary to do this here, since 'schedule' events cannot (currently) + # be limited (they run on all forks' default branches). + if: startswith(github.repository, 'adafruit/') + env: + ADABOT_GITHUB_USER: ${{ secrets.ADABOT_GITHUB_USER }} + ADABOT_GITHUB_ACCESS_TOKEN: ${{ secrets.ADABOT_GITHUB_ACCESS_TOKEN }} + RTD_TOKEN: ${{ secrets.RTD_TOKEN }} + BIGQUERY_PRIVATE_KEY: ${{ secrets.BIGQUERY_PRIVATE_KEY }} + BIGQUERY_CLIENT_EMAIL: ${{ secrets.BIGQUERY_CLIENT_EMAIL }} + steps: + - name: Set up Python 3.11 + uses: actions/setup-python@v6 + with: + python-version: 3.11 + - name: Versions + run: | + python3 --version + - uses: actions/checkout@v6 + with: + repository: 'adafruit/adabot' + submodules: true + - name: Install deps + run: | + pip install -r requirements.txt + - name: Make Directory For Report Files + run: mkdir -p bin/adabot + - name: Set Date Variable + id: today + run: | + echo date=$( + date +%Y%m%d + ) >> $GITHUB_OUTPUT + - name: Run adabot.circuitpython_libraries + env: + # LIB_CHECK_CP_FILE is for circuitpython_libraries.py output + LIB_CHECK_CP_FILE: bin/adabot/circuitpython_library_report_${{ steps.today.outputs.date }}.txt + run: | + python3 -u -m adabot.circuitpython_libraries -o $LIB_CHECK_CP_FILE + continue-on-error: true + - name: Run adabot.circuitpython_library_download_stats + env: + # LIB_DL_STATS_FILE is for future Bundle and PyPi download stats script + LIB_DL_STATS_FILE: bin/adabot/library_download_stats_${{ steps.today.outputs.date }}.txt + run: | + python3 -u -m adabot.circuitpython_library_download_stats -o $LIB_DL_STATS_FILE + continue-on-error: true + - name: Run adabot.arduino_libraries + env: + # LIB_CHECK_ARD_FILE is for arduino_libraries.py output + LIB_CHECK_ARD_FILE: bin/adabot/arduino_library_report_${{ steps.today.outputs.date }}.txt + run: | + python3 -u -m adabot.arduino_libraries -o $LIB_CHECK_ARD_FILE + continue-on-error: true + - name: Check For Files + run: | + ls bin/adabot + - name: Upload Reports To AWS S3 + if: ${{ github.event_name != 'workflow_dispatch' }} + env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + run: "[ -z \"$AWS_ACCESS_KEY_ID\" ] || aws s3 cp bin/adabot/ s3://adafruit-circuit-python/adabot/bin/reports/ --recursive --no-progress --region us-east-1" diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 80bfc72bd9812..83154bbbd2b66 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -23,36 +23,36 @@ jobs: TEST_native: --emit native TEST_native_mpy: --via-mpy --emit native -d basics float micropython steps: - - name: Set up repository - uses: actions/checkout@v4 - with: - submodules: false - show-progress: false - fetch-depth: 1 - - name: Set up python - uses: actions/setup-python@v5 - with: - python-version: 3.12 - - name: Set up submodules - uses: ./.github/actions/deps/submodules - with: - target: tests - - name: Set up external - if: matrix.test == 'all' - uses: ./.github/actions/deps/external - - name: Set up mpy-cross - uses: ./.github/actions/mpy_cross - with: - cp-version: ${{ inputs.cp-version }} - - name: Build unix port - run: make -C ports/unix VARIANT=coverage -j4 - - name: Run tests - run: ./run-tests.py -j4 ${{ env[format('TEST_{0}', matrix.test)] }} - working-directory: tests - - name: Print failure info - run: ./run-tests.py -j4 --print-failures - if: failure() - working-directory: tests + - name: Set up repository + uses: actions/checkout@v6 + with: + submodules: false + show-progress: false + fetch-depth: 1 + - name: Set up python + uses: actions/setup-python@v6 + with: + python-version: 3.12 + - name: Set up submodules + uses: ./.github/actions/deps/submodules + with: + target: tests + - name: Set up external + if: matrix.test == 'all' + uses: ./.github/actions/deps/external + - name: Set up mpy-cross + uses: ./.github/actions/mpy_cross + with: + cp-version: ${{ inputs.cp-version }} + - name: Build unix port + run: make -C ports/unix VARIANT=coverage -j4 + - name: Run tests + run: ./run-tests.py -j4 ${{ env[format('TEST_{0}', matrix.test)] }} + working-directory: tests + - name: Print failure info + run: ./run-tests.py -j4 --print-failures + if: failure() + working-directory: tests # Not working after MicroPython v1.23 merge. # - name: Build native modules # if: matrix.test == 'all' @@ -66,3 +66,39 @@ jobs: # if: matrix.test == 'all' # run: ./run-natmodtests.py extmod/{heapq*,random*,re*}.py # working-directory: tests + + zephyr: + runs-on: ubuntu-24.04 + strategy: + fail-fast: false + env: + CP_VERSION: ${{ inputs.cp-version }} + steps: + - name: Set up repository + uses: actions/checkout@v6 + with: + submodules: false + show-progress: false + fetch-depth: 1 + - name: Set up python + uses: actions/setup-python@v6 + with: + python-version: 3.13 + - name: Set up Zephyr + uses: ./.github/actions/deps/ports/zephyr-cp + - name: Set up submodules + id: set-up-submodules + uses: ./.github/actions/deps/submodules + with: + target: zephyr-cp + - name: Set up external + uses: ./.github/actions/deps/external + - name: Build native sim target + run: make -C ports/zephyr-cp -j2 BOARD=native_native_sim + - name: Build bsim + run: make -j 2 everything + working-directory: ports/zephyr-cp/tools/bsim + - name: Build native_nrf5340bsim + run: make -C ports/zephyr-cp -j2 BOARD=native_nrf5340bsim + - name: Run Zephyr tests + run: make -C ports/zephyr-cp test diff --git a/.gitignore b/.gitignore index f996911d8d746..ca6872387dd27 100644 --- a/.gitignore +++ b/.gitignore @@ -55,6 +55,9 @@ __pycache__/ ###################### GNUmakefile user.props +user_pre_mpconfigport.mk +user_post_mpconfigport.mk +user_post_circuitpy_defns.mk # Sphinx output ############### @@ -107,3 +110,10 @@ TAGS # windsurf rules .windsurfrules + +# git-review-web outputs +.review + +# Zephyr trace files +**/channel0_0 +**/*.perfetto-trace diff --git a/.gitmodules b/.gitmodules index b1ec0f9faee7f..a7b67b8d86051 100644 --- a/.gitmodules +++ b/.gitmodules @@ -143,7 +143,7 @@ [submodule "ports/espressif/esp-idf"] path = ports/espressif/esp-idf url = https://github.com/adafruit/esp-idf.git - branch = circuitpython-v5.4.1 + branch = circuitpython-v5.5.1 [submodule "ports/espressif/esp-protocols"] path = ports/espressif/esp-protocols url = https://github.com/adafruit/esp-protocols.git @@ -172,7 +172,7 @@ url = https://github.com/adafruit/Adafruit_CircuitPython_SimpleMath [submodule "ports/raspberrypi/sdk"] path = ports/raspberrypi/sdk - url = https://github.com/adafruit/pico-sdk.git + url = https://github.com/raspberrypi/pico-sdk.git branch = force_inline_critical_section_2.1.1 [submodule "data/nvm.toml"] path = data/nvm.toml @@ -415,3 +415,9 @@ [submodule "ports/espressif/microros-lib"] path = ports/espressif/microros-lib url = https://github.com/hierophect/microros-lib.git +[submodule "frozen/Adafruit_CircuitPython_OPT4048"] + path = frozen/Adafruit_CircuitPython_OPT4048 + url = https://github.com/adafruit/Adafruit_CircuitPython_OPT4048.git +[submodule "frozen/CircuitPython_edupico2_paj7620"] + path = frozen/CircuitPython_edupico2_paj7620 + url = https://github.com/CytronTechnologies/CircuitPython_edupico2_paj7620.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f7e7956498d68..c39faf99f00d2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 + rev: v6.0.0 hooks: - id: check-yaml - id: end-of-file-fixer @@ -28,7 +28,7 @@ repos: lib/mbedtls_errors/generate_errors.diff ) - repo: https://github.com/codespell-project/codespell - rev: v2.2.4 + rev: v2.4.1 hooks: - id: codespell args: [-w] @@ -55,7 +55,7 @@ repos: language: python - repo: https://github.com/astral-sh/ruff-pre-commit # Ruff version. - rev: v0.9.4 + rev: v0.15.7 hooks: # Run the linter. - id: ruff @@ -63,6 +63,6 @@ repos: # Run the formatter. - id: ruff-format - repo: https://github.com/tox-dev/pyproject-fmt - rev: "v2.5.0" + rev: "v2.21.0" hooks: - id: pyproject-fmt diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000000000..e7d2fdbbe8caf --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,4 @@ +- Capture CircuitPython output by finding the matching device in `/dev/serial/by-id` +- You can mount the CIRCUITPY drive by doing `udisksctl mount -b /dev/disk/by-label/CIRCUITPY` and access it via `/run/media//CIRCUITPY`. +- `circup` is a command line tool to install libraries and examples to CIRCUITPY. +- When connecting to serial devices on Linux use /dev/serial/by-id. These will be more stable than /dev/ttyACM*. diff --git a/BUILDING.md b/BUILDING.md index 34cd544d73658..6778965bed099 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -64,6 +64,52 @@ If you aren't sure what boards exist, have a peek in the boards subdirectory of If you have a fast computer with many cores, consider adding `-j` to your build flags, such as `-j17` on a 6-core 12-thread machine. +## Configuration + +Ports and boards are preconfigured, thus make knows how to build a specific +board. Power users can change the configuration of a specific board or port, +either by passing compile-time options to make, or by creating appropriate +make include files. + +The configuration system is hierarchical. A higher level will typically only +set an option that a lower level hasn't configured: + +* board configuration: `mpconfigport.mk` +* pre-port user configuration: `user_pre_mpconfigport.mk` +* port configuration: `mpconfigport.mk` +* post-port user configuration: `user_post_mpconfigport.mk` +* global configuration: `py/circuitpython_mpconfig.mk` + +The board configuration is within the board-directory, e.g. +`ports/raspberrypi/boards/raspberry_pi_pico/`, the port configuration is +in the port-directory, e.g. `ports/raspberrypi/`. + +Editing these configuration files is the way to go if you want to change +the default behavior and ultimately create a pull-request. Otherwise, +changes should go into one of the user configuration files. + +User specific configurations are optional and should be maintained out of +tree. Passing `-I directory` tells make where to search for the additional +configuration files. E.g. to speed up boots by removing the wait-time for +the save-mode button press, you would: + +* create a directory: `mkdir -p ~/my_cp_config` +* create the config file: `echo 'CIRCUITPY_SKIP_SAFE_MODE_WAIT=0' > ~/my_cp_config/user_pre_mpconfigport.mk` +* run make with: `make -I ~/my_cp_config BOARD=raspberry_pi_pico` + +Besides the `user*mpconfigport.mk` files, there is another optional file +named `user_post_circuitpy_defns.mk`. This file is included at the end +and can be used to tweak compiler-definitions that are not covered by +one of the compile time options `CIRCUITPY_*`. + +Example: to create a build for the Pico2-W with an integrated saves-partition, +you would create a `user_post_circuitpy_defns.mk` with the following content: + + $(info ===> processing user_post_circuitpy_defns.mk) + ifeq (${BOARD},raspberry_pi_pico2_w) + CFLAGS += -DCIRCUITPY_SAVES_PARTITION_SIZE=1048576 + endif + ## Testing If you are working on changes to the core language, you might find it useful to run the test suite. diff --git a/LICENSE_MicroPython b/LICENSE_MicroPython index 469ae1927396d..1fad9b4134e01 100644 --- a/LICENSE_MicroPython +++ b/LICENSE_MicroPython @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-2024 Damien P. George +Copyright (c) 2013-2025 Damien P. George Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile index f2fc070a6331d..175a1b6a3025f 100644 --- a/Makefile +++ b/Makefile @@ -299,6 +299,9 @@ update-frozen-libraries: one-of-each: samd21 litex mimxrt10xx nordic stm +analog: + $(MAKE) -C ports/analog/ BOARD=apard32690 + samd21: $(MAKE) -C ports/atmel-samd BOARD=trinket_m0 @@ -372,5 +375,6 @@ coverage-fresh: make -j -C ports/unix VARIANT=coverage .PHONY: run-tests +# If TESTS="abc.py def.py" is specified as an arg, run only those tests. Otherwise, run all tests. run-tests: - cd tests; MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py + cd tests; MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py $(TESTS) diff --git a/README.rst b/README.rst index 80aa1b2aee282..89c7d9229d8ed 100644 --- a/README.rst +++ b/README.rst @@ -226,7 +226,7 @@ Ports Ports include the code unique to a microcontroller line. -The following ports are available: ``atmel-samd``, ``cxd56``, ``espressif``, ``litex``, ``mimxrt10xx``, ``nordic``, ``raspberrypi``, ``renode``, ``silabs`` (``efr32``), ``stm``, ``unix``. +The following ports are available: ``atmel-samd``, ``cxd56``, ``espressif``, ``litex``, ``mimxrt10xx``, ``nordic``, ``raspberrypi``, ``renode``, ``silabs`` (``efr32``), ``stm``, ``unix``, and ``zephyr-cp``. However, not all ports are fully functional. Some have limited functionality and known serious bugs. For details, refer to the **Port status** section in the `latest release `__ notes. diff --git a/conf.py b/conf.py index 34436c09020e3..e2a8cbbd82faa 100644 --- a/conf.py +++ b/conf.py @@ -192,7 +192,12 @@ def autoapi_prepare_jinja_env(jinja_env): # Port READMEs in various formats "ports/*/README*", ] -exclude_patterns = ["docs/autoapi/templates/**", "docs/README.md"] +exclude_patterns = [ + "docs/autoapi/templates/**", + "docs/README.md", + "AGENTS.md", + "**/AGENTS.md", +] # The reST default role (used for this markup: `text`) to use for all # documents. diff --git a/devices/ble_hci/common-hal/_bleio/Adapter.c b/devices/ble_hci/common-hal/_bleio/Adapter.c index fac7eb9f5fddf..03ec7a5588f11 100644 --- a/devices/ble_hci/common-hal/_bleio/Adapter.c +++ b/devices/ble_hci/common-hal/_bleio/Adapter.c @@ -29,7 +29,7 @@ #include "shared-bindings/_bleio/ScanEntry.h" #include "shared-bindings/time/__init__.h" -#if CIRCUITPY_OS_GETENV +#if CIRCUITPY_SETTINGS_TOML #include "shared-bindings/os/__init__.h" #endif @@ -261,7 +261,7 @@ static void _adapter_set_name(bleio_adapter_obj_t *self, mp_obj_str_t *name_obj) static void bleio_adapter_hci_init(bleio_adapter_obj_t *self) { mp_int_t name_len = 0; - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML mp_obj_t name = common_hal_os_getenv("CIRCUITPY_BLE_NAME", mp_const_none); if (name != mp_const_none) { mp_arg_validate_type_string(name, MP_QSTR_CIRCUITPY_BLE_NAME); diff --git a/devices/ble_hci/common-hal/_bleio/Characteristic.c b/devices/ble_hci/common-hal/_bleio/Characteristic.c index a33b8ff478472..49b66d38e7256 100644 --- a/devices/ble_hci/common-hal/_bleio/Characteristic.c +++ b/devices/ble_hci/common-hal/_bleio/Characteristic.c @@ -180,7 +180,7 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, } const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); - common_hal_bleio_check_connected(conn_handle); + bleio_check_connected(conn_handle); uint16_t cccd_value = (notify ? CCCD_NOTIFY : 0) | diff --git a/devices/ble_hci/common-hal/_bleio/Connection.h b/devices/ble_hci/common-hal/_bleio/Connection.h index 04edb104ddcb2..02a000501bfde 100644 --- a/devices/ble_hci/common-hal/_bleio/Connection.h +++ b/devices/ble_hci/common-hal/_bleio/Connection.h @@ -61,6 +61,7 @@ typedef struct { uint8_t disconnect_reason; } bleio_connection_obj_t; +void bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/devices/ble_hci/common-hal/_bleio/PacketBuffer.h b/devices/ble_hci/common-hal/_bleio/PacketBuffer.h index 563f365d21b01..4e001339fe323 100644 --- a/devices/ble_hci/common-hal/_bleio/PacketBuffer.h +++ b/devices/ble_hci/common-hal/_bleio/PacketBuffer.h @@ -28,4 +28,7 @@ typedef struct { bool packet_queued; } bleio_packet_buffer_obj_t; +// Unused, but needed for _common_hal_bleio_packet_buffer_construct() +typedef void *ble_event_handler_t; + void bleio_packet_buffer_update(bleio_packet_buffer_obj_t *self, mp_buffer_info_t *bufinfo); diff --git a/devices/ble_hci/common-hal/_bleio/__init__.c b/devices/ble_hci/common-hal/_bleio/__init__.c index 6376f6f10c8c9..f9fdbc50f6489 100644 --- a/devices/ble_hci/common-hal/_bleio/__init__.c +++ b/devices/ble_hci/common-hal/_bleio/__init__.c @@ -84,7 +84,7 @@ bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void) { return &common_hal_bleio_adapter_obj; } -void common_hal_bleio_check_connected(uint16_t conn_handle) { +void bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLE_CONN_HANDLE_INVALID) { mp_raise_ConnectionError(MP_ERROR_TEXT("Not connected")); } diff --git a/docs/environment.rst b/docs/environment.rst index b97820b3caf74..36404eb81f928 100644 --- a/docs/environment.rst +++ b/docs/environment.rst @@ -187,7 +187,7 @@ Allows the entry of a display scaling factor used during the terminalio console The entered scaling factor only affects the terminalio console and has no impact on the UART, Web Workflow, BLE Workflow, etc consoles. -This feature is not enabled on boards that the CIRCUITPY_OS_GETENV (os CIRCUIPTY_FULL_BUILD) +This feature is not enabled on boards that the CIRCUITPY_SETTINGS_TOML (or CIRCUITPY_FULL_BUILD) flag has been set to 0. Currently this is primarily boards with limited flash including some of the Atmel_samd boards based on the SAMD21/M0 microprocessor. @@ -197,7 +197,7 @@ Specifies a custom font file path to use for the terminalio console instead of t ``/fonts/terminal.lvfontbin``. This allows users to create and use custom fonts for the CircuitPython console. -This feature requires both CIRCUITPY_OS_GETENV and CIRCUITPY_LVFONTIO to be enabled. +This feature requires both CIRCUITPY_SETTINGS_TOML and CIRCUITPY_LVFONTIO to be enabled. Example: diff --git a/docs/library/collections.rst b/docs/library/collections.rst index b006a97dcaf22..c3ec763d20e1b 100644 --- a/docs/library/collections.rst +++ b/docs/library/collections.rst @@ -109,3 +109,19 @@ Classes a 2 w 5 b 3 + + .. method:: OrderedDict.popitem() + + Remove and return a (key, value) pair from the dictionary. + Pairs are returned in LIFO order. + + .. admonition:: Difference to CPython + :class: attention + + ``OrderedDict.popitem()`` does not support the ``last=False`` argument and + will always remove and return the last item if present. + + A workaround for this is to use ``pop()`` to remove the first item:: + + first_key = next(iter(d)) + d.pop(first_key) diff --git a/docs/library/platform.rst b/docs/library/platform.rst index c091477d84cb1..c19ef0f5df524 100644 --- a/docs/library/platform.rst +++ b/docs/library/platform.rst @@ -36,3 +36,11 @@ Functions Returns a tuple of strings *(lib, version)*, where *lib* is the name of the libc that MicroPython is linked to, and *version* the corresponding version of this libc. + +.. function:: processor() + + Returns a string with a detailed name of the processor, if one is available. + If no name for the processor is known, it will return an empty string + instead. + + This is currently available only on RISC-V targets (both 32 and 64 bits). diff --git a/docs/library/re.rst b/docs/library/re.rst index 19b15d2d2c299..b8aeefd90cfa4 100644 --- a/docs/library/re.rst +++ b/docs/library/re.rst @@ -154,8 +154,8 @@ Regex objects Compiled regular expression. Instances of this class are created using `re.compile()`. -.. method:: regex.match(string) - regex.search(string) +.. method:: regex.match(string, [pos, [endpos]]) + regex.search(string, [pos, [endpos]]) regex.sub(replace, string, count=0, flags=0, /) Similar to the module-level functions :meth:`match`, :meth:`search` @@ -163,6 +163,16 @@ Compiled regular expression. Instances of this class are created using Using methods is (much) more efficient if the same regex is applied to multiple strings. + The optional second parameter *pos* gives an index in the string where the + search is to start; it defaults to ``0``. This is not completely equivalent + to slicing the string; the ``'^'`` pattern character matches at the real + beginning of the string and at positions just after a newline, but not + necessarily at the index where the search is to start. + + The optional parameter *endpos* limits how far the string will be searched; + it will be as if the string is *endpos* characters long, so only the + characters from *pos* to ``endpos - 1`` will be searched for a match. + .. method:: regex.split(string, max_split=-1, /) Split a *string* using regex. If *max_split* is given, it specifies diff --git a/docs/library/sys.rst b/docs/library/sys.rst index 8def36a2b07a2..c9c687df35936 100644 --- a/docs/library/sys.rst +++ b/docs/library/sys.rst @@ -12,17 +12,9 @@ Functions .. function:: exit(retval=0, /) Terminate current program with a given exit code. Underlyingly, this - function raise as `SystemExit` exception. If an argument is given, its + function raises a `SystemExit` exception. If an argument is given, its value given as an argument to `SystemExit`. -.. function:: print_exception(exc, file=sys.stdout, /) - - This function is deprecated and will be removed starting in - CircuitPython 10.x, `traceback.print_exception()` should be used instead. - - Print exception with a traceback to a file-like object *file* (or - `sys.stdout` by default). - .. admonition:: Difference to CPython :class: attention @@ -52,6 +44,8 @@ Constants * *version* - tuple (major, minor, micro), e.g. (1, 7, 0) * *_machine* - string describing the underlying machine * *_mpy* - supported mpy file-format version (optional attribute) + * *_build* - string that can help identify the configuration that + MicroPython was built with This object is the recommended way to distinguish CircuitPython from other Python implementations (note that it still may not exist in the very @@ -116,15 +110,15 @@ Constants .. data:: stderr - Standard error ``stream``. + Standard error `stream`. .. data:: stdin - Standard input ``stream``. + Standard input `stream`. .. data:: stdout - Standard output ``stream``. + Standard output `stream`. .. data:: version diff --git a/docs/reference/glossary.rst b/docs/reference/glossary.rst index 9e9330de4ccfe..391dc307d2343 100644 --- a/docs/reference/glossary.rst +++ b/docs/reference/glossary.rst @@ -186,6 +186,13 @@ Glossary Most MicroPython boards make a REPL available over a UART, and this is typically accessible on a host PC via USB. + small integer + MicroPython optimises the internal representation of integers such that + "small" values do not take up space on the heap, and calculations with + them do not require heap allocation. On most 32-bit ports, this + corresponds to values in the interval ``-2**30 <= x < 2**30``, but this + should be considered an implementation detail and not relied upon. + stream Also known as a "file-like object". A Python object which provides sequential read-write access to the underlying data. A stream object diff --git a/docs/rstjinja.py b/docs/rstjinja.py index e7d8a312f1f9d..04c855a1a4049 100644 --- a/docs/rstjinja.py +++ b/docs/rstjinja.py @@ -38,3 +38,7 @@ def rstjinja(app, docname, source): def setup(app): app.connect("source-read", rstjinja) + return { + "parallel_read_safe": True, + "parallel_write_safe": True, + } diff --git a/docs/shared_bindings_matrix.py b/docs/shared_bindings_matrix.py index b4fe4ff7e2464..8fbee4beea5b5 100644 --- a/docs/shared_bindings_matrix.py +++ b/docs/shared_bindings_matrix.py @@ -84,8 +84,9 @@ "keypad.Keys": "CIRCUITPY_KEYPAD_KEYS", "keypad.ShiftRegisterKeys": "CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS", "keypad_demux.DemuxKeyMatrix": "CIRCUITPY_KEYPAD_DEMUX", - "os.getenv": "CIRCUITPY_OS_GETENV", + "os.getenv": "CIRCUITPY_SETTINGS_TOML", "select": "MICROPY_PY_SELECT_SELECT", + "supervisor.get_setting": "CIRCUITPY_SETTINGS_TOML", "sys": "CIRCUITPY_SYS", "terminalio": "CIRCUITPY_DISPLAYIO", "usb": "CIRCUITPY_PYUSB", diff --git a/docs/workflows.md b/docs/workflows.md index 761504144d8b9..84d7530e2fe99 100644 --- a/docs/workflows.md +++ b/docs/workflows.md @@ -44,6 +44,12 @@ A few boards have SD card automounting. (This is based on the ``DEFAULT_SD`` set ``mpconfigboard.h``.) The card is writable from CircuitPython by default and read-only to the host. `storage.remount()` can be used to remount the drive to the host as read-write. +On most other boards, except for ``atmel-samd`` boards, an SD card mounted in user code +at ``/sd`` will become visible after a few seconds on the attached host computer, as an +additional drive besides CIRCUITPY and (if present) CPSAVES. It will present with the volume +label on the SD card. Depending on the host operating system settings, the drive may or may not be +auto-mounted on the host. Host writes to drives mounted by user code will not trigger a reload. + ### CDC serial CircuitPython exposes one CDC USB interface for CircuitPython serial. This is a standard serial USB interface. diff --git a/extmod/extmod.mk b/extmod/extmod.mk index daa6f267342ae..5b2772e4e2a99 100644 --- a/extmod/extmod.mk +++ b/extmod/extmod.mk @@ -162,7 +162,7 @@ endif ifeq ($(MICROPY_VFS_LFS2),1) CFLAGS_EXTMOD += -DMICROPY_VFS_LFS2=1 -CFLAGS_THIRDPARTY += -DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT +CFLAGS_THIRDPARTY += -DLFS2_NO_MALLOC -DLFS2_NO_DEBUG -DLFS2_NO_WARN -DLFS2_NO_ERROR -DLFS2_NO_ASSERT -DLFS2_DEFINES=extmod/littlefs-include/lfs2_defines.h SRC_THIRDPARTY_C += $(addprefix $(LITTLEFS_DIR)/,\ lfs2.c \ lfs2_util.c \ @@ -461,7 +461,7 @@ ESP_HOSTED_SRC_C = $(addprefix $(ESP_HOSTED_DIR)/,\ ) ifeq ($(MICROPY_PY_BLUETOOTH),1) -ESP_HOSTED_SRC_C += $(ESP_HOSTED_DIR)/esp_hosted_bthci.c +ESP_HOSTED_SRC_C += $(ESP_HOSTED_DIR)/esp_hosted_bthci_uart.c endif # Include the protobuf-c support functions diff --git a/extmod/lwip-include/arch/cc.h b/extmod/lwip-include/arch/cc.h deleted file mode 100644 index 400dc6ec75de1..0000000000000 --- a/extmod/lwip-include/arch/cc.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H -#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H - -#include - -// Generate lwip's internal types from stdint - -typedef uint8_t u8_t; -typedef int8_t s8_t; -typedef uint16_t u16_t; -typedef int16_t s16_t; -typedef uint32_t u32_t; -typedef int32_t s32_t; - -typedef u32_t mem_ptr_t; - -#define U16_F "hu" -#define S16_F "hd" -#define X16_F "hx" -#define U32_F "u" -#define S32_F "d" -#define X32_F "x" - -#define X8_F "02x" -#define SZT_F "u" - -#define BYTE_ORDER LITTLE_ENDIAN - -#define LWIP_CHKSUM_ALGORITHM 2 - -#include -#define LWIP_PLATFORM_DIAG(x) -#define LWIP_PLATFORM_ASSERT(x) { assert(1); } - -//#define PACK_STRUCT_FIELD(x) x __attribute__((packed)) -#define PACK_STRUCT_FIELD(x) x -#define PACK_STRUCT_STRUCT __attribute__((packed)) -#define PACK_STRUCT_BEGIN -#define PACK_STRUCT_END - -#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_CC_H diff --git a/extmod/lwip-include/arch/perf.h b/extmod/lwip-include/arch/perf.h deleted file mode 100644 index d310fc339f162..0000000000000 --- a/extmod/lwip-include/arch/perf.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H -#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H - -#define PERF_START /* null definition */ -#define PERF_STOP(x) /* null definition */ - -#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_ARCH_PERF_H diff --git a/extmod/lwip-include/lwipopts.h b/extmod/lwip-include/lwipopts.h deleted file mode 100644 index 584decfe85f83..0000000000000 --- a/extmod/lwip-include/lwipopts.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H -#define MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H - -#include -#include -#include - -// We're running without an OS for this port. We don't provide any services except light protection. -#define NO_SYS 1 - -#define SYS_LIGHTWEIGHT_PROT 1 -#include -typedef uint32_t sys_prot_t; - -#define TCP_LISTEN_BACKLOG 1 - -// We'll put these into a proper ifdef once somebody implements an ethernet driver -#define LWIP_ARP 0 -#define LWIP_ETHERNET 0 - -#define LWIP_DNS 1 - -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 - -// CIRCUITPY-CHANGE: #if instead of #ifdef -#if MICROPY_PY_LWIP_SLIP -#define LWIP_HAVE_SLIPIF 1 -#endif - -// For now, we can simply define this as a macro for the timer code. But this function isn't -// universal and other ports will need to do something else. It may be necessary to move -// things like this into a port-provided header file. -#define sys_now mp_hal_ticks_ms - -#endif // MICROPY_INCLUDED_EXTMOD_LWIP_INCLUDE_LWIPOPTS_H diff --git a/extmod/lwip-include/lwipopts_common.h b/extmod/lwip-include/lwipopts_common.h deleted file mode 100644 index 3e4230909499e..0000000000000 --- a/extmod/lwip-include/lwipopts_common.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2025 Damien P. George - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef MICROPY_INCLUDED_LWIPOPTS_COMMON_H -#define MICROPY_INCLUDED_LWIPOPTS_COMMON_H - -#include "py/mpconfig.h" - -// This sys-arch protection is not needed. -// Ports either protect lwIP code with flags, or run it at PendSV priority. -#define SYS_ARCH_DECL_PROTECT(lev) do { } while (0) -#define SYS_ARCH_PROTECT(lev) do { } while (0) -#define SYS_ARCH_UNPROTECT(lev) do { } while (0) - -#define NO_SYS 1 -#define SYS_LIGHTWEIGHT_PROT 1 -#define MEM_ALIGNMENT 4 - -#define LWIP_CHKSUM_ALGORITHM 3 -#define LWIP_CHECKSUM_CTRL_PER_NETIF 1 - -#define LWIP_ARP 1 -#define LWIP_ETHERNET 1 -#define LWIP_RAW 1 -#define LWIP_NETCONN 0 -#define LWIP_SOCKET 0 -#define LWIP_STATS 0 -#define LWIP_NETIF_HOSTNAME 1 - -#define LWIP_DHCP 1 -#define LWIP_DHCP_CHECK_LINK_UP 1 -#define LWIP_DHCP_DOES_ACD_CHECK 0 // to speed DHCP up -#define LWIP_DNS 1 -#define LWIP_DNS_SUPPORT_MDNS_QUERIES 1 -#define LWIP_MDNS_RESPONDER 1 -#define LWIP_IGMP 1 - -#if MICROPY_PY_LWIP_PPP -#define PPP_SUPPORT 1 -#define PAP_SUPPORT 1 -#define CHAP_SUPPORT 1 -#endif - -#define LWIP_NUM_NETIF_CLIENT_DATA LWIP_MDNS_RESPONDER -#define MEMP_NUM_UDP_PCB (4 + LWIP_MDNS_RESPONDER) - -// The mDNS responder requires 5 timers per IP version plus 2 others. Not having enough silently breaks it. -#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + (LWIP_MDNS_RESPONDER * (2 + (5 * (LWIP_IPV4 + LWIP_IPV6))))) - -#define SO_REUSE 1 -#define TCP_LISTEN_BACKLOG 1 - -// TCP memory settings. -// Default lwIP settings takes 15800 bytes; TCP d/l: 380k/s local, 7.2k/s remote; TCP u/l is very slow. -#ifndef MEM_SIZE - -#if 0 -// lwIP takes 19159 bytes; TCP d/l and u/l are around 320k/s on local network. -#define MEM_SIZE (5000) -#define TCP_WND (4 * TCP_MSS) -#define TCP_SND_BUF (4 * TCP_MSS) -#endif - -#if 1 -// lwIP takes 26700 bytes; TCP dl/ul are around 750/600 k/s on local network. -#define MEM_SIZE (8000) -#define TCP_MSS (800) -#define TCP_WND (8 * TCP_MSS) -#define TCP_SND_BUF (8 * TCP_MSS) -#define MEMP_NUM_TCP_SEG (32) -#endif - -#if 0 -// lwIP takes 45600 bytes; TCP dl/ul are around 1200/1000 k/s on local network. -#define MEM_SIZE (16000) -#define TCP_MSS (1460) -#define TCP_WND (8 * TCP_MSS) -#define TCP_SND_BUF (8 * TCP_MSS) -#define MEMP_NUM_TCP_SEG (32) -#endif - -#endif // MEM_SIZE - -// Needed for PPP. -#define sys_jiffies sys_now - -typedef uint32_t sys_prot_t; - -#endif // MICROPY_INCLUDED_LWIPOPTS_COMMON_H diff --git a/extmod/modjson.c b/extmod/modjson.c index 67da36f0ccf71..77f4a336aa821 100644 --- a/extmod/modjson.c +++ b/extmod/modjson.c @@ -227,7 +227,8 @@ static mp_obj_t _mod_json_load(mp_obj_t stream_obj, bool return_first_json) { for (;;) { cont: if (S_END(s)) { - break; + // Input finished abruptly in the middle of a composite entity. + goto fail; } mp_obj_t next = MP_OBJ_NULL; bool enter = false; diff --git a/extmod/modplatform.c b/extmod/modplatform.c index c6d4d31b97ec1..e1f5476c3b3aa 100644 --- a/extmod/modplatform.c +++ b/extmod/modplatform.c @@ -61,11 +61,49 @@ static mp_obj_t platform_libc_ver(size_t n_args, const mp_obj_t *pos_args, mp_ma } static MP_DEFINE_CONST_FUN_OBJ_KW(platform_libc_ver_obj, 0, platform_libc_ver); +#ifdef __riscv +static mp_obj_t platform_processor(void) { + #if (__riscv_xlen <= 64) && !defined(__linux__) + uintptr_t misa_csr = 0; + + // Load the MISA CSR directly. + __asm volatile ( + "csrr %0, misa \n" + : "+r" (misa_csr) + : + : + ); + + char processor_buffer[31] = { // "RV32"/"RV64" + up to 26 chars. + #if (__riscv_xlen < 64) + "RV32" + #else + "RV64" + #endif + }; + mp_uint_t offset = 4; + for (mp_uint_t bit = 0; bit < 26; bit++) { + if (misa_csr & (1U << bit)) { + processor_buffer[offset++] = 'A' + bit; + } + } + + return mp_obj_new_str(processor_buffer, offset); + #else + return MP_OBJ_NEW_QSTR(MP_QSTR_); + #endif +} +static MP_DEFINE_CONST_FUN_OBJ_0(platform_processor_obj, platform_processor); +#endif + static const mp_rom_map_elem_t modplatform_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_platform) }, { MP_ROM_QSTR(MP_QSTR_platform), MP_ROM_PTR(&platform_platform_obj) }, { MP_ROM_QSTR(MP_QSTR_python_compiler), MP_ROM_PTR(&platform_python_compiler_obj) }, { MP_ROM_QSTR(MP_QSTR_libc_ver), MP_ROM_PTR(&platform_libc_ver_obj) }, + #ifdef __riscv + { MP_ROM_QSTR(MP_QSTR_processor), MP_ROM_PTR(&platform_processor_obj) }, + #endif }; static MP_DEFINE_CONST_DICT(modplatform_globals, modplatform_globals_table); diff --git a/extmod/modre.c b/extmod/modre.c index ba2927eb4a8a6..36fff1d1dc668 100644 --- a/extmod/modre.c +++ b/extmod/modre.c @@ -196,10 +196,11 @@ static void re_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t // Note: this function can't be named re_exec because it may clash with system headers, eg on FreeBSD static mp_obj_t re_exec_helper(bool is_anchored, uint n_args, const mp_obj_t *args) { - (void)n_args; mp_obj_re_t *self; + bool was_compiled = false; if (mp_obj_is_type(args[0], (mp_obj_type_t *)&re_type)) { self = MP_OBJ_TO_PTR(args[0]); + was_compiled = true; } else { self = MP_OBJ_TO_PTR(mod_re_compile(1, args)); } @@ -207,37 +208,28 @@ static mp_obj_t re_exec_helper(bool is_anchored, uint n_args, const mp_obj_t *ar size_t len; subj.begin_line = subj.begin = mp_obj_str_get_data(args[1], &len); subj.end = subj.begin + len; - // CIRCUITPY-CHANGE - #if MICROPY_PY_RE_MATCH_SPAN_START_END && !(defined(MICROPY_ENABLE_DYNRUNTIME) && MICROPY_ENABLE_DYNRUNTIME) - - if (n_args > 2) { - const mp_obj_type_t *self_type = mp_obj_get_type(args[1]); - mp_int_t str_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(args[1])); - const byte *begin = (const byte *)subj.begin; - int pos = mp_obj_get_int(args[2]); - if (pos >= str_len) { - return mp_const_none; - } - if (pos < 0) { - pos = 0; + if (was_compiled && n_args > 2) { + // Arg #2 is starting-pos + mp_int_t startpos = mp_obj_get_int(args[2]); + if (startpos > (mp_int_t)len) { + startpos = len; + } else if (startpos < 0) { + startpos = 0; } - const byte *pos_ptr = str_index_to_ptr(self_type, begin, len, MP_OBJ_NEW_SMALL_INT(pos), true); - - const byte *endpos_ptr = (const byte *)subj.end; + subj.begin += startpos; if (n_args > 3) { - int endpos = mp_obj_get_int(args[3]); - if (endpos <= pos) { - return mp_const_none; + // Arg #3 is ending-pos + mp_int_t endpos = mp_obj_get_int(args[3]); + if (endpos > (mp_int_t)len) { + endpos = len; + } else if (endpos < startpos) { + endpos = startpos; } - // Will cap to length - endpos_ptr = str_index_to_ptr(self_type, begin, len, args[3], true); + subj.end = subj.begin_line + endpos; } - - subj.begin = (const char *)pos_ptr; - subj.end = (const char *)endpos_ptr; } - #endif + int caps_num = (self->re.sub + 1) * 2; mp_obj_match_t *match = m_new_obj_var(mp_obj_match_t, caps, char *, caps_num); // cast is a workaround for a bug in msvc: it treats const char** as a const pointer instead of a pointer to pointer to const char @@ -458,6 +450,9 @@ static mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { const char *re_str = mp_obj_str_get_str(args[0]); int size = re1_5_sizecode(re_str); if (size == -1) { + #if MICROPY_ERROR_REPORTING >= MICROPY_ERROR_REPORTING_NORMAL + mp_raise_ValueError(MP_ERROR_TEXT("regex too complex")); + #endif goto error; } mp_obj_re_t *o = mp_obj_malloc_var(mp_obj_re_t, re.insts, char, size, (mp_obj_type_t *)&re_type); @@ -470,6 +465,7 @@ static mp_obj_t mod_re_compile(size_t n_args, const mp_obj_t *args) { int error = re1_5_compilecode(&o->re, re_str); if (error != 0) { error: + // CIRCUITPY-CHANGE: capitalized mp_raise_ValueError(MP_ERROR_TEXT("Error in regex")); } #if MICROPY_PY_RE_DEBUG diff --git a/extmod/modtime.c b/extmod/modtime.c index deb4bb4c9ace7..ee898828a4ab1 100644 --- a/extmod/modtime.c +++ b/extmod/modtime.c @@ -53,26 +53,26 @@ // - weekday is 0-6 for Mon-Sun // - yearday is 1-366 static mp_obj_t time_localtime(size_t n_args, const mp_obj_t *args) { + timeutils_struct_time_t tm; if (n_args == 0 || args[0] == mp_const_none) { // Get current date and time. - return mp_time_localtime_get(); + mp_time_localtime_get(&tm); } else { // Convert given seconds to tuple. - mp_int_t seconds = mp_obj_get_int(args[0]); - timeutils_struct_time_t tm; + mp_timestamp_t seconds = timeutils_obj_get_timestamp(args[0]); timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); - mp_obj_t tuple[8] = { - tuple[0] = mp_obj_new_int(tm.tm_year), - tuple[1] = mp_obj_new_int(tm.tm_mon), - tuple[2] = mp_obj_new_int(tm.tm_mday), - tuple[3] = mp_obj_new_int(tm.tm_hour), - tuple[4] = mp_obj_new_int(tm.tm_min), - tuple[5] = mp_obj_new_int(tm.tm_sec), - tuple[6] = mp_obj_new_int(tm.tm_wday), - tuple[7] = mp_obj_new_int(tm.tm_yday), - }; - return mp_obj_new_tuple(8, tuple); } + mp_obj_t tuple[8] = { + mp_obj_new_int(tm.tm_year), + mp_obj_new_int(tm.tm_mon), + mp_obj_new_int(tm.tm_mday), + mp_obj_new_int(tm.tm_hour), + mp_obj_new_int(tm.tm_min), + mp_obj_new_int(tm.tm_sec), + mp_obj_new_int(tm.tm_wday), + mp_obj_new_int(tm.tm_yday), + }; + return mp_obj_new_tuple(8, tuple); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_time_localtime_obj, 0, 1, time_localtime); @@ -90,7 +90,7 @@ static mp_obj_t time_mktime(mp_obj_t tuple) { mp_raise_TypeError(MP_ERROR_TEXT("mktime needs a tuple of length 8 or 9")); } - return mp_obj_new_int_from_uint(timeutils_mktime(mp_obj_get_int(elem[0]), + return timeutils_obj_from_timestamp(timeutils_mktime(mp_obj_get_int(elem[0]), mp_obj_get_int(elem[1]), mp_obj_get_int(elem[2]), mp_obj_get_int(elem[3]), mp_obj_get_int(elem[4]), mp_obj_get_int(elem[5]))); } diff --git a/extmod/vfs.c b/extmod/vfs.c index 0cf93c3966c90..c4156990bcc38 100644 --- a/extmod/vfs.c +++ b/extmod/vfs.c @@ -101,7 +101,7 @@ mp_vfs_mount_t *mp_vfs_lookup_path(const char *path, const char **path_out) { return MP_STATE_VM(vfs_cur); } -// Version of mp_vfs_lookup_path that takes and returns uPy string objects. +// Version of mp_vfs_lookup_path that takes and returns MicroPython string objects. static mp_vfs_mount_t *lookup_path(mp_obj_t path_in, mp_obj_t *path_out) { const char *path = mp_obj_str_get_str(path_in); const char *p_out; diff --git a/extmod/vfs.h b/extmod/vfs.h index 67d5d9239a33e..699232e56cd87 100644 --- a/extmod/vfs.h +++ b/extmod/vfs.h @@ -52,6 +52,8 @@ #define MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED (0x0020) // Bit set when something has claimed the right to mutate the blockdev. #define MP_BLOCKDEV_FLAG_LOCKED (0x0040) +// Ignore write protections. Used to override other flags temporarily. +#define MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION (0x0080) // constants for block protocol ioctl #define MP_BLOCKDEV_IOCTL_INIT (1) diff --git a/extmod/vfs_blockdev.c b/extmod/vfs_blockdev.c index f1187b9bbf2f5..74d1262364ef4 100644 --- a/extmod/vfs_blockdev.c +++ b/extmod/vfs_blockdev.c @@ -63,7 +63,16 @@ void mp_vfs_blockdev_init(mp_vfs_blockdev_t *self, mp_obj_t bdev) { #endif #if CIRCUITPY_SDIOIO if (mp_obj_get_type(bdev) == &sdioio_SDCard_type) { - // TODO: Enable native blockdev for SDIO too. + self->flags |= MP_BLOCKDEV_FLAG_NATIVE | MP_BLOCKDEV_FLAG_HAVE_IOCTL; + self->readblocks[0] = mp_const_none; + self->readblocks[1] = bdev; + self->readblocks[2] = (mp_obj_t)sdioio_sdcard_readblocks; // native version + self->writeblocks[0] = mp_const_none; + self->writeblocks[1] = bdev; + self->writeblocks[2] = (mp_obj_t)sdioio_sdcard_writeblocks; // native version + self->u.ioctl[0] = mp_const_none; + self->u.ioctl[1] = bdev; + self->u.ioctl[2] = (mp_obj_t)sdioio_sdcard_ioctl; // native version } #endif if (self->u.ioctl[0] != MP_OBJ_NULL) { @@ -99,7 +108,7 @@ static int mp_vfs_blockdev_call_rw(mp_obj_t *args, size_t block_num, size_t bloc // and negative integer on errors. Check for positive integer // results as some callers (i.e. littlefs) will produce corrupt // results from these. - int i = MP_OBJ_SMALL_INT_VALUE(ret); + int i = mp_obj_get_int(ret); return i > 0 ? (-MP_EINVAL) : i; } } diff --git a/extmod/vfs_fat.c b/extmod/vfs_fat.c index 5011a820af864..95ed79eb18bd5 100644 --- a/extmod/vfs_fat.c +++ b/extmod/vfs_fat.c @@ -58,7 +58,7 @@ // CIRCUITPY-CHANGE // Factoring this common call saves about 90 bytes. -static NORETURN void mp_raise_OSError_fresult(FRESULT res) { +static MP_NORETURN void mp_raise_OSError_fresult(FRESULT res) { mp_raise_OSError(fresult_to_errno_table[res]); } @@ -427,13 +427,10 @@ static MP_DEFINE_CONST_FUN_OBJ_2(fat_vfs_statvfs_obj, fat_vfs_statvfs); static mp_obj_t vfs_fat_mount(mp_obj_t self_in, mp_obj_t readonly, mp_obj_t mkfs) { fs_user_mount_t *self = MP_OBJ_TO_PTR(self_in); - // Read-only device indicated by writeblocks[0] == MP_OBJ_NULL. - // User can specify read-only device by: - // 1. readonly=True keyword argument - // 2. nonexistent writeblocks method (then writeblocks[0] == MP_OBJ_NULL already) - if (mp_obj_is_true(readonly)) { - self->blockdev.writeblocks[0] = MP_OBJ_NULL; - } + // CIRCUITPY-CHANGE: Use MP_BLOCKDEV_FLAG_USB_WRITABLE instead of writeblocks[0] =/!= MP_OBJ_NULL + // to specify read-write. + // If readonly to Python, it's writable by USB and vice versa. + filesystem_set_writable_by_usb(self, mp_obj_is_true(readonly)); // check if we need to make the filesystem FRESULT res = (self->blockdev.flags & MP_BLOCKDEV_FLAG_NO_FILESYSTEM) ? FR_NO_FILESYSTEM : FR_OK; diff --git a/extmod/vfs_fat_diskio.c b/extmod/vfs_fat_diskio.c index 6f500104d1e86..591da07eb5630 100644 --- a/extmod/vfs_fat_diskio.c +++ b/extmod/vfs_fat_diskio.c @@ -43,6 +43,9 @@ #include "lib/oofatfs/diskio.h" #include "extmod/vfs_fat.h" +// CIRCUITPY-CHANGE +#include "supervisor/filesystem.h" + typedef void *bdev_t; static fs_user_mount_t *disk_get_device(void *bdev) { return (fs_user_mount_t *)bdev; @@ -153,7 +156,8 @@ DRESULT disk_ioctl( if (ret != mp_const_none && MP_OBJ_SMALL_INT_VALUE(ret) != 0) { // error initialising stat = STA_NOINIT; - } else if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL) { + // CIRCUITPY-CHANGE: writability from Python check + } else if (!filesystem_is_writable_by_python(vfs)) { stat = STA_PROTECT; } else { stat = 0; diff --git a/extmod/vfs_lfsx.c b/extmod/vfs_lfsx.c index 4b10ca3aa597c..bbdd21cfb9176 100644 --- a/extmod/vfs_lfsx.c +++ b/extmod/vfs_lfsx.c @@ -104,6 +104,12 @@ static void MP_VFS_LFSx(init_config)(MP_OBJ_VFS_LFSx * self, mp_obj_t bdev, size config->read_buffer = m_new(uint8_t, config->cache_size); config->prog_buffer = m_new(uint8_t, config->cache_size); config->lookahead_buffer = m_new(uint8_t, config->lookahead_size); + #ifdef LFS2_MULTIVERSION + // This can be set to override the on-disk lfs version. + // eg. for compat with lfs2 < v2.6 add the following to make: + // CFLAGS += '-DLFS2_MULTIVERSION=0x00020000' + config->disk_version = LFS2_MULTIVERSION; + #endif #endif } @@ -300,7 +306,7 @@ static mp_obj_t MP_VFS_LFSx(chdir)(mp_obj_t self_in, mp_obj_t path_in) { struct LFSx_API (info) info; int ret = LFSx_API(stat)(&self->lfs, path, &info); if (ret < 0 || info.type != LFSx_MACRO(_TYPE_DIR)) { - mp_raise_OSError(-MP_ENOENT); + mp_raise_OSError(MP_ENOENT); } } @@ -378,7 +384,7 @@ static mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { mp_raise_OSError(-ret); } - mp_uint_t mtime = 0; + mp_timestamp_t mtime = 0; #if LFS_BUILD_VERSION == 2 uint8_t mtime_buf[8]; lfs2_ssize_t sz = lfs2_getattr(&self->lfs, path, LFS_ATTR_MTIME, &mtime_buf, sizeof(mtime_buf)); @@ -400,9 +406,9 @@ static mp_obj_t MP_VFS_LFSx(stat)(mp_obj_t self_in, mp_obj_t path_in) { t->items[4] = MP_OBJ_NEW_SMALL_INT(0); // st_uid t->items[5] = MP_OBJ_NEW_SMALL_INT(0); // st_gid t->items[6] = mp_obj_new_int_from_uint(info.size); // st_size - t->items[7] = mp_obj_new_int_from_uint(mtime); // st_atime - t->items[8] = mp_obj_new_int_from_uint(mtime); // st_mtime - t->items[9] = mp_obj_new_int_from_uint(mtime); // st_ctime + t->items[7] = timeutils_obj_from_timestamp(mtime); // st_atime + t->items[8] = timeutils_obj_from_timestamp(mtime); // st_mtime + t->items[9] = timeutils_obj_from_timestamp(mtime); // st_ctime return MP_OBJ_FROM_PTR(t); } diff --git a/extmod/vfs_lfsx_file.c b/extmod/vfs_lfsx_file.c index ab5cce50088b9..56daa53e06869 100644 --- a/extmod/vfs_lfsx_file.c +++ b/extmod/vfs_lfsx_file.c @@ -90,9 +90,9 @@ mp_obj_t MP_VFS_LFSx(file_open)(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mod } #if LFS_BUILD_VERSION == 1 - MP_OBJ_VFS_LFSx_FILE *o = mp_obj_malloc_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->prog_size, type); + MP_OBJ_VFS_LFSx_FILE *o = mp_obj_malloc_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, file_buffer, uint8_t, self->lfs.cfg->prog_size, type); #else - MP_OBJ_VFS_LFSx_FILE *o = mp_obj_malloc_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, uint8_t, self->lfs.cfg->cache_size, type); + MP_OBJ_VFS_LFSx_FILE *o = mp_obj_malloc_var_with_finaliser(MP_OBJ_VFS_LFSx_FILE, file_buffer, uint8_t, self->lfs.cfg->cache_size, type); #endif o->vfs = self; #if !MICROPY_GC_CONSERVATIVE_CLEAR diff --git a/extmod/vfs_posix.c b/extmod/vfs_posix.c index bd9a6d84062cf..27f833e802f69 100644 --- a/extmod/vfs_posix.c +++ b/extmod/vfs_posix.c @@ -137,7 +137,7 @@ static mp_obj_t vfs_posix_make_new(const mp_obj_type_t *type, size_t n_args, siz vstr_add_char(&vfs->root, '/'); } vfs->root_len = vfs->root.len; - vfs->readonly = false; + vfs->readonly = !MICROPY_VFS_POSIX_WRITABLE; return MP_OBJ_FROM_PTR(vfs); } @@ -160,10 +160,21 @@ static mp_obj_t vfs_posix_umount(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(vfs_posix_umount_obj, vfs_posix_umount); +static inline bool vfs_posix_is_readonly(mp_obj_vfs_posix_t *self) { + return !MICROPY_VFS_POSIX_WRITABLE || self->readonly; +} + +static void vfs_posix_require_writable(mp_obj_t self_in) { + mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); + if (vfs_posix_is_readonly(self)) { + mp_raise_OSError(MP_EROFS); + } +} + static mp_obj_t vfs_posix_open(mp_obj_t self_in, mp_obj_t path_in, mp_obj_t mode_in) { mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); const char *mode = mp_obj_str_get_str(mode_in); - if (self->readonly + if (vfs_posix_is_readonly(self) && (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL || strchr(mode, '+') != NULL)) { mp_raise_OSError(MP_EROFS); } @@ -303,6 +314,7 @@ typedef struct _mp_obj_listdir_t { } mp_obj_listdir_t; static mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) { + vfs_posix_require_writable(self_in); mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); const char *path = vfs_posix_get_path_str(self, path_in); MP_THREAD_GIL_EXIT(); @@ -320,11 +332,13 @@ static mp_obj_t vfs_posix_mkdir(mp_obj_t self_in, mp_obj_t path_in) { static MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_mkdir_obj, vfs_posix_mkdir); static mp_obj_t vfs_posix_remove(mp_obj_t self_in, mp_obj_t path_in) { + vfs_posix_require_writable(self_in); return vfs_posix_fun1_helper(self_in, path_in, unlink); } static MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_remove_obj, vfs_posix_remove); static mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_t new_path_in) { + vfs_posix_require_writable(self_in); mp_obj_vfs_posix_t *self = MP_OBJ_TO_PTR(self_in); const char *old_path = vfs_posix_get_path_str(self, old_path_in); const char *new_path = vfs_posix_get_path_str(self, new_path_in); @@ -339,6 +353,7 @@ static mp_obj_t vfs_posix_rename(mp_obj_t self_in, mp_obj_t old_path_in, mp_obj_ static MP_DEFINE_CONST_FUN_OBJ_3(vfs_posix_rename_obj, vfs_posix_rename); static mp_obj_t vfs_posix_rmdir(mp_obj_t self_in, mp_obj_t path_in) { + vfs_posix_require_writable(self_in); return vfs_posix_fun1_helper(self_in, path_in, rmdir); } static MP_DEFINE_CONST_FUN_OBJ_2(vfs_posix_rmdir_obj, vfs_posix_rmdir); diff --git a/extmod/vfs_reader.c b/extmod/vfs_reader.c index de5c4e03d3c13..6c36770295be6 100644 --- a/extmod/vfs_reader.c +++ b/extmod/vfs_reader.c @@ -83,7 +83,7 @@ void mp_reader_new_file(mp_reader_t *reader, qstr filename) { }; mp_obj_t file = mp_vfs_open(MP_ARRAY_SIZE(args), &args[0], (mp_map_t *)&mp_const_empty_map); - const mp_stream_p_t *stream_p = mp_get_stream(file); + const mp_stream_p_t *stream_p = mp_get_stream_raise(file, MP_STREAM_OP_READ); int errcode = 0; #if MICROPY_VFS_ROM diff --git a/frozen/Adafruit_CircuitPython_AHTx0 b/frozen/Adafruit_CircuitPython_AHTx0 index ff95dd5f3d018..043c7163c1d66 160000 --- a/frozen/Adafruit_CircuitPython_AHTx0 +++ b/frozen/Adafruit_CircuitPython_AHTx0 @@ -1 +1 @@ -Subproject commit ff95dd5f3d0186c5cdc8bd8cb34ac22ac2e2225d +Subproject commit 043c7163c1d66a9d3ef2150e6140f92af973c9b1 diff --git a/frozen/Adafruit_CircuitPython_APDS9960 b/frozen/Adafruit_CircuitPython_APDS9960 index 00127a75d22f0..0134a126af843 160000 --- a/frozen/Adafruit_CircuitPython_APDS9960 +++ b/frozen/Adafruit_CircuitPython_APDS9960 @@ -1 +1 @@ -Subproject commit 00127a75d22f035096ea9317ad57c74c6a9b4232 +Subproject commit 0134a126af8430126c9a569a29958802716262fc diff --git a/frozen/Adafruit_CircuitPython_BLE b/frozen/Adafruit_CircuitPython_BLE index 1acb303cc7f63..63c9af9a343c6 160000 --- a/frozen/Adafruit_CircuitPython_BLE +++ b/frozen/Adafruit_CircuitPython_BLE @@ -1 +1 @@ -Subproject commit 1acb303cc7f63a752c9fb87655d2ec478e564be2 +Subproject commit 63c9af9a343c68ebbd013054b799d2e35a513343 diff --git a/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center b/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center index 476082b43c9e5..a636fc984dced 160000 --- a/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center +++ b/frozen/Adafruit_CircuitPython_BLE_Apple_Notification_Center @@ -1 +1 @@ -Subproject commit 476082b43c9e5971da20a320a05546a8285d4891 +Subproject commit a636fc984dced58f5f0815c5c531a03ee092f7ad diff --git a/frozen/Adafruit_CircuitPython_Bitmap_Font b/frozen/Adafruit_CircuitPython_Bitmap_Font index 5ca3f55f2e393..2b320bb26492b 160000 --- a/frozen/Adafruit_CircuitPython_Bitmap_Font +++ b/frozen/Adafruit_CircuitPython_Bitmap_Font @@ -1 +1 @@ -Subproject commit 5ca3f55f2e39302c787ca93f95276e8269024038 +Subproject commit 2b320bb26492b84d3ddabb2cd712d8db41a8b983 diff --git a/frozen/Adafruit_CircuitPython_BusDevice b/frozen/Adafruit_CircuitPython_BusDevice index afe91665e4389..d4b283c85b7b2 160000 --- a/frozen/Adafruit_CircuitPython_BusDevice +++ b/frozen/Adafruit_CircuitPython_BusDevice @@ -1 +1 @@ -Subproject commit afe91665e438947bd3d88ba4a0f937ec58ff1035 +Subproject commit d4b283c85b7b2043eb0d095638a47305a1f4ef61 diff --git a/frozen/Adafruit_CircuitPython_CircuitPlayground b/frozen/Adafruit_CircuitPython_CircuitPlayground index d093fed40590a..2ee9b7ddd9d85 160000 --- a/frozen/Adafruit_CircuitPython_CircuitPlayground +++ b/frozen/Adafruit_CircuitPython_CircuitPlayground @@ -1 +1 @@ -Subproject commit d093fed40590af312e44b1efa8d88ecaef9aaed4 +Subproject commit 2ee9b7ddd9d8574f1a154c67b5c849d2e44e3435 diff --git a/frozen/Adafruit_CircuitPython_ConnectionManager b/frozen/Adafruit_CircuitPython_ConnectionManager index 95f39faaa647b..89e59ec81bdfa 160000 --- a/frozen/Adafruit_CircuitPython_ConnectionManager +++ b/frozen/Adafruit_CircuitPython_ConnectionManager @@ -1 +1 @@ -Subproject commit 95f39faaa647b4215f615603368a453742423a09 +Subproject commit 89e59ec81bdfa7349b08ba3adf0d3cefc57af4d2 diff --git a/frozen/Adafruit_CircuitPython_Crickit b/frozen/Adafruit_CircuitPython_Crickit index efeb183228ff9..e3c372fecf720 160000 --- a/frozen/Adafruit_CircuitPython_Crickit +++ b/frozen/Adafruit_CircuitPython_Crickit @@ -1 +1 @@ -Subproject commit efeb183228ff9640aec5938f9c2305766579dc25 +Subproject commit e3c372fecf7209320826009de88818d91809cff6 diff --git a/frozen/Adafruit_CircuitPython_DRV2605 b/frozen/Adafruit_CircuitPython_DRV2605 index f120d56222166..18b7397ec2123 160000 --- a/frozen/Adafruit_CircuitPython_DRV2605 +++ b/frozen/Adafruit_CircuitPython_DRV2605 @@ -1 +1 @@ -Subproject commit f120d56222166af85b33e8e9c70eff6aec2e4828 +Subproject commit 18b7397ec21235fc398625a52f995a3be31eb59a diff --git a/frozen/Adafruit_CircuitPython_DS3231 b/frozen/Adafruit_CircuitPython_DS3231 index a5d94eee49d32..552104c8aed47 160000 --- a/frozen/Adafruit_CircuitPython_DS3231 +++ b/frozen/Adafruit_CircuitPython_DS3231 @@ -1 +1 @@ -Subproject commit a5d94eee49d324bad474847749c3d481a1f7c908 +Subproject commit 552104c8aed472c7437413e94b8954e63b4a4d4d diff --git a/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 b/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 index 4b382e8986db3..8c7acd451ad53 160000 --- a/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 +++ b/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 @@ -1 +1 @@ -Subproject commit 4b382e8986db36eaef558fec67be543205f268b2 +Subproject commit 8c7acd451ad53f7dc3b33a704c885032a03c1064 diff --git a/frozen/Adafruit_CircuitPython_Display_Shapes b/frozen/Adafruit_CircuitPython_Display_Shapes index e886723104183..bf6c2addf2465 160000 --- a/frozen/Adafruit_CircuitPython_Display_Shapes +++ b/frozen/Adafruit_CircuitPython_Display_Shapes @@ -1 +1 @@ -Subproject commit e8867231041837735ef2769a6dc793887d1979ca +Subproject commit bf6c2addf2465625de747d2f4ea1613fcded7840 diff --git a/frozen/Adafruit_CircuitPython_Display_Text b/frozen/Adafruit_CircuitPython_Display_Text index 7d1f187aac8e8..6450c7a3dc4c1 160000 --- a/frozen/Adafruit_CircuitPython_Display_Text +++ b/frozen/Adafruit_CircuitPython_Display_Text @@ -1 +1 @@ -Subproject commit 7d1f187aac8e899e791324cc78633bf4f32c984b +Subproject commit 6450c7a3dc4c16c4af4b75eda549537dffb5a73c diff --git a/frozen/Adafruit_CircuitPython_DotStar b/frozen/Adafruit_CircuitPython_DotStar index 163f2f166aee1..a12e2ce2046af 160000 --- a/frozen/Adafruit_CircuitPython_DotStar +++ b/frozen/Adafruit_CircuitPython_DotStar @@ -1 +1 @@ -Subproject commit 163f2f166aee11d82303492bb1e5af4937e57b62 +Subproject commit a12e2ce2046afc1791ca6aa2bdeb33cb97f7def0 diff --git a/frozen/Adafruit_CircuitPython_ESP32SPI b/frozen/Adafruit_CircuitPython_ESP32SPI index 063b90c8706dd..ec1d2e66ace17 160000 --- a/frozen/Adafruit_CircuitPython_ESP32SPI +++ b/frozen/Adafruit_CircuitPython_ESP32SPI @@ -1 +1 @@ -Subproject commit 063b90c8706ddef97cc4abf9cb78e0cc09ff3c6c +Subproject commit ec1d2e66ace176c13d7128a839287601a97a6bda diff --git a/frozen/Adafruit_CircuitPython_FakeRequests b/frozen/Adafruit_CircuitPython_FakeRequests index aa034280ebfed..f9f9d061a6645 160000 --- a/frozen/Adafruit_CircuitPython_FakeRequests +++ b/frozen/Adafruit_CircuitPython_FakeRequests @@ -1 +1 @@ -Subproject commit aa034280ebfed80c245827ff1d49a098ace64b03 +Subproject commit f9f9d061a66453e0d3164ffd4e046ed3b6007669 diff --git a/frozen/Adafruit_CircuitPython_FocalTouch b/frozen/Adafruit_CircuitPython_FocalTouch index 2fb86313db340..e18ecd678ec3a 160000 --- a/frozen/Adafruit_CircuitPython_FocalTouch +++ b/frozen/Adafruit_CircuitPython_FocalTouch @@ -1 +1 @@ -Subproject commit 2fb86313db3408e57b1fbfbc56359ccb4f16f38b +Subproject commit e18ecd678ec3ac4fef884db18ddb2aab56bf44f9 diff --git a/frozen/Adafruit_CircuitPython_HID b/frozen/Adafruit_CircuitPython_HID index d06b8b812caef..a147d3b5b9763 160000 --- a/frozen/Adafruit_CircuitPython_HID +++ b/frozen/Adafruit_CircuitPython_HID @@ -1 +1 @@ -Subproject commit d06b8b812caef3ae2eebb662f4e57ca306ce3219 +Subproject commit a147d3b5b976371601b460622209d327f51eb681 diff --git a/frozen/Adafruit_CircuitPython_HTTPServer b/frozen/Adafruit_CircuitPython_HTTPServer index b70106b17bbfa..d55c6f54aba54 160000 --- a/frozen/Adafruit_CircuitPython_HTTPServer +++ b/frozen/Adafruit_CircuitPython_HTTPServer @@ -1 +1 @@ -Subproject commit b70106b17bbfa0070f8573e1e06e384d9d4577de +Subproject commit d55c6f54aba54df9b0e0b9d83ddf7fca46eea5e4 diff --git a/frozen/Adafruit_CircuitPython_IRRemote b/frozen/Adafruit_CircuitPython_IRRemote index b92d69304212e..646cc051881a0 160000 --- a/frozen/Adafruit_CircuitPython_IRRemote +++ b/frozen/Adafruit_CircuitPython_IRRemote @@ -1 +1 @@ -Subproject commit b92d69304212ee57a5f008317fcc4ebaf75ddebb +Subproject commit 646cc051881a07d8bd5c442320479c6e5e553567 diff --git a/frozen/Adafruit_CircuitPython_IS31FL3731 b/frozen/Adafruit_CircuitPython_IS31FL3731 index a0d701892d8be..c011e8e0d021a 160000 --- a/frozen/Adafruit_CircuitPython_IS31FL3731 +++ b/frozen/Adafruit_CircuitPython_IS31FL3731 @@ -1 +1 @@ -Subproject commit a0d701892d8bef096d80f1117bee718cecb380ff +Subproject commit c011e8e0d021a6c5870242eb721f012922002fc8 diff --git a/frozen/Adafruit_CircuitPython_ImageLoad b/frozen/Adafruit_CircuitPython_ImageLoad index 135b0e4478b34..458bc46b63ea2 160000 --- a/frozen/Adafruit_CircuitPython_ImageLoad +++ b/frozen/Adafruit_CircuitPython_ImageLoad @@ -1 +1 @@ -Subproject commit 135b0e4478b34e1271e6bd87fa6d8efa0bef64b5 +Subproject commit 458bc46b63ea2bfc51529b05b62b4df3c5902e17 diff --git a/frozen/Adafruit_CircuitPython_LC709203F b/frozen/Adafruit_CircuitPython_LC709203F index b007bcae07b34..e32e39a18d23a 160000 --- a/frozen/Adafruit_CircuitPython_LC709203F +++ b/frozen/Adafruit_CircuitPython_LC709203F @@ -1 +1 @@ -Subproject commit b007bcae07b346fd28aaee770dcabc9dde698c67 +Subproject commit e32e39a18d23ae58fe97b3472253b6cbcbc8ebd0 diff --git a/frozen/Adafruit_CircuitPython_LED_Animation b/frozen/Adafruit_CircuitPython_LED_Animation index 5d13d0966a775..7cc736df1628f 160000 --- a/frozen/Adafruit_CircuitPython_LED_Animation +++ b/frozen/Adafruit_CircuitPython_LED_Animation @@ -1 +1 @@ -Subproject commit 5d13d0966a775369eca5b137cfef9583dfa8bb42 +Subproject commit 7cc736df1628f767c4fc6f23287cedbf6e80f4ce diff --git a/frozen/Adafruit_CircuitPython_LIS3DH b/frozen/Adafruit_CircuitPython_LIS3DH index 640b18ec1bfd7..ca871b8bc59b2 160000 --- a/frozen/Adafruit_CircuitPython_LIS3DH +++ b/frozen/Adafruit_CircuitPython_LIS3DH @@ -1 +1 @@ -Subproject commit 640b18ec1bfd71e0a70f7ff3b8784043cd2d2671 +Subproject commit ca871b8bc59b23acbfd779bf59d420599e8bcbb5 diff --git a/frozen/Adafruit_CircuitPython_LSM6DS b/frozen/Adafruit_CircuitPython_LSM6DS index 2f50836f4bf0d..9d94dc6a3350c 160000 --- a/frozen/Adafruit_CircuitPython_LSM6DS +++ b/frozen/Adafruit_CircuitPython_LSM6DS @@ -1 +1 @@ -Subproject commit 2f50836f4bf0d9e48e4b8e046ba4d4167ad6dbdc +Subproject commit 9d94dc6a3350cb8766baa4a1247e36576b00f68a diff --git a/frozen/Adafruit_CircuitPython_MIDI b/frozen/Adafruit_CircuitPython_MIDI index c4e693c2d4904..5feb31fbd31f2 160000 --- a/frozen/Adafruit_CircuitPython_MIDI +++ b/frozen/Adafruit_CircuitPython_MIDI @@ -1 +1 @@ -Subproject commit c4e693c2d4904d885cf842efc25687ccaccbabfa +Subproject commit 5feb31fbd31f2475862e0cb55c89359cb688ad34 diff --git a/frozen/Adafruit_CircuitPython_MPU6050 b/frozen/Adafruit_CircuitPython_MPU6050 index 05a0c3b72279d..d3761591ccf76 160000 --- a/frozen/Adafruit_CircuitPython_MPU6050 +++ b/frozen/Adafruit_CircuitPython_MPU6050 @@ -1 +1 @@ -Subproject commit 05a0c3b72279db9fa2431308a77e6ab7ba040c8a +Subproject commit d3761591ccf7629b39675dd1db45184b96a3b779 diff --git a/frozen/Adafruit_CircuitPython_Motor b/frozen/Adafruit_CircuitPython_Motor index 89facc69a405a..f55b2910b0109 160000 --- a/frozen/Adafruit_CircuitPython_Motor +++ b/frozen/Adafruit_CircuitPython_Motor @@ -1 +1 @@ -Subproject commit 89facc69a405ae83702ce566414adc39d46068f1 +Subproject commit f55b2910b01094904461d769581221c20418a267 diff --git a/frozen/Adafruit_CircuitPython_NeoPixel b/frozen/Adafruit_CircuitPython_NeoPixel index 0ba2f2122a54a..d3eaf5c45dde4 160000 --- a/frozen/Adafruit_CircuitPython_NeoPixel +++ b/frozen/Adafruit_CircuitPython_NeoPixel @@ -1 +1 @@ -Subproject commit 0ba2f2122a54a71b1bc3576f87b1ba7dfc9db11e +Subproject commit d3eaf5c45dde45588f9a2eb2ffbe0b9025251bee diff --git a/frozen/Adafruit_CircuitPython_OPT4048 b/frozen/Adafruit_CircuitPython_OPT4048 new file mode 160000 index 0000000000000..ddfe6b13acef7 --- /dev/null +++ b/frozen/Adafruit_CircuitPython_OPT4048 @@ -0,0 +1 @@ +Subproject commit ddfe6b13acef7076333ee8e0ecd27bc4186512b9 diff --git a/frozen/Adafruit_CircuitPython_PCF8563 b/frozen/Adafruit_CircuitPython_PCF8563 index 74bb72d1c607e..16e3112885b0b 160000 --- a/frozen/Adafruit_CircuitPython_PCF8563 +++ b/frozen/Adafruit_CircuitPython_PCF8563 @@ -1 +1 @@ -Subproject commit 74bb72d1c607e44cf0d5349c466acd34863c11b4 +Subproject commit 16e3112885b0b6149b4dfa1749181424dd5da122 diff --git a/frozen/Adafruit_CircuitPython_Pixel_Framebuf b/frozen/Adafruit_CircuitPython_Pixel_Framebuf index 1db789cf99429..555b30acf78ba 160000 --- a/frozen/Adafruit_CircuitPython_Pixel_Framebuf +++ b/frozen/Adafruit_CircuitPython_Pixel_Framebuf @@ -1 +1 @@ -Subproject commit 1db789cf99429e27d740279000788edc794d9d0d +Subproject commit 555b30acf78ba65cec48f64b3350e19fddada4ce diff --git a/frozen/Adafruit_CircuitPython_PortalBase b/frozen/Adafruit_CircuitPython_PortalBase index 3dce5bca3bcd2..b67904c7bca12 160000 --- a/frozen/Adafruit_CircuitPython_PortalBase +++ b/frozen/Adafruit_CircuitPython_PortalBase @@ -1 +1 @@ -Subproject commit 3dce5bca3bcd27354becc6d3ecf82244f1e26ffe +Subproject commit b67904c7bca12d7ee443e16bc40d64fd67c7cb48 diff --git a/frozen/Adafruit_CircuitPython_ProgressBar b/frozen/Adafruit_CircuitPython_ProgressBar index 6ba9d9d991ada..fab168ab38f61 160000 --- a/frozen/Adafruit_CircuitPython_ProgressBar +++ b/frozen/Adafruit_CircuitPython_ProgressBar @@ -1 +1 @@ -Subproject commit 6ba9d9d991ada6c0cea6a32bd64595cfd37e06b2 +Subproject commit fab168ab38f61f654735140c67c7defdad512de8 diff --git a/frozen/Adafruit_CircuitPython_RFM69 b/frozen/Adafruit_CircuitPython_RFM69 index 07be137bf5bda..1837cb10b353a 160000 --- a/frozen/Adafruit_CircuitPython_RFM69 +++ b/frozen/Adafruit_CircuitPython_RFM69 @@ -1 +1 @@ -Subproject commit 07be137bf5bda7a0469225c9cbb09b9a0aa08791 +Subproject commit 1837cb10b353a6349c33d87c0f045c80c08003e3 diff --git a/frozen/Adafruit_CircuitPython_RFM9x b/frozen/Adafruit_CircuitPython_RFM9x index 609aafb018b1c..ad3a28df5e0ca 160000 --- a/frozen/Adafruit_CircuitPython_RFM9x +++ b/frozen/Adafruit_CircuitPython_RFM9x @@ -1 +1 @@ -Subproject commit 609aafb018b1cf5b7f60f2a7c961b827dce7468e +Subproject commit ad3a28df5e0cab2a24b9a6acf0667fe7c5a26563 diff --git a/frozen/Adafruit_CircuitPython_Register b/frozen/Adafruit_CircuitPython_Register index 96d0a4774f552..2a7039c548456 160000 --- a/frozen/Adafruit_CircuitPython_Register +++ b/frozen/Adafruit_CircuitPython_Register @@ -1 +1 @@ -Subproject commit 96d0a4774f5525b926c131618e436b8e5c218a2f +Subproject commit 2a7039c548456ddf7573814967ce494f61adbc46 diff --git a/frozen/Adafruit_CircuitPython_Requests b/frozen/Adafruit_CircuitPython_Requests index 5e646b244cf36..44186adfc6a98 160000 --- a/frozen/Adafruit_CircuitPython_Requests +++ b/frozen/Adafruit_CircuitPython_Requests @@ -1 +1 @@ -Subproject commit 5e646b244cf36f879f15aaf77a270e4c7e6e8336 +Subproject commit 44186adfc6a982f26007cba2b103dd65cbb68c65 diff --git a/frozen/Adafruit_CircuitPython_SD b/frozen/Adafruit_CircuitPython_SD index ee4d73293c8d0..c700b45d484a7 160000 --- a/frozen/Adafruit_CircuitPython_SD +++ b/frozen/Adafruit_CircuitPython_SD @@ -1 +1 @@ -Subproject commit ee4d73293c8d059cd0c8bcf46758e62f5393cbee +Subproject commit c700b45d484a7e5054cf1856b871f3f25edb5ebb diff --git a/frozen/Adafruit_CircuitPython_SHT4x b/frozen/Adafruit_CircuitPython_SHT4x index 26a0a407d43bd..2b5317830e662 160000 --- a/frozen/Adafruit_CircuitPython_SHT4x +++ b/frozen/Adafruit_CircuitPython_SHT4x @@ -1 +1 @@ -Subproject commit 26a0a407d43bd6208deffdf577e214d899855c0e +Subproject commit 2b5317830e66233017e68f8b459261bc4240307d diff --git a/frozen/Adafruit_CircuitPython_SSD1306 b/frozen/Adafruit_CircuitPython_SSD1306 index d75b4d593cd18..04d8d531e8ed2 160000 --- a/frozen/Adafruit_CircuitPython_SSD1306 +++ b/frozen/Adafruit_CircuitPython_SSD1306 @@ -1 +1 @@ -Subproject commit d75b4d593cd184cbea5e237f5212cd9122d46263 +Subproject commit 04d8d531e8ed2df45c061c8b3c6d91abda001f13 diff --git a/frozen/Adafruit_CircuitPython_SSD1680 b/frozen/Adafruit_CircuitPython_SSD1680 index b7d511711c8f5..c98b43e11eca5 160000 --- a/frozen/Adafruit_CircuitPython_SSD1680 +++ b/frozen/Adafruit_CircuitPython_SSD1680 @@ -1 +1 @@ -Subproject commit b7d511711c8f5557082d9c4307a89ecdec608727 +Subproject commit c98b43e11eca52c9885c48e866325595f5614eb8 diff --git a/frozen/Adafruit_CircuitPython_ST7789 b/frozen/Adafruit_CircuitPython_ST7789 index 0f7269267c0d1..8fb8cbf7c4cf4 160000 --- a/frozen/Adafruit_CircuitPython_ST7789 +++ b/frozen/Adafruit_CircuitPython_ST7789 @@ -1 +1 @@ -Subproject commit 0f7269267c0d17ada34926333bbda4021e5d7cb3 +Subproject commit 8fb8cbf7c4cf404c1334df67a7512ccf99a00c52 diff --git a/frozen/Adafruit_CircuitPython_SimpleIO b/frozen/Adafruit_CircuitPython_SimpleIO index d5278d246bcf6..b024e7276cf37 160000 --- a/frozen/Adafruit_CircuitPython_SimpleIO +++ b/frozen/Adafruit_CircuitPython_SimpleIO @@ -1 +1 @@ -Subproject commit d5278d246bcf658ef5d44e7658c956fac29bd9e1 +Subproject commit b024e7276cf3796dddcced3cbe438195d310661b diff --git a/frozen/Adafruit_CircuitPython_SimpleMath b/frozen/Adafruit_CircuitPython_SimpleMath index 33f82828598a3..9ebbdae834ca3 160000 --- a/frozen/Adafruit_CircuitPython_SimpleMath +++ b/frozen/Adafruit_CircuitPython_SimpleMath @@ -1 +1 @@ -Subproject commit 33f82828598a3a10c73dfa50601fef4beac40be8 +Subproject commit 9ebbdae834ca3225bf411ba54c9e3148d8c26b6c diff --git a/frozen/Adafruit_CircuitPython_Thermistor b/frozen/Adafruit_CircuitPython_Thermistor index 2b45967cc5283..bdfeb28d741a0 160000 --- a/frozen/Adafruit_CircuitPython_Thermistor +++ b/frozen/Adafruit_CircuitPython_Thermistor @@ -1 +1 @@ -Subproject commit 2b45967cc5283e71b7826f6a158d8c8556dde287 +Subproject commit bdfeb28d741a0233c4d8789b610d116831c9c0dd diff --git a/frozen/Adafruit_CircuitPython_Ticks b/frozen/Adafruit_CircuitPython_Ticks index 6e159f899b017..415f35dceaf43 160000 --- a/frozen/Adafruit_CircuitPython_Ticks +++ b/frozen/Adafruit_CircuitPython_Ticks @@ -1 +1 @@ -Subproject commit 6e159f899b017e920a6058a6b16735af8a6e852e +Subproject commit 415f35dceaf43f6e2bbc879f7049fed5e52f3c4a diff --git a/frozen/Adafruit_CircuitPython_UC8151D b/frozen/Adafruit_CircuitPython_UC8151D index 776b932ebf769..ec3fd72eb4219 160000 --- a/frozen/Adafruit_CircuitPython_UC8151D +++ b/frozen/Adafruit_CircuitPython_UC8151D @@ -1 +1 @@ -Subproject commit 776b932ebf76937e464ab2656834e7a1c1e3434b +Subproject commit ec3fd72eb4219520c2ad5dd33e2e865a7e4b9d6a diff --git a/frozen/Adafruit_CircuitPython_Wave b/frozen/Adafruit_CircuitPython_Wave index 6fba948b024ec..10f7ed33cbf75 160000 --- a/frozen/Adafruit_CircuitPython_Wave +++ b/frozen/Adafruit_CircuitPython_Wave @@ -1 +1 @@ -Subproject commit 6fba948b024ec210b3cf1f1b068b3eebc82fe8d4 +Subproject commit 10f7ed33cbf75b0f42ffe9aae721f57583a33ce6 diff --git a/frozen/Adafruit_CircuitPython_Wiznet5k b/frozen/Adafruit_CircuitPython_Wiznet5k index 6b3484d1ee243..1e786b93a5a1d 160000 --- a/frozen/Adafruit_CircuitPython_Wiznet5k +++ b/frozen/Adafruit_CircuitPython_Wiznet5k @@ -1 +1 @@ -Subproject commit 6b3484d1ee243a7e8bc0513ab84956e1b6e2a520 +Subproject commit 1e786b93a5a1dede2fa6f44634336f666d153f6a diff --git a/frozen/Adafruit_CircuitPython_asyncio b/frozen/Adafruit_CircuitPython_asyncio index 24818f817f511..d0d63f113c7da 160000 --- a/frozen/Adafruit_CircuitPython_asyncio +++ b/frozen/Adafruit_CircuitPython_asyncio @@ -1 +1 @@ -Subproject commit 24818f817f5118f59aa696a04776049c179c0f4f +Subproject commit d0d63f113c7da0852bdc5aa303cd738e45d40fbc diff --git a/frozen/Adafruit_CircuitPython_framebuf b/frozen/Adafruit_CircuitPython_framebuf index 0fedf2f308ed6..3d2036d2926f7 160000 --- a/frozen/Adafruit_CircuitPython_framebuf +++ b/frozen/Adafruit_CircuitPython_framebuf @@ -1 +1 @@ -Subproject commit 0fedf2f308ed6b3e8261661e4810e613f33d7171 +Subproject commit 3d2036d2926f7d04a00908845dd14cb18e3d8b90 diff --git a/frozen/Adafruit_CircuitPython_seesaw b/frozen/Adafruit_CircuitPython_seesaw index 94c541f45313d..daab794981f17 160000 --- a/frozen/Adafruit_CircuitPython_seesaw +++ b/frozen/Adafruit_CircuitPython_seesaw @@ -1 +1 @@ -Subproject commit 94c541f45313dc7eb98a4cd1a6c3af39f001cc49 +Subproject commit daab794981f177041fe1a4a9266a9688c45480e9 diff --git a/frozen/CircuitPython_edupico2_paj7620 b/frozen/CircuitPython_edupico2_paj7620 new file mode 160000 index 0000000000000..16a4d78472499 --- /dev/null +++ b/frozen/CircuitPython_edupico2_paj7620 @@ -0,0 +1 @@ +Subproject commit 16a4d784724992862cffd594aeed8c627ecc18b3 diff --git a/frozen/circuitpython-stage b/frozen/circuitpython-stage index ccac175f5e9c8..7b71363acb67c 160000 --- a/frozen/circuitpython-stage +++ b/frozen/circuitpython-stage @@ -1 +1 @@ -Subproject commit ccac175f5e9c8392d436058d4364a8ca5e58abc7 +Subproject commit 7b71363acb67cb1b254b12607f35280cfb4ad6c1 diff --git a/lib/berkeley-db-1.xx b/lib/berkeley-db-1.xx index 85373b548f1fb..0f3bb6947c2f5 160000 --- a/lib/berkeley-db-1.xx +++ b/lib/berkeley-db-1.xx @@ -1 +1 @@ -Subproject commit 85373b548f1fb0119a463582570b44189dfb09ae +Subproject commit 0f3bb6947c2f57233916dccd7bb425d7bf86e5a6 diff --git a/lib/libm_dbl/libm.h b/lib/libm_dbl/libm.h index cbae6916625e2..fa49ad1cddda5 100644 --- a/lib/libm_dbl/libm.h +++ b/lib/libm_dbl/libm.h @@ -89,7 +89,9 @@ do { \ (d) = __u.f; \ } while (0) +#if !defined(DBL_EPSILON) #define DBL_EPSILON 2.22044604925031308085e-16 +#endif int __rem_pio2(double, double*); int __rem_pio2_large(double*, double*, int, int, int); diff --git a/lib/libm_dbl/rint.c b/lib/libm_dbl/rint.c index fbba390e7d723..b85dec8f2465e 100644 --- a/lib/libm_dbl/rint.c +++ b/lib/libm_dbl/rint.c @@ -2,7 +2,7 @@ #include #include -#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 +#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 || FLT_EVAL_METHOD==16 #define EPS DBL_EPSILON #elif FLT_EVAL_METHOD==2 #define EPS LDBL_EPSILON diff --git a/lib/littlefs/lfs1.c b/lib/littlefs/lfs1.c index 6a3fd670012cc..ec18dc470258c 100644 --- a/lib/littlefs/lfs1.c +++ b/lib/littlefs/lfs1.c @@ -2141,7 +2141,7 @@ int lfs1_format(lfs1_t *lfs1, const struct lfs1_config *cfg) { .d.elen = sizeof(superblock.d) - sizeof(superblock.d.magic) - 4, .d.nlen = sizeof(superblock.d.magic), .d.version = LFS1_DISK_VERSION, - .d.magic = {"littlefs"}, + .d.magic = {'l', 'i', 't', 't', 'l', 'e', 'f', 's'}, .d.block_size = lfs1->cfg->block_size, .d.block_count = lfs1->cfg->block_count, .d.root = {lfs1->root[0], lfs1->root[1]}, diff --git a/lib/littlefs/lfs2.c b/lib/littlefs/lfs2.c index 613213669c8f7..8d18b7d110521 100644 --- a/lib/littlefs/lfs2.c +++ b/lib/littlefs/lfs2.c @@ -93,6 +93,7 @@ static int lfs2_bd_read(lfs2_t *lfs2, // bypass cache? diff = lfs2_aligndown(diff, lfs2->cfg->read_size); int err = lfs2->cfg->read(lfs2->cfg, block, off, data, diff); + LFS2_ASSERT(err <= 0); if (err) { return err; } @@ -282,6 +283,21 @@ static int lfs2_bd_erase(lfs2_t *lfs2, lfs2_block_t block) { /// Small type-level utilities /// + +// some operations on paths +static inline lfs2_size_t lfs2_path_namelen(const char *path) { + return strcspn(path, "/"); +} + +static inline bool lfs2_path_islast(const char *path) { + lfs2_size_t namelen = lfs2_path_namelen(path); + return path[namelen + strspn(path + namelen, "/")] == '\0'; +} + +static inline bool lfs2_path_isdir(const char *path) { + return path[lfs2_path_namelen(path)] != '\0'; +} + // operations on block pairs static inline void lfs2_pair_swap(lfs2_block_t pair[2]) { lfs2_block_t t = pair[0]; @@ -389,18 +405,15 @@ struct lfs2_diskoff { // operations on global state static inline void lfs2_gstate_xor(lfs2_gstate_t *a, const lfs2_gstate_t *b) { - for (int i = 0; i < 3; i++) { - ((uint32_t*)a)[i] ^= ((const uint32_t*)b)[i]; - } + a->tag ^= b->tag; + a->pair[0] ^= b->pair[0]; + a->pair[1] ^= b->pair[1]; } static inline bool lfs2_gstate_iszero(const lfs2_gstate_t *a) { - for (int i = 0; i < 3; i++) { - if (((uint32_t*)a)[i] != 0) { - return false; - } - } - return true; + return a->tag == 0 + && a->pair[0] == 0 + && a->pair[1] == 0; } #ifndef LFS2_READONLY @@ -550,9 +563,9 @@ static int lfs2_dir_compact(lfs2_t *lfs2, lfs2_mdir_t *source, uint16_t begin, uint16_t end); static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file, const void *buffer, lfs2_size_t size); -static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_ssize_t lfs2_file_write_(lfs2_t *lfs2, lfs2_file_t *file, const void *buffer, lfs2_size_t size); -static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file); +static int lfs2_file_sync_(lfs2_t *lfs2, lfs2_file_t *file); static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file); static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file); @@ -574,65 +587,72 @@ static int lfs21_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); #endif -static int lfs2_dir_rawrewind(lfs2_t *lfs2, lfs2_dir_t *dir); +static int lfs2_dir_rewind_(lfs2_t *lfs2, lfs2_dir_t *dir); static lfs2_ssize_t lfs2_file_flushedread(lfs2_t *lfs2, lfs2_file_t *file, void *buffer, lfs2_size_t size); -static lfs2_ssize_t lfs2_file_rawread(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_ssize_t lfs2_file_read_(lfs2_t *lfs2, lfs2_file_t *file, void *buffer, lfs2_size_t size); -static int lfs2_file_rawclose(lfs2_t *lfs2, lfs2_file_t *file); -static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file); +static int lfs2_file_close_(lfs2_t *lfs2, lfs2_file_t *file); +static lfs2_soff_t lfs2_file_size_(lfs2_t *lfs2, lfs2_file_t *file); -static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2); -static int lfs2_fs_rawtraverse(lfs2_t *lfs2, +static lfs2_ssize_t lfs2_fs_size_(lfs2_t *lfs2); +static int lfs2_fs_traverse_(lfs2_t *lfs2, int (*cb)(void *data, lfs2_block_t block), void *data, bool includeorphans); static int lfs2_deinit(lfs2_t *lfs2); -static int lfs2_rawunmount(lfs2_t *lfs2); +static int lfs2_unmount_(lfs2_t *lfs2); /// Block allocator /// + +// allocations should call this when all allocated blocks are committed to +// the filesystem +// +// after a checkpoint, the block allocator may realloc any untracked blocks +static void lfs2_alloc_ckpoint(lfs2_t *lfs2) { + lfs2->lookahead.ckpoint = lfs2->block_count; +} + +// drop the lookahead buffer, this is done during mounting and failed +// traversals in order to avoid invalid lookahead state +static void lfs2_alloc_drop(lfs2_t *lfs2) { + lfs2->lookahead.size = 0; + lfs2->lookahead.next = 0; + lfs2_alloc_ckpoint(lfs2); +} + #ifndef LFS2_READONLY static int lfs2_alloc_lookahead(void *p, lfs2_block_t block) { lfs2_t *lfs2 = (lfs2_t*)p; - lfs2_block_t off = ((block - lfs2->free.off) + lfs2_block_t off = ((block - lfs2->lookahead.start) + lfs2->block_count) % lfs2->block_count; - if (off < lfs2->free.size) { - lfs2->free.buffer[off / 32] |= 1U << (off % 32); + if (off < lfs2->lookahead.size) { + lfs2->lookahead.buffer[off / 8] |= 1U << (off % 8); } return 0; } #endif -// indicate allocated blocks have been committed into the filesystem, this -// is to prevent blocks from being garbage collected in the middle of a -// commit operation -static void lfs2_alloc_ack(lfs2_t *lfs2) { - lfs2->free.ack = lfs2->block_count; -} - -// drop the lookahead buffer, this is done during mounting and failed -// traversals in order to avoid invalid lookahead state -static void lfs2_alloc_drop(lfs2_t *lfs2) { - lfs2->free.size = 0; - lfs2->free.i = 0; - lfs2_alloc_ack(lfs2); -} - #ifndef LFS2_READONLY -static int lfs2_fs_rawgc(lfs2_t *lfs2) { - // Move free offset at the first unused block (lfs2->free.i) - // lfs2->free.i is equal lfs2->free.size when all blocks are used - lfs2->free.off = (lfs2->free.off + lfs2->free.i) % lfs2->block_count; - lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, lfs2->free.ack); - lfs2->free.i = 0; +static int lfs2_alloc_scan(lfs2_t *lfs2) { + // move lookahead buffer to the first unused block + // + // note we limit the lookahead buffer to at most the amount of blocks + // checkpointed, this prevents the math in lfs2_alloc from underflowing + lfs2->lookahead.start = (lfs2->lookahead.start + lfs2->lookahead.next) + % lfs2->block_count; + lfs2->lookahead.next = 0; + lfs2->lookahead.size = lfs2_min( + 8*lfs2->cfg->lookahead_size, + lfs2->lookahead.ckpoint); // find mask of free blocks from tree - memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); - int err = lfs2_fs_rawtraverse(lfs2, lfs2_alloc_lookahead, lfs2, true); + memset(lfs2->lookahead.buffer, 0, lfs2->cfg->lookahead_size); + int err = lfs2_fs_traverse_(lfs2, lfs2_alloc_lookahead, lfs2, true); if (err) { lfs2_alloc_drop(lfs2); return err; @@ -645,36 +665,49 @@ static int lfs2_fs_rawgc(lfs2_t *lfs2) { #ifndef LFS2_READONLY static int lfs2_alloc(lfs2_t *lfs2, lfs2_block_t *block) { while (true) { - while (lfs2->free.i != lfs2->free.size) { - lfs2_block_t off = lfs2->free.i; - lfs2->free.i += 1; - lfs2->free.ack -= 1; - - if (!(lfs2->free.buffer[off / 32] & (1U << (off % 32)))) { + // scan our lookahead buffer for free blocks + while (lfs2->lookahead.next < lfs2->lookahead.size) { + if (!(lfs2->lookahead.buffer[lfs2->lookahead.next / 8] + & (1U << (lfs2->lookahead.next % 8)))) { // found a free block - *block = (lfs2->free.off + off) % lfs2->block_count; - - // eagerly find next off so an alloc ack can - // discredit old lookahead blocks - while (lfs2->free.i != lfs2->free.size && - (lfs2->free.buffer[lfs2->free.i / 32] - & (1U << (lfs2->free.i % 32)))) { - lfs2->free.i += 1; - lfs2->free.ack -= 1; + *block = (lfs2->lookahead.start + lfs2->lookahead.next) + % lfs2->block_count; + + // eagerly find next free block to maximize how many blocks + // lfs2_alloc_ckpoint makes available for scanning + while (true) { + lfs2->lookahead.next += 1; + lfs2->lookahead.ckpoint -= 1; + + if (lfs2->lookahead.next >= lfs2->lookahead.size + || !(lfs2->lookahead.buffer[lfs2->lookahead.next / 8] + & (1U << (lfs2->lookahead.next % 8)))) { + return 0; + } } - - return 0; } + + lfs2->lookahead.next += 1; + lfs2->lookahead.ckpoint -= 1; } - // check if we have looked at all blocks since last ack - if (lfs2->free.ack == 0) { - LFS2_ERROR("No more free space %"PRIu32, - lfs2->free.i + lfs2->free.off); + // In order to keep our block allocator from spinning forever when our + // filesystem is full, we mark points where there are no in-flight + // allocations with a checkpoint before starting a set of allocations. + // + // If we've looked at all blocks since the last checkpoint, we report + // the filesystem as out of storage. + // + if (lfs2->lookahead.ckpoint <= 0) { + LFS2_ERROR("No more free space 0x%"PRIx32, + (lfs2->lookahead.start + lfs2->lookahead.next) + % lfs2->block_count); return LFS2_ERR_NOSPC; } - int err = lfs2_fs_rawgc(lfs2); + // No blocks in our lookahead buffer, we need to scan the filesystem for + // unused blocks in the next lookahead window. + int err = lfs2_alloc_scan(lfs2); if(err) { return err; } @@ -690,11 +723,14 @@ static lfs2_stag_t lfs2_dir_getslice(lfs2_t *lfs2, const lfs2_mdir_t *dir, lfs2_tag_t ntag = dir->etag; lfs2_stag_t gdiff = 0; + // synthetic moves if (lfs2_gstate_hasmovehere(&lfs2->gdisk, dir->pair) && - lfs2_tag_id(gmask) != 0 && - lfs2_tag_id(lfs2->gdisk.tag) <= lfs2_tag_id(gtag)) { - // synthetic moves - gdiff -= LFS2_MKTAG(0, 1, 0); + lfs2_tag_id(gmask) != 0) { + if (lfs2_tag_id(lfs2->gdisk.tag) == lfs2_tag_id(gtag)) { + return LFS2_ERR_NOENT; + } else if (lfs2_tag_id(lfs2->gdisk.tag) < lfs2_tag_id(gtag)) { + gdiff -= LFS2_MKTAG(0, 1, 0); + } } // iterate over dir block backwards (for faster lookups) @@ -704,6 +740,7 @@ static lfs2_stag_t lfs2_dir_getslice(lfs2_t *lfs2, const lfs2_mdir_t *dir, int err = lfs2_bd_read(lfs2, NULL, &lfs2->rcache, sizeof(ntag), dir->pair[0], off, &ntag, sizeof(ntag)); + LFS2_ASSERT(err <= 0); if (err) { return err; } @@ -732,6 +769,7 @@ static lfs2_stag_t lfs2_dir_getslice(lfs2_t *lfs2, const lfs2_mdir_t *dir, err = lfs2_bd_read(lfs2, NULL, &lfs2->rcache, diff, dir->pair[0], off+sizeof(tag)+goff, gbuffer, diff); + LFS2_ASSERT(err <= 0); if (err) { return err; } @@ -793,9 +831,6 @@ static int lfs2_dir_getread(lfs2_t *lfs2, const lfs2_mdir_t *dir, size -= diff; continue; } - - // rcache takes priority - diff = lfs2_min(diff, rcache->off-off); } // load to cache, first condition can no longer fail @@ -1247,6 +1282,7 @@ static lfs2_stag_t lfs2_dir_fetchmatch(lfs2_t *lfs2, if (err == LFS2_ERR_CORRUPT) { break; } + return err; } lfs2_fcrc_fromle32(&fcrc); @@ -1438,32 +1474,46 @@ static int lfs2_dir_find_match(void *data, return LFS2_CMP_EQ; } +// lfs2_dir_find tries to set path and id even if file is not found +// +// returns: +// - 0 if file is found +// - LFS2_ERR_NOENT if file or parent is not found +// - LFS2_ERR_NOTDIR if parent is not a dir static lfs2_stag_t lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir, const char **path, uint16_t *id) { // we reduce path to a single name if we can find it const char *name = *path; - if (id) { - *id = 0x3ff; - } // default to root dir lfs2_stag_t tag = LFS2_MKTAG(LFS2_TYPE_DIR, 0x3ff, 0); dir->tail[0] = lfs2->root[0]; dir->tail[1] = lfs2->root[1]; + // empty paths are not allowed + if (*name == '\0') { + return LFS2_ERR_INVAL; + } + while (true) { nextname: - // skip slashes - name += strspn(name, "/"); + // skip slashes if we're a directory + if (lfs2_tag_type3(tag) == LFS2_TYPE_DIR) { + name += strspn(name, "/"); + } lfs2_size_t namelen = strcspn(name, "/"); - // skip '.' and root '..' - if ((namelen == 1 && memcmp(name, ".", 1) == 0) || - (namelen == 2 && memcmp(name, "..", 2) == 0)) { + // skip '.' + if (namelen == 1 && memcmp(name, ".", 1) == 0) { name += namelen; goto nextname; } + // error on unmatched '..', trying to go above root? + if (namelen == 2 && memcmp(name, "..", 2) == 0) { + return LFS2_ERR_INVAL; + } + // skip if matched by '..' in name const char *suffix = name + namelen; lfs2_size_t sufflen; @@ -1475,7 +1525,9 @@ static lfs2_stag_t lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir, break; } - if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { + if (sufflen == 1 && memcmp(suffix, ".", 1) == 0) { + // noop + } else if (sufflen == 2 && memcmp(suffix, "..", 2) == 0) { depth -= 1; if (depth == 0) { name = suffix + sufflen; @@ -1489,14 +1541,14 @@ static lfs2_stag_t lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir, } // found path - if (name[0] == '\0') { + if (*name == '\0') { return tag; } // update what we've found so far *path = name; - // only continue if we hit a directory + // only continue if we're a directory if (lfs2_tag_type3(tag) != LFS2_TYPE_DIR) { return LFS2_ERR_NOTDIR; } @@ -1516,8 +1568,7 @@ static lfs2_stag_t lfs2_dir_find(lfs2_t *lfs2, lfs2_mdir_t *dir, tag = lfs2_dir_fetchmatch(lfs2, dir, dir->tail, LFS2_MKTAG(0x780, 0, 0), LFS2_MKTAG(LFS2_TYPE_NAME, 0, namelen), - // are we last name? - (strchr(name, '/') == NULL) ? id : NULL, + id, lfs2_dir_find_match, &(struct lfs2_dir_find_match){ lfs2, name, namelen}); if (tag < 0) { @@ -2105,13 +2156,14 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir, // And we cap at half a block to avoid degenerate cases with // nearly-full metadata blocks. // + lfs2_size_t metadata_max = (lfs2->cfg->metadata_max) + ? lfs2->cfg->metadata_max + : lfs2->cfg->block_size; if (end - split < 0xff && size <= lfs2_min( - lfs2->cfg->block_size - 40, + metadata_max - 40, lfs2_alignup( - (lfs2->cfg->metadata_max - ? lfs2->cfg->metadata_max - : lfs2->cfg->block_size)/2, + metadata_max/2, lfs2->cfg->prog_size))) { break; } @@ -2146,14 +2198,16 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir, && lfs2_pair_cmp(dir->pair, (const lfs2_block_t[2]){0, 1}) == 0) { // oh no! we're writing too much to the superblock, // should we expand? - lfs2_ssize_t size = lfs2_fs_rawsize(lfs2); + lfs2_ssize_t size = lfs2_fs_size_(lfs2); if (size < 0) { return size; } - // do we have extra space? littlefs can't reclaim this space - // by itself, so expand cautiously - if ((lfs2_size_t)size < lfs2->block_count/2) { + // littlefs cannot reclaim expanded superblocks, so expand cautiously + // + // if our filesystem is more than ~88% full, don't expand, this is + // somewhat arbitrary + if (lfs2->block_count - size > lfs2->block_count/8) { LFS2_DEBUG("Expanding superblock at rev %"PRIu32, dir->rev); int err = lfs2_dir_split(lfs2, dir, attrs, attrcount, source, begin, end); @@ -2166,7 +2220,8 @@ static int lfs2_dir_splittingcompact(lfs2_t *lfs2, lfs2_mdir_t *dir, // we can do, we'll error later if we've become frozen LFS2_WARN("Unable to expand superblock"); } else { - end = begin; + // duplicate the superblock entry into the new superblock + end = 1; } } } @@ -2213,7 +2268,7 @@ static int lfs2_dir_relocatingcommit(lfs2_t *lfs2, lfs2_mdir_t *dir, } } - if (dir->erased) { + if (dir->erased && dir->count < 0xff) { // try to commit struct lfs2_commit commit = { .block = dir->pair[0], @@ -2312,7 +2367,8 @@ fixmlist:; if (d->m.pair != pair) { for (int i = 0; i < attrcount; i++) { if (lfs2_tag_type3(attrs[i].tag) == LFS2_TYPE_DELETE && - d->id == lfs2_tag_id(attrs[i].tag)) { + d->id == lfs2_tag_id(attrs[i].tag) && + d->type != LFS2_TYPE_DIR) { d->m.pair[0] = LFS2_BLOCK_NULL; d->m.pair[1] = LFS2_BLOCK_NULL; } else if (lfs2_tag_type3(attrs[i].tag) == LFS2_TYPE_DELETE && @@ -2333,7 +2389,9 @@ fixmlist:; while (d->id >= d->m.count && d->m.split) { // we split and id is on tail now - d->id -= d->m.count; + if (lfs2_pair_cmp(d->m.tail, lfs2->root) != 0) { + d->id -= d->m.count; + } int err = lfs2_dir_fetch(lfs2, &d->m, d->m.tail); if (err) { return err; @@ -2499,7 +2557,7 @@ static int lfs2_dir_orphaningcommit(lfs2_t *lfs2, lfs2_mdir_t *dir, if (err != LFS2_ERR_NOENT) { if (lfs2_gstate_hasorphans(&lfs2->gstate)) { // next step, clean up orphans - err = lfs2_fs_preporphans(lfs2, -hasparent); + err = lfs2_fs_preporphans(lfs2, -(int8_t)hasparent); if (err) { return err; } @@ -2564,7 +2622,7 @@ static int lfs2_dir_commit(lfs2_t *lfs2, lfs2_mdir_t *dir, /// Top level directory operations /// #ifndef LFS2_READONLY -static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) { +static int lfs2_mkdir_(lfs2_t *lfs2, const char *path) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs2_fs_forceconsistency(lfs2); if (err) { @@ -2575,18 +2633,18 @@ static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) { cwd.next = lfs2->mlist; uint16_t id; err = lfs2_dir_find(lfs2, &cwd.m, &path, &id); - if (!(err == LFS2_ERR_NOENT && id != 0x3ff)) { + if (!(err == LFS2_ERR_NOENT && lfs2_path_islast(path))) { return (err < 0) ? err : LFS2_ERR_EXIST; } // check that name fits - lfs2_size_t nlen = strlen(path); + lfs2_size_t nlen = lfs2_path_namelen(path); if (nlen > lfs2->name_max) { return LFS2_ERR_NAMETOOLONG; } // build up new directory - lfs2_alloc_ack(lfs2); + lfs2_alloc_ckpoint(lfs2); lfs2_mdir_t dir; err = lfs2_dir_alloc(lfs2, &dir); if (err) { @@ -2660,7 +2718,7 @@ static int lfs2_rawmkdir(lfs2_t *lfs2, const char *path) { } #endif -static int lfs2_dir_rawopen(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { +static int lfs2_dir_open_(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { lfs2_stag_t tag = lfs2_dir_find(lfs2, &dir->m, &path, NULL); if (tag < 0) { return tag; @@ -2704,14 +2762,14 @@ static int lfs2_dir_rawopen(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { return 0; } -static int lfs2_dir_rawclose(lfs2_t *lfs2, lfs2_dir_t *dir) { +static int lfs2_dir_close_(lfs2_t *lfs2, lfs2_dir_t *dir) { // remove from list of mdirs lfs2_mlist_remove(lfs2, (struct lfs2_mlist *)dir); return 0; } -static int lfs2_dir_rawread(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { +static int lfs2_dir_read_(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { memset(info, 0, sizeof(*info)); // special offset for '.' and '..' @@ -2756,9 +2814,9 @@ static int lfs2_dir_rawread(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *inf return true; } -static int lfs2_dir_rawseek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { +static int lfs2_dir_seek_(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { // simply walk from head dir - int err = lfs2_dir_rawrewind(lfs2, dir); + int err = lfs2_dir_rewind_(lfs2, dir); if (err) { return err; } @@ -2793,12 +2851,12 @@ static int lfs2_dir_rawseek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { return 0; } -static lfs2_soff_t lfs2_dir_rawtell(lfs2_t *lfs2, lfs2_dir_t *dir) { +static lfs2_soff_t lfs2_dir_tell_(lfs2_t *lfs2, lfs2_dir_t *dir) { (void)lfs2; return dir->pos; } -static int lfs2_dir_rawrewind(lfs2_t *lfs2, lfs2_dir_t *dir) { +static int lfs2_dir_rewind_(lfs2_t *lfs2, lfs2_dir_t *dir) { // reload the head dir int err = lfs2_dir_fetch(lfs2, &dir->m, dir->head); if (err) { @@ -3004,7 +3062,7 @@ static int lfs2_ctz_traverse(lfs2_t *lfs2, /// Top level file operations /// -static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file, +static int lfs2_file_opencfg_(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags, const struct lfs2_file_config *cfg) { #ifndef LFS2_READONLY @@ -3029,7 +3087,7 @@ static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file, // allocate entry for file if it doesn't exist lfs2_stag_t tag = lfs2_dir_find(lfs2, &file->m, &path, &file->id); - if (tag < 0 && !(tag == LFS2_ERR_NOENT && file->id != 0x3ff)) { + if (tag < 0 && !(tag == LFS2_ERR_NOENT && lfs2_path_islast(path))) { err = tag; goto cleanup; } @@ -3049,8 +3107,14 @@ static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file, goto cleanup; } + // don't allow trailing slashes + if (lfs2_path_isdir(path)) { + err = LFS2_ERR_NOTDIR; + goto cleanup; + } + // check that name fits - lfs2_size_t nlen = strlen(path); + lfs2_size_t nlen = lfs2_path_namelen(path); if (nlen > lfs2->name_max) { err = LFS2_ERR_NAMETOOLONG; goto cleanup; @@ -3166,22 +3230,22 @@ static int lfs2_file_rawopencfg(lfs2_t *lfs2, lfs2_file_t *file, #ifndef LFS2_READONLY file->flags |= LFS2_F_ERRED; #endif - lfs2_file_rawclose(lfs2, file); + lfs2_file_close_(lfs2, file); return err; } #ifndef LFS2_NO_MALLOC -static int lfs2_file_rawopen(lfs2_t *lfs2, lfs2_file_t *file, +static int lfs2_file_open_(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags) { static const struct lfs2_file_config defaults = {0}; - int err = lfs2_file_rawopencfg(lfs2, file, path, flags, &defaults); + int err = lfs2_file_opencfg_(lfs2, file, path, flags, &defaults); return err; } #endif -static int lfs2_file_rawclose(lfs2_t *lfs2, lfs2_file_t *file) { +static int lfs2_file_close_(lfs2_t *lfs2, lfs2_file_t *file) { #ifndef LFS2_READONLY - int err = lfs2_file_rawsync(lfs2, file); + int err = lfs2_file_sync_(lfs2, file); #else int err = 0; #endif @@ -3272,7 +3336,7 @@ static int lfs2_file_relocate(lfs2_t *lfs2, lfs2_file_t *file) { #ifndef LFS2_READONLY static int lfs2_file_outline(lfs2_t *lfs2, lfs2_file_t *file) { file->off = file->pos; - lfs2_alloc_ack(lfs2); + lfs2_alloc_ckpoint(lfs2); int err = lfs2_file_relocate(lfs2, file); if (err) { return err; @@ -3364,7 +3428,7 @@ static int lfs2_file_flush(lfs2_t *lfs2, lfs2_file_t *file) { } #ifndef LFS2_READONLY -static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file) { +static int lfs2_file_sync_(lfs2_t *lfs2, lfs2_file_t *file) { if (file->flags & LFS2_F_ERRED) { // it's not safe to do anything if our file errored return 0; @@ -3379,6 +3443,15 @@ static int lfs2_file_rawsync(lfs2_t *lfs2, lfs2_file_t *file) { if ((file->flags & LFS2_F_DIRTY) && !lfs2_pair_isnull(file->m.pair)) { + // before we commit metadata, we need sync the disk to make sure + // data writes don't complete after metadata writes + if (!(file->flags & LFS2_F_INLINE)) { + err = lfs2_bd_sync(lfs2, &lfs2->pcache, &lfs2->rcache, false); + if (err) { + return err; + } + } + // update dir entry uint16_t type; const void *buffer; @@ -3477,7 +3550,7 @@ static lfs2_ssize_t lfs2_file_flushedread(lfs2_t *lfs2, lfs2_file_t *file, return size; } -static lfs2_ssize_t lfs2_file_rawread(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_ssize_t lfs2_file_read_(lfs2_t *lfs2, lfs2_file_t *file, void *buffer, lfs2_size_t size) { LFS2_ASSERT((file->flags & LFS2_O_RDONLY) == LFS2_O_RDONLY); @@ -3502,11 +3575,7 @@ static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file, lfs2_size_t nsize = size; if ((file->flags & LFS2_F_INLINE) && - lfs2_max(file->pos+nsize, file->ctz.size) > - lfs2_min(0x3fe, lfs2_min( - lfs2->cfg->cache_size, - (lfs2->cfg->metadata_max ? - lfs2->cfg->metadata_max : lfs2->cfg->block_size) / 8))) { + lfs2_max(file->pos+nsize, file->ctz.size) > lfs2->inline_max) { // inline file doesn't fit anymore int err = lfs2_file_outline(lfs2, file); if (err) { @@ -3535,7 +3604,7 @@ static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file, } // extend file with new blocks - lfs2_alloc_ack(lfs2); + lfs2_alloc_ckpoint(lfs2); int err = lfs2_ctz_extend(lfs2, &file->cache, &lfs2->rcache, file->block, file->pos, &file->block, &file->off); @@ -3578,13 +3647,13 @@ static lfs2_ssize_t lfs2_file_flushedwrite(lfs2_t *lfs2, lfs2_file_t *file, data += diff; nsize -= diff; - lfs2_alloc_ack(lfs2); + lfs2_alloc_ckpoint(lfs2); } return size; } -static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_ssize_t lfs2_file_write_(lfs2_t *lfs2, lfs2_file_t *file, const void *buffer, lfs2_size_t size) { LFS2_ASSERT((file->flags & LFS2_O_WRONLY) == LFS2_O_WRONLY); @@ -3628,25 +3697,19 @@ static lfs2_ssize_t lfs2_file_rawwrite(lfs2_t *lfs2, lfs2_file_t *file, } #endif -static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file, +static lfs2_soff_t lfs2_file_seek_(lfs2_t *lfs2, lfs2_file_t *file, lfs2_soff_t off, int whence) { // find new pos + // + // fortunately for us, littlefs is limited to 31-bit file sizes, so we + // don't have to worry too much about integer overflow lfs2_off_t npos = file->pos; if (whence == LFS2_SEEK_SET) { npos = off; } else if (whence == LFS2_SEEK_CUR) { - if ((lfs2_soff_t)file->pos + off < 0) { - return LFS2_ERR_INVAL; - } else { - npos = file->pos + off; - } + npos = file->pos + (lfs2_off_t)off; } else if (whence == LFS2_SEEK_END) { - lfs2_soff_t res = lfs2_file_rawsize(lfs2, file) + off; - if (res < 0) { - return LFS2_ERR_INVAL; - } else { - npos = res; - } + npos = (lfs2_off_t)lfs2_file_size_(lfs2, file) + (lfs2_off_t)off; } if (npos > lfs2->file_max) { @@ -3661,13 +3724,8 @@ static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file, // if we're only reading and our new offset is still in the file's cache // we can avoid flushing and needing to reread the data - if ( -#ifndef LFS2_READONLY - !(file->flags & LFS2_F_WRITING) -#else - true -#endif - ) { + if ((file->flags & LFS2_F_READING) + && file->off != lfs2->cfg->block_size) { int oindex = lfs2_ctz_index(lfs2, &(lfs2_off_t){file->pos}); lfs2_off_t noff = npos; int nindex = lfs2_ctz_index(lfs2, &noff); @@ -3692,7 +3750,7 @@ static lfs2_soff_t lfs2_file_rawseek(lfs2_t *lfs2, lfs2_file_t *file, } #ifndef LFS2_READONLY -static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { +static int lfs2_file_truncate_(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { LFS2_ASSERT((file->flags & LFS2_O_WRONLY) == LFS2_O_WRONLY); if (size > LFS2_FILE_MAX) { @@ -3700,15 +3758,12 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz } lfs2_off_t pos = file->pos; - lfs2_off_t oldsize = lfs2_file_rawsize(lfs2, file); + lfs2_off_t oldsize = lfs2_file_size_(lfs2, file); if (size < oldsize) { // revert to inline file? - if (size <= lfs2_min(0x3fe, lfs2_min( - lfs2->cfg->cache_size, - (lfs2->cfg->metadata_max ? - lfs2->cfg->metadata_max : lfs2->cfg->block_size) / 8))) { + if (size <= lfs2->inline_max) { // flush+seek to head - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_SET); + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, 0, LFS2_SEEK_SET); if (res < 0) { return (int)res; } @@ -3753,14 +3808,14 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz } } else if (size > oldsize) { // flush+seek if not already at end - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_END); + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, 0, LFS2_SEEK_END); if (res < 0) { return (int)res; } // fill with zeros while (file->pos < size) { - res = lfs2_file_rawwrite(lfs2, file, &(uint8_t){0}, 1); + res = lfs2_file_write_(lfs2, file, &(uint8_t){0}, 1); if (res < 0) { return (int)res; } @@ -3768,7 +3823,7 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz } // restore pos - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, pos, LFS2_SEEK_SET); + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, pos, LFS2_SEEK_SET); if (res < 0) { return (int)res; } @@ -3777,13 +3832,13 @@ static int lfs2_file_rawtruncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t siz } #endif -static lfs2_soff_t lfs2_file_rawtell(lfs2_t *lfs2, lfs2_file_t *file) { +static lfs2_soff_t lfs2_file_tell_(lfs2_t *lfs2, lfs2_file_t *file) { (void)lfs2; return file->pos; } -static int lfs2_file_rawrewind(lfs2_t *lfs2, lfs2_file_t *file) { - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, 0, LFS2_SEEK_SET); +static int lfs2_file_rewind_(lfs2_t *lfs2, lfs2_file_t *file) { + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, 0, LFS2_SEEK_SET); if (res < 0) { return (int)res; } @@ -3791,7 +3846,7 @@ static int lfs2_file_rawrewind(lfs2_t *lfs2, lfs2_file_t *file) { return 0; } -static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file) { +static lfs2_soff_t lfs2_file_size_(lfs2_t *lfs2, lfs2_file_t *file) { (void)lfs2; #ifndef LFS2_READONLY @@ -3805,18 +3860,24 @@ static lfs2_soff_t lfs2_file_rawsize(lfs2_t *lfs2, lfs2_file_t *file) { /// General fs operations /// -static int lfs2_rawstat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { +static int lfs2_stat_(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { lfs2_mdir_t cwd; lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); if (tag < 0) { return (int)tag; } + // only allow trailing slashes on dirs + if (strchr(path, '/') != NULL + && lfs2_tag_type3(tag) != LFS2_TYPE_DIR) { + return LFS2_ERR_NOTDIR; + } + return lfs2_dir_getinfo(lfs2, &cwd, lfs2_tag_id(tag), info); } #ifndef LFS2_READONLY -static int lfs2_rawremove(lfs2_t *lfs2, const char *path) { +static int lfs2_remove_(lfs2_t *lfs2, const char *path) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs2_fs_forceconsistency(lfs2); if (err) { @@ -3872,7 +3933,9 @@ static int lfs2_rawremove(lfs2_t *lfs2, const char *path) { } lfs2->mlist = dir.next; - if (lfs2_tag_type3(tag) == LFS2_TYPE_DIR) { + if (lfs2_gstate_hasorphans(&lfs2->gstate)) { + LFS2_ASSERT(lfs2_tag_type3(tag) == LFS2_TYPE_DIR); + // fix orphan err = lfs2_fs_preporphans(lfs2, -1); if (err) { @@ -3895,7 +3958,7 @@ static int lfs2_rawremove(lfs2_t *lfs2, const char *path) { #endif #ifndef LFS2_READONLY -static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { +static int lfs2_rename_(lfs2_t *lfs2, const char *oldpath, const char *newpath) { // deorphan if we haven't yet, needed at most once after poweron int err = lfs2_fs_forceconsistency(lfs2); if (err) { @@ -3914,7 +3977,7 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath uint16_t newid; lfs2_stag_t prevtag = lfs2_dir_find(lfs2, &newcwd, &newpath, &newid); if ((prevtag < 0 || lfs2_tag_id(prevtag) == 0x3ff) && - !(prevtag == LFS2_ERR_NOENT && newid != 0x3ff)) { + !(prevtag == LFS2_ERR_NOENT && lfs2_path_islast(newpath))) { return (prevtag < 0) ? (int)prevtag : LFS2_ERR_INVAL; } @@ -3925,8 +3988,14 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath struct lfs2_mlist prevdir; prevdir.next = lfs2->mlist; if (prevtag == LFS2_ERR_NOENT) { + // if we're a file, don't allow trailing slashes + if (lfs2_path_isdir(newpath) + && lfs2_tag_type3(oldtag) != LFS2_TYPE_DIR) { + return LFS2_ERR_NOTDIR; + } + // check that name fits - lfs2_size_t nlen = strlen(newpath); + lfs2_size_t nlen = lfs2_path_namelen(newpath); if (nlen > lfs2->name_max) { return LFS2_ERR_NAMETOOLONG; } @@ -3938,7 +4007,9 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath newoldid += 1; } } else if (lfs2_tag_type3(prevtag) != lfs2_tag_type3(oldtag)) { - return LFS2_ERR_ISDIR; + return (lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR) + ? LFS2_ERR_ISDIR + : LFS2_ERR_NOTDIR; } else if (samepair && newid == newoldid) { // we're renaming to ourselves?? return 0; @@ -3984,7 +4055,8 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath {LFS2_MKTAG_IF(prevtag != LFS2_ERR_NOENT, LFS2_TYPE_DELETE, newid, 0), NULL}, {LFS2_MKTAG(LFS2_TYPE_CREATE, newid, 0), NULL}, - {LFS2_MKTAG(lfs2_tag_type3(oldtag), newid, strlen(newpath)), newpath}, + {LFS2_MKTAG(lfs2_tag_type3(oldtag), + newid, lfs2_path_namelen(newpath)), newpath}, {LFS2_MKTAG(LFS2_FROM_MOVE, newid, lfs2_tag_id(oldtag)), &oldcwd}, {LFS2_MKTAG_IF(samepair, LFS2_TYPE_DELETE, newoldid, 0), NULL})); @@ -4007,8 +4079,10 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath } lfs2->mlist = prevdir.next; - if (prevtag != LFS2_ERR_NOENT - && lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR) { + if (lfs2_gstate_hasorphans(&lfs2->gstate)) { + LFS2_ASSERT(prevtag != LFS2_ERR_NOENT + && lfs2_tag_type3(prevtag) == LFS2_TYPE_DIR); + // fix orphan err = lfs2_fs_preporphans(lfs2, -1); if (err) { @@ -4030,7 +4104,7 @@ static int lfs2_rawrename(lfs2_t *lfs2, const char *oldpath, const char *newpath } #endif -static lfs2_ssize_t lfs2_rawgetattr(lfs2_t *lfs2, const char *path, +static lfs2_ssize_t lfs2_getattr_(lfs2_t *lfs2, const char *path, uint8_t type, void *buffer, lfs2_size_t size) { lfs2_mdir_t cwd; lfs2_stag_t tag = lfs2_dir_find(lfs2, &cwd, &path, NULL); @@ -4088,7 +4162,7 @@ static int lfs2_commitattr(lfs2_t *lfs2, const char *path, #endif #ifndef LFS2_READONLY -static int lfs2_rawsetattr(lfs2_t *lfs2, const char *path, +static int lfs2_setattr_(lfs2_t *lfs2, const char *path, uint8_t type, const void *buffer, lfs2_size_t size) { if (size > lfs2->attr_max) { return LFS2_ERR_NOSPC; @@ -4099,13 +4173,28 @@ static int lfs2_rawsetattr(lfs2_t *lfs2, const char *path, #endif #ifndef LFS2_READONLY -static int lfs2_rawremoveattr(lfs2_t *lfs2, const char *path, uint8_t type) { +static int lfs2_removeattr_(lfs2_t *lfs2, const char *path, uint8_t type) { return lfs2_commitattr(lfs2, path, type, NULL, 0x3ff); } #endif /// Filesystem operations /// + +// compile time checks, see lfs2.h for why these limits exist +#if LFS2_NAME_MAX > 1022 +#error "Invalid LFS2_NAME_MAX, must be <= 1022" +#endif + +#if LFS2_FILE_MAX > 2147483647 +#error "Invalid LFS2_FILE_MAX, must be <= 2147483647" +#endif + +#if LFS2_ATTR_MAX > 1022 +#error "Invalid LFS2_ATTR_MAX, must be <= 1022" +#endif + +// common filesystem initialization static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { lfs2->cfg = cfg; lfs2->block_count = cfg->block_count; // May be 0 @@ -4126,6 +4215,14 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { // which littlefs currently does not support LFS2_ASSERT((bool)0x80000000); + // check that the required io functions are provided + LFS2_ASSERT(lfs2->cfg->read != NULL); +#ifndef LFS2_READONLY + LFS2_ASSERT(lfs2->cfg->prog != NULL); + LFS2_ASSERT(lfs2->cfg->erase != NULL); + LFS2_ASSERT(lfs2->cfg->sync != NULL); +#endif + // validate that the lfs2-cfg sizes were initiated properly before // performing any arithmetic logics with them LFS2_ASSERT(lfs2->cfg->read_size != 0); @@ -4153,6 +4250,23 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { // wear-leveling. LFS2_ASSERT(lfs2->cfg->block_cycles != 0); + // check that compact_thresh makes sense + // + // metadata can't be compacted below block_size/2, and metadata can't + // exceed a block_size + LFS2_ASSERT(lfs2->cfg->compact_thresh == 0 + || lfs2->cfg->compact_thresh >= lfs2->cfg->block_size/2); + LFS2_ASSERT(lfs2->cfg->compact_thresh == (lfs2_size_t)-1 + || lfs2->cfg->compact_thresh <= lfs2->cfg->block_size); + + // check that metadata_max is a multiple of read_size and prog_size, + // and a factor of the block_size + LFS2_ASSERT(!lfs2->cfg->metadata_max + || lfs2->cfg->metadata_max % lfs2->cfg->read_size == 0); + LFS2_ASSERT(!lfs2->cfg->metadata_max + || lfs2->cfg->metadata_max % lfs2->cfg->prog_size == 0); + LFS2_ASSERT(!lfs2->cfg->metadata_max + || lfs2->cfg->block_size % lfs2->cfg->metadata_max == 0); // setup read cache if (lfs2->cfg->read_buffer) { @@ -4180,15 +4294,14 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { lfs2_cache_zero(lfs2, &lfs2->rcache); lfs2_cache_zero(lfs2, &lfs2->pcache); - // setup lookahead, must be multiple of 64-bits, 32-bit aligned + // setup lookahead buffer, note mount finishes initializing this after + // we establish a decent pseudo-random seed LFS2_ASSERT(lfs2->cfg->lookahead_size > 0); - LFS2_ASSERT(lfs2->cfg->lookahead_size % 8 == 0 && - (uintptr_t)lfs2->cfg->lookahead_buffer % 4 == 0); if (lfs2->cfg->lookahead_buffer) { - lfs2->free.buffer = lfs2->cfg->lookahead_buffer; + lfs2->lookahead.buffer = lfs2->cfg->lookahead_buffer; } else { - lfs2->free.buffer = lfs2_malloc(lfs2->cfg->lookahead_size); - if (!lfs2->free.buffer) { + lfs2->lookahead.buffer = lfs2_malloc(lfs2->cfg->lookahead_size); + if (!lfs2->lookahead.buffer) { err = LFS2_ERR_NOMEM; goto cleanup; } @@ -4215,6 +4328,27 @@ static int lfs2_init(lfs2_t *lfs2, const struct lfs2_config *cfg) { LFS2_ASSERT(lfs2->cfg->metadata_max <= lfs2->cfg->block_size); + LFS2_ASSERT(lfs2->cfg->inline_max == (lfs2_size_t)-1 + || lfs2->cfg->inline_max <= lfs2->cfg->cache_size); + LFS2_ASSERT(lfs2->cfg->inline_max == (lfs2_size_t)-1 + || lfs2->cfg->inline_max <= lfs2->attr_max); + LFS2_ASSERT(lfs2->cfg->inline_max == (lfs2_size_t)-1 + || lfs2->cfg->inline_max <= ((lfs2->cfg->metadata_max) + ? lfs2->cfg->metadata_max + : lfs2->cfg->block_size)/8); + lfs2->inline_max = lfs2->cfg->inline_max; + if (lfs2->inline_max == (lfs2_size_t)-1) { + lfs2->inline_max = 0; + } else if (lfs2->inline_max == 0) { + lfs2->inline_max = lfs2_min( + lfs2->cfg->cache_size, + lfs2_min( + lfs2->attr_max, + ((lfs2->cfg->metadata_max) + ? lfs2->cfg->metadata_max + : lfs2->cfg->block_size)/8)); + } + // setup default state lfs2->root[0] = LFS2_BLOCK_NULL; lfs2->root[1] = LFS2_BLOCK_NULL; @@ -4245,7 +4379,7 @@ static int lfs2_deinit(lfs2_t *lfs2) { } if (!lfs2->cfg->lookahead_buffer) { - lfs2_free(lfs2->free.buffer); + lfs2_free(lfs2->lookahead.buffer); } return 0; @@ -4254,7 +4388,7 @@ static int lfs2_deinit(lfs2_t *lfs2) { #ifndef LFS2_READONLY -static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { +static int lfs2_format_(lfs2_t *lfs2, const struct lfs2_config *cfg) { int err = 0; { err = lfs2_init(lfs2, cfg); @@ -4265,12 +4399,12 @@ static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { LFS2_ASSERT(cfg->block_count != 0); // create free lookahead - memset(lfs2->free.buffer, 0, lfs2->cfg->lookahead_size); - lfs2->free.off = 0; - lfs2->free.size = lfs2_min(8*lfs2->cfg->lookahead_size, + memset(lfs2->lookahead.buffer, 0, lfs2->cfg->lookahead_size); + lfs2->lookahead.start = 0; + lfs2->lookahead.size = lfs2_min(8*lfs2->cfg->lookahead_size, lfs2->block_count); - lfs2->free.i = 0; - lfs2_alloc_ack(lfs2); + lfs2->lookahead.next = 0; + lfs2_alloc_ckpoint(lfs2); // create root dir lfs2_mdir_t root; @@ -4321,7 +4455,31 @@ static int lfs2_rawformat(lfs2_t *lfs2, const struct lfs2_config *cfg) { } #endif -static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { +struct lfs2_tortoise_t { + lfs2_block_t pair[2]; + lfs2_size_t i; + lfs2_size_t period; +}; + +static int lfs2_tortoise_detectcycles( + const lfs2_mdir_t *dir, struct lfs2_tortoise_t *tortoise) { + // detect cycles with Brent's algorithm + if (lfs2_pair_issync(dir->tail, tortoise->pair)) { + LFS2_WARN("Cycle detected in tail list"); + return LFS2_ERR_CORRUPT; + } + if (tortoise->i == tortoise->period) { + tortoise->pair[0] = dir->tail[0]; + tortoise->pair[1] = dir->tail[1]; + tortoise->i = 0; + tortoise->period *= 2; + } + tortoise->i += 1; + + return LFS2_ERR_OK; +} + +static int lfs2_mount_(lfs2_t *lfs2, const struct lfs2_config *cfg) { int err = lfs2_init(lfs2, cfg); if (err) { return err; @@ -4329,23 +4487,16 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { // scan directory blocks for superblock and any global updates lfs2_mdir_t dir = {.tail = {0, 1}}; - lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; - lfs2_size_t tortoise_i = 1; - lfs2_size_t tortoise_period = 1; + struct lfs2_tortoise_t tortoise = { + .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}, + .i = 1, + .period = 1, + }; while (!lfs2_pair_isnull(dir.tail)) { - // detect cycles with Brent's algorithm - if (lfs2_pair_issync(dir.tail, tortoise)) { - LFS2_WARN("Cycle detected in tail list"); - err = LFS2_ERR_CORRUPT; + err = lfs2_tortoise_detectcycles(&dir, &tortoise); + if (err < 0) { goto cleanup; } - if (tortoise_i == tortoise_period) { - tortoise[0] = dir.tail[0]; - tortoise[1] = dir.tail[1]; - tortoise_i = 0; - tortoise_period *= 2; - } - tortoise_i += 1; // fetch next block in tail list lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, &dir, dir.tail, @@ -4394,6 +4545,7 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { // found older minor version? set an in-device only bit in the // gstate so we know we need to rewrite the superblock before // the first write + bool needssuperblock = false; if (minor_version < lfs2_fs_disk_version_minor(lfs2)) { LFS2_DEBUG("Found older minor version " "v%"PRIu16".%"PRIu16" < v%"PRIu16".%"PRIu16, @@ -4401,10 +4553,11 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { minor_version, lfs2_fs_disk_version_major(lfs2), lfs2_fs_disk_version_minor(lfs2)); - // note this bit is reserved on disk, so fetching more gstate - // will not interfere here - lfs2_fs_prepsuperblock(lfs2, true); + needssuperblock = true; } + // note this bit is reserved on disk, so fetching more gstate + // will not interfere here + lfs2_fs_prepsuperblock(lfs2, needssuperblock); // check superblock configuration if (superblock.name_max) { @@ -4438,6 +4591,9 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { } lfs2->attr_max = superblock.attr_max; + + // we also need to update inline_max in case attr_max changed + lfs2->inline_max = lfs2_min(lfs2->inline_max, lfs2->attr_max); } // this is where we get the block_count from disk if block_count=0 @@ -4478,23 +4634,23 @@ static int lfs2_rawmount(lfs2_t *lfs2, const struct lfs2_config *cfg) { // setup free lookahead, to distribute allocations uniformly across // boots, we start the allocator at a random location - lfs2->free.off = lfs2->seed % lfs2->block_count; + lfs2->lookahead.start = lfs2->seed % lfs2->block_count; lfs2_alloc_drop(lfs2); return 0; cleanup: - lfs2_rawunmount(lfs2); + lfs2_unmount_(lfs2); return err; } -static int lfs2_rawunmount(lfs2_t *lfs2) { +static int lfs2_unmount_(lfs2_t *lfs2) { return lfs2_deinit(lfs2); } /// Filesystem filesystem operations /// -static int lfs2_fs_rawstat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { +static int lfs2_fs_stat_(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { // if the superblock is up-to-date, we must be on the most recent // minor version of littlefs if (!lfs2_gstate_needssuperblock(&lfs2->gstate)) { @@ -4534,7 +4690,7 @@ static int lfs2_fs_rawstat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { return 0; } -int lfs2_fs_rawtraverse(lfs2_t *lfs2, +int lfs2_fs_traverse_(lfs2_t *lfs2, int (*cb)(void *data, lfs2_block_t block), void *data, bool includeorphans) { // iterate over metadata pairs @@ -4553,22 +4709,17 @@ int lfs2_fs_rawtraverse(lfs2_t *lfs2, } #endif - lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; - lfs2_size_t tortoise_i = 1; - lfs2_size_t tortoise_period = 1; + struct lfs2_tortoise_t tortoise = { + .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}, + .i = 1, + .period = 1, + }; + int err = LFS2_ERR_OK; while (!lfs2_pair_isnull(dir.tail)) { - // detect cycles with Brent's algorithm - if (lfs2_pair_issync(dir.tail, tortoise)) { - LFS2_WARN("Cycle detected in tail list"); + err = lfs2_tortoise_detectcycles(&dir, &tortoise); + if (err < 0) { return LFS2_ERR_CORRUPT; } - if (tortoise_i == tortoise_period) { - tortoise[0] = dir.tail[0]; - tortoise[1] = dir.tail[1]; - tortoise_i = 0; - tortoise_period *= 2; - } - tortoise_i += 1; for (int i = 0; i < 2; i++) { int err = cb(data, dir.tail[i]); @@ -4647,22 +4798,17 @@ static int lfs2_fs_pred(lfs2_t *lfs2, // iterate over all directory directory entries pdir->tail[0] = 0; pdir->tail[1] = 1; - lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; - lfs2_size_t tortoise_i = 1; - lfs2_size_t tortoise_period = 1; + struct lfs2_tortoise_t tortoise = { + .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}, + .i = 1, + .period = 1, + }; + int err = LFS2_ERR_OK; while (!lfs2_pair_isnull(pdir->tail)) { - // detect cycles with Brent's algorithm - if (lfs2_pair_issync(pdir->tail, tortoise)) { - LFS2_WARN("Cycle detected in tail list"); + err = lfs2_tortoise_detectcycles(pdir, &tortoise); + if (err < 0) { return LFS2_ERR_CORRUPT; } - if (tortoise_i == tortoise_period) { - tortoise[0] = pdir->tail[0]; - tortoise[1] = pdir->tail[1]; - tortoise_i = 0; - tortoise_period *= 2; - } - tortoise_i += 1; if (lfs2_pair_cmp(pdir->tail, pair) == 0) { return 0; @@ -4712,22 +4858,17 @@ static lfs2_stag_t lfs2_fs_parent(lfs2_t *lfs2, const lfs2_block_t pair[2], // use fetchmatch with callback to find pairs parent->tail[0] = 0; parent->tail[1] = 1; - lfs2_block_t tortoise[2] = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}; - lfs2_size_t tortoise_i = 1; - lfs2_size_t tortoise_period = 1; + struct lfs2_tortoise_t tortoise = { + .pair = {LFS2_BLOCK_NULL, LFS2_BLOCK_NULL}, + .i = 1, + .period = 1, + }; + int err = LFS2_ERR_OK; while (!lfs2_pair_isnull(parent->tail)) { - // detect cycles with Brent's algorithm - if (lfs2_pair_issync(parent->tail, tortoise)) { - LFS2_WARN("Cycle detected in tail list"); - return LFS2_ERR_CORRUPT; - } - if (tortoise_i == tortoise_period) { - tortoise[0] = parent->tail[0]; - tortoise[1] = parent->tail[1]; - tortoise_i = 0; - tortoise_period *= 2; + err = lfs2_tortoise_detectcycles(parent, &tortoise); + if (err < 0) { + return err; } - tortoise_i += 1; lfs2_stag_t tag = lfs2_dir_fetchmatch(lfs2, parent, parent->tail, LFS2_MKTAG(0x7ff, 0, 0x3ff), @@ -4999,7 +5140,7 @@ static int lfs2_fs_forceconsistency(lfs2_t *lfs2) { #endif #ifndef LFS2_READONLY -static int lfs2_fs_rawmkconsistent(lfs2_t *lfs2) { +static int lfs2_fs_mkconsistent_(lfs2_t *lfs2) { // lfs2_fs_forceconsistency does most of the work here int err = lfs2_fs_forceconsistency(lfs2); if (err) { @@ -5035,9 +5176,9 @@ static int lfs2_fs_size_count(void *p, lfs2_block_t block) { return 0; } -static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2) { +static lfs2_ssize_t lfs2_fs_size_(lfs2_t *lfs2) { lfs2_size_t size = 0; - int err = lfs2_fs_rawtraverse(lfs2, lfs2_fs_size_count, &size, false); + int err = lfs2_fs_traverse_(lfs2, lfs2_fs_size_count, &size, false); if (err) { return err; } @@ -5045,41 +5186,118 @@ static lfs2_ssize_t lfs2_fs_rawsize(lfs2_t *lfs2) { return size; } +// explicit garbage collection #ifndef LFS2_READONLY -static int lfs2_fs_rawgrow(lfs2_t *lfs2, lfs2_size_t block_count) { - // shrinking is not supported - LFS2_ASSERT(block_count >= lfs2->block_count); +static int lfs2_fs_gc_(lfs2_t *lfs2) { + // force consistency, even if we're not necessarily going to write, + // because this function is supposed to take care of janitorial work + // isn't it? + int err = lfs2_fs_forceconsistency(lfs2); + if (err) { + return err; + } - if (block_count > lfs2->block_count) { - lfs2->block_count = block_count; + // try to compact metadata pairs, note we can't really accomplish + // anything if compact_thresh doesn't at least leave a prog_size + // available + if (lfs2->cfg->compact_thresh + < lfs2->cfg->block_size - lfs2->cfg->prog_size) { + // iterate over all mdirs + lfs2_mdir_t mdir = {.tail = {0, 1}}; + while (!lfs2_pair_isnull(mdir.tail)) { + err = lfs2_dir_fetch(lfs2, &mdir, mdir.tail); + if (err) { + return err; + } - // fetch the root - lfs2_mdir_t root; - int err = lfs2_dir_fetch(lfs2, &root, lfs2->root); + // not erased? exceeds our compaction threshold? + if (!mdir.erased || ((lfs2->cfg->compact_thresh == 0) + ? mdir.off > lfs2->cfg->block_size - lfs2->cfg->block_size/8 + : mdir.off > lfs2->cfg->compact_thresh)) { + // the easiest way to trigger a compaction is to mark + // the mdir as unerased and add an empty commit + mdir.erased = false; + err = lfs2_dir_commit(lfs2, &mdir, NULL, 0); + if (err) { + return err; + } + } + } + } + + // try to populate the lookahead buffer, unless it's already full + if (lfs2->lookahead.size < lfs2_min( + 8 * lfs2->cfg->lookahead_size, + lfs2->block_count)) { + err = lfs2_alloc_scan(lfs2); if (err) { return err; } + } - // update the superblock - lfs2_superblock_t superblock; - lfs2_stag_t tag = lfs2_dir_get(lfs2, &root, LFS2_MKTAG(0x7ff, 0x3ff, 0), - LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), - &superblock); - if (tag < 0) { - return tag; - } - lfs2_superblock_fromle32(&superblock); + return 0; +} +#endif - superblock.block_count = lfs2->block_count; +#ifndef LFS2_READONLY +#ifdef LFS2_SHRINKNONRELOCATING +static int lfs2_shrink_checkblock(void *data, lfs2_block_t block) { + lfs2_size_t threshold = *((lfs2_size_t*)data); + if (block >= threshold) { + return LFS2_ERR_NOTEMPTY; + } + return 0; +} +#endif - lfs2_superblock_tole32(&superblock); - err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS( - {tag, &superblock})); +static int lfs2_fs_grow_(lfs2_t *lfs2, lfs2_size_t block_count) { + int err; + + if (block_count == lfs2->block_count) { + return 0; + } + + +#ifndef LFS2_SHRINKNONRELOCATING + // shrinking is not supported + LFS2_ASSERT(block_count >= lfs2->block_count); +#endif +#ifdef LFS2_SHRINKNONRELOCATING + if (block_count < lfs2->block_count) { + err = lfs2_fs_traverse_(lfs2, lfs2_shrink_checkblock, &block_count, true); if (err) { return err; } } +#endif + + lfs2->block_count = block_count; + // fetch the root + lfs2_mdir_t root; + err = lfs2_dir_fetch(lfs2, &root, lfs2->root); + if (err) { + return err; + } + + // update the superblock + lfs2_superblock_t superblock; + lfs2_stag_t tag = lfs2_dir_get(lfs2, &root, LFS2_MKTAG(0x7ff, 0x3ff, 0), + LFS2_MKTAG(LFS2_TYPE_INLINESTRUCT, 0, sizeof(superblock)), + &superblock); + if (tag < 0) { + return tag; + } + lfs2_superblock_fromle32(&superblock); + + superblock.block_count = lfs2->block_count; + + lfs2_superblock_tole32(&superblock); + err = lfs2_dir_commit(lfs2, &root, LFS2_MKATTRS( + {tag, &superblock})); + if (err) { + return err; + } return 0; } #endif @@ -5451,10 +5669,10 @@ static int lfs21_mount(lfs2_t *lfs2, struct lfs21 *lfs21, lfs2->lfs21->root[1] = LFS2_BLOCK_NULL; // setup free lookahead - lfs2->free.off = 0; - lfs2->free.size = 0; - lfs2->free.i = 0; - lfs2_alloc_ack(lfs2); + lfs2->lookahead.start = 0; + lfs2->lookahead.size = 0; + lfs2->lookahead.next = 0; + lfs2_alloc_ckpoint(lfs2); // load superblock lfs21_dir_t dir; @@ -5505,7 +5723,7 @@ static int lfs21_unmount(lfs2_t *lfs2) { } /// v1 migration /// -static int lfs2_rawmigrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { +static int lfs2_migrate_(lfs2_t *lfs2, const struct lfs2_config *cfg) { struct lfs21 lfs21; // Indeterminate filesystem size not allowed for migration. @@ -5759,7 +5977,7 @@ int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) { ".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " + ".block_cycles=%"PRId32", .cache_size=%"PRIu32", " ".lookahead_size=%"PRIu32", .read_buffer=%p, " ".prog_buffer=%p, .lookahead_buffer=%p, " ".name_max=%"PRIu32", .file_max=%"PRIu32", " @@ -5772,7 +5990,7 @@ int lfs2_format(lfs2_t *lfs2, const struct lfs2_config *cfg) { cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, cfg->name_max, cfg->file_max, cfg->attr_max); - err = lfs2_rawformat(lfs2, cfg); + err = lfs2_format_(lfs2, cfg); LFS2_TRACE("lfs2_format -> %d", err); LFS2_UNLOCK(cfg); @@ -5789,7 +6007,7 @@ int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) { ".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " + ".block_cycles=%"PRId32", .cache_size=%"PRIu32", " ".lookahead_size=%"PRIu32", .read_buffer=%p, " ".prog_buffer=%p, .lookahead_buffer=%p, " ".name_max=%"PRIu32", .file_max=%"PRIu32", " @@ -5802,7 +6020,7 @@ int lfs2_mount(lfs2_t *lfs2, const struct lfs2_config *cfg) { cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, cfg->name_max, cfg->file_max, cfg->attr_max); - err = lfs2_rawmount(lfs2, cfg); + err = lfs2_mount_(lfs2, cfg); LFS2_TRACE("lfs2_mount -> %d", err); LFS2_UNLOCK(cfg); @@ -5816,7 +6034,7 @@ int lfs2_unmount(lfs2_t *lfs2) { } LFS2_TRACE("lfs2_unmount(%p)", (void*)lfs2); - err = lfs2_rawunmount(lfs2); + err = lfs2_unmount_(lfs2); LFS2_TRACE("lfs2_unmount -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5831,7 +6049,7 @@ int lfs2_remove(lfs2_t *lfs2, const char *path) { } LFS2_TRACE("lfs2_remove(%p, \"%s\")", (void*)lfs2, path); - err = lfs2_rawremove(lfs2, path); + err = lfs2_remove_(lfs2, path); LFS2_TRACE("lfs2_remove -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5847,7 +6065,7 @@ int lfs2_rename(lfs2_t *lfs2, const char *oldpath, const char *newpath) { } LFS2_TRACE("lfs2_rename(%p, \"%s\", \"%s\")", (void*)lfs2, oldpath, newpath); - err = lfs2_rawrename(lfs2, oldpath, newpath); + err = lfs2_rename_(lfs2, oldpath, newpath); LFS2_TRACE("lfs2_rename -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5862,7 +6080,7 @@ int lfs2_stat(lfs2_t *lfs2, const char *path, struct lfs2_info *info) { } LFS2_TRACE("lfs2_stat(%p, \"%s\", %p)", (void*)lfs2, path, (void*)info); - err = lfs2_rawstat(lfs2, path, info); + err = lfs2_stat_(lfs2, path, info); LFS2_TRACE("lfs2_stat -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5878,7 +6096,7 @@ lfs2_ssize_t lfs2_getattr(lfs2_t *lfs2, const char *path, LFS2_TRACE("lfs2_getattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", (void*)lfs2, path, type, buffer, size); - lfs2_ssize_t res = lfs2_rawgetattr(lfs2, path, type, buffer, size); + lfs2_ssize_t res = lfs2_getattr_(lfs2, path, type, buffer, size); LFS2_TRACE("lfs2_getattr -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -5895,7 +6113,7 @@ int lfs2_setattr(lfs2_t *lfs2, const char *path, LFS2_TRACE("lfs2_setattr(%p, \"%s\", %"PRIu8", %p, %"PRIu32")", (void*)lfs2, path, type, buffer, size); - err = lfs2_rawsetattr(lfs2, path, type, buffer, size); + err = lfs2_setattr_(lfs2, path, type, buffer, size); LFS2_TRACE("lfs2_setattr -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5911,7 +6129,7 @@ int lfs2_removeattr(lfs2_t *lfs2, const char *path, uint8_t type) { } LFS2_TRACE("lfs2_removeattr(%p, \"%s\", %"PRIu8")", (void*)lfs2, path, type); - err = lfs2_rawremoveattr(lfs2, path, type); + err = lfs2_removeattr_(lfs2, path, type); LFS2_TRACE("lfs2_removeattr -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5926,10 +6144,10 @@ int lfs2_file_open(lfs2_t *lfs2, lfs2_file_t *file, const char *path, int flags) return err; } LFS2_TRACE("lfs2_file_open(%p, %p, \"%s\", %x)", - (void*)lfs2, (void*)file, path, flags); + (void*)lfs2, (void*)file, path, (unsigned)flags); LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawopen(lfs2, file, path, flags); + err = lfs2_file_open_(lfs2, file, path, flags); LFS2_TRACE("lfs2_file_open -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5946,11 +6164,11 @@ int lfs2_file_opencfg(lfs2_t *lfs2, lfs2_file_t *file, } LFS2_TRACE("lfs2_file_opencfg(%p, %p, \"%s\", %x, %p {" ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})", - (void*)lfs2, (void*)file, path, flags, + (void*)lfs2, (void*)file, path, (unsigned)flags, (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawopencfg(lfs2, file, path, flags, cfg); + err = lfs2_file_opencfg_(lfs2, file, path, flags, cfg); LFS2_TRACE("lfs2_file_opencfg -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5965,7 +6183,7 @@ int lfs2_file_close(lfs2_t *lfs2, lfs2_file_t *file) { LFS2_TRACE("lfs2_file_close(%p, %p)", (void*)lfs2, (void*)file); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawclose(lfs2, file); + err = lfs2_file_close_(lfs2, file); LFS2_TRACE("lfs2_file_close -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5981,7 +6199,7 @@ int lfs2_file_sync(lfs2_t *lfs2, lfs2_file_t *file) { LFS2_TRACE("lfs2_file_sync(%p, %p)", (void*)lfs2, (void*)file); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawsync(lfs2, file); + err = lfs2_file_sync_(lfs2, file); LFS2_TRACE("lfs2_file_sync -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -5999,7 +6217,7 @@ lfs2_ssize_t lfs2_file_read(lfs2_t *lfs2, lfs2_file_t *file, (void*)lfs2, (void*)file, buffer, size); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_ssize_t res = lfs2_file_rawread(lfs2, file, buffer, size); + lfs2_ssize_t res = lfs2_file_read_(lfs2, file, buffer, size); LFS2_TRACE("lfs2_file_read -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6017,7 +6235,7 @@ lfs2_ssize_t lfs2_file_write(lfs2_t *lfs2, lfs2_file_t *file, (void*)lfs2, (void*)file, buffer, size); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_ssize_t res = lfs2_file_rawwrite(lfs2, file, buffer, size); + lfs2_ssize_t res = lfs2_file_write_(lfs2, file, buffer, size); LFS2_TRACE("lfs2_file_write -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6035,7 +6253,7 @@ lfs2_soff_t lfs2_file_seek(lfs2_t *lfs2, lfs2_file_t *file, (void*)lfs2, (void*)file, off, whence); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_soff_t res = lfs2_file_rawseek(lfs2, file, off, whence); + lfs2_soff_t res = lfs2_file_seek_(lfs2, file, off, whence); LFS2_TRACE("lfs2_file_seek -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6052,7 +6270,7 @@ int lfs2_file_truncate(lfs2_t *lfs2, lfs2_file_t *file, lfs2_off_t size) { (void*)lfs2, (void*)file, size); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - err = lfs2_file_rawtruncate(lfs2, file, size); + err = lfs2_file_truncate_(lfs2, file, size); LFS2_TRACE("lfs2_file_truncate -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6068,7 +6286,7 @@ lfs2_soff_t lfs2_file_tell(lfs2_t *lfs2, lfs2_file_t *file) { LFS2_TRACE("lfs2_file_tell(%p, %p)", (void*)lfs2, (void*)file); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_soff_t res = lfs2_file_rawtell(lfs2, file); + lfs2_soff_t res = lfs2_file_tell_(lfs2, file); LFS2_TRACE("lfs2_file_tell -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6082,7 +6300,7 @@ int lfs2_file_rewind(lfs2_t *lfs2, lfs2_file_t *file) { } LFS2_TRACE("lfs2_file_rewind(%p, %p)", (void*)lfs2, (void*)file); - err = lfs2_file_rawrewind(lfs2, file); + err = lfs2_file_rewind_(lfs2, file); LFS2_TRACE("lfs2_file_rewind -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6097,9 +6315,9 @@ lfs2_soff_t lfs2_file_size(lfs2_t *lfs2, lfs2_file_t *file) { LFS2_TRACE("lfs2_file_size(%p, %p)", (void*)lfs2, (void*)file); LFS2_ASSERT(lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)file)); - lfs2_soff_t res = lfs2_file_rawsize(lfs2, file); + lfs2_soff_t res = lfs2_file_size_(lfs2, file); - LFS2_TRACE("lfs2_file_size -> %"PRId32, res); + LFS2_TRACE("lfs2_file_size -> %"PRIu32, res); LFS2_UNLOCK(lfs2->cfg); return res; } @@ -6112,7 +6330,7 @@ int lfs2_mkdir(lfs2_t *lfs2, const char *path) { } LFS2_TRACE("lfs2_mkdir(%p, \"%s\")", (void*)lfs2, path); - err = lfs2_rawmkdir(lfs2, path); + err = lfs2_mkdir_(lfs2, path); LFS2_TRACE("lfs2_mkdir -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6128,7 +6346,7 @@ int lfs2_dir_open(lfs2_t *lfs2, lfs2_dir_t *dir, const char *path) { LFS2_TRACE("lfs2_dir_open(%p, %p, \"%s\")", (void*)lfs2, (void*)dir, path); LFS2_ASSERT(!lfs2_mlist_isopen(lfs2->mlist, (struct lfs2_mlist*)dir)); - err = lfs2_dir_rawopen(lfs2, dir, path); + err = lfs2_dir_open_(lfs2, dir, path); LFS2_TRACE("lfs2_dir_open -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6142,7 +6360,7 @@ int lfs2_dir_close(lfs2_t *lfs2, lfs2_dir_t *dir) { } LFS2_TRACE("lfs2_dir_close(%p, %p)", (void*)lfs2, (void*)dir); - err = lfs2_dir_rawclose(lfs2, dir); + err = lfs2_dir_close_(lfs2, dir); LFS2_TRACE("lfs2_dir_close -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6157,7 +6375,7 @@ int lfs2_dir_read(lfs2_t *lfs2, lfs2_dir_t *dir, struct lfs2_info *info) { LFS2_TRACE("lfs2_dir_read(%p, %p, %p)", (void*)lfs2, (void*)dir, (void*)info); - err = lfs2_dir_rawread(lfs2, dir, info); + err = lfs2_dir_read_(lfs2, dir, info); LFS2_TRACE("lfs2_dir_read -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6172,7 +6390,7 @@ int lfs2_dir_seek(lfs2_t *lfs2, lfs2_dir_t *dir, lfs2_off_t off) { LFS2_TRACE("lfs2_dir_seek(%p, %p, %"PRIu32")", (void*)lfs2, (void*)dir, off); - err = lfs2_dir_rawseek(lfs2, dir, off); + err = lfs2_dir_seek_(lfs2, dir, off); LFS2_TRACE("lfs2_dir_seek -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6186,7 +6404,7 @@ lfs2_soff_t lfs2_dir_tell(lfs2_t *lfs2, lfs2_dir_t *dir) { } LFS2_TRACE("lfs2_dir_tell(%p, %p)", (void*)lfs2, (void*)dir); - lfs2_soff_t res = lfs2_dir_rawtell(lfs2, dir); + lfs2_soff_t res = lfs2_dir_tell_(lfs2, dir); LFS2_TRACE("lfs2_dir_tell -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6200,7 +6418,7 @@ int lfs2_dir_rewind(lfs2_t *lfs2, lfs2_dir_t *dir) { } LFS2_TRACE("lfs2_dir_rewind(%p, %p)", (void*)lfs2, (void*)dir); - err = lfs2_dir_rawrewind(lfs2, dir); + err = lfs2_dir_rewind_(lfs2, dir); LFS2_TRACE("lfs2_dir_rewind -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6214,7 +6432,7 @@ int lfs2_fs_stat(lfs2_t *lfs2, struct lfs2_fsinfo *fsinfo) { } LFS2_TRACE("lfs2_fs_stat(%p, %p)", (void*)lfs2, (void*)fsinfo); - err = lfs2_fs_rawstat(lfs2, fsinfo); + err = lfs2_fs_stat_(lfs2, fsinfo); LFS2_TRACE("lfs2_fs_stat -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6228,7 +6446,7 @@ lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2) { } LFS2_TRACE("lfs2_fs_size(%p)", (void*)lfs2); - lfs2_ssize_t res = lfs2_fs_rawsize(lfs2); + lfs2_ssize_t res = lfs2_fs_size_(lfs2); LFS2_TRACE("lfs2_fs_size -> %"PRId32, res); LFS2_UNLOCK(lfs2->cfg); @@ -6243,7 +6461,7 @@ int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void *, lfs2_block_t), void *data) LFS2_TRACE("lfs2_fs_traverse(%p, %p, %p)", (void*)lfs2, (void*)(uintptr_t)cb, data); - err = lfs2_fs_rawtraverse(lfs2, cb, data, true); + err = lfs2_fs_traverse_(lfs2, cb, data, true); LFS2_TRACE("lfs2_fs_traverse -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6251,32 +6469,32 @@ int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void *, lfs2_block_t), void *data) } #ifndef LFS2_READONLY -int lfs2_fs_gc(lfs2_t *lfs2) { +int lfs2_fs_mkconsistent(lfs2_t *lfs2) { int err = LFS2_LOCK(lfs2->cfg); if (err) { return err; } - LFS2_TRACE("lfs2_fs_gc(%p)", (void*)lfs2); + LFS2_TRACE("lfs2_fs_mkconsistent(%p)", (void*)lfs2); - err = lfs2_fs_rawgc(lfs2); + err = lfs2_fs_mkconsistent_(lfs2); - LFS2_TRACE("lfs2_fs_gc -> %d", err); + LFS2_TRACE("lfs2_fs_mkconsistent -> %d", err); LFS2_UNLOCK(lfs2->cfg); return err; } #endif #ifndef LFS2_READONLY -int lfs2_fs_mkconsistent(lfs2_t *lfs2) { +int lfs2_fs_gc(lfs2_t *lfs2) { int err = LFS2_LOCK(lfs2->cfg); if (err) { return err; } - LFS2_TRACE("lfs2_fs_mkconsistent(%p)", (void*)lfs2); + LFS2_TRACE("lfs2_fs_gc(%p)", (void*)lfs2); - err = lfs2_fs_rawmkconsistent(lfs2); + err = lfs2_fs_gc_(lfs2); - LFS2_TRACE("lfs2_fs_mkconsistent -> %d", err); + LFS2_TRACE("lfs2_fs_gc -> %d", err); LFS2_UNLOCK(lfs2->cfg); return err; } @@ -6290,7 +6508,7 @@ int lfs2_fs_grow(lfs2_t *lfs2, lfs2_size_t block_count) { } LFS2_TRACE("lfs2_fs_grow(%p, %"PRIu32")", (void*)lfs2, block_count); - err = lfs2_fs_rawgrow(lfs2, block_count); + err = lfs2_fs_grow_(lfs2, block_count); LFS2_TRACE("lfs2_fs_grow -> %d", err); LFS2_UNLOCK(lfs2->cfg); @@ -6308,7 +6526,7 @@ int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { ".read=%p, .prog=%p, .erase=%p, .sync=%p, " ".read_size=%"PRIu32", .prog_size=%"PRIu32", " ".block_size=%"PRIu32", .block_count=%"PRIu32", " - ".block_cycles=%"PRIu32", .cache_size=%"PRIu32", " + ".block_cycles=%"PRId32", .cache_size=%"PRIu32", " ".lookahead_size=%"PRIu32", .read_buffer=%p, " ".prog_buffer=%p, .lookahead_buffer=%p, " ".name_max=%"PRIu32", .file_max=%"PRIu32", " @@ -6321,7 +6539,7 @@ int lfs2_migrate(lfs2_t *lfs2, const struct lfs2_config *cfg) { cfg->read_buffer, cfg->prog_buffer, cfg->lookahead_buffer, cfg->name_max, cfg->file_max, cfg->attr_max); - err = lfs2_rawmigrate(lfs2, cfg); + err = lfs2_migrate_(lfs2, cfg); LFS2_TRACE("lfs2_migrate -> %d", err); LFS2_UNLOCK(cfg); diff --git a/lib/littlefs/lfs2.h b/lib/littlefs/lfs2.h index 559ccebac9269..aee0619e9326a 100644 --- a/lib/littlefs/lfs2.h +++ b/lib/littlefs/lfs2.h @@ -21,7 +21,7 @@ extern "C" // Software library version // Major (top-nibble), incremented on backwards incompatible changes // Minor (bottom-nibble), incremented on feature additions -#define LFS2_VERSION 0x00020008 +#define LFS2_VERSION 0x0002000b #define LFS2_VERSION_MAJOR (0xffff & (LFS2_VERSION >> 16)) #define LFS2_VERSION_MINOR (0xffff & (LFS2_VERSION >> 0)) @@ -52,16 +52,15 @@ typedef uint32_t lfs2_block_t; #endif // Maximum size of a file in bytes, may be redefined to limit to support other -// drivers. Limited on disk to <= 4294967296. However, above 2147483647 the -// functions lfs2_file_seek, lfs2_file_size, and lfs2_file_tell will return -// incorrect values due to using signed integers. Stored in superblock and -// must be respected by other littlefs drivers. +// drivers. Limited on disk to <= 2147483647. Stored in superblock and must be +// respected by other littlefs drivers. #ifndef LFS2_FILE_MAX #define LFS2_FILE_MAX 2147483647 #endif // Maximum size of custom attributes in bytes, may be redefined, but there is -// no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022. +// no real benefit to using a smaller LFS2_ATTR_MAX. Limited to <= 1022. Stored +// in superblock and must be respected by other littlefs drivers. #ifndef LFS2_ATTR_MAX #define LFS2_ATTR_MAX 1022 #endif @@ -205,7 +204,8 @@ struct lfs2_config { // program sizes. lfs2_size_t block_size; - // Number of erasable blocks on the device. + // Number of erasable blocks on the device. Defaults to block_count stored + // on disk when zero. lfs2_size_t block_count; // Number of erase cycles before littlefs evicts metadata logs and moves @@ -226,9 +226,20 @@ struct lfs2_config { // Size of the lookahead buffer in bytes. A larger lookahead buffer // increases the number of blocks found during an allocation pass. The // lookahead buffer is stored as a compact bitmap, so each byte of RAM - // can track 8 blocks. Must be a multiple of 8. + // can track 8 blocks. lfs2_size_t lookahead_size; + // Threshold for metadata compaction during lfs2_fs_gc in bytes. Metadata + // pairs that exceed this threshold will be compacted during lfs2_fs_gc. + // Defaults to ~88% block_size when zero, though the default may change + // in the future. + // + // Note this only affects lfs2_fs_gc. Normal compactions still only occur + // when full. + // + // Set to -1 to disable metadata compaction during lfs2_fs_gc. + lfs2_size_t compact_thresh; + // Optional statically allocated read buffer. Must be cache_size. // By default lfs2_malloc is used to allocate this buffer. void *read_buffer; @@ -237,25 +248,24 @@ struct lfs2_config { // By default lfs2_malloc is used to allocate this buffer. void *prog_buffer; - // Optional statically allocated lookahead buffer. Must be lookahead_size - // and aligned to a 32-bit boundary. By default lfs2_malloc is used to - // allocate this buffer. + // Optional statically allocated lookahead buffer. Must be lookahead_size. + // By default lfs2_malloc is used to allocate this buffer. void *lookahead_buffer; // Optional upper limit on length of file names in bytes. No downside for // larger names except the size of the info struct which is controlled by - // the LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX when zero. Stored in - // superblock and must be respected by other littlefs drivers. + // the LFS2_NAME_MAX define. Defaults to LFS2_NAME_MAX or name_max stored on + // disk when zero. lfs2_size_t name_max; // Optional upper limit on files in bytes. No downside for larger files - // but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX when zero. Stored - // in superblock and must be respected by other littlefs drivers. + // but must be <= LFS2_FILE_MAX. Defaults to LFS2_FILE_MAX or file_max stored + // on disk when zero. lfs2_size_t file_max; // Optional upper limit on custom attributes in bytes. No downside for // larger attributes size but must be <= LFS2_ATTR_MAX. Defaults to - // LFS2_ATTR_MAX when zero. + // LFS2_ATTR_MAX or attr_max stored on disk when zero. lfs2_size_t attr_max; // Optional upper limit on total space given to metadata pairs in bytes. On @@ -264,6 +274,15 @@ struct lfs2_config { // Defaults to block_size when zero. lfs2_size_t metadata_max; + // Optional upper limit on inlined files in bytes. Inlined files live in + // metadata and decrease storage requirements, but may be limited to + // improve metadata-related performance. Must be <= cache_size, <= + // attr_max, and <= block_size/8. Defaults to the largest possible + // inline_max when zero. + // + // Set to -1 to disable inlined files. + lfs2_size_t inline_max; + #ifdef LFS2_MULTIVERSION // On-disk version to use when writing in the form of 16-bit major version // + 16-bit minor version. This limiting metadata to what is supported by @@ -430,19 +449,20 @@ typedef struct lfs2 { lfs2_gstate_t gdisk; lfs2_gstate_t gdelta; - struct lfs2_free { - lfs2_block_t off; + struct lfs2_lookahead { + lfs2_block_t start; lfs2_block_t size; - lfs2_block_t i; - lfs2_block_t ack; - uint32_t *buffer; - } free; + lfs2_block_t next; + lfs2_block_t ckpoint; + uint8_t *buffer; + } lookahead; const struct lfs2_config *cfg; lfs2_size_t block_count; lfs2_size_t name_max; lfs2_size_t file_max; lfs2_size_t attr_max; + lfs2_size_t inline_max; #ifdef LFS2_MIGRATE struct lfs21 *lfs21; @@ -712,18 +732,6 @@ lfs2_ssize_t lfs2_fs_size(lfs2_t *lfs2); // Returns a negative error code on failure. int lfs2_fs_traverse(lfs2_t *lfs2, int (*cb)(void*, lfs2_block_t), void *data); -// Attempt to proactively find free blocks -// -// Calling this function is not required, but may allowing the offloading of -// the expensive block allocation scan to a less time-critical code path. -// -// Note: littlefs currently does not persist any found free blocks to disk. -// This may change in the future. -// -// Returns a negative error code on failure. Finding no free blocks is -// not an error. -int lfs2_fs_gc(lfs2_t *lfs2); - #ifndef LFS2_READONLY // Attempt to make the filesystem consistent and ready for writing // @@ -736,11 +744,33 @@ int lfs2_fs_gc(lfs2_t *lfs2); int lfs2_fs_mkconsistent(lfs2_t *lfs2); #endif +#ifndef LFS2_READONLY +// Attempt any janitorial work +// +// This currently: +// 1. Calls mkconsistent if not already consistent +// 2. Compacts metadata > compact_thresh +// 3. Populates the block allocator +// +// Though additional janitorial work may be added in the future. +// +// Calling this function is not required, but may allow the offloading of +// expensive janitorial work to a less time-critical code path. +// +// Returns a negative error code on failure. Accomplishing nothing is not +// an error. +int lfs2_fs_gc(lfs2_t *lfs2); +#endif + #ifndef LFS2_READONLY // Grows the filesystem to a new size, updating the superblock with the new // block count. // -// Note: This is irreversible. +// If LFS2_SHRINKNONRELOCATING is defined, this function will also accept +// block_counts smaller than the current configuration, after checking +// that none of the blocks that are being removed are in use. +// Note that littlefs's pseudorandom block allocation means that +// this is very unlikely to work in the general case. // // Returns a negative error code on failure. int lfs2_fs_grow(lfs2_t *lfs2, lfs2_size_t block_count); diff --git a/lib/littlefs/lfs2_util.c b/lib/littlefs/lfs2_util.c index c9850e78869c3..4fe7e5340ce77 100644 --- a/lib/littlefs/lfs2_util.c +++ b/lib/littlefs/lfs2_util.c @@ -11,6 +11,8 @@ #ifndef LFS2_CONFIG +// If user provides their own CRC impl we don't need this +#ifndef LFS2_CRC // Software CRC implementation with small lookup table uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) { static const uint32_t rtable[16] = { @@ -29,6 +31,7 @@ uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) { return crc; } +#endif #endif diff --git a/lib/littlefs/lfs2_util.h b/lib/littlefs/lfs2_util.h index dd2cbcc106d84..12c82a630b079 100644 --- a/lib/littlefs/lfs2_util.h +++ b/lib/littlefs/lfs2_util.h @@ -8,6 +8,9 @@ #ifndef LFS2_UTIL_H #define LFS2_UTIL_H +#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x) +#define LFS2_STRINGIZE2(x) #x + // Users can override lfs2_util.h with their own configuration by defining // LFS2_CONFIG as a header file to include (-DLFS2_CONFIG=lfs2_config.h). // @@ -15,11 +18,26 @@ // provided by the config file. To start, I would suggest copying lfs2_util.h // and modifying as needed. #ifdef LFS2_CONFIG -#define LFS2_STRINGIZE(x) LFS2_STRINGIZE2(x) -#define LFS2_STRINGIZE2(x) #x #include LFS2_STRINGIZE(LFS2_CONFIG) #else +// Alternatively, users can provide a header file which defines +// macros and other things consumed by littlefs. +// +// For example, provide my_defines.h, which contains +// something like: +// +// #include +// extern void *my_malloc(size_t sz); +// #define LFS2_MALLOC(sz) my_malloc(sz) +// +// And build littlefs with the header by defining LFS2_DEFINES. +// (-DLFS2_DEFINES=my_defines.h) + +#ifdef LFS2_DEFINES +#include LFS2_STRINGIZE(LFS2_DEFINES) +#endif + // System includes #include #include @@ -177,10 +195,10 @@ static inline uint32_t lfs2_fromle32(uint32_t a) { (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)) return __builtin_bswap32(a); #else - return (((uint8_t*)&a)[0] << 0) | - (((uint8_t*)&a)[1] << 8) | - (((uint8_t*)&a)[2] << 16) | - (((uint8_t*)&a)[3] << 24); + return ((uint32_t)((uint8_t*)&a)[0] << 0) | + ((uint32_t)((uint8_t*)&a)[1] << 8) | + ((uint32_t)((uint8_t*)&a)[2] << 16) | + ((uint32_t)((uint8_t*)&a)[3] << 24); #endif } @@ -200,10 +218,10 @@ static inline uint32_t lfs2_frombe32(uint32_t a) { (defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) return a; #else - return (((uint8_t*)&a)[0] << 24) | - (((uint8_t*)&a)[1] << 16) | - (((uint8_t*)&a)[2] << 8) | - (((uint8_t*)&a)[3] << 0); + return ((uint32_t)((uint8_t*)&a)[0] << 24) | + ((uint32_t)((uint8_t*)&a)[1] << 16) | + ((uint32_t)((uint8_t*)&a)[2] << 8) | + ((uint32_t)((uint8_t*)&a)[3] << 0); #endif } @@ -212,12 +230,22 @@ static inline uint32_t lfs2_tobe32(uint32_t a) { } // Calculate CRC-32 with polynomial = 0x04c11db7 +#ifdef LFS2_CRC +static inline uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size) { + return LFS2_CRC(crc, buffer, size); +} +#else uint32_t lfs2_crc(uint32_t crc, const void *buffer, size_t size); +#endif // Allocate memory, only used if buffers are not provided to littlefs -// Note, memory must be 64-bit aligned +// +// littlefs current has no alignment requirements, as it only allocates +// byte-level buffers. static inline void *lfs2_malloc(size_t size) { -#ifndef LFS2_NO_MALLOC +#if defined(LFS2_MALLOC) + return LFS2_MALLOC(size); +#elif !defined(LFS2_NO_MALLOC) return malloc(size); #else (void)size; @@ -227,7 +255,9 @@ static inline void *lfs2_malloc(size_t size) { // Deallocate memory, only used if buffers are not provided to littlefs static inline void lfs2_free(void *p) { -#ifndef LFS2_NO_MALLOC +#if defined(LFS2_FREE) + LFS2_FREE(p); +#elif !defined(LFS2_NO_MALLOC) free(p); #else (void)p; diff --git a/lib/micropython-lib b/lib/micropython-lib index 5b496e944ec04..6ae440a8a1442 160000 --- a/lib/micropython-lib +++ b/lib/micropython-lib @@ -1 +1 @@ -Subproject commit 5b496e944ec045177afa1620920a168410b7f60b +Subproject commit 6ae440a8a144233e6e703f6759b7e7a0afaa37a4 diff --git a/lib/protomatter b/lib/protomatter index 0bd9873153ab0..f83bac7e42107 160000 --- a/lib/protomatter +++ b/lib/protomatter @@ -1 +1 @@ -Subproject commit 0bd9873153ab0a91d6737c392622159a4515a2e5 +Subproject commit f83bac7e421077812523fddb83d3e25f29753315 diff --git a/lib/tinyusb b/lib/tinyusb index 542e5b4550a01..c1bf19ed6cf1e 160000 --- a/lib/tinyusb +++ b/lib/tinyusb @@ -1 +1 @@ -Subproject commit 542e5b4550a01d034b78308d77c408ed89427513 +Subproject commit c1bf19ed6cf1eaa791f221c1bc5ce4b3d069f76d diff --git a/locale/ID.po b/locale/ID.po index ee7b8dfc4a3e1..ca862a9714768 100644 --- a/locale/ID.po +++ b/locale/ID.po @@ -6,15 +6,15 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-06-30 16:01+0000\n" -"Last-Translator: MAE \n" +"PO-Revision-Date: 2026-02-13 15:09+0000\n" +"Last-Translator: Arif Budiman \n" "Language-Team: LANGUAGE \n" "Language: ID\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" -"X-Generator: Weblate 5.13-dev\n" +"X-Generator: Weblate 5.16-dev\n" #: main.c msgid "" @@ -116,7 +116,7 @@ msgstr "%q gagal: %d" #: shared-module/audiodelays/MultiTapDelay.c msgid "%q in %q must be of type %q or %q, not %q" -msgstr "" +msgstr "%q dalam %q harus bertipe %q atau %q, bukan %q" #: py/argcheck.c shared-module/audiofilters/Filter.c msgid "%q in %q must be of type %q, not %q" @@ -289,7 +289,7 @@ msgstr "%q, %q, dan %q semuanya harus memiliki panjang yang sama" #: py/objint.c shared-bindings/_bleio/Connection.c #: shared-bindings/storage/__init__.c msgid "%q=%q" -msgstr "" +msgstr "%q=%q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "%q[%u] shifts in more bits than pin count" @@ -566,7 +566,7 @@ msgstr "Sudah disebarkan." #: ports/atmel-samd/common-hal/canio/Listener.c msgid "Already have all-matches listener" -msgstr "" +msgstr "Sudah memiliki listener semua-kecocokan" #: ports/espressif/common-hal/_bleio/__init__.c msgid "Already in progress" @@ -610,7 +610,7 @@ msgstr "Nilai array harus berupa byte tunggal." #: ports/atmel-samd/common-hal/spitarget/SPITarget.c msgid "Async SPI transfer in progress on this bus, keep awaiting." -msgstr "" +msgstr "Transfer SPI async sedang berlangsung pada bus ini, terus menunggu." #: shared-module/memorymonitor/AllocationAlarm.c #, c-format @@ -624,7 +624,7 @@ msgstr "Konversi audio belum diimplementasikan" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c msgid "Audio source error" -msgstr "" +msgstr "Kesalahan sumber audio" #: shared-bindings/wifi/Radio.c msgid "AuthMode.OPEN is not used with password" @@ -767,7 +767,7 @@ msgstr "" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Can't construct AudioOut because continuous channel already open" -msgstr "" +msgstr "Tidak dapat membuat AudioOut karena kanal berkelanjutan sudah terbuka" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c @@ -817,7 +817,7 @@ msgstr "Tidak dapat merekam ke file" #: shared-module/storage/__init__.c msgid "Cannot remount path when visible via USB." -msgstr "" +msgstr "Tidak dapat me-remount jalur ketika terlihat melalui USB." #: shared-bindings/digitalio/DigitalInOut.c msgid "Cannot set value when direction is input." @@ -834,7 +834,7 @@ msgstr "Tidak dapat membuat subkelas dari irisan" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Cannot use GPIO0..15 together with GPIO32..47" -msgstr "" +msgstr "Tidak dapat menggunakan GPIO0..15 bersamaan dengan GPIO32..47" #: ports/nordic/common-hal/alarm/pin/PinAlarm.c msgid "Cannot wake on pin edge, only level" @@ -873,7 +873,7 @@ msgstr "Tipe array koordinat memiliki ukuran yang berbeda" #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" -msgstr "" +msgstr "Tidak dapat mempublikasikan ke topik ROS" #: shared-bindings/_bleio/Adapter.c msgid "Could not set address" @@ -890,7 +890,7 @@ msgstr "Tidak dapat mengalokasikan dekoder" #: ports/espressif/common-hal/rclcpy/__init__.c #, c-format msgid "Critical ROS failure during soft reboot, reset required: %d" -msgstr "" +msgstr "Kegagalan ROS kritis selama soft reboot, reset diperlukan: %d" #: ports/stm/common-hal/analogio/AnalogOut.c msgid "DAC Channel Init Error" @@ -1009,28 +1009,30 @@ msgstr "Gagal memperoleh mutex, err 0x%04x" #: ports/raspberrypi/common-hal/mdns/Server.c msgid "Failed to add service TXT record" -msgstr "" +msgstr "Gagal menambahkan record TXT layanan" #: shared-bindings/mdns/Server.c msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" +"Gagal menambahkan record TXT layanan; ditemukan non-string atau bytes dalam " +"txt_records" #: shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" -msgstr "" +msgstr "Gagal mengalokasikan buffer %q" #: ports/espressif/common-hal/wifi/__init__.c msgid "Failed to allocate Wifi memory" -msgstr "" +msgstr "Gagal mengalokasikan memori Wifi" #: ports/espressif/common-hal/wifi/ScannedNetworks.c msgid "Failed to allocate wifi scan memory" -msgstr "" +msgstr "Gagal mengalokasikan memori pemindaian wifi" #: ports/stm/common-hal/audiopwmio/PWMAudioOut.c msgid "Failed to buffer the sample" -msgstr "" +msgstr "Gagal membuffer sampel" #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c @@ -1043,23 +1045,23 @@ msgstr "Gagal terhubung: habis waktu" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to create continuous channels: invalid arg" -msgstr "" +msgstr "Gagal membuat kanal berkelanjutan: argumen tidak valid" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to create continuous channels: invalid state" -msgstr "" +msgstr "Gagal membuat kanal berkelanjutan: status tidak valid" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to create continuous channels: no mem" -msgstr "" +msgstr "Gagal membuat kanal berkelanjutan: tidak ada memori" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to create continuous channels: not found" -msgstr "" +msgstr "Gagal membuat kanal berkelanjutan: tidak ditemukan" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to enable continuous" -msgstr "" +msgstr "Gagal mengaktifkan berkelanjutan" #: shared-module/audiomp3/MP3Decoder.c msgid "Failed to parse MP3 file" @@ -1067,7 +1069,7 @@ msgstr "Gagal mengurai file MP3" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to register continuous events callback" -msgstr "" +msgstr "Gagal mendaftarkan callback event berkelanjutan" #: ports/nordic/sd_mutex.c #, c-format @@ -1076,11 +1078,11 @@ msgstr "Gagal melepaskan mutex, err 0x%04x" #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" -msgstr "" +msgstr "Gagal menetapkan hostname" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "Failed to start async audio" -msgstr "" +msgstr "Gagal memulai audio async" #: supervisor/shared/safe_mode.c msgid "Failed to write internal flash." @@ -1093,43 +1095,44 @@ msgstr "File sudah ada" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c #: shared-module/os/getenv.c msgid "File not found" -msgstr "" +msgstr "File tidak ditemukan" #: ports/atmel-samd/common-hal/canio/Listener.c #: ports/espressif/common-hal/canio/Listener.c #: ports/mimxrt10xx/common-hal/canio/Listener.c #: ports/stm/common-hal/canio/Listener.c msgid "Filters too complex" -msgstr "" +msgstr "Filter terlalu kompleks" #: ports/espressif/common-hal/dualbank/__init__.c msgid "Firmware is duplicate" -msgstr "" +msgstr "Firmware adalah duplikat" #: ports/espressif/common-hal/dualbank/__init__.c msgid "Firmware is invalid" -msgstr "" +msgstr "Firmware tidak valid" #: ports/espressif/common-hal/dualbank/__init__.c msgid "Firmware is too big" -msgstr "" +msgstr "Firmware terlalu besar" #: shared-bindings/bitmaptools/__init__.c msgid "For L8 colorspace, input bitmap must have 8 bits per pixel" -msgstr "" +msgstr "Untuk colorspace L8, bitmap input harus memiliki 8 bit per piksel" #: shared-bindings/bitmaptools/__init__.c msgid "For RGB colorspaces, input bitmap must have 16 bits per pixel" -msgstr "" +msgstr "Untuk colorspace RGB, bitmap input harus memiliki 16 bit per piksel" #: ports/cxd56/common-hal/camera/Camera.c shared-module/audiocore/WaveFile.c msgid "Format not supported" -msgstr "" +msgstr "Format tidak didukung" #: ports/mimxrt10xx/common-hal/microcontroller/Processor.c msgid "" "Frequency must be 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 or 1008 Mhz" msgstr "" +"Frekuensi harus 24, 150, 396, 450, 528, 600, 720, 816, 912, 960 atau 1008 Mhz" #: shared-bindings/bitbangio/I2C.c shared-bindings/bitbangio/SPI.c #: shared-bindings/busio/I2C.c shared-bindings/busio/SPI.c @@ -1138,11 +1141,11 @@ msgstr "Fungsinya membutuhkan kunci" #: ports/cxd56/common-hal/gnss/GNSS.c msgid "GNSS init" -msgstr "" +msgstr "Inisialisasi GNSS" #: ports/espressif/common-hal/espidf/__init__.c msgid "Generic Failure" -msgstr "" +msgstr "Kegagalan Umum" #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c @@ -1152,7 +1155,7 @@ msgstr "Grup sudah digunakan" #: supervisor/shared/safe_mode.c msgid "Hard fault: memory access or instruction error." -msgstr "" +msgstr "Hard fault: kesalahan akses memori atau instruksi." #: ports/mimxrt10xx/common-hal/busio/SPI.c #: ports/mimxrt10xx/common-hal/busio/UART.c ports/stm/common-hal/busio/I2C.c @@ -1163,7 +1166,7 @@ msgstr "Perangkat keras sedang digunakan, coba pin alternatif" #: supervisor/shared/safe_mode.c msgid "Heap allocation when VM not running." -msgstr "" +msgstr "Alokasi heap ketika VM tidak berjalan." #: extmod/vfs_posix_file.c py/objstringio.c msgid "I/O operation on closed file" @@ -1171,16 +1174,16 @@ msgstr "operasi I/O pada file tertutup" #: ports/stm/common-hal/busio/I2C.c msgid "I2C init error" -msgstr "" +msgstr "Kesalahan inisialisasi I2C" #: ports/raspberrypi/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c msgid "I2C peripheral in use" -msgstr "" +msgstr "Periferal I2C sedang digunakan" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "In-buffer elements must be <= 4 bytes long" -msgstr "" +msgstr "Elemen in-buffer harus <= 4 byte panjangnya" #: shared-bindings/_pew/PewPew.c msgid "Incorrect buffer size" @@ -1188,24 +1191,25 @@ msgstr "Ukuran penyangga salah" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Init program size invalid" -msgstr "" +msgstr "Ukuran program inisialisasi tidak valid" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Initial set pin direction conflicts with initial out pin direction" -msgstr "" +msgstr "Arah pin set awal berkonflik dengan arah pin out awal" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Initial set pin state conflicts with initial out pin state" -msgstr "" +msgstr "Status pin set awal berkonflik dengan status pin out awal" #: shared-bindings/bitops/__init__.c #, c-format msgid "Input buffer length (%d) must be a multiple of the strand count (%d)" msgstr "" +"Panjang buffer input (%d) harus merupakan kelipatan dari jumlah strand (%d)" #: ports/atmel-samd/common-hal/pulseio/PulseIn.c msgid "Input taking too long" -msgstr "" +msgstr "Input memakan waktu terlalu lama" #: py/moderrno.c msgid "Input/output error" @@ -1223,20 +1227,20 @@ msgstr "Enkripsi tidak cukup" #: shared-module/jpegio/JpegDecoder.c msgid "Insufficient memory pool for the image" -msgstr "" +msgstr "Pool memori tidak mencukupi untuk gambar" #: shared-module/jpegio/JpegDecoder.c msgid "Insufficient stream input buffer" -msgstr "" +msgstr "Buffer input stream tidak mencukupi" #: ports/espressif/common-hal/wifi/Radio.c #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Interface must be started" -msgstr "" +msgstr "Interface harus dimulai" #: ports/atmel-samd/audio_dma.c ports/raspberrypi/audio_dma.c msgid "Internal audio buffer too small" -msgstr "" +msgstr "Buffer audio internal terlalu kecil" #: ports/stm/common-hal/busio/UART.c msgid "Internal define error" @@ -1244,7 +1248,7 @@ msgstr "Kesalahan definisi internal" #: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c msgid "Internal error" -msgstr "" +msgstr "Kesalahan internal" #: shared-module/rgbmatrix/RGBMatrix.c #, c-format @@ -1262,19 +1266,19 @@ msgstr "Kesalahan internal #%d" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-bindings/pwmio/PWMOut.c msgid "Internal resource(s) in use" -msgstr "" +msgstr "Sumber daya internal sedang digunakan" #: supervisor/shared/safe_mode.c msgid "Internal watchdog timer expired." -msgstr "" +msgstr "Timer watchdog internal kedaluwarsa." #: supervisor/shared/safe_mode.c msgid "Interrupt error." -msgstr "" +msgstr "Kesalahan interrupt." #: shared-module/jpegio/JpegDecoder.c msgid "Interrupted by output function" -msgstr "" +msgstr "Diinterupsi oleh fungsi output" #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c @@ -1290,12 +1294,12 @@ msgstr "" #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" -msgstr "" +msgstr "%q tidak valid" #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" -msgstr "" +msgstr "%q dan %q tidak valid" #: ports/atmel-samd/common-hal/microcontroller/Pin.c #: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c @@ -1311,19 +1315,19 @@ msgstr "Nilai Unit ADC tidak valid" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid BLE parameter" -msgstr "" +msgstr "Parameter BLE tidak valid" #: shared-bindings/wifi/Radio.c msgid "Invalid BSSID" -msgstr "" +msgstr "BSSID tidak valid" #: shared-bindings/wifi/Radio.c msgid "Invalid MAC address" -msgstr "" +msgstr "Alamat MAC tidak valid" #: ports/espressif/common-hal/rclcpy/__init__.c msgid "Invalid ROS domain ID" -msgstr "" +msgstr "ID domain ROS tidak valid" #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" @@ -1336,16 +1340,16 @@ msgstr "Bit per nilai tidak valid" #: shared-module/os/getenv.c #, c-format msgid "Invalid byte %.*s" -msgstr "" +msgstr "Byte tidak valid %.*s" #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" -msgstr "" +msgstr "data_pins[%d] tidak valid" #: shared-module/msgpack/__init__.c msgid "Invalid format" -msgstr "" +msgstr "Format tidak valid" #: shared-module/audiocore/WaveFile.c msgid "Invalid format chunk size" @@ -1353,29 +1357,29 @@ msgstr "Ukuran potongan format tidak valid" #: shared-bindings/wifi/Radio.c msgid "Invalid hex password" -msgstr "" +msgstr "Password hex tidak valid" #: ports/espressif/common-hal/wifi/Radio.c #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Invalid multicast MAC address" -msgstr "" +msgstr "Alamat MAC multicast tidak valid" #: ports/espressif/common-hal/espidf/__init__.c msgid "Invalid size" -msgstr "" +msgstr "Ukuran tidak valid" #: shared-module/ssl/SSLSocket.c msgid "Invalid socket for TLS" -msgstr "" +msgstr "Socket tidak valid untuk TLS" #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" -msgstr "" +msgstr "Status tidak valid" #: shared-module/os/getenv.c msgid "Invalid unicode escape" -msgstr "" +msgstr "Escape unicode tidak valid" #: shared-bindings/aesio/aes.c msgid "Key must be 16, 24, or 32 bytes long" @@ -1383,11 +1387,11 @@ msgstr "Panjang kunci harus 16, 24, atau 32 byte" #: shared-module/os/getenv.c msgid "Key not found" -msgstr "" +msgstr "Kunci tidak ditemukan" #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" -msgstr "" +msgstr "Pemetaan LED harus sesuai dengan ukuran display" #: py/compile.c msgid "LHS of keyword arg must be an id" @@ -1395,74 +1399,75 @@ msgstr "LHS dari keyword arg harus menjadi sebuah id" #: shared-module/displayio/Group.c msgid "Layer already in a group" -msgstr "" +msgstr "Layer sudah ada dalam grup" #: shared-module/displayio/Group.c msgid "Layer must be a Group or TileGrid subclass" -msgstr "" +msgstr "Layer harus merupakan subclass Group atau TileGrid" #: shared-bindings/audiocore/RawSample.c msgid "Length of %q must be an even multiple of channel_count * type_size" msgstr "" +"Panjang %q harus merupakan kelipatan genap dari channel_count * type_size" #: ports/espressif/common-hal/espidf/__init__.c msgid "MAC address was invalid" -msgstr "" +msgstr "Alamat MAC tidak valid" #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/espressif/common-hal/_bleio/Descriptor.c msgid "MITM security not supported" -msgstr "" +msgstr "Keamanan MITM tidak didukung" #: ports/stm/common-hal/sdioio/SDCard.c #, c-format msgid "MMC/SDIO Clock Error %x" -msgstr "" +msgstr "Kesalahan Clock MMC/SDIO %x" #: shared-bindings/is31fl3741/IS31FL3741.c msgid "Mapping must be a tuple" -msgstr "" +msgstr "Mapping harus berupa tuple" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" -msgstr "" +msgstr "Ukuran data tidak cocok" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched swap flag" -msgstr "" +msgstr "Flag swap tidak cocok" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_in_pin. %q[%u] reads pin(s)" -msgstr "" +msgstr "first_in_pin tidak ada. %q[%u] membaca pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_in_pin. %q[%u] shifts in from pin(s)" -msgstr "" +msgstr "first_in_pin tidak ada. %q[%u] melakukan shift in dari pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_in_pin. %q[%u] waits based on pin" -msgstr "" +msgstr "first_in_pin tidak ada. %q[%u] menunggu berdasarkan pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_out_pin. %q[%u] shifts out to pin(s)" -msgstr "" +msgstr "first_out_pin tidak ada. %q[%u] melakukan shift out ke pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_out_pin. %q[%u] writes pin(s)" -msgstr "" +msgstr "first_out_pin tidak ada. %q[%u] menulis pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing first_set_pin. %q[%u] sets pin(s)" -msgstr "" +msgstr "first_set_pin tidak ada. %q[%u] menetapkan pin" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Missing jmp_pin. %q[%u] jumps on pin" -msgstr "" +msgstr "jmp_pin tidak ada. %q[%u] melompat pada pin" #: shared-module/storage/__init__.c msgid "Mount point directory missing" -msgstr "" +msgstr "Direktori titik mount tidak ada" #: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c #: shared-bindings/displayio/Group.c @@ -1471,7 +1476,7 @@ msgstr "Harus berupa subclass %q." #: ports/espressif/common-hal/dotclockframebuffer/DotClockFramebuffer.c msgid "Must provide 5/6/5 RGB pins" -msgstr "" +msgstr "Harus menyediakan pin RGB 5/6/5" #: ports/mimxrt10xx/common-hal/busio/SPI.c shared-bindings/busio/SPI.c msgid "Must provide MISO or MOSI pin" @@ -1484,23 +1489,23 @@ msgstr "Harus menggunakan kelipatan 6 pin rgb, bukan %d" #: supervisor/shared/safe_mode.c msgid "NLR jump failed. Likely memory corruption." -msgstr "" +msgstr "Lompatan NLR gagal. Kemungkinan korupsi memori." #: ports/espressif/common-hal/nvm/ByteArray.c msgid "NVS Error" -msgstr "" +msgstr "Kesalahan NVS" #: shared-bindings/socketpool/SocketPool.c msgid "Name or service not known" -msgstr "" +msgstr "Nama atau layanan tidak diketahui" #: shared-bindings/displayio/TileGrid.c msgid "New bitmap must be same size as old bitmap" -msgstr "" +msgstr "Bitmap baru harus berukuran sama dengan bitmap lama" #: ports/espressif/common-hal/_bleio/__init__.c msgid "Nimble out of memory" -msgstr "" +msgstr "Nimble kehabisan memori" #: ports/atmel-samd/common-hal/busio/UART.c #: ports/espressif/common-hal/busio/SPI.c @@ -1535,26 +1540,26 @@ msgstr "tidak ada channel DMA ditemukan" #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c msgid "No DMA pacing timer found" -msgstr "" +msgstr "Tidak ditemukan timer pacing DMA" #: shared-module/adafruit_bus_device/i2c_device/I2CDevice.c #, c-format msgid "No I2C device at address: 0x%x" -msgstr "" +msgstr "Tidak ada perangkat I2C pada alamat: 0x%x" #: supervisor/shared/web_workflow/web_workflow.c msgid "No IP" -msgstr "" +msgstr "Tidak ada IP" #: ports/atmel-samd/common-hal/microcontroller/__init__.c #: ports/cxd56/common-hal/microcontroller/__init__.c #: ports/mimxrt10xx/common-hal/microcontroller/__init__.c msgid "No bootloader present" -msgstr "" +msgstr "Tidak ada bootloader" #: shared-module/usb/core/Device.c msgid "No configuration set" -msgstr "" +msgstr "Tidak ada konfigurasi yang ditetapkan" #: shared-bindings/_bleio/PacketBuffer.c msgid "No connection: length cannot be determined" @@ -1576,11 +1581,11 @@ msgstr "Tidak ada perangkat keras acak yang tersedia" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "No in in program" -msgstr "" +msgstr "Tidak ada in dalam program" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "No in or out in program" -msgstr "" +msgstr "Tidak ada in atau out dalam program" #: py/objint.c shared-bindings/time/__init__.c msgid "No long integer support" @@ -1588,18 +1593,18 @@ msgstr "Tidak ada dukungan bilangan bulat yang panjang" #: shared-bindings/wifi/Radio.c msgid "No network with that ssid" -msgstr "" +msgstr "Tidak ada jaringan dengan ssid tersebut" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "No out in program" -msgstr "" +msgstr "Tidak ada out dalam program" #: ports/atmel-samd/common-hal/busio/I2C.c #: ports/espressif/common-hal/busio/I2C.c #: ports/mimxrt10xx/common-hal/busio/I2C.c ports/nordic/common-hal/busio/I2C.c #: ports/raspberrypi/common-hal/busio/I2C.c msgid "No pull up found on SDA or SCL; check your wiring" -msgstr "" +msgstr "Tidak ditemukan pull up pada SDA atau SCL; periksa pengkabelan Anda" #: shared-module/touchio/TouchIn.c msgid "No pulldown on pin; 1Mohm recommended" @@ -1607,7 +1612,7 @@ msgstr "Tidak ada pull-down pada pin; 1Mohm direkomendasikan" #: shared-module/touchio/TouchIn.c msgid "No pullup on pin; 1Mohm recommended" -msgstr "" +msgstr "Tidak ada pullup pada pin; 1Mohm direkomendasikan" #: py/moderrno.c msgid "No space left on device" @@ -1615,7 +1620,7 @@ msgstr "Tidak ada ruang yang tersisa di perangkat" #: py/moderrno.c msgid "No such device" -msgstr "" +msgstr "Perangkat tidak ada" #: py/moderrno.c msgid "No such file/directory" @@ -1627,15 +1632,15 @@ msgstr "Penghitung waktu tidak tersedia" #: shared-module/usb/core/Device.c msgid "No usb host port initialized" -msgstr "" +msgstr "Tidak ada port host usb yang diinisialisasi" #: ports/nordic/common-hal/_bleio/__init__.c msgid "Nordic system firmware out of memory" -msgstr "" +msgstr "Firmware sistem Nordic kehabisan memori" #: shared-bindings/ipaddress/IPv4Address.c shared-bindings/ipaddress/__init__.c msgid "Not a valid IP string" -msgstr "" +msgstr "Bukan string IP yang valid" #: ports/espressif/common-hal/_bleio/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c @@ -1656,7 +1661,7 @@ msgstr "" #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format msgid "Number of data_pins must be %d or %d, not %d" -msgstr "" +msgstr "Jumlah data_pins harus %d atau %d, bukan %d" #: shared-bindings/util.c msgid "" @@ -1670,17 +1675,17 @@ msgstr "Parity ganjil tidak didukung" #: supervisor/shared/bluetooth/bluetooth.c msgid "Off" -msgstr "" +msgstr "Mati" #: supervisor/shared/bluetooth/bluetooth.c msgid "Ok" -msgstr "" +msgstr "Ok" #: ports/atmel-samd/common-hal/audiobusio/PDMIn.c #: ports/raspberrypi/common-hal/audiobusio/PDMIn.c #, c-format msgid "Only 8 or 16 bit mono with %dx oversampling supported." -msgstr "" +msgstr "Hanya mono 8 atau 16 bit dengan oversampling %dx yang didukung." #: ports/espressif/common-hal/wifi/__init__.c #: ports/raspberrypi/common-hal/wifi/__init__.c @@ -1701,51 +1706,51 @@ msgstr "" #: shared-bindings/_bleio/Adapter.c msgid "Only connectable advertisements can be directed" -msgstr "" +msgstr "Hanya advertisement yang dapat terhubung yang dapat diarahkan" #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Only edge detection is available on this hardware" -msgstr "" +msgstr "Hanya deteksi tepi yang tersedia pada perangkat keras ini" #: shared-bindings/ipaddress/__init__.c msgid "Only int or string supported for ip" -msgstr "" +msgstr "Hanya int atau string yang didukung untuk ip" #: ports/espressif/common-hal/alarm/touch/TouchAlarm.c msgid "Only one %q can be set in deep sleep." -msgstr "" +msgstr "Hanya satu %q yang dapat ditetapkan dalam deep sleep." #: ports/espressif/common-hal/espulp/ULPAlarm.c msgid "Only one %q can be set." -msgstr "" +msgstr "Hanya satu %q yang dapat ditetapkan." #: ports/espressif/common-hal/i2ctarget/I2CTarget.c #: ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c msgid "Only one address is allowed" -msgstr "" +msgstr "Hanya satu alamat yang diperbolehkan" #: ports/atmel-samd/common-hal/alarm/time/TimeAlarm.c #: ports/nordic/common-hal/alarm/time/TimeAlarm.c #: ports/stm/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set" -msgstr "" +msgstr "Hanya satu alarm alarm.time yang dapat ditetapkan" #: ports/espressif/common-hal/alarm/time/TimeAlarm.c #: ports/raspberrypi/common-hal/alarm/time/TimeAlarm.c msgid "Only one alarm.time alarm can be set." -msgstr "" +msgstr "Hanya satu alarm alarm.time yang dapat ditetapkan." #: shared-module/displayio/ColorConverter.c msgid "Only one color can be transparent at a time" -msgstr "" +msgstr "Hanya satu warna yang dapat transparan pada satu waktu" #: py/moderrno.c msgid "Operation not permitted" -msgstr "" +msgstr "Operasi tidak diizinkan" #: ports/espressif/common-hal/espidf/__init__.c msgid "Operation or feature not supported" -msgstr "" +msgstr "Operasi atau fitur tidak didukung" #: ports/espressif/common-hal/espidf/__init__.c msgid "Operation timed out" @@ -1753,7 +1758,7 @@ msgstr "Waktu habis" #: ports/raspberrypi/common-hal/mdns/Server.c msgid "Out of MDNS service slots" -msgstr "" +msgstr "Kehabisan slot layanan MDNS" #: ports/espressif/common-hal/espidf/__init__.c msgid "Out of memory" @@ -1767,27 +1772,27 @@ msgstr "Kehabisan socket" #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Out-buffer elements must be <= 4 bytes long" -msgstr "" +msgstr "Elemen out-buffer harus <= 4 byte panjangnya" #: ports/stm/common-hal/pwmio/PWMOut.c msgid "PWM restart" -msgstr "" +msgstr "Restart PWM" #: ports/raspberrypi/common-hal/countio/Counter.c msgid "PWM slice already in use" -msgstr "" +msgstr "Slice PWM sudah digunakan" #: ports/raspberrypi/common-hal/countio/Counter.c msgid "PWM slice channel A already in use" -msgstr "" +msgstr "Channel A slice PWM sudah digunakan" #: shared-bindings/spitarget/SPITarget.c msgid "Packet buffers for an SPI transfer must have the same length." -msgstr "" +msgstr "Buffer paket untuk transfer SPI harus memiliki panjang yang sama." #: shared-module/jpegio/JpegDecoder.c msgid "Parameter error" -msgstr "" +msgstr "Kesalahan parameter" #: ports/espressif/common-hal/audiobusio/__init__.c msgid "Peripheral in use" @@ -1799,7 +1804,7 @@ msgstr "Izin ditolak" #: ports/stm/common-hal/alarm/pin/PinAlarm.c msgid "Pin cannot wake from Deep Sleep" -msgstr "" +msgstr "Pin tidak dapat membangunkan dari Deep Sleep" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Pin count too large" @@ -1843,7 +1848,7 @@ msgstr "Pin harus berbagi potongan PWM" #: shared-module/usb/core/Device.c msgid "Pipe error" -msgstr "" +msgstr "Kesalahan pipe" #: py/builtinhelp.c msgid "Plus any modules on the filesystem\n" @@ -1869,7 +1874,7 @@ msgstr "" #: main.c msgid "Pretending to deep sleep until alarm, CTRL-C or file write.\n" -msgstr "" +msgstr "Berpura-pura deep sleep sampai alarm, CTRL-C atau penulisan file.\n" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Program does IN without loading ISR" @@ -1889,7 +1894,7 @@ msgstr "Program terlalu lama" #: shared-bindings/rclcpy/Publisher.c msgid "Publishers can only be created from a parent node" -msgstr "" +msgstr "Publisher hanya dapat dibuat dari node induk" #: shared-bindings/digitalio/DigitalInOut.c msgid "Pull not used when direction is output." @@ -1913,23 +1918,23 @@ msgstr "Kesalahan Init RNG" #: ports/espressif/common-hal/rclcpy/__init__.c msgid "ROS failed to initialize. Is agent connected?" -msgstr "" +msgstr "ROS gagal diinisialisasi. Apakah agen terhubung?" #: ports/espressif/common-hal/rclcpy/__init__.c msgid "ROS internal setup failure" -msgstr "" +msgstr "Kegagalan pengaturan internal ROS" #: ports/espressif/common-hal/rclcpy/__init__.c msgid "ROS memory allocator failure" -msgstr "" +msgstr "Kegagalan alokator memori ROS" #: ports/espressif/common-hal/rclcpy/Node.c msgid "ROS node failed to initialize" -msgstr "" +msgstr "Node ROS gagal diinisialisasi" #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "ROS topic failed to initialize" -msgstr "" +msgstr "Topik ROS gagal diinisialisasi" #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c @@ -2003,18 +2008,18 @@ msgstr "Kartu SD Format CSD tidak didukung" #: ports/cxd56/common-hal/sdioio/SDCard.c msgid "SDCard init" -msgstr "" +msgstr "Inisialisasi SDCard" #: ports/stm/common-hal/sdioio/SDCard.c #, c-format msgid "SDIO GetCardInfo Error %d" -msgstr "" +msgstr "Kesalahan GetCardInfo SDIO %d" #: ports/espressif/common-hal/sdioio/SDCard.c #: ports/stm/common-hal/sdioio/SDCard.c #, c-format msgid "SDIO Init Error %x" -msgstr "" +msgstr "Kesalahan Init SDIO %x" #: ports/espressif/common-hal/busio/SPI.c msgid "SPI configuration failed" @@ -2022,7 +2027,7 @@ msgstr "Konfigurasi SPI gagal" #: ports/stm/common-hal/busio/SPI.c msgid "SPI init error" -msgstr "" +msgstr "Kesalahan inisialisasi SPI" #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" @@ -2030,7 +2035,7 @@ msgstr "Periferal SPI sedang digunakan" #: ports/stm/common-hal/busio/SPI.c msgid "SPI re-init" -msgstr "" +msgstr "Inisialisasi ulang SPI" #: shared-bindings/is31fl3741/FrameBuffer.c msgid "Scale dimensions must divide by 3" @@ -2146,7 +2151,7 @@ msgstr "Lebar ubin harus persis membagi lebar bitmap" #: shared-module/tilepalettemapper/TilePaletteMapper.c msgid "TilePaletteMapper may only be bound to a TileGrid once" -msgstr "" +msgstr "TilePaletteMapper hanya dapat diikat ke TileGrid sekali" #: shared-bindings/alarm/time/TimeAlarm.c msgid "Time is in the past." @@ -2197,12 +2202,12 @@ msgstr "Traceback (bagian terakhir dari panggilan terkini):\n" #: ports/stm/common-hal/busio/UART.c msgid "UART de-init" -msgstr "" +msgstr "De-inisialisasi UART" #: ports/cxd56/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c #: ports/stm/common-hal/busio/UART.c msgid "UART init" -msgstr "" +msgstr "Inisialisasi UART" #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" @@ -2210,11 +2215,11 @@ msgstr "Periferal UART sedang digunakan" #: ports/stm/common-hal/busio/UART.c msgid "UART re-init" -msgstr "" +msgstr "Inisialisasi ulang UART" #: ports/stm/common-hal/busio/UART.c msgid "UART write" -msgstr "" +msgstr "Penulisan UART" #: main.c msgid "UID:" @@ -2222,7 +2227,7 @@ msgstr "UID:" #: shared-module/usb_hid/Device.c msgid "USB busy" -msgstr "" +msgstr "USB sibuk" #: supervisor/shared/safe_mode.c msgid "USB devices need more endpoints than are available." @@ -2234,7 +2239,7 @@ msgstr "Perangkat USB menggunakan terlalu banyak nama antarmuka." #: shared-module/usb_hid/Device.c msgid "USB error" -msgstr "" +msgstr "Kesalahan USB" #: shared-bindings/_bleio/UUID.c msgid "UUID integer value must be 0-0xffff" @@ -2283,7 +2288,7 @@ msgstr "Tidak dapat membaca data palet warna" #: ports/mimxrt10xx/common-hal/canio/CAN.c msgid "Unable to send CAN Message: all Tx message buffers are busy" -msgstr "" +msgstr "Tidak dapat mengirim Pesan CAN: semua buffer pesan Tx sibuk" #: ports/espressif/common-hal/mdns/Server.c #: ports/raspberrypi/common-hal/mdns/Server.c @@ -2373,7 +2378,7 @@ msgstr "" #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" -msgstr "" +msgstr "Colorspace tidak didukung" #: shared-module/displayio/bus_core.c msgid "Unsupported display bus type" @@ -2439,17 +2444,17 @@ msgstr "" #: supervisor/shared/web_workflow/web_workflow.c msgid "Wi-Fi: " -msgstr "" +msgstr "Wi-Fi: " #: ports/espressif/common-hal/wifi/Radio.c #: ports/raspberrypi/common-hal/wifi/Radio.c #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "WiFi is not enabled" -msgstr "" +msgstr "WiFi tidak diaktifkan" #: main.c msgid "Woken up by alarm.\n" -msgstr "" +msgstr "Dibangunkan oleh alarm.\n" #: ports/espressif/common-hal/_bleio/PacketBuffer.c #: ports/nordic/common-hal/_bleio/PacketBuffer.c @@ -2528,7 +2533,7 @@ msgstr "__init __() harus mengembalikan None" #: py/objtype.c #, c-format msgid "__init__() should return None, not '%s'" -msgstr "" +msgstr "__init__() harus mengembalikan None, bukan '%s'" #: py/objobject.c msgid "__new__ arg must be a user-type" @@ -2544,7 +2549,7 @@ msgstr "alamatnya kosong" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "already playing" -msgstr "" +msgstr "sudah diputar" #: py/compile.c msgid "annotation must be an identifier" @@ -2568,15 +2573,15 @@ msgstr "Argumen argsort harus berupa ndarray" #: extmod/ulab/code/numpy/numerical.c msgid "argsort is not implemented for flattened arrays" -msgstr "" +msgstr "argsort tidak diimplementasikan untuk array yang diratakan" #: extmod/ulab/code/numpy/random/random.c msgid "argument must be None, an integer or a tuple of integers" -msgstr "" +msgstr "argumen harus None, integer atau tuple integer" #: py/compile.c msgid "argument name reused" -msgstr "" +msgstr "nama argumen digunakan kembali" #: py/argcheck.c shared-bindings/_stage/__init__.c #: shared-bindings/digitalio/DigitalInOut.c @@ -2589,15 +2594,15 @@ msgstr "argumen harus berupa ndarrays" #: extmod/ulab/code/ndarray.c msgid "array and index length must be equal" -msgstr "" +msgstr "panjang array dan indeks harus sama" #: extmod/ulab/code/numpy/io/io.c msgid "array has too many dimensions" -msgstr "" +msgstr "array memiliki terlalu banyak dimensi" #: extmod/ulab/code/ndarray.c msgid "array is too big" -msgstr "" +msgstr "array terlalu besar" #: py/objarray.c shared-bindings/alarm/SleepMemory.c #: shared-bindings/memorymap/AddressRange.c shared-bindings/nvm/ByteArray.c @@ -2606,15 +2611,15 @@ msgstr "diperlukan array/byte di sisi kanan" #: py/asmxtensa.c msgid "asm overflow" -msgstr "" +msgstr "asm overflow" #: py/compile.c msgid "async for/with outside async function" -msgstr "" +msgstr "async for/with di luar fungsi async" #: extmod/ulab/code/numpy/numerical.c msgid "attempt to get (arg)min/(arg)max of empty sequence" -msgstr "" +msgstr "percobaan untuk mendapatkan (arg)min/(arg)max dari urutan kosong" #: extmod/ulab/code/numpy/numerical.c msgid "attempt to get argmin/argmax of an empty sequence" @@ -2622,27 +2627,27 @@ msgstr "berusaha mendapatkan argmin/argmax dari urutan kosong" #: py/objstr.c msgid "attributes not supported" -msgstr "" +msgstr "atribut tidak didukung" #: ports/espressif/common-hal/audioio/AudioOut.c msgid "audio format not supported" -msgstr "" +msgstr "format audio tidak didukung" #: extmod/ulab/code/ulab_tools.c msgid "axis is out of bounds" -msgstr "" +msgstr "axis berada di luar batas" #: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c msgid "axis must be None, or an integer" -msgstr "" +msgstr "axis harus None, atau integer" #: extmod/ulab/code/numpy/numerical.c msgid "axis too long" -msgstr "" +msgstr "axis terlalu panjang" #: shared-bindings/bitmaptools/__init__.c msgid "background value out of range of target" -msgstr "" +msgstr "nilai latar belakang di luar jangkauan target" #: py/builtinevex.c msgid "bad compile mode" @@ -2654,7 +2659,7 @@ msgstr "specifier salah konversi" #: py/objstr.c msgid "bad format string" -msgstr "" +msgstr "string format salah" #: py/binary.c py/objarray.c msgid "bad typecode" @@ -2662,27 +2667,27 @@ msgstr "typecode buruk" #: py/emitnative.c msgid "binary op %q not implemented" -msgstr "" +msgstr "operasi biner %q tidak diimplementasikan" #: ports/espressif/common-hal/audiobusio/PDMIn.c msgid "bit_depth must be 8, 16, 24, or 32." -msgstr "" +msgstr "bit_depth harus 8, 16, 24, atau 32." #: shared-module/bitmapfilter/__init__.c msgid "bitmap size and depth must match" -msgstr "" +msgstr "ukuran dan kedalaman bitmap harus cocok" #: shared-bindings/bitmaptools/__init__.c msgid "bitmap sizes must match" -msgstr "" +msgstr "ukuran bitmap harus cocok" #: extmod/modrandom.c msgid "bits must be 32 or less" -msgstr "" +msgstr "bit harus 32 atau kurang" #: shared-bindings/audiofreeverb/Freeverb.c msgid "bits_per_sample must be 16" -msgstr "" +msgstr "bits_per_sample harus 16" #: shared-bindings/audiodelays/Chorus.c shared-bindings/audiodelays/Echo.c #: shared-bindings/audiodelays/MultiTapDelay.c @@ -2691,19 +2696,19 @@ msgstr "" #: shared-bindings/audiofilters/Filter.c shared-bindings/audiofilters/Phaser.c #: shared-bindings/audiomixer/Mixer.c msgid "bits_per_sample must be 8 or 16" -msgstr "" +msgstr "bits_per_sample harus 8 atau 16" #: py/emitinlinethumb.c msgid "branch not in range" -msgstr "" +msgstr "branch tidak dalam jangkauan" #: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c msgid "buffer is smaller than requested size" -msgstr "" +msgstr "buffer lebih kecil dari ukuran yang diminta" #: extmod/ulab/code/numpy/create.c extmod/ulab/code/utils/utils.c msgid "buffer size must be a multiple of element size" -msgstr "" +msgstr "ukuran buffer harus kelipatan dari ukuran elemen" #: shared-module/struct/__init__.c msgid "buffer size must match format" @@ -2711,27 +2716,27 @@ msgstr "ukuran buffer harus sesuai dengan format" #: shared-bindings/bitbangio/SPI.c shared-bindings/busio/SPI.c msgid "buffer slices must be of equal length" -msgstr "" +msgstr "potongan buffer harus memiliki panjang yang sama" #: py/modstruct.c shared-module/struct/__init__.c msgid "buffer too small" -msgstr "" +msgstr "buffer terlalu kecil" #: shared-bindings/socketpool/Socket.c shared-bindings/ssl/SSLSocket.c msgid "buffer too small for requested bytes" -msgstr "" +msgstr "buffer terlalu kecil untuk byte yang diminta" #: py/emitbc.c msgid "bytecode overflow" -msgstr "" +msgstr "bytecode overflow" #: py/objarray.c msgid "bytes length not a multiple of item size" -msgstr "" +msgstr "panjang bytes bukan kelipatan dari ukuran item" #: py/objstr.c msgid "bytes value out of range" -msgstr "" +msgstr "nilai bytes di luar jangkauan" #: ports/atmel-samd/bindings/samd/Clock.c msgid "calibration is out of range" @@ -2744,7 +2749,7 @@ msgstr "kalibrasi adalah read only" #: shared-module/vectorio/Circle.c shared-module/vectorio/Polygon.c #: shared-module/vectorio/Rectangle.c msgid "can only have one parent" -msgstr "" +msgstr "hanya dapat memiliki satu induk" #: py/emitinlinethumb.c msgid "can only have up to 4 parameters to Thumb assembly" @@ -2752,15 +2757,15 @@ msgstr "hanya mampu memiliki hingga 4 parameter untuk Thumb assembly" #: py/emitinlinextensa.c msgid "can only have up to 4 parameters to Xtensa assembly" -msgstr "" +msgstr "hanya dapat memiliki hingga 4 parameter untuk assembly Xtensa" #: extmod/ulab/code/ndarray.c msgid "can only specify one unknown dimension" -msgstr "" +msgstr "hanya dapat menentukan satu dimensi yang tidak diketahui" #: py/objtype.c msgid "can't add special method to already-subclassed class" -msgstr "" +msgstr "tidak dapat menambahkan metode khusus ke kelas yang sudah di-subclass" #: py/compile.c msgid "can't assign to expression" @@ -2768,50 +2773,50 @@ msgstr "tidak dapat menetapkan ke ekspresi" #: extmod/modasyncio.c msgid "can't cancel self" -msgstr "" +msgstr "tidak dapat membatalkan diri sendiri" #: py/obj.c shared-module/adafruit_pixelbuf/PixelBuf.c msgid "can't convert %q to %q" -msgstr "" +msgstr "tidak dapat mengonversi %q ke %q" #: py/obj.c #, c-format msgid "can't convert %s to complex" -msgstr "" +msgstr "tidak dapat mengonversi %s ke complex" #: py/obj.c #, c-format msgid "can't convert %s to float" -msgstr "" +msgstr "tidak dapat mengonversi %s ke float" #: py/objint.c py/runtime.c #, c-format msgid "can't convert %s to int" -msgstr "" +msgstr "tidak dapat mengonversi %s ke int" #: py/objstr.c msgid "can't convert '%q' object to %q implicitly" -msgstr "" +msgstr "tidak dapat mengonversi objek '%q' ke %q secara implisit" #: extmod/ulab/code/numpy/vector.c msgid "can't convert complex to float" -msgstr "" +msgstr "tidak dapat mengonversi complex ke float" #: py/obj.c msgid "can't convert to complex" -msgstr "" +msgstr "tidak dapat mengonversi ke complex" #: py/obj.c msgid "can't convert to float" -msgstr "" +msgstr "tidak dapat mengonversi ke float" #: py/runtime.c msgid "can't convert to int" -msgstr "" +msgstr "tidak dapat mengonversi ke int" #: py/objstr.c msgid "can't convert to str implicitly" -msgstr "" +msgstr "tidak dapat mengonversi ke str secara implisit" #: py/compile.c msgid "can't declare nonlocal in outer code" @@ -2823,93 +2828,95 @@ msgstr "tidak bisa menghapus ekspresi" #: py/emitnative.c msgid "can't do binary op between '%q' and '%q'" -msgstr "" +msgstr "tidak dapat melakukan operasi biner antara '%q' dan '%q'" #: py/emitnative.c msgid "can't do unary op of '%q'" -msgstr "" +msgstr "tidak dapat melakukan operasi unary dari '%q'" #: py/emitnative.c msgid "can't implicitly convert '%q' to 'bool'" -msgstr "" +msgstr "tidak dapat mengonversi '%q' ke 'bool' secara implisit" #: py/runtime.c msgid "can't import name %q" -msgstr "" +msgstr "tidak dapat mengimpor nama %q" #: py/emitnative.c msgid "can't load from '%q'" -msgstr "" +msgstr "tidak dapat memuat dari '%q'" #: py/emitnative.c msgid "can't load with '%q' index" -msgstr "" +msgstr "tidak dapat memuat dengan indeks '%q'" #: py/builtinimport.c msgid "can't perform relative import" -msgstr "" +msgstr "tidak dapat melakukan impor relatif" #: py/objgenerator.c msgid "can't send non-None value to a just-started generator" -msgstr "" +msgstr "tidak dapat mengirim nilai non-None ke generator yang baru dimulai" #: shared-module/sdcardio/SDCard.c msgid "can't set 512 block size" -msgstr "" +msgstr "tidak dapat mengatur ukuran blok 512" #: py/objexcept.c py/objnamedtuple.c msgid "can't set attribute" -msgstr "" +msgstr "tidak dapat mengatur atribut" #: py/runtime.c msgid "can't set attribute '%q'" -msgstr "" +msgstr "tidak dapat mengatur atribut '%q'" #: py/emitnative.c msgid "can't store '%q'" -msgstr "" +msgstr "tidak dapat menyimpan '%q'" #: py/emitnative.c msgid "can't store to '%q'" -msgstr "" +msgstr "tidak dapat menyimpan ke '%q'" #: py/emitnative.c msgid "can't store with '%q' index" -msgstr "" +msgstr "tidak dapat menyimpan dengan indeks '%q'" #: py/objstr.c msgid "" "can't switch from automatic field numbering to manual field specification" msgstr "" +"tidak dapat beralih dari penomoran field otomatis ke spesifikasi field manual" #: py/objstr.c msgid "" "can't switch from manual field specification to automatic field numbering" msgstr "" +"tidak dapat beralih dari spesifikasi field manual ke penomoran field otomatis" #: py/objcomplex.c msgid "can't truncate-divide a complex number" -msgstr "" +msgstr "tidak dapat truncate-divide bilangan complex" #: extmod/modasyncio.c msgid "can't wait" -msgstr "" +msgstr "tidak dapat menunggu" #: extmod/ulab/code/ndarray.c msgid "cannot assign new shape" -msgstr "" +msgstr "tidak dapat menetapkan bentuk baru" #: extmod/ulab/code/ndarray_operators.c msgid "cannot cast output with casting rule" -msgstr "" +msgstr "tidak dapat melakukan cast output dengan aturan casting" #: extmod/ulab/code/ndarray.c msgid "cannot convert complex to dtype" -msgstr "" +msgstr "tidak dapat mengonversi complex ke dtype" #: extmod/ulab/code/ndarray.c msgid "cannot convert complex type" -msgstr "" +msgstr "tidak dapat mengonversi tipe complex" #: py/objtype.c msgid "cannot create '%q' instances" @@ -2917,71 +2924,71 @@ msgstr "" #: py/objtype.c msgid "cannot create instance" -msgstr "" +msgstr "tidak dapat membuat instance" #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" -msgstr "" +msgstr "tidak dapat menghapus elemen array" #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" -msgstr "" +msgstr "tidak dapat membentuk ulang array" #: py/emitnative.c msgid "casting" -msgstr "" +msgstr "casting" #: ports/stm/common-hal/pwmio/PWMOut.c msgid "channel re-init" -msgstr "" +msgstr "inisialisasi ulang channel" #: shared-bindings/_stage/Text.c msgid "chars buffer too small" -msgstr "" +msgstr "buffer chars terlalu kecil" #: py/modbuiltins.c msgid "chr() arg not in range(0x110000)" -msgstr "" +msgstr "argumen chr() tidak dalam rentang(0x110000)" #: py/modbuiltins.c msgid "chr() arg not in range(256)" -msgstr "" +msgstr "argumen chr() tidak dalam rentang(256)" #: shared-bindings/bitmaptools/__init__.c msgid "clip point must be (x,y) tuple" -msgstr "" +msgstr "clip point harus berupa tuple (x,y)" #: shared-bindings/msgpack/ExtType.c msgid "code outside range 0~127" -msgstr "" +msgstr "kode di luar rentang 0~127" #: shared-bindings/displayio/Palette.c msgid "color buffer must be 3 bytes (RGB) or 4 bytes (RGB + pad byte)" -msgstr "" +msgstr "buffer warna harus 3 byte (RGB) atau 4 byte (RGB + pad byte)" #: shared-bindings/displayio/Palette.c msgid "color buffer must be a buffer, tuple, list, or int" -msgstr "" +msgstr "buffer warna harus berupa buffer, tuple, list, atau int" #: shared-bindings/displayio/Palette.c msgid "color buffer must be a bytearray or array of type 'b' or 'B'" -msgstr "" +msgstr "buffer warna harus berupa bytearray atau array bertipe 'b' atau 'B'" #: shared-bindings/displayio/Palette.c msgid "color must be between 0x000000 and 0xffffff" -msgstr "" +msgstr "warna harus antara 0x000000 dan 0xffffff" #: py/emitnative.c msgid "comparison of int and uint" -msgstr "" +msgstr "perbandingan int dan uint" #: py/objcomplex.c msgid "complex divide by zero" -msgstr "" +msgstr "pembagian complex dengan nol" #: py/objfloat.c py/parsenum.c msgid "complex values not supported" -msgstr "" +msgstr "nilai complex tidak didukung" #: extmod/modzlib.c msgid "compression header" @@ -2989,56 +2996,56 @@ msgstr "kompresi header" #: py/emitnative.c msgid "conversion to object" -msgstr "" +msgstr "konversi ke objek" #: extmod/ulab/code/numpy/filter.c msgid "convolve arguments must be linear arrays" -msgstr "" +msgstr "argumen convolve harus berupa array linear" #: extmod/ulab/code/numpy/filter.c msgid "convolve arguments must be ndarrays" -msgstr "" +msgstr "argumen convolve harus berupa ndarray" #: extmod/ulab/code/numpy/filter.c msgid "convolve arguments must not be empty" -msgstr "" +msgstr "argumen convolve tidak boleh kosong" #: extmod/ulab/code/numpy/io/io.c msgid "corrupted file" -msgstr "" +msgstr "file rusak" #: extmod/ulab/code/numpy/poly.c msgid "could not invert Vandermonde matrix" -msgstr "" +msgstr "tidak dapat membalik matriks Vandermonde" #: shared-module/sdcardio/SDCard.c msgid "couldn't determine SD card version" -msgstr "" +msgstr "tidak dapat menentukan versi kartu SD" #: extmod/ulab/code/numpy/numerical.c msgid "cross is defined for 1D arrays of length 3" -msgstr "" +msgstr "cross didefinisikan untuk array 1D dengan panjang 3" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "data must be iterable" -msgstr "" +msgstr "data harus dapat diiterasi" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "data must be of equal length" -msgstr "" +msgstr "data harus memiliki panjang yang sama" #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "data pin #%d in use" -msgstr "" +msgstr "pin data #%d sedang digunakan" #: extmod/ulab/code/ndarray.c msgid "data type not understood" -msgstr "" +msgstr "tipe data tidak dipahami" #: py/parsenum.c msgid "decimal numbers not supported" -msgstr "" +msgstr "angka desimal tidak didukung" #: py/compile.c msgid "default 'except' must be last" @@ -3046,60 +3053,62 @@ msgstr "'except' standar harus terakhir" #: shared-bindings/msgpack/__init__.c msgid "default is not a function" -msgstr "" +msgstr "default bukan fungsi" #: shared-bindings/audiobusio/PDMIn.c msgid "" "destination buffer must be a bytearray or array of type 'B' for bit_depth = 8" msgstr "" +"buffer tujuan harus berupa bytearray atau array bertipe 'B' untuk bit_depth " +"= 8" #: shared-bindings/audiobusio/PDMIn.c msgid "destination buffer must be an array of type 'H' for bit_depth = 16" -msgstr "" +msgstr "buffer tujuan harus berupa array bertipe 'H' untuk bit_depth = 16" #: py/objdict.c msgid "dict update sequence has wrong length" -msgstr "" +msgstr "urutan pembaruan dict memiliki panjang yang salah" #: extmod/ulab/code/numpy/numerical.c msgid "diff argument must be an ndarray" -msgstr "" +msgstr "argumen diff harus berupa ndarray" #: extmod/ulab/code/numpy/numerical.c msgid "differentiation order out of range" -msgstr "" +msgstr "urutan diferensiasi di luar jangkauan" #: extmod/ulab/code/numpy/transform.c msgid "dimensions do not match" -msgstr "" +msgstr "dimensi tidak cocok" #: py/emitnative.c msgid "div/mod not implemented for uint" -msgstr "" +msgstr "div/mod tidak diimplementasikan untuk uint" #: extmod/ulab/code/numpy/create.c msgid "divide by zero" -msgstr "" +msgstr "pembagian dengan nol" #: py/runtime.c msgid "division by zero" -msgstr "" +msgstr "pembagian dengan nol" #: extmod/ulab/code/numpy/vector.c msgid "dtype must be float, or complex" -msgstr "" +msgstr "dtype harus float, atau complex" #: extmod/ulab/code/ndarray_operators.c msgid "dtype of int32 is not supported" -msgstr "" +msgstr "dtype int32 tidak didukung" #: py/objdeque.c msgid "empty" -msgstr "" +msgstr "kosong" #: extmod/ulab/code/numpy/io/io.c msgid "empty file" -msgstr "" +msgstr "file kosong" #: extmod/modasyncio.c extmod/modheapq.c msgid "empty heap" @@ -3107,19 +3116,19 @@ msgstr "heap kosong" #: py/objstr.c msgid "empty separator" -msgstr "" +msgstr "pemisah kosong" #: shared-bindings/random/__init__.c msgid "empty sequence" -msgstr "" +msgstr "urutan kosong" #: py/objstr.c msgid "end of format while looking for conversion specifier" -msgstr "" +msgstr "akhir format saat mencari spesifier konversi" #: shared-bindings/alarm/time/TimeAlarm.c msgid "epoch_time not supported on this board" -msgstr "" +msgstr "epoch_time tidak didukung pada board ini" #: ports/nordic/common-hal/busio/UART.c #, c-format @@ -3128,19 +3137,19 @@ msgstr "error = 0x%08lX" #: py/runtime.c msgid "exceptions must derive from BaseException" -msgstr "" +msgstr "exception harus diturunkan dari BaseException" #: py/objstr.c msgid "expected ':' after format specifier" -msgstr "" +msgstr "mengharapkan ':' setelah spesifier format" #: py/obj.c msgid "expected tuple/list" -msgstr "" +msgstr "mengharapkan tuple/list" #: py/modthread.c msgid "expecting a dict for keyword args" -msgstr "" +msgstr "mengharapkan dict untuk argumen keyword" #: py/compile.c msgid "expecting an assembler instruction" @@ -3156,7 +3165,7 @@ msgstr "key:value diharapkan untuk dict" #: shared-bindings/msgpack/__init__.c msgid "ext_hook is not a function" -msgstr "" +msgstr "ext_hook bukan fungsi" #: py/argcheck.c msgid "extra keyword arguments given" @@ -3170,11 +3179,11 @@ msgstr "argumen posisi ekstra telah diberikan" #: shared-bindings/displayio/OnDiskBitmap.c shared-bindings/gifio/OnDiskGif.c #: shared-bindings/synthio/__init__.c shared-module/gifio/GifWriter.c msgid "file must be a file opened in byte mode" -msgstr "" +msgstr "file harus berupa file yang dibuka dalam mode byte" #: shared-bindings/traceback/__init__.c msgid "file write is not available" -msgstr "" +msgstr "penulisan file tidak tersedia" #: shared-bindings/storage/__init__.c msgid "filesystem must provide mount method" @@ -3182,63 +3191,63 @@ msgstr "" #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" -msgstr "" +msgstr "argumen pertama harus dapat dipanggil" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "first argument must be a function" -msgstr "" +msgstr "argumen pertama harus berupa fungsi" #: extmod/ulab/code/numpy/create.c msgid "first argument must be a tuple of ndarrays" -msgstr "" +msgstr "argumen pertama harus berupa tuple ndarray" #: extmod/ulab/code/numpy/transform.c extmod/ulab/code/numpy/vector.c msgid "first argument must be an ndarray" -msgstr "" +msgstr "argumen pertama harus berupa ndarray" #: py/objtype.c msgid "first argument to super() must be type" -msgstr "" +msgstr "argumen pertama untuk super() harus bertipe type" #: extmod/ulab/code/scipy/linalg/linalg.c msgid "first two arguments must be ndarrays" -msgstr "" +msgstr "dua argumen pertama harus berupa ndarray" #: extmod/ulab/code/ndarray.c msgid "flattening order must be either 'C', or 'F'" -msgstr "" +msgstr "urutan perataan harus 'C', atau 'F'" #: extmod/ulab/code/numpy/numerical.c msgid "flip argument must be an ndarray" -msgstr "" +msgstr "argumen flip harus berupa ndarray" #: py/objint.c msgid "float too big" -msgstr "" +msgstr "float terlalu besar" #: py/nativeglue.c msgid "float unsupported" -msgstr "" +msgstr "float tidak didukung" #: shared-bindings/_stage/Text.c msgid "font must be 2048 bytes long" -msgstr "" +msgstr "font harus sepanjang 2048 byte" #: extmod/moddeflate.c msgid "format" -msgstr "" +msgstr "format" #: py/objstr.c msgid "format needs a dict" -msgstr "" +msgstr "format memerlukan dict" #: py/objdeque.c msgid "full" -msgstr "" +msgstr "penuh" #: py/argcheck.c msgid "function doesn't take keyword arguments" -msgstr "" +msgstr "fungsi tidak menerima argumen keyword" #: py/argcheck.c #, c-format @@ -3251,15 +3260,15 @@ msgstr "fungsi mendapatkan nilai ganda untuk argumen '%q'" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "function has the same sign at the ends of interval" -msgstr "" +msgstr "fungsi memiliki tanda yang sama pada ujung interval" #: extmod/ulab/code/ndarray.c msgid "function is defined for ndarrays only" -msgstr "" +msgstr "fungsi didefinisikan hanya untuk ndarray" #: extmod/ulab/code/numpy/carray/carray.c msgid "function is implemented for ndarrays only" -msgstr "" +msgstr "fungsi diimplementasikan hanya untuk ndarray" #: py/argcheck.c #, c-format @@ -3287,23 +3296,23 @@ msgstr "fungsi mengambil posisi argumen %d tapi %d yang diberikan" #: py/objgenerator.c msgid "generator already executing" -msgstr "" +msgstr "generator sudah berjalan" #: py/objgenerator.c msgid "generator ignored GeneratorExit" -msgstr "" +msgstr "generator mengabaikan GeneratorExit" #: py/objgenerator.c py/runtime.c msgid "generator raised StopIteration" -msgstr "" +msgstr "generator memunculkan StopIteration" #: shared-bindings/_stage/Layer.c msgid "graphic must be 2048 bytes long" -msgstr "" +msgstr "grafik harus sepanjang 2048 byte" #: extmod/modhashlib.c msgid "hash is final" -msgstr "" +msgstr "hash bersifat final" #: extmod/modheapq.c msgid "heap must be a list" @@ -3319,23 +3328,23 @@ msgstr "identifier didefinisi ulang sebagai nonlocal" #: py/compile.c msgid "import * not at module level" -msgstr "" +msgstr "import * tidak pada tingkat modul" #: py/persistentcode.c msgid "incompatible .mpy arch" -msgstr "" +msgstr "arsitektur .mpy tidak kompatibel" #: py/persistentcode.c msgid "incompatible .mpy file" -msgstr "" +msgstr "file .mpy tidak kompatibel" #: py/objstr.c msgid "incomplete format" -msgstr "" +msgstr "format tidak lengkap" #: py/objstr.c msgid "incomplete format key" -msgstr "" +msgstr "kunci format tidak lengkap" #: extmod/modbinascii.c msgid "incorrect padding" @@ -3343,11 +3352,11 @@ msgstr "lapisan (padding) tidak benar" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/transform.c msgid "index is out of bounds" -msgstr "" +msgstr "indeks berada di luar batas" #: shared-bindings/_pixelmap/PixelMap.c msgid "index must be tuple or int" -msgstr "" +msgstr "indeks harus berupa tuple atau int" #: extmod/ulab/code/numpy/numerical.c extmod/ulab/code/ulab_tools.c #: ports/espressif/common-hal/pulseio/PulseIn.c @@ -3357,19 +3366,19 @@ msgstr "index keluar dari jangkauan" #: py/obj.c msgid "indices must be integers" -msgstr "" +msgstr "indeks harus berupa integer" #: extmod/ulab/code/ndarray.c msgid "indices must be integers, slices, or Boolean lists" -msgstr "" +msgstr "indeks harus berupa integer, slice, atau list Boolean" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "initial values must be iterable" -msgstr "" +msgstr "nilai awal harus dapat diiterasi" #: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c msgid "initial_value length is wrong" -msgstr "" +msgstr "panjang initial_value salah" #: py/compile.c msgid "inline assembler must be a function" @@ -3377,98 +3386,98 @@ msgstr "inline assembler harus sebuah fungsi" #: extmod/ulab/code/numpy/vector.c msgid "input and output dimensions differ" -msgstr "" +msgstr "dimensi input dan output berbeda" #: extmod/ulab/code/numpy/vector.c msgid "input and output shapes differ" -msgstr "" +msgstr "bentuk input dan output berbeda" #: extmod/ulab/code/numpy/create.c msgid "input argument must be an integer, a tuple, or a list" -msgstr "" +msgstr "argumen input harus berupa integer, tuple, atau list" #: extmod/ulab/code/numpy/fft/fft_tools.c msgid "input array length must be power of 2" -msgstr "" +msgstr "panjang array input harus pangkat 2" #: extmod/ulab/code/numpy/create.c msgid "input arrays are not compatible" -msgstr "" +msgstr "array input tidak kompatibel" #: extmod/ulab/code/numpy/poly.c msgid "input data must be an iterable" -msgstr "" +msgstr "data input harus dapat diiterasi" #: extmod/ulab/code/numpy/vector.c msgid "input dtype must be float or complex" -msgstr "" +msgstr "dtype input harus float atau complex" #: extmod/ulab/code/numpy/poly.c msgid "input is not iterable" -msgstr "" +msgstr "input tidak dapat diiterasi" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "input matrix is asymmetric" -msgstr "" +msgstr "matriks input asimetris" #: extmod/ulab/code/numpy/linalg/linalg.c #: extmod/ulab/code/scipy/linalg/linalg.c msgid "input matrix is singular" -msgstr "" +msgstr "matriks input singular" #: extmod/ulab/code/numpy/create.c msgid "input must be 1- or 2-d" -msgstr "" +msgstr "input harus 1- atau 2-d" #: extmod/ulab/code/numpy/carray/carray.c msgid "input must be a 1D ndarray" -msgstr "" +msgstr "input harus berupa ndarray 1D" #: extmod/ulab/code/scipy/linalg/linalg.c extmod/ulab/code/user/user.c msgid "input must be a dense ndarray" -msgstr "" +msgstr "input harus berupa ndarray padat" #: extmod/ulab/code/user/user.c shared-bindings/_eve/__init__.c msgid "input must be an ndarray" -msgstr "" +msgstr "input harus berupa ndarray" #: extmod/ulab/code/numpy/carray/carray.c msgid "input must be an ndarray, or a scalar" -msgstr "" +msgstr "input harus berupa ndarray, atau skalar" #: extmod/ulab/code/scipy/signal/signal.c msgid "input must be one-dimensional" -msgstr "" +msgstr "input harus satu dimensi" #: extmod/ulab/code/ulab_tools.c msgid "input must be square matrix" -msgstr "" +msgstr "input harus berupa matriks persegi" #: extmod/ulab/code/numpy/numerical.c msgid "input must be tuple, list, range, or ndarray" -msgstr "" +msgstr "input harus berupa tuple, list, range, atau ndarray" #: extmod/ulab/code/numpy/poly.c msgid "input vectors must be of equal length" -msgstr "" +msgstr "vektor input harus memiliki panjang yang sama" #: extmod/ulab/code/numpy/approx.c msgid "interp is defined for 1D iterables of equal length" -msgstr "" +msgstr "interp didefinisikan untuk iterable 1D dengan panjang yang sama" #: shared-bindings/_bleio/Adapter.c #, c-format msgid "interval must be in range %s-%s" -msgstr "" +msgstr "interval harus dalam rentang %s-%s" #: py/compile.c msgid "invalid arch" -msgstr "" +msgstr "arsitektur tidak valid" #: shared-bindings/bitmaptools/__init__.c #, c-format msgid "invalid bits_per_pixel %d, must be, 1, 2, 4, 8, 16, 24, or 32" -msgstr "" +msgstr "bits_per_pixel %d tidak valid, harus 1, 2, 4, 8, 16, 24, atau 32" #: shared-module/ssl/SSLSocket.c msgid "invalid cert" @@ -3477,24 +3486,24 @@ msgstr "cert tidak valid" #: shared-bindings/bitmaptools/__init__.c #, c-format msgid "invalid element size %d for bits_per_pixel %d\n" -msgstr "" +msgstr "element size %d tidak valid untuk bits_per_pixel %d\n" #: shared-bindings/bitmaptools/__init__.c #, c-format msgid "invalid element_size %d, must be, 1, 2, or 4" -msgstr "" +msgstr "element_size %d tidak valid, harus 1, 2, atau 4" #: shared-bindings/traceback/__init__.c msgid "invalid exception" -msgstr "" +msgstr "exception tidak valid" #: py/objstr.c msgid "invalid format specifier" -msgstr "" +msgstr "spesifier format tidak valid" #: shared-bindings/wifi/Radio.c msgid "invalid hostname" -msgstr "" +msgstr "hostname tidak valid" #: shared-module/ssl/SSLSocket.c msgid "invalid key" @@ -3506,11 +3515,11 @@ msgstr "micropython decorator tidak valid" #: ports/espressif/common-hal/espcamera/Camera.c msgid "invalid setting" -msgstr "" +msgstr "pengaturan tidak valid" #: shared-bindings/random/__init__.c msgid "invalid step" -msgstr "" +msgstr "step tidak valid" #: py/compile.c py/parse.c msgid "invalid syntax" @@ -3518,40 +3527,42 @@ msgstr "syntax tidak valid" #: py/parsenum.c msgid "invalid syntax for integer" -msgstr "" +msgstr "sintaks tidak valid untuk integer" #: py/parsenum.c #, c-format msgid "invalid syntax for integer with base %d" -msgstr "" +msgstr "sintaks tidak valid untuk integer dengan basis %d" #: py/parsenum.c msgid "invalid syntax for number" -msgstr "" +msgstr "sintaks tidak valid untuk angka" #: py/objtype.c msgid "issubclass() arg 1 must be a class" -msgstr "" +msgstr "argumen 1 issubclass() harus berupa class" #: py/objtype.c msgid "issubclass() arg 2 must be a class or a tuple of classes" -msgstr "" +msgstr "argumen 2 issubclass() harus berupa class atau tuple class" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "iterations did not converge" -msgstr "" +msgstr "iterasi tidak konvergen" #: py/objstr.c msgid "join expects a list of str/bytes objects consistent with self object" -msgstr "" +msgstr "join mengharapkan list objek str/bytes yang konsisten dengan objek self" #: py/argcheck.c msgid "keyword argument(s) not implemented - use normal args instead" msgstr "" +"argumen keyword tidak diimplementasikan - gunakan argumen normal sebagai " +"gantinya" #: py/emitinlinethumb.c py/emitinlinextensa.c msgid "label '%q' not defined" -msgstr "" +msgstr "label '%q' tidak didefinisikan" #: py/compile.c msgid "label redefined" @@ -3559,101 +3570,101 @@ msgstr "label didefinis ulang" #: py/objarray.c msgid "lhs and rhs should be compatible" -msgstr "" +msgstr "lhs dan rhs harus kompatibel" #: py/emitnative.c msgid "local '%q' has type '%q' but source is '%q'" -msgstr "" +msgstr "lokal '%q' memiliki tipe '%q' tetapi sumber adalah '%q'" #: py/emitnative.c msgid "local '%q' used before type known" -msgstr "" +msgstr "lokal '%q' digunakan sebelum tipe diketahui" #: py/vm.c msgid "local variable referenced before assignment" -msgstr "" +msgstr "variabel lokal direferensikan sebelum penetapan" #: ports/espressif/common-hal/canio/CAN.c msgid "loopback + silent mode not supported by peripheral" -msgstr "" +msgstr "mode loopback + silent tidak didukung oleh periferal" #: ports/espressif/common-hal/mdns/Server.c #: ports/raspberrypi/common-hal/mdns/Server.c msgid "mDNS already initialized" -msgstr "" +msgstr "mDNS sudah diinisialisasi" #: ports/espressif/common-hal/mdns/Server.c #: ports/raspberrypi/common-hal/mdns/Server.c msgid "mDNS only works with built-in WiFi" -msgstr "" +msgstr "mDNS hanya bekerja dengan WiFi bawaan" #: py/parse.c msgid "malformed f-string" -msgstr "" +msgstr "f-string salah bentuk" #: shared-bindings/_stage/Layer.c msgid "map buffer too small" -msgstr "" +msgstr "buffer peta terlalu kecil" #: py/modmath.c shared-bindings/math/__init__.c msgid "math domain error" -msgstr "" +msgstr "kesalahan domain matematika" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "matrix is not positive definite" -msgstr "" +msgstr "matriks tidak definit positif" #: ports/espressif/common-hal/_bleio/Descriptor.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c #, c-format msgid "max_length must be 0-%d when fixed_length is %s" -msgstr "" +msgstr "max_length harus 0-%d ketika fixed_length adalah %s" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/random/random.c msgid "maximum number of dimensions is " -msgstr "" +msgstr "jumlah maksimum dimensi adalah " #: py/runtime.c msgid "maximum recursion depth exceeded" -msgstr "" +msgstr "kedalaman rekursi maksimum terlampaui" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "maxiter must be > 0" -msgstr "" +msgstr "maxiter harus > 0" #: extmod/ulab/code/scipy/optimize/optimize.c msgid "maxiter should be > 0" -msgstr "" +msgstr "maxiter harus > 0" #: extmod/ulab/code/numpy/numerical.c msgid "median argument must be an ndarray" -msgstr "" +msgstr "argumen median harus berupa ndarray" #: py/runtime.c #, c-format msgid "memory allocation failed, allocating %u bytes" -msgstr "" +msgstr "alokasi memori gagal, mengalokasikan %u byte" #: py/runtime.c msgid "memory allocation failed, heap is locked" -msgstr "" +msgstr "alokasi memori gagal, heap terkunci" #: py/objarray.c msgid "memoryview offset too large" -msgstr "" +msgstr "offset memoryview terlalu besar" #: py/objarray.c msgid "memoryview: length is not a multiple of itemsize" -msgstr "" +msgstr "memoryview: panjang bukan kelipatan dari itemsize" #: extmod/modtime.c msgid "mktime needs a tuple of length 8 or 9" -msgstr "" +msgstr "mktime memerlukan tuple dengan panjang 8 atau 9" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "mode must be complete, or reduced" -msgstr "" +msgstr "mode harus complete, atau reduced" #: py/builtinimport.c msgid "module not found" @@ -3661,11 +3672,11 @@ msgstr "modul tidak ditemukan" #: ports/espressif/common-hal/wifi/Monitor.c msgid "monitor init failed" -msgstr "" +msgstr "inisialisasi monitor gagal" #: extmod/ulab/code/numpy/poly.c msgid "more degrees of freedom than data points" -msgstr "" +msgstr "lebih banyak derajat kebebasan daripada titik data" #: py/compile.c msgid "multiple *x in assignment" @@ -3673,76 +3684,76 @@ msgstr "perkalian *x dalam assignment" #: py/objtype.c msgid "multiple bases have instance lay-out conflict" -msgstr "" +msgstr "beberapa basis memiliki konflik tata letak instance" #: py/objtype.c msgid "multiple inheritance not supported" -msgstr "" +msgstr "pewarisan berganda tidak didukung" #: py/emitnative.c msgid "must raise an object" -msgstr "" +msgstr "harus memunculkan objek" #: py/modbuiltins.c msgid "must use keyword argument for key function" -msgstr "" +msgstr "harus menggunakan argumen keyword untuk fungsi kunci" #: py/runtime.c msgid "name '%q' isn't defined" -msgstr "" +msgstr "nama '%q' tidak didefinisikan" #: py/runtime.c msgid "name not defined" -msgstr "" +msgstr "nama tidak didefinisikan" #: py/qstr.c msgid "name too long" -msgstr "" +msgstr "nama terlalu panjang" #: py/persistentcode.c msgid "native code in .mpy unsupported" -msgstr "" +msgstr "kode native dalam .mpy tidak didukung" #: py/asmthumb.c msgid "native method too big" -msgstr "" +msgstr "metode native terlalu besar" #: py/emitnative.c msgid "native yield" -msgstr "" +msgstr "yield native" #: extmod/ulab/code/ndarray.c msgid "ndarray length overflows" -msgstr "" +msgstr "panjang ndarray meluap" #: py/runtime.c #, c-format msgid "need more than %d values to unpack" -msgstr "" +msgstr "memerlukan lebih dari %d nilai untuk membongkar" #: py/modmath.c msgid "negative factorial" -msgstr "" +msgstr "faktorial negatif" #: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative power with no float support" -msgstr "" +msgstr "pangkat negatif tanpa dukungan float" #: py/objint_mpz.c py/runtime.c msgid "negative shift count" -msgstr "" +msgstr "jumlah shift negatif" #: shared-bindings/_pixelmap/PixelMap.c msgid "nested index must be int" -msgstr "" +msgstr "indeks bersarang harus int" #: shared-module/sdcardio/SDCard.c msgid "no SD card" -msgstr "" +msgstr "tidak ada kartu SD" #: py/vm.c msgid "no active exception to reraise" -msgstr "" +msgstr "tidak ada exception aktif untuk dimunculkan kembali" #: py/compile.c msgid "no binding for nonlocal found" @@ -3750,11 +3761,11 @@ msgstr "tidak ada ikatan/bind pada temuan nonlocal" #: shared-module/msgpack/__init__.c msgid "no default packer" -msgstr "" +msgstr "tidak ada packer default" #: extmod/modrandom.c extmod/ulab/code/numpy/random/random.c msgid "no default seed" -msgstr "" +msgstr "tidak ada seed default" #: py/builtinimport.c msgid "no module named '%q'" @@ -3762,16 +3773,16 @@ msgstr "tidak ada modul yang bernama '%q'" #: shared-module/sdcardio/SDCard.c msgid "no response from SD card" -msgstr "" +msgstr "tidak ada respons dari kartu SD" #: ports/espressif/common-hal/espcamera/Camera.c py/objobject.c py/runtime.c msgid "no such attribute" -msgstr "" +msgstr "atribut tidak ada" #: ports/espressif/common-hal/_bleio/Connection.c #: ports/nordic/common-hal/_bleio/Connection.c msgid "non-UUID found in service_uuids_whitelist" -msgstr "" +msgstr "non-UUID ditemukan dalam service_uuids_whitelist" #: py/compile.c msgid "non-default argument follows default argument" @@ -3783,19 +3794,19 @@ msgstr "digit non-hex ditemukan" #: ports/nordic/common-hal/_bleio/Adapter.c msgid "non-zero timeout must be > 0.01" -msgstr "" +msgstr "timeout non-nol harus > 0.01" #: shared-bindings/_bleio/Adapter.c msgid "non-zero timeout must be >= interval" -msgstr "" +msgstr "timeout non-nol harus >= interval" #: shared-bindings/_bleio/UUID.c msgid "not a 128-bit UUID" -msgstr "" +msgstr "bukan UUID 128-bit" #: py/parse.c msgid "not a constant" -msgstr "" +msgstr "bukan konstanta" #: py/objstr.c msgid "not all arguments converted during string formatting" @@ -3807,65 +3818,65 @@ msgstr "" #: extmod/ulab/code/numpy/carray/carray_tools.c msgid "not implemented for complex dtype" -msgstr "" +msgstr "tidak diimplementasikan untuk dtype complex" #: extmod/ulab/code/numpy/bitwise.c msgid "not supported for input types" -msgstr "" +msgstr "tidak didukung untuk tipe input" #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" -msgstr "" +msgstr "jumlah titik harus setidaknya 2" #: py/builtinhelp.c msgid "object " -msgstr "" +msgstr "objek " #: py/obj.c #, c-format msgid "object '%s' isn't a tuple or list" -msgstr "" +msgstr "objek '%s' bukan tuple atau list" #: py/obj.c msgid "object doesn't support item assignment" -msgstr "" +msgstr "objek tidak mendukung penetapan item" #: py/obj.c msgid "object doesn't support item deletion" -msgstr "" +msgstr "objek tidak mendukung penghapusan item" #: py/obj.c msgid "object has no len" -msgstr "" +msgstr "objek tidak memiliki len" #: py/obj.c msgid "object isn't subscriptable" -msgstr "" +msgstr "objek tidak dapat disubskripsikan" #: py/runtime.c msgid "object not an iterator" -msgstr "" +msgstr "objek bukan iterator" #: py/objtype.c py/runtime.c msgid "object not callable" -msgstr "" +msgstr "objek tidak dapat dipanggil" #: py/sequence.c shared-bindings/displayio/Group.c msgid "object not in sequence" -msgstr "" +msgstr "objek tidak dalam urutan" #: py/runtime.c msgid "object not iterable" -msgstr "" +msgstr "objek tidak dapat diiterasi" #: py/obj.c #, c-format msgid "object of type '%s' has no len()" -msgstr "" +msgstr "objek bertipe '%s' tidak memiliki len()" #: py/obj.c msgid "object with buffer protocol required" -msgstr "" +msgstr "objek dengan protokol buffer diperlukan" #: py/objstr.c msgid "odd-length string" @@ -3873,146 +3884,146 @@ msgstr "panjang data string memiliki keganjilan (odd-length)" #: supervisor/shared/web_workflow/web_workflow.c msgid "off" -msgstr "" +msgstr "mati" #: extmod/ulab/code/utils/utils.c msgid "offset is too large" -msgstr "" +msgstr "offset terlalu besar" #: shared-bindings/dualbank/__init__.c msgid "offset must be >= 0" -msgstr "" +msgstr "offset harus >= 0" #: extmod/ulab/code/numpy/create.c msgid "offset must be non-negative and no greater than buffer length" -msgstr "" +msgstr "offset harus non-negatif dan tidak lebih besar dari panjang buffer" #: ports/nordic/common-hal/audiobusio/PDMIn.c #: ports/stm/common-hal/audiobusio/PDMIn.c msgid "only bit_depth=16 is supported" -msgstr "" +msgstr "hanya bit_depth=16 yang didukung" #: ports/stm/common-hal/audiobusio/PDMIn.c msgid "only mono is supported" -msgstr "" +msgstr "hanya mono yang didukung" #: extmod/ulab/code/numpy/create.c msgid "only ndarrays can be concatenated" -msgstr "" +msgstr "hanya ndarray yang dapat digabungkan" #: ports/stm/common-hal/audiobusio/PDMIn.c msgid "only oversample=64 is supported" -msgstr "" +msgstr "hanya oversample=64 yang didukung" #: ports/nordic/common-hal/audiobusio/PDMIn.c #: ports/stm/common-hal/audiobusio/PDMIn.c msgid "only sample_rate=16000 is supported" -msgstr "" +msgstr "hanya sample_rate=16000 yang didukung" #: py/objarray.c py/objstr.c py/objstrunicode.c py/objtuple.c #: shared-bindings/alarm/SleepMemory.c shared-bindings/memorymap/AddressRange.c #: shared-bindings/nvm/ByteArray.c msgid "only slices with step=1 (aka None) are supported" -msgstr "" +msgstr "hanya slice dengan step=1 (alias None) yang didukung" #: py/vm.c msgid "opcode" -msgstr "" +msgstr "opcode" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/bitwise.c #: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/vector.c msgid "operands could not be broadcast together" -msgstr "" +msgstr "operan tidak dapat di-broadcast bersama" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "operation is defined for 2D arrays only" -msgstr "" +msgstr "operasi didefinisikan hanya untuk array 2D" #: extmod/ulab/code/numpy/linalg/linalg.c msgid "operation is defined for ndarrays only" -msgstr "" +msgstr "operasi didefinisikan hanya untuk ndarray" #: extmod/ulab/code/ndarray.c msgid "operation is implemented for 1D Boolean arrays only" -msgstr "" +msgstr "operasi diimplementasikan hanya untuk array Boolean 1D" #: extmod/ulab/code/numpy/numerical.c msgid "operation is not implemented on ndarrays" -msgstr "" +msgstr "operasi tidak diimplementasikan pada ndarray" #: extmod/ulab/code/ndarray.c msgid "operation is not supported for given type" -msgstr "" +msgstr "operasi tidak didukung untuk tipe yang diberikan" #: extmod/ulab/code/ndarray_operators.c msgid "operation not supported for the input types" -msgstr "" +msgstr "operasi tidak didukung untuk tipe input" #: py/modbuiltins.c msgid "ord expects a character" -msgstr "" +msgstr "ord mengharapkan karakter" #: py/modbuiltins.c #, c-format msgid "ord() expected a character, but string of length %d found" -msgstr "" +msgstr "ord() mengharapkan karakter, tetapi string dengan panjang %d ditemukan" #: extmod/ulab/code/utils/utils.c msgid "out array is too small" -msgstr "" +msgstr "array out terlalu kecil" #: extmod/ulab/code/numpy/random/random.c msgid "out has wrong type" -msgstr "" +msgstr "out memiliki tipe yang salah" #: extmod/ulab/code/numpy/vector.c msgid "out keyword is not supported for complex dtype" -msgstr "" +msgstr "keyword out tidak didukung untuk dtype complex" #: extmod/ulab/code/numpy/vector.c msgid "out keyword is not supported for function" -msgstr "" +msgstr "keyword out tidak didukung untuk fungsi" #: extmod/ulab/code/utils/utils.c msgid "out must be a float dense array" -msgstr "" +msgstr "out harus berupa array padat float" #: extmod/ulab/code/numpy/vector.c msgid "out must be an ndarray" -msgstr "" +msgstr "out harus berupa ndarray" #: extmod/ulab/code/numpy/vector.c msgid "out must be of float dtype" -msgstr "" +msgstr "out harus bertipe dtype float" #: shared-bindings/bitmaptools/__init__.c msgid "out of range of target" -msgstr "" +msgstr "di luar jangkauan target" #: extmod/ulab/code/numpy/random/random.c msgid "output array has wrong type" -msgstr "" +msgstr "array output memiliki tipe yang salah" #: extmod/ulab/code/numpy/random/random.c msgid "output array must be contiguous" -msgstr "" +msgstr "array output harus berdekatan" #: py/objint_mpz.c msgid "overflow converting long int to machine word" -msgstr "" +msgstr "overflow saat mengonversi long int ke machine word" #: py/modstruct.c #, c-format msgid "pack expected %d items for packing (got %d)" -msgstr "" +msgstr "pack mengharapkan %d item untuk packing (mendapat %d)" #: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c msgid "palette must be 32 bytes long" -msgstr "" +msgstr "palet harus sepanjang 32 byte" #: py/emitinlinextensa.c msgid "parameters must be registers in sequence a2 to a5" -msgstr "" +msgstr "parameter harus berupa register dalam urutan a2 hingga a5" #: py/emitinlinethumb.c msgid "parameters must be registers in sequence r0 to r3" @@ -4020,7 +4031,7 @@ msgstr "parameter harus menjadi register dalam urutan r0 sampai r3" #: extmod/vfs_posix_file.c msgid "poll on file not available on win32" -msgstr "" +msgstr "poll pada file tidak tersedia di win32" #: ports/espressif/common-hal/pulseio/PulseIn.c msgid "pop from an empty PulseIn" @@ -4033,31 +4044,31 @@ msgstr "Muncul dari PulseIn yang kosong" #: ports/stm/common-hal/pulseio/PulseIn.c py/objdict.c py/objlist.c py/objset.c #: shared-bindings/ps2io/Ps2.c msgid "pop from empty %q" -msgstr "" +msgstr "pop dari %q kosong" #: shared-bindings/socketpool/Socket.c msgid "port must be >= 0" -msgstr "" +msgstr "port harus >= 0" #: py/compile.c msgid "positional arg after **" -msgstr "" +msgstr "argumen posisional setelah **" #: py/compile.c msgid "positional arg after keyword arg" -msgstr "" +msgstr "argumen posisional setelah argumen keyword" #: py/objint_mpz.c msgid "pow() 3rd argument cannot be 0" -msgstr "" +msgstr "argumen ketiga pow() tidak boleh 0" #: py/objint_mpz.c msgid "pow() with 3 arguments requires integers" -msgstr "" +msgstr "pow() dengan 3 argumen memerlukan integer" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "pull masks conflict with direction masks" -msgstr "" +msgstr "mask pull berkonflik dengan mask arah" #: py/parse.c msgid "raw f-strings are not supported" @@ -4065,7 +4076,7 @@ msgstr "" #: extmod/ulab/code/numpy/fft/fft_tools.c msgid "real and imaginary parts must be of equal length" -msgstr "" +msgstr "bagian real dan imajiner harus memiliki panjang yang sama" #: py/builtinimport.c msgid "relative import" @@ -4074,11 +4085,11 @@ msgstr "impor relatif" #: py/obj.c #, c-format msgid "requested length %d but object has length %d" -msgstr "" +msgstr "panjang yang diminta %d tetapi objek memiliki panjang %d" #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" -msgstr "" +msgstr "hasil tidak dapat di-cast ke tipe yang ditentukan" #: py/compile.c msgid "return annotation must be an identifier" @@ -4086,29 +4097,29 @@ msgstr "anotasi return harus sebuah identifier" #: py/emitnative.c msgid "return expected '%q' but got '%q'" -msgstr "" +msgstr "return mengharapkan '%q' tetapi mendapat '%q'" #: shared-bindings/rgbmatrix/RGBMatrix.c #, c-format msgid "rgb_pins[%d] duplicates another pin assignment" -msgstr "" +msgstr "rgb_pins[%d] menduplikasi penetapan pin lain" #: shared-bindings/rgbmatrix/RGBMatrix.c #, c-format msgid "rgb_pins[%d] is not on the same port as clock" -msgstr "" +msgstr "rgb_pins[%d] tidak pada port yang sama dengan clock" #: extmod/ulab/code/numpy/numerical.c msgid "roll argument must be an ndarray" -msgstr "" +msgstr "argumen roll harus berupa ndarray" #: py/objstr.c msgid "rsplit(None,n)" -msgstr "" +msgstr "rsplit(None,n)" #: shared-bindings/audiofreeverb/Freeverb.c msgid "samples_signed must be true" -msgstr "" +msgstr "samples_signed harus true" #: ports/atmel-samd/common-hal/audiobusio/PDMIn.c #: ports/raspberrypi/common-hal/audiobusio/PDMIn.c @@ -4117,7 +4128,7 @@ msgstr "nilai sampling keluar dari jangkauan" #: py/modmicropython.c msgid "schedule queue full" -msgstr "" +msgstr "antrian jadwal penuh" #: py/builtinimport.c msgid "script compilation not supported" @@ -4125,43 +4136,43 @@ msgstr "kompilasi script tidak didukung" #: py/nativeglue.c msgid "set unsupported" -msgstr "" +msgstr "set tidak didukung" #: extmod/ulab/code/numpy/random/random.c msgid "shape must be None, and integer or a tuple of integers" -msgstr "" +msgstr "shape harus None, dan integer atau tuple integer" #: extmod/ulab/code/ndarray.c msgid "shape must be integer or tuple of integers" -msgstr "" +msgstr "shape harus integer atau tuple integer" #: shared-module/msgpack/__init__.c msgid "short read" -msgstr "" +msgstr "pembacaan pendek" #: py/objstr.c msgid "sign not allowed in string format specifier" -msgstr "" +msgstr "tanda tidak diperbolehkan dalam spesifier format string" #: py/objstr.c msgid "sign not allowed with integer format specifier 'c'" -msgstr "" +msgstr "tanda tidak diperbolehkan dengan spesifier format integer 'c'" #: extmod/ulab/code/ulab_tools.c msgid "size is defined for ndarrays only" -msgstr "" +msgstr "size didefinisikan hanya untuk ndarray" #: extmod/ulab/code/numpy/random/random.c msgid "size must match out.shape when used together" -msgstr "" +msgstr "size harus cocok dengan out.shape ketika digunakan bersama" #: py/nativeglue.c msgid "slice unsupported" -msgstr "" +msgstr "slice tidak didukung" #: py/objint.c py/sequence.c msgid "small int overflow" -msgstr "" +msgstr "overflow int kecil" #: main.c msgid "soft reboot\n" @@ -4169,39 +4180,39 @@ msgstr "memulai ulang software(soft reboot)\n" #: extmod/ulab/code/numpy/numerical.c msgid "sort argument must be an ndarray" -msgstr "" +msgstr "argumen sort harus berupa ndarray" #: extmod/ulab/code/scipy/signal/signal.c msgid "sos array must be of shape (n_section, 6)" -msgstr "" +msgstr "array sos harus berbentuk (n_section, 6)" #: extmod/ulab/code/scipy/signal/signal.c msgid "sos[:, 3] should be all ones" -msgstr "" +msgstr "sos[:, 3] harus semuanya satu" #: extmod/ulab/code/scipy/signal/signal.c msgid "sosfilt requires iterable arguments" -msgstr "" +msgstr "sosfilt memerlukan argumen yang dapat diiterasi" #: shared-bindings/bitmaptools/__init__.c msgid "source palette too large" -msgstr "" +msgstr "palet sumber terlalu besar" #: shared-bindings/bitmaptools/__init__.c msgid "source_bitmap must have value_count of 2 or 65536" -msgstr "" +msgstr "source_bitmap harus memiliki value_count 2 atau 65536" #: shared-bindings/bitmaptools/__init__.c msgid "source_bitmap must have value_count of 65536" -msgstr "" +msgstr "source_bitmap harus memiliki value_count 65536" #: shared-bindings/bitmaptools/__init__.c msgid "source_bitmap must have value_count of 8" -msgstr "" +msgstr "source_bitmap harus memiliki value_count 8" #: extmod/modre.c msgid "splitting with sub-captures" -msgstr "" +msgstr "pemisahan dengan sub-capture" #: py/objstr.c msgid "start/end indices" @@ -4209,28 +4220,28 @@ msgstr "" #: shared-bindings/random/__init__.c msgid "stop not reachable from start" -msgstr "" +msgstr "stop tidak dapat dijangkau dari start" #: py/stream.c shared-bindings/getpass/__init__.c msgid "stream operation not supported" -msgstr "" +msgstr "operasi stream tidak didukung" #: py/objarray.c py/objstr.c msgid "string argument without an encoding" -msgstr "" +msgstr "argumen string tanpa encoding" #: py/objstrunicode.c msgid "string index out of range" -msgstr "" +msgstr "indeks string di luar jangkauan" #: py/objstrunicode.c #, c-format msgid "string indices must be integers, not %s" -msgstr "" +msgstr "indeks string harus integer, bukan %s" #: py/objarray.c py/objstr.c msgid "substring not found" -msgstr "" +msgstr "substring tidak ditemukan" #: py/compile.c msgid "super() can't find self" @@ -4242,87 +4253,87 @@ msgstr "sintaksis error pada JSON" #: extmod/modtime.c msgid "ticks interval overflow" -msgstr "" +msgstr "overflow interval tick" #: ports/nordic/common-hal/watchdog/WatchDogTimer.c msgid "timeout duration exceeded the maximum supported value" -msgstr "" +msgstr "durasi timeout melebihi nilai maksimum yang didukung" #: ports/nordic/common-hal/_bleio/Adapter.c msgid "timeout must be < 655.35 secs" -msgstr "" +msgstr "timeout harus < 655.35 detik" #: ports/raspberrypi/common-hal/floppyio/__init__.c msgid "timeout waiting for flux" -msgstr "" +msgstr "timeout menunggu flux" #: ports/raspberrypi/common-hal/floppyio/__init__.c #: shared-module/floppyio/__init__.c msgid "timeout waiting for index pulse" -msgstr "" +msgstr "timeout menunggu pulsa indeks" #: shared-module/sdcardio/SDCard.c msgid "timeout waiting for v1 card" -msgstr "" +msgstr "timeout menunggu kartu v1" #: shared-module/sdcardio/SDCard.c msgid "timeout waiting for v2 card" -msgstr "" +msgstr "timeout menunggu kartu v2" #: ports/stm/common-hal/pwmio/PWMOut.c msgid "timer re-init" -msgstr "" +msgstr "inisialisasi ulang timer" #: shared-bindings/time/__init__.c msgid "timestamp out of range for platform time_t" -msgstr "" +msgstr "timestamp di luar jangkauan untuk time_t platform" #: extmod/ulab/code/ndarray.c msgid "tobytes can be invoked for dense arrays only" -msgstr "" +msgstr "tobytes hanya dapat dipanggil untuk array padat" #: py/compile.c msgid "too many args" -msgstr "" +msgstr "terlalu banyak argumen" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/create.c msgid "too many dimensions" -msgstr "" +msgstr "terlalu banyak dimensi" #: extmod/ulab/code/ndarray.c msgid "too many indices" -msgstr "" +msgstr "terlalu banyak indeks" #: py/asmthumb.c msgid "too many locals for native method" -msgstr "" +msgstr "terlalu banyak lokal untuk metode native" #: py/runtime.c #, c-format msgid "too many values to unpack (expected %d)" -msgstr "" +msgstr "terlalu banyak nilai untuk dibongkar (diharapkan %d)" #: extmod/ulab/code/numpy/approx.c msgid "trapz is defined for 1D arrays of equal length" -msgstr "" +msgstr "trapz didefinisikan untuk array 1D dengan panjang yang sama" #: extmod/ulab/code/numpy/approx.c msgid "trapz is defined for 1D iterables" -msgstr "" +msgstr "trapz didefinisikan untuk iterable 1D" #: py/obj.c msgid "tuple/list has wrong length" -msgstr "" +msgstr "tuple/list memiliki panjang yang salah" #: ports/espressif/common-hal/canio/CAN.c #, c-format msgid "twai_driver_install returned esp-idf error #%d" -msgstr "" +msgstr "twai_driver_install mengembalikan kesalahan esp-idf #%d" #: ports/espressif/common-hal/canio/CAN.c #, c-format msgid "twai_start returned esp-idf error #%d" -msgstr "" +msgstr "twai_start mengembalikan kesalahan esp-idf #%d" #: shared-bindings/busio/UART.c shared-bindings/canio/CAN.c msgid "tx and rx cannot both be None" @@ -4330,27 +4341,27 @@ msgstr "tx dan rx keduanya tidak boleh kosong" #: py/objtype.c msgid "type '%q' isn't an acceptable base type" -msgstr "" +msgstr "tipe '%q' bukan tipe basis yang dapat diterima" #: py/objtype.c msgid "type isn't an acceptable base type" -msgstr "" +msgstr "tipe bukan tipe basis yang dapat diterima" #: py/runtime.c msgid "type object '%q' has no attribute '%q'" -msgstr "" +msgstr "objek tipe '%q' tidak memiliki atribut '%q'" #: py/objtype.c msgid "type takes 1 or 3 arguments" -msgstr "" +msgstr "type memerlukan 1 atau 3 argumen" #: py/objint_longlong.c msgid "ulonglong too large" -msgstr "" +msgstr "ulonglong terlalu besar" #: py/parse.c msgid "unexpected indent" -msgstr "" +msgstr "indentasi tak terduga" #: py/bc.c msgid "unexpected keyword argument" @@ -4363,20 +4374,20 @@ msgstr "keyword argumen '%q' tidak diharapkan" #: py/lexer.c msgid "unicode name escapes" -msgstr "" +msgstr "escape nama unicode" #: py/parse.c msgid "unindent doesn't match any outer indent level" -msgstr "" +msgstr "unindent tidak cocok dengan tingkat indentasi luar mana pun" #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" -msgstr "" +msgstr "spesifier konversi tidak dikenal %c" #: py/objstr.c msgid "unknown format code '%c' for object of type '%q'" -msgstr "" +msgstr "kode format tidak dikenal '%c' untuk objek bertipe '%q'" #: py/compile.c msgid "unknown type" @@ -4384,100 +4395,102 @@ msgstr "tipe tidak diketahui" #: py/compile.c msgid "unknown type '%q'" -msgstr "" +msgstr "tipe tidak dikenal '%q'" #: py/objstr.c #, c-format msgid "unmatched '%c' in format" -msgstr "" +msgstr "'%c' tidak cocok dalam format" #: py/objtype.c py/runtime.c msgid "unreadable attribute" -msgstr "" +msgstr "atribut tidak dapat dibaca" #: shared-bindings/displayio/TileGrid.c shared-bindings/terminalio/Terminal.c #: shared-bindings/tilepalettemapper/TilePaletteMapper.c #: shared-bindings/vectorio/VectorShape.c msgid "unsupported %q type" -msgstr "" +msgstr "tipe %q tidak didukung" #: py/emitinlinethumb.c #, c-format msgid "unsupported Thumb instruction '%s' with %d arguments" -msgstr "" +msgstr "instruksi Thumb tidak didukung '%s' dengan %d argumen" #: py/emitinlinextensa.c #, c-format msgid "unsupported Xtensa instruction '%s' with %d arguments" -msgstr "" +msgstr "instruksi Xtensa tidak didukung '%s' dengan %d argumen" #: shared-module/bitmapfilter/__init__.c msgid "unsupported bitmap depth" -msgstr "" +msgstr "kedalaman bitmap tidak didukung" #: shared-module/gifio/GifWriter.c msgid "unsupported colorspace for GifWriter" -msgstr "" +msgstr "colorspace tidak didukung untuk GifWriter" #: shared-bindings/bitmaptools/__init__.c msgid "unsupported colorspace for dither" -msgstr "" +msgstr "colorspace tidak didukung untuk dither" #: py/objstr.c #, c-format msgid "unsupported format character '%c' (0x%x) at index %d" -msgstr "" +msgstr "karakter format tidak didukung '%c' (0x%x) pada indeks %d" #: py/runtime.c msgid "unsupported type for %q: '%s'" -msgstr "" +msgstr "tipe tidak didukung untuk %q: '%s'" #: py/runtime.c msgid "unsupported type for operator" -msgstr "" +msgstr "tipe tidak didukung untuk operator" #: py/runtime.c msgid "unsupported types for %q: '%q', '%q'" -msgstr "" +msgstr "tipe tidak didukung untuk %q: '%q', '%q'" #: extmod/ulab/code/numpy/io/io.c msgid "usecols is too high" -msgstr "" +msgstr "usecols terlalu tinggi" #: extmod/ulab/code/numpy/io/io.c msgid "usecols keyword must be specified" -msgstr "" +msgstr "keyword usecols harus ditentukan" #: py/objint.c #, c-format msgid "value must fit in %d byte(s)" -msgstr "" +msgstr "nilai harus muat dalam %d byte" #: shared-bindings/bitmaptools/__init__.c msgid "value out of range of target" -msgstr "" +msgstr "nilai di luar jangkauan target" #: extmod/moddeflate.c msgid "wbits" -msgstr "" +msgstr "wbit" #: shared-bindings/bitmapfilter/__init__.c msgid "" "weights must be a sequence with an odd square number of elements (usually 9 " "or 25)" msgstr "" +"weights harus berupa urutan dengan jumlah persegi ganjil elemen (biasanya 9 " +"atau 25)" #: shared-bindings/bitmapfilter/__init__.c msgid "weights must be an object of type %q, %q, %q, or %q, not %q " -msgstr "" +msgstr "weights harus berupa objek bertipe %q, %q, %q, atau %q, bukan %q " #: shared-bindings/is31fl3741/FrameBuffer.c msgid "width must be greater than zero" -msgstr "" +msgstr "lebar harus lebih besar dari nol" #: ports/raspberrypi/common-hal/wifi/Monitor.c msgid "wifi.Monitor not available" -msgstr "" +msgstr "wifi.Monitor tidak tersedia" #: shared-bindings/_bleio/Adapter.c msgid "window must be <= interval" @@ -4493,11 +4506,11 @@ msgstr "sumbu yang ditentukan salah" #: extmod/ulab/code/numpy/io/io.c msgid "wrong dtype" -msgstr "" +msgstr "dtype salah" #: extmod/ulab/code/numpy/transform.c msgid "wrong index type" -msgstr "" +msgstr "tipe indeks salah" #: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/create.c #: extmod/ulab/code/numpy/io/io.c extmod/ulab/code/numpy/transform.c @@ -4507,11 +4520,11 @@ msgstr "tipe input salah" #: extmod/ulab/code/numpy/transform.c msgid "wrong length of condition array" -msgstr "" +msgstr "panjang array kondisi salah" #: extmod/ulab/code/numpy/transform.c msgid "wrong length of index array" -msgstr "" +msgstr "panjang array indeks salah" #: extmod/ulab/code/numpy/create.c py/objarray.c py/objstr.c msgid "wrong number of arguments" diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 262b5de769c89..833991ac476e0 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -78,6 +78,11 @@ msgid "" "%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" msgstr "" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "" @@ -99,6 +104,7 @@ msgid "%q contains duplicate pins" msgstr "" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "" @@ -111,6 +117,8 @@ msgid "%q in %q must be of type %q, not %q" msgstr "" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -118,7 +126,8 @@ msgstr "" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "" @@ -130,7 +139,9 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" -#: shared-module/bitbangio/SPI.c +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "" @@ -162,8 +173,8 @@ msgstr "" msgid "%q must be %d" msgstr "" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -233,23 +244,29 @@ msgstr "" msgid "%q must be power of 2" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "" @@ -300,7 +317,7 @@ msgstr "" msgid "'%q' argument required" msgstr "" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "" @@ -445,6 +462,7 @@ msgstr "" msgid ", in %q\n" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -463,7 +481,7 @@ msgstr "" msgid "AP could not be started" msgstr "" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "" @@ -498,7 +516,8 @@ msgstr "" msgid "All SPI peripherals are in use" msgstr "" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "" @@ -569,7 +588,7 @@ msgstr "" msgid "Already scanning for wifi networks" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "" @@ -607,6 +626,7 @@ msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -632,6 +652,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -649,20 +670,17 @@ msgstr "" msgid "Boot device must be first (interface #0)." msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" msgstr "" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "" @@ -705,10 +723,6 @@ msgstr "" msgid "Bus pin %d is already in use" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "" - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "" @@ -760,6 +774,10 @@ msgstr "" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -794,6 +812,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "" @@ -844,6 +863,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -920,6 +943,7 @@ msgstr "" #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "" @@ -928,6 +952,7 @@ msgid "Done" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "" @@ -939,8 +964,17 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "" @@ -987,7 +1021,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1005,10 +1039,12 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "" @@ -1045,6 +1081,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1062,7 +1102,6 @@ msgid "File exists" msgstr "" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "" @@ -1115,6 +1154,7 @@ msgstr "" msgid "Generic Failure" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -1213,7 +1253,8 @@ msgstr "" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1247,22 +1288,29 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" msgstr "" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1296,6 +1344,10 @@ msgstr "" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "" @@ -1304,17 +1356,12 @@ msgstr "" msgid "Invalid bits per value" msgstr "" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "" @@ -1339,12 +1386,13 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "" @@ -1352,10 +1400,6 @@ msgstr "" msgid "Key must be 16, 24, or 32 bytes long" msgstr "" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "" @@ -1394,6 +1438,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1435,8 +1483,7 @@ msgstr "" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "" @@ -1483,7 +1530,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "" @@ -1501,6 +1548,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "" @@ -1615,14 +1663,10 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -1716,6 +1760,7 @@ msgid "Operation or feature not supported" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "" @@ -1855,6 +1900,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -1894,6 +1940,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -1986,6 +2033,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2031,10 +2082,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" @@ -2117,11 +2171,16 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2160,6 +2219,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2168,6 +2231,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -2192,10 +2263,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "" @@ -2212,6 +2279,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" @@ -2220,6 +2288,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -2325,6 +2394,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" @@ -2347,6 +2420,13 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c @@ -2486,6 +2566,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -2552,10 +2636,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2769,6 +2849,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2867,14 +2951,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3029,7 +3113,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3128,10 +3212,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" @@ -3172,10 +3252,6 @@ msgstr "" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3257,10 +3333,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -3327,10 +3399,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3421,6 +3489,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3615,6 +3687,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3663,10 +3739,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3688,7 +3760,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -3765,6 +3837,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3778,6 +3854,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" @@ -3978,10 +4058,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" @@ -4039,6 +4115,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4048,6 +4128,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4312,10 +4396,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4337,10 +4417,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" diff --git a/locale/cs.po b/locale/cs.po index 58785e0a5d75a..14eb245b9b813 100644 --- a/locale/cs.po +++ b/locale/cs.po @@ -89,6 +89,11 @@ msgid "" "%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" msgstr "%d adresní pin, %d rgb pin a %d dlaždice indikuje výšku %d, ne %d" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q a %q obsahují duplicitní piny" @@ -110,6 +115,7 @@ msgid "%q contains duplicate pins" msgstr "%q obsahuje duplicitní piny" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q: selhání %d" @@ -122,6 +128,8 @@ msgid "%q in %q must be of type %q, not %q" msgstr "%q v %q musí být typu %q, ne %q" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -129,7 +137,8 @@ msgstr "%q v %q musí být typu %q, ne %q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q se právě používá" @@ -141,7 +150,9 @@ msgstr "Index %q je mimo rozsah" msgid "%q indices must be integers, not %s" msgstr "Indexy %q musí být celá čísla, nikoli %s" -#: shared-module/bitbangio/SPI.c +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "Inicializace %q selhala" @@ -173,8 +184,8 @@ msgstr "Délka %q musí být >= %d" msgid "%q must be %d" msgstr "%q musí být %d" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -244,23 +255,29 @@ msgstr "%q musí být typu %q, ne %q" msgid "%q must be power of 2" msgstr "%q musí být mocnina 2" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "%q je mimo hranice" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "%q je mimo rozsah" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "%q krok nemůže být nula" @@ -311,7 +328,7 @@ msgstr "%s chyba 0x%x" msgid "'%q' argument required" msgstr "Je vyžadován argument '%q'" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "Objekt '%q' nepodporuje '%q'" @@ -456,6 +473,7 @@ msgstr "∗x musí být cíl přiřazení" msgid ", in %q\n" msgstr ", v% q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -474,7 +492,7 @@ msgstr "pow() nepodporuje 3 argumenty" msgid "AP could not be started" msgstr "AP nemohl být spuštěn" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "Adresa musí být %d bajtů dlouhá" @@ -509,7 +527,8 @@ msgstr "Všechny RX FIFO jsou používány" msgid "All SPI peripherals are in use" msgstr "Všechny SPI periferie jsou používány" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Všechny UART periferie jsou používány" @@ -580,7 +599,7 @@ msgstr "Již běží" msgid "Already scanning for wifi networks" msgstr "Již skenuje wifi sítě" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "Došlo k chybě při načítání '%s'\n" @@ -618,6 +637,7 @@ msgstr "Konverze audia není implementována" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -645,6 +665,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "Baudrate není podporován periférií" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -662,20 +683,17 @@ msgstr "Velikost bitmapy a počet bitů na hodnotu se musí shodovat" msgid "Boot device must be first (interface #0)." msgstr "Bootovací zařízení musí být první (rozhraní #0)." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "RX a TX jsou vyžadovány pro kontrolu toku" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" msgstr "Jas není nastavitelný" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Vyrovnávací paměť + offset je příliš malý %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "Prvky bufferu musí být 4 bajty dlouhé nebo méně" @@ -718,10 +736,6 @@ msgstr "Buffer příliš malý" msgid "Bus pin %d is already in use" msgstr "Sběrnicový pin %d je již používán" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "Buffer musí být dlouhý 16 bajtů." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "Bloky CBC musí být násobky 16 bajtů" @@ -776,6 +790,10 @@ msgstr "Nelze změnit USB zařízení" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "Není možné vytvořit nový adaptér; použití _bleio.adapter;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -810,6 +828,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "Nelze nastavit hodnotu, když směr je vstup." @@ -861,6 +880,10 @@ msgstr "Pole souřadnic mají různé délky" msgid "Coordinate arrays types have different sizes" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -938,6 +961,7 @@ msgstr "Displej musí mít 16bitový barevný prostor." #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "Otočení displeje musí být po 90 stupních" @@ -946,6 +970,7 @@ msgid "Done" msgstr "Hotovo" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "" @@ -957,8 +982,17 @@ msgstr "Při zpracování uvedené výjimky nastala další výjimka:" msgid "ECB only operates on 16 bytes at a time" msgstr "ECB operuje najednou pouze 16 bajtů" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "ESP-IDF alokace paměti selhala" @@ -1005,7 +1039,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "Chyba alokace %q bufferu" @@ -1023,10 +1057,12 @@ msgstr "Nepodařilo se nabufferovat sample" #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "Připojení se nezdařilo: interní chyba" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "Nepodařilo se připojit: časový limit" @@ -1063,6 +1099,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "Nepodařilo se uvolnit mutex, err 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1080,7 +1120,6 @@ msgid "File exists" msgstr "soubor existuje" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "Soubor nenalezen" @@ -1135,6 +1174,7 @@ msgstr "Inicializace GNSS" msgid "Generic Failure" msgstr "Základní chyba" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -1233,7 +1273,8 @@ msgstr "Interní audio buffer je příliš malý" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "Interní chyba" @@ -1267,22 +1308,29 @@ msgstr "Chyba přerušení." msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" msgstr "Špatný %s" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1316,6 +1364,10 @@ msgstr "Chybná MAC adresa" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "Neplatný argument" @@ -1324,17 +1376,12 @@ msgstr "Neplatný argument" msgid "Invalid bits per value" msgstr "" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "Špatný byte %.*s" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "Chybný data_pin[%d]" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "Špatný formát" @@ -1359,12 +1406,13 @@ msgstr "Chybná velikost" msgid "Invalid socket for TLS" msgstr "Chybný soket pro TLS" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" msgstr "Chybný stav" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "Neplatná unicode escape sekvence" @@ -1372,10 +1420,6 @@ msgstr "Neplatná unicode escape sekvence" msgid "Key must be 16, 24, or 32 bytes long" msgstr "Klíč musí být dlouhý 16, 24 nebo 32 bajtů" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "Klíč nenalezen" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "Mapování LED musí korespondovat s velikostí displeje" @@ -1414,6 +1458,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1455,8 +1503,7 @@ msgstr "Chybí jmp_pin. %q[%u] skáče na pin" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "Musí být podtřída %q." @@ -1503,7 +1550,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "Žádný %q pin" @@ -1521,6 +1568,7 @@ msgstr "Žádný DAC na čipu" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "Nebyl nalezen žádný kanál DMA" @@ -1635,14 +1683,10 @@ msgid "Not connected" msgstr "Nepřipojený" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "Nehraje" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -1737,6 +1781,7 @@ msgid "Operation or feature not supported" msgstr "Operace nebo funkce není podporována" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "Časový limit operace vypršel" @@ -1878,6 +1923,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -1917,6 +1963,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2009,6 +2056,10 @@ msgstr "Konfigurace SPI selhala" msgid "SPI init error" msgstr "Chyba inicializace SPI" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "SPI periferie je používána" @@ -2054,10 +2105,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "SocketPool je možné použít pouze s wifi.radio" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "Zdrojové a cílové buffery musí být stejné délky" @@ -2140,11 +2194,16 @@ msgstr "Čas je v minulosti." msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "Časový limit je příliš dlouhý: maximální limit je %d vteřin" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "V samplu je příliš mnoho kanálů" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2184,6 +2243,10 @@ msgstr "De-inicializace UART" msgid "UART init" msgstr "Inicializace UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "UART periférie je používána" @@ -2192,6 +2255,14 @@ msgstr "UART periférie je používána" msgid "UART re-init" msgstr "Opětovná inicializace UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "Zápis na UART" @@ -2216,10 +2287,6 @@ msgstr "USB zařízení používají příliš mnoho názvů rozhraní." msgid "USB error" msgstr "Chyba USB" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "UUID integer musí být 0-0xffff" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "UUID řetězec neodpovídá 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" @@ -2236,6 +2303,7 @@ msgstr "Nelze přistupovat k nezarovnanému IO registru" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" @@ -2244,6 +2312,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "Není možné vytvořit zámek" @@ -2349,6 +2418,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "Nepodporovaný barevný prostor" @@ -2371,6 +2444,13 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c @@ -2515,6 +2595,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -2581,10 +2665,6 @@ msgstr "pole je příliš velké" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "přetečení v asm" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2798,6 +2878,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2896,14 +2980,14 @@ msgstr "nelze převést complex na dtype" msgid "cannot convert complex type" msgstr "nelze převést typ complex" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "nelze smazat prvky pole" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "nelze změnit rozměry pole" @@ -3058,7 +3142,7 @@ msgstr "dimenze nesouhlasí" msgid "div/mod not implemented for uint" msgstr "div/mod nejsou implementované pro uint" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "dělení nulou" @@ -3157,10 +3241,6 @@ msgstr "" msgid "file write is not available" msgstr "zápis do souboru není dostupný" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "První argument musí být zavolatelný" @@ -3201,10 +3281,6 @@ msgstr "" msgid "float unsupported" msgstr "float není podporován" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3286,10 +3362,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "generátor způsobil StopIteration" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "hash je konečný" @@ -3356,10 +3428,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "výchozí hodnoty musí být iterovatelné" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "délka initial_value je chybná" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3450,6 +3518,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3644,6 +3716,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3692,10 +3768,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3717,7 +3789,7 @@ msgstr "záporný faktoriál" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -3794,6 +3866,10 @@ msgstr "není implementováno pro komplexní dtype" msgid "not supported for input types" msgstr "není podporováno pro vstupní typy" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3807,6 +3883,10 @@ msgstr "objekt " msgid "object '%s' isn't a tuple or list" msgstr "objekt '%s' není tuple or nebo list" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" @@ -4007,10 +4087,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" @@ -4068,6 +4144,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4077,6 +4157,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4341,10 +4425,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "neočekávané odsazení" @@ -4366,10 +4446,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" @@ -4538,6 +4614,29 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "asm overflow" +#~ msgstr "přetečení v asm" + +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Vyrovnávací paměť + offset je příliš malý %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "Buffer musí být dlouhý 16 bajtů." + +#~ msgid "UUID integer value must be 0-0xffff" +#~ msgstr "UUID integer musí být 0-0xffff" + +#~ msgid "initial_value length is wrong" +#~ msgstr "délka initial_value je chybná" + +#, c-format +#~ msgid "Invalid byte %.*s" +#~ msgstr "Špatný byte %.*s" + +#~ msgid "Key not found" +#~ msgstr "Klíč nenalezen" + #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c vyžaduje int nebo char" @@ -4830,8 +4929,8 @@ msgstr "" #~ msgstr "IV musí být dlouhé %d bajtů" #~ msgid "" -#~ "Incompatible .mpy file. Please update all .mpy files. See http://adafru." -#~ "it/mpy-update for more info." +#~ "Incompatible .mpy file. Please update all .mpy files. See http://" +#~ "adafru.it/mpy-update for more info." #~ msgstr "" #~ "Nekompatibilní soubor .mpy. Aktualizujte prosím všechny soubory .mpy. " #~ "Další informace naleznete na adrese http://adafru.it/mpy-update." diff --git a/locale/el.po b/locale/el.po index 082ae20351e3b..f298c5c17e32f 100644 --- a/locale/el.po +++ b/locale/el.po @@ -93,6 +93,11 @@ msgid "" msgstr "" "%d pin διεύθυνσης, %d rgb ping και %d πλακίδια αναδεικνύουν ύψος %d, όχι %d" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q και %q περιέχουν διπλότυπα pins" @@ -114,6 +119,7 @@ msgid "%q contains duplicate pins" msgstr "%q περιέχει διπλότυπα pins" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q αποτυχία: %d" @@ -126,6 +132,8 @@ msgid "%q in %q must be of type %q, not %q" msgstr "%q στο %q πρέπει να είναι τύπου %q, όχι %q" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -133,7 +141,8 @@ msgstr "%q στο %q πρέπει να είναι τύπου %q, όχι %q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q είναι σε χρήση" @@ -145,7 +154,9 @@ msgstr "%q δείκτης εκτός εμβέλειας" msgid "%q indices must be integers, not %s" msgstr "%q δείκτες πρέπει να είναι ακέραιοι, όχι %s" -#: shared-module/bitbangio/SPI.c +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "%q εκκίνηση απέτυχε" @@ -177,8 +188,8 @@ msgstr "%q μήκος πρέπει να είναι >= %d" msgid "%q must be %d" msgstr "%q πρέπει να είναι %d" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -248,23 +259,29 @@ msgstr "%q πρέπει να είναι τύπου %q, όχι %q" msgid "%q must be power of 2" msgstr "%q πρέπει να είναι δύναμη του 2" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "%q εκτός ορίων" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "%q εκτός εμβέλειας" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "%q μεταονομάστηκε σε %q" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "%q βήμα δεν μπορεί να είναι μηδέν" @@ -315,7 +332,7 @@ msgstr "%s σφάλμα 0x%x" msgid "'%q' argument required" msgstr "'%q' όρισμα απαιτείται" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "'%q' αντικείμενο δεν υποστηρίζει '%q'" @@ -460,6 +477,7 @@ msgstr "*x πρέπει να είναι στόχος ανάθεσης" msgid ", in %q\n" msgstr ", στο %q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -478,7 +496,7 @@ msgstr "pow() με 3 παραμέτρους δεν υποστηρίζεται" msgid "AP could not be started" msgstr "AP δεν μπόρεσε να εκκινηθεί" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "Η διεύθυνση πρέπει να είναι %d bytes μεγάλη" @@ -513,7 +531,8 @@ msgstr "Όλα τα RX FIFOs είναι σε χρήση" msgid "All SPI peripherals are in use" msgstr "Όλα τα SPI περιφεριακά είναι σε χρήση" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Όλα τα UART περιφεριακά ειναι σε χρήση" @@ -584,7 +603,7 @@ msgstr "Τρέχει ήδη" msgid "Already scanning for wifi networks" msgstr "Ήδη γίνεται σάρωση για δίκτυα wifi" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "Παρουσιάστηκε σφάλμα κατά την ανάκτηση '%s':\n" @@ -622,6 +641,7 @@ msgstr "Η μετατροπή ήχου δεν υποστηρίζεται" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -649,6 +669,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "Baudrate δεν υποστηρίζεται από την περιφεριακή συσκευή" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -666,20 +687,17 @@ msgstr "Το μέγεθος του bitmap και τα bits ανα τιμή πρ msgid "Boot device must be first (interface #0)." msgstr "Η συσκευή εκκίνησης πρέπει να επιλεχθεί πρώτα (διεπαφή #0)." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "Και RX και TX απαιτούνται για έλεγχο flow" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" msgstr "H φωτεινότητα δεν μπορεί να προσαρμοστεί" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Buffer + offset είναι πολύ μικρά %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "Στοιχεία του buffer πρέπει να είναι το πολύ 4 bytes" @@ -722,10 +740,6 @@ msgstr "Buffer πολύ μικρός" msgid "Bus pin %d is already in use" msgstr "Bus pin %d είναι ήδη σε χρήση" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "Byte buffer πρέπει να είναι 16 bytes." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "CBC blocks πρέπει να είναι πολλαπλάσια του 16 bytes" @@ -779,6 +793,10 @@ msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "" "Δεν μπορεί να δημιουργηθεί νέο Adapter; χρησιμοποιείστε _bleio.adapter;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -815,6 +833,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "Δεν μπορεί να οριστεί τιμή οταν η κατεύθυνση είναι input." @@ -867,6 +886,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -945,6 +968,7 @@ msgstr "Η οθόνη πρέπει να έχει 16 bit χρωματική ευ #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "Η περιστροφή της οθόνη πρέπει να γίνεται σε βήματα 90 μοιρών" @@ -953,6 +977,7 @@ msgid "Done" msgstr "Ολοκληρώθηκε" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "Ο τρόπος οδήγησης δεν χρησιμοποιείται όταν η κατεύθυνση είναι είσοδος." @@ -965,8 +990,17 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "ECB δουλεύει μόνο σε 16 bytes κάθε φορά" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "ESP-IDF δέσμευση μνήμης απέτυχε" @@ -1013,7 +1047,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1031,10 +1065,12 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "" @@ -1071,6 +1107,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1088,7 +1128,6 @@ msgid "File exists" msgstr "" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "" @@ -1141,6 +1180,7 @@ msgstr "" msgid "Generic Failure" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -1239,7 +1279,8 @@ msgstr "" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1273,22 +1314,29 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" msgstr "" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1322,6 +1370,10 @@ msgstr "" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "" @@ -1330,17 +1382,12 @@ msgstr "" msgid "Invalid bits per value" msgstr "" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "" @@ -1365,12 +1412,13 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "" @@ -1378,10 +1426,6 @@ msgstr "" msgid "Key must be 16, 24, or 32 bytes long" msgstr "" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "" @@ -1420,6 +1464,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1461,8 +1509,7 @@ msgstr "" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "" @@ -1509,7 +1556,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "" @@ -1527,6 +1574,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "" @@ -1641,14 +1689,10 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -1742,6 +1786,7 @@ msgid "Operation or feature not supported" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "" @@ -1883,6 +1928,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -1922,6 +1968,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2014,6 +2061,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2059,10 +2110,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" @@ -2145,11 +2199,16 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2188,6 +2247,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2196,6 +2259,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -2220,10 +2291,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "" @@ -2240,6 +2307,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" @@ -2248,6 +2316,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -2353,6 +2422,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" @@ -2375,6 +2448,13 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c @@ -2514,6 +2594,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -2580,10 +2664,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2797,6 +2877,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2895,14 +2979,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3057,7 +3141,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3156,10 +3240,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" @@ -3200,10 +3280,6 @@ msgstr "" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3285,10 +3361,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -3355,10 +3427,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3449,6 +3517,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3643,6 +3715,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3691,10 +3767,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3716,7 +3788,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -3793,6 +3865,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3806,6 +3882,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" @@ -4006,10 +4086,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" @@ -4067,6 +4143,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4076,6 +4156,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4340,10 +4424,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4365,10 +4445,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" @@ -4537,6 +4613,16 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "%q renamed %q" +#~ msgstr "%q μεταονομάστηκε σε %q" + +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Buffer + offset είναι πολύ μικρά %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "Byte buffer πρέπει να είναι 16 bytes." + #~ msgid "%q moved from %q to %q" #~ msgstr "%q μετακινήθηκε από το %q στο %q" diff --git a/locale/es.po b/locale/es.po index fa7377b5b53d5..85a03408b7517 100644 --- a/locale/es.po +++ b/locale/es.po @@ -8,8 +8,8 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-07-17 04:01+0000\n" -"Last-Translator: Jean Serrano \n" +"PO-Revision-Date: 2025-08-03 08:01+0000\n" +"Last-Translator: MAE \n" "Language-Team: \n" "Language: es\n" "MIME-Version: 1.0\n" @@ -1803,10 +1803,9 @@ msgid "PWM slice channel A already in use" msgstr "Segmento del PWM canal A ya esta en uso" #: shared-bindings/spitarget/SPITarget.c -#, fuzzy msgid "Packet buffers for an SPI transfer must have the same length." msgstr "" -"Búferes de paquetes para transferencia SPI deben de ser de igual longitud" +"Búferes de paquetes para transferencia SPI deben de ser de igual longitud." #: shared-module/jpegio/JpegDecoder.c msgid "Parameter error" diff --git a/locale/fr.po b/locale/fr.po index 3a53a97e8fa09..f871c669f93ed 100644 --- a/locale/fr.po +++ b/locale/fr.po @@ -8,7 +8,7 @@ msgstr "" "Project-Id-Version: 0.1\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-06-30 16:01+0000\n" +"PO-Revision-Date: 2025-08-03 08:01+0000\n" "Last-Translator: MAE \n" "Language-Team: \n" "Language: fr\n" @@ -49,7 +49,7 @@ msgid "" "Press reset to exit safe mode.\n" msgstr "" "\n" -"Pour quitter le mode sans échec, appuyez sur reset.\n" +"Appuyer sur reset pour sortir du mode sans échec.\n" #: supervisor/shared/safe_mode.c msgid "" @@ -57,7 +57,7 @@ msgid "" "You are in safe mode because:\n" msgstr "" "\n" -"Le mode sans échec est actif , car:\n" +"Le mode sans échec est actif pour cette raison:\n" #: py/obj.c msgid " File \"%q\"" @@ -1879,15 +1879,15 @@ msgstr "Plus tout autres modules présents sur le système de fichiers\n" #: shared-module/vectorio/Polygon.c msgid "Polygon needs at least 3 points" -msgstr "Polygon a besoin d'au moins 3 points" +msgstr "Un polygone a besoin d'au moins 3 points" #: supervisor/shared/safe_mode.c msgid "Power dipped. Make sure you are providing enough power." -msgstr "Chute de puissance.Apportez plus d'énergie." +msgstr "Chute de puissance. Assurez-vous de fournir assez de puissance." #: shared-bindings/_bleio/Adapter.c msgid "Prefix buffer must be on the heap" -msgstr "Le tampon de préfixe doit être sur la pile" +msgstr "Le tampon de préfixe doit être sur le tas" #: main.c msgid "Press any key to enter the REPL. Use CTRL-D to reload.\n" @@ -1919,11 +1919,12 @@ msgstr "Programme trop long" #: shared-bindings/rclcpy/Publisher.c msgid "Publishers can only be created from a parent node" -msgstr "Les Publishers ne peuvent etre crées que du noeud parent" +msgstr "Les Publishers ne peuvent être créés qu'à partir du nœud parent" #: shared-bindings/digitalio/DigitalInOut.c msgid "Pull not used when direction is output." -msgstr "Le tirage 'pull' n'est pas utilisé quand la direction est 'output'." +msgstr "" +"L'attribut \"pull\" n'est pas utilisé quand la direction est \"output\"." #: ports/raspberrypi/common-hal/countio/Counter.c msgid "RISE_AND_FALL not available on this chip" @@ -2024,7 +2025,7 @@ msgstr "Format correct mais non supporté" #: main.c msgid "Running in safe mode! Not running saved code.\n" -msgstr "Mode sans- échec ! Le code sauvegardé n'est pas éxecuté.\n" +msgstr "Mode sans échec ! Le code sauvegardé n'est pas éxecuté.\n" #: shared-module/sdcardio/SDCard.c msgid "SD card CSD format not supported" @@ -3971,7 +3972,7 @@ msgstr "seules les tranches avec 'step=1' (cad None) sont supportées" #: py/vm.c msgid "opcode" -msgstr "opcode" +msgstr "" #: extmod/ulab/code/ndarray.c extmod/ulab/code/numpy/bitwise.c #: extmod/ulab/code/numpy/compare.c extmod/ulab/code/numpy/vector.c diff --git a/locale/hi.po b/locale/hi.po index 8e4aa0990aee7..f9d7b898198f6 100644 --- a/locale/hi.po +++ b/locale/hi.po @@ -80,6 +80,11 @@ msgid "" "%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" msgstr "" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "" @@ -101,6 +106,7 @@ msgid "%q contains duplicate pins" msgstr "" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "" @@ -113,6 +119,8 @@ msgid "%q in %q must be of type %q, not %q" msgstr "" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -120,7 +128,8 @@ msgstr "" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "" @@ -132,7 +141,9 @@ msgstr "" msgid "%q indices must be integers, not %s" msgstr "" -#: shared-module/bitbangio/SPI.c +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "" @@ -164,8 +175,8 @@ msgstr "" msgid "%q must be %d" msgstr "" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -235,23 +246,29 @@ msgstr "" msgid "%q must be power of 2" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "" @@ -302,7 +319,7 @@ msgstr "" msgid "'%q' argument required" msgstr "" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "" @@ -447,6 +464,7 @@ msgstr "" msgid ", in %q\n" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -465,7 +483,7 @@ msgstr "" msgid "AP could not be started" msgstr "" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "" @@ -500,7 +518,8 @@ msgstr "" msgid "All SPI peripherals are in use" msgstr "" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "" @@ -571,7 +590,7 @@ msgstr "" msgid "Already scanning for wifi networks" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "" @@ -609,6 +628,7 @@ msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -634,6 +654,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -651,20 +672,17 @@ msgstr "" msgid "Boot device must be first (interface #0)." msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" msgstr "" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "" @@ -707,10 +725,6 @@ msgstr "" msgid "Bus pin %d is already in use" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "" - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "" @@ -762,6 +776,10 @@ msgstr "" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -796,6 +814,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "" @@ -846,6 +865,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -922,6 +945,7 @@ msgstr "" #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "" @@ -930,6 +954,7 @@ msgid "Done" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "" @@ -941,8 +966,17 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "" @@ -989,7 +1023,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1007,10 +1041,12 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "" @@ -1047,6 +1083,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1064,7 +1104,6 @@ msgid "File exists" msgstr "" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "" @@ -1117,6 +1156,7 @@ msgstr "" msgid "Generic Failure" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -1215,7 +1255,8 @@ msgstr "" msgid "Internal define error" msgstr "" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "" @@ -1249,22 +1290,29 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" msgstr "" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1298,6 +1346,10 @@ msgstr "" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "" @@ -1306,17 +1358,12 @@ msgstr "" msgid "Invalid bits per value" msgstr "" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "" @@ -1341,12 +1388,13 @@ msgstr "" msgid "Invalid socket for TLS" msgstr "" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" msgstr "" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "" @@ -1354,10 +1402,6 @@ msgstr "" msgid "Key must be 16, 24, or 32 bytes long" msgstr "" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "" @@ -1396,6 +1440,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1437,8 +1485,7 @@ msgstr "" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "" @@ -1485,7 +1532,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "" @@ -1503,6 +1550,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "" @@ -1617,14 +1665,10 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -1718,6 +1762,7 @@ msgid "Operation or feature not supported" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "" @@ -1857,6 +1902,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -1896,6 +1942,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -1988,6 +2035,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2033,10 +2084,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" @@ -2119,11 +2173,16 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2162,6 +2221,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2170,6 +2233,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -2194,10 +2265,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "" @@ -2214,6 +2281,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" @@ -2222,6 +2290,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -2327,6 +2396,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" @@ -2349,6 +2422,13 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c @@ -2488,6 +2568,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -2554,10 +2638,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2771,6 +2851,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2869,14 +2953,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3031,7 +3115,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3130,10 +3214,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" @@ -3174,10 +3254,6 @@ msgstr "" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3259,10 +3335,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -3329,10 +3401,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3423,6 +3491,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3617,6 +3689,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3665,10 +3741,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3690,7 +3762,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -3767,6 +3839,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3780,6 +3856,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" @@ -3980,10 +4060,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" @@ -4041,6 +4117,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4050,6 +4130,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4314,10 +4398,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4339,10 +4419,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" diff --git a/locale/ko.po b/locale/ko.po index 8bde494eeec78..9e570208d398a 100644 --- a/locale/ko.po +++ b/locale/ko.po @@ -91,6 +91,11 @@ msgid "" msgstr "" "%d 주소 핀들, %d rgb 핀들과 %d 타일 들은 높이가 %d임을 나타낸다, %d가 아니라" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q 및 %q에 중복된 핀이 포함" @@ -112,6 +117,7 @@ msgid "%q contains duplicate pins" msgstr "%q에 중복된 핀이 포함" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q 실패: %d" @@ -124,6 +130,8 @@ msgid "%q in %q must be of type %q, not %q" msgstr "%q의 %q는 %q가 아니라 %q 유형이어야 합니다" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -131,7 +139,8 @@ msgstr "%q의 %q는 %q가 아니라 %q 유형이어야 합니다" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q 사용 중입니다" @@ -143,7 +152,9 @@ msgstr "%q 인덱스 범위를 벗어났습니다" msgid "%q indices must be integers, not %s" msgstr "%q 인덱스는 %s 가 아닌 정수 여야합니다" -#: shared-module/bitbangio/SPI.c +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "%q 초기화 실패" @@ -175,8 +186,8 @@ msgstr "%q 길이는 >= %d이어야 합니다" msgid "%q must be %d" msgstr "%q는 %d이어야 합니다" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -251,25 +262,31 @@ msgstr "%q는 %q가 아니라 %q 유형이어야 합니다" msgid "%q must be power of 2" msgstr "%q는 2의 거듭제곱이어야 합니다" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c #, fuzzy msgid "%q out of bounds" msgstr "%q가 경계를 벗어남" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c #, fuzzy msgid "%q out of range" msgstr "%q가 범위를 벗어남" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "%q가 %q로 이름이 변경되었습니다" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c #, fuzzy msgid "%q step cannot be zero" @@ -324,7 +341,7 @@ msgstr "%s 오류 0x%x" msgid "'%q' argument required" msgstr "'%q' 인수가 필요합니다" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "'%q' 개체가 '%q'를 지원하지 않습니다" @@ -475,6 +492,7 @@ msgstr "*x는 할당 대상이어야 합니다" msgid ", in %q\n" msgstr ", 에서 %q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -494,7 +512,7 @@ msgstr "pow() 는 3개의 인수를 지원하지 않습니다" msgid "AP could not be started" msgstr "AP를 시작할 수 없습니다" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "주소 길이는 %d 바이트 여야합니다" @@ -531,7 +549,8 @@ msgstr "모든 RX FIFOs가 사용 중입니다" msgid "All SPI peripherals are in use" msgstr "사용중인 모든 SPI주변 기기" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "사용중인 모든 UART주변 기기" @@ -608,7 +627,7 @@ msgstr "이미 실행 중입니다" msgid "Already scanning for wifi networks" msgstr "이미 wifi 네트워크를 찾고 있습니다" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "%s'을(를) 검색하는 동안 오류가 발생했습니다:\n" @@ -648,6 +667,7 @@ msgstr "오디오 변환이 구현되지 않음" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -675,6 +695,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "주변 기기에서 전송 속도가 지원되지 않습니다" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -692,20 +713,17 @@ msgstr "비트맵 크기와 값 당 비트가 일치해야 합니다" msgid "Boot device must be first (interface #0)." msgstr "부팅 장치는 첫 번째(인터페이스 #0)여야 합니다." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "플로우 제어에 RX와 TX가 모두 필요합니다" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" msgstr "밝기를 조절할 수 없습니다" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Buffer + offset이 너무 작습니다 %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "버퍼 요소는 4바이트 이하여야 합니다" @@ -748,10 +766,6 @@ msgstr "버퍼가 너무 작습니다" msgid "Bus pin %d is already in use" msgstr "Bus 핀 %d은 이미 사용 중입니다" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "잘못된 크기의 버퍼. 16 바이트 여야합니다." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "CBC 블록은 16 바이트의 배수여야 합니다" @@ -804,6 +818,10 @@ msgstr "현재 USB 디바이스를 변경할 수 없습니다" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "_bleio.adapter를 사용해서; 새로운 Adapter를 만들 수 없습니다;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -838,6 +856,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "방향이 입력되면 값을 설정할 수 없습니다." @@ -890,6 +909,10 @@ msgstr "좌표 배열의 길이가 다릅니다" msgid "Coordinate arrays types have different sizes" msgstr "좌표 배열 유형은 크기가 다릅니다" +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -967,6 +990,7 @@ msgstr "디스플레이는 16 비트 색 공간을 가져야 합니다." #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "디스플레이 회전은 90도씩 증가해야 합니다" @@ -975,6 +999,7 @@ msgid "Done" msgstr "완료" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "방향을 입력할 때 드라이브 모드는 사용되지 않습니다." @@ -986,8 +1011,17 @@ msgstr "위 예외를 처리하는 동안, 또 다른 예외가 발생하였습 msgid "ECB only operates on 16 bytes at a time" msgstr "ECB는 한 번에 16 바이트에서만 작동합니다" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "ESP-IDF 메모리 할당에 실패하였습니다" @@ -1037,7 +1071,7 @@ msgstr "" "서비스 TXT 레코드를 추가하는 것에 실패했습니다; txt_records에서 비문자열 또" "는 바이트가 발견되었습니다" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "%q 버퍼 할당에 실패했습니다" @@ -1055,10 +1089,12 @@ msgstr "샘플 버퍼링에 실패했습니다" #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "연결에 실패했습니다: 내부 오류" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "연결에 실패했습니다: 시간 초과" @@ -1095,6 +1131,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "뮤텍스 해제에 실패했습니다, 오류 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1113,7 +1153,6 @@ msgid "File exists" msgstr "파일이 있습니다" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "파일을 찾을 수 없습니다" @@ -1168,6 +1207,7 @@ msgstr "GNSS 초기화" msgid "Generic Failure" msgstr "일반 오류" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -1266,7 +1306,8 @@ msgstr "내부 오디오 버퍼가 너무 작습니다" msgid "Internal define error" msgstr "내부 정의 오류" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "내부 오류" @@ -1300,22 +1341,29 @@ msgstr "인터럽트 오류." msgid "Interrupted by output function" msgstr "출력 함수로 인해 종료되었다" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" msgstr "잘못된 %q" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1349,6 +1397,10 @@ msgstr "잘못된 MAC 주소" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "잘못된 인수" @@ -1357,17 +1409,12 @@ msgstr "잘못된 인수" msgid "Invalid bits per value" msgstr "값 당 잘못된 비트" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "잘못된 바이트 %.*s" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "잘못된 data_pins[%d]" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "잘못된 형식" @@ -1392,12 +1439,13 @@ msgstr "잘못된 크기" msgid "Invalid socket for TLS" msgstr "TLS에 대한 잘못된 소켓" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" msgstr "잘못된 상태" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "잘못된 유니코드 이스케이프" @@ -1405,10 +1453,6 @@ msgstr "잘못된 유니코드 이스케이프" msgid "Key must be 16, 24, or 32 bytes long" msgstr "키는 16, 24, 또는 32 바이트 길이여야 합니다" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "키를 찾을 수 없습니다" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "LED 매핑은 디스플레이 크기와 일치해야 합니다" @@ -1447,6 +1491,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "매핑은 투플이어야 합니다" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1491,8 +1539,7 @@ msgstr "jmp_pin이 누락되었습니다. %q[%u] 핀으로 점프합니다" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "%q의 하위클래스여야 합니다." @@ -1540,7 +1587,7 @@ msgstr "빠른 메모리 부족" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "%q 핀이 없습니다" @@ -1558,6 +1605,7 @@ msgstr "칩에 DAC가 없습니다" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "DMA 채널을 찾을 수 없습니다" @@ -1675,15 +1723,10 @@ msgid "Not connected" msgstr "연결되지 않았습니다" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "재생되지 않았습니다" -#: shared-module/jpegio/JpegDecoder.c -#, fuzzy -msgid "Not supported JPEG standard" -msgstr "지원되지 않는 JPEG 표준" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -1782,6 +1825,7 @@ msgid "Operation or feature not supported" msgstr "작업 또는 기능이 지원되지 않습니다" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #, fuzzy msgid "Operation timed out" msgstr "작업 시간 초과되었습니다" @@ -1930,6 +1974,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c #, fuzzy msgid "Pull not used when direction is output." msgstr "방향이 출력 될 때 풀은 사용되지 않습니다" @@ -1970,6 +2015,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2062,6 +2108,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2107,10 +2157,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" @@ -2193,11 +2246,16 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2236,6 +2294,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2244,6 +2306,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -2268,10 +2338,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "UUID문자열이 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'형식이 아닙니다" @@ -2289,6 +2355,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" @@ -2297,6 +2364,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -2402,6 +2470,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" @@ -2424,6 +2496,13 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c @@ -2563,6 +2642,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -2629,10 +2712,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2846,6 +2925,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2944,14 +3027,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3106,7 +3189,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3205,10 +3288,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" @@ -3249,10 +3328,6 @@ msgstr "float이 너무 큽니다" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3334,10 +3409,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -3404,10 +3475,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3498,6 +3565,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3692,6 +3763,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3740,10 +3815,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3765,7 +3836,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -3842,6 +3913,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3855,6 +3930,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" @@ -4055,10 +4134,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" @@ -4116,6 +4191,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4125,6 +4204,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4389,10 +4472,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4414,10 +4493,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" @@ -4586,6 +4661,27 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#~ msgid "%q renamed %q" +#~ msgstr "%q가 %q로 이름이 변경되었습니다" + +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Buffer + offset이 너무 작습니다 %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "잘못된 크기의 버퍼. 16 바이트 여야합니다." + +#, c-format +#~ msgid "Invalid byte %.*s" +#~ msgstr "잘못된 바이트 %.*s" + +#~ msgid "Key not found" +#~ msgstr "키를 찾을 수 없습니다" + +#, fuzzy +#~ msgid "Not supported JPEG standard" +#~ msgstr "지원되지 않는 JPEG 표준" + #, fuzzy #~ msgid "%q moved from %q to %q" #~ msgstr "%q가 %q에서 %q로 이동했습니다" diff --git a/locale/pl.po b/locale/pl.po index 9b20c513e7a09..2e55f983e0027 100644 --- a/locale/pl.po +++ b/locale/pl.po @@ -7,8 +7,8 @@ msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-01-04 12:55-0600\n" -"PO-Revision-Date: 2025-07-23 09:07+0000\n" -"Last-Translator: MuskoM \n" +"PO-Revision-Date: 2025-08-06 18:01+0000\n" +"Last-Translator: MAE \n" "Language-Team: pl\n" "Language: pl\n" "MIME-Version: 1.0\n" @@ -90,8 +90,8 @@ msgstr "%%c wymaga int lub char" msgid "" "%d address pins, %d rgb pins and %d tiles indicate a height of %d, not %d" msgstr "" -"%d pinów adresowalnych, %d pinów rgb oraz %d płytek wskazują na wysokość " -"%d, nie %d" +"%d pinów adresowalnych, %d pinów rgb oraz %d płytek wskazują na wysokość %d, " +"nie %d" #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" @@ -204,7 +204,7 @@ msgstr "" #: py/argcheck.c msgid "%q must be >= %d" -msgstr "" +msgstr "%q musi być >= %d" #: shared-bindings/analogbufio/BufferedIn.c msgid "%q must be a bytearray or array of type 'H' or 'B'" diff --git a/locale/ru.po b/locale/ru.po index d3d9415230455..0f7c6c0944b2f 100644 --- a/locale/ru.po +++ b/locale/ru.po @@ -93,6 +93,11 @@ msgstr "" "Адресные контакты %d, контакты rgb %d и плитки %d обозначают высоту %d, а не " "%d" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q и %q содержат пины дупликаты" @@ -114,6 +119,7 @@ msgid "%q contains duplicate pins" msgstr "%q содержит пины дупликаты" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q сбой: %d" @@ -126,6 +132,8 @@ msgid "%q in %q must be of type %q, not %q" msgstr "%q в %q должно быть типа %q, а не %q" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -133,7 +141,8 @@ msgstr "%q в %q должно быть типа %q, а не %q" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q используется" @@ -145,7 +154,9 @@ msgstr "Индекс %q вне диапазона" msgid "%q indices must be integers, not %s" msgstr "Индексы %q должны быть целыми числами, а не %s" -#: shared-module/bitbangio/SPI.c +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "Инициализация %q не удалась" @@ -177,8 +188,8 @@ msgstr "Длинна %q должна быть >= %d" msgid "%q must be %d" msgstr "%q должно быть %d" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -248,23 +259,29 @@ msgstr "%q должно быть типа %q, а не %q" msgid "%q must be power of 2" msgstr "%q должен быть во 2-й степени" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "%q за пределом" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "%q вне диапазона" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "%q переименован %q" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "Шаг %q не может быть нулём" @@ -315,7 +332,7 @@ msgstr "%s ошибка 0x%x" msgid "'%q' argument required" msgstr "Требуется аргумент '%q'" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "Объект '%q' не поддерживает '%q'" @@ -460,6 +477,7 @@ msgstr "*x должно быть целью назначения" msgid ", in %q\n" msgstr ", в %q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -478,7 +496,7 @@ msgstr "Pow() с 3 аргументами не поддерживается" msgid "AP could not be started" msgstr "AP не может быть запущен" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "Адрес должен быть длиной %d байт" @@ -513,7 +531,8 @@ msgstr "Все RX FIFO уже используются" msgid "All SPI peripherals are in use" msgstr "Все периферийные устройства SPI уже используются" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Все периферийные устройства UART уже используются" @@ -584,7 +603,7 @@ msgstr "Уже запущен" msgid "Already scanning for wifi networks" msgstr "Поиск сетей wifi уже происходит" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "Произошла ошибка при получении '%s':\n" @@ -622,6 +641,7 @@ msgstr "Преобразование звука не реализовано" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -649,6 +669,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "Скорость передачи данных не поддерживается периферийным устройством" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -667,20 +688,17 @@ msgstr "" msgid "Boot device must be first (interface #0)." msgstr "Загрузочное устройство должно быть первым (интерфейс #0)." +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "Для управления потоком требуется как RX так и TX" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" msgstr "Яркость не регулируется" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Буфер + сдвиг слишком малы %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "Элементы буфера должны иметь длину не более 4 байт" @@ -723,10 +741,6 @@ msgstr "Слишком маленький буфер" msgid "Bus pin %d is already in use" msgstr "Вывод шины %d уже используется" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "Буфер байтов должен быть размером 16 байтам." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "Блоки CBC должны быть кратны 16 байтам" @@ -781,6 +795,10 @@ msgstr "Невозможно изменить USB устройство сейч msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "Невозможно создать новый Adapter; используйте _bleio.adapter;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -817,6 +835,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "Невозможно установить значение при вводе направления." @@ -870,6 +889,10 @@ msgstr "Координатные массивы имеют разные длин msgid "Coordinate arrays types have different sizes" msgstr "Типы массивов координат имеют разные размеры" +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -948,6 +971,7 @@ msgstr "Дисплей должен иметь 16 битное цветовое #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "Поворот дисплея должен осуществляться с шагом 90 градусов" @@ -956,6 +980,7 @@ msgid "Done" msgstr "Выполнено" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "Режим движения не используется при вводе направления." @@ -968,8 +993,17 @@ msgstr "" msgid "ECB only operates on 16 bytes at a time" msgstr "ECB работает только с 16 байтами за раз" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "Ошибка выделения памяти ESP-IDF" @@ -1018,7 +1052,7 @@ msgstr "" "Не удалось добавить служебную TXT-запись; в txt_records обнаружена нестрока " "или байт" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "Не удалось выделить буфер %q" @@ -1036,10 +1070,12 @@ msgstr "Не удалось выполнить буферизацию образ #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "Не удалось подключиться: внутренняя ошибка" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "Не удалось подключиться: таймаут" @@ -1076,6 +1112,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "Не удалось освободить mutex, ошибка 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1093,7 +1133,6 @@ msgid "File exists" msgstr "Файл существует" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "Файл не найден" @@ -1152,6 +1191,7 @@ msgstr "Инициализация GNSS" msgid "Generic Failure" msgstr "Общий сбой" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -1254,7 +1294,8 @@ msgstr "Внутренний звуковой буфер слишком мал" msgid "Internal define error" msgstr "Внутренняя ошибка определения" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "Внутренняя ошибка" @@ -1288,22 +1329,29 @@ msgstr "Прерванная ошибка." msgid "Interrupted by output function" msgstr "Прерывается функцией выхода" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" msgstr "Недопустимый %q" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1337,6 +1385,10 @@ msgstr "Неверный MAC-адрес" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "Недопустимый аргумент" @@ -1345,17 +1397,12 @@ msgstr "Недопустимый аргумент" msgid "Invalid bits per value" msgstr "Недопустимое бит-на-значение" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "Неверный байт %.*s" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "Неверный data_pins[%d]" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "Недопустимый формат" @@ -1380,12 +1427,13 @@ msgstr "Неверный размер" msgid "Invalid socket for TLS" msgstr "Неверный сокет для TLS" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" msgstr "Неверное состояние" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "Недопустимое экранирование Юникода" @@ -1393,10 +1441,6 @@ msgstr "Недопустимое экранирование Юникода" msgid "Key must be 16, 24, or 32 bytes long" msgstr "Ключ должен быть длинной 16, 24 или 32 байта" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "Ключ не найден" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "Светодиодные сопоставления должны соответствовать размеру дисплея" @@ -1435,6 +1479,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "Сопоставление должно быть кортежом" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1476,8 +1524,7 @@ msgstr "Не хватает jmp_pin.%q [%u] прыгает на пин" msgid "Mount point directory missing" msgstr "Отсутствует каталог точки монтирования" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "Должен быть субклассом %q." @@ -1526,7 +1573,7 @@ msgstr "Изображение памяти" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "Нет пина %q" @@ -1544,6 +1591,7 @@ msgstr "DAC отсутствует на чипе" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "Канал DMA не найден" @@ -1658,14 +1706,10 @@ msgid "Not connected" msgstr "Не подключено" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "Не воспроизводится (Not playing)" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "Не поддерживается Стандарт JPEG" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -1763,6 +1807,7 @@ msgid "Operation or feature not supported" msgstr "Операция или функция, не поддерживаемые" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "Истекло время ожидания операции" @@ -1908,6 +1953,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "Тяга не используется, когда выводится направление." @@ -1947,6 +1993,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2039,6 +2086,10 @@ msgstr "Сбой конфигурации SPI" msgid "SPI init error" msgstr "Ошибка инициализации SPI" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "Используемое периферийное устройство SPI" @@ -2084,10 +2135,13 @@ msgstr "Фрагменты не поддерживаются" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "SocketPool можно использовать только с wifi.radio" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "Исходный и конечный буферы должны иметь одинаковую длину" @@ -2175,11 +2229,16 @@ msgstr "Время в прошлом." msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "Таймаут слишком длинный: максимальная длина таймаута %d секунд" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "Слишком много каналов в выборке" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "Слишком много каналов в выборке." @@ -2218,6 +2277,10 @@ msgstr "Деинициализация UART" msgid "UART init" msgstr "Инициализация UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "Используемое периферийное устройство UART" @@ -2226,6 +2289,14 @@ msgstr "Используемое периферийное устройство U msgid "UART re-init" msgstr "Повторная инициализация UART" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "Запись UART" @@ -2250,10 +2321,6 @@ msgstr "USB-устройства указывают слишком много и msgid "USB error" msgstr "Ошибка USB" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "Целое значение UUID должно быть равно 0-0xffff" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "UUID строка не 'xxxxxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxxxxxxxxxx \"" @@ -2270,6 +2337,7 @@ msgstr "Невозможно получить доступ к невыровне #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "Не удается выделить буферы для подписанного преобразования" @@ -2278,6 +2346,7 @@ msgid "Unable to allocate to the heap." msgstr "Невозможно выделить место в куче." #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "Не удается создать блокировку" @@ -2386,6 +2455,10 @@ msgstr "" "Неуказанная проблема. Возможно, запрос на сопряжение на другом устройстве " "был отклонен или проигнорирован." +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "Неподдерживаемое цветовое пространство" @@ -2408,6 +2481,13 @@ msgstr "Неподдерживаемый тип сокета" msgid "Update failed" msgstr "Обновление не удалось" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c @@ -2554,6 +2634,10 @@ msgstr "__new__ arg должен быть пользовательского т msgid "a bytes-like object is required" msgstr "Требуется байтоподобный объект" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "адреса пусты" @@ -2620,10 +2704,6 @@ msgstr "массив слишком велик" msgid "array/bytes required on right side" msgstr "массив/байты, необходимые справа" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "Переполнение ASM" - #: py/compile.c msgid "async for/with outside async function" msgstr "async для/вместе с внешней async-функцией" @@ -2842,6 +2922,10 @@ msgstr "не может превратиться в полосу неявно" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "не может объявить нелокальный во внешнем коде" @@ -2944,14 +3028,14 @@ msgstr "не может превратить комплекс в dtype" msgid "cannot convert complex type" msgstr "Не удается преобразовать сложный тип" -#: py/objtype.c -msgid "cannot create instance" -msgstr "Не удается создать экземпляр" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "Не удается удалить элементы массива" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "Не удается изменить форму массива" @@ -3112,7 +3196,7 @@ msgstr "Размеры не совпадают" msgid "div/mod not implemented for uint" msgstr "div/mod не реализован для uint" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "Делим на ноль" @@ -3211,10 +3295,6 @@ msgstr "Файл должен быть файлом, открытым в бай msgid "file write is not available" msgstr "Запись файлов недоступна" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "Файловая система должна предусматривать метод монтирования" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "Первый аргумент должен быть вызываемым" @@ -3255,10 +3335,6 @@ msgstr "Поплавок слишком большой" msgid "float unsupported" msgstr "Плавающий без поддержки" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "Длина шрифта должна составлять 2048 байт" - #: extmod/moddeflate.c msgid "format" msgstr "формат" @@ -3340,10 +3416,6 @@ msgstr "генератор проигнорировал Выход" msgid "generator raised StopIteration" msgstr "генератор поднят Остановить итерацию" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "Длина рисунка должна составлять 2048 байт" - #: extmod/modhashlib.c msgid "hash is final" msgstr "хэш является окончательным" @@ -3410,10 +3482,6 @@ msgstr "индексы должны быть целыми числами, сре msgid "initial values must be iterable" msgstr "Начальные значения должны быть итерируемыми" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "длина первоначального_значения ошибочна" - #: py/compile.c msgid "inline assembler must be a function" msgstr "Встроенный ассемблер должен быть функцией" @@ -3504,6 +3572,10 @@ msgstr "interp определен для 1D-итераций одинаково msgid "interval must be in range %s-%s" msgstr "Интервал должен находиться в диапазоне %s-%s" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "недействительная арка" @@ -3704,6 +3776,10 @@ msgstr "mktime нужен кортеж длины 8 или 9" msgid "mode must be complete, or reduced" msgstr "Режим должен быть завершенным или уменьшенным" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "модуль не найден" @@ -3752,10 +3828,6 @@ msgstr "слишком длинное имя" msgid "native code in .mpy unsupported" msgstr "Нативный код в .mpy не поддерживается" -#: py/asmthumb.c -msgid "native method too big" -msgstr "родной метод слишком большой" - #: py/emitnative.c msgid "native yield" msgstr "родной урожай" @@ -3777,7 +3849,7 @@ msgstr "отрицательный факториал" msgid "negative power with no float support" msgstr "Отрицательная мощность без поплавковой опоры" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "Количество отрицательных сдвигов" @@ -3856,6 +3928,10 @@ msgstr "не реализовано для сложного типа d" msgid "not supported for input types" msgstr "Не поддерживается для типов ввода" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "Количество баллов должно быть не менее 2" @@ -3869,6 +3945,10 @@ msgstr "объект " msgid "object '%s' isn't a tuple or list" msgstr "Объект \"%s\" не является кортежом или списком" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "Объект не поддерживает назначение элементов" @@ -4069,10 +4149,6 @@ msgstr "переполнение преобразование длинного msgid "pack expected %d items for packing (got %d)" msgstr "Упаковка ожидаемых %d товаров для упаковки (получил %d)" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "Длина палитры должна составлять 32 байта" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" @@ -4130,6 +4206,10 @@ msgstr "Маски вытягивания конфликтуют с маскам msgid "real and imaginary parts must be of equal length" msgstr "реальные и воображаемые части должны быть одинаковой длины" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "Относительный импорт" @@ -4139,6 +4219,10 @@ msgstr "Относительный импорт" msgid "requested length %d but object has length %d" msgstr "запрашиваемая длина %d, но объект имеет длину %d" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "Результаты не могут быть приведены к указанному типу" @@ -4405,10 +4489,6 @@ msgstr "тип объекта '%q' не имеет атрибута '%q \"" msgid "type takes 1 or 3 arguments" msgstr "тип занимает 1 или 3 аргумента" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "голова длинная слишком большая" - #: py/parse.c msgid "unexpected indent" msgstr "Неожиданный отступ" @@ -4430,10 +4510,6 @@ msgstr "Экранирование имен в Юникоде" msgid "unindent doesn't match any outer indent level" msgstr "Отступ не совпадает ни с одним уровнем внешнего отступа" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" @@ -4604,9 +4680,59 @@ msgstr "zi должно быть типа float" msgid "zi must be of shape (n_section, 2)" msgstr "zi должен иметь форму (n_section, 2)" +#~ msgid "%q renamed %q" +#~ msgstr "%q переименован %q" + +#~ msgid "asm overflow" +#~ msgstr "Переполнение ASM" + +#~ msgid "cannot create instance" +#~ msgstr "Не удается создать экземпляр" + +#~ msgid "native method too big" +#~ msgstr "родной метод слишком большой" + +#~ msgid "ulonglong too large" +#~ msgstr "голова длинная слишком большая" + +#~ msgid "font must be 2048 bytes long" +#~ msgstr "Длина шрифта должна составлять 2048 байт" + +#~ msgid "graphic must be 2048 bytes long" +#~ msgstr "Длина рисунка должна составлять 2048 байт" + +#~ msgid "palette must be 32 bytes long" +#~ msgstr "Длина палитры должна составлять 32 байта" + +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Буфер + сдвиг слишком малы %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "Буфер байтов должен быть размером 16 байтам." + +#~ msgid "UUID integer value must be 0-0xffff" +#~ msgstr "Целое значение UUID должно быть равно 0-0xffff" + +#~ msgid "initial_value length is wrong" +#~ msgstr "длина первоначального_значения ошибочна" + +#, c-format +#~ msgid "Invalid byte %.*s" +#~ msgstr "Неверный байт %.*s" + +#~ msgid "Key not found" +#~ msgstr "Ключ не найден" + +#~ msgid "Not supported JPEG standard" +#~ msgstr "Не поддерживается Стандарт JPEG" + #~ msgid "%q moved from %q to %q" #~ msgstr "%q переместился из %q в %q" +#~ msgid "filesystem must provide mount method" +#~ msgstr "Файловая система должна предусматривать метод монтирования" + #~ msgid "start/end indices" #~ msgstr "Начальные/конечные индексы" @@ -5106,8 +5232,8 @@ msgstr "zi должен иметь форму (n_section, 2)" #~ msgstr "IV должен быть длиной %d байт" #~ msgid "" -#~ "Incompatible .mpy file. Please update all .mpy files. See http://adafru." -#~ "it/mpy-update for more info." +#~ "Incompatible .mpy file. Please update all .mpy files. See http://" +#~ "adafru.it/mpy-update for more info." #~ msgstr "" #~ "Несовместимый файл .mpy. Пожалуйста, обновите все файлы .mpy. См. http://" #~ "adafru.it/mpy-update для получения дополнительной информации." diff --git a/locale/tr.po b/locale/tr.po index 10a288553a698..1012dbf8b3937 100644 --- a/locale/tr.po +++ b/locale/tr.po @@ -91,6 +91,11 @@ msgstr "" "%d adres pinleri, %d RGB pinleri ve %d döşemeleri %d'nin yüksekliği " "gösterir, %d'nin değil" +#: py/emitinlinextensa.c +#, c-format +msgid "%d is not a multiple of %d" +msgstr "" + #: shared-bindings/microcontroller/Pin.c msgid "%q and %q contain duplicate pins" msgstr "%q ve %q yinelenen pinler içeriyor" @@ -112,6 +117,7 @@ msgid "%q contains duplicate pins" msgstr "%q yinelenen pinler içeriyor" #: ports/atmel-samd/common-hal/sdioio/SDCard.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "%q failure: %d" msgstr "%q hata: %d" @@ -124,6 +130,8 @@ msgid "%q in %q must be of type %q, not %q" msgstr "" #: ports/espressif/common-hal/espulp/ULP.c +#: ports/espressif/common-hal/mipidsi/Bus.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/usb_host/Port.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c @@ -131,7 +139,8 @@ msgstr "" #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/usb_host/Port.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/microcontroller/Pin.c shared-module/max3421e/Max3421E.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/microcontroller/Pin.c +#: shared-module/max3421e/Max3421E.c msgid "%q in use" msgstr "%q kullanımda" @@ -143,7 +152,9 @@ msgstr "%q indeksi aralık dışında" msgid "%q indices must be integers, not %s" msgstr "%q indeksleri integer olmalı, %s değil" -#: shared-module/bitbangio/SPI.c +#: ports/analog/common-hal/busio/SPI.c ports/analog/common-hal/busio/UART.c +#: shared-bindings/digitalio/DigitalInOutProtocol.c +#: shared-module/busdisplay/BusDisplay.c msgid "%q init failed" msgstr "%q init başarısız oldu" @@ -175,8 +186,8 @@ msgstr "%q boyutu >= %d olmalıdır" msgid "%q must be %d" msgstr "%q, %d olmalıdır" -#: py/argcheck.c shared-bindings/busdisplay/BusDisplay.c -#: shared-bindings/displayio/Bitmap.c +#: ports/zephyr-cp/bindings/zephyr_display/Display.c py/argcheck.c +#: shared-bindings/busdisplay/BusDisplay.c shared-bindings/displayio/Bitmap.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-bindings/is31fl3741/FrameBuffer.c #: shared-bindings/rgbmatrix/RGBMatrix.c @@ -246,23 +257,29 @@ msgstr "" msgid "%q must be power of 2" msgstr "%q, 2'nin kuvveti olmalıdır" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' attribute" +msgstr "" + +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "%q object missing '%q' method" +msgstr "" + #: shared-bindings/wifi/Monitor.c msgid "%q out of bounds" msgstr "%q sınırların dışında" +#: ports/analog/common-hal/busio/SPI.c #: ports/atmel-samd/common-hal/pulseio/PulseIn.c #: ports/cxd56/common-hal/pulseio/PulseIn.c #: ports/nordic/common-hal/pulseio/PulseIn.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c #: ports/stm/common-hal/pulseio/PulseIn.c py/argcheck.c -#: shared-bindings/canio/Match.c shared-bindings/time/__init__.c +#: shared-bindings/bitmaptools/__init__.c shared-bindings/canio/Match.c +#: shared-bindings/time/__init__.c msgid "%q out of range" msgstr "%q aralık dışında" -#: py/objmodule.c -msgid "%q renamed %q" -msgstr "" - #: py/objrange.c py/objslice.c shared-bindings/random/__init__.c msgid "%q step cannot be zero" msgstr "%q sıfır olamaz" @@ -313,7 +330,7 @@ msgstr "%s hatası 0x%x" msgid "'%q' argument required" msgstr "'%q' argümanı gerekli" -#: py/proto.c +#: py/proto.c shared-bindings/digitalio/DigitalInOutProtocol.c msgid "'%q' object does not support '%q'" msgstr "'%q' nesnesi '%q' öğesini desteklemiyor" @@ -458,6 +475,7 @@ msgstr "*x atama hedefi olmalıdır" msgid ", in %q\n" msgstr ", içinde %q\n" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c @@ -476,7 +494,7 @@ msgstr "3-argümanlı pow() desteklenmemektedir" msgid "AP could not be started" msgstr "" -#: shared-bindings/_bleio/Address.c shared-bindings/ipaddress/IPv4Address.c +#: shared-bindings/ipaddress/IPv4Address.c #, c-format msgid "Address must be %d bytes long" msgstr "Adres %d byte uzunluğunda olmalıdır" @@ -511,7 +529,8 @@ msgstr "Tüm RX FIFO'ları kullanımda" msgid "All SPI peripherals are in use" msgstr "Tüm SPI çevre birimleri kullanımda" -#: ports/espressif/common-hal/busio/UART.c ports/nordic/common-hal/busio/UART.c +#: ports/analog/common-hal/busio/UART.c ports/espressif/common-hal/busio/UART.c +#: ports/nordic/common-hal/busio/UART.c msgid "All UART peripherals are in use" msgstr "Tüm UART çevre birimleri kullanımda" @@ -582,7 +601,7 @@ msgstr "Halihazırda çalışıyor" msgid "Already scanning for wifi networks" msgstr "Halihazırda wifi ağları için tarama yapılıyor" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c #, c-format msgid "An error occurred while retrieving '%s':\n" msgstr "" @@ -620,6 +639,7 @@ msgstr "Ses dönüşümü implemente edilmedi" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Audio source error" msgstr "" @@ -647,6 +667,7 @@ msgstr "" msgid "Baudrate not supported by peripheral" msgstr "Baudhızı, çevre birimi tarafından desteklenmiyor" +#: ports/zephyr-cp/common-hal/zephyr_display/Display.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c msgid "Below minimum frame rate" @@ -664,20 +685,17 @@ msgstr "Bitmap boyutu ve bit başına değer uyuşmalı" msgid "Boot device must be first (interface #0)." msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/mimxrt10xx/common-hal/busio/UART.c msgid "Both RX and TX required for flow control" msgstr "Hem RX hem de TX akış kontrolü için gerekli" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c msgid "Brightness not adjustable" msgstr "Parlaklık ayarlanabilir değil" -#: shared-bindings/_bleio/UUID.c -#, c-format -msgid "Buffer + offset too small %d %d %d" -msgstr "Buffer + offset çok küçük %d %d %d" - #: ports/raspberrypi/bindings/rp2pio/StateMachine.c msgid "Buffer elements must be 4 bytes long or less" msgstr "Buffer elementleri 4 bit olmak zorunda" @@ -720,10 +738,6 @@ msgstr "" msgid "Bus pin %d is already in use" msgstr "Veriyolu pini %d kullanımda" -#: shared-bindings/_bleio/UUID.c -msgid "Byte buffer must be 16 bytes." -msgstr "Bit buffer'ı 16bit olmalı." - #: shared-bindings/aesio/aes.c msgid "CBC blocks must be multiples of 16 bytes" msgstr "CBC blokları 16 baytın katları şeklinde olmalı" @@ -775,6 +789,10 @@ msgstr "USB aygıtları şu an değiştirilemez" msgid "Cannot create a new Adapter; use _bleio.adapter;" msgstr "yeni Adaptör oluşturulamadı; _bleio.adapter kullanın;" +#: shared-module/i2cioexpander/IOExpander.c +msgid "Cannot deinitialize board IOExpander" +msgstr "" + #: shared-bindings/displayio/Bitmap.c #: shared-bindings/memorymonitor/AllocationSize.c #: shared-bindings/pulseio/PulseIn.c @@ -810,6 +828,7 @@ msgid "Cannot remount path when visible via USB." msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Cannot set value when direction is input." msgstr "Yön, giriş olduğunda değer ayarlanamıyor." @@ -860,6 +879,10 @@ msgstr "" msgid "Coordinate arrays types have different sizes" msgstr "" +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-module/usb/core/Device.c +msgid "Could not allocate DMA capable buffer" +msgstr "" + #: ports/espressif/common-hal/rclcpy/Publisher.c msgid "Could not publish to ROS topic" msgstr "" @@ -936,6 +959,7 @@ msgstr "Ekran 16 bitlik bir renk uzayına sahip olmalıdır." #: shared-bindings/busdisplay/BusDisplay.c #: shared-bindings/epaperdisplay/EPaperDisplay.c #: shared-bindings/framebufferio/FramebufferDisplay.c +#: shared-bindings/mipidsi/Display.c msgid "Display rotation must be in 90 degree increments" msgstr "Ekran dönüşü 90 derecelik artışlarla olmalıdır" @@ -944,6 +968,7 @@ msgid "Done" msgstr "Tamamlandı" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Drive mode not used when direction is input." msgstr "Yön, giriş olduğunda sürüş modu kullanılmaz." @@ -955,8 +980,17 @@ msgstr "Yukarıdaki hatanın işlenmesi sırasında başka bir hata oluştu:" msgid "ECB only operates on 16 bytes at a time" msgstr "ECB aynı anda yalnızca 16 baytla çalışır" +#: py/asmxtensa.c +msgid "ERROR: %q %q not word-aligned" +msgstr "" + +#: py/asmxtensa.c +msgid "ERROR: xtensa %q out of range" +msgstr "" + #: ports/espressif/common-hal/busio/SPI.c #: ports/espressif/common-hal/canio/CAN.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "ESP-IDF memory allocation failed" msgstr "" @@ -1003,7 +1037,7 @@ msgid "" "Failed to add service TXT record; non-string or bytes found in txt_records" msgstr "" -#: shared-module/rgbmatrix/RGBMatrix.c +#: ports/analog/common-hal/busio/UART.c shared-module/rgbmatrix/RGBMatrix.c msgid "Failed to allocate %q buffer" msgstr "" @@ -1021,10 +1055,12 @@ msgstr "" #: ports/espressif/common-hal/_bleio/Adapter.c #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: internal error" msgstr "Bağlantı kurulamadı: internal error" #: ports/nordic/common-hal/_bleio/Adapter.c +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c msgid "Failed to connect: timeout" msgstr "Bağlantı kurulamadı: timeout" @@ -1061,6 +1097,10 @@ msgstr "" msgid "Failed to release mutex, err 0x%04x" msgstr "Muteks serbest bırakılamadı, err 0x%04x" +#: ports/analog/common-hal/busio/SPI.c +msgid "Failed to set SPI Clock Mode" +msgstr "" + #: ports/zephyr-cp/common-hal/wifi/Radio.c msgid "Failed to set hostname" msgstr "" @@ -1078,7 +1118,6 @@ msgid "File exists" msgstr "Dosya var" #: shared-bindings/supervisor/__init__.c shared-module/lvfontio/OnDiskFont.c -#: shared-module/os/getenv.c msgid "File not found" msgstr "" @@ -1135,6 +1174,7 @@ msgstr "GNSS init" msgid "Generic Failure" msgstr "" +#: ports/zephyr-cp/bindings/zephyr_display/Display.c #: shared-bindings/framebufferio/FramebufferDisplay.c #: shared-module/busdisplay/BusDisplay.c #: shared-module/framebufferio/FramebufferDisplay.c @@ -1233,7 +1273,8 @@ msgstr "Dahili ses arabelleği çok küçük" msgid "Internal define error" msgstr "Dahili tanımlama hatası" -#: shared-bindings/pwmio/PWMOut.c shared-module/os/getenv.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c shared-bindings/pwmio/PWMOut.c +#: supervisor/shared/settings.c msgid "Internal error" msgstr "Dahili hata" @@ -1267,22 +1308,29 @@ msgstr "" msgid "Interrupted by output function" msgstr "" +#: ports/analog/common-hal/busio/UART.c +#: ports/analog/peripherals/max32690/max32_i2c.c +#: ports/analog/peripherals/max32690/max32_spi.c +#: ports/analog/peripherals/max32690/max32_uart.c #: ports/espressif/common-hal/_bleio/Service.c #: ports/espressif/common-hal/espulp/ULP.c #: ports/espressif/common-hal/microcontroller/Processor.c +#: ports/espressif/common-hal/mipidsi/Display.c #: ports/mimxrt10xx/common-hal/audiobusio/__init__.c #: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c #: ports/raspberrypi/bindings/picodvi/Framebuffer.c #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2040.c py/argcheck.c #: shared-bindings/digitalio/DigitalInOut.c -#: shared-bindings/epaperdisplay/EPaperDisplay.c shared-bindings/pwmio/PWMOut.c -#: shared-bindings/supervisor/__init__.c +#: shared-bindings/epaperdisplay/EPaperDisplay.c +#: shared-bindings/i2cioexpander/IOPin.c shared-bindings/mipidsi/Display.c +#: shared-bindings/pwmio/PWMOut.c shared-bindings/supervisor/__init__.c #: shared-module/aurora_epaper/aurora_framebuffer.c #: shared-module/lvfontio/OnDiskFont.c msgid "Invalid %q" msgstr "Geçersiz %q" +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c #: ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c #: shared-module/aurora_epaper/aurora_framebuffer.c msgid "Invalid %q and %q" @@ -1316,6 +1364,10 @@ msgstr "Geçersiz MAC adresi" msgid "Invalid ROS domain ID" msgstr "" +#: ports/zephyr-cp/common-hal/_bleio/Adapter.c +msgid "Invalid advertising data" +msgstr "" + #: ports/espressif/common-hal/espidf/__init__.c py/moderrno.c msgid "Invalid argument" msgstr "Geçersiz argüman" @@ -1325,17 +1377,12 @@ msgstr "Geçersiz argüman" msgid "Invalid bits per value" msgstr "Geçersiz bit başına değer" -#: shared-module/os/getenv.c -#, c-format -msgid "Invalid byte %.*s" -msgstr "" - #: ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c #, c-format msgid "Invalid data_pins[%d]" msgstr "Geçersiz veri_pini [%d]" -#: shared-module/msgpack/__init__.c +#: shared-module/msgpack/__init__.c supervisor/shared/settings.c msgid "Invalid format" msgstr "" @@ -1360,12 +1407,13 @@ msgstr "Geçersiz boyut" msgid "Invalid socket for TLS" msgstr "TLS için geçersiz soket" +#: ports/analog/common-hal/busio/SPI.c #: ports/espressif/common-hal/espidf/__init__.c #: ports/nordic/common-hal/_bleio/__init__.c msgid "Invalid state" msgstr "Geçersiz durum" -#: shared-module/os/getenv.c +#: supervisor/shared/settings.c msgid "Invalid unicode escape" msgstr "" @@ -1373,10 +1421,6 @@ msgstr "" msgid "Key must be 16, 24, or 32 bytes long" msgstr "Anahtar 16, 24 veya 32 bayt uzunluğunda olmalıdır" -#: shared-module/os/getenv.c -msgid "Key not found" -msgstr "" - #: shared-module/is31fl3741/FrameBuffer.c msgid "LED mappings must match display size" msgstr "LED eşlemeleri ekran boyutuyla eşleşmelidir" @@ -1415,6 +1459,10 @@ msgstr "" msgid "Mapping must be a tuple" msgstr "Map tuple olmalıdır" +#: py/persistentcode.c +msgid "MicroPython .mpy file; use CircuitPython mpy-cross" +msgstr "" + #: ports/raspberrypi/bindings/rp2pio/StateMachine.c #: ports/raspberrypi/common-hal/rp2pio/StateMachine.c msgid "Mismatched data size" @@ -1456,8 +1504,7 @@ msgstr "" msgid "Mount point directory missing" msgstr "" -#: ports/zephyr-cp/bindings/zephyr_serial/UART.c shared-bindings/busio/UART.c -#: shared-bindings/displayio/Group.c +#: shared-bindings/busio/UART.c shared-bindings/displayio/Group.c msgid "Must be a %q subclass." msgstr "" @@ -1504,7 +1551,7 @@ msgstr "" #: ports/stm/common-hal/busio/UART.c shared-bindings/fourwire/FourWire.c #: shared-bindings/i2cdisplaybus/I2CDisplayBus.c #: shared-bindings/paralleldisplaybus/ParallelBus.c -#: shared-module/bitbangio/SPI.c +#: shared-bindings/qspibus/QSPIBus.c shared-module/bitbangio/SPI.c msgid "No %q pin" msgstr "%q pini yok" @@ -1522,6 +1569,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "No DMA channel found" msgstr "" @@ -1636,14 +1684,10 @@ msgid "Not connected" msgstr "" #: shared-bindings/audiobusio/I2SOut.c shared-bindings/audioio/AudioOut.c -#: shared-bindings/audiopwmio/PWMAudioOut.c +#: shared-bindings/audiopwmio/PWMAudioOut.c shared-bindings/mcp4822/MCP4822.c msgid "Not playing" msgstr "" -#: shared-module/jpegio/JpegDecoder.c -msgid "Not supported JPEG standard" -msgstr "" - #: ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c #: ports/espressif/common-hal/sdioio/SDCard.c #, c-format @@ -1737,6 +1781,7 @@ msgid "Operation or feature not supported" msgstr "" #: ports/espressif/common-hal/espidf/__init__.c +#: ports/espressif/common-hal/qspibus/QSPIBus.c msgid "Operation timed out" msgstr "" @@ -1879,6 +1924,7 @@ msgid "Publishers can only be created from a parent node" msgstr "" #: shared-bindings/digitalio/DigitalInOut.c +#: shared-bindings/i2cioexpander/IOPin.c msgid "Pull not used when direction is output." msgstr "" @@ -1918,6 +1964,7 @@ msgstr "" msgid "ROS topic failed to initialize" msgstr "" +#: ports/analog/common-hal/busio/UART.c #: ports/atmel-samd/common-hal/busio/UART.c ports/cxd56/common-hal/busio/UART.c #: ports/nordic/common-hal/busio/UART.c ports/stm/common-hal/busio/UART.c msgid "RS485" @@ -2010,6 +2057,10 @@ msgstr "" msgid "SPI init error" msgstr "" +#: ports/analog/common-hal/busio/SPI.c +msgid "SPI needs MOSI, MISO, and SCK" +msgstr "" + #: ports/raspberrypi/common-hal/busio/SPI.c msgid "SPI peripheral in use" msgstr "" @@ -2055,10 +2106,13 @@ msgstr "" #: ports/espressif/common-hal/socketpool/SocketPool.c #: ports/raspberrypi/common-hal/socketpool/SocketPool.c -#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c msgid "SocketPool can only be used with wifi.radio" msgstr "" +#: ports/zephyr-cp/common-hal/socketpool/SocketPool.c +msgid "SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork" +msgstr "" + #: shared-bindings/aesio/aes.c msgid "Source and destination buffers must be the same length" msgstr "" @@ -2141,11 +2195,16 @@ msgstr "" msgid "Timeout is too long: Maximum timeout length is %d seconds" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "Timeout must be < 100 seconds" +msgstr "" + #: ports/atmel-samd/common-hal/audiobusio/I2SOut.c msgid "Too many channels in sample" msgstr "" #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Too many channels in sample." msgstr "" @@ -2184,6 +2243,10 @@ msgstr "" msgid "UART init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART needs TX & RX" +msgstr "" + #: ports/raspberrypi/common-hal/busio/UART.c msgid "UART peripheral in use" msgstr "" @@ -2192,6 +2255,14 @@ msgstr "" msgid "UART re-init" msgstr "" +#: ports/analog/common-hal/busio/UART.c +msgid "UART read error" +msgstr "" + +#: ports/analog/common-hal/busio/UART.c +msgid "UART transaction timeout" +msgstr "" + #: ports/stm/common-hal/busio/UART.c msgid "UART write" msgstr "" @@ -2216,10 +2287,6 @@ msgstr "" msgid "USB error" msgstr "" -#: shared-bindings/_bleio/UUID.c -msgid "UUID integer value must be 0-0xffff" -msgstr "" - #: shared-bindings/_bleio/UUID.c msgid "UUID string not 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'" msgstr "" @@ -2236,6 +2303,7 @@ msgstr "" #: ports/atmel-samd/common-hal/audioio/AudioOut.c #: ports/raspberrypi/common-hal/audiobusio/I2SOut.c #: ports/raspberrypi/common-hal/audiopwmio/PWMAudioOut.c +#: ports/raspberrypi/common-hal/mcp4822/MCP4822.c msgid "Unable to allocate buffers for signed conversion" msgstr "" @@ -2244,6 +2312,7 @@ msgid "Unable to allocate to the heap." msgstr "" #: ports/espressif/common-hal/busio/I2C.c +#: ports/espressif/common-hal/busio/SPI.c msgid "Unable to create lock" msgstr "" @@ -2349,6 +2418,10 @@ msgid "" "declined or ignored." msgstr "" +#: shared-module/jpegio/JpegDecoder.c +msgid "Unsupported JPEG (may be progressive)" +msgstr "" + #: shared-bindings/bitmaptools/__init__.c msgid "Unsupported colorspace" msgstr "" @@ -2371,6 +2444,13 @@ msgstr "" msgid "Update failed" msgstr "" +#: ports/zephyr-cp/common-hal/audiobusio/I2SOut.c +#: ports/zephyr-cp/common-hal/busio/I2C.c +#: ports/zephyr-cp/common-hal/busio/SPI.c +#: ports/zephyr-cp/common-hal/busio/UART.c +msgid "Use device tree to define %q devices" +msgstr "" + #: ports/espressif/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Characteristic.c #: ports/nordic/common-hal/_bleio/Descriptor.c @@ -2510,6 +2590,10 @@ msgstr "" msgid "a bytes-like object is required" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "address out of range" +msgstr "" + #: shared-bindings/i2ctarget/I2CTarget.c msgid "addresses is empty" msgstr "" @@ -2576,10 +2660,6 @@ msgstr "" msgid "array/bytes required on right side" msgstr "" -#: py/asmxtensa.c -msgid "asm overflow" -msgstr "" - #: py/compile.c msgid "async for/with outside async function" msgstr "" @@ -2793,6 +2873,10 @@ msgstr "" msgid "can't create '%q' instances" msgstr "" +#: py/objtype.c +msgid "can't create instance" +msgstr "" + #: py/compile.c msgid "can't declare nonlocal in outer code" msgstr "" @@ -2891,14 +2975,14 @@ msgstr "" msgid "cannot convert complex type" msgstr "" -#: py/objtype.c -msgid "cannot create instance" -msgstr "" - #: extmod/ulab/code/ndarray.c msgid "cannot delete array elements" msgstr "" +#: py/compile.c +msgid "cannot emit native code for this architecture" +msgstr "" + #: extmod/ulab/code/ndarray.c msgid "cannot reshape array" msgstr "" @@ -3053,7 +3137,7 @@ msgstr "" msgid "div/mod not implemented for uint" msgstr "" -#: extmod/ulab/code/numpy/create.c +#: extmod/ulab/code/numpy/create.c py/objint_longlong.c py/objint_mpz.c msgid "divide by zero" msgstr "" @@ -3152,10 +3236,6 @@ msgstr "" msgid "file write is not available" msgstr "" -#: shared-bindings/storage/__init__.c -msgid "filesystem must provide mount method" -msgstr "" - #: extmod/ulab/code/numpy/vector.c msgid "first argument must be a callable" msgstr "" @@ -3196,10 +3276,6 @@ msgstr "" msgid "float unsupported" msgstr "" -#: shared-bindings/_stage/Text.c -msgid "font must be 2048 bytes long" -msgstr "" - #: extmod/moddeflate.c msgid "format" msgstr "" @@ -3281,10 +3357,6 @@ msgstr "" msgid "generator raised StopIteration" msgstr "" -#: shared-bindings/_stage/Layer.c -msgid "graphic must be 2048 bytes long" -msgstr "" - #: extmod/modhashlib.c msgid "hash is final" msgstr "" @@ -3351,10 +3423,6 @@ msgstr "" msgid "initial values must be iterable" msgstr "" -#: shared-bindings/_bleio/Characteristic.c shared-bindings/_bleio/Descriptor.c -msgid "initial_value length is wrong" -msgstr "" - #: py/compile.c msgid "inline assembler must be a function" msgstr "" @@ -3445,6 +3513,10 @@ msgstr "" msgid "interval must be in range %s-%s" msgstr "" +#: py/emitinlinerv32.c +msgid "invalid RV32 instruction '%q'" +msgstr "" + #: py/compile.c msgid "invalid arch" msgstr "" @@ -3639,6 +3711,10 @@ msgstr "" msgid "mode must be complete, or reduced" msgstr "" +#: py/runtime.c +msgid "module '%q' has no attribute '%q'" +msgstr "" + #: py/builtinimport.c msgid "module not found" msgstr "" @@ -3687,10 +3763,6 @@ msgstr "" msgid "native code in .mpy unsupported" msgstr "" -#: py/asmthumb.c -msgid "native method too big" -msgstr "" - #: py/emitnative.c msgid "native yield" msgstr "" @@ -3712,7 +3784,7 @@ msgstr "" msgid "negative power with no float support" msgstr "" -#: py/objint_mpz.c py/runtime.c +#: py/objint_longlong.c py/objint_mpz.c py/runtime.c msgid "negative shift count" msgstr "" @@ -3789,6 +3861,10 @@ msgstr "" msgid "not supported for input types" msgstr "" +#: shared-bindings/i2cioexpander/IOExpander.c +msgid "num_pins must be 8 or 16" +msgstr "" + #: extmod/ulab/code/numpy/create.c msgid "number of points must be at least 2" msgstr "" @@ -3802,6 +3878,10 @@ msgstr "" msgid "object '%s' isn't a tuple or list" msgstr "" +#: shared-bindings/digitalio/DigitalInOutProtocol.c +msgid "object does not support DigitalInOut protocol" +msgstr "" + #: py/obj.c msgid "object doesn't support item assignment" msgstr "" @@ -4002,10 +4082,6 @@ msgstr "" msgid "pack expected %d items for packing (got %d)" msgstr "" -#: shared-bindings/_stage/Layer.c shared-bindings/_stage/Text.c -msgid "palette must be 32 bytes long" -msgstr "" - #: py/emitinlinerv32.c msgid "parameters must be registers in sequence a0 to a3" msgstr "" @@ -4063,6 +4139,10 @@ msgstr "" msgid "real and imaginary parts must be of equal length" msgstr "" +#: extmod/modre.c +msgid "regex too complex" +msgstr "" + #: py/builtinimport.c msgid "relative import" msgstr "" @@ -4072,6 +4152,10 @@ msgstr "" msgid "requested length %d but object has length %d" msgstr "" +#: py/objint_longlong.c py/parsenum.c +msgid "result overflows long long storage" +msgstr "" + #: extmod/ulab/code/ndarray_operators.c msgid "results cannot be cast to specified type" msgstr "" @@ -4336,10 +4420,6 @@ msgstr "" msgid "type takes 1 or 3 arguments" msgstr "" -#: py/objint_longlong.c -msgid "ulonglong too large" -msgstr "" - #: py/parse.c msgid "unexpected indent" msgstr "" @@ -4361,10 +4441,6 @@ msgstr "" msgid "unindent doesn't match any outer indent level" msgstr "" -#: py/emitinlinerv32.c -msgid "unknown RV32 instruction '%q'" -msgstr "" - #: py/objstr.c #, c-format msgid "unknown conversion specifier %c" @@ -4533,6 +4609,13 @@ msgstr "" msgid "zi must be of shape (n_section, 2)" msgstr "" +#, c-format +#~ msgid "Buffer + offset too small %d %d %d" +#~ msgstr "Buffer + offset çok küçük %d %d %d" + +#~ msgid "Byte buffer must be 16 bytes." +#~ msgstr "Bit buffer'ı 16bit olmalı." + #, c-format #~ msgid "%%c requires int or char" #~ msgstr "%%c int veya char tipine ihtiyaç duyar" @@ -4772,8 +4855,8 @@ msgstr "" #~ msgstr "IV %d bayt uzunluğunda olmalı" #~ msgid "" -#~ "Incompatible .mpy file. Please update all .mpy files. See http://adafru." -#~ "it/mpy-update for more info." +#~ "Incompatible .mpy file. Please update all .mpy files. See http://" +#~ "adafru.it/mpy-update for more info." #~ msgstr "" #~ "Uyumsuz .mpy dosyası. Lütfen tüm .mpy dosyalarını güncelleyin. Daha fazla " #~ "bilgi için http://adafru.it/mpy-update ." diff --git a/main.c b/main.c index 1f387b7ca3ceb..ce058cc49c4b5 100644 --- a/main.c +++ b/main.c @@ -21,6 +21,7 @@ #include "py/stackctrl.h" #include "shared/readline/readline.h" +#include "shared/runtime/gchelper.h" #include "shared/runtime/pyexec.h" #include "background.h" @@ -30,7 +31,6 @@ #include "supervisor/cpu.h" #include "supervisor/filesystem.h" #include "supervisor/port.h" -#include "supervisor/shared/cpu_regs.h" #include "supervisor/shared/reload.h" #include "supervisor/shared/safe_mode.h" #include "supervisor/shared/serial.h" @@ -43,6 +43,7 @@ #include "supervisor/shared/external_flash/external_flash.h" #include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Processor.h" #include "shared-bindings/supervisor/__init__.h" #include "shared-bindings/supervisor/Runtime.h" @@ -112,8 +113,8 @@ uint8_t value_out = 0; #endif -#if MICROPY_ENABLE_PYSTACK && CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif static void reset_devices(void) { @@ -124,23 +125,24 @@ static void reset_devices(void) { static uint8_t *_heap; static uint8_t *_pystack; +static volatile bool _vm_is_running = false; static const char line_clear[] = "\x1b[2K\x1b[0G"; #if MICROPY_ENABLE_PYSTACK || MICROPY_ENABLE_GC static uint8_t *_allocate_memory(safe_mode_t safe_mode, const char *env_key, size_t default_size, size_t *final_size) { *final_size = default_size; - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML if (safe_mode == SAFE_MODE_NONE) { mp_int_t size; - if (common_hal_os_getenv_int(env_key, &size) == GETENV_OK && size > 0) { + if (settings_get_int(env_key, &size) == SETTINGS_OK && size > 0) { *final_size = size; } } #endif uint8_t *ptr = port_malloc(*final_size, false); - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML if (ptr == NULL) { // Fallback to the build size. ptr = port_malloc(default_size, false); @@ -207,20 +209,33 @@ static void start_mp(safe_mode_t safe_mode) { // Always return to root common_hal_os_chdir("/"); + + _vm_is_running = true; } static void stop_mp(void) { + _vm_is_running = false; #if MICROPY_VFS - mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); // Unmount all heap allocated vfs mounts. - while (gc_nbytes(vfs) > 0) { - vfs = vfs->next; - } - MP_STATE_VM(vfs_mount_table) = vfs; - // The last vfs is CIRCUITPY and the root directory. - while (vfs->next != NULL) { - vfs = vfs->next; + mp_vfs_mount_t *vfs = MP_STATE_VM(vfs_mount_table); + if (vfs != NULL) { + do { + if (gc_ptr_on_heap(vfs)) { + // mp_vfs_umount will splice out an unmounted vfs from the vfs_mount_table linked list. + mp_vfs_umount(vfs->obj); + // Start over at the beginning of the list since the first entry may have been removed. + vfs = MP_STATE_VM(vfs_mount_table); + continue; + } + vfs = vfs->next; + } while (vfs != NULL); + + // The last vfs is CIRCUITPY and the root directory. + vfs = MP_STATE_VM(vfs_mount_table); + while (vfs->next != NULL) { + vfs = vfs->next; + } } MP_STATE_VM(vfs_cur) = vfs; #endif @@ -401,8 +416,13 @@ static void cleanup_after_vm(mp_obj_t exception) { // Free the heap last because other modules may reference heap memory and need to shut down. filesystem_flush(); + + // Runs finalisers while shutting down the heap. stop_mp(); + // Don't reset pins until finalisers have run. + reset_all_pins(); + // Let the workflows know we've reset in case they want to restart. supervisor_workflow_reset(); } @@ -505,6 +525,7 @@ static bool __attribute__((noinline)) run_code_py(safe_mode_t safe_mode, bool *s // Finished executing python code. Cleanup includes filesystem flush and a board reset. + _vm_is_running = false; cleanup_after_vm(_exec_result.exception); _exec_result.exception = NULL; @@ -855,6 +876,14 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { boot_output = &boot_text; #endif + // Get the base filesystem. + fs_user_mount_t *vfs = filesystem_circuitpy(); + FATFS *fs = &vfs->fatfs; + + // Allow boot.py access to CIRCUITPY, and allow writes to boot_out.txt. + // We can't use the regular flags for this, because they might get modified inside boot.py. + filesystem_set_ignore_write_protection(vfs, true); + // Write version info mp_printf(&mp_plat_print, "%s\nBoard ID:%s\n", MICROPY_FULL_VERSION_INFO, CIRCUITPY_BOARD_ID); #if CIRCUITPY_MICROCONTROLLER && COMMON_HAL_MCU_PROCESSOR_UID_LENGTH > 0 @@ -873,10 +902,6 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { #ifdef CIRCUITPY_BOOT_OUTPUT_FILE - // Get the base filesystem. - fs_user_mount_t *vfs = filesystem_circuitpy(); - FATFS *fs = &vfs->fatfs; - boot_output = NULL; #if CIRCUITPY_STATUS_BAR supervisor_status_bar_resume(); @@ -898,9 +923,6 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { // in case power is momentary or will fail shortly due to, say a low, battery. mp_hal_delay_ms(1000); - // USB isn't up, so we can write the file. - // operating at the oofatfs (f_open) layer means the usb concurrent write permission - // is not even checked! f_open(fs, &boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); UINT chars_written; f_write(&boot_output_file, boot_text.buf, boot_text.len, &chars_written); @@ -908,6 +930,9 @@ static void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) { filesystem_flush(); } #endif + + // Back to regular filesystem protections. + filesystem_set_ignore_write_protection(vfs, false); } cleanup_after_vm(_exec_result.exception); @@ -995,8 +1020,12 @@ int __attribute__((used)) main(void) { // initialise the cpu and peripherals set_safe_mode(port_init()); + // All ports need pins reset, after never-reset pins are marked in port_init(); + reset_all_pins(); + port_heap_init(); + // Turn on RX and TX LEDs if we have them. init_rxtx_leds(); @@ -1120,7 +1149,7 @@ int __attribute__((used)) main(void) { } else { skip_repl = false; } - } else if (exit_code != 0) { + } else if (exit_code != PYEXEC_NORMAL_EXIT) { break; } @@ -1136,13 +1165,7 @@ int __attribute__((used)) main(void) { void gc_collect(void) { gc_collect_start(); - // Load register values onto the stack. They get collected below with the rest of the stack. - size_t regs[SAVED_REGISTER_COUNT]; - mp_uint_t sp = cpu_get_regs_and_sp(regs); - - // This naively collects all object references from an approximate stack - // range. - gc_collect_root((void **)sp, ((mp_uint_t)port_stack_get_top() - sp) / sizeof(mp_uint_t)); + gc_helper_collect_regs_and_stack(); // This collects root pointers from the VFS mount table. Some of them may // have lost their references in the VM even though they are mounted. @@ -1179,22 +1202,22 @@ void gc_collect(void) { gc_collect_end(); } -// Ports may provide an implementation of this function if it is needed -MP_WEAK void port_gc_collect(void) { -} - size_t gc_get_max_new_split(void) { return port_heap_get_largest_free_size(); } -void NORETURN nlr_jump_fail(void *val) { +void MP_NORETURN nlr_jump_fail(void *val) { reset_into_safe_mode(SAFE_MODE_NLR_JUMP_FAIL); while (true) { } } +bool vm_is_running(void) { + return _vm_is_running; +} + #ifndef NDEBUG -static void NORETURN __fatal_error(const char *msg) { +static void MP_NORETURN __fatal_error(const char *msg) { #if CIRCUITPY_DEBUG == 0 reset_into_safe_mode(SAFE_MODE_HARD_FAULT); #endif diff --git a/mpy-cross/main.c b/mpy-cross/main.c index 989aec68bb519..add07c3d49b3e 100644 --- a/mpy-cross/main.c +++ b/mpy-cross/main.c @@ -34,13 +34,19 @@ #include "py/persistentcode.h" #include "py/runtime.h" #include "py/gc.h" -#include "py/stackctrl.h" +#include "py/parsenum.h" #include "genhdr/mpversion.h" #ifdef _WIN32 // CIRCUITPY-CHANGE #include "fmode.h" #endif +#if MICROPY_EMIT_NATIVE && MICROPY_EMIT_RV32 +#include "py/asmrv32.h" + +static asm_rv32_backend_options_t rv32_options = { 0 }; +#endif + // Command line options, with their defaults static uint emit_opt = MP_EMIT_OPT_NONE; mp_uint_t mp_verbose_flag = 0; @@ -82,13 +88,20 @@ static int compile_and_save(const char *file, const char *output_file, const cha source_name = qstr_from_str(source_file); } - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #endif mp_parse_tree_t parse_tree = mp_parse(lex, MP_PARSE_FILE_INPUT); mp_compiled_module_t cm; cm.context = m_new_obj(mp_module_context_t); + cm.arch_flags = 0; + #if MICROPY_EMIT_NATIVE && MICROPY_EMIT_RV32 + if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_RV32IMC && mp_dynamic_compiler.backend_options != NULL) { + cm.arch_flags = ((asm_rv32_backend_options_t *)mp_dynamic_compiler.backend_options)->allowed_extensions; + } + #endif + mp_compile_to_raw_code(&parse_tree, source_name, false, &cm); if ((output_file != NULL && strcmp(output_file, "-") == 0) || @@ -131,7 +144,10 @@ static int usage(char **argv) { "Target specific options:\n" "-msmall-int-bits=number : set the maximum bits used to encode a small-int\n" "-march= : set architecture for native emitter;\n" - " x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp, armv7emdp, xtensa, xtensawin, rv32imc, debug\n" + " x86, x64, armv6, armv6m, armv7m, armv7em, armv7emsp,\n" + " armv7emdp, xtensa, xtensawin, rv32imc, rv64imc, host, debug\n" + "-march-flags= : set architecture-specific flags (can be either a dec/hex/bin value or a string)\n" + " supported flags for rv32imc: zba\n" "\n" "Implementation specific options:\n", argv[0] ); @@ -212,9 +228,39 @@ static char *backslash_to_forwardslash(char *path) { return path; } -MP_NOINLINE int main_(int argc, char **argv) { - mp_stack_set_limit(40000 * (sizeof(void *) / 4)); +// This will need to be reworked in case mpy-cross needs to set more bits than +// what its small int representation allows to fit in there. +static bool parse_integer(const char *value, mp_uint_t *integer) { + assert(value && "Attempting to parse a NULL string"); + assert(integer && "Attempting to store into a NULL integer buffer"); + + size_t value_length = strlen(value); + int base = 10; + if (value_length > 2 && value[0] == '0') { + if ((value[1] | 0x20) == 'b') { + base = 2; + } else if ((value[1] | 0x20) == 'x') { + base = 16; + } else { + return false; + } + } + + bool valid = false; + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t parsed = mp_parse_num_integer(value, value_length, base, NULL); + if (mp_obj_is_small_int(parsed)) { + *integer = MP_OBJ_SMALL_INT_VALUE(parsed); + valid = true; + } + nlr_pop(); + } + return valid; +} + +MP_NOINLINE int main_(int argc, char **argv) { pre_process_options(argc, argv); char *heap = malloc(heap_size); @@ -237,11 +283,13 @@ MP_NOINLINE int main_(int argc, char **argv) { // don't support native emitter unless -march is specified mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_NONE; mp_dynamic_compiler.nlr_buf_num_regs = 0; + mp_dynamic_compiler.backend_options = NULL; const char *input_file = NULL; const char *output_file = NULL; const char *source_file = NULL; bool option_parsing_active = true; + const char *arch_flags = NULL; // parse main options for (int a = 1; a < argc; a++) { @@ -318,6 +366,9 @@ MP_NOINLINE int main_(int argc, char **argv) { } else if (strcmp(arch, "rv32imc") == 0) { mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_RV32IMC; mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_RV32I; + } else if (strcmp(arch, "rv64imc") == 0) { + mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_RV64IMC; + mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_RV64I; } else if (strcmp(arch, "debug") == 0) { mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_DEBUG; mp_dynamic_compiler.nlr_buf_num_regs = 0; @@ -331,6 +382,9 @@ MP_NOINLINE int main_(int argc, char **argv) { #elif defined(__arm__) && !defined(__thumb2__) mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_ARMV6; mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_ARM_THUMB_FP; + #elif defined(__riscv) && (__riscv_xlen == 64) + mp_dynamic_compiler.native_arch = MP_NATIVE_ARCH_RV64IMC; + mp_dynamic_compiler.nlr_buf_num_regs = MICROPY_NLR_NUM_REGS_RV64I; #else mp_printf(&mp_stderr_print, "unable to determine host architecture for -march=host\n"); exit(1); @@ -338,6 +392,8 @@ MP_NOINLINE int main_(int argc, char **argv) { } else { return usage(argv); } + } else if (strncmp(argv[a], "-march-flags=", sizeof("-march-flags=") - 1) == 0) { + arch_flags = argv[a] + sizeof("-march-flags=") - 1; } else if (strcmp(argv[a], "--") == 0) { option_parsing_active = false; } else { @@ -352,6 +408,38 @@ MP_NOINLINE int main_(int argc, char **argv) { } } + if (arch_flags && mp_dynamic_compiler.native_arch != MP_NATIVE_ARCH_NONE) { + bool processed = false; + #if MICROPY_EMIT_NATIVE && MICROPY_EMIT_RV32 + if (mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_RV32IMC) { + mp_dynamic_compiler.backend_options = (void *)&rv32_options; + mp_uint_t raw_flags = 0; + if (parse_integer(arch_flags, &raw_flags)) { + if ((raw_flags & ~((mp_uint_t)RV32_EXT_ALL)) == 0) { + rv32_options.allowed_extensions = raw_flags; + processed = true; + } + } else if (strncmp(arch_flags, "zba", sizeof("zba") - 1) == 0) { + rv32_options.allowed_extensions |= RV32_EXT_ZBA; + processed = true; + } + } + #endif + if (!processed) { + mp_printf(&mp_stderr_print, "unrecognised arch flags\n"); + exit(1); + } + } + + #if MICROPY_EMIT_NATIVE + if ((MP_STATE_VM(default_emit_opt) == MP_EMIT_OPT_NATIVE_PYTHON + || MP_STATE_VM(default_emit_opt) == MP_EMIT_OPT_VIPER) + && mp_dynamic_compiler.native_arch == MP_NATIVE_ARCH_NONE) { + mp_printf(&mp_stderr_print, "arch not specified\n"); + exit(1); + } + #endif + if (input_file == NULL) { mp_printf(&mp_stderr_print, "no input file\n"); exit(1); @@ -371,7 +459,7 @@ MP_NOINLINE int main_(int argc, char **argv) { } int main(int argc, char **argv) { - mp_stack_ctrl_init(); + mp_cstack_init_with_sp_here(40000 * (sizeof(void *) / 4)); return main_(argc, argv); } diff --git a/mpy-cross/mpconfigport.h b/mpy-cross/mpconfigport.h index edbd9f87c0558..36bcbc113941c 100644 --- a/mpy-cross/mpconfigport.h +++ b/mpy-cross/mpconfigport.h @@ -59,6 +59,7 @@ #define MICROPY_COMP_CONST_FOLDING (1) #define MICROPY_COMP_MODULE_CONST (1) #define MICROPY_COMP_CONST (1) +#define MICROPY_COMP_CONST_FLOAT (1) #define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1) #define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (1) #define MICROPY_COMP_RETURN_IF_EXPR (1) @@ -90,11 +91,12 @@ #define MICROPY_GCREGS_SETJMP (1) #endif -#define MICROPY_PY___FILE__ (0) +#define MICROPY_MODULE___FILE__ (0) #define MICROPY_PY_ARRAY (0) #define MICROPY_PY_ATTRTUPLE (0) #define MICROPY_PY_COLLECTIONS (0) -#define MICROPY_PY_MATH (0) +#define MICROPY_PY_MATH (MICROPY_COMP_CONST_FLOAT) +#define MICROPY_PY_MATH_CONSTANTS (MICROPY_COMP_CONST_FLOAT) #define MICROPY_PY_CMATH (0) #define MICROPY_PY_GC (0) #define MICROPY_PY_IO (0) @@ -102,23 +104,6 @@ // type definitions for the specific machine -#ifdef __LP64__ -typedef long mp_int_t; // must be pointer size -typedef unsigned long mp_uint_t; // must be pointer size -#elif defined(__MINGW32__) && defined(_WIN64) -#include -typedef __int64 mp_int_t; -typedef unsigned __int64 mp_uint_t; -#elif defined(_MSC_VER) && defined(_WIN64) -typedef __int64 mp_int_t; -typedef unsigned __int64 mp_uint_t; -#else -// These are definitions for machines where sizeof(int) == sizeof(void*), -// regardless for actual size. -typedef int mp_int_t; // must be pointer size -typedef unsigned int mp_uint_t; // must be pointer size -#endif - // Cannot include , as it may lead to symbol name clashes #if _FILE_OFFSET_BITS == 64 && !defined(__LP64__) typedef long long mp_off_t; @@ -143,7 +128,7 @@ typedef long mp_off_t; #ifdef _MSC_VER #define MP_ENDIANNESS_LITTLE (1) -#define NORETURN __declspec(noreturn) +#define MP_NORETURN __declspec(noreturn) #define MP_NOINLINE __declspec(noinline) #define MP_ALWAYSINLINE __forceinline #define MP_LIKELY(x) (x) diff --git a/mpy-cross/mpy_cross/__init__.py b/mpy-cross/mpy_cross/__init__.py index 91cd6f99335b3..6d7002a3b89d2 100644 --- a/mpy-cross/mpy_cross/__init__.py +++ b/mpy-cross/mpy_cross/__init__.py @@ -25,7 +25,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from __future__ import print_function import os import re import stat @@ -44,6 +43,7 @@ "NATIVE_ARCH_XTENSA": "xtensa", "NATIVE_ARCH_XTENSAWIN": "xtensawin", "NATIVE_ARCH_RV32IMC": "rv32imc", + "NATIVE_ARCH_RV64IMC": "rv64imc", } globals().update(NATIVE_ARCHS) diff --git a/mpy-cross/mpy_cross/__main__.py b/mpy-cross/mpy_cross/__main__.py index 2b6b81c333362..fe78a9e077e77 100644 --- a/mpy-cross/mpy_cross/__main__.py +++ b/mpy-cross/mpy_cross/__main__.py @@ -25,7 +25,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from __future__ import print_function import argparse import sys diff --git a/ports/analog/Makefile b/ports/analog/Makefile index 08b245c5897e3..6b86bd7039636 100644 --- a/ports/analog/Makefile +++ b/ports/analog/Makefile @@ -2,15 +2,17 @@ # # SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries # SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +# SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. # # SPDX-License-Identifier: MIT +BOARD ?= apard32690 +CROSS_COMPILE = arm-none-eabi- + # Includes mpconfigboard.mk & mpconfigport.mk, # along with numerous other shared environment makefiles. include ../../py/circuitpy_mkenv.mk -CROSS_COMPILE = arm-none-eabi- - # MCU_SERIES e.g. "max32" # MCU_VARIANT e.g. "max32690" # defined in mpconfigboard.mk @@ -19,6 +21,7 @@ MCU_SERIES_UPPER := $(shell echo $(MCU_SERIES) | tr '[:lower:]' '[:upper:]') MCU_VARIANT_LOWER := $(shell echo $(MCU_VARIANT) | tr '[:upper:]' '[:lower:]') MCU_VARIANT_UPPER := $(shell echo $(MCU_VARIANT) | tr '[:lower:]' '[:upper:]') + # ******************************************************************************* #### MSDK INCLUDES #### # Necessary for msdk makefiles @@ -58,6 +61,7 @@ DIE_TYPE=me18 endif PERIPH_SRC = $(ADI_PERIPH)/Source +PERIPH_INC = $(ADI_PERIPH)/Include/$(MCU_VARIANT_UPPER) INC += -I. INC += -I../.. @@ -74,7 +78,7 @@ INC += \ -I$(TOP)/lib/cmsis/inc \ -I$(CMSIS_ROOT)/Include \ -I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Include \ - -I$(ADI_PERIPH)/Include/$(MCU_VARIANT_UPPER) \ + -I$(PERIPH_INC) \ -I$(PERIPH_SRC)/SYS \ -I$(PERIPH_SRC)/CTB \ -I$(PERIPH_SRC)/DMA \ @@ -83,7 +87,10 @@ INC += \ -I$(PERIPH_SRC)/ICC \ -I$(PERIPH_SRC)/TMR \ -I$(PERIPH_SRC)/RTC \ - -I$(PERIPH_SRC)/UART + -I$(PERIPH_SRC)/UART \ + -I$(PERIPH_SRC)/TRNG \ + -I$(PERIPH_SRC)/I2C \ + -I$(PERIPH_SRC)/SPI INC += -I$(CMSIS_ROOT)/Device/Maxim/$(MCU_VARIANT_UPPER)/Source/GCC @@ -116,13 +123,22 @@ SRC_MAX32 += \ $(PERIPH_SRC)/TMR/tmr_$(DIE_TYPE).c \ $(PERIPH_SRC)/UART/uart_common.c \ $(PERIPH_SRC)/UART/uart_$(DIE_TYPE).c \ - $(PERIPH_SRC)/UART/uart_revb.c + $(PERIPH_SRC)/UART/uart_revb.c \ + $(PERIPH_SRC)/TRNG/trng_revb.c \ + $(PERIPH_SRC)/TRNG/trng_$(DIE_TYPE).c \ + $(PERIPH_SRC)/I2C/i2c_$(DIE_TYPE).c \ + $(PERIPH_SRC)/I2C/i2c_reva.c \ + $(PERIPH_SRC)/SPI/spi_$(DIE_TYPE).c \ + $(PERIPH_SRC)/SPI/spi_reva1.c SRC_C += $(SRC_MAX32) \ boards/$(BOARD)/board.c \ boards/$(BOARD)/pins.c \ peripherals/$(MCU_VARIANT_LOWER)/pins.c \ - peripherals/$(MCU_VARIANT_LOWER)/gpios.c + peripherals/$(MCU_VARIANT_LOWER)/gpios.c \ + peripherals/$(MCU_VARIANT_LOWER)/max32_uart.c \ + peripherals/$(MCU_VARIANT_LOWER)/max32_i2c.c \ + peripherals/$(MCU_VARIANT_LOWER)/max32_spi.c # ******************************************************************************* ### Compiler & Linker Flags ### @@ -138,8 +154,10 @@ LINKERFILE = linking/$(MCU_VARIANT_LOWER)_cktpy.ld LDFLAGS += -nostartfiles -specs=nano.specs endif -SRC_S_UPPER = supervisor/shared/cpu_regs.S SRC_S += $(STARTUPFILE) +SRC_S += shared/runtime/gchelper_thumb2.s + +SRC_C += shared/runtime/gchelper_native.c # Needed to compile some MAX32 headers CFLAGS += -D$(MCU_VARIANT_UPPER) \ @@ -236,7 +254,6 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) # List of sources for qstr extraction @@ -263,11 +280,16 @@ flash-msdk: -f interface/cmsis-dap.cfg -f target/$(MCU_VARIANT_LOWER).cfg \ -c "program $(BUILD)/firmware.elf verify; init; reset; exit" +flash-openocd-jlink: + $(OPENOCD) -s $(OPENOCD_SCRIPTS) \ + -f interface/jlink.cfg -f target/$(MCU_VARIANT_LOWER).cfg \ + -c "program $(BUILD)/firmware.elf verify; init; reset; exit" + # flash target using JLink JLINK_DEVICE = $(MCU_VARIANT_LOWER) JLINKEXE ?= JLink.exe -JLINKEXE += -if SWD -device ${JLINK_DEVICE} -speed 10000 +JLINKEXE += -if SWD -device ${JLINK_DEVICE} -speed 4000 COMMAND_FILE := tools/flash_max32.jlink flash-jlink: $(BUILD)/firmware.bin diff --git a/ports/analog/background.c b/ports/analog/background.c index ffad007ffa51c..ad840c6dcdabc 100644 --- a/ports/analog/background.c +++ b/ports/analog/background.c @@ -19,19 +19,13 @@ extern const mxc_gpio_cfg_t led_pin[]; extern const int num_leds; /** NOTE: ALL "ticks" refer to a 1/1024 s period */ -static int status_led_ticks = 0; +static int status_ticks = 0; // This function is where port-specific background // tasks should be performed // Execute port specific actions during background tick. Only if ticks are enabled. void port_background_tick(void) { - status_led_ticks++; - - // Set an LED approx. 1/s - if (status_led_ticks > 1024) { - MXC_GPIO_OutToggle(led_pin[2].port, led_pin[2].mask); - status_led_ticks = 0; - } + status_ticks++; } // Execute port specific actions during background tasks. This is before the diff --git a/ports/analog/boards/apard32690/mpconfigboard.mk b/ports/analog/boards/apard32690/mpconfigboard.mk index 7cc54ccfc6dd0..0e62f25f3f859 100644 --- a/ports/analog/boards/apard32690/mpconfigboard.mk +++ b/ports/analog/boards/apard32690/mpconfigboard.mk @@ -19,7 +19,6 @@ USB_VID=0x0456 USB_PID=0x003C USB_MANUFACTURER="Analog Devices, Inc." USB_PRODUCT="MAX32690 APARD" -USB_HIGHSPEED=1 # NOTE: MAX32 devices do not support IN/OUT pairs on the same EP USB_NUM_ENDPOINT_PAIRS=12 diff --git a/ports/analog/boards/max32690evkit/mpconfigboard.mk b/ports/analog/boards/max32690evkit/mpconfigboard.mk index 61413216d8175..a97bc9ddacd0b 100644 --- a/ports/analog/boards/max32690evkit/mpconfigboard.mk +++ b/ports/analog/boards/max32690evkit/mpconfigboard.mk @@ -19,7 +19,6 @@ USB_VID=0x0456 USB_PID=0x003D USB_MANUFACTURER="Analog Devices, Inc." USB_PRODUCT="MAX32690 EvKit" -USB_HIGHSPEED=1 # NOTE: MAX32 devices do not support IN/OUT pairs on the same EP USB_NUM_ENDPOINT_PAIRS=12 diff --git a/ports/analog/common-hal/busio/I2C.c b/ports/analog/common-hal/busio/I2C.c new file mode 100644 index 0000000000000..7ebe721b3f427 --- /dev/null +++ b/ports/analog/common-hal/busio/I2C.c @@ -0,0 +1,256 @@ +/* + * This file is part of Adafruit for EFR32 project + * + * The MIT License (MIT) + * + * Copyright 2023 Silicon Laboratories Inc. www.silabs.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/busio/I2C.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "max32_port.h" + +#define I2C_PRIORITY 1 + +// Set each bit to indicate an active I2c +static uint8_t i2c_active = 0; +static volatile int i2c_err; + +// I2C struct for configuring GPIO pins +extern const mxc_gpio_cfg_t i2c_maps[NUM_I2C]; + +// Construct I2C protocol, this function init i2c peripheral +void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, + const mcu_pin_obj_t *scl, + const mcu_pin_obj_t *sda, + uint32_t frequency, uint32_t timeout) { + // Check for NULL Pointers && valid I2C settings + assert(self); + + /* NOTE: The validate_obj_is_free_pin() calls in shared-bindings/busio/I2C.c + will ensure that scl and sda are both pins and cannot be null + ref: https://github.com/adafruit/circuitpython/pull/10413 + */ + + // Assign I2C ID based on pins + int i2c_id = pinsToI2c(sda, scl); + if (i2c_id == -1) { + return; + } else { + self->i2c_id = i2c_id; + self->i2c_regs = MXC_I2C_GET_I2C(i2c_id); + } + + // Check for valid I2C controller + assert((self->i2c_id >= 0) && (self->i2c_id < NUM_I2C)); + assert(!(i2c_active & (1 << self->i2c_id))); + + // Attach I2C pins + self->sda = sda; + self->scl = scl; + common_hal_mcu_pin_claim(self->sda); + common_hal_mcu_pin_claim(self->scl); + + // Clear all flags + MXC_I2C_ClearFlags(self->i2c_regs, 0xFFFFFF, 0xFFFFFF); + + // Init as master, no slave address + MXC_I2C_Shutdown(self->i2c_regs); + MXC_I2C_Init(self->i2c_regs, 1, 0); + + // Set frequency arg (CircuitPython shared-bindings validate) + MXC_I2C_SetFrequency(self->i2c_regs, frequency); + + // Indicate to this module that the I2C is active + i2c_active |= (1 << self->i2c_id); + + // Set the timeout to a default value + if (timeout > 100) { + self->timeout = 1; + } else { + self->timeout = timeout; + } + + return; +} + +// Never reset I2C obj when reload +void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { + common_hal_never_reset_pin(self->sda); + common_hal_never_reset_pin(self->scl); +} + +// Check I2C status, deinited or not +bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) { + return self->sda == NULL; +} + +// Deinit i2c obj, reset I2C pin +void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { + MXC_I2C_Shutdown(self->i2c_regs); + + common_hal_reset_pin(self->sda); + common_hal_reset_pin(self->scl); + + self->sda = NULL; + self->scl = NULL; +} + +// Probe device in I2C bus +bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { + uint32_t int_fl0; + bool ret = 0; + + // If not in Master mode, error out (can happen in some error conditions) + if (!(self->i2c_regs->ctrl & MXC_F_I2C_CTRL_MST_MODE)) { + return false; + } + + // Clear FIFOs & all interrupt flags + MXC_I2C_ClearRXFIFO(self->i2c_regs); + MXC_I2C_ClearTXFIFO(self->i2c_regs); + MXC_I2C_ClearFlags(self->i2c_regs, 0xFFFFFF, 0xFFFFFF); + + // Pre-load target address into transmit FIFO + addr = (addr << 1); + self->i2c_regs->fifo = addr; + + // Set start bit & wait for it to clear + MXC_I2C_Start(self->i2c_regs); + + // wait for ACK/NACK + while (!(self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_ADDR_ACK) && + !(self->i2c_regs->intfl0 & MXC_F_I2C_INTFL0_ADDR_NACK_ERR)) { + } + + // Save interrupt flags for ACK/NACK checking + int_fl0 = self->i2c_regs->intfl0; + + // Set / Wait for stop + MXC_I2C_Stop(self->i2c_regs); + + // Wait for controller not busy, then clear flags + while (self->i2c_regs->status & MXC_F_I2C_STATUS_BUSY) { + ; + } + MXC_I2C_ClearFlags(self->i2c_regs, 0xFFFFFF, 0xFFFFFF); + + if (int_fl0 & MXC_F_I2C_INTFL0_ADDR_ACK) { + ret = true; + } else { + ret = false; + } + return ret; +} + +// Lock I2C bus +bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { + + if (self->i2c_regs->status & MXC_F_I2C_STATUS_BUSY) { + return false; + } else { + self->has_lock = true; + return true; + } +} + +// Check I2C lock status +bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { + return self->has_lock; +} + +// Unlock I2C bus +void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { + self->has_lock = false; +} + +// Write data to the device selected by address +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, + const uint8_t *data, size_t len) { + + int ret; + mxc_i2c_req_t wr_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_buf = (uint8_t *)data, + .tx_len = len, + .rx_buf = NULL, + .rx_len = 0, + .callback = NULL + }; + ret = MXC_I2C_MasterTransaction(&wr_req); + if (ret) { + return -MP_EIO; + } + + return 0; +} + +// Read into buffer from the device selected by address +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, + uint16_t addr, + uint8_t *data, size_t len) { + + int ret; + mxc_i2c_req_t rd_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_buf = NULL, + .tx_len = 0, + .rx_buf = data, + .rx_len = len, + .callback = NULL + }; + ret = MXC_I2C_MasterTransaction(&rd_req); + if (ret) { + // Return I/O error + return -MP_EIO; + } + + return 0; +} + +// Write the bytes from out_data to the device selected by address +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, + uint8_t *out_data, size_t out_len, + uint8_t *in_data, size_t in_len) { + + int ret; + mxc_i2c_req_t wr_rd_req = { + .addr = addr, + .i2c = self->i2c_regs, + .tx_buf = out_data, + .tx_len = out_len, + .rx_buf = in_data, + .rx_len = in_len, + .callback = NULL + }; + ret = MXC_I2C_MasterTransaction(&wr_rd_req); + if (ret) { + return -MP_EIO; + } + + return 0; +} diff --git a/ports/analog/common-hal/busio/I2C.h b/ports/analog/common-hal/busio/I2C.h new file mode 100644 index 0000000000000..55c230ee2f97f --- /dev/null +++ b/ports/analog/common-hal/busio/I2C.h @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" + +// HAL-specific +#include "i2c.h" +#include "gpio.h" + +// Define a struct for what BUSIO.I2C should carry +typedef struct { + mp_obj_base_t base; + + int i2c_id; + mxc_i2c_regs_t *i2c_regs; + const mcu_pin_obj_t *scl; + const mcu_pin_obj_t *sda; + const int frequency; + + uint32_t timeout; + bool has_lock; +} busio_i2c_obj_t; diff --git a/ports/analog/common-hal/busio/SPI.c b/ports/analog/common-hal/busio/SPI.c new file mode 100644 index 0000000000000..de3856b23b3f1 --- /dev/null +++ b/ports/analog/common-hal/busio/SPI.c @@ -0,0 +1,321 @@ +/* + * This file is part of Adafruit for EFR32 project + * + * The MIT License (MIT) + * + * Copyright 2023 Silicon Laboratories Inc. www.silabs.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "shared-bindings/busio/SPI.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/__init__.h" +#include "supervisor/board.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "max32_port.h" +#include "spi_reva1.h" + +// Note that any bugs introduced in this file can cause crashes +// at startupfor chips using external SPI flash. + +#define SPI_PRIORITY 1 + +typedef enum { + SPI_FREE = 0, + SPI_BUSY, +} spi_status_t; + +// Set each bit to indicate an active SPI +static uint8_t spi_active = 0; +static spi_status_t spi_status[NUM_SPI]; +static volatile int spi_err; + +// Construct SPI protocol, this function init SPI peripheral +void common_hal_busio_spi_construct(busio_spi_obj_t *self, + const mcu_pin_obj_t *sck, + const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *miso, + bool half_duplex) { + int err = 0; + + // Check for NULL Pointer + assert(self); + + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + + // Assign SPI ID based on pins + int spi_id = pinsToSpi(mosi, miso, sck); + if (spi_id == -1) { + return; + } else { + self->spi_id = spi_id; + self->spi_regs = MXC_SPI_GET_SPI(spi_id); + } + + // Other pins default to true + mxc_spi_pins_t spi_pins = { + .clock = TRUE, + .mosi = TRUE, + .miso = TRUE, + .ss0 = FALSE, + .ss1 = FALSE, + .ss2 = FALSE, + .vddioh = true, + .drvstr = MXC_GPIO_DRVSTR_0 + }; + + assert((self->spi_id >= 0) && (self->spi_id < NUM_SPI)); + + // Init SPI controller + if ((mosi != NULL) && (miso != NULL) && (sck != NULL)) { + // spi, mastermode, quadModeUsed, numSubs, ssPolarity, frequency + err = MXC_SPI_Init(self->spi_regs, MXC_SPI_TYPE_CONTROLLER, MXC_SPI_INTERFACE_STANDARD, + 1, 0x01, 1000000, spi_pins); + MXC_GPIO_SetVSSEL(MXC_GPIO_GET_GPIO(sck->port), MXC_GPIO_VSSEL_VDDIOH, (sck->mask | miso->mask | mosi->mask | MXC_GPIO_PIN_0)); + if (err) { + // NOTE: Reuse existing messages from locales/circuitpython.pot to save space + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_SPI); + } + } else { + mp_raise_NotImplementedError(MP_ERROR_TEXT("SPI needs MOSI, MISO, and SCK")); + } + + // Attach SPI pins + self->mosi = mosi; + self->miso = miso; + self->sck = sck; + common_hal_mcu_pin_claim(self->mosi); + common_hal_mcu_pin_claim(self->miso); + common_hal_mcu_pin_claim(self->sck); + + // Indicate to this module that the SPI is active + spi_active |= (1 << self->spi_id); + + return; +} + +// Never reset SPI when reload +void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { + common_hal_never_reset_pin(self->mosi); + common_hal_never_reset_pin(self->miso); + common_hal_never_reset_pin(self->sck); + common_hal_never_reset_pin(self->nss); +} + +// Check SPI status, deinited or not +bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { + return self->sck == NULL; +} + +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->sck = NULL; +} + +// Deinit SPI obj +void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { + + MXC_SPI_Shutdown(self->spi_regs); + common_hal_reset_pin(self->mosi); + common_hal_reset_pin(self->miso); + common_hal_reset_pin(self->sck); + common_hal_reset_pin(self->nss); + + self->mosi = NULL; + self->miso = NULL; + self->nss = NULL; + + common_hal_busio_spi_mark_deinit(self); +} + +// Configures the SPI bus. The SPI object must be locked. +bool common_hal_busio_spi_configure(busio_spi_obj_t *self, + uint32_t baudrate, + uint8_t polarity, + uint8_t phase, + uint8_t bits) { + + mxc_spi_clkmode_t clk_mode; + int ret; + + self->baudrate = baudrate; + self->polarity = polarity; + self->phase = phase; + self->bits = bits; + + switch ((polarity << 1) | (phase)) { + case 0b00: + clk_mode = MXC_SPI_CLKMODE_0; + break; + case 0b01: + clk_mode = MXC_SPI_CLKMODE_1; + break; + case 0b10: + clk_mode = MXC_SPI_CLKMODE_2; + break; + case 0b11: + clk_mode = MXC_SPI_CLKMODE_3; + break; + default: + // should not be reachable; validated in shared-bindings/busio/SPI.c + return false; + } + + ret = MXC_SPI_SetFrequency(self->spi_regs, baudrate); + if (ret) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q out of range"), MP_QSTR_baudrate); + return false; + } + ret = MXC_SPI_SetDataSize(self->spi_regs, bits); + if (ret == E_BAD_PARAM) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q out of range"), MP_QSTR_bits); + return false; + } else if (ret == E_BAD_STATE) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Invalid state")); + } + ret = MXC_SPI_SetMode(self->spi_regs, clk_mode); + if (ret) { + mp_raise_ValueError(MP_ERROR_TEXT("Failed to set SPI Clock Mode")); + return false; + } + return true; +} + +// Lock SPI bus +bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { + if (spi_status[self->spi_id] != SPI_BUSY) { + self->has_lock = true; + return true; + } else { + return false; + } +} + +// Check SPI lock status +bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { + return self->has_lock; +} + +// Unlock SPI bus +void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { + self->has_lock = false; +} + +// Write the data contained in buffer +bool common_hal_busio_spi_write(busio_spi_obj_t *self, + const uint8_t *data, + size_t len) { + int ret = 0; + + mxc_spi_req_t wr_req = { + .spi = self->spi_regs, + .ssIdx = 0, + .txCnt = 0, + .rxCnt = 0, + .txData = (uint8_t *)data, + .txLen = len, + .rxData = NULL, + .rxLen = 0, + .ssDeassert = 1, + .completeCB = NULL, + .txDummyValue = 0xFF, + }; + ret = MXC_SPI_MasterTransaction(&wr_req); + if (ret) { + return false; + } else { + return true; + } +} + +// Read data into buffer +bool common_hal_busio_spi_read(busio_spi_obj_t *self, + uint8_t *data, size_t len, + uint8_t write_value) { + + int ret = 0; + + mxc_spi_req_t rd_req = { + .spi = self->spi_regs, + .ssIdx = 0, + .txCnt = 0, + .rxCnt = 0, + .txData = NULL, + .txLen = len, + .rxData = data, + .rxLen = len, + .ssDeassert = 1, + .completeCB = NULL, + .txDummyValue = write_value, + }; + ret = MXC_SPI_MasterTransaction(&rd_req); + if (ret) { + return false; + } else { + return true; + } +} + +// Write out the data in data_out +// while simultaneously reading data into data_in +bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, + const uint8_t *data_out, + uint8_t *data_in, + size_t len) { + + int ret = 0; + + mxc_spi_req_t rd_req = { + .spi = self->spi_regs, + .ssIdx = 0, + .txCnt = 0, + .rxCnt = 0, + .txData = (uint8_t *)data_out, + .txLen = len, + .rxData = data_in, + .rxLen = len, + .ssDeassert = 1, + .completeCB = NULL, + .txDummyValue = 0xFF, + }; + ret = MXC_SPI_MasterTransaction(&rd_req); + if (ret) { + return false; + } else { + return true; + } +} + +// Get SPI baudrate +uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) { + return self->baudrate; +} + +// Get SPI phase +uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) { + return self->phase; +} + +// Get SPI polarity +uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { + return self->polarity; +} diff --git a/ports/analog/common-hal/busio/SPI.h b/ports/analog/common-hal/busio/SPI.h new file mode 100644 index 0000000000000..d10601876fb5e --- /dev/null +++ b/ports/analog/common-hal/busio/SPI.h @@ -0,0 +1,41 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#ifndef MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H +#define MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" + +// HAL-specific +#include "spi.h" + +// Define a struct for what BUSIO.SPI should carry +typedef struct { + mp_obj_base_t base; + + // Spi regs & pins + int spi_id; + mxc_spi_regs_t *spi_regs; + const mcu_pin_obj_t *sck; + const mcu_pin_obj_t *mosi; + const mcu_pin_obj_t *miso; + const mcu_pin_obj_t *nss; + + // Configuration + uint32_t baudrate; + uint16_t prescaler; + uint8_t polarity; + uint8_t phase; + uint8_t bits; + + // Synch data + bool has_lock; +} busio_spi_obj_t; + +void spi_reset(void); + +#endif // MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_SPI_H diff --git a/ports/analog/common-hal/busio/UART.c b/ports/analog/common-hal/busio/UART.c new file mode 100644 index 0000000000000..be7851f52a21c --- /dev/null +++ b/ports/analog/common-hal/busio/UART.c @@ -0,0 +1,460 @@ +/* + * This file is part of Adafruit for EFR32 project + * + * The MIT License (MIT) + * + * Copyright 2023 Silicon Laboratories Inc. www.silabs.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#if CIRCUITPY_BUSIO_UART + +#include "mpconfigport.h" +#include "supervisor/shared/tick.h" + +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/busio/UART.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared/readline/readline.h" +#include "shared/runtime/interrupt_char.h" + +#include "py/gc.h" +#include "py/ringbuf.h" +#include "py/mperrno.h" +#include "py/mpprint.h" +#include "py/runtime.h" + +#include "max32_port.h" +#include "UART.h" +#include "nvic_table.h" + +// UART IRQ Priority +#define UART_PRIORITY 1 + +typedef enum { + UART_9600 = 9600, + UART_14400 = 14400, + UART_19200 = 19200, + UART_38400 = 38400, + UART_57600 = 57600, + UART_115200 = 115200, + UART_230400 = 230400, + UART_460800 = 460800, + UART_921600 = 921600, +} uart_valid_baudrates; + +typedef enum { + UART_FREE = 0, + UART_BUSY, + UART_NEVER_RESET, +} uart_status_t; + +static uint32_t timeout_ms = 0; + +// Set each bit to indicate an active UART +// will be checked by ISR Handler for which ones to call +static uint8_t uarts_active = 0; +static uart_status_t uart_status[NUM_UARTS]; +static volatile int uart_err; +static uint8_t uart_never_reset_mask = 0; +static busio_uart_obj_t *context; + +static bool isValidBaudrate(uint32_t baudrate) { + switch (baudrate) { + case UART_9600: + return true; + break; + case UART_14400: + return true; + break; + case UART_19200: + return true; + break; + case UART_38400: + return true; + break; + case UART_57600: + return true; + break; + case UART_115200: + return true; + break; + case UART_230400: + return true; + break; + case UART_460800: + return true; + break; + case UART_921600: + return true; + break; + default: + return false; + break; + } +} + +static mxc_uart_parity_t convertParity(busio_uart_parity_t busio_parity) { + switch (busio_parity) { + case BUSIO_UART_PARITY_NONE: + return MXC_UART_PARITY_DISABLE; + case BUSIO_UART_PARITY_EVEN: + return MXC_UART_PARITY_EVEN_0; + case BUSIO_UART_PARITY_ODD: + return MXC_UART_PARITY_ODD_0; + default: + // not reachable due to validation in shared-bindings/busio/SPI.c + return MXC_UART_PARITY_DISABLE; + } +} + +void uart_isr(void) { + for (int i = 0; i < NUM_UARTS; i++) { + if (uarts_active & (1 << i)) { + MXC_UART_AsyncHandler(MXC_UART_GET_UART(i)); + } + } +} + +// Callback gets called when AsyncRequest is COMPLETE +// (e.g. txLen == txCnt) +static volatile void uartCallback(mxc_uart_req_t *req, int error) { + uart_status[MXC_UART_GET_IDX(req->uart)] = UART_FREE; + uart_err = error; + + MXC_SYS_Crit_Enter(); + ringbuf_put_n(context->ringbuf, req->rxData, req->rxLen); + MXC_SYS_Crit_Exit(); +} + +// Construct an underlying UART object. +void common_hal_busio_uart_construct(busio_uart_obj_t *self, + const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx, + const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts, + const mcu_pin_obj_t *rs485_dir, bool rs485_invert, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, + mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer, + bool sigint_enabled) { + int err, temp; + + // Check for NULL Pointers && valid UART settings + assert(self); + + // Assign UART ID based on pins + temp = pinsToUart(tx, rx); + if (temp == -1) { + // Error will be indicated by pinsToUart(tx, rx) function + return; + } else { + self->uart_id = temp; + self->uart_regs = MXC_UART_GET_UART(temp); + } + assert((self->uart_id >= 0) && (self->uart_id < NUM_UARTS)); + + // Check for size of ringbuffer, desire powers of 2 + // At least use 1 byte if no size is given + assert((receiver_buffer_size & (receiver_buffer_size - 1)) == 0); + if (receiver_buffer_size == 0) { + receiver_buffer_size += 1; + } + + // Indicate RS485 not implemented + if ((rs485_dir != NULL) || (rs485_invert)) { + mp_raise_NotImplementedError(MP_ERROR_TEXT("RS485")); + } + + if ((rx != NULL) && (tx != NULL)) { + err = MXC_UART_Init(self->uart_regs, baudrate, MXC_UART_IBRO_CLK); + if (err != E_NO_ERROR) { + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_UART); + } + + // attach & configure pins + self->tx_pin = tx; + self->rx_pin = rx; + common_hal_mcu_pin_claim(self->tx_pin); + common_hal_mcu_pin_claim(self->rx_pin); + } else { + mp_raise_NotImplementedError(MP_ERROR_TEXT("UART needs TX & RX")); + } + + if ((cts) && (rts)) { + MXC_UART_SetFlowCtrl(self->uart_regs, MXC_UART_FLOW_EN, 8); + self->cts_pin = cts; + self->rts_pin = rts; + common_hal_mcu_pin_claim(self->cts_pin); + common_hal_mcu_pin_claim(self->rts_pin); + } else if (cts || rts) { + mp_raise_ValueError(MP_ERROR_TEXT("Both RX and TX required for flow control")); + } + + // Set stop bits & data size + assert((stop == 1) || (stop == 2)); + mp_arg_validate_int(bits, 8, MP_QSTR_bits); + MXC_UART_SetDataSize(self->uart_regs, bits); + MXC_UART_SetStopBits(self->uart_regs, stop); + + // Set parity + MXC_UART_SetParity(self->uart_regs, convertParity(parity)); + + // attach UART parameters + self->stop_bits = stop; // must be 1 or 2 + self->bits = bits; + self->parity = parity; + self->baudrate = baudrate; + self->error = E_NO_ERROR; + + // Indicate to this module that the UART is active + uarts_active |= (1 << self->uart_id); + + // Set the timeout to a default value + if (((timeout < 0.0) || (timeout > 100.0))) { + self->timeout = 1.0; + } else { + self->timeout = timeout; + } + + // Initialize ringbuffer + if (receiver_buffer == NULL) { + self->ringbuf = m_malloc_without_collect(receiver_buffer_size); + if (!ringbuf_alloc(self->ringbuf, receiver_buffer_size)) { + m_malloc_fail(receiver_buffer_size); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to allocate %q buffer"), + MP_QSTR_UART); + } + } else { + if (!(ringbuf_init(self->ringbuf, receiver_buffer, receiver_buffer_size))) { + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("Failed to allocate %q buffer"), + MP_QSTR_UART); + } + } + + context = self; + + // Setup UART interrupt + NVIC_ClearPendingIRQ(MXC_UART_GET_IRQ(self->uart_id)); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + NVIC_SetPriority(MXC_UART_GET_IRQ(self->uart_id), UART_PRIORITY); + NVIC_SetVector(MXC_UART_GET_IRQ(self->uart_id), (uint32_t)uart_isr); + + NVIC_EnableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + + return; +} + +void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { + assert(self); + + if (!common_hal_busio_uart_deinited(self)) { + // First disable the ISR to avoid pre-emption + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + + // Shutdown the UART Controller + MXC_UART_Shutdown(self->uart_regs); + self->error = E_UNINITIALIZED; + + assert(self->rx_pin && self->tx_pin); + reset_pin_number(self->rx_pin->port, self->rx_pin->mask); + reset_pin_number(self->tx_pin->port, self->tx_pin->mask); + + if (self->cts_pin && self->rts_pin) { + reset_pin_number(self->cts_pin->port, self->cts_pin->mask); + reset_pin_number(self->rts_pin->port, self->rts_pin->mask); + } + + self->tx_pin = NULL; + self->rx_pin = NULL; + self->cts_pin = NULL; + self->rts_pin = NULL; + + ringbuf_deinit(self->ringbuf); + + // Indicate to this module that the UART is not active + uarts_active &= ~(1 << self->uart_id); + } +} + +bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { + if (uarts_active & (1 << self->uart_id)) { + return false; + } else { + return true; + }; +} + +// Read characters. len is in characters. +size_t common_hal_busio_uart_read(busio_uart_obj_t *self, + uint8_t *data, size_t len, int *errcode) { + int err; + uint32_t start_time = 0; + static size_t bytes_remaining; + + // Setup globals & status tracking + uart_err = E_NO_ERROR; + uarts_active |= (1 << self->uart_id); + uart_status[self->uart_id] = UART_BUSY; + bytes_remaining = len; + + mxc_uart_req_t uart_rd_req; + uart_rd_req.rxCnt = 0; + uart_rd_req.txCnt = 0; + uart_rd_req.rxData = data; + uart_rd_req.txData = NULL; + uart_rd_req.rxLen = bytes_remaining; + uart_rd_req.txLen = 0; + uart_rd_req.uart = self->uart_regs; + uart_rd_req.callback = (void *)uartCallback; + + // Initiate the read transaction + start_time = supervisor_ticks_ms64(); + err = MXC_UART_TransactionAsync(&uart_rd_req); + if (err != E_NO_ERROR) { + *errcode = err; + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("UART read error")); + } + + // Wait for transaction completion or timeout + while ((uart_status[self->uart_id] != UART_FREE) && + (supervisor_ticks_ms64() - start_time < (self->timeout * 1000))) { + } + + // If the timeout gets hit, abort and error out + if (uart_status[self->uart_id] != UART_FREE) { + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_RuntimeError(MP_ERROR_TEXT("UART transaction timeout")); + } + // Check for errors from the callback + else if (uart_err != E_NO_ERROR) { + mp_raise_RuntimeError(MP_ERROR_TEXT("UART read error")); + MXC_UART_AbortAsync(self->uart_regs); + } + + // Copy the data from the ringbuf (or return error) + MXC_SYS_Crit_Enter(); + err = ringbuf_get_n(context->ringbuf, data, len); + MXC_SYS_Crit_Exit(); + + return err; +} + +// Write characters. len is in characters NOT bytes! +// This function blocks until the timeout finishes +size_t common_hal_busio_uart_write(busio_uart_obj_t *self, + const uint8_t *data, size_t len, int *errcode) { + int err; + static size_t bytes_remaining; + + // Setup globals & status tracking + uart_err = E_NO_ERROR; + uarts_active |= (1 << self->uart_id); + uart_status[self->uart_id] = UART_BUSY; + bytes_remaining = len; + + mxc_uart_req_t uart_wr_req = {}; + + // Setup transaction + uart_wr_req.rxCnt = 0; + uart_wr_req.txCnt = 0; + uart_wr_req.rxData = NULL; + uart_wr_req.txData = data; + uart_wr_req.txLen = bytes_remaining; + uart_wr_req.rxLen = 0; + uart_wr_req.uart = self->uart_regs; + uart_wr_req.callback = (void *)uartCallback; + + // Start the transaction + err = MXC_UART_TransactionAsync(&uart_wr_req); + if (err != E_NO_ERROR) { + *errcode = err; + MXC_UART_AbortAsync(self->uart_regs); + NVIC_DisableIRQ(MXC_UART_GET_IRQ(self->uart_id)); + mp_raise_ValueError(MP_ERROR_TEXT("All UART peripherals are in use")); + } + + // Wait for transaction completion + while (uart_status[self->uart_id] != UART_FREE) { + // Call the handler and abort if errors + uart_err = MXC_UART_AsyncHandler(self->uart_regs); + if (uart_err != E_NO_ERROR) { + MXC_UART_AbortAsync(self->uart_regs); + } + } + // Check for errors from the callback + if (uart_err != E_NO_ERROR) { + MXC_UART_AbortAsync(self->uart_regs); + } + + return len; +} + +uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { + return self->baudrate; +} + +// Validate baudrate +void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) { + if (isValidBaudrate(baudrate)) { + self->baudrate = baudrate; + } else { + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_baudrate); + } +} + +mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) { + return self->timeout; +} + +void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) { + if (timeout > 100.0) { + mp_raise_ValueError(MP_ERROR_TEXT("Timeout must be < 100 seconds")); + } + + timeout_ms = 1000 * (uint32_t)timeout; + self->timeout = (uint32_t)timeout; + + return; +} + +uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { + return ringbuf_num_filled(self->ringbuf); +} + +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { + MXC_UART_ClearRXFIFO(self->uart_regs); + ringbuf_clear(self->ringbuf); +} + +bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { + return !(MXC_UART_GetStatus(self->uart_regs) & (MXC_F_UART_STATUS_TX_BUSY)); +} + +void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { + common_hal_never_reset_pin(self->tx_pin); + common_hal_never_reset_pin(self->rx_pin); + common_hal_never_reset_pin(self->cts_pin); + common_hal_never_reset_pin(self->rts_pin); + uart_never_reset_mask |= (1 << (self->uart_id)); +} + +#endif // CIRCUITPY_BUSIO_UART diff --git a/ports/analog/common-hal/busio/UART.h b/ports/analog/common-hal/busio/UART.h new file mode 100644 index 0000000000000..296c738772e68 --- /dev/null +++ b/ports/analog/common-hal/busio/UART.h @@ -0,0 +1,39 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#ifndef MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_UART_H +#define MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_UART_H + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" +#include "py/ringbuf.h" + +#include "max32_port.h" + +// Define a struct for what BUSIO.UART should contain +typedef struct { + mp_obj_base_t base; + int error; + float timeout; + + int uart_id; + int uart_map; + mxc_uart_regs_t *uart_regs; + ringbuf_t *ringbuf; + bool parity; + uint8_t bits; + uint8_t stop_bits; + uint32_t baudrate; + + const mcu_pin_obj_t *rx_pin; + const mcu_pin_obj_t *tx_pin; + const mcu_pin_obj_t *rts_pin; + const mcu_pin_obj_t *cts_pin; +} busio_uart_obj_t; + +void uart_reset(void); + +#endif // MICROPY_INCLUDED_MAX32_COMMON_HAL_BUSIO_UART_H diff --git a/ports/analog/common-hal/busio/__init__.c b/ports/analog/common-hal/busio/__init__.c new file mode 100644 index 0000000000000..ff05be051bb25 --- /dev/null +++ b/ports/analog/common-hal/busio/__init__.c @@ -0,0 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT diff --git a/ports/analog/common-hal/digitalio/DigitalInOut.c b/ports/analog/common-hal/digitalio/DigitalInOut.c index 7d4048d77e70e..93e2242fbb6f6 100644 --- a/ports/analog/common-hal/digitalio/DigitalInOut.c +++ b/ports/analog/common-hal/digitalio/DigitalInOut.c @@ -107,7 +107,7 @@ digitalio_direction_t common_hal_digitalio_digitalinout_get_direction( if (self->pin->port < 4) { // Check that I/O mode is enabled and we don't have in AND out on at the same time - MP_STATIC_ASSERT(!((port->en0 & mask) && (port->inen & mask) && (port->outen & mask))); + MP_STATIC_ASSERT_NONCONSTEXPR(!((port->en0 & mask) && (port->inen & mask) && (port->outen & mask))); if ((port->en0 & mask) && (port->outen & mask)) { return DIRECTION_OUTPUT; diff --git a/ports/analog/common-hal/microcontroller/Pin.c b/ports/analog/common-hal/microcontroller/Pin.c index 4545aa039c2fa..83e2f3b9c3a76 100644 --- a/ports/analog/common-hal/microcontroller/Pin.c +++ b/ports/analog/common-hal/microcontroller/Pin.c @@ -37,7 +37,7 @@ void reset_all_pins(void) { } void reset_pin_number(uint8_t pin_port, uint8_t pin_pad) { - if (pin_port == INVALID_PIN || pin_port > NUM_GPIO_PORTS) { + if ((pin_port == INVALID_PIN) || (pin_port > NUM_GPIO_PORTS)) { return; } diff --git a/ports/analog/common-hal/os/__init__.c b/ports/analog/common-hal/os/__init__.c index 7b607cf6b3c4b..508b40798e22b 100644 --- a/ports/analog/common-hal/os/__init__.c +++ b/ports/analog/common-hal/os/__init__.c @@ -2,6 +2,7 @@ // // SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries // SPDX-FileCopyrightText: Copyright (c) 2019 Lucian Copeland for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. // // SPDX-License-Identifier: MIT @@ -15,9 +16,14 @@ // #include "peripherals/periph.h" +// true random number generator, TRNG +#include "trng.h" + bool common_hal_os_urandom(uint8_t *buffer, uint32_t length) { #if (HAS_TRNG) - // todo (low prior): implement + // get a random number of "length" number of bytes + MXC_TRNG_Random(buffer, length); + return true; #else #endif return false; diff --git a/ports/analog/common-hal/rtc/RTC.c b/ports/analog/common-hal/rtc/RTC.c new file mode 100644 index 0000000000000..c9d2258c1ece1 --- /dev/null +++ b/ports/analog/common-hal/rtc/RTC.c @@ -0,0 +1,42 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Nick Moore for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2019 Artur Pacholec +// SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" +#include "shared/timeutils/timeutils.h" +#include "supervisor/port.h" + +// This is the time in seconds since 2000 that the RTC was started. +// TODO: Change the offset to ticks so that it can be a subsecond adjustment. +static uint32_t rtc_offset = 0; + +void common_hal_rtc_get_time(timeutils_struct_time_t *tm) { + uint64_t ticks_s = port_get_raw_ticks(NULL) / 1024; + timeutils_seconds_since_2000_to_struct_time(rtc_offset + ticks_s, tm); +} + +void common_hal_rtc_set_time(timeutils_struct_time_t *tm) { + uint64_t ticks_s = port_get_raw_ticks(NULL) / 1024; + uint32_t epoch_s = timeutils_seconds_since_2000( + tm->tm_year, tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec + ); + rtc_offset = epoch_s - ticks_s; +} + +// the calibration function will be implemented in near future +// the RTC oscillator on my MAX32690-APARD is only off by 0.001 second +// the inaccuracy is still tolerable +int common_hal_rtc_get_calibration(void) { + return 0; +} + +void common_hal_rtc_set_calibration(int calibration) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_calibration); +} diff --git a/ports/analog/common-hal/rtc/RTC.h b/ports/analog/common-hal/rtc/RTC.h new file mode 100644 index 0000000000000..590bc93ab5231 --- /dev/null +++ b/ports/analog/common-hal/rtc/RTC.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Noralf Trønnes +// SPDX-FileCopyrightText: Copyright (c) 2019 Artur Pacholec +// SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +extern void rtc_reset(void); diff --git a/ports/zephyr-cp/common-hal/zephyr_serial/__init__.c b/ports/analog/common-hal/rtc/__init__.c similarity index 70% rename from ports/zephyr-cp/common-hal/zephyr_serial/__init__.c rename to ports/analog/common-hal/rtc/__init__.c index 2784446ba15a0..ddf788bb0b5ad 100644 --- a/ports/zephyr-cp/common-hal/zephyr_serial/__init__.c +++ b/ports/analog/common-hal/rtc/__init__.c @@ -1,7 +1,6 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. // // SPDX-License-Identifier: MIT - -// No zephyr_serial module functions. diff --git a/ports/analog/max32_port.h b/ports/analog/max32_port.h index 5d92fcfe6d3ee..89830e96b0a49 100644 --- a/ports/analog/max32_port.h +++ b/ports/analog/max32_port.h @@ -22,29 +22,34 @@ #include "system_max32690.h" #include "max32690.h" +// UART Ports & pins +#include "peripherals/max32690/max32_uart.h" +#include "peripherals/max32690/max32_i2c.h" +#include "peripherals/max32690/max32_spi.h" + /** START: GPIO4 Handling specific to MAX32690 */ -#define GPIO4_PIN_MASK 0x00000003 -#define GPIO4_RESET_MASK 0xFFFFFF77 -#define GPIO4_OUTEN_MASK(mask) \ + #define GPIO4_PIN_MASK 0x00000003 + #define GPIO4_RESET_MASK 0xFFFFFF77 + #define GPIO4_OUTEN_MASK(mask) \ (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_OE_POS) | \ ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_OE_POS - 1))) -#define GPIO4_PULLDIS_MASK(mask) \ + #define GPIO4_PULLDIS_MASK(mask) \ (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_PE_POS) | \ ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_PE_POS - 1))) -#define GPIO4_DATAOUT_MASK(mask) \ + #define GPIO4_DATAOUT_MASK(mask) \ (((mask & (1 << 0)) << MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ ((mask & (1 << 1)) << (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) -#define GPIO4_DATAOUT_GET_MASK(mask) \ + #define GPIO4_DATAOUT_GET_MASK(mask) \ ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_DO) >> MXC_F_MCR_GPIO4_CTRL_P40_DO_POS) | \ ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_DO) >> \ (MXC_F_MCR_GPIO4_CTRL_P41_DO_POS - 1))) & \ mask) -#define GPIO4_DATAIN_MASK(mask) \ + #define GPIO4_DATAIN_MASK(mask) \ ((((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P40_IN) >> MXC_F_MCR_GPIO4_CTRL_P40_IN_POS) | \ ((MXC_MCR->gpio4_ctrl & MXC_F_MCR_GPIO4_CTRL_P41_IN) >> \ (MXC_F_MCR_GPIO4_CTRL_P41_IN_POS - 1))) & \ mask) -#define GPIO4_AFEN_MASK(mask) \ + #define GPIO4_AFEN_MASK(mask) \ (((mask & (1 << 0)) << MXC_F_MCR_OUTEN_PDOWN_OUT_EN_POS) | \ ((mask & (1 << 1)) >> (MXC_F_MCR_OUTEN_SQWOUT_EN_POS + 1))) /** END: GPIO4 Handling specific to MAX32690 */ diff --git a/ports/analog/mpconfigport.h b/ports/analog/mpconfigport.h index c4b3ee031cacf..3296661fca787 100644 --- a/ports/analog/mpconfigport.h +++ b/ports/analog/mpconfigport.h @@ -12,6 +12,8 @@ // 24KiB stack #define CIRCUITPY_DEFAULT_STACK_SIZE 0x6000 +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) + // Also includes mpconfigboard.h #include "py/circuitpy_mpconfig.h" diff --git a/ports/analog/mpconfigport.mk b/ports/analog/mpconfigport.mk index 1729a284a3f47..f1cd0bb2901e9 100644 --- a/ports/analog/mpconfigport.mk +++ b/ports/analog/mpconfigport.mk @@ -2,6 +2,7 @@ # # SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries # SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +# SPDX-FileCopyrightText: Copyright (c) 2025 Peggy Zhu, Analog Devices, Inc. # # SPDX-License-Identifier: MIT @@ -21,9 +22,8 @@ INTERNAL_FLASH_FILESYSTEM = 1 #################################################################################### # These modules are implemented in ports//common-hal: -# Plan to implement -CIRCUITPY_BUSIO ?= 0 -CIRCUITPY_RTC ?= 0 +CIRCUITPY_BUSIO ?= 1 +CIRCUITPY_RTC ?= 1 # Other modules (may or may not implement): CIRCUITPY_ANALOGIO ?= 0 @@ -51,7 +51,7 @@ CIRCUITPY_BITBANGIO ?= 1 # Requires Microcontroller CIRCUITPY_TOUCHIO ?= 1 # Requires OS -CIRCUITPY_RANDOM ?= 0 +CIRCUITPY_RANDOM ?= 1 # Requires busio.UART CIRCUITPY_CONSOLE_UART ?= 0 # Does nothing without I2C diff --git a/ports/analog/peripherals/max32690/gpios.h b/ports/analog/peripherals/max32690/gpios.h index 4677bf8f33dbe..fe91227728291 100644 --- a/ports/analog/peripherals/max32690/gpios.h +++ b/ports/analog/peripherals/max32690/gpios.h @@ -4,6 +4,8 @@ // // SPDX-License-Identifier: MIT +#pragma once + #include "py/obj.h" #include "py/mphal.h" diff --git a/ports/analog/peripherals/max32690/max32_i2c.c b/ports/analog/peripherals/max32690/max32_i2c.c new file mode 100644 index 0000000000000..4b9ec8b4db264 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_i2c.c @@ -0,0 +1,75 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +#include "common-hal/busio/I2C.h" +#include "max32_i2c.h" +#include "max32690.h" + +#include "py/runtime.h" +#include "py/mperrno.h" + + +/* Note: The MAX32690 assigns the same alternate function to multiple sets + * of pins. The drivers will enable both sets so that either can be used. + * Users should ensure the unused set is left unconnected. + * + * See MAX32690 Rev A2 Errata #16: + * https://www.analog.com/media/en/technical-documentation/data-sheets/max32690_a2_errata_rev2.pdf + * + * Additionally, note that the TQFN package does not expose some of the duplicate pins. For this package, + * enabling the un-routed GPIOs has been shown to cause initialization issues with the I2C block. + * To work around this, "MAX32690GTK_PACKAGE_TQFN" can be defined by the build system. The recommend place + * to do it is in the "board.mk" file of the BSP. This will prevent the inaccessible pins from being configured. + */ + +const mxc_gpio_cfg_t i2c_maps[NUM_I2C] = { + // I2C0 + { MXC_GPIO2, (MXC_GPIO_PIN_7 | MXC_GPIO_PIN_8), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // I2C1 + { MXC_GPIO0, (MXC_GPIO_PIN_11 | MXC_GPIO_PIN_12), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // I2C2 + { MXC_GPIO1, (MXC_GPIO_PIN_7 | MXC_GPIO_PIN_8), MXC_GPIO_FUNC_ALT3, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, +}; +#ifndef MAX32690GTK_PACKAGE_TQFN +const mxc_gpio_cfg_t i2c_maps_extra[NUM_I2C] = { + // I2C0A + { MXC_GPIO0, (MXC_GPIO_PIN_30 | MXC_GPIO_PIN_31), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_PULL_UP, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, + // I2C1A + { MXC_GPIO2, (MXC_GPIO_PIN_17 | MXC_GPIO_PIN_18), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_PULL_UP, MXC_GPIO_VSSEL_VDDIOH, MXC_GPIO_DRVSTR_0 }, + // I2C2C + { MXC_GPIO0, (MXC_GPIO_PIN_13 | MXC_GPIO_PIN_14), MXC_GPIO_FUNC_ALT3, + MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, +}; +#endif + +int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl) { + for (int i = 0; i < NUM_I2C; i++) { + if ((i2c_maps[i].port == (MXC_GPIO_GET_GPIO(sda->port))) + && (i2c_maps[i].mask == ((sda->mask) | (scl->mask)))) { + return i; + } + } + + // Additional for loop to cover alternate potential I2C maps + #ifndef MAX32690GTK_PACKAGE_TQFN + for (int i = 0; i < NUM_I2C; i++) { + if ((i2c_maps_extra[i].port == (MXC_GPIO_GET_GPIO(sda->port))) + && (i2c_maps_extra[i].mask == ((sda->mask) | (scl->mask)))) { + return i; + } + } + #endif + + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pins); + return -1; +} diff --git a/ports/analog/peripherals/max32690/max32_i2c.h b/ports/analog/peripherals/max32690/max32_i2c.h new file mode 100644 index 0000000000000..b64cfd308dcf5 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_i2c.h @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "i2c_regs.h" +#include "mxc_sys.h" +#include "i2c.h" +#include "peripherals/pins.h" + +#define NUM_I2C 3 + +int pinsToI2c(const mcu_pin_obj_t *sda, const mcu_pin_obj_t *scl); diff --git a/ports/analog/peripherals/max32690/max32_spi.c b/ports/analog/peripherals/max32690/max32_spi.c new file mode 100644 index 0000000000000..c78fd64dbd705 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_spi.c @@ -0,0 +1,44 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +#include "common-hal/busio/SPI.h" +#include "max32_spi.h" +#include "max32690.h" + +#include "py/runtime.h" +#include "py/mperrno.h" + +const mxc_gpio_cfg_t spi_maps[NUM_SPI] = { + // SPI0 + { MXC_GPIO2, (MXC_GPIO_PIN_27 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), + MXC_GPIO_FUNC_ALT2, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI1 + { MXC_GPIO1, (MXC_GPIO_PIN_26 | MXC_GPIO_PIN_28 | MXC_GPIO_PIN_29), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI2 + { MXC_GPIO2, (MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3 | MXC_GPIO_PIN_4), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI3 + { MXC_GPIO0, (MXC_GPIO_PIN_16 | MXC_GPIO_PIN_20 | MXC_GPIO_PIN_21), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + // SPI4 + { MXC_GPIO1, (MXC_GPIO_PIN_1 | MXC_GPIO_PIN_2 | MXC_GPIO_PIN_3), + MXC_GPIO_FUNC_ALT1, MXC_GPIO_PAD_NONE, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, +}; + +int pinsToSpi(const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, + const mcu_pin_obj_t *sck) { + for (int i = 0; i < NUM_SPI; i++) { + if ((spi_maps[i].port == (MXC_GPIO_GET_GPIO(mosi->port))) + && (spi_maps[i].mask == ((mosi->mask) | (miso->mask) | (sck->mask)))) { + return i; + } + } + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pins); + return -1; +} diff --git a/ports/analog/peripherals/max32690/max32_spi.h b/ports/analog/peripherals/max32690/max32_spi.h new file mode 100644 index 0000000000000..76bb48a59a7e5 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_spi.h @@ -0,0 +1,17 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "spi_regs.h" +#include "mxc_sys.h" +#include "spi.h" +#include "peripherals/pins.h" + +#define NUM_SPI 5 + +int pinsToSpi(const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, + const mcu_pin_obj_t *sck); diff --git a/ports/analog/peripherals/max32690/max32_uart.c b/ports/analog/peripherals/max32690/max32_uart.c new file mode 100644 index 0000000000000..7d37708797415 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_uart.c @@ -0,0 +1,36 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +#include "common-hal/busio/UART.h" +#include "max32_uart.h" +#include "max32690.h" + +#include "py/runtime.h" +#include "py/mperrno.h" + +const mxc_gpio_cfg_t uart_maps[NUM_UARTS] = { + { MXC_GPIO2, (MXC_GPIO_PIN_11 | MXC_GPIO_PIN_12), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO2, (MXC_GPIO_PIN_14 | MXC_GPIO_PIN_16), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO1, (MXC_GPIO_PIN_9 | MXC_GPIO_PIN_10), MXC_GPIO_FUNC_ALT1, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 }, + { MXC_GPIO3, (MXC_GPIO_PIN_0 | MXC_GPIO_PIN_1), MXC_GPIO_FUNC_ALT2, + MXC_GPIO_PAD_WEAK_PULL_UP, MXC_GPIO_VSSEL_VDDIO, MXC_GPIO_DRVSTR_0 } +}; + +int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx) { + for (int i = 0; i < NUM_UARTS; i++) { + if ((uart_maps[i].port == (MXC_GPIO_GET_GPIO(tx->port))) + && (uart_maps[i].mask == ((tx->mask) | (rx->mask)))) { + return i; + } + } + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pins); + return -1; +} diff --git a/ports/analog/peripherals/max32690/max32_uart.h b/ports/analog/peripherals/max32690/max32_uart.h new file mode 100644 index 0000000000000..f03b500a3c507 --- /dev/null +++ b/ports/analog/peripherals/max32690/max32_uart.h @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Brandon Hurst, Analog Devices, Inc. +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "uart_regs.h" +#include "mxc_sys.h" +#include "uart.h" +#include "peripherals/pins.h" + +#define NUM_UARTS 4 + +int pinsToUart(const mcu_pin_obj_t *rx, const mcu_pin_obj_t *tx); diff --git a/ports/analog/supervisor/port.c b/ports/analog/supervisor/port.c index ad003c12ed9e0..ee410a38148e9 100644 --- a/ports/analog/supervisor/port.c +++ b/ports/analog/supervisor/port.c @@ -39,11 +39,15 @@ // Sys includes #include "max32_port.h" +#include "nvic_table.h" // Timers #include "mxc_delay.h" #include "rtc.h" +// true random number generator, TRNG +#include "trng.h" + // msec to RTC subsec ticks (4 kHz) /* Converts a time in milleseconds to equivalent RSSA register value */ #define MSEC_TO_SS_ALARM(x) (0 - ((x * 4096) / 1000)) @@ -63,18 +67,27 @@ static uint32_t subsec, sec = 0; static uint32_t tick_flag = 0; // defined by cmsis core files -extern void NVIC_SystemReset(void) NORETURN; +extern void NVIC_SystemReset(void) MP_NORETURN; volatile uint32_t system_ticks = 0; void SysTick_Handler(void) { system_ticks++; + + MXC_DelayHandler(); } safe_mode_t port_init(void) { int err = E_NO_ERROR; + // Set Vector Table to RAM & configure ARM core to use RAM-based ISRs + // This allows definition of ISRs with custom names + // + // Useful for mapping ISRs with names not related to a specific IRQn. + // Source: https://arm-software.github.io/CMSIS_5/Core/html/using_VTOR_pg.html + NVIC_SetRAM(); + // 1ms tick timer SysTick_Config(SystemCoreClock / 1000); NVIC_EnableIRQ(SysTick_IRQn); @@ -112,9 +125,18 @@ safe_mode_t port_init(void) { } ; + // enable TRNG (true random number generator) + #ifdef CIRCUITPY_RANDOM + MXC_TRNG_Init(); + #endif + return SAFE_MODE_NONE; } +void TRNG_IRQHandler(void) { + MXC_TRNG_Handler(); +} + void RTC_IRQHandler(void) { // Read flags to clear int flags = MXC_RTC_GetFlags(); @@ -144,7 +166,6 @@ void reset_cpu(void) { // Reset MCU state void reset_port(void) { - reset_all_pins(); } // Reset to the bootloader diff --git a/ports/analog/tools/debug-dap.gdb b/ports/analog/tools/debug-dap.gdb new file mode 100644 index 0000000000000..d0aa7e2b13fa0 --- /dev/null +++ b/ports/analog/tools/debug-dap.gdb @@ -0,0 +1,3 @@ +target remote :3333 +break main +continue diff --git a/ports/analog/tools/debug-dap.sh b/ports/analog/tools/debug-dap.sh new file mode 100644 index 0000000000000..df2964fcb122a --- /dev/null +++ b/ports/analog/tools/debug-dap.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Primer on taking cmd line args in Linux shell scripts +# $0 is the script itself +# $1 $2 $3 are arg1, arg2, arg3, etc + +# Export OCD Path +### USER ACTION: Replace with your path to OpenOCD ### +OCD_PATH="~/MaximSDK/Tools/OpenOCD" + +# Call openocd to setup the debug server, storing the PID for OpenOCD +sh -c "openocd -s $OCD_PATH/scripts -f interface/cmsis-dap.cfg -f target/$1.cfg -c \"init; reset halt\" " & + +# Allow enough time for OCD server to set up +# + wait for the sleep to finish +sleep 3 & +wait $! + +# spawn the gdb process and store the gdb_pid +gdb-multiarch build-apard32690/firmware.elf -x "tools/debug-dap.gdb" + +# when gdb exits, kill all openocd processes +killall openocd diff --git a/ports/analog/tools/flash_max32.jlink b/ports/analog/tools/flash_max32.jlink index 4c9cbacb96fee..0453f4cb077fd 100644 --- a/ports/analog/tools/flash_max32.jlink +++ b/ports/analog/tools/flash_max32.jlink @@ -1,6 +1,7 @@ -si 1 +selectinterface swd erase -loadbin build-APARD/firmware.bin 0x10000000 -r -g +loadbin build-apard32690/firmware.bin 0x10000000 +reset +halt +go exit diff --git a/ports/atmel-samd/Makefile b/ports/atmel-samd/Makefile index 9582eff212d60..0aaf6e3d849bf 100644 --- a/ports/atmel-samd/Makefile +++ b/ports/atmel-samd/Makefile @@ -169,7 +169,7 @@ LIBS += -lm endif ifeq ($(CHIP_FAMILY), samd21) -LDFLAGS += -mthumb -mcpu=cortex-m0plus -Lasf/thirdparty/CMSIS/Lib/GCC/ +LDFLAGS += -mthumb -mcpu=cortex-m0plus -Lasf/thirdparty/CMSIS/Lib/GCC/ # codespell:ignore thirdparty BOOTLOADER_SIZE := 0x2000 else ifeq ($(CHIP_FAMILY), samd51) LDFLAGS += -mthumb -mcpu=cortex-m4 @@ -284,6 +284,7 @@ SRC_C += \ lib/tinyusb/src/portable/microchip/samd/dcd_samd.c \ mphalport.c \ reset.c \ + shared/runtime/gchelper_native.c \ timer_handler.c \ $(SRC_PERIPHERALS) \ @@ -308,7 +309,11 @@ ifeq ($(CIRCUITPY_AUDIOBUSIO),1) SRC_C += peripherals/samd/i2s.c peripherals/samd/$(PERIPHERALS_CHIP_FAMILY)/i2s.c endif -SRC_S_UPPER = supervisor/shared/cpu_regs.S +ifeq ($(CHIP_FAMILY), samd21) +SRC_S += shared/runtime/gchelper_thumb1.s +else +SRC_S += shared/runtime/gchelper_thumb2.s +endif OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_ASF:.c=.o)) @@ -317,7 +322,7 @@ ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) QSTR_GLOBAL_REQUIREMENTS += $(HEADER_BUILD)/sdiodata.h diff --git a/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h index 5979d1ee9979f..157db0d3f7fbd 100644 --- a/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/samd21/hpl_sercom_config.h @@ -431,7 +431,7 @@ // <0x2=>8x arithmetic // <0x3=>8x fractional // <0x3=>3x -// How many over-sampling bits used when samling data state +// How many over-sampling bits used when sampling data state // usart_arch_sampr #ifndef CONF_SERCOM_2_USART_SAMPR #define CONF_SERCOM_2_USART_SAMPR 0x0 diff --git a/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h index e05560e635823..6dd21277b78d0 100644 --- a/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/samd51/hpl_sercom_config.h @@ -431,7 +431,7 @@ // <0x2=>8x arithmetic // <0x3=>8x fractional // <0x3=>3x -// How many over-sampling bits used when samling data state +// How many over-sampling bits used when sampling data state // usart_arch_sampr #ifndef CONF_SERCOM_2_USART_SAMPR #define CONF_SERCOM_2_USART_SAMPR 0x0 diff --git a/ports/atmel-samd/asf4_conf/same51/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/same51/hpl_sercom_config.h index e05560e635823..6dd21277b78d0 100644 --- a/ports/atmel-samd/asf4_conf/same51/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/same51/hpl_sercom_config.h @@ -431,7 +431,7 @@ // <0x2=>8x arithmetic // <0x3=>8x fractional // <0x3=>3x -// How many over-sampling bits used when samling data state +// How many over-sampling bits used when sampling data state // usart_arch_sampr #ifndef CONF_SERCOM_2_USART_SAMPR #define CONF_SERCOM_2_USART_SAMPR 0x0 diff --git a/ports/atmel-samd/asf4_conf/same54/hpl_sercom_config.h b/ports/atmel-samd/asf4_conf/same54/hpl_sercom_config.h index e05560e635823..6dd21277b78d0 100644 --- a/ports/atmel-samd/asf4_conf/same54/hpl_sercom_config.h +++ b/ports/atmel-samd/asf4_conf/same54/hpl_sercom_config.h @@ -431,7 +431,7 @@ // <0x2=>8x arithmetic // <0x3=>8x fractional // <0x3=>3x -// How many over-sampling bits used when samling data state +// How many over-sampling bits used when sampling data state // usart_arch_sampr #ifndef CONF_SERCOM_2_USART_SAMPR #define CONF_SERCOM_2_USART_SAMPR 0x0 diff --git a/ports/atmel-samd/audio_dma.c b/ports/atmel-samd/audio_dma.c index e25b74d9a2893..e39804015063b 100644 --- a/ports/atmel-samd/audio_dma.c +++ b/ports/atmel-samd/audio_dma.c @@ -39,14 +39,14 @@ uint8_t find_sync_event_channel_raise(void) { } void audio_dma_disable_channel(uint8_t channel) { - if (channel >= AUDIO_DMA_CHANNEL_COUNT) { + if (channel == NO_DMA_CHANNEL) { return; } dma_disable_channel(channel); } void audio_dma_enable_channel(uint8_t channel) { - if (channel >= AUDIO_DMA_CHANNEL_COUNT) { + if (channel == NO_DMA_CHANNEL) { return; } dma_enable_channel(channel); @@ -171,8 +171,8 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, bool output_signed, uint32_t output_register_address, uint8_t dma_trigger_source) { - uint8_t dma_channel = dma_allocate_channel(true); - if (dma_channel >= AUDIO_DMA_CHANNEL_COUNT) { + uint8_t dma_channel = dma_allocate_audio_channel(); + if (dma_channel == NO_DMA_CHANNEL) { return AUDIO_DMA_DMA_BUSY; } @@ -298,14 +298,14 @@ audio_dma_result audio_dma_setup_playback(audio_dma_t *dma, void audio_dma_stop(audio_dma_t *dma) { uint8_t channel = dma->dma_channel; - if (channel < AUDIO_DMA_CHANNEL_COUNT) { + if (channel != NO_DMA_CHANNEL) { audio_dma_disable_channel(channel); disable_event_channel(dma->event_channel); MP_STATE_PORT(playing_audio)[channel] = NULL; audio_dma_state[channel] = NULL; dma_free_channel(dma->dma_channel); } - dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT; + dma->dma_channel = NO_DMA_CHANNEL; dma->playing_in_progress = false; } @@ -318,7 +318,7 @@ void audio_dma_resume(audio_dma_t *dma) { } bool audio_dma_get_paused(audio_dma_t *dma) { - if (dma->dma_channel >= AUDIO_DMA_CHANNEL_COUNT) { + if (dma->dma_channel == NO_DMA_CHANNEL) { return false; } uint32_t status = dma_transfer_status(dma->dma_channel); @@ -327,7 +327,7 @@ bool audio_dma_get_paused(audio_dma_t *dma) { } void audio_dma_init(audio_dma_t *dma) { - dma->dma_channel = AUDIO_DMA_CHANNEL_COUNT; + dma->dma_channel = NO_DMA_CHANNEL; } void audio_dma_reset(void) { @@ -341,7 +341,7 @@ void audio_dma_reset(void) { } bool audio_dma_get_playing(audio_dma_t *dma) { - if (dma->dma_channel >= AUDIO_DMA_CHANNEL_COUNT) { + if (dma->dma_channel == NO_DMA_CHANNEL) { return false; } return dma->playing_in_progress; diff --git a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk index 33165c8cfc5d0..9a2cdcd668ed2 100644 --- a/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/bradanlanestudio_coin_m0/mpconfigboard.mk @@ -18,7 +18,7 @@ CHIP_FAMILY = samd21 SPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" -LONGINT_IMPL = NONE +LONGINT_IMPL = MPZ # the M0 Coin has limited functionality and many modules can be eliminated @@ -26,7 +26,7 @@ LONGINT_IMPL = NONE # Disable modules that are unusable on this special-purpose board. -CIRCUITPY_FULL_BUILD = 0 +CIRCUITPY_FULL_BUILD = 1 CIRCUITPY_AUDIOIO = 1 CIRCUITPY_DISPLAYIO = 0 @@ -38,7 +38,8 @@ CIRCUITPY_ROTARYIO = 0 CIRCUITPY_RTC = 0 CIRCUITPY_USB_HID = 1 CIRCUITPY_USB_MIDI = 0 - +CIRCUITPY_NEOPIXEL_WRITE = 1 +CIRCUITPY_PIXELBUF = 1 # Include these Python libraries in firmware. FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HID diff --git a/ports/atmel-samd/boards/circuitbrains_basic_m0/mpconfigboard.mk b/ports/atmel-samd/boards/circuitbrains_basic_m0/mpconfigboard.mk index 7c301e3ea9692..121becc472d36 100755 --- a/ports/atmel-samd/boards/circuitbrains_basic_m0/mpconfigboard.mk +++ b/ports/atmel-samd/boards/circuitbrains_basic_m0/mpconfigboard.mk @@ -11,4 +11,5 @@ EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 -CIRCUITPY_JPEGIO = 0 +CIRCUITPY_ERRNO = 0 +CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk index 004181148415a..1152fb4acc419 100644 --- a/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_express/mpconfigboard.mk @@ -10,4 +10,6 @@ SPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C, W25Q16JVxQ" LONGINT_IMPL = MPZ +CIRCUITPY_CODEOP = 0 +CIRCUITPY_ERRNO = 0 CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h index be48cb810550e..8fdf20f186b00 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.h @@ -20,6 +20,8 @@ #define SPI_FLASH_SCK_PIN &pin_PA09 #define SPI_FLASH_CS_PIN &pin_PA13 +#define BOARD_HAS_CRYSTAL 1 + #define DEFAULT_I2C_BUS_SCL (&pin_PA23) #define DEFAULT_I2C_BUS_SDA (&pin_PA22) diff --git a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk index 573a69bee327c..dd58e8d1ce5a7 100644 --- a/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m0_supersized/mpconfigboard.mk @@ -11,3 +11,5 @@ EXTERNAL_FLASH_DEVICES = "S25FL064L" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 +CIRCUITPY_ERRNO = 0 +CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk index a7c18acba3c55..ee414b6e214b0 100644 --- a/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m4_can/mpconfigboard.mk @@ -17,6 +17,7 @@ CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_GIFIO = 0 CIRCUITPY_I2CTARGET = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_PS2IO = 0 CIRCUITPY_SYNTHIO = 0 CIRCUITPY_LTO_PARTITION = one diff --git a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk index 4958581056e68..f780ea4aa3d0f 100644 --- a/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/feather_m4_express/mpconfigboard.mk @@ -14,6 +14,7 @@ CIRCUITPY__EVE = 1 CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_SYNTHIO = 0 CIRCUITPY_VECTORIO = 0 diff --git a/ports/atmel-samd/boards/hallowing_m0_express/board.c b/ports/atmel-samd/boards/hallowing_m0_express/board.c index 637129a0e9d5e..fd5ad5485120f 100644 --- a/ports/atmel-samd/boards/hallowing_m0_express/board.c +++ b/ports/atmel-samd/boards/hallowing_m0_express/board.c @@ -54,9 +54,9 @@ void board_init(void) { common_hal_busio_spi_never_reset(spi); common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA28, // Command or data - &pin_PA01, // Chip select - &pin_PA27, // Reset + MP_OBJ_FROM_PTR(&pin_PA28), // Command or data + MP_OBJ_FROM_PTR(&pin_PA01), // Chip select + MP_OBJ_FROM_PTR(&pin_PA27), // Reset 12000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/hallowing_m4_express/board.c b/ports/atmel-samd/boards/hallowing_m4_express/board.c index bfef5fbcd75dd..c7217b70b0a97 100644 --- a/ports/atmel-samd/boards/hallowing_m4_express/board.c +++ b/ports/atmel-samd/boards/hallowing_m4_express/board.c @@ -35,9 +35,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PB31, // TFT_DC Command or data - &pin_PA27, // TFT_CS Chip select - &pin_PB30, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PB31), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PA27), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PB30), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/hallowing_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/hallowing_m4_express/mpconfigboard.mk index 3dbef7c576a13..76c60b4464639 100644 --- a/ports/atmel-samd/boards/hallowing_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/hallowing_m4_express/mpconfigboard.mk @@ -16,6 +16,7 @@ CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_I2CDISPLAYBUS = 0 CIRCUITPY_I2CTARGET = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_PARALLELDISPLAYBUS = 0 CIRCUITPY_RGBMATRIX = 0 CIRCUITPY_SHARPDISPLAY = 0 diff --git a/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk index 27bf8c165da88..be22cb94fae92 100644 --- a/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/itsybitsy_m4_express/mpconfigboard.mk @@ -10,11 +10,13 @@ QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "GD25Q16C,W25Q16JVxQ" LONGINT_IMPL = MPZ +CIRCUITPY__EVE = 1 CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 -CIRCUITPY_GIFIO = 0 CIRCUITPY_JPEGIO = 0 -CIRCUITPY_TILEPALETTEMAPPER = 0 +CIRCUITPY_MSGPACK = 0 +CIRCUITPY_SYNTHIO = 0 +CIRCUITPY_VECTORIO = 0 CIRCUITPY_BITBANG_APA102 = 1 diff --git a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk index d758d6878fb7f..d4801359cf8c5 100644 --- a/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m0_express/mpconfigboard.mk @@ -11,4 +11,5 @@ EXTERNAL_FLASH_DEVICES = "S25FL216K, GD25Q16C, W25Q16JVxQ" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 +CIRCUITPY_ERRNO = 0 CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/metro_m4_airlift_lite/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m4_airlift_lite/mpconfigboard.mk index 69cd5b0e2863e..cb7e690c6c02d 100644 --- a/ports/atmel-samd/boards/metro_m4_airlift_lite/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m4_airlift_lite/mpconfigboard.mk @@ -11,9 +11,13 @@ EXTERNAL_FLASH_DEVICES = "S25FL116K, S25FL216K, GD25Q16C, W25Q16JVxQ" LONGINT_IMPL = MPZ CIRCUITPY__EVE = 0 +CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_SYNTHIO = 0 +CIRCUITPY_VECTORIO = 0 + # We don't have room for the fonts for terminalio for certain languages, # so turn off terminalio, and if it's off and displayio is on, diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk index 55a307ed4e308..d5aef01a0374f 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.mk @@ -14,6 +14,7 @@ CIRCUITPY__EVE = 1 CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_SYNTHIO = 0 CIRCUITPY_VECTORIO = 0 diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython/board.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/board.c new file mode 100644 index 0000000000000..b44a1ae51e04b --- /dev/null +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.h b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.h new file mode 100644 index 0000000000000..4416d8517d279 --- /dev/null +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.h @@ -0,0 +1,33 @@ + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Microchip Curiosity CircuitPython" +#define MICROPY_HW_MCU_NAME "same51j20" +#define CIRCUITPY_MCU_FAMILY samd51 + +#define MICROPY_HW_LED_STATUS (&pin_PB23) +#define MICROPY_HW_NEOPIXEL (&pin_PB22) + +#define BOARD_HAS_CRYSTAL 1 + +// USB is always used internally so skip the pin objects for it. +#define IGNORE_PIN_PA24 1 +#define IGNORE_PIN_PA25 1 + +#define DEFAULT_I2C_BUS_SCL (&pin_PB30) +#define DEFAULT_I2C_BUS_SDA (&pin_PB31) + +#define CIRCUITPY_BOARD_SPI (3) +// These correspond to the CIRCUITPY_BOARD_BUS_SINGLETON definitions in pins.c +#define CIRCUITPY_BOARD_SPI_PIN { \ + {.clock = &pin_PB03, .mosi = &pin_PB02, .miso = &pin_PB00}, /*board.SPI()*/ \ + {.clock = &pin_PA05, .mosi = &pin_PA04, .miso = NULL}, /*board.LCD_SPI()*/ \ + {.clock = &pin_PA17, .mosi = &pin_PA16, .miso = &pin_PA18}, /*board.SD_SPI()*/ \ +} + +#define DEFAULT_CAN_BUS_TX (&pin_PB12) +#define DEFAULT_CAN_BUS_RX (&pin_PB13) +#define DEFAULT_CAN_BUS_STDBY (&pin_PB17) + +#define DEFAULT_UART_BUS_RX (&pin_PA23) +#define DEFAULT_UART_BUS_TX (&pin_PA22) diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.mk b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.mk new file mode 100644 index 0000000000000..985eb4c6a4465 --- /dev/null +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x04D8 +USB_PID = 0xE52B +USB_PRODUCT = "Microchip Curiosity CircuitPython" +USB_MANUFACTURER = "Microchip Technology Inc" + +CHIP_VARIANT = SAME51J20A +CHIP_FAMILY = same51 + +QSPI_FLASH_FILESYSTEM = 1 +EXTERNAL_FLASH_DEVICES = "SST26VF016B,SST26VF032B,SST26VF064B" +LONGINT_IMPL = MPZ + +CIRCUITPY__EVE = 1 +CIRCUITPY_CANIO = 1 diff --git a/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c new file mode 100644 index 0000000000000..49c7f7565e26e --- /dev/null +++ b/ports/atmel-samd/boards/microchip_curiosity_circuitpython/pins.c @@ -0,0 +1,91 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2017 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +// The singleton for board.SPI() is already defined. +// board.LCD_SPI() +CIRCUITPY_BOARD_BUS_SINGLETON(lcd_spi, spi, 1) +// board.SD_SPI() +CIRCUITPY_BOARD_BUS_SINGLETON(sd_spi, spi, 2) + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_PA15) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_PA20) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_PA21) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_PA27) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_PB14) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_PB15) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_PB16) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_PB23) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_PB23) }, + + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_PB22) }, + + { MP_ROM_QSTR(MP_QSTR_VREF), MP_ROM_PTR(&pin_PA03) }, + + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PB04) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PB05) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_PB06) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_PB07) }, + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_PB08) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_PB09) }, + + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&pin_PA02) }, + + { MP_ROM_QSTR(MP_QSTR_CAP1), MP_ROM_PTR(&pin_PB09) }, + + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_PA07) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_PA04) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_PA05) }, + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_PA06) }, + + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_PB30) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_PB31) }, + + { MP_ROM_QSTR(MP_QSTR_BLE_TX), MP_ROM_PTR(&pin_PA12) }, + { MP_ROM_QSTR(MP_QSTR_BLE_RX), MP_ROM_PTR(&pin_PA13) }, + { MP_ROM_QSTR(MP_QSTR_BLE_CLR), MP_ROM_PTR(&pin_PA14) }, + + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_PA16) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_PA18) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_PA17) }, + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_PA19) }, + + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_PB00) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_PB00) }, + { MP_ROM_QSTR(MP_QSTR_IMU_INT), MP_ROM_PTR(&pin_PB00) }, + + { MP_ROM_QSTR(MP_QSTR_CS), MP_ROM_PTR(&pin_PB01) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_PB01) }, + { MP_ROM_QSTR(MP_QSTR_IMU_ADDR), MP_ROM_PTR(&pin_PB01) }, + + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_PB02) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_PB02) }, + + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_PB03) }, + { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_PB03) }, + + { MP_ROM_QSTR(MP_QSTR_DEBUG_TX), MP_ROM_PTR(&pin_PA22) }, + { MP_ROM_QSTR(MP_QSTR_DEBUG_RX), MP_ROM_PTR(&pin_PA23) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_RX), MP_ROM_PTR(&pin_PB13) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_TX), MP_ROM_PTR(&pin_PB12) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_CAN_STDBY), MP_ROM_PTR(&pin_PB17) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_PB17) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SPI), MP_ROM_PTR(&board_lcd_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_SD_SPI), MP_ROM_PTR(&board_sd_spi_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk b/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk index 73c56fb5fbab3..8fae53c0101f9 100644 --- a/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk +++ b/ports/atmel-samd/boards/mini_sam_m4/mpconfigboard.mk @@ -10,9 +10,13 @@ QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q16JVxM, W25Q16JVxQ" LONGINT_IMPL = MPZ +CIRCUITPY__EVE = 1 +CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 CIRCUITPY_SYNTHIO = 0 +CIRCUITPY_VECTORIO = 0 CIRCUITPY_BITBANG_APA102 = 1 diff --git a/ports/atmel-samd/boards/monster_m4sk/board.c b/ports/atmel-samd/boards/monster_m4sk/board.c index 1d4fd69b7f708..1c143ad92701f 100644 --- a/ports/atmel-samd/boards/monster_m4sk/board.c +++ b/ports/atmel-samd/boards/monster_m4sk/board.c @@ -35,9 +35,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA07, // TFT_DC Command or data - &pin_PA06, // TFT_CS Chip select - &pin_PA04, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PA07), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PA06), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA04), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/openbook_m4/board.c b/ports/atmel-samd/boards/openbook_m4/board.c index 3ad2e162ecf3c..ebabfb84e9bfa 100644 --- a/ports/atmel-samd/boards/openbook_m4/board.c +++ b/ports/atmel-samd/boards/openbook_m4/board.c @@ -44,50 +44,33 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PB05, // EPD_DC Command or data - &pin_PB07, // EPD_CS Chip select - &pin_PA00, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_PB05), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_PB07), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA00), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct(display, - bus, - start_sequence, - sizeof(start_sequence), - 0, // start up time - stop_sequence, - sizeof(stop_sequence), - 300, // width - 400, // height - 300, // RAM width - 400, // RAM height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x13, // write_black_ram_command - false, // black_bits_inverted - NO_COMMAND, // write_color_ram_command (can add this for grayscale eventually) - false, // color_bits_inverted - 0x000000, // highlight_color - refresh_sequence, // refresh_display_sequence - sizeof(refresh_sequence), - 40, // refresh_time - &pin_PA01, // busy_pin - false, // busy_state - 5, // seconds_per_frame - false, // chip_select (don't always toggle chip select) - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = start_sequence; + args.start_sequence_len = sizeof(start_sequence); + args.stop_sequence = stop_sequence; + args.stop_sequence_len = sizeof(stop_sequence); + args.width = 300; + args.height = 400; + args.ram_width = 300; + args.ram_height = 400; + args.rotation = 270; + args.write_black_ram_command = 0x13; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 40.0; + args.busy_pin = &pin_PA01; + args.seconds_per_frame = 5.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/atmel-samd/boards/pewpew_lcd/board.c b/ports/atmel-samd/boards/pewpew_lcd/board.c index 6a10132e11dc2..d60efd39bf752 100644 --- a/ports/atmel-samd/boards/pewpew_lcd/board.c +++ b/ports/atmel-samd/boards/pewpew_lcd/board.c @@ -54,9 +54,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA19, // TFT_DC Command or data - &pin_PA17, // TFT_CS Chip select - &pin_PA18, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PA19), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PA17), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA18), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/pewpew_m4/board.c b/ports/atmel-samd/boards/pewpew_m4/board.c index 2007d755c694a..ac67b82a3dc7a 100644 --- a/ports/atmel-samd/boards/pewpew_m4/board.c +++ b/ports/atmel-samd/boards/pewpew_m4/board.c @@ -83,9 +83,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA16, // TFT_DC Command or data - &pin_PA11, // TFT_CS Chip select - &pin_PA17, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PA16), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PA11), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA17), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/pybadge/board.c b/ports/atmel-samd/boards/pybadge/board.c index 545918374e270..e938e96e19e3c 100644 --- a/ports/atmel-samd/boards/pybadge/board.c +++ b/ports/atmel-samd/boards/pybadge/board.c @@ -56,9 +56,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PB05, // TFT_DC Command or data - &pin_PB07, // TFT_CS Chip select - &pin_PA00, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PB05), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PB07), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA00), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/pygamer/board.c b/ports/atmel-samd/boards/pygamer/board.c index 53275ac686a15..fcf6aff0e6556 100644 --- a/ports/atmel-samd/boards/pygamer/board.c +++ b/ports/atmel-samd/boards/pygamer/board.c @@ -57,9 +57,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PB05, // TFT_DC Command or data - &pin_PB12, // TFT_CS Chip select - &pin_PA00, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PB05), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PB12), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PA00), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/pygamer/mpconfigboard.mk b/ports/atmel-samd/boards/pygamer/mpconfigboard.mk index 19fa4f19b9de6..0c6f16f8b6381 100644 --- a/ports/atmel-samd/boards/pygamer/mpconfigboard.mk +++ b/ports/atmel-samd/boards/pygamer/mpconfigboard.mk @@ -12,6 +12,7 @@ LONGINT_IMPL = MPZ CIRCUITPY_AESIO = 0 CIRCUITPY_FLOPPYIO = 0 +CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_GIFIO = 0 CIRCUITPY_I2CDISPLAYBUS = 0 diff --git a/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c b/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c index d3d1ae3a956ff..1ee80c9878604 100644 --- a/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c +++ b/ports/atmel-samd/boards/seeeduino_wio_terminal/board.c @@ -52,9 +52,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PC06, // TFT_DC Command or data - &pin_PB21, // TFT_CS Chip select - &pin_PC07, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_PC06), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_PB21), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_PC07), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/boards/seeeduino_wio_terminal/mpconfigboard.mk b/ports/atmel-samd/boards/seeeduino_wio_terminal/mpconfigboard.mk index dd8afd51f6e60..4ade6a148315d 100644 --- a/ports/atmel-samd/boards/seeeduino_wio_terminal/mpconfigboard.mk +++ b/ports/atmel-samd/boards/seeeduino_wio_terminal/mpconfigboard.mk @@ -9,6 +9,14 @@ CHIP_FAMILY = samd51 QSPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" LONGINT_IMPL = MPZ + +CIRCUITPY__EVE = 1 +CIRCUITPY_CODEOP = 0 CIRCUITPY_FLOPPYIO = 0 -CIRCUITPY_FRAMEBUFFERIO = 0 +CIRCUITPY_JPEGIO = 0 +CIRCUITPY_MSGPACK = 0 +CIRCUITPY_PARALLELDISPLAYBUS = 0 +CIRCUITPY_RGBMATRIX = 0 +CIRCUITPY_SHARPDISPLAY = 0 CIRCUITPY_SYNTHIO = 0 +CIRCUITPY_VECTORIO = 0 diff --git a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk index d19e606319b63..441853c554df9 100644 --- a/ports/atmel-samd/boards/snekboard/mpconfigboard.mk +++ b/ports/atmel-samd/boards/snekboard/mpconfigboard.mk @@ -9,3 +9,6 @@ CHIP_FAMILY = samd21 SPI_FLASH_FILESYSTEM = 1 EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" LONGINT_IMPL = MPZ + +CIRCUITPY_CODEOP = 0 +CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk b/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk index 64049e8d5981c..b34dfb3d2e624 100755 --- a/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk +++ b/ports/atmel-samd/boards/sparkfun_redboard_turbo/mpconfigboard.mk @@ -11,4 +11,5 @@ EXTERNAL_FLASH_DEVICES = "W25Q32FV" LONGINT_IMPL = MPZ CIRCUITPY_CODEOP = 0 +CIRCUITPY_ERRNO = 0 CIRCUITPY_RAINBOWIO = 0 diff --git a/ports/atmel-samd/boards/ugame10/board.c b/ports/atmel-samd/boards/ugame10/board.c index bbf3839c516fb..78d03a3fd9a02 100644 --- a/ports/atmel-samd/boards/ugame10/board.c +++ b/ports/atmel-samd/boards/ugame10/board.c @@ -53,9 +53,9 @@ void board_init(void) { busio_spi_obj_t *spi = common_hal_board_create_spi(0); common_hal_fourwire_fourwire_construct(bus, spi, - &pin_PA09, // Command or data - &pin_PA08, // Chip select - NULL, // Reset + MP_OBJ_FROM_PTR(&pin_PA09), // Command or data + MP_OBJ_FROM_PTR(&pin_PA08), // Chip select + MP_OBJ_NULL, // Reset 24000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/atmel-samd/common-hal/alarm/__init__.c b/ports/atmel-samd/common-hal/alarm/__init__.c index 483aa1a9679ad..38ef58bea7659 100644 --- a/ports/atmel-samd/common-hal/alarm/__init__.c +++ b/ports/atmel-samd/common-hal/alarm/__init__.c @@ -133,7 +133,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala _setup_sleep_alarms(true, n_alarms, alarms); } -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_time_timealarm_prepare_for_deep_sleep(); // port_disable_tick(); // TODO: Required for SAMD? diff --git a/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c b/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c index 3c1fbb5ba7b32..de98b7ec85ae6 100644 --- a/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c +++ b/ports/atmel-samd/common-hal/alarm/touch/TouchAlarm.c @@ -7,6 +7,6 @@ #include "shared-bindings/alarm/touch/TouchAlarm.h" #include "shared-bindings/microcontroller/__init__.h" -NORETURN void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) { +MP_NORETURN void common_hal_alarm_touch_touchalarm_construct(alarm_touch_touchalarm_obj_t *self, const mcu_pin_obj_t *pin) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_TouchAlarm); } diff --git a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c index a1a67d0408247..f409563f5ed69 100644 --- a/ports/atmel-samd/common-hal/audiobusio/PDMIn.c +++ b/ports/atmel-samd/common-hal/audiobusio/PDMIn.c @@ -364,7 +364,7 @@ static uint16_t filter_sample(uint32_t pdm_samples[4]) { // output_buffer_length is the number of slots, not the number of bytes. uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t *self, uint16_t *output_buffer, uint32_t output_buffer_length) { - uint8_t dma_channel = dma_allocate_channel(true); + uint8_t dma_channel = dma_allocate_audio_channel(); pdmin_event_channel = find_sync_event_channel_raise(); pdmin_dma_block_done = false; diff --git a/ports/atmel-samd/common-hal/busio/I2C.c b/ports/atmel-samd/common-hal/busio/I2C.c index e13b5410ae32a..d9da050dabeae 100644 --- a/ports/atmel-samd/common-hal/busio/I2C.c +++ b/ports/atmel-samd/common-hal/busio/I2C.c @@ -135,6 +135,7 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { if (common_hal_busio_i2c_deinited(self)) { return; } + allow_reset_sercom(self->i2c_desc.device.hw); i2c_m_sync_disable(&self->i2c_desc); i2c_m_sync_deinit(&self->i2c_desc); @@ -175,7 +176,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { uint16_t attempts = ATTEMPTS; @@ -196,17 +197,17 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, if (status == I2C_OK) { return 0; } else if (status == I2C_ERR_BAD_ADDRESS) { - return MP_ENODEV; + return -MP_ENODEV; } - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { uint16_t attempts = ATTEMPTS; @@ -227,14 +228,14 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, if (status == ERR_NONE) { return 0; } else if (status == I2C_ERR_BAD_ADDRESS) { - return MP_ENODEV; + return -MP_ENODEV; } - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/atmel-samd/common-hal/busio/SPI.c b/ports/atmel-samd/common-hal/busio/SPI.c index 5d7633be7f8d3..19fd81e425d3e 100644 --- a/ports/atmel-samd/common-hal/busio/SPI.c +++ b/ports/atmel-samd/common-hal/busio/SPI.c @@ -21,7 +21,17 @@ #include "samd/dma.h" #include "samd/sercom.h" -void setup_pin(const mcu_pin_obj_t *pin, uint32_t pinmux); + +static void setup_pin(const mcu_pin_obj_t *pin, uint32_t pinmux, const enum gpio_direction direction) { + gpio_set_pin_direction(pin->number, direction); + gpio_set_pin_pull_mode(pin->number, GPIO_PULL_OFF); + gpio_set_pin_function(pin->number, pinmux); + if (direction == GPIO_DIRECTION_OUT) { + // Use strong drive strength for SPI outputs. + hri_port_set_PINCFG_DRVSTR_bit(PORT, (enum gpio_port)GPIO_PORT(pin->number), GPIO_PIN(pin->number)); + } + claim_pin(pin); +} void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, @@ -43,7 +53,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } // Ensure the object starts in its deinit state. - self->clock_pin = NO_PIN; + common_hal_busio_spi_mark_deinit(self); // Special case for SAMR21 boards. (feather_radiofruit_zigbee) #if defined(PIN_PC19F_SERCOM4_PAD0) @@ -128,6 +138,7 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, // Pads must be set after spi_m_sync_init(), which uses default values from // the prototypical SERCOM. + // Set to SPI host mode and choose pads. hri_sercomspi_write_CTRLA_MODE_bf(sercom, 3); hri_sercomspi_write_CTRLA_DOPO_bf(sercom, dopo); hri_sercomspi_write_CTRLA_DIPO_bf(sercom, miso_pad); @@ -141,20 +152,20 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_OSError(MP_EIO); } - setup_pin(clock, clock_pinmux); + setup_pin(clock, clock_pinmux, GPIO_DIRECTION_OUT); self->clock_pin = clock->number; if (mosi_none) { self->MOSI_pin = NO_PIN; } else { - setup_pin(mosi, mosi_pinmux); + setup_pin(mosi, mosi_pinmux, GPIO_DIRECTION_OUT); self->MOSI_pin = mosi->number; } if (miso_none) { self->MISO_pin = NO_PIN; } else { - setup_pin(miso, miso_pinmux); + setup_pin(miso, miso_pinmux, GPIO_DIRECTION_IN); self->MISO_pin = miso->number; } @@ -173,6 +184,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock_pin == NO_PIN; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock_pin = NO_PIN; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; @@ -184,7 +199,9 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { reset_pin_number(self->clock_pin); reset_pin_number(self->MOSI_pin); reset_pin_number(self->MISO_pin); - self->clock_pin = NO_PIN; + + // This smashes self->clock_pin, so don't do it before resetting the pin above. + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, @@ -317,11 +334,3 @@ uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { void *hw = self->spi_desc.dev.prvt; return hri_sercomspi_get_CTRLA_CPOL_bit(hw); } - -void setup_pin(const mcu_pin_obj_t *pin, uint32_t pinmux) { - gpio_set_pin_direction(pin->number, GPIO_DIRECTION_OUT); - gpio_set_pin_pull_mode(pin->number, GPIO_PULL_OFF); - gpio_set_pin_function(pin->number, pinmux); - claim_pin(pin); - hri_port_set_PINCFG_DRVSTR_bit(PORT, (enum gpio_port)GPIO_PORT(pin->number), GPIO_PIN(pin->number)); -} diff --git a/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c index a6d8fef0f394b..56eb82678ec01 100644 --- a/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c +++ b/ports/atmel-samd/common-hal/imagecapture/ParallelImageCapture.c @@ -135,7 +135,8 @@ void common_hal_imagecapture_parallelimagecapture_singleshot_capture(imagecaptur mp_buffer_info_t bufinfo; mp_get_buffer_raise(buffer, &bufinfo, MP_BUFFER_RW); - uint8_t dma_channel = dma_allocate_channel(true); + // Allocate a permanent channel (not really audio). + uint8_t dma_channel = dma_allocate_audio_channel(); uint32_t *dest = bufinfo.buf; size_t count = bufinfo.len / 4; // PCC receives 4 bytes (2 pixels) at a time diff --git a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c index c6cd4605784ab..a8c87d82ed268 100644 --- a/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/atmel-samd/common-hal/rotaryio/IncrementalEncoder.c @@ -17,6 +17,10 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { + + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + if (!pin_a->has_extint) { raise_ValueError_invalid_pin_name(MP_QSTR_pin_a); } @@ -83,10 +87,13 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o turn_off_eic_channel(self->eic_channel_b); reset_pin_number(self->pin_a); - self->pin_a = NO_PIN; - reset_pin_number(self->pin_b); - self->pin_b = NO_PIN; + + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { + self->pin_a = NO_PIN; } void incrementalencoder_interrupt_handler(uint8_t channel) { diff --git a/ports/atmel-samd/common-hal/sdioio/SDCard.c b/ports/atmel-samd/common-hal/sdioio/SDCard.c index 8ff1a391bb265..e38a664ed48a8 100644 --- a/ports/atmel-samd/common-hal/sdioio/SDCard.c +++ b/ports/atmel-samd/common-hal/sdioio/SDCard.c @@ -9,6 +9,7 @@ #include "py/runtime.h" #include "common-hal/microcontroller/Pin.h" +#include "extmod/vfs.h" #include "shared-bindings/sdioio/SDCard.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" @@ -170,43 +171,85 @@ static void debug_print_state(sdioio_sdcard_obj_t *self, const char *what, sd_mm #endif } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { - check_for_deinit(self); - check_whole_block(bufinfo); +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); self->state_programming = true; - sd_mmc_err_t r = sd_mmc_init_write_blocks(0, start_block, bufinfo->len / 512); + sd_mmc_err_t r = sd_mmc_init_write_blocks(0, start_block, num_blocks); if (r != SD_MMC_OK) { debug_print_state(self, "sd_mmc_init_write_blocks", r); - return -EIO; + return -MP_EIO; } - r = sd_mmc_start_write_blocks(bufinfo->buf, bufinfo->len / 512); + r = sd_mmc_start_write_blocks(buf, num_blocks); if (r != SD_MMC_OK) { debug_print_state(self, "sd_mmc_start_write_blocks", r); - return -EIO; + return -MP_EIO; } - // debug_print_state(self, "after writeblocks OK"); return 0; } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { check_for_deinit(self); check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); - sd_mmc_err_t r = sd_mmc_init_read_blocks(0, start_block, bufinfo->len / 512); + sd_mmc_err_t r = sd_mmc_init_read_blocks(0, start_block, num_blocks); if (r != SD_MMC_OK) { debug_print_state(self, "sd_mmc_init_read_blocks", r); - return -EIO; + return -MP_EIO; } - r = sd_mmc_start_read_blocks(bufinfo->buf, bufinfo->len / 512); + r = sd_mmc_start_read_blocks(buf, num_blocks); if (r != SD_MMC_OK) { debug_print_state(self, "sd_mmc_start_read_blocks", r); - return -EIO; + return -MP_EIO; } sd_mmc_wait_end_of_write_blocks(0); return 0; } +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { + check_for_deinit(self); + check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } +} + bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) { check_for_deinit(self); return true; diff --git a/ports/atmel-samd/common-hal/spitarget/SPITarget.c b/ports/atmel-samd/common-hal/spitarget/SPITarget.c index b9062911cc353..e5011b141fc12 100644 --- a/ports/atmel-samd/common-hal/spitarget/SPITarget.c +++ b/ports/atmel-samd/common-hal/spitarget/SPITarget.c @@ -192,7 +192,7 @@ void common_hal_spitarget_spi_target_transfer_start(spitarget_spi_target_obj_t * self->miso_packet = miso_packet; Sercom *sercom = self->spi_desc.dev.prvt; - self->running_dma = shared_dma_transfer_start(sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0); + shared_dma_transfer_start(&self->running_dma, sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0); // There is an issue where if an unexpected SPI transfer is received before the user calls "end" for the in-progress, expected // transfer, the SERCOM has an error and gets confused. This can be detected from INTFLAG.ERROR. I think the code in @@ -200,7 +200,7 @@ void common_hal_spitarget_spi_target_transfer_start(spitarget_spi_target_obj_t * // s->SPI.DATA.reg) is supposed to fix this, but experimentation seems to show that it does not in fact fix anything. Anyways, if // the ERROR bit is set, let's just reset the peripheral and then setup the transfer again -- that seems to work. if (hri_sercomspi_get_INTFLAG_ERROR_bit(sercom)) { - shared_dma_transfer_close(self->running_dma); + shared_dma_transfer_close(&self->running_dma); // disable the sercom spi_m_sync_disable(&self->spi_desc); @@ -223,19 +223,19 @@ void common_hal_spitarget_spi_target_transfer_start(spitarget_spi_target_obj_t * spi_m_sync_enable(&self->spi_desc); hri_sercomspi_wait_for_sync(sercom, SERCOM_SPI_SYNCBUSY_MASK); - self->running_dma = shared_dma_transfer_start(sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0); + shared_dma_transfer_start(&self->running_dma, sercom, miso_packet, &sercom->SPI.DATA.reg, &sercom->SPI.DATA.reg, mosi_packet, len, 0); } } bool common_hal_spitarget_spi_target_transfer_is_finished(spitarget_spi_target_obj_t *self) { - return self->running_dma.failure == 1 || shared_dma_transfer_finished(self->running_dma); + return self->running_dma.failure == 1 || shared_dma_transfer_finished(&self->running_dma); } int common_hal_spitarget_spi_target_transfer_close(spitarget_spi_target_obj_t *self) { if (self->running_dma.failure == 1) { return 0; } - int res = shared_dma_transfer_close(self->running_dma); + int res = shared_dma_transfer_close(&self->running_dma); self->running_dma.failure = 1; self->mosi_packet = NULL; diff --git a/ports/atmel-samd/common-hal/spitarget/SPITarget.h b/ports/atmel-samd/common-hal/spitarget/SPITarget.h index 50f2bcb33094b..9fff23c174370 100644 --- a/ports/atmel-samd/common-hal/spitarget/SPITarget.h +++ b/ports/atmel-samd/common-hal/spitarget/SPITarget.h @@ -18,7 +18,7 @@ typedef struct { uint8_t *mosi_packet; const uint8_t *miso_packet; - dma_descr_t running_dma; + dma_transfer_t running_dma; } spitarget_spi_target_obj_t; #endif // MICROPY_INCLUDED_ATMEL_SAMD_COMMON_HAL_BUSIO_SPI_TARGET_H diff --git a/ports/atmel-samd/mpconfigport.mk b/ports/atmel-samd/mpconfigport.mk index 861ef37464633..b0947ea6f2529 100644 --- a/ports/atmel-samd/mpconfigport.mk +++ b/ports/atmel-samd/mpconfigport.mk @@ -14,6 +14,8 @@ CIRCUITPY_LTO = 1 CIRCUITPY_KEYPAD_DEMUX ?= 0 CIRCUITPY_LVFONTIO ?= 0 +CIRCUITPY_DIGITALINOUT_PROTOCOL = 0 + ###################################################################### # Put samd21-only choices here. @@ -46,10 +48,10 @@ CIRCUITPY_I2CTARGET ?= 0 CIRCUITPY_JSON ?= 0 CIRCUITPY_KEYPAD ?= 0 CIRCUITPY_MSGPACK ?= 0 -CIRCUITPY_OS_GETENV ?= 0 CIRCUITPY_PIXELMAP ?= 0 CIRCUITPY_RE ?= 0 CIRCUITPY_SDCARDIO ?= 0 +CIRCUITPY_SETTINGS_TOML ?= 0 CIRCUITPY_SPITARGET ?= 0 CIRCUITPY_SYNTHIO ?= 0 CIRCUITPY_TOUCHIO_USE_NATIVE ?= 1 diff --git a/ports/atmel-samd/peripherals b/ports/atmel-samd/peripherals index d3210221bbd01..da0a1d7dccb34 160000 --- a/ports/atmel-samd/peripherals +++ b/ports/atmel-samd/peripherals @@ -1 +1 @@ -Subproject commit d3210221bbd018ae9d0183ea4640c42cf4bce672 +Subproject commit da0a1d7dccb34b7fa07738e7a8ce25118c88d1a3 diff --git a/ports/atmel-samd/reset.c b/ports/atmel-samd/reset.c index 9cacd4ab951ae..816e23e3251db 100644 --- a/ports/atmel-samd/reset.c +++ b/ports/atmel-samd/reset.c @@ -9,7 +9,7 @@ #include "reset.h" #include "supervisor/filesystem.h" -void NVIC_SystemReset(void) NORETURN; +void NVIC_SystemReset(void) MP_NORETURN; void reset(void) { filesystem_flush(); diff --git a/ports/atmel-samd/reset.h b/ports/atmel-samd/reset.h index c74d25fa01ea5..248d43779fdc9 100644 --- a/ports/atmel-samd/reset.h +++ b/ports/atmel-samd/reset.h @@ -16,6 +16,6 @@ extern uint32_t _bootloader_dbl_tap; -void reset_to_bootloader(void) NORETURN; -void reset(void) NORETURN; +void reset_to_bootloader(void) MP_NORETURN; +void reset(void) MP_NORETURN; bool bootloader_available(void); diff --git a/ports/atmel-samd/sd_mmc/sd_mmc.c b/ports/atmel-samd/sd_mmc/sd_mmc.c index 60b4d9f8985a1..6724e88f42a7a 100644 --- a/ports/atmel-samd/sd_mmc/sd_mmc.c +++ b/ports/atmel-samd/sd_mmc/sd_mmc.c @@ -160,9 +160,9 @@ static struct sd_mmc_card sd_mmc_cards[CONF_SD_MMC_MEM_CNT]; /** HAL driver instance */ static void *sd_mmc_hal; -/** Index of current slot configurated */ +/** Index of current slot configured */ static uint8_t sd_mmc_slot_sel; -/** Pointer on current slot configurated */ +/** Pointer on current slot configured */ static struct sd_mmc_card *sd_mmc_card; /** Number of block to read or write on the current transfer */ static uint16_t sd_mmc_nb_block_to_tranfer = 0; @@ -238,7 +238,7 @@ static bool mmc_mci_op_cond(void) { uint32_t retry, resp; /* - * Timeout 1s = 400KHz / ((6+6)*8) cylces = 4200 retry + * Timeout 1s = 400KHz / ((6+6)*8) cycles = 4200 retry * 6 = cmd byte size * 6 = response byte size */ @@ -277,7 +277,7 @@ static bool sd_mci_op_cond(uint8_t v2) { uint32_t arg, retry, resp; /* - * Timeout 1s = 400KHz / ((6+6+6+6)*8) cylces = 2100 retry + * Timeout 1s = 400KHz / ((6+6+6+6)*8) cycles = 2100 retry * 6 = cmd byte size * 6 = response byte size * 6 = cmd byte size @@ -339,7 +339,7 @@ static bool sdio_op_cond(void) { /* * Wait card ready - * Timeout 1s = 400KHz / ((6+4)*8) cylces = 5000 retry + * Timeout 1s = 400KHz / ((6+4)*8) cycles = 5000 retry * 6 = cmd byte size * 4(SPI) 6(MCI) = response byte size */ @@ -1292,13 +1292,13 @@ static bool sd_mmc_mci_install_mmc(void) { void sd_mmc_init(void *hal, sd_mmc_detect_t *card_detects, sd_mmc_detect_t *wp_detects) { /* GPIO will be used to detect card and write protect. - * The related clocks and pinmux must be configurated in good + * The related clocks and pinmux must be configured in good * condition. */ for (uint8_t slot = 0; slot < CONF_SD_MMC_MEM_CNT; slot++) { sd_mmc_cards[slot].state = SD_MMC_CARD_STATE_NO_CARD; } - sd_mmc_slot_sel = 0xFF; /* No slot configurated */ + sd_mmc_slot_sel = 0xFF; /* No slot configured */ sd_mmc_hal = hal; _cd = card_detects; _wp = wp_detects; diff --git a/ports/atmel-samd/sd_mmc/sd_mmc.h b/ports/atmel-samd/sd_mmc/sd_mmc.h index cdc98e7108e2e..7b70a631164d5 100644 --- a/ports/atmel-samd/sd_mmc/sd_mmc.h +++ b/ports/atmel-samd/sd_mmc/sd_mmc.h @@ -113,7 +113,7 @@ typedef struct sd_mmc_detect { uint16_t val; /**< Detection value */ } sd_mmc_detect_t; -/** This SD MMC stack uses the maximum block size autorized (512 bytes) */ +/** This SD MMC stack uses the maximum block size authorized (512 bytes) */ #define SD_MMC_BLOCK_SIZE 512 /** diff --git a/ports/atmel-samd/supervisor/port.c b/ports/atmel-samd/supervisor/port.c index 03c2c2543a7a7..288446cf62e71 100644 --- a/ports/atmel-samd/supervisor/port.c +++ b/ports/atmel-samd/supervisor/port.c @@ -339,6 +339,7 @@ safe_mode_t port_init(void) { init_shared_dma(); // Reset everything into a known state before board_init. + // Pins are reset in main() after this routine returns. reset_port(); #ifdef SAMD21 @@ -411,8 +412,6 @@ void reset_port(void) { reset_ticks(); } - reset_all_pins(); - // Output clocks for debugging. // not supported by SAMD51G; uncomment for SAMD51J or update for 51G // #ifdef SAM_D5X_E5X @@ -686,7 +685,7 @@ void port_idle_until_interrupt(void) { /** * \brief Default interrupt handler for unused IRQs. */ -__attribute__((used)) NORETURN void HardFault_Handler(void) { +__attribute__((used)) MP_NORETURN void HardFault_Handler(void) { #ifdef ENABLE_MICRO_TRACE_BUFFER // Turn off the micro trace buffer so we don't fill it up in the infinite // loop below. diff --git a/ports/broadcom/Makefile b/ports/broadcom/Makefile index f60ebbf045e1f..dda93c6fd7ce0 100644 --- a/ports/broadcom/Makefile +++ b/ports/broadcom/Makefile @@ -59,6 +59,8 @@ SRC_C += bindings/videocore/__init__.c \ SRC_S = peripherals/broadcom/boot$(SUFFIX).s +SRC_C += shared/runtime/gchelper_generic.c + OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) ifeq ($(INTERNAL_LIBM),1) diff --git a/ports/broadcom/common-hal/busio/I2C.c b/ports/broadcom/common-hal/busio/I2C.c index 6a77cec08af83..7c1eafe281dc9 100644 --- a/ports/broadcom/common-hal/busio/I2C.c +++ b/ports/broadcom/common-hal/busio/I2C.c @@ -137,7 +137,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { // Discussion of I2C implementation is here: https://github.com/raspberrypi/linux/issues/254 -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { COMPLETE_MEMORY_READS; self->peripheral->S_b.DONE = true; @@ -182,7 +182,7 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, while (self->peripheral->S_b.ERR == 1) { RUN_BACKGROUND_TASKS; } - return MP_ENODEV; + return -MP_ENODEV; } if (loop_len < len) { @@ -192,19 +192,19 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, return 0; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { COMPLETE_MEMORY_READS; self->peripheral->A_b.ADDR = addr; if (self->finish_write) { self->finish_write = false; if (self->peripheral->S_b.ERR == 1) { - return MP_ENODEV; + return -MP_ENODEV; } self->peripheral->FIFO_b.DATA = self->last_write_data; } else { @@ -236,15 +236,15 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, while (self->peripheral->S_b.ERR == 1) { RUN_BACKGROUND_TASKS; } - return MP_ENODEV; + return -MP_ENODEV; } return 0; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/broadcom/common-hal/busio/SPI.c b/ports/broadcom/common-hal/busio/SPI.c index 2396a9b921ee2..9e4834e86f352 100644 --- a/ports/broadcom/common-hal/busio/SPI.c +++ b/ports/broadcom/common-hal/busio/SPI.c @@ -30,31 +30,8 @@ static SPI0_Type *spi[NUM_SPI] = {SPI0, NULL, NULL}; static SPI1_Type *aux_spi[NUM_SPI] = {NULL, SPI1, SPI2}; #endif -static bool never_reset_spi[NUM_SPI]; static bool spi_in_use[NUM_SPI]; -void reset_spi(void) { - for (size_t i = 0; i < NUM_SPI; i++) { - if (never_reset_spi[i]) { - continue; - } - - if (i == 1 || i == 2) { - if (i == 1) { - AUX->ENABLES_b.SPI_1 = false; - } else { - AUX->ENABLES_b.SPI_2 = false; - } - aux_spi[i]->CNTL0 = 0; - } else { - // Set CS back to default. All 0 except read enable. - spi[i]->CS = SPI0_CS_REN_Msk; - } - - spi_in_use[i] = false; - } -} - void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { @@ -67,6 +44,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_NotImplementedError(MP_ERROR_TEXT("Half duplex SPI is not implemented")); } + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + // BCM_VERSION != 2711 have 3 SPI but as listed in peripherals/gen/pins.c two are on // index 0, once one index 0 SPI is found the other will throw an invalid_pins error. for (size_t i = 0; i < NUM_SPI; i++) { @@ -118,8 +98,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset_spi[self->index] = true; - common_hal_never_reset_pin(self->clock); common_hal_never_reset_pin(self->MOSI); common_hal_never_reset_pin(self->MISO); @@ -129,16 +107,19 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; } - never_reset_spi[self->index] = false; common_hal_reset_pin(self->clock); common_hal_reset_pin(self->MOSI); common_hal_reset_pin(self->MISO); - self->clock = NULL; + spi_in_use[self->index] = false; if (self->index == 1 || @@ -149,7 +130,12 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { } else if (self->index == 2) { AUX->ENABLES_b.SPI_2 = false; } + } else { + // Set CS back to default. All 0 except read enable. + spi[self->index]->CS = SPI0_CS_REN_Msk; } + + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, diff --git a/ports/broadcom/common-hal/busio/SPI.h b/ports/broadcom/common-hal/busio/SPI.h index aec91263677c0..3d52b6b0a4488 100644 --- a/ports/broadcom/common-hal/busio/SPI.h +++ b/ports/broadcom/common-hal/busio/SPI.h @@ -23,5 +23,3 @@ typedef struct { uint8_t bits; uint8_t index; } busio_spi_obj_t; - -void reset_spi(void); diff --git a/ports/broadcom/common-hal/sdioio/SDCard.c b/ports/broadcom/common-hal/sdioio/SDCard.c index f87d40cd8dcab..2c9e6a2bb587a 100644 --- a/ports/broadcom/common-hal/sdioio/SDCard.c +++ b/ports/broadcom/common-hal/sdioio/SDCard.c @@ -11,6 +11,7 @@ #include "py/runtime.h" #include "common-hal/microcontroller/Pin.h" +#include "extmod/vfs.h" #include "shared-bindings/sdioio/SDCard.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" @@ -304,44 +305,95 @@ static void check_whole_block(mp_buffer_info_t *bufinfo) { } } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +// Native function for VFS blockdev layer +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!self->init) { - return -EIO; + return -MP_EIO; } - check_whole_block(bufinfo); self->state_programming = true; COMPLETE_MEMORY_READS; - sdmmc_err_t error = sdmmc_write_sectors(&self->card_info, bufinfo->buf, - start_block, bufinfo->len / 512); + sdmmc_err_t error = sdmmc_write_sectors(&self->card_info, buf, + start_block, num_blocks); COMPLETE_MEMORY_READS; if (error != SDMMC_OK) { mp_printf(&mp_plat_print, "write sectors result %d\n", error); - return -EIO; + return -MP_EIO; } return 0; } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (!self->init) { - return -EIO; + return -MP_EIO; } check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (!self->init) { + return -MP_EIO; + } COMPLETE_MEMORY_READS; - sdmmc_err_t error = sdmmc_read_sectors(&self->card_info, bufinfo->buf, - start_block, bufinfo->len / 512); + sdmmc_err_t error = sdmmc_read_sectors(&self->card_info, buf, + start_block, num_blocks); COMPLETE_MEMORY_READS; if (error != SDMMC_OK) { - mp_printf(&mp_plat_print, "read sectors result %d when reading block %d for %d\n", error, start_block, bufinfo->len / 512); - return -EIO; + mp_printf(&mp_plat_print, "read sectors result %d when reading block %d for %d\n", error, start_block, num_blocks); + return -MP_EIO; } return 0; } +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { + if (!self->init) { + return -MP_EIO; + } + check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } +} + bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) { if (!self->init) { return false; diff --git a/ports/broadcom/mpconfigport.h b/ports/broadcom/mpconfigport.h index 8b749ca03100a..73fae8295ded2 100644 --- a/ports/broadcom/mpconfigport.h +++ b/ports/broadcom/mpconfigport.h @@ -26,6 +26,8 @@ #define MICROPY_FATFS_EXFAT (1) #define MICROPY_FATFS_MKFS_FAT32 (1) +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) + //////////////////////////////////////////////////////////////////////////////////////////////////// // This also includes mpconfigboard.h. diff --git a/ports/broadcom/mpconfigport.mk b/ports/broadcom/mpconfigport.mk index d906b5d2aa467..844794b107a21 100644 --- a/ports/broadcom/mpconfigport.mk +++ b/ports/broadcom/mpconfigport.mk @@ -22,7 +22,6 @@ CIRCUITPY_VIDEOCORE = 1 INTERNAL_FLASH_FILESYSTEM = 1 USB_NUM_ENDPOINT_PAIRS = 8 -USB_HIGHSPEED = 1 CIRCUITPY_BUILD_EXTENSIONS ?= disk.img.zip,kernel8.img diff --git a/ports/broadcom/supervisor/port.c b/ports/broadcom/supervisor/port.c index 83dd50f1b8b52..a0f0be30dd758 100644 --- a/ports/broadcom/supervisor/port.c +++ b/ports/broadcom/supervisor/port.c @@ -67,7 +67,6 @@ safe_mode_t port_init(void) { void reset_port(void) { #if CIRCUITPY_BUSIO reset_i2c(); - reset_spi(); reset_uart(); #endif @@ -85,8 +84,6 @@ void reset_port(void) { #if CIRCUITPY_AUDIOCORE audio_dma_reset(); #endif - - reset_all_pins(); } void reset_to_bootloader(void) { diff --git a/ports/cxd56/Makefile b/ports/cxd56/Makefile index fdf3646c80d27..88c3921022061 100644 --- a/ports/cxd56/Makefile +++ b/ports/cxd56/Makefile @@ -74,7 +74,7 @@ CFLAGS += \ -fdata-sections \ -Wall \ -OPTIMIZATION_FLAGS ?= -O2 -fno-inline-functions +OPTIMIZATION_FLAGS ?= -O2 # option to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk CFLAGS += $(OPTIMIZATION_FLAGS) @@ -110,7 +110,9 @@ LDFLAGS = \ CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_CXD56 -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_CDC_RX_BUFSIZE=1024 -DCFG_TUD_MIDI_TX_BUFSIZE=512 -DCFG_TUD_CDC_TX_BUFSIZE=1024 -DCFG_TUD_MSC_BUFSIZE=512 $(CFLAGS_MOD) -SRC_S_UPPER = supervisor/shared/cpu_regs.S +SRC_S = shared/runtime/gchelper_thumb2.s + +SRC_C += shared/runtime/gchelper_native.c SRC_C += \ background.c \ @@ -120,7 +122,7 @@ SRC_C += \ lib/tinyusb/src/portable/sony/cxd56/dcd_cxd56.c \ OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) diff --git a/ports/cxd56/common-hal/busio/I2C.c b/ports/cxd56/common-hal/busio/I2C.c index fb33c242a0367..8e846b2402dc3 100644 --- a/ports/cxd56/common-hal/busio/I2C.c +++ b/ports/cxd56/common-hal/busio/I2C.c @@ -91,7 +91,7 @@ bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { return I2C_TRANSFER(self->i2c_dev, &msg, 1) < 0 ? false : true; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, const uint8_t *data, size_t len, bool stop) { +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, const uint8_t *data, size_t len, bool stop) { struct i2c_msg_s msg; msg.frequency = self->frequency; @@ -102,12 +102,12 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addre return -I2C_TRANSFER(self->i2c_dev, &msg, 1); } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *data, size_t len) { struct i2c_msg_s msg; msg.frequency = self->frequency; @@ -118,9 +118,9 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8 return -I2C_TRANSFER(self->i2c_dev, &msg, 1); } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/cxd56/common-hal/busio/SPI.c b/ports/cxd56/common-hal/busio/SPI.c index 8eedb624201dd..6c49e0f275066 100644 --- a/ports/cxd56/common-hal/busio/SPI.c +++ b/ports/cxd56/common-hal/busio/SPI.c @@ -54,7 +54,7 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { return; } - self->spi_dev = NULL; + common_hal_busio_spi_mark_deinit(self); reset_pin_number(self->clock_pin->number); reset_pin_number(self->mosi_pin->number); @@ -65,6 +65,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->spi_dev == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->spi_dev = NULL; +} + bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { uint8_t mode; diff --git a/ports/cxd56/common-hal/microcontroller/__init__.c b/ports/cxd56/common-hal/microcontroller/__init__.c index fa872f2807ba0..f7ac0c174ae07 100644 --- a/ports/cxd56/common-hal/microcontroller/__init__.c +++ b/ports/cxd56/common-hal/microcontroller/__init__.c @@ -61,6 +61,8 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { void common_hal_mcu_reset(void) { filesystem_flush(); boardctl(BOARDIOC_RESET, 0); + // boardctl is noreturn in this case. + __builtin_unreachable(); } static const mp_rom_map_elem_t mcu_pin_globals_table[] = { diff --git a/ports/cxd56/common-hal/sdioio/SDCard.c b/ports/cxd56/common-hal/sdioio/SDCard.c index aa1d137177066..227011a88710f 100644 --- a/ports/cxd56/common-hal/sdioio/SDCard.c +++ b/ports/cxd56/common-hal/sdioio/SDCard.c @@ -8,6 +8,7 @@ #include #include +#include "extmod/vfs.h" #include "py/mperrno.h" #include "py/runtime.h" @@ -95,22 +96,73 @@ static void check_whole_block(mp_buffer_info_t *bufinfo) { } } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +// Native function for VFS blockdev layer +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + int result = self->inode->u.i_bops->read(self->inode, buf, start_block, num_blocks); + if (result < 0) { + return -MP_EIO; + } + return 0; +} + +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (common_hal_sdioio_sdcard_deinited(self)) { raise_deinited_error(); } check_whole_block(bufinfo); - return self->inode->u.i_bops->read(self->inode, bufinfo->buf, start_block, bufinfo->len / 512); + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +// Native function for VFS blockdev layer +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + int result = self->inode->u.i_bops->write(self->inode, buf, start_block, num_blocks); + if (result < 0) { + return -MP_EIO; + } + return 0; +} + +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { if (common_hal_sdioio_sdcard_deinited(self)) { raise_deinited_error(); } check_whole_block(bufinfo); - return self->inode->u.i_bops->write(self->inode, bufinfo->buf, start_block, bufinfo->len / 512); + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } } void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self) { diff --git a/ports/cxd56/mkspk/clefia.c b/ports/cxd56/mkspk/clefia.c index e946ee5348a5a..d36f638fa0b0d 100644 --- a/ports/cxd56/mkspk/clefia.c +++ b/ports/cxd56/mkspk/clefia.c @@ -466,16 +466,16 @@ int clefiakeyset(unsigned char *rk, const unsigned char *skey) { void clefiaencrypt(unsigned char *ct, const unsigned char *pt, const unsigned char *rk, const int r) { - unsigned char rin[16]; + unsigned char r_in[16]; unsigned char rout[16]; - bytecpy(rin, pt, 16); + bytecpy(r_in, pt, 16); - bytexor(rin + 4, rin + 4, rk + 0, 4); /* initial key whitening */ - bytexor(rin + 12, rin + 12, rk + 4, 4); + bytexor(r_in + 4, r_in + 4, rk + 0, 4); /* initial key whitening */ + bytexor(r_in + 12, r_in + 12, rk + 4, 4); rk += 8; - clefiagfn4(rout, rin, rk, r); /* GFN_{4,r} */ + clefiagfn4(rout, r_in, rk, r); /* GFN_{4,r} */ bytecpy(ct, rout, 16); bytexor(ct + 4, ct + 4, rk + r * 8 + 0, 4); /* final key whitening */ diff --git a/ports/cxd56/mpconfigport.h b/ports/cxd56/mpconfigport.h index 3bcb252868786..ec606a76547bc 100644 --- a/ports/cxd56/mpconfigport.h +++ b/ports/cxd56/mpconfigport.h @@ -8,6 +8,9 @@ #define MICROPY_PY_SYS_PLATFORM "CXD56" +// SD card socket on board is configured for sdioio, which is not supported for automatic USB presentation. +#define CIRCUITPY_SDCARD_USB (0) + // 64kiB stack #define CIRCUITPY_DEFAULT_STACK_SIZE (0x10000) @@ -20,6 +23,8 @@ #define USB_MSC_EP_NUM_OUT (5) #define USB_MSC_EP_NUM_IN (4) +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) + #include "py/circuitpy_mpconfig.h" #define MICROPY_BYTES_PER_GC_BLOCK (32) diff --git a/ports/cxd56/mpconfigport.mk b/ports/cxd56/mpconfigport.mk index cb54233a07e51..ebfaa91f251ae 100644 --- a/ports/cxd56/mpconfigport.mk +++ b/ports/cxd56/mpconfigport.mk @@ -1,5 +1,3 @@ -USB_HIGHSPEED = 1 - # Number of USB endpoint pairs. USB_NUM_ENDPOINT_PAIRS = 6 diff --git a/ports/cxd56/supervisor/port.c b/ports/cxd56/supervisor/port.c index 75abcbf6d93e0..3dc54df96fbb5 100644 --- a/ports/cxd56/supervisor/port.c +++ b/ports/cxd56/supervisor/port.c @@ -66,8 +66,6 @@ void reset_port(void) { #if CIRCUITPY_RTC rtc_reset(); #endif - - reset_all_pins(); } void reset_to_bootloader(void) { diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index a063101991fdb..2545b33d22a39 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -8,6 +8,8 @@ include ../../py/circuitpy_mkenv.mk ifeq ($(IDF_TARGET),esp32s3) BT_IDF_TARGET = esp32c3 +else ifeq ($(IDF_TARGET),esp32c61) +BT_IDF_TARGET = esp32c6 else BT_IDF_TARGET = $(IDF_TARGET) endif @@ -64,6 +66,7 @@ INC += \ -isystem esp-idf/components/esp_driver_i2s/include \ -isystem esp-idf/components/esp_driver_$(IDF_TARGET)/include \ -isystem esp-idf/components/esp_driver_ledc/include \ + -isystem esp-idf/components/esp_driver_parlio/include \ -isystem esp-idf/components/esp_driver_pcnt/include \ -isystem esp-idf/components/esp_driver_rmt/include \ -isystem esp-idf/components/esp_driver_sdio/include \ @@ -73,6 +76,7 @@ INC += \ -isystem esp-idf/components/esp_driver_uart/include \ -isystem esp-idf/components/esp_event/include \ -isystem esp-idf/components/esp_hw_support/dma/include \ + -isystem esp-idf/components/esp_hw_support/ldo/include \ -isystem esp-idf/components/esp_hw_support/include \ -isystem esp-idf/components/esp_hw_support/include/soc \ -isystem esp-idf/components/esp_hw_support/port/$(IDF_TARGET)/private_include \ @@ -119,6 +123,9 @@ INC += \ -isystem esp-idf/components/soc/include \ -isystem esp-idf/components/soc/$(IDF_TARGET)/include \ -isystem esp-idf/components/soc/$(IDF_TARGET)/register \ + -isystem esp-idf/components/soc/$(IDF_TARGET)/register/hw_ver3 \ + -isystem esp-idf/components/soc/$(IDF_TARGET)/register/hw_ver2 \ + -isystem esp-idf/components/soc/$(IDF_TARGET)/register/hw_ver1 \ -isystem esp-idf/components/spi_flash/include \ -isystem esp-idf/components/usb/include \ -isystem esp-idf/components/ulp/ulp_fsm/include \ @@ -149,6 +156,8 @@ CFLAGS += -DSTACK_CANARY_VALUE=0xa5a5a5a5 REGISTRATION_FUNCTIONS = \ -u ld_include_highint_hdl \ -u __cxx_fatal_exception \ + -u __cxx_init_dummy \ + -u __cxa_guard_dummy \ -u esp_app_desc \ -u esp_timer_init_include_func \ -u uart_vfs_include_dev_init \ @@ -156,13 +165,18 @@ REGISTRATION_FUNCTIONS = \ -u __ubsan_include \ -u esp_system_include_startup_funcs \ -u esp_efuse_startup_include_func \ - -u newlib_include_heap_impl \ - -u newlib_include_syscalls_impl \ - -u newlib_include_pthread_impl \ - -u newlib_include_assert_impl \ - -u newlib_include_init_funcs \ + -u esp_libc_include_heap_impl \ + -u esp_libc_include_reent_syscalls_impl \ + -u esp_libc_include_syscalls_impl \ + -u esp_libc_include_pthread_impl \ + -u esp_libc_include_assert_impl \ + -u esp_libc_include_getentropy_impl \ + -u esp_libc_include_init_funcs \ + -u esp_libc_init_funcs \ -u include_esp_phy_override \ - -u vfs_include_syscalls_impl + -u vfs_include_syscalls_impl \ + -u esp_vfs_include_nullfs_register \ + -u usb_serial_jtag_vfs_include_dev_init #Debugging/Optimization @@ -196,7 +210,6 @@ CFLAGS += $(INC) -Werror -Wall -std=gnu11 -Wl,--gc-sections $(BASE_CFLAGS) $(C_D # Most current ESPs have nano versions of newlib in ROM so we use them. ifneq ($(IDF_TARGET),esp32c6) CFLAGS += --specs=nano.specs - LDFLAGS += -T$(IDF_TARGET).rom.newlib-nano.ld else LDFLAGS += -T$(IDF_TARGET).rom.newlib-normal.ld endif @@ -206,9 +219,12 @@ ifeq ($(IDF_TARGET_ARCH),xtensa) # `#include "xtensa/xtensa_api.h"`. CFLAGS += -mlongcalls -isystem esp-idf/components/xtensa/deprecated_include/ -Wno-error=cpp + CFLAGS += -DMICROPY_GCREGS_SETJMP=1 # Wrap longjmp with a patched version that protects register window update with a critical section LDFLAGS += -Wl,--wrap=longjmp + + SRC_C += shared/runtime/gchelper_generic.c else ifeq ($(IDF_TARGET_ARCH),riscv) ifeq ($(IDF_TARGET),esp32p4) @@ -220,9 +236,11 @@ else ifeq ($(IDF_TARGET_ARCH),riscv) LDFLAGS += \ -Lesp-idf/components/riscv/ld \ -Trom.api.ld + + SRC_C += shared/runtime/gchelper_native.c + SRC_S = shared/runtime/gchelper_rv32i.s endif -$(BUILD)/lib/tlsf/tlsf.o: CFLAGS += -Wno-cast-align LDFLAGS += $(CFLAGS) -Wl,-nostdlib -Wl,-Map=$@.map -Wl,-cref -Wl,--undefined=uxTopUsedPriority @@ -244,7 +262,9 @@ LDFLAGS += \ ifeq ($(IDF_TARGET),esp32) LDFLAGS += \ -Tesp32.rom.newlib-data.ld \ - -Tesp32.rom.newlib-funcs.ld \ + -Tesp32.rom.syscalls.ld \ + -Tesp32.rom.libc-funcs.ld \ + -Tesp32.rom.newlib-reent-funcs.ld \ -Tesp32.rom.spiflash_legacy.ld CHIP_COMPONENTS = \ @@ -254,7 +274,9 @@ else ifeq ($(IDF_TARGET),esp32c2) LDFLAGS += \ -Tesp32c2.rom.ble.ld \ -Tesp32c2.rom.heap.ld \ + -Tesp32c2.rom.libc.ld \ -Tesp32c2.rom.newlib.ld \ + -Tesp32c2.rom.newlib-nano.ld \ -Tesp32c2.rom.version.ld \ -Tesp32c2.rom.systimer.ld \ -Tesp32c2.rom.wdt.ld @@ -267,6 +289,7 @@ CHIP_COMPONENTS = \ else ifeq ($(IDF_TARGET),esp32c3) LDFLAGS += \ -Tesp32c3.rom.newlib.ld \ + -Tesp32c3.rom.libc.ld \ -Tesp32c3.rom.version.ld \ -Tesp32c3.rom.eco3_bt_funcs.ld \ -Tesp32c3.rom.eco3.ld \ @@ -280,6 +303,7 @@ LDFLAGS += \ -Tesp32c6.rom.phy.ld \ -Tesp32c6.rom.pp.ld \ -Tesp32c6.rom.net80211.ld \ + -Tesp32c6.rom.libc.ld \ -Tesp32c6.rom.newlib.ld \ -Tesp32c6.rom.coexist.ld \ -Tesp32c6.rom.heap.ld \ @@ -287,22 +311,42 @@ LDFLAGS += \ -Tesp32c6.rom.wdt.ld +CHIP_COMPONENTS = \ + esp_driver_tsens + +else ifeq ($(IDF_TARGET),esp32c61) +LDFLAGS += \ + -Tesp32c61.rom.phy.ld \ + -Tesp32c61.rom.pp.ld \ + -Tesp32c61.rom.net80211.ld \ + -Tesp32c61.rom.libc.ld \ + -Tesp32c61.rom.newlib.ld \ + -Tesp32c61.rom.version.ld \ + -Tesp32c61.rom.coexist.ld \ + -Tesp32c61.rom.heap.ld \ + -Tesp32c61.rom.systimer.ld \ + -Tesp32c61.rom.wdt.ld + + CHIP_COMPONENTS = \ esp_driver_tsens else ifeq ($(IDF_TARGET),esp32p4) LDFLAGS += \ + -Tesp32p4.rom.libc.ld \ -Tesp32p4.rom.newlib.ld \ -Tesp32p4.rom.systimer.ld \ -Tesp32p4.rom.wdt.ld CHIP_COMPONENTS = \ - esp_driver_tsens + esp_driver_tsens \ + esp_driver_usb_serial_jtag else ifeq ($(IDF_TARGET),esp32h2) LDFLAGS += \ -Tesp32h2.rom.heap.ld \ + -Tesp32h2.rom.libc.ld \ -Tesp32h2.rom.newlib.ld \ -Tesp32h2.rom.systimer.ld \ -Tesp32h2.rom.wdt.ld @@ -312,8 +356,9 @@ CHIP_COMPONENTS = \ else ifeq ($(IDF_TARGET),esp32s2) LDFLAGS += \ + -Tesp32s2.rom.libc-funcs.ld \ -Tesp32s2.rom.newlib-data.ld \ - -Tesp32s2.rom.newlib-funcs.ld \ + -Tesp32s2.rom.newlib-reent-funcs.ld \ -Tesp32s2.rom.spiflash_legacy.ld CHIP_COMPONENTS = \ @@ -322,6 +367,7 @@ CHIP_COMPONENTS = \ else ifeq ($(IDF_TARGET),esp32s3) LDFLAGS += \ + -Tesp32s3.rom.libc.ld \ -Tesp32s3.rom.newlib.ld \ -Tesp32s3.rom.version.ld \ -Tesp32s3.rom.systimer.ld \ @@ -350,6 +396,8 @@ else ifeq ($(IDF_TARGET),esp32c3) CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32C3 else ifeq ($(IDF_TARGET),esp32c6) CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32C6 +else ifeq ($(IDF_TARGET),esp32c61) +CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32C61 else ifeq ($(IDF_TARGET),esp32p4) CFLAGS += -DCFG_TUSB_MCU=OPT_MCU_ESP32P4 else ifeq ($(IDF_TARGET),esp32h2) @@ -431,6 +479,13 @@ CFLAGS += \ -isystem esp-idf/components/esp_lcd/rgb/include endif +ifneq ($(CIRCUITPY_MIPIDSI),0) +CFLAGS += \ + -isystem esp-idf/components/esp_lcd/include \ + -isystem esp-idf/components/esp_lcd/interface \ + -isystem esp-idf/components/esp_lcd/dsi/include +endif + ifneq ($(CIRCUITPY_ESPCAMERA),0) SRC_CAMERA := \ $(wildcard common-hal/espcamera/*.c) \ @@ -539,8 +594,6 @@ FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) endif -SRC_S_UPPER = supervisor/shared/cpu_regs.S - OBJ += $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) ifeq ($(INTERNAL_LIBM),1) @@ -548,7 +601,7 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) $(BUILD)/$(FATFS_DIR)/ff.o: COPT += -Os $(filter $(PY_BUILD)/../extmod/vfs_fat_%.o, $(PY_O)): COPT += -Os @@ -662,7 +715,8 @@ ifneq ($(IDF_TARGET),esp32p4) BINARY_BLOBS = esp-idf/components/esp_phy/lib/$(IDF_TARGET)/libphy.a endif ifneq ($(CIRCUITPY_WIFI),0) - BINARY_BLOBS += esp-idf/components/esp_coex/lib/$(IDF_TARGET)/libcoexist.a $(addprefix esp-idf/components/esp_wifi/lib/$(IDF_TARGET)/, $(BINARY_WIFI_BLOBS)) + BINARY_BLOBS += $(addprefix esp-idf/components/esp_wifi/lib/$(IDF_TARGET)/, $(BINARY_WIFI_BLOBS)) + BINARY_BLOBS += esp-idf/components/esp_coex/lib/$(IDF_TARGET)/libcoexist.a ifneq ($(IDF_TARGET),esp32c2) BINARY_BLOBS += $(addprefix esp-idf/components/esp_wifi/lib/$(IDF_TARGET)/, libmesh.a libwapi.a) endif @@ -682,6 +736,7 @@ ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) BLE_IMPL_esp32c2 := libble BLE_IMPL_esp32c3 := esp32c3 BLE_IMPL_esp32c6 := libble + BLE_IMPL_esp32c61 := libble BLE_IMPL_esp32h2 := libble BLE_IMPL = $(BLE_IMPL_$(IDF_TARGET)) @@ -700,8 +755,8 @@ ifneq ($(CIRCUITPY_BLEIO_NATIVE),0) ifeq ($(BLE_IMPL),libble) BINARY_BLOBS += esp-idf/components/esp_phy/lib/$(IDF_TARGET)/libbtbb.a - ifeq ($(IDF_TARGET),esp32c6) - BINARY_BLOBS += esp-idf/components/bt/controller/lib_$(IDF_TARGET)/$(IDF_TARGET)-bt-lib/$(IDF_TARGET)/libble_app.a + ifeq ($(BT_IDF_TARGET),esp32c6) + BINARY_BLOBS += esp-idf/components/bt/controller/lib_$(BT_IDF_TARGET)/$(BT_IDF_TARGET)-bt-lib/$(IDF_TARGET)/libble_app.a else BINARY_BLOBS += esp-idf/components/bt/controller/lib_$(IDF_TARGET)/$(IDF_TARGET)-bt-lib/libble_app.a endif @@ -719,9 +774,15 @@ endif ifneq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),0) ESP_IDF_COMPONENTS_LINK += esp_lcd endif +ifneq ($(CIRCUITPY_MIPIDSI),0) + ESP_IDF_COMPONENTS_LINK += esp_lcd +endif ifneq ($(CIRCUITPY_PARALLELDISPLAYBUS),0) ESP_IDF_COMPONENTS_LINK += esp_lcd endif +ifneq ($(CIRCUITPY_QSPIBUS),0) + ESP_IDF_COMPONENTS_LINK += esp_lcd +endif ifneq ($(CIRCUITPY_USB_DEVICE),0) ESP_IDF_COMPONENTS_LINK += usb endif @@ -769,6 +830,8 @@ else ifeq ($(IDF_TARGET),esp32c3) BOOTLOADER_OFFSET = 0x0 else ifeq ($(IDF_TARGET),esp32c6) BOOTLOADER_OFFSET = 0x0 +else ifeq ($(IDF_TARGET),esp32c61) +BOOTLOADER_OFFSET = 0x0 else ifeq ($(IDF_TARGET),esp32p4) BOOTLOADER_OFFSET = 0x2000 else ifeq ($(IDF_TARGET),esp32s3) @@ -782,7 +845,7 @@ endif IDF_CMAKE_TARGETS = \ bootloader/bootloader.bin \ - esp-idf/esp_system/__ldgen_output_sections.ld \ + __ldgen_output_sections.ld \ $(foreach component, $(ESP_IDF_COMPONENTS_LINK), esp-idf/$(component)/lib$(component).a) PARTITION_TABLE_OFFSET = 0x8000 diff --git a/ports/espressif/README.rst b/ports/espressif/README.rst index 63174fd167067..fe5542aafe46f 100644 --- a/ports/espressif/README.rst +++ b/ports/espressif/README.rst @@ -222,4 +222,3 @@ And follow the Espressif GDB tutorial `instructions for connecting base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO40, // TFT_DC Command or data - &pin_GPIO39, // TFT_CS Chip select - &pin_GPIO38, // TFT_RESET Reset + MP_OBJ_FROM_PTR(&pin_GPIO40), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO39), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO38), // TFT_RESET Reset 40000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.c b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.c index e554be121dbef..1fe3fd6280021 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.c +++ b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO40, // DC - &pin_GPIO42, // CS - &pin_GPIO41, // RST + MP_OBJ_FROM_PTR(&pin_GPIO40), // DC + MP_OBJ_FROM_PTR(&pin_GPIO42), // CS + MP_OBJ_FROM_PTR(&pin_GPIO41), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.h index 42b51434edfd3..f2ec187abde63 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.h @@ -23,7 +23,7 @@ #define DEFAULT_SPI_BUS_MOSI (&pin_GPIO35) #define DEFAULT_SPI_BUS_MISO (&pin_GPIO37) -#define DEFAULT_UART_BUS_RX (&pin_GPIO34) -#define DEFAULT_UART_BUS_TX (&pin_GPIO35) +#define DEFAULT_UART_BUS_RX (&pin_GPIO38) +#define DEFAULT_UART_BUS_TX (&pin_GPIO39) #define DOUBLE_TAP_PIN (&pin_GPIO34) diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.mk b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.mk index 06a1bf706d345..a0be8c7a5ca0a 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.mk +++ b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/mpconfigboard.mk @@ -13,3 +13,6 @@ CIRCUITPY_ESP_FLASH_SIZE = 4MB CIRCUITPY_ESP_PSRAM_SIZE = 2MB CIRCUITPY_ESP_PSRAM_MODE = qio CIRCUITPY_ESP_PSRAM_FREQ = 80m + +# Already have a display. +CIRCUITPY_PARALLELDISPLAYBUS = 0 diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/sdkconfig b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/sdkconfig index e69de29bb2d1d..e962866216039 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/sdkconfig +++ b/ports/espressif/boards/adafruit_feather_esp32s2_reverse_tft/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c b/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c index 1ebb357aed5bd..22af4caa652b7 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c +++ b/ports/espressif/boards/adafruit_feather_esp32s2_tft/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO39, // DC - &pin_GPIO7, // CS - &pin_GPIO40, // RST + MP_OBJ_FROM_PTR(&pin_GPIO39), // DC + MP_OBJ_FROM_PTR(&pin_GPIO7), // CS + MP_OBJ_FROM_PTR(&pin_GPIO40), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/board.c b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/board.c index e554be121dbef..1fe3fd6280021 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/board.c +++ b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO40, // DC - &pin_GPIO42, // CS - &pin_GPIO41, // RST + MP_OBJ_FROM_PTR(&pin_GPIO40), // DC + MP_OBJ_FROM_PTR(&pin_GPIO42), // CS + MP_OBJ_FROM_PTR(&pin_GPIO41), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h index c11780faf41ea..5053a1b2b589b 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_feather_esp32s3_reverse_tft/mpconfigboard.h @@ -23,7 +23,7 @@ #define DEFAULT_SPI_BUS_MOSI (&pin_GPIO35) #define DEFAULT_SPI_BUS_MISO (&pin_GPIO37) -#define DEFAULT_UART_BUS_RX (&pin_GPIO34) -#define DEFAULT_UART_BUS_TX (&pin_GPIO35) +#define DEFAULT_UART_BUS_RX (&pin_GPIO38) +#define DEFAULT_UART_BUS_TX (&pin_GPIO39) #define DOUBLE_TAP_PIN (&pin_GPIO34) diff --git a/ports/espressif/boards/adafruit_feather_esp32s3_tft/board.c b/ports/espressif/boards/adafruit_feather_esp32s3_tft/board.c index e3569d864e73c..587c742a64f36 100644 --- a/ports/espressif/boards/adafruit_feather_esp32s3_tft/board.c +++ b/ports/espressif/boards/adafruit_feather_esp32s3_tft/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO39, // DC - &pin_GPIO7, // CS - &pin_GPIO40, // RST + MP_OBJ_FROM_PTR(&pin_GPIO39), // DC + MP_OBJ_FROM_PTR(&pin_GPIO7), // CS + MP_OBJ_FROM_PTR(&pin_GPIO40), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/adafruit_funhouse/board.c b/ports/espressif/boards/adafruit_funhouse/board.c index 061a6abf0d8ca..83499f5a49229 100644 --- a/ports/espressif/boards/adafruit_funhouse/board.c +++ b/ports/espressif/boards/adafruit_funhouse/board.c @@ -37,9 +37,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO39, // TFT_DC Command or data - &pin_GPIO40, // TFT_CS Chip select - &pin_GPIO41, // TFT_RESET Reset + MP_OBJ_FROM_PTR(&pin_GPIO39), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO40), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO41), // TFT_RESET Reset 5000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c index 6d4fcbf135e14..1437f75165d89 100644 --- a/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c +++ b/ports/espressif/boards/adafruit_magtag_2.9_grayscale/board.c @@ -135,10 +135,23 @@ const uint8_t ssd1680_display_refresh_sequence[] = { 0x20, 0x00, 0x00 }; -static bool detect_ssd1680(void) { - // Bitbang 4-wire SPI with a bidirectional data line to read register 0x71. - // On the IL0373 it will return 0x13 or similar. On the SSD1680 it is - // unsupported and will be 0xff. + +typedef enum { + DISPLAY_IL0373, + DISPLAY_SSD1680_COLSTART_0, + DISPLAY_SSD1680_COLSTART_8, +} display_type_t; + +static display_type_t detect_display_type(void) { + // Bitbang 4-wire SPI with a bidirectional data line to read the first word of register 0x2e, + // which is the 10-byte USER ID. + // On the IL0373 it will return 0xff because it's not a valid register. + // With SSD1680, we have seen two types: + // 1. The first batch of displays, labeled "FPC-A005 20.06.15 TRX", which needs colstart=0. + // These have 10 byes of zeros in the User ID + // 2. Second batch, labeled "FPC-7619rev.b", which needs colstart=8. + // The USER ID for these boards is [0x44, 0x0, 0x4, 0x0, 0x25, 0x0, 0x1, 0x78, 0x2b, 0xe] + // So let's distinguish just by the first byte. digitalio_digitalinout_obj_t data; digitalio_digitalinout_obj_t clock; digitalio_digitalinout_obj_t chip_select; @@ -163,7 +176,7 @@ static bool detect_ssd1680(void) { common_hal_digitalio_digitalinout_switch_to_output(&reset, true, DRIVE_MODE_PUSH_PULL); common_hal_digitalio_digitalinout_switch_to_output(&clock, false, DRIVE_MODE_PUSH_PULL); - uint8_t status_read = 0x71; + uint8_t status_read = 0x2e; // SSD1680 User ID register. Not a valid register on IL0373. for (int i = 0; i < 8; i++) { common_hal_digitalio_digitalinout_set_value(&data, (status_read & (1 << (7 - i))) != 0); common_hal_digitalio_digitalinout_set_value(&clock, true); @@ -192,11 +205,20 @@ static bool detect_ssd1680(void) { common_hal_digitalio_digitalinout_deinit(&chip_select); common_hal_digitalio_digitalinout_deinit(&data_command); common_hal_digitalio_digitalinout_deinit(&reset); - return status == 0xff; + + switch (status) { + case 0xff: + return DISPLAY_IL0373; + default: // who knows? Just guess. + case 0x00: + return DISPLAY_SSD1680_COLSTART_0; + case 0x44: + return DISPLAY_SSD1680_COLSTART_8; + } } void board_init(void) { - bool is_ssd1680 = detect_ssd1680(); + display_type_t display_type = detect_display_type(); fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; busio_spi_obj_t *spi = &bus->inline_bus; @@ -206,9 +228,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO7, // EPD_DC Command or data - &pin_GPIO8, // EPD_CS Chip select - &pin_GPIO6, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO7), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO8), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO6), // EPD_RST Reset 4000000, // Baudrate 0, // Polarity 0); // Phase @@ -216,74 +238,59 @@ void board_init(void) { epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - if (is_ssd1680) { - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - ssd1680_display_start_sequence, sizeof(ssd1680_display_start_sequence), - 0, // start up time - ssd1680_display_stop_sequence, sizeof(ssd1680_display_stop_sequence), - 296, // width - 128, // height - 250, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - 0x44, // set_column_window_command - 0x45, // set_row_window_command - 0x4e, // set_current_column_command - 0x4f, // set_current_row_command - 0x24, // write_black_ram_command - false, // black_bits_inverted - 0x26, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - ssd1680_display_refresh_sequence, sizeof(ssd1680_display_refresh_sequence), - 1.0, // refresh_time - &pin_GPIO5, // busy_pin - true, // busy_state - 5.0, // seconds_per_frame - false, // always_toggle_chip_select - true, // grayscale - false, // acep - false, // spectra6 - true, // two_byte_sequence_length - true); // address_little_endian + if (display_type == DISPLAY_IL0373) { + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = il0373_display_start_sequence; + args.start_sequence_len = sizeof(il0373_display_start_sequence); + args.stop_sequence = il0373_display_stop_sequence; + args.stop_sequence_len = sizeof(il0373_display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 160; + args.ram_height = 296; + args.rotation = 270; + args.write_black_ram_command = 0x10; + args.write_color_ram_command = 0x13; + args.refresh_sequence = il0373_display_refresh_sequence; + args.refresh_sequence_len = sizeof(il0373_display_refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO5; + args.seconds_per_frame = 5.0; + args.grayscale = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } else { - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - il0373_display_start_sequence, sizeof(il0373_display_start_sequence), - 0, // start up time - il0373_display_stop_sequence, sizeof(il0373_display_stop_sequence), - 296, // width - 128, // height - 160, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x10, // write_black_ram_command - false, // black_bits_inverted - 0x13, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - il0373_display_refresh_sequence, sizeof(il0373_display_refresh_sequence), - 1.0, // refresh_time - &pin_GPIO5, // busy_pin - false, // busy_state - 5.0, // seconds_per_frame - false, // always_toggle_chip_select - true, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + // Default colstart is 0. + if (display_type == DISPLAY_SSD1680_COLSTART_8) { + args.colstart = 8; + } + args.bus = bus; + args.start_sequence = ssd1680_display_start_sequence; + args.start_sequence_len = sizeof(ssd1680_display_start_sequence); + args.stop_sequence = ssd1680_display_stop_sequence; + args.stop_sequence_len = sizeof(ssd1680_display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 250; + args.ram_height = 296; + args.rotation = 270; + args.set_column_window_command = 0x44; + args.set_row_window_command = 0x45; + args.set_current_column_command = 0x4e; + args.set_current_row_command = 0x4f; + args.write_black_ram_command = 0x24; + args.write_color_ram_command = 0x26; + args.refresh_sequence = ssd1680_display_refresh_sequence; + args.refresh_sequence_len = sizeof(ssd1680_display_refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO5; + args.busy_state = true; + args.seconds_per_frame = 5.0; + args.grayscale = true; + args.two_byte_sequence_length = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } } diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.h b/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.h index ad97ab057fb4d..9760bbfeb44aa 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_qtpy_esp32s3_4mbflash_2mbpsram/mpconfigboard.h @@ -25,3 +25,6 @@ #define CIRCUITPY_BOARD_UART_PIN {{.tx = &pin_GPIO5, .rx = &pin_GPIO16}} #define DOUBLE_TAP_PIN (&pin_GPIO10) + +// Reduce wifi.radio.tx_power due to the antenna design of this board +#define CIRCUITPY_WIFI_DEFAULT_TX_POWER (15) diff --git a/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h b/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h index fcdefda3b4036..6073bd47d0e09 100644 --- a/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h +++ b/ports/espressif/boards/adafruit_qtpy_esp32s3_nopsram/mpconfigboard.h @@ -25,3 +25,6 @@ #define CIRCUITPY_BOARD_UART_PIN {{.tx = &pin_GPIO5, .rx = &pin_GPIO16}} #define DOUBLE_TAP_PIN (&pin_GPIO10) + +// Reduce wifi.radio.tx_power due to the antenna design of this board +#define CIRCUITPY_WIFI_DEFAULT_TX_POWER (15) diff --git a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk index 515b6da5c07c4..da3e6a14496a8 100644 --- a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk +++ b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/mpconfigboard.mk @@ -9,4 +9,7 @@ CIRCUITPY_ESP_FLASH_SIZE = 2MB CIRCUITPY_DUALBANK = 0 +CIRCUITPY_JPEGIO = 0 +CIRCUITPY_CANIO = 0 + CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 diff --git a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig index e962866216039..5fd531f274a6a 100644 --- a/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig +++ b/ports/espressif/boards/ai_thinker_esp32-c3s-2m/sdkconfig @@ -9,6 +9,18 @@ # # end of LWIP +# +# Wireless Coexistence +# +# CONFIG_ESP_COEX_SW_COEXIST_ENABLE is not set +# end of Wireless Coexistence + +# +# LibC +# +# CONFIG_LIBC_OPTIMIZED_MISALIGNED_ACCESS is not set +# end of LibC + # end of Component config # end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/deshipu_ugame_s3/board.c b/ports/espressif/boards/deshipu_ugame_s3/board.c new file mode 100644 index 0000000000000..dc09786e74ce7 --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/board.c @@ -0,0 +1,123 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "supervisor/board.h" +#include "mpconfigboard.h" + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + +#include "esp_log.h" +#include "esp_err.h" + +fourwire_fourwire_obj_t board_display_obj; + +#define DELAY 0x80 + +uint8_t display_init_sequence[] = { + 0x01, 0 | DELAY, 0x80, // Software reset then delay 0x80 (128ms) + 0xEF, 3, 0x03, 0x80, 0x02, + 0xCF, 3, 0x00, 0xC1, 0x30, + 0xED, 4, 0x64, 0x03, 0x12, 0x81, + 0xE8, 3, 0x85, 0x00, 0x78, + 0xCB, 5, 0x39, 0x2C, 0x00, 0x34, 0x02, + 0xF7, 1, 0x20, + 0xEA, 2, 0x00, 0x00, + 0xc0, 1, 0x23, // Power control VRH[5:0] + 0xc1, 1, 0x10, // Power control SAP[2:0];BT[3:0] + 0xc5, 2, 0x3e, 0x28, // VCM control + 0xc7, 1, 0x86, // VCM control2 + 0x37, 1, 0x00, // Vertical scroll zero + 0x3a, 1, 0x55, // COLMOD: Pixel Format Set + 0xb1, 2, 0x00, 0x18, // Frame Rate Control (In Normal Mode/Full Colors) + 0xb6, 3, 0x08, 0x82, 0x27, // Display Function Control + 0xF2, 1, 0x00, // 3Gamma Function Disable + 0x26, 1, 0x01, // Gamma curve selected + 0xe0, 15, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00, // Set Gamma + 0xe1, 15, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F, // Set Gamma + 0x11, 0 | DELAY, 0x78, // Exit Sleep then delay 0x78 (120ms) + 0x29, 0 | DELAY, 0x78, // Display on then delay 0x78 (120ms) + 0x36, 1, 0x38, +}; + + +void board_init(void) { + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + busio_spi_obj_t *spi = &bus->inline_bus; + common_hal_busio_spi_construct(spi, &pin_GPIO12, &pin_GPIO11, NULL, false); + common_hal_busio_spi_never_reset(spi); + + bus->base.type = &fourwire_fourwire_type; + common_hal_fourwire_fourwire_construct(bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO10), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO13), // TFT_RESET Reset + 48000000L, // Baudrate + 0, // Polarity + 0); // Phase + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 320, // Width (after rotation) + 240, // Height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // Color depth + false, // Grayscale + false, // Pixels in a byte share a row. Only used for depth < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // Set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // Set row command + MIPI_COMMAND_WRITE_MEMORY_START, // Write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO21, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 20, // native_frames_per_second + true, // backlight_on_high + false, // not SH1107 + 50000); // backlight pwm frequency +} + +void board_deinit(void) { +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.h b/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.h new file mode 100644 index 0000000000000..da7c3b4e8a43c --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.h @@ -0,0 +1,28 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 Scott Shawcroft for Adafruit Industries + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#define MICROPY_HW_BOARD_NAME "uGame S3" +#define MICROPY_HW_MCU_NAME "ESP32S3" diff --git a/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.mk b/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.mk new file mode 100644 index 0000000000000..36a0ce040c509 --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/mpconfigboard.mk @@ -0,0 +1,27 @@ +USB_VID = 0x1209 +USB_PID = 0xD187 +USB_PRODUCT = "uGameS3" +USB_MANUFACTURER = "deshipu" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +CIRCUITPY_STAGE = 1 +CIRCUITPY_KEYPAD = 1 + +CIRCUITPY_CANIO = 0 +CIRCUITPY_DUALBANK = 0 +CIRCUITPY_ESPCAMERA = 0 +CIRCUITPY_FRAMEBUFFERIO = 0 +CIRCUITPY_PARALLELDISPLAYBUS = 0 +CIRCUITPY_RGBMATRIX = 0 +CIRCUITPY_ROTARYIO = 0 + +FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-stage/ugame_s3 diff --git a/ports/espressif/boards/deshipu_ugame_s3/pins.c b/ports/espressif/boards/deshipu_ugame_s3/pins.c new file mode 100644 index 0000000000000..54bd5dcdb4f0d --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/pins.c @@ -0,0 +1,43 @@ +#include "py/objtuple.h" +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_P1), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_P2), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_P3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_P4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_P5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_P6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_P7), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_BUTTON_LEFT), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_UP), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_RIGHT), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_DOWN), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_X), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_O), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_Z), MP_ROM_PTR(&pin_GPIO47) }, + + { MP_ROM_QSTR(MP_QSTR_LIGHT), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_BATTERY), MP_ROM_PTR(&pin_GPIO8) }, + + + { MP_ROM_QSTR(MP_QSTR_AUDIO_BCLK), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_AUDIO_LRCLK), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_AUDIO_DATA), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_AUDIO_GAIN), MP_ROM_PTR(&pin_GPIO18) }, + + { MP_ROM_QSTR(MP_QSTR_TFT_RESET), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_TFT_BACKLIGHT), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_TFT_SCK), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_TFT_MOSI), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/deshipu_ugame_s3/sdkconfig b/ports/espressif/boards/deshipu_ugame_s3/sdkconfig new file mode 100644 index 0000000000000..1bddb7a89fbb7 --- /dev/null +++ b/ports/espressif/boards/deshipu_ugame_s3/sdkconfig @@ -0,0 +1,22 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +CONFIG_LWIP_LOCAL_HOSTNAME="espressif-esp32s3" +# end of LWIP + +# +# Camera configuration +# +# CONFIG_OV7725_SUPPORT is not set +# CONFIG_OV3660_SUPPORT is not set +# end of Camera configuration + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/elecrow_crowpanel_3.5/board.c b/ports/espressif/boards/elecrow_crowpanel_3.5/board.c index 3adfb512e3f27..0748e03fce88b 100755 --- a/ports/espressif/boards/elecrow_crowpanel_3.5/board.c +++ b/ports/espressif/boards/elecrow_crowpanel_3.5/board.c @@ -54,8 +54,8 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO2, // TFT_DC Command or data - &pin_GPIO15, // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO2), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO15), // TFT_CS Chip select NULL, // TFT_RST Reset 20000000, // Baudrate 0, // Polarity diff --git a/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c b/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c index 88150dd401b97..c292ea113aa3f 100644 --- a/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c +++ b/ports/espressif/boards/elecrow_crowpanel_4_2_epaper/board.c @@ -56,49 +56,37 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO46, // EPD_DC Command or data - &pin_GPIO45, // EPD_CS Chip select - &pin_GPIO47, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO46), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO45), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO47), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase -// Set up the DisplayIO epaper object + // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 1, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 400, // width - 300, // height - 400, // ram_width - 300, // ram_height - 0, // colstart - 0, // rowstart - 0, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x24, // write_black_ram_command - false, // black_bits_inverted - 0x26, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO48, // busy_pin - true, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.start_up_time = 1.0; + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 400; + args.height = 300; + args.ram_width = 400; + args.ram_height = 300; + args.write_black_ram_command = 0x24; + args.write_color_ram_command = 0x26; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO48; + args.busy_state = true; + args.seconds_per_frame = 2.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c b/ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c new file mode 100644 index 0000000000000..b3b20cfe2d3f0 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h new file mode 100644 index 0000000000000..d38d4f4d09fdf --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.h @@ -0,0 +1,23 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Board setup +#define MICROPY_HW_BOARD_NAME "ESP32-C3-Lyra-V2" +#define MICROPY_HW_MCU_NAME "ESP32-C3N4" + +// Status LED +#define MICROPY_HW_NEOPIXEL (&pin_GPIO10) +#define MICROPY_HW_NEOPIXEL_COUNT (1) + +// Default bus pins +#define DEFAULT_UART_BUS_RX (&pin_GPIO20) +#define DEFAULT_UART_BUS_TX (&pin_GPIO21) + +// Serial over UART +#define CIRCUITPY_CONSOLE_UART_RX DEFAULT_UART_BUS_RX +#define CIRCUITPY_CONSOLE_UART_TX DEFAULT_UART_BUS_TX diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk new file mode 100644 index 0000000000000..4b23a76ba6a15 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/mpconfigboard.mk @@ -0,0 +1,10 @@ +CIRCUITPY_CREATOR_ID = 0x000C303A +CIRCUITPY_CREATION_ID = 0x00C3A000 + +IDF_TARGET = esp32c3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 4MB + +CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 diff --git a/ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c b/ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c new file mode 100644 index 0000000000000..5f7c3d084dd3f --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c3_lyra_v2/pins.c @@ -0,0 +1,41 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_MTMS), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_MTDI), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_MTCK), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_MTDO), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/tests/feature_check/io_module.py.exp b/ports/espressif/boards/espressif_esp32c3_lyra_v2/sdkconfig similarity index 100% rename from tests/feature_check/io_module.py.exp rename to ports/espressif/boards/espressif_esp32c3_lyra_v2/sdkconfig diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/board.c b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/board.c new file mode 100644 index 0000000000000..a3a9eec047145 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h new file mode 100644 index 0000000000000..d901bfb37ba28 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.h @@ -0,0 +1,15 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "ESP32-C61-DevKitC-1-N8R2" +#define MICROPY_HW_MCU_NAME "ESP32C61" + +#define DEFAULT_UART_BUS_RX (&pin_GPIO10) +#define DEFAULT_UART_BUS_TX (&pin_GPIO11) diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk new file mode 100644 index 0000000000000..1092a874a6868 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/mpconfigboard.mk @@ -0,0 +1,13 @@ +CIRCUITPY_CREATOR_ID = 0x000C303A +CIRCUITPY_CREATION_ID = 0x00C61001 + +IDF_TARGET = esp32c61 +IDF_TARGET_ARCH = riscv + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 8MB + +CIRCUITPY_ESP_PSRAM_SIZE = 2MB +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 80m diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/pins.c b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/pins.c new file mode 100644 index 0000000000000..901e52166576b --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/pins.c @@ -0,0 +1,48 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_IO22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_IO23), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_IO24), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_IO25), MP_ROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_IO26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_IO27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_IO28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_IO29), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/espressif_esp32c61_devkitc_1_n8r2/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h b/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h index 646fb62e5623e..89a103e619625 100644 --- a/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h +++ b/ports/espressif/boards/espressif_esp32p4_function_ev/mpconfigboard.h @@ -1,6 +1,6 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT @@ -16,5 +16,11 @@ #define DEFAULT_UART_BUS_RX (&pin_GPIO38) #define DEFAULT_UART_BUS_TX (&pin_GPIO37) +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO8) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO7) + // Use the second USB device (numbered 0 and 1) #define CIRCUITPY_USB_DEVICE_INSTANCE 1 +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) + +#define CIRCUITPY_USB_HOST_INSTANCE 0 diff --git a/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c b/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c index 3bb64f434d02f..165ce711b736a 100644 --- a/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c +++ b/ports/espressif/boards/espressif_esp32p4_function_ev/pins.c @@ -1,6 +1,6 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries // // SPDX-License-Identifier: MIT @@ -9,47 +9,82 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, - { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, - { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, - { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, - { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, - { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + // Header Block J1 + { MP_ROM_QSTR(MP_QSTR_I2C_SDA), MP_ROM_PTR(&pin_GPIO7) }, { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_I2C_SCL), MP_ROM_PTR(&pin_GPIO8) }, { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, - { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, - { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) }, - { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, - { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, - { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, - { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) }, - { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, - { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, - { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, - { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_IO35), MP_ROM_PTR(&pin_GPIO35) }, - { MP_ROM_QSTR(MP_QSTR_IO36), MP_ROM_PTR(&pin_GPIO36) }, + + { MP_ROM_QSTR(MP_QSTR_IO23), MP_ROM_PTR(&pin_GPIO23) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO37) }, { MP_ROM_QSTR(MP_QSTR_IO37), MP_ROM_PTR(&pin_GPIO37) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO38) }, { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, - { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, - { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, - { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, - { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, - { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, - { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, - { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, + + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_IO22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + + { MP_ROM_QSTR(MP_QSTR_C6_WAKEUP), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_IO36), MP_ROM_PTR(&pin_GPIO36) }, + + { MP_ROM_QSTR(MP_QSTR_IO32), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_IO24), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_IO25), MP_ROM_PTR(&pin_GPIO25) }, + + { MP_ROM_QSTR(MP_QSTR_IO33), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_IO26), MP_ROM_PTR(&pin_GPIO26) }, + + { MP_ROM_QSTR(MP_QSTR_C6_EN), MP_ROM_PTR(&pin_GPIO54) }, + { MP_ROM_QSTR(MP_QSTR_IO54), MP_ROM_PTR(&pin_GPIO54) }, + + { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, + + { MP_ROM_QSTR(MP_QSTR_PA_CTRL), MP_ROM_PTR(&pin_GPIO53) }, + { MP_ROM_QSTR(MP_QSTR_IO53), MP_ROM_PTR(&pin_GPIO53) }, + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, { MP_ROM_QSTR(MP_QSTR_IO47), MP_ROM_PTR(&pin_GPIO47) }, - { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, - { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_IO27), MP_ROM_PTR(&pin_GPIO27) }, + + // I2S + { MP_ROM_QSTR(MP_QSTR_I2S_DSDIN), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCK), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_I2S_ASDOUT), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SCLK), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_I2S_MCLK), MP_ROM_PTR(&pin_GPIO13) }, + + // Ethernet + { MP_ROM_QSTR(MP_QSTR_RMII_RXDV), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_RMII_RXD0), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_RMII_RXD1), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_MDC), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_RMII_TXD0), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_RMII_TXD1), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_RMII_TXEN), MP_ROM_PTR(&pin_GPIO49) }, + { MP_ROM_QSTR(MP_QSTR_RMII_CLK), MP_ROM_PTR(&pin_GPIO50) }, + { MP_ROM_QSTR(MP_QSTR_PHY_RSTN), MP_ROM_PTR(&pin_GPIO51) }, + { MP_ROM_QSTR(MP_QSTR_MDIO), MP_ROM_PTR(&pin_GPIO52) }, - { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, - { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + // SD Card + { MP_ROM_QSTR(MP_QSTR_SD_DATA0), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SD_DATA1), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_SD_DATA2), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_SD_DATA3), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_SD_CMD), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_SD_PWRN), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/espressif_esp32s3_box/board.c b/ports/espressif/boards/espressif_esp32s3_box/board.c index 54edb47bac33d..a27411fd79c16 100644 --- a/ports/espressif/boards/espressif_esp32s3_box/board.c +++ b/ports/espressif/boards/espressif_esp32s3_box/board.c @@ -31,9 +31,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO48, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO48), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/espressif_esp32s3_box_lite/board.c b/ports/espressif/boards/espressif_esp32s3_box_lite/board.c index e675c0f915b6d..48a49e03bc3e6 100644 --- a/ports/espressif/boards/espressif_esp32s3_box_lite/board.c +++ b/ports/espressif/boards/espressif_esp32s3_box_lite/board.c @@ -32,9 +32,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO48, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO48), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/espressif_esp32s3_eye/board.c b/ports/espressif/boards/espressif_esp32s3_eye/board.c index c48c9428cf468..09a05cc72c446 100644 --- a/ports/espressif/boards/espressif_esp32s3_eye/board.c +++ b/ports/espressif/boards/espressif_esp32s3_eye/board.c @@ -56,8 +56,8 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO43, // DC - &pin_GPIO44, // CS + MP_OBJ_FROM_PTR(&pin_GPIO43), // DC + MP_OBJ_FROM_PTR(&pin_GPIO44), // CS NULL, // no reset pin 40000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c b/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c index 94d08bb56c817..3b5aa06b6767e 100644 --- a/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c +++ b/ports/espressif/boards/espressif_esp32s3_usb_otg_n8/board.c @@ -59,9 +59,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO8, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO8), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/hardkernel_odroid_go/board.c b/ports/espressif/boards/hardkernel_odroid_go/board.c index 647fcd740eba3..5a8b6ccaefee9 100644 --- a/ports/espressif/boards/hardkernel_odroid_go/board.c +++ b/ports/espressif/boards/hardkernel_odroid_go/board.c @@ -51,8 +51,8 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO21, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO21), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select NULL, // TFT_RST Reset 40000000, // Baudrate 0, // Polarity diff --git a/ports/espressif/boards/heltec_vision_master_e290/board.c b/ports/espressif/boards/heltec_vision_master_e290/board.c index e392f65a76432..5a102c7119bd0 100644 --- a/ports/espressif/boards/heltec_vision_master_e290/board.c +++ b/ports/espressif/boards/heltec_vision_master_e290/board.c @@ -54,49 +54,44 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // EPD_DC Command or data - &pin_GPIO3, // EPD_CS Chip select - &pin_GPIO5, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO3), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO5), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase -// Set up the DisplayIO epaper object + // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 296, // width - 128, // height - 250, // ram_width - 296, // ram_height - 8, // colstart - 0, // rowstart - 90, // rotation - 0x44, // set_column_window_command - 0x45, // set_row_window_command - 0x4E, // set_current_column_command - 0x4F, // set_current_row_command - 0x24, // write_black_ram_command - false, // black_bits_inverted - 0x26, // write_color_ram_command - false, // color_bits_inverted - 0xFF0000, // highlight_color - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO6, // busy_pin - true, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - true); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 250; + args.ram_height = 296; + args.colstart = 8; + args.rotation = 90; + args.set_column_window_command = 0x44; + args.set_row_window_command = 0x45; + args.set_current_column_command = 0x4E; + args.set_current_row_command = 0x4F; + args.write_black_ram_command = 0x24; + args.write_color_ram_command = 0x26; + args.highlight_color = 0xFF0000; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO6; + args.busy_state = true; + args.seconds_per_frame = 2.0; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/espressif/boards/heltec_wireless_paper/board.c b/ports/espressif/boards/heltec_wireless_paper/board.c index 28fb6ae41b9a1..38f6472e085e6 100644 --- a/ports/espressif/boards/heltec_wireless_paper/board.c +++ b/ports/espressif/boards/heltec_wireless_paper/board.c @@ -94,49 +94,36 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO5, // EPD_DC Command or data - &pin_GPIO4, // EPD_CS Chip select - &pin_GPIO6, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO5), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO4), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO6), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase -// Set up the DisplayIO epaper object + // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 250, // width - 122, // height - 128, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x13, // write_black_ram_command - false, // black_bits_inverted - 0x10, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO7, // busy_pin - false, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 250; + args.height = 122; + args.ram_width = 128; + args.ram_height = 296; + args.rotation = 270; + args.write_black_ram_command = 0x13; + args.write_color_ram_command = 0x10; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO7; + args.seconds_per_frame = 2.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/espressif/boards/hexky_s2/board.c b/ports/espressif/boards/hexky_s2/board.c index c1e1801fed659..41f97796be4fb 100644 --- a/ports/espressif/boards/hexky_s2/board.c +++ b/ports/espressif/boards/hexky_s2/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO34, // DC - &pin_GPIO33, // CS - &pin_GPIO41, // RST + MP_OBJ_FROM_PTR(&pin_GPIO34), // DC + MP_OBJ_FROM_PTR(&pin_GPIO33), // CS + MP_OBJ_FROM_PTR(&pin_GPIO41), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/hiibot_iots2/board.c b/ports/espressif/boards/hiibot_iots2/board.c index e9941cbc3685c..e4bff05822c33 100644 --- a/ports/espressif/boards/hiibot_iots2/board.c +++ b/ports/espressif/boards/hiibot_iots2/board.c @@ -62,8 +62,8 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO35, // DC - &pin_GPIO36, // CS + MP_OBJ_FROM_PTR(&pin_GPIO35), // DC + MP_OBJ_FROM_PTR(&pin_GPIO36), // CS NULL, // NO RST ? 40000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/lilygo_tdeck/board.c b/ports/espressif/boards/lilygo_tdeck/board.c index 91a2f6d2ad702..a1008f2173f89 100644 --- a/ports/espressif/boards/lilygo_tdeck/board.c +++ b/ports/espressif/boards/lilygo_tdeck/board.c @@ -32,8 +32,8 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO11, // TFT_DC Command or data - &pin_GPIO12, // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO11), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO12), // TFT_CS Chip select NULL, // TFT_RST Reset 60000000, // Baudrate 0, // Polarity diff --git a/ports/espressif/boards/lilygo_tdisplay_s3_pro/board.c b/ports/espressif/boards/lilygo_tdisplay_s3_pro/board.c index ede20df200fa9..0782e2161ca8a 100644 --- a/ports/espressif/boards/lilygo_tdisplay_s3_pro/board.c +++ b/ports/espressif/boards/lilygo_tdisplay_s3_pro/board.c @@ -34,9 +34,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO9, // TFT_DC Command or data - &pin_GPIO39, // TFT_CS Chip select - &pin_GPIO47, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO39), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO47), // TFT_RST Reset 40000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/lilygo_tdongle_s3/board.c b/ports/espressif/boards/lilygo_tdongle_s3/board.c index 8907dc7fe9828..ede75a5842e35 100644 --- a/ports/espressif/boards/lilygo_tdongle_s3/board.c +++ b/ports/espressif/boards/lilygo_tdongle_s3/board.c @@ -60,9 +60,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO2, // DC - &pin_GPIO4, // CS - &pin_GPIO1, // RST + MP_OBJ_FROM_PTR(&pin_GPIO2), // DC + MP_OBJ_FROM_PTR(&pin_GPIO4), // CS + MP_OBJ_FROM_PTR(&pin_GPIO1), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/lilygo_tdongle_s3/mpconfigboard.h b/ports/espressif/boards/lilygo_tdongle_s3/mpconfigboard.h index fe5525ef05633..0edea0bf63b40 100644 --- a/ports/espressif/boards/lilygo_tdongle_s3/mpconfigboard.h +++ b/ports/espressif/boards/lilygo_tdongle_s3/mpconfigboard.h @@ -11,5 +11,8 @@ #define MICROPY_HW_BOARD_NAME "LILYGO T-Dongle S3" #define MICROPY_HW_MCU_NAME "ESP32S3" -#define DEFAULT_UART_BUS_RX (&pin_GPIO44) -#define DEFAULT_UART_BUS_TX (&pin_GPIO43) +#define MICROPY_HW_APA102_MOSI (&pin_GPIO40) +#define MICROPY_HW_APA102_SCK (&pin_GPIO39) + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO44) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO43) diff --git a/ports/espressif/boards/lilygo_tembed_esp32s3/board.c b/ports/espressif/boards/lilygo_tembed_esp32s3/board.c index 698e6980eceef..46e2ecf863c19 100644 --- a/ports/espressif/boards/lilygo_tembed_esp32s3/board.c +++ b/ports/espressif/boards/lilygo_tembed_esp32s3/board.c @@ -32,9 +32,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO13, // TFT_DC Command or data - &pin_GPIO10, // TFT_CS Chip select - &pin_GPIO9, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO13), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO10), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/lilygo_tqt_pro_nopsram/board.c b/ports/espressif/boards/lilygo_tqt_pro_nopsram/board.c index c7e711aee66a5..fe72222a22cbe 100644 --- a/ports/espressif/boards/lilygo_tqt_pro_nopsram/board.c +++ b/ports/espressif/boards/lilygo_tqt_pro_nopsram/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO6, // DC - &pin_GPIO5, // CS - &pin_GPIO1, // RST + MP_OBJ_FROM_PTR(&pin_GPIO6), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO1), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/lilygo_tqt_pro_psram/board.c b/ports/espressif/boards/lilygo_tqt_pro_psram/board.c index c7e711aee66a5..fe72222a22cbe 100644 --- a/ports/espressif/boards/lilygo_tqt_pro_psram/board.c +++ b/ports/espressif/boards/lilygo_tqt_pro_psram/board.c @@ -57,9 +57,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO6, // DC - &pin_GPIO5, // CS - &pin_GPIO1, // RST + MP_OBJ_FROM_PTR(&pin_GPIO6), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO1), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/board.c b/ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/board.c index 2b77e24b91e7b..6fcfb5c19a479 100644 --- a/ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/board.c +++ b/ports/espressif/boards/lilygo_ttgo_t8_s2_st7789/board.c @@ -62,9 +62,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO37, // DC - &pin_GPIO34, // CS - &pin_GPIO38, // RST + MP_OBJ_FROM_PTR(&pin_GPIO37), // DC + MP_OBJ_FROM_PTR(&pin_GPIO34), // CS + MP_OBJ_FROM_PTR(&pin_GPIO38), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_16m/board.c b/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_16m/board.c index 97574398ca2c1..8ed5462a2d364 100644 --- a/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_16m/board.c +++ b/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_16m/board.c @@ -42,9 +42,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO16, // DC - &pin_GPIO5, // CS - &pin_GPIO23, // RST + MP_OBJ_FROM_PTR(&pin_GPIO16), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO23), // RST 24000000, // baudrate (default from the driver) // 40000000, 0, // polarity diff --git a/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_4m/board.c b/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_4m/board.c index 2d3cb64d4559c..a90cc5eb66adb 100644 --- a/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_4m/board.c +++ b/ports/espressif/boards/lilygo_ttgo_tdisplay_esp32_4m/board.c @@ -41,9 +41,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO16, // DC - &pin_GPIO5, // CS - &pin_GPIO23, // RST + MP_OBJ_FROM_PTR(&pin_GPIO16), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO23), // RST 24000000, // baudrate (default from the driver) // 40000000, 0, // polarity diff --git a/ports/espressif/boards/lilygo_twatch_2020_v3/board.c b/ports/espressif/boards/lilygo_twatch_2020_v3/board.c index 266515c2338e6..e88a82ea24f5b 100644 --- a/ports/espressif/boards/lilygo_twatch_2020_v3/board.c +++ b/ports/espressif/boards/lilygo_twatch_2020_v3/board.c @@ -46,8 +46,8 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO5, // CS + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS NULL, // RST 24000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/lilygo_twatch_s3/board.c b/ports/espressif/boards/lilygo_twatch_s3/board.c index 51f5933d177ec..801914a4991c0 100644 --- a/ports/espressif/boards/lilygo_twatch_s3/board.c +++ b/ports/espressif/boards/lilygo_twatch_s3/board.c @@ -108,8 +108,8 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO38, // DC - &pin_GPIO12, // CS + MP_OBJ_FROM_PTR(&pin_GPIO38), // DC + MP_OBJ_FROM_PTR(&pin_GPIO12), // CS NULL, // RST 40000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/lolin_s3_mini_pro/board.c b/ports/espressif/boards/lolin_s3_mini_pro/board.c index cd5a083dbdba1..39e645bc7b553 100644 --- a/ports/espressif/boards/lolin_s3_mini_pro/board.c +++ b/ports/espressif/boards/lolin_s3_mini_pro/board.c @@ -39,9 +39,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO36, // DC - &pin_GPIO35, // CS - &pin_GPIO34, // RST + MP_OBJ_FROM_PTR(&pin_GPIO36), // DC + MP_OBJ_FROM_PTR(&pin_GPIO35), // CS + MP_OBJ_FROM_PTR(&pin_GPIO34), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_atoms3/board.c b/ports/espressif/boards/m5stack_atoms3/board.c index df620976b61b4..3dbd8a1a2b8b6 100644 --- a/ports/espressif/boards/m5stack_atoms3/board.c +++ b/ports/espressif/boards/m5stack_atoms3/board.c @@ -39,9 +39,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO33, // DC - &pin_GPIO15, // CS - &pin_GPIO34, // RST + MP_OBJ_FROM_PTR(&pin_GPIO33), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO34), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_cardputer/board.c b/ports/espressif/boards/m5stack_cardputer/board.c index 6fdc2f8ea5c46..00630cd36d291 100644 --- a/ports/espressif/boards/m5stack_cardputer/board.c +++ b/ports/espressif/boards/m5stack_cardputer/board.c @@ -50,9 +50,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO34, // DC - &pin_GPIO37, // CS - &pin_GPIO33, // RST + MP_OBJ_FROM_PTR(&pin_GPIO34), // DC + MP_OBJ_FROM_PTR(&pin_GPIO37), // CS + MP_OBJ_FROM_PTR(&pin_GPIO33), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk b/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk index cd18ccf8a5a81..bc30ae3afd5e9 100644 --- a/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_cardputer/mpconfigboard.mk @@ -13,4 +13,4 @@ CIRCUITPY_ESP_FLASH_SIZE = 8MB CIRCUITPY_ESPCAMERA = 0 CIRCUITPY_MAX3421E = 0 -SRC_C += boards/$(BOARD)/cardputer_keyboard.c +SRC_C += module/cardputer_keyboard.c diff --git a/ports/espressif/boards/m5stack_cardputer_ros/board.c b/ports/espressif/boards/m5stack_cardputer_ros/board.c index 6fdc2f8ea5c46..00630cd36d291 100644 --- a/ports/espressif/boards/m5stack_cardputer_ros/board.c +++ b/ports/espressif/boards/m5stack_cardputer_ros/board.c @@ -50,9 +50,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO34, // DC - &pin_GPIO37, // CS - &pin_GPIO33, // RST + MP_OBJ_FROM_PTR(&pin_GPIO34), // DC + MP_OBJ_FROM_PTR(&pin_GPIO37), // CS + MP_OBJ_FROM_PTR(&pin_GPIO33), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c b/ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c deleted file mode 100644 index 73880f66e19d9..0000000000000 --- a/ports/espressif/boards/m5stack_cardputer_ros/cardputer_keyboard.c +++ /dev/null @@ -1,238 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft -// -// SPDX-License-Identifier: MIT - -#include "py/obj.h" -#include "py/objstr.h" -#include "py/runtime.h" - -#include "supervisor/shared/serial.h" -#include "shared-bindings/keypad/EventQueue.h" -#include "shared-bindings/keypad_demux/DemuxKeyMatrix.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-module/keypad/EventQueue.h" -#include "shared-module/keypad_demux/DemuxKeyMatrix.h" -#include "supervisor/shared/reload.h" - -#include "keymap.h" - -//| """M5Stack Cardputer keyboard integration. -//| """ -//| -//| """The KEYBOARD object is an instance of DemuxKeyMatrix, configured with correct pins. -//| The pins cannot be used for any other purposes (even though exposed in the board module). -//| By default all keyboard events are consumed and routed to the standard input - there is -//| not much use of the KEYBOARD object in this configuration - just read the input via sys.stdin. -//| -//| If you need to manually process individual key up / key down events via KEYBOARD.events, -//| call `detach_serial()`. -//| """" -//| KEYBOARD: keypad_demux.DemuxKeymatrix -//| -keypad_demux_demuxkeymatrix_obj_t cardputer_keyboard_obj; -bool cardputer_keyboard_serial_attached = false; - -void cardputer_keyboard_init(void); -void keyboard_seq(const char *seq); -void update_keyboard(keypad_eventqueue_obj_t *queue); - -//| def detach_serial() -> None: -//| """Stops consuming keyboard events and routing them to sys.stdin.""" -//| ... -//| -static mp_obj_t detach_serial(void) { - cardputer_keyboard_serial_attached = false; - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, NULL); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_0(detach_serial_obj, detach_serial); - -//| def attach_serial() -> None: -//| """Starts consuming keyboard events and routing them to sys.stdin.""" -//| ... -//| -static mp_obj_t attach_serial(void) { - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, update_keyboard); - cardputer_keyboard_serial_attached = true; - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_0(attach_serial_obj, attach_serial); - -//| def key_to_char(key: int, shifted: bool) -> str | None: -//| """Converts a key index to the respective key (with or without shift modifier). -//| Returns None for functional & modifier keys or whenever not 0 <= key < 56. -//| """ -//| ... -//| -static mp_obj_t key_to_char(mp_obj_t key_obj, mp_obj_t shifted_obj) { - mp_int_t key = mp_obj_get_int(key_obj); - if (key < 0 || key > (mp_int_t)(sizeof keymap / sizeof *keymap) || keymap[key] == 0) { - return mp_const_none; - } else if (shifted_obj == mp_const_true) { - return mp_obj_new_str(&keymap_shifted[key], 1); - } else { - return mp_obj_new_str(&keymap[key], 1); - } -} -static MP_DEFINE_CONST_FUN_OBJ_2(key_to_char_obj, key_to_char); - -// Ring buffer of characters consumed from keyboard events (when serial attached) -ringbuf_t keyqueue; -char keybuf[32]; - -keypad_event_obj_t event; -char keystate[56]; - -// Keyboard pins -const mcu_pin_obj_t *row_addr_pins[] = { - &pin_GPIO8, - &pin_GPIO9, - &pin_GPIO11, -}; - -const mcu_pin_obj_t *column_pins[] = { - &pin_GPIO13, - &pin_GPIO15, - &pin_GPIO3, - &pin_GPIO4, - &pin_GPIO5, - &pin_GPIO6, - &pin_GPIO7 -}; - -void cardputer_keyboard_init(void) { - cardputer_keyboard_obj.base.type = &keypad_demux_demuxkeymatrix_type; - common_hal_keypad_demux_demuxkeymatrix_construct( - &cardputer_keyboard_obj, // self - 3, // num_row_addr_pins - row_addr_pins, // row_addr_pins - 7, // num_column_pins - column_pins, // column_pins - true, // columns_to_anodes - false, // transpose - 0.01f, // interval - 20, // max_events - 2 // debounce_threshold - ); - demuxkeymatrix_never_reset(&cardputer_keyboard_obj); - - ringbuf_init(&keyqueue, (uint8_t *)keybuf, sizeof(keybuf)); - attach_serial(); -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -void board_serial_init(void) { - cardputer_keyboard_init(); -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -bool board_serial_connected(void) { - return cardputer_keyboard_serial_attached; -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -uint32_t board_serial_bytes_available(void) { - if (cardputer_keyboard_serial_attached) { - return ringbuf_num_filled(&keyqueue); - } else { - return 0; - } -} - -// Overrides the weakly linked function from supervisor/shared/serial.c -char board_serial_read(void) { - if (cardputer_keyboard_serial_attached) { - return ringbuf_get(&keyqueue); - } else { - return 0; - } -} - -void keyboard_seq(const char *seq) { - while (*seq) { - ringbuf_put(&keyqueue, *seq++); - } -} - -void update_keyboard(keypad_eventqueue_obj_t *queue) { - uint8_t ascii = 0; - - if (common_hal_keypad_eventqueue_get_length(queue) == 0) { - return; - } - - while (common_hal_keypad_eventqueue_get_into(queue, &event)) { - if (event.pressed) { - keystate[event.key_number] = 1; - - if (keystate[KEY_CTRL]) { - if (keystate[KEY_ALT] && keystate[KEY_BACKSPACE]) { - reload_initiate(RUN_REASON_REPL_RELOAD); - } - ascii = keymap[event.key_number]; - if (ascii >= 'a' && ascii <= 'z') { - ascii -= 'a' - 1; - } - - if (ascii == mp_interrupt_char) { - mp_sched_keyboard_interrupt(); - } - } else if (keystate[KEY_SHIFT]) { - ascii = keymap_shifted[event.key_number]; - } else if (keystate[KEY_FN] && event.key_number != KEY_FN) { - switch (event.key_number | FN_MOD) - { - case KEY_DOWN: - keyboard_seq("\e[B"); - break; - case KEY_UP: - keyboard_seq("\e[A"); - break; - case KEY_DELETE: - keyboard_seq("\e[3~"); - break; - case KEY_LEFT: - keyboard_seq("\e[D"); - break; - case KEY_RIGHT: - keyboard_seq("\e[C"); - break; - case KEY_ESC: - ringbuf_put(&keyqueue, '\e'); - break; - } - } else { - ascii = keymap[event.key_number]; - } - - if (ascii > 0) { - if (keystate[KEY_ALT]) { - ringbuf_put(&keyqueue, '\e'); - } else if (keystate[KEY_OPT]) { - ringbuf_put(&keyqueue, '\x10'); - } - ringbuf_put(&keyqueue, ascii); - } - } else { - keystate[event.key_number] = 0; - } - } -} - -static const mp_rom_map_elem_t cardputer_keyboard_module_globals_table[] = { - {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cardputer_keyboard)}, - {MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&cardputer_keyboard_obj)}, - {MP_ROM_QSTR(MP_QSTR_attach_serial), MP_ROM_PTR(&attach_serial_obj)}, - {MP_ROM_QSTR(MP_QSTR_detach_serial), MP_ROM_PTR(&detach_serial_obj)}, - {MP_ROM_QSTR(MP_QSTR_key_to_char), MP_ROM_PTR(&key_to_char_obj)}, -}; -MP_DEFINE_CONST_DICT(cardputer_keyboard_module_globals, cardputer_keyboard_module_globals_table); - -const mp_obj_module_t cardputer_keyboard_module = { - .base = {&mp_type_module}, - .globals = (mp_obj_dict_t *)&cardputer_keyboard_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_cardputer_keyboard, cardputer_keyboard_module); diff --git a/ports/espressif/boards/m5stack_cardputer_ros/keymap.h b/ports/espressif/boards/m5stack_cardputer_ros/keymap.h deleted file mode 100644 index 0256fafaa0f57..0000000000000 --- a/ports/espressif/boards/m5stack_cardputer_ros/keymap.h +++ /dev/null @@ -1,214 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#define SHIFT_MOD 0x40 -#define FN_MOD 0x80 - -#define KEY_OPT 0 -#define KEY_Z 1 -#define KEY_C 2 -#define KEY_B 3 -#define KEY_M 4 -#define KEY_DOT 5 -#define KEY_SPACE 6 -#define KEY_SHIFT 7 -#define KEY_S 8 -#define KEY_F 9 -#define KEY_H 10 -#define KEY_K 11 -#define KEY_SEMICOLON 12 -#define KEY_ENTER 13 -#define KEY_Q 14 -#define KEY_E 15 -#define KEY_T 16 -#define KEY_U 17 -#define KEY_O 18 -#define KEY_LEFT_BRACKET 19 -#define KEY_BACKSLASH 20 -#define KEY_1 21 -#define KEY_3 22 -#define KEY_5 23 -#define KEY_7 24 -#define KEY_9 25 -#define KEY_UNDERSCORE 26 -#define KEY_BACKSPACE 27 -#define KEY_CTRL 28 -#define KEY_ALT 29 -#define KEY_X 30 -#define KEY_V 31 -#define KEY_N 32 -#define KEY_COMMA 33 -#define KEY_SLASH 34 -#define KEY_FN 35 -#define KEY_A 36 -#define KEY_D 37 -#define KEY_G 38 -#define KEY_J 39 -#define KEY_L 40 -#define KEY_APOSTROPHE 41 -#define KEY_TAB 42 -#define KEY_W 43 -#define KEY_R 44 -#define KEY_Y 45 -#define KEY_I 46 -#define KEY_P 47 -#define KEY_RIGHT_BRACKET 48 -#define KEY_GRAVE 49 -#define KEY_2 50 -#define KEY_4 51 -#define KEY_6 52 -#define KEY_8 53 -#define KEY_0 54 -#define KEY_EQUALS 55 - -#define KEY_GREATER (5 | SHIFT_MOD) -#define KEY_COLON (12 | SHIFT_MOD) -#define KEY_LEFT_CURLY_BRACKET (19 | SHIFT_MOD) -#define KEY_PIPE (20 | SHIFT_MOD) -#define KEY_EXCLAMATION (21 | SHIFT_MOD) -#define KEY_HASH (22 | SHIFT_MOD) -#define KEY_PERCENT (23 | SHIFT_MOD) -#define KEY_AMPERSAND (24 | SHIFT_MOD) -#define KEY_OPEN_PARENTHESIS (25 | SHIFT_MOD) -#define KEY_MINUS (26 | SHIFT_MOD) -#define KEY_LESS (33 | SHIFT_MOD) -#define KEY_QUESTION (34 | SHIFT_MOD) -#define KEY_DOUBLE_QUOTE (41 | SHIFT_MOD) -#define KEY_RIGHT_CURLY_BRACKET (48 | SHIFT_MOD) -#define KEY_TILDE (49 | SHIFT_MOD) -#define KEY_AT (50 | SHIFT_MOD) -#define KEY_DOLLAR (51 | SHIFT_MOD) -#define KEY_CARET (52 | SHIFT_MOD) -#define KEY_ASTERISK (53 | SHIFT_MOD) -#define KEY_CLOSE_PARENTHESIS (54 | SHIFT_MOD) -#define KEY_PLUS (55 | SHIFT_MOD) - -#define KEY_DOWN (5 | FN_MOD) -#define KEY_UP (12 | FN_MOD) -#define KEY_DELETE (27 | FN_MOD) -#define KEY_LEFT (33 | FN_MOD) -#define KEY_RIGHT (34 | FN_MOD) -#define KEY_ESC (49 | FN_MOD) - -const char keymap[56] = { - 0, // KEY_OPT - 'z', // KEY_Z - 'c', // KEY_C - 'b', // KEY_B - 'm', // KEY_M - '.', // KEY_DOT - ' ', // KEY_SPACE - 0, // KEY_SHIFT - 's', // KEY_S - 'f', // KEY_F - 'h', // KEY_H - 'k', // KEY_K - ';', // KEY_SEMICOLON - '\r',// KEY_ENTER - 'q', // KEY_Q - 'e', // KEY_E - 't', // KEY_T - 'u', // KEY_U - 'o', // KEY_O - '[', // KEY_LEFT_BRACKET - '\\',// KEY_BACKSLASH - '1', // KEY_1 - '3', // KEY_3 - '5', // KEY_5 - '7', // KEY_7 - '9', // KEY_9 - '_', // KEY_UNDERSCORE - '\b',// KEY_BACKSPACE - 0, // KEY_CTRL - 0, // KEY_ALT - 'x', // KEY_X - 'v', // KEY_V - 'n', // KEY_N - ',', // KEY_COMMA - '/', // KEY_SLASH - 0, // KEY_FN - 'a', // KEY_A - 'd', // KEY_D - 'g', // KEY_G - 'j', // KEY_J - 'l', // KEY_L - '\'',// KEY_APOSTROPHE - '\t',// KEY_TAB - 'w', // KEY_W - 'r', // KEY_R - 'y', // KEY_Y - 'i', // KEY_I - 'p', // KEY_P - ']', // KEY_RIGHT_BRACKET - '`', // KEY_GRAVE - '2', // KEY_2 - '4', // KEY_4 - '6', // KEY_6 - '8', // KEY_8 - '0', // KEY_0 - '=' // KEY_EQUALS -}; - -const char keymap_shifted[56] = { - 0, // KEY_OPT - 'Z', // KEY_Z - 'C', // KEY_C - 'B', // KEY_B - 'M', // KEY_M - '>', // KEY_DOT -> '>' - ' ', // KEY_SPACE - 0, // KEY_SHIFT - 'S', // KEY_S - 'F', // KEY_F - 'H', // KEY_H - 'K', // KEY_K - ':', // KEY_SEMICOLON -> ':' - '\r',// KEY_ENTER - 'Q', // KEY_Q - 'E', // KEY_E - 'T', // KEY_T - 'U', // KEY_U - 'O', // KEY_O - '{', // KEY_LEFT_BRACKET -> '{' - '|', // KEY_BACKSLASH -> '|' - '!', // KEY_1 -> '!' - '#', // KEY_3 -> '#' - '%', // KEY_5 -> '%' - '&', // KEY_7 -> '&' - '(', // KEY_9 -> '(' - '-', // KEY_UNDERSCORE -> '-' - '\b',// KEY_BACKSPACE - 0, // KEY_CTRL - 0, // KEY_ALT - 'X', // KEY_X - 'V', // KEY_V - 'N', // KEY_N - '<', // KEY_COMMA -> '<' - '?', // KEY_SLASH -> '?' - 0, // KEY_FN - 'A', // KEY_A - 'D', // KEY_D - 'G', // KEY_G - 'J', // KEY_J - 'L', // KEY_L - '"', // KEY_APOSTROPHE -> '"' - '\t',// KEY_TAB - 'W', // KEY_W - 'R', // KEY_R - 'Y', // KEY_Y - 'I', // KEY_I - 'P', // KEY_P - '}', // KEY_RIGHT_BRACKET -> '}' - '~', // KEY_GRAVE -> '~' - '@', // KEY_2 -> '@' - '$', // KEY_4 -> '$' - '^', // KEY_6 -> '^' - '*', // KEY_8 -> '*' - ')', // KEY_0 -> ')' - '+' // KEY_EQUALS -> '+' -}; diff --git a/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk b/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk index 573f947f0a8f5..ede48c2f01523 100644 --- a/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk +++ b/ports/espressif/boards/m5stack_cardputer_ros/mpconfigboard.mk @@ -15,4 +15,4 @@ CIRCUITPY_RCLCPY = 1 CIRCUITPY_ESPCAMERA = 0 CIRCUITPY_MAX3421E = 0 -SRC_C += boards/$(BOARD)/cardputer_keyboard.c +SRC_C += module/cardputer_keyboard.c diff --git a/ports/espressif/boards/m5stack_core2/board.c b/ports/espressif/boards/m5stack_core2/board.c index 9d5c2bd650a51..2d63436235af1 100644 --- a/ports/espressif/boards/m5stack_core2/board.c +++ b/ports/espressif/boards/m5stack_core2/board.c @@ -318,9 +318,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO15, // DC - &pin_GPIO5, // CS - NULL, // RST + MP_OBJ_FROM_PTR(&pin_GPIO15), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_NULL, // RST 32000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_core_basic/board.c b/ports/espressif/boards/m5stack_core_basic/board.c index 4ae128817a116..5a18d33e52767 100644 --- a/ports/espressif/boards/m5stack_core_basic/board.c +++ b/ports/espressif/boards/m5stack_core_basic/board.c @@ -42,9 +42,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO14, // CS - &pin_GPIO33, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO14), // CS + MP_OBJ_FROM_PTR(&pin_GPIO33), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_core_fire/board.c b/ports/espressif/boards/m5stack_core_fire/board.c index 4ae128817a116..5a18d33e52767 100644 --- a/ports/espressif/boards/m5stack_core_fire/board.c +++ b/ports/espressif/boards/m5stack_core_fire/board.c @@ -42,9 +42,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO14, // CS - &pin_GPIO33, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO14), // CS + MP_OBJ_FROM_PTR(&pin_GPIO33), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_cores3/board.c b/ports/espressif/boards/m5stack_cores3/board.c index f49d634999730..c14be4ae0d142 100644 --- a/ports/espressif/boards/m5stack_cores3/board.c +++ b/ports/espressif/boards/m5stack_cores3/board.c @@ -45,9 +45,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO35, // DC - &pin_GPIO3, // CS - NULL, // RST + MP_OBJ_FROM_PTR(&pin_GPIO35), // DC + MP_OBJ_FROM_PTR(&pin_GPIO3), // CS + MP_OBJ_NULL, // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_cores3/pins.c b/ports/espressif/boards/m5stack_cores3/pins.c index 2ebb225277ded..8b40d9e84842a 100644 --- a/ports/espressif/boards/m5stack_cores3/pins.c +++ b/ports/espressif/boards/m5stack_cores3/pins.c @@ -28,42 +28,59 @@ CIRCUITPY_BOARD_BUS_SINGLETON(porta_i2c, i2c, 1) static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - // M5 Bus (except I2S & PORT B) + // M5 Bus (except I2S) { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO37) }, { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO35) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, { MP_ROM_QSTR(MP_QSTR_D44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_PORTC_RX), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_A18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_SDA), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO6) }, - { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO6) }, + + { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_A8), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_A9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, { MP_ROM_QSTR(MP_QSTR_D43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_PORTC_TX), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_A17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_SCL), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO7) }, - // Port B - { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, - // I2S { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_ROM_PTR(&pin_GPIO34) }, { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_ROM_PTR(&pin_GPIO33) }, - { MP_ROM_QSTR(MP_QSTR_IS2_DATA), MP_ROM_PTR(&pin_GPIO13) }, - { MP_ROM_QSTR(MP_QSTR_IS2_MASTER_CLOCK), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_OUT), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_IN), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_I2S_MASTER_CLOCK), MP_ROM_PTR(&pin_GPIO0) }, // Camera { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA), MP_ROM_PTR(&camera_data_tuple) }, diff --git a/ports/espressif/boards/m5stack_cores3_se/board.c b/ports/espressif/boards/m5stack_cores3_se/board.c new file mode 100644 index 0000000000000..bf5ccd17f36fe --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/board.c @@ -0,0 +1,224 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" +#include "common-hal/microcontroller/Pin.h" + + +#define DELAY 0x80 +#define AXP2101_I2C_ADDRESS 0x34 +#define AW9523B_I2C_ADDRESS 0x58 + +uint8_t display_init_sequence[] = { + 0x01, DELAY, 0x80, // Software reset then delay 0x80 (128ms) + 0xC8, 0x03, 0xFF, 0x93, 0x42, // Turn on the external command + 0xC0, 0x02, 0x12, 0x12, // Power Control 1 + 0xC1, 0x01, 0x03, // Power Control 2 + 0xC5, 0x01, 0xF2, // VCOM Control 1 + 0xB0, 0x01, 0xE0, // RGB Interface SYNC Mode + 0xF6, 0x03, 0x01, 0x00, 0x00, // Interface control + 0XE0, 0x0F, 0x00, 0x0C, 0x11, 0x04, 0x11, 0x08, 0x37, 0x89, 0x4C, 0x06, 0x0C, 0x0A, 0x2E, 0x34, 0x0F, // Positive Gamma Correction + 0xE1, 0x0F, 0x00, 0x0B, 0x11, 0x05, 0x13, 0x09, 0x33, 0x67, 0x48, 0x07, 0x0E, 0x0B, 0x2E, 0x33, 0x0F, // Negative Gamma Correction + 0xB6, 0x04, 0x08, 0x82, 0x1D, 0x04, // Display Function Control + 0x3A, 0x01, 0x55, // COLMOD: Pixel Format Set 16 bit + 0x21, 0x00, // Display inversion ON + 0x36, 0x01, 0x08, // Memory Access Control: RGB order + 0x11, DELAY, 0x78, // Exit Sleep then delay 0x78 (120ms) + 0x29, DELAY, 0x78, // Display on then delay 0x78 (120ms) +}; + +static bool display_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO35), // DC + MP_OBJ_FROM_PTR(&pin_GPIO3), // CS + NULL, // RST + 40000000, // baudrate + 0, // polarity + 0 // phase + ); + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 320, // width (after rotation) + 240, // height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + NULL, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 61, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); + + return true; +} + +static bool axp2101_init(busio_i2c_obj_t *i2c) { + int rc; + uint8_t write_buf[2]; + + // 0x90 = 0b1011_0011 // LDOS ON/OFF control 0 + // Compared to CoreS3: ALDO2 (bit 2) and ALDO3 (bit 3) disabled (no ES7210/camera) + write_buf[0] = 0x90; + write_buf[1] = 0b10110011; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x92, 0x0D // ALDO1 set to 1.8v for AW88298 + write_buf[0] = 0x92; + write_buf[1] = 0x0D; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x95, 0x1C // ALDO4 set to 3.3v for TF card slot + write_buf[0] = 0x95; + write_buf[1] = 0x1C; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x99, 0x18 // DLDO1 set to 2.9v for TFT backlight + write_buf[0] = 0x99; + write_buf[1] = 0x18; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x27, 0x00 // PowerKey Hold=1sec / PowerOff=4sec + write_buf[0] = 0x27; + write_buf[1] = 0x00; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x69, 0x11 // CHGLED setting + write_buf[0] = 0x69; + write_buf[1] = 0x11; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x10, 0x30 // PMU common config + write_buf[0] = 0x10; + write_buf[1] = 0x30; + rc = common_hal_busio_i2c_write(i2c, AXP2101_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + return true; +} + +static bool aw9523b_init(busio_i2c_obj_t *i2c) { + int rc; + uint8_t write_buf[2]; + + // 0x02 = 0b0000_0111 // AW_RST, BUD_OUT_EN, TOUCH_RST + write_buf[0] = 0x02; + write_buf[1] = 0b00000111; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x03 = 0b1000_0011 // BOOST_EN, CAM_RST, LCD_RST + write_buf[0] = 0x03; + write_buf[1] = 0b10000011; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x04 = 0b0001_1000 // Set TF_SW, ES_INT as input + write_buf[0] = 0x04; + write_buf[1] = 0b00011000; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x05 = 0b0000_1100 // Set AW_INT, TOUCH_INT as input + write_buf[0] = 0x05; + write_buf[1] = 0b00001100; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + // 0x11 = 0b0001_0000 // Set P0 outputs in push pull mode + write_buf[0] = 0x11; + write_buf[1] = 0b00010000; + rc = common_hal_busio_i2c_write(i2c, AW9523B_I2C_ADDRESS, write_buf, sizeof(write_buf)); + if (rc != 0) { + return false; + } + + return true; +} + +void board_init(void) { + // Deselect SD card on shared SPI bus before display init (see #10536) + config_pin_as_output_with_level(GPIO_NUM_4, true); + + busio_i2c_obj_t *internal_i2c = common_hal_board_create_i2c(0); + + if (!axp2101_init(internal_i2c)) { + mp_printf(&mp_plat_print, "could not initialize AXP2101"); + return; + } + + if (!aw9523b_init(internal_i2c)) { + mp_printf(&mp_plat_print, "could not initialize AW9523B"); + return; + } + + if (!display_init()) { + mp_printf(&mp_plat_print, "could not initialize the display"); + return; + } +} diff --git a/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.h b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.h new file mode 100644 index 0000000000000..070ccf9195e5b --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.h @@ -0,0 +1,25 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "M5Stack CoreS3 SE" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO5) + +#define CIRCUITPY_BOARD_I2C (2) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO11, .sda = &pin_GPIO12}, \ + {.scl = &pin_GPIO1, .sda = &pin_GPIO2}} + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO36) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO37) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO35) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO18) +#define DEFAULT_UART_BUS_TX (&pin_GPIO17) diff --git a/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk new file mode 100644 index 0000000000000..2413c43cebea1 --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/mpconfigboard.mk @@ -0,0 +1,27 @@ +USB_VID = 0x303A +USB_PID = 0x8348 + +USB_PRODUCT = "M5Stack CoreS3 SE" +USB_MANUFACTURER = "M5Stack" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +CIRCUITPY_PARALLELDISPLAYBUS = 0 + +OPTIMIZATION_FLAGS = -Os + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ConnectionManager +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Shapes +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_FakeRequests +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Requests diff --git a/ports/espressif/boards/m5stack_cores3_se/pins.c b/ports/espressif/boards/m5stack_cores3_se/pins.c new file mode 100644 index 0000000000000..f0e20926bfa67 --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/pins.c @@ -0,0 +1,87 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +CIRCUITPY_BOARD_BUS_SINGLETON(porta_i2c, i2c, 1) + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // M5 Bus (except I2S) + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO36) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_D44), MP_ROM_PTR(&pin_GPIO44) }, + + { MP_ROM_QSTR(MP_QSTR_PORTC_RX), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_A18), MP_ROM_PTR(&pin_GPIO18) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_PORTA_SDA), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO6) }, + + { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_A8), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_A9), MP_ROM_PTR(&pin_GPIO9) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_D43), MP_ROM_PTR(&pin_GPIO43) }, + + { MP_ROM_QSTR(MP_QSTR_PORTC_TX), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_A17), MP_ROM_PTR(&pin_GPIO17) }, + + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_PORTA_SCL), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_IR), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO7) }, + + // I2S + { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_OUT), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DATA_IN), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_I2S_MASTER_CLOCK), MP_ROM_PTR(&pin_GPIO0) }, + + // Display + { MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_GPIO35) }, + + // Misc + { MP_ROM_QSTR(MP_QSTR_I2C_INTERRUPT), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_SDCARD_CS), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO0) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_I2C), MP_ROM_PTR(&board_porta_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)} +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/m5stack_cores3_se/sdkconfig b/ports/espressif/boards/m5stack_cores3_se/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/m5stack_cores3_se/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/m5stack_dial/board.c b/ports/espressif/boards/m5stack_dial/board.c index 4e7cf4b0240a9..6214255612bd2 100644 --- a/ports/espressif/boards/m5stack_dial/board.c +++ b/ports/espressif/boards/m5stack_dial/board.c @@ -49,9 +49,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO4, // DC - &pin_GPIO7, // CS - &pin_GPIO8, // RST + MP_OBJ_FROM_PTR(&pin_GPIO4), // DC + MP_OBJ_FROM_PTR(&pin_GPIO7), // CS + MP_OBJ_FROM_PTR(&pin_GPIO8), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_dinmeter/board.c b/ports/espressif/boards/m5stack_dinmeter/board.c new file mode 100644 index 0000000000000..37fafa994dc4d --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/board.c @@ -0,0 +1,107 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "mpconfigboard.h" +#include "supervisor/board.h" +#include "supervisor/shared/serial.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" +#include "py/runtime.h" +#include "py/ringbuf.h" +#include "shared/runtime/interrupt_char.h" + + +uint8_t display_init_sequence[] = { + 0x01, 0x80, 0x96, // SWRESET and Delay 150ms + 0x11, 0x80, 0xff, // SLPOUT and Delay + 0xb1, 0x03, 0x01, 0x2C, 0x2D, // _FRMCTR1 + 0xb2, 0x03, 0x01, 0x2C, 0x2D, // _FRMCTR2 + 0xb3, 0x06, 0x01, 0x2C, 0x2D, 0x01, 0x2C, 0x2D, // _FRMCTR3 + 0xb4, 0x01, 0x07, // _INVCTR line inversion + 0xc0, 0x03, 0xa2, 0x02, 0x84, // _PWCTR1 GVDD = 4.7V, 1.0uA + 0xc1, 0x01, 0xc5, // _PWCTR2 VGH=14.7V, VGL=-7.35V + 0xc2, 0x02, 0x0a, 0x00, // _PWCTR3 Opamp current small, Boost frequency + 0xc3, 0x02, 0x8a, 0x2a, + 0xc4, 0x02, 0x8a, 0xee, + 0xc5, 0x01, 0x0e, // _VMCTR1 VCOMH = 4V, VOML = -1.1V + 0x36, 0x01, 0xc8, // MADCTL Rotate display + 0x21, 0x00, // _INVON + 0x3a, 0x01, 0x05, // COLMOD - 16bit color + 0xe0, 0x10, 0x02, 0x1c, 0x07, 0x12, 0x37, 0x32, 0x29, 0x2d, 0x29, 0x25, 0x2B, 0x39, 0x00, 0x01, 0x03, 0x10, // _GMCTRP1 Gamma + 0xe1, 0x10, 0x03, 0x1d, 0x07, 0x06, 0x2E, 0x2C, 0x29, 0x2D, 0x2E, 0x2E, 0x37, 0x3F, 0x00, 0x00, 0x02, 0x10, // _GMCTRN1 + 0x13, 0x80, 0x0a, // _NORON + 0x29, 0x80, 0x64 // _DISPON +}; + + +// Overrides the weakly linked function from supervisor/shared/board.c +void board_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + // see here for inspiration: https://github.com/m5stack/M5GFX/blob/33d7d3135e816a86a008fae8ab3757938cee95d2/src/M5GFX.cpp#L1350 + common_hal_fourwire_fourwire_construct( + bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO4), // DC + MP_OBJ_FROM_PTR(&pin_GPIO7), // CS + MP_OBJ_FROM_PTR(&pin_GPIO8), // RST + 40000000, // baudrate + 0, // polarity + 0 // phase + ); + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 240, // width (after rotation) + 135, // height (after rotation) + 40, // column start + 52, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO9, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 80, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} + + +bool espressif_board_reset_pin_number(gpio_num_t pin_number) { + // Hold pin must be set high to avoid a power off when battery powered + if (pin_number == 46) { + // Turn on hold output + config_pin_as_output_with_level(pin_number, true); + return true; + } + return false; +} + +// TODO: Should we turn off the display when asleep, in board_deinit() ? diff --git a/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.h b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.h new file mode 100644 index 0000000000000..2d69e61ea950a --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 CDarius +// SPDX-FileCopyrightText: Copyright (c) 2025 juergenpabel +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "M5Stack DinMeter" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define CIRCUITPY_BOARD_I2C (2) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO12, .sda = &pin_GPIO11}, \ + {.scl = &pin_GPIO15, .sda = &pin_GPIO13}} + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO6, .mosi = &pin_GPIO5}} + +#define CIRCUITPY_I2C_ALLOW_INTERNAL_PULL_UP (1) diff --git a/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk new file mode 100644 index 0000000000000..81a23af1fe464 --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/mpconfigboard.mk @@ -0,0 +1,15 @@ +USB_VID = 0x303A +USB_PID = 0x830B + +USB_PRODUCT = "DinMeter" +USB_MANUFACTURER = "M5Stack" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 8MB + +# Very few pins. +CIRCUITPY_ESPCAMERA = 0 +CIRCUITPY_PARALLELDISPLAYBUS = 0 diff --git a/ports/espressif/boards/m5stack_dinmeter/pins.c b/ports/espressif/boards/m5stack_dinmeter/pins.c new file mode 100644 index 0000000000000..f75e05e848b0b --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/pins.c @@ -0,0 +1,61 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Juergen Pabel +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +#include "shared-module/displayio/__init__.h" + +CIRCUITPY_BOARD_BUS_SINGLETON(porta_i2c, i2c, 1) + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // Port A + { MP_ROM_QSTR(MP_QSTR_PORTA_SCL), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_SDA), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) }, + + // Port B + { MP_ROM_QSTR(MP_QSTR_PORTB_IN), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_PORTB_OUT), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, + + // Encoder + { MP_ROM_QSTR(MP_QSTR_ENC_A), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_ENC_B), MP_ROM_PTR(&pin_GPIO40) }, + + // Button + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO0) }, + + // Misc + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_KNOB_BUTTON), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_POWER_HOLD), MP_ROM_PTR(&pin_GPIO46) }, + + // Display + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_TFT_RESET), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_TFT_BACKLIGHT), MP_ROM_PTR(&pin_GPIO9) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_I2C), MP_ROM_PTR(&board_porta_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)} +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/m5stack_dinmeter/sdkconfig b/ports/espressif/boards/m5stack_dinmeter/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/m5stack_dinmeter/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/m5stack_stick_c/board.c b/ports/espressif/boards/m5stack_stick_c/board.c index 641d2d1db7e78..491ddead86035 100644 --- a/ports/espressif/boards/m5stack_stick_c/board.c +++ b/ports/espressif/boards/m5stack_stick_c/board.c @@ -154,9 +154,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO23, // DC - &pin_GPIO5, // CS - &pin_GPIO18, // RST + MP_OBJ_FROM_PTR(&pin_GPIO23), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO18), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_stick_c_plus/board.c b/ports/espressif/boards/m5stack_stick_c_plus/board.c index eec6a20826030..8902521db8e3f 100644 --- a/ports/espressif/boards/m5stack_stick_c_plus/board.c +++ b/ports/espressif/boards/m5stack_stick_c_plus/board.c @@ -154,9 +154,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO23, // DC - &pin_GPIO5, // CS - &pin_GPIO18, // RST + MP_OBJ_FROM_PTR(&pin_GPIO23), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO18), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_stick_c_plus2/board.c b/ports/espressif/boards/m5stack_stick_c_plus2/board.c index a50e38df09e46..d84faa1888d21 100644 --- a/ports/espressif/boards/m5stack_stick_c_plus2/board.c +++ b/ports/espressif/boards/m5stack_stick_c_plus2/board.c @@ -50,9 +50,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO14, // DC - &pin_GPIO5, // CS - &pin_GPIO12, // RST + MP_OBJ_FROM_PTR(&pin_GPIO14), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/m5stack_tab5/board.c b/ports/espressif/boards/m5stack_tab5/board.c new file mode 100644 index 0000000000000..7c7c768d5c154 --- /dev/null +++ b/ports/espressif/boards/m5stack_tab5/board.c @@ -0,0 +1,379 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/mipidsi/Bus.h" +#include "shared-bindings/mipidsi/Display.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/framebufferio/__init__.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" +#include "ports/espressif/common-hal/microcontroller/Pin.h" + +// Statically allocate the MIPI DSI bus (only one DSI bus on ESP32-P4) +static mipidsi_bus_obj_t board_mipidsi_bus; + +// ILI9881C initialization sequence +static const uint8_t ili9881c_init_sequence[] = { + 0xff, 0x03, 0x98, 0x81, 0x00, + 0x01, 0x80, 0x78, // software reset + // CMD_Page 1 + 0xff, 0x03, 0x98, 0x81, 0x01, + 0xb7, 0x01, 0x03, // set 2 lane + // CMD Page 0 + 0xff, 0x03, 0x98, 0x81, 0x00, + 0x11, 0x80, 0x0a, // out of sleep + 0x36, 0x01, 0x00, // madctl + 0x3a, 0x01, 0x55, // colmod + // CMD_Page 3 + 0xff, 0x03, 0x98, 0x81, 0x03, + 0x01, 0x01, 0x00, + 0x02, 0x01, 0x00, + 0x03, 0x01, 0x73, + 0x04, 0x01, 0x00, + 0x05, 0x01, 0x00, + 0x06, 0x01, 0x08, + 0x07, 0x01, 0x00, + 0x08, 0x01, 0x00, + 0x09, 0x01, 0x1b, + 0x0a, 0x01, 0x01, + 0x0b, 0x01, 0x01, + 0x0c, 0x01, 0x0d, + 0x0d, 0x01, 0x01, + 0x0e, 0x01, 0x01, + 0x0f, 0x01, 0x26, + 0x10, 0x01, 0x26, + 0x11, 0x01, 0x00, + 0x12, 0x01, 0x00, + 0x13, 0x01, 0x02, + 0x14, 0x01, 0x00, + 0x15, 0x01, 0x00, + 0x16, 0x01, 0x00, + 0x17, 0x01, 0x00, + 0x18, 0x01, 0x00, + 0x19, 0x01, 0x00, + 0x1a, 0x01, 0x00, + 0x1b, 0x01, 0x00, + 0x1c, 0x01, 0x00, + 0x1d, 0x01, 0x00, + 0x1e, 0x01, 0x40, + 0x1f, 0x01, 0x00, + 0x20, 0x01, 0x06, + 0x21, 0x01, 0x01, + 0x22, 0x01, 0x00, + 0x23, 0x01, 0x00, + 0x24, 0x01, 0x00, + 0x25, 0x01, 0x00, + 0x26, 0x01, 0x00, + 0x27, 0x01, 0x00, + 0x28, 0x01, 0x33, + 0x29, 0x01, 0x03, + 0x2a, 0x01, 0x00, + 0x2b, 0x01, 0x00, + 0x2c, 0x01, 0x00, + 0x2d, 0x01, 0x00, + 0x2e, 0x01, 0x00, + 0x2f, 0x01, 0x00, + 0x30, 0x01, 0x00, + 0x31, 0x01, 0x00, + 0x32, 0x01, 0x00, + 0x33, 0x01, 0x00, + 0x34, 0x01, 0x00, + 0x35, 0x01, 0x00, + 0x36, 0x01, 0x00, + 0x37, 0x01, 0x00, + 0x38, 0x01, 0x00, + 0x39, 0x01, 0x00, + 0x3a, 0x01, 0x00, + 0x3b, 0x01, 0x00, + 0x3c, 0x01, 0x00, + 0x3d, 0x01, 0x00, + 0x3e, 0x01, 0x00, + 0x3f, 0x01, 0x00, + 0x40, 0x01, 0x00, + 0x41, 0x01, 0x00, + 0x42, 0x01, 0x00, + 0x43, 0x01, 0x00, + 0x44, 0x01, 0x00, + 0x50, 0x01, 0x01, + 0x51, 0x01, 0x23, + 0x52, 0x01, 0x45, + 0x53, 0x01, 0x67, + 0x54, 0x01, 0x89, + 0x55, 0x01, 0xab, + 0x56, 0x01, 0x01, + 0x57, 0x01, 0x23, + 0x58, 0x01, 0x45, + 0x59, 0x01, 0x67, + 0x5a, 0x01, 0x89, + 0x5b, 0x01, 0xab, + 0x5c, 0x01, 0xcd, + 0x5d, 0x01, 0xef, + 0x5e, 0x01, 0x11, + 0x5f, 0x01, 0x02, + 0x60, 0x01, 0x00, + 0x61, 0x01, 0x07, + 0x62, 0x01, 0x06, + 0x63, 0x01, 0x0e, + 0x64, 0x01, 0x0f, + 0x65, 0x01, 0x0c, + 0x66, 0x01, 0x0d, + 0x67, 0x01, 0x02, + 0x68, 0x01, 0x02, + 0x69, 0x01, 0x02, + 0x6a, 0x01, 0x02, + 0x6b, 0x01, 0x02, + 0x6c, 0x01, 0x02, + 0x6d, 0x01, 0x02, + 0x6e, 0x01, 0x02, + 0x6f, 0x01, 0x02, + 0x70, 0x01, 0x02, + 0x71, 0x01, 0x02, + 0x72, 0x01, 0x02, + 0x73, 0x01, 0x05, + 0x74, 0x01, 0x01, + 0x75, 0x01, 0x02, + 0x76, 0x01, 0x00, + 0x77, 0x01, 0x07, + 0x78, 0x01, 0x06, + 0x79, 0x01, 0x0e, + 0x7a, 0x01, 0x0f, + 0x7b, 0x01, 0x0c, + 0x7c, 0x01, 0x0d, + 0x7d, 0x01, 0x02, + 0x7e, 0x01, 0x02, + 0x7f, 0x01, 0x02, + 0x80, 0x01, 0x02, + 0x81, 0x01, 0x02, + 0x82, 0x01, 0x02, + 0x83, 0x01, 0x02, + 0x84, 0x01, 0x02, + 0x85, 0x01, 0x02, + 0x86, 0x01, 0x02, + 0x87, 0x01, 0x02, + 0x88, 0x01, 0x02, + 0x89, 0x01, 0x05, + 0x8a, 0x01, 0x01, + // CMD_Page 4 + 0xff, 0x03, 0x98, 0x81, 0x04, + 0x38, 0x01, 0x01, + 0x39, 0x01, 0x00, + 0x6c, 0x01, 0x15, + 0x6e, 0x01, 0x1a, + 0x6f, 0x01, 0x25, + 0x3a, 0x01, 0xa4, + 0x8d, 0x01, 0x20, + 0x87, 0x01, 0xba, + 0x3b, 0x01, 0x98, + // CMD_Page 1 + 0xff, 0x03, 0x98, 0x81, 0x01, + 0x22, 0x01, 0x0a, + 0x31, 0x01, 0x00, + 0x50, 0x01, 0x6b, + 0x51, 0x01, 0x66, + 0x53, 0x01, 0x73, + 0x55, 0x01, 0x8b, + 0x60, 0x01, 0x1b, + 0x61, 0x01, 0x01, + 0x62, 0x01, 0x0c, + 0x63, 0x01, 0x00, + // Gamma P + 0xa0, 0x01, 0x00, + 0xa1, 0x01, 0x15, + 0xa2, 0x01, 0x1f, + 0xa3, 0x01, 0x13, + 0xa4, 0x01, 0x11, + 0xa5, 0x01, 0x21, + 0xa6, 0x01, 0x17, + 0xa7, 0x01, 0x1b, + 0xa8, 0x01, 0x6b, + 0xa9, 0x01, 0x1e, + 0xaa, 0x01, 0x2b, + 0xab, 0x01, 0x5d, + 0xac, 0x01, 0x19, + 0xad, 0x01, 0x14, + 0xae, 0x01, 0x4b, + 0xaf, 0x01, 0x1d, + 0xb0, 0x01, 0x27, + 0xb1, 0x01, 0x49, + 0xb2, 0x01, 0x5d, + 0xb3, 0x01, 0x39, + // Gamma N + 0xc0, 0x01, 0x00, + 0xc1, 0x01, 0x01, + 0xc2, 0x01, 0x0c, + 0xc3, 0x01, 0x11, + 0xc4, 0x01, 0x15, + 0xc5, 0x01, 0x28, + 0xc6, 0x01, 0x1b, + 0xc7, 0x01, 0x1c, + 0xc8, 0x01, 0x62, + 0xc9, 0x01, 0x1c, + 0xca, 0x01, 0x29, + 0xcb, 0x01, 0x60, + 0xcc, 0x01, 0x16, + 0xcd, 0x01, 0x17, + 0xce, 0x01, 0x4a, + 0xcf, 0x01, 0x23, + 0xd0, 0x01, 0x24, + 0xd1, 0x01, 0x4f, + 0xd2, 0x01, 0x5f, + 0xd3, 0x01, 0x39, + // CMD_Page 0 + 0xff, 0x03, 0x98, 0x81, 0x00, + 0x35, 0x00, + 0xfe, 0x00, + 0x29, 0x00, +}; + +// I2C addresses +#define GOODIX_TOUCH_ADDRESS 0x14 +#define ST7123_ADDRESS 0x55 +#define I2C_DEV_ADDR_PI4IOE1 0x43 + +// PI4IOE GPIO expander registers +#define PI4IO_REG_CHIP_RESET 0x01 +#define PI4IO_REG_IO_DIR 0x03 +#define PI4IO_REG_OUT_SET 0x05 +#define PI4IO_REG_OUT_H_IM 0x07 +#define PI4IO_REG_PULL_SEL 0x0D +#define PI4IO_REG_PULL_EN 0x0B + +void board_init(void) { + // Initialize I2C for GPIO expander and display detection + busio_i2c_obj_t *i2c = common_hal_board_create_i2c(0); + + // Initialize PI4IOE GPIO expander to control LCD reset + uint8_t write_buf[2]; + + common_hal_busio_i2c_try_lock(i2c); + + // Chip reset + write_buf[0] = PI4IO_REG_CHIP_RESET; + write_buf[1] = 0xFF; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Set IO direction (bit 7 as output for LCD reset) + write_buf[0] = PI4IO_REG_IO_DIR; + write_buf[1] = 0b01111111; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Set output high-impedance mode + write_buf[0] = PI4IO_REG_OUT_H_IM; + write_buf[1] = 0b00000000; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Set pull select + write_buf[0] = PI4IO_REG_PULL_SEL; + write_buf[1] = 0b01111111; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Enable pull resistors + write_buf[0] = PI4IO_REG_PULL_EN; + write_buf[1] = 0b01111111; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Set output state (including LCD reset) + write_buf[0] = PI4IO_REG_OUT_SET; + write_buf[1] = 0b01110110; + common_hal_busio_i2c_write(i2c, I2C_DEV_ADDR_PI4IOE1, write_buf, 2); + + // Small delay for reset to take effect + mp_hal_delay_ms(100); + + // Probe I2C bus to detect which display is present + bool has_goodix = common_hal_busio_i2c_probe(i2c, GOODIX_TOUCH_ADDRESS); + bool has_st7123 = common_hal_busio_i2c_probe(i2c, ST7123_ADDRESS); + + common_hal_busio_i2c_unlock(i2c); + + // Configure display parameters based on detected hardware + uint32_t bus_frequency; + uint32_t pixel_clock_frequency; + const uint8_t *init_sequence; + size_t init_sequence_len; + uint32_t hsync_pulse_width, hsync_back_porch, hsync_front_porch; + uint32_t vsync_pulse_width, vsync_back_porch, vsync_front_porch; + + if (has_goodix) { + // ILI9881C display with Goodix touch + bus_frequency = 730000000; + pixel_clock_frequency = 60000000; + init_sequence = ili9881c_init_sequence; + init_sequence_len = sizeof(ili9881c_init_sequence); + hsync_pulse_width = 40; + hsync_back_porch = 140; + hsync_front_porch = 40; + vsync_pulse_width = 4; + vsync_back_porch = 20; + vsync_front_porch = 20; + } else if (has_st7123) { + // ST7123 display + bus_frequency = 965000000; + pixel_clock_frequency = 70000000; + init_sequence = NULL; // ST7123 doesn't use init sequence + init_sequence_len = 0; + hsync_pulse_width = 2; + hsync_back_porch = 40; + hsync_front_porch = 40; + vsync_pulse_width = 2; + vsync_back_porch = 8; + vsync_front_porch = 220; + + // Not tested now + return; + } else { + return; + } + + // Initialize the statically allocated MIPI DSI bus + board_mipidsi_bus.base.type = &mipidsi_bus_type; + common_hal_mipidsi_bus_construct(&board_mipidsi_bus, bus_frequency, 2); // 2 lanes + + // Allocate display bus for the display object + primary_display_bus_t *display_bus_obj = allocate_display_bus_or_raise(); + mipidsi_display_obj_t *display = &display_bus_obj->mipidsi; + display->base.type = &mipidsi_display_type; + + common_hal_mipidsi_display_construct( + display, + &board_mipidsi_bus, // Use statically allocated bus + init_sequence, + init_sequence_len, + 0, // virtual_channel + 720, // width + 1280, // height + 0, // rotation + 16, // color_depth + &pin_GPIO22, // backlight_pin (LCD_BL) + 0.1f, // brightness + 60, // native_frames_per_second + true, // backlight_on_high + hsync_pulse_width, + hsync_back_porch, + hsync_front_porch, + vsync_pulse_width, + vsync_back_porch, + vsync_front_porch, + pixel_clock_frequency + ); + + // Create framebuffer display + framebufferio_framebufferdisplay_obj_t *fb_display = &allocate_display()->framebuffer_display; + fb_display->base.type = &framebufferio_framebufferdisplay_type; + + common_hal_framebufferio_framebufferdisplay_construct( + fb_display, + MP_OBJ_FROM_PTR(display), + 0, // rotation + true // auto_refresh + ); +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/m5stack_tab5/mpconfigboard.h b/ports/espressif/boards/m5stack_tab5/mpconfigboard.h new file mode 100644 index 0000000000000..6c748e93ecdbe --- /dev/null +++ b/ports/espressif/boards/m5stack_tab5/mpconfigboard.h @@ -0,0 +1,29 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "M5Stack Tab5" +#define MICROPY_HW_MCU_NAME "ESP32P4" + +// I2C bus for touch, IMU, RTC, power monitor, and GPIO expanders +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO32, .sda = &pin_GPIO31}} + +// UART +#define DEFAULT_UART_BUS_RX (&pin_GPIO38) +#define DEFAULT_UART_BUS_TX (&pin_GPIO37) + +// SPI on M5-Bus +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO5) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO18) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO19) + +// Use the second USB device (numbered 0 and 1) +#define CIRCUITPY_USB_DEVICE_INSTANCE 0 +#define CIRCUITPY_ESP32P4_SWAP_LSFS (1) diff --git a/ports/espressif/boards/m5stack_tab5/mpconfigboard.mk b/ports/espressif/boards/m5stack_tab5/mpconfigboard.mk new file mode 100644 index 0000000000000..4871a259e7a48 --- /dev/null +++ b/ports/espressif/boards/m5stack_tab5/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x303A +USB_PID = 0x832B +USB_PRODUCT = "M5Stack Tab5" +USB_MANUFACTURER = "M5Stack" + +IDF_TARGET = esp32p4 + +CIRCUITPY_ESP_FLASH_SIZE = 16MB +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m + +CIRCUITPY_ESP_PSRAM_SIZE = 32MB +CIRCUITPY_ESP_PSRAM_MODE = hpi +CIRCUITPY_ESP_PSRAM_FREQ = 200m diff --git a/ports/espressif/boards/m5stack_tab5/pins.c b/ports/espressif/boards/m5stack_tab5/pins.c new file mode 100644 index 0000000000000..c64d8803c02cf --- /dev/null +++ b/ports/espressif/boards/m5stack_tab5/pins.c @@ -0,0 +1,132 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // M5-Bus and general GPIO + { MP_ROM_QSTR(MP_QSTR_G2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_G3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_G4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_G6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_G7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_G16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_G17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_G35), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_G45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_G47), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_G48), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_G51), MP_ROM_PTR(&pin_GPIO51) }, + { MP_ROM_QSTR(MP_QSTR_G52), MP_ROM_PTR(&pin_GPIO52) }, + + // SPI (on M5-Bus) + { MP_ROM_QSTR(MP_QSTR_G5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_G18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_G19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO19) }, + + // UART (on M5-Bus) + { MP_ROM_QSTR(MP_QSTR_G37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_TXD0), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_G38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_RXD0), MP_ROM_PTR(&pin_GPIO38) }, + + // I2C (Touch, IMU, RTC, Power Monitor, GPIO Expanders) + { MP_ROM_QSTR(MP_QSTR_G31), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_G32), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO32) }, + + // Touch Panel + { MP_ROM_QSTR(MP_QSTR_G23), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_TP_INT), MP_ROM_PTR(&pin_GPIO23) }, + + // LCD + { MP_ROM_QSTR(MP_QSTR_G22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_GPIO22) }, + + // Audio ES8388/ES7210 + { MP_ROM_QSTR(MP_QSTR_G26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DSDIN), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_G27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SCLK), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_G28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_I2S_ASDOUT), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_G29), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCK), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_G30), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_I2S_MCLK), MP_ROM_PTR(&pin_GPIO30) }, + + // Camera + { MP_ROM_QSTR(MP_QSTR_G36), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_CAM_MCLK), MP_ROM_PTR(&pin_GPIO36) }, + + // microSD Card (SDIO mode) + { MP_ROM_QSTR(MP_QSTR_G39), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT0), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_G40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT1), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_G41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT2), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_G42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_SD_DAT3), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_G43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_G44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_SD_CMD), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO44) }, + + // RS485 + { MP_ROM_QSTR(MP_QSTR_G20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_RS485_TX), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_G21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_RS485_RX), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_G34), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_RS485_DIR), MP_ROM_PTR(&pin_GPIO34) }, + + // HY2.0-4P PORT.A + { MP_ROM_QSTR(MP_QSTR_G53), MP_ROM_PTR(&pin_GPIO53) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_Y), MP_ROM_PTR(&pin_GPIO53) }, + { MP_ROM_QSTR(MP_QSTR_G54), MP_ROM_PTR(&pin_GPIO54) }, + { MP_ROM_QSTR(MP_QSTR_PORTA_W), MP_ROM_PTR(&pin_GPIO54) }, + + // ESP32-C6 SDIO interface + { MP_ROM_QSTR(MP_QSTR_G8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_D3), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_G9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_D2), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_G10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_D1), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_G11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_D0), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_G12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_CK), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_G13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_C6_SDIO2_CMD), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_G14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_C6_IO2), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_G15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_C6_RESET), MP_ROM_PTR(&pin_GPIO15) }, + + // I2C and SPI objects + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)} +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/m5stack_tab5/sdkconfig b/ports/espressif/boards/m5stack_tab5/sdkconfig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/espressif/boards/makerfabs_tft7/board.c b/ports/espressif/boards/makerfabs_tft7/board.c index dd0ffa2652836..a994cfef38118 100644 --- a/ports/espressif/boards/makerfabs_tft7/board.c +++ b/ports/espressif/boards/makerfabs_tft7/board.c @@ -6,13 +6,14 @@ #include "supervisor/board.h" #include "mpconfigboard.h" + #include "shared-bindings/board/__init__.h" #include "shared-bindings/dotclockframebuffer/DotClockFramebuffer.h" #include "shared-bindings/dotclockframebuffer/__init__.h" #include "shared-bindings/framebufferio/FramebufferDisplay.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/displayio/__init__.h" -#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" static const mcu_pin_obj_t *blue_pins[] = { &pin_GPIO8, @@ -40,14 +41,14 @@ static const mcu_pin_obj_t *red_pins[] = { static void display_init(void) { mp_int_t height = 0, width = 0, frequency = 0; - os_getenv_err_t result; + settings_err_t result; - result = common_hal_os_getenv_int("CIRCUITPY_DISPLAY_WIDTH", &width); - if (result == GETENV_OK && width == 800) { + result = settings_get_int("CIRCUITPY_DISPLAY_WIDTH", &width); + if (result == SETTINGS_OK && width == 800) { width = 800; height = 480; frequency = 6500000; - } else if (result == GETENV_OK && width == 1024) { + } else if (result == SETTINGS_OK && width == 1024) { width = 1024; height = 600; frequency = 10000000; diff --git a/ports/espressif/boards/morpheans_morphesp-240/board.c b/ports/espressif/boards/morpheans_morphesp-240/board.c index 5b1c3f82bbbdb..44266cce6ed9f 100644 --- a/ports/espressif/boards/morpheans_morphesp-240/board.c +++ b/ports/espressif/boards/morpheans_morphesp-240/board.c @@ -138,9 +138,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO14, // DC - &pin_GPIO10, // CS - &pin_GPIO9, // RST + MP_OBJ_FROM_PTR(&pin_GPIO14), // DC + MP_OBJ_FROM_PTR(&pin_GPIO10), // CS + MP_OBJ_FROM_PTR(&pin_GPIO9), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/oxocard_artwork/board.c b/ports/espressif/boards/oxocard_artwork/board.c index a27cdb7003bdc..5fe8e8145583a 100644 --- a/ports/espressif/boards/oxocard_artwork/board.c +++ b/ports/espressif/boards/oxocard_artwork/board.c @@ -43,9 +43,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO15, // CS - &pin_GPIO4, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO4), // RST 24000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/oxocard_connect/board.c b/ports/espressif/boards/oxocard_connect/board.c index 89be45c82262e..2c01381147437 100644 --- a/ports/espressif/boards/oxocard_connect/board.c +++ b/ports/espressif/boards/oxocard_connect/board.c @@ -44,9 +44,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO15, // CS - &pin_GPIO4, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO4), // RST 24000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/oxocard_galaxy/board.c b/ports/espressif/boards/oxocard_galaxy/board.c index a27cdb7003bdc..5fe8e8145583a 100644 --- a/ports/espressif/boards/oxocard_galaxy/board.c +++ b/ports/espressif/boards/oxocard_galaxy/board.c @@ -43,9 +43,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO15, // CS - &pin_GPIO4, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO4), // RST 24000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/oxocard_science/board.c b/ports/espressif/boards/oxocard_science/board.c index a27cdb7003bdc..5fe8e8145583a 100644 --- a/ports/espressif/boards/oxocard_science/board.c +++ b/ports/espressif/boards/oxocard_science/board.c @@ -43,9 +43,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO27, // DC - &pin_GPIO15, // CS - &pin_GPIO4, // RST + MP_OBJ_FROM_PTR(&pin_GPIO27), // DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // CS + MP_OBJ_FROM_PTR(&pin_GPIO4), // RST 24000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c new file mode 100644 index 0000000000000..a3a9eec047145 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h new file mode 100644 index 0000000000000..2114fecf373a4 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Prokyber Ai-On-The-Edge-Cam" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO12) + +#define MICROPY_HW_LED_STATUS (&pin_GPIO48) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}} diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk new file mode 100644 index 0000000000000..82419bb80745c --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x1209 +USB_PID = 0xDABB +USB_PRODUCT = "AI-ON-THE-EDGE-CAM" +USB_MANUFACTURER = "Prokyber" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m +CIRCUITPY_ESP_PSRAM_SIZE = 8MB diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c new file mode 100644 index 0000000000000..68dc40781a480 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/pins.c @@ -0,0 +1,106 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/objtuple.h" +#include "shared-bindings/board/__init__.h" + +static const mp_rom_obj_tuple_t camera_data_tuple = { + {&mp_type_tuple}, + 8, + { + MP_ROM_PTR(&pin_GPIO11), + MP_ROM_PTR(&pin_GPIO9), + MP_ROM_PTR(&pin_GPIO8), + MP_ROM_PTR(&pin_GPIO10), + MP_ROM_PTR(&pin_GPIO47), + MP_ROM_PTR(&pin_GPIO18), + MP_ROM_PTR(&pin_GPIO17), + MP_ROM_PTR(&pin_GPIO16), + } +}; + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_PER_EN), MP_ROM_PTR(&pin_GPIO46) }, + + // SD + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, + + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, + + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, + + // Ethernet + { MP_ROM_QSTR(MP_QSTR_ETH_RST), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_INT), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_MOSI), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_MISO), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_CLK), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_ETH_CS), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, + + // LEDs + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_IO48), MP_ROM_PTR(&pin_GPIO48) }, + + // UART + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, + + // I2C + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA), MP_ROM_PTR(&camera_data_tuple) }, + + { MP_ROM_QSTR(MP_QSTR_CAMERA_VSYNC), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_HREF), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA9), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_XCLK), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA8), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA7), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_PCLK), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA6), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA2), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA5), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA3), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_DATA4), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_CAMERA_SIOD), MP_ROM_PTR(&pin_GPIO4) }, // SDA + { MP_ROM_QSTR(MP_QSTR_CAMERA_SIOC), MP_ROM_PTR(&pin_GPIO5) }, // SCL + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig new file mode 100644 index 0000000000000..ac1c535717672 --- /dev/null +++ b/ports/espressif/boards/prokyber_ai_on_the_edge_cam/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP +CONFIG_OV2640_SUPPORT=y +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/mpconfigboard.h b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/mpconfigboard.h index c05b6a93a1054..d7f24b255d69c 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/mpconfigboard.h +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/mpconfigboard.h @@ -12,9 +12,9 @@ #define DEFAULT_UART_BUS_RX (&pin_GPIO44) #define DEFAULT_UART_BUS_TX (&pin_GPIO43) -#define DEFAULT_SPI_BUS_SCK (&pin_GPIO7) -#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO9) -#define DEFAULT_SPI_BUS_MISO (&pin_GPIO8) +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO7) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO9) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO8) -#define DEFAULT_I2C_BUS_SCL (&pin_GPIO6) -#define DEFAULT_I2C_BUS_SDA (&pin_GPIO5) +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO6) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO5) diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c index aa3a6f7b7f577..fbb6014685ca4 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/pins.c @@ -31,49 +31,92 @@ static const mp_rom_obj_tuple_t camera_data_tuple = { static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS - { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO1) }, - { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO2) }, - { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO4) }, - { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO5) }, - { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO6) }, { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO5) }, - { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO6) }, - { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, - { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO9) }, - { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO8) }, - { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SD), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_CAM_VSYNC), MP_ROM_PTR(&pin_GPIO38) }, + + { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_I2S_SCK), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_MTCK), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_CAM_SCL), MP_ROM_PTR(&pin_GPIO39) }, + + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WS), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_MTDO), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO40) }, + + { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_MTDI), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_MIC_DATA), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_RX_1), MP_ROM_PTR(&pin_GPIO41) }, + + { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_MTMS), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_MIC_CLK), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_TX_1), MP_ROM_PTR(&pin_GPIO42) }, + + { MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_ADC), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CAM_XCLK), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_SCK_1), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_CAM_PCLK), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_MISO_1), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D5), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_MOSI_1), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D6), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO21) }, { MP_ROM_QSTR(MP_QSTR_SDCS), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_CAM_DATA), MP_ROM_PTR(&camera_data_tuple) }, { MP_ROM_QSTR(MP_QSTR_CAM_D0), MP_ROM_PTR(&pin_GPIO15) }, { MP_ROM_QSTR(MP_QSTR_CAM_D1), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_CAM_D2), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_CAM_D3), MP_ROM_PTR(&pin_GPIO16) }, { MP_ROM_QSTR(MP_QSTR_CAM_D4), MP_ROM_PTR(&pin_GPIO14) }, - { MP_ROM_QSTR(MP_QSTR_CAM_D5), MP_ROM_PTR(&pin_GPIO12) }, - { MP_ROM_QSTR(MP_QSTR_CAM_D6), MP_ROM_PTR(&pin_GPIO11) }, { MP_ROM_QSTR(MP_QSTR_CAM_D7), MP_ROM_PTR(&pin_GPIO48) }, - { MP_ROM_QSTR(MP_QSTR_CAM_XCLK), MP_ROM_PTR(&pin_GPIO10) }, { MP_ROM_QSTR(MP_QSTR_CAM_HREF), MP_ROM_PTR(&pin_GPIO47) }, - { MP_ROM_QSTR(MP_QSTR_CAM_PCLK), MP_ROM_PTR(&pin_GPIO13) }, - { MP_ROM_QSTR(MP_QSTR_CAM_VSYNC), MP_ROM_PTR(&pin_GPIO38) }, - { MP_ROM_QSTR(MP_QSTR_CAM_SCL), MP_ROM_PTR(&pin_GPIO39) }, - { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO40) }, - { MP_ROM_QSTR(MP_QSTR_MIC_DATA), MP_ROM_PTR(&pin_GPIO41) }, - { MP_ROM_QSTR(MP_QSTR_MIC_CLK), MP_ROM_PTR(&pin_GPIO42) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, diff --git a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/sdkconfig b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/sdkconfig index 919f4d9f345ff..2d98e8225429b 100644 --- a/ports/espressif/boards/seeed_xiao_esp32_s3_sense/sdkconfig +++ b/ports/espressif/boards/seeed_xiao_esp32_s3_sense/sdkconfig @@ -5,15 +5,13 @@ # Component config # # -# LWIP # -# end of LWIP # Camera configuration # -CONFIG_OV2640_SUPPORT=y # CONFIG_OV7725_SUPPORT is not set -# CONFIG_OV3660_SUPPORT is not set +CONFIG_OV2640_SUPPORT=y # end of Camera configuration + # end of Component config # end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/board.c b/ports/espressif/boards/sensebox_eye_esp32s3/board.c new file mode 100644 index 0000000000000..fea7fe2c3002e --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "mpconfigboard.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h new file mode 100644 index 0000000000000..3908c492f1b01 --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "senseBox-eye ESP32S3" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO45) + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO1) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO2) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk new file mode 100644 index 0000000000000..55e822c5dea4a --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/mpconfigboard.mk @@ -0,0 +1,21 @@ +USB_VID = 0x303A +USB_PID = 0x82D2 + +USB_PRODUCT = "senseBox-eye ESP32S3" +USB_MANUFACTURER = "senseBox" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_SIZE = 8MB +CIRCUITPY_ESP_FLASH_FREQ = 80m + +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +CIRCUITPY_ESPCAMERA = 1 +CIRCUITPY_AUDIOBUSIO = 1 + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/pins.c b/ports/espressif/boards/sensebox_eye_esp32s3/pins.c new file mode 100644 index 0000000000000..c218fafa918ce --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/pins.c @@ -0,0 +1,82 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/objtuple.h" +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +CIRCUITPY_BOARD_BUS_SINGLETON(sscb_i2c, i2c, 2) + +static const mp_rom_obj_tuple_t camera_data_tuple = { + // The order matters. + // They must be ordered from low to high (CAM_D0, CAM_D1...CAM_D7). + + // Do not include any of the control pins in here. + {&mp_type_tuple}, + 8, + { + MP_ROM_PTR(&pin_GPIO11), + MP_ROM_PTR(&pin_GPIO9), + MP_ROM_PTR(&pin_GPIO8), + MP_ROM_PTR(&pin_GPIO10), + MP_ROM_PTR(&pin_GPIO12), + MP_ROM_PTR(&pin_GPIO18), + MP_ROM_PTR(&pin_GPIO17), + MP_ROM_PTR(&pin_GPIO16), + } +}; + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO0) }, + {MP_ROM_QSTR(MP_QSTR_BUTTON_SW), MP_ROM_PTR(&pin_GPIO47) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_A14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_A48), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_D48), MP_ROM_PTR(&pin_GPIO48) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO45) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_UART_ENABLE), MP_ROM_PTR(&pin_GPIO26) }, + + { MP_ROM_QSTR(MP_QSTR_CAM_DATA), MP_ROM_PTR(&camera_data_tuple) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D0), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D1), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D2), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D3), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D4), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D5), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D6), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_CAM_D7), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_CAM_XCLK), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_CAM_HREF), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_CAM_PCLK), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_CAM_VSYNC), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_CAM_SCL), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_CAM_SDA), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_PWDN), MP_ROM_PTR(&pin_GPIO46) }, + + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCLK), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_SD_ENABLE), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/sensebox_eye_esp32s3/sdkconfig b/ports/espressif/boards/sensebox_eye_esp32s3/sdkconfig new file mode 100644 index 0000000000000..919f4d9f345ff --- /dev/null +++ b/ports/espressif/boards/sensebox_eye_esp32s3/sdkconfig @@ -0,0 +1,19 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP +# Camera configuration +# +CONFIG_OV2640_SUPPORT=y +# CONFIG_OV7725_SUPPORT is not set +# CONFIG_OV3660_SUPPORT is not set +# end of Camera configuration +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.mk b/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.mk index 9d3fbecabde71..6b97c870cc032 100644 --- a/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.mk +++ b/ports/espressif/boards/sensebox_mcu_esp32s2/mpconfigboard.mk @@ -1,7 +1,7 @@ USB_VID = 0x303A USB_PID = 0x81B9 USB_PRODUCT = "senseBox MCU-S2 ESP32S2" -USB_MANUFACTURER = "Espressif" +USB_MANUFACTURER = "senseBox" IDF_TARGET = esp32s2 diff --git a/ports/espressif/boards/spotpear_esp32c3_lcd_1_44/board.c b/ports/espressif/boards/spotpear_esp32c3_lcd_1_44/board.c index 89bcbf6b8dd7f..f4288ae45ace5 100644 --- a/ports/espressif/boards/spotpear_esp32c3_lcd_1_44/board.c +++ b/ports/espressif/boards/spotpear_esp32c3_lcd_1_44/board.c @@ -42,9 +42,9 @@ static bool display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO0, // DC - &pin_GPIO2, // CS - &pin_GPIO5, // RST + MP_OBJ_FROM_PTR(&pin_GPIO0), // DC + MP_OBJ_FROM_PTR(&pin_GPIO2), // CS + MP_OBJ_FROM_PTR(&pin_GPIO5), // RST 10000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/spotpear_esp32c3_lcd_1_69/board.c b/ports/espressif/boards/spotpear_esp32c3_lcd_1_69/board.c index 6299402e8e07a..30879e714045d 100644 --- a/ports/espressif/boards/spotpear_esp32c3_lcd_1_69/board.c +++ b/ports/espressif/boards/spotpear_esp32c3_lcd_1_69/board.c @@ -39,9 +39,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO2, // DC - &pin_GPIO3, // CS - &pin_GPIO10, // RST + MP_OBJ_FROM_PTR(&pin_GPIO2), // DC + MP_OBJ_FROM_PTR(&pin_GPIO3), // CS + MP_OBJ_FROM_PTR(&pin_GPIO10), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/sqfmi_watchy/board.c b/ports/espressif/boards/sqfmi_watchy/board.c index 393b8b759028a..9cea4e9809598 100644 --- a/ports/espressif/boards/sqfmi_watchy/board.c +++ b/ports/espressif/boards/sqfmi_watchy/board.c @@ -150,9 +150,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO10, // EPD_DC Command or data - &pin_GPIO5, // EPD_CS Chip select - &pin_GPIO9, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO10), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO9), // EPD_RST Reset 2000000, // Baudrate 0, // Polarity 0 // Phase @@ -160,40 +160,33 @@ void board_init(void) { epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - (double)0.01, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 200, // width - 200, // height - 200, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 0, // rotation - 0x44, // set_column_window_command - 0x45, // set_row_window_command - 0x4E, // set_current_column_command - 0x4F, // set_current_row_command - 0x24, // write_black_ram_command - true, // black_bits_inverted - 0x26, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - refresh_sequence, sizeof(refresh_sequence), - (double)1.0, // refresh_time - &pin_GPIO19, // busy_pin - true, // busy_state - (double)5.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - true // address_little_endian - ); + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.start_up_time = 0.01; + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 200; + args.height = 200; + args.ram_width = 200; + args.ram_height = 296; + args.set_column_window_command = 0x44; + args.set_row_window_command = 0x45; + args.set_current_column_command = 0x4E; + args.set_current_row_command = 0x4F; + args.write_black_ram_command = 0x24; + args.black_bits_inverted = true; + args.write_color_ram_command = 0x26; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO19; + args.busy_state = true; + args.seconds_per_frame = 5.0; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } bool board_requests_safe_mode(void) { diff --git a/ports/espressif/boards/sunton_esp32_2424S012/board.c b/ports/espressif/boards/sunton_esp32_2424S012/board.c index c313ded863764..731694a4e1b8a 100644 --- a/ports/espressif/boards/sunton_esp32_2424S012/board.c +++ b/ports/espressif/boards/sunton_esp32_2424S012/board.c @@ -105,9 +105,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO2, // DC - &pin_GPIO10, // CS - NULL, // RST + MP_OBJ_FROM_PTR(&pin_GPIO2), // DC + MP_OBJ_FROM_PTR(&pin_GPIO10), // CS + MP_OBJ_NULL, // RST 80000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/sunton_esp32_2432S024C/board.c b/ports/espressif/boards/sunton_esp32_2432S024C/board.c index 8b4201d88e81d..de7504a871532 100644 --- a/ports/espressif/boards/sunton_esp32_2432S024C/board.c +++ b/ports/espressif/boards/sunton_esp32_2432S024C/board.c @@ -6,13 +6,14 @@ #include "supervisor/board.h" #include "mpconfigboard.h" + +#include "common-hal/microcontroller/Pin.h" +#include "driver/gpio.h" #include "shared-bindings/board/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/displayio/__init__.h" #include "shared-module/displayio/mipi_constants.h" -#include "driver/gpio.h" -#include "common-hal/microcontroller/Pin.h" -#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" uint8_t display_init_sequence[] = { 0x01, 0x80, 0x80, // # Software reset then delay 0x80 (128ms) @@ -50,17 +51,17 @@ static void display_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO2, // TFT_DC Command or data - &pin_GPIO15, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO2), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO15), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 6000000, // Baudrate 0, // Polarity 0); // Phase busdisplay_busdisplay_obj_t *display = &allocate_display()->display; display->base.type = &busdisplay_busdisplay_type; - os_getenv_err_t result = common_hal_os_getenv_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); - if (result != GETENV_OK) { + settings_err_t result = settings_get_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); + if (result != SETTINGS_OK) { rotation = 0; } diff --git a/ports/espressif/boards/sunton_esp32_2432S024C/pins.c b/ports/espressif/boards/sunton_esp32_2432S024C/pins.c index a69f3150e9248..6544017cd999c 100644 --- a/ports/espressif/boards/sunton_esp32_2432S024C/pins.c +++ b/ports/espressif/boards/sunton_esp32_2432S024C/pins.c @@ -46,7 +46,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO5) }, - // ILI9341 dsplay (spi) + // ILI9341 display (spi) { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_GPIO13) }, { MP_ROM_QSTR(MP_QSTR_LCD_MISO), MP_ROM_PTR(&pin_GPIO12) }, { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_GPIO14) }, diff --git a/ports/espressif/boards/sunton_esp32_2432S028/board.c b/ports/espressif/boards/sunton_esp32_2432S028/board.c index b249c45dd8fa2..214e3b13e158c 100644 --- a/ports/espressif/boards/sunton_esp32_2432S028/board.c +++ b/ports/espressif/boards/sunton_esp32_2432S028/board.c @@ -6,13 +6,14 @@ #include "supervisor/board.h" #include "mpconfigboard.h" + +#include "common-hal/microcontroller/Pin.h" +#include "driver/gpio.h" #include "shared-bindings/board/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/displayio/__init__.h" #include "shared-module/displayio/mipi_constants.h" -#include "driver/gpio.h" -#include "common-hal/microcontroller/Pin.h" -#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" uint8_t display_init_sequence[] = { 0x01, 0x80, 0x80, // # Software reset then delay 0x80 (128ms) @@ -50,17 +51,17 @@ static void display_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO2, // TFT_DC Command or data - &pin_GPIO15, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO2), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO15), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 6000000, // Baudrate 0, // Polarity 0); // Phase busdisplay_busdisplay_obj_t *display = &allocate_display()->display; display->base.type = &busdisplay_busdisplay_type; - os_getenv_err_t result = common_hal_os_getenv_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); - if (result != GETENV_OK) { + settings_err_t result = settings_get_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); + if (result != SETTINGS_OK) { rotation = 0; } diff --git a/ports/espressif/boards/sunton_esp32_2432S028/pins.c b/ports/espressif/boards/sunton_esp32_2432S028/pins.c index b852ab11e04c2..7791c1685eb38 100644 --- a/ports/espressif/boards/sunton_esp32_2432S028/pins.c +++ b/ports/espressif/boards/sunton_esp32_2432S028/pins.c @@ -45,7 +45,7 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO5) }, - // ILI9341 dsplay (spi) + // ILI9341 display (spi) { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_GPIO13) }, { MP_ROM_QSTR(MP_QSTR_LCD_MISO), MP_ROM_PTR(&pin_GPIO12) }, { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_GPIO14) }, diff --git a/ports/espressif/boards/sunton_esp32_2432S032C/board.c b/ports/espressif/boards/sunton_esp32_2432S032C/board.c index b2160e0aa4f34..48e206040d819 100644 --- a/ports/espressif/boards/sunton_esp32_2432S032C/board.c +++ b/ports/espressif/boards/sunton_esp32_2432S032C/board.c @@ -43,9 +43,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO2, // TFT_DC - &pin_GPIO15, // TFT_CS - NULL, // TFT_RST + MP_OBJ_FROM_PTR(&pin_GPIO2), // TFT_DC + MP_OBJ_FROM_PTR(&pin_GPIO15), // TFT_CS + MP_OBJ_NULL, // TFT_RST 26600000, // Baudrate 0, // Polarity 0 // Phase diff --git a/ports/espressif/boards/sunton_esp32_8048S050/board.c b/ports/espressif/boards/sunton_esp32_8048S050/board.c index e01f857bfd980..a6f573a86e79b 100644 --- a/ports/espressif/boards/sunton_esp32_8048S050/board.c +++ b/ports/espressif/boards/sunton_esp32_8048S050/board.c @@ -6,13 +6,14 @@ #include "supervisor/board.h" #include "mpconfigboard.h" + #include "shared-bindings/board/__init__.h" #include "shared-bindings/dotclockframebuffer/DotClockFramebuffer.h" #include "shared-bindings/dotclockframebuffer/__init__.h" #include "shared-bindings/framebufferio/FramebufferDisplay.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-module/displayio/__init__.h" -#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" static const mcu_pin_obj_t *blue_pins[] = { &pin_GPIO8, @@ -47,8 +48,8 @@ static void display_init(void) { dotclockframebuffer_framebuffer_obj_t *framebuffer = &allocate_display_bus_or_raise()->dotclock; framebuffer->base.type = &dotclockframebuffer_framebuffer_type; - os_getenv_err_t result = common_hal_os_getenv_int("CIRCUITPY_DISPLAY_FREQUENCY", &frequency); - if (result != GETENV_OK) { + settings_err_t result = settings_get_int("CIRCUITPY_DISPLAY_FREQUENCY", &frequency); + if (result != SETTINGS_OK) { frequency = 12500000; } diff --git a/ports/espressif/boards/vidi_x/board.c b/ports/espressif/boards/vidi_x/board.c index e53dfd82ad9de..7ba7094b91778 100644 --- a/ports/espressif/boards/vidi_x/board.c +++ b/ports/espressif/boards/vidi_x/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO21, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO21), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 40000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/board.c b/ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/board.c index 7dd0e0c0fa2ab..e56c096e40371 100644 --- a/ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/board.c +++ b/ports/espressif/boards/waveshare_esp32_c6_lcd_1_47/board.c @@ -48,9 +48,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO15, // DC - &pin_GPIO14, // CS - &pin_GPIO21, // RST + MP_OBJ_FROM_PTR(&pin_GPIO15), // DC + MP_OBJ_FROM_PTR(&pin_GPIO14), // CS + MP_OBJ_FROM_PTR(&pin_GPIO21), // RST 80000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/waveshare_esp32_s2_pico_lcd/board.c b/ports/espressif/boards/waveshare_esp32_s2_pico_lcd/board.c index cb6fb3103382e..0bf710ec34c69 100644 --- a/ports/espressif/boards/waveshare_esp32_s2_pico_lcd/board.c +++ b/ports/espressif/boards/waveshare_esp32_s2_pico_lcd/board.c @@ -61,9 +61,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO18, // DC - &pin_GPIO9, // CS - &pin_GPIO21, // RST + MP_OBJ_FROM_PTR(&pin_GPIO18), // DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // CS + MP_OBJ_FROM_PTR(&pin_GPIO21), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c new file mode 100644 index 0000000000000..7b57f48bcdbdd --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/board.c @@ -0,0 +1,106 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/DigitalInOut.h" + +#include "shared-bindings/qspibus/QSPIBus.h" +#include "shared-bindings/busdisplay/BusDisplay.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" + +// RM690B0 AMOLED initialization sequence. +// Format: command byte, length | 0x80 (if delay), data bytes..., [delay ms] +// Based on vendor recommendations, tested with Waveshare 2.41" AMOLED panel. +// Non-const to match upstream common_hal_busdisplay_busdisplay_construct signature. +static uint8_t display_init_sequence[] = { + // Page select and configuration + 0xFE, 0x01, 0x20, // Enter user command mode + 0x26, 0x01, 0x0A, // Bias setting + 0x24, 0x01, 0x80, // Source output control + 0xFE, 0x01, 0x13, // Page 13 + 0xEB, 0x01, 0x0E, // Vendor command + 0xFE, 0x01, 0x00, // Return to page 0 + // Display configuration + 0x3A, 0x01, 0x55, // COLMOD: 16-bit RGB565 + 0xC2, 0x81, 0x00, 0x0A, // Vendor command + 10ms delay + 0x35, 0x00, // Tearing effect line on (no data) + 0x51, 0x81, 0x00, 0x0A, // Brightness control + 10ms delay + // Power on + 0x11, 0x80, 0x50, // Sleep out + 80ms delay + // Display window (MV=1: CASET→rows, RASET→cols) + 0x2A, 0x04, 0x00, 0x10, 0x01, 0xD1, // CASET: 16..465 (450px + 16 offset) + 0x2B, 0x04, 0x00, 0x00, 0x02, 0x57, // RASET: 0..599 (600px) + // Enable display + 0x29, 0x80, 0x0A, // Display on + 10ms delay + // Memory access: MV=1, ML=1 for landscape + 0x36, 0x81, 0x30, 0x0A, // MADCTL + 10ms delay + // Brightness + 0x51, 0x01, 0xFF, // Set brightness to maximum +}; + +void board_init(void) { + // 0. Enable display power before any bus/display init. + digitalio_digitalinout_obj_t power_pin; + power_pin.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(&power_pin, CIRCUITPY_LCD_POWER); + common_hal_digitalio_digitalinout_set_value(&power_pin, true); + common_hal_digitalio_digitalinout_never_reset(&power_pin); + // Allow power rail to settle before reset/init. + mp_hal_delay_ms(200); + + // 1. Allocate and construct QSPI bus + qspibus_qspibus_obj_t *bus = &allocate_display_bus_or_raise()->qspi_bus; + bus->base.type = &qspibus_qspibus_type; + + common_hal_qspibus_qspibus_construct(bus, + CIRCUITPY_LCD_CLK, // clock + CIRCUITPY_LCD_D0, // data0 + CIRCUITPY_LCD_D1, // data1 + CIRCUITPY_LCD_D2, // data2 + CIRCUITPY_LCD_D3, // data3 + CIRCUITPY_LCD_CS, // cs + NULL, // dcx (not used, QSPI uses encoded commands) + CIRCUITPY_LCD_RESET, // reset + 40000000); // 40 MHz + + // 2. Allocate and construct BusDisplay with RM690B0 init sequence. + // Physical panel: 450 cols × 600 rows. + // MADCTL MV=1 swaps row/col → logical 600×450 landscape. + busdisplay_busdisplay_obj_t *display = &allocate_display_or_raise()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct(display, + bus, + 600, // width (logical, after MV=1 swap) + 450, // height (logical, after MV=1 swap) + 0, // colstart + 16, // rowstart (physical row offset) + 0, // rotation + 16, // color_depth (RGB565) + false, // grayscale + false, // pixels_in_byte_share_row + 1, // bytes_per_cell + false, // reverse_pixels_in_byte + false, // reverse_bytes_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set_column_command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set_row_command + MIPI_COMMAND_WRITE_MEMORY_START, // write_ram_command + display_init_sequence, + sizeof(display_init_sequence), + NULL, // backlight_pin (AMOLED — no backlight GPIO) + 0x51, // brightness_command + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000); // backlight_pwm_frequency +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h new file mode 100644 index 0000000000000..9ac461ee61dde --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.h @@ -0,0 +1,34 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Waveshare ESP32-S3-Touch-AMOLED-2.41" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +// I2C bus - Disabled on boot to avoid conflicts. User must manually initialize I2C. +#define CIRCUITPY_BOARD_I2C (0) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO48, .sda = &pin_GPIO47}} + +// Display refresh buffer: 8192 bytes = 2048 uint32_t words on stack. +// ESP32-S3 main task stack is 24KB; verified safe with this board. +#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (8192) + +// AMOLED Display (displayio + qspibus path) - initialized in board_init() +#define CIRCUITPY_BOARD_DISPLAY (1) +#define CIRCUITPY_LCD_CS (&pin_GPIO9) +#define CIRCUITPY_LCD_CLK (&pin_GPIO10) +#define CIRCUITPY_LCD_D0 (&pin_GPIO11) +#define CIRCUITPY_LCD_D1 (&pin_GPIO12) +#define CIRCUITPY_LCD_D2 (&pin_GPIO13) +#define CIRCUITPY_LCD_D3 (&pin_GPIO14) +#define CIRCUITPY_LCD_RESET (&pin_GPIO21) +#define CIRCUITPY_LCD_POWER (&pin_GPIO16) + +// No default SPI bus — SD card uses SDIO, display uses QSPI. +#define CIRCUITPY_BOARD_SPI (0) + +// Default UART bus +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk new file mode 100644 index 0000000000000..f901246804cdf --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/mpconfigboard.mk @@ -0,0 +1,35 @@ +# This file is part of the CircuitPython project: https://circuitpython.org +# SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +# SPDX-License-Identifier: MIT + +USB_VID = 0x303A +USB_PID = 0x8278 +USB_MANUFACTURER = "Waveshare" +USB_PRODUCT = "ESP32-S3-Touch-AMOLED-2.41" + +IDF_TARGET = esp32s3 + +# Flash configuration - 16MB QSPI Flash +CIRCUITPY_ESP_FLASH_SIZE = 16MB +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m + +# PSRAM configuration - 8MB Octal PSRAM +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +OPTIMIZATION_FLAGS = -Os + +# QSPI bus for RM690B0 AMOLED display +CIRCUITPY_QSPIBUS = 1 +CIRCUITPY_PARALLELDISPLAYBUS = 0 + +# No camera on this board +CIRCUITPY_ESPCAMERA = 0 + +# Capacitive touch not available; board uses I2C touch controller +CIRCUITPY_TOUCHIO = 0 + +# SD card via SDMMC interface +CIRCUITPY_SDIOIO = 1 diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c new file mode 100644 index 0000000000000..8804cace06dcf --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/pins.c @@ -0,0 +1,109 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/mphal.h" +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // ================================================================= + // ONBOARD PERIPHERALS - Functional Names + // ================================================================= + + // Boot/Control/Battery/Display Power + // NOTE: GPIO16 is shared between battery control circuitry and LCD power + // (see CIRCUITPY_QSPIBUS_PANEL_POWER_PIN in mpconfigboard.h). + { MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_KEY_BAT), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_BAT_CONTROL), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_LCD_POWER), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_BAT_ADC), MP_ROM_PTR(&pin_GPIO17) }, + + // I2C Bus (shared by Touch, RTC, IMU, IO Expander) + // NOTE: board.I2C auto-initialization is disabled (CIRCUITPY_BOARD_I2C=0) + // to avoid boot conflicts. Users must manually create I2C bus: + // i2c = busio.I2C(board.SCL, board.SDA) + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO48) }, + + // Touch Panel (FT6336U on I2C) + // NOTE: TP_INT is routed through the TCA9554 IO expander (EXIO2), + // not a direct ESP32-S3 GPIO — no TOUCH_INT alias is possible here. + { MP_ROM_QSTR(MP_QSTR_TP_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_TP_SCL), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_TP_RESET), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_SCL), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_RST), MP_ROM_PTR(&pin_GPIO3) }, + + // RTC (PCF85063 on I2C) + { MP_ROM_QSTR(MP_QSTR_RTC_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_RTC_SCL), MP_ROM_PTR(&pin_GPIO48) }, + + // IMU (QMI8658 on I2C) + { MP_ROM_QSTR(MP_QSTR_IMU_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_IMU_SCL), MP_ROM_PTR(&pin_GPIO48) }, + + // I/O Expander (TCA9554 on I2C) + { MP_ROM_QSTR(MP_QSTR_EXIO_SDA), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_EXIO_SCL), MP_ROM_PTR(&pin_GPIO48) }, + + // USB + { MP_ROM_QSTR(MP_QSTR_USB_D_N), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_USB_D_P), MP_ROM_PTR(&pin_GPIO20) }, + + // UART + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + + // QSPI Display (RM690B0) - canonical generic LCD aliases. + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CLK), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D0), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D1), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D2), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D3), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_LCD_RESET), MP_ROM_PTR(&pin_GPIO21) }, + + // Display Aliases + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_SCK), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_D0), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_D1), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_D2), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_D3), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_RST), MP_ROM_PTR(&pin_GPIO21) }, + + // SD Card (SDIO / SDMMC) + { MP_ROM_QSTR(MP_QSTR_SDIO_CLK), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_SDIO_CMD), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_SDIO_D0), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_SDIO_D3), MP_ROM_PTR(&pin_GPIO2) }, + + // ================================================================= + // GENERAL PURPOSE I/O (IOxx - Espressif Convention) + // ================================================================= + // Only pins NOT dedicated to onboard peripherals are exposed here. + // Use functional names above for dedicated pins (e.g., SDA, SD_CS). + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, // BOOT button (available when not holding BOOT) + { MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, // TP_RESET (available if touch not used) + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, // Available + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, // Available + + // Board display (initialized in board_init) + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig new file mode 100644 index 0000000000000..fa60cc4c2378e --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_amoled_241/sdkconfig @@ -0,0 +1,3 @@ +# +# Configuration file for the Waveshare ESP32-S3 Touch AMOLED 2.41 +# diff --git a/ports/espressif/boards/waveshare_esp32_s3_geek/board.c b/ports/espressif/boards/waveshare_esp32_s3_geek/board.c index 414188fb46a52..0ce447532642c 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_geek/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_geek/board.c @@ -38,9 +38,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // TFT_DC - &pin_GPIO10, // TFT_CS - &pin_GPIO9, // TFT_RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // TFT_DC + MP_OBJ_FROM_PTR(&pin_GPIO10), // TFT_CS + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_RST 50000000, // Baudrate 0, // Polarity 0 // Phase diff --git a/ports/espressif/boards/waveshare_esp32_s3_lcd_1_28/board.c b/ports/espressif/boards/waveshare_esp32_s3_lcd_1_28/board.c index 36990c8556691..ee47ccb0bc96e 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_lcd_1_28/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_lcd_1_28/board.c @@ -105,9 +105,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // DC - &pin_GPIO9, // CS - &pin_GPIO12, // RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // RST 80000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/waveshare_esp32_s3_lcd_1_47/board.c b/ports/espressif/boards/waveshare_esp32_s3_lcd_1_47/board.c index f3dd38522f74f..4ecf87948ad04 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_lcd_1_47/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_lcd_1_47/board.c @@ -48,9 +48,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO41, // DC - &pin_GPIO42, // CS - &pin_GPIO39, // RST + MP_OBJ_FROM_PTR(&pin_GPIO41), // DC + MP_OBJ_FROM_PTR(&pin_GPIO42), // CS + MP_OBJ_FROM_PTR(&pin_GPIO39), // RST 80000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c new file mode 100644 index 0000000000000..1bdd74e77ee68 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/board.c @@ -0,0 +1,145 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 natheihei +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + +#define DELAY 0x80 + +// Driver is JD9853 +// 172 X 320 Pixels RGB 18-bit +// Init sequence converted from Arduino example + +uint8_t display_init_sequence[] = { + // Command: 0x11 (SLPOUT: Sleep Out) + // Description: Exits sleep mode. A 120ms delay is added for the power supply and clock circuits to stabilize. + 0x11, 0 | DELAY, 120, + + 0xDF, 2, 0x98, 0x53, + 0xB2, 1, 0x23, + + 0xB7, 4, 0x00, 0x47, 0x00, 0x6F, + 0xBB, 6, 0x1C, 0x1A, 0x55, 0x73, 0x63, 0xF0, + 0xC0, 2, 0x44, 0xA4, + 0xC1, 1, 0x16, + 0xC3, 8, 0x7D, 0x07, 0x14, 0x06, 0xCF, 0x71, 0x72, 0x77, + 0xC4, 12, 0x00, 0x00, 0xA0, 0x79, 0x0B, 0x0A, 0x16, 0x79, 0x0B, 0x0A, 0x16, 0x82, + + 0xC8, 32, 0x3F, 0x32, 0x29, 0x29, 0x27, 0x2B, 0x27, 0x28, 0x28, 0x26, 0x25, 0x17, 0x12, 0x0D, 0x04, 0x00, + 0x3F, 0x32, 0x29, 0x29, 0x27, 0x2B, 0x27, 0x28, 0x28, 0x26, 0x25, 0x17, 0x12, 0x0D, 0x04, 0x00, + + 0xD0, 5, 0x04, 0x06, 0x6B, 0x0F, 0x00, + 0xD7, 2, 0x00, 0x30, + 0xE6, 1, 0x14, + 0xDE, 1, 0x01, + + 0xB7, 5, 0x03, 0x13, 0xEF, 0x35, 0x35, + 0xC1, 3, 0x14, 0x15, 0xC0, + 0xC2, 2, 0x06, 0x3A, + 0xC4, 2, 0x72, 0x12, + 0xBE, 1, 0x00, + 0xDE, 1, 0x02, + + 0xE5, 3, 0x00, 0x02, 0x00, + 0xE5, 3, 0x01, 0x02, 0x00, + + 0xDE, 1, 0x00, + + // Command: 0x35 (TEON: Tearing Effect Line ON) + // Description: Turns on the Tearing Effect output signal. + 0x35, 1, 0x00, + + // Command: 0x3A (COLMOD: Pixel Format Set) + // Description: Sets the pixel format for the MCU interface. + 0x3A, 1, 0x05, + + // Command: 0x2A (CASET: Column Address Set) + // Description: Defines the accessible column range in frame memory. + 0x2A, 4, 0x00, 0x22, 0x00, 0xCD, + + // Command: 0x2B (PASET: Page Address Set) + // Description: Defines the accessible page (row) range. + 0x2B, 4, 0x00, 0x00, 0x01, 0x3F, + + 0xDE, 1, 0x02, + 0xE5, 3, 0x00, 0x02, 0x00, + 0xDE, 1, 0x00, + + // Command: 0x36 (MADCTL: Memory Access Control) + // Description: Sets the read/write scanning direction of the frame memory. + 0x36, 1, 0x00, + + // Command: 0x21 (INVON: Display Inversion ON) + // 0x21, 0 | DELAY, 10, + + // Command: 0x29 (DISPON: Display ON) + // Description: Turns the display on by enabling output from the frame memory. + 0x29, 0, +}; + +static void display_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO45), // DC + MP_OBJ_FROM_PTR(&pin_GPIO21), // CS + MP_OBJ_FROM_PTR(&pin_GPIO40), // RST + 80000000, // baudrate + 0, // polarity + 0 // phase + ); + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 172, // width (after rotation) + 320, // height (after rotation) + 34, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO46, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} + +void board_init(void) { + // Display + display_init(); +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.h new file mode 100644 index 0000000000000..0dc2ad4a6616b --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 natheihei +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Waveshare ESP32-S3 Touch LCD 1.47" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define CIRCUITPY_BOARD_UART (1) +#define CIRCUITPY_BOARD_UART_PIN {{.rx = &pin_GPIO44, .tx = &pin_GPIO43}} + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO41, .sda = &pin_GPIO42}} /* for Touchscreen */ + +#define CIRCUITPY_BOARD_SPI (2) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO38, .mosi = &pin_GPIO39}, /* for LCD display */ \ + {.clock = &pin_GPIO16, .mosi = &pin_GPIO15, .miso = &pin_GPIO17} /* for SD Card */} diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.mk new file mode 100644 index 0000000000000..b337b21149537 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x303A +USB_PID = 0x8325 +USB_PRODUCT = "ESP32-S3-Touch-LCD-1.47" +USB_MANUFACTURER = "Waveshare Electronics" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/pins.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/pins.c new file mode 100644 index 0000000000000..32c7d0dd21d7e --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/pins.c @@ -0,0 +1,58 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 natheihei +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +CIRCUITPY_BOARD_BUS_SINGLETON(touch_i2c, i2c, 0) +CIRCUITPY_BOARD_BUS_SINGLETON(sd_spi, spi, 1) + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // UART + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + + // GPIO + { MP_ROM_QSTR(MP_QSTR_GPIO1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GPIO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GPIO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GPIO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GPIO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GPIO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GPIO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GPIO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GPIO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GPIO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GPIO11), MP_ROM_PTR(&pin_GPIO11) }, + + // I2C (occupied by Touch I2C) + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + + // SD Card + { MP_ROM_QSTR(MP_QSTR_SD_CMD), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_SD_CLK), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_SD_D0), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SD_D1), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SD_D2), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_SD_D3), MP_ROM_PTR(&pin_GPIO14) }, + // CLK, CMD, D0 is also included in the SPI object as its MISO pin, MOSI pin, and SCK pin respectively + { MP_ROM_QSTR(MP_QSTR_SD_SPI), MP_ROM_PTR(&board_sd_spi_obj) }, + + // AXS5106L Touch + { MP_ROM_QSTR(MP_QSTR_TOUCH_I2C), MP_ROM_PTR(&board_touch_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_RST), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_TOUCH_IRQ), MP_ROM_PTR(&pin_GPIO48) }, + + // JD9853 LCD Display + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, + +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_1_47/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2/board.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2/board.c index e7a6b86440ee5..71e2d745623b9 100644 --- a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2/board.c +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2/board.c @@ -34,9 +34,9 @@ void board_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO42, // DC - &pin_GPIO45, // CS - &pin_GPIO0, // RST + MP_OBJ_FROM_PTR(&pin_GPIO42), // DC + MP_OBJ_FROM_PTR(&pin_GPIO45), // CS + MP_OBJ_FROM_PTR(&pin_GPIO0), // RST // 24000000, 40000000, // baudrate 0, // polarity diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c new file mode 100755 index 0000000000000..49177f48fe7d9 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/board.c @@ -0,0 +1,77 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + +#define DELAY 0x80 + +// display init sequence according to LilyGO example app +uint8_t display_init_sequence[] = { + 0x01, DELAY, 0x96, // _SWRESET and Delay 150ms + 0x11, DELAY, 0xFF, // _SLPOUT and Delay 500ms + 0x3A, DELAY | 1, 0x55, 0x0A, // _COLMOD and Delay 10ms + 0x21, DELAY, 0x0A, // _INVON Hack and Delay 10ms + 0x13, DELAY, 0x0A, // _NORON and Delay 10ms + 0x36, 0x01, 0x60, // _MADCTL + 0x29, DELAY, 0xFF, // _DISPON and Delay 500ms +}; + +void board_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO41), // DC + MP_OBJ_FROM_PTR(&pin_GPIO42), // CS + MP_OBJ_FROM_PTR(&pin_GPIO39), // RST + 40000000, // baudrate + 0, // polarity + 0 // phase + ); + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 320, // width (after rotation) + 240, // height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO5, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.h b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.h new file mode 100755 index 0000000000000..3e0db8bf494d5 --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.h @@ -0,0 +1,19 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Neradoc +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Waveshare ESP32-S3 Touch LCD 2.8" +#define MICROPY_HW_MCU_NAME "ESP32S3" + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO3, .sda = &pin_GPIO1}} + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO40, .mosi = &pin_GPIO45, .miso = &pin_GPIO46}} + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk new file mode 100755 index 0000000000000..421a58976002a --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/mpconfigboard.mk @@ -0,0 +1,14 @@ +USB_VID = 0x303A +USB_PID = 0x825F +USB_MANUFACTURER = "Waveshare Electronics" +USB_PRODUCT = "ESP32-S3 Touch LCD 2.8" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = opi +CIRCUITPY_ESP_PSRAM_FREQ = 80m diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c new file mode 100755 index 0000000000000..0bfb3e940b4de --- /dev/null +++ b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/pins.c @@ -0,0 +1,86 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // LCD (SPI0) + { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MISO), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_LCD_DC), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_LCD_RST), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_GPIO5) }, // PWM-capable + + // microSD (SPI1) + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO21) }, + + // Touch panel (I2C0) + { MP_ROM_QSTR(MP_QSTR_TP_SCL), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_TP_SDA), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_TP_RST), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_TP_INT), MP_ROM_PTR(&pin_GPIO4) }, + + // IMU (I2C1) + { MP_ROM_QSTR(MP_QSTR_IMU_SCL), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IMU_SDA), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_IMU_INT2), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_IMU_INT1), MP_ROM_PTR(&pin_GPIO13) }, + + // I2S Audio + { MP_ROM_QSTR(MP_QSTR_I2S_BCK), MP_ROM_PTR(&pin_GPIO48) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DIN), MP_ROM_PTR(&pin_GPIO47) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCK), MP_ROM_PTR(&pin_GPIO38) }, + + // Battery management + { MP_ROM_QSTR(MP_QSTR_BAT_CONTROL), MP_ROM_PTR(&pin_GPIO7) }, // control pin + { MP_ROM_QSTR(MP_QSTR_BAT_PWR), MP_ROM_PTR(&pin_GPIO6) }, // Board name + { MP_ROM_QSTR(MP_QSTR_KEY_BAT), MP_ROM_PTR(&pin_GPIO6) }, // Schematics name + { MP_ROM_QSTR(MP_QSTR_BAT_ADC), MP_ROM_PTR(&pin_GPIO8) }, // VBAT sense (ADC) + + // UART header + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + + // I2C header + { MP_ROM_QSTR(MP_QSTR_I2C_SCL), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_I2C_SDA), MP_ROM_PTR(&pin_GPIO11) }, + + // Boot/User button + { MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON0), MP_ROM_PTR(&pin_GPIO0) }, + + // Primary bus pins + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO40) }, // Primary SPI (LCD) + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO46) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO3) }, // Primary I2C (TP) + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO1) }, + + // Objects + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, + + // User accessible + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_IO19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_IO20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/sdkconfig b/ports/espressif/boards/waveshare_esp32_s3_touch_lcd_2_8/sdkconfig new file mode 100755 index 0000000000000..e69de29bb2d1d diff --git a/ports/espressif/boards/xteink_x4/board.c b/ports/espressif/boards/xteink_x4/board.c new file mode 100644 index 0000000000000..b5b193c65fec6 --- /dev/null +++ b/ports/espressif/boards/xteink_x4/board.c @@ -0,0 +1,130 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// SPDX-FileCopyrightText: Copyright (c) 2021 skieast/Bruce Segal +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/board.h" + +#define DELAY 0x80 + +// SSD1677 controller driving a GDEQ0426T82 4.26" 800x480 E-Ink display. + +const uint8_t ssd1677_display_start_sequence[] = { + // Software Reset + 0x12, DELAY, 0x00, 0x14, + + // Temperature Sensor Control (use internal sensor) + 0x18, 0x00, 0x01, 0x80, + + // Booster Soft Start + 0x0C, 0x00, 0x05, 0xAE, 0xC7, 0xC3, 0xC0, 0x40, + + // Driver Output Control: 480 gates, GD=0, SM=1, TB=0 = 0x02 + 0x01, 0x00, 0x03, 0xDF, 0x01, 0x02, + + // Data Entry Mode: X increment, Y decrement = 0x01 + 0x11, 0x00, 0x01, 0x01, + + // Border Waveform Control + 0x3C, 0x00, 0x01, 0x01, + + // Set RAM X Address Start/End: 0 to 799 + 0x44, 0x00, 0x04, 0x00, 0x00, 0x1F, 0x03, + + // Set RAM Y Address Start/End: 479 down to 0 + 0x45, 0x00, 0x04, 0xDF, 0x01, 0x00, 0x00, + + // Set RAM X Counter to 0 + 0x4E, 0x00, 0x02, 0x00, 0x00, + + // Set RAM Y Counter to 479 + 0x4F, 0x00, 0x02, 0xDF, 0x01, + + // Auto Write BW RAM (clear to white) + 0x46, DELAY, 0x01, 0xF7, 0xFF, + + // Display Update Control 1: bypass RED + 0x21, 0x00, 0x02, 0x40, 0x00, + + // Display Update Control 2: full refresh with OTP LUT + 0x22, 0x00, 0x01, 0xF7, +}; + +const uint8_t ssd1677_display_stop_sequence[] = { + 0x22, 0x00, 0x01, 0x83, + 0x20, 0x00, 0x00, + 0x10, 0x00, 0x01, 0x01, +}; + +const uint8_t ssd1677_display_refresh_sequence[] = { + 0x20, 0x00, 0x00, +}; + +void board_init(void) { + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + busio_spi_obj_t *spi = &bus->inline_bus; + common_hal_busio_spi_construct(spi, &pin_GPIO8, &pin_GPIO10, NULL, false); + common_hal_busio_spi_never_reset(spi); + + bus->base.type = &fourwire_fourwire_type; + common_hal_fourwire_fourwire_construct(bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO4), + MP_OBJ_FROM_PTR(&pin_GPIO21), + MP_OBJ_FROM_PTR(&pin_GPIO5), + 40000000, + 0, + 0); + + epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; + display->base.type = &epaperdisplay_epaperdisplay_type; + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = ssd1677_display_start_sequence; + args.start_sequence_len = sizeof(ssd1677_display_start_sequence); + args.stop_sequence = ssd1677_display_stop_sequence; + args.stop_sequence_len = sizeof(ssd1677_display_stop_sequence); + args.width = 800; + args.height = 480; + args.ram_width = 800; + args.ram_height = 480; + args.rotation = 0; + args.write_black_ram_command = 0x24; + args.black_bits_inverted = false; + args.refresh_sequence = ssd1677_display_refresh_sequence; + args.refresh_sequence_len = sizeof(ssd1677_display_refresh_sequence); + args.refresh_time = 1.6; + args.busy_pin = &pin_GPIO6; + args.busy_state = true; + args.seconds_per_frame = 5.0; + args.grayscale = false; + args.two_byte_sequence_length = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); +} + +bool espressif_board_reset_pin_number(gpio_num_t pin_number) { + return false; +} + +void board_deinit(void) { + epaperdisplay_epaperdisplay_obj_t *display = &displays[0].epaper_display; + if (display->base.type == &epaperdisplay_epaperdisplay_type) { + while (common_hal_epaperdisplay_epaperdisplay_get_busy(display)) { + RUN_BACKGROUND_TASKS; + } + } + common_hal_displayio_release_displays(); +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/xteink_x4/mpconfigboard.h b/ports/espressif/boards/xteink_x4/mpconfigboard.h new file mode 100644 index 0000000000000..5d4bb37222826 --- /dev/null +++ b/ports/espressif/boards/xteink_x4/mpconfigboard.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// SPDX-FileCopyrightText: Copyright (c) 2021 skieast/Bruce Segal +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Board setup +#define MICROPY_HW_BOARD_NAME "Xteink X4" +#define MICROPY_HW_MCU_NAME "ESP32-C3" + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO8, .mosi = &pin_GPIO10, .miso = &pin_GPIO7}} + +// For entering safe mode +#define CIRCUITPY_BOOT_BUTTON (&pin_GPIO3) diff --git a/ports/espressif/boards/xteink_x4/mpconfigboard.mk b/ports/espressif/boards/xteink_x4/mpconfigboard.mk new file mode 100644 index 0000000000000..8668aad18180c --- /dev/null +++ b/ports/espressif/boards/xteink_x4/mpconfigboard.mk @@ -0,0 +1,29 @@ +CIRCUITPY_CREATOR_ID = 0x0EEE0000 +CIRCUITPY_CREATION_ID = 0x00C30001 + +IDF_TARGET = esp32c3 + +CIRCUITPY_ESP_FLASH_MODE = dio +CIRCUITPY_ESP_FLASH_FREQ = 40m +CIRCUITPY_ESP_FLASH_SIZE = 16MB + +CIRCUITPY_ESP_USB_SERIAL_JTAG = 1 + +CIRCUITPY_PARALLELDISPLAYBUS = 0 +CIRCUITPY_RGBMATRIX = 0 +CIRCUITPY_AUDIOBUSIO = 0 +CIRCUITPY_AUDIOCORE = 0 +CIRCUITPY_AUDIOMIXER = 0 +CIRCUITPY_AUDIOMP3 = 0 +CIRCUITPY_CAMERA = 0 +CIRCUITPY_CANIO = 0 +CIRCUITPY_DOTCLOCKFRAMEBUFFER = 0 +CIRCUITPY_KEYPAD = 0 +CIRCUITPY_ROTARYIO = 0 +CIRCUITPY_USB_HID = 0 +CIRCUITPY_USB_MIDI = 0 +CIRCUITPY_ULAB = 0 +CIRCUITPY_PULSEIO = 0 +CIRCUITPY_PWMIO = 0 +CIRCUITPY_RAINBOWIO = 0 +CIRCUITPY_SYNTHIO = 0 diff --git a/ports/espressif/boards/xteink_x4/pins.c b/ports/espressif/boards/xteink_x4/pins.c new file mode 100644 index 0000000000000..6b8232d4cb3a6 --- /dev/null +++ b/ports/espressif/boards/xteink_x4/pins.c @@ -0,0 +1,52 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// SPDX-FileCopyrightText: Copyright (c) 2021 skieast/Bruce Segal +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_BOOT0), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO0) }, + + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_ADC_1), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_ADC_2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) }, + + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_EPD_MOSI), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_EPD_SCK), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_EPD_BUSY), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_EPD_RESET), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_EPD_DC), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_EPD_CS), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].epaper_display)}, + +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/xteink_x4/sdkconfig b/ports/espressif/boards/xteink_x4/sdkconfig new file mode 100644 index 0000000000000..e962866216039 --- /dev/null +++ b/ports/espressif/boards/xteink_x4/sdkconfig @@ -0,0 +1,14 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/boards/yoto_mini_2024/board.c b/ports/espressif/boards/yoto_mini_2024/board.c new file mode 100644 index 0000000000000..fb6a96a957d06 --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/board.c @@ -0,0 +1,203 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "board.h" + +#include "supervisor/board.h" + +#include "extmod/vfs_fat.h" + +#include "py/mpstate.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/i2cioexpander/IOExpander.h" +#include "shared-bindings/i2cioexpander/IOPin.h" +#include "shared-bindings/sdioio/SDCard.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" + +#include "supervisor/filesystem.h" + +static sdioio_sdcard_obj_t sdmmc; +static mp_vfs_mount_t _sdcard_vfs; +static fs_user_mount_t _sdcard_usermount; +static i2cioexpander_ioexpander_obj_t ioexpander; + +#define DELAY 0x80 + +// This is a GC9306. +uint8_t display_init_sequence[] = { + 0xfe, 0, + 0xef, 0, + + // // sw reset + // 0x01, 0 | DELAY, 150, + // normal display mode on + // 0x13, 0, + // display and color format settings + 0x36, 1, 0x48, // Memory access control. mini does 0x48, not 0, 2, 3, 4 or 6 + 0x3A, 1 | DELAY, 0x55, 10, // COLMOD. mini does 0x06 + 0xa4, 2, 0x44, 0x44, // power control 7 + 0xa5, 2, 0x42, 0x42, + 0xaa, 2, 0x88, 0x88, + 0xae, 1, 0x2b, + 0xe8, 2, 0x22, 0x0b, // frame rate + 0xe3, 2, 0x01, 0x10, + 0xff, 1, 0x61, + 0xac, 1, 0x00, + 0xad, 1, 0x33, + 0xaf, 1, 0x77, + 0xa6, 2, 0x1c, 0x1c, // power control 2 + 0xa7, 2, 0x1c, 0x1c, // power control 3 + 0xa8, 2, 0x10, 0x10, // power control 4 + 0xa9, 2, 0x0d, 0x0d, // power control 5 + 0xf0, 6, 0x02, 0x01, 0x00, 0x00, 0x00, 0x05, // Gamma settings + 0xf1, 6, 0x01, 0x02, 0x00, 0x06, 0x10, 0x0e, + 0xf2, 6, 0x03, 0x11, 0x28, 0x02, 0x00, 0x48, + 0xf3, 6, 0x0c, 0x11, 0x30, 0x00, 0x00, 0x46, + 0xf4, 6, 0x05, 0x1f, 0x1f, 0x36, 0x30, 0x0f, + 0xf5, 6, 0x04, 0x1d, 0x1a, 0x38, 0x3f, 0x0f, // Last gamma setting + 0x35, 1, 0x00, + 0x44, 2, 0x00, 0x0a, // set tear scan line + 0x21, 0, // display inversion on + // sleep out + 0x11, 0 | DELAY, 255, + + // display on + 0x29, 0 | DELAY, 255, +}; + +void board_init(void) { + // Wait for everything to start + mp_hal_delay_ms(300); + + // Initialize the board's peripherals here. + busio_i2c_obj_t *i2c = MP_OBJ_TO_PTR(common_hal_board_create_i2c(0)); + + // Initialize the IOExpander + ioexpander.base.type = &i2cioexpander_ioexpander_type; + common_hal_i2cioexpander_ioexpander_construct( + &ioexpander, + MP_OBJ_FROM_PTR(i2c), + 0x20, // I2C address + 16, // Number of pins + 2, // Output register + 0, // Input register + 6); // Direction register + + board_set(MP_QSTR_IOEXPANDER, MP_OBJ_FROM_PTR(&ioexpander)); + board_set(MP_QSTR_PLUG_STATUS, ioexpander.pins->items[8 + 5]); + board_set(MP_QSTR_CHARGE_STATUS, ioexpander.pins->items[8 + 7]); + board_set(MP_QSTR_POWER_BUTTON, ioexpander.pins->items[8 + 3]); + board_set(MP_QSTR_ENC1_BUTTON, ioexpander.pins->items[5]); + board_set(MP_QSTR_ENC2_BUTTON, ioexpander.pins->items[4]); + board_set(MP_QSTR_HEADPHONE_DETECT, ioexpander.pins->items[8 + 1]); + board_set(MP_QSTR_PACTRL, ioexpander.pins->items[6]); + board_set(MP_QSTR_DISPLAY_CS, ioexpander.pins->items[0]); + board_set(MP_QSTR_DISPLAY_DC, ioexpander.pins->items[1]); + board_set(MP_QSTR_DISPLAY_RESET, ioexpander.pins->items[2]); + + board_set(MP_QSTR_LEVEL_CONVERTER, ioexpander.pins->items[3]); + board_set(MP_QSTR_LEVEL_POWER_ENABLE, ioexpander.pins->items[8 + 4]); + board_set(MP_QSTR_LEVEL_VINHOLD, ioexpander.pins->items[8 + 6]); + + board_set(MP_QSTR_TILT, ioexpander.pins->items[8 + 2]); + + // Only on some variants + board_set(MP_QSTR_RTC_INT, ioexpander.pins->items[7]); + + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[2]), true, DRIVE_MODE_PUSH_PULL); + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[3]), true, DRIVE_MODE_PUSH_PULL); + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[6]), true, DRIVE_MODE_PUSH_PULL); + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[14]), true, DRIVE_MODE_PUSH_PULL); + + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + ioexpander.pins->items[1], // DC + ioexpander.pins->items[0], // CS + ioexpander.pins->items[2], // RST + 25000000, // baudrate + 0, // polarity + 0 // phase + ); + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 240, // width (after rotation) + 240, // height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO26, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); + + digitalinout_result_t sd_enable_res = common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander.pins->items[12]), false, DRIVE_MODE_PUSH_PULL); + if (sd_enable_res != DIGITALINOUT_OK) { + mp_printf(&mp_plat_print, "Failed to initialize IOExpander. Skipping SD card\n"); + return; + } + + sdmmc.base.type = &sdioio_SDCard_type; + const mcu_pin_obj_t *data_pins[4] = {MP_ROM_PTR(&pin_GPIO2), MP_ROM_PTR(&pin_GPIO4), MP_ROM_PTR(&pin_GPIO12), MP_ROM_PTR(&pin_GPIO13)}; + common_hal_sdioio_sdcard_construct(&sdmmc, MP_ROM_PTR(&pin_GPIO14), MP_ROM_PTR(&pin_GPIO15), 4, data_pins, 25 * 1000000); + + fs_user_mount_t *vfs = &_sdcard_usermount; + vfs->base.type = &mp_fat_vfs_type; + vfs->fatfs.drv = vfs; + + // Initialise underlying block device + vfs->blockdev.block_size = FF_MIN_SS; // default, will be populated by call to MP_BLOCKDEV_IOCTL_BLOCK_SIZE + mp_vfs_blockdev_init(&vfs->blockdev, &sdmmc); + + // mount the block device so the VFS methods can be used + FRESULT res = f_mount(&vfs->fatfs); + if (res != FR_OK) { + common_hal_sdioio_sdcard_deinit(&sdmmc); + return; + } + common_hal_sdioio_sdcard_never_reset(&sdmmc); + + filesystem_set_concurrent_write_protection(vfs, true); + filesystem_set_writable_by_usb(vfs, false); + + mp_vfs_mount_t *sdcard_vfs = &_sdcard_vfs; + sdcard_vfs->str = "/sd"; + sdcard_vfs->len = 3; + sdcard_vfs->obj = MP_OBJ_FROM_PTR(&_sdcard_usermount); + sdcard_vfs->next = MP_STATE_VM(vfs_mount_table); + MP_STATE_VM(vfs_mount_table) = sdcard_vfs; +} diff --git a/ports/espressif/boards/yoto_mini_2024/board.h b/ports/espressif/boards/yoto_mini_2024/board.h new file mode 100644 index 0000000000000..1e40b62b047db --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/board.h @@ -0,0 +1,3 @@ +#include "py/obj.h" + +extern void board_set(qstr key, mp_obj_t value); diff --git a/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h new file mode 100644 index 0000000000000..4fcc2afaae870 --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.h @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2022 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Yoto Mini 2024" +#define MICROPY_HW_MCU_NAME "ESP32" + +#define CIRCUITPY_MUTABLE_BOARD (1) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO25, .sda = &pin_GPIO21}} +#define CIRCUITPY_BOARD_I2C_SPEED (400000) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO23, .mosi = &pin_GPIO22, .miso = NULL}} + +#define CIRCUITPY_BOARD_UART (1) +#define CIRCUITPY_BOARD_UART_PIN {{.tx = &pin_GPIO17, .rx = &pin_GPIO16}} + +// UART pins attached to the USB-serial converter chip +#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1) +#define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO3) diff --git a/ports/espressif/boards/yoto_mini_2024/mpconfigboard.mk b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.mk new file mode 100644 index 0000000000000..356e7015a04fa --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/mpconfigboard.mk @@ -0,0 +1,14 @@ +CIRCUITPY_CREATOR_ID = 0x40100000 +CIRCUITPY_CREATION_ID = 0x00320002 + +IDF_TARGET = esp32 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 8MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 40m + +CIRCUITPY_I2CIOEXPANDER = 1 diff --git a/ports/espressif/boards/yoto_mini_2024/pins.c b/ports/espressif/boards/yoto_mini_2024/pins.c new file mode 100644 index 0000000000000..d4ff3b4863fb5 --- /dev/null +++ b/ports/espressif/boards/yoto_mini_2024/pins.c @@ -0,0 +1,79 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT +// + +#include "board.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static mp_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_MUTABLE_BOARD_DICT_STANDARD_ITEMS + + // External pins are in silkscreen order, from top to bottom, left side, then right side + { MP_ROM_QSTR(MP_QSTR_ENC1A), MP_OBJ_FROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_ENC1B), MP_OBJ_FROM_PTR(&pin_GPIO35) }, + + { MP_ROM_QSTR(MP_QSTR_ENC2A), MP_OBJ_FROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_ENC2B), MP_OBJ_FROM_PTR(&pin_GPIO27) }, + + { MP_ROM_QSTR(MP_QSTR_SCL), MP_OBJ_FROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_OBJ_FROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY_SCK), MP_OBJ_FROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_MOSI), MP_OBJ_FROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_NFC_IN), MP_OBJ_FROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_OBJ_FROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_NFC_OUT), MP_OBJ_FROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_OBJ_FROM_PTR(&pin_GPIO33) }, + + { MP_ROM_QSTR(MP_QSTR_I2S_MCLK), MP_OBJ_FROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_OBJ_FROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_I2S_BCLK), MP_OBJ_FROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_OBJ_FROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCLK), MP_OBJ_FROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DOUT), MP_OBJ_FROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER_INT), MP_OBJ_FROM_PTR(&pin_GPIO34) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_OBJ_FROM_PTR(&displays[0].display)}, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_OBJ_FROM_PTR(&board_i2c_obj) }, + + // Filled in by board_init() + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_PLUG_STATUS), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_CHARGE_STATUS), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_POWER_BUTTON), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_ENC1_BUTTON), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_ENC2_BUTTON), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_HEADPHONE_DETECT), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_PACTRL), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_DC), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_RESET), mp_const_none }, + + { MP_ROM_QSTR(MP_QSTR_LEVEL_CONVERTER), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_LEVEL_POWER_ENABLE), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_LEVEL_VINHOLD), mp_const_none }, + + { MP_ROM_QSTR(MP_QSTR_TILT), mp_const_none }, + + // Only on some variants + { MP_ROM_QSTR(MP_QSTR_RTC_INT), mp_const_none }, + +}; +MP_DEFINE_MUTABLE_DICT(board_module_globals, board_module_globals_table); + +void board_set(qstr q, mp_obj_t value) { + mp_obj_t key = MP_OBJ_NEW_QSTR(q); + for (size_t i = 0; i < MP_ARRAY_SIZE(board_module_globals_table); i++) { + if (board_module_globals_table[i].key == key) { + board_module_globals_table[i].value = value; + return; + } + } +} diff --git a/ports/espressif/boards/yoto_mini_2024/sdkconfig b/ports/espressif/boards/yoto_mini_2024/sdkconfig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/espressif/boards/yoto_player_v3/board.c b/ports/espressif/boards/yoto_player_v3/board.c new file mode 100644 index 0000000000000..fd6ee40b9f32e --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/board.c @@ -0,0 +1,148 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "board.h" + +#include "supervisor/board.h" + +#include "extmod/vfs_fat.h" + +#include "py/mpstate.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/i2cioexpander/IOExpander.h" +#include "shared-bindings/i2cioexpander/IOPin.h" +#include "shared-bindings/sdioio/SDCard.h" + +#include "supervisor/filesystem.h" + +static sdioio_sdcard_obj_t sdmmc; +static mp_vfs_mount_t _sdcard_vfs; +static fs_user_mount_t _sdcard_usermount; +static i2cioexpander_ioexpander_obj_t ioexpander0; // First chip (p0/p1) +static i2cioexpander_ioexpander_obj_t ioexpander1; // Second chip (p2/p3) + +void board_init(void) { + // Wait for everything to start + mp_hal_delay_ms(300); + + // Initialize the board's peripherals here. + busio_i2c_obj_t *i2c = MP_OBJ_TO_PTR(common_hal_board_create_i2c(0)); + + // Initialize the IOExpanders + // V3/V3-E uses two PI4IOE5V6416 chips (16 pins each) + // First chip (0x20): p0 (pins 0-7), p1 (pins 8-15) + ioexpander0.base.type = &i2cioexpander_ioexpander_type; + common_hal_i2cioexpander_ioexpander_construct( + &ioexpander0, + MP_OBJ_FROM_PTR(i2c), + 0x20, // I2C address for first chip + 16, // Number of pins + 2, // Output register + 0, // Input register + 6); // Direction register + + // Second chip (0x21): p2 (pins 0-7), p3 (pins 8-15) + ioexpander1.base.type = &i2cioexpander_ioexpander_type; + common_hal_i2cioexpander_ioexpander_construct( + &ioexpander1, + MP_OBJ_FROM_PTR(i2c), + 0x21, // I2C address for second chip + 16, // Number of pins + 2, // Output register + 0, // Input register + 6); // Direction register + + board_set(MP_QSTR_IOEXPANDER0, MP_OBJ_FROM_PTR(&ioexpander0)); + board_set(MP_QSTR_IOEXPANDER1, MP_OBJ_FROM_PTR(&ioexpander1)); + + // Battery and charging (from first chip - p0/p1) + board_set(MP_QSTR_BATTERY_ALERT, ioexpander0.pins->items[6]); // IOX.0.6 + board_set(MP_QSTR_QI_STATUS, ioexpander0.pins->items[7]); // IOX.0.7 + board_set(MP_QSTR_QI_I2C_INT, ioexpander0.pins->items[0]); // IOX.0.0 + board_set(MP_QSTR_USB_STATUS, ioexpander0.pins->items[8 + 0]); // IOX.1.0 + board_set(MP_QSTR_CHARGE_STATUS, ioexpander0.pins->items[8 + 4]); // IOX.1.4 + + // Buttons (from first chip - p0/p1) + board_set(MP_QSTR_POWER_BUTTON, ioexpander0.pins->items[8 + 3]); // IOX.1.3 + board_set(MP_QSTR_ENC1_BUTTON, ioexpander0.pins->items[5]); // IOX.0.5 + board_set(MP_QSTR_ENC2_BUTTON, ioexpander0.pins->items[4]); // IOX.0.4 + + // Audio (from first chip - p0/p1 and second chip - p2/p3) + board_set(MP_QSTR_HEADPHONE_DETECT, ioexpander0.pins->items[8 + 1]); // IOX.1.1 + board_set(MP_QSTR_PACTRL, ioexpander1.pins->items[4]); // IOX.2.4 + + // Display - V3/V3-E uses ht16d35x with 4 CS lines (from second chip - p2) + board_set(MP_QSTR_DISPLAY_CS0, ioexpander1.pins->items[0]); // IOX.2.0 + board_set(MP_QSTR_DISPLAY_CS1, ioexpander1.pins->items[1]); // IOX.2.1 + board_set(MP_QSTR_DISPLAY_CS2, ioexpander1.pins->items[2]); // IOX.2.2 + board_set(MP_QSTR_DISPLAY_CS3, ioexpander1.pins->items[3]); // IOX.2.3 + + // Power control (from second chip - p2/p3) + board_set(MP_QSTR_LEVEL_CONVERTER, ioexpander1.pins->items[8 + 0]); // IOX.3.0 + board_set(MP_QSTR_LEVEL_POWER_ENABLE, ioexpander1.pins->items[5]); // IOX.2.5 + board_set(MP_QSTR_LEVEL_VINHOLD, ioexpander1.pins->items[8 + 1]); // IOX.3.1 + board_set(MP_QSTR_LEVEL_VOUTEN, ioexpander1.pins->items[8 + 3]); // IOX.3.3 + + // Sensors (from first chip - p0/p1) + board_set(MP_QSTR_TILT, ioexpander0.pins->items[8 + 2]); // IOX.1.2 + board_set(MP_QSTR_RTC_INT, ioexpander0.pins->items[1]); // IOX.0.1 + + // Qi charging control (V3-E, from second chip - p2/p3) + board_set(MP_QSTR_QI_CHARGE_ENABLE, ioexpander1.pins->items[6]); // IOX.2.6 + board_set(MP_QSTR_USB_CHARGE_ENABLE, ioexpander1.pins->items[7]); // IOX.2.7 + board_set(MP_QSTR_QI_ENABLE_5W, ioexpander1.pins->items[8 + 5]); // IOX.3.5 + + // Output pin 3 high. Not clear why. + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander0.pins->items[3]), true, DRIVE_MODE_PUSH_PULL); + + // Initialize output pins + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[0]), true, DRIVE_MODE_PUSH_PULL); // DISPLAY_CS0 (IOX.2.0) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[1]), true, DRIVE_MODE_PUSH_PULL); // DISPLAY_CS1 (IOX.2.1) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[2]), true, DRIVE_MODE_PUSH_PULL); // DISPLAY_CS2 (IOX.2.2) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[3]), true, DRIVE_MODE_PUSH_PULL); // DISPLAY_CS3 (IOX.2.3) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[4]), true, DRIVE_MODE_PUSH_PULL); // PACTRL (IOX.2.4) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[5]), false, DRIVE_MODE_PUSH_PULL); // LEVEL_POWER_ENABLE (IOX.2.5) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[6]), true, DRIVE_MODE_PUSH_PULL); // QI_CHARGE_ENABLE + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[7]), false, DRIVE_MODE_PUSH_PULL); // USB_CHARGE_ENABLE + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[8 + 0]), true, DRIVE_MODE_PUSH_PULL); // LEVEL_CONVERTER (IOX.3.0) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[8 + 1]), true, DRIVE_MODE_PUSH_PULL); // VINHOLD (IOX.3.1) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[8 + 3]), true, DRIVE_MODE_PUSH_PULL); // VOUTEN (IOX.3.3) + common_hal_i2cioexpander_iopin_switch_to_output(MP_OBJ_TO_PTR(ioexpander1.pins->items[8 + 5]), false, DRIVE_MODE_PUSH_PULL); // QI_ENABLE_5W (IOX.3.5) + + // Initialize SD card + // V3/V3-E uses single-line SD (sd1 mode), not 4-line (sd4 mode) like Mini + sdmmc.base.type = &sdioio_SDCard_type; + const mcu_pin_obj_t *data_pins[1] = {MP_ROM_PTR(&pin_GPIO2)}; + common_hal_sdioio_sdcard_construct(&sdmmc, MP_ROM_PTR(&pin_GPIO14), MP_ROM_PTR(&pin_GPIO15), 1, data_pins, 25 * 1000000); + + fs_user_mount_t *vfs = &_sdcard_usermount; + vfs->base.type = &mp_fat_vfs_type; + vfs->fatfs.drv = vfs; + + // Initialise underlying block device + vfs->blockdev.block_size = FF_MIN_SS; // default, will be populated by call to MP_BLOCKDEV_IOCTL_BLOCK_SIZE + mp_vfs_blockdev_init(&vfs->blockdev, &sdmmc); + + // mount the block device so the VFS methods can be used + FRESULT res = f_mount(&vfs->fatfs); + if (res != FR_OK) { + common_hal_sdioio_sdcard_deinit(&sdmmc); + return; + } + common_hal_sdioio_sdcard_never_reset(&sdmmc); + + filesystem_set_concurrent_write_protection(vfs, true); + filesystem_set_writable_by_usb(vfs, false); + + mp_vfs_mount_t *sdcard_vfs = &_sdcard_vfs; + sdcard_vfs->str = "/sd"; + sdcard_vfs->len = 3; + sdcard_vfs->obj = MP_OBJ_FROM_PTR(&_sdcard_usermount); + sdcard_vfs->next = MP_STATE_VM(vfs_mount_table); + MP_STATE_VM(vfs_mount_table) = sdcard_vfs; +} diff --git a/ports/espressif/boards/yoto_player_v3/board.h b/ports/espressif/boards/yoto_player_v3/board.h new file mode 100644 index 0000000000000..1e40b62b047db --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/board.h @@ -0,0 +1,3 @@ +#include "py/obj.h" + +extern void board_set(qstr key, mp_obj_t value); diff --git a/ports/espressif/boards/yoto_player_v3/mpconfigboard.h b/ports/espressif/boards/yoto_player_v3/mpconfigboard.h new file mode 100644 index 0000000000000..1660c4881f98a --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/mpconfigboard.h @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2022 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Yoto Player V3" +#define MICROPY_HW_MCU_NAME "ESP32" + +#define CIRCUITPY_MUTABLE_BOARD (1) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO25, .sda = &pin_GPIO21}} +#define CIRCUITPY_BOARD_I2C_SPEED (400000) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO23, .mosi = &pin_GPIO22, .miso = &pin_GPIO26}} + +#define CIRCUITPY_BOARD_UART (1) +#define CIRCUITPY_BOARD_UART_PIN {{.tx = &pin_GPIO17, .rx = &pin_GPIO16}} + +// UART pins attached to the USB-serial converter chip +#define CIRCUITPY_CONSOLE_UART_TX (&pin_GPIO1) +#define CIRCUITPY_CONSOLE_UART_RX (&pin_GPIO3) diff --git a/ports/espressif/boards/yoto_player_v3/mpconfigboard.mk b/ports/espressif/boards/yoto_player_v3/mpconfigboard.mk new file mode 100644 index 0000000000000..a1b940316c40a --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/mpconfigboard.mk @@ -0,0 +1,14 @@ +CIRCUITPY_CREATOR_ID = 0x40100000 +CIRCUITPY_CREATION_ID = 0x00320001 + +IDF_TARGET = esp32 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 8MB + +CIRCUITPY_ESP_PSRAM_SIZE = 8MB +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 40m + +CIRCUITPY_I2CIOEXPANDER = 1 diff --git a/ports/espressif/boards/yoto_player_v3/pins.c b/ports/espressif/boards/yoto_player_v3/pins.c new file mode 100644 index 0000000000000..0862a020fdfce --- /dev/null +++ b/ports/espressif/boards/yoto_player_v3/pins.c @@ -0,0 +1,104 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2020 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT +// + +#include "board.h" + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static mp_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_MUTABLE_BOARD_DICT_STANDARD_ITEMS + + // External pins are in silkscreen order, from top to bottom, left side, then right side + // Encoder 1: V3-E uses GPIO26/13, V3 uses GPIO35/13 + { MP_ROM_QSTR(MP_QSTR_ENC1A), MP_OBJ_FROM_PTR(&pin_GPIO26) }, // V3-E: GPIO26, V3: GPIO35 + { MP_ROM_QSTR(MP_QSTR_ENC1A_V3), MP_OBJ_FROM_PTR(&pin_GPIO35) }, // V3-E: GPIO26, V3: GPIO35 + { MP_ROM_QSTR(MP_QSTR_ENC1B), MP_OBJ_FROM_PTR(&pin_GPIO13) }, + + // Encoder 2: Both V3 and V3-E use GPIO27/4 + { MP_ROM_QSTR(MP_QSTR_ENC2A), MP_OBJ_FROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_ENC2B), MP_OBJ_FROM_PTR(&pin_GPIO4) }, + + // Light sensor (V3/V3-E) + { MP_ROM_QSTR(MP_QSTR_LIGHT_SENSOR), MP_OBJ_FROM_PTR(&pin_GPIO36) }, + + // Temperature sensors (V3/V3-E) + { MP_ROM_QSTR(MP_QSTR_TEMP_SENSOR), MP_OBJ_FROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_QI_RX_TEMP_SENSOR), MP_OBJ_FROM_PTR(&pin_GPIO35) }, // V3-E only + + { MP_ROM_QSTR(MP_QSTR_SCL), MP_OBJ_FROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_OBJ_FROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY_SCK), MP_OBJ_FROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_OBJ_FROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_MOSI), MP_OBJ_FROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_OBJ_FROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY_MISO), MP_OBJ_FROM_PTR(&pin_GPIO26) }, // V3/V3-E have MISO + { MP_ROM_QSTR(MP_QSTR_MISO), MP_OBJ_FROM_PTR(&pin_GPIO26) }, + + { MP_ROM_QSTR(MP_QSTR_NFC_IN), MP_OBJ_FROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_OBJ_FROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_NFC_OUT), MP_OBJ_FROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_OBJ_FROM_PTR(&pin_GPIO33) }, + + { MP_ROM_QSTR(MP_QSTR_I2S_MCLK), MP_OBJ_FROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_I2S_BIT_CLOCK), MP_OBJ_FROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_I2S_BCLK), MP_OBJ_FROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_I2S_WORD_SELECT), MP_OBJ_FROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_I2S_LRCLK), MP_OBJ_FROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_I2S_DOUT), MP_OBJ_FROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER_INT), MP_OBJ_FROM_PTR(&pin_GPIO34) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_OBJ_FROM_PTR(&board_i2c_obj) }, + + // Filled in by board_init() + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER0), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_IOEXPANDER1), mp_const_none }, + { MP_ROM_QSTR(MP_QSTR_BATTERY_ALERT), mp_const_none }, // IOX.0.6 + { MP_ROM_QSTR(MP_QSTR_QI_STATUS), mp_const_none }, // IOX.0.7 + { MP_ROM_QSTR(MP_QSTR_USB_STATUS), mp_const_none }, // IOX.1.0 + { MP_ROM_QSTR(MP_QSTR_CHARGE_STATUS), mp_const_none }, // IOX.1.4 + { MP_ROM_QSTR(MP_QSTR_POWER_BUTTON), mp_const_none }, // IOX.1.3 + { MP_ROM_QSTR(MP_QSTR_ENC1_BUTTON), mp_const_none }, // IOX.0.5 + { MP_ROM_QSTR(MP_QSTR_ENC2_BUTTON), mp_const_none }, // IOX.0.4 + { MP_ROM_QSTR(MP_QSTR_HEADPHONE_DETECT), mp_const_none }, // IOX.1.1 + { MP_ROM_QSTR(MP_QSTR_PACTRL), mp_const_none }, // IOX.2.4 + // V3/V3-E use ht16d35x display with 4 CS lines + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS0), mp_const_none }, // IOX.2.0 + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS1), mp_const_none }, // IOX.2.1 + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS2), mp_const_none }, // IOX.2.2 + { MP_ROM_QSTR(MP_QSTR_DISPLAY_CS3), mp_const_none }, // IOX.2.3 + + { MP_ROM_QSTR(MP_QSTR_LEVEL_CONVERTER), mp_const_none }, // IOX.3.0 + { MP_ROM_QSTR(MP_QSTR_LEVEL_POWER_ENABLE), mp_const_none }, // IOX.2.5 + { MP_ROM_QSTR(MP_QSTR_LEVEL_VINHOLD), mp_const_none }, // IOX.3.1 + { MP_ROM_QSTR(MP_QSTR_LEVEL_VOUTEN), mp_const_none }, // IOX.3.3 + + { MP_ROM_QSTR(MP_QSTR_TILT), mp_const_none }, // IOX.1.2 + { MP_ROM_QSTR(MP_QSTR_RTC_INT), mp_const_none }, // IOX.0.1 + + // Qi charging pins (V3-E) + { MP_ROM_QSTR(MP_QSTR_QI_CHARGE_ENABLE), mp_const_none }, // IOX.2.6 + { MP_ROM_QSTR(MP_QSTR_QI_ENABLE_5W), mp_const_none }, // IOX.3.5 + { MP_ROM_QSTR(MP_QSTR_QI_I2C_INT), mp_const_none }, // IOX.0.0 + + // USB-C charging pins (V3/V3-E) + { MP_ROM_QSTR(MP_QSTR_USB_CHARGE_ENABLE), mp_const_none }, // IOX.2.7 + +}; +MP_DEFINE_MUTABLE_DICT(board_module_globals, board_module_globals_table); + +void board_set(qstr q, mp_obj_t value) { + mp_obj_t key = MP_OBJ_NEW_QSTR(q); + for (size_t i = 0; i < MP_ARRAY_SIZE(board_module_globals_table); i++) { + if (board_module_globals_table[i].key == key) { + board_module_globals_table[i].value = value; + return; + } + } +} diff --git a/ports/espressif/boards/yoto_player_v3/sdkconfig b/ports/espressif/boards/yoto_player_v3/sdkconfig new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/ports/espressif/common-hal/_bleio/Adapter.c b/ports/espressif/common-hal/_bleio/Adapter.c index f604e02c20007..aca3f8c2042ca 100644 --- a/ports/espressif/common-hal/_bleio/Adapter.c +++ b/ports/espressif/common-hal/_bleio/Adapter.c @@ -44,8 +44,8 @@ #include "esp_nimble_hci.h" #include "nvs_flash.h" -#if CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif // Status variables used while busy-waiting for events. @@ -105,10 +105,10 @@ void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enable ble_svc_gatt_init(); ble_svc_ans_init(); - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML char ble_name[1 + MYNEWT_VAL_BLE_SVC_GAP_DEVICE_NAME_MAX_LENGTH]; - os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); - if (result == GETENV_OK) { + settings_err_t result = settings_get_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); + if (result == SETTINGS_OK) { ble_svc_gap_device_name_set(ble_name); } else #endif @@ -384,7 +384,7 @@ static int _connect_event(struct ble_gap_event *event, void *self_in) { // connection and need a new tuple. self->connection_objs = NULL; } else { - // The loop waiting for the connection to be comnpleted will stop when _connection_status changes. + // The loop waiting for the connection to be completed will stop when _connection_status changes. _connection_status = -event->connect.status; } break; @@ -733,7 +733,7 @@ void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { } bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) { - return ble_gap_adv_active(); + return common_hal_bleio_adapter_get_enabled(self) && ble_gap_adv_active(); } bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) { diff --git a/ports/espressif/common-hal/_bleio/Characteristic.c b/ports/espressif/common-hal/_bleio/Characteristic.c index 7e917d6334b6b..736c61c650eb4 100644 --- a/ports/espressif/common-hal/_bleio/Characteristic.c +++ b/ports/espressif/common-hal/_bleio/Characteristic.c @@ -120,7 +120,7 @@ void common_hal_bleio_characteristic_deinit(bleio_characteristic_obj_t *self) { return; } if (self->current_value != NULL) { - if (gc_nbytes(self->current_value) > 0) { + if (gc_ptr_on_heap(self->current_value)) { m_free(self->current_value); } else { port_free(self->current_value); @@ -320,7 +320,7 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, } const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); - common_hal_bleio_check_connected(conn_handle); + bleio_check_connected(conn_handle); uint16_t cccd_value = (notify ? 1 << 0 : 0) | diff --git a/ports/espressif/common-hal/_bleio/Connection.h b/ports/espressif/common-hal/_bleio/Connection.h index 326eca02ecd8a..5f33eb43b5df9 100644 --- a/ports/espressif/common-hal/_bleio/Connection.h +++ b/ports/espressif/common-hal/_bleio/Connection.h @@ -64,6 +64,7 @@ void bleio_connection_clear(bleio_connection_internal_t *self); int bleio_connection_event_cb(struct ble_gap_event *event, void *connection_in); +void bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/ports/espressif/common-hal/_bleio/__init__.c b/ports/espressif/common-hal/_bleio/__init__.c index 119fe55921e8a..4fdd0a48a2000 100644 --- a/ports/espressif/common-hal/_bleio/__init__.c +++ b/ports/espressif/common-hal/_bleio/__init__.c @@ -155,7 +155,7 @@ void check_notify(BaseType_t result) { mp_raise_msg(&mp_type_TimeoutError, NULL); } -void common_hal_bleio_check_connected(uint16_t conn_handle) { +void bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLEIO_HANDLE_INVALID) { mp_raise_ConnectionError(MP_ERROR_TEXT("Not connected")); } diff --git a/ports/espressif/common-hal/alarm/__init__.c b/ports/espressif/common-hal/alarm/__init__.c index 629f976039fd1..4f8e7e530425a 100644 --- a/ports/espressif/common-hal/alarm/__init__.c +++ b/ports/espressif/common-hal/alarm/__init__.c @@ -189,7 +189,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala _setup_sleep_alarms(true, n_alarms, alarms); } -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { alarm_pin_pinalarm_prepare_for_deep_sleep(); #if CIRCUITPY_ALARM_TOUCH alarm_touch_touchalarm_prepare_for_deep_sleep(); diff --git a/ports/espressif/common-hal/alarm/pin/PinAlarm.c b/ports/espressif/common-hal/alarm/pin/PinAlarm.c index 45f676fa5993f..97ad3b2a94255 100644 --- a/ports/espressif/common-hal/alarm/pin/PinAlarm.c +++ b/ports/espressif/common-hal/alarm/pin/PinAlarm.c @@ -14,10 +14,12 @@ #include "esp_sleep.h" #include "hal/gpio_ll.h" +#include "driver/gpio.h" #include "esp_debug_helpers.h" #ifdef SOC_PM_SUPPORT_EXT0_WAKEUP #include "soc/rtc_cntl_reg.h" +#include "soc/rtc_io_reg.h" #endif #include "driver/rtc_io.h" @@ -108,7 +110,17 @@ mp_obj_t alarm_pin_pinalarm_record_wake_alarm(void) { #ifdef SOC_PM_SUPPORT_EXT0_WAKEUP if (cause == ESP_SLEEP_WAKEUP_EXT0) { - pin_number = REG_GET_FIELD(RTC_IO_EXT_WAKEUP0_REG, RTC_IO_EXT_WAKEUP0_SEL); + int rtc_io_pin_number = REG_GET_FIELD(RTC_IO_EXT_WAKEUP0_REG, RTC_IO_EXT_WAKEUP0_SEL); + // Look up the GPIO equivalent pin for this RTC GPIO pin. On ESP32, the numbering + // is different for RTC_GPIO and regular GPIO, and there's no mapping table. + // The RTC and GPIO pin numbers match for all other current chips, so we could skip this + // for those chips, but it's not expensive, and maybe there will be another mismatch in the future. + for (gpio_num_t gpio_num = 0; gpio_num < SOC_GPIO_PIN_COUNT; gpio_num++) { + if (rtc_io_number_get(gpio_num) == rtc_io_pin_number) { + pin_number = gpio_num; + break; + } + } } else { #endif #ifdef SOC_PM_SUPPORT_EXT1_WAKEUP @@ -348,38 +360,40 @@ void alarm_pin_pinalarm_set_alarms(bool deep_sleep, size_t n_alarms, const mp_ob if (gpio_isr_register(gpio_interrupt, NULL, 0, &gpio_interrupt_handle) != ESP_OK) { mp_raise_ValueError(MP_ERROR_TEXT("Can only alarm on RTC IO from deep sleep.")); } - for (size_t i = 0; i < 64; i++) { - uint64_t mask = 1ull << i; + for (gpio_num_t pin = 0; pin < SOC_GPIO_PIN_COUNT; pin++) { + uint64_t mask = 1ULL << pin; bool high = (high_alarms & mask) != 0; bool low = (low_alarms & mask) != 0; bool pull = (pull_pins & mask) != 0; if (!(high || low)) { continue; } - if (rtc_gpio_is_valid_gpio(i)) { + if (rtc_gpio_is_valid_gpio(pin)) { #ifdef SOC_PM_SUPPORT_RTC_PERIPH_PD - rtc_gpio_deinit(i); + rtc_gpio_deinit(pin); #endif } - gpio_int_type_t interrupt_mode = GPIO_INTR_DISABLE; - gpio_pull_mode_t pull_mode = GPIO_FLOATING; + gpio_set_direction(pin, GPIO_MODE_INPUT); + if (pull) { + if (high) { + ESP_ERROR_CHECK(gpio_set_pull_mode(pin, GPIO_PULLDOWN_ONLY)); + } else { + ESP_ERROR_CHECK(gpio_set_pull_mode(pin, GPIO_PULLUP_ONLY)); + } + } else { + ESP_ERROR_CHECK(gpio_set_pull_mode(pin, GPIO_FLOATING)); + } + gpio_int_type_t intr = GPIO_INTR_DISABLE; if (high) { - interrupt_mode = GPIO_INTR_HIGH_LEVEL; - pull_mode = GPIO_PULLDOWN_ONLY; + intr = GPIO_INTR_HIGH_LEVEL; } if (low) { - interrupt_mode = GPIO_INTR_LOW_LEVEL; - pull_mode = GPIO_PULLUP_ONLY; - } - gpio_set_direction(i, GPIO_MODE_DEF_INPUT); - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[i], PIN_FUNC_GPIO); - if (pull) { - gpio_set_pull_mode(i, pull_mode); + intr = GPIO_INTR_LOW_LEVEL; } - never_reset_pin_number(i); - // Sets interrupt type and wakeup bits. - gpio_wakeup_enable(i, interrupt_mode); - gpio_intr_enable(i); + never_reset_pin_number(pin); + gpio_wakeup_enable(pin, intr); + gpio_set_intr_type(pin, intr); + gpio_intr_enable(pin); } // Wait for any pulls to settle. mp_hal_delay_ms(50); diff --git a/ports/espressif/common-hal/analogbufio/BufferedIn.c b/ports/espressif/common-hal/analogbufio/BufferedIn.c index aac7ad28daf25..20c5c060574aa 100644 --- a/ports/espressif/common-hal/analogbufio/BufferedIn.c +++ b/ports/espressif/common-hal/analogbufio/BufferedIn.c @@ -33,7 +33,7 @@ #elif defined(CONFIG_IDF_TARGET_ESP32S2) #define ADC_RESULT_BYTE 2 #define ADC_CONV_LIMIT_EN 0 -#elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(CONFIG_IDF_TARGET_ESP32P4) +#elif defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(CONFIG_IDF_TARGET_ESP32P4) || defined(CONFIG_IDF_TARGET_ESP32C61) #define ADC_RESULT_BYTE 4 #define ADC_CONV_LIMIT_EN 0 #elif defined(CONFIG_IDF_TARGET_ESP32S3) diff --git a/ports/espressif/common-hal/analogio/AnalogIn.c b/ports/espressif/common-hal/analogio/AnalogIn.c index 16340e91ac91c..34cd5b846cf22 100644 --- a/ports/espressif/common-hal/analogio/AnalogIn.c +++ b/ports/espressif/common-hal/analogio/AnalogIn.c @@ -32,6 +32,8 @@ #define DATA_WIDTH ADC_BITWIDTH_12 #elif defined(CONFIG_IDF_TARGET_ESP32C6) #define DATA_WIDTH ADC_BITWIDTH_12 +#elif defined(CONFIG_IDF_TARGET_ESP32C61) +#define DATA_WIDTH ADC_BITWIDTH_12 #elif defined(CONFIG_IDF_TARGET_ESP32P4) #define DATA_WIDTH ADC_BITWIDTH_12 #elif defined(CONFIG_IDF_TARGET_ESP32S2) @@ -86,10 +88,8 @@ uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) { adc_cali_scheme_ver_t supported_schemes; adc_cali_check_scheme(&supported_schemes); - #ifndef CONFIG_IDF_TARGET_ESP32P4 adc_cali_scheme_ver_t calibration_scheme = 0; adc_cali_handle_t calibration; - #endif #if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) && ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED adc_cali_curve_fitting_config_t config = { .unit_id = self->pin->adc_index, @@ -137,11 +137,7 @@ uint16_t common_hal_analogio_analogin_get_value(analogio_analogin_obj_t *self) { // This corrects non-linear regions of the ADC range with a LUT, so it's a better reading than raw int voltage; - #ifdef CONFIG_IDF_TARGET_ESP32P4 - voltage = 0; - #else adc_cali_raw_to_voltage(calibration, adc_reading, &voltage); - #endif #if defined(ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED) && ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED if (calibration_scheme == ADC_CALI_SCHEME_VER_CURVE_FITTING) { diff --git a/ports/espressif/common-hal/busio/I2C.c b/ports/espressif/common-hal/busio/I2C.c index 98c453ba3c086..890f9339c2254 100644 --- a/ports/espressif/common-hal/busio/I2C.c +++ b/ports/espressif/common-hal/busio/I2C.c @@ -128,6 +128,12 @@ void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { i2c_del_master_bus(self->handle); self->handle = NULL; + // Release the mutex before we delete it. Otherwise FreeRTOS gets unhappy. + xSemaphoreGive(self->xSemaphore); + vSemaphoreDelete(self->xSemaphore); + self->xSemaphore = NULL; + self->has_lock = false; + common_hal_reset_pin(self->sda_pin); common_hal_reset_pin(self->scl_pin); common_hal_busio_i2c_mark_deinit(self); @@ -160,20 +166,23 @@ bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { } void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { + if (common_hal_busio_i2c_deinited(self)) { + return; + } xSemaphoreGive(self->xSemaphore); self->has_lock = false; } -static uint8_t convert_esp_err(esp_err_t result) { +static mp_negative_errno_t convert_esp_err(esp_err_t result) { switch (result) { case ESP_OK: return 0; case ESP_FAIL: - return MP_ENODEV; + return -MP_ENODEV; case ESP_ERR_TIMEOUT: - return MP_ETIMEDOUT; + return -MP_ETIMEDOUT; default: - return MP_EIO; + return -MP_EIO; } } @@ -184,7 +193,7 @@ static size_t _transaction_duration_ms(size_t frequency, size_t len) { return (len + 1) / bytes_per_ms + 1000; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = addr, @@ -197,7 +206,7 @@ uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const u return convert_esp_err(result); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, .device_address = addr, @@ -210,7 +219,7 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t return convert_esp_err(result); } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { i2c_device_config_t dev_config = { .dev_addr_length = I2C_ADDR_BIT_LEN_7, diff --git a/ports/espressif/common-hal/busio/SPI.c b/ports/espressif/common-hal/busio/SPI.c index 6439ca4112993..21dc8c383794e 100644 --- a/ports/espressif/common-hal/busio/SPI.c +++ b/ports/espressif/common-hal/busio/SPI.c @@ -16,26 +16,12 @@ #define SPI_MAX_DMA_BITS (SPI_MAX_DMA_LEN * 8) #define MAX_SPI_TRANSACTIONS 10 -static bool spi_never_reset[SOC_SPI_PERIPH_NUM]; static spi_device_handle_t spi_handle[SOC_SPI_PERIPH_NUM]; -static StaticSemaphore_t spi_mutex[SOC_SPI_PERIPH_NUM]; static bool spi_bus_is_free(spi_host_device_t host_id) { return spi_bus_get_attr(host_id) == NULL; } -void spi_reset(void) { - for (spi_host_device_t host_id = SPI2_HOST; host_id < SOC_SPI_PERIPH_NUM; host_id++) { - if (spi_never_reset[host_id]) { - continue; - } - if (!spi_bus_is_free(host_id)) { - spi_bus_remove_device(spi_handle[host_id]); - spi_bus_free(host_id); - } - } -} - static void set_spi_config(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { // 128 is a 50% duty cycle. @@ -61,6 +47,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + const spi_bus_config_t bus_config = { .mosi_io_num = mosi != NULL ? mosi->number : -1, .miso_io_num = miso != NULL ? miso->number : -1, @@ -90,6 +79,12 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, raise_ValueError_invalid_pins(); } + self->mutex = xSemaphoreCreateMutex(); + if (self->mutex == NULL) { + spi_bus_free(self->host_id); + mp_raise_RuntimeError(MP_ERROR_TEXT("Unable to create lock")); + } + set_spi_config(self, 250000, 0, 0, 8); self->MOSI = mosi; @@ -103,12 +98,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, claim_pin(miso); } claim_pin(clock); - - self->mutex = xSemaphoreCreateMutexStatic(&spi_mutex[self->host_id]); } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - spi_never_reset[self->host_id] = true; common_hal_never_reset_pin(self->clock); if (self->MOSI != NULL) { common_hal_never_reset_pin(self->MOSI); @@ -122,6 +114,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; @@ -132,11 +128,10 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { RUN_BACKGROUND_TASKS; } - // Mark us as deinit early in case we are used in an interrupt. + // Mark as deinit early in case we are used in an interrupt. common_hal_reset_pin(self->clock); - self->clock = NULL; + common_hal_busio_spi_mark_deinit(self); - spi_never_reset[self->host_id] = false; spi_bus_remove_device(spi_handle[self->host_id]); spi_bus_free(self->host_id); @@ -162,19 +157,27 @@ bool common_hal_busio_spi_configure(busio_spi_obj_t *self, return true; } -bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { +// Wait as long as needed for the lock. This is used by SD card access from USB. +// Overrides the default busy-wait implementation in shared-bindings/busio/SPI.c +bool common_hal_busio_spi_wait_for_lock(busio_spi_obj_t *self, uint32_t timeout_ms) { if (common_hal_busio_spi_deinited(self)) { return false; } - return xSemaphoreTake(self->mutex, 1) == pdTRUE; + return xSemaphoreTake(self->mutex, pdMS_TO_TICKS(timeout_ms)) == pdTRUE; +} + +bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { + return common_hal_busio_spi_wait_for_lock(self, 0); } bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { - return self->mutex != NULL && xSemaphoreGetMutexHolder(self->mutex) == xTaskGetCurrentTaskHandle(); + return (self->mutex != NULL) && (xSemaphoreGetMutexHolder(self->mutex) == xTaskGetCurrentTaskHandle()); } void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { - xSemaphoreGive(self->mutex); + if (self->mutex != NULL) { + xSemaphoreGive(self->mutex); + } } bool common_hal_busio_spi_write(busio_spi_obj_t *self, diff --git a/ports/espressif/common-hal/busio/SPI.h b/ports/espressif/common-hal/busio/SPI.h index 820f29333c756..ac2f404042b2f 100644 --- a/ports/espressif/common-hal/busio/SPI.h +++ b/ports/espressif/common-hal/busio/SPI.h @@ -25,5 +25,3 @@ typedef struct { SemaphoreHandle_t mutex; } busio_spi_obj_t; - -void spi_reset(void); diff --git a/ports/espressif/common-hal/digitalio/DigitalInOut.c b/ports/espressif/common-hal/digitalio/DigitalInOut.c index ed537419405b3..e4272ba1ff5ff 100644 --- a/ports/espressif/common-hal/digitalio/DigitalInOut.c +++ b/ports/espressif/common-hal/digitalio/DigitalInOut.c @@ -11,8 +11,11 @@ #include "hal/gpio_hal.h" static bool _pin_is_input(uint8_t pin_number) { - const uint32_t iomux = READ_PERI_REG(GPIO_PIN_MUX_REG[pin_number]); - return (iomux & FUN_IE) != 0; + gpio_io_config_t config; + if (gpio_get_io_config((gpio_num_t)pin_number, &config) != ESP_OK) { + return false; + } + return config.ie; } void digitalio_digitalinout_preserve_for_deep_sleep(size_t n_dios, digitalio_digitalinout_obj_t *preserve_dios[]) { @@ -113,10 +116,12 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_drive_mode( digitalio_drive_mode_t common_hal_digitalio_digitalinout_get_drive_mode( digitalio_digitalinout_obj_t *self) { - if (GPIO_HAL_GET_HW(GPIO_PORT_0)->pin[self->pin->number].pad_driver == 1) { + gpio_io_config_t config; + if (gpio_get_io_config((gpio_num_t)self->pin->number, &config) != ESP_OK) { + // Should it fail closed or open? return DRIVE_MODE_OPEN_DRAIN; } - return DRIVE_MODE_PUSH_PULL; + return config.od ? DRIVE_MODE_OPEN_DRAIN : DRIVE_MODE_PUSH_PULL; } digitalinout_result_t common_hal_digitalio_digitalinout_set_pull( @@ -134,11 +139,10 @@ digitalinout_result_t common_hal_digitalio_digitalinout_set_pull( digitalio_pull_t common_hal_digitalio_digitalinout_get_pull( digitalio_digitalinout_obj_t *self) { - gpio_num_t gpio_num = self->pin->number; - if (REG_GET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PU)) { - return PULL_UP; - } else if (REG_GET_BIT(GPIO_PIN_MUX_REG[gpio_num], FUN_PD)) { - return PULL_DOWN; + gpio_io_config_t config; + if (gpio_get_io_config((gpio_num_t)self->pin->number, &config) != ESP_OK) { + // Should it fail closed or open? + return PULL_NONE; } - return PULL_NONE; + return config.pd ? PULL_DOWN : PULL_UP; } diff --git a/ports/espressif/common-hal/espnow/ESPNow.c b/ports/espressif/common-hal/espnow/ESPNow.c index 79bded96fb41d..ae11db250321c 100644 --- a/ports/espressif/common-hal/espnow/ESPNow.c +++ b/ports/espressif/common-hal/espnow/ESPNow.c @@ -56,7 +56,7 @@ typedef struct { // Callback triggered when a sent packet is acknowledged by the peer (or not). // Just count the number of responses and number of failures. // These are used in the send() logic. -static void send_cb(const uint8_t *mac, esp_now_send_status_t status) { +static void send_cb(const esp_now_send_info_t *tx_info, esp_now_send_status_t status) { espnow_obj_t *self = MP_STATE_PORT(espnow_singleton); if (status == ESP_NOW_SEND_SUCCESS) { self->send_success++; diff --git a/ports/espressif/common-hal/microcontroller/Pin.c b/ports/espressif/common-hal/microcontroller/Pin.c index 70912afb545b4..f995e65545d3c 100644 --- a/ports/espressif/common-hal/microcontroller/Pin.c +++ b/ports/espressif/common-hal/microcontroller/Pin.c @@ -77,6 +77,15 @@ static uint64_t _in_use_pin_mask; #define GPIO_SEL_47 ((uint64_t)PIN_BIT(47)) /*!< Pin 47 selected */ #define GPIO_SEL_48 ((uint64_t)PIN_BIT(48)) /*!< Pin 48 selected */ #endif +#if SOC_GPIO_PIN_COUNT > 49 +#define GPIO_SEL_49 ((uint64_t)PIN_BIT(49)) /*!< Pin 49 selected */ +#define GPIO_SEL_50 ((uint64_t)PIN_BIT(50)) /*!< Pin 50 selected */ +#define GPIO_SEL_51 ((uint64_t)PIN_BIT(51)) /*!< Pin 51 selected */ +#define GPIO_SEL_52 ((uint64_t)PIN_BIT(52)) /*!< Pin 52 selected */ +#define GPIO_SEL_53 ((uint64_t)PIN_BIT(53)) /*!< Pin 53 selected */ +#define GPIO_SEL_54 ((uint64_t)PIN_BIT(54)) /*!< Pin 54 selected */ +#define GPIO_SEL_55 ((uint64_t)PIN_BIT(55)) /*!< Pin 55 selected */ +#endif // Bit mask of all pins that should never EVER be reset. // Typically these are SPI flash and PSRAM control pins, and communication pins. @@ -146,6 +155,29 @@ static const uint64_t pin_mask_reset_forbidden = #endif #endif // ESP32C6 + #if defined(CONFIG_IDF_TARGET_ESP32C61) + // Never ever reset pins used to communicate with SPI flash. + GPIO_SEL_15 | // SPICS0 (flash CS#) + GPIO_SEL_16 | // SPIQ (MISO/SIO1) + GPIO_SEL_17 | // SPIWP (WP#/SIO2) + GPIO_SEL_19 | // SPIHD (HOLD#/SIO3) + GPIO_SEL_20 | // SPICLK (CLK) + GPIO_SEL_21 | // SPID (MOSI/SIO0) + #if CIRCUITPY_ESP_USB_SERIAL_JTAG + // Never ever reset serial/JTAG communication pins. + GPIO_SEL_12 | // USB D- + GPIO_SEL_13 | // USB D+ + #endif + #if defined(CONFIG_SPIRAM) + GPIO_SEL_14 | // SPICS1 (PSRAM CS#); keep if PSRAM in use + #endif + #if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) && CONFIG_ESP_CONSOLE_UART_DEFAULT && CONFIG_ESP_CONSOLE_UART_NUM == 0 + // Never reset debug UART/console pins. + GPIO_SEL_10 | + GPIO_SEL_11 | + #endif + #endif // ESP32C6 + #if defined(CONFIG_IDF_TARGET_ESP32H2) // Never ever reset pins used to communicate with the in-package SPI flash. GPIO_SEL_15 | @@ -172,17 +204,27 @@ static const uint64_t pin_mask_reset_forbidden = #endif // ESP32H2 #if defined(CONFIG_IDF_TARGET_ESP32P4) - // Never ever reset pins used to communicate with the SPI flash. - GPIO_SEL_28 | - GPIO_SEL_29 | - GPIO_SEL_30 | - GPIO_SEL_32 | - GPIO_SEL_33 | - GPIO_SEL_34 | - #if CIRCUITPY_ESP_USB_SERIAL_JTAG + // SPI flash is on dedicated pins. + + // USB is on the FS OTG + #if CIRCUITPY_USB_DEVICE_INSTANCE == 0 + #if CIRCUITPY_ESP32P4_SWAP_LSFS == 1 + // We leave 24 and 25 alone in addition to 26 and 27 when swapped. It doesn't work otherwise. (Not sure why.) + GPIO_SEL_24 | // USB D- + GPIO_SEL_25 | // USB D+ + #endif + GPIO_SEL_26 | // USB D- + GPIO_SEL_27 | // USB D+ + #endif + #if CIRCUITPY_ESP_USB_SERIAL_JTAG || (defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) && CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG) // Never ever reset serial/JTAG communication pins. - GPIO_SEL_50 | // USB D- - GPIO_SEL_51 | // USB D+ + #if CIRCUITPY_ESP32P4_SWAP_LSFS == 1 + GPIO_SEL_26 | // USB D- + GPIO_SEL_27 | // USB D+ + #else + GPIO_SEL_24 | // USB D- + GPIO_SEL_25 | // USB D+ + #endif #endif #if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) && CONFIG_ESP_CONSOLE_UART_DEFAULT && CONFIG_ESP_CONSOLE_UART_NUM == 0 // Never reset debug UART/console pins. @@ -370,9 +412,8 @@ void reset_all_pins(void) { gpio_deep_sleep_hold_dis(); #endif - for (uint8_t i = 0; i < GPIO_PIN_COUNT; i++) { - uint32_t iomux_address = GPIO_PIN_MUX_REG[i]; - if (iomux_address == 0 || + for (gpio_num_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) { + if (!GPIO_IS_VALID_GPIO(i) || _never_reset(i) || _skip_reset_once(i) || _preserved_pin(i)) { diff --git a/ports/espressif/common-hal/microcontroller/Processor.c b/ports/espressif/common-hal/microcontroller/Processor.c index 8a6a14c0236f5..0056465f1c5cd 100644 --- a/ports/espressif/common-hal/microcontroller/Processor.c +++ b/ports/espressif/common-hal/microcontroller/Processor.c @@ -59,7 +59,7 @@ uint32_t common_hal_mcu_processor_get_frequency(void) { // If the requested frequency is not supported by the hardware, return the next lower supported frequency static uint32_t get_valid_cpu_frequency(uint32_t requested_freq_mhz) { - #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) + #if defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C61) uint32_t valid_cpu_frequencies[] = {20, 40, 80, 160}; #elif defined(CONFIG_IDF_TARGET_ESP32C2) uint32_t valid_cpu_frequencies[] = {20, 40, 80, 120}; @@ -126,6 +126,8 @@ void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) { uint32_t mac_address_part = REG_READ(EFUSE_RD_MAC_SYS_0_REG); #elif defined(CONFIG_IDF_TARGET_ESP32C2) uint32_t mac_address_part = REG_READ(EFUSE_RD_BLK2_DATA0_REG); + #elif defined(CONFIG_IDF_TARGET_ESP32C61) + uint32_t mac_address_part = REG_READ(EFUSE_RD_MAC_SYS0_REG); #else uint32_t mac_address_part = REG_READ(EFUSE_RD_MAC_SPI_SYS_0_REG); #endif @@ -145,6 +147,8 @@ void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) { mac_address_part = REG_READ(EFUSE_RD_MAC_SYS_1_REG); #elif defined(CONFIG_IDF_TARGET_ESP32C2) mac_address_part = REG_READ(EFUSE_RD_BLK2_DATA1_REG); + #elif defined(CONFIG_IDF_TARGET_ESP32C61) + mac_address_part = REG_READ(EFUSE_RD_MAC_SYS1_REG); #else mac_address_part = REG_READ(EFUSE_RD_MAC_SPI_SYS_1_REG); #endif diff --git a/ports/espressif/common-hal/microcontroller/__init__.c b/ports/espressif/common-hal/microcontroller/__init__.c index 918366b8933c4..9b50cd7b5d286 100644 --- a/ports/espressif/common-hal/microcontroller/__init__.c +++ b/ports/espressif/common-hal/microcontroller/__init__.c @@ -36,6 +36,9 @@ #elif defined(CONFIG_IDF_TARGET_ESP32C6) #include "soc/lp_aon_reg.h" #include "esp32c6/rom/rtc.h" +#elif defined(CONFIG_IDF_TARGET_ESP32C61) +#include "soc/lp_aon_reg.h" +#include "esp32c61/rom/rtc.h" #elif defined(CONFIG_IDF_TARGET_ESP32P4) #include "esp32p4/rom/rtc.h" #elif defined(CONFIG_IDF_TARGET_ESP32S2) @@ -83,7 +86,7 @@ void common_hal_mcu_enable_interrupts(void) { void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { switch (runmode) { case RUNMODE_UF2: - #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) + #if defined(CONFIG_IDF_TARGET_ESP32) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32C61) mp_arg_error_invalid(MP_QSTR_run_mode); #else // 0x11F2 is APP_REQUEST_UF2_RESET_HINT & is defined by TinyUF2 @@ -139,7 +142,7 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { } } -void NORETURN common_hal_mcu_reset(void) { +void MP_NORETURN common_hal_mcu_reset(void) { filesystem_flush(); // TODO: implement as part of flash improvements esp_restart(); } diff --git a/ports/espressif/common-hal/mipidsi/Bus.c b/ports/espressif/common-hal/mipidsi/Bus.c new file mode 100644 index 0000000000000..dbb559ea30f22 --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/Bus.c @@ -0,0 +1,69 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/mipidsi/Bus.h" +#include "bindings/espidf/__init__.h" +#include "py/runtime.h" +#include + +void common_hal_mipidsi_bus_construct(mipidsi_bus_obj_t *self, mp_uint_t frequency, uint8_t num_lanes) { + self->frequency = frequency; + self->num_data_lanes = num_lanes; + self->bus_handle = NULL; + + if (self->use_count > 0) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_mipidsi); + } + + esp_ldo_channel_handle_t ldo_mipi_phy = NULL; + esp_ldo_channel_config_t ldo_mipi_phy_config = { + .chan_id = 3, + .voltage_mv = 2500, + }; + ESP_ERROR_CHECK(esp_ldo_acquire_channel(&ldo_mipi_phy_config, &ldo_mipi_phy)); + + // Create the DSI bus + esp_lcd_dsi_bus_config_t bus_config = { + .bus_id = 0, + .num_data_lanes = num_lanes, + .phy_clk_src = MIPI_DSI_PHY_CLK_SRC_DEFAULT, + .lane_bit_rate_mbps = frequency / 1000000, // Convert Hz to MHz + }; + + CHECK_ESP_RESULT(esp_lcd_new_dsi_bus(&bus_config, &self->bus_handle)); +} + +void common_hal_mipidsi_bus_deinit(mipidsi_bus_obj_t *self) { + if (self->use_count > 0) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_Bus); + } + if (common_hal_mipidsi_bus_deinited(self)) { + return; + } + + // Delete the DSI bus + if (self->bus_handle != NULL) { + esp_lcd_del_dsi_bus(self->bus_handle); + self->bus_handle = NULL; + } + + self->frequency = 0; + self->num_data_lanes = 0; +} + +bool common_hal_mipidsi_bus_deinited(mipidsi_bus_obj_t *self) { + return self->bus_handle == NULL; +} + +void mipidsi_bus_increment_use_count(mipidsi_bus_obj_t *self) { + self->use_count++; +} +void mipidsi_bus_decrement_use_count(mipidsi_bus_obj_t *self) { + self->use_count--; + if (self->use_count == 0) { + common_hal_mipidsi_bus_deinit(self); + } +} diff --git a/ports/espressif/common-hal/mipidsi/Bus.h b/ports/espressif/common-hal/mipidsi/Bus.h new file mode 100644 index 0000000000000..b6257fd7223dd --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/Bus.h @@ -0,0 +1,21 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include + +typedef struct { + mp_obj_base_t base; + mp_uint_t frequency; + esp_lcd_dsi_bus_handle_t bus_handle; + uint8_t num_data_lanes; + uint8_t use_count; // Up to 4 displays +} mipidsi_bus_obj_t; + +void mipidsi_bus_increment_use_count(mipidsi_bus_obj_t *self); +void mipidsi_bus_decrement_use_count(mipidsi_bus_obj_t *self); diff --git a/ports/espressif/common-hal/mipidsi/Display.c b/ports/espressif/common-hal/mipidsi/Display.c new file mode 100644 index 0000000000000..46ef46ed60185 --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/Display.c @@ -0,0 +1,291 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/mipidsi/Display.h" +#include "shared-bindings/mipidsi/Bus.h" +#include "shared-bindings/pwmio/PWMOut.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/time/__init__.h" +#include "bindings/espidf/__init__.h" +#include +#include +#include "py/runtime.h" + +// Cache write-back function (should be from rom/cache.h but it's not always available) +extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size); + +void common_hal_mipidsi_display_construct(mipidsi_display_obj_t *self, + mipidsi_bus_obj_t *bus, + const uint8_t *init_sequence, + size_t init_sequence_len, + mp_uint_t virtual_channel, + mp_uint_t width, + mp_uint_t height, + mp_int_t rotation, + mp_uint_t color_depth, + const mcu_pin_obj_t *backlight_pin, + mp_float_t brightness, + mp_uint_t native_frames_per_second, + bool backlight_on_high, + mp_uint_t hsync_pulse_width, + mp_uint_t hsync_back_porch, + mp_uint_t hsync_front_porch, + mp_uint_t vsync_pulse_width, + mp_uint_t vsync_back_porch, + mp_uint_t vsync_front_porch, + mp_uint_t pixel_clock_frequency) { + self->bus = bus; + self->virtual_channel = virtual_channel; + self->width = width; + self->height = height; + self->rotation = rotation; + self->color_depth = color_depth; + self->native_frames_per_second = native_frames_per_second; + self->backlight_on_high = backlight_on_high; + self->framebuffer = NULL; + self->dbi_io_handle = NULL; + self->dpi_panel_handle = NULL; + + // Create the DBI interface for sending commands + esp_lcd_dbi_io_config_t dbi_config = { + .virtual_channel = virtual_channel, + .lcd_cmd_bits = 8, + .lcd_param_bits = 8, + }; + + CHECK_ESP_RESULT(esp_lcd_new_panel_io_dbi(bus->bus_handle, &dbi_config, &self->dbi_io_handle)); + + // Determine the pixel format based on color depth + lcd_color_format_t color_format; + if (color_depth == 16) { + color_format = LCD_COLOR_FMT_RGB565; + } else if (color_depth == 24) { + color_format = LCD_COLOR_FMT_RGB888; + } else { + common_hal_mipidsi_display_deinit(self); + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_color_depth); + } + + // Create the DPI panel for sending pixel data + esp_lcd_dpi_panel_config_t dpi_config = { + .virtual_channel = virtual_channel, + .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, + .dpi_clock_freq_mhz = pixel_clock_frequency / 1000000, + .in_color_format = color_format, + .num_fbs = 1, + .video_timing = { + .h_size = width, + .v_size = height, + .hsync_pulse_width = hsync_pulse_width, + .hsync_back_porch = hsync_back_porch, + .hsync_front_porch = hsync_front_porch, + .vsync_pulse_width = vsync_pulse_width, + .vsync_back_porch = vsync_back_porch, + .vsync_front_porch = vsync_front_porch, + }, + .flags = { + .use_dma2d = false, + .disable_lp = false, + }, + }; + + esp_err_t ret = esp_lcd_new_panel_dpi(bus->bus_handle, &dpi_config, &self->dpi_panel_handle); + if (ret != ESP_OK) { + common_hal_mipidsi_display_deinit(self); + CHECK_ESP_RESULT(ret); + } + + // Get the framebuffer allocated by the driver + void *fb = NULL; + ret = esp_lcd_dpi_panel_get_frame_buffer(self->dpi_panel_handle, 1, &fb); + if (ret != ESP_OK || fb == NULL) { + common_hal_mipidsi_display_deinit(self); + CHECK_ESP_RESULT(ret); + } + + self->framebuffer = (uint8_t *)fb; + self->framebuffer_size = width * height * (color_depth / 8); + + // Send initialization sequence (format matches busdisplay) + #define DELAY 0x80 + uint32_t i = 0; + while (i < init_sequence_len) { + const uint8_t *cmd = init_sequence + i; + uint8_t data_size = *(cmd + 1); + bool delay = (data_size & DELAY) != 0; + data_size &= ~DELAY; + const uint8_t *data = cmd + 2; + esp_lcd_panel_io_tx_param(self->dbi_io_handle, cmd[0], data, data_size); + + uint16_t delay_length_ms = 0; + if (delay) { + data_size++; + delay_length_ms = *(cmd + 1 + data_size); + if (delay_length_ms == 255) { + delay_length_ms = 500; + } + } + common_hal_time_delay_ms(delay_length_ms); + i += 2 + data_size; + } + + // Initialize the panel after sending init commands + ret = esp_lcd_panel_init(self->dpi_panel_handle); + if (ret != ESP_OK) { + common_hal_mipidsi_display_deinit(self); + CHECK_ESP_RESULT(ret); + } + + // Setup backlight PWM if pin is provided + self->backlight_inout.base.type = &mp_type_NoneType; + if (backlight_pin != NULL && common_hal_mcu_pin_is_free(backlight_pin)) { + #if (CIRCUITPY_PWMIO) + pwmout_result_t result = common_hal_pwmio_pwmout_construct(&self->backlight_pwm, backlight_pin, 0, 50000, false); + if (result != PWMOUT_OK) { + self->backlight_inout.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(&self->backlight_inout, backlight_pin); + common_hal_never_reset_pin(backlight_pin); + } else { + self->backlight_pwm.base.type = &pwmio_pwmout_type; + common_hal_pwmio_pwmout_never_reset(&self->backlight_pwm); + } + #else + self->backlight_inout.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct(&self->backlight_inout, backlight_pin); + common_hal_never_reset_pin(backlight_pin); + #endif + + // Set initial brightness + #if (CIRCUITPY_PWMIO) + if (self->backlight_pwm.base.type == &pwmio_pwmout_type) { + common_hal_pwmio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t)(brightness * 0xFFFF)); + } else + #endif + if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { + bool on = brightness > 0; + if (!backlight_on_high) { + on = !on; + } + common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, on); + } + } + mipidsi_bus_increment_use_count(self->bus); +} + +void common_hal_mipidsi_display_deinit(mipidsi_display_obj_t *self) { + if (common_hal_mipidsi_display_deinited(self)) { + return; + } + + // Cleanup backlight + #if (CIRCUITPY_PWMIO) + if (self->backlight_pwm.base.type == &pwmio_pwmout_type) { + common_hal_pwmio_pwmout_deinit(&self->backlight_pwm); + } else + #endif + if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { + common_hal_digitalio_digitalinout_deinit(&self->backlight_inout); + } + + // Delete the DPI panel + if (self->dpi_panel_handle != NULL) { + esp_lcd_panel_del(self->dpi_panel_handle); + self->dpi_panel_handle = NULL; + } + + // Delete the DBI interface + if (self->dbi_io_handle != NULL) { + esp_lcd_panel_io_del(self->dbi_io_handle); + self->dbi_io_handle = NULL; + } + + mipidsi_bus_decrement_use_count(self->bus); + self->bus = NULL; + self->framebuffer = NULL; +} + +bool common_hal_mipidsi_display_deinited(mipidsi_display_obj_t *self) { + return self->dpi_panel_handle == NULL; +} + +void common_hal_mipidsi_display_refresh(mipidsi_display_obj_t *self) { + // Drawing the framebuffer we got from the IDF will flush the cache(s) so + // DMA can see our changes. It won't cause an extra copy. + esp_lcd_panel_draw_bitmap(self->dpi_panel_handle, 0, 0, self->width, self->height, self->framebuffer); + + // The DPI panel will automatically refresh from the framebuffer + // No explicit refresh call is needed as the DSI hardware continuously + // sends data from the framebuffer to the display +} + +mp_float_t common_hal_mipidsi_display_get_brightness(mipidsi_display_obj_t *self) { + return self->current_brightness; +} + +bool common_hal_mipidsi_display_set_brightness(mipidsi_display_obj_t *self, mp_float_t brightness) { + if (!self->backlight_on_high) { + brightness = 1.0 - brightness; + } + bool ok = false; + + // Avoid PWM types and functions when the module isn't enabled + #if (CIRCUITPY_PWMIO) + bool ispwm = (self->backlight_pwm.base.type == &pwmio_pwmout_type) ? true : false; + #else + bool ispwm = false; + #endif + + if (ispwm) { + #if (CIRCUITPY_PWMIO) + common_hal_pwmio_pwmout_set_duty_cycle(&self->backlight_pwm, (uint16_t)(0xffff * brightness)); + ok = true; + #else + ok = false; + #endif + } else if (self->backlight_inout.base.type == &digitalio_digitalinout_type) { + common_hal_digitalio_digitalinout_set_value(&self->backlight_inout, brightness > 0.99); + ok = true; + } + if (ok) { + self->current_brightness = brightness; + } + return ok; +} + +int common_hal_mipidsi_display_get_width(mipidsi_display_obj_t *self) { + return self->width; +} + +int common_hal_mipidsi_display_get_height(mipidsi_display_obj_t *self) { + return self->height; +} + +int common_hal_mipidsi_display_get_row_stride(mipidsi_display_obj_t *self) { + return self->width * (self->color_depth / 8); +} + +int common_hal_mipidsi_display_get_color_depth(mipidsi_display_obj_t *self) { + return self->color_depth; +} + +int common_hal_mipidsi_display_get_native_frames_per_second(mipidsi_display_obj_t *self) { + return self->native_frames_per_second; +} + +bool common_hal_mipidsi_display_get_grayscale(mipidsi_display_obj_t *self) { + return false; +} + +mp_int_t common_hal_mipidsi_display_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags) { + mipidsi_display_obj_t *self = (mipidsi_display_obj_t *)self_in; + + bufinfo->buf = self->framebuffer; + bufinfo->len = self->framebuffer_size; + bufinfo->typecode = 'B'; + + return 0; +} diff --git a/ports/espressif/common-hal/mipidsi/Display.h b/ports/espressif/common-hal/mipidsi/Display.h new file mode 100644 index 0000000000000..cbc87e268dc80 --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/Display.h @@ -0,0 +1,38 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/mipidsi/Bus.h" +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/digitalio/DigitalInOut.h" +#include "common-hal/pwmio/PWMOut.h" +#include +#include + +typedef struct { + mp_obj_base_t base; + mipidsi_bus_obj_t *bus; + mp_uint_t virtual_channel; + mp_uint_t width; + mp_uint_t height; + mp_int_t rotation; + mp_uint_t color_depth; + mp_uint_t native_frames_per_second; + uint8_t *framebuffer; + esp_lcd_panel_io_handle_t dbi_io_handle; + esp_lcd_panel_handle_t dpi_panel_handle; + size_t framebuffer_size; + union { + digitalio_digitalinout_obj_t backlight_inout; + #if CIRCUITPY_PWMIO + pwmio_pwmout_obj_t backlight_pwm; + #endif + }; + bool backlight_on_high; + mp_float_t current_brightness; +} mipidsi_display_obj_t; diff --git a/ports/espressif/common-hal/mipidsi/__init__.c b/ports/espressif/common-hal/mipidsi/__init__.c new file mode 100644 index 0000000000000..173f2146e87ac --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/mipidsi/__init__.h" diff --git a/ports/espressif/common-hal/mipidsi/__init__.h b/ports/espressif/common-hal/mipidsi/__init__.h new file mode 100644 index 0000000000000..972a7c082fd7b --- /dev/null +++ b/ports/espressif/common-hal/mipidsi/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c b/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c index 7721b5e197f49..52c4b67bf4efe 100644 --- a/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c +++ b/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.c @@ -18,7 +18,7 @@ #include "common-hal/microcontroller/Pin.h" #include "py/runtime.h" -#include "driver/gpio.h" +#include /* * Current pin limitations for ESP32-S2 ParallelBus: diff --git a/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.h b/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.h index 4724ba7231a4e..114a1e527a6d5 100644 --- a/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.h +++ b/ports/espressif/common-hal/paralleldisplaybus/ParallelBus.h @@ -8,7 +8,7 @@ #include "common-hal/digitalio/DigitalInOut.h" -#include "esp-idf/components/esp_lcd/include/esp_lcd_panel_io.h" +#include typedef struct { mp_obj_base_t base; diff --git a/ports/espressif/common-hal/pulseio/PulseOut.c b/ports/espressif/common-hal/pulseio/PulseOut.c index 68cb64b5e60e1..8995043b8259c 100644 --- a/ports/espressif/common-hal/pulseio/PulseOut.c +++ b/ports/espressif/common-hal/pulseio/PulseOut.c @@ -69,7 +69,7 @@ void common_hal_pulseio_pulseout_send(pulseio_pulseout_obj_t *self, uint16_t *pu // Circuitpython allows 16 bit pulse values, while ESP32 only allows 15 bits // Thus, we use entire items for one pulse, rather than switching inside each item for (size_t i = 0; i < length; i++) { - // Setting the RMT duration to 0 has undefined behavior, so avoid that pre-emptively. + // Setting the RMT duration to 0 has undefined behavior, so avoid that preemptively. if (pulses[i] == 0) { continue; } diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.c b/ports/espressif/common-hal/qspibus/QSPIBus.c new file mode 100644 index 0000000000000..6fb872248e2a6 --- /dev/null +++ b/ports/espressif/common-hal/qspibus/QSPIBus.c @@ -0,0 +1,571 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT + +#include "shared-bindings/qspibus/QSPIBus.h" + +#include "common-hal/microcontroller/Pin.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" + +#include "py/gc.h" +#include "py/runtime.h" + +#include "driver/gpio.h" +#include "esp_heap_caps.h" +#include "soc/soc_caps.h" +#include + +#define QSPI_OPCODE_WRITE_CMD (0x02U) +#define QSPI_OPCODE_WRITE_COLOR (0x32U) +#define LCD_CMD_RAMWR (0x2CU) +#define LCD_CMD_RAMWRC (0x3CU) +#define LCD_CMD_DISPOFF (0x28U) +#define LCD_CMD_SLPIN (0x10U) +#define QSPI_DMA_BUFFER_COUNT (2U) +#define QSPI_DMA_BUFFER_SIZE (16U * 1024U) +#define QSPI_COLOR_TIMEOUT_MS (1000U) +static void qspibus_release_dma_buffers(qspibus_qspibus_obj_t *self) { + for (size_t i = 0; i < QSPI_DMA_BUFFER_COUNT; i++) { + if (self->dma_buffer[i] != NULL) { + heap_caps_free(self->dma_buffer[i]); + self->dma_buffer[i] = NULL; + } + } + self->dma_buffer_size = 0; + self->active_buffer = 0; + self->inflight_transfers = 0; + self->transfer_in_progress = false; +} + +static bool qspibus_allocate_dma_buffers(qspibus_qspibus_obj_t *self) { + const size_t candidates[] = { + QSPI_DMA_BUFFER_SIZE, + QSPI_DMA_BUFFER_SIZE / 2, + QSPI_DMA_BUFFER_SIZE / 4, + }; + + for (size_t c = 0; c < MP_ARRAY_SIZE(candidates); c++) { + size_t size = candidates[c]; + bool ok = true; + for (size_t i = 0; i < QSPI_DMA_BUFFER_COUNT; i++) { + self->dma_buffer[i] = heap_caps_malloc(size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT); + if (self->dma_buffer[i] == NULL) { + ok = false; + break; + } + } + if (ok) { + self->dma_buffer_size = size; + self->active_buffer = 0; + self->inflight_transfers = 0; + self->transfer_in_progress = false; + return true; + } + qspibus_release_dma_buffers(self); + } + return false; +} + +// Reset transfer bookkeeping after timeout/error. Drains any stale semaphore +// tokens that late ISR completions may have posted after the timeout expired. +static void qspibus_reset_transfer_state(qspibus_qspibus_obj_t *self) { + self->inflight_transfers = 0; + self->transfer_in_progress = false; + if (self->transfer_done_sem != NULL) { + while (xSemaphoreTake(self->transfer_done_sem, 0) == pdTRUE) { + } + } +} + +static bool qspibus_wait_one_transfer_done(qspibus_qspibus_obj_t *self, TickType_t timeout) { + if (self->inflight_transfers == 0) { + self->transfer_in_progress = false; + return true; + } + + if (xSemaphoreTake(self->transfer_done_sem, timeout) != pdTRUE) { + return false; + } + self->inflight_transfers--; + self->transfer_in_progress = (self->inflight_transfers > 0); + return true; +} + +static bool qspibus_wait_all_transfers_done(qspibus_qspibus_obj_t *self, TickType_t timeout) { + while (self->inflight_transfers > 0) { + if (!qspibus_wait_one_transfer_done(self, timeout)) { + return false; + } + } + return true; +} + +static void qspibus_send_command_bytes( + qspibus_qspibus_obj_t *self, + uint8_t command, + const uint8_t *data, + size_t len) { + + if (!self->bus_initialized) { + raise_deinited_error(); + } + if (self->inflight_transfers >= QSPI_DMA_BUFFER_COUNT) { + if (!qspibus_wait_one_transfer_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg(MP_ERROR_TEXT("Operation timed out")); + } + } + + uint32_t packed_cmd = ((uint32_t)QSPI_OPCODE_WRITE_CMD << 24) | ((uint32_t)command << 8); + esp_err_t err = esp_lcd_panel_io_tx_param(self->io_handle, packed_cmd, data, len); + if (err != ESP_OK) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_QSPI, (int)err); + } +} + +static void qspibus_send_color_bytes( + qspibus_qspibus_obj_t *self, + uint8_t command, + const uint8_t *data, + size_t len) { + + if (!self->bus_initialized) { + raise_deinited_error(); + } + + if (len == 0) { + qspibus_send_command_bytes(self, command, NULL, 0); + return; + } + + // RAMWR must transition to RAMWRC for continued payload chunks. + uint8_t chunk_command = command; + const uint8_t *cursor = data; + size_t remaining = len; + + // Drain stale semaphore tokens that late ISR completions may have + // posted after a previous qspibus_reset_transfer_state(). Without + // this, a stale token could satisfy a future wait, causing the next + // transfer to skip its real DMA-done wait. + if (self->inflight_transfers == 0 && self->transfer_done_sem != NULL) { + while (xSemaphoreTake(self->transfer_done_sem, 0) == pdTRUE) { + } + } + + while (remaining > 0) { + // inflight_transfers is only modified in task context (never from ISR), + // so no atomic/critical section is needed. The ISR only signals the + // counting semaphore; all counter bookkeeping happens task-side. + if (self->inflight_transfers >= QSPI_DMA_BUFFER_COUNT) { + if (!qspibus_wait_one_transfer_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg(MP_ERROR_TEXT("Operation timed out")); + } + } + + size_t chunk = remaining; + if (chunk > self->dma_buffer_size) { + chunk = self->dma_buffer_size; + } + + uint8_t *buffer = self->dma_buffer[self->active_buffer]; + memcpy(buffer, cursor, chunk); + + uint32_t packed_cmd = ((uint32_t)QSPI_OPCODE_WRITE_COLOR << 24) | ((uint32_t)chunk_command << 8); + esp_err_t err = esp_lcd_panel_io_tx_color(self->io_handle, packed_cmd, buffer, chunk); + if (err != ESP_OK) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_QSPI, (int)err); + } + + self->inflight_transfers++; + self->transfer_in_progress = true; + self->active_buffer = (self->active_buffer + 1) % QSPI_DMA_BUFFER_COUNT; + + if (chunk_command == LCD_CMD_RAMWR) { + chunk_command = LCD_CMD_RAMWRC; + } + + cursor += chunk; + remaining -= chunk; + } + + // Let DMA complete asynchronously. The next begin_transaction() will + // wait for all in-flight transfers, allowing fill_area() computation + // to overlap with DMA. The explicit wait is only needed for the + // Python write_data() API path where callers expect the transfer to + // be finished on return. +} + +static bool qspibus_is_color_payload_command(uint8_t command) { + return command == LCD_CMD_RAMWR || command == LCD_CMD_RAMWRC; +} + +static void qspibus_panel_sleep_best_effort(qspibus_qspibus_obj_t *self) { + if (!self->bus_initialized || self->io_handle == NULL) { + return; + } + + if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + } + + // If a command is buffered, flush it first so the panel state machine + // doesn't get a truncated transaction before sleep. + if (self->has_pending_command) { + uint32_t pending = ((uint32_t)QSPI_OPCODE_WRITE_CMD << 24) | ((uint32_t)self->pending_command << 8); + (void)esp_lcd_panel_io_tx_param(self->io_handle, pending, NULL, 0); + self->has_pending_command = false; + } + + uint32_t disp_off = ((uint32_t)QSPI_OPCODE_WRITE_CMD << 24) | ((uint32_t)LCD_CMD_DISPOFF << 8); + (void)esp_lcd_panel_io_tx_param(self->io_handle, disp_off, NULL, 0); + vTaskDelay(pdMS_TO_TICKS(20)); + + uint32_t sleep_in = ((uint32_t)QSPI_OPCODE_WRITE_CMD << 24) | ((uint32_t)LCD_CMD_SLPIN << 8); + (void)esp_lcd_panel_io_tx_param(self->io_handle, sleep_in, NULL, 0); + vTaskDelay(pdMS_TO_TICKS(120)); +} + +static bool IRAM_ATTR qspibus_on_color_trans_done( + esp_lcd_panel_io_handle_t io_handle, + esp_lcd_panel_io_event_data_t *event_data, + void *user_ctx) { + (void)io_handle; + (void)event_data; + + qspibus_qspibus_obj_t *self = (qspibus_qspibus_obj_t *)user_ctx; + if (self->transfer_done_sem == NULL) { + return false; + } + BaseType_t x_higher_priority_task_woken = pdFALSE; + + xSemaphoreGiveFromISR(self->transfer_done_sem, &x_higher_priority_task_woken); + return x_higher_priority_task_woken == pdTRUE; +} + +void common_hal_qspibus_qspibus_construct( + qspibus_qspibus_obj_t *self, + const mcu_pin_obj_t *clock, + const mcu_pin_obj_t *data0, + const mcu_pin_obj_t *data1, + const mcu_pin_obj_t *data2, + const mcu_pin_obj_t *data3, + const mcu_pin_obj_t *cs, + const mcu_pin_obj_t *dcx, + const mcu_pin_obj_t *reset, + uint32_t frequency) { + + self->io_handle = NULL; + self->host_id = SPI2_HOST; + self->clock_pin = clock->number; + self->data0_pin = data0->number; + self->data1_pin = data1->number; + self->data2_pin = data2->number; + self->data3_pin = data3->number; + self->cs_pin = cs->number; + self->dcx_pin = (dcx != NULL) ? dcx->number : -1; + self->reset_pin = (reset != NULL) ? reset->number : -1; + self->frequency = frequency; + self->bus_initialized = false; + self->in_transaction = false; + self->has_pending_command = false; + self->pending_command = 0; + self->transfer_in_progress = false; + self->active_buffer = 0; + self->inflight_transfers = 0; + self->dma_buffer_size = 0; + self->dma_buffer[0] = NULL; + self->dma_buffer[1] = NULL; + self->transfer_done_sem = NULL; + + self->transfer_done_sem = xSemaphoreCreateCounting(QSPI_DMA_BUFFER_COUNT, 0); + if (self->transfer_done_sem == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("ESP-IDF memory allocation failed")); + } + + if (!qspibus_allocate_dma_buffers(self)) { + common_hal_qspibus_qspibus_deinit(self); + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + } + + const spi_bus_config_t bus_config = { + .sclk_io_num = self->clock_pin, + .mosi_io_num = self->data0_pin, + .miso_io_num = self->data1_pin, + .quadwp_io_num = self->data2_pin, + .quadhd_io_num = self->data3_pin, + .max_transfer_sz = self->dma_buffer_size, + .flags = SPICOMMON_BUSFLAG_MASTER | SPICOMMON_BUSFLAG_GPIO_PINS, + }; + + esp_err_t err = spi_bus_initialize(self->host_id, &bus_config, SPI_DMA_CH_AUTO); + if (err != ESP_OK) { + common_hal_qspibus_qspibus_deinit(self); + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_SPI); + } + + // Mark bus as initialized so deinit knows to call spi_bus_free(). + self->bus_initialized = true; + + const esp_lcd_panel_io_spi_config_t io_config = { + .cs_gpio_num = self->cs_pin, + .dc_gpio_num = -1, + .spi_mode = 0, + .pclk_hz = self->frequency, + .trans_queue_depth = QSPI_DMA_BUFFER_COUNT, + .on_color_trans_done = qspibus_on_color_trans_done, + .user_ctx = self, + .lcd_cmd_bits = 32, + .lcd_param_bits = 8, + .flags = { + .quad_mode = 1, + }, + }; + + err = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)self->host_id, &io_config, &self->io_handle); + if (err != ESP_OK) { + common_hal_qspibus_qspibus_deinit(self); + mp_raise_OSError_msg_varg(MP_ERROR_TEXT("%q failure: %d"), MP_QSTR_QSPI, (int)err); + } + + claim_pin(clock); + claim_pin(data0); + claim_pin(data1); + claim_pin(data2); + claim_pin(data3); + claim_pin(cs); + if (dcx != NULL) { + claim_pin(dcx); + gpio_set_direction((gpio_num_t)self->dcx_pin, GPIO_MODE_OUTPUT); + gpio_set_level((gpio_num_t)self->dcx_pin, 1); + } + + if (reset != NULL) { + claim_pin(reset); + + gpio_set_direction((gpio_num_t)self->reset_pin, GPIO_MODE_OUTPUT); + gpio_set_level((gpio_num_t)self->reset_pin, 0); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level((gpio_num_t)self->reset_pin, 1); + vTaskDelay(pdMS_TO_TICKS(120)); + } +} + +void common_hal_qspibus_qspibus_deinit(qspibus_qspibus_obj_t *self) { + if (self->bus_initialized) { + qspibus_panel_sleep_best_effort(self); + self->in_transaction = false; + + if (self->io_handle != NULL) { + esp_lcd_panel_io_del(self->io_handle); + self->io_handle = NULL; + } + + spi_bus_free(self->host_id); + self->bus_initialized = false; + } + + if (self->transfer_done_sem != NULL) { + // Set NULL before delete so late ISR callbacks (if any) see NULL and skip. + SemaphoreHandle_t sem = self->transfer_done_sem; + self->transfer_done_sem = NULL; + vSemaphoreDelete(sem); + } + + qspibus_release_dma_buffers(self); + + reset_pin_number(self->clock_pin); + reset_pin_number(self->data0_pin); + reset_pin_number(self->data1_pin); + reset_pin_number(self->data2_pin); + reset_pin_number(self->data3_pin); + reset_pin_number(self->cs_pin); + if (self->dcx_pin >= 0) { + reset_pin_number(self->dcx_pin); + } + if (self->reset_pin >= 0) { + reset_pin_number(self->reset_pin); + } + + self->bus_initialized = false; + self->in_transaction = false; + self->has_pending_command = false; + self->pending_command = 0; + self->transfer_in_progress = false; + self->inflight_transfers = 0; +} + +bool common_hal_qspibus_qspibus_deinited(qspibus_qspibus_obj_t *self) { + return !self->bus_initialized; +} + +void common_hal_qspibus_qspibus_write_command( + qspibus_qspibus_obj_t *self, + uint8_t command) { + if (!self->bus_initialized) { + raise_deinited_error(); + } + if (self->in_transaction) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); + } + + // If caller stages command-only operations repeatedly, flush the previous + // pending command as no-data before replacing it. + if (self->has_pending_command) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + } + + self->pending_command = command; + self->has_pending_command = true; +} + +void common_hal_qspibus_qspibus_write_data( + qspibus_qspibus_obj_t *self, + const uint8_t *data, + size_t len) { + if (!self->bus_initialized) { + raise_deinited_error(); + } + if (self->in_transaction) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); + } + if (len == 0) { + if (self->has_pending_command) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + self->has_pending_command = false; + } + return; + } + if (!self->has_pending_command) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); + } + + if (qspibus_is_color_payload_command(self->pending_command)) { + qspibus_send_color_bytes(self, self->pending_command, data, len); + // Python API: wait for DMA to finish so callers see the transfer as + // complete on return. The internal displayio path skips this wait + // to allow fill_area/DMA overlap. + if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + mp_raise_OSError_msg(MP_ERROR_TEXT("Operation timed out")); + } + } else { + qspibus_send_command_bytes(self, self->pending_command, data, len); + } + self->has_pending_command = false; +} + +bool common_hal_qspibus_qspibus_reset(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + if (!self->bus_initialized || self->reset_pin < 0) { + return false; + } + + gpio_set_level((gpio_num_t)self->reset_pin, 0); + vTaskDelay(pdMS_TO_TICKS(10)); + gpio_set_level((gpio_num_t)self->reset_pin, 1); + vTaskDelay(pdMS_TO_TICKS(120)); + return true; +} + +bool common_hal_qspibus_qspibus_bus_free(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + return self->bus_initialized && !self->in_transaction && !self->transfer_in_progress && !self->has_pending_command; +} + +bool common_hal_qspibus_qspibus_begin_transaction(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + if (!self->bus_initialized || self->in_transaction || self->has_pending_command) { + return false; + } + // Wait for any in-flight DMA to complete before starting a new + // transaction. This replaces the old non-blocking bus_free() check + // and enables fill_area()/DMA overlap: the CPU fills the next + // subrectangle while the previous DMA runs, and this wait only + // blocks when we actually need the bus for the next send. + if (self->transfer_in_progress) { + if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + return false; + } + } + self->in_transaction = true; + return true; +} + +void common_hal_qspibus_qspibus_send( + mp_obj_t obj, + display_byte_type_t data_type, + display_chip_select_behavior_t chip_select, + const uint8_t *data, + uint32_t data_length) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + (void)chip_select; + if (!self->bus_initialized) { + raise_deinited_error(); + } + if (!self->in_transaction) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); + } + + if (data_type == DISPLAY_COMMAND) { + for (uint32_t i = 0; i < data_length; i++) { + if (self->has_pending_command) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + } + self->pending_command = data[i]; + self->has_pending_command = true; + } + return; + } + + if (!self->has_pending_command) { + if (data_length == 0) { + // Zero-length data write after a no-data command is benign. + return; + } + mp_raise_RuntimeError(MP_ERROR_TEXT("Internal error")); + } + + if (data_length == 0) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + self->has_pending_command = false; + return; + } + + if (qspibus_is_color_payload_command(self->pending_command)) { + qspibus_send_color_bytes(self, self->pending_command, data, data_length); + } else { + qspibus_send_command_bytes(self, self->pending_command, data, data_length); + } + self->has_pending_command = false; +} + +void common_hal_qspibus_qspibus_end_transaction(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + if (!self->bus_initialized) { + return; + } + if (self->has_pending_command) { + qspibus_send_command_bytes(self, self->pending_command, NULL, 0); + self->has_pending_command = false; + } + self->in_transaction = false; +} + +void common_hal_qspibus_qspibus_flush(mp_obj_t obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(obj); + if (!self->bus_initialized) { + return; + } + if (!qspibus_wait_all_transfers_done(self, pdMS_TO_TICKS(QSPI_COLOR_TIMEOUT_MS))) { + qspibus_reset_transfer_state(self); + } +} + +void common_hal_qspibus_qspibus_collect_ptrs(mp_obj_t obj) { + (void)obj; +} diff --git a/ports/espressif/common-hal/qspibus/QSPIBus.h b/ports/espressif/common-hal/qspibus/QSPIBus.h new file mode 100644 index 0000000000000..eaaa971a79c49 --- /dev/null +++ b/ports/espressif/common-hal/qspibus/QSPIBus.h @@ -0,0 +1,50 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include + +#include "py/obj.h" + +#include "esp-idf/components/esp_lcd/include/esp_lcd_panel_io.h" +#include "driver/spi_master.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +typedef struct { + mp_obj_base_t base; + + // ESP LCD panel IO handle used for QSPI transactions. + esp_lcd_panel_io_handle_t io_handle; + + // SPI host (SPI2_HOST on ESP32-S3). + spi_host_device_t host_id; + + // Claimed GPIO numbers. + int8_t clock_pin; + int8_t data0_pin; + int8_t data1_pin; + int8_t data2_pin; + int8_t data3_pin; + int8_t cs_pin; + int8_t dcx_pin; // -1 when optional DCX line is not provided. + int8_t reset_pin; // -1 when reset line is not provided. + + uint32_t frequency; + bool bus_initialized; + bool in_transaction; + bool has_pending_command; + uint8_t pending_command; + bool transfer_in_progress; + uint8_t active_buffer; + uint8_t inflight_transfers; + size_t dma_buffer_size; + uint8_t *dma_buffer[2]; + + // Signaled from ISR when panel IO transfer completes. + SemaphoreHandle_t transfer_done_sem; +} qspibus_qspibus_obj_t; diff --git a/ports/espressif/common-hal/qspibus/__init__.c b/ports/espressif/common-hal/qspibus/__init__.c new file mode 100644 index 0000000000000..2f763b218cb43 --- /dev/null +++ b/ports/espressif/common-hal/qspibus/__init__.c @@ -0,0 +1,3 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT diff --git a/ports/espressif/common-hal/qspibus/__init__.h b/ports/espressif/common-hal/qspibus/__init__.h new file mode 100644 index 0000000000000..2f763b218cb43 --- /dev/null +++ b/ports/espressif/common-hal/qspibus/__init__.h @@ -0,0 +1,3 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT diff --git a/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c b/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c index ba8b70221b729..f65c7fdd9f110 100644 --- a/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/espressif/common-hal/rotaryio/IncrementalEncoder.c @@ -21,6 +21,10 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode // // These routines also implicitly configure the weak internal pull-ups, as expected // in CircuitPython. + + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + pcnt_unit_config_t unit_config = { // Set counter limit .low_limit = INT16_MIN, @@ -87,6 +91,10 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o pcnt_del_channel(self->channel_a); pcnt_del_channel(self->channel_b); pcnt_del_unit(self->unit); + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { self->unit = NULL; } diff --git a/ports/espressif/common-hal/sdioio/SDCard.c b/ports/espressif/common-hal/sdioio/SDCard.c index 87ba32f802095..102f4a4048e48 100644 --- a/ports/espressif/common-hal/sdioio/SDCard.c +++ b/ports/espressif/common-hal/sdioio/SDCard.c @@ -10,6 +10,7 @@ #include "driver/sdmmc_host.h" #include "ports/espressif/esp-idf/components/sdmmc/include/sdmmc_cmd.h" +#include "extmod/vfs.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/util.h" #include "shared-bindings/sdioio/SDCard.h" @@ -168,32 +169,73 @@ static void check_whole_block(mp_buffer_info_t *bufinfo, int sector_size) { } } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { - common_hal_sdioio_sdcard_check_for_deinit(self); - check_whole_block(bufinfo, self->card.csd.sector_size); - esp_err_t err; - ESP_LOGI(TAG, "in common_hal_sdioio_sdcard_writeblocks"); - // err = sdmmc_io_write_blocks(&self->card, 1, start_block, bufinfo->buf, bufinfo->len); - err = sdmmc_write_sectors(&self->card, bufinfo->buf, start_block, bufinfo->len / self->card.csd.sector_size); +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + ESP_LOGI(TAG, "in sdioio_sdcard_writeblocks"); + esp_err_t err = sdmmc_write_sectors(&self->card, buf, start_block, num_blocks); if (err != ESP_OK) { ESP_LOGW(TAG, "Failed to write blocks with err 0x%X", err); + return -MP_EIO; } return 0; } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { common_hal_sdioio_sdcard_check_for_deinit(self); check_whole_block(bufinfo, self->card.csd.sector_size); - esp_err_t err; - ESP_LOGI(TAG, "in common_hal_sdioio_sdcard_readblocks"); - // err = sdmmc_io_read_blocks(&self->card, 1, start_block, bufinfo->buf, bufinfo->len); - err = sdmmc_read_sectors(&self->card, bufinfo->buf, start_block, bufinfo->len / self->card.csd.sector_size); + + uint32_t num_blocks = bufinfo->len / self->card.csd.sector_size; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + ESP_LOGI(TAG, "in sdioio_sdcard_readblocks"); + esp_err_t err = sdmmc_read_sectors(&self->card, buf, start_block, num_blocks); if (err != ESP_OK) { ESP_LOGW(TAG, "Failed to read blocks with err 0x%X", err); + return -MP_EIO; } return 0; } +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { + common_hal_sdioio_sdcard_check_for_deinit(self); + check_whole_block(bufinfo, self->card.csd.sector_size); + + uint32_t num_blocks = bufinfo->len / self->card.csd.sector_size; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } +} + bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) { return true; } diff --git a/ports/espressif/common-hal/socketpool/Socket.c b/ports/espressif/common-hal/socketpool/Socket.c index 0a36eff95b454..5128e93c8589e 100644 --- a/ports/espressif/common-hal/socketpool/Socket.c +++ b/ports/espressif/common-hal/socketpool/Socket.c @@ -483,7 +483,7 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, socklen_t socklen = sizeof(error_code); result = getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen); if (result < 0 || error_code != 0) { - mp_raise_OSError(errno); + mp_raise_OSError(error_code); } self->connected = true; return; diff --git a/ports/espressif/common-hal/wifi/__init__.c b/ports/espressif/common-hal/wifi/__init__.c index 1a4c014bfbc5b..0eacd2bab3ec0 100644 --- a/ports/espressif/common-hal/wifi/__init__.c +++ b/ports/espressif/common-hal/wifi/__init__.c @@ -146,8 +146,8 @@ void common_hal_wifi_init(bool user_initiated) { common_hal_wifi_radio_obj.base.type = &wifi_radio_type; if (!wifi_ever_inited) { - ESP_ERROR_CHECK(esp_netif_init()); ESP_ERROR_CHECK(esp_event_loop_create_default()); + ESP_ERROR_CHECK(esp_netif_init()); wifi_ever_inited = true; } @@ -175,6 +175,7 @@ void common_hal_wifi_init(bool user_initiated) { &self->handler_instance_got_ip)); wifi_init_config_t config = WIFI_INIT_CONFIG_DEFAULT(); + esp_err_t result = esp_wifi_init(&config); #ifdef CONFIG_ESP32_WIFI_NVS_ENABLED // Generally we don't use this because we store ssid and passwords ourselves in the filesystem. esp_err_t err = nvs_flash_init(); @@ -185,8 +186,10 @@ void common_hal_wifi_init(bool user_initiated) { err = nvs_flash_init(); } ESP_ERROR_CHECK(err); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_FLASH)); + #else + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); #endif - esp_err_t result = esp_wifi_init(&config); if (result == ESP_ERR_NO_MEM) { if (gc_alloc_possible()) { mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Failed to allocate Wifi memory")); diff --git a/ports/espressif/esp-idf b/ports/espressif/esp-idf index f50ec8ecdb31f..1d2c73f641af7 160000 --- a/ports/espressif/esp-idf +++ b/ports/espressif/esp-idf @@ -1 +1 @@ -Subproject commit f50ec8ecdb31f681e6a778f145de95f849c1089d +Subproject commit 1d2c73f641af70c24274e2d3e74641592bee97e5 diff --git a/ports/espressif/esp-idf-config/sdkconfig-debug.defaults b/ports/espressif/esp-idf-config/sdkconfig-debug.defaults index a9c8e34e54706..368ba81bb4aa7 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-debug.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-debug.defaults @@ -7,6 +7,21 @@ CONFIG_COMPILER_OPTIMIZATION_SIZE=y # end of Compiler options +# Bootloader config +# +# +# Log +# +# +# Format +# +CONFIG_BOOTLOADER_LOG_COLORS=y +# end of Format + +# end of Log + +# end of Bootloader config + # # Component config # @@ -19,6 +34,17 @@ CONFIG_ESP_CONSOLE_SECONDARY_NONE=y # CONFIG_ESP_CONSOLE_SECONDARY_USB_SERIAL_JTAG is not set # end of ESP System Settings +# +# Log +# +# +# Format +# +CONFIG_LOG_COLORS=y +# end of Format + +# end of Log + # end of Component config # end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32c6.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32c6.defaults index 85dde905f3caa..63f42cac95244 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-esp32c6.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32c6.defaults @@ -49,6 +49,12 @@ CONFIG_ESP_PHY_ENABLE_USB=y CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=4 # end of Wi-Fi +# +# mbedTLS +# +# CONFIG_MBEDTLS_HARDWARE_SHA is not set +# end of mbedTLS + # end of Component config # end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32c61.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32c61.defaults new file mode 100644 index 0000000000000..85dde905f3caa --- /dev/null +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32c61.defaults @@ -0,0 +1,54 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# Bluetooth +# +CONFIG_BT_ENABLED=y +CONFIG_BT_NIMBLE_ENABLED=y +# +# NimBLE Options +# +CONFIG_BT_NIMBLE_LOG_LEVEL_NONE=y +CONFIG_BT_NIMBLE_NVS_PERSIST=y +# +# Memory Settings +# +CONFIG_BT_NIMBLE_TRANSPORT_ACL_FROM_LL_COUNT=20 +CONFIG_BT_NIMBLE_TRANSPORT_EVT_SIZE=70 +# end of Memory Settings + +CONFIG_BT_NIMBLE_EXT_ADV=y +# end of NimBLE Options + +# end of Bluetooth + +# +# Driver Configurations +# +# +# PCNT Configuration +# +CONFIG_PCNT_CTRL_FUNC_IN_IRAM=y +# end of PCNT Configuration + +# end of Driver Configurations + +# +# PHY +# +CONFIG_ESP_PHY_ENABLE_USB=y +# end of PHY + +# +# Wi-Fi +# +CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=4 +# end of Wi-Fi + +# end of Component config + +# end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults index 5c748bd0e6f02..101f90f9fa4fe 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32s2.defaults @@ -55,7 +55,7 @@ CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_TYPE_FSM=y CONFIG_ULP_COPROC_TYPE_RISCV=y # Note: enabling both ULPs simultaneously only works due to a modification of adafruit/esp-idf # (see adafruit/esp-idf/pull/16) until espressif/esp-idf/issues/12999 is fixed. -CONFIG_ULP_COPROC_RESERVE_MEM=8176 +CONFIG_ULP_COPROC_RESERVE_MEM=8144 # end of Ultra Low Power (ULP) Co-processor # diff --git a/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults b/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults index fe3c3e0a2da88..2553c648018bd 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-esp32s3.defaults @@ -95,7 +95,7 @@ CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_TYPE_FSM=y CONFIG_ULP_COPROC_TYPE_RISCV=y # Note: enabling both ULPs simultaneously only works due to a modification of adafruit/esp-idf # (see adafruit/esp-idf/pull/16) until espressif/esp-idf/issues/12999 is fixed. -CONFIG_ULP_COPROC_RESERVE_MEM=8176 +CONFIG_ULP_COPROC_RESERVE_MEM=8144 # end of Ultra Low Power (ULP) Co-processor # end of Component config diff --git a/ports/espressif/esp-idf-config/sdkconfig-opt.defaults b/ports/espressif/esp-idf-config/sdkconfig-opt.defaults index 16f5b990386a1..0da23e8122762 100644 --- a/ports/espressif/esp-idf-config/sdkconfig-opt.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig-opt.defaults @@ -38,6 +38,24 @@ CONFIG_ESP_CONSOLE_SECONDARY_NONE=y CONFIG_LOG_DEFAULT_LEVEL_NONE=y # end of Log output +# +# Log +# +# +# Log Level +# +# +# Level Settings +# +# CONFIG_LOG_DYNAMIC_LEVEL_CONTROL is not set +CONFIG_LOG_TAG_LEVEL_IMPL_NONE=y +# CONFIG_LOG_TAG_LEVEL_IMPL_CACHE_AND_LINKED_LIST is not set +# end of Level Settings + +# end of Log Level + +# end of Log + # end of Component config # end of Espressif IoT Development Framework Configuration diff --git a/ports/espressif/esp-idf-config/sdkconfig.defaults b/ports/espressif/esp-idf-config/sdkconfig.defaults index b54f599b2a5b7..5c4d6a31a62c1 100644 --- a/ports/espressif/esp-idf-config/sdkconfig.defaults +++ b/ports/espressif/esp-idf-config/sdkconfig.defaults @@ -20,6 +20,12 @@ CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM=y CONFIG_GPTIMER_ISR_IRAM_SAFE=y # end of GPTimer Configuration +# +# Touch Configuration +# +CONFIG_TOUCH_SUPPRESS_DEPRECATE_WARN=y +# end of Touch Configuration + # end of Driver Configurations # @@ -59,6 +65,13 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384 CONFIG_FREERTOS_HZ=1000 # end of Kernel +# +# LibC +# +# end of LWIP +CONFIG_LIBC_OPTIMIZED_MISALIGNED_ACCESS=y +# end of LibC + # # LWIP # @@ -71,16 +84,6 @@ CONFIG_LWIP_IPV6_AUTOCONFIG=y CONFIG_LWIP_IPV6_RDNSS_MAX_DNS_SERVERS=0 CONFIG_LWIP_IPV6_DHCP6=y # -# TCP -# -CONFIG_LWIP_MAX_ACTIVE_TCP=4 -CONFIG_LWIP_MAX_LISTENING_TCP=4 -CONFIG_LWIP_TCP_SYNMAXRTX=6 -CONFIG_LWIP_TCP_SND_BUF_DEFAULT=2880 -CONFIG_LWIP_TCP_WND_DEFAULT=2880 -CONFIG_LWIP_TCP_RTO_TIME=3000 -# end of TCP - # end of LWIP # diff --git a/ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c b/ports/espressif/module/cardputer_keyboard.c similarity index 83% rename from ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c rename to ports/espressif/module/cardputer_keyboard.c index 73880f66e19d9..548275001acc5 100644 --- a/ports/espressif/boards/m5stack_cardputer/cardputer_keyboard.c +++ b/ports/espressif/module/cardputer_keyboard.c @@ -16,7 +16,7 @@ #include "shared-module/keypad_demux/DemuxKeyMatrix.h" #include "supervisor/shared/reload.h" -#include "keymap.h" +#include "cardputer_keymap.h" //| """M5Stack Cardputer keyboard integration. //| """ @@ -31,12 +31,13 @@ //| """" //| KEYBOARD: keypad_demux.DemuxKeymatrix //| -keypad_demux_demuxkeymatrix_obj_t cardputer_keyboard_obj; -bool cardputer_keyboard_serial_attached = false; -void cardputer_keyboard_init(void); -void keyboard_seq(const char *seq); -void update_keyboard(keypad_eventqueue_obj_t *queue); +keypad_demux_demuxkeymatrix_obj_t cardputer_keyboard; +static bool cardputer_keyboard_serial_attached = false; + +// Forward declarations. +static void keyboard_seq(const char *seq); +static void update_keyboard(keypad_eventqueue_obj_t *queue); //| def detach_serial() -> None: //| """Stops consuming keyboard events and routing them to sys.stdin.""" @@ -44,7 +45,7 @@ void update_keyboard(keypad_eventqueue_obj_t *queue); //| static mp_obj_t detach_serial(void) { cardputer_keyboard_serial_attached = false; - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, NULL); + common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard.events, NULL); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_0(detach_serial_obj, detach_serial); @@ -54,7 +55,7 @@ static MP_DEFINE_CONST_FUN_OBJ_0(detach_serial_obj, detach_serial); //| ... //| static mp_obj_t attach_serial(void) { - common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard_obj.events, update_keyboard); + common_hal_keypad_eventqueue_set_event_handler(cardputer_keyboard.events, update_keyboard); cardputer_keyboard_serial_attached = true; return mp_const_none; } @@ -79,20 +80,20 @@ static mp_obj_t key_to_char(mp_obj_t key_obj, mp_obj_t shifted_obj) { static MP_DEFINE_CONST_FUN_OBJ_2(key_to_char_obj, key_to_char); // Ring buffer of characters consumed from keyboard events (when serial attached) -ringbuf_t keyqueue; -char keybuf[32]; +static ringbuf_t keyqueue; +static char keybuf[32]; keypad_event_obj_t event; -char keystate[56]; +static char keystate[56] = {0}; // Keyboard pins -const mcu_pin_obj_t *row_addr_pins[] = { +static const mcu_pin_obj_t *row_addr_pins[] = { &pin_GPIO8, &pin_GPIO9, &pin_GPIO11, }; -const mcu_pin_obj_t *column_pins[] = { +static const mcu_pin_obj_t *column_pins[] = { &pin_GPIO13, &pin_GPIO15, &pin_GPIO3, @@ -102,22 +103,22 @@ const mcu_pin_obj_t *column_pins[] = { &pin_GPIO7 }; -void cardputer_keyboard_init(void) { - cardputer_keyboard_obj.base.type = &keypad_demux_demuxkeymatrix_type; +static void cardputer_keyboard_init(void) { common_hal_keypad_demux_demuxkeymatrix_construct( - &cardputer_keyboard_obj, // self - 3, // num_row_addr_pins - row_addr_pins, // row_addr_pins - 7, // num_column_pins - column_pins, // column_pins - true, // columns_to_anodes - false, // transpose - 0.01f, // interval - 20, // max_events - 2 // debounce_threshold + &cardputer_keyboard, // self + MP_ARRAY_SIZE(row_addr_pins), // num_row_addr_pins + row_addr_pins, // row_addr_pins + MP_ARRAY_SIZE(column_pins), // num_column_pins + column_pins, // column_pins + true, // columns_to_anodes + false, // transpose + 0.01f, // interval + 20, // max_events + 2, // debounce_threshold + false // use_gc_allocator ); - demuxkeymatrix_never_reset(&cardputer_keyboard_obj); + demuxkeymatrix_never_reset(&cardputer_keyboard); ringbuf_init(&keyqueue, (uint8_t *)keybuf, sizeof(keybuf)); attach_serial(); } @@ -150,13 +151,13 @@ char board_serial_read(void) { } } -void keyboard_seq(const char *seq) { +static void keyboard_seq(const char *seq) { while (*seq) { ringbuf_put(&keyqueue, *seq++); } } -void update_keyboard(keypad_eventqueue_obj_t *queue) { +static void update_keyboard(keypad_eventqueue_obj_t *queue) { uint8_t ascii = 0; if (common_hal_keypad_eventqueue_get_length(queue) == 0) { @@ -223,7 +224,7 @@ void update_keyboard(keypad_eventqueue_obj_t *queue) { static const mp_rom_map_elem_t cardputer_keyboard_module_globals_table[] = { {MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_cardputer_keyboard)}, - {MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&cardputer_keyboard_obj)}, + {MP_ROM_QSTR(MP_QSTR_KEYBOARD), MP_ROM_PTR(&cardputer_keyboard)}, {MP_ROM_QSTR(MP_QSTR_attach_serial), MP_ROM_PTR(&attach_serial_obj)}, {MP_ROM_QSTR(MP_QSTR_detach_serial), MP_ROM_PTR(&detach_serial_obj)}, {MP_ROM_QSTR(MP_QSTR_key_to_char), MP_ROM_PTR(&key_to_char_obj)}, diff --git a/ports/espressif/boards/m5stack_cardputer/keymap.h b/ports/espressif/module/cardputer_keymap.h similarity index 100% rename from ports/espressif/boards/m5stack_cardputer/keymap.h rename to ports/espressif/module/cardputer_keymap.h diff --git a/ports/espressif/mpconfigport.h b/ports/espressif/mpconfigport.h index 712eb67f1f42a..7f1a581571eb2 100644 --- a/ports/espressif/mpconfigport.h +++ b/ports/espressif/mpconfigport.h @@ -27,7 +27,7 @@ // Nearly all boards have this because it is used to enter the ROM bootloader. #ifndef CIRCUITPY_BOOT_BUTTON - #if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) + #if defined(CONFIG_IDF_TARGET_ESP32C2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) || defined(CONFIG_IDF_TARGET_ESP32H2) || defined(CONFIG_IDF_TARGET_ESP32C61) #define CIRCUITPY_BOOT_BUTTON (&pin_GPIO9) #elif !defined(CONFIG_IDF_TARGET_ESP32) #define CIRCUITPY_BOOT_BUTTON (&pin_GPIO0) @@ -68,3 +68,7 @@ extern portMUX_TYPE background_task_mutex; #ifndef CIRCUITPY_WIFI_DEFAULT_TX_POWER #define CIRCUITPY_WIFI_DEFAULT_TX_POWER (20) #endif + +#ifndef CIRCUITPY_ESP32P4_SWAP_LSFS +#define CIRCUITPY_ESP32P4_SWAP_LSFS (0) +#endif diff --git a/ports/espressif/mpconfigport.mk b/ports/espressif/mpconfigport.mk index f05df8c07c87a..67f8ca2987bd8 100644 --- a/ports/espressif/mpconfigport.mk +++ b/ports/espressif/mpconfigport.mk @@ -17,6 +17,9 @@ CROSS_COMPILE = riscv32-esp-elf- else ifeq ($(IDF_TARGET),esp32c6) IDF_TARGET_ARCH = riscv CROSS_COMPILE = riscv32-esp-elf- +else ifeq ($(IDF_TARGET),esp32c61) +IDF_TARGET_ARCH = riscv +CROSS_COMPILE = riscv32-esp-elf- else ifeq ($(IDF_TARGET),esp32h2) IDF_TARGET_ARCH = riscv CROSS_COMPILE = riscv32-esp-elf- @@ -54,6 +57,10 @@ CIRCUITPY_HASHLIB_MBEDTLS_ONLY = 0 CIRCUITPY_PORT_SERIAL = 1 +CIRCUITPY_LIB_TLSF = 0 + +CIRCUITPY_LIBC_STRING0 = 0 + # These modules are implemented in ports//common-hal: CIRCUITPY__EVE ?= 1 CIRCUITPY_ALARM ?= 1 @@ -61,7 +68,7 @@ CIRCUITPY_ALARM_TOUCH ?= 0 CIRCUITPY_ANALOGBUFIO ?= 1 CIRCUITPY_AUDIOBUSIO ?= 1 CIRCUITPY_AUDIOBUSIO_PDMIN ?= 0 -CIRCUITPY_AUDIOIO ?= 0 +CIRCUITPY_AUDIOIO ?= 1 CIRCUITPY_BLEIO_HCI = 0 CIRCUITPY_BLEIO_NATIVE ?= 1 CIRCUITPY_CANIO ?= 1 @@ -89,19 +96,17 @@ CIRCUITPY_WATCHDOG ?= 1 CIRCUITPY_WIFI ?= 1 CIRCUITPY_SOCKETPOOL_IPV6 ?= 1 -# Conditionally turn off modules/features +# Conditionally turn off modules/features per chip type +#### esp32 ############################################################ ifeq ($(IDF_TARGET),esp32) # Modules CIRCUITPY_ALARM_TOUCH = 1 -CIRCUITPY_AUDIOIO ?= 1 CIRCUITPY_RGBMATRIX = 0 -# SDMMC not supported yet -CIRCUITPY_SDIOIO = 0 - # Has no USB CIRCUITPY_USB_DEVICE = 0 +#### esp32c2 ########################################################## else ifeq ($(IDF_TARGET),esp32c2) # C2 ROM spits out the UART at 74880 when connected to a 26mhz crystal! @@ -128,6 +133,9 @@ CIRCUITPY_ANALOGBUFIO = 0 # No I2S CIRCUITPY_AUDIOBUSIO = 0 +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No RMT CIRCUITPY_NEOPIXEL_WRITE = 0 CIRCUITPY_PULSEIO = 0 @@ -142,6 +150,7 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG = 0 +#### esp32c3 ########################################################## else ifeq ($(IDF_TARGET),esp32c3) # Modules CIRCUITPY_ESPCAMERA = 0 @@ -151,6 +160,9 @@ CIRCUITPY_MEMORYMAP = 0 # No I80 support from the IDF CIRCUITPY_PARALLELDISPLAYBUS = 0 +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No PCNT peripheral CIRCUITPY_FREQUENCYIO = 0 CIRCUITPY_COUNTIO = 0 @@ -165,6 +177,7 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 1 +#### esp32c6 ########################################################## else ifeq ($(IDF_TARGET),esp32c6) # Modules CIRCUITPY_ESPCAMERA = 0 @@ -172,6 +185,35 @@ CIRCUITPY_ESPULP = 0 CIRCUITPY_MEMORYMAP = 0 CIRCUITPY_RGBMATRIX = 0 +# No DAC +CIRCUITPY_AUDIOIO = 0 + +# No space for this +CIRCUITPY_AUDIOBUSIO = 0 + +# No I80 support from the IDF +CIRCUITPY_PARALLELDISPLAYBUS = 0 + +# No SDMMC +CIRCUITPY_SDIOIO = 0 + +CIRCUITPY_TOUCHIO ?= 1 +CIRCUITPY_TOUCHIO_USE_NATIVE = 0 +# Features +CIRCUITPY_USB_DEVICE = 0 +CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 1 + +#### esp32c6 ########################################################## +else ifeq ($(IDF_TARGET),esp32c61) +# Modules +CIRCUITPY_ESPCAMERA = 0 +CIRCUITPY_ESPULP = 0 +CIRCUITPY_MEMORYMAP = 0 +CIRCUITPY_RGBMATRIX = 0 + +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No space for this CIRCUITPY_AUDIOBUSIO = 0 @@ -187,6 +229,20 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 CIRCUITPY_USB_DEVICE = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 1 +# No TWAI on chip +CIRCUITPY_CANIO = 0 + +# No RMT on chip +CIRCUITPY_NEOPIXEL_WRITE = 0 +CIRCUITPY_PULSEIO = 0 +CIRCUITPY_RGBMATRIX = 0 + +# No PCNT on chip +CIRCUITPY_COUNTIO = 0 +CIRCUITPY_ROTARYIO = 0 +CIRCUITPY_FREQUENCYIO = 0 + +#### esp32h2 ########################################################## else ifeq ($(IDF_TARGET),esp32h2) # Modules CIRCUITPY_ESPCAMERA = 0 @@ -194,6 +250,9 @@ CIRCUITPY_ESPULP = 0 CIRCUITPY_MEMORYMAP = 0 CIRCUITPY_RGBMATRIX = 0 +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No I80 support from the IDF CIRCUITPY_PARALLELDISPLAYBUS = 0 @@ -210,8 +269,12 @@ CIRCUITPY_WIFI = 0 CIRCUITPY_MAX3421E = 0 +#### esp32p4 ########################################################## else ifeq ($(IDF_TARGET),esp32p4) +# No DAC +CIRCUITPY_AUDIOIO = 0 + # No wifi # TODO: Support ESP32-C6 coprocessor on some boards. CIRCUITPY_BLEIO_NATIVE = 0 @@ -224,7 +287,6 @@ CIRCUITPY_TOUCHIO_USE_NATIVE = 0 # Second stage bootloader doesn't work when the factory partition is empty due to # UF2 missing. UF2_BOOTLOADER = 0 -USB_HIGHSPEED = 1 CIRCUITPY_USB_HID = 0 CIRCUITPY_USB_MIDI = 0 CIRCUITPY_TUSB_MEM_ALIGN = 64 @@ -246,10 +308,15 @@ CIRCUITPY_PARALLELDISPLAYBUS = 0 # Library doesn't support P4 yet it seems CIRCUITPY_ESPCAMERA = 0 +# P4 has MIPI-DSI +CIRCUITPY_MIPIDSI = 1 + +#### esp32s2 ########################################################## else ifeq ($(IDF_TARGET),esp32s2) # Modules -CIRCUITPY_ALARM_TOUCH = 1 +CIRCUITPY_ALARM_TOUCH = $(CIRCUITPY_ALARM) CIRCUITPY_AUDIOIO ?= 1 + # No BLE in hw CIRCUITPY_BLEIO_NATIVE = 0 @@ -258,12 +325,19 @@ CIRCUITPY_SDIOIO = 0 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 0 +#### esp32s3 ########################################################## else ifeq ($(IDF_TARGET),esp32s3) + # Modules -CIRCUITPY_ALARM_TOUCH = 1 +CIRCUITPY_ALARM_TOUCH = $(CIRCUITPY_ALARM) CIRCUITPY_AUDIOBUSIO_PDMIN = 1 CIRCUITPY_ESP_USB_SERIAL_JTAG ?= 0 + +# No DAC +CIRCUITPY_AUDIOIO = 0 + endif +#### end chip-specific choices ######################################## # No room for large modules on 2MB boards # 2MB boards have a single firmware partition, and can't do dualbank. diff --git a/ports/espressif/peripherals/esp32c61/pins.c b/ports/espressif/peripherals/esp32c61/pins.c new file mode 100644 index 0000000000000..a32e1e8da9856 --- /dev/null +++ b/ports/espressif/peripherals/esp32c61/pins.c @@ -0,0 +1,38 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 microDev +// +// SPDX-License-Identifier: MIT + +#include "peripherals/pins.h" + +const mcu_pin_obj_t pin_GPIO0 = PIN(0, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO1 = PIN(1, ADC_UNIT_1, ADC_CHANNEL_0, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO2 = PIN(2, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO3 = PIN(3, ADC_UNIT_1, ADC_CHANNEL_1, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO4 = PIN(4, ADC_UNIT_1, ADC_CHANNEL_2, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO5 = PIN(5, ADC_UNIT_1, ADC_CHANNEL_3, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO6 = PIN(6, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO7 = PIN(7, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO8 = PIN(8, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO9 = PIN(9, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO10 = PIN(10, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO11 = PIN(11, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO12 = PIN(12, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO13 = PIN(13, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO14 = PIN(14, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO15 = PIN(15, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO16 = PIN(16, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO17 = PIN(17, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO18 = PIN(18, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO19 = PIN(19, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO20 = PIN(20, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO21 = PIN(21, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO22 = PIN(22, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO23 = PIN(23, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO24 = PIN(24, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO25 = PIN(25, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO26 = PIN(26, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO27 = PIN(27, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO28 = PIN(28, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); +const mcu_pin_obj_t pin_GPIO29 = PIN(29, NO_ADC, NO_ADC_CHANNEL, NO_TOUCH_CHANNEL); diff --git a/ports/espressif/peripherals/esp32c61/pins.h b/ports/espressif/peripherals/esp32c61/pins.h new file mode 100644 index 0000000000000..908c9fe29f0e2 --- /dev/null +++ b/ports/espressif/peripherals/esp32c61/pins.h @@ -0,0 +1,72 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2023 Scott Shawcroft for Adafruit Industries LLC +// +// SPDX-License-Identifier: MIT + +// DO NOT include this file directly. +// Use shared-bindings/microcontroller/Pin.h instead. +// This ensures that all necessary includes are already included. + +#pragma once + +#define GPIO0_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO0; +#define GPIO1_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO1; +#define GPIO2_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO2; +#define GPIO3_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO3; +#define GPIO4_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO4; +#define GPIO5_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO5; +#define GPIO6_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO6; +#define GPIO7_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO7; +#define GPIO8_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO8; +#define GPIO9_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO9; +#define GPIO10_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO10; +#define GPIO11_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO11; +#define GPIO12_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO12; +#define GPIO13_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO13; +#define GPIO14_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO14; +#define GPIO15_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO15; +#define GPIO16_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO16; +#define GPIO17_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO17; +#define GPIO18_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO18; +#define GPIO19_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO19; +#define GPIO20_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO20; +#define GPIO21_EXISTS 0 +extern const mcu_pin_obj_t pin_GPIO21; +#define GPIO22_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO22; +#define GPIO23_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO23; +#define GPIO24_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO24; +#define GPIO25_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO25; +#define GPIO26_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO26; +#define GPIO27_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO27; +#define GPIO28_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO28; +#define GPIO29_EXISTS 1 +extern const mcu_pin_obj_t pin_GPIO29; diff --git a/ports/espressif/peripherals/pins.h b/ports/espressif/peripherals/pins.h index f89855e308f59..bbe42be1bcae2 100644 --- a/ports/espressif/peripherals/pins.h +++ b/ports/espressif/peripherals/pins.h @@ -14,7 +14,7 @@ #include "components/hal/include/hal/gpio_types.h" #include "components/hal/include/hal/adc_types.h" -#include "components/hal/include/hal/touch_sensor_types.h" +#include "components/hal/include/hal/touch_sensor_legacy_types.h" typedef struct { mp_obj_base_t base; @@ -52,6 +52,8 @@ extern const mp_obj_type_t mcu_pin_type; #include "esp32c3/pins.h" #elif defined(CONFIG_IDF_TARGET_ESP32C6) #include "esp32c6/pins.h" +#elif defined(CONFIG_IDF_TARGET_ESP32C61) +#include "esp32c61/pins.h" #elif defined(CONFIG_IDF_TARGET_ESP32P4) #include "esp32p4/pins.h" #elif defined(CONFIG_IDF_TARGET_ESP32H2) diff --git a/ports/espressif/supervisor/port.c b/ports/espressif/supervisor/port.c index 48ae29ab4f927..d2aafb99f0bd6 100644 --- a/ports/espressif/supervisor/port.c +++ b/ports/espressif/supervisor/port.c @@ -103,7 +103,7 @@ static esp_timer_handle_t _sleep_timer; TaskHandle_t circuitpython_task = NULL; -extern void esp_restart(void) NORETURN; +extern void esp_restart(void) MP_NORETURN; static void tick_on_cp_core(void *arg) { supervisor_tick(); @@ -216,6 +216,11 @@ static void _never_reset_spi_ram_flash(void) { never_reset_pin_number(bootloader_flash_get_wp_pin()); } #endif // CONFIG_IDF_TARGET_ESP32 + #if defined(CONFIG_IDF_TARGET_ESP32C61) + #if defined(CONFIG_SPIRAM) + common_hal_never_reset_pin(&pin_GPIO14); + #endif + #endif } safe_mode_t port_init(void) { @@ -239,19 +244,19 @@ safe_mode_t port_init(void) { #endif // Send the ROM output out of the UART. This includes early logs. - #if DEBUG + #if DEBUG && (defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) && CONFIG_ESP_CONSOLE_UART_DEFAULT) esp_rom_install_uart_printf(); #endif #define pin_GPIOn(n) pin_GPIO##n #define pin_GPIOn_EXPAND(x) pin_GPIOn(x) - #ifdef CONFIG_CONSOLE_UART_TX_GPIO - common_hal_never_reset_pin(&pin_GPIOn_EXPAND(CONFIG_CONSOLE_UART_TX_GPIO)); + #ifdef CONFIG_ESP_CONSOLE_UART_TX_GPIO + common_hal_never_reset_pin(&pin_GPIOn_EXPAND(CONFIG_ESP_CONSOLE_UART_TX_GPIO)); #endif - #ifdef CONFIG_CONSOLE_UART_RX_GPIO - common_hal_never_reset_pin(&pin_GPIOn_EXPAND(CONFIG_CONSOLE_UART_RX_GPIO)); + #ifdef CONFIG_ESP_CONSOLE_UART_RX_GPIO + common_hal_never_reset_pin(&pin_GPIOn_EXPAND(CONFIG_ESP_CONSOLE_UART_RX_GPIO)); #endif #ifndef ENABLE_JTAG @@ -271,7 +276,7 @@ safe_mode_t port_init(void) { common_hal_never_reset_pin(&pin_GPIO40); common_hal_never_reset_pin(&pin_GPIO41); common_hal_never_reset_pin(&pin_GPIO42); - #elif defined(CONFIG_IDF_TARGET_ESP32P4) + #elif defined(CONFIG_IDF_TARGET_ESP32P4) || defined(CONFIG_IDF_TARGET_ESP32C61) common_hal_never_reset_pin(&pin_GPIO3); common_hal_never_reset_pin(&pin_GPIO4); common_hal_never_reset_pin(&pin_GPIO5); @@ -351,14 +356,11 @@ void reset_port(void) { ssl_reset(); #endif - reset_all_pins(); - #if CIRCUITPY_ANALOGIO analogout_reset(); #endif #if CIRCUITPY_BUSIO - spi_reset(); uart_reset(); #endif @@ -402,8 +404,8 @@ void reset_port(void) { watchdog_reset(); #endif - // Yield so the idle task can run and do any IDF cleanup needed. - port_yield(); + // Yield so the idle task, at priority 0, can run and do any IDF cleanup needed. + port_task_sleep_ms(4); } void reset_to_bootloader(void) { @@ -481,8 +483,13 @@ void port_wake_main_task_from_isr(void) { } } -void port_yield(void) { - vTaskDelay(4); +// Yield to other tasks at the same priority. +void port_task_yield(void) { + vTaskDelay(0); +} + +void port_task_sleep_ms(uint32_t msecs) { + vTaskDelay(pdMS_TO_TICKS(msecs)); } void sleep_timer_cb(void *arg) { diff --git a/ports/espressif/supervisor/usb.c b/ports/espressif/supervisor/usb.c index 612abaa808ae2..e4d34ee7694f3 100644 --- a/ports/espressif/supervisor/usb.c +++ b/ports/espressif/supervisor/usb.c @@ -2,6 +2,7 @@ // // SPDX-FileCopyrightText: Copyright (c) 2018 hathach for Adafruit Industries // SPDX-FileCopyrightText: Copyright (c) 2019 Lucian Copeland for Adafruit Industries +// SPDX-FileContributor: 2025 Nicolai Electronics // // SPDX-License-Identifier: MIT @@ -27,6 +28,10 @@ #include "tusb.h" +#ifdef CONFIG_IDF_TARGET_ESP32P4 +#include "hal/usb_serial_jtag_ll.h" +#endif + #if CIRCUITPY_USB_DEVICE #ifdef CFG_TUSB_DEBUG #define USBD_STACK_SIZE (3 * configMINIMAL_STACK_SIZE) @@ -37,7 +42,7 @@ StackType_t usb_device_stack[USBD_STACK_SIZE]; StaticTask_t usb_device_taskdef; -static usb_phy_handle_t phy_hdl; +static usb_phy_handle_t device_phy_hdl; // USB Device Driver task // This top level thread process all usb events and invoke callbacks @@ -51,34 +56,10 @@ static void usb_device_task(void *param) { tud_task(); tud_cdc_write_flush(); } - vTaskDelay(1); - } -} - -/** - * Callback invoked when received an "wanted" char. - * @param itf Interface index (for multiple cdc interfaces) - * @param wanted_char The wanted char (set previously) - */ -void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { - (void)itf; // not used - // CircuitPython's VM is run in a separate FreeRTOS task from TinyUSB. - // So, we must notify the other task when a CTRL-C is received. - port_wake_main_task(); - // Workaround for using shared/runtime/interrupt_char.c - // Compare mp_interrupt_char with wanted_char and ignore if not matched - if (mp_interrupt_char == wanted_char) { - tud_cdc_read_flush(); // flush read fifo - mp_sched_keyboard_interrupt(); + // Yield with zero delay to switch to any other tasks at same priority. + port_task_yield(); } } - -void tud_cdc_rx_cb(uint8_t itf) { - (void)itf; - // Workaround for "press any key to enter REPL" response being delayed on espressif. - // Wake main task when any key is pressed. - port_wake_main_task(); -} #endif // CIRCUITPY_USB_DEVICE void init_usb_hardware(void) { @@ -86,14 +67,45 @@ void init_usb_hardware(void) { // Configure USB PHY usb_phy_config_t phy_conf = { .controller = USB_PHY_CTRL_OTG, + #if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 1 + .target = USB_PHY_TARGET_UTMI, + #else .target = USB_PHY_TARGET_INT, - + #endif .otg_mode = USB_OTG_MODE_DEVICE, + #if defined(CONFIG_IDF_TARGET_ESP32P4) && CIRCUITPY_USB_DEVICE_INSTANCE == 0 + .otg_speed = USB_PHY_SPEED_FULL, + #else // https://github.com/hathach/tinyusb/issues/2943#issuecomment-2601888322 // Set speed to undefined (auto-detect) to avoid timing/race issue with S3 with host such as macOS .otg_speed = USB_PHY_SPEED_UNDEFINED, + #endif }; - usb_new_phy(&phy_conf, &phy_hdl); + usb_new_phy(&phy_conf, &device_phy_hdl); + + #if CIRCUITPY_ESP32P4_SWAP_LSFS == 1 + #ifndef CONFIG_IDF_TARGET_ESP32P4 + #error "LSFS swap is only supported on ESP32P4" + #endif + // Switch the USB PHY + const usb_serial_jtag_pull_override_vals_t override_disable_usb = { + .dm_pd = true, .dm_pu = false, .dp_pd = true, .dp_pu = false + }; + const usb_serial_jtag_pull_override_vals_t override_enable_usb = { + .dm_pd = false, .dm_pu = false, .dp_pd = false, .dp_pu = true + }; + + // Drop off the bus by removing the pull-up on USB DP + usb_serial_jtag_ll_phy_enable_pull_override(&override_disable_usb); + + // Select USB mode by swapping and un-swapping the two PHYs + vTaskDelay(pdMS_TO_TICKS(500)); // Wait for disconnect before switching to device + usb_serial_jtag_ll_phy_select(1); + + // Put the device back onto the bus by re-enabling the pull-up on USB DP + usb_serial_jtag_ll_phy_enable_pull_override(&override_enable_usb); + usb_serial_jtag_ll_phy_disable_pull_override(); + #endif // Pin the USB task to the same core as CircuitPython. This way we leave // the other core for networking. @@ -101,7 +113,7 @@ void init_usb_hardware(void) { "usbd", USBD_STACK_SIZE, NULL, - 5, + 1, usb_device_stack, &usb_device_taskdef, xPortGetCoreID()); diff --git a/ports/espressif/tools/build_memory_info.py b/ports/espressif/tools/build_memory_info.py index 9a3c55501388e..0cf609c4d63f4 100644 --- a/ports/espressif/tools/build_memory_info.py +++ b/ports/espressif/tools/build_memory_info.py @@ -61,6 +61,12 @@ ("LP SRAM", (0x5000_0000,), 16 * 1024), ("HP SRAM", (0x4080_0000,), 512 * 1024), ], + "esp32c61": [ + # Name, Start, Length + ("LP SRAM", (0x5000_0000,), 16 * 1024), + ("HP SRAM", (0x4080_0000,), 320 * 1024), + ("PSRAM", (0x4200_0000,), 2 * 1024 * 1024), + ], "esp32h2": [ # Name, Start, Length ("LP SRAM", (0x5000_0000,), 4 * 1024), diff --git a/ports/espressif/tools/decode_backtrace.py b/ports/espressif/tools/decode_backtrace.py index 024e636207ec8..16cef9e0822cc 100644 --- a/ports/espressif/tools/decode_backtrace.py +++ b/ports/espressif/tools/decode_backtrace.py @@ -12,20 +12,41 @@ board = sys.argv[1] print(board) +elfs = [ + f"build-{board}/firmware.elf", + # Add additional ELF files here such as the ROM ELF files from: + # https://github.com/espressif/esp-rom-elfs/releases + # "/home/tannewt/Downloads/esp-rom-elfs-20241011/esp32c6_rev0_rom.elf", +] + while True: + print('"Backtrace:" or "Stack memory:". CTRL-D to finish multiline paste') addresses = input("? ") if addresses.startswith("Backtrace:"): addresses = addresses[len("Backtrace:") :] - if addresses.startswith("Stack memory:"): - addresses = addresses[len("Stack memory:") :] - addresses = addresses.strip().split() - addresses = [address.split(":")[0] for address in addresses] + addresses = addresses.strip().split() + addresses = [address.split(":")[0] for address in addresses] + elif addresses.startswith("Stack memory:"): + addresses = [] + extra_lines = sys.stdin.readlines() + for line in extra_lines: + if not line.strip(): + continue + addresses.extend(line.split(":")[1].strip().split()) for address in addresses: - result = subprocess.run( - ["xtensa-esp32s2-elf-addr2line", "-aipfe", "build-{}/firmware.elf".format(board)] - + [address], - capture_output=True, - ) - stdout = result.stdout.decode("utf-8") - if "?? ??" not in stdout: - print(stdout.strip()) + if address == "0xa5a5a5a5": + # Skip stack fill value. + continue + for elf in elfs: + result = subprocess.run( + ["riscv32-esp-elf-addr2line", "-aipfe", elf, address], + capture_output=True, + ) + stdout = result.stdout.decode("utf-8") + if not stdout: + continue + if "?? ??" not in stdout: + print(stdout.strip()) + break + + print("loop") diff --git a/ports/espressif/tools/update_sdkconfig.py b/ports/espressif/tools/update_sdkconfig.py index 8837d84321b30..d40614edb75d8 100644 --- a/ports/espressif/tools/update_sdkconfig.py +++ b/ports/espressif/tools/update_sdkconfig.py @@ -157,7 +157,7 @@ def sym_default(sym): default=False, help="Updates the sdkconfigs outside of the board directory.", ) -def update(debug, board, update_all): # noqa: C901: too complex +def update(debug, board, update_all): # noqa: C901 too complex """Updates related sdkconfig files based on the build directory version that was likely modified by menuconfig.""" @@ -321,7 +321,7 @@ def update(debug, board, update_all): # noqa: C901: too complex if print_debug: print(" " * len(current_group), i, config_string.strip()) - # Some files are `rsource`d into another kconfig with $IDF_TARGET as + # Some files are `rsource`d into another kconfig with $IDF_TARGET as # codespell:ignore rsource # part of the path. kconfiglib doesn't show this as a reference so # we have to look ourselves. target_reference = target in item.name_and_loc diff --git a/ports/litex/Makefile b/ports/litex/Makefile index 98abe985699cf..cc813a49f8e70 100644 --- a/ports/litex/Makefile +++ b/ports/litex/Makefile @@ -32,7 +32,7 @@ ifeq ($(DEBUG), 1) OPTIMIZATION_FLAGS ?= -Og else CFLAGS += -DNDEBUG -ggdb3 - OPTIMIZATION_FLAGS ?= -O2 -fno-inline-functions + OPTIMIZATION_FLAGS ?= -O2 endif # option to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk @@ -78,8 +78,11 @@ SRC_C += lib/tinyusb/src/portable/valentyusb/eptri/dcd_eptri.c endif SRC_S_UPPER = \ - crt0-vexriscv.S \ - supervisor/shared/cpu_regs.S + crt0-vexriscv.S + +SRC_S = shared/runtime/gchelper_rv32i.s + +SRC_C += shared/runtime/gchelper_native.c $(BUILD)/lib/tlsf/tlsf.o: CFLAGS += -Wno-cast-align @@ -94,6 +97,7 @@ ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) diff --git a/ports/litex/common-hal/microcontroller/Pin.c b/ports/litex/common-hal/microcontroller/Pin.c index dea848f1ec3f3..bb3636c5b3faa 100644 --- a/ports/litex/common-hal/microcontroller/Pin.c +++ b/ports/litex/common-hal/microcontroller/Pin.c @@ -11,6 +11,10 @@ static uint8_t claimed_pins[1]; +void reset_all_pins(void) { + // TODO +} + // Mark pin as free and return it to a quiescent state. void reset_pin_number(uint8_t pin_port, uint8_t pin_number) { if (pin_port == 0x0F) { diff --git a/ports/litex/supervisor/port.c b/ports/litex/supervisor/port.c index 98fce16152e0b..7f9aab7f7ffb4 100644 --- a/ports/litex/supervisor/port.c +++ b/ports/litex/supervisor/port.c @@ -59,7 +59,6 @@ extern uint32_t _heap_start; extern uint32_t _estack; void reset_port(void) { - // reset_all_pins(); // i2c_reset(); // spi_reset(); // uart_reset(); diff --git a/ports/mimxrt10xx/Makefile b/ports/mimxrt10xx/Makefile index e6928eead619d..74667fd3d6446 100644 --- a/ports/mimxrt10xx/Makefile +++ b/ports/mimxrt10xx/Makefile @@ -162,8 +162,11 @@ SRC_C += \ endif SRC_S_UPPER = \ - sdk/devices/$(CHIP_FAMILY)/gcc/startup_$(CHIP_CORE).S \ - supervisor/shared/cpu_regs.S + sdk/devices/$(CHIP_FAMILY)/gcc/startup_$(CHIP_CORE).S + +SRC_S = shared/runtime/gchelper_thumb2.s + +SRC_C += shared/runtime/gchelper_native.c OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_SDK:.c=.o)) @@ -172,7 +175,7 @@ ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S:.S=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) diff --git a/ports/mimxrt10xx/common-hal/busio/I2C.c b/ports/mimxrt10xx/common-hal/busio/I2C.c index 132b3083212a1..93c0ab301235b 100644 --- a/ports/mimxrt10xx/common-hal/busio/I2C.c +++ b/ports/mimxrt10xx/common-hal/busio/I2C.c @@ -201,7 +201,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { lpi2c_master_transfer_t xfer = { 0 }; @@ -215,15 +215,15 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, return 0; } - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { lpi2c_master_transfer_t xfer = { 0 }; @@ -237,12 +237,12 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, return 0; } - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/mimxrt10xx/common-hal/busio/SPI.c b/ports/mimxrt10xx/common-hal/busio/SPI.c index 732b23d8c9b3b..90695be4b4e6d 100644 --- a/ports/mimxrt10xx/common-hal/busio/SPI.c +++ b/ports/mimxrt10xx/common-hal/busio/SPI.c @@ -26,7 +26,6 @@ // arrays use 0 based numbering: SPI1 is stored at index 0 static bool reserved_spi[MP_ARRAY_SIZE(mcu_spi_banks)]; -static bool never_reset_spi[MP_ARRAY_SIZE(mcu_spi_banks)]; #if IMXRT11XX static const clock_ip_name_t s_lpspiClocks[] = LPSPI_CLOCKS; @@ -53,22 +52,6 @@ static void config_periph_pin(const mcu_periph_obj_t *periph) { | IOMUXC_SW_PAD_CTL_PAD_SRE(0)); } -void spi_reset(void) { - for (uint i = 0; i < MP_ARRAY_SIZE(mcu_spi_banks); i++) { - if (!never_reset_spi[i]) { - reserved_spi[i] = false; - #if IMXRT11XX - // Skip resetting SPIs that aren't clocked. Doing so generates a bus fault. - if ((CCM->LPCG[s_lpspiClocks[i + 1]].STATUS0 & CCM_LPCG_STATUS0_ON_MASK) == ((uint32_t)kCLOCK_Off & CCM_LPCG_STATUS0_ON_MASK)) { - continue; - } - #endif - - LPSPI_Deinit(mcu_spi_banks[i]); - } - } -} - void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { @@ -82,6 +65,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_half_duplex); } + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + for (uint i = 0; i < sck_count; i++) { if (mcu_spi_sck_list[i].pin != clock) { continue; @@ -200,7 +186,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset_spi[self->clock->bank_idx - 1] = true; common_hal_never_reset_pin(self->clock->pin); if (self->mosi != NULL) { common_hal_never_reset_pin(self->mosi->pin); @@ -214,21 +199,25 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; } LPSPI_Deinit(self->spi); reserved_spi[self->clock->bank_idx - 1] = false; - never_reset_spi[self->clock->bank_idx - 1] = false; common_hal_reset_pin(self->clock->pin); common_hal_reset_pin(self->mosi->pin); common_hal_reset_pin(self->miso->pin); - self->clock = NULL; self->mosi = NULL; self->miso = NULL; + + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, diff --git a/ports/mimxrt10xx/common-hal/busio/SPI.h b/ports/mimxrt10xx/common-hal/busio/SPI.h index 67801078261c1..d86489428ec78 100644 --- a/ports/mimxrt10xx/common-hal/busio/SPI.h +++ b/ports/mimxrt10xx/common-hal/busio/SPI.h @@ -21,5 +21,3 @@ typedef struct { const mcu_periph_obj_t *mosi; const mcu_periph_obj_t *miso; } busio_spi_obj_t; - -void spi_reset(void); diff --git a/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c b/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c index c71f8b212e183..211b5c186f3fa 100644 --- a/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/mimxrt10xx/common-hal/rotaryio/IncrementalEncoder.c @@ -26,6 +26,9 @@ static void encoder_change(void *self_in) { void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + self->pin_a = pin_a; self->pin_b = pin_b; @@ -49,7 +52,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode } bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self) { - return !self->pin_a; + return self->pin_a == NULL; } void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self) { @@ -62,6 +65,9 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o common_hal_reset_pin(self->pin_a); common_hal_reset_pin(self->pin_b); + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { self->pin_a = NULL; - self->pin_b = NULL; } diff --git a/ports/mimxrt10xx/common-hal/usb_host/Port.c b/ports/mimxrt10xx/common-hal/usb_host/Port.c index 31af4ed582332..a7e73ab5b5c49 100644 --- a/ports/mimxrt10xx/common-hal/usb_host/Port.c +++ b/ports/mimxrt10xx/common-hal/usb_host/Port.c @@ -47,5 +47,8 @@ usb_host_port_obj_t *common_hal_usb_host_port_construct(const mcu_pin_obj_t *dp, self->dp = dp; self->dm = dm; + claim_pin(dp); + claim_pin(dm); + return self; } diff --git a/ports/mimxrt10xx/mpconfigport.h b/ports/mimxrt10xx/mpconfigport.h index 4d5e4e59700bd..df634dc73282e 100644 --- a/ports/mimxrt10xx/mpconfigport.h +++ b/ports/mimxrt10xx/mpconfigport.h @@ -25,6 +25,8 @@ extern uint8_t _ld_default_stack_size; // are aligned to cache lines. #define MICROPY_BYTES_PER_GC_BLOCK (32) +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED (1) + #include "py/circuitpy_mpconfig.h" // TODO: diff --git a/ports/mimxrt10xx/mpconfigport.mk b/ports/mimxrt10xx/mpconfigport.mk index 20d802eb3548e..b3f078c7ecf98 100644 --- a/ports/mimxrt10xx/mpconfigport.mk +++ b/ports/mimxrt10xx/mpconfigport.mk @@ -2,8 +2,6 @@ LD_FILE = $(FLASH).ld $(CHIP_FAMILY).ld imxrt10xx.ld INTERNAL_LIBM = 1 -USB_HIGHSPEED = 1 - # Number of USB endpoint pairs. USB_NUM_ENDPOINT_PAIRS = 8 # Align buffers on the cache boundary so we don't inadvertently load them early. diff --git a/ports/mimxrt10xx/reset.h b/ports/mimxrt10xx/reset.h index 04c951221e9e5..cae838556bce3 100644 --- a/ports/mimxrt10xx/reset.h +++ b/ports/mimxrt10xx/reset.h @@ -16,6 +16,6 @@ #define DBL_TAP_MAGIC 0xf01669ef // Randomly selected, adjusted to have first and last bit set #define DBL_TAP_MAGIC_QUICK_BOOT 0xf02669ef -void reset_to_bootloader(void) NORETURN; -void reset(void) NORETURN; +void reset_to_bootloader(void) MP_NORETURN; +void reset(void) MP_NORETURN; bool bootloader_available(void); diff --git a/ports/mimxrt10xx/supervisor/port.c b/ports/mimxrt10xx/supervisor/port.c index d7f7f280d1958..62d2569cfde4f 100644 --- a/ports/mimxrt10xx/supervisor/port.c +++ b/ports/mimxrt10xx/supervisor/port.c @@ -425,10 +425,6 @@ safe_mode_t port_init(void) { } void reset_port(void) { - #if CIRCUITPY_BUSIO - spi_reset(); - #endif - #if CIRCUITPY_AUDIOIO audio_dma_reset(); #endif @@ -450,8 +446,6 @@ void reset_port(void) { #endif // reset_event_system(); - - reset_all_pins(); } void reset_to_bootloader(void) { diff --git a/ports/nordic/Makefile b/ports/nordic/Makefile index aa614e097ba3c..1e283fc699c7f 100755 --- a/ports/nordic/Makefile +++ b/ports/nordic/Makefile @@ -36,8 +36,8 @@ ifeq ($(DEBUG), 1) CFLAGS += -ggdb3 OPTIMIZATION_FLAGS = -Og else - OPTIMIZATION_FLAGS ?= -O2 -fno-inline-functions - CFLAGS += -DNDEBUG -ggdb3 + OPTIMIZATION_FLAGS ?= -O2 + CFLAGS += -DNDEBUG endif ifeq ($(NRF_DEBUG_PRINT), 1) @@ -146,7 +146,9 @@ SRC_C += $(SRC_DCD) $(patsubst %.c,$(BUILD)/%.o,$(SRC_DCD)): CFLAGS += -Wno-missing-prototypes endif # CIRCUITPY_USB_DEVICE -SRC_S_UPPER = supervisor/shared/cpu_regs.S +SRC_S = shared/runtime/gchelper_thumb2.s + +SRC_C += shared/runtime/gchelper_native.c OBJ += $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_NRFX:.c=.o)) @@ -155,7 +157,7 @@ ifeq ($(INTERNAL_LIBM),1) OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) +OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) # nrfx uses undefined preprocessor variables quite casually, so we can't do diff --git a/ports/nordic/boards/clue_nrf52840_express/board.c b/ports/nordic/boards/clue_nrf52840_express/board.c index e2cd8a324bab7..0b331b63a9612 100644 --- a/ports/nordic/boards/clue_nrf52840_express/board.c +++ b/ports/nordic/boards/clue_nrf52840_express/board.c @@ -34,9 +34,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_P0_13, // TFT_DC Command or data - &pin_P0_12, // TFT_CS Chip select - &pin_P1_03, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_P0_13), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_P0_12), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_P1_03), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/nordic/boards/electronut_labs_blip/mpconfigboard.mk b/ports/nordic/boards/electronut_labs_blip/mpconfigboard.mk index 0e3b1b9048950..fc59a101e454f 100644 --- a/ports/nordic/boards/electronut_labs_blip/mpconfigboard.mk +++ b/ports/nordic/boards/electronut_labs_blip/mpconfigboard.mk @@ -11,3 +11,4 @@ INTERNAL_FLASH_FILESYSTEM = 1 CIRCUITPY_AUDIOIO = 0 CIRCUITPY_DISPLAYIO = 1 CIRCUITPY_STAGE = 1 +CIRCUITPY_DIGITALINOUT_PROTOCOL = 0 diff --git a/ports/nordic/boards/hiibot_bluefi/board.c b/ports/nordic/boards/hiibot_bluefi/board.c index 73430d675eb2d..7a681140ddbc4 100644 --- a/ports/nordic/boards/hiibot_bluefi/board.c +++ b/ports/nordic/boards/hiibot_bluefi/board.c @@ -34,9 +34,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_P0_27, // TFT_DC Command or data - &pin_P0_05, // TFT_CS Chip select - NULL, // no TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_P0_27), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_P0_05), // TFT_CS Chip select + MP_OBJ_NULL, // no TFT_RST Reset // &pin_P1_14, // TFT_RST Reset 60000000, // Baudrate 0, // Polarity diff --git a/ports/nordic/boards/makerdiary_nrf52840_m2_devkit/board.c b/ports/nordic/boards/makerdiary_nrf52840_m2_devkit/board.c index 64199b98009f0..4f9c051478ac3 100644 --- a/ports/nordic/boards/makerdiary_nrf52840_m2_devkit/board.c +++ b/ports/nordic/boards/makerdiary_nrf52840_m2_devkit/board.c @@ -35,9 +35,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_P0_08, // TFT_DC Command or data - &pin_P0_06, // TFT_CS Chip select - &pin_P1_09, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_P0_08), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_P0_06), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_P1_09), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/nordic/boards/ohs2020_badge/board.c b/ports/nordic/boards/ohs2020_badge/board.c index ea716006cb4bf..d16d99473f014 100644 --- a/ports/nordic/boards/ohs2020_badge/board.c +++ b/ports/nordic/boards/ohs2020_badge/board.c @@ -34,9 +34,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_P0_08, // TFT_DC Command or data - &pin_P0_14, // TFT_CS Chip select - &pin_P0_13, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_P0_08), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_P0_14), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_P0_13), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/nordic/common-hal/_bleio/Adapter.c b/ports/nordic/common-hal/_bleio/Adapter.c index 9cf40ae91b3ff..2ca9df897107e 100644 --- a/ports/nordic/common-hal/_bleio/Adapter.c +++ b/ports/nordic/common-hal/_bleio/Adapter.c @@ -32,9 +32,8 @@ #include "supervisor/usb.h" #endif -#if CIRCUITPY_OS_GETENV -#include "shared-bindings/os/__init__.h" -#include "shared-module/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif #define BLE_MIN_CONN_INTERVAL MSEC_TO_UNITS(15, UNIT_0_625_MS) @@ -324,11 +323,11 @@ static void bleio_adapter_reset_name(bleio_adapter_obj_t *self) { default_ble_name[len - 1] = nibble_to_hex_lower[addr.addr[0] & 0xf]; default_ble_name[len] = '\0'; // for now we add null for compatibility with C ASCIIZ strings - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML char ble_name[32]; - os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); - if (result == GETENV_OK) { + settings_err_t result = settings_get_str("CIRCUITPY_BLE_NAME", ble_name, sizeof(ble_name)); + if (result == SETTINGS_OK) { common_hal_bleio_adapter_set_name(self, ble_name); return; } diff --git a/ports/nordic/common-hal/_bleio/Attribute.h b/ports/nordic/common-hal/_bleio/Attribute.h index 5fa6b4d637767..9a58e16bb862e 100644 --- a/ports/nordic/common-hal/_bleio/Attribute.h +++ b/ports/nordic/common-hal/_bleio/Attribute.h @@ -6,6 +6,15 @@ #pragma once +#include + +#include "py/obj.h" + #include "shared-module/_bleio/Attribute.h" extern void bleio_attribute_gatts_set_security_mode(ble_gap_conn_sec_mode_t *perm, bleio_attribute_security_mode_t security_mode); + +size_t bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); +size_t bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); +void bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); diff --git a/ports/nordic/common-hal/_bleio/Characteristic.c b/ports/nordic/common-hal/_bleio/Characteristic.c index 1975c2c3d79ce..2e6042e48e7fb 100644 --- a/ports/nordic/common-hal/_bleio/Characteristic.c +++ b/ports/nordic/common-hal/_bleio/Characteristic.c @@ -80,7 +80,7 @@ void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, // to allocate. self->initial_value_len = initial_value_bufinfo->len; if (gc_alloc_possible()) { - if (gc_nbytes(initial_value_bufinfo->buf) > 0) { + if (gc_ptr_on_heap(initial_value_bufinfo->buf)) { uint8_t *initial_value = m_malloc_without_collect(self->initial_value_len); memcpy(initial_value, initial_value_bufinfo->buf, self->initial_value_len); self->initial_value = initial_value; @@ -138,10 +138,10 @@ size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *sel if (self->handle != BLE_GATT_HANDLE_INVALID) { uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); if (common_hal_bleio_service_get_is_remote(self->service)) { - return common_hal_bleio_gattc_read(self->handle, conn_handle, buf, len); + return bleio_gattc_read(self->handle, conn_handle, buf, len); } else { // conn_handle is ignored for non-system attributes. - return common_hal_bleio_gatts_read(self->handle, conn_handle, buf, len); + return bleio_gatts_read(self->handle, conn_handle, buf, len); } } @@ -159,7 +159,7 @@ void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, if (common_hal_bleio_service_get_is_remote(self->service)) { uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); // Last argument is true if write-no-reponse desired. - common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, + bleio_gattc_write(self->handle, conn_handle, bufinfo, (self->props & CHAR_PROP_WRITE_NO_RESPONSE)); } else { // Validate data length for local characteristics only. @@ -172,7 +172,7 @@ void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, // Always write the value locally even if no connections are active. // conn_handle is ignored for non-system attributes, so we use BLE_CONN_HANDLE_INVALID. - common_hal_bleio_gatts_write(self->handle, BLE_CONN_HANDLE_INVALID, bufinfo); + bleio_gatts_write(self->handle, BLE_CONN_HANDLE_INVALID, bufinfo); // Check to see if we need to notify or indicate any active connections. for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { bleio_connection_internal_t *connection = &bleio_connections[i]; @@ -255,7 +255,7 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, } const uint16_t conn_handle = bleio_connection_get_conn_handle(self->service->connection); - common_hal_bleio_check_connected(conn_handle); + bleio_check_connected(conn_handle); uint16_t cccd_value = (notify ? BLE_GATT_HVX_NOTIFICATION : 0) | diff --git a/ports/nordic/common-hal/_bleio/Connection.h b/ports/nordic/common-hal/_bleio/Connection.h index 110677dc78b41..ea1edf1760311 100644 --- a/ports/nordic/common-hal/_bleio/Connection.h +++ b/ports/nordic/common-hal/_bleio/Connection.h @@ -65,6 +65,7 @@ typedef struct { void bleio_connection_clear(bleio_connection_internal_t *self); bool connection_on_ble_evt(ble_evt_t *ble_evt, void *self_in); +void bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); bleio_connection_internal_t *bleio_conn_handle_to_connection(uint16_t conn_handle); diff --git a/ports/nordic/common-hal/_bleio/Descriptor.c b/ports/nordic/common-hal/_bleio/Descriptor.c index 959c8e5c9c0c0..0d27ca5dc2edf 100644 --- a/ports/nordic/common-hal/_bleio/Descriptor.c +++ b/ports/nordic/common-hal/_bleio/Descriptor.c @@ -43,9 +43,9 @@ size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8 if (self->handle != BLE_GATT_HANDLE_INVALID) { uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection); if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { - return common_hal_bleio_gattc_read(self->handle, conn_handle, buf, len); + return bleio_gattc_read(self->handle, conn_handle, buf, len); } else { - return common_hal_bleio_gatts_read(self->handle, conn_handle, buf, len); + return bleio_gatts_read(self->handle, conn_handle, buf, len); } } @@ -58,7 +58,7 @@ void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buff uint16_t conn_handle = bleio_connection_get_conn_handle(self->characteristic->service->connection); if (common_hal_bleio_service_get_is_remote(self->characteristic->service)) { // false means WRITE_REQ, not write-no-response - common_hal_bleio_gattc_write(self->handle, conn_handle, bufinfo, false); + bleio_gattc_write(self->handle, conn_handle, bufinfo, false); } else { // Validate data length for local descriptors only. if (self->fixed_length && bufinfo->len != self->max_length) { @@ -68,7 +68,7 @@ void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buff mp_raise_ValueError(MP_ERROR_TEXT("Value length > max_length")); } - common_hal_bleio_gatts_write(self->handle, conn_handle, bufinfo); + bleio_gatts_write(self->handle, conn_handle, bufinfo); } } diff --git a/ports/nordic/common-hal/_bleio/Service.c b/ports/nordic/common-hal/_bleio/Service.c index edd67a5fe739d..1bd75f8a48114 100644 --- a/ports/nordic/common-hal/_bleio/Service.c +++ b/ports/nordic/common-hal/_bleio/Service.c @@ -131,7 +131,7 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, BLE_GAP_CONN_SEC_MODE_SET_OPEN(&user_desc_md.read_perm); // If the description is on the Python heap, then have the SD copy it. If not, assume it's // static and will live for longer than the SD. - user_desc_md.vloc = gc_nbytes(user_description) > 0 ? BLE_GATTS_VLOC_STACK : BLE_GATTS_VLOC_USER; + user_desc_md.vloc = gc_ptr_on_heap(user_description) ? BLE_GATTS_VLOC_STACK : BLE_GATTS_VLOC_USER; char_md.p_user_desc_md = &user_desc_md; char_md.p_char_user_desc = (const uint8_t *)user_description; char_md.char_user_desc_max_size = strlen(user_description); diff --git a/ports/nordic/common-hal/_bleio/__init__.c b/ports/nordic/common-hal/_bleio/__init__.c index 454937dcd354a..9dc58d7687c90 100644 --- a/ports/nordic/common-hal/_bleio/__init__.c +++ b/ports/nordic/common-hal/_bleio/__init__.c @@ -112,14 +112,14 @@ void bleio_reset(void) { // It currently only has properties and no state. Inited by bleio_reset bleio_adapter_obj_t common_hal_bleio_adapter_obj; -void common_hal_bleio_check_connected(uint16_t conn_handle) { +void bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLE_CONN_HANDLE_INVALID) { mp_raise_ConnectionError(MP_ERROR_TEXT("Not connected")); } } // GATTS read of a Characteristic or Descriptor. -size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { +size_t bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { // conn_handle is ignored unless this is a system attribute. // If we're not connected, that's OK, because we can still read and write the local value. @@ -133,7 +133,7 @@ size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_ return gatts_value.len; } -void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) { +void bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo) { // conn_handle is ignored unless this is a system attribute. // If we're not connected, that's OK, because we can still read and write the local value. @@ -188,8 +188,8 @@ static bool _on_gattc_read_rsp_evt(ble_evt_t *ble_evt, void *param) { return true; } -size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { - common_hal_bleio_check_connected(conn_handle); +size_t bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len) { + bleio_check_connected(conn_handle); read_info_t read_info; read_info.buf = buf; @@ -213,15 +213,15 @@ size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_ RUN_BACKGROUND_TASKS; } // Test if we were disconnected while reading - common_hal_bleio_check_connected(read_info.conn_handle); + bleio_check_connected(read_info.conn_handle); ble_drv_remove_event_handler(_on_gattc_read_rsp_evt, &read_info); check_gatt_status(read_info.status); return read_info.final_len; } -void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) { - common_hal_bleio_check_connected(conn_handle); +void bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response) { + bleio_check_connected(conn_handle); ble_gattc_write_params_t write_params = { .write_op = write_no_response ? BLE_GATT_OP_WRITE_CMD: BLE_GATT_OP_WRITE_REQ, diff --git a/ports/nordic/common-hal/alarm/__init__.c b/ports/nordic/common-hal/alarm/__init__.c index 9753b0321fd06..026e117b9a67a 100644 --- a/ports/nordic/common-hal/alarm/__init__.c +++ b/ports/nordic/common-hal/alarm/__init__.c @@ -240,7 +240,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala #define PRESCALER_VALUE_IN_DEEP_SLEEP (1024) -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_time_timealarm_prepare_for_deep_sleep(); diff --git a/ports/nordic/common-hal/busio/I2C.c b/ports/nordic/common-hal/busio/I2C.c index 3558fad165ba6..999737f632bba 100644 --- a/ports/nordic/common-hal/busio/I2C.c +++ b/ports/nordic/common-hal/busio/I2C.c @@ -47,18 +47,18 @@ void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { never_reset_pin_number(self->sda_pin_number); } -static uint8_t twi_error_to_mp(const nrfx_err_t err) { +static mp_negative_errno_t twi_error_to_mp(const nrfx_err_t err) { switch (err) { case NRFX_ERROR_DRV_TWI_ERR_ANACK: - return MP_ENODEV; + return -MP_ENODEV; case NRFX_ERROR_BUSY: - return MP_EBUSY; + return -MP_EBUSY; case NRFX_ERROR_INVALID_ADDR: case NRFX_ERROR_DRV_TWI_ERR_DNACK: case NRFX_ERROR_DRV_TWI_ERR_OVERRUN: - return MP_EIO; + return -MP_EIO; case NRFX_ERROR_TIMEOUT: - return MP_ETIMEDOUT; + return -MP_ETIMEDOUT; default: break; } @@ -258,9 +258,9 @@ static nrfx_err_t _twim_xfer_with_timeout(busio_i2c_obj_t *self, nrfx_twim_xfer_ } } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool stopBit) { +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool stopBit) { if (len == 0) { - return common_hal_busio_i2c_probe(self, addr) ? 0 : MP_ENODEV; + return common_hal_busio_i2c_probe(self, addr) ? 0 : -MP_ENODEV; } nrfx_err_t err = NRFX_SUCCESS; @@ -286,11 +286,11 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, return twi_error_to_mp(err); } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { if (len == 0) { return 0; } @@ -317,9 +317,9 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t return twi_error_to_mp(err); } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/nordic/common-hal/busio/SPI.c b/ports/nordic/common-hal/busio/SPI.c index 8af4c5f4e83f1..de54dd08a2783 100644 --- a/ports/nordic/common-hal/busio/SPI.c +++ b/ports/nordic/common-hal/busio/SPI.c @@ -59,26 +59,13 @@ static const spim_peripheral_t spim_peripherals[] = { #endif }; -static bool never_reset[MP_ARRAY_SIZE(spim_peripherals)]; - // Separate RAM area for SPIM3 transmit buffer to avoid SPIM3 hardware errata. // https://infocenter.nordicsemi.com/index.jsp?topic=%2Ferrata_nRF52840_Rev2%2FERR%2FnRF52840%2FRev2%2Flatest%2Fanomaly_840_198.html static uint8_t *spim3_transmit_buffer = (uint8_t *)SPIM3_BUFFER_RAM_START_ADDR; -void spi_reset(void) { - for (size_t i = 0; i < MP_ARRAY_SIZE(spim_peripherals); i++) { - if (never_reset[i]) { - continue; - } - nrfx_spim_uninit(&spim_peripherals[i].spim); - } -} - void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { for (size_t i = 0; i < MP_ARRAY_SIZE(spim_peripherals); i++) { if (self->spim_peripheral == &spim_peripherals[i]) { - never_reset[i] = true; - never_reset_pin_number(self->clock_pin_number); never_reset_pin_number(self->MOSI_pin_number); never_reset_pin_number(self->MISO_pin_number); @@ -125,6 +112,9 @@ static nrf_spim_frequency_t baudrate_to_spim_frequency(const uint32_t baudrate) void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + if (half_duplex) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_half_duplex); } @@ -178,6 +168,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock_pin_number == NO_PIN; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock_pin_number = NO_PIN; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; @@ -188,6 +182,8 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { reset_pin_number(self->clock_pin_number); reset_pin_number(self->MOSI_pin_number); reset_pin_number(self->MISO_pin_number); + + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { diff --git a/ports/nordic/common-hal/busio/SPI.h b/ports/nordic/common-hal/busio/SPI.h index 7bfddd9625dcb..3260c5c27dee2 100644 --- a/ports/nordic/common-hal/busio/SPI.h +++ b/ports/nordic/common-hal/busio/SPI.h @@ -23,5 +23,3 @@ typedef struct { uint8_t MOSI_pin_number; uint8_t MISO_pin_number; } busio_spi_obj_t; - -void spi_reset(void); diff --git a/ports/nordic/common-hal/busio/UART.c b/ports/nordic/common-hal/busio/UART.c index 0dfe6ae3f5de2..6939486a5e673 100644 --- a/ports/nordic/common-hal/busio/UART.c +++ b/ports/nordic/common-hal/busio/UART.c @@ -122,7 +122,7 @@ void uart_reset(void) { void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { // Don't never reset objects on the heap. - if (gc_alloc_possible() && gc_nbytes(self) > 0) { + if (gc_alloc_possible() && gc_ptr_on_heap(self)) { return; } for (size_t i = 0; i < MP_ARRAY_SIZE(nrfx_uartes); i++) { @@ -346,7 +346,7 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, RUN_BACKGROUND_TASKS; } - if (!nrfx_is_in_ram(data) && gc_alloc_possible() && gc_nbytes(tx_buf) > 0) { + if (!nrfx_is_in_ram(data) && gc_alloc_possible() && gc_ptr_on_heap(tx_buf)) { gc_free(tx_buf); } diff --git a/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c b/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c index b0617fae7e566..2e8b308ff64d9 100644 --- a/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/nordic/common-hal/rotaryio/IncrementalEncoder.c @@ -32,6 +32,9 @@ static void _intr_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) { void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + self->pin_a = pin_a->number; self->pin_b = pin_b->number; @@ -78,6 +81,10 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o nrfx_gpiote_in_uninit(self->pin_b); reset_pin_number(self->pin_a); reset_pin_number(self->pin_b); + + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { self->pin_a = NO_PIN; - self->pin_b = NO_PIN; } diff --git a/ports/nordic/mpconfigport.mk b/ports/nordic/mpconfigport.mk index 502e71ae4ae16..48536ec2acd7b 100644 --- a/ports/nordic/mpconfigport.mk +++ b/ports/nordic/mpconfigport.mk @@ -63,6 +63,8 @@ CIRCUITPY_MEMORYMAP ?= 1 CIRCUITPY_RGBMATRIX ?= 1 CIRCUITPY_FRAMEBUFFERIO ?= 1 +CIRCUITPY_HASHLIB ?= 1 + CIRCUITPY_COUNTIO ?= 1 CIRCUITPY_WATCHDOG ?= 1 diff --git a/ports/nordic/supervisor/port.c b/ports/nordic/supervisor/port.c index ed371c6ad8582..1eabfcbe2166b 100644 --- a/ports/nordic/supervisor/port.c +++ b/ports/nordic/supervisor/port.c @@ -189,7 +189,6 @@ safe_mode_t port_init(void) { void reset_port(void) { #if CIRCUITPY_BUSIO - spi_reset(); uart_reset(); #endif @@ -216,8 +215,6 @@ void reset_port(void) { nrfx_gpiote_uninit(); } nrfx_gpiote_init(NRFX_GPIOTE_CONFIG_IRQ_PRIORITY); - - reset_all_pins(); } void reset_to_bootloader(void) { diff --git a/ports/raspberrypi/Makefile b/ports/raspberrypi/Makefile index ba2db8a750825..cef2806c87497 100644 --- a/ports/raspberrypi/Makefile +++ b/ports/raspberrypi/Makefile @@ -25,7 +25,8 @@ INC_CYW43 := \ CFLAGS_CYW43 := \ -DCYW43_LWIP=1 \ -DPICO_CYW43_ARCH_THREADSAFE_BACKGROUND=1 \ - -DCYW43_USE_SPI \ + -DCYW43_USE_SPI=1 \ + -DUSE_SDIOIT=0 \ -DIGNORE_GPIO25 \ -DIGNORE_GPIO23 \ -DIGNORE_GPIO24 \ @@ -125,6 +126,7 @@ INC += \ -isystem sdk/src/rp2_common/hardware_powman/include/ \ -isystem sdk/src/rp2_common/hardware_pwm/include/ \ -isystem sdk/src/rp2_common/hardware_resets/include/ \ + -isystem sdk/src/rp2_common/hardware_rcp/include/ \ -isystem sdk/src/rp2_common/hardware_rtc/include/ \ -isystem sdk/src/rp2_common/hardware_spi/include/ \ -isystem sdk/src/rp2_common/hardware_sync/include/ \ @@ -149,6 +151,7 @@ INC += \ -isystem sdk/src/rp2_common/pico_float/include/ \ -isystem sdk/src/rp2_common/pico_runtime/include/ \ -isystem sdk/src/rp2_common/pico_runtime_init/include/ \ + -isystem sdk/src/rp2_common/pico_platform_common/include/ \ -isystem sdk/src/rp2_common/pico_platform_compiler/include/ \ -isystem sdk/src/rp2_common/pico_platform_sections/include/ \ -isystem sdk/src/rp2_common/pico_platform_panic/include/ \ @@ -393,6 +396,8 @@ OTHER_PICO_FLAGS := \ -Wl,--wrap=__aeabi_uidivmod \ -Wl,--wrap=__aeabi_uldivmod +SRC_S = shared/runtime/gchelper_thumb1.s + ifeq ($(CHIP_VARIANT),RP2040) CFLAGS += \ -march=armv6-m \ @@ -431,12 +436,12 @@ UF2_ID = 0xE48BFF56 DOUBLE_EABI = rp2040 endif ifeq ($(CHIP_VARIANT),RP2350) -CFLAGS += \ - -march=armv8-m.main+fp+dsp \ +AFLAGS = -march=armv8-m.main+fp+dsp \ -mthumb \ - -mabi=aapcs-linux \ -mcpu=cortex-m33 \ -mfloat-abi=softfp +CFLAGS += $(AFLAGS) \ + -mabi=aapcs-linux # ARM Secure family id UF2_ID = 0xe48bff59 @@ -470,6 +475,7 @@ endif endif +SRC_C += shared/runtime/gchelper_native.c SRC_SDK := \ src/common/hardware_claim/claim.c \ @@ -669,7 +675,6 @@ SRC_S_UPPER = sdk/src/rp2_common/hardware_irq/irq_handler_chain.S \ sdk/src/rp2_common/pico_double/double_aeabi_$(DOUBLE_EABI).S \ sdk/src/rp2_common/pico_int64_ops/pico_int64_ops_aeabi.S \ sdk/src/rp2_common/pico_crt0/crt0.S \ - supervisor/shared/cpu_regs.S \ $(SRC_S_UPPER_CHIP_VARIANT) ifeq ($(CIRCUITPY_PICODVI),1) diff --git a/ports/raspberrypi/bindings/rp2pio/StateMachine.h b/ports/raspberrypi/bindings/rp2pio/StateMachine.h index afdffd1eccd3b..16f884bcfca32 100644 --- a/ports/raspberrypi/bindings/rp2pio/StateMachine.h +++ b/ports/raspberrypi/bindings/rp2pio/StateMachine.h @@ -42,6 +42,7 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self); bool common_hal_rp2pio_statemachine_deinited(rp2pio_statemachine_obj_t *self); +void common_hal_rp2pio_statemachine_mark_deinit(rp2pio_statemachine_obj_t *self); void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self); diff --git a/ports/raspberrypi/boards/adafruit_floppsy_rp2040/board.c b/ports/raspberrypi/boards/adafruit_floppsy_rp2040/board.c index 5056a4d9c7a15..882bc9a1a9e0b 100644 --- a/ports/raspberrypi/boards/adafruit_floppsy_rp2040/board.c +++ b/ports/raspberrypi/boards/adafruit_floppsy_rp2040/board.c @@ -34,9 +34,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - CIRCUITPY_BOARD_TFT_DC, - CIRCUITPY_BOARD_TFT_CS, - NULL, // TFT_RESET Reset + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_DC), + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_CS), + MP_OBJ_NULL, // TFT_RESET Reset 40000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c b/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c index 673d9303d6b55..ec37b398e229f 100644 --- a/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c +++ b/ports/raspberrypi/boards/adafruit_macropad_rp2040/board.c @@ -29,7 +29,7 @@ uint8_t display_init_sequence[] = { 0xda, 1, 0x12, // com pins 0x81, 1, 0xff, // contrast 255 0xd9, 1, 0x1f, // pre/dis-charge 2DCLKs/2CLKs - 0xdb, 1, 0x20, // VCOM deslect 0.770 + 0xdb, 1, 0x20, // VCOM select 0.770 0x20, 1, 0x20, 0x33, 0, // VPP 9V 0xa6, 0, // not inverted @@ -46,9 +46,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO24, // Command or data - &pin_GPIO22, // Chip select - &pin_GPIO23, // Reset + MP_OBJ_FROM_PTR(&pin_GPIO24), // Command or data + MP_OBJ_FROM_PTR(&pin_GPIO22), // Chip select + MP_OBJ_FROM_PTR(&pin_GPIO23), // Reset 10000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/adafruit_tinychad_rp2350/board.c b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/board.c new file mode 100644 index 0000000000000..fddd2572c1fcd --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/board.c @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "supervisor/board.h" + +#include "common-hal/picodvi/__init__.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. + +void board_init(void) { + picodvi_autoconstruct(); +} diff --git a/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.h b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.h new file mode 100644 index 0000000000000..958ab01a0b6a8 --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.h @@ -0,0 +1,20 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#define MICROPY_HW_BOARD_NAME "Adafruit Tinychad RP2350" +#define MICROPY_HW_MCU_NAME "rp2350a" + +#define MICROPY_HW_NEOPIXEL (&pin_GPIO17) + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO19) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO18) + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO10) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO11) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO12) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO1) +#define DEFAULT_UART_BUS_TX (&pin_GPIO0) diff --git a/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.mk b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.mk new file mode 100644 index 0000000000000..8d2a9c2f5273f --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/mpconfigboard.mk @@ -0,0 +1,10 @@ +USB_VID = 0x239A +USB_PID = 0x8170 +USB_PRODUCT = "Tinychad RP2350" +USB_MANUFACTURER = "Adafruit" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "GD25Q64C,W25Q64JVxQ" diff --git a/ports/raspberrypi/boards/adafruit_tinychad_rp2350/pico-sdk-configboard.h b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/pico-sdk-configboard.h new file mode 100644 index 0000000000000..2d9283a9192f2 --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/pico-sdk-configboard.h @@ -0,0 +1,10 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Put board-specific pico-sdk definitions here. This file must exist. + +// Allow extra time for xosc to start. +#define PICO_XOSC_STARTUP_DELAY_MULTIPLIER 64 diff --git a/ports/raspberrypi/boards/adafruit_tinychad_rp2350/pins.c b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/pins.c new file mode 100644 index 0000000000000..0d769fd4d565d --- /dev/null +++ b/ports/raspberrypi/boards/adafruit_tinychad_rp2350/pins.c @@ -0,0 +1,54 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, + + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + + { MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_D21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_D22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_D23), MP_ROM_PTR(&pin_GPIO23) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_CS), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO17) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c index 19e18bd544bee..f091c4b05a8ad 100644 --- a/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c +++ b/ports/raspberrypi/boards/bradanlanestudio_explorer_rp2040/board.c @@ -212,9 +212,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO11, // DEFAULT_SPI_BUS_DC, // EPD_DC Command or data - &pin_GPIO13, // DEFAULT_SPI_BUS_CS, // EPD_CS Chip select - &pin_GPIO10, // DEFAULT_SPI_BUS_RESET, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO11), // DEFAULT_SPI_BUS_DC, // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO13), // DEFAULT_SPI_BUS_CS, // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO10), // DEFAULT_SPI_BUS_RESET, // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase @@ -251,73 +251,62 @@ void board_init(void) { if ((vid_setting == 1) || // DCNextGen SSD1681 BWR rotated 270 (vid_setting == 3) || // Explorer SSD1681 BW rotated 0 (vid_setting == 4)) { // Explorer SSD1681 BWR rotated 0 - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - _start_sequence_ssd1681, sizeof(_start_sequence_ssd1681), - 1.0, // start up time - _stop_sequence_ssd1681, sizeof(_stop_sequence_ssd1681), - WIDTH, // width - HEIGHT, // height - WIDTH, // ram_width - HEIGHT + 0x60, // ram_height RAM is actually only 200 bits high but we use 296 to match the 9 bits - 0, // colstart - 0, // rowstart - rotation, // rotation - SSD_SET_RAMXPOS, // set_column_window_command - SSD_SET_RAMYPOS, // set_row_window_command - SSD_SET_RAMXCOUNT, // set_current_column_command - SSD_SET_RAMYCOUNT, // set_current_row_command - SSD_WRITE_RAM_BLK, // write_black_ram_command - false, // black_bits_inverted - SSD_WRITE_RAM_RED, // write_color_ram_command - false, // color_bits_inverted - 0xFF0000, // highlight_color (RED for tri-color display) - _refresh_sequence_ssd1681, sizeof(_refresh_sequence_ssd1681), // refresh_display_command - refresh_time, // refresh_time - &pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin - true, // busy_state - seconds_per_frame, // seconds_per_frame (does not seem the user can change this) - true, // always_toggle_chip_select - false, // not grayscale - false, // not acep - false, // not spectra6 - false, // not two_byte_sequence_length - true); // address_little_endian + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = _start_sequence_ssd1681; + args.start_sequence_len = sizeof(_start_sequence_ssd1681); + args.start_up_time = 1.0; + args.stop_sequence = _stop_sequence_ssd1681; + args.stop_sequence_len = sizeof(_stop_sequence_ssd1681); + args.width = WIDTH; + args.height = HEIGHT; + args.ram_width = WIDTH; + args.ram_height = HEIGHT + 0x60; + args.rotation = rotation; + args.set_column_window_command = SSD_SET_RAMXPOS; + args.set_row_window_command = SSD_SET_RAMYPOS; + args.set_current_column_command = SSD_SET_RAMXCOUNT; + args.set_current_row_command = SSD_SET_RAMYCOUNT; + args.write_black_ram_command = SSD_WRITE_RAM_BLK; + args.write_color_ram_command = SSD_WRITE_RAM_RED; + args.highlight_color = 0xFF0000; + args.refresh_sequence = _refresh_sequence_ssd1681; + args.refresh_sequence_len = sizeof(_refresh_sequence_ssd1681); + args.refresh_time = refresh_time; + args.busy_pin = &pin_GPIO9; + args.busy_state = true; + args.seconds_per_frame = seconds_per_frame; + args.always_toggle_chip_select = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } else if (vid_setting == 2) { // Explorer SSD1608 BW - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - _start_sequence_ssd1608, sizeof(_start_sequence_ssd1608), - 1.0, // start up time - _stop_sequence_ssd1608, sizeof(_stop_sequence_ssd1608), - WIDTH, // width - HEIGHT, // height - WIDTH, // ram_width - HEIGHT /* + 0x60 */, // ram_height RAM is actually only 200 bits high but we use 296 to match the 9 bits - 0, // colstart - 0, // rowstart - rotation, // rotation - SSD_SET_RAMXPOS, // set_column_window_command - SSD_SET_RAMYPOS, // set_row_window_command - SSD_SET_RAMXCOUNT, // set_current_column_command - SSD_SET_RAMYCOUNT, // set_current_row_command - SSD_WRITE_RAM_BLK, // write_black_ram_command - false, // black_bits_inverted - NO_COMMAND, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color (RED for tri-color display) - _refresh_sequence_ssd1608, sizeof(_refresh_sequence_ssd1608), // refresh_display_command - refresh_time, // refresh_time - &pin_GPIO9, // DEFAULT_SPI_BUS_BUSY, // busy_pin - true, // busy_state - seconds_per_frame, // seconds_per_frame (does not seem the user can change this) - true, // always_toggle_chip_select - false, // not grayscale - false, // not acep - false, // not spectra6 - false, // not two_byte_sequence_length - true); // address_little_endian + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = _start_sequence_ssd1608; + args.start_sequence_len = sizeof(_start_sequence_ssd1608); + args.start_up_time = 1.0; + args.stop_sequence = _stop_sequence_ssd1608; + args.stop_sequence_len = sizeof(_stop_sequence_ssd1608); + args.width = WIDTH; + args.height = HEIGHT; + args.ram_width = WIDTH; + args.ram_height = HEIGHT; + args.rotation = rotation; + args.set_column_window_command = SSD_SET_RAMXPOS; + args.set_row_window_command = SSD_SET_RAMYPOS; + args.set_current_column_command = SSD_SET_RAMXCOUNT; + args.set_current_row_command = SSD_SET_RAMYCOUNT; + args.write_black_ram_command = SSD_WRITE_RAM_BLK; + args.write_color_ram_command = NO_COMMAND; + args.refresh_sequence = _refresh_sequence_ssd1608; + args.refresh_sequence_len = sizeof(_refresh_sequence_ssd1608); + args.refresh_time = refresh_time; + args.busy_pin = &pin_GPIO9; + args.busy_state = true; + args.seconds_per_frame = seconds_per_frame; + args.always_toggle_chip_select = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } else { // what should happen if this firmware is installed on some other board? // currently, we mark the display as None diff --git a/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/board.c b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/board.c new file mode 100644 index 0000000000000..e6a868ab21226 --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/link.ld b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/link.ld new file mode 100644 index 0000000000000..e814bead4c51e --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/link.ld @@ -0,0 +1 @@ +firmware_size = 1532k; diff --git a/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.h b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.h new file mode 100644 index 0000000000000..8eab7e5a52557 --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.h @@ -0,0 +1,21 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Cytron EDU PICO 2" +#define MICROPY_HW_MCU_NAME "rp2350a" + +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1) + +#define MICROPY_HW_LED_STATUS (&pin_CYW0) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}} + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO18, .mosi = &pin_GPIO19, .miso = &pin_GPIO16}} diff --git a/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.mk b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.mk new file mode 100644 index 0000000000000..ac0bb6d6f2736 --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/mpconfigboard.mk @@ -0,0 +1,54 @@ +USB_VID = 0x2E8A +USB_PID = 0x107E +USB_PRODUCT = "Cytron EDU PICO 2 for Pico 2W" +USB_MANUFACTURER = "Cytron" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" + +CIRCUITPY_SDCARDIO = 1 + +CIRCUITPY__EVE = 1 + +CIRCUITPY_CYW43 = 1 +CIRCUITPY_SSL = 1 +CIRCUITPY_HASHLIB = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_MDNS = 1 +CIRCUITPY_SOCKETPOOL = 1 +CIRCUITPY_WIFI = 1 + +CFLAGS += \ + -DCYW43_PIN_WL_DYNAMIC=0 \ + -DCYW43_DEFAULT_PIN_WL_HOST_WAKE=24 \ + -DCYW43_DEFAULT_PIN_WL_REG_ON=23 \ + -DCYW43_DEFAULT_PIN_WL_CLOCK=29 \ + -DCYW43_DEFAULT_PIN_WL_DATA_IN=24 \ + -DCYW43_DEFAULT_PIN_WL_DATA_OUT=24 \ + -DCYW43_DEFAULT_PIN_WL_CS=25 \ + -DCYW43_WL_GPIO_COUNT=3 \ + -DCYW43_WL_GPIO_LED_PIN=0 + -DCYW43_PIO_CLOCK_DIV_INT=3 + +# The default is -O3. +OPTIMIZATION_FLAGS = -Os + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Motor +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SimpleIO +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_framebuf +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_OPT4048 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ImageLoad +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_AHTx0 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SD +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/CircuitPython_edupico2_paj7620 + +CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' diff --git a/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pico-sdk-configboard.h b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pico-sdk-configboard.h new file mode 100644 index 0000000000000..110195b779498 --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pico-sdk-configboard.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pins.c b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pins.c new file mode 100644 index 0000000000000..83b93e7268596 --- /dev/null +++ b/ports/raspberrypi/boards/cytron_edu_v2_pico_2w/pins.c @@ -0,0 +1,86 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + + // Motor Controls + { MP_ROM_QSTR(MP_QSTR_M1A), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_M1B), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_M2A), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_M2B), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_LOG_SWITCH), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + + // SPI Pins for SD Card + { MP_ROM_QSTR(MP_QSTR_SD_MISO), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_SD_CS), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SD_SCK), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SD_MOSI), MP_ROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_USB_RELAY), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_SMPS_MODE), MP_ROM_PTR(&pin_CYW1) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_CYW0) }, + + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_CYW2) }, + + { MP_ROM_QSTR(MP_QSTR_GP26_A0), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, + + { MP_ROM_QSTR(MP_QSTR_GP27_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + + { MP_ROM_QSTR(MP_QSTR_USB_HOST_DATA_PLUS), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_USB_HOST_DATA_MINUS), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_USB_HOST_POWER), MP_ROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_GP28_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SD_SPI), MP_ROM_PTR(&board_spi_obj) }, + + + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/cytron_maker_pi_rp2040/pins.c b/ports/raspberrypi/boards/cytron_maker_pi_rp2040/pins.c index 53fae76ac1b4f..dbfc713930b6e 100644 --- a/ports/raspberrypi/boards/cytron_maker_pi_rp2040/pins.c +++ b/ports/raspberrypi/boards/cytron_maker_pi_rp2040/pins.c @@ -62,6 +62,8 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP29_A3), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_VBATT), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, diff --git a/ports/raspberrypi/boards/hack_club_sprig/board.c b/ports/raspberrypi/boards/hack_club_sprig/board.c index 89b1b3b75b7bf..94c5a7c5be9ec 100644 --- a/ports/raspberrypi/boards/hack_club_sprig/board.c +++ b/ports/raspberrypi/boards/hack_club_sprig/board.c @@ -10,7 +10,7 @@ #include "shared-bindings/fourwire/FourWire.h" #include "shared-module/displayio/__init__.h" #include "shared-module/displayio/mipi_constants.h" -#include "supervisor/shared/board.h" +#include "shared-bindings/board/__init__.h" // display init sequence from CircuitPython library https://github.com/adafruit/Adafruit_CircuitPython_ST7735R/blob/dfae353330cf051d1f31db9e4b681c8d70900cc5/adafruit_st7735r.py @@ -57,17 +57,14 @@ uint8_t display_init_sequence[] = { void board_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; - busio_spi_obj_t *spi = &bus->inline_bus; - common_hal_busio_spi_construct(spi, &pin_GPIO18, &pin_GPIO19, &pin_GPIO16, false); - common_hal_busio_spi_never_reset(spi); - bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO22, // DC - &pin_GPIO20, // CS - &pin_GPIO26, // RST + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_DC), + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_CS), + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_TFT_RESET), 30000000, 0, 0); @@ -92,7 +89,7 @@ void board_init(void) { MIPI_COMMAND_WRITE_MEMORY_START, // Write memory command display_init_sequence, sizeof(display_init_sequence), - &pin_GPIO17, // backlight pin + CIRCUITPY_BOARD_TFT_BACKLIGHT, NO_BRIGHTNESS_COMMAND, 1.0f, // brightness false, // single_byte_bounds @@ -104,4 +101,8 @@ void board_init(void) { 50000); // backlight pwm frequency } +void board_deinit(void) { + common_hal_displayio_release_displays(); +} + // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/hack_club_sprig/mpconfigboard.h b/ports/raspberrypi/boards/hack_club_sprig/mpconfigboard.h index 041aca6d45eb7..bfc0469a71178 100644 --- a/ports/raspberrypi/boards/hack_club_sprig/mpconfigboard.h +++ b/ports/raspberrypi/boards/hack_club_sprig/mpconfigboard.h @@ -10,3 +10,11 @@ #define MICROPY_HW_MCU_NAME "rp2040" #define MICROPY_HW_LED_STATUS (&pin_GPIO4) + +#define CIRCUITPY_BOARD_TFT_DC (&pin_GPIO22) +#define CIRCUITPY_BOARD_TFT_CS (&pin_GPIO20) +#define CIRCUITPY_BOARD_TFT_RESET (&pin_GPIO26) +#define CIRCUITPY_BOARD_TFT_BACKLIGHT (&pin_GPIO17) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO18, .mosi = &pin_GPIO19, .miso = &pin_GPIO16 }} diff --git a/ports/raspberrypi/boards/hack_club_sprig/pins.c b/ports/raspberrypi/boards/hack_club_sprig/pins.c index e5ce147839890..7802ef5466d1d 100644 --- a/ports/raspberrypi/boards/hack_club_sprig/pins.c +++ b/ports/raspberrypi/boards/hack_club_sprig/pins.c @@ -60,7 +60,6 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, - // Start Sprig-specific definitions { MP_ROM_QSTR(MP_QSTR_BLUE_LED), MP_ROM_PTR(&pin_GPIO4) }, @@ -79,18 +78,23 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_BUTTON_K), MP_ROM_PTR(&pin_GPIO14) }, { MP_ROM_QSTR(MP_QSTR_BUTTON_L), MP_ROM_PTR(&pin_GPIO15) }, - { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO16) }, - { MP_ROM_QSTR(MP_QSTR_TFT_LITE), MP_ROM_PTR(&pin_GPIO17) }, { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO18) }, { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO19) }, - { MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_CARD_CS), MP_ROM_PTR(&pin_GPIO21) }, - { MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_GPIO22) }, - { MP_ROM_QSTR(MP_QSTR_TFT_RESET), MP_ROM_PTR(&pin_GPIO23) }, + + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_DC) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_CS) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_RESET), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_RESET) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_TFT_BACKLIGHT), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_BACKLIGHT) }, + { MP_ROM_QSTR(MP_QSTR_TFT_LITE), MP_ROM_PTR(CIRCUITPY_BOARD_TFT_BACKLIGHT) }, + { MP_ROM_QSTR(MP_QSTR_WHITE_LED), MP_ROM_PTR(&pin_GPIO28) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/heiafr_picomo_v2/board.c b/ports/raspberrypi/boards/heiafr_picomo_v2/board.c index 8c60341489fa1..3824c5959f5ee 100644 --- a/ports/raspberrypi/boards/heiafr_picomo_v2/board.c +++ b/ports/raspberrypi/boards/heiafr_picomo_v2/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO16, // TFT_DC Command or data - &pin_GPIO17, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO16), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 62500000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/heiafr_picomo_v3/board.c b/ports/raspberrypi/boards/heiafr_picomo_v3/board.c index 8c60341489fa1..3824c5959f5ee 100644 --- a/ports/raspberrypi/boards/heiafr_picomo_v3/board.c +++ b/ports/raspberrypi/boards/heiafr_picomo_v3/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO16, // TFT_DC Command or data - &pin_GPIO17, // TFT_CS Chip select - NULL, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO16), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // TFT_CS Chip select + MP_OBJ_NULL, // TFT_RST Reset 62500000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c b/ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c index 78cc912130e79..67e63daed1d67 100644 --- a/ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c +++ b/ports/raspberrypi/boards/lilygo_t_display_rp2040/board.c @@ -63,9 +63,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO1, // DC - &pin_GPIO5, // CS - NULL, // RST (Reset pin tie to 0, do not set here) + MP_OBJ_FROM_PTR(&pin_GPIO1), // DC + MP_OBJ_FROM_PTR(&pin_GPIO5), // CS + MP_OBJ_NULL, // RST (Reset pin tie to 0, do not set here) 40000000, // baudrate 1, // polarity 0 // phase diff --git a/ports/raspberrypi/boards/mtm_computer/board.c b/ports/raspberrypi/boards/mtm_computer/board.c index e6a868ab21226..4fec0cd5b8b59 100644 --- a/ports/raspberrypi/boards/mtm_computer/board.c +++ b/ports/raspberrypi/boards/mtm_computer/board.c @@ -5,5 +5,36 @@ // SPDX-License-Identifier: MIT #include "supervisor/board.h" +#include "shared-bindings/mcp4822/MCP4822.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "peripherals/pins.h" +#include "py/runtime.h" +// board.DAC() — factory function that constructs an mcp4822.MCP4822 with +// the MTM Workshop Computer's DAC pins (GP18=SCK, GP19=SDI, GP21=CS). + +static mp_obj_t board_dac_singleton = MP_OBJ_NULL; + +static mp_obj_t board_dac_factory(void) { + if (board_dac_singleton == MP_OBJ_NULL) { + mcp4822_mcp4822_obj_t *dac = mp_obj_malloc_with_finaliser( + mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); + common_hal_mcp4822_mcp4822_construct( + dac, + &pin_GPIO18, // clock (SCK) + &pin_GPIO19, // mosi (SDI) + &pin_GPIO21, // cs + 1); // gain 1x + board_dac_singleton = MP_OBJ_FROM_PTR(dac); + } + return board_dac_singleton; +} +MP_DEFINE_CONST_FUN_OBJ_0(board_dac_obj, board_dac_factory); + +void board_deinit(void) { + if (board_dac_singleton != MP_OBJ_NULL) { + common_hal_mcp4822_mcp4822_deinit(MP_OBJ_TO_PTR(board_dac_singleton)); + board_dac_singleton = MP_OBJ_NULL; + } +} // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk index 718c393d1686f..45c99711d72a3 100644 --- a/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk +++ b/ports/raspberrypi/boards/mtm_computer/mpconfigboard.mk @@ -11,3 +11,4 @@ EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" CIRCUITPY_AUDIOEFFECTS = 1 CIRCUITPY_IMAGECAPTURE = 0 CIRCUITPY_PICODVI = 0 +CIRCUITPY_MCP4822 = 1 diff --git a/ports/raspberrypi/boards/mtm_computer/pins.c b/ports/raspberrypi/boards/mtm_computer/pins.c index 6987818233102..ffdf2687702c6 100644 --- a/ports/raspberrypi/boards/mtm_computer/pins.c +++ b/ports/raspberrypi/boards/mtm_computer/pins.c @@ -6,6 +6,8 @@ #include "shared-bindings/board/__init__.h" +extern const mp_obj_fun_builtin_fixed_t board_dac_obj; + static const mp_rom_map_elem_t board_module_globals_table[] = { CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS @@ -21,7 +23,6 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_PULSE_2_IN), MP_ROM_PTR(&pin_GPIO3) }, { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, - { MP_ROM_QSTR(MP_QSTR_NORM_PROBE), MP_ROM_PTR(&pin_GPIO4) }, { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, @@ -105,6 +106,9 @@ static const mp_rom_map_elem_t board_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, { MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) }, + // Factory function: dac = board.DAC() returns a configured mcp4822.MCP4822 + { MP_ROM_QSTR(MP_QSTR_DAC), MP_ROM_PTR(&board_dac_obj) }, + // { MP_ROM_QSTR(MP_QSTR_EEPROM_I2C), MP_ROM_PTR(&board_i2c_obj) }, }; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/pajenicko_picopad/board.c b/ports/raspberrypi/boards/pajenicko_picopad/board.c index 0ca13b3ec2e1b..d8f9392262383 100644 --- a/ports/raspberrypi/boards/pajenicko_picopad/board.c +++ b/ports/raspberrypi/boards/pajenicko_picopad/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO17, // TFT_DC Command or data - &pin_GPIO21, // TFT_CS Chip select - &pin_GPIO20, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO17), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO21), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO20), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/pimoroni_badger2040/board.c b/ports/raspberrypi/boards/pimoroni_badger2040/board.c index 5950d11da8a11..80eddb49e1330 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2040/board.c +++ b/ports/raspberrypi/boards/pimoroni_badger2040/board.c @@ -263,9 +263,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO20, // EPD_DC Command or data - &pin_GPIO17, // EPD_CS Chip select - &pin_GPIO21, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO20), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO21), // EPD_RST Reset 1200000, // Baudrate 0, // Polarity 0); // Phase @@ -273,39 +273,27 @@ void board_init(void) { // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 296, // width - 128, // height - 160, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - DTM2, // write_black_ram_command - true, // black_bits_inverted - DTM1, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO26, // busy_pin - false, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 160; + args.ram_height = 296; + args.rotation = 270; + args.write_black_ram_command = DTM2; + args.black_bits_inverted = true; + args.write_color_ram_command = DTM1; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO26; + args.seconds_per_frame = 2.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/raspberrypi/boards/pimoroni_badger2040w/board.c b/ports/raspberrypi/boards/pimoroni_badger2040w/board.c index 7dc11af01eca3..ef5da7ceabcb7 100644 --- a/ports/raspberrypi/boards/pimoroni_badger2040w/board.c +++ b/ports/raspberrypi/boards/pimoroni_badger2040w/board.c @@ -263,9 +263,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO20, // EPD_DC Command or data - &pin_GPIO17, // EPD_CS Chip select - &pin_GPIO21, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO20), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO21), // EPD_RST Reset 1200000, // Baudrate 0, // Polarity 0); // Phase @@ -273,39 +273,27 @@ void board_init(void) { // Set up the DisplayIO epaper object epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 296, // width - 128, // height - 160, // ram_width - 296, // ram_height - 0, // colstart - 0, // rowstart - 270, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - DTM2, // write_black_ram_command - true, // black_bits_inverted - DTM1, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - refresh_sequence, sizeof(refresh_sequence), // refresh_display_command - 1.0, // refresh_time - &pin_GPIO26, // busy_pin - false, // busy_state - 2.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - false, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 296; + args.height = 128; + args.ram_width = 160; + args.ram_height = 296; + args.rotation = 270; + args.write_black_ram_command = DTM2; + args.black_bits_inverted = true; + args.write_color_ram_command = DTM1; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO26; + args.seconds_per_frame = 2.0; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } void board_deinit(void) { diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/badger2350-shared.h b/ports/raspberrypi/boards/pimoroni_badger2350/badger2350-shared.h new file mode 100644 index 0000000000000..4cc67efd4df7e --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/badger2350-shared.h @@ -0,0 +1,15 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/digitalio/DigitalInOut.h" + +extern digitalio_digitalinout_obj_t i2c_power_en_pin_obj; +extern const mp_obj_fun_builtin_fixed_t set_update_speed_obj; +extern const mp_obj_fun_builtin_fixed_t get_reset_state_obj; +extern const mp_obj_fun_builtin_fixed_t on_reset_pressed_obj; diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/board.c b/ports/raspberrypi/boards/pimoroni_badger2350/board.c new file mode 100644 index 0000000000000..1da13b8033995 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/board.c @@ -0,0 +1,225 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Bob Abeles +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" + +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/board.h" +#include "supervisor/board.h" +#include "badger2350-shared.h" + +#include "hardware/gpio.h" +#include "hardware/structs/iobank0.h" + +digitalio_digitalinout_obj_t i2c_power_en_pin_obj; +static volatile uint32_t reset_button_state = 0; + +// Forward declaration to satisfy -Wmissing-prototypes +static void preinit_button_state(void) __attribute__((constructor(101))); + + +// pin definitions +// Button pin definitions for Badger2350 +#define SW_A_PIN 7 +#define SW_B_PIN 9 +#define SW_C_PIN 10 +#define SW_DOWN_PIN 6 +#define SW_UP_PIN 11 + +static const uint8_t _sw_pin_nrs[] = { + SW_A_PIN, SW_B_PIN, SW_C_PIN, SW_DOWN_PIN, SW_UP_PIN +}; + +// Mask of all front button pins +#define SW_MASK ((1 << SW_A_PIN) | (1 << SW_B_PIN) | (1 << SW_C_PIN) | \ + (1 << SW_DOWN_PIN) | (1 << SW_UP_PIN)) + +// This function runs BEFORE main() via constructor attribute! +// This is the key to fast button state detection. +// Priority 101 = runs very early + +static void preinit_button_state(void) { + // Configure button pins as inputs with pull-downs using direct register access + // This is faster than SDK functions and works before full init + + for (size_t i = 0; i < sizeof(_sw_pin_nrs); i++) { + uint8_t pin_nr = _sw_pin_nrs[i]; + // Set as input + sio_hw->gpio_oe_clr = 1u << pin_nr; + // enable pull-ups + pads_bank0_hw->io[pin_nr] = PADS_BANK0_GPIO0_IE_BITS | + PADS_BANK0_GPIO0_PUE_BITS; + // Set GPIO function + iobank0_hw->io[pin_nr].ctrl = 5; // SIO function + } + + // Small delay for pins to settle (just a few cycles) + for (volatile int i = 0; i < 100; i++) { + __asm volatile ("nop"); + } + + // Capture button states NOW - before anything else runs + reset_button_state = ~sio_hw->gpio_in & SW_MASK; +} + +static mp_obj_t _get_reset_state(void) { + return mp_obj_new_int(reset_button_state); +} +MP_DEFINE_CONST_FUN_OBJ_0(get_reset_state_obj, _get_reset_state); + +static mp_obj_t _on_reset_pressed(mp_obj_t pin_in) { + mcu_pin_obj_t *pin = MP_OBJ_TO_PTR(pin_in); + return mp_obj_new_bool( + (reset_button_state & (1 << pin->number)) != 0); +} +MP_DEFINE_CONST_FUN_OBJ_1(on_reset_pressed_obj, _on_reset_pressed); + +// The display uses an SSD1680 control chip. +uint8_t _start_sequence[] = { + 0x12, 0x80, 0x00, 0x14, // soft reset and wait 20ms + 0x11, 0x00, 0x01, 0x03, // Ram data entry mode + 0x3c, 0x00, 0x01, 0x03, // border color + 0x2c, 0x00, 0x01, 0x28, // Set vcom voltage + 0x03, 0x00, 0x01, 0x17, // Set gate voltage + 0x04, 0x00, 0x03, 0x41, 0xae, 0x32, // Set source voltage + 0x4e, 0x00, 0x01, 0x00, // ram x count + 0x4f, 0x00, 0x02, 0x00, 0x00, // ram y count + 0x01, 0x00, 0x03, 0x07, 0x01, 0x00, // set display size + 0x32, 0x00, 0x99, // Update waveforms + + // offset 44 + 0x40, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L0 + 0xA0, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L1 + 0xA8, 0x65, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L2 + 0xAA, 0x65, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // VS L4 + + // offset 104 + 0x02, 0x00, 0x00, 0x05, 0x0A, 0x00, 0x03, // Group0 (with default speed==0) + // offset 111 + 0x19, 0x19, 0x00, 0x02, 0x00, 0x00, 0x03, // Group1 (with default speed==0) + // offset 118 + 0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x03, // Group2 (with default speed==0) + + // offset 125 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group3 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group4 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group5 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group6 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group7 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group8 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group9 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group10 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Group11 + 0x44, 0x42, 0x22, 0x22, 0x23, 0x32, 0x00, // Config + 0x00, 0x00, // FR, XON + + 0x22, 0x00, 0x01, 0xc7, // display update mode + +}; + +const uint8_t _stop_sequence[] = { + 0x10, 0x00, 0x01, 0x01 // DSM deep sleep mode 1 +}; + +const uint8_t _refresh_sequence[] = { + 0x20, 0x00, 0x00 // ADUS +}; + +// Change update speed. This changes the repeat-count in the LUTs +// Pimoroni uses: 0 == slow ... 3 == fast +// and calculates the LUT repeat count as 3-speed + +#define SPEED_OFFSET_1 110 +#define SPEED_OFFSET_2 117 +#define SPEED_OFFSET_3 124 + +static mp_obj_t _set_update_speed(mp_obj_t speed_in) { + mp_int_t speed = mp_obj_get_int(speed_in); + uint8_t count = (uint8_t)3 - (uint8_t)(speed & 3); + _start_sequence[SPEED_OFFSET_1] = count; + _start_sequence[SPEED_OFFSET_2] = count; + _start_sequence[SPEED_OFFSET_3] = count; + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_1(set_update_speed_obj, _set_update_speed); + +void board_init(void) { + // Drive the I2C_POWER_EN pin high + i2c_power_en_pin_obj.base.type = &digitalio_digitalinout_type; + common_hal_digitalio_digitalinout_construct( + &i2c_power_en_pin_obj, &pin_GPIO27); + common_hal_digitalio_digitalinout_switch_to_output( + &i2c_power_en_pin_obj, true, DRIVE_MODE_PUSH_PULL); + common_hal_digitalio_digitalinout_never_reset(&i2c_power_en_pin_obj); + + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + busio_spi_obj_t *spi = &bus->inline_bus; + common_hal_busio_spi_construct(spi, &pin_GPIO18, &pin_GPIO19, NULL, false); + common_hal_busio_spi_never_reset(spi); + + bus->base.type = &fourwire_fourwire_type; + common_hal_fourwire_fourwire_construct(bus, + spi, + MP_OBJ_FROM_PTR(&pin_GPIO20), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO21), // EPD_RST Reset + 12000000, // Baudrate + 0, // Polarity + 0); // Phase + + // create and configure display + epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; + display->base.type = &epaperdisplay_epaperdisplay_type; + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = _start_sequence; + args.start_sequence_len = sizeof(_start_sequence); + args.stop_sequence = _stop_sequence; + args.stop_sequence_len = sizeof(_stop_sequence); + args.width = 264; + args.height = 176; + args.ram_width = 250; + args.ram_height = 296; + args.rotation = 270; + args.set_column_window_command = 0x44; + args.set_row_window_command = 0x45; + args.set_current_column_command = 0x4e; + args.set_current_row_command = 0x4f; + args.write_black_ram_command = 0x24; + args.write_color_ram_command = 0x26; + args.color_bits_inverted = true; + args.refresh_sequence = _refresh_sequence; + args.refresh_sequence_len = sizeof(_refresh_sequence); + args.refresh_time = 1.0; + args.busy_pin = &pin_GPIO16; + args.busy_state = true; + args.seconds_per_frame = 3.0; + args.grayscale = true; + args.two_byte_sequence_length = true; + args.address_little_endian = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); +} + +void board_deinit(void) { + epaperdisplay_epaperdisplay_obj_t *display = &displays[0].epaper_display; + if (display->base.type == &epaperdisplay_epaperdisplay_type) { + while (common_hal_epaperdisplay_epaperdisplay_get_busy(display)) { + RUN_BACKGROUND_TASKS; + } + } + common_hal_displayio_release_displays(); +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/link.ld b/ports/raspberrypi/boards/pimoroni_badger2350/link.ld new file mode 100644 index 0000000000000..e814bead4c51e --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/link.ld @@ -0,0 +1 @@ +firmware_size = 1532k; diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.h b/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.h new file mode 100644 index 0000000000000..6cffd190ad606 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.h @@ -0,0 +1,21 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Bernhard Bablok +// +// SPDX-License-Identifier: MIT + +#define MICROPY_HW_BOARD_NAME "Pimoroni Badger 2350" +#define MICROPY_HW_MCU_NAME "rp2350a" + +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1) + +#define MICROPY_HW_LED_STATUS (&pin_GPIO0) + +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO4) +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO5) + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19) + +#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO8) diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.mk new file mode 100644 index 0000000000000..0157ccf149960 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/mpconfigboard.mk @@ -0,0 +1,38 @@ +USB_VID = 0x2E8A +USB_PID = 0x1100 +USB_PRODUCT = "Pimoroni Badger 2350" +USB_MANUFACTURER = "Pimoroni" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" + +CIRCUITPY__EVE = 1 + +CIRCUITPY_CYW43 = 1 +CIRCUITPY_SSL = 1 +CIRCUITPY_HASHLIB = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_MDNS = 1 +CIRCUITPY_SOCKETPOOL = 1 +CIRCUITPY_WIFI = 1 + +# PIO clock divider set to 2 (default), consider changing if TM2 gSPI +# becomes unreliable. +CFLAGS += \ + -DCYW43_PIN_WL_DYNAMIC=0 \ + -DCYW43_DEFAULT_PIN_WL_HOST_WAKE=24 \ + -DCYW43_DEFAULT_PIN_WL_REG_ON=23 \ + -DCYW43_DEFAULT_PIN_WL_CLOCK=29 \ + -DCYW43_DEFAULT_PIN_WL_DATA_IN=24 \ + -DCYW43_DEFAULT_PIN_WL_DATA_OUT=24 \ + -DCYW43_DEFAULT_PIN_WL_CS=25 \ + -DCYW43_WL_GPIO_COUNT=3 \ + -DCYW43_PIO_CLOCK_DIV_INT=2 \ + -DCYW43_PIO_CLOCK_DIV_FRAC=0 +# Must be accompanied by a linker script change +CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' + +FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-pcf85063a diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/pico-sdk-configboard.h b/ports/raspberrypi/boards/pimoroni_badger2350/pico-sdk-configboard.h new file mode 100644 index 0000000000000..42e0612bf8ba6 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/pico-sdk-configboard.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Bob Abeles +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/pimoroni_badger2350/pins.c b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c new file mode 100644 index 0000000000000..1a66961e68fcf --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_badger2350/pins.c @@ -0,0 +1,135 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Bob Abeles +// +// SPDX-License-Identifier: MIT + +#include "py/objtuple.h" +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" +#include "badger2350-shared.h" + + +// LUT manipulation +static const mp_rom_map_elem_t lut_update_table[] = { + { MP_ROM_QSTR(MP_QSTR_SET_UPDATE_SPEED), (mp_obj_t)&set_update_speed_obj }, + { MP_ROM_QSTR(MP_QSTR_SPEED_SLOW), MP_ROM_INT(0) }, + { MP_ROM_QSTR(MP_QSTR_SPEED_FAST), MP_ROM_INT(1) }, + { MP_ROM_QSTR(MP_QSTR_SPEED_FASTER), MP_ROM_INT(2) }, + { MP_ROM_QSTR(MP_QSTR_SPEED_FASTEST), MP_ROM_INT(3) }, +}; +MP_DEFINE_CONST_DICT(lut_update_dict, lut_update_table); + +const mp_obj_module_t display_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&lut_update_dict, +}; + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_display), MP_ROM_PTR(&display_module) }, + + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_LED0), MP_ROM_PTR(&pin_GPIO0) }, + + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_LED1), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_LED2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_LED3), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, + + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_SW_DOWN), MP_ROM_PTR(&pin_GPIO6) }, + + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SW_A), MP_ROM_PTR(&pin_GPIO7) }, + + // GP8 is reserved for PSRAM chip select + + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_SW_B), MP_ROM_PTR(&pin_GPIO9) }, + + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_SW_C), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_SW_UP), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_RTC_ALARM), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_SW_RESET), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_SW_INT), MP_ROM_PTR(&pin_GPIO15) }, + + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_INKY_BUSY), MP_ROM_PTR(&pin_GPIO16) }, + + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_INKY_CS), MP_ROM_PTR(&pin_GPIO17) }, + + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO18) }, + + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_INKY_DC), MP_ROM_PTR(&pin_GPIO20) }, + + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_INKY_RST), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_SW_HOME), MP_ROM_PTR(&pin_GPIO22) }, + + // GP23, GP24, GP25, and GP29 are reserved for RM2 gSPI + + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_VBAT_SENSE), MP_ROM_PTR(&pin_GPIO26) }, + + // GP27 is the used for I2C power-enable, driven high by board.c + { MP_ROM_QSTR(MP_QSTR_I2C_POWER_EN), MP_ROM_PTR(&i2c_power_en_pin_obj) }, + + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_SENSE_1V1), MP_ROM_PTR(&pin_GPIO28) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + + // Pins accessed though the RM2 module (CYW43439) + // CYW0, CYW1 is unconnected + { MP_ROM_QSTR(MP_QSTR_CHARGE_STAT), MP_ROM_PTR(&pin_CYW2) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].epaper_display)}, + + // button-state on reset + // Use `board.RESET_STATE()` to query the state of all buttons at reset. + // To detect individual key-presses, `board.ON_RESET_PRESSED()` + // is simpler. + { MP_ROM_QSTR(MP_QSTR_RESET_STATE), (mp_obj_t)&get_reset_state_obj }, + + // Use `board.ON_RESET_PRESSED(board.SW_A)` to check if `SW_A` was pressed + // during reset. + { MP_ROM_QSTR(MP_QSTR_ON_RESET_PRESSED), (mp_obj_t)&on_reset_pressed_obj }, + +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/board.c b/ports/raspberrypi/boards/pimoroni_explorer2350/board.c new file mode 100644 index 0000000000000..d0c6d9a57ce9f --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/board.c @@ -0,0 +1,145 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/paralleldisplaybus/ParallelBus.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "hardware/gpio.h" + +// Display pins from Pimoroni Explorer parallel bus: {cs=27, dc=28, wr=30, rd=31, d0=32, bl=26} +#define LCD_BACKLIGHT_PIN 26 +#define LCD_CS_PIN 27 +#define LCD_DC_PIN 28 +#define LCD_WR_PIN 30 +#define LCD_RD_PIN 31 +#define LCD_D0_PIN 32 // Data pins are GPIO32-39 (8 consecutive pins) + +#define DELAY 0x80 + +// ST7789V display init sequence for 320x240 parallel bus +// Based on Pimoroni's pimoroni-pico ST7789 driver configuration +uint8_t display_init_sequence[] = { + // Software reset + 0x01, 0 | DELAY, 150, + // Sleep out + 0x11, 0 | DELAY, 255, + // Tearing effect line on (frame sync) + 0x35, 1, 0x00, + // COLMOD: 16-bit color (5-6-5 RGB) + 0x3A, 1, 0x55, + // Porch control (PORCTRL) + 0xB2, 5, 0x0C, 0x0C, 0x00, 0x33, 0x33, + // Gate control (GCTRL) - VGH=13.26V, VGL=-10.43V + 0xB7, 1, 0x35, + // VCOM setting (VCOMS) + 0xBB, 1, 0x1F, + // LCM control (LCMCTRL) + 0xC0, 1, 0x2C, + // VDV and VRH command enable (VDVVRHEN) + 0xC2, 1, 0x01, + // VRH set (VRHS) + 0xC3, 1, 0x12, + // VDV set (VDVS) + 0xC4, 1, 0x20, + // Frame rate control (FRCTRL2) + 0xC6, 1, 0x0F, + // Power control 1 (PWCTRL1) + 0xD0, 2, 0xA4, 0xA1, + // RAM control (RAMCTRL) - for proper endianness + 0xB0, 2, 0x00, 0xC0, + // Positive gamma correction + 0xE0, 14, 0xD0, 0x08, 0x11, 0x08, 0x0C, 0x15, 0x39, 0x33, 0x50, 0x36, 0x13, 0x14, 0x29, 0x2D, + // Negative gamma correction + 0xE1, 14, 0xD0, 0x08, 0x10, 0x08, 0x06, 0x06, 0x39, 0x44, 0x51, 0x0B, 0x16, 0x14, 0x2F, 0x31, + // Inversion on + 0x21, 0, + // Normal display mode on + 0x13, 0 | DELAY, 10, + // MADCTL: MX=0, MY=1, MV=1, ML=1 (COL_ORDER | SWAP_XY | SCAN_ORDER) = 0x70 + // This configures the 320x240 display in landscape orientation + 0x36, 1, 0x70, + // Display on + 0x29, 0 | DELAY, 100, +}; + +static void display_init(void) { + paralleldisplaybus_parallelbus_obj_t *bus = &allocate_display_bus()->parallel_bus; + bus->base.type = ¶lleldisplaybus_parallelbus_type; + + common_hal_paralleldisplaybus_parallelbus_construct(bus, + &pin_GPIO32, // Data0 (D0) - data pins are sequential GPIO32-39 + &pin_GPIO28, // Command/Data (DC) + &pin_GPIO27, // Chip select (CS) + &pin_GPIO30, // Write (WR) + &pin_GPIO31, // Read (RD) + NULL, // Reset (directly connected to board reset) + 15000000); // Frequency - ST7789 min clock cycle ~66ns = ~15MHz + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + + common_hal_busdisplay_busdisplay_construct(display, + bus, + 320, // Width + 240, // Height + 0, // column start + 0, // row start + 0, // rotation + 16, // Color depth + false, // grayscale + false, // pixels_in_byte_share_row + 1, // bytes per cell + false, // reverse_pixels_in_byte + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + &pin_GPIO26, // Backlight pin (BL) + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} + +void board_init(void) { + // Ensure backlight is on before display init + board_reset_pin_number(LCD_BACKLIGHT_PIN); + display_init(); +} + +// Prevent the backlight pin from being reset, keeping display visible across soft resets +bool board_reset_pin_number(uint8_t pin_number) { + if (pin_number == LCD_BACKLIGHT_PIN) { + // Keep backlight on - set high output without glitching + gpio_put(pin_number, 1); + gpio_set_dir(pin_number, GPIO_OUT); + gpio_set_function(pin_number, GPIO_FUNC_SIO); + return true; + } + return false; +} + +void reset_board(void) { + // Keep backlight on during reset + board_reset_pin_number(LCD_BACKLIGHT_PIN); +} + +void board_deinit(void) { + // Backlight will be handled by board_reset_pin_number +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.h b/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.h new file mode 100644 index 0000000000000..dd269aa6b844a --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "Pimoroni Explorer" +#define MICROPY_HW_MCU_NAME "rp2350b" + +#define MICROPY_HW_LED_STATUS (&pin_GPIO25) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO21, .sda = &pin_GPIO20}} + +#define DEFAULT_UART_BUS_RX (&pin_GPIO1) +#define DEFAULT_UART_BUS_TX (&pin_GPIO0) diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.mk b/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.mk new file mode 100644 index 0000000000000..4e4882910c8ab --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/mpconfigboard.mk @@ -0,0 +1,10 @@ +USB_VID = 0x2E8A +USB_PID = 0x10C0 +USB_PRODUCT = "Explorer" +USB_MANUFACTURER = "Pimoroni" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = B +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/pico-sdk-configboard.h b/ports/raspberrypi/boards/pimoroni_explorer2350/pico-sdk-configboard.h new file mode 100644 index 0000000000000..66b57dfd13dc2 --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/pico-sdk-configboard.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/pimoroni_explorer2350/pins.c b/ports/raspberrypi/boards/pimoroni_explorer2350/pins.c new file mode 100644 index 0000000000000..8154a43de005d --- /dev/null +++ b/ports/raspberrypi/boards/pimoroni_explorer2350/pins.c @@ -0,0 +1,124 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // User GPIOs (accent connector) + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + + // Servo pins + { MP_ROM_QSTR(MP_QSTR_SERVO1), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_SERVO2), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_SERVO3), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_SERVO4), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + + // Audio + { MP_ROM_QSTR(MP_QSTR_AUDIO), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_AMP_EN), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + + // Buttons + { MP_ROM_QSTR(MP_QSTR_SW_C), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_SW_B), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_SW_A), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_SW_X), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_SW_Y), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_SW_Z), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + + // I2C + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + + // User button + { MP_ROM_QSTR(MP_QSTR_SW_USER), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + + // LED? + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_GP25), MP_ROM_PTR(&pin_GPIO25) }, + + // Display parallel bus pins (ST7789V 320x240) + // Pins from Pimoroni: {cs=27, dc=28, wr=30, rd=31, d0=32, bl=26} + { MP_ROM_QSTR(MP_QSTR_LCD_BL), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_LCD_DC), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_LCD_WR), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_GP30), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_LCD_RD), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_GP31), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D0), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_GP32), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D1), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_GP33), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D2), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_GP34), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D3), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_GP35), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D4), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_GP36), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D5), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_GP37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D6), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_GP38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_LCD_D7), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_GP39), MP_ROM_PTR(&pin_GPIO39) }, + + // ADC pins (RP2350B extended GPIOs) + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_ADC0), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_GP40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_ADC1), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_GP41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_ADC2), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_GP42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_ADC3), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_GP43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_ADC4), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_GP44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_ADC5), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_GP45), MP_ROM_PTR(&pin_GPIO45) }, + + // I2C object + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + + // Display object + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c index 7969143ec6621..58c54334d59af 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_5_7/board.c @@ -58,48 +58,35 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO28, // EPD_DC Command or data - &pin_GPIO17, // EPD_CS Chip select - &pin_GPIO27, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO28), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO27), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 1.0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 600, // width - 448, // height - 640, // ram_width - 480, // ram_height - 0, // colstart - 0, // rowstart - 0, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x10, // write_black_ram_command - false, // black_bits_inverted - NO_COMMAND, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - refresh_sequence, sizeof(refresh_sequence), - 28.0, // refresh_time - NULL, // busy_pin - false, // busy_state - 40.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - true, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.start_up_time = 1.0; + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 600; + args.height = 448; + args.ram_width = 640; + args.ram_height = 480; + args.write_black_ram_command = 0x10; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 28.0; + args.busy_pin = NULL; + args.seconds_per_frame = 40.0; + args.acep = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c index 7c71e03b4281d..fc6ca67f3ae65 100644 --- a/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c +++ b/ports/raspberrypi/boards/pimoroni_inky_frame_7_3/board.c @@ -117,48 +117,36 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO28, // EPD_DC Command or data - &pin_GPIO17, // EPD_CS Chip select - &pin_GPIO27, // EPD_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO28), // EPD_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO17), // EPD_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO27), // EPD_RST Reset 1000000, // Baudrate 0, // Polarity 0); // Phase epaperdisplay_epaperdisplay_obj_t *display = &allocate_display()->epaper_display; display->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - display, - bus, - display_start_sequence, sizeof(display_start_sequence), - 1.0, // start up time - display_stop_sequence, sizeof(display_stop_sequence), - 800, // width - 480, // height - 800, // ram_width - 480, // ram_height - 0, // colstart - 0, // rowstart - 180, // rotation - NO_COMMAND, // set_column_window_command - NO_COMMAND, // set_row_window_command - NO_COMMAND, // set_current_column_command - NO_COMMAND, // set_current_row_command - 0x10, // write_black_ram_command - false, // black_bits_inverted - NO_COMMAND, // write_color_ram_command - false, // color_bits_inverted - 0x000000, // highlight_color - refresh_sequence, sizeof(refresh_sequence), - 28.0, // refresh_time - NULL, // busy_pin - false, // busy_state - 30.0, // seconds_per_frame - false, // always_toggle_chip_select - false, // grayscale - true, // acep - false, // spectra6 - false, // two_byte_sequence_length - false); // address_little_endian + + epaperdisplay_construct_args_t args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + args.bus = bus; + args.start_sequence = display_start_sequence; + args.start_sequence_len = sizeof(display_start_sequence); + args.start_up_time = 1.0; + args.stop_sequence = display_stop_sequence; + args.stop_sequence_len = sizeof(display_stop_sequence); + args.width = 800; + args.height = 480; + args.ram_width = 800; + args.ram_height = 480; + args.rotation = 180; + args.write_black_ram_command = 0x10; + args.refresh_sequence = refresh_sequence; + args.refresh_sequence_len = sizeof(refresh_sequence); + args.refresh_time = 28.0; + args.busy_pin = NULL; + args.seconds_per_frame = 30.0; + args.acep = true; + common_hal_epaperdisplay_epaperdisplay_construct(display, &args); } // Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/pimoroni_picosystem/board.c b/ports/raspberrypi/boards/pimoroni_picosystem/board.c index 40756b56426e4..e5956e43ed110 100644 --- a/ports/raspberrypi/boards/pimoroni_picosystem/board.c +++ b/ports/raspberrypi/boards/pimoroni_picosystem/board.c @@ -51,9 +51,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO9, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO4, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_RST Reset 60000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/pimoroni_plasma2350/mpconfigboard.h b/ports/raspberrypi/boards/pimoroni_plasma2350/mpconfigboard.h index 72b1e1d04399f..097cbd8baa301 100644 --- a/ports/raspberrypi/boards/pimoroni_plasma2350/mpconfigboard.h +++ b/ports/raspberrypi/boards/pimoroni_plasma2350/mpconfigboard.h @@ -5,7 +5,7 @@ // SPDX-License-Identifier: MIT #define MICROPY_HW_BOARD_NAME "Pimoroni Plasma 2350" -#define MICROPY_HW_MCU_NAME "rp2350b" +#define MICROPY_HW_MCU_NAME "rp2350a" #define CIRCUITPY_RGB_STATUS_INVERTED_PWM #define CIRCUITPY_RGB_STATUS_R (&pin_GPIO16) diff --git a/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.h b/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.h index d9e62abb1edd2..576299d939cfb 100644 --- a/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.h +++ b/ports/raspberrypi/boards/pimoroni_plasma2350w/mpconfigboard.h @@ -5,7 +5,7 @@ // SPDX-License-Identifier: MIT #define MICROPY_HW_BOARD_NAME "Pimoroni Plasma 2350W" -#define MICROPY_HW_MCU_NAME "rp2350A" +#define MICROPY_HW_MCU_NAME "rp2350a" #define CIRCUITPY_RGB_STATUS_INVERTED_PWM #define CIRCUITPY_RGB_STATUS_R (&pin_GPIO16) diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/board.c b/ports/raspberrypi/boards/studiolab_picoexpander/board.c new file mode 100644 index 0000000000000..299f32da04f8c --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/link.ld b/ports/raspberrypi/boards/studiolab_picoexpander/link.ld new file mode 100644 index 0000000000000..e814bead4c51e --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/link.ld @@ -0,0 +1 @@ +firmware_size = 1532k; diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h new file mode 100644 index 0000000000000..ea475f6822261 --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.h @@ -0,0 +1,20 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#define MICROPY_HW_BOARD_NAME "Studiolab Pico Expander" +#define MICROPY_HW_MCU_NAME "rp2350a" + +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL (1) +#define CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE (1) + +#define MICROPY_HW_LED_STATUS (&pin_CYW0) +#define MICROPY_HW_NEOPIXEL (&pin_GPIO2) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO5, .sda = &pin_GPIO4}} + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO18, .mosi = &pin_GPIO19, .miso = &pin_GPIO16}} diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk new file mode 100644 index 0000000000000..eabc2c41e0722 --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/mpconfigboard.mk @@ -0,0 +1,35 @@ +USB_VID = 0x1209 +USB_PID = 0xC1C1 +USB_PRODUCT = "StudioLab Pico Expander" +USB_MANUFACTURER = "Raspberry Pi" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q32JVxQ" + +CIRCUITPY__EVE = 1 + +CIRCUITPY_CYW43 = 1 +CIRCUITPY_SSL = 1 +CIRCUITPY_HASHLIB = 1 +CIRCUITPY_WEB_WORKFLOW = 1 +CIRCUITPY_MDNS = 1 +CIRCUITPY_SOCKETPOOL = 1 +CIRCUITPY_WIFI = 1 + +CFLAGS += \ + -DCYW43_PIN_WL_DYNAMIC=0 \ + -DCYW43_DEFAULT_PIN_WL_HOST_WAKE=24 \ + -DCYW43_DEFAULT_PIN_WL_REG_ON=23 \ + -DCYW43_DEFAULT_PIN_WL_CLOCK=29 \ + -DCYW43_DEFAULT_PIN_WL_DATA_IN=24 \ + -DCYW43_DEFAULT_PIN_WL_DATA_OUT=24 \ + -DCYW43_DEFAULT_PIN_WL_CS=25 \ + -DCYW43_WL_GPIO_COUNT=3 \ + -DCYW43_WL_GPIO_LED_PIN=0 \ + -DCYW43_PIO_CLOCK_DIV_INT=3 + +# Must be accompanied by a linker script change +CFLAGS += -DCIRCUITPY_FIRMWARE_SIZE='(1536 * 1024)' diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/pico-sdk-configboard.h b/ports/raspberrypi/boards/studiolab_picoexpander/pico-sdk-configboard.h new file mode 100644 index 0000000000000..945802d1ab116 --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/pico-sdk-configboard.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/studiolab_picoexpander/pins.c b/ports/raspberrypi/boards/studiolab_picoexpander/pins.c new file mode 100644 index 0000000000000..6faafbfd177ea --- /dev/null +++ b/ports/raspberrypi/boards/studiolab_picoexpander/pins.c @@ -0,0 +1,113 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Jeff Epler for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_D0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + + { MP_ROM_QSTR(MP_QSTR_D1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + + { MP_ROM_QSTR(MP_QSTR_D2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + + { MP_ROM_QSTR(MP_QSTR_D3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_D4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_D5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_D6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + + { MP_ROM_QSTR(MP_QSTR_D7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + + { MP_ROM_QSTR(MP_QSTR_D8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_D9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + + { MP_ROM_QSTR(MP_QSTR_D10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_D11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_D12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_D13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_D14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + + { MP_ROM_QSTR(MP_QSTR_BTN), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_D15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + + { MP_ROM_QSTR(MP_QSTR_D16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + + { MP_ROM_QSTR(MP_QSTR_D17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + + { MP_ROM_QSTR(MP_QSTR_D18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + + { MP_ROM_QSTR(MP_QSTR_D19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + + { MP_ROM_QSTR(MP_QSTR_D20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + + { MP_ROM_QSTR(MP_QSTR_D21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + + { MP_ROM_QSTR(MP_QSTR_D22), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_A26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26_A0), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO26) }, + + { MP_ROM_QSTR(MP_QSTR_A27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27_A1), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO27) }, + + { MP_ROM_QSTR(MP_QSTR_A28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP28_A2), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO28) }, + + { MP_ROM_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, + + { MP_ROM_QSTR(MP_QSTR_SMPS_MODE), MP_ROM_PTR(&pin_CYW1) }, + + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_CYW0) }, + + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_CYW2) }, + + { MP_ROM_QSTR(MP_QSTR_STEMMA_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/board.c b/ports/raspberrypi/boards/tinycircuits_thumby/board.c new file mode 100644 index 0000000000000..3a306b35f0d76 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/board.c @@ -0,0 +1,81 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" + +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + + +uint8_t display_init_sequence[] = { + 0xAE, 0, // DISPLAY_OFF + 0x20, 1, 0x00, // Set memory addressing to horizontal mode. + 0x81, 1, 0xcf, // set contrast control + 0xA1, 0, // Column 127 is segment 0 + 0xA6, 0, // Normal display + 0xc8, 0, // Normal display + 0xA8, 1, 0x3f, // Mux ratio is 1/64 + 0xd5, 1, 0x80, // Set divide ratio + 0xd9, 1, 0xf1, // Set pre-charge period + 0xda, 1, 0x12, // Set com configuration + 0xdb, 1, 0x40, // Set vcom configuration + 0x8d, 1, 0x14, // Enable charge pump + 0xAF, 0, // DISPLAY_ON +}; + +void board_init(void) { + busio_spi_obj_t *spi = common_hal_board_create_spi(0); + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + bus->base.type = &fourwire_fourwire_type; + common_hal_fourwire_fourwire_construct(bus, + spi, + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_OLED_DC), // Command or data + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_OLED_CS), // Chip select + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_OLED_RESET), // Reset + 10000000, // Baudrate + 0, // Polarity + 0); // Phase + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 72, // Width (after rotation) + 40, // Height (after rotation) + 28, // column start + 28, // row start + 0, // rotation + 1, // Color depth + true, // grayscale + false, // pixels in byte share row. only used for depth < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + 0x21, // Set column command + 0x22, // Set row command + 44, // Write memory command + display_init_sequence, + sizeof(display_init_sequence), + NULL, // backlight pin + 0x81, + 1.0f, // brightness + true, // single_byte_bounds + true, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 0); // backlight pwm frequency +} + +void reset_board(void) { +} diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h new file mode 100644 index 0000000000000..cec0fc10f55b1 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.h @@ -0,0 +1,20 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "TinyCircuits Thumby" +#define MICROPY_HW_MCU_NAME "rp2040" + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19) + +#define CIRCUITPY_BOARD_OLED_DC (&pin_GPIO17) +#define CIRCUITPY_BOARD_OLED_CS (&pin_GPIO16) +#define CIRCUITPY_BOARD_OLED_RESET (&pin_GPIO20) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = DEFAULT_SPI_BUS_SCK, .mosi = DEFAULT_SPI_BUS_MOSI, .miso = NULL}} diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk new file mode 100644 index 0000000000000..d8140e6cb0a13 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/mpconfigboard.mk @@ -0,0 +1,18 @@ +USB_VID = 0x1209 +USB_PID = 0x3500 +USB_PRODUCT = "Thumby" +USB_MANUFACTURER = "TinyCircuits" + +CHIP_VARIANT = RP2040 +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q16JVxQ" + +CIRCUITPY_STAGE = 1 +CIRCUITPY_AUDIOIO = 1 +CIRCUITPY_AUDIOPWMIO = 1 +CIRCUITPY_KEYPAD = 1 + +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_framebuf +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SSD1306 +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_DisplayIO_SSD1306 diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/pico-sdk-configboard.h b/ports/raspberrypi/boards/tinycircuits_thumby/pico-sdk-configboard.h new file mode 100644 index 0000000000000..ce5a7645b4e22 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/pico-sdk-configboard.h @@ -0,0 +1,12 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. + +// Allow extra time for xosc to start. +#define PICO_XOSC_STARTUP_DELAY_MULTIPLIER 64 diff --git a/ports/raspberrypi/boards/tinycircuits_thumby/pins.c b/ports/raspberrypi/boards/tinycircuits_thumby/pins.c new file mode 100644 index 0000000000000..0a087bb8db8a9 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby/pins.c @@ -0,0 +1,48 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // Link Cable (ASR00074) + { MP_ROM_QSTR(MP_QSTR_EXT_TX), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_EXT), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_EXT_PU), MP_ROM_PTR(&pin_GPIO1) }, + + // 0.42 inch OLED AST1042 + { MP_ROM_QSTR(MP_QSTR_OLED_CS), MP_ROM_PTR(CIRCUITPY_BOARD_OLED_CS) }, + { MP_ROM_QSTR(MP_QSTR_OLED_DC), MP_ROM_PTR(CIRCUITPY_BOARD_OLED_DC) }, + { MP_ROM_QSTR(MP_QSTR_OLED_RESET), MP_ROM_PTR(CIRCUITPY_BOARD_OLED_RESET) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(DEFAULT_SPI_BUS_SCK) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(DEFAULT_SPI_BUS_MOSI) }, + + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, + + // Buttons + { MP_ROM_QSTR(MP_QSTR_BUTTON_LEFT), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_UP), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_RIGHT), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_DOWN), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_1), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_2), MP_ROM_PTR(&pin_GPIO27) }, + + // Mono PWM Speaker + { MP_ROM_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_GPIO28) }, + + // Hardware revision ID pins + { MP_OBJ_NEW_QSTR(MP_QSTR_ID3), MP_ROM_PTR(&pin_GPIO12) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ID2), MP_ROM_PTR(&pin_GPIO13) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ID1), MP_ROM_PTR(&pin_GPIO14) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_ID0), MP_ROM_PTR(&pin_GPIO15) }, + + // Power pins + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_GPIO26) } +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c b/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c new file mode 100644 index 0000000000000..17c512cb2a637 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/board.c @@ -0,0 +1,108 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/busio/SPI.h" +#include "shared-bindings/fourwire/FourWire.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/displayio/mipi_constants.h" +#include "shared-bindings/board/__init__.h" + + +#define DELAY 0x80 + +// display init sequence according to TinyCircuits-Tiny-Game-Engine +uint8_t display_init_sequence[] = { + 0xFE, 0, // inter register enable 1 + 0xEF, 0, // inter register enable 2 + 0xB0, 1, 0xC0, + 0xB1, 1, 0x80, + 0xB2, 1, 0x2F, + 0xB3, 1, 0x03, + 0xB7, 1, 0x01, + 0xB6, 1, 0x19, + 0xAC, 1, 0xC8, // Complement Principle of RGB 5, 6, 5 + 0xAB, 1, 0x0f, // ? + 0x3A, 1, 0x05, // COLMOD: Pixel Format Set + 0xB4, 1, 0x04, // ? + 0xA8, 1, 0x07, // Frame Rate Set + 0xB8, 1, 0x08, // ? + 0xE7, 1, 0x5A, // VREG_CTL + 0xE8, 1, 0x23, // VGH_SET + 0xE9, 1, 0x47, // VGL_SET + 0xEA, 1, 0x99, // VGH_VGL_CLK + 0xC6, 1, 0x30, // ? + 0xC7, 1, 0x1F, // ? + 0xF0, 14, 0x05, 0x1D, 0x51, 0x2F, 0x85, 0x2A, 0x11, 0x62, 0x00, 0x07, 0x07, 0x0F, 0x08, 0x1F, // SET_GAMMA1 + 0xF1, 14, 0x2E, 0x41, 0x62, 0x56, 0xA5, 0x3A, 0x3f, 0x60, 0x0F, 0x07, 0x0A, 0x18, 0x18, 0x1D, // SET_GAMMA2 + 0x11, 0 | DELAY, 120, + 0x29, 0 | DELAY, 10, // display on +}; + +void board_init(void) { + fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus; + busio_spi_obj_t *spi = &bus->inline_bus; + common_hal_busio_spi_construct( + spi, + DEFAULT_SPI_BUS_SCK, // CLK + DEFAULT_SPI_BUS_MOSI, // MOSI + NULL, // MISO not connected + false // Not half-duplex + ); + + common_hal_busio_spi_never_reset(spi); + + bus->base.type = &fourwire_fourwire_type; + + common_hal_fourwire_fourwire_construct( + bus, + spi, + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_LCD_DC), // DC + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_LCD_CS), // CS + MP_OBJ_FROM_PTR(CIRCUITPY_BOARD_LCD_RESET), // RST + 80000000, // baudrate + 0, // polarity + 0 // phase + ); + + busdisplay_busdisplay_obj_t *display = &allocate_display()->display; + display->base.type = &busdisplay_busdisplay_type; + common_hal_busdisplay_busdisplay_construct( + display, + bus, + 128, // width (after rotation) + 128, // height (after rotation) + 0, // column start + 0, // row start + 0, // rotation + 16, // color depth + false, // grayscale + false, // pixels in a byte share a row. Only valid for depths < 8 + 1, // bytes per cell. Only valid for depths < 8 + false, // reverse_pixels_in_byte. Only valid for depths < 8 + true, // reverse_pixels_in_word + MIPI_COMMAND_SET_COLUMN_ADDRESS, // set column command + MIPI_COMMAND_SET_PAGE_ADDRESS, // set row command + MIPI_COMMAND_WRITE_MEMORY_START, // write memory command + display_init_sequence, + sizeof(display_init_sequence), + CIRCUITPY_BOARD_LCD_BACKLIGHT, // backlight pin + NO_BRIGHTNESS_COMMAND, + 1.0f, // brightness + false, // single_byte_bounds + false, // data_as_commands + true, // auto_refresh + 60, // native_frames_per_second + true, // backlight_on_high + false, // SH1107_addressing + 50000 // backlight pwm frequency + ); +} + +void reset_board(void) { +} diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h new file mode 100644 index 0000000000000..fc76138bb8a0b --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.h @@ -0,0 +1,27 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#pragma once + +#define MICROPY_HW_BOARD_NAME "TinyCircuits Thumby Color" +#define MICROPY_HW_MCU_NAME "rp2350" + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO9) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO8) + +#define CIRCUITPY_BOARD_I2C (1) +#define CIRCUITPY_BOARD_I2C_PIN {{.scl = DEFAULT_I2C_BUS_SCL, .sda = DEFAULT_I2C_BUS_SDA}} + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO18) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO19) + +#define CIRCUITPY_BOARD_LCD_DC (&pin_GPIO16) +#define CIRCUITPY_BOARD_LCD_CS (&pin_GPIO17) +#define CIRCUITPY_BOARD_LCD_RESET (&pin_GPIO4) +#define CIRCUITPY_BOARD_LCD_BACKLIGHT (&pin_GPIO7) + +#define CIRCUITPY_BOARD_SPI (1) +#define CIRCUITPY_BOARD_SPI_PIN {{.clock = DEFAULT_SPI_BUS_SCK, .mosi = DEFAULT_SPI_BUS_MOSI, .miso = NULL}} diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk new file mode 100644 index 0000000000000..828d4a638d5ab --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/mpconfigboard.mk @@ -0,0 +1,15 @@ +USB_VID = 0x1209 +USB_PID = 0x3501 +USB_PRODUCT = "Thumby Color" +USB_MANUFACTURER = "TinyCircuits" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = A +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" + +CIRCUITPY_STAGE = 1 +CIRCUITPY_AUDIOIO = 1 +CIRCUITPY_AUDIOPWMIO = 1 +CIRCUITPY_KEYPAD = 1 diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/pico-sdk-configboard.h b/ports/raspberrypi/boards/tinycircuits_thumby_color/pico-sdk-configboard.h new file mode 100644 index 0000000000000..110195b779498 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/pico-sdk-configboard.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/tinycircuits_thumby_color/pins.c b/ports/raspberrypi/boards/tinycircuits_thumby_color/pins.c new file mode 100644 index 0000000000000..d0e63512d7ab6 --- /dev/null +++ b/ports/raspberrypi/boards/tinycircuits_thumby_color/pins.c @@ -0,0 +1,58 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Cooper Dalrymple +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" +#include "shared-module/displayio/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + // Buttons + { MP_ROM_QSTR(MP_QSTR_BUTTON_UP), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_LEFT), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_DOWN), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_RIGHT), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_A), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_B), MP_ROM_PTR(&pin_GPIO25) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_BUMPER_LEFT), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_BUMPER_RIGHT), MP_ROM_PTR(&pin_GPIO22) }, + { MP_ROM_QSTR(MP_QSTR_BUTTON_MENU), MP_ROM_PTR(&pin_GPIO26) }, + + // LED + { MP_ROM_QSTR(MP_QSTR_LED_R), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_LED_G), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_LED_B), MP_ROM_PTR(&pin_GPIO12) }, + + // Rumble + { MP_ROM_QSTR(MP_QSTR_RUMBLE), MP_ROM_PTR(&pin_GPIO5) }, + + // Mono PWM Speaker + { MP_ROM_QSTR(MP_QSTR_SPEAKER), MP_ROM_PTR(&pin_GPIO23) }, + { MP_ROM_QSTR(MP_QSTR_SPEAKER_ENABLE), MP_ROM_PTR(&pin_GPIO20) }, + + // 0.85 inch TFT GC9107 + { MP_ROM_QSTR(MP_QSTR_LCD_CS), MP_ROM_PTR(CIRCUITPY_BOARD_LCD_CS) }, + { MP_ROM_QSTR(MP_QSTR_LCD_DC), MP_ROM_PTR(CIRCUITPY_BOARD_LCD_DC) }, + { MP_ROM_QSTR(MP_QSTR_LCD_RESET), MP_ROM_PTR(CIRCUITPY_BOARD_LCD_RESET) }, + { MP_ROM_QSTR(MP_QSTR_LCD_BACKLIGHT), MP_ROM_PTR(CIRCUITPY_BOARD_LCD_BACKLIGHT) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SCK), MP_ROM_PTR(DEFAULT_SPI_BUS_SCK) }, + { MP_ROM_QSTR(MP_QSTR_LCD_MOSI), MP_ROM_PTR(DEFAULT_SPI_BUS_MOSI) }, + + { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) }, + { MP_ROM_QSTR(MP_QSTR_LCD_SPI), MP_ROM_PTR(&board_spi_obj) }, + + // RTC I2C + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(DEFAULT_I2C_BUS_SDA) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(DEFAULT_I2C_BUS_SCL) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + + // Power pins + { MP_ROM_QSTR(MP_QSTR_CHARGE_STAT), MP_ROM_PTR(&pin_GPIO24) }, + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO29) }, + +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/boards/ugame22/board.c b/ports/raspberrypi/boards/ugame22/board.c index 6b8152b14eedf..ed17a13ebc105 100644 --- a/ports/raspberrypi/boards/ugame22/board.c +++ b/ports/raspberrypi/boards/ugame22/board.c @@ -52,9 +52,9 @@ void board_init(void) { bus->base.type = &fourwire_fourwire_type; common_hal_fourwire_fourwire_construct(bus, spi, - &pin_GPIO4, // TFT_DC Command or data - &pin_GPIO5, // TFT_CS Chip select - &pin_GPIO1, // TFT_RST Reset + MP_OBJ_FROM_PTR(&pin_GPIO4), // TFT_DC Command or data + MP_OBJ_FROM_PTR(&pin_GPIO5), // TFT_CS Chip select + MP_OBJ_FROM_PTR(&pin_GPIO1), // TFT_RST Reset 80000000L, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/raspberrypi/boards/waveshare_rp2040_geek/board.c b/ports/raspberrypi/boards/waveshare_rp2040_geek/board.c index 71269f0d1852f..431840fb0c5c1 100644 --- a/ports/raspberrypi/boards/waveshare_rp2040_geek/board.c +++ b/ports/raspberrypi/boards/waveshare_rp2040_geek/board.c @@ -37,9 +37,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // TFT_DC - &pin_GPIO9, // TFT_CS - &pin_GPIO12, // TFT_RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // TFT_DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // TFT_RST 50000000, // Baudrate 0, // Polarity 0 // Phase diff --git a/ports/raspberrypi/boards/waveshare_rp2040_lcd_0_96/board.c b/ports/raspberrypi/boards/waveshare_rp2040_lcd_0_96/board.c index f17ca53d72464..44d279da43fed 100644 --- a/ports/raspberrypi/boards/waveshare_rp2040_lcd_0_96/board.c +++ b/ports/raspberrypi/boards/waveshare_rp2040_lcd_0_96/board.c @@ -60,9 +60,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // DC - &pin_GPIO9, // CS - &pin_GPIO12, // RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/raspberrypi/boards/waveshare_rp2350_geek/board.c b/ports/raspberrypi/boards/waveshare_rp2350_geek/board.c index 71269f0d1852f..431840fb0c5c1 100644 --- a/ports/raspberrypi/boards/waveshare_rp2350_geek/board.c +++ b/ports/raspberrypi/boards/waveshare_rp2350_geek/board.c @@ -37,9 +37,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // TFT_DC - &pin_GPIO9, // TFT_CS - &pin_GPIO12, // TFT_RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // TFT_DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // TFT_CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // TFT_RST 50000000, // Baudrate 0, // Polarity 0 // Phase diff --git a/ports/raspberrypi/boards/waveshare_rp2350_lcd_0_96/board.c b/ports/raspberrypi/boards/waveshare_rp2350_lcd_0_96/board.c index f17ca53d72464..44d279da43fed 100644 --- a/ports/raspberrypi/boards/waveshare_rp2350_lcd_0_96/board.c +++ b/ports/raspberrypi/boards/waveshare_rp2350_lcd_0_96/board.c @@ -60,9 +60,9 @@ static void display_init(void) { common_hal_fourwire_fourwire_construct( bus, spi, - &pin_GPIO8, // DC - &pin_GPIO9, // CS - &pin_GPIO12, // RST + MP_OBJ_FROM_PTR(&pin_GPIO8), // DC + MP_OBJ_FROM_PTR(&pin_GPIO9), // CS + MP_OBJ_FROM_PTR(&pin_GPIO12), // RST 40000000, // baudrate 0, // polarity 0 // phase diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/board.c b/ports/raspberrypi/boards/weact_studio_rp2350b_core/board.c new file mode 100644 index 0000000000000..e6a868ab21226 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/board.c @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h new file mode 100644 index 0000000000000..95a81334c73d5 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.h @@ -0,0 +1,10 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#define MICROPY_HW_BOARD_NAME "WeAct Studio RP2350B Core" +#define MICROPY_HW_MCU_NAME "rp2350b" + +#define CIRCUITPY_PSRAM_CHIP_SELECT (&pin_GPIO0) diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk new file mode 100644 index 0000000000000..d86f28280f3a0 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/mpconfigboard.mk @@ -0,0 +1,12 @@ +USB_VID = 0x1209 +USB_PID = 0xDEC1 +USB_PRODUCT = "RP2350B Core" +USB_MANUFACTURER = "WeAct Studio" + +CHIP_VARIANT = RP2350 +CHIP_PACKAGE = B +CHIP_FAMILY = rp2 + +EXTERNAL_FLASH_DEVICES = "W25Q128JVxQ" + +CIRCUITPY__EVE = 1 diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/pico-sdk-configboard.h b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pico-sdk-configboard.h new file mode 100644 index 0000000000000..66b57dfd13dc2 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pico-sdk-configboard.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Put board-specific pico-sdk definitions here. This file must exist. diff --git a/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c new file mode 100644 index 0000000000000..d6a82becfdd47 --- /dev/null +++ b/ports/raspberrypi/boards/weact_studio_rp2350b_core/pins.c @@ -0,0 +1,91 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_GP0), MP_ROM_PTR(&pin_GPIO0) }, + { MP_ROM_QSTR(MP_QSTR_GP1), MP_ROM_PTR(&pin_GPIO1) }, + { MP_ROM_QSTR(MP_QSTR_GP2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_GP3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_GP4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_GP5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_GP6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_GP7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_GP8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_GP9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_GP10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_GP11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_GP12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_GP13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_GP14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_GP15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_GP16), MP_ROM_PTR(&pin_GPIO16) }, + { MP_ROM_QSTR(MP_QSTR_GP17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_GP18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_GP19), MP_ROM_PTR(&pin_GPIO19) }, + { MP_ROM_QSTR(MP_QSTR_GP20), MP_ROM_PTR(&pin_GPIO20) }, + { MP_ROM_QSTR(MP_QSTR_GP21), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_GP22), MP_ROM_PTR(&pin_GPIO22) }, + + { MP_ROM_QSTR(MP_QSTR_GP23), MP_ROM_PTR(&pin_GPIO23) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_BUTTON), MP_ROM_PTR(&pin_GPIO23) }, + + { MP_ROM_QSTR(MP_QSTR_GP24), MP_ROM_PTR(&pin_GPIO24) }, + + { MP_ROM_QSTR(MP_QSTR_GP25), MP_ROM_PTR(&pin_GPIO25) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO25) }, + + { MP_ROM_QSTR(MP_QSTR_GP26), MP_ROM_PTR(&pin_GPIO26) }, + { MP_ROM_QSTR(MP_QSTR_GP27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_GP28), MP_ROM_PTR(&pin_GPIO28) }, + { MP_ROM_QSTR(MP_QSTR_GP29), MP_ROM_PTR(&pin_GPIO29) }, + { MP_ROM_QSTR(MP_QSTR_GP30), MP_ROM_PTR(&pin_GPIO30) }, + { MP_ROM_QSTR(MP_QSTR_GP31), MP_ROM_PTR(&pin_GPIO31) }, + { MP_ROM_QSTR(MP_QSTR_GP32), MP_ROM_PTR(&pin_GPIO32) }, + { MP_ROM_QSTR(MP_QSTR_GP33), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_GP34), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_GP35), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_GP36), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_GP37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_GP38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_GP39), MP_ROM_PTR(&pin_GPIO39) }, + + { MP_ROM_QSTR(MP_QSTR_GP40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_GPIO40) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP40_A0), MP_ROM_PTR(&pin_GPIO40) }, + + { MP_ROM_QSTR(MP_QSTR_GP41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_GPIO41) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP41_A1), MP_ROM_PTR(&pin_GPIO41) }, + + { MP_ROM_QSTR(MP_QSTR_GP42), MP_ROM_PTR(&pin_GPIO42) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A2), MP_ROM_PTR(&pin_GPIO42) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP42_A2), MP_ROM_PTR(&pin_GPIO42) }, + + { MP_ROM_QSTR(MP_QSTR_GP43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A3), MP_ROM_PTR(&pin_GPIO43) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP43_A3), MP_ROM_PTR(&pin_GPIO43) }, + + { MP_ROM_QSTR(MP_QSTR_GP44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A4), MP_ROM_PTR(&pin_GPIO44) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP44_A4), MP_ROM_PTR(&pin_GPIO44) }, + + { MP_ROM_QSTR(MP_QSTR_GP45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A5), MP_ROM_PTR(&pin_GPIO45) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP45_A5), MP_ROM_PTR(&pin_GPIO45) }, + + { MP_ROM_QSTR(MP_QSTR_GP46), MP_ROM_PTR(&pin_GPIO46) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A6), MP_ROM_PTR(&pin_GPIO46) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP46_A6), MP_ROM_PTR(&pin_GPIO46) }, + + { MP_ROM_QSTR(MP_QSTR_GP47), MP_ROM_PTR(&pin_GPIO47) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_A7), MP_ROM_PTR(&pin_GPIO47) }, + { MP_OBJ_NEW_QSTR(MP_QSTR_GP47_A7), MP_ROM_PTR(&pin_GPIO47) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/raspberrypi/common-hal/alarm/__init__.c b/ports/raspberrypi/common-hal/alarm/__init__.c index c47ddc3150515..a72b3a368d407 100644 --- a/ports/raspberrypi/common-hal/alarm/__init__.c +++ b/ports/raspberrypi/common-hal/alarm/__init__.c @@ -197,7 +197,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala _setup_sleep_alarms(true, n_alarms, alarms); } -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { bool timealarm_set = alarm_time_timealarm_is_set(); #if CIRCUITPY_CYW43 diff --git a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c index 327bec8de7280..b436469d09098 100644 --- a/ports/raspberrypi/common-hal/audiobusio/PDMIn.c +++ b/ports/raspberrypi/common-hal/audiobusio/PDMIn.c @@ -43,7 +43,7 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, // Use the state machine to manage pins. common_hal_rp2pio_statemachine_construct(&self->state_machine, pdmin, MP_ARRAY_SIZE(pdmin), - sample_rate * 32 * 2, // Frequency based on sample rate + sample_rate * OVERSAMPLING * 2, // Frequency based on sample rate NULL, 0, NULL, 0, // may_exec NULL, 1, PIO_PINMASK32_NONE, PIO_PINMASK32_ALL, // out pin @@ -64,7 +64,8 @@ void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, PIO_FIFO_TYPE_DEFAULT, PIO_MOV_STATUS_DEFAULT, PIO_MOV_N_DEFAULT); uint32_t actual_frequency = common_hal_rp2pio_statemachine_get_frequency(&self->state_machine); - if (actual_frequency < MIN_MIC_CLOCK) { + if (actual_frequency < 2 * MIN_MIC_CLOCK) { // 2 PIO samples per audio clock + common_hal_audiobusio_pdmin_deinit(self); mp_raise_ValueError(MP_ERROR_TEXT("sampling rate out of range")); } diff --git a/ports/raspberrypi/common-hal/busio/I2C.c b/ports/raspberrypi/common-hal/busio/I2C.c index 0f7e023f0e9c5..1441a2a7a0668 100644 --- a/ports/raspberrypi/common-hal/busio/I2C.c +++ b/ports/raspberrypi/common-hal/busio/I2C.c @@ -86,7 +86,7 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, // // Do not use the default supplied clock stretching timeout here. // It is too short for some devices. Use the busio timeout instead. - shared_module_bitbangio_i2c_construct(&self->bitbangio_i2c, scl, sda, + shared_module_bitbangio_i2c_construct(&self->bitbangio_i2c, MP_OBJ_FROM_PTR(scl), MP_OBJ_FROM_PTR(sda), frequency, BUS_TIMEOUT_US); self->baudrate = i2c_init(self->peripheral, frequency); @@ -144,7 +144,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { if (len == 0) { // The RP2040 I2C peripheral will not perform 0 byte writes. @@ -174,20 +174,20 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, } switch (result) { case PICO_ERROR_GENERIC: - return MP_ENODEV; + return -MP_ENODEV; case PICO_ERROR_TIMEOUT: - return MP_ETIMEDOUT; + return -MP_ETIMEDOUT; default: - return MP_EIO; + return -MP_EIO; } } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { size_t result = i2c_read_timeout_us(self->peripheral, addr, data, len, false, BUS_TIMEOUT_US); if (result == len) { @@ -195,17 +195,17 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, } switch (result) { case PICO_ERROR_GENERIC: - return MP_ENODEV; + return -MP_ENODEV; case PICO_ERROR_TIMEOUT: - return MP_ETIMEDOUT; + return -MP_ETIMEDOUT; default: - return MP_EIO; + return -MP_EIO; } } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/raspberrypi/common-hal/busio/SPI.c b/ports/raspberrypi/common-hal/busio/SPI.c index d20bc4d7d10aa..aeb06d919ae45 100644 --- a/ports/raspberrypi/common-hal/busio/SPI.c +++ b/ports/raspberrypi/common-hal/busio/SPI.c @@ -19,24 +19,14 @@ #define NO_INSTANCE 0xff -static bool never_reset_spi[2]; -static spi_inst_t *spi[2] = {spi0, spi1}; - -void reset_spi(void) { - for (size_t i = 0; i < 2; i++) { - if (never_reset_spi[i]) { - continue; - } - - spi_deinit(spi[i]); - } -} - void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { size_t instance_index = NO_INSTANCE; + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + if (half_duplex) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_half_duplex); } @@ -96,8 +86,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, } void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset_spi[spi_get_index(self->peripheral)] = true; - common_hal_never_reset_pin(self->clock); common_hal_never_reset_pin(self->MOSI); common_hal_never_reset_pin(self->MISO); @@ -107,17 +95,21 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->clock == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->clock = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; } - never_reset_spi[spi_get_index(self->peripheral)] = false; spi_deinit(self->peripheral); common_hal_reset_pin(self->clock); common_hal_reset_pin(self->MOSI); common_hal_reset_pin(self->MISO); - self->clock = NULL; + + common_hal_busio_spi_mark_deinit(self); } bool common_hal_busio_spi_configure(busio_spi_obj_t *self, @@ -182,7 +174,10 @@ static bool _transfer(busio_spi_obj_t *self, chan_tx = dma_claim_unused_channel(false); chan_rx = dma_claim_unused_channel(false); } - bool use_dma = chan_rx >= 0 && chan_tx >= 0; + bool has_dma_channels = chan_rx >= 0 && chan_tx >= 0; + // Only use DMA if both data buffers are in SRAM. Otherwise, we'll stall the DMA with PSRAM or flash cache misses. + bool data_in_sram = data_in >= (uint8_t *)SRAM_BASE && data_out >= (uint8_t *)SRAM_BASE; + bool use_dma = has_dma_channels && data_in_sram; if (use_dma) { dma_channel_config c = dma_channel_get_default_config(chan_tx); channel_config_set_transfer_data_size(&c, DMA_SIZE_8); diff --git a/ports/raspberrypi/common-hal/busio/SPI.h b/ports/raspberrypi/common-hal/busio/SPI.h index 8510eb7693ae2..3d43c1eff0072 100644 --- a/ports/raspberrypi/common-hal/busio/SPI.h +++ b/ports/raspberrypi/common-hal/busio/SPI.h @@ -25,5 +25,3 @@ typedef struct { uint8_t phase; uint8_t bits; } busio_spi_obj_t; - -void reset_spi(void); diff --git a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c index d1c92eea9988f..a40f6e26608a9 100644 --- a/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c +++ b/ports/raspberrypi/common-hal/i2ctarget/I2CTarget.c @@ -85,6 +85,13 @@ void common_hal_i2ctarget_i2c_target_deinit(i2ctarget_i2c_target_obj_t *self) { } int common_hal_i2ctarget_i2c_target_is_addressed(i2ctarget_i2c_target_obj_t *self, uint8_t *address, bool *is_read, bool *is_restart) { + // Interrupt bits must be cleared by software. Clear the STOP and + // RESTART interrupt bits after an I2C transaction finishes. + if (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) { + self->peripheral->hw->clr_stop_det; + self->peripheral->hw->clr_restart_det; + } + if (!((self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_RX_FULL_BITS) || (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_RD_REQ_BITS))) { return 0; } @@ -98,12 +105,20 @@ int common_hal_i2ctarget_i2c_target_is_addressed(i2ctarget_i2c_target_obj_t *sel } int common_hal_i2ctarget_i2c_target_read_byte(i2ctarget_i2c_target_obj_t *self, uint8_t *data) { - if (self->peripheral->hw->status & I2C_IC_STATUS_RFNE_BITS) { - *data = (uint8_t)self->peripheral->hw->data_cmd; - return 1; - } else { - return 0; + // Wait for data to arrive or a STOP condition indicating the transfer is over. + // Without this wait, the CPU can drain the FIFO faster than bytes arrive on the + // I2C bus, causing transfers to be split into multiple partial reads. + for (int t = 0; t < 100; t++) { + if (self->peripheral->hw->status & I2C_IC_STATUS_RFNE_BITS) { + *data = (uint8_t)self->peripheral->hw->data_cmd; + return 1; + } + if (self->peripheral->hw->raw_intr_stat & I2C_IC_INTR_STAT_R_STOP_DET_BITS) { + break; + } + mp_hal_delay_us(10); } + return 0; } int common_hal_i2ctarget_i2c_target_write_byte(i2ctarget_i2c_target_obj_t *self, uint8_t data) { diff --git a/ports/raspberrypi/common-hal/mcp4822/MCP4822.c b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c new file mode 100644 index 0000000000000..7a8ad7d4df29c --- /dev/null +++ b/ports/raspberrypi/common-hal/mcp4822/MCP4822.c @@ -0,0 +1,286 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT +// +// MCP4822 dual-channel 12-bit SPI DAC audio output. +// Uses PIO + DMA for non-blocking audio playback, mirroring audiobusio.I2SOut. + +#include +#include + +#include "mpconfigport.h" + +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "common-hal/mcp4822/MCP4822.h" +#include "shared-bindings/mcp4822/MCP4822.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/audiocore/__init__.h" +#include "bindings/rp2pio/StateMachine.h" + +// ───────────────────────────────────────────────────────────────────────────── +// PIO program for MCP4822 SPI DAC +// ───────────────────────────────────────────────────────────────────────────── +// +// Pin assignment: +// OUT pin (1) = MOSI — serial data out +// SET pins (N) = MOSI through CS — for CS control & command-bit injection +// SIDE-SET pin (1) = SCK — serial clock +// +// SET PINS bit mapping (bit0=MOSI, ..., bit N=CS): +// 0 = CS low, MOSI low 1 = CS low, MOSI high +// (1 << cs_bit_pos) = CS high, MOSI low +// +// SIDE-SET (1 pin, SCK): side 0 = SCK low, side 1 = SCK high +// +// MCP4822 16-bit command word: +// [15] channel (0=A, 1=B) [14] don't care [13] gain (1=1x, 0=2x) +// [12] output enable (1) [11:0] 12-bit data +// +// DMA feeds unsigned 16-bit audio samples. RP2040 narrow-write replication +// fills both halves of the 32-bit PIO FIFO entry with the same value, +// giving mono→stereo for free. +// +// The PIO pulls 32 bits, then sends two SPI transactions: +// Channel A: cmd nibble, then all 16 sample bits from upper half-word +// Channel B: cmd nibble, then all 16 sample bits from lower half-word +// The MCP4822 captures exactly 16 bits per CS frame (4 cmd + 12 data), +// so only the top 12 of the 16 sample bits become DAC data. The bottom +// 4 sample bits clock out harmlessly after the DAC has latched. +// This gives correct 16-bit → 12-bit scaling (effectively sample >> 4). +// +// PIO instruction encoding with .side_set 1 (no opt): +// [15:13] opcode [12] side-set [11:8] delay [7:0] operands +// +// Total: 26 instructions, 86 PIO clocks per audio sample. +// ───────────────────────────────────────────────────────────────────────────── + +static const uint16_t mcp4822_pio_program[] = { + // side SCK + // 0: pull noblock side 0 ; Get 32 bits or keep X if FIFO empty + 0x8080, + // 1: mov x, osr side 0 ; Save for pull-noblock fallback + 0xA027, + + // ── Channel A: command nibble 0b0011 (1x gain) ──────────────────────── + // 2: set pins, 0 side 0 ; CS low, MOSI=0 (bit15=0: channel A) + 0xE000, + // 3: nop side 1 ; SCK high — latch bit 15 + 0xB042, + // 4: set pins, 0 side 0 ; MOSI=0 (bit14=0: don't care) + 0xE000, + // 5: nop side 1 ; SCK high + 0xB042, + // 6: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) [patched for 2x] + 0xE001, + // 7: nop side 1 ; SCK high + 0xB042, + // 8: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) + 0xE001, + // 9: nop side 1 ; SCK high + 0xB042, + // 10: set y, 15 side 0 ; Loop counter: 16 sample bits + 0xE04F, + // 11: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopA) + 0x6001, + // 12: jmp y--, 11 side 1 ; SCK high, loop back + 0x108B, + // 13: set pins, 4 side 0 ; CS high — DAC A latches + 0xE004, + + // ── Channel B: command nibble 0b1011 (1x gain) ──────────────────────── + // 14: set pins, 1 side 0 ; CS low, MOSI=1 (bit15=1: channel B) + 0xE001, + // 15: nop side 1 ; SCK high + 0xB042, + // 16: set pins, 0 side 0 ; MOSI=0 (bit14=0) + 0xE000, + // 17: nop side 1 ; SCK high + 0xB042, + // 18: set pins, 1 side 0 ; MOSI=1 (bit13=1: gain 1x) [patched for 2x] + 0xE001, + // 19: nop side 1 ; SCK high + 0xB042, + // 20: set pins, 1 side 0 ; MOSI=1 (bit12=1: output active) + 0xE001, + // 21: nop side 1 ; SCK high + 0xB042, + // 22: set y, 15 side 0 ; Loop counter: 16 sample bits + 0xE04F, + // 23: out pins, 1 side 0 ; Data bit → MOSI; SCK low (bitloopB) + 0x6001, + // 24: jmp y--, 23 side 1 ; SCK high, loop back + 0x1097, + // 25: set pins, 4 side 0 ; CS high — DAC B latches + 0xE004, +}; + +// Clocks per sample: 2 (pull+mov) + 42 (chanA) + 42 (chanB) = 86 +// Per channel: 8(4 cmd bits × 2 clks) + 1(set y) + 32(16 bits × 2 clks) + 1(cs high) = 42 +#define MCP4822_CLOCKS_PER_SAMPLE 86 + +// MCP4822 gain bit (bit 13) position in the PIO program: +// Instruction 6 = channel A gain bit +// Instruction 18 = channel B gain bit +// 1x gain: set pins, 1 (0xE001) — bit 13 = 1 +// 2x gain: set pins, 0 (0xE000) — bit 13 = 0 +#define MCP4822_PIO_GAIN_INSTR_A 6 +#define MCP4822_PIO_GAIN_INSTR_B 18 +#define MCP4822_PIO_GAIN_1X 0xE001 // set pins, 1 +#define MCP4822_PIO_GAIN_2X 0xE000 // set pins, 0 + +void mcp4822_reset(void) { +} + +// Caller validates that pins are free. +void common_hal_mcp4822_mcp4822_construct(mcp4822_mcp4822_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs, uint8_t gain) { + + // The SET pin group spans from MOSI to CS. + // MOSI must have a lower GPIO number than CS, gap at most 4. + if (cs->number <= mosi->number || (cs->number - mosi->number) > 4) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q and %q"), MP_QSTR_CS, MP_QSTR_MOSI); + } + + uint8_t set_count = cs->number - mosi->number + 1; + + // Build a mutable copy of the PIO program and patch the gain bit + uint16_t program[MP_ARRAY_SIZE(mcp4822_pio_program)]; + memcpy(program, mcp4822_pio_program, sizeof(mcp4822_pio_program)); + uint16_t gain_instr = (gain == 2) ? MCP4822_PIO_GAIN_2X : MCP4822_PIO_GAIN_1X; + program[MCP4822_PIO_GAIN_INSTR_A] = gain_instr; + program[MCP4822_PIO_GAIN_INSTR_B] = gain_instr; + + // Initial SET pin state: CS high (bit at CS position), others low + uint32_t cs_bit_position = cs->number - mosi->number; + pio_pinmask32_t initial_set_state = PIO_PINMASK32_FROM_VALUE(1u << cs_bit_position); + pio_pinmask32_t initial_set_dir = PIO_PINMASK32_FROM_VALUE((1u << set_count) - 1); + + common_hal_rp2pio_statemachine_construct( + &self->state_machine, + program, MP_ARRAY_SIZE(mcp4822_pio_program), + 44100 * MCP4822_CLOCKS_PER_SAMPLE, // Initial frequency; play() adjusts + NULL, 0, // No init program + NULL, 0, // No may_exec + mosi, 1, // OUT: MOSI, 1 pin + PIO_PINMASK32_NONE, PIO_PINMASK32_ALL, // OUT state=low, dir=output + NULL, 0, // IN: none + PIO_PINMASK32_NONE, PIO_PINMASK32_NONE, // IN pulls: none + mosi, set_count, // SET: MOSI..CS + initial_set_state, initial_set_dir, // SET state (CS high), dir=output + clock, 1, false, // SIDE-SET: SCK, 1 pin, not pindirs + PIO_PINMASK32_NONE, // SIDE-SET state: SCK low + PIO_PINMASK32_FROM_VALUE(0x1), // SIDE-SET dir: output + false, // No sideset enable + NULL, PULL_NONE, // No jump pin + PIO_PINMASK_NONE, // No wait GPIO + true, // Exclusive pin use + false, 32, false, // OUT shift: no autopull, 32-bit, shift left + false, // Don't wait for txstall + false, 32, false, // IN shift (unused) + false, // Not user-interruptible + 0, -1, // Wrap: whole program + PIO_ANY_OFFSET, + PIO_FIFO_TYPE_DEFAULT, + PIO_MOV_STATUS_DEFAULT, + PIO_MOV_N_DEFAULT + ); + + self->playing = false; + audio_dma_init(&self->dma); +} + +bool common_hal_mcp4822_mcp4822_deinited(mcp4822_mcp4822_obj_t *self) { + return common_hal_rp2pio_statemachine_deinited(&self->state_machine); +} + +void common_hal_mcp4822_mcp4822_deinit(mcp4822_mcp4822_obj_t *self) { + if (common_hal_mcp4822_mcp4822_deinited(self)) { + return; + } + if (common_hal_mcp4822_mcp4822_get_playing(self)) { + common_hal_mcp4822_mcp4822_stop(self); + } + common_hal_rp2pio_statemachine_deinit(&self->state_machine); + audio_dma_deinit(&self->dma); +} + +void common_hal_mcp4822_mcp4822_play(mcp4822_mcp4822_obj_t *self, + mp_obj_t sample, bool loop) { + + if (common_hal_mcp4822_mcp4822_get_playing(self)) { + common_hal_mcp4822_mcp4822_stop(self); + } + + uint8_t bits_per_sample = audiosample_get_bits_per_sample(sample); + if (bits_per_sample < 16) { + bits_per_sample = 16; + } + + uint32_t sample_rate = audiosample_get_sample_rate(sample); + uint8_t channel_count = audiosample_get_channel_count(sample); + if (channel_count > 2) { + mp_raise_ValueError(MP_ERROR_TEXT("Too many channels in sample.")); + } + + // PIO clock = sample_rate × clocks_per_sample + common_hal_rp2pio_statemachine_set_frequency( + &self->state_machine, + (uint32_t)sample_rate * MCP4822_CLOCKS_PER_SAMPLE); + common_hal_rp2pio_statemachine_restart(&self->state_machine); + + audio_dma_result result = audio_dma_setup_playback( + &self->dma, + sample, + loop, + false, // single_channel_output + 0, // audio_channel + false, // output_signed = false (unsigned for MCP4822) + bits_per_sample, // output_resolution + (uint32_t)&self->state_machine.pio->txf[self->state_machine.state_machine], + self->state_machine.tx_dreq, + false); // swap_channel + + if (result == AUDIO_DMA_DMA_BUSY) { + common_hal_mcp4822_mcp4822_stop(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("No DMA channel found")); + } else if (result == AUDIO_DMA_MEMORY_ERROR) { + common_hal_mcp4822_mcp4822_stop(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("Unable to allocate buffers for signed conversion")); + } else if (result == AUDIO_DMA_SOURCE_ERROR) { + common_hal_mcp4822_mcp4822_stop(self); + mp_raise_RuntimeError(MP_ERROR_TEXT("Audio source error")); + } + + self->playing = true; +} + +void common_hal_mcp4822_mcp4822_pause(mcp4822_mcp4822_obj_t *self) { + audio_dma_pause(&self->dma); +} + +void common_hal_mcp4822_mcp4822_resume(mcp4822_mcp4822_obj_t *self) { + audio_dma_resume(&self->dma); +} + +bool common_hal_mcp4822_mcp4822_get_paused(mcp4822_mcp4822_obj_t *self) { + return audio_dma_get_paused(&self->dma); +} + +void common_hal_mcp4822_mcp4822_stop(mcp4822_mcp4822_obj_t *self) { + audio_dma_stop(&self->dma); + common_hal_rp2pio_statemachine_stop(&self->state_machine); + self->playing = false; +} + +bool common_hal_mcp4822_mcp4822_get_playing(mcp4822_mcp4822_obj_t *self) { + bool playing = audio_dma_get_playing(&self->dma); + if (!playing && self->playing) { + common_hal_mcp4822_mcp4822_stop(self); + } + return playing; +} diff --git a/ports/raspberrypi/common-hal/mcp4822/MCP4822.h b/ports/raspberrypi/common-hal/mcp4822/MCP4822.h new file mode 100644 index 0000000000000..53c8e862b63c5 --- /dev/null +++ b/ports/raspberrypi/common-hal/mcp4822/MCP4822.h @@ -0,0 +1,22 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/rp2pio/StateMachine.h" + +#include "audio_dma.h" +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + rp2pio_statemachine_obj_t state_machine; + audio_dma_t dma; + bool playing; +} mcp4822_mcp4822_obj_t; + +void mcp4822_reset(void); diff --git a/ports/raspberrypi/common-hal/mcp4822/__init__.c b/ports/raspberrypi/common-hal/mcp4822/__init__.c new file mode 100644 index 0000000000000..48981fc88a713 --- /dev/null +++ b/ports/raspberrypi/common-hal/mcp4822/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +// No module-level init needed. diff --git a/ports/raspberrypi/common-hal/os/__init__.c b/ports/raspberrypi/common-hal/os/__init__.c index 616bb8d8c7929..d2a8da4caefdd 100644 --- a/ports/raspberrypi/common-hal/os/__init__.c +++ b/ports/raspberrypi/common-hal/os/__init__.c @@ -18,35 +18,159 @@ #include +#ifdef HAS_RP2350_TRNG +#include "hardware/structs/trng.h" +#include "hardware/sync.h" +#endif + // NIST Special Publication 800-90B (draft) recommends several extractors, // including the SHA hash family and states that if the amount of entropy input // is twice the number of bits output from them, that output can be considered -// essentially fully random. If every RANDOM_SAFETY_MARGIN bits from -// `rosc_hw->randombit` have at least 1 bit of entropy, then this criterion is met. +// essentially fully random. +// +// This works by seeding `random_state` with entropy from hardware sources +// (SHA-256 as the conditioning function), then using that state as a counter +// input (SHA-256 as a CSPRNG), re-seeding at least every 256 blocks (8kB). // -// This works by seeding the `random_state` with plenty of random bits (SHA256 -// as entropy harvesting function), then using that state it as a counter input -// (SHA256 as a CSPRNG), re-seeding at least every 256 blocks (8kB). +// On RP2350, entropy comes from both the dedicated TRNG peripheral and the +// ROSC. On RP2040, the ROSC is the only available source. // // In practice, `PractRand` doesn't detect any gross problems with the output // random numbers on samples of 1 to 8 megabytes, no matter the setting of -// RANDOM_SAFETY_MARGIN. (it does detect "unusual" results from time to time, +// ROSC_SAFETY_MARGIN. (it does detect "unusual" results from time to time, // as it will with any RNG) -#define RANDOM_SAFETY_MARGIN (4) + +// Number of ROSC collection rounds on RP2040. Each round feeds +// SHA256_BLOCK_SIZE bytes into the hash; we do 2*N rounds so the +// raw-to-output ratio satisfies 800-90B's 2:1 minimum. +#define ROSC_SAFETY_MARGIN (4) static BYTE random_state[SHA256_BLOCK_SIZE]; + +// Collect `count` bytes from the ROSC, one bit per read. +static void rosc_random_bytes(BYTE *buf, size_t count) { + for (size_t i = 0; i < count; i++) { + buf[i] = rosc_hw->randombit & 1; + for (int k = 0; k < 8; k++) { + buf[i] = (buf[i] << 1) ^ (rosc_hw->randombit & 1); + } + } +} + +#ifdef HAS_RP2350_TRNG + +// TRNG_DEBUG_CONTROL bypass bits: +// +// bit 1 VNC_BYPASS Von Neumann corrector +// bit 2 TRNG_CRNGT_BYPASS Continuous Random Number Generator Test +// bit 3 AUTO_CORRELATE_BYPASS Autocorrelation test +// +// We bypass Von Neumann and autocorrelation but keep CRNGT. +// +// Von Neumann (bypassed): ~4x throughput cost for bias removal. +// Redundant here because SHA-256 conditioning already handles +// biased input -- that's what the 2:1 oversampling ratio is for. +// +// Autocorrelation (bypassed): has a non-trivial false-positive rate +// at high sampling speeds and halts the TRNG until SW reset on +// failure. SHA-256 is not bothered by correlated input. ARM's own +// TZ-TRNG 90B reference configuration also bypasses it (0x0A). +// +// CRNGT (kept): compares consecutive 192-bit EHR outputs. Flags if +// identical -- false-positive rate 2^-192, throughput cost zero. +// This is our early warning for a stuck oscillator or a successful +// injection lock to a fixed state. +#define TRNG_BYPASS_BITS \ + (TRNG_TRNG_DEBUG_CONTROL_VNC_BYPASS_BITS | \ + TRNG_TRNG_DEBUG_CONTROL_AUTO_CORRELATE_BYPASS_BITS) + +// Collect 192 raw bits (6 x 32-bit words) from the TRNG. +// Returns false on CRNGT failure (consecutive identical EHR outputs). +// +// Holds PICO_SPINLOCK_ID_RAND (the SDK's lock for this peripheral) +// with interrupts disabled for the duration of the collection, which +// takes ~192 ROSC cycles (~24us at 8MHz). +static bool trng_collect_192(uint32_t out[6]) { + spin_lock_t *lock = spin_lock_instance(PICO_SPINLOCK_ID_RAND); + uint32_t save = spin_lock_blocking(lock); + + trng_hw->trng_debug_control = TRNG_BYPASS_BITS; + // One rng_clk cycle between samples. The SDK uses 0 here, but it + // also sets debug_control = -1u (full bypass). The behavior of + // sample_cnt1 = 0 with health tests still active is undocumented, + // so we use 1 to be safe. + trng_hw->sample_cnt1 = 1; + trng_hw->rnd_source_enable = 1; + trng_hw->rng_icr = 0xFFFFFFFF; + + while (trng_hw->trng_busy) { + } + + if (trng_hw->rng_isr & TRNG_RNG_ISR_CRNGT_ERR_BITS) { + // Drain ehr_data so the hardware starts a fresh collection. + // (Reading the last word clears the valid flag.) + for (int i = 0; i < 6; i++) { + (void)trng_hw->ehr_data[i]; + } + trng_hw->rng_icr = TRNG_RNG_ISR_CRNGT_ERR_BITS; + spin_unlock(lock, save); + return false; + } + + for (int i = 0; i < 6; i++) { + out[i] = trng_hw->ehr_data[i]; + } + + // Switch the inverter chain length for the next collection, using + // bits from the sample we just read. Only bits [1:0] matter -- they + // select one of four chain lengths, changing the ROSC frequency. + // This is borrowed from pico_rand's injection-locking countermeasure. + // (The SDK uses its PRNG state here instead of raw output; either + // works since the real defense is SHA-256 conditioning, not this.) + trng_hw->trng_config = out[0]; + + spin_unlock(lock, save); + return true; +} + +#endif // HAS_RP2350_TRNG + static void seed_random_bits(BYTE out[SHA256_BLOCK_SIZE]) { CRYAL_SHA256_CTX context; sha256_init(&context); - for (int i = 0; i < 2 * RANDOM_SAFETY_MARGIN; i++) { - for (int j = 0; j < SHA256_BLOCK_SIZE; j++) { - out[j] = rosc_hw->randombit & 1; - for (int k = 0; k < 8; k++) { - out[j] = (out[j] << 1) ^ (rosc_hw->randombit & 1); + + #ifdef HAS_RP2350_TRNG + // 384 bits from TRNG + 384 bits from ROSC = 768 bits into the hash, + // giving a 3:1 ratio over the 256-bit output (800-90B wants >= 2:1). + // Two independent sources so a failure in one doesn't zero the input. + + // TRNG: 2 x 192 bits. + for (int i = 0; i < 2; i++) { + uint32_t trng_buf[6] = {0}; + for (int attempt = 0; attempt < 3; attempt++) { + if (trng_collect_192(trng_buf)) { + break; } + // CRNGT failure. If all 3 retries fail, trng_buf stays zeroed + // and we rely entirely on the ROSC contribution below. } + sha256_update(&context, (const BYTE *)trng_buf, sizeof(trng_buf)); + } + + // ROSC: 2 x 24 bytes = 384 bits. + for (int i = 0; i < 2; i++) { + BYTE rosc_buf[24]; + rosc_random_bytes(rosc_buf, sizeof(rosc_buf)); + sha256_update(&context, rosc_buf, sizeof(rosc_buf)); + } + #else + // RP2040: ROSC is the only entropy source. + for (int i = 0; i < 2 * ROSC_SAFETY_MARGIN; i++) { + rosc_random_bytes(out, SHA256_BLOCK_SIZE); sha256_update(&context, out, SHA256_BLOCK_SIZE); } + #endif + sha256_final(&context, out); } @@ -61,10 +185,11 @@ static void get_random_bits(BYTE out[SHA256_BLOCK_SIZE]) { } bool common_hal_os_urandom(uint8_t *buffer, mp_uint_t length) { -#define ROSC_POWER_SAVE (1) // assume ROSC is not necessarily active all the time + #define ROSC_POWER_SAVE (1) // assume ROSC is not necessarily active all the time #if ROSC_POWER_SAVE uint32_t old_rosc_ctrl = rosc_hw->ctrl; - rosc_hw->ctrl = (old_rosc_ctrl & ~ROSC_CTRL_ENABLE_BITS) | (ROSC_CTRL_ENABLE_VALUE_ENABLE << 12); + rosc_hw->ctrl = (old_rosc_ctrl & ~ROSC_CTRL_ENABLE_BITS) + | (ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB); #endif while (length) { size_t n = MIN(length, SHA256_BLOCK_SIZE); diff --git a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c index 79ec315d497e4..2dc19558314cb 100644 --- a/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c +++ b/ports/raspberrypi/common-hal/picodvi/Framebuffer_RP2350.c @@ -54,15 +54,15 @@ #define SYNC_V1_H1 (TMDS_CTRL_11 | (TMDS_CTRL_00 << 10) | (TMDS_CTRL_00 << 20)) #define MODE_720_H_SYNC_POLARITY 0 -#define MODE_720_H_FRONT_PORCH 24 -#define MODE_720_H_SYNC_WIDTH 64 -#define MODE_720_H_BACK_PORCH 88 +#define MODE_720_H_FRONT_PORCH 8 +#define MODE_720_H_SYNC_WIDTH 32 +#define MODE_720_H_BACK_PORCH 40 #define MODE_720_H_ACTIVE_PIXELS 720 #define MODE_720_V_SYNC_POLARITY 0 #define MODE_720_V_FRONT_PORCH 3 #define MODE_720_V_SYNC_WIDTH 4 -#define MODE_720_V_BACK_PORCH 13 +#define MODE_720_V_BACK_PORCH 218 #define MODE_720_V_ACTIVE_LINES 400 #define MODE_640_H_SYNC_POLARITY 0 @@ -74,7 +74,7 @@ #define MODE_640_V_SYNC_POLARITY 0 #define MODE_640_V_FRONT_PORCH 10 #define MODE_640_V_SYNC_WIDTH 2 -#define MODE_640_V_BACK_PORCH 33 +#define MODE_640_V_BACK_PORCH 133 #define MODE_640_V_ACTIVE_LINES 480 #define MODE_720_V_TOTAL_LINES ( \ diff --git a/ports/raspberrypi/common-hal/picodvi/__init__.c b/ports/raspberrypi/common-hal/picodvi/__init__.c index e8f344852de96..c2d4b297b39ba 100644 --- a/ports/raspberrypi/common-hal/picodvi/__init__.c +++ b/ports/raspberrypi/common-hal/picodvi/__init__.c @@ -10,8 +10,8 @@ #include "shared-bindings/busio/I2C.h" #include "shared-bindings/board/__init__.h" #include "shared-module/displayio/__init__.h" -#include "shared-module/os/__init__.h" #include "supervisor/shared/safe_mode.h" +#include "supervisor/shared/settings.h" #include "py/gc.h" #include "py/runtime.h" #include "supervisor/port_heap.h" @@ -22,7 +22,7 @@ static bool picodvi_autoconstruct_enabled(mp_int_t *default_width, mp_int_t *def buf[0] = 0; // (any failure leaves the content of buf untouched: an empty nul-terminated string - (void)common_hal_os_getenv_str("CIRCUITPY_PICODVI_ENABLE", buf, sizeof(buf)); + (void)settings_get_str("CIRCUITPY_PICODVI_ENABLE", buf, sizeof(buf)); if (!strcasecmp(buf, "never")) { return false; @@ -106,10 +106,10 @@ void picodvi_autoconstruct(void) { mp_int_t color_depth = 8; mp_int_t rotation = 0; - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_WIDTH", &width); - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_HEIGHT", &height); - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_COLOR_DEPTH", &color_depth); - (void)common_hal_os_getenv_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); + (void)settings_get_int("CIRCUITPY_DISPLAY_WIDTH", &width); + (void)settings_get_int("CIRCUITPY_DISPLAY_HEIGHT", &height); + (void)settings_get_int("CIRCUITPY_DISPLAY_COLOR_DEPTH", &color_depth); + (void)settings_get_int("CIRCUITPY_DISPLAY_ROTATION", &rotation); if (height == 0) { switch (width) { diff --git a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c index 113bd1900dd88..d71ff60b2ebc3 100644 --- a/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c +++ b/ports/raspberrypi/common-hal/rotaryio/IncrementalEncoder.c @@ -43,6 +43,9 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { const mcu_pin_obj_t *pins[] = { pin_a, pin_b }; + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + // Start out with swapped to match behavior with other ports. self->swapped = true; if (!common_hal_rp2pio_pins_are_sequential(2, pins)) { @@ -89,6 +92,7 @@ void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencode } bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self) { + // Use the deinit state of the PIO state machine. return common_hal_rp2pio_statemachine_deinited(&self->state_machine); } @@ -100,6 +104,11 @@ void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_o common_hal_rp2pio_statemachine_deinit(&self->state_machine); } +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { + // Use the deinit state of the PIO state machine. + common_hal_rp2pio_statemachine_mark_deinit(&self->state_machine); +} + static void incrementalencoder_interrupt_handler(void *self_in) { rotaryio_incrementalencoder_obj_t *self = self_in; diff --git a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c index fefecf05ff715..71050677f225f 100644 --- a/ports/raspberrypi/common-hal/rp2pio/StateMachine.c +++ b/ports/raspberrypi/common-hal/rp2pio/StateMachine.c @@ -523,9 +523,10 @@ static void consider_instruction(introspect_t *state, uint16_t full_instruction, } } if (instruction == pio_instr_bits_wait) { - uint16_t wait_source = (full_instruction & 0x0060) >> 5; - uint16_t wait_index = (full_instruction & 0x001f) + state->inputs.pio_gpio_offset; - if (wait_source == 0 && !PIO_PINMASK_IS_SET(state->inputs.pins_we_use, wait_index)) { // GPIO + const uint16_t wait_source = (full_instruction & 0x0060) >> 5; + const uint16_t wait_index = full_instruction & 0x001f; + const uint16_t wait_pin = wait_index + state->inputs.pio_gpio_offset; + if (wait_source == 0 && !PIO_PINMASK_IS_SET(state->inputs.pins_we_use, wait_pin)) { // GPIO mp_raise_ValueError_varg(MP_ERROR_TEXT("%q[%u] uses extra pin"), what_program, i); } else if (wait_source == 1) { // Input pin if (!state->inputs.has_in_pin) { @@ -627,6 +628,9 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, int mov_status_type, int mov_status_n) { + // Ensure object starts in its deinit state. + common_hal_rp2pio_statemachine_mark_deinit(self); + // First, check that all pins are free OR already in use by any PIO if exclusive_pin_use is false. pio_pinmask_t pins_we_use = wait_gpio_mask; PIO_PINMASK_MERGE(pins_we_use, _check_pins_free(first_out_pin, out_pin_count, exclusive_pin_use)); @@ -743,7 +747,7 @@ void common_hal_rp2pio_statemachine_construct(rp2pio_statemachine_obj_t *self, mov_status_type, mov_status_n); if (!ok) { // indicate state machine never inited - self->state_machine = NUM_PIO_STATE_MACHINES; + common_hal_rp2pio_statemachine_mark_deinit(self); mp_raise_RuntimeError(MP_ERROR_TEXT("All state machines in use")); } } @@ -826,6 +830,10 @@ void common_hal_rp2pio_statemachine_deinit(rp2pio_statemachine_obj_t *self) { rp2pio_statemachine_deinit(self, false); } +void common_hal_rp2pio_statemachine_mark_deinit(rp2pio_statemachine_obj_t *self) { + self->state_machine = NUM_PIO_STATE_MACHINES; +} + void common_hal_rp2pio_statemachine_never_reset(rp2pio_statemachine_obj_t *self) { rp2pio_statemachine_never_reset(self->pio, self->state_machine); // TODO: never reset all the pins @@ -853,25 +861,64 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, // This implementation is based on SPI but varies because the tx and rx buffers // may be different lengths and occur at different times or speeds. - // Use DMA for large transfers if channels are available. - // Don't exceed FIFO size. - const size_t dma_min_size_threshold = self->fifo_depth; int chan_tx = -1; int chan_rx = -1; size_t len = MAX(out_len, in_len); bool tx = data_out != NULL; bool rx = data_in != NULL; - bool use_dma = len >= dma_min_size_threshold || swap_out || swap_in; + bool free_data_out = false; + bool free_data_in = false; + uint8_t *sram_data_out = (uint8_t *)data_out; + uint8_t *sram_data_in = data_in; + bool tx_fits_in_fifo = (out_len / out_stride_in_bytes) <= self->fifo_depth; + bool rx_fits_in_fifo = (in_len / in_stride_in_bytes) <= self->fifo_depth; + bool use_dma = !(tx_fits_in_fifo && rx_fits_in_fifo) || swap_out || swap_in; + if (use_dma) { - // Use DMA channels to service the two FIFOs + // We can only reliably use DMA for SRAM buffers. So, if we're given PSRAM buffers, + // then copy them to SRAM first. If we can't, then fail. + // Use DMA channels to service the two FIFOs. Fail if we can't allocate DMA channels. if (tx) { + if (data_out < (uint8_t *)SRAM_BASE) { + // Try to allocate a temporary buffer for DMA transfer + uint8_t *temp_buffer = (uint8_t *)port_malloc(len, true); + if (temp_buffer == NULL) { + mp_printf(&mp_plat_print, "Failed to allocate temporary buffer for DMA tx\n"); + return false; + } + memcpy(temp_buffer, data_out, len); + sram_data_out = temp_buffer; + free_data_out = true; + } chan_tx = dma_claim_unused_channel(false); // DMA allocation failed... if (chan_tx < 0) { + if (free_data_out) { + port_free(sram_data_out); + } + if (free_data_in) { + port_free(sram_data_in); + } return false; } } if (rx) { + if (data_in < (uint8_t *)SRAM_BASE) { + // Try to allocate a temporary buffer for DMA transfer + uint8_t *temp_buffer = (uint8_t *)port_malloc(len, true); + if (temp_buffer == NULL) { + mp_printf(&mp_plat_print, "Failed to allocate temporary buffer for DMA rx\n"); + if (chan_tx >= 0) { + dma_channel_unclaim(chan_tx); + } + if (free_data_out) { + port_free(sram_data_out); + } + return false; + } + sram_data_in = temp_buffer; + free_data_in = true; + } chan_rx = dma_claim_unused_channel(false); // DMA allocation failed... if (chan_rx < 0) { @@ -879,6 +926,12 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, if (chan_tx >= 0) { dma_channel_unclaim(chan_tx); } + if (free_data_out) { + port_free(sram_data_out); + } + if (free_data_in) { + port_free(sram_data_in); + } return false; } } @@ -910,7 +963,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, channel_config_set_bswap(&c, swap_out); dma_channel_configure(chan_tx, &c, tx_destination, - data_out, + sram_data_out, out_len / out_stride_in_bytes, false); channel_mask |= 1u << chan_tx; @@ -923,7 +976,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, channel_config_set_write_increment(&c, true); channel_config_set_bswap(&c, swap_in); dma_channel_configure(chan_rx, &c, - data_in, + sram_data_in, rx_source, in_len / in_stride_in_bytes, false); @@ -950,8 +1003,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, self->pio->fdebug = stall_mask; } - // If we have claimed only one channel successfully, we should release immediately. This also - // releases the DMA after use_dma has been done. + // Release the DMA channels after use_dma has been done. if (chan_rx >= 0) { dma_channel_unclaim(chan_rx); } @@ -960,31 +1012,31 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, } if (!use_dma && !(self->user_interruptible && mp_hal_is_interrupted())) { - // Use software for small transfers, or if couldn't claim two DMA channels + // Use software for small transfers size_t rx_remaining = in_len / in_stride_in_bytes; size_t tx_remaining = out_len / out_stride_in_bytes; while (rx_remaining || tx_remaining) { while (tx_remaining && !pio_sm_is_tx_fifo_full(self->pio, self->state_machine)) { if (out_stride_in_bytes == 1) { - *tx_destination = *data_out; + *tx_destination = *sram_data_out; } else if (out_stride_in_bytes == 2) { - *((uint16_t *)tx_destination) = *((uint16_t *)data_out); + *((uint16_t *)tx_destination) = *((uint16_t *)sram_data_out); } else if (out_stride_in_bytes == 4) { - *((uint32_t *)tx_destination) = *((uint32_t *)data_out); + *((uint32_t *)tx_destination) = *((uint32_t *)sram_data_out); } - data_out += out_stride_in_bytes; + sram_data_out += out_stride_in_bytes; --tx_remaining; } while (rx_remaining && !pio_sm_is_rx_fifo_empty(self->pio, self->state_machine)) { if (in_stride_in_bytes == 1) { - *data_in = (uint8_t)*rx_source; + *sram_data_in = (uint8_t)*rx_source; } else if (in_stride_in_bytes == 2) { - *((uint16_t *)data_in) = *((uint16_t *)rx_source); + *((uint16_t *)sram_data_in) = *((uint16_t *)rx_source); } else if (in_stride_in_bytes == 4) { - *((uint32_t *)data_in) = *((uint32_t *)rx_source); + *((uint32_t *)sram_data_in) = *((uint32_t *)rx_source); } - data_in += in_stride_in_bytes; + sram_data_in += in_stride_in_bytes; --rx_remaining; } RUN_BACKGROUND_TASKS; @@ -996,7 +1048,7 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, self->pio->fdebug = stall_mask; } // Wait for the state machine to finish transmitting the data we've queued - // up. + // up (either from the CPU or via DMA.) if (tx) { while (!pio_sm_is_tx_fifo_empty(self->pio, self->state_machine) || (self->wait_for_txstall && (self->pio->fdebug & stall_mask) == 0)) { @@ -1006,6 +1058,14 @@ static bool _transfer(rp2pio_statemachine_obj_t *self, } } } + if (free_data_out) { + port_free(sram_data_out); + } + if (free_data_in) { + // Copy the data from the SRAM buffer to the user PSRAM buffer. + memcpy(data_in, sram_data_in, len); + port_free(sram_data_in); + } return true; } diff --git a/ports/raspberrypi/common-hal/socketpool/Socket.c b/ports/raspberrypi/common-hal/socketpool/Socket.c index 018365def8c75..0543fd53dc3e5 100644 --- a/ports/raspberrypi/common-hal/socketpool/Socket.c +++ b/ports/raspberrypi/common-hal/socketpool/Socket.c @@ -89,7 +89,7 @@ static mp_obj_t socketpool_ip_addr_and_port_to_tuple(const ip_addr_t *addr, int // socket API. // Extension to lwIP error codes -// Matches lwIP 2.0.3 +// Matches lwIP 2.2.1 #undef _ERR_BADF #define _ERR_BADF -17 static const int error_lookup_table[] = { @@ -251,7 +251,7 @@ static void _lwip_tcp_err_unaccepted(void *arg, err_t err) { // because it's only ever used by lwIP if tcp_connect is called on the TCP PCB. socketpool_socket_obj_t *socket = (socketpool_socket_obj_t *)pcb->connected; - // Array is not volatile because thiss callback is executed within the lwIP context + // Array is not volatile because this callback is executed within the lwIP context uint8_t alloc = socket->incoming.connection.alloc; struct tcp_pcb **tcp_array = (struct tcp_pcb **)lwip_socket_incoming_array(socket); @@ -473,7 +473,12 @@ static mp_uint_t lwip_tcp_send(socketpool_socket_obj_t *socket, const byte *buf, MICROPY_PY_LWIP_ENTER - u16_t available = tcp_sndbuf(socket->pcb.tcp); + // If the socket is still connecting then don't let data be written to it. + // Otherwise, get the number of available bytes in the output buffer. + u16_t available = 0; + if (socket->state != STATE_CONNECTING) { + available = tcp_sndbuf(socket->pcb.tcp); + } if (available == 0) { // Non-blocking socket @@ -490,7 +495,8 @@ static mp_uint_t lwip_tcp_send(socketpool_socket_obj_t *socket, const byte *buf, // If peer fully closed socket, we would have socket->state set to ERR_RST (connection // reset) by error callback. // Avoid sending too small packets, so wait until at least 16 bytes available - while (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16) { + while (socket->state == STATE_CONNECTING + || (socket->state >= STATE_CONNECTED && (available = tcp_sndbuf(socket->pcb.tcp)) < 16)) { MICROPY_PY_LWIP_EXIT if (socket->timeout != (unsigned)-1 && mp_hal_ticks_ms() - start > socket->timeout) { *_errno = MP_ETIMEDOUT; @@ -531,9 +537,10 @@ static mp_uint_t lwip_tcp_send(socketpool_socket_obj_t *socket, const byte *buf, MICROPY_PY_LWIP_REENTER } - // If the output buffer is getting full then send the data to the lower layers - if (err == ERR_OK && tcp_sndbuf(socket->pcb.tcp) < TCP_SND_BUF / 4) { - err = tcp_output(socket->pcb.tcp); + // Use nagle algorithm to determine when to send segment buffer (can be + // disabled with TCP_NODELAY socket option) + if (err == ERR_OK) { + err = tcp_output_nagle(socket->pcb.tcp); } MICROPY_PY_LWIP_EXIT @@ -551,6 +558,12 @@ static mp_uint_t lwip_tcp_receive(socketpool_socket_obj_t *socket, byte *buf, mp // Check for any pending errors STREAM_ERROR_CHECK(socket); + if (socket->state == STATE_LISTENING) { + // original socket in listening state, not the accepted connection. + *_errno = MP_ENOTCONN; + return -1; + } + if (socket->incoming.pbuf == NULL) { // Non-blocking socket diff --git a/ports/raspberrypi/common-hal/usb_host/Port.c b/ports/raspberrypi/common-hal/usb_host/Port.c index 5439b39bf3aa3..b8a49725030ea 100644 --- a/ports/raspberrypi/common-hal/usb_host/Port.c +++ b/ports/raspberrypi/common-hal/usb_host/Port.c @@ -146,6 +146,8 @@ usb_host_port_obj_t *common_hal_usb_host_port_construct(const mcu_pin_obj_t *dp, self->base.type = &usb_host_port_type; self->dp = dp; self->dm = dm; + claim_pin(dp); + claim_pin(dm); PIO pio = pio_get_instance(pio_cfg.pio_tx_num); diff --git a/ports/raspberrypi/common-hal/wifi/Radio.c b/ports/raspberrypi/common-hal/wifi/Radio.c index 3c22c3548d191..73e7794ecc2dc 100644 --- a/ports/raspberrypi/common-hal/wifi/Radio.c +++ b/ports/raspberrypi/common-hal/wifi/Radio.c @@ -48,7 +48,7 @@ static inline void nw_put_le32(uint8_t *buf, uint32_t x) { buf[3] = x >> 24; } -NORETURN static void ro_attribute(qstr attr) { +MP_NORETURN static void ro_attribute(qstr attr) { mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q is read-only for this board"), attr); } diff --git a/ports/raspberrypi/common-hal/wifi/__init__.h b/ports/raspberrypi/common-hal/wifi/__init__.h index 73988e8437987..4005c48a3cde2 100644 --- a/ports/raspberrypi/common-hal/wifi/__init__.h +++ b/ports/raspberrypi/common-hal/wifi/__init__.h @@ -11,7 +11,7 @@ #include "lwip/ip_addr.h" void wifi_reset(void); -NORETURN void raise_cyw_error(int err); +MP_NORETURN void raise_cyw_error(int err); #define CHECK_CYW_RESULT(x) do { int res = (x); if (res != 0) raise_cyw_error(res); } while (0) void ipaddress_ipaddress_to_lwip(mp_obj_t ip_address, ip_addr_t *lwip_ip_address); diff --git a/ports/raspberrypi/lib/Pico-PIO-USB b/ports/raspberrypi/lib/Pico-PIO-USB index 032a469e79f6a..675543bcc9baa 160000 --- a/ports/raspberrypi/lib/Pico-PIO-USB +++ b/ports/raspberrypi/lib/Pico-PIO-USB @@ -1 +1 @@ -Subproject commit 032a469e79f6a4ba40760d7868e6db26e15002d7 +Subproject commit 675543bcc9baa8170f868ab7ba316d418dbcf41f diff --git a/ports/raspberrypi/lib/cyw43-driver b/ports/raspberrypi/lib/cyw43-driver index c1075d4bc4404..dd7568229f3bf 160000 --- a/ports/raspberrypi/lib/cyw43-driver +++ b/ports/raspberrypi/lib/cyw43-driver @@ -1 +1 @@ -Subproject commit c1075d4bc440422cf2b2fd12c64a1f53f77660ee +Subproject commit dd7568229f3bf7a37737b9e1ef250c26efe75b23 diff --git a/ports/raspberrypi/lwip_src/lwip_mem.c b/ports/raspberrypi/lwip_src/lwip_mem.c index 244f51e289bc9..23b185354b072 100644 --- a/ports/raspberrypi/lwip_src/lwip_mem.c +++ b/ports/raspberrypi/lwip_src/lwip_mem.c @@ -8,10 +8,12 @@ #include #include "lib/tlsf/tlsf.h" #include "lwip_mem.h" +#include "shared-bindings/microcontroller/__init__.h" #include "supervisor/port_heap.h" void *lwip_heap_malloc(size_t size) { - return port_malloc(size, true); + void *ptr = port_malloc(size, true); + return ptr; } void lwip_heap_free(void *ptr) { diff --git a/ports/raspberrypi/mpconfigport.h b/ports/raspberrypi/mpconfigport.h index 3eb576de39e67..c4253937c986c 100644 --- a/ports/raspberrypi/mpconfigport.h +++ b/ports/raspberrypi/mpconfigport.h @@ -36,10 +36,8 @@ #define CIRCUITPY_PROCESSOR_COUNT (2) -// For many RP2 boards BOOTSEL is not connected to a GPIO pin. -#ifndef CIRCUITPY_BOOT_BUTTON +// For RP2 boards we use a custom way to read BOOTSEL #define CIRCUITPY_BOOT_BUTTON_NO_GPIO (1) -#endif #if CIRCUITPY_USB_HOST #define CIRCUITPY_USB_HOST_INSTANCE 1 diff --git a/ports/raspberrypi/mpconfigport.mk b/ports/raspberrypi/mpconfigport.mk index 1404b2b06777c..90be1afd10922 100644 --- a/ports/raspberrypi/mpconfigport.mk +++ b/ports/raspberrypi/mpconfigport.mk @@ -11,11 +11,13 @@ CIRCUITPY_FLOPPYIO ?= 1 CIRCUITPY_FRAMEBUFFERIO ?= $(CIRCUITPY_DISPLAYIO) CIRCUITPY_FULL_BUILD ?= 1 CIRCUITPY_AUDIOMP3 ?= 1 +CIRCUITPY_AUDIOSPEED ?= 1 CIRCUITPY_BITOPS ?= 1 CIRCUITPY_HASHLIB ?= 1 CIRCUITPY_HASHLIB_MBEDTLS ?= 1 CIRCUITPY_IMAGECAPTURE ?= 1 CIRCUITPY_MAX3421E ?= 0 +CIRCUITPY_MCP4822 ?= 0 CIRCUITPY_MEMORYMAP ?= 1 CIRCUITPY_PWMIO ?= 1 CIRCUITPY_RGBMATRIX ?= $(CIRCUITPY_DISPLAYIO) @@ -30,6 +32,7 @@ CIRCUITPY_FREQUENCYIO = 0 # Use PWM internally CIRCUITPY_I2CTARGET = 1 +CIRCUITPY_I2CIOEXPANDER = 1 CIRCUITPY_NVM = 1 # Use PIO internally CIRCUITPY_PULSEIO ?= 1 @@ -59,6 +62,9 @@ CIRCUITPY_CYW43_INIT_DELAY ?= 1000 endif ifeq ($(CHIP_VARIANT),RP2350) +# RP2350 has PSRAM that is not DMA-capable +CIRCUITPY_ALL_MEMORY_DMA_CAPABLE = 0 + # This needs to be implemented. CIRCUITPY_ALARM = 0 # Default PICODVI on because it doesn't require much code in RAM to talk to HSTX. diff --git a/ports/raspberrypi/sdk b/ports/raspberrypi/sdk index 96b363a15598d..a1438dff1d38b 160000 --- a/ports/raspberrypi/sdk +++ b/ports/raspberrypi/sdk @@ -1 +1 @@ -Subproject commit 96b363a15598d0a17a77542ba63150b7d3fa5fd5 +Subproject commit a1438dff1d38bd9c65dbd693f0e5db4b9ae91779 diff --git a/ports/raspberrypi/supervisor/port.c b/ports/raspberrypi/supervisor/port.c index 4be42380491cb..0316e34ad951c 100644 --- a/ports/raspberrypi/supervisor/port.c +++ b/ports/raspberrypi/supervisor/port.c @@ -264,32 +264,51 @@ void port_heap_init(void) { void *port_malloc(size_t size, bool dma_capable) { if (!dma_capable && _psram_size > 0) { + common_hal_mcu_disable_interrupts(); void *block = tlsf_malloc(_psram_heap, size); + common_hal_mcu_enable_interrupts(); if (block) { return block; } } + common_hal_mcu_disable_interrupts(); void *block = tlsf_malloc(_heap, size); + common_hal_mcu_enable_interrupts(); return block; } void port_free(void *ptr) { + common_hal_mcu_disable_interrupts(); if (((size_t)ptr) < SRAM_BASE) { tlsf_free(_psram_heap, ptr); } else { tlsf_free(_heap, ptr); } + common_hal_mcu_enable_interrupts(); } void *port_realloc(void *ptr, size_t size, bool dma_capable) { if (_psram_size > 0 && ((ptr != NULL && ((size_t)ptr) < SRAM_BASE) || (ptr == NULL && !dma_capable))) { + common_hal_mcu_disable_interrupts(); void *block = tlsf_realloc(_psram_heap, ptr, size); + common_hal_mcu_enable_interrupts(); if (block) { return block; } } - return tlsf_realloc(_heap, ptr, size); + common_hal_mcu_disable_interrupts(); + void *new_ptr = tlsf_realloc(_heap, ptr, size); + common_hal_mcu_enable_interrupts(); + return new_ptr; +} + +#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE +bool port_buffer_is_dma_capable(const void *ptr) { + // For RP2350, DMA can only access SRAM, not PSRAM + // PSRAM addresses are below SRAM_BASE + return ptr != NULL && ((size_t)ptr) >= SRAM_BASE; } +#endif static bool max_size_walker(void *ptr, size_t size, int used, void *user) { size_t *max_size = (size_t *)user; @@ -403,7 +422,6 @@ safe_mode_t port_init(void) { void reset_port(void) { #if CIRCUITPY_BUSIO - reset_spi(); reset_uart(); #endif @@ -434,8 +452,6 @@ void reset_port(void) { #if CIRCUITPY_WIFI wifi_reset(); #endif - - reset_all_pins(); } void reset_to_bootloader(void) { @@ -572,7 +588,7 @@ void port_idle_until_interrupt(void) { /** * \brief Default interrupt handler for unused IRQs. */ -extern NORETURN void isr_hardfault(void); // provide a prototype to avoid a missing-prototypes diagnostic +extern MP_NORETURN void isr_hardfault(void); // provide a prototype to avoid a missing-prototypes diagnostic __attribute__((used)) void __not_in_flash_func(isr_hardfault)(void) { // Only safe mode from core 0 which is running CircuitPython. Core 1 faulting // should not be fatal to CP. (Fingers crossed.) @@ -584,7 +600,7 @@ __attribute__((used)) void __not_in_flash_func(isr_hardfault)(void) { } } -void port_yield(void) { +void port_task_yield(void) { #if CIRCUITPY_CYW43 cyw43_arch_poll(); #endif diff --git a/ports/renode/Makefile b/ports/renode/Makefile index 92541c03e77b9..c4ce66194e9d1 100644 --- a/ports/renode/Makefile +++ b/ports/renode/Makefile @@ -45,7 +45,9 @@ SRC_C += \ background.c \ mphalport.c \ -SRC_S_UPPER = supervisor/shared/cpu_regs.S +SRC_S = shared/runtime/gchelper_thumb1.s + +SRC_C += shared/runtime/gchelper_native.c OBJ = $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) @@ -54,7 +56,6 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) $(BUILD)/%.o: $(BUILD)/%.S diff --git a/ports/renode/common-hal/busio/I2C.c b/ports/renode/common-hal/busio/I2C.c index c94f63cb880e3..41649f180b3d9 100644 --- a/ports/renode/common-hal/busio/I2C.c +++ b/ports/renode/common-hal/busio/I2C.c @@ -44,19 +44,19 @@ bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return 0; } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { - return MP_EIO; + return -MP_EIO; } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - return MP_EIO; + return -MP_EIO; } void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { diff --git a/ports/renode/common-hal/busio/SPI.c b/ports/renode/common-hal/busio/SPI.c index 6f5f60506074f..1f66fc5ec4af1 100644 --- a/ports/renode/common-hal/busio/SPI.c +++ b/ports/renode/common-hal/busio/SPI.c @@ -21,6 +21,9 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return true; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; diff --git a/ports/renode/supervisor/port.c b/ports/renode/supervisor/port.c index d12ebd2efddb9..a40e222d34df3 100644 --- a/ports/renode/supervisor/port.c +++ b/ports/renode/supervisor/port.c @@ -210,7 +210,7 @@ __attribute__((used)) void HardFault_Handler(void) { } } -void port_yield(void) { +void port_task_yield(void) { } void port_boot_info(void) { diff --git a/ports/silabs/Makefile b/ports/silabs/Makefile index 58929dd498fed..810ab53f1f331 100644 --- a/ports/silabs/Makefile +++ b/ports/silabs/Makefile @@ -71,7 +71,7 @@ ifeq ($(DEBUG), 1) CFLAGS += -fno-inline -fno-ipa-sra -Og else CFLAGS += -DNDEBUG - OPTIMIZATION_FLAGS ?= -Os -fno-inline-functions + OPTIMIZATION_FLAGS ?= -Os CFLAGS += -g endif @@ -90,7 +90,10 @@ ifneq (,$(wildcard boards/$(BOARD)/sensor.c)) SRC_C += boards/$(BOARD)/sensor.c endif -SRC_S = boards/mp_efr32xg24_gchelper.s +AFLAGS = -mcpu=cortex-m33 -mthumb -mfpu=fpv5-sp-d16 -mfloat-abi=hard +SRC_S = shared/runtime/gchelper_thumb1.s + +SRC_C += shared/runtime/gchelper_native.c OBJ += $(PY_O) $(SUPERVISOR_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_COMMON_HAL_SHARED_MODULE_EXPANDED:.c=.o)) diff --git a/ports/silabs/common-hal/_bleio/Characteristic.c b/ports/silabs/common-hal/_bleio/Characteristic.c index 37fb792cd4d75..9ccc6ad9c364e 100644 --- a/ports/silabs/common-hal/_bleio/Characteristic.c +++ b/ports/silabs/common-hal/_bleio/Characteristic.c @@ -249,7 +249,7 @@ size_t common_hal_bleio_characteristic_get_value( return 0; } -// Get max length of charateristic +// Get max length of characteristic size_t common_hal_bleio_characteristic_get_max_length( bleio_characteristic_obj_t *self) { return self->max_length; @@ -373,7 +373,7 @@ void common_hal_bleio_characteristic_add_descriptor( // This indicates the new added descriptor shall be started. sc = sl_bt_gattdb_start_characteristic(gattdb_session, self->handle); if (SL_STATUS_OK != sc) { - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Start charateristic fail.")); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Start characteristic fail.")); return; } @@ -395,7 +395,7 @@ void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, const uint16_t conn_handle = bleio_connection_get_conn_handle( self->service->connection); - common_hal_bleio_check_connected(conn_handle); + bleio_check_connected(conn_handle); notify = 1; indicate = 0; if (notify) { diff --git a/ports/silabs/common-hal/_bleio/Connection.h b/ports/silabs/common-hal/_bleio/Connection.h index 56872024c932c..148caecba1101 100644 --- a/ports/silabs/common-hal/_bleio/Connection.h +++ b/ports/silabs/common-hal/_bleio/Connection.h @@ -85,6 +85,7 @@ typedef struct void bleio_connection_clear(bleio_connection_internal_t *self); +void bleio_check_connected(uint16_t conn_handle); uint16_t bleio_connection_get_conn_handle(bleio_connection_obj_t *self); mp_obj_t bleio_connection_new_from_internal( diff --git a/ports/silabs/common-hal/_bleio/PacketBuffer.c b/ports/silabs/common-hal/_bleio/PacketBuffer.c index dec833fe62f85..881cf1622eba7 100644 --- a/ports/silabs/common-hal/_bleio/PacketBuffer.c +++ b/ports/silabs/common-hal/_bleio/PacketBuffer.c @@ -145,7 +145,7 @@ void _common_hal_bleio_packet_buffer_construct( uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, - ble_event_handler_t static_handler_entry) { + ble_event_handler_t *static_handler_entry) { bleio_characteristic_properties_t temp_prop; self->characteristic = characteristic; diff --git a/ports/silabs/common-hal/_bleio/Service.c b/ports/silabs/common-hal/_bleio/Service.c index abc8ffe74edff..50f0c4593a67e 100644 --- a/ports/silabs/common-hal/_bleio/Service.c +++ b/ports/silabs/common-hal/_bleio/Service.c @@ -128,7 +128,7 @@ bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { return self->uuid; } -// Get tuple charateristic of service +// Get tuple characteristic of service mp_obj_tuple_t *common_hal_bleio_service_get_characteristics( bleio_service_obj_t *self) { return mp_obj_new_tuple(self->characteristic_list->len, @@ -210,19 +210,19 @@ void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, } if (SL_STATUS_OK != sc) { - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Add charateristic fail.")); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Add characteristic fail.")); } sc = sl_bt_gattdb_start_characteristic(gattdb_session, characteristic->handle); if (SL_STATUS_OK != sc) { - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Start charateristic fail.")); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Start characteristic fail.")); return; } sc = sl_bt_gattdb_commit(gattdb_session); if (SL_STATUS_OK != sc) { - mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Commit charateristic fail.")); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Commit characteristic fail.")); return; } mp_obj_list_append(self->characteristic_list, diff --git a/ports/silabs/common-hal/_bleio/__init__.c b/ports/silabs/common-hal/_bleio/__init__.c index c6ae8a0b606c8..4c5ce1f045d72 100644 --- a/ports/silabs/common-hal/_bleio/__init__.c +++ b/ports/silabs/common-hal/_bleio/__init__.c @@ -102,7 +102,7 @@ void check_ble_error(int error_code) { } } -void common_hal_bleio_check_connected(uint16_t conn_handle) { +void bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLEIO_HANDLE_INVALID) { mp_raise_ConnectionError(MP_ERROR_TEXT("Not connected")); } diff --git a/ports/silabs/common-hal/busio/I2C.c b/ports/silabs/common-hal/busio/I2C.c index ae18f561c502f..6c2b04d821d7c 100644 --- a/ports/silabs/common-hal/busio/I2C.c +++ b/ports/silabs/common-hal/busio/I2C.c @@ -145,7 +145,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { } // Write data to the device selected by address -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { I2C_TransferSeq_TypeDef seq; @@ -159,13 +159,13 @@ uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, ret = I2CSPM_Transfer(self->i2cspm, &seq); if (ret != i2cTransferDone) { - return MP_EIO; + return -MP_EIO; } return 0; } // Read into buffer from the device selected by address -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { @@ -180,13 +180,13 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, ret = I2CSPM_Transfer(self->i2cspm, &seq); if (ret != i2cTransferDone) { - return MP_EIO; + return -MP_EIO; } return 0; } // Write the bytes from out_data to the device selected by address, -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { @@ -204,7 +204,7 @@ uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, ret = I2CSPM_Transfer(self->i2cspm, &seq); if (ret != i2cTransferDone) { - return MP_EIO; + return -MP_EIO; } return 0; } diff --git a/ports/silabs/common-hal/busio/SPI.c b/ports/silabs/common-hal/busio/SPI.c index 74cfc69bfb2a0..fb3b7c4fd20c7 100644 --- a/ports/silabs/common-hal/busio/SPI.c +++ b/ports/silabs/common-hal/busio/SPI.c @@ -37,16 +37,6 @@ static SPIDRV_HandleData_t spidrv_eusart_handle; static SPIDRV_Init_t spidrv_eusart_init = SPIDRV_MASTER_EUSART1; static bool in_used = false; -static bool never_reset = false; - -// Reset SPI when reload -void spi_reset(void) { - if (!never_reset && in_used) { - SPIDRV_DeInit(&spidrv_eusart_handle); - in_used = false; - } - return; -} // Construct SPI protocol, this function init SPI peripheral void common_hal_busio_spi_construct(busio_spi_obj_t *self, @@ -61,6 +51,9 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, MP_ERROR_TEXT("Half duplex SPI is not implemented")); } + // Ensure the object starts in its deinit state. + common_hal_busio_spi_mark_deinit(self); + if ((sck != NULL) && (mosi != NULL) && (miso != NULL)) { if (sck->function_list[FN_EUSART1_SCLK] == 1 && miso->function_list[FN_EUSART1_RX] == 1 @@ -108,7 +101,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, // Never reset SPI when reload void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset = true; common_hal_never_reset_pin(self->mosi); common_hal_never_reset_pin(self->miso); common_hal_never_reset_pin(self->sck); @@ -119,6 +111,10 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->sck == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->sck = NULL; +} + // Deinit SPI obj void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { @@ -132,13 +128,14 @@ void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { } in_used = false; - self->sck = NULL; self->mosi = NULL; self->miso = NULL; self->handle = NULL; common_hal_reset_pin(self->mosi); common_hal_reset_pin(self->miso); common_hal_reset_pin(self->sck); + + common_hal_busio_spi_mark_deinit(self); } // Configures the SPI bus. The SPI object must be locked. diff --git a/ports/silabs/common-hal/microcontroller/__init__.c b/ports/silabs/common-hal/microcontroller/__init__.c index b1472fcf43725..7244af6619dd6 100644 --- a/ports/silabs/common-hal/microcontroller/__init__.c +++ b/ports/silabs/common-hal/microcontroller/__init__.c @@ -60,6 +60,9 @@ void common_hal_mcu_on_next_reset(mcu_runmode_t runmode) { } } +// CHIP_Reset does not return, but is not declared as such in the SDK. +void __attribute__ ((noreturn)) CHIP_Reset(void); + void common_hal_mcu_reset(void) { filesystem_flush(); // TODO: implement as part of flash improvements CHIP_Reset(); diff --git a/ports/silabs/supervisor/port.c b/ports/silabs/supervisor/port.c index 2409e907deac1..85a6e0f92a5ec 100644 --- a/ports/silabs/supervisor/port.c +++ b/ports/silabs/supervisor/port.c @@ -156,10 +156,7 @@ safe_mode_t port_init(void) { } void reset_port(void) { - reset_all_pins(); - #if CIRCUITPY_BUSIO - spi_reset(); uart_reset(); #endif diff --git a/ports/stm/Makefile b/ports/stm/Makefile index 9db283767e0ec..2ed3bddb5b7fb 100755 --- a/ports/stm/Makefile +++ b/ports/stm/Makefile @@ -38,8 +38,7 @@ ifeq ($(DEBUG), 1) CFLAGS += -fno-inline -fno-ipa-sra else CFLAGS += -DNDEBUG - OPTIMIZATION_FLAGS ?= -O2 -fno-inline-functions - CFLAGS += -ggdb3 + OPTIMIZATION_FLAGS ?= -O2 endif # to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk @@ -212,10 +211,12 @@ ifneq ($(CIRCUITPY_USB),0) endif endif -SRC_S_UPPER = supervisor/shared/cpu_regs.S SRC_S = \ + shared/runtime/gchelper_thumb1.s \ st_driver/cmsis_device_$(MCU_SERIES_LOWER)/Source/Templates/gcc/startup_$(MCU_VARIANT_LOWER).s +SRC_C += shared/runtime/gchelper_native.c + ifneq ($(FROZEN_MPY_DIR),) FROZEN_MPY_PY_FILES := $(shell find -L $(FROZEN_MPY_DIR) -type f -name '*.py') FROZEN_MPY_MPY_FILES := $(addprefix $(BUILD)/,$(FROZEN_MPY_PY_FILES:.py=.mpy)) @@ -229,7 +230,6 @@ OBJ += $(addprefix $(BUILD)/, $(SRC_LIBM:.c=.o)) endif OBJ += $(addprefix $(BUILD)/, $(SRC_CIRCUITPY_COMMON:.c=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_S:.s=.o)) -OBJ += $(addprefix $(BUILD)/, $(SRC_S_UPPER:.S=.o)) OBJ += $(addprefix $(BUILD)/, $(SRC_MOD:.c=.o)) $(BUILD)/$(FATFS_DIR)/ff.o: COPT += -Os diff --git a/ports/stm/boards/espruino_pico/README.md b/ports/stm/boards/espruino_pico/README.md index 86df1ad32a380..d930e9aeca30c 100644 --- a/ports/stm/boards/espruino_pico/README.md +++ b/ports/stm/boards/espruino_pico/README.md @@ -12,4 +12,4 @@ The Espruino Pico is normally updated via a bootloader activated by the Espruino - Restart the board. -To reinstall Espruino, follow the same steps with the latest Espruino Pico binary from espruino.com/binaries. This will reinstall the usual Espruino bootloader. You must un-short the BOOT0/BTN jumper to re-use the original Espruino Bootloader again. If you used a Pencil mark then you may need to use cleaning fluid and a small brush to totally clear out the graphite. +To reinstall Espruino, follow the same steps with the latest Espruino Pico binary from espruino.com/binaries. This will reinstall the usual Espruino bootloader. You must un-short the BOOT0/BTN jumper to reuse the original Espruino Bootloader again. If you used a Pencil mark then you may need to use cleaning fluid and a small brush to totally clear out the graphite. diff --git a/ports/stm/boards/espruino_wifi/README.MD b/ports/stm/boards/espruino_wifi/README.MD index cc78811f1c7e0..cebe99bf6d1a8 100644 --- a/ports/stm/boards/espruino_wifi/README.MD +++ b/ports/stm/boards/espruino_wifi/README.MD @@ -12,4 +12,4 @@ The Espruino Wifi is normally updated via a bootloader activated by the Espruino - Restart the board. -To reinstall Espruino, follow the same steps with the latest Espruino Wifi binary from espruino.com/binaries. This will reinstall the usual Espruino bootloader. You must un-short the BOOT0/BTN jumper to re-use the original Espruino Bootloader again. If you used a Pencil mark then you may need to use cleaning fluid and a small brush to totally clear out the graphite. +To reinstall Espruino, follow the same steps with the latest Espruino Wifi binary from espruino.com/binaries. This will reinstall the usual Espruino bootloader. You must un-short the BOOT0/BTN jumper to reuse the original Espruino Bootloader again. If you used a Pencil mark then you may need to use cleaning fluid and a small brush to totally clear out the graphite. diff --git a/ports/stm/boards/meowbit_v121/board.c b/ports/stm/boards/meowbit_v121/board.c index 181f433a372b9..c3c745f64a14d 100644 --- a/ports/stm/boards/meowbit_v121/board.c +++ b/ports/stm/boards/meowbit_v121/board.c @@ -55,9 +55,9 @@ void board_init(void) { busio_spi_obj_t *internal_spi = &supervisor_flash_spi_bus; common_hal_fourwire_fourwire_construct(bus, internal_spi, - &pin_PA08, // Command or data - &pin_PB12, // Chip select - &pin_PB10, // Reset + MP_OBJ_FROM_PTR(&pin_PA08), // Command or data + MP_OBJ_FROM_PTR(&pin_PB12), // Chip select + MP_OBJ_FROM_PTR(&pin_PB10), // Reset 24000000, // Baudrate 0, // Polarity 0); // Phase diff --git a/ports/stm/boards/meowbit_v121/mpconfigboard.mk b/ports/stm/boards/meowbit_v121/mpconfigboard.mk index 3a128fb8343cc..419f073b5ea2c 100644 --- a/ports/stm/boards/meowbit_v121/mpconfigboard.mk +++ b/ports/stm/boards/meowbit_v121/mpconfigboard.mk @@ -23,6 +23,7 @@ LD_FILE = boards/STM32F401xe_boot.ld # LD_FILE = boards/STM32F401xe_fs.ld CIRCUITPY_AESIO = 0 +CIRCUITPY_CODEOP = 0 CIRCUITPY_BITMAPFILTER = 0 CIRCUITPY_BITMAPTOOLS = 0 CIRCUITPY_BLEIO_HCI = 0 @@ -30,6 +31,7 @@ CIRCUITPY_EPAPERDISPLAY = 0 CIRCUITPY_FRAMEBUFFERIO = 0 CIRCUITPY_I2CDISPLAYBUS = 0 CIRCUITPY_KEYPAD_DEMUX = 0 +CIRCUITPY_PIXELMAP = 0 CIRCUITPY_SHARPDISPLAY = 0 CIRCUITPY_TILEPALETTEMAPPER = 0 CIRCUITPY_ULAB = 0 @@ -37,4 +39,6 @@ CIRCUITPY_ZLIB = 0 CIRCUITPY_STAGE = 1 +CIRCUITPY_DIGITALINOUT_PROTOCOL = 0 + FROZEN_MPY_DIRS += $(TOP)/frozen/circuitpython-stage/meowbit diff --git a/ports/stm/boards/sparkfun_stm32f405_micromod/mpconfigboard.mk b/ports/stm/boards/sparkfun_stm32f405_micromod/mpconfigboard.mk index d1df685c6764e..31c2b7e2874f9 100644 --- a/ports/stm/boards/sparkfun_stm32f405_micromod/mpconfigboard.mk +++ b/ports/stm/boards/sparkfun_stm32f405_micromod/mpconfigboard.mk @@ -1,5 +1,5 @@ USB_VID = 0X1B4F -USB_PID = 0x0027 +USB_PID = 0x0029 USB_PRODUCT = "SparkFun STM32 MicroMod Processor" USB_MANUFACTURER = "SparkFun Electronics" diff --git a/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk b/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk index 64be6f4f14242..c46a270df652a 100644 --- a/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk +++ b/ports/stm/boards/stm32f411ce_blackpill_with_flash/mpconfigboard.mk @@ -9,6 +9,7 @@ EXTERNAL_FLASH_DEVICES = GD25Q16C,W25Q16JVxQ,W25Q64FV,W25Q32JVxQ,W25Q64JVxQ LONGINT_IMPL = MPZ INTERNAL_FLASH_FILESYSTEM = 0 +OPTIMIZATION_FLAGS = -Os MCU_SERIES = F4 MCU_VARIANT = STM32F411xE diff --git a/ports/stm/common-hal/alarm/__init__.c b/ports/stm/common-hal/alarm/__init__.c index 3d6c4466882b4..1be8f8dc10d09 100644 --- a/ports/stm/common-hal/alarm/__init__.c +++ b/ports/stm/common-hal/alarm/__init__.c @@ -135,7 +135,7 @@ void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *ala _setup_sleep_alarms(true, n_alarms, alarms); } -void NORETURN common_hal_alarm_enter_deep_sleep(void) { +void MP_NORETURN common_hal_alarm_enter_deep_sleep(void) { alarm_set_wakeup_reason(STM_WAKEUP_UNDEF); alarm_pin_pinalarm_prepare_for_deep_sleep(); alarm_time_timealarm_prepare_for_deep_sleep(); diff --git a/ports/stm/common-hal/busio/I2C.c b/ports/stm/common-hal/busio/I2C.c index e6957989cc72c..7f7eb990e4d07 100644 --- a/ports/stm/common-hal/busio/I2C.c +++ b/ports/stm/common-hal/busio/I2C.c @@ -212,7 +212,7 @@ void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { self->has_lock = false; } -static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +static mp_negative_errno_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { HAL_StatusTypeDef result; if (!transmit_stop_bit) { @@ -234,19 +234,19 @@ static uint8_t _common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, result = HAL_I2C_Master_Transmit(&(self->handle), (uint16_t)(addr << 1), (uint8_t *)data, (uint16_t)len, 500); } - return result == HAL_OK ? 0 : MP_EIO; + return result == HAL_OK ? 0 : -MP_EIO; } -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { return _common_hal_busio_i2c_write(self, addr, data, len, true); } -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { if (!self->frame_in_prog) { return HAL_I2C_Master_Receive(&(self->handle), (uint16_t)(addr << 1), data, (uint16_t)len, 500) - == HAL_OK ? 0 : MP_EIO; + == HAL_OK ? 0 : -MP_EIO; } else { HAL_StatusTypeDef result = HAL_I2C_Master_Seq_Receive_IT(&(self->handle), (uint16_t)(addr << 1), (uint8_t *)data, @@ -259,9 +259,9 @@ uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, } } -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); + mp_negative_errno_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); if (result != 0) { return result; } diff --git a/ports/stm/common-hal/busio/SPI.c b/ports/stm/common-hal/busio/SPI.c index 98696271ca93a..24b65fbf54c0c 100644 --- a/ports/stm/common-hal/busio/SPI.c +++ b/ports/stm/common-hal/busio/SPI.c @@ -19,7 +19,6 @@ #define MAX_SPI 6 static bool reserved_spi[MAX_SPI]; -static bool never_reset_spi[MAX_SPI]; #define ALL_CLOCKS 0xFF static void spi_clock_enable(uint8_t mask); @@ -76,18 +75,6 @@ static uint32_t stm32_baud_to_spi_div(uint32_t baudrate, uint16_t *prescaler, ui return SPI_BAUDRATEPRESCALER_256; } -void spi_reset(void) { - uint16_t never_reset_mask = 0x00; - for (int i = 0; i < MAX_SPI; i++) { - if (!never_reset_spi[i]) { - reserved_spi[i] = false; - } else { - never_reset_mask |= 1 << i; - } - } - spi_clock_disable(ALL_CLOCKS & ~(never_reset_mask)); -} - static const mcu_periph_obj_t *find_pin_function(const mcu_periph_obj_t *table, size_t sz, const mcu_pin_obj_t *pin, int periph_index) { for (size_t i = 0; i < sz; i++, table++) { if (periph_index == table->periph_index && pin == table->pin) { @@ -149,6 +136,10 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *sck, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { + // Ensure the object starts in its deinit state before check_pins sets + // self->sck, self->mosi, and self->miso. + common_hal_busio_spi_mark_deinit(self); + int periph_index = check_pins(self, sck, mosi, miso); SPI_TypeDef *SPIx = mcu_spi_banks[periph_index - 1]; @@ -224,7 +215,6 @@ void common_hal_busio_spi_construct(busio_spi_obj_t *self, void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { - never_reset_spi[self->sck->periph_index - 1] = true; never_reset_pin_number(self->sck->pin->port, self->sck->pin->number); if (self->mosi != NULL) { never_reset_pin_number(self->mosi->pin->port, self->mosi->pin->number); @@ -238,13 +228,16 @@ bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { return self->sck == NULL; } +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + self->sck = NULL; +} + void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { if (common_hal_busio_spi_deinited(self)) { return; } spi_clock_disable(1 << (self->sck->periph_index - 1)); reserved_spi[self->sck->periph_index - 1] = false; - never_reset_spi[self->sck->periph_index - 1] = false; reset_pin_number(self->sck->pin->port, self->sck->pin->number); if (self->mosi != NULL) { diff --git a/ports/stm/common-hal/sdioio/SDCard.c b/ports/stm/common-hal/sdioio/SDCard.c index 92e552c2e8984..2d24f24d82504 100644 --- a/ports/stm/common-hal/sdioio/SDCard.c +++ b/ports/stm/common-hal/sdioio/SDCard.c @@ -6,6 +6,8 @@ #include #include "shared-bindings/sdioio/SDCard.h" + +#include "extmod/vfs.h" #include "py/mperrno.h" #include "py/runtime.h" @@ -239,9 +241,10 @@ static void check_for_deinit(sdioio_sdcard_obj_t *self) { } } -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { - check_for_deinit(self); - check_whole_block(bufinfo); +// Native function for VFS blockdev layer +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); self->state_programming = true; common_hal_mcu_disable_interrupts(); @@ -251,17 +254,27 @@ int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t sta #else uint32_t time_out = 1000; #endif - HAL_StatusTypeDef r = HAL_SD_WriteBlocks(&self->handle, bufinfo->buf, start_block, bufinfo->len / 512, time_out); + HAL_StatusTypeDef r = HAL_SD_WriteBlocks(&self->handle, buf, start_block, num_blocks, time_out); common_hal_mcu_enable_interrupts(); if (r != HAL_OK) { - return -EIO; + return -MP_EIO; } return 0; } -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { check_for_deinit(self); check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, + uint32_t start_block, uint32_t num_blocks) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); wait_write_complete(self); common_hal_mcu_disable_interrupts(); #ifdef STM32H750xx @@ -270,14 +283,48 @@ int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t star #else uint32_t time_out = 1000; #endif - HAL_StatusTypeDef r = HAL_SD_ReadBlocks(&self->handle, bufinfo->buf, start_block, bufinfo->len / 512, time_out); + HAL_StatusTypeDef r = HAL_SD_ReadBlocks(&self->handle, buf, start_block, num_blocks, time_out); common_hal_mcu_enable_interrupts(); if (r != HAL_OK) { - return -EIO; + return -MP_EIO; } return 0; } +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) { + check_for_deinit(self); + check_whole_block(bufinfo); + + uint32_t num_blocks = bufinfo->len / 512; + return sdioio_sdcard_readblocks(MP_OBJ_FROM_PTR(self), bufinfo->buf, + start_block, num_blocks); +} + +// Native function for VFS blockdev layer +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, + mp_int_t *out_value) { + sdioio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); + *out_value = 0; + + switch (cmd) { + case MP_BLOCKDEV_IOCTL_DEINIT: + case MP_BLOCKDEV_IOCTL_SYNC: + // SDIO operations are synchronous, no action needed + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: + *out_value = common_hal_sdioio_sdcard_get_count(self); + return true; + + case MP_BLOCKDEV_IOCTL_BLOCK_SIZE: + *out_value = 512; // SD cards use 512-byte sectors + return true; + + default: + return false; // Unsupported command + } +} + bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) { check_for_deinit(self); return true; diff --git a/ports/stm/mpconfigport.mk b/ports/stm/mpconfigport.mk index 83759fc5d2405..96724a5296090 100644 --- a/ports/stm/mpconfigport.mk +++ b/ports/stm/mpconfigport.mk @@ -10,6 +10,10 @@ ifeq ($(MCU_VARIANT),$(filter $(MCU_VARIANT),STM32F405xx STM32F407xx)) USB_NUM_ENDPOINT_PAIRS = 4 endif +ifeq ($(INTERNAL_FLASH_FILESYSTEM),1) + OPTIMIZATION_FLAGS ?= -Os +endif + ifeq ($(MCU_VARIANT),STM32F407xx) UF2_FAMILY_ID ?= 0x6d0922fa endif diff --git a/ports/stm/mpconfigport_nanbox.h b/ports/stm/mpconfigport_nanbox.h deleted file mode 100644 index 164850112e01f..0000000000000 --- a/ports/stm/mpconfigport_nanbox.h +++ /dev/null @@ -1,25 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Damien P. George -// -// SPDX-License-Identifier: MIT - -#pragma once - -// select nan-boxing object model -#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_D) - -// native emitters don't work with nan-boxing -#define MICROPY_EMIT_X86 (0) -#define MICROPY_EMIT_X64 (0) -#define MICROPY_EMIT_THUMB (0) -#define MICROPY_EMIT_ARM (0) - -#include - -typedef int64_t mp_int_t; -typedef uint64_t mp_uint_t; -#define UINT_FMT "%llu" -#define INT_FMT "%lld" - -#include diff --git a/ports/stm/peripherals/stm32l4/stm32l4r5xx/gpio.c b/ports/stm/peripherals/stm32l4/stm32l4r5xx/gpio.c index 18fe8b760a267..a4adc58e42a0a 100644 --- a/ports/stm/peripherals/stm32l4/stm32l4r5xx/gpio.c +++ b/ports/stm/peripherals/stm32l4/stm32l4r5xx/gpio.c @@ -17,7 +17,7 @@ void stm32_peripherals_gpio_init(void) { __HAL_RCC_GPIOF_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); - // These ports are not used on the Swan R5 but may need to be enabeld on other boards + // These ports are not used on the Swan R5 but may need to be enabled on other boards // __HAL_RCC_GPIOH_CLK_ENABLE(); // __HAL_RCC_GPIOI_CLK_ENABLE(); diff --git a/ports/stm/supervisor/port.c b/ports/stm/supervisor/port.c index c5a1685a7fddc..1ffc8656a888e 100644 --- a/ports/stm/supervisor/port.c +++ b/ports/stm/supervisor/port.c @@ -39,7 +39,7 @@ #include STM32_HAL_H -void NVIC_SystemReset(void) NORETURN; +void NVIC_SystemReset(void) MP_NORETURN; #if (CPY_STM32H7) || (CPY_STM32F7) #if defined(CIRCUITPY_HW_SDRAM_SIZE) @@ -302,14 +302,11 @@ void SysTick_Handler(void) { } void reset_port(void) { - reset_all_pins(); - #if CIRCUITPY_RTC rtc_reset(); #endif #if CIRCUITPY_BUSIO - spi_reset(); uart_reset(); #endif #if CIRCUITPY_SDIOIO diff --git a/ports/unix/Makefile b/ports/unix/Makefile index bb1f6a4740a35..32e3b44b2273a 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -27,6 +27,9 @@ FROZEN_MANIFEST ?= variants/manifest.py # This should be configured by the mpconfigvariant.mk PROG ?= micropython +# For use in test rules below +ABS_PROG = $(abspath $(BUILD)/$(PROG)) + # qstr definitions (must come before including py.mk) QSTR_DEFS += qstrdefsport.h QSTR_GLOBAL_DEPENDENCIES += $(VARIANT_DIR)/mpconfigvariant.h @@ -46,13 +49,18 @@ INC += -I$(BUILD) # compiler settings CWARN = -Wall -Werror -CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion +// CIRCUITPY-CHANGE: add -Wno-missing-field-initializers +CWARN += -Wextra -Wno-unused-parameter -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Wno-missing-field-initializers CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EXTRA) # Force the use of 64-bits for file sizes in C library functions on 32-bit platforms. # This option has no effect on 64-bit builds. CFLAGS += -D_FILE_OFFSET_BITS=64 +# Force the use of 64-bits for time_t in C library functions on 32-bit platforms. +# This option has no effect on 64-bit builds. +CFLAGS += -D_TIME_BITS=64 + # Debugging/Optimization ifdef DEBUG COPT ?= -Og @@ -139,7 +147,11 @@ ifeq ($(MICROPY_PY_SOCKET),1) CFLAGS += -DMICROPY_PY_SOCKET=1 endif ifeq ($(MICROPY_PY_THREAD),1) +ifeq ($(MICROPY_PY_THREAD_GIL),1) +CFLAGS += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=1 +else CFLAGS += -DMICROPY_PY_THREAD=1 -DMICROPY_PY_THREAD_GIL=0 +endif LDFLAGS += $(LIBPTHREAD) endif @@ -208,6 +220,8 @@ SRC_C += \ input.c \ alloc.c \ fatfs_port.c \ + shared-module/os/__init__.c \ + supervisor/shared/settings.c \ supervisor/stub/filesystem.c \ supervisor/stub/safe_mode.c \ supervisor/stub/stack.c \ @@ -223,6 +237,7 @@ $(BUILD)/supervisor/shared/translate/translate.o: $(HEADER_BUILD)/qstrdefs.gener SHARED_SRC_C += $(addprefix shared/,\ runtime/gchelper_generic.c \ + runtime/pyexec.c \ timeutils/timeutils.c \ $(SHARED_SRC_C_EXTRA) \ ) @@ -278,7 +293,7 @@ print-failures clean-failures: TEST_ARGS ?= test: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py $(TEST_ARGS) + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) diff --git a/ports/unix/README.md b/ports/unix/README.md index b7aa6e3fef76d..ee983a882cc83 100644 --- a/ports/unix/README.md +++ b/ports/unix/README.md @@ -72,6 +72,14 @@ To run the complete testsuite, use: $ make test +There are other make targets to interact with the testsuite: + + $ make test//int # Run all tests matching the pattern "int" + $ make test/ports/unix # Run all tests in ports/unix + $ make test-failures # Re-run only the failed tests + $ make print-failures # print the differences for failed tests + $ make clean-failures # delete the .exp and .out files from failed tests + The Unix port comes with a built-in package manager called `mip`, e.g.: $ ./build-standard/micropython -m mip install hmac @@ -155,3 +163,21 @@ The default compiler optimisation level is -Os, or -Og if `DEBUG=1` is set. Setting the variable `COPT` will explicitly set the optimisation level. For example `make [other arguments] COPT=-O0 DEBUG=1` will build a binary with no optimisations, assertions enabled, and debug symbols. + +### Sanitizers + +Sanitizers are extra runtime checks supported by gcc and clang. The CI process +supports building with the "undefined behavior" (UBSan) or "address" (ASan) +sanitizers. The script `tools/ci.sh` is the source of truth about how to build +and run in these modes. + +Several classes of checks are disabled via compiler flags: + +* In the undefined behavior sanitizer, checks based on the presence of the + `non_null` attribute are disabled because the code makes technically incorrect + calls like `memset(NULL, 0, 0)`. A future C standard is likely to permit such + calls. +* In the address sanitizer, `detect_stack_use_after_return` is disabled. This + check is intended to make sure locals in a "returned from" stack frame are not + used. However, this mode interferes with various assumptions that + MicroPython's stack checking, NLR, and GC rely on. diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 7f13f9756f177..49426f0f3e865 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -97,6 +97,7 @@ static const mp_rom_map_elem_t rawfile_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, { MP_ROM_QSTR(MP_QSTR_write1), MP_ROM_PTR(&mp_stream_write1_obj) }, { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, + { MP_ROM_QSTR(MP_QSTR_readinto1), MP_ROM_PTR(&mp_stream_readinto1_obj) }, { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&mp_stream_ioctl_obj) }, }; @@ -171,19 +172,34 @@ static void pairheap_test(size_t nops, int *ops) { if (mp_pairheap_is_empty(pairheap_lt, heap)) { mp_printf(&mp_plat_print, " -"); } else { - mp_printf(&mp_plat_print, " %d", mp_pairheap_peek(pairheap_lt, heap) - &node[0]); + mp_printf(&mp_plat_print, " %d", (int)(mp_pairheap_peek(pairheap_lt, heap) - &node[0])); ; } } mp_printf(&mp_plat_print, "\npop all:"); while (!mp_pairheap_is_empty(pairheap_lt, heap)) { - mp_printf(&mp_plat_print, " %d", mp_pairheap_peek(pairheap_lt, heap) - &node[0]); + mp_printf(&mp_plat_print, " %d", (int)(mp_pairheap_peek(pairheap_lt, heap) - &node[0])); ; heap = mp_pairheap_pop(pairheap_lt, heap); } mp_printf(&mp_plat_print, "\n"); } +// CIRCUITPY-CHANGE: not turned on in CircuitPython +#if MICROPY_SCHEDULER_STATIC_NODES +static mp_sched_node_t mp_coverage_sched_node; +static bool coverage_sched_function_continue; + +static void coverage_sched_function(mp_sched_node_t *node) { + (void)node; + mp_printf(&mp_plat_print, "scheduled function\n"); + if (coverage_sched_function_continue) { + // Re-scheduling node will cause it to run again next time scheduled functions are run + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + } +} +#endif + // function to run extra tests for things that can't be checked by scripts static mp_obj_t extra_coverage(void) { // mp_printf (used by ports that don't have a native printf) @@ -191,10 +207,22 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "# mp_printf\n"); mp_printf(&mp_plat_print, "%d %+d % d\n", -123, 123, 123); // sign mp_printf(&mp_plat_print, "%05d\n", -123); // negative number with zero padding - mp_printf(&mp_plat_print, "%ld\n", 123); // long - mp_printf(&mp_plat_print, "%lx\n", 0x123); // long hex - mp_printf(&mp_plat_print, "%X\n", 0x1abcdef); // capital hex - mp_printf(&mp_plat_print, "%.2s %.3s '%4.4s' '%5.5q' '%.3q'\n", "abc", "abc", "abc", MP_QSTR_True, MP_QSTR_True); // fixed string precision + mp_printf(&mp_plat_print, "%ld\n", 123l); // long + mp_printf(&mp_plat_print, "%lx\n", 0x123fl); // long hex + mp_printf(&mp_plat_print, "%lX\n", 0x123fl); // capital long hex + if (sizeof(mp_int_t) == 8) { + mp_printf(&mp_plat_print, "%llx\n", LLONG_MAX); // long long hex + mp_printf(&mp_plat_print, "%llX\n", LLONG_MAX); // capital long long hex + mp_printf(&mp_plat_print, "%llu\n", ULLONG_MAX); // unsigned long long + } else { + // fake for platforms without narrower mp_int_t + mp_printf(&mp_plat_print, "7fffffffffffffff\n"); + mp_printf(&mp_plat_print, "7FFFFFFFFFFFFFFF\n"); + mp_printf(&mp_plat_print, "18446744073709551615\n"); + } + mp_printf(&mp_plat_print, "%p\n", (void *)0x789f); // pointer + mp_printf(&mp_plat_print, "%P\n", (void *)0x789f); // pointer uppercase + mp_printf(&mp_plat_print, "%.2s %.3s '%4.4s' '%5.5q' '%.3q'\n", "abc", "abc", "abc", (qstr)MP_QSTR_True, (qstr)MP_QSTR_True); // fixed string precision mp_printf(&mp_plat_print, "%.*s\n", -1, "abc"); // negative string precision mp_printf(&mp_plat_print, "%b %b\n", 0, 1); // bools #ifndef NDEBUG @@ -204,10 +232,36 @@ static mp_obj_t extra_coverage(void) { #endif mp_printf(&mp_plat_print, "%d\n", 0x80000000); // should print signed mp_printf(&mp_plat_print, "%u\n", 0x80000000); // should print unsigned - mp_printf(&mp_plat_print, "%x\n", 0x80000000); // should print unsigned - mp_printf(&mp_plat_print, "%X\n", 0x80000000); // should print unsigned - mp_printf(&mp_plat_print, "abc\n%"); // string ends in middle of format specifier + mp_printf(&mp_plat_print, "%x\n", 0x8000000f); // should print unsigned + mp_printf(&mp_plat_print, "%X\n", 0x8000000f); // should print unsigned + // note: storing the string in a variable is enough to prevent the + // format string checker from checking this format string. Otherwise, + // it would be a compile time diagnostic under the format string + // checker. + const char msg[] = "abc\n%"; + mp_printf(&mp_plat_print, msg); // string ends in middle of format specifier mp_printf(&mp_plat_print, "%%\n"); // literal % character + mp_printf(&mp_plat_print, ".%-3s.\n", "a"); // left adjust + + // Check that all kinds of mp_printf arguments are parsed out + // correctly, by having a char argument before and after each main type + // of value that can be formatted. + mp_printf(&mp_plat_print, "%c%%%c\n", '<', '>'); + mp_printf(&mp_plat_print, "%c%p%c\n", '<', (void *)0xaaaa, '>'); + mp_printf(&mp_plat_print, "%c%b%c\n", '<', true, '>'); + mp_printf(&mp_plat_print, "%c%d%c\n", '<', 0xaaaa, '>'); + mp_printf(&mp_plat_print, "%c%ld%c\n", '<', 0xaaaal, '>'); + mp_printf(&mp_plat_print, "%c" INT_FMT "%c\n", '<', (mp_int_t)0xaaaa, '>'); + mp_printf(&mp_plat_print, "%c%s%c\n", '<', "test", '>'); + mp_printf(&mp_plat_print, "%c%f%c\n", '<', MICROPY_FLOAT_CONST(1000.), '>'); + mp_printf(&mp_plat_print, "%c%q%c\n", '<', (qstr)MP_QSTR_True, '>'); + if (sizeof(mp_int_t) == 8) { + mp_printf(&mp_plat_print, "%c%lld%c\n", '<', LLONG_MAX, '>'); + } else { + mp_printf(&mp_plat_print, "<9223372036854775807>\n"); + } + + } // GC @@ -220,11 +274,11 @@ static mp_obj_t extra_coverage(void) { gc_unlock(); // using gc_realloc to resize to 0, which means free the memory - void *p = gc_alloc(4, false); + void *p = gc_alloc(4, 0); mp_printf(&mp_plat_print, "%p\n", gc_realloc(p, 0, false)); // calling gc_nbytes with a non-heap pointer - mp_printf(&mp_plat_print, "%p\n", gc_nbytes(NULL)); + mp_printf(&mp_plat_print, "%d\n", (int)gc_nbytes(NULL)); } // GC initialisation and allocation stress test, to check the logic behind ALLOC_TABLE_GAP_BYTE @@ -285,7 +339,7 @@ static mp_obj_t extra_coverage(void) { } ptrs[i][j] = j; } - mp_printf(&mp_plat_print, "%d %d\n", i, all_zero); + mp_printf(&mp_plat_print, "%d %d\n", (int)i, (int)all_zero); // hide the pointer from the GC and collect ptrs[i] = FLIP_POINTER(ptrs[i]); @@ -301,7 +355,7 @@ static mp_obj_t extra_coverage(void) { break; } } - mp_printf(&mp_plat_print, "%d %d\n", i, correct_contents); + mp_printf(&mp_plat_print, "%d %d\n", (int)i, (int)correct_contents); } // free the memory blocks @@ -400,7 +454,7 @@ static mp_obj_t extra_coverage(void) { // create a bytearray via mp_obj_new_bytearray mp_buffer_info_t bufinfo; mp_get_buffer_raise(mp_obj_new_bytearray(4, "data"), &bufinfo, MP_BUFFER_RW); - mp_printf(&mp_plat_print, "%.*s\n", bufinfo.len, bufinfo.buf); + mp_printf(&mp_plat_print, "%.*s\n", (int)bufinfo.len, bufinfo.buf); } // mpz @@ -464,6 +518,38 @@ static mp_obj_t extra_coverage(void) { mp_int_t value_signed; mpz_as_int_checked(&mpz, &value_signed); mp_printf(&mp_plat_print, "%d\n", (int)value_signed); + + // hash the zero mpz integer + mpz_set_from_int(&mpz, 0); + mp_printf(&mp_plat_print, "%d\n", (int)mpz_hash(&mpz)); + + // convert the mpz zero integer to int + mp_printf(&mp_plat_print, "%d\n", mpz_as_int_checked(&mpz, &value_signed)); + mp_printf(&mp_plat_print, "%d\n", (int)value_signed); + + // mpz_set_from_float with 0 as argument + mpz_set_from_float(&mpz, 0); + mp_printf(&mp_plat_print, "%f\n", mpz_as_float(&mpz)); + + // convert a large integer value (stored in a mpz) to mp_uint_t and to ll; + mp_obj_t obj_bigint = mp_obj_new_int_from_uint((mp_uint_t)0xdeadbeef); + mp_printf(&mp_plat_print, "%x\n", (int)mp_obj_get_uint(obj_bigint)); + obj_bigint = mp_obj_new_int_from_ll(0xc0ffee777c0ffeell); + long long value_ll = mp_obj_get_ll(obj_bigint); + mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); + + // convert a large integer value (stored via a struct object) to uint and to ll + // `deadbeef` global is an uctypes.struct defined by extra_coverage.py + obj_bigint = mp_load_global(MP_QSTR_deadbeef); + mp_printf(&mp_plat_print, "%x\n", (int)mp_obj_get_uint(obj_bigint)); + value_ll = mp_obj_get_ll(obj_bigint); + mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); + + // convert a smaller integer value to mp_uint_t and to ll + obj_bigint = mp_obj_new_int_from_uint(0xc0ffee); + mp_printf(&mp_plat_print, "%x\n", (int)mp_obj_get_uint(obj_bigint)); + value_ll = mp_obj_get_ll(obj_bigint); + mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); } // runtime utils @@ -481,7 +567,7 @@ static mp_obj_t extra_coverage(void) { mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), mp_obj_new_str_from_cstr("abc"), mp_obj_new_str_from_cstr("abc")); // mp_obj_int_get_checked with mp_obj_int_t that has a value that is a small integer - mp_printf(&mp_plat_print, "%d\n", mp_obj_int_get_checked(mp_obj_int_new_mpz())); + mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_checked(MP_OBJ_FROM_PTR(mp_obj_int_new_mpz()))); // mp_obj_int_get_uint_checked with non-negative small-int mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_uint_checked(MP_OBJ_NEW_SMALL_INT(1))); @@ -506,6 +592,22 @@ static mp_obj_t extra_coverage(void) { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } + // mp_obj_get_uint from a non-int object (should raise exception) + if (nlr_push(&nlr) == 0) { + mp_obj_get_uint(mp_const_none); + nlr_pop(); + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + + // mp_obj_int_get_ll from a non-int object (should raise exception) + if (nlr_push(&nlr) == 0) { + mp_obj_get_ll(mp_const_none); + nlr_pop(); + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + // call mp_obj_new_exception_args (it's a part of the public C API and not used in the core) mp_obj_print_exception(&mp_plat_print, mp_obj_new_exception_args(&mp_type_ValueError, 0, NULL)); } @@ -515,26 +617,6 @@ static mp_obj_t extra_coverage(void) { mp_emitter_warning(MP_PASS_CODE_SIZE, "test"); } - // format float - { - mp_printf(&mp_plat_print, "# format float\n"); - - // format with inadequate buffer size - char buf[5]; - mp_format_float(1, buf, sizeof(buf), 'g', 0, '+'); - mp_printf(&mp_plat_print, "%s\n", buf); - - // format with just enough buffer so that precision must be - // set from 0 to 1 twice - char buf2[8]; - mp_format_float(1, buf2, sizeof(buf2), 'g', 0, '+'); - mp_printf(&mp_plat_print, "%s\n", buf2); - - // format where precision is trimmed to avoid buffer overflow - mp_format_float(1, buf2, sizeof(buf2), 'e', 0, '+'); - mp_printf(&mp_plat_print, "%s\n", buf2); - } - // binary { mp_printf(&mp_plat_print, "# binary\n"); @@ -558,14 +640,26 @@ static mp_obj_t extra_coverage(void) { fun_bc.context = &context; fun_bc.child_table = NULL; fun_bc.bytecode = (const byte *)"\x01"; // just needed for n_state + #if MICROPY_PY_SYS_SETTRACE + struct _mp_raw_code_t rc = {}; + fun_bc.rc = &rc; + #endif mp_code_state_t *code_state = m_new_obj_var(mp_code_state_t, state, mp_obj_t, 1); code_state->fun_bc = &fun_bc; code_state->ip = (const byte *)"\x00"; // just needed for an invalid opcode code_state->sp = &code_state->state[0]; code_state->exc_sp_idx = 0; code_state->old_globals = NULL; + #if MICROPY_STACKLESS + code_state->prev = NULL; + #endif + #if MICROPY_PY_SYS_SETTRACE + code_state->prev_state = NULL; + code_state->frame = NULL; + #endif + mp_vm_return_kind_t ret = mp_execute_bytecode(code_state, MP_OBJ_NULL); - mp_printf(&mp_plat_print, "%d %d\n", ret, mp_obj_get_type(code_state->state[0]) == &mp_type_NotImplementedError); + mp_printf(&mp_plat_print, "%d %d\n", (int)ret, mp_obj_get_type(code_state->state[0]) == &mp_type_NotImplementedError); } // scheduler @@ -622,9 +716,25 @@ static mp_obj_t extra_coverage(void) { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } mp_handle_pending(true); + + // CIRCUITPY-CHANGE: not turned on in CircuitPython + #if MICROPY_SCHEDULER_STATIC_NODES + coverage_sched_function_continue = true; + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + for (int i = 0; i < 3; ++i) { + mp_printf(&mp_plat_print, "loop\n"); + mp_handle_pending(true); + } + // Clear this flag to prevent the function scheduling itself again + coverage_sched_function_continue = false; + // Will only run the first time through this loop, then not scheduled again + for (int i = 0; i < 3; ++i) { + mp_handle_pending(true); + } + #endif } - // CIRCUITPY-CHANGE: ringbuf is different + // CIRCUITPY-CHANGE: ringbuf is quite different // ringbuf { #define RINGBUF_SIZE 99 @@ -636,36 +746,36 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "# ringbuf\n"); // Single-byte put/get with empty ringbuf. - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); ringbuf_put(&ringbuf, 22); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_get(&ringbuf)); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); // Two-byte put/get with empty ringbuf. ringbuf_put16(&ringbuf, 0xaa55); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%04x\n", ringbuf_get16(&ringbuf)); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); // Two-byte put with full ringbuf. for (int i = 0; i < RINGBUF_SIZE; ++i) { ringbuf_put(&ringbuf, i); } - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_put16(&ringbuf, 0x11bb)); // Two-byte put with one byte free. ringbuf_get(&ringbuf); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_put16(&ringbuf, 0x3377)); ringbuf_get(&ringbuf); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); mp_printf(&mp_plat_print, "%d\n", ringbuf_put16(&ringbuf, 0xcc99)); for (int i = 0; i < RINGBUF_SIZE - 2; ++i) { ringbuf_get(&ringbuf); } mp_printf(&mp_plat_print, "%04x\n", ringbuf_get16(&ringbuf)); - mp_printf(&mp_plat_print, "%d %d\n", ringbuf_num_empty(&ringbuf), ringbuf_num_filled(&ringbuf)); + mp_printf(&mp_plat_print, "%d %d\n", (int)ringbuf_num_empty(&ringbuf), (int)ringbuf_num_filled(&ringbuf)); // Two-byte put with wrap around on first byte: ringbuf_clear(&ringbuf); @@ -777,7 +887,7 @@ static mp_obj_t extra_coverage(void) { mp_obj_streamtest_t *s2 = mp_obj_malloc(mp_obj_streamtest_t, &mp_type_stest_textio2); // return a tuple of data for testing on the Python side - mp_obj_t items[] = {(mp_obj_t)&str_no_hash_obj, (mp_obj_t)&bytes_no_hash_obj, MP_OBJ_FROM_PTR(s), MP_OBJ_FROM_PTR(s2)}; + mp_obj_t items[] = {MP_OBJ_FROM_PTR(&str_no_hash_obj), MP_OBJ_FROM_PTR(&bytes_no_hash_obj), MP_OBJ_FROM_PTR(s), MP_OBJ_FROM_PTR(s2)}; return mp_obj_new_tuple(MP_ARRAY_SIZE(items), items); } MP_DEFINE_CONST_FUN_OBJ_0(extra_coverage_obj, extra_coverage); diff --git a/ports/unix/coveragecpp.cpp b/ports/unix/coveragecpp.cpp index 377b5acf763ce..8ba308f6468f3 100644 --- a/ports/unix/coveragecpp.cpp +++ b/ports/unix/coveragecpp.cpp @@ -1,12 +1,77 @@ extern "C" { -// CIRCUITPY-CHANGE: do not include everything: it causes compilation warnings -#include "py/obj.h" +// Include the complete public API to verify everything compiles as C++. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include } +// Invoke all (except one, see below) public API macros which initialize structs to make sure +// they are C++-compatible, meaning they explicitly initialize all struct members. +mp_obj_t f0(); +MP_DEFINE_CONST_FUN_OBJ_0(f0_obj, f0); +mp_obj_t f1(mp_obj_t); +MP_DEFINE_CONST_FUN_OBJ_1(f1_obj, f1); +mp_obj_t f2(mp_obj_t, mp_obj_t); +MP_DEFINE_CONST_FUN_OBJ_2(f2_obj, f2); +mp_obj_t f3(mp_obj_t, mp_obj_t, mp_obj_t); +MP_DEFINE_CONST_FUN_OBJ_3(f3_obj, f3); +mp_obj_t fvar(size_t, const mp_obj_t *); +MP_DEFINE_CONST_FUN_OBJ_VAR(fvar_obj, 1, fvar); +mp_obj_t fvarbetween(size_t, const mp_obj_t *); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(fvarbetween_obj, 1, 2, fvarbetween); +mp_obj_t fkw(size_t, const mp_obj_t *, mp_map_t *); +MP_DEFINE_CONST_FUN_OBJ_KW(fkw_obj, 1, fkw); + +static const mp_rom_map_elem_t table[] = { + { MP_ROM_QSTR(MP_QSTR_f0), MP_ROM_PTR(&f0_obj) }, +}; +MP_DEFINE_CONST_MAP(map, table); +MP_DEFINE_CONST_DICT(dict, table); + +static const qstr attrtuple_fields[] = { + MP_QSTR_f0, +}; +MP_DEFINE_ATTRTUPLE(attrtuple, attrtuple_fields, 1, MP_ROM_PTR(&f0_obj)); + +void nlr_cb(void *); +void nlr_cb(void *){ +} + +// The MP_DEFINE_CONST_OBJ_TYPE macro is not C++-compatible because each of the +// MP_DEFINE_CONST_OBJ_TYPE_NARGS_X macros only initializes some of _mp_obj_type_t's +// .slot_index_xxx members but that cannot be fixed to be done in a deterministic way. + + #if defined(MICROPY_UNIX_COVERAGE) // Just to test building of C++ code. static mp_obj_t extra_cpp_coverage_impl() { + MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, nlr_cb, (void *) nlr_cb); + + // To avoid 'error: unused variable [-Werror,-Wunused-const-variable]'. + (void) ctx; + (void) f0_obj; + (void) f1_obj; + (void) f2_obj; + (void) f3_obj; + (void) fvar_obj; + (void) fvarbetween_obj; + (void) fkw_obj; + (void) map; + (void) dict; + (void) attrtuple; return mp_const_none; } diff --git a/ports/unix/input.c b/ports/unix/input.c index 31926a5a8e1af..260e9eac8c9db 100644 --- a/ports/unix/input.c +++ b/ports/unix/input.c @@ -104,6 +104,9 @@ void prompt_write_history(void) { #if MICROPY_USE_READLINE == 1 char *home = getenv("HOME"); if (home != NULL) { + if (MP_STATE_THREAD(gc_lock_depth) != 0) { + return; + } vstr_t vstr; vstr_init(&vstr, 50); vstr_printf(&vstr, "%s/.micropython.history", home); diff --git a/ports/unix/main.c b/ports/unix/main.c index ce8b89136370b..e42400fe5f9a3 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -54,38 +54,11 @@ #include "extmod/vfs_posix.h" #include "genhdr/mpversion.h" #include "input.h" - -// CIRCUITPY-CHANGE -#if defined(MICROPY_UNIX_COVERAGE) -#include "py/objstr.h" -typedef int os_getenv_err_t; -mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_); -os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len); -os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value); - -static mp_obj_t mod_os_getenv_int(mp_obj_t var_in) { - mp_int_t value; - os_getenv_err_t result = common_hal_os_getenv_int(mp_obj_str_get_str(var_in), &value); - if (result == 0) { - return mp_obj_new_int(value); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mod_os_getenv_int_obj, mod_os_getenv_int); - -static mp_obj_t mod_os_getenv_str(mp_obj_t var_in) { - char buf[4096]; - os_getenv_err_t result = common_hal_os_getenv_str(mp_obj_str_get_str(var_in), buf, sizeof(buf)); - if (result == 0) { - return mp_obj_new_str_copy(&mp_type_str, (byte *)buf, strlen(buf)); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mod_os_getenv_str_obj, mod_os_getenv_str); -#endif +#include "stack_size.h" +#include "shared/runtime/pyexec.h" // Command line options, with their defaults -static bool compile_only = false; +bool mp_compile_only = false; static uint emit_opt = MP_EMIT_OPT_NONE; #if MICROPY_ENABLE_GC @@ -119,7 +92,8 @@ static void stderr_print_strn(void *env, const char *str, size_t len) { const mp_print_t mp_stderr_print = {NULL, stderr_print_strn}; -#define FORCED_EXIT (0x100) +// CIRCUITPY-CHANGE: be consistent about using PYEXEC_FORCED_EXIT +// #define FORCED_EXIT (0x100) // If exc is SystemExit, return value where FORCED_EXIT bit set, // and lower 8 bits are SystemExit value. For all other exceptions, // return 1. @@ -132,7 +106,8 @@ static int handle_uncaught_exception(mp_obj_base_t *exc) { if (exit_val != mp_const_none && !mp_obj_get_int_maybe(exit_val, &val)) { val = 1; } - return FORCED_EXIT | (val & 255); + // CIRCUITPY-CHANGE: be consistent about using PYEXEC_FORCED_EXIT + return PYEXEC_FORCED_EXIT | (val & 255); } // Report all other exceptions @@ -141,8 +116,6 @@ static int handle_uncaught_exception(mp_obj_base_t *exc) { } #define LEX_SRC_STR (1) -#define LEX_SRC_VSTR (2) -#define LEX_SRC_FILENAME (3) #define LEX_SRC_STDIN (4) // Returns standard error codes: 0 for success, 1 for all other errors, @@ -158,19 +131,13 @@ static int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu if (source_kind == LEX_SRC_STR) { const char *line = source; lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, line, strlen(line), false); - } else if (source_kind == LEX_SRC_VSTR) { - const vstr_t *vstr = source; - lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, false); - } else if (source_kind == LEX_SRC_FILENAME) { - const char *filename = (const char *)source; - lex = mp_lexer_new_from_file(qstr_from_str(filename)); } else { // LEX_SRC_STDIN lex = mp_lexer_new_from_fd(MP_QSTR__lt_stdin_gt_, 0, false); } qstr source_name = lex->source_name; - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ if (input_kind == MP_PARSE_FILE_INPUT) { mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); } @@ -189,7 +156,7 @@ static int execute_from_lexer(int source_kind, const void *source, mp_parse_inpu mp_obj_t module_fun = mp_compile(&parse_tree, source_name, is_repl); - if (!compile_only) { + if (!mp_compile_only) { // execute it mp_call_function_0(module_fun); } @@ -226,92 +193,32 @@ static char *strjoin(const char *s1, int sep_char, const char *s2) { #endif static int do_repl(void) { - mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION); - mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE); - mp_hal_stdout_tx_str("\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); - #if MICROPY_USE_READLINE == 1 - // use MicroPython supplied readline + // use MicroPython supplied readline-based REPL - vstr_t line; - vstr_init(&line, 16); + int ret = 0; for (;;) { - mp_hal_stdio_mode_raw(); - - input_restart: - vstr_reset(&line); - int ret = readline(&line, mp_repl_get_ps1()); - mp_parse_input_kind_t parse_input_kind = MP_PARSE_SINGLE_INPUT; - - if (ret == CHAR_CTRL_C) { - // cancel input - mp_hal_stdout_tx_str("\r\n"); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // EOF - printf("\n"); - mp_hal_stdio_mode_orig(); - vstr_clear(&line); - return 0; - } else if (ret == CHAR_CTRL_E) { - // paste mode - mp_hal_stdout_tx_str("\npaste mode; Ctrl-C to cancel, Ctrl-D to finish\n=== "); - vstr_reset(&line); - for (;;) { - char c = mp_hal_stdin_rx_chr(); - if (c == CHAR_CTRL_C) { - // cancel everything - mp_hal_stdout_tx_str("\n"); - goto input_restart; - } else if (c == CHAR_CTRL_D) { - // end of input - mp_hal_stdout_tx_str("\n"); - break; - } else { - // add char to buffer and echo - vstr_add_byte(&line, c); - if (c == '\r') { - mp_hal_stdout_tx_str("\n=== "); - } else { - mp_hal_stdout_tx_strn(&c, 1); - } - } - } - parse_input_kind = MP_PARSE_FILE_INPUT; - } else if (line.len == 0) { - if (ret != 0) { - printf("\n"); + if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { + if ((ret = pyexec_raw_repl()) != 0) { + break; } - goto input_restart; } else { - // got a line with non-zero length, see if it needs continuing - while (mp_repl_continue_with_input(vstr_null_terminated_str(&line))) { - vstr_add_byte(&line, '\n'); - ret = readline(&line, mp_repl_get_ps2()); - if (ret == CHAR_CTRL_C) { - // cancel everything - printf("\n"); - goto input_restart; - } else if (ret == CHAR_CTRL_D) { - // stop entering compound statement - break; - } + if ((ret = pyexec_friendly_repl()) != 0) { + break; } } - - mp_hal_stdio_mode_orig(); - - ret = execute_from_lexer(LEX_SRC_VSTR, &line, parse_input_kind, true); - if (ret & FORCED_EXIT) { - return ret; - } } + return ret; #else // use simple readline + mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION); + mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE); + mp_hal_stdout_tx_str("\nUse Ctrl-D to exit, Ctrl-E for paste mode\n"); + for (;;) { char *line = prompt((char *)mp_repl_get_ps1()); if (line == NULL) { @@ -331,7 +238,8 @@ static int do_repl(void) { int ret = execute_from_lexer(LEX_SRC_STR, line, MP_PARSE_SINGLE_INPUT, true); free(line); - if (ret & FORCED_EXIT) { + // CIRCUITPY-CHANGE: be consistent about using PYEXEC_FORCED_EXIT + if (ret & PYEXEC_FORCED_EXIT) { return ret; } } @@ -339,12 +247,44 @@ static int do_repl(void) { #endif } +static inline int convert_pyexec_result(int ret) { + #if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING + // With exit code handling enabled: + // pyexec returns exit code with PYEXEC_FORCED_EXIT flag set for SystemExit + // Unix port expects: 0 for success, non-zero for error/exit + if (ret & PYEXEC_FORCED_EXIT) { + // SystemExit: extract exit code from lower bits + return ret & 0xFF; + } + // Normal execution or exception: return as-is (0 for success, 1 for exception) + return ret; + #else + // pyexec returns 1 for success, 0 for exception, PYEXEC_FORCED_EXIT for SystemExit + // Convert to unix port's expected codes: 0 for success, 1 for exception, FORCED_EXIT|val for SystemExit + if (ret == 1) { + return 0; // success + } else if (ret & PYEXEC_FORCED_EXIT) { + return ret; // SystemExit with exit value in lower 8 bits + } else { + return 1; // exception + } + #endif +} + static int do_file(const char *file) { - return execute_from_lexer(LEX_SRC_FILENAME, file, MP_PARSE_FILE_INPUT, false); + // CIRCUITPY-CHANGE: pyexec_file result arg + pyexec_result_t pyexec_result; + return convert_pyexec_result(pyexec_file(file, &pyexec_result)); } static int do_str(const char *str) { - return execute_from_lexer(LEX_SRC_STR, str, MP_PARSE_FILE_INPUT, false); + vstr_t vstr; + vstr.buf = (char *)str; + vstr.len = strlen(str); + // CIRCUITPY-CHANGE: pyexec_vstr result arg + pyexec_result_t pyexec_result; + int ret = pyexec_vstr(&vstr, true, &pyexec_result); + return convert_pyexec_result(ret); } static void print_help(char **argv) { @@ -413,7 +353,7 @@ static void pre_process_options(int argc, char **argv) { } if (0) { } else if (strcmp(argv[a + 1], "compile-only") == 0) { - compile_only = true; + mp_compile_only = true; } else if (strcmp(argv[a + 1], "emit=bytecode") == 0) { emit_opt = MP_EMIT_OPT_BYTECODE; #if MICROPY_EMIT_NATIVE @@ -485,10 +425,13 @@ static void set_sys_argv(char *argv[], int argc, int start_arg) { #if MICROPY_PY_SYS_EXECUTABLE extern mp_obj_str_t mp_sys_executable_obj; -static char executable_path[MICROPY_ALLOC_PATH_MAX]; +static char *executable_path = NULL; static void sys_set_excecutable(char *argv0) { - if (realpath(argv0, executable_path)) { + if (executable_path == NULL) { + executable_path = realpath(argv0, NULL); + } + if (executable_path != NULL) { mp_obj_str_set_data(&mp_sys_executable_obj, (byte *)executable_path, strlen(executable_path)); } } @@ -508,11 +451,7 @@ int main(int argc, char **argv) { #endif // Define a reasonable stack limit to detect stack overflow. - mp_uint_t stack_size = 40000 * (sizeof(void *) / 4); - #if defined(__arm__) && !defined(__thumb2__) - // ARM (non-Thumb) architectures require more stack. - stack_size *= 2; - #endif + mp_uint_t stack_size = 40000 * UNIX_STACK_MULTIPLIER; // We should capture stack top ASAP after start, and it should be // captured guaranteedly before any other stack variables are allocated. @@ -645,24 +584,9 @@ MP_NOINLINE int main_(int argc, char **argv) { // CIRCUITPY-CHANGE: test native base classes work as needed by CircuitPython libraries. extern const mp_obj_type_t native_base_class_type; mp_store_global(MP_QSTR_NativeBaseClass, MP_OBJ_FROM_PTR(&native_base_class_type)); - mp_store_global(MP_QSTR_getenv_int, MP_OBJ_FROM_PTR(&mod_os_getenv_int_obj)); - mp_store_global(MP_QSTR_getenv_str, MP_OBJ_FROM_PTR(&mod_os_getenv_str_obj)); } #endif - // Here is some example code to create a class and instance of that class. - // First is the Python, then the C code. - // - // class TestClass: - // pass - // test_obj = TestClass() - // test_obj.attr = 42 - // - // mp_obj_t test_class_type, test_class_instance; - // test_class_type = mp_obj_new_type(qstr_from_str("TestClass"), mp_const_empty_tuple, mp_obj_new_dict(0)); - // mp_store_name(qstr_from_str("test_obj"), test_class_instance = mp_call_function_0(test_class_type)); - // mp_store_attr(test_class_instance, qstr_from_str("attr"), mp_obj_new_int(42)); - /* printf("bytes:\n"); printf(" total %d\n", m_get_total_bytes_allocated()); @@ -716,12 +640,18 @@ MP_NOINLINE int main_(int argc, char **argv) { subpkg_tried = false; reimport: + mp_hal_set_interrupt_char(CHAR_CTRL_C); if (nlr_push(&nlr) == 0) { mod = mp_builtin___import__(MP_ARRAY_SIZE(import_args), import_args); + mp_hal_set_interrupt_char(-1); + mp_handle_pending(true); nlr_pop(); } else { // uncaught exception - return handle_uncaught_exception(nlr.ret_val) & 0xff; + mp_hal_set_interrupt_char(-1); + mp_handle_pending(false); + ret = handle_uncaught_exception(nlr.ret_val) & 0xff; + break; } // If this module is a package, see if it has a `__main__.py`. @@ -758,11 +688,9 @@ MP_NOINLINE int main_(int argc, char **argv) { return invalid_args(); } } else { - char *pathbuf = malloc(PATH_MAX); - char *basedir = realpath(argv[a], pathbuf); + char *basedir = realpath(argv[a], NULL); if (basedir == NULL) { mp_printf(&mp_stderr_print, "%s: can't open file '%s': [Errno %d] %s\n", argv[0], argv[a], errno, strerror(errno)); - free(pathbuf); // CPython exits with 2 in such case ret = 2; break; @@ -771,7 +699,7 @@ MP_NOINLINE int main_(int argc, char **argv) { // Set base dir of the script as first entry in sys.path. char *p = strrchr(basedir, '/'); mp_obj_list_store(mp_sys_path, MP_OBJ_NEW_SMALL_INT(0), mp_obj_new_str_via_qstr(basedir, p - basedir)); - free(pathbuf); + free(basedir); set_sys_argv(argv, argc, a); ret = do_file(argv[a]); @@ -811,7 +739,7 @@ MP_NOINLINE int main_(int argc, char **argv) { #endif #if MICROPY_PY_BLUETOOTH - void mp_bluetooth_deinit(void); + int mp_bluetooth_deinit(void); mp_bluetooth_deinit(); #endif @@ -837,8 +765,20 @@ MP_NOINLINE int main_(int argc, char **argv) { #endif #endif + #if MICROPY_PY_SYS_EXECUTABLE && !defined(NDEBUG) + // Again, make memory leak detector happy + free(executable_path); + #endif + // printf("total bytes = %d\n", m_get_total_bytes_allocated()); - return ret & 0xff; + + // CIRCUITPY-CHANGE: handle PYEXEC_EXCEPTION + if (ret & PYEXEC_EXCEPTION) { + // Return exit status code 1 so the invoker knows there was an uncaught exception. + return 1; + } else { + return ret & 0xff; + } } void nlr_jump_fail(void *val) { diff --git a/ports/unix/modos.c b/ports/unix/modos.c index 896bf351b83f1..af8a1cc4da871 100644 --- a/ports/unix/modos.c +++ b/ports/unix/modos.c @@ -32,13 +32,12 @@ #include "py/runtime.h" #include "py/mphal.h" -// CIRCUITPY-CHANGE: enhanced getenv +// CIRCUITPY-CHANGE: use shared-module os getenv #if defined(MICROPY_UNIX_COVERAGE) #include "py/objstr.h" -typedef int os_getenv_err_t; +#include "shared-module/os/__init__.h" +#include "supervisor/shared/settings.h" mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_); -os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len); -os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value); #endif static mp_obj_t mp_os_getenv(size_t n_args, const mp_obj_t *args) { @@ -58,30 +57,7 @@ static mp_obj_t mp_os_getenv(size_t n_args, const mp_obj_t *args) { } return mp_obj_new_str_from_cstr(s); } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_os_getenv_obj, 1, 2, mp_os_getenv); - -// CIRCUITPY-CHANGE: getenv differences -#if defined(MICROPY_UNIX_COVERAGE) -static mp_obj_t mp_os_getenv_int(mp_obj_t var_in) { - mp_int_t value; - os_getenv_err_t result = common_hal_os_getenv_int(mp_obj_str_get_str(var_in), &value); - if (result == 0) { - return mp_obj_new_int(value); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mp_os_getenv_int_obj, mp_os_getenv_int); - -static mp_obj_t mp_os_getenv_str(mp_obj_t var_in) { - char buf[4096]; - os_getenv_err_t result = common_hal_os_getenv_str(mp_obj_str_get_str(var_in), buf, sizeof(buf)); - if (result == 0) { - return mp_obj_new_str_copy(&mp_type_str, (byte *)buf, strlen(buf)); - } - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_1(mp_os_getenv_str_obj, mp_os_getenv_str); -#endif +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_os_getenv_obj, 1, 2, mp_os_getenv); static mp_obj_t mp_os_putenv(mp_obj_t key_in, mp_obj_t value_in) { const char *key = mp_obj_str_get_str(key_in); diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c index fbd94b5ecd129..4f0550dbea765 100644 --- a/ports/unix/modtime.c +++ b/ports/unix/modtime.c @@ -34,6 +34,7 @@ #include "py/mphal.h" #include "py/runtime.h" +#include "shared/timeutils/timeutils.h" #ifdef _WIN32 static inline int msec_sleep_tv(struct timeval *tv) { @@ -130,12 +131,7 @@ static mp_obj_t mod_time_gm_local_time(size_t n_args, const mp_obj_t *args, stru if (n_args == 0) { t = time(NULL); } else { - #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE - mp_float_t val = mp_obj_get_float(args[0]); - t = (time_t)MICROPY_FLOAT_C_FUN(trunc)(val); - #else - t = mp_obj_get_int(args[0]); - #endif + t = (time_t)timeutils_obj_get_timestamp(args[0]); } struct tm *tm = time_func(&t); @@ -193,10 +189,10 @@ static mp_obj_t mod_time_mktime(mp_obj_t tuple) { time.tm_isdst = -1; // auto-detect } time_t ret = mktime(&time); - if (ret == -1) { + if (ret == (time_t)-1) { mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("invalid mktime usage")); } - return mp_obj_new_int(ret); + return timeutils_obj_from_timestamp(ret); } MP_DEFINE_CONST_FUN_OBJ_1(mod_time_mktime_obj, mod_time_mktime); diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 4d9fe9f1dc4a2..815be76b4e90c 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -32,6 +32,9 @@ // features to work on Unix-like systems, see mpconfigvariant.h (and // mpconfigvariant_common.h) for feature enabling. +// For time_t, needed by MICROPY_TIMESTAMP_IMPL_TIME_T. +#include + // For size_t and ssize_t #include @@ -41,6 +44,7 @@ // CIRCUITPY-CHANGE #define CIRCUITPY_MICROPYTHON_ADVANCED (1) #define MICROPY_PY_ASYNC_AWAIT (1) +#define MICROPY_PY_DOUBLE_TYPECODE (1) #define MICROPY_PY_UCTYPES (0) #ifndef MICROPY_CONFIG_ROM_LEVEL @@ -78,21 +82,6 @@ #define MICROPY_EMIT_ARM (1) #endif -// Type definitions for the specific machine based on the word size. -#ifndef MICROPY_OBJ_REPR -#ifdef __LP64__ -typedef long mp_int_t; // must be pointer size -typedef unsigned long mp_uint_t; // must be pointer size -#else -// These are definitions for machines where sizeof(int) == sizeof(void*), -// regardless of actual size. -typedef int mp_int_t; // must be pointer size -typedef unsigned int mp_uint_t; // must be pointer size -#endif -#else -// Assume that if we already defined the obj repr then we also defined types. -#endif - // Cannot include , as it may lead to symbol name clashes #if _FILE_OFFSET_BITS == 64 && !defined(__LP64__) typedef long long mp_off_t; @@ -135,6 +124,9 @@ typedef long mp_off_t; // VFS stat functions should return time values relative to 1970/1/1 #define MICROPY_EPOCH_IS_1970 (1) +// port modtime functions use time_t +#define MICROPY_TIMESTAMP_IMPL (MICROPY_TIMESTAMP_IMPL_TIME_T) + // Assume that select() call, interrupted with a signal, and erroring // with EINTR, updates remaining timeout value. #define MICROPY_SELECT_REMAINING_TIME (1) @@ -145,6 +137,9 @@ typedef long mp_off_t; #define MICROPY_STACKLESS_STRICT (0) #endif +// Recursive mutex is needed when threading is enabled, regardless of GIL setting. +#define MICROPY_PY_THREAD_RECURSIVE_MUTEX (MICROPY_PY_THREAD) + // Implementation of the machine module. #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/unix/modmachine.c" @@ -172,6 +167,12 @@ typedef long mp_off_t; // Enable sys.executable. #define MICROPY_PY_SYS_EXECUTABLE (1) +// Enable support for compile-only mode. +#define MICROPY_PYEXEC_COMPILE_ONLY (1) + +// Enable handling of sys.exit() exit codes. +#define MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (1) + #define MICROPY_PY_SOCKET_LISTEN_BACKLOG_DEFAULT (SOMAXCONN < 128 ? SOMAXCONN : 128) // Bare-metal ports don't have stderr. Printing debug to stderr may give tests diff --git a/ports/unix/mpconfigport.mk b/ports/unix/mpconfigport.mk index 26c04faf4c5ca..c836663cd2339 100644 --- a/ports/unix/mpconfigport.mk +++ b/ports/unix/mpconfigport.mk @@ -14,6 +14,7 @@ MICROPY_PY_BTREE = 0 # _thread module using pthreads MICROPY_PY_THREAD = 1 +MICROPY_PY_THREAD_GIL = 0 # Subset of CPython termios module MICROPY_PY_TERMIOS = 1 diff --git a/ports/unix/mphalport.h b/ports/unix/mphalport.h index 7f71217632a8e..d9cd05b3de82b 100644 --- a/ports/unix/mphalport.h +++ b/ports/unix/mphalport.h @@ -25,7 +25,6 @@ */ #include #include -// CIRCUITPY-CHANGE: extra include #include #ifndef CHAR_CTRL_C @@ -38,6 +37,20 @@ #define MICROPY_END_ATOMIC_SECTION(x) (void)x; mp_thread_unix_end_atomic_section() #endif +// In lieu of a WFI(), slow down polling from being a tight loop. +// +// Note that we don't delay for the full TIMEOUT_MS, as execution +// can't be woken from the delay. +#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) \ + do { \ + MP_THREAD_GIL_EXIT(); \ + mp_hal_delay_us(500); \ + MP_THREAD_GIL_ENTER(); \ + } while (0) + +// The port provides `mp_hal_stdio_mode_raw()` and `mp_hal_stdio_mode_orig()`. +#define MICROPY_HAL_HAS_STDIO_MODE_SWITCH (1) + // CIRCUITPY-CHANGE: mp_hal_set_interrupt_char(int) instead of char void mp_hal_set_interrupt_char(int c); bool mp_hal_is_interrupted(void); @@ -110,3 +123,6 @@ enum { void mp_hal_get_mac(int idx, uint8_t buf[6]); #endif + +// Global variable to control compile-only mode. +extern bool mp_compile_only; diff --git a/ports/unix/mpthreadport.c b/ports/unix/mpthreadport.c index 5172645bc147a..a41b3ec9f4701 100644 --- a/ports/unix/mpthreadport.c +++ b/ports/unix/mpthreadport.c @@ -31,6 +31,7 @@ #include "py/runtime.h" #include "py/mpthread.h" #include "py/gc.h" +#include "stack_size.h" #if MICROPY_PY_THREAD @@ -45,8 +46,14 @@ // potential conflict with other uses of the more commonly used SIGUSR1. #ifdef SIGRTMIN #define MP_THREAD_GC_SIGNAL (SIGRTMIN + 5) +#ifdef __ANDROID__ +#define MP_THREAD_TERMINATE_SIGNAL (SIGRTMIN + 6) +#endif #else #define MP_THREAD_GC_SIGNAL (SIGUSR1) +#ifdef __ANDROID__ +#define MP_THREAD_TERMINATE_SIGNAL (SIGUSR2) +#endif #endif // This value seems to be about right for both 32-bit and 64-bit builds. @@ -107,6 +114,18 @@ static void mp_thread_gc(int signo, siginfo_t *info, void *context) { } } +// On Android, pthread_cancel and pthread_setcanceltype are not implemented. +// To achieve that result a new signal handler responding on either +// (SIGRTMIN + 6) or SIGUSR2 is installed on every child thread. The sole +// purpose of this new signal handler is to terminate the thread in a safe +// asynchronous manner. + +#ifdef __ANDROID__ +static void mp_thread_terminate(int signo, siginfo_t *info, void *context) { + pthread_exit(NULL); +} +#endif + void mp_thread_init(void) { pthread_key_create(&tls_key, NULL); pthread_setspecific(tls_key, &mp_state_ctx.thread); @@ -135,6 +154,14 @@ void mp_thread_init(void) { sa.sa_sigaction = mp_thread_gc; sigemptyset(&sa.sa_mask); sigaction(MP_THREAD_GC_SIGNAL, &sa, NULL); + + // Install a signal handler for asynchronous termination if needed. + #if defined(__ANDROID__) + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = mp_thread_terminate; + sigemptyset(&sa.sa_mask); + sigaction(MP_THREAD_TERMINATE_SIGNAL, &sa, NULL); + #endif } void mp_thread_deinit(void) { @@ -142,7 +169,11 @@ void mp_thread_deinit(void) { while (thread->next != NULL) { mp_thread_t *th = thread; thread = thread->next; + #if defined(__ANDROID__) + pthread_kill(th->id, MP_THREAD_TERMINATE_SIGNAL); + #else pthread_cancel(th->id); + #endif free(th); } mp_thread_unix_end_atomic_section(); @@ -200,7 +231,9 @@ void mp_thread_start(void) { } #endif + #if !defined(__ANDROID__) pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + #endif mp_thread_unix_begin_atomic_section(); for (mp_thread_t *th = thread; th != NULL; th = th->next) { if (th->id == pthread_self()) { @@ -212,14 +245,14 @@ void mp_thread_start(void) { } mp_uint_t mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size) { - // default stack size is 8k machine-words + // default stack size if (*stack_size == 0) { - *stack_size = 8192 * sizeof(void *); + *stack_size = 32768 * UNIX_STACK_MULTIPLIER; } // minimum stack size is set by pthreads - if (*stack_size < PTHREAD_STACK_MIN) { - *stack_size = PTHREAD_STACK_MIN; + if (*stack_size < (size_t)PTHREAD_STACK_MIN) { + *stack_size = (size_t)PTHREAD_STACK_MIN; } // ensure there is enough stack to include a stack-overflow margin diff --git a/ports/unix/stack_size.h b/ports/unix/stack_size.h new file mode 100644 index 0000000000000..f6159bb69d529 --- /dev/null +++ b/ports/unix/stack_size.h @@ -0,0 +1,54 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 Angus Gratton + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_UNIX_STACK_SIZE_H +#define MICROPY_INCLUDED_UNIX_STACK_SIZE_H + +#include "py/misc.h" + +// Define scaling factors for the stack size (also applies to main thread) +#ifndef UNIX_STACK_MULTIPLIER + +#if defined(__arm__) && !defined(__thumb2__) +// ARM (non-Thumb) architectures require more stack. +#define UNIX_STACK_MUL_ARM 2 +#else +#define UNIX_STACK_MUL_ARM 1 +#endif + +#if MP_SANITIZER_BUILD +// Sanitizer features consume significant stack in some cases +// This multiplier can probably be removed when using GCC 12 or newer. +#define UNIX_STACK_MUL_SANITIZERS 4 +#else +#define UNIX_STACK_MUL_SANITIZERS 1 +#endif + +// Double the stack size for 64-bit builds, plus additional scaling +#define UNIX_STACK_MULTIPLIER ((sizeof(void *) / 4) * UNIX_STACK_MUL_ARM * UNIX_STACK_MUL_SANITIZERS) + +#endif // UNIX_STACK_MULTIPLIER + +#endif // MICROPY_INCLUDED_UNIX_STACK_SIZE_H diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index ca79d3d0d2b61..03f77ec14c6db 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -39,11 +39,22 @@ // Enable additional features. #define MICROPY_DEBUG_PARSE_RULE_NAME (1) +// CIRCUITPY-CHANGE: off +#define MICROPY_PY_SYS_SETTRACE (0) #define MICROPY_TRACKED_ALLOC (1) #define MICROPY_WARNINGS_CATEGORY (1) #undef MICROPY_VFS_ROM_IOCTL #define MICROPY_VFS_ROM_IOCTL (1) #define MICROPY_PY_CRYPTOLIB_CTR (1) +// CIRCUITPY-CHANGE: off +#define MICROPY_SCHEDULER_STATIC_NODES (0) + +// Enable os.uname for attrtuple coverage test +#define MICROPY_PY_OS_UNAME (1) +#define MICROPY_HW_BOARD_NAME "a machine" +#define MICROPY_HW_MCU_NAME MICROPY_PY_SYS_PLATFORM +// Keep the standard banner message +#define MICROPY_BANNER_MACHINE MICROPY_PY_SYS_PLATFORM " [" MICROPY_PLATFORM_COMPILER "] version" // CIRCUITPY-CHANGE: Disable things never used in circuitpython #define MICROPY_PY_CRYPTOLIB (0) diff --git a/ports/unix/variants/coverage/mpconfigvariant.mk b/ports/unix/variants/coverage/mpconfigvariant.mk index 6ec0e85377ccf..292e4de81699f 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.mk +++ b/ports/unix/variants/coverage/mpconfigvariant.mk @@ -106,7 +106,6 @@ SRC_BITMAP := \ shared-module/floppyio/__init__.c \ shared-module/jpegio/__init__.c \ shared-module/jpegio/JpegDecoder.c \ - shared-module/os/getenv.c \ shared-module/rainbowio/__init__.c \ shared-module/struct/__init__.c \ shared-module/synthio/__init__.c \ @@ -164,8 +163,8 @@ CFLAGS += \ -DCIRCUITPY_GIFIO=1 \ -DCIRCUITPY_JPEGIO=1 \ -DCIRCUITPY_LOCALE=1 \ - -DCIRCUITPY_OS_GETENV=1 \ -DCIRCUITPY_RAINBOWIO=1 \ + -DCIRCUITPY_SETTINGS_TOML=1 \ -DCIRCUITPY_STRUCT=1 \ -DCIRCUITPY_SYNTHIO=1 \ -DCIRCUITPY_SYNTHIO_MAX_CHANNELS=14 \ diff --git a/ports/unix/variants/longlong/mpconfigvariant.h b/ports/unix/variants/longlong/mpconfigvariant.h new file mode 100644 index 0000000000000..d50d360b1fe5c --- /dev/null +++ b/ports/unix/variants/longlong/mpconfigvariant.h @@ -0,0 +1,44 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2016 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +// This config exists to test that the MICROPY_LONGINT_IMPL_LONGLONG variant +// (i.e. minimal form of "big integer" that's backed by 64-bit int only) builds +// and passes tests. + +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) + +// We build it on top of REPR C, which uses memory-efficient floating point +// objects encoded directly mp_obj_t (30 bits only). +// Therefore this variant should be built using MICROPY_FORCE_32BIT=1 + +#define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) + +// Set base feature level. +#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES) + +// Enable extra Unix features. +#include "../mpconfigvariant_common.h" diff --git a/ports/unix/variants/longlong/mpconfigvariant.mk b/ports/unix/variants/longlong/mpconfigvariant.mk new file mode 100644 index 0000000000000..2d2c3706469fb --- /dev/null +++ b/ports/unix/variants/longlong/mpconfigvariant.mk @@ -0,0 +1,8 @@ +# build interpreter with "bigints" implemented as "longlong" + +# otherwise, small int is essentially 64-bit +MICROPY_FORCE_32BIT := 1 + +MICROPY_PY_FFI := 0 + +MPY_TOOL_FLAGS = -mlongint-impl longlong diff --git a/ports/unix/variants/minimal/mpconfigvariant.h b/ports/unix/variants/minimal/mpconfigvariant.h index 97ed786b8f409..0e280a8f73058 100644 --- a/ports/unix/variants/minimal/mpconfigvariant.h +++ b/ports/unix/variants/minimal/mpconfigvariant.h @@ -49,6 +49,7 @@ #define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (1) #define MICROPY_ENABLE_COMPILER (1) #define MICROPY_ENABLE_EXTERNAL_IMPORT (1) +#define MICROPY_STACK_CHECK (1) #define MICROPY_FULL_CHECKS (1) #define MICROPY_HELPER_REPL (1) #define MICROPY_KBD_EXCEPTION (1) @@ -61,6 +62,5 @@ #define MICROPY_PY_BUILTINS_RANGE_ATTRS (1) #define MICROPY_PY_GENERATOR_PEND_THROW (1) -// Enable just the sys and os built-in modules. -#define MICROPY_PY_SYS (1) +// Add just the os built-in module. #define MICROPY_PY_OS (1) diff --git a/ports/unix/variants/mpconfigvariant_common.h b/ports/unix/variants/mpconfigvariant_common.h index 9eeed8797366c..1ac59c95572dd 100644 --- a/ports/unix/variants/mpconfigvariant_common.h +++ b/ports/unix/variants/mpconfigvariant_common.h @@ -29,7 +29,7 @@ // Send raise KeyboardInterrupt directly from the signal handler rather than // scheduling it into the VM. -#define MICROPY_ASYNC_KBD_INTR (1) +#define MICROPY_ASYNC_KBD_INTR (!MICROPY_PY_THREAD_GIL) // Enable helpers for printing debugging information. #ifndef MICROPY_DEBUG_PRINTERS @@ -104,12 +104,6 @@ #define MICROPY_PY_TIME_CUSTOM_SLEEP (1) #define MICROPY_PY_TIME_INCLUDEFILE "ports/unix/modtime.c" -#if MICROPY_PY_SSL -#define MICROPY_PY_HASHLIB_MD5 (1) -#define MICROPY_PY_HASHLIB_SHA1 (1) -#define MICROPY_PY_CRYPTOLIB (1) -#endif - // The "select" module is enabled by default, but disable select.select(). #define MICROPY_PY_SELECT_POSIX_OPTIMISATIONS (1) #define MICROPY_PY_SELECT_SELECT (0) diff --git a/ports/unix/variants/nanbox/mpconfigvariant.h b/ports/unix/variants/nanbox/mpconfigvariant.h index 7b13b7dc6ce4e..c9e9c1f63d95d 100644 --- a/ports/unix/variants/nanbox/mpconfigvariant.h +++ b/ports/unix/variants/nanbox/mpconfigvariant.h @@ -41,10 +41,3 @@ #define MICROPY_EMIT_X64 (0) #define MICROPY_EMIT_THUMB (0) #define MICROPY_EMIT_ARM (0) - -#include - -typedef int64_t mp_int_t; -typedef uint64_t mp_uint_t; -#define UINT_FMT "%llu" -#define INT_FMT "%lld" diff --git a/ports/zephyr-cp/AGENTS.md b/ports/zephyr-cp/AGENTS.md new file mode 100644 index 0000000000000..58a3912e2ba05 --- /dev/null +++ b/ports/zephyr-cp/AGENTS.md @@ -0,0 +1,7 @@ +- Build a board by doing `make BOARD=_`. +- The corresponding configuration files are in `boards//` +- The files (not folders) in `boards/` directory are used by Zephyr. +- To flash it on a board do `make BOARD=_ flash`. +- Zephyr board docs are at `zephyr/boards//`. +- Run zephyr-cp tests with `make test`. +- Do not add new translatable error strings (`MP_ERROR_TEXT`). Instead, use `raise_zephyr_error()` or `CHECK_ZEPHYR_RESULT()` from `bindings/zephyr_kernel/__init__.h` to convert Zephyr errno values into Python exceptions. diff --git a/ports/zephyr-cp/CLAUDE.md b/ports/zephyr-cp/CLAUDE.md new file mode 100644 index 0000000000000..43c994c2d3617 --- /dev/null +++ b/ports/zephyr-cp/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/ports/zephyr-cp/CMakeLists.txt b/ports/zephyr-cp/CMakeLists.txt index e35b4b7c764d2..9d115f1e17688 100644 --- a/ports/zephyr-cp/CMakeLists.txt +++ b/ports/zephyr-cp/CMakeLists.txt @@ -5,6 +5,15 @@ project(circuitpython) target_sources(app PRIVATE zephyr_main.c) +# Add I2C emulator control for native_sim testing +if(CONFIG_BOARD_NATIVE_SIM) + target_sources(app PRIVATE native_sim_i2c_emul_control.c) +endif() + +if(CONFIG_TRACING_PERFETTO) + zephyr_include_directories(${ZEPHYR_BINARY_DIR}/subsys/tracing/perfetto/proto) +endif() + # From: https://github.com/zephyrproject-rtos/zephyr/blob/main/samples/application_development/external_lib/CMakeLists.txt # The external static library that we are linking with does not know # how to build for this platform so we export all the flags used in diff --git a/ports/zephyr-cp/Kconfig.sysbuild b/ports/zephyr-cp/Kconfig.sysbuild index cd74ff13592c1..11b49446f422f 100644 --- a/ports/zephyr-cp/Kconfig.sysbuild +++ b/ports/zephyr-cp/Kconfig.sysbuild @@ -8,6 +8,7 @@ config NET_CORE_BOARD default "nrf5340dk/nrf5340/cpunet" if $(BOARD) = "nrf5340dk" default "nrf7002dk/nrf5340/cpunet" if $(BOARD) = "nrf7002dk" default "nrf5340_audio_dk/nrf5340/cpunet" if $(BOARD) = "nrf5340_audio_dk" + default "nrf5340bsim/nrf5340/cpunet" if $(BOARD) = "nrf5340bsim" config NET_CORE_IMAGE_HCI_IPC bool "HCI IPC image on network core" diff --git a/ports/zephyr-cp/Makefile b/ports/zephyr-cp/Makefile index 217e094e3dc0e..12f22d7c3ae83 100644 --- a/ports/zephyr-cp/Makefile +++ b/ports/zephyr-cp/Makefile @@ -8,11 +8,24 @@ BUILD ?= build-$(BOARD) TRANSLATION ?= en_US -.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash debug clean menuconfig +# Compute shield args once. Command-line SHIELD/SHIELDS values override board defaults from circuitpython.toml. +ifneq ($(strip $(BOARD)),) +WEST_SHIELD_ARGS := $(shell SHIELD_ORIGIN="$(origin SHIELD)" SHIELDS_ORIGIN="$(origin SHIELDS)" SHIELD="$(SHIELD)" SHIELDS="$(SHIELDS)" python cptools/get_west_shield_args.py $(BOARD)) +endif + +WEST_CMAKE_ARGS := -DZEPHYR_BOARD_ALIASES=$(CURDIR)/boards/board_aliases.cmake -Dzephyr-cp_TRANSLATION=$(TRANSLATION) + +# When DEBUG=1, apply additional Kconfig fragments for debug-friendly settings. +DEBUG_CONF_FILE ?= $(CURDIR)/debug.conf +ifeq ($(DEBUG),1) +WEST_CMAKE_ARGS += -Dzephyr-cp_EXTRA_CONF_FILE=$(DEBUG_CONF_FILE) +endif + +.PHONY: $(BUILD)/zephyr-cp/zephyr/zephyr.elf flash recover debug debug-jlink debugserver attach run run-sim clean menuconfig all clean-all sim clean-sim test fetch-port-submodules $(BUILD)/zephyr-cp/zephyr/zephyr.elf: python cptools/pre_zephyr_build_prep.py $(BOARD) - west build -b $(BOARD) -d $(BUILD) --sysbuild -- -DZEPHYR_BOARD_ALIASES=$(CURDIR)/boards/board_aliases.cmake -Dzephyr-cp_TRANSLATION=$(TRANSLATION) + west build -b $(BOARD) -d $(BUILD) $(WEST_SHIELD_ARGS) --sysbuild -- $(WEST_CMAKE_ARGS) $(BUILD)/firmware.elf: $(BUILD)/zephyr-cp/zephyr/zephyr.elf cp $^ $@ @@ -20,14 +33,67 @@ $(BUILD)/firmware.elf: $(BUILD)/zephyr-cp/zephyr/zephyr.elf $(BUILD)/firmware.hex: $(BUILD)/zephyr-cp/zephyr/zephyr.elf cp $(BUILD)/zephyr-cp/zephyr/zephyr.hex $@ +$(BUILD)/firmware.exe: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + cp $(BUILD)/zephyr-cp/zephyr/zephyr.exe $@ + +$(BUILD)/firmware.uf2: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + cp $(BUILD)/zephyr-cp/zephyr/zephyr.uf2 $@ + flash: $(BUILD)/zephyr-cp/zephyr/zephyr.elf west flash -d $(BUILD) +recover: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + west flash --recover -d $(BUILD) + debug: $(BUILD)/zephyr-cp/zephyr/zephyr.elf west debug -d $(BUILD) +debug-jlink: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + west debug --runner jlink -d $(BUILD) + +debugserver: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + west debugserver -d $(BUILD) + +attach: $(BUILD)/zephyr-cp/zephyr/zephyr.elf + west attach -d $(BUILD) + +run: $(BUILD)/firmware.exe + $^ + +run-sim: + $(MAKE) BOARD=native_native_sim BUILD=build-native_native_sim build-native_native_sim/firmware.exe + truncate -s 2M build-native_native_sim/flash.bin + mformat -i build-native_native_sim/flash.bin :: + @if [ -d CIRCUITPY ] && [ -n "$$(find CIRCUITPY -mindepth 1 -print -quit)" ]; then \ + echo "Populating build-native_native_sim/flash.bin from ./CIRCUITPY"; \ + mcopy -s -i build-native_native_sim/flash.bin CIRCUITPY/* ::; \ + fi + build-native_native_sim/firmware.exe --flash=build-native_native_sim/flash.bin --flash_rm -wait_uart -rt --i2s_capture=build-native_native_sim/i2s_capture.wav + menuconfig: - west build --sysbuild -d $(BUILD) -t menuconfig + west build $(WEST_SHIELD_ARGS) --sysbuild -d $(BUILD) -t menuconfig -- $(WEST_CMAKE_ARGS) clean: rm -rf $(BUILD) + +fetch-port-submodules: + python ../../tools/ci_fetch_deps.py zephyr-cp + +# Build all boards. The + prefix allows jobserver file descriptors to be passed through. +# This enables parallel builds across all boards when invoked with `make -jN all`. +all: + +python cptools/build_all_boards.py --continue-on-error + +clean-all: + rm -rf build build-* + +# Build all sim boards concurrently using the same jobserver as `make all`. +sim: + +python cptools/build_all_boards.py --vendor native --continue-on-error + +clean-sim: + rm -rf $(wildcard build-native_*) + +test: build-native_native_sim/zephyr-cp/zephyr/zephyr.exe + pytest cptools/tests + pytest tests/ -v diff --git a/ports/zephyr-cp/README.md b/ports/zephyr-cp/README.md index 4d08936cad66c..162ed90bcc8b7 100644 --- a/ports/zephyr-cp/README.md +++ b/ports/zephyr-cp/README.md @@ -15,11 +15,11 @@ pip install west west init -l zephyr-config west update west zephyr-export -pip install -r lib/zephyr/scripts/requirements.txt +pip install -r zephyr/scripts/requirements.txt west sdk install ``` -Now to build from the top level: +Now to build from `ports/zephyr-cp`: ```sh make BOARD=nordic_nrf7002dk @@ -28,6 +28,77 @@ make BOARD=nordic_nrf7002dk This uses Zephyr's cmake to generate Makefiles that then delegate to `tools/cpbuild/build_circuitpython.py` to build the CircuitPython bits in parallel. +## Native simulator build container + +Building the native sim requires `libsdl2-dev:i386` and other 32bit dependencies that +can cause conflicts on 64bit systems resulting in the removal of 64bit versions of critical +software such as the display manager and network manager. A Containerfile and a few scripts +are provided to set up a container to make the native sim build inside without affecting the +host system. + +The container automatically mounts this instance of the circuitpython repo inside at +`/home/dev/circuitpython`. Changes made in the repo inside the container and on the host PC +will sync automatically between host and container. + +To use the container file: + +1. Build the container with `podman build -t zephyr-cp-dev -f native_sim_build_Containerfile .` +2. Run/Start the container by running `./native_sim_build_run_container.sh` on the host PC. + The script will automatically run or start based on whether the container has been run before. +3. Init requirements inside the container with `./native_sim_build_init_container.sh` + +To delete the container and cleanup associated files: +```sh +podman ps -a --filter ancestor=zephyr-cp-dev -q | xargs -r podman rm -f +podman rmi zephyr-cp-dev +podman image prune -f +podman rm -f zcp +``` + +## Running the native simulator + +From `ports/zephyr-cp`, run: + +```sh +make run-sim +``` + +`run-sim` starts the native simulator in realtime. +It prints the PTY path to connect to the simulator REPL. +If a local `./CIRCUITPY/` folder exists, its files are used as the simulator's CIRCUITPY drive. + +Edit files in `./CIRCUITPY` (for example `code.py`) and rerun `make run-sim` to test changes. + +## Shields + +Board defaults can be set in `boards///circuitpython.toml`: + +```toml +SHIELDS = ["shield1", "shield2"] +``` + +For example, `boards/renesas/ek_ra8d1/circuitpython.toml` enables: + +```toml +SHIELDS = ["rtkmipilcdb00000be"] +``` + +You can override shield selection from the command line: + +```sh +# Single shield +make BOARD=renesas_ek_ra8d1 SHIELD=rtkmipilcdb00000be + +# Multiple shields (comma, semicolon, or space separated) +make BOARD=my_vendor_my_board SHIELDS="shield1,shield2" +``` + +Behavior and precedence: + +- If `SHIELD` or `SHIELDS` is explicitly provided, it overrides board defaults. +- If neither is provided, defaults from `circuitpython.toml` are used. +- Use `SHIELD=` (empty) to disable a board default shield for one build. + ## Testing other boards [Any Zephyr board](https://docs.zephyrproject.org/latest/boards/index.html#) can diff --git a/ports/zephyr-cp/app.overlay b/ports/zephyr-cp/app.overlay new file mode 100644 index 0000000000000..0f34d64a80f1b --- /dev/null +++ b/ports/zephyr-cp/app.overlay @@ -0,0 +1,10 @@ +&zephyr_udc0 { + cdc_acm_console: cdc_acm_console { + compatible = "zephyr,cdc-acm-uart"; + label = "CircuitPython Console"; + }; + cdc_acm_data: cdc_acm_data { + compatible = "zephyr,cdc-acm-uart"; + label = "CircuitPython Data"; + }; +}; diff --git a/ports/zephyr-cp/background.c b/ports/zephyr-cp/background.c index 9afade8913697..56e9e98f1f245 100644 --- a/ports/zephyr-cp/background.c +++ b/ports/zephyr-cp/background.c @@ -9,17 +9,7 @@ #include "py/runtime.h" #include "supervisor/port.h" -#if CIRCUITPY_DISPLAYIO -#include "shared-module/displayio/__init__.h" -#endif - -#if CIRCUITPY_AUDIOBUSIO -#include "common-hal/audiobusio/I2SOut.h" -#endif - -#if CIRCUITPY_AUDIOPWMIO -#include "common-hal/audiopwmio/PWMAudioOut.h" -#endif +#include void port_start_background_tick(void) { } @@ -28,18 +18,12 @@ void port_finish_background_tick(void) { } void port_background_tick(void) { - #if CIRCUITPY_AUDIOPWMIO - audiopwmout_background(); - #endif - #if CIRCUITPY_AUDIOBUSIO - i2s_background(); - #endif -} - -// Allow boards to override this. -MP_WEAK void board_background_task(void) { + // No, ticks. We use Zephyr threads instead. } void port_background_task(void) { - board_background_task(); + // Make sure time advances in the simulator. + #if defined(CONFIG_ARCH_POSIX) + k_busy_wait(100); + #endif } diff --git a/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.c b/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.c new file mode 100644 index 0000000000000..fc0fb9ecc4f8f --- /dev/null +++ b/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.c @@ -0,0 +1,37 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/hostnetwork/HostNetwork.h" + +#include "py/runtime.h" + +//| class HostNetwork: +//| """Native networking for the host simulator.""" +//| +//| def __init__(self) -> None: +//| """Create a HostNetwork instance.""" +//| ... +//| +static mp_obj_t hostnetwork_hostnetwork_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *args) { + mp_arg_check_num(n_args, n_kw, 0, 0, false); + + hostnetwork_hostnetwork_obj_t *self = mp_obj_malloc(hostnetwork_hostnetwork_obj_t, &hostnetwork_hostnetwork_type); + common_hal_hostnetwork_hostnetwork_construct(self); + return MP_OBJ_FROM_PTR(self); +} + +static const mp_rom_map_elem_t hostnetwork_hostnetwork_locals_dict_table[] = { +}; +static MP_DEFINE_CONST_DICT(hostnetwork_hostnetwork_locals_dict, hostnetwork_hostnetwork_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + hostnetwork_hostnetwork_type, + MP_QSTR_HostNetwork, + MP_TYPE_FLAG_NONE, + make_new, hostnetwork_hostnetwork_make_new, + locals_dict, &hostnetwork_hostnetwork_locals_dict + ); diff --git a/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.h b/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.h new file mode 100644 index 0000000000000..009d8c0608743 --- /dev/null +++ b/ports/zephyr-cp/bindings/hostnetwork/HostNetwork.h @@ -0,0 +1,19 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +} hostnetwork_hostnetwork_obj_t; + +extern const mp_obj_type_t hostnetwork_hostnetwork_type; + +void common_hal_hostnetwork_hostnetwork_construct(hostnetwork_hostnetwork_obj_t *self); diff --git a/ports/zephyr-cp/bindings/hostnetwork/__init__.c b/ports/zephyr-cp/bindings/hostnetwork/__init__.c new file mode 100644 index 0000000000000..92302b67d0e97 --- /dev/null +++ b/ports/zephyr-cp/bindings/hostnetwork/__init__.c @@ -0,0 +1,26 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" + +#include "bindings/hostnetwork/__init__.h" +#include "bindings/hostnetwork/HostNetwork.h" + +//| """Host networking support for the native simulator.""" +//| + +static const mp_rom_map_elem_t hostnetwork_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_hostnetwork) }, + { MP_ROM_QSTR(MP_QSTR_HostNetwork), MP_ROM_PTR(&hostnetwork_hostnetwork_type) }, +}; +static MP_DEFINE_CONST_DICT(hostnetwork_module_globals, hostnetwork_module_globals_table); + +const mp_obj_module_t hostnetwork_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&hostnetwork_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_hostnetwork, hostnetwork_module); diff --git a/ports/zephyr-cp/bindings/hostnetwork/__init__.h b/ports/zephyr-cp/bindings/hostnetwork/__init__.h new file mode 100644 index 0000000000000..a6731546bdef1 --- /dev/null +++ b/ports/zephyr-cp/bindings/hostnetwork/__init__.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "bindings/hostnetwork/HostNetwork.h" + +extern hostnetwork_hostnetwork_obj_t common_hal_hostnetwork_obj; diff --git a/ports/zephyr-cp/bindings/zephyr_display/Display.c b/ports/zephyr-cp/bindings/zephyr_display/Display.c new file mode 100644 index 0000000000000..0923618c50a7f --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_display/Display.c @@ -0,0 +1,195 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/zephyr_display/Display.h" + +#include "py/objproperty.h" +#include "py/objtype.h" +#include "py/runtime.h" +#include "shared-bindings/displayio/Group.h" +#include "shared-module/displayio/__init__.h" + +static mp_obj_t zephyr_display_display_make_new(const mp_obj_type_t *type, + size_t n_args, + size_t n_kw, + const mp_obj_t *all_args) { + (void)type; + (void)n_args; + (void)n_kw; + (void)all_args; + mp_raise_NotImplementedError(NULL); + return mp_const_none; +} + +static zephyr_display_display_obj_t *native_display(mp_obj_t display_obj) { + mp_obj_t native = mp_obj_cast_to_native_base(display_obj, &zephyr_display_display_type); + mp_obj_assert_native_inited(native); + return MP_OBJ_TO_PTR(native); +} + +static mp_obj_t zephyr_display_display_obj_show(mp_obj_t self_in, mp_obj_t group_in) { + (void)self_in; + (void)group_in; + mp_raise_AttributeError(MP_ERROR_TEXT(".show(x) removed. Use .root_group = x")); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_show_obj, zephyr_display_display_obj_show); + +static mp_obj_t zephyr_display_display_obj_refresh(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { + ARG_target_frames_per_second, + ARG_minimum_frames_per_second, + }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_target_frames_per_second, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_minimum_frames_per_second, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + zephyr_display_display_obj_t *self = native_display(pos_args[0]); + + uint32_t maximum_ms_per_real_frame = NO_FPS_LIMIT; + mp_int_t minimum_frames_per_second = args[ARG_minimum_frames_per_second].u_int; + if (minimum_frames_per_second > 0) { + maximum_ms_per_real_frame = 1000 / minimum_frames_per_second; + } + + uint32_t target_ms_per_frame; + if (args[ARG_target_frames_per_second].u_obj == mp_const_none) { + target_ms_per_frame = NO_FPS_LIMIT; + } else { + target_ms_per_frame = 1000 / mp_obj_get_int(args[ARG_target_frames_per_second].u_obj); + } + + return mp_obj_new_bool(common_hal_zephyr_display_display_refresh( + self, + target_ms_per_frame, + maximum_ms_per_real_frame)); +} +MP_DEFINE_CONST_FUN_OBJ_KW(zephyr_display_display_refresh_obj, 1, zephyr_display_display_obj_refresh); + +static mp_obj_t zephyr_display_display_obj_get_auto_refresh(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return mp_obj_new_bool(common_hal_zephyr_display_display_get_auto_refresh(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_auto_refresh_obj, zephyr_display_display_obj_get_auto_refresh); + +static mp_obj_t zephyr_display_display_obj_set_auto_refresh(mp_obj_t self_in, mp_obj_t auto_refresh) { + zephyr_display_display_obj_t *self = native_display(self_in); + common_hal_zephyr_display_display_set_auto_refresh(self, mp_obj_is_true(auto_refresh)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_auto_refresh_obj, zephyr_display_display_obj_set_auto_refresh); + +MP_PROPERTY_GETSET(zephyr_display_display_auto_refresh_obj, + (mp_obj_t)&zephyr_display_display_get_auto_refresh_obj, + (mp_obj_t)&zephyr_display_display_set_auto_refresh_obj); + +static mp_obj_t zephyr_display_display_obj_get_brightness(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + mp_float_t brightness = common_hal_zephyr_display_display_get_brightness(self); + if (brightness < 0) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Brightness not adjustable")); + } + return mp_obj_new_float(brightness); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_brightness_obj, zephyr_display_display_obj_get_brightness); + +static mp_obj_t zephyr_display_display_obj_set_brightness(mp_obj_t self_in, mp_obj_t brightness_obj) { + zephyr_display_display_obj_t *self = native_display(self_in); + mp_float_t brightness = mp_obj_get_float(brightness_obj); + if (brightness < 0.0f || brightness > 1.0f) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q must be %d-%d"), MP_QSTR_brightness, 0, 1); + } + bool ok = common_hal_zephyr_display_display_set_brightness(self, brightness); + if (!ok) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Brightness not adjustable")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_brightness_obj, zephyr_display_display_obj_set_brightness); + +MP_PROPERTY_GETSET(zephyr_display_display_brightness_obj, + (mp_obj_t)&zephyr_display_display_get_brightness_obj, + (mp_obj_t)&zephyr_display_display_set_brightness_obj); + +static mp_obj_t zephyr_display_display_obj_get_width(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_zephyr_display_display_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_width_obj, zephyr_display_display_obj_get_width); +MP_PROPERTY_GETTER(zephyr_display_display_width_obj, (mp_obj_t)&zephyr_display_display_get_width_obj); + +static mp_obj_t zephyr_display_display_obj_get_height(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_zephyr_display_display_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_height_obj, zephyr_display_display_obj_get_height); +MP_PROPERTY_GETTER(zephyr_display_display_height_obj, (mp_obj_t)&zephyr_display_display_get_height_obj); + +static mp_obj_t zephyr_display_display_obj_get_rotation(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return MP_OBJ_NEW_SMALL_INT(common_hal_zephyr_display_display_get_rotation(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_rotation_obj, zephyr_display_display_obj_get_rotation); + +static mp_obj_t zephyr_display_display_obj_set_rotation(mp_obj_t self_in, mp_obj_t value) { + zephyr_display_display_obj_t *self = native_display(self_in); + common_hal_zephyr_display_display_set_rotation(self, mp_obj_get_int(value)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_rotation_obj, zephyr_display_display_obj_set_rotation); + +MP_PROPERTY_GETSET(zephyr_display_display_rotation_obj, + (mp_obj_t)&zephyr_display_display_get_rotation_obj, + (mp_obj_t)&zephyr_display_display_set_rotation_obj); + +static mp_obj_t zephyr_display_display_obj_get_root_group(mp_obj_t self_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + return common_hal_zephyr_display_display_get_root_group(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(zephyr_display_display_get_root_group_obj, zephyr_display_display_obj_get_root_group); + +static mp_obj_t zephyr_display_display_obj_set_root_group(mp_obj_t self_in, mp_obj_t group_in) { + zephyr_display_display_obj_t *self = native_display(self_in); + displayio_group_t *group = NULL; + if (group_in != mp_const_none) { + group = MP_OBJ_TO_PTR(native_group(group_in)); + } + + bool ok = common_hal_zephyr_display_display_set_root_group(self, group); + if (!ok) { + mp_raise_ValueError(MP_ERROR_TEXT("Group already used")); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(zephyr_display_display_set_root_group_obj, zephyr_display_display_obj_set_root_group); + +MP_PROPERTY_GETSET(zephyr_display_display_root_group_obj, + (mp_obj_t)&zephyr_display_display_get_root_group_obj, + (mp_obj_t)&zephyr_display_display_set_root_group_obj); + +static const mp_rom_map_elem_t zephyr_display_display_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_show), MP_ROM_PTR(&zephyr_display_display_show_obj) }, + { MP_ROM_QSTR(MP_QSTR_refresh), MP_ROM_PTR(&zephyr_display_display_refresh_obj) }, + + { MP_ROM_QSTR(MP_QSTR_auto_refresh), MP_ROM_PTR(&zephyr_display_display_auto_refresh_obj) }, + { MP_ROM_QSTR(MP_QSTR_brightness), MP_ROM_PTR(&zephyr_display_display_brightness_obj) }, + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&zephyr_display_display_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&zephyr_display_display_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_rotation), MP_ROM_PTR(&zephyr_display_display_rotation_obj) }, + { MP_ROM_QSTR(MP_QSTR_root_group), MP_ROM_PTR(&zephyr_display_display_root_group_obj) }, +}; +static MP_DEFINE_CONST_DICT(zephyr_display_display_locals_dict, zephyr_display_display_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + zephyr_display_display_type, + MP_QSTR_Display, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, zephyr_display_display_make_new, + locals_dict, &zephyr_display_display_locals_dict); diff --git a/ports/zephyr-cp/bindings/zephyr_display/Display.h b/ports/zephyr-cp/bindings/zephyr_display/Display.h new file mode 100644 index 0000000000000..a50dda8fe8b69 --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_display/Display.h @@ -0,0 +1,37 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/displayio/Group.h" +#include "common-hal/zephyr_display/Display.h" + +extern const mp_obj_type_t zephyr_display_display_type; + +#define NO_FPS_LIMIT 0xffffffff + +void common_hal_zephyr_display_display_construct_from_device(zephyr_display_display_obj_t *self, + const struct device *device, + uint16_t rotation, + bool auto_refresh); + +bool common_hal_zephyr_display_display_refresh(zephyr_display_display_obj_t *self, + uint32_t target_ms_per_frame, + uint32_t maximum_ms_per_real_frame); + +bool common_hal_zephyr_display_display_get_auto_refresh(zephyr_display_display_obj_t *self); +void common_hal_zephyr_display_display_set_auto_refresh(zephyr_display_display_obj_t *self, bool auto_refresh); + +uint16_t common_hal_zephyr_display_display_get_width(zephyr_display_display_obj_t *self); +uint16_t common_hal_zephyr_display_display_get_height(zephyr_display_display_obj_t *self); +uint16_t common_hal_zephyr_display_display_get_rotation(zephyr_display_display_obj_t *self); +void common_hal_zephyr_display_display_set_rotation(zephyr_display_display_obj_t *self, int rotation); + +mp_float_t common_hal_zephyr_display_display_get_brightness(zephyr_display_display_obj_t *self); +bool common_hal_zephyr_display_display_set_brightness(zephyr_display_display_obj_t *self, mp_float_t brightness); + +mp_obj_t common_hal_zephyr_display_display_get_root_group(zephyr_display_display_obj_t *self); +bool common_hal_zephyr_display_display_set_root_group(zephyr_display_display_obj_t *self, displayio_group_t *root_group); diff --git a/ports/zephyr-cp/bindings/zephyr_display/__init__.c b/ports/zephyr-cp/bindings/zephyr_display/__init__.c new file mode 100644 index 0000000000000..eecfeeaec58ae --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_display/__init__.c @@ -0,0 +1,24 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/runtime.h" + +#include "bindings/zephyr_display/Display.h" + +static const mp_rom_map_elem_t zephyr_display_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr_display) }, + { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&zephyr_display_display_type) }, +}; + +static MP_DEFINE_CONST_DICT(zephyr_display_module_globals, zephyr_display_module_globals_table); + +const mp_obj_module_t zephyr_display_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&zephyr_display_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_zephyr_display, zephyr_display_module); diff --git a/ports/zephyr-cp/bindings/zephyr_display/__init__.h b/ports/zephyr-cp/bindings/zephyr_display/__init__.h new file mode 100644 index 0000000000000..4256bfac2fe16 --- /dev/null +++ b/ports/zephyr-cp/bindings/zephyr_display/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/ports/zephyr-cp/bindings/zephyr_serial/UART.c b/ports/zephyr-cp/bindings/zephyr_serial/UART.c deleted file mode 100644 index dbb643645f5b9..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_serial/UART.c +++ /dev/null @@ -1,291 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include - -#include "bindings/zephyr_serial/UART.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "shared-bindings/util.h" - -#include "shared/runtime/context_manager_helpers.h" -#include "shared/runtime/interrupt_char.h" - -#include "py/stream.h" -#include "py/objproperty.h" -#include "py/objtype.h" -#include "py/runtime.h" -#include "py/stream.h" - -#define STREAM_DEBUG(...) (void)0 -// #define STREAM_DEBUG(...) mp_printf(&mp_plat_print __VA_OPT__(,) __VA_ARGS__) - -//| class UART: -//| """A bidirectional serial protocol. Already initialized for Zephyr defined -//| busses in :py:mod:`board`. -//| -//| .. raw:: html -//| -//|

-//|

-//| Available on these boards -//|
    -//| {% for board in support_matrix_reverse["zephyr_serial.UART"] %} -//|
  • {{ board }} -//| {% endfor %} -//|
-//|
-//|

-//| -//| """ -//| - -static void validate_timeout(mp_float_t timeout) { - mp_arg_validate_int_range((int)timeout, 0, 100, MP_QSTR_timeout); -} - -// Helper to ensure we have the native super class instead of a subclass. -static zephyr_serial_uart_obj_t *native_uart(mp_obj_t uart_obj) { - mp_obj_t native_uart = mp_obj_cast_to_native_base(uart_obj, MP_OBJ_FROM_PTR(&zephyr_serial_uart_type)); - if (native_uart == MP_OBJ_NULL) { - mp_raise_ValueError_varg(MP_ERROR_TEXT("Must be a %q subclass."), MP_QSTR_UART); - } - mp_obj_assert_native_inited(native_uart); - return MP_OBJ_TO_PTR(native_uart); -} - - -//| def deinit(self) -> None: -//| """Deinitialises the UART and releases any hardware resources for reuse.""" -//| ... -//| -static mp_obj_t _zephyr_serial_uart_obj_deinit(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - zephyr_serial_uart_deinit(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_deinit_obj, _zephyr_serial_uart_obj_deinit); - -static void check_for_deinit(zephyr_serial_uart_obj_t *self) { - if (zephyr_serial_uart_deinited(self)) { - raise_deinited_error(); - } -} - -//| def __enter__(self) -> UART: -//| """No-op used by Context Managers.""" -//| ... -//| -// Provided by context manager helper. - -//| def __exit__(self) -> None: -//| """Automatically deinitializes the hardware when exiting a context. See -//| :ref:`lifetime-and-contextmanagers` for more info.""" -//| ... -//| -// Provided by context manager helper. - -// These are standard stream methods. Code is in py/stream.c. -// -//| def read(self, nbytes: Optional[int] = None) -> Optional[bytes]: -//| """Read bytes. If ``nbytes`` is specified then read at most that many -//| bytes. Otherwise, read everything that arrives until the connection -//| times out. Providing the number of bytes expected is highly recommended -//| because it will be faster. If no bytes are read, return ``None``. -//| -//| .. note:: When no bytes are read due to a timeout, this function returns ``None``. -//| This matches the behavior of `io.RawIOBase.read` in Python 3, but -//| differs from pyserial which returns ``b''`` in that situation. -//| -//| :return: Data read -//| :rtype: bytes or None""" -//| ... -//| - -//| def readinto(self, buf: WriteableBuffer) -> Optional[int]: -//| """Read bytes into the ``buf``. Read at most ``len(buf)`` bytes. -//| -//| :return: number of bytes read and stored into ``buf`` -//| :rtype: int or None (on a non-blocking error) -//| -//| *New in CircuitPython 4.0:* No length parameter is permitted.""" -//| ... -//| - -//| def readline(self) -> bytes: -//| """Read a line, ending in a newline character, or -//| return ``None`` if a timeout occurs sooner, or -//| return everything readable if no newline is found and -//| ``timeout=0`` -//| -//| :return: the line read -//| :rtype: bytes or None""" -//| ... -//| - -//| def write(self, buf: ReadableBuffer) -> Optional[int]: -//| """Write the buffer of bytes to the bus. -//| -//| *New in CircuitPython 4.0:* ``buf`` must be bytes, not a string. -//| -//| :return: the number of bytes written -//| :rtype: int or None""" -//| ... -//| - -// These three methods are used by the shared stream methods. -static mp_uint_t _zephyr_serial_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { - STREAM_DEBUG("_zephyr_serial_uart_read stream %d\n", size); - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - byte *buf = buf_in; - - // make sure we want at least 1 char - if (size == 0) { - return 0; - } - - return zephyr_serial_uart_read(self, buf, size, errcode); -} - -static mp_uint_t _zephyr_serial_uart_write(mp_obj_t self_in, const void *buf_in, mp_uint_t size, int *errcode) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - const byte *buf = buf_in; - - return zephyr_serial_uart_write(self, buf, size, errcode); -} - -static mp_uint_t _zephyr_serial_uart_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - mp_uint_t ret; - if (request == MP_STREAM_POLL) { - mp_uint_t flags = arg; - ret = 0; - if ((flags & MP_STREAM_POLL_RD) && zephyr_serial_uart_rx_characters_available(self) > 0) { - ret |= MP_STREAM_POLL_RD; - } - if ((flags & MP_STREAM_POLL_WR) && zephyr_serial_uart_ready_to_tx(self)) { - ret |= MP_STREAM_POLL_WR; - } - } else { - *errcode = MP_EINVAL; - ret = MP_STREAM_ERROR; - } - return ret; -} - -//| baudrate: int -//| """The current baudrate.""" -static mp_obj_t _zephyr_serial_uart_obj_get_baudrate(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - return MP_OBJ_NEW_SMALL_INT(zephyr_serial_uart_get_baudrate(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_get_baudrate_obj, _zephyr_serial_uart_obj_get_baudrate); - -static mp_obj_t _zephyr_serial_uart_obj_set_baudrate(mp_obj_t self_in, mp_obj_t baudrate) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - zephyr_serial_uart_set_baudrate(self, mp_obj_get_int(baudrate)); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(_zephyr_serial_uart_set_baudrate_obj, _zephyr_serial_uart_obj_set_baudrate); - - -MP_PROPERTY_GETSET(_zephyr_serial_uart_baudrate_obj, - (mp_obj_t)&_zephyr_serial_uart_get_baudrate_obj, - (mp_obj_t)&_zephyr_serial_uart_set_baudrate_obj); - -//| in_waiting: int -//| """The number of bytes in the input buffer, available to be read""" -static mp_obj_t _zephyr_serial_uart_obj_get_in_waiting(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - return MP_OBJ_NEW_SMALL_INT(zephyr_serial_uart_rx_characters_available(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_get_in_waiting_obj, _zephyr_serial_uart_obj_get_in_waiting); - -MP_PROPERTY_GETTER(_zephyr_serial_uart_in_waiting_obj, - (mp_obj_t)&_zephyr_serial_uart_get_in_waiting_obj); - -//| timeout: float -//| """The current timeout, in seconds (float).""" -//| -static mp_obj_t _zephyr_serial_uart_obj_get_timeout(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - return mp_obj_new_float(zephyr_serial_uart_get_timeout(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_get_timeout_obj, _zephyr_serial_uart_obj_get_timeout); - -static mp_obj_t _zephyr_serial_uart_obj_set_timeout(mp_obj_t self_in, mp_obj_t timeout) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - mp_float_t timeout_float = mp_obj_get_float(timeout); - validate_timeout(timeout_float); - zephyr_serial_uart_set_timeout(self, timeout_float); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(_zephyr_serial_uart_set_timeout_obj, _zephyr_serial_uart_obj_set_timeout); - - -MP_PROPERTY_GETSET(_zephyr_serial_uart_timeout_obj, - (mp_obj_t)&_zephyr_serial_uart_get_timeout_obj, - (mp_obj_t)&_zephyr_serial_uart_set_timeout_obj); - -//| def reset_input_buffer(self) -> None: -//| """Discard any unread characters in the input buffer.""" -//| ... -//| -//| -static mp_obj_t _zephyr_serial_uart_obj_reset_input_buffer(mp_obj_t self_in) { - zephyr_serial_uart_obj_t *self = native_uart(self_in); - check_for_deinit(self); - zephyr_serial_uart_clear_rx_buffer(self); - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(_zephyr_serial_uart_reset_input_buffer_obj, _zephyr_serial_uart_obj_reset_input_buffer); - -static const mp_rom_map_elem_t _zephyr_serial_uart_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&_zephyr_serial_uart_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&_zephyr_serial_uart_deinit_obj) }, - { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, - { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, - - // Standard stream methods. - { MP_ROM_QSTR(MP_QSTR_read), MP_ROM_PTR(&mp_stream_read_obj) }, - { MP_ROM_QSTR(MP_QSTR_readline), MP_ROM_PTR(&mp_stream_unbuffered_readline_obj)}, - { MP_ROM_QSTR(MP_QSTR_readinto), MP_ROM_PTR(&mp_stream_readinto_obj) }, - { MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mp_stream_write_obj) }, - - { MP_ROM_QSTR(MP_QSTR_reset_input_buffer), MP_ROM_PTR(&_zephyr_serial_uart_reset_input_buffer_obj) }, - - // Properties - { MP_ROM_QSTR(MP_QSTR_baudrate), MP_ROM_PTR(&_zephyr_serial_uart_baudrate_obj) }, - { MP_ROM_QSTR(MP_QSTR_in_waiting), MP_ROM_PTR(&_zephyr_serial_uart_in_waiting_obj) }, - { MP_ROM_QSTR(MP_QSTR_timeout), MP_ROM_PTR(&_zephyr_serial_uart_timeout_obj) }, - -}; -static MP_DEFINE_CONST_DICT(_zephyr_serial_uart_locals_dict, _zephyr_serial_uart_locals_dict_table); - -static const mp_stream_p_t uart_stream_p = { - .read = _zephyr_serial_uart_read, - .write = _zephyr_serial_uart_write, - .ioctl = _zephyr_serial_uart_ioctl, - .is_text = false, - // Disallow optional length argument for .readinto() - .pyserial_readinto_compatibility = true, -}; - -MP_DEFINE_CONST_OBJ_TYPE( - zephyr_serial_uart_type, - MP_QSTR_UART, - MP_TYPE_FLAG_ITER_IS_ITERNEXT | MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, - locals_dict, &_zephyr_serial_uart_locals_dict, - iter, mp_stream_unbuffered_iter, - protocol, &uart_stream_p - ); diff --git a/ports/zephyr-cp/bindings/zephyr_serial/UART.h b/ports/zephyr-cp/bindings/zephyr_serial/UART.h deleted file mode 100644 index 704b9a2d605a3..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_serial/UART.h +++ /dev/null @@ -1,41 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "common-hal/microcontroller/Pin.h" -#include "common-hal/zephyr_serial/UART.h" -#include "py/ringbuf.h" - -#include - -extern const mp_obj_type_t zephyr_serial_uart_type; - -// Construct an underlying UART object. -extern void zephyr_serial_uart_construct(zephyr_serial_uart_obj_t *self, - const struct device *const uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer); - -extern void zephyr_serial_uart_deinit(zephyr_serial_uart_obj_t *self); -extern bool zephyr_serial_uart_deinited(zephyr_serial_uart_obj_t *self); - -// Read characters. len is in characters NOT bytes! -extern size_t zephyr_serial_uart_read(zephyr_serial_uart_obj_t *self, - uint8_t *data, size_t len, int *errcode); - -// Write characters. len is in characters NOT bytes! -extern size_t zephyr_serial_uart_write(zephyr_serial_uart_obj_t *self, - const uint8_t *data, size_t len, int *errcode); - -extern uint32_t zephyr_serial_uart_get_baudrate(zephyr_serial_uart_obj_t *self); -extern void zephyr_serial_uart_set_baudrate(zephyr_serial_uart_obj_t *self, uint32_t baudrate); -extern mp_float_t zephyr_serial_uart_get_timeout(zephyr_serial_uart_obj_t *self); -extern void zephyr_serial_uart_set_timeout(zephyr_serial_uart_obj_t *self, mp_float_t timeout); - -extern uint32_t zephyr_serial_uart_rx_characters_available(zephyr_serial_uart_obj_t *self); -extern void zephyr_serial_uart_clear_rx_buffer(zephyr_serial_uart_obj_t *self); -extern bool zephyr_serial_uart_ready_to_tx(zephyr_serial_uart_obj_t *self); - -extern void zephyr_serial_uart_never_reset(zephyr_serial_uart_obj_t *self); diff --git a/ports/zephyr-cp/bindings/zephyr_serial/__init__.c b/ports/zephyr-cp/bindings/zephyr_serial/__init__.c deleted file mode 100644 index f4a3c7b92e3be..0000000000000 --- a/ports/zephyr-cp/bindings/zephyr_serial/__init__.c +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include - -#include "py/obj.h" -#include "py/runtime.h" - -#include "shared-bindings/microcontroller/Pin.h" -#include "bindings/zephyr_serial/__init__.h" -#include "bindings/zephyr_serial/UART.h" - -#include "py/runtime.h" - -//| """Zephyr UART driver for fixed busses.""" - -static const mp_rom_map_elem_t zephyr_serial_module_globals_table[] = { - { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_zephyr_serial) }, - { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&zephyr_serial_uart_type) }, -}; - -static MP_DEFINE_CONST_DICT(zephyr_serial_module_globals, zephyr_serial_module_globals_table); - -const mp_obj_module_t zephyr_serial_module = { - .base = { &mp_type_module }, - .globals = (mp_obj_dict_t *)&zephyr_serial_module_globals, -}; - -MP_REGISTER_MODULE(MP_QSTR_zephyr_serial, zephyr_serial_module); diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..989332a143103 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Adafruit Industries LLC Feather Bluefruit Sense" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = false +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = false diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..eacb0f9607df9 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_sense_zephyr/circuitpython.toml @@ -0,0 +1,4 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] +USB_VID=0x239A +USB_PID=0x8088 +NAME="Feather Bluefruit Sense" diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..51c02dad4cf30 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Adafruit Industries LLC Feather nRF52840 Express" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..c4d1099a77e1d --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_nrf52840_zephyr/circuitpython.toml @@ -0,0 +1,4 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] +USB_VID=0x239A +USB_PID=0x802A +NAME="Feather nRF52840 Express" diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..c1d6e2ed3ce8f --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Adafruit Industries LLC Feather RP2040" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..9d3c229ed1b45 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit/feather_rp2040_zephyr/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf new file mode 100644 index 0000000000000..20176b34be052 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.conf @@ -0,0 +1,10 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_USE_DT_CODE_PARTITION=y + +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay new file mode 100644 index 0000000000000..e01ebd6df1f51 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_nrf52840_sense_uf2.overlay @@ -0,0 +1,46 @@ +/ { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,uart-mcumgr = &uart0; + zephyr,bt-mon-uart = &uart0; + zephyr,bt-c2h-uart = &uart0; + }; +}; + +&zephyr_udc0 { + /delete-node/ board_cdc_acm_uart; +}; + + +&gd25q16 { + /delete-node/ partitions; +}; + +/delete-node/ &storage_partition; +/delete-node/ &code_partition; + +&flash0 { + partitions { + code_partition: partition@26000 { + label = "Application"; + reg = <0x00026000 0x000c4000>; + }; + + storage_partition: partition@ea000 { + label = "storage"; + reg = <0x000ea000 0x00008000>; + }; + + nvm_partition: partition@f2000 { + label = "nvm"; + reg = <0x000f2000 0x00002000>; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf new file mode 100644 index 0000000000000..20176b34be052 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.conf @@ -0,0 +1,10 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_USE_DT_CODE_PARTITION=y + +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n diff --git a/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay new file mode 100644 index 0000000000000..e01ebd6df1f51 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_nrf52840_uf2.overlay @@ -0,0 +1,46 @@ +/ { + chosen { + zephyr,console = &uart0; + zephyr,shell-uart = &uart0; + zephyr,uart-mcumgr = &uart0; + zephyr,bt-mon-uart = &uart0; + zephyr,bt-c2h-uart = &uart0; + }; +}; + +&zephyr_udc0 { + /delete-node/ board_cdc_acm_uart; +}; + + +&gd25q16 { + /delete-node/ partitions; +}; + +/delete-node/ &storage_partition; +/delete-node/ &code_partition; + +&flash0 { + partitions { + code_partition: partition@26000 { + label = "Application"; + reg = <0x00026000 0x000c4000>; + }; + + storage_partition: partition@ea000 { + label = "storage"; + reg = <0x000ea000 0x00008000>; + }; + + nvm_partition: partition@f2000 { + label = "nvm"; + reg = <0x000f2000 0x00002000>; + }; + }; +}; + +&uart0 { + status = "okay"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf b/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf new file mode 100644 index 0000000000000..91c3c15b37d1e --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_rp2040.conf @@ -0,0 +1 @@ +CONFIG_GPIO=y diff --git a/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay b/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay new file mode 100644 index 0000000000000..ce9083dd62d42 --- /dev/null +++ b/ports/zephyr-cp/boards/adafruit_feather_rp2040.overlay @@ -0,0 +1,34 @@ +&flash0 { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Reserved memory for the second stage bootloader */ + second_stage_bootloader: partition@0 { + label = "second_stage_bootloader"; + reg = <0x00000000 0x100>; + read-only; + }; + + code_partition: partition@100 { + label = "code-partition"; + reg = <0x100 (0x180000 - 0x100)>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(2) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/board_aliases.cmake b/ports/zephyr-cp/boards/board_aliases.cmake index b8df9fb87f5b8..ad0c1b5a57acf 100644 --- a/ports/zephyr-cp/boards/board_aliases.cmake +++ b/ports/zephyr-cp/boards/board_aliases.cmake @@ -1,8 +1,53 @@ -set(pca10056_BOARD_ALIAS nrf52840dk/nrf52840) -set(renesas_ek_ra6m5_BOARD_ALIAS ek_ra6m5) -set(renesas_ek_ra8d1_BOARD_ALIAS ek_ra8d1) -set(nordic_nrf54l15dk_BOARD_ALIAS nrf54l15dk/nrf54l15/cpuapp) -set(nordic_nrf5340dk_BOARD_ALIAS nrf5340dk/nrf5340/cpuapp) -set(nordic_nrf7002dk_BOARD_ALIAS nrf7002dk/nrf5340/cpuapp) -set(st_stm32h7b3i_dk_BOARD_ALIAS stm32h7b3i_dk) -set(st_nucleo_u575zi_q_BOARD_ALIAS nucleo_u575zi_q/stm32u575xx) +# Workaround for Zephyr alias handling with BOARD_QUALIFIERS in newer Zephyr. +# +# Instead of using _BOARD_ALIAS variables, translate BOARD directly so +# we don't end up with a spurious trailing '/' in BOARD_QUALIFIERS. +macro(cp_board_alias alias target) + if(BOARD STREQUAL "${alias}") + if(NOT "${target}" MATCHES "^([^@/]+)(@[^@/]+)?(/([^@]+))?$") + message(FATAL_ERROR "Invalid alias target '${target}'") + endif() + + set(BOARD_ALIAS "${alias}" CACHE STRING "Board alias, provided by user") + set(BOARD "${CMAKE_MATCH_1}") + + if(CMAKE_MATCH_2) + string(REPLACE "@" "" _rev "${CMAKE_MATCH_2}") + set(BOARD_REVISION "${_rev}") + else() + unset(BOARD_REVISION) + endif() + + if(CMAKE_MATCH_4) + set(BOARD_QUALIFIERS "${CMAKE_MATCH_4}") + else() + unset(BOARD_QUALIFIERS) + endif() + endif() +endmacro() + +cp_board_alias(pca10056 nrf52840dk/nrf52840) +cp_board_alias(adafruit_feather_nrf52840_zephyr adafruit_feather_nrf52840/nrf52840/uf2) +cp_board_alias(adafruit_feather_nrf52840_sense_zephyr adafruit_feather_nrf52840/nrf52840/sense/uf2) +cp_board_alias(adafruit_feather_rp2040_zephyr adafruit_feather_rp2040/rp2040) +cp_board_alias(renesas_ek_ra6m5 ek_ra6m5) +cp_board_alias(renesas_ek_ra8d1 ek_ra8d1) +cp_board_alias(renesas_da14695_dk_usb da14695_dk_usb) +cp_board_alias(native_native_sim native_sim/native) +cp_board_alias(native_nrf5340bsim nrf5340bsim/nrf5340/cpuapp) +cp_board_alias(nordic_nrf54l15dk nrf54l15dk/nrf54l15/cpuapp) +cp_board_alias(nordic_nrf54h20dk nrf54h20dk/nrf54h20/cpuapp) +cp_board_alias(nordic_nrf5340dk nrf5340dk/nrf5340/cpuapp) +cp_board_alias(nordic_nrf7002dk nrf7002dk/nrf5340/cpuapp) +cp_board_alias(nxp_frdm_mcxn947 frdm_mcxn947/mcxn947/cpu0) +cp_board_alias(nxp_frdm_rw612 frdm_rw612) +cp_board_alias(nxp_mimxrt1170_evk mimxrt1170_evk@A/mimxrt1176/cm7) +cp_board_alias(st_stm32h7b3i_dk stm32h7b3i_dk) +cp_board_alias(st_stm32h750b_dk stm32h750b_dk/stm32h750xx/ext_flash_app) +cp_board_alias(st_stm32wba65i_dk1 stm32wba65i_dk1) +cp_board_alias(st_nucleo_u575zi_q nucleo_u575zi_q/stm32u575xx) +cp_board_alias(raspberrypi_rpi_pico_zephyr rpi_pico/rp2040) +cp_board_alias(raspberrypi_rpi_pico_w_zephyr rpi_pico/rp2040/w) +cp_board_alias(raspberrypi_rpi_pico2_zephyr rpi_pico2/rp2350a/m33) +cp_board_alias(raspberrypi_rpi_pico2_w_zephyr rpi_pico2/rp2350a/m33/w) +cp_board_alias(st_nucleo_n657x0_q nucleo_n657x0_q/stm32n657xx) diff --git a/ports/zephyr-cp/boards/da14695_dk_usb.conf b/ports/zephyr-cp/boards/da14695_dk_usb.conf new file mode 100644 index 0000000000000..145a93934070f --- /dev/null +++ b/ports/zephyr-cp/boards/da14695_dk_usb.conf @@ -0,0 +1,20 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=3 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/da14695_dk_usb.overlay b/ports/zephyr-cp/boards/da14695_dk_usb.overlay new file mode 100644 index 0000000000000..fbc1817c759c6 --- /dev/null +++ b/ports/zephyr-cp/boards/da14695_dk_usb.overlay @@ -0,0 +1,10 @@ +&flash0 { + partitions{ + circuitpy_partition: partition@118000 { + label = "circuitpy"; + reg = <0x118000 (DT_SIZE_M(4) - DT_SIZE_K(1120))>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/ek_ra8d1.conf b/ports/zephyr-cp/boards/ek_ra8d1.conf new file mode 100644 index 0000000000000..f979d31e751f1 --- /dev/null +++ b/ports/zephyr-cp/boards/ek_ra8d1.conf @@ -0,0 +1,3 @@ + +# Enable Zephyr display subsystem so DT chosen zephyr,display creates a device. +CONFIG_DISPLAY=y diff --git a/ports/zephyr-cp/boards/ek_ra8d1.overlay b/ports/zephyr-cp/boards/ek_ra8d1.overlay index 29735d02b8fab..c13dfc457f508 100644 --- a/ports/zephyr-cp/boards/ek_ra8d1.overlay +++ b/ports/zephyr-cp/boards/ek_ra8d1.overlay @@ -1,3 +1,5 @@ &s28hl512t { /delete-node/ partitions; }; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.conf b/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.conf new file mode 100644 index 0000000000000..61f2d18ca3c78 --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.conf @@ -0,0 +1 @@ +CONFIG_DMA_TCD_QUEUE_SIZE=4 diff --git a/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay b/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay new file mode 100644 index 0000000000000..83242201c5b12 --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_mcxn947_mcxn947_cpu0.overlay @@ -0,0 +1,5 @@ +&w25q64jvssiq { + /delete-node/ partitions; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/frdm_rw612.conf b/ports/zephyr-cp/boards/frdm_rw612.conf new file mode 100644 index 0000000000000..ac9a43646a18c --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_rw612.conf @@ -0,0 +1,47 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y + +CONFIG_WIFI=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT_EVENT_INFO=y + +CONFIG_NET_HOSTNAME_ENABLE=y +CONFIG_NET_HOSTNAME_DYNAMIC=y +CONFIG_NET_HOSTNAME="circuitpython" + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_RSA_C=y +CONFIG_MBEDTLS_PKCS1_V15=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=y +CONFIG_MBEDTLS_ENTROPY_C=y +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CTR_DRBG_C=y +CONFIG_MBEDTLS_SHA1=y +CONFIG_MBEDTLS_USE_PSA_CRYPTO=n + +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=8 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 + +CONFIG_UDC_WORKQUEUE_STACK_SIZE=1024 diff --git a/ports/zephyr-cp/boards/frdm_rw612.overlay b/ports/zephyr-cp/boards/frdm_rw612.overlay new file mode 100644 index 0000000000000..c6a021d999956 --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_rw612.overlay @@ -0,0 +1,11 @@ +&w25q512jvfiq { + partitions { + /delete-node/ partition@620000; + circuitpy_partition: partition@620000 { + label = "circuitpy"; + reg = <0x00620000 (DT_SIZE_M(58) - DT_SIZE_K(128))>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay b/ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay new file mode 100644 index 0000000000000..9c517e4325514 --- /dev/null +++ b/ports/zephyr-cp/boards/frdm_rw612_rw612_cpu0.overlay @@ -0,0 +1,12 @@ +&w25q512jvfiq { + partitions { + /delete-node/ storage_partition; + circuitpy_partition: partition@620000 { + label = "circuitpy"; + reg = <0x00620000 (DT_SIZE_M(58) - DT_SIZE_K(128))>; + }; + } + +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.conf b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.conf new file mode 100644 index 0000000000000..61f2d18ca3c78 --- /dev/null +++ b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.conf @@ -0,0 +1 @@ +CONFIG_DMA_TCD_QUEUE_SIZE=4 diff --git a/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay new file mode 100644 index 0000000000000..ac6fdd8654e29 --- /dev/null +++ b/ports/zephyr-cp/boards/mimxrt1170_evk_mimxrt1176_cm7.overlay @@ -0,0 +1,15 @@ +&is25wp128 { + partitions { + /delete-node/ partition@e20000; + circuitpy_partition: partition@e20000 { + label = "circuitpy"; + reg = <0x00e20000 (DT_SIZE_M(2) - DT_SIZE_K(128))>; + }; + }; +}; + +&sai1 { + mclk-output; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml new file mode 100644 index 0000000000000..b293424b539fc --- /dev/null +++ b/ports/zephyr-cp/boards/native/native_sim/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "POSIX/Native Boards Native simulator - native_sim" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = true # Zephyr board has audiobusio +audiocore = true # Zephyr board has audiobusio +audiodelays = true # Zephyr board has audiobusio +audiofilters = true # Zephyr board has audiobusio +audiofreeverb = true # Zephyr board has audiobusio +audioio = false +audiomixer = true # Zephyr board has audiobusio +audiomp3 = true # Zephyr board has audiobusio +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has displayio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true # Zephyr networking enabled +hostnetwork = true # Zephyr board has hostnetwork +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = true # Zephyr networking enabled +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = true # Zephyr networking enabled +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = true # Zephyr board has audiobusio +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = true # Zephyr board has zephyr_display +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml b/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml new file mode 100644 index 0000000000000..fbda7c563b835 --- /dev/null +++ b/ports/zephyr-cp/boards/native/native_sim/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "exe"] diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml new file mode 100644 index 0000000000000..6d6d5841bbc5f --- /dev/null +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "POSIX/Native Boards nRF5340 simulated boards (BabbleSim)" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml b/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/native/nrf5340bsim/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/native_sim.conf b/ports/zephyr-cp/boards/native_sim.conf new file mode 100644 index 0000000000000..e02cd0ac84eb2 --- /dev/null +++ b/ports/zephyr-cp/boards/native_sim.conf @@ -0,0 +1,43 @@ +CONFIG_EMUL=y +CONFIG_GPIO=y +CONFIG_NATIVE_SIM_SLOWDOWN_TO_REAL_TIME=n + +# So we can test safe mode +CONFIG_NATIVE_SIM_REBOOT=y + +CONFIG_TRACING=y +CONFIG_TRACING_PERFETTO=y +CONFIG_TRACING_SYNC=y +CONFIG_TRACING_BACKEND_POSIX=y +CONFIG_TRACING_GPIO=y + +# I2C emulation for testing +CONFIG_I2C_EMUL=y + +# Display emulation for display/terminal golden tests. +CONFIG_DISPLAY=y +CONFIG_SDL_DISPLAY=y + +# EEPROM emulation for testing +CONFIG_EEPROM=y +CONFIG_EEPROM_AT24=y +CONFIG_EEPROM_AT2X_EMUL=y + +# I2S SDL emulation for audio testing +CONFIG_I2S_SDL=y + +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_TCP=y +CONFIG_NET_SOCKETS=y +CONFIG_ETH_NATIVE_TAP=n +CONFIG_NET_DRIVERS=y +CONFIG_NET_SOCKETS_OFFLOAD=y +CONFIG_NET_NATIVE_OFFLOADED_SOCKETS=y +CONFIG_HEAP_MEM_POOL_SIZE=1024 + +CONFIG_NET_LOG=y + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SHA1=y +CONFIG_MBEDTLS_SHA256=y diff --git a/ports/zephyr-cp/boards/native_sim.overlay b/ports/zephyr-cp/boards/native_sim.overlay new file mode 100644 index 0000000000000..aee9d17f9d099 --- /dev/null +++ b/ports/zephyr-cp/boards/native_sim.overlay @@ -0,0 +1,56 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Device tree overlay for CircuitPython on native_sim. + * Adds simulated SRAM region required by CircuitPython build system. + */ + +#include + +/ { + sram0: memory@20000000 { + device_type = "memory"; + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x20000000 DT_SIZE_M(1)>; + zephyr,memory-region = "SRAM"; + }; + + chosen { + zephyr,sram = &sram0; + /delete-property/ zephyr,flash; + /delete-property/ zephyr,code-partition; + }; +}; + +&flash0 { + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + circuitpy_partition: partition@0 { + label = "circuitpy"; + reg = <0x00000000 DT_SIZE_K(2040)>; + }; + + nvm_partition: partition@1fe000 { + label = "nvm"; + reg = <0x001fe000 0x00002000>; + }; + }; +}; + +/* Add emulated I2C devices for testing */ +&i2c0 { + at24_eeprom: eeprom@50 { + compatible = "atmel,at24"; + reg = <0x50>; + size = <256>; + pagesize = <8>; + address-width = <8>; + timeout = <5>; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml index 222a325e346b6..b5d36e615669b 100644 --- a/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf5340dk/autogen_board_info.toml @@ -2,55 +2,58 @@ name = "Nordic Semiconductor nRF5340 DK" [modules] -__future__ = false -_bleio = false +__future__ = true +_bleio = true # Zephyr board has _bleio _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false atexit = false -audiobusio = false -audiocore = false -audiodelays = false -audiofilters = false -audiofreeverb = false +audiobusio = true # Zephyr board has audiobusio +audiocore = true # Zephyr board has audiobusio +audiodelays = true # Zephyr board has audiobusio +audiofilters = true # Zephyr board has audiobusio +audiofreeverb = true # Zephyr board has audiobusio audioio = false -audiomixer = false -audiomp3 = false +audiomixer = true # Zephyr board has audiobusio +audiomp3 = true # Zephyr board has audiobusio audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false -hashlib = false -i2cdisplaybus = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false @@ -59,14 +62,16 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false -math = false +lvfontio = true # Zephyr board has busio +math = true max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false microcontroller = true -msgpack = false +mipidsi = false +msgpack = true neopixel_write = false nvm = false onewireio = false @@ -76,27 +81,28 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +qspibus = false +rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false -synthio = false -terminalio = false -tilepalettemapper = false +supervisor = true +synthio = true # Zephyr board has audiobusio +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = true @@ -105,10 +111,10 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false -zephyr_serial = true -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml new file mode 100644 index 0000000000000..2c72ba1570de2 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Nordic Semiconductor nRF54H20 DK" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/nordic/nrf54h20dk/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml index 41631c3d6c02c..d981883bf65d2 100644 --- a/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf54l15dk/autogen_board_info.toml @@ -2,15 +2,15 @@ name = "Nordic Semiconductor nRF54L15 DK" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -24,33 +24,36 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false -hashlib = false -i2cdisplaybus = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false @@ -59,14 +62,16 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false -math = false +lvfontio = true # Zephyr board has busio +math = true max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false microcontroller = true -msgpack = false +mipidsi = false +msgpack = true neopixel_write = false nvm = false onewireio = false @@ -76,27 +81,28 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +qspibus = false +rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = false @@ -105,10 +111,10 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false -zephyr_serial = true -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml index ea009aa36e1ca..5faed268cdb7d 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/autogen_board_info.toml @@ -2,8 +2,8 @@ name = "Nordic Semiconductor nRF7002 DK" [modules] -__future__ = false -_bleio = false +__future__ = true +_bleio = true # Zephyr board has _bleio _eve = false _pew = false _pixelmap = false @@ -24,49 +24,54 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false -hashlib = false -i2cdisplaybus = false +hashlib = true # Zephyr networking enabled +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false -ipaddress = false +ipaddress = true # Zephyr networking enabled is31fl3741 = false jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false -math = false +lvfontio = true # Zephyr board has busio +math = true max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false microcontroller = true -msgpack = false +mipidsi = false +msgpack = true neopixel_write = false nvm = false onewireio = false @@ -76,27 +81,28 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +qspibus = false +rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = true # Zephyr networking enabled spitarget = false ssl = true # Zephyr networking enabled storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = true @@ -105,10 +111,10 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = true # Zephyr board has wifi +zephyr_display = false zephyr_kernel = false -zephyr_serial = true zlib = false diff --git a/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml b/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml index 76b10578813bd..191ed9d2d2c52 100644 --- a/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml +++ b/ports/zephyr-cp/boards/nordic/nrf7002dk/circuitpython.toml @@ -2,3 +2,4 @@ CIRCUITPY_BUILD_EXTENSIONS = ["elf"] USB_VID=0x239A USB_PID=0x8168 BLOBS=["nrf_wifi"] +DISABLED_MODULES=["aesio", "adafruit_bus_device", "zlib"] diff --git a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf new file mode 100644 index 0000000000000..57628a61e2057 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.conf @@ -0,0 +1,35 @@ +# Configuration for nrf5340bsim simulated board +# Mirror settings from native_sim.conf for compatibility + +CONFIG_GPIO=y + +# Enable Bluetooth stack - bsim is for BT simulation +CONFIG_BT=y +CONFIG_BT_HCI=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_BROADCASTER=y + +CONFIG_BT_L2CAP_TX_MTU=253 +CONFIG_BT_BUF_CMD_TX_COUNT=2 +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_HCI_VS=y +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=3 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 + +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 + +# Ensure the network core image starts when using native simulator +CONFIG_NATIVE_SIMULATOR_AUTOSTART_MCU=y + +CONFIG_TRACING=y +CONFIG_TRACING_PERFETTO=y +CONFIG_TRACING_SYNC=y +CONFIG_TRACING_BACKEND_POSIX=y +CONFIG_TRACING_GPIO=y diff --git a/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay new file mode 100644 index 0000000000000..eeb043c6f3ce3 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf5340bsim_nrf5340_cpuapp.overlay @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: Apache-2.0 */ + +/ { + chosen { + zephyr,sram = &sram0; + }; +}; + +&sram0 { + compatible = "zephyr,memory-region", "mmio-sram"; + zephyr,memory-region = "SRAM"; +}; + +&flash0 { + /delete-node/ partitions; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + circuitpy_partition: partition@0 { + label = "circuitpy"; + reg = <0x00000000 DT_SIZE_K(1024)>; + }; + }; +}; + +/* Note: bsim doesn't have USB, so we don't include app.overlay */ diff --git a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf index fa0532e815069..145a93934070f 100644 --- a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -4,3 +4,17 @@ CONFIG_BT_CENTRAL=y CONFIG_BT_BROADCASTER=y CONFIG_BT_OBSERVER=y CONFIG_BT_EXT_ADV=y + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=3 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay b/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 0000000000000..eb899df8cc9ea --- /dev/null +++ b/ports/zephyr-cp/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,24 @@ +&pinctrl { + i2s0_default_alt: i2s0_default_alt { + group1 { + psels = , + , + , + , + ; + }; + }; +}; + +&clock { + hfclkaudio-frequency = <11289600>; +}; + +i2s_rxtx: &i2s0 { + status = "okay"; + pinctrl-0 = <&i2s0_default_alt>; + pinctrl-names = "default"; + clock-source = "ACLK"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf new file mode 100644 index 0000000000000..a55b90c50e747 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.conf @@ -0,0 +1,8 @@ +CONFIG_FLASH_MSPI_NOR_LAYOUT_PAGE_SIZE=4096 + +# Reduce flash usage for this board. +CONFIG_LOG=y +CONFIG_LOG_MAX_LEVEL=2 +CONFIG_ASSERT=n +CONFIG_FRAME_POINTER=n +CONFIG_HW_STACK_PROTECTION=n diff --git a/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay new file mode 100644 index 0000000000000..a70eede551ed8 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf54h20dk_nrf54h20_cpuapp.overlay @@ -0,0 +1,47 @@ +&gpio6 { + status = "okay"; + zephyr,pm-device-runtime-auto; +}; + +&exmif { + status = "okay"; + zephyr,pm-device-runtime-auto; +}; + +&mx25uw63 { + status = "okay"; +}; + +/* + * SDA = P2.11 + * SCL = P2.10 + */ + +&pinctrl { + i2c130_default: i2c130_default { + group1 { + psels = , + ; + }; + }; + + i2c130_sleep: i2c130_sleep { + group1 { + psels = , + ; + low-power-enable; + }; + }; +}; + +&i2c130 { + clock-frequency = ; + pinctrl-0 = <&i2c130_default>; + pinctrl-1 = <&i2c130_sleep>; + pinctrl-names = "default", "sleep"; + zephyr,concat-buf-size = <256>; + memory-regions = <&cpuapp_dma_region>; + status = "okay"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay b/ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay new file mode 100644 index 0000000000000..39db1e981afe7 --- /dev/null +++ b/ports/zephyr-cp/boards/nrf54l15dk_nrf54l15_cpuapp.overlay @@ -0,0 +1 @@ +// No app.overlay because it doesn't have USB. diff --git a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf b/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf index 6f01624bb3a4a..afb546a980d2f 100644 --- a/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/boards/nrf7002dk_nrf5340_cpuapp.conf @@ -1,12 +1,20 @@ CONFIG_NETWORKING=y CONFIG_WIFI=y -CONFIG_MBEDTLS_TLS_VERSION_1_2=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y CONFIG_MBEDTLS_USE_PSA_CRYPTO=n +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 + CONFIG_BT=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_CENTRAL=y CONFIG_BT_BROADCASTER=y CONFIG_BT_OBSERVER=y CONFIG_BT_EXT_ADV=y + +CONFIG_LOG=n +CONFIG_ASSERT=n +CONFIG_TEST_RANDOM_GENERATOR=y diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml new file mode 100644 index 0000000000000..8105f7731daae --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "NXP Semiconductors FRDM-MCXN947" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = true # Zephyr board has audiobusio +audiocore = true # Zephyr board has audiobusio +audiodelays = true # Zephyr board has audiobusio +audiofilters = true # Zephyr board has audiobusio +audiofreeverb = true # Zephyr board has audiobusio +audioio = false +audiomixer = true # Zephyr board has audiobusio +audiomp3 = true # Zephyr board has audiobusio +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = true # Zephyr board has audiobusio +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/nxp/frdm_mcxn947/circuitpython.toml b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_mcxn947/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml new file mode 100644 index 0000000000000..a23bc81503c2d --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "NXP Semiconductors FRDM-RW612" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true # Zephyr networking enabled +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = true # Zephyr networking enabled +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = true # Zephyr networking enabled +spitarget = false +ssl = true # Zephyr networking enabled +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = true # Zephyr board has wifi +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/nxp/frdm_rw612/circuitpython.toml b/ports/zephyr-cp/boards/nxp/frdm_rw612/circuitpython.toml new file mode 100644 index 0000000000000..9bceea470cab1 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/frdm_rw612/circuitpython.toml @@ -0,0 +1,5 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +BLOBS=["hal_nxp"] + +[blob_fetch_args] +hal_nxp = ["--allow-regex", "^rw61x/"] diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml new file mode 100644 index 0000000000000..e97292fee76cb --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "NXP Semiconductors MIMXRT1170-EVK" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = true # Zephyr board has audiobusio +audiocore = true # Zephyr board has audiobusio +audiodelays = true # Zephyr board has audiobusio +audiofilters = true # Zephyr board has audiobusio +audiofreeverb = true # Zephyr board has audiobusio +audioio = false +audiomixer = true # Zephyr board has audiobusio +audiomp3 = true # Zephyr board has audiobusio +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = true # Zephyr board has audiobusio +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/circuitpython.toml b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/circuitpython.toml new file mode 100644 index 0000000000000..3272dd4c5f319 --- /dev/null +++ b/ports/zephyr-cp/boards/nxp/mimxrt1170_evk/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..83b54ae2fa269 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Raspberry Pi Foundation Raspberry Pi Pico 2" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true # Zephyr networking enabled +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = true # Zephyr networking enabled +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = true # Zephyr networking enabled +spitarget = false +ssl = true # Zephyr networking enabled +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = true # Zephyr board has wifi +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..0f901d1149ea3 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_w_zephyr/circuitpython.toml @@ -0,0 +1,2 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] +BLOBS=["hal_infineon"] diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..65c5bc003aea5 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Raspberry Pi Foundation Raspberry Pi Pico 2" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..9d3c229ed1b45 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico2_zephyr/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..97288095e000f --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Raspberry Pi Foundation Raspberry Pi Pico" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true # Zephyr networking enabled +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = true # Zephyr networking enabled +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = true # Zephyr networking enabled +spitarget = false +ssl = true # Zephyr networking enabled +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = true # Zephyr board has wifi +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..0f901d1149ea3 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_w_zephyr/circuitpython.toml @@ -0,0 +1,2 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] +BLOBS=["hal_infineon"] diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml new file mode 100644 index 0000000000000..18e1fc835657b --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Raspberry Pi Foundation Raspberry Pi Pico" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = true # Zephyr board has nvm +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/circuitpython.toml b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/circuitpython.toml new file mode 100644 index 0000000000000..9d3c229ed1b45 --- /dev/null +++ b/ports/zephyr-cp/boards/raspberrypi/rpi_pico_zephyr/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf", "uf2"] diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml new file mode 100644 index 0000000000000..529254cfec271 --- /dev/null +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "Renesas Electronics Corporation DA14695 Development Kit USB" + +[modules] +__future__ = true +_bleio = true # Zephyr board has _bleio +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml new file mode 100644 index 0000000000000..f7fad6bc4443a --- /dev/null +++ b/ports/zephyr-cp/boards/renesas/da14695_dk_usb/circuitpython.toml @@ -0,0 +1,2 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +BLOBS=["hal_renesas"] diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml index a96d1bad1d2a6..7a1c0bfd21e31 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/autogen_board_info.toml @@ -2,15 +2,15 @@ name = "Renesas Electronics Corporation RA6M5 Evaluation Kit" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -24,33 +24,36 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false -hashlib = false -i2cdisplaybus = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false @@ -59,14 +62,16 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false -math = false +lvfontio = true # Zephyr board has busio +math = true max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false microcontroller = true -msgpack = false +mipidsi = false +msgpack = true neopixel_write = false nvm = false onewireio = false @@ -76,39 +81,40 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +qspibus = false +rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false -usb_cdc = false # No TinyUSB settings for r7fa6m5bh3cfc +usb_cdc = true usb_hid = false usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false -zephyr_serial = true -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/renesas/ek_ra6m5/circuitpython.toml b/ports/zephyr-cp/boards/renesas/ek_ra6m5/circuitpython.toml index 3272dd4c5f319..13ff40aa042db 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra6m5/circuitpython.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra6m5/circuitpython.toml @@ -1 +1,3 @@ +# TX is on pin P411. RX is on pin P410. + CIRCUITPY_BUILD_EXTENSIONS = ["elf"] diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml index e4946decb190b..185859b080756 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/autogen_board_info.toml @@ -2,15 +2,15 @@ name = "Renesas Electronics Corporation RA8D1 Evaluation Kit" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -24,33 +24,36 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has displayio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false -hashlib = false -i2cdisplaybus = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false @@ -59,14 +62,16 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false -math = false +lvfontio = true # Zephyr board has busio +math = true max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false microcontroller = true -msgpack = false +mipidsi = false +msgpack = true neopixel_write = false nvm = false onewireio = false @@ -76,39 +81,40 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +qspibus = false +rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false -usb_cdc = false # No TinyUSB settings for r7fa8d1bhecbd +usb_cdc = true usb_hid = false usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false +zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false -zephyr_serial = true -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/renesas/ek_ra8d1/circuitpython.toml b/ports/zephyr-cp/boards/renesas/ek_ra8d1/circuitpython.toml index 3272dd4c5f319..0e19d8d71574e 100644 --- a/ports/zephyr-cp/boards/renesas/ek_ra8d1/circuitpython.toml +++ b/ports/zephyr-cp/boards/renesas/ek_ra8d1/circuitpython.toml @@ -1 +1,2 @@ CIRCUITPY_BUILD_EXTENSIONS = ["elf"] +SHIELDS = ["rtkmipilcdb00000be"] diff --git a/ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf b/ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf new file mode 100644 index 0000000000000..145a93934070f --- /dev/null +++ b/ports/zephyr-cp/boards/renesas_da14695_dk_usb.conf @@ -0,0 +1,20 @@ +CONFIG_BT=y +CONFIG_BT_PERIPHERAL=y +CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_COUNT=3 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay new file mode 100644 index 0000000000000..eb94fe7de675a --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33.overlay @@ -0,0 +1,25 @@ +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + code_partition: partition@0 { + label = "code-partition"; + reg = <0x0 0x180000>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(4) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf new file mode 100644 index 0000000000000..1a0d0010dca2a --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.conf @@ -0,0 +1,24 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y + +CONFIG_WIFI=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT_EVENT_INFO=y + +CONFIG_NET_HOSTNAME_ENABLE=y +CONFIG_NET_HOSTNAME_DYNAMIC=y +CONFIG_NET_HOSTNAME="circuitpython" + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_RSA_C=y +CONFIG_MBEDTLS_PKCS1_V15=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=y +CONFIG_MBEDTLS_ENTROPY_C=y +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CTR_DRBG_C=y +CONFIG_MBEDTLS_SHA1=y +CONFIG_MBEDTLS_USE_PSA_CRYPTO=n diff --git a/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay new file mode 100644 index 0000000000000..eb94fe7de675a --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico2_rp2350a_m33_w.overlay @@ -0,0 +1,25 @@ +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + code_partition: partition@0 { + label = "code-partition"; + reg = <0x0 0x180000>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(4) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay b/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay new file mode 100644 index 0000000000000..ce9083dd62d42 --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico_rp2040.overlay @@ -0,0 +1,34 @@ +&flash0 { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Reserved memory for the second stage bootloader */ + second_stage_bootloader: partition@0 { + label = "second_stage_bootloader"; + reg = <0x00000000 0x100>; + read-only; + }; + + code_partition: partition@100 { + label = "code-partition"; + reg = <0x100 (0x180000 - 0x100)>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(2) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf new file mode 100644 index 0000000000000..11d26d946b16d --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.conf @@ -0,0 +1,26 @@ +CONFIG_NETWORKING=y +CONFIG_NET_IPV4=y +CONFIG_NET_DHCPV4=y +CONFIG_NET_SOCKETS=y + +CONFIG_WIFI=y +CONFIG_NET_L2_WIFI_MGMT=y +CONFIG_NET_MGMT_EVENT=y +CONFIG_NET_MGMT_EVENT_INFO=y + +CONFIG_NET_HOSTNAME_ENABLE=y +CONFIG_NET_HOSTNAME_DYNAMIC=y +CONFIG_NET_HOSTNAME="circuitpython" + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_SSL_PROTO_TLS1_2=y +CONFIG_MBEDTLS_RSA_C=y +CONFIG_MBEDTLS_PKCS1_V15=y +CONFIG_MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED=y +CONFIG_MBEDTLS_ENTROPY_C=y +CONFIG_MBEDTLS_CIPHER_AES_ENABLED=y +CONFIG_MBEDTLS_CTR_DRBG_C=y +CONFIG_MBEDTLS_SHA1=y +CONFIG_MBEDTLS_USE_PSA_CRYPTO=n + +CONFIG_TEST_RANDOM_GENERATOR=y diff --git a/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay new file mode 100644 index 0000000000000..ce9083dd62d42 --- /dev/null +++ b/ports/zephyr-cp/boards/rpi_pico_rp2040_w.overlay @@ -0,0 +1,34 @@ +&flash0 { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Reserved memory for the second stage bootloader */ + second_stage_bootloader: partition@0 { + label = "second_stage_bootloader"; + reg = <0x00000000 0x100>; + read-only; + }; + + code_partition: partition@100 { + label = "code-partition"; + reg = <0x100 (0x180000 - 0x100)>; + read-only; + }; + + nvm_partition: partition@180000 { + label = "nvm"; + reg = <0x180000 0x1000>; + }; + + circuitpy_partition: partition@181000 { + label = "circuitpy"; + reg = <0x181000 (DT_SIZE_M(2) - 0x181000)>; + }; + }; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml new file mode 100644 index 0000000000000..7686dbf320bce --- /dev/null +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "STMicroelectronics Nucleo N657X0-Q" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml new file mode 100644 index 0000000000000..83e6bcd39c4f9 --- /dev/null +++ b/ports/zephyr-cp/boards/st/nucleo_n657x0_q/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] diff --git a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml index 7519d8817687b..4037dac0481ea 100644 --- a/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/nucleo_u575zi_q/autogen_board_info.toml @@ -2,15 +2,15 @@ name = "STMicroelectronics Nucleo U575ZI Q" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -24,33 +24,36 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has busio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false -hashlib = false -i2cdisplaybus = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false @@ -59,14 +62,16 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false -math = false +lvfontio = true # Zephyr board has busio +math = true max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false microcontroller = true -msgpack = false +mipidsi = false +msgpack = true neopixel_write = false nvm = false onewireio = false @@ -76,27 +81,28 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +qspibus = false +rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = false struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false usb_cdc = true @@ -105,10 +111,10 @@ usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false +zephyr_display = false zephyr_kernel = false -zephyr_serial = true -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml new file mode 100644 index 0000000000000..1a97bb3616350 --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "STMicroelectronics STM32H750B Discovery Kit" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has displayio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = true # Zephyr board has flash +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = false +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = true # Zephyr board has zephyr_display +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/stm32h750b_dk/circuitpython.toml b/ports/zephyr-cp/boards/st/stm32h750b_dk/circuitpython.toml new file mode 100644 index 0000000000000..83e6bcd39c4f9 --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32h750b_dk/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] diff --git a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml index e029020982d20..4723617a5a7f9 100644 --- a/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml +++ b/ports/zephyr-cp/boards/st/stm32h7b3i_dk/autogen_board_info.toml @@ -2,15 +2,15 @@ name = "STMicroelectronics STM32H7B3I Discovery kit" [modules] -__future__ = false +__future__ = true _bleio = false _eve = false _pew = false _pixelmap = false _stage = false -adafruit_bus_device = false +adafruit_bus_device = true adafruit_pixelbuf = false -aesio = false +aesio = true alarm = false analogbufio = false analogio = false @@ -24,33 +24,36 @@ audioio = false audiomixer = false audiomp3 = false audiopwmio = false +audiospeed = false aurora_epaper = false bitbangio = false -bitmapfilter = false -bitmaptools = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio bitops = false board = false -busdisplay = false -busio = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio camera = false canio = false codeop = false countio = false digitalio = true -displayio = false +displayio = true # Zephyr board has displayio dotclockframebuffer = false dualbank = false -epaperdisplay = false +epaperdisplay = true # Zephyr board has busio floppyio = false -fontio = false -fourwire = false -framebufferio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio frequencyio = false getpass = false gifio = false gnss = false -hashlib = false -i2cdisplaybus = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false i2ctarget = false imagecapture = false ipaddress = false @@ -59,14 +62,16 @@ jpegio = false keypad = false keypad_demux = false locale = false -lvfontio = false -math = false +lvfontio = true # Zephyr board has busio +math = true max3421e = false +mcp4822 = false mdns = false memorymap = false memorymonitor = false microcontroller = true -msgpack = false +mipidsi = false +msgpack = true neopixel_write = false nvm = false onewireio = false @@ -76,39 +81,40 @@ ps2io = false pulseio = false pwmio = false qrio = false -rainbowio = false +qspibus = false +rainbowio = true random = true rclcpy = false rgbmatrix = false -rotaryio = false +rotaryio = true # Zephyr board has rotaryio rtc = false -sdcardio = false +sdcardio = true # Zephyr board has busio sdioio = false -sharpdisplay = false +sharpdisplay = true # Zephyr board has busio socketpool = false spitarget = false ssl = false storage = true # Zephyr board has flash struct = true -supervisor = false +supervisor = true synthio = false -terminalio = false -tilepalettemapper = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio time = true touchio = false -traceback = false +traceback = true uheap = false usb = false -usb_cdc = false +usb_cdc = true usb_hid = false usb_host = false usb_midi = false usb_video = false ustack = false -vectorio = false -warnings = false +vectorio = true # Zephyr board has busio +warnings = true watchdog = false wifi = false +zephyr_display = true # Zephyr board has zephyr_display zephyr_kernel = false -zephyr_serial = true -zlib = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml new file mode 100644 index 0000000000000..749b040b6d43e --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/autogen_board_info.toml @@ -0,0 +1,120 @@ +# This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info. +name = "STMicroelectronics STM32WBA65I Discovery kit" + +[modules] +__future__ = true +_bleio = false +_eve = false +_pew = false +_pixelmap = false +_stage = false +adafruit_bus_device = true +adafruit_pixelbuf = false +aesio = true +alarm = false +analogbufio = false +analogio = false +atexit = false +audiobusio = false +audiocore = false +audiodelays = false +audiofilters = false +audiofreeverb = false +audioio = false +audiomixer = false +audiomp3 = false +audiopwmio = false +audiospeed = false +aurora_epaper = false +bitbangio = false +bitmapfilter = true # Zephyr board has busio +bitmaptools = true # Zephyr board has busio +bitops = false +board = false +busdisplay = true # Zephyr board has busio +busio = true # Zephyr board has busio +camera = false +canio = false +codeop = false +countio = false +digitalio = true +displayio = true # Zephyr board has busio +dotclockframebuffer = false +dualbank = false +epaperdisplay = true # Zephyr board has busio +floppyio = false +fontio = true # Zephyr board has busio +fourwire = true # Zephyr board has busio +framebufferio = true # Zephyr board has busio +frequencyio = false +getpass = false +gifio = false +gnss = false +hashlib = true +hostnetwork = false +i2cdisplaybus = true # Zephyr board has busio +i2cioexpander = false +i2ctarget = false +imagecapture = false +ipaddress = false +is31fl3741 = false +jpegio = false +keypad = false +keypad_demux = false +locale = false +lvfontio = true # Zephyr board has busio +math = true +max3421e = false +mcp4822 = false +mdns = false +memorymap = false +memorymonitor = false +microcontroller = true +mipidsi = false +msgpack = true +neopixel_write = false +nvm = false +onewireio = false +os = true +paralleldisplaybus = false +ps2io = false +pulseio = false +pwmio = false +qrio = false +qspibus = false +rainbowio = true +random = true +rclcpy = false +rgbmatrix = false +rotaryio = true # Zephyr board has rotaryio +rtc = false +sdcardio = true # Zephyr board has busio +sdioio = false +sharpdisplay = true # Zephyr board has busio +socketpool = false +spitarget = false +ssl = false +storage = false +struct = true +supervisor = true +synthio = false +terminalio = true # Zephyr board has busio +tilepalettemapper = true # Zephyr board has busio +time = true +touchio = false +traceback = true +uheap = false +usb = false +usb_cdc = true +usb_hid = false +usb_host = false +usb_midi = false +usb_video = false +ustack = false +vectorio = true # Zephyr board has busio +warnings = true +watchdog = false +wifi = false +zephyr_display = false +zephyr_kernel = false +zlib = true diff --git a/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml new file mode 100644 index 0000000000000..83e6bcd39c4f9 --- /dev/null +++ b/ports/zephyr-cp/boards/st/stm32wba65i_dk1/circuitpython.toml @@ -0,0 +1 @@ +CIRCUITPY_BUILD_EXTENSIONS = ["hex"] diff --git a/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.conf b/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.conf new file mode 100644 index 0000000000000..24afffb8e88ee --- /dev/null +++ b/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.conf @@ -0,0 +1,2 @@ +# Enable Zephyr display subsystem so the built-in LTDC panel is available. +CONFIG_DISPLAY=y diff --git a/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay b/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay new file mode 100644 index 0000000000000..fdb4960477b77 --- /dev/null +++ b/ports/zephyr-cp/boards/stm32h750b_dk_stm32h750xx_ext_flash_app.overlay @@ -0,0 +1,14 @@ +&ext_flash { + partitions { + /delete-node/ partition@7800000; + + circuitpy_partition: partition@7800000 { + label = "circuitpy"; + reg = <0x7800000 DT_SIZE_M(8)>; + }; + }; +}; + +&rng { + status = "okay"; +}; diff --git a/ports/zephyr-cp/boards/stm32h7b3i_dk.conf b/ports/zephyr-cp/boards/stm32h7b3i_dk.conf new file mode 100644 index 0000000000000..24afffb8e88ee --- /dev/null +++ b/ports/zephyr-cp/boards/stm32h7b3i_dk.conf @@ -0,0 +1,2 @@ +# Enable Zephyr display subsystem so the built-in LTDC panel is available. +CONFIG_DISPLAY=y diff --git a/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay b/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay index 88ad0415485b8..c2b5f3129c48a 100644 --- a/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay +++ b/ports/zephyr-cp/boards/stm32h7b3i_dk.overlay @@ -2,6 +2,28 @@ /delete-node/ partitions; }; +&sram5 { + status = "disabled"; +}; + &rng { status = "okay"; }; + +&fdcan1 { + status = "disabled"; +}; + +/ { + chosen { + /delete-property/ zephyr,canbus; + }; +}; + +zephyr_udc0: &usbotg_hs { + pinctrl-0 = <&usb_otg_hs_dm_pa11 &usb_otg_hs_dp_pa12>; + pinctrl-names = "default"; + status = "okay"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/boards/stm32wba65i_dk1.conf b/ports/zephyr-cp/boards/stm32wba65i_dk1.conf new file mode 100644 index 0000000000000..55d951959e669 --- /dev/null +++ b/ports/zephyr-cp/boards/stm32wba65i_dk1.conf @@ -0,0 +1,24 @@ +# USB OTG on STM32WBA requires VOS Range 1. Keep HCLK > 16 MHz. +CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC=32000000 + +# Bluetooth doesn't start up for some reason. +# CONFIG_BT=y +# CONFIG_BT_PERIPHERAL=y +# CONFIG_BT_CENTRAL=y +CONFIG_BT_BROADCASTER=y +CONFIG_BT_OBSERVER=y +CONFIG_BT_EXT_ADV=y +CONFIG_BT_STM32WBA_USE_TEMP_BASED_CALIB=n + +CONFIG_BT_DEVICE_APPEARANCE_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_DEVICE_NAME_MAX=28 +CONFIG_BT_L2CAP_TX_MTU=253 + +# BT Buffers +CONFIG_BT_BUF_CMD_TX_SIZE=255 +CONFIG_BT_BUF_EVT_RX_COUNT=16 +CONFIG_BT_BUF_EVT_RX_SIZE=255 +CONFIG_BT_BUF_ACL_TX_SIZE=251 +CONFIG_BT_BUF_ACL_RX_COUNT_EXTRA=1 +CONFIG_BT_BUF_ACL_RX_SIZE=255 diff --git a/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay b/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay new file mode 100644 index 0000000000000..3b72ffc99845c --- /dev/null +++ b/ports/zephyr-cp/boards/stm32wba65i_dk1.overlay @@ -0,0 +1,61 @@ +&flash0 { + /delete-node/ partitions; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x00000000 DT_SIZE_K(64)>; + }; + + slot0_partition: partition@10000 { + label = "image-0"; + reg = <0x00010000 DT_SIZE_K(928)>; + }; + + storage_partition: partition@f80000 { + label = "storage"; + reg = <0x001e0000 DT_SIZE_K(64)>; + }; + + circuitpy_partition: partition@108000 { + label = "circuitpy"; + reg = <0x00108000 DT_SIZE_K(992)>; + }; + }; +}; + +&rng { + status = "okay"; +}; + +/* + * USB on STM32WBA requires VOS Range 1. Zephyr selects VOS from HCLK, and + * 16 MHz keeps it in Range 2, which trips an assertion in udc_stm32. + * Run SYSCLK from full 32 MHz HSE so VOS is set to Range 1. + */ +&clk_hse { + /delete-property/ hse-div2; +}; + +&rcc { + clock-frequency = ; + ahb5-prescaler = <1>; +}; + +zephyr_udc0: &usbotg_hs { + status = "okay"; +}; + +&otghs_phy { + /* OTG HS clock source is 32 MHz HSE */ + clocks = <&rcc STM32_CLOCK(AHB2, 15)>, + <&rcc STM32_SRC_HSE OTGHS_SEL(0)>; + clock-reference = "SYSCFG_OTG_HS_PHY_CLK_32MHz"; + status = "okay"; +}; + +#include "../app.overlay" diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.c b/ports/zephyr-cp/common-hal/_bleio/Adapter.c new file mode 100644 index 0000000000000..d1410f02e1b12 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.c @@ -0,0 +1,679 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "py/gc.h" +#include "py/runtime.h" +#include "bindings/zephyr_kernel/__init__.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "shared-bindings/_bleio/Address.h" +#include "shared-module/_bleio/Address.h" +#include "shared-module/_bleio/ScanResults.h" +#include "supervisor/shared/tick.h" + +bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT]; + +static bool scan_callbacks_registered = false; +static bleio_scanresults_obj_t *active_scan_results = NULL; +static struct bt_le_scan_cb scan_callbacks; +static bool ble_advertising = false; +static bool ble_adapter_enabled = true; + +#define BLEIO_ADV_MAX_FIELDS 16 +#define BLEIO_ADV_MAX_DATA_LEN 31 +static struct bt_data adv_data[BLEIO_ADV_MAX_FIELDS]; +static struct bt_data scan_resp_data[BLEIO_ADV_MAX_FIELDS]; +static uint8_t adv_data_storage[BLEIO_ADV_MAX_DATA_LEN]; +static uint8_t scan_resp_storage[BLEIO_ADV_MAX_DATA_LEN]; + +static uint8_t bleio_address_type_from_zephyr(const bt_addr_le_t *addr) { + if (addr == NULL) { + return BLEIO_ADDRESS_TYPE_PUBLIC; + } + + switch (addr->type) { + case BT_ADDR_LE_PUBLIC: + case BT_ADDR_LE_PUBLIC_ID: + return BLEIO_ADDRESS_TYPE_PUBLIC; + case BT_ADDR_LE_RANDOM: + case BT_ADDR_LE_RANDOM_ID: + case BT_ADDR_LE_UNRESOLVED: + if (BT_ADDR_IS_RPA(&addr->a)) { + return BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_RESOLVABLE; + } + if (BT_ADDR_IS_NRPA(&addr->a)) { + return BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE; + } + return BLEIO_ADDRESS_TYPE_RANDOM_STATIC; + default: + return BLEIO_ADDRESS_TYPE_PUBLIC; + } +} + +static uint8_t bleio_address_type_to_zephyr(uint8_t type) { + switch (type) { + case BLEIO_ADDRESS_TYPE_PUBLIC: + return BT_ADDR_LE_PUBLIC; + case BLEIO_ADDRESS_TYPE_RANDOM_STATIC: + case BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_RESOLVABLE: + case BLEIO_ADDRESS_TYPE_RANDOM_PRIVATE_NON_RESOLVABLE: + return BT_ADDR_LE_RANDOM; + default: + return BT_ADDR_LE_PUBLIC; + } +} + +static bleio_connection_internal_t *bleio_connection_find_by_conn(const struct bt_conn *conn) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + if (connection->conn == conn) { + return connection; + } + } + + return NULL; +} + +static bleio_connection_internal_t *bleio_connection_track(struct bt_conn *conn) { + bleio_connection_internal_t *connection = bleio_connection_find_by_conn(conn); + if (connection == NULL) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *candidate = &bleio_connections[i]; + if (candidate->conn == NULL) { + connection = candidate; + break; + } + } + } + + if (connection == NULL) { + return NULL; + } + + if (connection->conn == NULL) { + connection->conn = bt_conn_ref(conn); + } + + return connection; +} + +static void bleio_connection_clear(bleio_connection_internal_t *self) { + if (self == NULL) { + return; + } + + if (self->conn != NULL) { + bt_conn_unref(self->conn); + self->conn = NULL; + } + + self->connection_obj = mp_const_none; +} + +static void bleio_connection_release(bleio_connection_internal_t *connection, uint8_t reason) { + if (connection == NULL) { + return; + } + + if (connection->connection_obj != mp_const_none) { + bleio_connection_obj_t *connection_obj = MP_OBJ_TO_PTR(connection->connection_obj); + connection_obj->connection = NULL; + connection_obj->disconnect_reason = reason; + } + + bleio_connection_clear(connection); + common_hal_bleio_adapter_obj.connection_objs = NULL; +} + +static void bleio_connected_cb(struct bt_conn *conn, uint8_t err) { + if (err != 0) { + return; + } + + if (bleio_connection_track(conn) == NULL) { + bt_conn_disconnect(conn, BT_HCI_ERR_CONN_LIMIT_EXCEEDED); + return; + } + + // When connectable advertising results in a connection, the controller + // auto-stops advertising. Clear our flag to match (we cannot call + // stop_advertising() here because this callback runs in Zephyr's BT + // thread context). + ble_advertising = false; + + common_hal_bleio_adapter_obj.connection_objs = NULL; +} + +static void bleio_disconnected_cb(struct bt_conn *conn, uint8_t reason) { + printk("disconnected %p\n", conn); + bleio_connection_release(bleio_connection_find_by_conn(conn), reason); +} + +BT_CONN_CB_DEFINE(bleio_connection_callbacks) = { + .connected = bleio_connected_cb, + .disconnected = bleio_disconnected_cb, +}; + +static void scan_recv_cb(const struct bt_le_scan_recv_info *info, struct net_buf_simple *buf) { + if (active_scan_results == NULL || info == NULL || buf == NULL) { + return; + } + + const bool connectable = (info->adv_props & BT_GAP_ADV_PROP_CONNECTABLE) != 0; + const bool scan_response = (info->adv_props & BT_GAP_ADV_PROP_SCAN_RESPONSE) != 0; + const bt_addr_le_t *addr = info->addr; + + uint8_t addr_bytes[NUM_BLEIO_ADDRESS_BYTES] = {0}; + if (addr != NULL) { + memcpy(addr_bytes, addr->a.val, sizeof(addr_bytes)); + } + + shared_module_bleio_scanresults_append(active_scan_results, + supervisor_ticks_ms64(), + connectable, + scan_response, + info->rssi, + addr_bytes, + bleio_address_type_from_zephyr(addr), + buf->data, + buf->len); +} + +static void scan_timeout_cb(void) { + if (active_scan_results == NULL) { + return; + } + shared_module_bleio_scanresults_set_done(active_scan_results, true); + active_scan_results = NULL; +} + +// We need to disassemble the full advertisement packet because the Zephyr takes +// in each ADT in an array. +static size_t bleio_parse_adv_data(const uint8_t *raw, size_t raw_len, struct bt_data *out, + size_t out_len, uint8_t *storage, size_t storage_len) { + size_t count = 0; + size_t offset = 0; + size_t storage_offset = 0; + + while (offset < raw_len) { + uint8_t field_len = raw[offset]; + if (field_len == 0) { + offset++; + continue; + } + uint8_t data_len = field_len - 1; + if (offset + field_len + 1 > raw_len || + count >= out_len || + field_len < 1 || + storage_offset + data_len > storage_len) { + mp_raise_ValueError(MP_ERROR_TEXT("Invalid advertising data")); + } + uint8_t type = raw[offset + 1]; + memcpy(storage + storage_offset, raw + offset + 2, data_len); + out[count].type = type; + out[count].data_len = data_len; + out[count].data = storage + storage_offset; + storage_offset += data_len; + count++; + offset += field_len + 1; + } + + return count; +} + +static uint16_t bleio_validate_and_convert_timeout(mp_float_t timeout) { + mp_arg_validate_float_range(timeout, 0, UINT16_MAX, MP_QSTR_timeout); + + if (timeout <= 0.0f) { + return 0; + } + + const mp_int_t timeout_units = + mp_arg_validate_int_range((mp_int_t)(timeout * 100.0f + 0.5f), 1, UINT16_MAX, MP_QSTR_timeout); + + return (uint16_t)timeout_units; +} + +void common_hal_bleio_adapter_set_enabled(bleio_adapter_obj_t *self, bool enabled) { + if (enabled == ble_adapter_enabled) { + return; + } + if (enabled) { + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_clear(&bleio_connections[i]); + } + if (!bt_is_ready()) { + int err = bt_enable(NULL); + if (err != 0) { + raise_zephyr_error(err); + } + } + ble_adapter_enabled = true; + return; + } + + // On Zephyr bsim + HCI IPC, disabling and immediately re-enabling BLE can + // race endpoint rebinding during soft reload. Keep the controller running, + // but present adapter.enabled=False to CircuitPython code. + common_hal_bleio_adapter_stop_scan(self); + common_hal_bleio_adapter_stop_advertising(self); + ble_adapter_enabled = false; +} + +bool common_hal_bleio_adapter_get_enabled(bleio_adapter_obj_t *self) { + return ble_adapter_enabled; +} + +mp_int_t common_hal_bleio_adapter_get_tx_power(bleio_adapter_obj_t *self) { + struct bt_hci_cp_vs_read_tx_power_level *cp; + struct bt_hci_rp_vs_read_tx_power_level *rp; + struct net_buf *buf, *rsp = NULL; + + buf = bt_hci_cmd_alloc(K_MSEC(1000)); + if (!buf) { + mp_raise_msg(&mp_type_MemoryError, NULL); + } + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle_type = BT_HCI_VS_LL_HANDLE_TYPE_ADV; + cp->handle = 0; + + int err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_READ_TX_POWER_LEVEL, buf, &rsp); + if (err) { + raise_zephyr_error(err); + } + + rp = (void *)rsp->data; + int8_t power = rp->tx_power_level; + net_buf_unref(rsp); + return power; +} + +void common_hal_bleio_adapter_set_tx_power(bleio_adapter_obj_t *self, mp_int_t tx_power) { + struct bt_hci_cp_vs_write_tx_power_level *cp; + struct net_buf *buf, *rsp = NULL; + + buf = bt_hci_cmd_alloc(K_MSEC(3000)); + if (!buf) { + mp_raise_msg(&mp_type_MemoryError, NULL); + } + cp = net_buf_add(buf, sizeof(*cp)); + cp->handle_type = BT_HCI_VS_LL_HANDLE_TYPE_ADV; + cp->handle = 0; + cp->tx_power_level = (int8_t)tx_power; + + int err = bt_hci_cmd_send_sync(BT_HCI_OP_VS_WRITE_TX_POWER_LEVEL, buf, &rsp); + if (err) { + raise_zephyr_error(err); + } + + net_buf_unref(rsp); +} + +bleio_address_obj_t *common_hal_bleio_adapter_get_address(bleio_adapter_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_adapter_set_address(bleio_adapter_obj_t *self, bleio_address_obj_t *address) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_str_t *common_hal_bleio_adapter_get_name(bleio_adapter_obj_t *self) { + (void)self; + const char *name = bt_get_name(); + return mp_obj_new_str(name, strlen(name)); +} + +void common_hal_bleio_adapter_set_name(bleio_adapter_obj_t *self, const char *name) { + (void)self; + size_t len = strlen(name); + int err = 0; + if (len > CONFIG_BT_DEVICE_NAME_MAX) { + char truncated[CONFIG_BT_DEVICE_NAME_MAX + 1]; + memcpy(truncated, name, CONFIG_BT_DEVICE_NAME_MAX); + truncated[CONFIG_BT_DEVICE_NAME_MAX] = '\0'; + err = bt_set_name(truncated); + } else { + err = bt_set_name(name); + } + if (err != 0) { + raise_zephyr_error(err); + } +} + +void common_hal_bleio_adapter_start_advertising(bleio_adapter_obj_t *self, + bool connectable, bool anonymous, uint32_t timeout, mp_float_t interval, + mp_buffer_info_t *advertising_data_bufinfo, + mp_buffer_info_t *scan_response_data_bufinfo, + mp_int_t tx_power, const bleio_address_obj_t *directed_to) { + (void)directed_to; + (void)interval; + + if (advertising_data_bufinfo->len > BLEIO_ADV_MAX_DATA_LEN || + scan_response_data_bufinfo->len > BLEIO_ADV_MAX_DATA_LEN) { + mp_raise_NotImplementedError(NULL); + } + + if (timeout != 0) { + mp_raise_NotImplementedError(NULL); + } + + if (ble_advertising) { + raise_zephyr_error(-EALREADY); + } + + bt_addr_le_t id_addrs[CONFIG_BT_ID_MAX]; + size_t id_count = CONFIG_BT_ID_MAX; + bt_id_get(id_addrs, &id_count); + if (id_count == 0 || bt_addr_le_eq(&id_addrs[BT_ID_DEFAULT], BT_ADDR_LE_ANY)) { + int id = bt_id_create(NULL, NULL); + if (id < 0) { + printk("Failed to create identity address: %d\n", id); + raise_zephyr_error(id); + } + } + + size_t adv_count = bleio_parse_adv_data(advertising_data_bufinfo->buf, + advertising_data_bufinfo->len, + adv_data, + BLEIO_ADV_MAX_FIELDS, + adv_data_storage, + sizeof(adv_data_storage)); + + size_t scan_resp_count = 0; + if (scan_response_data_bufinfo->len > 0) { + scan_resp_count = bleio_parse_adv_data(scan_response_data_bufinfo->buf, + scan_response_data_bufinfo->len, + scan_resp_data, + BLEIO_ADV_MAX_FIELDS, + scan_resp_storage, + sizeof(scan_resp_storage)); + } + + if (anonymous) { + mp_raise_NotImplementedError(NULL); + } + + struct bt_le_adv_param adv_params; + if (connectable) { + adv_params = (struct bt_le_adv_param)BT_LE_ADV_PARAM_INIT( + BT_LE_ADV_OPT_CONN, + BT_GAP_ADV_FAST_INT_MIN_1, + BT_GAP_ADV_FAST_INT_MAX_1, + NULL); + } else if (scan_resp_count > 0) { + adv_params = (struct bt_le_adv_param)BT_LE_ADV_PARAM_INIT( + BT_LE_ADV_OPT_SCANNABLE, + BT_GAP_ADV_FAST_INT_MIN_2, + BT_GAP_ADV_FAST_INT_MAX_2, + NULL); + } else { + adv_params = (struct bt_le_adv_param)BT_LE_ADV_PARAM_INIT( + 0, + BT_GAP_ADV_FAST_INT_MIN_2, + BT_GAP_ADV_FAST_INT_MAX_2, + NULL); + } + + common_hal_bleio_adapter_set_tx_power(self, tx_power); + + raise_zephyr_error(bt_le_adv_start(&adv_params, + adv_data, + adv_count, + scan_resp_count > 0 ? scan_resp_data : NULL, + scan_resp_count)); + + ble_advertising = true; +} + +void common_hal_bleio_adapter_stop_advertising(bleio_adapter_obj_t *self) { + (void)self; + if (!ble_advertising) { + return; + } + bt_le_adv_stop(); + ble_advertising = false; +} + +bool common_hal_bleio_adapter_get_advertising(bleio_adapter_obj_t *self) { + (void)self; + return ble_advertising; +} + +mp_obj_t common_hal_bleio_adapter_start_scan(bleio_adapter_obj_t *self, uint8_t *prefixes, size_t prefix_length, bool extended, mp_int_t buffer_size, mp_float_t timeout, mp_float_t interval, mp_float_t window, mp_int_t minimum_rssi, bool active) { + (void)extended; + + if (self->scan_results != NULL) { + if (!shared_module_bleio_scanresults_get_done(self->scan_results)) { + common_hal_bleio_adapter_stop_scan(self); + } else { + self->scan_results = NULL; + } + } + + int err = 0; + + self->scan_results = shared_module_bleio_new_scanresults(buffer_size, prefixes, prefix_length, minimum_rssi); + active_scan_results = self->scan_results; + + if (!scan_callbacks_registered) { + scan_callbacks.recv = scan_recv_cb; + scan_callbacks.timeout = scan_timeout_cb; + err = bt_le_scan_cb_register(&scan_callbacks); + if (err != 0) { + self->scan_results = NULL; + active_scan_results = NULL; + raise_zephyr_error(err); + } + scan_callbacks_registered = true; + } + + uint16_t interval_units = (uint16_t)((interval / 0.000625f) + 0.5f); + uint16_t window_units = (uint16_t)((window / 0.000625f) + 0.5f); + uint16_t timeout_units = bleio_validate_and_convert_timeout(timeout); + + struct bt_le_scan_param scan_params = { + .type = active ? BT_LE_SCAN_TYPE_ACTIVE : BT_LE_SCAN_TYPE_PASSIVE, + .options = BT_LE_SCAN_OPT_FILTER_DUPLICATE, + .interval = interval_units, + .window = window_units, + .timeout = (uint16_t)timeout_units, + .interval_coded = 0, + .window_coded = 0, + }; + + err = bt_le_scan_start(&scan_params, NULL); + if (err != 0) { + self->scan_results = NULL; + active_scan_results = NULL; + raise_zephyr_error(err); + } + + return MP_OBJ_FROM_PTR(self->scan_results); +} + +void common_hal_bleio_adapter_stop_scan(bleio_adapter_obj_t *self) { + if (self->scan_results == NULL) { + return; + } + bt_le_scan_stop(); + shared_module_bleio_scanresults_set_done(self->scan_results, true); + active_scan_results = NULL; + self->scan_results = NULL; +} + +bool common_hal_bleio_adapter_get_connected(bleio_adapter_obj_t *self) { + if (!ble_adapter_enabled) { + return false; + } + + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + if (bleio_connections[i].conn != NULL) { + return true; + } + } + + return false; +} + +mp_obj_t common_hal_bleio_adapter_get_connections(bleio_adapter_obj_t *self) { + if (!ble_adapter_enabled) { + self->connection_objs = NULL; + return mp_const_empty_tuple; + } + + if (self->connection_objs != NULL) { + return self->connection_objs; + } + + size_t total_connected = 0; + mp_obj_t items[BLEIO_TOTAL_CONNECTION_COUNT]; + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + if (connection->conn == NULL) { + continue; + } + + if (connection->connection_obj == mp_const_none) { + connection->connection_obj = bleio_connection_new_from_internal(connection); + } + + items[total_connected] = connection->connection_obj; + total_connected++; + } + + self->connection_objs = mp_obj_new_tuple(total_connected, items); + return self->connection_objs; +} + +mp_obj_t common_hal_bleio_adapter_connect(bleio_adapter_obj_t *self, bleio_address_obj_t *address, mp_float_t timeout) { + common_hal_bleio_adapter_stop_scan(self); + + const uint16_t timeout_units = bleio_validate_and_convert_timeout(timeout); + + mp_buffer_info_t address_bufinfo; + mp_get_buffer_raise(address->bytes, &address_bufinfo, MP_BUFFER_READ); + + bt_addr_le_t peer = { + .type = bleio_address_type_to_zephyr(address->type), + }; + memcpy(peer.a.val, address_bufinfo.buf, NUM_BLEIO_ADDRESS_BYTES); + + struct bt_conn_le_create_param create_params = BT_CONN_LE_CREATE_PARAM_INIT( + BT_CONN_LE_OPT_NONE, + BT_GAP_SCAN_FAST_INTERVAL, + BT_GAP_SCAN_FAST_INTERVAL); + create_params.timeout = timeout_units; + + struct bt_conn *conn = NULL; + int err = bt_conn_le_create(&peer, &create_params, BT_LE_CONN_PARAM_DEFAULT, &conn); + if (err != 0) { + raise_zephyr_error(err); + } + + while (true) { + struct bt_conn_info info; + err = bt_conn_get_info(conn, &info); + if (err == 0) { + if (info.state == BT_CONN_STATE_CONNECTED) { + break; + } + + if (info.state == BT_CONN_STATE_DISCONNECTED) { + bt_conn_unref(conn); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Failed to connect: timeout")); + } + } else if (err != -ENOTCONN) { + bt_conn_unref(conn); + raise_zephyr_error(err); + } + + RUN_BACKGROUND_TASKS; + } + + bleio_connection_internal_t *connection = bleio_connection_find_by_conn(conn); + if (connection == NULL) { + connection = bleio_connection_track(conn); + } + + if (connection == NULL) { + bt_conn_unref(conn); + mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Failed to connect: internal error")); + } + + // bt_conn_le_create() gave us a ref in `conn`; `connection` keeps its own + // ref via bleio_connection_track(). Drop the create ref now. + bt_conn_unref(conn); + + self->connection_objs = NULL; + return bleio_connection_new_from_internal(connection); +} + +void common_hal_bleio_adapter_erase_bonding(bleio_adapter_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_adapter_is_bonded_to_central(bleio_adapter_obj_t *self) { + return false; +} + +void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter) { + gc_collect_root((void **)adapter, sizeof(bleio_adapter_obj_t) / sizeof(size_t)); + gc_collect_root((void **)bleio_connections, sizeof(bleio_connections) / sizeof(size_t)); +} + +void bleio_adapter_reset(bleio_adapter_obj_t *adapter) { + if (adapter == NULL) { + return; + } + + common_hal_bleio_adapter_stop_scan(adapter); + common_hal_bleio_adapter_stop_advertising(adapter); + + for (size_t i = 0; i < BLEIO_TOTAL_CONNECTION_COUNT; i++) { + bleio_connection_internal_t *connection = &bleio_connections[i]; + if (connection->conn != NULL) { + bt_conn_disconnect(connection->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + if (connection->connection_obj != MP_OBJ_NULL && + connection->connection_obj != mp_const_none) { + bleio_connection_obj_t *connection_obj = MP_OBJ_TO_PTR(connection->connection_obj); + connection_obj->connection = NULL; + connection_obj->disconnect_reason = BT_HCI_ERR_REMOTE_USER_TERM_CONN; + } + bleio_connection_clear(connection); + } + + adapter->scan_results = NULL; + adapter->connection_objs = NULL; + active_scan_results = NULL; + ble_advertising = false; + ble_adapter_enabled = bt_is_ready(); +} + +bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void) { + return &common_hal_bleio_adapter_obj; +} + +uint16_t bleio_adapter_get_name(char *buf, uint16_t len) { + const char *name = bt_get_name(); + uint16_t full_len = strlen(name); + if (len > 0) { + uint16_t copy_len = len < full_len ? len : full_len; + memcpy(buf, name, copy_len); + } + return full_len; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Adapter.h b/ports/zephyr-cp/common-hal/_bleio/Adapter.h new file mode 100644 index 0000000000000..c15c698e2a525 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Adapter.h @@ -0,0 +1,29 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "py/objtuple.h" + +#include "shared-bindings/_bleio/Connection.h" +#include "shared-bindings/_bleio/ScanResults.h" + +#define BLEIO_TOTAL_CONNECTION_COUNT CONFIG_BT_MAX_CONN + +extern bleio_connection_internal_t bleio_connections[BLEIO_TOTAL_CONNECTION_COUNT]; + +typedef struct { + mp_obj_base_t base; + bleio_scanresults_obj_t *scan_results; + mp_obj_t name; + mp_obj_tuple_t *connection_objs; + bool user_advertising; +} bleio_adapter_obj_t; + +void bleio_adapter_gc_collect(bleio_adapter_obj_t *adapter); +void bleio_adapter_reset(bleio_adapter_obj_t *adapter); diff --git a/ports/zephyr-cp/common-hal/_bleio/Attribute.c b/ports/zephyr-cp/common-hal/_bleio/Attribute.c new file mode 100644 index 0000000000000..6312f3d46b80f --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Attribute.c @@ -0,0 +1,8 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Attribute is defined in shared-module, no port-specific implementation needed diff --git a/ports/zephyr-cp/common-hal/_bleio/Attribute.h b/ports/zephyr-cp/common-hal/_bleio/Attribute.h new file mode 100644 index 0000000000000..24b0f78a106e5 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Attribute.h @@ -0,0 +1,10 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/_bleio/Attribute.h" diff --git a/ports/zephyr-cp/common-hal/_bleio/Characteristic.c b/ports/zephyr-cp/common-hal/_bleio/Characteristic.c new file mode 100644 index 0000000000000..386be6004d2a1 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Characteristic.c @@ -0,0 +1,67 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/Characteristic.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Service.h" + +bleio_characteristic_properties_t common_hal_bleio_characteristic_get_properties(bleio_characteristic_obj_t *self) { + return self->props; +} + +mp_obj_tuple_t *common_hal_bleio_characteristic_get_descriptors(bleio_characteristic_obj_t *self) { + return mp_obj_new_tuple(self->descriptor_list->len, self->descriptor_list->items); +} + +bleio_service_obj_t *common_hal_bleio_characteristic_get_service(bleio_characteristic_obj_t *self) { + return self->service; +} + +bleio_uuid_obj_t *common_hal_bleio_characteristic_get_uuid(bleio_characteristic_obj_t *self) { + return self->uuid; +} + +size_t common_hal_bleio_characteristic_get_max_length(bleio_characteristic_obj_t *self) { + return self->max_length; +} + +size_t common_hal_bleio_characteristic_get_value(bleio_characteristic_obj_t *self, uint8_t *buf, size_t len) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_add_descriptor(bleio_characteristic_obj_t *self, bleio_descriptor_obj_t *descriptor) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_construct(bleio_characteristic_obj_t *self, bleio_service_obj_t *service, uint16_t handle, bleio_uuid_obj_t *uuid, bleio_characteristic_properties_t props, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo, const char *user_description) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_characteristic_deinited(bleio_characteristic_obj_t *self) { + return self->service == NULL; +} + +void common_hal_bleio_characteristic_deinit(bleio_characteristic_obj_t *self) { + // Nothing to do +} + +void common_hal_bleio_characteristic_set_cccd(bleio_characteristic_obj_t *self, bool notify, bool indicate) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_set_value(bleio_characteristic_obj_t *self, mp_buffer_info_t *bufinfo) { + mp_raise_NotImplementedError(NULL); +} + +void bleio_characteristic_set_observer(bleio_characteristic_obj_t *self, mp_obj_t observer) { + self->observer = observer; +} + +void bleio_characteristic_clear_observer(bleio_characteristic_obj_t *self) { + self->observer = mp_const_none; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Characteristic.h b/ports/zephyr-cp/common-hal/_bleio/Characteristic.h new file mode 100644 index 0000000000000..b710a9f2662b8 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Characteristic.h @@ -0,0 +1,40 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "py/objlist.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "shared-module/_bleio/Characteristic.h" +#include "common-hal/_bleio/Descriptor.h" +#include "common-hal/_bleio/Service.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct _bleio_characteristic_obj { + mp_obj_base_t base; + bleio_service_obj_t *service; + bleio_uuid_obj_t *uuid; + mp_obj_t observer; + uint8_t *current_value; + uint16_t current_value_len; + uint16_t current_value_alloc; + uint16_t max_length; + uint16_t def_handle; + uint16_t handle; + bleio_characteristic_properties_t props; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; + mp_obj_list_t *descriptor_list; + uint16_t user_desc_handle; + uint16_t cccd_handle; + uint16_t sccd_handle; + bool fixed_length; +} bleio_characteristic_obj_t; + +void bleio_characteristic_set_observer(bleio_characteristic_obj_t *self, mp_obj_t observer); +void bleio_characteristic_clear_observer(bleio_characteristic_obj_t *self); diff --git a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c new file mode 100644 index 0000000000000..17e000e905eb7 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.c @@ -0,0 +1,73 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/mperrno.h" +#include "py/runtime.h" +#include "shared-bindings/_bleio/CharacteristicBuffer.h" + +void _common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + uint8_t *buffer, size_t buffer_size, + void *static_handler_entry, + bool watch_for_interrupt_char) { + (void)self; + (void)characteristic; + (void)timeout; + (void)buffer; + (void)buffer_size; + (void)static_handler_entry; + (void)watch_for_interrupt_char; + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_buffer_construct(bleio_characteristic_buffer_obj_t *self, + bleio_characteristic_obj_t *characteristic, + mp_float_t timeout, + size_t buffer_size) { + (void)self; + (void)characteristic; + (void)timeout; + (void)buffer_size; + mp_raise_NotImplementedError(NULL); +} + +uint32_t common_hal_bleio_characteristic_buffer_read(bleio_characteristic_buffer_obj_t *self, uint8_t *data, size_t len, int *errcode) { + (void)self; + (void)data; + (void)len; + if (errcode != NULL) { + *errcode = MP_EAGAIN; + } + mp_raise_NotImplementedError(NULL); +} + +uint32_t common_hal_bleio_characteristic_buffer_rx_characters_available(bleio_characteristic_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_characteristic_buffer_clear_rx_buffer(bleio_characteristic_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_characteristic_buffer_deinited(bleio_characteristic_buffer_obj_t *self) { + return self->deinited; +} + +void common_hal_bleio_characteristic_buffer_deinit(bleio_characteristic_buffer_obj_t *self) { + if (self == NULL) { + return; + } + self->deinited = true; +} + +bool common_hal_bleio_characteristic_buffer_connected(bleio_characteristic_buffer_obj_t *self) { + (void)self; + return false; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h new file mode 100644 index 0000000000000..91ea262945af5 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/CharacteristicBuffer.h @@ -0,0 +1,19 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "py/obj.h" +#include "shared-bindings/_bleio/Characteristic.h" + +typedef struct { + mp_obj_base_t base; + bleio_characteristic_obj_t *characteristic; + bool deinited; +} bleio_characteristic_buffer_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.c b/ports/zephyr-cp/common-hal/_bleio/Connection.c new file mode 100644 index 0000000000000..938359c79caf7 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.c @@ -0,0 +1,97 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include +#include + +#include "py/runtime.h" +#include "bindings/zephyr_kernel/__init__.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Connection.h" + +void common_hal_bleio_connection_pair(bleio_connection_internal_t *self, bool bond) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_connection_disconnect(bleio_connection_internal_t *self) { + if (self == NULL || self->conn == NULL) { + return; + } + + int err = bt_conn_disconnect(self->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + if (err != 0 && err != -ENOTCONN) { + raise_zephyr_error(err); + } + + // The connection may now be disconnecting; force connections tuple rebuild. + common_hal_bleio_adapter_obj.connection_objs = NULL; +} + +bool common_hal_bleio_connection_get_connected(bleio_connection_obj_t *self) { + if (self == NULL || self->connection == NULL) { + return false; + } + + bleio_connection_internal_t *connection = self->connection; + if (connection->conn == NULL) { + return false; + } + + struct bt_conn_info info; + if (bt_conn_get_info(connection->conn, &info) != 0) { + return false; + } + + return info.state == BT_CONN_STATE_CONNECTED || info.state == BT_CONN_STATE_DISCONNECTING; +} + +mp_int_t common_hal_bleio_connection_get_max_packet_length(bleio_connection_internal_t *self) { + if (self == NULL || self->conn == NULL) { + return 20; + } + + uint16_t mtu = bt_gatt_get_mtu(self->conn); + if (mtu < 3) { + return 20; + } + return mtu - 3; +} + +bool common_hal_bleio_connection_get_paired(bleio_connection_obj_t *self) { + return false; +} + +mp_obj_tuple_t *common_hal_bleio_connection_discover_remote_services(bleio_connection_obj_t *self, mp_obj_t service_uuids_whitelist) { + mp_raise_NotImplementedError(NULL); +} + +mp_float_t common_hal_bleio_connection_get_connection_interval(bleio_connection_internal_t *self) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_connection_set_connection_interval(bleio_connection_internal_t *self, mp_float_t new_interval) { + mp_raise_NotImplementedError(NULL); +} + +mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection) { + if (connection == NULL) { + return mp_const_none; + } + + if (connection->connection_obj != mp_const_none) { + return connection->connection_obj; + } + + bleio_connection_obj_t *connection_obj = mp_obj_malloc(bleio_connection_obj_t, &bleio_connection_type); + connection_obj->connection = connection; + connection_obj->disconnect_reason = 0; + connection->connection_obj = MP_OBJ_FROM_PTR(connection_obj); + + return connection->connection_obj; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Connection.h b/ports/zephyr-cp/common-hal/_bleio/Connection.h new file mode 100644 index 0000000000000..dc14125db5f7c --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Connection.h @@ -0,0 +1,27 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "py/obj.h" + +struct bt_conn; + +typedef struct { + struct bt_conn *conn; + mp_obj_t connection_obj; +} bleio_connection_internal_t; + +typedef struct { + mp_obj_base_t base; + bleio_connection_internal_t *connection; + uint8_t disconnect_reason; +} bleio_connection_obj_t; + +mp_obj_t bleio_connection_new_from_internal(bleio_connection_internal_t *connection); diff --git a/ports/zephyr-cp/common-hal/_bleio/Descriptor.c b/ports/zephyr-cp/common-hal/_bleio/Descriptor.c new file mode 100644 index 0000000000000..a3e65a5e006f6 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Descriptor.c @@ -0,0 +1,30 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/Descriptor.h" +#include "shared-bindings/_bleio/Characteristic.h" + +void common_hal_bleio_descriptor_construct(bleio_descriptor_obj_t *self, bleio_characteristic_obj_t *characteristic, bleio_uuid_obj_t *uuid, bleio_attribute_security_mode_t read_perm, bleio_attribute_security_mode_t write_perm, mp_int_t max_length, bool fixed_length, mp_buffer_info_t *initial_value_bufinfo) { + mp_raise_NotImplementedError(NULL); +} + +bleio_uuid_obj_t *common_hal_bleio_descriptor_get_uuid(bleio_descriptor_obj_t *self) { + return self->uuid; +} + +bleio_characteristic_obj_t *common_hal_bleio_descriptor_get_characteristic(bleio_descriptor_obj_t *self) { + mp_raise_NotImplementedError(NULL); +} + +size_t common_hal_bleio_descriptor_get_value(bleio_descriptor_obj_t *self, uint8_t *buf, size_t len) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_descriptor_set_value(bleio_descriptor_obj_t *self, mp_buffer_info_t *bufinfo) { + mp_raise_NotImplementedError(NULL); +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Descriptor.h b/ports/zephyr-cp/common-hal/_bleio/Descriptor.h new file mode 100644 index 0000000000000..1d29cb27a509b --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Descriptor.h @@ -0,0 +1,24 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/_bleio/Attribute.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct _bleio_descriptor_obj { + mp_obj_base_t base; + bleio_uuid_obj_t *uuid; + uint16_t handle; + bleio_attribute_security_mode_t read_perm; + bleio_attribute_security_mode_t write_perm; + uint16_t max_length; + bool fixed_length; + uint8_t *value; + uint16_t value_length; +} bleio_descriptor_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c new file mode 100644 index 0000000000000..82fe8a3d1760c --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.c @@ -0,0 +1,66 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/PacketBuffer.h" + +void common_hal_bleio_packet_buffer_construct( + bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, + size_t buffer_size, size_t max_packet_size) { + (void)self; + (void)characteristic; + (void)buffer_size; + (void)max_packet_size; + mp_raise_NotImplementedError(NULL); +} + +mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len) { + (void)self; + (void)data; + (void)len; + (void)header; + (void)header_len; + mp_raise_NotImplementedError(NULL); +} + +mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len) { + (void)self; + (void)data; + (void)len; + mp_raise_NotImplementedError(NULL); +} + +mp_int_t common_hal_bleio_packet_buffer_get_incoming_packet_length(bleio_packet_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +mp_int_t common_hal_bleio_packet_buffer_get_outgoing_packet_length(bleio_packet_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_packet_buffer_flush(bleio_packet_buffer_obj_t *self) { + (void)self; + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_bleio_packet_buffer_deinited(bleio_packet_buffer_obj_t *self) { + return self->deinited; +} + +void common_hal_bleio_packet_buffer_deinit(bleio_packet_buffer_obj_t *self) { + if (self == NULL) { + return; + } + self->deinited = true; +} + +bool common_hal_bleio_packet_buffer_connected(bleio_packet_buffer_obj_t *self) { + (void)self; + return false; +} diff --git a/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h new file mode 100644 index 0000000000000..c8cd763fd6146 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/PacketBuffer.h @@ -0,0 +1,21 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "py/obj.h" + +typedef struct _bleio_characteristic_obj bleio_characteristic_obj_t; + +typedef void *ble_event_handler_t; + +typedef struct { + mp_obj_base_t base; + bool deinited; +} bleio_packet_buffer_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/Service.c b/ports/zephyr-cp/common-hal/_bleio/Service.c new file mode 100644 index 0000000000000..cefc85b6df655 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Service.c @@ -0,0 +1,46 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/Service.h" +#include "shared-bindings/_bleio/Characteristic.h" + +uint32_t _common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary, mp_obj_list_t *characteristic_list) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_service_construct(bleio_service_obj_t *self, bleio_uuid_obj_t *uuid, bool is_secondary) { + mp_raise_NotImplementedError(NULL); +} + +void common_hal_bleio_service_deinit(bleio_service_obj_t *self) { + // Nothing to do +} + +void common_hal_bleio_service_from_remote_service(bleio_service_obj_t *self, bleio_connection_obj_t *connection, bleio_uuid_obj_t *uuid, bool is_secondary) { + mp_raise_NotImplementedError(NULL); +} + +bleio_uuid_obj_t *common_hal_bleio_service_get_uuid(bleio_service_obj_t *self) { + return self->uuid; +} + +mp_obj_tuple_t *common_hal_bleio_service_get_characteristics(bleio_service_obj_t *self) { + return mp_obj_new_tuple(self->characteristic_list->len, self->characteristic_list->items); +} + +bool common_hal_bleio_service_get_is_remote(bleio_service_obj_t *self) { + return self->is_remote; +} + +bool common_hal_bleio_service_get_is_secondary(bleio_service_obj_t *self) { + return self->is_secondary; +} + +void common_hal_bleio_service_add_characteristic(bleio_service_obj_t *self, bleio_characteristic_obj_t *characteristic, mp_buffer_info_t *initial_value_bufinfo, const char *user_description) { + mp_raise_NotImplementedError(NULL); +} diff --git a/ports/zephyr-cp/common-hal/_bleio/Service.h b/ports/zephyr-cp/common-hal/_bleio/Service.h new file mode 100644 index 0000000000000..86727d3b0f73b --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/Service.h @@ -0,0 +1,23 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "py/objlist.h" +#include "common-hal/_bleio/UUID.h" + +typedef struct bleio_service_obj { + mp_obj_base_t base; + bleio_uuid_obj_t *uuid; + mp_obj_t connection; + mp_obj_list_t *characteristic_list; + uint16_t start_handle; + uint16_t end_handle; + bool is_remote; + bool is_secondary; +} bleio_service_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/UUID.c b/ports/zephyr-cp/common-hal/_bleio/UUID.c new file mode 100644 index 0000000000000..916eedb2c4745 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/UUID.c @@ -0,0 +1,52 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/runtime.h" +#include "shared-bindings/_bleio/UUID.h" + +void common_hal_bleio_uuid_construct(bleio_uuid_obj_t *self, mp_int_t uuid16, const uint8_t uuid128[16]) { + if (uuid16 != 0) { + // 16-bit UUID + self->size = 16; + // Convert 16-bit UUID to 128-bit + // Bluetooth Base UUID: 00000000-0000-1000-8000-00805F9B34FB + const uint8_t base_uuid[16] = {0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + memcpy(self->uuid128, base_uuid, 16); + self->uuid128[12] = (uuid16 & 0xff); + self->uuid128[13] = (uuid16 >> 8) & 0xff; + } else { + // 128-bit UUID + self->size = 128; + memcpy(self->uuid128, uuid128, 16); + } +} + +uint32_t common_hal_bleio_uuid_get_uuid16(bleio_uuid_obj_t *self) { + if (self->size == 16) { + return (self->uuid128[13] << 8) | self->uuid128[12]; + } + return 0; +} + +void common_hal_bleio_uuid_get_uuid128(bleio_uuid_obj_t *self, uint8_t uuid128[16]) { + memcpy(uuid128, self->uuid128, 16); +} + +uint32_t common_hal_bleio_uuid_get_size(bleio_uuid_obj_t *self) { + return self->size; +} + +void common_hal_bleio_uuid_pack_into(bleio_uuid_obj_t *self, uint8_t *buf) { + if (self->size == 16) { + buf[0] = self->uuid128[12]; + buf[1] = self->uuid128[13]; + } else { + memcpy(buf, self->uuid128, 16); + } +} diff --git a/ports/zephyr-cp/common-hal/_bleio/UUID.h b/ports/zephyr-cp/common-hal/_bleio/UUID.h new file mode 100644 index 0000000000000..386f5a7b8b971 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/UUID.h @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + uint8_t uuid128[16]; + uint8_t size; +} bleio_uuid_obj_t; diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.c b/ports/zephyr-cp/common-hal/_bleio/__init__.c new file mode 100644 index 0000000000000..719564c1cd47e --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.c @@ -0,0 +1,51 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "shared-bindings/_bleio/__init__.h" +#include "shared-bindings/_bleio/Adapter.h" +#include "common-hal/_bleio/Adapter.h" +#include "supervisor/shared/bluetooth/bluetooth.h" + +// The singleton _bleio.Adapter object +bleio_adapter_obj_t common_hal_bleio_adapter_obj; + +void common_hal_bleio_init(void) { + common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; + bleio_adapter_reset(&common_hal_bleio_adapter_obj); +} + +void bleio_user_reset(void) { + if (common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { + // Stop any user scanning or advertising. + common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); + common_hal_bleio_adapter_stop_advertising(&common_hal_bleio_adapter_obj); + } + + // Maybe start advertising the BLE workflow. + supervisor_bluetooth_background(); +} + +void bleio_reset(void) { + common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; + if (!common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { + return; + } + + supervisor_stop_bluetooth(); + bleio_adapter_reset(&common_hal_bleio_adapter_obj); + common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); + supervisor_start_bluetooth(); +} + +void common_hal_bleio_gc_collect(void) { + bleio_adapter_gc_collect(&common_hal_bleio_adapter_obj); +} + +void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist) { + mp_raise_NotImplementedError(NULL); +} diff --git a/ports/zephyr-cp/common-hal/_bleio/__init__.h b/ports/zephyr-cp/common-hal/_bleio/__init__.h new file mode 100644 index 0000000000000..1502767c61597 --- /dev/null +++ b/ports/zephyr-cp/common-hal/_bleio/__init__.h @@ -0,0 +1,10 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2018 Dan Halbert for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Placeholder for Zephyr-specific BLE defines diff --git a/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c new file mode 100644 index 0000000000000..e858552c524c0 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.c @@ -0,0 +1,299 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/audiobusio/I2SOut.h" + +#include +#include +#include +#include +#include + +#include "bindings/zephyr_kernel/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-module/audiocore/__init__.h" +#include "py/runtime.h" + +#if CIRCUITPY_AUDIOBUSIO_I2SOUT + +#define AUDIO_THREAD_STACK_SIZE 2048 +#define AUDIO_THREAD_PRIORITY 5 + +// Forward declarations +static void fill_buffer(audiobusio_i2sout_obj_t *self, uint8_t *buffer, size_t buffer_size); +static void audio_thread_func(void *self_in, void *unused1, void *unused2); + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_audiobusio_i2sout_construct_from_device(audiobusio_i2sout_obj_t *self, const struct device *i2s_device) { + self->base.type = &audiobusio_i2sout_type; + self->i2s_dev = i2s_device; + self->left_justified = false; + self->playing = false; + self->paused = false; + self->sample = NULL; + self->slab_buffer = NULL; + self->thread_stack = NULL; + self->thread_id = NULL; + self->block_size = 0; + + return MP_OBJ_FROM_PTR(self); +} + +// Standard audiobusio construct - not used in Zephyr port (devices come from device tree) +void common_hal_audiobusio_i2sout_construct(audiobusio_i2sout_obj_t *self, + const mcu_pin_obj_t *bit_clock, const mcu_pin_obj_t *word_select, + const mcu_pin_obj_t *data, const mcu_pin_obj_t *main_clock, bool left_justified) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("Use device tree to define %q devices"), MP_QSTR_I2S); +} + +bool common_hal_audiobusio_i2sout_deinited(audiobusio_i2sout_obj_t *self) { + return self->i2s_dev == NULL; +} + +void common_hal_audiobusio_i2sout_deinit(audiobusio_i2sout_obj_t *self) { + if (common_hal_audiobusio_i2sout_deinited(self)) { + return; + } + + // Stop playback (which will free buffers) + common_hal_audiobusio_i2sout_stop(self); + + // Note: Pins and I2S device are managed by Zephyr, not released here + self->i2s_dev = NULL; +} + +static void fill_buffer(audiobusio_i2sout_obj_t *self, uint8_t *buffer, size_t buffer_size) { + if (self->sample == NULL || self->paused || self->stopping) { + // Fill with silence + memset(buffer, 0, buffer_size); + return; + } + + uint32_t bytes_filled = 0; + while (bytes_filled < buffer_size) { + uint8_t *sample_buffer; + uint32_t sample_buffer_length; + + audioio_get_buffer_result_t result = audiosample_get_buffer( + self->sample, false, 0, &sample_buffer, &sample_buffer_length); + + if (result == GET_BUFFER_ERROR) { + // Error getting buffer, stop playback + self->stopping = true; + memset(buffer + bytes_filled, 0, buffer_size - bytes_filled); + return; + } + + if (result == GET_BUFFER_DONE) { + if (self->loop) { + // Reset to beginning + audiosample_reset_buffer(self->sample, false, 0); + } else { + // Done playing, fill rest with silence + self->stopping = true; + i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DRAIN); + memset(buffer + bytes_filled, 0, buffer_size - bytes_filled); + return; + } + } + + // Copy data to buffer + uint32_t bytes_to_copy = sample_buffer_length; + if (bytes_filled + bytes_to_copy > buffer_size) { + bytes_to_copy = buffer_size - bytes_filled; + } + + memcpy(buffer + bytes_filled, sample_buffer, bytes_to_copy); + bytes_filled += bytes_to_copy; + } +} + +static void audio_thread_func(void *self_in, void *unused1, void *unused2) { + audiobusio_i2sout_obj_t *self = (audiobusio_i2sout_obj_t *)self_in; + + while (!self->stopping) { + uint8_t *next_buffer = NULL; + // Wait until I2S has freed the buffer it is sending. + if (k_mem_slab_alloc(&self->mem_slab, (void **)&next_buffer, K_FOREVER) != 0) { + break; + } + if (self->stopping) { + // Stopping so break. + k_mem_slab_free(&self->mem_slab, next_buffer); + break; + } + fill_buffer(self, next_buffer, self->block_size); + + // Write to I2S + int ret = i2s_write(self->i2s_dev, next_buffer, self->block_size); + if (ret < 0) { + printk("i2s_write failed: %d\n", ret); + k_mem_slab_free(&self->mem_slab, next_buffer); + // Error writing, stop playback + self->playing = false; + break; + } + } +} + +void common_hal_audiobusio_i2sout_play(audiobusio_i2sout_obj_t *self, + mp_obj_t sample, bool loop) { + // Stop any existing playback + if (self->playing) { + common_hal_audiobusio_i2sout_stop(self); + } + + // Get sample information + uint8_t bits_per_sample = audiosample_get_bits_per_sample(sample); + uint32_t sample_rate = audiosample_get_sample_rate(sample); + uint8_t channel_count = audiosample_get_channel_count(sample); + + // Store sample parameters + self->sample = sample; + self->loop = loop; + self->bytes_per_sample = bits_per_sample / 8; + self->channel_count = channel_count; + self->stopping = false; + + // Get buffer structure from the sample + bool single_buffer, samples_signed; + uint32_t max_buffer_length; + uint8_t sample_spacing; + audiosample_get_buffer_structure(sample, /* single_channel_output */ false, + &single_buffer, &samples_signed, &max_buffer_length, &sample_spacing); + + // Use max_buffer_length from the sample as the block size + self->block_size = max_buffer_length; + if (channel_count == 1) { + // Make room for stereo samples. + self->block_size *= 2; + } + size_t block_size = self->block_size; + uint32_t num_blocks = 4; // Use 4 blocks for buffering + + // Allocate memory slab buffer + self->slab_buffer = m_malloc(self->block_size * num_blocks); + + // Initialize memory slab + int ret = k_mem_slab_init(&self->mem_slab, self->slab_buffer, block_size, num_blocks); + CHECK_ZEPHYR_RESULT(ret); + + // Configure I2S + struct i2s_config config; + config.word_size = bits_per_sample; + config.channels = 2; + config.format = self->left_justified ? I2S_FMT_DATA_FORMAT_LEFT_JUSTIFIED : I2S_FMT_DATA_FORMAT_I2S; + config.options = I2S_OPT_BIT_CLK_MASTER | I2S_OPT_FRAME_CLK_MASTER; + config.frame_clk_freq = sample_rate; + config.mem_slab = &self->mem_slab; + config.block_size = block_size; + config.timeout = 1000; // Not a k_timeout_t. In milliseconds. + + // Configure returns EINVAL if the I2S device is not ready. We loop on this + // because it should be ready after it comes to a complete stop. + ret = -EAGAIN; + while (ret == -EAGAIN) { + ret = i2s_configure(self->i2s_dev, I2S_DIR_TX, &config); + } + if (ret != 0) { + common_hal_audiobusio_i2sout_stop(self); + raise_zephyr_error(ret); + } + + // Fill every slab before starting playback to avoid underruns. + for (uint32_t i = 0; i < num_blocks; i++) { + uint8_t *buf = NULL; + k_mem_slab_alloc(&self->mem_slab, (void **)&buf, K_NO_WAIT); + fill_buffer(self, buf, block_size); + ret = i2s_write(self->i2s_dev, buf, block_size); + if (ret < 0) { + printk("i2s_write failed: %d\n", ret); + k_mem_slab_free(&self->mem_slab, buf); + common_hal_audiobusio_i2sout_stop(self); + raise_zephyr_error(ret); + } + } + + // Allocate thread stack with proper MPU alignment for HW stack protection + self->thread_stack = k_thread_stack_alloc(AUDIO_THREAD_STACK_SIZE, 0); + + // Create and start audio processing thread + self->thread_id = k_thread_create(&self->thread, self->thread_stack, + AUDIO_THREAD_STACK_SIZE, + audio_thread_func, + self, NULL, NULL, + AUDIO_THREAD_PRIORITY, 0, K_NO_WAIT); + // Start I2S + ret = i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START); + if (ret < 0) { + common_hal_audiobusio_i2sout_stop(self); + raise_zephyr_error(ret); + } + + self->playing = true; +} + +void common_hal_audiobusio_i2sout_stop(audiobusio_i2sout_obj_t *self) { + if (!self->playing) { + return; + } + + self->playing = false; + self->paused = false; + self->stopping = true; + + // Stop I2S + i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_DROP); + + // Wait for thread to finish + if (self->thread_id != NULL) { + k_thread_join(self->thread_id, K_FOREVER); + self->thread_id = NULL; + } + + // Free thread stack + if (self->thread_stack != NULL) { + k_thread_stack_free(self->thread_stack); + self->thread_stack = NULL; + } + + // Free buffers + if (self->slab_buffer != NULL) { + m_free(self->slab_buffer); + self->slab_buffer = NULL; + } + + self->sample = NULL; +} + +bool common_hal_audiobusio_i2sout_get_playing(audiobusio_i2sout_obj_t *self) { + return self->playing; +} + +void common_hal_audiobusio_i2sout_pause(audiobusio_i2sout_obj_t *self) { + if (!self->playing || self->paused) { + return; + } + + self->paused = true; + i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_STOP); +} + +void common_hal_audiobusio_i2sout_resume(audiobusio_i2sout_obj_t *self) { + if (!self->playing || !self->paused) { + return; + } + + self->paused = false; + // Thread will automatically resume filling buffers + i2s_trigger(self->i2s_dev, I2S_DIR_TX, I2S_TRIGGER_START); +} + +bool common_hal_audiobusio_i2sout_get_paused(audiobusio_i2sout_obj_t *self) { + return self->paused; +} + +#endif // CIRCUITPY_AUDIOBUSIO_I2SOUT diff --git a/ports/zephyr-cp/common-hal/audiobusio/I2SOut.h b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.h new file mode 100644 index 0000000000000..916471fa83328 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/I2SOut.h @@ -0,0 +1,47 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "common-hal/microcontroller/Pin.h" +#include "shared-module/audiocore/__init__.h" + +#include +#include +#include + +#if CIRCUITPY_AUDIOBUSIO_I2SOUT + +typedef struct { + mp_obj_base_t base; + const struct device *i2s_dev; + const mcu_pin_obj_t *bit_clock; + const mcu_pin_obj_t *word_select; + const mcu_pin_obj_t *data; + const mcu_pin_obj_t *main_clock; + mp_obj_t sample; + struct k_mem_slab mem_slab; + char *slab_buffer; + struct k_thread thread; + k_thread_stack_t *thread_stack; + k_tid_t thread_id; + size_t block_size; + bool left_justified; + bool playing; + bool paused; + bool loop; + bool stopping; + bool single_buffer; + uint8_t bytes_per_sample; + uint8_t channel_count; +} audiobusio_i2sout_obj_t; + +mp_obj_t common_hal_audiobusio_i2sout_construct_from_device(audiobusio_i2sout_obj_t *self, const struct device *i2s_device); + +void i2sout_reset(void); + +#endif // CIRCUITPY_AUDIOBUSIO_I2SOUT diff --git a/ports/zephyr-cp/common-hal/audiobusio/PDMIn.c b/ports/zephyr-cp/common-hal/audiobusio/PDMIn.c new file mode 100644 index 0000000000000..3d3cfef525849 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/PDMIn.c @@ -0,0 +1,39 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/audiobusio/PDMIn.h" + +#include "py/runtime.h" + +#if CIRCUITPY_AUDIOBUSIO_PDMIN + +void common_hal_audiobusio_pdmin_construct(audiobusio_pdmin_obj_t *self, + const mcu_pin_obj_t *clock_pin, const mcu_pin_obj_t *data_pin, + uint32_t sample_rate, uint8_t bit_depth, bool mono, uint8_t oversample) { + mp_raise_NotImplementedError(NULL); +} + +bool common_hal_audiobusio_pdmin_deinited(audiobusio_pdmin_obj_t *self) { + return true; +} + +void common_hal_audiobusio_pdmin_deinit(audiobusio_pdmin_obj_t *self) { +} + +uint8_t common_hal_audiobusio_pdmin_get_bit_depth(audiobusio_pdmin_obj_t *self) { + return 0; +} + +uint32_t common_hal_audiobusio_pdmin_get_sample_rate(audiobusio_pdmin_obj_t *self) { + return 0; +} + +uint32_t common_hal_audiobusio_pdmin_record_to_buffer(audiobusio_pdmin_obj_t *self, + uint16_t *output_buffer, uint32_t output_buffer_length) { + return 0; +} + +#endif // CIRCUITPY_AUDIOBUSIO_PDMIN diff --git a/ports/zephyr-cp/common-hal/audiobusio/PDMIn.h b/ports/zephyr-cp/common-hal/audiobusio/PDMIn.h new file mode 100644 index 0000000000000..195a436f3cf61 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/PDMIn.h @@ -0,0 +1,18 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "common-hal/microcontroller/Pin.h" + +#if CIRCUITPY_AUDIOBUSIO_PDMIN + +typedef struct { + mp_obj_base_t base; +} audiobusio_pdmin_obj_t; + +#endif // CIRCUITPY_AUDIOBUSIO_PDMIN diff --git a/ports/zephyr-cp/common-hal/audiobusio/__init__.c b/ports/zephyr-cp/common-hal/audiobusio/__init__.c new file mode 100644 index 0000000000000..5d2e802904d01 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// No special initialization required for audiobusio diff --git a/ports/zephyr-cp/common-hal/audiobusio/__init__.h b/ports/zephyr-cp/common-hal/audiobusio/__init__.h new file mode 100644 index 0000000000000..8ba7882bf9474 --- /dev/null +++ b/ports/zephyr-cp/common-hal/audiobusio/__init__.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// No common definitions needed for audiobusio diff --git a/ports/zephyr-cp/common-hal/busio/I2C.c b/ports/zephyr-cp/common-hal/busio/I2C.c new file mode 100644 index 0000000000000..84e95721b2736 --- /dev/null +++ b/ports/zephyr-cp/common-hal/busio/I2C.c @@ -0,0 +1,152 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/busio/I2C.h" +#include "py/mperrno.h" +#include "py/runtime.h" + +#include +#include +#include + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_i2c_construct_from_device(busio_i2c_obj_t *self, const struct device *i2c_device) { + self->base.type = &busio_i2c_type; + self->i2c_device = i2c_device; + k_mutex_init(&self->mutex); + self->has_lock = false; + return MP_OBJ_FROM_PTR(self); +} + +// Standard busio construct - not used in Zephyr port (devices come from device tree) +void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, + const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, + uint32_t frequency, uint32_t timeout_ms) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("Use device tree to define %q devices"), MP_QSTR_I2C); +} + +bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) { + // Always leave it active (managed by Zephyr) + return false; +} + +void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { + if (common_hal_busio_i2c_deinited(self)) { + return; + } + // Always leave it active (managed by Zephyr) +} + +void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) { + // Not needed for Zephyr port +} + +bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { + if (common_hal_busio_i2c_deinited(self)) { + return false; + } + + // Try a zero-length write to probe for device + uint8_t dummy; + int ret = i2c_write(self->i2c_device, &dummy, 0, addr); + return ret == 0; +} + +bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { + if (common_hal_busio_i2c_deinited(self)) { + return false; + } + + self->has_lock = k_mutex_lock(&self->mutex, K_NO_WAIT) == 0; + return self->has_lock; +} + +bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { + return self->has_lock; +} + +void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { + self->has_lock = false; + k_mutex_unlock(&self->mutex); +} + +mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, + const uint8_t *data, size_t len) { + + if (common_hal_busio_i2c_deinited(self)) { + return -MP_EIO; + } + + int ret = i2c_write(self->i2c_device, data, len, addr); + if (ret != 0) { + // Map Zephyr error codes to errno + if (ret == -ENOTSUP) { + return -MP_EOPNOTSUPP; + } else if (ret == -EIO || ret == -ENXIO) { + return -MP_EIO; + } else if (ret == -EBUSY) { + return -MP_EBUSY; + } + return -MP_EIO; + } + + return 0; +} + +mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, + uint8_t *data, size_t len) { + + if (common_hal_busio_i2c_deinited(self)) { + return -MP_EIO; + } + + if (len == 0) { + return 0; + } + + int ret = i2c_read(self->i2c_device, data, len, addr); + if (ret != 0) { + // Map Zephyr error codes to errno + if (ret == -ENOTSUP) { + return -MP_EOPNOTSUPP; + } else if (ret == -EIO || ret == -ENXIO) { + return -MP_EIO; + } else if (ret == -EBUSY) { + return -MP_EBUSY; + } + return -MP_EIO; + } + + return 0; +} + +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, + uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { + + if (common_hal_busio_i2c_deinited(self)) { + return -MP_EIO; + } + + // Use i2c_write_read for combined transaction with repeated start + int ret = i2c_write_read(self->i2c_device, addr, out_data, out_len, in_data, in_len); + if (ret != 0) { + // Map Zephyr error codes to errno + if (ret == -ENOTSUP) { + return -MP_EOPNOTSUPP; + } else if (ret == -EIO || ret == -ENXIO) { + return -MP_EIO; + } else if (ret == -EBUSY) { + return -MP_EBUSY; + } + return -MP_EIO; + } + + return 0; +} + +void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { + // Not needed for Zephyr port (devices are managed by Zephyr) +} diff --git a/ports/zephyr-cp/common-hal/busio/I2C.h b/ports/zephyr-cp/common-hal/busio/I2C.h new file mode 100644 index 0000000000000..4fa877739b781 --- /dev/null +++ b/ports/zephyr-cp/common-hal/busio/I2C.h @@ -0,0 +1,20 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include + +typedef struct { + mp_obj_base_t base; + const struct device *i2c_device; + struct k_mutex mutex; + bool has_lock; +} busio_i2c_obj_t; + +// Helper function to construct from Zephyr device tree device +mp_obj_t common_hal_busio_i2c_construct_from_device(busio_i2c_obj_t *self, const struct device *i2c_device); diff --git a/ports/zephyr-cp/common-hal/busio/SPI.c b/ports/zephyr-cp/common-hal/busio/SPI.c new file mode 100644 index 0000000000000..2864c90b49092 --- /dev/null +++ b/ports/zephyr-cp/common-hal/busio/SPI.c @@ -0,0 +1,281 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/busio/SPI.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "py/gc.h" +#include "shared/runtime/interrupt_char.h" +#include "supervisor/port.h" + +#include +#include +#include + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_spi_construct_from_device(busio_spi_obj_t *self, const struct device *spi_device) { + self->base.type = &busio_spi_type; + self->spi_device = spi_device; + k_mutex_init(&self->mutex); + self->has_lock = false; + self->active_config = 0; + + k_poll_signal_init(&self->signal); + + // Default configuration for both config slots + self->config[0].frequency = 100000; + self->config[0].operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_LINES_SINGLE; + self->config[1].frequency = 100000; + self->config[1].operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_LINES_SINGLE; + + return MP_OBJ_FROM_PTR(self); +} + +// Standard busio construct - not used in Zephyr port (devices come from device tree) +void common_hal_busio_spi_construct(busio_spi_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *miso, bool half_duplex) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("Use device tree to define %q devices"), MP_QSTR_SPI); +} + +bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { + // Always leave it active + return false; +} + +void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { + if (common_hal_busio_spi_deinited(self)) { + return; + } + // Always leave it active +} + +void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self) { + // Not needed for Zephyr port +} + +bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + self->has_lock = k_mutex_lock(&self->mutex, K_NO_WAIT) == 0; + return self->has_lock; +} + +bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { + return self->has_lock; +} + +void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { + self->has_lock = false; + k_mutex_unlock(&self->mutex); +} + +bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + // Set operation mode based on polarity and phase + uint16_t operation = SPI_OP_MODE_MASTER | SPI_WORD_SET(bits) | SPI_LINES_SINGLE; + + if (polarity) { + operation |= SPI_MODE_CPOL; + } + if (phase) { + operation |= SPI_MODE_CPHA; + } + + // Check if settings have changed. We must switch to the other config slot if they have because + // Zephyr drivers are allowed to use the pointer value to know if it has changed. + struct spi_config *current_config = &self->config[self->active_config]; + if (current_config->frequency != baudrate || current_config->operation != operation) { + // Settings changed, switch to the other config slot + self->active_config = 1 - self->active_config; + + // Update the new active configuration + self->config[self->active_config].frequency = baudrate; + self->config[self->active_config].operation = operation; + } + + return true; +} + +bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + if (len == 0) { + return true; + } + + const struct spi_buf tx_buf = { + .buf = (void *)data, + .len = len + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + // Initialize the signal for async operation + k_poll_signal_reset(&self->signal); + + int ret = spi_transceive_signal(self->spi_device, &self->config[self->active_config], &tx, NULL, &self->signal); + if (ret != 0) { + return false; + } + + // Wait for the transfer to complete while running background tasks + int signaled = 0; + int result = 0; + while (!signaled && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + k_poll_signal_check(&self->signal, &signaled, &result); + } + + return signaled && result == 0; +} + +bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + if (len == 0) { + return true; + } + + // For read, we need to write dummy bytes + // We'll allocate a temporary buffer if write_value is not 0 + uint8_t *tx_data = NULL; + bool need_free = false; + bool used_port_malloc = false; + + if (write_value != 0) { + // Use port_malloc if GC isn't active, otherwise use m_malloc + if (gc_alloc_possible()) { + tx_data = m_malloc(len); + } else { + tx_data = port_malloc(len, false); + used_port_malloc = true; + } + if (tx_data == NULL) { + return false; + } + memset(tx_data, write_value, len); + need_free = true; + } + + const struct spi_buf tx_buf = { + .buf = tx_data, + .len = tx_data ? len : 0 + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = tx_data ? 1 : 0 + }; + + const struct spi_buf rx_buf = { + .buf = data, + .len = len + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1 + }; + + // Initialize the signal for async operation + k_poll_signal_reset(&self->signal); + + int ret = spi_transceive_signal(self->spi_device, &self->config[self->active_config], &tx, &rx, &self->signal); + + if (need_free) { + if (used_port_malloc) { + port_free(tx_data); + } else { + m_free(tx_data); + } + } + + if (ret != 0) { + return false; + } + + // Wait for the transfer to complete while running background tasks + int signaled = 0; + int result = 0; + while (!signaled && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + k_poll_signal_check(&self->signal, &signaled, &result); + } + + return signaled && result == 0; +} + +bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len) { + if (common_hal_busio_spi_deinited(self)) { + return false; + } + + if (len == 0) { + return true; + } + + const struct spi_buf tx_buf = { + .buf = (void *)data_out, + .len = len + }; + const struct spi_buf_set tx = { + .buffers = &tx_buf, + .count = 1 + }; + + const struct spi_buf rx_buf = { + .buf = data_in, + .len = len + }; + const struct spi_buf_set rx = { + .buffers = &rx_buf, + .count = 1 + }; + + // Initialize the signal for async operation + k_poll_signal_reset(&self->signal); + + int ret = spi_transceive_signal(self->spi_device, &self->config[self->active_config], &tx, &rx, &self->signal); + if (ret != 0) { + return false; + } + + // Wait for the transfer to complete while running background tasks + int signaled = 0; + int result = 0; + while (!signaled && !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + k_poll_signal_check(&self->signal, &signaled, &result); + } + + return signaled && result == 0; +} + +uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) { + return self->config[self->active_config].frequency; +} + +uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) { + return (self->config[self->active_config].operation & SPI_MODE_CPHA) ? 1 : 0; +} + +uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { + return (self->config[self->active_config].operation & SPI_MODE_CPOL) ? 1 : 0; +} + +void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { + // Not needed for Zephyr port (devices are managed by Zephyr) +} diff --git a/ports/zephyr-cp/common-hal/busio/SPI.h b/ports/zephyr-cp/common-hal/busio/SPI.h new file mode 100644 index 0000000000000..87411c9825ce6 --- /dev/null +++ b/ports/zephyr-cp/common-hal/busio/SPI.h @@ -0,0 +1,24 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include +#include + +typedef struct { + mp_obj_base_t base; + const struct device *spi_device; + struct k_mutex mutex; + bool has_lock; + struct spi_config config[2]; // Two configs for pointer comparison by driver + uint8_t active_config; // Index of currently active config (0 or 1) + struct k_poll_signal signal; +} busio_spi_obj_t; + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_spi_construct_from_device(busio_spi_obj_t *self, const struct device *spi_device); diff --git a/ports/zephyr-cp/common-hal/busio/UART.c b/ports/zephyr-cp/common-hal/busio/UART.c new file mode 100644 index 0000000000000..9940853da50a5 --- /dev/null +++ b/ports/zephyr-cp/common-hal/busio/UART.c @@ -0,0 +1,158 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/busio/UART.h" + +#include "shared/runtime/interrupt_char.h" +#include "py/mpconfig.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "py/runtime.h" +#include "py/stream.h" + +#include +#include + +#include +#include +LOG_MODULE_REGISTER(busio_uart); + +/* + * Read characters from UART until line end is detected. Afterwards push the + * data to the message queue. + */ +static void serial_cb(const struct device *dev, void *user_data) { + busio_uart_obj_t *self = (busio_uart_obj_t *)user_data; + + uint8_t c; + + if (!uart_irq_update(dev)) { + return; + } + + if (!uart_irq_rx_ready(dev)) { + return; + } + + /* read until FIFO empty */ + while (uart_fifo_read(dev, &c, 1) == 1) { + if (mp_interrupt_char == c) { + common_hal_busio_uart_clear_rx_buffer(self); + mp_sched_keyboard_interrupt(); + } else if (!self->rx_paused) { + if (k_msgq_put(&self->msgq, &c, K_NO_WAIT) != 0) { + self->rx_paused = true; + } + } + } +} + +void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { + // Not needed for Zephyr port (devices are managed by Zephyr) +} + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_uart_construct_from_device(busio_uart_obj_t *self, const struct device *uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer) { + self->base.type = &busio_uart_type; + self->uart_device = uart_device; + int ret = uart_irq_callback_user_data_set(uart_device, serial_cb, self); + + if (ret < 0) { + LOG_ERR("Failed to set UART IRQ callback: %d", ret); + } + + k_msgq_init(&self->msgq, receiver_buffer, 1, receiver_buffer_size); + + self->timeout = K_FOREVER; + self->write_timeout = K_FOREVER; + self->rx_paused = false; + uart_irq_rx_enable(uart_device); + + return MP_OBJ_FROM_PTR(self); +} + +// Standard busio construct - not used in Zephyr port (devices come from device tree) +void common_hal_busio_uart_construct(busio_uart_obj_t *self, + const mcu_pin_obj_t *tx, const mcu_pin_obj_t *rx, + const mcu_pin_obj_t *rts, const mcu_pin_obj_t *cts, + const mcu_pin_obj_t *rs485_dir, bool rs485_invert, + uint32_t baudrate, uint8_t bits, busio_uart_parity_t parity, uint8_t stop, + mp_float_t timeout, uint16_t receiver_buffer_size, byte *receiver_buffer, + bool sigint_enabled) { + mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("Use device tree to define %q devices"), MP_QSTR_UART); +} + +bool common_hal_busio_uart_deinited(busio_uart_obj_t *self) { + return !device_is_ready(self->uart_device); +} + +void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { + // Leave it active (managed by Zephyr) +} + +// Read characters. +size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) { + size_t count = 0; + while (count < len && k_msgq_get(&self->msgq, data + count, self->timeout) == 0) { + count++; + } + if (count > 0) { + self->rx_paused = false; + } + + return count; +} + +// Write characters. +size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + for (int i = 0; i < len; i++) { + uart_poll_out(self->uart_device, data[i]); + } + + return len; +} + +uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { + struct uart_config config; + uart_config_get(self->uart_device, &config); + return config.baudrate; +} + +void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) { + struct uart_config config; + uart_config_get(self->uart_device, &config); + config.baudrate = baudrate; + uart_configure(self->uart_device, &config); +} + +mp_float_t common_hal_busio_uart_get_timeout(busio_uart_obj_t *self) { + return (mp_float_t)self->timeout.ticks / 1000000.0; +} + +void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeout) { + self->timeout = K_USEC((uint64_t)(timeout * 1000000)); +} + +mp_float_t common_hal_busio_uart_get_write_timeout(busio_uart_obj_t *self) { + return (mp_float_t)self->write_timeout.ticks / 1000000.0; +} + +void common_hal_busio_uart_set_write_timeout(busio_uart_obj_t *self, mp_float_t write_timeout) { + self->write_timeout = K_USEC((uint64_t)(write_timeout * 1000000)); +} + +uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { + return k_msgq_num_used_get(&self->msgq); +} + +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self) { + k_msgq_purge(&self->msgq); +} + +bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { + return true; +} diff --git a/ports/zephyr-cp/common-hal/zephyr_serial/UART.h b/ports/zephyr-cp/common-hal/busio/UART.h similarity index 51% rename from ports/zephyr-cp/common-hal/zephyr_serial/UART.h rename to ports/zephyr-cp/common-hal/busio/UART.h index 4e220ee630755..25e767c5b285a 100644 --- a/ports/zephyr-cp/common-hal/zephyr_serial/UART.h +++ b/ports/zephyr-cp/common-hal/busio/UART.h @@ -17,6 +17,13 @@ typedef struct { struct k_msgq msgq; k_timeout_t timeout; + k_timeout_t write_timeout; bool rx_paused; // set by irq if no space in rbuf -} zephyr_serial_uart_obj_t; +} busio_uart_obj_t; + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_busio_uart_construct_from_device(busio_uart_obj_t *self, const struct device *uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer); + +// Internal helper for clearing buffer +void common_hal_busio_uart_clear_rx_buffer(busio_uart_obj_t *self); diff --git a/ports/zephyr-cp/common-hal/zephyr_spi/__init__.c b/ports/zephyr-cp/common-hal/busio/__init__.c similarity index 55% rename from ports/zephyr-cp/common-hal/zephyr_spi/__init__.c rename to ports/zephyr-cp/common-hal/busio/__init__.c index d3a4d4727339f..135a53f490734 100644 --- a/ports/zephyr-cp/common-hal/zephyr_spi/__init__.c +++ b/ports/zephyr-cp/common-hal/busio/__init__.c @@ -1,7 +1,7 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// SPDX-FileCopyrightText: Copyright (c) 2025 Adafruit Industries LLC // // SPDX-License-Identifier: MIT -// No zephyr_spi module functions. +// No busio module functions. diff --git a/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.c b/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.c new file mode 100644 index 0000000000000..494b21cb02db9 --- /dev/null +++ b/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.c @@ -0,0 +1,15 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/hostnetwork/HostNetwork.h" + +hostnetwork_hostnetwork_obj_t common_hal_hostnetwork_obj = { + .base = { &hostnetwork_hostnetwork_type }, +}; + +void common_hal_hostnetwork_hostnetwork_construct(hostnetwork_hostnetwork_obj_t *self) { + (void)self; +} diff --git a/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.h b/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.h new file mode 100644 index 0000000000000..a6731546bdef1 --- /dev/null +++ b/ports/zephyr-cp/common-hal/hostnetwork/HostNetwork.h @@ -0,0 +1,11 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "bindings/hostnetwork/HostNetwork.h" + +extern hostnetwork_hostnetwork_obj_t common_hal_hostnetwork_obj; diff --git a/ports/zephyr-cp/common-hal/microcontroller/Processor.c b/ports/zephyr-cp/common-hal/microcontroller/Processor.c index ddc8b97056d2b..9f512a686ec14 100644 --- a/ports/zephyr-cp/common-hal/microcontroller/Processor.c +++ b/ports/zephyr-cp/common-hal/microcontroller/Processor.c @@ -21,7 +21,11 @@ float common_hal_mcu_processor_get_temperature(void) { extern uint32_t SystemCoreClock; uint32_t common_hal_mcu_processor_get_frequency(void) { + #ifdef __ARM__ return SystemCoreClock; + #else + return CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC; + #endif } float common_hal_mcu_processor_get_voltage(void) { diff --git a/ports/zephyr-cp/common-hal/microcontroller/__init__.c b/ports/zephyr-cp/common-hal/microcontroller/__init__.c index 6ebf5f4c36818..be33cd26c9ace 100644 --- a/ports/zephyr-cp/common-hal/microcontroller/__init__.c +++ b/ports/zephyr-cp/common-hal/microcontroller/__init__.c @@ -11,7 +11,7 @@ #include "common-hal/microcontroller/Pin.h" #include "common-hal/microcontroller/Processor.h" -// #include "shared-bindings/nvm/ByteArray.h" +#include "shared-bindings/nvm/ByteArray.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/Processor.h" @@ -93,14 +93,12 @@ const mcu_processor_obj_t common_hal_mcu_processor_obj = { }, }; -#if CIRCUITPY_NVM && CIRCUITPY_INTERNAL_NVM_SIZE > 0 +#if CIRCUITPY_NVM // The singleton nvm.ByteArray object. const nvm_bytearray_obj_t common_hal_mcu_nvm_obj = { .base = { .type = &nvm_bytearray_type, }, - .start_address = (uint8_t *)CIRCUITPY_INTERNAL_NVM_START_ADDR, - .len = CIRCUITPY_INTERNAL_NVM_SIZE, }; #endif diff --git a/ports/zephyr-cp/common-hal/nvm/ByteArray.c b/ports/zephyr-cp/common-hal/nvm/ByteArray.c new file mode 100644 index 0000000000000..b8f552d677389 --- /dev/null +++ b/ports/zephyr-cp/common-hal/nvm/ByteArray.c @@ -0,0 +1,103 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/runtime.h" +#include "common-hal/nvm/ByteArray.h" +#include "shared-bindings/nvm/ByteArray.h" + +#include + +#include +#include + +#define NVM_PARTITION nvm_partition + +#if FIXED_PARTITION_EXISTS(NVM_PARTITION) + +static const struct flash_area *nvm_area = NULL; +static size_t nvm_erase_size = 0; + +static bool ensure_nvm_open(void) { + if (nvm_area != NULL) { + return true; + } + int rc = flash_area_open(FIXED_PARTITION_ID(NVM_PARTITION), &nvm_area); + if (rc != 0) { + return false; + } + + const struct device *dev = flash_area_get_device(nvm_area); + struct flash_pages_info info; + flash_get_page_info_by_offs(dev, nvm_area->fa_off, &info); + nvm_erase_size = info.size; + + return true; +} + +uint32_t common_hal_nvm_bytearray_get_length(const nvm_bytearray_obj_t *self) { + if (!ensure_nvm_open()) { + return 0; + } + return nvm_area->fa_size; +} + +bool common_hal_nvm_bytearray_set_bytes(const nvm_bytearray_obj_t *self, + uint32_t start_index, uint8_t *values, uint32_t len) { + if (!ensure_nvm_open()) { + return false; + } + + uint32_t address = start_index; + while (len > 0) { + uint32_t page_offset = address % nvm_erase_size; + uint32_t page_start = address - page_offset; + uint32_t write_len = MIN(len, nvm_erase_size - page_offset); + + uint8_t *buffer = m_malloc(nvm_erase_size); + if (buffer == NULL) { + return false; + } + + // Read the full erase page. + int rc = flash_area_read(nvm_area, page_start, buffer, nvm_erase_size); + if (rc != 0) { + m_free(buffer); + return false; + } + + // Modify the relevant bytes. + memcpy(buffer + page_offset, values, write_len); + + // Erase the page. + rc = flash_area_erase(nvm_area, page_start, nvm_erase_size); + if (rc != 0) { + m_free(buffer); + return false; + } + + // Write the page back. + rc = flash_area_write(nvm_area, page_start, buffer, nvm_erase_size); + m_free(buffer); + if (rc != 0) { + return false; + } + + address += write_len; + values += write_len; + len -= write_len; + } + return true; +} + +void common_hal_nvm_bytearray_get_bytes(const nvm_bytearray_obj_t *self, + uint32_t start_index, uint32_t len, uint8_t *values) { + if (!ensure_nvm_open()) { + return; + } + flash_area_read(nvm_area, start_index, values, len); +} + +#endif // FIXED_PARTITION_EXISTS(NVM_PARTITION) diff --git a/ports/zephyr-cp/common-hal/nvm/ByteArray.h b/ports/zephyr-cp/common-hal/nvm/ByteArray.h new file mode 100644 index 0000000000000..9c771aaa3a950 --- /dev/null +++ b/ports/zephyr-cp/common-hal/nvm/ByteArray.h @@ -0,0 +1,13 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; +} nvm_bytearray_obj_t; diff --git a/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.c b/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.c new file mode 100644 index 0000000000000..d36b571535afe --- /dev/null +++ b/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.c @@ -0,0 +1,129 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// +// SPDX-License-Identifier: MIT + +#include "common-hal/rotaryio/IncrementalEncoder.h" +#include "shared-bindings/rotaryio/IncrementalEncoder.h" +#include "shared-module/rotaryio/IncrementalEncoder.h" + +#include "bindings/zephyr_kernel/__init__.h" +#include "py/runtime.h" + +#include +#include +#include +#include + +static void incrementalencoder_gpio_callback(const struct device *port, + struct gpio_callback *cb, gpio_port_pins_t pins) { + (void)port; + (void)pins; + rotaryio_incrementalencoder_gpio_callback_t *callback = + CONTAINER_OF(cb, rotaryio_incrementalencoder_gpio_callback_t, callback); + rotaryio_incrementalencoder_obj_t *self = callback->encoder; + if (self == NULL || self->pin_a == NULL) { + return; + } + + int a = gpio_pin_get(self->pin_a->port, self->pin_a->number); + int b = gpio_pin_get(self->pin_b->port, self->pin_b->number); + if (a < 0 || b < 0) { + return; + } + uint8_t new_state = ((uint8_t)a << 1) | (uint8_t)b; + shared_module_softencoder_state_update(self, new_state); +} + +void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, + const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b) { + // Ensure object starts in its deinit state. + common_hal_rotaryio_incrementalencoder_mark_deinit(self); + + self->pin_a = pin_a; + self->pin_b = pin_b; + self->divisor = 4; + + if (!device_is_ready(pin_a->port) || !device_is_ready(pin_b->port)) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(-ENODEV); + } + + int result = gpio_pin_configure(pin_a->port, pin_a->number, GPIO_INPUT | GPIO_PULL_UP); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + result = gpio_pin_configure(pin_b->port, pin_b->number, GPIO_INPUT | GPIO_PULL_UP); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + self->callback_a.encoder = self; + gpio_init_callback(&self->callback_a.callback, incrementalencoder_gpio_callback, + BIT(pin_a->number)); + result = gpio_add_callback(pin_a->port, &self->callback_a.callback); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + self->callback_b.encoder = self; + gpio_init_callback(&self->callback_b.callback, incrementalencoder_gpio_callback, + BIT(pin_b->number)); + result = gpio_add_callback(pin_b->port, &self->callback_b.callback); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + result = gpio_pin_interrupt_configure(pin_a->port, pin_a->number, GPIO_INT_EDGE_BOTH); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + result = gpio_pin_interrupt_configure(pin_b->port, pin_b->number, GPIO_INT_EDGE_BOTH); + if (result != 0) { + common_hal_rotaryio_incrementalencoder_deinit(self); + raise_zephyr_error(result); + } + + int a = gpio_pin_get(pin_a->port, pin_a->number); + int b = gpio_pin_get(pin_b->port, pin_b->number); + uint8_t quiescent_state = ((uint8_t)(a > 0) << 1) | (uint8_t)(b > 0); + shared_module_softencoder_state_init(self, quiescent_state); + + claim_pin(pin_a); + claim_pin(pin_b); +} + +bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self) { + return self->pin_a == NULL; +} + +void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self) { + if (common_hal_rotaryio_incrementalencoder_deinited(self)) { + return; + } + + // Best-effort cleanup. During failed construct(), some of these may not be + // initialized yet. Ignore cleanup errors. + gpio_pin_interrupt_configure(self->pin_a->port, self->pin_a->number, GPIO_INT_DISABLE); + gpio_pin_interrupt_configure(self->pin_b->port, self->pin_b->number, GPIO_INT_DISABLE); + gpio_remove_callback(self->pin_a->port, &self->callback_a.callback); + gpio_remove_callback(self->pin_b->port, &self->callback_b.callback); + + reset_pin(self->pin_a); + reset_pin(self->pin_b); + + common_hal_rotaryio_incrementalencoder_mark_deinit(self); +} + +void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self) { + self->pin_a = NULL; + self->pin_b = NULL; +} diff --git a/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.h b/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.h new file mode 100644 index 0000000000000..a0d2bb392e264 --- /dev/null +++ b/ports/zephyr-cp/common-hal/rotaryio/IncrementalEncoder.h @@ -0,0 +1,31 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2024 Adafruit Industries LLC +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include + +#include "common-hal/microcontroller/Pin.h" +#include "py/obj.h" + +typedef struct rotaryio_incrementalencoder_obj rotaryio_incrementalencoder_obj_t; + +typedef struct { + struct gpio_callback callback; + rotaryio_incrementalencoder_obj_t *encoder; +} rotaryio_incrementalencoder_gpio_callback_t; + +struct rotaryio_incrementalencoder_obj { + mp_obj_base_t base; + const mcu_pin_obj_t *pin_a; + const mcu_pin_obj_t *pin_b; + rotaryio_incrementalencoder_gpio_callback_t callback_a; + rotaryio_incrementalencoder_gpio_callback_t callback_b; + uint8_t state; // + int8_t sub_count; // count intermediate transitions between detents + int8_t divisor; // Number of quadrature edges required per count + mp_int_t position; +}; diff --git a/ports/zephyr-cp/common-hal/zephyr_i2c/__init__.c b/ports/zephyr-cp/common-hal/rotaryio/__init__.c similarity index 84% rename from ports/zephyr-cp/common-hal/zephyr_i2c/__init__.c rename to ports/zephyr-cp/common-hal/rotaryio/__init__.c index db93a442f0480..67cae26a8b7fe 100644 --- a/ports/zephyr-cp/common-hal/zephyr_i2c/__init__.c +++ b/ports/zephyr-cp/common-hal/rotaryio/__init__.c @@ -4,4 +4,4 @@ // // SPDX-License-Identifier: MIT -// No zephyr_i2c module functions. +// No rotaryio module functions. diff --git a/ports/zephyr-cp/common-hal/socketpool/Socket.c b/ports/zephyr-cp/common-hal/socketpool/Socket.c index 857526a12debd..ca8ba4198394a 100644 --- a/ports/zephyr-cp/common-hal/socketpool/Socket.c +++ b/ports/zephyr-cp/common-hal/socketpool/Socket.c @@ -20,165 +20,104 @@ #include "supervisor/shared/tick.h" #include "supervisor/workflow.h" -#include - -// void socketpool_resolve_host_or_throw(int family, int type, const char *hostname, struct sockaddr_storage *addr, int port) { -// // struct addrinfo *result_i; -// // const struct addrinfo hints = { -// // .ai_family = family, -// // .ai_socktype = type, -// // }; -// // int error = socketpool_getaddrinfo_common(hostname, port, &hints, &result_i); -// if (true) { -// common_hal_socketpool_socketpool_raise_gaierror_noname(); -// } -// // memcpy(addr, result_i->ai_addr, sizeof(struct sockaddr_storage)); -// // lwip_freeaddrinfo(result_i); -// } - -// static void resolve_host_or_throw(socketpool_socket_obj_t *self, const char *hostname, struct sockaddr_storage *addr, int port) { -// socketpool_resolve_host_or_throw(self->family, self->type, hostname, addr, port); -// } - -// StackType_t socket_select_stack[2 * configMINIMAL_STACK_SIZE]; - -// /* Socket state table: -// * 0 := Closed (unused) -// * 1 := Open -// * 2 := Closing (remove from rfds) -// * Index into socket_fd_state is calculated from actual lwip fd. idx := fd - LWIP_SOCKET_OFFSET -// */ -// #define FDSTATE_CLOSED 0 -// #define FDSTATE_OPEN 1 -// #define FDSTATE_CLOSING 2 -// static uint8_t socket_fd_state[CONFIG_LWIP_MAX_SOCKETS]; +#include +#include +#include +#include +#include -// How long to wait between checks for a socket to connect. -#define SOCKET_CONNECT_POLL_INTERVAL_MS 100 +#include +#include -// static socketpool_socket_obj_t *user_socket[CONFIG_LWIP_MAX_SOCKETS]; -// StaticTask_t socket_select_task_buffer; -// TaskHandle_t socket_select_task_handle; -// static int socket_change_fd = -1; - -// static void socket_select_task(void *arg) { -// uint64_t signal; -// fd_set readfds; -// fd_set excptfds; - -// while (true) { -// FD_ZERO(&readfds); -// FD_ZERO(&excptfds); -// FD_SET(socket_change_fd, &readfds); -// int max_fd = socket_change_fd; -// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { -// if ((socket_fd_state[i] == FDSTATE_OPEN) && (user_socket[i] == NULL)) { -// int sockfd = i + LWIP_SOCKET_OFFSET; -// max_fd = MAX(max_fd, sockfd); -// FD_SET(sockfd, &readfds); -// FD_SET(sockfd, &excptfds); -// } -// } - -// int num_triggered = select(max_fd + 1, &readfds, NULL, &excptfds, NULL); -// // Hard error (or someone closed a socket on another thread) -// if (num_triggered == -1) { -// assert(errno == EBADF); -// continue; -// } - -// assert(num_triggered > 0); - -// // Notice event trigger -// if (FD_ISSET(socket_change_fd, &readfds)) { -// read(socket_change_fd, &signal, sizeof(signal)); -// num_triggered--; -// } - -// // Handle active FDs, close the dead ones -// for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { -// int sockfd = i + LWIP_SOCKET_OFFSET; -// if (socket_fd_state[i] != FDSTATE_CLOSED) { -// if (FD_ISSET(sockfd, &readfds) || FD_ISSET(sockfd, &excptfds)) { -// if (socket_fd_state[i] == FDSTATE_CLOSING) { -// socket_fd_state[i] = FDSTATE_CLOSED; -// num_triggered--; -// } -// } -// } -// } - -// if (num_triggered > 0) { -// // Wake up CircuitPython by queuing request -// supervisor_workflow_request_background(); -// ulTaskNotifyTake(pdTRUE, portMAX_DELAY); -// } -// } - -// close(socket_change_fd); -// socket_change_fd = -1; -// vTaskDelete(NULL); -// } +#define SOCKETPOOL_IP_STR_LEN 48 -void socket_user_reset(void) { - // if (socket_change_fd < 0) { - // esp_vfs_eventfd_config_t config = ESP_VFS_EVENTD_CONFIG_DEFAULT(); - // ESP_ERROR_CHECK(esp_vfs_eventfd_register(&config)); - - // // Clear initial socket states - // for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { - // socket_fd_state[i] = FDSTATE_CLOSED; - // user_socket[i] = NULL; - // } - // socket_change_fd = eventfd(0, 0); - // // Run this at the same priority as CP so that the web workflow background task can be - // // queued while CP is running. Both tasks can still sleep and, therefore, sleep overall. - // socket_select_task_handle = xTaskCreateStaticPinnedToCore(socket_select_task, - // "socket_select", - // 2 * configMINIMAL_STACK_SIZE, - // NULL, - // uxTaskPriorityGet(NULL), - // socket_select_stack, - // &socket_select_task_buffer, - // xPortGetCoreID()); - // } else { - // // Not init - close open user sockets - // for (size_t i = 0; i < MP_ARRAY_SIZE(socket_fd_state); i++) { - // if ((socket_fd_state[i] == FDSTATE_OPEN) && user_socket[i]) { - // common_hal_socketpool_socket_close(user_socket[i]); - // } - // } - // } +static mp_obj_t _format_address(const struct sockaddr *addr, int family) { + char ip_str[SOCKETPOOL_IP_STR_LEN]; + const struct sockaddr_in *a = (void *)addr; + + switch (family) { + #if CIRCUITPY_SOCKETPOOL_IPV6 + case AF_INET6: + zsock_inet_ntop(family, &((const struct sockaddr_in6 *)a)->sin6_addr, ip_str, sizeof(ip_str)); + break; + #endif + default: + case AF_INET: + zsock_inet_ntop(family, &((const struct sockaddr_in *)a)->sin_addr, ip_str, sizeof(ip_str)); + break; + } + return mp_obj_new_str(ip_str, strlen(ip_str)); } -// Unblock select task (ok if not blocked yet) -void socketpool_socket_poll_resume(void) { - // if (socket_select_task_handle) { - // xTaskNotifyGive(socket_select_task_handle); - // } +static mp_obj_t _sockaddr_to_tuple(const struct sockaddr_storage *sockaddr) { + mp_obj_t args[4] = { + _format_address((const struct sockaddr *)sockaddr, sockaddr->ss_family), + }; + int n = 2; + #if CIRCUITPY_SOCKETPOOL_IPV6 + if (sockaddr->ss_family == AF_INET6) { + const struct sockaddr_in6 *addr6 = (const void *)sockaddr; + args[1] = MP_OBJ_NEW_SMALL_INT(ntohs(addr6->sin6_port)); + args[2] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_flowinfo); + args[3] = MP_OBJ_NEW_SMALL_INT(addr6->sin6_scope_id); + n = 4; + } else + #endif + { + const struct sockaddr_in *addr = (const void *)sockaddr; + args[1] = MP_OBJ_NEW_SMALL_INT(ntohs(addr->sin_port)); + } + return mp_obj_new_tuple(n, args); +} + +static void socketpool_resolve_host_or_throw(int family, int type, const char *hostname, struct sockaddr_storage *addr, int port) { + const struct zsock_addrinfo hints = { + .ai_family = family, + .ai_socktype = type, + }; + struct zsock_addrinfo *result_i = NULL; + char service_buf[6]; + + snprintf(service_buf, sizeof(service_buf), "%d", port); + + int error = zsock_getaddrinfo(hostname, service_buf, &hints, &result_i); + if (error != 0 || result_i == NULL) { + common_hal_socketpool_socketpool_raise_gaierror_noname(); + } + + memcpy(addr, result_i->ai_addr, sizeof(struct sockaddr_storage)); + zsock_freeaddrinfo(result_i); } -// The writes below send an event to the socket select task so that it redoes the -// select with the new open socket set. +static void resolve_host_or_throw(socketpool_socket_obj_t *self, const char *hostname, struct sockaddr_storage *addr, int port) { + socketpool_resolve_host_or_throw(self->family, self->type, hostname, addr, port); +} -// static bool register_open_socket(int fd) { -// if (fd < FD_SETSIZE) { -// socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN; -// user_socket[fd - LWIP_SOCKET_OFFSET] = NULL; +// How long to wait between checks for a socket to connect. +#define SOCKET_CONNECT_POLL_INTERVAL_MS 100 -// uint64_t signal = 1; -// write(socket_change_fd, &signal, sizeof(signal)); -// socketpool_socket_poll_resume(); -// return true; -// } -// return false; -// } +void socket_user_reset(void) { + // User sockets are heap objects with __del__ bound to close(). + // During VM shutdown/reset, gc_sweep_all() runs finalizers, so sockets + // are closed there rather than being tracked and closed explicitly here. +} + +static struct k_work_delayable socketpool_poll_work; +static bool socketpool_poll_work_initialized; -// static void mark_user_socket(int fd, socketpool_socket_obj_t *obj) { -// socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_OPEN; -// user_socket[fd - LWIP_SOCKET_OFFSET] = obj; -// // No need to wakeup select task -// } +static void socketpool_poll_work_handler(struct k_work *work) { + (void)work; + supervisor_workflow_request_background(); +} + +// Unblock select task (ok if not blocked yet) +void socketpool_socket_poll_resume(void) { + if (!socketpool_poll_work_initialized) { + k_work_init_delayable(&socketpool_poll_work, socketpool_poll_work_handler); + socketpool_poll_work_initialized = true; + } + k_work_schedule(&socketpool_poll_work, K_MSEC(10)); +} static bool _socketpool_socket(socketpool_socketpool_obj_t *self, socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, @@ -212,16 +151,23 @@ static bool _socketpool_socket(socketpool_socketpool_obj_t *self, sock->pool = self; sock->timeout_ms = (uint)-1; - // Create LWIP socket - // int socknum = -1; - // socknum = lwip_socket(sock->family, sock->type, sock->ipproto); - // if (socknum < 0) { - // return false; - // } + int socknum = zsock_socket(sock->family, sock->type, sock->ipproto); + if (socknum < 0) { + return false; + } - // sock->num = socknum; - // // Sockets should be nonblocking in most cases - // lwip_fcntl(socknum, F_SETFL, O_NONBLOCK); + sock->num = socknum; + + // Enable address reuse by default to avoid bind failures on recently-used ports. + int reuseaddr = 1; + if (zsock_setsockopt(socknum, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)) < 0) { + // Ignore if SO_REUSEADDR is unsupported. + } + + // Sockets should be nonblocking in most cases. + if (zsock_fcntl(socknum, F_SETFL, O_NONBLOCK) < 0) { + // Ignore if non-blocking is unsupported. + } return true; } @@ -235,11 +181,6 @@ bool socketpool_socket(socketpool_socketpool_obj_t *self, return false; } - // This shouldn't happen since we have room for the same number of sockets as LWIP. - // if (!register_open_socket(sock->num)) { - // lwip_close(sock->num); - // return false; - // } return true; } @@ -260,13 +201,12 @@ socketpool_socket_obj_t *common_hal_socketpool_socket(socketpool_socketpool_obj_ if (!_socketpool_socket(self, family, type, proto, sock)) { mp_raise_RuntimeError(MP_ERROR_TEXT("Out of sockets")); } - // mark_user_socket(sock->num, sock); return sock; } int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, socketpool_socket_obj_t *accepted) { struct sockaddr_storage peer_addr; - // socklen_t socklen = sizeof(peer_addr); + socklen_t socklen = sizeof(peer_addr); int newsoc = -1; bool timed_out = false; uint64_t start_ticks = supervisor_ticks_ms64(); @@ -277,11 +217,23 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; } RUN_BACKGROUND_TASKS; - // newsoc = lwip_accept(self->num, (struct sockaddr *)&peer_addr, &socklen); + #if CIRCUITPY_HOSTNETWORK + if (self->timeout_ms == 0) { + struct zsock_timeval tv = { + .tv_sec = 0, + .tv_usec = 1000, + }; + zsock_setsockopt(self->num, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + } + #endif + newsoc = zsock_accept(self->num, (struct sockaddr *)&peer_addr, &socklen); // In non-blocking mode, fail instead of timing out if (newsoc == -1 && (self->timeout_ms == 0 || mp_hal_is_interrupted())) { return -MP_EAGAIN; } + if (newsoc == -1 && errno != EAGAIN && errno != EWOULDBLOCK) { + return -errno; + } } if (timed_out) { @@ -293,18 +245,14 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, } // We got a socket. New client socket will not be non-blocking by default, so make it non-blocking. - // lwip_fcntl(newsoc, F_SETFL, O_NONBLOCK); + if (zsock_fcntl(newsoc, F_SETFL, O_NONBLOCK) < 0) { + // Ignore if non-blocking is unsupported. + } if (accepted != NULL) { // Error if called with open socket object. assert(common_hal_socketpool_socket_get_closed(accepted)); - // Register if system socket - // if (!register_open_socket(newsoc)) { - // lwip_close(newsoc); - // return -MP_EBADF; - // } - // Replace the old accepted socket with the new one. accepted->num = newsoc; accepted->pool = self->pool; @@ -313,7 +261,7 @@ int socketpool_socket_accept(socketpool_socket_obj_t *self, mp_obj_t *peer_out, } if (peer_out) { - *peer_out = sockaddr_to_tuple(&peer_addr); + *peer_out = _sockaddr_to_tuple(&peer_addr); } return newsoc; @@ -327,7 +275,6 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o if (newsoc > 0) { // Create the socket - // mark_user_socket(newsoc, sock); sock->base.type = &socketpool_socket_type; sock->num = newsoc; sock->pool = self->pool; @@ -343,40 +290,42 @@ socketpool_socket_obj_t *common_hal_socketpool_socket_accept(socketpool_socket_o size_t common_hal_socketpool_socket_bind(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port) { - // struct sockaddr_storage bind_addr; + struct sockaddr_storage bind_addr; const char *broadcast = ""; + uint32_t local_port = port; - // bind_addr.ss_family = self->family; + memset(&bind_addr, 0, sizeof(bind_addr)); + bind_addr.ss_family = self->family; #if CIRCUITPY_SOCKETPOOL_IPV6 if (self->family == AF_INET6) { struct sockaddr_in6 *addr6 = (void *)&bind_addr; - addr6->sin6_port = htons(port); + addr6->sin6_port = htons(local_port); // no ipv6 broadcast if (hostlen == 0) { memset(&addr6->sin6_addr, 0, sizeof(addr6->sin6_addr)); } else { - socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port); + socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, local_port); } } else #endif { - // struct sockaddr_in *addr4 = (void *)&bind_addr; - // addr4->sin_port = htons(port); + struct sockaddr_in *addr4 = (void *)&bind_addr; + addr4->sin_port = htons(local_port); if (hostlen == 0) { - // addr4->sin_addr.s_addr = IPADDR_ANY; + addr4->sin_addr.s_addr = htonl(INADDR_ANY); } else if (hostlen == strlen(broadcast) && memcmp(host, broadcast, strlen(broadcast)) == 0) { - // addr4->sin_addr.s_addr = IPADDR_BROADCAST; + addr4->sin_addr.s_addr = htonl(INADDR_BROADCAST); } else { - // socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, port); + socketpool_resolve_host_or_throw(self->family, self->type, host, &bind_addr, local_port); } } - // int result = lwip_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); - // if (result == 0) { - // return 0; - // } + int result = zsock_bind(self->num, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); + if (result == 0) { + return 0; + } return errno; } @@ -390,20 +339,11 @@ void socketpool_socket_close(socketpool_socket_obj_t *self) { } #endif self->connected = false; - // int fd = self->num; - // Ignore bogus/closed sockets - // if (fd >= LWIP_SOCKET_OFFSET) { - // if (user_socket[fd - LWIP_SOCKET_OFFSET] == NULL) { - // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSING; - // lwip_shutdown(fd, SHUT_RDWR); - // lwip_close(fd); - // } else { - // lwip_shutdown(fd, SHUT_RDWR); - // lwip_close(fd); - // socket_fd_state[fd - LWIP_SOCKET_OFFSET] = FDSTATE_CLOSED; - // user_socket[fd - LWIP_SOCKET_OFFSET] = NULL; - // } - // } + int fd = self->num; + if (fd >= 0) { + zsock_shutdown(fd, ZSOCK_SHUT_RDWR); + zsock_close(fd); + } self->num = -1; } @@ -413,16 +353,11 @@ void common_hal_socketpool_socket_close(socketpool_socket_obj_t *self) { void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port) { - // struct sockaddr_storage addr; - // resolve_host_or_throw(self, host, &addr, port); - - // Replace above with function call ----- - - // Emulate SO_CONTIMEO, which is not implemented by lwip. - // All our sockets are non-blocking, so we check the timeout ourselves. + (void)hostlen; + struct sockaddr_storage addr; + resolve_host_or_throw(self, host, &addr, port); - int result = -1; - // result = lwip_connect(self->num, (struct sockaddr *)&addr, addr.s2_len); + int result = zsock_connect(self->num, (struct sockaddr *)&addr, sizeof(addr)); if (result == 0) { // Connected immediately. @@ -436,12 +371,7 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, return; } - // struct timeval timeout = { - // .tv_sec = 0, - // .tv_usec = SOCKET_CONNECT_POLL_INTERVAL_MS * 1000, - // }; - - // Keep checking, using select(), until timeout expires, at short intervals. + // Keep checking, using poll(), until timeout expires, at short intervals. // This allows ctrl-C interrupts to be detected and background tasks to run. mp_uint_t timeout_left = self->timeout_ms; @@ -452,14 +382,22 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, return; } - // fd_set fds; - // FD_ZERO(&fds); - // FD_SET(self->num, &fds); + struct zsock_pollfd fd = { + .fd = self->num, + .events = ZSOCK_POLLOUT, + }; + int poll_timeout = SOCKET_CONNECT_POLL_INTERVAL_MS; + if (self->timeout_ms == (uint)-1) { + poll_timeout = -1; + } else if (timeout_left < SOCKET_CONNECT_POLL_INTERVAL_MS) { + poll_timeout = timeout_left; + } - // result = select(self->num + 1, NULL, &fds, NULL, &timeout); + result = zsock_poll(&fd, 1, poll_timeout); if (result == 0) { - // No change to fd's after waiting for timeout, so try again if some time is still left. - // Don't wrap below 0, because we're using a uint. + if (self->timeout_ms == (uint)-1) { + continue; + } if (timeout_left < SOCKET_CONNECT_POLL_INTERVAL_MS) { timeout_left = 0; } else { @@ -469,16 +407,14 @@ void common_hal_socketpool_socket_connect(socketpool_socket_obj_t *self, } if (result < 0) { - // Some error happened when doing select(); error is in errno. mp_raise_OSError(errno); } - // select() indicated the socket is writable. Check if any connection error occurred. int error_code = 0; - // socklen_t socklen = sizeof(error_code); - // result = getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen); + socklen_t socklen = sizeof(error_code); + result = zsock_getsockopt(self->num, SOL_SOCKET, SO_ERROR, &error_code, &socklen); if (result < 0 || error_code != 0) { - mp_raise_OSError(errno); + mp_raise_OSError(error_code != 0 ? error_code : errno); } self->connected = true; return; @@ -498,17 +434,15 @@ bool common_hal_socketpool_socket_get_connected(socketpool_socket_obj_t *self) { } bool common_hal_socketpool_socket_listen(socketpool_socket_obj_t *self, int backlog) { - // return lwip_listen(self->num, backlog) == 0; - return false; + return zsock_listen(self->num, backlog) == 0; } mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *self, uint8_t *buf, uint32_t len, mp_obj_t *source_out) { - // struct sockaddr_storage source_addr; - // socklen_t socklen = sizeof(source_addr); + struct sockaddr_storage source_addr; + socklen_t socklen = sizeof(source_addr); - // LWIP Socket uint64_t start_ticks = supervisor_ticks_ms64(); int received = -1; bool timed_out = false; @@ -519,11 +453,18 @@ mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *se timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; } RUN_BACKGROUND_TASKS; - // received = lwip_recvfrom(self->num, buf, len, 0, (struct sockaddr *)&source_addr, &socklen); + received = zsock_recvfrom(self->num, buf, len, ZSOCK_MSG_DONTWAIT, (struct sockaddr *)&source_addr, &socklen); - // In non-blocking mode, fail instead of looping - if (received == -1 && self->timeout_ms == 0) { - mp_raise_OSError(MP_EAGAIN); + if (received < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + mp_raise_OSError(errno); + } + + if (received == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + // In non-blocking mode, fail instead of looping + if (self->timeout_ms == 0) { + mp_raise_OSError(MP_EAGAIN); + } + continue; } } @@ -537,7 +478,7 @@ mp_uint_t common_hal_socketpool_socket_recvfrom_into(socketpool_socket_obj_t *se } if (source_out) { - // *source_out = sockaddr_to_tuple(&source_addr); + *source_out = _sockaddr_to_tuple(&source_addr); } return received; @@ -549,7 +490,6 @@ int socketpool_socket_recv_into(socketpool_socket_obj_t *self, bool timed_out = false; if (self->num != -1) { - // LWIP Socket uint64_t start_ticks = supervisor_ticks_ms64(); received = -1; while (received == -1 && @@ -558,7 +498,16 @@ int socketpool_socket_recv_into(socketpool_socket_obj_t *self, timed_out = supervisor_ticks_ms64() - start_ticks >= self->timeout_ms; } RUN_BACKGROUND_TASKS; - // received = lwip_recv(self->num, (void *)buf, len, 0); + received = zsock_recv(self->num, (void *)buf, len, ZSOCK_MSG_DONTWAIT); + if (received < 0 && errno != EAGAIN && errno != EWOULDBLOCK) { + return -errno; + } + if (received < 1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + if (self->timeout_ms == 0) { + return -MP_EAGAIN; + } + continue; + } // In non-blocking mode, fail instead of looping if (received < 1 && self->timeout_ms == 0) { if ((received == 0) || (errno == ENOTCONN)) { @@ -597,9 +546,7 @@ mp_uint_t common_hal_socketpool_socket_recv_into(socketpool_socket_obj_t *self, int socketpool_socket_send(socketpool_socket_obj_t *self, const uint8_t *buf, uint32_t len) { int sent = -1; if (self->num != -1) { - // LWIP Socket - // TODO: deal with potential failure/add timeout? - // sent = lwip_send(self->num, buf, len, 0); + sent = zsock_send(self->num, buf, len, 0); } else { sent = -MP_EBADF; } @@ -626,15 +573,15 @@ mp_uint_t common_hal_socketpool_socket_send(socketpool_socket_obj_t *self, const mp_uint_t common_hal_socketpool_socket_sendto(socketpool_socket_obj_t *self, const char *host, size_t hostlen, uint32_t port, const uint8_t *buf, uint32_t len) { - // struct sockaddr_storage addr; - // resolve_host_or_throw(self, host, &addr, port); + (void)hostlen; + struct sockaddr_storage addr; + resolve_host_or_throw(self, host, &addr, port); - // int bytes_sent = lwip_sendto(self->num, buf, len, 0, (struct sockaddr *)&addr, addr.s2_len); - // if (bytes_sent < 0) { - // mp_raise_BrokenPipeError(); - // return 0; - // } - int bytes_sent = 0; + int bytes_sent = zsock_sendto(self->num, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)); + if (bytes_sent < 0) { + mp_raise_BrokenPipeError(); + return 0; + } return bytes_sent; } @@ -648,7 +595,44 @@ mp_int_t common_hal_socketpool_socket_get_type(socketpool_socket_obj_t *self) { int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int level, int optname, const void *value, size_t optlen) { - int err = 0; // lwip_setsockopt(self->num, level, optname, value, optlen); + int zephyr_level = level; + int zephyr_optname = optname; + + switch (level) { + case SOCKETPOOL_SOL_SOCKET: + zephyr_level = SOL_SOCKET; + break; + case SOCKETPOOL_IPPROTO_IP: + zephyr_level = IPPROTO_IP; + break; + case SOCKETPOOL_IPPROTO_TCP: + zephyr_level = IPPROTO_TCP; + break; + case SOCKETPOOL_IPPROTO_UDP: + zephyr_level = IPPROTO_UDP; + break; + #if CIRCUITPY_SOCKETPOOL_IPV6 + case SOCKETPOOL_IPPROTO_IPV6: + zephyr_level = IPPROTO_IPV6; + break; + #endif + } + + if (zephyr_level == SOL_SOCKET) { + switch (optname) { + case SOCKETPOOL_SO_REUSEADDR: + zephyr_optname = SO_REUSEADDR; + break; + } + } else if (zephyr_level == IPPROTO_TCP) { + switch (optname) { + case SOCKETPOOL_TCP_NODELAY: + zephyr_optname = TCP_NODELAY; + break; + } + } + + int err = zsock_setsockopt(self->num, zephyr_level, zephyr_optname, value, optlen); if (err != 0) { return -errno; } @@ -656,29 +640,23 @@ int common_hal_socketpool_socket_setsockopt(socketpool_socket_obj_t *self, int l } bool common_hal_socketpool_readable(socketpool_socket_obj_t *self) { - // struct timeval immediate = {0, 0}; + struct zsock_pollfd fd = { + .fd = self->num, + .events = ZSOCK_POLLIN, + }; - // fd_set fds; - // FD_ZERO(&fds); - // FD_SET(self->num, &fds); - // int num_triggered = select(self->num + 1, &fds, NULL, &fds, &immediate); - - // including returning true in the error case - // return num_triggered != 0; - return false; + int num_triggered = zsock_poll(&fd, 1, 0); + return num_triggered > 0; } bool common_hal_socketpool_writable(socketpool_socket_obj_t *self) { - // struct timeval immediate = {0, 0}; - - // fd_set fds; - // FD_ZERO(&fds); - // FD_SET(self->num, &fds); - // int num_triggered = select(self->num + 1, NULL, &fds, &fds, &immediate); + struct zsock_pollfd fd = { + .fd = self->num, + .events = ZSOCK_POLLOUT, + }; - // including returning true in the error case - // return num_triggered != 0; - return false; + int num_triggered = zsock_poll(&fd, 1, 0); + return num_triggered > 0; } void socketpool_socket_move(socketpool_socket_obj_t *self, socketpool_socket_obj_t *sock) { diff --git a/ports/zephyr-cp/common-hal/socketpool/SocketPool.c b/ports/zephyr-cp/common-hal/socketpool/SocketPool.c index 4531c5bf1b7fa..c9a3f008321a1 100644 --- a/ports/zephyr-cp/common-hal/socketpool/SocketPool.c +++ b/ports/zephyr-cp/common-hal/socketpool/SocketPool.c @@ -8,114 +8,116 @@ #include "common-hal/socketpool/Socket.h" #include "py/runtime.h" +#if CIRCUITPY_HOSTNETWORK +#include "bindings/hostnetwork/__init__.h" +#endif +#if CIRCUITPY_WIFI #include "shared-bindings/wifi/__init__.h" +#endif #include "common-hal/socketpool/__init__.h" +#include +#include + +#include + void common_hal_socketpool_socketpool_construct(socketpool_socketpool_obj_t *self, mp_obj_t radio) { - if (radio != MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj)) { - mp_raise_ValueError(MP_ERROR_TEXT("SocketPool can only be used with wifi.radio")); + bool is_wifi = false; + #if CIRCUITPY_WIFI + is_wifi = radio == MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj); + #endif + bool is_hostnetwork = false; + #if CIRCUITPY_HOSTNETWORK + is_hostnetwork = mp_obj_is_type(radio, &hostnetwork_hostnetwork_type); + #endif + if (!(is_wifi || is_hostnetwork)) { + mp_raise_ValueError(MP_ERROR_TEXT("SocketPool can only be used with wifi.radio or hostnetwork.HostNetwork")); } } // common_hal_socketpool_socket is in socketpool/Socket.c to centralize open socket tracking. -// int socketpool_getaddrinfo_common(const char *host, int service, const struct addrinfo *hints, struct addrinfo **res) { -// // As of 2022, the version of lwip in esp-idf does not handle the -// // trailing-dot syntax of domain names, so emulate it. -// // Remove this once https://github.com/espressif/esp-idf/issues/10013 has -// // been implemented -// if (host) { -// size_t strlen_host = strlen(host); -// if (strlen_host && host[strlen_host - 1] == '.') { -// mp_obj_t nodot = mp_obj_new_str(host, strlen_host - 1); -// host = mp_obj_str_get_str(nodot); -// } -// } - -// // char service_buf[6]; -// // snprintf(service_buf, sizeof(service_buf), "%d", service); - -// // return lwip_getaddrinfo(host, service_buf, hints, res); -// return 0; -// } - -// static mp_obj_t format_address(const struct sockaddr *addr, int family) { -// char ip_str[IPADDR_STRLEN_MAX]; // big enough for any supported address type -// const struct sockaddr_in *a = (void *)addr; - -// switch (family) { -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// case AF_INET6: -// inet_ntop(family, &((const struct sockaddr_in6 *)a)->sin6_addr, ip_str, sizeof(ip_str)); -// break; -// #endif -// default: -// case AF_INET: -// inet_ntop(family, &((const struct sockaddr_in *)a)->sin_addr, ip_str, sizeof(ip_str)); -// break; -// } -// return mp_obj_new_str(ip_str, strlen(ip_str)); -// } - -// static mp_obj_t convert_sockaddr(const struct addrinfo *ai, int port) { -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// mp_int_t n_tuple = ai->ai_family == AF_INET6 ? 4 : 2; -// #else -// mp_int_t n_tuple = 2; -// #endif -// mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_tuple, NULL)); -// result->items[0] = format_address(ai->ai_addr, ai->ai_family); -// result->items[1] = MP_OBJ_NEW_SMALL_INT(port); -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// if (ai->ai_family == AF_INET6) { -// const struct sockaddr_in6 *ai6 = (void *)ai->ai_addr; -// result->items[2] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_flowinfo); -// result->items[3] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_scope_id); -// } -// #endif -// return result; -// } - -// static mp_obj_t convert_addrinfo(const struct addrinfo *ai, int port) { -// MP_STATIC_ASSERT(AF_INET == SOCKETPOOL_AF_INET); -// #if CIRCUITPY_SOCKETPOOL_IPV6 -// MP_STATIC_ASSERT(AF_INET6 == SOCKETPOOL_AF_INET6); -// #endif -// // MP_STATIC_ASSERT(AF_UNSPEC == SOCKETPOOL_AF_UNSPEC); -// mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL)); -// result->items[0] = MP_OBJ_NEW_SMALL_INT(ai->ai_family); -// result->items[1] = MP_OBJ_NEW_SMALL_INT(ai->ai_socktype); -// result->items[2] = MP_OBJ_NEW_SMALL_INT(ai->ai_protocol); -// result->items[3] = ai->ai_canonname ? mp_obj_new_str(ai->ai_canonname, strlen(ai->ai_canonname)) : MP_OBJ_NEW_QSTR(MP_QSTR_); -// result->items[4] = convert_sockaddr(ai, port); -// return result; -// } +static int socketpool_getaddrinfo_common(const char *host, int service, const struct zsock_addrinfo *hints, struct zsock_addrinfo **res) { + char service_buf[6]; + snprintf(service_buf, sizeof(service_buf), "%d", service); + + return zsock_getaddrinfo(host, service_buf, hints, res); +} + +#define SOCKETPOOL_IP_STR_LEN 48 + +static mp_obj_t format_address(const struct sockaddr *addr, int family) { + char ip_str[SOCKETPOOL_IP_STR_LEN]; + const struct sockaddr_in *a = (void *)addr; + + switch (family) { + #if CIRCUITPY_SOCKETPOOL_IPV6 + case AF_INET6: + zsock_inet_ntop(family, &((const struct sockaddr_in6 *)a)->sin6_addr, ip_str, sizeof(ip_str)); + break; + #endif + default: + case AF_INET: + zsock_inet_ntop(family, &((const struct sockaddr_in *)a)->sin_addr, ip_str, sizeof(ip_str)); + break; + } + return mp_obj_new_str(ip_str, strlen(ip_str)); +} + +static mp_obj_t convert_sockaddr(const struct zsock_addrinfo *ai, int port) { + #if CIRCUITPY_SOCKETPOOL_IPV6 + mp_int_t n_tuple = ai->ai_family == AF_INET6 ? 4 : 2; + #else + mp_int_t n_tuple = 2; + #endif + mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(n_tuple, NULL)); + result->items[0] = format_address(ai->ai_addr, ai->ai_family); + result->items[1] = MP_OBJ_NEW_SMALL_INT(port); + #if CIRCUITPY_SOCKETPOOL_IPV6 + if (ai->ai_family == AF_INET6) { + const struct sockaddr_in6 *ai6 = (void *)ai->ai_addr; + result->items[2] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_flowinfo); + result->items[3] = MP_OBJ_NEW_SMALL_INT(ai6->sin6_scope_id); + } + #endif + return result; +} + +static mp_obj_t convert_addrinfo(const struct zsock_addrinfo *ai, int port) { + mp_obj_tuple_t *result = MP_OBJ_TO_PTR(mp_obj_new_tuple(5, NULL)); + result->items[0] = MP_OBJ_NEW_SMALL_INT(ai->ai_family); + result->items[1] = MP_OBJ_NEW_SMALL_INT(ai->ai_socktype); + result->items[2] = MP_OBJ_NEW_SMALL_INT(ai->ai_protocol); + result->items[3] = ai->ai_canonname ? mp_obj_new_str(ai->ai_canonname, strlen(ai->ai_canonname)) : MP_OBJ_NEW_QSTR(MP_QSTR_); + result->items[4] = convert_sockaddr(ai, port); + return result; +} mp_obj_t common_hal_socketpool_getaddrinfo_raise(socketpool_socketpool_obj_t *self, const char *host, int port, int family, int type, int proto, int flags) { - // const struct addrinfo hints = { - // .ai_flags = flags, - // .ai_family = family, - // .ai_protocol = proto, - // .ai_socktype = type, - // }; - - // struct addrinfo *res = NULL; - // int err = socketpool_getaddrinfo_common(host, port, &hints, &res); - if (true) { + const struct zsock_addrinfo hints = { + .ai_flags = flags, + .ai_family = family, + .ai_protocol = proto, + .ai_socktype = type, + }; + + struct zsock_addrinfo *res = NULL; + int err = socketpool_getaddrinfo_common(host, port, &hints, &res); + if (err != 0 || res == NULL) { common_hal_socketpool_socketpool_raise_gaierror_noname(); } nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { mp_obj_t result = mp_obj_new_list(0, NULL); - // for (struct addrinfo *ai = res; ai; ai = ai->ai_next) { - // mp_obj_list_append(result, convert_addrinfo(ai, port)); - // } + for (struct zsock_addrinfo *ai = res; ai; ai = ai->ai_next) { + mp_obj_list_append(result, convert_addrinfo(ai, port)); + } nlr_pop(); - // lwip_freeaddrinfo(res); + zsock_freeaddrinfo(res); return result; } else { - // lwip_freeaddrinfo(res); + zsock_freeaddrinfo(res); nlr_raise(MP_OBJ_FROM_PTR(nlr.ret_val)); } } diff --git a/ports/zephyr-cp/common-hal/socketpool/SocketPool.h b/ports/zephyr-cp/common-hal/socketpool/SocketPool.h index 64f91e01e1cf1..7891a8b8e4820 100644 --- a/ports/zephyr-cp/common-hal/socketpool/SocketPool.h +++ b/ports/zephyr-cp/common-hal/socketpool/SocketPool.h @@ -6,6 +6,9 @@ #pragma once +#include +#include + #include "py/obj.h" typedef struct { diff --git a/ports/zephyr-cp/common-hal/usb_cdc/Serial.c b/ports/zephyr-cp/common-hal/usb_cdc/Serial.c new file mode 100644 index 0000000000000..272a78b31419e --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_cdc/Serial.c @@ -0,0 +1,68 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared/runtime/interrupt_char.h" +#include "shared-bindings/usb_cdc/Serial.h" +#include "shared-bindings/busio/UART.h" +#include "supervisor/shared/tick.h" + +mp_obj_t common_hal_usb_cdc_serial_construct_from_device(usb_cdc_serial_obj_t *self, const struct device *uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer) { + common_hal_busio_uart_construct_from_device(self, uart_device, receiver_buffer_size, receiver_buffer); + self->base.type = &usb_cdc_serial_type; + return MP_OBJ_FROM_PTR(self); +} + +size_t common_hal_usb_cdc_serial_read(usb_cdc_serial_obj_t *self, uint8_t *data, size_t len, int *errcode) { + return common_hal_busio_uart_read(self, data, len, errcode); +} + +size_t common_hal_usb_cdc_serial_write(usb_cdc_serial_obj_t *self, const uint8_t *data, size_t len, int *errcode) { + return common_hal_busio_uart_write(self, data, len, errcode); +} + +uint32_t common_hal_usb_cdc_serial_get_in_waiting(usb_cdc_serial_obj_t *self) { + return common_hal_busio_uart_rx_characters_available(self); +} + +uint32_t common_hal_usb_cdc_serial_get_out_waiting(usb_cdc_serial_obj_t *self) { + // Return number of FIFO bytes currently occupied. + // return CFG_TUD_CDC_TX_BUFSIZE - tud_cdc_n_write_available(self->idx); + return 0; +} + +void common_hal_usb_cdc_serial_reset_input_buffer(usb_cdc_serial_obj_t *self) { + common_hal_busio_uart_clear_rx_buffer(self); +} + +uint32_t common_hal_usb_cdc_serial_reset_output_buffer(usb_cdc_serial_obj_t *self) { + // return tud_cdc_n_write_clear(self->idx); + return 0; +} + +uint32_t common_hal_usb_cdc_serial_flush(usb_cdc_serial_obj_t *self) { + // return tud_cdc_n_write_flush(self->idx); + return 0; +} + +bool common_hal_usb_cdc_serial_get_connected(usb_cdc_serial_obj_t *self) { + return !common_hal_busio_uart_deinited(self); +} + +mp_float_t common_hal_usb_cdc_serial_get_timeout(usb_cdc_serial_obj_t *self) { + return common_hal_busio_uart_get_timeout(self); +} + +void common_hal_usb_cdc_serial_set_timeout(usb_cdc_serial_obj_t *self, mp_float_t timeout) { + common_hal_busio_uart_set_timeout(self, timeout); +} + +mp_float_t common_hal_usb_cdc_serial_get_write_timeout(usb_cdc_serial_obj_t *self) { + return common_hal_busio_uart_get_write_timeout(self); +} + +void common_hal_usb_cdc_serial_set_write_timeout(usb_cdc_serial_obj_t *self, mp_float_t write_timeout) { + common_hal_busio_uart_set_write_timeout(self, write_timeout); +} diff --git a/ports/zephyr-cp/common-hal/usb_cdc/Serial.h b/ports/zephyr-cp/common-hal/usb_cdc/Serial.h new file mode 100644 index 0000000000000..9a110545a3700 --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_cdc/Serial.h @@ -0,0 +1,14 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-bindings/busio/UART.h" + +typedef busio_uart_obj_t usb_cdc_serial_obj_t; + +// Helper function for Zephyr-specific initialization from device tree +mp_obj_t common_hal_usb_cdc_serial_construct_from_device(usb_cdc_serial_obj_t *self, const struct device *uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer); diff --git a/ports/zephyr-cp/common-hal/usb_cdc/__init__.c b/ports/zephyr-cp/common-hal/usb_cdc/__init__.c new file mode 100644 index 0000000000000..7bcae39673320 --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_cdc/__init__.c @@ -0,0 +1,40 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/gc.h" +#include "py/obj.h" +#include "py/mphal.h" +#include "py/runtime.h" +#include "py/objtuple.h" +#include "shared-bindings/usb_cdc/__init__.h" +#include "shared-bindings/usb_cdc/Serial.h" +#include "supervisor/usb.h" + +static bool usb_cdc_console_is_enabled; +static bool usb_cdc_data_is_enabled; + +void usb_cdc_set_defaults(void) { + common_hal_usb_cdc_enable(true, + false); +} + +bool usb_cdc_console_enabled(void) { + return usb_cdc_console_is_enabled; +} + +bool usb_cdc_data_enabled(void) { + return usb_cdc_data_is_enabled; +} + +bool common_hal_usb_cdc_disable(void) { + return common_hal_usb_cdc_enable(false, false); +} + +bool common_hal_usb_cdc_enable(bool console, bool data) { + usb_cdc_console_is_enabled = console; + usb_cdc_data_is_enabled = data; + return true; +} diff --git a/ports/zephyr-cp/common-hal/usb_cdc/__init__.h b/ports/zephyr-cp/common-hal/usb_cdc/__init__.h new file mode 100644 index 0000000000000..daf06d580ad63 --- /dev/null +++ b/ports/zephyr-cp/common-hal/usb_cdc/__init__.h @@ -0,0 +1,16 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2021 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/mpconfig.h" +#include "py/objtuple.h" +#include "supervisor/usb.h" + +bool usb_cdc_console_enabled(void); +bool usb_cdc_data_enabled(void); + +void usb_cdc_set_defaults(void); diff --git a/ports/zephyr-cp/common-hal/wifi/Radio.c b/ports/zephyr-cp/common-hal/wifi/Radio.c index 726b406b3ca89..35a0b76a36268 100644 --- a/ports/zephyr-cp/common-hal/wifi/Radio.c +++ b/ports/zephyr-cp/common-hal/wifi/Radio.c @@ -86,13 +86,19 @@ void common_hal_wifi_radio_set_enabled(wifi_radio_obj_t *self, bool enabled) { // mdns_server_deinit_singleton(); // #endif printk("net_if_down\n"); - CHECK_ZEPHYR_RESULT(net_if_down(self->sta_netif)); + int res = net_if_down(self->sta_netif); + if (res < 0 && res != -EALREADY) { + raise_zephyr_error(res); + } self->started = false; return; } if (!self->started && enabled) { printk("net_if_up\n"); - CHECK_ZEPHYR_RESULT(net_if_up(self->sta_netif)); + int res = net_if_up(self->sta_netif); + if (res < 0 && res != -EALREADY) { + raise_zephyr_error(res); + } self->started = true; self->current_scan = NULL; // common_hal_wifi_radio_set_tx_power(self, CIRCUITPY_WIFI_DEFAULT_TX_POWER); diff --git a/ports/zephyr-cp/common-hal/wifi/__init__.c b/ports/zephyr-cp/common-hal/wifi/__init__.c index 57f073a9cbb08..f9e02be247600 100644 --- a/ports/zephyr-cp/common-hal/wifi/__init__.c +++ b/ports/zephyr-cp/common-hal/wifi/__init__.c @@ -29,6 +29,8 @@ wifi_radio_obj_t common_hal_wifi_radio_obj; #include #include +#include + #define MAC_ADDRESS_LENGTH 6 static void schedule_background_on_cp_core(void *arg) { @@ -44,20 +46,23 @@ static void schedule_background_on_cp_core(void *arg) { static struct net_mgmt_event_callback wifi_cb; static struct net_mgmt_event_callback ipv4_cb; -static void _event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_event, struct net_if *iface) { +static void _event_handler(struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface) { wifi_radio_obj_t *self = &common_hal_wifi_radio_obj; - printk("_event_handler cb %p event %08x if %p\n", cb, mgmt_event, iface); + (void)iface; switch (mgmt_event) { - case NET_EVENT_WIFI_SCAN_RESULT: + case NET_EVENT_WIFI_SCAN_RESULT: { printk("NET_EVENT_WIFI_SCAN_RESULT\n"); - struct wifi_scan_result *result = (struct wifi_scan_result *)cb->info; - if (self->current_scan != NULL) { + const struct wifi_scan_result *result = cb->info; + if (result != NULL && self->current_scan != NULL) { wifi_scannednetworks_scan_result(self->current_scan, result); } break; + } case NET_EVENT_WIFI_SCAN_DONE: - printk("NET_EVENT_WIFI_SCAN_DONE\n"); + printk("NET_EVENT_WIFI_SCAN_DONE (thread: %s prio=%d)\n", + k_thread_name_get(k_current_get()), + k_thread_priority_get(k_current_get())); if (self->current_scan != NULL) { k_poll_signal_raise(&self->current_scan->channel_done, 0); } @@ -101,6 +106,9 @@ static void _event_handler(struct net_mgmt_event_callback *cb, uint32_t mgmt_eve case NET_EVENT_WIFI_AP_STA_DISCONNECTED: printk("NET_EVENT_WIFI_AP_STA_DISCONNECTED\n"); break; + default: + printk("unhandled net event %x\n", mgmt_event); + break; } } @@ -281,6 +289,7 @@ void common_hal_wifi_init(bool user_initiated) { net_mgmt_add_event_callback(&wifi_cb); net_mgmt_add_event_callback(&ipv4_cb); + #if defined(CONFIG_NET_HOSTNAME) // Set the default hostname capped at NET_HOSTNAME_MAX_LEN characters. We trim off // the start of the board name (likely manufacturer) because the end is // often more unique to the board. @@ -298,9 +307,10 @@ void common_hal_wifi_init(bool user_initiated) { } snprintf(cpy_default_hostname, sizeof(cpy_default_hostname), "cpy-%s-%02x%02x%02x%02x%02x%02x", CIRCUITPY_BOARD_ID + board_trim, mac->addr[0], mac->addr[1], mac->addr[2], mac->addr[3], mac->addr[4], mac->addr[5]); - if (net_hostname_set(cpy_default_hostname, strlen(cpy_default_hostname)) != 0) { - printk("setting hostname failed\n"); - } + CHECK_ZEPHYR_RESULT(net_hostname_set(cpy_default_hostname, strlen(cpy_default_hostname))); + #else + printk("Hostname support disabled in Zephyr config\n"); + #endif // set station mode to avoid the default SoftAP common_hal_wifi_radio_start_station(self); // start wifi diff --git a/ports/zephyr-cp/common-hal/zephyr_display/Display.c b/ports/zephyr-cp/common-hal/zephyr_display/Display.c new file mode 100644 index 0000000000000..d1585b5f291d5 --- /dev/null +++ b/ports/zephyr-cp/common-hal/zephyr_display/Display.c @@ -0,0 +1,440 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "bindings/zephyr_display/Display.h" + +#include + +#include "bindings/zephyr_kernel/__init__.h" +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/time/__init__.h" +#include "shared-module/displayio/__init__.h" +#include "supervisor/shared/display.h" +#include "supervisor/shared/tick.h" + +static const displayio_area_t *zephyr_display_get_refresh_areas(zephyr_display_display_obj_t *self) { + if (self->core.full_refresh) { + self->core.area.next = NULL; + return &self->core.area; + } else if (self->core.current_group != NULL) { + return displayio_group_get_refresh_areas(self->core.current_group, NULL); + } + return NULL; +} + +static enum display_pixel_format zephyr_display_select_pixel_format(const struct display_capabilities *caps) { + uint32_t formats = caps->supported_pixel_formats; + + if (formats & PIXEL_FORMAT_RGB_565) { + return PIXEL_FORMAT_RGB_565; + } + if (formats & PIXEL_FORMAT_RGB_888) { + return PIXEL_FORMAT_RGB_888; + } + if (formats & PIXEL_FORMAT_ARGB_8888) { + return PIXEL_FORMAT_ARGB_8888; + } + if (formats & PIXEL_FORMAT_RGB_565X) { + return PIXEL_FORMAT_RGB_565X; + } + if (formats & PIXEL_FORMAT_L_8) { + return PIXEL_FORMAT_L_8; + } + if (formats & PIXEL_FORMAT_AL_88) { + return PIXEL_FORMAT_AL_88; + } + if (formats & PIXEL_FORMAT_MONO01) { + return PIXEL_FORMAT_MONO01; + } + if (formats & PIXEL_FORMAT_MONO10) { + return PIXEL_FORMAT_MONO10; + } + return caps->current_pixel_format; +} + +static void zephyr_display_select_colorspace(zephyr_display_display_obj_t *self, + uint16_t *color_depth, + uint8_t *bytes_per_cell, + bool *grayscale, + bool *pixels_in_byte_share_row, + bool *reverse_pixels_in_byte, + bool *reverse_bytes_in_word) { + *color_depth = 16; + *bytes_per_cell = 2; + *grayscale = false; + *pixels_in_byte_share_row = false; + *reverse_pixels_in_byte = false; + *reverse_bytes_in_word = false; + + if (self->pixel_format == PIXEL_FORMAT_RGB_565X) { + // RGB_565X is big-endian RGB_565, so byte-swap from native LE. + *reverse_bytes_in_word = true; + } else if (self->pixel_format == PIXEL_FORMAT_RGB_888) { + *color_depth = 24; + *bytes_per_cell = 3; + *reverse_bytes_in_word = false; + } else if (self->pixel_format == PIXEL_FORMAT_ARGB_8888) { + *color_depth = 32; + *bytes_per_cell = 4; + *reverse_bytes_in_word = false; + } else if (self->pixel_format == PIXEL_FORMAT_L_8 || + self->pixel_format == PIXEL_FORMAT_AL_88) { + *color_depth = 8; + *bytes_per_cell = 1; + *grayscale = true; + *reverse_bytes_in_word = false; + } else if (self->pixel_format == PIXEL_FORMAT_MONO01 || + self->pixel_format == PIXEL_FORMAT_MONO10) { + bool vtiled = self->capabilities.screen_info & SCREEN_INFO_MONO_VTILED; + bool msb_first = self->capabilities.screen_info & SCREEN_INFO_MONO_MSB_FIRST; + *color_depth = 1; + *bytes_per_cell = 1; + *grayscale = true; + *pixels_in_byte_share_row = !vtiled; + *reverse_pixels_in_byte = msb_first; + *reverse_bytes_in_word = false; + } +} + +void common_hal_zephyr_display_display_construct_from_device(zephyr_display_display_obj_t *self, + const struct device *device, + uint16_t rotation, + bool auto_refresh) { + self->auto_refresh = false; + + if (device == NULL || !device_is_ready(device)) { + raise_zephyr_error(-ENODEV); + } + + self->device = device; + display_get_capabilities(self->device, &self->capabilities); + + self->pixel_format = zephyr_display_select_pixel_format(&self->capabilities); + if (self->pixel_format != self->capabilities.current_pixel_format) { + (void)display_set_pixel_format(self->device, self->pixel_format); + display_get_capabilities(self->device, &self->capabilities); + self->pixel_format = self->capabilities.current_pixel_format; + } + + uint16_t color_depth; + uint8_t bytes_per_cell; + bool grayscale; + bool pixels_in_byte_share_row; + bool reverse_pixels_in_byte; + bool reverse_bytes_in_word; + zephyr_display_select_colorspace(self, &color_depth, &bytes_per_cell, + &grayscale, &pixels_in_byte_share_row, &reverse_pixels_in_byte, + &reverse_bytes_in_word); + + displayio_display_core_construct( + &self->core, + self->capabilities.x_resolution, + self->capabilities.y_resolution, + 0, + color_depth, + grayscale, + pixels_in_byte_share_row, + bytes_per_cell, + reverse_pixels_in_byte, + reverse_bytes_in_word); + + self->native_frames_per_second = 60; + self->native_ms_per_frame = 1000 / self->native_frames_per_second; + self->first_manual_refresh = !auto_refresh; + + if (rotation != 0) { + common_hal_zephyr_display_display_set_rotation(self, rotation); + } + + (void)display_blanking_off(self->device); + + displayio_display_core_set_root_group(&self->core, &circuitpython_splash); + common_hal_zephyr_display_display_set_auto_refresh(self, auto_refresh); +} + +uint16_t common_hal_zephyr_display_display_get_width(zephyr_display_display_obj_t *self) { + return displayio_display_core_get_width(&self->core); +} + +uint16_t common_hal_zephyr_display_display_get_height(zephyr_display_display_obj_t *self) { + return displayio_display_core_get_height(&self->core); +} + +mp_float_t common_hal_zephyr_display_display_get_brightness(zephyr_display_display_obj_t *self) { + (void)self; + return -1; +} + +bool common_hal_zephyr_display_display_set_brightness(zephyr_display_display_obj_t *self, mp_float_t brightness) { + (void)self; + (void)brightness; + return false; +} + +static bool zephyr_display_refresh_area(zephyr_display_display_obj_t *self, const displayio_area_t *area) { + uint16_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE / sizeof(uint32_t); + + displayio_area_t clipped; + if (!displayio_display_core_clip_area(&self->core, area, &clipped)) { + return true; + } + + uint16_t rows_per_buffer = displayio_area_height(&clipped); + uint8_t pixels_per_word = (sizeof(uint32_t) * 8) / self->core.colorspace.depth; + // For AL_88, displayio fills at 1 byte/pixel (L_8) but output needs 2 bytes/pixel, + // so halve the effective pixels_per_word for buffer sizing. + uint8_t effective_pixels_per_word = pixels_per_word; + if (self->pixel_format == PIXEL_FORMAT_AL_88) { + effective_pixels_per_word = sizeof(uint32_t) / 2; + } + uint16_t pixels_per_buffer = displayio_area_size(&clipped); + uint16_t subrectangles = 1; + + // When pixels_in_byte_share_row is false (mono vtiled), 8 vertical pixels + // pack into one column byte. The byte layout needs width * ceil(height/8) + // bytes, which can exceed the pixel-count-based buffer size. + bool vtiled = self->core.colorspace.depth < 8 && + !self->core.colorspace.pixels_in_byte_share_row; + + bool needs_subdivision = displayio_area_size(&clipped) > buffer_size * effective_pixels_per_word; + if (vtiled && !needs_subdivision) { + uint16_t width = displayio_area_width(&clipped); + uint16_t height = displayio_area_height(&clipped); + uint32_t vtiled_bytes = (uint32_t)width * ((height + 7) / 8); + needs_subdivision = vtiled_bytes > buffer_size * sizeof(uint32_t); + } + + if (needs_subdivision) { + rows_per_buffer = buffer_size * effective_pixels_per_word / displayio_area_width(&clipped); + if (vtiled) { + rows_per_buffer = (rows_per_buffer / 8) * 8; + if (rows_per_buffer == 0) { + rows_per_buffer = 8; + } + } + if (rows_per_buffer == 0) { + rows_per_buffer = 1; + } + subrectangles = displayio_area_height(&clipped) / rows_per_buffer; + if (displayio_area_height(&clipped) % rows_per_buffer != 0) { + subrectangles++; + } + pixels_per_buffer = rows_per_buffer * displayio_area_width(&clipped); + buffer_size = pixels_per_buffer / pixels_per_word; + if (pixels_per_buffer % pixels_per_word) { + buffer_size += 1; + } + // Ensure buffer is large enough for vtiled packing. + if (vtiled) { + uint16_t width = displayio_area_width(&clipped); + uint16_t vtiled_words = (width * ((rows_per_buffer + 7) / 8) + sizeof(uint32_t) - 1) / sizeof(uint32_t); + if (vtiled_words > buffer_size) { + buffer_size = vtiled_words; + } + } + // Ensure buffer is large enough for AL_88 expansion. + if (self->pixel_format == PIXEL_FORMAT_AL_88) { + uint16_t al88_words = (pixels_per_buffer * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t); + if (al88_words > buffer_size) { + buffer_size = al88_words; + } + } + } + + uint32_t buffer[buffer_size]; + uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t mask[mask_length]; + + uint16_t remaining_rows = displayio_area_height(&clipped); + + for (uint16_t j = 0; j < subrectangles; j++) { + displayio_area_t subrectangle = { + .x1 = clipped.x1, + .y1 = clipped.y1 + rows_per_buffer * j, + .x2 = clipped.x2, + .y2 = clipped.y1 + rows_per_buffer * (j + 1), + }; + + if (remaining_rows < rows_per_buffer) { + subrectangle.y2 = subrectangle.y1 + remaining_rows; + } + remaining_rows -= rows_per_buffer; + + memset(mask, 0, mask_length * sizeof(mask[0])); + memset(buffer, 0, buffer_size * sizeof(buffer[0])); + + displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); + + uint16_t width = displayio_area_width(&subrectangle); + uint16_t height = displayio_area_height(&subrectangle); + size_t pixel_count = (size_t)width * (size_t)height; + + if (self->pixel_format == PIXEL_FORMAT_MONO10) { + uint8_t *bytes = (uint8_t *)buffer; + size_t byte_count = (pixel_count + 7) / 8; + for (size_t i = 0; i < byte_count; i++) { + bytes[i] = ~bytes[i]; + } + } + + if (self->pixel_format == PIXEL_FORMAT_AL_88) { + uint8_t *bytes = (uint8_t *)buffer; + for (size_t i = pixel_count; i > 0; i--) { + bytes[(i - 1) * 2 + 1] = 0xFF; + bytes[(i - 1) * 2] = bytes[i - 1]; + } + } + + // Compute buf_size based on the Zephyr pixel format. + uint32_t buf_size_bytes; + if (self->pixel_format == PIXEL_FORMAT_MONO01 || + self->pixel_format == PIXEL_FORMAT_MONO10) { + buf_size_bytes = (pixel_count + 7) / 8; + } else if (self->pixel_format == PIXEL_FORMAT_AL_88) { + buf_size_bytes = pixel_count * 2; + } else { + buf_size_bytes = pixel_count * (self->core.colorspace.depth / 8); + } + + struct display_buffer_descriptor desc = { + .buf_size = buf_size_bytes, + .width = width, + .height = height, + .pitch = width, + .frame_incomplete = false, + }; + + int err = display_write(self->device, subrectangle.x1, subrectangle.y1, &desc, buffer); + if (err < 0) { + return false; + } + + RUN_BACKGROUND_TASKS; + } + + return true; +} + +static void zephyr_display_refresh(zephyr_display_display_obj_t *self) { + if (!displayio_display_core_start_refresh(&self->core)) { + return; + } + + const displayio_area_t *current_area = zephyr_display_get_refresh_areas(self); + while (current_area != NULL) { + if (!zephyr_display_refresh_area(self, current_area)) { + break; + } + current_area = current_area->next; + } + + displayio_display_core_finish_refresh(&self->core); +} + +void common_hal_zephyr_display_display_set_rotation(zephyr_display_display_obj_t *self, int rotation) { + bool transposed = (self->core.rotation == 90 || self->core.rotation == 270); + bool will_transposed = (rotation == 90 || rotation == 270); + if (transposed != will_transposed) { + int tmp = self->core.width; + self->core.width = self->core.height; + self->core.height = tmp; + } + + displayio_display_core_set_rotation(&self->core, rotation); + + if (self == &displays[0].zephyr_display) { + supervisor_stop_terminal(); + supervisor_start_terminal(self->core.width, self->core.height); + } + + if (self->core.current_group != NULL) { + displayio_group_update_transform(self->core.current_group, &self->core.transform); + } +} + +uint16_t common_hal_zephyr_display_display_get_rotation(zephyr_display_display_obj_t *self) { + return self->core.rotation; +} + +bool common_hal_zephyr_display_display_refresh(zephyr_display_display_obj_t *self, + uint32_t target_ms_per_frame, + uint32_t maximum_ms_per_real_frame) { + if (!self->auto_refresh && !self->first_manual_refresh && (target_ms_per_frame != NO_FPS_LIMIT)) { + uint64_t current_time = supervisor_ticks_ms64(); + uint32_t current_ms_since_real_refresh = current_time - self->core.last_refresh; + if (current_ms_since_real_refresh > maximum_ms_per_real_frame) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Below minimum frame rate")); + } + uint32_t current_ms_since_last_call = current_time - self->last_refresh_call; + self->last_refresh_call = current_time; + if (current_ms_since_last_call > target_ms_per_frame) { + return false; + } + uint32_t remaining_time = target_ms_per_frame - (current_ms_since_real_refresh % target_ms_per_frame); + while (supervisor_ticks_ms64() - self->last_refresh_call < remaining_time) { + RUN_BACKGROUND_TASKS; + } + } + self->first_manual_refresh = false; + zephyr_display_refresh(self); + return true; +} + +bool common_hal_zephyr_display_display_get_auto_refresh(zephyr_display_display_obj_t *self) { + return self->auto_refresh; +} + +void common_hal_zephyr_display_display_set_auto_refresh(zephyr_display_display_obj_t *self, bool auto_refresh) { + self->first_manual_refresh = !auto_refresh; + if (auto_refresh != self->auto_refresh) { + if (auto_refresh) { + supervisor_enable_tick(); + } else { + supervisor_disable_tick(); + } + } + self->auto_refresh = auto_refresh; +} + +void zephyr_display_display_background(zephyr_display_display_obj_t *self) { + if (self->auto_refresh && (supervisor_ticks_ms64() - self->core.last_refresh) > self->native_ms_per_frame) { + zephyr_display_refresh(self); + } +} + +void release_zephyr_display(zephyr_display_display_obj_t *self) { + common_hal_zephyr_display_display_set_auto_refresh(self, false); + release_display_core(&self->core); + self->device = NULL; + self->base.type = &mp_type_NoneType; +} + +void zephyr_display_display_collect_ptrs(zephyr_display_display_obj_t *self) { + (void)self; + displayio_display_core_collect_ptrs(&self->core); +} + +void zephyr_display_display_reset(zephyr_display_display_obj_t *self) { + if (self->device != NULL && device_is_ready(self->device)) { + common_hal_zephyr_display_display_set_auto_refresh(self, true); + displayio_display_core_set_root_group(&self->core, &circuitpython_splash); + self->core.full_refresh = true; + } else { + release_zephyr_display(self); + } +} + +mp_obj_t common_hal_zephyr_display_display_get_root_group(zephyr_display_display_obj_t *self) { + if (self->core.current_group == NULL) { + return mp_const_none; + } + return self->core.current_group; +} + +bool common_hal_zephyr_display_display_set_root_group(zephyr_display_display_obj_t *self, displayio_group_t *root_group) { + return displayio_display_core_set_root_group(&self->core, root_group); +} diff --git a/ports/zephyr-cp/common-hal/zephyr_display/Display.h b/ports/zephyr-cp/common-hal/zephyr_display/Display.h new file mode 100644 index 0000000000000..bdec1e96ba5f3 --- /dev/null +++ b/ports/zephyr-cp/common-hal/zephyr_display/Display.h @@ -0,0 +1,31 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" +#include "shared-module/displayio/display_core.h" + +typedef struct { + mp_obj_base_t base; + displayio_display_core_t core; + const struct device *device; + struct display_capabilities capabilities; + enum display_pixel_format pixel_format; + uint64_t last_refresh_call; + uint16_t native_frames_per_second; + uint16_t native_ms_per_frame; + bool auto_refresh; + bool first_manual_refresh; +} zephyr_display_display_obj_t; + +void zephyr_display_display_background(zephyr_display_display_obj_t *self); +void zephyr_display_display_collect_ptrs(zephyr_display_display_obj_t *self); +void zephyr_display_display_reset(zephyr_display_display_obj_t *self); +void release_zephyr_display(zephyr_display_display_obj_t *self); diff --git a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c b/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c deleted file mode 100644 index d5bb0d4466912..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.c +++ /dev/null @@ -1,92 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries -// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec -// SPDX-FileCopyrightText: Copyright (c) 2017 hathach -// SPDX-FileCopyrightText: Copyright (c) 2016 Sandeep Mistry All right reserved. -// -// SPDX-License-Identifier: MIT - -#include "shared-bindings/busio/I2C.h" -#include "shared-bindings/microcontroller/__init__.h" -#include "shared-bindings/microcontroller/Pin.h" -#include "supervisor/shared/tick.h" -#include "py/mperrno.h" -#include "py/runtime.h" - - -void common_hal_busio_i2c_never_reset(busio_i2c_obj_t *self) { - // never_reset_pin_number(self->scl_pin_number); - // never_reset_pin_number(self->sda_pin_number); -} - -void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, const mcu_pin_obj_t *scl, const mcu_pin_obj_t *sda, uint32_t frequency, uint32_t timeout) { - -} - -bool common_hal_busio_i2c_deinited(busio_i2c_obj_t *self) { - // return self->sda_pin_number == NO_PIN; - return true; -} - -void common_hal_busio_i2c_deinit(busio_i2c_obj_t *self) { - if (common_hal_busio_i2c_deinited(self)) { - return; - } - - // nrfx_twim_uninit(&self->twim_peripheral->twim); - - // reset_pin_number(self->sda_pin_number); - // reset_pin_number(self->scl_pin_number); - - // self->twim_peripheral->in_use = false; - // common_hal_busio_i2c_mark_deinit(self); -} - -void common_hal_busio_i2c_mark_deinit(busio_i2c_obj_t *self) { - // self->sda_pin_number = NO_PIN; -} - -// nrfx_twim_tx doesn't support 0-length data so we fall back to the hal API -bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr) { - bool found = true; - - return found; -} - -bool common_hal_busio_i2c_try_lock(busio_i2c_obj_t *self) { - if (common_hal_busio_i2c_deinited(self)) { - return false; - } - bool grabbed_lock = false; - return grabbed_lock; -} - -bool common_hal_busio_i2c_has_lock(busio_i2c_obj_t *self) { - return self->has_lock; -} - -void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self) { - self->has_lock = false; -} - -uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len) { - return 0; -} - -uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { - if (len == 0) { - return 0; - } - -} - -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t addr, - uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len) { - uint8_t result = _common_hal_busio_i2c_write(self, addr, out_data, out_len, false); - if (result != 0) { - return result; - } - - return common_hal_busio_i2c_read(self, addr, in_data, in_len); -} diff --git a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h b/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h deleted file mode 100644 index 22e9251b3f11a..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_i2c/I2C.h +++ /dev/null @@ -1,17 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "py/obj.h" - -typedef struct { - mp_obj_base_t base; - // twim_peripheral_t *twim_peripheral; - bool has_lock; - uint8_t scl_pin_number; - uint8_t sda_pin_number; -} busio_i2c_obj_t; diff --git a/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c b/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c index 042f06b0ed217..178f33e028d73 100644 --- a/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c +++ b/ports/zephyr-cp/common-hal/zephyr_kernel/__init__.c @@ -6,11 +6,15 @@ #include "bindings/zephyr_kernel/__init__.h" #include "py/runtime.h" +#include #include - +#include void raise_zephyr_error(int err) { + if (err == 0) { + return; + } switch (-err) { case EALREADY: printk("EALREADY\n"); @@ -39,7 +43,20 @@ void raise_zephyr_error(int err) { case ENOTSUP: printk("ENOTSUP\n"); break; + case EADDRINUSE: + printk("EADDRINUSE\n"); + break; + case EIO: + printk("EIO\n"); + break; + case ENOSYS: + printk("ENOSYS\n"); + break; + case EINVAL: + printk("EINVAL\n"); + break; default: printk("Zephyr error %d\n", err); } + mp_raise_OSError(-err); } diff --git a/ports/zephyr-cp/common-hal/zephyr_serial/UART.c b/ports/zephyr-cp/common-hal/zephyr_serial/UART.c deleted file mode 100644 index fcc05c22f1aad..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_serial/UART.c +++ /dev/null @@ -1,130 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include "shared-bindings/microcontroller/__init__.h" -#include "bindings/zephyr_serial/UART.h" - -#include "shared/runtime/interrupt_char.h" -#include "py/mpconfig.h" -#include "py/gc.h" -#include "py/mperrno.h" -#include "py/runtime.h" -#include "py/stream.h" - -#include -#include - -#include - -/* - * Read characters from UART until line end is detected. Afterwards push the - * data to the message queue. - */ -static void serial_cb(const struct device *dev, void *user_data) { - zephyr_serial_uart_obj_t *self = (zephyr_serial_uart_obj_t *)user_data; - - uint8_t c; - - if (!uart_irq_update(dev)) { - return; - } - - if (!uart_irq_rx_ready(dev)) { - return; - } - - /* read until FIFO empty */ - while (uart_fifo_read(dev, &c, 1) == 1) { - if (mp_interrupt_char == c) { - zephyr_serial_uart_clear_rx_buffer(self); - mp_sched_keyboard_interrupt(); - } else if (!self->rx_paused) { - if (k_msgq_put(&self->msgq, &c, K_NO_WAIT) != 0) { - self->rx_paused = true; - } - } - } -} - -void zephyr_serial_uart_never_reset(zephyr_serial_uart_obj_t *self) { -} - - -void zephyr_serial_uart_construct(zephyr_serial_uart_obj_t *self, const struct device *const uart_device, uint16_t receiver_buffer_size, byte *receiver_buffer) { - self->uart_device = uart_device; - int ret = uart_irq_callback_user_data_set(uart_device, serial_cb, self); - - - k_msgq_init(&self->msgq, receiver_buffer, 1, receiver_buffer_size); - - if (ret < 0) { - if (ret == -ENOTSUP) { - printk("Interrupt-driven UART API support not enabled\n"); - } else if (ret == -ENOSYS) { - printk("UART device does not support interrupt-driven API\n"); - } else { - printk("Error setting UART callback: %d\n", ret); - } - return; - } - self->timeout = K_USEC(100); - uart_irq_rx_enable(uart_device); -} - -bool zephyr_serial_uart_deinited(zephyr_serial_uart_obj_t *self) { - return !device_is_ready(self->uart_device); -} - -void zephyr_serial_uart_deinit(zephyr_serial_uart_obj_t *self) { -} - -// Read characters. -size_t zephyr_serial_uart_read(zephyr_serial_uart_obj_t *self, uint8_t *data, size_t len, int *errcode) { - size_t count = 0; - while (count < len && k_msgq_get(&self->msgq, data + count, self->timeout) == 0) { - count++; - } - if (count > 0) { - self->rx_paused = false; - } - - return count; -} - -// Write characters. -size_t zephyr_serial_uart_write(zephyr_serial_uart_obj_t *self, const uint8_t *data, size_t len, int *errcode) { - for (int i = 0; i < len; i++) { - uart_poll_out(self->uart_device, data[i]); - } - - return len; -} - -uint32_t zephyr_serial_uart_get_baudrate(zephyr_serial_uart_obj_t *self) { - return 0; -} - -void zephyr_serial_uart_set_baudrate(zephyr_serial_uart_obj_t *self, uint32_t baudrate) { -} - -mp_float_t zephyr_serial_uart_get_timeout(zephyr_serial_uart_obj_t *self) { - return 0; -} - -void zephyr_serial_uart_set_timeout(zephyr_serial_uart_obj_t *self, mp_float_t timeout) { -} - -uint32_t zephyr_serial_uart_rx_characters_available(zephyr_serial_uart_obj_t *self) { - return k_msgq_num_used_get(&self->msgq); -} - -void zephyr_serial_uart_clear_rx_buffer(zephyr_serial_uart_obj_t *self) { - k_msgq_purge(&self->msgq); -} - -bool zephyr_serial_uart_ready_to_tx(zephyr_serial_uart_obj_t *self) { - return true; -} diff --git a/ports/zephyr-cp/common-hal/zephyr_spi/SPI.c b/ports/zephyr-cp/common-hal/zephyr_spi/SPI.c deleted file mode 100644 index 5601492fd697b..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_spi/SPI.c +++ /dev/null @@ -1,74 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2019 Dan Halbert for Adafruit Industries -// SPDX-FileCopyrightText: Copyright (c) 2018 Artur Pacholec -// -// SPDX-License-Identifier: MIT - -#include - -#include "shared-bindings/busio/SPI.h" -#include "py/mperrno.h" -#include "py/runtime.h" - -void spi_reset(void) { -} - -void common_hal_busio_spi_never_reset(busio_spi_obj_t *self) { -} - -void common_hal_busio_spi_construct(busio_spi_obj_t *self, const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, const mcu_pin_obj_t *miso, bool half_duplex) { - -} - -bool common_hal_busio_spi_deinited(busio_spi_obj_t *self) { -} - -void common_hal_busio_spi_deinit(busio_spi_obj_t *self) { - if (common_hal_busio_spi_deinited(self)) { - return; - } -} - -bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits) { - return true; -} - -bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self) { - if (common_hal_busio_spi_deinited(self)) { - return false; - } - bool grabbed_lock = false; - return grabbed_lock; -} - -bool common_hal_busio_spi_has_lock(busio_spi_obj_t *self) { - return self->has_lock; -} - -void common_hal_busio_spi_unlock(busio_spi_obj_t *self) { - self->has_lock = false; -} - -bool common_hal_busio_spi_write(busio_spi_obj_t *self, const uint8_t *data, size_t len) { - return true; -} - -bool common_hal_busio_spi_read(busio_spi_obj_t *self, uint8_t *data, size_t len, uint8_t write_value) { - return true; -} - -bool common_hal_busio_spi_transfer(busio_spi_obj_t *self, const uint8_t *data_out, uint8_t *data_in, size_t len) { - return true; -} - -uint32_t common_hal_busio_spi_get_frequency(busio_spi_obj_t *self) { -} - -uint8_t common_hal_busio_spi_get_phase(busio_spi_obj_t *self) { - return 0; -} - -uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self) { - return 0; -} diff --git a/ports/zephyr-cp/common-hal/zephyr_spi/SPI.h b/ports/zephyr-cp/common-hal/zephyr_spi/SPI.h deleted file mode 100644 index 5a1c91c8f0fc7..0000000000000 --- a/ports/zephyr-cp/common-hal/zephyr_spi/SPI.h +++ /dev/null @@ -1,20 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include "py/obj.h" - -typedef struct { - mp_obj_base_t base; - // const spim_peripheral_t *spim_peripheral; - bool has_lock; - uint8_t clock_pin_number; - uint8_t MOSI_pin_number; - uint8_t MISO_pin_number; -} busio_spi_obj_t; - -void spi_reset(void); diff --git a/ports/zephyr-cp/cptools/board_tools.py b/ports/zephyr-cp/cptools/board_tools.py index 088b4bb54914b..305abab3f19b5 100644 --- a/ports/zephyr-cp/cptools/board_tools.py +++ b/ports/zephyr-cp/cptools/board_tools.py @@ -1,3 +1,6 @@ +import tomllib + + def find_mpconfigboard(portdir, board_id): next_underscore = board_id.find("_") while next_underscore != -1: @@ -8,3 +11,27 @@ def find_mpconfigboard(portdir, board_id): return p next_underscore = board_id.find("_", next_underscore + 1) return None + + +def load_mpconfigboard(portdir, board_id): + mpconfigboard_path = find_mpconfigboard(portdir, board_id) + if mpconfigboard_path is None or not mpconfigboard_path.exists(): + return None, {} + + with mpconfigboard_path.open("rb") as f: + return mpconfigboard_path, tomllib.load(f) + + +def get_shields(mpconfigboard): + shields = mpconfigboard.get("SHIELDS") + if shields is None: + shields = mpconfigboard.get("SHIELD") + + if shields is None: + return [] + if isinstance(shields, str): + return [shields] + if isinstance(shields, (list, tuple)): + return [str(shield) for shield in shields] + + return [str(shields)] diff --git a/ports/zephyr-cp/cptools/build_all_boards.py b/ports/zephyr-cp/cptools/build_all_boards.py new file mode 100755 index 0000000000000..8505e732efe71 --- /dev/null +++ b/ports/zephyr-cp/cptools/build_all_boards.py @@ -0,0 +1,520 @@ +#!/usr/bin/env python3 +""" +Build all CircuitPython boards for the Zephyr port. + +This script discovers all boards by finding circuitpython.toml files +and builds them in parallel while sharing a single jobserver across +all builds. + +This is agent generated and works. Don't bother reading too closely because it +is just a tool for us. +""" + +import argparse +import concurrent.futures +import os +import pathlib +import shlex +import subprocess +import sys +import time + + +class Jobserver: + def __init__(self, read_fd, write_fd, jobs=None, owns_fds=False): + self.read_fd = read_fd + self.write_fd = write_fd + self.jobs = jobs + self.owns_fds = owns_fds + + def acquire(self): + while True: + try: + os.read(self.read_fd, 1) + return + except InterruptedError: + continue + + def release(self): + while True: + try: + os.write(self.write_fd, b"+") + return + except InterruptedError: + continue + + def pass_fds(self): + return (self.read_fd, self.write_fd) + + def close(self): + if self.owns_fds: + os.close(self.read_fd) + os.close(self.write_fd) + + +def _parse_makeflags_jobserver(makeflags): + jobserver_auth = None + jobs = None + + for token in shlex.split(makeflags): + if token == "-j" or token == "--jobs": + continue + if token.startswith("-j") and token != "-j": + try: + jobs = int(token[2:]) + except ValueError: + pass + elif token.startswith("--jobs="): + try: + jobs = int(token.split("=", 1)[1]) + except ValueError: + pass + elif token.startswith("--jobserver-auth=") or token.startswith("--jobserver-fds="): + jobserver_auth = token.split("=", 1)[1] + + if not jobserver_auth: + return None, jobs, False + + if jobserver_auth.startswith("fifo:"): + fifo_path = jobserver_auth[len("fifo:") :] + read_fd = os.open(fifo_path, os.O_RDONLY) + write_fd = os.open(fifo_path, os.O_WRONLY) + os.set_inheritable(read_fd, True) + os.set_inheritable(write_fd, True) + return (read_fd, write_fd), jobs, True + + if "," in jobserver_auth: + read_fd, write_fd = jobserver_auth.split(",", 1) + return (int(read_fd), int(write_fd)), jobs, False + + return None, jobs, False + + +def _create_jobserver(jobs): + read_fd, write_fd = os.pipe() + os.set_inheritable(read_fd, True) + os.set_inheritable(write_fd, True) + for _ in range(jobs): + os.write(write_fd, b"+") + return Jobserver(read_fd, write_fd, jobs=jobs, owns_fds=True) + + +def _jobserver_from_env(): + makeflags = os.environ.get("MAKEFLAGS", "") + fds, jobs, owns_fds = _parse_makeflags_jobserver(makeflags) + if not fds: + return None, jobs + read_fd, write_fd = fds + os.set_inheritable(read_fd, True) + os.set_inheritable(write_fd, True) + return Jobserver(read_fd, write_fd, jobs=jobs, owns_fds=owns_fds), jobs + + +def discover_boards(port_dir): + """ + Discover all boards by finding circuitpython.toml files. + + Returns a list of (vendor, board) tuples. + """ + boards = [] + boards_dir = port_dir / "boards" + + # Find all circuitpython.toml files + for toml_file in boards_dir.glob("*/*/circuitpython.toml"): + # Extract vendor and board from path: boards/vendor/board/circuitpython.toml + parts = toml_file.relative_to(boards_dir).parts + if len(parts) == 3: + vendor = parts[0] + board = parts[1] + boards.append((vendor, board)) + + return sorted(boards) + + +def build_board( + port_dir, + vendor, + board, + extra_args=None, + jobserver=None, + env=None, + log_dir=None, +): + """ + Build a single board using make. + + Args: + port_dir: Path to the zephyr-cp port directory + vendor: Board vendor name + board: Board name + extra_args: Additional arguments to pass to make + jobserver: Jobserver instance to limit parallel builds + env: Environment variables for the subprocess + log_dir: Directory to write build logs + + Returns: + (success: bool, elapsed_time: float, output: str, log_path: pathlib.Path) + """ + board_id = f"{vendor}_{board}" + start_time = time.time() + log_path = None + + cmd = ["make", f"BOARD={board_id}"] + + # Add extra arguments (like -j) + if extra_args: + cmd.extend(extra_args) + + if jobserver: + jobserver.acquire() + + try: + result = subprocess.run( + cmd, + cwd=port_dir, + # Inherit stdin alongside jobserver file descriptors + stdin=sys.stdin, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + env=env, + pass_fds=jobserver.pass_fds() if jobserver else (), + ) + elapsed = time.time() - start_time + output = result.stdout or "" + if log_dir: + log_path = log_dir / f"{board_id}.log" + log_path.write_text(output) + return result.returncode == 0, elapsed, output, log_path + except KeyboardInterrupt: + raise + finally: + if jobserver: + jobserver.release() + + +def _format_status(status): + state = status["state"] + elapsed = status.get("elapsed") + if state == "queued": + return "QUEUED" + if state == "running": + return f"RUNNING {elapsed:.1f}s" + if state == "success": + return f"SUCCESS {elapsed:.1f}s" + if state == "failed": + return f"FAILED {elapsed:.1f}s" + if state == "skipped": + return "SKIPPED" + return state.upper() + + +def _build_status_table(boards, statuses, start_time, stop_submitting): + from rich.table import Table + from rich.text import Text + + elapsed = time.time() - start_time + title = f"Building {len(boards)} boards | Elapsed: {elapsed:.1f}s" + if stop_submitting: + title += " | STOPPING AFTER FAILURE" + + table = Table(title=title) + table.add_column("#", justify="right") + table.add_column("Board", no_wrap=True) + table.add_column("Status", no_wrap=True) + + for i, (vendor, board) in enumerate(boards): + board_id = f"{vendor}_{board}" + status_text = _format_status(statuses[i]) + state = statuses[i]["state"] + style = None + if state == "success": + style = "green" + elif state == "failed": + style = "red" + table.add_row( + f"{i + 1}/{len(boards)}", + board_id, + Text(status_text, style=style) if style else status_text, + ) + + return table + + +def _run_builds_tui( + port_dir, + boards, + extra_args, + jobserver, + env, + log_dir, + max_workers, + continue_on_error, +): + from rich.live import Live + + statuses = [ + {"state": "queued", "elapsed": 0.0, "start": None, "log_path": None} for _ in boards + ] + results = [] + futures = {} + next_index = 0 + stop_submitting = False + start_time = time.time() + + with Live( + _build_status_table(boards, statuses, start_time, stop_submitting), + refresh_per_second=4, + transient=False, + ) as live: + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: + while next_index < len(boards) and len(futures) < max_workers: + vendor, board = boards[next_index] + statuses[next_index]["state"] = "running" + statuses[next_index]["start"] = time.time() + future = executor.submit( + build_board, + port_dir, + vendor, + board, + extra_args, + jobserver, + env, + log_dir, + ) + futures[future] = next_index + next_index += 1 + + while futures: + for status in statuses: + if status["state"] == "running": + status["elapsed"] = time.time() - status["start"] + + live.update(_build_status_table(boards, statuses, start_time, stop_submitting)) + + done, _ = concurrent.futures.wait( + futures, + timeout=0.1, + return_when=concurrent.futures.FIRST_COMPLETED, + ) + for future in done: + index = futures.pop(future) + vendor, board = boards[index] + success, elapsed, _output, log_path = future.result() + statuses[index]["elapsed"] = elapsed + statuses[index]["log_path"] = log_path + statuses[index]["state"] = "success" if success else "failed" + results.append((vendor, board, success, elapsed)) + + if not success and not continue_on_error: + stop_submitting = True + + if not stop_submitting and next_index < len(boards): + vendor, board = boards[next_index] + statuses[next_index]["state"] = "running" + statuses[next_index]["start"] = time.time() + future = executor.submit( + build_board, + port_dir, + vendor, + board, + extra_args, + jobserver, + env, + log_dir, + ) + futures[future] = next_index + next_index += 1 + + if stop_submitting: + for index in range(next_index, len(boards)): + if statuses[index]["state"] == "queued": + statuses[index]["state"] = "skipped" + + live.update(_build_status_table(boards, statuses, start_time, stop_submitting)) + + return results, stop_submitting + + +def _run_builds_plain( + port_dir, + boards, + extra_args, + jobserver, + env, + log_dir, + max_workers, + continue_on_error, +): + results = [] + futures = {} + next_index = 0 + stop_submitting = False + + with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: + while next_index < len(boards) and len(futures) < max_workers: + vendor, board = boards[next_index] + future = executor.submit( + build_board, + port_dir, + vendor, + board, + extra_args, + jobserver, + env, + log_dir, + ) + futures[future] = (vendor, board) + next_index += 1 + + while futures: + done, _ = concurrent.futures.wait( + futures, + return_when=concurrent.futures.FIRST_COMPLETED, + ) + for future in done: + vendor, board = futures.pop(future) + success, elapsed, _output, _log_path = future.result() + board_id = f"{vendor}_{board}" + status = "SUCCESS" if success else "FAILURE" + print(f"{board_id}: {status} ({elapsed:.1f}s)") + results.append((vendor, board, success, elapsed)) + + if not success and not continue_on_error: + stop_submitting = True + + if not stop_submitting and next_index < len(boards): + vendor, board = boards[next_index] + future = executor.submit( + build_board, + port_dir, + vendor, + board, + extra_args, + jobserver, + env, + log_dir, + ) + futures[future] = (vendor, board) + next_index += 1 + + return results, stop_submitting + + +def main(): + parser = argparse.ArgumentParser( + description="Build all CircuitPython boards for the Zephyr port", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" +Examples: + # Build all boards in parallel with 32 jobserver slots + %(prog)s -j32 + + # Build all boards using make's jobserver (recommended) + make -j32 all +""", + ) + parser.add_argument( + "-j", + "--jobs", + type=int, + default=None, + help="Number of shared jobserver slots across all board builds", + ) + parser.add_argument( + "--continue-on-error", + action="store_true", + help="Continue building remaining boards even if one fails", + ) + parser.add_argument( + "--vendor", + type=str, + default=None, + help="Only build boards from this vendor (e.g. 'native' for sim boards)", + ) + + args = parser.parse_args() + + if args.jobs is not None and args.jobs < 1: + print("ERROR: --jobs must be at least 1") + return 2 + + # Get the port directory + port_dir = pathlib.Path(__file__).parent.resolve().parent + + # Discover all boards + boards = discover_boards(port_dir) + + if args.vendor: + boards = [(v, b) for v, b in boards if v == args.vendor] + + if not boards: + print("ERROR: No boards found!") + return 1 + + # Prepare jobserver and extra make arguments + jobserver, detected_jobs = _jobserver_from_env() + env = os.environ.copy() + + extra_args = [] + jobserver_jobs = detected_jobs + + if not jobserver: + jobserver_jobs = args.jobs if args.jobs else (os.cpu_count() or 1) + jobserver = _create_jobserver(jobserver_jobs) + env["MAKEFLAGS"] = ( + f"-j{jobserver_jobs} --jobserver-auth={jobserver.read_fd},{jobserver.write_fd}" + ) + + max_workers = jobserver_jobs + if max_workers is None: + max_workers = min(len(boards), os.cpu_count() or 1) + max_workers = max(1, min(len(boards), max_workers)) + + # Build all boards + log_dir = port_dir / "build-logs" + log_dir.mkdir(parents=True, exist_ok=True) + + try: + use_tui = sys.stdout.isatty() + if use_tui: + try: + import rich # noqa: F401 + except ImportError: + use_tui = False + + if use_tui: + results, stop_submitting = _run_builds_tui( + port_dir, + boards, + extra_args, + jobserver, + env, + log_dir, + max_workers, + args.continue_on_error, + ) + else: + results, stop_submitting = _run_builds_plain( + port_dir, + boards, + extra_args, + jobserver, + env, + log_dir, + max_workers, + args.continue_on_error, + ) + except KeyboardInterrupt: + print("\n\nBuild interrupted by user.") + return 130 # Standard exit code for SIGINT + finally: + if jobserver: + jobserver.close() + + failed = [r for r in results if not r[2]] + return 0 if len(failed) == 0 else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/ports/zephyr-cp/cptools/build_circuitpython.py b/ports/zephyr-cp/cptools/build_circuitpython.py index 61e103e9fe109..1a55997974635 100644 --- a/ports/zephyr-cp/cptools/build_circuitpython.py +++ b/ports/zephyr-cp/cptools/build_circuitpython.py @@ -1,16 +1,16 @@ import asyncio -import colorlog -import sys import logging import os import pathlib -import tomllib -import tomlkit -import yaml import pickle +import sys -import cpbuild import board_tools +import colorlog +import cpbuild +import tomlkit +import tomllib +import yaml logger = logging.getLogger(__name__) @@ -43,6 +43,7 @@ ALWAYS_ON_MODULES = ["sys", "collections"] DEFAULT_MODULES = [ + "__future__", "time", "os", "microcontroller", @@ -51,9 +52,81 @@ "json", "random", "digitalio", - "zephyr_serial", + "rotaryio", + "rainbowio", + "traceback", + "warnings", + "supervisor", + "errno", + "io", + "math", + "msgpack", + "aesio", + "hashlib", + "zlib", + "adafruit_bus_device", ] -MPCONFIG_FLAGS = ["ulab", "nvm", "displayio", "warnings", "alarm", "array", "json"] +# Flags that don't match with with a *bindings module. Some used by adafruit_requests +MPCONFIG_FLAGS = ["array", "errno", "io", "json", "math"] + +# List of other modules (the value) that can be enabled when another one (the key) is. +REVERSE_DEPENDENCIES = { + "audiobusio": ["audiocore"], + "audiocore": [ + "audiodelays", + "audiofilters", + "audiofreeverb", + "audiomixer", + "audiomp3", + "synthio", + ], + "busio": ["fourwire", "i2cdisplaybus", "sdcardio", "sharpdisplay"], + "fourwire": ["displayio", "busdisplay", "epaperdisplay"], + "i2cdisplaybus": ["displayio", "busdisplay", "epaperdisplay"], + # Zephyr display backends need displayio and, by extension, terminalio so + # the REPL console appears on the display by default. + "zephyr_display": ["displayio"], + "displayio": [ + "vectorio", + "bitmapfilter", + "bitmaptools", + "terminalio", + "lvfontio", + "tilepalettemapper", + "fontio", + ], + "sharpdisplay": ["framebufferio"], + "framebufferio": ["displayio"], +} + +# Other flags to set when a module is enabled +EXTRA_FLAGS = { + "audiobusio": {"AUDIOBUSIO_I2SOUT": 1, "AUDIOBUSIO_PDMIN": 0}, + "busio": {"BUSIO_SPI": 1, "BUSIO_I2C": 1}, + "rotaryio": {"ROTARYIO_SOFTENCODER": 1}, + "synthio": {"SYNTHIO_MAX_CHANNELS": 12}, +} + +# Library sources. Will be globbed from the top level directory +# No QSTR processing or CIRCUITPY specific flags +LIBRARY_SOURCE = { + "audiomp3": ["lib/mp3/src/*.c"], + "zlib": [ + "lib/uzlib/tinflate.c", + "lib/uzlib/tinfzlib.c", + "lib/uzlib/tinfgzip.c", + "lib/uzlib/adler32.c", + "lib/uzlib/crc32.c", + ], +} + +SHARED_MODULE_AND_COMMON_HAL = ["_bleio", "os", "rotaryio"] + +# Mapping from module directory name to the flag name used in CIRCUITPY_ +MODULE_FLAG_NAMES = { + "__future__": "FUTURE", + "_bleio": "BLEIO", +} async def preprocess_and_split_defs(compiler, source_file, build_path, flags): @@ -84,10 +157,17 @@ async def preprocess_and_split_defs(compiler, source_file, build_path, flags): async def collect_defs(mode, build_path): output_file = build_path / f"{mode}defs.collected" splitdir = build_path / "genhdr" / mode + to_collect = list(splitdir.glob(f"**/*.{mode}")) + batch_size = 50 await cpbuild.run_command( - ["cat", "-s", *splitdir.glob(f"**/*.{mode}"), ">", output_file], + ["cat", "-s", *to_collect[:batch_size], ">", output_file], splitdir, ) + for i in range(0, len(to_collect), batch_size): + await cpbuild.run_command( + ["cat", "-s", *to_collect[i : i + batch_size], ">>", output_file], + splitdir, + ) return output_file @@ -162,54 +242,111 @@ async def generate_root_pointer_header(build_path): ) -TINYUSB_SETTINGS = { - "": { - "CFG_TUSB_MCU": "OPT_MCU_MIMXRT10XX", - "CFG_TUD_CDC_RX_BUFSIZE": 640, - "CFG_TUD_CDC_TX_BUFSIZE": 512, - }, - "stm32u575xx": {"CFG_TUSB_MCU": "OPT_MCU_STM32U5"}, - "nrf52840": {"CFG_TUSB_MCU": "OPT_MCU_NRF5X"}, - "nrf5340": {"CFG_TUSB_MCU": "OPT_MCU_NRF5X"}, - # "r7fa8d1bhecbd": {"CFG_TUSB_MCU": "OPT_MCU_RAXXX", "USB_HIGHSPEED": "1", "USBHS_USB_INT_RESUME_IRQn": "54", "USBFS_INT_IRQn": "54", "CIRCUITPY_USB_DEVICE_INSTANCE": "1"}, - # ifeq ($(CHIP_FAMILY),$(filter $(CHIP_FAMILY),MIMXRT1011 MIMXRT1015)) - # CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=64 -DCFG_TUD_MSC_BUFSIZE=512 - # else - # CFLAGS += -DCFG_TUD_MIDI_RX_BUFSIZE=512 -DCFG_TUD_MIDI_TX_BUFSIZE=512 -DCFG_TUD_MSC_BUFSIZE=1024 - # endif -} +async def generate_display_resources(output_path, translation, font, extra_characters): + await cpbuild.run_command( + [ + "python", + srcdir / "tools" / "gen_display_resources.py", + "--font", + srcdir / font, + "--sample_file", + srcdir / "locale" / f"{translation}.po", + "--extra_characters", + repr(extra_characters), + "--output_c_file", + output_path, + ], + srcdir, + check_hash=[output_path], + ) -TINYUSB_SOURCE = { - "stm32u575xx": [ - "src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c", - "src/portable/synopsys/dwc2/dcd_dwc2.c", - "src/portable/synopsys/dwc2/hcd_dwc2.c", - "src/portable/synopsys/dwc2/dwc2_common.c", - ], - "nrf52840": [ - "src/portable/nordic/nrf5x/dcd_nrf5x.c", - ], - "nrf5340": [ - "src/portable/nordic/nrf5x/dcd_nrf5x.c", - ], - # "r7fa8d1bhecbd": [ - # "src/portable/renesas/rusb2/dcd_rusb2.c", - # "src/portable/renesas/rusb2/hcd_rusb2.c", - # "src/portable/renesas/rusb2/rusb2_common.c", - # ], -} + +async def generate_web_workflow_static(output_path, static_files): + await cpbuild.run_command( + [ + "python", + srcdir / "tools" / "gen_web_workflow_static.py", + "--output_c_file", + output_path, + *static_files, + ], + srcdir, + check_hash=[output_path], + ) + + +def determine_enabled_modules(board_info, portdir, srcdir): + """Determine which CircuitPython modules should be enabled based on board capabilities. + + Args: + board_info: Dictionary containing board hardware capabilities + portdir: Path to the port directory (ports/zephyr-cp) + srcdir: Path to the CircuitPython source root + + Returns: + tuple: (enabled_modules set, module_reasons dict) + """ + enabled_modules = set(DEFAULT_MODULES) + module_reasons = {} + + if board_info["wifi"]: + enabled_modules.add("wifi") + module_reasons["wifi"] = "Zephyr board has wifi" + + if board_info["flash_count"] > 0: + enabled_modules.add("storage") + module_reasons["storage"] = "Zephyr board has flash" + + network_enabled = board_info.get("wifi", False) or board_info.get("hostnetwork", False) + + if network_enabled: + enabled_modules.add("ipaddress") + module_reasons["ipaddress"] = "Zephyr networking enabled" + enabled_modules.add("socketpool") + module_reasons["socketpool"] = "Zephyr networking enabled" + enabled_modules.add("hashlib") + module_reasons["hashlib"] = "Zephyr networking enabled" + + if board_info.get("wifi", False) or board_info.get("ethernet", False): + enabled_modules.add("ssl") + module_reasons["ssl"] = "Zephyr networking enabled" + + for port_module in (portdir / "bindings").iterdir(): + if not board_info.get(port_module.name, False): + continue + enabled_modules.add(port_module.name) + module_reasons[port_module.name] = f"Zephyr board has {port_module.name}" + + for shared_module in (srcdir / "shared-bindings").iterdir(): + if not board_info.get(shared_module.name, False) or not shared_module.glob("*.c"): + continue + enabled_modules.add(shared_module.name) + module_reasons[shared_module.name] = f"Zephyr board has {shared_module.name}" + + more_modules = [] + more_modules.extend(REVERSE_DEPENDENCIES.get(shared_module.name, [])) + while more_modules: + reverse_dependency = more_modules.pop(0) + if reverse_dependency in enabled_modules: + continue + logger.debug(f"Enabling {reverse_dependency} because {shared_module.name} is enabled") + enabled_modules.add(reverse_dependency) + more_modules.extend(REVERSE_DEPENDENCIES.get(reverse_dependency, [])) + module_reasons[reverse_dependency] = f"Zephyr board has {shared_module.name}" + + return enabled_modules, module_reasons async def build_circuitpython(): circuitpython_flags = ["-DCIRCUITPY"] port_flags = [] enable_mpy_native = False - full_build = False + full_build = True usb_host = False - tusb_mem_align = 4 + zephyr_board = cmake_args["BOARD"] board = cmake_args["BOARD_ALIAS"] if not board: - board = cmake_args["BOARD"] + board = zephyr_board translation = cmake_args["TRANSLATION"] if not translation: translation = "en_US" @@ -218,26 +355,30 @@ async def build_circuitpython(): lto = cmake_args.get("LTO", "n") == "y" circuitpython_flags.append(f"-DCIRCUITPY_ENABLE_MPY_NATIVE={1 if enable_mpy_native else 0}") circuitpython_flags.append(f"-DCIRCUITPY_FULL_BUILD={1 if full_build else 0}") + circuitpython_flags.append(f"-DCIRCUITPY_SETTINGS_TOML={1 if full_build else 0}") + circuitpython_flags.append("-DCIRCUITPY_STATUS_BAR=1") circuitpython_flags.append(f"-DCIRCUITPY_USB_HOST={1 if usb_host else 0}") - circuitpython_flags.append(f'-DCIRCUITPY_BOARD_ID=\\"{board}\\"') - circuitpython_flags.append(f"-DCIRCUITPY_TUSB_MEM_ALIGN={tusb_mem_align}") + circuitpython_flags.append(f"-DCIRCUITPY_BOARD_ID='\"{board}\"'") circuitpython_flags.append(f"-DCIRCUITPY_TRANSLATE_OBJECT={1 if lto else 0}") circuitpython_flags.append("-DINTERNAL_FLASH_FILESYSTEM") circuitpython_flags.append("-DLONGINT_IMPL_MPZ") circuitpython_flags.append("-DCIRCUITPY_SSL_MBEDTLS") - circuitpython_flags.append('-DFFCONF_H=\\"lib/oofatfs/ffconf.h\\"') + circuitpython_flags.append("-DFFCONF_H='\"lib/oofatfs/ffconf.h\"'") + circuitpython_flags.append( + "-D_DEFAULT_SOURCE" + ) # To get more from picolibc to match newlib such as M_PI circuitpython_flags.extend(("-I", srcdir)) - circuitpython_flags.extend(("-I", srcdir / "lib/tinyusb/src")) - circuitpython_flags.extend(("-I", srcdir / "supervisor/shared/usb")) circuitpython_flags.extend(("-I", builddir)) circuitpython_flags.extend(("-I", portdir)) - # circuitpython_flags.extend(("-I", srcdir / "ports" / port / "peripherals")) - - # circuitpython_flags.extend(("-I", build_path / board_id)) genhdr = builddir / "genhdr" genhdr.mkdir(exist_ok=True, parents=True) version_header = genhdr / "mpversion.h" + mpconfigboard_fn = board_tools.find_mpconfigboard(portdir, board) + mpconfigboard = {"USB_VID": 0x1209, "USB_PID": 0x000C, "USB_INTERFACE_NAME": "CircuitPython"} + if mpconfigboard_fn is not None and mpconfigboard_fn.exists(): + with mpconfigboard_fn.open("rb") as f: + mpconfigboard.update(tomllib.load(f)) async with asyncio.TaskGroup() as tg: tg.create_task( cpbuild.run_command( @@ -254,13 +395,10 @@ async def build_circuitpython(): ) ) - board_autogen_task = tg.create_task(zephyr_dts_to_cp_board(builddir, zephyrbuilddir)) + board_autogen_task = tg.create_task( + zephyr_dts_to_cp_board(zephyr_board, portdir, builddir, zephyrbuilddir, mpconfigboard) + ) board_info = board_autogen_task.result() - mpconfigboard_fn = board_tools.find_mpconfigboard(portdir, board) - mpconfigboard = { - "USB_VID": 0x1209, - "USB_PID": 0x000C, - } if mpconfigboard_fn is None: mpconfigboard_fn = ( portdir / "boards" / board_info["vendor_id"] / board / "circuitpython.toml" @@ -268,31 +406,28 @@ async def build_circuitpython(): logging.warning( f"Could not find board config at: boards/{board_info['vendor_id']}/{board}" ) - elif mpconfigboard_fn.exists(): - with mpconfigboard_fn.open("rb") as f: - mpconfigboard = tomllib.load(f) autogen_board_info_fn = mpconfigboard_fn.parent / "autogen_board_info.toml" - enabled_modules = set(DEFAULT_MODULES) - module_reasons = {} - if board_info["wifi"]: - enabled_modules.add("wifi") - module_reasons["wifi"] = "Zephyr board has wifi" + creator_id = mpconfigboard.get("CIRCUITPY_CREATOR_ID", mpconfigboard.get("USB_VID", 0x1209)) + creation_id = mpconfigboard.get("CIRCUITPY_CREATION_ID", mpconfigboard.get("USB_PID", 0x000C)) + circuitpython_flags.append(f"-DCIRCUITPY_CREATOR_ID=0x{creator_id:08x}") + circuitpython_flags.append(f"-DCIRCUITPY_CREATION_ID=0x{creation_id:08x}") - if board_info["flash_count"] > 0: - enabled_modules.add("storage") - module_reasons["storage"] = "Zephyr board has flash" + vendor = mpconfigboard.get("VENDOR", board_info["vendor"]) + name = mpconfigboard.get("NAME", board_info["name"]) - if "wifi" in enabled_modules: - enabled_modules.add("socketpool") - enabled_modules.add("ssl") - module_reasons["socketpool"] = "Zephyr networking enabled" - module_reasons["ssl"] = "Zephyr networking enabled" + enabled_modules, module_reasons = determine_enabled_modules(board_info, portdir, srcdir) + for m in mpconfigboard.get("DISABLED_MODULES", []): + enabled_modules.discard(m) + + web_workflow_enabled = board_info.get("wifi", False) or board_info.get("hostnetwork", False) circuitpython_flags.extend(board_info["cflags"]) + circuitpython_flags.append(f"-DCIRCUITPY_WEB_WORKFLOW={1 if web_workflow_enabled else 0}") supervisor_source = [ "main.c", + "extmod/modjson.c", "extmod/vfs_fat.c", "lib/tlsf/tlsf.c", portdir / "background.c", @@ -301,7 +436,10 @@ async def build_circuitpython(): portdir / "common-hal/microcontroller/Processor.c", portdir / "common-hal/os/__init__.c", "shared/readline/readline.c", + "shared/runtime/buffer_helper.c", "shared/runtime/context_manager_helpers.c", + "shared/runtime/gchelper_generic.c", + "shared/runtime/gchelper_native.c", "shared/runtime/pyexec.c", "shared/runtime/interrupt_char.c", "shared/runtime/stdout_helpers.c", @@ -319,139 +457,56 @@ async def build_circuitpython(): supervisor_source = [pathlib.Path(p) for p in supervisor_source] supervisor_source.extend(board_info["source_files"]) supervisor_source.extend(top.glob("supervisor/shared/*.c")) + if "_bleio" in enabled_modules: + supervisor_source.append(top / "supervisor/shared/bluetooth/bluetooth.c") supervisor_source.append(top / "supervisor/shared/translate/translate.c") - # if web_workflow: - # supervisor_source.extend(top.glob("supervisor/shared/web_workflow/*.c")) + if web_workflow_enabled: + supervisor_source.extend(top.glob("supervisor/shared/web_workflow/*.c")) - usb_num_endpoint_pairs = board_info.get("usb_num_endpoint_pairs", 0) - soc = board_info["soc"] - usb_ok = usb_num_endpoint_pairs > 0 and soc in TINYUSB_SETTINGS - circuitpython_flags.append(f"-DCIRCUITPY_TINYUSB={1 if usb_ok else 0}") + usb_ok = board_info.get("usb_device", False) circuitpython_flags.append(f"-DCIRCUITPY_USB_DEVICE={1 if usb_ok else 0}") - tinyusb_files = [] if usb_ok: enabled_modules.add("usb_cdc") - for setting in TINYUSB_SETTINGS[soc]: - circuitpython_flags.append(f"-D{setting}={TINYUSB_SETTINGS[soc][setting]}") - tinyusb_files.extend((top / "lib" / "tinyusb" / path for path in TINYUSB_SOURCE[soc])) + for macro in ("USB_PID", "USB_VID"): + print(f"Setting {macro} to {mpconfigboard.get(macro)}") circuitpython_flags.append(f"-D{macro}=0x{mpconfigboard.get(macro):04x}") + circuitpython_flags.append( + f"-DUSB_INTERFACE_NAME='\"{mpconfigboard['USB_INTERFACE_NAME']}\"'" + ) for macro, limit, value in ( - ("USB_PRODUCT", 16, board_info["name"]), - ("USB_MANUFACTURER", 8, board_info["vendor"]), + ("USB_PRODUCT", 16, name), + ("USB_MANUFACTURER", 8, vendor), ): circuitpython_flags.append(f"-D{macro}='\"{value}\"'") circuitpython_flags.append(f"-D{macro}_{limit}='\"{value[:limit]}\"'") - usb_interface_name = "CircuitPython" - - circuitpython_flags.append("-DCFG_TUSB_OS=OPT_OS_ZEPHYR") - circuitpython_flags.append(f"-DUSB_INTERFACE_NAME='\"{usb_interface_name}\"'") - circuitpython_flags.append(f"-DUSB_NUM_ENDPOINT_PAIRS={usb_num_endpoint_pairs}") - for direction in ("IN", "OUT"): - circuitpython_flags.append(f"-DUSB_NUM_{direction}_ENDPOINTS={usb_num_endpoint_pairs}") - # USB is special because it doesn't have a matching module. - msc_enabled = board_info["flash_count"] > 0 - if msc_enabled: - circuitpython_flags.append("-DCFG_TUD_MSC_BUFSIZE=1024") - circuitpython_flags.append("-DCIRCUITPY_USB_MSC_ENABLED_DEFAULT=1") - tinyusb_files.append(top / "lib/tinyusb/src/class/msc/msc_device.c") - supervisor_source.append(top / "supervisor/shared/usb/usb_msc_flash.c") - circuitpython_flags.append(f"-DCIRCUITPY_USB_MSC={1 if msc_enabled else 0}") - if "usb_cdc" in enabled_modules: - tinyusb_files.extend(top.glob("lib/tinyusb/*.c")) - tinyusb_files.append(top / "lib/tinyusb/src/class/cdc/cdc_device.c") - circuitpython_flags.append("-DCFG_TUD_CDC_RX_BUFSIZE=640") - circuitpython_flags.append("-DCFG_TUD_CDC_TX_BUFSIZE=512") - circuitpython_flags.append("-DCFG_TUD_CDC=2") - circuitpython_flags.append("-DCIRCUITPY_USB_CDC_CONSOLE_ENABLED_DEFAULT=1") - circuitpython_flags.append("-DCIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT=0") - - if "usb_hid_enabled_default" not in mpconfigboard: - mpconfigboard["usb_hid_enabled_default"] = usb_num_endpoint_pairs >= 5 - if "usb_midi_enabled_default" not in mpconfigboard: - mpconfigboard["usb_midi_enabled_default"] = usb_num_endpoint_pairs >= 8 - - tinyusb_files.extend( - (top / "lib/tinyusb/src/common/tusb_fifo.c", top / "lib/tinyusb/src/tusb.c") - ) - supervisor_source.extend( - (portdir / "supervisor/usb.c", top / "supervisor/shared/usb/usb.c") - ) + circuitpython_flags.append("-DCIRCUITPY_USB_CDC_CONSOLE_ENABLED_DEFAULT=1") + circuitpython_flags.append("-DCIRCUITPY_USB_CDC_DATA_ENABLED_DEFAULT=0") - tinyusb_files.extend( - ( - top / "lib/tinyusb/src/device/usbd.c", - top / "lib/tinyusb/src/device/usbd_control.c", - ) - ) supervisor_source.extend( - (top / "supervisor/shared/usb/usb_desc.c", top / "supervisor/shared/usb/usb_device.c") + (portdir / "supervisor/usb.c", srcdir / "supervisor/shared/usb.c") ) - elif usb_num_endpoint_pairs > 0: - module_reasons["usb_cdc"] = f"No TinyUSB settings for {soc}" - - circuitpython_flags.append(f"-DCIRCUITPY_PORT_SERIAL={0 if usb_ok else 1}") - # ifeq ($(CIRCUITPY_USB_HID), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/class/hid/hid_device.c \ - # shared-bindings/usb_hid/__init__.c \ - # shared-bindings/usb_hid/Device.c \ - # shared-module/usb_hid/__init__.c \ - # shared-module/usb_hid/Device.c \ - - # endif - - # ifeq ($(CIRCUITPY_USB_MIDI), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/class/midi/midi_device.c \ - # shared-bindings/usb_midi/__init__.c \ - # shared-bindings/usb_midi/PortIn.c \ - # shared-bindings/usb_midi/PortOut.c \ - # shared-module/usb_midi/__init__.c \ - # shared-module/usb_midi/PortIn.c \ - # shared-module/usb_midi/PortOut.c \ - - # endif - - # ifeq ($(CIRCUITPY_USB_VIDEO), 1) - # SRC_SUPERVISOR += \ - # shared-bindings/usb_video/__init__.c \ - # shared-module/usb_video/__init__.c \ - # shared-bindings/usb_video/USBFramebuffer.c \ - # shared-module/usb_video/USBFramebuffer.c \ - # lib/tinyusb/src/class/video/video_device.c \ - - # CFLAGS += -DCFG_TUD_VIDEO=1 -DCFG_TUD_VIDEO_STREAMING=1 -DCFG_TUD_VIDEO_STREAMING_EP_BUFSIZE=256 -DCFG_TUD_VIDEO_STREAMING_BULK=1 - # endif - - # ifeq ($(CIRCUITPY_USB_VENDOR), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/class/vendor/vendor_device.c \ - - # endif - - # ifeq ($(CIRCUITPY_TINYUSB_HOST), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/host/hub.c \ - # lib/tinyusb/src/host/usbh.c \ - - # endif - - # ifeq ($(CIRCUITPY_USB_KEYBOARD_WORKFLOW), 1) - # SRC_SUPERVISOR += \ - # lib/tinyusb/src/class/hid/hid_host.c \ - # supervisor/shared/usb/host_keyboard.c \ - - # endif - - if "ssl" in enabled_modules: + + creator_id = mpconfigboard.get("CIRCUITPY_CREATOR_ID", mpconfigboard.get("USB_VID")) + creation_id = mpconfigboard.get("CIRCUITPY_CREATION_ID", mpconfigboard.get("USB_PID")) + if creator_id is not None: + circuitpython_flags.append(f"-DCIRCUITPY_CREATOR_ID=0x{creator_id:08x}") + if creation_id is not None: + circuitpython_flags.append(f"-DCIRCUITPY_CREATION_ID=0x{creation_id:08x}") + + # Always use port serial. It'll switch between USB and UART automatically. + circuitpython_flags.append("-DCIRCUITPY_PORT_SERIAL=1") + + if "hashlib" in enabled_modules: + circuitpython_flags.append("-DCIRCUITPY_HASHLIB_MBEDTLS=1") + if "ssl" not in enabled_modules: + circuitpython_flags.append("-DCIRCUITPY_HASHLIB_MBEDTLS_ONLY=1") + + if "ssl" in enabled_modules or "hashlib" in enabled_modules: # TODO: Figure out how to get these paths from zephyr - circuitpython_flags.append('-DMBEDTLS_CONFIG_FILE=\\"config-tls-generic.h\\"') - circuitpython_flags.extend( - ("-isystem", portdir / "modules" / "crypto" / "tinycrypt" / "lib" / "include") - ) + circuitpython_flags.append("-DMBEDTLS_CONFIG_FILE='\"config-mbedtls.h\"'") circuitpython_flags.extend( ("-isystem", portdir / "modules" / "crypto" / "mbedtls" / "include") ) @@ -462,24 +517,27 @@ async def build_circuitpython(): ("-isystem", portdir / "modules" / "crypto" / "mbedtls" / "include") ) circuitpython_flags.extend(("-isystem", zephyrdir / "modules" / "mbedtls" / "configs")) - supervisor_source.append(top / "lib" / "mbedtls_config" / "crt_bundle.c") + if "ssl" in enabled_modules: + supervisor_source.append(top / "lib" / "mbedtls_config" / "crt_bundle.c") # Make sure all modules have a setting by filling in defaults. hal_source = [] + library_sources = [] autogen_board_info = tomlkit.document() autogen_board_info.add( tomlkit.comment( "This file is autogenerated when a board is built. Do not edit. Do commit it to git. Other scripts use its info." ) ) - autogen_board_info.add("name", board_info["vendor"] + " " + board_info["name"]) + autogen_board_info.add("name", vendor + " " + name) autogen_modules = tomlkit.table() autogen_board_info.add("modules", autogen_modules) for module in sorted( list(top.glob("shared-bindings/*")) + list(portdir.glob("bindings/*")), key=lambda x: x.name, ): - if not module.is_dir(): + # Skip files and directories without C source files (like artifacts from a docs build) + if not module.is_dir() or len(list(module.glob("*.c"))) == 0: continue enabled = module.name in enabled_modules # print(f"Module {module.name} enabled: {enabled}") @@ -487,25 +545,36 @@ async def build_circuitpython(): if module.name in module_reasons: v.comment(module_reasons[module.name]) autogen_modules.add(module.name, v) - circuitpython_flags.append(f"-DCIRCUITPY_{module.name.upper()}={1 if enabled else 0}") + flag_name = MODULE_FLAG_NAMES.get(module.name, module.name.upper()) + circuitpython_flags.append(f"-DCIRCUITPY_{flag_name}={1 if enabled else 0}") + + if enabled: + if module.name in EXTRA_FLAGS: + for flag, value in EXTRA_FLAGS[module.name].items(): + circuitpython_flags.append(f"-DCIRCUITPY_{flag}={value}") if enabled: hal_source.extend(portdir.glob(f"bindings/{module.name}/*.c")) + len_before = len(hal_source) hal_source.extend(top.glob(f"ports/zephyr-cp/common-hal/{module.name}/*.c")) - hal_source.extend(top.glob(f"shared-bindings/{module.name}/*.c")) - hal_source.extend(top.glob(f"shared-module/{module.name}/*.c")) + # Only include shared-module/*.c if no common-hal/*.c files were found + if len(hal_source) == len_before or module.name in SHARED_MODULE_AND_COMMON_HAL: + hal_source.extend(top.glob(f"shared-module/{module.name}/**/*.c")) + hal_source.extend(top.glob(f"shared-bindings/{module.name}/**/*.c")) + if module.name in LIBRARY_SOURCE: + for library_source in LIBRARY_SOURCE[module.name]: + library_sources.extend(top.glob(library_source)) if os.environ.get("CI", "false") == "true": - # Fail the build if it isn't up to date. + # Warn if it isn't up to date. if ( not autogen_board_info_fn.exists() or autogen_board_info_fn.read_text() != tomlkit.dumps(autogen_board_info) ): - logger.error("autogen_board_info.toml is out of date.") - raise RuntimeError( + logger.warning( f"autogen_board_info.toml is missing or out of date. Please run `make BOARD={board}` locally and commit {autogen_board_info_fn}." ) - elif autogen_board_info_fn.parent.exists(): + if autogen_board_info_fn.parent.exists(): autogen_board_info_fn.write_text(tomlkit.dumps(autogen_board_info)) for mpflag in MPCONFIG_FLAGS: @@ -558,6 +627,23 @@ async def build_circuitpython(): tg.create_task(generate_module_header(board_build)) tg.create_task(generate_root_pointer_header(board_build)) + if "terminalio" in enabled_modules: + output_path = board_build / f"autogen_display_resources-{translation}.c" + font_path = srcdir / mpconfigboard.get( + "CIRCUITPY_DISPLAY_FONT", "tools/fonts/ter-u12n.bdf" + ) + extra_characters = mpconfigboard.get("CIRCUITPY_FONT_EXTRA_CHARACTERS", "") + tg.create_task( + generate_display_resources(output_path, translation, font_path, extra_characters) + ) + source_files.append(output_path) + + if web_workflow_enabled: + output_path = board_build / "autogen_web_workflow_static.c" + static_files = sorted((srcdir / "supervisor/shared/web_workflow/static").glob("*")) + tg.create_task(generate_web_workflow_static(output_path, static_files)) + source_files.append(output_path) + # This file is generated by the QSTR/translation process. source_files.append(builddir / f"translations-{translation}.c") # These files don't include unique QSTRs. They just need to be compiled. @@ -575,14 +661,16 @@ async def build_circuitpython(): source_files.append(portdir / "common-hal/zephyr_kernel/__init__.c") # source_files.append(srcdir / "ports" / port / "peripherals" / "nrf" / "nrf52840" / "pins.c") - assembly_files.append(srcdir / "supervisor/shared/cpu_regs.S") - source_files.extend(assembly_files) - source_files.extend(tinyusb_files) - objects = [] async with asyncio.TaskGroup() as tg: + for source_file in library_sources: + source_file = top / source_file + build_file = source_file.with_suffix(".o") + object_file = builddir / (build_file.relative_to(top)) + objects.append(object_file) + tg.create_task(compiler.compile(source_file, object_file)) for source_file in source_files: source_file = top / source_file build_file = source_file.with_suffix(".o") diff --git a/ports/zephyr-cp/cptools/compat2driver.py b/ports/zephyr-cp/cptools/compat2driver.py index 2b818c4c23762..7da74ffcf18c5 100644 --- a/ports/zephyr-cp/cptools/compat2driver.py +++ b/ports/zephyr-cp/cptools/compat2driver.py @@ -1,18 +1,31 @@ # This file was generated by gen_compat2driver.py COMPAT_TO_DRIVER = { # adc + "adi_ad4050_adc": "adc", + "adi_ad4052_adc": "adc", "adi_ad4114_adc": "adc", + "adi_ad4130_adc": "adc", + "adi_ad4170_adc": "adc", + "adi_ad4190_adc": "adc", + "adi_ad4195_adc": "adc", "adi_ad559x_adc": "adc", + "adi_ad7124_adc": "adc", "adi_max32_adc": "adc", "ambiq_adc": "adc", "atmel_sam0_adc": "adc", "atmel_sam_adc": "adc", "atmel_sam_afec": "adc", + "bflb_adc": "adc", + "ene_kb106x_adc": "adc", "ene_kb1200_adc": "adc", "espressif_esp32_adc": "adc", "gd_gd32_adc": "adc", - "infineon_cat1_adc": "adc", + "infineon_adc": "adc", + "infineon_autanalog_sar_adc": "adc", + "infineon_hppass_sar_adc": "adc", + "infineon_sar_adc": "adc", "infineon_xmc4xxx_adc": "adc", + "ite_it51xxx_adc": "adc", "ite_it8xxx2_adc": "adc", "lltc_ltc2451": "adc", "maxim_max11102": "adc", @@ -26,6 +39,8 @@ "maxim_max11117": "adc", "maxim_max11253": "adc", "maxim_max11254": "adc", + "microchip_adc_g1": "adc", + "microchip_mcp356xr": "adc", "microchip_xec_adc": "adc", "nordic_nrf_adc": "adc", "nordic_nrf_saadc": "adc", @@ -37,13 +52,23 @@ "nxp_lpc_lpadc": "adc", "nxp_mcux_12b1msps_sar": "adc", "nxp_s32_adc_sar": "adc", + "nxp_sar_adc": "adc", "nxp_vf610_adc": "adc", "raspberrypi_pico_adc": "adc", - "renesas_ra_adc": "adc", + "realtek_rts5912_adc": "adc", + "renesas_ra_adc12": "adc", + "renesas_ra_adc16": "adc", + "renesas_rx_adc": "adc", + "renesas_rz_adc": "adc", + "renesas_rz_adc_c": "adc", + "renesas_rz_adc_e": "adc", + "renesas_rza2m_adc": "adc", "renesas_smartbond_adc": "adc", "renesas_smartbond_sdadc": "adc", + "sifli_sf32lb_gpadc": "adc", "silabs_gecko_adc": "adc", - "silabs_gecko_iadc": "adc", + "silabs_iadc": "adc", + "silabs_siwx91x_adc": "adc", "st_stm32_adc": "adc", "st_stm32wb0_adc": "adc", "telink_b91_adc": "adc", @@ -55,10 +80,27 @@ "ti_ads1114": "adc", "ti_ads1115": "adc", "ti_ads1119": "adc", + "ti_ads114s06": "adc", "ti_ads114s08": "adc", + "ti_ads124s06": "adc", + "ti_ads124s08": "adc", "ti_ads131m02": "adc", "ti_ads7052": "adc", + "ti_ads7950": "adc", + "ti_ads7951": "adc", + "ti_ads7952": "adc", + "ti_ads7953": "adc", + "ti_ads7954": "adc", + "ti_ads7955": "adc", + "ti_ads7956": "adc", + "ti_ads7957": "adc", + "ti_ads7958": "adc", + "ti_ads7959": "adc", + "ti_ads7960": "adc", + "ti_ads7961": "adc", + "ti_am335x_adc": "adc", "ti_cc13xx_cc26xx_adc": "adc", + "ti_cc23x0_adc": "adc", "ti_cc32xx_adc": "adc", "ti_lmp90077": "adc", "ti_lmp90078": "adc", @@ -72,40 +114,65 @@ "ti_tla2022": "adc", "ti_tla2024": "adc", "vnd_adc": "adc", + "wch_adc": "adc", "zephyr_adc_emul": "adc", # # audio + "ambiq_pdm": "audio", + "cirrus_cs43l22": "audio", + "dlg_da7212": "audio", + "maxim_max98091": "audio", + "nordic_nrf_pdm": "audio", "nxp_dmic": "audio", + "nxp_micfil": "audio", + "sifli_sf32lb_audcodec": "audio", "st_mpxxdtyy": "audio", + "ti_pcm1681": "audio", "ti_tas6422dac": "audio", + "ti_tlv320aic3110": "audio", "ti_tlv320dac": "audio", "wolfson_wm8904": "audio", + "wolfson_wm8962": "audio", + # + # audio/mic_privacy/intel + "intel_adsp_mic_privacy": "audio/mic_privacy/intel", # # auxdisplay + "gpio_7_segment": "auxdisplay", "hit_hd44780": "auxdisplay", "jhd_jhd1313": "auxdisplay", "noritake_itron": "auxdisplay", "ptc_pt6314": "auxdisplay", "sparkfun_serlcd": "auxdisplay", + "titanmec_tm1637": "auxdisplay", # # bbram "ite_it8xxx2_bbram": "bbram", "microchip_xec_bbram": "bbram", + "motorola_mc146818_bbram": "bbram", "nuvoton_npcx_bbram": "bbram", + "realtek_rts5912_bbram": "bbram", "st_stm32_bbram": "bbram", "zephyr_bbram_emul": "bbram", # + # biometrics + "adh_tech_gt5x": "biometrics", + "zephyr_biometrics_emul": "biometrics", + "zhiantec_zfm_x0": "biometrics", + # # bluetooth/hci "ambiq_bt_hci_spi": "bluetooth/hci", "espressif_esp32_bt_hci": "bluetooth/hci", - "infineon_cat1_bless_hci": "bluetooth/hci", + "infineon_bless_hci": "bluetooth/hci", + "infineon_bt_hci_uart": "bluetooth/hci", "infineon_cyw208xx_hci": "bluetooth/hci", - "infineon_cyw43xxx_bt_hci": "bluetooth/hci", "nxp_bt_hci_uart": "bluetooth/hci", "nxp_hci_ble": "bluetooth/hci", "renesas_bt_hci_da1453x": "bluetooth/hci", "renesas_bt_hci_da1469x": "bluetooth/hci", + "sifli_sf32lb_mailbox": "bluetooth/hci", "silabs_bt_hci_efr32": "bluetooth/hci", + "silabs_siwx91x_bt_hci": "bluetooth/hci", "st_hci_spi_v1": "bluetooth/hci", "st_hci_spi_v2": "bluetooth/hci", "st_hci_stm32wb0": "bluetooth/hci", @@ -117,7 +184,11 @@ "zephyr_bt_hci_uart": "bluetooth/hci", "zephyr_bt_hci_userchan": "bluetooth/hci", # + # cache + "bflb_l1c": "cache", + # # can + "adi_max32_can": "can", "atmel_sam0_can": "can", "atmel_sam_can": "can", "espressif_esp32_twai": "can", @@ -132,6 +203,7 @@ "nxp_s32_canxl": "can", "renesas_ra_canfd": "can", "renesas_rcar_can": "can", + "renesas_rz_canfd": "can", "st_stm32_bxcan": "can", "st_stm32_fdcan": "can", "st_stm32h7_fdcan": "can", @@ -145,21 +217,43 @@ # # charger "maxim_max20335_charger": "charger", + "nxp_pca9422_charger": "charger", + "nxp_pf1550_charger": "charger", "sbs_sbs_charger": "charger", "ti_bq24190": "charger", "ti_bq25180": "charger", + "ti_bq25186": "charger", + "ti_bq25188": "charger", + "ti_bq25713": "charger", + "x_powers_axp2101_charger": "charger", + "zephyr_charger_gpio": "charger", # # clock_control "adi_max32_gcr": "clock_control", + "alif_clockctrl": "clock_control", "ambiq_clkctrl": "clock_control", "arm_beetle_syscon": "clock_control", "arm_scmi_clock": "clock_control", "aspeed_ast10x0_clock": "clock_control", "atmel_sam_pmc": "clock_control", - "espressif_esp32_rtc": "clock_control", + "bflb_bl60x_clock_controller": "clock_control", + "bflb_bl61x_clock_controller": "clock_control", + "bflb_bl70x_clock_controller": "clock_control", + "espressif_esp32_clock": "clock_control", "fixed_clock": "clock_control", + "focaltech_ft9001_cpm": "clock_control", "gd_gd32_cctl": "clock_control", + "infineon_fixed_clock": "clock_control", + "infineon_fixed_factor_clock": "clock_control", + "infineon_peri_div": "clock_control", "intel_agilex5_clock": "clock_control", + "ite_it51xxx_ecpm": "clock_control", + "microchip_pic32cm_jh_clock": "clock_control", + "microchip_pic32cm_pl_clock": "clock_control", + "microchip_pic32cz_ca_clock": "clock_control", + "microchip_sam_d5x_e5x_clock": "clock_control", + "microchip_sam_pmc": "clock_control", + "microchip_sama7g5_sckc": "clock_control", "microchip_xec_pcr": "clock_control", "nordic_nrf54h_hfxo": "clock_control", "nordic_nrf_auxpll": "clock_control", @@ -167,7 +261,9 @@ "nordic_nrf_fll16m": "clock_control", "nordic_nrf_hsfll_global": "clock_control", "nordic_nrf_hsfll_local": "clock_control", + "nordic_nrf_iron_hsfll_local": "clock_control", "nordic_nrf_lfclk": "clock_control", + "nordic_nrfs_audiopll": "clock_control", "nuvoton_npcm_pcc": "clock_control", "nuvoton_npcx_pcc": "clock_control", "nuvoton_numaker_scc": "clock_control", @@ -179,27 +275,51 @@ "nxp_kinetis_sim": "clock_control", "nxp_lpc11u6x_syscon": "clock_control", "nxp_lpc_syscon": "clock_control", + "nxp_mc_cgm": "clock_control", "nxp_s32_clock": "clock_control", "nxp_scg_k4": "clock_control", "openisa_rv32m1_pcc": "clock_control", "pwm_clock": "clock_control", "raspberrypi_pico_clock_controller": "clock_control", + "realtek_bee_cctl": "clock_control", + "realtek_rts5817_clock": "clock_control", "realtek_rts5912_sccon": "clock_control", "renesas_r8a7795_cpg_mssr": "clock_control", "renesas_r8a779f0_cpg_mssr": "clock_control", + "renesas_ra_cgc_subclk": "clock_control", + "renesas_rx_cgc_pclk": "clock_control", + "renesas_rx_cgc_pll": "clock_control", + "renesas_rx_cgc_root_clock": "clock_control", + "renesas_rz_cgc": "clock_control", + "renesas_rz_cpg": "clock_control", + "renesas_rza2m_cpg": "clock_control", + "sifli_sf32lb_hxt48": "clock_control", + "sifli_sf32lb_rcc_clk": "clock_control", "silabs_series_clock": "clock_control", "silabs_si32_ahb": "clock_control", "silabs_si32_apb": "clock_control", "silabs_si32_pll": "clock_control", + "silabs_siwx91x_clock": "clock_control", "smartbond_clock": "clock_control", "st_stm32_clock_mco": "clock_control", "st_stm32_clock_mux": "clock_control", "st_stm32f1_clock_mco": "clock_control", + "ti_k2g_sci_clk": "clock_control", "wch_rcc": "clock_control", # # comparator + "ite_it51xxx_vcmp": "comparator", + "microchip_ac_g1_comparator": "comparator", "nordic_nrf_comp": "comparator", "nordic_nrf_lpcomp": "comparator", + "nxp_acomp": "comparator", + "nxp_cmp": "comparator", + "nxp_hscmp": "comparator", + "renesas_ra_acmphs": "comparator", + "renesas_ra_lvd": "comparator", + "renesas_rx_lvd": "comparator", + "silabs_acmp": "comparator", + "st_stm32_comp": "comparator", "zephyr_fake_comp": "comparator", # # coredump @@ -208,21 +328,34 @@ # counter "adi_max32_counter": "counter", "adi_max32_rtc_counter": "counter", + "adi_max32_wut": "counter", "ambiq_counter": "counter", "andestech_atcpit100": "counter", "arm_cmsdk_dtimer": "counter", "arm_cmsdk_timer": "counter", "atmel_sam0_tc32": "counter", "atmel_sam_tc": "counter", + "espressif_esp32_counter": "counter", "espressif_esp32_rtc_timer": "counter", - "espressif_esp32_timer": "counter", "gd_gd32_timer": "counter", - "infineon_cat1_counter": "counter", + "infineon_counter": "counter", + "infineon_tcpwm_counter": "counter", + "ite_it51xxx_counter": "counter", + "ite_it8xxx2_counter": "counter", "maxim_ds3231": "counter", "microchip_mcp7940n": "counter", + "microchip_sam_pit64b_counter": "counter", + "microchip_tc_g1_counter": "counter", + "microchip_tcc_g1_counter": "counter", "microchip_xec_timer": "counter", + "microcrystal_rv3032_counter": "counter", + "neorv32_gptmr": "counter", "nordic_nrf_rtc": "counter", "nordic_nrf_timer": "counter", + "nuvoton_npck_lct": "counter", + "nuvoton_npcx_lct_v1": "counter", + "nuvoton_npcx_lct_v2": "counter", + "nxp_ftm": "counter", "nxp_imx_epit": "counter", "nxp_imx_gpt": "counter", "nxp_imx_qtmr": "counter", @@ -231,37 +364,68 @@ "nxp_lpc_ctimer": "counter", "nxp_lpc_rtc": "counter", "nxp_lpc_rtc_highres": "counter", + "nxp_lpit": "counter", "nxp_mrt": "counter", "nxp_pit": "counter", "nxp_rtc": "counter", + "nxp_rtc_jdp": "counter", "nxp_s32_sys_timer": "counter", + "nxp_stm": "counter", "nxp_tpm_timer": "counter", + "raspberrypi_pico_pit": "counter", + "raspberrypi_pico_pit_channel": "counter", "raspberrypi_pico_timer": "counter", + "realtek_rts5912_slwtimer": "counter", + "realtek_rts5912_timer": "counter", "renesas_ra_agt_counter": "counter", + "renesas_rz_cmtw_counter": "counter", + "renesas_rz_gtm_counter": "counter", "renesas_smartbond_timer": "counter", + "silabs_burtc_counter": "counter", "silabs_gecko_rtcc": "counter", - "silabs_gecko_stimer": "counter", + "silabs_timer_counter": "counter", "snps_dw_timers": "counter", "st_stm32_counter": "counter", + "ti_cc23x0_lgpt": "counter", + "ti_cc23x0_rtc": "counter", + "ti_mspm0_timer_counter": "counter", "xlnx_xps_timer_1_00_a": "counter", - "zephyr_native_posix_counter": "counter", + "zephyr_native_sim_counter": "counter", + # + # crc + "nxp_crc": "crc", + "nxp_lpc_crc": "crc", + "renesas_ra_crc": "crc", + "sifli_sf32lb_crc": "crc", # # crypto "atmel_ataes132a": "crypto", + "espressif_esp32_aes": "crypto", + "espressif_esp32_sha": "crypto", "intel_adsp_sha": "crypto", + "ite_it51xxx_sha": "crypto", "ite_it8xxx2_sha": "crypto", "ite_it8xxx2_sha_v2": "crypto", + "microchip_sha_g1_crypto": "crypto", "microchip_xec_symcr": "crypto", "nordic_nrf_ecb": "crypto", "nuvoton_npcx_sha": "crypto", "nxp_mcux_dcp": "crypto", + "nxp_s32_crypto_hse_mu": "crypto", + "raspberrypi_pico_sha256": "crypto", + "realtek_rts5912_sha": "crypto", "renesas_smartbond_crypto": "crypto", "silabs_si32_aes": "crypto", "st_stm32_aes": "crypto", "st_stm32_cryp": "crypto", + "st_stm32_hash": "crypto", + "ti_cc23x0_aes": "crypto", # # dac "adi_ad559x_dac": "dac", + "adi_ad5601": "dac", + "adi_ad5611": "dac", + "adi_ad5621": "dac", "adi_ad5628": "dac", "adi_ad5648": "dac", "adi_ad5668": "dac", @@ -279,15 +443,21 @@ "adi_max22017_dac": "dac", "atmel_sam0_dac": "dac", "atmel_sam_dac": "dac", + "atmel_samd5x_dac": "dac", "espressif_esp32_dac": "dac", "gd_gd32_dac": "dac", + "microchip_dac_g1": "dac", "microchip_mcp4725": "dac", "microchip_mcp4728": "dac", + "nxp_dac12": "dac", "nxp_gau_dac": "dac", "nxp_kinetis_dac": "dac", "nxp_kinetis_dac32": "dac", "nxp_lpdac": "dac", + "renesas_ra_dac": "dac", + "silabs_vdac": "dac", "st_stm32_dac": "dac", + "ti_dac161s997": "dac", "ti_dacx0501": "dac", "vnd_dac": "dac", # @@ -303,9 +473,19 @@ # dai/intel/ssp "intel_ssp_dai": "dai/intel/ssp", # + # dai/nxp/esai + "nxp_dai_esai": "dai/nxp/esai", + # + # dai/nxp/micfil + "nxp_dai_micfil": "dai/nxp/micfil", + # # dai/nxp/sai "nxp_dai_sai": "dai/nxp/sai", # + # debug + "nordic_coresight_nrf": "debug", + "silabs_pti": "debug", + # # disk "st_stm32_sdmmc": "disk", "zephyr_flash_disk": "disk", @@ -317,42 +497,52 @@ "nvme_controller": "disk/nvme", # # display + "chipone_co5300": "display", "frida_nt35510": "display", "galaxycore_gc9x01x": "display", + "himax_hx8379c": "display", "himax_hx8394": "display", "ilitek_ili9806e_dsi": "display", "intel_multiboot_framebuffer": "display", "istech_ist3931": "display", + "jdi_lpm013m126": "display", "led_strip_matrix": "display", "maxim_max7219": "display", "nxp_dcnano_lcdif": "display", "nxp_imx_elcdif": "display", + "nxp_imx_lcdifv3": "display", "orisetech_otm8009a": "display", "raydium_rm67162": "display", "raydium_rm68200": "display", "renesas_ra_glcdc": "display", "renesas_smartbond_display": "display", "sharp_ls0xx": "display", + "sitronix_st75256": "display", + "sitronix_st7701": "display", "sitronix_st7735r": "display", "sitronix_st7789v": "display", "sitronix_st7796s": "display", "solomon_ssd1322": "display", "st_stm32_ltdc": "display", + "waveshare_dsi2dpi": "display", "zephyr_dummy_dc": "display", + "zephyr_hub12": "display", "zephyr_sdl_dc": "display", # # dma "adi_max32_dma": "dma", "altr_msgdma": "dma", - "andestech_atcdmac300": "dma", + "andestech_atcdmacx00": "dma", + "arm_dma_pl330": "dma", "atmel_sam0_dmac": "dma", "atmel_sam_xdmac": "dma", + "bflb_dma": "dma", "brcm_iproc_pax_dma_v1": "dma", "brcm_iproc_pax_dma_v2": "dma", "espressif_esp32_gdma": "dma", "gd_gd32_dma": "dma", "gd_gd32_dma_v1": "dma", - "infineon_cat1_dma": "dma", + "infineon_dma": "dma", "infineon_xmc4xxx_dma": "dma", "intel_adsp_gpdma": "dma", "intel_adsp_hda_host_in": "dma", @@ -361,7 +551,11 @@ "intel_adsp_hda_link_out": "dma", "intel_lpss": "dma", "intel_sedi_dma": "dma", + "microchip_dmac_g1_dma": "dma", "microchip_xec_dmac": "dma", + "nuvoton_npcx_gdma": "dma", + "nxp_4ch_dma": "dma", + "nxp_edma": "dma", "nxp_lpc_dma": "dma", "nxp_mcux_edma": "dma", "nxp_pxp": "dma", @@ -369,9 +563,15 @@ "nxp_smartdma": "dma", "nxp_sof_host_dma": "dma", "raspberrypi_pico_dma": "dma", + "renesas_ra_dma": "dma", + "renesas_rz_dmac": "dma", + "renesas_rz_dmac_b": "dma", "renesas_smartbond_dma": "dma", + "sifli_sf32lb_dmac": "dma", + "silabs_gpdma": "dma", "silabs_ldma": "dma", "silabs_si32_dma": "dma", + "silabs_siwx91x_dma": "dma", "snps_designware_dma": "dma", "snps_designware_dma_axi": "dma", "st_stm32_bdma": "dma", @@ -380,6 +580,8 @@ "st_stm32_dma_v2bis": "dma", "st_stm32_dmamux": "dma", "st_stm32u5_dma": "dma", + "ti_cc23x0_dma": "dma", + "wch_wch_dma": "dma", "xlnx_axi_dma_1_00_a": "dma", "xlnx_eth_dma": "dma", "zephyr_dma_emul": "dma", @@ -389,40 +591,57 @@ # # edac "intel_ibecc": "edac", + "nxp_erm": "edac", + "xlnx_zynqmp_ddrc_2_40a": "edac", # # eeprom "atmel_at24": "eeprom", "fujitsu_mb85rcxx": "eeprom", "fujitsu_mb85rsxx": "eeprom", + "infineon_fm25xxx": "eeprom", "microchip_xec_eeprom": "eeprom", "nxp_lpc11u6x_eeprom": "eeprom", "st_stm32_eeprom": "eeprom", - "ti_tmp116_eeprom": "eeprom", + "ti_tmp11x_eeprom": "eeprom", "zephyr_emu_eeprom": "eeprom", "zephyr_fake_eeprom": "eeprom", "zephyr_sim_eeprom": "eeprom", # # entropy "adi_max32_trng": "entropy", + "adi_maxq10xx_trng": "entropy", + "ambiq_puf_trng": "entropy", "atmel_sam_trng": "entropy", + "brcm_iproc_rng200": "entropy", "espressif_esp32_trng": "entropy", + "gd_gd32_trng": "entropy", "litex_prbs": "entropy", + "microchip_trng_g1_entropy": "entropy", "neorv32_trng": "entropy", + "nordic_nrf_cracen_ctrdrbg": "entropy", "nordic_nrf_rng": "entropy", "nuvoton_npcx_drbg": "entropy", + "nxp_ele_trng": "entropy", "nxp_imx_caam": "entropy", "nxp_kinetis_rnga": "entropy", "nxp_kinetis_trng": "entropy", "nxp_lpc_rng": "entropy", "openisa_rv32m1_trng": "entropy", + "raspberrypi_pico_rng": "entropy", "renesas_smartbond_trng": "entropy", + "sensry_sy1xx_trng": "entropy", + "sifli_sf32lb_trng": "entropy", "silabs_gecko_semailbox": "entropy", "silabs_gecko_trng": "entropy", + "silabs_siwx91x_rng": "entropy", "st_stm32_rng": "entropy", + "st_stm32_rng_noirq": "entropy", "telink_b91_trng": "entropy", "ti_cc13xx_cc26xx_trng": "entropy", + "ti_mspm0_trng": "entropy", + "virtio_device4": "entropy", "zephyr_bt_hci_entropy": "entropy", - "zephyr_native_posix_rng": "entropy", + "zephyr_native_sim_rng": "entropy", "zephyr_psa_crypto_rng": "entropy", # # espi @@ -435,6 +654,7 @@ "nuvoton_npcx_espi": "espi", "nuvoton_npcx_espi_taf": "espi", "nuvoton_npcx_host_sub": "espi", + "realtek_rts5912_espi": "espi", "zephyr_espi_emul_controller": "espi", # # ethernet @@ -448,26 +668,40 @@ "litex_liteeth": "ethernet", "microchip_enc28j60": "ethernet", "microchip_enc424j600": "ethernet", - "microchip_ksz8794": "ethernet", - "microchip_ksz8863": "ethernet", "microchip_lan865x": "ethernet", "microchip_lan9250": "ethernet", "nuvoton_numaker_ethernet": "ethernet", - "nxp_imx_netc_psi": "ethernet", + "nxp_enet": "ethernet", + "nxp_enet1g": "ethernet", + "nxp_enet_mac": "ethernet", "nxp_s32_gmac": "ethernet", "nxp_s32_netc_psi": "ethernet", "nxp_s32_netc_vsi": "ethernet", "renesas_ra_ethernet": "ethernet", + "sensry_sy1xx_mac": "ethernet", "siemens_ivshmem_eth": "ethernet", "silabs_gecko_ethernet": "ethernet", "smsc_lan91c111": "ethernet", + "smsc_lan91c111_mdio": "ethernet", "smsc_lan9220": "ethernet", "snps_designware_ethernet": "ethernet", "snps_ethernet_cyclonev": "ethernet", "st_stm32_ethernet": "ethernet", "ti_stellaris_ethernet": "ethernet", + "virtio_net": "ethernet", "vnd_ethernet": "ethernet", "wiznet_w5500": "ethernet", + "wiznet_w6100": "ethernet", + "xlnx_axi_ethernet_1_00_a": "ethernet", + "xlnx_gem": "ethernet", + "xlnx_xps_ethernetlite_1_00_a_mac": "ethernet", + "xlnx_xps_ethernetlite_3_00_a_mac": "ethernet", + # + # ethernet/dsa + "microchip_ksz8463": "ethernet/dsa", + "microchip_ksz8794": "ethernet/dsa", + "microchip_ksz8863": "ethernet/dsa", + "nxp_netc_switch": "ethernet/dsa", # # ethernet/dwc_xgmac "snps_dwcxgmac": "ethernet/dwc_xgmac", @@ -476,57 +710,102 @@ "nxp_enet_qos": "ethernet/eth_nxp_enet_qos", "nxp_enet_qos_mac": "ethernet/eth_nxp_enet_qos", # - # ethernet/nxp_enet - "nxp_enet": "ethernet/nxp_enet", - "nxp_enet1g": "ethernet/nxp_enet", - "nxp_enet_mac": "ethernet/nxp_enet", - "nxp_kinetis_ethernet": "ethernet/nxp_enet", + # ethernet/intel + "intel_eth_plat": "ethernet/intel", + "intel_igc_mac": "ethernet/intel", + # + # ethernet/mdio + "adi_adin2111_mdio": "ethernet/mdio", + "atmel_sam_mdio": "ethernet/mdio", + "espressif_esp32_mdio": "ethernet/mdio", + "infineon_xmc4xxx_mdio": "ethernet/mdio", + "intel_igc_mdio": "ethernet/mdio", + "litex_liteeth_mdio": "ethernet/mdio", + "microchip_lan865x_mdio": "ethernet/mdio", + "nxp_enet_mdio": "ethernet/mdio", + "nxp_enet_qos_mdio": "ethernet/mdio", + "nxp_imx_netc_emdio": "ethernet/mdio", + "nxp_s32_gmac_mdio": "ethernet/mdio", + "nxp_s32_netc_emdio": "ethernet/mdio", + "renesas_ra_mdio": "ethernet/mdio", + "sensry_sy1xx_mdio": "ethernet/mdio", + "snps_dwcxgmac_mdio": "ethernet/mdio", + "st_stm32_mdio": "ethernet/mdio", + "xlnx_axi_ethernet_1_00_a_mdio": "ethernet/mdio", + "xlnx_xps_ethernetlite_1_00_a_mdio": "ethernet/mdio", + "xlnx_xps_ethernetlite_3_00_a_mdio": "ethernet/mdio", + "zephyr_mdio_gpio": "ethernet/mdio", + # + # ethernet/nxp_imx_netc + "nxp_imx_netc_blk_ctrl": "ethernet/nxp_imx_netc", + "nxp_imx_netc_psi": "ethernet/nxp_imx_netc", # # ethernet/phy "adi_adin1100_phy": "ethernet/phy", "adi_adin2111_phy": "ethernet/phy", "davicom_dm8806_phy": "ethernet/phy", "ethernet_phy": "ethernet/phy", + "ethernet_phy_fixed_link": "ethernet/phy", "microchip_ksz8081": "ethernet/phy", + "microchip_ksz9131": "ethernet/phy", + "microchip_lan8742": "ethernet/phy", "microchip_t1s_phy": "ethernet/phy", "microchip_vsc8541": "ethernet/phy", + "motorcomm_yt8521": "ethernet/phy", "nxp_tja1103": "ethernet/phy", + "nxp_tja11xx": "ethernet/phy", "qca_ar8031": "ethernet/phy", "realtek_rtl8211f": "ethernet/phy", "ti_dp83825": "ethernet/phy", + "ti_dp83867": "ethernet/phy", # # firmware/scmi + "arm_scmi": "firmware/scmi", "arm_scmi_shmem": "firmware/scmi", # + # firmware/tisci + "ti_k2g_sci": "firmware/tisci", + # # flash "adi_max32_flash_controller": "flash", - "altr_nios2_qspi_nor": "flash", + "adi_max32_spixf_nor": "flash", "ambiq_flash_controller": "flash", "andestech_qspi_nor": "flash", + "andestech_qspi_nor_xip": "flash", + "atmel_at25xv021a": "flash", "atmel_at45": "flash", "atmel_sam0_nvmctrl": "flash", "atmel_sam_flash_controller": "flash", + "bflb_flash_controller": "flash", "cdns_nand": "flash", "cdns_qspi_nor": "flash", "espressif_esp32_flash_controller": "flash", "gd_gd32_flash_controller": "flash", - "infineon_cat1_flash_controller": "flash", - "infineon_cat1_qspi_flash": "flash", + "infineon_flash_controller": "flash", + "infineon_qspi_flash": "flash", "infineon_xmc4xxx_flash_controller": "flash", + "ite_it51xxx_manual_flash_1k": "flash", "ite_it8xxx2_flash_controller": "flash", + "jedec_mspi_nor": "flash", + "jedec_spi_nand": "flash", "jedec_spi_nor": "flash", + "microchip_nvmctrl_g1_flash": "flash", "mspi_atxp032": "flash", + "mspi_is25xx0xx": "flash", + "netsol_s3axx04": "flash", "nordic_mram": "flash", "nordic_nrf51_flash_controller": "flash", "nordic_nrf52_flash_controller": "flash", "nordic_nrf53_flash_controller": "flash", "nordic_nrf91_flash_controller": "flash", + "nordic_nrf_mramc": "flash", "nordic_qspi_nor": "flash", "nordic_rram_controller": "flash", "nuvoton_npcx_fiu_nor": "flash", "nuvoton_npcx_fiu_qspi": "flash", "nuvoton_numaker_fmc": "flash", "nuvoton_numaker_rmc": "flash", + "nxp_c40_flash_controller": "flash", "nxp_iap_fmc11": "flash", "nxp_iap_fmc54": "flash", "nxp_iap_fmc55": "flash", @@ -535,18 +814,31 @@ "nxp_imx_flexspi_mx25um51345g": "flash", "nxp_imx_flexspi_nor": "flash", "nxp_kinetis_ftfa": "flash", + "nxp_kinetis_ftfc": "flash", "nxp_kinetis_ftfe": "flash", "nxp_kinetis_ftfl": "flash", "nxp_msf1": "flash", + "nxp_s32_qspi_hyperflash": "flash", "nxp_s32_qspi_nor": "flash", + "nxp_s32_xspi_hyperram": "flash", + "nxp_xspi_nor": "flash", "openisa_rv32m1_ftfe": "flash", "raspberrypi_pico_flash_controller": "flash", + "realtek_rts5912_flash_controller": "flash", "renesas_ra_flash_hp_controller": "flash", + "renesas_ra_flash_lp_controller": "flash", + "renesas_ra_mram_controller": "flash", "renesas_ra_ospi_b_nor": "flash", "renesas_ra_qspi_nor": "flash", + "renesas_rx_flash": "flash", + "renesas_rz_qspi_spibsc": "flash", + "renesas_rz_qspi_xspi": "flash", "renesas_smartbond_flash_controller": "flash", + "sifli_sf32lb_mpi_qspi_nor": "flash", "silabs_gecko_flash_controller": "flash", + "silabs_series2_flash_controller": "flash", "silabs_si32_flash_controller": "flash", + "silabs_siwx91x_flash_controller": "flash", "st_stm32_flash_controller": "flash", "st_stm32_ospi_nor": "flash", "st_stm32_qspi_nor": "flash", @@ -556,6 +848,7 @@ "st_stm32wba_flash_controller": "flash", "telink_b91_flash_controller": "flash", "ti_cc13xx_cc26xx_flash_controller": "flash", + "ti_cc23x0_flash_controller": "flash", "zephyr_mspi_emul_flash": "flash", "zephyr_sim_flash": "flash", # @@ -568,12 +861,27 @@ "renesas_slg47115": "fpga", "xlnx_fpga": "fpga", # + # fuel_gauge/axp2101 + "x_powers_axp2101_fuel_gauge": "fuel_gauge/axp2101", + # # fuel_gauge/bq27z746 "ti_bq27z746": "fuel_gauge/bq27z746", # + # fuel_gauge/bq40z50 + "ti_bq40z50": "fuel_gauge/bq40z50", + # # fuel_gauge/composite "zephyr_fuel_gauge_composite": "fuel_gauge/composite", # + # fuel_gauge/hy4245 + "hycon_hy4245": "fuel_gauge/hy4245", + # + # fuel_gauge/lc709203f + "onnn_lc709203f": "fuel_gauge/lc709203f", + # + # fuel_gauge/ltc2959 + "adi_ltc2959": "fuel_gauge/ltc2959", + # # fuel_gauge/max17048 "maxim_max17048": "fuel_gauge/max17048", # @@ -581,47 +889,63 @@ "sbs_sbs_gauge": "fuel_gauge/sbs_gauge", "sbs_sbs_gauge_new_api": "fuel_gauge/sbs_gauge", # + # fuel_gauge/sy24561 + "silergy_sy24561": "fuel_gauge/sy24561", + # # gnss "gnss_nmea_generic": "gnss", "luatos_air530z": "gnss", "quectel_lc26g": "gnss", "quectel_lc76g": "gnss", "quectel_lc86g": "gnss", - "u_blox_m8": "gnss", "zephyr_gnss_emul": "gnss", # + # gnss/u_blox + "u_blox_f9p": "gnss/u_blox", + "u_blox_m8": "gnss/u_blox", + # # gpio "adi_ad559x_gpio": "gpio", "adi_adp5585_gpio": "gpio", "adi_max14906_gpio": "gpio", + "adi_max14915_gpio": "gpio", "adi_max14916_gpio": "gpio", + "adi_max14917_gpio": "gpio", "adi_max22017_gpio": "gpio", "adi_max22190_gpio": "gpio", + "adi_max22199_gpio": "gpio", "adi_max32_gpio": "gpio", + "aesc_gpio": "gpio", "altr_pio_1_0": "gpio", "ambiq_gpio_bank": "gpio", "andestech_atcgpio100": "gpio", "arm_cmsdk_gpio": "gpio", + "arm_mmio32_gpio": "gpio", "atmel_sam0_gpio": "gpio", "atmel_sam4l_gpio": "gpio", "atmel_sam_gpio": "gpio", "awinic_aw9523b_gpio": "gpio", + "bflb_bl60x_70x_gpio": "gpio", + "bflb_bl61x_gpio": "gpio", "brcm_bcm2711_gpio": "gpio", "brcm_brcmstb_gpio": "gpio", "brcm_iproc_gpio": "gpio", "cypress_cy8c95xx_gpio_port": "gpio", "cypress_psoc6_gpio": "gpio", "efinix_sapphire_gpio": "gpio", + "ene_kb106x_gpio": "gpio", "ene_kb1200_gpio": "gpio", "espressif_esp32_gpio": "gpio", + "espressif_esp32_lpgpio": "gpio", "fcs_fxl6408": "gpio", "gaisler_grgpio": "gpio", "gd_gd32_gpio": "gpio", - "infineon_cat1_gpio": "gpio", + "infineon_gpio": "gpio", "infineon_tle9104_gpio": "gpio", "infineon_xmc4xxx_gpio": "gpio", "intel_gpio": "gpio", "intel_sedi_gpio": "gpio", + "ite_it51xxx_gpio": "gpio", "ite_it8801_gpio": "gpio", "ite_it8xxx2_gpio": "gpio", "ite_it8xxx2_gpio_v2": "gpio", @@ -636,12 +960,13 @@ "microchip_mcp23s09": "gpio", "microchip_mcp23s17": "gpio", "microchip_mcp23s18": "gpio", - "microchip_mec5_gpio": "gpio", "microchip_mpfs_gpio": "gpio", + "microchip_port_g1_gpio": "gpio", + "microchip_sam_pio4": "gpio", "microchip_xec_gpio": "gpio", - "microchip_xec_gpio_v2": "gpio", "neorv32_gpio": "gpio", "nordic_npm1300_gpio": "gpio", + "nordic_npm1304_gpio": "gpio", "nordic_npm2100_gpio": "gpio", "nordic_npm6001_gpio": "gpio", "nordic_nrf_gpio": "gpio", @@ -656,27 +981,38 @@ "nxp_kinetis_gpio": "gpio", "nxp_lpc11u6x_gpio": "gpio", "nxp_lpc_gpio_port": "gpio", + "nxp_pca6408": "gpio", + "nxp_pca6416": "gpio", "nxp_pca9538": "gpio", "nxp_pca9539": "gpio", "nxp_pca9554": "gpio", "nxp_pca9555": "gpio", "nxp_pca95xx": "gpio", + "nxp_pcal6408": "gpio", "nxp_pcal6408a": "gpio", + "nxp_pcal6416": "gpio", "nxp_pcal6416a": "gpio", "nxp_pcal6524": "gpio", "nxp_pcal6534": "gpio", + "nxp_pcal9538": "gpio", + "nxp_pcal9539": "gpio", + "nxp_pcal9722": "gpio", "nxp_pcf857x": "gpio", - "nxp_s32_gpio": "gpio", "nxp_sc18im704_gpio": "gpio", + "nxp_sc18is606_gpio": "gpio", + "nxp_siul2_gpio": "gpio", "openisa_rv32m1_gpio": "gpio", "quicklogic_eos_s3_gpio": "gpio", - "raspberrypi_pico_gpio": "gpio", + "raspberrypi_pico_gpio_port": "gpio", "raspberrypi_rp1_gpio": "gpio", + "realtek_ameba_gpio": "gpio", + "realtek_bee_gpio": "gpio", "realtek_rts5912_gpio": "gpio", - "renesas_ra_gpio": "gpio", "renesas_ra_gpio_ioport": "gpio", "renesas_rcar_gpio": "gpio", + "renesas_rx_gpio": "gpio", "renesas_rz_gpio": "gpio", + "renesas_rza2m_gpio": "gpio", "renesas_rzt2m_gpio": "gpio", "renesas_smartbond_gpio": "gpio", "richtek_rt1718s": "gpio", @@ -685,18 +1021,25 @@ "semtech_sx1509b": "gpio", "sensry_sy1xx_gpio": "gpio", "sifive_gpio0": "gpio", + "sifli_sf32lb_gpio": "gpio", "silabs_gecko_gpio_port": "gpio", + "silabs_gpio": "gpio", "silabs_si32_gpio": "gpio", + "silabs_siwx91x_gpio": "gpio", + "silabs_siwx91x_gpio_uulp": "gpio", "snps_creg_gpio": "gpio", "snps_designware_gpio": "gpio", + "st_mfxstm32l152": "gpio", "st_stm32_gpio": "gpio", "st_stmpe1600": "gpio", "telink_b91_gpio": "gpio", - "ti_ads114s0x_gpio": "gpio", + "ti_ads1x4s0x_gpio": "gpio", "ti_cc13xx_cc26xx_gpio": "gpio", + "ti_cc23x0_gpio": "gpio", "ti_cc32xx_gpio": "gpio", "ti_davinci_gpio": "gpio", "ti_lmp90xxx_gpio": "gpio", + "ti_mspm0_gpio": "gpio", "ti_sn74hc595": "gpio", "ti_stellaris_gpio": "gpio", "ti_tca6424a": "gpio", @@ -710,11 +1053,15 @@ "zephyr_gpio_emul": "gpio", "zephyr_gpio_emul_sdl": "gpio", # - # haptics - "ti_drv2605": "haptics", + # haptics/cirrus + "cirrus_cs40l5x": "haptics/cirrus", + # + # haptics/ti + "ti_drv2605": "haptics/ti", # # hdlc_rcp_if "nxp_hdlc_rcp_if": "hdlc_rcp_if", + "spi_hdlc_rcp_if": "hdlc_rcp_if", "uart_hdlc_rcp_if": "hdlc_rcp_if", # # hwinfo @@ -728,7 +1075,9 @@ "nxp_lpc_uid": "hwinfo", # # hwspinlock + "nxp_sema42": "hwspinlock", "sqn_hwspinlock": "hwspinlock", + "vnd_hwspinlock": "hwspinlock", # # i2c "adi_max32_i2c": "i2c", @@ -740,47 +1089,68 @@ "atmel_sam_i2c_twi": "i2c", "atmel_sam_i2c_twihs": "i2c", "atmel_sam_i2c_twim": "i2c", + "bflb_i2c": "i2c", "brcm_iproc_i2c": "i2c", + "cdns_i2c": "i2c", "ene_kb1200_i2c": "i2c", "espressif_esp32_i2c": "i2c", "fsl_imx21_i2c": "i2c", "gd_gd32_i2c": "i2c", "gpio_i2c": "i2c", "gpio_i2c_switch": "i2c", - "infineon_cat1_i2c": "i2c", + "infineon_i2c": "i2c", "infineon_xmc4xxx_i2c": "i2c", "intel_sedi_i2c": "i2c", "ite_enhance_i2c": "i2c", + "ite_it51xxx_i2c": "i2c", "ite_it8xxx2_i2c": "i2c", "litex_i2c": "i2c", + "litex_litei2c": "i2c", "microchip_mpfs_i2c": "i2c", + "microchip_sercom_g1_i2c": "i2c", "microchip_xec_i2c": "i2c", "microchip_xec_i2c_v2": "i2c", + "nordic_nrf_twi": "i2c", + "nordic_nrf_twim": "i2c", "nordic_nrf_twis": "i2c", "nuvoton_npcx_i2c_ctrl": "i2c", "nuvoton_npcx_i2c_port": "i2c", "nuvoton_numaker_i2c": "i2c", + "nxp_ii2c": "i2c", "nxp_kinetis_i2c": "i2c", "nxp_lpc11u6x_i2c": "i2c", "nxp_lpc_i2c": "i2c", "nxp_lpi2c": "i2c", "nxp_sc18im704_i2c": "i2c", "openisa_rv32m1_lpi2c": "i2c", + "realtek_rts5912_i2c": "i2c", + "renesas_ra_i2c_sci": "i2c", + "renesas_ra_i2c_sci_b": "i2c", "renesas_ra_iic": "i2c", "renesas_rcar_i2c": "i2c", + "renesas_rx_i2c": "i2c", + "renesas_rz_iic": "i2c", + "renesas_rz_riic": "i2c", + "renesas_rza2m_riic": "i2c", "renesas_smartbond_i2c": "i2c", + "sensry_sy1xx_i2c": "i2c", "sifive_i2c0": "i2c", + "sifli_sf32lb_i2c": "i2c", "silabs_gecko_i2c": "i2c", + "silabs_i2c": "i2c", + "snps_designware_i2c": "i2c", "st_stm32_i2c_v1": "i2c", "st_stm32_i2c_v2": "i2c", "telink_b91_i2c": "i2c", "ti_cc13xx_cc26xx_i2c": "i2c", + "ti_cc23x0_i2c": "i2c", "ti_cc32xx_i2c": "i2c", "ti_omap_i2c": "i2c", "ti_tca9544a": "i2c", "ti_tca9546a": "i2c", "ti_tca9548a": "i2c", "vnd_i2c": "i2c", + "wch_i2c": "i2c", "xlnx_xps_iic_2_00_a": "i2c", "xlnx_xps_iic_2_1": "i2c", "zephyr_i2c_emul_controller": "i2c", @@ -789,26 +1159,42 @@ "zephyr_i2c_target_eeprom": "i2c/target", # # i2s + "adi_max32_i2s": "i2s", + "ambiq_i2s": "i2s", "atmel_sam_ssc": "i2s", "espressif_esp32_i2s": "i2s", + "infineon_i2s": "i2s", + "nordic_nrf_i2s": "i2s", "nxp_lpc_i2s": "i2s", "nxp_mcux_i2s": "i2s", + "renesas_ra_i2s_ssie": "i2s", + "silabs_siwx91x_i2s": "i2s", "st_stm32_i2s": "i2s", + "st_stm32_sai": "i2s", "vnd_i2s": "i2s", + "zephyr_i2s_sdl": "i2s", # # i3c + "adi_max32_i3c": "i3c", "cdns_i3c": "i3c", + "ite_it51xxx_i3cm": "i3c", + "ite_it51xxx_i3cs": "i3c", "nuvoton_npcx_i3c": "i3c", "nxp_mcux_i3c": "i3c", + "renesas_ra_i3c": "i3c", + "snps_designware_i3c": "i3c", "st_stm32_i3c": "i3c", "vnd_i3c": "i3c", # # ieee802154 "atmel_rf2xx": "ieee802154", "decawave_dw1000": "ieee802154", + "espressif_esp32_ieee802154": "ieee802154", "nordic_nrf_ieee802154": "ieee802154", "nxp_kw41z_ieee802154": "ieee802154", "nxp_mcr20a": "ieee802154", + "nxp_mcxw_ieee802154": "ieee802154", + "st_stm32wba_ieee802154": "ieee802154", "telink_b91_zb": "ieee802154", "ti_cc1200": "ieee802154", "ti_cc13xx_cc26xx_ieee802154": "ieee802154", @@ -819,10 +1205,15 @@ # input "adc_keys": "input", "analog_axis": "input", + "arduino_modulino_buttons": "input", + "bflb_irx": "input", + "chipsemi_chsc5x": "input", "chipsemi_chsc6x": "input", "cirque_pinnacle": "input", + "cypress_cy8cmbr3xxx": "input", "espressif_esp32_touch": "input", "focaltech_ft5336": "input", + "focaltech_ft6146": "input", "futaba_sbus": "input", "goodix_gt911": "input", "gpio_kbd_matrix": "input", @@ -830,22 +1221,35 @@ "gpio_qdec": "input", "hynitron_cst816s": "input", "ilitek_ili2132a": "input", + "ite_it51xxx_kbd": "input", "ite_it8801_kbd": "input", "ite_it8xxx2_kbd": "input", "microchip_cap12xx": "input", "microchip_xec_kbd": "input", "nintendo_nunchuk": "input", "nuvoton_npcx_kbd": "input", + "nxp_mcux_kpp": "input", + "parade_tma525b": "input", "pixart_pat912x": "input", "pixart_paw32xx": "input", "pixart_pmw3610": "input", + "realtek_rts5912_kbd": "input", + "renesas_ra_ctsu": "input", + "renesas_ra_ctsu_button": "input", + "renesas_ra_ctsu_slider": "input", + "renesas_ra_ctsu_wheel": "input", + "renesas_rx_ctsu": "input", "sitronix_cf1133": "input", + "st_stm32_tsc": "input", "st_stmpe811": "input", + "vishay_vs1838b": "input", + "wch_ch9350l": "input", "xptek_xpt2046": "input", "zephyr_input_sdl_touch": "input", "zephyr_native_linux_evdev": "input", # # interrupt_controller + "adi_max32_rv32_intc": "interrupt_controller", "arm_gic_v1": "interrupt_controller", "arm_gic_v2": "interrupt_controller", "arm_gic_v3": "interrupt_controller", @@ -853,24 +1257,33 @@ "atmel_sam0_eic": "interrupt_controller", "gaisler_irqmp": "interrupt_controller", "gd_gd32_exti": "interrupt_controller", + "hazard3_hazard3_intc": "interrupt_controller", "infineon_xmc4xxx_intc": "interrupt_controller", "intel_ace_intc": "interrupt_controller", "intel_cavs_intc": "interrupt_controller", "intel_ioapic": "interrupt_controller", "intel_loapic": "interrupt_controller", "intel_vt_d": "interrupt_controller", + "ite_it51xxx_wuc": "interrupt_controller", "ite_it8xxx2_wuc": "interrupt_controller", "litex_vexriscv_intc0": "interrupt_controller", "mediatek_adsp_intc": "interrupt_controller", + "microchip_eic_g1_intc": "interrupt_controller", "microchip_xec_ecia": "interrupt_controller", "nuclei_eclic": "interrupt_controller", "nuvoton_npcx_miwu": "interrupt_controller", + "nxp_gint": "interrupt_controller", "nxp_irqsteer_intc": "interrupt_controller", "nxp_pint": "interrupt_controller", - "nxp_s32_siul2_eirq": "interrupt_controller", "nxp_s32_wkpu": "interrupt_controller", + "nxp_siul2_eirq": "interrupt_controller", "openisa_rv32m1_intmux": "interrupt_controller", - "renesas_ra_interrupt_controller_unit": "interrupt_controller", + "renesas_rx_grp_intc": "interrupt_controller", + "renesas_rx_icu": "interrupt_controller", + "renesas_rz_ext_irq": "interrupt_controller", + "renesas_rz_tint": "interrupt_controller", + "riscv_clic": "interrupt_controller", + "riscv_imsic": "interrupt_controller", "shared_irq": "interrupt_controller", "sifive_plic_1_0_0": "interrupt_controller", "snps_arcv2_intc": "interrupt_controller", @@ -878,6 +1291,7 @@ "st_stm32wb0_gpio_intc": "interrupt_controller", "swerv_pic": "interrupt_controller", "ti_vim": "interrupt_controller", + "wch_exti": "interrupt_controller", "wch_pfic": "interrupt_controller", # # ipm @@ -893,20 +1307,23 @@ "xlnx_zynqmp_ipi_mailbox": "ipm", "zephyr_mbox_ipm": "ipm", # - # kscan - "zephyr_kscan_input": "kscan", - # # led + "arduino_modulino_buttons_leds": "led", + "dac_leds": "led", "gpio_leds": "led", "holtek_ht16k33": "led", "issi_is31fl3194": "led", "issi_is31fl3216a": "led", "issi_is31fl3733": "led", + "leds_group_multicolor": "led", "microchip_xec_bbled": "led", "nordic_npm1300_led": "led", + "nordic_npm1304_led": "led", + "nxp_pca9533": "led", "nxp_pca9633": "led", "onnn_ncp5623": "led", "pwm_leds": "led", + "sct_sct2024": "led", "ti_lp3943": "led", "ti_lp5009": "led", "ti_lp5012": "led", @@ -920,6 +1337,7 @@ # # led_strip "apa_apa102": "led_strip", + "arduino_modulino_pixels": "led_strip", "greeled_lpd8803": "led_strip", "greeled_lpd8806": "led_strip", "ti_tlc5971": "led_strip", @@ -928,14 +1346,23 @@ "worldsemi_ws2812_i2s": "led_strip", "worldsemi_ws2812_rpi_pico_pio": "led_strip", "worldsemi_ws2812_spi": "led_strip", + "worldsemi_ws2812_uart": "led_strip", # # lora "reyax_rylrxxx": "lora", - "semtech_sx1272": "lora", - "semtech_sx1276": "lora", + # + # lora/loramac-node + "semtech_sx1272": "lora/loramac-node", + "semtech_sx1276": "lora/loramac-node", + # + # lora/native/sx126x + "semtech_sx1261": "lora/native/sx126x", + "semtech_sx1262": "lora/native/sx126x", + "st_stm32wl_subghz_radio": "lora/native/sx126x", # # mbox "andestech_mbox_plic_sw": "mbox", + "arm_mhuv3": "mbox", "espressif_mbox_esp32": "mbox", "linaro_ivshmem_mbox": "mbox", "nordic_mbox_nrf_ipc": "mbox", @@ -948,45 +1375,44 @@ "nxp_mbox_imx_mu": "mbox", "nxp_mbox_mailbox": "mbox", "nxp_s32_mru": "mbox", + "raspberrypi_pico_mbox": "mbox", + "renesas_ra_ipc_mbox": "mbox", + "renesas_rz_mhu_mbox": "mbox", "st_mbox_stm32_hsem": "mbox", - # - # mdio - "adi_adin2111_mdio": "mdio", - "atmel_sam_mdio": "mdio", - "espressif_esp32_mdio": "mdio", - "infineon_xmc4xxx_mdio": "mdio", - "litex_liteeth_mdio": "mdio", - "microchip_lan865x_mdio": "mdio", - "nxp_enet_mdio": "mdio", - "nxp_enet_qos_mdio": "mdio", - "nxp_imx_netc_emdio": "mdio", - "nxp_s32_gmac_mdio": "mdio", - "nxp_s32_netc_emdio": "mdio", - "renesas_ra_mdio": "mdio", - "smsc_lan91c111_mdio": "mdio", - "snps_dwcxgmac_mdio": "mdio", - "st_stm32_mdio": "mdio", - "zephyr_mdio_gpio": "mdio", + "ti_omap_mailbox": "mbox", + "ti_secure_proxy": "mbox", + "xlnx_mbox_versal_ipi_mailbox": "mbox", # # memc + "adi_max32_hpb": "memc", "atmel_sam_smc": "memc", + "bflb_bl61x_psram": "memc", "mspi_aps6404l": "memc", + "mspi_aps_z8": "memc", "nxp_imx_flexspi": "memc", + "nxp_imx_flexspi_is66wvs8m8": "memc", "nxp_imx_flexspi_s27ks0641": "memc", "nxp_imx_flexspi_w956a8mbya": "memc", "nxp_s32_qspi": "memc", + "nxp_s32_xspi": "memc", + "nxp_xspi": "memc", + "nxp_xspi_psram": "memc", "renesas_ra_sdram": "memc", "renesas_smartbond_nor_psram": "memc", "sifive_fu740_c000_ddr": "memc", + "silabs_siwx91x_qspi_memory": "memc", "st_stm32_fmc": "memc", "st_stm32_fmc_nor_psram": "memc", "st_stm32_fmc_sdram": "memc", + "st_stm32_ospi_psram": "memc", + "st_stm32_xspi_psram": "memc", "st_stm32h7_fmc": "memc", # # mfd "adi_ad559x": "mfd", "adi_adp5585": "mfd", "adi_max22017": "mfd", + "adi_maxq10xx": "mfd", "awinic_aw9523b": "mfd", "infineon_tle9104": "mfd", "ite_it8801_altctrl": "mfd", @@ -994,18 +1420,29 @@ "maxim_ds3231_mfd": "mfd", "maxim_max20335": "mfd", "maxim_max31790": "mfd", + "microchip_sam_flexcom": "mfd", + "microcrystal_rv3032_mfd": "mfd", + "motorola_mc146818_mfd": "mfd", "nordic_npm1300": "mfd", + "nordic_npm1304": "mfd", "nordic_npm2100": "mfd", "nordic_npm6001": "mfd", "nuvoton_nct38xx": "mfd", "nxp_lp_flexcomm": "mfd", + "nxp_pca9422": "mfd", + "nxp_pf1550": "mfd", + "nxp_sc18is606": "mfd", "rohm_bd8lb600fs": "mfd", - "x_powers_axp192": "mfd", # # mipi_dbi + "bflb_dbi": "mipi_dbi", + "espressif_esp32_lcd_cam_mipi_dbi": "mipi_dbi", "nxp_lcdic": "mipi_dbi", + "nxp_mipi_dbi_dcnano_lcdif": "mipi_dbi", "nxp_mipi_dbi_flexio_lcdif": "mipi_dbi", + "raspberrypi_pico_mipi_dbi_pio": "mipi_dbi", "renesas_smartbond_mipi_dbi": "mipi_dbi", + "sifli_sf32lb_lcdc_mipi_dbi": "mipi_dbi", "st_stm32_fmc_mipi_dbi": "mipi_dbi", "zephyr_mipi_dbi_bitbang": "mipi_dbi", "zephyr_mipi_dbi_spi": "mipi_dbi", @@ -1013,6 +1450,7 @@ # mipi_dsi "nxp_imx_mipi_dsi": "mipi_dsi", "nxp_mipi_dsi_2l": "mipi_dsi", + "nxp_mipi_dsi_dwc": "mipi_dsi", "renesas_ra_mipi_dsi": "mipi_dsi", "st_stm32_mipi_dsi": "mipi_dsi", "vnd_mipi_dsi": "mipi_dsi", @@ -1022,6 +1460,8 @@ # # misc/ethos_u "arm_ethos_u": "misc/ethos_u", + "nuvoton_numaker_npu": "misc/ethos_u", + "renesas_ra_npu": "misc/ethos_u", # # misc/ft8xx "ftdi_ft800": "misc/ft8xx", @@ -1029,12 +1469,22 @@ # misc/grove_lcd_rgb "seeed_grove_lcd_rgb": "misc/grove_lcd_rgb", # + # misc/interconn/renesas_elc + "renesas_ra_elc": "misc/interconn/renesas_elc", + # # misc/mcux_flexio "nxp_flexio": "misc/mcux_flexio", # # misc/nordic_vpr_launcher "nordic_nrf_vpr_coprocessor": "misc/nordic_vpr_launcher", # + # misc/nxp_imx93_mediamix + "nxp_imx93_mediamix": "misc/nxp_imx93_mediamix", + # + # misc/nxp_rtxxx_dsp_ctrl + "nxp_rt600_dsp_ctrl": "misc/nxp_rtxxx_dsp_ctrl", + "nxp_rt700_dsp_ctrl_hifi4": "misc/nxp_rtxxx_dsp_ctrl", + # # misc/nxp_s32_emios "nxp_s32_emios": "misc/nxp_s32_emios", # @@ -1044,20 +1494,32 @@ # misc/renesas_ra_external_interrupt "renesas_ra_external_interrupt": "misc/renesas_ra_external_interrupt", # + # misc/renesas_rx_dtc + "renesas_rx_dtc": "misc/renesas_rx_dtc", + # + # misc/renesas_rx_external_interrupt + "renesas_rx_external_interrupt": "misc/renesas_rx_external_interrupt", + # + # misc/stm32n6_axisram + "st_stm32n6_ramcfg": "misc/stm32n6_axisram", + # # misc/timeaware_gpio "intel_timeaware_gpio": "misc/timeaware_gpio", # # mm + "intel_adsp_mtl_tlb": "mm", "intel_adsp_tlb": "mm", # # modem "nordic_nrf91_slm": "modem", "quectel_bg95": "modem", + "quectel_bg96": "modem", "quectel_bg9x": "modem", "quectel_eg25_g": "modem", - "simcom_sim7080": "modem", + "quectel_eg800q": "modem", + "simcom_a76xx": "modem", "sqn_gm02s": "modem", - "swir_hl7800": "modem", + "st_st87mxx": "modem", "telit_me310g1": "modem", "telit_me910g1": "modem", "u_blox_lara_r6": "modem", @@ -1065,10 +1527,36 @@ "u_blox_sara_r5": "modem", "wnc_m14a2a": "modem", # + # modem/hl78xx + "swir_hl7800": "modem/hl78xx", + "swir_hl7800_gnss": "modem/hl78xx", + "swir_hl7800_offload": "modem/hl78xx", + "swir_hl7812": "modem/hl78xx", + "swir_hl7812_gnss": "modem/hl78xx", + "swir_hl7812_offload": "modem/hl78xx", + # + # modem/simcom/sim7080 + "simcom_sim7080": "modem/simcom/sim7080", + # # mspi "ambiq_mspi_controller": "mspi", + "snps_designware_ssi": "mspi", + "st_stm32_ospi_controller": "mspi", + "st_stm32_qspi_controller": "mspi", + "st_stm32_xspi_controller": "mspi", "zephyr_mspi_emul_controller": "mspi", # + # opamp + "nxp_opamp": "opamp", + "nxp_opamp_fast": "opamp", + "st_stm32_opamp": "opamp", + # + # otp + "nxp_ocotp": "otp", + "sifli_sf32lb_efuse": "otp", + "st_stm32_bsec": "otp", + "zephyr_otp_emul": "otp", + # # pcie/controller "brcm_brcmstb_pcie": "pcie/controller", # @@ -1086,10 +1574,12 @@ "nuvoton_npcx_peci": "peci", # # pinctrl + "alif_pinctrl": "pinctrl", + "brcm_bcm2711_pinctrl": "pinctrl", + "ene_kb106x_pinctrl": "pinctrl", "ene_kb1200_pinctrl": "pinctrl", "infineon_xmc4xxx_pinctrl": "pinctrl", "ite_it8xxx2_pinctrl_func": "pinctrl", - "microchip_mec5_pinctrl": "pinctrl", "microchip_xec_pinctrl": "pinctrl", "nuvoton_numaker_pinctrl": "pinctrl", "nuvoton_numicro_pinctrl": "pinctrl", @@ -1099,12 +1589,19 @@ "realtek_rts5912_pinctrl": "pinctrl", "sensry_sy1xx_pinctrl": "pinctrl", "sifive_pinctrl": "pinctrl", + "sifli_sf32lb52x_pinmux": "pinctrl", "silabs_dbus_pinctrl": "pinctrl", + "silabs_siwx91x_pinctrl": "pinctrl", "snps_emsdp_pinctrl": "pinctrl", "telink_b91_pinctrl": "pinctrl", "ti_cc13xx_cc26xx_pinctrl": "pinctrl", + "ti_cc23x0_pinctrl": "pinctrl", "ti_cc32xx_pinctrl": "pinctrl", "ti_k3_pinctrl": "pinctrl", + "ti_mspm0_pinctrl": "pinctrl", + "wch_00x_afio": "pinctrl", + "wch_20x_30x_afio": "pinctrl", + "wch_afio": "pinctrl", "xlnx_pinctrl_zynq": "pinctrl", "xlnx_pinctrl_zynqmp": "pinctrl", # @@ -1112,46 +1609,69 @@ "renesas_rcar_pfc": "pinctrl/renesas/rcar", # # pinctrl/renesas/rz + "renesas_rza2m_pinctrl": "pinctrl/renesas/rz", "renesas_rzt2m_pinctrl": "pinctrl/renesas/rz", # # pm_cpu_ops + "arm_fvp_pwrc": "pm_cpu_ops", "arm_psci_0_2": "pm_cpu_ops", "arm_psci_1_1": "pm_cpu_ops", # # power_domain + "arm_scmi_power_domain": "power_domain", "intel_adsp_power_domain": "power_domain", + "nordic_nrfs_gdpwr": "power_domain", + "nordic_nrfs_swext": "power_domain", "nxp_scu_pd": "power_domain", "power_domain_gpio": "power_domain", "power_domain_gpio_monitor": "power_domain", + "power_domain_soc_state_change": "power_domain", + "silabs_siwx91x_power_domain": "power_domain", + "ti_sci_pm_domain": "power_domain", # # ps2 + "ite_it51xxx_ps2": "ps2", "microchip_xec_ps2": "ps2", "nuvoton_npcx_ps2_channel": "ps2", "nuvoton_npcx_ps2_ctrl": "ps2", # + # psi5 + "nxp_s32_psi5": "psi5", + # # ptp_clock "nxp_enet_ptp_clock": "ptp_clock", + "nxp_netc_ptp_clock": "ptp_clock", # # pwm "adi_max32_pwm": "pwm", + "ambiq_ctimer_pwm": "pwm", + "ambiq_timer_pwm": "pwm", "atmel_sam0_tc_pwm": "pwm", "atmel_sam0_tcc_pwm": "pwm", "atmel_sam_pwm": "pwm", + "bflb_pwm_1": "pwm", + "bflb_pwm_2": "pwm", + "ene_kb106x_pwm": "pwm", "ene_kb1200_pwm": "pwm", "espressif_esp32_ledc": "pwm", "espressif_esp32_mcpwm": "pwm", "fsl_imx27_pwm": "pwm", "gd_gd32_pwm": "pwm", - "infineon_cat1_pwm": "pwm", + "infineon_tcpwm_pwm": "pwm", "infineon_xmc4xxx_ccu4_pwm": "pwm", "infineon_xmc4xxx_ccu8_pwm": "pwm", "intel_blinky_pwm": "pwm", + "ite_it51xxx_pwm": "pwm", "ite_it8801_pwm": "pwm", "ite_it8xxx2_pwm": "pwm", "litex_pwm": "pwm", "maxim_max31790_pwm": "pwm", + "microchip_tc_g1_pwm": "pwm", + "microchip_tcc_g1_pwm": "pwm", "microchip_xec_pwm": "pwm", "microchip_xec_pwmbbled": "pwm", + "neorv32_pwm": "pwm", + "nordic_nrf_pwm": "pwm", "nordic_nrf_sw_pwm": "pwm", "nuvoton_npcx_pwm": "pwm", "nuvoton_numaker_pwm": "pwm", @@ -1167,14 +1687,27 @@ "nxp_sctimer_pwm": "pwm", "openisa_rv32m1_tpm": "pwm", "raspberrypi_pico_pwm": "pwm", + "realtek_rts5912_pwm": "pwm", "renesas_pwm_rcar": "pwm", "renesas_ra_pwm": "pwm", + "renesas_rx_mtu_pwm": "pwm", + "renesas_rz_gpt_pwm": "pwm", + "renesas_rz_mtu_pwm": "pwm", + "renesas_rza2m_gpt_pwm": "pwm", "sifive_pwm0": "pwm", + "sifli_sf32lb_atim_pwm": "pwm", + "sifli_sf32lb_gpt_pwm": "pwm", "silabs_gecko_pwm": "pwm", + "silabs_letimer_pwm": "pwm", + "silabs_siwx91x_pwm": "pwm", + "silabs_timer_pwm": "pwm", "st_stm32_pwm": "pwm", "telink_b91_pwm": "pwm", "ti_cc13xx_cc26xx_timer_pwm": "pwm", + "ti_cc23x0_lgpt_pwm": "pwm", + "ti_mspm0_timer_pwm": "pwm", "vnd_pwm": "pwm", + "wch_gptm_pwm": "pwm", "xlnx_xps_timer_1_00_a_pwm": "pwm", "zephyr_fake_pwm": "pwm", # @@ -1185,70 +1718,110 @@ "mps_mpm54304": "regulator", "nordic_npm1100": "regulator", "nordic_npm1300_regulator": "regulator", + "nordic_npm1304_regulator": "regulator", "nordic_npm2100_regulator": "regulator", "nordic_npm6001_regulator": "regulator", "nxp_pca9420": "regulator", + "nxp_pca9422_regulator": "regulator", + "nxp_pf1550_regulator": "regulator", "nxp_vref": "regulator", + "nxp_vrefv1": "regulator", "raspberrypi_core_supply_regulator": "regulator", "regulator_fixed": "regulator", "regulator_gpio": "regulator", "renesas_smartbond_regulator": "regulator", - "x_powers_axp192_regulator": "regulator", + "st_stm32_vrefbuf": "regulator", + "ti_tps55287": "regulator", "zephyr_fake_regulator": "regulator", # # reset "aspeed_ast10x0_reset": "reset", + "focaltech_ft9001_cpm_rctl": "reset", "gd_gd32_rctl": "reset", "intel_socfpga_reset": "reset", + "microchip_mpfs_reset": "reset", + "microchip_rstc_g1_reset": "reset", "nuvoton_npcx_rst": "reset", "nuvoton_numaker_rst": "reset", "nxp_lpc_syscon_reset": "reset", + "nxp_mrcc_reset": "reset", "nxp_rstctl": "reset", "raspberrypi_pico_reset": "reset", + "realtek_rts5817_reset": "reset", + "reset_mmio": "reset", + "sifli_sf32lb_rcc_rctl": "reset", "st_stm32_rcc_rctl": "reset", # # retained_mem "nordic_nrf_gpregret": "retained_mem", + "silabs_buram": "retained_mem", "zephyr_retained_ram": "retained_mem", "zephyr_retained_reg": "retained_mem", # # rtc + "adi_max31331": "rtc", "ambiq_am1805": "rtc", "ambiq_rtc": "rtc", "atmel_sam_rtc": "rtc", - "infineon_cat1_rtc": "rtc", + "epson_rx8130ce_rtc": "rtc", + "infineon_rtc": "rtc", "infineon_xmc4xxx_rtc": "rtc", + "maxim_ds1302": "rtc", "maxim_ds1307": "rtc", + "maxim_ds1337": "rtc", "maxim_ds3231_rtc": "rtc", + "microchip_rtc_g1": "rtc", + "microchip_rtc_g2": "rtc", "microcrystal_rv3028": "rtc", + "microcrystal_rv3032": "rtc", "microcrystal_rv8803": "rtc", "microcrystal_rv_8263_c8": "rtc", "motorola_mc146818": "rtc", "nuvoton_numaker_rtc": "rtc", "nxp_irtc": "rtc", + "nxp_pcf2123": "rtc", + "nxp_pcf85063a": "rtc", "nxp_pcf8523": "rtc", "nxp_pcf8563": "rtc", "raspberrypi_pico_rtc": "rtc", + "realtek_rts5912_rtc": "rtc", + "renesas_ra_rtc": "rtc", "renesas_smartbond_rtc": "rtc", + "sifli_sf32lb_rtc": "rtc", + "silabs_siwx91x_rtc": "rtc", "st_stm32_rtc": "rtc", + "ti_bq32002": "rtc", + "ti_mspm0_rtc": "rtc", "zephyr_fake_rtc": "rtc", + "zephyr_rtc_counter": "rtc", "zephyr_rtc_emul": "rtc", # # sdhc + "adi_max32_sdhc": "sdhc", + "ambiq_sdio": "sdhc", "atmel_sam_hsmci": "sdhc", "cdns_sdhc": "sdhc", "espressif_esp32_sdhc_slot": "sdhc", - "infineon_cat1_sdhc_sdio": "sdhc", + "infineon_sdhc_sdio": "sdhc", "intel_emmc_host": "sdhc", + "microchip_sama7g5_sdmmc": "sdhc", "nxp_imx_usdhc": "sdhc", "nxp_lpc_sdif": "sdhc", "renesas_ra_sdhc": "sdhc", "renesas_rcar_mmc": "sdhc", + "st_stm32_sdio": "sdhc", + "xlnx_versal_8_9a": "sdhc", "zephyr_sdhc_spi_slot": "sdhc", # # sensor/a01nyub "dfrobot_a01nyub": "sensor/a01nyub", # + # sensor/adi/ad2s1210 + "adi_ad2s1210": "sensor/adi/ad2s1210", + # + # sensor/adi/ade7978 + "adi_ade7978": "sensor/adi/ade7978", + # # sensor/adi/adltc2990 "adi_adltc2990": "sensor/adi/adltc2990", # @@ -1271,12 +1844,24 @@ # sensor/adi/adxl372 "adi_adxl372": "sensor/adi/adxl372", # + # sensor/adi/max30210 + "adi_max30210": "sensor/adi/max30210", + # + # sensor/adi/max32664c + "maxim_max32664c": "sensor/adi/max32664c", + # + # sensor/als31300 + "allegro_als31300": "sensor/als31300", + # # sensor/amd_sb_tsi "amd_sb_tsi": "sensor/amd_sb_tsi", # # sensor/amg88xx "panasonic_amg88xx": "sensor/amg88xx", # + # sensor/ams/ams_as5048 + "ams_as5048": "sensor/ams/ams_as5048", + # # sensor/ams/ams_as5600 "ams_as5600": "sensor/ams/ams_as5600", # @@ -1339,6 +1924,12 @@ # sensor/bosch/bmc150_magn "bosch_bmc150_magn": "sensor/bosch/bmc150_magn", # + # sensor/bosch/bme280 + "bosch_bme280": "sensor/bosch/bme280", + # + # sensor/bosch/bme680 + "bosch_bme680": "sensor/bosch/bme680", + # # sensor/bosch/bmg160 "bosch_bmg160": "sensor/bosch/bmg160", # @@ -1355,6 +1946,12 @@ # sensor/bosch/bmi323 "bosch_bmi323": "sensor/bosch/bmi323", # + # sensor/bosch/bmm150 + "bosch_bmm150": "sensor/bosch/bmm150", + # + # sensor/bosch/bmm350 + "bosch_bmm350": "sensor/bosch/bmm350", + # # sensor/bosch/bmp180 "bosch_bmp180": "sensor/bosch/bmp180", # @@ -1362,6 +1959,12 @@ "bosch_bmp388": "sensor/bosch/bmp388", "bosch_bmp390": "sensor/bosch/bmp388", # + # sensor/bosch/bmp581 + "bosch_bmp581": "sensor/bosch/bmp581", + # + # sensor/broadcom/afbr_s50 + "brcm_afbr_s50": "sensor/broadcom/afbr_s50", + # # sensor/current_amp "current_sense_amplifier": "sensor/current_amp", # @@ -1377,6 +1980,9 @@ # sensor/espressif/pcnt_esp32 "espressif_esp32_pcnt": "sensor/espressif/pcnt_esp32", # + # sensor/everlight/als_pt19 + "everlight_als_pt19": "sensor/everlight/als_pt19", + # # sensor/explorir_m "gss_explorir_m": "sensor/explorir_m", # @@ -1414,6 +2020,12 @@ # sensor/infineon/xmc4xxx_temp "infineon_xmc4xxx_temp": "sensor/infineon/xmc4xxx_temp", # + # sensor/ist8310 + "isentek_ist8310": "sensor/ist8310", + # + # sensor/ite/ite_tach_it51xxx + "ite_it51xxx_tach": "sensor/ite/ite_tach_it51xxx", + # # sensor/ite/ite_tach_it8xxx2 "ite_it8xxx2_tach": "sensor/ite/ite_tach_it8xxx2", # @@ -1423,6 +2035,9 @@ # sensor/jedec/jc42 "jedec_jc_42_4_temp": "sensor/jedec/jc42", # + # sensor/liteon/ltrf216a + "liteon_ltrf216a": "sensor/liteon/ltrf216a", + # # sensor/lm35 "lm35": "sensor/lm35", # @@ -1432,9 +2047,6 @@ # sensor/lm77 "lm77": "sensor/lm77", # - # sensor/ltrf216a - "ltr_f216a": "sensor/ltrf216a", - # # sensor/maxim/ds18b20 "maxim_ds18b20": "sensor/maxim/ds18b20", "maxim_ds18s20": "sensor/maxim/ds18b20", @@ -1458,6 +2070,9 @@ # sensor/maxim/max31855 "maxim_max31855": "sensor/maxim/max31855", # + # sensor/maxim/max31865 + "maxim_max31865": "sensor/maxim/max31865", + # # sensor/maxim/max31875 "maxim_max31875": "sensor/maxim/max31875", # @@ -1467,11 +2082,15 @@ # sensor/maxim/max6675 "maxim_max6675": "sensor/maxim/max6675", # + # sensor/mb7040 + "maxbotix_mb7040": "sensor/mb7040", + # # sensor/meas/ms5607 "meas_ms5607": "sensor/meas/ms5607", # # sensor/meas/ms5837 - "meas_ms5837": "sensor/meas/ms5837", + "meas_ms5837_02ba": "sensor/meas/ms5837", + "meas_ms5837_30ba": "sensor/meas/ms5837", # # sensor/melexis/mlx90394 "melexis_mlx90394": "sensor/melexis/mlx90394", @@ -1479,6 +2098,9 @@ # sensor/memsic/mc3419 "memsic_mc3419": "sensor/memsic/mc3419", # + # sensor/memsic/mmc56x3 + "memsic_mmc56x3": "sensor/memsic/mmc56x3", + # # sensor/mhz19b "winsen_mhz19b": "sensor/mhz19b", # @@ -1491,14 +2113,18 @@ # sensor/microchip/mcp970x "microchip_mcp970x": "sensor/microchip/mcp970x", # + # sensor/microchip/mtch9010 + "microchip_mtch9010": "sensor/microchip/mtch9010", + # # sensor/microchip/tcn75a "microchip_tcn75a": "sensor/microchip/tcn75a", # # sensor/nct75 "onnn_nct75": "sensor/nct75", # - # sensor/nordic/npm1300_charger - "nordic_npm1300_charger": "sensor/nordic/npm1300_charger", + # sensor/nordic/npm13xx_charger + "nordic_npm1300_charger": "sensor/nordic/npm13xx_charger", + "nordic_npm1304_charger": "sensor/nordic/npm13xx_charger", # # sensor/nordic/npm2100_vbat "nordic_npm2100_vbat": "sensor/nordic/npm2100_vbat", @@ -1520,6 +2146,9 @@ # sensor/nuvoton/nuvoton_adc_cmp_npcx "nuvoton_adc_cmp": "sensor/nuvoton/nuvoton_adc_cmp_npcx", # + # sensor/nuvoton/nuvoton_adc_v2t_npcx + "nuvoton_npcx_adc_v2t": "sensor/nuvoton/nuvoton_adc_v2t_npcx", + # # sensor/nuvoton/nuvoton_tach_npcx "nuvoton_npcx_tach": "sensor/nuvoton/nuvoton_tach_npcx", # @@ -1541,9 +2170,21 @@ # sensor/nxp/nxp_kinetis_temp "nxp_kinetis_temperature": "sensor/nxp/nxp_kinetis_temp", # + # sensor/nxp/nxp_lpadc_temp40 + "nxp_lpadc_temp40": "sensor/nxp/nxp_lpadc_temp40", + # + # sensor/nxp/nxp_pmc_tmpsns + "nxp_pmc_tmpsns": "sensor/nxp/nxp_pmc_tmpsns", + # # sensor/nxp/nxp_tempmon "nxp_tempmon": "sensor/nxp/nxp_tempmon", # + # sensor/nxp/nxp_tempsense + "nxp_tempsense": "sensor/nxp/nxp_tempsense", + # + # sensor/nxp/nxp_tmpsns + "nxp_tmpsns": "sensor/nxp/nxp_tmpsns", + # # sensor/nxp/p3t1755 "nxp_p3t1755": "sensor/nxp/p3t1755", # @@ -1553,12 +2194,40 @@ # sensor/nxp/qdec_nxp_s32 "nxp_qdec_s32": "sensor/nxp/qdec_nxp_s32", # + # sensor/nxp/qdec_tpm + "nxp_tpm_qdec": "sensor/nxp/qdec_tpm", + # + # sensor/omron/2smpb_02e + "omron_2smpb_02e": "sensor/omron/2smpb_02e", + # + # sensor/omron/d6f + "omron_d6f_p0001": "sensor/omron/d6f", + "omron_d6f_p0010": "sensor/omron/d6f", + # + # sensor/pixart/paa3905 + "pixart_paa3905": "sensor/pixart/paa3905", + # + # sensor/pixart/paj7620 + "pixart_paj7620": "sensor/pixart/paj7620", + # + # sensor/pixart/pat9136 + "pixart_pat9136": "sensor/pixart/pat9136", + # # sensor/pms7003 "plantower_pms7003": "sensor/pms7003", # + # sensor/pni/rm3100 + "pni_rm3100": "sensor/pni/rm3100", + # + # sensor/pzem004t + "peacefair_pzem004t": "sensor/pzem004t", + # # sensor/qdec_sam "atmel_sam_tc_qdec": "sensor/qdec_sam", # + # sensor/realtek/rts5912 + "realtek_rts5912_tach": "sensor/realtek/rts5912", + # # sensor/renesas/hs300x "renesas_hs300x": "sensor/renesas/hs300x", # @@ -1571,12 +2240,21 @@ # sensor/rohm/bd8lb600fs "rohm_bd8lb600fs_diagnostics": "sensor/rohm/bd8lb600fs", # + # sensor/rohm/bh1730 + "rohm_bh1730": "sensor/rohm/bh1730", + # # sensor/rohm/bh1750 "rohm_bh1750": "sensor/rohm/bh1750", # + # sensor/rohm/bh1790 + "rohm_bh1790": "sensor/rohm/bh1790", + # # sensor/rpi_pico_temp "raspberrypi_pico_temp": "sensor/rpi_pico_temp", # + # sensor/rv3032_temp + "microcrystal_rv3032_temp": "sensor/rv3032_temp", + # # sensor/s11059 "hamamatsu_s11059": "sensor/s11059", # @@ -1606,6 +2284,9 @@ # sensor/sensirion/sts4x "sensirion_sts4x": "sensor/sensirion/sts4x", # + # sensor/sifli/sf32lb_tsen + "sifli_sf32lb_tsen": "sensor/sifli/sf32lb_tsen", + # # sensor/silabs/si7006 "sensirion_sht21": "sensor/silabs/si7006", "silabs_si7006": "sensor/silabs/si7006", @@ -1643,6 +2324,9 @@ # sensor/st/iis3dhhc "st_iis3dhhc": "sensor/st/iis3dhhc", # + # sensor/st/iis3dwb + "st_iis3dwb": "sensor/st/iis3dwb", + # # sensor/st/ism330dhcx "st_ism330dhcx": "sensor/st/ism330dhcx", # @@ -1702,7 +2386,16 @@ "st_lsm6dso16is": "sensor/st/lsm6dso16is", # # sensor/st/lsm6dsv16x + "DT_DRV_COMPAT_LSM6DSV16X": "sensor/st/lsm6dsv16x", + "DT_DRV_COMPAT_LSM6DSV32X": "sensor/st/lsm6dsv16x", "st_lsm6dsv16x": "sensor/st/lsm6dsv16x", + "st_lsm6dsv32x": "sensor/st/lsm6dsv16x", + # + # sensor/st/lsm6dsvxxx + "st_ism6hg256x": "sensor/st/lsm6dsvxxx", + "st_lsm6dsv320x": "sensor/st/lsm6dsvxxx", + "st_lsm6dsv80x": "sensor/st/lsm6dsvxxx", + "st_lsm6dsvxxx": "sensor/st/lsm6dsvxxx", # # sensor/st/lsm9ds0_gyro "st_lsm9ds0_gyro": "sensor/st/lsm9ds0_gyro", @@ -1713,6 +2406,9 @@ # sensor/st/lsm9ds1 "st_lsm9ds1": "sensor/st/lsm9ds1", # + # sensor/st/lsm9ds1_mag + "st_lsm9ds1_mag": "sensor/st/lsm9ds1_mag", + # # sensor/st/qdec_stm32 "st_stm32_qdec": "sensor/st/qdec_stm32", # @@ -1745,18 +2441,36 @@ # sensor/sx9500 "semtech_sx9500": "sensor/sx9500", # + # sensor/tach_gpio + "zephyr_tach_gpio": "sensor/tach_gpio", + # + # sensor/tdk/icm40627 + "invensense_icm40627": "sensor/tdk/icm40627", + # # sensor/tdk/icm42605 "invensense_icm42605": "sensor/tdk/icm42605", # - # sensor/tdk/icm42670 - "invensense_icm42670p": "sensor/tdk/icm42670", - "invensense_icm42670s": "sensor/tdk/icm42670", + # sensor/tdk/icm4268x + "invensense_icm42688": "sensor/tdk/icm4268x", + "invensense_icm4268x": "sensor/tdk/icm4268x", # - # sensor/tdk/icm42688 - "invensense_icm42688": "sensor/tdk/icm42688", + # sensor/tdk/icm42x70 + "invensense_icm42370p": "sensor/tdk/icm42x70", + "invensense_icm42670p": "sensor/tdk/icm42x70", + "invensense_icm42670s": "sensor/tdk/icm42x70", # - # sensor/tdk/icp10125 - "invensense_icp10125": "sensor/tdk/icp10125", + # sensor/tdk/icm45686 + "invensense_icm45605": "sensor/tdk/icm45686", + "invensense_icm45605s": "sensor/tdk/icm45686", + "invensense_icm45686": "sensor/tdk/icm45686", + "invensense_icm45686s": "sensor/tdk/icm45686", + "invensense_icm45688p": "sensor/tdk/icm45686", + # + # sensor/tdk/icp101xx + "invensense_icp101xx": "sensor/tdk/icp101xx", + # + # sensor/tdk/icp201xx + "invensense_icp201xx": "sensor/tdk/icp201xx", # # sensor/tdk/mpu6050 "invensense_mpu6050": "sensor/tdk/mpu6050", @@ -1776,26 +2490,34 @@ # sensor/ti/ina219 "ti_ina219": "sensor/ti/ina219", # - # sensor/ti/ina226 - "ti_ina226": "sensor/ti/ina226", - # - # sensor/ti/ina23x - "ti_ina230": "sensor/ti/ina23x", - "ti_ina236": "sensor/ti/ina23x", - "ti_ina237": "sensor/ti/ina23x", + # sensor/ti/ina2xx + "ti_ina226": "sensor/ti/ina2xx", + "ti_ina228": "sensor/ti/ina2xx", + "ti_ina230": "sensor/ti/ina2xx", + "ti_ina232": "sensor/ti/ina2xx", + "ti_ina236": "sensor/ti/ina2xx", + "ti_ina237": "sensor/ti/ina2xx", # # sensor/ti/ina3221 "ti_ina3221": "sensor/ti/ina3221", # + # sensor/ti/ina7xx + "ti_ina7xx": "sensor/ti/ina7xx", + # # sensor/ti/lm95234 "national_lm95234": "sensor/ti/lm95234", # - # sensor/ti/opt3001 - "ti_opt3001": "sensor/ti/opt3001", + # sensor/ti/opt300x + "ti_opt3001": "sensor/ti/opt300x", + "ti_opt3004": "sensor/ti/opt300x", + "ti_opt300x": "sensor/ti/opt300x", # # sensor/ti/ti_hdc "ti_hdc": "sensor/ti/ti_hdc", # + # sensor/ti/ti_hdc302x + "ti_hdc302x": "sensor/ti/ti_hdc302x", + # # sensor/ti/tmag5170 "ti_tmag5170": "sensor/ti/tmag5170", # @@ -1807,6 +2529,7 @@ # # sensor/ti/tmp108 "ams_as6212": "sensor/ti/tmp108", + "ams_as6221": "sensor/ti/tmp108", "ti_tmp108": "sensor/ti/tmp108", # # sensor/ti/tmp112 @@ -1815,8 +2538,11 @@ # sensor/ti/tmp114 "ti_tmp114": "sensor/ti/tmp114", # - # sensor/ti/tmp116 - "ti_tmp116": "sensor/ti/tmp116", + # sensor/ti/tmp11x + "ti_tmp11x": "sensor/ti/tmp11x", + # + # sensor/ti/tmp435 + "ti_tmp435": "sensor/ti/tmp435", # # sensor/tsic_xx6 "ist_tsic_xx6": "sensor/tsic_xx6", @@ -1830,6 +2556,12 @@ # sensor/vishay/vcnl4040 "vishay_vcnl4040": "sensor/vishay/vcnl4040", # + # sensor/vishay/veml6031 + "vishay_veml6031": "sensor/vishay/veml6031", + # + # sensor/vishay/veml6046 + "vishay_veml6046": "sensor/vishay/veml6046", + # # sensor/vishay/veml7700 "vishay_veml7700": "sensor/vishay/veml7700", # @@ -1839,37 +2571,65 @@ # sensor/wsen/wsen_hids_2525020210002 "we_wsen_hids_2525020210002": "sensor/wsen/wsen_hids_2525020210002", # + # sensor/wsen/wsen_isds_2536030320001 + "we_wsen_isds_2536030320001": "sensor/wsen/wsen_isds_2536030320001", + # + # sensor/wsen/wsen_itds_2533020201601 + "we_wsen_itds_2533020201601": "sensor/wsen/wsen_itds_2533020201601", + # + # sensor/wsen/wsen_pads_2511020213301 + "we_wsen_pads_2511020213301": "sensor/wsen/wsen_pads_2511020213301", + # + # sensor/wsen/wsen_pdms_25131308XXX05 + "we_wsen_pdms_25131308xxx05": "sensor/wsen/wsen_pdms_25131308XXX05", + # # sensor/wsen/wsen_pdus_25131308XXXXX "we_wsen_pdus_25131308xxxxx": "sensor/wsen/wsen_pdus_25131308XXXXX", # + # sensor/wsen/wsen_tids_2521020222501 + "we_wsen_tids_2521020222501": "sensor/wsen/wsen_tids_2521020222501", + # + # sensor/xbr818 + "phosense_xbr818": "sensor/xbr818", + # + # sent + "nxp_s32_sent": "sent", + # # serial "SBSA_COMPAT": "serial", "adi_max32_uart": "serial", + "aesc_uart": "serial", "altr_jtag_uart": "serial", "altr_uart": "serial", + "ambiq_uart": "serial", "arm_cmsdk_uart": "serial", "arm_pl011": "serial", "atmel_sam0_uart": "serial", "atmel_sam_uart": "serial", "atmel_sam_usart": "serial", + "bflb_uart": "serial", "brcm_bcm2711_aux_uart": "serial", "cdns_uart": "serial", "cypress_psoc6_uart": "serial", "efinix_sapphire_uart0": "serial", + "ene_kb106x_uart": "serial", "ene_kb1200_uart": "serial", + "espressif_esp32_lpuart": "serial", "espressif_esp32_uart": "serial", "espressif_esp32_usb_serial": "serial", + "focaltech_ft9001_usart": "serial", "gaisler_apbuart": "serial", "gd_gd32_usart": "serial", - "infineon_cat1_uart": "serial", + "infineon_uart": "serial", "infineon_xmc4xxx_uart": "serial", "intel_lw_uart": "serial", "intel_sedi_uart": "serial", + "ite_it51xxx_uart": "serial", "ite_it8xxx2_uart": "serial", "litex_uart": "serial", "lowrisc_opentitan_uart": "serial", "microchip_coreuart": "serial", - "microchip_mec5_uart": "serial", + "microchip_sercom_g1_uart": "serial", "microchip_xec_uart": "serial", "neorv32_uart": "serial", "nordic_nrf_uart": "serial", @@ -1888,38 +2648,51 @@ "openisa_rv32m1_lpuart": "serial", "quicklogic_usbserialport_s3b": "serial", "raspberrypi_pico_uart_pio": "serial", + "realtek_ameba_loguart": "serial", + "realtek_bee_uart": "serial", "realtek_rts5912_uart": "serial", "renesas_ra8_uart_sci_b": "serial", "renesas_ra_sci_uart": "serial", - "renesas_ra_uart_sci": "serial", "renesas_rcar_hscif": "serial", "renesas_rcar_scif": "serial", + "renesas_rx_uart_sci": "serial", + "renesas_rx_uart_sci_qemu": "serial", + "renesas_rz_sci_b_uart": "serial", + "renesas_rz_sci_uart": "serial", "renesas_rz_scif_uart": "serial", + "renesas_rza2m_scif_uart": "serial", "renesas_rzt2m_uart": "serial", "renesas_smartbond_uart": "serial", "segger_rtt_uart": "serial", "sensry_sy1xx_uart": "serial", "sifive_uart0": "serial", + "sifli_sf32lb_usart": "serial", "silabs_eusart_uart": "serial", "silabs_gecko_leuart": "serial", "silabs_gecko_uart": "serial", "silabs_gecko_usart": "serial", "silabs_si32_usart": "serial", + "silabs_usart_uart": "serial", "snps_hostlink_uart": "serial", "st_stm32_uart": "serial", "telink_b91_uart": "serial", "ti_cc13xx_cc26xx_uart": "serial", + "ti_cc23x0_uart": "serial", "ti_cc32xx_uart": "serial", "ti_msp432p4xx_uart": "serial", + "ti_mspm0_uart": "serial", "ti_stellaris_uart": "serial", + "virtio_console": "serial", "vnd_serial": "serial", "wch_usart": "serial", "xen_hvc_consoleio": "serial", "xlnx_xps_uartlite_1_00_a": "serial", "xlnx_xuartps": "serial", - "zephyr_native_posix_uart": "serial", + "zephyr_native_pty_uart": "serial", "zephyr_native_tty_uart": "serial", "zephyr_nus_uart": "serial", + "zephyr_uart_bitbang": "serial", + "zephyr_uart_bridge": "serial", "zephyr_uart_emul": "serial", # # sip_svc @@ -1938,22 +2711,29 @@ "arm_pl022": "spi", "atmel_sam0_spi": "spi", "atmel_sam_spi": "spi", + "bflb_spi": "spi", + "cdns_spi": "spi", "cypress_psoc6_spi": "spi", + "egis_et171_spi": "spi", "espressif_esp32_spi": "spi", "gaisler_spimctrl": "spi", "gd_gd32_spi": "spi", - "infineon_cat1_spi": "spi", + "infineon_spi": "spi", "infineon_xmc4xxx_spi": "spi", "intel_penwell_spi": "spi", "intel_sedi_spi": "spi", + "ite_it51xxx_spi": "spi", "ite_it8xxx2_spi": "spi", "litex_spi": "spi", "litex_spi_litespi": "spi", "lowrisc_opentitan_spi": "spi", "microchip_mpfs_qspi": "spi", "microchip_mpfs_spi": "spi", + "microchip_sercom_g1_spi": "spi", "microchip_xec_qmspi": "spi", "microchip_xec_qmspi_ldma": "spi", + "nordic_nrf_spi": "spi", + "nordic_nrf_spim": "spi", "nuvoton_npcx_spip": "spi", "nuvoton_numaker_spi": "spi", "nxp_dspi": "spi", @@ -1961,20 +2741,33 @@ "nxp_imx_ecspi": "spi", "nxp_lpc_spi": "spi", "nxp_s32_spi": "spi", + "nxp_sc18is606_spi": "spi", "opencores_spi_simple": "spi", "openisa_rv32m1_lpspi": "spi", "raspberrypi_pico_spi_pio": "spi", + "realtek_rts5912_spi": "spi", "renesas_ra8_spi_b": "spi", "renesas_ra_spi": "spi", + "renesas_ra_spi_sci": "spi", + "renesas_ra_spi_sci_b": "spi", + "renesas_rx_rspi": "spi", + "renesas_rz_rspi": "spi", + "renesas_rz_spi": "spi", "renesas_smartbond_spi": "spi", + "sensry_sy1xx_spi": "spi", "sifive_spi0": "spi", + "sifli_sf32lb_spi": "spi", "silabs_eusart_spi": "spi", + "silabs_gspi": "spi", "silabs_usart_spi": "spi", "snps_designware_spi": "spi", "st_stm32_spi": "spi", "telink_b91_spi": "spi", "ti_cc13xx_cc26xx_spi": "spi", + "ti_cc23x0_spi": "spi", + "ti_omap_mcspi": "spi", "vnd_spi": "spi", + "wch_spi": "spi", "xlnx_xps_spi_2_00_a": "spi", "zephyr_spi_bitbang": "spi", "zephyr_spi_emul_controller": "spi", @@ -1983,68 +2776,92 @@ "nxp_lpspi": "spi/spi_nxp_lpspi", # # stepper - "zephyr_fake_stepper": "stepper", - "zephyr_gpio_stepper": "stepper", + "zephyr_fake_stepper_ctrl": "stepper", + "zephyr_fake_stepper_driver": "stepper", + # + # stepper/adi_tmc/tmc22xx + "adi_tmc2209": "stepper/adi_tmc/tmc22xx", + # + # stepper/adi_tmc/tmc50xx + "adi_tmc50xx": "stepper/adi_tmc/tmc50xx", + "adi_tmc50xx_stepper_ctrl": "stepper/adi_tmc/tmc50xx", + "adi_tmc50xx_stepper_driver": "stepper/adi_tmc/tmc50xx", + # + # stepper/adi_tmc/tmc51xx + "adi_tmc51xx": "stepper/adi_tmc/tmc51xx", + "adi_tmc51xx_stepper_ctrl": "stepper/adi_tmc/tmc51xx", + "adi_tmc51xx_stepper_driver": "stepper/adi_tmc/tmc51xx", # - # stepper/adi_tmc - "adi_tmc2209": "stepper/adi_tmc", - "adi_tmc5041": "stepper/adi_tmc", + # stepper/allegro + "allegro_a4979": "stepper/allegro", + # + # stepper/gpio_stepper + "zephyr_gpio_step_dir_stepper_ctrl": "stepper/gpio_stepper", + "zephyr_h_bridge_stepper_ctrl": "stepper/gpio_stepper", # # stepper/ti - "ti_drv8424": "stepper/ti", + "ti_drv84xx": "stepper/ti", # # syscon + "bflb_efuse": "syscon", "syscon": "syscon", # # tee/optee "linaro_optee_tz": "tee/optee", # # timer + "adi_max32_rv32_sys_timer": "timer", "ambiq_stimer": "timer", - "andestech_machine_timer": "timer", "atmel_sam0_rtc": "timer", "gaisler_gptimer": "timer", + "infineon_lp_timer": "timer", "intel_adsp_timer": "timer", "intel_hpet": "timer", + "ite_it51xxx_timer": "timer", "ite_it8xxx2_timer": "timer", "litex_timer0": "timer", - "lowrisc_machine_timer": "timer", - "microchip_mec5_ktimer": "timer", + "microchip_sam_pit64b": "timer", "microchip_xec_rtos_timer": "timer", - "neorv32_machine_timer": "timer", - "niosv_machine_timer": "timer", - "nuclei_systimer": "timer", "nuvoton_npcx_itim_timer": "timer", "nxp_gpt_hw_timer": "timer", - "nxp_kinetis_lptmr": "timer", "nxp_lptmr": "timer", "nxp_os_timer": "timer", "openisa_rv32m1_lptmr": "timer", "realtek_rts5912_rtmr": "timer", + "renesas_ra_ulpt_timer": "timer", "renesas_rcar_cmt": "timer", - "scr_machine_timer": "timer", - "sifive_clint0": "timer", + "renesas_rx_timer_cmt": "timer", + "renesas_rz_gtm_os_timer": "timer", + "renesas_rza2m_ostm": "timer", + "riscv_machine_timer": "timer", "silabs_gecko_burtc": "timer", "st_stm32_lptim": "timer", "sy1xx_sys_timer": "timer", - "telink_machine_timer": "timer", "ti_am654_timer": "timer", "ti_cc13xx_cc26xx_rtc_timer": "timer", + "ti_cc23x0_rtc_timer": "timer", + "ti_cc23x0_systim_timer": "timer", "wch_systick": "timer", "xlnx_ttcps": "timer", # # usb/bc12 "diodes_pi3usb9201": "usb/bc12", # + # usb/common/stm32 + "*/": "usb/common/stm32", + "st_stm32u5_otghs_phy": "usb/common/stm32", + # # usb/device - "atmel_sam0_usb": "usb/device", "atmel_sam_usbc": "usb/device", "atmel_sam_usbhs": "usb/device", # # usb/udc + "adi_max32_usbhs": "usb/udc", "ambiq_usb": "usb/udc", + "atmel_sam0_usb": "usb/udc", "ite_it82xx2_usb": "usb/udc", "nordic_nrf_usbd": "usb/udc", + "nuvoton_numaker_hsusbd": "usb/udc", "nuvoton_numaker_usbd": "usb/udc", "nxp_ehci": "usb/udc", "nxp_kinetis_usbd": "usb/udc", @@ -2061,6 +2878,10 @@ # # usb/uhc "maxim_max3421e_spi": "usb/uhc", + "nxp_uhc_ehci": "usb/uhc", + "nxp_uhc_ip3516hs": "usb/uhc", + "nxp_uhc_khci": "usb/uhc", + "nxp_uhc_ohci": "usb/uhc", "zephyr_uhc_virtual": "usb/uhc", # # usb_c/ppc @@ -2069,6 +2890,7 @@ # # usb_c/tcpc "nuvoton_numaker_tcpc": "usb_c/tcpc", + "onnn_fusb307_tcpc": "usb_c/tcpc", "parade_ps8xxx": "usb_c/tcpc", "richtek_rt1715": "usb_c/tcpc", "st_stm32_ucpd": "usb_c/tcpc", @@ -2080,19 +2902,34 @@ # # video "aptina_mt9m114": "video", - "espressif_esp32_lcd_cam": "video", + "espressif_esp32_lcd_cam_dvp": "video", "galaxycore_gc2145": "video", + "himax_hm01b0": "video", + "himax_hm0360": "video", "nxp_imx_csi": "video", "nxp_mipi_csi2rx": "video", "nxp_video_smartdma": "video", "ovti_ov2640": "video", "ovti_ov5640": "video", + "ovti_ov5642": "video", "ovti_ov7670": "video", + "ovti_ov7675": "video", "ovti_ov7725": "video", + "ovti_ov9655": "video", + "renesas_ra_ceu": "video", + "sony_imx335": "video", + "st_mipid02": "video", "st_stm32_dcmi": "video", - "zephyr_sw_generator": "video", + "st_stm32_dcmipp": "video", + "st_stm32_jpeg": "video", + "st_stm32_venc": "video", "zephyr_video_emul_imager": "video", "zephyr_video_emul_rx": "video", + "zephyr_video_sw_generator": "video", + # + # virtio + "virtio_mmio": "virtio", + "virtio_pci": "virtio", # # virtualization "qemu_ivshmem": "virtualization", @@ -2109,45 +2946,66 @@ # # watchdog "adi_max32_watchdog": "watchdog", + "adi_max42500_watchdog": "watchdog", "ambiq_watchdog": "watchdog", "andestech_atcwdt200": "watchdog", "arm_cmsdk_watchdog": "watchdog", "atmel_sam0_watchdog": "watchdog", + "atmel_sam4l_watchdog": "watchdog", "atmel_sam_watchdog": "watchdog", + "bflb_wdt": "watchdog", + "ene_kb106x_watchdog": "watchdog", "ene_kb1200_watchdog": "watchdog", "espressif_esp32_watchdog": "watchdog", "espressif_esp32_xt_wdt": "watchdog", "gd_gd32_fwdgt": "watchdog", "gd_gd32_wwdgt": "watchdog", - "infineon_cat1_watchdog": "watchdog", + "infineon_watchdog": "watchdog", "infineon_xmc4xxx_watchdog": "watchdog", "intel_adsp_watchdog": "watchdog", "intel_tco_wdt": "watchdog", + "ite_it51xxx_watchdog": "watchdog", "ite_it8xxx2_watchdog": "watchdog", "litex_watchdog": "watchdog", "lowrisc_opentitan_aontimer": "watchdog", + "microchip_wdt_g1": "watchdog", "microchip_xec_watchdog": "watchdog", "nordic_npm1300_wdt": "watchdog", + "nordic_npm1304_wdt": "watchdog", "nordic_npm2100_wdt": "watchdog", "nordic_npm6001_wdt": "watchdog", + "nordic_nrf_wdt": "watchdog", "nuvoton_npcx_watchdog": "watchdog", "nuvoton_numaker_wwdt": "watchdog", + "nxp_cop": "watchdog", + "nxp_ewm": "watchdog", "nxp_fs26_wdog": "watchdog", "nxp_imx_wdog": "watchdog", "nxp_kinetis_wdog": "watchdog", "nxp_lpc_wwdt": "watchdog", + "nxp_rtwdog": "watchdog", "nxp_s32_swt": "watchdog", "nxp_wdog32": "watchdog", "raspberrypi_pico_watchdog": "watchdog", + "realtek_rts5817_watchdog": "watchdog", + "realtek_rts5912_watchdog": "watchdog", + "renesas_rx_iwdt": "watchdog", + "renesas_rz_wdt": "watchdog", "renesas_smartbond_watchdog": "watchdog", "sifive_wdt": "watchdog", + "sifli_sf32lb_wdt": "watchdog", "silabs_gecko_wdog": "watchdog", + "silabs_siwx91x_wdt": "watchdog", "snps_designware_watchdog": "watchdog", "st_stm32_watchdog": "watchdog", "st_stm32_window_watchdog": "watchdog", "ti_cc13xx_cc26xx_watchdog": "watchdog", + "ti_cc23x0_wdt": "watchdog", "ti_cc32xx_watchdog": "watchdog", + "ti_j7_rti_wdt": "watchdog", "ti_tps382x": "watchdog", + "wch_iwdg": "watchdog", + "xlnx_versal_wwdt": "watchdog", "xlnx_xps_timebase_wdt_1_00_a": "watchdog", # # wifi/esp32/src @@ -2156,16 +3014,25 @@ # wifi/esp_at "espressif_esp_at": "wifi/esp_at", # + # wifi/esp_hosted + "espressif_esp_hosted": "wifi/esp_hosted", + # # wifi/eswifi "inventek_eswifi": "wifi/eswifi", "inventek_eswifi_uart": "wifi/eswifi", # + # wifi/infineon + "infineon_airoc_wifi": "wifi/infineon", + # # wifi/nrf_wifi/off_raw_tx/src "nordic_wlan": "wifi/nrf_wifi/off_raw_tx/src", # # wifi/nxp "nxp_wifi": "wifi/nxp", # + # wifi/siwx91x + "silabs_siwx91x_wifi": "wifi/siwx91x", + # # wifi/winc1500 "atmel_winc1500": "wifi/winc1500", } diff --git a/ports/zephyr-cp/cptools/cpbuild.py b/ports/zephyr-cp/cptools/cpbuild.py index ef836b3df8af3..edaf020c264a0 100644 --- a/ports/zephyr-cp/cptools/cpbuild.py +++ b/ports/zephyr-cp/cptools/cpbuild.py @@ -1,15 +1,14 @@ import asyncio -import inspect +import atexit +import hashlib +import json import logging import os import pathlib -import shlex -import time -import hashlib -import atexit -import json import re -import sys +import tempfile +import time +from typing import Optional logger = logging.getLogger(__name__) @@ -111,7 +110,14 @@ def _create_semaphore(): max_track = 0 -async def run_command(command, working_directory, description=None, check_hash=[], extradeps=[]): +async def run_command( + command, + working_directory, + description=None, + check_hash=[], + extradeps=[], + responsefile: Optional[pathlib.Path] = None, +): """ Runs a command asynchronously. The command should ideally be a list of strings and pathlib.Path objects. If all of the paths haven't been modified since the last @@ -124,26 +130,39 @@ async def run_command(command, working_directory, description=None, check_hash=[ Paths in check_hash are hashed before and after the command. If the hash is the same, then the old mtimes are reset. This is helpful if a command may produce - the same result and you don't want the rest of the build impacted. + the same result and you don't want the rest of the build impacted + + responsefile is used to store the command line arguments if they are too long for the OS. + The arguments will be replaced with @ and tried again. + If None, commands that are too long will fail. """ paths = [] + responsefile_contents = None if isinstance(command, list): for i, part in enumerate(command): if isinstance(part, pathlib.Path): paths.append(part) part = part.relative_to(working_directory, walk_up=True) - # if isinstance(part, list): command[i] = str(part) - command = " ".join(command) + command_string = " ".join(command) + + # When on windows, use a responsefile if the command string is >= 8192 + if responsefile is not None and os.name == "nt" and len(command_string) >= 8192: + # Escape backslashes + responsefile_contents = "\n".join(part.replace("\\", "\\\\") for part in command[1:]) + responsefile.write_text(responsefile_contents) + command_string = f"{command[0]} -v @{responsefile}" + else: + command_string = command - command_hash = hashlib.sha3_256(command.encode("utf-8")) + command_hash = hashlib.sha3_256(command_string.encode("utf-8")) command_hash.update(str(working_directory).encode("utf-8")) command_hash = command_hash.hexdigest() # If a command is run multiple times, then wait for the first one to continue. Don't run it again. if command_hash in ALREADY_RUN: - logger.debug(f"Already running {command_hash} {command}") + logger.debug(f"Already running {command_hash} {command_string}") await ALREADY_RUN[command_hash].wait() return ALREADY_RUN[command_hash] = asyncio.Event() @@ -169,7 +188,7 @@ async def run_command(command, working_directory, description=None, check_hash=[ run_reason = f"{p.relative_to(working_directory, walk_up=True)} is newer" break if nothing_newer: - logger.debug(f"Nothing newer {command[-32:]}") + logger.debug(f"Nothing newer {command_string[-32:]}") ALREADY_RUN[command_hash].set() return else: @@ -196,7 +215,7 @@ async def run_command(command, working_directory, description=None, check_hash=[ track = tracks.pop() start_time = time.perf_counter_ns() // 1000 process = await asyncio.create_subprocess_shell( - command, + command_string, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, cwd=working_directory, @@ -242,22 +261,25 @@ async def run_command(command, working_directory, description=None, check_hash=[ raise cancellation if description: logger.info(f"{description} ({run_reason})") - logger.debug(command) + logger.debug(command_string) else: - logger.info(f"{command} ({run_reason})") + logger.info(f"{command_string} ({run_reason})") if old_newest_file == newest_file: logger.error("No files were modified by the command.") raise RuntimeError() else: if command_hash in LAST_BUILD_TIMES: del LAST_BUILD_TIMES[command_hash] + logger.error(command_string) + if responsefile_contents: + logger.error(f"Response file contents:\n{responsefile_contents}") + logger.error(f"Return code: {process.returncode}") if stdout: logger.info(stdout.decode("utf-8").strip()) if stderr: logger.warning(stderr.decode("utf-8").strip()) if not stdout and not stderr: logger.warning("No output") - logger.error(command) if cancellation: raise cancellation raise RuntimeError() @@ -335,8 +357,8 @@ async def preprocess( ): output_file.parent.mkdir(parents=True, exist_ok=True) depfile = output_file.parent / (output_file.name + ".d") - if depfile.exists(): - pass + responsefile = output_file.parent / (output_file.name + ".rsp") + await run_command( [ self.c_compiler, @@ -354,6 +376,7 @@ async def preprocess( description=f"Preprocess {source_file.relative_to(self.srcdir)} -> {output_file.relative_to(self.builddir)}", working_directory=self.srcdir, check_hash=[output_file], + responsefile=responsefile, ) async def compile( @@ -365,6 +388,7 @@ async def compile( output_file = self.builddir / output_file output_file.parent.mkdir(parents=True, exist_ok=True) depfile = output_file.with_suffix(".d") + responsefile = output_file.with_suffix(".rsp") extradeps = [] if depfile.exists(): depfile_contents = depfile.read_text().split() @@ -375,25 +399,30 @@ async def compile( extradeps.append(pathlib.Path(dep)) else: extradeps.append(self.srcdir / dep) + await run_command( - [self.c_compiler, self.cflags, "-MMD", "-c", source_file, *flags, "-o", output_file], + [ + self.c_compiler, + self.cflags, + "-MMD", + "-c", + source_file, + *flags, + "-o", + output_file, + ], description=f"Compile {source_file.relative_to(self.srcdir)} -> {output_file.relative_to(self.builddir)}", working_directory=self.srcdir, extradeps=extradeps, + responsefile=responsefile, ) async def archive(self, objects: list[pathlib.Path], output_file: pathlib.Path): output_file.parent.mkdir(parents=True, exist_ok=True) - # Do one file at a time so that we don't have a long command line. run_command - # should skip unchanged files ok. - input_files = output_file.with_suffix(output_file.suffix + ".input_files") - input_file_content = "\n".join(str(p) for p in objects) - # Windows paths have \ as separator but ar wants them as / (like UNIX) - input_file_content = input_file_content.replace("\\", "/") - input_files.write_text(input_file_content) + responsefile = output_file.with_suffix(".rsp") await run_command( - [self.ar, "rvs", output_file, f"@{input_files}"], + [self.ar, "rvs", output_file, *objects], description=f"Create archive {output_file.relative_to(self.srcdir)}", working_directory=self.srcdir, - extradeps=objects, + responsefile=responsefile, ) diff --git a/ports/zephyr-cp/cptools/gen_compat2driver.py b/ports/zephyr-cp/cptools/gen_compat2driver.py index 0cb6a16f9da54..ffe8da185969f 100644 --- a/ports/zephyr-cp/cptools/gen_compat2driver.py +++ b/ports/zephyr-cp/cptools/gen_compat2driver.py @@ -2,8 +2,8 @@ mapping = {} -drivers = pathlib.Path("lib/zephyr/drivers") -for p in drivers.glob("**/*.c"): +drivers = pathlib.Path("zephyr/drivers") +for p in drivers.glob("**/*.[ch]"): for line in p.open(): if line.startswith("#define DT_DRV_COMPAT"): compat = line.rsplit(None, 1)[-1].strip() diff --git a/ports/zephyr-cp/cptools/get_west_shield_args.py b/ports/zephyr-cp/cptools/get_west_shield_args.py new file mode 100644 index 0000000000000..deda6bf5f26f8 --- /dev/null +++ b/ports/zephyr-cp/cptools/get_west_shield_args.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +"""Resolve shield arguments for west build. + +Priority: +1. SHIELD / SHIELDS make variables (if explicitly provided) +2. SHIELD / SHIELDS from boards///circuitpython.toml +""" + +import argparse +import os +import pathlib +import re +import shlex + +import board_tools + + +def split_shields(raw): + if not raw: + return [] + + return [shield for shield in re.split(r"[,;\s]+", raw.strip()) if shield] + + +def dedupe(values): + deduped = [] + seen = set() + + for value in values: + if value in seen: + continue + seen.add(value) + deduped.append(value) + + return deduped + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("board") + args = parser.parse_args() + + portdir = pathlib.Path(__file__).resolve().parent.parent + + _, mpconfigboard = board_tools.load_mpconfigboard(portdir, args.board) + + shield_origin = os.environ.get("SHIELD_ORIGIN", "undefined") + shields_origin = os.environ.get("SHIELDS_ORIGIN", "undefined") + + shield_override = os.environ.get("SHIELD", "") + shields_override = os.environ.get("SHIELDS", "") + + override_requested = shield_origin != "undefined" or shields_origin != "undefined" + + if override_requested: + shields = split_shields(shield_override) + split_shields(shields_override) + else: + shields = board_tools.get_shields(mpconfigboard) + + shields = dedupe(shields) + + west_shield_args = [] + for shield in shields: + west_shield_args.extend(("--shield", shield)) + + print(shlex.join(west_shield_args)) + + +if __name__ == "__main__": + main() diff --git a/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py b/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py index 0ed280cbc6775..f42fc1a3a8547 100644 --- a/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py +++ b/ports/zephyr-cp/cptools/pre_zephyr_build_prep.py @@ -2,7 +2,6 @@ import pathlib import subprocess import sys -import tomllib import board_tools @@ -10,14 +9,20 @@ board = sys.argv[-1] -mpconfigboard = board_tools.find_mpconfigboard(portdir, board) -if mpconfigboard is None: +_, mpconfigboard = board_tools.load_mpconfigboard(portdir, board) +if not mpconfigboard: # Assume it doesn't need any prep. sys.exit(0) -with mpconfigboard.open("rb") as f: - mpconfigboard = tomllib.load(f) - blobs = mpconfigboard.get("BLOBS", []) +blob_fetch_args = mpconfigboard.get("blob_fetch_args", {}) for blob in blobs: - subprocess.run(["west", "blobs", "fetch", blob], check=True) + args = blob_fetch_args.get(blob, []) + subprocess.run(["west", "blobs", "fetch", blob, *args], check=True) + +if board.endswith("bsim"): + subprocess.run( + ["make", "everything", "-j", "8"], + cwd=portdir / "tools" / "bsim", + check=True, + ) diff --git a/ports/zephyr-cp/cptools/tests/README.md b/ports/zephyr-cp/cptools/tests/README.md new file mode 100644 index 0000000000000..6bc8aa43ce694 --- /dev/null +++ b/ports/zephyr-cp/cptools/tests/README.md @@ -0,0 +1,28 @@ +# Zephyr2CP Tests + +This directory contains unit tests for the `zephyr2cp.py` module using real device tree parsing and pytest. + +## Running Tests + +To run all tests: +```bash +cd /home/tannewt/repos/circuitpython/ports/zephyr-cp +make test +``` + +For verbose output: +```bash +pytest test_zephyr2cp.py -v +``` + +To run specific test classes: +```bash +pytest test_zephyr2cp.py::TestFindFlashDevices -v +pytest test_zephyr2cp.py::TestFindRAMRegions -v +pytest test_zephyr2cp.py::TestIntegration -v +``` + +To run a specific test: +```bash +pytest test_zephyr2cp.py::TestFindFlashDevices::test_valid_flash_device -v +``` diff --git a/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py b/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py new file mode 100644 index 0000000000000..b147ae0605ed1 --- /dev/null +++ b/ports/zephyr-cp/cptools/tests/test_zephyr2cp.py @@ -0,0 +1,589 @@ +import sys +import pathlib +import tempfile + +# Add devicetree library to path +portdir = pathlib.Path(__file__).parent.parent.parent +sys.path.append(str(portdir / "zephyr/scripts/dts/python-devicetree/src/")) + +from devicetree import dtlib + +# Add parent directory to path to import zephyr2cp +sys.path.insert(0, str(pathlib.Path(__file__).parent.parent)) + +# Mock cpbuild before importing +sys.modules["cpbuild"] = type(sys)("cpbuild") +sys.modules["cpbuild"].run_in_thread = lambda x: x + +from zephyr2cp import find_flash_devices, find_ram_regions, BLOCKED_FLASH_COMPAT, MINIMUM_RAM_SIZE + + +def parse_dts_string(dts_content): + """ + Parse a device tree string and return the dtlib.DT object. + + Args: + dts_content: String containing device tree source + + Returns: + dtlib.DT object with parsed device tree + """ + with tempfile.NamedTemporaryFile(mode="w", suffix=".dts", delete=False) as f: + f.write(dts_content) + f.flush() + temp_path = f.name + + try: + dt = dtlib.DT(temp_path) + return dt + finally: + pathlib.Path(temp_path).unlink() + + +class TestFindFlashDevices: + """Test suite for find_flash_devices function.""" + + def test_no_compatible_returns_empty(self): + """Test that device tree with no flash devices returns empty list.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + memory@0 { + reg = <0x0 0x100000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert result == [] + + def test_chosen_flash_excluded(self): + """Test that chosen flash nodes are excluded.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "soc-nv-flash"; + reg = <0x0 0x100000>; + }; + + chosen { + zephyr,flash = &flash0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert result == [], "Chosen flash should be excluded" + + def test_blocked_compat_excluded(self): + """Test that blocked compatible strings are excluded.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + qspi0: qspi@40000 { + compatible = "renesas,ra-qspi"; + reg = <0x40000 0x1000>; + }; + + spi0: spi@50000 { + compatible = "nordic,nrf-spim"; + reg = <0x50000 0x1000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert result == [], "Blocked flash controllers should be excluded" + + def test_valid_flash_device(self): + """Test that valid flash device is detected.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0 0x100000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert len(result) == 1 + assert result[0] == "flash0" + + def test_valid_flash_device_multiple_drivers(self): + """Test that valid flash device is detected.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "other,driver", "jedec,spi-nor"; + reg = <0x0 0x100000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert len(result) == 1 + assert result[0] == "flash0" + + def test_external_flash_not_chosen(self): + """Test external flash is included when internal is chosen.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + internal_flash: flash@0 { + compatible = "soc-nv-flash"; + reg = <0x0 0x100000>; + }; + + external_flash: flash@1000000 { + compatible = "jedec,spi-nor"; + reg = <0x1000000 0x800000>; + }; + + chosen { + zephyr,flash = &internal_flash; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + + # Should only include external flash, not chosen internal flash + assert len(result) == 1 + assert "external_flash" in result[0] + assert "internal_flash" not in result[0] + + def test_disabled_flash_excluded(self): + """Test that disabled flash devices are excluded.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0 0x100000>; + status = "disabled"; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_flash_devices(dt) + assert result == [], "Disabled flash should be excluded" + + +class TestFindRAMRegions: + """Test suite for find_ram_regions function.""" + + def test_no_ram_returns_empty(self): + """Test that device tree with no RAM returns empty list.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + flash@0 { + compatible = "soc-nv-flash"; + reg = <0x0 0x100000>; + }; + + chosen { + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + assert result == [] + + def test_chosen_sram_basic(self): + """Test chosen sram region is detected correctly.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + + chosen { + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + + assert len(result) == 1 + label, start, end, size, path = result[0] + + assert label == "sram0" + assert start == "z_mapped_end" + assert ( + end + == "(uint32_t*) (DT_REG_ADDR(DT_NODELABEL(sram0)) + DT_REG_SIZE(DT_NODELABEL(sram0)))" + ) + assert size == 0x40000 + + def test_memory_region_with_custom_name(self): + """Test memory region with zephyr,memory-region property.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + + reserved_mem: memory@30000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x30000000 0x10000>; + zephyr,memory-region = "CUSTOM_REGION"; + }; + + chosen { + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + + # Should have both regions, chosen first + assert len(result) == 2 + + # First should be chosen SRAM + assert result[0][0] == "sram0" + + # Second should be custom region + label, start, end, size, path = result[1] + assert label == "reserved_mem" + assert start == "__CUSTOM_REGION_end" + + def test_memory_region_requires_sram_or_device_type(self): + """Test memory regions require mmio-sram compatibility or device_type=memory.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + + reserved_mem: memory@30000000 { + compatible = "zephyr,memory-region"; + reg = <0x30000000 0x10000>; + zephyr,memory-region = "CUSTOM_REGION"; + }; + + external_mem: memory@40000000 { + compatible = "zephyr,memory-region"; + device_type = "memory"; + reg = <0x40000000 0x20000>; + zephyr,memory-region = "EXT_REGION"; + }; + + chosen { + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + + assert len(result) == 2 + assert result[0][0] == "sram0" + assert result[1][0] == "external_mem" + + def test_disabled_ram_excluded(self): + """Test that disabled RAM regions are excluded.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + + sram1: memory@30000000 { + compatible = "zephyr,memory-region", "mmio-sram"; + reg = <0x30000000 0x10000>; + status = "disabled"; + zephyr,memory-region = "CUSTOM_REGION"; + }; + + chosen { + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + result = find_ram_regions(dt) + + # Should only have chosen SRAM, not disabled one + assert len(result) == 1 + assert result[0][0] == "sram0" + + +class TestIntegration: + """Integration tests with realistic device tree configurations.""" + + def test_typical_nrf_board_configuration(self): + """Test typical Nordic nRF board with internal and external flash.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + + soc { + #address-cells = <1>; + #size-cells = <1>; + + flash0: flash@0 { + compatible = "soc-nv-flash"; + reg = <0x0 0x100000>; + }; + + sram0: memory@20000000 { + compatible = "mmio-sram"; + reg = <0x20000000 0x40000>; + }; + }; + + external_flash: spi_flash@0 { + compatible = "jedec,spi-nor"; + reg = <0x0 0x800000>; + }; + + chosen { + zephyr,flash = &flash0; + zephyr,sram = &sram0; + }; +}; +""" + dt = parse_dts_string(dts) + + # Test flash detection + flashes = find_flash_devices(dt) + assert len(flashes) == 1, "Should find external flash only" + assert "external_flash" in flashes[0] + + # Test RAM detection + rams = find_ram_regions(dt) + assert len(rams) == 1, "Should find chosen SRAM only" + assert rams[0][0] == "sram0" + + def test_board_with_nrf5340_regions(self): + """Test that RAM subregions are included with the right addresses.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + chosen { + zephyr,sram = &sram0_image; + }; + /* node '/soc/peripheral@50000000/qspi@2b000' defined in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:475 */ + qspi: qspi@2b000 { + compatible = "nordic,nrf-qspi"; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:476 */ + #address-cells = < 0x1 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:477 */ + #size-cells = < 0x0 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:478 */ + reg = < 0x2b000 0x1000 >, + < 0x10000000 0x10000000 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:479 */ + reg-names = "qspi", + "qspi_mm"; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:480 */ + interrupts = < 0x2b 0x1 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_peripherals.dtsi:481 */ + status = "okay"; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:118 */ + + /* node '/soc/peripheral@50000000/qspi@2b000/mx25r6435f@0' defined in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:123 */ + mx25r64: mx25r6435f@0 { + compatible = "nordic,qspi-nor"; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:124 */ + reg = < 0x0 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:125 */ + writeoc = "pp4io"; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:127 */ + readoc = "read4io"; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:129 */ + sck-frequency = < 0x7a1200 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:130 */ + jedec-id = [ C2 28 17 ]; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:131 */ + sfdp-bfp = [ E5 20 F1 FF FF FF FF 03 44 EB 08 6B 08 3B 04 BB EE FF FF FF FF FF 00 FF FF FF 00 + FF 0C 20 0F 52 10 D8 00 FF 23 72 F5 00 82 ED 04 CC 44 83 68 44 30 B0 30 B0 F7 C4 + D5 5C 00 BE 29 FF F0 D0 FF FF ]; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:132 */ + size = < 0x4000000 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:136 */ + has-dpd; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:137 */ + t-enter-dpd = < 0x2710 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:138 */ + t-exit-dpd = < 0x88b8 >; /* in zephyr/boards/nordic/nrf5340dk/nrf5340_cpuapp_common.dtsi:139 */ + }; + }; + + /* node '/soc/memory@20000000' defined in zephyr/dts/arm/nordic/nrf5340_cpuapp.dtsi:55 */ + sram0: memory@20000000 { + compatible = "mmio-sram"; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp.dtsi:56 */ + #address-cells = < 0x1 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp.dtsi:57 */ + #size-cells = < 0x1 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp.dtsi:58 */ + reg = < 0x20000000 0x80000 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_qkaa.dtsi:15 */ + ranges = < 0x0 0x20000000 0x80000 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_qkaa.dtsi:16 */ + + /* node '/soc/memory@20000000/sram@0' defined in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:16 */ + sram0_image: sram@0 { + reg = < 0x0 0x70000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:18 */ + ranges = < 0x0 0x0 0x70000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:19 */ + #address-cells = < 0x1 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:20 */ + #size-cells = < 0x1 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:21 */ + + /* node '/soc/memory@20000000/sram@0/sram0_image@0' defined in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:23 */ + sram0_s: sram0_image@0 { + reg = < 0x0 0x40000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:25 */ + }; + }; + + /* node '/soc/memory@20000000/sram@40000' defined in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:33 */ + sram0_ns: sram@40000 { + reg = < 0x40000 0x40000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:35 */ + ranges = < 0x0 0x40000 0x40000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:36 */ + #address-cells = < 0x1 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:37 */ + #size-cells = < 0x1 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:38 */ + + /* node '/soc/memory@20000000/sram@40000/sram0_ns@0' defined in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:40 */ + sram0_ns_app: sram0_ns@0 { + reg = < 0x0 0x30000 >; /* in zephyr/dts/vendor/nordic/nrf5340_sram_partition.dtsi:42 */ + }; + }; + + /* node '/soc/memory@20000000/sram@70000' defined in zephyr/dts/vendor/nordic/nrf5340_shared_sram_partition.dtsi:27 */ + sram0_shared: sram@70000 { + reg = < 0x70000 0x10000 >; /* in zephyr/dts/vendor/nordic/nrf5340_shared_sram_partition.dtsi:29 */ + phandle = < 0x11 >; /* in zephyr/dts/arm/nordic/nrf5340_cpuapp_ipc.dtsi:9 */ + }; + }; +}; +""" + dt = parse_dts_string(dts) + flashes = find_flash_devices(dt) + rams = find_ram_regions(dt) + + # Should only get chosen SRAM + assert len(rams) == 1 + assert rams[0][0] == "sram0_image" + + assert len(flashes) == 1 + assert flashes[0] == "mx25r64" + + def test_board_with_chosen_memory_region(self): + """Test that RAM subregions are included with the right addresses.""" + dts = """ +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + chosen { + zephyr,sram = &axisram2; /* in zephyr/boards/st/nucleo_n657x0_q/nucleo_n657x0_q_common.dtsi:18 */ + }; + + + /* node '/memory@34000000' defined in zephyr/dts/arm/st/n6/stm32n6.dtsi:42 */ + axisram1: memory@34000000 { + compatible = "zephyr,memory-region", + "mmio-sram"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:43 */ + zephyr,memory-region = "AXISRAM1"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:44 */ + reg = < 0x34000000 0x200000 >; /* in zephyr/dts/arm/st/n6/stm32n657X0.dtsi:12 */ + }; + + /* node '/memory@34180400' defined in zephyr/dts/arm/st/n6/stm32n6.dtsi:47 */ + axisram2: memory@34180400 { + compatible = "zephyr,memory-region", + "mmio-sram"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:48 */ + zephyr,memory-region = "AXISRAM2"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:49 */ + reg = < 0x34180400 0x7fc00 >; /* in zephyr/dts/arm/st/n6/stm32n657X0.dtsi:17 */ + }; + + /* node '/soc/ramcfg@42023100' defined in zephyr/dts/arm/st/n6/stm32n6.dtsi:251 */ + ramcfg_sram3_axi: ramcfg@42023100 { + compatible = "st,stm32n6-ramcfg"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:252 */ + #address-cells = < 0x1 >; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:253 */ + #size-cells = < 0x1 >; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:254 */ + reg = < 0x42023100 0x80 >; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:255 */ + + /* node '/soc/ramcfg@42023100/memory@34200000' defined in zephyr/dts/arm/st/n6/stm32n6.dtsi:259 */ + axisram3: memory@34200000 { + compatible = "zephyr,memory-region", + "mmio-sram"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:260 */ + zephyr,memory-region = "AXISRAM3"; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:261 */ + zephyr,memory-attr = < 0x100000 >; /* in zephyr/dts/arm/st/n6/stm32n6.dtsi:262 */ + reg = < 0x34200000 0x70000 >; /* in zephyr/dts/arm/st/n6/stm32n657X0.dtsi:23 */ + status = "disabled"; /* in zephyr/dts/arm/st/n6/stm32n657X0.dtsi:24 */ + }; + }; +}; +""" + dt = parse_dts_string(dts) + rams = find_ram_regions(dt) + + print(rams) + + # Should only get chosen SRAM + assert len(rams) == 2 + assert rams[0][0] == "axisram2" + assert rams[1][0] == "axisram1" diff --git a/ports/zephyr-cp/cptools/zephyr2cp.py b/ports/zephyr-cp/cptools/zephyr2cp.py index cd0e3a6f2aade..d40501f1d1d00 100644 --- a/ports/zephyr-cp/cptools/zephyr2cp.py +++ b/ports/zephyr-cp/cptools/zephyr2cp.py @@ -1,16 +1,29 @@ import logging import pathlib -import cpbuild -from devicetree import dtlib +import cpbuild import yaml - from compat2driver import COMPAT_TO_DRIVER +from devicetree import dtlib logger = logging.getLogger(__name__) +logger.setLevel(logging.DEBUG) + +# GPIO flags defined here: include/zephyr/dt-bindings/gpio/gpio.h +GPIO_ACTIVE_LOW = 1 << 0 + +MINIMUM_RAM_SIZE = 1024 MANUAL_COMPAT_TO_DRIVER = { "renesas_ra_nv_flash": "flash", + "soc_nv_flash": "flash", + "nordic_nrf_uarte": "serial", + "nordic_nrf_uart": "serial", + "nordic_nrf_twim": "i2c", + "nordic_nrf_twi": "i2c", + "nordic_nrf_spim": "spi", + "nordic_nrf_spi": "spi", + "nordic_nrf_i2s": "i2s", } # These are controllers, not the flash devices themselves. @@ -20,6 +33,10 @@ "nordic,nrf-spim", ) +BUSIO_CLASSES = {"serial": "UART", "i2c": "I2C", "spi": "SPI"} + +AUDIOBUSIO_CLASSES = {"i2s": "I2SOut"} + CONNECTORS = { "mikro-bus": [ "AN", @@ -82,6 +99,94 @@ "D12", "D13", ], + "nordic,expansion-board-header": [ + "P1_04", + "P1_05", + "P1_06", + "P1_07", + "P1_08", + "P1_09", + "P1_10", + "P1_11", + "P1_12", + "P1_13", + "P1_14", + ], + "arducam,dvp-20pin-connector": [ + "SCL", + "SDA", + "VS", + "HS", + "PCLK", + "XCLK", + "D7", + "D6", + "D5", + "D4", + "D3", + "D2", + "D1", + "D0", + "PEN", + "PDN", + "GPIO0", + "GPIO1", + ], + "nxp,cam-44pins-connector": ["CAM_RESETB", "CAM_PWDN"], + "nxp,lcd-8080": [ + "TOUCH_SCL", + "TOUCH_SDA", + "TOUCH_INT", + "BACKLIGHT", + "RESET", + "LCD_DC", + "LCD_CS", + "LCD_WR", + "LCD_RD", + "LCD_TE", + "LCD_D0", + "LCD_D1", + "LCD_D2", + "LCD_D3", + "LCD_D4", + "LCD_D5", + "LCD_D6", + "LCD_D7", + "LCD_D8", + "LCD_D9", + "LCD_D10", + "LCD_D11", + "LCD_D12", + "LCD_D13", + "LCD_D14", + "LCD_D15", + ], + "nxp,lcd-pmod": [ + "LCD_WR", + "TOUCH_SCL", + "LCD_DC", + "TOUCH_SDA", + "LCD_MOSI", + "TOUCH_RESET", + "LCD_CS", + "TOUCH_INT", + ], + "raspberrypi,csi-connector": [ + "CSI_D0_N", + "CSI_D0_P", + "CSI_D1_N", + "CSI_D1_P", + "CSI_CK_N", + "CSI_CK_P", + "CSI_D2_N", + "CSI_D2_P", + "CSI_D3_N", + "CSI_D3_P", + "IO0", + "IO1", + "I2C_SCL", + "I2C_SDA", + ], "renesas,ra-gpio-mipi-header": [ "IIC_SDA", "DISP_BLEN", @@ -89,25 +194,287 @@ "DISP_INT", "DISP_RST", ], + "renesas,ra-parallel-graphics-header": [ + "DISP_BLEN", + "IIC_SDA", + "DISP_INT", + "IIC_SCL", + "DISP_RST", + "LCDC_TCON0", + "LCDC_CLK", + "LCDC_TCON2", + "LCDC_TCON1", + "LCDC_EXTCLK", + "LCDC_TCON3", + "LCDC_DATA01", + "LCDC_DATA00", + "LCDC_DATA03", + "LCDC_DATA02", + "LCDC_DATA05", + "LCDC_DATA04", + "LCDC_DATA07", + "LCDC_DATA16", + "LCDC_DATA09", + "LCDC_DATA08", + "LCDC_DATA11", + "LCDC_DATA10", + "LCDC_DATA13", + "LCDC_DATA12", + "LCDC_DATA15", + "LCDC_DATA14", + "LCDC_DATA17", + "LCDC_DATA16", + "LCDC_DATA19", + "LCDC_DATA18", + "LCDC_DATA21", + "LCDC_DATA20", + "LCDC_DATA23", + "LCDC_DATA22", + ], + "st,stm32-dcmi-camera-fpu-330zh": [ + "SCL", + "SDA", + "RESET", + "PEN", + "VS", + "HS", + "PCLK", + "D7", + "D6", + "D5", + "D4", + "D3", + "D2", + "D1", + "D0", + ], + "raspberrypi,pico-header": [ + "GP0", + "GP1", + "GP2", + "GP3", + "GP4", + "GP5", + "GP6", + "GP7", + "GP8", + "GP9", + "GP10", + "GP11", + "GP12", + "GP13", + "GP14", + "GP15", + "GP16", + "GP17", + "GP18", + "GP19", + "GP20", + "GP21", + "GP22", + ["GP26_A0", "GP26", "A0"], + ["GP27_A1", "GP27", "A1"], + ["GP28_A2", "GP28", "A2"], + ], } +EXCEPTIONAL_DRIVERS = ["entropy", "gpio", "led"] + + +def find_flash_devices(device_tree): + """ + Find all flash devices from a device tree. + + Args: + device_tree: Parsed device tree (dtlib.DT object) + + Returns: + List of device tree flash device reference strings + """ + # Build path2chosen mapping + path2chosen = {} + for k in device_tree.root.nodes["chosen"].props: + value = device_tree.root.nodes["chosen"].props[k] + path2chosen[value.to_path()] = k + + flashes = [] + logger.debug("Flash devices:") + + # Traverse all nodes in the device tree + remaining_nodes = set([device_tree.root]) + while remaining_nodes: + node = remaining_nodes.pop() + remaining_nodes.update(node.nodes.values()) + + # Get compatible strings + compatible = [] + if "compatible" in node.props: + compatible = node.props["compatible"].to_strings() + + # Get status + status = node.props.get("status", None) + if status is None: + status = "okay" + else: + status = status.to_string() + + # Check if this is a flash device + if not compatible or status != "okay": + continue + + # Check for flash driver via compat2driver + drivers = [] + for c in compatible: + underscored = c.replace(",", "_").replace("-", "_") + driver = COMPAT_TO_DRIVER.get(underscored, None) + if not driver: + driver = MANUAL_COMPAT_TO_DRIVER.get(underscored, None) + if driver: + drivers.append(driver) + logger.debug(f" {node.labels[0] if node.labels else node.name} drivers: {drivers}") + + if "flash" not in drivers: + continue + + # Skip chosen nodes because they are used by Zephyr + if node in path2chosen: + logger.debug( + f" skipping flash {node.labels[0] if node.labels else node.name} (chosen)" + ) + continue + + # Skip blocked flash compatibles (controllers, not actual flash devices) + if compatible[0] in BLOCKED_FLASH_COMPAT: + logger.debug( + f" skipping flash {node.labels[0] if node.labels else node.name} (blocked compat)" + ) + continue + + if node.labels: + flashes.append(node.labels[0]) + + logger.debug("Flash devices:") + for flash in flashes: + logger.debug(f" {flash}") + + return flashes + + +def _label_to_end(label): + return f"(uint32_t*) (DT_REG_ADDR(DT_NODELABEL({label})) + DT_REG_SIZE(DT_NODELABEL({label})))" + + +def find_ram_regions(device_tree): + """ + Find all RAM regions from a device tree. Includes the zephyr,sram node and + any zephyr,memory-region nodes. + + Returns: + List of RAM region info tuples: (label, start, end, size, path) + """ + rams = [] + chosen = None + # Get the chosen SRAM node directly + if "zephyr,sram" in device_tree.root.nodes["chosen"].props: + chosen = device_tree.root.nodes["chosen"].props["zephyr,sram"].to_path() + label = chosen.labels[0] + size = chosen.props["reg"].to_nums()[1] + logger.debug(f"Found chosen SRAM node: {label} with size {size}") + rams.append((label, "z_mapped_end", _label_to_end(label), size, chosen.path)) + + # Traverse all nodes in the device tree to find memory-region nodes + remaining_nodes = set([device_tree.root]) + while remaining_nodes: + node = remaining_nodes.pop() + + # Check status first so we don't add child nodes that aren't active. + status = node.props.get("status", None) + if status is None: + status = "okay" + else: + status = status.to_string() + + if status != "okay": + continue + + if node == chosen: + continue + + remaining_nodes.update(node.nodes.values()) + + if "compatible" not in node.props or not node.labels: + continue + + compatible = node.props["compatible"].to_strings() + + if "zephyr,memory-region" not in compatible or "zephyr,memory-region" not in node.props: + continue + + is_mmio_sram = "mmio-sram" in compatible + device_type = node.props.get("device_type") + has_memory_device_type = device_type and device_type.to_string() == "memory" + if not (is_mmio_sram or has_memory_device_type): + continue + + size = node.props["reg"].to_nums()[1] + + start = "__" + node.props["zephyr,memory-region"].to_string() + "_end" + end = _label_to_end(node.labels[0]) + + # Filter by minimum size + if size >= MINIMUM_RAM_SIZE: + logger.debug( + f"Adding extra RAM info: ({node.labels[0]}, {start}, {end}, {size}, {node.path})" + ) + info = (node.labels[0], start, end, size, node.path) + rams.append(info) + + return rams + @cpbuild.run_in_thread -def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 +def zephyr_dts_to_cp_board(board_id, portdir, builddir, zephyrbuilddir, mpconfigboard=None): # noqa: C901 board_dir = builddir / "board" # Auto generate board files from device tree. board_info = { "wifi": False, "usb_device": False, + "_bleio": False, + "hostnetwork": board_id in ["native_sim"], + "audiobusio": False, } + config_bt_enabled = False + config_bt_found = False + config_present = True + config = zephyrbuilddir / ".config" + if not config.exists(): + config_present = False + else: + for line in config.read_text().splitlines(): + if line.startswith("CONFIG_BT="): + config_bt_enabled = line.strip().endswith("=y") + config_bt_found = True + break + if line.startswith("# CONFIG_BT is not set"): + config_bt_enabled = False + config_bt_found = True + break + runners = zephyrbuilddir / "runners.yaml" runners = yaml.safe_load(runners.read_text()) zephyr_board_dir = pathlib.Path(runners["config"]["board_dir"]) board_yaml = zephyr_board_dir / "board.yml" board_yaml = yaml.safe_load(board_yaml.read_text()) - board_info["vendor_id"] = board_yaml["board"]["vendor"] + if "board" not in board_yaml and "boards" in board_yaml: + for board in board_yaml["boards"]: + if board["name"] == board_id: + board_yaml = board + break + else: + board_yaml = board_yaml["board"] + board_info["vendor_id"] = board_yaml["vendor"] vendor_index = zephyr_board_dir.parent / "index.rst" if vendor_index.exists(): vendor_index = vendor_index.read_text() @@ -116,9 +483,11 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 else: vendor_name = board_info["vendor_id"] board_info["vendor"] = vendor_name - soc_name = board_yaml["board"]["socs"][0]["name"] + soc_name = board_yaml["socs"][0]["name"] board_info["soc"] = soc_name - board_name = board_yaml["board"]["full_name"] + board_name = board_yaml["full_name"] + if mpconfigboard and "NAME" in mpconfigboard: + board_name = mpconfigboard["NAME"] board_info["name"] = board_name # board_id_yaml = zephyr_board_dir / (zephyr_board_dir.name + ".yaml") # board_id_yaml = yaml.safe_load(board_id_yaml.read_text()) @@ -126,27 +495,42 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 # board_name = board_id_yaml["name"] dts = zephyrbuilddir / "zephyr.dts" - edt_pickle = dtlib.DT(dts) + device_tree = dtlib.DT(dts) node2alias = {} - for alias in edt_pickle.alias2node: - node = edt_pickle.alias2node[alias] + for alias in device_tree.alias2node: + node = device_tree.alias2node[alias] if node not in node2alias: node2alias[node] = [] node2alias[node].append(alias) ioports = {} all_ioports = [] board_names = {} - flashes = [] - rams = [] status_led = None + status_led_inverted = False path2chosen = {} chosen2path = {} + + # Find flash and RAM regions using extracted functions + flashes = find_flash_devices(device_tree) + rams = find_ram_regions(device_tree) # Returns filtered and sorted list + + # Store active Zephyr device labels per-driver so that we can make them available via board. + active_zephyr_devices = {} usb_num_endpoint_pairs = 0 - for k in edt_pickle.root.nodes["chosen"].props: - value = edt_pickle.root.nodes["chosen"].props[k] + ble_hardware_present = False + for k in device_tree.root.nodes["chosen"].props: + value = device_tree.root.nodes["chosen"].props[k] path2chosen[value.to_path()] = k chosen2path[k] = value.to_path() - remaining_nodes = set([edt_pickle.root]) + + chosen_display = chosen2path.get("zephyr,display") + if chosen_display is not None: + status = chosen_display.props.get("status", None) + if status is None or status.to_string() == "okay": + board_info["zephyr_display"] = True + board_info["displayio"] = True + + remaining_nodes = set([device_tree.root]) while remaining_nodes: node = remaining_nodes.pop() remaining_nodes.update(node.nodes.values()) @@ -161,52 +545,23 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 compatible = [] if "compatible" in node.props: compatible = node.props["compatible"].to_strings() - logger.debug(node.name, status) + logger.debug(f"{node.name}: {status}") + logger.debug(f"compatible: {compatible}") chosen = None if node in path2chosen: chosen = path2chosen[node] - logger.debug(" chosen:", chosen) + logger.debug(f" chosen: {chosen}") for c in compatible: underscored = c.replace(",", "_").replace("-", "_") driver = COMPAT_TO_DRIVER.get(underscored, None) - if "mmio" in c: - logger.debug(" ", c, node.labels, node.props) - address, size = node.props["reg"].to_nums() - end = address + size - if chosen == "zephyr,sram": - start = "z_mapped_end" - elif "zephyr,memory-region" in node.props: - start = "__" + node.props["zephyr,memory-region"].to_string() + "_end" - else: - # Check to see if the chosen sram is a subset of this region. If it is, - # then do as above for a smaller region and assume the rest is reserved. - chosen_sram = chosen2path["zephyr,sram"] - chosen_address, chosen_size = chosen_sram.props["reg"].to_nums() - chosen_end = chosen_address + chosen_size - if address <= chosen_address <= end and address <= chosen_end <= end: - start = "z_mapped_end" - address = chosen_address - size = chosen_size - end = chosen_end - else: - start = address - info = (node.labels[0], start, end, size, node.path) - if chosen == "zephyr,sram": - rams.insert(0, info) - else: - rams.append(info) if not driver: driver = MANUAL_COMPAT_TO_DRIVER.get(underscored, None) - logger.debug(" ", underscored, driver) - if not driver: + logger.debug(f" {c} -> {underscored} -> {driver}") + if not driver or status != "okay": continue - if driver == "flash" and status == "okay": - if not chosen and compatible[0] not in BLOCKED_FLASH_COMPAT: - # Skip chosen nodes because they are used by Zephyr. - flashes.append(f"DEVICE_DT_GET(DT_NODELABEL({node.labels[0]}))") - else: - logger.debug(" skipping due to blocked compat") - if driver == "usb/udc" and status == "okay": + if driver == "flash": + pass # Handled by find_flash_devices() + elif driver == "usb/udc" or "zephyr_udc0" in node.labels: board_info["usb_device"] = True props = node.props if "num-bidir-endpoints" not in props: @@ -220,8 +575,28 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 single_direction_endpoints.append(props[eps].to_num() if eps in props else 0) # Count separate in/out pairs as bidirectional. usb_num_endpoint_pairs += min(single_direction_endpoints) - if driver.startswith("wifi") and status == "okay": + elif driver.startswith("wifi"): board_info["wifi"] = True + elif driver == "bluetooth/hci": + ble_hardware_present = True + elif driver in AUDIOBUSIO_CLASSES: + # audiobusio driver (i2s, audio/dmic) + board_info["audiobusio"] = True + logger.info(f"Supported audiobusio driver: {driver}") + if driver not in active_zephyr_devices: + active_zephyr_devices[driver] = [] + active_zephyr_devices[driver].append(node.labels) + elif driver in EXCEPTIONAL_DRIVERS: + pass + elif driver in BUSIO_CLASSES: + # busio driver (i2c, spi, uart) + board_info["busio"] = True + logger.info(f"Supported busio driver: {driver}") + if driver not in active_zephyr_devices: + active_zephyr_devices[driver] = [] + active_zephyr_devices[driver].append(node.labels) + else: + logger.warning(f"Unsupported driver: {driver}") if gpio: if "ngpios" in node.props: @@ -231,22 +606,37 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 all_ioports.append(node.labels[0]) if status == "okay": ioports[node.labels[0]] = set(range(0, ngpios)) - if gpio_map: - i = 0 - for offset, t, label in gpio_map._markers: - if not label: - continue - num = int.from_bytes(gpio_map.value[offset + 4 : offset + 8], "big") - if (label, num) not in board_names: - board_names[(label, num)] = [] - board_names[(label, num)].append(CONNECTORS[compatible[0]][i]) - i += 1 + if gpio_map and compatible and compatible[0] != "gpio-nexus": + connector_pins = CONNECTORS.get(compatible[0], None) + if connector_pins is None: + logger.warning(f"Unsupported connector mapping compatible: {compatible[0]}") + else: + i = 0 + for offset, t, label in gpio_map._markers: + if not label: + continue + if i >= len(connector_pins): + logger.warning( + f"Connector mapping for {compatible[0]} has more pins than names; " + f"stopping at {len(connector_pins)}" + ) + break + num = int.from_bytes(gpio_map.value[offset + 4 : offset + 8], "big") + if (label, num) not in board_names: + board_names[(label, num)] = [] + pin_entry = connector_pins[i] + if isinstance(pin_entry, list): + board_names[(label, num)].extend(pin_entry) + else: + board_names[(label, num)].append(pin_entry) + i += 1 if "gpio-leds" in compatible: for led in node.nodes: led = node.nodes[led] props = led.props ioport = props["gpios"]._markers[1][2] num = int.from_bytes(props["gpios"].value[4:8], "big") + flags = int.from_bytes(props["gpios"].value[8:12], "big") if "label" in props: if (ioport, num) not in board_names: board_names[(ioport, num)] = [] @@ -257,6 +647,7 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 if "led0" in node2alias[led]: board_names[(ioport, num)].append("LED") status_led = (ioport, num) + status_led_inverted = flags & GPIO_ACTIVE_LOW board_names[(ioport, num)].extend(node2alias[led]) if "gpio-keys" in compatible: @@ -273,15 +664,21 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 board_names[(ioport, num)].append("BUTTON") board_names[(ioport, num)].extend(node2alias[key]) - a, b = all_ioports[:2] - i = 0 - while a[i] == b[i]: - i += 1 - shared_prefix = a[:i] - for ioport in ioports: - if not ioport.startswith(shared_prefix): - shared_prefix = "" - break + if len(all_ioports) > 1: + a, b = all_ioports[:2] + i = 0 + max_i = min(len(a), len(b)) + while i < max_i and a[i] == b[i]: + i += 1 + shared_prefix = a[:i] + for ioport in ioports: + if not ioport.startswith(shared_prefix): + shared_prefix = "" + break + elif all_ioports: + shared_prefix = all_ioports[0] + else: + shared_prefix = "" pin_defs = [] pin_declarations = ["#pragma once"] @@ -302,7 +699,13 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 board_pin_names = board_names.get((ioport, num), []) for board_pin_name in board_pin_names: - board_pin_name = board_pin_name.upper().replace(" ", "_").replace("-", "_") + board_pin_name = ( + board_pin_name.upper() + .replace(" ", "_") + .replace("-", "_") + .replace("(", "") + .replace(")", "") + ) board_pin_mapping.append( f"{{ MP_ROM_QSTR(MP_QSTR_{board_pin_name}), MP_ROM_PTR(&pin_{pin_object_name}) }}," ) @@ -312,33 +715,171 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 board_pin_mapping = "\n ".join(board_pin_mapping) mcu_pin_mapping = "\n ".join(mcu_pin_mapping) + zephyr_binding_headers = [] + zephyr_binding_objects = [] + zephyr_binding_labels = [] + i2sout_instance_names = [] + for driver, instances in active_zephyr_devices.items(): + # Determine if this is busio or audiobusio + if driver in BUSIO_CLASSES: + module = "busio" + driverclass = BUSIO_CLASSES[driver] + elif driver in AUDIOBUSIO_CLASSES: + module = "audiobusio" + driverclass = AUDIOBUSIO_CLASSES[driver] + else: + continue + + zephyr_binding_headers.append(f'#include "shared-bindings/{module}/{driverclass}.h"') + + # Designate a main device such as board.I2C or board.I2S. + if len(instances) == 1: + instances[0].append(driverclass) + else: + # Check to see if a main device has already been designated + found_main = False + for labels in instances: + for label in labels: + if label == driverclass: + found_main = True + if not found_main: + for priority_label in (f"zephyr_{driver}", f"arduino_{driver}"): + for labels in instances: + if priority_label in labels: + labels.append(driverclass) + found_main = True + break + if found_main: + break + for labels in instances: + instance_name = f"{driver.replace('/', '_')}_{labels[0]}" + c_function_name = f"_{instance_name}" + singleton_ptr = f"{c_function_name}_singleton" + function_object = f"{c_function_name}_obj" + obj_type = f"{module}_{driverclass.lower()}" + + # Handle special cases for different drivers + if driver == "serial": + # UART needs a receiver buffer + buffer_decl = f"static byte {instance_name}_buffer[128];" + construct_call = f"common_hal_busio_uart_construct_from_device(&{instance_name}_obj, DEVICE_DT_GET(DT_NODELABEL({labels[0]})), 128, {instance_name}_buffer)" + else: + # Default case (I2C, SPI, I2S) + buffer_decl = "" + construct_call = f"common_hal_{module}_{driverclass.lower()}_construct_from_device(&{instance_name}_obj, DEVICE_DT_GET(DT_NODELABEL({labels[0]})))" + + if driver == "i2s": + i2sout_instance_names.append(instance_name) + + zephyr_binding_objects.append( + f"""{buffer_decl} +static {obj_type}_obj_t {instance_name}_obj; +static mp_obj_t {singleton_ptr} = mp_const_none; +static mp_obj_t {c_function_name}(void) {{ + if ({singleton_ptr} != mp_const_none) {{ + return {singleton_ptr}; + }} + {singleton_ptr} = {construct_call}; + return {singleton_ptr}; +}} +static MP_DEFINE_CONST_FUN_OBJ_0({function_object}, {c_function_name});""".lstrip() + ) + for label in labels: + zephyr_binding_labels.append( + f"{{ MP_ROM_QSTR(MP_QSTR_{label.upper()}), MP_ROM_PTR(&{function_object}) }}," + ) + zephyr_binding_headers = "\n".join(zephyr_binding_headers) + zephyr_binding_objects = "\n".join(zephyr_binding_objects) + zephyr_binding_labels = "\n".join(zephyr_binding_labels) + + # Generate i2sout_reset() that stops all board I2SOut instances + if i2sout_instance_names: + stop_calls = "\n ".join( + f"common_hal_audiobusio_i2sout_stop(&{name}_obj);" for name in i2sout_instance_names + ) + i2sout_reset_func = f""" +void i2sout_reset(void) {{ + {stop_calls} +}}""" + else: + i2sout_reset_func = "" + + zephyr_display_header = "" + zephyr_display_object = "" + zephyr_display_board_entry = "" + if board_info.get("zephyr_display", False): + zephyr_display_header = """ +#include +#include +#include "shared-module/displayio/__init__.h" +#include "bindings/zephyr_display/Display.h" + """.strip() + zephyr_display_object = """ +void board_init(void) { +#if CIRCUITPY_ZEPHYR_DISPLAY && DT_HAS_CHOSEN(zephyr_display) + // Always allocate a display slot so board.DISPLAY is at least a valid + // NoneType object even if the underlying Zephyr display is unavailable. + primary_display_t *display_obj = allocate_display(); + if (display_obj == NULL) { + return; + } + + zephyr_display_display_obj_t *display = &display_obj->zephyr_display; + display->base.type = &mp_type_NoneType; + + const struct device *display_dev = device_get_binding(DEVICE_DT_NAME(DT_CHOSEN(zephyr_display))); + if (display_dev == NULL || !device_is_ready(display_dev)) { + return; + } + + display->base.type = &zephyr_display_display_type; + common_hal_zephyr_display_display_construct_from_device(display, display_dev, 0, true); +#endif +} + """.strip() + zephyr_display_board_entry = ( + "{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].zephyr_display) }," + ) + board_dir.mkdir(exist_ok=True, parents=True) header = board_dir / "mpconfigboard.h" if status_led: status_led = f"#define MICROPY_HW_LED_STATUS (&pin_{status_led})\n" + status_led_inverted = ( + f"#define MICROPY_HW_LED_STATUS_INVERTED ({'1' if status_led_inverted else '0'})\n" + ) else: status_led = "" + status_led_inverted = "" ram_list = [] ram_externs = [] max_size = 0 for ram in rams: device, start, end, size, path = ram max_size = max(max_size, size) - if isinstance(start, str): + # We always start at the end of a Zephyr linker section so we need the externs and &. + # Native/simulated boards don't have real memory-mapped RAM, so we allocate static arrays. + if board_id in ["native_sim"] or "bsim" in board_id: + ram_externs.append("// This is a native board so we provide all of RAM for our heaps.") + ram_externs.append(f"static uint32_t _{device}[{size // 4}]; // {path}") + start = f"(const uint32_t *) (_{device})" + end = f"(const uint32_t *)(_{device} + {size // 4})" + else: ram_externs.append(f"extern uint32_t {start};") start = "&" + start - else: - start = f"(uint32_t*) 0x{start:08x}" - ram_list.append(f" {start}, (uint32_t*) 0x{end:08x}, // {path}") + ram_list.append(f" {start}, {end}, // {path}") ram_list = "\n".join(ram_list) ram_externs = "\n".join(ram_externs) + flashes = [f"DEVICE_DT_GET(DT_NODELABEL({flash}))" for flash in flashes] + new_header_content = f"""#pragma once #define MICROPY_HW_BOARD_NAME "{board_name}" #define MICROPY_HW_MCU_NAME "{soc_name}" #define CIRCUITPY_RAM_DEVICE_COUNT {len(rams)} {status_led} +{status_led_inverted} """ if not header.exists() or header.read_text() != new_header_content: header.write_text(new_header_content) @@ -348,16 +889,33 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 pins.write_text(pin_declarations) board_c = board_dir / "board.c" + hostnetwork_include = "" + hostnetwork_entry = "" + if board_info.get("hostnetwork", False): + hostnetwork_include = ( + '#if CIRCUITPY_HOSTNETWORK\n#include "bindings/hostnetwork/__init__.h"\n#endif\n' + ) + hostnetwork_entry = ( + "#if CIRCUITPY_HOSTNETWORK\n" + " { MP_ROM_QSTR(MP_QSTR_NETWORK), MP_ROM_PTR(&common_hal_hostnetwork_obj) },\n" + "#endif\n" + ) + new_board_c_content = f""" // This file is autogenerated by build_circuitpython.py #include "shared-bindings/board/__init__.h" +{hostnetwork_include} + #include #include "py/obj.h" #include "py/mphal.h" +{zephyr_binding_headers} +{zephyr_display_header} + const struct device* const flashes[] = {{ {", ".join(flashes)} }}; const int circuitpy_flash_device_count = {len(flashes)}; @@ -369,6 +927,10 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 {pin_defs} +{zephyr_binding_objects} +{zephyr_display_object} +{i2sout_reset_func} + static const mp_rom_map_elem_t mcu_pin_globals_table[] = {{ {mcu_pin_mapping} }}; @@ -377,17 +939,36 @@ def zephyr_dts_to_cp_board(builddir, zephyrbuilddir): # noqa: C901 static const mp_rom_map_elem_t board_module_globals_table[] = {{ CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS +{hostnetwork_entry} +{zephyr_display_board_entry} {board_pin_mapping} -// {{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }}, +{zephyr_binding_labels} + }}; MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); """ - if not board_c.exists() or new_board_c_content != board_c.read_text(): - board_c.write_text(new_board_c_content) + board_c.write_text(new_board_c_content) + if ble_hardware_present: + if not config_present: + raise RuntimeError( + "Missing Zephyr .config; CONFIG_BT must be set explicitly when BLE hardware is present." + ) + if not config_bt_found: + raise RuntimeError( + "CONFIG_BT is missing from Zephyr .config; set it explicitly when BLE hardware is present." + ) + + board_info["_bleio"] = ble_hardware_present and config_bt_enabled board_info["source_files"] = [board_c] board_info["cflags"] = ("-I", board_dir) board_info["flash_count"] = len(flashes) + board_info["rotaryio"] = bool(ioports) board_info["usb_num_endpoint_pairs"] = usb_num_endpoint_pairs + + # Detect NVM partition from the device tree. + nvm_node = device_tree.label2node.get("nvm_partition") + board_info["nvm"] = nvm_node is not None + return board_info diff --git a/ports/zephyr-cp/debug.conf b/ports/zephyr-cp/debug.conf new file mode 100644 index 0000000000000..90c1f52d4db6b --- /dev/null +++ b/ports/zephyr-cp/debug.conf @@ -0,0 +1,15 @@ +CONFIG_DEBUG=y +CONFIG_DEBUG_OPTIMIZATIONS=y +CONFIG_LOG_MAX_LEVEL=4 + +CONFIG_STACK_SENTINEL=y +CONFIG_DEBUG_THREAD_INFO=y +CONFIG_DEBUG_INFO=y +CONFIG_EXCEPTION_STACK_TRACE=y + +CONFIG_ASSERT=y +CONFIG_LOG_BLOCK_IN_THREAD=y +CONFIG_FRAME_POINTER=y + +CONFIG_FLASH_LOG_LEVEL_DBG=y +CONFIG_LOG_MODE_IMMEDIATE=y diff --git a/ports/zephyr-cp/docs/perfetto-tracing.md b/ports/zephyr-cp/docs/perfetto-tracing.md new file mode 100644 index 0000000000000..ff35d361f70bd --- /dev/null +++ b/ports/zephyr-cp/docs/perfetto-tracing.md @@ -0,0 +1,150 @@ +# Perfetto Tracing + +The Zephyr port supports Perfetto tracing for performance analysis. This document +describes how to capture, validate, and view traces. + +## Capturing Traces + +Traces are written to `circuitpython.perfetto-trace` in the port directory when +running with tracing enabled (e.g., on native_sim). + +## Validating Traces + +### Using trace_processor + +The Perfetto trace_processor tool can validate and query trace files: + +```bash +~/repos/perfetto/tools/trace_processor circuitpython.perfetto-trace +``` + +This will download the trace_processor binary if needed and open an interactive +SQL shell. If the trace loads successfully, you can query it: + +```sql +SELECT COUNT(*) FROM slice; +``` + +### Using the Perfetto UI + +Open https://ui.perfetto.dev and drag your trace file onto the page. + +## Debugging Invalid Traces + +### Common Error: Packets Skipped Due to Invalid Incremental State + +If trace_processor reports packets being skipped with messages like: + +``` +packet_skipped_seq_needs_incremental_state_invalid +``` + +This means packets have `SEQ_NEEDS_INCREMENTAL_STATE` (value 2) set but no +prior packet set `SEQ_INCREMENTAL_STATE_CLEARED` (value 1) to initialize the +incremental state. + +**Root Cause**: The process descriptor packet (which sets `SEQ_INCREMENTAL_STATE_CLEARED`) +must be emitted before any other trace packets. + +**Diagnosis**: Use protoc to inspect the raw trace: + +```bash +protoc --decode_raw < circuitpython.perfetto-trace | head -100 +``` + +Look for field 13 (sequence_flags) in the first few packets: + +- `13: 1` = SEQ_INCREMENTAL_STATE_CLEARED (good - should be first) +- `13: 2` = SEQ_NEEDS_INCREMENTAL_STATE (requires prior cleared packet) + +A valid trace should have the process descriptor with `13: 1` as one of the +first packets. + +**Fix**: Ensure `perfetto_start()` is called before any trace events are emitted. +The descriptor emit functions in `perfetto_encoder.c` should check: + +```c +if (!started) { + perfetto_start(); +} +``` + +### Analyzing Raw Trace Structure + +To understand the trace structure: + +```bash +# Count total packets +protoc --decode_raw < circuitpython.perfetto-trace | grep -c "^1 {" + +# Find all sequence_flags values +protoc --decode_raw < circuitpython.perfetto-trace | grep "13:" | sort | uniq -c + +# Look for track descriptors (field 60) +protoc --decode_raw < circuitpython.perfetto-trace | grep -A20 "60 {" + +# Look for process descriptors (field 3 inside track_descriptor) +protoc --decode_raw < circuitpython.perfetto-trace | grep -B5 "3 {" +``` + +### Key Protobuf Field Numbers + +TracePacket fields: + +| Field | Description | +|-------|-------------| +| 8 | timestamp | +| 10 | trusted_packet_sequence_id | +| 11 | track_event | +| 12 | interned_data | +| 13 | sequence_flags | +| 60 | track_descriptor | + +TrackDescriptor fields (inside field 60): + +| Field | Description | +|-------|-------------| +| 1 | uuid | +| 2 | name | +| 3 | process (ProcessDescriptor) | +| 4 | thread (ThreadDescriptor) | +| 5 | parent_uuid | + +## Build Verification + +After modifying tracing code, verify the build is updated: + +```bash +# Check source vs object file timestamps +ls -la zephyr/subsys/tracing/perfetto/perfetto_encoder.c +ls -la zephyr/build/zephyr/subsys/tracing/perfetto/CMakeFiles/subsys__tracing__perfetto.dir/perfetto_encoder.c.obj +``` + +The object file timestamp must be newer than the source file timestamp. If not, +rebuild the project before capturing a new trace. + +## Architecture + +The tracing implementation consists of: + +- `perfetto_encoder.c`: Encodes trace packets using nanopb +- `perfetto_top.c`: Implements Zephyr tracing hooks (sys_trace_*) +- `perfetto_encoder.h`: Public API and UUID definitions + +Key UUIDs: + +| Constant | Value | Description | +|----------|-------|-------------| +| PROCESS_UUID | 1 | Root process track | +| ISR_TRACK_UUID | 2 | Interrupt service routine track | +| TRACE_TRACK_UUID | 3 | Top-level trace track | + +### Initialization Flow + +1. `SYS_INIT` calls `perfetto_init()` at POST_KERNEL priority 0 +2. `perfetto_init()` calls `perfetto_encoder_init()` +3. `perfetto_initialized` is set to true +4. Thread hooks start firing +5. First emit function calls `perfetto_start()` +6. `perfetto_start()` emits process descriptor with `SEQ_INCREMENTAL_STATE_CLEARED` +7. Subsequent packets use `SEQ_NEEDS_INCREMENTAL_STATE` diff --git a/ports/zephyr-cp/mpconfigport.h b/ports/zephyr-cp/mpconfigport.h index 5b5b077a37152..9e7bc62c0fe40 100644 --- a/ports/zephyr-cp/mpconfigport.h +++ b/ports/zephyr-cp/mpconfigport.h @@ -17,6 +17,14 @@ #define CIRCUITPY_DEBUG_TINYUSB 0 +// NVM size is determined at runtime from the Zephyr partition table. +#define CIRCUITPY_INTERNAL_NVM_SIZE 1 + +// Disable native _Float16 handling for host builds. +#define MICROPY_FLOAT_USE_NATIVE_FLT16 (0) + +#define MICROPY_NLR_THUMB_USE_LONG_JUMP (1) + //////////////////////////////////////////////////////////////////////////////////////////////////// // This also includes mpconfigboard.h. diff --git a/ports/zephyr-cp/mphalport.h b/ports/zephyr-cp/mphalport.h index b3adf5830d059..ee696a4f04370 100644 --- a/ports/zephyr-cp/mphalport.h +++ b/ports/zephyr-cp/mphalport.h @@ -13,7 +13,12 @@ #include "py/mpconfig.h" #include "supervisor/shared/tick.h" +#include + #define mp_hal_ticks_ms() ((mp_uint_t)supervisor_ticks_ms32()) -#define mp_hal_delay_us(us) NRFX_DELAY_US((uint32_t)(us)) + +static inline void mp_hal_delay_us(mp_uint_t us) { + k_busy_wait((uint32_t)us); +} bool mp_hal_stdin_any(void); diff --git a/ports/zephyr-cp/native_sim_build_Containerfile b/ports/zephyr-cp/native_sim_build_Containerfile new file mode 100644 index 0000000000000..3cd1a677da351 --- /dev/null +++ b/ports/zephyr-cp/native_sim_build_Containerfile @@ -0,0 +1,40 @@ +FROM ubuntu:24.04 +ENV DEBIAN_FRONTEND=noninteractive TZ=Etc/UTC +ENV PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig + +RUN dpkg --add-architecture i386 && apt-get update && apt-get install -y --no-install-recommends \ + git cmake ninja-build gperf ccache dfu-util device-tree-compiler \ + wget python3 python3-dev python3-pip python3-venv python3-setuptools \ + python3-tk python3-wheel xz-utils file make gcc \ + gcc-multilib g++-multilib libc6-dev-i386 \ + libsdl2-dev:i386 libsdl2-image-dev:i386 \ + libmagic1 mtools pkg-config ca-certificates unzip \ + protobuf-compiler sudo \ + && rm -rf /var/lib/apt/lists/* + +# Remove the default ubuntu:24.04 'ubuntu' user (UID 1000) so 'dev' can take +# UID 1000 — required for --userns=keep-id to map host UID 1000 to 'dev'. +RUN userdel -r ubuntu 2>/dev/null || true \ + && useradd -m -u 1000 -s /bin/bash dev \ + && echo 'dev ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/dev \ + && chmod 440 /etc/sudoers.d/dev +USER dev +WORKDIR /home/dev + +RUN python3 -m venv /home/dev/.venv +ENV PATH="/home/dev/.venv/bin:${PATH}" +RUN pip install --no-cache-dir west pytest pyelftools pyyaml intelhex protobuf grpcio-tools + +# The repo is expected to be bind-mounted here at runtime: +# podman run -v ~/circuitpython:/home/dev/circuitpython:Z --userns=keep-id ... +# On first run, inside the container do: +# cd ~/circuitpython/ports/zephyr-cp +# west init -l zephyr-config +# west update +# west zephyr-export +# pip install -r zephyr/scripts/requirements.txt +# pip install -r ../../requirements-dev.txt +# west sdk install -t x86_64-zephyr-elf +# python ../../tools/ci_fetch_deps.py zephyr-cp +WORKDIR /home/dev/circuitpython/ports/zephyr-cp +CMD ["/bin/bash"] diff --git a/ports/zephyr-cp/native_sim_build_init_container.sh b/ports/zephyr-cp/native_sim_build_init_container.sh new file mode 100755 index 0000000000000..19831e1fcc28a --- /dev/null +++ b/ports/zephyr-cp/native_sim_build_init_container.sh @@ -0,0 +1,43 @@ +#!/usr/bin/env bash +# One-time setup to run INSIDE the zephyr-cp-dev container on the first +# launch against a fresh bind-mounted circuitpython checkout. +# +# Usage (inside the container): +# cd ~/circuitpython/ports/zephyr-cp +# ./native_sim_build_init_container.sh +# +# Safe to re-run; west/pip/etc. are idempotent. +set -euo pipefail + +git config --global --add safe.directory /home/dev/circuitpython + +cd "$(dirname "${BASH_SOURCE[0]}")" + +echo "==> west init" +if [ ! -d .west ]; then + west init -l zephyr-config +else + echo " (already initialized, skipping)" +fi + +echo "==> west update" +west update + +echo "==> west zephyr-export" +west zephyr-export + +echo "==> pip install Zephyr requirements" +pip install -r zephyr/scripts/requirements.txt + +echo "==> pip install CircuitPython dev requirements" +pip install -r ../../requirements-dev.txt + +echo "==> west sdk install (x86_64-zephyr-elf)" +west sdk install -t x86_64-zephyr-elf + +echo "==> fetch port submodules" +python ../../tools/ci_fetch_deps.py zephyr-cp + +echo +echo "First-run setup complete." +echo "You can now build with: make BOARD=native_native_sim" diff --git a/ports/zephyr-cp/native_sim_build_run_container.sh b/ports/zephyr-cp/native_sim_build_run_container.sh new file mode 100755 index 0000000000000..e30aca22b46a7 --- /dev/null +++ b/ports/zephyr-cp/native_sim_build_run_container.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Launch (or re-attach to) the zephyr-cp-dev container with the enclosing +# circuitpython checkout bind-mounted at /home/dev/circuitpython. Works from +# any CWD — the mount is resolved relative to this script's location. +# +# On first invocation, creates a persistent container named "zcp". On +# subsequent invocations, re-starts the same container so installed state +# (e.g. the Zephyr SDK under /home/dev/zephyr-sdk-*) survives across sessions. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +CONTAINER_NAME="zcp" +IMAGE="zephyr-cp-dev" + +if podman container exists "$CONTAINER_NAME"; then + exec podman start -ai "$CONTAINER_NAME" +else + exec podman run -it --name "$CONTAINER_NAME" \ + -v "$REPO_ROOT:/home/dev/circuitpython:Z" \ + --userns=keep-id \ + "$IMAGE" "$@" +fi diff --git a/ports/zephyr-cp/native_sim_i2c_emul_control.c b/ports/zephyr-cp/native_sim_i2c_emul_control.c new file mode 100644 index 0000000000000..de467122a7bf5 --- /dev/null +++ b/ports/zephyr-cp/native_sim_i2c_emul_control.c @@ -0,0 +1,169 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Command-line control for enabling/disabling emulated I2C devices + * on native_sim. This allows testing device hot-plug and error scenarios. + */ + +#include +#include +#include +#include +#include +#include + +#include "nsi_cmdline.h" +#include "posix_native_task.h" + +LOG_MODULE_REGISTER(i2c_emul_control, LOG_LEVEL_INF); + +#define MAX_DISABLED_DEVICES 16 + +struct disabled_device { + const char *name; + const struct emul *emul; + struct i2c_emul_api mock_api; + bool disabled; +}; + +static struct disabled_device disabled_devices[MAX_DISABLED_DEVICES]; +static int num_disabled_devices = 0; + +static char *disabled_device_args[MAX_DISABLED_DEVICES]; +static int num_disabled_device_args = 0; + +/* + * Mock transfer function that returns -EIO (NACK) when device is disabled, + * or -ENOSYS to fall back to the real emulator. + */ +static int disabled_device_transfer(const struct emul *target, + struct i2c_msg *msgs, + int num_msgs, + int addr) { + ARG_UNUSED(msgs); + ARG_UNUSED(num_msgs); + ARG_UNUSED(addr); + + for (int i = 0; i < num_disabled_devices; i++) { + if (disabled_devices[i].emul == target) { + if (disabled_devices[i].disabled) { + LOG_DBG("Device %s is disabled, returning -EIO", + disabled_devices[i].name); + return -EIO; + } + break; + } + } + /* Fall back to normal emulator behavior */ + return -ENOSYS; +} + +int i2c_emul_control_disable_device(const char *name) { + const struct emul *emul = emul_get_binding(name); + if (!emul) { + LOG_ERR("Emulator '%s' not found", name); + return -ENODEV; + } + + if (emul->bus_type != EMUL_BUS_TYPE_I2C) { + LOG_ERR("Emulator '%s' is not an I2C device", name); + return -EINVAL; + } + + /* Find existing entry or create new one */ + int idx = -1; + for (int i = 0; i < num_disabled_devices; i++) { + if (disabled_devices[i].emul == emul) { + idx = i; + break; + } + } + + if (idx < 0) { + if (num_disabled_devices >= MAX_DISABLED_DEVICES) { + LOG_ERR("Too many disabled devices"); + return -ENOMEM; + } + idx = num_disabled_devices++; + disabled_devices[idx].name = name; + disabled_devices[idx].emul = emul; + disabled_devices[idx].mock_api.transfer = disabled_device_transfer; + + /* Install our mock_api to intercept transfers */ + emul->bus.i2c->mock_api = &disabled_devices[idx].mock_api; + } + + disabled_devices[idx].disabled = true; + LOG_INF("Disabled I2C emulator: %s", name); + return 0; +} + +int i2c_emul_control_enable_device(const char *name) { + for (int i = 0; i < num_disabled_devices; i++) { + if (strcmp(disabled_devices[i].name, name) == 0) { + disabled_devices[i].disabled = false; + LOG_INF("Enabled I2C emulator: %s", name); + return 0; + } + } + LOG_ERR("Device '%s' not in disabled list", name); + return -ENODEV; +} + +bool i2c_emul_control_is_disabled(const char *name) { + for (int i = 0; i < num_disabled_devices; i++) { + if (strcmp(disabled_devices[i].name, name) == 0) { + return disabled_devices[i].disabled; + } + } + return false; +} + +/* Command-line option handler */ +static void cmd_disable_i2c_device(char *argv, int offset) { + /* The value is at argv + offset (after the '=' in --disable-i2c=value) */ + char *value = argv + offset; + if (num_disabled_device_args < MAX_DISABLED_DEVICES) { + disabled_device_args[num_disabled_device_args++] = value; + } else { + printk("i2c_emul_control: Too many --disable-i2c arguments, ignoring: %s\n", value); + } +} + +static struct args_struct_t i2c_emul_args[] = { + { + .option = "disable-i2c", + .name = "device", + .type = 's', + .call_when_found = cmd_disable_i2c_device, + .descript = "Disable an emulated I2C device by name (can be repeated). " + "Example: --disable-i2c=bmi160" + }, + ARG_TABLE_ENDMARKER +}; + +static void register_cmdline_opts(void) { + nsi_add_command_line_opts(i2c_emul_args); +} + +/* Register command-line options early in boot */ +NATIVE_TASK(register_cmdline_opts, PRE_BOOT_1, 0); + +static int apply_disabled_devices(void) { + LOG_DBG("Applying %d disabled device(s)", num_disabled_device_args); + for (int i = 0; i < num_disabled_device_args; i++) { + int rc = i2c_emul_control_disable_device(disabled_device_args[i]); + if (rc != 0) { + LOG_WRN("Failed to disable I2C device '%s': %d", + disabled_device_args[i], rc); + } + } + return 0; +} + +/* + * Apply after emulators are initialized. + * I2C emulators are registered at POST_KERNEL level, so we need to run + * at APPLICATION level to ensure they exist. + */ +SYS_INIT(apply_disabled_devices, APPLICATION, 99); diff --git a/ports/zephyr-cp/prj.conf b/ports/zephyr-cp/prj.conf index f769d7dc6b819..893ff259a97c7 100644 --- a/ports/zephyr-cp/prj.conf +++ b/ports/zephyr-cp/prj.conf @@ -1,28 +1,56 @@ -CONFIG_SYS_HEAP_RUNTIME_STATS=y +CONFIG_SYS_HEAP_RUNTIME_STATS=n CONFIG_FLASH=y CONFIG_FLASH_MAP=y -CONFIG_STD_C23=y CONFIG_DYNAMIC_INTERRUPTS=y CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_FLASH_MAP_LABELS=y CONFIG_MAIN_STACK_SIZE=24288 -CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 -CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 CONFIG_THREAD_STACK_INFO=y -CONFIG_STACK_SENTINEL=y -CONFIG_DEBUG_THREAD_INFO=y -CONFIG_DEBUG_INFO=y +CONFIG_STACK_SENTINEL=n +CONFIG_DEBUG_THREAD_INFO=n +CONFIG_EXCEPTION_STACK_TRACE=n -CONFIG_USB_DEVICE_STACK=n +CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_USBD_CDC_ACM_CLASS=y +CONFIG_USBD_MSC_STACK_SIZE=1536 +CONFIG_CDC_ACM_SERIAL_INITIALIZE_AT_BOOT=n + +CONFIG_USBD_MSC_CLASS=y +CONFIG_USBD_MSC_LUNS_PER_INSTANCE=1 CONFIG_HWINFO=y CONFIG_REBOOT=y CONFIG_ENTROPY_GENERATOR=y -CONFIG_ASSERT=y -CONFIG_LOG_BLOCK_IN_THREAD=y +CONFIG_ASSERT=n +CONFIG_LOG_BLOCK_IN_THREAD=n CONFIG_EVENTS=y + +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y + +CONFIG_I2C=y +CONFIG_SPI=y +CONFIG_SPI_ASYNC=y + +CONFIG_LOG=y +CONFIG_LOG_MAX_LEVEL=2 +CONFIG_FRAME_POINTER=n + +CONFIG_NET_HOSTNAME_ENABLE=y +CONFIG_NET_HOSTNAME_DYNAMIC=y +CONFIG_NET_HOSTNAME="circuitpython" + +CONFIG_I2S=y +CONFIG_DYNAMIC_THREAD=y +CONFIG_DYNAMIC_THREAD_ALLOC=y +CONFIG_DYNAMIC_THREAD_PREFER_ALLOC=y + +CONFIG_MBEDTLS=y +CONFIG_MBEDTLS_BUILTIN=y +CONFIG_MBEDTLS_SHA1=y +CONFIG_MBEDTLS_SHA256=y diff --git a/ports/zephyr-cp/socs/nrf5340_cpuapp.conf b/ports/zephyr-cp/socs/nrf5340_cpuapp.conf index bf70997d83f29..734d26db8e939 100644 --- a/ports/zephyr-cp/socs/nrf5340_cpuapp.conf +++ b/ports/zephyr-cp/socs/nrf5340_cpuapp.conf @@ -1,4 +1,3 @@ -CONFIG_NRFX_UARTE0=y -CONFIG_NRFX_UARTE1=y +CONFIG_NRFX_UARTE=y CONFIG_NRFX_POWER=y diff --git a/ports/zephyr-cp/socs/stm32h7b3xx.conf b/ports/zephyr-cp/socs/stm32h7b3xx.conf index 3c7daeb753de8..14a93b52ce802 100644 --- a/ports/zephyr-cp/socs/stm32h7b3xx.conf +++ b/ports/zephyr-cp/socs/stm32h7b3xx.conf @@ -1,4 +1 @@ -CONFIG_USE_STM32_LL_USB=y -CONFIG_USE_STM32_HAL_PCD=y - CONFIG_MEMC=y diff --git a/ports/zephyr-cp/socs/stm32u575xx.conf b/ports/zephyr-cp/socs/stm32u575xx.conf index 78cefbfef402f..e69de29bb2d1d 100644 --- a/ports/zephyr-cp/socs/stm32u575xx.conf +++ b/ports/zephyr-cp/socs/stm32u575xx.conf @@ -1,2 +0,0 @@ -CONFIG_USE_STM32_LL_USB=y -CONFIG_USE_STM32_HAL_PCD=y diff --git a/ports/zephyr-cp/supervisor/flash.c b/ports/zephyr-cp/supervisor/flash.c index 6b893f89abe55..a13bb3cffcb20 100644 --- a/ports/zephyr-cp/supervisor/flash.c +++ b/ports/zephyr-cp/supervisor/flash.c @@ -21,7 +21,7 @@ #include #define CIRCUITPY_PARTITION circuitpy_partition -static struct flash_area *filesystem_area = NULL; +static const struct flash_area *filesystem_area = NULL; #if !FIXED_PARTITION_EXISTS(CIRCUITPY_PARTITION) static struct flash_area _dynamic_area; @@ -31,15 +31,14 @@ static struct flash_area _dynamic_area; extern const struct device *const flashes[]; extern const int circuitpy_flash_device_count; -// Size of an erase area +// Size of an erase page. static size_t _page_size; -// Size of a write area -static size_t _row_size; +// The value a flash byte has after being erased (usually 0xFF, but can differ). +static uint8_t _erase_value; -// Number of file system blocks in a page. +// Number of FILESYSTEM_BLOCK_SIZE blocks in an erase page. static size_t _blocks_per_page; -static size_t _rows_per_block; static uint32_t _page_mask; #define NO_PAGE_LOADED 0xFFFFFFFF @@ -49,14 +48,41 @@ static uint32_t _current_page_address; static uint32_t _scratch_page_address; -// Track which blocks (up to 32) in the current sector currently live in the -// cache. -static uint32_t _dirty_mask; -static uint32_t _loaded_mask; +// Per-block flags packed into a uint8_t array (one byte per block). +#define ROW_LOADED 0x01 // Block data is present in the cache. +#define ROW_DIRTY 0x02 // Block has been modified since last flush. +#define ROW_ERASED 0x04 // Block is all 0xFF; no need to write after erase. + +static uint8_t *_row_flags = NULL; + +static inline void _clear_row_flags(void) { + if (_row_flags != NULL) { + memset(_row_flags, 0, _blocks_per_page); + } +} + +static inline bool _any_dirty(void) { + for (size_t i = 0; i < _blocks_per_page; i++) { + if (_row_flags[i] & ROW_DIRTY) { + return true; + } + } + return false; +} + +// Check if a buffer is entirely the erase value. +static bool _buffer_is_erased(const uint8_t *buf, size_t len) { + for (size_t i = 0; i < len; i++) { + if (buf[i] != _erase_value) { + return false; + } + } + return true; +} // Table of pointers to each cached block. Should be zero'd after allocation. -#define FLASH_CACHE_TABLE_NUM_ENTRIES (_blocks_per_page * _rows_per_block) -#define FLASH_CACHE_TABLE_SIZE (FLASH_CACHE_TABLE_NUM_ENTRIES * sizeof (uint8_t *)) +#define FLASH_CACHE_TABLE_NUM_ENTRIES (_blocks_per_page) +#define FLASH_CACHE_TABLE_SIZE (FLASH_CACHE_TABLE_NUM_ENTRIES * sizeof(uint8_t *)) static uint8_t **flash_cache_table = NULL; static K_MUTEX_DEFINE(_mutex); @@ -111,6 +137,10 @@ void supervisor_flash_init(void) { const struct device *d = flashes[i]; printk("flash %p %s\n", d, d->name); + if (!device_is_ready(d)) { + printk(" not ready\n"); + continue; + } if (covered_by_areas[i]) { printk(" covered by flash area\n"); continue; @@ -167,52 +197,44 @@ void supervisor_flash_init(void) { return; } - const struct device *d = flash_area_get_device(filesystem_area); - _row_size = flash_get_write_block_size(d); - if (_row_size < 256) { - if (256 % _row_size == 0) { - _row_size = 256; - } else { - size_t new_row_size = _row_size; - while (new_row_size < 256) { - new_row_size += _row_size; - } - _row_size = new_row_size; - } - } struct flash_pages_info first_info; + const struct device *d = flash_area_get_device(filesystem_area); + const struct flash_parameters *fp = flash_get_parameters(d); + _erase_value = fp->erase_value; flash_get_page_info_by_offs(d, filesystem_area->fa_off, &first_info); struct flash_pages_info last_info; - flash_get_page_info_by_offs(d, filesystem_area->fa_off + filesystem_area->fa_size - _row_size, &last_info); + flash_get_page_info_by_offs(d, filesystem_area->fa_off + filesystem_area->fa_size - FILESYSTEM_BLOCK_SIZE, &last_info); _page_size = first_info.size; if (_page_size < FILESYSTEM_BLOCK_SIZE) { _page_size = FILESYSTEM_BLOCK_SIZE; } - printk(" erase page size %d\n", _page_size); - // Makes sure that a cached page has 32 or fewer rows. Our dirty mask is - // only 32 bits. - while (_page_size / _row_size > 32) { - _row_size *= 2; - } - printk(" write row size %d\n", _row_size); _blocks_per_page = _page_size / FILESYSTEM_BLOCK_SIZE; - printk(" blocks per page %d\n", _blocks_per_page); - _rows_per_block = FILESYSTEM_BLOCK_SIZE / _row_size; _page_mask = ~(_page_size - 1); + printk(" erase page size %d\n", _page_size); + printk(" blocks per page %d\n", _blocks_per_page); + + _row_flags = port_malloc(_blocks_per_page, false); + if (_row_flags == NULL) { + printk("Unable to allocate row flags (%d bytes)\n", _blocks_per_page); + filesystem_area = NULL; + return; + } + memset(_row_flags, 0, _blocks_per_page); + // The last page is the scratch sector. - _scratch_page_address = last_info.start_offset; + _scratch_page_address = last_info.start_offset - filesystem_area->fa_off; _current_page_address = NO_PAGE_LOADED; } uint32_t supervisor_flash_get_block_size(void) { - return 512; + return FILESYSTEM_BLOCK_SIZE; } uint32_t supervisor_flash_get_block_count(void) { if (filesystem_area == NULL) { return 0; } - return (_scratch_page_address - filesystem_area->fa_off) / 512; + return _scratch_page_address / FILESYSTEM_BLOCK_SIZE; } @@ -241,10 +263,8 @@ static bool write_flash(uint32_t address, const uint8_t *data, uint32_t data_len static bool block_erased(uint32_t sector_address) { uint8_t short_buffer[4]; if (read_flash(sector_address, short_buffer, 4)) { - for (uint16_t i = 0; i < 4; i++) { - if (short_buffer[i] != 0xff) { - return false; - } + if (!_buffer_is_erased(short_buffer, 4)) { + return false; } } else { return false; @@ -253,10 +273,8 @@ static bool block_erased(uint32_t sector_address) { // Now check the full length. uint8_t full_buffer[FILESYSTEM_BLOCK_SIZE]; if (read_flash(sector_address, full_buffer, FILESYSTEM_BLOCK_SIZE)) { - for (uint16_t i = 0; i < FILESYSTEM_BLOCK_SIZE; i++) { - if (short_buffer[i] != 0xff) { - return false; - } + if (!_buffer_is_erased(full_buffer, FILESYSTEM_BLOCK_SIZE)) { + return false; } } else { return false; @@ -274,17 +292,26 @@ static bool erase_page(uint32_t sector_address) { return res == 0; } -// Sector is really 24 bits. static bool copy_block(uint32_t src_address, uint32_t dest_address) { - // Copy row by row to minimize RAM buffer. - uint8_t buffer[_row_size]; - for (uint32_t i = 0; i < FILESYSTEM_BLOCK_SIZE / _row_size; i++) { - if (!read_flash(src_address + i * _row_size, buffer, _row_size)) { - return false; - } - if (!write_flash(dest_address + i * _row_size, buffer, _row_size)) { - return false; - } + uint8_t buffer[FILESYSTEM_BLOCK_SIZE]; + if (!read_flash(src_address, buffer, FILESYSTEM_BLOCK_SIZE)) { + return false; + } + if (!write_flash(dest_address, buffer, FILESYSTEM_BLOCK_SIZE)) { + return false; + } + return true; +} + +// Load a block into the ram cache and set its flags. Returns false on read error. +static bool _load_block_into_cache(size_t block_index) { + if (!read_flash(_current_page_address + block_index * FILESYSTEM_BLOCK_SIZE, + flash_cache_table[block_index], FILESYSTEM_BLOCK_SIZE)) { + return false; + } + _row_flags[block_index] |= ROW_LOADED; + if (_buffer_is_erased(flash_cache_table[block_index], FILESYSTEM_BLOCK_SIZE)) { + _row_flags[block_index] |= ROW_ERASED; } return true; } @@ -299,12 +326,12 @@ static bool flush_scratch_flash(void) { // cached. bool copy_to_scratch_ok = true; for (size_t i = 0; i < _blocks_per_page; i++) { - if ((_dirty_mask & (1 << i)) == 0) { + if (!(_row_flags[i] & ROW_DIRTY)) { copy_to_scratch_ok = copy_to_scratch_ok && copy_block(_current_page_address + i * FILESYSTEM_BLOCK_SIZE, _scratch_page_address + i * FILESYSTEM_BLOCK_SIZE); } - _loaded_mask |= (1 << i); + _row_flags[i] |= ROW_LOADED; } if (!copy_to_scratch_ok) { // TODO(tannewt): Do more here. We opted to not erase and copy bad data @@ -337,7 +364,7 @@ static void release_ram_cache(void) { port_free(flash_cache_table); flash_cache_table = NULL; _current_page_address = NO_PAGE_LOADED; - _loaded_mask = 0; + _clear_row_flags(); } // Attempts to allocate a new set of page buffers for caching a full sector in @@ -346,7 +373,7 @@ static void release_ram_cache(void) { static bool allocate_ram_cache(void) { flash_cache_table = port_malloc(FLASH_CACHE_TABLE_SIZE, false); if (flash_cache_table == NULL) { - // Not enough space even for the cache table. + printk("Unable to allocate ram cache table\n"); return false; } @@ -355,21 +382,20 @@ static bool allocate_ram_cache(void) { bool success = true; for (size_t i = 0; i < _blocks_per_page && success; i++) { - for (size_t j = 0; j < _rows_per_block && success; j++) { - uint8_t *page_cache = port_malloc(_row_size, false); - if (page_cache == NULL) { - success = false; - break; - } - flash_cache_table[i * _rows_per_block + j] = page_cache; + uint8_t *block_cache = port_malloc(FILESYSTEM_BLOCK_SIZE, false); + if (block_cache == NULL) { + success = false; + break; } + flash_cache_table[i] = block_cache; } // We couldn't allocate enough so give back what we got. if (!success) { + printk("Unable to allocate ram cache pages\n"); release_ram_cache(); } - _loaded_mask = 0; + _clear_row_flags(); _current_page_address = NO_PAGE_LOADED; return success; } @@ -382,7 +408,7 @@ static bool flush_ram_cache(bool keep_cache) { return true; } - if (_current_page_address == NO_PAGE_LOADED || _dirty_mask == 0) { + if (_current_page_address == NO_PAGE_LOADED || !_any_dirty()) { if (!keep_cache) { release_ram_cache(); } @@ -393,21 +419,12 @@ static bool flush_ram_cache(bool keep_cache) { // erase below. bool copy_to_ram_ok = true; for (size_t i = 0; i < _blocks_per_page; i++) { - if ((_loaded_mask & (1 << i)) == 0) { - for (size_t j = 0; j < _rows_per_block; j++) { - copy_to_ram_ok = read_flash( - _current_page_address + (i * _rows_per_block + j) * _row_size, - flash_cache_table[i * _rows_per_block + j], - _row_size); - if (!copy_to_ram_ok) { - break; - } + if (!(_row_flags[i] & ROW_LOADED)) { + if (!_load_block_into_cache(i)) { + copy_to_ram_ok = false; + break; } } - if (!copy_to_ram_ok) { - break; - } - _loaded_mask |= (1 << i); } if (!copy_to_ram_ok) { @@ -417,14 +434,19 @@ static bool flush_ram_cache(bool keep_cache) { erase_page(_current_page_address); // Lastly, write all the data in ram that we've cached. for (size_t i = 0; i < _blocks_per_page; i++) { - for (size_t j = 0; j < _rows_per_block; j++) { - write_flash(_current_page_address + (i * _rows_per_block + j) * _row_size, - flash_cache_table[i * _rows_per_block + j], - _row_size); + // Skip blocks that are entirely erased — the page erase already + // set them to 0xFF so writing would be redundant. + if (_row_flags[i] & ROW_ERASED) { + continue; } + write_flash(_current_page_address + i * FILESYSTEM_BLOCK_SIZE, + flash_cache_table[i], + FILESYSTEM_BLOCK_SIZE); + } + // Nothing is dirty anymore. Clear dirty but keep loaded and erased. + for (size_t i = 0; i < _blocks_per_page; i++) { + _row_flags[i] &= ~ROW_DIRTY; } - // Nothing is dirty anymore. Some may already be in the cache cleanly. - _dirty_mask = 0; // We're done with the cache for now so give it back. if (!keep_cache) { @@ -487,15 +509,10 @@ static bool _flash_read_block(uint8_t *dest, uint32_t block) { // Mask out the lower bits that designate the address within the sector. uint32_t page_address = address & _page_mask; size_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % _blocks_per_page; - uint32_t mask = 1 << (block_index); // We're reading from the currently cached sector. - if (_current_page_address == page_address && (mask & _loaded_mask) > 0) { + if (_current_page_address == page_address && (_row_flags[block_index] & ROW_LOADED)) { if (flash_cache_table != NULL) { - for (int i = 0; i < _rows_per_block; i++) { - memcpy(dest + i * _row_size, - flash_cache_table[block_index * _rows_per_block + i], - _row_size); - } + memcpy(dest, flash_cache_table[block_index], FILESYSTEM_BLOCK_SIZE); return true; } uint32_t scratch_block_address = _scratch_page_address + block_index * FILESYSTEM_BLOCK_SIZE; @@ -514,7 +531,6 @@ static bool _flash_write_block(const uint8_t *data, uint32_t block) { // Mask out the lower bits that designate the address within the sector. uint32_t page_address = address & _page_mask; size_t block_index = (address / FILESYSTEM_BLOCK_SIZE) % _blocks_per_page; - uint32_t mask = 1 << (block_index); // Flush the cache if we're moving onto a different page. if (_current_page_address != page_address) { // Check to see if we'd write to an erased block and aren't writing to @@ -529,19 +545,12 @@ static bool _flash_write_block(const uint8_t *data, uint32_t block) { erase_page(_scratch_page_address); } _current_page_address = page_address; - _dirty_mask = 0; - _loaded_mask = 0; + _clear_row_flags(); } - _dirty_mask |= mask; - _loaded_mask |= mask; - + _row_flags[block_index] = ROW_DIRTY | ROW_LOADED; // Copy the block to the appropriate cache. if (flash_cache_table != NULL) { - for (int i = 0; i < _rows_per_block; i++) { - memcpy(flash_cache_table[block_index * _rows_per_block + i], - data + i * _row_size, - _row_size); - } + memcpy(flash_cache_table[block_index], data, FILESYSTEM_BLOCK_SIZE); return true; } else { uint32_t scratch_block_address = _scratch_page_address + block_index * FILESYSTEM_BLOCK_SIZE; diff --git a/ports/zephyr-cp/supervisor/port.c b/ports/zephyr-cp/supervisor/port.c index 860584c2b19d1..047a4efe8bf01 100644 --- a/ports/zephyr-cp/supervisor/port.c +++ b/ports/zephyr-cp/supervisor/port.c @@ -7,15 +7,40 @@ #include "supervisor/port.h" #include "mpconfigboard.h" +#include "supervisor/shared/tick.h" + +#if CIRCUITPY_AUDIOBUSIO_I2SOUT +#include "common-hal/audiobusio/I2SOut.h" +#endif #include #include #include +#include + +#if defined(CONFIG_ARCH_POSIX) +#include +#include + +#include "cmdline.h" +#include "posix_board_if.h" +#include "posix_native_task.h" +#endif #include "lib/tlsf/tlsf.h" #include +#include + +#if defined(CONFIG_TRACING_PERFETTO) && defined(CONFIG_BOARD_NATIVE_SIM) +#include "perfetto_encoder.h" +#include +#define CIRCUITPY_PERFETTO_TRACK_GROUP_UUID 0x3000ULL +#define CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID 0x3001ULL +#define CIRCUITPY_PERFETTO_OUTER_HEAP_USED_UUID 0x3002ULL +#endif static tlsf_t heap; +static size_t tlsf_heap_used = 0; // Auto generated in pins.c extern const struct device *const rams[]; @@ -23,10 +48,98 @@ extern const uint32_t *const ram_bounds[]; extern const size_t circuitpy_max_ram_size; static pool_t pools[CIRCUITPY_RAM_DEVICE_COUNT]; +static uint8_t valid_pool_count = 0; +static bool zephyr_malloc_active = false; +static void *zephyr_malloc_top = NULL; +static void *zephyr_malloc_bottom = NULL; static K_EVENT_DEFINE(main_needed); +static struct k_timer tick_timer; + +#if defined(CONFIG_ARCH_POSIX) +// Number of VM runs before exiting. +// <= 0 means run forever. +// INT32_MAX means option was not provided. +static int32_t native_sim_vm_runs = INT32_MAX; +static uint32_t native_sim_reset_port_count = 0; + +static struct args_struct_t native_sim_reset_port_args[] = { + { + .option = "vm-runs", + .name = "count", + .type = 'i', + .dest = &native_sim_vm_runs, + .descript = "Exit native_sim after this many VM runs. " + "Example: --vm-runs=2" + }, + ARG_TABLE_ENDMARKER +}; + +static void native_sim_register_cmdline_opts(void) { + native_add_command_line_opts(native_sim_reset_port_args); +} + +NATIVE_TASK(native_sim_register_cmdline_opts, PRE_BOOT_1, 0); +#endif + +#if defined(CONFIG_TRACING_PERFETTO) && defined(CONFIG_BOARD_NATIVE_SIM) +static bool perfetto_circuitpython_tracks_emitted; + +static void perfetto_emit_outer_heap_stats(void) { + if (!perfetto_start()) { + return; + } + size_t total = tlsf_heap_used; + #if defined(CONFIG_COMMON_LIBC_MALLOC) && defined(CONFIG_SYS_HEAP_RUNTIME_STATS) + extern int malloc_runtime_stats_get(struct sys_memory_stats *stats); + struct sys_memory_stats stats; + if (malloc_runtime_stats_get(&stats) == 0) { + total += stats.allocated_bytes; + } + #endif + perfetto_emit_counter(CIRCUITPY_PERFETTO_OUTER_HEAP_USED_UUID, (int64_t)total); + Z_SPIN_DELAY(1); +} + +static void perfetto_emit_circuitpython_tracks(void) { + if (perfetto_circuitpython_tracks_emitted) { + return; + } + if (!perfetto_start()) { + return; + } + perfetto_emit_track_descriptor(CIRCUITPY_PERFETTO_TRACK_GROUP_UUID, + perfetto_get_process_uuid(), + "CircuitPython"); + perfetto_emit_counter_track_descriptor(CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID, + CIRCUITPY_PERFETTO_TRACK_GROUP_UUID, + "VM Heap Used", + PERFETTO_COUNTER_UNIT_BYTES); + perfetto_emit_counter_track_descriptor(CIRCUITPY_PERFETTO_OUTER_HEAP_USED_UUID, + CIRCUITPY_PERFETTO_TRACK_GROUP_UUID, + "Outer Heap Used", + PERFETTO_COUNTER_UNIT_BYTES); + perfetto_circuitpython_tracks_emitted = true; +} +#else +static inline void perfetto_emit_outer_heap_stats(void) { +} + +static inline void perfetto_emit_circuitpython_tracks(void) { +} +#endif + +static void _tick_function(struct k_timer *timer_id) { + supervisor_tick(); +} + safe_mode_t port_init(void) { + // We run CircuitPython at the lowest priority (just higher than idle.) + // This allows networking and USB to preempt us. + k_thread_priority_set(k_current_get(), CONFIG_NUM_PREEMPT_PRIORITIES - 1); + k_timer_init(&tick_timer, _tick_function, NULL); + perfetto_emit_circuitpython_tracks(); return SAFE_MODE_NONE; } @@ -42,7 +155,19 @@ void reset_cpu(void) { } void reset_port(void) { + #if CIRCUITPY_AUDIOBUSIO_I2SOUT + i2sout_reset(); + #endif + #if defined(CONFIG_ARCH_POSIX) + native_sim_reset_port_count++; + if (native_sim_vm_runs != INT32_MAX && + native_sim_vm_runs > 0 && + native_sim_reset_port_count >= (uint32_t)(native_sim_vm_runs + 1)) { + printk("posix: exiting after %d VM runs\n", native_sim_vm_runs); + posix_exit(0); + } + #endif } void reset_to_bootloader(void) { @@ -57,23 +182,27 @@ void port_wake_main_task_from_isr(void) { k_event_set(&main_needed, 1); } -void port_yield(void) { +void port_task_yield(void) { k_yield(); } +void port_task_sleep_ms(uint32_t msecs) { + k_msleep(msecs); +} + void port_boot_info(void) { } // Get stack limit address uint32_t *port_stack_get_limit(void) { - return k_current_get()->stack_info.start; + return (uint32_t *)k_current_get()->stack_info.start; } // Get stack top address uint32_t *port_stack_get_top(void) { _thread_stack_info_t stack_info = k_current_get()->stack_info; - return stack_info.start + stack_info.size - stack_info.delta; + return (uint32_t *)(stack_info.start + stack_info.size - stack_info.delta); } // Save and retrieve a word from memory that is preserved over reset. Used for safe mode. @@ -85,6 +214,10 @@ uint32_t port_get_saved_word(void) { } uint64_t port_get_raw_ticks(uint8_t *subticks) { + // Make sure time advances in the simulator. + #if defined(CONFIG_ARCH_POSIX) + k_busy_wait(100); + #endif int64_t uptime = k_uptime_ticks() * 32768 / CONFIG_SYS_CLOCK_TICKS_PER_SEC; if (subticks != NULL) { *subticks = uptime % 32; @@ -94,12 +227,12 @@ uint64_t port_get_raw_ticks(uint8_t *subticks) { // Enable 1/1024 second tick. void port_enable_tick(void) { - + k_timer_start(&tick_timer, K_USEC(1000000 / 1024), K_USEC(1000000 / 1024)); } // Disable 1/1024 second tick. void port_disable_tick(void) { - + k_timer_stop(&tick_timer); } static k_timeout_t next_timeout; @@ -123,36 +256,110 @@ void port_idle_until_interrupt(void) { // Zephyr doesn't maintain one multi-heap. So, make our own using TLSF. void port_heap_init(void) { + // Do a test malloc to determine if Zephyr has an outer heap that may + // overlap with a memory region we've identified in ram_bounds. We'll + // corrupt each other if we both use it. + #ifdef CONFIG_COMMON_LIBC_MALLOC + uint32_t *test_malloc = malloc(32); + free(test_malloc); // Free right away so we don't forget. We don't actually write it anyway. + zephyr_malloc_active = test_malloc != NULL; + #endif + for (size_t i = 0; i < CIRCUITPY_RAM_DEVICE_COUNT; i++) { uint32_t *heap_bottom = ram_bounds[2 * i]; uint32_t *heap_top = ram_bounds[2 * i + 1]; size_t size = (heap_top - heap_bottom) * sizeof(uint32_t); + // The linker script may fill up a region we thought we could use at + // build time. (The ram_bounds values are sometimes determined by the + // linker.) So, we need to guard against regions that aren't actually + // free. + if (size < 1024) { + printk("Skipping region because the linker filled it up.\n"); + continue; + } + #ifdef CONFIG_COMMON_LIBC_MALLOC + // Skip a ram region if our test malloc is within it. We'll use Zephyr's + // malloc to share that space with Zephyr. + if (heap_bottom <= test_malloc && test_malloc < heap_top) { + zephyr_malloc_top = heap_top; + zephyr_malloc_bottom = heap_bottom; + printk("Skipping region because Zephyr malloc is within bounds\n"); + pools[i] = NULL; + continue; + } + #endif printk("Init heap at %p - %p with size %d\n", heap_bottom, heap_top, size); // If this crashes, then make sure you've enabled all of the Kconfig needed for the drivers. - if (i == 0) { + if (valid_pool_count == 0) { heap = tlsf_create_with_pool(heap_bottom, size, circuitpy_max_ram_size); pools[i] = tlsf_get_pool(heap); } else { pools[i] = tlsf_add_pool(heap, heap_bottom + 1, size - sizeof(uint32_t)); } + valid_pool_count++; } + perfetto_emit_outer_heap_stats(); #if !DT_HAS_CHOSEN(zephyr_sram) #error "No SRAM!" #endif } void *port_malloc(size_t size, bool dma_capable) { - void *block = tlsf_malloc(heap, size); + void *block = NULL; + if (valid_pool_count > 0) { + block = tlsf_malloc(heap, size); + } + if (block != NULL) { + tlsf_heap_used += tlsf_block_size(block); + } + #ifdef CONFIG_COMMON_LIBC_MALLOC + if (block == NULL) { + block = malloc(size); + } + #endif + if (block != NULL) { + perfetto_emit_outer_heap_stats(); + } return block; } void port_free(void *ptr) { - tlsf_free(heap, ptr); + if (ptr == NULL) { + return; + } + if (valid_pool_count > 0 && !(ptr >= zephyr_malloc_bottom && ptr < zephyr_malloc_top)) { + tlsf_heap_used -= tlsf_block_size(ptr); + tlsf_free(heap, ptr); + } else { + #ifdef CONFIG_COMMON_LIBC_MALLOC + free(ptr); + #endif + } + perfetto_emit_outer_heap_stats(); } void *port_realloc(void *ptr, size_t size, bool dma_capable) { - return tlsf_realloc(heap, ptr, size); + if (ptr == NULL) { + return port_malloc(size, dma_capable); + } + if (valid_pool_count > 0 && !(ptr >= zephyr_malloc_bottom && ptr < zephyr_malloc_top)) { + size_t old_size = tlsf_block_size(ptr); + void *new_block = tlsf_realloc(heap, ptr, size); + if (new_block != NULL) { + tlsf_heap_used = tlsf_heap_used - old_size + tlsf_block_size(new_block); + perfetto_emit_outer_heap_stats(); + } + return new_block; + } + #ifdef CONFIG_COMMON_LIBC_MALLOC + void *new_block = realloc(ptr, size); + if (new_block != NULL) { + perfetto_emit_outer_heap_stats(); + } + return new_block; + #endif + return NULL; } static bool max_size_walker(void *ptr, size_t size, int used, void *user) { @@ -165,9 +372,26 @@ static bool max_size_walker(void *ptr, size_t size, int used, void *user) { size_t port_heap_get_largest_free_size(void) { size_t max_size = 0; - for (size_t i = 0; i < CIRCUITPY_RAM_DEVICE_COUNT; i++) { - tlsf_walk_pool(pools[i], max_size_walker, &max_size); + if (valid_pool_count > 0) { + for (size_t i = 0; i < CIRCUITPY_RAM_DEVICE_COUNT; i++) { + if (pools[i] == NULL) { + continue; + } + tlsf_walk_pool(pools[i], max_size_walker, &max_size); + } + // IDF does this. Not sure why. + return tlsf_fit_size(heap, max_size); + } + return 64 * 1024; +} + +void assert_post_action(const char *file, unsigned int line) { + // printk("Assertion failed at %s:%u\n", file, line); + // Check that this is arm + #if defined(__arm__) + __asm__ ("bkpt"); + #endif + while (1) { + ; } - // IDF does this. Not sure why. - return tlsf_fit_size(heap, max_size); } diff --git a/ports/zephyr-cp/supervisor/serial.c b/ports/zephyr-cp/supervisor/serial.c index 97e2bee9ce576..3ae3e73fa10c1 100644 --- a/ports/zephyr-cp/supervisor/serial.c +++ b/ports/zephyr-cp/supervisor/serial.c @@ -6,52 +6,80 @@ #include "supervisor/shared/serial.h" -#include "bindings/zephyr_serial/UART.h" +#include "supervisor/zephyr-cp.h" -static zephyr_serial_uart_obj_t zephyr_console; +#if CIRCUITPY_USB_DEVICE == 1 +#include "shared-bindings/usb_cdc/Serial.h" +usb_cdc_serial_obj_t *usb_console; +#else +#include "shared-bindings/busio/UART.h" +static busio_uart_obj_t uart_console; static uint8_t buffer[64]; +#endif void port_serial_early_init(void) { #if CIRCUITPY_USB_DEVICE == 0 - zephyr_console.base.type = &zephyr_serial_uart_type; - zephyr_serial_uart_construct(&zephyr_console, DEVICE_DT_GET(DT_CHOSEN(zephyr_console)), sizeof(buffer), buffer); + uart_console.base.type = &busio_uart_type; + common_hal_busio_uart_construct_from_device(&uart_console, DEVICE_DT_GET(DT_CHOSEN(zephyr_console)), sizeof(buffer), buffer); #endif } void port_serial_init(void) { + #if CIRCUITPY_USB_DEVICE == 1 + usb_console = usb_cdc_serial_get_console(); + #endif } bool port_serial_connected(void) { #if CIRCUITPY_USB_DEVICE == 1 - return false; + if (usb_console == NULL) { + return false; + } + return common_hal_usb_cdc_serial_get_connected(usb_console); #else return true; #endif } char port_serial_read(void) { - #if CIRCUITPY_USB_DEVICE == 0 + #if CIRCUITPY_USB_DEVICE == 1 + if (usb_console == NULL) { + return -1; + } char buf[1]; - size_t count = zephyr_serial_uart_read(&zephyr_console, buf, 1, NULL); + size_t count = common_hal_usb_cdc_serial_read(usb_console, buf, 1, NULL); if (count == 0) { return -1; } return buf[0]; #else - return -1; + char buf[1]; + size_t count = common_hal_busio_uart_read(&uart_console, buf, 1, NULL); + if (count == 0) { + return -1; + } + return buf[0]; #endif } uint32_t port_serial_bytes_available(void) { - #if CIRCUITPY_USB_DEVICE == 0 - return zephyr_serial_uart_rx_characters_available(&zephyr_console); + #if CIRCUITPY_USB_DEVICE == 1 + if (usb_console == NULL) { + return 0; + } + return common_hal_usb_cdc_serial_get_in_waiting(usb_console); #else - return 0; + return common_hal_busio_uart_rx_characters_available(&uart_console); #endif } void port_serial_write_substring(const char *text, uint32_t length) { - #if CIRCUITPY_USB_DEVICE == 0 - zephyr_serial_uart_write(&zephyr_console, text, length, NULL); + #if CIRCUITPY_USB_DEVICE == 1 + if (usb_console == NULL) { + return; + } + common_hal_usb_cdc_serial_write(usb_console, text, length, NULL); + #else + common_hal_busio_uart_write(&uart_console, text, length, NULL); #endif } diff --git a/ports/zephyr-cp/supervisor/usb.c b/ports/zephyr-cp/supervisor/usb.c index 18eb2847ad981..a42a5192f4f30 100644 --- a/ports/zephyr-cp/supervisor/usb.c +++ b/ports/zephyr-cp/supervisor/usb.c @@ -1,202 +1,380 @@ #include "supervisor/usb.h" -#include "tusb_option.h" +#include "shared-bindings/usb_cdc/__init__.h" +#include "shared-bindings/usb_cdc/Serial.h" -#if CFG_TUSB_MCU == OPT_MCU_STM32U5 -#include -#endif +#include "supervisor/zephyr-cp.h" -#if CFG_TUSB_MCU == OPT_MCU_NRF5X -#include -#include -#endif +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" +#include "lib/oofatfs/diskio.h" +#include "lib/oofatfs/ff.h" #include +#include #include -#include #include +#include +#include -#if DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otghs) -#define UDC_IRQ_NAME otghs -#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_otgfs) -#define UDC_IRQ_NAME otgfs -#elif DT_HAS_COMPAT_STATUS_OKAY(st_stm32_usb) -#define UDC_IRQ_NAME usb -#elif DT_HAS_COMPAT_STATUS_OKAY(renesas_ra_usb) -#define UDC_IRQ_NAME usbhs_ir -#endif +#include "shared-module/storage/__init__.h" +#include "supervisor/filesystem.h" +#include "supervisor/shared/reload.h" -#if DT_HAS_COMPAT_STATUS_OKAY(renesas_ra_usb) -#define USB_NAME usbhs -#else -#define USB_NAME zephyr_udc0 -#endif +#include +LOG_MODULE_REGISTER(cpusb, CONFIG_LOG_DEFAULT_LEVEL); + +#define USB_DEVICE DT_NODELABEL(zephyr_udc0) + +USBD_DEVICE_DEFINE(main_usbd, + DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), + USB_VID, USB_PID); + +USBD_DESC_LANG_DEFINE(main_lang); +USBD_DESC_MANUFACTURER_DEFINE(main_mfr, USB_MANUFACTURER); +USBD_DESC_PRODUCT_DEFINE(main_product, USB_PRODUCT); + +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); -#define USB_DEVICE DT_NODELABEL(USB_NAME) +/* doc configuration instantiation start */ +static const uint8_t attributes = 0; -#ifdef UDC_IRQ_NAME -#define UDC_IRQ DT_IRQ_BY_NAME(USB_DEVICE, UDC_IRQ_NAME, irq) -#define UDC_IRQ_PRI DT_IRQ_BY_NAME(USB_DEVICE, UDC_IRQ_NAME, priority) -#else -#define UDC_IRQ DT_IRQ(USB_DEVICE, irq) -#define UDC_IRQ_PRI DT_IRQ(USB_DEVICE, priority) +USBD_CONFIGURATION_DEFINE(main_fs_config, + attributes, + 100, &fs_cfg_desc); + +USBD_CONFIGURATION_DEFINE(main_hs_config, + attributes, + 100, &hs_cfg_desc); + +static usb_cdc_serial_obj_t usb_cdc_console_obj; +static usb_cdc_serial_obj_t usb_cdc_data_obj; + +#ifndef USBD_DEFINE_MSC_LUN +#error "MSC not enabled" #endif -PINCTRL_DT_DEFINE(USB_DEVICE); -static const struct pinctrl_dev_config *usb_pcfg = - PINCTRL_DT_DEV_CONFIG_GET(USB_DEVICE); +#define LUN_COUNT 1 +#define MSC_FLASH_BLOCK_SIZE 512 + +// The ellipsis range in the designated initializer of `ejected` is not standard C, +// but it works in both gcc and clang. +static bool locked[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; + +// Set to true if a write was in a file data or metadata area, +// as opposed to in the filesystem metadata area (e.g., dirty bit). +// Used to determine if an auto-reload is warranted. +static bool content_write[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; + +int _zephyr_disk_init(struct disk_info *disk); +int _zephyr_disk_status(struct disk_info *disk); +int _zephyr_disk_read(struct disk_info *disk, uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector); +int _zephyr_disk_write(struct disk_info *disk, const uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector); +int _zephyr_disk_ioctl(struct disk_info *disk, uint8_t cmd, void *buff); + +static const struct disk_operations disk_ops = { + .init = _zephyr_disk_init, + .status = _zephyr_disk_status, + .read = _zephyr_disk_read, + .write = _zephyr_disk_write, + .ioctl = _zephyr_disk_ioctl, +}; -#if CFG_TUSB_MCU == OPT_MCU_NRF5X -// Value is chosen to be as same as NRFX_POWER_USB_EVT_* in nrfx_power.h -enum { - USB_EVT_DETECTED = 0, - USB_EVT_REMOVED = 1, - USB_EVT_READY = 2 +static struct disk_info circuitpy_disk = { + .name = "CIRCUITPY", + .ops = &disk_ops, + .dev = NULL }; -#ifdef NRF5340_XXAA - #define LFCLK_SRC_RC CLOCK_LFCLKSRC_SRC_LFRC - #define VBUSDETECT_Msk USBREG_USBREGSTATUS_VBUSDETECT_Msk - #define OUTPUTRDY_Msk USBREG_USBREGSTATUS_OUTPUTRDY_Msk - #define GPIOTE_IRQn GPIOTE1_IRQn -#else - #define LFCLK_SRC_RC CLOCK_LFCLKSRC_SRC_RC - #define VBUSDETECT_Msk POWER_USBREGSTATUS_VBUSDETECT_Msk - #define OUTPUTRDY_Msk POWER_USBREGSTATUS_OUTPUTRDY_Msk -#endif +USBD_DEFINE_MSC_LUN(circuitpy_lun, "CIRCUITPY", "Zephyr", "FlashDisk", "0.00"); -// tinyusb function that handles power event (detected, ready, removed) -// We must call it within SD's SOC event handler, or set it as power event handler if SD is not enabled. -extern void tusb_hal_nrf_power_event(uint32_t event); -// nrf power callback, could be unused if SD is enabled or usb is disabled (board_test example) -TU_ATTR_UNUSED static void power_event_handler(nrfx_power_usb_evt_t event) { - tusb_hal_nrf_power_event((uint32_t)event); +int _zephyr_disk_init(struct disk_info *disk) { + printk("Initializing disk\n"); + return 0; } -#endif -void init_usb_hardware(void) { - #if CFG_TUSB_MCU == OPT_MCU_RAXXX - #if !USBHS_PHY_CLOCK_SOURCE_IS_XTAL - if (data->udc_cfg.usb_speed == USBD_SPEED_HS) { - LOG_ERR("High-speed operation is not supported in case PHY clock source is not " - "XTAL"); - return; +int _zephyr_disk_status(struct disk_info *disk) { + fs_user_mount_t *root = filesystem_circuitpy(); + int lun = 0; + if (root == NULL) { + printk("Status: No media\n"); + return DISK_STATUS_NOMEDIA; } - #endif + if (!filesystem_is_writable_by_usb(root)) { + printk("Status: Read-only\n"); + return DISK_STATUS_WR_PROTECT; + } + // Lock the blockdev once we say we're writable. + if (!locked[lun] && !blockdev_lock(root)) { + printk("Status: Locked\n"); + return DISK_STATUS_WR_PROTECT; + } + locked[lun] = true; + return DISK_STATUS_OK; +} + +int _zephyr_disk_read(struct disk_info *disk, uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector) { + fs_user_mount_t *root = filesystem_circuitpy(); + + uint32_t disk_block_count; + disk_ioctl(root, GET_SECTOR_COUNT, &disk_block_count); + + if (start_sector + num_sector > disk_block_count) { + return -EIO; + } + disk_read(root, data_buf, start_sector, num_sector); + return 0; +} - R_ICU->IELSR[UDC_IRQ] = ELC_EVENT_USBHS_USB_INT_RESUME; +int _zephyr_disk_write(struct disk_info *disk, const uint8_t *data_buf, uint32_t start_sector, uint32_t num_sector) { + fs_user_mount_t *root = filesystem_circuitpy(); + int lun = 0; + autoreload_suspend(AUTORELOAD_SUSPEND_USB); + disk_write(root, data_buf, start_sector, num_sector); + // Since by getting here we assume the mount is read-only to + // CircuitPython let's update the cached FatFs sector if it's the one + // we just wrote. + if + #if FF_MAX_SS != FF_MIN_SS + (root->fatfs.ssize == MSC_FLASH_BLOCK_SIZE) + #else + // The compiler can optimize this away. + (FF_MAX_SS == FILESYSTEM_BLOCK_SIZE) #endif + { + if (start_sector == root->fatfs.winsect && start_sector > 0) { + memcpy(root->fatfs.win, + data_buf + MSC_FLASH_BLOCK_SIZE * (root->fatfs.winsect - start_sector), + MSC_FLASH_BLOCK_SIZE); + } + } + + // A write to an lba below fatbase is in the filesystem metadata (BPB) area or the "Reserved Region", + // and is probably setting or clearing the dirty bit. This should not trigger auto-reload. + // All other writes will trigger auto-reload. + if (start_sector >= root->fatfs.fatbase) { + content_write[lun] = true; + } + return 0; +} + +int _zephyr_disk_ioctl(struct disk_info *disk, uint8_t cmd, void *buff) { + + fs_user_mount_t *root = filesystem_circuitpy(); + int lun = 0; + switch (cmd) { + case DISK_IOCTL_GET_SECTOR_COUNT: + disk_ioctl(root, GET_SECTOR_COUNT, buff); + return 0; + case DISK_IOCTL_GET_SECTOR_SIZE: + disk_ioctl(root, GET_SECTOR_SIZE, buff); + return 0; + case DISK_IOCTL_CTRL_SYNC: + disk_ioctl(root, CTRL_SYNC, buff); + autoreload_resume(AUTORELOAD_SUSPEND_USB); + + // This write is complete; initiate an autoreload if this was a file data or metadata write, + // not just a dirty-bit write. + if (content_write[lun] && lun == 0) { + autoreload_trigger(); + content_write[lun] = false; + } + return 0; + default: + printk("Unsupported disk ioctl %02x\n", cmd); + return -ENOTSUP; + } + return 0; +} +static void _msg_cb(struct usbd_context *const ctx, const struct usbd_msg *msg) { + LOG_INF("USBD message: %s", usbd_msg_type_string(msg->type)); + + if (usbd_can_detect_vbus(ctx)) { + if (msg->type == USBD_MSG_VBUS_READY) { + if (usbd_enable(ctx)) { + LOG_ERR("Failed to enable device support"); + } + } + + if (msg->type == USBD_MSG_VBUS_REMOVED) { + if (usbd_disable(ctx)) { + LOG_ERR("Failed to disable device support"); + } + } + } +} - IRQ_CONNECT(UDC_IRQ, UDC_IRQ_PRI, usb_irq_handler, 0, 0); +void usb_init(void) { + printk("Initializing USB\n"); + int err; + + printk("Adding language descriptor\n"); + err = usbd_add_descriptor(&main_usbd, &main_lang); + if (err) { + LOG_ERR("Failed to initialize language descriptor (%d)", err); + return; + } - /* Configure USB GPIOs */ - int err = pinctrl_apply_state(usb_pcfg, PINCTRL_STATE_DEFAULT); - if (err < 0) { - printk("USB pinctrl setup failed (%d)\n", err); - } else { - printk("USB pins setup\n"); + err = usbd_add_descriptor(&main_usbd, &main_mfr); + if (err) { + LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err); + return; } -// #ifdef USB_DRD_FS -// // STM32U535/STM32U545 + err = usbd_add_descriptor(&main_usbd, &main_product); + if (err) { + LOG_ERR("Failed to initialize product descriptor (%d)", err); + return; + } -// /* Enable USB power on Pwrctrl CR2 register */ -// HAL_PWREx_EnableVddUSB(); + bool console = usb_cdc_console_enabled(); + if (console) { + uint8_t *receiver_buffer = port_malloc(128, true); + if (receiver_buffer != NULL) { + common_hal_usb_cdc_serial_construct_from_device(&usb_cdc_console_obj, DEVICE_DT_GET(DT_NODELABEL(cdc_acm_console)), 128, receiver_buffer); + } else { + console = false; + } + } + usb_cdc_set_console(console ? MP_OBJ_FROM_PTR(&usb_cdc_console_obj) : mp_const_none); + + bool data = usb_cdc_data_enabled(); + if (data) { + uint8_t *receiver_buffer = port_malloc(128, true); + if (receiver_buffer != NULL) { + common_hal_usb_cdc_serial_construct_from_device(&usb_cdc_data_obj, DEVICE_DT_GET(DT_NODELABEL(cdc_acm_data)), 128, receiver_buffer); + } else { + data = false; + } + } + usb_cdc_set_data(data ? MP_OBJ_FROM_PTR(&usb_cdc_data_obj) : mp_const_none); -// /* USB clock enable */ -// __HAL_RCC_USB_FS_CLK_ENABLE(); + err = disk_access_register(&circuitpy_disk); + if (err) { + printk("Failed to register disk access %d\n", err); + return; + } -// #endif + if (USBD_SUPPORTS_HIGH_SPEED && + usbd_caps_speed(&main_usbd) == USBD_SPEED_HS) { + printk("Adding High-Speed configuration\n"); + err = usbd_add_configuration(&main_usbd, USBD_SPEED_HS, + &main_hs_config); + if (err) { + LOG_ERR("Failed to add High-Speed configuration"); + return; + } + + printk("Registering High-Speed cdc_acm class\n"); + if (usb_cdc_console_enabled()) { + err = usbd_register_class(&main_usbd, "cdc_acm_0", USBD_SPEED_HS, 1); + if (err) { + printk("Failed to add register classes %d\n", err); + return; + } + } + + if (usb_cdc_data_enabled()) { + err = usbd_register_class(&main_usbd, "cdc_acm_1", USBD_SPEED_HS, 1); + if (err) { + printk("Failed to add register classes %d\n", err); + return; + } + } + + err = usbd_register_class(&main_usbd, "msc_0", USBD_SPEED_HS, 1); + if (err) { + printk("Failed to add register MSC class %d\n", err); + return; + } else { + printk("Registered MSC class for high speed\n"); + } + + usbd_device_set_code_triple(&main_usbd, USBD_SPEED_HS, + USB_BCC_MISCELLANEOUS, 0x02, 0x01); + } - #if CFG_TUSB_MCU == OPT_MCU_STM32U5 && defined(USB_OTG_FS) - /* Enable USB power on Pwrctrl CR2 register */ - // HAL_PWREx_EnableVddUSB(); - LL_PWR_EnableVddUSB(); + /* doc configuration register start */ + printk("Adding Full-Speed configuration\n"); + err = usbd_add_configuration(&main_usbd, USBD_SPEED_FS, + &main_fs_config); + if (err) { + LOG_ERR("Failed to add Full-Speed configuration"); + return; + } + /* doc configuration register end */ - /* USB clock enable */ - __HAL_RCC_USB_OTG_FS_CLK_ENABLE(); + /* doc functions register start */ - #endif + if (usb_cdc_console_enabled()) { + printk("Registering Full-Speed cdc_acm class\n"); + err = usbd_register_class(&main_usbd, "cdc_acm_0", USBD_SPEED_FS, 1); + if (err) { + printk("Failed to add register classes\n"); + return; + } + } -// #ifdef USB_OTG_HS -// // STM59x/Ax/Fx/Gx only have 1 USB HS port + if (usb_cdc_data_enabled()) { + printk("Registering Full-Speed cdc_acm class\n"); + err = usbd_register_class(&main_usbd, "cdc_acm_1", USBD_SPEED_FS, 1); + if (err) { + printk("Failed to add register classes\n"); + return; + } + } -// #if CFG_TUSB_OS == OPT_OS_FREERTOS -// // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher ) -// NVIC_SetPriority(OTG_HS_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY); -// #endif + err = usbd_register_class(&main_usbd, "msc_0", USBD_SPEED_FS, 1); + if (err) { + printk("Failed to add register MSC class %d\n", err); + return; + } + /* doc functions register end */ -// /* USB clock enable */ -// __HAL_RCC_USB_OTG_HS_CLK_ENABLE(); -// __HAL_RCC_USBPHYC_CLK_ENABLE(); + usbd_device_set_code_triple(&main_usbd, USBD_SPEED_FS, + USB_BCC_MISCELLANEOUS, 0x02, 0x01); + printk("Setting self powered\n"); + usbd_self_powered(&main_usbd, attributes & USB_SCD_SELF_POWERED); -// /* Enable USB power on Pwrctrl CR2 register */ -// HAL_PWREx_EnableVddUSB(); -// HAL_PWREx_EnableUSBHSTranceiverSupply(); + printk("Registering callback\n"); + err = usbd_msg_register_cb(&main_usbd, _msg_cb); + if (err) { + LOG_ERR("Failed to register message callback"); + return; + } -// /*Configuring the SYSCFG registers OTG_HS PHY*/ -// HAL_SYSCFG_EnableOTGPHY(SYSCFG_OTG_HS_PHY_ENABLE); + printk("usbd_init\n"); + err = usbd_init(&main_usbd); + if (err) { + LOG_ERR("Failed to initialize device support"); + return; + } -// // Disable VBUS sense (B device) -// USB_OTG_HS->GCCFG &= ~USB_OTG_GCCFG_VBDEN; + printk("USB initialized\n"); -// // B-peripheral session valid override enable -// USB_OTG_HS->GCCFG |= USB_OTG_GCCFG_VBVALEXTOEN; -// USB_OTG_HS->GCCFG |= USB_OTG_GCCFG_VBVALOVAL; -// #endif // USB_OTG_FS + if (!usbd_can_detect_vbus(&main_usbd)) { + err = usbd_enable(&main_usbd); + if (err) { + LOG_ERR("Failed to enable device support"); + return; + } + printk("usbd enabled\n"); + } +} +bool usb_connected(void) { + return false; +} - #if CFG_TUSB_MCU == OPT_MCU_NRF5X - #ifdef CONFIG_HAS_HW_NRF_USBREG - /* Use CLOCK/POWER priority for compatibility with other series where - * USB events are handled by CLOCK interrupt handler. - */ - IRQ_CONNECT(USBREGULATOR_IRQn, - DT_IRQ(DT_INST(0, nordic_nrf_clock), priority), - nrfx_isr, nrfx_usbreg_irq_handler, 0); - irq_enable(USBREGULATOR_IRQn); - #endif - // USB power may already be ready at this time -> no event generated - // We need to invoke the handler based on the status initially - uint32_t usb_reg; - { - // Power module init - static const nrfx_power_config_t pwr_cfg = { - .dcdcen = (DT_PROP(DT_INST(0, nordic_nrf5x_regulator), regulator_initial_mode) - == NRF5X_REG_MODE_DCDC), - #if NRFX_POWER_SUPPORTS_DCDCEN_VDDH - .dcdcenhv = COND_CODE_1(CONFIG_SOC_SERIES_NRF52X, - (DT_NODE_HAS_STATUS_OKAY(DT_INST(0, nordic_nrf52x_regulator_hv))), - (DT_NODE_HAS_STATUS_OKAY(DT_INST(0, nordic_nrf53x_regulator_hv)))), - #endif - }; - nrfx_power_init(&pwr_cfg); - - // Register tusb function as USB power handler - // cause cast-function-type warning - const nrfx_power_usbevt_config_t config = {.handler = power_event_handler}; - nrfx_power_usbevt_init(&config); - nrfx_power_usbevt_enable(); - - // USB power may already be ready at this time -> no event generated - // We need to invoke the handler based on the status initially - #ifdef NRF5340_XXAA - usb_reg = NRF_USBREGULATOR->USBREGSTATUS; - #else - usb_reg = NRF_POWER->USBREGSTATUS; - #endif - } - - if (usb_reg & VBUSDETECT_Msk) { - tusb_hal_nrf_power_event(USB_EVT_DETECTED); - } - if (usb_reg & OUTPUTRDY_Msk) { - tusb_hal_nrf_power_event(USB_EVT_READY); - } - - printk("usb started hopefully\n"); - #endif +void usb_disconnect(void) { +} +usb_cdc_serial_obj_t *usb_cdc_serial_get_console(void) { + if (usb_cdc_console_enabled()) { + return &usb_cdc_console_obj; + } + return NULL; } diff --git a/ports/zephyr-cp/supervisor/zephyr-cp.h b/ports/zephyr-cp/supervisor/zephyr-cp.h new file mode 100644 index 0000000000000..ecf7d42e3b48c --- /dev/null +++ b/ports/zephyr-cp/supervisor/zephyr-cp.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/usb_cdc/Serial.h" + +usb_cdc_serial_obj_t *usb_cdc_serial_get_console(void); diff --git a/ports/zephyr-cp/sysbuild.cmake b/ports/zephyr-cp/sysbuild.cmake index f0968e05b5c9b..3c3acf0a803c7 100644 --- a/ports/zephyr-cp/sysbuild.cmake +++ b/ports/zephyr-cp/sysbuild.cmake @@ -18,4 +18,6 @@ if(SB_CONFIG_NET_CORE_IMAGE_HCI_IPC) CACHE INTERNAL "" ) + native_simulator_set_child_images(${DEFAULT_IMAGE} ${NET_APP}) + native_simulator_set_final_executable(${DEFAULT_IMAGE}) endif() diff --git a/ports/zephyr-cp/tests/TEST_IDEAS.md b/ports/zephyr-cp/tests/TEST_IDEAS.md new file mode 100644 index 0000000000000..6ef71d7a5a041 --- /dev/null +++ b/ports/zephyr-cp/tests/TEST_IDEAS.md @@ -0,0 +1,623 @@ +# CircuitPython Simulator Test Ideas + +Test ideas for the native_sim simulator, organized by module/category. + +## Core Python / Interpreter + +### 1. Multiple file priority +Test that boot.py runs before code.py, and main.py is used as fallback when code.py doesn't exist. + +```python +# boot.py +print("boot.py ran") + +# code.py +print("code.py ran") +``` + +Expected output order: "boot.py ran" then "code.py ran" + +### 2. Exception handling +Verify tracebacks print correctly with file/line info, and exception types propagate properly. + +```python +def inner(): + raise ValueError("test error") + +def outer(): + inner() + +outer() +``` + +### 3. Memory / gc module +Test `gc.collect()`, `gc.mem_free()`, `gc.mem_alloc()`, and behavior under memory pressure. + +```python +import gc +gc.collect() +free_before = gc.mem_free() +data = [0] * 1000 +free_after = gc.mem_free() +assert free_after < free_before +del data +gc.collect() +free_final = gc.mem_free() +print("done") +``` + +### 4. Import system +Test importing frozen modules vs filesystem modules, verify import errors are clear. + +```python +import sys +import board +import digitalio +print(f"modules loaded: {len(sys.modules)}") +print("done") +``` + +--- + +## digitalio + +### 5. Input mode +Test reading GPIO input state. May require trace file injection or loopback configuration. + +```python +import board +import digitalio + +pin = digitalio.DigitalInOut(board.D0) +pin.direction = digitalio.Direction.INPUT +value = pin.value +print(f"input value: {value}") +print("done") +``` + +### 6. Pull resistors +Verify pull-up/pull-down configuration affects input readings. + +```python +import board +import digitalio + +pin = digitalio.DigitalInOut(board.D0) +pin.direction = digitalio.Direction.INPUT +pin.pull = digitalio.Pull.UP +value_up = pin.value +pin.pull = digitalio.Pull.DOWN +value_down = pin.value +print(f"pull-up: {value_up}, pull-down: {value_down}") +print("done") +``` + +### 7. Direction switching +Switch same pin between input/output modes multiple times. + +```python +import board +import digitalio + +pin = digitalio.DigitalInOut(board.D0) +pin.direction = digitalio.Direction.OUTPUT +pin.value = True +pin.direction = digitalio.Direction.INPUT +_ = pin.value +pin.direction = digitalio.Direction.OUTPUT +pin.value = False +print("done") +``` + +--- + +## time module + +### 8. time.sleep() precision +Verify sleep timing via Perfetto trace timestamps. Use GPIO transitions as timing markers. + +```python +import time +import board +import digitalio + +led = digitalio.DigitalInOut(board.LED) +led.direction = digitalio.Direction.OUTPUT + +led.value = True +time.sleep(0.05) # 50ms +led.value = False +time.sleep(0.1) # 100ms +led.value = True +time.sleep(0.05) # 50ms +led.value = False +print("done") +``` + +Verify trace shows: 50ms high, 100ms low, 50ms high pattern. + +### 9. time.monotonic() +Test monotonic clock increments correctly and never goes backward. + +```python +import time + +samples = [] +for _ in range(10): + samples.append(time.monotonic()) + time.sleep(0.01) + +# Verify monotonic increase +for i in range(1, len(samples)): + assert samples[i] > samples[i-1], "monotonic went backward!" + +elapsed = samples[-1] - samples[0] +assert 0.08 < elapsed < 0.15, f"unexpected elapsed: {elapsed}" +print("done") +``` + +### 10. time.localtime / struct_time +Test time structure operations if available. + +```python +import time + +t = time.localtime() +print(f"year: {t.tm_year}") +print(f"month: {t.tm_mon}") +print(f"day: {t.tm_mday}") +print("done") +``` + +--- + +## microcontroller + +### 11. microcontroller.cpu properties +Test frequency, temperature, UID properties. + +```python +import microcontroller + +print(f"frequency: {microcontroller.cpu.frequency}") +print(f"uid: {microcontroller.cpu.uid.hex()}") +print("done") +``` + +### 12. Safe mode +Trigger and verify safe mode entry (requires CONFIG_NATIVE_SIM_REBOOT=y). + +```python +import microcontroller +# This would trigger safe mode - test carefully +# microcontroller.on_next_reset(microcontroller.RunMode.SAFE_MODE) +# microcontroller.reset() +print("done") +``` + +### 13. Reset reason +Test microcontroller.reset_reason after various reset types. + +```python +import microcontroller + +reason = microcontroller.reset_reason +print(f"reset reason: {reason}") +print("done") +``` + +--- + +## os module + +### 14. os.uname() +Verify returns correct system info for native_sim. + +```python +import os + +info = os.uname() +print(f"sysname: {info.sysname}") +print(f"machine: {info.machine}") +print(f"release: {info.release}") +print("done") +``` + +### 15. Filesystem operations +Test os.listdir(), os.stat(), os.remove(). + +```python +import os + +# List root +files = os.listdir("/") +print(f"root files: {files}") + +# Stat a file +if "code.py" in files: + stat = os.stat("/code.py") + print(f"code.py size: {stat[6]}") + +print("done") +``` + +### 16. os.getenv() +Test environment variable reading. + +```python +import os + +# May return None if not set +path = os.getenv("PATH") +print(f"PATH exists: {path is not None}") +print("done") +``` + +--- + +## board module + +### 17. board.board_id +Verify board ID matches expected value. + +```python +import board + +print(f"board_id: {board.board_id}") +assert board.board_id == "native_native_sim" +print("done") +``` + +### 18. Pin availability +Test that board.LED and other defined pins exist. + +```python +import board + +assert hasattr(board, "LED"), "board.LED missing" +print(f"LED pin: {board.LED}") +print("done") +``` + +--- + +## Filesystem + +### 19. File read/write +Create, read, and modify files on CIRCUITPY. + +```python +# Write a test file +with open("/test.txt", "w") as f: + f.write("hello world") + +# Read it back +with open("/test.txt", "r") as f: + content = f.read() + +assert content == "hello world" +print("done") +``` + +### 20. Directory operations +Test mkdir, rmdir, nested paths. + +```python +import os + +# Create directory +os.mkdir("/testdir") +assert "testdir" in os.listdir("/") + +# Create file in directory +with open("/testdir/file.txt", "w") as f: + f.write("nested") + +# Clean up +os.remove("/testdir/file.txt") +os.rmdir("/testdir") +assert "testdir" not in os.listdir("/") +print("done") +``` + +### 21. Large file handling +Test with larger files approaching flash limits. + +```python +import gc + +# Write 10KB file +data = "x" * 10240 +with open("/large.txt", "w") as f: + f.write(data) + +# Verify size +import os +stat = os.stat("/large.txt") +assert stat[6] == 10240 + +# Clean up +os.remove("/large.txt") +gc.collect() +print("done") +``` + +--- + +## Error Conditions + +### 22. Syntax error in code.py +Verify graceful error message when code.py has syntax error. + +```python +# code.py with intentional syntax error: +def broken( + print("missing close paren" +``` + +Expected: Clear syntax error message with line number. + +### 23. Runtime error +Verify traceback format shows file, line, and function. + +```python +def cause_error(): + x = 1 / 0 + +cause_error() +``` + +Expected: Traceback showing ZeroDivisionError with line info. + +### 24. Keyboard interrupt +Test Ctrl+C handling via PTY if applicable. + +```python +import time + +print("starting long loop") +for i in range(100): + print(f"iteration {i}") + time.sleep(0.1) +print("done") +``` + +Send Ctrl+C during execution, verify clean KeyboardInterrupt. + +--- + +## busio (if emulation available) + +### 25. busio.UART +Basic UART operations if configured. + +```python +import board +import busio + +# Check if UART pins exist +if hasattr(board, "TX") and hasattr(board, "RX"): + uart = busio.UART(board.TX, board.RX, baudrate=115200) + uart.write(b"test") + uart.deinit() +print("done") +``` + +### 26. busio.I2C scan +Scan for emulated I2C devices. + +```python +import board +import busio + +if hasattr(board, "SCL") and hasattr(board, "SDA"): + i2c = busio.I2C(board.SCL, board.SDA) + while not i2c.try_lock(): + pass + devices = i2c.scan() + print(f"I2C devices: {[hex(d) for d in devices]}") + i2c.unlock() + i2c.deinit() +print("done") +``` + +### 27. busio.SPI +Basic SPI transfer to emulated device. + +```python +import board +import busio + +if hasattr(board, "SCK") and hasattr(board, "MOSI"): + spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=getattr(board, "MISO", None)) + while not spi.try_lock(): + pass + spi.configure(baudrate=1000000) + result = bytearray(4) + spi.write_readinto(b"\x00\x00\x00\x00", result) + spi.unlock() + spi.deinit() +print("done") +``` + +--- + +## Serial/PTY Input + +These tests require extending conftest.py to support writing to the PTY, not just reading. + +### 28. Basic serial input +Test reading single characters from serial via PTY write. + +```python +import sys + +print("ready") +char = sys.stdin.read(1) +print(f"received: {repr(char)}") +print("done") +``` + +**Test harness**: After seeing "ready", write "A" to PTY, verify output shows "received: 'A'". + +### 29. input() function +Test the built-in input() function with PTY input. + +```python +print("ready") +name = input("Enter name: ") +print(f"hello {name}") +print("done") +``` + +**Test harness**: After seeing "Enter name:", write "World\n" to PTY, verify "hello World". + +### 30. Serial line buffering +Test reading a complete line with newline termination. + +```python +import sys + +print("ready") +line = sys.stdin.readline() +print(f"got: {repr(line)}") +print("done") +``` + +**Test harness**: Write "test line\n" to PTY, verify complete line received. + +### 31. usb_cdc.console data read +Test reading from usb_cdc.console.data if available. + +```python +import usb_cdc + +print("ready") +if usb_cdc.console: + while not usb_cdc.console.in_waiting: + pass + data = usb_cdc.console.read(usb_cdc.console.in_waiting) + print(f"console got: {data}") +print("done") +``` + +**Test harness**: Write bytes to PTY, verify they're received via usb_cdc.console. + +### 32. REPL interaction +Test entering REPL mode and executing commands interactively. + +```python +# No code.py - boots to REPL +``` + +**Test harness**: +1. Boot with empty/no code.py to get REPL prompt +2. Write "1 + 1\r\n" to PTY +3. Verify output contains "2" +4. Write "print('hello')\r\n" +5. Verify output contains "hello" + +### 33. Ctrl+C interrupt via PTY +Test sending Ctrl+C (0x03) to interrupt running code. + +```python +import time + +print("starting") +for i in range(100): + print(f"loop {i}") + time.sleep(0.1) +print("completed") # Should not reach this +``` + +**Test harness**: +1. Wait for "loop 5" in output +2. Write b"\x03" (Ctrl+C) to PTY +3. Verify KeyboardInterrupt raised +4. Verify "completed" NOT in output + +### 34. Ctrl+D soft reload via PTY +Test sending Ctrl+D (0x04) to trigger soft reload. + +```python +print("first run") +import time +time.sleep(10) # Long sleep to allow interrupt +print("done") +``` + +**Test harness**: +1. Wait for "first run" +2. Write b"\x04" (Ctrl+D) to PTY +3. Verify code restarts (see "first run" again or reload message) + +### 35. Serial input timeout +Test behavior when waiting for input with timeout. + +```python +import sys +import select + +print("ready") +# Poll for input with timeout +readable, _, _ = select.select([sys.stdin], [], [], 0.5) +if readable: + data = sys.stdin.read(1) + print(f"got: {repr(data)}") +else: + print("timeout") +print("done") +``` + +**Test harness**: Don't send anything, verify "timeout" appears. + +--- + +## Fixture Changes for PTY Input + +The `run_circuitpython` fixture in conftest.py needs to be extended: + +```python +@dataclass +class SimulatorResult: + output: str + trace_file: Path + pty_write_fd: int # New: file descriptor for writing to PTY + +def _run(code: str | None, timeout: float = 5.0, ...) -> SimulatorResult: + # Open PTY for both read AND write + pty_fd = os.open(pty_path, os.O_RDWR | os.O_NONBLOCK) + # ... +``` + +Or provide a callback/queue mechanism: + +```python +def _run(code, timeout=5.0, input_sequence=None): + """ + input_sequence: list of (trigger_text, bytes_to_send) tuples + When trigger_text is seen in output, send bytes_to_send to PTY + """ +``` + +--- + +## Implementation Priority + +Suggested order for implementation: + +### Phase 1: Basic module tests (no fixture changes) +1. **#17 board.board_id** - Quick sanity check +2. **#14 os.uname()** - Tests another module, simple +3. **#9 time.monotonic()** - Core timing functionality +4. **#8 time.sleep() precision** - Builds on GPIO trace infrastructure +5. **#1 Multiple file priority** - Tests interpreter boot sequence +6. **#19 File read/write** - Tests filesystem layer +7. **#3 Memory / gc** - Tests memory management +8. **#2 Exception handling** - Tests error reporting + +### Phase 2: PTY input tests (requires fixture extension) +9. **#28 Basic serial input** - Foundation for all input tests +10. **#33 Ctrl+C interrupt** - Important for interactive use +11. **#29 input() function** - Common user pattern +12. **#32 REPL interaction** - Tests interactive mode diff --git a/ports/zephyr-cp/tests/__init__.py b/ports/zephyr-cp/tests/__init__.py new file mode 100644 index 0000000000000..e9039dd88b346 --- /dev/null +++ b/ports/zephyr-cp/tests/__init__.py @@ -0,0 +1,162 @@ +from pathlib import Path + +import serial +import subprocess +import threading +import time + + +class StdSerial: + def __init__(self, stdin, stdout): + self.stdin = stdin + self.stdout = stdout + + def read(self, amount=None): + data = self.stdout.read(amount) + if data == b"": + raise EOFError("stdout closed") + return data + + def write(self, buf): + if self.stdin is None: + return + self.stdin.write(buf) + self.stdin.flush() + + def close(self): + if self.stdin is not None: + self.stdin.close() + self.stdout.close() + + @property + def in_waiting(self): + if self.stdout is None: + return 0 + return len(self.stdout.peek()) + + +class SerialSaver: + """Capture serial output in a background thread so output isn't missed.""" + + def __init__(self, serial_obj, name="serial"): + self.all_output = "" + self.all_input = "" + self.serial = serial_obj + self.name = name + + self._stop = threading.Event() + self._lock = threading.Lock() + self._cv = threading.Condition(self._lock) + self._reader = threading.Thread(target=self._reader_loop, daemon=True) + self._reader.start() + + def _reader_loop(self): + while not self._stop.is_set(): + try: + read = self.serial.read(1) + except Exception: + # Serial port closed or device disconnected. + break + + if read == b"": + # Timeout with no data — keep waiting. Only a real + # exception or an explicit stop should end the loop. + continue + + text = read.decode("utf-8", errors="replace") + with self._cv: + self.all_output += text + self._cv.notify_all() + in_waiting = 0 + try: + in_waiting = self.serial.in_waiting + except OSError: + pass + if in_waiting > 0: + self.all_output += self.serial.read().decode("utf-8", errors="replace") + + def wait_for(self, text, timeout=10): + with self._cv: + while text not in self.all_output and self._reader.is_alive(): + if not self._cv.wait(timeout=timeout): + break + if text not in self.all_output: + tail = self.all_output[-400:] + raise TimeoutError( + f"Timed out waiting for {text!r} on {self.name}. Output tail:\n{tail}" + ) + + def read(self, amount=None): + # Kept for compatibility with existing callers. + return + + def close(self): + if not self.serial: + return + + self._stop.set() + self._reader.join(timeout=1.0) + try: + self.serial.close() + except Exception: + pass + self.serial = None + + def write(self, text): + self.all_input += text + self.serial.write(text.encode("utf-8")) + + +class NativeSimProcess: + def __init__(self, cmd, timeout=5, trace_file=None, env=None, flash_file=None): + if trace_file: + cmd.append(f"--trace-file={trace_file}") + + self._timeout = timeout + self.trace_file = trace_file + self.flash_file = flash_file + print("Running", " ".join(cmd)) + self._proc = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=None, + env=env, + ) + if self._proc.stdout is None: + raise RuntimeError("Failed to capture simulator stdout") + + # Discard the test warning + uart_pty_line = self._proc.stdout.readline().decode("utf-8") + if "connected to pseudotty:" not in uart_pty_line: + raise RuntimeError("Failed to connect to UART") + pty_path = uart_pty_line.strip().rsplit(":", maxsplit=1)[1].strip() + self.serial = SerialSaver( + serial.Serial(pty_path, baudrate=115200, timeout=0.05, write_timeout=0), + name="uart0", + ) + self.debug_serial = SerialSaver( + StdSerial(self._proc.stdin, self._proc.stdout), name="debug" + ) + + def shutdown(self): + if self._proc.poll() is None: + self._proc.terminate() + self._proc.wait(timeout=self._timeout) + + self.serial.close() + self.debug_serial.close() + + def display_capture_paths(self) -> list[Path]: + """Return paths to numbered PNG capture files produced by trace-driven capture.""" + pattern = getattr(self, "_capture_png_pattern", None) + count = getattr(self, "_capture_count", 0) + if not pattern or count == 0: + return [] + return [Path(pattern % i) for i in range(count)] + + def wait_until_done(self): + start_time = time.monotonic() + while self._proc.poll() is None and time.monotonic() - start_time < self._timeout: + time.sleep(0.01) + self.shutdown() diff --git a/ports/zephyr-cp/tests/bsim/__init__.py b/ports/zephyr-cp/tests/bsim/__init__.py new file mode 100644 index 0000000000000..75136bccf43ae --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/__init__.py @@ -0,0 +1,3 @@ +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") diff --git a/ports/zephyr-cp/tests/bsim/conftest.py b/ports/zephyr-cp/tests/bsim/conftest.py new file mode 100644 index 0000000000000..493f4c92b3ba0 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/conftest.py @@ -0,0 +1,227 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Pytest fixtures for CircuitPython bsim testing.""" + +import logging +import os +import shutil +import subprocess +from pathlib import Path + +import pytest + +from .. import SerialSaver, StdSerial + +logger = logging.getLogger(__name__) + +ZEPHYR_CP = Path(__file__).resolve().parents[2] +BSIM_BUILD_DIR = ZEPHYR_CP / "build-native_nrf5340bsim" +BSIM_SYSBUILD_BINARY = BSIM_BUILD_DIR / "zephyr/zephyr.exe" +BSIM_BINARY = BSIM_BUILD_DIR / "zephyr-cp/zephyr/zephyr.exe" +BSIM_ROOT = ZEPHYR_CP / "tools/bsim" +BSIM_PHY_BINARY = BSIM_ROOT / "bin/bs_2G4_phy_v1" + + +@pytest.fixture +def native_sim_env() -> dict[str, str]: + env = os.environ.copy() + env["BSIM_OUT_PATH"] = str(BSIM_ROOT) + env["BSIM_COMPONENTS_PATH"] = str(BSIM_ROOT / "components") + lib_path = str(BSIM_ROOT / "lib") + existing = env.get("LD_LIBRARY_PATH", "") + env["LD_LIBRARY_PATH"] = f"{lib_path}:{existing}" if existing else lib_path + return env + + +@pytest.fixture +def bsim_binary(): + """Return path to nrf5340bsim binary, skip if not built.""" + if BSIM_SYSBUILD_BINARY.exists(): + return BSIM_SYSBUILD_BINARY + if not BSIM_BINARY.exists(): + pytest.skip(f"nrf5340bsim not built: {BSIM_BINARY}") + return BSIM_BINARY + + +@pytest.fixture +def bsim_phy_binary(): + """Return path to BabbleSim PHY binary, skip if not present.""" + if not BSIM_PHY_BINARY.exists(): + pytest.skip(f"bs_2G4_phy_v1 not found: {BSIM_PHY_BINARY}") + return BSIM_PHY_BINARY + + +class BsimPhyInstance: + def __init__(self, proc: subprocess.Popen, serial: SerialSaver, timeout: float): + self.proc = proc + self.serial = serial + self.timeout = timeout + + def finish_sim(self) -> None: + self.serial.wait_for("Cleaning up", timeout=self.timeout + 5) + + def shutdown(self) -> None: + if self.proc.poll() is None: + self.proc.terminate() + self.proc.wait(timeout=2) + self.serial.close() + + +class ZephyrSampleProcess: + def __init__(self, proc: subprocess.Popen, timeout: float): + self._proc = proc + self._timeout = timeout + if proc.stdout is None: + raise RuntimeError("Failed to capture Zephyr sample stdout") + self.serial = SerialSaver(StdSerial(None, proc.stdout), name="zephyr sample") + + def shutdown(self) -> None: + if self._proc.poll() is None: + self._proc.terminate() + self._proc.wait(timeout=self._timeout) + self.serial.close() + + +@pytest.fixture +def bsim_phy(request, bsim_phy_binary, native_sim_env, sim_id): + duration_marker = request.node.get_closest_marker("duration") + duration = float(duration_marker.args[0]) if duration_marker else 20.0 + + devices = 1 + if "circuitpython2" in request.fixturenames or "zephyr_sample" in request.fixturenames: + devices = 2 + + sample_marker = request.node.get_closest_marker("zephyr_sample") + if sample_marker is not None: + sample_device_id = int(sample_marker.kwargs.get("device_id", 1)) + devices = max(devices, sample_device_id + 1) + + # Do not pass -sim_length: if the PHY exits on simulated time, device 0 can + # still be flushing UART output and test output can get truncated. Instead, + # let pytest own process lifetime and terminate the PHY at fixture teardown. + cmd = [ + "stdbuf", + "-oL", + str(bsim_phy_binary), + "-v=9", # Cleaning up level is on 9. Connecting is 7. + f"-s={sim_id}", + f"-D={devices}", + "-argschannel", + "-at=40", # 40 dB attenuation (default 60) so RSSI ~ -40 dBm + ] + print("Running:", " ".join(cmd)) + proc = subprocess.Popen( + cmd, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=native_sim_env, + cwd=BSIM_ROOT / "bin", + ) + if proc.stdout is None: + raise RuntimeError("Failed to capture bsim phy stdout") + + # stdbuf -oL forces line-buffered stdout so SerialSaver can + # stream-read PHY output in real time. Wrapping in StdSerial + # ensures the reader thread exits on EOF when the PHY process + # terminates, rather than spinning on empty timeout reads. + phy_output = SerialSaver(StdSerial(None, proc.stdout), name="bsim phy") + try: + phy_output.wait_for("Connecting", timeout=2) + except TimeoutError: + if proc.poll() is not None: + print(phy_output.all_output) + raise RuntimeError("bsim PHY exited immediately") + # Assume bsim is running + + phy = BsimPhyInstance(proc, phy_output, timeout=duration) + yield phy + phy.shutdown() + + print("bsim phy output:") + print(phy_output.all_output) + + +def _build_zephyr_sample(build_dir: Path, source_dir: Path, board: str) -> Path: + if shutil.which("west") is None: + raise RuntimeError("west not found") + + cmd = [ + "west", + "build", + "-b", + board, + "-d", + str(build_dir), + "-p=auto", + str(source_dir), + ] + logger.info("Building Zephyr sample: %s", " ".join(cmd)) + subprocess.run(cmd, check=True, cwd=ZEPHYR_CP) + + return build_dir / "zephyr/zephyr.exe" + + +@pytest.fixture +def zephyr_sample(request, bsim_phy, native_sim_env, sim_id): + marker = request.node.get_closest_marker("zephyr_sample") + if marker is None or len(marker.args) != 1: + raise RuntimeError( + "zephyr_sample fixture requires @pytest.mark.zephyr_sample('')" + ) + + sample = marker.args[0] + board = marker.kwargs.get("board", "nrf52_bsim") + device_id = int(marker.kwargs.get("device_id", 1)) + timeout = float(marker.kwargs.get("timeout", 10.0)) + + sample_rel = str(sample).removeprefix("zephyr/samples/") + source_dir = ZEPHYR_CP / "zephyr/samples" / sample_rel + if not source_dir.exists(): + pytest.skip(f"Zephyr sample not found: {source_dir}") + + build_name = f"build-bsim-sample-{sample_rel.replace('/', '_')}-{board}" + build_dir = ZEPHYR_CP / build_name + binary = build_dir / "zephyr/zephyr.exe" + + if not binary.exists(): + try: + binary = _build_zephyr_sample(build_dir, source_dir, board) + except (subprocess.CalledProcessError, RuntimeError) as exc: + pytest.skip(f"Failed to build Zephyr sample {sample_rel}: {exc}") + + if not binary.exists(): + pytest.skip(f"Zephyr sample binary not found: {binary}") + + cmd = [ + str(binary), + f"-s={sim_id}", + f"-d={device_id}", + "-disconnect_on_exit=1", + ] + logger.info("Running: %s", " ".join(cmd)) + proc = subprocess.Popen( + cmd, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + env=native_sim_env, + ) + sample_proc = ZephyrSampleProcess(proc, timeout=timeout) + yield sample_proc + sample_proc.shutdown() + + print("Zephyr sample output:") + print(sample_proc.serial.all_output) + + +# pytest markers are defined inside out meaning the bottom one is first in the +# list and the top is last. So use negative indices to reverse them. +@pytest.fixture +def circuitpython1(circuitpython): + return circuitpython[-1] + + +@pytest.fixture +def circuitpython2(circuitpython): + return circuitpython[-2] diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_basics.py b/ports/zephyr-cp/tests/bsim/test_bsim_basics.py new file mode 100644 index 0000000000000..477292ddd5465 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_basics.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Basic BabbleSim connectivity tests for nrf5340bsim.""" + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_CODE = """\ +print("bsim ready") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_CODE}) +@pytest.mark.duration(3) +def test_bsim_dual_instance_connect(bsim_phy, circuitpython1, circuitpython2): + """Run two bsim instances on the same sim id and verify UART output.""" + + # Wait for both devices to complete before checking output. + circuitpython1.wait_until_done() + circuitpython2.wait_until_done() + + output0 = circuitpython1.serial.all_output + output1 = circuitpython2.serial.all_output + + assert "Board ID:native_nrf5340bsim" in output0 + assert "Board ID:native_nrf5340bsim" in output1 + assert "bsim ready" in output0 + assert "bsim ready" in output1 diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py new file mode 100644 index 0000000000000..33680fe2506f5 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_advertising.py @@ -0,0 +1,174 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE advertising tests for nrf5340bsim.""" + +import logging +import re + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +logger = logging.getLogger(__name__) + +BSIM_ADV_CODE = """\ +import _bleio +import time + +name = b"CPADV" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +adapter = _bleio.adapter +print("adv start") +adapter.start_advertising(advertisement, connectable=False) +print("adv started") +time.sleep(4) +adapter.stop_advertising() +print("adv stop") +""" + +BSIM_ADV_INTERRUPT_RELOAD_CODE = """\ +import _bleio +import time + +name = b"CPADV" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +adapter = _bleio.adapter +print("adv run start") +adapter.start_advertising(advertisement, connectable=False) +print("adv running") +time.sleep(10) +adapter.stop_advertising() +print("adv run done") +""" + + +BSIM_TX_POWER_DEFAULT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +name = b"CPTXPWR" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +print("advertising default") +adapter.start_advertising(advertisement) +time.sleep(4) +adapter.stop_advertising() +print("done") +""" + +BSIM_TX_POWER_LOW_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +name = b"CPTXPWR" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +print("advertising low") +adapter.start_advertising(advertisement, tx_power=-20) +time.sleep(4) +adapter.stop_advertising() +print("done") +""" + + +@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.circuitpy_drive({"code.py": BSIM_ADV_CODE}) +def test_bsim_advertise_and_scan(bsim_phy, circuitpython, zephyr_sample): + """Advertise from CircuitPython and verify Zephyr observer sees traffic.""" + observer = zephyr_sample + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + observer_output = observer.serial.all_output + assert "adv start" in cp_output + assert "adv started" in cp_output + assert "adv stop" in cp_output + assert "Device found:" in observer_output + assert "AD data len 10" in observer_output + + +@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.code_py_runs(2) +@pytest.mark.duration(25) +@pytest.mark.circuitpy_drive({"code.py": BSIM_ADV_INTERRUPT_RELOAD_CODE}) +def test_bsim_advertise_ctrl_c_reload(bsim_phy, circuitpython, zephyr_sample): + """Ensure advertising resumes after Ctrl-C and a reload.""" + observer = zephyr_sample + + circuitpython.serial.wait_for("adv running") + observer.serial.wait_for("Device found:") + observer_count_before = observer.serial.all_output.count("Device found:") + + circuitpython.serial.write("\x03") + circuitpython.serial.wait_for("KeyboardInterrupt") + + circuitpython.serial.write("\x04") + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + observer_output = observer.serial.all_output + logger.info(observer_output) + logger.info(cp_output) + + assert "adv run start" in cp_output + assert "KeyboardInterrupt" in cp_output + assert cp_output.count("adv running") >= 2 + assert cp_output.count("adv run done") >= 1 + assert observer_output.count("Device found:") >= observer_count_before + 1 + assert "Already advertising" not in cp_output + + +@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.circuitpy_drive({"code.py": BSIM_TX_POWER_DEFAULT_CODE}) +def test_bsim_tx_power_default_rssi(bsim_phy, circuitpython, zephyr_sample): + """Verify default TX power produces expected RSSI.""" + observer = zephyr_sample + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + obs_output = observer.serial.all_output + + assert "advertising default" in cp_output + assert "done" in cp_output + + # Observer: "Device found: (RSSI ), type , AD data len " + # Advertisement is 12 bytes: flags (3) + name (9). + # With 40 dB channel attenuation and 0 dBm TX → RSSI ~ -39 + rssi_pattern = re.compile(r"RSSI (-?\d+)\), type \d+, AD data len 12") + all_rssi = [int(m.group(1)) for m in rssi_pattern.finditer(obs_output)] + logger.info("RSSI values: %s", all_rssi) + + assert len(all_rssi) > 0, "Observer saw no advertisements" + assert all_rssi[0] == -39, f"Expected RSSI -39 (0 dBm TX), got {all_rssi[0]}" + + +@pytest.mark.zephyr_sample("bluetooth/observer") +@pytest.mark.circuitpy_drive({"code.py": BSIM_TX_POWER_LOW_CODE}) +def test_bsim_tx_power_low_rssi(bsim_phy, circuitpython, zephyr_sample): + """Verify low TX power reduces RSSI.""" + observer = zephyr_sample + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + obs_output = observer.serial.all_output + + assert "advertising low" in cp_output + assert "done" in cp_output + + # With 40 dB channel attenuation and -20 dBm TX → RSSI ~ -59 + rssi_pattern = re.compile(r"RSSI (-?\d+)\), type \d+, AD data len 12") + all_rssi = [int(m.group(1)) for m in rssi_pattern.finditer(obs_output)] + logger.info("RSSI values: %s", all_rssi) + + assert len(all_rssi) > 0, "Observer saw no advertisements" + assert all_rssi[0] < -39, f"Expected lower RSSI with -20 dBm TX, got {all_rssi[0]}" diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py new file mode 100644 index 0000000000000..21cfeaf79da50 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_connect.py @@ -0,0 +1,119 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE central connection tests for nrf5340bsim.""" + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_CONNECT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("connect start") +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable: + target = entry.address + print("found target") + break +adapter.stop_scan() +print("have target", target is not None) + +if target is None: + raise RuntimeError("No connectable target found") + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected, adapter.connected, len(adapter.connections)) +connection.disconnect() + +for _ in range(40): + if not connection.connected and not adapter.connected: + break + time.sleep(0.1) + +print("disconnected", connection.connected, adapter.connected, len(adapter.connections)) +""" + +BSIM_RECONNECT_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("run start") +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable: + target = entry.address + print("run found target") + break +adapter.stop_scan() +print("run have target", target is not None) + +if target is None: + raise RuntimeError("No connectable target found") + +connection = adapter.connect(target, timeout=5.0) +print("run connected", connection.connected, adapter.connected, len(adapter.connections)) +connection.disconnect() + +for _ in range(50): + if not connection.connected and not adapter.connected and len(adapter.connections) == 0: + break + time.sleep(0.1) + +print("run disconnected", connection.connected, adapter.connected, len(adapter.connections)) +""" + + +@pytest.mark.zephyr_sample("bluetooth/peripheral") +@pytest.mark.duration(14) +@pytest.mark.circuitpy_drive({"code.py": BSIM_CONNECT_CODE}) +def test_bsim_connect_zephyr_peripheral(bsim_phy, circuitpython, zephyr_sample): + """Connect to the Zephyr peripheral sample and disconnect cleanly.""" + peripheral = zephyr_sample + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + peripheral_output = peripheral.serial.all_output + + assert "connect start" in cp_output + assert "found target" in cp_output + assert "have target True" in cp_output + assert "connected True True 1" in cp_output + assert "disconnected False False 0" in cp_output + + assert "Advertising successfully started" in peripheral_output + assert "Connected" in peripheral_output + + +@pytest.mark.zephyr_sample("bluetooth/peripheral_sc_only") +@pytest.mark.code_py_runs(2) +@pytest.mark.duration(26) +@pytest.mark.circuitpy_drive({"code.py": BSIM_RECONNECT_CODE}) +def test_bsim_reconnect_zephyr_peripheral(bsim_phy, circuitpython, zephyr_sample): + """Connect/disconnect, soft reload, then connect/disconnect again.""" + peripheral = zephyr_sample + + circuitpython.serial.wait_for("run disconnected") + circuitpython.serial.wait_for("Press any key to enter the REPL") + circuitpython.serial.write("\x04") + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + peripheral_output = peripheral.serial.all_output + + assert cp_output.count("run start") >= 2 + assert cp_output.count("run found target") >= 2 + assert cp_output.count("run have target True") >= 2 + assert cp_output.count("run connected True True 1") >= 2 + assert cp_output.count("run disconnected False False 0") >= 2 + + assert "Advertising successfully started" in peripheral_output + assert peripheral_output.count("Connected") >= 2 diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py new file mode 100644 index 0000000000000..69435d3825624 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_name.py @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE name tests for nrf5340bsim.""" + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_NAME_CODE = """\ +import _bleio + +adapter = _bleio.adapter +adapter.enabled = True +adapter.name = "CPNAME" +print("name", adapter.name) +""" + + +@pytest.mark.circuitpy_drive({"code.py": BSIM_NAME_CODE}) +def test_bsim_set_name(bsim_phy, circuitpython): + """Set the BLE name and read it back on bsim.""" + circuitpython.wait_until_done() + + assert "name CPNAME" in circuitpython.serial.all_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py new file mode 100644 index 0000000000000..7a4bbfaecd942 --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_peripheral.py @@ -0,0 +1,120 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE peripheral connection tests for nrf5340bsim.""" + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_PERIPHERAL_CODE = """\ +import _bleio +import time +import sys + +adapter = _bleio.adapter + +name = b"CPPERIPH" +advertisement = bytes((2, 0x01, 0x06, len(name) + 1, 0x09)) + name + +print("peripheral start") +adapter.start_advertising(advertisement, connectable=True) +print("advertising", adapter.advertising) + +was_connected = False +timeout = time.monotonic() + 8.0 +while not was_connected and time.monotonic() < timeout: + time.sleep(0.01) + was_connected = adapter.connected + +if not was_connected: + print("connect timed out") + sys.exit(-1) + +print("connected", was_connected, "advertising", adapter.advertising) + +if was_connected: + timeout = time.monotonic() + 8.0 + while adapter.connected and time.monotonic() < timeout: + time.sleep(0.1) + +print("disconnected", adapter.connected, len(adapter.connections)) +""" + +BSIM_CENTRAL_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("central start") +target = None +for entry in adapter.start_scan(timeout=6.0, active=True): + if entry.connectable and b"CPPERIPH" in entry.advertisement_bytes: + target = entry.address + print("found peripheral") + break +adapter.stop_scan() +print("have target", target is not None) + +if target is None: + raise RuntimeError("No connectable peripheral found") + +connection = adapter.connect(target, timeout=5.0) +print("connected", connection.connected, adapter.connected, len(adapter.connections)) +connection.disconnect() + +timeout = time.monotonic() + 4.0 +while (connection.connected or adapter.connected) and time.monotonic() < timeout: + time.sleep(0.1) + +print("disconnected", connection.connected, adapter.connected, len(adapter.connections)) +""" + + +@pytest.mark.zephyr_sample("bluetooth/central") +@pytest.mark.duration(14) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CODE}) +def test_bsim_peripheral_zephyr_central(bsim_phy, circuitpython, zephyr_sample): + """Advertise as connectable from CP; Zephyr central connects and disconnects.""" + central = zephyr_sample + + circuitpython.wait_until_done() + + cp_output = circuitpython.serial.all_output + central_output = central.serial.all_output + + assert "peripheral start" in cp_output + assert "advertising True" in cp_output + assert "connected True advertising False" in cp_output + assert "disconnected False 0" in cp_output + + assert "Scanning successfully started" in central_output + assert "Connected:" in central_output + assert "Disconnected:" in central_output + + +@pytest.mark.duration(14) +@pytest.mark.circuitpy_drive({"code.py": BSIM_PERIPHERAL_CODE}) +@pytest.mark.circuitpy_drive({"code.py": BSIM_CENTRAL_CODE}) +def test_bsim_peripheral_cp_central(bsim_phy, circuitpython1, circuitpython2): + """Two CP instances: device 0 peripheral, device 1 central.""" + peripheral = circuitpython1 + central = circuitpython2 + + central.wait_until_done() + peripheral.wait_until_done() + + periph_output = peripheral.serial.all_output + central_output = central.serial.all_output + + assert "peripheral start" in periph_output + assert "advertising True" in periph_output + assert "connected True advertising False" in periph_output + assert "disconnected False 0" in periph_output + + assert "central start" in central_output + assert "found peripheral" in central_output + assert "have target True" in central_output + assert "connected True True 1" in central_output + assert "disconnected False False 0" in central_output diff --git a/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py new file mode 100644 index 0000000000000..19455b7bfa3fc --- /dev/null +++ b/ports/zephyr-cp/tests/bsim/test_bsim_ble_scan.py @@ -0,0 +1,112 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""BLE scanning tests for nrf5340bsim.""" + +import pytest + +pytestmark = pytest.mark.circuitpython_board("native_nrf5340bsim") + +BSIM_SCAN_CODE = """\ +import _bleio + +adapter = _bleio.adapter +print("scan start") +scan = adapter.start_scan(timeout=4.0, active=True) +found = False +for entry in scan: + if b"zephyrproject" in entry.advertisement_bytes: + print("found beacon") + found = True + break +adapter.stop_scan() +print("scan done", found) +""" + +BSIM_SCAN_RELOAD_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("scan run start") +found = False +for entry in adapter.start_scan(active=True): + if b"zephyrproject" in entry.advertisement_bytes: + print("found beacon run") + found = True + break +adapter.stop_scan() +print("scan run done", found) +""" + +BSIM_SCAN_RELOAD_NO_STOP_CODE = """\ +import _bleio +import time + +adapter = _bleio.adapter + +print("scan run start") +found = False +for entry in adapter.start_scan(active=True): + if b"zephyrproject" in entry.advertisement_bytes: + print("found beacon run") + found = True + break +print("scan run done", found) +""" + + +@pytest.mark.zephyr_sample("bluetooth/beacon") +@pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_CODE}) +def test_bsim_scan_zephyr_beacon(bsim_phy, circuitpython, zephyr_sample): + """Scan for Zephyr beacon sample advertisement using bsim.""" + _ = zephyr_sample + + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "scan start" in output + assert "found beacon" in output + assert "scan done True" in output + + +@pytest.mark.zephyr_sample("bluetooth/beacon") +@pytest.mark.code_py_runs(2) +@pytest.mark.duration(4) +@pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_RELOAD_CODE}) +def test_bsim_scan_zephyr_beacon_reload(bsim_phy, circuitpython, zephyr_sample): + """Scan for Zephyr beacon, soft reload, and scan again.""" + _ = zephyr_sample + + circuitpython.serial.wait_for("scan run done") + circuitpython.serial.wait_for("Press any key to enter the REPL") + circuitpython.serial.write("\x04") + + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert output.count("scan run start") >= 2 + assert output.count("found beacon run") >= 2 + assert output.count("scan run done True") >= 2 + + +@pytest.mark.xfail(strict=False, reason="scan without stop_scan may fail on reload") +@pytest.mark.zephyr_sample("bluetooth/beacon") +@pytest.mark.code_py_runs(2) +@pytest.mark.duration(8) +@pytest.mark.circuitpy_drive({"code.py": BSIM_SCAN_RELOAD_NO_STOP_CODE}) +def test_bsim_scan_zephyr_beacon_reload_no_stop(bsim_phy, circuitpython, zephyr_sample): + """Scan for Zephyr beacon without explicit stop, soft reload, and scan again.""" + _ = zephyr_sample + + circuitpython.serial.wait_for("scan run done") + circuitpython.serial.wait_for("Press any key to enter the REPL") + circuitpython.serial.write("\x04") + + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert output.count("scan run start") >= 2 + assert output.count("found beacon run") >= 2 + assert output.count("scan run done True") >= 2 diff --git a/ports/zephyr-cp/tests/conftest.py b/ports/zephyr-cp/tests/conftest.py new file mode 100644 index 0000000000000..03451048324de --- /dev/null +++ b/ports/zephyr-cp/tests/conftest.py @@ -0,0 +1,381 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Pytest fixtures for CircuitPython native_sim testing.""" + +import logging +import os +import subprocess +from pathlib import Path + +import pytest +import serial + +from .perfetto_input_trace import write_input_trace + +from perfetto.trace_processor import TraceProcessor + +from . import NativeSimProcess + +logger = logging.getLogger(__name__) + + +def pytest_addoption(parser): + parser.addoption( + "--update-goldens", + action="store_true", + default=False, + help="Overwrite golden images with captured output instead of comparing.", + ) + + +def pytest_configure(config): + config.addinivalue_line( + "markers", "circuitpy_drive(files): run CircuitPython with files in the flash image" + ) + config.addinivalue_line( + "markers", "disable_i2c_devices(*names): disable native_sim I2C emulator devices" + ) + config.addinivalue_line( + "markers", "circuitpython_board(board_id): which board id to use in the test" + ) + config.addinivalue_line( + "markers", + "zephyr_sample(sample, board='nrf52_bsim', device_id=1): build and run a Zephyr sample for bsim tests", + ) + config.addinivalue_line( + "markers", + "duration(seconds): native_sim timeout and bsim PHY simulation duration", + ) + config.addinivalue_line( + "markers", + "code_py_runs(count): stop native_sim after count code.py runs (default: 1)", + ) + config.addinivalue_line( + "markers", + "input_trace(trace): inject input signal trace data into native_sim", + ) + config.addinivalue_line( + "markers", + "native_sim_rt: run native_sim in realtime mode (-rt instead of -no-rt)", + ) + config.addinivalue_line( + "markers", + "display(capture_times_ns=None): run test with SDL display; " + "capture_times_ns is a list of nanosecond timestamps for trace-triggered captures", + ) + config.addinivalue_line( + "markers", + "display_pixel_format(format): override the display pixel format " + "(e.g. 'RGB_565', 'ARGB_8888')", + ) + config.addinivalue_line( + "markers", + "display_mono_vtiled(value): override the mono vtiled screen_info flag (True or False)", + ) + config.addinivalue_line( + "markers", + "flash_config(erase_block_size=N, total_size=N): override flash simulator parameters", + ) + + +ZEPHYR_CP = Path(__file__).parent.parent +BUILD_DIR = ZEPHYR_CP / "build-native_native_sim" +BINARY = BUILD_DIR / "zephyr-cp/zephyr/zephyr.exe" + + +def _iter_uart_tx_slices(trace_file: Path) -> list[tuple[int, int, str, str]]: + """Return UART TX slices as (timestamp_ns, duration_ns, text, device_name).""" + tp = TraceProcessor(file_path=str(trace_file)) + result = tp.query( + """ + SELECT s.ts, s.dur, s.name, dev.name AS device_name + FROM slice s + JOIN track tx ON s.track_id = tx.id + JOIN track dev ON tx.parent_id = dev.id + JOIN track uart ON dev.parent_id = uart.id + WHERE tx.name = "TX" AND uart.name = "UART" + ORDER BY s.ts + """ + ) + return [ + (int(row.ts), int(row.dur or 0), row.name or "", row.device_name or "UART") + for row in result + ] + + +def log_uart_trace_output(trace_file: Path) -> None: + """Log UART TX output from Perfetto trace with timestamps for line starts.""" + if not logger.isEnabledFor(logging.INFO): + return + slices = _iter_uart_tx_slices(trace_file) + if not slices: + return + + buffers: dict[str, list[str]] = {} + line_start_ts: dict[str, int | None] = {} + + for ts, dur, text, device in slices: + if device not in buffers: + buffers[device] = [] + line_start_ts[device] = None + + if not text: + continue + + char_step = dur / max(len(text), 1) if dur > 0 else 0.0 + for idx, ch in enumerate(text): + if line_start_ts[device] is None: + line_start_ts[device] = int(ts + idx * char_step) + buffers[device].append(ch) + if ch == "\n": + line_text = "".join(buffers[device]).rstrip("\n") + logger.info( + "UART trace %s @%d ns: %s", + device, + line_start_ts[device], + repr(line_text), + ) + buffers[device] = [] + line_start_ts[device] = None + + for device, buf in buffers.items(): + if buf: + logger.info( + "UART trace %s @%d ns (partial): %s", + device, + line_start_ts[device] or 0, + repr("".join(buf)), + ) + + +@pytest.fixture +def board(request): + board = request.node.get_closest_marker("circuitpython_board") + if board is not None: + board = board.args[0] + else: + board = "native_native_sim" + return board + + +@pytest.fixture +def native_sim_binary(request, board): + """Return path to native_sim binary, skip if not built.""" + build_dir = ZEPHYR_CP / f"build-{board}" + binary = build_dir / "zephyr-cp/zephyr/zephyr.exe" + + if not binary.exists(): + pytest.skip(f"binary not built: {binary}") + return binary + + +@pytest.fixture +def native_sim_env() -> dict[str, str]: + return {} + + +PIXEL_FORMAT_BITMASK = { + "RGB_888": 1 << 0, + "MONO01": 1 << 1, + "MONO10": 1 << 2, + "ARGB_8888": 1 << 3, + "RGB_565": 1 << 4, + "BGR_565": 1 << 5, + "L_8": 1 << 6, + "AL_88": 1 << 7, +} + + +@pytest.fixture +def pixel_format(request) -> str: + """Indirect-parametrize fixture: adds display_pixel_format marker.""" + fmt = request.param + request.node.add_marker(pytest.mark.display_pixel_format(fmt)) + return fmt + + +@pytest.fixture +def sim_id(request) -> str: + return request.node.nodeid.replace("/", "_") + + +@pytest.fixture +def circuitpython(request, board, sim_id, native_sim_binary, native_sim_env, tmp_path): + """Run CircuitPython with given code string and return PTY output.""" + + instance_count = 1 + if "circuitpython1" in request.fixturenames and "circuitpython2" in request.fixturenames: + instance_count = 2 + + drives = list(request.node.iter_markers_with_node("circuitpy_drive")) + if len(drives) != instance_count: + raise RuntimeError(f"not enough drives for {instance_count} instances") + + input_trace_markers = list(request.node.iter_markers_with_node("input_trace")) + if len(input_trace_markers) > 1: + raise RuntimeError("expected at most one input_trace marker") + + input_trace = None + if input_trace_markers and len(input_trace_markers[0][1].args) == 1: + input_trace = input_trace_markers[0][1].args[0] + + input_trace_file = None + if input_trace is not None: + input_trace_file = tmp_path / "input.perfetto" + write_input_trace(input_trace_file, input_trace) + + marker = request.node.get_closest_marker("duration") + if marker is None: + timeout = 10 + else: + timeout = marker.args[0] + + runs_marker = request.node.get_closest_marker("code_py_runs") + if runs_marker is None: + code_py_runs = 1 + else: + code_py_runs = int(runs_marker.args[0]) + + display_marker = request.node.get_closest_marker("display") + if display_marker is None: + display_marker = request.node.get_closest_marker("display_capture") + + capture_times_ns = None + if display_marker is not None: + capture_times_ns = display_marker.kwargs.get("capture_times_ns", None) + + pixel_format_marker = request.node.get_closest_marker("display_pixel_format") + pixel_format = None + if pixel_format_marker is not None and pixel_format_marker.args: + pixel_format = pixel_format_marker.args[0] + + mono_vtiled_marker = request.node.get_closest_marker("display_mono_vtiled") + mono_vtiled = None + if mono_vtiled_marker is not None and mono_vtiled_marker.args: + mono_vtiled = mono_vtiled_marker.args[0] + + # If capture_times_ns is set, merge display_capture track into input trace. + if capture_times_ns is not None: + if input_trace is None: + input_trace = {} + else: + input_trace = dict(input_trace) + input_trace["display_capture"] = list(capture_times_ns) + if input_trace_file is None: + input_trace_file = tmp_path / "input.perfetto" + write_input_trace(input_trace_file, input_trace) + + use_realtime = request.node.get_closest_marker("native_sim_rt") is not None + + flash_config_marker = request.node.get_closest_marker("flash_config") + flash_total_size = 2 * 1024 * 1024 # default 2MB + flash_erase_block_size = None + flash_write_block_size = None + if flash_config_marker: + flash_total_size = flash_config_marker.kwargs.get("total_size", flash_total_size) + flash_erase_block_size = flash_config_marker.kwargs.get("erase_block_size", None) + flash_write_block_size = flash_config_marker.kwargs.get("write_block_size", None) + + procs = [] + for i in range(instance_count): + flash = tmp_path / f"flash-{i}.bin" + flash.write_bytes(b"\xff" * flash_total_size) + files = None + if len(drives[i][1].args) == 1: + files = drives[i][1].args[0] + if files is not None: + subprocess.run(["mformat", "-i", str(flash), "::"], check=True) + tmp_drive = tmp_path / f"drive{i}" + tmp_drive.mkdir(exist_ok=True) + + for name, content in files.items(): + src = tmp_drive / name + if isinstance(content, bytes): + src.write_bytes(content) + else: + src.write_text(content) + subprocess.run(["mcopy", "-i", str(flash), str(src), f"::{name}"], check=True) + + trace_file = tmp_path / f"trace-{i}.perfetto" + + if "bsim" in board: + cmd = [str(native_sim_binary), f"--flash_app={flash}"] + if instance_count > 1: + cmd.append("-disconnect_on_exit=1") + cmd.extend( + ( + f"-s={sim_id}", + f"-d={i}", + "-uart0_pty", + "-uart0_pty_wait_for_readers", + "-uart_pty_wait", + f"--vm-runs={code_py_runs + 1}", + ) + ) + else: + cmd = [str(native_sim_binary), f"--flash={flash}"] + # native_sim vm-runs includes the boot VM setup run. + realtime_flag = "-rt" if use_realtime else "-no-rt" + cmd.extend( + ( + realtime_flag, + "-display_headless", + "-i2s_earless", + "-wait_uart", + f"--vm-runs={code_py_runs + 1}", + ) + ) + + if flash_erase_block_size is not None: + cmd.append(f"--flash_erase_block_size={flash_erase_block_size}") + if flash_write_block_size is not None: + cmd.append(f"--flash_write_block_size={flash_write_block_size}") + if flash_config_marker and "total_size" in flash_config_marker.kwargs: + cmd.append(f"--flash_total_size={flash_total_size}") + + if input_trace_file is not None: + cmd.append(f"--input-trace={input_trace_file}") + + marker = request.node.get_closest_marker("disable_i2c_devices") + if marker and len(marker.args) > 0: + for device in marker.args: + cmd.append(f"--disable-i2c={device}") + + if pixel_format is not None: + cmd.append(f"--display_pixel_format={PIXEL_FORMAT_BITMASK[pixel_format]}") + + if mono_vtiled is not None: + cmd.append(f"--display_mono_vtiled={'true' if mono_vtiled else 'false'}") + + env = os.environ.copy() + env.update(native_sim_env) + + capture_png_pattern = None + if capture_times_ns is not None: + if instance_count == 1: + capture_png_pattern = str(tmp_path / "frame_%d.png") + else: + capture_png_pattern = str(tmp_path / f"frame-{i}_%d.png") + cmd.append(f"--display_capture_png={capture_png_pattern}") + + logger.info("Running: %s", " ".join(cmd)) + proc = NativeSimProcess(cmd, timeout, trace_file, env, flash_file=flash) + proc.display_dump = None + proc._capture_png_pattern = capture_png_pattern + proc._capture_count = len(capture_times_ns) if capture_times_ns is not None else 0 + procs.append(proc) + if instance_count == 1: + yield procs[0] + else: + yield procs + for i, proc in enumerate(procs): + if instance_count > 1: + print(f"---------- Instance {i} -----------") + proc.shutdown() + + print("All serial output:") + print(proc.serial.all_output) + print() + print("All debug serial output:") + print(proc.debug_serial.all_output) diff --git a/ports/zephyr-cp/tests/docs/babblesim.md b/ports/zephyr-cp/tests/docs/babblesim.md new file mode 100644 index 0000000000000..75d45079b2e68 --- /dev/null +++ b/ports/zephyr-cp/tests/docs/babblesim.md @@ -0,0 +1,81 @@ +# BabbleSim testing + +This document describes how to build and run CircuitPython tests against the +BabbleSim (bsim) nRF5340 board. + +## Board target + +We use the Zephyr BabbleSim board for the nRF5340 application core: + +- Zephyr board: `nrf5340bsim/nrf5340/cpuapp` +- CircuitPython board alias: `native_nrf5340bsim` + +The tests expect two bsim instances to run in the same simulation, which allows +future BLE/802.15.4 multi-node tests. + +## Prerequisites + +BabbleSim needs to be available to Zephyr. Either: + +- Use the repo-provided `tools/bsim` checkout (if present) +- Or set environment variables: + +``` +export BSIM_COMPONENTS_PATH=/path/to/bsim/components +export BSIM_OUT_PATH=/path/to/bsim +``` + +## Build + +``` +CCACHE_TEMPDIR=/tmp/ccache-tmp make -j BOARD=native_nrf5340bsim +``` + +If you do not use ccache, you can omit `CCACHE_TEMPDIR`. + +## Run the bsim test + +``` +pytest tests/test_bsim_basics.py -v +``` + +## BLE scan + advertising tests + +The BLE tests run multiple bsim instances and build Zephyr samples on-demand: + +- `tests/test_bsim_ble_scan.py` scans for the Zephyr beacon sample +- `tests/test_bsim_ble_advertising.py` advertises from CircuitPython while the + Zephyr observer sample scans + +The fixtures build the Zephyr samples if missing: + +- Beacon: `zephyr/samples/bluetooth/beacon` (board `nrf52_bsim`) +- Observer: `zephyr/samples/bluetooth/observer` (board `nrf52_bsim`) + +Run the tests with: + +``` +pytest tests/test_bsim_ble_scan.py -v +pytest tests/test_bsim_ble_advertising.py -v +``` + +## Pytest markers + +For bsim-specific test tuning: + +- `@pytest.mark.duration(seconds)` controls simulation runtime/timeout. + +Example: + +```py +pytestmark = pytest.mark.duration(30.0) +``` + +## Notes + +- The bsim test spawns two instances that share a sim id. It only checks UART + output for now, but is the base for BLE/Thread multi-node tests. +- The BLE tests rely on the sysbuild HCI IPC net-core image for the nRF5340 + simulator (enabled via `sysbuild.conf`). +- The board uses a custom devicetree overlay to provide the SRAM region and + CircuitPython flash partition expected by the port. diff --git a/ports/zephyr-cp/tests/docs/i2c_emulator_cmdline_control.md b/ports/zephyr-cp/tests/docs/i2c_emulator_cmdline_control.md new file mode 100644 index 0000000000000..8ee2925915c83 --- /dev/null +++ b/ports/zephyr-cp/tests/docs/i2c_emulator_cmdline_control.md @@ -0,0 +1,306 @@ +# Command-Line Control of Emulated I2C Devices in native_sim + +This document describes an approach for enabling/disabling emulated I2C devices +at runtime via command-line options in Zephyr's native_sim environment. + +## Background + +Zephyr's I2C emulation framework (`zephyr,i2c-emul-controller`) provides: + +1. **Bus emulation** - Fake I2C controller that routes transfers to emulated devices +2. **Device emulators** - Software implementations of I2C peripherals (sensors, etc.) +3. **Backend APIs** - Test interfaces for manipulating emulator state + +However, there's no built-in mechanism to enable/disable emulated devices from the +command line. This capability would be useful for: + +- Testing device hot-plug scenarios +- Simulating hardware failures +- Testing error handling paths in CircuitPython + +## Relevant Zephyr APIs + +### I2C Emulator Structure + +From `include/zephyr/drivers/i2c_emul.h`: + +```c +struct i2c_emul { + sys_snode_t node; + const struct emul *target; + const struct i2c_emul_api *api; + struct i2c_emul_api *mock_api; // If non-NULL, takes precedence + uint16_t addr; +}; + +struct i2c_emul_api { + i2c_emul_transfer_t transfer; +}; +``` + +Key insight: The `mock_api` field allows overriding the normal transfer function. +If `mock_api->transfer()` returns `-ENOSYS`, it falls back to the real API. + +### Command-Line Registration + +From `boards/native/native_sim/cmdline.h`: + +```c +void native_add_command_line_opts(struct args_struct_t *args); +``` + +### Emulator Lookup + +From `include/zephyr/drivers/emul.h`: + +```c +const struct emul *emul_get_binding(const char *name); +``` + +## Implementation Approach + +### 1. Create a Disabled Device Registry + +Track which devices are "disabled" (should NACK all transactions): + +```c +// i2c_emul_cmdline.c + +#include +#include +#include +#include "nsi_cmdline.h" + +#define MAX_DISABLED_DEVICES 16 + +static struct { + const char *name; + const struct emul *emul; + struct i2c_emul_api mock_api; + struct i2c_emul_api *original_mock_api; + bool disabled; +} disabled_devices[MAX_DISABLED_DEVICES]; + +static int num_disabled_devices = 0; +``` + +### 2. Mock Transfer Function + +Return `-EIO` (simulates NACK) when device is disabled: + +```c +static int disabled_device_transfer(const struct emul *target, + struct i2c_msg *msgs, + int num_msgs, + int addr) +{ + // Find this device in our registry + for (int i = 0; i < num_disabled_devices; i++) { + if (disabled_devices[i].emul == target) { + if (disabled_devices[i].disabled) { + // Device is disabled - simulate NACK + return -EIO; + } + break; + } + } + // Fall back to normal emulator behavior + return -ENOSYS; +} +``` + +### 3. Enable/Disable Functions + +```c +int i2c_emul_cmdline_disable_device(const char *name) +{ + const struct emul *emul = emul_get_binding(name); + if (!emul || emul->bus_type != EMUL_BUS_TYPE_I2C) { + return -ENODEV; + } + + // Find or create registry entry + int idx = -1; + for (int i = 0; i < num_disabled_devices; i++) { + if (disabled_devices[i].emul == emul) { + idx = i; + break; + } + } + + if (idx < 0) { + if (num_disabled_devices >= MAX_DISABLED_DEVICES) { + return -ENOMEM; + } + idx = num_disabled_devices++; + disabled_devices[idx].name = name; + disabled_devices[idx].emul = emul; + disabled_devices[idx].mock_api.transfer = disabled_device_transfer; + + // Save and replace mock_api + disabled_devices[idx].original_mock_api = emul->bus.i2c->mock_api; + emul->bus.i2c->mock_api = &disabled_devices[idx].mock_api; + } + + disabled_devices[idx].disabled = true; + return 0; +} + +int i2c_emul_cmdline_enable_device(const char *name) +{ + for (int i = 0; i < num_disabled_devices; i++) { + if (strcmp(disabled_devices[i].name, name) == 0) { + disabled_devices[i].disabled = false; + return 0; + } + } + return -ENODEV; +} +``` + +### 4. Command-Line Option Registration + +```c +static char *disabled_device_args[MAX_DISABLED_DEVICES]; +static int num_disabled_device_args = 0; + +static void cmd_disable_i2c_device(char *argv, int offset) +{ + ARG_UNUSED(offset); + if (num_disabled_device_args < MAX_DISABLED_DEVICES) { + disabled_device_args[num_disabled_device_args++] = argv; + } +} + +static struct args_struct_t i2c_emul_args[] = { + { + .option = "disable-i2c", + .name = "device_name", + .type = 's', + .call_when_found = cmd_disable_i2c_device, + .descript = "Disable an emulated I2C device (can be repeated)" + }, + ARG_TABLE_ENDMARKER +}; + +static void register_cmdline_opts(void) +{ + native_add_command_line_opts(i2c_emul_args); +} + +// Hook into native_sim initialization +NATIVE_TASK(register_cmdline_opts, PRE_BOOT_1, 0); + +static void apply_disabled_devices(void) +{ + for (int i = 0; i < num_disabled_device_args; i++) { + int rc = i2c_emul_cmdline_disable_device(disabled_device_args[i]); + if (rc != 0) { + printk("Warning: Failed to disable I2C device '%s': %d\n", + disabled_device_args[i], rc); + } + } +} + +// Apply after emulators are initialized +NATIVE_TASK(apply_disabled_devices, PRE_BOOT_3, 0); +``` + +### 5. Usage + +After building with this code: + +```bash +# Disable a sensor at startup +./build/zephyr/zephyr.exe --disable-i2c=bmi160@68 + +# Disable multiple devices +./build/zephyr/zephyr.exe --disable-i2c=bmi160@68 --disable-i2c=sht4x@44 +``` + +## Runtime Control Extension + +For runtime enable/disable (not just at startup), you could: + +### Option A: Use a Named Pipe / FIFO + +```c +// Create a FIFO that accepts commands +// "disable bmi160@68" or "enable bmi160@68" +static void *cmdline_control_thread(void *arg) +{ + int fd = open("/tmp/i2c_emul_control", O_RDONLY); + char buf[256]; + while (read(fd, buf, sizeof(buf)) > 0) { + if (strncmp(buf, "disable ", 8) == 0) { + i2c_emul_cmdline_disable_device(buf + 8); + } else if (strncmp(buf, "enable ", 7) == 0) { + i2c_emul_cmdline_enable_device(buf + 7); + } + } + return NULL; +} +``` + +### Option B: Signal Handler + +Use `SIGUSR1`/`SIGUSR2` with a config file that specifies which device to toggle. + +### Option C: Shared Memory + +Map a shared memory region that external tools can write to for device state. + +## Integration with CircuitPython Tests + +For pytest-based tests, you could: + +```python +import subprocess +import os + +class NativeSimProcess: + def __init__(self, exe_path): + self.exe_path = exe_path + self.control_fifo = "/tmp/i2c_emul_control" + + def start(self, disabled_devices=None): + args = [self.exe_path] + if disabled_devices: + for dev in disabled_devices: + args.extend(["--disable-i2c", dev]) + self.proc = subprocess.Popen(args, ...) + + def disable_device(self, name): + """Runtime disable via FIFO""" + with open(self.control_fifo, 'w') as f: + f.write(f"disable {name}\n") + + def enable_device(self, name): + """Runtime enable via FIFO""" + with open(self.control_fifo, 'w') as f: + f.write(f"enable {name}\n") +``` + +## Alternative: Device Tree Approach + +For compile-time configuration, use device tree overlays: + +```dts +// boards/native_sim_no_bmi160.overlay +&bmi160 { + status = "disabled"; +}; +``` + +Build separate variants: +```bash +west build -b native_sim -- -DDTC_OVERLAY_FILE=boards/native_sim_no_bmi160.overlay +``` + +## References + +- Zephyr I2C Emulation: `zephyr/drivers/i2c/i2c_emul.c` +- Emulator Framework: `zephyr/doc/hardware/emulator/bus_emulators.rst` +- Native Sim Docs: `zephyr/boards/native/native_sim/doc/index.rst` +- Command-line handling: `zephyr/boards/native/native_sim/cmdline.c` +- Example mock API usage: `zephyr/tests/drivers/sensor/bmi160/src/i2c.c` diff --git a/ports/zephyr-cp/tests/docs/web_workflow.md b/ports/zephyr-cp/tests/docs/web_workflow.md new file mode 100644 index 0000000000000..eb6742b8a3ec6 --- /dev/null +++ b/ports/zephyr-cp/tests/docs/web_workflow.md @@ -0,0 +1,37 @@ +# Web Workflow native_sim Tests + +These tests validate CircuitPython's web workflow support in the Zephyr native_sim port, including filesystem write behavior with and without USB-style write protection. + +## Coverage + +- `test_web_workflow_hostnetwork`: Verifies the web workflow HTTP server responds and enforces authentication (`/edit/` returns `401 Unauthorized`). +- `test_web_workflow_write_code_py_conflict`: Exercises a write attempt while the filesystem is protected (no `boot.py` remount). The DELETE request should return `409 Conflict`. +- `test_web_workflow_write_code_py_remount`: Uses a `boot.py` remount to allow CircuitPython to write. A PUT request updates `code.py`, and a subsequent GET verifies the contents. + +## Filesystem Setup + +The tests create a flash image with: + +- `settings.toml` containing `CIRCUITPY_WEB_API_PASSWORD="testpass"` so the web workflow starts using the on-device settings file. +- `boot.py` (for the remount test only) with: + ```python + import storage + storage.remount("/", readonly=False) + ``` + This disables concurrent write protection so the web workflow can write to CIRCUITPY. + +## Running the Tests + +Build native_sim (if needed): + +```bash +make BOARD=native_native_sim +``` + +Run the tests: + +```bash +pytest -q ports/zephyr-cp/tests/test_web_workflow.py::test_web_workflow_hostnetwork -vv +pytest -q ports/zephyr-cp/tests/test_web_workflow.py::test_web_workflow_write_code_py_conflict -vv +pytest -q ports/zephyr-cp/tests/test_web_workflow.py::test_web_workflow_write_code_py_remount -vv +``` diff --git a/ports/zephyr-cp/tests/perfetto_input_trace.py b/ports/zephyr-cp/tests/perfetto_input_trace.py new file mode 100644 index 0000000000000..494c9cdcadf69 --- /dev/null +++ b/ports/zephyr-cp/tests/perfetto_input_trace.py @@ -0,0 +1,159 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries LLC +# SPDX-License-Identifier: MIT + +"""Utilities for creating Perfetto input trace files for native_sim tests. + +This module can be used directly from Python or from the command line: + + python -m tests.perfetto_input_trace input_trace.json output.perfetto + +Input JSON format — counter tracks use [timestamp, value] pairs, instant +tracks use bare timestamps: + +{ + "gpio_emul.01": [[8000000000, 0], [9000000000, 1], [10000000000, 0]], + "display_capture": [500000000, 1000000000] +} +""" + +from __future__ import annotations + +import argparse +import json +from pathlib import Path +from typing import Mapping, Sequence + +# Counter tracks: list of (timestamp, value) pairs. +# Instant tracks: list of timestamps (bare ints). +InputTraceData = Mapping[str, Sequence[tuple[int, int] | int]] + + +def _load_perfetto_pb2(): + from perfetto.protos.perfetto.trace import perfetto_trace_pb2 as perfetto_pb2 + + return perfetto_pb2 + + +def _is_instant_track(events: Sequence) -> bool: + """Return True if *events* is a list of bare timestamps (instant track).""" + if not events: + return False + return isinstance(events[0], int) + + +def build_input_trace(trace_data: InputTraceData, *, sequence_id: int = 1): + """Build a Perfetto Trace protobuf for input replay counter and instant tracks.""" + perfetto_pb2 = _load_perfetto_pb2() + trace = perfetto_pb2.Trace() + + seq_incremental_state_cleared = 1 + seq_needs_incremental_state = 2 + + for idx, (track_name, events) in enumerate(trace_data.items()): + track_uuid = 1001 + idx + instant = _is_instant_track(events) + + desc_packet = trace.packet.add() + desc_packet.timestamp = 0 + desc_packet.trusted_packet_sequence_id = sequence_id + if idx == 0: + desc_packet.sequence_flags = seq_incremental_state_cleared + desc_packet.track_descriptor.uuid = track_uuid + desc_packet.track_descriptor.name = track_name + if not instant: + desc_packet.track_descriptor.counter.unit = ( + perfetto_pb2.CounterDescriptor.Unit.UNIT_COUNT + ) + + if instant: + for ts in events: + event_packet = trace.packet.add() + event_packet.timestamp = ts + event_packet.trusted_packet_sequence_id = sequence_id + event_packet.sequence_flags = seq_needs_incremental_state + event_packet.track_event.type = perfetto_pb2.TrackEvent.Type.TYPE_INSTANT + event_packet.track_event.track_uuid = track_uuid + else: + for ts, value in events: + event_packet = trace.packet.add() + event_packet.timestamp = ts + event_packet.trusted_packet_sequence_id = sequence_id + event_packet.sequence_flags = seq_needs_incremental_state + event_packet.track_event.type = perfetto_pb2.TrackEvent.Type.TYPE_COUNTER + event_packet.track_event.track_uuid = track_uuid + event_packet.track_event.counter_value = value + + return trace + + +def write_input_trace( + trace_file: Path, trace_data: InputTraceData, *, sequence_id: int = 1 +) -> None: + """Write input replay data to a Perfetto trace file.""" + trace = build_input_trace(trace_data, sequence_id=sequence_id) + trace_file.parent.mkdir(parents=True, exist_ok=True) + trace_file.write_bytes(trace.SerializeToString()) + + +def _parse_trace_json(data: object) -> dict[str, list[tuple[int, int]] | list[int]]: + if not isinstance(data, dict): + raise ValueError("top-level JSON value must be an object") + + parsed: dict[str, list[tuple[int, int]] | list[int]] = {} + for track_name, events in data.items(): + if not isinstance(track_name, str): + raise ValueError("track names must be strings") + if not isinstance(events, list): + raise ValueError(f"track {track_name!r} must map to a list of events") + + if not events: + parsed[track_name] = [] + continue + + # Distinguish instant (bare ints) vs counter ([ts, value] pairs). + if isinstance(events[0], (int, float)): + parsed[track_name] = [int(ts) for ts in events] + else: + parsed_events: list[tuple[int, int]] = [] + for event in events: + if not isinstance(event, (list, tuple)) or len(event) != 2: + raise ValueError( + f"track {track_name!r} events must be [timestamp, value] pairs " + "or bare timestamps" + ) + timestamp_ns, value = event + parsed_events.append((int(timestamp_ns), int(value))) + parsed[track_name] = parsed_events + + return parsed + + +def _build_arg_parser() -> argparse.ArgumentParser: + parser = argparse.ArgumentParser( + description="Generate a Perfetto input trace file used by native_sim --input-trace" + ) + parser.add_argument("input_json", type=Path, help="Path to input trace JSON") + parser.add_argument("output_trace", type=Path, help="Output .perfetto file path") + parser.add_argument( + "--sequence-id", + type=int, + default=1, + help="trusted_packet_sequence_id to use (default: 1)", + ) + return parser + + +def main(argv: Sequence[str] | None = None) -> int: + parser = _build_arg_parser() + args = parser.parse_args(argv) + + trace_json = json.loads(args.input_json.read_text()) + trace_data = _parse_trace_json(trace_json) + write_input_trace(args.output_trace, trace_data, sequence_id=args.sequence_id) + + print(f"Wrote {args.output_trace} ({len(trace_data)} tracks)") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/ports/zephyr-cp/tests/test_adafruit_bus_device.py b/ports/zephyr-cp/tests/test_adafruit_bus_device.py new file mode 100644 index 0000000000000..3022838506e77 --- /dev/null +++ b/ports/zephyr-cp/tests/test_adafruit_bus_device.py @@ -0,0 +1,236 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test adafruit_bus_device.i2c_device.I2CDevice against the AT24 EEPROM emulator.""" + +import pytest + + +PROBE_OK_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) +print(f"device created: {type(device).__name__}") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": PROBE_OK_CODE}) +def test_i2cdevice_probe_success(circuitpython): + """Constructing I2CDevice with probe=True succeeds when AT24 is present at 0x50.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "device created: I2CDevice" in output + assert "done" in output + + +PROBE_FAIL_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +try: + device = I2CDevice(i2c, 0x51) + print("unexpected_success") +except ValueError as e: + print(f"probe_failed: {e}") +except OSError as e: + print(f"probe_failed: {e}") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": PROBE_FAIL_CODE}) +def test_i2cdevice_probe_failure(circuitpython): + """Constructing I2CDevice with probe=True raises when no device responds.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "probe_failed" in output + assert "unexpected_success" not in output + assert "done" in output + + +PROBE_DISABLED_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +# probe=False should not raise even without a device at this address +device = I2CDevice(i2c, 0x51, probe=False) +print(f"device created: {type(device).__name__}") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": PROBE_DISABLED_CODE}) +def test_i2cdevice_probe_disabled(circuitpython): + """Constructing I2CDevice with probe=False succeeds regardless of device presence.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "device created: I2CDevice" in output + assert "done" in output + + +READ_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) + +# Read first byte by writing the memory address, then reading. +buf = bytearray(1) +with device: + device.write(bytes([0x00])) + device.readinto(buf) +print(f"AT24 byte 0: 0x{buf[0]:02X}") +if buf[0] == 0xFF: + print("eeprom_valid") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": READ_CODE}) +def test_i2cdevice_write_then_readinto_separate(circuitpython): + """write() followed by readinto() inside a single context manager block reads EEPROM data.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "AT24 byte 0: 0xFF" in output + assert "eeprom_valid" in output + assert "done" in output + + +WRITE_THEN_READINTO_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) + +out_buf = bytes([0x00]) +in_buf = bytearray(4) +with device: + device.write_then_readinto(out_buf, in_buf) +print(f"first 4 bytes: {[hex(b) for b in in_buf]}") +if all(b == 0xFF for b in in_buf): + print("eeprom_valid") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_THEN_READINTO_CODE}) +def test_i2cdevice_write_then_readinto_atomic(circuitpython): + """write_then_readinto() performs an atomic write+read against the EEPROM.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "first 4 bytes:" in output + assert "eeprom_valid" in output + assert "done" in output + + +WRITE_READBACK_CODE = """\ +import board +import time +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) + +# Write four bytes starting at EEPROM address 0x10. +payload = bytes([0xDE, 0xAD, 0xBE, 0xEF]) +with device: + device.write(bytes([0x10]) + payload) + +# EEPROM needs a moment to commit the write internally. +time.sleep(0.01) + +readback = bytearray(4) +with device: + device.write_then_readinto(bytes([0x10]), readback) +print(f"readback: {[hex(b) for b in readback]}") +if bytes(readback) == payload: + print("readback_ok") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_READBACK_CODE}) +def test_i2cdevice_write_then_read_roundtrip(circuitpython): + """Writing bytes to the EEPROM and reading them back returns the written values.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "readback_ok" in output + assert "done" in output + + +SLICE_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +device = I2CDevice(i2c, 0x50) + +# Use start/end kwargs to send only a slice of the outgoing buffer. +out = bytearray([0xAA, 0x00, 0xBB]) +dest = bytearray(4) +with device: + # Only send the middle byte (the memory address 0x00). + device.write_then_readinto(out, dest, out_start=1, out_end=2, in_start=0, in_end=2) +print(f"partial read: {[hex(b) for b in dest]}") +# Only the first two entries should have been written by the read. +if dest[0] == 0xFF and dest[1] == 0xFF and dest[2] == 0x00 and dest[3] == 0x00: + print("slice_ok") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": SLICE_CODE}) +def test_i2cdevice_buffer_slices(circuitpython): + """write_then_readinto honors out_start/out_end and in_start/in_end bounds.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "slice_ok" in output + assert "done" in output + + +DISABLED_CODE = """\ +import board +from adafruit_bus_device.i2c_device import I2CDevice + +i2c = board.I2C() +try: + device = I2CDevice(i2c, 0x50) + print("unexpected_success") +except (ValueError, OSError) as e: + print(f"probe_failed: {e}") +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": DISABLED_CODE}) +@pytest.mark.disable_i2c_devices("eeprom@50") +def test_i2cdevice_probe_fails_when_device_disabled(circuitpython): + """Probe fails when the AT24 emulator device is disabled on the bus.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "probe_failed" in output + assert "unexpected_success" not in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/test_aesio.py b/ports/zephyr-cp/tests/test_aesio.py new file mode 100644 index 0000000000000..3ae016ac8e346 --- /dev/null +++ b/ports/zephyr-cp/tests/test_aesio.py @@ -0,0 +1,223 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test the aesio module.""" + +import pytest + +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + + +KEY = b"Sixteen byte key" +PLAINTEXT = b"CircuitPython!!!" # 16 bytes + + +ECB_CODE = """\ +import aesio + +key = b'Sixteen byte key' +inp = b'CircuitPython!!!' +outp = bytearray(len(inp)) +cipher = aesio.AES(key, aesio.MODE_ECB) +cipher.encrypt_into(inp, outp) +print(f"ciphertext_hex: {outp.hex()}") + +decrypted = bytearray(len(outp)) +cipher.decrypt_into(bytes(outp), decrypted) +print(f"decrypted: {bytes(decrypted)}") +print(f"match: {bytes(decrypted) == inp}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": ECB_CODE}) +def test_aesio_ecb(circuitpython): + """AES-ECB round-trips and matches CPython cryptography's output.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + encryptor = Cipher(algorithms.AES(KEY), modes.ECB()).encryptor() + expected_hex = (encryptor.update(PLAINTEXT) + encryptor.finalize()).hex() + assert f"ciphertext_hex: {expected_hex}" in output + assert "match: True" in output + assert "done" in output + + +IV = b"InitializationVe" # 16 bytes +CBC_PLAINTEXT = b"CircuitPython!!!" * 2 # 32 bytes, multiple of 16 +CTR_PLAINTEXT = b"CircuitPython is fun to use!" # 28 bytes, arbitrary length + + +CBC_CODE = """\ +import aesio + +key = b'Sixteen byte key' +iv = b'InitializationVe' +inp = b'CircuitPython!!!' * 2 +outp = bytearray(len(inp)) +cipher = aesio.AES(key, aesio.MODE_CBC, iv) +print(f"mode: {cipher.mode}") +cipher.encrypt_into(inp, outp) +print(f"ciphertext_hex: {outp.hex()}") + +# Re-create cipher to reset IV state for decryption. +cipher2 = aesio.AES(key, aesio.MODE_CBC, iv) +decrypted = bytearray(len(outp)) +cipher2.decrypt_into(bytes(outp), decrypted) +print(f"match: {bytes(decrypted) == inp}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": CBC_CODE}) +def test_aesio_cbc(circuitpython): + """AES-CBC round-trips and matches CPython cryptography's output.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + encryptor = Cipher(algorithms.AES(KEY), modes.CBC(IV)).encryptor() + expected_hex = (encryptor.update(CBC_PLAINTEXT) + encryptor.finalize()).hex() + assert "mode: 2" in output + assert f"ciphertext_hex: {expected_hex}" in output + assert "match: True" in output + assert "done" in output + + +CTR_CODE = """\ +import aesio + +key = b'Sixteen byte key' +iv = b'InitializationVe' +inp = b'CircuitPython is fun to use!' +outp = bytearray(len(inp)) +cipher = aesio.AES(key, aesio.MODE_CTR, iv) +print(f"mode: {cipher.mode}") +cipher.encrypt_into(inp, outp) +print(f"ciphertext_hex: {outp.hex()}") + +cipher2 = aesio.AES(key, aesio.MODE_CTR, iv) +decrypted = bytearray(len(outp)) +cipher2.decrypt_into(bytes(outp), decrypted) +print(f"decrypted: {bytes(decrypted)}") +print(f"match: {bytes(decrypted) == inp}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": CTR_CODE}) +def test_aesio_ctr(circuitpython): + """AES-CTR handles arbitrary-length buffers and matches CPython output.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + encryptor = Cipher(algorithms.AES(KEY), modes.CTR(IV)).encryptor() + expected_hex = (encryptor.update(CTR_PLAINTEXT) + encryptor.finalize()).hex() + assert "mode: 6" in output + assert f"ciphertext_hex: {expected_hex}" in output + assert "match: True" in output + assert "done" in output + + +REKEY_CODE = """\ +import aesio + +key1 = b'Sixteen byte key' +key2 = b'Another 16 byte!' +inp = b'CircuitPython!!!' + +cipher = aesio.AES(key1, aesio.MODE_ECB) +out1 = bytearray(16) +cipher.encrypt_into(inp, out1) +print(f"ct1_hex: {out1.hex()}") + +cipher.rekey(key2) +out2 = bytearray(16) +cipher.encrypt_into(inp, out2) +print(f"ct2_hex: {out2.hex()}") +print(f"different: {bytes(out1) != bytes(out2)}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": REKEY_CODE}) +def test_aesio_rekey(circuitpython): + """rekey() switches the active key; ciphertexts match CPython for both keys.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + enc1 = Cipher(algorithms.AES(b"Sixteen byte key"), modes.ECB()).encryptor() + ct1 = (enc1.update(PLAINTEXT) + enc1.finalize()).hex() + enc2 = Cipher(algorithms.AES(b"Another 16 byte!"), modes.ECB()).encryptor() + ct2 = (enc2.update(PLAINTEXT) + enc2.finalize()).hex() + assert f"ct1_hex: {ct1}" in output + assert f"ct2_hex: {ct2}" in output + assert "different: True" in output + assert "done" in output + + +MODE_PROPERTY_CODE = """\ +import aesio + +key = b'Sixteen byte key' +iv = b'InitializationVe' +cipher = aesio.AES(key, aesio.MODE_ECB) +print(f"initial: {cipher.mode}") +print(f"ECB={aesio.MODE_ECB} CBC={aesio.MODE_CBC} CTR={aesio.MODE_CTR}") + +for name, m in (("ECB", aesio.MODE_ECB), ("CBC", aesio.MODE_CBC), ("CTR", aesio.MODE_CTR)): + cipher.mode = m + print(f"set_{name}: {cipher.mode}") + +try: + cipher.mode = 99 +except NotImplementedError as e: + print(f"bad_mode: NotImplementedError") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": MODE_PROPERTY_CODE}) +def test_aesio_mode_property(circuitpython): + """The mode property is readable, writable, and rejects unsupported values.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "initial: 1" in output + assert "ECB=1 CBC=2 CTR=6" in output + assert "set_ECB: 1" in output + assert "set_CBC: 2" in output + assert "set_CTR: 6" in output + assert "bad_mode: NotImplementedError" in output + assert "done" in output + + +KEY_LENGTHS_CODE = """\ +import aesio + +inp = b'CircuitPython!!!' +for key in (b'A' * 16, b'B' * 24, b'C' * 32): + cipher = aesio.AES(key, aesio.MODE_ECB) + out = bytearray(16) + cipher.encrypt_into(inp, out) + print(f"len{len(key)}: {out.hex()}") + +try: + aesio.AES(b'too short', aesio.MODE_ECB) +except ValueError: + print("bad_key: ValueError") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": KEY_LENGTHS_CODE}) +def test_aesio_key_lengths(circuitpython): + """AES-128/192/256 keys all work and match CPython; bad key length raises.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + for key in (b"A" * 16, b"B" * 24, b"C" * 32): + encryptor = Cipher(algorithms.AES(key), modes.ECB()).encryptor() + expected = (encryptor.update(PLAINTEXT) + encryptor.finalize()).hex() + assert f"len{len(key)}: {expected}" in output + assert "bad_key: ValueError" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/test_audiobusio.py b/ports/zephyr-cp/tests/test_audiobusio.py new file mode 100644 index 0000000000000..5a899139c2210 --- /dev/null +++ b/ports/zephyr-cp/tests/test_audiobusio.py @@ -0,0 +1,216 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries LLC +# SPDX-License-Identifier: MIT + +"""Test audiobusio I2SOut functionality on native_sim.""" + +from pathlib import Path + +import pytest +from perfetto.trace_processor import TraceProcessor + + +I2S_PLAY_CODE = """\ +import array +import math +import audiocore +import board +import time + +# Generate a 440 Hz sine wave, 16-bit signed stereo at 16000 Hz +sample_rate = 16000 +length = sample_rate // 440 # ~36 samples per period +values = [] +for i in range(length): + v = int(math.sin(math.pi * 2 * i / length) * 30000) + values.append(v) # left + values.append(v) # right + +sample = audiocore.RawSample( + array.array("h", values), + sample_rate=sample_rate, + channel_count=2, +) + +dac = board.I2S0() +print("playing") +dac.play(sample, loop=True) +time.sleep(0.5) +dac.stop() +print("stopped") +print("done") +""" + + +def parse_i2s_trace(trace_file: Path, track_name: str) -> list[tuple[int, int]]: + """Parse I2S counter trace from Perfetto trace file.""" + tp = TraceProcessor(file_path=str(trace_file)) + result = tp.query( + f""" + SELECT c.ts, c.value + FROM counter c + JOIN track t ON c.track_id = t.id + WHERE t.name LIKE "%{track_name}" + ORDER BY c.ts + """ + ) + return [(int(row.ts), int(row.value)) for row in result] + + +@pytest.mark.duration(10) +@pytest.mark.circuitpy_drive({"code.py": I2S_PLAY_CODE}) +def test_i2s_play_and_stop(circuitpython): + """Test I2SOut play and stop produce expected output and correct waveform traces.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "playing" in output + assert "stopped" in output + assert "done" in output + + # Check that Perfetto trace has I2S counter tracks with data + left_trace = parse_i2s_trace(circuitpython.trace_file, "Left") + right_trace = parse_i2s_trace(circuitpython.trace_file, "Right") + + # Should have counter events (initial zero + audio data) + assert len(left_trace) > 10, f"Expected many Left channel events, got {len(left_trace)}" + assert len(right_trace) > 10, f"Expected many Right channel events, got {len(right_trace)}" + + # Verify timestamps are spread out (not all the same) + left_timestamps = [ts for ts, _ in left_trace] + assert left_timestamps[-1] > left_timestamps[1], "Timestamps should increase over time" + time_span_ns = left_timestamps[-1] - left_timestamps[1] + # We play for 0.5s, so span should be at least 100ms + assert time_span_ns > 100_000_000, f"Expected >100ms time span, got {time_span_ns / 1e6:.1f}ms" + + # Audio data should contain non-zero values (sine wave) + # Skip the initial zero value + left_values = [v for _, v in left_trace if v != 0] + right_values = [v for _, v in right_trace if v != 0] + assert len(left_values) > 5, "Left channel has too few non-zero values" + assert len(right_values) > 5, "Right channel has too few non-zero values" + + # Sine wave should have both positive and negative values + assert any(v > 0 for v in left_values), "Left channel has no positive values" + assert any(v < 0 for v in left_values), "Left channel has no negative values" + + # Verify amplitude is in the expected range (we generate with amplitude 30000) + max_left = max(left_values) + min_left = min(left_values) + assert max_left > 20000, f"Left max {max_left} too low, expected >20000" + assert min_left < -20000, f"Left min {min_left} too high, expected <-20000" + + # Left and right should match (we write the same value to both channels) + # Compare a subset of matching timestamps + left_by_ts = dict(left_trace) + right_by_ts = dict(right_trace) + common_ts = sorted(set(left_by_ts.keys()) & set(right_by_ts.keys())) + mismatches = 0 + for ts in common_ts[:100]: + if left_by_ts[ts] != right_by_ts[ts]: + mismatches += 1 + assert mismatches == 0, ( + f"{mismatches} L/R mismatches in first {min(100, len(common_ts))} common timestamps" + ) + + +I2S_PLAY_NO_STOP_CODE = """\ +import array +import math +import audiocore +import board +import time + +sample_rate = 16000 +length = sample_rate // 440 +values = [] +for i in range(length): + v = int(math.sin(math.pi * 2 * i / length) * 30000) + values.append(v) + values.append(v) + +sample = audiocore.RawSample( + array.array("h", values), + sample_rate=sample_rate, + channel_count=2, +) + +dac = board.I2S0() +dac.play(sample, loop=True) +print("playing") +time.sleep(0.2) +# Exit without calling dac.stop() — reset_port should clean up +print("exiting") +""" + + +@pytest.mark.duration(15) +@pytest.mark.code_py_runs(2) +@pytest.mark.circuitpy_drive({"code.py": I2S_PLAY_NO_STOP_CODE}) +def test_i2s_stops_on_code_exit(circuitpython): + """Test I2S is stopped by reset_port when code.py exits without explicit stop.""" + # First run: plays audio then exits without stopping + circuitpython.serial.wait_for("exiting") + circuitpython.serial.wait_for("Press any key to enter the REPL") + # Trigger soft reload + circuitpython.serial.write("\x04") + + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + # Should see "playing" and "exiting" at least twice (once per run) + assert output.count("playing") >= 2 + assert output.count("exiting") >= 2 + + +I2S_PAUSE_RESUME_CODE = """\ +import array +import math +import audiocore +import board +import time + +sample_rate = 16000 +length = sample_rate // 440 +values = [] +for i in range(length): + v = int(math.sin(math.pi * 2 * i / length) * 30000) + values.append(v) + values.append(v) + +sample = audiocore.RawSample( + array.array("h", values), + sample_rate=sample_rate, + channel_count=2, +) + +dac = board.I2S0() +dac.play(sample, loop=True) +print("playing") +time.sleep(0.2) + +dac.pause() +print("paused") +assert dac.paused +time.sleep(0.1) + +dac.resume() +print("resumed") +assert not dac.paused +time.sleep(0.2) + +dac.stop() +print("done") +""" + + +@pytest.mark.duration(10) +@pytest.mark.circuitpy_drive({"code.py": I2S_PAUSE_RESUME_CODE}) +def test_i2s_pause_resume(circuitpython): + """Test I2SOut pause and resume work correctly.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "playing" in output + assert "paused" in output + assert "resumed" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/test_basics.py b/ports/zephyr-cp/tests/test_basics.py new file mode 100644 index 0000000000000..84b31849a8e81 --- /dev/null +++ b/ports/zephyr-cp/tests/test_basics.py @@ -0,0 +1,117 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test basic native_sim functionality.""" + +import pytest + + +@pytest.mark.circuitpy_drive(None) +def test_blank_flash_hello_world(circuitpython): + """Test that an erased flash shows code.py output header.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "Board ID:native_native_sim" in output + assert "UID:" in output + assert "code.py output:" in output + assert "Hello World" in output + assert "done" in output + + +# --- PTY Input Tests --- + + +INPUT_CODE = """\ +import sys + +print("ready") +char = sys.stdin.read(1) +print(f"received: {repr(char)}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": INPUT_CODE}) +def test_basic_serial_input(circuitpython): + """Test reading single character from serial via PTY write.""" + circuitpython.serial.wait_for("ready") + circuitpython.serial.write("A") + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "ready" in output + assert "received: 'A'" in output + assert "done" in output + + +INPUT_FUNC_CODE = """\ +print("ready") +name = input("Enter name: ") +print(f"hello {name}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": INPUT_FUNC_CODE}) +def test_input_function(circuitpython): + """Test the built-in input() function with PTY input.""" + circuitpython.serial.wait_for("Enter name:") + circuitpython.serial.write("World\r") + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "ready" in output + assert "Enter name:" in output + assert "hello World" in output + assert "done" in output + + +INTERRUPT_CODE = """\ +import time + +print("starting") +for i in range(100): + print(f"loop {i}") + time.sleep(0.1) +print("completed") +""" + + +@pytest.mark.circuitpy_drive({"code.py": INTERRUPT_CODE}) +def test_ctrl_c_interrupt(circuitpython): + """Test sending Ctrl+C (0x03) to interrupt running code.""" + circuitpython.serial.wait_for("loop 5") + circuitpython.serial.write("\x03") + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "starting" in output + assert "loop 5" in output + assert "KeyboardInterrupt" in output + assert "completed" not in output + + +RELOAD_CODE = """\ +print("first run") +import time +time.sleep(1) +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": RELOAD_CODE}) +@pytest.mark.code_py_runs(2) +def test_ctrl_d_soft_reload(circuitpython): + """Test sending Ctrl+D (0x04) to trigger soft reload.""" + circuitpython.serial.wait_for("first run") + circuitpython.serial.write("\x04") + circuitpython.wait_until_done() + + # Should see "first run" appear multiple times due to reload + # or see a soft reboot message + output = circuitpython.serial.all_output + assert "first run" in output + # The soft reload should restart the code before "done" is printed + assert "done" in output + assert output.count("first run") > 1 diff --git a/ports/zephyr-cp/tests/test_digitalio.py b/ports/zephyr-cp/tests/test_digitalio.py new file mode 100644 index 0000000000000..22c64f7b83fd7 --- /dev/null +++ b/ports/zephyr-cp/tests/test_digitalio.py @@ -0,0 +1,171 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries LLC +# SPDX-License-Identifier: MIT + +"""Test digitalio functionality on native_sim.""" + +import re +from pathlib import Path + +import pytest +from perfetto.trace_processor import TraceProcessor + + +DIGITALIO_INPUT_TRACE_READ_CODE = """\ +import time +import digitalio +import microcontroller + +pin = digitalio.DigitalInOut(microcontroller.pin.P_01) +pin.direction = digitalio.Direction.INPUT + +start = time.monotonic() +last = pin.value +print(f"t_abs={time.monotonic():.3f} initial={last}") + +# Poll long enough to observe a high pulse injected through input trace. +while time.monotonic() - start < 8.0: + value = pin.value + if value != last: + print(f"t_abs={time.monotonic():.3f} edge={value}") + last = value + time.sleep(0.05) + +print(f"t_abs={time.monotonic():.3f} done") +""" + + +DIGITALIO_INPUT_TRACE = { + "gpio_emul.01": [ + (8_000_000_000, 0), + (9_000_000_000, 1), + (10_000_000_000, 0), + ], +} + + +@pytest.mark.duration(14.0) +@pytest.mark.circuitpy_drive({"code.py": DIGITALIO_INPUT_TRACE_READ_CODE}) +@pytest.mark.input_trace(DIGITALIO_INPUT_TRACE) +def test_digitalio_reads_input_trace(circuitpython): + """Test DigitalInOut input reads values injected via input trace.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + + initial_match = re.search(r"t_abs=([0-9]+\.[0-9]+) initial=False", output) + edge_match = re.search(r"t_abs=([0-9]+\.[0-9]+) edge=True", output) + done_match = re.search(r"t_abs=([0-9]+\.[0-9]+) done", output) + + assert initial_match is not None + assert edge_match is not None + assert done_match is not None + + initial_abs = float(initial_match.group(1)) + edge_abs = float(edge_match.group(1)) + done_abs = float(done_match.group(1)) + + # Input trace edge is at 9.0s for gpio_emul.01. + assert 8.5 <= edge_abs <= 9.5 + assert initial_abs <= edge_abs <= done_abs + + +BLINK_CODE = """\ +import time +import board +import digitalio + +led = digitalio.DigitalInOut(board.LED) +led.direction = digitalio.Direction.OUTPUT + +for i in range(3): + print(f"LED on {i}") + led.value = True + time.sleep(0.1) + print(f"LED off {i}") + led.value = False + time.sleep(0.1) + +print("done") +""" + + +def parse_gpio_trace(trace_file: Path, pin_name: str = "gpio_emul.00") -> list[tuple[int, int]]: + """Parse GPIO trace from Perfetto trace file.""" + tp = TraceProcessor(file_path=str(trace_file)) + result = tp.query( + f''' + SELECT c.ts, c.value + FROM counter c + JOIN track t ON c.track_id = t.id + WHERE t.name = "{pin_name}" + ORDER BY c.ts + ''' + ) + return [(row.ts, int(row.value)) for row in result] + + +@pytest.mark.circuitpy_drive({"code.py": BLINK_CODE}) +def test_digitalio_blink_output(circuitpython): + """Test blink program produces expected output and GPIO traces.""" + circuitpython.wait_until_done() + + # Check serial output + output = circuitpython.serial.all_output + assert "LED on 0" in output + assert "LED off 0" in output + assert "LED on 2" in output + assert "LED off 2" in output + assert "done" in output + + # Check GPIO traces - LED is on gpio_emul.00 + gpio_trace = parse_gpio_trace(circuitpython.trace_file, "gpio_emul.00") + + # Deduplicate by timestamp (keep last value at each timestamp) + by_timestamp = {} + for ts, val in gpio_trace: + by_timestamp[ts] = val + sorted_trace = sorted(by_timestamp.items()) + + # Find transition points (where value changes), skipping initialization at ts=0 + transitions = [] + for i in range(1, len(sorted_trace)): + prev_ts, prev_val = sorted_trace[i - 1] + curr_ts, curr_val = sorted_trace[i] + if prev_val != curr_val and curr_ts > 0: + transitions.append((curr_ts, curr_val)) + + # We expect at least 6 transitions (3 on + 3 off) from the blink loop + assert len(transitions) >= 6, f"Expected at least 6 transitions, got {len(transitions)}" + + # Verify timing between consecutive transitions + # Each sleep is 0.1s = 100ms = 100,000,000 ns + expected_interval_ns = 100_000_000 + tolerance_ns = 20_000_000 # 20ms tolerance + + # Find a sequence of 6 consecutive transitions with ~100ms intervals (the blink loop) + # This filters out initialization and cleanup noise + blink_transitions = [] + for i in range(len(transitions) - 1): + interval = transitions[i + 1][0] - transitions[i][0] + if abs(interval - expected_interval_ns) < tolerance_ns: + if not blink_transitions: + blink_transitions.append(transitions[i]) + blink_transitions.append(transitions[i + 1]) + elif blink_transitions: + # Found end of blink sequence + break + + assert len(blink_transitions) >= 6, ( + f"Expected at least 6 blink transitions with ~100ms intervals, got {len(blink_transitions)}" + ) + + # Verify timing between blink transitions + for i in range(1, min(6, len(blink_transitions))): + prev_ts = blink_transitions[i - 1][0] + curr_ts = blink_transitions[i][0] + interval = curr_ts - prev_ts + assert abs(interval - expected_interval_ns) < tolerance_ns, ( + f"Transition interval {interval / 1_000_000:.1f}ms deviates from " + f"expected {expected_interval_ns / 1_000_000:.1f}ms by more than " + f"{tolerance_ns / 1_000_000:.1f}ms tolerance" + ) diff --git a/ports/zephyr-cp/tests/test_flash.py b/ports/zephyr-cp/tests/test_flash.py new file mode 100644 index 0000000000000..e2e5f4de14f81 --- /dev/null +++ b/ports/zephyr-cp/tests/test_flash.py @@ -0,0 +1,171 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test flash filesystem with various erase block sizes.""" + +import subprocess + +import pytest + + +def read_file_from_flash(flash_file, path): + """Extract a file from the FAT filesystem in the flash image.""" + result = subprocess.run( + ["mcopy", "-i", str(flash_file), f"::{path}", "-"], + capture_output=True, + ) + if result.returncode != 0: + raise FileNotFoundError( + f"Failed to read ::{path} from {flash_file}: {result.stderr.decode()}" + ) + return result.stdout.decode() + + +WRITE_READ_CODE = """\ +import storage +storage.remount("/", readonly=False) +with open("/test.txt", "w") as f: + f.write("hello flash") +with open("/test.txt", "r") as f: + content = f.read() +print(f"content: {content}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_READ_CODE}) +def test_flash_default_erase_size(circuitpython): + """Test filesystem write/read with default 4KB erase blocks.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "content: hello flash" in output + assert "done" in output + + content = read_file_from_flash(circuitpython.flash_file, "test.txt") + assert content == "hello flash" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_READ_CODE}) +@pytest.mark.flash_config(erase_block_size=65536) +def test_flash_64k_erase_blocks(circuitpython): + """Test filesystem write/read with 64KB erase blocks (128 blocks per page).""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "content: hello flash" in output + assert "done" in output + + content = read_file_from_flash(circuitpython.flash_file, "test.txt") + assert content == "hello flash" + + +@pytest.mark.circuitpy_drive({"code.py": WRITE_READ_CODE}) +@pytest.mark.flash_config(erase_block_size=262144, total_size=4 * 1024 * 1024) +def test_flash_256k_erase_blocks(circuitpython): + """Test filesystem write/read with 256KB erase blocks (like RA8D1 OSPI).""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "content: hello flash" in output + assert "done" in output + + content = read_file_from_flash(circuitpython.flash_file, "test.txt") + assert content == "hello flash" + + +MULTI_FILE_CODE = """\ +import storage +storage.remount("/", readonly=False) +for i in range(5): + name = f"/file{i}.txt" + with open(name, "w") as f: + f.write(f"data{i}" * 100) +print("multi_file_ok") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": MULTI_FILE_CODE}) +@pytest.mark.flash_config(erase_block_size=262144, total_size=4 * 1024 * 1024) +def test_flash_256k_multi_file(circuitpython): + """Test multiple file writes with 256KB erase blocks to exercise cache flushing.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "multi_file_ok" in output + + for i in range(5): + content = read_file_from_flash(circuitpython.flash_file, f"file{i}.txt") + assert content == f"data{i}" * 100, f"file{i}.txt mismatch" + + +EXISTING_DATA_CODE = """\ +import storage +storage.remount("/", readonly=False) + +# Write a file to populate the erase page. +original_data = "A" * 4000 +with open("/original.txt", "w") as f: + f.write(original_data) + +# Force a flush so the data is written to flash. +storage.remount("/", readonly=True) +storage.remount("/", readonly=False) + +# Now write a small new file. This updates FAT metadata and directory +# entries that share an erase page with original.txt, exercising the +# read-modify-write cycle on the cached page. +with open("/small.txt", "w") as f: + f.write("tiny") + +# Read back original to check it survived. +with open("/original.txt", "r") as f: + readback = f.read() +if readback == original_data: + print("existing_data_ok") +else: + print(f"MISMATCH: got {len(readback)} bytes") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": EXISTING_DATA_CODE}) +@pytest.mark.flash_config(erase_block_size=262144, total_size=4 * 1024 * 1024) +def test_flash_256k_existing_data_survives(circuitpython): + """Test that existing data in an erase page survives when new data is written. + + With 256KB erase blocks (512 blocks per page), writing to any block in + the page triggers an erase-rewrite of the entire page. Existing blocks + must be preserved through the read-modify-write cycle. + """ + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "existing_data_ok" in output + + # Verify both files survived on the actual flash image. + original = read_file_from_flash(circuitpython.flash_file, "original.txt") + assert original == "A" * 4000, f"original.txt corrupted: got {len(original)} bytes" + + small = read_file_from_flash(circuitpython.flash_file, "small.txt") + assert small == "tiny" + + +OVERWRITE_CODE = """\ +import storage +storage.remount("/", readonly=False) +with open("/overwrite.txt", "w") as f: + f.write("first version") +with open("/overwrite.txt", "w") as f: + f.write("second version") +print("overwrite_ok") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": OVERWRITE_CODE}) +@pytest.mark.flash_config(erase_block_size=262144, total_size=4 * 1024 * 1024) +def test_flash_256k_overwrite(circuitpython): + """Test overwriting a file with 256KB erase blocks to exercise erase-rewrite cycle.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + assert "overwrite_ok" in output + + content = read_file_from_flash(circuitpython.flash_file, "overwrite.txt") + assert content == "second version" diff --git a/ports/zephyr-cp/tests/test_hashlib.py b/ports/zephyr-cp/tests/test_hashlib.py new file mode 100644 index 0000000000000..94463ba1e991a --- /dev/null +++ b/ports/zephyr-cp/tests/test_hashlib.py @@ -0,0 +1,152 @@ +# SPDX-FileCopyrightText: 2026 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test the hashlib module against CPython hashlib.""" + +import hashlib + +import pytest + + +SHORT_DATA = b"CircuitPython!" +CHUNK_A = b"Hello, " +CHUNK_B = b"CircuitPython world!" +LONG_DATA = b"The quick brown fox jumps over the lazy dog." * 64 + + +SHA256_CODE = """\ +import hashlib + +h = hashlib.new("sha256", b"CircuitPython!") +print(f"digest_size: {h.digest_size}") +print(f"digest_hex: {h.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": SHA256_CODE}) +def test_hashlib_sha256_basic(circuitpython): + """sha256 digest on a small buffer matches CPython hashlib.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + expected = hashlib.sha256(SHORT_DATA).hexdigest() + assert "digest_size: 32" in output + assert f"digest_hex: {expected}" in output + assert "done" in output + + +SHA1_CODE = """\ +import hashlib + +h = hashlib.new("sha1", b"CircuitPython!") +print(f"digest_size: {h.digest_size}") +print(f"digest_hex: {h.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": SHA1_CODE}) +def test_hashlib_sha1_basic(circuitpython): + """sha1 digest on a small buffer matches CPython hashlib.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + expected = hashlib.sha1(SHORT_DATA).hexdigest() + assert "digest_size: 20" in output + assert f"digest_hex: {expected}" in output + assert "done" in output + + +UPDATE_CODE = """\ +import hashlib + +h = hashlib.new("sha256") +h.update(b"Hello, ") +h.update(b"CircuitPython world!") +print(f"chunked_hex: {h.digest().hex()}") + +h2 = hashlib.new("sha256", b"Hello, CircuitPython world!") +print(f"oneshot_hex: {h2.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": UPDATE_CODE}) +def test_hashlib_sha256_update_chunks(circuitpython): + """Multiple update() calls produce the same digest as a single buffer, and match CPython.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + expected = hashlib.sha256(CHUNK_A + CHUNK_B).hexdigest() + assert f"chunked_hex: {expected}" in output + assert f"oneshot_hex: {expected}" in output + assert "done" in output + + +LONG_CODE = """\ +import hashlib + +data = b"The quick brown fox jumps over the lazy dog." * 64 +h = hashlib.new("sha256", data) +print(f"sha256_hex: {h.digest().hex()}") + +h1 = hashlib.new("sha1", data) +print(f"sha1_hex: {h1.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": LONG_CODE}) +def test_hashlib_long_input(circuitpython): + """Digests of a multi-block input match CPython for both sha1 and sha256.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"sha256_hex: {hashlib.sha256(LONG_DATA).hexdigest()}" in output + assert f"sha1_hex: {hashlib.sha1(LONG_DATA).hexdigest()}" in output + assert "done" in output + + +EMPTY_CODE = """\ +import hashlib + +h = hashlib.new("sha256", b"") +print(f"empty256: {h.digest().hex()}") + +h = hashlib.new("sha1", b"") +print(f"empty1: {h.digest().hex()}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": EMPTY_CODE}) +def test_hashlib_empty_input(circuitpython): + """Empty-input digests match CPython well-known values.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"empty256: {hashlib.sha256(b'').hexdigest()}" in output + assert f"empty1: {hashlib.sha1(b'').hexdigest()}" in output + assert "done" in output + + +BAD_ALGO_CODE = """\ +import hashlib + +try: + hashlib.new("md5", b"data") +except ValueError: + print("bad_algo: ValueError") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": BAD_ALGO_CODE}) +def test_hashlib_unsupported_algorithm(circuitpython): + """Unsupported algorithm names raise ValueError.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "bad_algo: ValueError" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/test_i2c.py b/ports/zephyr-cp/tests/test_i2c.py new file mode 100644 index 0000000000000..ec5229faa2f26 --- /dev/null +++ b/ports/zephyr-cp/tests/test_i2c.py @@ -0,0 +1,102 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test I2C functionality on native_sim.""" + +import pytest + +I2C_SCAN_CODE = """\ +import board + +i2c = board.I2C() +while not i2c.try_lock(): + pass +devices = i2c.scan() +print(f"I2C devices: {[hex(d) for d in devices]}") +i2c.unlock() +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": I2C_SCAN_CODE}) +def test_i2c_scan(circuitpython): + """Test I2C bus scanning finds emulated devices. + + The AT24 EEPROM emulator responds to zero-length probe writes, + so it should appear in scan results at address 0x50. + """ + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "I2C devices:" in output + # AT24 EEPROM should be at address 0x50 + assert "0x50" in output + assert "done" in output + + +AT24_READ_CODE = """\ +import board + +i2c = board.I2C() +while not i2c.try_lock(): + pass + +# AT24 EEPROM at address 0x50 +AT24_ADDR = 0x50 + +# Read first byte from address 0 +result = bytearray(1) +try: + i2c.writeto_then_readfrom(AT24_ADDR, bytes([0x00]), result) + value = result[0] + print(f"AT24 byte 0: 0x{value:02X}") + # Fresh EEPROM should be 0xFF + if value == 0xFF: + print("eeprom_valid") + else: + print(f"unexpected value: expected 0xFF, got 0x{value:02X}") +except OSError as e: + print(f"I2C error: {e}") + +i2c.unlock() +i2c.deinit() +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": AT24_READ_CODE}) +def test_i2c_at24_read(circuitpython): + """Test reading from AT24 EEPROM emulator.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "AT24 byte 0: 0xFF" in output + assert "eeprom_valid" in output + assert "done" in output + + +@pytest.mark.circuitpy_drive({"code.py": I2C_SCAN_CODE}) +@pytest.mark.disable_i2c_devices("eeprom@50") +def test_i2c_device_disabled(circuitpython): + """Test that disabled I2C device doesn't appear in scan.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "I2C devices:" in output + # AT24 at 0x50 should NOT appear when disabled + assert "0x50" not in output + assert "done" in output + + +@pytest.mark.circuitpy_drive({"code.py": AT24_READ_CODE}) +@pytest.mark.disable_i2c_devices("eeprom@50") +def test_i2c_device_disabled_communication_fails(circuitpython): + """Test that communication with disabled I2C device fails.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + # Should get an I2C error when trying to communicate + assert "I2C error" in output + assert "eeprom_valid" not in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/test_msgpack.py b/ports/zephyr-cp/tests/test_msgpack.py new file mode 100644 index 0000000000000..09bed35209c0a --- /dev/null +++ b/ports/zephyr-cp/tests/test_msgpack.py @@ -0,0 +1,137 @@ +# SPDX-FileCopyrightText: 2026 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test the msgpack module.""" + +import pytest + + +ROUNDTRIP_CODE = """\ +import msgpack +from io import BytesIO + +obj = {"list": [True, False, None, 1, 3.125], "str": "blah"} +b = BytesIO() +msgpack.pack(obj, b) +encoded = b.getvalue() +print(f"encoded_len: {len(encoded)}") +print(f"encoded_hex: {encoded.hex()}") + +b.seek(0) +decoded = msgpack.unpack(b) +print(f"decoded: {decoded}") +print(f"match: {decoded == obj}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": ROUNDTRIP_CODE}) +def test_msgpack_roundtrip(circuitpython): + """Pack and unpack a dict containing the basic msgpack types.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "match: True" in output + assert "done" in output + + +USE_LIST_CODE = """\ +import msgpack +from io import BytesIO + +b = BytesIO() +msgpack.pack([1, 2, 3], b) + +b.seek(0) +as_list = msgpack.unpack(b) +print(f"as_list: {as_list} type={type(as_list).__name__}") + +b.seek(0) +as_tuple = msgpack.unpack(b, use_list=False) +print(f"as_tuple: {as_tuple} type={type(as_tuple).__name__}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": USE_LIST_CODE}) +def test_msgpack_use_list(circuitpython): + """use_list=False should return a tuple instead of a list.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "as_list: [1, 2, 3] type=list" in output + assert "as_tuple: (1, 2, 3) type=tuple" in output + assert "done" in output + + +EXTTYPE_CODE = """\ +from msgpack import pack, unpack, ExtType +from io import BytesIO + +class MyClass: + def __init__(self, val): + self.value = val + +data = MyClass(b"my_value") + +def encoder(obj): + if isinstance(obj, MyClass): + return ExtType(1, obj.value) + return f"no encoder for {obj}" + +def decoder(code, data): + if code == 1: + return MyClass(data) + return f"no decoder for type {code}" + +buf = BytesIO() +pack(data, buf, default=encoder) +buf.seek(0) +decoded = unpack(buf, ext_hook=decoder) +print(f"decoded_type: {type(decoded).__name__}") +print(f"decoded_value: {decoded.value}") +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": EXTTYPE_CODE}) +def test_msgpack_exttype(circuitpython): + """ExtType with a custom encoder/decoder should round-trip.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "decoded_type: MyClass" in output + assert "decoded_value: b'my_value'" in output + assert "done" in output + + +EXTTYPE_PROPS_CODE = """\ +from msgpack import ExtType + +e = ExtType(5, b"hello") +print(f"code: {e.code}") +print(f"data: {e.data}") + +e.code = 10 +print(f"new_code: {e.code}") + +try: + ExtType(128, b"x") +except (ValueError, OverflowError) as ex: + print(f"range_error: {type(ex).__name__}") + +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": EXTTYPE_PROPS_CODE}) +def test_msgpack_exttype_properties(circuitpython): + """ExtType exposes code/data as read/write properties and rejects out-of-range codes.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "code: 5" in output + assert "data: b'hello'" in output + assert "new_code: 10" in output + assert "range_error:" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/test_nvm.py b/ports/zephyr-cp/tests/test_nvm.py new file mode 100644 index 0000000000000..5de304afc1bdc --- /dev/null +++ b/ports/zephyr-cp/tests/test_nvm.py @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: 2025 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test NVM functionality on native_sim.""" + +import pytest + + +NVM_BASIC_CODE = """\ +import microcontroller + +nvm = microcontroller.nvm +print(f"nvm length: {len(nvm)}") + +# Write some bytes +nvm[0] = 42 +nvm[1] = 99 +print(f"nvm[0]: {nvm[0]}") +print(f"nvm[1]: {nvm[1]}") + +# Write a slice +nvm[2:5] = b"\\x01\\x02\\x03" +print(f"nvm[2:5]: {list(nvm[2:5])}") + +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": NVM_BASIC_CODE}) +def test_nvm_read_write(circuitpython): + """Test basic NVM read and write operations.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "nvm length: 8192" in output + assert "nvm[0]: 42" in output + assert "nvm[1]: 99" in output + assert "nvm[2:5]: [1, 2, 3]" in output + assert "done" in output + + +NVM_PERSIST_CODE = """\ +import microcontroller + +nvm = microcontroller.nvm +value = nvm[0] +print(f"nvm[0]: {value}") + +if value == 255: + # First run: write a marker + nvm[0] = 123 + print("wrote marker") +else: + print(f"marker found: {value}") + +print("done") +""" + + +@pytest.mark.circuitpy_drive({"code.py": NVM_PERSIST_CODE}) +@pytest.mark.code_py_runs(2) +def test_nvm_persists_across_reload(circuitpython): + """Test that NVM data persists across soft reloads.""" + circuitpython.serial.wait_for("wrote marker") + # Trigger soft reload + circuitpython.serial.write("\x04") + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "nvm[0]: 255" in output + assert "wrote marker" in output + assert "marker found: 123" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/test_rotaryio.py b/ports/zephyr-cp/tests/test_rotaryio.py new file mode 100644 index 0000000000000..e9a5c1913cb20 --- /dev/null +++ b/ports/zephyr-cp/tests/test_rotaryio.py @@ -0,0 +1,132 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries LLC +# SPDX-License-Identifier: MIT + +"""Test rotaryio functionality on native_sim.""" + +import pytest + + +ROTARY_CODE_5S = """\ +import time +import microcontroller +import rotaryio + +encoder = rotaryio.IncrementalEncoder(microcontroller.pin.P_01, microcontroller.pin.P_02) + +time.sleep(5.0) # Sleep long enough for trace events to complete +print(f"position={encoder.position}") +print("done") +""" + + +ROTARY_CODE_7S = """\ +import time +import microcontroller +import rotaryio + +encoder = rotaryio.IncrementalEncoder(microcontroller.pin.P_01, microcontroller.pin.P_02) + +time.sleep(7.0) # Sleep long enough for trace events to complete +print(f"position={encoder.position}") +print("done") +""" + + +CLOCKWISE_TRACE = { + "gpio_emul.01": [ + (4_000_000_000, 0), # 4.0s: initial state (low) + (4_100_000_000, 1), # 4.1s: A goes high (A leads) + (4_300_000_000, 0), # 4.3s: A goes low + ], + "gpio_emul.02": [ + (4_000_000_000, 0), # 4.0s: initial state (low) + (4_200_000_000, 1), # 4.2s: B goes high (B follows) + (4_400_000_000, 0), # 4.4s: B goes low + ], +} + +COUNTERCLOCKWISE_TRACE = { + "gpio_emul.01": [ + (4_000_000_000, 0), # 4.0s: initial state (low) + (4_200_000_000, 1), # 4.2s: A goes high (A follows) + (4_400_000_000, 0), # 4.4s: A goes low + ], + "gpio_emul.02": [ + (4_000_000_000, 0), # 4.0s: initial state (low) + (4_100_000_000, 1), # 4.1s: B goes high (B leads) + (4_300_000_000, 0), # 4.3s: B goes low + ], +} + +BOTH_DIRECTIONS_TRACE = { + "gpio_emul.01": [ + (4_000_000_000, 0), # Initial state + # First clockwise detent + (4_100_000_000, 1), # A rises (leads) + (4_300_000_000, 0), # A falls + # Second clockwise detent + (4_500_000_000, 1), # A rises (leads) + (4_700_000_000, 0), # A falls + # First counter-clockwise detent + (5_000_000_000, 1), # A rises (follows) + (5_200_000_000, 0), # A falls + # Second counter-clockwise detent + (5_400_000_000, 1), # A rises (follows) + (5_600_000_000, 0), # A falls + # Third counter-clockwise detent + (5_800_000_000, 1), # A rises (follows) + (6_000_000_000, 0), # A falls + ], + "gpio_emul.02": [ + (4_000_000_000, 0), # Initial state + # First clockwise detent + (4_200_000_000, 1), # B rises (follows) + (4_400_000_000, 0), # B falls + # Second clockwise detent + (4_600_000_000, 1), # B rises (follows) + (4_800_000_000, 0), # B falls + # First counter-clockwise detent + (4_900_000_000, 1), # B rises (leads) + (5_100_000_000, 0), # B falls + # Second counter-clockwise detent + (5_300_000_000, 1), # B rises (leads) + (5_500_000_000, 0), # B falls + # Third counter-clockwise detent + (5_700_000_000, 1), # B rises (leads) + (5_900_000_000, 0), # B falls + ], +} + + +@pytest.mark.circuitpy_drive({"code.py": ROTARY_CODE_5S}) +@pytest.mark.input_trace(CLOCKWISE_TRACE) +def test_rotaryio_incrementalencoder_clockwise(circuitpython): + """Test clockwise rotation increments position.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "position=1" in output + assert "done" in output + + +@pytest.mark.circuitpy_drive({"code.py": ROTARY_CODE_5S}) +@pytest.mark.input_trace(COUNTERCLOCKWISE_TRACE) +def test_rotaryio_incrementalencoder_counterclockwise(circuitpython): + """Test counter-clockwise rotation decrements position.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "position=-1" in output + assert "done" in output + + +@pytest.mark.duration(12.0) +@pytest.mark.circuitpy_drive({"code.py": ROTARY_CODE_7S}) +@pytest.mark.input_trace(BOTH_DIRECTIONS_TRACE) +def test_rotaryio_incrementalencoder_both_directions(circuitpython): + """Test rotation in both directions: 2 clockwise, then 3 counter-clockwise.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "position=-1" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/test_web_workflow.py b/ports/zephyr-cp/tests/test_web_workflow.py new file mode 100644 index 0000000000000..d3a00782a333e --- /dev/null +++ b/ports/zephyr-cp/tests/test_web_workflow.py @@ -0,0 +1,142 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Tests for web workflow on native_sim.""" + +from __future__ import annotations + +import json +import re + +import pytest +import requests + + +pytestmark = pytest.mark.native_sim_rt + +WEB_WORKFLOW_PORT = 8090 +WEB_WORKFLOW_PASSWORD = "testpass" + +WEB_WORKFLOW_CODE = """\ +import time + +# Keep the VM alive while the web workflow starts. +time.sleep(3) +""" + +WEB_WORKFLOW_UPDATED_CODE = """\ +print("updated") +""" + +WEB_WORKFLOW_SETTINGS = f"""\ +CIRCUITPY_WEB_API_PASSWORD="{WEB_WORKFLOW_PASSWORD}" +CIRCUITPY_WEB_API_PORT={WEB_WORKFLOW_PORT} +""" + +WEB_WORKFLOW_SETTINGS_PORT_80 = f"""\ +CIRCUITPY_WEB_API_PASSWORD="{WEB_WORKFLOW_PASSWORD}" +CIRCUITPY_WEB_API_PORT=80 +""" + +WEB_WORKFLOW_BOOT = """\ +import storage + +storage.remount("/", readonly=False) +""" + + +@pytest.mark.circuitpy_drive( + { + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS, + } +) +def test_web_workflow_hostnetwork(circuitpython): + """Ensure web workflow responds over hostnetwork.""" + circuitpython.serial.wait_for(f"127.0.0.1:{WEB_WORKFLOW_PORT}") + response = requests.get(f"http://127.0.0.1:{WEB_WORKFLOW_PORT}/edit/", timeout=1.0) + + assert response.status_code == 401 + + +@pytest.mark.circuitpy_drive( + { + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS, + } +) +def test_web_workflow_version_json_hostnetwork_ip_and_port(circuitpython): + """Ensure /cp/version.json reports hostnetwork endpoint with configured port.""" + circuitpython.serial.wait_for(f"127.0.0.1:{WEB_WORKFLOW_PORT}") + response = requests.get( + f"http://127.0.0.1:{WEB_WORKFLOW_PORT}/cp/version.json", + auth=("", WEB_WORKFLOW_PASSWORD), + timeout=1.0, + ) + + assert response.status_code == 200 + + payload = json.loads(response.text) + assert payload["ip"] == "127.0.0.1" + assert payload["port"] == WEB_WORKFLOW_PORT + + +@pytest.mark.circuitpy_drive( + { + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS, + } +) +def test_web_workflow_status_line_hostnetwork_non_default_port(circuitpython): + """Status line should include hostnetwork IP and non-default port.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + + # Remove ANSI control sequences before matching. + output = re.sub(r"\x1b\[[0-9;]*[A-Za-z]", "", output) + assert "127.0.0.1:8090" in output + + +@pytest.mark.circuitpy_drive( + { + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS_PORT_80, + } +) +def test_web_workflow_status_line_hostnetwork_default_port(circuitpython): + """Status line should show IP without :80 for default HTTP port.""" + circuitpython.wait_until_done() + output = circuitpython.serial.all_output + + output = re.sub(r"\x1b\[[0-9;]*[A-Za-z]", "", output) + assert "127.0.0.1" in output + assert "127.0.0.1:80" not in output + + +@pytest.mark.circuitpy_drive( + { + "boot.py": WEB_WORKFLOW_BOOT, + "code.py": WEB_WORKFLOW_CODE, + "settings.toml": WEB_WORKFLOW_SETTINGS, + } +) +def test_web_workflow_write_code_py_remount(circuitpython): + """Ensure web workflow can update code.py after remounting.""" + circuitpython.serial.wait_for(f"127.0.0.1:{WEB_WORKFLOW_PORT}") + body = WEB_WORKFLOW_UPDATED_CODE.encode("utf-8") + + response = requests.put( + f"http://127.0.0.1:{WEB_WORKFLOW_PORT}/fs/code.py", + auth=("", WEB_WORKFLOW_PASSWORD), + data=body, + timeout=1.0, + ) + assert response.status_code in (201, 204) + + response = requests.get( + f"http://127.0.0.1:{WEB_WORKFLOW_PORT}/fs/code.py", + auth=("", WEB_WORKFLOW_PASSWORD), + timeout=1.0, + ) + assert response.status_code == 200 + assert WEB_WORKFLOW_UPDATED_CODE in response.text diff --git a/ports/zephyr-cp/tests/test_zlib.py b/ports/zephyr-cp/tests/test_zlib.py new file mode 100644 index 0000000000000..bd89d8e1c257a --- /dev/null +++ b/ports/zephyr-cp/tests/test_zlib.py @@ -0,0 +1,110 @@ +# SPDX-FileCopyrightText: 2026 Tim Cocks for Adafruit Industries +# SPDX-License-Identifier: MIT + +"""Test the zlib module against CPython zlib. + +CircuitPython's zlib only implements ``decompress``; these tests compress data +with CPython and verify the zephyr-cp port decodes it back to the same bytes. +""" + +import zlib + +import pytest + + +PLAIN_TEXT = b"CircuitPython running on Zephyr says hello!" +REPEATED_TEXT = b"The quick brown fox jumps over the lazy dog. " * 32 +BINARY_BLOB = bytes(range(256)) * 4 + + +def _make_code(compressed: bytes, wbits: int, expected_len: int) -> str: + return ( + "import zlib\n" + f"compressed = {compressed!r}\n" + f"out = zlib.decompress(compressed, {wbits})\n" + f'print(f"out_len: {{len(out)}}")\n' + f'print(f"expected_len: {expected_len}")\n' + 'print(f"out_hex: {out.hex()}")\n' + 'print("done")\n' + ) + + +ZLIB_COMPRESSED = zlib.compress(PLAIN_TEXT) +ZLIB_CODE = _make_code(ZLIB_COMPRESSED, wbits=15, expected_len=len(PLAIN_TEXT)) + + +@pytest.mark.circuitpy_drive({"code.py": ZLIB_CODE}) +def test_zlib_decompress_zlib_format(circuitpython): + """Data produced by CPython zlib.compress() round-trips through CircuitPython zlib.decompress().""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(PLAIN_TEXT)}" in output + assert f"out_hex: {PLAIN_TEXT.hex()}" in output + assert "done" in output + + +REPEATED_COMPRESSED = zlib.compress(REPEATED_TEXT) +REPEATED_CODE = _make_code(REPEATED_COMPRESSED, wbits=15, expected_len=len(REPEATED_TEXT)) + + +@pytest.mark.circuitpy_drive({"code.py": REPEATED_CODE}) +def test_zlib_decompress_repeated(circuitpython): + """Highly-compressible repeated input decompresses correctly (back-references).""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(REPEATED_TEXT)}" in output + # Repeated text has many back-references; shrinks a lot. + assert len(REPEATED_COMPRESSED) < len(REPEATED_TEXT) // 4 + assert f"out_hex: {REPEATED_TEXT.hex()}" in output + assert "done" in output + + +BINARY_COMPRESSED = zlib.compress(BINARY_BLOB) +BINARY_CODE = _make_code(BINARY_COMPRESSED, wbits=15, expected_len=len(BINARY_BLOB)) + + +@pytest.mark.circuitpy_drive({"code.py": BINARY_CODE}) +def test_zlib_decompress_binary_bytes(circuitpython): + """Decompression preserves every byte value (0x00-0xFF).""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(BINARY_BLOB)}" in output + assert f"out_hex: {BINARY_BLOB.hex()}" in output + assert "done" in output + + +# Raw DEFLATE stream (no zlib header/trailer). +_raw_compressor = zlib.compressobj(wbits=-15) +RAW_COMPRESSED = _raw_compressor.compress(PLAIN_TEXT) + _raw_compressor.flush() +RAW_CODE = _make_code(RAW_COMPRESSED, wbits=-15, expected_len=len(PLAIN_TEXT)) + + +@pytest.mark.circuitpy_drive({"code.py": RAW_CODE}) +def test_zlib_decompress_raw_deflate(circuitpython): + """Raw DEFLATE streams (wbits=-15) decompress to the original bytes.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(PLAIN_TEXT)}" in output + assert f"out_hex: {PLAIN_TEXT.hex()}" in output + assert "done" in output + + +# Gzip wrapper (wbits=31). +_gzip_compressor = zlib.compressobj(wbits=31) +GZIP_COMPRESSED = _gzip_compressor.compress(PLAIN_TEXT) + _gzip_compressor.flush() +GZIP_CODE = _make_code(GZIP_COMPRESSED, wbits=31, expected_len=len(PLAIN_TEXT)) + + +@pytest.mark.circuitpy_drive({"code.py": GZIP_CODE}) +def test_zlib_decompress_gzip_format(circuitpython): + """Gzip-wrapped streams (wbits=31) decompress to the original bytes.""" + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert f"out_len: {len(PLAIN_TEXT)}" in output + assert f"out_hex: {PLAIN_TEXT.hex()}" in output + assert "done" in output diff --git a/ports/zephyr-cp/tests/zephyr_display/README.md b/ports/zephyr-cp/tests/zephyr_display/README.md new file mode 100644 index 0000000000000..6b50202154352 --- /dev/null +++ b/ports/zephyr-cp/tests/zephyr_display/README.md @@ -0,0 +1,45 @@ +# Zephyr Display Golden Tests + +This directory contains native_sim golden-image tests for the Zephyr-specific `zephyr_display` path. + +## What is tested + +- `board.DISPLAY` is present and usable. +- CircuitPython terminal/console tilegrids are attached to the default display root group. +- Deterministic console terminal output matches a checked-in golden image. +- `zephyr_display` pixel format constants are exposed. +- `displayio` rendering produces expected stripe colors at sampled pixel locations. + +## Files + +- `test_zephyr_display.py` – pytest tests. +- `golden/terminal_console_output_320x240.png` – console terminal output golden reference image. + +## How capture works + +These tests use trace-driven SDL display capture triggered by Perfetto instant events: + +- `--input-trace=` provides a Perfetto trace containing a `"display_capture"` track + with instant events at the desired capture timestamps. +- `--display_capture_png=` specifies the output PNG pattern (may contain `%d` for + a sequence number). +- `--display_headless` runs SDL in headless/hidden-window mode (always enabled for native_sim tests). + +The test harness sets these flags automatically when tests use +`@pytest.mark.display(capture_times_ns=[...])`. + +## Regenerating the console golden image + +```bash +rm -rf /tmp/zephyr-display-golden +pytest -q ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py::test_console_output_golden \ + --basetemp=/tmp/zephyr-display-golden +cp /tmp/zephyr-display-golden/test_console_output_golden0/frame_0.png \ + ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.png +``` + +## Running the tests + +```bash +pytest -q ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py +``` diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240.png new file mode 100644 index 0000000000000..5739b8ee156eb Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_AL_88.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_AL_88.png new file mode 100644 index 0000000000000..6bdb72b802c97 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_AL_88.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_ARGB_8888.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_ARGB_8888.png new file mode 100644 index 0000000000000..90b422792db10 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_ARGB_8888.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_BGR_565.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_BGR_565.png new file mode 100644 index 0000000000000..5739b8ee156eb Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_BGR_565.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_L_8.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_L_8.png new file mode 100644 index 0000000000000..6bdb72b802c97 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_L_8.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_MONO01.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_MONO01.png new file mode 100644 index 0000000000000..b22de4417ab38 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_MONO01.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_MONO10.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_MONO10.png new file mode 100644 index 0000000000000..b22de4417ab38 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_MONO10.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_565.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_565.png new file mode 100644 index 0000000000000..5739b8ee156eb Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_565.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_888.png b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_888.png new file mode 100644 index 0000000000000..c4301da47c530 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/color_gradient_320x240_RGB_888.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.mask.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.mask.png new file mode 100644 index 0000000000000..3ebb967560f0a Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.mask.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.png new file mode 100644 index 0000000000000..77577a65601ab Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_AL_88.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_AL_88.png new file mode 100644 index 0000000000000..f9ceedebe4a20 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_AL_88.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_ARGB_8888.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_ARGB_8888.png new file mode 100644 index 0000000000000..90b422792db10 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_ARGB_8888.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_BGR_565.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_BGR_565.png new file mode 100644 index 0000000000000..77577a65601ab Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_BGR_565.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_L_8.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_L_8.png new file mode 100644 index 0000000000000..f9ceedebe4a20 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_L_8.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01.png new file mode 100644 index 0000000000000..59928bcd10704 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01_no_vtiled.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01_no_vtiled.png new file mode 100644 index 0000000000000..59928bcd10704 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO01_no_vtiled.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10.png new file mode 100644 index 0000000000000..59928bcd10704 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10_no_vtiled.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10_no_vtiled.png new file mode 100644 index 0000000000000..59928bcd10704 Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_MONO10_no_vtiled.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_565.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_565.png new file mode 100644 index 0000000000000..77577a65601ab Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_565.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_888.png b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_888.png new file mode 100644 index 0000000000000..28d51925caefe Binary files /dev/null and b/ports/zephyr-cp/tests/zephyr_display/golden/terminal_console_output_320x240_RGB_888.png differ diff --git a/ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py b/ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py new file mode 100644 index 0000000000000..6700c42ec4a94 --- /dev/null +++ b/ports/zephyr-cp/tests/zephyr_display/test_zephyr_display.py @@ -0,0 +1,365 @@ +# SPDX-FileCopyrightText: 2026 Scott Shawcroft for Adafruit Industries +# SPDX-License-Identifier: MIT + +import colorsys +import shutil +import struct +from pathlib import Path + +import pytest +from PIL import Image + + +def _read_image(path: Path) -> tuple[int, int, bytes]: + """Read an image file and return (width, height, RGB bytes).""" + with Image.open(path) as img: + rgb = img.convert("RGB") + return rgb.width, rgb.height, rgb.tobytes() + + +def _read_mask(golden_path: Path) -> bytes | None: + """Load the companion mask PNG for a golden image, if it exists. + + Non-zero pixels are masked (skipped during comparison). + """ + mask_path = golden_path.with_suffix(".mask.png") + if not mask_path.exists(): + return None + with Image.open(mask_path) as img: + return img.convert("L").tobytes() + + +def _assert_pixels_equal_masked( + golden_pixels: bytes, actual_pixels: bytes, mask: bytes | None = None +): + """Assert pixels match, skipping positions where the mask is non-zero.""" + assert len(golden_pixels) == len(actual_pixels) + for i in range(0, len(golden_pixels), 3): + if mask is not None and mask[i // 3] != 0: + continue + if golden_pixels[i : i + 3] != actual_pixels[i : i + 3]: + pixel = i // 3 + assert False, ( + f"Pixel {pixel} mismatch: " + f"golden={tuple(golden_pixels[i : i + 3])} " + f"actual={tuple(actual_pixels[i : i + 3])}" + ) + + +BOARD_DISPLAY_AVAILABLE_CODE = """\ +import board +print(hasattr(board, 'DISPLAY')) +print(type(board.DISPLAY).__name__) +print(board.DISPLAY.width, board.DISPLAY.height) +print('done') +""" + + +@pytest.mark.circuitpy_drive({"code.py": BOARD_DISPLAY_AVAILABLE_CODE}) +@pytest.mark.display +@pytest.mark.duration(8) +def test_board_display_available(circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "True" in output + assert "Display" in output + assert "320 240" in output + assert "done" in output + + +CONSOLE_TERMINAL_PRESENT_CODE = """\ +import board + +root = board.DISPLAY.root_group +has_terminal_tilegrids = ( + type(root).__name__ == 'Group' and + len(root) >= 2 and + type(root[0]).__name__ == 'TileGrid' and + type(root[-1]).__name__ == 'TileGrid' +) +print('has_terminal_tilegrids:', has_terminal_tilegrids) +print('done') +""" + + +@pytest.mark.circuitpy_drive({"code.py": CONSOLE_TERMINAL_PRESENT_CODE}) +@pytest.mark.display +@pytest.mark.duration(8) +def test_console_terminal_present_by_default(circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "has_terminal_tilegrids: True" in output + assert "done" in output + + +CONSOLE_OUTPUT_GOLDEN_CODE = """\ +import time +time.sleep(0.25) +print('done') +while True: + time.sleep(1) +""" + + +def _golden_compare_or_update(request, captures, golden_path, mask_path=None): + """Compare captured PNG against golden, or update golden if --update-goldens. + + mask_path overrides the default companion mask lookup (golden.mask.png). + """ + if not captures or not captures[0].exists(): + pytest.skip("display capture was not produced") + + if request.config.getoption("--update-goldens"): + golden_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(captures[0], golden_path) + return + + gw, gh, gpx = _read_image(golden_path) + dw, dh, dpx = _read_image(captures[0]) + if mask_path is not None: + mask = _read_mask(mask_path) + else: + mask = _read_mask(golden_path) + + assert (dw, dh) == (gw, gh) + _assert_pixels_equal_masked(gpx, dpx, mask) + + +@pytest.mark.circuitpy_drive({"code.py": CONSOLE_OUTPUT_GOLDEN_CODE}) +@pytest.mark.display(capture_times_ns=[4_000_000_000]) +@pytest.mark.duration(8) +def test_console_output_golden(request, circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "done" in output + + golden = Path(__file__).parent / "golden" / "terminal_console_output_320x240.png" + _golden_compare_or_update(request, circuitpython.display_capture_paths(), golden) + + +PIXEL_FORMATS = ["ARGB_8888", "RGB_888", "RGB_565", "BGR_565", "L_8", "AL_88", "MONO01", "MONO10"] + +# Shared mask: the same screen regions vary regardless of pixel format. +_CONSOLE_GOLDEN_MASK = Path(__file__).parent / "golden" / "terminal_console_output_320x240.png" + + +@pytest.mark.circuitpy_drive({"code.py": CONSOLE_OUTPUT_GOLDEN_CODE}) +@pytest.mark.display(capture_times_ns=[4_000_000_000]) +@pytest.mark.duration(8) +@pytest.mark.parametrize( + "pixel_format", + PIXEL_FORMATS, + indirect=True, +) +def test_console_output_golden_pixel_format(pixel_format, request, circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "done" in output + + golden_name = f"terminal_console_output_320x240_{pixel_format}.png" + golden = Path(__file__).parent / "golden" / golden_name + _golden_compare_or_update( + request, circuitpython.display_capture_paths(), golden, _CONSOLE_GOLDEN_MASK + ) + + +MONO_NO_VTILED_FORMATS = ["MONO01", "MONO10"] + + +@pytest.mark.circuitpy_drive({"code.py": CONSOLE_OUTPUT_GOLDEN_CODE}) +@pytest.mark.display(capture_times_ns=[4_000_000_000]) +@pytest.mark.display_mono_vtiled(False) +@pytest.mark.duration(8) +@pytest.mark.parametrize( + "pixel_format", + MONO_NO_VTILED_FORMATS, + indirect=True, +) +def test_console_output_golden_mono_no_vtiled(pixel_format, request, circuitpython): + circuitpython.wait_until_done() + + output = circuitpython.serial.all_output + assert "done" in output + + golden_name = f"terminal_console_output_320x240_{pixel_format}_no_vtiled.png" + golden = Path(__file__).parent / "golden" / golden_name + _golden_compare_or_update( + request, circuitpython.display_capture_paths(), golden, _CONSOLE_GOLDEN_MASK + ) + + +def _generate_gradient_bmp(width, height): + """Generate a 24-bit BMP with HSL color gradient. + + Hue sweeps left to right, lightness goes from black (bottom) to white (top), + saturation is 1.0. + """ + row_size = width * 3 + row_padding = (4 - (row_size % 4)) % 4 + padded_row_size = row_size + row_padding + pixel_data_size = padded_row_size * height + file_size = 14 + 40 + pixel_data_size + + header = struct.pack( + "<2sIHHI", + b"BM", + file_size, + 0, + 0, + 14 + 40, + ) + info = struct.pack( + "stack_adjust < 0x100) { emit_al(as, asm_arm_op_sub_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); } else { - asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, as->stack_adjust); - emit_al(as, asm_arm_op_sub_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, ASM_ARM_REG_R8)); + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, as->stack_adjust); + emit_al(as, asm_arm_op_sub_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, REG_TEMP)); } } } @@ -182,8 +182,8 @@ void asm_arm_exit(asm_arm_t *as) { if (as->stack_adjust < 0x100) { emit_al(as, asm_arm_op_add_imm(ASM_ARM_REG_SP, ASM_ARM_REG_SP, as->stack_adjust)); } else { - asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, as->stack_adjust); - emit_al(as, asm_arm_op_add_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, ASM_ARM_REG_R8)); + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, as->stack_adjust); + emit_al(as, asm_arm_op_add_reg(ASM_ARM_REG_SP, ASM_ARM_REG_SP, REG_TEMP)); } } @@ -293,10 +293,10 @@ void asm_arm_orr_reg_reg_reg(asm_arm_t *as, uint rd, uint rn, uint rm) { void asm_arm_mov_reg_local_addr(asm_arm_t *as, uint rd, int local_num) { if (local_num >= 0x40) { - // mov r8, #local_num*4 - // add rd, sp, r8 - asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, local_num << 2); - emit_al(as, asm_arm_op_add_reg(rd, ASM_ARM_REG_SP, ASM_ARM_REG_R8)); + // mov temp, #local_num*4 + // add rd, sp, temp + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, local_num << 2); + emit_al(as, asm_arm_op_add_reg(rd, ASM_ARM_REG_SP, REG_TEMP)); } else { // add rd, sp, #local_num*4 emit_al(as, asm_arm_op_add_imm(rd, ASM_ARM_REG_SP, local_num << 2)); @@ -333,14 +333,22 @@ void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs) { emit_al(as, 0x1a00050 | (rd << 12) | (rs << 8) | rd); } -void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { - // ldr rd, [rn, #off] - emit_al(as, 0x5900000 | (rn << 16) | (rd << 12) | byte_offset); +void asm_arm_ldr_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { + if (byte_offset < 0x1000) { + // ldr rd, [rn, #off] + emit_al(as, 0x5900000 | (rn << 16) | (rd << 12) | byte_offset); + } else { + // mov temp, #off + // ldr rd, [rn, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x7900000 | (rn << 16) | (rd << 12) | REG_TEMP); + } } -void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn) { - // ldrh rd, [rn] - emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12)); +void asm_arm_ldrh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { + // ldrh doesn't support scaled register index + emit_al(as, 0x1a00080 | (REG_TEMP << 12) | rn); // mov temp, rn, lsl #1 + emit_al(as, 0x19000b0 | (rm << 16) | (rd << 12) | REG_TEMP); // ldrh rd, [rm, temp]; } void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { @@ -348,31 +356,69 @@ void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offs // ldrh rd, [rn, #off] emit_al(as, 0x1d000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf)); } else { - // mov r8, #off - // ldrh rd, [rn, r8] - asm_arm_mov_reg_i32_optimised(as, ASM_ARM_REG_R8, byte_offset); - emit_al(as, 0x19000b0 | (rn << 16) | (rd << 12) | ASM_ARM_REG_R8); + // mov temp, #off + // ldrh rd, [rn, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x19000b0 | (rn << 16) | (rd << 12) | REG_TEMP); } } -void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn) { - // ldrb rd, [rn] - emit_al(as, 0x5d00000 | (rn << 16) | (rd << 12)); +void asm_arm_ldrb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { + // ldrb rd, [rm, rn] + emit_al(as, 0x7d00000 | (rm << 16) | (rd << 12) | rn); +} + +void asm_arm_ldrb_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { + if (byte_offset < 0x1000) { + // ldrb rd, [rn, #off] + emit_al(as, 0x5d00000 | (rn << 16) | (rd << 12) | byte_offset); + } else { + // mov temp, #off + // ldrb rd, [rn, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x7d00000 | (rn << 16) | (rd << 12) | REG_TEMP); + } } -void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset) { - // str rd, [rm, #off] - emit_al(as, 0x5800000 | (rm << 16) | (rd << 12) | byte_offset); +void asm_arm_ldr_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { + // ldr rd, [rm, rn, lsl #2] + emit_al(as, 0x7900100 | (rm << 16) | (rd << 12) | rn); } -void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm) { - // strh rd, [rm] - emit_al(as, 0x1c000b0 | (rm << 16) | (rd << 12)); +void asm_arm_str_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset) { + if (byte_offset < 0x1000) { + // str rd, [rm, #off] + emit_al(as, 0x5800000 | (rm << 16) | (rd << 12) | byte_offset); + } else { + // mov temp, #off + // str rd, [rm, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x7800000 | (rm << 16) | (rd << 12) | REG_TEMP); + } } -void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm) { - // strb rd, [rm] - emit_al(as, 0x5c00000 | (rm << 16) | (rd << 12)); +void asm_arm_strh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset) { + if (byte_offset < 0x100) { + // strh rd, [rn, #off] + emit_al(as, 0x1c000b0 | (rn << 16) | (rd << 12) | ((byte_offset & 0xf0) << 4) | (byte_offset & 0xf)); + } else { + // mov temp, #off + // strh rd, [rn, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x18000b0 | (rn << 16) | (rd << 12) | REG_TEMP); + } +} + +void asm_arm_strb_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset) { + if (byte_offset < 0x1000) { + // strb rd, [rm, #off] + emit_al(as, 0x5c00000 | (rm << 16) | (rd << 12) | byte_offset); + } else { + // mov temp, #off + // strb rd, [rm, temp] + asm_arm_mov_reg_i32_optimised(as, REG_TEMP, byte_offset); + emit_al(as, 0x7c00000 | (rm << 16) | (rd << 12) | REG_TEMP); + } } void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { @@ -382,8 +428,8 @@ void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { // strh doesn't support scaled register index - emit_al(as, 0x1a00080 | (ASM_ARM_REG_R8 << 12) | rn); // mov r8, rn, lsl #1 - emit_al(as, 0x18000b0 | (rm << 16) | (rd << 12) | ASM_ARM_REG_R8); // strh rd, [rm, r8] + emit_al(as, 0x1a00080 | (REG_TEMP << 12) | rn); // mov temp, rn, lsl #1 + emit_al(as, 0x18000b0 | (rm << 16) | (rd << 12) | REG_TEMP); // strh rd, [rm, temp] } void asm_arm_strb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn) { @@ -398,7 +444,7 @@ void asm_arm_bcc_label(asm_arm_t *as, int cond, uint label) { rel -= 8; // account for instruction prefetch, PC is 8 bytes ahead of this instruction rel >>= 2; // in ARM mode the branch target is 32-bit aligned, so the 2 LSB are omitted - if (SIGNED_FIT24(rel)) { + if (MP_FIT_SIGNED(24, rel)) { emit(as, cond | 0xa000000 | (rel & 0xffffff)); } else { printf("asm_arm_bcc: branch does not fit in 24 bits\n"); diff --git a/py/asmarm.h b/py/asmarm.h index a0e057fce4450..f3fd586a37530 100644 --- a/py/asmarm.h +++ b/py/asmarm.h @@ -109,13 +109,18 @@ void asm_arm_lsr_reg_reg(asm_arm_t *as, uint rd, uint rs); void asm_arm_asr_reg_reg(asm_arm_t *as, uint rd, uint rs); // memory -void asm_arm_ldr_reg_reg(asm_arm_t *as, uint rd, uint rn, uint byte_offset); -void asm_arm_ldrh_reg_reg(asm_arm_t *as, uint rd, uint rn); +void asm_arm_ldr_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset); void asm_arm_ldrh_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset); -void asm_arm_ldrb_reg_reg(asm_arm_t *as, uint rd, uint rn); -void asm_arm_str_reg_reg(asm_arm_t *as, uint rd, uint rm, uint byte_offset); -void asm_arm_strh_reg_reg(asm_arm_t *as, uint rd, uint rm); -void asm_arm_strb_reg_reg(asm_arm_t *as, uint rd, uint rm); +void asm_arm_ldrb_reg_reg_offset(asm_arm_t *as, uint rd, uint rn, uint byte_offset); +void asm_arm_str_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset); +void asm_arm_strh_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset); +void asm_arm_strb_reg_reg_offset(asm_arm_t *as, uint rd, uint rm, uint byte_offset); + +// load from array +void asm_arm_ldr_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); +void asm_arm_ldrh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); +void asm_arm_ldrb_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); + // store to array void asm_arm_str_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); void asm_arm_strh_reg_reg_reg(asm_arm_t *as, uint rd, uint rm, uint rn); @@ -160,12 +165,12 @@ void asm_arm_bx_reg(asm_arm_t *as, uint reg_src); // Holds a pointer to mp_fun_table #define REG_FUN_TABLE ASM_ARM_REG_FUN_TABLE -#define ASM_T asm_arm_t -#define ASM_END_PASS asm_arm_end_pass -#define ASM_ENTRY asm_arm_entry -#define ASM_EXIT asm_arm_exit +#define ASM_T asm_arm_t +#define ASM_END_PASS asm_arm_end_pass +#define ASM_ENTRY(as, num_locals, name) asm_arm_entry((as), (num_locals)) +#define ASM_EXIT asm_arm_exit -#define ASM_JUMP asm_arm_b_label +#define ASM_JUMP asm_arm_b_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ do { \ asm_arm_cmp_reg_i8(as, reg, 0); \ @@ -203,18 +208,28 @@ void asm_arm_bx_reg(asm_arm_t *as, uint reg_src); #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_arm_sub_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_arm_mul_reg_reg_reg((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_arm_ldrb_reg_reg((as), (reg_dest), (reg_base)) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_arm_ldrh_reg_reg((as), (reg_dest), (reg_base)) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_arm_ldrh_reg_reg_offset((as), (reg_dest), (reg_base), 2 * (uint16_offset)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_arm_ldr_reg_reg((as), (reg_dest), (reg_base), 0) - -#define ASM_STORE_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_str_reg_reg((as), (reg_dest), (reg_base), 4 * (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_value, reg_base) asm_arm_strb_reg_reg((as), (reg_value), (reg_base)) -#define ASM_STORE16_REG_REG(as, reg_value, reg_base) asm_arm_strh_reg_reg((as), (reg_value), (reg_base)) -#define ASM_STORE32_REG_REG(as, reg_value, reg_base) asm_arm_str_reg_reg((as), (reg_value), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_arm_ldrb_reg_reg_offset((as), (reg_dest), (reg_base), (byte_offset)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, halfword_offset) asm_arm_ldrh_reg_reg_offset((as), (reg_dest), (reg_base), 2 * (halfword_offset)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_arm_ldr_reg_reg_offset((as), (reg_dest), (reg_base), 4 * (word_offset)) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_value, reg_base, word_offset) ASM_STORE32_REG_REG_OFFSET((as), (reg_value), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_value, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_value), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_value, reg_base, byte_offset) asm_arm_strb_reg_reg_offset((as), (reg_value), (reg_base), (byte_offset)) +#define ASM_STORE16_REG_REG(as, reg_value, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_value), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_value, reg_base, halfword_offset) asm_arm_strh_reg_reg_offset((as), (reg_value), (reg_base), 2 * (halfword_offset)) +#define ASM_STORE32_REG_REG(as, reg_value, reg_base) ASM_STORE32_REG_REG_OFFSET((as), (reg_value), (reg_base), 0) +#define ASM_STORE32_REG_REG_OFFSET(as, reg_value, reg_base, word_offset) asm_arm_str_reg_reg_offset((as), (reg_value), (reg_base), 4 * (word_offset)) + +#define ASM_LOAD8_REG_REG_REG(as, reg_dest, reg_base, reg_index) asm_arm_ldrb_reg_reg_reg((as), (reg_dest), (reg_base), (reg_index)) +#define ASM_LOAD16_REG_REG_REG(as, reg_dest, reg_base, reg_index) asm_arm_ldrh_reg_reg_reg((as), (reg_dest), (reg_base), (reg_index)) +#define ASM_LOAD32_REG_REG_REG(as, reg_dest, reg_base, reg_index) asm_arm_ldr_reg_reg_reg((as), (reg_dest), (reg_base), (reg_index)) +#define ASM_STORE8_REG_REG_REG(as, reg_val, reg_base, reg_index) asm_arm_strb_reg_reg_reg((as), (reg_val), (reg_base), (reg_index)) +#define ASM_STORE16_REG_REG_REG(as, reg_val, reg_base, reg_index) asm_arm_strh_reg_reg_reg((as), (reg_val), (reg_base), (reg_index)) +#define ASM_STORE32_REG_REG_REG(as, reg_val, reg_base, reg_index) asm_arm_str_reg_reg_reg((as), (reg_val), (reg_base), (reg_index)) #endif // GENERIC_ASM_API diff --git a/py/asmbase.c b/py/asmbase.c index 3fce543a7f485..07dbf4430f9db 100644 --- a/py/asmbase.c +++ b/py/asmbase.c @@ -53,7 +53,7 @@ void mp_asm_base_start_pass(mp_asm_base_t *as, int pass) { } else { // allocating executable RAM is platform specific MP_PLAT_ALLOC_EXEC(as->code_offset, (void **)&as->code_base, &as->code_size); - assert(as->code_base != NULL); + assert(as->code_size == 0 || as->code_base != NULL); } as->pass = pass; as->suppress = false; @@ -102,7 +102,7 @@ void mp_asm_base_label_assign(mp_asm_base_t *as, size_t label) { // align must be a multiple of 2 void mp_asm_base_align(mp_asm_base_t *as, unsigned int align) { - as->code_offset = (as->code_offset + align - 1) & (~(align - 1)); + as->code_offset = (as->code_offset + align - 1) & (~(size_t)(align - 1)); } // this function assumes a little endian machine diff --git a/py/asmrv32.c b/py/asmrv32.c index c24d05a1384d4..1d0cea6c02616 100644 --- a/py/asmrv32.c +++ b/py/asmrv32.c @@ -36,6 +36,8 @@ #if MICROPY_EMIT_RV32 #include "py/asmrv32.h" +#include "py/mpstate.h" +#include "py/persistentcode.h" #if MICROPY_DEBUG_VERBOSE #define DEBUG_PRINT (1) @@ -450,18 +452,24 @@ void asm_rv32_emit_mov_reg_local_addr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t asm_rv32_opcode_cadd(state, rd, ASM_RV32_REG_SP); } -void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { - mp_int_t scaled_offset = offset * sizeof(ASM_WORD_SIZE); +static const uint8_t RV32_LOAD_OPCODE_TABLE[3] = { + 0x04, 0x05, 0x02 +}; - if (scaled_offset >= 0 && RV32_IS_IN_C_REGISTER_WINDOW(rd) && RV32_IS_IN_C_REGISTER_WINDOW(rs) && FIT_UNSIGNED(scaled_offset, 6)) { +void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, int32_t offset, mp_uint_t operation_size) { + assert(operation_size <= 2 && "Operation size value out of range."); + + int32_t scaled_offset = offset << operation_size; + + if (scaled_offset >= 0 && operation_size == 2 && RV32_IS_IN_C_REGISTER_WINDOW(rd) && RV32_IS_IN_C_REGISTER_WINDOW(rs) && MP_FIT_UNSIGNED(6, scaled_offset)) { // c.lw rd', offset(rs') asm_rv32_opcode_clw(state, RV32_MAP_IN_C_REGISTER_WINDOW(rd), RV32_MAP_IN_C_REGISTER_WINDOW(rs), scaled_offset); return; } - if (FIT_SIGNED(scaled_offset, 12)) { - // lw rd, offset(rs) - asm_rv32_opcode_lw(state, rd, rs, scaled_offset); + if (MP_FIT_SIGNED(12, scaled_offset)) { + // lbu|lhu|lw rd, offset(rs) + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x03, RV32_LOAD_OPCODE_TABLE[operation_size], rd, rs, scaled_offset)); return; } @@ -469,12 +477,12 @@ void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_ mp_uint_t lower = 0; split_immediate(scaled_offset, &upper, &lower); - // lui rd, HI(offset) ; Or c.lui if possible - // c.add rd, rs - // lw rd, LO(offset)(rd) + // lui rd, HI(offset) ; Or c.lui if possible + // c.add rd, rs + // lbu|lhu|lw rd, LO(offset)(rd) load_upper_immediate(state, rd, upper); asm_rv32_opcode_cadd(state, rd, rs); - asm_rv32_opcode_lw(state, rd, rd, lower); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x03, RV32_LOAD_OPCODE_TABLE[operation_size], rd, rd, lower)); } void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label) { @@ -497,12 +505,20 @@ void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label) { asm_rv32_opcode_jalr(state, ASM_RV32_REG_ZERO, REG_TEMP2, lower); } -void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { - mp_int_t scaled_offset = offset * ASM_WORD_SIZE; +void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, int32_t offset, mp_uint_t operation_size) { + assert(operation_size <= 2 && "Operation size value out of range."); - if (FIT_SIGNED(scaled_offset, 12)) { - // sw rd, offset(rs) - asm_rv32_opcode_sw(state, rd, rs, scaled_offset); + int32_t scaled_offset = offset << operation_size; + + if (scaled_offset >= 0 && operation_size == 2 && RV32_IS_IN_C_REGISTER_WINDOW(rd) && RV32_IS_IN_C_REGISTER_WINDOW(rs) && MP_FIT_UNSIGNED(6, scaled_offset)) { + // c.sw rd', offset(rs') + asm_rv32_opcode_csw(state, RV32_MAP_IN_C_REGISTER_WINDOW(rd), RV32_MAP_IN_C_REGISTER_WINDOW(rs), scaled_offset); + return; + } + + if (MP_FIT_SIGNED(12, scaled_offset)) { + // sb|sh|sw rd, offset(rs) + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0x23, operation_size, rs, rd, scaled_offset)); return; } @@ -510,12 +526,12 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint mp_uint_t lower = 0; split_immediate(scaled_offset, &upper, &lower); - // lui temporary, HI(offset) ; Or c.lui if possible - // c.add temporary, rs - // sw rd, LO(offset)(temporary) + // lui temporary, HI(offset) ; Or c.lui if possible + // c.add temporary, rs + // sb|sh|sw rd, LO(offset)(temporary) load_upper_immediate(state, REG_TEMP2, upper); asm_rv32_opcode_cadd(state, REG_TEMP2, rs); - asm_rv32_opcode_sw(state, rd, REG_TEMP2, lower); + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_S(0x23, operation_size, REG_TEMP2, rd, lower)); } void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t label) { @@ -530,27 +546,6 @@ void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t labe asm_rv32_opcode_addi(state, rd, rd, lower); } -void asm_rv32_emit_load16_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset) { - mp_int_t scaled_offset = offset * sizeof(uint16_t); - - if (FIT_SIGNED(scaled_offset, 12)) { - // lhu rd, offset(rs) - asm_rv32_opcode_lhu(state, rd, rs, scaled_offset); - return; - } - - mp_uint_t upper = 0; - mp_uint_t lower = 0; - split_immediate(scaled_offset, &upper, &lower); - - // lui rd, HI(offset) ; Or c.lui if possible - // c.add rd, rs - // lhu rd, LO(offset)(rd) - load_upper_immediate(state, rd, upper); - asm_rv32_opcode_cadd(state, rd, rs); - asm_rv32_opcode_lhu(state, rd, rd, lower); -} - void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) { if (rs == rd) { // c.li rd, 0 @@ -562,14 +557,39 @@ void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs) asm_rv32_opcode_xor(state, rd, rd, rs); } +static bool asm_rv32_allow_zba_opcodes(void) { + return asm_rv32_allowed_extensions() & RV32_EXT_ZBA; +} + +static void asm_rv32_fix_up_scaled_reg_reg_reg(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size) { + assert(operation_size <= 2 && "Operation size value out of range."); + + if (operation_size > 0 && asm_rv32_allow_zba_opcodes()) { + // sh{1,2}add rs1, rs2, rs1 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 1 << operation_size, 0x10, rs1, rs2, rs1)); + } else { + if (operation_size > 0) { + asm_rv32_opcode_cslli(state, rs2, operation_size); + } + asm_rv32_opcode_cadd(state, rs1, rs2); + } +} + +void asm_rv32_emit_load_reg_reg_reg(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size) { + asm_rv32_fix_up_scaled_reg_reg_reg(state, rs1, rs2, operation_size); + asm_rv32_emit_load_reg_reg_offset(state, rd, rs1, 0, operation_size); +} + +void asm_rv32_emit_store_reg_reg_reg(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size) { + asm_rv32_fix_up_scaled_reg_reg_reg(state, rs1, rs2, operation_size); + asm_rv32_emit_store_reg_reg_offset(state, rd, rs1, 0, operation_size); +} + void asm_rv32_meta_comparison_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd) { - // c.li rd, 1 ; - // beq rs1, rs2, 6 ; PC + 0 - // c.li rd, 0 ; PC + 4 - // ... ; PC + 6 - asm_rv32_opcode_cli(state, rd, 1); - asm_rv32_opcode_beq(state, rs1, rs2, 6); - asm_rv32_opcode_cli(state, rd, 0); + // sub rd, rs1, rs2 + // sltiu rd, rd, 1 + asm_rv32_opcode_sub(state, rd, rs1, rs2); + asm_rv32_opcode_sltiu(state, rd, rd, 1); } void asm_rv32_meta_comparison_ne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd) { @@ -580,26 +600,15 @@ void asm_rv32_meta_comparison_ne(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2 } void asm_rv32_meta_comparison_lt(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd, bool unsigned_comparison) { - // slt(u) rd, rs1, rs2 - if (unsigned_comparison) { - asm_rv32_opcode_sltu(state, rd, rs1, rs2); - } else { - asm_rv32_opcode_slt(state, rd, rs1, rs2); - } + // slt|sltu rd, rs1, rs2 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, (0x02 | (unsigned_comparison ? 1 : 0)), 0x00, rd, rs1, rs2)); } void asm_rv32_meta_comparison_le(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd, bool unsigned_comparison) { - // c.li rd, 1 ; - // beq rs1, rs2, 8 ; PC + 0 - // slt(u) rd, rs1, rs2 ; PC + 4 - // ... ; PC + 8 - asm_rv32_opcode_cli(state, rd, 1); - asm_rv32_opcode_beq(state, rs1, rs2, 8); - if (unsigned_comparison) { - asm_rv32_opcode_sltu(state, rd, rs1, rs2); - } else { - asm_rv32_opcode_slt(state, rd, rs1, rs2); - } + // slt[u] rd, rs2, rs1 + // xori rd, rd, 1 + asm_rv32_meta_comparison_lt(state, rs2, rs1, rd, unsigned_comparison); + asm_rv32_opcode_xori(state, rd, rd, 1); } #endif // MICROPY_EMIT_RV32 diff --git a/py/asmrv32.h b/py/asmrv32.h index b09f48eb12f66..6f709daa11bce 100644 --- a/py/asmrv32.h +++ b/py/asmrv32.h @@ -122,6 +122,18 @@ typedef struct _asm_rv32_t { mp_uint_t locals_stack_offset; } asm_rv32_t; +enum { + RV32_EXT_NONE = 0, + RV32_EXT_ZBA = 1 << 0, + + RV32_EXT_ALL = RV32_EXT_ZBA +}; + +typedef struct _asm_rv32_backend_options_t { + // This is a bitmask holding a combination of RV32_EXT_* entries. + uint8_t allowed_extensions; +} asm_rv32_backend_options_t; + void asm_rv32_entry(asm_rv32_t *state, mp_uint_t locals); void asm_rv32_exit(asm_rv32_t *state); void asm_rv32_end_pass(asm_rv32_t *state); @@ -583,6 +595,24 @@ static inline void asm_rv32_opcode_remu(asm_rv32_t *state, mp_uint_t rd, mp_uint asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x07, 0x01, rd, rs1, rs2)); } +// SH1ADD RD, RS1, RS2 +static inline void asm_rv32_opcode_sh1add(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0010000 ..... ..... 010 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x02, 0x10, rd, rs1, rs2)); +} + +// SH2ADD RD, RS1, RS2 +static inline void asm_rv32_opcode_sh2add(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0010000 ..... ..... 100 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x04, 0x10, rd, rs1, rs2)); +} + +// SH3ADD RD, RS1, RS2 +static inline void asm_rv32_opcode_sh3add(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { + // R: 0010000 ..... ..... 110 ..... 0110011 + asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_R(0x33, 0x06, 0x10, rd, rs1, rs2)); +} + // SLL RD, RS1, RS2 static inline void asm_rv32_opcode_sll(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2) { // R: 0000000 ..... ..... 001 ..... 0110011 @@ -679,6 +709,19 @@ static inline void asm_rv32_opcode_xori(asm_rv32_t *state, mp_uint_t rd, mp_uint asm_rv32_emit_word_opcode(state, RV32_ENCODE_TYPE_I(0x13, 0x04, rd, rs, immediate)); } +#define MICROPY_RV32_EXTENSIONS \ + (MICROPY_EMIT_RV32_ZBA ? RV32_EXT_ZBA : 0) + +static inline uint8_t asm_rv32_allowed_extensions(void) { + uint8_t extensions = MICROPY_RV32_EXTENSIONS; + #if MICROPY_DYNAMIC_COMPILER + if (mp_dynamic_compiler.backend_options != NULL) { + extensions |= ((asm_rv32_backend_options_t *)mp_dynamic_compiler.backend_options)->allowed_extensions; + } + #endif + return extensions; +} + #define ASM_WORD_SIZE (4) #define ASM_HALFWORD_SIZE (2) @@ -702,6 +745,8 @@ void asm_rv32_meta_comparison_lt(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2 void asm_rv32_meta_comparison_le(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t rd, bool unsigned_comparison); void asm_rv32_emit_optimised_load_immediate(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate); +void asm_rv32_emit_load_reg_reg_reg(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size); +void asm_rv32_emit_store_reg_reg_reg(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t operation_size); #ifdef GENERIC_ASM_API @@ -709,17 +754,16 @@ void asm_rv32_emit_call_ind(asm_rv32_t *state, mp_uint_t index); void asm_rv32_emit_jump(asm_rv32_t *state, mp_uint_t label); void asm_rv32_emit_jump_if_reg_eq(asm_rv32_t *state, mp_uint_t rs1, mp_uint_t rs2, mp_uint_t label); void asm_rv32_emit_jump_if_reg_nonzero(asm_rv32_t *state, mp_uint_t rs, mp_uint_t label); -void asm_rv32_emit_load16_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset); -void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, mp_int_t offset); +void asm_rv32_emit_load_reg_reg_offset(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs, int32_t offset, mp_uint_t operation_size); void asm_rv32_emit_mov_local_reg(asm_rv32_t *state, mp_uint_t local, mp_uint_t rs); void asm_rv32_emit_mov_reg_local_addr(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local); void asm_rv32_emit_mov_reg_local(asm_rv32_t *state, mp_uint_t rd, mp_uint_t local); void asm_rv32_emit_mov_reg_pcrel(asm_rv32_t *state, mp_uint_t rd, mp_uint_t label); void asm_rv32_emit_optimised_xor(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs); -void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_uint_t base, mp_int_t offset); +void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_uint_t base, int32_t offset, mp_uint_t operation_size); #define ASM_T asm_rv32_t -#define ASM_ENTRY(state, labels) asm_rv32_entry(state, labels) +#define ASM_ENTRY(state, labels, name) asm_rv32_entry(state, labels) #define ASM_EXIT(state) asm_rv32_exit(state) #define ASM_END_PASS(state) asm_rv32_end_pass(state) @@ -732,12 +776,13 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_ #define ASM_JUMP_IF_REG_NONZERO(state, rs, label, bool_test) asm_rv32_emit_jump_if_reg_nonzero(state, rs, label) #define ASM_JUMP_IF_REG_ZERO(state, rs, label, bool_test) asm_rv32_emit_jump_if_reg_eq(state, rs, ASM_RV32_REG_ZERO, label) #define ASM_JUMP_REG(state, rs) asm_rv32_opcode_cjr(state, rs) -#define ASM_LOAD16_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load16_reg_reg_offset(state, rd, rs, offset) -#define ASM_LOAD16_REG_REG(state, rd, rs) asm_rv32_opcode_lhu(state, rd, rs, 0) -#define ASM_LOAD32_REG_REG(state, rd, rs) ASM_LOAD_REG_REG_OFFSET(state, rd, rs, 0) -#define ASM_LOAD8_REG_REG(state, rd, rs) asm_rv32_opcode_lbu(state, rd, rs, 0) -#define ASM_LOAD_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset) -#define ASM_LOAD_REG_REG(state, rd, rs) ASM_LOAD32_REG_REG(state, rd, rs) +#define ASM_LOAD_REG_REG_OFFSET(state, rd, rs, offset) ASM_LOAD32_REG_REG_OFFSET(state, rd, rs, offset) +#define ASM_LOAD8_REG_REG(state, rd, rs) ASM_LOAD8_REG_REG_OFFSET(state, rd, rs, 0) +#define ASM_LOAD16_REG_REG(state, rd, rs) ASM_LOAD16_REG_REG_OFFSET(state, rd, rs, 0) +#define ASM_LOAD32_REG_REG(state, rd, rs) ASM_LOAD32_REG_REG_OFFSET(state, rd, rs, 0) +#define ASM_LOAD8_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset, 0) +#define ASM_LOAD16_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset, 1) +#define ASM_LOAD32_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_load_reg_reg_offset(state, rd, rs, offset, 2) #define ASM_LSL_REG_REG(state, rd, rs) asm_rv32_opcode_sll(state, rd, rd, rs) #define ASM_LSR_REG_REG(state, rd, rs) asm_rv32_opcode_srl(state, rd, rd, rs) #define ASM_MOV_LOCAL_REG(state, local, rs) asm_rv32_emit_mov_local_reg(state, local, rs) @@ -750,14 +795,22 @@ void asm_rv32_emit_store_reg_reg_offset(asm_rv32_t *state, mp_uint_t source, mp_ #define ASM_NEG_REG(state, rd) asm_rv32_opcode_sub(state, rd, ASM_RV32_REG_ZERO, rd) #define ASM_NOT_REG(state, rd) asm_rv32_opcode_xori(state, rd, rd, -1) #define ASM_OR_REG_REG(state, rd, rs) asm_rv32_opcode_or(state, rd, rd, rs) -#define ASM_STORE16_REG_REG(state, rs1, rs2) asm_rv32_opcode_sh(state, rs1, rs2, 0) -#define ASM_STORE32_REG_REG(state, rs1, rs2) ASM_STORE_REG_REG_OFFSET(state, rs1, rs2, 0) -#define ASM_STORE8_REG_REG(state, rs1, rs2) asm_rv32_opcode_sb(state, rs1, rs2, 0) -#define ASM_STORE_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset) -#define ASM_STORE_REG_REG(state, rs1, rs2) ASM_STORE32_REG_REG(state, rs1, rs2) +#define ASM_STORE_REG_REG_OFFSET(state, rd, rs, offset) ASM_STORE32_REG_REG_OFFSET(state, rd, rs, offset) +#define ASM_STORE8_REG_REG(state, rs1, rs2) ASM_STORE8_REG_REG_OFFSET(state, rs1, rs2, 0) +#define ASM_STORE16_REG_REG(state, rs1, rs2) ASM_STORE16_REG_REG_OFFSET(state, rs1, rs2, 0) +#define ASM_STORE32_REG_REG(state, rs1, rs2) ASM_STORE32_REG_REG_OFFSET(state, rs1, rs2, 0) +#define ASM_STORE8_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset, 0) +#define ASM_STORE16_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset, 1) +#define ASM_STORE32_REG_REG_OFFSET(state, rd, rs, offset) asm_rv32_emit_store_reg_reg_offset(state, rd, rs, offset, 2) #define ASM_SUB_REG_REG(state, rd, rs) asm_rv32_opcode_sub(state, rd, rd, rs) #define ASM_XOR_REG_REG(state, rd, rs) asm_rv32_emit_optimised_xor(state, rd, rs) #define ASM_CLR_REG(state, rd) +#define ASM_LOAD8_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_load_reg_reg_reg(state, rd, rs1, rs2, 0) +#define ASM_LOAD16_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_load_reg_reg_reg(state, rd, rs1, rs2, 1) +#define ASM_LOAD32_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_load_reg_reg_reg(state, rd, rs1, rs2, 2) +#define ASM_STORE8_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_store_reg_reg_reg(state, rd, rs1, rs2, 0) +#define ASM_STORE16_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_store_reg_reg_reg(state, rd, rs1, rs2, 1) +#define ASM_STORE32_REG_REG_REG(state, rd, rs1, rs2) asm_rv32_emit_store_reg_reg_reg(state, rd, rs1, rs2, 2) #endif diff --git a/py/asmthumb.c b/py/asmthumb.c index 420815e80269a..58cc7aea88085 100644 --- a/py/asmthumb.c +++ b/py/asmthumb.c @@ -37,7 +37,6 @@ #include "py/asmthumb.h" #include "py/misc.h" -#define UNSIGNED_FIT5(x) ((uint32_t)(x) < 32) #define UNSIGNED_FIT7(x) ((uint32_t)(x) < 128) #define UNSIGNED_FIT8(x) (((x) & 0xffffff00) == 0) #define UNSIGNED_FIT16(x) (((x) & 0xffff0000) == 0) @@ -52,12 +51,6 @@ #define OP_SUB_W_RRI_HI(reg_src) (0xf2a0 | (reg_src)) #define OP_SUB_W_RRI_LO(reg_dest, imm11) ((imm11 << 4 & 0x7000) | reg_dest << 8 | (imm11 & 0xff)) -#define OP_LDR_W_HI(reg_base) (0xf8d0 | (reg_base)) -#define OP_LDR_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12)) - -#define OP_LDRH_W_HI(reg_base) (0xf8b0 | (reg_base)) -#define OP_LDRH_W_LO(reg_dest, imm12) ((reg_dest) << 12 | (imm12)) - static inline byte *asm_thumb_get_cur_to_write_bytes(asm_thumb_t *as, int n) { return mp_asm_base_get_cur_to_write_bytes(&as->base, n); } @@ -274,9 +267,8 @@ bool asm_thumb_b_n_label(asm_thumb_t *as, uint label) { #define OP_BCC_N(cond, byte_offset) (0xd000 | ((cond) << 8) | (((byte_offset) >> 1) & 0x00ff)) -// all these bit-arithmetic operations need coverage testing! -#define OP_BCC_W_HI(cond, byte_offset) (0xf000 | ((cond) << 6) | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 14) & 0x003f)) -#define OP_BCC_W_LO(byte_offset) (0x8000 | ((byte_offset) & 0x2000) | (((byte_offset) >> 1) & 0x0fff)) +#define OP_BCC_W_HI(cond, byte_offset) (0xf000 | ((cond) << 6) | (((byte_offset) >> 10) & 0x0400) | (((byte_offset) >> 12) & 0x003f)) +#define OP_BCC_W_LO(byte_offset) (0x8000 | (((byte_offset) >> 5) & 0x2000) | (((byte_offset) >> 8) & 0x0800) | (((byte_offset) >> 1) & 0x07ff)) bool asm_thumb_bcc_nw_label(asm_thumb_t *as, int cond, uint label, bool wide) { mp_uint_t dest = get_label_dest(as, label); @@ -432,11 +424,6 @@ void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label) { asm_thumb_add_reg_reg(as, rlo_dest, ASM_THUMB_REG_R15); // 2 bytes } -// ARMv7-M only -static inline void asm_thumb_ldr_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { - asm_thumb_op32(as, OP_LDR_W_HI(reg_base), OP_LDR_W_LO(reg_dest, word_offset * 4)); -} - // emits code for: reg_dest = reg_base + offset << offset_shift static void asm_thumb_add_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint reg_base, uint offset, uint offset_shift) { if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8) { @@ -450,12 +437,12 @@ static void asm_thumb_add_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint re asm_thumb_lsl_rlo_rlo_i5(as, reg_dest, reg_dest, offset_shift); asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_base); } else if (reg_dest != reg_base) { - asm_thumb_mov_rlo_i16(as, reg_dest, offset << offset_shift); - asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_dest); + asm_thumb_mov_reg_i32_optimised(as, reg_dest, offset << offset_shift); + asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_base); } else { uint reg_other = reg_dest ^ 7; asm_thumb_op16(as, OP_PUSH_RLIST((1 << reg_other))); - asm_thumb_mov_rlo_i16(as, reg_other, offset << offset_shift); + asm_thumb_mov_reg_i32_optimised(as, reg_other, offset << offset_shift); asm_thumb_add_rlo_rlo_rlo(as, reg_dest, reg_dest, reg_other); asm_thumb_op16(as, OP_POP_RLIST((1 << reg_other))); } @@ -464,30 +451,50 @@ static void asm_thumb_add_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint re } } -void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset) { - if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(word_offset)) { - asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_base, word_offset); - } else if (asm_thumb_allow_armv7m(as)) { - asm_thumb_ldr_reg_reg_i12(as, reg_dest, reg_base, word_offset); +#define OP_LDR_STR_W_HI(operation_size, reg) ((0xf880 | (operation_size) << 5) | (reg)) +#define OP_LDR_STR_W_LO(reg, imm12) (((reg) << 12) | (imm12)) + +#define OP_LDR 0x01 +#define OP_STR 0x00 + +#define OP_LDR_W 0x10 +#define OP_STR_W 0x00 + +static const uint8_t OP_LDR_STR_TABLE[3] = { + 0x0E, 0x10, 0x0C +}; + +void asm_thumb_load_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint reg_base, uint offset, uint operation_size) { + assert(operation_size <= 2 && "Operation size out of range."); + + if (MP_FIT_UNSIGNED(5, offset) && (reg_dest < ASM_THUMB_REG_R8) && (reg_base < ASM_THUMB_REG_R8)) { + // Can use T1 encoding + asm_thumb_op16(as, ((OP_LDR_STR_TABLE[operation_size] | OP_LDR) << 11) | (offset << 6) | (reg_base << 3) | reg_dest); + } else if (asm_thumb_allow_armv7m(as) && MP_FIT_UNSIGNED(12, offset << operation_size)) { + // Can use T3 encoding + asm_thumb_op32(as, (OP_LDR_STR_W_HI(operation_size, reg_base) | OP_LDR_W), OP_LDR_STR_W_LO(reg_dest, (offset << operation_size))); } else { - asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, word_offset - 31, 2); - asm_thumb_ldr_rlo_rlo_i5(as, reg_dest, reg_dest, 31); + // Must use the generic sequence + asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, offset - 31, operation_size); + asm_thumb_op16(as, ((OP_LDR_STR_TABLE[operation_size] | OP_LDR) << 11) | (31 << 6) | (reg_dest << 3) | (reg_dest)); } } -// ARMv7-M only -static inline void asm_thumb_ldrh_reg_reg_i12(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset) { - asm_thumb_op32(as, OP_LDRH_W_HI(reg_base), OP_LDRH_W_LO(reg_dest, uint16_offset * 2)); -} +void asm_thumb_store_reg_reg_offset(asm_thumb_t *as, uint reg_src, uint reg_base, uint offset, uint operation_size) { + assert(operation_size <= 2 && "Operation size out of range."); -void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset) { - if (reg_dest < ASM_THUMB_REG_R8 && reg_base < ASM_THUMB_REG_R8 && UNSIGNED_FIT5(uint16_offset)) { - asm_thumb_ldrh_rlo_rlo_i5(as, reg_dest, reg_base, uint16_offset); - } else if (asm_thumb_allow_armv7m(as)) { - asm_thumb_ldrh_reg_reg_i12(as, reg_dest, reg_base, uint16_offset); + if (MP_FIT_UNSIGNED(5, offset) && (reg_src < ASM_THUMB_REG_R8) && (reg_base < ASM_THUMB_REG_R8)) { + // Can use T1 encoding + asm_thumb_op16(as, ((OP_LDR_STR_TABLE[operation_size] | OP_STR) << 11) | (offset << 6) | (reg_base << 3) | reg_src); + } else if (asm_thumb_allow_armv7m(as) && MP_FIT_UNSIGNED(12, offset << operation_size)) { + // Can use T3 encoding + asm_thumb_op32(as, (OP_LDR_STR_W_HI(operation_size, reg_base) | OP_STR_W), OP_LDR_STR_W_LO(reg_src, (offset << operation_size))); } else { - asm_thumb_add_reg_reg_offset(as, reg_dest, reg_base, uint16_offset - 31, 1); - asm_thumb_ldrh_rlo_rlo_i5(as, reg_dest, reg_dest, 31); + // Must use the generic sequence + asm_thumb_op16(as, OP_PUSH_RLIST(1 << reg_base)); + asm_thumb_add_reg_reg_offset(as, reg_base, reg_base, offset - 31, operation_size); + asm_thumb_op16(as, ((OP_LDR_STR_TABLE[operation_size] | OP_STR) << 11) | (31 << 6) | (reg_base << 3) | reg_src); + asm_thumb_op16(as, OP_POP_RLIST(1 << reg_base)); } } @@ -495,6 +502,7 @@ void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint r #define OP_BW_HI(byte_offset) (0xf000 | (((byte_offset) >> 12) & 0x07ff)) #define OP_BW_LO(byte_offset) (0xb800 | (((byte_offset) >> 1) & 0x07ff)) +// In Thumb1 mode, this may clobber r1. void asm_thumb_b_label(asm_thumb_t *as, uint label) { mp_uint_t dest = get_label_dest(as, label); mp_int_t rel = dest - as->base.code_offset; @@ -514,19 +522,40 @@ void asm_thumb_b_label(asm_thumb_t *as, uint label) { if (asm_thumb_allow_armv7m(as)) { asm_thumb_op32(as, OP_BW_HI(rel), OP_BW_LO(rel)); } else { + // this code path has to be the same instruction size irrespective of the value of rel + bool need_align = as->base.code_offset & 2u; if (SIGNED_FIT12(rel)) { - // this code path has to be the same number of instructions irrespective of rel asm_thumb_op16(as, OP_B_N(rel)); - } else { asm_thumb_op16(as, ASM_THUMB_OP_NOP); - if (dest != (mp_uint_t)-1) { - // we have an actual branch > 12 bits; this is not handled yet - mp_raise_NotImplementedError(MP_ERROR_TEXT("native method too big")); + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + if (need_align) { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + } + } else { + // do a large jump using: + // (nop) + // ldr r1, [pc, _data] + // add pc, r1 + // _data: .word rel + // + // note: can't use r0 as a temporary because native code can have the return value + // in that register and use a large jump to get to the exit point of the function + + rel -= 2; // account for the "ldr r1, [pc, _data]" + if (need_align) { + asm_thumb_op16(as, ASM_THUMB_OP_NOP); + rel -= 2; // account for this nop } + asm_thumb_ldr_rlo_pcrel_i8(as, ASM_THUMB_REG_R1, 0); + asm_thumb_add_reg_reg(as, ASM_THUMB_REG_R15, ASM_THUMB_REG_R1); + asm_thumb_op16(as, rel & 0xffff); + asm_thumb_op16(as, rel >> 16); } } } +// In Thumb1 mode, this may clobber r1. void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) { mp_uint_t dest = get_label_dest(as, label); mp_int_t rel = dest - as->base.code_offset; @@ -547,8 +576,15 @@ void asm_thumb_bcc_label(asm_thumb_t *as, int cond, uint label) { asm_thumb_op32(as, OP_BCC_W_HI(cond, rel), OP_BCC_W_LO(rel)); } else { // reverse the sense of the branch to jump over a longer branch - asm_thumb_op16(as, OP_BCC_N(cond ^ 1, 0)); + size_t code_offset_start = as->base.code_offset; + byte *c = asm_thumb_get_cur_to_write_bytes(as, 2); asm_thumb_b_label(as, label); + size_t bytes_to_skip = as->base.code_offset - code_offset_start; + uint16_t op = OP_BCC_N(cond ^ 1, bytes_to_skip - 4); + if (c != NULL) { + c[0] = op; + c[1] = op >> 8; + } } } @@ -569,7 +605,7 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel) { void asm_thumb_bl_ind(asm_thumb_t *as, uint fun_id, uint reg_temp) { // Load ptr to function from table, indexed by fun_id, then call it - asm_thumb_ldr_reg_reg_i12_optimised(as, reg_temp, ASM_THUMB_REG_FUN_TABLE, fun_id); + asm_thumb_load_reg_reg_offset(as, reg_temp, ASM_THUMB_REG_FUN_TABLE, fun_id, 2); asm_thumb_op16(as, OP_BLX(reg_temp)); } diff --git a/py/asmthumb.h b/py/asmthumb.h index 0584ed3227aad..cb786694f0b78 100644 --- a/py/asmthumb.h +++ b/py/asmthumb.h @@ -251,6 +251,50 @@ static inline void asm_thumb_bx_reg(asm_thumb_t *as, uint r_src) { asm_thumb_format_5(as, ASM_THUMB_FORMAT_5_BX, 0, r_src); } +// FORMAT 7: load/store with register offset +// FORMAT 8: load/store sign-extended byte/halfword + +#define ASM_THUMB_FORMAT_7_LDR (0x5800) +#define ASM_THUMB_FORMAT_7_STR (0x5000) +#define ASM_THUMB_FORMAT_7_WORD_TRANSFER (0x0000) +#define ASM_THUMB_FORMAT_7_BYTE_TRANSFER (0x0400) +#define ASM_THUMB_FORMAT_8_LDRH (0x5A00) +#define ASM_THUMB_FORMAT_8_STRH (0x5200) + +#define ASM_THUMB_FORMAT_7_8_ENCODE(op, rlo_dest, rlo_base, rlo_index) \ + ((op) | ((rlo_index) << 6) | ((rlo_base) << 3) | ((rlo_dest))) + +static inline void asm_thumb_format_7_8(asm_thumb_t *as, uint op, uint rlo_dest, uint rlo_base, uint rlo_index) { + assert(rlo_dest < ASM_THUMB_REG_R8); + assert(rlo_base < ASM_THUMB_REG_R8); + assert(rlo_index < ASM_THUMB_REG_R8); + asm_thumb_op16(as, ASM_THUMB_FORMAT_7_8_ENCODE(op, rlo_dest, rlo_base, rlo_index)); +} + +static inline void asm_thumb_ldrb_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_7_LDR | ASM_THUMB_FORMAT_7_BYTE_TRANSFER, rlo_dest, rlo_base, rlo_index); +} + +static inline void asm_thumb_ldrh_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_8_LDRH, rlo_dest, rlo_base, rlo_index); +} + +static inline void asm_thumb_ldr_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_7_LDR | ASM_THUMB_FORMAT_7_WORD_TRANSFER, rlo_dest, rlo_base, rlo_index); +} + +static inline void asm_thumb_strb_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_7_STR | ASM_THUMB_FORMAT_7_BYTE_TRANSFER, rlo_src, rlo_base, rlo_index); +} + +static inline void asm_thumb_strh_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_8_STRH, rlo_dest, rlo_base, rlo_index); +} + +static inline void asm_thumb_str_rlo_rlo_rlo(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint rlo_index) { + asm_thumb_format_7_8(as, ASM_THUMB_FORMAT_7_STR | ASM_THUMB_FORMAT_7_WORD_TRANSFER, rlo_src, rlo_base, rlo_index); +} + // FORMAT 9: load/store with immediate offset // For word transfers the offset must be aligned, and >>2 @@ -273,24 +317,6 @@ static inline void asm_thumb_format_9_10(asm_thumb_t *as, uint op, uint rlo_dest asm_thumb_op16(as, ASM_THUMB_FORMAT_9_10_ENCODE(op, rlo_dest, rlo_base, offset)); } -static inline void asm_thumb_str_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint word_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_src, rlo_base, word_offset); -} -static inline void asm_thumb_strb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint byte_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_STR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER, rlo_src, rlo_base, byte_offset); -} -static inline void asm_thumb_strh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_src, uint rlo_base, uint uint16_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_STRH, rlo_src, rlo_base, uint16_offset); -} -static inline void asm_thumb_ldr_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint word_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_WORD_TRANSFER, rlo_dest, rlo_base, word_offset); -} -static inline void asm_thumb_ldrb_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint byte_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_9_LDR | ASM_THUMB_FORMAT_9_BYTE_TRANSFER, rlo_dest, rlo_base, byte_offset); -} -static inline void asm_thumb_ldrh_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_base, uint uint16_offset) { - asm_thumb_format_9_10(as, ASM_THUMB_FORMAT_10_LDRH, rlo_dest, rlo_base, uint16_offset); -} static inline void asm_thumb_lsl_rlo_rlo_i5(asm_thumb_t *as, uint rlo_dest, uint rlo_src, uint shift) { asm_thumb_format_1(as, ASM_THUMB_FORMAT_1_LSL, rlo_dest, rlo_src, shift); } @@ -338,8 +364,10 @@ void asm_thumb_mov_reg_local(asm_thumb_t *as, uint rlo_dest, int local_num); // void asm_thumb_mov_reg_local_addr(asm_thumb_t *as, uint rlo_dest, int local_num); // convenience void asm_thumb_mov_reg_pcrel(asm_thumb_t *as, uint rlo_dest, uint label); -void asm_thumb_ldr_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint word_offset); // convenience -void asm_thumb_ldrh_reg_reg_i12_optimised(asm_thumb_t *as, uint reg_dest, uint reg_base, uint uint16_offset); // convenience +// Generate optimised load dest, [src, #offset] sequence +void asm_thumb_load_reg_reg_offset(asm_thumb_t *as, uint reg_dest, uint reg_base, uint offset, uint operation_size); +// Generate optimised store src, [dest, #offset] sequence +void asm_thumb_store_reg_reg_offset(asm_thumb_t *as, uint reg_src, uint reg_base, uint offset, uint operation_size); void asm_thumb_b_label(asm_thumb_t *as, uint label); // convenience: picks narrow or wide branch void asm_thumb_bcc_label(asm_thumb_t *as, int cc, uint label); // convenience: picks narrow or wide branch @@ -376,12 +404,12 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel); #define REG_FUN_TABLE ASM_THUMB_REG_FUN_TABLE -#define ASM_T asm_thumb_t -#define ASM_END_PASS asm_thumb_end_pass -#define ASM_ENTRY asm_thumb_entry -#define ASM_EXIT asm_thumb_exit +#define ASM_T asm_thumb_t +#define ASM_END_PASS asm_thumb_end_pass +#define ASM_ENTRY(as, num_locals, name) asm_thumb_entry((as), (num_locals)) +#define ASM_EXIT asm_thumb_exit -#define ASM_JUMP asm_thumb_b_label +#define ASM_JUMP asm_thumb_b_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ do { \ asm_thumb_cmp_rlo_i8(as, reg, 0); \ @@ -419,18 +447,44 @@ void asm_thumb_b_rel12(asm_thumb_t *as, int rel); #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_thumb_sub_rlo_rlo_rlo((as), (reg_dest), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_thumb_format_4((as), ASM_THUMB_FORMAT_4_MUL, (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_ldr_reg_reg_i12_optimised((as), (reg_dest), (reg_base), (word_offset)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrb_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_thumb_ldrh_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_thumb_ldrh_reg_reg_i12_optimised((as), (reg_dest), (reg_base), (uint16_offset)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_thumb_ldr_rlo_rlo_i5((as), (reg_dest), (reg_base), 0) - -#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_thumb_strb_rlo_rlo_i5((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_thumb_strh_rlo_rlo_i5((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_thumb_str_rlo_rlo_i5((as), (reg_src), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_thumb_load_reg_reg_offset((as), (reg_dest), (reg_base), (byte_offset), 0) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, halfword_offset) asm_thumb_load_reg_reg_offset((as), (reg_dest), (reg_base), (halfword_offset), 1) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_thumb_load_reg_reg_offset((as), (reg_dest), (reg_base), (word_offset), 2) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_src, reg_base, byte_offset) asm_thumb_store_reg_reg_offset((as), (reg_src), (reg_base), (byte_offset), 0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_src, reg_base, halfword_offset) asm_thumb_store_reg_reg_offset((as), (reg_src), (reg_base), (halfword_offset), 1) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_thumb_store_reg_reg_offset((as), (reg_src), (reg_base), (word_offset), 2) + +#define ASM_LOAD8_REG_REG_REG(as, reg_dest, reg_base, reg_index) asm_thumb_ldrb_rlo_rlo_rlo((as), (reg_dest), (reg_base), (reg_index)) +#define ASM_LOAD16_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_thumb_lsl_rlo_rlo_i5((as), (reg_index), (reg_index), 1); \ + asm_thumb_ldrh_rlo_rlo_rlo((as), (reg_dest), (reg_base), (reg_index)); \ + } while (0) +#define ASM_LOAD32_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_thumb_lsl_rlo_rlo_i5((as), (reg_index), (reg_index), 2); \ + asm_thumb_ldr_rlo_rlo_rlo((as), (reg_dest), (reg_base), (reg_index)); \ + } while (0) +#define ASM_STORE8_REG_REG_REG(as, reg_val, reg_base, reg_index) asm_thumb_strb_rlo_rlo_rlo((as), (reg_val), (reg_base), (reg_index)) +#define ASM_STORE16_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_thumb_lsl_rlo_rlo_i5((as), (reg_index), (reg_index), 1); \ + asm_thumb_strh_rlo_rlo_rlo((as), (reg_val), (reg_base), (reg_index)); \ + } while (0) +#define ASM_STORE32_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_thumb_lsl_rlo_rlo_i5((as), (reg_index), (reg_index), 2); \ + asm_thumb_str_rlo_rlo_rlo((as), (reg_val), (reg_base), (reg_index)); \ + } while (0) #endif // GENERIC_ASM_API diff --git a/py/asmx64.h b/py/asmx64.h index 03070b5f63d3b..1e8cb0c905f2b 100644 --- a/py/asmx64.h +++ b/py/asmx64.h @@ -155,12 +155,12 @@ void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32); // Holds a pointer to mp_fun_table #define REG_FUN_TABLE ASM_X64_REG_FUN_TABLE -#define ASM_T asm_x64_t -#define ASM_END_PASS asm_x64_end_pass -#define ASM_ENTRY asm_x64_entry -#define ASM_EXIT asm_x64_exit +#define ASM_T asm_x64_t +#define ASM_END_PASS asm_x64_end_pass +#define ASM_ENTRY(as, num_locals, name) asm_x64_entry((as), (num_locals)) +#define ASM_EXIT asm_x64_exit -#define ASM_JUMP asm_x64_jmp_label +#define ASM_JUMP asm_x64_jmp_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ do { \ if (bool_test) { \ @@ -206,18 +206,21 @@ void asm_x64_call_ind(asm_x64_t *as, size_t fun_id, int temp_r32); #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x64_sub_r64_r64((as), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x64_mul_r64_r64((as), (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem64_to_r64((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x64_mov_mem64_to_r64((as), (reg_base), 8 * (word_offset), (reg_dest)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem8_to_r64zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 2 * (uint16_offset), (reg_dest)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x64_mov_mem32_to_r64zx((as), (reg_base), 0, (reg_dest)) - -#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 8 * (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x64_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x64_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x64_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, qword_offset) asm_x64_mov_mem64_to_r64((as), (reg_base), 8 * (qword_offset), (reg_dest)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_x64_mov_mem8_to_r64zx((as), (reg_base), (byte_offset), (reg_dest)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x64_mov_mem16_to_r64zx((as), (reg_base), 2 * (word_offset), (reg_dest)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, dword_offset) asm_x64_mov_mem32_to_r64zx((as), (reg_base), 4 * (dword_offset), (reg_dest)) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, qword_offset) asm_x64_mov_r64_to_mem64((as), (reg_src), (reg_base), 8 * (qword_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_src, reg_base, byte_offset) asm_x64_mov_r8_to_mem8((as), (reg_src), (reg_base), (byte_offset)) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x64_mov_r16_to_mem16((as), (reg_src), (reg_base), 2 * (word_offset)) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG_OFFSET(as, reg_src, reg_base, dword_offset) asm_x64_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (dword_offset)) #endif // GENERIC_ASM_API diff --git a/py/asmx86.h b/py/asmx86.h index 7796d69762617..f5d37228a2f04 100644 --- a/py/asmx86.h +++ b/py/asmx86.h @@ -150,12 +150,12 @@ void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r // Holds a pointer to mp_fun_table #define REG_FUN_TABLE ASM_X86_REG_FUN_TABLE -#define ASM_T asm_x86_t -#define ASM_END_PASS asm_x86_end_pass -#define ASM_ENTRY asm_x86_entry -#define ASM_EXIT asm_x86_exit +#define ASM_T asm_x86_t +#define ASM_END_PASS asm_x86_end_pass +#define ASM_ENTRY(as, num_locals, name) asm_x86_entry((as), (num_locals)) +#define ASM_EXIT asm_x86_exit -#define ASM_JUMP asm_x86_jmp_label +#define ASM_JUMP asm_x86_jmp_label #define ASM_JUMP_IF_REG_ZERO(as, reg, label, bool_test) \ do { \ if (bool_test) { \ @@ -201,18 +201,21 @@ void asm_x86_call_ind(asm_x86_t *as, size_t fun_id, mp_uint_t n_args, int temp_r #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_x86_sub_r32_r32((as), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_x86_mul_r32_r32((as), (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x86_mov_mem32_to_r32((as), (reg_base), 4 * (word_offset), (reg_dest)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem8_to_r32zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 0, (reg_dest)) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 2 * (uint16_offset), (reg_dest)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_x86_mov_mem32_to_r32((as), (reg_base), 0, (reg_dest)) - -#define ASM_STORE_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_x86_mov_r8_to_mem8((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_x86_mov_r16_to_mem16((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, dword_offset) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (dword_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_x86_mov_mem8_to_r32zx((as), (reg_base), (byte_offset), (reg_dest)) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_x86_mov_mem16_to_r32zx((as), (reg_base), 2 * (word_offset), (reg_dest)) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, dword_offset) asm_x86_mov_mem32_to_r32((as), (reg_base), 4 * (dword_offset), (reg_dest)) + +#define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, dword_offset) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), (dword_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_src, reg_base, byte_offset) asm_x86_mov_r8_to_mem8((as), (reg_src), (reg_base), (byte_offset)) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) asm_x86_mov_r16_to_mem16((as), (reg_src), (reg_base), 2 * (word_offset)) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG_OFFSET(as, reg_src, reg_base, dword_offset) asm_x86_mov_r32_to_mem32((as), (reg_src), (reg_base), 4 * (dword_offset)) #endif // GENERIC_ASM_API diff --git a/py/asmxtensa.c b/py/asmxtensa.c index 0fbe351dcf3c7..bc3e717d9f3a0 100644 --- a/py/asmxtensa.c +++ b/py/asmxtensa.c @@ -34,9 +34,20 @@ #include "py/asmxtensa.h" +#if N_XTENSAWIN +#define REG_TEMP ASM_XTENSA_REG_TEMPORARY_WIN +#else +#define REG_TEMP ASM_XTENSA_REG_TEMPORARY +#endif + #define WORD_SIZE (4) +#define SIGNED_FIT6(x) ((((x) & 0xffffffe0) == 0) || (((x) & 0xffffffe0) == 0xffffffe0)) #define SIGNED_FIT8(x) ((((x) & 0xffffff80) == 0) || (((x) & 0xffffff80) == 0xffffff80)) #define SIGNED_FIT12(x) ((((x) & 0xfffff800) == 0) || (((x) & 0xfffff800) == 0xfffff800)) +#define SIGNED_FIT18(x) ((((x) & 0xfffe0000) == 0) || (((x) & 0xfffe0000) == 0xfffe0000)) + +#define ET_OUT_OF_RANGE MP_ERROR_TEXT("ERROR: xtensa %q out of range") +#define ET_NOT_ALIGNED MP_ERROR_TEXT("ERROR: %q %q not word-aligned") void asm_xtensa_end_pass(asm_xtensa_t *as) { as->num_const = as->cur_const; @@ -47,9 +58,9 @@ void asm_xtensa_end_pass(asm_xtensa_t *as) { if (as->base.pass == MP_ASM_PASS_EMIT) { uint8_t *d = as->base.code_base; printf("XTENSA ASM:"); - for (int i = 0; i < ((as->base.code_size + 15) & ~15); ++i) { + for (size_t i = 0; i < ((as->base.code_size + 15) & ~15); ++i) { if (i % 16 == 0) { - printf("\n%08x:", (uint32_t)&d[i]); + printf("\n%p:", &d[i]); } if (i % 2 == 0) { printf(" "); @@ -62,10 +73,12 @@ void asm_xtensa_end_pass(asm_xtensa_t *as) { } void asm_xtensa_entry(asm_xtensa_t *as, int num_locals) { - // jump over the constants - asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4); - mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte - as->const_table = (uint32_t *)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4); + if (as->num_const > 0) { + // jump over the constants + asm_xtensa_op_j(as, as->num_const * WORD_SIZE + 4 - 4); + mp_asm_base_get_cur_to_write_bytes(&as->base, 1); // padding/alignment byte + as->const_table = (uint32_t *)mp_asm_base_get_cur_to_write_bytes(&as->base, as->num_const * 4); + } // adjust the stack-pointer to store a0, a12, a13, a14, a15 and locals, 16-byte aligned as->stack_adjust = (((ASM_XTENSA_NUM_REGS_SAVED + num_locals) * WORD_SIZE) + 15) & ~15; @@ -146,22 +159,60 @@ void asm_xtensa_j_label(asm_xtensa_t *as, uint label) { asm_xtensa_op_j(as, rel); } +static bool calculate_branch_displacement(asm_xtensa_t *as, uint label, ptrdiff_t *displacement) { + assert(displacement != NULL && "Displacement pointer is NULL"); + + uint32_t label_offset = get_label_dest(as, label); + *displacement = (ptrdiff_t)(label_offset - as->base.code_offset - 4); + return (label_offset != (uint32_t)-1) && (*displacement < 0); +} + void asm_xtensa_bccz_reg_label(asm_xtensa_t *as, uint cond, uint reg, uint label) { - uint32_t dest = get_label_dest(as, label); - int32_t rel = dest - as->base.code_offset - 4; - if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT12(rel)) { - printf("ERROR: xtensa bccz out of range\n"); + ptrdiff_t rel = 0; + bool can_emit_short_jump = calculate_branch_displacement(as, label, &rel); + + if (can_emit_short_jump && SIGNED_FIT12(rel)) { + // Backwards BCCZ opcodes with an offset that fits in 12 bits can + // be emitted without any change. + asm_xtensa_op_bccz(as, cond, reg, rel); + return; } - asm_xtensa_op_bccz(as, cond, reg, rel); + + // Range is effectively extended to 18 bits, as a more complex jump code + // sequence is emitted. + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT18(rel - 6)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_bccz); + } + + // ~BCCZ skip ; +0 <- Condition is flipped here (EQ -> NE, etc.) + // J addr ; +3 + // skip: ; +6 + asm_xtensa_op_bccz(as, cond ^ 1, reg, 6 - 4); + asm_xtensa_op_j(as, rel - 3); } void asm_xtensa_bcc_reg_reg_label(asm_xtensa_t *as, uint cond, uint reg1, uint reg2, uint label) { - uint32_t dest = get_label_dest(as, label); - int32_t rel = dest - as->base.code_offset - 4; - if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT8(rel)) { - printf("ERROR: xtensa bcc out of range\n"); + ptrdiff_t rel = 0; + bool can_emit_short_jump = calculate_branch_displacement(as, label, &rel); + + if (can_emit_short_jump && SIGNED_FIT8(rel)) { + // Backwards BCC opcodes with an offset that fits in 8 bits can + // be emitted without any change. + asm_xtensa_op_bcc(as, cond, reg1, reg2, rel); + return; + } + + // Range is effectively extended to 18 bits, as a more complex jump code + // sequence is emitted. + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT18(rel - 6)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_bcc); } - asm_xtensa_op_bcc(as, cond, reg1, reg2, rel); + + // ~BCC skip ; +0 <- Condition is flipped here (EQ -> NE, etc.) + // J addr ; +3 + // skip: ; +6 + asm_xtensa_op_bcc(as, cond ^ 8, reg1, reg2, 6 - 4); + asm_xtensa_op_j(as, rel - 3); } // convenience function; reg_dest must be different from reg_src[12] @@ -179,6 +230,8 @@ size_t asm_xtensa_mov_reg_i32(asm_xtensa_t *as, uint reg_dest, uint32_t i32) { // store the constant in the table if (as->const_table != NULL) { as->const_table[as->cur_const] = i32; + } else { + assert((as->base.pass != MP_ASM_PASS_EMIT) && "Constants table was not built."); } ++as->cur_const; return loc; @@ -240,17 +293,53 @@ void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, u } else if (word_offset < 256) { asm_xtensa_op_l32i(as, reg_dest, reg_base, word_offset); } else { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow")); + asm_xtensa_mov_reg_i32_optimised(as, reg_dest, word_offset * 4); + asm_xtensa_op_add_n(as, reg_dest, reg_base, reg_dest); + asm_xtensa_op_l32i_n(as, reg_dest, reg_dest, 0); } } -void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset) { - if (word_offset < 16) { - asm_xtensa_op_s32i_n(as, reg_src, reg_base, word_offset); - } else if (word_offset < 256) { - asm_xtensa_op_s32i(as, reg_src, reg_base, word_offset); +void asm_xtensa_load_reg_reg_offset(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint offset, uint operation_size) { + assert(operation_size <= 2 && "Operation size value out of range."); + + if (operation_size == 2 && MP_FIT_UNSIGNED(4, offset)) { + asm_xtensa_op_l32i_n(as, reg_dest, reg_base, offset); + return; + } + + if (MP_FIT_UNSIGNED(8, offset)) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, operation_size, reg_base, reg_dest, offset)); + return; + } + + asm_xtensa_mov_reg_i32_optimised(as, reg_dest, offset << operation_size); + asm_xtensa_op_add_n(as, reg_dest, reg_base, reg_dest); + if (operation_size == 2) { + asm_xtensa_op_l32i_n(as, reg_dest, reg_dest, 0); } else { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("asm overflow")); + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, operation_size, reg_dest, reg_dest, 0)); + } +} + +void asm_xtensa_store_reg_reg_offset(asm_xtensa_t *as, uint reg_src, uint reg_base, uint offset, uint operation_size) { + assert(operation_size <= 2 && "Operation size value out of range."); + + if (operation_size == 2 && MP_FIT_UNSIGNED(4, offset)) { + asm_xtensa_op_s32i_n(as, reg_src, reg_base, offset); + return; + } + + if (MP_FIT_UNSIGNED(8, offset)) { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 0x04 | operation_size, reg_base, reg_src, offset)); + return; + } + + asm_xtensa_mov_reg_i32_optimised(as, REG_TEMP, offset << operation_size); + asm_xtensa_op_add_n(as, REG_TEMP, reg_base, REG_TEMP); + if (operation_size == 2) { + asm_xtensa_op_s32i_n(as, reg_src, REG_TEMP, 0); + } else { + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(2, 0x04 | operation_size, REG_TEMP, reg_src, 0)); } } @@ -264,4 +353,47 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx) { asm_xtensa_op_callx8(as, ASM_XTENSA_REG_A8); } +void asm_xtensa_bit_branch(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t bit, mp_uint_t label, mp_uint_t condition) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 4; + if (as->base.pass == MP_ASM_PASS_EMIT && !SIGNED_FIT8(rel)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_bit_branch); + } + asm_xtensa_op24(as, ASM_XTENSA_ENCODE_RRI8(7, condition | ((bit >> 4) & 0x01), reg, bit & 0x0F, rel & 0xFF)); +} + +void asm_xtensa_call0(asm_xtensa_t *as, mp_uint_t label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset - 3; + if (as->base.pass == MP_ASM_PASS_EMIT) { + if ((dest & 0x03) != 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_NOT_ALIGNED, MP_QSTR_call0, MP_QSTR_target); + } + if ((rel & 0x03) != 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_NOT_ALIGNED, MP_QSTR_call0, MP_QSTR_location); + } + if (!SIGNED_FIT18(rel)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_call0); + } + } + asm_xtensa_op_call0(as, rel); +} + +void asm_xtensa_l32r(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t label) { + uint32_t dest = get_label_dest(as, label); + int32_t rel = dest - as->base.code_offset; + if (as->base.pass == MP_ASM_PASS_EMIT) { + if ((dest & 0x03) != 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_NOT_ALIGNED, MP_QSTR_l32r, MP_QSTR_target); + } + if ((rel & 0x03) != 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_NOT_ALIGNED, MP_QSTR_l32r, MP_QSTR_location); + } + if (!SIGNED_FIT18(rel) || (rel >= 0)) { + mp_raise_msg_varg(&mp_type_RuntimeError, ET_OUT_OF_RANGE, MP_QSTR_l32r); + } + } + asm_xtensa_op_l32r(as, reg, as->base.code_offset, dest); +} + #endif // MICROPY_EMIT_XTENSA || MICROPY_EMIT_INLINE_XTENSA || MICROPY_EMIT_XTENSAWIN diff --git a/py/asmxtensa.h b/py/asmxtensa.h index a8c39206bd008..ed98c9d730516 100644 --- a/py/asmxtensa.h +++ b/py/asmxtensa.h @@ -64,9 +64,11 @@ #define ASM_XTENSA_REG_A14 (14) #define ASM_XTENSA_REG_A15 (15) -// for bccz +// for bccz and bcci #define ASM_XTENSA_CCZ_EQ (0) #define ASM_XTENSA_CCZ_NE (1) +#define ASM_XTENSA_CCZ_LT (2) +#define ASM_XTENSA_CCZ_GE (3) // for bcc and setcc #define ASM_XTENSA_CC_NONE (0) @@ -291,15 +293,24 @@ void asm_xtensa_mov_local_reg(asm_xtensa_t *as, int local_num, uint reg_src); void asm_xtensa_mov_reg_local(asm_xtensa_t *as, uint reg_dest, int local_num); void asm_xtensa_mov_reg_local_addr(asm_xtensa_t *as, uint reg_dest, int local_num); void asm_xtensa_mov_reg_pcrel(asm_xtensa_t *as, uint reg_dest, uint label); -void asm_xtensa_l32i_optimised(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint word_offset); -void asm_xtensa_s32i_optimised(asm_xtensa_t *as, uint reg_src, uint reg_base, uint word_offset); +void asm_xtensa_load_reg_reg_offset(asm_xtensa_t *as, uint reg_dest, uint reg_base, uint offset, uint operation_size); +void asm_xtensa_store_reg_reg_offset(asm_xtensa_t *as, uint reg_src, uint reg_base, uint offset, uint operation_size); void asm_xtensa_call_ind(asm_xtensa_t *as, uint idx); void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); +void asm_xtensa_bit_branch(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t bit, mp_uint_t label, mp_uint_t condition); +void asm_xtensa_immediate_branch(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t immediate, mp_uint_t label, mp_uint_t cond); +void asm_xtensa_call0(asm_xtensa_t *as, mp_uint_t label); +void asm_xtensa_l32r(asm_xtensa_t *as, mp_uint_t reg, mp_uint_t label); // Holds a pointer to mp_fun_table #define ASM_XTENSA_REG_FUN_TABLE ASM_XTENSA_REG_A15 #define ASM_XTENSA_REG_FUN_TABLE_WIN ASM_XTENSA_REG_A7 +// Internal temporary register (currently aliased to REG_ARG_5 for xtensa, +// and to REG_TEMP2 for xtensawin). +#define ASM_XTENSA_REG_TEMPORARY ASM_XTENSA_REG_A6 +#define ASM_XTENSA_REG_TEMPORARY_WIN ASM_XTENSA_REG_A12 + // CIRCUITPY-CHANGE: prevent #if warning #if defined(GENERIC_ASM_API) && GENERIC_ASM_API @@ -330,9 +341,9 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); #define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED #define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE -#define ASM_ENTRY(as, nlocal) asm_xtensa_entry((as), (nlocal)) -#define ASM_EXIT(as) asm_xtensa_exit((as)) -#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind((as), (idx)) +#define ASM_ENTRY(as, nlocal, name) asm_xtensa_entry((as), (nlocal)) +#define ASM_EXIT(as) asm_xtensa_exit((as)) +#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind((as), (idx)) #else // Configuration for windowed calls with window size 8 @@ -360,9 +371,9 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); #define ASM_NUM_REGS_SAVED ASM_XTENSA_NUM_REGS_SAVED_WIN #define REG_FUN_TABLE ASM_XTENSA_REG_FUN_TABLE_WIN -#define ASM_ENTRY(as, nlocal) asm_xtensa_entry_win((as), (nlocal)) -#define ASM_EXIT(as) asm_xtensa_exit_win((as)) -#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind_win((as), (idx)) +#define ASM_ENTRY(as, nlocal, name) asm_xtensa_entry_win((as), (nlocal)) +#define ASM_EXIT(as) asm_xtensa_exit_win((as)) +#define ASM_CALL_IND(as, idx) asm_xtensa_call_ind_win((as), (idx)) #endif @@ -408,16 +419,51 @@ void asm_xtensa_call_ind_win(asm_xtensa_t *as, uint idx); #define ASM_SUB_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_sub((as), (reg_dest), (reg_dest), (reg_src)) #define ASM_MUL_REG_REG(as, reg_dest, reg_src) asm_xtensa_op_mull((as), (reg_dest), (reg_dest), (reg_src)) -#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_l32i_optimised((as), (reg_dest), (reg_base), (word_offset)) -#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l8ui((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), 0) -#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, uint16_offset) asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), (uint16_offset)) -#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) ASM_LOAD8_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD8_REG_REG_OFFSET(as, reg_dest, reg_base, byte_offset) asm_xtensa_load_reg_reg_offset((as), (reg_dest), (reg_base), (byte_offset), 0) +#define ASM_LOAD8_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_xtensa_op_add_n((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_l8ui((as), (reg_dest), (reg_base), 0); \ + } while (0) +#define ASM_LOAD16_REG_REG(as, reg_dest, reg_base) ASM_LOAD16_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD16_REG_REG_OFFSET(as, reg_dest, reg_base, halfword_offset) asm_xtensa_load_reg_reg_offset((as), (reg_dest), (reg_base), (halfword_offset), 1) +#define ASM_LOAD16_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_xtensa_op_addx2((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_l16ui((as), (reg_dest), (reg_base), 0); \ + } while (0) +#define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) ASM_LOAD32_REG_REG_OFFSET((as), (reg_dest), (reg_base), 0) +#define ASM_LOAD32_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_load_reg_reg_offset((as), (reg_dest), (reg_base), (word_offset), 2) +#define ASM_LOAD32_REG_REG_REG(as, reg_dest, reg_base, reg_index) \ + do { \ + asm_xtensa_op_addx4((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_l32i_n((as), (reg_dest), (reg_base), 0); \ + } while (0) -#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_s32i_optimised((as), (reg_dest), (reg_base), (word_offset)) -#define ASM_STORE8_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s8i((as), (reg_src), (reg_base), 0) -#define ASM_STORE16_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s16i((as), (reg_src), (reg_base), 0) -#define ASM_STORE32_REG_REG(as, reg_src, reg_base) asm_xtensa_op_s32i_n((as), (reg_src), (reg_base), 0) +#define ASM_STORE_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) ASM_STORE32_REG_REG_OFFSET((as), (reg_dest), (reg_base), (word_offset)) +#define ASM_STORE8_REG_REG(as, reg_src, reg_base) ASM_STORE8_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE8_REG_REG_OFFSET(as, reg_src, reg_base, byte_offset) asm_xtensa_store_reg_reg_offset((as), (reg_src), (reg_base), (byte_offset), 0) +#define ASM_STORE8_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_xtensa_op_add_n((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_s8i((as), (reg_val), (reg_base), 0); \ + } while (0) +#define ASM_STORE16_REG_REG(as, reg_src, reg_base) ASM_STORE16_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE16_REG_REG_OFFSET(as, reg_src, reg_base, halfword_offset) asm_xtensa_store_reg_reg_offset((as), (reg_src), (reg_base), (halfword_offset), 1) +#define ASM_STORE16_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_xtensa_op_addx2((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_s16i((as), (reg_val), (reg_base), 0); \ + } while (0) +#define ASM_STORE32_REG_REG(as, reg_src, reg_base) ASM_STORE32_REG_REG_OFFSET((as), (reg_src), (reg_base), 0) +#define ASM_STORE32_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) asm_xtensa_store_reg_reg_offset((as), (reg_dest), (reg_base), (word_offset), 2) +#define ASM_STORE32_REG_REG_REG(as, reg_val, reg_base, reg_index) \ + do { \ + asm_xtensa_op_addx4((as), (reg_base), (reg_index), (reg_base)); \ + asm_xtensa_op_s32i_n((as), (reg_val), (reg_base), 0); \ + } while (0) #endif // GENERIC_ASM_API diff --git a/py/bc.c b/py/bc.c index c2956030e38e1..4caa022e3d8b4 100644 --- a/py/bc.c +++ b/py/bc.c @@ -88,7 +88,7 @@ const byte *mp_decode_uint_skip(const byte *ptr) { return ptr; } -static NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) { +static MP_NORETURN void fun_pos_args_mismatch(mp_obj_fun_bc_t *f, size_t expected, size_t given) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE // generic message, used also for other argument issues (void)f; diff --git a/py/bc.h b/py/bc.h index 7658f66414f17..3b7a6fe1c960b 100644 --- a/py/bc.h +++ b/py/bc.h @@ -226,6 +226,7 @@ typedef struct _mp_compiled_module_t { bool has_native; size_t n_qstr; size_t n_obj; + size_t arch_flags; #endif } mp_compiled_module_t; @@ -303,7 +304,7 @@ static inline void mp_module_context_alloc_tables(mp_module_context_t *context, size_t nq = (n_qstr * sizeof(qstr_short_t) + sizeof(mp_uint_t) - 1) / sizeof(mp_uint_t); size_t no = n_obj; // CIRCUITPY-CHANGE - mp_uint_t *mem = m_malloc_items(nq + no); + mp_uint_t *mem = (mp_uint_t *)m_malloc_items(nq + no); context->constants.qstr_table = (qstr_short_t *)mem; context->constants.obj_table = (mp_obj_t *)(mem + nq); #else @@ -316,25 +317,35 @@ static inline void mp_module_context_alloc_tables(mp_module_context_t *context, #endif } +typedef struct _mp_code_lineinfo_t { + size_t bc_increment; + size_t line_increment; +} mp_code_lineinfo_t; + +static inline mp_code_lineinfo_t mp_bytecode_decode_lineinfo(const byte **line_info) { + mp_code_lineinfo_t result; + size_t c = (*line_info)[0]; + if ((c & 0x80) == 0) { + // 0b0LLBBBBB encoding + result.bc_increment = c & 0x1f; + result.line_increment = c >> 5; + *line_info += 1; + } else { + // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) + result.bc_increment = c & 0xf; + result.line_increment = ((c << 4) & 0x700) | (*line_info)[1]; + *line_info += 2; + } + return result; +} + static inline size_t mp_bytecode_get_source_line(const byte *line_info, const byte *line_info_top, size_t bc_offset) { size_t source_line = 1; while (line_info < line_info_top) { - size_t c = *line_info; - size_t b, l; - if ((c & 0x80) == 0) { - // 0b0LLBBBBB encoding - b = c & 0x1f; - l = c >> 5; - line_info += 1; - } else { - // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) - b = c & 0xf; - l = ((c << 4) & 0x700) | line_info[1]; - line_info += 2; - } - if (bc_offset >= b) { - bc_offset -= b; - source_line += l; + mp_code_lineinfo_t decoded = mp_bytecode_decode_lineinfo(&line_info); + if (bc_offset >= decoded.bc_increment) { + bc_offset -= decoded.bc_increment; + source_line += decoded.line_increment; } else { // found source line corresponding to bytecode offset break; diff --git a/py/binary.c b/py/binary.c index 728f29815cd78..180a13beec7bf 100644 --- a/py/binary.c +++ b/py/binary.c @@ -71,8 +71,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { case 'Q': size = 8; break; - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'P': case 'O': case 'S': @@ -128,8 +127,7 @@ size_t mp_binary_get_size(char struct_type, char val_type, size_t *palign) { align = alignof(long long); size = sizeof(long long); break; - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'P': case 'O': case 'S': @@ -208,7 +206,7 @@ static float mp_decode_half_float(uint16_t hf) { ++e; } - fpu.i = ((hf & 0x8000) << 16) | (e << 23) | (m << 13); + fpu.i = ((hf & 0x8000u) << 16) | (e << 23) | (m << 13); return fpu.f; } @@ -289,20 +287,17 @@ mp_obj_t mp_binary_get_val_array(char typecode, void *p, size_t index) { #if MICROPY_PY_BUILTINS_FLOAT case 'f': return mp_obj_new_float_from_f(((float *)p)[index]); - #if MICROPY_PY_DOUBLE_TYPECODE case 'd': return mp_obj_new_float_from_d(((double *)p)[index]); #endif - #endif - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES - // Extension to CPython: array of objects + // Extension to CPython: array of objects + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'O': return ((mp_obj_t *)p)[index]; // Extension to CPython: array of pointers case 'P': return mp_obj_new_int((mp_int_t)(uintptr_t)((void **)p)[index]); - #endif + #endif } return MP_OBJ_NEW_SMALL_INT(val); } @@ -352,14 +347,11 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * long long val = mp_binary_get_int(size, is_signed(val_type), (struct_type == '>'), p); - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - if (MICROPY_NONSTANDARD_TYPECODES && (val_type == 'O')) { + if (MICROPY_PY_STRUCT_UNSAFE_TYPECODES && val_type == 'O') { return (mp_obj_t)(mp_uint_t)val; - #if MICROPY_NONSTANDARD_TYPECODES - } else if (val_type == 'S') { + } else if (MICROPY_PY_STRUCT_UNSAFE_TYPECODES && val_type == 'S') { const char *s_val = (const char *)(uintptr_t)(mp_uint_t)val; return mp_obj_new_str_from_cstr(s_val); - #endif #if MICROPY_PY_BUILTINS_FLOAT } else if (val_type == 'e') { return mp_obj_new_float_from_f(mp_decode_half_float(val)); @@ -369,7 +361,6 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * float f; } fpu = {val}; return mp_obj_new_float_from_f(fpu.f); - #if MICROPY_PY_DOUBLE_TYPECODE } else if (val_type == 'd') { union { uint64_t i; @@ -377,7 +368,6 @@ mp_obj_t mp_binary_get_val(char struct_type, char val_type, byte *p_base, byte * } fpu = {val}; return mp_obj_new_float_from_d(fpu.f); #endif - #endif } else if (is_signed(val_type)) { if ((long long)MP_SMALL_INT_MIN <= val && val <= (long long)MP_SMALL_INT_MAX) { return mp_obj_new_int((mp_int_t)val); @@ -430,8 +420,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p mp_uint_t val; switch (val_type) { - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'O': val = (mp_uint_t)val_in; break; @@ -449,6 +438,7 @@ void mp_binary_set_val(char struct_type, char val_type, mp_obj_t val_in, byte *p val = fp_sp.i; break; } + // CIRCUITPY-CHANGE #if MICROPY_PY_DOUBLE_TYPECODE case 'd': { union { @@ -507,14 +497,11 @@ void mp_binary_set_val_array(char typecode, void *p, size_t index, mp_obj_t val_ case 'f': ((float *)p)[index] = mp_obj_get_float_to_f(val_in); break; - #if MICROPY_PY_DOUBLE_TYPECODE case 'd': ((double *)p)[index] = mp_obj_get_float_to_d(val_in); break; #endif - #endif - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES // Extension to CPython: array of objects case 'O': ((mp_obj_t *)p)[index] = val_in; @@ -582,18 +569,15 @@ void mp_binary_set_val_array_from_int(char typecode, void *p, size_t index, mp_i case 'f': ((float *)p)[index] = (float)val; break; - #if MICROPY_PY_DOUBLE_TYPECODE case 'd': ((double *)p)[index] = (double)val; break; #endif - #endif - // CIRCUITPY-CHANGE: non-standard typecodes can be turned off - #if MICROPY_NONSTANDARD_TYPECODES - // Extension to CPython: array of pointers + // Extension to CPython: array of pointers + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES case 'P': ((void **)p)[index] = (void *)(uintptr_t)val; break; - #endif + #endif } } diff --git a/py/builtin.h b/py/builtin.h index 6efe3e8facabd..388bc8470053d 100644 --- a/py/builtin.h +++ b/py/builtin.h @@ -138,6 +138,7 @@ extern const mp_obj_module_t mp_module_sys; extern const mp_obj_module_t mp_module_errno; extern const mp_obj_module_t mp_module_uctypes; extern const mp_obj_module_t mp_module_machine; +extern const mp_obj_module_t mp_module_math; extern const char MICROPY_PY_BUILTINS_HELP_TEXT[]; diff --git a/py/builtinhelp.c b/py/builtinhelp.c index d041d6915a63a..9ab2a12d21455 100644 --- a/py/builtinhelp.c +++ b/py/builtinhelp.c @@ -162,9 +162,8 @@ static void mp_help_print_obj(const mp_obj_t obj) { } if (map != NULL) { for (uint i = 0; i < map->alloc; i++) { - mp_obj_t key = map->table[i].key; - if (key != MP_OBJ_NULL) { - mp_help_print_info_about_object(key, map->table[i].value); + if (mp_map_slot_is_filled(map, i)) { + mp_help_print_info_about_object(map->table[i].key, map->table[i].value); } } } diff --git a/py/builtinimport.c b/py/builtinimport.c index 17b3f11c4a001..8fcac22ccb491 100644 --- a/py/builtinimport.c +++ b/py/builtinimport.c @@ -118,7 +118,7 @@ static mp_import_stat_t stat_module(vstr_t *path) { // path (i.e. "/mod_name(.py)"). static mp_import_stat_t stat_top_level(qstr mod_name, vstr_t *dest) { DEBUG_printf("stat_top_level: '%s'\n", qstr_str(mod_name)); - #if MICROPY_PY_SYS + #if MICROPY_PY_SYS && MICROPY_PY_SYS_PATH size_t path_num; mp_obj_t *path_items; mp_obj_get_array(mp_sys_path, &path_num, &path_items); @@ -155,7 +155,7 @@ static mp_import_stat_t stat_top_level(qstr mod_name, vstr_t *dest) { #if MICROPY_MODULE_FROZEN_STR || MICROPY_ENABLE_COMPILER static void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) { - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ qstr source_name = lex->source_name; mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #endif @@ -168,7 +168,7 @@ static void do_load_from_lexer(mp_module_context_t *context, mp_lexer_t *lex) { #if (MICROPY_HAS_FILE_READER && MICROPY_PERSISTENT_CODE_LOAD) || MICROPY_MODULE_FROZEN_MPY static void do_execute_proto_fun(const mp_module_context_t *context, mp_proto_fun_t proto_fun, qstr source_name) { - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ mp_store_attr(MP_OBJ_FROM_PTR(&context->module), MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); #else (void)source_name; @@ -227,7 +227,7 @@ static void do_load(mp_module_context_t *module_obj, vstr_t *file) { if (frozen_type == MP_FROZEN_MPY) { const mp_frozen_module_t *frozen = modref; module_obj->constants = frozen->constants; - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ qstr frozen_file_qstr = qstr_from_str(file_str + frozen_path_prefix_len); #else qstr frozen_file_qstr = MP_QSTRnull; @@ -270,7 +270,7 @@ static void do_load(mp_module_context_t *module_obj, vstr_t *file) { // Convert a relative (to the current module) import, going up "level" levels, // into an absolute import. -static void evaluate_relative_import(mp_int_t level, const char **module_name, size_t *module_name_len) { +static void evaluate_relative_import(mp_int_t level, const char **module_name, size_t *module_name_len, mp_obj_t globals) { // What we want to do here is to take the name of the current module, // remove trailing components, and concatenate the passed-in // module name. @@ -279,7 +279,7 @@ static void evaluate_relative_import(mp_int_t level, const char **module_name, s // module's position in the package hierarchy." // http://legacy.python.org/dev/peps/pep-0328/#relative-imports-and-name - mp_obj_t current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___name__)); + mp_obj_t current_module_name_obj = mp_obj_dict_get(globals, MP_OBJ_NEW_QSTR(MP_QSTR___name__)); assert(current_module_name_obj != MP_OBJ_NULL); #if MICROPY_MODULE_OVERRIDE_MAIN_IMPORT && MICROPY_CPYTHON_COMPAT @@ -287,12 +287,12 @@ static void evaluate_relative_import(mp_int_t level, const char **module_name, s // This is a module loaded by -m command-line switch (e.g. unix port), // and so its __name__ has been set to "__main__". Get its real name // that we stored during import in the __main__ attribute. - current_module_name_obj = mp_obj_dict_get(MP_OBJ_FROM_PTR(mp_globals_get()), MP_OBJ_NEW_QSTR(MP_QSTR___main__)); + current_module_name_obj = mp_obj_dict_get(globals, MP_OBJ_NEW_QSTR(MP_QSTR___main__)); } #endif // If we have a __path__ in the globals dict, then we're a package. - bool is_pkg = mp_map_lookup(&mp_globals_get()->map, MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); + bool is_pkg = mp_map_lookup(mp_obj_dict_get_map(globals), MP_OBJ_NEW_QSTR(MP_QSTR___path__), MP_MAP_LOOKUP); #if DEBUG_PRINT DEBUG_printf("Current module/package: "); @@ -370,7 +370,7 @@ static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, // Immediately return if the module at this level is already loaded. mp_map_elem_t *elem; - #if MICROPY_PY_SYS + #if MICROPY_PY_SYS && MICROPY_PY_SYS_PATH // If sys.path is empty, the intention is to force using a built-in. This // means we should also ignore any loaded modules with the same name // which may have come from the filesystem. @@ -406,6 +406,7 @@ static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, // all the locations in sys.path. stat = stat_top_level(level_mod_name, &path); + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES // If filesystem failed, now try and see if it matches an extensible // built-in module. if (stat == MP_IMPORT_STAT_NO_EXIST) { @@ -414,6 +415,7 @@ static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, return module_obj; } } + #endif } else { DEBUG_printf("Searching for sub-module\n"); @@ -451,7 +453,9 @@ static mp_obj_t process_import_at_level(qstr full_mod_name, qstr level_mod_name, if (stat == MP_IMPORT_STAT_NO_EXIST) { // Not found -- fail. - #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE + // CIRCUITPY-CHANGE: always the use more verbose error message that names missing module. + // Otherwise `import a` where `a` imports `b`, but `b` is missing will give a confusing error. + #if 0 && (MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE) mp_raise_msg(&mp_type_ImportError, MP_ERROR_TEXT("module not found")); #else mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("no module named '%q'"), full_mod_name); @@ -575,10 +579,19 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { const char *module_name = mp_obj_str_get_data(module_name_obj, &module_name_len); if (level != 0) { + // This is the dict with all global symbols. + mp_obj_t globals = MP_OBJ_FROM_PTR(mp_globals_get()); + if (n_args >= 2 && args[1] != mp_const_none) { + globals = args[1]; + if (!mp_obj_is_type(globals, &mp_type_dict)) { + mp_raise_TypeError(NULL); + } + } + // Turn "foo.bar" with level=3 into ".foo.bar". // Current module name is extracted from globals().__name__. - evaluate_relative_import(level, &module_name, &module_name_len); // module_name is now an absolute module path. + evaluate_relative_import(level, &module_name, &module_name_len, globals); } if (module_name_len == 0) { @@ -654,11 +667,13 @@ mp_obj_t mp_builtin___import___default(size_t n_args, const mp_obj_t *args) { if (module_obj != MP_OBJ_NULL) { return module_obj; } + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES // Now try as an extensible built-in (e.g. `time`). module_obj = mp_module_get_builtin(module_name_qstr, true); if (module_obj != MP_OBJ_NULL) { return module_obj; } + #endif // Couldn't find the module, so fail #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE diff --git a/py/circuitpy_defns.mk b/py/circuitpy_defns.mk index d6569bc39bd8e..e0ad8fa81bc10 100644 --- a/py/circuitpy_defns.mk +++ b/py/circuitpy_defns.mk @@ -146,6 +146,9 @@ endif ifeq ($(CIRCUITPY_AUDIOMP3),1) SRC_PATTERNS += audiomp3/% endif +ifeq ($(CIRCUITPY_AUDIOSPEED),1) +SRC_PATTERNS += audiospeed/% +endif ifeq ($(CIRCUITPY_AURORA_EPAPER),1) SRC_PATTERNS += aurora_epaper/% endif @@ -201,6 +204,9 @@ endif ifeq ($(CIRCUITPY_DISPLAYIO),1) SRC_PATTERNS += displayio/% endif +ifeq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),1) +SRC_PATTERNS += dotclockframebuffer/% +endif ifeq ($(CIRCUITPY_DUALBANK),1) SRC_PATTERNS += dualbank/% endif @@ -255,6 +261,9 @@ endif ifeq ($(CIRCUITPY_I2CTARGET),1) SRC_PATTERNS += i2ctarget/% endif +ifeq ($(CIRCUITPY_I2CIOEXPANDER),1) +SRC_PATTERNS += i2cioexpander/% +endif ifeq ($(CIRCUITPY_IMAGECAPTURE),1) SRC_PATTERNS += imagecapture/% endif @@ -282,6 +291,9 @@ endif ifeq ($(CIRCUITPY_MAX3421E),1) SRC_PATTERNS += max3421e/% endif +ifeq ($(CIRCUITPY_MCP4822),1) +SRC_PATTERNS += mcp4822/% +endif ifeq ($(CIRCUITPY_MDNS),1) SRC_PATTERNS += mdns/% endif @@ -294,6 +306,9 @@ endif ifeq ($(CIRCUITPY_MICROCONTROLLER),1) SRC_PATTERNS += microcontroller/% endif +ifeq ($(CIRCUITPY_MIPIDSI),1) +SRC_PATTERNS += mipidsi/% +endif ifeq ($(CIRCUITPY_MSGPACK),1) SRC_PATTERNS += msgpack/% endif @@ -348,8 +363,8 @@ endif ifeq ($(CIRCUITPY_RGBMATRIX),1) SRC_PATTERNS += rgbmatrix/% endif -ifeq ($(CIRCUITPY_DOTCLOCKFRAMEBUFFER),1) -SRC_PATTERNS += dotclockframebuffer/% +ifeq ($(CIRCUITPY_QSPIBUS),1) +SRC_PATTERNS += qspibus/% endif ifeq ($(CIRCUITPY_RP2PIO),1) SRC_PATTERNS += rp2pio/% @@ -523,6 +538,8 @@ SRC_COMMON_HAL_ALL = \ i2ctarget/I2CTarget.c \ i2ctarget/__init__.c \ max3421e/Max3421E.c \ + mcp4822/__init__.c \ + mcp4822/MCP4822.c \ memorymap/__init__.c \ memorymap/AddressRange.c \ microcontroller/__init__.c \ @@ -531,6 +548,9 @@ SRC_COMMON_HAL_ALL = \ mdns/__init__.c \ mdns/Server.c \ mdns/RemoteService.c \ + mipidsi/Bus.c \ + mipidsi/Display.c \ + mipidsi/__init__.c \ neopixel_write/__init__.c \ nvm/ByteArray.c \ nvm/__init__.c \ @@ -543,6 +563,8 @@ SRC_COMMON_HAL_ALL = \ pulseio/__init__.c \ pwmio/PWMOut.c \ pwmio/__init__.c \ + qspibus/QSPIBus.c \ + qspibus/__init__.c \ rclcpy/__init__.c \ rclcpy/Node.c \ rclcpy/Publisher.c \ @@ -606,6 +628,7 @@ $(filter $(SRC_PATTERNS), \ canio/Match.c \ codeop/__init__.c \ countio/Edge.c \ + digitalio/DigitalInOutProtocol.c \ digitalio/Direction.c \ digitalio/DriveMode.c \ digitalio/Pull.c \ @@ -673,6 +696,8 @@ SRC_SHARED_MODULE_ALL = \ audiocore/RawSample.c \ audiocore/WaveFile.c \ audiocore/__init__.c \ + audiospeed/SpeedChanger.c \ + audiospeed/__init__.c \ audiodelays/Echo.c \ audiodelays/Chorus.c \ audiodelays/PitchShift.c \ @@ -719,6 +744,9 @@ SRC_SHARED_MODULE_ALL = \ dotclockframebuffer/__init__.c \ epaperdisplay/__init__.c \ epaperdisplay/EPaperDisplay.c \ + i2cioexpander/IOExpander.c \ + i2cioexpander/IOPin.c \ + i2cioexpander/__init__.c \ floppyio/__init__.c \ fontio/BuiltinFont.c \ fontio/__init__.c \ @@ -946,7 +974,6 @@ SRC_SHARED_MODULE_INTERNAL = \ $(filter $(SRC_PATTERNS), \ displayio/bus_core.c \ displayio/display_core.c \ - os/getenv.c \ usb/utf16le.c \ ) @@ -992,7 +1019,6 @@ endif # Sources used in all ports except unix. SRC_CIRCUITPY_COMMON = \ - shared/libc/string0.c \ shared/readline/readline.c \ lib/oofatfs/ff.c \ lib/oofatfs/ffunicode.c \ @@ -1004,6 +1030,10 @@ SRC_CIRCUITPY_COMMON = \ shared/runtime/stdout_helpers.c \ shared/runtime/sys_stdio_mphal.c +ifeq ($(CIRCUITPY_LIBC_STRING0),1) +SRC_CIRCUITPY_COMMON += shared/libc/string0.c +endif + ifeq ($(CIRCUITPY_QRIO),1) SRC_CIRCUITPY_COMMON += lib/quirc/lib/decode.c lib/quirc/lib/identify.c lib/quirc/lib/quirc.c lib/quirc/lib/version_db.c $(BUILD)/lib/quirc/lib/%.o: CFLAGS += -Wno-type-limits -Wno-shadow -Wno-sign-compare -include shared-module/qrio/quirc_alloc.h diff --git a/py/circuitpy_mkenv.mk b/py/circuitpy_mkenv.mk index 7bdf943a14d04..6416d6bd19cfb 100644 --- a/py/circuitpy_mkenv.mk +++ b/py/circuitpy_mkenv.mk @@ -43,6 +43,10 @@ ifneq ($(VALID_BOARD),) include boards/$(BOARD)/mpconfigboard.mk endif +# user-specific settings that mpconfigport does not override +# (i.e. mpconfigport.mk uses "foo ?= bar") +-include user_pre_mpconfigport.mk + # Port-specific include mpconfigport.mk @@ -52,6 +56,10 @@ ifneq ($(VALID_BOARD),) include $(TOP)/py/circuitpy_mpconfig.mk endif +# user-specific overrides of hard-coded settings +# (i.e. xxx.mk uses "foo = bar") +-include user_post_mpconfigport.mk + # qstr definitions (must come before including py.mk) QSTR_DEFS = qstrdefsport.h @@ -62,3 +70,6 @@ include $(TOP)/supervisor/supervisor.mk # Include make rules and variables common across CircuitPython builds. include $(TOP)/py/circuitpy_defns.mk + +# user specific +-include user_post_circuitpy_defns.mk diff --git a/py/circuitpy_mpconfig.h b/py/circuitpy_mpconfig.h index 3ca06f7a0fd3a..4b23784c5416c 100644 --- a/py/circuitpy_mpconfig.h +++ b/py/circuitpy_mpconfig.h @@ -17,6 +17,10 @@ // Always 1: defined in circuitpy_mpconfig.mk // #define CIRCUITPY (1) +#ifndef MP_SSIZE_MAX +#define MP_SSIZE_MAX (0x7fffffff) +#endif + // REPR_C encodes qstrs, 31-bit ints, and 30-bit floats in a single 32-bit word. #ifndef MICROPY_OBJ_REPR #define MICROPY_OBJ_REPR (MICROPY_OBJ_REPR_C) @@ -42,10 +46,18 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_PY_BLUETOOTH (0) #define MICROPY_PY_LWIP_SLIP (0) #define MICROPY_PY_OS_DUPTERM (0) +#define MICROPY_PYEXEC_COMPILE_ONLY (0) #define MICROPY_ROM_TEXT_COMPRESSION (0) #define MICROPY_VFS_LFS1 (0) #define MICROPY_VFS_LFS2 (0) +// Always turn on exit code handling +#define MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (1) + +#ifndef MICROPY_GCREGS_SETJMP +#define MICROPY_GCREGS_SETJMP (0) +#endif + // Sorted alphabetically for easy finding. // // default is 128; consider raising to reduce fragmentation. @@ -84,7 +96,6 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_MEM_STATS (0) #define MICROPY_MODULE_BUILTIN_INIT (1) #define MICROPY_MODULE_BUILTIN_SUBPACKAGES (1) -#define MICROPY_NONSTANDARD_TYPECODES (0) #define MICROPY_OPT_COMPUTED_GOTO (1) #define MICROPY_OPT_COMPUTED_GOTO_SAVE_SPACE (CIRCUITPY_COMPUTED_GOTO_SAVE_SPACE) #define MICROPY_OPT_LOAD_ATTR_FAST_PATH (CIRCUITPY_OPT_LOAD_ATTR_FAST_PATH) @@ -137,6 +148,7 @@ extern void common_hal_mcu_enable_interrupts(void); #define MICROPY_PY_RE (CIRCUITPY_RE) // Supplanted by shared-bindings/struct #define MICROPY_PY_STRUCT (0) +#define MICROPY_PY_STRUCT_UNSAFE_TYPECODES (0) #define MICROPY_PY_SYS (CIRCUITPY_SYS) #define MICROPY_PY_SYS_MAXSIZE (1) #define MICROPY_PY_SYS_STDFILES (1) @@ -189,21 +201,6 @@ extern void common_hal_mcu_enable_interrupts(void); // Track stack usage. Expose results via ustack module. #define MICROPY_MAX_STACK_USAGE (0) -#define UINT_FMT "%u" -#define INT_FMT "%d" -#ifdef __LP64__ -typedef long mp_int_t; // must be pointer size -typedef unsigned long mp_uint_t; // must be pointer size -#else -// These are definitions for machines where sizeof(int) == sizeof(void*), -// regardless of actual size. -typedef int mp_int_t; // must be pointer size -typedef unsigned int mp_uint_t; // must be pointer size -#endif -#if __GNUC__ >= 10 // on recent gcc versions we can check that this is so -_Static_assert(sizeof(mp_int_t) == sizeof(void *)); -_Static_assert(sizeof(mp_uint_t) == sizeof(void *)); -#endif typedef long mp_off_t; #define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(str, len) @@ -299,12 +296,10 @@ typedef long mp_off_t; #ifdef LONGINT_IMPL_MPZ #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MP_SSIZE_MAX (0x7fffffff) #endif #ifdef LONGINT_IMPL_LONGLONG #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) -#define MP_SSIZE_MAX (0x7fffffff) #endif #ifndef MICROPY_PY_REVERSE_SPECIAL_METHODS @@ -317,10 +312,17 @@ typedef long mp_off_t; // Default board buses. +#ifndef CIRCUITPY_MUTABLE_BOARD +#define CIRCUITPY_MUTABLE_BOARD (0) +#endif + #ifndef CIRCUITPY_BOARD_I2C #if defined(DEFAULT_I2C_BUS_SCL) && defined(DEFAULT_I2C_BUS_SDA) #define CIRCUITPY_BOARD_I2C (1) #define CIRCUITPY_BOARD_I2C_PIN {{.scl = DEFAULT_I2C_BUS_SCL, .sda = DEFAULT_I2C_BUS_SDA}} +#ifndef CIRCUITPY_BOARD_I2C_SPEED +#define CIRCUITPY_BOARD_I2C_SPEED (100000) +#endif #else #define CIRCUITPY_BOARD_I2C (0) #endif @@ -345,6 +347,9 @@ typedef long mp_off_t; #endif +// For easy debugging printf's. +#define PLAT_PRINTF(...) mp_printf(&mp_plat_print, __VA_ARGS__) + #if MICROPY_PY_ASYNC_AWAIT && !CIRCUITPY_TRACEBACK #error CIRCUITPY_ASYNCIO requires CIRCUITPY_TRACEBACK #endif @@ -380,9 +385,11 @@ typedef long mp_off_t; #define CIRCUITPY_DISPLAY_LIMIT (1) #endif -// Framebuffer area size in bytes. Rounded down to power of four for alignment. +// Display area buffer size in bytes for _refresh_area() VLA. +// Allocated on stack; boards with larger displays can override per-board. +// Default 512 bytes = 128 uint32_t words. #ifndef CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE -#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (128) +#define CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE (512) #endif #else @@ -453,6 +460,7 @@ void background_callback_run_all(void); #define MICROPY_VM_HOOK_LOOP RUN_BACKGROUND_TASKS; #define MICROPY_VM_HOOK_RETURN RUN_BACKGROUND_TASKS; +#define MICROPY_INTERNAL_EVENT_HOOK (RUN_BACKGROUND_TASKS) // CIRCUITPY_AUTORELOAD_DELAY_MS = 0 will completely disable autoreload. #ifndef CIRCUITPY_AUTORELOAD_DELAY_MS @@ -513,6 +521,18 @@ void background_callback_run_all(void); // USB settings +#ifndef CIRCUITPY_SDCARD_USB +#if CIRCUITPY_USB_DEVICE +#define CIRCUITPY_SDCARD_USB (CIRCUITPY_SDCARDIO && CIRCUITPY_USB_MSC) +#else +#define CIRCUITPY_SDCARD_USB (0) +#endif +#endif + +#if CIRCUITPY_SDCARD_USB && !(CIRCUITPY_SDCARDIO) +#error CIRCUITPY_SDCARD_USB requires CIRCUITPY_SDCARDIO +#endif + // Debug level for TinyUSB. Only outputs over debug UART so it doesn't cause // additional USB logging. #ifndef CIRCUITPY_DEBUG_TINYUSB @@ -523,10 +543,18 @@ void background_callback_run_all(void); #define CIRCUITPY_USB_DEVICE_INSTANCE 0 #endif +#ifndef CIRCUITPY_USB_DEVICE_HIGH_SPEED +#define CIRCUITPY_USB_DEVICE_HIGH_SPEED 0 +#endif + #ifndef CIRCUITPY_USB_HOST_INSTANCE #define CIRCUITPY_USB_HOST_INSTANCE -1 #endif +#ifndef CIRCUITPY_USB_HOST_HIGH_SPEED +#define CIRCUITPY_USB_HOST_HIGH_SPEED 0 +#endif + // If the port requires certain USB endpoint numbers, define these in mpconfigport.h. #ifndef USB_CDC_EP_NUM_NOTIFICATION @@ -626,7 +654,7 @@ void background_callback_run_all(void); // Align the internal sector buffer. Useful when it is passed into TinyUSB for // loads. #ifndef MICROPY_FATFS_WINDOW_ALIGNMENT -#define MICROPY_FATFS_WINDOW_ALIGNMENT CIRCUITPY_TUSB_MEM_ALIGN +#define MICROPY_FATFS_WINDOW_ALIGNMENT 64 // Espressif is strictest #endif #define FF_FS_CASE_INSENSITIVE_COMPARISON_ASCII_ONLY (1) diff --git a/py/circuitpy_mpconfig.mk b/py/circuitpy_mpconfig.mk index 2009c4e177da6..170122903286b 100644 --- a/py/circuitpy_mpconfig.mk +++ b/py/circuitpy_mpconfig.mk @@ -62,6 +62,15 @@ CFLAGS += -DCIRCUITPY_FULL_BUILD=$(CIRCUITPY_FULL_BUILD) # increased build time CIRCUITPY_MESSAGE_COMPRESSION_LEVEL ?= 9 +# By default, use our copy of TLSF. Some vendor SDKs may provide their own +# implementation of TLSF, which can be used instead by setting CIRCUITPY_LIB_TLSF=0. +CIRCUITPY_LIB_TLSF ?= 1 + +# By default, use our copy of string0 (memcpy and friends) because it is optimized. Some vendor SDKs +# or ROMs may provide their own implementation of string0, which can be used instead by setting +# CIRCUITPY_LIBC_STRING0=0. +CIRCUITPY_LIBC_STRING0 ?= 1 + # Reduce the size of in-flash properties. Requires support in the .ld linker # file, so not enabled by default. CIRCUITPY_OPTIMIZE_PROPERTY_FLASH_SIZE ?= 0 @@ -98,6 +107,11 @@ CFLAGS += -DCIRCUITPY_ALARM=$(CIRCUITPY_ALARM) CIRCUITPY_ALARM_TOUCH ?= $(CIRCUITPY_ALARM) CFLAGS += -DCIRCUITPY_ALARM_TOUCH=$(CIRCUITPY_ALARM_TOUCH) +# Enable DMA buffer management for platforms where not all memory is DMA-capable +# Platforms with PSRAM or other non-DMA memory should set this to 0 +CIRCUITPY_ALL_MEMORY_DMA_CAPABLE ?= 1 +CFLAGS += -DCIRCUITPY_ALL_MEMORY_DMA_CAPABLE=$(CIRCUITPY_ALL_MEMORY_DMA_CAPABLE) + CIRCUITPY_ANALOGBUFIO ?= 0 CFLAGS += -DCIRCUITPY_ANALOGBUFIO=$(CIRCUITPY_ANALOGBUFIO) @@ -227,6 +241,12 @@ CFLAGS += -DCIRCUITPY_CYW43=$(CIRCUITPY_CYW43) CIRCUITPY_DIGITALIO ?= 1 CFLAGS += -DCIRCUITPY_DIGITALIO=$(CIRCUITPY_DIGITALIO) +# Enable the DigitalInOut protocol on the native DigitalInOut type. +# This allows other C code to use DigitalInOut objects polymorphically. +# Disable on small builds to save space. +CIRCUITPY_DIGITALINOUT_PROTOCOL ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_DIGITALINOUT_PROTOCOL=$(CIRCUITPY_DIGITALINOUT_PROTOCOL) + CIRCUITPY_COPROC ?= 0 CFLAGS += -DCIRCUITPY_COPROC=$(CIRCUITPY_COPROC) @@ -242,6 +262,10 @@ CFLAGS += -DCIRCUITPY_BUSDISPLAY=$(CIRCUITPY_BUSDISPLAY) CIRCUITPY_FOURWIRE ?= $(CIRCUITPY_DISPLAYIO) CFLAGS += -DCIRCUITPY_FOURWIRE=$(CIRCUITPY_FOURWIRE) +# QSPI bus protocol for quad-SPI displays (like RM690B0) +CIRCUITPY_QSPIBUS ?= 0 +CFLAGS += -DCIRCUITPY_QSPIBUS=$(CIRCUITPY_QSPIBUS) + CIRCUITPY_EPAPERDISPLAY ?= $(CIRCUITPY_DISPLAYIO) CFLAGS += -DCIRCUITPY_EPAPERDISPLAY=$(CIRCUITPY_EPAPERDISPLAY) @@ -275,9 +299,6 @@ CFLAGS += -DCIRCUITPY_DUALBANK=$(CIRCUITPY_DUALBANK) CIRCUITPY_ENABLE_MPY_NATIVE ?= 0 CFLAGS += -DCIRCUITPY_ENABLE_MPY_NATIVE=$(CIRCUITPY_ENABLE_MPY_NATIVE) -CIRCUITPY_OS_GETENV ?= $(CIRCUITPY_FULL_BUILD) -CFLAGS += -DCIRCUITPY_OS_GETENV=$(CIRCUITPY_OS_GETENV) - CIRCUITPY_ERRNO ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_ERRNO=$(CIRCUITPY_ERRNO) @@ -329,9 +350,15 @@ CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS=$(CIRCUITPY_HASHLIB_MBEDTLS) CIRCUITPY_HASHLIB_MBEDTLS_ONLY ?= $(call enable-if-all,$(CIRCUITPY_HASHLIB_MBEDTLS) $(call enable-if-not,$(CIRCUITPY_SSL))) CFLAGS += -DCIRCUITPY_HASHLIB_MBEDTLS_ONLY=$(CIRCUITPY_HASHLIB_MBEDTLS_ONLY) +# Always zero because it is for Zephyr only +CFLAGS += -DCIRCUITPY_HOSTNETWORK=0 + CIRCUITPY_I2CTARGET ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_I2CTARGET=$(CIRCUITPY_I2CTARGET) +CIRCUITPY_I2CIOEXPANDER ?= 0 +CFLAGS += -DCIRCUITPY_I2CIOEXPANDER=$(CIRCUITPY_I2CIOEXPANDER) + CIRCUITPY_IMAGECAPTURE ?= 0 CFLAGS += -DCIRCUITPY_IMAGECAPTURE=$(CIRCUITPY_IMAGECAPTURE) @@ -391,6 +418,9 @@ CFLAGS += -DCIRCUITPY_MEMORYMONITOR=$(CIRCUITPY_MEMORYMONITOR) CIRCUITPY_MICROCONTROLLER ?= 1 CFLAGS += -DCIRCUITPY_MICROCONTROLLER=$(CIRCUITPY_MICROCONTROLLER) +CIRCUITPY_MIPIDSI ?= 0 +CFLAGS += -DCIRCUITPY_MIPIDSI=$(CIRCUITPY_MIPIDSI) + CIRCUITPY_MSGPACK ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_MSGPACK=$(CIRCUITPY_MSGPACK) @@ -509,6 +539,9 @@ CFLAGS += -DCIRCUITPY_SERIAL_BLE=$(CIRCUITPY_SERIAL_BLE) CIRCUITPY_SETTABLE_PROCESSOR_FREQUENCY?= 0 CFLAGS += -DCIRCUITPY_SETTABLE_PROCESSOR_FREQUENCY=$(CIRCUITPY_SETTABLE_PROCESSOR_FREQUENCY) +CIRCUITPY_SETTINGS_TOML ?= $(CIRCUITPY_FULL_BUILD) +CFLAGS += -DCIRCUITPY_SETTINGS_TOML=$(CIRCUITPY_SETTINGS_TOML) + CIRCUITPY_SHARPDISPLAY ?= $(CIRCUITPY_FRAMEBUFFERIO) CFLAGS += -DCIRCUITPY_SHARPDISPLAY=$(CIRCUITPY_SHARPDISPLAY) @@ -677,6 +710,9 @@ CFLAGS += -DCIRCUITPY_USB_KEYBOARD_WORKFLOW=$(CIRCUITPY_USB_KEYBOARD_WORKFLOW) CIRCUITPY_USTACK ?= 0 CFLAGS += -DCIRCUITPY_USTACK=$(CIRCUITPY_USTACK) +# Zephyr display integration is only built by ports/zephyr-cp. +CFLAGS += -DCIRCUITPY_ZEPHYR_DISPLAY=0 + # for decompressing utilities CIRCUITPY_ZLIB ?= $(CIRCUITPY_FULL_BUILD) CFLAGS += -DCIRCUITPY_ZLIB=$(CIRCUITPY_ZLIB) diff --git a/py/compile.c b/py/compile.c index c7dd2fd476136..c8704131234f1 100644 --- a/py/compile.c +++ b/py/compile.c @@ -103,6 +103,7 @@ static const emit_method_table_t *emit_native_table[] = { &emit_native_xtensa_method_table, &emit_native_xtensawin_method_table, &emit_native_rv32_method_table, + NULL, &emit_native_debug_method_table, }; @@ -3295,7 +3296,9 @@ static void compile_scope_inline_asm(compiler_t *comp, scope_t *scope, pass_kind } // check structure of parse node - assert(MP_PARSE_NODE_IS_STRUCT(pns2->nodes[0])); + if (!MP_PARSE_NODE_IS_STRUCT(pns2->nodes[0])) { + goto not_an_instruction; + } if (!MP_PARSE_NODE_IS_NULL(pns2->nodes[1])) { goto not_an_instruction; } @@ -3587,6 +3590,13 @@ void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool case MP_EMIT_OPT_NATIVE_PYTHON: case MP_EMIT_OPT_VIPER: if (emit_native == NULL) { + // The check looks like this to work around a false + // warning in GCC 13 (and possibly later), where it + // assumes that the check will always fail. + if ((uintptr_t)NATIVE_EMITTER_TABLE == (uintptr_t)NULL) { + comp->compile_error = mp_obj_new_exception_msg(&mp_type_NotImplementedError, MP_ERROR_TEXT("cannot emit native code for this architecture")); + goto emit_finished; + } emit_native = NATIVE_EMITTER(new)(&comp->emit_common, &comp->compile_error, &comp->next_label, max_num_labels); } comp->emit_method_table = NATIVE_EMITTER_TABLE; @@ -3619,6 +3629,10 @@ void mp_compile_to_raw_code(mp_parse_tree_t *parse_tree, qstr source_file, bool } } + #if MICROPY_EMIT_NATIVE +emit_finished: + #endif + if (comp->compile_error != MP_OBJ_NULL) { // if there is no line number for the error then use the line // number for the start of this scope diff --git a/py/cstack.h b/py/cstack.h index b12a18e13fcad..bcd919d31f932 100644 --- a/py/cstack.h +++ b/py/cstack.h @@ -35,7 +35,7 @@ void mp_cstack_init_with_sp_here(size_t stack_size); -inline static void mp_cstack_init_with_top(void *top, size_t stack_size) { +static inline void mp_cstack_init_with_top(void *top, size_t stack_size) { MP_STATE_THREAD(stack_top) = (char *)top; #if MICROPY_STACK_CHECK @@ -54,7 +54,7 @@ void mp_cstack_check(void); #else -inline static void mp_cstack_check(void) { +static inline void mp_cstack_check(void) { // No-op when stack checking is disabled } diff --git a/py/dynruntime.h b/py/dynruntime.h index e87cf6591c4b6..ab155a1387579 100644 --- a/py/dynruntime.h +++ b/py/dynruntime.h @@ -70,7 +70,7 @@ #define m_realloc(ptr, new_num_bytes) (m_realloc_dyn((ptr), (new_num_bytes))) #define m_realloc_maybe(ptr, new_num_bytes, allow_move) (m_realloc_maybe_dyn((ptr), (new_num_bytes), (allow_move))) -static NORETURN inline void m_malloc_fail_dyn(size_t num_bytes) { +static MP_NORETURN inline void m_malloc_fail_dyn(size_t num_bytes) { mp_fun_table.raise_msg( mp_fun_table.load_global(MP_QSTR_MemoryError), "memory allocation failed"); @@ -289,7 +289,7 @@ static inline void *mp_obj_malloc_helper_dyn(size_t num_bytes, const mp_obj_type #define nlr_raise(o) (mp_raise_dyn(o)) #define mp_raise_type_arg(type, arg) (mp_raise_dyn(mp_obj_new_exception_arg1_dyn((type), (arg)))) -// CIRCUITPY-CHANGE: use str +// CIRCUITPY-CHANGE: use mp_raise_msg_str #define mp_raise_msg(type, msg) (mp_fun_table.raise_msg_str((type), (msg))) #define mp_raise_OSError(er) (mp_raise_OSError_dyn(er)) #define mp_raise_NotImplementedError(msg) (mp_raise_msg(&mp_type_NotImplementedError, (msg))) @@ -301,14 +301,14 @@ static inline mp_obj_t mp_obj_new_exception_arg1_dyn(const mp_obj_type_t *exc_ty return mp_call_function_n_kw(MP_OBJ_FROM_PTR(exc_type), 1, 0, &args[0]); } -static NORETURN inline void mp_raise_dyn(mp_obj_t o) { +static MP_NORETURN inline void mp_raise_dyn(mp_obj_t o) { mp_fun_table.raise(o); for (;;) { } } // CIRCUITPY-CHANGE: new routine -static NORETURN inline void mp_raise_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { +static MP_NORETURN inline void mp_raise_arg1(const mp_obj_type_t *exc_type, mp_obj_t arg) { mp_fun_table.raise(mp_obj_new_exception_arg1_dyn(exc_type, arg)); for (;;) { } diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 807befb464a84..030728cfc9adf 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -63,14 +63,14 @@ else ifeq ($(ARCH),armv6m) # thumb CROSS = arm-none-eabi- CFLAGS_ARCH += -mthumb -mcpu=cortex-m0 -MICROPY_FLOAT_IMPL ?= none +MICROPY_FLOAT_IMPL ?= float else ifeq ($(ARCH),armv7m) # thumb CROSS = arm-none-eabi- CFLAGS_ARCH += -mthumb -mcpu=cortex-m3 -MICROPY_FLOAT_IMPL ?= none +MICROPY_FLOAT_IMPL ?= float else ifeq ($(ARCH),armv7emsp) @@ -124,6 +124,10 @@ else $(error architecture '$(ARCH)' not supported) endif +ifneq ($(findstring -musl,$(shell $(CROSS)gcc -dumpmachine)),) +USE_MUSL := 1 +endif + MICROPY_FLOAT_IMPL_UPPER = $(shell echo $(MICROPY_FLOAT_IMPL) | tr '[:lower:]' '[:upper:]') CFLAGS += $(CFLAGS_ARCH) -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_$(MICROPY_FLOAT_IMPL_UPPER) @@ -147,6 +151,8 @@ ifeq ($(LINK_RUNTIME),1) # distribution. ifeq ($(USE_PICOLIBC),1) LIBM_NAME := libc.a +else ifeq ($(USE_MUSL),1) +LIBM_NAME := libc.a else LIBM_NAME := libm.a endif @@ -166,6 +172,9 @@ endif endif MPY_LD_FLAGS += $(addprefix -l, $(LIBGCC_PATH) $(LIBM_PATH)) endif +ifneq ($(MPY_EXTERN_SYM_FILE),) +MPY_LD_FLAGS += --externs "$(realpath $(MPY_EXTERN_SYM_FILE))" +endif CFLAGS += $(CFLAGS_EXTRA) diff --git a/py/emitcommon.c b/py/emitcommon.c index a9eb6e2021fc8..1369c544c54f5 100644 --- a/py/emitcommon.c +++ b/py/emitcommon.c @@ -25,6 +25,7 @@ */ #include +#include #include "py/emit.h" #include "py/nativeglue.h" @@ -72,7 +73,25 @@ static bool strictly_equal(mp_obj_t a, mp_obj_t b) { } return true; } else { - return mp_obj_equal(a, b); + if (!mp_obj_equal(a, b)) { + return false; + } + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_COMP_CONST_FLOAT + if (a_type == &mp_type_float) { + mp_float_t a_val = mp_obj_float_get(a); + // CIRCUITPY-CHANGE: ignore float equal warning + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" + if (a_val == (mp_float_t)0.0) { + // Although 0.0 == -0.0, they are not strictly_equal and + // must be stored as two different constants in .mpy files + mp_float_t b_val = mp_obj_float_get(b); + return signbit(a_val) == signbit(b_val); + } + #pragma GCC diagnostic pop + } + #endif + return true; } } diff --git a/py/emitglue.c b/py/emitglue.c index 8cc14bdc54eb5..c6ad3e3bc3845 100644 --- a/py/emitglue.c +++ b/py/emitglue.c @@ -32,6 +32,7 @@ #include #include "py/emitglue.h" +#include "py/mphal.h" #include "py/runtime0.h" #include "py/bc.h" #include "py/objfun.h" @@ -133,6 +134,9 @@ void mp_emit_glue_assign_native(mp_raw_code_t *rc, mp_raw_code_kind_t kind, cons "mcr p15, 0, r0, c7, c7, 0\n" // invalidate I-cache and D-cache : : : "r0", "cc"); #endif + #elif (MICROPY_EMIT_RV32 || MICROPY_EMIT_INLINE_RV32) && defined(MP_HAL_CLEAN_DCACHE) + // Flush the D-cache. + MP_HAL_CLEAN_DCACHE(fun_data, fun_len); #endif rc->kind = kind; diff --git a/py/emitinlinerv32.c b/py/emitinlinerv32.c index a539242b84d95..e81b152087de3 100644 --- a/py/emitinlinerv32.c +++ b/py/emitinlinerv32.c @@ -196,7 +196,6 @@ typedef enum { CALL_R, // Opcode Register CALL_RL, // Opcode Register, Label CALL_N, // Opcode - CALL_I, // Opcode Immediate CALL_RII, // Opcode Register, Register, Immediate CALL_RIR, // Opcode Register, Immediate(Register) CALL_COUNT @@ -210,7 +209,6 @@ typedef enum { #define U (1 << 2) // Unsigned immediate #define Z (1 << 3) // Non-zero -typedef void (*call_l_t)(asm_rv32_t *state, mp_uint_t label_index); typedef void (*call_ri_t)(asm_rv32_t *state, mp_uint_t rd, mp_int_t immediate); typedef void (*call_rri_t)(asm_rv32_t *state, mp_uint_t rd, mp_uint_t rs1, mp_int_t immediate); typedef void (*call_rii_t)(asm_rv32_t *state, mp_uint_t rd, mp_uint_t immediate1, mp_int_t immediate2); @@ -225,7 +223,7 @@ typedef struct _opcode_t { uint16_t argument1_mask : 4; uint16_t argument2_mask : 4; uint16_t argument3_mask : 4; - uint16_t arguments_count : 2; + uint16_t parse_nodes : 2; // 2 bits available here uint32_t calling_convention : 4; uint32_t argument1_kind : 4; @@ -234,7 +232,8 @@ typedef struct _opcode_t { uint32_t argument2_shift : 4; uint32_t argument3_kind : 4; uint32_t argument3_shift : 4; - // 4 bits available here + uint32_t required_extensions : 1; + // 3 bits available here void *emitter; } opcode_t; @@ -297,88 +296,91 @@ static const uint32_t OPCODE_MASKS[] = { }; static const opcode_t OPCODES[] = { - { MP_QSTR_add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_add }, - { MP_QSTR_addi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_addi }, - { MP_QSTR_and_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_and }, - { MP_QSTR_andi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_andi }, - { MP_QSTR_auipc, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, asm_rv32_opcode_auipc }, - { MP_QSTR_beq, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_beq }, - { MP_QSTR_bge, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bge }, - { MP_QSTR_bgeu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bgeu }, - { MP_QSTR_blt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_blt }, - { MP_QSTR_bltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bltu }, - { MP_QSTR_bne, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, asm_rv32_opcode_bne }, - { MP_QSTR_csrrc, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_csrrc }, - { MP_QSTR_csrrs, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_csrrs }, - { MP_QSTR_csrrw, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_csrrw }, - { MP_QSTR_csrrci, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, asm_rv32_opcode_csrrci }, - { MP_QSTR_csrrsi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, asm_rv32_opcode_csrrsi }, - { MP_QSTR_csrrwi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, asm_rv32_opcode_csrrwi }, - { MP_QSTR_c_add, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, asm_rv32_opcode_cadd }, - { MP_QSTR_c_addi, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, IZ, 0, N, 0, asm_rv32_opcode_caddi }, - { MP_QSTR_c_addi4spn, MASK_0000FF00, MASK_000003FC, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 0, N, 0, asm_rv32_opcode_caddi4spn }, - { MP_QSTR_c_and, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_cand }, - { MP_QSTR_c_andi, MASK_0000FF00, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, RC, 0, I, 0, N, 0, asm_rv32_opcode_candi }, - { MP_QSTR_c_beqz, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, asm_rv32_opcode_cbeqz }, - { MP_QSTR_c_bnez, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, asm_rv32_opcode_cbnez }, - { MP_QSTR_c_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_cebreak }, - { MP_QSTR_c_j, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, asm_rv32_opcode_cj }, - { MP_QSTR_c_jal, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, asm_rv32_opcode_cjal }, - { MP_QSTR_c_jalr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, asm_rv32_opcode_cjalr }, - { MP_QSTR_c_jr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, asm_rv32_opcode_cjr }, - { MP_QSTR_c_li, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, asm_rv32_opcode_cli }, - { MP_QSTR_c_lui, MASK_FFFFFFFA, MASK_0001F800, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 12, N, 0, asm_rv32_opcode_clui }, - { MP_QSTR_c_lw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 3, CALL_RIR, RC, 0, I, 0, RC, 0, asm_rv32_opcode_clw }, - { MP_QSTR_c_lwsp, MASK_FFFFFFFE, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, asm_rv32_opcode_clwsp }, - { MP_QSTR_c_mv, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, asm_rv32_opcode_cmv }, - { MP_QSTR_c_nop, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_cnop }, - { MP_QSTR_c_or, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_cor }, - { MP_QSTR_c_slli, MASK_FFFFFFFE, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, R, 0, IU, 0, N, 0, asm_rv32_opcode_cslli }, - { MP_QSTR_c_srai, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, asm_rv32_opcode_csrai }, - { MP_QSTR_c_srli, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, asm_rv32_opcode_csrli }, - { MP_QSTR_c_sub, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_csub }, - { MP_QSTR_c_sw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 3, CALL_RIR, RC, 0, I, 0, RC, 0, asm_rv32_opcode_csw }, - { MP_QSTR_c_swsp, MASK_FFFFFFFF, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, asm_rv32_opcode_cswsp }, - { MP_QSTR_c_xor, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, asm_rv32_opcode_cxor }, - { MP_QSTR_div, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_div }, - { MP_QSTR_divu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_divu }, - { MP_QSTR_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_ebreak }, - { MP_QSTR_ecall, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, asm_rv32_opcode_ecall }, - { MP_QSTR_jal, MASK_FFFFFFFF, MASK_001FFFFE, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, asm_rv32_opcode_jal }, - { MP_QSTR_jalr, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_jalr }, - { MP_QSTR_la, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, opcode_la }, - { MP_QSTR_lb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lb }, - { MP_QSTR_lbu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lbu }, - { MP_QSTR_lh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lh }, - { MP_QSTR_lhu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lhu }, - { MP_QSTR_li, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, opcode_li }, - { MP_QSTR_lui, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, asm_rv32_opcode_lui }, - { MP_QSTR_lw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_lw }, - { MP_QSTR_mv, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, asm_rv32_opcode_cmv }, - { MP_QSTR_mul, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mul }, - { MP_QSTR_mulh, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mulh }, - { MP_QSTR_mulhsu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mulhsu }, - { MP_QSTR_mulhu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_mulhu }, - { MP_QSTR_or_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_or }, - { MP_QSTR_ori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_ori }, - { MP_QSTR_rem, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_rem }, - { MP_QSTR_remu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_remu }, - { MP_QSTR_sb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_sb }, - { MP_QSTR_sh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_sh }, - { MP_QSTR_sll, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sll }, - { MP_QSTR_slli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_slli }, - { MP_QSTR_slt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_slt }, - { MP_QSTR_slti, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_slti }, - { MP_QSTR_sltiu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_sltiu }, - { MP_QSTR_sltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sltu }, - { MP_QSTR_sra, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sra }, - { MP_QSTR_srai, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_srai }, - { MP_QSTR_srl, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_srl }, - { MP_QSTR_srli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, asm_rv32_opcode_srli }, - { MP_QSTR_sub, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_sub }, - { MP_QSTR_sw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 3, CALL_RIR, R, 0, I, 0, R, 0, asm_rv32_opcode_sw }, - { MP_QSTR_xor, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, asm_rv32_opcode_xor }, - { MP_QSTR_xori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, asm_rv32_opcode_xori }, + { MP_QSTR_add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_add }, + { MP_QSTR_addi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_addi }, + { MP_QSTR_and_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_and }, + { MP_QSTR_andi, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_andi }, + { MP_QSTR_auipc, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, RV32_EXT_NONE, asm_rv32_opcode_auipc }, + { MP_QSTR_beq, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_beq }, + { MP_QSTR_bge, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_bge }, + { MP_QSTR_bgeu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_bgeu }, + { MP_QSTR_blt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_blt }, + { MP_QSTR_bltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_bltu }, + { MP_QSTR_bne, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00001FFE, 3, CALL_RRL, R, 0, R, 0, L, 0, RV32_EXT_NONE, asm_rv32_opcode_bne }, + { MP_QSTR_csrrc, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrc }, + { MP_QSTR_csrrs, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrs }, + { MP_QSTR_csrrw, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrw }, + { MP_QSTR_csrrci, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrci }, + { MP_QSTR_csrrsi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrsi }, + { MP_QSTR_csrrwi, MASK_FFFFFFFF, MASK_00000FFF, MASK_0000001F, 3, CALL_RII, R, 0, IU, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_csrrwi }, + { MP_QSTR_c_add, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cadd }, + { MP_QSTR_c_addi, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, IZ, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_caddi }, + { MP_QSTR_c_addi4spn, MASK_0000FF00, MASK_000003FC, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_caddi4spn }, + { MP_QSTR_c_and, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cand }, + { MP_QSTR_c_andi, MASK_0000FF00, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, RC, 0, I, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_candi }, + { MP_QSTR_c_beqz, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cbeqz }, + { MP_QSTR_c_bnez, MASK_0000FF00, MASK_000001FE, MASK_NOT_USED, 2, CALL_RL, RC, 0, L, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cbnez }, + { MP_QSTR_c_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cebreak }, + { MP_QSTR_c_j, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cj }, + { MP_QSTR_c_jal, MASK_00000FFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_L, L, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cjal }, + { MP_QSTR_c_jalr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cjalr }, + { MP_QSTR_c_jr, MASK_FFFFFFFE, MASK_NOT_USED, MASK_NOT_USED, 1, CALL_R, R, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cjr }, + { MP_QSTR_c_li, MASK_FFFFFFFE, MASK_0000003F, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cli }, + { MP_QSTR_c_lui, MASK_FFFFFFFA, MASK_0001F800, MASK_NOT_USED, 2, CALL_RI, R, 0, IUZ, 12, N, 0, RV32_EXT_NONE, asm_rv32_opcode_clui }, + { MP_QSTR_c_lw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 2, CALL_RIR, RC, 0, I, 0, RC, 0, RV32_EXT_NONE, asm_rv32_opcode_clw }, + { MP_QSTR_c_lwsp, MASK_FFFFFFFE, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_clwsp }, + { MP_QSTR_c_mv, MASK_FFFFFFFE, MASK_FFFFFFFE, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cmv }, + { MP_QSTR_c_nop, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cnop }, + { MP_QSTR_c_or, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cor }, + { MP_QSTR_c_slli, MASK_FFFFFFFE, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, R, 0, IU, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cslli }, + { MP_QSTR_c_srai, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_csrai }, + { MP_QSTR_c_srli, MASK_0000FF00, MASK_0000001F, MASK_NOT_USED, 2, CALL_RI, RC, 0, IU, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_csrli }, + { MP_QSTR_c_sub, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_csub }, + { MP_QSTR_c_sw, MASK_0000FF00, MASK_0000007C, MASK_0000FF00, 2, CALL_RIR, RC, 0, I, 0, RC, 0, RV32_EXT_NONE, asm_rv32_opcode_csw }, + { MP_QSTR_c_swsp, MASK_FFFFFFFF, MASK_000000FC, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cswsp }, + { MP_QSTR_c_xor, MASK_0000FF00, MASK_0000FF00, MASK_NOT_USED, 2, CALL_RR, RC, 0, RC, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cxor }, + { MP_QSTR_div, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_div }, + { MP_QSTR_divu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_divu }, + { MP_QSTR_ebreak, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_ebreak }, + { MP_QSTR_ecall, MASK_NOT_USED, MASK_NOT_USED, MASK_NOT_USED, 0, CALL_N, N, 0, N, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_ecall }, + { MP_QSTR_jal, MASK_FFFFFFFF, MASK_001FFFFE, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_jal }, + { MP_QSTR_jalr, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_jalr }, + { MP_QSTR_la, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RL, R, 0, L, 0, N, 0, RV32_EXT_NONE, opcode_la }, + { MP_QSTR_lb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lb }, + { MP_QSTR_lbu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lbu }, + { MP_QSTR_lh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lh }, + { MP_QSTR_lhu, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lhu }, + { MP_QSTR_li, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 0, N, 0, RV32_EXT_NONE, opcode_li }, + { MP_QSTR_lui, MASK_FFFFFFFF, MASK_FFFFF000, MASK_NOT_USED, 2, CALL_RI, R, 0, I, 12, N, 0, RV32_EXT_NONE, asm_rv32_opcode_lui }, + { MP_QSTR_lw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_lw }, + { MP_QSTR_mv, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_NOT_USED, 2, CALL_RR, R, 0, R, 0, N, 0, RV32_EXT_NONE, asm_rv32_opcode_cmv }, + { MP_QSTR_mul, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_mul }, + { MP_QSTR_mulh, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_mulh }, + { MP_QSTR_mulhsu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_mulhsu }, + { MP_QSTR_mulhu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_mulhu }, + { MP_QSTR_or_, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_or }, + { MP_QSTR_ori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_ori }, + { MP_QSTR_rem, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_rem }, + { MP_QSTR_remu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_remu }, + { MP_QSTR_sb, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sb }, + { MP_QSTR_sh, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sh }, + { MP_QSTR_sh1add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_ZBA, asm_rv32_opcode_sh1add }, + { MP_QSTR_sh2add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_ZBA, asm_rv32_opcode_sh2add }, + { MP_QSTR_sh3add, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_ZBA, asm_rv32_opcode_sh3add }, + { MP_QSTR_sll, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sll }, + { MP_QSTR_slli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_slli }, + { MP_QSTR_slt, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_slt }, + { MP_QSTR_slti, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_slti }, + { MP_QSTR_sltiu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_sltiu }, + { MP_QSTR_sltu, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sltu }, + { MP_QSTR_sra, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sra }, + { MP_QSTR_srai, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_srai }, + { MP_QSTR_srl, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_srl }, + { MP_QSTR_srli, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_0000001F, 3, CALL_RRI, R, 0, R, 0, IU, 0, RV32_EXT_NONE, asm_rv32_opcode_srli }, + { MP_QSTR_sub, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sub }, + { MP_QSTR_sw, MASK_FFFFFFFF, MASK_00000FFF, MASK_FFFFFFFF, 2, CALL_RIR, R, 0, I, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_sw }, + { MP_QSTR_xor, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_FFFFFFFF, 3, CALL_RRR, R, 0, R, 0, R, 0, RV32_EXT_NONE, asm_rv32_opcode_xor }, + { MP_QSTR_xori, MASK_FFFFFFFF, MASK_FFFFFFFF, MASK_00000FFF, 3, CALL_RRI, R, 0, R, 0, I, 0, RV32_EXT_NONE, asm_rv32_opcode_xori }, }; #undef RC @@ -388,9 +390,9 @@ static const opcode_t OPCODES[] = { // These two checks assume the bitmasks are contiguous. -static bool is_in_signed_mask(mp_uint_t mask, mp_uint_t value) { - mp_uint_t leading_zeroes = mp_clz(mask); - if (leading_zeroes == 0 || leading_zeroes > 32) { +static bool is_in_signed_mask(uint32_t mask, mp_uint_t value) { + uint32_t leading_zeroes = mp_clz(mask); + if (leading_zeroes == 0) { return true; } mp_uint_t positive_mask = ~(mask & ~(1U << (31 - leading_zeroes))); @@ -435,9 +437,9 @@ static bool validate_integer(mp_uint_t value, mp_uint_t mask, mp_uint_t flags) { #define ET_WRONG_ARGUMENTS_COUNT MP_ERROR_TEXT("opcode '%q': expecting %d arguments") #define ET_OUT_OF_RANGE MP_ERROR_TEXT("opcode '%q' argument %d: out of range") -static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, - const opcode_t *opcode, mp_parse_node_t node, mp_uint_t node_index) { +static bool serialise_argument(emit_inline_asm_t *emit, const opcode_t *opcode, mp_parse_node_t node, mp_uint_t node_index, mp_uint_t *serialised) { assert((node_index < 3) && "Invalid argument node number."); + assert(serialised && "Serialised value pointer is NULL."); uint32_t kind = 0; uint32_t shift = 0; @@ -466,17 +468,19 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, break; } + mp_uint_t serialised_value = 0; + switch (kind & 0x03) { case N: assert(mask == OPCODE_MASKS[MASK_NOT_USED] && "Invalid mask index for missing operand."); - return true; + break; case R: { mp_uint_t register_index; if (!parse_register_node(node, ®ister_index, false)) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_register)); + ET_WRONG_ARGUMENT_KIND, opcode->qstring, node_index + 1, MP_QSTR_register)); return false; } @@ -484,11 +488,11 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("opcode '%q' argument %d: unknown register"), - opcode_qstr, node_index + 1)); + opcode->qstring, node_index + 1)); return false; } - return true; + serialised_value = (kind & C) ? RV32_MAP_IN_C_REGISTER_WINDOW(register_index) : register_index; } break; @@ -497,7 +501,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, if (!mp_parse_node_get_int_maybe(node, &object)) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_integer)); + ET_WRONG_ARGUMENT_KIND, opcode->qstring, node_index + 1, MP_QSTR_integer)); return false; } @@ -516,7 +520,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, goto zero_immediate; } - return true; + serialised_value = immediate; } break; @@ -524,7 +528,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, if (!MP_PARSE_NODE_IS_ID(node)) { emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_label)); + ET_WRONG_ARGUMENT_KIND, opcode->qstring, node_index + 1, MP_QSTR_label)); return false; } @@ -534,7 +538,7 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("opcode '%q' argument %d: undefined label '%q'"), - opcode_qstr, node_index + 1, qstring)); + opcode->qstring, node_index + 1, qstring)); return false; } @@ -548,43 +552,45 @@ static bool validate_argument(emit_inline_asm_t *emit, qstr opcode_qstr, goto out_of_range; } } - return true; + + serialised_value = displacement; } break; default: assert(!"Unknown argument kind"); + MP_UNREACHABLE; break; } - return false; + *serialised = serialised_value; + return true; out_of_range: emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode_qstr, node_index + 1)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode->qstring, node_index + 1)); return false; zero_immediate: emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("opcode '%q' argument %d: must not be zero"), - opcode_qstr, node_index + 1)); + opcode->qstring, node_index + 1)); return false; } -static bool parse_register_offset_node(emit_inline_asm_t *emit, qstr opcode_qstr, const opcode_t *opcode_data, mp_parse_node_t node, mp_uint_t node_index, mp_parse_node_t *register_node, mp_parse_node_t *offset_node, bool *negative) { - assert(register_node != NULL && "Register node pointer is NULL."); - assert(offset_node != NULL && "Offset node pointer is NULL."); - assert(negative != NULL && "Negative pointer is NULL."); +static bool serialise_register_offset_node(emit_inline_asm_t *emit, const opcode_t *opcode_data, mp_parse_node_t node, mp_uint_t node_index, mp_uint_t *offset, mp_uint_t *base) { + assert(offset && "Attempting to store the offset value into NULL."); + assert(base && "Attempting to store the base register into NULL."); if (!MP_PARSE_NODE_IS_STRUCT_KIND(node, PN_atom_expr_normal) && !MP_PARSE_NODE_IS_STRUCT_KIND(node, PN_factor_2)) { goto invalid_structure; } mp_parse_node_struct_t *node_struct = (mp_parse_node_struct_t *)node; - *negative = false; + bool negative = false; if (MP_PARSE_NODE_IS_STRUCT_KIND(node, PN_factor_2)) { if (MP_PARSE_NODE_IS_TOKEN_KIND(node_struct->nodes[0], MP_TOKEN_OP_MINUS)) { - *negative = true; + negative = true; } else { if (!MP_PARSE_NODE_IS_TOKEN_KIND(node_struct->nodes[0], MP_TOKEN_OP_PLUS)) { goto invalid_structure; @@ -596,186 +602,97 @@ static bool parse_register_offset_node(emit_inline_asm_t *emit, qstr opcode_qstr node_struct = (mp_parse_node_struct_t *)node_struct->nodes[1]; } - if (*negative) { + if (negative) { // If the value is negative, RULE_atom_expr_normal's first token will be the // offset stripped of its negative marker; range check will then fail if the // default method is used, so a custom check is used instead. mp_obj_t object; if (!mp_parse_node_get_int_maybe(node_struct->nodes[0], &object)) { emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_WRONG_ARGUMENT_KIND, opcode_qstr, 2, MP_QSTR_integer)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_WRONG_ARGUMENT_KIND, opcode_data->qstring, 2, MP_QSTR_integer)); return false; } mp_uint_t value = mp_obj_get_int_truncated(object); value = (~value + 1) & (mp_uint_t)-1; if (!validate_integer(value << opcode_data->argument2_shift, OPCODE_MASKS[opcode_data->argument2_mask], opcode_data->argument2_kind)) { emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode_qstr, 2)); + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode_data->qstring, 2)); return false; } + *offset = value; } else { - if (!validate_argument(emit, opcode_qstr, opcode_data, node_struct->nodes[0], 1)) { + if (!serialise_argument(emit, opcode_data, node_struct->nodes[0], 1, offset)) { return false; } } - *offset_node = node_struct->nodes[0]; node_struct = (mp_parse_node_struct_t *)node_struct->nodes[1]; - if (!validate_argument(emit, opcode_qstr, opcode_data, node_struct->nodes[0], 2)) { + if (!serialise_argument(emit, opcode_data, node_struct->nodes[0], 2, base)) { return false; } - *register_node = node_struct->nodes[0]; return true; invalid_structure: emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENT_KIND, opcode_qstr, node_index + 1, MP_QSTR_offset)); + ET_WRONG_ARGUMENT_KIND, opcode_data->qstring, node_index + 1, MP_QSTR_offset)); return false; } -static void handle_opcode(emit_inline_asm_t *emit, qstr opcode, const opcode_t *opcode_data, mp_parse_node_t *arguments) { - mp_uint_t rd = 0; - mp_uint_t rs1 = 0; - mp_uint_t rs2 = 0; - +static void handle_opcode(emit_inline_asm_t *emit, const opcode_t *opcode_data, mp_uint_t *arguments) { switch (opcode_data->calling_convention) { - case CALL_RRR: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); - parse_register_node(arguments[2], &rs2, opcode_data->argument3_kind & C); - ((call_rrr_t)opcode_data->emitter)(&emit->as, rd, rs1, rs2); + case CALL_RRR: + ((call_rrr_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1], arguments[2]); break; - } - case CALL_RR: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); - ((call_rr_t)opcode_data->emitter)(&emit->as, rd, rs1); + case CALL_RR: + ((call_rr_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1]); break; - } - case CALL_RRI: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); - mp_obj_t object; - mp_parse_node_get_int_maybe(arguments[2], &object); - mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument3_shift; - ((call_rri_t)opcode_data->emitter)(&emit->as, rd, rs1, immediate); + case CALL_RRI: + ((call_rri_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1], arguments[2]); break; - } - case CALL_RI: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - mp_obj_t object; - mp_parse_node_get_int_maybe(arguments[1], &object); - mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument2_shift; - ((call_ri_t)opcode_data->emitter)(&emit->as, rd, immediate); + case CALL_RI: + ((call_ri_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1]); break; - } - case CALL_R: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - ((call_r_t)opcode_data->emitter)(&emit->as, rd); + case CALL_R: + ((call_r_t)opcode_data->emitter)(&emit->as, arguments[0]); break; - } - case CALL_RRL: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - parse_register_node(arguments[1], &rs1, opcode_data->argument2_kind & C); - qstr qstring; - mp_uint_t label_index = lookup_label(emit, arguments[2], &qstring); - ptrdiff_t displacement = label_code_offset(emit, label_index); - ((call_rri_t)opcode_data->emitter)(&emit->as, rd, rs1, displacement); + case CALL_RRL: + ((call_rri_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1], (ptrdiff_t)arguments[2]); break; - } - case CALL_RL: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - qstr qstring; - mp_uint_t label_index = lookup_label(emit, arguments[1], &qstring); - ptrdiff_t displacement = label_code_offset(emit, label_index); - ((call_ri_t)opcode_data->emitter)(&emit->as, rd, displacement); + case CALL_RL: + ((call_ri_t)opcode_data->emitter)(&emit->as, arguments[0], (ptrdiff_t)arguments[1]); break; - } - case CALL_L: { - qstr qstring; - mp_uint_t label_index = lookup_label(emit, arguments[0], &qstring); - ptrdiff_t displacement = label_code_offset(emit, label_index); - ((call_i_t)opcode_data->emitter)(&emit->as, displacement); + case CALL_L: + ((call_i_t)opcode_data->emitter)(&emit->as, (ptrdiff_t)arguments[0]); break; - } case CALL_N: ((call_n_t)opcode_data->emitter)(&emit->as); break; - case CALL_I: { - mp_obj_t object; - mp_parse_node_get_int_maybe(arguments[0], &object); - mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument1_shift; - ((call_i_t)opcode_data->emitter)(&emit->as, immediate); + case CALL_RII: + ((call_rii_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[1], arguments[2]); break; - } - - case CALL_RII: { - parse_register_node(arguments[0], &rd, opcode_data->argument1_kind & C); - mp_obj_t object; - mp_parse_node_get_int_maybe(arguments[1], &object); - mp_uint_t immediate1 = mp_obj_get_int_truncated(object) << opcode_data->argument2_shift; - mp_parse_node_get_int_maybe(arguments[2], &object); - mp_uint_t immediate2 = mp_obj_get_int_truncated(object) << opcode_data->argument3_shift; - ((call_rii_t)opcode_data->emitter)(&emit->as, rd, immediate1, immediate2); - break; - } case CALL_RIR: - assert(!"Should not get here."); + // The last two arguments indices are swapped on purpose. + ((call_rri_t)opcode_data->emitter)(&emit->as, arguments[0], arguments[2], arguments[1]); break; default: assert(!"Unhandled call convention."); + MP_UNREACHABLE; break; } } -static bool handle_load_store_opcode_with_offset(emit_inline_asm_t *emit, qstr opcode, const opcode_t *opcode_data, mp_parse_node_t *argument_nodes) { - mp_parse_node_t nodes[3] = {0}; - if (!validate_argument(emit, opcode, opcode_data, argument_nodes[0], 0)) { - return false; - } - nodes[0] = argument_nodes[0]; - bool negative = false; - if (!parse_register_offset_node(emit, opcode, opcode_data, argument_nodes[1], 1, &nodes[1], &nodes[2], &negative)) { - return false; - } - - mp_uint_t rd = 0; - mp_uint_t rs1 = 0; - if (!parse_register_node(nodes[0], &rd, opcode_data->argument1_kind & C)) { - return false; - } - if (!parse_register_node(nodes[1], &rs1, opcode_data->argument3_kind & C)) { - return false; - } - - mp_obj_t object; - mp_parse_node_get_int_maybe(nodes[2], &object); - mp_uint_t immediate = mp_obj_get_int_truncated(object) << opcode_data->argument2_shift; - if (negative) { - immediate = (~immediate + 1) & (mp_uint_t)-1; - } - if (!is_in_signed_mask(OPCODE_MASKS[opcode_data->argument2_mask], immediate)) { - emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_OUT_OF_RANGE, opcode, 2)); - return false; - } - - ((call_rri_t)opcode_data->emitter)(&emit->as, rd, rs1, immediate); - return true; -} - static void emit_inline_rv32_opcode(emit_inline_asm_t *emit, qstr opcode, mp_uint_t arguments_count, mp_parse_node_t *argument_nodes) { const opcode_t *opcode_data = NULL; for (mp_uint_t index = 0; index < MP_ARRAY_SIZE(OPCODES); index++) { @@ -785,10 +702,9 @@ static void emit_inline_rv32_opcode(emit_inline_asm_t *emit, qstr opcode, mp_uin } } - if (!opcode_data) { - emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - MP_ERROR_TEXT("unknown RV32 instruction '%q'"), opcode)); + if (!opcode_data || (asm_rv32_allowed_extensions() & opcode_data->required_extensions) != opcode_data->required_extensions) { + emit_inline_rv32_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + MP_ERROR_TEXT("invalid RV32 instruction '%q'"), opcode)); return; } @@ -796,36 +712,36 @@ static void emit_inline_rv32_opcode(emit_inline_asm_t *emit, qstr opcode, mp_uin assert((opcode_data->argument2_mask < MP_ARRAY_SIZE(OPCODE_MASKS)) && "Argument #2 opcode mask index out of bounds."); assert((opcode_data->argument3_mask < MP_ARRAY_SIZE(OPCODE_MASKS)) && "Argument #3 opcode mask index out of bounds."); assert((opcode_data->calling_convention < CALL_COUNT) && "Calling convention index out of bounds."); - if (opcode_data->calling_convention != CALL_RIR) { - if (opcode_data->arguments_count != arguments_count) { - emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, - ET_WRONG_ARGUMENTS_COUNT, opcode, opcode_data->arguments_count)); - return; - } - if (opcode_data->arguments_count >= 1 && !validate_argument(emit, opcode, opcode_data, argument_nodes[0], 0)) { + mp_uint_t serialised_arguments[3] = { 0 }; + if (arguments_count != opcode_data->parse_nodes) { + emit_inline_rv32_error_exc(emit, + mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, + ET_WRONG_ARGUMENTS_COUNT, opcode, opcode_data->parse_nodes)); + return; + } + + if (opcode_data->parse_nodes >= 1 && !serialise_argument(emit, opcode_data, argument_nodes[0], 0, &serialised_arguments[0])) { + return; + } + if (opcode_data->calling_convention == CALL_RIR) { + // "register, offset(base)" calls require some preprocessing to + // split the offset and base nodes - not to mention that if the offset + // is negative, the parser won't see the offset as a single node but as + // a sequence of the minus sign token followed by the number itself. + + if (!serialise_register_offset_node(emit, opcode_data, argument_nodes[1], 1, &serialised_arguments[1], &serialised_arguments[2])) { return; } - if (opcode_data->arguments_count >= 2 && !validate_argument(emit, opcode, opcode_data, argument_nodes[1], 1)) { + } else { + if (opcode_data->parse_nodes >= 2 && !serialise_argument(emit, opcode_data, argument_nodes[1], 1, &serialised_arguments[1])) { return; } - if (opcode_data->arguments_count >= 3 && !validate_argument(emit, opcode, opcode_data, argument_nodes[2], 2)) { + if (opcode_data->parse_nodes >= 3 && !serialise_argument(emit, opcode_data, argument_nodes[2], 2, &serialised_arguments[2])) { return; } - handle_opcode(emit, opcode, opcode_data, argument_nodes); - return; - } - - assert((opcode_data->argument2_kind & U) == 0 && "Offset must not be unsigned."); - assert((opcode_data->argument2_kind & Z) == 0 && "Offset can be zero."); - - if (arguments_count != 2) { - emit_inline_rv32_error_exc(emit, - mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, ET_WRONG_ARGUMENTS_COUNT, opcode, 2)); - return; } - handle_load_store_opcode_with_offset(emit, opcode, opcode_data, argument_nodes); + handle_opcode(emit, opcode_data, serialised_arguments); } #undef N diff --git a/py/emitinlinethumb.c b/py/emitinlinethumb.c index 7818bb4f46da8..d6596337ae5a6 100644 --- a/py/emitinlinethumb.c +++ b/py/emitinlinethumb.c @@ -150,27 +150,27 @@ typedef struct _reg_name_t { byte reg; byte name[3]; } reg_name_t; static const reg_name_t reg_name_table[] = { - {0, "r0\0"}, - {1, "r1\0"}, - {2, "r2\0"}, - {3, "r3\0"}, - {4, "r4\0"}, - {5, "r5\0"}, - {6, "r6\0"}, - {7, "r7\0"}, - {8, "r8\0"}, - {9, "r9\0"}, - {10, "r10"}, - {11, "r11"}, - {12, "r12"}, - {13, "r13"}, - {14, "r14"}, - {15, "r15"}, - {10, "sl\0"}, - {11, "fp\0"}, - {13, "sp\0"}, - {14, "lr\0"}, - {15, "pc\0"}, + {0, {'r', '0' }}, + {1, {'r', '1' }}, + {2, {'r', '2' }}, + {3, {'r', '3' }}, + {4, {'r', '4' }}, + {5, {'r', '5' }}, + {6, {'r', '6' }}, + {7, {'r', '7' }}, + {8, {'r', '8' }}, + {9, {'r', '9' }}, + {10, {'r', '1', '0' }}, + {11, {'r', '1', '1' }}, + {12, {'r', '1', '2' }}, + {13, {'r', '1', '3' }}, + {14, {'r', '1', '4' }}, + {15, {'r', '1', '5' }}, + {10, {'s', 'l' }}, + {11, {'f', 'p' }}, + {13, {'s', 'p' }}, + {14, {'l', 'r' }}, + {15, {'p', 'c' }}, }; #define MAX_SPECIAL_REGISTER_NAME_LENGTH 7 @@ -368,20 +368,20 @@ typedef struct _cc_name_t { byte cc; byte name[2]; } cc_name_t; static const cc_name_t cc_name_table[] = { - { ASM_THUMB_CC_EQ, "eq" }, - { ASM_THUMB_CC_NE, "ne" }, - { ASM_THUMB_CC_CS, "cs" }, - { ASM_THUMB_CC_CC, "cc" }, - { ASM_THUMB_CC_MI, "mi" }, - { ASM_THUMB_CC_PL, "pl" }, - { ASM_THUMB_CC_VS, "vs" }, - { ASM_THUMB_CC_VC, "vc" }, - { ASM_THUMB_CC_HI, "hi" }, - { ASM_THUMB_CC_LS, "ls" }, - { ASM_THUMB_CC_GE, "ge" }, - { ASM_THUMB_CC_LT, "lt" }, - { ASM_THUMB_CC_GT, "gt" }, - { ASM_THUMB_CC_LE, "le" }, + { ASM_THUMB_CC_EQ, { 'e', 'q' }}, + { ASM_THUMB_CC_NE, { 'n', 'e' }}, + { ASM_THUMB_CC_CS, { 'c', 's' }}, + { ASM_THUMB_CC_CC, { 'c', 'c' }}, + { ASM_THUMB_CC_MI, { 'm', 'i' }}, + { ASM_THUMB_CC_PL, { 'p', 'l' }}, + { ASM_THUMB_CC_VS, { 'v', 's' }}, + { ASM_THUMB_CC_VC, { 'v', 'c' }}, + { ASM_THUMB_CC_HI, { 'h', 'i' }}, + { ASM_THUMB_CC_LS, { 'l', 's' }}, + { ASM_THUMB_CC_GE, { 'g', 'e' }}, + { ASM_THUMB_CC_LT, { 'l', 't' }}, + { ASM_THUMB_CC_GT, { 'g', 't' }}, + { ASM_THUMB_CC_LE, { 'l', 'e' }}, }; typedef struct _format_4_op_t { byte op; @@ -389,21 +389,21 @@ typedef struct _format_4_op_t { byte op; } format_4_op_t; #define X(x) (((x) >> 4) & 0xff) // only need 1 byte to distinguish these ops static const format_4_op_t format_4_op_table[] = { - { X(ASM_THUMB_FORMAT_4_EOR), "eor" }, - { X(ASM_THUMB_FORMAT_4_LSL), "lsl" }, - { X(ASM_THUMB_FORMAT_4_LSR), "lsr" }, - { X(ASM_THUMB_FORMAT_4_ASR), "asr" }, - { X(ASM_THUMB_FORMAT_4_ADC), "adc" }, - { X(ASM_THUMB_FORMAT_4_SBC), "sbc" }, - { X(ASM_THUMB_FORMAT_4_ROR), "ror" }, - { X(ASM_THUMB_FORMAT_4_TST), "tst" }, - { X(ASM_THUMB_FORMAT_4_NEG), "neg" }, - { X(ASM_THUMB_FORMAT_4_CMP), "cmp" }, - { X(ASM_THUMB_FORMAT_4_CMN), "cmn" }, - { X(ASM_THUMB_FORMAT_4_ORR), "orr" }, - { X(ASM_THUMB_FORMAT_4_MUL), "mul" }, - { X(ASM_THUMB_FORMAT_4_BIC), "bic" }, - { X(ASM_THUMB_FORMAT_4_MVN), "mvn" }, + { X(ASM_THUMB_FORMAT_4_EOR), {'e', 'o', 'r' }}, + { X(ASM_THUMB_FORMAT_4_LSL), {'l', 's', 'l' }}, + { X(ASM_THUMB_FORMAT_4_LSR), {'l', 's', 'r' }}, + { X(ASM_THUMB_FORMAT_4_ASR), {'a', 's', 'r' }}, + { X(ASM_THUMB_FORMAT_4_ADC), {'a', 'd', 'c' }}, + { X(ASM_THUMB_FORMAT_4_SBC), {'s', 'b', 'c' }}, + { X(ASM_THUMB_FORMAT_4_ROR), {'r', 'o', 'r' }}, + { X(ASM_THUMB_FORMAT_4_TST), {'t', 's', 't' }}, + { X(ASM_THUMB_FORMAT_4_NEG), {'n', 'e', 'g' }}, + { X(ASM_THUMB_FORMAT_4_CMP), {'c', 'm', 'p' }}, + { X(ASM_THUMB_FORMAT_4_CMN), {'c', 'm', 'n' }}, + { X(ASM_THUMB_FORMAT_4_ORR), {'o', 'r', 'r' }}, + { X(ASM_THUMB_FORMAT_4_MUL), {'m', 'u', 'l' }}, + { X(ASM_THUMB_FORMAT_4_BIC), {'b', 'i', 'c' }}, + { X(ASM_THUMB_FORMAT_4_MVN), {'m', 'v', 'n' }}, }; #undef X @@ -428,10 +428,10 @@ typedef struct _format_vfp_op_t { char name[3]; } format_vfp_op_t; static const format_vfp_op_t format_vfp_op_table[] = { - { 0x30, "add" }, - { 0x34, "sub" }, - { 0x20, "mul" }, - { 0x80, "div" }, + { 0x30, {'a', 'd', 'd' }}, + { 0x34, {'s', 'u', 'b' }}, + { 0x20, {'m', 'u', 'l' }}, + { 0x80, {'d', 'i', 'v' }}, }; // shorthand alias for whether we allow ARMv7-M instructions diff --git a/py/emitinlinextensa.c b/py/emitinlinextensa.c index fed259cfc6b20..d0eb3d566fce3 100644 --- a/py/emitinlinextensa.c +++ b/py/emitinlinextensa.c @@ -173,7 +173,7 @@ static int get_arg_label(emit_inline_asm_t *emit, const char *op, mp_parse_node_ #define RRI8_B (2) typedef struct _opcode_table_3arg_t { - uint16_t name; // actually a qstr, which should fit in 16 bits + qstr_short_t name; uint8_t type; uint8_t a0 : 4; uint8_t a1 : 4; @@ -187,6 +187,13 @@ static const opcode_table_3arg_t opcode_table_3arg[] = { {MP_QSTR_add, RRR, 0, 8}, {MP_QSTR_sub, RRR, 0, 12}, {MP_QSTR_mull, RRR, 2, 8}, + {MP_QSTR_addx2, RRR, 0, 9}, + {MP_QSTR_addx4, RRR, 0, 10}, + {MP_QSTR_addx8, RRR, 0, 11}, + {MP_QSTR_subx2, RRR, 0, 13}, + {MP_QSTR_subx4, RRR, 0, 14}, + {MP_QSTR_subx8, RRR, 0, 15}, + {MP_QSTR_src, RRR, 1, 8}, // load/store/addi opcodes: reg, reg, imm // upper nibble of type encodes the range of the immediate arg @@ -208,21 +215,62 @@ static const opcode_table_3arg_t opcode_table_3arg[] = { {MP_QSTR_bge, RRI8_B, ASM_XTENSA_CC_GE, 0}, {MP_QSTR_bgeu, RRI8_B, ASM_XTENSA_CC_GEU, 0}, {MP_QSTR_blt, RRI8_B, ASM_XTENSA_CC_LT, 0}, + {MP_QSTR_bltu, RRI8_B, ASM_XTENSA_CC_LTU, 0}, {MP_QSTR_bnall, RRI8_B, ASM_XTENSA_CC_NALL, 0}, {MP_QSTR_bne, RRI8_B, ASM_XTENSA_CC_NE, 0}, {MP_QSTR_bnone, RRI8_B, ASM_XTENSA_CC_NONE, 0}, }; +// The index of the first four qstrs matches the CCZ condition value to be +// embedded into the opcode. +static const qstr_short_t BCCZ_OPCODES[] = { + MP_QSTR_beqz, MP_QSTR_bnez, MP_QSTR_bltz, MP_QSTR_bgez, + MP_QSTR_beqz_n, MP_QSTR_bnez_n +}; + +#if MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES +typedef struct _single_opcode_t { + qstr_short_t name; + uint16_t value; +} single_opcode_t; + +static const single_opcode_t NOARGS_OPCODES[] = { + {MP_QSTR_dsync, 0x2030}, + {MP_QSTR_esync, 0x2020}, + {MP_QSTR_extw, 0x20D0}, + {MP_QSTR_ill, 0x0000}, + {MP_QSTR_isync, 0x2000}, + {MP_QSTR_memw, 0x20C0}, + {MP_QSTR_rsync, 0x2010}, +}; +#endif + static void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_args, mp_parse_node_t *pn_args) { size_t op_len; const char *op_str = (const char *)qstr_data(op, &op_len); if (n_args == 0) { - if (op == MP_QSTR_ret_n) { + if (op == MP_QSTR_ret_n || op == MP_QSTR_ret) { asm_xtensa_op_ret_n(&emit->as); - } else { - goto unknown_op; + return; + } else if (op == MP_QSTR_nop) { + asm_xtensa_op24(&emit->as, 0x20F0); + return; + } else if (op == MP_QSTR_nop_n) { + asm_xtensa_op16(&emit->as, 0xF03D); + return; } + #if MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES + for (size_t index = 0; index < MP_ARRAY_SIZE(NOARGS_OPCODES); index++) { + const single_opcode_t *opcode = &NOARGS_OPCODES[index]; + if (op == opcode->name) { + asm_xtensa_op24(&emit->as, opcode->value); + return; + } + } + #endif + + goto unknown_op; } else if (n_args == 1) { if (op == MP_QSTR_callx0) { @@ -234,19 +282,45 @@ static void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ } else if (op == MP_QSTR_jx) { uint r0 = get_arg_reg(emit, op_str, pn_args[0]); asm_xtensa_op_jx(&emit->as, r0); + } else if (op == MP_QSTR_ssl) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op_ssl(&emit->as, r0); + } else if (op == MP_QSTR_ssr) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op_ssr(&emit->as, r0); + } else if (op == MP_QSTR_ssai) { + mp_uint_t sa = get_arg_i(emit, op_str, pn_args[0], 0, 31); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 4, sa & 0x0F, (sa >> 4) & 0x01)); + } else if (op == MP_QSTR_ssa8b) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 3, r0, 0)); + } else if (op == MP_QSTR_ssa8l) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 2, r0, 0)); + } else if (op == MP_QSTR_call0) { + mp_uint_t label = get_arg_label(emit, op_str, pn_args[0]); + asm_xtensa_call0(&emit->as, label); + #if MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES + } else if (op == MP_QSTR_fsync) { + mp_uint_t imm3 = get_arg_i(emit, op_str, pn_args[0], 0, 7); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 0, 2, 8 | imm3, 0)); + } else if (op == MP_QSTR_ill_n) { + asm_xtensa_op16(&emit->as, 0xF06D); + #endif } else { goto unknown_op; } } else if (n_args == 2) { uint r0 = get_arg_reg(emit, op_str, pn_args[0]); - if (op == MP_QSTR_beqz) { - int label = get_arg_label(emit, op_str, pn_args[1]); - asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_EQ, r0, label); - } else if (op == MP_QSTR_bnez) { - int label = get_arg_label(emit, op_str, pn_args[1]); - asm_xtensa_bccz_reg_label(&emit->as, ASM_XTENSA_CCZ_NE, r0, label); - } else if (op == MP_QSTR_mov || op == MP_QSTR_mov_n) { + for (size_t index = 0; index < MP_ARRAY_SIZE(BCCZ_OPCODES); index++) { + if (op == BCCZ_OPCODES[index]) { + mp_uint_t label = get_arg_label(emit, op_str, pn_args[1]); + asm_xtensa_bccz_reg_label(&emit->as, index & 0x03, r0, label); + return; + } + } + if (op == MP_QSTR_mov || op == MP_QSTR_mov_n) { // we emit mov.n for both "mov" and "mov_n" opcodes uint r1 = get_arg_reg(emit, op_str, pn_args[1]); asm_xtensa_op_mov_n(&emit->as, r0, r1); @@ -254,7 +328,53 @@ static void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ // for convenience we emit l32r if the integer doesn't fit in movi uint32_t imm = get_arg_i(emit, op_str, pn_args[1], 0, 0); asm_xtensa_mov_reg_i32(&emit->as, r0, imm); - } else { + } else if (op == MP_QSTR_abs_) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 6, r0, 1, r1)); + } else if (op == MP_QSTR_neg) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 6, r0, 0, r1)); + } else if (op == MP_QSTR_sll) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 10, r0, r1, 0)); + } else if (op == MP_QSTR_sra) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 11, r0, 0, r1)); + } else if (op == MP_QSTR_srl) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 9, r0, 0, r1)); + } else if (op == MP_QSTR_nsa) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 14, r1, r0)); + } else if (op == MP_QSTR_nsau) { + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 0, 4, 15, r1, r0)); + } else if (op == MP_QSTR_l32r) { + mp_uint_t label = get_arg_label(emit, op_str, pn_args[1]); + asm_xtensa_l32r(&emit->as, r0, label); + } else if (op == MP_QSTR_movi_n) { + mp_int_t imm = get_arg_i(emit, op_str, pn_args[1], -32, 95); + asm_xtensa_op_movi_n(&emit->as, r0, imm); + } else + #if MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES + if (op == MP_QSTR_rsr) { + mp_uint_t sr = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RSR(0, 3, 0, sr, r0)); + } else if (op == MP_QSTR_rur) { + mp_uint_t imm8 = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 3, 14, r0, (imm8 >> 4) & 0x0F, imm8 & 0x0F)); + } else if (op == MP_QSTR_wsr) { + mp_uint_t sr = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RSR(0, 3, 1, sr, r0)); + } else if (op == MP_QSTR_wur) { + mp_uint_t sr = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RSR(0, 3, 15, sr, r0)); + } else if (op == MP_QSTR_xsr) { + mp_uint_t sr = get_arg_i(emit, op_str, pn_args[1], 0, 255); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RSR(0, 1, 6, sr, r0)); + } else + #endif + { goto unknown_op; } @@ -288,7 +408,72 @@ static void emit_inline_xtensa_op(emit_inline_asm_t *emit, qstr op, mp_uint_t n_ return; } } - goto unknown_op; + + if (op == MP_QSTR_add_n) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t r2 = get_arg_reg(emit, op_str, pn_args[2]); + asm_xtensa_op16(&emit->as, ASM_XTENSA_ENCODE_RRRN(10, r0, r1, r2)); + } else if (op == MP_QSTR_addi_n) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_int_t imm4 = get_arg_i(emit, op_str, pn_args[2], -1, 15); + asm_xtensa_op16(&emit->as, ASM_XTENSA_ENCODE_RRRN(11, r0, r1, (imm4 != 0 ? imm4 : -1))); + } else if (op == MP_QSTR_addmi) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_int_t imm8 = get_arg_i(emit, op_str, pn_args[2], -128 * 256, 127 * 256); + if ((imm8 & 0xFF) != 0) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("%d is not a multiple of %d"), imm8, 256)); + } else { + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRI8(2, 13, r1, r0, imm8 >> 8)); + } + } else if (op == MP_QSTR_bbci) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t bit = get_arg_i(emit, op_str, pn_args[1], 0, 31); + mp_int_t label = get_arg_label(emit, op_str, pn_args[2]); + asm_xtensa_bit_branch(&emit->as, r0, bit, label, 6); + } else if (op == MP_QSTR_bbsi) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t bit = get_arg_i(emit, op_str, pn_args[1], 0, 31); + mp_uint_t label = get_arg_label(emit, op_str, pn_args[2]); + asm_xtensa_bit_branch(&emit->as, r0, bit, label, 14); + } else if (op == MP_QSTR_slli) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t bits = 32 - get_arg_i(emit, op_str, pn_args[2], 1, 31); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 0 | ((bits >> 4) & 0x01), r0, r1, bits & 0x0F)); + } else if (op == MP_QSTR_srai) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t bits = get_arg_i(emit, op_str, pn_args[2], 0, 31); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 2 | ((bits >> 4) & 0x01), r0, bits & 0x0F, r1)); + } else if (op == MP_QSTR_srli) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t bits = get_arg_i(emit, op_str, pn_args[2], 0, 15); + asm_xtensa_op24(&emit->as, ASM_XTENSA_ENCODE_RRR(0, 1, 4, r0, bits, r1)); + } else if (op == MP_QSTR_l32i_n) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t imm = get_arg_i(emit, op_str, pn_args[2], 0, 60); + if ((imm & 0x03) != 0) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("%d is not a multiple of %d"), imm, 4)); + } else { + asm_xtensa_op_l32i_n(&emit->as, r0, r1, imm >> 2); + } + } else if (op == MP_QSTR_s32i_n) { + mp_uint_t r0 = get_arg_reg(emit, op_str, pn_args[0]); + mp_uint_t r1 = get_arg_reg(emit, op_str, pn_args[1]); + mp_uint_t imm = get_arg_i(emit, op_str, pn_args[2], 0, 60); + if ((imm & 0x03) != 0) { + emit_inline_xtensa_error_exc(emit, mp_obj_new_exception_msg_varg(&mp_type_SyntaxError, MP_ERROR_TEXT("%d is not a multiple of %d"), imm, 4)); + } else { + asm_xtensa_op_s32i_n(&emit->as, r0, r1, imm >> 2); + } + } else { + goto unknown_op; + } } else { goto unknown_op; diff --git a/py/emitnative.c b/py/emitnative.c index a888418e5dfed..6f12200f7e171 100644 --- a/py/emitnative.c +++ b/py/emitnative.c @@ -222,12 +222,6 @@ static const uint8_t reg_local_table[MAX_REGS_FOR_LOCAL_VARS] = {REG_LOCAL_1, RE *emit->error_slot = mp_obj_new_exception_msg_varg(&mp_type_ViperTypeError, __VA_ARGS__); \ } while (0) -#if N_RV32 -#define FIT_SIGNED(value, bits) \ - ((((value) & ~((1U << ((bits) - 1)) - 1)) == 0) || \ - (((value) & ~((1U << ((bits) - 1)) - 1)) == ~((1U << ((bits) - 1)) - 1))) -#endif - typedef enum { STACK_VALUE, STACK_REG, @@ -335,12 +329,27 @@ struct _emit_t { #define ASM_CLR_REG(state, rd) ASM_XOR_REG_REG(state, rd, rd) #endif +#if N_RV32 +#define ASM_MOV_LOCAL_MP_OBJ_NULL(as, local_num, reg_temp) \ + ASM_MOV_LOCAL_REG(as, local_num, REG_ZERO) +#else +#define ASM_MOV_LOCAL_MP_OBJ_NULL(as, local_num, reg_temp) \ + ASM_MOV_REG_IMM(as, reg_temp, (mp_uint_t)MP_OBJ_NULL); \ + ASM_MOV_LOCAL_REG(as, local_num, reg_temp) +#endif + static void emit_load_reg_with_object(emit_t *emit, int reg, mp_obj_t obj); static void emit_native_global_exc_entry(emit_t *emit); static void emit_native_global_exc_exit(emit_t *emit); static void emit_native_load_const_obj(emit_t *emit, mp_obj_t obj); emit_t *EXPORT_FUN(new)(mp_emit_common_t * emit_common, mp_obj_t *error_slot, uint *label_slot, mp_uint_t max_num_labels) { + // Generated code performing exception handling assumes that MP_OBJ_NULL + // equals to 0 to simplify some checks, leveraging dedicated opcodes for + // comparisons against 0. If this assumption does not hold true anymore + // then generated code won't work correctly. + MP_STATIC_ASSERT(MP_OBJ_NULL == 0); + emit_t *emit = m_new0(emit_t, 1); emit->emit_common = emit_common; emit->error_slot = error_slot; @@ -467,6 +476,30 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->stack_info[i].vtype = VTYPE_UNBOUND; } + char *qualified_name = NULL; + + #if N_DEBUG + scope_t *current_scope = scope; + vstr_t *qualified_name_vstr = vstr_new(qstr_len(current_scope->simple_name)); + size_t fragment_length = 0; + const byte *fragment_pointer; + for (;;) { + fragment_pointer = qstr_data(current_scope->simple_name, &fragment_length); + vstr_hint_size(qualified_name_vstr, fragment_length); + memmove(qualified_name_vstr->buf + fragment_length, qualified_name_vstr->buf, qualified_name_vstr->len); + memcpy(qualified_name_vstr->buf, fragment_pointer, fragment_length); + qualified_name_vstr->len += fragment_length; + if (current_scope->parent == NULL || current_scope->parent->simple_name == MP_QSTR__lt_module_gt_) { + break; + } + vstr_ins_char(qualified_name_vstr, 0, '.'); + current_scope = current_scope->parent; + } + qualified_name = vstr_null_terminated_str(qualified_name_vstr); + #else + (void)qualified_name; + #endif + mp_asm_base_start_pass(&emit->as->base, pass == MP_PASS_EMIT ? MP_ASM_PASS_EMIT : MP_ASM_PASS_COMPUTE); // generate code for entry to function @@ -513,7 +546,7 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop } // Entry to function - ASM_ENTRY(emit->as, emit->stack_start + emit->n_state - num_locals_in_regs); + ASM_ENTRY(emit->as, emit->stack_start + emit->n_state - num_locals_in_regs, qualified_name); #if N_X86 asm_x86_mov_arg_to_r32(emit->as, 0, REG_PARENT_ARG_1); @@ -584,7 +617,7 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop if (emit->scope->scope_flags & MP_SCOPE_FLAG_GENERATOR) { mp_asm_base_data(&emit->as->base, ASM_WORD_SIZE, (uintptr_t)emit->start_offset); - ASM_ENTRY(emit->as, emit->code_state_start); + ASM_ENTRY(emit->as, emit->code_state_start, qualified_name); // Reset the state size for the state pointed to by REG_GENERATOR_STATE emit->code_state_start = 0; @@ -616,7 +649,7 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop emit->stack_start = emit->code_state_start + SIZEOF_CODE_STATE; // Allocate space on C-stack for code_state structure, which includes state - ASM_ENTRY(emit->as, emit->stack_start + emit->n_state); + ASM_ENTRY(emit->as, emit->stack_start + emit->n_state, qualified_name); // Prepare incoming arguments for call to mp_setup_code_state @@ -682,6 +715,10 @@ static void emit_native_start_pass(emit_t *emit, pass_kind_t pass, scope_t *scop } } } + + #if N_DEBUG + vstr_free(qualified_name_vstr); + #endif } static inline void emit_native_write_code_info_byte(emit_t *emit, byte val) { @@ -1311,8 +1348,7 @@ static void emit_native_global_exc_entry(emit_t *emit) { // Check LOCAL_IDX_THROW_VAL for any injected value ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_THROW_VAL(emit)); - ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)MP_OBJ_NULL); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); + ASM_MOV_LOCAL_MP_OBJ_NULL(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); emit_call(emit, MP_F_NATIVE_RAISE); } } @@ -1579,87 +1615,50 @@ static void emit_native_load_subscr(emit_t *emit) { switch (vtype_base) { case VTYPE_PTR8: { // pointer to 8-bit memory - // TODO optimise to use thumb ldrb r1, [r2, r3] + #ifdef ASM_LOAD8_REG_REG_OFFSET + ASM_LOAD8_REG_REG_OFFSET(emit->as, REG_RET, reg_base, index_value); + #else if (index_value != 0) { // index is non-zero - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_ldrb_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 12)) { - asm_rv32_opcode_lbu(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_op_l8ui(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base reg_base = reg_index; } ASM_LOAD8_REG_REG(emit->as, REG_RET, reg_base); // load from (base+index) + #endif break; } case VTYPE_PTR16: { // pointer to 16-bit memory + #ifdef ASM_LOAD16_REG_REG_OFFSET + ASM_LOAD16_REG_REG_OFFSET(emit->as, REG_RET, reg_base, index_value); + #else if (index_value != 0) { // index is a non-zero immediate - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_ldrh_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 11)) { - asm_rv32_opcode_lhu(emit->as, REG_RET, reg_base, index_value << 1); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_op_l16ui(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base reg_base = reg_index; } ASM_LOAD16_REG_REG(emit->as, REG_RET, reg_base); // load from (base+2*index) + #endif break; } case VTYPE_PTR32: { // pointer to 32-bit memory + #ifdef ASM_LOAD32_REG_REG_OFFSET + ASM_LOAD32_REG_REG_OFFSET(emit->as, REG_RET, reg_base, index_value); + #else if (index_value != 0) { // index is a non-zero immediate - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_ldr_rlo_rlo_i5(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 10)) { - asm_rv32_opcode_lw(emit->as, REG_RET, reg_base, index_value << 2); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_l32i_optimised(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif need_reg_single(emit, reg_index, 0); ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 4*index to base reg_base = reg_index; } ASM_LOAD32_REG_REG(emit->as, REG_RET, reg_base); // load from (base+4*index) + #endif break; } default: @@ -1680,40 +1679,36 @@ static void emit_native_load_subscr(emit_t *emit) { switch (vtype_base) { case VTYPE_PTR8: { // pointer to 8-bit memory - // TODO optimise to use thumb ldrb r1, [r2, r3] + #ifdef ASM_LOAD8_REG_REG_REG + ASM_LOAD8_REG_REG_REG(emit->as, REG_RET, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_LOAD8_REG_REG(emit->as, REG_RET, REG_ARG_1); // store value to (base+index) + #endif break; } case VTYPE_PTR16: { // pointer to 16-bit memory - #if N_XTENSA || N_XTENSAWIN - asm_xtensa_op_addx2(emit->as, REG_ARG_1, reg_index, REG_ARG_1); - asm_xtensa_op_l16ui(emit->as, REG_RET, REG_ARG_1, 0); - break; - #endif + #ifdef ASM_LOAD16_REG_REG_REG + ASM_LOAD16_REG_REG_REG(emit->as, REG_RET, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_LOAD16_REG_REG(emit->as, REG_RET, REG_ARG_1); // load from (base+2*index) + #endif break; } case VTYPE_PTR32: { // pointer to word-size memory - #if N_RV32 - asm_rv32_opcode_slli(emit->as, REG_TEMP2, reg_index, 2); - asm_rv32_opcode_cadd(emit->as, REG_ARG_1, REG_TEMP2); - asm_rv32_opcode_lw(emit->as, REG_RET, REG_ARG_1, 0); - break; - #elif N_XTENSA || N_XTENSAWIN - asm_xtensa_op_addx4(emit->as, REG_ARG_1, reg_index, REG_ARG_1); - asm_xtensa_op_l32i_n(emit->as, REG_RET, REG_ARG_1, 0); - break; - #endif + #ifdef ASM_LOAD32_REG_REG_REG + ASM_LOAD32_REG_REG_REG(emit->as, REG_RET, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_LOAD32_REG_REG(emit->as, REG_RET, REG_ARG_1); // load from (base+4*index) + #endif break; } default: @@ -1856,92 +1851,47 @@ static void emit_native_store_subscr(emit_t *emit) { switch (vtype_base) { case VTYPE_PTR8: { // pointer to 8-bit memory - // TODO optimise to use thumb strb r1, [r2, r3] + #ifdef ASM_STORE8_REG_REG_OFFSET + ASM_STORE8_REG_REG_OFFSET(emit->as, reg_value, reg_base, index_value); + #else if (index_value != 0) { // index is non-zero - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_strb_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 12)) { - asm_rv32_opcode_sb(emit->as, reg_value, reg_base, index_value); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_op_s8i(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif ASM_MOV_REG_IMM(emit->as, reg_index, index_value); - #if N_ARM - asm_arm_strb_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); - return; - #endif ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add index to base reg_base = reg_index; } ASM_STORE8_REG_REG(emit->as, reg_value, reg_base); // store value to (base+index) + #endif break; } case VTYPE_PTR16: { // pointer to 16-bit memory + #ifdef ASM_STORE16_REG_REG_OFFSET + ASM_STORE16_REG_REG_OFFSET(emit->as, reg_value, reg_base, index_value); + #else if (index_value != 0) { // index is a non-zero immediate - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_strh_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 11)) { - asm_rv32_opcode_sh(emit->as, reg_value, reg_base, index_value << 1); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_op_s16i(emit->as, REG_RET, reg_base, index_value); - break; - } - #endif ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 1); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 2*index to base reg_base = reg_index; } ASM_STORE16_REG_REG(emit->as, reg_value, reg_base); // store value to (base+2*index) + #endif break; } case VTYPE_PTR32: { // pointer to 32-bit memory + #ifdef ASM_STORE32_REG_REG_OFFSET + ASM_STORE32_REG_REG_OFFSET(emit->as, reg_value, reg_base, index_value); + #else if (index_value != 0) { // index is a non-zero immediate - #if N_THUMB - if (index_value > 0 && index_value < 32) { - asm_thumb_str_rlo_rlo_i5(emit->as, reg_value, reg_base, index_value); - break; - } - #elif N_RV32 - if (FIT_SIGNED(index_value, 10)) { - asm_rv32_opcode_sw(emit->as, reg_value, reg_base, index_value << 2); - break; - } - #elif N_XTENSA || N_XTENSAWIN - if (index_value > 0 && index_value < 256) { - asm_xtensa_s32i_optimised(emit->as, REG_RET, reg_base, index_value); - break; - } - #elif N_ARM - ASM_MOV_REG_IMM(emit->as, reg_index, index_value); - asm_arm_str_reg_reg_reg(emit->as, reg_value, reg_base, reg_index); - return; - #endif ASM_MOV_REG_IMM(emit->as, reg_index, index_value << 2); ASM_ADD_REG_REG(emit->as, reg_index, reg_base); // add 4*index to base reg_base = reg_index; } ASM_STORE32_REG_REG(emit->as, reg_value, reg_base); // store value to (base+4*index) + #endif break; } default: @@ -1972,50 +1922,36 @@ static void emit_native_store_subscr(emit_t *emit) { switch (vtype_base) { case VTYPE_PTR8: { // pointer to 8-bit memory - // TODO optimise to use thumb strb r1, [r2, r3] - #if N_ARM - asm_arm_strb_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); - break; - #endif + #ifdef ASM_STORE8_REG_REG_REG + ASM_STORE8_REG_REG_REG(emit->as, reg_value, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_STORE8_REG_REG(emit->as, reg_value, REG_ARG_1); // store value to (base+index) + #endif break; } case VTYPE_PTR16: { // pointer to 16-bit memory - #if N_ARM - asm_arm_strh_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); - break; - #elif N_XTENSA || N_XTENSAWIN - asm_xtensa_op_addx2(emit->as, REG_ARG_1, reg_index, REG_ARG_1); - asm_xtensa_op_s16i(emit->as, reg_value, REG_ARG_1, 0); - break; - #endif + #ifdef ASM_STORE16_REG_REG_REG + ASM_STORE16_REG_REG_REG(emit->as, reg_value, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_STORE16_REG_REG(emit->as, reg_value, REG_ARG_1); // store value to (base+2*index) + #endif break; } case VTYPE_PTR32: { // pointer to 32-bit memory - #if N_ARM - asm_arm_str_reg_reg_reg(emit->as, reg_value, REG_ARG_1, reg_index); - break; - #elif N_RV32 - asm_rv32_opcode_slli(emit->as, REG_TEMP2, reg_index, 2); - asm_rv32_opcode_cadd(emit->as, REG_ARG_1, REG_TEMP2); - asm_rv32_opcode_sw(emit->as, reg_value, REG_ARG_1, 0); - break; - #elif N_XTENSA || N_XTENSAWIN - asm_xtensa_op_addx4(emit->as, REG_ARG_1, reg_index, REG_ARG_1); - asm_xtensa_op_s32i_n(emit->as, reg_value, REG_ARG_1, 0); - break; - #endif + #ifdef ASM_STORE32_REG_REG_REG + ASM_STORE32_REG_REG_REG(emit->as, reg_value, REG_ARG_1, reg_index); + #else ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_ADD_REG_REG(emit->as, REG_ARG_1, reg_index); // add index to base ASM_STORE32_REG_REG(emit->as, reg_value, REG_ARG_1); // store value to (base+4*index) + #endif break; } default: @@ -2328,8 +2264,7 @@ static void emit_native_with_cleanup(emit_t *emit, mp_uint_t label) { // Replace exception with MP_OBJ_NULL. emit_native_label_assign(emit, *emit->label_slot); - ASM_MOV_REG_IMM(emit->as, REG_TEMP0, (mp_uint_t)MP_OBJ_NULL); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); + ASM_MOV_LOCAL_MP_OBJ_NULL(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); // end of with cleanup nlr_catch block emit_native_label_assign(emit, *emit->label_slot + 1); @@ -2436,8 +2371,7 @@ static void emit_native_for_iter_end(emit_t *emit) { static void emit_native_pop_except_jump(emit_t *emit, mp_uint_t label, bool within_exc_handler) { if (within_exc_handler) { // Cancel any active exception so subsequent handlers don't see it - ASM_MOV_REG_IMM(emit->as, REG_TEMP0, (mp_uint_t)MP_OBJ_NULL); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); + ASM_MOV_LOCAL_MP_OBJ_NULL(emit->as, LOCAL_IDX_EXC_VAL(emit), REG_TEMP0); } else { emit_native_leave_exc_stack(emit, false); } @@ -3122,8 +3056,7 @@ static void emit_native_yield(emit_t *emit, int kind) { if (kind == MP_EMIT_YIELD_VALUE) { // Check LOCAL_IDX_THROW_VAL for any injected value ASM_MOV_REG_LOCAL(emit->as, REG_ARG_1, LOCAL_IDX_THROW_VAL(emit)); - ASM_MOV_REG_IMM(emit->as, REG_ARG_2, (mp_uint_t)MP_OBJ_NULL); - ASM_MOV_LOCAL_REG(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); + ASM_MOV_LOCAL_MP_OBJ_NULL(emit->as, LOCAL_IDX_THROW_VAL(emit), REG_ARG_2); emit_call(emit, MP_F_NATIVE_RAISE); } else { // Label loop entry diff --git a/py/emitndebug.c b/py/emitndebug.c index bd896a75c8ddf..e49c5cdbffa7b 100644 --- a/py/emitndebug.c +++ b/py/emitndebug.c @@ -108,8 +108,8 @@ static void asm_debug_end_pass(asm_debug_t *as) { (void)as; } -static void asm_debug_entry(asm_debug_t *as, int num_locals) { - asm_debug_printf(as, "ENTRY(num_locals=%d)\n", num_locals); +static void asm_debug_entry(asm_debug_t *as, int num_locals, char *name) { + asm_debug_printf(as, "ENTRY(%s, num_locals=%d)\n", name != NULL ? name : "?", num_locals); } static void asm_debug_exit(asm_debug_t *as) { @@ -195,8 +195,8 @@ static void asm_debug_setcc_reg_reg_reg(asm_debug_t *as, int op, int reg1, int r #define ASM_T asm_debug_t #define ASM_END_PASS asm_debug_end_pass -#define ASM_ENTRY(as, num_locals) \ - asm_debug_entry(as, num_locals) +#define ASM_ENTRY(as, num_locals, name) \ + asm_debug_entry(as, num_locals, name) #define ASM_EXIT(as) \ asm_debug_exit(as) @@ -251,8 +251,6 @@ static void asm_debug_setcc_reg_reg_reg(asm_debug_t *as, int op, int reg1, int r #define ASM_MUL_REG_REG(as, reg_dest, reg_src) \ asm_debug_reg_reg(as, "mul", reg_dest, reg_src) -#define ASM_LOAD_REG_REG(as, reg_dest, reg_base) \ - asm_debug_reg_reg(as, "load", reg_dest, reg_base) #define ASM_LOAD_REG_REG_OFFSET(as, reg_dest, reg_base, word_offset) \ asm_debug_reg_reg_offset(as, "load", reg_dest, reg_base, word_offset) #define ASM_LOAD8_REG_REG(as, reg_dest, reg_base) \ @@ -264,8 +262,6 @@ static void asm_debug_setcc_reg_reg_reg(asm_debug_t *as, int op, int reg1, int r #define ASM_LOAD32_REG_REG(as, reg_dest, reg_base) \ asm_debug_reg_reg(as, "load32", reg_dest, reg_base) -#define ASM_STORE_REG_REG(as, reg_src, reg_base) \ - asm_debug_reg_reg(as, "store", reg_src, reg_base) #define ASM_STORE_REG_REG_OFFSET(as, reg_src, reg_base, word_offset) \ asm_debug_reg_reg_offset(as, "store", reg_src, reg_base, word_offset) #define ASM_STORE8_REG_REG(as, reg_src, reg_base) \ diff --git a/py/formatfloat.c b/py/formatfloat.c index b4348122ff428..1ea34f84bf7fb 100644 --- a/py/formatfloat.c +++ b/py/formatfloat.c @@ -33,394 +33,537 @@ #include #include #include "py/formatfloat.h" +#include "py/parsenum.h" /*********************************************************************** Routine for converting a arbitrary floating point number into a string. - The code in this function was inspired from Fred Bayer's pdouble.c. - Since pdouble.c was released as Public Domain, I'm releasing this - code as public domain as well. + The code in this function was inspired from Dave Hylands's previous + version, which was itself inspired from Fred Bayer's pdouble.c. The original code can be found in https://github.com/dhylands/format-float - Dave Hylands - ***********************************************************************/ -#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT -// 1 sign bit, 8 exponent bits, and 23 mantissa bits. -// exponent values 0 and 255 are reserved, exponent can be 1 to 254. -// exponent is stored with a bias of 127. -// The min and max floats are on the order of 1x10^37 and 1x10^-37 - -#define FPTYPE float -#define FPCONST(x) x##F -#define FPROUND_TO_ONE 0.9999995F -#define FPDECEXP 32 -#define FPMIN_BUF_SIZE 6 // +9e+99 +// Float formatting debug code is intended for use in ports/unix only, +// as it uses the libc float printing function as a reference. +#define DEBUG_FLOAT_FORMATTING 0 + +#if DEBUG_FLOAT_FORMATTING +#define DEBUG_PRINTF(...) fprintf(stderr, __VA_ARGS__) +#else +#define DEBUG_PRINTF(...) +#endif + +#if MICROPY_FLOAT_FORMAT_IMPL == MICROPY_FLOAT_FORMAT_IMPL_EXACT || MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define MP_FFUINT_FMT "%lu" +#else +#define MP_FFUINT_FMT "%u" +#endif + +static inline int fp_expval(mp_float_t x) { + mp_float_union_t fb = { x }; + return (int)fb.p.exp - MP_FLOAT_EXP_OFFSET; +} -#define FLT_SIGN_MASK 0x80000000 +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE -static inline int fp_signbit(float x) { - mp_float_union_t fb = {x}; - return fb.i & FLT_SIGN_MASK; +static inline int fp_isless1(mp_float_t x) { + return x < 1.0; } -#define fp_isnan(x) isnan(x) -#define fp_isinf(x) isinf(x) -static inline int fp_iszero(float x) { - mp_float_union_t fb = {x}; - return fb.i == 0; + +static inline int fp_iszero(mp_float_t x) { + return x == 0.0; } -static inline int fp_isless1(float x) { - mp_float_union_t fb = {x}; + +#if MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_APPROX +static inline int fp_equal(mp_float_t x, mp_float_t y) { + return x == y; +} +#else +static inline mp_float_t fp_diff(mp_float_t x, mp_float_t y) { + return x - y; +} +#endif + +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT + +// The functions below are roughly equivalent to the ones above, +// but they are optimized to reduce code footprint by skipping +// handling for special values such as nan, inf, +/-0.0 +// for ports where FP support is done in software. +// +// They also take into account lost bits of REPR_C as needed. + +static inline int fp_isless1(mp_float_t x) { + mp_float_union_t fb = { x }; return fb.i < 0x3f800000; } -#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +static inline int fp_iszero(mp_float_t x) { + mp_float_union_t x_check = { x }; + return !x_check.i; // this is valid for REPR_C as well +} -// CIRCUITPY-CHANGE: prevent warnings -#pragma GCC diagnostic ignored "-Wfloat-equal" -#define FPTYPE double -#define FPCONST(x) x -#define FPROUND_TO_ONE 0.999999999995 -#define FPDECEXP 256 -#define FPMIN_BUF_SIZE 7 // +9e+199 -#define fp_signbit(x) signbit(x) -#define fp_isnan(x) isnan(x) -#define fp_isinf(x) isinf(x) -#define fp_iszero(x) (x == 0) -#define fp_isless1(x) (x < 1.0) +#if MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_APPROX +static inline int fp_equal(mp_float_t x, mp_float_t y) { + mp_float_union_t x_check = { x }; + mp_float_union_t y_check = { y }; + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + return (x_check.i & ~3) == (y_check.i & ~3); + #else + return x_check.i == y_check.i; + #endif +} +#else +static inline mp_float_t fp_diff(mp_float_t x, mp_float_t y) { + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C + mp_float_union_t x_check = { x }; + mp_float_union_t y_check = { y }; + x_check.i &= ~3; + y_check.i &= ~3; + return x_check.f - y_check.f; + #else + return x - y; + #endif +} +#endif -#endif // MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT/DOUBLE +#endif -static inline int fp_expval(FPTYPE x) { - mp_float_union_t fb = {x}; - return (int)((fb.i >> MP_FLOAT_FRAC_BITS) & (~(0xFFFFFFFF << MP_FLOAT_EXP_BITS))) - MP_FLOAT_EXP_OFFSET; +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define FPMIN_BUF_SIZE 6 // +9e+99 +#define MAX_MANTISSA_DIGITS (9) +#define SAFE_MANTISSA_DIGITS (6) +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#define FPMIN_BUF_SIZE 7 // +9e+199 +#define MAX_MANTISSA_DIGITS (19) +#define SAFE_MANTISSA_DIGITS (16) +#endif + +// Internal formatting flags +#define FMT_MODE_E 0x01 // render using scientific notation (%e) +#define FMT_MODE_G 0x02 // render using general format (%g) +#define FMT_MODE_F 0x04 // render using using expanded fixed-point format (%f) +#define FMT_E_CASE 0x20 // don't change this value (used for case conversion!) + +static char *mp_prepend_zeros(char *s, int cnt) { + *s++ = '0'; + *s++ = '.'; + while (cnt > 0) { + *s++ = '0'; + cnt--; + } + return s; } -int mp_format_float(FPTYPE f, char *buf, size_t buf_size, char fmt, int prec, char sign) { +// Helper to convert a decimal mantissa (provided as an mp_large_float_uint_t) to string +static int mp_format_mantissa(mp_large_float_uint_t mantissa, mp_large_float_uint_t mantissa_cap, char *buf, char *s, + int num_digits, int max_exp_zeros, int trailing_zeros, int dec, int e, int fmt_flags) { - char *s = buf; + DEBUG_PRINTF("mantissa=" MP_FFUINT_FMT " exp=%d (cap=" MP_FFUINT_FMT "):\n", mantissa, e, mantissa_cap); - if (buf_size <= FPMIN_BUF_SIZE) { - // FPMIN_BUF_SIZE is the minimum size needed to store any FP number. - // If the buffer does not have enough room for this (plus null terminator) - // then don't try to format the float. + if (mantissa) { + // If rounding/searching created an extra digit or removed too many, fix mantissa first + if (mantissa >= mantissa_cap) { + if (fmt_flags & FMT_MODE_F) { + assert(e >= 0); + num_digits++; + dec++; + } else { + mantissa /= 10; + e++; + } + } + } - if (buf_size >= 2) { - *s++ = '?'; + // When 'g' format is used, replace small exponents by explicit zeros + if ((fmt_flags & FMT_MODE_G) && e != 0) { + if (e >= 0) { + // If 0 < e < max_exp_zeros, expand positive exponent into trailing zeros + if (e < max_exp_zeros) { + dec += e; + if (dec >= num_digits) { + trailing_zeros = dec - (num_digits - 1); + } + e = 0; + } + } else { + // If -4 <= e < 0, expand negative exponent without losing significant digits + if (e >= -4) { + int cnt = 0; + while (e < 0 && !(mantissa % 10)) { + mantissa /= 10; + cnt++; + e++; + } + num_digits -= cnt; + s = mp_prepend_zeros(s, cnt - e - 1); + dec = 255; + e = 0; + } } - if (buf_size >= 1) { - *s = '\0'; + } + + // Convert the integer mantissa to string + for (int digit = num_digits - 1; digit >= 0; digit--) { + int digit_ofs = (digit > dec ? digit + 1 : digit); + s[digit_ofs] = '0' + (int)(mantissa % 10); + mantissa /= 10; + } + int dot = (dec >= 255); + if (dec + 1 < num_digits) { + dot = 1; + s++; + s[dec] = '.'; + } + s += num_digits; + #if DEBUG_FLOAT_FORMATTING + *s = 0; + DEBUG_PRINTF(" = %s exp=%d num_digits=%d zeros=%d dec=%d\n", buf, e, num_digits, trailing_zeros, dec); + #endif + + // Append or remove trailing zeros, as required by format + if (trailing_zeros) { + dec -= num_digits - 1; + while (trailing_zeros--) { + if (!dec--) { + *s++ = '.'; + dot = 1; + } + *s++ = '0'; } - return buf_size >= 2; } - if (fp_signbit(f) && !fp_isnan(f)) { - *s++ = '-'; - f = -f; - } else { - if (sign) { - *s++ = sign; + if (fmt_flags & FMT_MODE_G) { + // 'g' format requires to remove trailing zeros after decimal point + if (dot) { + while (s[-1] == '0') { + s--; + } + if (s[-1] == '.') { + s--; + } + } + } + + // Append the exponent if needed + if (((e != 0) || (fmt_flags & FMT_MODE_E)) && !(fmt_flags & FMT_MODE_F)) { + *s++ = 'E' | (fmt_flags & FMT_E_CASE); + if (e >= 0) { + *s++ = '+'; + } else { + *s++ = '-'; + e = -e; } + if (e >= 100) { + *s++ = '0' + (e / 100); + } + *s++ = '0' + ((e / 10) % 10); + *s++ = '0' + (e % 10); } + *s = '\0'; + DEBUG_PRINTF(" ===> %s\n", buf); - // buf_remaining contains bytes available for digits and exponent. - // It is buf_size minus room for the sign and null byte. - int buf_remaining = buf_size - 1 - (s - buf); + return s - buf; +} +// minimal value expected for buf_size, to avoid checking everywhere for overflow +#define MIN_BUF_SIZE (MAX_MANTISSA_DIGITS + 10) + +int mp_format_float(mp_float_t f_entry, char *buf_entry, size_t buf_size, char fmt, int prec, char sign) { + assert(buf_size >= MIN_BUF_SIZE); + + // Handle sign + mp_float_t f = f_entry; + char *buf = buf_entry; + if (signbit(f_entry) && !isnan(f_entry)) { + f = -f; + sign = '-'; + } + if (sign) { + *buf++ = sign; + buf_size--; + } + + // Handle inf/nan + char uc = fmt & 0x20; { - char uc = fmt & 0x20; - if (fp_isinf(f)) { + char *s = buf; + if (isinf(f)) { *s++ = 'I' ^ uc; *s++ = 'N' ^ uc; *s++ = 'F' ^ uc; goto ret; - } else if (fp_isnan(f)) { + } else if (isnan(f)) { *s++ = 'N' ^ uc; *s++ = 'A' ^ uc; *s++ = 'N' ^ uc; ret: *s = '\0'; - return s - buf; + return s - buf_entry; } } + // Decode format character + int fmt_flags = (unsigned char)uc; // setup FMT_E_CASE, clear all other bits + char lofmt = (char)(fmt | 0x20); // fmt in lowercase + if (lofmt == 'f') { + fmt_flags |= FMT_MODE_F; + } else if (lofmt == 'g') { + fmt_flags |= FMT_MODE_G; + } else { + fmt_flags |= FMT_MODE_E; + } + + // When precision is unspecified, default to 6 if (prec < 0) { prec = 6; } - char e_char = 'E' | (fmt & 0x20); // e_char will match case of fmt - fmt |= 0x20; // Force fmt to be lowercase - char org_fmt = fmt; - if (fmt == 'g' && prec == 0) { - prec = 1; + // Use high precision for `repr`, but switch to exponent mode + // after 16 digits in any case to match CPython behaviour + int max_exp_zeros = (prec < (int)buf_size - 3 ? prec : (int)buf_size - 3); + if (prec == MP_FLOAT_REPR_PREC) { + prec = MAX_MANTISSA_DIGITS; + max_exp_zeros = 16; } - int e; - int dec = 0; - char e_sign = '\0'; - int num_digits = 0; - int signed_e = 0; - // Approximate power of 10 exponent from binary exponent. - // abs(e_guess) is lower bound on abs(power of 10 exponent). - int e_guess = (int)(fp_expval(f) * FPCONST(0.3010299956639812)); // 1/log2(10). - if (fp_iszero(f)) { - e = 0; - if (fmt == 'f') { - // Truncate precision to prevent buffer overflow - if (prec + 2 > buf_remaining) { - prec = buf_remaining - 2; - } - num_digits = prec + 1; - } else { - // Truncate precision to prevent buffer overflow - if (prec + 6 > buf_remaining) { - prec = buf_remaining - 6; - } - if (fmt == 'e') { - e_sign = '+'; - } + // Precompute the exact decimal exponent of f, such that + // abs(e) is lower bound on abs(power of 10 exponent). + int e = 0; + if (!fp_iszero(f)) { + // Approximate power of 10 exponent from binary exponent. + e = (int)(fp_expval(f) * MICROPY_FLOAT_CONST(0.3010299956639812)); // 1/log2(10). + int positive_exp = !fp_isless1(f); + mp_float_t u_base = (mp_float_t)mp_decimal_exp((mp_large_float_t)1.0, e + positive_exp); + while ((f >= u_base) == positive_exp) { + e += (positive_exp ? 1 : -1); + u_base = (mp_float_t)mp_decimal_exp((mp_large_float_t)1.0, e + positive_exp); } - } else if (fp_isless1(f)) { - FPTYPE f_entry = f; // Save f in case we go to 'f' format. - // Build negative exponent - e = -e_guess; - FPTYPE u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e); - while (u_base > f) { - ++e; - u_base = MICROPY_FLOAT_C_FUN(pow)(10, -e); - } - // Normalize out the inferred unit. Use divide because - // pow(10, e) * pow(10, -e) is slightly < 1 for some e in float32 - // (e.g. print("%.12f" % ((1e13) * (1e-13)))) - f /= u_base; - - // If the user specified 'g' format, and e is <= 4, then we'll switch - // to the fixed format ('f') - - if (fmt == 'f' || (fmt == 'g' && e <= 4)) { - fmt = 'f'; - dec = 0; + } - if (org_fmt == 'g') { - prec += (e - 1); - } + // For 'e' format, prec is # digits after the decimal + // For 'f' format, prec is # digits after the decimal + // For 'g' format, prec is the max number of significant digits + // + // For 'e' & 'g' format, there will be a single digit before the decimal + // For 'f' format, zeros must be expanded instead of using an exponent. + // Make sure there is enough room in the buffer for them, or switch to format 'g'. + if ((fmt_flags & FMT_MODE_F) && e > 0) { + int req_size = e + prec + 2; + if (req_size > (int)buf_size) { + fmt_flags ^= FMT_MODE_F; + fmt_flags |= FMT_MODE_G; + prec++; + } + } - // truncate precision to prevent buffer overflow - if (prec + 2 > buf_remaining) { - prec = buf_remaining - 2; + // To work independently of the format, we precompute: + // - the max number of significant digits to produce + // - the number of leading zeros to prepend (mode f only) + // - the number of trailing zeros to append + int max_digits = prec; + int lead_zeros = 0; + int trail_zeros = 0; + if (fmt_flags & FMT_MODE_F) { + if (max_digits > (int)buf_size - 3) { + // cannot satisfy requested number of decimals given buf_size, sorry + max_digits = (int)buf_size - 3; + } + if (e < 0) { + if (max_digits > 2 && e < -2) { + // Insert explicit leading zeros + lead_zeros = (-e < max_digits ? -e : max_digits) - 2; + max_digits -= lead_zeros; + } else { + max_digits++; } - - num_digits = prec; - signed_e = 0; - f = f_entry; - ++num_digits; } else { - // For e & g formats, we'll be printing the exponent, so set the - // sign. - e_sign = '-'; - dec = 0; - - if (prec > (buf_remaining - FPMIN_BUF_SIZE)) { - prec = buf_remaining - FPMIN_BUF_SIZE; - if (fmt == 'g') { - prec++; - } - } - signed_e = -e; + max_digits += e + 1; } } else { - // Build positive exponent. - // We don't modify f at this point to avoid inaccuracies from - // scaling it. Instead, we find the product of powers of 10 - // that is not greater than it, and use that to start the - // mantissa. - e = e_guess; - FPTYPE next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1); - while (f >= next_u) { - ++e; - next_u = MICROPY_FLOAT_C_FUN(pow)(10, e + 1); + if (!(fmt_flags & FMT_MODE_G) || max_digits == 0) { + max_digits++; } + } + if (max_digits > MAX_MANTISSA_DIGITS) { + // use trailing zeros to avoid overflowing the mantissa + trail_zeros = max_digits - MAX_MANTISSA_DIGITS; + max_digits = MAX_MANTISSA_DIGITS; + } + int overhead = (fmt_flags & FMT_MODE_F ? 3 : FPMIN_BUF_SIZE + 1); + if (trail_zeros > (int)buf_size - max_digits - overhead) { + // cannot satisfy requested number of decimals given buf_size, sorry + trail_zeros = (int)buf_size - max_digits - overhead; + } - // If the user specified fixed format (fmt == 'f') and e makes the - // number too big to fit into the available buffer, then we'll - // switch to the 'e' format. - - if (fmt == 'f') { - if (e >= buf_remaining) { - fmt = 'e'; - } else if ((e + prec + 2) > buf_remaining) { - prec = buf_remaining - e - 2; - if (prec < 0) { - // This means no decimal point, so we can add one back - // for the decimal. - prec++; - } - } - } - if (fmt == 'e' && prec > (buf_remaining - FPMIN_BUF_SIZE)) { - prec = buf_remaining - FPMIN_BUF_SIZE; - } - if (fmt == 'g') { - // Truncate precision to prevent buffer overflow - if (prec + (FPMIN_BUF_SIZE - 1) > buf_remaining) { - prec = buf_remaining - (FPMIN_BUF_SIZE - 1); - } - } - // If the user specified 'g' format, and e is < prec, then we'll switch - // to the fixed format. + // When the caller asks for more precision than available for sure, + // Look for a shorter (rounded) representation first, and only dig + // into more digits if there is no short representation. + int num_digits = (SAFE_MANTISSA_DIGITS < max_digits ? SAFE_MANTISSA_DIGITS : max_digits); +try_again: + ; - if (fmt == 'g' && e < prec) { - fmt = 'f'; - prec -= (e + 1); - } - if (fmt == 'f') { - dec = e; - num_digits = prec + e + 1; + char *s = buf; + int extra_zeros = trail_zeros + (max_digits - num_digits); + int decexp; + int dec = 0; + + if (fp_iszero(f)) { + // no need for scaling 0.0 + decexp = 0; + } else if (fmt_flags & FMT_MODE_F) { + decexp = num_digits - 1; + if (e < 0) { + // Negative exponent: we keep a single leading zero in the mantissa, + // as using more would waste precious digits needed for accuracy. + if (lead_zeros > 0) { + // We are using leading zeros + s = mp_prepend_zeros(s, lead_zeros); + decexp += lead_zeros + 1; + dec = 255; // no decimal dot + } else { + // Small negative exponent, work directly on the mantissa + dec = 0; + } } else { - e_sign = '+'; + // Positive exponent: we will add trailing zeros separately + decexp -= e; + dec = e; } - signed_e = e; + } else { + decexp = num_digits - e - 1; } - if (prec < 0) { - // This can happen when the prec is trimmed to prevent buffer overflow - prec = 0; + DEBUG_PRINTF("input=%.19g e=%d fmt=%c max_d=%d num_d=%d decexp=%d dec=%d l0=%d r0=%d\n", + (double)f, e, lofmt, max_digits, num_digits, decexp, dec, lead_zeros, extra_zeros); + + // At this point, + // - buf points to beginning of output buffer for the unsigned representation + // - num_digits == the number of mantissa digits to add + // - (dec + 1) == the number of digits to print before adding a decimal point + // - decexp == the power of 10 exponent to apply to f to get the decimal mantissa + // - e == the power of 10 exponent to append ('e' or 'g' format) + mp_large_float_uint_t mantissa_cap = 10; + for (int n = 1; n < num_digits; n++) { + mantissa_cap *= 10; } - // At this point e contains the absolute value of the power of 10 exponent. - // (dec + 1) == the number of dgits before the decimal. - - // For e, prec is # digits after the decimal - // For f, prec is # digits after the decimal - // For g, prec is the max number of significant digits - // - // For e & g there will be a single digit before the decimal - // for f there will be e digits before the decimal - - if (fmt == 'e') { - num_digits = prec + 1; - } else if (fmt == 'g') { - if (prec == 0) { - prec = 1; + // Build the decimal mantissa into a large uint + mp_large_float_uint_t mantissa = 1; + if (sizeof(mp_large_float_t) == sizeof(mp_float_t) && num_digits > SAFE_MANTISSA_DIGITS && decexp > 1) { + // if we don't have large floats, use integer multiply to produce the last digits + if (num_digits > SAFE_MANTISSA_DIGITS + 1 && decexp > 2) { + mantissa = 100; + decexp -= 2; + } else { + mantissa = 10; + decexp -= 1; } - num_digits = prec; } - - int d = 0; - for (int digit_index = signed_e; num_digits >= 0; --digit_index) { - FPTYPE u_base = FPCONST(1.0); - if (digit_index > 0) { - // Generate 10^digit_index for positive digit_index. - u_base = MICROPY_FLOAT_C_FUN(pow)(10, digit_index); - } - for (d = 0; d < 9; ++d) { - if (f < u_base) { - break; - } - f -= u_base; - } - // We calculate one more digit than we display, to use in rounding - // below. So only emit the digit if it's one that we display. - if (num_digits > 0) { - // Emit this number (the leading digit). - *s++ = '0' + d; - if (dec == 0 && prec > 0) { - *s++ = '.'; - } - } - --dec; - --num_digits; - if (digit_index <= 0) { - // Once we get below 1.0, we scale up f instead of calculating - // negative powers of 10 in u_base. This provides better - // renditions of exact decimals like 1/16 etc. - f *= FPCONST(10.0); + mp_large_float_t mantissa_f = mp_decimal_exp((mp_large_float_t)f, decexp); + mantissa *= (mp_large_float_uint_t)(mantissa_f + (mp_large_float_t)0.5); + DEBUG_PRINTF("input=%.19g fmt=%c num_digits=%d dec=%d mantissa=" MP_FFUINT_FMT " r0=%d\n", (double)f, lofmt, num_digits, dec, mantissa, extra_zeros); + + // Finally convert the decimal mantissa to a floating-point string, according to formatting rules + int reprlen = mp_format_mantissa(mantissa, mantissa_cap, buf, s, num_digits, max_exp_zeros, extra_zeros, dec, e, fmt_flags); + assert(reprlen + 1 <= (int)buf_size); + + #if MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_APPROX + + if (num_digits < max_digits) { + // The initial precision might not be sufficient for an exact representation + // for all numbers. If the result is not exact, restart using next precision. + // parse the resulting number and compare against the original + mp_float_t check; + DEBUG_PRINTF("input=%.19g, compare to float('%s')\n", (double)f, buf); + mp_parse_float_internal(buf, reprlen, &check); + if (!fp_equal(check, f)) { + num_digits++; + DEBUG_PRINTF("Not perfect, retry using more digits (%d)\n", num_digits); + goto try_again; } } - // Rounding. If the next digit to print is >= 5, round up. - if (d >= 5) { - char *rs = s; - rs--; - while (1) { - if (*rs == '.') { - rs--; - continue; - } - if (*rs < '0' || *rs > '9') { - // + or - - rs++; // So we sit on the digit to the right of the sign - break; + + #else + + // The initial decimal mantissa might not have been be completely accurate due + // to the previous loating point operations. The best way to verify this is to + // parse the resulting number and compare against the original + mp_float_t check; + DEBUG_PRINTF("input=%.19g, compare to float('%s')\n", (double)f, buf); + mp_parse_float_internal(buf, reprlen, &check); + mp_float_t diff = fp_diff(check, f); + mp_float_t best_diff = diff; + mp_large_float_uint_t best_mantissa = mantissa; + + if (fp_iszero(diff)) { + // we have a perfect match + DEBUG_PRINTF(MP_FFUINT_FMT ": perfect match (direct)\n", mantissa); + } else { + // In order to get the best possible representation, we will perform a + // dichotomic search for a reversible representation. + // This will also provide optimal rounding on the fly. + unsigned err_range = 1; + if (num_digits > SAFE_MANTISSA_DIGITS) { + err_range <<= 3 * (num_digits - SAFE_MANTISSA_DIGITS); + } + int maxruns = 3 + 3 * (MAX_MANTISSA_DIGITS - SAFE_MANTISSA_DIGITS); + while (maxruns-- > 0) { + // update mantissa according to dichotomic search + if (signbit(diff)) { + mantissa += err_range; + } else { + // mantissa is expected to always have more significant digits than err_range + assert(mantissa >= err_range); + mantissa -= err_range; } - if (*rs < '9') { - (*rs)++; + // retry conversion + reprlen = mp_format_mantissa(mantissa, mantissa_cap, buf, s, num_digits, max_exp_zeros, extra_zeros, dec, e, fmt_flags); + assert(reprlen + 1 <= (int)buf_size); + DEBUG_PRINTF("input=%.19g, compare to float('%s')\n", (double)f, buf); + mp_parse_float_internal(buf, reprlen, &check); + DEBUG_PRINTF("check=%.19g num_digits=%d e=%d mantissa=" MP_FFUINT_FMT "\n", (double)check, num_digits, e, mantissa); + diff = fp_diff(check, f); + if (fp_iszero(diff)) { + // we have a perfect match + DEBUG_PRINTF(MP_FFUINT_FMT ": perfect match\n", mantissa); break; } - *rs = '0'; - if (rs == buf) { - break; + // keep track of our best estimate + mp_float_t delta = MICROPY_FLOAT_C_FUN(fabs)(diff) - MICROPY_FLOAT_C_FUN(fabs)(best_diff); + if (signbit(delta) || (fp_iszero(delta) && !(mantissa % 10u))) { + best_diff = diff; + best_mantissa = mantissa; } - rs--; - } - if (*rs == '0') { - // We need to insert a 1 - if (rs[1] == '.' && fmt != 'f') { - // We're going to round 9.99 to 10.00 - // Move the decimal point - rs[0] = '.'; - rs[1] = '0'; - if (e_sign == '-') { - e--; - if (e == 0) { - e_sign = '+'; - } - } else { - e++; - } + // string repr is not perfect: continue a dichotomic improvement + DEBUG_PRINTF(MP_FFUINT_FMT ": %.19g, err_range=%d\n", mantissa, (double)check, err_range); + if (err_range > 1) { + err_range >>= 1; } else { - // Need at extra digit at the end to make room for the leading '1' - // but if we're at the buffer size limit, just drop the final digit. - if ((size_t)(s + 1 - buf) < buf_size) { - s++; + // We have tried all possible mantissa, without finding a reversible repr. + // Check if we have an alternate precision to try. + if (num_digits < max_digits) { + num_digits++; + DEBUG_PRINTF("Failed to find a perfect match, try with more digits (%d)\n", num_digits); + goto try_again; } + // Otherwise, keep the closest one, which is either the first one or the last one. + if (mantissa == best_mantissa) { + // Last guess is the best one + DEBUG_PRINTF(MP_FFUINT_FMT ": last guess was the best one\n", mantissa); + } else { + // We had a better guess earlier + DEBUG_PRINTF(MP_FFUINT_FMT ": use best guess\n", mantissa); + reprlen = mp_format_mantissa(best_mantissa, mantissa_cap, buf, s, num_digits, max_exp_zeros, extra_zeros, dec, e, fmt_flags); + } + break; } - char *ss = s; - while (ss > rs) { - *ss = ss[-1]; - ss--; - } - *rs = '1'; } } + #endif - // verify that we did not overrun the input buffer so far - assert((size_t)(s + 1 - buf) <= buf_size); - - if (org_fmt == 'g' && prec > 0) { - // Remove trailing zeros and a trailing decimal point - while (s[-1] == '0') { - s--; - } - if (s[-1] == '.') { - s--; - } - } - // Append the exponent - if (e_sign) { - *s++ = e_char; - *s++ = e_sign; - if (FPMIN_BUF_SIZE == 7 && e >= 100) { - *s++ = '0' + (e / 100); - } - *s++ = '0' + ((e / 10) % 10); - *s++ = '0' + (e % 10); - } - *s = '\0'; - - // verify that we did not overrun the input buffer - assert((size_t)(s + 1 - buf) <= buf_size); - - return s - buf; + return buf + reprlen - buf_entry; } #endif // MICROPY_FLOAT_IMPL != MICROPY_FLOAT_IMPL_NONE diff --git a/py/formatfloat.h b/py/formatfloat.h index 9a1643b4ddff5..7b1414672b77b 100644 --- a/py/formatfloat.h +++ b/py/formatfloat.h @@ -29,6 +29,7 @@ #include "py/mpconfig.h" #if MICROPY_PY_BUILTINS_FLOAT +#define MP_FLOAT_REPR_PREC (99) // magic `prec` value for optimal `repr` behaviour int mp_format_float(mp_float_t f, char *buf, size_t bufSize, char fmt, int prec, char sign); #endif diff --git a/py/gc.c b/py/gc.c index 338803dc245cc..757c27e3de696 100644 --- a/py/gc.c +++ b/py/gc.c @@ -32,6 +32,12 @@ #include "py/gc.h" #include "py/runtime.h" +#if defined(__ZEPHYR__) +#include +#include +#include +#endif + #if MICROPY_DEBUG_VALGRIND #include #endif @@ -45,6 +51,12 @@ #include "shared-module/memorymonitor/__init__.h" #endif +#if defined(__ZEPHYR__) && defined(CONFIG_TRACING_PERFETTO) && defined(CONFIG_BOARD_NATIVE_SIM) +#include "perfetto_encoder.h" +#define CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID 0x3001ULL +#define CIRCUITPY_PERFETTO_VM_HEAP_MAX_FREE_UUID 0x3002ULL +#endif + #if MICROPY_ENABLE_GC #if MICROPY_DEBUG_VERBOSE // print debugging info @@ -158,6 +170,32 @@ void __attribute__ ((noinline)) gc_log_change(uint32_t start_block, uint32_t len #pragma GCC pop_options #endif +#if defined(__ZEPHYR__) && defined(CONFIG_TRACING_PERFETTO) && defined(CONFIG_BOARD_NATIVE_SIM) +static void gc_perfetto_emit_heap_stats(void) { + if (!perfetto_start()) { + return; + } + gc_info_t info; + gc_info(&info); + perfetto_emit_counter(CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID, (int64_t)info.used); + Z_SPIN_DELAY(1); +} + +static void gc_perfetto_emit_heap_stopped(void) { + if (!perfetto_start()) { + return; + } + perfetto_emit_counter(CIRCUITPY_PERFETTO_VM_HEAP_USED_UUID, 0); + Z_SPIN_DELAY(1); +} +#else +static inline void gc_perfetto_emit_heap_stats(void) { +} + +static inline void gc_perfetto_emit_heap_stopped(void) { +} +#endif + // Static functions for individual steps of the GC mark/sweep sequence static void gc_collect_start_common(void); static void *gc_get_ptr(void **ptrs, int i); @@ -284,6 +322,7 @@ void gc_init(void *start, void *end) { #endif GC_MUTEX_INIT(); + gc_perfetto_emit_heap_stats(); } #if MICROPY_GC_SPLIT_HEAP @@ -311,8 +350,9 @@ void gc_add(void *start, void *end) { #if MICROPY_GC_SPLIT_HEAP_AUTO // CIRCUITPY-CHANGE: Added function to compute heap size with selective collect table static size_t compute_heap_size(size_t total_blocks) { - // Add two blocks to account for allocation alignment. - total_blocks += 2; + // Round up to the nearest multiple of BLOCKS_PER_ATB. Partial ATB blocks aren't supported and + // will result in a heap that is too small. + total_blocks = ((total_blocks + BLOCKS_PER_ATB - 1) / BLOCKS_PER_ATB) * BLOCKS_PER_ATB; size_t atb_bytes = (total_blocks + BLOCKS_PER_ATB - 1) / BLOCKS_PER_ATB; size_t ftb_bytes = 0; size_t ctb_bytes = 0; @@ -326,12 +366,13 @@ static size_t compute_heap_size(size_t total_blocks) { // Compute bytes needed to build a heap with total_blocks blocks. size_t total_heap = - atb_bytes + sizeof(mp_state_mem_area_t) + + atb_bytes + + ALLOC_TABLE_GAP_BYTE + ftb_bytes + ctb_bytes + pool_bytes - + ALLOC_TABLE_GAP_BYTE - + sizeof(mp_state_mem_area_t); + + BYTES_PER_BLOCK; // Extra block of bytes to account for end pointer alignment // Round up size to the nearest multiple of BYTES_PER_BLOCK. total_heap = (total_heap + BYTES_PER_BLOCK - 1) / BYTES_PER_BLOCK; @@ -423,6 +464,7 @@ void gc_deinit(void) { // Run any finalisers before we stop using the heap. This will also free // any additional heap areas (but not the first.) gc_sweep_all(); + gc_perfetto_emit_heap_stopped(); memset(&MP_STATE_MEM(area), 0, sizeof(MP_STATE_MEM(area))); } @@ -444,7 +486,7 @@ bool gc_is_locked(void) { } // CIRCUITPY-CHANGE: additional function -bool gc_ptr_on_heap(void *ptr) { +bool gc_ptr_on_heap(const void *ptr) { for (mp_state_mem_area_t *area = &MP_STATE_MEM(area); area != NULL; area = NEXT_AREA(area)) { if (ptr >= (void *)area->gc_pool_start // must be above start of pool && ptr < (void *)area->gc_pool_end) { // must be below end of pool @@ -652,6 +694,7 @@ void gc_collect_end(void) { } MP_STATE_THREAD(gc_lock_depth) &= ~GC_COLLECT_FLAG; GC_EXIT(); + gc_perfetto_emit_heap_stats(); } static void gc_deal_with_stack_overflow(void) { @@ -1067,6 +1110,8 @@ void *gc_alloc(size_t n_bytes, unsigned int alloc_flags) { memorymonitor_track_allocation(end_block - start_block + 1); #endif + gc_perfetto_emit_heap_stats(); + return ret_ptr; } @@ -1148,6 +1193,7 @@ void gc_free(void *ptr) { } while (ATB_GET_KIND(area, block) == AT_TAIL); GC_EXIT(); + gc_perfetto_emit_heap_stats(); #if EXTENSIVE_HEAP_PROFILING gc_dump_alloc_table(&mp_plat_print); @@ -1288,6 +1334,8 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { memorymonitor_track_allocation(new_blocks); #endif + gc_perfetto_emit_heap_stats(); + return ptr_in; } @@ -1325,6 +1373,8 @@ void *gc_realloc(void *ptr_in, size_t n_bytes, bool allow_move) { memorymonitor_track_allocation(new_blocks); #endif + gc_perfetto_emit_heap_stats(); + return ptr_in; } @@ -1397,7 +1447,7 @@ void gc_dump_alloc_table(const mp_print_t *print) { } if (bl2 - bl >= 2 * DUMP_BYTES_PER_LINE) { // there are at least 2 lines containing only free blocks, so abbreviate their printing - mp_printf(print, "\n (%u lines all free)", (uint)(bl2 - bl) / DUMP_BYTES_PER_LINE); + mp_printf(print, "\n (%u lines all free)", (uint)((bl2 - bl) / DUMP_BYTES_PER_LINE)); bl = bl2 & (~(DUMP_BYTES_PER_LINE - 1)); if (bl >= area->gc_alloc_table_byte_len * BLOCKS_PER_ATB) { // got to end of heap @@ -1440,7 +1490,7 @@ void gc_dump_alloc_table(const mp_print_t *print) { break; } */ - /* this prints the uPy object type of the head block */ + /* this prints the MicroPython object type of the head block */ case AT_HEAD: { // CIRCUITPY-CHANGE: compiler warning avoidance #pragma GCC diagnostic push diff --git a/py/gc.h b/py/gc.h index ebc32b080fb47..f9028d637c5c6 100644 --- a/py/gc.h +++ b/py/gc.h @@ -71,6 +71,11 @@ bool gc_alloc_possible(void); // Use this function to sweep the whole heap and run all finalisers void gc_sweep_all(void); +// These functions are used to manage weakrefs. +void gc_weakref_mark(void *ptr); +void gc_weakref_about_to_be_freed(void *ptr); +void gc_weakref_sweep(void); + enum { GC_ALLOC_FLAG_HAS_FINALISER = 1, // CIRCUITPY-CHANGE @@ -87,7 +92,7 @@ void *gc_realloc(void *ptr, size_t n_bytes, bool allow_move); // CIRCUITPY-CHANGE // True if the pointer is on the MP heap. Doesn't require that it is the start // of a block. -bool gc_ptr_on_heap(void *ptr); +bool gc_ptr_on_heap(const void *ptr); typedef struct _gc_info_t { size_t total; diff --git a/py/make_root_pointers.py b/py/make_root_pointers.py index efe398b8227ca..5da343c1270ec 100644 --- a/py/make_root_pointers.py +++ b/py/make_root_pointers.py @@ -6,8 +6,6 @@ "struct _mp_state_vm_t" in py/mpstate.h """ -from __future__ import print_function - import argparse import io import re diff --git a/py/makecompresseddata.py b/py/makecompresseddata.py index 1bce3e8e8374a..a6fa7db00b280 100644 --- a/py/makecompresseddata.py +++ b/py/makecompresseddata.py @@ -1,5 +1,3 @@ -from __future__ import print_function - import collections import re import sys diff --git a/py/makemoduledefs.py b/py/makemoduledefs.py index 29162ab387c8b..c8fdc21f6fbe2 100644 --- a/py/makemoduledefs.py +++ b/py/makemoduledefs.py @@ -14,8 +14,6 @@ the built-in version. """ -from __future__ import print_function - import sys import re import io @@ -87,19 +85,25 @@ def generate_module_table_header(modules): ) ) + # There should always be at least one module (__main__ in runtime.c) + assert mod_defs + print("\n#define MICROPY_REGISTERED_MODULES \\") for mod_def in sorted(mod_defs): print(" {mod_def} \\".format(mod_def=mod_def)) - print("// MICROPY_REGISTERED_MODULES") - print("\n#define MICROPY_REGISTERED_EXTENSIBLE_MODULES \\") + # There are not necessarily any extensible modules (e.g., bare-arm or minimal x86) + print("\n#define MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES ", len(extensible_mod_defs)) - for mod_def in sorted(extensible_mod_defs): - print(" {mod_def} \\".format(mod_def=mod_def)) + if extensible_mod_defs: + print("\n#define MICROPY_REGISTERED_EXTENSIBLE_MODULES \\") + + for mod_def in sorted(extensible_mod_defs): + print(" {mod_def} \\".format(mod_def=mod_def)) - print("// MICROPY_REGISTERED_EXTENSIBLE_MODULES") + print("// MICROPY_REGISTERED_EXTENSIBLE_MODULES") def generate_module_delegations(delegations): diff --git a/py/makeqstrdata.py b/py/makeqstrdata.py index 45cc896f9252e..3fdb2d5476b8a 100644 --- a/py/makeqstrdata.py +++ b/py/makeqstrdata.py @@ -1,31 +1,13 @@ """ Process raw qstr file and output qstr data with length, hash and data bytes. -This script works with Python 2.7, 3.3 and 3.4. - -CIRCUITPY-CHANGE: -For documentation about the format of compressed translated strings, see -supervisor/shared/translate/translate.h +This script works with Python 3.3+. """ -from __future__ import print_function - - import re import sys -# CIRCUITPY-CHANGE -if hasattr(sys.stdout, "reconfigure"): - sys.stdout.reconfigure(encoding="utf-8") - sys.stderr.reconfigure(errors="backslashreplace") - -# Python 2/3 compatibility: -# - iterating through bytes is different -# - codepoint2name from html.entities is hard-coded -if sys.version_info[0] == 2: - bytes_cons = lambda val, enc=None: bytearray(val) -elif sys.version_info[0] == 3: # Also handles MicroPython - bytes_cons = bytes +bytes_cons = bytes # fmt: off codepoint2name = { @@ -51,7 +33,7 @@ 954: "kappa", 8656: "lArr", 955: "lambda", 9001: "lang", 171: "laquo", 8592: "larr", 8968: "lceil", 8220: "ldquo", 8804: "le", 8970: "lfloor", 8727: "lowast", 9674: "loz", 8206: "lrm", 8249: "lsaquo", 8216: "lsquo", 60: "lt", 175: "macr", 8212: "mdash", 181: "micro", 183: "middot", 8722: "minus", - 956: "mu", 8711: "nabla", 160: "nbsp", 8211: "ndash", 8800: "ne", 8715: "ni", 172: "not", 8713: "notin", + 956: "mu", 8711: "nabla", 160: "nbsp", 8211: "ndash", 8800: "ne", 8715: "ni", 172: "not", 8713: "notin", # codespell:ignore notin 8836: "nsub", 241: "ntilde", 957: "nu", 243: "oacute", 244: "ocirc", 339: "oelig", 242: "ograve", 8254: "oline", 969: "omega", 959: "omicron", 8853: "oplus", 8744: "or", 170: "ordf", 186: "ordm", 248: "oslash", 245: "otilde", 8855: "otimes", 246: "ouml", 182: "para", 8706: "part", 8240: "permil", @@ -67,7 +49,6 @@ 253: "yacute", 165: "yen", 255: "yuml", 950: "zeta", 8205: "zwj", 8204: "zwnj" } # fmt: on -# end compatibility code codepoint2name[ord("-")] = "hyphen" diff --git a/py/makeqstrdefs.py b/py/makeqstrdefs.py index a403985769489..8c07899baf843 100644 --- a/py/makeqstrdefs.py +++ b/py/makeqstrdefs.py @@ -2,11 +2,9 @@ This script processes the output from the C preprocessor and extracts all qstr. Each qstr is transformed into a qstr definition of the form 'Q(...)'. -This script works with Python 3.x (CIRCUITPY-CHANGE: not 2.x) +This script works with Python 3.3+. """ -from __future__ import print_function - import io import os import re @@ -139,6 +137,7 @@ def qstr_unescape(qstr): return qstr +# CIRCUITPY-CHANGE: output_filename as an arg def process_file(f, output_filename=None): # match gcc-like output (# n "file") and msvc-like output (#line n "file") re_line = re.compile(r"^#(?:line)?\s+\d+\s\"([^\"]+)\"") @@ -292,6 +291,7 @@ class Args: args.input_filename = sys.argv[3] # Unused for command=cat args.output_dir = sys.argv[4] args.output_file = None if len(sys.argv) == 5 else sys.argv[5] # Unused for command=split + # CIRCUITPY-CHANGE if args.output_file == "_": args.output_file = None @@ -306,6 +306,7 @@ class Args: if args.command == "split": with io.open(args.input_filename, encoding="utf-8") as infile: + # CIRCUITPY-CHANGE: pass output_file process_file(infile, args.output_file) if args.command == "cat": diff --git a/py/makeversionhdr.py b/py/makeversionhdr.py index 307bfac1e9adb..94321ef0bad4d 100644 --- a/py/makeversionhdr.py +++ b/py/makeversionhdr.py @@ -5,8 +5,6 @@ This script works with Python 3.7 and newer """ -from __future__ import print_function - import argparse import sys import os diff --git a/py/malloc.c b/py/malloc.c index fcf930ecfaa83..fc9795043a879 100644 --- a/py/malloc.c +++ b/py/malloc.c @@ -54,9 +54,11 @@ // freely accessed - for interfacing with system and 3rd-party libs for // example. On the other hand, some (e.g. bare-metal) ports may use GC // heap as system heap, so, to avoid warnings, we do undef's first. -// CIRCUITPY-CHANGE: Add selective collect support to malloc to optimize GC for large buffers +#undef malloc #undef free #undef realloc +#define malloc(b) gc_alloc((b), false) +#define malloc_with_finaliser(b) gc_alloc((b), true) #define free gc_free #define realloc(ptr, n) gc_realloc(ptr, n, true) #define realloc_ext(ptr, n, mv) gc_realloc(ptr, n, mv) @@ -68,6 +70,7 @@ #error MICROPY_ENABLE_FINALISER requires MICROPY_ENABLE_GC #endif +// CIRCUITPY-CHANGE: Add selective collect support to malloc to optimize GC for large buffers #if MICROPY_ENABLE_SELECTIVE_COLLECT #error MICROPY_ENABLE_SELECTIVE_COLLECT requires MICROPY_ENABLE_GC #endif @@ -85,7 +88,7 @@ static void *realloc_ext(void *ptr, size_t n_bytes, bool allow_move) { #endif // MICROPY_ENABLE_GC -// CIRCUITPY-CHANGE: Add malloc helper with flags instead of a list of bools. +// CIRCUITPY-CHANGE: Add malloc helper to factor out flag handling and allow combinations. void *m_malloc_helper(size_t num_bytes, uint8_t flags) { void *ptr; #if MICROPY_ENABLE_GC @@ -112,7 +115,6 @@ void *m_malloc_helper(size_t num_bytes, uint8_t flags) { MP_STATE_MEM(current_bytes_allocated) += num_bytes; UPDATE_PEAK(); #endif - // CIRCUITPY-CHANGE // If this config is set then the GC clears all memory, so we don't need to. #if !MICROPY_GC_CONSERVATIVE_CLEAR if (flags & M_MALLOC_ENSURE_ZEROED) { @@ -124,26 +126,37 @@ void *m_malloc_helper(size_t num_bytes, uint8_t flags) { } void *m_malloc(size_t num_bytes) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: use helper return m_malloc_helper(num_bytes, M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT); } void *m_malloc_maybe(size_t num_bytes) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: use helper return m_malloc_helper(num_bytes, M_MALLOC_COLLECT); } +#if MICROPY_ENABLE_FINALISER +void *m_malloc_with_finaliser(size_t num_bytes) { + // CIRCUITPY-CHANGE: use helper + return m_malloc_helper(num_bytes, M_MALLOC_COLLECT | M_MALLOC_WITH_FINALISER); +} +#endif + void *m_malloc0(size_t num_bytes) { - return m_malloc_helper(num_bytes, M_MALLOC_ENSURE_ZEROED | M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT); + // CIRCUITPY-CHANGE: use helper + return m_malloc_helper(num_bytes, + (MICROPY_GC_CONSERVATIVE_CLEAR ? 0 : M_MALLOC_ENSURE_ZEROED) + | M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT); } +// CIRCUITPY-CHANGE: add selective collect void *m_malloc_without_collect(size_t num_bytes) { - // CIRCUITPY-CHANGE return m_malloc_helper(num_bytes, M_MALLOC_RAISE_ERROR); } +// CIRCUITPY-CHANGE: add selective collect void *m_malloc_maybe_without_collect(size_t num_bytes) { - // CIRCUITPY-CHANGE + return m_malloc_helper(num_bytes, 0); } @@ -224,6 +237,31 @@ void m_free(void *ptr) #if MICROPY_TRACKED_ALLOC +#if MICROPY_PY_THREAD && !MICROPY_PY_THREAD_GIL +// If there's no GIL, use the GC recursive mutex to protect the tracked node linked list +// under m_tracked_head. +// +// (For ports with GIL, the expectation is to only call tracked alloc functions +// while holding the GIL.) + +static inline void m_tracked_node_lock(void) { + mp_thread_recursive_mutex_lock(&MP_STATE_MEM(gc_mutex), 1); +} + +static inline void m_tracked_node_unlock(void) { + mp_thread_recursive_mutex_unlock(&MP_STATE_MEM(gc_mutex)); +} + +#else + +static inline void m_tracked_node_lock(void) { +} + +static inline void m_tracked_node_unlock(void) { +} + +#endif + #define MICROPY_TRACKED_ALLOC_STORE_SIZE (!MICROPY_ENABLE_GC) typedef struct _m_tracked_node_t { @@ -237,6 +275,7 @@ typedef struct _m_tracked_node_t { #if MICROPY_DEBUG_VERBOSE static size_t m_tracked_count_links(size_t *nb) { + m_tracked_node_lock(); m_tracked_node_t *node = MP_STATE_VM(m_tracked_head); size_t n = 0; *nb = 0; @@ -249,6 +288,7 @@ static size_t m_tracked_count_links(size_t *nb) { #endif node = node->next; } + m_tracked_node_unlock(); return n; } #endif @@ -263,12 +303,14 @@ void *m_tracked_calloc(size_t nmemb, size_t size) { size_t n = m_tracked_count_links(&nb); DEBUG_printf("m_tracked_calloc(%u, %u) -> (%u;%u) %p\n", (int)nmemb, (int)size, (int)n, (int)nb, node); #endif + m_tracked_node_lock(); if (MP_STATE_VM(m_tracked_head) != NULL) { MP_STATE_VM(m_tracked_head)->prev = node; } node->prev = NULL; node->next = MP_STATE_VM(m_tracked_head); MP_STATE_VM(m_tracked_head) = node; + m_tracked_node_unlock(); #if MICROPY_TRACKED_ALLOC_STORE_SIZE node->size = nmemb * size; #endif @@ -294,7 +336,8 @@ void m_tracked_free(void *ptr_in) { size_t nb; size_t n = m_tracked_count_links(&nb); DEBUG_printf("m_tracked_free(%p, [%p, %p], nbytes=%u, links=%u;%u)\n", node, node->prev, node->next, (int)data_bytes, (int)n, (int)nb); - #endif + #endif // MICROPY_DEBUG_VERBOSE + m_tracked_node_lock(); if (node->next != NULL) { node->next->prev = node->prev; } @@ -303,6 +346,7 @@ void m_tracked_free(void *ptr_in) { } else { MP_STATE_VM(m_tracked_head) = node->next; } + m_tracked_node_unlock(); m_free(node #if MICROPY_MALLOC_USES_ALLOCATED_SIZE #if MICROPY_TRACKED_ALLOC_STORE_SIZE diff --git a/py/misc.h b/py/misc.h index eb7fc54be6fec..d5d7950574f90 100644 --- a/py/misc.h +++ b/py/misc.h @@ -26,6 +26,8 @@ #ifndef MICROPY_INCLUDED_PY_MISC_H #define MICROPY_INCLUDED_PY_MISC_H +#include "py/mpconfig.h" + // a mini library of useful types and functions /** types *******************************************************/ @@ -33,13 +35,25 @@ #include #include #include - -// CIRCUITPY-CHANGE: include directly instead of depending on previous includes -#include "mpconfig.h" +// CIRCUITPY-CHANGE: #ifdef instead of #if +#ifdef __cplusplus // Required on at least one compiler to get ULLONG_MAX +#include +#else +#include +#endif typedef unsigned char byte; typedef unsigned int uint; +#ifndef __has_builtin +#define __has_builtin(x) (0) +#endif +#ifndef __has_feature +// This macro is supported by Clang and gcc>=14 +#define __has_feature(x) (0) +#endif + + /** generic ops *************************************************/ #ifndef MIN @@ -54,7 +68,15 @@ typedef unsigned int uint; #define MP_STRINGIFY(x) MP_STRINGIFY_HELPER(x) // Static assertion macro +// CIRCUITPY-CHANGE: defined() +#if defined(__cplusplus) +#define MP_STATIC_ASSERT(cond) static_assert((cond), #cond) +#elif __GNUC__ >= 5 || __STDC_VERSION__ >= 201112L +#define MP_STATIC_ASSERT(cond) _Static_assert((cond), #cond) +#else #define MP_STATIC_ASSERT(cond) ((void)sizeof(char[1 - 2 * !(cond)])) +#endif + // In C++ things like comparing extern const pointers are not constant-expressions so cannot be used // in MP_STATIC_ASSERT. Note that not all possible compiler versions will reject this. Some gcc versions // do, others only with -Werror=vla, msvc always does. @@ -63,7 +85,11 @@ typedef unsigned int uint; #if defined(_MSC_VER) || defined(__cplusplus) #define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)1) #else -#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) MP_STATIC_ASSERT(cond) +// CIRCUITPY-CHANGE: defined() +#if defined(__clang__) +#pragma GCC diagnostic ignored "-Wgnu-folding-constant" +#endif +#define MP_STATIC_ASSERT_NONCONSTEXPR(cond) ((void)sizeof(char[1 - 2 * !(cond)])) #endif // Round-up integer division @@ -88,9 +114,9 @@ typedef unsigned int uint; #define m_new0(type, num) ((type *)(m_malloc0(sizeof(type) * (num)))) #define m_new_obj(type) (m_new(type, 1)) #define m_new_obj_maybe(type) (m_new_maybe(type, 1)) -#define m_new_obj_var(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_helper(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num), M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT)) -#define m_new_obj_var0(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_helper(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num), M_MALLOC_ENSURE_ZEROED | M_MALLOC_RAISE_ERROR | M_MALLOC_COLLECT)) -#define m_new_obj_var_maybe(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_helper(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num), M_MALLOC_COLLECT)) +#define m_new_obj_var(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var0(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc0(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) +#define m_new_obj_var_maybe(obj_type, var_field, var_type, var_num) ((obj_type *)m_malloc_maybe(offsetof(obj_type, var_field) + sizeof(var_type) * (var_num))) #if MICROPY_MALLOC_USES_ALLOCATED_SIZE #define m_renew(type, ptr, old_num, new_num) ((type *)(m_realloc((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num)))) #define m_renew_maybe(type, ptr, old_num, new_num, allow_move) ((type *)(m_realloc_maybe((ptr), sizeof(type) * (old_num), sizeof(type) * (new_num), (allow_move)))) @@ -116,9 +142,13 @@ typedef unsigned int uint; void *m_malloc_helper(size_t num_bytes, uint8_t flags); void *m_malloc(size_t num_bytes); void *m_malloc_maybe(size_t num_bytes); +void *m_malloc_with_finaliser(size_t num_bytes); void *m_malloc0(size_t num_bytes); + +// CIRCUITPY-CHANGE: _without_collect void *m_malloc_without_collect(size_t num_bytes); void *m_malloc_maybe_without_collect(size_t num_bytes); + #if MICROPY_MALLOC_USES_ALLOCATED_SIZE void *m_realloc(void *ptr, size_t old_num_bytes, size_t new_num_bytes); void *m_realloc_maybe(void *ptr, size_t old_num_bytes, size_t new_num_bytes, bool allow_move); @@ -128,7 +158,7 @@ void *m_realloc(void *ptr, size_t new_num_bytes); void *m_realloc_maybe(void *ptr, size_t new_num_bytes, bool allow_move); void m_free(void *ptr); #endif -NORETURN void m_malloc_fail(size_t num_bytes); +MP_NORETURN void m_malloc_fail(size_t num_bytes); #if MICROPY_TRACKED_ALLOC // These alloc/free functions track the pointers in a linked list so the GC does not reclaim @@ -151,7 +181,7 @@ size_t m_get_peak_bytes_allocated(void); // align ptr to the nearest multiple of "alignment" #define MP_ALIGN(ptr, alignment) (void *)(((uintptr_t)(ptr) + ((alignment) - 1)) & ~((alignment) - 1)) -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: addition #define sizeof_field(TYPE, MEMBER) sizeof((((TYPE *)0)->MEMBER)) /** unichar / UTF-8 *********************************************/ @@ -298,6 +328,25 @@ typedef union _mp_float_union_t { mp_float_uint_t i; } mp_float_union_t; +#if MICROPY_FLOAT_FORMAT_IMPL == MICROPY_FLOAT_FORMAT_IMPL_EXACT + +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +// Exact float conversion requires using internally a bigger sort of floating point +typedef double mp_large_float_t; +#elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +typedef long double mp_large_float_t; +#endif +// Always use a 64 bit mantissa for formatting and parsing +typedef uint64_t mp_large_float_uint_t; + +#else // MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_EXACT + +// No bigger floating points +typedef mp_float_t mp_large_float_t; +typedef mp_float_uint_t mp_large_float_uint_t; + +#endif + #endif // MICROPY_PY_BUILTINS_FLOAT /** ROM string compression *************/ @@ -403,26 +452,28 @@ static inline bool mp_check(bool value) { static inline uint32_t mp_popcount(uint32_t x) { return __popcnt(x); } -#else +#else // _MSC_VER #define mp_clz(x) __builtin_clz(x) #define mp_clzl(x) __builtin_clzl(x) #define mp_clzll(x) __builtin_clzll(x) #define mp_ctz(x) __builtin_ctz(x) #define mp_check(x) (x) -#if defined __has_builtin #if __has_builtin(__builtin_popcount) #define mp_popcount(x) __builtin_popcount(x) -#endif -#endif -#if !defined(mp_popcount) +#else static inline uint32_t mp_popcount(uint32_t x) { x = x - ((x >> 1) & 0x55555555); x = (x & 0x33333333) + ((x >> 2) & 0x33333333); x = (x + (x >> 4)) & 0x0F0F0F0F; - return x * 0x01010101; + return (x * 0x01010101) >> 24; } -#endif -#endif +#endif // __has_builtin(__builtin_popcount) +#endif // _MSC_VER + +#define MP_FIT_UNSIGNED(bits, value) (((value) & (~0U << (bits))) == 0) +#define MP_FIT_SIGNED(bits, value) \ + (MP_FIT_UNSIGNED(((bits) - 1), (value)) || \ + (((value) & (~0U << ((bits) - 1))) == (~0U << ((bits) - 1)))) // mp_int_t can be larger than long, i.e. Windows 64-bit, nan-box variants static inline uint32_t mp_clz_mpi(mp_int_t x) { @@ -438,16 +489,107 @@ static inline uint32_t mp_clz_mpi(mp_int_t x) { } return zeroes; #else - MP_STATIC_ASSERT(sizeof(mp_int_t) == sizeof(long long) - || sizeof(mp_int_t) == sizeof(long)); + #if MP_INT_MAX == INT_MAX + return mp_clz((unsigned)x); + #elif MP_INT_MAX == LONG_MAX + return mp_clzl((unsigned long)x); + #elif MP_INT_MAX == LLONG_MAX + return mp_clzll((unsigned long long)x); + #else + #error Unexpected MP_INT_MAX value + #endif + #endif +} + +// Overflow-checked operations + +// Integer overflow builtins were added to GCC 5, but __has_builtin only in GCC 10 +// +// Note that the builtins has a defined result when overflow occurs, whereas the custom +// functions below don't update the result if an overflow would occur (to avoid UB). +#define MP_GCC_HAS_BUILTIN_OVERFLOW (__GNUC__ >= 5) - // ugly, but should compile to single intrinsic unless O0 is set - if (mp_check(sizeof(mp_int_t) == sizeof(long))) { - return mp_clzl((unsigned long)x); +#if MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC + +#define mp_mul_ull_overflow __builtin_umulll_overflow +#define mp_mul_ll_overflow __builtin_smulll_overflow +static inline bool mp_mul_mp_int_t_overflow(mp_int_t x, mp_int_t y, mp_int_t *res) { + // __builtin_mul_overflow is a type-generic function, this inline ensures the argument + // types are checked to match mp_int_t. + return __builtin_mul_overflow(x, y, res); +} + +#else + +bool mp_mul_ll_overflow(long long int x, long long int y, long long int *res); +bool mp_mul_mp_int_t_overflow(mp_int_t x, mp_int_t y, mp_int_t *res); +static inline bool mp_mul_ull_overflow(unsigned long long int x, unsigned long long int y, unsigned long long int *res) { + if (y > 0 && x > (ULLONG_MAX / y)) { + return true; // overflow + } + *res = x * y; + return false; +} + +#endif + +#if __has_builtin(__builtin_saddll_overflow) || MP_GCC_HAS_BUILTIN_OVERFLOW +#define mp_add_ll_overflow __builtin_saddll_overflow +#else +static inline bool mp_add_ll_overflow(long long int lhs, long long int rhs, long long int *res) { + bool overflow; + + if (rhs > 0) { + overflow = (lhs > LLONG_MAX - rhs); } else { - return mp_clzll((unsigned long long)x); + overflow = (lhs < LLONG_MIN - rhs); } - #endif + + if (!overflow) { + *res = lhs + rhs; + } + + return overflow; +} +#endif + +#if __has_builtin(__builtin_ssubll_overflow) || MP_GCC_HAS_BUILTIN_OVERFLOW +#define mp_sub_ll_overflow __builtin_ssubll_overflow +#else +static inline bool mp_sub_ll_overflow(long long int lhs, long long int rhs, long long int *res) { + bool overflow; + + if (rhs > 0) { + overflow = (lhs < LLONG_MIN + rhs); + } else { + overflow = (lhs > LLONG_MAX + rhs); + } + + if (!overflow) { + *res = lhs - rhs; + } + + return overflow; } +#endif + + +// Helper macros for detecting if sanitizers are enabled +// +// Use sparingly, not for masking issues reported by sanitizers! +// +// Can be detected automatically in Clang and gcc>=14, need to be +// set manually otherwise. +#ifndef MP_UBSAN +#define MP_UBSAN __has_feature(undefined_behavior_sanitizer) +#endif + +#ifndef MP_ASAN +#define MP_ASAN __has_feature(address_sanitizer) +#endif + +#ifndef MP_SANITIZER_BUILD +#define MP_SANITIZER_BUILD (MP_UBSAN || MP_ASAN) +#endif #endif // MICROPY_INCLUDED_PY_MISC_H diff --git a/py/mkrules.cmake b/py/mkrules.cmake index 4374b8b4da3cb..e3d769cc59b5c 100644 --- a/py/mkrules.cmake +++ b/py/mkrules.cmake @@ -14,6 +14,9 @@ set(MICROPY_MODULEDEFS "${MICROPY_GENHDR_DIR}/moduledefs.h") set(MICROPY_ROOT_POINTERS_SPLIT "${MICROPY_GENHDR_DIR}/root_pointers.split") set(MICROPY_ROOT_POINTERS_COLLECTED "${MICROPY_GENHDR_DIR}/root_pointers.collected") set(MICROPY_ROOT_POINTERS "${MICROPY_GENHDR_DIR}/root_pointers.h") +set(MICROPY_COMPRESSED_SPLIT "${MICROPY_GENHDR_DIR}/compressed.split") +set(MICROPY_COMPRESSED_COLLECTED "${MICROPY_GENHDR_DIR}/compressed.collected") +set(MICROPY_COMPRESSED_DATA "${MICROPY_GENHDR_DIR}/compressed.data.h") if(NOT MICROPY_PREVIEW_VERSION_2) set(MICROPY_PREVIEW_VERSION_2 0) @@ -32,6 +35,13 @@ if(MICROPY_BOARD) ) endif() +# Need to do this before extracting MICROPY_CPP_DEF below. +if(MICROPY_ROM_TEXT_COMPRESSION) + target_compile_definitions(${MICROPY_TARGET} PUBLIC + MICROPY_ROM_TEXT_COMPRESSION=\(1\) + ) +endif() + # Need to do this before extracting MICROPY_CPP_DEF below. Rest of frozen # manifest handling is at the end of this file. if(MICROPY_FROZEN_MANIFEST) @@ -84,6 +94,18 @@ target_sources(${MICROPY_TARGET} PRIVATE ${MICROPY_ROOT_POINTERS} ) +if(MICROPY_ROM_TEXT_COMPRESSION) + target_sources(${MICROPY_TARGET} PRIVATE + ${MICROPY_COMPRESSED_DATA} + ) +endif() + +# Ensure genhdr directory is removed on clean + +set_property(TARGET ${MICROPY_TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${MICROPY_GENHDR_DIR}" +) + # Command to force the build of another command # Generate mpversion.h @@ -197,11 +219,41 @@ add_custom_command( DEPENDS ${MICROPY_ROOT_POINTERS_COLLECTED} ${MICROPY_PY_DIR}/make_root_pointers.py ) +# Generate compressed.data.h + +add_custom_command( + OUTPUT ${MICROPY_COMPRESSED_SPLIT} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py split compress ${MICROPY_QSTRDEFS_LAST} ${MICROPY_GENHDR_DIR}/compress _ + COMMAND touch ${MICROPY_COMPRESSED_SPLIT} + DEPENDS ${MICROPY_QSTRDEFS_LAST} + VERBATIM + COMMAND_EXPAND_LISTS +) + +add_custom_command( + OUTPUT ${MICROPY_COMPRESSED_COLLECTED} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makeqstrdefs.py cat compress _ ${MICROPY_GENHDR_DIR}/compress ${MICROPY_COMPRESSED_COLLECTED} + BYPRODUCTS "${MICROPY_COMPRESSED_COLLECTED}.hash" + DEPENDS ${MICROPY_COMPRESSED_SPLIT} + VERBATIM + COMMAND_EXPAND_LISTS +) + +add_custom_command( + OUTPUT ${MICROPY_COMPRESSED_DATA} + COMMAND ${Python3_EXECUTABLE} ${MICROPY_PY_DIR}/makecompresseddata.py ${MICROPY_COMPRESSED_COLLECTED} > ${MICROPY_COMPRESSED_DATA} + DEPENDS ${MICROPY_COMPRESSED_COLLECTED} ${MICROPY_PY_DIR}/makecompresseddata.py +) + # Build frozen code if enabled if(MICROPY_FROZEN_MANIFEST) set(MICROPY_FROZEN_CONTENT "${CMAKE_BINARY_DIR}/frozen_content.c") + set_property(TARGET ${MICROPY_TARGET} APPEND PROPERTY ADDITIONAL_CLEAN_FILES + "${CMAKE_BINARY_DIR}/frozen_mpy" + ) + target_sources(${MICROPY_TARGET} PRIVATE ${MICROPY_FROZEN_CONTENT} ) diff --git a/py/mkrules.mk b/py/mkrules.mk index f364297f0f209..03988996c71ce 100644 --- a/py/mkrules.mk +++ b/py/mkrules.mk @@ -11,6 +11,7 @@ ifeq ($(MICROPY_PREVIEW_VERSION_2),1) CFLAGS += -DMICROPY_PREVIEW_VERSION_2=1 endif +# CIRCUITPY-CHANGE: point to CircuitPython documentation HELP_BUILD_ERROR ?= "See \033[1;31mhttps://learn.adafruit.com/building-circuitpython; Adafruit Discord \#circuitpython-dev\033[0m" HELP_MPY_LIB_SUBMODULE ?= "\033[1;31mError: micropython-lib submodule is not initialized.\033[0m Run 'make submodules'" @@ -112,6 +113,14 @@ $(BUILD)/%.pp: %.c $(STEPECHO) "PreProcess $<" $(Q)$(CPP) $(CFLAGS) -Wp,-C,-dD,-dI -o $@ $< +.PHONY: $(BUILD)/%.sz +$(BUILD)/%.sz: $(BUILD)/%.o + $(Q)$(SIZE) $< + +# Special case for compiling auto-generated source files. +$(BUILD)/%.o: $(BUILD)/%.c + $(call compile_c) + # The following rule uses | to create an order only prerequisite. Order only # prerequisites only get built if they don't exist. They don't cause timestamp # checking to be performed. @@ -179,7 +188,7 @@ $(HEADER_BUILD)/compressed.collected: $(HEADER_BUILD)/compressed.split # will be created if they don't exist. OBJ_DIRS = $(sort $(dir $(OBJ))) $(OBJ): | $(OBJ_DIRS) -// CIRCUITPY-CHANGE: use $(Q) +# CIRCUITPY-CHANGE: use $(Q) $(OBJ_DIRS): $(Q)$(MKDIR) -p $@ @@ -260,7 +269,7 @@ submodules: $(ECHO) "Updating submodules: $(GIT_SUBMODULES)" ifneq ($(GIT_SUBMODULES),) $(Q)cd $(TOP) && git submodule sync $(GIT_SUBMODULES) - $(Q)cd $(TOP) && git submodule update --init --filter=blob:none $(GIT_SUBMODULES) || \ + $(Q)cd $(TOP) && git submodule update --init --filter=blob:none $(GIT_SUBMODULES) 2>/dev/null || \ git submodule update --init $(GIT_SUBMODULES) endif .PHONY: submodules diff --git a/py/modio.c b/py/modio.c index d3e563dbcf447..9aeb42d30aa13 100644 --- a/py/modio.c +++ b/py/modio.c @@ -169,12 +169,13 @@ static mp_obj_t bufwriter_flush(mp_obj_t self_in) { int err; mp_uint_t out_sz = mp_stream_write_exactly(self->stream, self->buf, self->len, &err); (void)out_sz; - // TODO: try to recover from a case of non-blocking stream, e.g. move - // remaining chunk to the beginning of buffer. - assert(out_sz == self->len); - self->len = 0; if (err != 0) { mp_raise_OSError(err); + } else { + // TODO: try to recover from a case of non-blocking stream, e.g. move + // remaining chunk to the beginning of buffer. + assert(out_sz == self->len); + self->len = 0; } } diff --git a/py/modmath.c b/py/modmath.c index 2b41bbcd7d15e..2a9ed1b7f2291 100644 --- a/py/modmath.c +++ b/py/modmath.c @@ -37,7 +37,7 @@ #define MP_PI_4 MICROPY_FLOAT_CONST(0.78539816339744830962) #define MP_3_PI_4 MICROPY_FLOAT_CONST(2.35619449019234492885) -static NORETURN void math_error(void) { +static MP_NORETURN void math_error(void) { mp_raise_ValueError(MP_ERROR_TEXT("math domain error")); } @@ -99,12 +99,16 @@ mp_float_t MICROPY_FLOAT_C_FUN(log2)(mp_float_t x) { MATH_FUN_1(sqrt, sqrt) // pow(x, y): returns x to the power of y #if MICROPY_PY_MATH_POW_FIX_NAN -mp_float_t pow_func(mp_float_t x, mp_float_t y) { +mp_float_t MICROPY_FLOAT_C_FUN(pow_func)(mp_float_t x, mp_float_t y) { // pow(base, 0) returns 1 for any base, even when base is NaN // pow(+1, exponent) returns 1 for any exponent, even when exponent is NaN if (x == MICROPY_FLOAT_CONST(1.0) || y == MICROPY_FLOAT_CONST(0.0)) { return MICROPY_FLOAT_CONST(1.0); } + // pow(base, NaN) returns NaN for any other value of base + if (isnan(y)) { + return y; + } return MICROPY_FLOAT_C_FUN(pow)(x, y); } MATH_FUN_2(pow, pow_func) @@ -161,6 +165,11 @@ MATH_FUN_2(atan2, atan2) MATH_FUN_1_TO_INT(ceil, ceil) // copysign(x, y) static mp_float_t MICROPY_FLOAT_C_FUN(copysign_func)(mp_float_t x, mp_float_t y) { + #if MICROPY_PY_MATH_COPYSIGN_FIX_NAN + if (isnan(y)) { + y = 0.0; + } + #endif return MICROPY_FLOAT_C_FUN(copysign)(x, y); } MATH_FUN_2(copysign, copysign_func) diff --git a/py/modmicropython.c b/py/modmicropython.c index ff25af8ff7eec..67bb32626c3fa 100644 --- a/py/modmicropython.c +++ b/py/modmicropython.c @@ -100,7 +100,7 @@ static mp_obj_t mp_micropython_qstr_info(size_t n_args, const mp_obj_t *args) { size_t n_pool, n_qstr, n_str_data_bytes, n_total_bytes; qstr_pool_info(&n_pool, &n_qstr, &n_str_data_bytes, &n_total_bytes); mp_printf(&mp_plat_print, "qstr pool: n_pool=%u, n_qstr=%u, n_str_data_bytes=%u, n_total_bytes=%u\n", - n_pool, n_qstr, n_str_data_bytes, n_total_bytes); + (uint)n_pool, (uint)n_qstr, (uint)n_str_data_bytes, (uint)n_total_bytes); if (n_args == 1) { // arg given means dump qstr data qstr_dump_data(); diff --git a/py/modsys.c b/py/modsys.c index 2adbc0b7bd66f..bbae93d0c3f78 100644 --- a/py/modsys.c +++ b/py/modsys.c @@ -95,8 +95,16 @@ static const MP_DEFINE_STR_OBJ(mp_sys_implementation_machine_obj, MICROPY_BANNER MP_ROM_PTR(&mp_sys_implementation_machine_obj) #if MICROPY_PERSISTENT_CODE_LOAD +// Note: Adding architecture flags to _mpy will break if the flags information +// takes up more bits than what is available in a small-int value. +#if MICROPY_EMIT_RV32 +#include "py/asmrv32.h" +#define MPY_FILE_ARCH_FLAGS (MICROPY_RV32_EXTENSIONS << 16) +#else +#define MPY_FILE_ARCH_FLAGS (0) +#endif #define SYS_IMPLEMENTATION_ELEMS__MPY \ - , MP_ROM_INT(MPY_FILE_HEADER_INT) + , MP_ROM_INT(MPY_FILE_HEADER_INT | MPY_FILE_ARCH_FLAGS) #else #define SYS_IMPLEMENTATION_ELEMS__MPY #endif @@ -113,6 +121,18 @@ static const MP_DEFINE_STR_OBJ(mp_sys_implementation__build_obj, MICROPY_BOARD_B #define SYS_IMPLEMENTATION_ELEMS__BUILD #endif +#if MICROPY_PY_THREAD +#if MICROPY_PY_THREAD_GIL +#define SYS_IMPLEMENTATION_ELEMS__THREAD \ + , MP_ROM_QSTR(MP_QSTR_GIL) +#else +#define SYS_IMPLEMENTATION_ELEMS__THREAD \ + , MP_ROM_QSTR(MP_QSTR_unsafe) +#endif +#else +#define SYS_IMPLEMENTATION_ELEMS__THREAD +#endif + #if MICROPY_PREVIEW_VERSION_2 #define SYS_IMPLEMENTATION_ELEMS__V2 \ , MP_ROM_TRUE @@ -130,6 +150,9 @@ static const qstr impl_fields[] = { #if defined(MICROPY_BOARD_BUILD_NAME) MP_QSTR__build, #endif + #if MICROPY_PY_THREAD + MP_QSTR__thread, + #endif #if MICROPY_PREVIEW_VERSION_2 MP_QSTR__v2, #endif @@ -137,20 +160,21 @@ static const qstr impl_fields[] = { static MP_DEFINE_ATTRTUPLE( mp_sys_implementation_obj, impl_fields, - 3 + MICROPY_PERSISTENT_CODE_LOAD + MICROPY_BOARD_BUILD + MICROPY_PREVIEW_VERSION_2, + 3 + MICROPY_PERSISTENT_CODE_LOAD + MICROPY_BOARD_BUILD + MICROPY_PY_THREAD + MICROPY_PREVIEW_VERSION_2, SYS_IMPLEMENTATION_ELEMS_BASE SYS_IMPLEMENTATION_ELEMS__MPY SYS_IMPLEMENTATION_ELEMS__BUILD + SYS_IMPLEMENTATION_ELEMS__THREAD SYS_IMPLEMENTATION_ELEMS__V2 ); #else static const mp_rom_obj_tuple_t mp_sys_implementation_obj = { {&mp_type_tuple}, 3 + MICROPY_PERSISTENT_CODE_LOAD, - // Do not include SYS_IMPLEMENTATION_ELEMS__BUILD or SYS_IMPLEMENTATION_ELEMS__V2 - // because SYS_IMPLEMENTATION_ELEMS__MPY may be empty if + // Do not include SYS_IMPLEMENTATION_ELEMS__BUILD, SYS_IMPLEMENTATION_ELEMS__THREAD + // or SYS_IMPLEMENTATION_ELEMS__V2 because SYS_IMPLEMENTATION_ELEMS__MPY may be empty if // MICROPY_PERSISTENT_CODE_LOAD is disabled, which means they'll share - // the same index. Cannot query _build or _v2 if MICROPY_PY_ATTRTUPLE is + // the same index. Cannot query _build, _thread or _v2 if MICROPY_PY_ATTRTUPLE is // disabled. { SYS_IMPLEMENTATION_ELEMS_BASE diff --git a/py/mpconfig.h b/py/mpconfig.h index a48958200616b..69a338d746a2f 100644 --- a/py/mpconfig.h +++ b/py/mpconfig.h @@ -26,6 +26,14 @@ #ifndef MICROPY_INCLUDED_PY_MPCONFIG_H #define MICROPY_INCLUDED_PY_MPCONFIG_H +#include + +#if defined(__cplusplus) // Required on at least one compiler to get ULLONG_MAX +#include +#else +#include +#endif + // CIRCUITPY-CHANGE // Is this a CircuitPython build? #ifndef CIRCUITPY @@ -40,7 +48,7 @@ // as well as a fallback to generate MICROPY_GIT_TAG if the git repo or tags // are unavailable. #define MICROPY_VERSION_MAJOR 1 -#define MICROPY_VERSION_MINOR 25 +#define MICROPY_VERSION_MINOR 27 #define MICROPY_VERSION_MICRO 0 #define MICROPY_VERSION_PRERELEASE 0 @@ -173,6 +181,78 @@ #define MICROPY_OBJ_IMMEDIATE_OBJS (MICROPY_OBJ_REPR != MICROPY_OBJ_REPR_D) #endif +// Definition of the `mp_int_t` and `mp_uint_t` types and associated macros. +// Normally, it suffices for the platform to do nothing: A type as wide +// as a pointer is chosen, unless nanboxing (REPR_D) is selected, in +// which case a 64-bit type is chosen to match the assumed size of +// double-precision floats. +// +// In the case of exceptions, the port, board, or variant must define +// MP_INT_TYPE as MP_INT_TYPE_OTHER and provide all the typedefs and +// defines. +#define MP_INT_TYPE_INTPTR (0) +#define MP_INT_TYPE_INT64 (1) +#define MP_INT_TYPE_OTHER (2) + +#if !defined(MP_INT_TYPE) +#if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D +#define MP_INT_TYPE (MP_INT_TYPE_INT64) +#else +#define MP_INT_TYPE (MP_INT_TYPE_INTPTR) +#endif +#endif + +#if MP_INT_TYPE == MP_INT_TYPE_INTPTR +typedef intptr_t mp_int_t; +typedef uintptr_t mp_uint_t; +#define MP_INT_MAX INTPTR_MAX +#define MP_INT_MIN INTPTR_MIN +#define MP_UINT_MAX INTPTR_UMAX +#elif MP_INT_TYPE == MP_INT_TYPE_INT64 +typedef int64_t mp_int_t; +typedef uint64_t mp_uint_t; +#define MP_INT_MAX INT64_MAX +#define MP_INT_MIN INT64_MIN +#define MP_UINT_MAX INT64_UMAX +#endif + +// mp_printf format support for mp_int_t. In the unusual case that MP_INT_MAX doesn't +// match any of the standard C types (int/long/long long), provide all 3 +// macros. Otherwise, rely on these automatic definitions. +#if !defined(INT_FMT) +#if MP_INT_MAX == INT_MAX +#define INT_FMT "%d" +#define UINT_FMT "%u" +#define HEX_FMT "%x" +#elif MP_INT_MAX == LONG_MAX +#define INT_FMT "%ld" +#define UINT_FMT "%lu" +#define HEX_FMT "%lx" +#elif MP_INT_MAX == LLONG_MAX +#define INT_FMT "%lld" +#define UINT_FMT "%llu" +#define HEX_FMT "%llx" +#else +#error Unexpected MP_INT_MAX value +#endif +#endif + +// mp_printf format support for size_t. In the unusual case that SIZE_MAX doesn't +// match any of the standard C types (int/long/long long), provide a +// macro. Otherwise, rely on these automatic definitions. +#if !defined(SIZE_FMT) +#if SIZE_MAX == UINT_MAX +#define SIZE_FMT "%u" +#elif SIZE_MAX == ULONG_MAX +#define SIZE_FMT "%lu" +#elif SIZE_MAX == ULLONG_MAX +#define SIZE_FMT "%llu" +#else +#error Unexpected SIZE_MAX value +#endif +#endif + + /*****************************************************************************/ /* Memory allocation policy */ @@ -418,6 +498,11 @@ #define MICROPY_EMIT_INLINE_XTENSA (0) #endif +// Whether to support uncommon Xtensa inline assembler opcodes +#ifndef MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES +#define MICROPY_EMIT_INLINE_XTENSA_UNCOMMON_OPCODES (0) +#endif + // Whether to emit Xtensa-Windowed native code #ifndef MICROPY_EMIT_XTENSAWIN #define MICROPY_EMIT_XTENSAWIN (0) @@ -428,9 +513,9 @@ #define MICROPY_EMIT_RV32 (0) #endif -// CIRCUITPY-CHANGE: make sure MICROPY_EMIT_NATIVE_DEBUG is defined -#ifndef MICROPY_EMIT_NATIVE_DEBUG -#define MICROPY_EMIT_NATIVE_DEBUG (0) +// Whether to emit RISC-V RV32 Zba opcodes in native code +#ifndef MICROPY_EMIT_RV32_ZBA +#define MICROPY_EMIT_RV32_ZBA (0) #endif // Whether to enable the RISC-V RV32 inline assembler @@ -438,6 +523,11 @@ #define MICROPY_EMIT_INLINE_RV32 (0) #endif +// Whether to enable the human-readable native instructions emitter +#ifndef MICROPY_EMIT_NATIVE_DEBUG +#define MICROPY_EMIT_NATIVE_DEBUG (0) +#endif + // Convenience definition for whether any native emitter is enabled #define MICROPY_EMIT_NATIVE (MICROPY_EMIT_X64 || MICROPY_EMIT_X86 || MICROPY_EMIT_THUMB || MICROPY_EMIT_ARM || MICROPY_EMIT_XTENSA || MICROPY_EMIT_XTENSAWIN || MICROPY_EMIT_RV32 || MICROPY_EMIT_NATIVE_DEBUG) @@ -497,6 +587,13 @@ #define MICROPY_COMP_CONST (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif +// Whether to enable float constant folding like 1.2+3.4 (when MICROPY_COMP_CONST_FOLDING is also enabled) +// and constant optimisation like id = const(1.2) (when MICROPY_COMP_CONST is also enabled) +// and constant lookup like math.inf (when MICROPY_COMP_MODULE_CONST is also enabled) +#ifndef MICROPY_COMP_CONST_FLOAT +#define MICROPY_COMP_CONST_FLOAT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + // Whether to enable optimisation of: a, b = c, d // Costs 124 bytes (Thumb2) #ifndef MICROPY_COMP_DOUBLE_TUPLE_ASSIGN @@ -727,6 +824,13 @@ #define MICROPY_STACK_CHECK_MARGIN (0) #endif +// The size of a separate stack used for hard IRQ handlers, which should be +// checked instead of the main stack when running a hard callback. 0 implies +// there is no separate ISR stack to check. +#ifndef MICROPY_STACK_SIZE_HARD_IRQ +#define MICROPY_STACK_SIZE_HARD_IRQ (0) +#endif + // Whether to have an emergency exception buffer #ifndef MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (0) @@ -896,8 +1000,25 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_COMPLEX (MICROPY_PY_BUILTINS_FLOAT) #endif -#ifndef MICROPY_PY_DOUBLE_TYPECODE -#define MICROPY_PY_DOUBLE_TYPECODE (MICROPY_PY_BUILTINS_FLOAT) +// Float to string conversion implementations +// +// Note that the EXACT method is only available if the compiler supports +// floating points larger than mp_float_t: +// - with MICROPY_FLOAT_IMPL_FLOAT, the compiler needs to support `double` +// - with MICROPY_FLOAT_IMPL_DOUBLE, the compiler needs to support `long double` +// +#define MICROPY_FLOAT_FORMAT_IMPL_BASIC (0) // smallest code, but inexact +#define MICROPY_FLOAT_FORMAT_IMPL_APPROX (1) // slightly bigger, almost perfect +#define MICROPY_FLOAT_FORMAT_IMPL_EXACT (2) // bigger code, and 100% exact repr + +#ifndef MICROPY_FLOAT_FORMAT_IMPL +#if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT +#define MICROPY_FLOAT_FORMAT_IMPL (MICROPY_FLOAT_FORMAT_IMPL_APPROX) +#elif defined(__SIZEOF_LONG_DOUBLE__) && __SIZEOF_LONG_DOUBLE__ > __SIZEOF_DOUBLE__ +#define MICROPY_FLOAT_FORMAT_IMPL (MICROPY_FLOAT_FORMAT_IMPL_EXACT) +#else +#define MICROPY_FLOAT_FORMAT_IMPL (MICROPY_FLOAT_FORMAT_IMPL_APPROX) +#endif #endif // Whether to use the native _Float16 for 16-bit float support @@ -932,6 +1053,64 @@ typedef double mp_float_t; #define MICROPY_FULL_CHECKS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif +// Ports can choose to use timestamps based on 2000-01-01 or 1970-01-01 +// Default is timestamps based on 2000-01-01 +#if !defined(MICROPY_EPOCH_IS_2000) && !defined(MICROPY_EPOCH_IS_1970) +#define MICROPY_EPOCH_IS_2000 (1) +#define MICROPY_EPOCH_IS_1970 (0) +#elif !defined(MICROPY_EPOCH_IS_1970) +#define MICROPY_EPOCH_IS_1970 (1 - (MICROPY_EPOCH_IS_2000)) +#elif !defined(MICROPY_EPOCH_IS_2000) +#define MICROPY_EPOCH_IS_2000 (1 - (MICROPY_EPOCH_IS_1970)) +#endif + +// To maintain reasonable compatibility with CPython on embedded systems, +// and avoid breaking anytime soon, time functions are defined to work +// at least between 1970 and 2099 (included) on any machine. +// +// Specific ports can enable extended date support +// - after 2099 using MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND +// - before 1970 using MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE +// The largest possible range is year 1600 to year 3000 +// +// By default, extended date support is only enabled for machines using 64 bit pointers, +// but it can be enabled by specific ports +#ifndef MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE +#if MP_SSIZE_MAX > 2147483647 +#define MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE (1) +#else +#define MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE (0) +#endif +#endif + +// When support for dates <1970 is enabled, supporting >=2100 does not cost anything +#ifndef MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND +#define MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND (MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE) +#endif + +// The type to be used to represent platform-specific timestamps depends on the choices above +#define MICROPY_TIMESTAMP_IMPL_LONG_LONG (0) +#define MICROPY_TIMESTAMP_IMPL_UINT (1) +#define MICROPY_TIMESTAMP_IMPL_TIME_T (2) + +#ifndef MICROPY_TIMESTAMP_IMPL +#if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE || MICROPY_EPOCH_IS_2000 +#define MICROPY_TIMESTAMP_IMPL (MICROPY_TIMESTAMP_IMPL_LONG_LONG) +#else +#define MICROPY_TIMESTAMP_IMPL (MICROPY_TIMESTAMP_IMPL_UINT) +#endif +#endif + +// `mp_timestamp_t` is the type that should be used by the port +// to represent timestamps, and is referenced to the platform epoch +#if MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_LONG_LONG +typedef long long mp_timestamp_t; +#elif MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_UINT +typedef mp_uint_t mp_timestamp_t; +#elif MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_TIME_T +typedef time_t mp_timestamp_t; +#endif + // Whether POSIX-semantics non-blocking streams are supported #ifndef MICROPY_STREAMS_NON_BLOCK #define MICROPY_STREAMS_NON_BLOCK (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -943,6 +1122,16 @@ typedef double mp_float_t; #define MICROPY_STREAMS_POSIX_API (0) #endif +// Whether to process __all__ when importing all public symbols from a module. +#ifndef MICROPY_MODULE___ALL__ +#define MICROPY_MODULE___ALL__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES) +#endif + +// Whether to set __file__ on imported modules. +#ifndef MICROPY_MODULE___FILE__ +#define MICROPY_MODULE___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#endif + // Whether modules can use MP_REGISTER_MODULE_DELEGATION() to delegate failed // attribute lookups to a custom handler function. #ifndef MICROPY_MODULE_ATTR_DELEGATION @@ -1030,6 +1219,16 @@ typedef double mp_float_t; #define MICROPY_ENABLE_VM_ABORT (0) #endif +// Whether to handle abort behavior in pyexec code +#ifndef MICROPY_PYEXEC_ENABLE_VM_ABORT +#define MICROPY_PYEXEC_ENABLE_VM_ABORT (0) +#endif + +// Whether to set exit codes according to the exit reason (keyboard interrupt, crash, normal exit, ...) +#ifndef MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING +#define MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING (0) +#endif + // Support for internal scheduler #ifndef MICROPY_ENABLE_SCHEDULER #define MICROPY_ENABLE_SCHEDULER (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -1065,6 +1264,11 @@ typedef double mp_float_t; #define MICROPY_VFS_POSIX (0) #endif +// Whether to include support for writable POSIX filesystems. +#ifndef MICROPY_VFS_POSIX_WRITABLE +#define MICROPY_VFS_POSIX_WRITABLE (1) +#endif + // Support for VFS FAT component, to mount a FAT filesystem within VFS #ifndef MICROPY_VFS_FAT #define MICROPY_VFS_FAT (0) @@ -1105,7 +1309,15 @@ typedef double mp_float_t; #define MICROPY_PY_FUNCTION_ATTRS_CODE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_FULL_FEATURES) #endif -// Whether to support the descriptors __get__, __set__, __delete__ +// Whether bound_method can just use == (feature disabled), or requires a call to +// mp_obj_equal (feature enabled), to test equality of the self and meth entities. +// This is only needed if objects and functions can be identical without being the +// same thing, eg when using an object proxy. +#ifndef MICROPY_PY_BOUND_METHOD_FULL_EQUALITY_CHECK +#define MICROPY_PY_BOUND_METHOD_FULL_EQUALITY_CHECK (0) +#endif + +// Whether to support the descriptors __get__, __set__, __delete__, __set_name__ // This costs some code size and makes load/store/delete of instance // attributes slower for the classes that use this feature #ifndef MICROPY_PY_DESCRIPTORS @@ -1362,11 +1574,6 @@ typedef double mp_float_t; #define MICROPY_PY_BUILTINS_HELP_MODULES (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) #endif -// Whether to set __file__ for imported modules -#ifndef MICROPY_PY___FILE__ -#define MICROPY_PY___FILE__ (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) -#endif - // Whether to provide mem-info related functions in micropython module #ifndef MICROPY_PY_MICROPYTHON_MEM_INFO #define MICROPY_PY_MICROPYTHON_MEM_INFO (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) @@ -1482,6 +1689,7 @@ typedef double mp_float_t; #endif // Whether to provide fix for pow(1, NaN) and pow(NaN, 0), which both should be 1 not NaN. +// Also fixes pow(base, NaN) to return NaN for other values of base. #ifndef MICROPY_PY_MATH_POW_FIX_NAN #define MICROPY_PY_MATH_POW_FIX_NAN (0) #endif @@ -1518,7 +1726,7 @@ typedef double mp_float_t; // Whether to provide "io.IOBase" class to support user streams #ifndef MICROPY_PY_IO_IOBASE -#define MICROPY_PY_IO_IOBASE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#define MICROPY_PY_IO_IOBASE (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif // Whether to provide "io.BytesIO" class @@ -1536,15 +1744,16 @@ typedef double mp_float_t; #define MICROPY_PY_STRUCT (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) #endif -// CIRCUITPY-CHANGE -// Whether to provide non-standard typecodes in "struct" module -#ifndef MICROPY_NONSTANDARD_TYPECODES -#define MICROPY_NONSTANDARD_TYPECODES (1) +// Whether struct module provides unsafe and non-standard typecodes O, P, S. +// These typecodes are not in CPython and can cause crashes by accessing arbitrary +// memory. +#ifndef MICROPY_PY_STRUCT_UNSAFE_TYPECODES +#define MICROPY_PY_STRUCT_UNSAFE_TYPECODES (1) #endif // Whether to provide "sys" module #ifndef MICROPY_PY_SYS -#define MICROPY_PY_SYS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES) +#define MICROPY_PY_SYS (1) #endif // Whether to initialise "sys.path" and "sys.argv" to their defaults in mp_init() @@ -1800,11 +2009,11 @@ typedef double mp_float_t; #endif #ifndef MICROPY_PY_HASHLIB_MD5 -#define MICROPY_PY_HASHLIB_MD5 (0) +#define MICROPY_PY_HASHLIB_MD5 (MICROPY_PY_SSL) #endif #ifndef MICROPY_PY_HASHLIB_SHA1 -#define MICROPY_PY_HASHLIB_SHA1 (0) +#define MICROPY_PY_HASHLIB_SHA1 (MICROPY_PY_SSL) #endif #ifndef MICROPY_PY_HASHLIB_SHA256 @@ -1812,7 +2021,7 @@ typedef double mp_float_t; #endif #ifndef MICROPY_PY_CRYPTOLIB -#define MICROPY_PY_CRYPTOLIB (0) +#define MICROPY_PY_CRYPTOLIB (MICROPY_PY_SSL) #endif // Depends on MICROPY_PY_CRYPTOLIB @@ -1935,6 +2144,11 @@ typedef double mp_float_t; #define MICROPY_PY_SSL_MBEDTLS_NEED_ACTIVE_CONTEXT (MICROPY_PY_SSL_ECDSA_SIGN_ALT) #endif +// Whether to support DTLS protocol (non-CPython feature) +#ifndef MICROPY_PY_SSL_DTLS +#define MICROPY_PY_SSL_DTLS (MICROPY_SSL_MBEDTLS && MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_EXTRA_FEATURES) +#endif + // Whether to provide the "vfs" module #ifndef MICROPY_PY_VFS #define MICROPY_PY_VFS (MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_CORE_FEATURES && MICROPY_VFS) @@ -2027,7 +2241,7 @@ typedef double mp_float_t; /*****************************************************************************/ /* Miscellaneous settings */ -// All uPy objects in ROM must be aligned on at least a 4 byte boundary +// All MicroPython objects in ROM must be aligned on at least a 4 byte boundary // so that the small-int/qstr/pointer distinction can be made. For machines // that don't do this (eg 16-bit CPU), define the following macro to something // like __attribute__((aligned(4))). @@ -2166,25 +2380,13 @@ typedef double mp_float_t; #define MP_SSIZE_MAX SSIZE_MAX #endif -// printf format spec to use for mp_int_t and friends -#ifndef INT_FMT -#if defined(__LP64__) -// Archs where mp_int_t == long, long != int -#define UINT_FMT "%lu" -#define INT_FMT "%ld" -#elif defined(_WIN64) -#define UINT_FMT "%llu" -#define INT_FMT "%lld" -#else -// Archs where mp_int_t == int -#define UINT_FMT "%u" -#define INT_FMT "%d" +// Modifier for function which doesn't return +#ifndef MP_NORETURN +#define MP_NORETURN __attribute__((noreturn)) #endif -#endif // INT_FMT -// Modifier for function which doesn't return -#ifndef NORETURN -#define NORETURN __attribute__((noreturn)) +#if !MICROPY_PREVIEW_VERSION_2 +#define NORETURN MP_NORETURN #endif // Modifier for weak functions @@ -2205,6 +2407,12 @@ typedef double mp_float_t; #define MP_INLINE inline MP_NO_INSTRUMENT #endif +// CIRCUITPY-CHANGE +// Modifier for functions whose return value should not be ignored +#ifndef MP_WARN_UNUSED_RESULT +#define MP_WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#endif + // Modifier for functions which should be never inlined #ifndef MP_NOINLINE #define MP_NOINLINE __attribute__((noinline)) @@ -2279,4 +2487,23 @@ typedef double mp_float_t; #define MP_WARN_CAT(x) (NULL) #endif +// If true, use __builtin_mul_overflow (a gcc intrinsic supported by clang) for +// overflow checking when multiplying two small ints. Otherwise, use a portable +// algorithm. +// +// Most MCUs have a 32x32->64 bit multiply instruction, in which case the +// intrinsic is likely to be faster and generate smaller code. The main exception is +// cortex-m0 with __ARM_ARCH_ISA_THUMB == 1. +// +// The intrinsic is in GCC starting with version 5. +#ifndef MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC +#if defined(__ARM_ARCH_ISA_THUMB) && (__GNUC__ >= 5) +#define MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC (__ARM_ARCH_ISA_THUMB >= 2) +#elif (__GNUC__ >= 5) +#define MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC (1) +#else +#define MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC (0) +#endif +#endif + #endif // MICROPY_INCLUDED_PY_MPCONFIG_H diff --git a/py/mperrno.h b/py/mperrno.h index 9e4ecd9419c66..6c675125f4e08 100644 --- a/py/mperrno.h +++ b/py/mperrno.h @@ -143,6 +143,9 @@ #endif +// Type for return values where 0 indicates success and negative values are negated MP_E* error codes. +typedef int mp_negative_errno_t; + #if MICROPY_PY_ERRNO #include "py/obj.h" diff --git a/py/mphal.h b/py/mphal.h index 95289ac856cb2..3907641be838a 100644 --- a/py/mphal.h +++ b/py/mphal.h @@ -27,6 +27,7 @@ #define MICROPY_INCLUDED_PY_MPHAL_H #include +#include #include "py/mpconfig.h" #ifdef MICROPY_MPHALPORT_H diff --git a/py/mpprint.c b/py/mpprint.c index e4e25f5a82e43..62e80d1e8e85c 100644 --- a/py/mpprint.c +++ b/py/mpprint.c @@ -40,8 +40,20 @@ #include "py/formatfloat.h" #endif -static const char pad_spaces[] = " "; -static const char pad_zeroes[] = "0000000000000000"; +static const char pad_spaces[16] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '}; +#define pad_spaces_size (sizeof(pad_spaces)) +static const char pad_common[23] = {'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '_', '0', '0', '0', ',', '0', '0'}; +// The contents of pad_common is arranged to provide the following padding +// strings with minimal flash size: +// 0000000000000000 <- pad_zeroes +// 0000_000 <- pad_zeroes_underscore (offset: 12, size 5) +// 000,00 <- pad_zeroes_comma (offset: 17, size 4) +#define pad_zeroes (pad_common + 0) +#define pad_zeroes_size (16) +#define pad_zeroes_underscore (pad_common + 12) +#define pad_zeroes_underscore_size (5) +#define pad_zeroes_comma (pad_common + 17) +#define pad_zeroes_comma_size (4) static void plat_print_strn(void *env, const char *str, size_t len) { (void)env; @@ -58,20 +70,37 @@ int mp_print_str(const mp_print_t *print, const char *str) { return len; } -int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width) { +int mp_print_strn(const mp_print_t *print, const char *str, size_t len, unsigned int flags, char fill, int width) { int left_pad = 0; int right_pad = 0; int pad = width - len; int pad_size; int total_chars_printed = 0; const char *pad_chars; + char grouping = flags >> PF_FLAG_SEP_POS; if (!fill || fill == ' ') { pad_chars = pad_spaces; - pad_size = sizeof(pad_spaces) - 1; - } else if (fill == '0') { + pad_size = pad_spaces_size; + } else if (fill == '0' && !grouping) { pad_chars = pad_zeroes; - pad_size = sizeof(pad_zeroes) - 1; + pad_size = pad_zeroes_size; + } else if (fill == '0') { + if (grouping == '_') { + pad_chars = pad_zeroes_underscore; + pad_size = pad_zeroes_underscore_size; + } else { + pad_chars = pad_zeroes_comma; + pad_size = pad_zeroes_comma_size; + } + // The result will never start with a grouping character. An extra leading zero is added. + // width is dead after this so we can use it in calculation + if (width % pad_size == 0) { + pad++; + width++; + } + // position the grouping character correctly within the pad repetition + pad_chars += pad_size - 1 - width % pad_size; } else { // Other pad characters are fairly unusual, so we'll take the hit // and output them 1 at a time. @@ -201,7 +230,7 @@ static int mp_print_int(const mp_print_t *print, mp_uint_t x, int sgn, int base, return len; } -int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec) { +int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, unsigned int base, int base_char, int flags, char fill, int width, int prec) { // These are the only values for "base" that are required to be supported by this // function, since Python only allows the user to format integers in these bases. // If needed this function could be generalised to handle other values. @@ -248,10 +277,7 @@ int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char int prefix_len = prefix - prefix_buf; prefix = prefix_buf; - char comma = '\0'; - if (flags & PF_FLAG_SHOW_COMMA) { - comma = ','; - } + char comma = flags >> PF_FLAG_SEP_POS; // The size of this buffer is rather arbitrary. If it's not large // enough, a dynamic one will be allocated. @@ -340,8 +366,8 @@ int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char } #if MICROPY_PY_BUILTINS_FLOAT -int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec) { - char buf[32]; +int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, unsigned int flags, char fill, int width, int prec) { + char buf[36]; char sign = '\0'; int chrs = 0; @@ -352,11 +378,17 @@ int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, c sign = ' '; } - int len = mp_format_float(f, buf, sizeof(buf), fmt, prec, sign); + int len = mp_format_float(f, buf, sizeof(buf) - 3, fmt, prec, sign); char *s = buf; - if ((flags & PF_FLAG_ADD_PERCENT) && (size_t)(len + 1) < sizeof(buf)) { + if ((flags & PF_FLAG_ALWAYS_DECIMAL) && strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) { + buf[len++] = '.'; + buf[len++] = '0'; + buf[len] = '\0'; + } + + if (flags & PF_FLAG_ADD_PERCENT) { buf[len++] = '%'; buf[len] = '\0'; } @@ -376,14 +408,6 @@ int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, c } #endif -// CIRCUITPY-CHANGE -static int print_str_common(const mp_print_t *print, const char *str, int prec, size_t len, int flags, int fill, int width) { - if (prec >= 0 && (size_t)prec < len) { - len = prec; - } - return mp_print_strn(print, str, len, flags, fill, width); -} - int mp_printf(const mp_print_t *print, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -424,8 +448,6 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { flags |= PF_FLAG_SHOW_SIGN; } else if (*fmt == ' ') { flags |= PF_FLAG_SPACE_SIGN; - } else if (*fmt == '!') { - flags |= PF_FLAG_NO_TRAILZ; } else if (*fmt == '0') { flags |= PF_FLAG_PAD_AFTER_SIGN; fill = '0'; @@ -459,16 +481,36 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { } } - // parse long specifiers (only for LP64 model where they make a difference) - #ifndef __LP64__ - const + // parse long and long long specifiers (only where they make a difference) + #if (MP_INT_MAX > INT_MAX && MP_INT_MAX == LONG_MAX) || defined(MICROPY_UNIX_COVERAGE) + #define SUPPORT_L_FORMAT (1) + #else + #define SUPPORT_L_FORMAT (0) #endif + #if SUPPORT_L_FORMAT bool long_arg = false; + #endif + + #if (MP_INT_MAX > LONG_MAX) || defined(MICROPY_UNIX_COVERAGE) + #define SUPPORT_LL_FORMAT (1) + #else + #define SUPPORT_LL_FORMAT (0) + #endif + #if SUPPORT_LL_FORMAT + bool long_long_arg = false; + #endif + if (*fmt == 'l') { ++fmt; - #ifdef __LP64__ + #if SUPPORT_L_FORMAT long_arg = true; #endif + #if SUPPORT_LL_FORMAT + if (*fmt == 'l') { + ++fmt; + long_long_arg = true; + } + #endif } if (*fmt == '\0') { @@ -492,27 +534,19 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { qstr qst = va_arg(args, qstr); size_t len; const char *str = (const char *)qstr_data(qst, &len); - // CIRCUITPY-CHANGE - chrs += print_str_common(print, str, prec, len, flags, fill, width); - break; - } - // CIRCUITPY-CHANGE: new code to print compressed strings - case 'S': { - mp_rom_error_text_t arg = va_arg(args, mp_rom_error_text_t); - size_t len_with_nul = decompress_length(arg); - size_t len = len_with_nul - 1; - char str[len_with_nul]; - decompress(arg, str); - chrs += print_str_common(print, str, prec, len, flags, fill, width); + if (prec >= 0 && (size_t)prec < len) { + len = prec; + } + chrs += mp_print_strn(print, str, len, flags, fill, width); break; } case 's': { const char *str = va_arg(args, const char *); #ifndef NDEBUG // With debugging enabled, catch printing of null string pointers - if (str == NULL) { - // CIRCUITPY-CHANGE - str = "(null)"; + if (prec != 0 && str == NULL) { + chrs += mp_print_strn(print, "(null)", 6, flags, fill, width); + break; } #endif size_t len = strlen(str); @@ -522,41 +556,58 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { chrs += mp_print_strn(print, str, len, flags, fill, width); break; } - case 'd': { - mp_int_t val; - if (long_arg) { - val = va_arg(args, long int); - } else { - val = va_arg(args, int); - } - chrs += mp_print_int(print, val, 1, 10, 'a', flags, fill, width); - break; - } + case 'd': + case 'p': + case 'P': case 'u': case 'x': case 'X': { - int base = 16 - ((*fmt + 1) & 6); // maps char u/x/X to base 10/16/16 - char fmt_c = (*fmt & 0xf0) - 'P' + 'A'; // maps char u/x/X to char a/a/A + char fmt_chr = *fmt; mp_uint_t val; - if (long_arg) { - val = va_arg(args, unsigned long int); + if (fmt_chr == 'p' || fmt_chr == 'P') { + val = va_arg(args, uintptr_t); + } + #if SUPPORT_LL_FORMAT + else if (long_long_arg) { + val = va_arg(args, unsigned long long); + } + #endif + #if SUPPORT_L_FORMAT + else if (long_arg) { + if (sizeof(long) != sizeof(mp_uint_t) && fmt_chr == 'd') { + val = va_arg(args, long); + } else { + val = va_arg(args, unsigned long); + } + } + #endif + else { + if (sizeof(int) != sizeof(mp_uint_t) && fmt_chr == 'd') { + val = va_arg(args, int); + } else { + val = va_arg(args, unsigned); + } + } + int base; + // Map format char x/p/X/P to a/a/A/A for hex letters. + // It doesn't matter what d/u map to. + char fmt_c = (fmt_chr & 0xf0) - 'P' + 'A'; + if (fmt_chr == 'd' || fmt_chr == 'u') { + base = 10; } else { - val = va_arg(args, unsigned int); + base = 16; + } + // CIRCUITPY-CHANGE: include "0x" for 'p' and 'P'. + if (fmt_chr == 'p' || fmt_chr == 'P') { + #if SUPPORT_INT_BASE_PREFIX + chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags | PF_FLAG_SHOW_PREFIX, fill, width); + #else + chrs += mp_print_strn(print, "0x", 2, flags, fill, width); + #endif } - chrs += mp_print_int(print, val, 0, base, fmt_c, flags, fill, width); + chrs += mp_print_int(print, val, fmt_chr == 'd', base, fmt_c, flags, fill, width); break; } - case 'p': - case 'P': // don't bother to handle upcase for 'P' - // Use unsigned long int to work on both ILP32 and LP64 systems - // CIRCUITPY-CHANGE: print 0x prefix - #if SUPPORT_INT_BASE_PREFIX - chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags | PF_FLAG_SHOW_PREFIX, fill, width); - #else - print->print_strn(print->data, "0x", 2); - chrs += mp_print_int(print, va_arg(args, unsigned long int), 0, 16, 'a', flags, fill, width) + 2; - #endif - break; #if MICROPY_PY_BUILTINS_FLOAT case 'e': case 'E': @@ -573,18 +624,21 @@ int mp_vprintf(const mp_print_t *print, const char *fmt, va_list args) { break; } #endif - // Because 'l' is eaten above, another 'l' means %ll. We need to support - // this length specifier for OBJ_REPR_D (64-bit NaN boxing). - // TODO Either enable this unconditionally, or provide a specific config var. - #if (MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_D) || defined(_WIN64) - case 'l': { - unsigned long long int arg_value = va_arg(args, unsigned long long int); - ++fmt; - assert(*fmt == 'u' || *fmt == 'd' || !"unsupported fmt char"); - chrs += mp_print_int(print, arg_value, *fmt == 'd', 10, 'a', flags, fill, width); + + // CIRCUITPY-CHANGE: new format code to print compressed strings + case 'S': { + mp_rom_error_text_t arg = va_arg(args, mp_rom_error_text_t); + size_t len_with_nul = decompress_length(arg); + size_t len = len_with_nul - 1; + char str[len_with_nul]; + decompress(arg, str); + if (prec >= 0 && (size_t)prec < len) { + len = prec; + } + chrs += mp_print_strn(print, str, len, flags, fill, width); break; } - #endif + default: // if it's not %% then it's an unsupported format character assert(*fmt == '%' || !"unsupported fmt char"); diff --git a/py/mpprint.h b/py/mpprint.h index e883cc27047f1..614c61ea41537 100644 --- a/py/mpprint.h +++ b/py/mpprint.h @@ -31,13 +31,13 @@ #define PF_FLAG_LEFT_ADJUST (0x001) #define PF_FLAG_SHOW_SIGN (0x002) #define PF_FLAG_SPACE_SIGN (0x004) -#define PF_FLAG_NO_TRAILZ (0x008) -#define PF_FLAG_SHOW_PREFIX (0x010) -#define PF_FLAG_SHOW_COMMA (0x020) -#define PF_FLAG_PAD_AFTER_SIGN (0x040) -#define PF_FLAG_CENTER_ADJUST (0x080) -#define PF_FLAG_ADD_PERCENT (0x100) -#define PF_FLAG_SHOW_OCTAL_LETTER (0x200) +#define PF_FLAG_SHOW_PREFIX (0x008) +#define PF_FLAG_PAD_AFTER_SIGN (0x010) +#define PF_FLAG_CENTER_ADJUST (0x020) +#define PF_FLAG_ADD_PERCENT (0x040) +#define PF_FLAG_SHOW_OCTAL_LETTER (0x080) +#define PF_FLAG_ALWAYS_DECIMAL (0x100) +#define PF_FLAG_SEP_POS (9) // must be above all the above PF_FLAGs #if MICROPY_PY_IO && MICROPY_PY_SYS_STDFILES #define MP_PYTHON_PRINTER &mp_sys_stdout_print @@ -69,9 +69,9 @@ extern const mp_print_t mp_sys_stdout_print; #endif int mp_print_str(const mp_print_t *print, const char *str); -int mp_print_strn(const mp_print_t *print, const char *str, size_t len, int flags, char fill, int width); +int mp_print_strn(const mp_print_t *print, const char *str, size_t len, unsigned int flags, char fill, int width); #if MICROPY_PY_BUILTINS_FLOAT -int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, int flags, char fill, int width, int prec); +int mp_print_float(const mp_print_t *print, mp_float_t f, char fmt, unsigned int flags, char fill, int width, int prec); #endif int mp_printf(const mp_print_t *print, const char *fmt, ...); diff --git a/py/mpstate.h b/py/mpstate.h index 4c48e9edaf4bd..7934e843f0651 100644 --- a/py/mpstate.h +++ b/py/mpstate.h @@ -71,6 +71,12 @@ enum { // This structure contains dynamic configuration for the compiler. #if MICROPY_DYNAMIC_COMPILER typedef struct mp_dynamic_compiler_t { + // This is used to let mpy-cross pass options to the emitter chosen with + // `native_arch`. The main use case for the time being is to give the + // RV32 emitter extended information about which extensions can be + // optionally used, in order to generate code that's better suited for the + // hardware platform the code will run on. + void *backend_options; uint8_t small_int_bits; // must be <= host small_int_bits uint8_t native_arch; uint8_t nlr_buf_num_regs; @@ -369,4 +375,7 @@ extern mp_state_thread_t *mp_thread_get_state(void); #define mp_thread_is_main_thread() (true) #endif +// CIRCUITPY-CHANGE: defined in main.c +bool vm_is_running(void); + #endif // MICROPY_INCLUDED_PY_MPSTATE_H diff --git a/py/mpz.c b/py/mpz.c index 7d8bc03ca8610..74fd175463600 100644 --- a/py/mpz.c +++ b/py/mpz.c @@ -1541,7 +1541,8 @@ mp_int_t mpz_hash(const mpz_t *z) { mp_uint_t val = 0; mpz_dig_t *d = z->dig + z->len; - while (d-- > z->dig) { + while (d > z->dig) { + d--; val = (val << DIG_SIZE) | *d; } @@ -1556,11 +1557,12 @@ bool mpz_as_int_checked(const mpz_t *i, mp_int_t *value) { mp_uint_t val = 0; mpz_dig_t *d = i->dig + i->len; - while (d-- > i->dig) { + while (d > i->dig) { if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> DIG_SIZE)) { // will overflow return false; } + d--; val = (val << DIG_SIZE) | *d; } @@ -1581,11 +1583,12 @@ bool mpz_as_uint_checked(const mpz_t *i, mp_uint_t *value) { mp_uint_t val = 0; mpz_dig_t *d = i->dig + i->len; - while (d-- > i->dig) { + while (d > i->dig) { if (val > (~(MP_OBJ_WORD_MSBIT_HIGH) >> (DIG_SIZE - 1))) { // will overflow return false; } + d--; val = (val << DIG_SIZE) | *d; } @@ -1646,7 +1649,8 @@ mp_float_t mpz_as_float(const mpz_t *i) { mp_float_t val = 0; mpz_dig_t *d = i->dig + i->len; - while (d-- > i->dig) { + while (d > i->dig) { + d--; val = val * DIG_BASE + *d; } @@ -1676,6 +1680,8 @@ size_t mpz_as_str_inpl(const mpz_t *i, unsigned int base, const char *prefix, ch size_t ilen = i->len; + int n_comma = (base == 10) ? 3 : 4; + char *s = str; if (ilen == 0) { if (prefix) { @@ -1721,7 +1727,7 @@ size_t mpz_as_str_inpl(const mpz_t *i, unsigned int base, const char *prefix, ch break; } } - if (!done && comma && (s - last_comma) == 3) { + if (!done && comma && (s - last_comma) == n_comma) { *s++ = comma; last_comma = s; } diff --git a/py/nativeglue.h b/py/nativeglue.h index 00ed9f3f4fcb7..01825ac60edb4 100644 --- a/py/nativeglue.h +++ b/py/nativeglue.h @@ -143,7 +143,7 @@ typedef struct _mp_fun_table_t { int (*printf_)(const mp_print_t *print, const char *fmt, ...); int (*vprintf_)(const mp_print_t *print, const char *fmt, va_list args); #if defined(__GNUC__) - NORETURN // Only certain compilers support no-return attributes in function pointer declarations + MP_NORETURN // Only certain compilers support no-return attributes in function pointer declarations #endif // CIRCUITPY-CHANGE: raise_msg_str instead of raise_msg void (*raise_msg_str)(const mp_obj_type_t *exc_type, const char *msg); diff --git a/py/nlr.c b/py/nlr.c index 7ab0c0955a294..de2a38ceff3e9 100644 --- a/py/nlr.c +++ b/py/nlr.c @@ -81,7 +81,7 @@ void nlr_call_jump_callbacks(nlr_buf_t *nlr) { } #if MICROPY_ENABLE_VM_ABORT -NORETURN void nlr_jump_abort(void) { +MP_NORETURN void nlr_jump_abort(void) { MP_STATE_THREAD(nlr_top) = MP_STATE_VM(nlr_abort); nlr_jump(NULL); } diff --git a/py/nlr.h b/py/nlr.h index 340627b7aa1f0..446e78c7ebbf7 100644 --- a/py/nlr.h +++ b/py/nlr.h @@ -233,18 +233,18 @@ unsigned int nlr_push(nlr_buf_t *); unsigned int nlr_push_tail(nlr_buf_t *top); void nlr_pop(void); -NORETURN void nlr_jump(void *val); +MP_NORETURN void nlr_jump(void *val); #if MICROPY_ENABLE_VM_ABORT #define nlr_set_abort(buf) MP_STATE_VM(nlr_abort) = buf #define nlr_get_abort() MP_STATE_VM(nlr_abort) -NORETURN void nlr_jump_abort(void); +MP_NORETURN void nlr_jump_abort(void); #endif // This must be implemented by a port. It's called by nlr_jump // if no nlr buf has been pushed. It must not return, but rather // should bail out with a fatal error. -NORETURN void nlr_jump_fail(void *val); +MP_NORETURN void nlr_jump_fail(void *val); // use nlr_raise instead of nlr_jump so that debugging is easier #ifndef MICROPY_DEBUG_NLR diff --git a/py/nlraarch64.c b/py/nlraarch64.c index d6d87ebc50db8..3318004b5e037 100644 --- a/py/nlraarch64.c +++ b/py/nlraarch64.c @@ -56,7 +56,7 @@ __asm( #endif ); -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) MP_STATIC_ASSERT(offsetof(nlr_buf_t, regs) == 16); // asm assumes it diff --git a/py/nlrmips.c b/py/nlrmips.c index cba52b16a266a..5c55db7e268ec 100644 --- a/py/nlrmips.c +++ b/py/nlrmips.c @@ -57,7 +57,7 @@ __asm( ".end nlr_push \n" ); -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm( "move $4, %0 \n" diff --git a/py/nlrpowerpc.c b/py/nlrpowerpc.c index 8a69fe1eeca6b..cf140400e68f5 100644 --- a/py/nlrpowerpc.c +++ b/py/nlrpowerpc.c @@ -78,7 +78,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm__ volatile ( @@ -167,7 +167,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm__ volatile ( diff --git a/py/nlrrv32.c b/py/nlrrv32.c index 9a12ede400daa..565a8629db5f0 100644 --- a/py/nlrrv32.c +++ b/py/nlrrv32.c @@ -50,7 +50,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { ); } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( "add x10, x0, %0 \n" // Load nlr_buf address. diff --git a/py/nlrrv64.c b/py/nlrrv64.c index e7ba79797b857..b7d1467b8f6f6 100644 --- a/py/nlrrv64.c +++ b/py/nlrrv64.c @@ -50,7 +50,7 @@ __attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { ); } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( "add x10, x0, %0 \n" // Load nlr_buf address. diff --git a/py/nlrthumb.c b/py/nlrthumb.c index 0aa1f053a2397..1791d8b49294e 100644 --- a/py/nlrthumb.c +++ b/py/nlrthumb.c @@ -36,8 +36,7 @@ // For reference, arm/thumb callee save regs are: // r4-r11, r13=sp -// CIRCUITPY-CHANGE: added returns_twice -__attribute__((naked, returns_twice)) unsigned int nlr_push(nlr_buf_t *nlr) { +__attribute__((naked)) unsigned int nlr_push(nlr_buf_t *nlr) { // If you get a linker error here, indicating that a relocation doesn't // fit, try the following (in that order): @@ -92,6 +91,10 @@ __attribute__((naked, returns_twice)) unsigned int nlr_push(nlr_buf_t *nlr) { "b nlr_push_tail \n" // do the rest in C #endif #endif + // CIRCUITPY-CHANGE: add input and clobbers to prevent smashing registers. + : // output operands + : "r" (nlr) // input operands + : "r1", "r2", "r3" // clobbers ); #if !defined(__clang__) && defined(__GNUC__) && (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8)) @@ -101,7 +104,7 @@ __attribute__((naked, returns_twice)) unsigned int nlr_push(nlr_buf_t *nlr) { #endif } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( @@ -141,7 +144,8 @@ NORETURN void nlr_jump(void *val) { "bx lr \n" // return : // output operands : "r" (top) // input operands - : "memory" // clobbered registers + // CIRCUITPY-CHANGE: better comment + : "memory" // clobbers ); MP_UNREACHABLE diff --git a/py/nlrx64.c b/py/nlrx64.c index d1ad91ff7d718..51224729fc90d 100644 --- a/py/nlrx64.c +++ b/py/nlrx64.c @@ -100,7 +100,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { #endif } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( diff --git a/py/nlrx86.c b/py/nlrx86.c index 085e30d2034a1..26bf0dc6ccb85 100644 --- a/py/nlrx86.c +++ b/py/nlrx86.c @@ -78,7 +78,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { #endif } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( diff --git a/py/nlrxtensa.c b/py/nlrxtensa.c index ff7af6edeef98..2d1bf35e381a3 100644 --- a/py/nlrxtensa.c +++ b/py/nlrxtensa.c @@ -55,7 +55,7 @@ unsigned int nlr_push(nlr_buf_t *nlr) { return 0; // needed to silence compiler warning } -NORETURN void nlr_jump(void *val) { +MP_NORETURN void nlr_jump(void *val) { MP_NLR_JUMP_HEAD(val, top) __asm volatile ( diff --git a/py/obj.c b/py/obj.c index 29ae76557f8b5..ba3e6a9a9964d 100644 --- a/py/obj.c +++ b/py/obj.c @@ -157,7 +157,7 @@ void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t if (MP_OBJ_TYPE_HAS_SLOT(type, print)) { MP_OBJ_TYPE_GET_SLOT(type, print)((mp_print_t *)print, o_in, kind); } else { - mp_printf(print, "<%q>", type->name); + mp_printf(print, "<%q>", (qstr)type->name); } } @@ -417,6 +417,36 @@ mp_int_t mp_obj_get_int(mp_const_obj_t arg) { return val; } +#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_NONE +mp_uint_t mp_obj_get_uint(mp_const_obj_t arg) { + if (!mp_obj_is_exact_type(arg, &mp_type_int)) { + mp_obj_t as_int = mp_unary_op(MP_UNARY_OP_INT_MAYBE, (mp_obj_t)arg); + if (as_int == MP_OBJ_NULL) { + mp_raise_TypeError_int_conversion(arg); + } + arg = as_int; + } + return mp_obj_int_get_uint_checked(arg); +} + +long long mp_obj_get_ll(mp_const_obj_t arg) { + if (!mp_obj_is_exact_type(arg, &mp_type_int)) { + mp_obj_t as_int = mp_unary_op(MP_UNARY_OP_INT_MAYBE, (mp_obj_t)arg); + if (as_int == MP_OBJ_NULL) { + mp_raise_TypeError_int_conversion(arg); + } + arg = as_int; + } + if (mp_obj_is_small_int(arg)) { + return MP_OBJ_SMALL_INT_VALUE(arg); + } else { + long long res; + mp_obj_int_to_bytes_impl((mp_obj_t)arg, MP_ENDIANNESS_BIG, sizeof(res), (byte *)&res); + return res; + } +} +#endif + mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg) { if (mp_obj_is_int(arg)) { return mp_obj_int_get_truncated(arg); diff --git a/py/obj.h b/py/obj.h index 38db17cba449b..eb7143bd43f62 100644 --- a/py/obj.h +++ b/py/obj.h @@ -184,13 +184,15 @@ static inline bool mp_obj_is_small_int(mp_const_obj_t o) { #define MP_OBJ_NEW_SMALL_INT(small_int) ((mp_obj_t)((((mp_uint_t)(small_int)) << 1) | 1)) #if MICROPY_PY_BUILTINS_FLOAT -#define MP_OBJ_NEW_CONST_FLOAT(f) MP_ROM_PTR((mp_obj_t)((((((uint64_t)f) & ~3) | 2) + 0x80800000) & 0xffffffff)) +#include +// note: MP_OBJ_NEW_CONST_FLOAT should be a MP_ROM_PTR but that macro isn't available yet +#define MP_OBJ_NEW_CONST_FLOAT(f) ((mp_obj_t)((((((uint64_t)f) & ~3) | 2) + 0x80800000) & 0xffffffff)) #define mp_const_float_e MP_OBJ_NEW_CONST_FLOAT(0x402df854) #define mp_const_float_pi MP_OBJ_NEW_CONST_FLOAT(0x40490fdb) +#define mp_const_float_nan MP_OBJ_NEW_CONST_FLOAT(0x7fc00000) #if MICROPY_PY_MATH_CONSTANTS #define mp_const_float_tau MP_OBJ_NEW_CONST_FLOAT(0x40c90fdb) #define mp_const_float_inf MP_OBJ_NEW_CONST_FLOAT(0x7f800000) -#define mp_const_float_nan MP_OBJ_NEW_CONST_FLOAT(0xffc00000) #endif static inline bool mp_obj_is_float(mp_const_obj_t o) { @@ -204,9 +206,17 @@ static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) { mp_float_t f; mp_uint_t u; } num = {.u = ((mp_uint_t)o - 0x80800000u) & ~3u}; + // Rather than always truncating toward zero, which creates a strong + // bias, copy the two previous bits to fill in the two missing bits. + // This appears to be a pretty good heuristic. + num.u |= (num.u >> 2) & 3u; return num.f; } static inline mp_obj_t mp_obj_new_float(mp_float_t f) { + if (isnan(f)) { + // prevent creation of bad nanboxed pointers via array.array or struct + return mp_const_float_nan; + } union { mp_float_t f; mp_uint_t u; @@ -257,8 +267,10 @@ static inline bool mp_obj_is_immediate_obj(mp_const_obj_t o) { #error MICROPY_OBJ_REPR_D requires MICROPY_FLOAT_IMPL_DOUBLE #endif +#include #define mp_const_float_e {((mp_obj_t)((uint64_t)0x4005bf0a8b145769 + 0x8004000000000000))} #define mp_const_float_pi {((mp_obj_t)((uint64_t)0x400921fb54442d18 + 0x8004000000000000))} +#define mp_const_float_nan {((mp_obj_t)((uint64_t)0x7ff8000000000000 + 0x8004000000000000))} #if MICROPY_PY_MATH_CONSTANTS #define mp_const_float_tau {((mp_obj_t)((uint64_t)0x401921fb54442d18 + 0x8004000000000000))} #define mp_const_float_inf {((mp_obj_t)((uint64_t)0x7ff0000000000000 + 0x8004000000000000))} @@ -276,6 +288,13 @@ static inline mp_float_t mp_obj_float_get(mp_const_obj_t o) { return num.f; } static inline mp_obj_t mp_obj_new_float(mp_float_t f) { + if (isnan(f)) { + // prevent creation of bad nanboxed pointers via array.array or struct + struct { + uint64_t r; + } num = mp_const_float_nan; + return num.r; + } union { mp_float_t f; uint64_t r; @@ -371,25 +390,25 @@ typedef struct _mp_rom_obj_t { mp_const_obj_t o; } mp_rom_obj_t; #define MP_DEFINE_CONST_FUN_OBJ_0(obj_name, fun_name) \ const mp_obj_fun_builtin_fixed_t obj_name = \ - {{&mp_type_fun_builtin_0}, .fun._0 = fun_name} + {.base = {.type = &mp_type_fun_builtin_0}, .fun = {._0 = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_1(obj_name, fun_name) \ const mp_obj_fun_builtin_fixed_t obj_name = \ - {{&mp_type_fun_builtin_1}, .fun._1 = fun_name} + {.base = {.type = &mp_type_fun_builtin_1}, .fun = {._1 = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_2(obj_name, fun_name) \ const mp_obj_fun_builtin_fixed_t obj_name = \ - {{&mp_type_fun_builtin_2}, .fun._2 = fun_name} + {.base = {.type = &mp_type_fun_builtin_2}, .fun = {._2 = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_3(obj_name, fun_name) \ const mp_obj_fun_builtin_fixed_t obj_name = \ - {{&mp_type_fun_builtin_3}, .fun._3 = fun_name} + {.base = {.type = &mp_type_fun_builtin_3}, .fun = {._3 = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_VAR(obj_name, n_args_min, fun_name) \ const mp_obj_fun_builtin_var_t obj_name = \ - {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, false), .fun.var = fun_name} + {.base = {.type = &mp_type_fun_builtin_var}, .sig = MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, false), .fun = {.var = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(obj_name, n_args_min, n_args_max, fun_name) \ const mp_obj_fun_builtin_var_t obj_name = \ - {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, false), .fun.var = fun_name} + {.base = {.type = &mp_type_fun_builtin_var}, .sig = MP_OBJ_FUN_MAKE_SIG(n_args_min, n_args_max, false), .fun = {.var = fun_name}} #define MP_DEFINE_CONST_FUN_OBJ_KW(obj_name, n_args_min, fun_name) \ const mp_obj_fun_builtin_var_t obj_name = \ - {{&mp_type_fun_builtin_var}, MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, true), .fun.kw = fun_name} + {.base = {.type = &mp_type_fun_builtin_var}, .sig = MP_OBJ_FUN_MAKE_SIG(n_args_min, MP_OBJ_FUN_ARGS_MAX, true), .fun = {.kw = fun_name}} // CIRCUITPY-CHANGE #define MP_DEFINE_CONST_PROP_GET(obj_name, fun_name) \ @@ -516,9 +535,7 @@ static inline bool mp_map_slot_is_filled(const mp_map_t *map, size_t pos) { void mp_map_init(mp_map_t *map, size_t n); void mp_map_init_fixed_table(mp_map_t *map, size_t n, const mp_obj_t *table); -mp_map_t *mp_map_new(size_t n); void mp_map_deinit(mp_map_t *map); -void mp_map_free(mp_map_t *map); mp_map_elem_t *mp_map_lookup(mp_map_t *map, mp_obj_t index, mp_map_lookup_kind_t lookup_kind); void mp_map_clear(mp_map_t *map); void mp_map_dump(mp_map_t *map); @@ -555,6 +572,10 @@ typedef mp_obj_t (*mp_fun_var_t)(size_t n, const mp_obj_t *); typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); // Flags for type behaviour (mp_obj_type_t.flags) +// If MP_TYPE_FLAG_IS_SUBCLASSED is set, then subclasses of this class have been created. +// Mutations to this class that would require updating all subclasses must be rejected. +// If MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS is set, then attribute lookups involving this +// class need to additionally check for special accessor methods, such as from descriptors. // If MP_TYPE_FLAG_EQ_NOT_REFLEXIVE is clear then __eq__ is reflexive (A==A returns True). // If MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE is clear then the type can't be equal to an // instance of any different class that also clears this flag. If this flag is set @@ -572,6 +593,8 @@ typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); // If MP_TYPE_FLAG_ITER_IS_STREAM is set then the type implicitly gets a "return self" // getiter, and mp_stream_unbuffered_iter for iternext. // If MP_TYPE_FLAG_INSTANCE_TYPE is set then this is an instance type (i.e. defined in Python). +// If MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE is set then the "subscr" slot allows a stack +// allocated slice to be passed in (no references to it will be retained after the call). #define MP_TYPE_FLAG_NONE (0x0000) #define MP_TYPE_FLAG_IS_SUBCLASSED (0x0001) #define MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS (0x0002) @@ -585,8 +608,9 @@ typedef mp_obj_t (*mp_fun_kw_t)(size_t n, const mp_obj_t *, mp_map_t *); #define MP_TYPE_FLAG_ITER_IS_CUSTOM (0x0100) #define MP_TYPE_FLAG_ITER_IS_STREAM (MP_TYPE_FLAG_ITER_IS_ITERNEXT | MP_TYPE_FLAG_ITER_IS_CUSTOM) #define MP_TYPE_FLAG_INSTANCE_TYPE (0x0200) +#define MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE (0x0400) // CIRCUITPY-CHANGE: check for valid types in json dumps -#define MP_TYPE_FLAG_PRINT_JSON (0x0400) +#define MP_TYPE_FLAG_PRINT_JSON (0x0800) typedef enum { PRINT_STR = 0, @@ -827,7 +851,7 @@ typedef struct _mp_obj_full_type_t { #define MP_DEFINE_CONST_OBJ_TYPE_NARGS(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, N, ...) MP_DEFINE_CONST_OBJ_TYPE_NARGS_##N // This macros is used to define a object type in ROM. -// Invoke as MP_DEFINE_CONST_OBJ_TYPE(_typename, _name, _flags, _make_new [, slot, func]*) +// Invoke as MP_DEFINE_CONST_OBJ_TYPE(_typename, _name, _flags, [, slot, func]*) // It uses the number of arguments to select which MP_DEFINE_CONST_OBJ_TYPE_* // macro to use based on the number of arguments. It works by shifting the // numeric values 12, 11, ... 0 by the number of arguments, such that the @@ -847,7 +871,7 @@ extern const mp_obj_type_t mp_type_bytearray; extern const mp_obj_type_t mp_type_memoryview; extern const mp_obj_type_t mp_type_float; extern const mp_obj_type_t mp_type_complex; -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: add traceback support extern const mp_obj_type_t mp_type_traceback; extern const mp_obj_type_t mp_type_tuple; extern const mp_obj_type_t mp_type_list; @@ -909,7 +933,7 @@ extern const mp_obj_type_t mp_type_ImportError; extern const mp_obj_type_t mp_type_IndentationError; extern const mp_obj_type_t mp_type_IndexError; extern const mp_obj_type_t mp_type_KeyboardInterrupt; -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: add ReloadException extern const mp_obj_type_t mp_type_ReloadException; extern const mp_obj_type_t mp_type_KeyError; extern const mp_obj_type_t mp_type_LookupError; @@ -917,9 +941,9 @@ extern const mp_obj_type_t mp_type_MemoryError; extern const mp_obj_type_t mp_type_NameError; extern const mp_obj_type_t mp_type_NotImplementedError; extern const mp_obj_type_t mp_type_OSError; -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: add ConnectionError extern const mp_obj_type_t mp_type_ConnectionError; -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: add BrokenPipeError extern const mp_obj_type_t mp_type_BrokenPipeError; extern const mp_obj_type_t mp_type_OverflowError; extern const mp_obj_type_t mp_type_RuntimeError; @@ -927,6 +951,7 @@ extern const mp_obj_type_t mp_type_StopAsyncIteration; extern const mp_obj_type_t mp_type_StopIteration; extern const mp_obj_type_t mp_type_SyntaxError; extern const mp_obj_type_t mp_type_SystemExit; +// CIRCUITPY-CHANGE: add TimeoutError extern const mp_obj_type_t mp_type_TimeoutError; extern const mp_obj_type_t mp_type_TypeError; extern const mp_obj_type_t mp_type_UnicodeError; @@ -991,11 +1016,11 @@ void *mp_obj_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); // Object allocation macros for allocating objects that have a finaliser. #if MICROPY_ENABLE_FINALISER #define mp_obj_malloc_with_finaliser(struct_type, obj_type) ((struct_type *)mp_obj_malloc_with_finaliser_helper(sizeof(struct_type), obj_type)) -#define mp_obj_malloc_var_with_finaliser(struct_type, var_type, var_num, obj_type) ((struct_type *)mp_obj_malloc_with_finaliser_helper(sizeof(struct_type) + sizeof(var_type) * (var_num), obj_type)) +#define mp_obj_malloc_var_with_finaliser(struct_type, var_field, var_type, var_num, obj_type) ((struct_type *)mp_obj_malloc_with_finaliser_helper(offsetof(struct_type, var_field) + sizeof(var_type) * (var_num), obj_type)) void *mp_obj_malloc_with_finaliser_helper(size_t num_bytes, const mp_obj_type_t *type); #else #define mp_obj_malloc_with_finaliser(struct_type, obj_type) mp_obj_malloc(struct_type, obj_type) -#define mp_obj_malloc_var_with_finaliser(struct_type, var_type, var_num, obj_type) mp_obj_malloc_var(struct_type, var_type, var_num, obj_type) +#define mp_obj_malloc_var_with_finaliser(struct_type, var_field, var_type, var_num, obj_type) mp_obj_malloc_var(struct_type, var_field, var_type, var_num, obj_type) #endif // These macros are derived from more primitive ones and are used to @@ -1032,7 +1057,6 @@ bool mp_obj_is_dict_or_ordereddict(mp_obj_t o); // type check is done on iter method to allow tuple, namedtuple, attrtuple #define mp_obj_is_tuple_compatible(o) (MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(o), iter) == mp_obj_tuple_getiter) -mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); static inline mp_obj_t mp_obj_new_bool(mp_int_t x) { return x ? mp_const_true : mp_const_false; } @@ -1053,10 +1077,10 @@ mp_obj_t mp_obj_new_str_from_utf8_vstr(vstr_t *vstr); // input data must be vali #endif mp_obj_t mp_obj_new_bytes_from_vstr(vstr_t *vstr); mp_obj_t mp_obj_new_bytes(const byte *data, size_t len); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine mp_obj_t mp_obj_new_bytes_of_zeros(size_t len); mp_obj_t mp_obj_new_bytearray(size_t n, const void *items); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine mp_obj_t mp_obj_new_bytearray_of_zeros(size_t n); mp_obj_t mp_obj_new_bytearray_by_ref(size_t n, void *items); #if MICROPY_PY_BUILTINS_FLOAT @@ -1092,7 +1116,7 @@ mp_obj_t mp_obj_new_memoryview(byte typecode, size_t nitems, void *items); const mp_obj_type_t *mp_obj_get_type(mp_const_obj_t o_in); const char *mp_obj_get_type_str(mp_const_obj_t o_in); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine #define mp_obj_get_type_qstr(o_in) (mp_obj_get_type((o_in))->name) bool mp_obj_is_subclass_fast(mp_const_obj_t object, mp_const_obj_t classinfo); // arguments should be type objects mp_obj_t mp_obj_cast_to_native_base(mp_obj_t self_in, mp_const_obj_t native_type); @@ -1100,7 +1124,7 @@ mp_obj_t mp_obj_cast_to_native_base(mp_obj_t self_in, mp_const_obj_t native_type void mp_obj_print_helper(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind); void mp_obj_print(mp_obj_t o, mp_print_kind_t kind); void mp_obj_print_exception(const mp_print_t *print, mp_obj_t exc); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine void mp_obj_print_exception_with_limit(const mp_print_t *print, mp_obj_t exc, mp_int_t limit); bool mp_obj_is_true(mp_obj_t arg); @@ -1114,6 +1138,8 @@ static inline bool mp_obj_is_integer(mp_const_obj_t o) { } mp_int_t mp_obj_get_int(mp_const_obj_t arg); +mp_uint_t mp_obj_get_uint(mp_const_obj_t arg); +long long mp_obj_get_ll(mp_const_obj_t arg); mp_int_t mp_obj_get_int_truncated(mp_const_obj_t arg); bool mp_obj_get_int_maybe(mp_const_obj_t arg, mp_int_t *value); #if MICROPY_PY_BUILTINS_FLOAT @@ -1163,7 +1189,7 @@ bool mp_obj_exception_match(mp_obj_t exc, mp_const_obj_t exc_type); void mp_obj_exception_clear_traceback(mp_obj_t self_in); void mp_obj_exception_add_traceback(mp_obj_t self_in, qstr file, size_t line, qstr block); void mp_obj_exception_get_traceback(mp_obj_t self_in, size_t *n, size_t **values); -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: new routine mp_obj_t mp_obj_exception_get_traceback_obj(mp_obj_t self_in); mp_obj_t mp_obj_exception_get_value(mp_obj_t self_in); mp_obj_t mp_obj_exception_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args); @@ -1240,7 +1266,7 @@ void mp_obj_tuple_del(mp_obj_t self_in); mp_int_t mp_obj_tuple_hash(mp_obj_t self_in); // list -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: public routine mp_obj_t mp_obj_list_clear(mp_obj_t self_in); mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg); mp_obj_t mp_obj_list_remove(mp_obj_t self_in, mp_obj_t value); @@ -1336,7 +1362,7 @@ typedef struct _mp_rom_obj_static_class_method_t { } mp_rom_obj_static_class_method_t; // property -// CIRCUITPY-CHANGE +// CIRCUITPY-CHANGE: extra args const mp_obj_t *mp_obj_property_get(mp_obj_t self_in, size_t *n_proxy); // sequence helpers diff --git a/py/objarray.c b/py/objarray.c index 0be1947167d6e..1e259a20ac8cb 100644 --- a/py/objarray.c +++ b/py/objarray.c @@ -126,6 +126,19 @@ static mp_obj_array_t *array_new(char typecode, size_t n) { #endif #if MICROPY_PY_BUILTINS_BYTEARRAY || MICROPY_PY_ARRAY +static void array_extend_impl(mp_obj_array_t *array, mp_obj_t arg, char typecode, size_t len) { + mp_obj_t iterable = mp_getiter(arg, NULL); + mp_obj_t item; + size_t i = 0; + while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { + if (len == 0) { + array_append(MP_OBJ_FROM_PTR(array), item); + } else { + mp_binary_set_val_array(typecode, array->items, i++, item); + } + } +} + static mp_obj_t array_construct(char typecode, mp_obj_t initializer) { // bytearrays can be raw-initialised from anything with the buffer protocol // other arrays can only be raw-initialised from bytes and bytearray objects @@ -158,18 +171,7 @@ static mp_obj_t array_construct(char typecode, mp_obj_t initializer) { } mp_obj_array_t *array = array_new(typecode, len); - - mp_obj_t iterable = mp_getiter(initializer, NULL); - mp_obj_t item; - size_t i = 0; - while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - if (len == 0) { - array_append(MP_OBJ_FROM_PTR(array), item); - } else { - mp_binary_set_val_array(typecode, array->items, i++, item); - } - } - + array_extend_impl(array, initializer, typecode, len); return MP_OBJ_FROM_PTR(array); } #endif @@ -298,7 +300,7 @@ static void memoryview_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { mp_obj_array_t *self = MP_OBJ_TO_PTR(self_in); dest[0] = MP_OBJ_NEW_SMALL_INT(mp_binary_get_size('@', self->typecode & TYPECODE_MASK, NULL)); } - // CIRCUITPY-CHANGE: prevent warning + // CIRCUITPY-CHANGE: add MICROPY_CPYTHON_COMPAT #if MICROPY_PY_BUILTINS_BYTES_HEX || MICROPY_CPYTHON_COMPAT else { // Need to forward to locals dict. @@ -493,39 +495,42 @@ static mp_obj_t array_extend(mp_obj_t self_in, mp_obj_t arg_in) { // allow to extend by anything that has the buffer protocol (extension to CPython) mp_buffer_info_t arg_bufinfo; - // CIRCUITPY-CHANGE: allow appending an iterable - if (mp_get_buffer(arg_in, &arg_bufinfo, MP_BUFFER_READ)) { - size_t sz = mp_binary_get_size('@', self->typecode, NULL); - - // convert byte count to element count - size_t len = arg_bufinfo.len / sz; - - // make sure we have enough room to extend - // TODO: alloc policy; at the moment we go conservative - if (self->free < len) { - self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz); - self->free = 0; - } else { - self->free -= len; - } + if (!mp_get_buffer(arg_in, &arg_bufinfo, MP_BUFFER_READ)) { + array_extend_impl(self, arg_in, 0, 0); + return mp_const_none; + } - // extend - mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte); - self->len += len; - } else { - // Otherwise argument must be an iterable of items to append - mp_obj_t iterable = mp_getiter(arg_in, NULL); - mp_obj_t item; - while ((item = mp_iternext(iterable)) != MP_OBJ_STOP_ITERATION) { - array_append(self_in, item); + size_t sz = mp_binary_get_size('@', self->typecode, NULL); + + // convert byte count to element count + size_t len = arg_bufinfo.len / sz; + + // make sure we have enough room to extend + // TODO: alloc policy; at the moment we go conservative + if (self->free < len) { + self->items = m_renew(byte, self->items, (self->len + self->free) * sz, (self->len + len) * sz); + self->free = 0; + + if (self_in == arg_in) { + // Get arg_bufinfo again in case self->items has moved + // + // (Note not possible to handle case that arg_in is a memoryview into self) + mp_get_buffer_raise(arg_in, &arg_bufinfo, MP_BUFFER_READ); } + } else { + self->free -= len; } + + // extend + mp_seq_copy((byte *)self->items + self->len * sz, arg_bufinfo.buf, len * sz, byte); + self->len += len; + return mp_const_none; } MP_DEFINE_CONST_FUN_OBJ_2(mp_obj_array_extend_obj, array_extend); #endif -// CIRCUITPY-CHANGE: buffer_finder used belo +// CIRCUITPY-CHANGE: buffer_finder used below #if MICROPY_PY_BUILTINS_BYTEARRAY && MICROPY_CPYTHON_COMPAT static mp_obj_t buffer_finder(size_t n_args, const mp_obj_t *args, int direction, bool is_index) { mp_check_self(mp_obj_is_type(args[0], &mp_type_bytearray)); @@ -564,7 +569,6 @@ static mp_obj_t buffer_finder(size_t n_args, const mp_obj_t *args, int direction } return MP_OBJ_NEW_SMALL_INT(p - (const byte *)haystack_bufinfo.buf); } - // CIRCUITPY-CHANGE: provides find, rfind, index static mp_obj_t buffer_find(size_t n_args, const mp_obj_t *args) { return buffer_finder(n_args, args, 1, false); @@ -667,7 +671,7 @@ static mp_obj_t array_subscr(mp_obj_t self_in, mp_obj_t index_in, mp_obj_t value mp_seq_replace_slice_no_grow(dest_items, o->len, slice.start, slice.stop, src_items + src_offs, src_len, item_sz); // CIRCUITPY-CHANGE - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES // Clear "freed" elements at the end of list // TODO: This is actually only needed for typecode=='O' mp_seq_clear(dest_items, o->len + len_adj, o->len, item_sz); @@ -803,7 +807,7 @@ MP_DEFINE_CONST_OBJ_TYPE( MP_DEFINE_CONST_OBJ_TYPE( mp_type_bytearray, MP_QSTR_bytearray, - MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER | MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE, make_new, bytearray_make_new, print, array_print, iter, array_iterator_new, @@ -842,7 +846,7 @@ MP_DEFINE_CONST_DICT(memoryview_locals_dict, memoryview_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( mp_type_memoryview, MP_QSTR_memoryview, - MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER, + MP_TYPE_FLAG_EQ_CHECKS_OTHER_TYPE | MP_TYPE_FLAG_ITER_IS_GETITER | MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE, make_new, memoryview_make_new, iter, array_iterator_new, unary_op, array_unary_op, diff --git a/py/objboundmeth.c b/py/objboundmeth.c index e3503ff154a65..6df67f7bf9e0c 100644 --- a/py/objboundmeth.c +++ b/py/objboundmeth.c @@ -102,7 +102,11 @@ static mp_obj_t bound_meth_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_ } mp_obj_bound_meth_t *lhs = MP_OBJ_TO_PTR(lhs_in); mp_obj_bound_meth_t *rhs = MP_OBJ_TO_PTR(rhs_in); + #if MICROPY_PY_BOUND_METHOD_FULL_EQUALITY_CHECK + return mp_obj_new_bool(mp_obj_equal(lhs->self, rhs->self) && mp_obj_equal(lhs->meth, rhs->meth)); + #else return mp_obj_new_bool(lhs->self == rhs->self && lhs->meth == rhs->meth); + #endif } #if MICROPY_PY_FUNCTION_ATTRS diff --git a/py/objcell.c b/py/objcell.c index 95966c7917cb7..5c030c7405a80 100644 --- a/py/objcell.c +++ b/py/objcell.c @@ -30,7 +30,7 @@ static void cell_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_cell_t *o = MP_OBJ_TO_PTR(o_in); - mp_printf(print, "obj); + mp_printf(print, "obj); if (o->obj == MP_OBJ_NULL) { mp_print_str(print, "(nil)"); } else { diff --git a/py/objcode.c b/py/objcode.c index 9b98a696798d4..09904f10f3683 100644 --- a/py/objcode.c +++ b/py/objcode.c @@ -69,6 +69,7 @@ static mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_ return consts; } +#if !MICROPY_PREVIEW_VERSION_2 static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { // const mp_bytecode_prelude_t *prelude = &rc->prelude; uint start = 0; @@ -106,6 +107,68 @@ static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) { m_del(byte, buffer, buffer_size); return o; } +#endif + +static mp_obj_t code_colines_iter(mp_obj_t); +static mp_obj_t code_colines_next(mp_obj_t); +typedef struct _mp_obj_colines_iter_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + const mp_raw_code_t *rc; + mp_uint_t bc; + mp_uint_t source_line; + const byte *ci; +} mp_obj_colines_iter_t; + +static mp_obj_t code_colines_iter(mp_obj_t self_in) { + mp_obj_code_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_colines_iter_t *iter = mp_obj_malloc(mp_obj_colines_iter_t, &mp_type_polymorph_iter); + iter->iternext = code_colines_next; + iter->rc = self->rc; + iter->bc = 0; + iter->source_line = 1; + iter->ci = self->rc->prelude.line_info; + return MP_OBJ_FROM_PTR(iter); +} +static MP_DEFINE_CONST_FUN_OBJ_1(code_colines_obj, code_colines_iter); + +static mp_obj_t code_colines_next(mp_obj_t iter_in) { + mp_obj_colines_iter_t *iter = MP_OBJ_TO_PTR(iter_in); + const byte *ci_end = iter->rc->prelude.line_info_top; + + mp_uint_t start = iter->bc; + mp_uint_t line_no = iter->source_line; + bool another = true; + + while (another && iter->ci < ci_end) { + another = false; + mp_code_lineinfo_t decoded = mp_bytecode_decode_lineinfo(&iter->ci); + iter->bc += decoded.bc_increment; + iter->source_line += decoded.line_increment; + + if (decoded.bc_increment == 0) { + line_no = iter->source_line; + another = true; + } else if (decoded.line_increment == 0) { + another = true; + } + } + + if (another) { + mp_uint_t prelude_size = (iter->rc->prelude.opcodes - (const byte *)iter->rc->fun_data); + mp_uint_t bc_end = iter->rc->fun_data_len - prelude_size; + if (iter->bc >= bc_end) { + return MP_OBJ_STOP_ITERATION; + } else { + iter->bc = bc_end; + } + } + + mp_uint_t end = iter->bc; + mp_obj_t next[3] = {MP_OBJ_NEW_SMALL_INT(start), MP_OBJ_NEW_SMALL_INT(end), MP_OBJ_NEW_SMALL_INT(line_no)}; + + return mp_obj_new_tuple(MP_ARRAY_SIZE(next), next); +} static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { if (dest[0] != MP_OBJ_NULL) { @@ -137,12 +200,18 @@ static void code_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest) { case MP_QSTR_co_names: dest[0] = MP_OBJ_FROM_PTR(o->dict_locals); break; + #if !MICROPY_PREVIEW_VERSION_2 case MP_QSTR_co_lnotab: if (!o->lnotab) { o->lnotab = raw_code_lnotab(rc); } dest[0] = o->lnotab; break; + #endif + case MP_QSTR_co_lines: + dest[0] = MP_OBJ_FROM_PTR(&code_colines_obj); + dest[1] = self_in; + break; } } @@ -168,7 +237,9 @@ mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t o->context = context; o->rc = rc; o->dict_locals = mp_locals_get(); // this is a wrong! how to do this properly? + #if !MICROPY_PREVIEW_VERSION_2 o->lnotab = MP_OBJ_NULL; + #endif return MP_OBJ_FROM_PTR(o); } diff --git a/py/objcode.h b/py/objcode.h index 8db9a34b6e1c9..7be15e23f5271 100644 --- a/py/objcode.h +++ b/py/objcode.h @@ -72,15 +72,16 @@ static inline const void *mp_code_get_proto_fun(mp_obj_code_t *self) { #include "py/emitglue.h" -#define MP_CODE_QSTR_MAP(context, idx) (context->constants.qstr_table[idx]) +#define MP_CODE_QSTR_MAP(context, idx) ((qstr)(context->constants.qstr_table[idx])) typedef struct _mp_obj_code_t { - // TODO this was 4 words mp_obj_base_t base; const mp_module_context_t *context; const mp_raw_code_t *rc; mp_obj_dict_t *dict_locals; + #if !MICROPY_PREVIEW_VERSION_2 mp_obj_t lnotab; + #endif } mp_obj_code_t; mp_obj_t mp_obj_new_code(const mp_module_context_t *context, const mp_raw_code_t *rc, bool result_required); diff --git a/py/objcomplex.c b/py/objcomplex.c index d7287efdc13dd..88f4444f09e4d 100644 --- a/py/objcomplex.c +++ b/py/objcomplex.c @@ -49,29 +49,18 @@ typedef struct _mp_obj_complex_t { static void complex_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_complex_t *o = MP_OBJ_TO_PTR(o_in); - #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - char buf[16]; - #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C - const int precision = 6; - #else - const int precision = 7; - #endif - #else - char buf[32]; - const int precision = 16; - #endif - if (o->real == 0) { - mp_format_float(o->imag, buf, sizeof(buf), 'g', precision, '\0'); - mp_printf(print, "%sj", buf); + const char *suffix; + int flags = 0; + if (o->real != 0) { + mp_print_str(print, "("); + mp_print_float(print, o->real, 'g', 0, '\0', -1, MP_FLOAT_REPR_PREC); + flags = PF_FLAG_SHOW_SIGN; + suffix = "j)"; } else { - mp_format_float(o->real, buf, sizeof(buf), 'g', precision, '\0'); - mp_printf(print, "(%s", buf); - if (o->imag >= 0 || isnan(o->imag)) { - mp_print_str(print, "+"); - } - mp_format_float(o->imag, buf, sizeof(buf), 'g', precision, '\0'); - mp_printf(print, "%sj)", buf); + suffix = "j"; } + mp_print_float(print, o->imag, 'g', flags, '\0', -1, MP_FLOAT_REPR_PREC); + mp_print_str(print, suffix); } static mp_obj_t complex_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/py/objdeque.c b/py/objdeque.c index 264c795801ba4..4e1ee82e9d4de 100644 --- a/py/objdeque.c +++ b/py/objdeque.c @@ -46,6 +46,11 @@ static mp_obj_t mp_obj_deque_extend(mp_obj_t self_in, mp_obj_t arg_in); static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque, mp_obj_iter_buf_t *iter_buf); #endif +// CIRCUITPY-CHANGE +static mp_obj_deque_t *native_deque(mp_obj_t self_in) { + return MP_OBJ_TO_PTR(mp_obj_cast_to_native_base(self_in, MP_OBJ_FROM_PTR(&mp_type_deque))); +} + static mp_obj_t deque_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 2, 3, false); @@ -79,7 +84,8 @@ static size_t deque_len(mp_obj_deque_t *self) { } static mp_obj_t deque_unary_op(mp_unary_op_t op, mp_obj_t self_in) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); switch (op) { case MP_UNARY_OP_BOOL: return mp_obj_new_bool(self->i_get != self->i_put); @@ -98,7 +104,8 @@ static mp_obj_t deque_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } static mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); size_t new_i_put = self->i_put + 1; if (new_i_put == self->alloc) { @@ -123,7 +130,8 @@ static mp_obj_t mp_obj_deque_append(mp_obj_t self_in, mp_obj_t arg) { static MP_DEFINE_CONST_FUN_OBJ_2(deque_append_obj, mp_obj_deque_append); static mp_obj_t mp_obj_deque_appendleft(mp_obj_t self_in, mp_obj_t arg) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); size_t new_i_get = self->i_get - 1; if (self->i_get == 0) { @@ -162,7 +170,8 @@ static mp_obj_t mp_obj_deque_extend(mp_obj_t self_in, mp_obj_t arg_in) { static MP_DEFINE_CONST_FUN_OBJ_2(deque_extend_obj, mp_obj_deque_extend); static mp_obj_t deque_popleft(mp_obj_t self_in) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); if (self->i_get == self->i_put) { mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty")); @@ -180,7 +189,8 @@ static mp_obj_t deque_popleft(mp_obj_t self_in) { static MP_DEFINE_CONST_FUN_OBJ_1(deque_popleft_obj, deque_popleft); static mp_obj_t deque_pop(mp_obj_t self_in) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); if (self->i_get == self->i_put) { mp_raise_msg(&mp_type_IndexError, MP_ERROR_TEXT("empty")); @@ -205,7 +215,8 @@ static mp_obj_t deque_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { // delete not supported, fall back to mp_obj_subscr() error message return MP_OBJ_NULL; } - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); size_t offset = mp_get_index(self->base.type, deque_len(self), index, false); size_t index_val = self->i_get + offset; @@ -226,7 +237,8 @@ static mp_obj_t deque_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { #if 0 static mp_obj_t deque_clear(mp_obj_t self_in) { - mp_obj_deque_t *self = MP_OBJ_TO_PTR(self_in); + // CIRCUITPY-CHANGE + mp_obj_deque_t *self = native_deque(self_in); self->i_get = self->i_put = 0; mp_seq_clear(self->items, 0, self->alloc, sizeof(*self->items)); return mp_const_none; @@ -286,7 +298,8 @@ typedef struct _mp_obj_deque_it_t { static mp_obj_t deque_it_iternext(mp_obj_t self_in) { mp_obj_deque_it_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_deque_t *deque = MP_OBJ_TO_PTR(self->deque); + // CIRCUITPY-CHANGE + mp_obj_deque_t *deque = native_deque(self->deque); if (self->cur != deque->i_put) { mp_obj_t o_out = deque->items[self->cur]; if (++self->cur == deque->alloc) { @@ -298,9 +311,10 @@ static mp_obj_t deque_it_iternext(mp_obj_t self_in) { } } -static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque, mp_obj_iter_buf_t *iter_buf) { - mp_obj_deque_t *deque_ = MP_OBJ_TO_PTR(deque); - size_t i_get = deque_->i_get; +static mp_obj_t mp_obj_new_deque_it(mp_obj_t deque_in, mp_obj_iter_buf_t *iter_buf) { + // CIRCUITPY-CHANGE + mp_obj_deque_t *deque = native_deque(deque_in); + size_t i_get = deque->i_get; assert(sizeof(mp_obj_deque_it_t) <= sizeof(mp_obj_iter_buf_t)); mp_obj_deque_it_t *o = (mp_obj_deque_it_t *)iter_buf; o->base.type = &mp_type_polymorph_iter; diff --git a/py/objdict.c b/py/objdict.c index 79a606f09707b..d459d1ae48647 100644 --- a/py/objdict.c +++ b/py/objdict.c @@ -101,7 +101,7 @@ static void dict_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_ #endif } if (MICROPY_PY_COLLECTIONS_ORDEREDDICT && self->base.type != &mp_type_dict && kind != PRINT_JSON) { - mp_printf(print, "%q(", self->base.type->name); + mp_printf(print, "%q(", (qstr)self->base.type->name); } mp_print_str(print, "{"); size_t cur = 0; @@ -616,7 +616,8 @@ static mp_obj_t dict_view_unary_op(mp_unary_op_t op, mp_obj_t o_in) { if (op == MP_UNARY_OP_HASH && o->kind == MP_DICT_VIEW_VALUES) { return MP_OBJ_NEW_SMALL_INT((mp_uint_t)o_in); } - return MP_OBJ_NULL; + // delegate all other ops to dict unary op handler + return dict_unary_op(op, o->dict); } static mp_obj_t dict_view_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { diff --git a/py/objfloat.c b/py/objfloat.c index 3610c2b85862d..ecc0f61a6abc9 100644 --- a/py/objfloat.c +++ b/py/objfloat.c @@ -34,6 +34,11 @@ #if MICROPY_PY_BUILTINS_FLOAT +// Workaround a bug in Windows SDK version 10.0.26100.0, where NAN is no longer constant. +#if defined(_MSC_VER) && !defined(_UCRT_NOISY_NAN) +#define _UCRT_NOISY_NAN +#endif + #include #include "py/formatfloat.h" @@ -51,13 +56,6 @@ #define M_PI (3.14159265358979323846) #endif -// Workaround a bug in recent MSVC where NAN is no longer constant. -// (By redefining back to the previous MSVC definition of NAN) -#if defined(_MSC_VER) && _MSC_VER >= 1942 -#undef NAN -#define NAN (-(float)(((float)(1e+300 * 1e+300)) * 0.0F)) -#endif - typedef struct _mp_obj_float_t { mp_obj_base_t base; mp_float_t value; @@ -116,23 +114,7 @@ mp_int_t mp_float_hash(mp_float_t src) { static void float_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_float_t o_val = mp_obj_float_get(o_in); - #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT - char buf[16]; - #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_C - const int precision = 6; - #else - const int precision = 7; - #endif - #else - char buf[32]; - const int precision = 16; - #endif - mp_format_float(o_val, buf, sizeof(buf), 'g', precision, '\0'); - mp_print_str(print, buf); - if (strchr(buf, '.') == NULL && strchr(buf, 'e') == NULL && strchr(buf, 'n') == NULL) { - // Python floats always have decimal point (unless inf or nan) - mp_print_str(print, ".0"); - } + mp_print_float(print, o_val, 'g', PF_FLAG_ALWAYS_DECIMAL, '\0', -1, MP_FLOAT_REPR_PREC); } static mp_obj_t float_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { @@ -324,6 +306,10 @@ mp_obj_t mp_obj_float_binary_op(mp_binary_op_t op, mp_float_t lhs_val, mp_obj_t lhs_val = MICROPY_FLOAT_CONST(1.0); break; } + if (isnan(rhs_val)) { + lhs_val = rhs_val; + break; + } #endif lhs_val = MICROPY_FLOAT_C_FUN(pow)(lhs_val, rhs_val); break; diff --git a/py/objfun.c b/py/objfun.c index e6a923d59e886..34565cf633632 100644 --- a/py/objfun.c +++ b/py/objfun.c @@ -184,7 +184,8 @@ static mp_obj_t fun_bc_make_new(const mp_obj_type_t *type, size_t n_args, size_t static void fun_bc_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_fun_bc_t *o = MP_OBJ_TO_PTR(o_in); - mp_printf(print, "", mp_obj_fun_get_name(o_in), o); + // CIRCUITPY-CHANGE: %p already prints "0x", so don't include it explicitly. + mp_printf(print, "", mp_obj_fun_get_name(o_in), o); } #endif diff --git a/py/objint.c b/py/objint.c index b12f09c9d38a7..cabbd7b8f0e12 100644 --- a/py/objint.c +++ b/py/objint.c @@ -117,9 +117,15 @@ static mp_fp_as_int_class_t mp_classify_fp_as_int(mp_float_t val) { } // 8 * sizeof(uintptr_t) counts the number of bits for a small int // TODO provide a way to configure this properly + #if MICROPY_OBJ_REPR == MICROPY_OBJ_REPR_B + if (e <= ((8 * sizeof(uintptr_t) + MP_FLOAT_EXP_BIAS - 4) << MP_FLOAT_EXP_SHIFT_I32)) { + return MP_FP_CLASS_FIT_SMALLINT; + } + #else if (e <= ((8 * sizeof(uintptr_t) + MP_FLOAT_EXP_BIAS - 3) << MP_FLOAT_EXP_SHIFT_I32)) { return MP_FP_CLASS_FIT_SMALLINT; } + #endif #if MICROPY_LONGINT_IMPL == MICROPY_LONGINT_IMPL_LONGLONG if (e <= (((sizeof(long long) * MP_BITS_PER_BYTE) + MP_FLOAT_EXP_BIAS - 2) << MP_FLOAT_EXP_SHIFT_I32)) { return MP_FP_CLASS_FIT_LONGINT; @@ -211,7 +217,7 @@ static const uint8_t log_base2_floor[] = { size_t mp_int_format_size(size_t num_bits, int base, const char *prefix, char comma) { assert(2 <= base && base <= 16); size_t num_digits = num_bits / log_base2_floor[base - 1] + 1; - size_t num_commas = comma ? num_digits / 3 : 0; + size_t num_commas = comma ? (base == 10 ? num_digits / 3 : num_digits / 4): 0; size_t prefix_len = prefix ? strlen(prefix) : 0; return num_digits + num_commas + prefix_len + 2; // +1 for sign, +1 for null byte } @@ -249,10 +255,11 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co char sign = '\0'; if (num < 0) { - num = -num; + num = -(fmt_uint_t)num; sign = '-'; } + int n_comma = (base == 10) ? 3 : 4; size_t needed_size = mp_int_format_size(sizeof(fmt_int_t) * 8, base, prefix, comma); if (needed_size > *buf_size) { *buf = m_new(char, needed_size); @@ -277,7 +284,7 @@ char *mp_obj_int_formatted(char **buf, size_t *buf_size, size_t *fmt_size, mp_co c += '0'; } *(--b) = c; - if (comma && num != 0 && b > str && (last_comma - b) == 3) { + if (comma && num != 0 && b > str && (last_comma - b) == n_comma) { *(--b) = comma; last_comma = b; } diff --git a/py/objint_longlong.c b/py/objint_longlong.c index 0fad693c7adcd..f24aa0cc18664 100644 --- a/py/objint_longlong.c +++ b/py/objint_longlong.c @@ -43,6 +43,10 @@ const mp_obj_int_t mp_sys_maxsize_obj = {{&mp_type_int}, MP_SSIZE_MAX}; #endif +static void raise_long_long_overflow(void) { + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("result overflows long long storage")); +} + // CIRCUITPY-CHANGE: bit_length mp_obj_t mp_obj_int_bit_length_impl(mp_obj_t self_in) { assert(mp_obj_is_type(self_in, &mp_type_int)); @@ -132,7 +136,6 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) { // small int if the value fits without truncation case MP_UNARY_OP_HASH: return MP_OBJ_NEW_SMALL_INT((mp_int_t)o->val); - case MP_UNARY_OP_POSITIVE: return o_in; case MP_UNARY_OP_NEGATIVE: @@ -159,6 +162,8 @@ mp_obj_t mp_obj_int_unary_op(mp_unary_op_t op, mp_obj_t o_in) { mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_in) { long long lhs_val; long long rhs_val; + bool overflow = false; + long long result; if (mp_obj_is_small_int(lhs_in)) { lhs_val = MP_OBJ_SMALL_INT_VALUE(lhs_in); @@ -171,21 +176,41 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i rhs_val = MP_OBJ_SMALL_INT_VALUE(rhs_in); } else if (mp_obj_is_exact_type(rhs_in, &mp_type_int)) { rhs_val = ((mp_obj_int_t *)rhs_in)->val; + #if MICROPY_PY_BUILTINS_FLOAT + } else if (mp_obj_is_float(rhs_in)) { + return mp_obj_float_binary_op(op, (mp_float_t)lhs_val, rhs_in); + #endif + #if MICROPY_PY_BUILTINS_COMPLEX + } else if (mp_obj_is_type(rhs_in, &mp_type_complex)) { + return mp_obj_complex_binary_op(op, (mp_float_t)lhs_val, 0, rhs_in); + #endif } else { // delegate to generic function to check for extra cases return mp_obj_int_binary_op_extra_cases(op, lhs_in, rhs_in); } + #if MICROPY_PY_BUILTINS_FLOAT + if (op == MP_BINARY_OP_TRUE_DIVIDE || op == MP_BINARY_OP_INPLACE_TRUE_DIVIDE) { + if (rhs_val == 0) { + goto zero_division; + } + return mp_obj_new_float((mp_float_t)lhs_val / (mp_float_t)rhs_val); + } + #endif + switch (op) { case MP_BINARY_OP_ADD: case MP_BINARY_OP_INPLACE_ADD: - return mp_obj_new_int_from_ll(lhs_val + rhs_val); + overflow = mp_add_ll_overflow(lhs_val, rhs_val, &result); + break; case MP_BINARY_OP_SUBTRACT: case MP_BINARY_OP_INPLACE_SUBTRACT: - return mp_obj_new_int_from_ll(lhs_val - rhs_val); + overflow = mp_sub_ll_overflow(lhs_val, rhs_val, &result); + break; case MP_BINARY_OP_MULTIPLY: case MP_BINARY_OP_INPLACE_MULTIPLY: - return mp_obj_new_int_from_ll(lhs_val * rhs_val); + overflow = mp_mul_ll_overflow(lhs_val, rhs_val, &result); + break; case MP_BINARY_OP_FLOOR_DIVIDE: case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: if (rhs_val == 0) { @@ -211,9 +236,21 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i case MP_BINARY_OP_LSHIFT: case MP_BINARY_OP_INPLACE_LSHIFT: - return mp_obj_new_int_from_ll(lhs_val << (int)rhs_val); + if (rhs_val < 0) { + // negative shift not allowed + mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); + } + overflow = rhs_val >= (sizeof(long long) * MP_BITS_PER_BYTE) + || lhs_val > (LLONG_MAX >> rhs_val) + || lhs_val < (LLONG_MIN >> rhs_val); + result = (unsigned long long)lhs_val << rhs_val; + break; case MP_BINARY_OP_RSHIFT: case MP_BINARY_OP_INPLACE_RSHIFT: + if ((int)rhs_val < 0) { + // negative shift not allowed + mp_raise_ValueError(MP_ERROR_TEXT("negative shift count")); + } return mp_obj_new_int_from_ll(lhs_val >> (int)rhs_val); case MP_BINARY_OP_POWER: @@ -225,18 +262,18 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i mp_raise_ValueError(MP_ERROR_TEXT("negative power with no float support")); #endif } - long long ans = 1; - while (rhs_val > 0) { + result = 1; + while (rhs_val > 0 && !overflow) { if (rhs_val & 1) { - ans *= lhs_val; + overflow = mp_mul_ll_overflow(result, lhs_val, &result); } - if (rhs_val == 1) { + if (rhs_val == 1 || overflow) { break; } rhs_val /= 2; - lhs_val *= lhs_val; + overflow = mp_mul_ll_overflow(lhs_val, lhs_val, &lhs_val); } - return mp_obj_new_int_from_ll(ans); + break; } case MP_BINARY_OP_LESS: @@ -254,8 +291,14 @@ mp_obj_t mp_obj_int_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i return MP_OBJ_NULL; // op not supported } + if (overflow) { + raise_long_long_overflow(); + } + + return mp_obj_new_int_from_ll(result); + zero_division: - mp_raise_ZeroDivisionError(); + mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("divide by zero")); } mp_obj_t mp_obj_new_int(mp_int_t value) { @@ -277,22 +320,12 @@ mp_obj_t mp_obj_new_int_from_ll(long long val) { } mp_obj_t mp_obj_new_int_from_ull(unsigned long long val) { - // TODO raise an exception if the unsigned long long won't fit if (val >> (sizeof(unsigned long long) * 8 - 1) != 0) { - mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("ulonglong too large")); + raise_long_long_overflow(); } return mp_obj_new_int_from_ll(val); } -mp_obj_t mp_obj_new_int_from_str_len(const char **str, size_t len, bool neg, unsigned int base) { - // TODO this does not honor the given length of the string, but it all cases it should anyway be null terminated - // TODO check overflow - char *endptr; - mp_obj_t result = mp_obj_new_int_from_ll(strtoll(*str, &endptr, base)); - *str = endptr; - return result; -} - mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in) { if (mp_obj_is_small_int(self_in)) { return MP_OBJ_SMALL_INT_VALUE(self_in); @@ -303,8 +336,33 @@ mp_int_t mp_obj_int_get_truncated(mp_const_obj_t self_in) { } mp_int_t mp_obj_int_get_checked(mp_const_obj_t self_in) { - // TODO: Check overflow - return mp_obj_int_get_truncated(self_in); + if (mp_obj_is_small_int(self_in)) { + return MP_OBJ_SMALL_INT_VALUE(self_in); + } else { + const mp_obj_int_t *self = self_in; + long long value = self->val; + mp_int_t truncated = (mp_int_t)value; + if ((long long)truncated == value) { + return truncated; + } + } + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("overflow converting long int to machine word")); +} + +mp_uint_t mp_obj_int_get_uint_checked(mp_const_obj_t self_in) { + if (mp_obj_is_small_int(self_in)) { + if (MP_OBJ_SMALL_INT_VALUE(self_in) >= 0) { + return MP_OBJ_SMALL_INT_VALUE(self_in); + } + } else { + const mp_obj_int_t *self = self_in; + long long value = self->val; + mp_uint_t truncated = (mp_uint_t)value; + if (value >= 0 && (long long)truncated == value) { + return truncated; + } + } + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("overflow converting long int to machine word")); } #if MICROPY_PY_BUILTINS_FLOAT diff --git a/py/objint_mpz.c b/py/objint_mpz.c index 111f53009fb1f..1bede811a4aaa 100644 --- a/py/objint_mpz.c +++ b/py/objint_mpz.c @@ -364,9 +364,10 @@ static mpz_t *mp_mpz_for_int(mp_obj_t arg, mpz_t *temp) { mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { if (!mp_obj_is_int(base) || !mp_obj_is_int(exponent) || !mp_obj_is_int(modulus)) { mp_raise_TypeError(MP_ERROR_TEXT("pow() with 3 arguments requires integers")); + } else if (modulus == MP_OBJ_NEW_SMALL_INT(0)) { + mp_raise_ValueError(MP_ERROR_TEXT("divide by zero")); } else { - mp_obj_t result = mp_obj_new_int_from_ull(0); // Use the _from_ull version as this forces an mpz int - mp_obj_int_t *res_p = (mp_obj_int_t *)MP_OBJ_TO_PTR(result); + mp_obj_int_t *res_p = mp_obj_int_new_mpz(); mpz_t l_temp, r_temp, m_temp; mpz_t *lhs = mp_mpz_for_int(base, &l_temp); @@ -389,7 +390,7 @@ mp_obj_t mp_obj_int_pow3(mp_obj_t base, mp_obj_t exponent, mp_obj_t modulus) { if (mod == &m_temp) { mpz_deinit(mod); } - return result; + return MP_OBJ_FROM_PTR(res_p); } } #endif diff --git a/py/objlist.c b/py/objlist.c index 3137e9fa53483..a29fc51b10472 100644 --- a/py/objlist.c +++ b/py/objlist.c @@ -118,7 +118,7 @@ static mp_obj_t list_unary_op(mp_unary_op_t op, mp_obj_t self_in) { } static mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: allow subclassing mp_obj_list_t *o = native_list(lhs); switch (op) { case MP_BINARY_OP_ADD: { @@ -142,7 +142,7 @@ static mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { if (n < 0) { n = 0; } - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: overflow check (PR#1279) size_t new_len = mp_seq_multiply_len(o->len, n); mp_obj_list_t *s = list_new(new_len); mp_seq_multiply(o->items, sizeof(*o->items), o->len, n, s->items); @@ -171,76 +171,66 @@ static mp_obj_t list_binary_op(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs) { } static mp_obj_t list_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { - // CIRCUITPY-CHANGE - mp_obj_list_t *self = native_list(self_in); - if (value == MP_OBJ_NULL) { - // delete - #if MICROPY_PY_BUILTINS_SLICE - if (mp_obj_is_type(index, &mp_type_slice)) { - mp_bound_slice_t slice; - if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { - mp_raise_NotImplementedError(NULL); + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + // CIRCUITPY-CHANGE: handle subclassing + mp_obj_list_t *self = native_list(self_in); + mp_bound_slice_t slice; + bool fast = mp_seq_get_fast_slice_indexes(self->len, index, &slice); + if (value == MP_OBJ_SENTINEL) { + // load + if (!fast) { + return mp_seq_extract_slice(self->items, &slice); } - - mp_int_t len_adj = slice.start - slice.stop; - assert(len_adj <= 0); - mp_seq_replace_slice_no_grow(self->items, self->len, slice.start, slice.stop, self->items /*NULL*/, 0, sizeof(*self->items)); + mp_obj_list_t *res = list_new(slice.stop - slice.start); + mp_seq_copy(res->items, self->items + slice.start, res->len, mp_obj_t); + return MP_OBJ_FROM_PTR(res); + } + // assign/delete + if (value == MP_OBJ_NULL) { + // delete is equivalent to slice assignment of an empty sequence + value = mp_const_empty_tuple; + } + if (!fast) { + mp_raise_NotImplementedError(NULL); + } + size_t value_len; + mp_obj_t *value_items; + mp_obj_get_array(value, &value_len, &value_items); + mp_int_t len_adj = value_len - (slice.stop - slice.start); + if (len_adj > 0) { + if (self->len + len_adj > self->alloc) { + // TODO: Might optimize memory copies here by checking if block can + // be grown inplace or not + self->items = m_renew(mp_obj_t, self->items, self->alloc, self->len + len_adj); + self->alloc = self->len + len_adj; + } + mp_seq_replace_slice_grow_inplace(self->items, self->len, + slice.start, slice.stop, value_items, value_len, len_adj, sizeof(*self->items)); + } else { + mp_seq_replace_slice_no_grow(self->items, self->len, + slice.start, slice.stop, value_items, value_len, sizeof(*self->items)); // Clear "freed" elements at the end of list mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items)); - self->len += len_adj; - return mp_const_none; + // TODO: apply allocation policy re: alloc_size } - #endif - // CIRCUITPY-CHANGE - mp_obj_t args[2] = {MP_OBJ_FROM_PTR(self), index}; + self->len += len_adj; + return mp_const_none; + } + #endif + if (value == MP_OBJ_NULL) { + // delete + // CIRCUITPY-CHANGE: handle subclassing + mp_obj_t args[2] = {MP_OBJ_FROM_PTR(self_in), index}; list_pop(2, args); return mp_const_none; } else if (value == MP_OBJ_SENTINEL) { // load - #if MICROPY_PY_BUILTINS_SLICE - if (mp_obj_is_type(index, &mp_type_slice)) { - mp_bound_slice_t slice; - if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice)) { - return mp_seq_extract_slice(self->items, &slice); - } - mp_obj_list_t *res = list_new(slice.stop - slice.start); - mp_seq_copy(res->items, self->items + slice.start, res->len, mp_obj_t); - return MP_OBJ_FROM_PTR(res); - } - #endif + // CIRCUITPY-CHANGE: handle subclassing + mp_obj_list_t *self = native_list(self_in); size_t index_val = mp_get_index(self->base.type, self->len, index, false); return self->items[index_val]; } else { - #if MICROPY_PY_BUILTINS_SLICE - if (mp_obj_is_type(index, &mp_type_slice)) { - size_t value_len; - mp_obj_t *value_items; - mp_obj_get_array(value, &value_len, &value_items); - mp_bound_slice_t slice_out; - if (!mp_seq_get_fast_slice_indexes(self->len, index, &slice_out)) { - mp_raise_NotImplementedError(NULL); - } - mp_int_t len_adj = value_len - (slice_out.stop - slice_out.start); - if (len_adj > 0) { - if (self->len + len_adj > self->alloc) { - // TODO: Might optimize memory copies here by checking if block can - // be grown inplace or not - self->items = m_renew(mp_obj_t, self->items, self->alloc, self->len + len_adj); - self->alloc = self->len + len_adj; - } - mp_seq_replace_slice_grow_inplace(self->items, self->len, - slice_out.start, slice_out.stop, value_items, value_len, len_adj, sizeof(*self->items)); - } else { - mp_seq_replace_slice_no_grow(self->items, self->len, - slice_out.start, slice_out.stop, value_items, value_len, sizeof(*self->items)); - // Clear "freed" elements at the end of list - mp_seq_clear(self->items, self->len + len_adj, self->len, sizeof(*self->items)); - // TODO: apply allocation policy re: alloc_size - } - self->len += len_adj; - return mp_const_none; - } - #endif mp_obj_list_store(self_in, index, value); return mp_const_none; } @@ -252,7 +242,7 @@ static mp_obj_t list_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); if (self->len >= self->alloc) { self->items = m_renew(mp_obj_t, self->items, self->alloc, self->alloc * 2); @@ -266,7 +256,7 @@ mp_obj_t mp_obj_list_append(mp_obj_t self_in, mp_obj_t arg) { static mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); if (mp_obj_is_type(arg_in, &mp_type_list)) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); mp_obj_list_t *arg = native_list(arg_in); @@ -285,7 +275,7 @@ static mp_obj_t list_extend(mp_obj_t self_in, mp_obj_t arg_in) { return mp_const_none; // return None, as per CPython } -// CIRCUITPY-CHANGE: used elsewhere so not static; impl is different +// CIRCUITPY-CHANGE: provide version for C use outside this file inline mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index) { if (self->len == 0) { // CIRCUITPY-CHANGE: more specific mp_raise @@ -303,18 +293,28 @@ inline mp_obj_t mp_obj_list_pop(mp_obj_list_t *self, size_t index) { return ret; } -// CIRCUITPY-CHANGE static mp_obj_t list_pop(size_t n_args, const mp_obj_t *args) { mp_check_self(mp_obj_is_type(args[0], &mp_type_list)); mp_obj_list_t *self = native_list(args[0]); size_t index = mp_get_index(self->base.type, self->len, n_args == 1 ? MP_OBJ_NEW_SMALL_INT(-1) : args[1], false); + // CIRCUITPY-CHANGE: use factored-out pop code return mp_obj_list_pop(self, index); } +// "head" is actually the *exclusive lower bound* of the range to sort. That is, +// the first element to be sorted is `head[1]`, not `head[0]`. Similarly `tail` +// is an *inclusive upper bound* of the range to sort. That is, the final +// element to sort is `tail[0]`, not `tail[-1]`. +// +// The pivot element is always chosen as `tail[0]`. +// +// These unusual choices allows structuring the partitioning +// process as a do/while loop, which generates smaller code than the equivalent +// code with usual C bounds & a while or for loop. static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn, mp_obj_t binop_less_result) { mp_cstack_check(); - while (head < tail) { - mp_obj_t *h = head - 1; + while (tail - head > 1) { // So long as at least 2 elements remain + mp_obj_t *h = head; mp_obj_t *t = tail; mp_obj_t v = key_fn == MP_OBJ_NULL ? tail[0] : mp_call_function_1(key_fn, tail[0]); // get pivot using key_fn for (;;) { @@ -325,19 +325,21 @@ static void mp_quicksort(mp_obj_t *head, mp_obj_t *tail, mp_obj_t key_fn, mp_obj if (h >= t) { break; } + // A pair of objects must be swapped to the other side of the partition mp_obj_t x = h[0]; h[0] = t[0]; t[0] = x; } + // Place the pivot element in the proper position mp_obj_t x = h[0]; h[0] = tail[0]; tail[0] = x; // do the smaller recursive call first, to keep stack within O(log(N)) - if (t - head < tail - h - 1) { + if (t - head < tail - h) { mp_quicksort(head, t, key_fn, binop_less_result); - head = h + 1; + head = h; } else { - mp_quicksort(h + 1, tail, key_fn, binop_less_result); + mp_quicksort(h, tail, key_fn, binop_less_result); tail = t; } } @@ -362,7 +364,7 @@ mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ mp_obj_list_t *self = native_list(pos_args[0]); if (self->len > 1) { - mp_quicksort(self->items, self->items + self->len - 1, + mp_quicksort(self->items - 1, self->items + self->len - 1, args.key.u_obj == mp_const_none ? MP_OBJ_NULL : args.key.u_obj, args.reverse.u_bool ? mp_const_false : mp_const_true); } @@ -370,10 +372,10 @@ mp_obj_t mp_obj_list_sort(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ return mp_const_none; } -// CIRCUITPY-CHANGE: used elsewhere so not static +// CIRCUITPY-CHANGE: rename and make not static for use elsewhere mp_obj_t mp_obj_list_clear(mp_obj_t self_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); self->len = 0; self->items = m_renew(mp_obj_t, self->items, self->alloc, LIST_MIN_ALLOC); @@ -384,27 +386,28 @@ mp_obj_t mp_obj_list_clear(mp_obj_t self_in) { static mp_obj_t list_copy(mp_obj_t self_in) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); return mp_obj_new_list(self->len, self->items); } static mp_obj_t list_count(mp_obj_t self_in, mp_obj_t value) { mp_check_self(mp_obj_is_type(self_in, &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(self_in); return mp_seq_count_obj(self->items, self->len, value); } static mp_obj_t list_index(size_t n_args, const mp_obj_t *args) { mp_check_self(mp_obj_is_type(args[0], &mp_type_list)); - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: handle subclassing mp_obj_list_t *self = native_list(args[0]); return mp_seq_index_obj(self->items, self->len, n_args, args); } -// CIRCUITPY-CHANGE: used elsewhere so not static -inline void mp_obj_list_insert(mp_obj_list_t *self, size_t index, mp_obj_t obj) { +// CIRCUITPY-CHANGE: factor out for C use elsewhere; not meant to emulate Python API +inline void mp_obj_list_insert(mp_obj_list_t *self_in, size_t index, mp_obj_t obj) { + mp_obj_list_t *self = native_list(self_in); mp_obj_list_append(MP_OBJ_FROM_PTR(self), mp_const_none); for (size_t i = self->len - 1; i > index; --i) { @@ -428,7 +431,7 @@ static mp_obj_t list_insert(mp_obj_t self_in, mp_obj_t idx, mp_obj_t obj) { if ((size_t)index > self->len) { index = self->len; } - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: use factored-out insert code mp_obj_list_insert(self, index, obj); return mp_const_none; } diff --git a/py/objmodule.c b/py/objmodule.c index a5c1dee968ea8..84b6a10f9abd4 100644 --- a/py/objmodule.c +++ b/py/objmodule.c @@ -34,11 +34,6 @@ #include "py/runtime.h" #include "py/builtin.h" -// CIRCUITPY-CHANGE -#if CIRCUITPY_WARNINGS -#include "shared-module/warnings/__init__.h" -#endif - static void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_module_t *self = MP_OBJ_TO_PTR(self_in); @@ -49,7 +44,7 @@ static void module_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kin module_name = mp_obj_str_get_str(elem->value); } - #if MICROPY_PY___FILE__ + #if MICROPY_MODULE___FILE__ // If we store __file__ to imported modules then try to lookup this // symbol to give more information about the module. elem = mp_map_lookup(&self->globals->map, MP_OBJ_NEW_QSTR(MP_QSTR___file__), MP_MAP_LOOKUP); @@ -157,11 +152,13 @@ static const mp_rom_map_elem_t mp_builtin_module_table[] = { }; MP_DEFINE_CONST_MAP(mp_builtin_module_map, mp_builtin_module_table); +#if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES static const mp_rom_map_elem_t mp_builtin_extensible_module_table[] = { // built-in modules declared with MP_REGISTER_EXTENSIBLE_MODULE() MICROPY_REGISTERED_EXTENSIBLE_MODULES }; MP_DEFINE_CONST_MAP(mp_builtin_extensible_module_map, mp_builtin_extensible_module_table); +#endif #if MICROPY_MODULE_ATTR_DELEGATION && defined(MICROPY_MODULE_DELEGATIONS) typedef struct _mp_module_delegation_entry_t { @@ -178,13 +175,13 @@ static const mp_module_delegation_entry_t mp_builtin_module_delegation_table[] = // Attempts to find (and initialise) a built-in, otherwise returns // MP_OBJ_NULL. mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible) { - // CIRCUITPY-CHANGE - #if CIRCUITPY_PARALLELDISPLAYBUS && CIRCUITPY_WARNINGS - if (module_name == MP_QSTR_paralleldisplay) { - warnings_warn(&mp_type_FutureWarning, MP_ERROR_TEXT("%q renamed %q"), MP_QSTR_paralleldisplay, MP_QSTR_paralleldisplaybus); - } + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES + const mp_map_t *map = extensible ? &mp_builtin_extensible_module_map : &mp_builtin_module_map; + #else + const mp_map_t *map = &mp_builtin_module_map; #endif - mp_map_elem_t *elem = mp_map_lookup((mp_map_t *)(extensible ? &mp_builtin_extensible_module_map : &mp_builtin_module_map), MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); + mp_map_elem_t *elem = mp_map_lookup((mp_map_t *)map, MP_OBJ_NEW_QSTR(module_name), MP_MAP_LOOKUP); + if (!elem) { #if MICROPY_PY_SYS // Special case for sys, which isn't extensible but can always be @@ -194,6 +191,7 @@ mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible) { } #endif + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES if (extensible) { // At this point we've already tried non-extensible built-ins, the // filesystem, and now extensible built-ins. No match, so fail @@ -210,6 +208,7 @@ mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible) { // the old behaviour of the u-prefix being used to force a built-in // import. // CIRCUITPY-CHANGE: Don't look for `ufoo`. + #endif return MP_OBJ_NULL; } diff --git a/py/objmodule.h b/py/objmodule.h index 8b14cd9fc3d30..221392ccce431 100644 --- a/py/objmodule.h +++ b/py/objmodule.h @@ -32,10 +32,16 @@ // Only include module definitions when not doing qstr extraction, because the // qstr extraction stage also generates this module definition header file. #include "genhdr/moduledefs.h" +// CIRCUITPY-CHANGE: avoid undef warning +#else +#define MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES (0) #endif extern const mp_map_t mp_builtin_module_map; + +#if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES extern const mp_map_t mp_builtin_extensible_module_map; +#endif mp_obj_t mp_module_get_builtin(qstr module_name, bool extensible); diff --git a/py/objnamedtuple.c b/py/objnamedtuple.c index b6bbe6e05d8b2..d687dbfda59d6 100644 --- a/py/objnamedtuple.c +++ b/py/objnamedtuple.c @@ -73,7 +73,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(namedtuple_asdict_obj, namedtuple_asdict); void namedtuple_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) { (void)kind; mp_obj_namedtuple_t *o = MP_OBJ_TO_PTR(o_in); - mp_printf(print, "%q", o->tuple.base.type->name); + mp_printf(print, "%q", (qstr)o->tuple.base.type->name); const qstr *fields = ((mp_obj_namedtuple_type_t *)o->tuple.base.type)->fields; mp_obj_attrtuple_print_helper(print, fields, &o->tuple); } diff --git a/py/objrange.c b/py/objrange.c index bde2ebaabb4ed..dea9fe5f2963a 100644 --- a/py/objrange.c +++ b/py/objrange.c @@ -41,9 +41,9 @@ typedef struct _mp_obj_range_it_t { static mp_obj_t range_it_iternext(mp_obj_t o_in) { mp_obj_range_it_t *o = MP_OBJ_TO_PTR(o_in); if ((o->step > 0 && o->cur < o->stop) || (o->step < 0 && o->cur > o->stop)) { - mp_obj_t o_out = MP_OBJ_NEW_SMALL_INT(o->cur); + mp_int_t cur = o->cur; o->cur += o->step; - return o_out; + return mp_obj_new_int(cur); } else { return MP_OBJ_STOP_ITERATION; } @@ -133,7 +133,7 @@ static mp_obj_t range_unary_op(mp_unary_op_t op, mp_obj_t self_in) { case MP_UNARY_OP_BOOL: return mp_obj_new_bool(len > 0); case MP_UNARY_OP_LEN: - return MP_OBJ_NEW_SMALL_INT(len); + return mp_obj_new_int(len); default: return MP_OBJ_NULL; // op not supported } @@ -165,20 +165,16 @@ static mp_obj_t range_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { #if MICROPY_PY_BUILTINS_SLICE if (mp_obj_is_type(index, &mp_type_slice)) { mp_bound_slice_t slice; - mp_seq_get_fast_slice_indexes(len, index, &slice); + mp_obj_slice_indices(index, len, &slice); mp_obj_range_t *o = mp_obj_malloc(mp_obj_range_t, &mp_type_range); o->start = self->start + slice.start * self->step; o->stop = self->start + slice.stop * self->step; o->step = slice.step * self->step; - if (slice.step < 0) { - // Negative slice steps have inclusive stop, so adjust for exclusive - o->stop -= self->step; - } return MP_OBJ_FROM_PTR(o); } #endif size_t index_val = mp_get_index(self->base.type, len, index, false); - return MP_OBJ_NEW_SMALL_INT(self->start + index_val * self->step); + return mp_obj_new_int(self->start + index_val * self->step); } else { return MP_OBJ_NULL; // op not supported } diff --git a/py/objringio.c b/py/objringio.c index ba1ec25307ea4..0025b26be3b07 100644 --- a/py/objringio.c +++ b/py/objringio.c @@ -39,22 +39,19 @@ typedef struct _micropython_ringio_obj_t { static mp_obj_t micropython_ringio_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); - mp_int_t buff_size = -1; mp_buffer_info_t bufinfo = {NULL, 0, 0}; if (!mp_get_buffer(args[0], &bufinfo, MP_BUFFER_RW)) { - buff_size = mp_obj_get_int(args[0]); + bufinfo.len = mp_obj_get_int(args[0]) + 1; + bufinfo.buf = m_new(uint8_t, bufinfo.len); } - micropython_ringio_obj_t *self = mp_obj_malloc(micropython_ringio_obj_t, type); - if (bufinfo.buf != NULL) { - // buffer passed in, use it directly for ringbuffer. - self->ringbuffer.buf = bufinfo.buf; - self->ringbuffer.size = bufinfo.len; - self->ringbuffer.iget = self->ringbuffer.iput = 0; - } else { - // Allocate new buffer, add one extra to buff_size as ringbuf consumes one byte for tracking. - ringbuf_alloc(&(self->ringbuffer), buff_size + 1); + if (bufinfo.len < 2 || bufinfo.len > UINT16_MAX) { + mp_raise_ValueError(NULL); } + micropython_ringio_obj_t *self = mp_obj_malloc(micropython_ringio_obj_t, type); + self->ringbuffer.buf = bufinfo.buf; + self->ringbuffer.size = bufinfo.len; + self->ringbuffer.iget = self->ringbuffer.iput = 0; return MP_OBJ_FROM_PTR(self); } diff --git a/py/objstr.c b/py/objstr.c index 1874cdb01d03d..bd650bbe18ad6 100644 --- a/py/objstr.c +++ b/py/objstr.c @@ -33,6 +33,7 @@ #include "py/objlist.h" #include "py/runtime.h" #include "py/cstack.h" +#include "py/objtuple.h" // CIRCUITPY-CHANGE const char nibble_to_hex_upper[16] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', @@ -46,7 +47,7 @@ static mp_obj_t str_modulo_format(mp_obj_t pattern, size_t n_args, const mp_obj_ #endif static mp_obj_t mp_obj_new_bytes_iterator(mp_obj_t str, mp_obj_iter_buf_t *iter_buf); -static NORETURN void bad_implicit_conversion(mp_obj_t self_in); +static MP_NORETURN void bad_implicit_conversion(mp_obj_t self_in); static mp_obj_t mp_obj_new_str_type_from_vstr(const mp_obj_type_t *type, vstr_t *vstr); @@ -372,8 +373,7 @@ mp_obj_t mp_obj_str_binary_op(mp_binary_op_t op, mp_obj_t lhs_in, mp_obj_t rhs_i mp_obj_t *args = &rhs_in; size_t n_args = 1; mp_obj_t dict = MP_OBJ_NULL; - if (mp_obj_is_type(rhs_in, &mp_type_tuple)) { - // TODO: Support tuple subclasses? + if (mp_obj_is_tuple_compatible(rhs_in)) { mp_obj_tuple_get(rhs_in, &n_args, &args); } else if (mp_obj_is_type(rhs_in, &mp_type_dict)) { dict = rhs_in; @@ -1018,7 +1018,7 @@ static mp_obj_t arg_as_int(mp_obj_t arg) { #endif #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE -static NORETURN void terse_str_format_value_error(void) { +static MP_NORETURN void terse_str_format_value_error(void) { mp_raise_ValueError(MP_ERROR_TEXT("bad format string")); } #else @@ -1205,7 +1205,7 @@ static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar int width = -1; int precision = -1; char type = '\0'; - int flags = 0; + unsigned int flags = 0; if (format_spec) { // The format specifier (from http://docs.python.org/2/library/string.html#formatspec) @@ -1250,8 +1250,9 @@ static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } } s = str_to_int(s, stop, &width); - if (*s == ',') { - flags |= PF_FLAG_SHOW_COMMA; + if (*s == ',' || *s == '_') { + MP_STATIC_ASSERT((unsigned)'_' << PF_FLAG_SEP_POS >> PF_FLAG_SEP_POS == '_'); + flags |= (unsigned)*s << PF_FLAG_SEP_POS; s++; } if (*s == '.') { @@ -1324,7 +1325,7 @@ static vstr_t mp_obj_str_format_helper(const char *str, const char *top, int *ar } case '\0': // No explicit format type implies 'd' - case 'n': // I don't think we support locales in uPy so use 'd' + case 'n': // I don't think we support locales in MicroPython so use 'd' case 'd': mp_print_mp_int(&print, arg, 10, 'a', flags, fill, width, 0); continue; @@ -2395,7 +2396,7 @@ bool mp_obj_str_equal(mp_obj_t s1, mp_obj_t s2) { } } -static NORETURN void bad_implicit_conversion(mp_obj_t self_in) { +static MP_NORETURN void bad_implicit_conversion(mp_obj_t self_in) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE mp_raise_TypeError(MP_ERROR_TEXT("can't convert to str implicitly")); #else diff --git a/py/objtuple.c b/py/objtuple.c index 42e8d56a806cc..b2ccbbb299082 100644 --- a/py/objtuple.c +++ b/py/objtuple.c @@ -31,9 +31,6 @@ #include "py/objtuple.h" #include "py/runtime.h" -// type check is done on getiter method to allow tuple, namedtuple, attrtuple -#define mp_obj_is_tuple_compatible(o) (MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(o), iter) == mp_obj_tuple_getiter) - /******************************************************************************/ /* tuple */ @@ -250,6 +247,9 @@ MP_DEFINE_CONST_OBJ_TYPE( // the zero-length tuple const mp_obj_tuple_t mp_const_empty_tuple_obj = {{&mp_type_tuple}, 0}; +// CIRCUITPY-CHANGE: No change here, but implementation was copied for +// mp_obj_new_port_tuple in supervisor/shared/port.c, which allocates using port_malloc(). +// Change that to match if this changes. mp_obj_t mp_obj_new_tuple(size_t n, const mp_obj_t *items) { if (n == 0) { return mp_const_empty_tuple; diff --git a/py/objtuple.h b/py/objtuple.h index e2d010e6c5a3d..783522a6222b5 100644 --- a/py/objtuple.h +++ b/py/objtuple.h @@ -57,7 +57,7 @@ extern const mp_obj_type_t mp_type_attrtuple; #define MP_DEFINE_ATTRTUPLE(tuple_obj_name, fields, nitems, ...) \ const mp_rom_obj_tuple_t tuple_obj_name = { \ - .base = {&mp_type_attrtuple}, \ + .base = {.type = &mp_type_attrtuple}, \ .len = nitems, \ .items = { __VA_ARGS__, MP_ROM_PTR((void *)fields) } \ } @@ -68,4 +68,7 @@ void mp_obj_attrtuple_print_helper(const mp_print_t *print, const qstr *fields, mp_obj_t mp_obj_new_attrtuple(const qstr *fields, size_t n, const mp_obj_t *items); +// type check is done on getiter method to allow tuple, namedtuple, attrtuple +#define mp_obj_is_tuple_compatible(o) (MP_OBJ_TYPE_GET_SLOT_OR_NULL(mp_obj_get_type(o), iter) == mp_obj_tuple_getiter) + #endif // MICROPY_INCLUDED_PY_OBJTUPLE_H diff --git a/py/objtype.c b/py/objtype.c index 074d6f39293db..b1a984b50e057 100644 --- a/py/objtype.c +++ b/py/objtype.c @@ -44,6 +44,7 @@ #define ENABLE_SPECIAL_ACCESSORS \ (MICROPY_PY_DESCRIPTORS || MICROPY_PY_DELATTR_SETATTR || MICROPY_PY_BUILTINS_PROPERTY) +static mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict); static mp_obj_t mp_obj_is_subclass(mp_obj_t object, mp_obj_t classinfo); static mp_obj_t static_class_method_make_new(const mp_obj_type_t *self_in, size_t n_args, size_t n_kw, const mp_obj_t *args); @@ -690,8 +691,8 @@ static void mp_obj_instance_load_attr(mp_obj_t self_in, qstr attr, mp_obj_t *des // try __getattr__ if (attr != MP_QSTR___getattr__) { #if MICROPY_PY_DESCRIPTORS - // With descriptors enabled, don't delegate lookups of __get__/__set__/__delete__. - if (attr == MP_QSTR___get__ || attr == MP_QSTR___set__ || attr == MP_QSTR___delete__) { + // With descriptors enabled, don't delegate lookups of __get__/__set__/__delete__/__set_name__. + if (attr == MP_QSTR___get__ || attr == MP_QSTR___set__ || attr == MP_QSTR___delete__ || attr == MP_QSTR___set_name__) { return; } #endif @@ -996,7 +997,7 @@ static bool check_for_special_accessors(mp_obj_t key, mp_obj_t value) { #endif #if MICROPY_PY_DESCRIPTORS static const uint8_t to_check[] = { - MP_QSTR___get__, MP_QSTR___set__, MP_QSTR___delete__, + MP_QSTR___get__, MP_QSTR___set__, MP_QSTR___delete__, // not needed for MP_QSTR___set_name__ though }; for (size_t i = 0; i < MP_ARRAY_SIZE(to_check); ++i) { mp_obj_t dest_temp[2]; @@ -1010,10 +1011,52 @@ static bool check_for_special_accessors(mp_obj_t key, mp_obj_t value) { } #endif +#if MICROPY_PY_DESCRIPTORS +// Shared data layout for the __set_name__ call and a linked list of calls to be made. +typedef union _setname_list_t setname_list_t; +union _setname_list_t { + mp_obj_t call[4]; + struct { + mp_obj_t _meth; + mp_obj_t _self; + setname_list_t *next; // can use the "owner" argument position temporarily for the linked list + mp_obj_t _name; + }; +}; + +// Append any `__set_name__` method on `value` to the setname list, with its per-attr args +static setname_list_t *setname_maybe_bind_append(setname_list_t *tail, mp_obj_t name, mp_obj_t value) { + // make certain our type-punning is safe: + MP_STATIC_ASSERT_NONCONSTEXPR(offsetof(setname_list_t, next) == offsetof(setname_list_t, call[2])); + + // tail is a blank list entry + mp_load_method_maybe(value, MP_QSTR___set_name__, tail->call); + if (tail->call[1] != MP_OBJ_NULL) { + // Each time a __set_name__ is found, leave it in-place in the former tail and allocate a new tail + tail->next = m_new_obj(setname_list_t); + tail->next->next = NULL; + tail->call[3] = name; + return tail->next; + } else { + return tail; + } +} + +// Execute the captured `__set_name__` calls, destroying the setname list in the process. +static inline void setname_consume_call_all(setname_list_t *head, mp_obj_t owner) { + setname_list_t *next; + while ((next = head->next) != NULL) { + head->call[2] = owner; + mp_call_method_n_kw(2, 0, head->call); + head = next; + } +} +#endif + static void type_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { (void)kind; mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "", self->name); + mp_printf(print, "", (qstr)self->name); } static mp_obj_t type_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { @@ -1043,7 +1086,7 @@ static mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp if (!MP_OBJ_TYPE_HAS_SLOT(self, make_new)) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE - mp_raise_TypeError(MP_ERROR_TEXT("cannot create instance")); + mp_raise_TypeError(MP_ERROR_TEXT("can't create instance")); #else // CIRCUITPY-CHANGE: more specific mp_raise mp_raise_TypeError_varg(MP_ERROR_TEXT("can't create '%q' instances"), self->name); @@ -1159,7 +1202,7 @@ MP_DEFINE_CONST_OBJ_TYPE( attr, type_attr ); -mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { +static mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) { // Verify input objects have expected type if (!mp_obj_is_type(bases_tuple, &mp_type_tuple)) { mp_raise_TypeError(NULL); @@ -1252,20 +1295,38 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) } } + #if MICROPY_PY_DESCRIPTORS + // To avoid any dynamic allocations when no __set_name__ exists, + // the head of this list is kept on the stack (marked blank with `next = NULL`). + setname_list_t setname_list = { .next = NULL }; + setname_list_t *setname_tail = &setname_list; + #endif + #if ENABLE_SPECIAL_ACCESSORS - // Check if the class has any special accessor methods - if (!(o->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS)) { - for (size_t i = 0; i < locals_ptr->map.alloc; i++) { - if (mp_map_slot_is_filled(&locals_ptr->map, i)) { - const mp_map_elem_t *elem = &locals_ptr->map.table[i]; - if (check_for_special_accessors(elem->key, elem->value)) { - o->flags |= MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; - break; - } + // Check if the class has any special accessor methods, + // and accumulate bound __set_name__ methods that need to be called + for (size_t i = 0; i < locals_ptr->map.alloc; i++) { + #if !MICROPY_PY_DESCRIPTORS + // __set_name__ needs to scan the entire locals map, can't early-terminate + if (o->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS) { + break; + } + #endif + + if (mp_map_slot_is_filled(&locals_ptr->map, i)) { + const mp_map_elem_t *elem = &locals_ptr->map.table[i]; + + if (!(o->flags & MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS) // elidable when the early-termination check is enabled + && check_for_special_accessors(elem->key, elem->value)) { + o->flags |= MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS; } + + #if MICROPY_PY_DESCRIPTORS + setname_tail = setname_maybe_bind_append(setname_tail, elem->key, elem->value); + #endif } } - #endif + #endif // ENABLE_SPECIAL_ACCESSORS const mp_obj_type_t *native_base; size_t num_native_bases = instance_count_native_bases(o, &native_base); @@ -1273,8 +1334,7 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) mp_raise_TypeError(MP_ERROR_TEXT("multiple bases have instance lay-out conflict")); } - mp_map_t *locals_map = &MP_OBJ_TYPE_GET_SLOT(o, locals_dict)->map; - mp_map_elem_t *elem = mp_map_lookup(locals_map, MP_OBJ_NEW_QSTR(MP_QSTR___new__), MP_MAP_LOOKUP); + mp_map_elem_t *elem = mp_map_lookup(&locals_ptr->map, MP_OBJ_NEW_QSTR(MP_QSTR___new__), MP_MAP_LOOKUP); if (elem != NULL) { // __new__ slot exists; check if it is a function if (mp_obj_is_fun(elem->value)) { @@ -1283,6 +1343,10 @@ mp_obj_t mp_obj_new_type(qstr name, mp_obj_t bases_tuple, mp_obj_t locals_dict) } } + #if MICROPY_PY_DESCRIPTORS + setname_consume_call_all(&setname_list, MP_OBJ_FROM_PTR(o)); + #endif + return MP_OBJ_FROM_PTR(o); } diff --git a/py/parse.c b/py/parse.c index a393b0ee8c153..36150ef468bdf 100644 --- a/py/parse.c +++ b/py/parse.c @@ -341,18 +341,34 @@ static uint8_t peek_rule(parser_t *parser, size_t n) { } #endif -bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { +#if MICROPY_COMP_CONST_FOLDING || MICROPY_EMIT_INLINE_ASM +static bool mp_parse_node_get_number_maybe(mp_parse_node_t pn, mp_obj_t *o) { if (MP_PARSE_NODE_IS_SMALL_INT(pn)) { *o = MP_OBJ_NEW_SMALL_INT(MP_PARSE_NODE_LEAF_SMALL_INT(pn)); return true; } else if (MP_PARSE_NODE_IS_STRUCT_KIND(pn, RULE_const_object)) { mp_parse_node_struct_t *pns = (mp_parse_node_struct_t *)pn; *o = mp_parse_node_extract_const_object(pns); - return mp_obj_is_int(*o); + return mp_obj_is_int(*o) + #if MICROPY_COMP_CONST_FLOAT + || mp_obj_is_float(*o) + #endif + ; } else { return false; } } +#endif + +#if MICROPY_EMIT_INLINE_ASM +bool mp_parse_node_get_int_maybe(mp_parse_node_t pn, mp_obj_t *o) { + return mp_parse_node_get_number_maybe(pn, o) + #if MICROPY_COMP_CONST_FLOAT + && mp_obj_is_int(*o) + #endif + ; +} +#endif #if MICROPY_COMP_CONST_TUPLE || MICROPY_COMP_CONST static bool mp_parse_node_is_const(mp_parse_node_t pn) { @@ -647,12 +663,32 @@ static const mp_rom_map_elem_t mp_constants_table[] = { #if MICROPY_PY_UCTYPES { MP_ROM_QSTR(MP_QSTR_uctypes), MP_ROM_PTR(&mp_module_uctypes) }, #endif + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_PY_MATH && MICROPY_COMP_CONST_FLOAT + { MP_ROM_QSTR(MP_QSTR_math), MP_ROM_PTR(&mp_module_math) }, + #endif // Extra constants as defined by a port MICROPY_PORT_CONSTANTS }; static MP_DEFINE_CONST_MAP(mp_constants_map, mp_constants_table); #endif +static bool binary_op_maybe(mp_binary_op_t op, mp_obj_t lhs, mp_obj_t rhs, mp_obj_t *res) { + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t tmp = mp_binary_op(op, lhs, rhs); + nlr_pop(); + #if MICROPY_PY_BUILTINS_COMPLEX + if (mp_obj_is_type(tmp, &mp_type_complex)) { + return false; + } + #endif + *res = tmp; + return true; + } else { + return false; + } +} + static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *num_args) { if (rule_id == RULE_or_test || rule_id == RULE_and_test) { @@ -711,7 +747,7 @@ static bool fold_logical_constants(parser_t *parser, uint8_t rule_id, size_t *nu } static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { - // this code does folding of arbitrary integer expressions, eg 1 + 2 * 3 + 4 + // this code does folding of arbitrary numeric expressions, eg 1 + 2 * 3 + 4 // it does not do partial folding, eg 1 + 2 + x -> 3 + x mp_obj_t arg0; @@ -721,7 +757,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { || rule_id == RULE_power) { // folding for binary ops: | ^ & ** mp_parse_node_t pn = peek_result(parser, num_args - 1); - if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + if (!mp_parse_node_get_number_maybe(pn, &arg0)) { return false; } mp_binary_op_t op; @@ -737,58 +773,45 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { for (ssize_t i = num_args - 2; i >= 0; --i) { pn = peek_result(parser, i); mp_obj_t arg1; - if (!mp_parse_node_get_int_maybe(pn, &arg1)) { + if (!mp_parse_node_get_number_maybe(pn, &arg1)) { return false; } - if (op == MP_BINARY_OP_POWER && mp_obj_int_sign(arg1) < 0) { - // ** can't have negative rhs + if (!binary_op_maybe(op, arg0, arg1, &arg0)) { return false; } - arg0 = mp_binary_op(op, arg0, arg1); } } else if (rule_id == RULE_shift_expr || rule_id == RULE_arith_expr || rule_id == RULE_term) { // folding for binary ops: << >> + - * @ / % // mp_parse_node_t pn = peek_result(parser, num_args - 1); - if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + if (!mp_parse_node_get_number_maybe(pn, &arg0)) { return false; } for (ssize_t i = num_args - 2; i >= 1; i -= 2) { pn = peek_result(parser, i - 1); mp_obj_t arg1; - if (!mp_parse_node_get_int_maybe(pn, &arg1)) { + if (!mp_parse_node_get_number_maybe(pn, &arg1)) { return false; } mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, i)); - if (tok == MP_TOKEN_OP_AT || tok == MP_TOKEN_OP_SLASH) { - // Can't fold @ or / - return false; - } mp_binary_op_t op = MP_BINARY_OP_LSHIFT + (tok - MP_TOKEN_OP_DBL_LESS); - int rhs_sign = mp_obj_int_sign(arg1); - if (op <= MP_BINARY_OP_RSHIFT) { - // << and >> can't have negative rhs - if (rhs_sign < 0) { - return false; - } - } else if (op >= MP_BINARY_OP_FLOOR_DIVIDE) { - // % and // can't have zero rhs - if (rhs_sign == 0) { - return false; - } + if (!binary_op_maybe(op, arg0, arg1, &arg0)) { + return false; } - arg0 = mp_binary_op(op, arg0, arg1); } } else if (rule_id == RULE_factor_2) { // folding for unary ops: + - ~ mp_parse_node_t pn = peek_result(parser, 0); - if (!mp_parse_node_get_int_maybe(pn, &arg0)) { + if (!mp_parse_node_get_number_maybe(pn, &arg0)) { return false; } mp_token_kind_t tok = MP_PARSE_NODE_LEAF_ARG(peek_result(parser, 1)); mp_unary_op_t op; if (tok == MP_TOKEN_OP_TILDE) { + if (!mp_obj_is_int(arg0)) { + return false; + } op = MP_UNARY_OP_INVERT; } else { assert(tok == MP_TOKEN_OP_PLUS || tok == MP_TOKEN_OP_MINUS); // should be @@ -860,7 +883,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { return false; } // id1.id2 - // look it up in constant table, see if it can be replaced with an integer + // look it up in constant table, see if it can be replaced with an integer or a float mp_parse_node_struct_t *pns1 = (mp_parse_node_struct_t *)pn1; assert(MP_PARSE_NODE_IS_ID(pns1->nodes[0])); qstr q_base = MP_PARSE_NODE_LEAF_ARG(pn0); @@ -871,7 +894,7 @@ static bool fold_constants(parser_t *parser, uint8_t rule_id, size_t num_args) { } mp_obj_t dest[2]; mp_load_method_maybe(elem->value, q_attr, dest); - if (!(dest[0] != MP_OBJ_NULL && mp_obj_is_int(dest[0]) && dest[1] == MP_OBJ_NULL)) { + if (!(dest[0] != MP_OBJ_NULL && (mp_obj_is_int(dest[0]) || mp_obj_is_float(dest[0])) && dest[1] == MP_OBJ_NULL)) { return false; } arg0 = dest[0]; diff --git a/py/parsenum.c b/py/parsenum.c index 874216f08d005..c52f040e1a210 100644 --- a/py/parsenum.c +++ b/py/parsenum.c @@ -28,6 +28,7 @@ #include #include "py/runtime.h" +#include "py/misc.h" #include "py/parsenumbase.h" #include "py/parsenum.h" #include "py/smallint.h" @@ -36,7 +37,7 @@ #include #endif -static NORETURN void raise_exc(mp_obj_t exc, mp_lexer_t *lex) { +static MP_NORETURN void raise_exc(mp_obj_t exc, mp_lexer_t *lex) { // if lex!=NULL then the parser called us and we need to convert the // exception's type from ValueError to SyntaxError and add traceback info if (lex != NULL) { @@ -46,6 +47,27 @@ static NORETURN void raise_exc(mp_obj_t exc, mp_lexer_t *lex) { nlr_raise(exc); } +#if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_LONGLONG +// For the common small integer parsing case, we parse directly to mp_int_t and +// check that the value doesn't overflow a smallint (in which case we fail over +// to bigint parsing if supported) +typedef mp_int_t parsed_int_t; + +#define PARSED_INT_MUL_OVERFLOW mp_mul_mp_int_t_overflow +#define PARSED_INT_FITS MP_SMALL_INT_FITS +#else +// In the special case where bigint support is long long, we save code size by +// parsing directly to long long and then return either a bigint or smallint +// from the same result. +// +// To avoid pulling in (slow) signed 64-bit math routines we do the initial +// parsing to an unsigned long long and only convert to signed at the end. +typedef unsigned long long parsed_int_t; + +#define PARSED_INT_MUL_OVERFLOW mp_mul_ull_overflow +#define PARSED_INT_FITS(I) ((I) <= (unsigned long long)LLONG_MAX + 1) +#endif + mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, mp_lexer_t *lex) { const byte *restrict str = (const byte *)str_; const byte *restrict top = str + len; @@ -77,7 +99,7 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m str += mp_parse_num_base((const char *)str, top - str, &base); // string should be an integer number - mp_int_t int_val = 0; + parsed_int_t parsed_val = 0; const byte *restrict str_val_start = str; for (; str < top; str++) { // get next digit as a value @@ -99,25 +121,32 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m break; } - // add next digi and check for overflow - if (mp_small_int_mul_overflow(int_val, base)) { + // add next digit and check for overflow + if (PARSED_INT_MUL_OVERFLOW(parsed_val, base, &parsed_val)) { goto overflow; } - int_val = int_val * base + dig; - if (!MP_SMALL_INT_FITS(int_val)) { + parsed_val += dig; + if (!PARSED_INT_FITS(parsed_val)) { goto overflow; } } - // negate value if needed - if (neg) { - int_val = -int_val; + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_LONGLONG + // The PARSED_INT_FITS check above ensures parsed_val fits in small int representation + ret_val = MP_OBJ_NEW_SMALL_INT(neg ? (-parsed_val) : parsed_val); +have_ret_val: + #else + // The PARSED_INT_FITS check above ensures parsed_val won't overflow signed long long + long long signed_val = -parsed_val; + if (!neg) { + if (signed_val == LLONG_MIN) { + goto overflow; + } + signed_val = -signed_val; } + ret_val = mp_obj_new_int_from_ll(signed_val); // Could be large or small int + #endif - // create the small int - ret_val = MP_OBJ_NEW_SMALL_INT(int_val); - -have_ret_val: // check we parsed something if (str == str_val_start) { goto value_error; @@ -136,6 +165,7 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m return ret_val; overflow: + #if MICROPY_LONGINT_IMPL != MICROPY_LONGINT_IMPL_LONGLONG // reparse using long int { const char *s2 = (const char *)str_val_start; @@ -143,6 +173,9 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m str = (const byte *)s2; goto have_ret_val; } + #else + mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("result overflows long long storage")); + #endif value_error: { @@ -167,6 +200,8 @@ mp_obj_t mp_parse_num_integer(const char *restrict str_, size_t len, int base, m } } +#if MICROPY_PY_BUILTINS_FLOAT + enum { REAL_IMAG_STATE_START = 0, REAL_IMAG_STATE_HAVE_REAL = 1, @@ -179,27 +214,81 @@ typedef enum { PARSE_DEC_IN_EXP, } parse_dec_in_t; -#if MICROPY_PY_BUILTINS_FLOAT // MANTISSA_MAX is used to retain precision while not overflowing mantissa -// SMALL_NORMAL_VAL is the smallest power of 10 that is still a normal float -// EXACT_POWER_OF_10 is the largest value of x so that 10^x can be stored exactly in a float -// Note: EXACT_POWER_OF_10 is at least floor(log_5(2^mantissa_length)). Indeed, 10^n = 2^n * 5^n -// so we only have to store the 5^n part in the mantissa (the 2^n part will go into the float's -// exponent). +#define MANTISSA_MAX (sizeof(mp_large_float_uint_t) == 8 ? 0x1999999999999998ULL : 0x19999998U) + +// MAX_EXACT_POWER_OF_5 is the largest value of x so that 5^x can be stored exactly in a float #if MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_FLOAT -#define MANTISSA_MAX 0x19999998U -#define SMALL_NORMAL_VAL (1e-37F) -#define SMALL_NORMAL_EXP (-37) -#define EXACT_POWER_OF_10 (9) +#define MAX_EXACT_POWER_OF_5 (10) #elif MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE -#define MANTISSA_MAX 0x1999999999999998ULL -#define SMALL_NORMAL_VAL (1e-307) -#define SMALL_NORMAL_EXP (-307) -#define EXACT_POWER_OF_10 (22) +#define MAX_EXACT_POWER_OF_5 (22) #endif +// Helper to compute `num * (10.0 ** dec_exp)` +mp_large_float_t mp_decimal_exp(mp_large_float_t num, int dec_exp) { + // CIRCUITPY-CHANGE: ignore float equal warning + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wfloat-equal" + if (dec_exp == 0 || num == (mp_large_float_t)(0.0)) { + return num; + } + #pragma GCC diagnostic pop + + #if MICROPY_FLOAT_FORMAT_IMPL == MICROPY_FLOAT_FORMAT_IMPL_EXACT + + // If the assert below fails, it means you have chosen MICROPY_FLOAT_FORMAT_IMPL_EXACT + // manually on a platform where `larger floats` are not supported, which would + // result in inexact conversions. To fix this issue, change your `mpconfigport.h` + // and select MICROPY_FLOAT_FORMAT_IMPL_APPROX instead + assert(sizeof(mp_large_float_t) > sizeof(mp_float_t)); + + // Perform power using simple multiplications, to avoid + // dependency to higher-precision pow() function + int neg_exp = (dec_exp < 0); + if (neg_exp) { + dec_exp = -dec_exp; + } + mp_large_float_t res = num; + mp_large_float_t expo = (mp_large_float_t)10.0; + while (dec_exp) { + if (dec_exp & 1) { + if (neg_exp) { + res /= expo; + } else { + res *= expo; + } + } + dec_exp >>= 1; + if (dec_exp) { + expo *= expo; + } + } + return res; + + #else + // MICROPY_FLOAT_FORMAT_IMPL != MICROPY_FLOAT_FORMAT_IMPL_EXACT + + mp_float_union_t res = {num}; + // Multiply first by (2.0 ** dec_exp) via the exponent + // - this will ensure that the result of `pow()` is always in mp_float_t range + // when the result is expected to be in mp_float_t range (e.g. during format) + // - we don't need to care about p.exp overflow, as (5.0 ** dec_exp) will anyway + // force the final result toward the proper edge if needed (0.0 or inf) + res.p.exp += dec_exp; + // Use positive exponents when they are more precise then negative + if (dec_exp < 0 && dec_exp >= -MAX_EXACT_POWER_OF_5) { + res.f /= MICROPY_FLOAT_C_FUN(pow)(5, -dec_exp); + } else { + res.f *= MICROPY_FLOAT_C_FUN(pow)(5, dec_exp); + } + return (mp_large_float_t)res.f; + + #endif +} + + // Break out inner digit accumulation routine to ease trailing zero deferral. -static mp_float_uint_t accept_digit(mp_float_uint_t p_mantissa, unsigned int dig, int *p_exp_extra, int in) { +static mp_large_float_uint_t accept_digit(mp_large_float_uint_t p_mantissa, unsigned int dig, int *p_exp_extra, int in) { // Core routine to ingest an additional digit. if (p_mantissa < MANTISSA_MAX) { // dec_val won't overflow so keep accumulating @@ -216,6 +305,85 @@ static mp_float_uint_t accept_digit(mp_float_uint_t p_mantissa, unsigned int dig return p_mantissa; } } + +// Helper to parse an unsigned decimal number into a mp_float_t +const char *mp_parse_float_internal(const char *str, size_t len, mp_float_t *res) { + const char *top = str + len; + + parse_dec_in_t in = PARSE_DEC_IN_INTG; + bool exp_neg = false; + mp_large_float_uint_t mantissa = 0; + int exp_val = 0; + int exp_extra = 0; + int trailing_zeros_intg = 0, trailing_zeros_frac = 0; + while (str < top) { + unsigned int dig = *str++; + if ('0' <= dig && dig <= '9') { + dig -= '0'; + if (in == PARSE_DEC_IN_EXP) { + // don't overflow exp_val when adding next digit, instead just truncate + // it and the resulting float will still be correct, either inf or 0.0 + // (use INT_MAX/2 to allow adding exp_extra at the end without overflow) + if (exp_val < (INT_MAX / 2 - 9) / 10) { + exp_val = 10 * exp_val + dig; + } + } else { + if (dig == 0 || mantissa >= MANTISSA_MAX) { + // Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them. + // Also, once we reach MANTISSA_MAX, treat every additional digit as a trailing zero. + if (in == PARSE_DEC_IN_INTG) { + ++trailing_zeros_intg; + } else { + ++trailing_zeros_frac; + } + } else { + // Time to un-defer any trailing zeros. Intg zeros first. + while (trailing_zeros_intg) { + mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_INTG); + --trailing_zeros_intg; + } + while (trailing_zeros_frac) { + mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_FRAC); + --trailing_zeros_frac; + } + mantissa = accept_digit(mantissa, dig, &exp_extra, in); + } + } + } else if (in == PARSE_DEC_IN_INTG && dig == '.') { + in = PARSE_DEC_IN_FRAC; + } else if (in != PARSE_DEC_IN_EXP && ((dig | 0x20) == 'e')) { + in = PARSE_DEC_IN_EXP; + if (str < top) { + if (str[0] == '+') { + str++; + } else if (str[0] == '-') { + str++; + exp_neg = true; + } + } + if (str == top) { + return NULL; + } + } else if (dig == '_') { + continue; + } else { + // unknown character + str--; + break; + } + } + + // work out the exponent + if (exp_neg) { + exp_val = -exp_val; + } + exp_val += exp_extra + trailing_zeros_intg; + + // At this point, we just need to multiply the mantissa by its base 10 exponent. + *res = (mp_float_t)mp_decimal_exp(mantissa, exp_val); + + return str; +} #endif // MICROPY_PY_BUILTINS_FLOAT #if MICROPY_PY_BUILTINS_COMPLEX @@ -253,111 +421,23 @@ parse_start:; const char *str_val_start = str; // determine what the string is - if (str < top && (str[0] | 0x20) == 'i') { - // string starts with 'i', should be 'inf' or 'infinity' (case insensitive) - if (str + 2 < top && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'f') { - // inf - str += 3; - dec_val = (mp_float_t)INFINITY; - if (str + 4 < top && (str[0] | 0x20) == 'i' && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'i' && (str[3] | 0x20) == 't' && (str[4] | 0x20) == 'y') { - // infinity - str += 5; - } - } - } else if (str < top && (str[0] | 0x20) == 'n') { - // string starts with 'n', should be 'nan' (case insensitive) - if (str + 2 < top && (str[1] | 0x20) == 'a' && (str[2] | 0x20) == 'n') { - // NaN - str += 3; - dec_val = MICROPY_FLOAT_C_FUN(nan)(""); + if (str + 2 < top && (str[0] | 0x20) == 'i' && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'f') { + // 'inf' or 'infinity' (case insensitive) + str += 3; + dec_val = (mp_float_t)INFINITY; + if (str + 4 < top && (str[0] | 0x20) == 'i' && (str[1] | 0x20) == 'n' && (str[2] | 0x20) == 'i' && (str[3] | 0x20) == 't' && (str[4] | 0x20) == 'y') { + // infinity + str += 5; } + } else if (str + 2 < top && (str[0] | 0x20) == 'n' && (str[1] | 0x20) == 'a' && (str[2] | 0x20) == 'n') { + // 'nan' (case insensitive) + str += 3; + dec_val = MICROPY_FLOAT_C_FUN(nan)(""); } else { // string should be a decimal number - parse_dec_in_t in = PARSE_DEC_IN_INTG; - bool exp_neg = false; - mp_float_uint_t mantissa = 0; - int exp_val = 0; - int exp_extra = 0; - int trailing_zeros_intg = 0, trailing_zeros_frac = 0; - while (str < top) { - unsigned int dig = *str++; - if ('0' <= dig && dig <= '9') { - dig -= '0'; - if (in == PARSE_DEC_IN_EXP) { - // don't overflow exp_val when adding next digit, instead just truncate - // it and the resulting float will still be correct, either inf or 0.0 - // (use INT_MAX/2 to allow adding exp_extra at the end without overflow) - if (exp_val < (INT_MAX / 2 - 9) / 10) { - exp_val = 10 * exp_val + dig; - } - } else { - if (dig == 0 || mantissa >= MANTISSA_MAX) { - // Defer treatment of zeros in fractional part. If nothing comes afterwards, ignore them. - // Also, once we reach MANTISSA_MAX, treat every additional digit as a trailing zero. - if (in == PARSE_DEC_IN_INTG) { - ++trailing_zeros_intg; - } else { - ++trailing_zeros_frac; - } - } else { - // Time to un-defer any trailing zeros. Intg zeros first. - while (trailing_zeros_intg) { - mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_INTG); - --trailing_zeros_intg; - } - while (trailing_zeros_frac) { - mantissa = accept_digit(mantissa, 0, &exp_extra, PARSE_DEC_IN_FRAC); - --trailing_zeros_frac; - } - mantissa = accept_digit(mantissa, dig, &exp_extra, in); - } - } - } else if (in == PARSE_DEC_IN_INTG && dig == '.') { - in = PARSE_DEC_IN_FRAC; - } else if (in != PARSE_DEC_IN_EXP && ((dig | 0x20) == 'e')) { - in = PARSE_DEC_IN_EXP; - if (str < top) { - if (str[0] == '+') { - str++; - } else if (str[0] == '-') { - str++; - exp_neg = true; - } - } - if (str == top) { - goto value_error; - } - } else if (dig == '_') { - continue; - } else { - // unknown character - str--; - break; - } - } - - // work out the exponent - if (exp_neg) { - exp_val = -exp_val; - } - - // apply the exponent, making sure it's not a subnormal value - exp_val += exp_extra + trailing_zeros_intg; - dec_val = (mp_float_t)mantissa; - if (exp_val < SMALL_NORMAL_EXP) { - exp_val -= SMALL_NORMAL_EXP; - dec_val *= SMALL_NORMAL_VAL; - } - - // At this point, we need to multiply the mantissa by its base 10 exponent. If possible, - // we would rather manipulate numbers that have an exact representation in IEEE754. It - // turns out small positive powers of 10 do, whereas small negative powers of 10 don't. - // So in that case, we'll yield a division of exact values rather than a multiplication - // of slightly erroneous values. - if (exp_val < 0 && exp_val >= -EXACT_POWER_OF_10) { - dec_val /= MICROPY_FLOAT_C_FUN(pow)(10, -exp_val); - } else { - dec_val *= MICROPY_FLOAT_C_FUN(pow)(10, exp_val); + str = mp_parse_float_internal(str, top - str, &dec_val); + if (!str) { + goto value_error; } } diff --git a/py/parsenum.h b/py/parsenum.h index f444632d23021..d532cb194a5d8 100644 --- a/py/parsenum.h +++ b/py/parsenum.h @@ -34,6 +34,11 @@ mp_obj_t mp_parse_num_integer(const char *restrict str, size_t len, int base, mp_lexer_t *lex); +#if MICROPY_PY_BUILTINS_FLOAT +mp_large_float_t mp_decimal_exp(mp_large_float_t num, int dec_exp); +const char *mp_parse_float_internal(const char *str, size_t len, mp_float_t *res); +#endif + #if MICROPY_PY_BUILTINS_COMPLEX mp_obj_t mp_parse_num_decimal(const char *str, size_t len, bool allow_imag, bool force_complex, mp_lexer_t *lex); diff --git a/py/persistentcode.c b/py/persistentcode.c index 1976729f4d879..5c3de3174bcd0 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -63,6 +63,10 @@ typedef struct _bytecode_prelude_t { uint code_info_size; } bytecode_prelude_t; +#if MICROPY_EMIT_RV32 +#include "py/asmrv32.h" +#endif + #endif // MICROPY_PERSISTENT_CODE_LOAD || MICROPY_PERSISTENT_CODE_SAVE #if MICROPY_PERSISTENT_CODE_LOAD @@ -72,6 +76,8 @@ typedef struct _bytecode_prelude_t { static int read_byte(mp_reader_t *reader); static size_t read_uint(mp_reader_t *reader); +#if MICROPY_EMIT_MACHINE_CODE + #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA // An mp_obj_list_t that tracks native text/BSS/rodata to prevent the GC from reclaiming them. @@ -86,8 +92,6 @@ static void track_root_pointer(void *ptr) { #endif -#if MICROPY_EMIT_MACHINE_CODE - typedef struct _reloc_info_t { mp_reader_t *reader; mp_module_context_t *context; @@ -422,15 +426,17 @@ static mp_raw_code_t *load_raw_code(mp_reader_t *reader, mp_module_context_t *co // Relocate and commit code to executable address space reloc_info_t ri = {reader, context, rodata, bss}; + #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA + if (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { + // Track the function data memory so it's not reclaimed by the GC. + track_root_pointer(fun_data); + } + #endif #if defined(MP_PLAT_COMMIT_EXEC) void *opt_ri = (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) ? &ri : NULL; fun_data = MP_PLAT_COMMIT_EXEC(fun_data, fun_data_len, opt_ri); #else if (native_scope_flags & MP_SCOPE_FLAG_VIPERRELOC) { - #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA - // Track the function data memory so it's not reclaimed by the GC. - track_root_pointer(fun_data); - #endif // Do the relocations. mp_native_relocate(&ri, fun_data, (uintptr_t)fun_data); } @@ -471,13 +477,16 @@ void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { read_bytes(reader, header, sizeof(header)); byte arch = MPY_FEATURE_DECODE_ARCH(header[2]); // CIRCUITPY-CHANGE: 'C', not 'M' + if (header[0] == 'M') { + mp_raise_ValueError(MP_ERROR_TEXT("MicroPython .mpy file; use CircuitPython mpy-cross")); + } if (header[0] != 'C' || header[1] != MPY_VERSION || (arch != MP_NATIVE_ARCH_NONE && MPY_FEATURE_DECODE_SUB_VERSION(header[2]) != MPY_SUB_VERSION) || header[3] > MP_SMALL_INT_BITS) { mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); } - if (MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE) { + if (arch != MP_NATIVE_ARCH_NONE) { if (!MPY_FEATURE_ARCH_TEST(arch)) { if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_NONE)) { // On supported ports this can be resolved by enabling feature, eg @@ -489,6 +498,23 @@ void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { } } + size_t arch_flags = 0; + if (MPY_FEATURE_ARCH_FLAGS_TEST(header[2])) { + #if MICROPY_EMIT_RV32 + arch_flags = read_uint(reader); + + if (MPY_FEATURE_ARCH_TEST(MP_NATIVE_ARCH_RV32IMC)) { + if ((arch_flags & (size_t)asm_rv32_allowed_extensions()) != arch_flags) { + mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); + } + } else + #endif + { + (void)arch_flags; + mp_raise_ValueError(MP_ERROR_TEXT("incompatible .mpy file")); + } + } + size_t n_qstr = read_uint(reader); size_t n_obj = read_uint(reader); mp_module_context_alloc_tables(cm->context, n_qstr, n_obj); @@ -510,6 +536,7 @@ void mp_raw_code_load(mp_reader_t *reader, mp_compiled_module_t *cm) { cm->has_native = MPY_FEATURE_DECODE_ARCH(header[2]) != MP_NATIVE_ARCH_NONE; cm->n_qstr = n_qstr; cm->n_obj = n_obj; + cm->arch_flags = arch_flags; #endif // Deregister exception handler and close the reader. @@ -679,7 +706,7 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { byte header[4] = { 'C', MPY_VERSION, - cm->has_native ? MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC) : 0, + (cm->arch_flags != 0 ? MPY_FEATURE_ARCH_FLAGS : 0) | (cm->has_native ? MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH_DYNAMIC) : 0), #if MICROPY_DYNAMIC_COMPILER mp_dynamic_compiler.small_int_bits, #else @@ -688,6 +715,10 @@ void mp_raw_code_save(mp_compiled_module_t *cm, mp_print_t *print) { }; mp_print_bytes(print, header, sizeof(header)); + if (cm->arch_flags) { + mp_print_uint(print, cm->arch_flags); + } + // Number of entries in constant table. mp_print_uint(print, cm->n_qstr); mp_print_uint(print, cm->n_obj); @@ -766,7 +797,7 @@ static void bit_vector_clear(bit_vector_t *self) { static bool bit_vector_is_set(bit_vector_t *self, size_t index) { const size_t bits_size = sizeof(*self->bits) * MP_BITS_PER_BYTE; return index / bits_size < self->alloc - && (self->bits[index / bits_size] & (1 << (index % bits_size))) != 0; + && (self->bits[index / bits_size] & ((uintptr_t)1 << (index % bits_size))) != 0; } static void bit_vector_set(bit_vector_t *self, size_t index) { @@ -777,7 +808,7 @@ static void bit_vector_set(bit_vector_t *self, size_t index) { self->bits = m_renew(uintptr_t, self->bits, self->alloc, new_alloc); self->alloc = new_alloc; } - self->bits[index / bits_size] |= 1 << (index % bits_size); + self->bits[index / bits_size] |= (uintptr_t)1 << (index % bits_size); } typedef struct _mp_opcode_t { diff --git a/py/persistentcode.h b/py/persistentcode.h index 46b474e57f707..85668e608c85b 100644 --- a/py/persistentcode.h +++ b/py/persistentcode.h @@ -50,7 +50,7 @@ // Macros to encode/decode native architecture to/from the feature byte #define MPY_FEATURE_ENCODE_ARCH(arch) ((arch) << 2) -#define MPY_FEATURE_DECODE_ARCH(feat) ((feat) >> 2) +#define MPY_FEATURE_DECODE_ARCH(feat) (((feat) >> 2) & 0x2F) // Define the host architecture #if MICROPY_EMIT_X86 @@ -90,6 +90,10 @@ #define MPY_FILE_HEADER_INT (MPY_VERSION \ | (MPY_FEATURE_ENCODE_SUB_VERSION(MPY_SUB_VERSION) | MPY_FEATURE_ENCODE_ARCH(MPY_FEATURE_ARCH)) << 8) +// Architecture-specific flags are present in the .mpy file +#define MPY_FEATURE_ARCH_FLAGS (0x40) +#define MPY_FEATURE_ARCH_FLAGS_TEST(x) (((x) & MPY_FEATURE_ARCH_FLAGS) == MPY_FEATURE_ARCH_FLAGS) + enum { MP_NATIVE_ARCH_NONE = 0, MP_NATIVE_ARCH_X86, @@ -103,6 +107,7 @@ enum { MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN, MP_NATIVE_ARCH_RV32IMC, + MP_NATIVE_ARCH_RV64IMC, MP_NATIVE_ARCH_DEBUG, // this entry should always be last }; diff --git a/py/profile.c b/py/profile.c index 397d0291f9fad..b5a0c54728c97 100644 --- a/py/profile.c +++ b/py/profile.c @@ -80,7 +80,7 @@ static void frame_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t "", frame, MP_CODE_QSTR_MAP(code->context, 0), - frame->lineno, + (int)frame->lineno, MP_CODE_QSTR_MAP(code->context, prelude->qstr_block_name_idx) ); } diff --git a/py/py.cmake b/py/py.cmake index 6c180ae53e6f2..b99d2d24b5db3 100644 --- a/py/py.cmake +++ b/py/py.cmake @@ -58,6 +58,7 @@ set(MICROPY_SOURCE_PY ${MICROPY_PY_DIR}/mpz.c ${MICROPY_PY_DIR}/nativeglue.c ${MICROPY_PY_DIR}/nlr.c + ${MICROPY_PY_DIR}/nlraarch64.c ${MICROPY_PY_DIR}/nlrmips.c ${MICROPY_PY_DIR}/nlrpowerpc.c ${MICROPY_PY_DIR}/nlrrv32.c diff --git a/py/py.mk b/py/py.mk index c05327bf81d55..21be07c794fb1 100644 --- a/py/py.mk +++ b/py/py.mk @@ -191,6 +191,7 @@ PY_CORE_O_BASENAME = $(addprefix py/,\ objnamedtuple.o \ objrange.o \ objreversed.o \ + objringio.o \ objset.o \ objsingleton.o \ objslice.o \ @@ -289,7 +290,7 @@ $(HEADER_BUILD)/compressed_translations.generated.h: $(PY_SRC)/maketranslationda PY_CORE_O += $(PY_BUILD)/translations-$(TRANSLATION).o # build a list of registered modules for py/objmodule.c. -$(HEADER_BUILD)/moduledefs.h: $(HEADER_BUILD)/moduledefs.collected +$(HEADER_BUILD)/moduledefs.h: $(HEADER_BUILD)/moduledefs.collected $(PY_SRC)/makemoduledefs.py @$(ECHO) "GEN $@" $(Q)$(PYTHON) $(PY_SRC)/makemoduledefs.py $< > $@ diff --git a/py/repl.c b/py/repl.c index c9a20305c79ff..bbfdd2ea6a6e2 100644 --- a/py/repl.c +++ b/py/repl.c @@ -181,8 +181,11 @@ static bool test_qstr(mp_obj_t obj, qstr name) { return dest[0] != MP_OBJ_NULL; } else { // try builtin module - return mp_map_lookup((mp_map_t *)&mp_builtin_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP) || - mp_map_lookup((mp_map_t *)&mp_builtin_extensible_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP); + return mp_map_lookup((mp_map_t *)&mp_builtin_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP) + #if MICROPY_HAVE_REGISTERED_EXTENSIBLE_MODULES + || mp_map_lookup((mp_map_t *)&mp_builtin_extensible_module_map, MP_OBJ_NEW_QSTR(name), MP_MAP_LOOKUP) + #endif + ; } } @@ -237,6 +240,10 @@ static void print_completions(const mp_print_t *print, for (qstr q = q_first; q <= q_last; ++q) { size_t d_len; const char *d_str = (const char *)qstr_data(q, &d_len); + // filter out words that begin with underscore unless there's already a partial match + if (s_len == 0 && d_str[0] == '_') { + continue; + } if (s_len <= d_len && strncmp(s_start, d_str, s_len) == 0) { if (test_qstr(obj, q)) { int gap = (line_len + WORD_SLOT_LEN - 1) / WORD_SLOT_LEN * WORD_SLOT_LEN - line_len; diff --git a/py/runtime.c b/py/runtime.c index 9def98380fa45..81abc3de87570 100644 --- a/py/runtime.c +++ b/py/runtime.c @@ -134,7 +134,7 @@ void mp_init(void) { MP_STATE_VM(mp_module_builtins_override_dict) = NULL; #endif - #if MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA + #if MICROPY_EMIT_MACHINE_CODE && (MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA || MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA) MP_STATE_VM(persistent_code_root_pointers) = MP_OBJ_NULL; #endif @@ -446,7 +446,7 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs // Operations that can overflow: // + result always fits in mp_int_t, then handled by SMALL_INT check // - result always fits in mp_int_t, then handled by SMALL_INT check - // * checked explicitly + // * checked explicitly for fit in mp_int_t, then handled by SMALL_INT check // / if lhs=MIN and rhs=-1; result always fits in mp_int_t, then handled by SMALL_INT check // % if lhs=MIN and rhs=-1; result always fits in mp_int_t, then handled by SMALL_INT check // << checked explicitly @@ -505,30 +505,16 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs break; case MP_BINARY_OP_MULTIPLY: case MP_BINARY_OP_INPLACE_MULTIPLY: { - - // If long long type exists and is larger than mp_int_t, then - // we can use the following code to perform overflow-checked multiplication. - // Otherwise (eg in x64 case) we must use mp_small_int_mul_overflow. - #if 0 - // compute result using long long precision - long long res = (long long)lhs_val * (long long)rhs_val; - if (res > MP_SMALL_INT_MAX || res < MP_SMALL_INT_MIN) { - // result overflowed SMALL_INT, so return higher precision integer - return mp_obj_new_int_from_ll(res); - } else { - // use standard precision - lhs_val = (mp_int_t)res; - } - #endif - - if (mp_small_int_mul_overflow(lhs_val, rhs_val)) { + mp_int_t int_res; + if (mp_mul_mp_int_t_overflow(lhs_val, rhs_val, &int_res)) { // use higher precision lhs = mp_obj_new_int_from_ll(lhs_val); goto generic_binary_op; } else { // use standard precision - return MP_OBJ_NEW_SMALL_INT(lhs_val * rhs_val); + lhs_val = int_res; } + break; } case MP_BINARY_OP_FLOOR_DIVIDE: case MP_BINARY_OP_INPLACE_FLOOR_DIVIDE: @@ -568,19 +554,19 @@ mp_obj_t MICROPY_WRAP_MP_BINARY_OP(mp_binary_op)(mp_binary_op_t op, mp_obj_t lhs mp_int_t ans = 1; while (rhs_val > 0) { if (rhs_val & 1) { - if (mp_small_int_mul_overflow(ans, lhs_val)) { + if (mp_mul_mp_int_t_overflow(ans, lhs_val, &ans)) { goto power_overflow; } - ans *= lhs_val; } if (rhs_val == 1) { break; } rhs_val /= 2; - if (mp_small_int_mul_overflow(lhs_val, lhs_val)) { + mp_int_t int_res; + if (mp_mul_mp_int_t_overflow(lhs_val, lhs_val, &int_res)) { goto power_overflow; } - lhs_val *= lhs_val; + lhs_val = int_res; } lhs_val = ans; } @@ -976,7 +962,7 @@ mp_obj_t mp_call_method_n_kw_var(bool have_self, size_t n_args_n_kw, const mp_ob // unpacked items are stored in reverse order into the array pointed to by items // CIRCUITPY-CHANGE: noline -void __attribute__((noinline, )) mp_unpack_sequence(mp_obj_t seq_in, size_t num, mp_obj_t *items) { +MP_NOINLINE void mp_unpack_sequence(mp_obj_t seq_in, size_t num, mp_obj_t *items) { size_t seq_len; if (mp_obj_is_type(seq_in, &mp_type_tuple) || mp_obj_is_type(seq_in, &mp_type_list)) { mp_obj_t *seq_items; @@ -1301,6 +1287,19 @@ void mp_load_method(mp_obj_t base, qstr attr, mp_obj_t *dest) { mp_raise_msg_varg(&mp_type_AttributeError, MP_ERROR_TEXT("type object '%q' has no attribute '%q'"), ((mp_obj_type_t *)MP_OBJ_TO_PTR(base))->name, attr); + #if MICROPY_MODULE___ALL__ && MICROPY_ERROR_REPORTING >= MICROPY_ERROR_REPORTING_DETAILED + } else if (mp_obj_is_type(base, &mp_type_module)) { + // report errors in __all__ as done by CPython + mp_obj_t dest_name[2]; + qstr module_name = MP_QSTR_; + mp_load_method_maybe(base, MP_QSTR___name__, dest_name); + if (mp_obj_is_qstr(dest_name[0])) { + module_name = mp_obj_str_get_qstr(dest_name[0]); + } + mp_raise_msg_varg(&mp_type_AttributeError, + MP_ERROR_TEXT("module '%q' has no attribute '%q'"), + module_name, attr); + #endif } else { mp_raise_msg_varg(&mp_type_AttributeError, MP_ERROR_TEXT("'%s' object has no attribute '%q'"), @@ -1619,7 +1618,7 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) { // build args array mp_obj_t args[5]; args[0] = MP_OBJ_NEW_QSTR(name); - args[1] = mp_const_none; // TODO should be globals + args[1] = MP_OBJ_FROM_PTR(mp_globals_get()); // globals of the current context args[2] = mp_const_none; // TODO should be locals args[3] = fromlist; args[4] = level; @@ -1639,20 +1638,17 @@ mp_obj_t mp_import_name(qstr name, mp_obj_t fromlist, mp_obj_t level) { } // CIRCUITPY-CHANGE: noinline -mp_obj_t __attribute__((noinline, )) mp_import_from(mp_obj_t module, qstr name) { +MP_NOINLINE mp_obj_t mp_import_from(mp_obj_t module, qstr name) { DEBUG_printf("import from %p %s\n", module, qstr_str(name)); mp_obj_t dest[2]; mp_load_method_maybe(module, name, dest); - if (dest[1] != MP_OBJ_NULL) { - // Hopefully we can't import bound method from an object - import_error: - mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("can't import name %q"), name); - } - - if (dest[0] != MP_OBJ_NULL) { + // Importing a bound method from a class instance. + return mp_obj_new_bound_meth(dest[0], dest[1]); + } else if (dest[0] != MP_OBJ_NULL) { + // Importing a function or attribute. return dest[0]; } @@ -1685,13 +1681,36 @@ mp_obj_t __attribute__((noinline, )) mp_import_from(mp_obj_t module, qstr name) goto import_error; #endif + +import_error: + mp_raise_msg_varg(&mp_type_ImportError, MP_ERROR_TEXT("can't import name %q"), name); } void mp_import_all(mp_obj_t module) { DEBUG_printf("import all %p\n", module); - // TODO: Support __all__ mp_map_t *map = &mp_obj_module_get_globals(module)->map; + + #if MICROPY_MODULE___ALL__ + mp_map_elem_t *elem = mp_map_lookup(map, MP_OBJ_NEW_QSTR(MP_QSTR___all__), MP_MAP_LOOKUP); + if (elem != NULL) { + // When __all__ is defined, we must explicitly load all specified + // symbols, possibly invoking the module __getattr__ function + size_t len; + mp_obj_t *items; + mp_obj_get_array(elem->value, &len, &items); + for (size_t i = 0; i < len; i++) { + qstr qname = mp_obj_str_get_qstr(items[i]); + mp_obj_t dest[2]; + mp_load_method(module, qname, dest); + mp_store_name(qname, dest[0]); + } + return; + } + #endif + + // By default, the set of public names includes all names found in the module's + // namespace which do not begin with an underscore character ('_') for (size_t i = 0; i < map->alloc; i++) { if (mp_map_slot_is_filled(map, i)) { // Entry in module global scope may be generated programmatically @@ -1747,7 +1766,7 @@ mp_obj_t mp_parse_compile_execute(mp_lexer_t *lex, mp_parse_input_kind_t parse_i #endif // MICROPY_ENABLE_COMPILER // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void m_malloc_fail(size_t num_bytes) { +MP_COLD MP_NORETURN void m_malloc_fail(size_t num_bytes) { DEBUG_printf("memory allocation failed, allocating %u bytes\n", (uint)num_bytes); #if MICROPY_ENABLE_GC if (gc_is_locked()) { @@ -1761,29 +1780,29 @@ NORETURN MP_COLD void m_malloc_fail(size_t num_bytes) { #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_type(const mp_obj_type_t *exc_type) { +MP_COLD MP_NORETURN void mp_raise_type(const mp_obj_type_t *exc_type) { nlr_raise(mp_obj_new_exception(exc_type)); } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_ValueError_no_msg(void) { +MP_COLD MP_NORETURN void mp_raise_ValueError_no_msg(void) { mp_raise_type(&mp_type_ValueError); } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_TypeError_no_msg(void) { +MP_COLD MP_NORETURN void mp_raise_TypeError_no_msg(void) { mp_raise_type(&mp_type_TypeError); } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_NotImplementedError_no_msg(void) { +MP_COLD MP_NORETURN void mp_raise_NotImplementedError_no_msg(void) { mp_raise_type(&mp_type_NotImplementedError); } #else // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg) { if (msg == NULL) { nlr_raise(mp_obj_new_exception(exc_type)); } else { @@ -1792,24 +1811,24 @@ NORETURN MP_COLD void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_t } // CIRCUITPY-CHANGE: new function for use below. -NORETURN MP_COLD void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list argptr) { +MP_COLD MP_NORETURN void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list argptr) { mp_obj_t exception = mp_obj_new_exception_msg_vlist(exc_type, fmt, argptr); nlr_raise(exception); } // CIRCUITPY-CHANGE: MP_COLD and use mp_raise_msg_vlist() -NORETURN MP_COLD void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(exc_type, fmt, argptr); va_end(argptr); } -NORETURN MP_COLD void mp_raise_ValueError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_ValueError, msg); } -NORETURN MP_COLD void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg) { +MP_COLD MP_NORETURN void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg) { if (msg == NULL) { nlr_raise(mp_obj_new_exception(exc_type)); } else { @@ -1818,17 +1837,17 @@ NORETURN MP_COLD void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_AttributeError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_AttributeError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_AttributeError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_RuntimeError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_RuntimeError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_RuntimeError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_RuntimeError, fmt, argptr); @@ -1836,17 +1855,17 @@ NORETURN MP_COLD void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_ImportError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_ImportError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_ImportError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_IndexError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_IndexError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_IndexError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_IndexError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_IndexError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_IndexError, fmt, argptr); @@ -1854,7 +1873,7 @@ NORETURN MP_COLD void mp_raise_IndexError_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_ValueError, fmt, argptr); @@ -1862,12 +1881,12 @@ NORETURN MP_COLD void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_TypeError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_TypeError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_TypeError, fmt, argptr); @@ -1875,12 +1894,12 @@ NORETURN MP_COLD void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_OSError_msg(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_OSError_msg(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_OSError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_OSError_errno_str(int errno_, mp_obj_t str) { +MP_COLD MP_NORETURN void mp_raise_OSError_errno_str(int errno_, mp_obj_t str) { mp_obj_t args[2] = { MP_OBJ_NEW_SMALL_INT(errno_), str, @@ -1889,7 +1908,7 @@ NORETURN MP_COLD void mp_raise_OSError_errno_str(int errno_, mp_obj_t str) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_OSError, fmt, argptr); @@ -1897,21 +1916,23 @@ NORETURN MP_COLD void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...) { } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_ConnectionError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_ConnectionError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_ConnectionError, msg); } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_BrokenPipeError(void) { +MP_COLD MP_NORETURN void mp_raise_BrokenPipeError(void) { mp_raise_type_arg(&mp_type_BrokenPipeError, MP_OBJ_NEW_SMALL_INT(MP_EPIPE)); } -NORETURN MP_COLD void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { +MP_COLD MP_NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_NotImplementedError, msg); } +#endif + // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_NotImplementedError, fmt, argptr); @@ -1919,7 +1940,7 @@ NORETURN MP_COLD void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...) { +MP_COLD MP_NORETURN void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_raise_msg_vlist(&mp_type_OverflowError, fmt, argptr); @@ -1927,11 +1948,11 @@ NORETURN MP_COLD void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...) } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg) { +MP_COLD MP_NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg) { nlr_raise(mp_obj_new_exception_arg1(exc_type, arg)); } -NORETURN void mp_raise_StopIteration(mp_obj_t arg) { +MP_NORETURN void mp_raise_StopIteration(mp_obj_t arg) { if (arg == MP_OBJ_NULL) { mp_raise_type(&mp_type_StopIteration); } else { @@ -1939,7 +1960,7 @@ NORETURN void mp_raise_StopIteration(mp_obj_t arg) { } } -NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) { +MP_NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) { #if MICROPY_ERROR_REPORTING <= MICROPY_ERROR_REPORTING_TERSE (void)arg; mp_raise_TypeError(MP_ERROR_TEXT("can't convert to int")); @@ -1950,12 +1971,12 @@ NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg) { } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_OSError(int errno_) { +MP_COLD MP_NORETURN void mp_raise_OSError(int errno_) { mp_raise_type_arg(&mp_type_OSError, MP_OBJ_NEW_SMALL_INT(errno_)); } // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_OSError_with_filename(int errno_, const char *filename) { +MP_COLD MP_NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename) { vstr_t vstr; vstr_init(&vstr, 32); vstr_printf(&vstr, "can't open %s", filename); @@ -1965,13 +1986,12 @@ NORETURN MP_COLD void mp_raise_OSError_with_filename(int errno_, const char *fil } // CIRCUITPY-CHANGE: added -NORETURN MP_COLD void mp_raise_ZeroDivisionError(void) { +MP_COLD MP_NORETURN void mp_raise_ZeroDivisionError(void) { mp_raise_msg(&mp_type_ZeroDivisionError, MP_ERROR_TEXT("division by zero")); } #if MICROPY_STACK_CHECK || MICROPY_ENABLE_PYSTACK // CIRCUITPY-CHANGE: MP_COLD -NORETURN MP_COLD void mp_raise_recursion_depth(void) { +MP_COLD MP_NORETURN void mp_raise_recursion_depth(void) { mp_raise_RuntimeError(MP_ERROR_TEXT("maximum recursion depth exceeded")); } #endif -#endif diff --git a/py/runtime.h b/py/runtime.h index a2d69638ffc38..22ed9c3eaf9b8 100644 --- a/py/runtime.h +++ b/py/runtime.h @@ -37,11 +37,12 @@ #include "supervisor/shared/translate/translate.h" // For use with mp_call_function_1_from_nlr_jump_callback. +// Initialize an nlr_jump_callback_node_call_function_1_t struct for use with +// nlr_push_jump_callback(&ctx.callback, mp_call_function_1_from_nlr_jump_callback); #define MP_DEFINE_NLR_JUMP_CALLBACK_FUNCTION_1(ctx, f, a) \ - nlr_jump_callback_node_call_function_1_t ctx = { \ - .func = (void (*)(void *))(f), \ - .arg = (a), \ - } + nlr_jump_callback_node_call_function_1_t ctx; \ + ctx.func = (void (*)(void *))(f); \ + ctx.arg = (a) typedef enum { MP_VM_RETURN_NORMAL, @@ -58,6 +59,12 @@ typedef enum { MP_ARG_KW_ONLY = 0x200, } mp_arg_flag_t; +typedef enum { + MP_HANDLE_PENDING_CALLBACKS_ONLY, + MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS, + MP_HANDLE_PENDING_CALLBACKS_AND_CLEAR_EXCEPTIONS, +} mp_handle_pending_behaviour_t; + typedef union _mp_arg_val_t { bool u_bool; mp_int_t u_int; @@ -106,7 +113,14 @@ void mp_sched_keyboard_interrupt(void); #if MICROPY_ENABLE_VM_ABORT void mp_sched_vm_abort(void); #endif -void mp_handle_pending(bool raise_exc); + +void mp_handle_pending_internal(mp_handle_pending_behaviour_t behavior); + +static inline void mp_handle_pending(bool raise_exc) { + mp_handle_pending_internal(raise_exc ? + MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS : + MP_HANDLE_PENDING_CALLBACKS_AND_CLEAR_EXCEPTIONS); +} #if MICROPY_ENABLE_SCHEDULER void mp_sched_lock(void); @@ -134,7 +148,7 @@ void mp_event_wait_indefinite(void); void mp_event_wait_ms(mp_uint_t timeout_ms); // extra printing method specifically for mp_obj_t's which are integral type -int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, int base, int base_char, int flags, char fill, int width, int prec); +int mp_print_mp_int(const mp_print_t *print, mp_obj_t x, unsigned base, int base_char, int flags, char fill, int width, int prec); void mp_arg_check_num_sig(size_t n_args, size_t n_kw, uint32_t sig); static inline void mp_arg_check_num(size_t n_args, size_t n_kw, size_t n_args_min, size_t n_args_max, bool takes_kw) { @@ -142,11 +156,11 @@ static inline void mp_arg_check_num(size_t n_args, size_t n_kw, size_t n_args_mi } void mp_arg_parse_all(size_t n_pos, const mp_obj_t *pos, mp_map_t *kws, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals); void mp_arg_parse_all_kw_array(size_t n_pos, size_t n_kw, const mp_obj_t *args, size_t n_allowed, const mp_arg_t *allowed, mp_arg_val_t *out_vals); -NORETURN void mp_arg_error_terse_mismatch(void); -NORETURN void mp_arg_error_unimpl_kw(void); +MP_NORETURN void mp_arg_error_terse_mismatch(void); +MP_NORETURN void mp_arg_error_unimpl_kw(void); // CIRCUITPY-CHANGE: arg validation routines -NORETURN void mp_arg_error_invalid(qstr arg_name); +MP_NORETURN void mp_arg_error_invalid(qstr arg_name); mp_int_t mp_arg_validate_int(mp_int_t i, mp_int_t required_i, qstr arg_name); mp_int_t mp_arg_validate_int_min(mp_int_t i, mp_int_t min, qstr arg_name); mp_int_t mp_arg_validate_int_max(mp_int_t i, mp_int_t j, qstr arg_name); @@ -194,9 +208,16 @@ static inline void mp_thread_init_state(mp_state_thread_t *ts, size_t stack_size ts->gc_lock_depth = 0; // There are no pending jump callbacks or exceptions yet + ts->nlr_top = NULL; ts->nlr_jump_callback_top = NULL; ts->mp_pending_exception = MP_OBJ_NULL; + #if MICROPY_PY_SYS_SETTRACE + ts->prof_trace_callback = MP_OBJ_NULL; + ts->prof_callback_is_executing = false; + ts->current_code_state = NULL; + #endif + // If locals/globals are not given, inherit from main thread if (locals == NULL) { locals = mp_state_ctx.thread.dict_locals; @@ -274,10 +295,10 @@ mp_obj_t mp_import_from(mp_obj_t module, qstr name); void mp_import_all(mp_obj_t module); #if MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_NONE -NORETURN void mp_raise_type(const mp_obj_type_t *exc_type); -NORETURN void mp_raise_ValueError_no_msg(void); -NORETURN void mp_raise_TypeError_no_msg(void); -NORETURN void mp_raise_NotImplementedError_no_msg(void); +MP_NORETURN void mp_raise_type(const mp_obj_type_t *exc_type); +MP_NORETURN void mp_raise_ValueError_no_msg(void); +MP_NORETURN void mp_raise_TypeError_no_msg(void); +MP_NORETURN void mp_raise_NotImplementedError_no_msg(void); #define mp_raise_msg(exc_type, msg) mp_raise_type(exc_type) #define mp_raise_msg_varg(exc_type, ...) mp_raise_type(exc_type) #define mp_raise_ValueError(msg) mp_raise_ValueError_no_msg() @@ -285,46 +306,46 @@ NORETURN void mp_raise_NotImplementedError_no_msg(void); #define mp_raise_NotImplementedError(msg) mp_raise_NotImplementedError_no_msg() #else #define mp_raise_type(exc_type) mp_raise_msg(exc_type, NULL) -NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); -NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); -NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); -NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); #endif // CIRCUITPY-CHANGE: new mp_raise routines -NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg); -NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); -NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt +MP_NORETURN void mp_raise_type_arg(const mp_obj_type_t *exc_type, mp_obj_t arg); +MP_NORETURN void mp_raise_msg(const mp_obj_type_t *exc_type, mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_msg_varg(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt , ...); -NORETURN void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list argptr); +MP_NORETURN void mp_raise_msg_vlist(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, va_list argptr); // Only use this string version in native mpy files. Otherwise, use the compressed string version. -NORETURN void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg); - -NORETURN void mp_raise_AttributeError(mp_rom_error_text_t msg); -NORETURN void mp_raise_BrokenPipeError(void); -NORETURN void mp_raise_ConnectionError(mp_rom_error_text_t msg); -NORETURN void mp_raise_ImportError(mp_rom_error_text_t msg); -NORETURN void mp_raise_IndexError(mp_rom_error_text_t msg); -NORETURN void mp_raise_IndexError_varg(mp_rom_error_text_t msg, ...); -NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); -NORETURN void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_OSError_errno_str(int errno_, mp_obj_t str); -NORETURN void mp_raise_OSError(int errno_); -NORETURN void mp_raise_OSError_msg(mp_rom_error_text_t msg); -NORETURN void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename); -NORETURN void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_recursion_depth(void); -NORETURN void mp_raise_RuntimeError(mp_rom_error_text_t msg); -NORETURN void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_StopIteration(mp_obj_t arg); -NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); -NORETURN void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg); -NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); -NORETURN void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_ZeroDivisionError(void); +MP_NORETURN void mp_raise_msg_str(const mp_obj_type_t *exc_type, const char *msg); + +MP_NORETURN void mp_raise_AttributeError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_BrokenPipeError(void); +MP_NORETURN void mp_raise_ConnectionError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_ImportError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_IndexError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_IndexError_varg(mp_rom_error_text_t msg, ...); +MP_NORETURN void mp_raise_NotImplementedError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_NotImplementedError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_OSError_errno_str(int errno_, mp_obj_t str); +MP_NORETURN void mp_raise_OSError(int errno_); +MP_NORETURN void mp_raise_OSError_msg(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_OSError_msg_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_OSError_with_filename(int errno_, const char *filename); +MP_NORETURN void mp_raise_OverflowError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_recursion_depth(void); +MP_NORETURN void mp_raise_RuntimeError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_RuntimeError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_StopIteration(mp_obj_t arg); +MP_NORETURN void mp_raise_TypeError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_TypeError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_TypeError_int_conversion(mp_const_obj_t arg); +MP_NORETURN void mp_raise_ValueError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_ValueError_varg(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_ZeroDivisionError(void); #if MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG #undef mp_check_self diff --git a/py/runtime_utils.c b/py/runtime_utils.c index 98252c3122048..33da558e30f08 100644 --- a/py/runtime_utils.c +++ b/py/runtime_utils.c @@ -53,3 +53,59 @@ mp_obj_t mp_call_function_2_protected(mp_obj_t fun, mp_obj_t arg1, mp_obj_t arg2 return MP_OBJ_NULL; } } + +#if !MICROPY_USE_GCC_MUL_OVERFLOW_INTRINSIC +bool mp_mul_ll_overflow(long long int x, long long int y, long long int *res) { + bool overflow; + + // Check for multiply overflow; see CERT INT32-C + if (x > 0) { // x is positive + if (y > 0) { // x and y are positive + overflow = (x > (LLONG_MAX / y)); + } else { // x positive, y nonpositive + overflow = (y < (LLONG_MIN / x)); + } // x positive, y nonpositive + } else { // x is nonpositive + if (y > 0) { // x is nonpositive, y is positive + overflow = (x < (LLONG_MIN / y)); + } else { // x and y are nonpositive + overflow = (x != 0 && y < (LLONG_MAX / x)); + } // End if x and y are nonpositive + } // End if x is nonpositive + + if (!overflow) { + *res = x * y; + } + + return overflow; +} + +bool mp_mul_mp_int_t_overflow(mp_int_t x, mp_int_t y, mp_int_t *res) { + // Check for multiply overflow; see CERT INT32-C + if (x > 0) { // x is positive + if (y > 0) { // x and y are positive + if (x > (MP_INT_MAX / y)) { + return true; + } + } else { // x positive, y nonpositive + if (y < (MP_INT_MIN / x)) { + return true; + } + } // x positive, y nonpositive + } else { // x is nonpositive + if (y > 0) { // x is nonpositive, y is positive + if (x < (MP_INT_MIN / y)) { + return true; + } + } else { // x and y are nonpositive + if (x != 0 && y < (MP_INT_MAX / x)) { + return true; + } + } // End if x and y are nonpositive + } // End if x is nonpositive + + // Result doesn't overflow + *res = x * y; + return false; +} +#endif diff --git a/py/scheduler.c b/py/scheduler.c index 5984346a6f68a..ee9cd96b331a6 100644 --- a/py/scheduler.c +++ b/py/scheduler.c @@ -97,17 +97,21 @@ static inline void mp_sched_run_pending(void) { #if MICROPY_SCHEDULER_STATIC_NODES // Run all pending C callbacks. - while (MP_STATE_VM(sched_head) != NULL) { - mp_sched_node_t *node = MP_STATE_VM(sched_head); - MP_STATE_VM(sched_head) = node->next; - if (MP_STATE_VM(sched_head) == NULL) { - MP_STATE_VM(sched_tail) = NULL; - } - mp_sched_callback_t callback = node->callback; - node->callback = NULL; - MICROPY_END_ATOMIC_SECTION(atomic_state); - callback(node); - atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + mp_sched_node_t *original_tail = MP_STATE_VM(sched_tail); + if (original_tail != NULL) { + mp_sched_node_t *node; + do { + node = MP_STATE_VM(sched_head); + MP_STATE_VM(sched_head) = node->next; + if (MP_STATE_VM(sched_head) == NULL) { + MP_STATE_VM(sched_tail) = NULL; + } + mp_sched_callback_t callback = node->callback; + node->callback = NULL; + MICROPY_END_ATOMIC_SECTION(atomic_state); + callback(node); + atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); + } while (node != original_tail); // Don't execute any callbacks scheduled during this run } #endif @@ -218,24 +222,27 @@ MP_REGISTER_ROOT_POINTER(mp_sched_item_t sched_queue[MICROPY_SCHEDULER_DEPTH]); // Called periodically from the VM or from "waiting" code (e.g. sleep) to // process background tasks and pending exceptions (e.g. KeyboardInterrupt). -void mp_handle_pending(bool raise_exc) { +void mp_handle_pending_internal(mp_handle_pending_behaviour_t behavior) { + bool handle_exceptions = (behavior != MP_HANDLE_PENDING_CALLBACKS_ONLY); + bool raise_exceptions = (behavior == MP_HANDLE_PENDING_CALLBACKS_AND_EXCEPTIONS); + // Handle pending VM abort. #if MICROPY_ENABLE_VM_ABORT - if (MP_STATE_VM(vm_abort) && mp_thread_is_main_thread()) { + if (handle_exceptions && MP_STATE_VM(vm_abort) && mp_thread_is_main_thread()) { MP_STATE_VM(vm_abort) = false; - if (raise_exc && nlr_get_abort() != NULL) { + if (raise_exceptions && nlr_get_abort() != NULL) { nlr_jump_abort(); } } #endif // Handle any pending exception. - if (MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { + if (handle_exceptions && MP_STATE_THREAD(mp_pending_exception) != MP_OBJ_NULL) { mp_uint_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION(); mp_obj_t obj = MP_STATE_THREAD(mp_pending_exception); if (obj != MP_OBJ_NULL) { MP_STATE_THREAD(mp_pending_exception) = MP_OBJ_NULL; - if (raise_exc) { + if (raise_exceptions) { MICROPY_END_ATOMIC_SECTION(atomic_state); nlr_raise(obj); } @@ -277,10 +284,6 @@ void mp_event_wait_indefinite(void) { MICROPY_EVENT_POLL_HOOK #else mp_event_handle_nowait(); - - // CIRCUITPY-CHANGE: don't starve CircuitPython background tasks - RUN_BACKGROUND_TASKS; - MICROPY_INTERNAL_WFE(-1); #endif } @@ -293,10 +296,6 @@ void mp_event_wait_ms(mp_uint_t timeout_ms) { MICROPY_EVENT_POLL_HOOK #else mp_event_handle_nowait(); - - // CIRCUITPY-CHANGE: don't starve CircuitPython background tasks - RUN_BACKGROUND_TASKS; - MICROPY_INTERNAL_WFE(timeout_ms); #endif } diff --git a/py/sequence.c b/py/sequence.c index ac7ad5368b91e..6bbc62f0f7fb1 100644 --- a/py/sequence.c +++ b/py/sequence.c @@ -34,18 +34,13 @@ #define SWAP(type, var1, var2) { type t = var2; var2 = var1; var1 = t; } // CIRCUITPY-CHANGE: detect sequence overflow -#if __GNUC__ < 5 -// n.b. does not actually detect overflow! -#define __builtin_mul_overflow(a, b, x) (*(x) = (a) * (b), false) -#endif - // Detect when a multiply causes an overflow. size_t mp_seq_multiply_len(size_t item_sz, size_t len) { - size_t new_len; - if (__builtin_mul_overflow(item_sz, len, &new_len)) { + mp_int_t new_len; + if (mp_mul_mp_int_t_overflow(item_sz, len, &new_len)) { mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("small int overflow")); } - return new_len; + return (size_t)new_len; } // Implements backend of sequence * integer operation. Assumes elements are diff --git a/py/showbc.c b/py/showbc.c index 6913d18c1ca82..792fccd013383 100644 --- a/py/showbc.c +++ b/py/showbc.c @@ -144,17 +144,9 @@ void mp_bytecode_print(const mp_print_t *print, const mp_raw_code_t *rc, size_t mp_uint_t source_line = 1; mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); for (const byte *ci = code_info; ci < line_info_top;) { - if ((ci[0] & 0x80) == 0) { - // 0b0LLBBBBB encoding - bc += ci[0] & 0x1f; - source_line += ci[0] >> 5; - ci += 1; - } else { - // 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) - bc += ci[0] & 0xf; - source_line += ((ci[0] << 4) & 0x700) | ci[1]; - ci += 2; - } + mp_code_lineinfo_t decoded = mp_bytecode_decode_lineinfo(&ci); + bc += decoded.bc_increment; + source_line += decoded.line_increment; mp_printf(print, " bc=" INT_FMT " line=" UINT_FMT "\n", bc, source_line); } } diff --git a/py/smallint.c b/py/smallint.c index aa542ca7bf29a..eb99b58667a07 100644 --- a/py/smallint.c +++ b/py/smallint.c @@ -26,32 +26,6 @@ #include "py/smallint.h" -bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y) { - // Check for multiply overflow; see CERT INT32-C - if (x > 0) { // x is positive - if (y > 0) { // x and y are positive - if (x > (MP_SMALL_INT_MAX / y)) { - return true; - } - } else { // x positive, y nonpositive - if (y < (MP_SMALL_INT_MIN / x)) { - return true; - } - } // x positive, y nonpositive - } else { // x is nonpositive - if (y > 0) { // x is nonpositive, y is positive - if (x < (MP_SMALL_INT_MIN / y)) { - return true; - } - } else { // x and y are nonpositive - if (x != 0 && y < (MP_SMALL_INT_MAX / x)) { - return true; - } - } // End if x and y are nonpositive - } // End if x is nonpositive - return false; -} - mp_int_t mp_small_int_modulo(mp_int_t dividend, mp_int_t divisor) { // Python specs require that mod has same sign as second operand dividend %= divisor; diff --git a/py/smallint.h b/py/smallint.h index 584e0018d1ba3..ec5b0af3b2867 100644 --- a/py/smallint.h +++ b/py/smallint.h @@ -68,7 +68,6 @@ // The number of bits in a MP_SMALL_INT including the sign bit. #define MP_SMALL_INT_BITS (MP_IMAX_BITS(MP_SMALL_INT_MAX) + 1) -bool mp_small_int_mul_overflow(mp_int_t x, mp_int_t y); mp_int_t mp_small_int_modulo(mp_int_t dividend, mp_int_t divisor); mp_int_t mp_small_int_floor_divide(mp_int_t num, mp_int_t denom); diff --git a/py/stream.c b/py/stream.c index fbf7fd878a133..520422cc92fcf 100644 --- a/py/stream.c +++ b/py/stream.c @@ -260,9 +260,14 @@ void mp_stream_write_adaptor(void *self, const char *buf, size_t len) { mp_stream_write(MP_OBJ_FROM_PTR(self), buf, len, MP_STREAM_RW_WRITE); } -static mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) { +static mp_obj_t stream_readinto_write_generic(size_t n_args, const mp_obj_t *args, byte flags) { mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_READ); + mp_get_buffer_raise(args[1], &bufinfo, (flags & MP_STREAM_RW_WRITE) ? MP_BUFFER_READ : MP_BUFFER_WRITE); + + // CPython extension, allow optional maximum length and offset: + // - stream.operation(buf, max_len) + // - stream.operation(buf, off, max_len) + // Similar to https://docs.python.org/3/library/socket.html#socket.socket.recv_into size_t max_len = (size_t)-1; size_t off = 0; if (n_args == 3) { @@ -275,45 +280,31 @@ static mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) { } } bufinfo.len -= off; - return mp_stream_write(args[0], (byte *)bufinfo.buf + off, MIN(bufinfo.len, max_len), MP_STREAM_RW_WRITE); + + // Perform the readinto or write operation. + return mp_stream_write(args[0], (byte *)bufinfo.buf + off, MIN(bufinfo.len, max_len), flags); +} + +static mp_obj_t stream_write_method(size_t n_args, const mp_obj_t *args) { + return stream_readinto_write_generic(n_args, args, MP_STREAM_RW_WRITE); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj, 2, 4, stream_write_method); -static mp_obj_t stream_write1_method(mp_obj_t self_in, mp_obj_t arg) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(arg, &bufinfo, MP_BUFFER_READ); - return mp_stream_write(self_in, bufinfo.buf, bufinfo.len, MP_STREAM_RW_WRITE | MP_STREAM_RW_ONCE); +static mp_obj_t stream_write1_method(size_t n_args, const mp_obj_t *args) { + return stream_readinto_write_generic(n_args, args, MP_STREAM_RW_WRITE | MP_STREAM_RW_ONCE); } -MP_DEFINE_CONST_FUN_OBJ_2(mp_stream_write1_obj, stream_write1_method); +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write1_obj, 2, 4, stream_write1_method); static mp_obj_t stream_readinto(size_t n_args, const mp_obj_t *args) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[1], &bufinfo, MP_BUFFER_WRITE); - - // CPython extension: if 2nd arg is provided, that's max len to read, - // instead of full buffer. Similar to - // https://docs.python.org/3/library/socket.html#socket.socket.recv_into - mp_uint_t len = bufinfo.len; - if (n_args > 2) { - len = mp_obj_get_int(args[2]); - if (len > bufinfo.len) { - len = bufinfo.len; - } - } - - int error; - mp_uint_t out_sz = mp_stream_read_exactly(args[0], bufinfo.buf, len, &error); - if (error != 0) { - if (mp_is_nonblocking_error(error)) { - return mp_const_none; - } - mp_raise_OSError(error); - } else { - return MP_OBJ_NEW_SMALL_INT(out_sz); - } + return stream_readinto_write_generic(n_args, args, MP_STREAM_RW_READ); } MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto_obj, 2, 3, stream_readinto); +static mp_obj_t stream_readinto1(size_t n_args, const mp_obj_t *args) { + return stream_readinto_write_generic(n_args, args, MP_STREAM_RW_READ | MP_STREAM_RW_ONCE); +} +MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto1_obj, 2, 3, stream_readinto1); + static mp_obj_t stream_readall(mp_obj_t self_in) { const mp_stream_p_t *stream_p = mp_get_stream(self_in); diff --git a/py/stream.h b/py/stream.h index 5e1cd5d441302..6e60e849b5f48 100644 --- a/py/stream.h +++ b/py/stream.h @@ -87,10 +87,11 @@ typedef struct _mp_stream_p_t { MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_read1_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_readinto1_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_unbuffered_readline_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_unbuffered_readlines_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write_obj); -MP_DECLARE_CONST_FUN_OBJ_2(mp_stream_write1_obj); +MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_write1_obj); MP_DECLARE_CONST_FUN_OBJ_1(mp_stream_close_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream___exit___obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(mp_stream_seek_obj); diff --git a/py/vm.c b/py/vm.c index 6fd1778261971..860aa36e39792 100644 --- a/py/vm.c +++ b/py/vm.c @@ -205,6 +205,22 @@ static mp_obj_t get_active_exception(mp_exc_stack_t *exc_sp, mp_exc_stack_t *exc return MP_OBJ_NULL; } +#if MICROPY_PY_BUILTINS_SLICE +// This function is marked "no inline" so it doesn't increase the C stack usage of the main VM function. +MP_NOINLINE static mp_obj_t *build_slice_stack_allocated(byte op, mp_obj_t *sp, mp_obj_t step) { + mp_obj_t stop = sp[2]; + mp_obj_t start = sp[1]; + mp_obj_slice_t slice = { .base = { .type = &mp_type_slice }, .start = start, .stop = stop, .step = step }; + if (op == MP_BC_LOAD_SUBSCR) { + SET_TOP(mp_obj_subscr(TOP(), MP_OBJ_FROM_PTR(&slice), MP_OBJ_SENTINEL)); + } else { // MP_BC_STORE_SUBSCR + mp_obj_subscr(TOP(), MP_OBJ_FROM_PTR(&slice), sp[-1]); + sp -= 2; + } + return sp; +} +#endif + // fastn has items in reverse order (fastn[0] is local[0], fastn[-1] is local[1], etc) // sp points to bottom of stack which grows up // returns: @@ -871,9 +887,20 @@ unwind_jump:; // 3-argument slice includes step step = POP(); } - mp_obj_t stop = POP(); - mp_obj_t start = TOP(); - SET_TOP(mp_obj_new_slice(start, stop, step)); + if ((*ip == MP_BC_LOAD_SUBSCR || *ip == MP_BC_STORE_SUBSCR) + && (mp_obj_get_type(sp[-2])->flags & MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE)) { + // Fast path optimisation for when the BUILD_SLICE is immediately followed + // by a LOAD/STORE_SUBSCR for an accepting type, to avoid needing to allocate + // the slice on the heap. In some cases (e.g. a[1:3] = x) this can result + // in no allocations at all. We can't do this for instance types because + // the get/set/delattr implementation may keep a reference to the slice. + byte op = *ip++; + sp = build_slice_stack_allocated(op, sp - 2, step); + } else { + mp_obj_t stop = POP(); + mp_obj_t start = TOP(); + SET_TOP(mp_obj_new_slice(start, stop, step)); + } DISPATCH(); } #endif diff --git a/py/vstr.c b/py/vstr.c index 00972edf89796..522509d0d05cf 100644 --- a/py/vstr.c +++ b/py/vstr.c @@ -51,7 +51,7 @@ void vstr_init(vstr_t *vstr, size_t alloc) { // Init the vstr so it allocs exactly enough ram to hold a null-terminated // string of the given length, and set the length. void vstr_init_len(vstr_t *vstr, size_t len) { - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: check for invalid length if (len == SIZE_MAX) { m_malloc_fail(len); } diff --git a/pyproject.toml b/pyproject.toml index 0db7eb70a7875..5f84ca104ac47 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,35 +1,26 @@ -# SPDX-FileCopyrightText: 2024 Scott Shawcroft for Adafruit Industries -# -# SPDX-License-Identifier: MIT - -[tool.setuptools_scm] # can be empty if no extra settings are needed, presence enables setuptools-scm - -# Ruff settings copied from MicroPython - [tool.ruff] -target-version = "py37" - +target-version = "py312" line-length = 99 -# Exclude third-party code from linting and formatting. Ruff doesn't know how to ignore submodules. -# Exclude the following tests: -# repl_: not real python files -# viper_args: uses f(*) +# Include Python source files that don't end with .py +extend-include = [ "tools/cc1" ] +# Exclude third-party code from linting and formatting extend-exclude = [ + # CIRCUITPY-CHANGES "extmod/ulab", "frozen", "lib", "ports/analog/msdk", "ports/atmel-samd/asf4", "ports/broadcom/peripherals", + "ports/espress/tools", "ports/espressif/esp-idf", "ports/espressif/esp-protocols", "ports/raspberrypi/sdk", "ports/silabs/gecko_sdk", "ports/silabs/tools/slc_cli_linux", "ports/stm/st_driver", - "tests/*/repl_*.py", - "tests/micropython/viper_args.py", + "tests/cpydiff/syntax_assign_expr.py", # intentionally incorrect CPython code "tools/adabot", "tools/bitmap_font", "tools/cc1", @@ -40,9 +31,32 @@ extend-exclude = [ ] # Exclude third-party code, and exclude the following tests: # basics: needs careful attention before applying automatic formatting -format.exclude = [ "tests/basics/*.py" ] +# repl_: not real python files +# viper_args: uses f(*) +format.exclude = [ + "tests/*/repl_*.py", + "tests/basics/*.py", + "tests/cmdline/cmd_compile_only_error.py", + "tests/micropython/test_normalize_newlines.py", + "tests/micropython/viper_args.py", +] lint.extend-select = [ "C9", "PLC" ] -lint.ignore = [ +lint.exclude = [ + # Ruff finds Python SyntaxError in these files + "tests/cmdline/cmd_compile_only_error.py", + "tests/cmdline/repl_autocomplete.py", + "tests/cmdline/repl_autocomplete_underscore.py", + "tests/cmdline/repl_autoindent.py", + "tests/cmdline/repl_basic.py", + "tests/cmdline/repl_cont.py", + "tests/cmdline/repl_emacs_keys.py", + "tests/cmdline/repl_paste.py", + "tests/cmdline/repl_words_move.py", + "tests/feature_check/repl_emacs_check.py", + "tests/feature_check/repl_words_move_check.py", + "tests/micropython/viper_args.py", +] +lint.extend-ignore = [ "E401", "E402", "E722", @@ -51,11 +65,27 @@ lint.ignore = [ "F401", "F403", "F405", - "PLC1901", + "PLC0206", + "PLC0415", # conditional imports are common in MicroPython ] # manifest.py files are evaluated with some global names pre-defined lint.per-file-ignores."**/manifest.py" = [ "F821" ] lint.per-file-ignores."ports/**/boards/**/manifest_*.py" = [ "F821" ] -# Exclude all tests from linting (does not apply to formatting). +lint.per-file-ignores."ports/cc3200/tools/uniflash.py" = [ "E711" ] +# Exclude all tests from linting. lint.per-file-ignores."tests/**/*.py" = [ "ALL" ] +# Uses assignment expressions. +lint.per-file-ignores."tests/cpydiff/syntax_assign_expr.py" = [ "F821" ] lint.mccabe.max-complexity = 40 + +[tool.codespell] +count = "" +ignore-regex = "\\b[A-Z]{3}\\b" +# CIRCUITPY-CHANGE: aranges +ignore-words-list = "ans,aranges,asend,deques,dout,emac,extint,hsi,iput,mis,notin,numer,ser,shft,synopsys,technic,ure,curren" +quiet-level = 3 +skip = """\ + */build*,./.git,./drivers/cc3100,./lib,./ports/cc3200/FreeRTOS,./ports/cc3200/bootmgr/sl,./ports/cc3200/hal,./ports/c\ + c3200/simplelink,./ports/cc3200/telnet,./ports/esp32/managed_components,./ports/nrf/drivers/bluetooth/s1*,./ports/stm\ + 32/usbhost,./tests,ACKNOWLEDGEMENTS,\ + """ diff --git a/requirements-dev.txt b/requirements-dev.txt index 261a5cc2c7fe8..6a33c49daecb2 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -4,7 +4,7 @@ jinja2 typer sh -click +click<8.2.0 cpp-coveralls requests @@ -40,3 +40,6 @@ setuptools # For zephyr port tomlkit +pytest +pytest-rerunfailures +perfetto diff --git a/shared-bindings/_bleio/Adapter.c b/shared-bindings/_bleio/Adapter.c index 92b910c8b2e0f..a1f81a063fdaa 100644 --- a/shared-bindings/_bleio/Adapter.c +++ b/shared-bindings/_bleio/Adapter.c @@ -215,7 +215,7 @@ static mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t args[ARG_interval].u_obj = mp_obj_new_float(ADV_INTERVAL_DEFAULT); } - const mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj); + const mp_float_t interval = mp_arg_validate_type_float(args[ARG_interval].u_obj, MP_QSTR_interval); if (interval < ADV_INTERVAL_MIN || interval > ADV_INTERVAL_MAX) { mp_raise_ValueError_varg(MP_ERROR_TEXT("interval must be in range %s-%s"), ADV_INTERVAL_MIN_STRING, ADV_INTERVAL_MAX_STRING); @@ -223,7 +223,7 @@ static mp_obj_t bleio_adapter_start_advertising(mp_uint_t n_args, const mp_obj_t bool connectable = args[ARG_connectable].u_bool; bool anonymous = args[ARG_anonymous].u_bool; - uint32_t timeout = args[ARG_timeout].u_int; + const uint32_t timeout = (uint32_t)mp_arg_validate_int_min(args[ARG_timeout].u_int, 0, MP_QSTR_timeout); if (data_bufinfo.len > 31 && connectable && scan_response_bufinfo.len > 0) { mp_raise_bleio_BluetoothError(MP_ERROR_TEXT("Cannot have scan responses for extended, connectable advertisements.")); } @@ -306,7 +306,7 @@ static mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args mp_float_t timeout = 0.0f; if (args[ARG_timeout].u_obj != mp_const_none) { - timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + timeout = mp_arg_validate_obj_float_non_negative(args[ARG_timeout].u_obj, 0.0f, MP_QSTR_timeout); } if (args[ARG_interval].u_obj == MP_OBJ_NULL) { @@ -317,7 +317,7 @@ static mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args args[ARG_window].u_obj = mp_obj_new_float(WINDOW_DEFAULT); } - const mp_float_t interval = mp_obj_get_float(args[ARG_interval].u_obj); + const mp_float_t interval = mp_arg_validate_type_float(args[ARG_interval].u_obj, MP_QSTR_interval); if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) { mp_raise_ValueError_varg(MP_ERROR_TEXT("interval must be in range %s-%s"), INTERVAL_MIN_STRING, INTERVAL_MAX_STRING); } @@ -329,7 +329,7 @@ static mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args } #pragma GCC diagnostic pop - const mp_float_t window = mp_obj_get_float(args[ARG_window].u_obj); + const mp_float_t window = mp_arg_validate_type_float(args[ARG_window].u_obj, MP_QSTR_window); if (window > interval) { mp_raise_ValueError(MP_ERROR_TEXT("window must be <= interval")); } @@ -339,12 +339,14 @@ static mp_obj_t bleio_adapter_start_scan(size_t n_args, const mp_obj_t *pos_args if (args[ARG_prefixes].u_obj != MP_OBJ_NULL) { mp_get_buffer_raise(args[ARG_prefixes].u_obj, &prefix_bufinfo, MP_BUFFER_READ); // An empty buffer may not be on the heap, but that doesn't matter. - if (prefix_bufinfo.len > 0 && gc_nbytes(prefix_bufinfo.buf) == 0) { + if (prefix_bufinfo.len > 0 && !gc_ptr_on_heap(prefix_bufinfo.buf)) { mp_raise_ValueError(MP_ERROR_TEXT("Prefix buffer must be on the heap")); } } - return common_hal_bleio_adapter_start_scan(self, prefix_bufinfo.buf, prefix_bufinfo.len, args[ARG_extended].u_bool, args[ARG_buffer_size].u_int, timeout, interval, window, args[ARG_minimum_rssi].u_int, args[ARG_active].u_bool); + const mp_int_t buffer_size = mp_arg_validate_int_min(args[ARG_buffer_size].u_int, 1, MP_QSTR_buffer_size); + + return common_hal_bleio_adapter_start_scan(self, prefix_bufinfo.buf, prefix_bufinfo.len, args[ARG_extended].u_bool, buffer_size, timeout, interval, window, args[ARG_minimum_rssi].u_int, args[ARG_active].u_bool); } static MP_DEFINE_CONST_FUN_OBJ_KW(bleio_adapter_start_scan_obj, 1, bleio_adapter_start_scan); @@ -416,7 +418,8 @@ static mp_obj_t bleio_adapter_connect(mp_uint_t n_args, const mp_obj_t *pos_args mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); bleio_address_obj_t *address = mp_arg_validate_type(args[ARG_address].u_obj, &bleio_address_type, MP_QSTR_address); - mp_float_t timeout = mp_obj_get_float(args[ARG_timeout].u_obj); + const mp_float_t timeout = + mp_arg_validate_obj_float_non_negative(args[ARG_timeout].u_obj, 0.0f, MP_QSTR_timeout); return common_hal_bleio_adapter_connect(self, address, timeout); } diff --git a/shared-bindings/_bleio/Address.c b/shared-bindings/_bleio/Address.c index 58f8a8adc1e61..10500bec9cb1d 100644 --- a/shared-bindings/_bleio/Address.c +++ b/shared-bindings/_bleio/Address.c @@ -37,20 +37,18 @@ static mp_obj_t bleio_address_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - bleio_address_obj_t *self = mp_obj_malloc(bleio_address_obj_t, &bleio_address_type); - const mp_obj_t address = args[ARG_address].u_obj; mp_buffer_info_t buf_info; mp_get_buffer_raise(address, &buf_info, MP_BUFFER_READ); - if (buf_info.len != NUM_BLEIO_ADDRESS_BYTES) { - mp_raise_ValueError_varg(MP_ERROR_TEXT("Address must be %d bytes long"), NUM_BLEIO_ADDRESS_BYTES); - } + mp_arg_validate_length(buf_info.len, NUM_BLEIO_ADDRESS_BYTES, MP_QSTR_address); - const mp_int_t address_type = args[ARG_address_type].u_int; - if (address_type < BLEIO_ADDRESS_TYPE_MIN || address_type > BLEIO_ADDRESS_TYPE_MAX) { - mp_arg_error_invalid(MP_QSTR_address_type); - } + const mp_int_t address_type = + mp_arg_validate_int_range(args[ARG_address_type].u_int, + BLEIO_ADDRESS_TYPE_MIN, + BLEIO_ADDRESS_TYPE_MAX, + MP_QSTR_address_type); + bleio_address_obj_t *self = mp_obj_malloc(bleio_address_obj_t, &bleio_address_type); common_hal_bleio_address_construct(self, buf_info.buf, address_type); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/_bleio/Characteristic.c b/shared-bindings/_bleio/Characteristic.c index 8d6ac43e487d1..0ed4c2b7eaa97 100644 --- a/shared-bindings/_bleio/Characteristic.c +++ b/shared-bindings/_bleio/Characteristic.c @@ -116,9 +116,10 @@ static mp_obj_t bleio_characteristic_add_to_service(size_t n_args, const mp_obj_ } mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - if (initial_value_bufinfo.len > max_length || - (fixed_length && initial_value_bufinfo.len != max_length)) { - mp_raise_ValueError(MP_ERROR_TEXT("initial_value length is wrong")); + if (fixed_length) { + mp_arg_validate_length(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); + } else { + mp_arg_validate_length_max(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); } const char *user_description = NULL; diff --git a/shared-bindings/_bleio/Descriptor.c b/shared-bindings/_bleio/Descriptor.c index 57cc605029f4a..9e29f57d8dd2b 100644 --- a/shared-bindings/_bleio/Descriptor.c +++ b/shared-bindings/_bleio/Descriptor.c @@ -99,9 +99,10 @@ static mp_obj_t bleio_descriptor_add_to_characteristic(size_t n_args, const mp_o } } mp_get_buffer_raise(initial_value, &initial_value_bufinfo, MP_BUFFER_READ); - if (initial_value_bufinfo.len > max_length || - (fixed_length && initial_value_bufinfo.len != max_length)) { - mp_raise_ValueError(MP_ERROR_TEXT("initial_value length is wrong")); + if (fixed_length) { + mp_arg_validate_length(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); + } else { + mp_arg_validate_length_max(initial_value_bufinfo.len, max_length, MP_QSTR_initial_value); } bleio_descriptor_obj_t *descriptor = mp_obj_malloc(bleio_descriptor_obj_t, &bleio_descriptor_type); diff --git a/shared-bindings/_bleio/PacketBuffer.c b/shared-bindings/_bleio/PacketBuffer.c index 47d71ebd55af4..aa1e6e8645a61 100644 --- a/shared-bindings/_bleio/PacketBuffer.c +++ b/shared-bindings/_bleio/PacketBuffer.c @@ -57,7 +57,10 @@ static mp_obj_t bleio_packet_buffer_make_new(const mp_obj_type_t *type, size_t n size_t max_packet_size = common_hal_bleio_characteristic_get_max_length(characteristic); if (args[ARG_max_packet_size].u_obj != mp_const_none) { - max_packet_size = mp_obj_get_int(args[ARG_max_packet_size].u_obj); + const mp_int_t max_packet_size_int = + mp_arg_validate_type_int(args[ARG_max_packet_size].u_obj, MP_QSTR_max_packet_size); + max_packet_size = + (size_t)mp_arg_validate_int_min(max_packet_size_int, 1, MP_QSTR_max_packet_size); } bleio_packet_buffer_obj_t *self = mp_obj_malloc(bleio_packet_buffer_obj_t, &bleio_packet_buffer_type); diff --git a/shared-bindings/_bleio/PacketBuffer.h b/shared-bindings/_bleio/PacketBuffer.h index 1a872512da27c..24fb24bce78d3 100644 --- a/shared-bindings/_bleio/PacketBuffer.h +++ b/shared-bindings/_bleio/PacketBuffer.h @@ -6,6 +6,7 @@ #pragma once +#include "shared-bindings/_bleio/Characteristic.h" #include "common-hal/_bleio/PacketBuffer.h" extern const mp_obj_type_t bleio_packet_buffer_type; @@ -21,13 +22,11 @@ void common_hal_bleio_packet_buffer_construct( bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, size_t buffer_size, size_t max_packet_size); // Allocation free version for BLE workflow use. -#if CIRCUITPY_SERIAL_BLE || CIRCUITPY_BLE_FILE_SERVICE void _common_hal_bleio_packet_buffer_construct( bleio_packet_buffer_obj_t *self, bleio_characteristic_obj_t *characteristic, uint32_t *incoming_buffer, size_t incoming_buffer_size, - uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t outgoing_buffer_size, + uint32_t *outgoing_buffer1, uint32_t *outgoing_buffer2, size_t max_packet_size, ble_event_handler_t *static_handler_entry); -#endif mp_int_t common_hal_bleio_packet_buffer_write(bleio_packet_buffer_obj_t *self, const uint8_t *data, size_t len, uint8_t *header, size_t header_len); mp_int_t common_hal_bleio_packet_buffer_readinto(bleio_packet_buffer_obj_t *self, uint8_t *data, size_t len); mp_int_t common_hal_bleio_packet_buffer_get_incoming_packet_length(bleio_packet_buffer_obj_t *self); diff --git a/shared-bindings/_bleio/UUID.c b/shared-bindings/_bleio/UUID.c index 2d28d5a9b61a6..eef8546e2535c 100644 --- a/shared-bindings/_bleio/UUID.c +++ b/shared-bindings/_bleio/UUID.c @@ -34,20 +34,18 @@ static mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); - bleio_uuid_obj_t *self = mp_obj_malloc(bleio_uuid_obj_t, &bleio_uuid_type); - const mp_obj_t value = all_args[0]; uint8_t uuid128[16]; if (mp_obj_is_int(value)) { - mp_int_t uuid16 = mp_obj_get_int(value); - if (uuid16 < 0 || uuid16 > 0xffff) { - mp_raise_ValueError(MP_ERROR_TEXT("UUID integer value must be 0-0xffff")); - } + const mp_int_t uuid16 = + mp_arg_validate_int_range(mp_obj_get_int(value), 0, 0xffff, MP_QSTR_value); // NULL means no 128-bit value. + bleio_uuid_obj_t *self = mp_obj_malloc(bleio_uuid_obj_t, &bleio_uuid_type); common_hal_bleio_uuid_construct(self, uuid16, NULL); + return MP_OBJ_FROM_PTR(self); } else { if (mp_obj_is_str(value)) { // 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' @@ -82,9 +80,7 @@ static mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, si mp_raise_ValueError(MP_ERROR_TEXT("UUID value is not str, int or byte buffer")); } - if (bufinfo.len != 16) { - mp_raise_ValueError(MP_ERROR_TEXT("Byte buffer must be 16 bytes.")); - } + mp_arg_validate_length(bufinfo.len, 16, MP_QSTR_value); memcpy(uuid128, bufinfo.buf, 16); } @@ -93,10 +89,12 @@ static mp_obj_t bleio_uuid_make_new(const mp_obj_type_t *type, size_t n_args, si uint32_t uuid16 = (uuid128[13] << 8) | uuid128[12]; uuid128[12] = 0; uuid128[13] = 0; + + bleio_uuid_obj_t *self = mp_obj_malloc(bleio_uuid_obj_t, &bleio_uuid_type); common_hal_bleio_uuid_construct(self, uuid16, uuid128); - } - return MP_OBJ_FROM_PTR(self); + return MP_OBJ_FROM_PTR(self); + } } //| uuid16: int @@ -171,12 +169,12 @@ static mp_obj_t bleio_uuid_pack_into(mp_uint_t n_args, const mp_obj_t *pos_args, mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_WRITE); - size_t offset = args[ARG_offset].u_int; - if (offset + common_hal_bleio_uuid_get_size(self) / 8 > bufinfo.len) { - mp_raise_ValueError(MP_ERROR_TEXT("Buffer + offset too small %d %d %d")); - } + const mp_int_t offset = + mp_arg_validate_int_range(args[ARG_offset].u_int, 0, (mp_int_t)bufinfo.len, MP_QSTR_offset); + const size_t packed_len = common_hal_bleio_uuid_get_size(self) / 8; + mp_arg_validate_length_min(bufinfo.len - (size_t)offset, packed_len, MP_QSTR_buffer); - common_hal_bleio_uuid_pack_into(self, bufinfo.buf + offset); + common_hal_bleio_uuid_pack_into(self, (uint8_t *)bufinfo.buf + (size_t)offset); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_KW(bleio_uuid_pack_into_obj, 1, bleio_uuid_pack_into); diff --git a/shared-bindings/_bleio/__init__.c b/shared-bindings/_bleio/__init__.c index 3bf8c1a2c9c56..1ec9fd96e766b 100644 --- a/shared-bindings/_bleio/__init__.c +++ b/shared-bindings/_bleio/__init__.c @@ -56,7 +56,7 @@ //| //| MP_DEFINE_BLEIO_EXCEPTION(BluetoothError, Exception) -NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t fmt, ...) { +MP_NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_bleio_BluetoothError, fmt, argptr); @@ -72,7 +72,7 @@ NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t fmt, ...) { //| //| MP_DEFINE_BLEIO_EXCEPTION(RoleError, bleio_BluetoothError) -NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg) { +MP_NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg) { mp_raise_msg(&mp_type_bleio_RoleError, msg); } @@ -83,7 +83,7 @@ NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg) { //| //| MP_DEFINE_BLEIO_EXCEPTION(SecurityError, bleio_BluetoothError) -NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t fmt, ...) { +MP_NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_bleio_SecurityError, fmt, argptr); diff --git a/shared-bindings/_bleio/__init__.h b/shared-bindings/_bleio/__init__.h index faf11ea1d0637..0c5a5fec06b29 100644 --- a/shared-bindings/_bleio/__init__.h +++ b/shared-bindings/_bleio/__init__.h @@ -45,19 +45,11 @@ void common_hal_bleio_init(void); extern mp_obj_t bleio_set_adapter(mp_obj_t adapter_obj); -NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t msg, ...); -NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg); -NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t msg, ...); +MP_NORETURN void mp_raise_bleio_BluetoothError(mp_rom_error_text_t msg, ...); +MP_NORETURN void mp_raise_bleio_RoleError(mp_rom_error_text_t msg); +MP_NORETURN void mp_raise_bleio_SecurityError(mp_rom_error_text_t msg, ...); bleio_adapter_obj_t *common_hal_bleio_allocate_adapter_or_raise(void); -void common_hal_bleio_check_connected(uint16_t conn_handle); - -uint16_t common_hal_bleio_device_get_conn_handle(mp_obj_t device); void common_hal_bleio_device_discover_remote_services(mp_obj_t device, mp_obj_t service_uuids_whitelist); -size_t common_hal_bleio_gatts_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); -void common_hal_bleio_gatts_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo); -size_t common_hal_bleio_gattc_read(uint16_t handle, uint16_t conn_handle, uint8_t *buf, size_t len); -void common_hal_bleio_gattc_write(uint16_t handle, uint16_t conn_handle, mp_buffer_info_t *bufinfo, bool write_no_response); - void common_hal_bleio_gc_collect(void); diff --git a/shared-bindings/_stage/Layer.c b/shared-bindings/_stage/Layer.c index de4861a70eedd..0d7a1bfbd5efc 100644 --- a/shared-bindings/_stage/Layer.c +++ b/shared-bindings/_stage/Layer.c @@ -38,38 +38,38 @@ static mp_obj_t layer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 4, 5, false); - layer_obj_t *self = mp_obj_malloc(layer_obj_t, type); - - self->width = mp_obj_get_int(args[0]); - self->height = mp_obj_get_int(args[1]); - self->x = 0; - self->y = 0; - self->frame = 0; - self->rotation = false; + mp_uint_t width = mp_arg_validate_int_min(mp_obj_get_int(args[0]), 0, MP_QSTR_width); + mp_uint_t height = mp_arg_validate_int_min(mp_obj_get_int(args[1]), 0, MP_QSTR_height); - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); - self->graphic = bufinfo.buf; - if (bufinfo.len != 2048) { - mp_raise_ValueError(MP_ERROR_TEXT("graphic must be 2048 bytes long")); - } + mp_buffer_info_t graphic_bufinfo; + mp_get_buffer_raise(args[2], &graphic_bufinfo, MP_BUFFER_READ); + mp_arg_validate_length(graphic_bufinfo.len, 2048, MP_QSTR_graphic); - mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); - self->palette = bufinfo.buf; - if (bufinfo.len != 32) { - mp_raise_ValueError(MP_ERROR_TEXT("palette must be 32 bytes long")); - } + mp_buffer_info_t palette_bufinfo; + mp_get_buffer_raise(args[3], &palette_bufinfo, MP_BUFFER_READ); + mp_arg_validate_length(palette_bufinfo.len, 32, MP_QSTR_palette); + mp_buffer_info_t map_bufinfo = { .buf = NULL }; if (n_args > 4) { - mp_get_buffer_raise(args[4], &bufinfo, MP_BUFFER_READ); - self->map = bufinfo.buf; - if (bufinfo.len < (self->width * self->height) / 2) { + mp_get_buffer_raise(args[4], &map_bufinfo, MP_BUFFER_READ); + if (map_bufinfo.len < (width * height) / 2) { mp_raise_ValueError(MP_ERROR_TEXT("map buffer too small")); } - } else { - self->map = NULL; } + // Only allocate after validation is finished. + layer_obj_t *self = mp_obj_malloc(layer_obj_t, type); + + self->width = width; + self->height = height; + self->x = 0; + self->y = 0; + self->frame = 0; + self->rotation = false; + self->graphic = graphic_bufinfo.buf; + self->palette = palette_bufinfo.buf; + self->map = map_bufinfo.buf; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/_stage/Text.c b/shared-bindings/_stage/Text.c index ecd4f644a781f..0013ffb669797 100644 --- a/shared-bindings/_stage/Text.c +++ b/shared-bindings/_stage/Text.c @@ -38,32 +38,32 @@ static mp_obj_t text_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 5, 5, false); - text_obj_t *self = mp_obj_malloc(text_obj_t, type); + mp_uint_t width = mp_arg_validate_int_min(mp_obj_get_int(args[0]), 0, MP_QSTR_width); + mp_uint_t height = mp_arg_validate_int_min(mp_obj_get_int(args[1]), 0, MP_QSTR_height); - self->width = mp_obj_get_int(args[0]); - self->height = mp_obj_get_int(args[1]); - self->x = 0; - self->y = 0; - - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(args[2], &bufinfo, MP_BUFFER_READ); - self->font = bufinfo.buf; - if (bufinfo.len != 2048) { - mp_raise_ValueError(MP_ERROR_TEXT("font must be 2048 bytes long")); - } + mp_buffer_info_t font_bufinfo; + mp_get_buffer_raise(args[2], &font_bufinfo, MP_BUFFER_READ); + mp_arg_validate_length(font_bufinfo.len, 2048, MP_QSTR_font); - mp_get_buffer_raise(args[3], &bufinfo, MP_BUFFER_READ); - self->palette = bufinfo.buf; - if (bufinfo.len != 32) { - mp_raise_ValueError(MP_ERROR_TEXT("palette must be 32 bytes long")); - } + mp_buffer_info_t palette_bufinfo; + mp_get_buffer_raise(args[3], &palette_bufinfo, MP_BUFFER_READ); + mp_arg_validate_length(palette_bufinfo.len, 32, MP_QSTR_palette); - mp_get_buffer_raise(args[4], &bufinfo, MP_BUFFER_READ); - self->chars = bufinfo.buf; - if (bufinfo.len < self->width * self->height) { + mp_buffer_info_t chars_bufinfo; + mp_get_buffer_raise(args[4], &chars_bufinfo, MP_BUFFER_READ); + if (chars_bufinfo.len < width * height) { mp_raise_ValueError(MP_ERROR_TEXT("chars buffer too small")); } + text_obj_t *self = mp_obj_malloc(text_obj_t, type); + self->width = width; + self->height = height; + self->x = 0; + self->y = 0; + self->font = font_bufinfo.buf; + self->palette = palette_bufinfo.buf; + self->chars = chars_bufinfo.buf; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c b/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c index 32d418923fb92..5fb821ceade80 100644 --- a/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c +++ b/shared-bindings/adafruit_bus_device/i2c_device/I2CDevice.c @@ -47,8 +47,6 @@ //| ... //| static mp_obj_t adafruit_bus_device_i2cdevice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - adafruit_bus_device_i2cdevice_obj_t *self = - mp_obj_malloc(adafruit_bus_device_i2cdevice_obj_t, &adafruit_bus_device_i2cdevice_type); enum { ARG_i2c, ARG_device_address, ARG_probe }; static const mp_arg_t allowed_args[] = { { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -60,12 +58,14 @@ static mp_obj_t adafruit_bus_device_i2cdevice_make_new(const mp_obj_type_t *type mp_obj_t *i2c = args[ARG_i2c].u_obj; + adafruit_bus_device_i2cdevice_obj_t *self = + mp_obj_malloc(adafruit_bus_device_i2cdevice_obj_t, &adafruit_bus_device_i2cdevice_type); common_hal_adafruit_bus_device_i2cdevice_construct(MP_OBJ_TO_PTR(self), i2c, args[ARG_device_address].u_int); if (args[ARG_probe].u_bool == true) { common_hal_adafruit_bus_device_i2cdevice_probe_for_device(self); } - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); } //| def __enter__(self) -> I2CDevice: diff --git a/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c b/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c index f62aedc47e3d0..2f4895e38ce92 100644 --- a/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c +++ b/shared-bindings/adafruit_bus_device/spi_device/SPIDevice.c @@ -59,8 +59,6 @@ //| ... //| static mp_obj_t adafruit_bus_device_spidevice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - adafruit_bus_device_spidevice_obj_t *self = - mp_obj_malloc(adafruit_bus_device_spidevice_obj_t, &adafruit_bus_device_spidevice_type); enum { ARG_spi, ARG_chip_select, ARG_cs_active_value, ARG_baudrate, ARG_polarity, ARG_phase, ARG_extra_clocks }; static const mp_arg_t allowed_args[] = { { MP_QSTR_spi, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -78,12 +76,11 @@ static mp_obj_t adafruit_bus_device_spidevice_make_new(const mp_obj_type_t *type mp_arg_validate_type_or_none(args[ARG_chip_select].u_obj, &digitalio_digitalinout_type, MP_QSTR_chip_select); - common_hal_adafruit_bus_device_spidevice_construct(MP_OBJ_TO_PTR(self), spi, args[ARG_chip_select].u_obj, args[ARG_cs_active_value].u_bool, args[ARG_baudrate].u_int, args[ARG_polarity].u_int, - args[ARG_phase].u_int, args[ARG_extra_clocks].u_int); - if (args[ARG_chip_select].u_obj != mp_const_none) { - digitalinout_result_t result = common_hal_digitalio_digitalinout_switch_to_output(MP_OBJ_TO_PTR(args[ARG_chip_select].u_obj), - true, DRIVE_MODE_PUSH_PULL); + digitalinout_result_t result = + common_hal_digitalio_digitalinout_switch_to_output(MP_OBJ_TO_PTR(args[ARG_chip_select].u_obj), + true, + DRIVE_MODE_PUSH_PULL); #if CIRCUITPY_DIGITALIO_HAVE_INPUT_ONLY if (result == DIGITALINOUT_INPUT_ONLY) { mp_raise_NotImplementedError(MP_ERROR_TEXT("Pin is input only")); @@ -93,7 +90,19 @@ static mp_obj_t adafruit_bus_device_spidevice_make_new(const mp_obj_type_t *type #endif } - return (mp_obj_t)self; + adafruit_bus_device_spidevice_obj_t *self = + mp_obj_malloc(adafruit_bus_device_spidevice_obj_t, &adafruit_bus_device_spidevice_type); + common_hal_adafruit_bus_device_spidevice_construct(MP_OBJ_TO_PTR(self), + spi, + args[ARG_chip_select].u_obj, + args[ARG_cs_active_value].u_bool, + args[ARG_baudrate].u_int, + args[ARG_polarity].u_int, + args[ARG_phase].u_int, + args[ARG_extra_clocks].u_int); + + + return MP_OBJ_FROM_PTR(self); } //| def __enter__(self) -> busio.SPI: diff --git a/shared-bindings/adafruit_pixelbuf/PixelBuf.c b/shared-bindings/adafruit_pixelbuf/PixelBuf.c index cdffbfa627aa1..4144f086b98d6 100644 --- a/shared-bindings/adafruit_pixelbuf/PixelBuf.c +++ b/shared-bindings/adafruit_pixelbuf/PixelBuf.c @@ -24,7 +24,7 @@ #include "extmod/ulab/code/ndarray.h" #endif -static NORETURN void invalid_byteorder(void) { +static MP_NORETURN void invalid_byteorder(void) { mp_arg_error_invalid(MP_QSTR_byteorder); } diff --git a/shared-bindings/aesio/aes.c b/shared-bindings/aesio/aes.c index a6916042fda19..75f2850a7ef96 100644 --- a/shared-bindings/aesio/aes.c +++ b/shared-bindings/aesio/aes.c @@ -55,8 +55,6 @@ static mp_obj_t aesio_aes_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - aesio_aes_obj_t *self = mp_obj_malloc(aesio_aes_obj_t, &aesio_aes_type); - enum { ARG_key, ARG_mode, ARG_IV, ARG_counter, ARG_segment_size }; static const mp_arg_t allowed_args[] = { {MP_QSTR_key, MP_ARG_OBJ | MP_ARG_REQUIRED, {.u_obj = MP_OBJ_NULL} }, @@ -100,8 +98,10 @@ static mp_obj_t aesio_aes_make_new(const mp_obj_type_t *type, size_t n_args, iv = bufinfo.buf; } + aesio_aes_obj_t *self = mp_obj_malloc(aesio_aes_obj_t, &aesio_aes_type); common_hal_aesio_aes_construct(self, key, key_length, iv, mode, args[ARG_counter].u_int); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/alarm/__init__.h b/shared-bindings/alarm/__init__.h index af92b381137c8..37bfd9c51b7cd 100644 --- a/shared-bindings/alarm/__init__.h +++ b/shared-bindings/alarm/__init__.h @@ -24,7 +24,7 @@ extern mp_obj_t common_hal_alarm_light_sleep_until_alarms(size_t n_alarms, const // it will exit idle as if deep sleep was exited extern void common_hal_alarm_set_deep_sleep_alarms(size_t n_alarms, const mp_obj_t *alarms, size_t n_dios, digitalio_digitalinout_obj_t **preserve_dios); -extern NORETURN void common_hal_alarm_enter_deep_sleep(void); +extern MP_NORETURN void common_hal_alarm_enter_deep_sleep(void); // May be used to re-initialize peripherals like GPIO, if the VM reset returned // them to a default state diff --git a/shared-bindings/alarm/pin/PinAlarm.c b/shared-bindings/alarm/pin/PinAlarm.c index 8d93ca8d0de6a..4633fdb233c8d 100644 --- a/shared-bindings/alarm/pin/PinAlarm.c +++ b/shared-bindings/alarm/pin/PinAlarm.c @@ -42,7 +42,6 @@ //| ... //| static mp_obj_t alarm_pin_pinalarm_make_new(const mp_obj_type_t *type, mp_uint_t n_args, size_t n_kw, const mp_obj_t *all_args) { - alarm_pin_pinalarm_obj_t *self = mp_obj_malloc(alarm_pin_pinalarm_obj_t, &alarm_pin_pinalarm_type); enum { ARG_pin, ARG_value, ARG_edge, ARG_pull }; static const mp_arg_t allowed_args[] = { { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -55,6 +54,7 @@ static mp_obj_t alarm_pin_pinalarm_make_new(const mp_obj_type_t *type, mp_uint_t const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); + alarm_pin_pinalarm_obj_t *self = mp_obj_malloc(alarm_pin_pinalarm_obj_t, &alarm_pin_pinalarm_type); common_hal_alarm_pin_pinalarm_construct(self, pin, args[ARG_value].u_bool, diff --git a/shared-bindings/alarm/time/TimeAlarm.c b/shared-bindings/alarm/time/TimeAlarm.c index 0277c22fc44bf..a3df6ddf7fd2e 100644 --- a/shared-bindings/alarm/time/TimeAlarm.c +++ b/shared-bindings/alarm/time/TimeAlarm.c @@ -46,8 +46,6 @@ mp_obj_t MP_WEAK rtc_get_time_source_time(void) { //| static mp_obj_t alarm_time_timealarm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - alarm_time_timealarm_obj_t *self = mp_obj_malloc(alarm_time_timealarm_obj_t, &alarm_time_timealarm_type); - enum { ARG_monotonic_time, ARG_epoch_time }; static const mp_arg_t allowed_args[] = { { MP_QSTR_monotonic_time, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, @@ -92,6 +90,7 @@ static mp_obj_t alarm_time_timealarm_make_new(const mp_obj_type_t *type, mp_raise_ValueError(MP_ERROR_TEXT("Time is in the past.")); } + alarm_time_timealarm_obj_t *self = mp_obj_malloc(alarm_time_timealarm_obj_t, &alarm_time_timealarm_type); common_hal_alarm_time_timealarm_construct(self, monotonic_time); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/alarm/touch/TouchAlarm.c b/shared-bindings/alarm/touch/TouchAlarm.c index f25e826cff01c..42b149c9eeea7 100644 --- a/shared-bindings/alarm/touch/TouchAlarm.c +++ b/shared-bindings/alarm/touch/TouchAlarm.c @@ -26,8 +26,6 @@ //| static mp_obj_t alarm_touch_touchalarm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - alarm_touch_touchalarm_obj_t *self = mp_obj_malloc(alarm_touch_touchalarm_obj_t, &alarm_touch_touchalarm_type); - enum { ARG_pin }; static const mp_arg_t allowed_args[] = { { MP_QSTR_pin, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -38,6 +36,7 @@ static mp_obj_t alarm_touch_touchalarm_make_new(const mp_obj_type_t *type, const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); + alarm_touch_touchalarm_obj_t *self = mp_obj_malloc(alarm_touch_touchalarm_obj_t, &alarm_touch_touchalarm_type); common_hal_alarm_touch_touchalarm_construct(self, pin); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/analogbufio/BufferedIn.c b/shared-bindings/analogbufio/BufferedIn.c index a25610207b49a..2c42ee1f32571 100644 --- a/shared-bindings/analogbufio/BufferedIn.c +++ b/shared-bindings/analogbufio/BufferedIn.c @@ -60,10 +60,8 @@ static mp_obj_t analogbufio_bufferedin_make_new(const mp_obj_type_t *type, size_ // Validate Pin const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); - // Create local object - analogbufio_bufferedin_obj_t *self = mp_obj_malloc_with_finaliser(analogbufio_bufferedin_obj_t, &analogbufio_bufferedin_type); - - // Call local interface in ports/common-hal/analogbufio + analogbufio_bufferedin_obj_t *self = + mp_obj_malloc_with_finaliser(analogbufio_bufferedin_obj_t, &analogbufio_bufferedin_type); common_hal_analogbufio_bufferedin_construct(self, pin, args[ARG_sample_rate].u_int); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/audiobusio/I2SOut.c b/shared-bindings/audiobusio/I2SOut.c index 9aaf7421c653c..952a00e2903ee 100644 --- a/shared-bindings/audiobusio/I2SOut.c +++ b/shared-bindings/audiobusio/I2SOut.c @@ -144,6 +144,8 @@ static void check_for_deinit(audiobusio_i2sout_obj_t *self) { //| //| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. //| +//| Mono samples will be converted to stereo by copying value to both the left channel and the right channel. +//| //| The sample itself should consist of 8 bit or 16 bit samples.""" //| ... //| diff --git a/shared-bindings/audiobusio/PDMIn.c b/shared-bindings/audiobusio/PDMIn.c index 2a3fd3540c38f..092897a15cff3 100644 --- a/shared-bindings/audiobusio/PDMIn.c +++ b/shared-bindings/audiobusio/PDMIn.c @@ -96,9 +96,6 @@ static mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_ar const mcu_pin_obj_t *clock_pin = validate_obj_is_free_pin(args[ARG_clock_pin].u_obj, MP_QSTR_clock_pin); const mcu_pin_obj_t *data_pin = validate_obj_is_free_pin(args[ARG_data_pin].u_obj, MP_QSTR_data_pin); - // create PDMIn object from the given pin - audiobusio_pdmin_obj_t *self = mp_obj_malloc_with_finaliser(audiobusio_pdmin_obj_t, &audiobusio_pdmin_type); - uint32_t sample_rate = args[ARG_sample_rate].u_int; uint8_t bit_depth = args[ARG_bit_depth].u_int; if (bit_depth % 8 != 0) { @@ -115,6 +112,7 @@ static mp_obj_t audiobusio_pdmin_make_new(const mp_obj_type_t *type, size_t n_ar : mp_obj_get_float(args[ARG_startup_delay].u_obj); mp_arg_validate_float_range(startup_delay, 0.0f, 1.0f, MP_QSTR_startup_delay); + audiobusio_pdmin_obj_t *self = mp_obj_malloc_with_finaliser(audiobusio_pdmin_obj_t, &audiobusio_pdmin_type); common_hal_audiobusio_pdmin_construct(self, clock_pin, data_pin, sample_rate, bit_depth, mono, oversample); @@ -155,11 +153,11 @@ static void check_for_deinit(audiobusio_pdmin_obj_t *self) { // Provided by context manager helper. -//| def record(self, destination: WriteableBuffer, destination_length: int) -> None: +//| def record(self, destination: WriteableBuffer, destination_length: int) -> int: //| """Records destination_length bytes of samples to destination. This is //| blocking. //| -//| An IOError may be raised when the destination is too slow to record the +//| An OSError may be raised when the destination is too slow to record the //| audio at the given rate. For internal flash, writing all 1s to the file //| before recording is recommended to speed up writes. //| @@ -176,22 +174,22 @@ static mp_obj_t audiobusio_pdmin_obj_record(mp_obj_t self_obj, mp_obj_t destinat mp_buffer_info_t bufinfo; if (mp_obj_is_type(destination, &mp_type_fileio)) { mp_raise_NotImplementedError(MP_ERROR_TEXT("Cannot record to a file")); - } else if (mp_get_buffer(destination, &bufinfo, MP_BUFFER_WRITE)) { - if (bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL) < length) { - mp_raise_ValueError(MP_ERROR_TEXT("Destination capacity is smaller than destination_length.")); - } - uint8_t bit_depth = common_hal_audiobusio_pdmin_get_bit_depth(self); - if (bufinfo.typecode != 'H' && bit_depth == 16) { - mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be an array of type 'H' for bit_depth = 16")); - } else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE && bit_depth == 8) { - mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be a bytearray or array of type 'B' for bit_depth = 8")); - } - // length is the buffer length in slots, not bytes. - uint32_t length_written = - common_hal_audiobusio_pdmin_record_to_buffer(self, bufinfo.buf, length); - return MP_OBJ_NEW_SMALL_INT(length_written); } - return mp_const_none; + + mp_get_buffer_raise(destination, &bufinfo, MP_BUFFER_WRITE); + if (bufinfo.len / mp_binary_get_size('@', bufinfo.typecode, NULL) < length) { + mp_raise_ValueError(MP_ERROR_TEXT("Destination capacity is smaller than destination_length.")); + } + uint8_t bit_depth = common_hal_audiobusio_pdmin_get_bit_depth(self); + if (bufinfo.typecode != 'H' && bit_depth == 16) { + mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be an array of type 'H' for bit_depth = 16")); + } else if (bufinfo.typecode != 'B' && bufinfo.typecode != BYTEARRAY_TYPECODE && bit_depth == 8) { + mp_raise_ValueError(MP_ERROR_TEXT("destination buffer must be a bytearray or array of type 'B' for bit_depth = 8")); + } + // length is the buffer length in slots, not bytes. + uint32_t length_written = + common_hal_audiobusio_pdmin_record_to_buffer(self, bufinfo.buf, length); + return MP_OBJ_NEW_SMALL_INT(length_written); } MP_DEFINE_CONST_FUN_OBJ_3(audiobusio_pdmin_record_obj, audiobusio_pdmin_obj_record); diff --git a/shared-bindings/audiocore/RawSample.c b/shared-bindings/audiocore/RawSample.c index 30c1d1ad600d3..8dc57903286d3 100644 --- a/shared-bindings/audiocore/RawSample.c +++ b/shared-bindings/audiocore/RawSample.c @@ -90,7 +90,6 @@ static mp_obj_t audioio_rawsample_make_new(const mp_obj_type_t *type, size_t n_a mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - audioio_rawsample_obj_t *self = mp_obj_malloc(audioio_rawsample_obj_t, &audioio_rawsample_type); mp_buffer_info_t bufinfo; mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); uint8_t bytes_per_sample = 1; @@ -103,9 +102,16 @@ static mp_obj_t audioio_rawsample_make_new(const mp_obj_type_t *type, size_t n_a if (!args[ARG_single_buffer].u_bool && bufinfo.len % (bytes_per_sample * args[ARG_channel_count].u_int * 2) != 0) { mp_raise_ValueError_varg(MP_ERROR_TEXT("Length of %q must be an even multiple of channel_count * type_size"), MP_QSTR_buffer); } - common_hal_audioio_rawsample_construct(self, ((uint8_t *)bufinfo.buf), bufinfo.len, - bytes_per_sample, signed_samples, args[ARG_channel_count].u_int, - args[ARG_sample_rate].u_int, args[ARG_single_buffer].u_bool); + + audioio_rawsample_obj_t *self = mp_obj_malloc(audioio_rawsample_obj_t, &audioio_rawsample_type); + common_hal_audioio_rawsample_construct(self, + ((uint8_t *)bufinfo.buf), + bufinfo.len, + bytes_per_sample, + signed_samples, + args[ARG_channel_count].u_int, + args[ARG_sample_rate].u_int, + args[ARG_single_buffer].u_bool); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audiocore/WaveFile.c b/shared-bindings/audiocore/WaveFile.c index 4ba7d24bfd2c5..c93bedd4213ea 100644 --- a/shared-bindings/audiocore/WaveFile.c +++ b/shared-bindings/audiocore/WaveFile.c @@ -60,7 +60,6 @@ static mp_obj_t audioio_wavefile_make_new(const mp_obj_type_t *type, size_t n_ar arg = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), arg, MP_ROM_QSTR(MP_QSTR_rb)); } - audioio_wavefile_obj_t *self = mp_obj_malloc(audioio_wavefile_obj_t, &audioio_wavefile_type); if (!mp_obj_is_type(arg, &mp_type_vfs_fat_fileio)) { mp_raise_TypeError(MP_ERROR_TEXT("file must be a file opened in byte mode")); } @@ -72,6 +71,8 @@ static mp_obj_t audioio_wavefile_make_new(const mp_obj_type_t *type, size_t n_ar buffer = bufinfo.buf; buffer_size = mp_arg_validate_length_range(bufinfo.len, 8, 1024, MP_QSTR_buffer); } + + audioio_wavefile_obj_t *self = mp_obj_malloc(audioio_wavefile_obj_t, &audioio_wavefile_type); common_hal_audioio_wavefile_construct(self, MP_OBJ_TO_PTR(arg), buffer, buffer_size); diff --git a/shared-bindings/audiodelays/Chorus.c b/shared-bindings/audiodelays/Chorus.c index c6bfa2ffa7eb1..87922ff110b1b 100644 --- a/shared-bindings/audiodelays/Chorus.c +++ b/shared-bindings/audiodelays/Chorus.c @@ -206,11 +206,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_chorus_get_playing_obj, audiodelays_chorus MP_PROPERTY_GETTER(audiodelays_chorus_playing_obj, (mp_obj_t)&audiodelays_chorus_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Chorus: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Chorus""" //| ... //| static mp_obj_t audiodelays_chorus_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -228,7 +232,7 @@ static mp_obj_t audiodelays_chorus_obj_play(size_t n_args, const mp_obj_t *pos_a mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_chorus_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_chorus_play_obj, 1, audiodelays_chorus_obj_play); diff --git a/shared-bindings/audiodelays/Chorus.h b/shared-bindings/audiodelays/Chorus.h index 10c6448df8955..63e9ba9c28d2c 100644 --- a/shared-bindings/audiodelays/Chorus.h +++ b/shared-bindings/audiodelays/Chorus.h @@ -18,10 +18,6 @@ void common_hal_audiodelays_chorus_construct(audiodelays_chorus_obj_t *self, uin void common_hal_audiodelays_chorus_deinit(audiodelays_chorus_obj_t *self); bool common_hal_audiodelays_chorus_deinited(audiodelays_chorus_obj_t *self); -uint32_t common_hal_audiodelays_chorus_get_sample_rate(audiodelays_chorus_obj_t *self); -uint8_t common_hal_audiodelays_chorus_get_channel_count(audiodelays_chorus_obj_t *self); -uint8_t common_hal_audiodelays_chorus_get_bits_per_sample(audiodelays_chorus_obj_t *self); - mp_obj_t common_hal_audiodelays_chorus_get_delay_ms(audiodelays_chorus_obj_t *self); void common_hal_audiodelays_chorus_set_delay_ms(audiodelays_chorus_obj_t *self, mp_obj_t delay_ms); diff --git a/shared-bindings/audiodelays/Echo.c b/shared-bindings/audiodelays/Echo.c index 5ae849b19aa41..dbb496c61b975 100644 --- a/shared-bindings/audiodelays/Echo.c +++ b/shared-bindings/audiodelays/Echo.c @@ -230,11 +230,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_echo_get_playing_obj, audiodelays_echo_obj MP_PROPERTY_GETTER(audiodelays_echo_playing_obj, (mp_obj_t)&audiodelays_echo_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Echo: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Echo""" //| ... //| static mp_obj_t audiodelays_echo_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -252,7 +256,7 @@ static mp_obj_t audiodelays_echo_obj_play(size_t n_args, const mp_obj_t *pos_arg mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_echo_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_echo_play_obj, 1, audiodelays_echo_obj_play); diff --git a/shared-bindings/audiodelays/MultiTapDelay.c b/shared-bindings/audiodelays/MultiTapDelay.c index 5b057eaf80d1b..2126dbf990435 100644 --- a/shared-bindings/audiodelays/MultiTapDelay.c +++ b/shared-bindings/audiodelays/MultiTapDelay.c @@ -233,11 +233,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiodelays_multi_tap_delay_get_playing_obj, audiodela MP_PROPERTY_GETTER(audiodelays_multi_tap_delay_playing_obj, (mp_obj_t)&audiodelays_multi_tap_delay_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> MultiTapDelay: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: MultiTapDelay""" //| ... //| static mp_obj_t audiodelays_multi_tap_delay_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -255,7 +259,7 @@ static mp_obj_t audiodelays_multi_tap_delay_obj_play(size_t n_args, const mp_obj mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_multi_tap_delay_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_multi_tap_delay_play_obj, 1, audiodelays_multi_tap_delay_obj_play); diff --git a/shared-bindings/audiodelays/PitchShift.c b/shared-bindings/audiodelays/PitchShift.c index df2189945aa5d..ee3bf5afdd7bd 100644 --- a/shared-bindings/audiodelays/PitchShift.c +++ b/shared-bindings/audiodelays/PitchShift.c @@ -94,8 +94,18 @@ static mp_obj_t audiodelays_pitch_shift_make_new(const mp_obj_type_t *type, size mp_raise_ValueError(MP_ERROR_TEXT("bits_per_sample must be 8 or 16")); } - audiodelays_pitch_shift_obj_t *self = mp_obj_malloc(audiodelays_pitch_shift_obj_t, &audiodelays_pitch_shift_type); - common_hal_audiodelays_pitch_shift_construct(self, args[ARG_semitones].u_obj, args[ARG_mix].u_obj, args[ARG_window].u_int, args[ARG_overlap].u_int, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + audiodelays_pitch_shift_obj_t *self = + mp_obj_malloc(audiodelays_pitch_shift_obj_t, &audiodelays_pitch_shift_type); + common_hal_audiodelays_pitch_shift_construct(self, + args[ARG_semitones].u_obj, + args[ARG_mix].u_obj, + args[ARG_window].u_int, + args[ARG_overlap].u_int, + args[ARG_buffer_size].u_int, + bits_per_sample, + args[ARG_samples_signed].u_bool, + channel_count, + sample_rate); return MP_OBJ_FROM_PTR(self); } @@ -190,11 +200,15 @@ MP_PROPERTY_GETTER(audiodelays_pitch_shift_playing_obj, (mp_obj_t)&audiodelays_pitch_shift_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> PitchShift: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: PitchShift""" //| ... //| static mp_obj_t audiodelays_pitch_shift_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -211,7 +225,7 @@ static mp_obj_t audiodelays_pitch_shift_obj_play(size_t n_args, const mp_obj_t * mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiodelays_pitch_shift_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiodelays_pitch_shift_play_obj, 1, audiodelays_pitch_shift_obj_play); diff --git a/shared-bindings/audiofilters/Distortion.c b/shared-bindings/audiofilters/Distortion.c index cb888d0c71b7b..bed28f5ed4973 100644 --- a/shared-bindings/audiofilters/Distortion.c +++ b/shared-bindings/audiofilters/Distortion.c @@ -145,8 +145,21 @@ static mp_obj_t audiofilters_distortion_make_new(const mp_obj_type_t *type, size mode = validate_distortion_mode(args[ARG_mode].u_obj, MP_QSTR_mode); } - audiofilters_distortion_obj_t *self = mp_obj_malloc(audiofilters_distortion_obj_t, &audiofilters_distortion_type); - common_hal_audiofilters_distortion_construct(self, args[ARG_drive].u_obj, args[ARG_pre_gain].u_obj, args[ARG_post_gain].u_obj, mode, args[ARG_soft_clip].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + audiofilters_distortion_obj_t *self = + mp_obj_malloc(audiofilters_distortion_obj_t, &audiofilters_distortion_type); + common_hal_audiofilters_distortion_construct(self, + args[ARG_drive].u_obj, + args[ARG_pre_gain].u_obj, + args[ARG_post_gain].u_obj, + mode, + args[ARG_soft_clip].u_obj, + args[ARG_mix].u_obj, + args[ARG_buffer_size].u_int, + bits_per_sample, + args[ARG_samples_signed].u_bool, + channel_count, + sample_rate); + return MP_OBJ_FROM_PTR(self); } @@ -309,11 +322,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_distortion_get_playing_obj, audiofilters_ MP_PROPERTY_GETTER(audiofilters_distortion_playing_obj, (mp_obj_t)&audiofilters_distortion_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Distortion: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Distortion""" //| ... //| static mp_obj_t audiofilters_distortion_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -331,7 +348,7 @@ static mp_obj_t audiofilters_distortion_obj_play(size_t n_args, const mp_obj_t * mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_distortion_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_distortion_play_obj, 1, audiofilters_distortion_obj_play); diff --git a/shared-bindings/audiofilters/Filter.c b/shared-bindings/audiofilters/Filter.c index 2a4887a4d42a2..2d0d12b226abe 100644 --- a/shared-bindings/audiofilters/Filter.c +++ b/shared-bindings/audiofilters/Filter.c @@ -91,7 +91,14 @@ static mp_obj_t audiofilters_filter_make_new(const mp_obj_type_t *type, size_t n } audiofilters_filter_obj_t *self = mp_obj_malloc(audiofilters_filter_obj_t, &audiofilters_filter_type); - common_hal_audiofilters_filter_construct(self, args[ARG_filter].u_obj, args[ARG_mix].u_obj, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + common_hal_audiofilters_filter_construct(self, + args[ARG_filter].u_obj, + args[ARG_mix].u_obj, + args[ARG_buffer_size].u_int, + bits_per_sample, + args[ARG_samples_signed].u_bool, + channel_count, + sample_rate); return MP_OBJ_FROM_PTR(self); } @@ -179,11 +186,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_filter_get_playing_obj, audiofilters_filt MP_PROPERTY_GETTER(audiofilters_filter_playing_obj, (mp_obj_t)&audiofilters_filter_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Filter: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Filter""" //| ... //| static mp_obj_t audiofilters_filter_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -201,7 +212,7 @@ static mp_obj_t audiofilters_filter_obj_play(size_t n_args, const mp_obj_t *pos_ mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_filter_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_filter_play_obj, 1, audiofilters_filter_obj_play); diff --git a/shared-bindings/audiofilters/Phaser.c b/shared-bindings/audiofilters/Phaser.c index e7ddd986176b3..2aed9623b7219 100644 --- a/shared-bindings/audiofilters/Phaser.c +++ b/shared-bindings/audiofilters/Phaser.c @@ -91,7 +91,16 @@ static mp_obj_t audiofilters_phaser_make_new(const mp_obj_type_t *type, size_t n } audiofilters_phaser_obj_t *self = mp_obj_malloc(audiofilters_phaser_obj_t, &audiofilters_phaser_type); - common_hal_audiofilters_phaser_construct(self, args[ARG_frequency].u_obj, args[ARG_feedback].u_obj, args[ARG_mix].u_obj, args[ARG_stages].u_int, args[ARG_buffer_size].u_int, bits_per_sample, args[ARG_samples_signed].u_bool, channel_count, sample_rate); + common_hal_audiofilters_phaser_construct(self, + args[ARG_frequency].u_obj, + args[ARG_feedback].u_obj, + args[ARG_mix].u_obj, + args[ARG_stages].u_int, + args[ARG_buffer_size].u_int, + bits_per_sample, + args[ARG_samples_signed].u_bool, + channel_count, + sample_rate); return MP_OBJ_FROM_PTR(self); } @@ -214,11 +223,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofilters_phaser_get_playing_obj, audiofilters_phas MP_PROPERTY_GETTER(audiofilters_phaser_playing_obj, (mp_obj_t)&audiofilters_phaser_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Phaser: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Phaser""" //| ... //| static mp_obj_t audiofilters_phaser_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -236,7 +249,7 @@ static mp_obj_t audiofilters_phaser_obj_play(size_t n_args, const mp_obj_t *pos_ mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofilters_phaser_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofilters_phaser_play_obj, 1, audiofilters_phaser_obj_play); diff --git a/shared-bindings/audiofreeverb/Freeverb.c b/shared-bindings/audiofreeverb/Freeverb.c index 62c9237a0d271..12eb7ef70ab79 100644 --- a/shared-bindings/audiofreeverb/Freeverb.c +++ b/shared-bindings/audiofreeverb/Freeverb.c @@ -196,11 +196,15 @@ MP_DEFINE_CONST_FUN_OBJ_1(audiofreeverb_freeverb_get_playing_obj, audiofreeverb_ MP_PROPERTY_GETTER(audiofreeverb_freeverb_playing_obj, (mp_obj_t)&audiofreeverb_freeverb_get_playing_obj); -//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> Freeverb: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| -//| The sample must match the encoding settings given in the constructor.""" +//| The sample must match the encoding settings given in the constructor. +//| +//| :return: The effect object itself. Can be used for chaining, ie: +//| ``audio.play(effect.play(sample))``. +//| :rtype: Freeverb""" //| ... //| static mp_obj_t audiofreeverb_freeverb_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -218,7 +222,7 @@ static mp_obj_t audiofreeverb_freeverb_obj_play(size_t n_args, const mp_obj_t *p mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiofreeverb_freeverb_play(self, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiofreeverb_freeverb_play_obj, 1, audiofreeverb_freeverb_obj_play); diff --git a/shared-bindings/audiofreeverb/Freeverb.h b/shared-bindings/audiofreeverb/Freeverb.h index 913953ebecf62..bde6cfffda7d2 100644 --- a/shared-bindings/audiofreeverb/Freeverb.h +++ b/shared-bindings/audiofreeverb/Freeverb.h @@ -18,10 +18,6 @@ void common_hal_audiofreeverb_freeverb_construct(audiofreeverb_freeverb_obj_t *s void common_hal_audiofreeverb_freeverb_deinit(audiofreeverb_freeverb_obj_t *self); bool common_hal_audiofreeverb_freeverb_deinited(audiofreeverb_freeverb_obj_t *self); -uint32_t common_hal_audiofreeverb_freeverb_get_sample_rate(audiofreeverb_freeverb_obj_t *self); -uint8_t common_hal_audiofreeverb_freeverb_get_channel_count(audiofreeverb_freeverb_obj_t *self); -uint8_t common_hal_audiofreeverb_freeverb_get_bits_per_sample(audiofreeverb_freeverb_obj_t *self); - mp_obj_t common_hal_audiofreeverb_freeverb_get_roomsize(audiofreeverb_freeverb_obj_t *self); void common_hal_audiofreeverb_freeverb_set_roomsize(audiofreeverb_freeverb_obj_t *self, mp_obj_t feedback); diff --git a/shared-bindings/audioio/AudioOut.c b/shared-bindings/audioio/AudioOut.c index 82aecefa370ba..8ea068ceb8f3a 100644 --- a/shared-bindings/audioio/AudioOut.c +++ b/shared-bindings/audioio/AudioOut.c @@ -103,7 +103,10 @@ static mp_obj_t audioio_audioout_make_new(const mp_obj_type_t *type, size_t n_ar // create AudioOut object from the given pin audioio_audioout_obj_t *self = mp_obj_malloc_with_finaliser(audioio_audioout_obj_t, &audioio_audioout_type); - common_hal_audioio_audioout_construct(self, left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int); + common_hal_audioio_audioout_construct(self, + left_channel_pin, + right_channel_pin, + args[ARG_quiescent_value].u_int); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audiomixer/Mixer.c b/shared-bindings/audiomixer/Mixer.c index dda6d06bd500c..516d079cd2a09 100644 --- a/shared-bindings/audiomixer/Mixer.c +++ b/shared-bindings/audiomixer/Mixer.c @@ -160,13 +160,17 @@ MP_PROPERTY_GETTER(audiomixer_mixer_voice_obj, //| def play( //| self, sample: circuitpython_typing.AudioSample, *, voice: int = 0, loop: bool = False -//| ) -> None: +//| ) -> Mixer: //| """Plays the sample once when loop=False and continuously when loop=True. //| Does not block. Use `playing` to block. //| //| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. //| -//| The sample must match the Mixer's encoding settings given in the constructor.""" +//| The sample must match the Mixer's encoding settings given in the constructor. +//| +//| :return: The mixer object itself. Can be used for chaining, ie: +//| ``audio.play(mixer.play(sample))``. +//| :rtype: Chorus""" //| ... //| static mp_obj_t audiomixer_mixer_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -189,7 +193,7 @@ static mp_obj_t audiomixer_mixer_obj_play(size_t n_args, const mp_obj_t *pos_arg mp_obj_t sample = args[ARG_sample].u_obj; common_hal_audiomixer_mixervoice_play(voice, sample, args[ARG_loop].u_bool); - return mp_const_none; + return MP_OBJ_FROM_PTR(self); } MP_DEFINE_CONST_FUN_OBJ_KW(audiomixer_mixer_play_obj, 1, audiomixer_mixer_obj_play); diff --git a/shared-bindings/audiomixer/MixerVoice.c b/shared-bindings/audiomixer/MixerVoice.c index fc292d4ea2f68..4957fab388460 100644 --- a/shared-bindings/audiomixer/MixerVoice.c +++ b/shared-bindings/audiomixer/MixerVoice.c @@ -29,8 +29,8 @@ // TODO: support mono or stereo voices static mp_obj_t audiomixer_mixervoice_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { mp_arg_check_num(n_args, n_kw, 0, 0, false); - audiomixer_mixervoice_obj_t *self = mp_obj_malloc(audiomixer_mixervoice_obj_t, &audiomixer_mixervoice_type); + audiomixer_mixervoice_obj_t *self = mp_obj_malloc(audiomixer_mixervoice_obj_t, &audiomixer_mixervoice_type); common_hal_audiomixer_mixervoice_construct(self); return MP_OBJ_FROM_PTR(self); @@ -114,6 +114,31 @@ MP_PROPERTY_GETSET(audiomixer_mixervoice_level_obj, (mp_obj_t)&audiomixer_mixervoice_get_level_obj, (mp_obj_t)&audiomixer_mixervoice_set_level_obj); +//| panning: synthio.BlockInput +//| """Defines the channel(s) in which the voice appears, as a floating point number between +//| -1 and 1. If your board does not support synthio, this property will only accept a float +//| value. This property is ignored if ``audiomixer.Mixer.channel_count=1``. +//| +//| -1 is left channel only, 0 is both channels, and 1 is right channel. For fractional values, +//| the note plays at full amplitude in one channel and partial amplitude in the other channel. +//| For instance -.5 plays at full amplitude in the left channel and 1/2 amplitude in the right +//| channel.""" +static mp_obj_t audiomixer_mixervoice_obj_get_panning(mp_obj_t self_in) { + return common_hal_audiomixer_mixervoice_get_panning(self_in); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiomixer_mixervoice_get_panning_obj, audiomixer_mixervoice_obj_get_panning); + +static mp_obj_t audiomixer_mixervoice_obj_set_panning(mp_obj_t self_in, mp_obj_t panning_in) { + audiomixer_mixervoice_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiomixer_mixervoice_set_panning(self, panning_in); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiomixer_mixervoice_set_panning_obj, audiomixer_mixervoice_obj_set_panning); + +MP_PROPERTY_GETSET(audiomixer_mixervoice_panning_obj, + (mp_obj_t)&audiomixer_mixervoice_get_panning_obj, + (mp_obj_t)&audiomixer_mixervoice_set_panning_obj); + //| loop: bool //| """Get or set the loop status of the currently playing sample.""" static mp_obj_t audiomixer_mixervoice_obj_get_loop(mp_obj_t self_in) { @@ -158,6 +183,7 @@ static const mp_rom_map_elem_t audiomixer_mixervoice_locals_dict_table[] = { // Properties { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&audiomixer_mixervoice_playing_obj) }, { MP_ROM_QSTR(MP_QSTR_level), MP_ROM_PTR(&audiomixer_mixervoice_level_obj) }, + { MP_ROM_QSTR(MP_QSTR_panning), MP_ROM_PTR(&audiomixer_mixervoice_panning_obj) }, { MP_ROM_QSTR(MP_QSTR_loop), MP_ROM_PTR(&audiomixer_mixervoice_loop_obj) }, }; static MP_DEFINE_CONST_DICT(audiomixer_mixervoice_locals_dict, audiomixer_mixervoice_locals_dict_table); diff --git a/shared-bindings/audiomixer/MixerVoice.h b/shared-bindings/audiomixer/MixerVoice.h index fe350eb1a7592..d60820f7f5734 100644 --- a/shared-bindings/audiomixer/MixerVoice.h +++ b/shared-bindings/audiomixer/MixerVoice.h @@ -18,6 +18,8 @@ void common_hal_audiomixer_mixervoice_stop(audiomixer_mixervoice_obj_t *self); void common_hal_audiomixer_mixervoice_end(audiomixer_mixervoice_obj_t *self); mp_obj_t common_hal_audiomixer_mixervoice_get_level(audiomixer_mixervoice_obj_t *self); void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *self, mp_obj_t gain); +mp_obj_t common_hal_audiomixer_mixervoice_get_panning(audiomixer_mixervoice_obj_t *self); +void common_hal_audiomixer_mixervoice_set_panning(audiomixer_mixervoice_obj_t *self, mp_obj_t value); bool common_hal_audiomixer_mixervoice_get_playing(audiomixer_mixervoice_obj_t *self); diff --git a/shared-bindings/audiomp3/MP3Decoder.c b/shared-bindings/audiomp3/MP3Decoder.c index ff6c77e85726b..433b308c156c8 100644 --- a/shared-bindings/audiomp3/MP3Decoder.c +++ b/shared-bindings/audiomp3/MP3Decoder.c @@ -91,8 +91,6 @@ static mp_obj_t audiomp3_mp3file_make_new(const mp_obj_type_t *type, size_t n_ar stream = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), stream, MP_ROM_QSTR(MP_QSTR_rb)); } - audiomp3_mp3file_obj_t *self = mp_obj_malloc_with_finaliser(audiomp3_mp3file_obj_t, &audiomp3_mp3file_type); - const mp_stream_p_t *stream_p = mp_get_stream_raise(stream, MP_STREAM_OP_READ); if (stream_p->is_text) { @@ -106,6 +104,8 @@ static mp_obj_t audiomp3_mp3file_make_new(const mp_obj_type_t *type, size_t n_ar buffer = bufinfo.buf; buffer_size = bufinfo.len; } + + audiomp3_mp3file_obj_t *self = mp_obj_malloc_with_finaliser(audiomp3_mp3file_obj_t, &audiomp3_mp3file_type); common_hal_audiomp3_mp3file_construct(self, stream, buffer, buffer_size); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/audiopwmio/PWMAudioOut.c b/shared-bindings/audiopwmio/PWMAudioOut.c index 3c96143364183..5b8d071924b83 100644 --- a/shared-bindings/audiopwmio/PWMAudioOut.c +++ b/shared-bindings/audiopwmio/PWMAudioOut.c @@ -100,8 +100,10 @@ static mp_obj_t audiopwmio_pwmaudioout_make_new(const mp_obj_type_t *type, size_ // create AudioOut object from the given pin // The object is created with a finaliser as some ports use these (rather than 'reset' functions) // to ensure resources are collected at interpreter shutdown. - audiopwmio_pwmaudioout_obj_t *self = mp_obj_malloc_with_finaliser(audiopwmio_pwmaudioout_obj_t, &audiopwmio_pwmaudioout_type); - common_hal_audiopwmio_pwmaudioout_construct(self, left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int); + audiopwmio_pwmaudioout_obj_t *self = + mp_obj_malloc_with_finaliser(audiopwmio_pwmaudioout_obj_t, &audiopwmio_pwmaudioout_type); + common_hal_audiopwmio_pwmaudioout_construct(self, + left_channel_pin, right_channel_pin, args[ARG_quiescent_value].u_int); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/audiospeed/SpeedChanger.c b/shared-bindings/audiospeed/SpeedChanger.c new file mode 100644 index 0000000000000..34efa1516b035 --- /dev/null +++ b/shared-bindings/audiospeed/SpeedChanger.c @@ -0,0 +1,138 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared/runtime/context_manager_helpers.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/audiospeed/SpeedChanger.h" +#include "shared-bindings/audiocore/__init__.h" +#include "shared-bindings/util.h" +#include "shared-module/audiospeed/SpeedChanger.h" + +// Convert a Python float to 16.16 fixed-point rate +static uint32_t rate_to_fp(mp_obj_t rate_obj) { + mp_float_t rate = mp_arg_validate_obj_float_range(rate_obj, 0.001, 1000.0, MP_QSTR_rate); + return (uint32_t)(rate * (1 << 16)); +} + +// Convert 16.16 fixed-point rate to Python float +static mp_obj_t fp_to_rate(uint32_t rate_fp) { + return mp_obj_new_float((mp_float_t)rate_fp / (1 << 16)); +} + +//| class SpeedChanger: +//| """Wraps an audio sample to play it back at a different speed. +//| +//| Uses nearest-neighbor resampling with a fixed-point phase accumulator +//| for CPU-efficient variable-speed playback.""" +//| +//| def __init__(self, source: circuitpython_typing.AudioSample, rate: float = 1.0) -> None: +//| """Create a SpeedChanger that wraps ``source``. +//| +//| :param audiosample source: The audio source to resample. +//| :param float rate: Playback speed multiplier. 1.0 = normal, 2.0 = double speed, +//| 0.5 = half speed. Must be positive. +//| +//| Playing a wave file at 1.5x speed:: +//| +//| import board +//| import audiocore +//| import audiospeed +//| import audioio +//| +//| wav = audiocore.WaveFile("drum.wav") +//| fast = audiospeed.SpeedChanger(wav, rate=1.5) +//| audio = audioio.AudioOut(board.A0) +//| audio.play(fast) +//| +//| # Change speed during playback: +//| fast.rate = 2.0 # double speed +//| fast.rate = 0.5 # half speed +//| """ +//| ... +//| +static mp_obj_t audiospeed_speedchanger_make_new(const mp_obj_type_t *type, + size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_source, ARG_rate }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_source, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_rate, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Validate source implements audiosample protocol + mp_obj_t source = args[ARG_source].u_obj; + audiosample_check(source); + + uint32_t rate_fp = 1 << 16; // default 1.0 + if (args[ARG_rate].u_obj != mp_const_none) { + rate_fp = rate_to_fp(args[ARG_rate].u_obj); + } + + audiospeed_speedchanger_obj_t *self = mp_obj_malloc(audiospeed_speedchanger_obj_t, &audiospeed_speedchanger_type); + common_hal_audiospeed_speedchanger_construct(self, source, rate_fp); + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialises the SpeedChanger and releases all memory resources for reuse.""" +//| ... +//| +static mp_obj_t audiospeed_speedchanger_deinit(mp_obj_t self_in) { + audiospeed_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_audiospeed_speedchanger_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(audiospeed_speedchanger_deinit_obj, audiospeed_speedchanger_deinit); + +//| rate: float +//| """Playback speed multiplier. Can be changed during playback.""" +//| +static mp_obj_t audiospeed_speedchanger_obj_get_rate(mp_obj_t self_in) { + audiospeed_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + audiosample_check_for_deinit(&self->base); + return fp_to_rate(common_hal_audiospeed_speedchanger_get_rate(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(audiospeed_speedchanger_get_rate_obj, audiospeed_speedchanger_obj_get_rate); + +static mp_obj_t audiospeed_speedchanger_obj_set_rate(mp_obj_t self_in, mp_obj_t rate_obj) { + audiospeed_speedchanger_obj_t *self = MP_OBJ_TO_PTR(self_in); + audiosample_check_for_deinit(&self->base); + common_hal_audiospeed_speedchanger_set_rate(self, rate_to_fp(rate_obj)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(audiospeed_speedchanger_set_rate_obj, audiospeed_speedchanger_obj_set_rate); + +MP_PROPERTY_GETSET(audiospeed_speedchanger_rate_obj, + (mp_obj_t)&audiospeed_speedchanger_get_rate_obj, + (mp_obj_t)&audiospeed_speedchanger_set_rate_obj); + +static const mp_rom_map_elem_t audiospeed_speedchanger_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&audiospeed_speedchanger_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_rate), MP_ROM_PTR(&audiospeed_speedchanger_rate_obj) }, + AUDIOSAMPLE_FIELDS, +}; +static MP_DEFINE_CONST_DICT(audiospeed_speedchanger_locals_dict, audiospeed_speedchanger_locals_dict_table); + +static const audiosample_p_t audiospeed_speedchanger_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_audiosample) + .reset_buffer = (audiosample_reset_buffer_fun)audiospeed_speedchanger_reset_buffer, + .get_buffer = (audiosample_get_buffer_fun)audiospeed_speedchanger_get_buffer, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + audiospeed_speedchanger_type, + MP_QSTR_SpeedChanger, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, audiospeed_speedchanger_make_new, + locals_dict, &audiospeed_speedchanger_locals_dict, + protocol, &audiospeed_speedchanger_proto + ); diff --git a/shared-bindings/audiospeed/SpeedChanger.h b/shared-bindings/audiospeed/SpeedChanger.h new file mode 100644 index 0000000000000..64a126a7a61fb --- /dev/null +++ b/shared-bindings/audiospeed/SpeedChanger.h @@ -0,0 +1,17 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "shared-module/audiospeed/SpeedChanger.h" + +extern const mp_obj_type_t audiospeed_speedchanger_type; + +void common_hal_audiospeed_speedchanger_construct(audiospeed_speedchanger_obj_t *self, + mp_obj_t source, uint32_t rate_fp); +void common_hal_audiospeed_speedchanger_deinit(audiospeed_speedchanger_obj_t *self); +void common_hal_audiospeed_speedchanger_set_rate(audiospeed_speedchanger_obj_t *self, uint32_t rate_fp); +uint32_t common_hal_audiospeed_speedchanger_get_rate(audiospeed_speedchanger_obj_t *self); diff --git a/shared-bindings/audiospeed/__init__.c b/shared-bindings/audiospeed/__init__.c new file mode 100644 index 0000000000000..b12e6db7e6bc7 --- /dev/null +++ b/shared-bindings/audiospeed/__init__.c @@ -0,0 +1,28 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/audiospeed/SpeedChanger.h" + +//| """Audio processing tools""" + +static const mp_rom_map_elem_t audiospeed_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audiospeed) }, + { MP_ROM_QSTR(MP_QSTR_SpeedChanger), MP_ROM_PTR(&audiospeed_speedchanger_type) }, +}; + +static MP_DEFINE_CONST_DICT(audiospeed_module_globals, audiospeed_module_globals_table); + +const mp_obj_module_t audiospeed_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&audiospeed_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_audiospeed, audiospeed_module); diff --git a/ports/zephyr-cp/bindings/zephyr_serial/__init__.h b/shared-bindings/audiospeed/__init__.h similarity index 67% rename from ports/zephyr-cp/bindings/zephyr_serial/__init__.h rename to shared-bindings/audiospeed/__init__.h index 370e233985f74..c4a52e5819d12 100644 --- a/ports/zephyr-cp/bindings/zephyr_serial/__init__.h +++ b/shared-bindings/audiospeed/__init__.h @@ -1,6 +1,6 @@ // This file is part of the CircuitPython project: https://circuitpython.org // -// SPDX-FileCopyrightText: Copyright (c) 2016 Scott Shawcroft +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt // // SPDX-License-Identifier: MIT diff --git a/shared-bindings/aurora_epaper/aurora_framebuffer.c b/shared-bindings/aurora_epaper/aurora_framebuffer.c index f1bb169328463..49e0b34a85616 100644 --- a/shared-bindings/aurora_epaper/aurora_framebuffer.c +++ b/shared-bindings/aurora_epaper/aurora_framebuffer.c @@ -64,7 +64,7 @@ //| ) -> None: //| """Create a framebuffer for the Aurora CoG display. //| -//| .. note:: Displays of size 1.9" and 2.6" are not tested, and may exibit unexpected behavior. +//| .. note:: Displays of size 1.9" and 2.6" are not tested, and may exhibit unexpected behavior. //| //| :param busio.SPI spi_bus: The SPI bus that the display is connected to //| :param microcontroller.Pin chip_select: The pin connected to the displays chip select input diff --git a/shared-bindings/bitbangio/I2C.c b/shared-bindings/bitbangio/I2C.c index d1a200d39b52f..e14133926994a 100644 --- a/shared-bindings/bitbangio/I2C.c +++ b/shared-bindings/bitbangio/I2C.c @@ -22,8 +22,8 @@ //| //| def __init__( //| self, -//| scl: microcontroller.Pin, -//| sda: microcontroller.Pin, +//| scl: Union[microcontroller.Pin, digitalio.DigitalInOutProtocol], +//| sda: Union[microcontroller.Pin, digitalio.DigitalInOutProtocol], //| *, //| frequency: int = 400000, //| timeout: int = 255, @@ -40,8 +40,8 @@ //| bit unpacking. Instead, use an existing driver or make one with //| :ref:`Register ` data descriptors. //| -//| :param ~microcontroller.Pin scl: The clock pin -//| :param ~microcontroller.Pin sda: The data pin +//| :param ~microcontroller.Pin scl: The clock pin or DigitalInOut object +//| :param ~microcontroller.Pin sda: The data pin or DigitalInOut object //| :param int frequency: The clock frequency of the bus //| :param int timeout: The maximum clock stretching timeout in microseconds""" //| ... @@ -57,12 +57,10 @@ static mp_obj_t bitbangio_i2c_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - const mcu_pin_obj_t *scl = validate_obj_is_free_pin(args[ARG_scl].u_obj, MP_QSTR_scl); - const mcu_pin_obj_t *sda = validate_obj_is_free_pin(args[ARG_sda].u_obj, MP_QSTR_sda); - bitbangio_i2c_obj_t *self = mp_obj_malloc_with_finaliser(bitbangio_i2c_obj_t, &bitbangio_i2c_type); - shared_module_bitbangio_i2c_construct(self, scl, sda, args[ARG_frequency].u_int, args[ARG_timeout].u_int); - return (mp_obj_t)self; + shared_module_bitbangio_i2c_construct(self, args[ARG_scl].u_obj, args[ARG_sda].u_obj, args[ARG_frequency].u_int, args[ARG_timeout].u_int); + + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: diff --git a/shared-bindings/bitbangio/I2C.h b/shared-bindings/bitbangio/I2C.h index 022be3692a6cf..cc9cd21b6012a 100644 --- a/shared-bindings/bitbangio/I2C.h +++ b/shared-bindings/bitbangio/I2C.h @@ -16,8 +16,8 @@ extern const mp_obj_type_t bitbangio_i2c_type; // Initializes the hardware peripheral. extern void shared_module_bitbangio_i2c_construct(bitbangio_i2c_obj_t *self, - const mcu_pin_obj_t *scl, - const mcu_pin_obj_t *sda, + mp_obj_t scl, + mp_obj_t sda, uint32_t frequency, uint32_t us_timeout); diff --git a/shared-bindings/bitbangio/SPI.c b/shared-bindings/bitbangio/SPI.c index 8938ae4898d56..de021867f3ca9 100644 --- a/shared-bindings/bitbangio/SPI.c +++ b/shared-bindings/bitbangio/SPI.c @@ -33,9 +33,9 @@ //| //| def __init__( //| self, -//| clock: microcontroller.Pin, -//| MOSI: Optional[microcontroller.Pin] = None, -//| MISO: Optional[microcontroller.Pin] = None, +//| clock: Union[microcontroller.Pin, digitalio.DigitalInOutProtocol], +//| MOSI: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, +//| MISO: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, //| ) -> None: //| """Construct an SPI object on the given pins. //| @@ -48,9 +48,9 @@ //| :ref:`Register ` data descriptors. //| //| -//| :param ~microcontroller.Pin clock: the pin to use for the clock. -//| :param ~microcontroller.Pin MOSI: the Main Out Selected In pin. -//| :param ~microcontroller.Pin MISO: the Main In Selected Out pin.""" +//| :param ~microcontroller.Pin clock: the pin to use for the clock or DigitalInOut object +//| :param ~microcontroller.Pin MOSI: the Main Out Selected In pin or DigitalInOut object +//| :param ~microcontroller.Pin MISO: the Main In Selected Out pin or DigitalInOut object""" //| ... //| @@ -65,13 +65,9 @@ static mp_obj_t bitbangio_spi_make_new(const mp_obj_type_t *type, size_t n_args, mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); - const mcu_pin_obj_t *mosi = validate_obj_is_free_pin_or_none(args[ARG_MOSI].u_obj, MP_QSTR_mosi); - const mcu_pin_obj_t *miso = validate_obj_is_free_pin_or_none(args[ARG_MISO].u_obj, MP_QSTR_miso); - bitbangio_spi_obj_t *self = mp_obj_malloc(bitbangio_spi_obj_t, &bitbangio_spi_type); - shared_module_bitbangio_spi_construct(self, clock, mosi, miso); - return (mp_obj_t)self; + shared_module_bitbangio_spi_construct(self, args[ARG_clock].u_obj, args[ARG_MOSI].u_obj, args[ARG_MISO].u_obj); + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: diff --git a/shared-bindings/bitbangio/SPI.h b/shared-bindings/bitbangio/SPI.h index dbe821683b0a2..19ec05152ad37 100644 --- a/shared-bindings/bitbangio/SPI.h +++ b/shared-bindings/bitbangio/SPI.h @@ -16,8 +16,7 @@ extern const mp_obj_type_t bitbangio_spi_type; // Construct an underlying SPI object. extern void shared_module_bitbangio_spi_construct(bitbangio_spi_obj_t *self, - const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *miso); + mp_obj_t clock, mp_obj_t mosi, mp_obj_t miso); extern void shared_module_bitbangio_spi_deinit(bitbangio_spi_obj_t *self); extern bool shared_module_bitbangio_spi_deinited(bitbangio_spi_obj_t *self); diff --git a/shared-bindings/bitmaptools/__init__.c b/shared-bindings/bitmaptools/__init__.c index 709e458211341..4d56f393189e4 100644 --- a/shared-bindings/bitmaptools/__init__.c +++ b/shared-bindings/bitmaptools/__init__.c @@ -185,8 +185,8 @@ static mp_obj_t bitmaptools_obj_rotozoom(size_t n_args, const mp_obj_t *pos_args ARG_angle, ARG_scale, ARG_skip_index}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, {MP_QSTR_ox, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->width / 2 {MP_QSTR_oy, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, // None convert to destination->height / 2 @@ -206,9 +206,9 @@ static mp_obj_t bitmaptools_obj_rotozoom(size_t n_args, const mp_obj_t *pos_args mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap - displayio_bitmap_t *source = MP_OBJ_TO_PTR(args[ARG_source_bitmap].u_obj); // the source bitmap + displayio_bitmap_t *source = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_source_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_source_bitmap)); // the source bitmap // ensure that the destination bitmap has at least as many `bits_per_value` as the source if (destination->bits_per_value < source->bits_per_value) { @@ -418,6 +418,46 @@ static mp_obj_t bitmaptools_alphablend(size_t n_args, const mp_obj_t *pos_args, } MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_alphablend_obj, 0, bitmaptools_alphablend); +//| def replace_color( +//| dest_bitmap: displayio.Bitmap, old_color: int, new_color: int +//| ) -> None: +//| """Replace any pixels of ``old_color`` with ``new_color`` in the ``bitmap`` +//| +//| :param displayio.Bitmap bitmap: Bitmap that will be changed +//| :param int old_color: Bitmap palette index that will overwritten +//| :param int new_color: Bitmap palette index that will get put in the bitmap""" +//| ... +//| +//| +static mp_obj_t bitmaptools_obj_replace_color(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum {ARG_bitmap, ARG_old_color, ARG_new_color}; + + static const mp_arg_t allowed_args[] = { + {MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_old_color, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_new_color, MP_ARG_REQUIRED | MP_ARG_INT}, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_bitmap)); + + uint32_t old_color, new_color, color_depth; + old_color = args[ARG_old_color].u_int; + new_color = args[ARG_new_color].u_int; + + color_depth = (1 << destination->bits_per_value); + if (color_depth <= old_color || color_depth <= new_color) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q out of range"), MP_QSTR_color); + } + + common_hal_bitmaptools_replace_color(destination, old_color, new_color); + + return mp_const_none; +} + +MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_replace_color_obj, 0, bitmaptools_obj_replace_color); + //| def fill_region( //| dest_bitmap: displayio.Bitmap, x1: int, y1: int, x2: int, y2: int, value: int //| ) -> None: @@ -438,14 +478,14 @@ static mp_obj_t bitmaptools_obj_fill_region(size_t n_args, const mp_obj_t *pos_a enum {ARG_dest_bitmap, ARGS_X1_Y1_X2_Y2, ARG_value}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, ALLOWED_ARGS_X1_Y1_X2_Y2(MP_ARG_REQUIRED, MP_ARG_REQUIRED), - {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT}, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap uint32_t value, color_depth; value = args[ARG_value].u_int; @@ -487,10 +527,10 @@ static mp_obj_t bitmaptools_obj_boundary_fill(size_t n_args, const mp_obj_t *pos enum {ARG_dest_bitmap, ARG_x, ARG_y, ARG_fill_color_value, ARG_replaced_color_value}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_fill_color_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_fill_color_value, MP_ARG_REQUIRED | MP_ARG_INT}, {MP_QSTR_replaced_color_value, MP_ARG_INT, {.u_int = INT_MAX} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -548,17 +588,17 @@ static mp_obj_t bitmaptools_obj_draw_line(size_t n_args, const mp_obj_t *pos_arg enum {ARG_dest_bitmap, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_value}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_x1, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_y1, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_x2, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_y2, MP_ARG_REQUIRED | MP_ARG_INT}, + {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT}, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap uint32_t value, color_depth; value = args[ARG_value].u_int; @@ -635,16 +675,16 @@ static mp_obj_t bitmaptools_obj_draw_polygon(size_t n_args, const mp_obj_t *pos_ enum {ARG_dest_bitmap, ARG_xs, ARG_ys, ARG_value, ARG_close}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_xs, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_ys, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL}}, - {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL}}, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_xs, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_ys, MP_ARG_REQUIRED | MP_ARG_OBJ}, + {MP_QSTR_value, MP_ARG_REQUIRED | MP_ARG_INT}, {MP_QSTR_close, MP_ARG_BOOL, {.u_bool = true}}, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap mp_buffer_info_t xs_buf, ys_buf; mp_get_buffer_raise(args[ARG_xs].u_obj, &xs_buf, MP_BUFFER_READ); @@ -717,8 +757,8 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_polygon_obj, 0, bitmaptools_obj_draw static mp_obj_t bitmaptools_arrayblit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_bitmap, ARG_data, ARGS_X1_Y1_X2_Y2, ARG_skip_index }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_data, MP_ARG_REQUIRED | MP_ARG_OBJ }, ALLOWED_ARGS_X1_Y1_X2_Y2(0, 0), { MP_QSTR_skip_index, MP_ARG_OBJ, {.u_obj = mp_const_none } }, }; @@ -782,9 +822,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_arrayblit_obj, 0, bitmaptools_arrayblit); static mp_obj_t bitmaptools_readinto(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_bitmap, ARG_file, ARG_bits_per_pixel, ARG_element_size, ARG_reverse_pixels_in_element, ARG_swap_bytes_in_element, ARG_reverse_rows }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_file, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_bits_per_pixel, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_file, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_bits_per_pixel, MP_ARG_REQUIRED | MP_ARG_INT }, { MP_QSTR_element_size, MP_ARG_INT, { .u_int = 1 } }, { MP_QSTR_reverse_pixels_in_element, MP_ARG_BOOL, { .u_bool = false } }, { MP_QSTR_swap_bytes_in_element, MP_ARG_BOOL, { .u_bool = false } }, @@ -873,9 +913,9 @@ MAKE_ENUM_TYPE(bitmaptools, DitherAlgorithm, bitmaptools_dither_algorithm); static mp_obj_t bitmaptools_dither(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum { ARG_dest_bitmap, ARG_source_bitmap, ARG_source_colorspace, ARG_algorithm }; static const mp_arg_t allowed_args[] = { - { MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - { MP_QSTR_source_colorspace, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, + { MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_source_colorspace, MP_ARG_REQUIRED | MP_ARG_OBJ }, { MP_QSTR_algorithm, MP_ARG_OBJ, { .u_obj = MP_ROM_PTR((void *)&dither_algorithm_Atkinson_obj) } }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; @@ -977,7 +1017,7 @@ static mp_obj_t bitmaptools_obj_draw_circle(size_t n_args, const mp_obj_t *pos_a mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - displayio_bitmap_t *destination = MP_OBJ_TO_PTR(args[ARG_dest_bitmap].u_obj); // the destination bitmap + displayio_bitmap_t *destination = MP_OBJ_TO_PTR(mp_arg_validate_type(args[ARG_dest_bitmap].u_obj, &displayio_bitmap_type, MP_QSTR_dest_bitmap)); // the destination bitmap uint32_t value, color_depth; value = args[ARG_value].u_int; @@ -1038,10 +1078,10 @@ MP_DEFINE_CONST_FUN_OBJ_KW(bitmaptools_draw_circle_obj, 0, bitmaptools_obj_draw_ static mp_obj_t bitmaptools_obj_blit(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum {ARG_destination, ARG_source, ARG_x, ARG_y, ARG_x1, ARG_y1, ARG_x2, ARG_y2, ARG_skip_source_index, ARG_skip_dest_index}; static const mp_arg_t allowed_args[] = { - {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} }, - {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, - {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT, {.u_obj = MP_OBJ_NULL} }, + {MP_QSTR_dest_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + {MP_QSTR_source_bitmap, MP_ARG_REQUIRED | MP_ARG_OBJ }, + {MP_QSTR_x, MP_ARG_REQUIRED | MP_ARG_INT }, + {MP_QSTR_y, MP_ARG_REQUIRED | MP_ARG_INT }, ALLOWED_ARGS_X1_Y1_X2_Y2(0, 0), {MP_QSTR_skip_source_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, {MP_QSTR_skip_dest_index, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, @@ -1103,6 +1143,7 @@ static const mp_rom_map_elem_t bitmaptools_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_rotozoom), MP_ROM_PTR(&bitmaptools_rotozoom_obj) }, { MP_ROM_QSTR(MP_QSTR_arrayblit), MP_ROM_PTR(&bitmaptools_arrayblit_obj) }, { MP_ROM_QSTR(MP_QSTR_alphablend), MP_ROM_PTR(&bitmaptools_alphablend_obj) }, + { MP_ROM_QSTR(MP_QSTR_replace_color), MP_ROM_PTR(&bitmaptools_replace_color_obj) }, { MP_ROM_QSTR(MP_QSTR_fill_region), MP_ROM_PTR(&bitmaptools_fill_region_obj) }, { MP_ROM_QSTR(MP_QSTR_boundary_fill), MP_ROM_PTR(&bitmaptools_boundary_fill_obj) }, { MP_ROM_QSTR(MP_QSTR_draw_line), MP_ROM_PTR(&bitmaptools_draw_line_obj) }, diff --git a/shared-bindings/bitmaptools/__init__.h b/shared-bindings/bitmaptools/__init__.h index 21116b8bd4276..f193ef6d95282 100644 --- a/shared-bindings/bitmaptools/__init__.h +++ b/shared-bindings/bitmaptools/__init__.h @@ -42,6 +42,9 @@ void common_hal_bitmaptools_fill_region(displayio_bitmap_t *destination, int16_t x2, int16_t y2, uint32_t value); +void common_hal_bitmaptools_replace_color(displayio_bitmap_t *destination, + uint32_t old_color, uint32_t new_color); + void common_hal_bitmaptools_boundary_fill(displayio_bitmap_t *destination, int16_t x, int16_t y, uint32_t fill_color_value, uint32_t replaced_color_value); diff --git a/shared-bindings/board/__init__.h b/shared-bindings/board/__init__.h index 7c9a59d2dbd0d..4334309973005 100644 --- a/shared-bindings/board/__init__.h +++ b/shared-bindings/board/__init__.h @@ -11,22 +11,23 @@ #include "shared-bindings/microcontroller/Pin.h" // for the pin definitions +#if CIRCUITPY_MUTABLE_BOARD +extern mp_obj_dict_t board_module_globals; +#else extern const mp_obj_dict_t board_module_globals; +#endif static const MP_DEFINE_STR_OBJ(board_module_id_obj, CIRCUITPY_BOARD_ID); -bool common_hal_board_is_i2c(mp_obj_t obj); mp_obj_t common_hal_board_get_i2c(const mp_int_t instance); mp_obj_t common_hal_board_create_i2c(const mp_int_t instance); mp_obj_t board_i2c(size_t n_args, const mp_obj_t *args); MP_DECLARE_CONST_FUN_OBJ_0(board_i2c_obj); -bool common_hal_board_is_spi(mp_obj_t obj); mp_obj_t common_hal_board_get_spi(const mp_int_t instance); mp_obj_t common_hal_board_create_spi(const mp_int_t instance); mp_obj_t board_spi(size_t n_args, const mp_obj_t *args); MP_DECLARE_CONST_FUN_OBJ_0(board_spi_obj); -bool common_hal_board_is_uart(mp_obj_t obj); mp_obj_t common_hal_board_get_uart(const mp_int_t instance); mp_obj_t common_hal_board_create_uart(const mp_int_t instance); mp_obj_t board_uart(size_t n_args, const mp_obj_t *args); @@ -41,3 +42,7 @@ MP_DECLARE_CONST_FUN_OBJ_0(board_uart_obj); #define CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS \ { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_board) }, \ { MP_ROM_QSTR(MP_QSTR_board_id), MP_ROM_PTR(&board_module_id_obj) }, + +#define CIRCUITPYTHON_MUTABLE_BOARD_DICT_STANDARD_ITEMS \ + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_board) }, \ + { MP_ROM_QSTR(MP_QSTR_board_id), MP_OBJ_FROM_PTR(&board_module_id_obj) }, diff --git a/shared-bindings/busdisplay/BusDisplay.c b/shared-bindings/busdisplay/BusDisplay.c index 297a869057700..0bf171a5d089c 100644 --- a/shared-bindings/busdisplay/BusDisplay.c +++ b/shared-bindings/busdisplay/BusDisplay.c @@ -122,7 +122,6 @@ //| :param int native_frames_per_second: Number of display refreshes per second that occur with the given init_sequence. //| :param bool backlight_on_high: If True, pulling the backlight pin high turns the backlight on. //| :param bool SH1107_addressing: Special quirk for SH1107, use upper/lower column set and page set -//| :param int set_vertical_scroll: This parameter is accepted but ignored for backwards compatibility. It will be removed in a future release. //| :param int backlight_pwm_frequency: The frequency to use to drive the PWM for backlight brightness control. Default is 50000. //| """ //| ... @@ -133,7 +132,7 @@ static mp_obj_t busdisplay_busdisplay_make_new(const mp_obj_type_t *type, size_t ARG_rotation, ARG_color_depth, ARG_grayscale, ARG_pixels_in_byte_share_row, ARG_bytes_per_cell, ARG_reverse_pixels_in_byte, ARG_reverse_bytes_in_word, ARG_set_column_command, ARG_set_row_command, ARG_write_ram_command, - ARG_set_vertical_scroll, ARG_backlight_pin, ARG_brightness_command, + ARG_backlight_pin, ARG_brightness_command, ARG_brightness, ARG_single_byte_bounds, ARG_data_as_commands, ARG_auto_refresh, ARG_native_frames_per_second, ARG_backlight_on_high, ARG_SH1107_addressing, ARG_backlight_pwm_frequency }; @@ -154,7 +153,6 @@ static mp_obj_t busdisplay_busdisplay_make_new(const mp_obj_type_t *type, size_t { MP_QSTR_set_column_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2a} }, { MP_QSTR_set_row_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2b} }, { MP_QSTR_write_ram_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x2c} }, - { MP_QSTR_set_vertical_scroll, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x0} }, { MP_QSTR_backlight_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, { MP_QSTR_brightness_command, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = NO_BRIGHTNESS_COMMAND} }, { MP_QSTR_brightness, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, diff --git a/shared-bindings/busio/I2C.c b/shared-bindings/busio/I2C.c index 8aa35ec6e0709..5fa62f9df3859 100644 --- a/shared-bindings/busio/I2C.c +++ b/shared-bindings/busio/I2C.c @@ -67,7 +67,7 @@ static mp_obj_t busio_i2c_make_new(const mp_obj_type_t *type, size_t n_args, siz busio_i2c_obj_t *self = mp_obj_malloc_with_finaliser(busio_i2c_obj_t, &busio_i2c_type); common_hal_busio_i2c_construct(self, scl, sda, args[ARG_frequency].u_int, args[ARG_timeout].u_int); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); #else mp_raise_NotImplementedError(NULL); #endif // CIRCUITPY_BUSIO_I2C @@ -106,13 +106,13 @@ static void check_for_deinit(busio_i2c_obj_t *self) { // Provided by context manager helper. static void check_lock(busio_i2c_obj_t *self) { - asm (""); + __asm__ (""); if (!common_hal_busio_i2c_has_lock(self)) { mp_raise_RuntimeError(MP_ERROR_TEXT("Function requires lock")); } } -//| def probe(self, address: int) -> List[int]: +//| def probe(self, address: int) -> bool: //| """Check if a device at the specified address responds. //| //| :param int address: 7-bit device address @@ -228,10 +228,10 @@ static mp_obj_t busio_i2c_readfrom_into(size_t n_args, const mp_obj_t *pos_args, start *= stride_in_bytes; length *= stride_in_bytes; - uint8_t status = + mp_negative_errno_t status = common_hal_busio_i2c_read(self, args[ARG_address].u_int, ((uint8_t *)bufinfo.buf) + start, length); if (status != 0) { - mp_raise_OSError(status); + mp_raise_OSError(-status); } return mp_const_none; @@ -290,11 +290,11 @@ static mp_obj_t busio_i2c_writeto(size_t n_args, const mp_obj_t *pos_args, mp_ma length *= stride_in_bytes; // do the transfer - uint8_t status = + mp_negative_errno_t status = common_hal_busio_i2c_write(self, args[ARG_address].u_int, ((uint8_t *)bufinfo.buf) + start, length); if (status != 0) { - mp_raise_OSError(status); + mp_raise_OSError(-status); } return mp_const_none; @@ -377,10 +377,10 @@ static mp_obj_t busio_i2c_writeto_then_readfrom(size_t n_args, const mp_obj_t *p in_start *= in_stride_in_bytes; in_length *= in_stride_in_bytes; - uint8_t status = common_hal_busio_i2c_write_read(self, args[ARG_address].u_int, + mp_negative_errno_t status = common_hal_busio_i2c_write_read(self, args[ARG_address].u_int, ((uint8_t *)out_bufinfo.buf) + out_start, out_length, ((uint8_t *)in_bufinfo.buf) + in_start, in_length); if (status != 0) { - mp_raise_OSError(status); + mp_raise_OSError(-status); } return mp_const_none; diff --git a/shared-bindings/busio/I2C.h b/shared-bindings/busio/I2C.h index 55f2d0f010850..2978a90754490 100644 --- a/shared-bindings/busio/I2C.h +++ b/shared-bindings/busio/I2C.h @@ -7,6 +7,7 @@ #pragma once #include "py/obj.h" +#include "py/mperrno.h" #include "common-hal/microcontroller/Pin.h" #include "common-hal/busio/I2C.h" @@ -35,17 +36,17 @@ extern void common_hal_busio_i2c_unlock(busio_i2c_obj_t *self); // Probe the bus to see if a device acknowledges the given address. extern bool common_hal_busio_i2c_probe(busio_i2c_obj_t *self, uint8_t addr); -// Write to the device and return 0 on success or an appropriate error code from mperrno.h -extern uint8_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, +// Write to the device and return 0 on success or a negative error code from mperrno.h +extern mp_negative_errno_t common_hal_busio_i2c_write(busio_i2c_obj_t *self, uint16_t address, const uint8_t *data, size_t len); // Reads memory of the i2c device picking up where it left off and return 0 on -// success or an appropriate error code from mperrno.h -extern uint8_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, +// success or a negative error code from mperrno.h +extern mp_negative_errno_t common_hal_busio_i2c_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *data, size_t len); // Do a write and then a read in the same I2C transaction. -uint8_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t address, +mp_negative_errno_t common_hal_busio_i2c_write_read(busio_i2c_obj_t *self, uint16_t address, uint8_t *out_data, size_t out_len, uint8_t *in_data, size_t in_len); // This is used by the supervisor to claim I2C devices indefinitely. diff --git a/shared-bindings/busio/SPI.c b/shared-bindings/busio/SPI.c index 0c8ae1bfdd72c..962080648ce4d 100644 --- a/shared-bindings/busio/SPI.c +++ b/shared-bindings/busio/SPI.c @@ -9,17 +9,17 @@ #include +#include "py/binary.h" +#include "py/mperrno.h" +#include "py/objproperty.h" +#include "py/runtime.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/util.h" - #include "shared/runtime/buffer_helper.h" #include "shared/runtime/context_manager_helpers.h" -#include "py/binary.h" -#include "py/mperrno.h" -#include "py/objproperty.h" -#include "py/runtime.h" - +#include "shared/runtime/interrupt_char.h" +#include "supervisor/shared/tick.h" //| class SPI: //| """A 3-4 wire serial protocol @@ -88,7 +88,6 @@ // TODO(tannewt): Support LSB SPI. static mp_obj_t busio_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { #if CIRCUITPY_BUSIO_SPI - busio_spi_obj_t *self = mp_obj_malloc(busio_spi_obj_t, &busio_spi_type); enum { ARG_clock, ARG_MOSI, ARG_MISO, ARG_half_duplex }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -107,6 +106,7 @@ static mp_obj_t busio_spi_make_new(const mp_obj_type_t *type, size_t n_args, siz mp_raise_ValueError(MP_ERROR_TEXT("Must provide MISO or MOSI pin")); } + busio_spi_obj_t *self = mp_obj_malloc_with_finaliser(busio_spi_obj_t, &busio_spi_type); common_hal_busio_spi_construct(self, clock, mosi, miso, args[ARG_half_duplex].u_bool); return MP_OBJ_FROM_PTR(self); #else @@ -140,7 +140,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(busio_spi_deinit_obj, busio_spi_obj_deinit); // Provided by context manager helper. static void check_lock(busio_spi_obj_t *self) { - asm (""); + __asm__ (""); if (!common_hal_busio_spi_has_lock(self)) { mp_raise_RuntimeError(MP_ERROR_TEXT("Function requires lock")); } @@ -466,6 +466,7 @@ MP_PROPERTY_GETTER(busio_spi_frequency_obj, static const mp_rom_map_elem_t busio_spi_locals_dict_table[] = { #if CIRCUITPY_BUSIO_SPI { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&busio_spi_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&busio_spi_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, @@ -493,3 +494,17 @@ MP_DEFINE_CONST_OBJ_TYPE( busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj, qstr arg_name) { return mp_arg_validate_type(obj, &busio_spi_type, arg_name); } + +// Wait as long as needed for the lock. This is used by SD card access from USB. +// The default implementation is to busy-wait while running the background tasks. espressif is different. +bool common_hal_busio_spi_wait_for_lock(busio_spi_obj_t *self, uint32_t timeout_ms) { + uint64_t deadline = supervisor_ticks_ms64() + timeout_ms; + while (supervisor_ticks_ms64() < deadline && + !mp_hal_is_interrupted()) { + if (common_hal_busio_spi_try_lock(self)) { + return true; + } + RUN_BACKGROUND_TASKS; + } + return false; +} diff --git a/shared-bindings/busio/SPI.h b/shared-bindings/busio/SPI.h index 69e582411a169..76ed697d66531 100644 --- a/shared-bindings/busio/SPI.h +++ b/shared-bindings/busio/SPI.h @@ -22,6 +22,10 @@ extern void common_hal_busio_spi_construct(busio_spi_obj_t *self, extern void common_hal_busio_spi_deinit(busio_spi_obj_t *self); extern bool common_hal_busio_spi_deinited(busio_spi_obj_t *self); +// Mark as deinit without deiniting. This is used by displayio after copying the +// object elsewhere and prevents the heap from deiniting the object. +extern void common_hal_busio_spi_mark_deinit(busio_spi_obj_t *self); + extern bool common_hal_busio_spi_configure(busio_spi_obj_t *self, uint32_t baudrate, uint8_t polarity, uint8_t phase, uint8_t bits); extern bool common_hal_busio_spi_try_lock(busio_spi_obj_t *self); @@ -50,3 +54,7 @@ uint8_t common_hal_busio_spi_get_polarity(busio_spi_obj_t *self); extern void common_hal_busio_spi_never_reset(busio_spi_obj_t *self); extern busio_spi_obj_t *validate_obj_is_spi_bus(mp_obj_t obj_in, qstr arg_name); + +// Wait as long as needed for the lock. This is used by SD card access from USB. +// For most ports, busy-wait while running the background tasks. +MP_WEAK bool common_hal_busio_spi_wait_for_lock(busio_spi_obj_t *self, uint32_t timeout_ms); diff --git a/shared-bindings/busio/UART.c b/shared-bindings/busio/UART.c index 5f1d301b675be..017b983778115 100644 --- a/shared-bindings/busio/UART.c +++ b/shared-bindings/busio/UART.c @@ -157,7 +157,7 @@ static mp_obj_t busio_uart_make_new(const mp_obj_type_t *type, size_t n_args, si common_hal_busio_uart_construct(self, tx, rx, rts, cts, rs485_dir, rs485_invert, args[ARG_baudrate].u_int, bits, parity, stop, timeout, buffer_size, NULL, false); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); #else mp_raise_NotImplementedError(NULL); #endif // CIRCUITPY_BUSIO_UART @@ -368,14 +368,14 @@ static mp_obj_t busio_uart_obj_reset_input_buffer(mp_obj_t self_in) { return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(busio_uart_reset_input_buffer_obj, busio_uart_obj_reset_input_buffer); -//| class Parity: -//| """Enum-like class to define the parity used to verify correct data transfer.""" +//| class Parity: +//| """Enum-like class to define the parity used to verify correct data transfer.""" //| -//| ODD: int -//| """Total number of ones should be odd.""" +//| ODD: int +//| """Total number of ones should be odd.""" //| -//| EVEN: int -//| """Total number of ones should be even.""" +//| EVEN: int +//| """Total number of ones should be even.""" //| //| const mp_obj_type_t busio_uart_parity_type; diff --git a/shared-bindings/camera/Camera.c b/shared-bindings/camera/Camera.c index 6f42fa08bcfc4..f0991082e5039 100644 --- a/shared-bindings/camera/Camera.c +++ b/shared-bindings/camera/Camera.c @@ -41,10 +41,10 @@ //| ... //| static mp_obj_t camera_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - camera_obj_t *self = mp_obj_malloc(camera_obj_t, &camera_type); // No arguments mp_arg_check_num(n_args, n_kw, 0, 0, false); + camera_obj_t *self = mp_obj_malloc(camera_obj_t, &camera_type); common_hal_camera_construct(self); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/canio/Match.c b/shared-bindings/canio/Match.c index 5e44a7ce4ed53..e7c3dba42aab8 100644 --- a/shared-bindings/canio/Match.c +++ b/shared-bindings/canio/Match.c @@ -47,7 +47,8 @@ static mp_obj_t canio_match_make_new(const mp_obj_type_t *type, size_t n_args, s canio_match_obj_t *self = mp_obj_malloc(canio_match_obj_t, &canio_match_type); common_hal_canio_match_construct(self, id, mask, args[ARG_extended].u_bool); - return self; + + return MP_OBJ_FROM_PTR(self); } //| id: int diff --git a/shared-bindings/canio/Message.c b/shared-bindings/canio/Message.c index c40892b5edb06..848b5b15e43cb 100644 --- a/shared-bindings/canio/Message.c +++ b/shared-bindings/canio/Message.c @@ -41,7 +41,8 @@ static mp_obj_t canio_message_make_new(const mp_obj_type_t *type, size_t n_args, canio_message_obj_t *self = mp_obj_malloc(canio_message_obj_t, &canio_message_type); common_hal_canio_message_construct(self, args[ARG_id].u_int, data.buf, data.len, args[ARG_extended].u_bool); - return self; + + return MP_OBJ_FROM_PTR(self); } //| id: int diff --git a/shared-bindings/canio/RemoteTransmissionRequest.c b/shared-bindings/canio/RemoteTransmissionRequest.c index 966fee71215b2..bdc91c494a4a3 100644 --- a/shared-bindings/canio/RemoteTransmissionRequest.c +++ b/shared-bindings/canio/RemoteTransmissionRequest.c @@ -42,7 +42,8 @@ static mp_obj_t canio_remote_transmission_request_make_new(const mp_obj_type_t * canio_remote_transmission_request_obj_t *self = mp_obj_malloc(canio_remote_transmission_request_obj_t, &canio_remote_transmission_request_type); common_hal_canio_remote_transmission_request_construct(self, args[ARG_id].u_int, length, args[ARG_extended].u_bool); - return self; + + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/canio/__init__.c b/shared-bindings/canio/__init__.c index c65b119ff2401..2300a6652dd6b 100644 --- a/shared-bindings/canio/__init__.c +++ b/shared-bindings/canio/__init__.c @@ -36,7 +36,7 @@ //| //| Other implementations of the CAN device may exist (for instance, attached //| via an SPI bus). If so their constructor arguments may differ, but -//| otherwise we encourage implementors to follow the API that the core uses. +//| otherwise we encourage implementers to follow the API that the core uses. //| //| For more information on working with this module, refer to //| `this Learn Guide on using it `_. diff --git a/shared-bindings/countio/Counter.c b/shared-bindings/countio/Counter.c index c2e3ddc740495..9f8bd00645ce5 100644 --- a/shared-bindings/countio/Counter.c +++ b/shared-bindings/countio/Counter.c @@ -66,8 +66,8 @@ static mp_obj_t countio_counter_make_new(const mp_obj_type_t *type, size_t n_arg const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); const countio_edge_t edge = validate_edge(args[ARG_edge].u_obj, MP_QSTR_edge); const digitalio_pull_t pull = validate_pull(args[ARG_pull].u_obj, MP_QSTR_pull); - countio_counter_obj_t *self = mp_obj_malloc_with_finaliser(countio_counter_obj_t, &countio_counter_type); + countio_counter_obj_t *self = mp_obj_malloc_with_finaliser(countio_counter_obj_t, &countio_counter_type); common_hal_countio_counter_construct(self, pin, edge, pull); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/digitalio/DigitalInOut.c b/shared-bindings/digitalio/DigitalInOut.c index a29fdcc371ca9..58f90b0b1419f 100644 --- a/shared-bindings/digitalio/DigitalInOut.c +++ b/shared-bindings/digitalio/DigitalInOut.c @@ -17,6 +17,7 @@ #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" #include "shared-bindings/digitalio/Direction.h" #include "shared-bindings/digitalio/DriveMode.h" #include "shared-bindings/digitalio/Pull.h" @@ -70,9 +71,9 @@ static mp_obj_t digitalio_digitalinout_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); - digitalio_digitalinout_obj_t *self = mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); - const mcu_pin_obj_t *pin = common_hal_digitalio_validate_pin(args[0]); + + digitalio_digitalinout_obj_t *self = mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); common_hal_digitalio_digitalinout_construct(self, pin); return MP_OBJ_FROM_PTR(self); @@ -190,9 +191,9 @@ static mp_obj_t digitalio_digitalinout_obj_get_direction(mp_obj_t self_in) { check_for_deinit(self); digitalio_direction_t direction = common_hal_digitalio_digitalinout_get_direction(self); if (direction == DIRECTION_INPUT) { - return (mp_obj_t)&digitalio_direction_input_obj; + return MP_OBJ_FROM_PTR(&digitalio_direction_input_obj); } - return (mp_obj_t)&digitalio_direction_output_obj; + return MP_OBJ_FROM_PTR(&digitalio_direction_output_obj); } MP_DEFINE_CONST_FUN_OBJ_1(digitalio_digitalinout_get_direction_obj, digitalio_digitalinout_obj_get_direction); @@ -254,9 +255,9 @@ static mp_obj_t digitalio_digitalinout_obj_get_drive_mode(mp_obj_t self_in) { } digitalio_drive_mode_t drive_mode = common_hal_digitalio_digitalinout_get_drive_mode(self); if (drive_mode == DRIVE_MODE_PUSH_PULL) { - return (mp_obj_t)&digitalio_drive_mode_push_pull_obj; + return MP_OBJ_FROM_PTR(&digitalio_drive_mode_push_pull_obj); } - return (mp_obj_t)&digitalio_drive_mode_open_drain_obj; + return MP_OBJ_FROM_PTR(&digitalio_drive_mode_open_drain_obj); } MP_DEFINE_CONST_FUN_OBJ_1(digitalio_digitalinout_get_drive_mode_obj, digitalio_digitalinout_obj_get_drive_mode); @@ -341,12 +342,91 @@ static const mp_rom_map_elem_t digitalio_digitalinout_locals_dict_table[] = { static MP_DEFINE_CONST_DICT(digitalio_digitalinout_locals_dict, digitalio_digitalinout_locals_dict_table); +// Protocol implementation - thin wrappers to match protocol signature +void digitalinout_deinit(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_digitalio_digitalinout_deinit(self); +} + +bool digitalinout_deinited(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_deinited(self); +} + +digitalinout_result_t digitalinout_switch_to_input(mp_obj_t self_in, digitalio_pull_t pull) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_switch_to_input(self, pull); +} + +digitalinout_result_t digitalinout_switch_to_output(mp_obj_t self_in, bool value, digitalio_drive_mode_t drive_mode) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_switch_to_output(self, value, drive_mode); +} + +digitalio_direction_t digitalinout_get_direction(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_get_direction(self); +} + +mp_negative_errno_t digitalinout_set_value(mp_obj_t self_in, bool value) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_digitalio_digitalinout_set_value(self, value); + return 0; +} + +mp_negative_errno_t digitalinout_get_value(mp_obj_t self_in, bool *value) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + *value = common_hal_digitalio_digitalinout_get_value(self); + return 0; +} + +digitalinout_result_t digitalinout_set_drive_mode(mp_obj_t self_in, digitalio_drive_mode_t drive_mode) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_set_drive_mode(self, drive_mode); +} + +digitalio_drive_mode_t digitalinout_get_drive_mode(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_get_drive_mode(self); +} + +digitalinout_result_t digitalinout_set_pull(mp_obj_t self_in, digitalio_pull_t pull) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_set_pull(self, pull); +} + +digitalio_pull_t digitalinout_get_pull(mp_obj_t self_in) { + digitalio_digitalinout_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_digitalio_digitalinout_get_pull(self); +} + +#if CIRCUITPY_DIGITALINOUT_PROTOCOL +static const digitalinout_p_t digitalinout_digitalinout_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_DigitalInOut) + .deinit = digitalinout_deinit, + .deinited = digitalinout_deinited, + .switch_to_input = digitalinout_switch_to_input, + .switch_to_output = digitalinout_switch_to_output, + .get_direction = digitalinout_get_direction, + .get_value = digitalinout_get_value, + .set_value = digitalinout_set_value, + .get_drive_mode = digitalinout_get_drive_mode, + .set_drive_mode = digitalinout_set_drive_mode, + .get_pull = digitalinout_get_pull, + .set_pull = digitalinout_set_pull, +}; +#endif + MP_DEFINE_CONST_OBJ_TYPE( digitalio_digitalinout_type, MP_QSTR_DigitalInOut, MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, make_new, digitalio_digitalinout_make_new, locals_dict, &digitalio_digitalinout_locals_dict + #if CIRCUITPY_DIGITALINOUT_PROTOCOL + , + protocol, &digitalinout_digitalinout_proto + #endif ); // Helper for validating digitalio.DigitalInOut arguments diff --git a/shared-bindings/digitalio/DigitalInOut.h b/shared-bindings/digitalio/DigitalInOut.h index f030e27a60b98..8d3fe8c00a2c5 100644 --- a/shared-bindings/digitalio/DigitalInOut.h +++ b/shared-bindings/digitalio/DigitalInOut.h @@ -28,6 +28,9 @@ typedef enum { #endif } digitalinout_result_t; +// Include protocol after types are defined +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" + typedef enum { DIGITALINOUT_REG_READ, DIGITALINOUT_REG_WRITE, @@ -54,3 +57,16 @@ digitalio_digitalinout_obj_t *assert_digitalinout(mp_obj_t obj); volatile uint32_t *common_hal_digitalio_digitalinout_get_reg(digitalio_digitalinout_obj_t *self, digitalinout_reg_op_t op, uint32_t *mask); bool common_hal_digitalio_has_reg_op(digitalinout_reg_op_t op); + +// Protocol wrapper functions - always available for direct calls +void digitalinout_deinit(mp_obj_t self_in); +bool digitalinout_deinited(mp_obj_t self_in); +digitalinout_result_t digitalinout_switch_to_input(mp_obj_t self_in, digitalio_pull_t pull); +digitalinout_result_t digitalinout_switch_to_output(mp_obj_t self_in, bool value, digitalio_drive_mode_t drive_mode); +digitalio_direction_t digitalinout_get_direction(mp_obj_t self_in); +mp_negative_errno_t digitalinout_set_value(mp_obj_t self_in, bool value); +mp_negative_errno_t digitalinout_get_value(mp_obj_t self_in, bool *value); +digitalinout_result_t digitalinout_set_drive_mode(mp_obj_t self_in, digitalio_drive_mode_t drive_mode); +digitalio_drive_mode_t digitalinout_get_drive_mode(mp_obj_t self_in); +digitalinout_result_t digitalinout_set_pull(mp_obj_t self_in, digitalio_pull_t pull); +digitalio_pull_t digitalinout_get_pull(mp_obj_t self_in); diff --git a/shared-bindings/digitalio/DigitalInOutProtocol.c b/shared-bindings/digitalio/DigitalInOutProtocol.c new file mode 100644 index 0000000000000..48b428ad46336 --- /dev/null +++ b/shared-bindings/digitalio/DigitalInOutProtocol.c @@ -0,0 +1,397 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" + +#include "py/obj.h" +#include "py/objtype.h" +#include "py/proto.h" +#include "py/runtime.h" +#include "py/nlr.h" +#include "py/gc.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" +#include "shared-bindings/util.h" +#include "supervisor/port_heap.h" + +//| from typing import Protocol, Optional +//| +//| class DigitalInOutProtocol(Protocol): +//| """Protocol for digital input/output pin control. +//| +//| Any object that implements this protocol can be used as a digital pin, +//| providing compatibility with code expecting a `digitalio.DigitalInOut`. +//| """ +//| +//| def deinit(self) -> None: +//| """Deinitialize the pin and release hardware resources.""" +//| ... +//| +//| def deinited(self) -> bool: +//| """Check whether the pin has been deinitialized. +//| +//| :return: True if deinitialized, False otherwise +//| """ +//| ... +//| +//| def switch_to_input(self, pull: Optional[digitalio.Pull] = None) -> None: +//| """Configure the pin as a digital input. +//| +//| :param pull: Pull resistor configuration (UP, DOWN, or None) +//| """ +//| ... +//| +//| def switch_to_output( +//| self, +//| value: bool = False, +//| drive_mode: digitalio.DriveMode = digitalio.DriveMode.PUSH_PULL +//| ) -> None: +//| """Configure the pin as a digital output. +//| +//| :param value: Initial output value (default False) +//| :param drive_mode: Output drive mode (PUSH_PULL or OPEN_DRAIN) +//| """ +//| ... +//| +//| @property +//| def direction(self) -> digitalio.Direction: +//| """The pin direction (INPUT or OUTPUT).""" +//| ... +//| +//| @direction.setter +//| def direction(self, value: digitalio.Direction) -> None: +//| ... +//| +//| @property +//| def value(self) -> bool: +//| """The digital logic level of the pin.""" +//| ... +//| +//| @value.setter +//| def value(self, val: bool) -> None: +//| ... +//| +//| @property +//| def pull(self) -> Optional[digitalio.Pull]: +//| """The pull resistor configuration for inputs (UP, DOWN, or None).""" +//| ... +//| +//| @pull.setter +//| def pull(self, pul: Optional[digitalio.Pull]) -> None: +//| ... +//| +//| @property +//| def drive_mode(self) -> digitalio.DriveMode: +//| """The drive mode for outputs (PUSH_PULL or OPEN_DRAIN).""" +//| ... +//| +//| @drive_mode.setter +//| def drive_mode(self, mode: digitalio.DriveMode) -> None: +//| ... +//| +// C Implementation Notes: +// ----------------------- +// For C implementations, define a digitalinout_p_t protocol structure and assign it +// to your type's protocol field in MP_DEFINE_CONST_OBJ_TYPE. +// +// Example: +// static const digitalinout_p_t my_type_proto = { +// MP_PROTO_IMPLEMENT(MP_QSTR_DigitalInOut) +// .construct = my_construct_func, +// .deinit = my_deinit_func, +// .deinited = my_deinited_func, +// .switch_to_input = my_switch_to_input_func, +// .switch_to_output = my_switch_to_output_func, +// .get_direction = my_get_direction_func, +// .get_value = my_get_value_func, +// .set_value = my_set_value_func, +// .get_drive_mode = my_get_drive_mode_func, +// .set_drive_mode = my_set_drive_mode_func, +// .get_pull = my_get_pull_func, +// .set_pull = my_set_pull_func, +// }; +// +// MP_DEFINE_CONST_OBJ_TYPE( +// my_type, +// MP_QSTR_MyType, +// MP_TYPE_FLAG_NONE, +// make_new, my_make_new, +// protocol, &my_type_proto +// ); +// +// See shared-bindings/digitalio/DigitalInOut.c for a complete example. +// + +#if CIRCUITPY_DIGITALINOUT_PROTOCOL +static void check_object_has_method(mp_obj_t obj, qstr method_name) { + mp_obj_t dest[2]; + mp_load_method_protected(obj, method_name, dest, true); + if (dest[0] == MP_OBJ_NULL) { + mp_raise_TypeError_varg(MP_ERROR_TEXT("%q object missing '%q' method"), MP_OBJ_TO_PTR(obj), method_name); + } +} + +static void check_object_has_attr(mp_obj_t obj, qstr attr_name) { + mp_obj_t dest[2]; + mp_load_method_protected(obj, attr_name, dest, true); + if (dest[0] == MP_OBJ_NULL) { + mp_raise_TypeError_varg(MP_ERROR_TEXT("%q object missing '%q' attribute"), MP_OBJ_TO_PTR(obj), attr_name); + } +} +#endif + +mp_obj_t digitalinout_protocol_from_pin( + mp_obj_t pin_or_dio, + qstr arg_name, + bool allow_none, + bool use_port_allocation, + bool *out_owns_pin) { + + *out_owns_pin = false; + + // Handle None case + if (allow_none && pin_or_dio == mp_const_none) { + return mp_const_none; + } + + // Check if it's a Pin + if (mp_obj_is_type(pin_or_dio, &mcu_pin_type)) { + // Validate the pin is free + const mcu_pin_obj_t *pin; + if (allow_none) { + pin = validate_obj_is_free_pin_or_none(pin_or_dio, arg_name); + if (pin == NULL) { + return mp_const_none; + } + } else { + pin = validate_obj_is_free_pin(pin_or_dio, arg_name); + } + + // Allocate and construct a DigitalInOut object + // Use port_malloc if GC is not available or if forced + digitalio_digitalinout_obj_t *dio; + if (use_port_allocation) { + dio = port_malloc(sizeof(digitalio_digitalinout_obj_t), false); + } else { + dio = m_malloc(sizeof(digitalio_digitalinout_obj_t)); + } + dio->base.type = &digitalio_digitalinout_type; + mp_obj_t dio_obj = MP_OBJ_FROM_PTR(dio); + *out_owns_pin = true; + + digitalinout_result_t result = common_hal_digitalio_digitalinout_construct(dio_obj, pin); + if (result != DIGITALINOUT_OK) { + // Free the allocation on error + if (use_port_allocation) { + port_free(dio); + } + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q init failed"), arg_name); + } + return dio_obj; + } + + #if CIRCUITPY_DIGITALINOUT_PROTOCOL + // Check if it natively implements the DigitalInOutProtocol + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, pin_or_dio); + if (proto != NULL) { + // Native protocol support - use it directly + return pin_or_dio; + } + + // Verify the object has the required methods/attributes + check_object_has_method(pin_or_dio, MP_QSTR_deinit); + check_object_has_method(pin_or_dio, MP_QSTR_switch_to_input); + check_object_has_method(pin_or_dio, MP_QSTR_switch_to_output); + check_object_has_attr(pin_or_dio, MP_QSTR_deinited); + check_object_has_attr(pin_or_dio, MP_QSTR_direction); + check_object_has_attr(pin_or_dio, MP_QSTR_value); + check_object_has_attr(pin_or_dio, MP_QSTR_drive_mode); + check_object_has_attr(pin_or_dio, MP_QSTR_pull); + + // Object has all required attributes - use it as DigitalInOutProtocol + return pin_or_dio; + #else + mp_raise_TypeError_varg(MP_ERROR_TEXT("'%q' object does not support '%q'"), + mp_obj_get_type_qstr(pin_or_dio), MP_QSTR_DigitalInOut); + #endif +} + +// These functions are only used when CIRCUITPY_DIGITALINOUT_PROTOCOL is enabled. +// Otherwise, the digitalinout_* functions are called directly. +#if CIRCUITPY_DIGITALINOUT_PROTOCOL +void digitalinout_protocol_deinit(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_protocol_digitalinout, self); + if (proto && proto->deinit) { + proto->deinit(self); + return; + } + + // Fallback to Python method call + mp_obj_t dest[2]; + mp_load_method_maybe(self, MP_QSTR_deinit, dest); + if (dest[0] != MP_OBJ_NULL) { + mp_call_method_n_kw(0, 0, dest); + return; + } +} + +bool digitalinout_protocol_deinited(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->deinited) { + return proto->deinited(self); + } + + // Try as attribute + mp_obj_t attr = mp_load_attr(self, MP_QSTR_deinited); + return mp_obj_is_true(attr); +} + +digitalinout_result_t digitalinout_protocol_switch_to_input(mp_obj_t self, digitalio_pull_t pull) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->switch_to_input) { + return proto->switch_to_input(self, pull); + } + + // Fallback to Python method call + mp_obj_t dest[3]; + mp_load_method_maybe(self, MP_QSTR_switch_to_input, dest); + if (dest[0] != MP_OBJ_NULL) { + mp_obj_t pull_obj = mp_const_none; + if (pull == PULL_UP) { + pull_obj = MP_OBJ_FROM_PTR(&digitalio_pull_up_obj); + } else if (pull == PULL_DOWN) { + pull_obj = MP_OBJ_FROM_PTR(&digitalio_pull_down_obj); + } + dest[2] = pull_obj; + mp_call_method_n_kw(1, 0, dest); + return DIGITALINOUT_OK; + } + + return DIGITALINOUT_PIN_BUSY; +} + +digitalinout_result_t digitalinout_protocol_switch_to_output(mp_obj_t self, bool value, digitalio_drive_mode_t drive_mode) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->switch_to_output) { + return proto->switch_to_output(self, value, drive_mode); + } + + // Fallback to Python method call + mp_obj_t dest[4]; + mp_load_method_maybe(self, MP_QSTR_switch_to_output, dest); + if (dest[0] != MP_OBJ_NULL) { + dest[2] = mp_obj_new_bool(value); + dest[3] = (drive_mode == DRIVE_MODE_PUSH_PULL) ? + MP_OBJ_FROM_PTR(&digitalio_drive_mode_push_pull_obj) : + MP_OBJ_FROM_PTR(&digitalio_drive_mode_open_drain_obj); + mp_call_method_n_kw(2, 0, dest); + return DIGITALINOUT_OK; + } + + mp_raise_TypeError(MP_ERROR_TEXT("object does not support DigitalInOut protocol")); +} + +digitalio_direction_t digitalinout_protocol_get_direction(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->get_direction) { + return proto->get_direction(self); + } + + // Fallback to Python attribute access + mp_obj_t direction = mp_load_attr(self, MP_QSTR_direction); + if (direction == MP_ROM_PTR(&digitalio_direction_input_obj)) { + return DIRECTION_INPUT; + } + return DIRECTION_OUTPUT; +} + +mp_negative_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->set_value) { + return proto->set_value(self, value); + } + + // Fallback to Python attribute assignment + mp_store_attr(self, MP_QSTR_value, mp_obj_new_bool(value)); + return 0; +} + +mp_negative_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->get_value) { + return proto->get_value(self, value); + } + + // Fallback to Python attribute access + *value = mp_obj_is_true(mp_load_attr(self, MP_QSTR_value)); + return 0; +} + +digitalinout_result_t digitalinout_protocol_set_drive_mode(mp_obj_t self, digitalio_drive_mode_t drive_mode) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->set_drive_mode) { + return proto->set_drive_mode(self, drive_mode); + } + + // Fallback to Python attribute assignment + mp_obj_t drive_mode_obj = (drive_mode == DRIVE_MODE_PUSH_PULL) ? + MP_OBJ_FROM_PTR(&digitalio_drive_mode_push_pull_obj) : + MP_OBJ_FROM_PTR(&digitalio_drive_mode_open_drain_obj); + mp_store_attr(self, MP_QSTR_drive_mode, drive_mode_obj); + return DIGITALINOUT_OK; +} + +digitalio_drive_mode_t digitalinout_protocol_get_drive_mode(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->get_drive_mode) { + return proto->get_drive_mode(self); + } + + // Fallback to Python attribute access + mp_obj_t drive_mode = mp_load_attr(self, MP_QSTR_drive_mode); + if (drive_mode == MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj)) { + return DRIVE_MODE_OPEN_DRAIN; + } + return DRIVE_MODE_PUSH_PULL; +} + +digitalinout_result_t digitalinout_protocol_set_pull(mp_obj_t self, digitalio_pull_t pull) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->set_pull) { + return proto->set_pull(self, pull); + } + + // Fallback to Python attribute assignment + mp_obj_t pull_obj = mp_const_none; + if (pull == PULL_UP) { + pull_obj = MP_OBJ_FROM_PTR(&digitalio_pull_up_obj); + } else if (pull == PULL_DOWN) { + pull_obj = MP_OBJ_FROM_PTR(&digitalio_pull_down_obj); + } + mp_store_attr(self, MP_QSTR_pull, pull_obj); + return DIGITALINOUT_OK; +} + +digitalio_pull_t digitalinout_protocol_get_pull(mp_obj_t self) { + const digitalinout_p_t *proto = mp_proto_get(MP_QSTR_DigitalInOut, self); + if (proto && proto->get_pull) { + return proto->get_pull(self); + } + + // Fallback to Python attribute access + mp_obj_t pull = mp_load_attr(self, MP_QSTR_pull); + if (pull == MP_OBJ_FROM_PTR(&digitalio_pull_up_obj)) { + return PULL_UP; + } else if (pull == MP_OBJ_FROM_PTR(&digitalio_pull_down_obj)) { + return PULL_DOWN; + } + return PULL_NONE; +} + +#endif // CIRCUITPY_DIGITALINOUT_PROTOCOL diff --git a/shared-bindings/digitalio/DigitalInOutProtocol.h b/shared-bindings/digitalio/DigitalInOutProtocol.h new file mode 100644 index 0000000000000..6cb92a3735f33 --- /dev/null +++ b/shared-bindings/digitalio/DigitalInOutProtocol.h @@ -0,0 +1,83 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" +#include "py/proto.h" +#include "py/mperrno.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" + +// Protocol structure for DigitalInOut implementations +// Note: mcu_pin_obj_t and digitalinout_result_t are defined by files that include this header +typedef struct _digitalinout_p_t { + MP_PROTOCOL_HEAD // MP_QSTR_DigitalInOut + void (*deinit)(mp_obj_t self); + bool (*deinited)(mp_obj_t self); + digitalinout_result_t (*switch_to_input)(mp_obj_t self, digitalio_pull_t pull); + digitalinout_result_t (*switch_to_output)(mp_obj_t self, bool value, digitalio_drive_mode_t drive_mode); + digitalio_direction_t (*get_direction)(mp_obj_t self); + mp_negative_errno_t (*set_value)(mp_obj_t self, bool value); // Return 0 if ok + mp_negative_errno_t (*get_value)(mp_obj_t self, bool *value); // Return 0 if ok + digitalinout_result_t (*set_drive_mode)(mp_obj_t self, digitalio_drive_mode_t drive_mode); + digitalio_drive_mode_t (*get_drive_mode)(mp_obj_t self); + digitalinout_result_t (*set_pull)(mp_obj_t self, digitalio_pull_t pull); + digitalio_pull_t (*get_pull)(mp_obj_t self); +} digitalinout_p_t; + +// Protocol helper functions +// These functions work with any object that implements the DigitalInOut protocol, +// either through native C protocol or Python attributes/methods. + +// Converts a Pin or DigitalInOutProtocol to a DigitalInOutProtocol object. +// If pin_or_dio is a Pin, allocates and initializes a DigitalInOut object. +// If pin_or_dio is already a DigitalInOutProtocol, returns it directly. +// If allow_none is true and pin_or_dio is None, returns None. +// If force_port_allocation is true, uses port_malloc instead of GC allocation. +// Sets *out_owns_pin to true if a new DigitalInOut was allocated (caller must deinit and free). +// Returns the DigitalInOutProtocol object to use. +// Raises an exception on error. +// Note: To free allocated objects, deinit first, then use gc_ptr_on_heap() to determine +// if port_free() should be called (if not on heap) or let GC handle it (if on heap). +mp_obj_t digitalinout_protocol_from_pin( + mp_obj_t pin_or_dio, + qstr arg_name, + bool allow_none, + bool force_port_allocation, + bool *out_owns_pin); + +#if CIRCUITPY_DIGITALINOUT_PROTOCOL +// Protocol helper functions that do protocol lookup or Python fallback +void digitalinout_protocol_deinit(mp_obj_t self); +bool digitalinout_protocol_deinited(mp_obj_t self); +digitalinout_result_t digitalinout_protocol_switch_to_input(mp_obj_t self, digitalio_pull_t pull); +digitalinout_result_t digitalinout_protocol_switch_to_output(mp_obj_t self, bool value, digitalio_drive_mode_t drive_mode); +digitalio_direction_t digitalinout_protocol_get_direction(mp_obj_t self); +mp_negative_errno_t digitalinout_protocol_set_value(mp_obj_t self, bool value); +mp_negative_errno_t digitalinout_protocol_get_value(mp_obj_t self, bool *value); +digitalinout_result_t digitalinout_protocol_set_drive_mode(mp_obj_t self, digitalio_drive_mode_t drive_mode); +digitalio_drive_mode_t digitalinout_protocol_get_drive_mode(mp_obj_t self); +digitalinout_result_t digitalinout_protocol_set_pull(mp_obj_t self, digitalio_pull_t pull); +digitalio_pull_t digitalinout_protocol_get_pull(mp_obj_t self); +#else +// When protocol is disabled, map directly to native DigitalInOut functions +#define digitalinout_protocol_deinit digitalinout_deinit +#define digitalinout_protocol_deinited digitalinout_deinited +#define digitalinout_protocol_switch_to_input digitalinout_switch_to_input +#define digitalinout_protocol_switch_to_output digitalinout_switch_to_output +#define digitalinout_protocol_get_direction digitalinout_get_direction +#define digitalinout_protocol_set_value digitalinout_set_value +#define digitalinout_protocol_get_value digitalinout_get_value +#define digitalinout_protocol_set_drive_mode digitalinout_set_drive_mode +#define digitalinout_protocol_get_drive_mode digitalinout_get_drive_mode +#define digitalinout_protocol_set_pull digitalinout_set_pull +#define digitalinout_protocol_get_pull digitalinout_get_pull +#endif diff --git a/shared-bindings/displayio/ColorConverter.c b/shared-bindings/displayio/ColorConverter.c index e2721cfdef244..bfd037016d41f 100644 --- a/shared-bindings/displayio/ColorConverter.c +++ b/shared-bindings/displayio/ColorConverter.c @@ -39,7 +39,6 @@ static mp_obj_t displayio_colorconverter_make_new(const mp_obj_type_t *type, siz mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); displayio_colorconverter_t *self = mp_obj_malloc(displayio_colorconverter_t, &displayio_colorconverter_type); - common_hal_displayio_colorconverter_construct(self, args[ARG_dither].u_bool, (displayio_colorspace_t)cp_enum_value(&displayio_colorspace_type, args[ARG_input_colorspace].u_obj, MP_QSTR_input_colorspace)); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/displayio/TileGrid.c b/shared-bindings/displayio/TileGrid.c index eddf3f66c4ec0..5d54c1e3e306b 100644 --- a/shared-bindings/displayio/TileGrid.c +++ b/shared-bindings/displayio/TileGrid.c @@ -49,7 +49,7 @@ void displayio_tilegrid_validate_pixel_shader(mp_obj_t pixel_shader) { //| self, //| bitmap: Union[Bitmap, OnDiskBitmap], //| *, -//| pixel_shader: Union[ColorConverter, Palette], +//| pixel_shader: Union[ColorConverter, Palette, tilepalettemapper.TilePaletteMapper], //| width: int = 1, //| height: int = 1, //| tile_width: Optional[int] = None, @@ -68,7 +68,7 @@ void displayio_tilegrid_validate_pixel_shader(mp_obj_t pixel_shader) { //| tile_width and tile_height match the height of the bitmap by default. //| //| :param Bitmap,OnDiskBitmap bitmap: The bitmap storing one or more tiles. -//| :param ColorConverter,Palette pixel_shader: The pixel shader that produces colors from values +//| :param ColorConverter,Palette,tilepalettemapper.TilePaletteMapper pixel_shader: The pixel shader that produces colors from values //| :param int width: Width of the grid in tiles. //| :param int height: Height of the grid in tiles. //| :param int tile_width: Width of a single tile in pixels. Defaults to the full Bitmap and must evenly divide into the Bitmap's dimensions. @@ -133,6 +133,7 @@ static mp_obj_t displayio_tilegrid_make_new(const mp_obj_type_t *type, size_t n_ bitmap_width / tile_width, bitmap_height / tile_height, pixel_shader, args[ARG_width].u_int, args[ARG_height].u_int, tile_width, tile_height, x, y, args[ARG_default_tile].u_int); + return MP_OBJ_FROM_PTR(self); } @@ -330,7 +331,7 @@ static mp_obj_t displayio_tilegrid_obj_contains(mp_obj_t self_in, mp_obj_t touch } MP_DEFINE_CONST_FUN_OBJ_2(displayio_tilegrid_contains_obj, displayio_tilegrid_obj_contains); -//| pixel_shader: Union[ColorConverter, Palette] +//| pixel_shader: Union[ColorConverter, Palette, tilepalettemapper.TilePaletteMapper] //| """The pixel shader of the tilegrid.""" static mp_obj_t displayio_tilegrid_obj_get_pixel_shader(mp_obj_t self_in) { displayio_tilegrid_t *self = native_tilegrid(self_in); diff --git a/shared-bindings/displayio/__init__.h b/shared-bindings/displayio/__init__.h index 88e9650cf44a9..583f6b217fa9f 100644 --- a/shared-bindings/displayio/__init__.h +++ b/shared-bindings/displayio/__init__.h @@ -49,4 +49,5 @@ typedef bool (*display_bus_begin_transaction)(mp_obj_t bus); typedef void (*display_bus_send)(mp_obj_t bus, display_byte_type_t byte_type, display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length); typedef void (*display_bus_end_transaction)(mp_obj_t bus); +typedef void (*display_bus_flush)(mp_obj_t bus); typedef void (*display_bus_collect_ptrs)(mp_obj_t bus); diff --git a/shared-bindings/epaperdisplay/EPaperDisplay.c b/shared-bindings/epaperdisplay/EPaperDisplay.c index 5d72df815d0a7..cd9c18e27d526 100644 --- a/shared-bindings/epaperdisplay/EPaperDisplay.c +++ b/shared-bindings/epaperdisplay/EPaperDisplay.c @@ -54,6 +54,7 @@ //| write_color_ram_command: Optional[int] = None, //| color_bits_inverted: bool = False, //| highlight_color: int = 0x000000, +//| highlight_color2: int = 0x000000, //| refresh_display_command: Union[int, circuitpython_typing.ReadableBuffer], //| refresh_time: float = 40, //| busy_pin: Optional[microcontroller.Pin] = None, @@ -97,6 +98,7 @@ //| :param int write_color_ram_command: Command used to write pixels values into the update region //| :param bool color_bits_inverted: True if 0 bits are used to show the color. Otherwise, 1 means to show color. //| :param int highlight_color: RGB888 of source color to highlight with third ePaper color. +//| :param int highlight_color2: RGB888 of source color to highlight with fourth ePaper color. //| :param int refresh_display_command: Command used to start a display refresh. Single int or byte-packed command sequence //| :param float refresh_time: Time it takes to refresh the display before the stop_sequence should be sent. Ignored when busy_pin is provided. //| :param microcontroller.Pin busy_pin: Pin used to signify the display is busy @@ -117,7 +119,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type, ARG_ram_width, ARG_ram_height, ARG_colstart, ARG_rowstart, ARG_rotation, ARG_set_column_window_command, ARG_set_row_window_command, ARG_set_current_column_command, ARG_set_current_row_command, ARG_write_black_ram_command, ARG_black_bits_inverted, - ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, + ARG_write_color_ram_command, ARG_color_bits_inverted, ARG_highlight_color, ARG_highlight_color2, ARG_refresh_display_command, ARG_refresh_time, ARG_busy_pin, ARG_busy_state, ARG_seconds_per_frame, ARG_always_toggle_chip_select, ARG_grayscale, ARG_advanced_color_epaper, ARG_spectra6, ARG_two_byte_sequence_length, ARG_start_up_time, ARG_address_little_endian }; @@ -141,6 +143,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type, { MP_QSTR_write_color_ram_command, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, { MP_QSTR_color_bits_inverted, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, { MP_QSTR_highlight_color, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} }, + { MP_QSTR_highlight_color2, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0x000000} }, { MP_QSTR_refresh_display_command, MP_ARG_OBJ | MP_ARG_REQUIRED }, { MP_QSTR_refresh_time, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(40)} }, { MP_QSTR_busy_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, @@ -181,6 +184,7 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type, mp_int_t write_color_ram_command = NO_COMMAND; mp_int_t highlight_color = args[ARG_highlight_color].u_int; + mp_int_t highlight_color2 = args[ARG_highlight_color2].u_int; if (args[ARG_write_color_ram_command].u_obj != mp_const_none) { write_color_ram_command = mp_obj_get_int(args[ARG_write_color_ram_command].u_obj); } @@ -207,20 +211,43 @@ static mp_obj_t epaperdisplay_epaperdisplay_make_new(const mp_obj_type_t *type, } self->base.type = &epaperdisplay_epaperdisplay_type; - common_hal_epaperdisplay_epaperdisplay_construct( - self, - display_bus, - start_bufinfo.buf, start_bufinfo.len, start_up_time, stop_bufinfo.buf, stop_bufinfo.len, - args[ARG_width].u_int, args[ARG_height].u_int, args[ARG_ram_width].u_int, args[ARG_ram_height].u_int, - args[ARG_colstart].u_int, args[ARG_rowstart].u_int, rotation, - args[ARG_set_column_window_command].u_int, args[ARG_set_row_window_command].u_int, - args[ARG_set_current_column_command].u_int, args[ARG_set_current_row_command].u_int, - args[ARG_write_black_ram_command].u_int, args[ARG_black_bits_inverted].u_bool, write_color_ram_command, - args[ARG_color_bits_inverted].u_bool, highlight_color, refresh_buf, refresh_buf_len, refresh_time, - busy_pin, args[ARG_busy_state].u_bool, seconds_per_frame, - args[ARG_always_toggle_chip_select].u_bool, args[ARG_grayscale].u_bool, args[ARG_advanced_color_epaper].u_bool, args[ARG_spectra6].u_bool, - two_byte_sequence_length, args[ARG_address_little_endian].u_bool - ); + epaperdisplay_construct_args_t construct_args = EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS; + construct_args.bus = display_bus; + construct_args.start_sequence = start_bufinfo.buf; + construct_args.start_sequence_len = start_bufinfo.len; + construct_args.start_up_time = start_up_time; + construct_args.stop_sequence = stop_bufinfo.buf; + construct_args.stop_sequence_len = stop_bufinfo.len; + construct_args.width = args[ARG_width].u_int; + construct_args.height = args[ARG_height].u_int; + construct_args.ram_width = args[ARG_ram_width].u_int; + construct_args.ram_height = args[ARG_ram_height].u_int; + construct_args.colstart = args[ARG_colstart].u_int; + construct_args.rowstart = args[ARG_rowstart].u_int; + construct_args.rotation = rotation; + construct_args.set_column_window_command = args[ARG_set_column_window_command].u_int; + construct_args.set_row_window_command = args[ARG_set_row_window_command].u_int; + construct_args.set_current_column_command = args[ARG_set_current_column_command].u_int; + construct_args.set_current_row_command = args[ARG_set_current_row_command].u_int; + construct_args.write_black_ram_command = args[ARG_write_black_ram_command].u_int; + construct_args.black_bits_inverted = args[ARG_black_bits_inverted].u_bool; + construct_args.write_color_ram_command = write_color_ram_command; + construct_args.color_bits_inverted = args[ARG_color_bits_inverted].u_bool; + construct_args.highlight_color = highlight_color; + construct_args.highlight_color2 = highlight_color2; + construct_args.refresh_sequence = refresh_buf; + construct_args.refresh_sequence_len = refresh_buf_len; + construct_args.refresh_time = refresh_time; + construct_args.busy_pin = busy_pin; + construct_args.busy_state = args[ARG_busy_state].u_bool; + construct_args.seconds_per_frame = seconds_per_frame; + construct_args.always_toggle_chip_select = args[ARG_always_toggle_chip_select].u_bool; + construct_args.grayscale = args[ARG_grayscale].u_bool; + construct_args.acep = args[ARG_advanced_color_epaper].u_bool; + construct_args.spectra6 = args[ARG_spectra6].u_bool; + construct_args.two_byte_sequence_length = two_byte_sequence_length; + construct_args.address_little_endian = args[ARG_address_little_endian].u_bool; + common_hal_epaperdisplay_epaperdisplay_construct(self, &construct_args); return self; } diff --git a/shared-bindings/epaperdisplay/EPaperDisplay.h b/shared-bindings/epaperdisplay/EPaperDisplay.h index 016a07f1490c5..83f0b0377fe6c 100644 --- a/shared-bindings/epaperdisplay/EPaperDisplay.h +++ b/shared-bindings/epaperdisplay/EPaperDisplay.h @@ -15,19 +15,84 @@ extern const mp_obj_type_t epaperdisplay_epaperdisplay_type; #define NO_COMMAND 0x100 +typedef struct { + mp_obj_t bus; + const uint8_t *start_sequence; + uint16_t start_sequence_len; + mp_float_t start_up_time; + const uint8_t *stop_sequence; + uint16_t stop_sequence_len; + uint16_t width; + uint16_t height; + uint16_t ram_width; + uint16_t ram_height; + int16_t colstart; + int16_t rowstart; + uint16_t rotation; + uint16_t set_column_window_command; + uint16_t set_row_window_command; + uint16_t set_current_column_command; + uint16_t set_current_row_command; + uint16_t write_black_ram_command; + bool black_bits_inverted; + uint16_t write_color_ram_command; + bool color_bits_inverted; + uint32_t highlight_color; + uint32_t highlight_color2; + const uint8_t *refresh_sequence; + uint16_t refresh_sequence_len; + mp_float_t refresh_time; + const mcu_pin_obj_t *busy_pin; + bool busy_state; + mp_float_t seconds_per_frame; + bool always_toggle_chip_select; + bool grayscale; + bool acep; + bool spectra6; + bool two_byte_sequence_length; + bool address_little_endian; +} epaperdisplay_construct_args_t; + +#define EPAPERDISPLAY_CONSTRUCT_ARGS_DEFAULTS { \ + .bus = mp_const_none, \ + .start_sequence = NULL, \ + .start_sequence_len = 0, \ + .start_up_time = 0.0, \ + .stop_sequence = NULL, \ + .stop_sequence_len = 0, \ + .width = 0, \ + .height = 0, \ + .ram_width = 0, \ + .ram_height = 0, \ + .colstart = 0, \ + .rowstart = 0, \ + .rotation = 0, \ + .set_column_window_command = NO_COMMAND, \ + .set_row_window_command = NO_COMMAND, \ + .set_current_column_command = NO_COMMAND, \ + .set_current_row_command = NO_COMMAND, \ + .write_black_ram_command = NO_COMMAND, \ + .black_bits_inverted = false, \ + .write_color_ram_command = NO_COMMAND, \ + .color_bits_inverted = false, \ + .highlight_color = 0x000000, \ + .highlight_color2 = 0x000000, \ + .refresh_sequence = NULL, \ + .refresh_sequence_len = 0, \ + .refresh_time = 0.0, \ + .busy_pin = NULL, \ + .busy_state = false, \ + .seconds_per_frame = 0.0, \ + .always_toggle_chip_select = false, \ + .grayscale = false, \ + .acep = false, \ + .spectra6 = false, \ + .two_byte_sequence_length = false, \ + .address_little_endian = false \ +} + void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdisplay_obj_t *self, - mp_obj_t bus, const uint8_t *start_sequence, uint16_t start_sequence_len, mp_float_t start_up_time, - const uint8_t *stop_sequence, uint16_t stop_sequence_len, - uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, - int16_t colstart, int16_t rowstart, uint16_t rotation, - uint16_t set_column_window_command, uint16_t set_row_window_command, - uint16_t set_current_column_command, uint16_t set_current_row_command, - uint16_t write_black_ram_command, bool black_bits_inverted, - uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, - const uint8_t *refresh_sequence, uint16_t refresh_sequence_len, mp_float_t refresh_time, - const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame, - bool always_toggle_chip_select, bool grayscale, bool acep, bool spectra6, bool two_byte_sequence_length, - bool address_little_endian); + const epaperdisplay_construct_args_t *args); bool common_hal_epaperdisplay_epaperdisplay_refresh(epaperdisplay_epaperdisplay_obj_t *self); diff --git a/shared-bindings/fourwire/FourWire.c b/shared-bindings/fourwire/FourWire.c index 083cf21a00367..608645aaebf0b 100644 --- a/shared-bindings/fourwire/FourWire.c +++ b/shared-bindings/fourwire/FourWire.c @@ -30,9 +30,9 @@ //| self, //| spi_bus: busio.SPI, //| *, -//| command: Optional[microcontroller.Pin], -//| chip_select: Optional[microcontroller.Pin], -//| reset: Optional[microcontroller.Pin] = None, +//| command: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, +//| chip_select: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, +//| reset: Optional[Union[microcontroller.Pin, digitalio.DigitalInOutProtocol]] = None, //| baudrate: int = 24000000, //| polarity: int = 0, //| phase: int = 0, @@ -73,10 +73,6 @@ static mp_obj_t fourwire_fourwire_make_new(const mp_obj_type_t *type, size_t n_a mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - const mcu_pin_obj_t *command = validate_obj_is_free_pin_or_none(args[ARG_command].u_obj, MP_QSTR_command); - const mcu_pin_obj_t *chip_select = validate_obj_is_free_pin_or_none(args[ARG_chip_select].u_obj, MP_QSTR_chip_select); - const mcu_pin_obj_t *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj, MP_QSTR_reset); - mp_obj_t spi = mp_arg_validate_type(args[ARG_spi_bus].u_obj, &busio_spi_type, MP_QSTR_spi_bus); fourwire_fourwire_obj_t *self = &allocate_display_bus_or_raise()->fourwire_bus; @@ -86,7 +82,7 @@ static mp_obj_t fourwire_fourwire_make_new(const mp_obj_type_t *type, size_t n_a uint8_t phase = (uint8_t)mp_arg_validate_int_range(args[ARG_phase].u_int, 0, 1, MP_QSTR_phase); common_hal_fourwire_fourwire_construct(self, - MP_OBJ_TO_PTR(spi), command, chip_select, reset, args[ARG_baudrate].u_int, polarity, phase); + MP_OBJ_TO_PTR(spi), args[ARG_command].u_obj, args[ARG_chip_select].u_obj, args[ARG_reset].u_obj, args[ARG_baudrate].u_int, polarity, phase); return self; } diff --git a/shared-bindings/fourwire/FourWire.h b/shared-bindings/fourwire/FourWire.h index 515a466b4b98c..3e77c25434d64 100644 --- a/shared-bindings/fourwire/FourWire.h +++ b/shared-bindings/fourwire/FourWire.h @@ -16,8 +16,8 @@ extern const mp_obj_type_t fourwire_fourwire_type; void common_hal_fourwire_fourwire_construct(fourwire_fourwire_obj_t *self, - busio_spi_obj_t *spi, const mcu_pin_obj_t *command, - const mcu_pin_obj_t *chip_select, const mcu_pin_obj_t *reset, uint32_t baudrate, + busio_spi_obj_t *spi, mp_obj_t command, + mp_obj_t chip_select, mp_obj_t reset, uint32_t baudrate, uint8_t polarity, uint8_t phase); void common_hal_fourwire_fourwire_deinit(fourwire_fourwire_obj_t *self); diff --git a/shared-bindings/gnss/GNSS.c b/shared-bindings/gnss/GNSS.c index 95c3ed975d501..3df23979c9ee1 100644 --- a/shared-bindings/gnss/GNSS.c +++ b/shared-bindings/gnss/GNSS.c @@ -38,7 +38,6 @@ //| ... //| static mp_obj_t gnss_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - gnss_obj_t *self = mp_obj_malloc(gnss_obj_t, &gnss_type); enum { ARG_system }; static const mp_arg_t allowed_args[] = { { MP_QSTR_system, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -63,7 +62,9 @@ static mp_obj_t gnss_make_new(const mp_obj_type_t *type, size_t n_args, size_t n mp_raise_TypeError(MP_ERROR_TEXT("System entry must be gnss.SatelliteSystem")); } + gnss_obj_t *self = mp_obj_malloc(gnss_obj_t, &gnss_type); common_hal_gnss_construct(self, selection); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/hashlib/__init__.c b/shared-bindings/hashlib/__init__.c index bfe19dee10794..354529cfa77cd 100644 --- a/shared-bindings/hashlib/__init__.c +++ b/shared-bindings/hashlib/__init__.c @@ -20,7 +20,7 @@ //| //| def new(name: str, data: bytes = b"") -> hashlib.Hash: //| """Returns a Hash object setup for the named algorithm. Raises ValueError when the named -//| algorithm is unsupported. +//| algorithm is unsupported. Supported algorithms for ``name`` are ``'sha1'`` and ``'sha256'``. //| //| :return: a hash object for the given algorithm //| :rtype: hashlib.Hash""" diff --git a/shared-bindings/i2cioexpander/IOExpander.c b/shared-bindings/i2cioexpander/IOExpander.c new file mode 100644 index 0000000000000..3efa0fd18685f --- /dev/null +++ b/shared-bindings/i2cioexpander/IOExpander.c @@ -0,0 +1,247 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/i2cioexpander/IOExpander.h" +#include "shared-bindings/busio/I2C.h" +#include "shared-bindings/util.h" +#include "shared/runtime/context_manager_helpers.h" + +//| class IOExpander: +//| """Control a generic I2C-based GPIO expander +//| +//| IOExpander provides a simple interface to I2C-based GPIO expanders that +//| use basic register reads and writes for control. The expander provides +//| individual pins through the `pins` attribute that implement the +//| DigitalInOutProtocol. +//| """ +//| +//| def __init__( +//| self, +//| i2c: busio.I2C, +//| address: int, +//| num_pins: int, +//| set_value_reg: Optional[int] = None, +//| get_value_reg: Optional[int] = None, +//| set_direction_reg: Optional[int] = None, +//| ) -> None: +//| """Initialize an I2C GPIO expander +//| +//| :param busio.I2C i2c: The I2C bus the expander is connected to +//| :param int address: The I2C device address +//| :param int num_pins: The number of GPIO pins (8 or 16) +//| :param int set_value_reg: Register address to write pin values (optional) +//| :param int get_value_reg: Register address to read pin values (optional) +//| :param int set_direction_reg: Register address to set pin directions (optional) +//| """ +//| ... + +static mp_obj_t i2cioexpander_ioexpander_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_i2c, ARG_address, ARG_num_pins, ARG_set_value_reg, ARG_get_value_reg, ARG_set_direction_reg }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_i2c, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_address, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_num_pins, MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_set_value_reg, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_get_value_reg, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_set_direction_reg, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + // Validate I2C object + mp_obj_t i2c = mp_arg_validate_type(args[ARG_i2c].u_obj, &busio_i2c_type, MP_QSTR_i2c); + + // Validate address + int address = args[ARG_address].u_int; + if (address < 0 || address > 0x7F) { + mp_raise_ValueError(MP_ERROR_TEXT("address out of range")); + } + + // Validate num_pins + int num_pins = args[ARG_num_pins].u_int; + if (num_pins != 8 && num_pins != 16) { + mp_raise_ValueError(MP_ERROR_TEXT("num_pins must be 8 or 16")); + } + + // Convert and validate register parameters + uint16_t set_value_reg = NO_REGISTER; + if (args[ARG_set_value_reg].u_obj != mp_const_none) { + mp_int_t reg = mp_obj_get_int(args[ARG_set_value_reg].u_obj); + mp_arg_validate_int_range(reg, 0, 255, MP_QSTR_set_value_reg); + set_value_reg = reg; + } + + uint16_t get_value_reg = NO_REGISTER; + if (args[ARG_get_value_reg].u_obj != mp_const_none) { + mp_int_t reg = mp_obj_get_int(args[ARG_get_value_reg].u_obj); + mp_arg_validate_int_range(reg, 0, 255, MP_QSTR_get_value_reg); + get_value_reg = reg; + } + + uint16_t set_direction_reg = NO_REGISTER; + if (args[ARG_set_direction_reg].u_obj != mp_const_none) { + mp_int_t reg = mp_obj_get_int(args[ARG_set_direction_reg].u_obj); + mp_arg_validate_int_range(reg, 0, 255, MP_QSTR_set_direction_reg); + set_direction_reg = reg; + } + + i2cioexpander_ioexpander_obj_t *self = + mp_obj_malloc(i2cioexpander_ioexpander_obj_t, &i2cioexpander_ioexpander_type); + common_hal_i2cioexpander_ioexpander_construct( + self, + i2c, + address, + num_pins, + set_value_reg, + get_value_reg, + set_direction_reg); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Deinitialize the expander. No further operations are possible.""" +//| ... +static mp_obj_t i2cioexpander_ioexpander_deinit(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_i2cioexpander_ioexpander_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_deinit_obj, i2cioexpander_ioexpander_deinit); + +//| def __enter__(self) -> IOExpander: +//| """No-op used by Context Managers.""" +//| ... +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +static mp_obj_t i2cioexpander_ioexpander___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + common_hal_i2cioexpander_ioexpander_deinit(MP_OBJ_TO_PTR(args[0])); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(i2cioexpander_ioexpander___exit___obj, 4, 4, i2cioexpander_ioexpander___exit__); + +//| @property +//| def input_value(self) -> int: +//| """Read the live value of all pins at once. Returns an integer where each +//| bit represents a pin's current state.""" +//| ... +static mp_obj_t i2cioexpander_ioexpander_obj_get_input_value(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t value; + if (!common_hal_i2cioexpander_ioexpander_get_input_value(self, &value)) { + mp_raise_OSError(MP_EIO); + } + return MP_OBJ_NEW_SMALL_INT(value); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_input_value_obj, i2cioexpander_ioexpander_obj_get_input_value); + +MP_PROPERTY_GETTER(i2cioexpander_ioexpander_input_value_obj, + (mp_obj_t)&i2cioexpander_ioexpander_get_input_value_obj); + +//| @property +//| def output_value(self) -> int: +//| """Get or set the cached output value. Reading returns the last value written, +//| not the live pin state. Writing updates the output pins.""" +//| ... +//| @output_value.setter +//| def output_value(self, val: int) -> None: ... +static mp_obj_t i2cioexpander_ioexpander_obj_get_output_value(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t value; + common_hal_i2cioexpander_ioexpander_get_output_value(self, &value); + return mp_obj_new_int(value); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_output_value_obj, i2cioexpander_ioexpander_obj_get_output_value); + +static mp_obj_t i2cioexpander_ioexpander_obj_set_output_value(mp_obj_t self_in, mp_obj_t value) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_negative_errno_t result = common_hal_i2cioexpander_ioexpander_set_output_value(self, mp_obj_get_int(value)); + if (result != 0) { + mp_raise_OSError(result); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_ioexpander_set_output_value_obj, i2cioexpander_ioexpander_obj_set_output_value); + +MP_PROPERTY_GETSET(i2cioexpander_ioexpander_output_value_obj, + (mp_obj_t)&i2cioexpander_ioexpander_get_output_value_obj, + (mp_obj_t)&i2cioexpander_ioexpander_set_output_value_obj); + +//| @property +//| def output_mask(self) -> int: +//| """Get or set which pins are configured as outputs. Each bit in the mask +//| represents a pin: 1 for output, 0 for input.""" +//| ... +//| @output_mask.setter +//| def output_mask(self, val: int) -> None: ... +static mp_obj_t i2cioexpander_ioexpander_obj_get_output_mask(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + size_t output_mask; + common_hal_i2cioexpander_ioexpander_get_output_mask(self, &output_mask); + return mp_obj_new_int(output_mask); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_output_mask_obj, i2cioexpander_ioexpander_obj_get_output_mask); + +static mp_obj_t i2cioexpander_ioexpander_obj_set_output_mask(mp_obj_t self_in, mp_obj_t value) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_negative_errno_t result = common_hal_i2cioexpander_ioexpander_set_output_mask(self, mp_obj_get_int(value)); + if (result != 0) { + mp_raise_OSError(result); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_ioexpander_set_output_mask_obj, i2cioexpander_ioexpander_obj_set_output_mask); + +MP_PROPERTY_GETSET(i2cioexpander_ioexpander_output_mask_obj, + (mp_obj_t)&i2cioexpander_ioexpander_get_output_mask_obj, + (mp_obj_t)&i2cioexpander_ioexpander_set_output_mask_obj); + +//| @property +//| def pins(self) -> Tuple[IOPin, ...]: +//| """A tuple of `IOPin` objects that implement the DigitalInOutProtocol. +//| Each pin can be used like a digitalio.DigitalInOut object.""" +//| ... +static mp_obj_t i2cioexpander_ioexpander_obj_get_pins(mp_obj_t self_in) { + i2cioexpander_ioexpander_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_ioexpander_get_pins(self); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_ioexpander_get_pins_obj, i2cioexpander_ioexpander_obj_get_pins); + +MP_PROPERTY_GETTER(i2cioexpander_ioexpander_pins_obj, + (mp_obj_t)&i2cioexpander_ioexpander_get_pins_obj); + +static const mp_rom_map_elem_t i2cioexpander_ioexpander_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&i2cioexpander_ioexpander_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&i2cioexpander_ioexpander___exit___obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_input_value), MP_ROM_PTR(&i2cioexpander_ioexpander_input_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_output_value), MP_ROM_PTR(&i2cioexpander_ioexpander_output_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_output_mask), MP_ROM_PTR(&i2cioexpander_ioexpander_output_mask_obj) }, + { MP_ROM_QSTR(MP_QSTR_pins), MP_ROM_PTR(&i2cioexpander_ioexpander_pins_obj) }, +}; +static MP_DEFINE_CONST_DICT(i2cioexpander_ioexpander_locals_dict, i2cioexpander_ioexpander_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + i2cioexpander_ioexpander_type, + MP_QSTR_IOExpander, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, i2cioexpander_ioexpander_make_new, + locals_dict, &i2cioexpander_ioexpander_locals_dict + ); diff --git a/shared-bindings/i2cioexpander/IOExpander.h b/shared-bindings/i2cioexpander/IOExpander.h new file mode 100644 index 0000000000000..5c93c6250cc7d --- /dev/null +++ b/shared-bindings/i2cioexpander/IOExpander.h @@ -0,0 +1,34 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-module/i2cioexpander/IOExpander.h" + +extern const mp_obj_type_t i2cioexpander_ioexpander_type; + +void common_hal_i2cioexpander_ioexpander_construct( + i2cioexpander_ioexpander_obj_t *self, + mp_obj_t i2c, + uint8_t address, + uint8_t num_pins, + uint16_t set_value_reg, + uint16_t get_value_reg, + uint16_t set_direction_reg); + +void common_hal_i2cioexpander_ioexpander_deinit(i2cioexpander_ioexpander_obj_t *self); +bool common_hal_i2cioexpander_ioexpander_deinited(i2cioexpander_ioexpander_obj_t *self); + +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_get_input_value(i2cioexpander_ioexpander_obj_t *self, size_t *value); +// No error return because this returns a cached value. +void common_hal_i2cioexpander_ioexpander_get_output_value(i2cioexpander_ioexpander_obj_t *self, size_t *value); +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_set_output_value(i2cioexpander_ioexpander_obj_t *self, size_t value); + +void common_hal_i2cioexpander_ioexpander_get_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t *mask); +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_set_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t mask); + +mp_obj_t common_hal_i2cioexpander_ioexpander_get_pins(i2cioexpander_ioexpander_obj_t *self); diff --git a/shared-bindings/i2cioexpander/IOPin.c b/shared-bindings/i2cioexpander/IOPin.c new file mode 100644 index 0000000000000..bba229bd31e22 --- /dev/null +++ b/shared-bindings/i2cioexpander/IOPin.c @@ -0,0 +1,326 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/i2cioexpander/IOPin.h" +#include "shared-module/i2cioexpander/IOPin.h" + +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" +#include "shared-bindings/util.h" + +static void check_result(digitalinout_result_t result) { + switch (result) { + case DIGITALINOUT_OK: + return; + case DIGITALINOUT_PIN_BUSY: + mp_raise_ValueError_varg(MP_ERROR_TEXT("%q in use"), MP_QSTR_Pin); + #if CIRCUITPY_DIGITALIO_HAVE_INPUT_ONLY + case DIGITALINOUT_INPUT_ONLY: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_direction); + #endif + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL + case DIGITALINOUT_INVALID_PULL: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_pull); + #endif + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE + case DIGITALINOUT_INVALID_DRIVE_MODE: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_drive_mode); + #endif + } +} + +static inline void check_for_deinit(i2cioexpander_iopin_obj_t *self) { + if (common_hal_i2cioexpander_iopin_deinited(self)) { + raise_deinited_error(); + } +} +//| class IOPin: +//| """Control a single pin on an `IOExpander` in the same way as `DigitalInOut`. +//| +//| Not constructed directly. Get from `IOExpander.pins` instead. +//| """ +//| + +//| def switch_to_output( +//| self, value: bool = False, drive_mode: digitalio.DriveMode = digitalio.DriveMode.PUSH_PULL +//| ) -> None: +//| """Set the drive mode and value and then switch to writing out digital values. +//| +//| :param bool value: default value to set upon switching +//| :param digitalio.DriveMode drive_mode: drive mode for the output""" +//| ... +static mp_obj_t i2cioexpander_iopin_switch_to_output(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_value, ARG_drive_mode }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_value, MP_ARG_BOOL, {.u_bool = false} }, + { MP_QSTR_drive_mode, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_PTR(&digitalio_drive_mode_push_pull_obj)} }, + }; + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + digitalio_drive_mode_t drive_mode = DRIVE_MODE_PUSH_PULL; + if (args[ARG_drive_mode].u_rom_obj == MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj)) { + drive_mode = DRIVE_MODE_OPEN_DRAIN; + } + check_result(common_hal_i2cioexpander_iopin_switch_to_output(self, args[ARG_value].u_bool, drive_mode)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(i2cioexpander_iopin_switch_to_output_obj, 1, i2cioexpander_iopin_switch_to_output); + +//| def switch_to_input(self, pull: Optional[digitalio.Pull] = None) -> None: +//| """Set the pull and then switch to read in digital values. +//| +//| :param digitalio.Pull pull: pull configuration for the input""" +//| ... +static mp_obj_t i2cioexpander_iopin_switch_to_input(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_pull }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_pull, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + }; + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + check_result(common_hal_i2cioexpander_iopin_switch_to_input(self, validate_pull(args[ARG_pull].u_rom_obj, MP_QSTR_pull))); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(i2cioexpander_iopin_switch_to_input_obj, 1, i2cioexpander_iopin_switch_to_input); + +//| direction: digitalio.Direction +//| """The direction of the pin.""" +static mp_obj_t i2cioexpander_iopin_obj_get_direction(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + digitalio_direction_t direction = common_hal_i2cioexpander_iopin_get_direction(self); + if (direction == DIRECTION_INPUT) { + return MP_OBJ_FROM_PTR(&digitalio_direction_input_obj); + } + return MP_OBJ_FROM_PTR(&digitalio_direction_output_obj); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_get_direction_obj, i2cioexpander_iopin_obj_get_direction); + +static mp_obj_t i2cioexpander_iopin_obj_set_direction(mp_obj_t self_in, mp_obj_t value) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (value == MP_ROM_PTR(&digitalio_direction_input_obj)) { + check_result(common_hal_i2cioexpander_iopin_switch_to_input(self, PULL_NONE)); + } else if (value == MP_ROM_PTR(&digitalio_direction_output_obj)) { + check_result(common_hal_i2cioexpander_iopin_switch_to_output(self, false, DRIVE_MODE_PUSH_PULL)); + } else { + mp_arg_error_invalid(MP_QSTR_direction); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_iopin_set_direction_obj, i2cioexpander_iopin_obj_set_direction); + +MP_PROPERTY_GETSET(i2cioexpander_iopin_direction_obj, + (mp_obj_t)&i2cioexpander_iopin_get_direction_obj, + (mp_obj_t)&i2cioexpander_iopin_set_direction_obj); + +//| value: bool +//| """The digital logic level of the pin.""" +static mp_obj_t i2cioexpander_iopin_obj_get_value(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + bool value; + mp_negative_errno_t res = common_hal_i2cioexpander_iopin_get_value(self, &value); + if (res != 0) { + mp_raise_OSError(-res); + } + return mp_obj_new_bool(value); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_get_value_obj, i2cioexpander_iopin_obj_get_value); + +static mp_obj_t i2cioexpander_iopin_obj_set_value(mp_obj_t self_in, mp_obj_t value) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_INPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Cannot set value when direction is input.")); + return mp_const_none; + } + mp_negative_errno_t res = common_hal_i2cioexpander_iopin_set_value(self, mp_obj_is_true(value)); + if (res != 0) { + mp_raise_OSError(-res); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_iopin_set_value_obj, i2cioexpander_iopin_obj_set_value); + +MP_PROPERTY_GETSET(i2cioexpander_iopin_value_obj, + (mp_obj_t)&i2cioexpander_iopin_get_value_obj, + (mp_obj_t)&i2cioexpander_iopin_set_value_obj); + +//| drive_mode: digitalio.DriveMode +//| """The pin drive mode.""" +static mp_obj_t i2cioexpander_iopin_obj_get_drive_mode(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_INPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Drive mode not used when direction is input.")); + return mp_const_none; + } + digitalio_drive_mode_t drive_mode = common_hal_i2cioexpander_iopin_get_drive_mode(self); + if (drive_mode == DRIVE_MODE_PUSH_PULL) { + return MP_OBJ_FROM_PTR(&digitalio_drive_mode_push_pull_obj); + } + return MP_OBJ_FROM_PTR(&digitalio_drive_mode_open_drain_obj); +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_get_drive_mode_obj, i2cioexpander_iopin_obj_get_drive_mode); + +static mp_obj_t i2cioexpander_iopin_obj_set_drive_mode(mp_obj_t self_in, mp_obj_t drive_mode) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_INPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Drive mode not used when direction is input.")); + return mp_const_none; + } + digitalio_drive_mode_t c_drive_mode = DRIVE_MODE_PUSH_PULL; + if (drive_mode == MP_ROM_PTR(&digitalio_drive_mode_open_drain_obj)) { + c_drive_mode = DRIVE_MODE_OPEN_DRAIN; + } + check_result(common_hal_i2cioexpander_iopin_set_drive_mode(self, c_drive_mode)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_iopin_set_drive_mode_obj, i2cioexpander_iopin_obj_set_drive_mode); + +MP_PROPERTY_GETSET(i2cioexpander_iopin_drive_mode_obj, + (mp_obj_t)&i2cioexpander_iopin_get_drive_mode_obj, + (mp_obj_t)&i2cioexpander_iopin_set_drive_mode_obj); + +//| pull: Optional[digitalio.Pull] +//| """The pin pull direction.""" +static mp_obj_t i2cioexpander_iopin_obj_get_pull(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_OUTPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Pull not used when direction is output.")); + return mp_const_none; + } + digitalio_pull_t pull = common_hal_i2cioexpander_iopin_get_pull(self); + if (pull == PULL_UP) { + return MP_OBJ_FROM_PTR(&digitalio_pull_up_obj); + } else if (pull == PULL_DOWN) { + return MP_OBJ_FROM_PTR(&digitalio_pull_down_obj); + } + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(i2cioexpander_iopin_get_pull_obj, i2cioexpander_iopin_obj_get_pull); + +static mp_obj_t i2cioexpander_iopin_obj_set_pull(mp_obj_t self_in, mp_obj_t pull_obj) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (common_hal_i2cioexpander_iopin_get_direction(self) == DIRECTION_OUTPUT) { + mp_raise_AttributeError(MP_ERROR_TEXT("Pull not used when direction is output.")); + return mp_const_none; + } + check_result(common_hal_i2cioexpander_iopin_set_pull(self, validate_pull(pull_obj, MP_QSTR_pull))); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(i2cioexpander_iopin_set_pull_obj, i2cioexpander_iopin_obj_set_pull); + +MP_PROPERTY_GETSET(i2cioexpander_iopin_pull_obj, + (mp_obj_t)&i2cioexpander_iopin_get_pull_obj, + (mp_obj_t)&i2cioexpander_iopin_set_pull_obj); + +// Protocol implementation for DigitalInOutProtocol +static void iopin_protocol_deinit(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_i2cioexpander_iopin_deinit(self); +} + +static bool iopin_protocol_deinited(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_deinited(self); +} + +static digitalinout_result_t iopin_protocol_switch_to_input(mp_obj_t self_in, digitalio_pull_t pull) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_switch_to_input(self, pull); +} + +static digitalinout_result_t iopin_protocol_switch_to_output(mp_obj_t self_in, bool value, digitalio_drive_mode_t drive_mode) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_switch_to_output(self, value, drive_mode); +} + +static digitalio_direction_t iopin_protocol_get_direction(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_get_direction(self); +} + +static mp_negative_errno_t iopin_protocol_get_value(mp_obj_t self_in, bool *value) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_get_value(self, value); +} + +static mp_negative_errno_t iopin_protocol_set_value(mp_obj_t self_in, bool value) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_set_value(self, value); +} + +static digitalio_drive_mode_t iopin_protocol_get_drive_mode(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_get_drive_mode(self); +} + +static digitalinout_result_t iopin_protocol_set_drive_mode(mp_obj_t self_in, digitalio_drive_mode_t drive_mode) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_set_drive_mode(self, drive_mode); +} + +static digitalio_pull_t iopin_protocol_get_pull(mp_obj_t self_in) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_get_pull(self); +} + +static digitalinout_result_t iopin_protocol_set_pull(mp_obj_t self_in, digitalio_pull_t pull) { + i2cioexpander_iopin_obj_t *self = MP_OBJ_TO_PTR(self_in); + return common_hal_i2cioexpander_iopin_set_pull(self, pull); +} + +static const digitalinout_p_t iopin_digitalinout_p = { + MP_PROTO_IMPLEMENT(MP_QSTR_DigitalInOut) + .deinit = iopin_protocol_deinit, + .deinited = iopin_protocol_deinited, + .switch_to_input = iopin_protocol_switch_to_input, + .switch_to_output = iopin_protocol_switch_to_output, + .get_direction = iopin_protocol_get_direction, + .get_value = iopin_protocol_get_value, + .set_value = iopin_protocol_set_value, + .get_drive_mode = iopin_protocol_get_drive_mode, + .set_drive_mode = iopin_protocol_set_drive_mode, + .get_pull = iopin_protocol_get_pull, + .set_pull = iopin_protocol_set_pull, +}; + +static const mp_rom_map_elem_t i2cioexpander_iopin_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR_switch_to_input), MP_ROM_PTR(&i2cioexpander_iopin_switch_to_input_obj) }, + { MP_ROM_QSTR(MP_QSTR_switch_to_output), MP_ROM_PTR(&i2cioexpander_iopin_switch_to_output_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_direction), MP_ROM_PTR(&i2cioexpander_iopin_direction_obj) }, + { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&i2cioexpander_iopin_value_obj) }, + { MP_ROM_QSTR(MP_QSTR_drive_mode), MP_ROM_PTR(&i2cioexpander_iopin_drive_mode_obj) }, + { MP_ROM_QSTR(MP_QSTR_pull), MP_ROM_PTR(&i2cioexpander_iopin_pull_obj) }, +}; +static MP_DEFINE_CONST_DICT(i2cioexpander_iopin_locals_dict, i2cioexpander_iopin_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + i2cioexpander_iopin_type, + MP_QSTR_IOPin, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + protocol, &iopin_digitalinout_p, + locals_dict, &i2cioexpander_iopin_locals_dict + ); diff --git a/shared-bindings/i2cioexpander/IOPin.h b/shared-bindings/i2cioexpander/IOPin.h new file mode 100644 index 0000000000000..c8155c71aa1f3 --- /dev/null +++ b/shared-bindings/i2cioexpander/IOPin.h @@ -0,0 +1,46 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-module/i2cioexpander/IOPin.h" + +extern const mp_obj_type_t i2cioexpander_iopin_type; + +mp_negative_errno_t i2cioexpander_iopin_construct( + i2cioexpander_iopin_obj_t *self, + i2cioexpander_ioexpander_obj_t *expander, + uint8_t pin_number); + +void common_hal_i2cioexpander_iopin_deinit(i2cioexpander_iopin_obj_t *self); +bool common_hal_i2cioexpander_iopin_deinited(i2cioexpander_iopin_obj_t *self); + +digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_input( + i2cioexpander_iopin_obj_t *self, + digitalio_pull_t pull); + +digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_output( + i2cioexpander_iopin_obj_t *self, + bool value, + digitalio_drive_mode_t drive_mode); + +digitalio_direction_t common_hal_i2cioexpander_iopin_get_direction(i2cioexpander_iopin_obj_t *self); + +mp_negative_errno_t common_hal_i2cioexpander_iopin_set_value(i2cioexpander_iopin_obj_t *self, bool value); +mp_negative_errno_t common_hal_i2cioexpander_iopin_get_value(i2cioexpander_iopin_obj_t *self, bool *value); + +digitalinout_result_t common_hal_i2cioexpander_iopin_set_drive_mode( + i2cioexpander_iopin_obj_t *self, + digitalio_drive_mode_t drive_mode); + +digitalio_drive_mode_t common_hal_i2cioexpander_iopin_get_drive_mode(i2cioexpander_iopin_obj_t *self); + +digitalinout_result_t common_hal_i2cioexpander_iopin_set_pull( + i2cioexpander_iopin_obj_t *self, + digitalio_pull_t pull); + +digitalio_pull_t common_hal_i2cioexpander_iopin_get_pull(i2cioexpander_iopin_obj_t *self); diff --git a/shared-bindings/i2cioexpander/__init__.c b/shared-bindings/i2cioexpander/__init__.c new file mode 100644 index 0000000000000..106c45c3768bf --- /dev/null +++ b/shared-bindings/i2cioexpander/__init__.c @@ -0,0 +1,49 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/i2cioexpander/__init__.h" +#include "shared-bindings/i2cioexpander/IOExpander.h" + +//| """Support for I2C-based GPIO expanders +//| +//| The `i2cioexpander` module contains classes to support I2C-based GPIO expanders +//| that can be controlled via simple register reads and writes. +//| +//| All classes change hardware state and should be deinitialized when they +//| are no longer needed if the program continues after use. To do so, either +//| call :py:meth:`!deinit` or use a context manager. See +//| :ref:`lifetime-and-contextmanagers` for more info. +//| +//| Example:: +//| +//| import board +//| import busio +//| import i2cioexpander +//| +//| i2c = busio.I2C(board.SCL, board.SDA) +//| expander = i2cioexpander.IOExpander(i2c, 0x20, 8, 0x01, 0x00, 0x03) +//| pin0 = expander.pins[0] +//| pin0.switch_to_output(value=True) +//| """ + +static const mp_rom_map_elem_t i2cioexpander_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_i2cioexpander) }, + { MP_ROM_QSTR(MP_QSTR_IOExpander), MP_ROM_PTR(&i2cioexpander_ioexpander_type) }, +}; + +static MP_DEFINE_CONST_DICT(i2cioexpander_module_globals, i2cioexpander_module_globals_table); + +const mp_obj_module_t i2cioexpander_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&i2cioexpander_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_i2cioexpander, i2cioexpander_module); diff --git a/shared-bindings/i2cioexpander/__init__.h b/shared-bindings/i2cioexpander/__init__.h new file mode 100644 index 0000000000000..e0d0668686859 --- /dev/null +++ b/shared-bindings/i2cioexpander/__init__.h @@ -0,0 +1,9 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Nothing now. diff --git a/shared-bindings/i2ctarget/I2CTarget.c b/shared-bindings/i2ctarget/I2CTarget.c index 8586d1843cc77..94dbf4e8be4f6 100644 --- a/shared-bindings/i2ctarget/I2CTarget.c +++ b/shared-bindings/i2ctarget/I2CTarget.c @@ -26,7 +26,8 @@ static mp_obj_t mp_obj_new_i2ctarget_i2c_target_request(i2ctarget_i2c_target_obj self->address = address; self->is_read = is_read; self->is_restart = is_restart; - return (mp_obj_t)self; + + return MP_OBJ_FROM_PTR(self); } //| class I2CTarget: @@ -50,7 +51,6 @@ static mp_obj_t mp_obj_new_i2ctarget_i2c_target_request(i2ctarget_i2c_target_obj //| ... //| static mp_obj_t i2ctarget_i2c_target_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - i2ctarget_i2c_target_obj_t *self = mp_obj_malloc_with_finaliser(i2ctarget_i2c_target_obj_t, &i2ctarget_i2c_target_type); enum { ARG_scl, ARG_sda, ARG_addresses, ARG_smbus }; static const mp_arg_t allowed_args[] = { { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -78,8 +78,10 @@ static mp_obj_t i2ctarget_i2c_target_make_new(const mp_obj_type_t *type, size_t mp_raise_ValueError(MP_ERROR_TEXT("addresses is empty")); } + i2ctarget_i2c_target_obj_t *self = mp_obj_malloc_with_finaliser(i2ctarget_i2c_target_obj_t, &i2ctarget_i2c_target_type); common_hal_i2ctarget_i2c_target_construct(self, scl, sda, addresses, i, args[ARG_smbus].u_bool); - return (mp_obj_t)self; + + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: diff --git a/shared-bindings/imagecapture/ParallelImageCapture.c b/shared-bindings/imagecapture/ParallelImageCapture.c index 866edb489ce3e..427eaeb379373 100644 --- a/shared-bindings/imagecapture/ParallelImageCapture.c +++ b/shared-bindings/imagecapture/ParallelImageCapture.c @@ -58,7 +58,6 @@ static mp_obj_t imagecapture_parallelimagecapture_make_new(const mp_obj_type_t * imagecapture_parallelimagecapture_obj_t *self = mp_obj_malloc(imagecapture_parallelimagecapture_obj_t, &imagecapture_parallelimagecapture_type); - common_hal_imagecapture_parallelimagecapture_construct(self, pins, pin_count, clock, vsync, href); return self; diff --git a/shared-bindings/ipaddress/IPv4Address.c b/shared-bindings/ipaddress/IPv4Address.c index 5aab02e9f5cce..7705e500e7e6a 100644 --- a/shared-bindings/ipaddress/IPv4Address.c +++ b/shared-bindings/ipaddress/IPv4Address.c @@ -59,7 +59,6 @@ static mp_obj_t ipaddress_ipv4address_make_new(const mp_obj_type_t *type, size_t ipaddress_ipv4address_obj_t *self = mp_obj_malloc(ipaddress_ipv4address_obj_t, &ipaddress_ipv4address_type); - common_hal_ipaddress_ipv4address_construct(self, buf, 4); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/is31fl3741/IS31FL3741.c b/shared-bindings/is31fl3741/IS31FL3741.c index 0c6b3479b047c..71c4437a1d23f 100644 --- a/shared-bindings/is31fl3741/IS31FL3741.c +++ b/shared-bindings/is31fl3741/IS31FL3741.c @@ -38,7 +38,6 @@ static mp_obj_t is31fl3741_IS31FL3741_make_new(const mp_obj_type_t *type, size_t mp_obj_t i2c = mp_arg_validate_type(args[ARG_i2c].u_obj, &busio_i2c_type, MP_QSTR_i2c_bus); is31fl3741_IS31FL3741_obj_t *self = mp_obj_malloc(is31fl3741_IS31FL3741_obj_t, &is31fl3741_IS31FL3741_type); - common_hal_is31fl3741_IS31FL3741_construct(self, MP_OBJ_TO_PTR(i2c), args[ARG_addr].u_int diff --git a/shared-bindings/jpegio/JpegDecoder.c b/shared-bindings/jpegio/JpegDecoder.c index 13287a23650cc..ad2a0622e7a61 100644 --- a/shared-bindings/jpegio/JpegDecoder.c +++ b/shared-bindings/jpegio/JpegDecoder.c @@ -43,7 +43,6 @@ static mp_obj_t jpegio_jpegdecoder_make_new(const mp_obj_type_t *type, size_t n_ mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); jpegio_jpegdecoder_obj_t *self = mp_obj_malloc(jpegio_jpegdecoder_obj_t, &jpegio_jpegdecoder_type); - self->base.type = &jpegio_jpegdecoder_type; common_hal_jpegio_jpegdecoder_construct(self); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/keypad/Event.c b/shared-bindings/keypad/Event.c index 51e446e8362d7..f83e01e785e6a 100644 --- a/shared-bindings/keypad/Event.c +++ b/shared-bindings/keypad/Event.c @@ -26,7 +26,6 @@ //| ... //| static mp_obj_t keypad_event_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - keypad_event_obj_t *self = mp_obj_malloc(keypad_event_obj_t, &keypad_event_type); enum { ARG_key_number, ARG_pressed, ARG_timestamp }; static const mp_arg_t allowed_args[] = { { MP_QSTR_key_number, MP_ARG_INT, {.u_int = 0} }, @@ -45,7 +44,9 @@ static mp_obj_t keypad_event_make_new(const mp_obj_type_t *type, size_t n_args, } (void)mp_obj_get_int_truncated(timestamp); // ensure that timestamp is an integer + keypad_event_obj_t *self = mp_obj_malloc(keypad_event_obj_t, &keypad_event_type); common_hal_keypad_event_construct(self, key_number, args[ARG_pressed].u_bool, timestamp); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/keypad/EventQueue.h b/shared-bindings/keypad/EventQueue.h index 893165e3220f1..02c3414b0764c 100644 --- a/shared-bindings/keypad/EventQueue.h +++ b/shared-bindings/keypad/EventQueue.h @@ -11,7 +11,7 @@ extern const mp_obj_type_t keypad_eventqueue_type; -void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events); +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events, bool use_gc_allocator); void common_hal_keypad_eventqueue_clear(keypad_eventqueue_obj_t *self); size_t common_hal_keypad_eventqueue_get_length(keypad_eventqueue_obj_t *self); diff --git a/shared-bindings/keypad/KeyMatrix.c b/shared-bindings/keypad/KeyMatrix.c index edb32c2c5c700..01416a2f8a190 100644 --- a/shared-bindings/keypad/KeyMatrix.c +++ b/shared-bindings/keypad/KeyMatrix.c @@ -81,7 +81,6 @@ static mp_obj_t keypad_keymatrix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { #if CIRCUITPY_KEYPAD_KEYMATRIX - keypad_keymatrix_obj_t *self = mp_obj_malloc(keypad_keymatrix_obj_t, &keypad_keymatrix_type); enum { ARG_row_pins, ARG_column_pins, ARG_columns_to_anodes, ARG_interval, ARG_max_events, ARG_debounce_threshold }; static const mp_arg_t allowed_args[] = { { MP_QSTR_row_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -123,7 +122,9 @@ static mp_obj_t keypad_keymatrix_make_new(const mp_obj_type_t *type, size_t n_ar column_pins_array[column] = pin; } + keypad_keymatrix_obj_t *self = mp_obj_malloc(keypad_keymatrix_obj_t, &keypad_keymatrix_type); common_hal_keypad_keymatrix_construct(self, num_row_pins, row_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, interval, max_events, debounce_threshold); + return MP_OBJ_FROM_PTR(self); #else mp_raise_NotImplementedError_varg(MP_ERROR_TEXT("%q"), MP_QSTR_KeyMatrix); diff --git a/shared-bindings/keypad/Keys.c b/shared-bindings/keypad/Keys.c index 64e8e51a67ded..5fd065fc6271c 100644 --- a/shared-bindings/keypad/Keys.c +++ b/shared-bindings/keypad/Keys.c @@ -81,7 +81,6 @@ static mp_obj_t keypad_keys_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { #if CIRCUITPY_KEYPAD_KEYS - keypad_keys_obj_t *self = mp_obj_malloc(keypad_keys_obj_t, &keypad_keys_type); enum { ARG_pins, ARG_value_when_pressed, ARG_pull, ARG_interval, ARG_max_events, ARG_debounce_threshold }; static const mp_arg_t allowed_args[] = { { MP_QSTR_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -112,6 +111,7 @@ static mp_obj_t keypad_keys_make_new(const mp_obj_type_t *type, size_t n_args, s validate_obj_is_free_pin(mp_obj_subscr(pins, MP_OBJ_NEW_SMALL_INT(i), MP_OBJ_SENTINEL), MP_QSTR_pin); } + keypad_keys_obj_t *self = mp_obj_malloc(keypad_keys_obj_t, &keypad_keys_type); common_hal_keypad_keys_construct(self, num_pins, pins_array, value_when_pressed, args[ARG_pull].u_bool, interval, max_events, debounce_threshold); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/keypad/ShiftRegisterKeys.c b/shared-bindings/keypad/ShiftRegisterKeys.c index 1347735b5d2f8..7f89a11ecf169 100644 --- a/shared-bindings/keypad/ShiftRegisterKeys.c +++ b/shared-bindings/keypad/ShiftRegisterKeys.c @@ -88,8 +88,6 @@ static mp_obj_t keypad_shiftregisterkeys_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { #if CIRCUITPY_KEYPAD_SHIFTREGISTERKEYS - keypad_shiftregisterkeys_obj_t *self = - mp_obj_malloc(keypad_shiftregisterkeys_obj_t, &keypad_shiftregisterkeys_type); enum { ARG_clock, ARG_data, ARG_latch, ARG_value_to_latch, ARG_key_count, ARG_value_when_pressed, ARG_interval, ARG_max_events, ARG_debounce_threshold }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -160,6 +158,8 @@ static mp_obj_t keypad_shiftregisterkeys_make_new(const mp_obj_type_t *type, siz const size_t max_events = (size_t)mp_arg_validate_int_min(args[ARG_max_events].u_int, 1, MP_QSTR_max_events); const uint8_t debounce_threshold = (uint8_t)mp_arg_validate_int_range(args[ARG_debounce_threshold].u_int, 1, 127, MP_QSTR_debounce_threshold); + keypad_shiftregisterkeys_obj_t *self = + mp_obj_malloc(keypad_shiftregisterkeys_obj_t, &keypad_shiftregisterkeys_type); common_hal_keypad_shiftregisterkeys_construct( self, clock, num_data_pins, data_pins_array, latch, value_to_latch, num_key_counts, key_count_array, value_when_pressed, interval, max_events, debounce_threshold); diff --git a/shared-bindings/keypad_demux/DemuxKeyMatrix.c b/shared-bindings/keypad_demux/DemuxKeyMatrix.c index d76a20aec9d01..554e461c2289b 100644 --- a/shared-bindings/keypad_demux/DemuxKeyMatrix.c +++ b/shared-bindings/keypad_demux/DemuxKeyMatrix.c @@ -80,7 +80,6 @@ //| static mp_obj_t keypad_demux_demuxkeymatrix_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - keypad_demux_demuxkeymatrix_obj_t *self = mp_obj_malloc(keypad_demux_demuxkeymatrix_obj_t, &keypad_demux_demuxkeymatrix_type); enum { ARG_row_addr_pins, ARG_column_pins, ARG_columns_to_anodes, ARG_transpose, ARG_interval, ARG_max_events, ARG_debounce_threshold }; static const mp_arg_t allowed_args[] = { { MP_QSTR_row_addr_pins, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -123,7 +122,10 @@ static mp_obj_t keypad_demux_demuxkeymatrix_make_new(const mp_obj_type_t *type, column_pins_array[column] = pin; } - common_hal_keypad_demux_demuxkeymatrix_construct(self, num_row_addr_pins, row_addr_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, args[ARG_transpose].u_bool, interval, max_events, debounce_threshold); + keypad_demux_demuxkeymatrix_obj_t *self = mp_obj_malloc(keypad_demux_demuxkeymatrix_obj_t, &keypad_demux_demuxkeymatrix_type); + // Last arg is use_gc_allocator, true during VM use. + common_hal_keypad_demux_demuxkeymatrix_construct(self, num_row_addr_pins, row_addr_pins_array, num_column_pins, column_pins_array, args[ARG_columns_to_anodes].u_bool, args[ARG_transpose].u_bool, interval, max_events, debounce_threshold, true); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/keypad_demux/DemuxKeyMatrix.h b/shared-bindings/keypad_demux/DemuxKeyMatrix.h index 8bdaa597dc035..25d36283e90fc 100644 --- a/shared-bindings/keypad_demux/DemuxKeyMatrix.h +++ b/shared-bindings/keypad_demux/DemuxKeyMatrix.h @@ -11,7 +11,7 @@ extern const mp_obj_type_t keypad_demux_demuxkeymatrix_type; -void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold); +void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold, bool use_gc_allocator); void common_hal_keypad_demux_demuxkeymatrix_deinit(keypad_demux_demuxkeymatrix_obj_t *self); diff --git a/shared-bindings/math/__init__.c b/shared-bindings/math/__init__.c index 54fe53280ca8e..49ca2958d8c96 100644 --- a/shared-bindings/math/__init__.c +++ b/shared-bindings/math/__init__.c @@ -26,7 +26,7 @@ //| """ //| -static NORETURN void math_error(void) { +static MP_NORETURN void math_error(void) { mp_raise_ValueError(MP_ERROR_TEXT("math domain error")); } diff --git a/shared-bindings/mcp4822/MCP4822.c b/shared-bindings/mcp4822/MCP4822.c new file mode 100644 index 0000000000000..f192caf4052a6 --- /dev/null +++ b/shared-bindings/mcp4822/MCP4822.c @@ -0,0 +1,243 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "shared/runtime/context_manager_helpers.h" +#include "py/binary.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/mcp4822/MCP4822.h" +#include "shared-bindings/util.h" + +//| class MCP4822: +//| """Output audio to an MCP4822 dual-channel 12-bit SPI DAC.""" +//| +//| def __init__( +//| self, +//| clock: microcontroller.Pin, +//| mosi: microcontroller.Pin, +//| cs: microcontroller.Pin, +//| *, +//| gain: int = 1, +//| ) -> None: +//| """Create an MCP4822 object associated with the given SPI pins. +//| +//| :param ~microcontroller.Pin clock: The SPI clock (SCK) pin +//| :param ~microcontroller.Pin mosi: The SPI data (SDI/MOSI) pin +//| :param ~microcontroller.Pin cs: The chip select (CS) pin +//| :param int gain: DAC output gain, 1 for 1x (0-2.048V) or 2 for 2x (0-4.096V). Default 1. +//| +//| Simple 8ksps 440 Hz sine wave:: +//| +//| import mcp4822 +//| import audiocore +//| import board +//| import array +//| import time +//| import math +//| +//| length = 8000 // 440 +//| sine_wave = array.array("H", [0] * length) +//| for i in range(length): +//| sine_wave[i] = int(math.sin(math.pi * 2 * i / length) * (2 ** 15) + 2 ** 15) +//| +//| sine_wave = audiocore.RawSample(sine_wave, sample_rate=8000) +//| dac = mcp4822.MCP4822(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(sine_wave, loop=True) +//| time.sleep(1) +//| dac.stop() +//| +//| Playing a wave file from flash:: +//| +//| import board +//| import audiocore +//| import mcp4822 +//| +//| f = open("sound.wav", "rb") +//| wav = audiocore.WaveFile(f) +//| +//| dac = mcp4822.MCP4822(clock=board.GP18, mosi=board.GP19, cs=board.GP21) +//| dac.play(wav) +//| while dac.playing: +//| pass""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_clock, ARG_mosi, ARG_cs, ARG_gain }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_mosi, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_cs, MP_ARG_OBJ | MP_ARG_KW_ONLY | MP_ARG_REQUIRED }, + { MP_QSTR_gain, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 1} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); + const mcu_pin_obj_t *mosi = validate_obj_is_free_pin(args[ARG_mosi].u_obj, MP_QSTR_mosi); + const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + const mp_int_t gain = mp_arg_validate_int_range(args[ARG_gain].u_int, 1, 2, MP_QSTR_gain); + + mcp4822_mcp4822_obj_t *self = mp_obj_malloc_with_finaliser(mcp4822_mcp4822_obj_t, &mcp4822_mcp4822_type); + common_hal_mcp4822_mcp4822_construct(self, clock, mosi, cs, (uint8_t)gain); + + return MP_OBJ_FROM_PTR(self); +} + +static void check_for_deinit(mcp4822_mcp4822_obj_t *self) { + if (common_hal_mcp4822_mcp4822_deinited(self)) { + raise_deinited_error(); + } +} + +//| def deinit(self) -> None: +//| """Deinitialises the MCP4822 and releases any hardware resources for reuse.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_deinit(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_mcp4822_mcp4822_deinit(self); + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_deinit_obj, mcp4822_mcp4822_deinit); + +//| def __enter__(self) -> MCP4822: +//| """No-op used by Context Managers.""" +//| ... +//| +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +//| +// Provided by context manager helper. + +//| def play(self, sample: circuitpython_typing.AudioSample, *, loop: bool = False) -> None: +//| """Plays the sample once when loop=False and continuously when loop=True. +//| Does not block. Use `playing` to block. +//| +//| Sample must be an `audiocore.WaveFile`, `audiocore.RawSample`, `audiomixer.Mixer` or `audiomp3.MP3Decoder`. +//| +//| The sample itself should consist of 8 bit or 16 bit samples.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_play(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_sample, ARG_loop }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_sample, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_loop, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} }, + }; + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mp_obj_t sample = args[ARG_sample].u_obj; + common_hal_mcp4822_mcp4822_play(self, sample, args[ARG_loop].u_bool); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(mcp4822_mcp4822_play_obj, 1, mcp4822_mcp4822_obj_play); + +//| def stop(self) -> None: +//| """Stops playback.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_stop(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_mcp4822_mcp4822_stop(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_stop_obj, mcp4822_mcp4822_obj_stop); + +//| playing: bool +//| """True when the audio sample is being output. (read-only)""" +//| +static mp_obj_t mcp4822_mcp4822_obj_get_playing(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mcp4822_mcp4822_get_playing(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_get_playing_obj, mcp4822_mcp4822_obj_get_playing); + +MP_PROPERTY_GETTER(mcp4822_mcp4822_playing_obj, + (mp_obj_t)&mcp4822_mcp4822_get_playing_obj); + +//| def pause(self) -> None: +//| """Stops playback temporarily while remembering the position. Use `resume` to resume playback.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_pause(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (!common_hal_mcp4822_mcp4822_get_playing(self)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Not playing")); + } + common_hal_mcp4822_mcp4822_pause(self); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_pause_obj, mcp4822_mcp4822_obj_pause); + +//| def resume(self) -> None: +//| """Resumes sample playback after :py:func:`pause`.""" +//| ... +//| +static mp_obj_t mcp4822_mcp4822_obj_resume(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + if (common_hal_mcp4822_mcp4822_get_paused(self)) { + common_hal_mcp4822_mcp4822_resume(self); + } + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_resume_obj, mcp4822_mcp4822_obj_resume); + +//| paused: bool +//| """True when playback is paused. (read-only)""" +//| +//| +static mp_obj_t mcp4822_mcp4822_obj_get_paused(mp_obj_t self_in) { + mcp4822_mcp4822_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_bool(common_hal_mcp4822_mcp4822_get_paused(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mcp4822_mcp4822_get_paused_obj, mcp4822_mcp4822_obj_get_paused); + +MP_PROPERTY_GETTER(mcp4822_mcp4822_paused_obj, + (mp_obj_t)&mcp4822_mcp4822_get_paused_obj); + +static const mp_rom_map_elem_t mcp4822_mcp4822_locals_dict_table[] = { + // Methods + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mcp4822_mcp4822_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mcp4822_mcp4822_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&default___enter___obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&default___exit___obj) }, + { MP_ROM_QSTR(MP_QSTR_play), MP_ROM_PTR(&mcp4822_mcp4822_play_obj) }, + { MP_ROM_QSTR(MP_QSTR_stop), MP_ROM_PTR(&mcp4822_mcp4822_stop_obj) }, + { MP_ROM_QSTR(MP_QSTR_pause), MP_ROM_PTR(&mcp4822_mcp4822_pause_obj) }, + { MP_ROM_QSTR(MP_QSTR_resume), MP_ROM_PTR(&mcp4822_mcp4822_resume_obj) }, + + // Properties + { MP_ROM_QSTR(MP_QSTR_playing), MP_ROM_PTR(&mcp4822_mcp4822_playing_obj) }, + { MP_ROM_QSTR(MP_QSTR_paused), MP_ROM_PTR(&mcp4822_mcp4822_paused_obj) }, +}; +static MP_DEFINE_CONST_DICT(mcp4822_mcp4822_locals_dict, mcp4822_mcp4822_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mcp4822_mcp4822_type, + MP_QSTR_MCP4822, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + make_new, mcp4822_mcp4822_make_new, + locals_dict, &mcp4822_mcp4822_locals_dict + ); diff --git a/shared-bindings/mcp4822/MCP4822.h b/shared-bindings/mcp4822/MCP4822.h new file mode 100644 index 0000000000000..b129aec306124 --- /dev/null +++ b/shared-bindings/mcp4822/MCP4822.h @@ -0,0 +1,25 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/mcp4822/MCP4822.h" +#include "common-hal/microcontroller/Pin.h" + +extern const mp_obj_type_t mcp4822_mcp4822_type; + +void common_hal_mcp4822_mcp4822_construct(mcp4822_mcp4822_obj_t *self, + const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, + const mcu_pin_obj_t *cs, uint8_t gain); + +void common_hal_mcp4822_mcp4822_deinit(mcp4822_mcp4822_obj_t *self); +bool common_hal_mcp4822_mcp4822_deinited(mcp4822_mcp4822_obj_t *self); +void common_hal_mcp4822_mcp4822_play(mcp4822_mcp4822_obj_t *self, mp_obj_t sample, bool loop); +void common_hal_mcp4822_mcp4822_stop(mcp4822_mcp4822_obj_t *self); +bool common_hal_mcp4822_mcp4822_get_playing(mcp4822_mcp4822_obj_t *self); +void common_hal_mcp4822_mcp4822_pause(mcp4822_mcp4822_obj_t *self); +void common_hal_mcp4822_mcp4822_resume(mcp4822_mcp4822_obj_t *self); +bool common_hal_mcp4822_mcp4822_get_paused(mcp4822_mcp4822_obj_t *self); diff --git a/shared-bindings/mcp4822/__init__.c b/shared-bindings/mcp4822/__init__.c new file mode 100644 index 0000000000000..bac2136d9e7c0 --- /dev/null +++ b/shared-bindings/mcp4822/__init__.c @@ -0,0 +1,36 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/mcp4822/__init__.h" +#include "shared-bindings/mcp4822/MCP4822.h" + +//| """Audio output via MCP4822 dual-channel 12-bit SPI DAC. +//| +//| The `mcp4822` module provides the `MCP4822` class for non-blocking +//| audio playback through the Microchip MCP4822 SPI DAC using PIO and DMA. +//| +//| All classes change hardware state and should be deinitialized when they +//| are no longer needed. To do so, either call :py:meth:`!deinit` or use a +//| context manager.""" + +static const mp_rom_map_elem_t mcp4822_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mcp4822) }, + { MP_ROM_QSTR(MP_QSTR_MCP4822), MP_ROM_PTR(&mcp4822_mcp4822_type) }, +}; + +static MP_DEFINE_CONST_DICT(mcp4822_module_globals, mcp4822_module_globals_table); + +const mp_obj_module_t mcp4822_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mcp4822_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_mcp4822, mcp4822_module); diff --git a/shared-bindings/mcp4822/__init__.h b/shared-bindings/mcp4822/__init__.h new file mode 100644 index 0000000000000..c4a52e5819d12 --- /dev/null +++ b/shared-bindings/mcp4822/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/shared-bindings/memorymap/AddressRange.c b/shared-bindings/memorymap/AddressRange.c index efc55ad37d775..5f7ee0deb9554 100644 --- a/shared-bindings/memorymap/AddressRange.c +++ b/shared-bindings/memorymap/AddressRange.c @@ -92,7 +92,6 @@ static mp_obj_t memorymap_addressrange_make_new(const mp_obj_type_t *type, size_ } memorymap_addressrange_obj_t *self = mp_obj_malloc(memorymap_addressrange_obj_t, &memorymap_addressrange_type); - common_hal_memorymap_addressrange_construct(self, (uint8_t *)start, length); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/memorymonitor/AllocationAlarm.c b/shared-bindings/memorymonitor/AllocationAlarm.c index 5475e2d7db411..00cfbbd9fe194 100644 --- a/shared-bindings/memorymonitor/AllocationAlarm.c +++ b/shared-bindings/memorymonitor/AllocationAlarm.c @@ -48,7 +48,6 @@ static mp_obj_t memorymonitor_allocationalarm_make_new(const mp_obj_type_t *type memorymonitor_allocationalarm_obj_t *self = mp_obj_malloc(memorymonitor_allocationalarm_obj_t, &memorymonitor_allocationalarm_type); - common_hal_memorymonitor_allocationalarm_construct(self, minimum_block_count); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/memorymonitor/__init__.c b/shared-bindings/memorymonitor/__init__.c index 64d0140ff7930..e364344b363bd 100644 --- a/shared-bindings/memorymonitor/__init__.c +++ b/shared-bindings/memorymonitor/__init__.c @@ -25,7 +25,7 @@ //| MP_DEFINE_MEMORYMONITOR_EXCEPTION(AllocationError, Exception) -NORETURN void mp_raise_memorymonitor_AllocationError(mp_rom_error_text_t fmt, ...) { +MP_NORETURN void mp_raise_memorymonitor_AllocationError(mp_rom_error_text_t fmt, ...) { va_list argptr; va_start(argptr, fmt); mp_obj_t exception = mp_obj_new_exception_msg_vlist(&mp_type_memorymonitor_AllocationError, fmt, argptr); diff --git a/shared-bindings/memorymonitor/__init__.h b/shared-bindings/memorymonitor/__init__.h index 28480bea7b52b..5f141b924d09d 100644 --- a/shared-bindings/memorymonitor/__init__.h +++ b/shared-bindings/memorymonitor/__init__.h @@ -23,4 +23,4 @@ void memorymonitor_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_pr extern const mp_obj_type_t mp_type_memorymonitor_AllocationError; -NORETURN void mp_raise_memorymonitor_AllocationError(mp_rom_error_text_t msg, ...); +MP_NORETURN void mp_raise_memorymonitor_AllocationError(mp_rom_error_text_t msg, ...); diff --git a/shared-bindings/microcontroller/Pin.c b/shared-bindings/microcontroller/Pin.c index e74d54b0770a7..1fcde50e060b6 100644 --- a/shared-bindings/microcontroller/Pin.c +++ b/shared-bindings/microcontroller/Pin.c @@ -175,14 +175,14 @@ void validate_pins(qstr what, uint8_t *pin_nos, mp_int_t max_pins, mp_obj_t seq, } } -NORETURN void raise_ValueError_invalid_pin(void) { +MP_NORETURN void raise_ValueError_invalid_pin(void) { mp_arg_error_invalid(MP_QSTR_pin); } -NORETURN void raise_ValueError_invalid_pins(void) { +MP_NORETURN void raise_ValueError_invalid_pins(void) { mp_arg_error_invalid(MP_QSTR_pins); } -NORETURN void raise_ValueError_invalid_pin_name(qstr pin_name) { +MP_NORETURN void raise_ValueError_invalid_pin_name(qstr pin_name) { mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q pin"), pin_name); } diff --git a/shared-bindings/microcontroller/Pin.h b/shared-bindings/microcontroller/Pin.h index 1245b5f23e4a7..8ddca71bfbbe3 100644 --- a/shared-bindings/microcontroller/Pin.h +++ b/shared-bindings/microcontroller/Pin.h @@ -21,9 +21,9 @@ void validate_no_duplicate_pins(mp_obj_t seq, qstr arg_name); void validate_no_duplicate_pins_2(mp_obj_t seq1, mp_obj_t seq2, qstr arg_name1, qstr arg_name2); void validate_list_is_free_pins(qstr what, const mcu_pin_obj_t **pins_out, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out); void validate_pins(qstr what, uint8_t *pin_nos, mp_int_t max_pins, mp_obj_t seq, uint8_t *count_out); -NORETURN void raise_ValueError_invalid_pin(void); -NORETURN void raise_ValueError_invalid_pins(void); -NORETURN void raise_ValueError_invalid_pin_name(qstr pin_name); +MP_NORETURN void raise_ValueError_invalid_pin(void); +MP_NORETURN void raise_ValueError_invalid_pins(void); +MP_NORETURN void raise_ValueError_invalid_pin_name(qstr pin_name); void assert_pin_free(const mcu_pin_obj_t *pin); diff --git a/shared-bindings/microcontroller/__init__.h b/shared-bindings/microcontroller/__init__.h index 2a4a973278162..5c5902ff965f5 100644 --- a/shared-bindings/microcontroller/__init__.h +++ b/shared-bindings/microcontroller/__init__.h @@ -20,7 +20,7 @@ extern void common_hal_mcu_disable_interrupts(void); extern void common_hal_mcu_enable_interrupts(void); extern void common_hal_mcu_on_next_reset(mcu_runmode_t runmode); -NORETURN extern void common_hal_mcu_reset(void); +MP_NORETURN extern void common_hal_mcu_reset(void); extern const mp_obj_dict_t mcu_pin_globals; diff --git a/shared-bindings/mipidsi/Bus.c b/shared-bindings/mipidsi/Bus.c new file mode 100644 index 0000000000000..bac3cd3c75e1b --- /dev/null +++ b/shared-bindings/mipidsi/Bus.c @@ -0,0 +1,84 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "shared-bindings/mipidsi/Bus.h" +#include "shared-bindings/util.h" +#include "shared/runtime/context_manager_helpers.h" + +//| class Bus: +//| def __init__( +//| self, +//| *, +//| frequency: int = 500_000_000, +//| num_lanes: int = 2, +//| ) -> None: +//| """Create a MIPI DSI Bus object. +//| +//| This creates a DSI bus interface. The specific pins used are determined by the board. +//| DSI supports 1-4 data lanes. +//| +//| :param int frequency: the high speed clock frequency in Hz (default 500 MHz) +//| :param int num_lanes: the number of data lanes to use (default 2, range 1-4) +//| """ +//| +// +// +// All MCUs we support only have one DSI bus but it can be shared between multiple displays. One +// display may live longer than the VM, so we need to allocate the bus outside the VM. To simplify +// memory tracking, we use a global object for the bus. +// +static mipidsi_bus_obj_t _mipidsi_bus_obj; + +static mp_obj_t mipidsi_bus_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_frequency, ARG_num_lanes }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 500000000} }, + { MP_QSTR_num_lanes, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 2} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + _mipidsi_bus_obj.base.type = &mipidsi_bus_type; + mipidsi_bus_obj_t *self = &_mipidsi_bus_obj; + + mp_uint_t frequency = (mp_uint_t)mp_arg_validate_int_min(args[ARG_frequency].u_int, 1, MP_QSTR_frequency); + uint8_t num_lanes = (uint8_t)mp_arg_validate_int_range(args[ARG_num_lanes].u_int, 1, 4, MP_QSTR_num_lanes); + + common_hal_mipidsi_bus_construct(self, frequency, num_lanes); + + return MP_OBJ_FROM_PTR(self); +} + +//| def deinit(self) -> None: +//| """Free the resources (pins, timers, etc.) associated with this +//| `mipidsi.Bus` instance. After deinitialization, no further operations +//| may be performed.""" +//| ... +//| +static mp_obj_t mipidsi_bus_deinit(mp_obj_t self_in) { + mipidsi_bus_obj_t *self = (mipidsi_bus_obj_t *)self_in; + common_hal_mipidsi_bus_deinit(self); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_bus_deinit_obj, mipidsi_bus_deinit); + +static const mp_rom_map_elem_t mipidsi_bus_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mipidsi_bus_deinit_obj) }, +}; +static MP_DEFINE_CONST_DICT(mipidsi_bus_locals_dict, mipidsi_bus_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + mipidsi_bus_type, + MP_QSTR_Bus, + MP_TYPE_FLAG_NONE, + make_new, mipidsi_bus_make_new, + locals_dict, &mipidsi_bus_locals_dict + ); diff --git a/shared-bindings/mipidsi/Bus.h b/shared-bindings/mipidsi/Bus.h new file mode 100644 index 0000000000000..f2fd13f7fc165 --- /dev/null +++ b/shared-bindings/mipidsi/Bus.h @@ -0,0 +1,15 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/mipidsi/Bus.h" + +extern const mp_obj_type_t mipidsi_bus_type; + +void common_hal_mipidsi_bus_construct(mipidsi_bus_obj_t *self, mp_uint_t frequency, uint8_t num_lanes); +void common_hal_mipidsi_bus_deinit(mipidsi_bus_obj_t *self); +bool common_hal_mipidsi_bus_deinited(mipidsi_bus_obj_t *self); diff --git a/shared-bindings/mipidsi/Display.c b/shared-bindings/mipidsi/Display.c new file mode 100644 index 0000000000000..149d31e52a93f --- /dev/null +++ b/shared-bindings/mipidsi/Display.c @@ -0,0 +1,300 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" +#include "py/objtype.h" + +#include "shared-bindings/mipidsi/Display.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" +#include "shared-module/framebufferio/FramebufferDisplay.h" + +//| class Display: +//| def __init__( +//| self, +//| bus: Bus, +//| init_sequence: ReadableBuffer, +//| *, +//| width: int, +//| height: int, +//| hsync_pulse_width: int, +//| hsync_back_porch: int, +//| hsync_front_porch: int, +//| vsync_pulse_width: int, +//| vsync_back_porch: int, +//| vsync_front_porch: int, +//| pixel_clock_frequency: int, +//| virtual_channel: int = 0, +//| rotation: int = 0, +//| color_depth: int = 16, +//| backlight_pin: Optional[microcontroller.Pin] = None, +//| brightness: float = 1.0, +//| native_frames_per_second: int = 60, +//| backlight_on_high: bool = True, +//| ) -> None: +//| """Create a MIPI DSI Display object connected to the given bus. +//| +//| This allocates a framebuffer and configures the DSI display to use the +//| specified virtual channel for communication. +//| +//| The framebuffer pixel format varies depending on color_depth: +//| +//| * 16 - Each two bytes is a pixel in RGB565 format. +//| * 24 - Each three bytes is a pixel in RGB888 format. +//| +//| A Display is often used in conjunction with a +//| `framebufferio.FramebufferDisplay`. +//| +//| :param Bus bus: the DSI bus to use +//| :param ~circuitpython_typing.ReadableBuffer init_sequence: Byte-packed initialization sequence for the display +//| :param int width: the width of the framebuffer in pixels +//| :param int height: the height of the framebuffer in pixels +//| :param int hsync_pulse_width: horizontal sync pulse width in pixel clocks +//| :param int hsync_back_porch: horizontal back porch in pixel clocks +//| :param int hsync_front_porch: horizontal front porch in pixel clocks +//| :param int vsync_pulse_width: vertical sync pulse width in lines +//| :param int vsync_back_porch: vertical back porch in lines +//| :param int vsync_front_porch: vertical front porch in lines +//| :param int pixel_clock_frequency: pixel clock frequency in Hz +//| :param int virtual_channel: the DSI virtual channel (0-3) +//| :param int rotation: the rotation of the display in degrees clockwise (0, 90, 180, 270) +//| :param int color_depth: the color depth of the framebuffer in bits (16 or 24) +//| :param microcontroller.Pin backlight_pin: Pin connected to the display's backlight +//| :param float brightness: Initial display brightness (0.0 to 1.0) +//| :param int native_frames_per_second: Number of display refreshes per second +//| :param bool backlight_on_high: If True, pulling the backlight pin high turns the backlight on +//| """ +//| + +static mp_obj_t mipidsi_display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_bus, ARG_init_sequence, ARG_width, ARG_height, ARG_hsync_pulse_width, ARG_hsync_back_porch, + ARG_hsync_front_porch, ARG_vsync_pulse_width, ARG_vsync_back_porch, ARG_vsync_front_porch, + ARG_pixel_clock_frequency, ARG_virtual_channel, ARG_rotation, + ARG_color_depth, ARG_backlight_pin, ARG_brightness, ARG_native_frames_per_second, + ARG_backlight_on_high }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_bus, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_init_sequence, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_width, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_height, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_hsync_pulse_width, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_hsync_back_porch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_hsync_front_porch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_vsync_pulse_width, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_vsync_back_porch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_vsync_front_porch, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_pixel_clock_frequency, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_INT }, + { MP_QSTR_virtual_channel, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_rotation, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_color_depth, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 16} }, + { MP_QSTR_backlight_pin, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = mp_const_none} }, + { MP_QSTR_brightness, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_obj = MP_OBJ_NEW_SMALL_INT(1)} }, + { MP_QSTR_native_frames_per_second, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 60} }, + { MP_QSTR_backlight_on_high, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = true} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + mipidsi_display_obj_t *self = &allocate_display_bus_or_raise()->mipidsi; + self->base.type = &mipidsi_display_type; + + mipidsi_bus_obj_t *bus = mp_arg_validate_type(args[ARG_bus].u_obj, &mipidsi_bus_type, MP_QSTR_bus); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_init_sequence].u_obj, &bufinfo, MP_BUFFER_READ); + + const mcu_pin_obj_t *backlight_pin = + validate_obj_is_free_pin_or_none(args[ARG_backlight_pin].u_obj, MP_QSTR_backlight_pin); + + mp_float_t brightness = mp_obj_get_float(args[ARG_brightness].u_obj); + + mp_int_t rotation = args[ARG_rotation].u_int; + if (rotation % 90 != 0) { + mp_raise_ValueError(MP_ERROR_TEXT("Display rotation must be in 90 degree increments")); + } + + mp_uint_t virtual_channel = (mp_uint_t)mp_arg_validate_int_range(args[ARG_virtual_channel].u_int, 0, 3, MP_QSTR_virtual_channel); + mp_uint_t width = (mp_uint_t)mp_arg_validate_int_min(args[ARG_width].u_int, 0, MP_QSTR_width); + mp_uint_t height = (mp_uint_t)mp_arg_validate_int_min(args[ARG_height].u_int, 0, MP_QSTR_height); + mp_uint_t color_depth = args[ARG_color_depth].u_int; + + if (color_depth != 16 && color_depth != 24) { + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_color_depth); + } + + common_hal_mipidsi_display_construct(self, bus, bufinfo.buf, bufinfo.len, virtual_channel, width, height, + rotation, color_depth, MP_OBJ_TO_PTR(backlight_pin), brightness, + args[ARG_native_frames_per_second].u_int, + args[ARG_backlight_on_high].u_bool, + args[ARG_hsync_pulse_width].u_int, + args[ARG_hsync_back_porch].u_int, + args[ARG_hsync_front_porch].u_int, + args[ARG_vsync_pulse_width].u_int, + args[ARG_vsync_back_porch].u_int, + args[ARG_vsync_front_porch].u_int, + args[ARG_pixel_clock_frequency].u_int); + + return MP_OBJ_FROM_PTR(self); +} + +// Helper to ensure we have the native super class instead of a subclass. +static mipidsi_display_obj_t *native_display(mp_obj_t display_obj) { + mp_obj_t native_display = mp_obj_cast_to_native_base(display_obj, &mipidsi_display_type); + mp_obj_assert_native_inited(native_display); + return MP_OBJ_TO_PTR(native_display); +} + +//| def deinit(self) -> None: +//| """Free the resources (pins, timers, etc.) associated with this +//| `mipidsi.Display` instance. After deinitialization, no further operations +//| may be performed.""" +//| ... +//| +static mp_obj_t mipidsi_display_deinit(mp_obj_t self_in) { + mipidsi_display_obj_t *self = native_display(self_in); + common_hal_mipidsi_display_deinit(self); + return mp_const_none; +} + +static MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_display_deinit_obj, mipidsi_display_deinit); + +static void check_for_deinit(mipidsi_display_obj_t *self) { + if (common_hal_mipidsi_display_deinited(self)) { + raise_deinited_error(); + } +} + +//| width: int +//| """The width of the framebuffer, in pixels.""" +static mp_obj_t mipidsi_display_get_width(mp_obj_t self_in) { + mipidsi_display_obj_t *self = native_display(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_mipidsi_display_get_width(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_display_get_width_obj, mipidsi_display_get_width); +MP_PROPERTY_GETTER(mipidsi_display_width_obj, + (mp_obj_t)&mipidsi_display_get_width_obj); + +//| height: int +//| """The height of the framebuffer, in pixels.""" +//| +//| +static mp_obj_t mipidsi_display_get_height(mp_obj_t self_in) { + mipidsi_display_obj_t *self = native_display(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_mipidsi_display_get_height(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_display_get_height_obj, mipidsi_display_get_height); + +MP_PROPERTY_GETTER(mipidsi_display_height_obj, + (mp_obj_t)&mipidsi_display_get_height_obj); + +//| color_depth: int +//| """The color depth of the framebuffer.""" +static mp_obj_t mipidsi_display_get_color_depth(mp_obj_t self_in) { + mipidsi_display_obj_t *self = native_display(self_in); + check_for_deinit(self); + return MP_OBJ_NEW_SMALL_INT(common_hal_mipidsi_display_get_color_depth(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(mipidsi_display_get_color_depth_obj, mipidsi_display_get_color_depth); +MP_PROPERTY_GETTER(mipidsi_display_color_depth_obj, + (mp_obj_t)&mipidsi_display_get_color_depth_obj); + + +static const mp_rom_map_elem_t mipidsi_display_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mipidsi_display_deinit_obj) }, + + { MP_ROM_QSTR(MP_QSTR_width), MP_ROM_PTR(&mipidsi_display_width_obj) }, + { MP_ROM_QSTR(MP_QSTR_height), MP_ROM_PTR(&mipidsi_display_height_obj) }, + { MP_ROM_QSTR(MP_QSTR_color_depth), MP_ROM_PTR(&mipidsi_display_color_depth_obj) }, +}; +static MP_DEFINE_CONST_DICT(mipidsi_display_locals_dict, mipidsi_display_locals_dict_table); + +static void mipidsi_display_get_bufinfo(mp_obj_t self_in, mp_buffer_info_t *bufinfo) { + common_hal_mipidsi_display_get_buffer(self_in, bufinfo, 0); +} + +static float mipidsi_display_get_brightness_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_brightness(self_in); +} + +static bool mipidsi_display_set_brightness_proto(mp_obj_t self_in, mp_float_t value) { + common_hal_mipidsi_display_set_brightness(self_in, value); + return true; +} + +// These versions exist so that the prototype matches the protocol, +// avoiding a type cast that can hide errors +static void mipidsi_display_swapbuffers(mp_obj_t self_in, uint8_t *dirty_row_bitmap) { + (void)dirty_row_bitmap; + common_hal_mipidsi_display_refresh(self_in); +} + +static void mipidsi_display_deinit_proto(mp_obj_t self_in) { + common_hal_mipidsi_display_deinit(self_in); +} + +static int mipidsi_display_get_width_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_width(self_in); +} + +static int mipidsi_display_get_height_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_height(self_in); +} + +static int mipidsi_display_get_color_depth_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_color_depth(self_in); +} + +static bool mipidsi_display_get_grayscale_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_grayscale(self_in); +} + +static int mipidsi_display_get_bytes_per_cell_proto(mp_obj_t self_in) { + return 1; +} + +static int mipidsi_display_get_native_frames_per_second_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_native_frames_per_second(self_in); +} + +static bool mipidsi_display_get_pixels_in_byte_share_row_proto(mp_obj_t self_in) { + return true; +} + +static int mipidsi_display_get_row_stride_proto(mp_obj_t self_in) { + return common_hal_mipidsi_display_get_row_stride(self_in); +} + +static const framebuffer_p_t mipidsi_display_proto = { + MP_PROTO_IMPLEMENT(MP_QSTR_protocol_framebuffer) + .get_bufinfo = mipidsi_display_get_bufinfo, + .set_brightness = mipidsi_display_set_brightness_proto, + .get_brightness = mipidsi_display_get_brightness_proto, + .get_width = mipidsi_display_get_width_proto, + .get_height = mipidsi_display_get_height_proto, + .get_color_depth = mipidsi_display_get_color_depth_proto, + .get_grayscale = mipidsi_display_get_grayscale_proto, + .get_row_stride = mipidsi_display_get_row_stride_proto, + .get_bytes_per_cell = mipidsi_display_get_bytes_per_cell_proto, + .get_native_frames_per_second = mipidsi_display_get_native_frames_per_second_proto, + .get_pixels_in_byte_share_row = mipidsi_display_get_pixels_in_byte_share_row_proto, + .swapbuffers = mipidsi_display_swapbuffers, + .deinit = mipidsi_display_deinit_proto, +}; + +MP_DEFINE_CONST_OBJ_TYPE( + mipidsi_display_type, + MP_QSTR_Display, + MP_TYPE_FLAG_HAS_SPECIAL_ACCESSORS, + locals_dict, &mipidsi_display_locals_dict, + make_new, mipidsi_display_make_new, + buffer, common_hal_mipidsi_display_get_buffer, + protocol, &mipidsi_display_proto + ); diff --git a/shared-bindings/mipidsi/Display.h b/shared-bindings/mipidsi/Display.h new file mode 100644 index 0000000000000..09cf130bf4a28 --- /dev/null +++ b/shared-bindings/mipidsi/Display.h @@ -0,0 +1,46 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "common-hal/mipidsi/Display.h" +#include "shared-bindings/mipidsi/Bus.h" +#include "common-hal/microcontroller/Pin.h" + +extern const mp_obj_type_t mipidsi_display_type; + +void common_hal_mipidsi_display_construct(mipidsi_display_obj_t *self, + mipidsi_bus_obj_t *bus, + const uint8_t *init_sequence, + size_t init_sequence_len, + mp_uint_t virtual_channel, + mp_uint_t width, + mp_uint_t height, + mp_int_t rotation, + mp_uint_t color_depth, + const mcu_pin_obj_t *backlight_pin, + mp_float_t brightness, + mp_uint_t native_frames_per_second, + bool backlight_on_high, + mp_uint_t hsync_pulse_width, + mp_uint_t hsync_back_porch, + mp_uint_t hsync_front_porch, + mp_uint_t vsync_pulse_width, + mp_uint_t vsync_back_porch, + mp_uint_t vsync_front_porch, + mp_uint_t pixel_clock_frequency); +void common_hal_mipidsi_display_deinit(mipidsi_display_obj_t *self); +bool common_hal_mipidsi_display_deinited(mipidsi_display_obj_t *self); +void common_hal_mipidsi_display_refresh(mipidsi_display_obj_t *self); +mp_float_t common_hal_mipidsi_display_get_brightness(mipidsi_display_obj_t *self); +bool common_hal_mipidsi_display_set_brightness(mipidsi_display_obj_t *self, mp_float_t brightness); +int common_hal_mipidsi_display_get_width(mipidsi_display_obj_t *self); +int common_hal_mipidsi_display_get_height(mipidsi_display_obj_t *self); +int common_hal_mipidsi_display_get_row_stride(mipidsi_display_obj_t *self); +int common_hal_mipidsi_display_get_color_depth(mipidsi_display_obj_t *self); +int common_hal_mipidsi_display_get_native_frames_per_second(mipidsi_display_obj_t *self); +bool common_hal_mipidsi_display_get_grayscale(mipidsi_display_obj_t *self); +mp_int_t common_hal_mipidsi_display_get_buffer(mp_obj_t self_in, mp_buffer_info_t *bufinfo, mp_uint_t flags); diff --git a/shared-bindings/mipidsi/__init__.c b/shared-bindings/mipidsi/__init__.c new file mode 100644 index 0000000000000..0b6b856592918 --- /dev/null +++ b/shared-bindings/mipidsi/__init__.c @@ -0,0 +1,30 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/mipidsi/Bus.h" +#include "shared-bindings/mipidsi/Display.h" + +//| """Low-level routines for interacting with MIPI DSI""" + +static const mp_rom_map_elem_t mipidsi_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mipidsi) }, + { MP_ROM_QSTR(MP_QSTR_Bus), MP_ROM_PTR(&mipidsi_bus_type) }, + { MP_ROM_QSTR(MP_QSTR_Display), MP_ROM_PTR(&mipidsi_display_type) }, +}; + +static MP_DEFINE_CONST_DICT(mipidsi_module_globals, mipidsi_module_globals_table); + +const mp_obj_module_t mipidsi_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&mipidsi_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_mipidsi, mipidsi_module); diff --git a/shared-bindings/mipidsi/__init__.h b/shared-bindings/mipidsi/__init__.h new file mode 100644 index 0000000000000..972a7c082fd7b --- /dev/null +++ b/shared-bindings/mipidsi/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/shared-bindings/msgpack/ExtType.c b/shared-bindings/msgpack/ExtType.c index f66abda8fa21e..c69416f9773e7 100644 --- a/shared-bindings/msgpack/ExtType.c +++ b/shared-bindings/msgpack/ExtType.c @@ -18,7 +18,6 @@ //| :param bytes data: representation.""" //| static mp_obj_t mod_msgpack_exttype_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - mod_msgpack_extype_obj_t *self = mp_obj_malloc(mod_msgpack_extype_obj_t, &mod_msgpack_exttype_type); enum { ARG_code, ARG_data }; static const mp_arg_t allowed_args[] = { { MP_QSTR_code, MP_ARG_INT | MP_ARG_REQUIRED }, @@ -28,11 +27,12 @@ static mp_obj_t mod_msgpack_exttype_make_new(const mp_obj_type_t *type, size_t n mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); int code = mp_arg_validate_int_range(args[ARG_code].u_int, 0, 127, MP_QSTR_code); + mp_obj_t data = args[ARG_data].u_obj; + mod_msgpack_extype_obj_t *self = mp_obj_malloc(mod_msgpack_extype_obj_t, &mod_msgpack_exttype_type); self->code = code; - - mp_obj_t data = args[ARG_data].u_obj; self->data = data; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/nvm/ByteArray.c b/shared-bindings/nvm/ByteArray.c index be01c1f706414..b278300626737 100644 --- a/shared-bindings/nvm/ByteArray.c +++ b/shared-bindings/nvm/ByteArray.c @@ -35,7 +35,7 @@ //| static mp_obj_t nvm_bytearray_unary_op(mp_unary_op_t op, mp_obj_t self_in) { nvm_bytearray_obj_t *self = MP_OBJ_TO_PTR(self_in); - uint16_t len = common_hal_nvm_bytearray_get_length(self); + uint32_t len = common_hal_nvm_bytearray_get_length(self); switch (op) { case MP_UNARY_OP_BOOL: return mp_obj_new_bool(len != 0); diff --git a/shared-bindings/onewireio/OneWire.c b/shared-bindings/onewireio/OneWire.c index a3750ae5e074d..2f69424cc234b 100644 --- a/shared-bindings/onewireio/OneWire.c +++ b/shared-bindings/onewireio/OneWire.c @@ -43,8 +43,8 @@ static mp_obj_t onewireio_onewire_make_new(const mp_obj_type_t *type, size_t n_a const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); onewireio_onewire_obj_t *self = mp_obj_malloc(onewireio_onewire_obj_t, &onewireio_onewire_type); - common_hal_onewireio_onewire_construct(self, pin); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/os/__init__.c b/shared-bindings/os/__init__.c index 5e28c02452d05..bbf5a5025c1bf 100644 --- a/shared-bindings/os/__init__.c +++ b/shared-bindings/os/__init__.c @@ -67,7 +67,7 @@ static MP_DEFINE_ATTRTUPLE( ); static mp_obj_t os_uname(void) { - return (mp_obj_t)&os_uname_info_obj; + return MP_OBJ_FROM_PTR(&os_uname_info_obj); } static MP_DEFINE_CONST_FUN_OBJ_0(os_uname_obj, os_uname); @@ -94,11 +94,20 @@ static mp_obj_t os_getcwd(void) { MP_DEFINE_CONST_FUN_OBJ_0(os_getcwd_obj, os_getcwd); //| def getenv(key: str, default: Optional[str] = None) -> Optional[str]: -//| """Get the environment variable value for the given key or return ``default``. +//| """Get the environment variable value for the given ``key`` from the +//| ``/settings.toml`` file. +//| If ``key`` is not present or the value is ill-formed, return the ``default`` value. +//| The value is returned as a string even if it can be parsed as an integer or boolean. +//| No errors are raised if the value is not a valid TOML value; instead the original +//| string is returned, or ``default`` if no value can be retrieved at all. //| -//| This may load values from disk so cache the result instead of calling this often. +//| If you want to retrieve the value as a ``str``, ``bool``, or ``int``, use `supervisor.get_setting()`. //| -//| On boards that do not support ``settings.toml`` reading in the core, this function will raise NotImplementedError. +//| The ``settings.toml`` file is re-scanned on every call, +//| so cache the result instead of calling `getenv()` frequently. +//| +//| On boards that do not support ``settings.toml`` reading in the core, +//| this function will raise NotImplementedError. //| //| .. raw:: html //| @@ -118,7 +127,7 @@ MP_DEFINE_CONST_FUN_OBJ_0(os_getcwd_obj, os_getcwd); //| //| static mp_obj_t os_getenv(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML enum { ARG_key, ARG_default }; static const mp_arg_t allowed_args[] = { { MP_QSTR_key, MP_ARG_REQUIRED | MP_ARG_OBJ }, diff --git a/shared-bindings/paralleldisplaybus/__init__.c b/shared-bindings/paralleldisplaybus/__init__.c index 24d968c9e3111..19a381570bada 100644 --- a/shared-bindings/paralleldisplaybus/__init__.c +++ b/shared-bindings/paralleldisplaybus/__init__.c @@ -29,6 +29,3 @@ const mp_obj_module_t paralleldisplaybus_module = { }; MP_REGISTER_MODULE(MP_QSTR_paralleldisplaybus, paralleldisplaybus_module); - -// Remove in CircuitPython 10 -MP_REGISTER_MODULE(MP_QSTR_paralleldisplay, paralleldisplaybus_module); diff --git a/shared-bindings/ps2io/Ps2.c b/shared-bindings/ps2io/Ps2.c index 8766ea7a2a11b..90e6b0b604561 100644 --- a/shared-bindings/ps2io/Ps2.c +++ b/shared-bindings/ps2io/Ps2.c @@ -58,7 +58,6 @@ static mp_obj_t ps2io_ps2_make_new(const mp_obj_type_t *type, size_t n_args, siz const mcu_pin_obj_t *data_pin = validate_obj_is_free_pin(args[ARG_data_pin].u_obj, MP_QSTR_data_pin); ps2io_ps2_obj_t *self = mp_obj_malloc(ps2io_ps2_obj_t, &ps2io_ps2_type); - common_hal_ps2io_ps2_construct(self, data_pin, clock_pin); return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/pulseio/PulseIn.c b/shared-bindings/pulseio/PulseIn.c index d9e6bfd7aecc3..830f7b7c62ce3 100644 --- a/shared-bindings/pulseio/PulseIn.c +++ b/shared-bindings/pulseio/PulseIn.c @@ -69,7 +69,6 @@ static mp_obj_t pulseio_pulsein_make_new(const mp_obj_type_t *type, size_t n_arg const mcu_pin_obj_t *pin = validate_obj_is_free_pin(args[ARG_pin].u_obj, MP_QSTR_pin); pulseio_pulsein_obj_t *self = mp_obj_malloc_with_finaliser(pulseio_pulsein_obj_t, &pulseio_pulsein_type); - common_hal_pulseio_pulsein_construct(self, pin, args[ARG_maxlen].u_int, args[ARG_idle_state].u_bool); diff --git a/shared-bindings/pulseio/PulseOut.c b/shared-bindings/pulseio/PulseOut.c index 947aa4b518ec8..58c98b8e2cce8 100644 --- a/shared-bindings/pulseio/PulseOut.c +++ b/shared-bindings/pulseio/PulseOut.c @@ -62,6 +62,7 @@ static mp_obj_t pulseio_pulseout_make_new(const mp_obj_type_t *type, size_t n_ar pulseio_pulseout_obj_t *self = mp_obj_malloc_with_finaliser(pulseio_pulseout_obj_t, &pulseio_pulseout_type); common_hal_pulseio_pulseout_construct(self, pin, frequency, duty_cycle); + return MP_OBJ_FROM_PTR(self); #else mp_raise_NotImplementedError(NULL); diff --git a/shared-bindings/qrio/QRDecoder.c b/shared-bindings/qrio/QRDecoder.c index 6a36d8b026582..e8977bc1efee3 100644 --- a/shared-bindings/qrio/QRDecoder.c +++ b/shared-bindings/qrio/QRDecoder.c @@ -34,7 +34,7 @@ static mp_obj_t qrio_qrdecoder_make_new(const mp_obj_type_t *type, size_t n_args qrio_qrdecoder_obj_t *self = mp_obj_malloc(qrio_qrdecoder_obj_t, &qrio_qrdecoder_type_obj); shared_module_qrio_qrdecoder_construct(self, args[ARG_width].u_int, args[ARG_height].u_int); - return self; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/qspibus/QSPIBus.c b/shared-bindings/qspibus/QSPIBus.c new file mode 100644 index 0000000000000..24c3089213fec --- /dev/null +++ b/shared-bindings/qspibus/QSPIBus.c @@ -0,0 +1,199 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT + +#include + +#include "shared-bindings/qspibus/QSPIBus.h" + +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "shared-module/displayio/__init__.h" + +#include "py/binary.h" +#include "py/obj.h" +#include "py/runtime.h" + +static void check_for_deinit(qspibus_qspibus_obj_t *self) { + if (common_hal_qspibus_qspibus_deinited(self)) { + raise_deinited_error(); + } +} + +//| class QSPIBus: +//| """QSPI bus for quad-SPI displays.""" +//| +//| def __init__( +//| self, +//| *, +//| clock: microcontroller.Pin, +//| data0: microcontroller.Pin, +//| data1: microcontroller.Pin, +//| data2: microcontroller.Pin, +//| data3: microcontroller.Pin, +//| cs: microcontroller.Pin, +//| dcx: Optional[microcontroller.Pin] = None, +//| reset: Optional[microcontroller.Pin] = None, +//| frequency: int = 80_000_000, +//| ) -> None: +//| """Create a QSPIBus object for quad-SPI display communication. +//| +//| :param ~microcontroller.Pin clock: QSPI clock pin +//| :param ~microcontroller.Pin data0: QSPI data line 0 +//| :param ~microcontroller.Pin data1: QSPI data line 1 +//| :param ~microcontroller.Pin data2: QSPI data line 2 +//| :param ~microcontroller.Pin data3: QSPI data line 3 +//| :param ~microcontroller.Pin cs: Chip select pin +//| :param ~microcontroller.Pin dcx: Optional data/command select pin. +//| Reserved for future hardware paths. Current ESP32-S3 implementation +//| uses encoded QSPI command words and does not require explicit DCX. +//| :param ~microcontroller.Pin reset: Optional reset pin +//| :param int frequency: Bus frequency in Hz (1-80MHz) +//| """ +//| ... +//| +static mp_obj_t qspibus_qspibus_make_new(const mp_obj_type_t *type, size_t n_args, + size_t n_kw, const mp_obj_t *all_args) { + + enum { ARG_clock, ARG_data0, ARG_data1, ARG_data2, ARG_data3, ARG_cs, ARG_dcx, ARG_reset, ARG_frequency }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_clock, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data0, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data1, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data2, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_data3, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_cs, MP_ARG_KW_ONLY | MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_dcx, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_reset, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_frequency, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 80000000} }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + const mcu_pin_obj_t *clock = validate_obj_is_free_pin(args[ARG_clock].u_obj, MP_QSTR_clock); + const mcu_pin_obj_t *data0 = validate_obj_is_free_pin(args[ARG_data0].u_obj, MP_QSTR_data0); + const mcu_pin_obj_t *data1 = validate_obj_is_free_pin(args[ARG_data1].u_obj, MP_QSTR_data1); + const mcu_pin_obj_t *data2 = validate_obj_is_free_pin(args[ARG_data2].u_obj, MP_QSTR_data2); + const mcu_pin_obj_t *data3 = validate_obj_is_free_pin(args[ARG_data3].u_obj, MP_QSTR_data3); + const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); + const mcu_pin_obj_t *dcx = validate_obj_is_free_pin_or_none(args[ARG_dcx].u_obj, MP_QSTR_dcx); + const mcu_pin_obj_t *reset = validate_obj_is_free_pin_or_none(args[ARG_reset].u_obj, MP_QSTR_reset); + + uint32_t frequency = (uint32_t)mp_arg_validate_int_range(args[ARG_frequency].u_int, 1, 80000000, MP_QSTR_frequency); + + qspibus_qspibus_obj_t *self = &allocate_display_bus_or_raise()->qspi_bus; + self->base.type = &qspibus_qspibus_type; + common_hal_qspibus_qspibus_construct(self, clock, data0, data1, data2, data3, cs, dcx, reset, frequency); + + return MP_OBJ_FROM_PTR(self); +} + +//| def reset(self) -> None: +//| """Perform a hardware reset using the reset pin. +//| +//| :raises RuntimeError: if no reset pin was provided at construction. +//| """ +//| ... +//| +static mp_obj_t qspibus_qspibus_obj_reset(mp_obj_t self_in) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + if (!common_hal_qspibus_qspibus_reset(MP_OBJ_FROM_PTR(self))) { + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("No %q pin"), MP_QSTR_reset); + } + return mp_const_none; +} +static MP_DEFINE_CONST_FUN_OBJ_1(qspibus_qspibus_reset_obj, qspibus_qspibus_obj_reset); + +//| def send( +//| self, command: int, data: ReadableBuffer, *, toggle_every_byte: bool = False +//| ) -> None: +//| """Sends the given command value followed by the full set of data. Display state, such as +//| vertical scroll, set via ``send`` may or may not be reset once the code is done.""" +//| ... +//| +static mp_obj_t qspibus_qspibus_send(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_command, ARG_data }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_command, MP_ARG_INT | MP_ARG_REQUIRED }, + { MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + check_for_deinit(self); + + uint8_t command = (uint8_t)mp_arg_validate_int_range(args[ARG_command].u_int, 0, 255, MP_QSTR_command); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(args[ARG_data].u_obj, &bufinfo, MP_BUFFER_READ); + + // Flush any pending command from a prior write_command() call. + // begin_transaction() returns false while has_pending_command is set, + // so entering the wait loop without flushing would spin forever. + if (self->has_pending_command) { + common_hal_qspibus_qspibus_write_data(self, NULL, 0); + } + + // Wait for display bus to be available, then acquire transaction. + while (!common_hal_qspibus_qspibus_begin_transaction(MP_OBJ_FROM_PTR(self))) { + RUN_BACKGROUND_TASKS; + } + common_hal_qspibus_qspibus_send(MP_OBJ_FROM_PTR(self), DISPLAY_COMMAND, CHIP_SELECT_UNTOUCHED, &command, 1); + common_hal_qspibus_qspibus_send(MP_OBJ_FROM_PTR(self), DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, ((uint8_t *)bufinfo.buf), bufinfo.len); + common_hal_qspibus_qspibus_end_transaction(MP_OBJ_FROM_PTR(self)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_KW(qspibus_qspibus_send_obj, 1, qspibus_qspibus_send); + +//| def write_command(self, command: int) -> None: +//| """Stage a command byte for subsequent :py:meth:`write_data`. +//| +//| If a previously staged command had no data, it is sent as +//| a command-only transaction before staging the new one. +//| """ +//| ... +//| +static mp_obj_t qspibus_qspibus_write_command(mp_obj_t self_in, mp_obj_t command_obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + uint8_t command = (uint8_t)mp_arg_validate_int_range(mp_obj_get_int(command_obj), 0, 255, MP_QSTR_command); + common_hal_qspibus_qspibus_write_command(self, command); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(qspibus_qspibus_write_command_obj, qspibus_qspibus_write_command); + +//| def write_data(self, data: ReadableBuffer) -> None: +//| """Send payload bytes for the most recently staged command.""" +//| ... +//| +static mp_obj_t qspibus_qspibus_write_data(mp_obj_t self_in, mp_obj_t data_obj) { + qspibus_qspibus_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(data_obj, &bufinfo, MP_BUFFER_READ); + common_hal_qspibus_qspibus_write_data(self, (const uint8_t *)bufinfo.buf, bufinfo.len); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(qspibus_qspibus_write_data_obj, qspibus_qspibus_write_data); + +static const mp_rom_map_elem_t qspibus_qspibus_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR_reset), MP_ROM_PTR(&qspibus_qspibus_reset_obj) }, + { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&qspibus_qspibus_send_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_command), MP_ROM_PTR(&qspibus_qspibus_write_command_obj) }, + { MP_ROM_QSTR(MP_QSTR_write_data), MP_ROM_PTR(&qspibus_qspibus_write_data_obj) }, +}; +static MP_DEFINE_CONST_DICT(qspibus_qspibus_locals_dict, qspibus_qspibus_locals_dict_table); + +MP_DEFINE_CONST_OBJ_TYPE( + qspibus_qspibus_type, + MP_QSTR_QSPIBus, + MP_TYPE_FLAG_NONE, + make_new, qspibus_qspibus_make_new, + locals_dict, &qspibus_qspibus_locals_dict + ); diff --git a/shared-bindings/qspibus/QSPIBus.h b/shared-bindings/qspibus/QSPIBus.h new file mode 100644 index 0000000000000..140b639279dc0 --- /dev/null +++ b/shared-bindings/qspibus/QSPIBus.h @@ -0,0 +1,53 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" + +#include "shared-bindings/displayio/__init__.h" + +#include "common-hal/microcontroller/Pin.h" +#include "common-hal/qspibus/QSPIBus.h" + +extern const mp_obj_type_t qspibus_qspibus_type; + +void common_hal_qspibus_qspibus_construct( + qspibus_qspibus_obj_t *self, + const mcu_pin_obj_t *clock, + const mcu_pin_obj_t *data0, + const mcu_pin_obj_t *data1, + const mcu_pin_obj_t *data2, + const mcu_pin_obj_t *data3, + const mcu_pin_obj_t *cs, + const mcu_pin_obj_t *dcx, + const mcu_pin_obj_t *reset, + uint32_t frequency); + +void common_hal_qspibus_qspibus_deinit(qspibus_qspibus_obj_t *self); +bool common_hal_qspibus_qspibus_deinited(qspibus_qspibus_obj_t *self); + +void common_hal_qspibus_qspibus_write_command( + qspibus_qspibus_obj_t *self, + uint8_t command); +void common_hal_qspibus_qspibus_write_data( + qspibus_qspibus_obj_t *self, + const uint8_t *data, + size_t len); + +bool common_hal_qspibus_qspibus_reset(mp_obj_t obj); +bool common_hal_qspibus_qspibus_bus_free(mp_obj_t obj); +bool common_hal_qspibus_qspibus_begin_transaction(mp_obj_t obj); +void common_hal_qspibus_qspibus_send( + mp_obj_t obj, + display_byte_type_t data_type, + display_chip_select_behavior_t chip_select, + const uint8_t *data, + uint32_t data_length); +void common_hal_qspibus_qspibus_end_transaction(mp_obj_t obj); +void common_hal_qspibus_qspibus_flush(mp_obj_t obj); +void common_hal_qspibus_qspibus_collect_ptrs(mp_obj_t obj); diff --git a/shared-bindings/qspibus/__init__.c b/shared-bindings/qspibus/__init__.c new file mode 100644 index 0000000000000..0281c41c80420 --- /dev/null +++ b/shared-bindings/qspibus/__init__.c @@ -0,0 +1,50 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT + +#include "py/obj.h" +#include "py/runtime.h" + +#include "shared-bindings/qspibus/__init__.h" +#include "shared-bindings/qspibus/QSPIBus.h" + +//| """QSPI bus protocol for quad-SPI displays +//| +//| The `qspibus` module provides a low-level QSPI bus interface for displays +//| that use four data lines. It is analogous to `fourwire` for standard SPI. +//| +//| Use :class:`qspibus.QSPIBus` to create a bus instance. +//| +//| Example usage:: +//| +//| import board +//| import qspibus +//| import displayio +//| +//| displayio.release_displays() +//| +//| bus = qspibus.QSPIBus( +//| clock=board.LCD_CLK, +//| data0=board.LCD_D0, +//| data1=board.LCD_D1, +//| data2=board.LCD_D2, +//| data3=board.LCD_D3, +//| cs=board.LCD_CS, +//| reset=board.LCD_RESET, +//| frequency=80_000_000, +//| ) +//| """ + +static const mp_rom_map_elem_t qspibus_module_globals_table[] = { + { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_qspibus) }, + { MP_ROM_QSTR(MP_QSTR_QSPIBus), MP_ROM_PTR(&qspibus_qspibus_type) }, +}; + +static MP_DEFINE_CONST_DICT(qspibus_module_globals, qspibus_module_globals_table); + +const mp_obj_module_t qspibus_module = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t *)&qspibus_module_globals, +}; + +MP_REGISTER_MODULE(MP_QSTR_qspibus, qspibus_module); diff --git a/shared-bindings/qspibus/__init__.h b/shared-bindings/qspibus/__init__.h new file mode 100644 index 0000000000000..9b4ca24332703 --- /dev/null +++ b/shared-bindings/qspibus/__init__.h @@ -0,0 +1,8 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// SPDX-FileCopyrightText: Copyright (c) 2026 Przemyslaw Patrick Socha +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-bindings/qspibus/QSPIBus.h" diff --git a/shared-bindings/rainbowio/__init__.c b/shared-bindings/rainbowio/__init__.c index a085669d7ff9a..e02df713eeba2 100644 --- a/shared-bindings/rainbowio/__init__.c +++ b/shared-bindings/rainbowio/__init__.c @@ -16,7 +16,8 @@ //| //| def colorwheel(n: float) -> int: //| """C implementation of the common colorwheel() function found in many examples. -//| Returns the colorwheel RGB value as an integer value for n (usable in neopixel and dotstar). +//| Takes a hue, a value between 0-255, and returns an RGB tuple encoded as an +//| integer value (usable in neopixel and dotstar). //| """ //| ... //| diff --git a/shared-bindings/rclcpy/Node.c b/shared-bindings/rclcpy/Node.c index a2fa9bd36a656..4097fbc1515f4 100644 --- a/shared-bindings/rclcpy/Node.c +++ b/shared-bindings/rclcpy/Node.c @@ -50,7 +50,8 @@ static mp_obj_t rclcpy_node_make_new(const mp_obj_type_t *type, size_t n_args, s rclcpy_node_obj_t *self = mp_obj_malloc_with_finaliser(rclcpy_node_obj_t, &rclcpy_node_type); common_hal_rclcpy_node_construct(self, node_name, namespace); - return (mp_obj_t)self; + + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: @@ -90,7 +91,7 @@ static mp_obj_t rclcpy_node_create_publisher(mp_obj_t self_in, mp_obj_t topic) { rclcpy_publisher_obj_t *publisher = mp_obj_malloc_with_finaliser(rclcpy_publisher_obj_t, &rclcpy_publisher_type); common_hal_rclcpy_publisher_construct(publisher, self, topic_name); - return (mp_obj_t)publisher; + return MP_OBJ_FROM_PTR(publisher); } static MP_DEFINE_CONST_FUN_OBJ_2(rclcpy_node_create_publisher_obj, rclcpy_node_create_publisher); diff --git a/shared-bindings/rclcpy/__init__.c b/shared-bindings/rclcpy/__init__.c index a6631642ea7c0..e195294cde61b 100644 --- a/shared-bindings/rclcpy/__init__.c +++ b/shared-bindings/rclcpy/__init__.c @@ -112,7 +112,8 @@ static mp_obj_t rclcpy_create_node(size_t n_args, const mp_obj_t *pos_args, mp_m rclcpy_node_obj_t *self = mp_obj_malloc_with_finaliser(rclcpy_node_obj_t, &rclcpy_node_type); common_hal_rclcpy_node_construct(self, node_name, namespace); - return (mp_obj_t)self; + + return MP_OBJ_FROM_PTR(self); } static MP_DEFINE_CONST_FUN_OBJ_KW(rclcpy_create_node_obj, 2, rclcpy_create_node); diff --git a/shared-bindings/rotaryio/IncrementalEncoder.h b/shared-bindings/rotaryio/IncrementalEncoder.h index 1c60dd69a1937..b3ceb3eeb1513 100644 --- a/shared-bindings/rotaryio/IncrementalEncoder.h +++ b/shared-bindings/rotaryio/IncrementalEncoder.h @@ -13,11 +13,15 @@ extern const mp_obj_type_t rotaryio_incrementalencoder_type; extern void common_hal_rotaryio_incrementalencoder_construct(rotaryio_incrementalencoder_obj_t *self, const mcu_pin_obj_t *pin_a, const mcu_pin_obj_t *pin_b); + extern void common_hal_rotaryio_incrementalencoder_deinit(rotaryio_incrementalencoder_obj_t *self); extern bool common_hal_rotaryio_incrementalencoder_deinited(rotaryio_incrementalencoder_obj_t *self); +extern void common_hal_rotaryio_incrementalencoder_mark_deinit(rotaryio_incrementalencoder_obj_t *self); + extern mp_int_t common_hal_rotaryio_incrementalencoder_get_position(rotaryio_incrementalencoder_obj_t *self); extern void common_hal_rotaryio_incrementalencoder_set_position(rotaryio_incrementalencoder_obj_t *self, mp_int_t new_position); + extern mp_int_t common_hal_rotaryio_incrementalencoder_get_divisor(rotaryio_incrementalencoder_obj_t *self); extern void common_hal_rotaryio_incrementalencoder_set_divisor(rotaryio_incrementalencoder_obj_t *self, mp_int_t new_divisor); diff --git a/shared-bindings/sdcardio/SDCard.c b/shared-bindings/sdcardio/SDCard.c index e6d8453eae108..9a04cea57e594 100644 --- a/shared-bindings/sdcardio/SDCard.c +++ b/shared-bindings/sdcardio/SDCard.c @@ -9,7 +9,8 @@ #include "py/objarray.h" #include "shared-bindings/sdcardio/SDCard.h" -#include "shared-module/sdcardio/SDCard.h" +#include "shared-bindings/util.h" + #include "common-hal/busio/SPI.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/microcontroller/Pin.h" @@ -42,6 +43,13 @@ //| Failure to do so can prevent the SD card from being recognized until it is //| powered off or re-inserted. //| +//| Exception: on boards where another SPI peripheral has a floating CS +//| pin with no hardware pull-up (such as the Feather RP2040 RFM), that +//| peripheral's CS must be driven HIGH before SD card initialization. +//| Failure to do so will corrupt the SPI bus during SD card init. In +//| these cases, initialize and drive the other peripheral's CS high +//| first, then initialize the SD card. +//| //| Example usage: //| //| .. code-block:: python @@ -52,12 +60,20 @@ //| import sdcardio //| import storage //| +//| # Make sure to make an "sd" folder on CIRCUITPY +//| //| sd = sdcardio.SDCard(board.SPI(), board.SD_CS) //| vfs = storage.VfsFat(sd) //| storage.mount(vfs, '/sd') -//| os.listdir('/sd')""" +//| print(os.listdir('/sd'))""" //| +static void check_for_deinit(sdcardio_sdcard_obj_t *self) { + if (common_hal_sdcardio_sdcard_deinited(self)) { + raise_deinited_error(); + } +} + static mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { enum { ARG_spi, ARG_cs, ARG_baudrate, NUM_ARGS }; static const mp_arg_t allowed_args[] = { @@ -72,11 +88,10 @@ static mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_arg busio_spi_obj_t *spi = validate_obj_is_spi_bus(args[ARG_spi].u_obj, MP_QSTR_spi); const mcu_pin_obj_t *cs = validate_obj_is_free_pin(args[ARG_cs].u_obj, MP_QSTR_cs); - sdcardio_sdcard_obj_t *self = mp_obj_malloc(sdcardio_sdcard_obj_t, &sdcardio_SDCard_type); - + sdcardio_sdcard_obj_t *self = mp_obj_malloc_with_finaliser(sdcardio_sdcard_obj_t, &sdcardio_SDCard_type); common_hal_sdcardio_sdcard_construct(self, spi, cs, args[ARG_baudrate].u_int); - return self; + return MP_OBJ_FROM_PTR(self); } @@ -89,6 +104,7 @@ static mp_obj_t sdcardio_sdcard_make_new(const mp_obj_type_t *type, size_t n_arg //| static mp_obj_t sdcardio_sdcard_count(mp_obj_t self_in) { sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + check_for_deinit(self); return mp_obj_new_int_from_ull(common_hal_sdcardio_sdcard_get_blockcount(self)); } MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_count_obj, sdcardio_sdcard_count); @@ -116,10 +132,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_deinit_obj, sdcardio_sdcard_deinit); //| static mp_obj_t _sdcardio_sdcard_readblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + check_for_deinit(self); + uint32_t start_block = mp_obj_get_int(start_block_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_WRITE); - sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; int result = common_hal_sdcardio_sdcard_readblocks(self, start_block, &bufinfo); if (result < 0) { mp_raise_OSError(-result); @@ -137,6 +155,7 @@ MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_readblocks_obj, _sdcardio_sdcard_readb //| static mp_obj_t sdcardio_sdcard_sync(mp_obj_t self_in) { sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + check_for_deinit(self); int result = common_hal_sdcardio_sdcard_sync(self); if (result < 0) { mp_raise_OSError(-result); @@ -158,10 +177,12 @@ MP_DEFINE_CONST_FUN_OBJ_1(sdcardio_sdcard_sync_obj, sdcardio_sdcard_sync); //| static mp_obj_t _sdcardio_sdcard_writeblocks(mp_obj_t self_in, mp_obj_t start_block_in, mp_obj_t buf_in) { + sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; + check_for_deinit(self); + uint32_t start_block = mp_obj_get_int(start_block_in); mp_buffer_info_t bufinfo; mp_get_buffer_raise(buf_in, &bufinfo, MP_BUFFER_READ); - sdcardio_sdcard_obj_t *self = (sdcardio_sdcard_obj_t *)self_in; int result = common_hal_sdcardio_sdcard_writeblocks(self, start_block, &bufinfo); if (result < 0) { mp_raise_OSError(-result); @@ -173,6 +194,7 @@ MP_DEFINE_CONST_FUN_OBJ_3(sdcardio_sdcard_writeblocks_obj, _sdcardio_sdcard_writ static const mp_rom_map_elem_t sdcardio_sdcard_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_count), MP_ROM_PTR(&sdcardio_sdcard_count_obj) }, { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&sdcardio_sdcard_deinit_obj) }, + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&sdcardio_sdcard_deinit_obj) }, { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&sdcardio_sdcard_readblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_sync), MP_ROM_PTR(&sdcardio_sdcard_sync_obj) }, { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&sdcardio_sdcard_writeblocks_obj) }, diff --git a/shared-bindings/sdcardio/SDCard.h b/shared-bindings/sdcardio/SDCard.h index ac27b47aa4d21..d0cb8206c349d 100644 --- a/shared-bindings/sdcardio/SDCard.h +++ b/shared-bindings/sdcardio/SDCard.h @@ -7,17 +7,20 @@ #pragma once +#include "py/mperrno.h" #include "shared-module/sdcardio/SDCard.h" extern const mp_obj_type_t sdcardio_SDCard_type; void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *spi, const mcu_pin_obj_t *cs, int baudrate); void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self); +bool common_hal_sdcardio_sdcard_deinited(sdcardio_sdcard_obj_t *self); void common_hal_sdcardio_sdcard_check_for_deinit(sdcardio_sdcard_obj_t *self); +void common_hal_sdcardio_sdcard_mark_deinit(sdcardio_sdcard_obj_t *self); int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self); -int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); -int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self); -int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); +mp_negative_errno_t common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); +mp_negative_errno_t common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self); +mp_negative_errno_t common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf); // Used by native vfs blockdev. mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); diff --git a/shared-bindings/sdioio/SDCard.c b/shared-bindings/sdioio/SDCard.c index 892cecd3fb4af..baf1e1660e845 100644 --- a/shared-bindings/sdioio/SDCard.c +++ b/shared-bindings/sdioio/SDCard.c @@ -67,7 +67,6 @@ //| static mp_obj_t sdioio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - sdioio_sdcard_obj_t *self = mp_obj_malloc(sdioio_sdcard_obj_t, &sdioio_SDCard_type); enum { ARG_clock, ARG_command, ARG_data, ARG_frequency, NUM_ARGS }; static const mp_arg_t allowed_args[] = { { MP_QSTR_clock, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ }, @@ -86,6 +85,7 @@ static mp_obj_t sdioio_sdcard_make_new(const mp_obj_type_t *type, size_t n_args, uint8_t num_data; validate_list_is_free_pins(MP_QSTR_data, data_pins, MP_ARRAY_SIZE(data_pins), args[ARG_data].u_obj, &num_data); + sdioio_sdcard_obj_t *self = mp_obj_malloc(sdioio_sdcard_obj_t, &sdioio_SDCard_type); common_hal_sdioio_sdcard_construct(self, clock, command, num_data, data_pins, args[ARG_frequency].u_int); return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/sdioio/SDCard.h b/shared-bindings/sdioio/SDCard.h index dfeaf8fca8962..042521aea50dd 100644 --- a/shared-bindings/sdioio/SDCard.h +++ b/shared-bindings/sdioio/SDCard.h @@ -7,6 +7,7 @@ #pragma once #include "py/obj.h" +#include "py/mperrno.h" #include "common-hal/microcontroller/Pin.h" #include "common-hal/sdioio/SDCard.h" @@ -35,9 +36,14 @@ uint8_t common_hal_sdioio_sdcard_get_width(sdioio_sdcard_obj_t *self); // Return number of device blocks uint32_t common_hal_sdioio_sdcard_get_count(sdioio_sdcard_obj_t *self); -// Read or write blocks -int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); -int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); +// Read or write blocks - returns 0 on success or negative error code from mperrno.h +mp_negative_errno_t common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); +mp_negative_errno_t common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo); + +// Used by native vfs blockdev. +mp_negative_errno_t sdioio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); +mp_negative_errno_t sdioio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t buflen); +bool sdioio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value); // This is used by the supervisor to claim SDIO devices indefinitely. extern void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self); diff --git a/shared-bindings/socketpool/Socket.h b/shared-bindings/socketpool/Socket.h index a883ebd505463..295f86e737169 100644 --- a/shared-bindings/socketpool/Socket.h +++ b/shared-bindings/socketpool/Socket.h @@ -6,6 +6,7 @@ #pragma once +#include "py/mperrno.h" #include "common-hal/socketpool/Socket.h" extern const mp_obj_type_t socketpool_socket_type; diff --git a/shared-bindings/socketpool/SocketPool.c b/shared-bindings/socketpool/SocketPool.c index e139e3a077ab2..1fc9e2c3335ca 100644 --- a/shared-bindings/socketpool/SocketPool.c +++ b/shared-bindings/socketpool/SocketPool.c @@ -24,21 +24,20 @@ //| a pool of sockets provided by the underlying OS. //| """ //| -//| def __init__(self, radio: wifi.Radio) -> None: +//| def __init__(self, radio: Union[wifi.Radio, hostnetwork.HostNetwork]) -> None: //| """Create a new SocketPool object for the provided radio //| -//| :param wifi.Radio radio: The (connected) network hardware to associate -//| with this SocketPool; currently, this will always be the object -//| returned by :py:attr:`wifi.radio` +//| :param radio: The (connected) network interface to associate with this +//| SocketPool, such as :py:attr:`wifi.radio` or :py:attr:`board.NETWORK`. //| """ //| ... //| static mp_obj_t socketpool_socketpool_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 1, false); - socketpool_socketpool_obj_t *s = mp_obj_malloc_with_finaliser(socketpool_socketpool_obj_t, &socketpool_socketpool_type); mp_obj_t radio = args[0]; + socketpool_socketpool_obj_t *s = mp_obj_malloc_with_finaliser(socketpool_socketpool_obj_t, &socketpool_socketpool_type); common_hal_socketpool_socketpool_construct(s, radio); return MP_OBJ_FROM_PTR(s); @@ -190,7 +189,7 @@ MP_DEFINE_CONST_OBJ_TYPE( locals_dict, &socketpool_socketpool_locals_dict ); -MP_WEAK NORETURN +MP_WEAK MP_NORETURN void common_hal_socketpool_socketpool_raise_gaierror_noname(void) { vstr_t vstr; mp_print_t print; diff --git a/shared-bindings/socketpool/SocketPool.h b/shared-bindings/socketpool/SocketPool.h index 36035ed00e11a..60d58372b9131 100644 --- a/shared-bindings/socketpool/SocketPool.h +++ b/shared-bindings/socketpool/SocketPool.h @@ -25,6 +25,6 @@ bool socketpool_socket(socketpool_socketpool_obj_t *self, socketpool_socketpool_addressfamily_t family, socketpool_socketpool_sock_t type, int proto, socketpool_socket_obj_t *sock); -NORETURN void common_hal_socketpool_socketpool_raise_gaierror_noname(void); +MP_NORETURN void common_hal_socketpool_socketpool_raise_gaierror_noname(void); mp_obj_t common_hal_socketpool_getaddrinfo_raise(socketpool_socketpool_obj_t *self, const char *host, int port, int family, int type, int proto, int flags); diff --git a/shared-bindings/spitarget/SPITarget.c b/shared-bindings/spitarget/SPITarget.c index eca92d800277b..08f6d554f3a45 100644 --- a/shared-bindings/spitarget/SPITarget.c +++ b/shared-bindings/spitarget/SPITarget.c @@ -33,7 +33,6 @@ //| ... //| static mp_obj_t spitarget_spi_target_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - spitarget_spi_target_obj_t *self = mp_obj_malloc(spitarget_spi_target_obj_t, &spitarget_spi_target_type); enum { ARG_sck, ARG_mosi, ARG_miso, ARG_ss }; static const mp_arg_t allowed_args[] = { { MP_QSTR_sck, MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -49,7 +48,9 @@ static mp_obj_t spitarget_spi_target_make_new(const mp_obj_type_t *type, size_t const mcu_pin_obj_t *miso = validate_obj_is_free_pin(args[ARG_miso].u_obj, MP_QSTR_miso); const mcu_pin_obj_t *ss = validate_obj_is_free_pin(args[ARG_ss].u_obj, MP_QSTR_ss); + spitarget_spi_target_obj_t *self = mp_obj_malloc(spitarget_spi_target_obj_t, &spitarget_spi_target_type); common_hal_spitarget_spi_target_construct(self, sck, mosi, miso, ss); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/ssl/SSLContext.c b/shared-bindings/ssl/SSLContext.c index 078a716cdb801..9546c50ed7c41 100644 --- a/shared-bindings/ssl/SSLContext.c +++ b/shared-bindings/ssl/SSLContext.c @@ -26,7 +26,6 @@ static mp_obj_t ssl_sslcontext_make_new(const mp_obj_type_t *type, size_t n_args mp_arg_check_num(n_args, n_kw, 0, 1, false); ssl_sslcontext_obj_t *s = mp_obj_malloc(ssl_sslcontext_obj_t, &ssl_sslcontext_type); - common_hal_ssl_sslcontext_construct(s); return MP_OBJ_FROM_PTR(s); diff --git a/shared-bindings/ssl/SSLSocket.c b/shared-bindings/ssl/SSLSocket.c index 92440f9ea30f7..4418a8e48d9c9 100644 --- a/shared-bindings/ssl/SSLSocket.c +++ b/shared-bindings/ssl/SSLSocket.c @@ -106,7 +106,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(ssl_sslsocket_connect_obj, ssl_sslsocket_connec //| def listen(self, backlog: int) -> None: //| """Set socket to listen for incoming connections //| -//| :param ~int backlog: length of backlog queue for waiting connetions""" +//| :param ~int backlog: length of backlog queue for waiting connections""" //| ... //| static mp_obj_t ssl_sslsocket_listen(mp_obj_t self_in, mp_obj_t backlog_in) { diff --git a/shared-bindings/ssl/__init__.c b/shared-bindings/ssl/__init__.c index 0d10090e9cb99..51a3ad3cfc42a 100644 --- a/shared-bindings/ssl/__init__.c +++ b/shared-bindings/ssl/__init__.c @@ -27,9 +27,9 @@ static mp_obj_t ssl_create_default_context(void) { ssl_sslcontext_obj_t *s = mp_obj_malloc(ssl_sslcontext_obj_t, &ssl_sslcontext_type); - common_hal_ssl_create_default_context(s); - return s; + + return MP_OBJ_FROM_PTR(s); } MP_DEFINE_CONST_FUN_OBJ_0(ssl_create_default_context_obj, ssl_create_default_context); diff --git a/shared-bindings/storage/__init__.c b/shared-bindings/storage/__init__.c index ee0fb4ab53c3a..14ec16f3096c7 100644 --- a/shared-bindings/storage/__init__.c +++ b/shared-bindings/storage/__init__.c @@ -54,15 +54,11 @@ static mp_obj_t storage_mount(size_t n_args, const mp_obj_t *pos_args, mp_map_t // get the mount point const char *mnt_str = mp_obj_str_get_str(args[ARG_mount_path].u_obj); - // Make sure we're given an object we can mount. - // TODO(tannewt): Make sure we have all the methods we need to operating it - // as a file system. + mp_obj_t vfs_obj = args[ARG_filesystem].u_obj; - mp_obj_t dest[2]; - mp_load_method_maybe(vfs_obj, MP_QSTR_mount, dest); - if (dest[0] == MP_OBJ_NULL) { - mp_raise_ValueError(MP_ERROR_TEXT("filesystem must provide mount method")); - } + + // Currently, the only supported filesystem is VfsFat. + mp_arg_validate_type(vfs_obj, &mp_fat_vfs_type, MP_QSTR_filesystem); common_hal_storage_mount(vfs_obj, mnt_str, args[ARG_readonly].u_bool); diff --git a/shared-bindings/storage/__init__.h b/shared-bindings/storage/__init__.h index ed3e8d44e4aed..6df604262956a 100644 --- a/shared-bindings/storage/__init__.h +++ b/shared-bindings/storage/__init__.h @@ -16,7 +16,7 @@ void common_hal_storage_umount_path(const char *path); void common_hal_storage_umount_object(mp_obj_t vfs_obj); void common_hal_storage_remount(const char *path, bool readonly, bool disable_concurrent_write_protection); mp_obj_t common_hal_storage_getmount(const char *path); -NORETURN void common_hal_storage_erase_filesystem(bool extended); +MP_NORETURN void common_hal_storage_erase_filesystem(bool extended); bool common_hal_storage_disable_usb_drive(void); bool common_hal_storage_enable_usb_drive(void); diff --git a/shared-bindings/supervisor/Runtime.c b/shared-bindings/supervisor/Runtime.c index 7b497639939f0..20cfd2e82007a 100644 --- a/shared-bindings/supervisor/Runtime.c +++ b/shared-bindings/supervisor/Runtime.c @@ -20,12 +20,12 @@ #include "supervisor/shared/status_leds.h" #include "supervisor/shared/bluetooth/bluetooth.h" -#if CIRCUITPY_DISPLAYIO -#include "shared-bindings/displayio/__init__.h" +#if CIRCUITPY_USB_DEVICE +#include "supervisor/usb.h" #endif -#if CIRCUITPY_TINYUSB -#include "tusb.h" +#if CIRCUITPY_DISPLAYIO +#include "shared-bindings/displayio/__init__.h" #endif static supervisor_run_reason_t _run_reason; @@ -52,7 +52,7 @@ static supervisor_run_reason_t _run_reason; //| """Returns the USB enumeration status (read-only).""" static mp_obj_t supervisor_runtime_get_usb_connected(mp_obj_t self) { #if CIRCUITPY_USB_DEVICE - return mp_obj_new_bool(tud_ready()); + return mp_obj_new_bool(usb_connected()); #else return mp_const_false; #endif diff --git a/shared-bindings/supervisor/__init__.c b/shared-bindings/supervisor/__init__.c index f2fc14c3c9a5d..a0a01f124f786 100644 --- a/shared-bindings/supervisor/__init__.c +++ b/shared-bindings/supervisor/__init__.c @@ -21,6 +21,10 @@ #include "supervisor/usb.h" #endif +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" +#endif + #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/supervisor/__init__.h" #include "shared-bindings/time/__init__.h" @@ -360,6 +364,68 @@ static mp_obj_t supervisor_set_usb_identification(size_t n_args, const mp_obj_t } MP_DEFINE_CONST_FUN_OBJ_KW(supervisor_set_usb_identification_obj, 0, supervisor_set_usb_identification); +//| def get_setting(key: str, default: object=None) -> int | str | bool: +//| """ +//| Get and parse the value for the given ``key`` from the ``/settings.toml`` file. +//| If ``key`` is not found or ``settings.toml`` is not present, return the ``default`` value. +//| +//| :param str key: The setting key to retrieve +//| :return: The setting value as an ``int``, ``str``, or ``bool`` depending on the value in the file +//| +//| :raises ValueError: If the value cannot be parsed as a valid TOML value. +//| +//| The value must be parseable as one of these types: +//| +//| - ``str``: Double-quoted string. +//| The string may include Unicode characters, and ``\\u`` Unicode escapes. Backslash-escaped characters +//| ``\\b``, ``\\r``, ``\\n``, ``\\t``, ``\\v``, ``\\v`` are also allowed. +//| - ``int``: signed or unsigned integer +//| - lower-case boolean words ``true`` and ``false``. +//| The values are returned as Python ``True`` or ``False`` values. +//| +//| Example:: +//| +//| # settings.toml: +//| WIDTH = 42 +//| color = "red" +//| DEBUG = true +//| +//| import supervisor +//| print(supervisor.get_setting("WIDTH")) # prints 42 +//| print(supervisor.get_setting("color")) # prints 'red' +//| print(supervisor.get_setting("DEBUG")) # prints True +//| """ +//| ... +//| +static mp_obj_t supervisor_get_setting(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + #if CIRCUITPY_SETTINGS_TOML + enum { ARG_key, ARG_default }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_key, MP_ARG_REQUIRED | MP_ARG_OBJ }, + { MP_QSTR_default, MP_ARG_OBJ, {.u_rom_obj = mp_const_none} }, + }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, (mp_arg_val_t *)&args); + + const char *key = mp_obj_str_get_str(args[ARG_key].u_obj); + mp_obj_t value; + settings_err_t result = settings_get_obj(key, &value); + + switch (result) { + case SETTINGS_OK: + return value; + case SETTINGS_ERR_NOT_FOUND: + case SETTINGS_ERR_OPEN: + return args[ARG_default].u_obj; + default: + mp_raise_ValueError_varg(MP_ERROR_TEXT("Invalid %q"), MP_QSTR_value); + } + #else + mp_raise_NotImplementedError(NULL); + #endif +} +MP_DEFINE_CONST_FUN_OBJ_KW(supervisor_get_setting_obj, 1, supervisor_get_setting); + static const mp_rom_map_elem_t supervisor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_supervisor) }, { MP_ROM_QSTR(MP_QSTR_runtime), MP_ROM_PTR(&common_hal_supervisor_runtime_obj) }, @@ -373,6 +439,7 @@ static const mp_rom_map_elem_t supervisor_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_set_next_code_file), MP_ROM_PTR(&supervisor_set_next_code_file_obj) }, { MP_ROM_QSTR(MP_QSTR_ticks_ms), MP_ROM_PTR(&supervisor_ticks_ms_obj) }, { MP_ROM_QSTR(MP_QSTR_get_previous_traceback), MP_ROM_PTR(&supervisor_get_previous_traceback_obj) }, + { MP_ROM_QSTR(MP_QSTR_get_setting), MP_ROM_PTR(&supervisor_get_setting_obj) }, { MP_ROM_QSTR(MP_QSTR_reset_terminal), MP_ROM_PTR(&supervisor_reset_terminal_obj) }, { MP_ROM_QSTR(MP_QSTR_set_usb_identification), MP_ROM_PTR(&supervisor_set_usb_identification_obj) }, { MP_ROM_QSTR(MP_QSTR_status_bar), MP_ROM_PTR(&shared_module_supervisor_status_bar_obj) }, diff --git a/shared-bindings/synthio/LFO.c b/shared-bindings/synthio/LFO.c index ee2d67d308903..2dfae22a57774 100644 --- a/shared-bindings/synthio/LFO.c +++ b/shared-bindings/synthio/LFO.c @@ -103,17 +103,17 @@ static mp_obj_t synthio_lfo_make_new(const mp_obj_type_t *type_in, size_t n_args } self->waveform_obj = args[ARG_waveform].u_obj; - mp_obj_t result = MP_OBJ_FROM_PTR(self); - properties_construct_helper(result, lfo_properties + 1, args + 1, MP_ARRAY_SIZE(lfo_properties) - 1); + mp_obj_t self_obj = MP_OBJ_FROM_PTR(self); + properties_construct_helper(self_obj, lfo_properties + 1, args + 1, MP_ARRAY_SIZE(lfo_properties) - 1); // Force computation of the LFO's initial output synthio_global_rate_scale = 0; self->base.last_tick = synthio_global_tick - 1; synthio_block_slot_t slot; - synthio_block_assign_slot(MP_OBJ_FROM_PTR(result), &slot, MP_QSTR_self); + synthio_block_assign_slot(self_obj, &slot, MP_QSTR_self); (void)synthio_block_slot_get(&slot); - return result; + return self_obj; }; //| waveform: Optional[ReadableBuffer] diff --git a/shared-bindings/synthio/Math.c b/shared-bindings/synthio/Math.c index 5e943b44d0fcc..96857fb351373 100644 --- a/shared-bindings/synthio/Math.c +++ b/shared-bindings/synthio/Math.c @@ -157,10 +157,10 @@ static mp_obj_t synthio_math_make_new_common(mp_arg_val_t args[MP_ARRAY_SIZE(mat self->base.last_tick = synthio_global_tick; - mp_obj_t result = MP_OBJ_FROM_PTR(self); - properties_construct_helper(result, math_properties, args, MP_ARRAY_SIZE(math_properties)); + mp_obj_t self_obj = MP_OBJ_FROM_PTR(self); + properties_construct_helper(self_obj, math_properties, args, MP_ARRAY_SIZE(math_properties)); - return result; + return self_obj; }; //| a: BlockInput diff --git a/shared-bindings/synthio/MidiTrack.c b/shared-bindings/synthio/MidiTrack.c index 9add8f1745c83..0304df12a8191 100644 --- a/shared-bindings/synthio/MidiTrack.c +++ b/shared-bindings/synthio/MidiTrack.c @@ -70,7 +70,6 @@ static mp_obj_t synthio_miditrack_make_new(const mp_obj_type_t *type, size_t n_a mp_get_buffer_raise(args[ARG_buffer].u_obj, &bufinfo, MP_BUFFER_READ); synthio_miditrack_obj_t *self = mp_obj_malloc(synthio_miditrack_obj_t, &synthio_miditrack_type); - common_hal_synthio_miditrack_construct(self, (uint8_t *)bufinfo.buf, bufinfo.len, args[ARG_tempo].u_int, @@ -115,6 +114,27 @@ static void check_for_deinit(synthio_miditrack_obj_t *self) { //| """32 bit value that tells how quickly samples are played in Hertz (cycles per second).""" //| +//| tempo: int +//| """Tempo of the streamed events, in MIDI ticks per second.""" +//| +static mp_obj_t synthio_miditrack_obj_get_tempo(mp_obj_t self_in) { + synthio_miditrack_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + return mp_obj_new_int(common_hal_synthio_miditrack_get_tempo(self)); +} +MP_DEFINE_CONST_FUN_OBJ_1(synthio_miditrack_get_tempo_obj, synthio_miditrack_obj_get_tempo); + +static mp_obj_t synthio_miditrack_obj_set_tempo(mp_obj_t self_in, mp_obj_t arg) { + synthio_miditrack_obj_t *self = MP_OBJ_TO_PTR(self_in); + check_for_deinit(self); + common_hal_synthio_miditrack_set_tempo(self, mp_obj_get_int(arg)); + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(synthio_miditrack_set_tempo_obj, synthio_miditrack_obj_set_tempo); +MP_PROPERTY_GETSET(synthio_miditrack_tempo_obj, + (mp_obj_t)&synthio_miditrack_get_tempo_obj, + (mp_obj_t)&synthio_miditrack_set_tempo_obj); + //| error_location: Optional[int] //| """Offset, in bytes within the midi data, of a decoding error""" //| @@ -141,6 +161,7 @@ static const mp_rom_map_elem_t synthio_miditrack_locals_dict_table[] = { // Properties { MP_ROM_QSTR(MP_QSTR_error_location), MP_ROM_PTR(&synthio_miditrack_error_location_obj) }, + { MP_ROM_QSTR(MP_QSTR_tempo), MP_ROM_PTR(&synthio_miditrack_tempo_obj) }, AUDIOSAMPLE_FIELDS, }; static MP_DEFINE_CONST_DICT(synthio_miditrack_locals_dict, synthio_miditrack_locals_dict_table); diff --git a/shared-bindings/synthio/MidiTrack.h b/shared-bindings/synthio/MidiTrack.h index 72b217e9613b1..cb154d3996b1a 100644 --- a/shared-bindings/synthio/MidiTrack.h +++ b/shared-bindings/synthio/MidiTrack.h @@ -15,3 +15,6 @@ void common_hal_synthio_miditrack_construct(synthio_miditrack_obj_t *self, const void common_hal_synthio_miditrack_deinit(synthio_miditrack_obj_t *self); mp_int_t common_hal_synthio_miditrack_get_error_location(synthio_miditrack_obj_t *self); + +mp_int_t common_hal_synthio_miditrack_get_tempo(synthio_miditrack_obj_t *self); +void common_hal_synthio_miditrack_set_tempo(synthio_miditrack_obj_t *self, mp_int_t value); diff --git a/shared-bindings/synthio/Note.c b/shared-bindings/synthio/Note.c index 95dd51fe6b0fc..183c59d5fbd32 100644 --- a/shared-bindings/synthio/Note.c +++ b/shared-bindings/synthio/Note.c @@ -61,11 +61,11 @@ static mp_obj_t synthio_note_make_new(const mp_obj_type_t *type_in, size_t n_arg mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(note_properties), note_properties, args); synthio_note_obj_t *self = mp_obj_malloc(synthio_note_obj_t, &synthio_note_type); + mp_obj_t self_obj = MP_OBJ_FROM_PTR(self); - mp_obj_t result = MP_OBJ_FROM_PTR(self); - properties_construct_helper(result, note_properties, args, MP_ARRAY_SIZE(note_properties)); + properties_construct_helper(self_obj, note_properties, args, MP_ARRAY_SIZE(note_properties)); - return result; + return self_obj; }; //| frequency: float diff --git a/shared-bindings/synthio/Synthesizer.c b/shared-bindings/synthio/Synthesizer.c index fb39e8ef50ede..35cc42a20371f 100644 --- a/shared-bindings/synthio/Synthesizer.c +++ b/shared-bindings/synthio/Synthesizer.c @@ -62,7 +62,6 @@ static mp_obj_t synthio_synthesizer_make_new(const mp_obj_type_t *type, size_t n mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); synthio_synthesizer_obj_t *self = mp_obj_malloc(synthio_synthesizer_obj_t, &synthio_synthesizer_type); - common_hal_synthio_synthesizer_construct(self, args[ARG_sample_rate].u_int, args[ARG_channel_count].u_int, diff --git a/shared-bindings/synthio/__init__.c b/shared-bindings/synthio/__init__.c index 6d42880541df4..6a21834751b63 100644 --- a/shared-bindings/synthio/__init__.c +++ b/shared-bindings/synthio/__init__.c @@ -252,7 +252,6 @@ static mp_obj_t synthio_from_file(size_t n_args, const mp_obj_t *pos_args, mp_ma } synthio_miditrack_obj_t *result = mp_obj_malloc(synthio_miditrack_obj_t, &synthio_miditrack_type); - common_hal_synthio_miditrack_construct(result, buffer, track_size, tempo, args[ARG_sample_rate].u_int, args[ARG_waveform].u_obj, mp_const_none, diff --git a/shared-bindings/terminalio/Terminal.c b/shared-bindings/terminalio/Terminal.c index f8394dcc17d52..72c43ba2dc152 100644 --- a/shared-bindings/terminalio/Terminal.c +++ b/shared-bindings/terminalio/Terminal.c @@ -21,7 +21,10 @@ #endif //| class Terminal: -//| """Display a character stream with a TileGrid +//| """Terminal manages tile indices and cursor position based on VT100 commands. The ``font`` should be +//| a `fontio.BuiltinFont` and the ``scroll_area`` TileGrid's bitmap should match the font's bitmap. +//| +//| Display a character stream with a TileGrid //| //| ASCII control: //| @@ -75,6 +78,52 @@ //| +--------+------------+------------+ //| | White | 37 | 47 | //| +--------+------------+------------+ +//| +//| Example Usage: +//| +//| .. code-block:: python +//| +//| import time +//| import displayio +//| import supervisor +//| from displayio import Group, TileGrid +//| from terminalio import FONT, Terminal +//| +//| main_group = Group() +//| display = supervisor.runtime.display +//| font_bb = FONT.get_bounding_box() +//| screen_size = (display.width // font_bb[0], display.height // font_bb[1]) +//| char_size = FONT.get_bounding_box() +//| +//| palette = displayio.Palette(2) +//| palette[0] = 0x000000 +//| palette[1] = 0xffffff +//| +//| tilegrid = TileGrid( +//| bitmap=FONT.bitmap, width=screen_size[0], height=screen_size[1], +//| tile_width=char_size[0], tile_height=char_size[1], pixel_shader=palette) +//| +//| terminal = Terminal(tilegrid, FONT) +//| +//| main_group.append(tilegrid) +//| display.root_group = main_group +//| +//| message = "Hello World\\n" +//| terminal.write(message) +//| +//| print(terminal.cursor_x, terminal.cursor_y) +//| move_cursor = chr(27) + "[10;10H" +//| terminal.write(f"Moving the cursor\\n{move_cursor} To here") +//| +//| cursor_home = chr(27) + f"[{screen_size[1]};0H" +//| terminal.write(cursor_home) +//| i = 1 +//| while True: +//| terminal.write(f"Writing again {i}\\n") +//| i = i + 1 +//| time.sleep(1) +//| +//| //| """ //| //| def __init__( @@ -84,8 +133,6 @@ //| *, //| status_bar: Optional[displayio.TileGrid] = None, //| ) -> None: -//| """Terminal manages tile indices and cursor position based on VT100 commands. The font should be -//| a `fontio.BuiltinFont` and the TileGrid's bitmap should match the font's bitmap.""" //| ... //| @@ -129,8 +176,8 @@ static mp_obj_t terminalio_terminal_make_new(const mp_obj_type_t *type, size_t n mp_arg_validate_int_min(scroll_area->width_in_tiles * scroll_area->height_in_tiles, 2, MP_QSTR_scroll_area_area); terminalio_terminal_obj_t *self = mp_obj_malloc(terminalio_terminal_obj_t, &terminalio_terminal_type); - common_hal_terminalio_terminal_construct(self, scroll_area, font, status_bar); + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/tilepalettemapper/TilePaletteMapper.c b/shared-bindings/tilepalettemapper/TilePaletteMapper.c index ed3e928957f9f..c22ea47bf2652 100644 --- a/shared-bindings/tilepalettemapper/TilePaletteMapper.c +++ b/shared-bindings/tilepalettemapper/TilePaletteMapper.c @@ -45,7 +45,6 @@ static mp_obj_t tilepalettemapper_tilepalettemapper_make_new(const mp_obj_type_t mp_raise_TypeError_varg(MP_ERROR_TEXT("unsupported %q type"), MP_QSTR_pixel_shader); } - tilepalettemapper_tilepalettemapper_t *self = mp_obj_malloc(tilepalettemapper_tilepalettemapper_t, &tilepalettemapper_tilepalettemapper_type); common_hal_tilepalettemapper_tilepalettemapper_construct(self, pixel_shader, args[ARG_input_color_count].u_int); diff --git a/shared-bindings/time/__init__.c b/shared-bindings/time/__init__.c index 504cd07d90111..e19d97251bcb8 100644 --- a/shared-bindings/time/__init__.c +++ b/shared-bindings/time/__init__.c @@ -22,12 +22,12 @@ //| //| //| def monotonic() -> float: -//| """Returns an always increasing value of time with an unknown reference +//| """Returns an always increasing value of time, in fractional seconds, with an unknown reference //| point. Only use it to compare against other values from `time.monotonic()` //| during the same code run. //| //| On most boards, `time.monotonic()` converts a 64-bit millisecond tick counter -//| to a float. Floats on most boards are encoded in 30 bits internally, with +//| to seconds, as a float. Floats on most boards are encoded in 30 bits internally, with //| effectively 22 bits of precision. The float returned by `time.monotonic()` will //| accurately represent time to millisecond precision only up to 2**22 milliseconds //| (about 1.165 hours). @@ -83,6 +83,16 @@ static mp_obj_t struct_time_make_new(const mp_obj_type_t *type, size_t n_args, s } //| class struct_time: +//| tm_year: int +//| tm_mon: int +//| tm_mday: int +//| tm_hour: int +//| tm_min: int +//| tm_sec: int +//| tm_wday: int +//| tm_yday: int +//| tm_isdst: int +//| //| def __init__(self, time_tuple: Sequence[int]) -> None: //| """Structure used to capture a date and time. Can be constructed from a `struct_time`, `tuple`, `list`, or `namedtuple` with 9 elements. //| @@ -210,7 +220,7 @@ static mp_obj_t time_monotonic_ns(void) { } MP_DEFINE_CONST_FUN_OBJ_0(time_monotonic_ns_obj, time_monotonic_ns); -//| def localtime(secs: int) -> struct_time: +//| def localtime(secs: Optional[int] = None) -> struct_time: //| """Convert a time expressed in seconds since Jan 1, 1970 to a struct_time in //| local time. If secs is not provided or None, the current time as returned //| by time() is used. diff --git a/shared-bindings/touchio/TouchIn.c b/shared-bindings/touchio/TouchIn.c index 19d97bd9d7c56..2250e88f599f1 100644 --- a/shared-bindings/touchio/TouchIn.c +++ b/shared-bindings/touchio/TouchIn.c @@ -56,7 +56,7 @@ static mp_obj_t touchio_touchin_make_new(const mp_obj_type_t *type, touchio_touchin_obj_t *self = mp_obj_malloc(touchio_touchin_obj_t, &touchio_touchin_type); common_hal_touchio_touchin_construct(self, pin, pull); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); } //| def deinit(self) -> None: diff --git a/shared-bindings/usb/core/__init__.c b/shared-bindings/usb/core/__init__.c index 545e182ea99a3..29dab285084ef 100644 --- a/shared-bindings/usb/core/__init__.c +++ b/shared-bindings/usb/core/__init__.c @@ -30,7 +30,7 @@ //| //| MP_DEFINE_USB_CORE_EXCEPTION(USBError, OSError) -NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...) { +MP_NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...) { mp_obj_t exception; if (fmt == NULL) { exception = mp_obj_new_exception(&mp_type_usb_core_USBError); @@ -50,7 +50,7 @@ NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...) { //| //| MP_DEFINE_USB_CORE_EXCEPTION(USBTimeoutError, usb_core_USBError) -NORETURN void mp_raise_usb_core_USBTimeoutError(void) { +MP_NORETURN void mp_raise_usb_core_USBTimeoutError(void) { mp_raise_type(&mp_type_usb_core_USBTimeoutError); } @@ -90,9 +90,10 @@ static mp_obj_t _next_device(usb_core_devices_obj_t *iter) { // We passed the filters. Now make a properly allocated object to // return to the user. usb_core_device_obj_t *self = mp_obj_malloc(usb_core_device_obj_t, &usb_core_device_type); - common_hal_usb_core_device_construct(self, i); + iter->next_index = i + 1; + return MP_OBJ_FROM_PTR(self); } // Iter is done. diff --git a/shared-bindings/usb/core/__init__.h b/shared-bindings/usb/core/__init__.h index 0dc08fae53257..adb8430a6c382 100644 --- a/shared-bindings/usb/core/__init__.h +++ b/shared-bindings/usb/core/__init__.h @@ -25,8 +25,8 @@ void usb_core_exception_print(const mp_print_t *print, mp_obj_t o_in, mp_print_k extern const mp_obj_type_t mp_type_usb_core_USBError; extern const mp_obj_type_t mp_type_usb_core_USBTimeoutError; -NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...); -NORETURN void mp_raise_usb_core_USBTimeoutError(void); +MP_NORETURN void mp_raise_usb_core_USBError(mp_rom_error_text_t fmt, ...); +MP_NORETURN void mp_raise_usb_core_USBTimeoutError(void); // Find is all Python object oriented so we don't need a separate common-hal API // for it. It uses the device common-hal instead. diff --git a/shared-bindings/usb_cdc/Serial.c b/shared-bindings/usb_cdc/Serial.c index 4d6851351afd2..eb526615927bd 100644 --- a/shared-bindings/usb_cdc/Serial.c +++ b/shared-bindings/usb_cdc/Serial.c @@ -194,7 +194,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_reset_output_buffer_obj, usb_cdc_serial static mp_obj_t usb_cdc_serial_get_timeout(mp_obj_t self_in) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_float_t timeout = common_hal_usb_cdc_serial_get_timeout(self); - return (timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->timeout); + return (timeout < 0.0f) ? mp_const_none : mp_obj_new_float(timeout); } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_timeout_obj, usb_cdc_serial_get_timeout); @@ -219,7 +219,7 @@ MP_PROPERTY_GETSET(usb_cdc_serial_timeout_obj, static mp_obj_t usb_cdc_serial_get_write_timeout(mp_obj_t self_in) { usb_cdc_serial_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_float_t write_timeout = common_hal_usb_cdc_serial_get_write_timeout(self); - return (write_timeout < 0.0f) ? mp_const_none : mp_obj_new_float(self->write_timeout); + return (write_timeout < 0.0f) ? mp_const_none : mp_obj_new_float(write_timeout); } MP_DEFINE_CONST_FUN_OBJ_1(usb_cdc_serial_get_write_timeout_obj, usb_cdc_serial_get_write_timeout); diff --git a/shared-bindings/usb_cdc/Serial.h b/shared-bindings/usb_cdc/Serial.h index 30249cef389c1..091f7514a4fb7 100644 --- a/shared-bindings/usb_cdc/Serial.h +++ b/shared-bindings/usb_cdc/Serial.h @@ -6,7 +6,11 @@ #pragma once +#if defined(__ZEPHYR__) && __ZEPHYR__ == 1 +#include "common-hal/usb_cdc/Serial.h" +#else #include "shared-module/usb_cdc/Serial.h" +#endif extern const mp_obj_type_t usb_cdc_serial_type; diff --git a/shared-bindings/usb_cdc/__init__.h b/shared-bindings/usb_cdc/__init__.h index 34099f7e8fd61..154f259f4101d 100644 --- a/shared-bindings/usb_cdc/__init__.h +++ b/shared-bindings/usb_cdc/__init__.h @@ -6,7 +6,11 @@ #pragma once +#if defined(__ZEPHYR__) && __ZEPHYR__ == 1 +#include "common-hal/usb_cdc/__init__.h" +#else #include "shared-module/usb_cdc/__init__.h" +#endif // Set the module dict entries. void usb_cdc_set_console(mp_obj_t serial_obj); diff --git a/shared-bindings/usb_hid/Device.c b/shared-bindings/usb_hid/Device.c index 7c9c354e4de10..66c9c01ad6e43 100644 --- a/shared-bindings/usb_hid/Device.c +++ b/shared-bindings/usb_hid/Device.c @@ -75,7 +75,6 @@ //| static mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - usb_hid_device_obj_t *self = mp_obj_malloc(usb_hid_device_obj_t, &usb_hid_device_type); enum { ARG_report_descriptor, ARG_usage_page, ARG_usage, ARG_report_ids, ARG_in_report_lengths, ARG_out_report_lengths }; static const mp_arg_t allowed_args[] = { { MP_QSTR_report_descriptor, MP_ARG_KW_ONLY | MP_ARG_REQUIRED | MP_ARG_OBJ }, @@ -98,7 +97,7 @@ static mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args const uint16_t usage_page = usage_page_arg; const mp_int_t usage_arg = args[ARG_usage].u_int; - mp_arg_validate_int_range(usage_arg, 1, 0xFFFF, MP_QSTR_usage); + mp_arg_validate_int_range(usage_arg, 0, 0xFFFF, MP_QSTR_usage); const uint16_t usage = usage_arg; mp_obj_t report_ids = args[ARG_report_ids].u_obj; @@ -141,9 +140,10 @@ static mp_obj_t usb_hid_device_make_new(const mp_obj_type_t *type, size_t n_args mp_raise_ValueError_varg(MP_ERROR_TEXT("%q length must be %d"), MP_QSTR_report_id_space_0, 1); } + usb_hid_device_obj_t *self = mp_obj_malloc(usb_hid_device_obj_t, &usb_hid_device_type); common_hal_usb_hid_device_construct( self, descriptor, usage_page, usage, report_ids_count, report_ids_array, in_report_lengths_array, out_report_lengths_array); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); } diff --git a/shared-bindings/usb_host/Port.c b/shared-bindings/usb_host/Port.c index 41e8513a1e8b2..2c61e641a05be 100644 --- a/shared-bindings/usb_host/Port.c +++ b/shared-bindings/usb_host/Port.c @@ -42,7 +42,7 @@ static mp_obj_t usb_host_port_make_new(const mp_obj_type_t *type, usb_host_port_obj_t *self = common_hal_usb_host_port_construct(dp, dm); - return (mp_obj_t)self; + return MP_OBJ_FROM_PTR(self); } static const mp_rom_map_elem_t usb_host_port_locals_dict_table[] = { diff --git a/shared-bindings/util.h b/shared-bindings/util.h index d53e5c6e8da13..11f0a00db71ea 100644 --- a/shared-bindings/util.h +++ b/shared-bindings/util.h @@ -9,7 +9,7 @@ #include "py/mpprint.h" #include "py/runtime.h" -NORETURN void raise_deinited_error(void); +MP_NORETURN void raise_deinited_error(void); void properties_print_helper(const mp_print_t *print, mp_obj_t self_in, const mp_arg_t *properties, size_t n_properties); void properties_construct_helper(mp_obj_t self_in, const mp_arg_t *args, const mp_arg_val_t *vals, size_t n_properties); bool path_exists(const char *path); diff --git a/shared-bindings/vectorio/VectorShape.c b/shared-bindings/vectorio/VectorShape.c index ff29609fc4e91..834f1fbff5083 100644 --- a/shared-bindings/vectorio/VectorShape.c +++ b/shared-bindings/vectorio/VectorShape.c @@ -69,7 +69,7 @@ mp_obj_t vectorio_vector_shape_make_new(const mp_obj_t shape, const mp_obj_t pix } else if (mp_obj_is_type(shape, &vectorio_circle_type)) { common_hal_vectorio_circle_set_on_dirty(self->ishape.shape, on_dirty); } else { - mp_raise_TypeError_varg(MP_ERROR_TEXT("unsupported %q type"), MP_QSTR_shape); + // Already excluded due to previous else-if chain. } return MP_OBJ_FROM_PTR(self); diff --git a/shared-bindings/wifi/Radio.c b/shared-bindings/wifi/Radio.c index a267dea11c1ff..9a7d445fd0473 100644 --- a/shared-bindings/wifi/Radio.c +++ b/shared-bindings/wifi/Radio.c @@ -99,7 +99,7 @@ MP_PROPERTY_GETSET(wifi_radio_enabled_obj, (mp_obj_t)&wifi_radio_get_enabled_obj, (mp_obj_t)&wifi_radio_set_enabled_obj); -//| hostname: Union[str | ReadableBuffer] +//| hostname: Union[str, ReadableBuffer] //| """Hostname for wifi interface. When the hostname is altered after interface started/connected //| the changes would only be reflected once the interface restarts/reconnects.""" static mp_obj_t wifi_radio_get_hostname(mp_obj_t self_in) { @@ -325,8 +325,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(wifi_radio_stop_station_obj, wifi_radio_stop_station); //| def start_ap( //| self, -//| ssid: Union[str | ReadableBuffer], -//| password: Union[str | ReadableBuffer] = b"", +//| ssid: Union[str, ReadableBuffer], +//| password: Union[str, ReadableBuffer] = b"", //| *, //| channel: int = 1, //| authmode: Iterable[AuthMode] = (), @@ -438,11 +438,11 @@ MP_PROPERTY_GETTER(wifi_radio_ap_active_obj, //| def connect( //| self, -//| ssid: Union[str | ReadableBuffer], -//| password: Union[str | ReadableBuffer] = b"", +//| ssid: Union[str, ReadableBuffer], +//| password: Union[str, ReadableBuffer] = b"", //| *, //| channel: int = 0, -//| bssid: Optional[Union[str | ReadableBuffer]] = None, +//| bssid: Optional[Union[str, ReadableBuffer]] = None, //| timeout: Optional[float] = None, //| ) -> None: //| """Connects to the given ssid and waits for an ip address. Reconnections are handled diff --git a/shared-module/audiocore/__init__.c b/shared-module/audiocore/__init__.c index bf80403137996..2ee683d75e1cd 100644 --- a/shared-module/audiocore/__init__.c +++ b/shared-module/audiocore/__init__.c @@ -196,12 +196,12 @@ void audiosample_convert_s16s_u8s(uint8_t *buffer_out, const int16_t *buffer_in, } } -void audiosample_must_match(audiosample_base_t *self, mp_obj_t other_in) { +void audiosample_must_match(audiosample_base_t *self, mp_obj_t other_in, bool allow_mono_to_stereo) { const audiosample_base_t *other = audiosample_check(other_in); if (other->sample_rate != self->sample_rate) { mp_raise_ValueError_varg(MP_ERROR_TEXT("The sample's %q does not match"), MP_QSTR_sample_rate); } - if (other->channel_count != self->channel_count) { + if ((!allow_mono_to_stereo || (allow_mono_to_stereo && self->channel_count != 2)) && other->channel_count != self->channel_count) { mp_raise_ValueError_varg(MP_ERROR_TEXT("The sample's %q does not match"), MP_QSTR_channel_count); } if (other->bits_per_sample != self->bits_per_sample) { diff --git a/shared-module/audiocore/__init__.h b/shared-module/audiocore/__init__.h index 875ac90ecc5c1..920fe752c3822 100644 --- a/shared-module/audiocore/__init__.h +++ b/shared-module/audiocore/__init__.h @@ -89,7 +89,7 @@ static inline void audiosample_get_buffer_structure_checked(mp_obj_t self_in, bo audiosample_get_buffer_structure(audiosample_check(self_in), single_channel_output, single_buffer, samples_signed, max_buffer_length, spacing); } -void audiosample_must_match(audiosample_base_t *self, mp_obj_t other); +void audiosample_must_match(audiosample_base_t *self, mp_obj_t other, bool allow_mono_to_stereo); void audiosample_convert_u8m_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes); void audiosample_convert_u8s_s16s(int16_t *buffer_out, const uint8_t *buffer_in, size_t nframes); diff --git a/shared-module/audiodelays/Chorus.c b/shared-module/audiodelays/Chorus.c index 1609cc5f94684..3c35b902189cd 100644 --- a/shared-module/audiodelays/Chorus.c +++ b/shared-module/audiodelays/Chorus.c @@ -4,6 +4,7 @@ // // SPDX-License-Identifier: MIT #include "shared-bindings/audiodelays/Chorus.h" +#include "shared-bindings/audiocore/__init__.h" #include #include @@ -104,9 +105,7 @@ bool common_hal_audiodelays_chorus_deinited(audiodelays_chorus_obj_t *self) { } void common_hal_audiodelays_chorus_deinit(audiodelays_chorus_obj_t *self) { - if (common_hal_audiodelays_chorus_deinited(self)) { - return; - } + audiosample_mark_deinit(&self->base); self->chorus_buffer = NULL; self->buffer[0] = NULL; self->buffer[1] = NULL; @@ -166,7 +165,7 @@ bool common_hal_audiodelays_chorus_get_playing(audiodelays_chorus_obj_t *self) { } void common_hal_audiodelays_chorus_play(audiodelays_chorus_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiodelays/Echo.c b/shared-module/audiodelays/Echo.c index 78764d9099a1c..0ca5fc8a7502e 100644 --- a/shared-module/audiodelays/Echo.c +++ b/shared-module/audiodelays/Echo.c @@ -204,7 +204,7 @@ bool common_hal_audiodelays_echo_get_playing(audiodelays_echo_obj_t *self) { } void common_hal_audiodelays_echo_play(audiodelays_echo_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiodelays/MultiTapDelay.c b/shared-module/audiodelays/MultiTapDelay.c index 7b702ecf1154a..9c33a5eaee750 100644 --- a/shared-module/audiodelays/MultiTapDelay.c +++ b/shared-module/audiodelays/MultiTapDelay.c @@ -285,7 +285,7 @@ bool common_hal_audiodelays_multi_tap_delay_get_playing(audiodelays_multi_tap_de } void common_hal_audiodelays_multi_tap_delay_play(audiodelays_multi_tap_delay_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiodelays/PitchShift.c b/shared-module/audiodelays/PitchShift.c index ac349da0dd50a..3b8c1a07c7b16 100644 --- a/shared-module/audiodelays/PitchShift.c +++ b/shared-module/audiodelays/PitchShift.c @@ -143,7 +143,7 @@ bool common_hal_audiodelays_pitch_shift_get_playing(audiodelays_pitch_shift_obj_ } void common_hal_audiodelays_pitch_shift_play(audiodelays_pitch_shift_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiofilters/Distortion.c b/shared-module/audiofilters/Distortion.c index ec5fe084e56d3..c4b6b7566ad0d 100644 --- a/shared-module/audiofilters/Distortion.c +++ b/shared-module/audiofilters/Distortion.c @@ -141,7 +141,7 @@ bool common_hal_audiofilters_distortion_get_playing(audiofilters_distortion_obj_ } void common_hal_audiofilters_distortion_play(audiofilters_distortion_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiofilters/Filter.c b/shared-module/audiofilters/Filter.c index 19b567532eb8e..4cedc810abad6 100644 --- a/shared-module/audiofilters/Filter.c +++ b/shared-module/audiofilters/Filter.c @@ -143,7 +143,7 @@ bool common_hal_audiofilters_filter_get_playing(audiofilters_filter_obj_t *self) } void common_hal_audiofilters_filter_play(audiofilters_filter_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiofilters/Phaser.c b/shared-module/audiofilters/Phaser.c index 81d9c0bea3083..01b938c3a0027 100644 --- a/shared-module/audiofilters/Phaser.c +++ b/shared-module/audiofilters/Phaser.c @@ -130,7 +130,7 @@ bool common_hal_audiofilters_phaser_get_playing(audiofilters_phaser_obj_t *self) } void common_hal_audiofilters_phaser_play(audiofilters_phaser_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiofreeverb/Freeverb.c b/shared-module/audiofreeverb/Freeverb.c index d910536013604..b57e82cdca2e5 100644 --- a/shared-module/audiofreeverb/Freeverb.c +++ b/shared-module/audiofreeverb/Freeverb.c @@ -8,6 +8,7 @@ // Fixed point ideas from - Paul Stoffregen in the Teensy audio library https://github.com/PaulStoffregen/Audio/blob/master/effect_freeverb.cpp // #include "shared-bindings/audiofreeverb/Freeverb.h" +#include "shared-bindings/audiocore/__init__.h" #include "shared-module/synthio/__init__.h" #include @@ -124,9 +125,7 @@ bool common_hal_audiofreeverb_freeverb_deinited(audiofreeverb_freeverb_obj_t *se } void common_hal_audiofreeverb_freeverb_deinit(audiofreeverb_freeverb_obj_t *self) { - if (common_hal_audiofreeverb_freeverb_deinited(self)) { - return; - } + audiosample_mark_deinit(&self->base); self->buffer[0] = NULL; self->buffer[1] = NULL; } @@ -195,7 +194,7 @@ bool common_hal_audiofreeverb_freeverb_get_playing(audiofreeverb_freeverb_obj_t } void common_hal_audiofreeverb_freeverb_play(audiofreeverb_freeverb_obj_t *self, mp_obj_t sample, bool loop) { - audiosample_must_match(&self->base, sample); + audiosample_must_match(&self->base, sample, false); self->sample = sample; self->loop = loop; diff --git a/shared-module/audiomixer/Mixer.c b/shared-module/audiomixer/Mixer.c index 2c727a5c0cdde..212686a092ee9 100644 --- a/shared-module/audiomixer/Mixer.c +++ b/shared-module/audiomixer/Mixer.c @@ -92,22 +92,23 @@ static inline uint32_t add16signed(uint32_t a, uint32_t b) { } __attribute__((always_inline)) -static inline uint32_t mult16signed(uint32_t val, int32_t mul) { +static inline uint32_t mult16signed(uint32_t val, int32_t mul[2]) { #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) - mul <<= 16; + mul[0] <<= 16; + mul[1] <<= 16; int32_t hi, lo; enum { bits = 16 }; // saturate to 16 bits enum { shift = 15 }; // shift is done automatically - asm volatile ("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul), "r" (val)); - asm volatile ("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul), "r" (val)); - asm volatile ("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); - asm volatile ("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); - asm volatile ("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack + __asm__ volatile ("smulwb %0, %1, %2" : "=r" (lo) : "r" (mul[0]), "r" (val)); + __asm__ volatile ("smulwt %0, %1, %2" : "=r" (hi) : "r" (mul[1]), "r" (val)); + __asm__ volatile ("ssat %0, %1, %2, asr %3" : "=r" (lo) : "I" (bits), "r" (lo), "I" (shift)); + __asm__ volatile ("ssat %0, %1, %2, asr %3" : "=r" (hi) : "I" (bits), "r" (hi), "I" (shift)); + __asm__ volatile ("pkhbt %0, %1, %2, lsl #16" : "=r" (val) : "r" (lo), "r" (hi)); // pack return val; #else uint32_t result = 0; - float mod_mul = (float)mul / (float)((1 << 15) - 1); for (int8_t i = 0; i < 2; i++) { + float mod_mul = (float)mul[i] / (float)((1 << 15) - 1); int16_t ai = (val >> (sizeof(uint16_t) * 8 * i)); int32_t intermediate = (int32_t)(ai * mod_mul); if (intermediate > SHRT_MAX) { @@ -154,9 +155,32 @@ static inline uint32_t pack8(uint32_t val) { return ((val & 0xff000000) >> 16) | ((val & 0xff00) >> 8); } +static inline uint32_t copy16lsb(uint32_t val) { + val &= 0x0000ffff; + return val | (val << 16); +} + +static inline uint32_t copy16msb(uint32_t val) { + val &= 0xffff0000; + return val | (val >> 16); +} + +static inline uint32_t copy8lsb(uint32_t val) { + val &= 0x00ff; + return val | (val << 8); +} + +static inline uint32_t copy8msb(uint32_t val) { + val &= 0xff00; + return val | (val >> 8); +} + +#define ALMOST_ONE (MICROPY_FLOAT_CONST(32767.) / 32768) + static void mix_down_one_voice(audiomixer_mixer_obj_t *self, audiomixer_mixervoice_obj_t *voice, bool voices_active, uint32_t *word_buffer, uint32_t length) { + audiosample_base_t *sample = MP_OBJ_TO_PTR(voice->sample); while (length != 0) { if (voice->buffer_length == 0) { if (!voice->more_data) { @@ -179,75 +203,163 @@ static void mix_down_one_voice(audiomixer_mixer_obj_t *self, uint32_t *src = voice->remaining_buffer; #if CIRCUITPY_SYNTHIO - uint32_t n = MIN(MIN(voice->buffer_length, length), SYNTHIO_MAX_DUR * self->base.channel_count); + uint32_t n; + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + n = MIN(MIN(voice->buffer_length, length), SYNTHIO_MAX_DUR * self->base.channel_count); + } else { + n = MIN(MIN(voice->buffer_length << 1, length), SYNTHIO_MAX_DUR * self->base.channel_count); + } // Get the current level from the BlockInput. These may change at run time so you need to do bounds checking if required. shared_bindings_synthio_lfo_tick(self->base.sample_rate, n / self->base.channel_count); uint16_t level = (uint16_t)(synthio_block_slot_get_limited(&voice->level, MICROPY_FLOAT_CONST(0.0), MICROPY_FLOAT_CONST(1.0)) * (1 << 15)); + int16_t panning = synthio_block_slot_get_scaled(&voice->panning, -ALMOST_ONE, ALMOST_ONE); #else - uint32_t n = MIN(voice->buffer_length, length); + uint32_t n; + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + n = MIN(voice->buffer_length, length); + } else { + n = MIN(voice->buffer_length << 1, length); + } uint16_t level = voice->level; + int16_t panning = voice->panning; #endif + uint16_t left_panning_scaled = 32768, right_panning_scaled = 32768; + if (MP_LIKELY(self->base.channel_count == 2)) { + if (panning >= 0) { + right_panning_scaled = 32767 - panning; + } else { + left_panning_scaled = 32767 + panning; + } + } + + int32_t loudness[2] = { level, level }; + if (MP_LIKELY(self->base.channel_count == 2)) { + loudness[0] = (left_panning_scaled * loudness[0]) >> 15; + loudness[1] = (right_panning_scaled * loudness[1]) >> 15; + } + // First active voice gets copied over verbatim. if (!voices_active) { if (MP_LIKELY(self->base.bits_per_sample == 16)) { if (MP_LIKELY(self->base.samples_signed)) { - for (uint32_t i = 0; i < n; i++) { - uint32_t v = src[i]; - word_buffer[i] = mult16signed(v, level); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n; i++) { + uint32_t v = src[i]; + word_buffer[i] = mult16signed(v, loudness); + } + } else { + for (uint32_t i = 0; i < n; i += 2) { + uint32_t v = src[i >> 1]; + word_buffer[i] = mult16signed(copy16lsb(v), loudness); + word_buffer[i + 1] = mult16signed(copy16msb(v), loudness); + } } } else { - for (uint32_t i = 0; i < n; i++) { - uint32_t v = src[i]; - v = tosigned16(v); - word_buffer[i] = mult16signed(v, level); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n; i++) { + uint32_t v = src[i]; + v = tosigned16(v); + word_buffer[i] = mult16signed(v, loudness); + } + } else { + for (uint32_t i = 0; i + 1 < n; i += 2) { + uint32_t v = src[i >> 1]; + v = tosigned16(v); + word_buffer[i] = mult16signed(copy16lsb(v), loudness); + word_buffer[i + 1] = mult16signed(copy16msb(v), loudness); + } } } } else { uint16_t *hword_buffer = (uint16_t *)word_buffer; uint16_t *hsrc = (uint16_t *)src; - for (uint32_t i = 0; i < n * 2; i++) { - uint32_t word = unpack8(hsrc[i]); - if (MP_LIKELY(!self->base.samples_signed)) { - word = tosigned16(word); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n * 2; i++) { + uint32_t word = unpack8(hsrc[i]); + if (MP_LIKELY(!self->base.samples_signed)) { + word = tosigned16(word); + } + word = mult16signed(word, loudness); + hword_buffer[i] = pack8(word); + } + } else { + for (uint32_t i = 0; i + 1 < n * 2; i += 2) { + uint32_t word = unpack8(hsrc[i >> 1]); + if (MP_LIKELY(!self->base.samples_signed)) { + word = tosigned16(word); + } + hword_buffer[i] = pack8(mult16signed(copy16lsb(word), loudness)); + hword_buffer[i + 1] = pack8(mult16signed(copy16msb(word), loudness)); } - word = mult16signed(word, level); - hword_buffer[i] = pack8(word); } } } else { if (MP_LIKELY(self->base.bits_per_sample == 16)) { if (MP_LIKELY(self->base.samples_signed)) { - for (uint32_t i = 0; i < n; i++) { - uint32_t word = src[i]; - word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n; i++) { + uint32_t word = src[i]; + word_buffer[i] = add16signed(mult16signed(word, loudness), word_buffer[i]); + } + } else { + for (uint32_t i = 0; i + 1 < n; i += 2) { + uint32_t word = src[i >> 1]; + word_buffer[i] = add16signed(mult16signed(copy16lsb(word), loudness), word_buffer[i]); + word_buffer[i + 1] = add16signed(mult16signed(copy16msb(word), loudness), word_buffer[i + 1]); + } } } else { - for (uint32_t i = 0; i < n; i++) { - uint32_t word = src[i]; - word = tosigned16(word); - word_buffer[i] = add16signed(mult16signed(word, level), word_buffer[i]); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n; i++) { + uint32_t word = src[i]; + word = tosigned16(word); + word_buffer[i] = add16signed(mult16signed(word, loudness), word_buffer[i]); + } + } else { + for (uint32_t i = 0; i + 1 < n; i += 2) { + uint32_t word = src[i >> 1]; + word = tosigned16(word); + word_buffer[i] = add16signed(mult16signed(copy16lsb(word), loudness), word_buffer[i]); + word_buffer[i + 1] = add16signed(mult16signed(copy16msb(word), loudness), word_buffer[i + 1]); + } } } } else { uint16_t *hword_buffer = (uint16_t *)word_buffer; uint16_t *hsrc = (uint16_t *)src; - for (uint32_t i = 0; i < n * 2; i++) { - uint32_t word = unpack8(hsrc[i]); - if (MP_LIKELY(!self->base.samples_signed)) { - word = tosigned16(word); + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + for (uint32_t i = 0; i < n * 2; i++) { + uint32_t word = unpack8(hsrc[i]); + if (MP_LIKELY(!self->base.samples_signed)) { + word = tosigned16(word); + } + word = mult16signed(word, loudness); + word = add16signed(word, unpack8(hword_buffer[i])); + hword_buffer[i] = pack8(word); + } + } else { + for (uint32_t i = 0; i + 1 < n * 2; i += 2) { + uint32_t word = unpack8(hsrc[i >> 1]); + if (MP_LIKELY(!self->base.samples_signed)) { + word = tosigned16(word); + } + hword_buffer[i] = pack8(add16signed(mult16signed(copy16lsb(word), loudness), unpack8(hword_buffer[i]))); + hword_buffer[i + 1] = pack8(add16signed(mult16signed(copy16msb(word), loudness), unpack8(hword_buffer[i + 1]))); } - word = mult16signed(word, level); - word = add16signed(word, unpack8(hword_buffer[i])); - hword_buffer[i] = pack8(word); } } } length -= n; word_buffer += n; - voice->remaining_buffer += n; - voice->buffer_length -= n; + if (MP_LIKELY(self->base.channel_count == sample->channel_count)) { + voice->remaining_buffer += n; + voice->buffer_length -= n; + } else { + voice->remaining_buffer += n >> 1; + voice->buffer_length -= n >> 1; + } } if (length && !voices_active) { diff --git a/shared-module/audiomixer/MixerVoice.c b/shared-module/audiomixer/MixerVoice.c index e0be869aa8e44..6c23a39215cbc 100644 --- a/shared-module/audiomixer/MixerVoice.c +++ b/shared-module/audiomixer/MixerVoice.c @@ -16,6 +16,7 @@ void common_hal_audiomixer_mixervoice_construct(audiomixer_mixervoice_obj_t *self) { self->sample = NULL; common_hal_audiomixer_mixervoice_set_level(self, mp_obj_new_float(1.0)); + common_hal_audiomixer_mixervoice_set_panning(self, mp_obj_new_float(0.0)); } void common_hal_audiomixer_mixervoice_set_parent(audiomixer_mixervoice_obj_t *self, audiomixer_mixer_obj_t *parent) { @@ -38,6 +39,22 @@ void common_hal_audiomixer_mixervoice_set_level(audiomixer_mixervoice_obj_t *sel #endif } +mp_obj_t common_hal_audiomixer_mixervoice_get_panning(audiomixer_mixervoice_obj_t *self) { + #if CIRCUITPY_SYNTHIO + return self->panning.obj; + #else + return mp_obj_new_float((mp_float_t)self->panning / ((1 << 15) - 1)); + #endif +} + +void common_hal_audiomixer_mixervoice_set_panning(audiomixer_mixervoice_obj_t *self, mp_obj_t arg) { + #if CIRCUITPY_SYNTHIO + synthio_block_assign_slot(arg, &self->panning, MP_QSTR_panning); + #else + self->panning = (uint16_t)(mp_arg_validate_obj_float_range(arg, -1, 1, MP_QSTR_panning) * ((1 << 15) - 1)); + #endif +} + bool common_hal_audiomixer_mixervoice_get_loop(audiomixer_mixervoice_obj_t *self) { return self->loop; } @@ -47,7 +64,7 @@ void common_hal_audiomixer_mixervoice_set_loop(audiomixer_mixervoice_obj_t *self } void common_hal_audiomixer_mixervoice_play(audiomixer_mixervoice_obj_t *self, mp_obj_t sample_in, bool loop) { - audiosample_must_match(&self->parent->base, sample_in); + audiosample_must_match(&self->parent->base, sample_in, true); // cast is safe, checked by must_match audiosample_base_t *sample = MP_OBJ_TO_PTR(sample_in); self->sample = sample; diff --git a/shared-module/audiomixer/MixerVoice.h b/shared-module/audiomixer/MixerVoice.h index dd9095515459a..75e7d47faa33d 100644 --- a/shared-module/audiomixer/MixerVoice.h +++ b/shared-module/audiomixer/MixerVoice.h @@ -23,7 +23,9 @@ typedef struct { uint32_t buffer_length; #if CIRCUITPY_SYNTHIO synthio_block_slot_t level; + synthio_block_slot_t panning; #else uint16_t level; + int16_t panning; #endif } audiomixer_mixervoice_obj_t; diff --git a/shared-module/audiospeed/SpeedChanger.c b/shared-module/audiospeed/SpeedChanger.c new file mode 100644 index 0000000000000..e76bed1f17c39 --- /dev/null +++ b/shared-module/audiospeed/SpeedChanger.c @@ -0,0 +1,175 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/audiospeed/SpeedChanger.h" + +#include +#include "py/runtime.h" +#include "py/gc.h" + +#include "shared-module/audiocore/WaveFile.h" +#include "shared-bindings/audiocore/__init__.h" + +#define OUTPUT_BUFFER_FRAMES 128 + +void common_hal_audiospeed_speedchanger_construct(audiospeed_speedchanger_obj_t *self, + mp_obj_t source, uint32_t rate_fp) { + audiosample_base_t *src_base = audiosample_check(source); + + self->source = source; + self->rate_fp = rate_fp; + self->phase = 0; + self->src_buffer = NULL; + self->src_buffer_length = 0; + self->src_sample_count = 0; + self->source_done = false; + self->source_exhausted = false; + + // Copy format from source + self->base.sample_rate = src_base->sample_rate; + self->base.channel_count = src_base->channel_count; + self->base.bits_per_sample = src_base->bits_per_sample; + self->base.samples_signed = src_base->samples_signed; + self->base.single_buffer = true; + + uint8_t bytes_per_frame = (src_base->bits_per_sample / 8) * src_base->channel_count; + self->output_buffer_length = OUTPUT_BUFFER_FRAMES * bytes_per_frame; + self->base.max_buffer_length = self->output_buffer_length; + + self->output_buffer = m_malloc_without_collect(self->output_buffer_length); + if (self->output_buffer == NULL) { + m_malloc_fail(self->output_buffer_length); + } +} + +void common_hal_audiospeed_speedchanger_deinit(audiospeed_speedchanger_obj_t *self) { + self->output_buffer = NULL; + self->source = MP_OBJ_NULL; + audiosample_mark_deinit(&self->base); +} + +void common_hal_audiospeed_speedchanger_set_rate(audiospeed_speedchanger_obj_t *self, uint32_t rate_fp) { + self->rate_fp = rate_fp; +} + +uint32_t common_hal_audiospeed_speedchanger_get_rate(audiospeed_speedchanger_obj_t *self) { + return self->rate_fp; +} + +// Fetch the next buffer from the source. Returns false if no data available. +static bool fetch_source_buffer(audiospeed_speedchanger_obj_t *self) { + if (self->source_exhausted) { + return false; + } + uint8_t *buf = NULL; + uint32_t len = 0; + audioio_get_buffer_result_t result = audiosample_get_buffer(self->source, false, 0, &buf, &len); + if (result == GET_BUFFER_ERROR) { + self->source_exhausted = true; + return false; + } + if (len == 0) { + self->source_exhausted = true; + return false; + } + self->src_buffer = buf; + self->src_buffer_length = len; + uint8_t bytes_per_frame = (self->base.bits_per_sample / 8) * self->base.channel_count; + self->src_sample_count = len / bytes_per_frame; + self->source_done = (result == GET_BUFFER_DONE); + // Reset phase to index within this new buffer + self->phase = 0; + return true; +} + +void audiospeed_speedchanger_reset_buffer(audiospeed_speedchanger_obj_t *self, + bool single_channel_output, uint8_t channel) { + if (single_channel_output && channel == 1) { + return; + } + audiosample_reset_buffer(self->source, false, 0); + self->phase = 0; + self->src_buffer = NULL; + self->src_buffer_length = 0; + self->src_sample_count = 0; + self->source_done = false; + self->source_exhausted = false; +} + +audioio_get_buffer_result_t audiospeed_speedchanger_get_buffer(audiospeed_speedchanger_obj_t *self, + bool single_channel_output, uint8_t channel, + uint8_t **buffer, uint32_t *buffer_length) { + + // Ensure we have a source buffer + if (self->src_buffer == NULL) { + if (!fetch_source_buffer(self)) { + *buffer = NULL; + *buffer_length = 0; + return GET_BUFFER_DONE; + } + } + + uint8_t bytes_per_sample = self->base.bits_per_sample / 8; + uint8_t channels = self->base.channel_count; + uint8_t bytes_per_frame = bytes_per_sample * channels; + uint32_t out_frames = 0; + uint32_t max_out_frames = self->output_buffer_length / bytes_per_frame; + + if (bytes_per_sample == 1) { + // 8-bit samples + uint8_t *out = self->output_buffer; + while (out_frames < max_out_frames) { + uint32_t src_index = self->phase >> SPEED_SHIFT; + // Advance to next source buffer if needed + if (src_index >= self->src_sample_count) { + if (self->source_done) { + self->source_exhausted = true; + break; + } + if (!fetch_source_buffer(self)) { + break; + } + src_index = 0; // phase was reset by fetch + } + uint8_t *src = self->src_buffer + src_index * bytes_per_frame; + for (uint8_t c = 0; c < channels; c++) { + *out++ = src[c]; + } + out_frames++; + self->phase += self->rate_fp; + } + } else { + // 16-bit samples + int16_t *out = (int16_t *)self->output_buffer; + while (out_frames < max_out_frames) { + uint32_t src_index = self->phase >> SPEED_SHIFT; + if (src_index >= self->src_sample_count) { + if (self->source_done) { + self->source_exhausted = true; + break; + } + if (!fetch_source_buffer(self)) { + break; + } + src_index = 0; + } + int16_t *src = (int16_t *)(self->src_buffer + src_index * bytes_per_frame); + for (uint8_t c = 0; c < channels; c++) { + *out++ = src[c]; + } + out_frames++; + self->phase += self->rate_fp; + } + } + + *buffer = self->output_buffer; + *buffer_length = out_frames * bytes_per_frame; + + if (out_frames == 0) { + return GET_BUFFER_DONE; + } + return self->source_exhausted ? GET_BUFFER_DONE : GET_BUFFER_MORE_DATA; +} diff --git a/shared-module/audiospeed/SpeedChanger.h b/shared-module/audiospeed/SpeedChanger.h new file mode 100644 index 0000000000000..e920c72caf461 --- /dev/null +++ b/shared-module/audiospeed/SpeedChanger.h @@ -0,0 +1,35 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" +#include "shared-module/audiocore/__init__.h" + +// Fixed-point 16.16 format +#define SPEED_SHIFT 16 + +typedef struct { + audiosample_base_t base; + mp_obj_t source; + uint8_t *output_buffer; + uint32_t output_buffer_length; // in bytes, allocated size + // Source buffer cache + uint8_t *src_buffer; + uint32_t src_buffer_length; // in bytes + uint32_t src_sample_count; // in frames + // Phase accumulator and rate in 16.16 fixed-point (units: source frames) + uint32_t phase; + uint32_t rate_fp; // 16.16 fixed-point rate + bool source_done; // source returned DONE on last get_buffer + bool source_exhausted; // source DONE and we consumed all of it +} audiospeed_speedchanger_obj_t; + +void audiospeed_speedchanger_reset_buffer(audiospeed_speedchanger_obj_t *self, + bool single_channel_output, uint8_t channel); +audioio_get_buffer_result_t audiospeed_speedchanger_get_buffer(audiospeed_speedchanger_obj_t *self, + bool single_channel_output, uint8_t channel, + uint8_t **buffer, uint32_t *buffer_length); diff --git a/shared-module/audiospeed/__init__.c b/shared-module/audiospeed/__init__.c new file mode 100644 index 0000000000000..7c9b271a4b501 --- /dev/null +++ b/shared-module/audiospeed/__init__.c @@ -0,0 +1,5 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT diff --git a/shared-module/audiospeed/__init__.h b/shared-module/audiospeed/__init__.h new file mode 100644 index 0000000000000..c4a52e5819d12 --- /dev/null +++ b/shared-module/audiospeed/__init__.h @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Tod Kurt +// +// SPDX-License-Identifier: MIT + +#pragma once diff --git a/shared-module/bitbangio/I2C.c b/shared-module/bitbangio/I2C.c index 0089bdc1e1e1e..d88455729e28b 100644 --- a/shared-module/bitbangio/I2C.c +++ b/shared-module/bitbangio/I2C.c @@ -8,10 +8,15 @@ #include "py/mperrno.h" #include "py/obj.h" #include "py/runtime.h" +#include "py/gc.h" #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" +#include "shared-bindings/util.h" +#include "supervisor/port.h" static void delay(bitbangio_i2c_obj_t *self) { // We need to use an accurate delay to get acceptable I2C @@ -19,61 +24,81 @@ static void delay(bitbangio_i2c_obj_t *self) { common_hal_mcu_delay_us(self->us_delay); } -static void scl_low(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_set_value(&self->scl, false); +static bool scl_low(bitbangio_i2c_obj_t *self) { + return digitalinout_protocol_set_value(self->scl, false) == 0; } -static void scl_release(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_set_value(&self->scl, true); +static bool scl_release(bitbangio_i2c_obj_t *self) { + if (digitalinout_protocol_set_value(self->scl, true) != 0) { + return false; + } uint32_t count = self->us_timeout; delay(self); // For clock stretching, wait for the SCL pin to be released, with timeout. - common_hal_digitalio_digitalinout_switch_to_input(&self->scl, PULL_UP); - for (; !common_hal_digitalio_digitalinout_get_value(&self->scl) && count; --count) { + digitalinout_protocol_switch_to_input(self->scl, PULL_UP); + bool value; + for (; count; --count) { + if (digitalinout_protocol_get_value(self->scl, &value) != 0) { + return false; + } + if (value) { + break; + } common_hal_mcu_delay_us(1); } - common_hal_digitalio_digitalinout_switch_to_output(&self->scl, true, DRIVE_MODE_OPEN_DRAIN); + digitalinout_protocol_switch_to_output(self->scl, true, DRIVE_MODE_OPEN_DRAIN); // raise exception on timeout if (count == 0) { mp_raise_msg_varg(&mp_type_TimeoutError, MP_ERROR_TEXT("%q too long"), MP_QSTR_timeout); } + return true; } -static void sda_low(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_set_value(&self->sda, false); +static bool sda_low(bitbangio_i2c_obj_t *self) { + return digitalinout_protocol_set_value(self->sda, false) == 0; } -static void sda_release(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_set_value(&self->sda, true); +static bool sda_release(bitbangio_i2c_obj_t *self) { + return digitalinout_protocol_set_value(self->sda, true) == 0; } -static bool sda_read(bitbangio_i2c_obj_t *self) { - common_hal_digitalio_digitalinout_switch_to_input(&self->sda, PULL_UP); - bool value = common_hal_digitalio_digitalinout_get_value(&self->sda); - common_hal_digitalio_digitalinout_switch_to_output(&self->sda, true, DRIVE_MODE_OPEN_DRAIN); - return value; +static bool sda_read(bitbangio_i2c_obj_t *self, bool *value) { + digitalinout_protocol_switch_to_input(self->sda, PULL_UP); + if (digitalinout_protocol_get_value(self->sda, value) != 0) { + return false; + } + digitalinout_protocol_switch_to_output(self->sda, true, DRIVE_MODE_OPEN_DRAIN); + return true; } -static void start(bitbangio_i2c_obj_t *self) { - sda_release(self); +static bool start(bitbangio_i2c_obj_t *self) { + if (!sda_release(self)) { + return false; + } delay(self); scl_release(self); sda_low(self); delay(self); + return true; } -static void stop(bitbangio_i2c_obj_t *self) { +static bool stop(bitbangio_i2c_obj_t *self) { delay(self); - sda_low(self); + if (!sda_low(self)) { + return false; + } delay(self); scl_release(self); sda_release(self); delay(self); + return true; } static int write_byte(bitbangio_i2c_obj_t *self, uint8_t val) { delay(self); - scl_low(self); + if (!scl_low(self)) { + return -1; + } for (int i = 7; i >= 0; i--) { if ((val >> i) & 1) { @@ -90,7 +115,10 @@ static int write_byte(bitbangio_i2c_obj_t *self, uint8_t val) { delay(self); scl_release(self); - int ret = sda_read(self); + bool ret; + if (!sda_read(self, &ret)) { + return -1; + } delay(self); scl_low(self); @@ -99,13 +127,17 @@ static int write_byte(bitbangio_i2c_obj_t *self, uint8_t val) { static bool read_byte(bitbangio_i2c_obj_t *self, uint8_t *val, bool ack) { delay(self); - scl_low(self); + if (!scl_low(self)) { + return false; + } delay(self); uint8_t data = 0; for (int i = 7; i >= 0; i--) { scl_release(self); - data = (data << 1) | sda_read(self); + bool bit; + sda_read(self, &bit); + data = (data << 1) | bit; scl_low(self); delay(self); } @@ -124,8 +156,8 @@ static bool read_byte(bitbangio_i2c_obj_t *self, uint8_t *val, bool ack) { } void shared_module_bitbangio_i2c_construct(bitbangio_i2c_obj_t *self, - const mcu_pin_obj_t *scl, - const mcu_pin_obj_t *sda, + mp_obj_t scl, + mp_obj_t sda, uint32_t frequency, uint32_t us_timeout) { @@ -134,32 +166,42 @@ void shared_module_bitbangio_i2c_construct(bitbangio_i2c_obj_t *self, if (self->us_delay == 0) { self->us_delay = 1; } - digitalinout_result_t result = common_hal_digitalio_digitalinout_construct(&self->scl, scl); - if (result != DIGITALINOUT_OK) { - return; - } - result = common_hal_digitalio_digitalinout_construct(&self->sda, sda); - if (result != DIGITALINOUT_OK) { - common_hal_digitalio_digitalinout_deinit(&self->scl); - return; - } - common_hal_digitalio_digitalinout_switch_to_output(&self->scl, true, DRIVE_MODE_OPEN_DRAIN); - common_hal_digitalio_digitalinout_switch_to_output(&self->sda, true, DRIVE_MODE_OPEN_DRAIN); - stop(self); + // Allocate the pins in the same place as self. + bool use_port_allocation = !gc_alloc_possible() || !gc_ptr_on_heap(self); + + // Convert scl from Pin to DigitalInOutProtocol + self->scl = digitalinout_protocol_from_pin(scl, MP_QSTR_scl, false, use_port_allocation, &self->own_scl); + + // Convert sda from Pin to DigitalInOutProtocol + self->sda = digitalinout_protocol_from_pin(sda, MP_QSTR_sda, false, use_port_allocation, &self->own_sda); + + digitalinout_protocol_switch_to_output(self->scl, true, DRIVE_MODE_OPEN_DRAIN); + digitalinout_protocol_switch_to_output(self->sda, true, DRIVE_MODE_OPEN_DRAIN); + + if (!stop(self)) { + mp_raise_OSError(MP_EIO); + } } bool shared_module_bitbangio_i2c_deinited(bitbangio_i2c_obj_t *self) { // If one is deinited, both will be. - return common_hal_digitalio_digitalinout_deinited(&self->scl); + return digitalinout_protocol_deinited(self->scl); } void shared_module_bitbangio_i2c_deinit(bitbangio_i2c_obj_t *self) { if (shared_module_bitbangio_i2c_deinited(self)) { return; } - common_hal_digitalio_digitalinout_deinit(&self->scl); - common_hal_digitalio_digitalinout_deinit(&self->sda); + // Only deinit and free the pins if we own them + if (self->own_scl) { + digitalinout_protocol_deinit(self->scl); + circuitpy_free_obj(self->scl); + } + if (self->own_sda) { + digitalinout_protocol_deinit(self->sda); + circuitpy_free_obj(self->sda); + } } bool shared_module_bitbangio_i2c_try_lock(bitbangio_i2c_obj_t *self) { @@ -182,18 +224,28 @@ void shared_module_bitbangio_i2c_unlock(bitbangio_i2c_obj_t *self) { } bool shared_module_bitbangio_i2c_probe(bitbangio_i2c_obj_t *self, uint8_t addr) { - start(self); - bool ok = write_byte(self, addr << 1); + if (!start(self)) { + mp_raise_OSError(MP_EIO); + } + int result = write_byte(self, addr << 1); stop(self); - return ok; + if (result < 0) { + mp_raise_OSError(MP_EIO); + } + return result; } uint8_t shared_module_bitbangio_i2c_write(bitbangio_i2c_obj_t *self, uint16_t addr, const uint8_t *data, size_t len, bool transmit_stop_bit) { // start the I2C transaction - start(self); + if (!start(self)) { + return MP_EIO; + } uint8_t status = 0; - if (!write_byte(self, addr << 1)) { + int result = write_byte(self, addr << 1); + if (result < 0) { + status = MP_EIO; + } else if (!result) { status = MP_ENODEV; } @@ -215,7 +267,9 @@ uint8_t shared_module_bitbangio_i2c_write(bitbangio_i2c_obj_t *self, uint16_t ad uint8_t shared_module_bitbangio_i2c_read(bitbangio_i2c_obj_t *self, uint16_t addr, uint8_t *data, size_t len) { // start the I2C transaction - start(self); + if (!start(self)) { + return MP_EIO; + } uint8_t status = 0; if (!write_byte(self, (addr << 1) | 1)) { status = MP_ENODEV; diff --git a/shared-module/bitbangio/I2C.h b/shared-module/bitbangio/I2C.h index 3908a3dd3740e..0cec9186e9b9d 100644 --- a/shared-module/bitbangio/I2C.h +++ b/shared-module/bitbangio/I2C.h @@ -12,9 +12,11 @@ typedef struct { mp_obj_base_t base; - digitalio_digitalinout_obj_t scl; - digitalio_digitalinout_obj_t sda; + mp_obj_t scl; + mp_obj_t sda; uint32_t us_delay; uint32_t us_timeout; volatile bool locked; + bool own_scl; + bool own_sda; } bitbangio_i2c_obj_t; diff --git a/shared-module/bitbangio/SPI.c b/shared-module/bitbangio/SPI.c index 56e978985edcc..9b89d1d4a1b05 100644 --- a/shared-module/bitbangio/SPI.c +++ b/shared-module/bitbangio/SPI.c @@ -7,64 +7,66 @@ #include "py/mpconfig.h" #include "py/obj.h" #include "py/runtime.h" +#include "py/gc.h" #include "common-hal/microcontroller/Pin.h" #include "shared-bindings/bitbangio/SPI.h" #include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" #include "shared-bindings/microcontroller/__init__.h" +#include "shared-bindings/microcontroller/Pin.h" +#include "shared-bindings/util.h" +#include "supervisor/port.h" #define MAX_BAUDRATE (common_hal_mcu_get_clock_frequency() / 48) void shared_module_bitbangio_spi_construct(bitbangio_spi_obj_t *self, - const mcu_pin_obj_t *clock, const mcu_pin_obj_t *mosi, - const mcu_pin_obj_t *miso) { - digitalinout_result_t result = common_hal_digitalio_digitalinout_construct(&self->clock, clock); - if (result != DIGITALINOUT_OK) { - mp_raise_ValueError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_clock); - } - common_hal_digitalio_digitalinout_switch_to_output(&self->clock, self->polarity == 1, DRIVE_MODE_PUSH_PULL); + mp_obj_t clock, mp_obj_t mosi, mp_obj_t miso) { - if (mosi != NULL) { - result = common_hal_digitalio_digitalinout_construct(&self->mosi, mosi); - if (result != DIGITALINOUT_OK) { - common_hal_digitalio_digitalinout_deinit(&self->clock); - mp_raise_ValueError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_mosi); - } - self->has_mosi = true; - common_hal_digitalio_digitalinout_switch_to_output(&self->mosi, false, DRIVE_MODE_PUSH_PULL); - } + // Allocate the pins in the same place as self. + bool use_port_allocation = !gc_alloc_possible() || !gc_ptr_on_heap(self); - if (miso != NULL) { - // Starts out as input by default, no need to change. - result = common_hal_digitalio_digitalinout_construct(&self->miso, miso); - if (result != DIGITALINOUT_OK) { - common_hal_digitalio_digitalinout_deinit(&self->clock); - if (mosi != NULL) { - common_hal_digitalio_digitalinout_deinit(&self->mosi); - } - mp_raise_ValueError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_miso); - } - self->has_miso = true; + // Convert clock from Pin to DigitalInOutProtocol + self->clock = digitalinout_protocol_from_pin(clock, MP_QSTR_clock, false, use_port_allocation, &self->own_clock); + digitalinout_protocol_switch_to_output(self->clock, self->polarity == 1, DRIVE_MODE_PUSH_PULL); + + // Convert mosi from Pin to DigitalInOutProtocol (optional) + self->mosi = digitalinout_protocol_from_pin(mosi, MP_QSTR_mosi, true, use_port_allocation, &self->own_mosi); + self->has_mosi = (self->mosi != mp_const_none); + if (self->has_mosi) { + digitalinout_protocol_switch_to_output(self->mosi, false, DRIVE_MODE_PUSH_PULL); } + + // Convert miso from Pin to DigitalInOutProtocol (optional) + self->miso = digitalinout_protocol_from_pin(miso, MP_QSTR_miso, true, use_port_allocation, &self->own_miso); + self->has_miso = (self->miso != mp_const_none); + // MISO starts out as input by default, no need to change + self->delay_half = 5; self->polarity = 0; self->phase = 0; } bool shared_module_bitbangio_spi_deinited(bitbangio_spi_obj_t *self) { - return common_hal_digitalio_digitalinout_deinited(&self->clock); + return digitalinout_protocol_deinited(self->clock); } void shared_module_bitbangio_spi_deinit(bitbangio_spi_obj_t *self) { if (shared_module_bitbangio_spi_deinited(self)) { return; } - common_hal_digitalio_digitalinout_deinit(&self->clock); - if (self->has_mosi) { - common_hal_digitalio_digitalinout_deinit(&self->mosi); + // Only deinit and free the pins if we own them + if (self->own_clock) { + digitalinout_protocol_deinit(self->clock); + circuitpy_free_obj(self->clock); + } + if (self->has_mosi && self->own_mosi) { + digitalinout_protocol_deinit(self->mosi); + circuitpy_free_obj(self->mosi); } - if (self->has_miso) { - common_hal_digitalio_digitalinout_deinit(&self->miso); + if (self->has_miso && self->own_miso) { + digitalinout_protocol_deinit(self->miso); + circuitpy_free_obj(self->miso); } } @@ -80,7 +82,7 @@ void shared_module_bitbangio_spi_configure(bitbangio_spi_obj_t *self, // If the polarity has changed, make sure we re-initialize the idle state // of the clock as well. self->polarity = polarity; - common_hal_digitalio_digitalinout_switch_to_output(&self->clock, polarity == 1, DRIVE_MODE_PUSH_PULL); + digitalinout_protocol_switch_to_output(self->clock, polarity == 1, DRIVE_MODE_PUSH_PULL); } self->phase = phase; } @@ -121,9 +123,15 @@ bool shared_module_bitbangio_spi_write(bitbangio_spi_obj_t *self, const uint8_t for (size_t i = 0; i < len; ++i) { uint8_t data_out = data[i]; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + if (i == 0 && j == 0) { + if (digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1) != 0) { + return false; + } + } else { + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); + } + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); } if (dest != NULL) { dest[i] = data_in; @@ -136,16 +144,22 @@ bool shared_module_bitbangio_spi_write(bitbangio_spi_obj_t *self, const uint8_t for (size_t i = 0; i < len; ++i) { uint8_t data_out = data[i]; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); + if (i == 0 && j == 0) { + if (!digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1)) { + return false; + } + } else { + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); + } if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); common_hal_mcu_delay_us(delay_half); } } @@ -176,14 +190,18 @@ bool shared_module_bitbangio_spi_read(bitbangio_spi_obj_t *self, uint8_t *data, if (delay_half <= MICROPY_PY_MACHINE_SPI_MIN_DELAY) { // Clock out zeroes while we read. if (self->has_mosi) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, false); + if (digitalinout_protocol_set_value(self->mosi, false) != 0) { + return false; + } } for (size_t i = 0; i < len; ++i) { uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); - data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); + bool bit; + digitalinout_protocol_get_value(self->miso, &bit); + data_in = (data_in << 1) | bit; + digitalinout_protocol_set_value(self->clock, self->polarity); } data[i] = data_in; } @@ -191,28 +209,32 @@ bool shared_module_bitbangio_spi_read(bitbangio_spi_obj_t *self, uint8_t *data, } #endif if (self->has_mosi) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, false); + if (!digitalinout_protocol_set_value(self->mosi, false)) { + return false; + } } for (size_t i = 0; i < len; ++i) { uint8_t data_out = write_data; uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { if (self->has_mosi) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); } if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); common_hal_mcu_delay_us(delay_half); } - data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso); + bool bit; + digitalinout_protocol_get_value(self->miso, &bit); + data_in = (data_in << 1) | bit; if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); common_hal_mcu_delay_us(delay_half); } } @@ -248,10 +270,18 @@ bool shared_module_bitbangio_spi_transfer(bitbangio_spi_obj_t *self, const uint8 uint8_t data_out = dout[i]; uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); - data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + if (i == 0 && j == 0) { + if (digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1) != 0) { + return false; + } + } else { + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); + } + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); + bool bit; + digitalinout_protocol_get_value(self->miso, &bit); + data_in = (data_in << 1) | bit; + digitalinout_protocol_set_value(self->clock, self->polarity); } din[i] = data_in; @@ -267,20 +297,28 @@ bool shared_module_bitbangio_spi_transfer(bitbangio_spi_obj_t *self, const uint8 uint8_t data_out = dout[i]; uint8_t data_in = 0; for (int j = 0; j < 8; ++j, data_out <<= 1) { - common_hal_digitalio_digitalinout_set_value(&self->mosi, (data_out >> 7) & 1); + if (i == 0 && j == 0) { + if (!digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1)) { + return false; + } + } else { + digitalinout_protocol_set_value(self->mosi, (data_out >> 7) & 1); + } if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, 1 - self->polarity); + digitalinout_protocol_set_value(self->clock, 1 - self->polarity); common_hal_mcu_delay_us(delay_half); } - data_in = (data_in << 1) | common_hal_digitalio_digitalinout_get_value(&self->miso); + bool bit; + digitalinout_protocol_get_value(self->miso, &bit); + data_in = (data_in << 1) | bit; if (self->phase == 0) { common_hal_mcu_delay_us(delay_half); - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); } else { - common_hal_digitalio_digitalinout_set_value(&self->clock, self->polarity); + digitalinout_protocol_set_value(self->clock, self->polarity); common_hal_mcu_delay_us(delay_half); } } diff --git a/shared-module/bitbangio/SPI.h b/shared-module/bitbangio/SPI.h index c19170af7c46f..5f3c109b7f685 100644 --- a/shared-module/bitbangio/SPI.h +++ b/shared-module/bitbangio/SPI.h @@ -12,13 +12,16 @@ typedef struct { mp_obj_base_t base; - digitalio_digitalinout_obj_t clock; - digitalio_digitalinout_obj_t mosi; - digitalio_digitalinout_obj_t miso; + mp_obj_t clock; + mp_obj_t mosi; + mp_obj_t miso; uint32_t delay_half; bool has_miso : 1; bool has_mosi : 1; uint8_t polarity : 1; uint8_t phase : 1; volatile bool locked : 1; + bool own_clock; + bool own_mosi; + bool own_miso; } bitbangio_spi_obj_t; diff --git a/shared-module/bitmaptools/__init__.c b/shared-module/bitmaptools/__init__.c index 8a41e387e513a..2f2b7aaf47583 100644 --- a/shared-module/bitmaptools/__init__.c +++ b/shared-module/bitmaptools/__init__.c @@ -204,6 +204,21 @@ void common_hal_bitmaptools_rotozoom(displayio_bitmap_t *self, int16_t ox, int16 } } +void common_hal_bitmaptools_replace_color(displayio_bitmap_t *destination, + uint32_t old_color, + uint32_t new_color) { + + int16_t x, y; + for (x = 0; x < destination->width; x++) { + for (y = 0; y < destination->height; y++) { + uint32_t pixel_val = common_hal_displayio_bitmap_get_pixel(destination, x, y); + if (pixel_val == old_color) { + displayio_bitmap_write_pixel(destination, x, y, new_color); + } + } + } +} + void common_hal_bitmaptools_fill_region(displayio_bitmap_t *destination, int16_t x1, int16_t y1, int16_t x2, int16_t y2, diff --git a/shared-module/board/__init__.c b/shared-module/board/__init__.c index 96735c8dbaa40..6bfc0bbf0f1d9 100644 --- a/shared-module/board/__init__.c +++ b/shared-module/board/__init__.c @@ -43,15 +43,6 @@ static const board_i2c_pin_t i2c_pin[CIRCUITPY_BOARD_I2C] = CIRCUITPY_BOARD_I2C_ static busio_i2c_obj_t i2c_obj[CIRCUITPY_BOARD_I2C]; static bool i2c_obj_created[CIRCUITPY_BOARD_I2C]; -bool common_hal_board_is_i2c(mp_obj_t obj) { - for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_I2C; instance++) { - if (obj == &i2c_obj[instance]) { - return true; - } - } - return false; -} - mp_obj_t common_hal_board_get_i2c(const mp_int_t instance) { return i2c_obj_created[instance] ? &i2c_obj[instance] : NULL; } @@ -89,15 +80,6 @@ static const board_spi_pin_t spi_pin[CIRCUITPY_BOARD_SPI] = CIRCUITPY_BOARD_SPI_ static busio_spi_obj_t spi_obj[CIRCUITPY_BOARD_SPI]; static bool spi_obj_created[CIRCUITPY_BOARD_SPI]; -bool common_hal_board_is_spi(mp_obj_t obj) { - for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_SPI; instance++) { - if (obj == &spi_obj[instance]) { - return true; - } - } - return false; -} - mp_obj_t common_hal_board_get_spi(const mp_int_t instance) { return spi_obj_created[instance] ? &spi_obj[instance] : NULL; } @@ -136,15 +118,6 @@ static const board_uart_pin_t uart_pin[CIRCUITPY_BOARD_UART] = CIRCUITPY_BOARD_U static busio_uart_obj_t uart_obj[CIRCUITPY_BOARD_UART]; static bool uart_obj_created[CIRCUITPY_BOARD_UART]; -bool common_hal_board_is_uart(mp_obj_t obj) { - for (uint8_t instance = 0; instance < CIRCUITPY_BOARD_UART; instance++) { - if (obj == &uart_obj[instance]) { - return true; - } - } - return false; -} - mp_obj_t common_hal_board_get_uart(const mp_int_t instance) { return uart_obj_created[instance] ? &uart_obj[instance] : NULL; } @@ -183,6 +156,10 @@ void reset_board_buses(void) { } } #endif + #if CIRCUITPY_I2CIOEXPANDER + // Assume the native I2C bus is used for IO Expander pins. + display_using_i2c = true; + #endif if (i2c_obj_created[instance]) { // make sure I2C lock is not held over a soft reset common_hal_busio_i2c_unlock(&i2c_obj[instance]); diff --git a/shared-module/busdisplay/BusDisplay.c b/shared-module/busdisplay/BusDisplay.c index ac57f3bf3e019..01e0f7a7896f0 100644 --- a/shared-module/busdisplay/BusDisplay.c +++ b/shared-module/busdisplay/BusDisplay.c @@ -6,6 +6,7 @@ #include "shared-bindings/busdisplay/BusDisplay.h" +#include "py/mphal.h" #include "py/runtime.h" #if CIRCUITPY_FOURWIRE #include "shared-bindings/fourwire/FourWire.h" @@ -16,6 +17,7 @@ #if CIRCUITPY_PARALLELDISPLAYBUS #include "shared-bindings/paralleldisplaybus/ParallelBus.h" #endif +#include "shared/runtime/interrupt_char.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/time/__init__.h" #include "shared-module/displayio/__init__.h" @@ -73,6 +75,9 @@ void common_hal_busdisplay_busdisplay_construct(busdisplay_busdisplay_obj_t *sel uint8_t *data = cmd + 2; while (!displayio_display_bus_begin_transaction(&self->bus)) { RUN_BACKGROUND_TASKS; + if (mp_hal_is_interrupted()) { + mp_raise_RuntimeError_varg(MP_ERROR_TEXT("%q init failed"), MP_QSTR_display); + } } if (self->bus.data_as_commands) { uint8_t full_command[data_size + 1]; @@ -80,7 +85,7 @@ void common_hal_busdisplay_busdisplay_construct(busdisplay_busdisplay_obj_t *sel memcpy(full_command + 1, data, data_size); self->bus.send(self->bus.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, full_command, data_size + 1); } else { - self->bus.send(self->bus.bus, DISPLAY_COMMAND, CHIP_SELECT_TOGGLE_EVERY_BYTE, cmd, 1); + self->bus.send(self->bus.bus, DISPLAY_COMMAND, data_size > 0 ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED, cmd, 1); self->bus.send(self->bus.bus, DISPLAY_DATA, CHIP_SELECT_UNTOUCHED, data, data_size); } displayio_display_bus_end_transaction(&self->bus); @@ -214,7 +219,7 @@ static void _send_pixels(busdisplay_busdisplay_obj_t *self, uint8_t *pixels, uin } static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_area_t *area) { - uint16_t buffer_size = 128; // In uint32_ts + uint16_t buffer_size = CIRCUITPY_DISPLAY_AREA_BUFFER_SIZE / sizeof(uint32_t); // In uint32_ts displayio_area_t clipped; // Clip the area to the display by overlapping the areas. If there is no overlap then we're done. @@ -256,9 +261,10 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are // Allocated and shared as a uint32_t array so the compiler knows the // alignment everywhere. - uint32_t buffer[buffer_size]; uint32_t mask_length = (pixels_per_buffer / 32) + 1; + uint32_t buffer[buffer_size]; uint32_t mask[mask_length]; + uint16_t remaining_rows = displayio_area_height(&clipped); for (uint16_t j = 0; j < subrectangles; j++) { @@ -273,8 +279,6 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are } remaining_rows -= rows_per_buffer; - displayio_display_bus_set_region_to_update(&self->bus, &self->core, &subrectangle); - uint16_t subrectangle_size_bytes; if (self->core.colorspace.depth >= 8) { subrectangle_size_bytes = displayio_area_size(&subrectangle) * (self->core.colorspace.depth / 8); @@ -287,12 +291,12 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are displayio_display_core_fill_area(&self->core, &subrectangle, mask, buffer); + displayio_display_bus_set_region_to_update(&self->bus, &self->core, &subrectangle); + // Can't acquire display bus; skip the rest of the data. - if (!displayio_display_bus_is_free(&self->bus)) { + if (!displayio_display_bus_begin_transaction(&self->bus)) { return false; } - - displayio_display_bus_begin_transaction(&self->bus); _send_pixels(self, (uint8_t *)buffer, subrectangle_size_bytes); displayio_display_bus_end_transaction(&self->bus); @@ -305,6 +309,10 @@ static bool _refresh_area(busdisplay_busdisplay_obj_t *self, const displayio_are usb_background(); #endif } + + // Drain any remaining asynchronous transfers. + displayio_display_bus_flush(&self->bus); + return true; } @@ -313,7 +321,11 @@ static void _refresh_display(busdisplay_busdisplay_obj_t *self) { // A refresh on this bus is already in progress. Try next display. return; } - displayio_display_core_start_refresh(&self->core); + if (!displayio_display_core_start_refresh(&self->core)) { + // Refresh for this display already in progress. + return; + } + const displayio_area_t *current_area = _get_refresh_areas(self); while (current_area != NULL) { _refresh_area(self, current_area); diff --git a/shared-module/displayio/ColorConverter.c b/shared-module/displayio/ColorConverter.c index ae7d3f02d5127..d4e64ee286eb8 100644 --- a/shared-module/displayio/ColorConverter.c +++ b/shared-module/displayio/ColorConverter.c @@ -163,8 +163,13 @@ uint8_t displayio_colorconverter_compute_sevencolor(uint32_t color_rgb888) { } } -void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_hue, uint32_t *color) { - +void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color) { + if (pixel_chroma <= 16) { + if (!colorspace->grayscale) { + *color = 0; + } + return; + } int16_t hue_diff = colorspace->tricolor_hue - pixel_hue; if ((-10 <= hue_diff && hue_diff <= 10) || hue_diff <= -220 || hue_diff >= 220) { if (colorspace->grayscale) { @@ -177,6 +182,21 @@ void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *co } } +void displayio_colorconverter_compute_fourcolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color) { + *color >>= 1; + if (pixel_chroma <= 16) { + return; + } + int16_t hue_diff = colorspace->tricolor_hue - pixel_hue; + if ((-10 <= hue_diff && hue_diff <= 10) || hue_diff <= -220 || hue_diff >= 220) { + *color = 2; + } + int16_t hue_diff2 = colorspace->fourcolor_hue - pixel_hue; + if ((-10 <= hue_diff2 && hue_diff2 <= 10) || hue_diff2 <= -220 || hue_diff2 >= 220) { + *color = 3; + } +} + void common_hal_displayio_colorconverter_convert(displayio_colorconverter_t *self, const _displayio_colorspace_t *colorspace, uint32_t input_color, uint32_t *output_color) { displayio_input_pixel_t input_pixel; input_pixel.pixel = input_color; @@ -313,18 +333,17 @@ void displayio_convert_color(const _displayio_colorspace_t *colorspace, bool dit output_color->pixel = packed; output_color->opaque = true; return; - } else if (colorspace->tricolor) { + } else if (colorspace->tricolor || colorspace->fourcolor) { uint8_t luma = displayio_colorconverter_compute_luma(pixel); + uint8_t pixel_chroma = displayio_colorconverter_compute_chroma(pixel); output_color->pixel = luma >> (8 - colorspace->depth); - if (displayio_colorconverter_compute_chroma(pixel) <= 16) { - if (!colorspace->grayscale) { - output_color->pixel = 0; - } - output_color->opaque = true; - return; - } uint8_t pixel_hue = displayio_colorconverter_compute_hue(pixel); - displayio_colorconverter_compute_tricolor(colorspace, pixel_hue, &output_color->pixel); + if (colorspace->tricolor) { + displayio_colorconverter_compute_tricolor(colorspace, pixel_chroma, pixel_hue, &output_color->pixel); + } else if (colorspace->fourcolor) { + displayio_colorconverter_compute_fourcolor(colorspace, pixel_chroma, pixel_hue, &output_color->pixel); + } + output_color->opaque = true; return; } else if (colorspace->grayscale && colorspace->depth <= 8) { uint8_t luma = displayio_colorconverter_compute_luma(pixel); diff --git a/shared-module/displayio/ColorConverter.h b/shared-module/displayio/ColorConverter.h index d3dbedfe160ab..304004d566a95 100644 --- a/shared-module/displayio/ColorConverter.h +++ b/shared-module/displayio/ColorConverter.h @@ -43,4 +43,5 @@ uint8_t displayio_colorconverter_compute_chroma(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_hue(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_sixcolor(uint32_t color_rgb888); uint8_t displayio_colorconverter_compute_sevencolor(uint32_t color_rgb888); -void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_hue, uint32_t *color); +void displayio_colorconverter_compute_tricolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color); +void displayio_colorconverter_compute_fourcolor(const _displayio_colorspace_t *colorspace, uint8_t pixel_chroma, uint8_t pixel_hue, uint32_t *color); diff --git a/shared-module/displayio/Palette.h b/shared-module/displayio/Palette.h index bb87a93d98133..24ae5c4b7a525 100644 --- a/shared-module/displayio/Palette.h +++ b/shared-module/displayio/Palette.h @@ -15,10 +15,11 @@ typedef struct { uint8_t depth; uint8_t bytes_per_cell; uint8_t tricolor_hue; - uint8_t tricolor_luma; + uint8_t fourcolor_hue; uint8_t grayscale_bit; // The lowest grayscale bit. Normally 8 - depth. bool grayscale; bool tricolor; + bool fourcolor; bool sixcolor; // Spectra6 e-ink screens. bool sevencolor; // Acep e-ink screens. bool pixels_in_byte_share_row; diff --git a/shared-module/displayio/Shape.c b/shared-module/displayio/Shape.c deleted file mode 100644 index 143d37c6c9b35..0000000000000 --- a/shared-module/displayio/Shape.c +++ /dev/null @@ -1,126 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include "shared-bindings/displayio/Shape.h" - -#include - -#include "py/runtime.h" -#include "py/misc.h" - -void common_hal_displayio_shape_construct(displayio_shape_t *self, uint32_t width, - uint32_t height, bool mirror_x, bool mirror_y) { - self->mirror_x = mirror_x; - self->mirror_y = mirror_y; - self->width = width; - if (self->mirror_x) { - width /= 2; - width += self->width % 2; - } - self->half_width = width; - - self->height = height; - if (self->mirror_y) { - height /= 2; - height += self->height % 2; - } - self->half_height = height; - - self->data = m_malloc_without_collect(height * sizeof(uint32_t)); - - for (uint16_t i = 0; i < height; i++) { - self->data[2 * i] = 0; - self->data[2 * i + 1] = width; - } - - self->dirty_area.x1 = 0; - self->dirty_area.x2 = width; - self->dirty_area.y1 = 0; - self->dirty_area.y2 = height; -} - -void common_hal_displayio_shape_set_boundary(displayio_shape_t *self, uint16_t y, uint16_t start_x, uint16_t end_x) { - uint16_t max_y = self->height - 1; - if (self->mirror_y) { - max_y = self->half_height - 1; - } - mp_arg_validate_int_range(y, 0, max_y, MP_QSTR_y); - uint16_t max_x = self->width - 1; - if (self->mirror_x) { - max_x = self->half_width - 1; - } - mp_arg_validate_int_range(start_x, 0, max_x, MP_QSTR_start_x); - mp_arg_validate_int_range(end_x, 0, max_x, MP_QSTR_end_x); - - uint16_t lower_x, upper_x, lower_y, upper_y; - - // find x-boundaries for updating based on current data and start_x, end_x, and mirror_x - lower_x = MIN(start_x, self->data[2 * y]); - - if (self->mirror_x) { - upper_x = self->width - lower_x + 1; // dirty rectangles are treated with max value exclusive - } else { - upper_x = MAX(end_x, self->data[2 * y + 1]) + 1; // dirty rectangles are treated with max value exclusive - } - - // find y-boundaries based on y and mirror_y - lower_y = y; - - if (self->mirror_y) { - upper_y = self->height - lower_y + 1; // dirty rectangles are treated with max value exclusive - } else { - upper_y = y + 1; // dirty rectangles are treated with max value exclusive - } - - self->data[2 * y] = start_x; // update the data array with the new boundaries - self->data[2 * y + 1] = end_x; - - if (self->dirty_area.x1 == self->dirty_area.x2) { // Dirty region is empty - self->dirty_area.x1 = lower_x; - self->dirty_area.x2 = upper_x; - self->dirty_area.y1 = lower_y; - self->dirty_area.y2 = upper_y; - - } else { // Dirty region is not empty - self->dirty_area.x1 = MIN(lower_x, self->dirty_area.x1); - self->dirty_area.x2 = MAX(upper_x, self->dirty_area.x2); - - self->dirty_area.y1 = MIN(lower_y, self->dirty_area.y1); - self->dirty_area.y2 = MAX(upper_y, self->dirty_area.y2); - } -} - -uint32_t common_hal_displayio_shape_get_pixel(void *obj, int16_t x, int16_t y) { - displayio_shape_t *self = obj; - if (x >= self->width || x < 0 || y >= self->height || y < 0) { - return 0; - } - if (self->mirror_x && x >= self->half_width) { - x = self->width - x - 1; - } - if (self->mirror_y && y >= self->half_height) { - y = self->height - y - 1; - } - uint16_t start_x = self->data[2 * y]; - uint16_t end_x = self->data[2 * y + 1]; - if (x < start_x || x > end_x) { - return 0; - } - return 1; -} - -displayio_area_t *displayio_shape_get_refresh_areas(displayio_shape_t *self, displayio_area_t *tail) { - if (self->dirty_area.x1 == self->dirty_area.x2) { - return tail; - } - self->dirty_area.next = tail; - return &self->dirty_area; -} - -void displayio_shape_finish_refresh(displayio_shape_t *self) { - self->dirty_area.x1 = 0; - self->dirty_area.x2 = 0; -} diff --git a/shared-module/displayio/Shape.h b/shared-module/displayio/Shape.h deleted file mode 100644 index 20ee182023fe9..0000000000000 --- a/shared-module/displayio/Shape.h +++ /dev/null @@ -1,28 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2019 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include - -#include "py/obj.h" -#include "shared-module/displayio/area.h" - -typedef struct { - mp_obj_base_t base; - uint16_t width; - uint16_t height; - uint16_t half_width; - uint16_t half_height; - uint16_t *data; - bool mirror_x; - bool mirror_y; - displayio_area_t dirty_area; -} displayio_shape_t; - -void displayio_shape_finish_refresh(displayio_shape_t *self); -displayio_area_t *displayio_shape_get_refresh_areas(displayio_shape_t *self, displayio_area_t *tail); diff --git a/shared-module/displayio/__init__.c b/shared-module/displayio/__init__.c index 4b467b75589d1..c1309ee09723d 100644 --- a/shared-module/displayio/__init__.c +++ b/shared-module/displayio/__init__.c @@ -10,9 +10,11 @@ #include "shared-bindings/displayio/__init__.h" #include "shared/runtime/interrupt_char.h" +#include "py/gc.h" #include "py/runtime.h" #include "shared-bindings/board/__init__.h" #include "shared-bindings/busio/I2C.h" +#include "shared-bindings/busio/SPI.h" #include "shared-bindings/displayio/Bitmap.h" #include "shared-bindings/displayio/Group.h" #include "shared-bindings/displayio/Palette.h" @@ -40,6 +42,13 @@ #include "shared-module/aurora_epaper/aurora_framebuffer.h" #endif +#if CIRCUITPY_MIPIDSI +#include "shared-bindings/mipidsi/Display.h" +#endif +#if CIRCUITPY_ZEPHYR_DISPLAY +#include "bindings/zephyr_display/Display.h" +#endif + #ifdef BOARD_USE_INTERNAL_SPI #include "supervisor/spi_flash_api.h" #endif @@ -63,7 +72,7 @@ displayio_buffer_transform_t null_transform = { .transpose_xy = false }; -#if CIRCUITPY_RGBMATRIX || CIRCUITPY_IS31FL3741 || CIRCUITPY_VIDEOCORE || CIRCUITPY_PICODVI +#if CIRCUITPY_RGBMATRIX || CIRCUITPY_IS31FL3741 || CIRCUITPY_VIDEOCORE || CIRCUITPY_PICODVI || CIRCUITPY_MIPIDSI static bool any_display_uses_this_framebuffer(mp_obj_base_t *obj) { for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { if (displays[i].display_base.type == &framebufferio_framebufferdisplay_type) { @@ -106,6 +115,10 @@ void displayio_background(void) { } else if (display_type == &epaperdisplay_epaperdisplay_type) { epaperdisplay_epaperdisplay_background(&displays[i].epaper_display); #endif + #if CIRCUITPY_ZEPHYR_DISPLAY + } else if (display_type == &zephyr_display_display_type) { + zephyr_display_display_background(&displays[i].zephyr_display); + #endif } } @@ -136,10 +149,17 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { } else if (display_type == &framebufferio_framebufferdisplay_type) { release_framebufferdisplay(&displays[i].framebuffer_display); #endif + #if CIRCUITPY_ZEPHYR_DISPLAY + } else if (display_type == &zephyr_display_display_type) { + release_zephyr_display(&displays[i].zephyr_display); + #endif } displays[i].display_base.type = &mp_type_NoneType; } for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { + if (i == primary_display_number) { + continue; + } mp_const_obj_t bus_type = display_buses[i].bus_base.type; if (bus_type == NULL || bus_type == &mp_type_NoneType) { continue; @@ -179,11 +199,21 @@ static void common_hal_displayio_release_displays_impl(bool keep_primary) { } else if (bus_type == &picodvi_framebuffer_type) { common_hal_picodvi_framebuffer_deinit(&display_buses[i].picodvi); #endif + #if CIRCUITPY_MIPIDSI + } else if (bus_type == &mipidsi_display_type) { + common_hal_mipidsi_display_deinit(&display_buses[i].mipidsi); + #endif + #if CIRCUITPY_QSPIBUS + } else if (bus_type == &qspibus_qspibus_type) { + common_hal_qspibus_qspibus_deinit(&display_buses[i].qspi_bus); + #endif } display_buses[i].bus_base.type = &mp_type_NoneType; } - supervisor_stop_terminal(); + if (!keep_primary) { + supervisor_stop_terminal(); + } } void common_hal_displayio_release_displays(void) { @@ -191,13 +221,15 @@ void common_hal_displayio_release_displays(void) { } void reset_displays(void) { - // In CircuitPython 10, release secondary displays before doing anything else: - // common_hal_displayio_release_displays_impl(true); + // TODO: In CircuitPython 11, uncomment the call. + // Release secondary displays. + // common_hal_displayio_release_displays_impl(/*keep_primary*/ true); // The SPI buses used by FourWires may be allocated on the heap so we need to move them inline. for (uint8_t i = 0; i < CIRCUITPY_DISPLAY_LIMIT; i++) { mp_const_obj_t display_bus_type = display_buses[i].bus_base.type; if (display_bus_type == NULL || display_bus_type == &mp_type_NoneType) { + display_buses[i].bus_base.type = &mp_type_NoneType; continue; #if CIRCUITPY_FOURWIRE } else if (display_bus_type == &fourwire_fourwire_type) { @@ -205,21 +237,12 @@ void reset_displays(void) { if (((size_t)fourwire->bus) < ((size_t)&display_buses) || ((size_t)fourwire->bus) > ((size_t)&display_buses + CIRCUITPY_DISPLAY_LIMIT * sizeof(primary_display_bus_t))) { busio_spi_obj_t *original_spi = fourwire->bus; - #if CIRCUITPY_BOARD_SPI - // We don't need to move original_spi if it is a board.SPI object because it is - // statically allocated already. (Doing so would also make it impossible to reference in - // a subsequent VM run.) - if (common_hal_board_is_spi(original_spi)) { + if (!gc_ptr_on_heap(original_spi)) { continue; } - #endif - #ifdef BOARD_USE_INTERNAL_SPI - if (original_spi == (mp_obj_t)(&supervisor_flash_spi_bus)) { - continue; - } - #endif memcpy(&fourwire->inline_bus, original_spi, sizeof(busio_spi_obj_t)); fourwire->bus = &fourwire->inline_bus; + // Check for other display buses that use the same spi bus and swap them too. for (uint8_t j = i + 1; j < CIRCUITPY_DISPLAY_LIMIT; j++) { if (display_buses[j].fourwire_bus.base.type == &fourwire_fourwire_type && @@ -227,6 +250,8 @@ void reset_displays(void) { display_buses[j].fourwire_bus.bus = &fourwire->inline_bus; } } + // Mark the old SPI object so it is considered deinit. + common_hal_busio_spi_mark_deinit(original_spi); } #endif #if CIRCUITPY_I2CDISPLAYBUS @@ -236,14 +261,9 @@ void reset_displays(void) { if (((size_t)i2c->bus) < ((size_t)&display_buses) || ((size_t)i2c->bus) > ((size_t)&display_buses + CIRCUITPY_DISPLAY_LIMIT * sizeof(primary_display_bus_t))) { busio_i2c_obj_t *original_i2c = i2c->bus; - #if CIRCUITPY_BOARD_I2C - // We don't need to move original_i2c if it is a board.I2C object because it is - // statically allocated already. (Doing so would also make it impossible to reference in - // a subsequent VM run.) - if (common_hal_board_is_i2c(original_i2c)) { + if (!gc_ptr_on_heap(original_i2c)) { continue; } - #endif memcpy(&i2c->inline_bus, original_i2c, sizeof(busio_i2c_obj_t)); i2c->bus = &i2c->inline_bus; // Check for other displays that use the same i2c bus and swap them too. @@ -272,22 +292,17 @@ void reset_displays(void) { if (((uint32_t)is31fb->is31fl3741->i2c) < ((uint32_t)&display_buses) || ((uint32_t)is31fb->is31fl3741->i2c) > ((uint32_t)&display_buses + CIRCUITPY_DISPLAY_LIMIT)) { - #if CIRCUITPY_BOARD_I2C - // We don't need to move original_i2c if it is the board.I2C object because it is - // statically allocated already. (Doing so would also make it impossible to reference in - // a subsequent VM run.) - if (common_hal_board_is_i2c(is31fb->is31fl3741->i2c)) { - continue; - } - #endif - is31fl3741_IS31FL3741_obj_t *original_is31 = is31fb->is31fl3741; - memcpy(&is31fb->inline_is31fl3741, original_is31, sizeof(is31fl3741_IS31FL3741_obj_t)); - is31fb->is31fl3741 = &is31fb->inline_is31fl3741; + if (gc_ptr_on_heap(original_is31)) { + memcpy(&is31fb->inline_is31fl3741, original_is31, sizeof(is31fl3741_IS31FL3741_obj_t)); + is31fb->is31fl3741 = &is31fb->inline_is31fl3741; + } busio_i2c_obj_t *original_i2c = is31fb->is31fl3741->i2c; - memcpy(&is31fb->is31fl3741->inline_i2c, original_i2c, sizeof(busio_i2c_obj_t)); - is31fb->is31fl3741->i2c = &is31fb->is31fl3741->inline_i2c; + if (gc_ptr_on_heap(original_i2c)) { + memcpy(&is31fb->is31fl3741->inline_i2c, original_i2c, sizeof(busio_i2c_obj_t)); + is31fb->is31fl3741->i2c = &is31fb->is31fl3741->inline_i2c; + } } if (!any_display_uses_this_framebuffer(&is31fb->base)) { @@ -319,15 +334,20 @@ void reset_displays(void) { #endif #if CIRCUITPY_AURORA_EPAPER } else if (display_bus_type == &aurora_framebuffer_type) { - #if CIRCUITPY_BOARD_SPI aurora_epaper_framebuffer_obj_t *aurora = &display_buses[i].aurora_epaper; - if (common_hal_board_is_spi(aurora->bus)) { + if (gc_ptr_on_heap(aurora->bus)) { common_hal_aurora_epaper_framebuffer_set_free_bus(false); } - #endif // Set to None, gets deinit'd up by display_base display_buses[i].bus_base.type = &mp_type_NoneType; #endif + #if CIRCUITPY_MIPIDSI + } else if (display_bus_type == &mipidsi_display_type) { + mipidsi_display_obj_t *display = &display_buses[i].mipidsi; + if (!any_display_uses_this_framebuffer(&display->base)) { + common_hal_mipidsi_display_deinit(display); + } + #endif } else { // Not an active display bus. continue; @@ -353,6 +373,10 @@ void reset_displays(void) { } else if (display_type == &framebufferio_framebufferdisplay_type) { framebufferio_framebufferdisplay_reset(&displays[i].framebuffer_display); #endif + #if CIRCUITPY_ZEPHYR_DISPLAY + } else if (display_type == &zephyr_display_display_type) { + zephyr_display_display_reset(&displays[i].zephyr_display); + #endif } } } @@ -404,6 +428,10 @@ void displayio_gc_collect(void) { } else if (display_type == &epaperdisplay_epaperdisplay_type) { epaperdisplay_epaperdisplay_collect_ptrs(&displays[i].epaper_display); #endif + #if CIRCUITPY_ZEPHYR_DISPLAY + } else if (display_type == &zephyr_display_display_type) { + zephyr_display_display_collect_ptrs(&displays[i].zephyr_display); + #endif } } } diff --git a/shared-module/displayio/__init__.h b/shared-module/displayio/__init__.h index 29b8c64c97240..eabff662782af 100644 --- a/shared-module/displayio/__init__.h +++ b/shared-module/displayio/__init__.h @@ -38,6 +38,15 @@ #if CIRCUITPY_DOTCLOCKFRAMEBUFFER #include "common-hal/dotclockframebuffer/DotClockFramebuffer.h" #endif +#if CIRCUITPY_MIPIDSI +#include "shared-bindings/mipidsi/Display.h" +#endif +#if CIRCUITPY_QSPIBUS +#include "shared-bindings/qspibus/QSPIBus.h" +#endif +#if CIRCUITPY_ZEPHYR_DISPLAY +#include "bindings/zephyr_display/Display.h" +#endif // Port unique frame buffers. #if CIRCUITPY_VIDEOCORE #include "bindings/videocore/Framebuffer.h" @@ -81,6 +90,12 @@ typedef struct { #if CIRCUITPY_AURORA_EPAPER aurora_epaper_framebuffer_obj_t aurora_epaper; #endif + #if CIRCUITPY_MIPIDSI + mipidsi_display_obj_t mipidsi; + #endif + #if CIRCUITPY_QSPIBUS + qspibus_qspibus_obj_t qspi_bus; + #endif }; } primary_display_bus_t; @@ -96,6 +111,9 @@ typedef struct { #if CIRCUITPY_FRAMEBUFFERIO framebufferio_framebufferdisplay_obj_t framebuffer_display; #endif + #if CIRCUITPY_ZEPHYR_DISPLAY + zephyr_display_display_obj_t zephyr_display; + #endif }; } primary_display_t; diff --git a/shared-module/displayio/bus_core.c b/shared-module/displayio/bus_core.c index e01b9d9eef685..9b3fb426217e1 100644 --- a/shared-module/displayio/bus_core.c +++ b/shared-module/displayio/bus_core.c @@ -17,6 +17,9 @@ #if CIRCUITPY_PARALLELDISPLAYBUS #include "shared-bindings/paralleldisplaybus/ParallelBus.h" #endif +#if CIRCUITPY_QSPIBUS +#include "shared-bindings/qspibus/QSPIBus.h" +#endif #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/time/__init__.h" #include "shared-module/displayio/__init__.h" @@ -49,6 +52,8 @@ void displayio_display_bus_construct(displayio_display_bus_t *self, self->SH1107_addressing = SH1107_addressing; self->address_little_endian = address_little_endian; + self->flush = NULL; + #if CIRCUITPY_PARALLELDISPLAYBUS if (mp_obj_is_type(bus, ¶lleldisplaybus_parallelbus_type)) { self->bus_reset = common_hal_paralleldisplaybus_parallelbus_reset; @@ -79,17 +84,33 @@ void displayio_display_bus_construct(displayio_display_bus_t *self, self->collect_ptrs = common_hal_i2cdisplaybus_i2cdisplaybus_collect_ptrs; } else #endif + #if CIRCUITPY_QSPIBUS + if (mp_obj_is_type(bus, &qspibus_qspibus_type)) { + self->bus_reset = common_hal_qspibus_qspibus_reset; + self->bus_free = common_hal_qspibus_qspibus_bus_free; + self->begin_transaction = common_hal_qspibus_qspibus_begin_transaction; + self->send = common_hal_qspibus_qspibus_send; + self->end_transaction = common_hal_qspibus_qspibus_end_transaction; + self->flush = common_hal_qspibus_qspibus_flush; + self->collect_ptrs = common_hal_qspibus_qspibus_collect_ptrs; + } else + #endif { mp_raise_ValueError(MP_ERROR_TEXT("Unsupported display bus type")); } self->bus = bus; } +// This is just a hint, and is not a reliable result, since the bus could be grabbed in between this called +// and the attempt to use the bus. Use displayio_display_bus_begin_transaction(), which is atomic. bool displayio_display_bus_is_free(displayio_display_bus_t *self) { return !self->bus || self->bus_free(self->bus); } -bool displayio_display_bus_begin_transaction(displayio_display_bus_t *self) { +MP_WARN_UNUSED_RESULT bool displayio_display_bus_begin_transaction(displayio_display_bus_t *self) { + if (!self->bus) { + return false; + } mp_obj_base_t *bus_base = MP_OBJ_TO_PTR(self->bus); if (bus_base->type == &mp_type_NoneType) { return false; @@ -128,7 +149,9 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d } // Set column. - displayio_display_bus_begin_transaction(self); + if (!displayio_display_bus_begin_transaction(self)) { + return; + } uint8_t data[5]; data[0] = self->column_command; uint8_t data_length = 1; @@ -167,7 +190,9 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d if (self->set_current_column_command != NO_COMMAND) { uint8_t command = self->set_current_column_command; - displayio_display_bus_begin_transaction(self); + if (!displayio_display_bus_begin_transaction(self)) { + return; + } self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); // Only send the first half of data because it is the first coordinate. self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); @@ -176,7 +201,9 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d // Set row. - displayio_display_bus_begin_transaction(self); + if (!displayio_display_bus_begin_transaction(self)) { + return; + } data[0] = self->row_command; data_length = 1; if (!self->data_as_commands) { @@ -211,7 +238,9 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d if (self->set_current_row_command != NO_COMMAND) { uint8_t command = self->set_current_row_command; - displayio_display_bus_begin_transaction(self); + if (!displayio_display_bus_begin_transaction(self)) { + return; + } self->send(self->bus, DISPLAY_COMMAND, chip_select, &command, 1); // Only send the first half of data because it is the first coordinate. self->send(self->bus, DISPLAY_DATA, chip_select, data, data_length / 2); @@ -219,6 +248,12 @@ void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, d } } +void displayio_display_bus_flush(displayio_display_bus_t *self) { + if (self->flush != NULL) { + self->flush(self->bus); + } +} + void displayio_display_bus_collect_ptrs(displayio_display_bus_t *self) { self->collect_ptrs(self->bus); } diff --git a/shared-module/displayio/bus_core.h b/shared-module/displayio/bus_core.h index 838454c92e6d8..75d03d7f28430 100644 --- a/shared-module/displayio/bus_core.h +++ b/shared-module/displayio/bus_core.h @@ -21,6 +21,7 @@ typedef struct { display_bus_begin_transaction begin_transaction; display_bus_send send; display_bus_end_transaction end_transaction; + display_bus_flush flush; display_bus_collect_ptrs collect_ptrs; uint16_t ram_width; uint16_t ram_height; @@ -49,6 +50,10 @@ void displayio_display_bus_end_transaction(displayio_display_bus_t *self); void displayio_display_bus_set_region_to_update(displayio_display_bus_t *self, displayio_display_core_t *display, displayio_area_t *area); +// Drain any pending asynchronous transfers on the bus. +// No-op for synchronous buses (FourWire, I2C, ParallelBus). +void displayio_display_bus_flush(displayio_display_bus_t *self); + void release_display_bus(displayio_display_bus_t *self); void displayio_display_bus_collect_ptrs(displayio_display_bus_t *self); diff --git a/shared-module/epaperdisplay/EPaperDisplay.c b/shared-module/epaperdisplay/EPaperDisplay.c index a5a303d82ae75..d34be9d5c7c0a 100644 --- a/shared-module/epaperdisplay/EPaperDisplay.c +++ b/shared-module/epaperdisplay/EPaperDisplay.c @@ -26,71 +26,73 @@ #define DELAY 0x80 void common_hal_epaperdisplay_epaperdisplay_construct(epaperdisplay_epaperdisplay_obj_t *self, - mp_obj_t bus, const uint8_t *start_sequence, uint16_t start_sequence_len, mp_float_t start_up_time, - const uint8_t *stop_sequence, uint16_t stop_sequence_len, - uint16_t width, uint16_t height, uint16_t ram_width, uint16_t ram_height, - int16_t colstart, int16_t rowstart, uint16_t rotation, - uint16_t set_column_window_command, uint16_t set_row_window_command, - uint16_t set_current_column_command, uint16_t set_current_row_command, - uint16_t write_black_ram_command, bool black_bits_inverted, - uint16_t write_color_ram_command, bool color_bits_inverted, uint32_t highlight_color, - const uint8_t *refresh_sequence, uint16_t refresh_sequence_len, mp_float_t refresh_time, - const mcu_pin_obj_t *busy_pin, bool busy_state, mp_float_t seconds_per_frame, - bool chip_select, bool grayscale, bool acep, bool spectra6, bool two_byte_sequence_length, bool address_little_endian) { + const epaperdisplay_construct_args_t *args) { uint16_t color_depth = 1; bool core_grayscale = true; - if (highlight_color != 0x000000) { + if (args->highlight_color != 0x000000) { self->core.colorspace.tricolor = true; - self->core.colorspace.tricolor_hue = displayio_colorconverter_compute_hue(highlight_color); - self->core.colorspace.tricolor_luma = displayio_colorconverter_compute_luma(highlight_color); + self->core.colorspace.tricolor_hue = displayio_colorconverter_compute_hue(args->highlight_color); } else { self->core.colorspace.tricolor = false; } - self->acep = acep || spectra6; - self->core.colorspace.sixcolor = spectra6; - self->core.colorspace.sevencolor = acep; + if (args->highlight_color != 0x000000 && args->highlight_color2 != 0x000000) { + self->core.colorspace.tricolor = false; + self->core.colorspace.fourcolor = true; + self->core.colorspace.fourcolor_hue = displayio_colorconverter_compute_hue(args->highlight_color2); + } else { + self->core.colorspace.fourcolor = false; + } + self->acep = args->acep || args->spectra6; + self->core.colorspace.sixcolor = args->spectra6; + self->core.colorspace.sevencolor = args->acep; + bool grayscale = args->grayscale; if (self->acep) { color_depth = 4; // bits. 7 colors + clean grayscale = false; core_grayscale = false; } + if ((args->highlight_color != 0x000000 || args->highlight_color2 != 0x000000) && args->write_color_ram_command == NO_COMMAND) { + color_depth = 2; + core_grayscale = false; + grayscale = false; + } - displayio_display_core_construct(&self->core, width, height, rotation, color_depth, core_grayscale, true, 1, true, true); - displayio_display_bus_construct(&self->bus, bus, ram_width, ram_height, - colstart, rowstart, - set_column_window_command, set_row_window_command, set_current_column_command, set_current_row_command, - false /* data_as_commands */, chip_select, - false /* SH1107_addressing */, address_little_endian); + displayio_display_core_construct(&self->core, args->width, args->height, args->rotation, color_depth, core_grayscale, true, 1, true, true); + displayio_display_bus_construct(&self->bus, args->bus, args->ram_width, args->ram_height, + args->colstart, args->rowstart, + args->set_column_window_command, args->set_row_window_command, args->set_current_column_command, args->set_current_row_command, + false /* data_as_commands */, args->always_toggle_chip_select, + false /* SH1107_addressing */, args->address_little_endian); - self->write_black_ram_command = write_black_ram_command; - self->black_bits_inverted = black_bits_inverted; - self->write_color_ram_command = write_color_ram_command; - self->color_bits_inverted = color_bits_inverted; - self->refresh_time = refresh_time * 1000; - self->busy_state = busy_state; + self->write_black_ram_command = args->write_black_ram_command; + self->black_bits_inverted = args->black_bits_inverted; + self->write_color_ram_command = args->write_color_ram_command; + self->color_bits_inverted = args->color_bits_inverted; + self->refresh_time = args->refresh_time * 1000; + self->busy_state = args->busy_state; self->refreshing = false; - self->milliseconds_per_frame = seconds_per_frame * 1000; - self->chip_select = chip_select ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED; + self->milliseconds_per_frame = args->seconds_per_frame * 1000; + self->chip_select = args->always_toggle_chip_select ? CHIP_SELECT_TOGGLE_EVERY_BYTE : CHIP_SELECT_UNTOUCHED; self->grayscale = grayscale; - self->start_sequence = start_sequence; - self->start_sequence_len = start_sequence_len; - self->start_up_time_ms = start_up_time * 1000; - self->stop_sequence = stop_sequence; - self->stop_sequence_len = stop_sequence_len; - self->refresh_sequence = refresh_sequence; - self->refresh_sequence_len = refresh_sequence_len; + self->start_sequence = args->start_sequence; + self->start_sequence_len = args->start_sequence_len; + self->start_up_time_ms = args->start_up_time * 1000; + self->stop_sequence = args->stop_sequence; + self->stop_sequence_len = args->stop_sequence_len; + self->refresh_sequence = args->refresh_sequence; + self->refresh_sequence_len = args->refresh_sequence_len; self->busy.base.type = &mp_type_NoneType; - self->two_byte_sequence_length = two_byte_sequence_length; - if (busy_pin != NULL) { + self->two_byte_sequence_length = args->two_byte_sequence_length; + if (args->busy_pin != NULL) { self->busy.base.type = &digitalio_digitalinout_type; - common_hal_digitalio_digitalinout_construct(&self->busy, busy_pin); - common_hal_never_reset_pin(busy_pin); + common_hal_digitalio_digitalinout_construct(&self->busy, args->busy_pin); + common_hal_never_reset_pin(args->busy_pin); } // Clear the color memory if it isn't in use. - if (highlight_color == 0x00 && write_color_ram_command != NO_COMMAND) { + if (args->highlight_color == 0x00 && args->highlight_color2 == 0x00 && args->write_color_ram_command != NO_COMMAND) { // TODO: Clear } @@ -151,7 +153,10 @@ static void send_command_sequence(epaperdisplay_epaperdisplay_obj_t *self, data_size = ((data_size & ~DELAY) << 8) + *(cmd + 2); data = cmd + 3; } - displayio_display_bus_begin_transaction(&self->bus); + while (!displayio_display_bus_begin_transaction(&self->bus) && + !mp_hal_is_interrupted()) { + RUN_BACKGROUND_TASKS; + } self->bus.send(self->bus.bus, DISPLAY_COMMAND, self->chip_select, cmd, 1); self->bus.send(self->bus.bus, DISPLAY_DATA, self->chip_select, data, data_size); displayio_display_bus_end_transaction(&self->bus); @@ -189,6 +194,10 @@ static void epaperdisplay_epaperdisplay_start_refresh(epaperdisplay_epaperdispla // Can't acquire display bus; skip updating this display. Try next display. return; } + if (!displayio_display_core_start_refresh(&self->core)) { + // Refresh on this display already in progress. + return; + } // run start sequence self->bus.bus_reset(self->bus.bus); @@ -199,7 +208,6 @@ static void epaperdisplay_epaperdisplay_start_refresh(epaperdisplay_epaperdispla if (mp_hal_is_interrupted()) { return; } - displayio_display_core_start_refresh(&self->core); } uint32_t common_hal_epaperdisplay_epaperdisplay_get_time_to_refresh(epaperdisplay_epaperdisplay_obj_t *self) { @@ -306,7 +314,10 @@ static bool epaperdisplay_epaperdisplay_refresh_area(epaperdisplay_epaperdisplay if (pass == 1) { write_command = self->write_color_ram_command; } - displayio_display_bus_begin_transaction(&self->bus); + if (!displayio_display_bus_begin_transaction(&self->bus)) { + // Display bus not available now. + return false; + } self->bus.send(self->bus.bus, DISPLAY_COMMAND, self->chip_select, &write_command, 1); displayio_display_bus_end_transaction(&self->bus); @@ -383,7 +394,9 @@ static bool _clean_area(epaperdisplay_epaperdisplay_obj_t *self) { memset(buffer, 0x77, width / 2); uint8_t write_command = self->write_black_ram_command; - displayio_display_bus_begin_transaction(&self->bus); + if (displayio_display_bus_begin_transaction(&self->bus)) { + return false; + } self->bus.send(self->bus.bus, DISPLAY_COMMAND, self->chip_select, &write_command, 1); displayio_display_bus_end_transaction(&self->bus); diff --git a/shared-module/fourwire/FourWire.c b/shared-module/fourwire/FourWire.c index 0a168fa1563fc..d87cb0cec0f95 100644 --- a/shared-module/fourwire/FourWire.c +++ b/shared-module/fourwire/FourWire.c @@ -11,13 +11,15 @@ #include "py/gc.h" #include "shared-bindings/busio/SPI.h" #include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-bindings/digitalio/DigitalInOutProtocol.h" #include "shared-bindings/microcontroller/Pin.h" #include "shared-bindings/microcontroller/__init__.h" #include "shared-bindings/time/__init__.h" +#include "supervisor/port.h" void common_hal_fourwire_fourwire_construct(fourwire_fourwire_obj_t *self, - busio_spi_obj_t *spi, const mcu_pin_obj_t *command, - const mcu_pin_obj_t *chip_select, const mcu_pin_obj_t *reset, uint32_t baudrate, + busio_spi_obj_t *spi, mp_obj_t command, + mp_obj_t chip_select, mp_obj_t reset, uint32_t baudrate, uint8_t polarity, uint8_t phase) { self->bus = spi; @@ -27,31 +29,27 @@ void common_hal_fourwire_fourwire_construct(fourwire_fourwire_obj_t *self, self->polarity = polarity; self->phase = phase; + // Allocate the pins in the same place as self. + bool use_port_allocation = !gc_alloc_possible() || !gc_ptr_on_heap(self); - self->command.base.type = &mp_type_NoneType; - if (command != NULL) { - self->command.base.type = &digitalio_digitalinout_type; - common_hal_digitalio_digitalinout_construct(&self->command, command); - common_hal_digitalio_digitalinout_switch_to_output(&self->command, true, DRIVE_MODE_PUSH_PULL); + self->command = digitalinout_protocol_from_pin(command, MP_QSTR_command, true, use_port_allocation, &self->own_command); + if (self->command != mp_const_none) { + digitalinout_protocol_switch_to_output(self->command, true, DRIVE_MODE_PUSH_PULL); common_hal_never_reset_pin(command); } - self->reset.base.type = &mp_type_NoneType; - if (reset != NULL) { - self->reset.base.type = &digitalio_digitalinout_type; - common_hal_digitalio_digitalinout_construct(&self->reset, reset); - common_hal_digitalio_digitalinout_switch_to_output(&self->reset, true, DRIVE_MODE_PUSH_PULL); + + self->reset = digitalinout_protocol_from_pin(reset, MP_QSTR_reset, true, use_port_allocation, &self->own_reset); + if (self->reset != mp_const_none) { + digitalinout_protocol_switch_to_output(self->reset, true, DRIVE_MODE_PUSH_PULL); common_hal_never_reset_pin(reset); common_hal_fourwire_fourwire_reset(self); } - self->chip_select.base.type = &mp_type_NoneType; - if (chip_select != NULL) { - self->chip_select.base.type = &digitalio_digitalinout_type; - common_hal_digitalio_digitalinout_construct(&self->chip_select, chip_select); - common_hal_digitalio_digitalinout_switch_to_output(&self->chip_select, true, DRIVE_MODE_PUSH_PULL); + self->chip_select = digitalinout_protocol_from_pin(chip_select, MP_QSTR_chip_select, true, use_port_allocation, &self->own_chip_select); + if (self->chip_select != mp_const_none) { + digitalinout_protocol_switch_to_output(self->chip_select, true, DRIVE_MODE_PUSH_PULL); common_hal_never_reset_pin(chip_select); } - } void common_hal_fourwire_fourwire_deinit(fourwire_fourwire_obj_t *self) { @@ -59,19 +57,33 @@ void common_hal_fourwire_fourwire_deinit(fourwire_fourwire_obj_t *self) { common_hal_busio_spi_deinit(self->bus); } - common_hal_reset_pin(self->command.pin); - common_hal_reset_pin(self->chip_select.pin); - common_hal_reset_pin(self->reset.pin); + // Only deinit and free the pins if we own them + if (self->command != mp_const_none && self->own_command) { + digitalinout_protocol_deinit(self->command); + circuitpy_free_obj(self->command); + } + if (self->chip_select != mp_const_none && self->own_chip_select) { + digitalinout_protocol_deinit(self->chip_select); + circuitpy_free_obj(self->chip_select); + } + if (self->reset != mp_const_none && self->own_reset) { + digitalinout_protocol_deinit(self->reset); + circuitpy_free_obj(self->reset); + } } bool common_hal_fourwire_fourwire_reset(mp_obj_t obj) { fourwire_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj); - if (self->reset.base.type == &mp_type_NoneType) { + if (self->reset == mp_const_none) { + return false; + } + if (digitalinout_protocol_set_value(self->reset, false) != 0) { return false; } - common_hal_digitalio_digitalinout_set_value(&self->reset, false); common_hal_mcu_delay_us(1000); - common_hal_digitalio_digitalinout_set_value(&self->reset, true); + if (digitalinout_protocol_set_value(self->reset, true) != 0) { + return false; + } common_hal_mcu_delay_us(1000); return true; } @@ -92,16 +104,23 @@ bool common_hal_fourwire_fourwire_begin_transaction(mp_obj_t obj) { } common_hal_busio_spi_configure(self->bus, self->frequency, self->polarity, self->phase, 8); - if (self->chip_select.base.type != &mp_type_NoneType) { - common_hal_digitalio_digitalinout_set_value(&self->chip_select, false); + if (self->chip_select != mp_const_none) { + // IO Expander CS can fail due to an I2C lock. + if (digitalinout_protocol_set_value(self->chip_select, false) != 0) { + common_hal_busio_spi_unlock(self->bus); + return false; + } } return true; } void common_hal_fourwire_fourwire_send(mp_obj_t obj, display_byte_type_t data_type, display_chip_select_behavior_t chip_select, const uint8_t *data, uint32_t data_length) { + if (data_length == 0) { + return; + } fourwire_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj); - if (self->command.base.type == &mp_type_NoneType) { + if (self->command == mp_const_none) { // When the data/command pin is not specified, we simulate a 9-bit SPI mode, by // adding a data/command bit to every byte, and then splitting the resulting data back // into 8-bit chunks for transmission. If the length of the data being transmitted @@ -133,24 +152,24 @@ void common_hal_fourwire_fourwire_send(mp_obj_t obj, display_byte_type_t data_ty if (bits > 0) { buffer = buffer << (8 - bits); common_hal_busio_spi_write(self->bus, &buffer, 1); - if (self->chip_select.base.type != &mp_type_NoneType) { + if (self->chip_select != mp_const_none) { // toggle CS to discard superfluous bits - common_hal_digitalio_digitalinout_set_value(&self->chip_select, true); + digitalinout_protocol_set_value(self->chip_select, true); common_hal_mcu_delay_us(1); - common_hal_digitalio_digitalinout_set_value(&self->chip_select, false); + digitalinout_protocol_set_value(self->chip_select, false); } } } else { - common_hal_digitalio_digitalinout_set_value(&self->command, data_type == DISPLAY_DATA); + digitalinout_protocol_set_value(self->command, data_type == DISPLAY_DATA); if (chip_select == CHIP_SELECT_TOGGLE_EVERY_BYTE) { // Toggle chip select after each command byte in case the display driver // IC latches commands based on it. for (size_t i = 0; i < data_length; i++) { common_hal_busio_spi_write(self->bus, &data[i], 1); - if (self->chip_select.base.type != &mp_type_NoneType) { - common_hal_digitalio_digitalinout_set_value(&self->chip_select, true); + if (self->chip_select != mp_const_none) { + digitalinout_protocol_set_value(self->chip_select, true); common_hal_mcu_delay_us(1); - common_hal_digitalio_digitalinout_set_value(&self->chip_select, false); + digitalinout_protocol_set_value(self->chip_select, false); } } } else { @@ -161,8 +180,8 @@ void common_hal_fourwire_fourwire_send(mp_obj_t obj, display_byte_type_t data_ty void common_hal_fourwire_fourwire_end_transaction(mp_obj_t obj) { fourwire_fourwire_obj_t *self = MP_OBJ_TO_PTR(obj); - if (self->chip_select.base.type != &mp_type_NoneType) { - common_hal_digitalio_digitalinout_set_value(&self->chip_select, true); + if (self->chip_select != mp_const_none) { + digitalinout_protocol_set_value(self->chip_select, true); } common_hal_busio_spi_unlock(self->bus); } diff --git a/shared-module/fourwire/FourWire.h b/shared-module/fourwire/FourWire.h index 629a426b3b035..c239a3dfde374 100644 --- a/shared-module/fourwire/FourWire.h +++ b/shared-module/fourwire/FourWire.h @@ -6,6 +6,8 @@ #pragma once +#include + #include "common-hal/busio/SPI.h" #include "common-hal/digitalio/DigitalInOut.h" #include "shared-module/displayio/Group.h" @@ -14,10 +16,13 @@ typedef struct { mp_obj_base_t base; busio_spi_obj_t *bus; busio_spi_obj_t inline_bus; - digitalio_digitalinout_obj_t command; - digitalio_digitalinout_obj_t chip_select; - digitalio_digitalinout_obj_t reset; + mp_obj_t command; + mp_obj_t chip_select; + mp_obj_t reset; uint32_t frequency; uint8_t polarity; uint8_t phase; + bool own_command; + bool own_chip_select; + bool own_reset; } fourwire_fourwire_obj_t; diff --git a/shared-module/framebufferio/FramebufferDisplay.c b/shared-module/framebufferio/FramebufferDisplay.c index 4ba2b1325815c..8116f4b0347cb 100644 --- a/shared-module/framebufferio/FramebufferDisplay.c +++ b/shared-module/framebufferio/FramebufferDisplay.c @@ -217,7 +217,10 @@ static void _refresh_display(framebufferio_framebufferdisplay_obj_t *self) { if (!self->bufinfo.buf) { return; } - displayio_display_core_start_refresh(&self->core); + if (!displayio_display_core_start_refresh(&self->core)) { + // Refresh on this display already in progress. + return; + } const displayio_area_t *current_area = _get_refresh_areas(self); if (current_area) { bool transposed = (self->core.rotation == 90 || self->core.rotation == 270); diff --git a/shared-module/hashlib/Hash.c b/shared-module/hashlib/Hash.c index a454966d99361..b7e966e951b5c 100644 --- a/shared-module/hashlib/Hash.c +++ b/shared-module/hashlib/Hash.c @@ -13,6 +13,9 @@ void common_hal_hashlib_hash_update(hashlib_hash_obj_t *self, const uint8_t *dat if (self->hash_type == MBEDTLS_SSL_HASH_SHA1) { mbedtls_sha1_update_ret(&self->sha1, data, datalen); return; + } else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { + mbedtls_sha256_update_ret(&self->sha256, data, datalen); + return; } } @@ -27,12 +30,19 @@ void common_hal_hashlib_hash_digest(hashlib_hash_obj_t *self, uint8_t *data, siz mbedtls_sha1_clone(©, &self->sha1); mbedtls_sha1_finish_ret(&self->sha1, data); mbedtls_sha1_clone(&self->sha1, ©); + } else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { + mbedtls_sha256_context copy; + mbedtls_sha256_clone(©, &self->sha256); + mbedtls_sha256_finish_ret(&self->sha256, data); + mbedtls_sha256_clone(&self->sha256, ©); } } size_t common_hal_hashlib_hash_get_digest_size(hashlib_hash_obj_t *self) { if (self->hash_type == MBEDTLS_SSL_HASH_SHA1) { return 20; + } else if (self->hash_type == MBEDTLS_SSL_HASH_SHA256) { + return 32; } return 0; } diff --git a/shared-module/hashlib/Hash.h b/shared-module/hashlib/Hash.h index ccc82037cb7aa..f3c2979e59c85 100644 --- a/shared-module/hashlib/Hash.h +++ b/shared-module/hashlib/Hash.h @@ -7,11 +7,13 @@ #pragma once #include "mbedtls/sha1.h" +#include "mbedtls/sha256.h" typedef struct { mp_obj_base_t base; union { mbedtls_sha1_context sha1; + mbedtls_sha256_context sha256; }; // Of MBEDTLS_SSL_HASH_* uint8_t hash_type; diff --git a/shared-module/hashlib/__init__.c b/shared-module/hashlib/__init__.c index be3a9f1895964..f9bc787d49f16 100644 --- a/shared-module/hashlib/__init__.c +++ b/shared-module/hashlib/__init__.c @@ -16,6 +16,11 @@ bool common_hal_hashlib_new(hashlib_hash_obj_t *self, const char *algorithm) { mbedtls_sha1_init(&self->sha1); mbedtls_sha1_starts_ret(&self->sha1); return true; + } else if (strcmp(algorithm, "sha256") == 0) { + self->hash_type = MBEDTLS_SSL_HASH_SHA256; + mbedtls_sha256_init(&self->sha256); + mbedtls_sha256_starts_ret(&self->sha256, 0); + return true; } return false; } diff --git a/shared-module/hashlib/__init__.h b/shared-module/hashlib/__init__.h index f72882a1c03b3..847bd8a834728 100644 --- a/shared-module/hashlib/__init__.h +++ b/shared-module/hashlib/__init__.h @@ -13,4 +13,9 @@ #define mbedtls_sha1_starts_ret mbedtls_sha1_starts #define mbedtls_sha1_update_ret mbedtls_sha1_update #define mbedtls_sha1_finish_ret mbedtls_sha1_finish + +#define mbedtls_sha256_starts_ret mbedtls_sha256_starts +#define mbedtls_sha256_update_ret mbedtls_sha256_update +#define mbedtls_sha256_finish_ret mbedtls_sha256_finish + #endif diff --git a/shared-module/i2cioexpander/IOExpander.c b/shared-module/i2cioexpander/IOExpander.c new file mode 100644 index 0000000000000..f4aac5c053245 --- /dev/null +++ b/shared-module/i2cioexpander/IOExpander.c @@ -0,0 +1,196 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/i2cioexpander/IOExpander.h" +#include "shared-bindings/i2cioexpander/IOPin.h" + +#include + +#include "py/gc.h" +#include "py/runtime.h" +#include "shared-bindings/busio/I2C.h" +#include "supervisor/port.h" + +void common_hal_i2cioexpander_ioexpander_construct( + i2cioexpander_ioexpander_obj_t *self, + mp_obj_t i2c, + uint8_t address, + uint8_t num_pins, + uint16_t set_value_reg, + uint16_t get_value_reg, + uint16_t set_direction_reg) { + + // Store the I2C bus + self->i2c = (busio_i2c_obj_t *)i2c; + self->address = address; + self->num_pins = num_pins; + self->output_value = 0; + self->output_mask = 0; + + // Parse optional register addresses + self->has_set_value = (set_value_reg != NO_REGISTER); + if (self->has_set_value) { + self->set_value_reg = set_value_reg; + } + + self->has_get_value = (get_value_reg != NO_REGISTER); + if (self->has_get_value) { + self->get_value_reg = get_value_reg; + } + + self->has_set_direction = (set_direction_reg != NO_REGISTER); + if (self->has_set_direction) { + self->set_direction_reg = set_direction_reg; + } + + bool allocate_in_port_heap = !gc_alloc_possible() || !gc_ptr_on_heap(self); + + // Allocate tuple with space for pin objects in items[] + size_t tuple_size = offsetof(mp_obj_tuple_t, items) + sizeof(mp_obj_t) * num_pins; + mp_obj_tuple_t *pins_tuple = allocate_in_port_heap ? port_malloc(tuple_size, false) : m_malloc(tuple_size); + pins_tuple->base.type = &mp_type_tuple; + pins_tuple->len = num_pins; + + // Create IOPin objects for each pin + size_t pin_size = sizeof(i2cioexpander_iopin_obj_t); + for (uint8_t i = 0; i < num_pins; i++) { + i2cioexpander_iopin_obj_t *pin = allocate_in_port_heap ? port_malloc(pin_size, false) : m_malloc(pin_size); + pin->base.type = &i2cioexpander_iopin_type; + i2cioexpander_iopin_construct(pin, self, i); + pins_tuple->items[i] = MP_OBJ_FROM_PTR(pin); + } + + self->pins = pins_tuple; +} + +void common_hal_i2cioexpander_ioexpander_deinit(i2cioexpander_ioexpander_obj_t *self) { + if (gc_alloc_possible() && !gc_ptr_on_heap(self)) { + mp_raise_RuntimeError(MP_ERROR_TEXT("Cannot deinitialize board IOExpander")); + } + for (uint8_t i = 0; i < self->num_pins; i++) { + circuitpy_free_obj(self->pins->items[i]); + } + circuitpy_free_obj(self->pins); + self->i2c = NULL; +} + +bool common_hal_i2cioexpander_ioexpander_deinited(i2cioexpander_ioexpander_obj_t *self) { + return self->i2c == NULL; +} + +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_get_input_value(i2cioexpander_ioexpander_obj_t *self, size_t *value) { + uint8_t buffer[2]; + uint8_t num_bytes = (self->num_pins > 8) ? 2 : 1; + + while (!common_hal_busio_i2c_try_lock(self->i2c)) { + RUN_BACKGROUND_TASKS; + } + + mp_negative_errno_t result; + if (self->has_get_value) { + // Send register address then read + result = common_hal_busio_i2c_write_read( + self->i2c, self->address, &self->get_value_reg, 1, buffer, num_bytes); + } else { + // Read directly without register address + result = common_hal_busio_i2c_read(self->i2c, self->address, buffer, num_bytes); + } + common_hal_busio_i2c_unlock(self->i2c); + + if (result != 0) { + return result; + } + + if (num_bytes == 2) { + *value = buffer[0] | (buffer[1] << 8); + } else { + *value = buffer[0]; + } + return 0; +} + +void common_hal_i2cioexpander_ioexpander_get_output_value(i2cioexpander_ioexpander_obj_t *self, size_t *value) { + *value = self->output_value; +} + +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_set_output_value(i2cioexpander_ioexpander_obj_t *self, size_t value) { + uint8_t buffer[5]; + uint8_t num_bytes = 0; + + // Add register address if provided + if (self->has_set_value) { + buffer[num_bytes++] = self->set_value_reg; + } + + size_t current_value = self->output_value; + if (current_value == value) { + return 0; + } + size_t diff = current_value ^ value; + + // Add value byte(s) but only if a high bit is changed + buffer[num_bytes++] = value & 0xFF; + if (self->num_pins > 8 && (diff >> 8) != 0) { + buffer[num_bytes++] = (value >> 8) & 0xFF; + } + if (self->num_pins > 16 && (diff >> 16) != 0) { + buffer[num_bytes++] = (value >> 16) & 0xFF; + } + if (self->num_pins > 24 && (diff >> 24) != 0) { + buffer[num_bytes++] = (value >> 24) & 0xFF; + } + + if (!common_hal_busio_i2c_try_lock(self->i2c)) { + return -MP_EBUSY; + } + + mp_negative_errno_t result = common_hal_busio_i2c_write(self->i2c, self->address, buffer, num_bytes); + common_hal_busio_i2c_unlock(self->i2c); + if (result == 0) { + self->output_value = value; + } + return result; +} + +void common_hal_i2cioexpander_ioexpander_get_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t *mask) { + *mask = self->output_mask; +} + +mp_negative_errno_t common_hal_i2cioexpander_ioexpander_set_output_mask(i2cioexpander_ioexpander_obj_t *self, size_t mask) { + self->output_mask = mask; + + // Only write to device if direction register is provided + if (!self->has_set_direction) { + return 0; + } + + uint8_t buffer[3]; + uint8_t num_bytes = 0; + + // Add register address + buffer[num_bytes++] = self->set_direction_reg; + + // Invert the mask so 0 indicates output. We taken 1 for output to match output enable. + size_t inverted_mask = ~mask; + + // Add mask byte(s) + buffer[num_bytes++] = inverted_mask & 0xFF; + if (self->num_pins > 8) { + buffer[num_bytes++] = (inverted_mask >> 8) & 0xFF; + } + + if (!common_hal_busio_i2c_try_lock(self->i2c)) { + return -MP_EBUSY; + } + + mp_negative_errno_t result = common_hal_busio_i2c_write(self->i2c, self->address, buffer, num_bytes); + common_hal_busio_i2c_unlock(self->i2c); + return result; +} + +mp_obj_t common_hal_i2cioexpander_ioexpander_get_pins(i2cioexpander_ioexpander_obj_t *self) { + return MP_OBJ_FROM_PTR(self->pins); +} diff --git a/shared-module/i2cioexpander/IOExpander.h b/shared-module/i2cioexpander/IOExpander.h new file mode 100644 index 0000000000000..e225bfc75c778 --- /dev/null +++ b/shared-module/i2cioexpander/IOExpander.h @@ -0,0 +1,41 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" +#include "py/objtuple.h" +#include "shared-bindings/busio/I2C.h" + +#define NO_REGISTER (0x100) + +typedef struct { + mp_obj_base_t base; + busio_i2c_obj_t *i2c; + uint8_t address; + uint8_t num_pins; + uint8_t set_value_reg; + uint8_t get_value_reg; + uint8_t set_direction_reg; + size_t output_value; + size_t output_mask; + bool has_set_value; + bool has_get_value; + bool has_set_direction; + mp_obj_tuple_t *pins; +} i2cioexpander_ioexpander_obj_t; + +void common_hal_i2cioexpander_ioexpander_construct( + i2cioexpander_ioexpander_obj_t *self, + mp_obj_t i2c, + uint8_t address, + uint8_t num_pins, + uint16_t set_value_reg, + uint16_t get_value_reg, + uint16_t set_direction_reg); diff --git a/shared-module/i2cioexpander/IOPin.c b/shared-module/i2cioexpander/IOPin.c new file mode 100644 index 0000000000000..04642623b3dc0 --- /dev/null +++ b/shared-module/i2cioexpander/IOPin.c @@ -0,0 +1,148 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/i2cioexpander/IOPin.h" +#include "shared-bindings/i2cioexpander/IOExpander.h" + +#include "py/runtime.h" + +mp_negative_errno_t i2cioexpander_iopin_construct( + i2cioexpander_iopin_obj_t *self, + i2cioexpander_ioexpander_obj_t *expander, + uint8_t pin_number) { + + if (pin_number >= expander->num_pins) { + return MP_EINVAL; // Reusing this for "invalid pin" + } + + self->expander = expander; + self->pin_number = pin_number; + self->direction = DIRECTION_INPUT; + + return 0; +} + +void common_hal_i2cioexpander_iopin_deinit(i2cioexpander_iopin_obj_t *self) { + // Switch to input on deinit. + common_hal_i2cioexpander_iopin_switch_to_input(self, PULL_NONE); +} + +bool common_hal_i2cioexpander_iopin_deinited(i2cioexpander_iopin_obj_t *self) { + return self->expander == NULL || common_hal_i2cioexpander_ioexpander_deinited(self->expander); +} + +digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_input( + i2cioexpander_iopin_obj_t *self, + digitalio_pull_t pull) { + + if (pull != PULL_NONE) { + // IO expanders typically don't support pull resistors + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL + return DIGITALINOUT_INVALID_PULL; + #endif + } + + self->direction = DIRECTION_INPUT; + + // Clear the output mask bit for this pin + size_t new_mask = self->expander->output_mask & ~(1 << self->pin_number); + common_hal_i2cioexpander_ioexpander_set_output_mask(self->expander, new_mask); + + return DIGITALINOUT_OK; +} + +digitalinout_result_t common_hal_i2cioexpander_iopin_switch_to_output( + i2cioexpander_iopin_obj_t *self, + bool value, + digitalio_drive_mode_t drive_mode) { + + if (drive_mode != DRIVE_MODE_PUSH_PULL) { + // IO expanders typically only support push-pull + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE + return DIGITALINOUT_INVALID_DRIVE_MODE; + #endif + } + + self->direction = DIRECTION_OUTPUT; + + // Set the value first + size_t new_value = self->expander->output_value; + if (value) { + new_value |= (1 << self->pin_number); + } else { + new_value &= ~(1 << self->pin_number); + } + common_hal_i2cioexpander_ioexpander_set_output_value(self->expander, new_value); + + // Set the output mask bit for this pin + size_t new_mask = self->expander->output_mask | (1 << self->pin_number); + common_hal_i2cioexpander_ioexpander_set_output_mask(self->expander, new_mask); + + return DIGITALINOUT_OK; +} + +digitalio_direction_t common_hal_i2cioexpander_iopin_get_direction(i2cioexpander_iopin_obj_t *self) { + return self->direction; +} + +mp_negative_errno_t common_hal_i2cioexpander_iopin_set_value(i2cioexpander_iopin_obj_t *self, bool value) { + size_t current_value; + common_hal_i2cioexpander_ioexpander_get_output_value(self->expander, ¤t_value); + size_t new_value; + if (value) { + new_value = current_value | (1 << self->pin_number); + } else { + new_value = current_value & ~(1 << self->pin_number); + } + if (new_value != current_value) { + return common_hal_i2cioexpander_ioexpander_set_output_value(self->expander, new_value); + } + return 0; +} + +mp_negative_errno_t common_hal_i2cioexpander_iopin_get_value(i2cioexpander_iopin_obj_t *self, bool *value) { + size_t full_value; + mp_negative_errno_t result = common_hal_i2cioexpander_ioexpander_get_input_value(self->expander, &full_value); + if (result != 0) { + return result; + } + *value = (full_value & (1 << self->pin_number)) != 0; + return 0; +} + +digitalinout_result_t common_hal_i2cioexpander_iopin_set_drive_mode( + i2cioexpander_iopin_obj_t *self, + digitalio_drive_mode_t drive_mode) { + + if (drive_mode != DRIVE_MODE_PUSH_PULL) { + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_DRIVE_MODE + return DIGITALINOUT_INVALID_DRIVE_MODE; + #endif + } + + return DIGITALINOUT_OK; +} + +digitalio_drive_mode_t common_hal_i2cioexpander_iopin_get_drive_mode(i2cioexpander_iopin_obj_t *self) { + return DRIVE_MODE_PUSH_PULL; +} + +digitalinout_result_t common_hal_i2cioexpander_iopin_set_pull( + i2cioexpander_iopin_obj_t *self, + digitalio_pull_t pull) { + + if (pull != PULL_NONE) { + #if CIRCUITPY_DIGITALIO_HAVE_INVALID_PULL + return DIGITALINOUT_INVALID_PULL; + #endif + } + + return DIGITALINOUT_OK; +} + +digitalio_pull_t common_hal_i2cioexpander_iopin_get_pull(i2cioexpander_iopin_obj_t *self) { + return PULL_NONE; +} diff --git a/shared-module/i2cioexpander/IOPin.h b/shared-module/i2cioexpander/IOPin.h new file mode 100644 index 0000000000000..0c4da8a0213fc --- /dev/null +++ b/shared-module/i2cioexpander/IOPin.h @@ -0,0 +1,26 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include + +#include "py/obj.h" +#include "shared-bindings/digitalio/Direction.h" +#include "shared-bindings/digitalio/DriveMode.h" +#include "shared-bindings/digitalio/Pull.h" +#include "shared-bindings/digitalio/DigitalInOut.h" +#include "shared-module/i2cioexpander/IOExpander.h" + +typedef struct { + mp_obj_base_t base; + i2cioexpander_ioexpander_obj_t *expander; + uint8_t pin_number; + digitalio_direction_t direction; +} i2cioexpander_iopin_obj_t; + +extern const mp_obj_type_t i2cioexpander_iopin_type; diff --git a/shared-module/i2cioexpander/__init__.c b/shared-module/i2cioexpander/__init__.c new file mode 100644 index 0000000000000..43e652db897e0 --- /dev/null +++ b/shared-module/i2cioexpander/__init__.c @@ -0,0 +1,7 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +// Nothing now. diff --git a/shared-module/jpegio/JpegDecoder.c b/shared-module/jpegio/JpegDecoder.c index 484ba8e7ccd09..89cb624a8d03f 100644 --- a/shared-module/jpegio/JpegDecoder.c +++ b/shared-module/jpegio/JpegDecoder.c @@ -46,7 +46,7 @@ static void check_jresult(JRESULT j) { msg = MP_ERROR_TEXT("Right format but not supported"); break; case JDR_FMT3: - msg = MP_ERROR_TEXT("Not supported JPEG standard"); + msg = MP_ERROR_TEXT("Unsupported JPEG (may be progressive)"); break; } mp_raise_RuntimeError(msg); diff --git a/shared-module/keypad/EventQueue.c b/shared-module/keypad/EventQueue.c index e5b362a045ddf..3130d8b59a1d7 100644 --- a/shared-module/keypad/EventQueue.c +++ b/shared-module/keypad/EventQueue.c @@ -15,9 +15,14 @@ #define EVENT_SIZE_BYTES (sizeof(uint16_t) + sizeof(mp_obj_t)) -void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events) { +void common_hal_keypad_eventqueue_construct(keypad_eventqueue_obj_t *self, size_t max_events, bool use_gc_allocator) { // Event queue is 16-bit values. - ringbuf_alloc(&self->encoded_events, max_events * EVENT_SIZE_BYTES); + const size_t size = max_events * EVENT_SIZE_BYTES; + if (use_gc_allocator) { + ringbuf_alloc(&self->encoded_events, size); + } else { + ringbuf_init(&self->encoded_events, port_malloc(size, false), size); + } self->overflowed = false; self->event_handler = NULL; } diff --git a/shared-module/keypad/KeyMatrix.c b/shared-module/keypad/KeyMatrix.c index 67a5546853552..28830f591b129 100644 --- a/shared-module/keypad/KeyMatrix.c +++ b/shared-module/keypad/KeyMatrix.c @@ -56,7 +56,7 @@ void common_hal_keypad_keymatrix_construct(keypad_keymatrix_obj_t *self, mp_uint self->columns_to_anodes = columns_to_anodes; self->funcs = &keymatrix_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, true); } void common_hal_keypad_keymatrix_deinit(keypad_keymatrix_obj_t *self) { diff --git a/shared-module/keypad/Keys.c b/shared-module/keypad/Keys.c index 39d50f95e3238..80dcf36e163ad 100644 --- a/shared-module/keypad/Keys.c +++ b/shared-module/keypad/Keys.c @@ -41,7 +41,7 @@ void common_hal_keypad_keys_construct(keypad_keys_obj_t *self, mp_uint_t num_pin self->value_when_pressed = value_when_pressed; self->funcs = &keys_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, true); } diff --git a/shared-module/keypad/ShiftRegisterKeys.c b/shared-module/keypad/ShiftRegisterKeys.c index 97917434ff81b..aa0c14dd36b50 100644 --- a/shared-module/keypad/ShiftRegisterKeys.c +++ b/shared-module/keypad/ShiftRegisterKeys.c @@ -71,7 +71,7 @@ void common_hal_keypad_shiftregisterkeys_construct(keypad_shiftregisterkeys_obj_ self->value_when_pressed = value_when_pressed; self->funcs = &shiftregisterkeys_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, true); } void common_hal_keypad_shiftregisterkeys_deinit(keypad_shiftregisterkeys_obj_t *self) { diff --git a/shared-module/keypad/__init__.c b/shared-module/keypad/__init__.c index 3ffa0433174cf..f1d5d395df789 100644 --- a/shared-module/keypad/__init__.c +++ b/shared-module/keypad/__init__.c @@ -85,14 +85,20 @@ void keypad_deregister_scanner(keypad_scanner_obj_t *scanner) { supervisor_release_lock(&keypad_scanners_linked_list_lock); } -void keypad_construct_common(keypad_scanner_obj_t *self, mp_float_t interval, size_t max_events, uint8_t debounce_threshold) { +void keypad_construct_common(keypad_scanner_obj_t *self, mp_float_t interval, size_t max_events, uint8_t debounce_threshold, bool use_gc_allocator) { size_t key_count = common_hal_keypad_generic_get_key_count(self); - self->debounce_counter = (int8_t *)m_malloc_without_collect(sizeof(int8_t) * key_count); + self->debounce_counter = + use_gc_allocator + ? (int8_t *)m_malloc_without_collect(sizeof(int8_t) * key_count) + : (int8_t *)port_malloc_zero(sizeof(int8_t) * key_count, false); self->interval_ticks = (mp_uint_t)(interval * 1024); // interval * 1000 * (1024/1000) - keypad_eventqueue_obj_t *events = mp_obj_malloc(keypad_eventqueue_obj_t, &keypad_eventqueue_type); - common_hal_keypad_eventqueue_construct(events, max_events); + keypad_eventqueue_obj_t *events = + use_gc_allocator + ? mp_obj_malloc(keypad_eventqueue_obj_t, &keypad_eventqueue_type) + : mp_obj_port_malloc(keypad_eventqueue_obj_t, &keypad_eventqueue_type); + common_hal_keypad_eventqueue_construct(events, max_events, use_gc_allocator); self->events = events; self->debounce_threshold = debounce_threshold; diff --git a/shared-module/keypad/__init__.h b/shared-module/keypad/__init__.h index a7d21753f6625..66fea4878543b 100644 --- a/shared-module/keypad/__init__.h +++ b/shared-module/keypad/__init__.h @@ -39,7 +39,7 @@ void keypad_reset(void); void keypad_register_scanner(keypad_scanner_obj_t *scanner); void keypad_deregister_scanner(keypad_scanner_obj_t *scanner); -void keypad_construct_common(keypad_scanner_obj_t *scanner, mp_float_t interval, size_t max_events, uint8_t debounce_cycles); +void keypad_construct_common(keypad_scanner_obj_t *scanner, mp_float_t interval, size_t max_events, uint8_t debounce_cycles, bool use_gc_allocator); bool keypad_debounce(keypad_scanner_obj_t *self, mp_uint_t key_number, bool current); void keypad_never_reset(keypad_scanner_obj_t *self); diff --git a/shared-module/keypad_demux/DemuxKeyMatrix.c b/shared-module/keypad_demux/DemuxKeyMatrix.c index b90669d772fcd..329f7679d3f76 100644 --- a/shared-module/keypad_demux/DemuxKeyMatrix.c +++ b/shared-module/keypad_demux/DemuxKeyMatrix.c @@ -31,38 +31,45 @@ static mp_uint_t row_column_to_key_number(keypad_demux_demuxkeymatrix_obj_t *sel return row * common_hal_keypad_demux_demuxkeymatrix_get_column_count(self) + column; } -void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold) { - +void common_hal_keypad_demux_demuxkeymatrix_construct(keypad_demux_demuxkeymatrix_obj_t *self, mp_uint_t num_row_addr_pins, const mcu_pin_obj_t *row_addr_pins[], mp_uint_t num_column_pins, const mcu_pin_obj_t *column_pins[], bool columns_to_anodes, bool transpose, mp_float_t interval, size_t max_events, uint8_t debounce_threshold, bool use_gc_allocator) { // the multiplexed pins are outputs so we can set the address for the target row // the sense of the address pins themselves doesn't change with columns_to_anodes // but the value output on the selected row line will be !columns_to_anodes mp_obj_t row_addr_dios[num_row_addr_pins]; for (size_t row = 0; row < num_row_addr_pins; row++) { digitalio_digitalinout_obj_t *dio = - mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); + use_gc_allocator + ? mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type) + : mp_obj_port_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); common_hal_digitalio_digitalinout_construct(dio, row_addr_pins[row]); common_hal_digitalio_digitalinout_switch_to_output(dio, false, DRIVE_MODE_PUSH_PULL); row_addr_dios[row] = dio; } - self->row_addr_digitalinouts = mp_obj_new_tuple(num_row_addr_pins, row_addr_dios); + self->row_addr_digitalinouts = + use_gc_allocator + ? mp_obj_new_tuple(num_row_addr_pins, row_addr_dios) + : mp_obj_new_port_tuple(num_row_addr_pins, row_addr_dios); // the column pins are always inputs, with default state based on columns_to_anodes mp_obj_t column_dios[num_column_pins]; for (size_t column = 0; column < num_column_pins; column++) { digitalio_digitalinout_obj_t *dio = - mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); - dio->base.type = &digitalio_digitalinout_type; + use_gc_allocator + ? mp_obj_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type) + : mp_obj_port_malloc(digitalio_digitalinout_obj_t, &digitalio_digitalinout_type); common_hal_digitalio_digitalinout_construct(dio, column_pins[column]); common_hal_digitalio_digitalinout_switch_to_input(dio, columns_to_anodes ? PULL_UP : PULL_DOWN); column_dios[column] = dio; } - self->column_digitalinouts = mp_obj_new_tuple(num_column_pins, column_dios); - + self->column_digitalinouts = + use_gc_allocator + ? mp_obj_new_tuple(num_column_pins, column_dios) + : mp_obj_new_port_tuple(num_column_pins, column_dios); self->columns_to_anodes = columns_to_anodes; self->transpose = transpose; self->funcs = &keymatrix_funcs; - keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold); + keypad_construct_common((keypad_scanner_obj_t *)self, interval, max_events, debounce_threshold, use_gc_allocator); } void common_hal_keypad_demux_demuxkeymatrix_deinit(keypad_demux_demuxkeymatrix_obj_t *self) { diff --git a/shared-module/lvfontio/OnDiskFont.c b/shared-module/lvfontio/OnDiskFont.c index 0cf65301d2270..efffed6307443 100644 --- a/shared-module/lvfontio/OnDiskFont.c +++ b/shared-module/lvfontio/OnDiskFont.c @@ -48,6 +48,7 @@ static inline void free_memory(lvfontio_ondiskfont_t *self, void *ptr) { // Forward declarations for helper functions static int16_t find_codepoint_slot(lvfontio_ondiskfont_t *self, uint32_t codepoint); +static bool slot_has_active_full_width_partner(lvfontio_ondiskfont_t *self, uint16_t slot); static uint16_t find_free_slot(lvfontio_ondiskfont_t *self, uint32_t codepoint); static FRESULT read_bits(FIL *file, size_t num_bits, uint8_t *byte_val, uint8_t *remaining_bits, uint32_t *result); static FRESULT read_glyph_dimensions(FIL *file, lvfontio_ondiskfont_t *self, uint32_t *advance_width, int32_t *bbox_x, int32_t *bbox_y, uint32_t *bbox_w, uint32_t *bbox_h, uint8_t *byte_val, uint8_t *remaining_bits); @@ -224,7 +225,12 @@ static bool load_font_header(lvfontio_ondiskfont_t *self, FIL *file, size_t *max // Throw away the bitmap bits. read_bits(file, self->header.bits_per_pixel * bbox_w * bbox_h, &byte_val, &remaining_bits, NULL); - if (advances[0] == glyph_advance) { + + if (glyph_advance == 0) { + // Ignore zero-advance glyphs when inferring the terminal cell width. + // Some fonts include placeholders/control glyphs with zero advance, + // which would otherwise skew default_advance_width too small. + } else if (advances[0] == glyph_advance) { advance_count[0]++; } else if (advances[1] == glyph_advance) { advance_count[1]++; @@ -253,9 +259,16 @@ static bool load_font_header(lvfontio_ondiskfont_t *self, FIL *file, size_t *max *max_slots = advance_count[1] * 2 + advance_count[0]; } } + } else { + *max_slots = advance_count[0] + advance_count[1]; } - + if (self->header.default_advance_width == 0) { + self->header.default_advance_width = 1; + } + if (*max_slots == 0) { + *max_slots = 1; + } found_glyf = true; } @@ -343,8 +356,9 @@ static int32_t get_char_id(lvfontio_ondiskfont_t *self, uint32_t codepoint) { for (size_t j = 0; j < self->cmap_ranges[i].entries_count; j++) { // Read code point at the index uint16_t candidate_codepoint_delta; - res = f_read(&self->file, &candidate_codepoint_delta, 2, NULL); - if (res != FR_OK) { + UINT bytes_read; + res = f_read(&self->file, &candidate_codepoint_delta, 2, &bytes_read); + if (res != FR_OK || bytes_read < 2) { return -1; } @@ -573,18 +587,20 @@ int16_t common_hal_lvfontio_ondiskfont_cache_glyph(lvfontio_ondiskfont_t *self, // Check if already cached int16_t existing_slot = find_codepoint_slot(self, codepoint); if (existing_slot >= 0) { - // Glyph is already cached, increment reference count + // Glyph is already cached, increment reference count(s). self->reference_counts[existing_slot]++; // Check if this is a full-width character by looking for a second slot - // with the same codepoint right after this one + // with the same codepoint right after this one, wrapping at the end. + uint16_t next_slot = (existing_slot + 1) % self->max_glyphs; + bool cached_is_full_width = self->codepoints[next_slot] == codepoint; + + if (cached_is_full_width) { + self->reference_counts[next_slot]++; + } + if (is_full_width != NULL) { - if (existing_slot + 1 < self->max_glyphs && - self->codepoints[existing_slot + 1] == codepoint) { - *is_full_width = true; - } else { - *is_full_width = false; - } + *is_full_width = cached_is_full_width; } return existing_slot; @@ -721,12 +737,37 @@ static int16_t find_codepoint_slot(lvfontio_ondiskfont_t *self, uint32_t codepoi for (uint16_t i = 0; i < self->max_glyphs; i++) { int16_t slot = (i + offset) % self->max_glyphs; if (self->codepoints[slot] == codepoint) { + // If this is the second slot of a full-width glyph pair, return the + // first slot so callers always get a canonical index. + if (slot > 0 && self->codepoints[slot - 1] == codepoint) { + return slot - 1; + } return slot; } } return -1; } +static bool slot_has_active_full_width_partner(lvfontio_ondiskfont_t *self, uint16_t slot) { + uint32_t codepoint = self->codepoints[slot]; + if (codepoint == LVFONTIO_INVALID_CODEPOINT) { + return false; + } + + // Don't evict one half of a full-width pair while the other half is still in use. + uint16_t prev_slot = (slot + self->max_glyphs - 1) % self->max_glyphs; + uint16_t next_slot = (slot + 1) % self->max_glyphs; + + if (self->codepoints[prev_slot] == codepoint && self->reference_counts[prev_slot] > 0) { + return true; + } + if (self->codepoints[next_slot] == codepoint && self->reference_counts[next_slot] > 0) { + return true; + } + + return false; +} + static uint16_t find_free_slot(lvfontio_ondiskfont_t *self, uint32_t codepoint) { size_t offset = codepoint % self->max_glyphs; @@ -738,10 +779,11 @@ static uint16_t find_free_slot(lvfontio_ondiskfont_t *self, uint32_t codepoint) } } - // If none found, look for slots with zero reference count, starting at the offset + // If none found, look for slots with zero reference count, starting at the offset. + // Avoid reusing one half of an active full-width glyph pair. for (uint16_t i = 0; i < self->max_glyphs; i++) { int16_t slot = (i + offset) % self->max_glyphs; - if (self->reference_counts[slot] == 0) { + if (self->reference_counts[slot] == 0 && !slot_has_active_full_width_partner(self, slot)) { return slot; } } diff --git a/shared-module/max3421e/Max3421E.c b/shared-module/max3421e/Max3421E.c index 4946e4e7d8dfa..3448cdd4c7596 100644 --- a/shared-module/max3421e/Max3421E.c +++ b/shared-module/max3421e/Max3421E.c @@ -83,7 +83,7 @@ void common_hal_max3421e_max3421e_deinit(max3421e_max3421e_obj_t *self) { // anyway. Don't run background tasks because this function is used by // tuh_task() which is run as a background task. #if CFG_TUSB_OS == OPT_OS_NONE -void osal_task_delay(uint32_t msec) { +void tusb_time_delay_ms_api(uint32_t msec) { uint32_t end_time = common_hal_time_monotonic_ms() + msec; while (common_hal_time_monotonic_ms() < end_time) { if (tuh_callback.prev != NULL) { diff --git a/shared-module/msgpack/__init__.c b/shared-module/msgpack/__init__.c index a98fb02de39dd..7c3e664bb6c1e 100644 --- a/shared-module/msgpack/__init__.c +++ b/shared-module/msgpack/__init__.c @@ -394,7 +394,9 @@ static mp_obj_t unpack(msgpack_stream_t *s, mp_obj_t ext_hook, bool use_list) { size_t len = code & 0b1111; mp_obj_dict_t *d = MP_OBJ_TO_PTR(mp_obj_new_dict(len)); for (size_t i = 0; i < len; i++) { - mp_obj_dict_store(d, unpack(s, ext_hook, use_list), unpack(s, ext_hook, use_list)); + mp_obj_t key = unpack(s, ext_hook, use_list); + mp_obj_t value = unpack(s, ext_hook, use_list); + mp_obj_dict_store(d, key, value); } return MP_OBJ_FROM_PTR(d); } @@ -462,7 +464,9 @@ static mp_obj_t unpack(msgpack_stream_t *s, mp_obj_t ext_hook, bool use_list) { size_t len = read_size(s, code - 0xde + 1); mp_obj_dict_t *d = MP_OBJ_TO_PTR(mp_obj_new_dict(len)); for (size_t i = 0; i < len; i++) { - mp_obj_dict_store(d, unpack(s, ext_hook, use_list), unpack(s, ext_hook, use_list)); + mp_obj_t key = unpack(s, ext_hook, use_list); + mp_obj_t value = unpack(s, ext_hook, use_list); + mp_obj_dict_store(d, key, value); } return MP_OBJ_FROM_PTR(d); } diff --git a/shared-module/os/__init__.c b/shared-module/os/__init__.c index 3594c9bb02877..3a9c0e31f8edf 100644 --- a/shared-module/os/__init__.c +++ b/shared-module/os/__init__.c @@ -17,6 +17,10 @@ #include "py/runtime.h" #include "shared-bindings/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" +#endif + // This provides all VFS related OS functions so that ports can share the code // as needed. @@ -281,4 +285,17 @@ void common_hal_os_utime(const char *path, mp_obj_t times) { mp_vfs_proxy_call(vfs, MP_QSTR_utime, 2, args); } +#if CIRCUITPY_SETTINGS_TOML +mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_) { + vstr_t vstr; + vstr_init(&vstr, 64); + + settings_err_t result = settings_get_raw_vstr(key, &vstr); + if (result == SETTINGS_OK) { + return mp_obj_new_str_from_vstr(&vstr); + } + return default_; +} +#endif + MP_REGISTER_ROOT_POINTER(const char *cwd_path); diff --git a/shared-module/os/__init__.h b/shared-module/os/__init__.h index d44bae1ffc560..830f762950975 100644 --- a/shared-module/os/__init__.h +++ b/shared-module/os/__init__.h @@ -6,26 +6,5 @@ #pragma once -typedef enum { - GETENV_OK = 0, - GETENV_ERR_OPEN, - GETENV_ERR_UNICODE, - GETENV_ERR_LENGTH, - GETENV_ERR_NOT_FOUND, - GETENV_ERR_UNEXPECTED = 0xff00, // logical or'd with the byte value -} os_getenv_err_t; - -// Allocation free version that returns the full length of the value. -// If it fits, the return value is 0-terminated. The passed in buffer -// may be modified even if an error is returned. Allocation free. -// An error that is not 'open' or 'not found' is printed on the repl. -os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len); - -// Returns GETENV_OK and sets value to the read value. Returns -// GETENV_ERR_... if the value was not numeric. allocation-free. -// If any error code is returned, value is guaranteed not modified -// An error that is not 'open' or 'not found' is printed on the repl. -os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value); - // Not made available to the VM but used by other modules to normalize paths. const char *common_hal_os_path_abspath(const char *path); diff --git a/shared-module/os/getenv.c b/shared-module/os/getenv.c deleted file mode 100644 index c7bfadf3418d8..0000000000000 --- a/shared-module/os/getenv.c +++ /dev/null @@ -1,417 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -// These functions are separate from __init__.c so that os.getenv() can be -// tested in the unix "coverage" build, without bringing in "our" os module - -#include -#include -#include - -#include "shared-bindings/os/__init__.h" -#include "shared-module/os/__init__.h" - -#include "py/gc.h" -#include "py/misc.h" -#include "py/mpstate.h" -#include "py/mpprint.h" -#include "py/objstr.h" -#include "py/parsenum.h" -#include "py/runtime.h" -#include "supervisor/filesystem.h" - -#define GETENV_PATH "/settings.toml" - -#include "extmod/vfs.h" -#include "extmod/vfs_fat.h" - -#if CIRCUITPY_OS_GETENV -typedef FIL file_arg; -static bool open_file(const char *name, file_arg *active_file) { - #if defined(UNIX) - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_obj_t file_obj = mp_call_function_2(MP_OBJ_FROM_PTR(&mp_builtin_open_obj), mp_obj_new_str(name, strlen(name)), MP_ROM_QSTR(MP_QSTR_rb)); - mp_arg_validate_type(file_obj, &mp_type_vfs_fat_fileio, MP_QSTR_file); - pyb_file_obj_t *file = MP_OBJ_TO_PTR(file_obj); - *active_file = file->fp; - nlr_pop(); - return true; - } else { - return false; - } - #else - fs_user_mount_t *fs_mount = filesystem_circuitpy(); - if (fs_mount == NULL) { - return false; - } - FATFS *fatfs = &fs_mount->fatfs; - FRESULT result = f_open(fatfs, active_file, name, FA_READ); - return result == FR_OK; - #endif -} -static void close_file(file_arg *active_file) { - // nothing -} -static bool is_eof(file_arg *active_file) { - return f_eof(active_file) || f_error(active_file); -} - -// Return 0 if there is no next character (EOF). -static uint8_t get_next_byte(FIL *active_file) { - uint8_t character = 0; - UINT quantity_read; - // If there's an error or quantity_read is 0, character will remain 0. - f_read(active_file, &character, 1, &quantity_read); - return character; -} -static void seek_eof(file_arg *active_file) { - f_lseek(active_file, f_size(active_file)); -} - -// For a fixed buffer, record the required size rather than throwing -static void vstr_add_byte_nonstd(vstr_t *vstr, byte b) { - if (!vstr->fixed_buf || vstr->alloc > vstr->len) { - vstr_add_byte(vstr, b); - } else { - vstr->len++; - } -} - -// For a fixed buffer, record the required size rather than throwing -static void vstr_add_char_nonstd(vstr_t *vstr, unichar c) { - size_t ulen = - (c < 0x80) ? 1 : - (c < 0x800) ? 2 : - (c < 0x10000) ? 3 : 4; - if (!vstr->fixed_buf || vstr->alloc > vstr->len + ulen) { - vstr_add_char(vstr, c); - } else { - vstr->len += ulen; - } -} - -static void next_line(file_arg *active_file) { - uint8_t character; - do { - character = get_next_byte(active_file); - } while (character != 0 && character != '\n'); -} - -// Discard whitespace, except for newlines, returning the next character after the whitespace. -// Return 0 if there is no next character (EOF). -static uint8_t consume_whitespace(file_arg *active_file) { - uint8_t character; - do { - character = get_next_byte(active_file); - } while (character != '\n' && character != 0 && unichar_isspace(character)); - return character; -} - -// Starting at the start of a new line, determines if the key matches the given -// key. -// -// If result is true, the key matches and file pointer is pointing just after the "=". -// If the result is false, the key does NOT match and the file pointer is -// pointing at the start of the next line, if any -static bool key_matches(file_arg *active_file, const char *key) { - uint8_t character; - character = consume_whitespace(active_file); - if (character == '[' || character == 0) { - seek_eof(active_file); - return false; - } - while (*key) { - if (character != *key++) { - // A character didn't match the key, so it's not a match - // If the non-matching char was not the end of the line, - // then consume the rest of the line - if (character != '\n') { - next_line(active_file); - } - return false; - } - character = get_next_byte(active_file); - } - // the next character could be whitespace; consume if necessary - if (unichar_isspace(character)) { - character = consume_whitespace(active_file); - } - // If we're not looking at the "=" then the key didn't match - if (character != '=') { - // A character didn't match the key, so it's not a match - // If the non-matching char was not the end of the line, - // then consume the rest of the line - if (character != '\n') { - next_line(active_file); - } - return false; - } - return true; -} - -static os_getenv_err_t read_unicode_escape(file_arg *active_file, int sz, vstr_t *buf) { - char hex_buf[sz + 1]; - for (int i = 0; i < sz; i++) { - hex_buf[i] = get_next_byte(active_file); - } - hex_buf[sz] = 0; - char *end; - unsigned long c = strtoul(hex_buf, &end, 16); - if (end != &hex_buf[sz]) { - return GETENV_ERR_UNEXPECTED | *end; - } - if (c >= 0x110000) { - return GETENV_ERR_UNICODE; - } - vstr_add_char_nonstd(buf, c); - return GETENV_OK; -} - -// Read a quoted string -static os_getenv_err_t read_string_value(file_arg *active_file, vstr_t *buf) { - while (true) { - int character = get_next_byte(active_file); - switch (character) { - case 0: - case '\n': - return GETENV_ERR_UNEXPECTED | character; - - case '"': - character = consume_whitespace(active_file); - switch (character) { - case '#': - next_line(active_file); - MP_FALLTHROUGH; - case 0: - case '\n': - return GETENV_OK; - default: - return GETENV_ERR_UNEXPECTED | character; - } - - case '\\': - character = get_next_byte(active_file); - switch (character) { - case 0: - case '\n': - return GETENV_ERR_UNEXPECTED | character; - case 'b': - character = '\b'; - break; - case 'r': - character = '\r'; - break; - case 'n': - character = '\n'; - break; - case 't': - character = '\t'; - break; - case 'v': - character = '\v'; - break; - case 'f': - character = '\f'; - break; - case 'U': - case 'u': { - int sz = (character == 'u') ? 4 : 8; - os_getenv_err_t res; - res = read_unicode_escape(active_file, sz, buf); - if (res != GETENV_OK) { - return res; - } - continue; - } - // default falls through, other escaped characters - // represent themselves - } - MP_FALLTHROUGH; - default: - vstr_add_byte_nonstd(buf, character); - } - } -} - -// Read a numeric value (non-quoted value) as a string -static os_getenv_err_t read_bare_value(file_arg *active_file, vstr_t *buf, int first_character) { - int character = first_character; - while (true) { - switch (character) { - case 0: - case '\n': - return GETENV_OK; - case '#': - next_line(active_file); - return GETENV_OK; - default: - vstr_add_byte_nonstd(buf, character); - } - character = get_next_byte(active_file); - } -} - -static mp_int_t read_value(file_arg *active_file, vstr_t *buf, bool *quoted) { - uint8_t character; - character = consume_whitespace(active_file); - *quoted = (character == '"'); - - if (*quoted) { - return read_string_value(active_file, buf); - } else { - return read_bare_value(active_file, buf, character); - } -} - -static os_getenv_err_t os_getenv_vstr(const char *path, const char *key, vstr_t *buf, bool *quoted) { - file_arg active_file; - if (!open_file(path, &active_file)) { - return GETENV_ERR_OPEN; - } - - os_getenv_err_t result = GETENV_ERR_NOT_FOUND; - while (!is_eof(&active_file)) { - if (key_matches(&active_file, key)) { - result = read_value(&active_file, buf, quoted); - break; - } - } - close_file(&active_file); - return result; -} - -static os_getenv_err_t os_getenv_buf_terminated(const char *key, char *value, size_t value_len, bool *quoted) { - vstr_t buf; - vstr_init_fixed_buf(&buf, value_len, value); - os_getenv_err_t result = os_getenv_vstr(GETENV_PATH, key, &buf, quoted); - - if (result == GETENV_OK) { - vstr_add_byte_nonstd(&buf, 0); - memcpy(value, buf.buf, MIN(buf.len, value_len)); - if (buf.len > value_len) { // this length includes trailing NUL - result = GETENV_ERR_LENGTH; - } - } - return result; -} - -static void print_dont_raise(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...) { - va_list argptr; - va_start(argptr, fmt); - mp_vcprintf(&mp_plat_print, fmt, argptr); - mp_printf(&mp_plat_print, "\n"); - va_end(argptr); -} - -static void handle_getenv_error(os_getenv_err_t error, void (*handle)(const mp_obj_type_t *exc_type, mp_rom_error_text_t fmt, ...)) { - if (error == GETENV_OK) { - return; - } - if (error & GETENV_ERR_UNEXPECTED) { - byte character = (error & 0xff); - char buf[8]; - vstr_t vstr; - vstr_init_fixed_buf(&vstr, sizeof(buf), buf); - mp_print_t print = { .data = &vstr, .print_strn = (mp_print_strn_t)vstr_add_strn }; - - if (character) { - mp_str_print_quoted(&print, &character, 1, true); - } else { - mp_str_print_quoted(&print, (byte *)"EOF", 3, true); - } - handle(&mp_type_ValueError, MP_ERROR_TEXT("Invalid byte %.*s"), vstr.len, vstr.buf); - } else { - switch (error) { - case GETENV_ERR_OPEN: - handle(&mp_type_ValueError, MP_ERROR_TEXT("%S"), MP_ERROR_TEXT("File not found")); - break; - case GETENV_ERR_UNICODE: - handle(&mp_type_ValueError, MP_ERROR_TEXT("%S"), MP_ERROR_TEXT("Invalid unicode escape")); - break; - case GETENV_ERR_NOT_FOUND: - handle(&mp_type_ValueError, MP_ERROR_TEXT("%S"), MP_ERROR_TEXT("Key not found")); - break; - default: - handle(&mp_type_RuntimeError, MP_ERROR_TEXT("%S"), MP_ERROR_TEXT("Internal error")); - break; - } - } -} - -static void common_hal_os_getenv_showerr(const char *key, os_getenv_err_t result) { - if (result != GETENV_OK && result != GETENV_ERR_OPEN && result != GETENV_ERR_NOT_FOUND) { - mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("An error occurred while retrieving '%s':\n"), key); - handle_getenv_error(result, print_dont_raise); - } -} - -static -os_getenv_err_t common_hal_os_getenv_str_inner(const char *key, char *value, size_t value_len) { - bool quoted; - os_getenv_err_t result = os_getenv_buf_terminated(key, value, value_len, "ed); - if (result == GETENV_OK && !quoted) { - result = GETENV_ERR_UNEXPECTED | value[0]; - } - return result; -} - -os_getenv_err_t common_hal_os_getenv_str(const char *key, char *value, size_t value_len) { - os_getenv_err_t result = common_hal_os_getenv_str_inner(key, value, value_len); - common_hal_os_getenv_showerr(key, result); - return result; -} - -mp_obj_t common_hal_os_getenv_path(const char *path, const char *key, mp_obj_t default_) { - vstr_t buf; - bool quoted; - - vstr_init(&buf, 64); - os_getenv_err_t result = os_getenv_vstr(path, key, &buf, "ed); - if (result == GETENV_ERR_NOT_FOUND || result == GETENV_ERR_OPEN) { - return default_; - } - handle_getenv_error(result, mp_raise_msg_varg); - - if (quoted) { - return mp_obj_new_str_from_vstr(&buf); - } else { - return mp_parse_num_integer(buf.buf, buf.len, 0, NULL); - } -} - -mp_obj_t common_hal_os_getenv(const char *key, mp_obj_t default_) { - return common_hal_os_getenv_path(GETENV_PATH, key, default_); -} - -static os_getenv_err_t common_hal_os_getenv_int_inner(const char *key, mp_int_t *value) { - char buf[16]; - bool quoted; - os_getenv_err_t result = os_getenv_buf_terminated(key, buf, sizeof(buf), "ed); - if (result != GETENV_OK) { - return result; - } - if (quoted) { - return GETENV_ERR_UNEXPECTED | '"'; - } - char *end; - long num = strtol(buf, &end, 0); - while (unichar_isspace(*end)) { - end++; - } - if (end == buf || *end) { // If the whole buffer was not consumed it's an error - return GETENV_ERR_UNEXPECTED | *end; - } - *value = (mp_int_t)num; - return GETENV_OK; -} - -os_getenv_err_t common_hal_os_getenv_int(const char *key, mp_int_t *value) { - os_getenv_err_t result = common_hal_os_getenv_int_inner(key, value); - common_hal_os_getenv_showerr(key, result); - return result; -} -#endif diff --git a/shared-module/sdcardio/SDCard.c b/shared-module/sdcardio/SDCard.c index bd3ea62d141e5..5d3c021f27ed4 100644 --- a/shared-module/sdcardio/SDCard.c +++ b/shared-module/sdcardio/SDCard.c @@ -7,15 +7,16 @@ // This implementation largely follows the structure of adafruit_sdcard.py #include "extmod/vfs.h" - #include "shared-bindings/busio/SPI.h" #include "shared-bindings/digitalio/DigitalInOut.h" #include "shared-bindings/sdcardio/SDCard.h" #include "shared-bindings/time/__init__.h" #include "shared-bindings/util.h" #include "shared-module/sdcardio/SDCard.h" +#include "supervisor/shared/tick.h" #include "py/mperrno.h" +#include "py/mphal.h" #if 0 #define DEBUG_PRINT(...) ((void)mp_printf(&mp_plat_print,##__VA_ARGS__)) @@ -23,7 +24,15 @@ #define DEBUG_PRINT(...) ((void)0) #endif -#define CMD_TIMEOUT (200) +// https://nodeloop.org/guides/sd-card-spi-init-guide/ is an excellent source of info for SPI card use. + +// https://www.taterli.com/wp-content/uploads/2017/05/Physical-Layer-Simplified-SpecificationV6.0.pdf +// specifies timeouts for read (100 ms), write (250 ms), erase (depends on size), and other operations. +#define CMD_TIMEOUT_MS (250) +#define SPI_TIMEOUT_MS (250) +// Init ready timeout. +#define READY_TIMEOUT_MS (300) + #define R1_IDLE_STATE (1 << 0) #define R1_ILLEGAL_COMMAND (1 << 2) @@ -32,18 +41,40 @@ #define TOKEN_STOP_TRAN (0xFD) #define TOKEN_DATA (0xFE) +bool common_hal_sdcardio_sdcard_deinited(sdcardio_sdcard_obj_t *self) { + // Also check SPI bus was deinited out from under us. + if (!self->bus || common_hal_busio_spi_deinited(self->bus)) { + return true; + } + return false; +} + +void common_hal_sdcardio_sdcard_mark_deinit(sdcardio_sdcard_obj_t *self) { + self->bus = NULL; +} + static void common_hal_sdcardio_check_for_deinit(sdcardio_sdcard_obj_t *self) { - if (!self->bus) { + if (common_hal_sdcardio_sdcard_deinited(self)) { raise_deinited_error(); } } static bool lock_and_configure_bus(sdcardio_sdcard_obj_t *self) { - common_hal_sdcardio_check_for_deinit(self); + if (common_hal_sdcardio_sdcard_deinited(self)) { + return false; + } - if (!common_hal_busio_spi_try_lock(self->bus)) { + if (!common_hal_busio_spi_wait_for_lock(self->bus, SPI_TIMEOUT_MS)) { return false; } + + // Make sure we can still use the SPI bus after grabbing the lock. + // The VM might be in the process of shutting down, and there could be a race. + if (!vm_is_running() && !self->persistent_mount) { + common_hal_busio_spi_unlock(self->bus); + return false; + } + common_hal_busio_spi_configure(self->bus, self->baudrate, 0, 0, 8); common_hal_digitalio_digitalinout_set_value(&self->cs, false); return true; @@ -56,11 +87,10 @@ static void lock_bus_or_throw(sdcardio_sdcard_obj_t *self) { } static void clock_card(sdcardio_sdcard_obj_t *self, int bytes) { - uint8_t buf[] = {0xff}; + uint8_t buf[bytes]; + memset(buf, 0xff, bytes); common_hal_digitalio_digitalinout_set_value(&self->cs, true); - for (int i = 0; i < bytes; i++) { - common_hal_busio_spi_write(self->bus, buf, 1); - } + common_hal_busio_spi_write(self->bus, buf, bytes); } static void extraclock_and_unlock_bus(sdcardio_sdcard_obj_t *self) { @@ -83,21 +113,35 @@ static uint8_t CRC7(const uint8_t *data, uint8_t n) { return (crc << 1) | 1; } -#define READY_TIMEOUT_NS (300 * 1000 * 1000) // 300ms -static int wait_for_ready(sdcardio_sdcard_obj_t *self) { - uint64_t deadline = common_hal_time_monotonic_ns() + READY_TIMEOUT_NS; - while (common_hal_time_monotonic_ns() < deadline) { +// Assumes that the spi lock has been acquired. +// +// Mask the incoming value with mask. Use 0xff to not mask. +// if not_match is true, wait for something NOT matching the value. +// Return the response as an int32_t (which is always >= 0), or -1 if timed out. +static int32_t wait_for_masked_response(sdcardio_sdcard_obj_t *self, uint8_t mask, uint8_t response, bool not_match, uint32_t timeout_ms) { + uint64_t deadline = supervisor_ticks_ms64() + timeout_ms; + while (supervisor_ticks_ms64() < deadline) { uint8_t b; common_hal_busio_spi_read(self->bus, &b, 1, 0xff); - if (b == 0xff) { - return 0; + if (((b & mask) == response) ^ not_match) { + return b; } } - return -ETIMEDOUT; + return -1; +} + +// Wait for the given response byte. +static bool wait_for_response(sdcardio_sdcard_obj_t *self, uint8_t response) { + return wait_for_masked_response(self, 0xff, response, false, CMD_TIMEOUT_MS) != -1; +} + +// Wait for 0xff, with a specific timeout. +static bool wait_for_ready(sdcardio_sdcard_obj_t *self) { + return wait_for_masked_response(self, 0xff, 0xff, false, READY_TIMEOUT_MS) != -1; } // Note: this is never called while "in cmd25" (in fact, it's only used by `exit_cmd25`) -static bool cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) { +static mp_negative_errno_t cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) { uint8_t cmdbuf[2] = {cmd, 0xff}; assert(!self->in_cmd25); @@ -105,17 +149,14 @@ static bool cmd_nodata(sdcardio_sdcard_obj_t *self, int cmd, int response) { common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf)); // Wait for the response (response[7] == response) - for (int i = 0; i < CMD_TIMEOUT; i++) { - common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff); - if (cmdbuf[0] == response) { - return 0; - } + if (wait_for_response(self, response)) { + return 0; } - return -EIO; + return -MP_EIO; } -static int exit_cmd25(sdcardio_sdcard_obj_t *self) { +static mp_negative_errno_t exit_cmd25(sdcardio_sdcard_obj_t *self) { if (self->in_cmd25) { DEBUG_PRINT("exit cmd25\n"); self->in_cmd25 = false; @@ -126,7 +167,7 @@ static int exit_cmd25(sdcardio_sdcard_obj_t *self) { // In Python API, defaults are response=None, data_block=True, wait=True static int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf, size_t response_len, bool data_block, bool wait) { - int r = exit_cmd25(self); + mp_negative_errno_t r = exit_cmd25(self); if (r < 0) { return r; } @@ -141,48 +182,43 @@ static int cmd(sdcardio_sdcard_obj_t *self, int cmd, int arg, void *response_buf cmdbuf[5] = CRC7(cmdbuf, 5); if (wait) { - r = wait_for_ready(self); - if (r < 0) { - return r; + if (!wait_for_ready(self)) { + return -MP_ETIMEDOUT; } } common_hal_busio_spi_write(self->bus, cmdbuf, sizeof(cmdbuf)); // Wait for the response (response[7] == 0) - bool response_received = false; - for (int i = 0; i < CMD_TIMEOUT; i++) { - common_hal_busio_spi_read(self->bus, cmdbuf, 1, 0xff); - if ((cmdbuf[0] & 0x80) == 0) { - response_received = true; - break; - } - } - - if (!response_received) { - return -EIO; + // Now wait for cmd response, which is the high bit being 0. + int32_t response = wait_for_masked_response(self, 0x80, 0, false, CMD_TIMEOUT_MS); + if (response == -1) { + return -MP_EIO; } if (response_buf) { if (data_block) { cmdbuf[1] = 0xff; - do { - // Wait for the start block byte - common_hal_busio_spi_read(self->bus, cmdbuf + 1, 1, 0xff); - } while (cmdbuf[1] != 0xfe); + if (!wait_for_response(self, 0xfe)) { + return -MP_EIO; + } } - common_hal_busio_spi_read(self->bus, response_buf, response_len, 0xff); + if (!common_hal_busio_spi_read(self->bus, response_buf, response_len, 0xff)) { + return -MP_EIO; + } if (data_block) { // Read and discard the CRC-CCITT checksum - common_hal_busio_spi_read(self->bus, cmdbuf + 1, 2, 0xff); + if (!common_hal_busio_spi_read(self->bus, cmdbuf + 1, 2, 0xff)) { + return -MP_EIO; + } } } - return cmdbuf[0]; + return response; } static int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *response_buf, size_t response_len, bool data_block, bool wait) { @@ -190,7 +226,8 @@ static int block_cmd(sdcardio_sdcard_obj_t *self, int cmd_, int block, void *res } static mp_rom_error_text_t init_card_v1(sdcardio_sdcard_obj_t *self) { - for (int i = 0; i < CMD_TIMEOUT; i++) { + uint64_t deadline = supervisor_ticks_ms64() + CMD_TIMEOUT_MS; + while (supervisor_ticks_ms64() < deadline) { if (cmd(self, 41, 0, NULL, 0, true, true) == 0) { return NULL; } @@ -199,7 +236,8 @@ static mp_rom_error_text_t init_card_v1(sdcardio_sdcard_obj_t *self) { } static mp_rom_error_text_t init_card_v2(sdcardio_sdcard_obj_t *self) { - for (int i = 0; i < CMD_TIMEOUT; i++) { + uint64_t deadline = supervisor_ticks_ms64() + CMD_TIMEOUT_MS; + while (supervisor_ticks_ms64() < deadline) { uint8_t ocr[4]; common_hal_time_delay_ms(50); cmd(self, 58, 0, ocr, sizeof(ocr), false, true); @@ -216,6 +254,8 @@ static mp_rom_error_text_t init_card_v2(sdcardio_sdcard_obj_t *self) { } static mp_rom_error_text_t init_card(sdcardio_sdcard_obj_t *self) { + // https://nodeloop.org/guides/sd-card-spi-init-guide/ recommends at least 74 bit clocks + // and says 80 bit clocks(10*8) is common. Value below is bytes, not bits. clock_card(self, 10); common_hal_digitalio_digitalinout_set_value(&self->cs, false); @@ -294,13 +334,16 @@ static mp_rom_error_text_t init_card(sdcardio_sdcard_obj_t *self) { return NULL; } -mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate) { +mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate, bool persistent_mount) { self->bus = bus; + self->persistent_mount = persistent_mount; common_hal_digitalio_digitalinout_construct(&self->cs, cs); common_hal_digitalio_digitalinout_switch_to_output(&self->cs, true, DRIVE_MODE_PUSH_PULL); self->cdv = 512; self->sectors = 0; + + // During initialization, talk to the SPI card between 100 khZ and 400 kHz. After that, can use full speed. self->baudrate = 250000; lock_bus_or_throw(self); @@ -318,18 +361,19 @@ mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio void common_hal_sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate) { - mp_rom_error_text_t result = sdcardio_sdcard_construct(self, bus, cs, baudrate); + // User mounted, so persistent_mount=false. + mp_rom_error_text_t result = sdcardio_sdcard_construct(self, bus, cs, baudrate, false); if (result != NULL) { mp_raise_OSError_msg(result); } } void common_hal_sdcardio_sdcard_deinit(sdcardio_sdcard_obj_t *self) { - if (!self->bus) { + if (common_hal_sdcardio_sdcard_deinited(self)) { return; } common_hal_sdcardio_sdcard_sync(self); - self->bus = 0; + common_hal_sdcardio_sdcard_mark_deinit(self); common_hal_digitalio_digitalinout_deinit(&self->cs); } @@ -339,23 +383,25 @@ int common_hal_sdcardio_sdcard_get_blockcount(sdcardio_sdcard_obj_t *self) { } static int readinto(sdcardio_sdcard_obj_t *self, void *buf, size_t size) { - uint8_t aux[2] = {0, 0}; - while (aux[0] != 0xfe) { - common_hal_busio_spi_read(self->bus, aux, 1, 0xff); + + if (!wait_for_response(self, 0xfe)) { + return -MP_EIO; } common_hal_busio_spi_read(self->bus, buf, size, 0xff); // Read checksum and throw it away - common_hal_busio_spi_read(self->bus, aux, sizeof(aux), 0xff); + uint8_t checksum[2]; + common_hal_busio_spi_read(self->bus, checksum, sizeof(checksum), 0xff); return 0; } +// The mp_uint_t is misleading; negative errors can be returned. mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t start_block, uint32_t nblocks) { // deinit check is in lock_and_configure_bus() sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!lock_and_configure_bus(self)) { - return MP_EAGAIN; + return -MP_ETIMEDOUT; } int r = 0; size_t buflen = 512 * nblocks; @@ -388,6 +434,7 @@ mp_uint_t sdcardio_sdcard_readblocks(mp_obj_t self_in, uint8_t *buf, uint32_t st } } extraclock_and_unlock_bus(self); + // No caller actually uses this value. return r; } @@ -400,7 +447,9 @@ int common_hal_sdcardio_sdcard_readblocks(sdcardio_sdcard_obj_t *self, uint32_t } static int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t size) { - wait_for_ready(self); + if (!wait_for_ready(self)) { + return -MP_ETIMEDOUT; + } uint8_t cmd[2]; cmd[0] = token; @@ -423,12 +472,12 @@ static int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t // with STATUS 010 indicating "data accepted", and other status bit // combinations indicating failure. // In practice, I was seeing cmd[0] as 0xe5, indicating success - for (int i = 0; i < CMD_TIMEOUT; i++) { + uint64_t deadline = supervisor_ticks_ms64() + CMD_TIMEOUT_MS; + while (supervisor_ticks_ms64() < deadline) { common_hal_busio_spi_read(self->bus, cmd, 1, 0xff); - DEBUG_PRINT("i=%02d cmd[0] = 0x%02x\n", i, cmd[0]); if ((cmd[0] & 0b00010001) == 0b00000001) { if ((cmd[0] & 0x1f) != 0x5) { - return -EIO; + return -MP_EIO; } else { break; } @@ -436,9 +485,11 @@ static int _write(sdcardio_sdcard_obj_t *self, uint8_t token, void *buf, size_t } // Wait for the write to finish - do { - common_hal_busio_spi_read(self->bus, cmd, 1, 0xff); - } while (cmd[0] == 0); + + // Wait for a non-zero value. + if (wait_for_masked_response(self, 0xff /*mask*/, 0, true /*not_match*/, CMD_TIMEOUT_MS) == -1) { + return -MP_EIO; + } // Success return 0; @@ -448,7 +499,7 @@ mp_uint_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t s // deinit check is in lock_and_configure_bus() sdcardio_sdcard_obj_t *self = MP_OBJ_TO_PTR(self_in); if (!lock_and_configure_bus(self)) { - return MP_EAGAIN; + return -MP_ETIMEDOUT; } if (!self->in_cmd25 || start_block != self->next_block) { @@ -480,23 +531,21 @@ mp_uint_t sdcardio_sdcard_writeblocks(mp_obj_t self_in, uint8_t *buf, uint32_t s return 0; } -int common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self) { +mp_negative_errno_t common_hal_sdcardio_sdcard_sync(sdcardio_sdcard_obj_t *self) { // deinit check is in lock_and_configure_bus() - lock_and_configure_bus(self); + if (!lock_and_configure_bus(self)) { + return -MP_ETIMEDOUT; + } int r = exit_cmd25(self); extraclock_and_unlock_bus(self); return r; } -int common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { - // deinit check is in lock_and_configure_bus() +mp_negative_errno_t common_hal_sdcardio_sdcard_writeblocks(sdcardio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *buf) { if (buf->len % 512 != 0) { mp_raise_ValueError_varg(MP_ERROR_TEXT("Buffer must be a multiple of %d bytes"), 512); } - lock_and_configure_bus(self); - int r = sdcardio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), buf->buf, start_block, buf->len / 512); - extraclock_and_unlock_bus(self); - return r; + return sdcardio_sdcard_writeblocks(MP_OBJ_FROM_PTR(self), buf->buf, start_block, buf->len / 512); } bool sdcardio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *out_value) { @@ -504,11 +553,8 @@ bool sdcardio_sdcard_ioctl(mp_obj_t self_in, size_t cmd, size_t arg, mp_int_t *o *out_value = 0; switch (cmd) { case MP_BLOCKDEV_IOCTL_DEINIT: - common_hal_sdcardio_sdcard_sync(self); - break; // TODO properly case MP_BLOCKDEV_IOCTL_SYNC: - common_hal_sdcardio_sdcard_sync(self); - break; + return common_hal_sdcardio_sdcard_sync(self) == 0; case MP_BLOCKDEV_IOCTL_BLOCK_COUNT: *out_value = common_hal_sdcardio_sdcard_get_blockcount(self); break; diff --git a/shared-module/sdcardio/SDCard.h b/shared-module/sdcardio/SDCard.h index f9cd64b812532..35a9ed1bf3c12 100644 --- a/shared-module/sdcardio/SDCard.h +++ b/shared-module/sdcardio/SDCard.h @@ -23,6 +23,9 @@ typedef struct { uint32_t sectors; uint32_t next_block; bool in_cmd25; + // Automounted SD cards are usually persistent across VM's. Note this as needed to allow access + // when the VM is not running. + bool persistent_mount; } sdcardio_sdcard_obj_t; -mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate); +mp_rom_error_text_t sdcardio_sdcard_construct(sdcardio_sdcard_obj_t *self, busio_spi_obj_t *bus, const mcu_pin_obj_t *cs, int baudrate, bool persistent_mount); diff --git a/shared-module/sdcardio/__init__.c b/shared-module/sdcardio/__init__.c index a49a1506712db..4a7a4d500cc3f 100644 --- a/shared-module/sdcardio/__init__.c +++ b/shared-module/sdcardio/__init__.c @@ -22,7 +22,7 @@ static mp_vfs_mount_t _sdcard_vfs; fs_user_mount_t _sdcard_usermount; static bool _init_error = false; -static bool _mounted = false; +static bool _automounted = false; #ifdef DEFAULT_SD_MOSI static busio_spi_obj_t busio_spi_obj; @@ -45,7 +45,7 @@ void automount_sd_card(void) { if (common_hal_digitalio_digitalinout_get_value(&sd_card_detect_pin) != DEFAULT_SD_CARD_INSERTED) { // No card. _init_error = false; - if (_mounted) { + if (_automounted) { // Unmount the card. mp_vfs_mount_t *cur = MP_STATE_VM(vfs_mount_table); if (cur == &_sdcard_vfs) { @@ -63,11 +63,11 @@ void automount_sd_card(void) { #ifdef DEFAULT_SD_MOSI common_hal_busio_spi_deinit(&busio_spi_obj); #endif - _mounted = false; + _automounted = false; } return; - } else if (_init_error || _mounted) { - // We've already tried and failed to init the card. Don't try again. + } else if (_init_error || _automounted) { + // We've already tried and failed to init the card, or it's still mounted. Don't try again. return; } @@ -81,10 +81,10 @@ void automount_sd_card(void) { common_hal_busio_spi_never_reset(spi_obj); #endif sdcard.base.type = &sdcardio_SDCard_type; - mp_rom_error_text_t error = sdcardio_sdcard_construct(&sdcard, spi_obj, DEFAULT_SD_CS, 25000000); + mp_rom_error_text_t error = sdcardio_sdcard_construct(&sdcard, spi_obj, DEFAULT_SD_CS, 25000000, true); if (error != NULL) { // Failed to communicate with the card. - _mounted = false; + _automounted = false; _init_error = true; #ifdef DEFAULT_SD_MOSI common_hal_busio_spi_deinit(spi_obj); @@ -104,7 +104,7 @@ void automount_sd_card(void) { // mount the block device so the VFS methods can be used FRESULT res = f_mount(&vfs->fatfs); if (res != FR_OK) { - _mounted = false; + _automounted = false; _init_error = true; common_hal_sdcardio_sdcard_deinit(&sdcard); #ifdef DEFAULT_SD_MOSI @@ -122,6 +122,6 @@ void automount_sd_card(void) { sdcard_vfs->obj = MP_OBJ_FROM_PTR(&_sdcard_usermount); sdcard_vfs->next = MP_STATE_VM(vfs_mount_table); MP_STATE_VM(vfs_mount_table) = sdcard_vfs; - _mounted = true; - #endif + _automounted = true; + #endif // DEFAULT_SD_CARD_DETECT } diff --git a/shared-module/sharpdisplay/SharpMemoryFramebuffer.c b/shared-module/sharpdisplay/SharpMemoryFramebuffer.c index 3c6eaf065df72..8b9629b002f45 100644 --- a/shared-module/sharpdisplay/SharpMemoryFramebuffer.c +++ b/shared-module/sharpdisplay/SharpMemoryFramebuffer.c @@ -54,11 +54,7 @@ static bool common_hal_sharpdisplay_framebuffer_get_pixels_in_byte_share_row(sha } void common_hal_sharpdisplay_framebuffer_reset(sharpdisplay_framebuffer_obj_t *self) { - if (self->bus != &self->inline_bus - #if CIRCUITPY_BOARD_SPI - && !common_hal_board_is_spi(self->bus) - #endif - ) { + if (self->bus != &self->inline_bus && gc_ptr_on_heap(self->bus)) { memcpy(&self->inline_bus, self->bus, sizeof(busio_spi_obj_t)); self->bus = &self->inline_bus; } diff --git a/shared-module/ssl/SSLSocket.c b/shared-module/ssl/SSLSocket.c index 8911fa2f454d8..fa1de8bb484aa 100644 --- a/shared-module/ssl/SSLSocket.c +++ b/shared-module/ssl/SSLSocket.c @@ -44,7 +44,7 @@ static void mbedtls_debug(void *ctx, int level, const char *file, int line, cons #define DEBUG_PRINT(...) do {} while (0) #endif -static NORETURN void mbedtls_raise_error(int err) { +static MP_NORETURN void mbedtls_raise_error(int err) { // _mbedtls_ssl_send and _mbedtls_ssl_recv (below) turn positive error codes from the // underlying socket into negative codes to pass them through mbedtls. Here we turn them // positive again so they get interpreted as the OSError they really are. The diff --git a/shared-module/storage/__init__.c b/shared-module/storage/__init__.c index 9a2c8271222e0..dedcee1ff23f5 100644 --- a/shared-module/storage/__init__.c +++ b/shared-module/storage/__init__.c @@ -126,6 +126,11 @@ void common_hal_storage_mount(mp_obj_t vfs_obj, const char *mount_path, bool rea // call the underlying object to do any mounting operation mp_vfs_proxy_call(vfs, MP_QSTR_mount, 2, (mp_obj_t *)&args); + fs_user_mount_t *vfs_fat = MP_OBJ_TO_PTR(vfs_obj); + // Filesystem is read-only to USB if writable by CircuitPython, and vice versa. + filesystem_set_writable_by_usb(vfs_fat, readonly); + filesystem_set_concurrent_write_protection(vfs_fat, true); + // Insert the vfs into the mount table by pushing it onto the front of the // mount table. mp_vfs_mount_t **vfsp = &MP_STATE_VM(vfs_mount_table); diff --git a/shared-module/struct/__init__.c b/shared-module/struct/__init__.c index 715b504302115..4b6b2b589efc8 100644 --- a/shared-module/struct/__init__.c +++ b/shared-module/struct/__init__.c @@ -14,7 +14,8 @@ #include "shared-bindings/struct/__init__.h" static void struct_validate_format(char fmt) { - #if MICROPY_NONSTANDARD_TYPECODES + #if MICROPY_PY_STRUCT_UNSAFE_TYPECODES + if (fmt == 'S' || fmt == 'O') { mp_raise_RuntimeError(MP_ERROR_TEXT("'S' and 'O' are not supported format types")); } diff --git a/shared-module/synthio/MidiTrack.c b/shared-module/synthio/MidiTrack.c index 94430289bda10..1cccd440fad9e 100644 --- a/shared-module/synthio/MidiTrack.c +++ b/shared-module/synthio/MidiTrack.c @@ -116,6 +116,16 @@ mp_int_t common_hal_synthio_miditrack_get_error_location(synthio_miditrack_obj_t return self->error_location; } +mp_int_t common_hal_synthio_miditrack_get_tempo(synthio_miditrack_obj_t *self) { + return self->tempo; +} + +void common_hal_synthio_miditrack_set_tempo(synthio_miditrack_obj_t *self, mp_int_t value) { + mp_int_t val = mp_arg_validate_int_min(value, 1, MP_QSTR_tempo); + self->synth.span.dur = (uint32_t)self->synth.span.dur * self->tempo / val; + self->tempo = val; +} + void synthio_miditrack_reset_buffer(synthio_miditrack_obj_t *self, bool single_channel_output, uint8_t channel) { synthio_synth_reset_buffer(&self->synth, single_channel_output, channel); diff --git a/shared-module/usb/core/Device.c b/shared-module/usb/core/Device.c index c9df0cf73805f..83def37de914e 100644 --- a/shared-module/usb/core/Device.c +++ b/shared-module/usb/core/Device.c @@ -1,12 +1,15 @@ // This file is part of the CircuitPython project: https://circuitpython.org // // SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2025 Sam Blenny // // SPDX-License-Identifier: MIT #include "shared-bindings/usb/core/Device.h" #include "tusb_config.h" +#include "supervisor/port.h" +#include "supervisor/port_heap.h" #include "lib/tinyusb/src/host/hcd.h" #include "lib/tinyusb/src/host/usbh.h" @@ -32,6 +35,28 @@ void tuh_umount_cb(uint8_t dev_addr) { static xfer_result_t _xfer_result; static size_t _actual_len; + +#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE +// Helper to ensure buffer is DMA-capable for transfer operations +static uint8_t *_ensure_dma_buffer(usb_core_device_obj_t *self, const uint8_t *buffer, size_t len, bool for_write) { + if (port_buffer_is_dma_capable(buffer)) { + return (uint8_t *)buffer; // Already DMA-capable, use directly + } + + uint8_t *dma_buffer = port_malloc(len, true); // true = DMA capable + if (dma_buffer == NULL) { + return NULL; // Allocation failed + } + + // Copy data to DMA buffer if writing + if (for_write && buffer != NULL) { + memcpy(dma_buffer, buffer, len); + } + + return dma_buffer; +} + +#endif bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t device_address) { if (!tuh_inited()) { mp_raise_RuntimeError(MP_ERROR_TEXT("No usb host port initialized")); @@ -45,7 +70,7 @@ bool common_hal_usb_core_device_construct(usb_core_device_obj_t *self, uint8_t d } self->device_address = device_address; self->first_langid = 0; - _xfer_result = 0xff; + _xfer_result = XFER_RESULT_INVALID; return true; } @@ -70,14 +95,18 @@ void common_hal_usb_core_device_deinit(usb_core_device_obj_t *self) { uint16_t common_hal_usb_core_device_get_idVendor(usb_core_device_obj_t *self) { uint16_t vid; uint16_t pid; - tuh_vid_pid_get(self->device_address, &vid, &pid); + if (!tuh_vid_pid_get(self->device_address, &vid, &pid)) { + mp_raise_usb_core_USBError(NULL); + } return vid; } uint16_t common_hal_usb_core_device_get_idProduct(usb_core_device_obj_t *self) { uint16_t vid; uint16_t pid; - tuh_vid_pid_get(self->device_address, &vid, &pid); + if (!tuh_vid_pid_get(self->device_address, &vid, &pid)) { + mp_raise_usb_core_USBError(NULL); + } return pid; } @@ -91,14 +120,109 @@ static void _transfer_done_cb(tuh_xfer_t *xfer) { static bool _wait_for_callback(void) { while (!mp_hal_is_interrupted() && - _xfer_result == 0xff) { + _xfer_result == XFER_RESULT_INVALID) { + // The background tasks include TinyUSB which will call the function + // we provided above. In other words, the callback isn't in an interrupt. + RUN_BACKGROUND_TASKS; + } + if (mp_hal_is_interrupted()) { + // Handle case of VM being interrupted by Ctrl-C or autoreload + return false; + } + // Handle callback result code from TinyUSB + xfer_result_t result = _xfer_result; + _xfer_result = XFER_RESULT_INVALID; + switch (result) { + case XFER_RESULT_SUCCESS: + return true; + case XFER_RESULT_FAILED: + mp_raise_usb_core_USBError(NULL); + break; + case XFER_RESULT_STALLED: + mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); + break; + case XFER_RESULT_TIMEOUT: + case XFER_RESULT_INVALID: + mp_raise_usb_core_USBTimeoutError(); + break; + } + return false; +} + +static void _prepare_for_transfer(void) { + // Prepare for transfer. Unless there is a timeout, these static globals will + // get modified by the _transfer_done_cb() callback when tinyusb finishes the + // transfer or encounters an error condition. + _xfer_result = XFER_RESULT_INVALID; + _actual_len = 0; +} + +static void _abort_transfer(tuh_xfer_t *xfer) { + bool aborted = tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); + if (aborted) { + // If the transfer was aborted, then we can continue. + return; + } + uint32_t start_time = supervisor_ticks_ms32(); + // If not, we need to wait for it to finish, otherwise we may free memory out from under it. + // Limit the wait time to 10 milliseconds to avoid blocking indefinitely. + while (_xfer_result == XFER_RESULT_INVALID && (supervisor_ticks_ms32() - start_time < 10)) { + // The background tasks include TinyUSB which will call the function + // we provided above. In other words, the callback isn't in an interrupt. + RUN_BACKGROUND_TASKS; + } +} + +// Only frees the transfer buffer on error. +static size_t _handle_timed_transfer_callback(tuh_xfer_t *xfer, mp_int_t timeout, bool our_buffer) { + if (xfer == NULL) { + mp_raise_usb_core_USBError(NULL); + return 0; + } + uint32_t start_time = supervisor_ticks_ms32(); + while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && + !mp_hal_is_interrupted() && + _xfer_result == XFER_RESULT_INVALID) { // The background tasks include TinyUSB which will call the function // we provided above. In other words, the callback isn't in an interrupt. RUN_BACKGROUND_TASKS; } + if (mp_hal_is_interrupted()) { + // Handle case of VM being interrupted by Ctrl-C or autoreload + _abort_transfer(xfer); + return 0; + } + // Handle transfer result code from TinyUSB xfer_result_t result = _xfer_result; - _xfer_result = 0xff; - return result == XFER_RESULT_SUCCESS; + _xfer_result = XFER_RESULT_INVALID; + if (our_buffer && result != XFER_RESULT_SUCCESS && result != XFER_RESULT_INVALID) { + port_free(xfer->buffer); + } + switch (result) { + case XFER_RESULT_SUCCESS: + return _actual_len; + case XFER_RESULT_FAILED: + mp_raise_usb_core_USBError(NULL); + break; + case XFER_RESULT_STALLED: + mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); + break; + case XFER_RESULT_TIMEOUT: + // This timeout comes from TinyUSB, so assume that it has stopped the + // transfer (note: timeout logic may be unimplemented on TinyUSB side) + mp_raise_usb_core_USBTimeoutError(); + break; + case XFER_RESULT_INVALID: + // This timeout comes from CircuitPython, not TinyUSB, so tell TinyUSB + // to stop the transfer and then wait to free the buffer. + _abort_transfer(xfer); + if (our_buffer) { + port_free(xfer->buffer); + } + mp_raise_usb_core_USBTimeoutError(); + break; + } + return 0; } static mp_obj_t _get_string(const uint16_t *temp_buf) { @@ -115,41 +239,75 @@ static void _get_langid(usb_core_device_obj_t *self) { } // Two control bytes and one uint16_t language code. uint16_t temp_buf[2]; - if (!tuh_descriptor_get_string(self->device_address, 0, 0, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { - return; + _prepare_for_transfer(); + if (!tuh_descriptor_get_string(self->device_address, 0, 0, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + self->first_langid = temp_buf[1]; } - self->first_langid = temp_buf[1]; } mp_obj_t common_hal_usb_core_device_get_serial_number(usb_core_device_obj_t *self) { uint16_t temp_buf[127]; - _get_langid(self); - if (!tuh_descriptor_get_serial_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { + tusb_desc_device_t descriptor; + // First, be sure not to ask TinyUSB for a non-existent string (avoid error) + if (!tuh_descriptor_get_device_local(self->device_address, &descriptor)) { return mp_const_none; } - return _get_string(temp_buf); + if (descriptor.iSerialNumber == 0) { + return mp_const_none; + } + // Device does provide this string, so continue + _get_langid(self); + _prepare_for_transfer(); + if (!tuh_descriptor_get_serial_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + return _get_string(temp_buf); + } + return mp_const_none; } mp_obj_t common_hal_usb_core_device_get_product(usb_core_device_obj_t *self) { uint16_t temp_buf[127]; - _get_langid(self); - if (!tuh_descriptor_get_product_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { + tusb_desc_device_t descriptor; + // First, be sure not to ask TinyUSB for a non-existent string (avoid error) + if (!tuh_descriptor_get_device_local(self->device_address, &descriptor)) { + return mp_const_none; + } + if (descriptor.iProduct == 0) { return mp_const_none; } - return _get_string(temp_buf); + // Device does provide this string, so continue + _get_langid(self); + _prepare_for_transfer(); + if (!tuh_descriptor_get_product_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + return _get_string(temp_buf); + } + return mp_const_none; } mp_obj_t common_hal_usb_core_device_get_manufacturer(usb_core_device_obj_t *self) { uint16_t temp_buf[127]; - _get_langid(self); - if (!tuh_descriptor_get_manufacturer_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0) || - !_wait_for_callback()) { + tusb_desc_device_t descriptor; + // First, be sure not to ask TinyUSB for a non-existent string (avoid error) + if (!tuh_descriptor_get_device_local(self->device_address, &descriptor)) { + return mp_const_none; + } + if (descriptor.iManufacturer == 0) { return mp_const_none; } - return _get_string(temp_buf); + // Device does provide this string, so continue + _get_langid(self); + _prepare_for_transfer(); + if (!tuh_descriptor_get_manufacturer_string(self->device_address, self->first_langid, temp_buf, sizeof(temp_buf), _transfer_done_cb, 0)) { + mp_raise_usb_core_USBError(NULL); + } else if (_wait_for_callback()) { + return _get_string(temp_buf); + } + return mp_const_none; } @@ -224,39 +382,18 @@ void common_hal_usb_core_device_set_configuration(usb_core_device_obj_t *self, m _wait_for_callback(); } -static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout) { - _xfer_result = 0xff; +// Raises an exception on failure. Returns the number of bytes transferred (maybe zero) on success. +static size_t _xfer(tuh_xfer_t *xfer, mp_int_t timeout, bool our_buffer) { + _prepare_for_transfer(); xfer->complete_cb = _transfer_done_cb; if (!tuh_edpt_xfer(xfer)) { + if (our_buffer) { + port_free(xfer->buffer); + } mp_raise_usb_core_USBError(NULL); return 0; } - uint32_t start_time = supervisor_ticks_ms32(); - while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && - !mp_hal_is_interrupted() && - _xfer_result == 0xff) { - // The background tasks include TinyUSB which will call the function - // we provided above. In other words, the callback isn't in an interrupt. - RUN_BACKGROUND_TASKS; - } - if (mp_hal_is_interrupted()) { - tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); - return 0; - } - xfer_result_t result = _xfer_result; - _xfer_result = 0xff; - if (result == XFER_RESULT_STALLED) { - mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); - } - if (result == 0xff) { - tuh_edpt_abort_xfer(xfer->daddr, xfer->ep_addr); - mp_raise_usb_core_USBTimeoutError(); - } - if (result == XFER_RESULT_SUCCESS) { - return _actual_len; - } - - return 0; + return _handle_timed_transfer_callback(xfer, timeout, our_buffer); } static bool _open_endpoint(usb_core_device_obj_t *self, mp_int_t endpoint) { @@ -313,12 +450,30 @@ mp_int_t common_hal_usb_core_device_write(usb_core_device_obj_t *self, mp_int_t mp_raise_usb_core_USBError(NULL); return 0; } + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Ensure buffer is in DMA-capable memory + uint8_t *dma_buffer = _ensure_dma_buffer(self, buffer, len, true); // true = for write + if (dma_buffer == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + return 0; + } + #else + uint8_t *dma_buffer = (uint8_t *)buffer; // All memory is DMA-capable + #endif + tuh_xfer_t xfer; xfer.daddr = self->device_address; xfer.ep_addr = endpoint; - xfer.buffer = (uint8_t *)buffer; + xfer.buffer = dma_buffer; xfer.buflen = len; - return _xfer(&xfer, timeout); + size_t result = _xfer(&xfer, timeout, dma_buffer != buffer); + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + if (dma_buffer != buffer) { + port_free(dma_buffer); + } + #endif + return result; } mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t endpoint, uint8_t *buffer, mp_int_t len, mp_int_t timeout) { @@ -326,12 +481,34 @@ mp_int_t common_hal_usb_core_device_read(usb_core_device_obj_t *self, mp_int_t e mp_raise_usb_core_USBError(NULL); return 0; } + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Ensure buffer is in DMA-capable memory + uint8_t *dma_buffer = _ensure_dma_buffer(self, buffer, len, false); // false = for read + if (dma_buffer == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + return 0; + } + #else + uint8_t *dma_buffer = buffer; // All memory is DMA-capable + #endif + tuh_xfer_t xfer; xfer.daddr = self->device_address; xfer.ep_addr = endpoint; - xfer.buffer = buffer; + xfer.buffer = dma_buffer; xfer.buflen = len; - return _xfer(&xfer, timeout); + mp_int_t result = _xfer(&xfer, timeout, dma_buffer != buffer); + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Copy data back to original buffer if needed + if (dma_buffer != buffer) { + memcpy(buffer, dma_buffer, result); + port_free(dma_buffer); + } + #endif + + return result; } mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, @@ -340,6 +517,23 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, uint8_t *buffer, mp_int_t len, mp_int_t timeout) { // Timeout is in ms. + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + // Determine if this is a write (host-to-device) or read (device-to-host) transfer + bool is_write = (bmRequestType & 0x80) == 0; // Bit 7: 0=host-to-device, 1=device-to-host + + // Ensure buffer is in DMA-capable memory + uint8_t *dma_buffer = NULL; + if (len > 0 && buffer != NULL) { + dma_buffer = _ensure_dma_buffer(self, buffer, len, is_write); + if (dma_buffer == NULL) { + mp_raise_msg(&mp_type_MemoryError, MP_ERROR_TEXT("Could not allocate DMA capable buffer")); + return 0; + } + } + #else + uint8_t *dma_buffer = buffer; // All memory is DMA-capable + #endif + tusb_control_request_t request = { .bmRequestType = bmRequestType, .bRequest = bRequest, @@ -351,42 +545,28 @@ mp_int_t common_hal_usb_core_device_ctrl_transfer(usb_core_device_obj_t *self, .daddr = self->device_address, .ep_addr = 0, .setup = &request, - .buffer = buffer, + .buffer = dma_buffer, .complete_cb = _transfer_done_cb, }; - _xfer_result = 0xff; - + _prepare_for_transfer(); if (!tuh_control_xfer(&xfer)) { mp_raise_usb_core_USBError(NULL); return 0; } - uint32_t start_time = supervisor_ticks_ms32(); - while ((timeout == 0 || supervisor_ticks_ms32() - start_time < (uint32_t)timeout) && - !mp_hal_is_interrupted() && - _xfer_result == 0xff) { - // The background tasks include TinyUSB which will call the function - // we provided above. In other words, the callback isn't in an interrupt. - RUN_BACKGROUND_TASKS; - } - if (mp_hal_is_interrupted()) { - tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); - return 0; - } - xfer_result_t result = _xfer_result; - _xfer_result = 0xff; - if (result == XFER_RESULT_STALLED) { - mp_raise_usb_core_USBError(MP_ERROR_TEXT("Pipe error")); - } - if (result == 0xff) { - tuh_edpt_abort_xfer(xfer.daddr, xfer.ep_addr); - mp_raise_usb_core_USBTimeoutError(); - } - if (result == XFER_RESULT_SUCCESS) { - return len; + mp_int_t result = (mp_int_t)_handle_timed_transfer_callback(&xfer, timeout, dma_buffer != buffer); + + #if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE + if (dma_buffer != buffer) { + // Copy data back to original buffer if this was a read transfer + if (buffer != NULL && !is_write) { + memcpy(buffer, dma_buffer, result); + } + port_free(dma_buffer); } + #endif - return 0; + return result; } bool common_hal_usb_core_device_is_kernel_driver_active(usb_core_device_obj_t *self, mp_int_t interface) { diff --git a/shared-module/usb_cdc/__init__.c b/shared-module/usb_cdc/__init__.c index 9a9f158f28cfb..644c4ccae792b 100644 --- a/shared-module/usb_cdc/__init__.c +++ b/shared-module/usb_cdc/__init__.c @@ -105,7 +105,7 @@ static const uint8_t usb_cdc_descriptor_template[] = { 0xFF, // 54 bEndpointAddress (OUT/H2D) [SET AT RUNTIME] #define CDC_DATA_OUT_ENDPOINT_INDEX 54 0x02, // 55 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 56,57 wMaxPacketSize 512 #else 0x40, 0x00, // 56,57 wMaxPacketSize 64 @@ -118,7 +118,7 @@ static const uint8_t usb_cdc_descriptor_template[] = { 0xFF, // 61 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number] #define CDC_DATA_IN_ENDPOINT_INDEX 61 0x02, // 62 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 63,64 wMaxPacketSize 512 #else 0x40, 0x00, // 63,64 wMaxPacketSize 64 @@ -326,7 +326,7 @@ static const uint8_t usb_vendor_descriptor_template[] = { 0xFF, // 11 bEndpointAddress (IN/D2H) [SET AT RUNTIME: number] #define VENDOR_OUT_ENDPOINT_INDEX 11 0x02, // 12 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 13,14 wMaxPacketSize 512 #else 0x40, 0x00, // 13,14 wMaxPacketSize 64 diff --git a/shared-module/usb_midi/__init__.c b/shared-module/usb_midi/__init__.c index 3808801ff7e1f..1722911536fc2 100644 --- a/shared-module/usb_midi/__init__.c +++ b/shared-module/usb_midi/__init__.c @@ -106,7 +106,7 @@ static const uint8_t usb_midi_descriptor_template[] = { 0xFF, // 66 bEndpointAddress (OUT/H2D) [SET AT RUNTIME] #define MIDI_STREAMING_OUT_ENDPOINT_INDEX (66) 0x02, // 67 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 68,69 wMaxPacketSize (512) #else 0x40, 0x00, // 68,69 wMaxPacketSize (64) @@ -126,7 +126,7 @@ static const uint8_t usb_midi_descriptor_template[] = { 0xFF, // 78 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number] #define MIDI_STREAMING_IN_ENDPOINT_INDEX (78) 0x02, // 79 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 80, 81 wMaxPacketSize (512) #else 0x40, 0x00, // 80, 81 wMaxPacketSize (64) diff --git a/shared/libc/abort_.c b/shared/libc/abort_.c index 3051eae81e0ce..54eab67d3fb49 100644 --- a/shared/libc/abort_.c +++ b/shared/libc/abort_.c @@ -1,7 +1,7 @@ #include -NORETURN void abort_(void); +MP_NORETURN void abort_(void); -NORETURN void abort_(void) { +MP_NORETURN void abort_(void) { mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("abort() called")); } diff --git a/shared/memzip/make-memzip.py b/shared/memzip/make-memzip.py index 92a5d6168bb1d..e406c55a43ca7 100755 --- a/shared/memzip/make-memzip.py +++ b/shared/memzip/make-memzip.py @@ -7,8 +7,6 @@ # This is somewhat like frozen modules in python, but allows arbitrary files # to be used. -from __future__ import print_function - import argparse import os import subprocess diff --git a/shared/netutils/trace.c b/shared/netutils/trace.c index a6dfb42c28f00..24af4d5ca315f 100644 --- a/shared/netutils/trace.c +++ b/shared/netutils/trace.c @@ -56,7 +56,7 @@ static const char *ethertype_str(uint16_t type) { } void netutils_ethernet_trace(const mp_print_t *print, size_t len, const uint8_t *buf, unsigned int flags) { - mp_printf(print, "[% 8d] ETH%cX len=%u", mp_hal_ticks_ms(), flags & NETUTILS_TRACE_IS_TX ? 'T' : 'R', len); + mp_printf(print, "[% 8u] ETH%cX len=%u", (unsigned)mp_hal_ticks_ms(), flags & NETUTILS_TRACE_IS_TX ? 'T' : 'R', len); mp_printf(print, " dst=%02x:%02x:%02x:%02x:%02x:%02x", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); mp_printf(print, " src=%02x:%02x:%02x:%02x:%02x:%02x", buf[6], buf[7], buf[8], buf[9], buf[10], buf[11]); diff --git a/shared/runtime/gchelper_generic.c b/shared/runtime/gchelper_generic.c index 45b2e4f7d848a..230a24440050f 100644 --- a/shared/runtime/gchelper_generic.c +++ b/shared/runtime/gchelper_generic.c @@ -42,13 +42,14 @@ // stack already by the caller. #if defined(__x86_64__) +// CIRCUITPY-CHANGE: use __asm__ instead of asm static void gc_helper_get_regs(gc_helper_regs_t arr) { - register long rbx asm ("rbx"); - register long rbp asm ("rbp"); - register long r12 asm ("r12"); - register long r13 asm ("r13"); - register long r14 asm ("r14"); - register long r15 asm ("r15"); + register long rbx __asm__ ("rbx"); + register long rbp __asm__ ("rbp"); + register long r12 __asm__ ("r12"); + register long r13 __asm__ ("r13"); + register long r14 __asm__ ("r14"); + register long r15 __asm__ ("r15"); #ifdef __clang__ // TODO: // This is dirty workaround for Clang. It tries to get around @@ -56,12 +57,12 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { // Application of this patch here is random, and done only to unbreak // MacOS build. Better, cross-arch ways to deal with Clang issues should // be found. - asm ("" : "=r" (rbx)); - asm ("" : "=r" (rbp)); - asm ("" : "=r" (r12)); - asm ("" : "=r" (r13)); - asm ("" : "=r" (r14)); - asm ("" : "=r" (r15)); + __asm__ ("" : "=r" (rbx)); + __asm__ ("" : "=r" (rbp)); + __asm__ ("" : "=r" (r12)); + __asm__ ("" : "=r" (r13)); + __asm__ ("" : "=r" (r14)); + __asm__ ("" : "=r" (r15)); #endif arr[0] = rbx; arr[1] = rbp; @@ -74,10 +75,10 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { #elif defined(__i386__) static void gc_helper_get_regs(gc_helper_regs_t arr) { - register long ebx asm ("ebx"); - register long esi asm ("esi"); - register long edi asm ("edi"); - register long ebp asm ("ebp"); + register long ebx __asm__ ("ebx"); + register long esi __asm__ ("esi"); + register long edi __asm__ ("edi"); + register long ebp __asm__ ("ebp"); #ifdef __clang__ // TODO: // This is dirty workaround for Clang. It tries to get around @@ -85,10 +86,10 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { // Application of this patch here is random, and done only to unbreak // MacOS build. Better, cross-arch ways to deal with Clang issues should // be found. - asm ("" : "=r" (ebx)); - asm ("" : "=r" (esi)); - asm ("" : "=r" (edi)); - asm ("" : "=r" (ebp)); + __asm__ ("" : "=r" (ebx)); + __asm__ ("" : "=r" (esi)); + __asm__ ("" : "=r" (edi)); + __asm__ ("" : "=r" (ebp)); #endif arr[0] = ebx; arr[1] = esi; @@ -105,16 +106,16 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wuninitialized" #endif - register long r4 asm ("r4"); - register long r5 asm ("r5"); - register long r6 asm ("r6"); - register long r7 asm ("r7"); - register long r8 asm ("r8"); - register long r9 asm ("r9"); - register long r10 asm ("r10"); - register long r11 asm ("r11"); - register long r12 asm ("r12"); - register long r13 asm ("r13"); + register long r4 __asm__ ("r4"); + register long r5 __asm__ ("r5"); + register long r6 __asm__ ("r6"); + register long r7 __asm__ ("r7"); + register long r8 __asm__ ("r8"); + register long r9 __asm__ ("r9"); + register long r10 __asm__ ("r10"); + register long r11 __asm__ ("r11"); + register long r12 __asm__ ("r12"); + register long r13 __asm__ ("r13"); arr[0] = r4; arr[1] = r5; arr[2] = r6; @@ -133,17 +134,17 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { #elif defined(__aarch64__) static void gc_helper_get_regs(gc_helper_regs_t arr) { - const register long x19 asm ("x19"); - const register long x20 asm ("x20"); - const register long x21 asm ("x21"); - const register long x22 asm ("x22"); - const register long x23 asm ("x23"); - const register long x24 asm ("x24"); - const register long x25 asm ("x25"); - const register long x26 asm ("x26"); - const register long x27 asm ("x27"); - const register long x28 asm ("x28"); - const register long x29 asm ("x29"); + const register long x19 __asm__ ("x19"); + const register long x20 __asm__ ("x20"); + const register long x21 __asm__ ("x21"); + const register long x22 __asm__ ("x22"); + const register long x23 __asm__ ("x23"); + const register long x24 __asm__ ("x24"); + const register long x25 __asm__ ("x25"); + const register long x26 __asm__ ("x26"); + const register long x27 __asm__ ("x27"); + const register long x28 __asm__ ("x28"); + const register long x29 __asm__ ("x29"); arr[0] = x19; arr[1] = x20; arr[2] = x21; @@ -163,18 +164,18 @@ static void gc_helper_get_regs(gc_helper_regs_t arr) { // for RV32I targets or gchelper_rv64i.s for RV64I targets. static void gc_helper_get_regs(gc_helper_regs_t arr) { - register uintptr_t s0 asm ("x8"); - register uintptr_t s1 asm ("x9"); - register uintptr_t s2 asm ("x18"); - register uintptr_t s3 asm ("x19"); - register uintptr_t s4 asm ("x20"); - register uintptr_t s5 asm ("x21"); - register uintptr_t s6 asm ("x22"); - register uintptr_t s7 asm ("x23"); - register uintptr_t s8 asm ("x24"); - register uintptr_t s9 asm ("x25"); - register uintptr_t s10 asm ("x26"); - register uintptr_t s11 asm ("x27"); + register uintptr_t s0 __asm__ ("x8"); + register uintptr_t s1 __asm__ ("x9"); + register uintptr_t s2 __asm__ ("x18"); + register uintptr_t s3 __asm__ ("x19"); + register uintptr_t s4 __asm__ ("x20"); + register uintptr_t s5 __asm__ ("x21"); + register uintptr_t s6 __asm__ ("x22"); + register uintptr_t s7 __asm__ ("x23"); + register uintptr_t s8 __asm__ ("x24"); + register uintptr_t s9 __asm__ ("x25"); + register uintptr_t s10 __asm__ ("x26"); + register uintptr_t s11 __asm__ ("x27"); arr[0] = s0; arr[1] = s1; arr[2] = s2; diff --git a/shared/runtime/interrupt_char.c b/shared/runtime/interrupt_char.c index 5cec1988f41b3..1f2702017190f 100644 --- a/shared/runtime/interrupt_char.c +++ b/shared/runtime/interrupt_char.c @@ -31,6 +31,7 @@ #if MICROPY_KBD_EXCEPTION +// CIRCUITPY-CHANGE #ifdef __ZEPHYR__ #include diff --git a/shared/runtime/interrupt_char.h b/shared/runtime/interrupt_char.h index c4a465456a8d1..44fd4b45a6121 100644 --- a/shared/runtime/interrupt_char.h +++ b/shared/runtime/interrupt_char.h @@ -29,6 +29,7 @@ // CIRCUITPY-CHANGE #include +// CIRCUITPY-CHANGE #ifdef __ZEPHYR__ #include diff --git a/shared/runtime/mpirq.c b/shared/runtime/mpirq.c deleted file mode 100644 index 6111b9b10cfe4..0000000000000 --- a/shared/runtime/mpirq.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2015 Daniel Campora - * 2018 Tobias Badertscher - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -#include - -#include "py/runtime.h" -#include "py/gc.h" -#include "shared/runtime/mpirq.h" - -#if MICROPY_ENABLE_SCHEDULER - -/****************************************************************************** - DECLARE PUBLIC DATA - ******************************************************************************/ - -const mp_arg_t mp_irq_init_args[] = { - { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, - { MP_QSTR_trigger, MP_ARG_INT, {.u_int = 0} }, - { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, -}; - -/****************************************************************************** - DECLARE PRIVATE DATA - ******************************************************************************/ - -/****************************************************************************** - DEFINE PUBLIC FUNCTIONS - ******************************************************************************/ - -mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent) { - mp_irq_obj_t *self = m_new0(mp_irq_obj_t, 1); - mp_irq_init(self, methods, parent); - return self; -} - -void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent) { - self->base.type = &mp_irq_type; - self->methods = (mp_irq_methods_t *)methods; - self->parent = parent; - self->handler = mp_const_none; - self->ishard = false; -} - -void mp_irq_handler(mp_irq_obj_t *self) { - if (self->handler != mp_const_none) { - if (self->ishard) { - // When executing code within a handler we must lock the scheduler to - // prevent any scheduled callbacks from running, and lock the GC to - // prevent any memory allocations. - mp_sched_lock(); - gc_lock(); - nlr_buf_t nlr; - if (nlr_push(&nlr) == 0) { - mp_call_function_1(self->handler, self->parent); - nlr_pop(); - } else { - // Uncaught exception; disable the callback so that it doesn't run again - self->methods->trigger(self->parent, 0); - self->handler = mp_const_none; - mp_printf(MICROPY_ERROR_PRINTER, "Uncaught exception in IRQ callback handler\n"); - mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); - } - gc_unlock(); - mp_sched_unlock(); - } else { - // Schedule call to user function - mp_sched_schedule(self->handler, self->parent); - } - } -} - -/******************************************************************************/ -// MicroPython bindings - -static mp_obj_t mp_irq_flags(mp_obj_t self_in) { - mp_irq_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_FLAGS)); -} -static MP_DEFINE_CONST_FUN_OBJ_1(mp_irq_flags_obj, mp_irq_flags); - -static mp_obj_t mp_irq_trigger(size_t n_args, const mp_obj_t *args) { - mp_irq_obj_t *self = MP_OBJ_TO_PTR(args[0]); - mp_obj_t ret_obj = mp_obj_new_int(self->methods->info(self->parent, MP_IRQ_INFO_TRIGGERS)); - if (n_args == 2) { - // Set trigger - self->methods->trigger(self->parent, mp_obj_get_int(args[1])); - } - return ret_obj; -} -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_irq_trigger_obj, 1, 2, mp_irq_trigger); - -static mp_obj_t mp_irq_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 0, 0, false); - mp_irq_handler(MP_OBJ_TO_PTR(self_in)); - return mp_const_none; -} - -static const mp_rom_map_elem_t mp_irq_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_flags), MP_ROM_PTR(&mp_irq_flags_obj) }, - { MP_ROM_QSTR(MP_QSTR_trigger), MP_ROM_PTR(&mp_irq_trigger_obj) }, -}; -static MP_DEFINE_CONST_DICT(mp_irq_locals_dict, mp_irq_locals_dict_table); - -MP_DEFINE_CONST_OBJ_TYPE( - mp_irq_type, - MP_QSTR_irq, - MP_TYPE_FLAG_NONE, - call, mp_irq_call, - locals_dict, &mp_irq_locals_dict - ); - -#endif // MICROPY_ENABLE_SCHEDULER diff --git a/shared/runtime/mpirq.h b/shared/runtime/mpirq.h deleted file mode 100644 index dd423c0101137..0000000000000 --- a/shared/runtime/mpirq.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * This file is part of the MicroPython project, http://micropython.org/ - * - * The MIT License (MIT) - * - * Copyright (c) 2015 Daniel Campora - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -#ifndef MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H -#define MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H - -#include "py/runtime.h" - -/****************************************************************************** - DEFINE CONSTANTS - ******************************************************************************/ - -enum { - MP_IRQ_ARG_INIT_handler = 0, - MP_IRQ_ARG_INIT_trigger, - MP_IRQ_ARG_INIT_hard, - MP_IRQ_ARG_INIT_NUM_ARGS, -}; - -/****************************************************************************** - DEFINE TYPES - ******************************************************************************/ - -typedef mp_uint_t (*mp_irq_trigger_fun_t)(mp_obj_t self, mp_uint_t trigger); -typedef mp_uint_t (*mp_irq_info_fun_t)(mp_obj_t self, mp_uint_t info_type); - -enum { - MP_IRQ_INFO_FLAGS, - MP_IRQ_INFO_TRIGGERS, -}; - -typedef struct _mp_irq_methods_t { - mp_irq_trigger_fun_t trigger; - mp_irq_info_fun_t info; -} mp_irq_methods_t; - -typedef struct _mp_irq_obj_t { - mp_obj_base_t base; - mp_irq_methods_t *methods; - mp_obj_t parent; - mp_obj_t handler; - bool ishard; -} mp_irq_obj_t; - -/****************************************************************************** - DECLARE EXPORTED DATA - ******************************************************************************/ - -extern const mp_arg_t mp_irq_init_args[]; -extern const mp_obj_type_t mp_irq_type; - -/****************************************************************************** - DECLARE PUBLIC FUNCTIONS - ******************************************************************************/ - -mp_irq_obj_t *mp_irq_new(const mp_irq_methods_t *methods, mp_obj_t parent); -void mp_irq_init(mp_irq_obj_t *self, const mp_irq_methods_t *methods, mp_obj_t parent); -void mp_irq_handler(mp_irq_obj_t *self); - -#endif // MICROPY_INCLUDED_LIB_UTILS_MPIRQ_H diff --git a/shared/runtime/pyexec.c b/shared/runtime/pyexec.c index 06680ff2dd125..bc5f89bdc81ad 100644 --- a/shared/runtime/pyexec.c +++ b/shared/runtime/pyexec.c @@ -38,15 +38,12 @@ #include "py/gc.h" #include "py/frozenmod.h" #include "py/mphal.h" -#if MICROPY_HW_ENABLE_USB -#include "irq.h" -#include "usb.h" -#endif #include "shared/readline/readline.h" #include "shared/runtime/pyexec.h" +#include "extmod/modplatform.h" #include "genhdr/mpversion.h" -// CIRCUITPY-CHANGE: atexit support +// CIRCUITPY-CHANGE: add atexit support #if CIRCUITPY_ATEXIT #include "shared-module/atexit/__init__.h" #endif @@ -87,57 +84,71 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input nlr_buf_t nlr; nlr.ret_val = NULL; if (nlr_push(&nlr) == 0) { - // CIRCUITPY-CHANGE - mp_obj_t module_fun = mp_const_none; - // CIRCUITPY-CHANGE + #if MICROPY_PYEXEC_ENABLE_VM_ABORT + nlr_set_abort(&nlr); + #endif + + // CIRCUITPY-CHANGE: move declaration for easier handling of atexit #if. + // Also make it possible to determine if module_fun was set. + mp_obj_t module_fun = NULL; + + // CIRCUITPY-CHANGE: add atexit support #if CIRCUITPY_ATEXIT - if (!(exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT)) + if (exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT) { + atexit_callback_t *callback = (atexit_callback_t *)source; + mp_call_function_n_kw(callback->func, callback->n_pos, callback->n_kw, callback->args); + } else #endif - // CIRCUITPY-CHANGE: multiple code changes - { - #if MICROPY_MODULE_FROZEN_MPY - if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { - // source is a raw_code object, create the function - const mp_frozen_module_t *frozen = source; - mp_module_context_t *ctx = m_new_obj(mp_module_context_t); - ctx->module.globals = mp_globals_get(); - ctx->constants = frozen->constants; - module_fun = mp_make_function_from_proto_fun(frozen->proto_fun, ctx, NULL); - } else - #endif - { - #if MICROPY_ENABLE_COMPILER - mp_lexer_t *lex; - if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { - const vstr_t *vstr = source; - lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); - } else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) { - lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source); - } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { - lex = mp_lexer_new_from_file(qstr_from_str(source)); - } else { - lex = (mp_lexer_t *)source; - } - // source is a lexer, parse and compile the script - qstr source_name = lex->source_name; - // CIRCUITPY-CHANGE - #if MICROPY_PY___FILE__ - if (input_kind == MP_PARSE_FILE_INPUT) { - mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); - } - #endif - mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); - module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL); - #else - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported")); - #endif + #if MICROPY_MODULE_FROZEN_MPY + if (exec_flags & EXEC_FLAG_SOURCE_IS_RAW_CODE) { + // source is a raw_code object, create the function + const mp_frozen_module_t *frozen = source; + mp_module_context_t *ctx = m_new_obj(mp_module_context_t); + ctx->module.globals = mp_globals_get(); + ctx->constants = frozen->constants; + module_fun = mp_make_function_from_proto_fun(frozen->proto_fun, ctx, NULL); + } else + #endif + { + #if MICROPY_ENABLE_COMPILER + mp_lexer_t *lex; + if (exec_flags & EXEC_FLAG_SOURCE_IS_VSTR) { + const vstr_t *vstr = source; + lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, vstr->buf, vstr->len, 0); + } else if (exec_flags & EXEC_FLAG_SOURCE_IS_READER) { + lex = mp_lexer_new(MP_QSTR__lt_stdin_gt_, *(mp_reader_t *)source); + } else if (exec_flags & EXEC_FLAG_SOURCE_IS_FILENAME) { + lex = mp_lexer_new_from_file(qstr_from_str(source)); + } else { + lex = (mp_lexer_t *)source; } - - // If the code was loaded from a file, collect any garbage before running. + // source is a lexer, parse and compile the script + qstr source_name = lex->source_name; + #if MICROPY_MODULE___FILE__ if (input_kind == MP_PARSE_FILE_INPUT) { - gc_collect(); + mp_store_global(MP_QSTR___file__, MP_OBJ_NEW_QSTR(source_name)); } + #endif + mp_parse_tree_t parse_tree = mp_parse(lex, input_kind); + #if defined(MICROPY_UNIX_COVERAGE) + // allow to print the parse tree in the coverage build + if (mp_verbose_flag >= 3) { + printf("----------------\n"); + mp_parse_node_print(&mp_plat_print, parse_tree.root, 0); + printf("----------------\n"); + } + #endif + module_fun = mp_compile(&parse_tree, source_name, exec_flags & EXEC_FLAG_IS_REPL); + #else + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("script compilation not supported")); + #endif + } + + // CIRCUITPY-CHANGE: garbage collect after loading + // If the code was loaded from a file, collect any garbage before running. + if (input_kind == MP_PARSE_FILE_INPUT) { + gc_collect(); } // execute code @@ -147,22 +158,19 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input #if MICROPY_REPL_INFO start = mp_hal_ticks_ms(); #endif - // CIRCUITPY-CHANGE - #if CIRCUITPY_ATEXIT - if (exec_flags & EXEC_FLAG_SOURCE_IS_ATEXIT) { - atexit_callback_t *callback = (atexit_callback_t *)source; - mp_call_function_n_kw(callback->func, callback->n_pos, callback->n_kw, callback->args); - } else + #if MICROPY_PYEXEC_COMPILE_ONLY + if (!mp_compile_only) #endif - // CIRCUITPY-CHANGE - if (module_fun != mp_const_none) { - mp_call_function_0(module_fun); + { + // CIRCUITPY-CHANGE: if atexit function was called, there is nothing to call. + if (module_fun != NULL) { + mp_call_function_0(module_fun); + } } mp_hal_set_interrupt_char(-1); // disable interrupt mp_handle_pending(true); // handle any pending exceptions (and any callbacks) nlr_pop(); - // CIRCUITPY-CHANGE - ret = 0; + ret = PYEXEC_NORMAL_EXIT; if (exec_flags & EXEC_FLAG_PRINT_EOF) { mp_hal_stdout_tx_strn("\x04", 1); } @@ -181,36 +189,65 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input mp_hal_stdout_tx_strn("\x04", 1); } - // check for SystemExit - - // CIRCUITPY-CHANGE + // CIRCUITPY-CHANGE: Name and use some values. // nlr.ret_val is an exception object. mp_obj_t exception_obj = (mp_obj_t)nlr.ret_val; + const mp_obj_type_t *exception_obj_type = mp_obj_get_type(exception_obj); - if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(exception_obj)), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { - // at the moment, the value of SystemExit is unused + #if MICROPY_PYEXEC_ENABLE_VM_ABORT + if (nlr.ret_val == NULL) { // abort + ret = PYEXEC_ABORT; + } else + #endif + + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exception_obj_type), MP_OBJ_FROM_PTR(&mp_type_SystemExit))) { // system exit + #if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING + // None is an exit value of 0; an int is its value; anything else is 1 + mp_obj_t val = mp_obj_exception_get_value(MP_OBJ_FROM_PTR(nlr.ret_val)); + if (val != mp_const_none) { + if (mp_obj_is_int(val)) { + ret = (int)mp_obj_int_get_truncated(val); + } else { + mp_obj_print_helper(MICROPY_ERROR_PRINTER, val, PRINT_STR); + mp_print_str(MICROPY_ERROR_PRINTER, "\n"); + ret = PYEXEC_UNHANDLED_EXCEPTION; + } + } else { + ret = PYEXEC_NORMAL_EXIT; + } + // Set PYEXEC_FORCED_EXIT flag so REPL knows to exit + ret |= PYEXEC_FORCED_EXIT; + #else ret = PYEXEC_FORCED_EXIT; - // CIRCUITPY-CHANGE + #endif + // CIRCUITPY-CHANGE: support DeepSleepRequest #if CIRCUITPY_ALARM - } else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(mp_obj_get_type(exception_obj)), MP_OBJ_FROM_PTR(&mp_type_DeepSleepRequest))) { + } else if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(exception_obj_type), MP_OBJ_FROM_PTR(&mp_type_DeepSleepRequest))) { ret = PYEXEC_DEEP_SLEEP; #endif + // CIRCUITPY-CHANGE: support ReloadException } else if (exception_obj == MP_OBJ_FROM_PTR(&MP_STATE_VM(mp_reload_exception))) { ret = PYEXEC_RELOAD; - } else { - mp_obj_print_exception(&mp_plat_print, exception_obj); - ret = PYEXEC_EXCEPTION; + } else { // other exception + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + ret = PYEXEC_UNHANDLED_EXCEPTION; + #if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING + if (mp_obj_is_subclass_fast(MP_OBJ_FROM_PTR(((mp_obj_base_t *)nlr.ret_val)->type), MP_OBJ_FROM_PTR(&mp_type_KeyboardInterrupt))) { // keyboard interrupt + ret = PYEXEC_KEYBOARD_INTERRUPT; + } + #endif } - } + + // CIRCUITPY_CHANGE: Fill in result out argument. if (result != NULL) { result->return_code = ret; - #if CIRCUITPY_ALARM // Don't set the exception object if we exited for deep sleep. - if (ret != 0 && ret != PYEXEC_DEEP_SLEEP) { - #else - if (ret != 0) { + if (ret != 0 + #if CIRCUITPY_ALARM + && ret != PYEXEC_DEEP_SLEEP #endif + ) { mp_obj_t return_value = (mp_obj_t)nlr.ret_val; result->exception = return_value; result->exception_line = -1; @@ -227,6 +264,10 @@ static int parse_compile_execute(const void *source, mp_parse_input_kind_t input } } + #if MICROPY_PYEXEC_ENABLE_VM_ABORT + nlr_set_abort(NULL); + #endif + #if MICROPY_REPL_INFO // display debugging info if wanted if ((exec_flags & EXEC_FLAG_ALLOW_DEBUGGING) && repl_display_debugging_info) { @@ -484,7 +525,10 @@ static int pyexec_friendly_repl_process_char(int c) { // CIRCUITPY-CHANGE: print CircuitPython-style banner. mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); mp_hal_stdout_tx_str("\r\n"); + #if MICROPY_PY_BUILTINS_HELP + // CIRCUITPY-CHANGE: don't print help info // mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n"); + #endif goto input_restart; } else if (ret == CHAR_CTRL_C) { // break @@ -570,10 +614,21 @@ MP_REGISTER_ROOT_POINTER(vstr_t * repl_line); #else // MICROPY_REPL_EVENT_DRIVEN +// CIRCUITPY-CHANGE: avoid warnings +#if !defined(MICROPY_HAL_HAS_STDIO_MODE_SWITCH) || !MICROPY_HAL_HAS_STDIO_MODE_SWITCH +// If the port doesn't need any stdio mode switching calls then provide trivial ones. +static inline void mp_hal_stdio_mode_raw(void) { +} +static inline void mp_hal_stdio_mode_orig(void) { +} +#endif + int pyexec_raw_repl(void) { vstr_t line; vstr_init(&line, 32); + mp_hal_stdio_mode_raw(); + raw_repl_reset: mp_hal_stdout_tx_str("raw REPL; CTRL-B to exit\r\n"); @@ -587,6 +642,7 @@ int pyexec_raw_repl(void) { if (vstr_len(&line) == 2 && vstr_str(&line)[0] == CHAR_CTRL_E) { int ret = do_reader_stdin(vstr_str(&line)[1]); if (ret & PYEXEC_FORCED_EXIT) { + mp_hal_stdio_mode_orig(); return ret; } vstr_reset(&line); @@ -599,6 +655,7 @@ int pyexec_raw_repl(void) { mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); pyexec_mode_kind = PYEXEC_MODE_FRIENDLY_REPL; + mp_hal_stdio_mode_orig(); return 0; } else if (c == CHAR_CTRL_C) { // clear line @@ -619,14 +676,18 @@ int pyexec_raw_repl(void) { // exit for a soft reset mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); + mp_hal_stdio_mode_orig(); return PYEXEC_FORCED_EXIT; } + // Switch to original terminal mode to execute code, eg to support keyboard interrupt (SIGINT). + mp_hal_stdio_mode_orig(); // CIRCUITPY-CHANGE: add last arg, handle reload int ret = parse_compile_execute(&line, MP_PARSE_FILE_INPUT, EXEC_FLAG_PRINT_EOF | EXEC_FLAG_SOURCE_IS_VSTR, NULL); if (ret & (PYEXEC_FORCED_EXIT | PYEXEC_RELOAD)) { return ret; } + mp_hal_stdio_mode_raw(); } } @@ -634,6 +695,8 @@ int pyexec_friendly_repl(void) { vstr_t line; vstr_init(&line, 32); + mp_hal_stdio_mode_raw(); + friendly_repl_reset: // CIRCUITPY-CHANGE: CircuitPython-style banner. mp_hal_stdout_tx_str("\r\n"); @@ -661,20 +724,6 @@ int pyexec_friendly_repl(void) { for (;;) { input_restart: - - #if MICROPY_HW_ENABLE_USB - if (usb_vcp_is_enabled()) { - // If the user gets to here and interrupts are disabled then - // they'll never see the prompt, traceback etc. The USB REPL needs - // interrupts to be enabled or no transfers occur. So we try to - // do the user a favor and re-enable interrupts. - if (query_irq() == IRQ_STATE_DISABLED) { - enable_irq(IRQ_STATE_ENABLED); - mp_hal_stdout_tx_str("MPY: enabling IRQs\r\n"); - } - } - #endif - // If the GC is locked at this point there is no way out except a reset, // so force the GC to be unlocked to help the user debug what went wrong. if (MP_STATE_THREAD(gc_lock_depth) != 0) { @@ -705,6 +754,7 @@ int pyexec_friendly_repl(void) { mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); pyexec_mode_kind = PYEXEC_MODE_RAW_REPL; + mp_hal_stdio_mode_orig(); return 0; } else if (ret == CHAR_CTRL_B) { // reset friendly REPL @@ -718,6 +768,7 @@ int pyexec_friendly_repl(void) { // exit for a soft reset mp_hal_stdout_tx_str("\r\n"); vstr_clear(&line); + mp_hal_stdio_mode_orig(); return PYEXEC_FORCED_EXIT; } else if (ret == CHAR_CTRL_E) { // paste mode @@ -762,11 +813,14 @@ int pyexec_friendly_repl(void) { } } + // Switch to original terminal mode to execute code, eg to support keyboard interrupt (SIGINT). + mp_hal_stdio_mode_orig(); // CIRCUITPY-CHANGE: add last arg ret = parse_compile_execute(&line, parse_input_kind, EXEC_FLAG_ALLOW_DEBUGGING | EXEC_FLAG_IS_REPL | EXEC_FLAG_SOURCE_IS_VSTR, NULL); if (ret & (PYEXEC_FORCED_EXIT | PYEXEC_RELOAD)) { return ret; } + mp_hal_stdio_mode_raw(); } } diff --git a/shared/runtime/pyexec.h b/shared/runtime/pyexec.h index 55a3003791cb2..9d1ef2a3cb029 100644 --- a/shared/runtime/pyexec.h +++ b/shared/runtime/pyexec.h @@ -51,6 +51,17 @@ extern pyexec_mode_kind_t pyexec_mode_kind; #define PYEXEC_DEEP_SLEEP (0x400) #define PYEXEC_RELOAD (0x800) +#if MICROPY_PYEXEC_ENABLE_EXIT_CODE_HANDLING +#define PYEXEC_NORMAL_EXIT (0) +#define PYEXEC_UNHANDLED_EXCEPTION (1) +#define PYEXEC_KEYBOARD_INTERRUPT (128 + 2) // same as SIG INT exit code +#define PYEXEC_ABORT (128 + 9) // same as SIG KILL exit code +#else +#define PYEXEC_NORMAL_EXIT (1) +#define PYEXEC_UNHANDLED_EXCEPTION (0) +#define PYEXEC_ABORT PYEXEC_FORCED_EXIT +#endif + int pyexec_raw_repl(void); int pyexec_friendly_repl(void); // CIRCUITPY-CHANGE: result out argument diff --git a/shared/timeutils/timeutils.c b/shared/timeutils/timeutils.c index 4282a0178dd6e..0c6916e06ddd6 100644 --- a/shared/timeutils/timeutils.c +++ b/shared/timeutils/timeutils.c @@ -29,12 +29,27 @@ #include "shared/timeutils/timeutils.h" -// LEAPOCH corresponds to 2000-03-01, which is a mod-400 year, immediately -// after Feb 29. We calculate seconds as a signed integer relative to that. +// To maintain reasonable compatibility with CPython on embedded systems, +// and avoid breaking anytime soon, timeutils functions are required to +// work properly between 1970 and 2099 on all ports. // -// Our timebase is relative to 2000-01-01. - -#define LEAPOCH ((31 + 29) * 86400) +// During that period of time, leap years occur every 4 years without +// exception, so we can keep the code short for 32 bit machines. + +// The last leap day before the required period is Feb 29, 1968. +// This is the number of days to add to get to that date. +#define PREV_LEAP_DAY ((mp_uint_t)(365 + 366 - (31 + 29))) +#define PREV_LEAP_YEAR 1968 + +// On ports where either MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND or +// MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE is enabled, we include extra +// code to support leap years outside of the 'easy' period. +// Computation is then made based on 1600 (a mod-400 year). +// This is the number of days between 1600 and 1968. +#define QC_BASE_DAY 134409 +#define QC_LEAP_YEAR 1600 +// This is the number of leap days between 1600 and 1970 +#define QC_LEAP_DAYS 89 #define DAYS_PER_400Y (365 * 400 + 97) #define DAYS_PER_100Y (365 * 100 + 24) @@ -42,8 +57,20 @@ static const uint16_t days_since_jan1[] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }; +// type used internally to count small integers relative to epoch +// (using uint when possible produces smaller code on some platforms) +#if MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE +typedef mp_int_t relint_t; +#else +typedef mp_uint_t relint_t; +#endif + bool timeutils_is_leap_year(mp_uint_t year) { + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0; + #else + return year % 4 == 0; + #endif } // month is one based @@ -65,67 +92,67 @@ mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date) { return yday; } -void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, timeutils_struct_time_t *tm) { - // The following algorithm was adapted from musl's __secs_to_tm and adapted - // for differences in MicroPython's timebase. - - mp_int_t seconds = t - LEAPOCH; +void timeutils_seconds_since_1970_to_struct_time(timeutils_timestamp_t seconds, timeutils_struct_time_t *tm) { + // The following algorithm was inspired from musl's __secs_to_tm + // and simplified to reduce code footprint in the simple case - mp_int_t days = seconds / 86400; + relint_t days = seconds / 86400; seconds %= 86400; + #if MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE if (seconds < 0) { seconds += 86400; days -= 1; } + #endif tm->tm_hour = seconds / 3600; tm->tm_min = seconds / 60 % 60; tm->tm_sec = seconds % 60; - mp_int_t wday = (days + 2) % 7; // Mar 1, 2000 was a Wednesday (2) + relint_t wday = (days + 3) % 7; // Jan 1, 1970 was a Thursday (3) + #if MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE if (wday < 0) { wday += 7; } + #endif tm->tm_wday = wday; - mp_int_t qc_cycles = days / DAYS_PER_400Y; + days += PREV_LEAP_DAY; + + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE + // rebase day to the oldest supported date (=> always positive) + mp_uint_t base_year = QC_LEAP_YEAR; + days += QC_BASE_DAY; + mp_uint_t qc_cycles = days / DAYS_PER_400Y; days %= DAYS_PER_400Y; - if (days < 0) { - days += DAYS_PER_400Y; - qc_cycles--; - } - mp_int_t c_cycles = days / DAYS_PER_100Y; + mp_uint_t c_cycles = days / DAYS_PER_100Y; if (c_cycles == 4) { c_cycles--; } days -= (c_cycles * DAYS_PER_100Y); - - mp_int_t q_cycles = days / DAYS_PER_4Y; + #else + mp_uint_t base_year = PREV_LEAP_YEAR; + mp_uint_t qc_cycles = 0; + mp_uint_t c_cycles = 0; + #endif + + mp_uint_t q_cycles = days / DAYS_PER_4Y; + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE if (q_cycles == 25) { q_cycles--; } + #endif days -= q_cycles * DAYS_PER_4Y; - mp_int_t years = days / 365; + relint_t years = days / 365; if (years == 4) { years--; } days -= (years * 365); - /* We will compute tm_yday at the very end - mp_int_t leap = !years && (q_cycles || !c_cycles); - - tm->tm_yday = days + 31 + 28 + leap; - if (tm->tm_yday >= 365 + leap) { - tm->tm_yday -= 365 + leap; - } - - tm->tm_yday++; // Make one based - */ - - tm->tm_year = 2000 + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; + tm->tm_year = base_year + years + 4 * q_cycles + 100 * c_cycles + 400 * qc_cycles; // Note: days_in_month[0] corresponds to March - static const int8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; + static const uint8_t days_in_month[] = {31, 30, 31, 30, 31, 31, 30, 31, 30, 31, 31, 29}; mp_int_t month; for (month = 0; days_in_month[month] <= days; month++) { @@ -144,21 +171,28 @@ void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, timeutils_struct_t } // returns the number of seconds, as an integer, since 2000-01-01 -mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, +timeutils_timestamp_t timeutils_seconds_since_1970(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { - return - second - + minute * 60 - + hour * 3600 - + (timeutils_year_day(year, month, date) - 1 - + ((year - 2000 + 3) / 4) // add a day each 4 years starting with 2001 - - ((year - 2000 + 99) / 100) // subtract a day each 100 years starting with 2001 - + ((year - 2000 + 399) / 400) // add a day each 400 years starting with 2001 - ) * 86400 - + (year - 2000) * 31536000; + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE + mp_uint_t ref_year = QC_LEAP_YEAR; + #else + mp_uint_t ref_year = PREV_LEAP_YEAR; + #endif + timeutils_timestamp_t res; + res = ((relint_t)year - 1970) * 365; + res += (year - (ref_year + 1)) / 4; // add a day each 4 years + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE + res -= (year - (ref_year + 1)) / 100; // subtract a day each 100 years + res += (year - (ref_year + 1)) / 400; // add a day each 400 years + res -= QC_LEAP_DAYS; + #endif + res += timeutils_year_day(year, month, date) - 1; + res *= 86400; + res += hour * 3600 + minute * 60 + second; + return res; } -mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, +timeutils_timestamp_t timeutils_mktime_1970(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { // Normalize the tuple. This allows things like: @@ -211,12 +245,16 @@ mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, year++; } } - return timeutils_seconds_since_2000(year, month, mday, hours, minutes, seconds); + return timeutils_seconds_since_1970(year, month, mday, hours, minutes, seconds); } // Calculate the weekday from the date. // The result is zero based with 0 = Monday. // by Michael Keith and Tom Craver, 1990. int timeutils_calc_weekday(int y, int m, int d) { - return ((d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 - y / 100 + y / 400) + 6) % 7; + return ((d += m < 3 ? y-- : y - 2, 23 * m / 9 + d + 4 + y / 4 + #if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE + - y / 100 + y / 400 + #endif + ) + 6) % 7; } diff --git a/shared/timeutils/timeutils.h b/shared/timeutils/timeutils.h index b41903d3b2777..35356b462aafc 100644 --- a/shared/timeutils/timeutils.h +++ b/shared/timeutils/timeutils.h @@ -27,12 +27,23 @@ #ifndef MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H #define MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H -// CIRCUITPY-CHANGE: MICROPY_EPOCH_IS_1970 -#include "mpconfigport.h" +#include "py/obj.h" +#if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE +#include // required for trunc() +#endif + +// `timeutils_timestamp_t` is the type used internally by timeutils to +// represent timestamps, and is always referenced to 1970. +// It may not match the platform-specific `mp_timestamp_t`. +#if MICROPY_TIME_SUPPORT_Y2100_AND_BEYOND || MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE +typedef long long timeutils_timestamp_t; +#else +typedef mp_uint_t timeutils_timestamp_t; +#endif // The number of seconds between 1970/1/1 and 2000/1/1 is calculated using: // time.mktime((2000,1,1,0,0,0,0,0,0)) - time.mktime((1970,1,1,0,0,0,0,0,0)) -#define TIMEUTILS_SECONDS_1970_TO_2000 (946684800ULL) +#define TIMEUTILS_SECONDS_1970_TO_2000 (946684800LL) typedef struct _timeutils_struct_time_t { uint16_t tm_year; // i.e. 2014 @@ -48,66 +59,116 @@ typedef struct _timeutils_struct_time_t { bool timeutils_is_leap_year(mp_uint_t year); mp_uint_t timeutils_days_in_month(mp_uint_t year, mp_uint_t month); mp_uint_t timeutils_year_day(mp_uint_t year, mp_uint_t month, mp_uint_t date); +int timeutils_calc_weekday(int y, int m, int d); -void timeutils_seconds_since_2000_to_struct_time(mp_uint_t t, +void timeutils_seconds_since_1970_to_struct_time(timeutils_timestamp_t t, timeutils_struct_time_t *tm); // Year is absolute, month/date are 1-based, hour/minute/second are 0-based. -mp_uint_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, +timeutils_timestamp_t timeutils_seconds_since_1970(mp_uint_t year, mp_uint_t month, mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second); // Year is absolute, month/mday are 1-based, hours/minutes/seconds are 0-based. -mp_uint_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, +timeutils_timestamp_t timeutils_mktime_1970(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds); +static inline mp_timestamp_t timeutils_obj_get_timestamp(mp_obj_t o_in) { + #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE + mp_float_t val = mp_obj_get_float(o_in); + return (mp_timestamp_t)MICROPY_FLOAT_C_FUN(trunc)(val); + #elif MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_UINT + return mp_obj_get_uint(o_in); + #else + return mp_obj_get_ll(o_in); + #endif +} + +static inline mp_obj_t timeutils_obj_from_timestamp(mp_timestamp_t t) { + #if MICROPY_TIMESTAMP_IMPL == MICROPY_TIMESTAMP_IMPL_UINT + return mp_obj_new_int_from_uint(t); + #else + return mp_obj_new_int_from_ll(t); + #endif +} + +static inline void timeutils_seconds_since_2000_to_struct_time(mp_timestamp_t t, timeutils_struct_time_t *tm) { + timeutils_seconds_since_1970_to_struct_time((timeutils_timestamp_t)(t + TIMEUTILS_SECONDS_1970_TO_2000), tm); +} + +// Year is absolute, month/date are 1-based, hour/minute/second are 0-based. +static inline mp_timestamp_t timeutils_seconds_since_2000(mp_uint_t year, mp_uint_t month, mp_uint_t date, + mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return (mp_timestamp_t)timeutils_seconds_since_1970(year, month, date, hour, minute, second) - TIMEUTILS_SECONDS_1970_TO_2000; +} + +// Year is absolute, month/mday are 1-based, hours/minutes/seconds are 0-based. +static inline mp_timestamp_t timeutils_mktime_2000(mp_uint_t year, mp_int_t month, mp_int_t mday, + mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { + return (mp_timestamp_t)timeutils_mktime_1970(year, month, mday, hours, minutes, seconds) - TIMEUTILS_SECONDS_1970_TO_2000; +} + + // Select the Epoch used by the port. #if MICROPY_EPOCH_IS_1970 -static inline void timeutils_seconds_since_epoch_to_struct_time(uint64_t t, timeutils_struct_time_t *tm) { - // TODO this will give incorrect results for dates before 2000/1/1 - timeutils_seconds_since_2000_to_struct_time((mp_uint_t)(t - TIMEUTILS_SECONDS_1970_TO_2000), tm); +static inline void timeutils_seconds_since_epoch_to_struct_time(mp_timestamp_t t, timeutils_struct_time_t *tm) { + timeutils_seconds_since_1970_to_struct_time(t, tm); +} + +// Year is absolute, month/date are 1-based, hour/minute/second are 0-based. +static inline mp_timestamp_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, mp_uint_t date, + mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return timeutils_seconds_since_1970(year, month, date, hour, minute, second); } // Year is absolute, month/mday are 1-based, hours/minutes/seconds are 0-based. -static inline uint64_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { - return timeutils_mktime_2000(year, month, mday, hours, minutes, seconds) + TIMEUTILS_SECONDS_1970_TO_2000; +static inline mp_timestamp_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, + mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { + return timeutils_mktime_1970(year, month, mday, hours, minutes, seconds); } -// Year is absolute, month/date are 1-based, hour/minute/second are 0-based. -static inline uint64_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, - mp_uint_t date, mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { - // TODO this will give incorrect results for dates before 2000/1/1 - return timeutils_seconds_since_2000(year, month, date, hour, minute, second) + TIMEUTILS_SECONDS_1970_TO_2000; +static inline mp_timestamp_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(int64_t ns) { + return (mp_timestamp_t)(ns / 1000000000LL); } -static inline mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) { - return (mp_uint_t)(ns / 1000000000ULL); +static inline int64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_timestamp_t s) { + return (int64_t)s * 1000000000LL; } -static inline uint64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(uint64_t ns) { +static inline int64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(int64_t ns) { return ns; } #else // Epoch is 2000 -#define timeutils_seconds_since_epoch_to_struct_time timeutils_seconds_since_2000_to_struct_time -#define timeutils_seconds_since_epoch timeutils_seconds_since_2000 -#define timeutils_mktime timeutils_mktime_2000 +static inline void timeutils_seconds_since_epoch_to_struct_time(mp_timestamp_t t, timeutils_struct_time_t *tm) { + timeutils_seconds_since_2000_to_struct_time(t, tm); +} -static inline uint64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_uint_t s) { - return ((uint64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000ULL; +// Year is absolute, month/date are 1-based, hour/minute/second are 0-based. +static inline mp_timestamp_t timeutils_seconds_since_epoch(mp_uint_t year, mp_uint_t month, mp_uint_t date, + mp_uint_t hour, mp_uint_t minute, mp_uint_t second) { + return timeutils_seconds_since_2000(year, month, date, hour, minute, second); } -static inline mp_uint_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(uint64_t ns) { - return ns / 1000000000ULL - TIMEUTILS_SECONDS_1970_TO_2000; +// Year is absolute, month/mday are 1-based, hours/minutes/seconds are 0-based. +static inline mp_timestamp_t timeutils_mktime(mp_uint_t year, mp_int_t month, mp_int_t mday, + mp_int_t hours, mp_int_t minutes, mp_int_t seconds) { + return timeutils_mktime_2000(year, month, mday, hours, minutes, seconds); +} + +static inline mp_timestamp_t timeutils_seconds_since_epoch_from_nanoseconds_since_1970(int64_t ns) { + return (mp_timestamp_t)(ns / 1000000000LL - TIMEUTILS_SECONDS_1970_TO_2000); +} + +static inline int64_t timeutils_seconds_since_epoch_to_nanoseconds_since_1970(mp_timestamp_t s) { + return ((int64_t)s + TIMEUTILS_SECONDS_1970_TO_2000) * 1000000000LL; } static inline int64_t timeutils_nanoseconds_since_epoch_to_nanoseconds_since_1970(int64_t ns) { - return ns + TIMEUTILS_SECONDS_1970_TO_2000 * 1000000000ULL; + return ns + TIMEUTILS_SECONDS_1970_TO_2000 * 1000000000LL; } #endif -int timeutils_calc_weekday(int y, int m, int d); - #endif // MICROPY_INCLUDED_LIB_TIMEUTILS_TIMEUTILS_H diff --git a/supervisor/filesystem.h b/supervisor/filesystem.h index a0445b0510da9..30cd83d443815 100644 --- a/supervisor/filesystem.h +++ b/supervisor/filesystem.h @@ -21,6 +21,7 @@ void filesystem_set_internal_writable_by_usb(bool usb_writable); void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection); void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable); void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concurrent_write_protection); +void filesystem_set_ignore_write_protection(fs_user_mount_t *vfs, bool ignore_write_protection); // Whether user code can modify the filesystem. It doesn't depend on the state // of USB. Don't use this for a workflow. In workflows, grab the shared file diff --git a/supervisor/port.h b/supervisor/port.h index cc49538218fac..f0e13e167cce1 100644 --- a/supervisor/port.h +++ b/supervisor/port.h @@ -9,6 +9,7 @@ #include #include +#include "py/obj.h" #include "supervisor/shared/safe_mode.h" // Provided by the linker; @@ -23,13 +24,13 @@ extern uint32_t _ebss; safe_mode_t port_init(void); // Reset the microcontroller completely. -void reset_cpu(void) NORETURN; +void reset_cpu(void) MP_NORETURN; // Reset the microcontroller state. void reset_port(void); // Reset to the bootloader -void reset_to_bootloader(void) NORETURN; +void reset_to_bootloader(void) MP_NORETURN; // Get stack limit address uint32_t *port_stack_get_limit(void); @@ -91,9 +92,17 @@ void port_wake_main_task(void); void port_wake_main_task_from_isr(void); // Some ports may use real RTOS tasks besides the background task framework of -// CircuitPython. Calling this will yield to other tasks and then return to the -// CircuitPython task when others are done. -void port_yield(void); +// CircuitPython. Calling this will yield to other tasks at the same priority level +// (or higher priority level if pre-emption is not immediate in the RTOS) +// and then return to the CircuitPython task when others are done. +// Note that this does NOT yield to lower priority tasks. Use port_task_sleep_ms() instead. +void port_task_yield(void); + +// On ports using real RTOS tasks, yield to other tasks for at least msecs. +// This will allow lower priority tasks to run. +// On non-RTOS implementations, this just sleeps for msecs and will run CircuitPython +// background tasks. +void port_task_sleep_ms(uint32_t msecs); // Some ports want to add information to boot_out.txt. // A default weak implementation is provided that does nothing. @@ -107,3 +116,15 @@ void port_gc_collect(void); // this function to sense the button. Ports that need to can override this // function to provide their own implementation. bool port_boot_button_pressed(void); + +// Allocating objects on the port heap, not the VM heap. +#define mp_obj_port_malloc(struct_type, obj_type) ((struct_type *)mp_obj_port_malloc_helper(sizeof(struct_type), obj_type)) +#define mp_obj_port_malloc_var(struct_type, var_field, var_type, var_num, obj_type) ((struct_type *)mp_obj_port_malloc_helper(offsetof(struct_type, var_field) + sizeof(var_type) * (var_num), obj_type)) + +void *mp_obj_port_malloc_helper(size_t num_bytes, const mp_obj_type_t *type); +mp_obj_t mp_obj_new_port_tuple(size_t n, const mp_obj_t *items); + +// Safely free an object that may have been allocated from either the GC heap or port heap. +// If the pointer is on the GC heap, it will be freed by the GC automatically. +// If the pointer is not on the GC heap, it will be freed with port_free. +void circuitpy_free_obj(mp_obj_t obj); diff --git a/supervisor/port_heap.h b/supervisor/port_heap.h index 07a3c884e241e..3e6b5a660fa79 100644 --- a/supervisor/port_heap.h +++ b/supervisor/port_heap.h @@ -21,9 +21,16 @@ void port_heap_init(void); void *port_malloc(size_t size, bool dma_capable); +void *port_malloc_zero(size_t size, bool dma_capable); void port_free(void *ptr); void *port_realloc(void *ptr, size_t size, bool dma_capable); +#if !CIRCUITPY_ALL_MEMORY_DMA_CAPABLE +// Check if a buffer pointer is in DMA-capable memory. DMA-capable memory is also accessible during +// flash operations. +bool port_buffer_is_dma_capable(const void *ptr); +#endif + size_t port_heap_get_largest_free_size(void); diff --git a/supervisor/shared/bluetooth/bluetooth.c b/supervisor/shared/bluetooth/bluetooth.c index 055a5cdf1fdfd..cccb371fc4dd2 100644 --- a/supervisor/shared/bluetooth/bluetooth.c +++ b/supervisor/shared/bluetooth/bluetooth.c @@ -36,8 +36,8 @@ #include "supervisor/shared/status_bar.h" #endif -#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" +#if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif @@ -216,11 +216,11 @@ void supervisor_bluetooth_init(void) { // Checking here allows us to have the status LED solidly on even if no button was // pressed. bool wifi_workflow_active = false; - #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV + #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_SETTINGS_TOML char _api_password[64]; const size_t api_password_len = sizeof(_api_password) - 1; - os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len); - wifi_workflow_active = result == GETENV_OK; + settings_err_t result = settings_get_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len); + wifi_workflow_active = result == SETTINGS_OK; #endif if (!bonded && !wifi_workflow_active) { boot_in_discovery_mode = true; diff --git a/supervisor/shared/cpu_regs.S b/supervisor/shared/cpu_regs.S deleted file mode 100644 index 90e5367ed5808..0000000000000 --- a/supervisor/shared/cpu_regs.S +++ /dev/null @@ -1,102 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#include "supervisor/shared/cpu_regs.h" - -#ifdef __arm__ -.syntax unified -.thumb -.text -.align 2 - -@ uint cpu_get_regs_and_sp(r0=uint regs[SAVED_REGISTER_COUNT]) -.global cpu_get_regs_and_sp -.thumb -.thumb_func -.type cpu_get_regs_and_sp, %function -cpu_get_regs_and_sp: -#if __ARM_ARCH_ISA_THUMB == 2 -@ store registers into given array -#ifdef __arm__ -stmia r0!, {r4-r11} -#endif -#if defined(__aarch64__) && __aarch64__ == 1 -#error "aarch64 not supported" -stmia r0!, {x19-x28} -#endif -#ifdef __ARM_FP -#ifdef __arm__ -vstmia r0!, {s16-s31} -#endif -#if defined(__aarch64__) && __aarch64__ == 1 -vst1.64 {d8-d15}, [r0], #16 -#endif -#endif -#endif -// Thumb 1 can only store directly from R0-R7. This is M0 and M23 mostly. -#if __ARM_ARCH_ISA_THUMB == 1 -str r4, [r0, #0] -str r5, [r0, #4] -str r6, [r0, #8] -str r7, [r0, #12] -push {r1} -mov r1, r8 -str r1, [r0, #16] -mov r1, r9 -str r1, [r0, #20] -mov r1, r10 -str r1, [r0, #24] -mov r1, r11 -str r1, [r0, #28] -mov r1, r12 -str r1, [r0, #32] -mov r1, r13 -str r1, [r0, #36] -pop {r1} -#endif - -@ return the sp -mov r0, sp -bx lr -#endif - -#ifdef __riscv -#if __riscv_xlen == 32 -.global cpu_get_regs_and_sp -.type cpu_get_regs_and_sp, %function -cpu_get_regs_and_sp: -sw s0, 0(a0) -sw s1, 4(a0) -sw s2, 8(a0) -sw s3, 12(a0) -sw s4, 16(a0) -sw s5, 20(a0) -sw s6, 24(a0) -sw s7, 28(a0) -sw s8, 32(a0) -sw s9, 36(a0) -sw s10, 40(a0) -sw s11, 44(a0) -#ifdef __riscv_vector -sw fs0, 48(a0) -sw fs1, 52(a0) -sw fs2, 56(a0) -sw fs3, 60(a0) -sw fs4, 64(a0) -sw fs5, 68(a0) -sw fs6, 72(a0) -sw fs7, 76(a0) -sw fs8, 80(a0) -sw fs9, 84(a0) -sw fs10, 88(a0) -sw fs11, 92(a0) -#endif -move a0, sp -ret -#else -#error "Unsupported RISC-V bit length" -#endif -#endif diff --git a/supervisor/shared/cpu_regs.h b/supervisor/shared/cpu_regs.h deleted file mode 100644 index 8243c2388cd9b..0000000000000 --- a/supervisor/shared/cpu_regs.h +++ /dev/null @@ -1,38 +0,0 @@ -// This file is part of the CircuitPython project: https://circuitpython.org -// -// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries -// -// SPDX-License-Identifier: MIT - -#pragma once - -#ifdef __arm__ -#define INTEGER_REGS 10 -#ifdef __ARM_FP -#define FLOATING_POINT_REGS 16 -#endif -#endif - -#ifdef __aarch64__ -#define INTEGER_REGS 10 -#ifdef __ARM_FP -#define FLOATING_POINT_REGS 8 -#endif -#endif - -#ifdef __riscv -#define INTEGER_REGS 12 -#ifdef __riscv_vector -#define FLOATING_POINT_REGS 12 -#endif -#endif - -#ifndef INTEGER_REGS -#define INTEGER_REGS 0 -#endif - -#ifndef FLOATING_POINT_REGS -#define FLOATING_POINT_REGS 0 -#endif - -#define SAVED_REGISTER_COUNT (INTEGER_REGS + FLOATING_POINT_REGS) diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c index 8d53b0e62be7e..1352c76b0066a 100644 --- a/supervisor/shared/display.c +++ b/supervisor/shared/display.c @@ -38,9 +38,11 @@ #if CIRCUITPY_TERMINALIO #include "supervisor/port.h" -#if CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" + +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif + #if CIRCUITPY_LVFONTIO #include "shared-bindings/lvfontio/OnDiskFont.h" #include "supervisor/filesystem.h" @@ -66,10 +68,10 @@ static bool check_for_custom_font(const char **font_path_out) { const char *default_font_path = "/fonts/terminal.lvfontbin"; const char *font_path = default_font_path; - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML // Buffer for storing custom font path static char custom_font_path[128]; - if (common_hal_os_getenv_str("CIRCUITPY_TERMINAL_FONT", custom_font_path, sizeof(custom_font_path)) == GETENV_OK) { + if (settings_get_str("CIRCUITPY_TERMINAL_FONT", custom_font_path, sizeof(custom_font_path)) == SETTINGS_OK) { // Use custom font path from environment variable font_path = custom_font_path; } @@ -125,7 +127,7 @@ static uint8_t *tilegrid_tiles = NULL; static size_t tilegrid_tiles_size = 0; #endif -#if CIRCUITPY_LVFONTIO +#if CIRCUITPY_LVFONTIO && CIRCUITPY_TERMINALIO static lvfontio_ondiskfont_t *lvfont = NULL; #endif @@ -179,8 +181,8 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) { if (width_in_tiles <= 120) { scale = 1; } - #if CIRCUITPY_OS_GETENV - (void)common_hal_os_getenv_int("CIRCUITPY_TERMINAL_SCALE", &scale); + #if CIRCUITPY_SETTINGS_TOML + (void)settings_get_int("CIRCUITPY_TERMINAL_SCALE", &scale); #endif width_in_tiles = MAX(1, width_px / (glyph_width * scale)); diff --git a/supervisor/shared/filesystem.c b/supervisor/shared/filesystem.c index 0df600e77fd2f..eb4b6548d11ff 100644 --- a/supervisor/shared/filesystem.c +++ b/supervisor/shared/filesystem.c @@ -148,8 +148,7 @@ bool filesystem_init(bool create_allowed, bool force_create) { res = f_mkdir(&circuitpy->fatfs, "/sd"); #if CIRCUITPY_FULL_BUILD MAKE_FILE_WITH_OPTIONAL_CONTENTS(&circuitpy->fatfs, "/sd/placeholder.txt", - "SD cards mounted at /sd will hide this file from Python." - " SD cards are not visible via USB CIRCUITPY.\n"); + "SD cards mounted at /sd will hide this file from Python.\n"); #endif #endif @@ -173,7 +172,7 @@ bool filesystem_init(bool create_allowed, bool force_create) { #endif #endif - #if CIRCUITPY_OS_GETENV + #if CIRCUITPY_SETTINGS_TOML make_empty_file(&circuitpy->fatfs, "/settings.toml"); #endif // make a sample code.py file @@ -248,13 +247,15 @@ void filesystem_set_writable_by_usb(fs_user_mount_t *vfs, bool usb_writable) { } bool filesystem_is_writable_by_python(fs_user_mount_t *vfs) { - return (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0 || - (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) == 0; + return ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0) || + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) == 0) || + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION) != 0); } bool filesystem_is_writable_by_usb(fs_user_mount_t *vfs) { - return (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0 || - (vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) != 0; + return ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_CONCURRENT_WRITE_PROTECTED) == 0) || + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_USB_WRITABLE) != 0) || + ((vfs->blockdev.flags & MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION) != 0); } void filesystem_set_internal_concurrent_write_protection(bool concurrent_write_protection) { @@ -269,6 +270,14 @@ void filesystem_set_concurrent_write_protection(fs_user_mount_t *vfs, bool concu } } +void filesystem_set_ignore_write_protection(fs_user_mount_t *vfs, bool ignore_write_protection) { + if (ignore_write_protection) { + vfs->blockdev.flags |= MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION; + } else { + vfs->blockdev.flags &= ~MP_BLOCKDEV_FLAG_IGNORE_WRITE_PROTECTION; + } +} + bool filesystem_present(void) { return _circuitpy_vfs.len > 0; } diff --git a/supervisor/shared/port.c b/supervisor/shared/port.c index 3b9f0c8718161..3c95f2b740931 100644 --- a/supervisor/shared/port.c +++ b/supervisor/shared/port.c @@ -8,7 +8,9 @@ #include +#include "py/mphal.h" #include "py/runtime.h" +#include "py/gc.h" #include "lib/tlsf/tlsf.h" @@ -25,7 +27,11 @@ MP_WEAK void port_wake_main_task(void) { MP_WEAK void port_wake_main_task_from_isr(void) { } -MP_WEAK void port_yield(void) { +MP_WEAK void port_task_yield(void) { +} + +MP_WEAK void port_task_sleep_ms(uint32_t msecs) { + mp_hal_delay_ms(msecs); } MP_WEAK void port_boot_info(void) { @@ -43,10 +49,35 @@ MP_WEAK void *port_malloc(size_t size, bool dma_capable) { return block; } +// Ensure allocated memory is zero. +MP_WEAK void *port_malloc_zero(size_t size, bool dma_capable) { + void *ptr = port_malloc(size, dma_capable); + if (ptr) { + memset(ptr, 0, size); + } + return ptr; +} + MP_WEAK void port_free(void *ptr) { tlsf_free(heap, ptr); } +// Safely free an object that may have been allocated from either the GC heap or port heap. +// If the pointer is on the GC heap, it will be freed by the GC automatically, so we do nothing. +// If the pointer is not on the GC heap (i.e., allocated with port_malloc), we free it with port_free. +// This is safe to call during shutdown when GC may not be available. +void circuitpy_free_obj(mp_obj_t obj) { + if (obj == mp_const_none) { + return; + } + void *ptr = MP_OBJ_TO_PTR(obj); + if (gc_alloc_possible() && gc_ptr_on_heap(ptr)) { + gc_free(ptr); + } else { + port_free(ptr); + } +} + MP_WEAK void *port_realloc(void *ptr, size_t size, bool dma_capable) { return tlsf_realloc(heap, ptr, size); } @@ -80,3 +111,30 @@ MP_WEAK bool port_boot_button_pressed(void) { return false; #endif } + +// Ports may provide an implementation of this function if it is needed +MP_WEAK void port_gc_collect(void) { +} + +// Allocates an object in the port heap, not the VM heap, and also sets type, for mp_obj_malloc{,_var} macros. +MP_NOINLINE void *mp_obj_port_malloc_helper(size_t num_bytes, const mp_obj_type_t *type) { + mp_obj_base_t *base = (mp_obj_base_t *)port_malloc_zero(num_bytes, false); + base->type = type; + return base; +} + +// Creates a tuple on the port heap, not the VM heap. +// Implementation copied from py/objtuple.c. +mp_obj_t mp_obj_new_port_tuple(size_t n, const mp_obj_t *items) { + if (n == 0) { + return mp_const_empty_tuple; + } + mp_obj_tuple_t *o = mp_obj_port_malloc_var(mp_obj_tuple_t, items, mp_obj_t, n, &mp_type_tuple); + o->len = n; + if (items) { + for (size_t i = 0; i < n; i++) { + o->items[i] = items[i]; + } + } + return MP_OBJ_FROM_PTR(o); +} diff --git a/supervisor/shared/safe_mode.c b/supervisor/shared/safe_mode.c index 5f24618f7f39b..3860de6621f71 100644 --- a/supervisor/shared/safe_mode.c +++ b/supervisor/shared/safe_mode.c @@ -18,6 +18,10 @@ #include "supervisor/shared/translate/translate.h" #include "supervisor/shared/tick.h" +#ifdef __ZEPHYR__ +#include +#endif + #define SAFE_MODE_DATA_GUARD 0xad0000af #define SAFE_MODE_DATA_GUARD_MASK 0xff0000ff @@ -99,10 +103,19 @@ void PLACE_IN_ITCM(safe_mode_on_next_reset)(safe_mode_t reason) { // Don't inline this so it's easy to break on it from GDB. void __attribute__((noinline, )) PLACE_IN_ITCM(reset_into_safe_mode)(safe_mode_t reason) { if (_safe_mode > SAFE_MODE_BROWNOUT && reason > SAFE_MODE_BROWNOUT) { + #ifdef __ZEPHYR__ + printk("Already in safe mode\n"); + printk("Reason: %d\n", reason); + printk("Current safe mode: %d\n", _safe_mode); + while (true) { + k_cpu_idle(); + } + #else while (true) { // This very bad because it means running in safe mode didn't save us. Only ignore brownout // because it may be due to a switch bouncing. } + #endif } safe_mode_on_next_reset(reason); diff --git a/supervisor/shared/safe_mode.h b/supervisor/shared/safe_mode.h index 8f4037cbe20c3..87f65d867ea62 100644 --- a/supervisor/shared/safe_mode.h +++ b/supervisor/shared/safe_mode.h @@ -38,6 +38,6 @@ void set_safe_mode(safe_mode_t safe_mode); safe_mode_t wait_for_safe_mode_reset(void); void safe_mode_on_next_reset(safe_mode_t reason); -void reset_into_safe_mode(safe_mode_t reason) NORETURN; +void reset_into_safe_mode(safe_mode_t reason) MP_NORETURN; void print_safe_mode_message(safe_mode_t reason); diff --git a/supervisor/shared/serial.c b/supervisor/shared/serial.c index 94b429b6ab650..5728a95e08f4c 100644 --- a/supervisor/shared/serial.c +++ b/supervisor/shared/serial.c @@ -219,7 +219,7 @@ void serial_init(void) { } bool serial_connected(void) { - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR if (tud_vendor_connected()) { return true; } @@ -235,11 +235,11 @@ bool serial_connected(void) { } #endif - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC if (usb_cdc_console_enabled() && tud_cdc_connected()) { return true; } - #elif CIRCUITPY_USB_DEVICE + #elif CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE if (tud_cdc_connected()) { return true; } @@ -273,7 +273,7 @@ bool serial_connected(void) { } char serial_read(void) { - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR if (tud_vendor_connected() && tud_vendor_available() > 0) { char tiny_buffer; tud_vendor_read(&tiny_buffer, 1); @@ -327,7 +327,7 @@ char serial_read(void) { return -1; } #endif - #if CIRCUITPY_USB_DEVICE + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE return (char)tud_cdc_read_char(); #endif @@ -338,7 +338,7 @@ uint32_t serial_bytes_available(void) { // There may be multiple serial input channels, so sum the count from all. uint32_t count = 0; - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR if (tud_vendor_connected()) { count += tud_vendor_available(); } @@ -360,7 +360,7 @@ uint32_t serial_bytes_available(void) { count += usb_keyboard_chars_available(); #endif - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC if (usb_cdc_console_enabled()) { count += tud_cdc_available(); } @@ -399,7 +399,7 @@ uint32_t serial_write_substring(const char *text, uint32_t length) { return length_sent; } - #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_VENDOR if (tud_vendor_connected()) { length_sent = tud_vendor_write(text, length); } @@ -423,7 +423,7 @@ uint32_t serial_write_substring(const char *text, uint32_t length) { } #endif - #if CIRCUITPY_USB_DEVICE + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE // Delay the very first write if (tud_cdc_connected() && !_first_write_done) { mp_hal_delay_ms(50); diff --git a/supervisor/shared/settings.c b/supervisor/shared/settings.c new file mode 100644 index 0000000000000..d1b05a5da9db5 --- /dev/null +++ b/supervisor/shared/settings.c @@ -0,0 +1,463 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries +// SPDX-FileCopyrightText: Copyright (c) 2026 Dan Halbert for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include +#include +#include + +#include "py/gc.h" +#include "py/misc.h" +#include "py/mpstate.h" +#include "py/mpprint.h" +#include "py/objstr.h" +#include "py/parsenum.h" +#include "py/runtime.h" +#include "supervisor/filesystem.h" +#include "supervisor/shared/settings.h" + +#define SETTINGS_PATH "/settings.toml" + +#include "extmod/vfs.h" +#include "extmod/vfs_fat.h" + +#if CIRCUITPY_SETTINGS_TOML +typedef FIL file_arg; +static bool open_file(const char *name, file_arg *file_handle) { + #if defined(UNIX) + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_obj_t file_obj = mp_call_function_2( + MP_OBJ_FROM_PTR(&mp_builtin_open_obj), mp_obj_new_str(name, strlen(name)), MP_ROM_QSTR(MP_QSTR_rb)); + mp_arg_validate_type(file_obj, &mp_type_vfs_fat_fileio, MP_QSTR_file); + pyb_file_obj_t *file = MP_OBJ_TO_PTR(file_obj); + *file_handle = file->fp; + nlr_pop(); + return true; + } else { + return false; + } + #else + fs_user_mount_t *fs_mount = filesystem_circuitpy(); + if (fs_mount == NULL) { + return false; + } + FATFS *fatfs = &fs_mount->fatfs; + FRESULT result = f_open(fatfs, file_handle, name, FA_READ); + return result == FR_OK; + #endif +} + +static void close_file(file_arg *file_handle) { + f_close(file_handle); +} + +static bool is_eof(file_arg *file_handle) { + return f_eof(file_handle) || f_error(file_handle); +} + +// Return 0 if there is no next character (EOF). +static uint8_t get_next_byte(FIL *file_handle) { + uint8_t character = 0; + UINT quantity_read; + // If there's an error or quantity_read is 0, character will remain 0. + f_read(file_handle, &character, 1, &quantity_read); + return character; +} + +static void seek_eof(file_arg *file_handle) { + f_lseek(file_handle, f_size(file_handle)); +} + +// For a fixed buffer, record the required size rather than throwing +static void vstr_add_byte_nonstd(vstr_t *vstr, byte b) { + if (!vstr->fixed_buf || vstr->alloc > vstr->len) { + vstr_add_byte(vstr, b); + } else { + vstr->len++; + } +} + +// For a fixed buffer, record the required size rather than throwing +static void vstr_add_char_nonstd(vstr_t *vstr, unichar c) { + size_t ulen = + (c < 0x80) ? 1 : + (c < 0x800) ? 2 : + (c < 0x10000) ? 3 : 4; + if (!vstr->fixed_buf || vstr->alloc > vstr->len + ulen) { + vstr_add_char(vstr, c); + } else { + vstr->len += ulen; + } +} + +static void next_line(file_arg *file_handle) { + uint8_t character; + do { + character = get_next_byte(file_handle); + } while (character != 0 && character != '\n'); +} + +// Discard whitespace, except for newlines, returning the next character after the whitespace. +// Return 0 if there is no next character (EOF). +static uint8_t consume_whitespace(file_arg *file_handle) { + uint8_t character; + do { + character = get_next_byte(file_handle); + } while (character != '\n' && character != 0 && unichar_isspace(character)); + return character; +} + +// Starting at the start of a new line, determines if the key matches the given key. +// +// If result is true, the key matches and file pointer is pointing just after the "=". +// If the result is false, the key does NOT match and the file pointer is +// pointing at the start of the next line, if any +static bool key_matches(file_arg *file_handle, const char *key) { + uint8_t character; + character = consume_whitespace(file_handle); + // [section] isn't implemented, so skip to end of file. + if (character == '[' || character == 0) { + seek_eof(file_handle); + return false; + } + while (*key) { + if (character != *key++) { + // A character didn't match the key, so it's not a match + // If the non-matching char was not the end of the line, + // then consume the rest of the line + if (character != '\n') { + next_line(file_handle); + } + return false; + } + character = get_next_byte(file_handle); + } + // the next character could be whitespace; consume if necessary + if (unichar_isspace(character)) { + character = consume_whitespace(file_handle); + } + // If we're not looking at the "=" then the key didn't match + if (character != '=') { + // A character didn't match the key, so it's not a match + // If the non-matching char was not the end of the line, + // then consume the rest of the line + if (character != '\n') { + next_line(file_handle); + } + return false; + } + return true; +} + +static settings_err_t read_unicode_escape(file_arg *file_handle, int sz, vstr_t *vstr) { + char hex_buf[sz + 1]; + for (int i = 0; i < sz; i++) { + hex_buf[i] = get_next_byte(file_handle); + } + hex_buf[sz] = 0; + char *end; + unsigned long c = strtoul(hex_buf, &end, 16); + if (end != &hex_buf[sz]) { + return SETTINGS_ERR_BAD_VALUE; + } + if (c >= 0x110000) { + return SETTINGS_ERR_UNICODE; + } + vstr_add_char_nonstd(vstr, c); + return SETTINGS_OK; +} + +// Read a quoted string +static settings_err_t read_string_value(file_arg *file_handle, vstr_t *vstr) { + while (true) { + int character = get_next_byte(file_handle); + switch (character) { + case 0: + case '\n': + return SETTINGS_ERR_BAD_VALUE; + + case '"': + character = consume_whitespace(file_handle); + switch (character) { + case '#': + next_line(file_handle); + MP_FALLTHROUGH; + case 0: + case '\n': + return SETTINGS_OK; + default: + return SETTINGS_ERR_BAD_VALUE; + } + + case '\\': + character = get_next_byte(file_handle); + switch (character) { + case 0: + case '\n': + return SETTINGS_ERR_BAD_VALUE; + case 'b': + character = '\b'; + break; + case 'r': + character = '\r'; + break; + case 'n': + character = '\n'; + break; + case 't': + character = '\t'; + break; + case 'v': + character = '\v'; + break; + case 'f': + character = '\f'; + break; + case 'U': + case 'u': { + int sz = (character == 'u') ? 4 : 8; + settings_err_t res; + res = read_unicode_escape(file_handle, sz, vstr); + if (res != SETTINGS_OK) { + return res; + } + continue; + } + // default falls through, other escaped characters + // represent themselves + } + MP_FALLTHROUGH; + default: + vstr_add_byte_nonstd(vstr, character); + } + } +} + +// Read a bare value (non-quoted value) as a string +// Trims leading and trailing spaces/tabs, stops at # comment or newline +static settings_err_t read_bare_value(file_arg *file_handle, vstr_t *vstr, int first_character) { + int character = first_character; + size_t trailing_space_count = 0; + + while (true) { + switch (character) { + case 0: + case '\n': + // Remove trailing spaces/tabs and \r + vstr->len -= trailing_space_count; + return SETTINGS_OK; + case '#': + // Remove trailing spaces/tabs and \r before comment + vstr->len -= trailing_space_count; + next_line(file_handle); + return SETTINGS_OK; + case ' ': + case '\t': + case '\r': + // Track potential trailing whitespace + vstr_add_byte_nonstd(vstr, character); + trailing_space_count++; + break; + default: + // Non-whitespace character resets trailing space count + vstr_add_byte_nonstd(vstr, character); + trailing_space_count = 0; + } + character = get_next_byte(file_handle); + } +} + +static mp_int_t read_value(file_arg *file_handle, vstr_t *vstr, bool *quoted) { + uint8_t character; + character = consume_whitespace(file_handle); + *quoted = (character == '"'); + + if (*quoted) { + return read_string_value(file_handle, vstr); + } else if (character == '\n' || character == 0) { + // Empty value is an error + return SETTINGS_ERR_BAD_VALUE; + } else { + return read_bare_value(file_handle, vstr, character); + } +} + +static settings_err_t settings_get_vstr(const char *key, vstr_t *vstr, bool *quoted) { + file_arg file_handle; + if (!open_file(SETTINGS_PATH, &file_handle)) { + return SETTINGS_ERR_OPEN; + } + + settings_err_t result = SETTINGS_ERR_NOT_FOUND; + while (!is_eof(&file_handle)) { + if (key_matches(&file_handle, key)) { + result = read_value(&file_handle, vstr, quoted); + break; + } + } + close_file(&file_handle); + return result; +} + +static settings_err_t settings_get_buf_terminated(const char *key, char *value, size_t value_len, bool *quoted) { + vstr_t vstr; + vstr_init_fixed_buf(&vstr, value_len, value); + settings_err_t result = settings_get_vstr(key, &vstr, quoted); + + if (result == SETTINGS_OK) { + vstr_add_byte_nonstd(&vstr, 0); + memcpy(value, vstr.buf, MIN(vstr.len, value_len)); + if (vstr.len > value_len) { // this length includes trailing NUL + result = SETTINGS_ERR_LENGTH; + } + } + return result; +} + +static void print_error(const char *key, settings_err_t result) { + switch (result) { + case SETTINGS_OK: + case SETTINGS_ERR_OPEN: + case SETTINGS_ERR_NOT_FOUND: + // These errors need not be printed. + // The code asking for the value is not necessarily expecting to find one. + return; + default: + mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("An error occurred while retrieving '%s':\n"), key); + break; + } + + switch (result) { + case SETTINGS_ERR_UNICODE: + mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("Invalid unicode escape")); + break; + case SETTINGS_ERR_BAD_VALUE: + mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("Invalid format")); + break; + default: + mp_cprintf(&mp_plat_print, MP_ERROR_TEXT("Internal error")); + break; + } + mp_printf(&mp_plat_print, "\n"); +} + + +static settings_err_t get_str(const char *key, char *value, size_t value_len) { + bool quoted; + settings_err_t result = settings_get_buf_terminated(key, value, value_len, "ed); + if (result == SETTINGS_OK && !quoted) { + result = SETTINGS_ERR_BAD_VALUE; + } + return result; +} + +settings_err_t settings_get_str(const char *key, char *value, size_t value_len) { + settings_err_t result = get_str(key, value, value_len); + print_error(key, result); + return result; +} + +static settings_err_t get_int(const char *key, mp_int_t *value) { + char buf[16]; + bool quoted; + settings_err_t result = settings_get_buf_terminated(key, buf, sizeof(buf), "ed); + if (result != SETTINGS_OK) { + return result; + } + if (quoted) { + return SETTINGS_ERR_BAD_VALUE; + } + char *end; + long num = strtol(buf, &end, 0); + while (unichar_isspace(*end)) { + end++; + } + if (end == buf || *end) { // If the whole buffer was not consumed it's an error + return SETTINGS_ERR_BAD_VALUE; + } + *value = (mp_int_t)num; + return SETTINGS_OK; +} + +settings_err_t settings_get_int(const char *key, mp_int_t *value) { + settings_err_t result = get_int(key, value); + print_error(key, result); + return result; +} + +static settings_err_t get_bool(const char *key, bool *value) { + char buf[16]; + bool quoted; + settings_err_t result = settings_get_buf_terminated(key, buf, sizeof(buf), "ed); + if (result != SETTINGS_OK) { + return result; + } + if (quoted) { + return SETTINGS_ERR_BAD_VALUE; + } + + // Check for "true" or "false" (case-sensitive) + if (strcmp(buf, "true") == 0) { + *value = true; + return SETTINGS_OK; + } else if (strcmp(buf, "false") == 0) { + *value = false; + return SETTINGS_OK; + } + + // Not a valid boolean value + return SETTINGS_ERR_BAD_VALUE; +} + +settings_err_t settings_get_bool(const char *key, bool *value) { + settings_err_t result = get_bool(key, value); + print_error(key, result); + return result; +} + +// Get the raw value as a vstr, whether quoted or bare. Value may be an invalid TOML value. +settings_err_t settings_get_raw_vstr(const char *key, vstr_t *vstr) { + bool quoted; + return settings_get_vstr(key, vstr, "ed); +} + +settings_err_t settings_get_obj(const char *key, mp_obj_t *value) { + vstr_t vstr; + vstr_init(&vstr, 32); + bool quoted; + + settings_err_t result = settings_get_vstr(key, &vstr, "ed); + if (result != SETTINGS_OK) { + return result; + } + + if (quoted) { + // Successfully parsed a quoted string + *value = mp_obj_new_str_from_vstr(&vstr); + return SETTINGS_OK; + } + + // Not a quoted string, try boolean + bool bool_val; + result = get_bool(key, &bool_val); + if (result == SETTINGS_OK) { + *value = mp_obj_new_bool(bool_val); + return SETTINGS_OK; + } + + // Not a boolean, try integer + mp_int_t int_val; + result = get_int(key, &int_val); + if (result == SETTINGS_OK) { + *value = mp_obj_new_int(int_val); + return SETTINGS_OK; + } + + return SETTINGS_ERR_BAD_VALUE; +} + +#endif diff --git a/supervisor/shared/settings.h b/supervisor/shared/settings.h new file mode 100644 index 0000000000000..eafa9962e9527 --- /dev/null +++ b/supervisor/shared/settings.h @@ -0,0 +1,53 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2022 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#pragma once + +#include "py/obj.h" + +typedef enum { + SETTINGS_OK = 0, + SETTINGS_ERR_OPEN, + SETTINGS_ERR_UNICODE, + SETTINGS_ERR_LENGTH, + SETTINGS_ERR_NOT_FOUND, + SETTINGS_ERR_BAD_VALUE, +} settings_err_t; + +// Read a string value from the settings file. +// If it fits, the return value is 0-terminated. The passed-in buffer +// may be modified even if an error is returned. Allocation free. +// An error that is not 'open' or 'not found' is printed on the repl. +// Returns an error if the value is not a quoted string. +settings_err_t settings_get_str(const char *key, char *value, size_t value_len); + +// Read an integer value from the settings file. +// Returns SETTINGS_OK and sets value to the read value. Returns +// SETTINGS_ERR_... if the value was not numeric. allocation-free. +// If any error code is returned, value is guaranteed not modified +// An error that is not 'open' or 'not found' is printed on the repl. +settings_err_t settings_get_int(const char *key, mp_int_t *value); + +// Read a boolean value from the settings file. +// Returns SETTINGS_OK and sets value to the read value. Returns +// SETTINGS_ERR_... if the value was not a boolean (true or false). allocation-free. +// If any error code is returned, value is guaranteed not modified +// An error that is not 'open' or 'not found' is printed on the repl. +settings_err_t settings_get_bool(const char *key, bool *value); + +// Read a value from the settings file and return as parsed Python object. +// Returns SETTINGS_OK and sets value to a parsed Python object from the RHS value: +// - Quoted strings return as str +// - Bare "true" or "false" return as bool +// - Valid integers return as int +// Returns SETTINGS_ERR_... if the value is not parseable as one of these types. +// An error that is not 'open' or 'not found' is printed on the repl. +settings_err_t settings_get_obj(const char *key, mp_obj_t *value); + +// Read the raw value as a string, whether quoted or bare. +// This is used by os.getenv() to always return strings. +// Does not print errors. +settings_err_t settings_get_raw_vstr(const char *key, vstr_t *vstr); diff --git a/supervisor/shared/status_leds.c b/supervisor/shared/status_leds.c index ffe8b46b20b7c..ca29fd79212cf 100644 --- a/supervisor/shared/status_leds.c +++ b/supervisor/shared/status_leds.c @@ -133,9 +133,9 @@ void status_led_init(void) { memset(status_apa102_color + 4, 0xff, APA102_BUFFER_LENGTH - 4); #if CIRCUITPY_BITBANG_APA102 shared_module_bitbangio_spi_construct(&status_apa102, - MICROPY_HW_APA102_SCK, - MICROPY_HW_APA102_MOSI, - NULL); + MP_OBJ_FROM_PTR(MICROPY_HW_APA102_SCK), + MP_OBJ_FROM_PTR(MICROPY_HW_APA102_MOSI), + mp_const_none); #else if (!common_hal_busio_spi_deinited(&status_apa102)) { common_hal_busio_spi_deinit(&status_apa102); diff --git a/supervisor/shared/usb.c b/supervisor/shared/usb.c new file mode 100644 index 0000000000000..ff0dd1f84672f --- /dev/null +++ b/supervisor/shared/usb.c @@ -0,0 +1,65 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Scott Shawcroft for Adafruit Industries +// +// SPDX-License-Identifier: MIT + +#include "supervisor/usb.h" + +#if CIRCUITPY_STORAGE +#include "shared-module/storage/__init__.h" +#endif + +#if CIRCUITPY_USB_DEVICE +#include "shared-bindings/supervisor/__init__.h" + +#if CIRCUITPY_USB_CDC +#include "shared-module/usb_cdc/__init__.h" +#endif + +#if CIRCUITPY_USB_HID +#include "shared-module/usb_hid/__init__.h" +#endif + +#if CIRCUITPY_USB_MIDI +#include "shared-module/usb_midi/__init__.h" +#endif + +#if CIRCUITPY_USB_VIDEO +#include "shared-module/usb_video/__init__.h" +#endif +#endif + +// Set up USB defaults before any USB changes are made in boot.py +void usb_set_defaults(void) { + #if CIRCUITPY_USB_DEVICE + #if CIRCUITPY_STORAGE && CIRCUITPY_USB_MSC + storage_usb_set_defaults(); + #endif + + #if CIRCUITPY_USB_CDC + usb_cdc_set_defaults(); + #endif + + #if CIRCUITPY_USB_HID + usb_hid_set_defaults(); + #endif + + #if CIRCUITPY_USB_MIDI + usb_midi_set_defaults(); + #endif + #endif +}; + +// Call this when ready to run code.py or a REPL, and a VM has been started. +void usb_setup_with_vm(void) { + #if CIRCUITPY_USB_DEVICE + #if CIRCUITPY_USB_HID + usb_hid_setup_devices(); + #endif + + #if CIRCUITPY_USB_MIDI + usb_midi_setup_ports(); + #endif + #endif +} diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h index ac2fd9f4f258c..f2d800b99127e 100644 --- a/supervisor/shared/usb/tusb_config.h +++ b/supervisor/shared/usb/tusb_config.h @@ -79,18 +79,16 @@ extern "C" { #if CIRCUITPY_USB_DEVICE -#if CIRCUITPY_USB_DEVICE_INSTANCE == 0 -#if USB_HIGHSPEED -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) +#if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 +#define _DEVICE_SPEED OPT_MODE_HIGH_SPEED #else -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE) +#define _DEVICE_SPEED 0 #endif + +#if CIRCUITPY_USB_DEVICE_INSTANCE == 0 +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | _DEVICE_SPEED) #elif CIRCUITPY_USB_DEVICE_INSTANCE == 1 -#if USB_HIGHSPEED -#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | OPT_MODE_HIGH_SPEED) -#else -#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE) -#endif +#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | _DEVICE_SPEED) #endif // Use DMA with the USB peripheral. @@ -157,18 +155,16 @@ extern "C" { #define CFG_TUH_RPI_PIO_USB 1 #endif -#if CIRCUITPY_USB_HOST_INSTANCE == 0 -#if USB_HIGHSPEED -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED) +#if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 +#define _HOST_SPEED OPT_MODE_HIGH_SPEED #else -#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST) +#define _HOST_SPEED 0 #endif + +#if CIRCUITPY_USB_HOST_INSTANCE == 0 +#define CFG_TUSB_RHPORT0_MODE (OPT_MODE_HOST | _HOST_SPEED) #elif CIRCUITPY_USB_HOST_INSTANCE == 1 -#if USB_HIGHSPEED -#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST | OPT_MODE_HIGH_SPEED) -#else -#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST) -#endif +#define CFG_TUSB_RHPORT1_MODE (OPT_MODE_HOST | _HOST_SPEED) #endif // Size of buffer to hold descriptors and other data used for enumeration diff --git a/supervisor/shared/usb/usb.c b/supervisor/shared/usb/usb.c index e67c15d022cc9..5061fee00633e 100644 --- a/supervisor/shared/usb/usb.c +++ b/supervisor/shared/usb/usb.c @@ -7,6 +7,7 @@ #include "py/objstr.h" #include "supervisor/background_callback.h" #include "supervisor/linker.h" +#include "supervisor/port.h" #include "supervisor/shared/tick.h" #include "supervisor/usb.h" #include "shared/readline/readline.h" @@ -87,6 +88,14 @@ bool usb_enabled(void) { return tusb_inited(); } +bool usb_connected(void) { + #if CIRCUITPY_TINYUSB && CIRCUITPY_USB_DEVICE + return tud_ready(); + #else + return false; + #endif +} + MP_WEAK void post_usb_init(void) { } @@ -144,40 +153,6 @@ void usb_init(void) { #endif } -// Set up USB defaults before any USB changes are made in boot.py -void usb_set_defaults(void) { - #if CIRCUITPY_USB_DEVICE - #if CIRCUITPY_STORAGE && CIRCUITPY_USB_MSC - storage_usb_set_defaults(); - #endif - - #if CIRCUITPY_USB_CDC - usb_cdc_set_defaults(); - #endif - - #if CIRCUITPY_USB_HID - usb_hid_set_defaults(); - #endif - - #if CIRCUITPY_USB_MIDI - usb_midi_set_defaults(); - #endif - #endif -}; - -// Call this when ready to run code.py or a REPL, and a VM has been started. -void usb_setup_with_vm(void) { - #if CIRCUITPY_USB_DEVICE - #if CIRCUITPY_USB_HID - usb_hid_setup_devices(); - #endif - - #if CIRCUITPY_USB_MIDI - usb_midi_setup_ports(); - #endif - #endif -} - void usb_background(void) { if (usb_enabled()) { #if CFG_TUSB_OS == OPT_OS_NONE || CFG_TUSB_OS == OPT_OS_PICO @@ -186,9 +161,8 @@ void usb_background(void) { tuh_task(); #endif #elif CFG_TUSB_OS == OPT_OS_FREERTOS - // Yield to FreeRTOS in case TinyUSB runs in a separate task. Don't use - // port_yield() because it has a longer delay. - vTaskDelay(0); + // TinyUSB may run in a separate task, at the same priority as CircuitPython. + port_task_yield(); #endif // No need to flush if there's no REPL. #if CIRCUITPY_USB_DEVICE && CIRCUITPY_USB_CDC diff --git a/supervisor/shared/usb/usb_device.c b/supervisor/shared/usb/usb_device.c index ec08b8bf4d7e2..16ae137eddefb 100644 --- a/supervisor/shared/usb/usb_device.c +++ b/supervisor/shared/usb/usb_device.c @@ -166,6 +166,9 @@ bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_requ * @param wanted_char The wanted char (set previously) */ void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { + // CircuitPython's VM is run in a separate FreeRTOS task from TinyUSB on ESP. + // So, we must notify the other task when a CTRL-C is received. + port_wake_main_task(); // Workaround for using shared/runtime/interrupt_char.c // Compare mp_interrupt_char with wanted_char and ignore if not matched if (mp_interrupt_char == wanted_char) { @@ -177,7 +180,14 @@ void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char) { void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms) { if (usb_cdc_console_enabled() && mp_interrupt_char != -1 && itf == 0 && duration_ms > 0) { mp_sched_keyboard_interrupt(); + port_wake_main_task(); } } +void tud_cdc_rx_cb(uint8_t itf) { + (void)itf; + // Workaround for "press any key to enter REPL" response being delayed on espressif. + // Wake main task when any key is pressed. + port_wake_main_task(); +} #endif diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c index 0b02faa5c18cb..5ea457ef328cc 100644 --- a/supervisor/shared/usb/usb_msc_flash.c +++ b/supervisor/shared/usb/usb_msc_flash.c @@ -5,7 +5,6 @@ // SPDX-License-Identifier: MIT #include "tusb.h" -// // #include "supervisor/flash.h" // For updating fatfs's cache #include "extmod/vfs.h" @@ -14,6 +13,7 @@ #include "lib/oofatfs/ff.h" #include "py/gc.h" #include "py/mpstate.h" +#include "py/runtime.h" #include "shared-module/storage/__init__.h" #include "supervisor/filesystem.h" @@ -28,7 +28,7 @@ #define SAVES_COUNT 0 #endif -#if CIRCUITPY_SDCARDIO +#if CIRCUITPY_SDCARD_USB #include "shared-module/sdcardio/__init__.h" #define SDCARD_COUNT 1 @@ -45,6 +45,11 @@ static bool ejected[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = true}; static bool eject_once[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; static bool locked[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; +// Set to true if a write was in a file data or metadata area, +// as opposed to in the filesystem metadata area (e.g., dirty bit). +// Used to determine if an auto-reload is warranted. +static bool content_write[LUN_COUNT] = { [0 ... (LUN_COUNT - 1)] = false}; + #include "tusb.h" static const uint8_t usb_msc_descriptor_template[] = { @@ -67,7 +72,7 @@ static const uint8_t usb_msc_descriptor_template[] = { 0xFF, // 11 bEndpointAddress (IN/D2H) [SET AT RUNTIME: 0x80 | number] #define MSC_IN_ENDPOINT_INDEX (11) 0x02, // 12 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 13,14 wMaxPacketSize 512 #else 0x40, 0x00, // 13,14 wMaxPacketSize 64 @@ -80,7 +85,7 @@ static const uint8_t usb_msc_descriptor_template[] = { 0xFF, // 18 bEndpointAddress (OUT/H2D) [SET AT RUNTIME] #define MSC_OUT_ENDPOINT_INDEX (18) 0x02, // 19 bmAttributes (Bulk) - #if USB_HIGHSPEED + #if CIRCUITPY_USB_DEVICE_HIGH_SPEED == 1 0x00, 0x02, // 20,21 wMaxPacketSize 512 #else 0x40, 0x00, // 20,21 wMaxPacketSize 64 @@ -132,7 +137,8 @@ static fs_user_mount_t *get_vfs(int lun) { if (lun == SAVES_LUN) { const char *path_under_mount; fs_user_mount_t *saves = filesystem_for_path("/saves", &path_under_mount); - if (saves != root && (saves->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0 && gc_nbytes(saves) == 0) { + if (saves != root && + (saves->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0 && !gc_ptr_on_heap(saves)) { return saves; } } @@ -141,7 +147,15 @@ static fs_user_mount_t *get_vfs(int lun) { if (lun == SDCARD_LUN) { const char *path_under_mount; fs_user_mount_t *sdcard = filesystem_for_path("/sd", &path_under_mount); - if (sdcard != root && (sdcard->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0) { + // If sdcard ("/sd") is on the root filesystem, nothing has been mounted there, so don't + // return it as a separate filesystem. + // If the SD card was automounted at startup, then it persists across VMs and its fs_user_mount_t is + // not on the heap. + // If the SD card filesystem was mounted by the user using heap objects, + // it should not be used when the VM has stopped running. + if ((sdcard != root) && + ((sdcard->blockdev.flags & MP_BLOCKDEV_FLAG_NATIVE) != 0) && + (vm_is_running() || !gc_ptr_on_heap(sdcard))) { return sdcard; } else { // Clear any ejected state so that a re-insert causes it to reappear. @@ -243,7 +257,7 @@ bool tud_msc_is_writable_cb(uint8_t lun) { if (vfs == NULL) { return false; } - if (vfs->blockdev.writeblocks[0] == MP_OBJ_NULL || !filesystem_is_writable_by_usb(vfs)) { + if (!filesystem_is_writable_by_usb(vfs)) { return false; } // Lock the blockdev once we say we're writable. @@ -290,16 +304,19 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t * if (vfs == NULL) { return -1; } + disk_write(vfs, buffer, lba, block_count); // Since by getting here we assume the mount is read-only to - // MicroPython let's update the cached FatFs sector if it's the one + // CircuitPython let's update the cached FatFs sector if it's the one // we just wrote. + if #if FF_MAX_SS != FF_MIN_SS - if (vfs->fatfs.ssize == MSC_FLASH_BLOCK_SIZE) { + (vfs->fatfs.ssize == MSC_FLASH_BLOCK_SIZE) #else // The compiler can optimize this away. - if (FF_MAX_SS == FILESYSTEM_BLOCK_SIZE) { - #endif + (FF_MAX_SS == FILESYSTEM_BLOCK_SIZE) + #endif + { if (lba == vfs->fatfs.winsect && lba > 0) { memcpy(vfs->fatfs.win, buffer + MSC_FLASH_BLOCK_SIZE * (vfs->fatfs.winsect - lba), @@ -307,17 +324,30 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t * } } + // A write to an lba below fatbase is in the filesystem metadata (BPB) area or the "Reserved Region", + // and is probably setting or clearing the dirty bit. This should not trigger auto-reload. + // All other writes will trigger auto-reload. + if (lba >= vfs->fatfs.fatbase) { + content_write[lun] = true; + } + return block_count * MSC_FLASH_BLOCK_SIZE; } // Callback invoked when WRITE10 command is completed (status received and accepted by host). // used to flush any pending cache. void tud_msc_write10_complete_cb(uint8_t lun) { - (void)lun; - - // This write is complete; initiate an autoreload. autoreload_resume(AUTORELOAD_SUSPEND_USB); - autoreload_trigger(); + + // This write is complete; initiate an autoreload if this was a file data or metadata write, + // not just a dirty-bit write. + if (content_write[lun] && + // Fast path: lun == 0 is CIRCUITPY, which can always trigger auto-reload if enabled. + // Don't autoreload if this lun was mounted by the user: that will cause a VM stop and an unmount. + (lun == 0 || !gc_ptr_on_heap(get_vfs(lun)))) { + autoreload_trigger(); + content_write[lun] = false; + } } // Invoked when received SCSI_CMD_INQUIRY @@ -337,7 +367,7 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun) { return false; } - #if CIRCUITPY_SDCARDIO + #ifdef SDCARD_LUN if (lun == SDCARD_LUN) { automount_sd_card(); } diff --git a/supervisor/shared/web_workflow/web_workflow.c b/supervisor/shared/web_workflow/web_workflow.c index eea11bd5fb4af..a14f79a5bb47f 100644 --- a/supervisor/shared/web_workflow/web_workflow.c +++ b/supervisor/shared/web_workflow/web_workflow.c @@ -8,6 +8,8 @@ #define _GNU_SOURCE #include +#include +#include #include #include "extmod/vfs.h" @@ -42,12 +44,16 @@ #include "shared-bindings/socketpool/Socket.h" #include "shared-bindings/socketpool/SocketPool.h" +#if CIRCUITPY_HOSTNETWORK +#include "bindings/hostnetwork/__init__.h" +#endif + #if CIRCUITPY_WIFI #include "shared-bindings/wifi/__init__.h" #endif -#if CIRCUITPY_OS_GETENV -#include "shared-module/os/__init__.h" +#if CIRCUITPY_SETTINGS_TOML +#include "supervisor/shared/settings.h" #endif enum request_state { @@ -84,12 +90,18 @@ typedef struct { char websocket_key[24 + 1]; } _request; +#if CIRCUITPY_WIFI static wifi_radio_error_t _wifi_status = WIFI_RADIO_ERROR_NONE; +#endif -#if CIRCUITPY_STATUS_BAR +#if CIRCUITPY_STATUS_BAR && (CIRCUITPY_WIFI || CIRCUITPY_HOSTNETWORK) // Store various last states to compute if status bar needs an update. static bool _last_enabled = false; static uint32_t _last_ip = 0; +static mp_int_t _last_web_api_port = 80; +#endif + +#if CIRCUITPY_STATUS_BAR && CIRCUITPY_WIFI static wifi_radio_error_t _last_wifi_status = WIFI_RADIO_ERROR_NONE; #endif @@ -171,11 +183,7 @@ static bool _base64_in_place(char *buf, size_t in_len, size_t out_len) { return true; } -static void _update_encoded_ip(void) { - uint32_t ipv4_address = 0; - if (common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) { - ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj); - } +static void _update_encoded_ip(uint32_t ipv4_address) { if (_encoded_ip != ipv4_address) { uint8_t *octets = (uint8_t *)&ipv4_address; snprintf(_our_ip_encoded, sizeof(_our_ip_encoded), "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]); @@ -183,75 +191,127 @@ static void _update_encoded_ip(void) { } } +static bool _get_web_workflow_ip(uint32_t *ipv4_address) { + *ipv4_address = 0; + #if CIRCUITPY_WIFI + if (!common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj)) { + return false; + } + *ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj); + return true; + #elif CIRCUITPY_HOSTNETWORK + // hostnetwork uses the host network namespace and is reachable via localhost. + *ipv4_address = 0x0100007f; // 127.0.0.1 + return true; + #else + return false; + #endif +} + +#if CIRCUITPY_STATUS_BAR +static void _print_web_workflow_endpoint(void) { + mp_printf(&mp_plat_print, "%s", _our_ip_encoded); + if (web_api_port != 80) { + mp_printf(&mp_plat_print, ":%d", web_api_port); + } +} +#endif + mdns_server_obj_t *supervisor_web_workflow_mdns(mp_obj_t network_interface) { - #if CIRCUITPY_MDNS + #if CIRCUITPY_MDNS && CIRCUITPY_WIFI if (network_interface == &common_hal_wifi_radio_obj && mdns.base.type == &mdns_server_type) { return &mdns; } #endif + (void)network_interface; return NULL; } #if CIRCUITPY_STATUS_BAR bool supervisor_web_workflow_status_dirty(void) { - return common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj) != _last_enabled || - _encoded_ip != _last_ip || - _last_wifi_status != _wifi_status; + #if CIRCUITPY_WIFI || CIRCUITPY_HOSTNETWORK + uint32_t ipv4_address = 0; + bool enabled = _get_web_workflow_ip(&ipv4_address); + if (enabled != _last_enabled || ipv4_address != _last_ip || web_api_port != _last_web_api_port) { + return true; + } + #if CIRCUITPY_WIFI + if (_last_wifi_status != _wifi_status) { + return true; + } + #endif + return false; + #else + return false; + #endif } #endif #if CIRCUITPY_STATUS_BAR void supervisor_web_workflow_status(void) { - _last_enabled = common_hal_wifi_radio_get_enabled(&common_hal_wifi_radio_obj); + #if CIRCUITPY_WIFI || CIRCUITPY_HOSTNETWORK + uint32_t ipv4_address = 0; + _last_enabled = _get_web_workflow_ip(&ipv4_address); + _last_web_api_port = web_api_port; + if (_last_enabled) { - uint32_t ipv4_address = wifi_radio_get_ipv4_address(&common_hal_wifi_radio_obj); + _update_encoded_ip(ipv4_address); + _last_ip = _encoded_ip; + if (ipv4_address != 0) { - _update_encoded_ip(); - _last_ip = _encoded_ip; - mp_printf(&mp_plat_print, "%s", _our_ip_encoded); - if (web_api_port != 80) { - mp_printf(&mp_plat_print, ":%d", web_api_port); - } + _print_web_workflow_endpoint(); // TODO: Use these unicode to show signal strength: ▂▄▆█ return; } - serial_write_compressed(MP_ERROR_TEXT("Wi-Fi: ")); - _last_wifi_status = _wifi_status; - if (_wifi_status == WIFI_RADIO_ERROR_AUTH_EXPIRE || - _wifi_status == WIFI_RADIO_ERROR_AUTH_FAIL) { - serial_write_compressed(MP_ERROR_TEXT("Authentication failure")); - } else if (_wifi_status != WIFI_RADIO_ERROR_NONE) { - mp_printf(&mp_plat_print, "%d", _wifi_status); - } else if (ipv4_address == 0) { - _last_ip = 0; - serial_write_compressed(MP_ERROR_TEXT("No IP")); - } else { - } - } else { - // Keep Wi-Fi print separate so its data can be matched with the one above. - serial_write_compressed(MP_ERROR_TEXT("Wi-Fi: ")); + } + + #if CIRCUITPY_WIFI + serial_write_compressed(MP_ERROR_TEXT("Wi-Fi: ")); + _last_wifi_status = _wifi_status; + if (!_last_enabled) { serial_write_compressed(MP_ERROR_TEXT("off")); + } else if (_wifi_status == WIFI_RADIO_ERROR_AUTH_EXPIRE || + _wifi_status == WIFI_RADIO_ERROR_AUTH_FAIL) { + serial_write_compressed(MP_ERROR_TEXT("Authentication failure")); + } else if (_wifi_status != WIFI_RADIO_ERROR_NONE) { + mp_printf(&mp_plat_print, "%d", _wifi_status); + } else { + _last_ip = 0; + serial_write_compressed(MP_ERROR_TEXT("No IP")); } + #endif + #else + return; + #endif } #endif bool supervisor_start_web_workflow(void) { - #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_WIFI && CIRCUITPY_OS_GETENV + #if CIRCUITPY_WEB_WORKFLOW && CIRCUITPY_SETTINGS_TOML && (CIRCUITPY_WIFI || CIRCUITPY_HOSTNETWORK) + #if CIRCUITPY_WIFI + mp_obj_t socketpool_radio = MP_OBJ_FROM_PTR(&common_hal_wifi_radio_obj); + #else + mp_obj_t socketpool_radio = MP_OBJ_FROM_PTR(&common_hal_hostnetwork_obj); + #endif + + settings_err_t result; + + #if CIRCUITPY_WIFI char ssid[33]; char password[64]; - os_getenv_err_t result = common_hal_os_getenv_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid)); - if (result != GETENV_OK || strlen(ssid) < 1) { + result = settings_get_str("CIRCUITPY_WIFI_SSID", ssid, sizeof(ssid)); + if (result != SETTINGS_OK || strlen(ssid) < 1) { return false; } - result = common_hal_os_getenv_str("CIRCUITPY_WIFI_PASSWORD", password, sizeof(password)); - if (result == GETENV_ERR_NOT_FOUND) { + result = settings_get_str("CIRCUITPY_WIFI_PASSWORD", password, sizeof(password)); + if (result == SETTINGS_ERR_NOT_FOUND) { // if password is unspecified, assume an open network password[0] = '\0'; - } else if (result != GETENV_OK) { + } else if (result != SETTINGS_OK) { return false; } @@ -275,6 +335,7 @@ bool supervisor_start_web_workflow(void) { common_hal_wifi_radio_set_enabled(&common_hal_wifi_radio_obj, false); return false; } + #endif // Skip starting the workflow if we're not starting from power on or reset. const mcu_reset_reason_t reset_reason = common_hal_mcu_processor_get_reset_reason(); @@ -289,17 +350,17 @@ bool supervisor_start_web_workflow(void) { bool initialized = pool.base.type == &socketpool_socketpool_type; if (!initialized) { - result = common_hal_os_getenv_str("CIRCUITPY_WEB_INSTANCE_NAME", web_instance_name, sizeof(web_instance_name)); - if (result != GETENV_OK || web_instance_name[0] == '\0') { + result = settings_get_str("CIRCUITPY_WEB_INSTANCE_NAME", web_instance_name, sizeof(web_instance_name)); + if (result != SETTINGS_OK || web_instance_name[0] == '\0') { strcpy(web_instance_name, MICROPY_HW_BOARD_NAME); } // (leaves new_port unchanged on any failure) - (void)common_hal_os_getenv_int("CIRCUITPY_WEB_API_PORT", &web_api_port); + (void)settings_get_int("CIRCUITPY_WEB_API_PORT", &web_api_port); const size_t api_password_len = sizeof(_api_password) - 1; - result = common_hal_os_getenv_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len); - if (result == GETENV_OK) { + result = settings_get_str("CIRCUITPY_WEB_API_PASSWORD", _api_password + 1, api_password_len); + if (result == SETTINGS_OK) { _api_password[0] = ':'; _base64_in_place(_api_password, strlen(_api_password), sizeof(_api_password) - 1); } else { // Skip starting web-workflow when no password is passed. @@ -307,7 +368,7 @@ bool supervisor_start_web_workflow(void) { } pool.base.type = &socketpool_socketpool_type; - common_hal_socketpool_socketpool_construct(&pool, &common_hal_wifi_radio_obj); + common_hal_socketpool_socketpool_construct(&pool, socketpool_radio); socketpool_socket_reset(&listening); socketpool_socket_reset(&active); @@ -373,7 +434,7 @@ void web_workflow_send_raw(socketpool_socket_obj_t *socket, bool flush, const ui total_sent += sent; if (total_sent < len) { // Yield so that network code can run. - port_yield(); + port_task_sleep_ms(4); } } } @@ -847,7 +908,9 @@ static void _reply_with_version_json(socketpool_socket_obj_t *socket, _request * instance_name = common_hal_mdns_server_get_instance_name(&mdns); } #endif - _update_encoded_ip(); + uint32_t ipv4_address = 0; + (void)_get_web_workflow_ip(&ipv4_address); + _update_encoded_ip(ipv4_address); // Note: this leverages the fact that C concats consecutive string literals together. mp_printf(&_socket_print, "{\"web_api_version\": 4, " @@ -1085,6 +1148,10 @@ static void _reply_static(socketpool_socket_obj_t *socket, _request *request, co "Content-Encoding: gzip\r\n", "Content-Length: ", encoded_len, "\r\n", "Content-Type: ", content_type, "\r\n", + #if CIRCUITPY_DEBUG == 0 + "Cache-Control: max-age=31536000\r\n", // Cache for a year. + "Vary: Accept\r\n", + #endif "\r\n", NULL); web_workflow_send_raw(socket, true, response, response_len); } diff --git a/supervisor/shared/web_workflow/web_workflow.h b/supervisor/shared/web_workflow/web_workflow.h index 439964dd333ac..9ec15a14ddc48 100644 --- a/supervisor/shared/web_workflow/web_workflow.h +++ b/supervisor/shared/web_workflow/web_workflow.h @@ -8,7 +8,12 @@ #include +#if CIRCUITPY_MDNS #include "shared-bindings/mdns/Server.h" +#else +typedef struct mdns_server_obj mdns_server_obj_t; +#endif + #include "shared-bindings/socketpool/Socket.h" // This background function should be called repeatedly. It cannot be done based diff --git a/supervisor/shared/workflow.c b/supervisor/shared/workflow.c index df2055580fc5d..7370e34e86007 100644 --- a/supervisor/shared/workflow.c +++ b/supervisor/shared/workflow.c @@ -43,10 +43,12 @@ void supervisor_workflow_reset(void) { #if CIRCUITPY_WEB_WORKFLOW bool result = supervisor_start_web_workflow(); - if (workflow_background_cb.fun) { - if (result) { - supervisor_workflow_request_background(); + if (result) { + if (!workflow_background_cb.fun) { + memset(&workflow_background_cb, 0, sizeof(workflow_background_cb)); + workflow_background_cb.fun = supervisor_web_workflow_background; } + supervisor_workflow_request_background(); } #endif } @@ -89,7 +91,7 @@ bool supervisor_workflow_active(void) { void supervisor_workflow_start(void) { // Start USB after giving boot.py a chance to tweak behavior. - #if CIRCUITPY_TINYUSB + #if CIRCUITPY_USB_DEVICE // Setup USB connection after heap is available. // It needs the heap to build descriptors. usb_init(); @@ -105,9 +107,11 @@ void supervisor_workflow_start(void) { #if CIRCUITPY_WEB_WORKFLOW if (supervisor_start_web_workflow()) { - // Enable background callbacks if web_workflow startup successful + // Enable background callbacks if web_workflow startup successful. memset(&workflow_background_cb, 0, sizeof(workflow_background_cb)); workflow_background_cb.fun = supervisor_web_workflow_background; + // Kick the first background run now that the callback is installed. + supervisor_workflow_request_background(); } #endif diff --git a/supervisor/supervisor.mk b/supervisor/supervisor.mk index 0ecc6ea3acfe6..bce47353f578f 100644 --- a/supervisor/supervisor.mk +++ b/supervisor/supervisor.mk @@ -1,6 +1,5 @@ SRC_SUPERVISOR = \ main.c \ - lib/tlsf/tlsf.c \ supervisor/port.c \ supervisor/shared/background_callback.c \ supervisor/shared/board.c \ @@ -20,6 +19,14 @@ SRC_SUPERVISOR = \ supervisor/shared/translate/translate.c \ supervisor/shared/workflow.c \ +ifeq ($(CIRCUITPY_SETTINGS_TOML),1) +SRC_SUPERVISOR += supervisor/shared/settings.c +endif + +ifeq ($(CIRCUITPY_LIB_TLSF),1) +SRC_SUPERVISOR += lib/tlsf/tlsf.c +endif + # For tlsf CFLAGS += -D_DEBUG=0 @@ -113,6 +120,7 @@ ifeq ($(CIRCUITPY_TINYUSB),1) lib/tinyusb/src/common/tusb_fifo.c \ lib/tinyusb/src/tusb.c \ supervisor/usb.c \ + supervisor/shared/usb.c \ supervisor/shared/usb/usb.c \ ifeq ($(CIRCUITPY_USB_DEVICE),1) @@ -256,9 +264,6 @@ ifeq ($(CIRCUITPY_USB_CDC),1) CFLAGS += -DCFG_TUD_CDC=2 endif -USB_HIGHSPEED ?= 0 -CFLAGS += -DUSB_HIGHSPEED=$(USB_HIGHSPEED) - $(BUILD)/supervisor/shared/translate/translate.o: $(HEADER_BUILD)/qstrdefs.generated.h $(HEADER_BUILD)/compressed_translations.generated.h CIRCUITPY_DISPLAY_FONT ?= "../../tools/fonts/ter-u12n.bdf" diff --git a/supervisor/usb.h b/supervisor/usb.h index 10f033a3616b8..d6e4236c9b48d 100644 --- a/supervisor/usb.h +++ b/supervisor/usb.h @@ -48,6 +48,7 @@ typedef struct { bool usb_enabled(void); void usb_add_interface_string(uint8_t interface_string_index, const char str[]); bool usb_build_descriptors(const usb_identification_t *identification); +bool usb_connected(void); void usb_disconnect(void); void usb_init(void); void usb_set_defaults(void); diff --git a/tests/README.md b/tests/README.md index 21e14eee5e128..534e7e0a05972 100644 --- a/tests/README.md +++ b/tests/README.md @@ -1,6 +1,7 @@ # MicroPython Test Suite -This directory contains tests for most parts of MicroPython. +This directory contains tests for most parts of MicroPython. To run it you will need +CPython 3.8.2 or newer, which is used to validate MicroPython's behaviour. To run all stable tests, run the "run-tests.py" script in this directory. By default that will run the test suite against the unix port of MicroPython. @@ -67,16 +68,14 @@ for a full list of command line options. ### Benchmarking a target -To run tests on a firmware target using `pyboard.py`, run the command line like +To run tests on a firmware target using a serial port, run the command line like this: ``` -./run-perfbench.py -p -d /dev/ttyACM0 168 100 +./run-perfbench.py -t /dev/ttyACM0 168 100 ``` -* `-p` indicates running on a remote target via pyboard.py, not the host. -* `-d PORTNAME` is the serial port, `/dev/ttyACM0` is the default if not - provided. +* `-t PORTNAME` is the serial port to use (and it supports shorthand like `a0`). * `168` is value `N`, the approximate CPU frequency in MHz (in this case Pyboard V1.1 is 168MHz). It's possible to choose other values as well: lower values like `10` will run much the tests much quicker, higher values like `1000` will @@ -136,11 +135,11 @@ Usually you want to know if something is faster or slower than a reference. To do this, copy the output of each `run-perfbench.py` run to a text file. This can be done multiple ways, but one way on Linux/macOS is with the `tee` -utility: `./run-perfbench.py -p 168 100 | tee pyb-run1.txt` +utility: `./run-perfbench.py -t a0 168 100 | tee pyb-run1.txt` Once you have two files with output from two different runs (maybe with different code or configuration), compare the runtimes with `./run-perfbench.py --t pybv-run1.txt pybv-run2.txt` or compare scores with `./run-perfbench.py -s +-m pybv-run1.txt pybv-run2.txt` or compare scores with `./run-perfbench.py -s pybv-run1.txt pybv-run2.txt`: ``` @@ -204,6 +203,18 @@ internal_bench/bytebuf: 1 tests performed (3 individual testcases) ``` +## Serial reliability and performance test + +Serial port reliability and performance can be tested using the `serial_test.py` script. +Pass the name of the port to test against, for example: + + $ ./serial_test.py -t /dev/ttyACM0 + +If no port is specified then `/dev/ttyACM0` is used as the default. + +The test will send data out to the target, and receive data from the target, in various +chunk sizes. The throughput of the serial connection will be reported for each sub-test. + ## Test key/certificates SSL/TLS tests in `multi_net` and `net_inet` use self-signed key/cert pairs diff --git a/tests/basics/annotate_var.py.exp b/tests/basics/annotate_var.py.exp deleted file mode 100644 index 9b6536e966b0e..0000000000000 --- a/tests/basics/annotate_var.py.exp +++ /dev/null @@ -1,5 +0,0 @@ -False -1 -(1, 2) -NameError -1 diff --git a/tests/basics/array_add.py b/tests/basics/array_add.py index 76ce59f761e06..e78615541c3cb 100644 --- a/tests/basics/array_add.py +++ b/tests/basics/array_add.py @@ -14,3 +14,9 @@ a1.extend(array.array('I', [5])) print(a1) + +a1.extend([6, 7]) +print(a1) + +a1.extend(i for i in (8, 9)) +print(a1) diff --git a/tests/basics/assign_expr.py.exp b/tests/basics/assign_expr.py.exp deleted file mode 100644 index 47da56b80d47b..0000000000000 --- a/tests/basics/assign_expr.py.exp +++ /dev/null @@ -1,16 +0,0 @@ -4 -True -2 -4 5 -5 -1 5 5 -5 -2 1 -1 0 -any True -8 -123 -any True -8 -[(1, 0), (2, 2), (3, 6), (4, 12)] -4 diff --git a/tests/basics/assign_expr_scope.py.exp b/tests/basics/assign_expr_scope.py.exp deleted file mode 100644 index 5c780b382215f..0000000000000 --- a/tests/basics/assign_expr_scope.py.exp +++ /dev/null @@ -1,23 +0,0 @@ -scope0 -1 -None -scope1 -[1] -1 -None -scope2 -[0, 1] -1 -1 -scope3 -[0, 1] -None -None -scope4 -[0, 1] -1 -1 -scope5 -[0, 1] -1 -None diff --git a/tests/basics/async_await.py.exp b/tests/basics/async_await.py.exp deleted file mode 100644 index b51c388a9339e..0000000000000 --- a/tests/basics/async_await.py.exp +++ /dev/null @@ -1,32 +0,0 @@ -4 -3 -2 -1 -0 -0 -1 -0 -0 -2 -1 -0 -0 -1 -0 -0 -3 -2 -1 -0 -0 -1 -0 -0 -2 -1 -0 -0 -1 -0 -0 -finished diff --git a/tests/basics/async_await2.py.exp b/tests/basics/async_await2.py.exp deleted file mode 100644 index fc9ff0aa535fc..0000000000000 --- a/tests/basics/async_await2.py.exp +++ /dev/null @@ -1,5 +0,0 @@ -wait value: 1 -return from send: message from wait(1) -wait got back: message from main -x = 100 -got StopIteration diff --git a/tests/basics/async_def.py.exp b/tests/basics/async_def.py.exp deleted file mode 100644 index f555ace99ab20..0000000000000 --- a/tests/basics/async_def.py.exp +++ /dev/null @@ -1,3 +0,0 @@ -decorator -foo -StopIteration diff --git a/tests/basics/async_for.py.exp b/tests/basics/async_for.py.exp deleted file mode 100644 index 6f59979c065de..0000000000000 --- a/tests/basics/async_for.py.exp +++ /dev/null @@ -1,51 +0,0 @@ -== start == -init -aiter -init -anext -a -anext -b -anext -c -anext -== finish == -== start == -init -aiter -init -anext -d -anext -e -anext -f -anext -AsyncIteratorWrapper-def -== finish == -init -== start == -aiter -init -anext -g -anext -h -anext -i -anext -AsyncIteratorWrapper-ghi -== finish == -init -== start == -aiter -init -anext -j -anext -k -anext -l -anext -AsyncIteratorWrapper-jkl -== finish == diff --git a/tests/basics/async_for2.py b/tests/basics/async_for2.py index bbdb02c49b209..82232d52fc2a1 100644 --- a/tests/basics/async_for2.py +++ b/tests/basics/async_for2.py @@ -1,7 +1,7 @@ # test waiting within "async for" __anext__ function # CIRCUITPY-CHANGE -# uPy allows normal generators to be awaitables. +# MicroPython allows normal generators to be awaitables. # CircuitPython does not. # In CircuitPython you need to have an __await__ method on an awaitable like in CPython; # and like in CPython, generators do not have __await__. diff --git a/tests/basics/async_for2.py.exp b/tests/basics/async_for2.py.exp deleted file mode 100644 index 52bbe90c85376..0000000000000 --- a/tests/basics/async_for2.py.exp +++ /dev/null @@ -1,32 +0,0 @@ -init -aiter -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -x 0 -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -x 1 -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -x 2 -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -x 3 -anext -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -finished diff --git a/tests/basics/async_syntaxerror.py.exp b/tests/basics/async_syntaxerror.py.exp deleted file mode 100644 index 5275689b41383..0000000000000 --- a/tests/basics/async_syntaxerror.py.exp +++ /dev/null @@ -1,2 +0,0 @@ -SyntaxError -SyntaxError diff --git a/tests/basics/async_with.py.exp b/tests/basics/async_with.py.exp deleted file mode 100644 index 6bbf84cb4b52c..0000000000000 --- a/tests/basics/async_with.py.exp +++ /dev/null @@ -1,11 +0,0 @@ -enter -body -exit None None -finished -enter -1 -exit error -ValueError -enter -exit -BaseException diff --git a/tests/basics/async_with2.py b/tests/basics/async_with2.py index 5bac38236fe4a..b18fa6bdee106 100644 --- a/tests/basics/async_with2.py +++ b/tests/basics/async_with2.py @@ -1,7 +1,7 @@ # test waiting within async with enter/exit functions # CIRCUITPY-CHANGE -# uPy allows normal generators to be awaitables. +# MicroPython allows normal generators to be awaitables. # CircuitPython does not. # In CircuitPython you need to have an __await__ method on an awaitable like in CPython; # and like in CPython, generators do not have __await__. diff --git a/tests/basics/async_with2.py.exp b/tests/basics/async_with2.py.exp deleted file mode 100644 index 76b173b4c24a7..0000000000000 --- a/tests/basics/async_with2.py.exp +++ /dev/null @@ -1,17 +0,0 @@ -enter -f start: 10 -coro yielded: 11 -coro yielded: 12 -f returned: 13 -body start -f start: 30 -coro yielded: 31 -coro yielded: 32 -body f returned: 33 -body end -exit None None -f start: 20 -coro yielded: 21 -coro yielded: 22 -f returned: 23 -finished diff --git a/tests/basics/async_with_break.py.exp b/tests/basics/async_with_break.py.exp deleted file mode 100644 index d077a88fad0e4..0000000000000 --- a/tests/basics/async_with_break.py.exp +++ /dev/null @@ -1,15 +0,0 @@ -enter -body -exit None None -finished -enter -body -exit None None -finally -finished -enter -body -exit None None -finally inner -finally outer -finished diff --git a/tests/basics/async_with_return.py.exp b/tests/basics/async_with_return.py.exp deleted file mode 100644 index d077a88fad0e4..0000000000000 --- a/tests/basics/async_with_return.py.exp +++ /dev/null @@ -1,15 +0,0 @@ -enter -body -exit None None -finished -enter -body -exit None None -finally -finished -enter -body -exit None None -finally inner -finally outer -finished diff --git a/tests/basics/attrtuple2.py b/tests/basics/attrtuple2.py new file mode 100644 index 0000000000000..081d24b6ae92c --- /dev/null +++ b/tests/basics/attrtuple2.py @@ -0,0 +1,25 @@ +# test os.uname() attrtuple, if available +try: + import os +except ImportError: + print("SKIP") + raise SystemExit + +try: + u = os.uname() +except AttributeError: + print("SKIP") + raise SystemExit + +# test printing of attrtuple +print(str(u).find("machine=") > 0) + +# test read attr +print(isinstance(u.machine, str)) + +# test str modulo operator for attrtuple +impl_str = ("%s " * len(u)) % u +test_str = "" +for val in u: + test_str += val + " " +print(impl_str == test_str) diff --git a/tests/basics/boundmeth1.py b/tests/basics/boundmeth1.py index fe1b454688d64..9f08d9bbb259b 100644 --- a/tests/basics/boundmeth1.py +++ b/tests/basics/boundmeth1.py @@ -1,6 +1,6 @@ # tests basics of bound methods -# uPy and CPython differ when printing a bound method, so just print the type +# MicroPython and CPython differ when printing a bound method, so just print the type print(type(repr([].append))) diff --git a/tests/basics/builtin_help.py b/tests/basics/builtin_help.py index 6ec39653fe993..3d4e4f26bd2bb 100644 --- a/tests/basics/builtin_help.py +++ b/tests/basics/builtin_help.py @@ -14,4 +14,10 @@ help(micropython) # help for a module help('modules') # list available modules +class A: + x = 1 + y = 2 + del x +help(A) + print('done') # so last bit of output is predictable diff --git a/tests/basics/builtin_pow3_intbig.py b/tests/basics/builtin_pow3_intbig.py index bedc8b36b7edd..41d2acbc0cc75 100644 --- a/tests/basics/builtin_pow3_intbig.py +++ b/tests/basics/builtin_pow3_intbig.py @@ -20,3 +20,8 @@ print(hex(pow(y, x-1, x))) # Should be 1, since x is prime print(hex(pow(y, y-1, x))) # Should be a 'big value' print(hex(pow(y, y-1, y))) # Should be a 'big value' + +try: + print(pow(1, 2, 0)) +except ValueError: + print("ValueError") diff --git a/tests/basics/builtin_range.py b/tests/basics/builtin_range.py index 66226bad16a6d..9608a8163107e 100644 --- a/tests/basics/builtin_range.py +++ b/tests/basics/builtin_range.py @@ -32,13 +32,29 @@ print(range(1, 4)[0:]) print(range(1, 4)[1:]) print(range(1, 4)[:-1]) +print(range(4, 1)[:]) +print(range(4, 1)[0:]) +print(range(4, 1)[1:]) +print(range(4, 1)[:-1]) print(range(7, -2, -4)[:]) print(range(1, 100, 5)[5:15:3]) print(range(1, 100, 5)[15:5:-3]) print(range(100, 1, -5)[5:15:3]) print(range(100, 1, -5)[15:5:-3]) +print(range(1, 100, 5)[5:15:-3]) +print(range(1, 100, 5)[15:5:3]) +print(range(100, 1, -5)[5:15:-3]) +print(range(100, 1, -5)[15:5:3]) +print(range(1, 100, 5)[5:15:3]) +print(range(1, 100, 5)[15:5:-3]) +print(range(1, 100, -5)[5:15:3]) +print(range(1, 100, -5)[15:5:-3]) +print(range(1, 100, 5)[5:15:-3]) +print(range(1, 100, 5)[15:5:3]) +print(range(1, 100, -5)[5:15:-3]) +print(range(1, 100, -5)[15:5:3]) -# for this case uPy gives a different stop value but the listed elements are still correct +# for this case MicroPython gives a different stop value but the listed elements are still correct print(list(range(7, -2, -4)[2:-2:])) # zero step diff --git a/tests/basics/builtin_range_maxsize.py b/tests/basics/builtin_range_maxsize.py new file mode 100644 index 0000000000000..b0f3a5e5129f1 --- /dev/null +++ b/tests/basics/builtin_range_maxsize.py @@ -0,0 +1,38 @@ +try: + from sys import maxsize +except ImportError: + print("SKIP") + raise SystemExit + +# Test the range builtin at extreme values. (https://github.com/micropython/micropython/issues/17684) +# +# This is written using asserts instead of prints because the value of `maxsize` differs. +# +# Numbers & counts right up against the max of mp_int_t also cause overflows, such as +# objrange.c:115:14: runtime error: signed integer overflow: 9223372036854775807 + 1 cannot be represented in type 'long int' +# so just avoid them for the purposes of this test. + +r = range(-maxsize + 1, -1) +assert r.start == -maxsize + 1 +assert r.stop == -1 +assert r[0] == -maxsize + 1 +assert r[1] == -maxsize + 2 +assert r[-1] == -2 +assert r[-2] == -3 + +ir = iter(r) +assert next(ir) == -maxsize + 1 +assert next(ir) == -maxsize + 2 + +r = range(0, maxsize - 1) +assert len(r) == maxsize - 1 +assert r.stop == maxsize - 1 + +r = range(maxsize, 0, -1) +assert len(r) == maxsize +assert r.start == maxsize +assert r[0] == maxsize +assert r[1] == maxsize - 1 +ir = iter(r) +assert next(ir) == maxsize +assert next(ir) == maxsize - 1 diff --git a/tests/basics/builtin_setattr.py b/tests/basics/builtin_setattr.py index e4acb14cac354..2c9a452c32774 100644 --- a/tests/basics/builtin_setattr.py +++ b/tests/basics/builtin_setattr.py @@ -21,5 +21,5 @@ def __init__(self): try: setattr(int, 'to_bytes', 1) except (AttributeError, TypeError): - # uPy raises AttributeError, CPython raises TypeError + # MicroPython raises AttributeError, CPython raises TypeError print('AttributeError/TypeError') diff --git a/tests/basics/builtin_slice.py b/tests/basics/builtin_slice.py index 3ff3c3dd04d43..cbce3d14ed699 100644 --- a/tests/basics/builtin_slice.py +++ b/tests/basics/builtin_slice.py @@ -1,11 +1,44 @@ # test builtin slice +# ensures that slices passed to user types are heap-allocated and can be +# safely stored as well as not overridden by subsequent slices. + # print slice class A: def __getitem__(self, idx): - print(idx) + print("get", idx) + print("abc"[1:]) + print("get", idx) + return idx + + def __setitem__(self, idx, value): + print("set", idx) + print("abc"[1:]) + print("set", idx) + self.saved_idx = idx + return idx + + def __delitem__(self, idx): + print("del", idx) + print("abc"[1:]) + print("del", idx) return idx -s = A()[1:2:3] + + +a = A() +s = a[1:2:3] +a[4:5:6] = s +del a[7:8:9] + +print(a.saved_idx) + +# nested slicing +print(A()[1 : A()[A()[2:3:4] : 5]]) + +# tuple slicing +a[1:2, 4:5, 7:8] +a[1, 4:5, 7:8, 2] +a[1:2, a[3:4], 5:6] # check type print(type(s) is slice) diff --git a/tests/basics/bytes_format_modulo.py.exp b/tests/basics/bytes_format_modulo.py.exp deleted file mode 100644 index 782b7f91fcbfe..0000000000000 --- a/tests/basics/bytes_format_modulo.py.exp +++ /dev/null @@ -1,5 +0,0 @@ -b'%' -b'=1=' -b'=1=2=' -b'=str=' -b"=b'str'=" diff --git a/tests/basics/class_bind_self.py b/tests/basics/class_bind_self.py index 813f876446ef2..8bece902ca254 100644 --- a/tests/basics/class_bind_self.py +++ b/tests/basics/class_bind_self.py @@ -52,7 +52,7 @@ def f4(self, arg): # generator print(C.f8(13)) print(C.f9(14)) -# not working in uPy +# not working in MicroPython #class C(list): # # this acts like a method and binds self # f1 = list.extend diff --git a/tests/basics/class_descriptor.py b/tests/basics/class_descriptor.py index 83d31674301d5..feaed2fbb2a43 100644 --- a/tests/basics/class_descriptor.py +++ b/tests/basics/class_descriptor.py @@ -1,22 +1,28 @@ class Descriptor: def __get__(self, obj, cls): - print('get') + print("get") print(type(obj) is Main) print(cls is Main) - return 'result' + return "result" def __set__(self, obj, val): - print('set') + print("set") print(type(obj) is Main) print(val) def __delete__(self, obj): - print('delete') + print("delete") print(type(obj) is Main) + def __set_name__(self, owner, name): + print("set_name", name) + print(owner.__name__ == "Main") + + class Main: Forward = Descriptor() + m = Main() try: m.__class__ @@ -26,15 +32,15 @@ class Main: raise SystemExit r = m.Forward -if 'Descriptor' in repr(r.__class__): +if "Descriptor" in repr(r.__class__): # Target doesn't support descriptors. - print('SKIP') + print("SKIP") raise SystemExit # Test assignment and deletion. print(r) -m.Forward = 'a' +m.Forward = "a" del m.Forward # Test that lookup of descriptors like __get__ are not passed into __getattr__. diff --git a/tests/basics/class_inplace_op2.py.exp b/tests/basics/class_inplace_op2.py.exp deleted file mode 100644 index 8c323b5178ef4..0000000000000 --- a/tests/basics/class_inplace_op2.py.exp +++ /dev/null @@ -1,12 +0,0 @@ -__imul__ -__imatmul__ -__ifloordiv__ -__itruediv__ -__imod__ -__ipow__ -__ior__ -__ixor__ -__iand__ -__ilshift__ -__irshift__ -TypeError diff --git a/tests/basics/class_ordereddict.py.exp b/tests/basics/class_ordereddict.py.exp deleted file mode 100644 index b723e327515c7..0000000000000 --- a/tests/basics/class_ordereddict.py.exp +++ /dev/null @@ -1 +0,0 @@ -['a', 'b', 'c', 'd', 'e'] diff --git a/tests/basics/class_setname_hazard.py b/tests/basics/class_setname_hazard.py new file mode 100644 index 0000000000000..77c0409346282 --- /dev/null +++ b/tests/basics/class_setname_hazard.py @@ -0,0 +1,182 @@ +# Test that __set_name__ can access and mutate its owner argument. + + +def skip_if_no_descriptors(): + class Descriptor: + def __get__(self, obj, cls): + return + + class TestClass: + Forward = Descriptor() + + a = TestClass() + try: + a.__class__ + except AttributeError: + # Target doesn't support __class__. + print("SKIP") + raise SystemExit + + b = a.Forward + if "Descriptor" in repr(b.__class__): + # Target doesn't support descriptors. + print("SKIP") + raise SystemExit + + +skip_if_no_descriptors() + + +# Test basic accesses and mutations. + + +class GetSibling: + def __set_name__(self, owner, name): + print(getattr(owner, name + "_sib")) + + +class GetSiblingTest: + desc = GetSibling() + desc_sib = 111 + + +t110 = GetSiblingTest() + + +class SetSibling: + def __set_name__(self, owner, name): + setattr(owner, name + "_sib", 121) + + +class SetSiblingTest: + desc = SetSibling() + + +t120 = SetSiblingTest() + +print(t120.desc_sib) + + +class DelSibling: + def __set_name__(self, owner, name): + delattr(owner, name + "_sib") + + +class DelSiblingTest: + desc = DelSibling() + desc_sib = 131 + + +t130 = DelSiblingTest() + +try: + print(t130.desc_sib) +except AttributeError: + print("AttributeError") + + +class GetSelf: + x = 211 + + def __set_name__(self, owner, name): + print(getattr(owner, name).x) + + +class GetSelfTest: + desc = GetSelf() + + +t210 = GetSelfTest() + + +class SetSelf: + def __set_name__(self, owner, name): + setattr(owner, name, 221) + + +class SetSelfTest: + desc = SetSelf() + + +t220 = SetSelfTest() + +print(t220.desc) + + +class DelSelf: + def __set_name__(self, owner, name): + delattr(owner, name) + + +class DelSelfTest: + desc = DelSelf() + + +t230 = DelSelfTest() + +try: + print(t230.desc) +except AttributeError: + print("AttributeError") + + +# Test exception behavior. + + +class Raise: + def __set_name__(self, owner, name): + raise Exception() + + +try: + + class RaiseTest: + desc = Raise() +except Exception as e: # CPython raises RuntimeError, MicroPython propagates the original exception + print("Exception") + + +# Ensure removed/overwritten class members still get __set_name__ called. + + +class SetSpecific: + def __init__(self, sib_name, sib_replace): + self.sib_name = sib_name + self.sib_replace = sib_replace + + def __set_name__(self, owner, name): + setattr(owner, self.sib_name, self.sib_replace) + + +class SetReplaceTest: + a = SetSpecific("b", 312) # one of these is changed first + b = SetSpecific("a", 311) + + +t310 = SetReplaceTest() +print(t310.a) +print(t310.b) + + +class DelSpecific: + def __init__(self, sib_name): + self.sib_name = sib_name + + def __set_name__(self, owner, name): + delattr(owner, self.sib_name) + + +class DelReplaceTest: + a = DelSpecific("b") # one of these is removed first + b = DelSpecific("a") + + +t320 = DelReplaceTest() +try: + print(t320.a) +except AttributeError: + print("AttributeError") +try: + print(t320.b) +except AttributeError: + print("AttributeError") diff --git a/tests/basics/class_setname_hazard_rand.py b/tests/basics/class_setname_hazard_rand.py new file mode 100644 index 0000000000000..4c9934c3bf068 --- /dev/null +++ b/tests/basics/class_setname_hazard_rand.py @@ -0,0 +1,111 @@ +# Test to make sure there's no sequence hazard even when a __set_name__ implementation +# mutates and reorders the namespace of its owner class. +# VERY hard bug to prove out except via a stochastic test. + + +try: + from random import choice + import re +except ImportError: + print("SKIP") + raise SystemExit + + +def skip_if_no_descriptors(): + class Descriptor: + def __get__(self, obj, cls): + return + + class TestClass: + Forward = Descriptor() + + a = TestClass() + try: + a.__class__ + except AttributeError: + # Target doesn't support __class__. + print("SKIP") + raise SystemExit + + b = a.Forward + if "Descriptor" in repr(b.__class__): + # Target doesn't support descriptors. + print("SKIP") + raise SystemExit + + +skip_if_no_descriptors() + +letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +# Would be r"[A-Z]{5}", but not all ports support the {n} quantifier. +junk_re = re.compile(r"[A-Z][A-Z][A-Z][A-Z][A-Z]") + + +def junk_fill(obj, n=10): # Add randomly-generated attributes to an object. + for i in range(n): + name = "".join(choice(letters) for j in range(5)) + setattr(obj, name, object()) + + +def junk_clear(obj): # Remove attributes added by junk_fill. + to_del = [name for name in dir(obj) if junk_re.match(name)] + for name in to_del: + delattr(obj, name) + + +def junk_sequencer(): + global runs + try: + while True: + owner, name = yield + runs[name] = runs.get(name, 0) + 1 + junk_fill(owner) + finally: + junk_clear(owner) + + +class JunkMaker: + def __set_name__(self, owner, name): + global seq + seq.send((owner, name)) + + +runs = {} +seq = junk_sequencer() +next(seq) + + +class Main: + a = JunkMaker() + b = JunkMaker() + c = JunkMaker() + d = JunkMaker() + e = JunkMaker() + f = JunkMaker() + g = JunkMaker() + h = JunkMaker() + i = JunkMaker() + j = JunkMaker() + k = JunkMaker() + l = JunkMaker() + m = JunkMaker() + n = JunkMaker() + o = JunkMaker() + p = JunkMaker() + q = JunkMaker() + r = JunkMaker() + s = JunkMaker() + t = JunkMaker() + u = JunkMaker() + v = JunkMaker() + w = JunkMaker() + x = JunkMaker() + y = JunkMaker() + z = JunkMaker() + + +seq.close() + +for k in letters.lower(): + print(k, runs.get(k, 0)) diff --git a/tests/basics/del_attr.py b/tests/basics/del_attr.py index 8487b97e311d0..90a8a5a6f833d 100644 --- a/tests/basics/del_attr.py +++ b/tests/basics/del_attr.py @@ -35,5 +35,5 @@ def f(): try: del int.to_bytes except (AttributeError, TypeError): - # uPy raises AttributeError, CPython raises TypeError + # MicroPython raises AttributeError, CPython raises TypeError print('AttributeError/TypeError') diff --git a/tests/basics/del_global.py b/tests/basics/del_global.py index d740357b0332d..e1aa074b97a43 100644 --- a/tests/basics/del_global.py +++ b/tests/basics/del_global.py @@ -14,7 +14,7 @@ def do_del(): try: do_del() except: # NameError: - # FIXME uPy returns KeyError for this + # FIXME MicroPython returns KeyError for this print("NameError") # delete globals using a list diff --git a/tests/basics/del_name.py b/tests/basics/del_name.py index c92be54d3b655..8393b2df92a72 100644 --- a/tests/basics/del_name.py +++ b/tests/basics/del_name.py @@ -10,7 +10,7 @@ try: del x except: # NameError: - # FIXME uPy returns KeyError for this + # FIXME MicroPython returns KeyError for this print("NameError") class C: diff --git a/tests/basics/dict_views.py b/tests/basics/dict_views.py index a82f47b6be26f..45f3238f1c624 100644 --- a/tests/basics/dict_views.py +++ b/tests/basics/dict_views.py @@ -6,6 +6,11 @@ # print a view with more than one item print({1:1, 2:1}.values()) +# `bool` and `len` unary ops +for d in ({}, {1: 2}, {1: 2, 3: 4}): + for op in (bool, len): + print(op(d.keys()), op(d.values()), op(d.items())) + # unsupported binary op on a dict values view try: {1:1}.values() + 1 diff --git a/tests/basics/exception_chain.py b/tests/basics/exception_chain.py index 579756c85fa20..96c06c847ab37 100644 --- a/tests/basics/exception_chain.py +++ b/tests/basics/exception_chain.py @@ -1,5 +1,12 @@ # CIRCUITPY-CHANGE: exception chaining is supported +import sys + +# The unix minimal build doesn't enable MICROPY_WARNINGS (required for this test). +if getattr(sys.implementation, "_build", None) == "minimal": + print("SKIP") + raise SystemExit + try: Exception().__cause__ except AttributeError: diff --git a/tests/basics/frozenset_set.py b/tests/basics/frozenset_set.py index 3bf456acfd70f..2b3c683260989 100644 --- a/tests/basics/frozenset_set.py +++ b/tests/basics/frozenset_set.py @@ -8,5 +8,5 @@ # "Instances of set are compared to instances of frozenset based on their # members. For example:" print(set('abc') == frozenset('abc')) -# This doesn't work in uPy +# This doesn't work in MicroPython #print(set('abc') in set([frozenset('abc')])) diff --git a/tests/basics/fun_code_colines.py b/tests/basics/fun_code_colines.py new file mode 100644 index 0000000000000..a8867770eddf1 --- /dev/null +++ b/tests/basics/fun_code_colines.py @@ -0,0 +1,81 @@ +# Check that we have sensical bytecode offsets in function.__code__.co_lines + +def f1(x, y, obj, obj2, obj3): + a = x + y # line 4: bc+4 line+4 + b = x - y # line 5: bc+4 line+1 + # line 6 + # line 7 + # line 8 + # line 9 + # line 10 + # line 11 + # line 12 + # line 13 + # line 14 + # line 15 + # line 16 + # line 17 + # line 18 + # line 19 + c = a * b # line 20: bc+4 line+15 + obj.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.fun() # line 21: bc+31 line+1; bc+27 line+0 + # line 22 + # line 23 + # line 24: bc+0 line+3 + # line 25 + # line 26 + # line 27: bc+0 line+3 + # line 28 + # line 29 + obj2.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.fun() # line 30: bc+31 line+3; bc+27 line+0 + # line 31 + # line 32 + # line 33: bc+0 line+3 + # line 34 + # line 35 + # line 36 + # line 37 + # line 38 + # line 39 + # line 40 + # line 41 + # line 42 + # line 43 + # line 44 + # line 45 + # line 46 + # line 47 + # line 48 + # line 49 + # line 50 + # line 51 + # line 52 + # line 53 + # line 54 + # line 55 + # line 56 + # line 57 + # line 58 + # line 59 + return obj3.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z.fun() # line 60: bc+31 line+27; bc+27 line+0 + +def f2(x, y): + a = x + y # line 63 + b = x - y # line 64 + return a * b # line 65 + +try: + f1.__code__.co_lines +except AttributeError: + print("SKIP") + raise SystemExit + +print("f1") +for start, end, line_no in f1.__code__.co_lines(): + print("line {} start: {}".format(line_no, start)) + print("line {} end: {}".format(line_no, end)) + +print("f2") +for start, end, line_no in f2.__code__.co_lines(): + print("line {} start: {}".format(line_no, start)) + print("line {} end: {}".format(line_no, end)) diff --git a/tests/basics/fun_code_colines.py.exp b/tests/basics/fun_code_colines.py.exp new file mode 100644 index 0000000000000..19bd4ef6e2a5a --- /dev/null +++ b/tests/basics/fun_code_colines.py.exp @@ -0,0 +1,20 @@ +f1 +line 4 start: 0 +line 4 end: 4 +line 5 start: 4 +line 5 end: 8 +line 20 start: 8 +line 20 end: 12 +line 21 start: 12 +line 21 end: 70 +line 30 start: 70 +line 30 end: 128 +line 60 start: 128 +line 60 end: 186 +f2 +line 63 start: 0 +line 63 end: 4 +line 64 start: 4 +line 64 end: 8 +line 65 start: 8 +line 65 end: 12 diff --git a/tests/basics/fun_code_full.py b/tests/basics/fun_code_full.py new file mode 100644 index 0000000000000..538863631a6e0 --- /dev/null +++ b/tests/basics/fun_code_full.py @@ -0,0 +1,38 @@ +# Test function.__code__ attributes not available with MICROPY_PY_BUILTINS_CODE <= MICROPY_PY_BUILTINS_CODE_BASIC + +try: + (lambda: 0).__code__.co_code +except AttributeError: + print("SKIP") + raise SystemExit + +def f(x, y): + a = x + y + b = x - y + return a * b + +code = f.__code__ + +print(type(code.co_code)) # both bytes (but mpy and cpy have different instruction sets) +print(code.co_consts) # (not necessarily the same set, but in this function they are) +print(code.co_filename.rsplit('/')[-1]) # same terminal filename but might be different paths on other ports +print(type(code.co_firstlineno)) # both ints (but mpy points to first line inside, cpy points to declaration) +print(code.co_name) +print(iter(code.co_names) is not None) # both iterable (but mpy returns dict with names as keys, cpy only the names; and not necessarily the same set) + +co_lines = code.co_lines() + +l = list(co_lines) +first_start = l[0][0] +last_end = l[-1][1] +print(first_start) # co_lines should start at the start of the bytecode +print(len(code.co_code) - last_end) # and end at the end of the bytecode + +prev_end = 0 +for start, end, line_no in l: + if end != prev_end: + print("non-contiguous") + break # the offset ranges should be contiguous + prev_end = end +else: + print("contiguous") diff --git a/tests/basics/fun_code_full.py.exp b/tests/basics/fun_code_full.py.exp new file mode 100644 index 0000000000000..830effadfc695 --- /dev/null +++ b/tests/basics/fun_code_full.py.exp @@ -0,0 +1,9 @@ + +(None,) +fun_code_full.py + +f +True +0 +0 +non-contiguous diff --git a/tests/basics/fun_code_lnotab.py b/tests/basics/fun_code_lnotab.py new file mode 100644 index 0000000000000..9223e5730f0ea --- /dev/null +++ b/tests/basics/fun_code_lnotab.py @@ -0,0 +1,34 @@ +# Test deprecation of co_lnotab + +try: + (lambda: 0).__code__.co_code +except AttributeError: + print("SKIP") + raise SystemExit + + +import unittest +import sys + + +mpy_is_v2 = getattr(sys.implementation, '_v2', False) + + +def f(): + pass + + +class Test(unittest.TestCase): + + @unittest.skipIf(mpy_is_v2, "Removed in MicroPython v2 and later.") + def test_co_lnotab_exists(self): + self.assertIsInstance(f.__code__.co_lnotab, bytes) + + @unittest.skipUnless(mpy_is_v2, "Not removed before MicroPython v2.") + def test_co_lnotab_removed(self): + with self.assertRaises(AttributeError): + f.__code__.co_lnotab + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/basics/fun_name.py b/tests/basics/fun_name.py index b2642280a2a65..8aac35eeafa25 100644 --- a/tests/basics/fun_name.py +++ b/tests/basics/fun_name.py @@ -16,7 +16,7 @@ def Fun(self): print('SKIP') raise SystemExit -# __name__ of a bound native method is not implemented in uPy +# __name__ of a bound native method is not implemented in MicroPython # the test here is to make sure it doesn't crash try: str((1).to_bytes.__name__) diff --git a/tests/basics/gc1.py b/tests/basics/gc1.py index 332bf9744c072..dff9538b14b5c 100644 --- a/tests/basics/gc1.py +++ b/tests/basics/gc1.py @@ -15,13 +15,13 @@ gc.collect() if hasattr(gc, 'mem_free'): - # uPy has these extra functions + # MicroPython has these extra functions # just test they execute and return an int assert type(gc.mem_free()) is int assert type(gc.mem_alloc()) is int if hasattr(gc, 'threshold'): - # uPy has this extra function + # MicroPython has this extra function # check execution and returns assert(gc.threshold(1) is None) assert(gc.threshold() == 0) diff --git a/tests/basics/import_instance_method.py b/tests/basics/import_instance_method.py new file mode 100644 index 0000000000000..d25b70ac5f0bf --- /dev/null +++ b/tests/basics/import_instance_method.py @@ -0,0 +1,38 @@ +# Test importing a method from a class instance. +# This is not a common thing to do, but ensures MicroPython has the same semantics as CPython. + +import sys + +if not hasattr(sys, "modules"): + print("SKIP") + raise SystemExit + + +class A: + def __init__(self, value): + self.value = value + + def meth(self): + return self.value + + def meth_with_arg(self, a): + return [self.value, a] + + +# Register a class instance as the module "mod". +sys.modules["mod"] = A(1) + +# Try importing it as a module. +import mod + +print(mod.meth()) +print(mod.meth_with_arg(2)) + +# Change the module. +sys.modules["mod"] = A(3) + +# Try importing it using "from ... import". +from mod import meth, meth_with_arg + +print(meth()) +print(meth_with_arg(4)) diff --git a/tests/basics/int_64_basics.py b/tests/basics/int_64_basics.py new file mode 100644 index 0000000000000..ef76793317ec8 --- /dev/null +++ b/tests/basics/int_64_basics.py @@ -0,0 +1,161 @@ +# test support for 64-bit long integers +# (some ports don't support arbitrary precision but do support these) + +# this test is adapted from int_big1.py with numbers kept within 64-bit signed range + +# to test arbitrary precision integers + +x = 1000000000000000000 +xn = -1000000000000000000 +y = 2000000000000000000 + +# printing +print(x) +print(y) +print('%#X' % (x - x)) # print prefix +print('{:#,}'.format(x)) # print with commas + +# construction +print(int(x)) + +# addition +print(x + 1) +print(x + y) +print(x + xn == 0) +print(bool(x + xn)) + +# subtraction +print(x - 1) +print(x - y) +print(y - x) +print(x - x == 0) +print(bool(x - x)) + +# multiplication +print(x * 2) +print(1090511627776 * 1048500) + +# integer division +print(x // 2) +print(y // x) + +# bit inversion +print(~x) +print(~(-x)) + +# left shift +print("left shift positive") +x = 0x40000000 +for i in range(32): + x = x << 1 + print(x) + +# right shift +print("right shift positive") +x = 0x2000000000000000 # TODO: why can't second-tip bit be set? +for i in range(64): + x = x >> 1 + print(x) + +# left shift of a negative number +print("left shift negative") +for i in range(8): + print(-10000000000000000 << i) + print(-10000000000000001 << i) + print(-10000000000000002 << i) + print(-10000000000000003 << i) + print(-10000000000000004 << i) + + +# right shift of a negative number +print("right shift negative") +for i in range(8): + print(-1000000000000000000 >> i) + print(-1000000000000000001 >> i) + print(-1000000000000000002 >> i) + print(-1000000000000000003 >> i) + print(-1000000000000000004 >> i) + +# conversion from string +print(int("1234567890123456789")) +print(int("-1234567890123456789")) +print(int("1234567890abcdef", 16)) +print(int("1234567890ABCDEF", 16)) +print(int("-1234567890ABCDEF", 16)) +print(int("ijklmnopqrsz", 36)) + +# numbers close to 64-bit limits +print(int("-9111222333444555666")) +print(int("9111222333444555666")) + +# numbers with preceding 0s +print(int("-00000000000000000000009111222333444555666")) +print(int("0000000000000000000000009111222333444555666")) + +# invalid characters in string +try: + print(int("1234567890abcdef")) +except ValueError: + print('ValueError'); +try: + print(int("123456789\x01")) +except ValueError: + print('ValueError'); + +# test parsing ints just on threshold of small to big +# for 32 bit archs +x = 1073741823 # small +x = -1073741823 # small +x = 1073741824 # big +x = -1073741824 # big +# for 64 bit archs +x = 4611686018427387903 # small +x = -4611686018427387903 # small +x = 4611686018427387904 # big +x = -4611686018427387904 # big + +# sys.maxsize is a constant bigint, so test it's compatible with dynamic ones +import sys +if hasattr(sys, "maxsize"): + print(sys.maxsize - 1 + 1 == sys.maxsize) +else: + print(True) # No maxsize property in this config + +# test extraction of big int value via mp_obj_get_int_maybe +x = 1 << 62 +print('a' * (x + 4 - x)) + +# test overflow check in mp_obj_get_int_maybe +x = 1 << 32 +r = None +try: + r = range(0, x) +except OverflowError: + # 32-bit target, correctly handled the overflow of x + print("ok") +if r is not None: + if len(r) == x: + # 64-bit target, everything is just a small-int + print("ok") + else: + # 32-bit target that did not handle the overflow of x + print("unhandled overflow") + +# negative shifts are invalid +try: + print((1 << 48) >> -4) +except ValueError as e: + print(e) + +try: + print((1 << 48) << -6) +except ValueError as e: + print(e) + +# Test that the most extreme 64 bit integer values all parse with int() +print(int("-9223372036854775807")) +print(int("-9223372036854775808")) +print(int("9223372036854775807")) + +# Test that the most negative 64 bit integer can be formed via arithmetic +print(-9223372036854775807-1) diff --git a/tests/basics/int_big_to_small.py b/tests/basics/int_big_to_small.py index 64280d0c635f5..c92d263fc2837 100644 --- a/tests/basics/int_big_to_small.py +++ b/tests/basics/int_big_to_small.py @@ -5,6 +5,17 @@ print("SKIP") raise SystemExit +# Skip this test on "REPR B" where (1<<29 + 1) is not a small int. +k = 1 << 29 +micropython.heap_lock() +try: + k = k + 1 +except MemoryError: + print("SKIP") + raise SystemExit +finally: + micropython.heap_unlock() + # All less than small int max. for d in (0, 27, 1<<29, -1861, -(1<<29)): i = 1<<70 diff --git a/tests/basics/int_big_to_small_int29.py b/tests/basics/int_big_to_small_int29.py new file mode 100644 index 0000000000000..438a74d2bc6a4 --- /dev/null +++ b/tests/basics/int_big_to_small_int29.py @@ -0,0 +1,22 @@ +try: + import micropython + micropython.heap_lock +except: + print("SKIP") + raise SystemExit + +# All less than small int max. +for d in (0, 27, 1<<28, -1861, -(1<<28)): + i = 1<<70 + print(i) + j = (1<<70) + d + print(j) + # k should now be a small int. + k = j - i + print(k) + + # Now verify that working with k doesn't allocate (i.e. it's a small int). + micropython.heap_lock() + print(k + 20) + print(k // 20) + micropython.heap_unlock() diff --git a/tests/basics/int_big_to_small_int29.py.exp b/tests/basics/int_big_to_small_int29.py.exp new file mode 100644 index 0000000000000..0920101924aa8 --- /dev/null +++ b/tests/basics/int_big_to_small_int29.py.exp @@ -0,0 +1,25 @@ +1180591620717411303424 +1180591620717411303424 +0 +20 +0 +1180591620717411303424 +1180591620717411303451 +27 +47 +1 +1180591620717411303424 +1180591620717679738880 +268435456 +268435476 +13421772 +1180591620717411303424 +1180591620717411301563 +-1861 +-1841 +-94 +1180591620717411303424 +1180591620717142867968 +-268435456 +-268435436 +-13421773 diff --git a/tests/basics/io_buffered_writer.py b/tests/basics/io_buffered_writer.py index 11df0324e2467..3cfee0103f777 100644 --- a/tests/basics/io_buffered_writer.py +++ b/tests/basics/io_buffered_writer.py @@ -1,10 +1,10 @@ -import io - try: + import io + io.BytesIO io.BufferedWriter -except AttributeError: - print("SKIP") +except (AttributeError, ImportError): + print('SKIP') raise SystemExit bts = io.BytesIO() @@ -28,3 +28,27 @@ # hashing a BufferedWriter print(type(hash(buf))) + +# Test failing flush() +class MyIO(io.IOBase): + def __init__(self): + self.count = 0 + + def write(self, buf): + self.count += 1 + if self.count < 3: + return None + print("writing", buf) + return len(buf) + + +buf = io.BufferedWriter(MyIO(), 8) + +buf.write(b"foobar") + +for _ in range(4): + try: + buf.flush() + print("flushed") + except OSError: + print("OSError") diff --git a/tests/basics/io_buffered_writer.py.exp b/tests/basics/io_buffered_writer.py.exp index 2209348f5af45..d61eb148b453c 100644 --- a/tests/basics/io_buffered_writer.py.exp +++ b/tests/basics/io_buffered_writer.py.exp @@ -4,3 +4,8 @@ b'foobarfoobar' b'foobarfoobar' b'foo' +OSError +OSError +writing bytearray(b'foobar') +flushed +flushed diff --git a/tests/basics/io_bytesio_cow.py b/tests/basics/io_bytesio_cow.py index 2edb7136a9691..543c12ad42ab2 100644 --- a/tests/basics/io_bytesio_cow.py +++ b/tests/basics/io_bytesio_cow.py @@ -1,6 +1,12 @@ # Make sure that write operations on io.BytesIO don't # change original object it was constructed from. -import io + +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + b = b"foobar" a = io.BytesIO(b) diff --git a/tests/basics/io_bytesio_ext.py b/tests/basics/io_bytesio_ext.py index 4d4c60c1363b7..92e715178116c 100644 --- a/tests/basics/io_bytesio_ext.py +++ b/tests/basics/io_bytesio_ext.py @@ -1,5 +1,11 @@ # Extended stream operations on io.BytesIO -import io + +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + a = io.BytesIO(b"foobar") a.seek(10) print(a.read(10)) diff --git a/tests/basics/io_bytesio_ext2.py b/tests/basics/io_bytesio_ext2.py index 414ac90a3b083..f60a6a9a6041e 100644 --- a/tests/basics/io_bytesio_ext2.py +++ b/tests/basics/io_bytesio_ext2.py @@ -1,4 +1,9 @@ -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + a = io.BytesIO(b"foobar") try: a.seek(-10) diff --git a/tests/basics/io_iobase.py b/tests/basics/io_iobase.py index b2ee5cd63a619..31f2616310e79 100644 --- a/tests/basics/io_iobase.py +++ b/tests/basics/io_iobase.py @@ -1,15 +1,17 @@ import io try: + import io + io.IOBase -except AttributeError: +except (AttributeError, ImportError): print("SKIP") raise SystemExit class MyIO(io.IOBase): def write(self, buf): - # CPython and uPy pass in different types for buf (str vs bytearray) - print("write", len(buf)) + # CPython and MicroPython pass in different types for buf (str vs bytearray) + print('write', len(buf)) return len(buf) -print("test", file=MyIO()) +print('test', file=MyIO()) diff --git a/tests/basics/io_stringio1.py b/tests/basics/io_stringio1.py index 7d355930f5a29..889e3ce697377 100644 --- a/tests/basics/io_stringio1.py +++ b/tests/basics/io_stringio1.py @@ -1,4 +1,9 @@ -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + a = io.StringIO() print('io.StringIO' in repr(a)) print(a.getvalue()) diff --git a/tests/basics/io_stringio_base.py b/tests/basics/io_stringio_base.py index 0f65fb3fabc3b..c8890dab73ab7 100644 --- a/tests/basics/io_stringio_base.py +++ b/tests/basics/io_stringio_base.py @@ -1,7 +1,11 @@ # Checks that an instance type inheriting from a native base that uses # MP_TYPE_FLAG_ITER_IS_STREAM will still have a getiter. -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit a = io.StringIO() a.write("hello\nworld\nmicro\npython\n") diff --git a/tests/basics/io_stringio_with.py b/tests/basics/io_stringio_with.py index a3aa6ec84e066..0155ad5382dcd 100644 --- a/tests/basics/io_stringio_with.py +++ b/tests/basics/io_stringio_with.py @@ -1,4 +1,9 @@ -import io +try: + import io +except ImportError: + print("SKIP") + raise SystemExit + # test __enter__/__exit__ with io.StringIO() as b: b.write("foo") diff --git a/tests/basics/io_write_ext.py b/tests/basics/io_write_ext.py index 79f7450954e4c..5af1de7a6c3aa 100644 --- a/tests/basics/io_write_ext.py +++ b/tests/basics/io_write_ext.py @@ -1,11 +1,12 @@ # This tests extended (MicroPython-specific) form of write: # write(buf, len) and write(buf, offset, len) -import io try: + import io + io.BytesIO -except AttributeError: - print("SKIP") +except (AttributeError, ImportError): + print('SKIP') raise SystemExit buf = io.BytesIO() diff --git a/tests/basics/list_pop.py b/tests/basics/list_pop.py index 87ed456f85174..58bcdd91e414c 100644 --- a/tests/basics/list_pop.py +++ b/tests/basics/list_pop.py @@ -10,7 +10,7 @@ else: raise AssertionError("No IndexError raised") -# popping such that list storage shrinks (tests implementation detail of uPy) +# popping such that list storage shrinks (tests implementation detail of MicroPython) l = list(range(20)) for i in range(len(l)): l.pop() diff --git a/tests/basics/module2.py b/tests/basics/module2.py index a135601579cd7..d1dc18930f00d 100644 --- a/tests/basics/module2.py +++ b/tests/basics/module2.py @@ -1,4 +1,4 @@ -# uPy behaviour only: builtin modules are read-only +# MicroPython behaviour only: builtin modules are read-only import sys try: sys.x = 1 diff --git a/tests/basics/namedtuple1.py b/tests/basics/namedtuple1.py index 362c60583ec32..b9689b58423b8 100644 --- a/tests/basics/namedtuple1.py +++ b/tests/basics/namedtuple1.py @@ -21,6 +21,9 @@ print(isinstance(t, tuple)) + # a NamedTuple can be used as a tuple + print("(%d, %d)" % t) + # Check tuple can compare equal to namedtuple with same elements print(t == (t[0], t[1]), (t[0], t[1]) == t) diff --git a/tests/basics/parser.py b/tests/basics/parser.py index 626b67ad7a941..6ae5f05ba458e 100644 --- a/tests/basics/parser.py +++ b/tests/basics/parser.py @@ -7,7 +7,7 @@ raise SystemExit # completely empty string -# uPy and CPy differ for this case +# MPy and CPy differ for this case #try: # compile("", "stdin", "single") #except SyntaxError: diff --git a/tests/basics/python34.py b/tests/basics/python34.py index 922234d22db93..3071b55eff8bf 100644 --- a/tests/basics/python34.py +++ b/tests/basics/python34.py @@ -28,7 +28,7 @@ def test_syntax(code): test_syntax("del ()") # can't delete empty tuple (in 3.6 we can) # from basics/sys1.py -# uPy prints version 3.4 +# MicroPython prints version 3.4 import sys print(sys.version[:3]) print(sys.version_info[0], sys.version_info[1]) diff --git a/tests/basics/python36.py.exp b/tests/basics/python36.py.exp deleted file mode 100644 index 4b65daafa18fe..0000000000000 --- a/tests/basics/python36.py.exp +++ /dev/null @@ -1,5 +0,0 @@ -100000 -165 -65535 -123 -83 diff --git a/tests/basics/self_type_check.py b/tests/basics/self_type_check.py index 3bd5b3eed6058..5f6f6f2ca6585 100644 --- a/tests/basics/self_type_check.py +++ b/tests/basics/self_type_check.py @@ -3,6 +3,14 @@ import skip_if skip_if.board_in("gemma_m0", "trinket_m0") +import sys + +# Minimal builds usually don't enable MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG, +# which is required for this test. +if getattr(sys.implementation, "_build", None) == "minimal": + print("SKIP") + raise SystemExit + list.append try: diff --git a/tests/basics/slice_optimise.py b/tests/basics/slice_optimise.py new file mode 100644 index 0000000000000..f663e16b8c2f9 --- /dev/null +++ b/tests/basics/slice_optimise.py @@ -0,0 +1,23 @@ +# Test that the slice-on-stack optimisation does not break various uses of slice +# (see MP_TYPE_FLAG_SUBSCR_ALLOWS_STACK_SLICE type option). +# +# Note: this test has a corresponding .py.exp file because hashing slice objects +# was not allowed in CPython until 3.12. + +try: + from collections import OrderedDict +except ImportError: + print("SKIP") + raise SystemExit + +# Attempt to index with a slice, error should contain the slice (failed key). +try: + dict()[:] +except KeyError as e: + print("KeyError", e.args) + +# Put a slice and another object into an OrderedDict, and retrieve them. +x = OrderedDict() +x[:"a"] = 1 +x["b"] = 2 +print(list(x.keys()), list(x.values())) diff --git a/tests/basics/slice_optimise.py.exp b/tests/basics/slice_optimise.py.exp new file mode 100644 index 0000000000000..3fa59aae15ae6 --- /dev/null +++ b/tests/basics/slice_optimise.py.exp @@ -0,0 +1,2 @@ +KeyError (slice(None, None, None),) +[slice(None, 'a', None), 'b'] [1, 2] diff --git a/tests/basics/special_methods2.py.exp b/tests/basics/special_methods2.py.exp deleted file mode 100644 index a9ae75be55c95..0000000000000 --- a/tests/basics/special_methods2.py.exp +++ /dev/null @@ -1,19 +0,0 @@ -__pos__ called -__pos__ called -__neg__ called -__invert__ called -__mul__ called -__matmul__ called -__truediv__ called -__floordiv__ called -__iadd__ called -__isub__ called -__mod__ called -__pow__ called -__or__ called -__and__ called -__xor__ called -__lshift__ called -__rshift__ called -['a', 'b', 'c'] -False diff --git a/tests/basics/string_format.py b/tests/basics/string_format.py index e8600f5836139..11e7836a73ece 100644 --- a/tests/basics/string_format.py +++ b/tests/basics/string_format.py @@ -22,7 +22,17 @@ def test(fmt, *args): test("{:4x}", 123) test("{:4X}", 123) +test("{:4,d}", 1) +test("{:4_d}", 1) +test("{:4_o}", 1) +test("{:4_b}", 1) +test("{:4_x}", 1) + test("{:4,d}", 12345678) +test("{:4_d}", 12345678) +test("{:4_o}", 12345678) +test("{:4_b}", 12345678) +test("{:4_x}", 12345678) test("{:#4b}", 10) test("{:#4o}", 123) diff --git a/tests/basics/string_format_sep.py b/tests/basics/string_format_sep.py new file mode 100644 index 0000000000000..de131fdaf3383 --- /dev/null +++ b/tests/basics/string_format_sep.py @@ -0,0 +1,9 @@ +try: + "%d" % 1 +except TypeError: + print("SKIP") + raise SystemExit + +for v in (0, 0x10, 0x1000, -0x10, -0x1000): + for sz in range(1, 12): print(("{:0%d,d}" % sz).format(v)) + for sz in range(1, 12): print(("{:0%d_x}" % sz).format(v)) diff --git a/tests/basics/string_fstring_debug.py.exp b/tests/basics/string_fstring_debug.py.exp deleted file mode 100644 index f0309e1c98aa9..0000000000000 --- a/tests/basics/string_fstring_debug.py.exp +++ /dev/null @@ -1,9 +0,0 @@ -x=1 -x=00000001 -a x=1 b 2 c -a x=00000001 b 2 c -a f() + g("foo") + h()=15 b -a f() + g("foo") + h()=0000000f b -a 1,=(1,) b -a x,y,=(1, 2) b -a x,1=(1, 1) b diff --git a/tests/basics/subclass_native_init.py b/tests/basics/subclass_native_init.py index 64167fa037e0c..102befd551fad 100644 --- a/tests/basics/subclass_native_init.py +++ b/tests/basics/subclass_native_init.py @@ -1,5 +1,9 @@ # test subclassing a native type and overriding __init__ +if not hasattr(object, "__init__"): + print("SKIP") + raise SystemExit + # overriding list.__init__() class L(list): def __init__(self, a, b): diff --git a/tests/basics/syntaxerror.py b/tests/basics/syntaxerror.py index c0702cb24525e..7b6ef9b7550ea 100644 --- a/tests/basics/syntaxerror.py +++ b/tests/basics/syntaxerror.py @@ -86,7 +86,7 @@ def test_syntax(code): test_syntax("nonlocal a") test_syntax("await 1") -# error on uPy, warning on CPy +# error on MPy, warning on CPy #test_syntax("def f():\n a = 1\n global a") # default except must be last @@ -98,7 +98,7 @@ def test_syntax(code): # non-keyword after keyword test_syntax("f(a=1, 2)") -# doesn't error on uPy but should +# doesn't error on MPy but should #test_syntax("f(1, i for i in i)") # all elements of dict/set must be pairs or singles diff --git a/tests/basics/sys1.py b/tests/basics/sys1.py index 7f261aa96459d..02946abc220be 100644 --- a/tests/basics/sys1.py +++ b/tests/basics/sys1.py @@ -31,6 +31,12 @@ # Effectively skip subtests print(str) +if hasattr(sys.implementation, '_thread'): + print(sys.implementation._thread in ("GIL", "unsafe")) +else: + # Effectively skip subtests + print(True) + try: print(sys.intern('micropython') == 'micropython') has_intern = True diff --git a/tests/basics/sys_tracebacklimit.py b/tests/basics/sys_tracebacklimit.py index 3ae372b8d524e..6f575fde65404 100644 --- a/tests/basics/sys_tracebacklimit.py +++ b/tests/basics/sys_tracebacklimit.py @@ -30,7 +30,7 @@ def print_exc(e): if l.startswith(" File "): l = l.split('"') print(l[0], l[2]) - # uPy and CPy tracebacks differ in that CPy prints a source line for + # MPy and CPy tracebacks differ in that CPy prints a source line for # each traceback entry. In this case, we know that offending line # has 4-space indent, so filter it out. elif not l.startswith(" "): diff --git a/tests/basics/sys_tracebacklimit.py.native.exp b/tests/basics/sys_tracebacklimit.py.native.exp new file mode 100644 index 0000000000000..f9d30c058564b --- /dev/null +++ b/tests/basics/sys_tracebacklimit.py.native.exp @@ -0,0 +1,22 @@ +ValueError: value + +limit 4 +ValueError: value + +limit 3 +ValueError: value + +limit 2 +ValueError: value + +limit 1 +ValueError: value + +limit 0 +ValueError: value + +limit -1 +ValueError: value + +True +False diff --git a/tests/basics/try_finally_continue.py.exp b/tests/basics/try_finally_continue.py.exp deleted file mode 100644 index 2901997b1aa18..0000000000000 --- a/tests/basics/try_finally_continue.py.exp +++ /dev/null @@ -1,9 +0,0 @@ -4 0 -continue -4 1 -continue -4 2 -continue -4 3 -continue -None diff --git a/tests/basics/tuple1.py b/tests/basics/tuple1.py index 72bb3f01bff4a..70ae071898e5b 100644 --- a/tests/basics/tuple1.py +++ b/tests/basics/tuple1.py @@ -17,7 +17,7 @@ x += (10, 11, 12) print(x) -# construction of tuple from large iterator (tests implementation detail of uPy) +# construction of tuple from large iterator (tests implementation detail of MicroPython) print(tuple(range(20))) # unsupported unary operation diff --git a/tests/circuitpython-manual/audiobusio/i2s_sample_loop.py b/tests/circuitpython-manual/audiobusio/i2s_sample_loop.py index 75ed4c7ae6f30..9778ac2b82025 100644 --- a/tests/circuitpython-manual/audiobusio/i2s_sample_loop.py +++ b/tests/circuitpython-manual/audiobusio/i2s_sample_loop.py @@ -1,35 +1,73 @@ +# SPDX-FileCopyrightText: 2018 Kattni Rembor for Adafruit Industries +# +# SPDX-License-Identifier: MIT import audiocore -import audiobusio import board import digitalio import array +import struct import time import math -import rp2pio -import adafruit_pioasm - -time.sleep(10) trigger = digitalio.DigitalInOut(board.D4) trigger.switch_to_output(True) # Generate one period of sine wav. -length = 8000 // 440 +sample_names = [ + "mono unsigned 8 bit", + "stereo unsigned 8 bit", + "mono signed 8 bit", + "stereo signed 8 bit", + "mono unsigned 16 bit", + "stereo unsigned 16 bit", + "mono signed 16 bit", + "stereo signed 16 bit", +] +sample_config = { + "mono unsigned 8 bit": {"format": "B", "channel_count": 1}, + "stereo unsigned 8 bit": {"format": "B", "channel_count": 2}, + "mono signed 8 bit": {"format": "b", "channel_count": 1}, + "stereo signed 8 bit": {"format": "b", "channel_count": 2}, + "mono unsigned 16 bit": {"format": "H", "channel_count": 1}, + "stereo unsigned 16 bit": {"format": "H", "channel_count": 2}, + "mono signed 16 bit": {"format": "h", "channel_count": 1}, + "stereo signed 16 bit": {"format": "h", "channel_count": 2}, +} -# signed 16 bit -s16 = array.array("h", [0] * length) -for i in range(length): - s16[i] = int(math.sin(math.pi * 2 * i / length) * (2**15)) - print(s16[i]) +for sample_rate in [8000, 16000, 32000, 44100]: + print(f"{sample_rate / 1000} kHz") + length = sample_rate // 440 -sample = audiocore.RawSample(s16, sample_rate=8000) + samples = [] -dac = audiobusio.I2SOut(bit_clock=board.D10, word_select=board.D11, data=board.D12) + for name in sample_names: + config = sample_config[name] + format = config["format"] + channel_count = config["channel_count"] + length = sample_rate // 440 + values = [] + for i in range(length): + range = 2 ** (struct.calcsize(format) * 8 - 1) - 1 + value = int(math.sin(math.pi * 2 * i / length) * range) + if "unsigned" in name: + value += range + values.append(value) + if channel_count == 2: + values.append(value) + sample = audiocore.RawSample( + array.array(format, values), sample_rate=sample_rate, channel_count=channel_count + ) + samples.append(sample) -trigger.value = False -dac.play(sample, loop=True) -time.sleep(1) -dac.stop() -trigger.value = True + dac = board.I2S0() + for sample, name in zip(samples, sample_names): + print(" ", name) + trigger.value = False + dac.play(sample, loop=True) + time.sleep(1) + dac.stop() + time.sleep(0.1) + trigger.value = True + print() print("done") diff --git a/tests/circuitpython/deque_subclass.py b/tests/circuitpython/deque_subclass.py new file mode 100644 index 0000000000000..5708407a9b630 --- /dev/null +++ b/tests/circuitpython/deque_subclass.py @@ -0,0 +1,40 @@ +try: + from collections import deque +except ImportError: + print("SKIP") + raise SystemExit + + +class DequeSubclass(deque): + def __init__(self, values, maxlen): + super().__init__(values, maxlen) + + def pop(self): + print("pop") + return super().pop() + + def popleft(self): + print("popleft") + return super().popleft() + + def append(self, value): + print("append") + return super().append(value) + + def appendleft(self, value): + print("appendleft") + return super().appendleft(value) + + def extend(self, value): + print("extend") + return super().extend(value) + + +d = DequeSubclass([1, 2, 3], 10) +print(d.append(4)) +print(d.appendleft(0)) +print(d.pop()) +print(d.popleft()) +d.extend([6, 7]) +# calling list() tests iteration. +print(list(d)) diff --git a/tests/circuitpython/getenv.py b/tests/circuitpython/getenv.py index 37819dd6f5fa5..3647b562eed13 100644 --- a/tests/circuitpython/getenv.py +++ b/tests/circuitpython/getenv.py @@ -58,7 +58,7 @@ def ioctl(self, op, arg): b'key = """\n', b"key =\n", b'key="', - b"key = strings must be quoted\n", + b"key = this is an unquoted string\n", ] @@ -75,6 +75,7 @@ def run_test(key, content): run_test("key", b"") +# key12 does not exist for i in range(13): run_test(f"key{i}", content_good) @@ -82,11 +83,9 @@ def run_test(key, content): for i in range(13): run_test(f"key{i}", content_good) -run_test(f"K", b"K = 7\r\n") -print(getenv_int("K")) - # Test value without trailing newline run_test(f"noeol", b"noeol=3") +# These return None now for content in content_bad: run_test("key", content) diff --git a/tests/circuitpython/getenv.py.exp b/tests/circuitpython/getenv.py.exp index 67707eb67fd75..89d654403dcf9 100644 --- a/tests/circuitpython/getenv.py.exp +++ b/tests/circuitpython/getenv.py.exp @@ -1,6 +1,6 @@ key None key0 'hello world' -key1 7 +key1 '7' key2 '\n' key3 'Áx' key4 'Áx' @@ -9,11 +9,11 @@ key6 '\t\r\x08' key7 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' key8 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' key9 'hello comment' -key10 127 -key11 0 +key10 '0x7f' +key11 '0' key12 None key0 'hello world' -key1 7 +key1 '7' key2 '\n' key3 'Áx' key4 'Áx' @@ -22,14 +22,12 @@ key6 '\t\r\x08' key7 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' key8 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' key9 'hello comment' -key10 127 -key11 0 +key10 '0x7f' +key11 '0' key12 None -K 7 -7 -noeol 3 -key Invalid byte '\n' -key Invalid byte '"' -key invalid syntax for integer with base 10: '' -key Invalid byte 'EOF' -key invalid syntax for integer with base 10: 'strings must be quoted' +noeol '3' +key None +key None +key None +key None +key 'this is an unquoted string' diff --git a/tests/cmdline/cmd_compile_only.py b/tests/cmdline/cmd_compile_only.py new file mode 100644 index 0000000000000..89964c1b5bdf2 --- /dev/null +++ b/tests/cmdline/cmd_compile_only.py @@ -0,0 +1,13 @@ +# cmdline: -X compile-only +# test compile-only functionality +print("This should not be printed") +x = 1 + 2 + + +def hello(): + return "world" + + +class TestClass: + def __init__(self): + self.value = 42 diff --git a/tests/cmdline/cmd_compile_only.py.exp b/tests/cmdline/cmd_compile_only.py.exp new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/cmdline/cmd_compile_only.py.exp @@ -0,0 +1 @@ + diff --git a/tests/cmdline/cmd_compile_only_error.py b/tests/cmdline/cmd_compile_only_error.py new file mode 100644 index 0000000000000..326937a5c07ef --- /dev/null +++ b/tests/cmdline/cmd_compile_only_error.py @@ -0,0 +1,6 @@ +# cmdline: -X compile-only +# test compile-only with syntax error +print("This should not be printed") +def broken_syntax( + # Missing closing parenthesis + return "error" diff --git a/tests/cmdline/cmd_compile_only_error.py.exp b/tests/cmdline/cmd_compile_only_error.py.exp new file mode 100644 index 0000000000000..3911f71d6244d --- /dev/null +++ b/tests/cmdline/cmd_compile_only_error.py.exp @@ -0,0 +1 @@ +CRASH \ No newline at end of file diff --git a/tests/cmdline/cmd_file_variable.py b/tests/cmdline/cmd_file_variable.py new file mode 100644 index 0000000000000..6cac6744d904e --- /dev/null +++ b/tests/cmdline/cmd_file_variable.py @@ -0,0 +1,5 @@ +# Test that __file__ is set correctly for script execution +try: + print("__file__ =", __file__) +except NameError: + print("__file__ not defined") diff --git a/tests/cmdline/cmd_file_variable.py.exp b/tests/cmdline/cmd_file_variable.py.exp new file mode 100644 index 0000000000000..6807569f662b5 --- /dev/null +++ b/tests/cmdline/cmd_file_variable.py.exp @@ -0,0 +1 @@ +__file__ = cmdline/cmd_file_variable.py diff --git a/tests/cmdline/cmd_module_atexit.py b/tests/cmdline/cmd_module_atexit.py new file mode 100644 index 0000000000000..100bc112777e1 --- /dev/null +++ b/tests/cmdline/cmd_module_atexit.py @@ -0,0 +1,16 @@ +# cmdline: -m cmdline.cmd_module_atexit +# +# Test running as a module and using sys.atexit. + +import sys + +if not hasattr(sys, "atexit"): + print("SKIP") + raise SystemExit + +# Verify we ran as a module. +print(sys.argv) + +sys.atexit(lambda: print("done")) + +print("start") diff --git a/tests/cmdline/cmd_module_atexit.py.exp b/tests/cmdline/cmd_module_atexit.py.exp new file mode 100644 index 0000000000000..2a0f756b1e799 --- /dev/null +++ b/tests/cmdline/cmd_module_atexit.py.exp @@ -0,0 +1,3 @@ +['cmdline.cmd_module_atexit', 'cmdline/cmd_module_atexit.py'] +start +done diff --git a/tests/cmdline/cmd_module_atexit_exc.py b/tests/cmdline/cmd_module_atexit_exc.py new file mode 100644 index 0000000000000..88940a7741fb9 --- /dev/null +++ b/tests/cmdline/cmd_module_atexit_exc.py @@ -0,0 +1,19 @@ +# cmdline: -m cmdline.cmd_module_atexit_exc +# +# Test running as a module and using sys.atexit, with script completion via sys.exit. + +import sys + +if not hasattr(sys, "atexit"): + print("SKIP") + raise SystemExit + +# Verify we ran as a module. +print(sys.argv) + +sys.atexit(lambda: print("done")) + +print("start") + +# This will raise SystemExit to finish the script, and atexit should still be run. +sys.exit(0) diff --git a/tests/cmdline/cmd_module_atexit_exc.py.exp b/tests/cmdline/cmd_module_atexit_exc.py.exp new file mode 100644 index 0000000000000..6320d9d2d3011 --- /dev/null +++ b/tests/cmdline/cmd_module_atexit_exc.py.exp @@ -0,0 +1,3 @@ +['cmdline.cmd_module_atexit_exc', 'cmdline/cmd_module_atexit_exc.py'] +start +done diff --git a/tests/cmdline/cmd_sys_exit_0.py b/tests/cmdline/cmd_sys_exit_0.py new file mode 100644 index 0000000000000..1294b739e8ff1 --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_0.py @@ -0,0 +1,5 @@ +# cmdline: +# test sys.exit(0) - success exit code +import sys + +sys.exit(0) diff --git a/tests/cmdline/cmd_sys_exit_0.py.exp b/tests/cmdline/cmd_sys_exit_0.py.exp new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_0.py.exp @@ -0,0 +1 @@ + diff --git a/tests/cmdline/cmd_sys_exit_error.py b/tests/cmdline/cmd_sys_exit_error.py new file mode 100644 index 0000000000000..ecac15e94f1bf --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_error.py @@ -0,0 +1,5 @@ +# cmdline: +# test sys.exit() functionality and exit codes +import sys + +sys.exit(123) diff --git a/tests/cmdline/cmd_sys_exit_error.py.exp b/tests/cmdline/cmd_sys_exit_error.py.exp new file mode 100644 index 0000000000000..3911f71d6244d --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_error.py.exp @@ -0,0 +1 @@ +CRASH \ No newline at end of file diff --git a/tests/cmdline/cmd_sys_exit_none.py b/tests/cmdline/cmd_sys_exit_none.py new file mode 100644 index 0000000000000..66e19666589ed --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_none.py @@ -0,0 +1,5 @@ +# cmdline: +# test sys.exit(None) - should exit with code 0 +import sys + +sys.exit(None) diff --git a/tests/cmdline/cmd_sys_exit_none.py.exp b/tests/cmdline/cmd_sys_exit_none.py.exp new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/tests/cmdline/cmd_sys_exit_none.py.exp @@ -0,0 +1 @@ + diff --git a/tests/cmdline/repl_autocomplete.py.exp b/tests/cmdline/repl_autocomplete.py.exp index 2e2397bb028d3..7840e52e8a4a2 100644 --- a/tests/cmdline/repl_autocomplete.py.exp +++ b/tests/cmdline/repl_autocomplete.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # tests for autocompletion >>> import sys >>> not_exist. @@ -11,4 +11,4 @@ Use \.\+ >>> i.lower('ABC') 'abc' >>> None. ->>> +>>> \$ diff --git a/tests/cmdline/repl_autocomplete_underscore.py b/tests/cmdline/repl_autocomplete_underscore.py new file mode 100644 index 0000000000000..e685a7fe7ff02 --- /dev/null +++ b/tests/cmdline/repl_autocomplete_underscore.py @@ -0,0 +1,33 @@ +# Test REPL autocompletion filtering of underscore attributes + +# Start paste mode +{\x05} +class TestClass: + def __init__(self): + self.public_attr = 1 + self._private_attr = 2 + self.__very_private = 3 + + def public_method(self): + pass + + def _private_method(self): + pass + + @property + def public_property(self): + return 42 + + @property + def _private_property(self): + return 99 + +{\x04} +# Paste executed + +# Create an instance +obj = TestClass() + +# Test tab completion on the instance +# The tab character after `obj.` and 'a' below triggers the completions +obj.{\x09}{\x09}a{\x09} diff --git a/tests/cmdline/repl_autocomplete_underscore.py.exp b/tests/cmdline/repl_autocomplete_underscore.py.exp new file mode 100644 index 0000000000000..98e6c2aeb05a3 --- /dev/null +++ b/tests/cmdline/repl_autocomplete_underscore.py.exp @@ -0,0 +1,41 @@ + +Adafruit CircuitPython \.\+ version +>>> # Test REPL autocompletion filtering of underscore attributes +>>> \$ +>>> # Start paste mode +>>> \$ +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== \$ +=== class TestClass: +=== def __init__(self): +=== self.public_attr = 1 +=== self._private_attr = 2 +=== self.__very_private = 3 +=== \$ +=== def public_method(self): +=== pass +=== \$ +=== def _private_method(self): +=== pass +=== \$ +=== @property +=== def public_property(self): +=== return 42 +=== \$ +=== @property +=== def _private_property(self): +=== return 99 +=== \$ +=== \$ +>>> # Paste executed +>>> \$ +>>> # Create an instance +>>> obj = TestClass() +>>> \$ +>>> # Test tab completion on the instance +>>> # The tab character after `obj.` and 'a' below triggers the completions +>>> obj.public_ +public_attr public_method public_property +>>> obj.public_attr +1 +>>> \$ diff --git a/tests/cmdline/repl_autoindent.py.exp b/tests/cmdline/repl_autoindent.py.exp index 9ff83a92870e8..04f00686b208d 100644 --- a/tests/cmdline/repl_autoindent.py.exp +++ b/tests/cmdline/repl_autoindent.py.exp @@ -1,22 +1,22 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # tests for autoindent >>> if 1: ... print(1) -... -... -... +... \$ +... \$ +... \$ 1 >>> if 0: ...  print(2) ... else: ... print(3) -... +... \$ 3 >>> if 0: ... print(4) ... else: ... print(5) -... +... \$ 5 ->>> +>>> \$ diff --git a/tests/cmdline/repl_basic.py.exp b/tests/cmdline/repl_basic.py.exp index 26442b6445589..5bdcc9d6d3246 100644 --- a/tests/cmdline/repl_basic.py.exp +++ b/tests/cmdline/repl_basic.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # basic REPL tests >>> print(1) 1 @@ -7,4 +7,4 @@ Use \.\+ 1 >>> 2 2 ->>> +>>> \$ diff --git a/tests/cmdline/repl_cont.py.exp b/tests/cmdline/repl_cont.py.exp index 6eed4a3e02c47..41f2436ac18a0 100644 --- a/tests/cmdline/repl_cont.py.exp +++ b/tests/cmdline/repl_cont.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # check REPL allows to continue input >>> 1 \\\\ ... + 2 @@ -54,4 +54,4 @@ two >>> if1 = 2 >>> print(if1) 2 ->>> +>>> \$ diff --git a/tests/cmdline/repl_emacs_keys.py.exp b/tests/cmdline/repl_emacs_keys.py.exp index 2e8667a8e6ca9..4979f8bbfc3ab 100644 --- a/tests/cmdline/repl_emacs_keys.py.exp +++ b/tests/cmdline/repl_emacs_keys.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # REPL tests of GNU-ish readline navigation >>> # history buffer navigation >>> 1 @@ -16,4 +16,4 @@ Use \.\+ >>> t = 121 >>> \.\+ 'foobar' ->>> +>>> \$ diff --git a/tests/cmdline/repl_inspect.py.exp b/tests/cmdline/repl_inspect.py.exp index 8ece5ffc37f5c..30257f31aab93 100644 --- a/tests/cmdline/repl_inspect.py.exp +++ b/tests/cmdline/repl_inspect.py.exp @@ -1,6 +1,6 @@ test -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # cmdline: -i -c print("test") >>> # -c option combined with -i option results in REPL ->>> +>>> \$ diff --git a/tests/cmdline/repl_lock.py b/tests/cmdline/repl_lock.py new file mode 100644 index 0000000000000..77e2e9327401a --- /dev/null +++ b/tests/cmdline/repl_lock.py @@ -0,0 +1,7 @@ +import micropython +micropython.heap_lock() +1+1 +micropython.heap_lock() +None # Cause the repl's line storage to be enlarged ---------------- +micropython.heap_lock() +raise SystemExit diff --git a/tests/cmdline/repl_lock.py.exp b/tests/cmdline/repl_lock.py.exp new file mode 100644 index 0000000000000..e6d63fcf20044 --- /dev/null +++ b/tests/cmdline/repl_lock.py.exp @@ -0,0 +1,10 @@ + +Adafruit CircuitPython \.\+ version +>>> import micropython +>>> micropython.heap_lock() +>>> 1+1 +2 +>>> micropython.heap_lock() +>>> None # Cause the repl's line storage to be enlarged ---------------- +>>> micropython.heap_lock() +>>> raise SystemExit diff --git a/tests/cmdline/repl_micropyinspect.py.exp b/tests/cmdline/repl_micropyinspect.py.exp index 3c9cbc030c0b5..36bef37c62c6b 100644 --- a/tests/cmdline/repl_micropyinspect.py.exp +++ b/tests/cmdline/repl_micropyinspect.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # cmdline: cmdline/repl_micropyinspect >>> # setting MICROPYINSPECT environment variable before program exit triggers REPL ->>> +>>> \$ diff --git a/tests/cmdline/repl_paste.py b/tests/cmdline/repl_paste.py new file mode 100644 index 0000000000000..7cec450fce7bf --- /dev/null +++ b/tests/cmdline/repl_paste.py @@ -0,0 +1,90 @@ +# Test REPL paste mode functionality + +# Basic paste mode with a simple function +{\x05} +def hello(): + print('Hello from paste mode!') +hello() +{\x04} + +# Paste mode with multiple indentation levels +{\x05} +def calculate(n): + if n > 0: + for i in range(n): + if i % 2 == 0: + print(f'Even: {i}') + else: + print(f'Odd: {i}') + else: + print('n must be positive') + +calculate(5) +{\x04} + +# Paste mode with blank lines +{\x05} +def function_with_blanks(): + print('First line') + + print('After blank line') + + + print('After two blank lines') + +function_with_blanks() +{\x04} + +# Paste mode with class definition and multiple methods +{\x05} +class TestClass: + def __init__(self, value): + self.value = value + + def display(self): + print(f'Value is: {self.value}') + + def double(self): + self.value *= 2 + return self.value + +obj = TestClass(21) +obj.display() +print(f'Doubled: {obj.double()}') +obj.display() +{\x04} + +# Paste mode with exception handling +{\x05} +try: + x = 1 / 0 +except ZeroDivisionError: + print('Caught division by zero') +finally: + print('Finally block executed') +{\x04} + +# Cancel paste mode with Ctrl-C +{\x05} +print('This should not execute') +{\x03} + +# Normal REPL still works after cancelled paste +print('Back to normal REPL') + +# Paste mode with syntax error +{\x05} +def bad_syntax(: + print('Missing parameter') +{\x04} + +# Paste mode with runtime error +{\x05} +def will_error(): + undefined_variable + +will_error() +{\x04} + +# Final test to show REPL is still functioning +1 + 2 + 3 diff --git a/tests/cmdline/repl_paste.py.exp b/tests/cmdline/repl_paste.py.exp new file mode 100644 index 0000000000000..cc5ac2f08007d --- /dev/null +++ b/tests/cmdline/repl_paste.py.exp @@ -0,0 +1,133 @@ + +Adafruit CircuitPython \.\+ version +>>> # Test REPL paste mode functionality +>>> \$ +>>> # Basic paste mode with a simple function +>>> \$ +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== \$ +=== def hello(): +=== print('Hello from paste mode!') +=== hello() +=== \$ +Hello from paste mode! +>>> \$ +>>> # Paste mode with multiple indentation levels +>>> \$ +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== \$ +=== def calculate(n): +=== if n > 0: +=== for i in range(n): +=== if i % 2 == 0: +=== print(f'Even: {i}') +=== else: +=== print(f'Odd: {i}') +=== else: +=== print('n must be positive') +=== \$ +=== calculate(5) +=== \$ +Even: 0 +Odd: 1 +Even: 2 +Odd: 3 +Even: 4 +>>> \$ +>>> # Paste mode with blank lines +>>> \$ +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== \$ +=== def function_with_blanks(): +=== print('First line') +=== \$ +=== print('After blank line') +=== \$ +=== \$ +=== print('After two blank lines') +=== \$ +=== function_with_blanks() +=== \$ +First line +After blank line +After two blank lines +>>> \$ +>>> # Paste mode with class definition and multiple methods +>>> \$ +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== \$ +=== class TestClass: +=== def __init__(self, value): +=== self.value = value +=== \$ +=== def display(self): +=== print(f'Value is: {self.value}') +=== \$ +=== def double(self): +=== self.value *= 2 +=== return self.value +=== \$ +=== obj = TestClass(21) +=== obj.display() +=== print(f'Doubled: {obj.double()}') +=== obj.display() +=== \$ +Value is: 21 +Doubled: 42 +Value is: 42 +>>> \$ +>>> # Paste mode with exception handling +>>> \$ +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== \$ +=== try: +=== x = 1 / 0 +=== except ZeroDivisionError: +=== print('Caught division by zero') +=== finally: +=== print('Finally block executed') +=== \$ +Caught division by zero +Finally block executed +>>> \$ +>>> # Cancel paste mode with Ctrl-C +>>> \$ +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== \$ +=== print('This should not execute') +=== \$ +>>> \$ +>>> \$ +>>> # Normal REPL still works after cancelled paste +>>> print('Back to normal REPL') +Back to normal REPL +>>> \$ +>>> # Paste mode with syntax error +>>> \$ +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== \$ +=== def bad_syntax(: +=== print('Missing parameter') +=== \$ +Traceback (most recent call last): + File "", line 2 +SyntaxError: invalid syntax +>>> \$ +>>> # Paste mode with runtime error +>>> \$ +paste mode; Ctrl-C to cancel, Ctrl-D to finish +=== \$ +=== def will_error(): +=== undefined_variable +=== \$ +=== will_error() +=== \$ +Traceback (most recent call last): + File "", line 5, in + File "", line 3, in will_error +NameError: name 'undefined_variable' isn't defined +>>> \$ +>>> # Final test to show REPL is still functioning +>>> 1 + 2 + 3 +6 +>>> \$ diff --git a/tests/cmdline/repl_sys_ps1_ps2.py.exp b/tests/cmdline/repl_sys_ps1_ps2.py.exp index 452a54fe5ae9b..d4bcf7a44d592 100644 --- a/tests/cmdline/repl_sys_ps1_ps2.py.exp +++ b/tests/cmdline/repl_sys_ps1_ps2.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # test changing ps1/ps2 >>> import sys >>> sys.ps1 = "PS1" diff --git a/tests/cmdline/repl_words_move.py.exp b/tests/cmdline/repl_words_move.py.exp index ba5c3648cff98..41ad3d8e73351 100644 --- a/tests/cmdline/repl_words_move.py.exp +++ b/tests/cmdline/repl_words_move.py.exp @@ -1,5 +1,5 @@ -CircuitPython \.\+ version -Use \.\+ + +Adafruit CircuitPython \.\+ version >>> # word movement >>> # backward-word, start in word >>> \.\+ @@ -19,7 +19,7 @@ Use \.\+ >>> # forward-word on eol. if cursor is moved, this will result in a SyntaxError >>> \.\+ 6 ->>> +>>> \$ >>> # kill word >>> # backward-kill-word, start in word >>> \.\+ @@ -33,7 +33,7 @@ Use \.\+ >>> # forward-kill-word, don't start in word >>> \.\+ 3 ->>> +>>> \$ >>> # extra move/kill shortcuts >>> # ctrl-left >>> \.\+ @@ -44,4 +44,4 @@ Use \.\+ >>> # ctrl-w >>> \.\+ 1 ->>> +>>> \$ diff --git a/tests/cpydiff/core_class_initsubclass.py b/tests/cpydiff/core_class_initsubclass.py new file mode 100644 index 0000000000000..8683271dcb0f2 --- /dev/null +++ b/tests/cpydiff/core_class_initsubclass.py @@ -0,0 +1,21 @@ +""" +categories: Core,Classes +description: ``__init_subclass__`` isn't automatically called. +cause: MicroPython does not currently implement PEP 487. +workaround: Manually call ``__init_subclass__`` after class creation if needed. e.g.:: + + class A(Base): + pass + A.__init_subclass__() + +""" + + +class Base: + @classmethod + def __init_subclass__(cls): + print(f"Base.__init_subclass__({cls.__name__})") + + +class A(Base): + pass diff --git a/tests/cpydiff/core_class_initsubclass_autoclassmethod.py b/tests/cpydiff/core_class_initsubclass_autoclassmethod.py new file mode 100644 index 0000000000000..b2f7628976c92 --- /dev/null +++ b/tests/cpydiff/core_class_initsubclass_autoclassmethod.py @@ -0,0 +1,31 @@ +""" +categories: Core,Classes +description: ``__init_subclass__`` isn't an implicit classmethod. +cause: MicroPython does not currently implement PEP 487. ``__init_subclass__`` is not currently in the list of special-cased class/static methods. +workaround: Decorate declarations of ``__init_subclass__`` with ``@classmethod``. +""" + + +def regularize_spelling(text, prefix="bound_"): + # for regularizing across the CPython "method" vs MicroPython "bound_method" spelling for the type of a bound classmethod + if text.startswith(prefix): + return text[len(prefix) :] + return text + + +class A: + def __init_subclass__(cls): + pass + + @classmethod + def manual_decorated(cls): + pass + + +a = type(A.__init_subclass__).__name__ +b = type(A.manual_decorated).__name__ + +print(regularize_spelling(a)) +print(regularize_spelling(b)) +if a != b: + print("FAIL") diff --git a/tests/cpydiff/core_class_initsubclass_kwargs.py b/tests/cpydiff/core_class_initsubclass_kwargs.py new file mode 100644 index 0000000000000..ed5157afeaede --- /dev/null +++ b/tests/cpydiff/core_class_initsubclass_kwargs.py @@ -0,0 +1,22 @@ +""" +categories: Core,Classes +description: MicroPython doesn't support parameterized ``__init_subclass__`` class customization. +cause: MicroPython does not currently implement PEP 487. The MicroPython syntax tree does not include a kwargs node after the class inheritance list. +workaround: Use class variables or another mechanism to specify base-class customizations. +""" + + +class Base: + @classmethod + def __init_subclass__(cls, arg=None, **kwargs): + cls.init_subclass_was_called = True + print(f"Base.__init_subclass__({cls.__name__}, {arg=!r}, {kwargs=!r})") + + +class A(Base, arg="arg"): + pass + + +# Regularize across MicroPython not automatically calling __init_subclass__ either. +if not getattr(A, "init_subclass_was_called", False): + A.__init_subclass__() diff --git a/tests/cpydiff/core_class_initsubclass_super.py b/tests/cpydiff/core_class_initsubclass_super.py new file mode 100644 index 0000000000000..d18671a74bf8e --- /dev/null +++ b/tests/cpydiff/core_class_initsubclass_super.py @@ -0,0 +1,22 @@ +""" +categories: Core,Classes +description: ``__init_subclass__`` can't be defined a cooperatively-recursive way. +cause: MicroPython does not currently implement PEP 487. The base object type does not have an ``__init_subclass__`` implementation. +workaround: Omit the recursive ``__init_subclass__`` call unless it's known that the grandparent also defines it. +""" + + +class Base: + @classmethod + def __init_subclass__(cls, **kwargs): + cls.init_subclass_was_called = True + super().__init_subclass__(**kwargs) + + +class A(Base): + pass + + +# Regularize across MicroPython not automatically calling __init_subclass__ either. +if not getattr(A, "init_subclass_was_called", False): + A.__init_subclass__() diff --git a/tests/cpydiff/core_fstring_concat.py b/tests/cpydiff/core_fstring_concat.py index 3daa13d75360e..2fbe1b961a1e6 100644 --- a/tests/cpydiff/core_fstring_concat.py +++ b/tests/cpydiff/core_fstring_concat.py @@ -1,5 +1,5 @@ """ -categories: Core +categories: Core,f-strings description: f-strings don't support concatenation with adjacent literals if the adjacent literals contain braces cause: MicroPython is optimised for code space. workaround: Use the + operator between literal strings when they are not both f-strings diff --git a/tests/cpydiff/core_fstring_parser.py b/tests/cpydiff/core_fstring_parser.py index 87cf1e63ed8c2..4964b9707aceb 100644 --- a/tests/cpydiff/core_fstring_parser.py +++ b/tests/cpydiff/core_fstring_parser.py @@ -1,5 +1,5 @@ """ -categories: Core +categories: Core,f-strings description: f-strings cannot support expressions that require parsing to resolve unbalanced nested braces and brackets cause: MicroPython is optimised for code space. workaround: Always use balanced braces and brackets in expressions inside f-strings diff --git a/tests/cpydiff/core_fstring_repr.py b/tests/cpydiff/core_fstring_repr.py index d37fb48db758c..2589a34b7e3b9 100644 --- a/tests/cpydiff/core_fstring_repr.py +++ b/tests/cpydiff/core_fstring_repr.py @@ -1,5 +1,5 @@ """ -categories: Core +categories: Core,f-strings description: f-strings don't support !a conversions cause: MicropPython does not implement ascii() workaround: None diff --git a/tests/cpydiff/core_import_all.py b/tests/cpydiff/core_import_all.py deleted file mode 100644 index 0fbe9d4d4ecd6..0000000000000 --- a/tests/cpydiff/core_import_all.py +++ /dev/null @@ -1,10 +0,0 @@ -""" -categories: Core,import -description: __all__ is unsupported in __init__.py in MicroPython. -cause: Not implemented. -workaround: Manually import the sub-modules directly in __init__.py using ``from . import foo, bar``. -""" - -from modules3 import * - -foo.hello() diff --git a/tests/cpydiff/modules3/__init__.py b/tests/cpydiff/modules3/__init__.py deleted file mode 100644 index 27a2bf2ad9044..0000000000000 --- a/tests/cpydiff/modules3/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__all__ = ["foo"] diff --git a/tests/cpydiff/modules3/foo.py b/tests/cpydiff/modules3/foo.py deleted file mode 100644 index dd9b9d4ddd4c4..0000000000000 --- a/tests/cpydiff/modules3/foo.py +++ /dev/null @@ -1,2 +0,0 @@ -def hello(): - print("hello") diff --git a/tests/cpydiff/modules_errno_enotsup.py b/tests/cpydiff/modules_errno_enotsup.py new file mode 100644 index 0000000000000..80e5ad9d03220 --- /dev/null +++ b/tests/cpydiff/modules_errno_enotsup.py @@ -0,0 +1,10 @@ +""" +categories: Modules,errno +description: MicroPython does not include ``ENOTSUP`` as a name for errno 95. +cause: MicroPython does not implement the ``ENOTSUP`` canonical alias for ``EOPNOTSUPP`` added in CPython 3.2. +workaround: Use ``errno.EOPNOTSUPP`` in place of ``errno.ENOTSUP``. +""" + +import errno + +print(f"{errno.errorcode[errno.EOPNOTSUPP]=!s}") diff --git a/tests/cpydiff/modules_struct_fewargs.py b/tests/cpydiff/modules_struct_fewargs.py index 49b2a3213c898..f6346a67938ed 100644 --- a/tests/cpydiff/modules_struct_fewargs.py +++ b/tests/cpydiff/modules_struct_fewargs.py @@ -1,6 +1,6 @@ """ categories: Modules,struct -description: Struct pack with too few args, not checked by uPy +description: Struct pack with too few args, not checked by MicroPython cause: Unknown workaround: Unknown """ diff --git a/tests/cpydiff/modules_struct_manyargs.py b/tests/cpydiff/modules_struct_manyargs.py index e3b78930f219f..b2ea93b6c9330 100644 --- a/tests/cpydiff/modules_struct_manyargs.py +++ b/tests/cpydiff/modules_struct_manyargs.py @@ -1,6 +1,6 @@ """ categories: Modules,struct -description: Struct pack with too many args, not checked by uPy +description: Struct pack with too many args, not checked by MicroPython cause: Unknown workaround: Unknown """ diff --git a/tests/cpydiff/modules_struct_whitespace_in_format.py b/tests/cpydiff/modules_struct_whitespace_in_format.py index a7a1d2facdfff..8b609425eb01c 100644 --- a/tests/cpydiff/modules_struct_whitespace_in_format.py +++ b/tests/cpydiff/modules_struct_whitespace_in_format.py @@ -1,6 +1,6 @@ """ categories: Modules,struct -description: Struct pack with whitespace in format, whitespace ignored by CPython, error on uPy +description: Struct pack with whitespace in format, whitespace ignored by CPython, error on MicroPython cause: MicroPython is optimised for code size. workaround: Don't use spaces in format strings. """ diff --git a/tests/cpydiff/syntax_arg_unpacking.py b/tests/cpydiff/syntax_arg_unpacking.py index e54832ddb9165..7133a8a28272c 100644 --- a/tests/cpydiff/syntax_arg_unpacking.py +++ b/tests/cpydiff/syntax_arg_unpacking.py @@ -1,5 +1,5 @@ """ -categories: Syntax +categories: Syntax,Unpacking description: Argument unpacking does not work if the argument being unpacked is the nth or greater argument where n is the number of bits in an MP_SMALL_INT. cause: The implementation uses an MP_SMALL_INT to flag args that need to be unpacked. workaround: Use fewer arguments. diff --git a/tests/cpydiff/syntax_literal_underscore.py b/tests/cpydiff/syntax_literal_underscore.py new file mode 100644 index 0000000000000..4b1406e9f3f71 --- /dev/null +++ b/tests/cpydiff/syntax_literal_underscore.py @@ -0,0 +1,19 @@ +""" +categories: Syntax,Literals +description: MicroPython accepts underscores in numeric literals where CPython doesn't +cause: Different parser implementation + +MicroPython's tokenizer ignores underscores in numeric literals, while CPython +rejects multiple consecutive underscores and underscores after the last digit. + +workaround: Remove the underscores not accepted by CPython. +""" + +try: + print(eval("1__1")) +except SyntaxError: + print("Should not work") +try: + print(eval("1_")) +except SyntaxError: + print("Should not work") diff --git a/tests/cpydiff/syntax_spaces.py b/tests/cpydiff/syntax_spaces.py index 03d25d56199d1..670cefdeac23e 100644 --- a/tests/cpydiff/syntax_spaces.py +++ b/tests/cpydiff/syntax_spaces.py @@ -1,8 +1,15 @@ """ -categories: Syntax,Spaces -description: uPy requires spaces between literal numbers and keywords, CPy doesn't -cause: Unknown -workaround: Unknown +categories: Syntax,Literals +description: MicroPython requires spaces between literal numbers and keywords or ".", CPython doesn't +cause: Different parser implementation + +MicroPython's tokenizer treats a sequence like ``1and`` as a single token, while CPython treats it as two tokens. + +Since CPython 3.11, when the literal number is followed by a token, this syntax causes a ``SyntaxWarning`` for an "invalid literal". When a literal number is followed by a "." denoting attribute access, CPython does not warn. + +workaround: Add a space between the integer literal and the intended next token. + +This also fixes the ``SyntaxWarning`` in CPython. """ try: @@ -17,3 +24,7 @@ print(eval("1if 1else 0")) except SyntaxError: print("Should have worked") +try: + print(eval("0x1.to_bytes(1)")) +except SyntaxError: + print("Should have worked") diff --git a/tests/cpydiff/types_complex_parser.py b/tests/cpydiff/types_complex_parser.py new file mode 100644 index 0000000000000..4a012987d9e48 --- /dev/null +++ b/tests/cpydiff/types_complex_parser.py @@ -0,0 +1,14 @@ +""" +categories: Types,complex +description: MicroPython's complex() accepts certain incorrect values that CPython rejects +cause: MicroPython is highly optimized for memory usage. +workaround: Do not use non-standard complex literals as argument to complex() + +MicroPython's ``complex()`` function accepts literals that contain a space and +no sign between the real and imaginary parts, and interprets it as a plus. +""" + +try: + print(complex("1 1j")) +except ValueError: + print("ValueError") diff --git a/tests/cpydiff/types_float_implicit_conversion.py b/tests/cpydiff/types_float_implicit_conversion.py index 3726839fac6d1..764c9e4e6ed7a 100644 --- a/tests/cpydiff/types_float_implicit_conversion.py +++ b/tests/cpydiff/types_float_implicit_conversion.py @@ -1,6 +1,6 @@ """ categories: Types,float -description: uPy allows implicit conversion of objects in maths operations while CPython does not. +description: MicroPython allows implicit conversion of objects in maths operations while CPython does not. cause: Unknown workaround: Objects should be wrapped in ``float(obj)`` for compatibility with CPython. """ diff --git a/tests/cpydiff/types_float_rounding.py b/tests/cpydiff/types_float_rounding.py deleted file mode 100644 index 206e359ed9be7..0000000000000 --- a/tests/cpydiff/types_float_rounding.py +++ /dev/null @@ -1,8 +0,0 @@ -""" -categories: Types,float -description: uPy and CPython outputs formats may differ -cause: Unknown -workaround: Unknown -""" - -print("%.1g" % -9.9) diff --git a/tests/cpydiff/types_oserror_errnomap.py b/tests/cpydiff/types_oserror_errnomap.py new file mode 100644 index 0000000000000..6627bd2af4ab7 --- /dev/null +++ b/tests/cpydiff/types_oserror_errnomap.py @@ -0,0 +1,48 @@ +""" +categories: Types,OSError +description: OSError constructor returns a plain OSError for all errno values, rather than a relevant subtype. +cause: MicroPython does not include the CPython-standard OSError subclasses. +workaround: Catch OSError and use its errno attribute to discriminate the cause. +""" + +import errno + +errno_list = [ # i.e. the set implemented by micropython + errno.EPERM, + errno.ENOENT, + errno.EIO, + errno.EBADF, + errno.EAGAIN, + errno.ENOMEM, + errno.EACCES, + errno.EEXIST, + errno.ENODEV, + errno.EISDIR, + errno.EINVAL, + errno.EOPNOTSUPP, + errno.EADDRINUSE, + errno.ECONNABORTED, + errno.ECONNRESET, + errno.ENOBUFS, + errno.ENOTCONN, + errno.ETIMEDOUT, + errno.ECONNREFUSED, + errno.EHOSTUNREACH, + errno.EALREADY, + errno.EINPROGRESS, +] + + +def errno_output_type(n): + try: + raise OSError(n, "") + except OSError as e: + return f"{type(e).__name__}" + except Exception as e: + return f"non-OSError {type(e).__name__}" + else: + return "no error" + + +for n in errno_list: + print(errno.errorcode[n], "=", errno_output_type(n)) diff --git a/tests/cpydiff/types_range_limits.py b/tests/cpydiff/types_range_limits.py new file mode 100644 index 0000000000000..e53d5fd4088f3 --- /dev/null +++ b/tests/cpydiff/types_range_limits.py @@ -0,0 +1,26 @@ +""" +categories: Types,range +description: Range objects with large start or stop arguments misbehave. +cause: Intermediate calculations overflow the C mp_int_t type +workaround: Avoid using such ranges +""" + +from sys import maxsize + +# A range including `maxsize-1` cannot be created +try: + print(range(-maxsize - 1, 0)) +except OverflowError: + print("OverflowError") + +# A range with `stop-start` exceeding sys.maxsize has incorrect len(), while CPython cannot calculate len(). +try: + print(len(range(-maxsize, maxsize))) +except OverflowError: + print("OverflowError") + +# A range with `stop-start` exceeding sys.maxsize has incorrect len() +try: + print(len(range(-maxsize, maxsize, maxsize))) +except OverflowError: + print("OverflowError") diff --git a/tests/cpydiff/types_str_formatsep.py b/tests/cpydiff/types_str_formatsep.py new file mode 100644 index 0000000000000..05d0b8d3d2cf4 --- /dev/null +++ b/tests/cpydiff/types_str_formatsep.py @@ -0,0 +1,19 @@ +""" +categories: Types,str +description: MicroPython accepts the "," grouping option with any radix, unlike CPython +cause: To reduce code size, MicroPython does not issue an error for this combination +workaround: Do not use a format string like ``{:,b}`` if CPython compatibility is required. +""" + +try: + print("{:,b}".format(99)) +except ValueError: + print("ValueError") +try: + print("{:,x}".format(99)) +except ValueError: + print("ValueError") +try: + print("{:,o}".format(99)) +except ValueError: + print("ValueError") diff --git a/tests/cpydiff/types_str_formatsep_float.py b/tests/cpydiff/types_str_formatsep_float.py new file mode 100644 index 0000000000000..b487cd3758e73 --- /dev/null +++ b/tests/cpydiff/types_str_formatsep_float.py @@ -0,0 +1,11 @@ +""" +categories: Types,str +description: MicroPython accepts but does not properly implement the "," or "_" grouping character for float values +cause: To reduce code size, MicroPython does not implement this combination. Grouping characters will not appear in the number's significant digits and will appear at incorrect locations in leading zeros. +workaround: Do not use a format string like ``{:,f}`` if exact CPython compatibility is required. +""" + +print("{:,f}".format(3141.159)) +print("{:_f}".format(3141.159)) +print("{:011,.2f}".format(3141.159)) +print("{:011_.2f}".format(3141.159)) diff --git a/tests/extmod/asyncio_basic.py.exp b/tests/extmod/asyncio_basic.py.exp deleted file mode 100644 index 478e22abc8ff6..0000000000000 --- a/tests/extmod/asyncio_basic.py.exp +++ /dev/null @@ -1,6 +0,0 @@ -start -after sleep -short -long -negative -took 200 400 0 diff --git a/tests/extmod/asyncio_event_queue.py b/tests/extmod/asyncio_event_queue.py new file mode 100644 index 0000000000000..e0125b1aefe13 --- /dev/null +++ b/tests/extmod/asyncio_event_queue.py @@ -0,0 +1,64 @@ +# Ensure that an asyncio task can wait on an Event when the +# _task_queue is empty +# https://github.com/micropython/micropython/issues/16569 + +try: + import asyncio +except ImportError: + print("SKIP") + raise SystemExit + +# This test requires checking that the asyncio scheduler +# remains active "indefinitely" when the task queue is empty. +# +# To check this, we need another independent scheduler that +# can wait for a certain amount of time. So we have to +# create one using micropython.schedule() and time.ticks_ms() +# +# Technically, this code breaks the rules, as it is clearly +# documented that Event.set() should _NOT_ be called from a +# schedule (soft IRQ) because in some cases, a race condition +# can occur, resulting in a crash. However: +# - since the risk of a race condition in that specific +# case has been analysed and excluded +# - given that there is no other simple alternative to +# write this test case, +# an exception to the rule was deemed acceptable. See +# https://github.com/micropython/micropython/pull/16772 + +import micropython, time + +try: + micropython.schedule +except AttributeError: + print("SKIP") + raise SystemExit + + +evt = asyncio.Event() + + +def schedule_watchdog(end_ticks): + if time.ticks_diff(end_ticks, time.ticks_ms()) <= 0: + print("asyncio still pending, unlocking event") + # Caution: about to call Event.set() from a schedule + # (see the note in the comment above) + evt.set() + return + micropython.schedule(schedule_watchdog, end_ticks) + + +async def foo(): + print("foo waiting") + schedule_watchdog(time.ticks_add(time.ticks_ms(), 100)) + await evt.wait() + print("foo done") + + +async def main(): + print("main started") + await foo() + print("main done") + + +asyncio.run(main()) diff --git a/tests/extmod/asyncio_event_queue.py.exp b/tests/extmod/asyncio_event_queue.py.exp new file mode 100644 index 0000000000000..ee42c96d83ed8 --- /dev/null +++ b/tests/extmod/asyncio_event_queue.py.exp @@ -0,0 +1,5 @@ +main started +foo waiting +asyncio still pending, unlocking event +foo done +main done diff --git a/tests/extmod/asyncio_heaplock.py b/tests/extmod/asyncio_heaplock.py index 8326443f0e6c5..9e9908de1cb6a 100644 --- a/tests/extmod/asyncio_heaplock.py +++ b/tests/extmod/asyncio_heaplock.py @@ -4,7 +4,11 @@ # - StreamWriter.write, stream is blocked and data to write is a bytes object # - StreamWriter.write, when stream is not blocked -import micropython +try: + import asyncio, micropython +except ImportError: + print("SKIP") + raise SystemExit # strict stackless builds can't call functions without allocating a frame on the heap try: @@ -24,12 +28,6 @@ def f(x): print("SKIP") raise SystemExit -try: - import asyncio -except ImportError: - print("SKIP") - raise SystemExit - class TestStream: def __init__(self, blocked): diff --git a/tests/extmod/asyncio_iterator_event.py b/tests/extmod/asyncio_iterator_event.py new file mode 100644 index 0000000000000..f61fefcf051be --- /dev/null +++ b/tests/extmod/asyncio_iterator_event.py @@ -0,0 +1,86 @@ +# Ensure that an asyncio task can wait on an Event when the +# _task_queue is empty, in the context of an async iterator +# https://github.com/micropython/micropython/issues/16318 + +try: + import asyncio +except ImportError: + print("SKIP") + raise SystemExit + +# This test requires checking that the asyncio scheduler +# remains active "indefinitely" when the task queue is empty. +# +# To check this, we need another independent scheduler that +# can wait for a certain amount of time. So we have to +# create one using micropython.schedule() and time.ticks_ms() +# +# Technically, this code breaks the rules, as it is clearly +# documented that Event.set() should _NOT_ be called from a +# schedule (soft IRQ) because in some cases, a race condition +# can occur, resulting in a crash. However: +# - since the risk of a race condition in that specific +# case has been analysed and excluded +# - given that there is no other simple alternative to +# write this test case, +# an exception to the rule was deemed acceptable. See +# https://github.com/micropython/micropython/pull/16772 + +import micropython, time + +try: + micropython.schedule +except AttributeError: + print("SKIP") + raise SystemExit + +ai = None + + +def schedule_watchdog(end_ticks): + if time.ticks_diff(end_ticks, time.ticks_ms()) <= 0: + print("good: asyncio iterator is still pending, exiting") + # Caution: ai.fetch_data() will invoke Event.set() + # (see the note in the comment above) + ai.fetch_data(None) + return + micropython.schedule(schedule_watchdog, end_ticks) + + +async def test(ai): + for x in range(3): + await asyncio.sleep(0.1) + ai.fetch_data("bar {}".format(x)) + + +class AsyncIterable: + def __init__(self): + self.message = None + self.evt = asyncio.Event() + + def __aiter__(self): + return self + + async def __anext__(self): + await self.evt.wait() + self.evt.clear() + if self.message is None: + raise StopAsyncIteration + return self.message + + def fetch_data(self, message): + self.message = message + self.evt.set() + + +async def main(): + global ai + ai = AsyncIterable() + asyncio.create_task(test(ai)) + schedule_watchdog(time.ticks_add(time.ticks_ms(), 500)) + async for message in ai: + print(message) + print("end main") + + +asyncio.run(main()) diff --git a/tests/extmod/asyncio_iterator_event.py.exp b/tests/extmod/asyncio_iterator_event.py.exp new file mode 100644 index 0000000000000..a1893197d02ef --- /dev/null +++ b/tests/extmod/asyncio_iterator_event.py.exp @@ -0,0 +1,5 @@ +bar 0 +bar 1 +bar 2 +good: asyncio iterator is still pending, exiting +end main diff --git a/tests/extmod/asyncio_lock.py.exp b/tests/extmod/asyncio_lock.py.exp deleted file mode 100644 index a37dfcbd2e519..0000000000000 --- a/tests/extmod/asyncio_lock.py.exp +++ /dev/null @@ -1,41 +0,0 @@ -False -True -False -have lock ----- -task start 1 -task start 2 -task start 3 -task have 1 0 -task have 2 0 -task have 3 0 -task have 1 1 -task have 2 1 -task have 3 1 -task have 1 2 -task end 1 -task have 2 2 -task end 2 -task have 3 2 -task end 3 ----- -task have True -task release False -task have True -task release False -task have again -task have again ----- -task got 0 -task release 0 -task cancel 1 -task got 2 -task release 2 -False ----- -task got 0 -task cancel 1 -task release 0 -task got 2 -task cancel 2 -False diff --git a/tests/extmod/asyncio_set_exception_handler.py b/tests/extmod/asyncio_set_exception_handler.py index 5935f0f4ebeaa..0ac4a624224ce 100644 --- a/tests/extmod/asyncio_set_exception_handler.py +++ b/tests/extmod/asyncio_set_exception_handler.py @@ -12,7 +12,7 @@ def custom_handler(loop, context): async def task(i): - # Raise with 2 args so exception prints the same in uPy and CPython + # Raise with 2 args so exception prints the same in MicroPython and CPython raise ValueError(i, i + 1) diff --git a/tests/extmod/asyncio_wait_for_linked_task.py b/tests/extmod/asyncio_wait_for_linked_task.py new file mode 100644 index 0000000000000..4dda62d5476c7 --- /dev/null +++ b/tests/extmod/asyncio_wait_for_linked_task.py @@ -0,0 +1,66 @@ +# Test asyncio.wait_for, with dependent tasks +# https://github.com/micropython/micropython/issues/16759 + +try: + import asyncio +except ImportError: + print("SKIP") + raise SystemExit + + +# CPython 3.12 deprecated calling get_event_loop() when there is no current event +# loop, so to make this test run on CPython requires setting the event loop. +if hasattr(asyncio, "set_event_loop"): + asyncio.set_event_loop(asyncio.new_event_loop()) + + +class Worker: + def __init__(self): + self._eventLoop = None + self._tasks = [] + + def launchTask(self, asyncJob): + if self._eventLoop is None: + self._eventLoop = asyncio.get_event_loop() + return self._eventLoop.create_task(asyncJob) + + async def job(self, prerequisite, taskName): + if prerequisite: + await prerequisite + await asyncio.sleep(0.1) + print(taskName, "work completed") + + def planTasks(self): + self._tasks.append(self.launchTask(self.job(None, "task0"))) + self._tasks.append(self.launchTask(self.job(self._tasks[0], "task1"))) + self._tasks.append(self.launchTask(self.job(self._tasks[1], "task2"))) + + async def waitForTask(self, taskIdx): + return await self._tasks[taskIdx] + + def syncWaitForTask(self, taskIdx): + return self._eventLoop.run_until_complete(self._tasks[taskIdx]) + + +async def async_test(): + print("--- async test") + worker = Worker() + worker.planTasks() + await worker.waitForTask(0) + print("-> task0 done") + await worker.waitForTask(2) + print("-> task2 done") + + +def sync_test(): + print("--- sync test") + worker = Worker() + worker.planTasks() + worker.syncWaitForTask(0) + print("-> task0 done") + worker.syncWaitForTask(2) + print("-> task2 done") + + +asyncio.get_event_loop().run_until_complete(async_test()) +sync_test() diff --git a/tests/extmod/asyncio_wait_task.py.exp b/tests/extmod/asyncio_wait_task.py.exp deleted file mode 100644 index 514a434223315..0000000000000 --- a/tests/extmod/asyncio_wait_task.py.exp +++ /dev/null @@ -1,12 +0,0 @@ -start -task 1 -task 2 ----- -start -hello -world -took 200 200 -task_raise -ValueError -task_raise -ValueError diff --git a/tests/extmod/binascii_hexlify.py b/tests/extmod/binascii_hexlify.py index d06029aabaffb..ae90b67336586 100644 --- a/tests/extmod/binascii_hexlify.py +++ b/tests/extmod/binascii_hexlify.py @@ -1,5 +1,5 @@ try: - import binascii + from binascii import hexlify except ImportError: print("SKIP") raise SystemExit @@ -10,10 +10,10 @@ b"\x7f\x80\xff", b"1234ABCDabcd", ): - print(binascii.hexlify(x)) + print(hexlify(x)) # Two-argument version (now supported in CPython) -print(binascii.hexlify(b"123", ":")) +print(hexlify(b"123", ":")) # zero length buffer -print(binascii.hexlify(b"", b":")) +print(hexlify(b"", b":")) diff --git a/tests/extmod/binascii_unhexlify.py b/tests/extmod/binascii_unhexlify.py index 731b6a2bd4d10..b08704cba65b2 100644 --- a/tests/extmod/binascii_unhexlify.py +++ b/tests/extmod/binascii_unhexlify.py @@ -1,5 +1,5 @@ try: - import binascii + from binascii import unhexlify except ImportError: print("SKIP") raise SystemExit @@ -10,18 +10,18 @@ b"7f80ff", b"313233344142434461626364", ): - print(binascii.unhexlify(x)) + print(unhexlify(x)) # CIRCUITPY-CHANGE # Unicode strings can be decoded -print(binascii.unhexlify("313233344142434461626364")) +print(unhexlify("313233344142434461626364")) try: - a = binascii.unhexlify(b"0") # odd buffer length + a = unhexlify(b"0") # odd buffer length except ValueError: print("ValueError") try: - a = binascii.unhexlify(b"gg") # digit not hex + a = unhexlify(b"gg") # digit not hex except ValueError: print("ValueError") diff --git a/tests/extmod/framebuf_blit.py b/tests/extmod/framebuf_blit.py new file mode 100644 index 0000000000000..b1d98b330a838 --- /dev/null +++ b/tests/extmod/framebuf_blit.py @@ -0,0 +1,68 @@ +# Test FrameBuffer.blit method. + +try: + import framebuf +except ImportError: + print("SKIP") + raise SystemExit + + +def printbuf(): + print("--8<--") + for y in range(h): + for x in range(w): + print("%02x" % buf[(x + y * w)], end="") + print() + print("-->8--") + + +w = 5 +h = 4 +buf = bytearray(w * h) +fbuf = framebuf.FrameBuffer(buf, w, h, framebuf.GS8) + +fbuf2 = framebuf.FrameBuffer(bytearray(4), 2, 2, framebuf.GS8) +fbuf2.fill(0xFF) + +# Blit another FrameBuffer, at various locations. +for x, y in ((-1, -1), (0, 0), (1, 1), (4, 3)): + fbuf.fill(0) + fbuf.blit(fbuf2, x, y) + printbuf() + +# Blit a bytes object. +fbuf.fill(0) +image = (b"\x10\x11\x12\x13", 2, 2, framebuf.GS8) +fbuf.blit(image, 1, 1) +printbuf() + +# Blit a bytes object that has a stride. +fbuf.fill(0) +image = (b"\x20\x21\xff\x22\x23\xff", 2, 2, framebuf.GS8, 3) +fbuf.blit(image, 1, 1) +printbuf() + +# Blit a bytes object with a bytes palette. +fbuf.fill(0) +image = (b"\x00\x01\x01\x00", 2, 2, framebuf.GS8) +palette = (b"\xa1\xa2", 2, 1, framebuf.GS8) +fbuf.blit(image, 1, 1, -1, palette) +printbuf() + +# Not enough elements in the tuple. +try: + fbuf.blit((0, 0, 0), 0, 0) +except ValueError: + print("ValueError") + +# Too many elements in the tuple. +try: + fbuf.blit((0, 0, 0, 0, 0, 0), 0, 0) +except ValueError: + print("ValueError") + +# Bytes too small. +try: + fbuf.blit((b"", 1, 1, framebuf.GS8), 0, 0) +except ValueError: + print("ValueError") diff --git a/tests/extmod/framebuf_blit.py.exp b/tests/extmod/framebuf_blit.py.exp new file mode 100644 index 0000000000000..e340f1990c783 --- /dev/null +++ b/tests/extmod/framebuf_blit.py.exp @@ -0,0 +1,45 @@ +--8<-- +ff00000000 +0000000000 +0000000000 +0000000000 +-->8-- +--8<-- +ffff000000 +ffff000000 +0000000000 +0000000000 +-->8-- +--8<-- +0000000000 +00ffff0000 +00ffff0000 +0000000000 +-->8-- +--8<-- +0000000000 +0000000000 +0000000000 +00000000ff +-->8-- +--8<-- +0000000000 +0010110000 +0012130000 +0000000000 +-->8-- +--8<-- +0000000000 +0020210000 +0022230000 +0000000000 +-->8-- +--8<-- +0000000000 +00a1a20000 +00a2a10000 +0000000000 +-->8-- +ValueError +ValueError +ValueError diff --git a/tests/extmod/hashlib_md5.py b/tests/extmod/hashlib_md5.py index 5f925fc97d02e..ebbe9155e0493 100644 --- a/tests/extmod/hashlib_md5.py +++ b/tests/extmod/hashlib_md5.py @@ -1,8 +1,7 @@ try: import hashlib except ImportError: - # This is neither uPy, nor cPy, so must be uPy with - # hashlib module disabled. + # MicroPython with hashlib module disabled. print("SKIP") raise SystemExit diff --git a/tests/extmod/hashlib_sha1.py b/tests/extmod/hashlib_sha1.py index af23033a591f2..46ffb73fcbe01 100644 --- a/tests/extmod/hashlib_sha1.py +++ b/tests/extmod/hashlib_sha1.py @@ -1,8 +1,7 @@ try: import hashlib except ImportError: - # This is neither uPy, nor cPy, so must be uPy with - # hashlib module disabled. + # MicroPython with hashlib module disabled. print("SKIP") raise SystemExit diff --git a/tests/extmod/hashlib_sha256.py b/tests/extmod/hashlib_sha256.py index 95cd301d160d0..209fcb3987792 100644 --- a/tests/extmod/hashlib_sha256.py +++ b/tests/extmod/hashlib_sha256.py @@ -1,8 +1,7 @@ try: import hashlib except ImportError: - # This is neither uPy, nor cPy, so must be uPy with - # hashlib module disabled. + # MicroPython with hashlib module disabled. print("SKIP") raise SystemExit diff --git a/tests/extmod/json_dump.py b/tests/extmod/json_dump.py index 897d33cc81253..0beb4f5f85698 100644 --- a/tests/extmod/json_dump.py +++ b/tests/extmod/json_dump.py @@ -16,11 +16,11 @@ # dump to a small-int not allowed try: json.dump(123, 1) -except (AttributeError, OSError): # CPython and uPy have different errors +except (AttributeError, OSError): # CPython and MicroPython have different errors print("Exception") # dump to an object not allowed try: json.dump(123, {}) -except (AttributeError, OSError): # CPython and uPy have different errors +except (AttributeError, OSError): # CPython and MicroPython have different errors print("Exception") diff --git a/tests/extmod/json_dump_iobase.py b/tests/extmod/json_dump_iobase.py index 94d317b87968f..81105e36dccec 100644 --- a/tests/extmod/json_dump_iobase.py +++ b/tests/extmod/json_dump_iobase.py @@ -18,7 +18,7 @@ def __init__(self): def write(self, buf): if type(buf) == bytearray: - # uPy passes a bytearray, CPython passes a str + # MicroPython passes a bytearray, CPython passes a str buf = str(buf, "ascii") self.buf += buf return len(buf) diff --git a/tests/extmod/json_dump_separators.py b/tests/extmod/json_dump_separators.py index 4f8e56dceb536..ce39294820fa9 100644 --- a/tests/extmod/json_dump_separators.py +++ b/tests/extmod/json_dump_separators.py @@ -25,20 +25,20 @@ # dump to a small-int not allowed try: json.dump(123, 1, separators=sep) - except (AttributeError, OSError): # CPython and uPy have different errors + except (AttributeError, OSError): # CPython and MicroPython have different errors print("Exception") # dump to an object not allowed try: json.dump(123, {}, separators=sep) - except (AttributeError, OSError): # CPython and uPy have different errors + except (AttributeError, OSError): # CPython and MicroPython have different errors print("Exception") try: s = StringIO() json.dump(False, s, separators={"a": 1}) -except (TypeError, ValueError): # CPython and uPy have different errors +except (TypeError, ValueError): # CPython and MicroPython have different errors print("Exception") # invalid separator types diff --git a/tests/extmod/json_dumps_extra.py b/tests/extmod/json_dumps_extra.py index a410b0ee0ef63..70efc86645115 100644 --- a/tests/extmod/json_dumps_extra.py +++ b/tests/extmod/json_dumps_extra.py @@ -1,4 +1,4 @@ -# test uPy json behaviour that's not valid in CPy +# test MicroPython json behaviour that's not valid in CPy # CIRCUITPY-CHANGE: This behavior matches CPython print("SKIP") raise SystemExit diff --git a/tests/extmod/json_dumps_separators.py b/tests/extmod/json_dumps_separators.py index a3a9ec308f09d..0a95f489a08ec 100644 --- a/tests/extmod/json_dumps_separators.py +++ b/tests/extmod/json_dumps_separators.py @@ -39,7 +39,7 @@ try: json.dumps(False, separators={"a": 1}) -except (TypeError, ValueError): # CPython and uPy have different errors +except (TypeError, ValueError): # CPython and MicroPython have different errors print("Exception") # invalid separator types diff --git a/tests/extmod/json_loads.py b/tests/extmod/json_loads.py index f9073c121e2ef..092402d715d69 100644 --- a/tests/extmod/json_loads.py +++ b/tests/extmod/json_loads.py @@ -71,3 +71,27 @@ def my_print(o): my_print(json.loads("[null] a")) except ValueError: print("ValueError") + +# incomplete object declaration +try: + my_print(json.loads('{"a":0,')) +except ValueError: + print("ValueError") + +# incomplete nested array declaration +try: + my_print(json.loads('{"a":0, [')) +except ValueError: + print("ValueError") + +# incomplete array declaration +try: + my_print(json.loads("[0,")) +except ValueError: + print("ValueError") + +# incomplete nested object declaration +try: + my_print(json.loads('[0, {"a":0, ')) +except ValueError: + print("ValueError") diff --git a/tests/extmod/json_loads_bytes.py.exp b/tests/extmod/json_loads_bytes.py.exp deleted file mode 100644 index c2735a99052d2..0000000000000 --- a/tests/extmod/json_loads_bytes.py.exp +++ /dev/null @@ -1,2 +0,0 @@ -[1, 2] -[None] diff --git a/tests/extmod/json_loads_int_64.py b/tests/extmod/json_loads_int_64.py new file mode 100644 index 0000000000000..f6236f1904a87 --- /dev/null +++ b/tests/extmod/json_loads_int_64.py @@ -0,0 +1,16 @@ +# Parse 64-bit integers from JSON payloads. +# +# This also exercises parsing integers from strings +# where the value may not be null terminated (last line) +try: + import json +except ImportError: + print("SKIP") + raise SystemExit + + +print(json.loads("9111222333444555666")) +print(json.loads("-9111222333444555666")) +print(json.loads("9111222333444555666")) +print(json.loads("-9111222333444555666")) +print(json.loads('["9111222333444555666777",9111222333444555666]')) diff --git a/tests/extmod/machine_hard_timer.py b/tests/extmod/machine_hard_timer.py new file mode 100644 index 0000000000000..8fe42ea850842 --- /dev/null +++ b/tests/extmod/machine_hard_timer.py @@ -0,0 +1,45 @@ +import sys + +try: + from machine import Timer + from time import sleep_ms +except: + print("SKIP") + raise SystemExit + +if sys.platform == "esp8266": + timer = Timer(0) +else: + # Hardware timers are not implemented. + print("SKIP") + raise SystemExit + +# Test both hard and soft IRQ handlers and both one-shot and periodic +# timers. We adjust period in tests/extmod/machine_soft_timer.py, so try +# adjusting freq here instead. The heap should be locked in hard callbacks +# and unlocked in soft callbacks. + + +def callback(t): + print("callback", mode[1], kind[1], freq, end=" ") + try: + allocate = bytearray(1) + print("unlocked") + except MemoryError: + print("locked") + + +modes = [(Timer.ONE_SHOT, "one-shot"), (Timer.PERIODIC, "periodic")] +kinds = [(False, "soft"), (True, "hard")] + +for mode in modes: + for kind in kinds: + for freq in 50, 25: + timer.init( + mode=mode[0], + freq=freq, + hard=kind[0], + callback=callback, + ) + sleep_ms(90) + timer.deinit() diff --git a/tests/extmod/machine_hard_timer.py.exp b/tests/extmod/machine_hard_timer.py.exp new file mode 100644 index 0000000000000..26cdc644fdd08 --- /dev/null +++ b/tests/extmod/machine_hard_timer.py.exp @@ -0,0 +1,16 @@ +callback one-shot soft 50 unlocked +callback one-shot soft 25 unlocked +callback one-shot hard 50 locked +callback one-shot hard 25 locked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 25 unlocked +callback periodic soft 25 unlocked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 25 locked +callback periodic hard 25 locked diff --git a/tests/extmod/machine_timer.py b/tests/extmod/machine_timer.py new file mode 100644 index 0000000000000..ef97ea4e94955 --- /dev/null +++ b/tests/extmod/machine_timer.py @@ -0,0 +1,48 @@ +import sys + +try: + from machine import Timer + from time import sleep_ms +except: + print("SKIP") + raise SystemExit + +if sys.platform in ("esp32", "esp8266", "nrf"): + # Software timers aren't implemented on the esp32 and esp8266 ports. + # The nrf port doesn't support selection of hard and soft callbacks, + # and only allows Timer(period=N), not Timer(freq=N). + print("SKIP") + raise SystemExit +else: + timer_id = -1 + +# Test both hard and soft IRQ handlers and both one-shot and periodic +# timers. We adjust period in tests/extmod/machine_soft_timer.py, so try +# adjusting freq here instead. The heap should be locked in hard callbacks +# and unlocked in soft callbacks. + + +def callback(t): + print("callback", mode[1], kind[1], freq, end=" ") + try: + allocate = bytearray(1) + print("unlocked") + except MemoryError: + print("locked") + + +modes = [(Timer.ONE_SHOT, "one-shot"), (Timer.PERIODIC, "periodic")] +kinds = [(False, "soft"), (True, "hard")] + +for mode in modes: + for kind in kinds: + for freq in 50, 25: + timer = Timer( + timer_id, + mode=mode[0], + freq=freq, + hard=kind[0], + callback=callback, + ) + sleep_ms(90) + timer.deinit() diff --git a/tests/extmod/machine_timer.py.exp b/tests/extmod/machine_timer.py.exp new file mode 100644 index 0000000000000..26cdc644fdd08 --- /dev/null +++ b/tests/extmod/machine_timer.py.exp @@ -0,0 +1,16 @@ +callback one-shot soft 50 unlocked +callback one-shot soft 25 unlocked +callback one-shot hard 50 locked +callback one-shot hard 25 locked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 50 unlocked +callback periodic soft 25 unlocked +callback periodic soft 25 unlocked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 50 locked +callback periodic hard 25 locked +callback periodic hard 25 locked diff --git a/tests/extmod/platform_basic.py b/tests/extmod/platform_basic.py new file mode 100644 index 0000000000000..eb6f2be13c17a --- /dev/null +++ b/tests/extmod/platform_basic.py @@ -0,0 +1,8 @@ +try: + import platform +except ImportError: + print("SKIP") + raise SystemExit + +print(type(platform.python_compiler())) +print(type(platform.libc_ver())) diff --git a/tests/extmod/random_extra_float.py b/tests/extmod/random_extra_float.py index 3b37ed8dcef2f..03973c583492d 100644 --- a/tests/extmod/random_extra_float.py +++ b/tests/extmod/random_extra_float.py @@ -1,12 +1,8 @@ try: import random -except ImportError: - print("SKIP") - raise SystemExit -try: - random.randint -except AttributeError: + random.random +except (ImportError, AttributeError): print("SKIP") raise SystemExit diff --git a/tests/extmod/re_error.py b/tests/extmod/re_error.py index f61d0913289e1..bd678c9d25156 100644 --- a/tests/extmod/re_error.py +++ b/tests/extmod/re_error.py @@ -11,7 +11,7 @@ def test_re(r): try: re.compile(r) print("OK") - except: # uPy and CPy use different errors, so just ignore the type + except: # MPy and CPy use different errors, so just ignore the type print("Error") diff --git a/tests/extmod/re_start_end_pos.py b/tests/extmod/re_start_end_pos.py new file mode 100644 index 0000000000000..bd16584374b89 --- /dev/null +++ b/tests/extmod/re_start_end_pos.py @@ -0,0 +1,78 @@ +# test start and end pos specification + +try: + import re +except ImportError: + print("SKIP") + raise SystemExit + + +def print_groups(match): + print("----") + try: + if match is not None: + i = 0 + while True: + print(match.group(i)) + i += 1 + except IndexError: + pass + + +p = re.compile(r"o") +m = p.match("dog") +print_groups(m) + +m = p.match("dog", 1) +print_groups(m) + +m = p.match("dog", 2) +print_groups(m) + +# No match past end of input +m = p.match("dog", 5) +print_groups(m) + +m = p.match("dog", 0, 1) +print_groups(m) + +# Caret only matches the actual beginning +p = re.compile(r"^o") +m = p.match("dog", 1) +print_groups(m) + +# End at beginning means searching empty string +p = re.compile(r"o") +m = p.match("dog", 1, 1) +print_groups(m) + +# End before the beginning doesn't match anything +m = p.match("dog", 2, 1) +print_groups(m) + +# Negative starting values don't crash +m = p.search("dog", -2) +print_groups(m) + +m = p.search("dog", -2, -5) +print_groups(m) + +# Search also works +print("--search") + +p = re.compile(r"o") +m = p.search("dog") +print_groups(m) + +m = p.search("dog", 1) +print_groups(m) + +m = p.search("dog", 2) +print_groups(m) + +# Negative starting values don't crash +m = p.search("dog", -2) +print_groups(m) + +m = p.search("dog", -2, -5) +print_groups(m) diff --git a/tests/extmod/re_sub.py b/tests/extmod/re_sub.py index 3959949724d98..bb9aa111287e6 100644 --- a/tests/extmod/re_sub.py +++ b/tests/extmod/re_sub.py @@ -62,7 +62,7 @@ def A(): except: print("invalid group") -# invalid group with very large number (to test overflow in uPy) +# invalid group with very large number (to test overflow in MicroPython) try: re.sub("(a)", "b\\199999999999999999999999999999999999999", "a") except: diff --git a/tests/extmod/re_sub_unmatched.py.exp b/tests/extmod/re_sub_unmatched.py.exp deleted file mode 100644 index 1e5f0fda0554d..0000000000000 --- a/tests/extmod/re_sub_unmatched.py.exp +++ /dev/null @@ -1 +0,0 @@ -1-a2 diff --git a/tests/extmod/socket_badconstructor.py b/tests/extmod/socket_badconstructor.py new file mode 100644 index 0000000000000..4a9d2668c7f1a --- /dev/null +++ b/tests/extmod/socket_badconstructor.py @@ -0,0 +1,22 @@ +# Test passing in bad values to socket.socket constructor. + +try: + import socket +except: + print("SKIP") + raise SystemExit + +try: + s = socket.socket(None) +except TypeError: + print("TypeError") + +try: + s = socket.socket(socket.AF_INET, None) +except TypeError: + print("TypeError") + +try: + s = socket.socket(socket.AF_INET, socket.SOCK_RAW, None) +except TypeError: + print("TypeError") diff --git a/tests/extmod/socket_fileno.py b/tests/extmod/socket_fileno.py new file mode 100644 index 0000000000000..da15825e3d56b --- /dev/null +++ b/tests/extmod/socket_fileno.py @@ -0,0 +1,17 @@ +# Test socket.fileno() functionality + +try: + import socket +except ImportError: + print("SKIP") + raise SystemExit + +if not hasattr(socket.socket, "fileno"): + print("SKIP") + raise SystemExit + +s = socket.socket() +print(s.fileno() >= 0) + +s.close() +print(s.fileno()) # should print -1 diff --git a/tests/extmod/time_mktime.py b/tests/extmod/time_mktime.py new file mode 100644 index 0000000000000..7fc643dc3cb8c --- /dev/null +++ b/tests/extmod/time_mktime.py @@ -0,0 +1,120 @@ +# test conversion from date tuple to timestamp and back + +try: + import time + + time.localtime +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +# Range of date expected to work on all MicroPython platforms +MIN_YEAR = 1970 +MAX_YEAR = 2099 +# CPython properly supported date range: +# - on Windows: year 1970 to 3000+ +# - on Unix: year 1583 to 3000+ + +# Start test from Jan 1, 2001 13:00 (Feb 2000 might already be broken) +SAFE_DATE = (2001, 1, 1, 13, 0, 0, 0, 0, -1) + + +# mktime function that checks that the result is reversible +def safe_mktime(date_tuple): + try: + res = time.mktime(date_tuple) + chk = time.localtime(res) + except OverflowError: + print("safe_mktime:", date_tuple, "overflow error") + return None + if chk[0:5] != date_tuple[0:5]: + print("safe_mktime:", date_tuple[0:5], " -> ", res, " -> ", chk[0:5]) + return None + return res + + +# localtime function that checks that the result is reversible +def safe_localtime(timestamp): + try: + res = time.localtime(timestamp) + chk = time.mktime(res) + except OverflowError: + print("safe_localtime:", timestamp, "overflow error") + return None + if chk != timestamp: + print("safe_localtime:", timestamp, " -> ", res, " -> ", chk) + return None + return res + + +# look for smallest valid timestamps by iterating backwards on tuple +def test_bwd(date_tuple): + curr_stamp = safe_mktime(date_tuple) + year = date_tuple[0] + month = date_tuple[1] - 1 + if month < 1: + year -= 1 + month = 12 + while year >= MIN_YEAR: + while month >= 1: + next_tuple = (year, month) + date_tuple[2:] + next_stamp = safe_mktime(next_tuple) + # at this stage, only test consistency and monotonicity + if next_stamp is None or next_stamp >= curr_stamp: + return date_tuple + date_tuple = next_tuple + curr_stamp = next_stamp + month -= 1 + year -= 1 + month = 12 + return date_tuple + + +# test day-by-day to ensure that every date is properly converted +def test_fwd(start_date): + DAYS_PER_MONTH = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31) + curr_stamp = safe_mktime(start_date) + curr_date = safe_localtime(curr_stamp) + while curr_date[0] <= MAX_YEAR: + if curr_date[2] < 15: + skip_days = 13 + else: + skip_days = 1 + next_stamp = curr_stamp + skip_days * 86400 + next_date = safe_localtime(next_stamp) + if next_date is None: + return curr_date + if next_date[2] != curr_date[2] + skip_days: + # next month + if next_date[2] != 1: + print("wrong day of month:", next_date) + return curr_date + # check the number of days in previous month + month_days = DAYS_PER_MONTH[curr_date[1]] + if month_days == 28 and curr_date[0] % 4 == 0: + if curr_date[0] % 100 != 0 or curr_date[0] % 400 == 0: + month_days += 1 + if curr_date[2] != month_days: + print("wrong day count in prev month:", curr_date[2], "vs", month_days) + return curr_date + if next_date[1] != curr_date[1] + 1: + # next year + if curr_date[1] != 12: + print("wrong month count in prev year:", curr_date[1]) + return curr_date + if next_date[1] != 1: + print("wrong month:", next_date) + return curr_date + if next_date[0] != curr_date[0] + 1: + print("wrong year:", next_date) + return curr_date + curr_stamp = next_stamp + curr_date = next_date + return curr_date + + +small_date = test_bwd(SAFE_DATE) +large_date = test_fwd(small_date) +print("tested from", small_date[0:3], "to", large_date[0:3]) +print(small_date[0:3], "wday is", small_date[6]) +print(large_date[0:3], "wday is", large_date[6]) diff --git a/tests/extmod/time_res.py b/tests/extmod/time_res.py index 548bef1f1747c..ef20050b914d7 100644 --- a/tests/extmod/time_res.py +++ b/tests/extmod/time_res.py @@ -37,9 +37,12 @@ def test(): time.sleep_ms(100) for func_name, _ in EXPECTED_MAP: try: - time_func = getattr(time, func_name, None) or globals()[func_name] + if func_name.endswith("_time"): + time_func = globals()[func_name] + else: + time_func = getattr(time, func_name) now = time_func() # may raise AttributeError - except (KeyError, AttributeError): + except AttributeError: continue try: results_map[func_name].add(now) diff --git a/tests/extmod/tls_dtls.py b/tests/extmod/tls_dtls.py index b2d716769d3f7..753ab2fee4f40 100644 --- a/tests/extmod/tls_dtls.py +++ b/tests/extmod/tls_dtls.py @@ -34,9 +34,19 @@ def ioctl(self, req, arg): # Wrap the DTLS Server dtls_server_ctx = SSLContext(PROTOCOL_DTLS_SERVER) dtls_server_ctx.verify_mode = CERT_NONE -dtls_server = dtls_server_ctx.wrap_socket(server_socket, do_handshake_on_connect=False) +dtls_server = dtls_server_ctx.wrap_socket( + server_socket, do_handshake_on_connect=False, client_id=b"dummy_client_id" +) print("Wrapped DTLS Server") +# wrap DTLS server with invalid client_id +try: + dtls_server = dtls_server_ctx.wrap_socket( + server_socket, do_handshake_on_connect=False, client_id=4 + ) +except OSError: + print("Failed to wrap DTLS Server with invalid client_id") + # Wrap the DTLS Client dtls_client_ctx = SSLContext(PROTOCOL_DTLS_CLIENT) dtls_client_ctx.verify_mode = CERT_NONE diff --git a/tests/extmod/tls_dtls.py.exp b/tests/extmod/tls_dtls.py.exp index 78d72bff18816..dbd005d0edfb8 100644 --- a/tests/extmod/tls_dtls.py.exp +++ b/tests/extmod/tls_dtls.py.exp @@ -1,3 +1,4 @@ Wrapped DTLS Server +Failed to wrap DTLS Server with invalid client_id Wrapped DTLS Client OK diff --git a/tests/extmod/ssl_noleak.py b/tests/extmod/tls_noleak.py similarity index 100% rename from tests/extmod/ssl_noleak.py rename to tests/extmod/tls_noleak.py diff --git a/tests/extmod/tls_threads.py b/tests/extmod/tls_threads.py new file mode 100644 index 0000000000000..1e0c3d23d2f38 --- /dev/null +++ b/tests/extmod/tls_threads.py @@ -0,0 +1,58 @@ +# Ensure that SSL sockets can be allocated from multiple +# threads without thread safety issues + +try: + import _thread + import io + import tls + import time +except ImportError: + print("SKIP") + raise SystemExit + +import unittest + + +class TestSocket(io.IOBase): + def write(self, buf): + return len(buf) + + def readinto(self, buf): + return 0 + + def ioctl(self, cmd, arg): + return 0 + + def setblocking(self, value): + pass + + +ITERS = 256 + + +class TLSThreads(unittest.TestCase): + def test_sslsocket_threaded(self): + self.done = False + # only run in two threads: too much RAM demand otherwise, and rp2 only + # supports two anyhow + _thread.start_new_thread(self._alloc_many_sockets, (True,)) + self._alloc_many_sockets(False) + while not self.done: + time.sleep(0.1) + print("done") + + def _alloc_many_sockets(self, set_done_flag): + print("start", _thread.get_ident()) + ctx = tls.SSLContext(tls.PROTOCOL_TLS_CLIENT) + ctx.verify_mode = tls.CERT_NONE + for n in range(ITERS): + s = TestSocket() + s = ctx.wrap_socket(s, do_handshake_on_connect=False) + s.close() # Free associated resources now from thread, not in a GC pass + print("done", _thread.get_ident()) + if set_done_flag: + self.done = True + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/extmod/uctypes_addressof.py b/tests/extmod/uctypes_addressof.py index c83089d0f72af..213fcc05eee2b 100644 --- a/tests/extmod/uctypes_addressof.py +++ b/tests/extmod/uctypes_addressof.py @@ -12,5 +12,8 @@ print(uctypes.addressof(uctypes.bytearray_at(1 << i, 8))) # Test address that is bigger than the greatest small-int but still within the address range. -large_addr = maxsize + 1 -print(uctypes.addressof(uctypes.bytearray_at(large_addr, 8)) == large_addr) +try: + large_addr = maxsize + 1 + print(uctypes.addressof(uctypes.bytearray_at(large_addr, 8)) == large_addr) +except OverflowError: + print(True) # systems with 64-bit bigints will overflow on the above operation diff --git a/tests/extmod/uctypes_array_load_store.py b/tests/extmod/uctypes_array_load_store.py index 9de079998566f..695352da57983 100644 --- a/tests/extmod/uctypes_array_load_store.py +++ b/tests/extmod/uctypes_array_load_store.py @@ -6,6 +6,13 @@ print("SKIP") raise SystemExit +# 'int' needs to be able to represent UINT64 for this test +try: + int("FF" * 8, 16) +except OverflowError: + print("SKIP") + raise SystemExit + N = 5 for endian in ("NATIVE", "LITTLE_ENDIAN", "BIG_ENDIAN"): diff --git a/tests/extmod/vfs_blockdev_invalid.py b/tests/extmod/vfs_blockdev_invalid.py index 4d00f4b00273a..29d6bd6b2f9f7 100644 --- a/tests/extmod/vfs_blockdev_invalid.py +++ b/tests/extmod/vfs_blockdev_invalid.py @@ -70,8 +70,8 @@ def test(vfs_class): try: with fs.open("test", "r") as f: print("opened") - except OSError as e: - print("OSError", e) + except Exception as e: + print(type(e), e) # This variant should succeed on open, may fail on read # unless the filesystem cached the contents already @@ -81,8 +81,8 @@ def test(vfs_class): bdev.read_res = res print("read 1", f.read(1)) print("read rest", f.read()) - except OSError as e: - print("OSError", e) + except Exception as e: + print(type(e), e) test(vfs.VfsLfs2) diff --git a/tests/extmod/vfs_blockdev_invalid.py.exp b/tests/extmod/vfs_blockdev_invalid.py.exp index 13695e0d88916..0ea0353501de3 100644 --- a/tests/extmod/vfs_blockdev_invalid.py.exp +++ b/tests/extmod/vfs_blockdev_invalid.py.exp @@ -2,27 +2,27 @@ opened read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 5] EIO + [Errno 5] EIO read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 22] EINVAL + [Errno 22] EINVAL read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 22] EINVAL + [Errno 22] EINVAL read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 22] EINVAL + can't convert str to int read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa opened read 1 a read rest aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO -OSError [Errno 5] EIO + [Errno 5] EIO + [Errno 5] EIO + [Errno 5] EIO + [Errno 5] EIO + [Errno 5] EIO + [Errno 5] EIO + can't convert str to int + can't convert str to int diff --git a/tests/extmod/vfs_fat_ilistdir_del.py b/tests/extmod/vfs_fat_ilistdir_del.py index a6e24ec92f3b5..964e6b12868eb 100644 --- a/tests/extmod/vfs_fat_ilistdir_del.py +++ b/tests/extmod/vfs_fat_ilistdir_del.py @@ -1,8 +1,7 @@ # Test ilistdir __del__ for VfsFat using a RAM device. -import gc try: - import os, vfs + import gc, os, vfs vfs.VfsFat except (ImportError, AttributeError): diff --git a/tests/extmod/vfs_lfs.py b/tests/extmod/vfs_lfs.py index 3ad57fd9c38f1..40d58e9c9f743 100644 --- a/tests/extmod/vfs_lfs.py +++ b/tests/extmod/vfs_lfs.py @@ -136,7 +136,7 @@ def test(bdev, vfs_class): print(fs.getcwd()) fs.chdir("../testdir") print(fs.getcwd()) - fs.chdir("../..") + fs.chdir("..") print(fs.getcwd()) fs.chdir(".//testdir") print(fs.getcwd()) diff --git a/tests/extmod/vfs_lfs_error.py b/tests/extmod/vfs_lfs_error.py index 2ac7629bfa8fc..73cdf3437330b 100644 --- a/tests/extmod/vfs_lfs_error.py +++ b/tests/extmod/vfs_lfs_error.py @@ -1,7 +1,7 @@ # Test for VfsLittle using a RAM device, testing error handling try: - import vfs + import errno, vfs vfs.VfsLfs1 vfs.VfsLfs2 @@ -41,14 +41,14 @@ def test(bdev, vfs_class): # mkfs with too-small block device try: vfs_class.mkfs(RAMBlockDevice(1)) - except OSError: - print("mkfs OSError") + except OSError as er: + print("mkfs OSError", er.errno > 0) # mount with invalid filesystem try: vfs_class(bdev) - except OSError: - print("mount OSError") + except OSError as er: + print("mount OSError", er.errno > 0) # set up for following tests vfs_class.mkfs(bdev) @@ -60,60 +60,60 @@ def test(bdev, vfs_class): # ilistdir try: fs.ilistdir("noexist") - except OSError: - print("ilistdir OSError") + except OSError as er: + print("ilistdir OSError", er) # remove try: fs.remove("noexist") - except OSError: - print("remove OSError") + except OSError as er: + print("remove OSError", er) # rmdir try: fs.rmdir("noexist") - except OSError: - print("rmdir OSError") + except OSError as er: + print("rmdir OSError", er) # rename try: fs.rename("noexist", "somethingelse") - except OSError: - print("rename OSError") + except OSError as er: + print("rename OSError", er) # mkdir try: fs.mkdir("testdir") - except OSError: - print("mkdir OSError") + except OSError as er: + print("mkdir OSError", er) # chdir to nonexistent try: fs.chdir("noexist") - except OSError: - print("chdir OSError") + except OSError as er: + print("chdir OSError", er) print(fs.getcwd()) # check still at root # chdir to file try: fs.chdir("testfile") - except OSError: - print("chdir OSError") + except OSError as er: + print("chdir OSError", er) print(fs.getcwd()) # check still at root # stat try: fs.stat("noexist") - except OSError: - print("stat OSError") + except OSError as er: + print("stat OSError", er) # error during seek with fs.open("testfile", "r") as f: f.seek(1 << 30) # SEEK_SET try: f.seek(1 << 30, 1) # SEEK_CUR - except OSError: - print("seek OSError") + except OSError as er: + print("seek OSError", er) bdev = RAMBlockDevice(30) diff --git a/tests/extmod/vfs_lfs_error.py.exp b/tests/extmod/vfs_lfs_error.py.exp index f4327f6962ec3..440607ed84b22 100644 --- a/tests/extmod/vfs_lfs_error.py.exp +++ b/tests/extmod/vfs_lfs_error.py.exp @@ -1,28 +1,28 @@ test -mkfs OSError -mount OSError -ilistdir OSError -remove OSError -rmdir OSError -rename OSError -mkdir OSError -chdir OSError +mkfs OSError True +mount OSError True +ilistdir OSError [Errno 2] ENOENT +remove OSError [Errno 2] ENOENT +rmdir OSError [Errno 2] ENOENT +rename OSError [Errno 2] ENOENT +mkdir OSError [Errno 17] EEXIST +chdir OSError [Errno 2] ENOENT / -chdir OSError +chdir OSError [Errno 2] ENOENT / -stat OSError -seek OSError +stat OSError [Errno 2] ENOENT +seek OSError [Errno 22] EINVAL test -mkfs OSError -mount OSError -ilistdir OSError -remove OSError -rmdir OSError -rename OSError -mkdir OSError -chdir OSError +mkfs OSError True +mount OSError True +ilistdir OSError [Errno 2] ENOENT +remove OSError [Errno 2] ENOENT +rmdir OSError [Errno 2] ENOENT +rename OSError [Errno 2] ENOENT +mkdir OSError [Errno 17] EEXIST +chdir OSError [Errno 2] ENOENT / -chdir OSError +chdir OSError [Errno 2] ENOENT / -stat OSError -seek OSError +stat OSError [Errno 2] ENOENT +seek OSError [Errno 22] EINVAL diff --git a/tests/extmod/vfs_lfs_ilistdir_del.py b/tests/extmod/vfs_lfs_ilistdir_del.py index 7b59bc412d983..828c85a258856 100644 --- a/tests/extmod/vfs_lfs_ilistdir_del.py +++ b/tests/extmod/vfs_lfs_ilistdir_del.py @@ -1,8 +1,7 @@ # Test ilistdir __del__ for VfsLittle using a RAM device. -import gc try: - import vfs + import gc, vfs vfs.VfsLfs2 except (ImportError, AttributeError): @@ -71,5 +70,10 @@ def test(bdev, vfs_class): fs.open("/test", "w").close() -bdev = RAMBlockDevice(30) +try: + bdev = RAMBlockDevice(30) +except MemoryError: + print("SKIP") + raise SystemExit + test(bdev, vfs.VfsLfs2) diff --git a/tests/extmod/vfs_mountinfo.py b/tests/extmod/vfs_mountinfo.py index f674e80763409..b31dc60ce76d7 100644 --- a/tests/extmod/vfs_mountinfo.py +++ b/tests/extmod/vfs_mountinfo.py @@ -5,7 +5,6 @@ except ImportError: print("SKIP") raise SystemExit -import errno class Filesystem: diff --git a/tests/extmod/vfs_posix.py b/tests/extmod/vfs_posix.py index d060c0b9c84f3..b3ca2753ba9cf 100644 --- a/tests/extmod/vfs_posix.py +++ b/tests/extmod/vfs_posix.py @@ -29,7 +29,21 @@ print(type(os.stat("/"))) # listdir and ilistdir -print(type(os.listdir("/"))) +target = "/" +try: + import platform + + # On Android non-root users are permitted full filesystem access only to + # selected directories. To let this test pass on bionic, the internal + # user-accessible storage area root is enumerated instead of the + # filesystem root. "/storage/emulated/0" should be there on pretty much + # any recent-ish device; querying the proper location requires a JNI + # round-trip, not really worth it. + if platform.platform().startswith("Android-"): + target = "/storage/emulated/0" +except ImportError: + pass +print(type(os.listdir(target))) # mkdir os.mkdir(temp_dir) diff --git a/tests/extmod/vfs_posix_ilistdir_del.py b/tests/extmod/vfs_posix_ilistdir_del.py index 78d7c854c543c..8b5984cd81c05 100644 --- a/tests/extmod/vfs_posix_ilistdir_del.py +++ b/tests/extmod/vfs_posix_ilistdir_del.py @@ -1,8 +1,7 @@ # Test ilistdir __del__ for VfsPosix. -import gc try: - import os, vfs + import gc, os, vfs vfs.VfsPosix except (ImportError, AttributeError): diff --git a/tests/extmod/vfs_posix_paths.py b/tests/extmod/vfs_posix_paths.py index b4fedc6716f01..c06318748a327 100644 --- a/tests/extmod/vfs_posix_paths.py +++ b/tests/extmod/vfs_posix_paths.py @@ -31,7 +31,7 @@ fs.mkdir("subdir/one") print('listdir("/"):', sorted(i[0] for i in fs.ilistdir("/"))) print('listdir("."):', sorted(i[0] for i in fs.ilistdir("."))) -print('getcwd() in {"", "/"}:', fs.getcwd() in {"", "/"}) +print('getcwd() in ("", "/"):', fs.getcwd() in ("", "/")) print('chdir("subdir"):', fs.chdir("subdir")) print("getcwd():", fs.getcwd()) print('mkdir("two"):', fs.mkdir("two")) diff --git a/tests/extmod/vfs_posix_paths.py.exp b/tests/extmod/vfs_posix_paths.py.exp index ecc13222aaaeb..d6a0960efd249 100644 --- a/tests/extmod/vfs_posix_paths.py.exp +++ b/tests/extmod/vfs_posix_paths.py.exp @@ -1,6 +1,6 @@ listdir("/"): ['subdir'] listdir("."): ['subdir'] -getcwd() in {"", "/"}: True +getcwd() in ("", "/"): True chdir("subdir"): None getcwd(): /subdir mkdir("two"): None diff --git a/tests/extmod/vfs_posix_readonly.py b/tests/extmod/vfs_posix_readonly.py new file mode 100644 index 0000000000000..e7821381006fd --- /dev/null +++ b/tests/extmod/vfs_posix_readonly.py @@ -0,0 +1,99 @@ +# Test for VfsPosix + +try: + import gc, os, vfs, errno + + vfs.VfsPosix +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +# We need a directory for testing that doesn't already exist. +# Skip the test if it does exist. +temp_dir = "vfs_posix_readonly_test_dir" +try: + os.stat(temp_dir) + raise SystemExit("Target directory {} exists".format(temp_dir)) +except OSError: + pass + +# mkdir (skip test if whole filesystem is readonly) +try: + os.mkdir(temp_dir) +except OSError as e: + if e.errno == errno.EROFS: + print("SKIP") + raise SystemExit + +fs_factory = lambda: vfs.VfsPosix(temp_dir) + +# mount +fs = fs_factory() +vfs.mount(fs, "/vfs") + +with open("/vfs/file", "w") as f: + f.write("content") + +# test reading works +with open("/vfs/file") as f: + print("file:", f.read()) + +os.mkdir("/vfs/emptydir") + +# umount +vfs.umount("/vfs") + +# mount read-only +fs = fs_factory() +vfs.mount(fs, "/vfs", readonly=True) + +# test reading works +with open("/vfs/file") as f: + print("file 2:", f.read()) + +# test writing fails +try: + with open("/vfs/test_write", "w"): + pass + print("opened") +except OSError as er: + print(repr(er)) + +# test removing fails +try: + os.unlink("/vfs/file") + print("unlinked") +except OSError as er: + print(repr(er)) + +# test renaming fails +try: + os.rename("/vfs/file2", "/vfs/renamed") + print("renamed") +except OSError as er: + print(repr(er)) + +# test removing directory fails +try: + os.rmdir("/vfs/emptydir") + print("rmdir'd") +except OSError as er: + print(repr(er)) + +# test creating directory fails +try: + os.mkdir("/vfs/emptydir2") + print("mkdir'd") +except OSError as er: + print(repr(er)) + +# umount +vfs.umount("/vfs") + +fs = fs_factory() +vfs.mount(fs, "/vfs") + +os.rmdir("/vfs/emptydir") +os.unlink("/vfs/file") + +os.rmdir(temp_dir) diff --git a/tests/extmod/vfs_posix_readonly.py.exp b/tests/extmod/vfs_posix_readonly.py.exp new file mode 100644 index 0000000000000..40e4316775ff3 --- /dev/null +++ b/tests/extmod/vfs_posix_readonly.py.exp @@ -0,0 +1,7 @@ +file: content +file 2: content +OSError(30,) +OSError(30,) +OSError(30,) +OSError(30,) +OSError(30,) diff --git a/tests/extmod/vfs_rom.py b/tests/extmod/vfs_rom.py index 770b6863b9c43..18ae1f5cf96c8 100644 --- a/tests/extmod/vfs_rom.py +++ b/tests/extmod/vfs_rom.py @@ -25,7 +25,7 @@ # An mpy file with four constant objects: str, bytes, long-int, float. test_mpy = ( # header - b"M\x06\x00\x1f" # mpy file header + b"M\x06\x00\x1e" # mpy file header, -msmall-int-bits=30 b"\x06" # n_qstr b"\x05" # n_obj # qstrs @@ -394,6 +394,7 @@ class TestMounted(TestBase): def setUp(self): self.orig_sys_path = list(sys.path) self.orig_cwd = os.getcwd() + sys.path = [] vfs.mount(vfs.VfsRom(self.romfs), "/test_rom") def tearDown(self): diff --git a/tests/extmod/websocket_toobig.py b/tests/extmod/websocket_toobig.py new file mode 100644 index 0000000000000..f4c5a74bbceac --- /dev/null +++ b/tests/extmod/websocket_toobig.py @@ -0,0 +1,28 @@ +try: + import io + import errno + import websocket +except ImportError: + print("SKIP") + raise SystemExit + +try: + buf = "x" * 65536 +except MemoryError: + print("SKIP") + raise SystemExit + + +# do a websocket write and then return the raw data from the stream +def ws_write(msg, sz): + s = io.BytesIO() + ws = websocket.websocket(s) + ws.write(msg) + s.seek(0) + return s.read(sz) + + +try: + print(ws_write(buf, 1)) +except OSError as e: + print("ioctl: ENOBUFS:", e.errno == errno.ENOBUFS) diff --git a/tests/extmod/websocket_toobig.py.exp b/tests/extmod/websocket_toobig.py.exp new file mode 100644 index 0000000000000..3bbd95282fdfe --- /dev/null +++ b/tests/extmod/websocket_toobig.py.exp @@ -0,0 +1 @@ +ioctl: ENOBUFS: True diff --git a/tests/extmod_hardware/machine_counter.py b/tests/extmod_hardware/machine_counter.py new file mode 100644 index 0000000000000..62ac1fed47ce7 --- /dev/null +++ b/tests/extmod_hardware/machine_counter.py @@ -0,0 +1,90 @@ +# Test machine.Counter implementation +# +# IMPORTANT: This test requires hardware connections: the out_pin and in_pin +# must be wired together. + +try: + from machine import Counter +except ImportError: + print("SKIP") + raise SystemExit + +import sys +from machine import Pin + +if "esp32" in sys.platform: + id = 0 + out_pin = 4 + in_pin = 5 +else: + print("Please add support for this test on this platform.") + raise SystemExit + +import unittest + +out_pin = Pin(out_pin, mode=Pin.OUT) +in_pin = Pin(in_pin, mode=Pin.IN) + + +def toggle(times): + for _ in range(times): + out_pin(1) + out_pin(0) + + +class TestCounter(unittest.TestCase): + def setUp(self): + out_pin(0) + self.counter = Counter(id, in_pin) + + def tearDown(self): + self.counter.deinit() + + def assertCounter(self, value): + self.assertEqual(self.counter.value(), value) + + def test_connections(self): + # Test the hardware connections are correct. If this test fails, all tests will fail. + out_pin(1) + self.assertEqual(1, in_pin()) + out_pin(0) + self.assertEqual(0, in_pin()) + + def test_count_rising(self): + self.assertCounter(0) + toggle(100) + self.assertCounter(100) + out_pin(1) + self.assertEqual(self.counter.value(0), 101) + self.assertCounter(0) # calling value(0) resets + out_pin(0) + self.assertCounter(0) # no rising edge + out_pin(1) + self.assertCounter(1) + + def test_change_directions(self): + self.assertCounter(0) + toggle(100) + self.assertCounter(100) + self.counter.init(in_pin, direction=Counter.DOWN) + self.assertCounter(0) # calling init() zeroes the counter + self.counter.value(100) # need to manually reset the value + self.assertCounter(100) + toggle(25) + self.assertCounter(75) + + def test_count_falling(self): + self.counter.init(in_pin, direction=Counter.UP, edge=Counter.FALLING) + toggle(20) + self.assertCounter(20) + out_pin(1) + self.assertCounter(20) # no falling edge + out_pin(0) + self.assertCounter(21) + self.counter.value(-(2**24)) + toggle(20) + self.assertCounter(-(2**24 - 20)) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/extmod_hardware/machine_encoder.py b/tests/extmod_hardware/machine_encoder.py new file mode 100644 index 0000000000000..c218c8bfb646a --- /dev/null +++ b/tests/extmod_hardware/machine_encoder.py @@ -0,0 +1,153 @@ +# Test machine.Encoder implementation +# +# IMPORTANT: This test requires hardware connections: +# - out0_pin and in0_pin must be wired together. +# - out1_pin and in1_pin must be wired together. + +try: + from machine import Encoder +except ImportError: + print("SKIP") + raise SystemExit + +import sys +import unittest +from machine import Pin +from target_wiring import encoder_loopback_id, encoder_loopback_out_pins, encoder_loopback_in_pins + +PRINT = False +PIN_INIT_VALUE = 1 + +id = encoder_loopback_id +out0_pin, out1_pin = encoder_loopback_out_pins +in0_pin, in1_pin = encoder_loopback_in_pins + +out0_pin = Pin(out0_pin, mode=Pin.OUT) +in0_pin = Pin(in0_pin, mode=Pin.IN) +out1_pin = Pin(out1_pin, mode=Pin.OUT) +in1_pin = Pin(in1_pin, mode=Pin.IN) + + +class TestEncoder(unittest.TestCase): + def setUp(self): + out0_pin(PIN_INIT_VALUE) + out1_pin(PIN_INIT_VALUE) + self.enc = Encoder(id, in0_pin, in1_pin, phases=1) + self.enc2 = Encoder(id + 1, in0_pin, in1_pin, phases=2) + self.enc4 = Encoder(id + 2, in0_pin, in1_pin, phases=4) + self.pulses = 0 # track the expected encoder position in software + if PRINT: + print( + "\nout0_pin() out1_pin() enc.value() enc2.value() enc4.value() |", + out0_pin(), + out1_pin(), + "|", + self.enc.value(), + self.enc2.value(), + self.enc4.value(), + ) + + def tearDown(self): + self.enc.deinit() + try: + self.enc2.deinit() + except: + pass + try: + self.enc4.deinit() + except: + pass + + def rotate(self, pulses): + for _ in range(abs(pulses)): + self.pulses += 1 if (pulses > 0) else -1 + if pulses > 0: + if self.pulses % 2: + out0_pin(not out0_pin()) + else: + out1_pin(not out1_pin()) + else: + if self.pulses % 2: + out1_pin(not out1_pin()) + else: + out0_pin(not out0_pin()) + if PRINT: + print( + "out0_pin() out1_pin() enc.value() enc2.value() enc4.value() pulses self.pulses |", + out0_pin(), + out1_pin(), + "|", + self.enc.value(), + self.enc2.value(), + self.enc4.value(), + "|", + pulses, + self.pulses, + ) + + def assertPosition(self, value, value2=None, value4=None): + self.assertEqual(self.enc.value(), value) + if not value2 is None: + self.assertEqual(self.enc2.value(), value2) + if not value4 is None: + self.assertEqual(self.enc4.value(), value4) + pass + + @unittest.skipIf(sys.platform == "mimxrt", "cannot read back the pin") + def test_connections(self): + # Test the hardware connections are correct. If this test fails, all tests will fail. + for ch, outp, inp in ((0, out0_pin, in0_pin), (1, out1_pin, in1_pin)): + print("Testing channel ", ch) + outp(1) + self.assertEqual(1, inp()) + outp(0) + self.assertEqual(0, inp()) + + def test_basics(self): + self.assertPosition(0) + self.rotate(100) + self.assertPosition(100 // 4, 100 // 2, 100) + self.rotate(-100) + self.assertPosition(0) + + def test_partial(self): + # With phase=1 (default), need 4x pulses to count a rotation + self.assertPosition(0) + self.rotate(1) + self.assertPosition(1, 1, 1) + self.rotate(1) + self.assertPosition(1, 1, 2) + self.rotate(1) + self.assertPosition(1, 2, 3) + self.rotate(1) + self.assertPosition(1, 2, 4) # +4 + self.rotate(1) + self.assertPosition(2, 3, 5) + self.rotate(1) + self.assertPosition(2, 3, 6) + self.rotate(1) + self.assertPosition(2, 4, 7) + self.rotate(1) + self.assertPosition(2, 4, 8) # +4 + self.rotate(-1) + self.assertPosition(2, 4, 7) + self.rotate(-3) + self.assertPosition(1, 2, 4) # -4 + self.rotate(-4) + self.assertPosition(0, 0, 0) # -4 + self.rotate(-1) + self.assertPosition(0, 0, -1) + self.rotate(-1) + self.assertPosition(0, -1, -2) + self.rotate(-1) + self.assertPosition(0, -1, -3) + self.rotate(-1) + self.assertPosition(-1, -2, -4) # -4 + self.rotate(-1) + self.assertPosition(-1, -2, -5) + self.rotate(-3) + self.assertPosition(-2, -4, -8) # -4 + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/extmod_hardware/machine_i2c_target.py b/tests/extmod_hardware/machine_i2c_target.py new file mode 100644 index 0000000000000..763e6f4771e0f --- /dev/null +++ b/tests/extmod_hardware/machine_i2c_target.py @@ -0,0 +1,307 @@ +# Test machine.I2CTarget. +# +# IMPORTANT: This test requires hardware connections: a SoftI2C instance must be +# wired to a hardware I2C target. See pin definitions below. + +import sys + +try: + from machine import Pin, SoftI2C, I2CTarget +except ImportError: + print("SKIP") + raise SystemExit + +import unittest + +ADDR = 67 + +kwargs_target = {} + +# Configure pins based on the target. +if sys.platform == "alif" and sys.implementation._build == "ALIF_ENSEMBLE": + args_controller = {"scl": "P1_1", "sda": "P1_0"} + args_target = (0,) # on pins P0_3/P0_2 +elif sys.platform == "esp32": + args_controller = {"scl": 5, "sda": 6} + args_target = (0,) # on pins 9/8 for C3 and S3, 18/19 for others + kwargs_target = {"scl": 9, "sda": 8} +elif sys.platform == "rp2": + args_controller = {"scl": 5, "sda": 4} + args_target = (1,) +elif sys.platform == "pyboard": + if sys.implementation._build == "NUCLEO_WB55": + args_controller = {"scl": "B8", "sda": "B9"} + args_target = (3,) + else: + args_controller = {"scl": "X1", "sda": "X2"} + args_target = ("X",) +elif "zephyr-nucleo_wb55rg" in sys.implementation._machine: + # PB8=I2C1_SCL, PB9=I2C1_SDA (on Arduino header D15/D14) + # PC0=I2C3_SCL, PC1=I2C3_SDA (on Arduino header A0/A1) + args_controller = {"scl": Pin(("gpiob", 8)), "sda": Pin(("gpiob", 9))} + args_target = ("i2c3",) +elif "zephyr-rpi_pico" in sys.implementation._machine: + args_controller = {"scl": Pin(("gpio0", 5)), "sda": Pin(("gpio0", 4))} + args_target = ("i2c1",) # on gpio7/gpio6 +elif sys.platform == "mimxrt": + if "Teensy" in sys.implementation._machine: + args_controller = {"scl": "A6", "sda": "A3"} # D20/D17 + else: + args_controller = {"scl": "D0", "sda": "D1"} + args_target = (0,) # pins 19/18 On Teensy 4.x +elif sys.platform == "samd": + args_controller = {"scl": "D5", "sda": "D1"} + args_target = () +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def config_pull_up(): + Pin(args_controller["scl"], Pin.OPEN_DRAIN, Pin.PULL_UP) + Pin(args_controller["sda"], Pin.OPEN_DRAIN, Pin.PULL_UP) + + +class TestMemory(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.mem = bytearray(8) + cls.i2c = SoftI2C(**args_controller) + cls.i2c_target = I2CTarget(*args_target, **kwargs_target, addr=ADDR, mem=cls.mem) + config_pull_up() + + @classmethod + def tearDownClass(cls): + cls.i2c_target.deinit() + + def test_scan(self): + self.assertIn(ADDR, self.i2c.scan()) + + def test_write(self): + self.mem[:] = b"01234567" + self.i2c.writeto_mem(ADDR, 0, b"test") + self.assertEqual(self.mem, bytearray(b"test4567")) + self.i2c.writeto_mem(ADDR, 4, b"TEST") + self.assertEqual(self.mem, bytearray(b"testTEST")) + + def test_write_wrap(self): + self.mem[:] = b"01234567" + self.i2c.writeto_mem(ADDR, 6, b"test") + self.assertEqual(self.mem, bytearray(b"st2345te")) + + @unittest.skipIf(sys.platform == "esp32", "write lengths larger than buffer unsupported") + def test_write_wrap_large(self): + self.mem[:] = b"01234567" + self.i2c.writeto_mem(ADDR, 0, b"testTESTmore") + self.assertEqual(self.mem, bytearray(b"moreTEST")) + + def test_read(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.readfrom_mem(ADDR, 0, 4), b"0123") + self.assertEqual(self.i2c.readfrom_mem(ADDR, 4, 4), b"4567") + + def test_read_wrap(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.readfrom_mem(ADDR, 0, 4), b"0123") + self.assertEqual(self.i2c.readfrom_mem(ADDR, 2, 4), b"2345") + self.assertEqual(self.i2c.readfrom_mem(ADDR, 6, 4), b"6701") + + @unittest.skipIf(sys.platform == "esp32", "read lengths larger than buffer unsupported") + def test_read_wrap_large(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.readfrom_mem(ADDR, 0, 12), b"012345670123") + + def test_write_read(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.writeto(ADDR, b"\x02"), 1) + self.assertEqual(self.i2c.readfrom(ADDR, 4), b"2345") + + @unittest.skipIf(sys.platform == "esp32", "read after read unsupported") + def test_write_read_read(self): + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.writeto(ADDR, b"\x02"), 1) + self.assertEqual(self.i2c.readfrom(ADDR, 4), b"2345") + self.assertEqual(self.i2c.readfrom(ADDR, 4), b"7012") + + +@unittest.skipUnless(hasattr(I2CTarget, "IRQ_END_READ"), "IRQ unsupported") +class TestMemoryIRQ(unittest.TestCase): + @staticmethod + def irq_handler(i2c_target): + flags = i2c_target.irq().flags() + TestMemoryIRQ.events[TestMemoryIRQ.num_events] = flags + TestMemoryIRQ.events[TestMemoryIRQ.num_events + 1] = i2c_target.memaddr + TestMemoryIRQ.num_events += 2 + + @classmethod + def setUpClass(cls): + cls.mem = bytearray(8) + cls.events = [0] * 8 + cls.num_events = 0 + cls.i2c = SoftI2C(**args_controller) + cls.i2c_target = I2CTarget(*args_target, **kwargs_target, addr=ADDR, mem=cls.mem) + cls.i2c_target.irq(TestMemoryIRQ.irq_handler) + config_pull_up() + + @classmethod + def tearDownClass(cls): + cls.i2c_target.deinit() + + @unittest.skipIf(sys.platform == "esp32", "scan doesn't trigger IRQ_END_WRITE") + def test_scan(self): + TestMemoryIRQ.num_events = 0 + self.i2c.scan() + self.assertEqual(self.events[: self.num_events], [I2CTarget.IRQ_END_WRITE, 0]) + + def test_write(self): + TestMemoryIRQ.num_events = 0 + self.mem[:] = b"01234567" + self.i2c.writeto_mem(ADDR, 2, b"test") + self.assertEqual(self.mem, bytearray(b"01test67")) + self.assertEqual(self.events[: self.num_events], [I2CTarget.IRQ_END_WRITE, 2]) + + def test_read(self): + TestMemoryIRQ.num_events = 0 + self.mem[:] = b"01234567" + self.assertEqual(self.i2c.readfrom_mem(ADDR, 2, 4), b"2345") + self.assertEqual(self.events[: self.num_events], [I2CTarget.IRQ_END_READ, 2]) + + +@unittest.skipUnless(hasattr(I2CTarget, "IRQ_WRITE_REQ"), "IRQ unsupported") +@unittest.skipIf(sys.platform == "mimxrt", "not working") +@unittest.skipIf(sys.platform == "pyboard", "can't queue more than one byte") +@unittest.skipIf(sys.platform == "samd", "not working") +@unittest.skipIf(sys.platform == "zephyr", "must call readinto/write in IRQ handler") +class TestPolling(unittest.TestCase): + @staticmethod + def irq_handler(i2c_target, buf=bytearray(1)): + flags = i2c_target.irq().flags() + if flags & I2CTarget.IRQ_READ_REQ: + i2c_target.write(b"0123") + + @classmethod + def setUpClass(cls): + cls.i2c = SoftI2C(**args_controller) + cls.i2c_target = I2CTarget(*args_target, addr=ADDR) + cls.i2c_target.irq( + TestPolling.irq_handler, + I2CTarget.IRQ_WRITE_REQ | I2CTarget.IRQ_READ_REQ, + hard=True, + ) + config_pull_up() + + @classmethod + def tearDownClass(cls): + cls.i2c_target.deinit() + + def test_read(self): + # Can't write data up front, must wait until IRQ_READ_REQ. + # self.assertEqual(self.i2c_target.write(b"abcd"), 4) + self.assertEqual(self.i2c.readfrom(ADDR, 4), b"0123") + + def test_write(self): + # Can do the read outside the IRQ, but requires IRQ_WRITE_REQ trigger to be set. + self.assertEqual(self.i2c.writeto(ADDR, b"0123"), 4) + buf = bytearray(8) + self.assertEqual(self.i2c_target.readinto(buf), 4) + self.assertEqual(buf, b"0123\x00\x00\x00\x00") + + +@unittest.skipUnless(hasattr(I2CTarget, "IRQ_ADDR_MATCH_READ"), "IRQ unsupported") +class TestIRQ(unittest.TestCase): + @staticmethod + def irq_handler(i2c_target, buf=bytearray(1)): + flags = i2c_target.irq().flags() + TestIRQ.events[TestIRQ.num_events] = flags + TestIRQ.num_events += 1 + if flags & I2CTarget.IRQ_READ_REQ: + i2c_target.write(b"Y") + if flags & I2CTarget.IRQ_WRITE_REQ: + i2c_target.readinto(buf) + TestIRQ.events[TestIRQ.num_events] = buf[0] + TestIRQ.num_events += 1 + + @classmethod + def setUpClass(cls): + cls.events = [0] * 8 + cls.num_events = 0 + cls.i2c = SoftI2C(**args_controller) + cls.i2c_target = I2CTarget(*args_target, addr=ADDR) + cls.i2c_target.irq( + TestIRQ.irq_handler, + I2CTarget.IRQ_ADDR_MATCH_READ + | I2CTarget.IRQ_ADDR_MATCH_WRITE + | I2CTarget.IRQ_WRITE_REQ + | I2CTarget.IRQ_READ_REQ + | I2CTarget.IRQ_END_READ + | I2CTarget.IRQ_END_WRITE, + hard=True, + ) + config_pull_up() + + @classmethod + def tearDownClass(cls): + cls.i2c_target.deinit() + + def test_scan(self): + TestIRQ.num_events = 0 + self.i2c.scan() + self.assertEqual( + self.events[: self.num_events], + [ + I2CTarget.IRQ_ADDR_MATCH_WRITE, + I2CTarget.IRQ_END_WRITE, + ], + ) + + def test_write(self): + TestIRQ.num_events = 0 + self.i2c.writeto(ADDR, b"XYZ") + self.assertEqual( + self.events[: self.num_events], + [ + I2CTarget.IRQ_ADDR_MATCH_WRITE, + I2CTarget.IRQ_WRITE_REQ, + ord(b"X"), + I2CTarget.IRQ_WRITE_REQ, + ord(b"Y"), + I2CTarget.IRQ_WRITE_REQ, + ord(b"Z"), + I2CTarget.IRQ_END_WRITE, + ], + ) + + def test_read(self): + TestIRQ.num_events = 0 + self.assertEqual(self.i2c.readfrom(ADDR, 1), b"Y") + self.assertEqual( + self.events[: self.num_events], + [ + I2CTarget.IRQ_ADDR_MATCH_READ, + I2CTarget.IRQ_READ_REQ, + I2CTarget.IRQ_READ_REQ, + I2CTarget.IRQ_END_READ, + ], + ) + + def test_write_read(self): + TestIRQ.num_events = 0 + self.i2c.writeto(ADDR, b"X", False) + self.assertEqual(self.i2c.readfrom(ADDR, 1), b"Y") + self.assertEqual( + self.events[: self.num_events], + [ + I2CTarget.IRQ_ADDR_MATCH_WRITE, + I2CTarget.IRQ_WRITE_REQ, + ord(b"X"), + I2CTarget.IRQ_END_WRITE, + I2CTarget.IRQ_ADDR_MATCH_READ, + I2CTarget.IRQ_READ_REQ, + I2CTarget.IRQ_READ_REQ, + I2CTarget.IRQ_END_READ, + ], + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/feature_check/float.py b/tests/feature_check/float.py deleted file mode 100644 index d6d2a99d2429d..0000000000000 --- a/tests/feature_check/float.py +++ /dev/null @@ -1,13 +0,0 @@ -# detect how many bits of precision the floating point implementation has - -try: - float -except NameError: - print(0) -else: - if float("1.0000001") == float("1.0"): - print(30) - elif float("1e300") == float("inf"): - print(32) - else: - print(64) diff --git a/tests/feature_check/float.py.exp b/tests/feature_check/float.py.exp deleted file mode 100644 index 900731ffd51ff..0000000000000 --- a/tests/feature_check/float.py.exp +++ /dev/null @@ -1 +0,0 @@ -64 diff --git a/tests/feature_check/inlineasm_rv32_zba.py b/tests/feature_check/inlineasm_rv32_zba.py new file mode 100644 index 0000000000000..81228819042ee --- /dev/null +++ b/tests/feature_check/inlineasm_rv32_zba.py @@ -0,0 +1,10 @@ +# check if RISC-V 32 inline asm supported Zba opcodes + + +@micropython.asm_rv32 +def f(): + sh1add(a0, a0, a0) + + +f() +print("rv32_zba") diff --git a/tests/feature_check/inlineasm_rv32_zba.py.exp b/tests/feature_check/inlineasm_rv32_zba.py.exp new file mode 100644 index 0000000000000..fde22f5f40090 --- /dev/null +++ b/tests/feature_check/inlineasm_rv32_zba.py.exp @@ -0,0 +1 @@ +rv32_zba diff --git a/tests/feature_check/int_64.py b/tests/feature_check/int_64.py new file mode 100644 index 0000000000000..4d053782ca82b --- /dev/null +++ b/tests/feature_check/int_64.py @@ -0,0 +1,2 @@ +# Check whether 64-bit long integers are supported +print(1 << 62) diff --git a/tests/feature_check/int_64.py.exp b/tests/feature_check/int_64.py.exp new file mode 100644 index 0000000000000..aef5454e66263 --- /dev/null +++ b/tests/feature_check/int_64.py.exp @@ -0,0 +1 @@ +4611686018427387904 diff --git a/tests/feature_check/io_module.py b/tests/feature_check/io_module.py deleted file mode 100644 index 9094e605316ba..0000000000000 --- a/tests/feature_check/io_module.py +++ /dev/null @@ -1,6 +0,0 @@ -try: - import io - - print("io") -except ImportError: - print("no") diff --git a/tests/feature_check/repl_emacs_check.py.exp b/tests/feature_check/repl_emacs_check.py.exp index 82a4e28ee4f84..5fe8ba1cd2df8 100644 --- a/tests/feature_check/repl_emacs_check.py.exp +++ b/tests/feature_check/repl_emacs_check.py.exp @@ -1,5 +1,5 @@ MicroPython \.\+ version -Use \.\+ +Type "help()" for more information. >>> # Check for emacs keys in REPL >>> t = \.\+ >>> t == 2 diff --git a/tests/feature_check/repl_words_move_check.py.exp b/tests/feature_check/repl_words_move_check.py.exp index 82a4e28ee4f84..5fe8ba1cd2df8 100644 --- a/tests/feature_check/repl_words_move_check.py.exp +++ b/tests/feature_check/repl_words_move_check.py.exp @@ -1,5 +1,5 @@ MicroPython \.\+ version -Use \.\+ +Type "help()" for more information. >>> # Check for emacs keys in REPL >>> t = \.\+ >>> t == 2 diff --git a/tests/feature_check/target_info.py b/tests/feature_check/target_info.py index f60f3b319192e..e95530023d7cb 100644 --- a/tests/feature_check/target_info.py +++ b/tests/feature_check/target_info.py @@ -19,5 +19,21 @@ "xtensa", "xtensawin", "rv32imc", -][sys_mpy >> 10] -print(platform, arch) + "rv64imc", +][(sys_mpy >> 10) & 0x0F] +arch_flags = sys_mpy >> 16 +build = getattr(sys.implementation, "_build", "unknown") +thread = getattr(sys.implementation, "_thread", None) + +# Detect how many bits of precision the floating point implementation has. +try: + if float("1.0000001") == float("1.0"): + float_prec = 30 + elif float("1e300") == float("inf"): + float_prec = 32 + else: + float_prec = 64 +except NameError: + float_prec = 0 + +print(platform, arch, arch_flags, build, thread, float_prec, len("α") == 1) diff --git a/tests/float/cmath_fun.py b/tests/float/cmath_fun.py index 39011733b02b4..0037d7c65596c 100644 --- a/tests/float/cmath_fun.py +++ b/tests/float/cmath_fun.py @@ -51,6 +51,9 @@ print("%.5g" % ret) elif type(ret) == tuple: print("%.5g %.5g" % ret) + elif f_name == "exp": + # exp amplifies REPR_C inaccuracies, so we need to check one digit less + print("complex(%.4g, %.4g)" % (real, ret.imag)) else: # some test (eg cmath.sqrt(-0.5)) disagree with CPython with tiny real part real = ret.real diff --git a/tests/float/float_array.py b/tests/float/float_array.py index 3d128da83819f..cfff3b220c657 100644 --- a/tests/float/float_array.py +++ b/tests/float/float_array.py @@ -19,4 +19,10 @@ def test(a): test(array("f")) test(array("d")) -print("{:.4f}".format(array("f", bytes(array("I", [0x3DCCCCCC])))[0])) +# hand-crafted floats, including non-standard nan +for float_hex in (0x3DCCCCCC, 0x7F800024, 0x7FC00004): + f = array("f", bytes(array("I", [float_hex])))[0] + if type(f) is float: + print("{:.4e}".format(f)) + else: + print(f) diff --git a/tests/float/float_format.py b/tests/float/float_format.py index 98ed0eb096fa4..0eb8b232b063a 100644 --- a/tests/float/float_format.py +++ b/tests/float/float_format.py @@ -2,14 +2,25 @@ # general rounding for val in (116, 1111, 1234, 5010, 11111): - print("%.0f" % val) - print("%.1f" % val) - print("%.3f" % val) + print("Test on %d / 1000:" % val) + for fmt in ("%.5e", "%.3e", "%.1e", "%.0e", "%.3f", "%.1f", "%.0f", "%.3g", "%.1g", "%.0g"): + print(fmt, fmt % (val / 1000)) + +# make sure round-up to the next unit is handled properly +for val in range(4, 9): + divi = 10**val + print("Test on 99994 / (10 ** %d):" % val) + for fmt in ("%.5e", "%.3e", "%.1e", "%.0e", "%.3f", "%.1f", "%.0f", "%.3g", "%.1g", "%.0g"): + print(fmt, fmt % (99994 / divi)) # make sure rounding is done at the correct precision for prec in range(8): print(("%%.%df" % prec) % 6e-5) +# make sure trailing zeroes are added properly +for prec in range(8): + print(("%%.%df" % prec) % 1e19) + # check certain cases that had a digit value of 10 render as a ":" character print("%.2e" % float("9" * 51 + "e-39")) print("%.2e" % float("9" * 40 + "e-21")) diff --git a/tests/float/float_format_accuracy.py b/tests/float/float_format_accuracy.py new file mode 100644 index 0000000000000..f9467f9c05d89 --- /dev/null +++ b/tests/float/float_format_accuracy.py @@ -0,0 +1,73 @@ +# Test accuracy of `repr` conversions. +# This test also increases code coverage for corner cases. + +try: + import array, math, random +except ImportError: + print("SKIP") + raise SystemExit + +# The largest errors come from seldom used very small numbers, near the +# limit of the representation. So we keep them out of this test to keep +# the max relative error display useful. +if float("1e-100") == 0.0: + # single-precision + float_type = "f" + float_size = 4 + # testing range + min_expo = -96 # i.e. not smaller than 1.0e-29 + # Expected results (given >=50'000 samples): + # - MICROPY_FLTCONV_IMPL_EXACT: 100% exact conversions + # - MICROPY_FLTCONV_IMPL_APPROX: >=98.53% exact conversions, max relative error <= 1.01e-7 + min_success = 0.980 # with only 1200 samples, the success rate is lower + max_rel_err = 1.1e-7 + # REPR_C is typically used with FORMAT_IMPL_BASIC, which has a larger error + is_REPR_C = float("1.0000001") == float("1.0") + if is_REPR_C: # REPR_C + min_success = 0.83 + max_rel_err = 5.75e-07 +else: + # double-precision + float_type = "d" + float_size = 8 + # testing range + min_expo = -845 # i.e. not smaller than 1.0e-254 + # Expected results (given >=200'000 samples): + # - MICROPY_FLTCONV_IMPL_EXACT: 100% exact conversions + # - MICROPY_FLTCONV_IMPL_APPROX: >=99.83% exact conversions, max relative error <= 2.7e-16 + min_success = 0.997 # with only 1200 samples, the success rate is lower + max_rel_err = 2.7e-16 + + +# Deterministic pseudorandom generator. Designed to be uniform +# on mantissa values and exponents, not on the represented number +def pseudo_randfloat(): + rnd_buff = bytearray(float_size) + for _ in range(float_size): + rnd_buff[_] = random.getrandbits(8) + return array.array(float_type, rnd_buff)[0] + + +random.seed(42) +stats = 0 +N = 1200 +max_err = 0 +for _ in range(N): + f = pseudo_randfloat() + while type(f) is not float or math.isinf(f) or math.isnan(f) or math.frexp(f)[1] <= min_expo: + f = pseudo_randfloat() + + str_f = repr(f) + f2 = float(str_f) + if f2 == f: + stats += 1 + else: + error = abs((f2 - f) / f) + if max_err < error: + max_err = error + +print(N, "values converted") +if stats / N >= min_success and max_err <= max_rel_err: + print("float format accuracy OK") +else: + print("FAILED: repr rate=%.3f%% max_err=%.3e" % (100 * stats / N, max_err)) diff --git a/tests/float/float_format_ints.py b/tests/float/float_format_ints.py index df4444166c5fa..7b7b30c4b340b 100644 --- a/tests/float/float_format_ints.py +++ b/tests/float/float_format_ints.py @@ -12,14 +12,42 @@ print(title, "with format", f_fmt, "gives", f_fmt.format(f)) print(title, "with format", g_fmt, "gives", g_fmt.format(f)) +# The tests below check border cases involving all mantissa bits. +# In case of REPR_C, where the mantissa is missing two bits, the +# the string representation for such numbers might not always be exactly +# the same but nevertheless be correct, so we must allow a few exceptions. +is_REPR_C = float("1.0000001") == float("1.0") + # 16777215 is 2^24 - 1, the largest integer that can be completely held # in a float32. -print("{:f}".format(16777215)) +val_str = "{:f}".format(16777215) + +# When using REPR_C, 16777215.0 is the same as 16777212.0 or 16777214.4 +# (depending on the implementation of pow() function, the result may differ) +if is_REPR_C and (val_str == "16777212.000000" or val_str == "16777214.400000"): + val_str = "16777215.000000" + +print(val_str) + # 4294967040 = 16777215 * 128 is the largest integer that is exactly # represented by a float32 and that will also fit within a (signed) int32. # The upper bound of our integer-handling code is actually double this, # but that constant might cause trouble on systems using 32 bit ints. -print("{:f}".format(2147483520)) +val_str = "{:f}".format(2147483520) + +# When using FLOAT_IMPL_FLOAT, 2147483520.0 == 2147483500.0 +# Both representations are valid, the second being "simpler" +is_float32 = float("1e300") == float("inf") +if is_float32 and val_str == "2147483500.000000": + val_str = "2147483520.000000" + +# When using REPR_C, 2147483520.0 is the same as 2147483200.0 +# Both representations are valid, the second being "simpler" +if is_REPR_C and val_str == "2147483200.000000": + val_str = "2147483520.000000" + +print(val_str) + # Very large positive integers can be a test for precision and resolution. # This is a weird way to represent 1e38 (largest power of 10 for float32). print("{:.6e}".format(float("9" * 30 + "e8"))) diff --git a/tests/float/float_parse_doubleprec.py b/tests/float/float_parse_doubleprec.py index 81fcadcee88b4..c1b0b4823b038 100644 --- a/tests/float/float_parse_doubleprec.py +++ b/tests/float/float_parse_doubleprec.py @@ -19,3 +19,9 @@ print(float("1.00000000000000000000e-307")) print(float("10.0000000000000000000e-308")) print(float("100.000000000000000000e-309")) + +# ensure repr() adds an extra digit when needed for accurate parsing +print(float(repr(float("2.0") ** 100)) == float("2.0") ** 100) + +# ensure repr does not add meaningless extra digits (1.234999999999) +print(repr(1.2345)) diff --git a/tests/float/float_struct_e.py b/tests/float/float_struct_e.py index 403fbc5db4cde..ba4134f3393ed 100644 --- a/tests/float/float_struct_e.py +++ b/tests/float/float_struct_e.py @@ -32,7 +32,7 @@ for i in (j, -j): x = struct.pack("", "=", "^"): for fill in ("", " ", "0", "@"): for sign in ("", "+", "-", " "): - # An empty precision defaults to 6, but when uPy is + # An empty precision defaults to 6, but when MicroPython is # configured to use a float, we can only use a # precision of 6 with numbers less than 10 and still # get results that compare to CPython (which uses @@ -164,7 +164,7 @@ def test_fmt(conv, fill, alignment, sign, prefix, width, precision, type, arg): for alignment in ("", "<", ">", "=", "^"): for fill in ("", " ", "0", "@"): for sign in ("", "+", "-", " "): - # An empty precision defaults to 6, but when uPy is + # An empty precision defaults to 6, but when MicroPython is # configured to use a float, we can only use a # precision of 6 with numbers less than 10 and still # get results that compare to CPython (which uses diff --git a/tests/float/string_format_fp30.py b/tests/float/string_format_fp30.py deleted file mode 100644 index 5f0b213daa342..0000000000000 --- a/tests/float/string_format_fp30.py +++ /dev/null @@ -1,42 +0,0 @@ -def test(fmt, *args): - print("{:8s}".format(fmt) + ">" + fmt.format(*args) + "<") - - -test("{:10.4}", 123.456) -test("{:10.4e}", 123.456) -test("{:10.4e}", -123.456) -# test("{:10.4f}", 123.456) -# test("{:10.4f}", -123.456) -test("{:10.4g}", 123.456) -test("{:10.4g}", -123.456) -test("{:10.4n}", 123.456) -test("{:e}", 100) -test("{:f}", 200) -test("{:g}", 300) - -test("{:10.4E}", 123.456) -test("{:10.4E}", -123.456) -# test("{:10.4F}", 123.456) -# test("{:10.4F}", -123.456) -test("{:10.4G}", 123.456) -test("{:10.4G}", -123.456) - -test("{:06e}", float("inf")) -test("{:06e}", float("-inf")) -test("{:06e}", float("nan")) - -# The following fails right now -# test("{:10.1}", 0.0) - -print("%.0f" % (1.750000 % 0.08333333333)) -# Below isn't compatible with single-precision float -# print("%.1f" % (1.750000 % 0.08333333333)) -# print("%.2f" % (1.750000 % 0.08333333333)) -# print("%.12f" % (1.750000 % 0.08333333333)) - -# tests for errors in format string - -try: - "{:10.1b}".format(0.0) -except ValueError: - print("ValueError") diff --git a/tests/float/string_format_modulo.py b/tests/float/string_format_modulo.py index 3c206b7393588..55314643351db 100644 --- a/tests/float/string_format_modulo.py +++ b/tests/float/string_format_modulo.py @@ -6,7 +6,7 @@ print("%u" % 1.0) # these 3 have different behaviour in Python 3.x versions -# uPy raises a TypeError, following Python 3.5 (earlier versions don't) +# MicroPython raises a TypeError, following Python 3.5 (earlier versions don't) # print("%x" % 18.0) # print("%o" % 18.0) # print("%X" % 18.0) diff --git a/tests/float/string_format_modulo3.py b/tests/float/string_format_modulo3.py index f9d9c43cdf42d..f8aeeda20f29c 100644 --- a/tests/float/string_format_modulo3.py +++ b/tests/float/string_format_modulo3.py @@ -1,3 +1,3 @@ -# uPy and CPython outputs differ for the following +# Test corner cases where MicroPython and CPython outputs used to differ in the past print("%.1g" % -9.9) # round up 'g' with '-' sign print("%.2g" % 99.9) # round up diff --git a/tests/float/string_format_modulo3.py.exp b/tests/float/string_format_modulo3.py.exp deleted file mode 100644 index 71432b3404519..0000000000000 --- a/tests/float/string_format_modulo3.py.exp +++ /dev/null @@ -1,2 +0,0 @@ --10 -100 diff --git a/tests/frozen/frozentest.py b/tests/frozen/frozentest.py index 78cdd60bf0431..74bdfb3b267f2 100644 --- a/tests/frozen/frozentest.py +++ b/tests/frozen/frozentest.py @@ -1,4 +1,4 @@ -print("uPy") +print("interned") print("a long string that is not interned") print("a string that has unicode αβγ chars") print(b"bytes 1234\x01") diff --git a/tests/import/builtin_ext.py b/tests/import/builtin_ext.py index 9d2344d400dae..79ee901ea6774 100644 --- a/tests/import/builtin_ext.py +++ b/tests/import/builtin_ext.py @@ -1,3 +1,9 @@ +try: + import uos, utime +except ImportError: + print("SKIP") + raise SystemExit + # Verify that sys is a builtin. import sys diff --git a/tests/import/builtin_import.py b/tests/import/builtin_import.py index 734498d1be47b..364b0bae912ae 100644 --- a/tests/import/builtin_import.py +++ b/tests/import/builtin_import.py @@ -20,3 +20,12 @@ __import__("xyz", None, None, None, -1) except ValueError: print("ValueError") + +# globals is not checked for level=0 +__import__("builtins", "globals") + +# globals must be a dict (or None) for level>0 +try: + __import__("builtins", "globals", None, None, 1) +except TypeError: + print("TypeError") diff --git a/tests/import/import_broken.py b/tests/import/import_broken.py index 3c7cf4a498528..440922157ef10 100644 --- a/tests/import/import_broken.py +++ b/tests/import/import_broken.py @@ -1,3 +1,9 @@ +try: + Exception.__class__ +except AttributeError: + print("SKIP") + raise SystemExit + import sys, pkg # Modules we import are usually added to sys.modules. diff --git a/tests/import/import_file.py b/tests/import/import_file.py index 90ec4e41e77aa..4cf307641c62d 100644 --- a/tests/import/import_file.py +++ b/tests/import/import_file.py @@ -1,3 +1,7 @@ +if "__file__" not in globals(): + print("SKIP") + raise SystemExit + import import1b print(import1b.__file__) diff --git a/tests/import/import_override.py b/tests/import/import_override.py index 029ebe54c1f70..0144e78cb9f32 100644 --- a/tests/import/import_override.py +++ b/tests/import/import_override.py @@ -2,6 +2,10 @@ def custom_import(name, globals, locals, fromlist, level): + # CPython always tries to import _io, so just let that through as-is. + if name == "_io": + return orig_import(name, globals, locals, fromlist, level) + print("import", name, fromlist, level) class M: diff --git a/tests/import/import_override.py.exp b/tests/import/import_override.py.exp deleted file mode 100644 index 365248da6d847..0000000000000 --- a/tests/import/import_override.py.exp +++ /dev/null @@ -1,2 +0,0 @@ -import import1b None 0 -456 diff --git a/tests/import/import_override2.py b/tests/import/import_override2.py new file mode 100644 index 0000000000000..25aac44fe98d0 --- /dev/null +++ b/tests/import/import_override2.py @@ -0,0 +1,18 @@ +# test overriding __import__ combined with importing from the filesystem + + +def custom_import(name, globals, locals, fromlist, level): + if level > 0: + print("import", name, fromlist, level) + return orig_import(name, globals, locals, fromlist, level) + + +orig_import = __import__ +try: + __import__("builtins").__import__ = custom_import +except AttributeError: + print("SKIP") + raise SystemExit + +# import calls __import__ behind the scenes +import pkg7.subpkg1.subpkg2.mod3 diff --git a/tests/import/import_pkg7.py.exp b/tests/import/import_pkg7.py.exp deleted file mode 100644 index 8f21a615f6a61..0000000000000 --- a/tests/import/import_pkg7.py.exp +++ /dev/null @@ -1,8 +0,0 @@ -pkg __name__: pkg7 -pkg __name__: pkg7.subpkg1 -pkg __name__: pkg7.subpkg1.subpkg2 -mod1 -mod2 -mod1.foo -mod2.bar -ImportError diff --git a/tests/import/import_pkg9.py b/tests/import/import_pkg9.py index 4de028494f1a2..c2e0b618b6969 100644 --- a/tests/import/import_pkg9.py +++ b/tests/import/import_pkg9.py @@ -13,4 +13,4 @@ import pkg9.mod2 pkg9.mod1() -print(pkg9.mod2.__name__, type(pkg9.mod2).__name__) +print(pkg9.mod2.__name__, type(pkg9.mod2)) diff --git a/tests/import/import_star.py b/tests/import/import_star.py new file mode 100644 index 0000000000000..2cb21b877d728 --- /dev/null +++ b/tests/import/import_star.py @@ -0,0 +1,59 @@ +# test `from package import *` conventions, including __all__ support +# +# This test requires MICROPY_CONFIG_ROM_LEVEL_AT_LEAST_BASIC_FEATURES + +try: + next(iter([]), 42) +except TypeError: + # 2-argument version of next() not supported + # we are probably not at MICROPY_CONFIG_ROM_LEVEL_BASIC_FEATURES + print("SKIP") + raise SystemExit + +# 1. test default visibility +from pkgstar_default import * + +print("visibleFun" in globals()) +print("VisibleClass" in globals()) +print("_hiddenFun" in globals()) +print("_HiddenClass" in globals()) +print(visibleFun()) + +# 2. test explicit visibility as defined by __all__ (as an array) +from pkgstar_all_array import * + +print("publicFun" in globals()) +print("PublicClass" in globals()) +print("unlistedFun" in globals()) +print("UnlistedClass" in globals()) +print("_privateFun" in globals()) +print("_PrivateClass" in globals()) +print(publicFun()) +# test dynamic import as used in asyncio +print("dynamicFun" in globals()) +print(dynamicFun()) + +# 3. test explicit visibility as defined by __all__ (as an tuple) +from pkgstar_all_tuple import * + +print("publicFun2" in globals()) +print("PublicClass2" in globals()) +print("unlistedFun2" in globals()) +print("UnlistedClass2" in globals()) +print(publicFun2()) + +# 4. test reporting of missing entries in __all__ +try: + from pkgstar_all_miss import * + + print("missed detection of incorrect __all__ definition") +except AttributeError as er: + print("AttributeError triggered for bad __all__ definition") + +# 5. test reporting of invalid __all__ definition +try: + from pkgstar_all_inval import * + + print("missed detection of incorrect __all__ definition") +except TypeError as er: + print("TypeError triggered for bad __all__ definition") diff --git a/tests/import/import_star_error.py b/tests/import/import_star_error.py index 9e1757b6ef5a2..73d9c863d6f51 100644 --- a/tests/import/import_star_error.py +++ b/tests/import/import_star_error.py @@ -1,5 +1,10 @@ # test errors with import * +if not hasattr(object, "__init__"): + # target doesn't have MICROPY_CPYTHON_COMPAT enabled, so doesn't check for "import *" + print("SKIP") + raise SystemExit + # 'import *' is not allowed in function scope try: exec("def foo(): from x import *") diff --git a/tests/import/module_getattr.py.exp b/tests/import/module_getattr.py.exp deleted file mode 100644 index bc59c12aa16bd..0000000000000 --- a/tests/import/module_getattr.py.exp +++ /dev/null @@ -1 +0,0 @@ -False diff --git a/tests/import/pkg7/subpkg1/subpkg2/mod3.py b/tests/import/pkg7/subpkg1/subpkg2/mod3.py index b0f4279fcf57b..f7f4c1e65f46f 100644 --- a/tests/import/pkg7/subpkg1/subpkg2/mod3.py +++ b/tests/import/pkg7/subpkg1/subpkg2/mod3.py @@ -5,7 +5,9 @@ print(bar) # attempted relative import beyond top-level package +# On older versions of CPython (eg 3.8) this is a ValueError, but on +# newer CPython (eg 3.11) and MicroPython it's an ImportError. try: from .... import mod1 -except ImportError: +except (ImportError, ValueError): print("ImportError") diff --git a/tests/import/pkgstar_all_array/__init__.py b/tests/import/pkgstar_all_array/__init__.py new file mode 100644 index 0000000000000..03b012123fe0c --- /dev/null +++ b/tests/import/pkgstar_all_array/__init__.py @@ -0,0 +1,49 @@ +__all__ = ["publicFun", "PublicClass", "dynamicFun"] + + +# Definitions below should always be imported by a star import +def publicFun(): + return 1 + + +class PublicClass: + def __init__(self): + self._val = 1 + + +# If __all__ support is enabled, definitions below +# should not be imported by a star import +def unlistedFun(): + return 0 + + +class UnlistedClass: + def __init__(self): + self._val = 0 + + +# Definitions below should be not be imported by a star import +# (they start with an underscore, and are not listed in __all__) +def _privateFun(): + return -1 + + +class _PrivateClass: + def __init__(self): + self._val = -1 + + +# Test lazy loaded function, as used by extmod/asyncio: +# Works with a star import only if __all__ support is enabled +_attrs = { + "dynamicFun": "funcs", +} + + +def __getattr__(attr): + mod = _attrs.get(attr, None) + if mod is None: + raise AttributeError(attr) + value = getattr(__import__(mod, globals(), locals(), True, 1), attr) + globals()[attr] = value + return value diff --git a/tests/import/pkgstar_all_array/funcs.py b/tests/import/pkgstar_all_array/funcs.py new file mode 100644 index 0000000000000..7540d70f66bce --- /dev/null +++ b/tests/import/pkgstar_all_array/funcs.py @@ -0,0 +1,2 @@ +def dynamicFun(): + return 777 diff --git a/tests/import/pkgstar_all_inval/__init__.py b/tests/import/pkgstar_all_inval/__init__.py new file mode 100644 index 0000000000000..7022476c19be8 --- /dev/null +++ b/tests/import/pkgstar_all_inval/__init__.py @@ -0,0 +1 @@ +__all__ = 42 diff --git a/tests/import/pkgstar_all_miss/__init__.py b/tests/import/pkgstar_all_miss/__init__.py new file mode 100644 index 0000000000000..f9bbb538072bf --- /dev/null +++ b/tests/import/pkgstar_all_miss/__init__.py @@ -0,0 +1,8 @@ +__all__ = ("existingFun", "missingFun") + + +def existingFun(): + return None + + +# missingFun is not defined, should raise an error on import diff --git a/tests/import/pkgstar_all_tuple/__init__.py b/tests/import/pkgstar_all_tuple/__init__.py new file mode 100644 index 0000000000000..433ddc8e97639 --- /dev/null +++ b/tests/import/pkgstar_all_tuple/__init__.py @@ -0,0 +1,22 @@ +__all__ = ("publicFun2", "PublicClass2") + + +# Definitions below should always be imported by a star import +def publicFun2(): + return 2 + + +class PublicClass2: + def __init__(self): + self._val = 2 + + +# If __all__ support is enabled, definitions below +# should not be imported by a star import +def unlistedFun2(): + return 0 + + +class UnlistedClass2: + def __init__(self): + self._val = 0 diff --git a/tests/import/pkgstar_default/__init__.py b/tests/import/pkgstar_default/__init__.py new file mode 100644 index 0000000000000..4947e4ce7f180 --- /dev/null +++ b/tests/import/pkgstar_default/__init__.py @@ -0,0 +1,20 @@ +# When __all__ is undefined, star import should only +# show objects that do not start with an underscore + + +def visibleFun(): + return 42 + + +class VisibleClass: + def __init__(self): + self._val = 42 + + +def _hiddenFun(): + return -1 + + +class _HiddenClass: + def __init__(self): + self._val = -1 diff --git a/tests/inlineasm/rv32/asmzba.py b/tests/inlineasm/rv32/asmzba.py new file mode 100644 index 0000000000000..75f3573c864d6 --- /dev/null +++ b/tests/inlineasm/rv32/asmzba.py @@ -0,0 +1,18 @@ +@micropython.asm_rv32 +def test_sh1add(a0, a1): + sh1add(a0, a0, a1) + + +@micropython.asm_rv32 +def test_sh2add(a0, a1): + sh2add(a0, a0, a1) + + +@micropython.asm_rv32 +def test_sh3add(a0, a1): + sh3add(a0, a0, a1) + + +print(hex(test_sh1add(10, 20))) +print(hex(test_sh2add(10, 20))) +print(hex(test_sh3add(10, 20))) diff --git a/tests/inlineasm/rv32/asmzba.py.exp b/tests/inlineasm/rv32/asmzba.py.exp new file mode 100644 index 0000000000000..5f56bd95642a4 --- /dev/null +++ b/tests/inlineasm/rv32/asmzba.py.exp @@ -0,0 +1,3 @@ +0x28 +0x3c +0x64 diff --git a/tests/inlineasm/thumb/asmerrors.py b/tests/inlineasm/thumb/asmerrors.py new file mode 100644 index 0000000000000..a26f9322540fd --- /dev/null +++ b/tests/inlineasm/thumb/asmerrors.py @@ -0,0 +1,4 @@ +try: + exec("@micropython.asm_thumb\ndef l():\n a = di(a2, a2, -1)") +except SyntaxError as e: + print(e) diff --git a/tests/inlineasm/thumb/asmerrors.py.exp b/tests/inlineasm/thumb/asmerrors.py.exp new file mode 100644 index 0000000000000..7590f171e5184 --- /dev/null +++ b/tests/inlineasm/thumb/asmerrors.py.exp @@ -0,0 +1 @@ +expecting an assembler instruction diff --git a/tests/inlineasm/xtensa/asmargs.py b/tests/inlineasm/xtensa/asmargs.py new file mode 100644 index 0000000000000..2bfccfcc69bb2 --- /dev/null +++ b/tests/inlineasm/xtensa/asmargs.py @@ -0,0 +1,44 @@ +# test passing arguments + + +@micropython.asm_xtensa +def arg0(): + movi(a2, 1) + + +print(arg0()) + + +@micropython.asm_xtensa +def arg1(a2): + addi(a2, a2, 1) + + +print(arg1(1)) + + +@micropython.asm_xtensa +def arg2(a2, a3): + add(a2, a2, a3) + + +print(arg2(1, 2)) + + +@micropython.asm_xtensa +def arg3(a2, a3, a4): + add(a2, a2, a3) + add(a2, a2, a4) + + +print(arg3(1, 2, 3)) + + +@micropython.asm_xtensa +def arg4(a2, a3, a4, a5): + add(a2, a2, a3) + add(a2, a2, a4) + add(a2, a2, a5) + + +print(arg4(1, 2, 3, 4)) diff --git a/tests/inlineasm/xtensa/asmargs.py.exp b/tests/inlineasm/xtensa/asmargs.py.exp new file mode 100644 index 0000000000000..e33a6964f46e8 --- /dev/null +++ b/tests/inlineasm/xtensa/asmargs.py.exp @@ -0,0 +1,5 @@ +1 +2 +3 +6 +10 diff --git a/tests/inlineasm/xtensa/asmarith.py b/tests/inlineasm/xtensa/asmarith.py new file mode 100644 index 0000000000000..1c0934eb7a631 --- /dev/null +++ b/tests/inlineasm/xtensa/asmarith.py @@ -0,0 +1,119 @@ +@micropython.asm_xtensa +def f1(a2): + abs_(a2, a2) + + +for value in (10, -10, 0): + print(f1(value)) + + +ADDMI_TEMPLATE = """ +@micropython.asm_xtensa +def f1(a2) -> int: + addmi(a2, a2, {}) +print(f1(0)) +""" + +for value in (-32768, -32767, 32512, 32513, 0): + try: + exec(ADDMI_TEMPLATE.format(value)) + except SyntaxError as error: + print(error) + + +@micropython.asm_xtensa +def a2(a2, a3) -> int: + addx2(a2, a2, a3) + + +@micropython.asm_xtensa +def a4(a2, a3) -> int: + addx4(a2, a2, a3) + + +@micropython.asm_xtensa +def a8(a2, a3) -> int: + addx8(a2, a2, a3) + + +@micropython.asm_xtensa +def s2(a2, a3) -> int: + subx2(a2, a2, a3) + + +@micropython.asm_xtensa +def s4(a2, a3) -> int: + subx4(a2, a2, a3) + + +@micropython.asm_xtensa +def s8(a2, a3) -> int: + subx8(a2, a2, a3) + + +for first, second in ((100, 100), (-100, 100), (-100, -100), (100, -100)): + print("a2", a2(first, second)) + print("a4", a4(first, second)) + print("a8", a8(first, second)) + print("s2", s2(first, second)) + print("s4", s4(first, second)) + print("s8", s8(first, second)) + + +@micropython.asm_xtensa +def f5(a2) -> int: + neg(a2, a2) + + +for value in (0, -100, 100): + print(f5(value)) + + +@micropython.asm_xtensa +def f6(): + movi(a2, 0x100) + movi(a3, 1) + add(a2, a2, a3) + addi(a2, a2, 1) + addi(a2, a2, -2) + sub(a2, a2, a3) + + +print(hex(f6())) + + +@micropython.asm_xtensa +def f7(): + movi(a2, 0x10FF) + movi(a3, 1) + and_(a4, a2, a3) + or_(a4, a4, a3) + movi(a3, 0x200) + xor(a2, a4, a3) + + +print(hex(f7())) + + +@micropython.asm_xtensa +def f8(a2, a3): + add_n(a2, a2, a3) + + +print(f8(100, 200)) + + +@micropython.asm_xtensa +def f9(a2): + addi_n(a2, a2, 1) + + +print(f9(100)) + + +@micropython.asm_xtensa +def f10(a2, a3) -> uint: + mull(a2, a2, a3) + + +print(hex(f10(0xC0000000, 2))) diff --git a/tests/inlineasm/xtensa/asmarith.py.exp b/tests/inlineasm/xtensa/asmarith.py.exp new file mode 100644 index 0000000000000..7aba46a27d9b5 --- /dev/null +++ b/tests/inlineasm/xtensa/asmarith.py.exp @@ -0,0 +1,40 @@ +10 +10 +0 +-32768 +-32767 is not a multiple of 256 +32512 +'addmi' integer 32513 isn't within range -32768..32512 +0 +a2 300 +a4 500 +a8 900 +s2 100 +s4 300 +s8 700 +a2 -100 +a4 -300 +a8 -700 +s2 -300 +s4 -500 +s8 -900 +a2 -300 +a4 -500 +a8 -900 +s2 -100 +s4 -300 +s8 -700 +a2 100 +a4 300 +a8 700 +s2 300 +s4 500 +s8 900 +0 +100 +-100 +0xff +0x201 +300 +101 +0x80000000 diff --git a/tests/inlineasm/xtensa/asmbranch.py b/tests/inlineasm/xtensa/asmbranch.py new file mode 100644 index 0000000000000..22bcd5a7c71a3 --- /dev/null +++ b/tests/inlineasm/xtensa/asmbranch.py @@ -0,0 +1,299 @@ +# test branch instructions + + +@micropython.asm_xtensa +def tball(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + ball(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tball(0xFFFFFFFF, 0xFFFFFFFF)) +print(tball(0xFFFEFFFF, 0xFFFFFFFF)) +print(tball(0x00000000, 0xFFFFFFFF)) +print(tball(0xFFFFFFFF, 0x01010101)) + + +@micropython.asm_xtensa +def tbany(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bany(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbany(0xFFFFFFFF, 0xFFFFFFFF)) +print(tbany(0xFFFEFFFF, 0xFFFFFFFF)) +print(tbany(0x00000000, 0xFFFFFFFF)) +print(tbany(0xFFFFFFFF, 0x01010101)) + + +@micropython.asm_xtensa +def tbbc(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bbc(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbbc(0xFFFFFFFF, 4)) +print(tbbc(0xFFFEFFFF, 16)) +print(tbbc(0x00000000, 1)) + + +BBCI_TEMPLATE = """ +@micropython.asm_xtensa +def tbbci(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bbci(a3, {}, end) + movi(a2, -1) + label(end) + +print(tbbci({})) +""" + + +for value, bit in ((0xFFFFFFFF, 4), (0xFFFEFFFF, 16), (0x00000000, 1)): + try: + exec(BBCI_TEMPLATE.format(bit, value)) + except SyntaxError as error: + print(error) + + +@micropython.asm_xtensa +def tbbs(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bbs(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbbs(0x00000000, 4)) +print(tbbs(0x00010000, 16)) +print(tbbs(0xFFFFFFFF, 1)) + + +BBSI_TEMPLATE = """ +@micropython.asm_xtensa +def tbbsi(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bbsi(a3, {}, end) + movi(a2, -1) + label(end) + +print(tbbsi({})) +""" + + +for value, bit in ((0x00000000, 4), (0x00010000, 16), (0xFFFFFFFF, 1)): + try: + exec(BBSI_TEMPLATE.format(bit, value)) + except SyntaxError as error: + print(error) + + +@micropython.asm_xtensa +def tbeq(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + beq(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbeq(0x00000000, 0x00000000)) +print(tbeq(0x00010000, 0x00000000)) +print(tbeq(0xFFFFFFFF, 0xFFFFFFFF)) + + +@micropython.asm_xtensa +def tbeqz(a2) -> int: + mov(a3, a2) + movi(a2, 0) + beqz(a3, end) + movi(a2, -1) + label(end) + + +print(tbeqz(0)) +print(tbeqz(0x12345678)) +print(tbeqz(-1)) + + +@micropython.asm_xtensa +def tbge(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bge(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbge(0x00000000, 0x00000000)) +print(tbge(0x00010000, 0x00000000)) +print(tbge(0xF0000000, 0xFFFFFFFF)) + + +@micropython.asm_xtensa +def tbgeu(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bgeu(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbgeu(0x00000000, 0x00000000)) +print(tbgeu(0x00010000, 0x00000000)) +print(tbgeu(0xF0000000, 0xFFFFFFFF)) +print(tbgeu(0xFFFFFFFF, 0xF0000000)) + + +@micropython.asm_xtensa +def tbgez(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bgez(a3, end) + movi(a2, -1) + label(end) + + +print(tbgez(0)) +print(tbgez(0x12345678)) +print(tbgez(-1)) + + +@micropython.asm_xtensa +def tblt(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + blt(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tblt(0x00000000, 0x00000000)) +print(tblt(0x00010000, 0x00000000)) +print(tblt(0xF0000000, 0xFFFFFFFF)) + + +@micropython.asm_xtensa +def tbltu(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bltu(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbltu(0x00000000, 0x00000000)) +print(tbltu(0x00010000, 0x00000000)) +print(tbltu(0xF0000000, 0xFFFFFFFF)) +print(tbltu(0xFFFFFFFF, 0xF0000000)) + + +@micropython.asm_xtensa +def tbltz(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bltz(a3, end) + movi(a2, -1) + label(end) + + +print(tbltz(0)) +print(tbltz(0x12345678)) +print(tbltz(-1)) + + +@micropython.asm_xtensa +def tbnall(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bnall(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbnall(0xFFFFFFFF, 0xFFFFFFFF)) +print(tbnall(0xFFFEFFFF, 0xFFFFFFFF)) +print(tbnall(0x00000000, 0xFFFFFFFF)) +print(tbnall(0xFFFFFFFF, 0x01010101)) + + +@micropython.asm_xtensa +def tbne(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bne(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbne(0x00000000, 0x00000000)) +print(tbne(0x00010000, 0x00000000)) +print(tbne(0xFFFFFFFF, 0xFFFFFFFF)) + + +@micropython.asm_xtensa +def tbnez(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bnez(a3, end) + movi(a2, -1) + label(end) + + +print(tbnez(0)) +print(tbnez(0x12345678)) +print(tbnez(-1)) + + +@micropython.asm_xtensa +def tbnone(a2, a3) -> int: + mov(a4, a2) + movi(a2, 0) + bnone(a4, a3, end) + movi(a2, -1) + label(end) + + +print(tbnone(0xFFFFFFFF, 0xFFFFFFFF)) +print(tbnone(0xFFFEFFFF, 0xFFFFFFFF)) +print(tbnone(0x00000000, 0xFFFFFFFF)) +print(tbnone(0x10101010, 0x01010101)) + + +@micropython.asm_xtensa +def tbeqz_n(a2) -> int: + mov(a3, a2) + movi(a2, 0) + beqz_n(a3, end) + movi(a2, -1) + label(end) + + +print(tbeqz_n(0)) +print(tbeqz_n(0x12345678)) +print(tbeqz_n(-1)) + + +@micropython.asm_xtensa +def tbnez_n(a2) -> int: + mov(a3, a2) + movi(a2, 0) + bnez(a3, end) + movi(a2, -1) + label(end) + + +print(tbnez_n(0)) +print(tbnez_n(0x12345678)) +print(tbnez_n(-1)) diff --git a/tests/inlineasm/xtensa/asmbranch.py.exp b/tests/inlineasm/xtensa/asmbranch.py.exp new file mode 100644 index 0000000000000..319a4b435ef2f --- /dev/null +++ b/tests/inlineasm/xtensa/asmbranch.py.exp @@ -0,0 +1,66 @@ +0 +-1 +-1 +0 +0 +0 +-1 +0 +-1 +0 +0 +-1 +0 +0 +-1 +0 +0 +-1 +0 +0 +0 +-1 +0 +0 +-1 +-1 +0 +0 +-1 +0 +0 +-1 +0 +0 +0 +-1 +-1 +-1 +0 +-1 +-1 +0 +-1 +-1 +-1 +0 +-1 +0 +0 +-1 +-1 +0 +-1 +-1 +0 +0 +-1 +-1 +0 +0 +0 +-1 +-1 +-1 +0 +0 diff --git a/tests/inlineasm/xtensa/asmjump.py b/tests/inlineasm/xtensa/asmjump.py new file mode 100644 index 0000000000000..f41c9482319da --- /dev/null +++ b/tests/inlineasm/xtensa/asmjump.py @@ -0,0 +1,26 @@ +@micropython.asm_xtensa +def jump() -> int: + movi(a2, 0) + j(NEXT) + addi(a2, a2, 1) + j(DONE) + label(NEXT) + addi(a2, a2, 2) + label(DONE) + + +print(jump()) + + +@micropython.asm_xtensa +def jumpx() -> int: + call0(ENTRY) + label(ENTRY) + movi(a2, 0) + addi(a3, a0, 12) + jx(a3) + movi(a2, 1) + movi(a2, 2) + + +print(jumpx()) diff --git a/tests/inlineasm/xtensa/asmjump.py.exp b/tests/inlineasm/xtensa/asmjump.py.exp new file mode 100644 index 0000000000000..51993f072d583 --- /dev/null +++ b/tests/inlineasm/xtensa/asmjump.py.exp @@ -0,0 +1,2 @@ +2 +2 diff --git a/tests/inlineasm/xtensa/asmloadstore.py b/tests/inlineasm/xtensa/asmloadstore.py new file mode 100644 index 0000000000000..b185e30520cc9 --- /dev/null +++ b/tests/inlineasm/xtensa/asmloadstore.py @@ -0,0 +1,98 @@ +import array + +# On the 8266 the generated code gets put into the IRAM segment, which is only +# word-addressable. Therefore, to test byte and halfword load/store opcodes +# some memory must be reserved in the DRAM segment. + +BYTE_DATA = array.array("B", (0x11, 0x22, 0x33, 0x44)) +WORD_DATA = array.array("h", (100, 200, -100, -200)) +DWORD_DATA = array.array("i", (100_000, -200_000, 300_000, -400_000)) + + +@micropython.asm_xtensa +def tl32r() -> int: + nop() + j(CODE) + align(4) + label(DATA) + data(1, 1, 2, 3, 4, 5, 6, 7) + align(4) + label(CODE) + nop_n() + nop_n() + l32r(a2, DATA) + + +print(hex(tl32r())) + + +@micropython.asm_xtensa +def tl32i() -> uint: + call0(ENTRY) + label(ENTRY) + l32i(a2, a0, 0) + + +print(hex(tl32i())) + + +@micropython.asm_xtensa +def tl8ui(a2) -> uint: + mov(a3, a2) + l8ui(a2, a3, 1) + + +print(hex(tl8ui(BYTE_DATA))) + + +@micropython.asm_xtensa +def tl16ui(a2) -> uint: + mov(a3, a2) + l16ui(a2, a3, 2) + + +print(tl16ui(WORD_DATA)) + + +@micropython.asm_xtensa +def tl16si(a2) -> int: + mov(a3, a2) + l16si(a2, a3, 6) + + +print(tl16si(WORD_DATA)) + + +@micropython.asm_xtensa +def ts8i(a2, a3): + s8i(a3, a2, 1) + + +ts8i(BYTE_DATA, 0xFF) +print(BYTE_DATA) + + +@micropython.asm_xtensa +def ts16i(a2, a3): + s16i(a3, a2, 2) + + +ts16i(WORD_DATA, -123) +print(WORD_DATA) + + +@micropython.asm_xtensa +def ts32i(a2, a3) -> uint: + s32i(a3, a2, 4) + + +ts32i(DWORD_DATA, -123456) +print(DWORD_DATA) + + +@micropython.asm_xtensa +def tl32i_n(a2) -> uint: + l32i_n(a2, a2, 8) + + +print(tl32i_n(DWORD_DATA)) diff --git a/tests/inlineasm/xtensa/asmloadstore.py.exp b/tests/inlineasm/xtensa/asmloadstore.py.exp new file mode 100644 index 0000000000000..e6672df6f8132 --- /dev/null +++ b/tests/inlineasm/xtensa/asmloadstore.py.exp @@ -0,0 +1,9 @@ +0x4030201 +0xf8002022 +0x22 +200 +-200 +array('B', [17, 255, 51, 68]) +array('h', [100, -123, -100, -200]) +array('i', [100000, -123456, 300000, -400000]) +300000 diff --git a/tests/inlineasm/xtensa/asmmisc.py b/tests/inlineasm/xtensa/asmmisc.py new file mode 100644 index 0000000000000..271ab836625dc --- /dev/null +++ b/tests/inlineasm/xtensa/asmmisc.py @@ -0,0 +1,25 @@ +@micropython.asm_xtensa +def tnop(a2, a3, a4, a5): + nop() + + +out2 = tnop(0x100, 0x200, 0x300, 0x400) +print(out2 == 0x100) + + +@micropython.asm_xtensa +def tnop_n(a2, a3, a4, a5): + nop_n() + + +out2 = tnop_n(0x100, 0x200, 0x300, 0x400) +print(out2 == 0x100) + + +@micropython.asm_xtensa +def tmov_n(a2, a3): + mov_n(a4, a3) + add(a2, a4, a3) + + +print(tmov_n(0, 1)) diff --git a/tests/inlineasm/xtensa/asmmisc.py.exp b/tests/inlineasm/xtensa/asmmisc.py.exp new file mode 100644 index 0000000000000..eefaa35daf0f1 --- /dev/null +++ b/tests/inlineasm/xtensa/asmmisc.py.exp @@ -0,0 +1,3 @@ +True +True +2 diff --git a/tests/inlineasm/xtensa/asmshift.py b/tests/inlineasm/xtensa/asmshift.py new file mode 100644 index 0000000000000..271ca1ccd494c --- /dev/null +++ b/tests/inlineasm/xtensa/asmshift.py @@ -0,0 +1,137 @@ +@micropython.asm_xtensa +def lsl1(a2): + slli(a2, a2, 1) + + +print(hex(lsl1(0x123))) + + +@micropython.asm_xtensa +def lsl23(a2): + slli(a2, a2, 23) + + +print(hex(lsl23(1))) + + +@micropython.asm_xtensa +def lsr1(a2): + srli(a2, a2, 1) + + +print(hex(lsr1(0x123))) + + +@micropython.asm_xtensa +def lsr15(a2): + srli(a2, a2, 15) + + +print(hex(lsr15(0x80000000))) + + +@micropython.asm_xtensa +def asr1(a2): + srai(a2, a2, 1) + + +print(hex(asr1(0x123))) + + +@micropython.asm_xtensa +def asr31(a2): + srai(a2, a2, 31) + + +print(hex(asr31(0x80000000))) + + +@micropython.asm_xtensa +def lsl1r(a2): + movi(a3, 1) + ssl(a3) + sll(a2, a2) + + +print(hex(lsl1r(0x123))) + + +@micropython.asm_xtensa +def lsr1r(a2): + movi(a3, 1) + ssr(a3) + srl(a2, a2) + + +print(hex(lsr1r(0x123))) + + +@micropython.asm_xtensa +def asr1r(a2): + movi(a3, 1) + ssr(a3) + sra(a2, a2) + + +print(hex(asr1r(0x123))) + + +@micropython.asm_xtensa +def sll9(a2): + ssai(9) + sll(a2, a2) + + +print(hex(sll9(1))) + + +@micropython.asm_xtensa +def srlr(a2, a3): + ssa8l(a3) + srl(a2, a2) + + +print(hex(srlr(0x12340000, 2))) + + +@micropython.asm_xtensa +def sllr(a2, a3): + ssa8b(a3) + sll(a2, a2) + + +print(hex(sllr(0x1234, 2))) + + +@micropython.asm_xtensa +def srcr(a2, a3, a4): + ssr(a4) + src(a2, a2, a3) + + +print(hex(srcr(0x00000001, 0x80000000, 2))) + + +@micropython.asm_xtensa +def srai24(a2): + srai(a2, a2, 24) + + +print(hex(srai24(0x12345678))) + + +@micropython.asm_xtensa +def nsar(a2, a3): + nsa(a2, a3) + + +print(nsar(0x12345678, 0)) +print(nsar(0x12345678, -1)) + + +@micropython.asm_xtensa +def nsaur(a2, a3): + nsau(a2, a3) + + +print(nsaur(0x12345678, 0)) diff --git a/tests/inlineasm/xtensa/asmshift.py.exp b/tests/inlineasm/xtensa/asmshift.py.exp new file mode 100644 index 0000000000000..3e2bb3b4aefa9 --- /dev/null +++ b/tests/inlineasm/xtensa/asmshift.py.exp @@ -0,0 +1,17 @@ +0x246 +0x800000 +0x91 +0x10000 +0x91 +-0x1 +0x246 +0x91 +0x91 +0x800000 +0x1234 +0x12340000 +0x60000000 +0x12 +31 +31 +32 diff --git a/tests/internal_bench/class_create-0-empty.py b/tests/internal_bench/class_create-0-empty.py new file mode 100644 index 0000000000000..1fd8ccd925715 --- /dev/null +++ b/tests/internal_bench/class_create-0-empty.py @@ -0,0 +1,11 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-1-slots.py b/tests/internal_bench/class_create-1-slots.py new file mode 100644 index 0000000000000..9b3e4b9570da2 --- /dev/null +++ b/tests/internal_bench/class_create-1-slots.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + l = ["x"] + for i in range(num // 40): + + class X: + __slots__ = l + + +bench.run(test) diff --git a/tests/internal_bench/class_create-1.1-slots5.py b/tests/internal_bench/class_create-1.1-slots5.py new file mode 100644 index 0000000000000..ccac77dec9daa --- /dev/null +++ b/tests/internal_bench/class_create-1.1-slots5.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + l = ["a", "b", "c", "d", "x"] + for i in range(num // 40): + + class X: + __slots__ = l + + +bench.run(test) diff --git a/tests/internal_bench/class_create-2-classattr.py b/tests/internal_bench/class_create-2-classattr.py new file mode 100644 index 0000000000000..049a7dab170c1 --- /dev/null +++ b/tests/internal_bench/class_create-2-classattr.py @@ -0,0 +1,11 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + x = 1 + + +bench.run(test) diff --git a/tests/internal_bench/class_create-2.1-classattr5.py b/tests/internal_bench/class_create-2.1-classattr5.py new file mode 100644 index 0000000000000..5051e7dcca70d --- /dev/null +++ b/tests/internal_bench/class_create-2.1-classattr5.py @@ -0,0 +1,15 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + a = 0 + b = 0 + c = 0 + d = 0 + x = 1 + + +bench.run(test) diff --git a/tests/internal_bench/class_create-2.3-classattr5objs.py b/tests/internal_bench/class_create-2.3-classattr5objs.py new file mode 100644 index 0000000000000..74540865dcd14 --- /dev/null +++ b/tests/internal_bench/class_create-2.3-classattr5objs.py @@ -0,0 +1,20 @@ +import bench + + +class Class: + pass + + +def test(num): + instance = Class() + for i in range(num // 40): + + class X: + a = instance + b = instance + c = instance + d = instance + x = instance + + +bench.run(test) diff --git a/tests/internal_bench/class_create-3-instancemethod.py b/tests/internal_bench/class_create-3-instancemethod.py new file mode 100644 index 0000000000000..e8c201cb2c3c6 --- /dev/null +++ b/tests/internal_bench/class_create-3-instancemethod.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + def x(self): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-4-classmethod.py b/tests/internal_bench/class_create-4-classmethod.py new file mode 100644 index 0000000000000..f34962bc67117 --- /dev/null +++ b/tests/internal_bench/class_create-4-classmethod.py @@ -0,0 +1,13 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + @classmethod + def x(cls): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-4.1-classmethod_implicit.py b/tests/internal_bench/class_create-4.1-classmethod_implicit.py new file mode 100644 index 0000000000000..f2d1fcfd18821 --- /dev/null +++ b/tests/internal_bench/class_create-4.1-classmethod_implicit.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + def __new__(cls): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-5-staticmethod.py b/tests/internal_bench/class_create-5-staticmethod.py new file mode 100644 index 0000000000000..0633556667544 --- /dev/null +++ b/tests/internal_bench/class_create-5-staticmethod.py @@ -0,0 +1,13 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + @staticmethod + def x(): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-6-getattribute.py b/tests/internal_bench/class_create-6-getattribute.py new file mode 100644 index 0000000000000..10a4fe7ce8d9f --- /dev/null +++ b/tests/internal_bench/class_create-6-getattribute.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + def __getattribute__(self, name): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-6.1-getattr.py b/tests/internal_bench/class_create-6.1-getattr.py new file mode 100644 index 0000000000000..b4b9ba2f5525b --- /dev/null +++ b/tests/internal_bench/class_create-6.1-getattr.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + def __getattr__(self, name): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-6.2-property.py b/tests/internal_bench/class_create-6.2-property.py new file mode 100644 index 0000000000000..cf847b6dc9c9f --- /dev/null +++ b/tests/internal_bench/class_create-6.2-property.py @@ -0,0 +1,13 @@ +import bench + + +def test(num): + for i in range(num // 40): + + class X: + @property + def x(self): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-6.3-descriptor.py b/tests/internal_bench/class_create-6.3-descriptor.py new file mode 100644 index 0000000000000..7b0a635726380 --- /dev/null +++ b/tests/internal_bench/class_create-6.3-descriptor.py @@ -0,0 +1,17 @@ +import bench + + +class D: + def __get__(self, instance, owner=None): + pass + + +def test(num): + descriptor = D() + for i in range(num // 40): + + class X: + x = descriptor + + +bench.run(test) diff --git a/tests/internal_bench/class_create-7-inherit.py b/tests/internal_bench/class_create-7-inherit.py new file mode 100644 index 0000000000000..f48fb215e0ab8 --- /dev/null +++ b/tests/internal_bench/class_create-7-inherit.py @@ -0,0 +1,14 @@ +import bench + + +def test(num): + class B: + pass + + for i in range(num // 40): + + class X(B): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-7.1-inherit_initsubclass.py b/tests/internal_bench/class_create-7.1-inherit_initsubclass.py new file mode 100644 index 0000000000000..0660fa86258fe --- /dev/null +++ b/tests/internal_bench/class_create-7.1-inherit_initsubclass.py @@ -0,0 +1,16 @@ +import bench + + +def test(num): + class B: + @classmethod + def __init_subclass__(cls): + pass + + for i in range(num // 40): + + class X(B): + pass + + +bench.run(test) diff --git a/tests/internal_bench/class_create-8-metaclass_setname.py b/tests/internal_bench/class_create-8-metaclass_setname.py new file mode 100644 index 0000000000000..e4515b54279d1 --- /dev/null +++ b/tests/internal_bench/class_create-8-metaclass_setname.py @@ -0,0 +1,17 @@ +import bench + + +class D: + def __set_name__(self, owner, name): + pass + + +def test(num): + descriptor = D() + for i in range(num // 40): + + class X: + x = descriptor + + +bench.run(test) diff --git a/tests/internal_bench/class_create-8.1-metaclass_setname5.py b/tests/internal_bench/class_create-8.1-metaclass_setname5.py new file mode 100644 index 0000000000000..5daa3f8471bca --- /dev/null +++ b/tests/internal_bench/class_create-8.1-metaclass_setname5.py @@ -0,0 +1,21 @@ +import bench + + +class D: + def __set_name__(self, owner, name): + pass + + +def test(num): + descriptor = D() + for i in range(num // 40): + + class X: + a = descriptor + b = descriptor + c = descriptor + d = descriptor + x = descriptor + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-0-object.py b/tests/internal_bench/class_instance-0-object.py new file mode 100644 index 0000000000000..401c8ea7e3cc2 --- /dev/null +++ b/tests/internal_bench/class_instance-0-object.py @@ -0,0 +1,11 @@ +import bench + +X = object + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-0.1-object-gc.py b/tests/internal_bench/class_instance-0.1-object-gc.py new file mode 100644 index 0000000000000..7c475963a8e81 --- /dev/null +++ b/tests/internal_bench/class_instance-0.1-object-gc.py @@ -0,0 +1,13 @@ +import bench +import gc + +X = object + + +def test(num): + for i in range(num // 5): + x = X() + gc.collect() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-1-empty.py b/tests/internal_bench/class_instance-1-empty.py new file mode 100644 index 0000000000000..617d47a86e92b --- /dev/null +++ b/tests/internal_bench/class_instance-1-empty.py @@ -0,0 +1,13 @@ +import bench + + +class X: + pass + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-1.1-classattr.py b/tests/internal_bench/class_instance-1.1-classattr.py new file mode 100644 index 0000000000000..4e667533d4aa1 --- /dev/null +++ b/tests/internal_bench/class_instance-1.1-classattr.py @@ -0,0 +1,13 @@ +import bench + + +class X: + x = 0 + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-1.2-func.py b/tests/internal_bench/class_instance-1.2-func.py new file mode 100644 index 0000000000000..21bf7a1ac48fc --- /dev/null +++ b/tests/internal_bench/class_instance-1.2-func.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def f(self): + pass + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-1.3-empty-gc.py b/tests/internal_bench/class_instance-1.3-empty-gc.py new file mode 100644 index 0000000000000..a5108ef8e8156 --- /dev/null +++ b/tests/internal_bench/class_instance-1.3-empty-gc.py @@ -0,0 +1,15 @@ +import bench +import gc + + +class X: + pass + + +def test(num): + for i in range(num // 5): + x = X() + gc.collect() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-2-init.py b/tests/internal_bench/class_instance-2-init.py new file mode 100644 index 0000000000000..86619d3154840 --- /dev/null +++ b/tests/internal_bench/class_instance-2-init.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def __init__(self): + pass + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-2.1-init_super.py b/tests/internal_bench/class_instance-2.1-init_super.py new file mode 100644 index 0000000000000..38bca5fef877a --- /dev/null +++ b/tests/internal_bench/class_instance-2.1-init_super.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def __init__(self): + return super().__init__() + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-2.2-new.py b/tests/internal_bench/class_instance-2.2-new.py new file mode 100644 index 0000000000000..dc5e78ea5ef98 --- /dev/null +++ b/tests/internal_bench/class_instance-2.2-new.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def __new__(cls): + return super().__new__(cls) + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-3-del.py b/tests/internal_bench/class_instance-3-del.py new file mode 100644 index 0000000000000..af700f72a94a8 --- /dev/null +++ b/tests/internal_bench/class_instance-3-del.py @@ -0,0 +1,14 @@ +import bench + + +class X: + def __del__(self): + pass + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-3.1-del-gc.py b/tests/internal_bench/class_instance-3.1-del-gc.py new file mode 100644 index 0000000000000..311c71c3571b0 --- /dev/null +++ b/tests/internal_bench/class_instance-3.1-del-gc.py @@ -0,0 +1,16 @@ +import bench +import gc + + +class X: + def __del__(self): + pass + + +def test(num): + for i in range(num // 5): + x = X() + gc.collect() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-4-slots.py b/tests/internal_bench/class_instance-4-slots.py new file mode 100644 index 0000000000000..51b067fedf0b7 --- /dev/null +++ b/tests/internal_bench/class_instance-4-slots.py @@ -0,0 +1,13 @@ +import bench + + +class X: + __slots__ = ["x"] + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/class_instance-4.1-slots5.py b/tests/internal_bench/class_instance-4.1-slots5.py new file mode 100644 index 0000000000000..8f5c2ecb456d8 --- /dev/null +++ b/tests/internal_bench/class_instance-4.1-slots5.py @@ -0,0 +1,13 @@ +import bench + + +class X: + __slots__ = ["a", "b", "c", "d", "x"] + + +def test(num): + for i in range(num // 5): + x = X() + + +bench.run(test) diff --git a/tests/internal_bench/var-6.2-instance-speciallookup.py b/tests/internal_bench/var-6.2-instance-speciallookup.py new file mode 100644 index 0000000000000..fee12b2f930c3 --- /dev/null +++ b/tests/internal_bench/var-6.2-instance-speciallookup.py @@ -0,0 +1,19 @@ +import bench + + +class Foo: + def __init__(self): + self.num = 20000000 + + def __delattr__(self, name): # just trigger the 'special lookups' flag on the class + pass + + +def test(num): + o = Foo() + i = 0 + while i < o.num: + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-6.3-instance-property.py b/tests/internal_bench/var-6.3-instance-property.py new file mode 100644 index 0000000000000..b4426ef7928e1 --- /dev/null +++ b/tests/internal_bench/var-6.3-instance-property.py @@ -0,0 +1,17 @@ +import bench + + +class Foo: + @property + def num(self): + return 20000000 + + +def test(num): + o = Foo() + i = 0 + while i < o.num: + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-6.4-instance-descriptor.py b/tests/internal_bench/var-6.4-instance-descriptor.py new file mode 100644 index 0000000000000..b4df69f878f10 --- /dev/null +++ b/tests/internal_bench/var-6.4-instance-descriptor.py @@ -0,0 +1,20 @@ +import bench + + +class Descriptor: + def __get__(self, instance, owner=None): + return 20000000 + + +class Foo: + num = Descriptor() + + +def test(num): + o = Foo() + i = 0 + while i < o.num: + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-6.5-instance-getattr.py b/tests/internal_bench/var-6.5-instance-getattr.py new file mode 100644 index 0000000000000..3b2ef6721105f --- /dev/null +++ b/tests/internal_bench/var-6.5-instance-getattr.py @@ -0,0 +1,16 @@ +import bench + + +class Foo: + def __getattr__(self, name): + return 20000000 + + +def test(num): + o = Foo() + i = 0 + while i < o.num: + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-6.6-instance-builtin_ordered.py b/tests/internal_bench/var-6.6-instance-builtin_ordered.py new file mode 100644 index 0000000000000..02d7525230192 --- /dev/null +++ b/tests/internal_bench/var-6.6-instance-builtin_ordered.py @@ -0,0 +1,12 @@ +import bench + + +def test(num): + i = 0 + o = set() # object with largest rom-frozen ordered locals_dict + n = "__contains__" # last element in that dict for longest lookup + while i < num: + i += hasattr(o, n) # True, converts to 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9-getattr.py b/tests/internal_bench/var-9-getattr.py new file mode 100644 index 0000000000000..69d2bfed2e0ac --- /dev/null +++ b/tests/internal_bench/var-9-getattr.py @@ -0,0 +1,16 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + o.num = num + i = 0 + while i < getattr(o, "num", num): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.1-getattr_default.py b/tests/internal_bench/var-9.1-getattr_default.py new file mode 100644 index 0000000000000..e803d39b32667 --- /dev/null +++ b/tests/internal_bench/var-9.1-getattr_default.py @@ -0,0 +1,15 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + i = 0 + while i < getattr(o, "num", num): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.2-getattr_default_special.py b/tests/internal_bench/var-9.2-getattr_default_special.py new file mode 100644 index 0000000000000..c48ec0742cff6 --- /dev/null +++ b/tests/internal_bench/var-9.2-getattr_default_special.py @@ -0,0 +1,16 @@ +import bench + + +class Foo: + def __delattr__(self, name): # just trigger the 'special lookups' flag on the class + pass + + +def test(num): + o = Foo() + i = 0 + while i < getattr(o, "num", num): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.3-except_ok.py b/tests/internal_bench/var-9.3-except_ok.py new file mode 100644 index 0000000000000..efc1a8f858cd0 --- /dev/null +++ b/tests/internal_bench/var-9.3-except_ok.py @@ -0,0 +1,23 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + o.num = num + + def get(): + try: + return o.num + except AttributeError: + return num + + i = 0 + while i < get(): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.4-except_selfinduced.py b/tests/internal_bench/var-9.4-except_selfinduced.py new file mode 100644 index 0000000000000..544609ca4b6b3 --- /dev/null +++ b/tests/internal_bench/var-9.4-except_selfinduced.py @@ -0,0 +1,22 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + + def get(): + try: + raise AttributeError + except AttributeError: + return num + + i = 0 + while i < get(): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.5-except_error.py b/tests/internal_bench/var-9.5-except_error.py new file mode 100644 index 0000000000000..caf83fa46ab3d --- /dev/null +++ b/tests/internal_bench/var-9.5-except_error.py @@ -0,0 +1,22 @@ +import bench + + +class Foo: + pass + + +def test(num): + o = Foo() + + def get(): + try: + return o.num + except AttributeError: + return num + + i = 0 + while i < get(): + i += 1 + + +bench.run(test) diff --git a/tests/internal_bench/var-9.6-except_error_special.py b/tests/internal_bench/var-9.6-except_error_special.py new file mode 100644 index 0000000000000..8bc395b4d7ef8 --- /dev/null +++ b/tests/internal_bench/var-9.6-except_error_special.py @@ -0,0 +1,23 @@ +import bench + + +class Foo: + def __delattr__(self, name): # just trigger the 'special lookups' flag on the class + pass + + +def test(num): + o = Foo() + + def get(): + try: + return o.num + except AttributeError: + return num + + i = 0 + while i < get(): + i += 1 + + +bench.run(test) diff --git a/tests/io/builtin_print_file.py b/tests/io/builtin_print_file.py index 822356a6cc305..e00f58635a9d2 100644 --- a/tests/io/builtin_print_file.py +++ b/tests/io/builtin_print_file.py @@ -13,5 +13,5 @@ try: print(file=1) -except (AttributeError, OSError): # CPython and uPy differ in error message +except (AttributeError, OSError): # CPython and MicroPython differ in error message print("Error") diff --git a/tests/io/file_seek.py b/tests/io/file_seek.py index 3990df8409051..b9f9593786fe6 100644 --- a/tests/io/file_seek.py +++ b/tests/io/file_seek.py @@ -30,5 +30,5 @@ try: f.seek(1) except (OSError, ValueError): - # CPy raises ValueError, uPy raises OSError + # CPy raises ValueError, MPy raises OSError print("OSError or ValueError") diff --git a/tests/micropython/builtin_execfile.py b/tests/micropython/builtin_execfile.py index 75a867bb94023..4fd4d66d4ee25 100644 --- a/tests/micropython/builtin_execfile.py +++ b/tests/micropython/builtin_execfile.py @@ -75,3 +75,24 @@ def open(self, file, mode): # Unmount the VFS object. vfs.umount(fs) + + +class EvilFilesystem: + def mount(self, readonly, mkfs): + print("mount", readonly, mkfs) + + def umount(self): + print("umount") + + def open(self, file, mode): + return None + + +fs = EvilFilesystem() +vfs.mount(fs, "/test_mnt") +try: + execfile("/test_mnt/test.py") + print("ExecFile succeeded") +except OSError: + print("OSError") +vfs.umount(fs) diff --git a/tests/micropython/builtin_execfile.py.exp b/tests/micropython/builtin_execfile.py.exp index 49703d570763d..d93dee547b6ea 100644 --- a/tests/micropython/builtin_execfile.py.exp +++ b/tests/micropython/builtin_execfile.py.exp @@ -5,3 +5,6 @@ open /test.py rb 123 TypeError umount +mount False False +OSError +umount diff --git a/tests/micropython/const_error.py b/tests/micropython/const_error.py index d35be530a7c8d..950360e4dc782 100644 --- a/tests/micropython/const_error.py +++ b/tests/micropython/const_error.py @@ -18,8 +18,6 @@ def test_syntax(code): # these operations are not supported within const test_syntax("A = const(1 @ 2)") -test_syntax("A = const(1 / 2)") -test_syntax("A = const(1 ** -2)") test_syntax("A = const(1 << -2)") test_syntax("A = const(1 >> -2)") test_syntax("A = const(1 % 0)") diff --git a/tests/micropython/const_error.py.exp b/tests/micropython/const_error.py.exp index 3edc3efe9c3e9..bef69eb32ea7d 100644 --- a/tests/micropython/const_error.py.exp +++ b/tests/micropython/const_error.py.exp @@ -5,5 +5,3 @@ SyntaxError SyntaxError SyntaxError SyntaxError -SyntaxError -SyntaxError diff --git a/tests/micropython/const_float.py b/tests/micropython/const_float.py new file mode 100644 index 0000000000000..c3a0df0276bf8 --- /dev/null +++ b/tests/micropython/const_float.py @@ -0,0 +1,23 @@ +# test constant optimisation, with consts that are floats + +from micropython import const + +# check we can make consts from floats +F1 = const(2.5) +F2 = const(-0.3) +print(type(F1), F1) +print(type(F2), F2) + +# check arithmetic with floats +F3 = const(F1 + F2) +F4 = const(F1**2) +print(F3, F4) + +# check int operations with float results +F5 = const(1 / 2) +F6 = const(2**-2) +print(F5, F6) + +# note: we also test float expression folding when +# we're compiling test cases in tests/float, as +# many expressions are resolved at compile time. diff --git a/tests/micropython/const_float.py.exp b/tests/micropython/const_float.py.exp new file mode 100644 index 0000000000000..17a86a6d936c2 --- /dev/null +++ b/tests/micropython/const_float.py.exp @@ -0,0 +1,4 @@ + 2.5 + -0.3 +2.2 6.25 +0.5 0.25 diff --git a/tests/micropython/const_math.py b/tests/micropython/const_math.py new file mode 100644 index 0000000000000..7ee5edc6d3240 --- /dev/null +++ b/tests/micropython/const_math.py @@ -0,0 +1,18 @@ +# Test expressions based on math module constants +try: + import math +except ImportError: + print("SKIP") + raise SystemExit + +from micropython import const + +# check that we can make consts from math constants +# (skip if the target has MICROPY_COMP_MODULE_CONST disabled) +try: + exec("two_pi = const(2.0 * math.pi)") +except SyntaxError: + print("SKIP") + raise SystemExit + +print(math.cos(two_pi)) diff --git a/tests/micropython/const_math.py.exp b/tests/micropython/const_math.py.exp new file mode 100644 index 0000000000000..d3827e75a5cad --- /dev/null +++ b/tests/micropython/const_math.py.exp @@ -0,0 +1 @@ +1.0 diff --git a/tests/micropython/decorator_error.py b/tests/micropython/decorator_error.py index 94772ac1e5fef..b5eae65b29209 100644 --- a/tests/micropython/decorator_error.py +++ b/tests/micropython/decorator_error.py @@ -1,4 +1,4 @@ -# test syntax errors for uPy-specific decorators +# test syntax errors for MicroPython-specific decorators def test_syntax(code): diff --git a/tests/micropython/emg_exc.py b/tests/micropython/emg_exc.py index a74757d028ff2..cf918b3dc8ac5 100644 --- a/tests/micropython/emg_exc.py +++ b/tests/micropython/emg_exc.py @@ -1,10 +1,9 @@ # test that emergency exceptions work -import micropython import sys try: - import io + import io, micropython except ImportError: print("SKIP") raise SystemExit diff --git a/tests/micropython/emg_exc.py.native.exp b/tests/micropython/emg_exc.py.native.exp new file mode 100644 index 0000000000000..879fa7ef5b943 --- /dev/null +++ b/tests/micropython/emg_exc.py.native.exp @@ -0,0 +1 @@ +ValueError(1,) diff --git a/tests/micropython/extreme_exc.py b/tests/micropython/extreme_exc.py index ad819e408fd82..17323b566d3b5 100644 --- a/tests/micropython/extreme_exc.py +++ b/tests/micropython/extreme_exc.py @@ -1,6 +1,10 @@ # test some extreme cases of allocating exceptions and tracebacks -import micropython +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit # Check for stackless build, which can't call functions without # allocating a frame on the heap. diff --git a/tests/micropython/heap_lock.py b/tests/micropython/heap_lock.py index f2892a6dc581e..209a3143461a5 100644 --- a/tests/micropython/heap_lock.py +++ b/tests/micropython/heap_lock.py @@ -1,6 +1,10 @@ # check that heap_lock/heap_unlock work as expected -import micropython +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit l = [] l2 = list(range(100)) diff --git a/tests/micropython/heap_locked.py b/tests/micropython/heap_locked.py index d9e5b5d4090af..d9d99493dd6b7 100644 --- a/tests/micropython/heap_locked.py +++ b/tests/micropython/heap_locked.py @@ -1,8 +1,10 @@ # test micropython.heap_locked() -import micropython +try: + import micropython -if not hasattr(micropython, "heap_locked"): + micropython.heap_locked +except (AttributeError, ImportError): print("SKIP") raise SystemExit diff --git a/tests/micropython/heapalloc.py b/tests/micropython/heapalloc.py index e19f8d0255deb..010bf878a07b0 100644 --- a/tests/micropython/heapalloc.py +++ b/tests/micropython/heapalloc.py @@ -1,6 +1,10 @@ # check that we can do certain things without allocating heap memory -import micropython +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit # Check for stackless build, which can't call functions without # allocating a frame on heap. diff --git a/tests/micropython/heapalloc_exc_compressed.py b/tests/micropython/heapalloc_exc_compressed.py index aa071d641c21c..96fe3ca4f7e00 100644 --- a/tests/micropython/heapalloc_exc_compressed.py +++ b/tests/micropython/heapalloc_exc_compressed.py @@ -1,4 +1,10 @@ -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # Tests both code paths for built-in exception raising. # mp_obj_new_exception_msg_varg (exception requires decompression at raise-time to format) diff --git a/tests/micropython/heapalloc_exc_compressed_emg_exc.py b/tests/micropython/heapalloc_exc_compressed_emg_exc.py index 48ce9dd69e6e2..31d937b8f9345 100644 --- a/tests/micropython/heapalloc_exc_compressed_emg_exc.py +++ b/tests/micropython/heapalloc_exc_compressed_emg_exc.py @@ -1,4 +1,10 @@ -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # Does the full test from heapalloc_exc_compressed.py but while the heap is # locked (this can only work when the emergency exception buf is enabled). diff --git a/tests/micropython/heapalloc_exc_raise.py b/tests/micropython/heapalloc_exc_raise.py index 99810e0075064..917ccf42f1e16 100644 --- a/tests/micropython/heapalloc_exc_raise.py +++ b/tests/micropython/heapalloc_exc_raise.py @@ -1,6 +1,12 @@ # Test that we can raise and catch (preallocated) exception # without memory allocation. -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit e = ValueError("error") diff --git a/tests/micropython/heapalloc_fail_bytearray.py b/tests/micropython/heapalloc_fail_bytearray.py index 1bf7ddd600b93..9ee73d1c583f2 100644 --- a/tests/micropython/heapalloc_fail_bytearray.py +++ b/tests/micropython/heapalloc_fail_bytearray.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with bytearray -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit class GetSlice: diff --git a/tests/micropython/heapalloc_fail_dict.py b/tests/micropython/heapalloc_fail_dict.py index ce2d158bd09c6..04c79183578ff 100644 --- a/tests/micropython/heapalloc_fail_dict.py +++ b/tests/micropython/heapalloc_fail_dict.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with dict -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # create dict x = 1 diff --git a/tests/micropython/heapalloc_fail_list.py b/tests/micropython/heapalloc_fail_list.py index 9a2e9a555f3e2..7afb6dc1019b3 100644 --- a/tests/micropython/heapalloc_fail_list.py +++ b/tests/micropython/heapalloc_fail_list.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with list -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit class GetSlice: diff --git a/tests/micropython/heapalloc_fail_memoryview.py b/tests/micropython/heapalloc_fail_memoryview.py index da2d1abffa634..17e3e1262472c 100644 --- a/tests/micropython/heapalloc_fail_memoryview.py +++ b/tests/micropython/heapalloc_fail_memoryview.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with memoryview -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit class GetSlice: diff --git a/tests/micropython/heapalloc_fail_set.py b/tests/micropython/heapalloc_fail_set.py index 3c347660ad763..0c4d85eef62a8 100644 --- a/tests/micropython/heapalloc_fail_set.py +++ b/tests/micropython/heapalloc_fail_set.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with set -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # create set x = 1 diff --git a/tests/micropython/heapalloc_fail_tuple.py b/tests/micropython/heapalloc_fail_tuple.py index de79385e3e3cb..11718a8107b3a 100644 --- a/tests/micropython/heapalloc_fail_tuple.py +++ b/tests/micropython/heapalloc_fail_tuple.py @@ -1,6 +1,12 @@ # test handling of failed heap allocation with tuple -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # create tuple x = 1 diff --git a/tests/micropython/heapalloc_inst_call.py b/tests/micropython/heapalloc_inst_call.py index 14d8826bf06ce..f78aa3cf87798 100644 --- a/tests/micropython/heapalloc_inst_call.py +++ b/tests/micropython/heapalloc_inst_call.py @@ -1,6 +1,13 @@ # Test that calling clazz.__call__() with up to at least 3 arguments # doesn't require heap allocation. -import micropython + +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit class Foo0: diff --git a/tests/micropython/heapalloc_int_from_bytes.py b/tests/micropython/heapalloc_int_from_bytes.py index 5fe50443ae335..3310ea95d1472 100644 --- a/tests/micropython/heapalloc_int_from_bytes.py +++ b/tests/micropython/heapalloc_int_from_bytes.py @@ -1,6 +1,13 @@ # Test that int.from_bytes() for small number of bytes generates # small int. -import micropython + +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit micropython.heap_lock() print(int.from_bytes(b"1", "little")) diff --git a/tests/micropython/heapalloc_slice.py b/tests/micropython/heapalloc_slice.py new file mode 100644 index 0000000000000..62d96595c719e --- /dev/null +++ b/tests/micropython/heapalloc_slice.py @@ -0,0 +1,18 @@ +# slice operations that don't require allocation +try: + from micropython import heap_lock, heap_unlock +except (ImportError, AttributeError): + heap_lock = heap_unlock = lambda: 0 + +b = bytearray(range(10)) + +m = memoryview(b) + +heap_lock() + +b[3:5] = b"aa" +m[5:7] = b"bb" + +heap_unlock() + +print(b) diff --git a/tests/micropython/heapalloc_str.py b/tests/micropython/heapalloc_str.py index 39aa56ccd7834..6372df5d37b13 100644 --- a/tests/micropython/heapalloc_str.py +++ b/tests/micropython/heapalloc_str.py @@ -1,5 +1,12 @@ # String operations which don't require allocation -import micropython + +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit micropython.heap_lock() diff --git a/tests/micropython/heapalloc_super.py b/tests/micropython/heapalloc_super.py index 51afae3d83d06..6839eee330acd 100644 --- a/tests/micropython/heapalloc_super.py +++ b/tests/micropython/heapalloc_super.py @@ -1,5 +1,12 @@ # test super() operations which don't require allocation -import micropython + +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # Check for stackless build, which can't call functions without # allocating a frame on heap. diff --git a/tests/micropython/heapalloc_traceback.py.native.exp b/tests/micropython/heapalloc_traceback.py.native.exp new file mode 100644 index 0000000000000..d6ac26aa829e1 --- /dev/null +++ b/tests/micropython/heapalloc_traceback.py.native.exp @@ -0,0 +1,3 @@ +StopIteration +StopIteration: + diff --git a/tests/micropython/heapalloc_yield_from.py b/tests/micropython/heapalloc_yield_from.py index 950717189087d..9d4f8c6940f16 100644 --- a/tests/micropython/heapalloc_yield_from.py +++ b/tests/micropython/heapalloc_yield_from.py @@ -1,6 +1,12 @@ # Check that yield-from can work without heap allocation -import micropython +try: + import micropython + + micropython.heap_lock +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit # Yielding from a function generator diff --git a/tests/micropython/import_mpy_native.py b/tests/micropython/import_mpy_native.py index ad63e025db2c8..59181b203bba9 100644 --- a/tests/micropython/import_mpy_native.py +++ b/tests/micropython/import_mpy_native.py @@ -54,12 +54,13 @@ def open(self, path, mode): # these are the test .mpy files # CIRCUITPY-CHANGE -valid_header = bytes([ord("C"), 6, mpy_arch, 31]) +small_int_bits = 31 +valid_header = bytes([77, 6, (mpy_arch & 0x3F), small_int_bits]) # fmt: off user_files = { # bad architecture (mpy_arch needed for sub-version) # CIRCUITPY-CHANGE - '/mod0.mpy': bytes([ord('C'), 6, 0xfc | mpy_arch, 31]), + '/mod0.mpy': bytes([ord('C'), 6, 0xfc | (mpy_arch & 3), small_int_bits]), # test loading of viper and asm '/mod1.mpy': valid_header + ( diff --git a/tests/micropython/import_mpy_native_gc.py b/tests/micropython/import_mpy_native_gc.py index 0d5b79f0a8dd4..5159cc9e9f9cf 100644 --- a/tests/micropython/import_mpy_native_gc.py +++ b/tests/micropython/import_mpy_native_gc.py @@ -57,9 +57,13 @@ def open(self, path, mode): # CIRCUITPY-CHANGE: 'C' instead of 'M' mpy marker. features0_file_contents = { # -march=x64 - 0x806: b'C\x06\n\x1f\x02\x004build/features0.native.mpy\x00\x12factorial\x00\x8a\x02\xe93\x00\x00\x00\xf3\x0f\x1e\xfaSH\x8b\x1d\x7f\x00\x00\x00\xbe\x02\x00\x00\x00\xffS\x18\xbf\x01\x00\x00\x00H\x85\xc0u\x0cH\x8bC \xbe\x02\x00\x00\x00[\xff\xe0H\x0f\xaf\xf8H\xff\xc8\xeb\xe6\xf3\x0f\x1e\xfaATUSH\x8b\x1dI\x00\x00\x00H\x8bG\x08L\x8bc(H\x8bx\x08A\xff\xd4H\x8d5#\x00\x00\x00H\x89\xc5H\x8b\x051\x00\x00\x00\x0f\xb7x\x02\xffShH\x89\xefA\xff\xd4H\x8b\x03[]A\\\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x11$\r&\xa5 \x01"\xff', + 0x806: b"C\x06\x0b\x1f\x03\x002build/test_x64.native.mpy\x00\x08add1\x00\x0cunused\x00\x91B\xe9I\x00\x00\x00H\x8b\x05\xf4\x00\x00\x00H\x8b\x00\xc3H\x8b\x05\xf9\x00\x00\x00\xbe\x02\x00\x00\x00\x8b8H\x8b\x05\xdb\x00\x00\x00H\x8b@ \xff\xe0H\x8b\x05\xce\x00\x00\x00S\xbe\x02\x00\x00\x00H\x8bX \xffP\x18\xbe\x02\x00\x00\x00H\x8dx\x01H\x89\xd8[\xff\xe0AVAUATUSH\x8b\x1d\xa3\x00\x00\x00H\x8bG\x08L\x8bk(H\x8bx\x08A\xff\xd5L\x8b5\x95\x00\x00\x00L\x8bchH\x8d5r\x00\x00\x00H\x89\xc5H\x8b\x05\x88\x00\x00\x00A\x0f\xb7~\x04\xc7\x00@\xe2\x01\x00A\xff\xd4H\x8d5C\x00\x00\x00\xbfV\x00\x00\x00A\xff\xd4A\x0f\xb7~\x02H\x8d5\x1f\x00\x00\x00A\xff\xd4H\x89\xefA\xff\xd5H\x8b\x03[]A\\A]A^\xc3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00+\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x04\x11@\rB\tD\xaf4\x016\xad8\x01:\xaf<\x01>\xff", # -march=armv6m - 0x1006: b"C\x06\x12\x1f\x02\x004build/features0.native.mpy\x00\x12factorial\x00\x88\x02\x18\xe0\x00\x00\x10\xb5\tK\tJ{D\x9cX\x02!\xe3h\x98G\x03\x00\x01 \x00+\x02\xd0XC\x01;\xfa\xe7\x02!#i\x98G\x10\xbd\xc0Fj\x00\x00\x00\x00\x00\x00\x00\xf8\xb5\nN\nK~D\xf4XChgiXh\xb8G\x05\x00\x07K\x08I\xf3XyDX\x88ck\x98G(\x00\xb8G h\xf8\xbd\xc0F:\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 \x11<\r>\xa58\x01:\xff", + 0x1006: b"C\x06\x13\x1f\x03\x008build/test_armv6m.native.mpy\x00\x08add1\x00\x0cunused\x00\x8eb0\xe0\x00\x00\x00\x00\x00\x00\x02K\x03J{D\x9bX\x18hpG\xd0\x00\x00\x00\x00\x00\x00\x00\x10\xb5\x05K\x05I\x06J{D\x9aX[X\x10h\x02!\x1bi\x98G\x10\xbd\xb8\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x10\xb5\x06K\x06J{D\x9bX\x02!\x1ci\xdbh\x98G\x02!\x010\xa0G\x10\xbd\xc0F\x96\x00\x00\x00\x00\x00\x00\x00\xf7\xb5\x12O\x12K\x7fD\xfdX\x12Lki|D\x00\x93ChXh\x00\x9b\x98G\x0fK\x01\x90\x0fJ\xfbXnk\x1a`\x0eK!\x00\xffX\xb8\x88\xb0G!\x00V \x081\xb0G!\x00x\x88\x101\xb0G\x01\x98\x00\x9b\x98G(h\xfe\xbd\xc0Fr\x00\x00\x00\x00\x00\x00\x00R\x00\x00\x00\x08\x00\x00\x00@\xe2\x01\x00\x04\x00\x00\x00\x00\x00\x00\x00\t\x00\x00\x00\x00\x00\x00\x00\x1d\x00\x00\x00\x00\x00\x00\x00A\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x04\x11p\rr\tt\xafd\x01f\xadh\x01j\xafl\x01n\xff", + # -march=xtensawin + 0x2806: b"C\x06+\x1f\x03\x00>build/test_xtensawin.native.mpy\x00\x08add1\x00\x0cunused\x00\x8a\x12\x06\x16\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006A\x00\x81\xf9\xff(\x08\x1d\xf0\x00\x006A\x00\x91\xfb\xff\x81\xf5\xff\xa8\t\x88H\x0c+\xe0\x08\x00-\n\x1d\xf0\x00\x006A\x00\x81\xf0\xff\xad\x02xH\x888\x0c+\xe0\x08\x00\x0c+\x1b\xaa\xe0\x07\x00-\n\x1d\xf06A\x00a\xe9\xff\x88\x122&\x05\xa2(\x01\xe0\x03\x00q\xe6\xff\x81\xea\xff\x92\xa7\x89\xa0\x99\x11H\xd6]\n\xb1\xe3\xff\xa2\x17\x02\x99\x08\xe0\x04\x00\xb1\xe2\xff\\j\xe0\x04\x00\xb1\xe1\xff\xa2\x17\x01\xe0\x04\x00\xad\x05\xe0\x03\x00(\x06\x1d\xf0p\x18\x04\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00(\x00\x00\x00\x00\x00\x00\x00\x1c\x00\x00\x00\x11\x02\r\x04\x07\x06\x03\t\x0c\xaf\x01\x01\x03\xad\x05\x01\x07\xaf\t\x01\x0b\xff", + # -march=rv32imc + 0x2C06: b'C\x06/\x1f\x03\x00:build/test_rv32imc.native.mpy\x00\x08add1\x00\x0cunused\x00\x8fb\x97\x0f\x00\x00g\x80\x0f\x05\x97\x07\x00\x00\x83\xa7\x07\x0e\x88C\x82\x80\x97\x07\x00\x00\x83\xa7G\r\x17\x07\x00\x00\x03\'\xc7\r\x9cK\x08C\x89E\x82\x87A\x11\x97\x07\x00\x00\x83\xa7\xa7\x0b"\xc4\x80K\xdcG\x06\xc6\x89E\x82\x97\xa2\x87"D\xb2@\x89E\x05\x05A\x01\x82\x87\\A\x01\x11"\xcc\x17\x04\x00\x00\x03$$\tN\xc6\xc8C\x83)D\x01\x06\xce&\xcaJ\xc8R\xc4\x82\x99\x17\n\x00\x00\x03*\xca\x07\x03)D\x03\xaa\x84\xf9g\x03UJ\x00\x93\x87\x07$\x17\x07\x00\x00\x03\'\x07\x07\x1c\xc3\x97\x05\x00\x00\x93\x85\xe5\x03\x02\x99\x97\x05\x00\x00\x93\x85\xc5\x03\x13\x05`\x05\x02\x99\x03U*\x00\x97\x05\x00\x00\x93\x85%\x03\x02\x99&\x85\x82\x99\x08@\xf2@bD\xd2DBI\xb2I"J\x05a\x82\x80\x00\x00\x00\x00\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00\x00\x00\x00,\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00P\x04\x11t\rv\xafx\xadz\t|\xafh\x01j\xadl\x01n\xafp\x01r\xff', } # Populate armv7m-derived archs based on armv6m. @@ -67,7 +71,7 @@ def open(self, path, mode): features0_file_contents[arch] = features0_file_contents[0x1006] # Check that a .mpy exists for the target (ignore sub-version in lookup). -sys_implementation_mpy = sys.implementation._mpy & ~(3 << 8) +sys_implementation_mpy = (sys.implementation._mpy & ~(3 << 8)) & 0xFFFF if sys_implementation_mpy not in features0_file_contents: print("SKIP") raise SystemExit diff --git a/tests/micropython/kbd_intr.py b/tests/micropython/kbd_intr.py index 81977aaa52f6f..e1ed7185ef0a2 100644 --- a/tests/micropython/kbd_intr.py +++ b/tests/micropython/kbd_intr.py @@ -1,10 +1,10 @@ # test the micropython.kbd_intr() function -import micropython - try: + import micropython + micropython.kbd_intr -except AttributeError: +except (ImportError, AttributeError): print("SKIP") raise SystemExit diff --git a/tests/micropython/meminfo.py b/tests/micropython/meminfo.py index 9df341fbb8334..f4dd8fdb6042c 100644 --- a/tests/micropython/meminfo.py +++ b/tests/micropython/meminfo.py @@ -1,12 +1,14 @@ # tests meminfo functions in micropython module -import micropython +try: + import micropython -# these functions are not always available -if not hasattr(micropython, "mem_info"): + micropython.mem_info +except (ImportError, AttributeError): print("SKIP") -else: - micropython.mem_info() - micropython.mem_info(1) - micropython.qstr_info() - micropython.qstr_info(1) + raise SystemExit + +micropython.mem_info() +micropython.mem_info(1) +micropython.qstr_info() +micropython.qstr_info(1) diff --git a/tests/micropython/memstats.py b/tests/micropython/memstats.py index dee3a4ce2f225..0e2e7b1c0b326 100644 --- a/tests/micropython/memstats.py +++ b/tests/micropython/memstats.py @@ -1,17 +1,19 @@ # tests meminfo functions in micropython module -import micropython +try: + import micropython -# these functions are not always available -if not hasattr(micropython, "mem_total"): + micropython.mem_total +except (ImportError, AttributeError): print("SKIP") -else: - t = micropython.mem_total() - c = micropython.mem_current() - p = micropython.mem_peak() + raise SystemExit - l = list(range(10000)) +t = micropython.mem_total() +c = micropython.mem_current() +p = micropython.mem_peak() - print(micropython.mem_total() > t) - print(micropython.mem_current() > c) - print(micropython.mem_peak() > p) +l = list(range(10000)) + +print(micropython.mem_total() > t) +print(micropython.mem_current() > c) +print(micropython.mem_peak() > p) diff --git a/tests/micropython/opt_level.py b/tests/micropython/opt_level.py index dd5493a7a3cb8..789197d8825bf 100644 --- a/tests/micropython/opt_level.py +++ b/tests/micropython/opt_level.py @@ -1,4 +1,10 @@ -import micropython as micropython +# test micropython.opt_level() + +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit # check we can get and set the level micropython.opt_level(0) diff --git a/tests/micropython/opt_level_lineno.py b/tests/micropython/opt_level_lineno.py index 40650b6819221..4ca76625de49a 100644 --- a/tests/micropython/opt_level_lineno.py +++ b/tests/micropython/opt_level_lineno.py @@ -1,7 +1,24 @@ -import micropython as micropython +# test micropython.opt_level() and line numbers + +try: + import micropython +except ImportError: + print("SKIP") + raise SystemExit # check that level 3 doesn't store line numbers # the expected output is that any line is printed as "line 1" micropython.opt_level(3) -# CIRCUITPY-CHANGE: use traceback.print_exception() instead of sys.print_exception() -exec("try:\n xyz\nexcept NameError as er:\n import traceback\n traceback.print_exception(er)") + +# force bytecode emitter, because native emitter doesn't store line numbers +exec(""" +@micropython.bytecode +def f(): + try: + xyz + except NameError as er: + # CIRCUITPY-CHANGE: use traceback.print_exception() instead of sys.print_exception() + import traceback + traceback.print_exception(er) +f() +""") diff --git a/tests/micropython/opt_level_lineno.py.exp b/tests/micropython/opt_level_lineno.py.exp index 469b90ba7938a..b50f0628c81fb 100644 --- a/tests/micropython/opt_level_lineno.py.exp +++ b/tests/micropython/opt_level_lineno.py.exp @@ -1,3 +1,3 @@ Traceback (most recent call last): - File "", line 1, in + File "", line 1, in f NameError: name 'xyz' isn't defined diff --git a/tests/micropython/ringio_big.py b/tests/micropython/ringio_big.py new file mode 100644 index 0000000000000..ddbbae12a630c --- /dev/null +++ b/tests/micropython/ringio_big.py @@ -0,0 +1,29 @@ +# Check that micropython.RingIO works correctly. + +try: + import micropython + + micropython.RingIO +except (ImportError, AttributeError): + print("SKIP") + raise SystemExit + +try: + # The maximum possible size + micropython.RingIO(bytearray(65535)) + micropython.RingIO(65534) + + try: + # Buffer may not be too big + micropython.RingIO(bytearray(65536)) + except ValueError as ex: + print(type(ex)) + + try: + # Size may not be too big + micropython.RingIO(65535) + except ValueError as ex: + print(type(ex)) +except MemoryError: + print("SKIP") + raise SystemExit diff --git a/tests/micropython/ringio_big.py.exp b/tests/micropython/ringio_big.py.exp new file mode 100644 index 0000000000000..72af34b383872 --- /dev/null +++ b/tests/micropython/ringio_big.py.exp @@ -0,0 +1,2 @@ + + diff --git a/tests/micropython/schedule.py b/tests/micropython/schedule.py index 6a91459ea3d54..f3dd32661267b 100644 --- a/tests/micropython/schedule.py +++ b/tests/micropython/schedule.py @@ -1,10 +1,10 @@ # test micropython.schedule() function -import micropython - try: + import micropython + micropython.schedule -except AttributeError: +except (ImportError, AttributeError): print("SKIP") raise SystemExit diff --git a/tests/micropython/stack_use.py b/tests/micropython/stack_use.py index 266885d9d1897..5d36fdca2fe01 100644 --- a/tests/micropython/stack_use.py +++ b/tests/micropython/stack_use.py @@ -1,7 +1,11 @@ # tests stack_use function in micropython module -import micropython -if not hasattr(micropython, "stack_use"): +try: + import micropython + + micropython.stack_use +except (ImportError, AttributeError): print("SKIP") -else: - print(type(micropython.stack_use())) # output varies + raise SystemExit + +print(type(micropython.stack_use())) # output varies diff --git a/tests/micropython/test_normalize_newlines.py b/tests/micropython/test_normalize_newlines.py new file mode 100644 index 0000000000000..f19aaa69a3f75 --- /dev/null +++ b/tests/micropython/test_normalize_newlines.py @@ -0,0 +1,14 @@ +# Test for normalize_newlines functionality +# This test verifies that test framework handles various newline combinations correctly + +# Note: This is more of an integration test since normalize_newlines is in the test framework +# The actual testing happens when this test is run through run-tests.py + +print("Testing newline handling") +print("Line 1\r\nLine 2") # Windows-style line ending - should be normalized +print("Line 3") # Normal line +print("Line 4") # Normal line +print("Line 5\nLine 6") # Unix-style line ending - already normalized + +# Test that literal \r in strings is preserved +print(repr("test\rstring")) # Should show 'test\rstring' not 'test\nstring' diff --git a/tests/micropython/test_normalize_newlines.py.exp b/tests/micropython/test_normalize_newlines.py.exp new file mode 100644 index 0000000000000..c4395468cf109 --- /dev/null +++ b/tests/micropython/test_normalize_newlines.py.exp @@ -0,0 +1,8 @@ +Testing newline handling +Line 1 +Line 2 +Line 3 +Line 4 +Line 5 +Line 6 +'test\rstring' diff --git a/tests/micropython/viper_large_jump.py b/tests/micropython/viper_large_jump.py new file mode 100644 index 0000000000000..1c5913dec1ea2 --- /dev/null +++ b/tests/micropython/viper_large_jump.py @@ -0,0 +1,20 @@ +COUNT = 600 + + +try: + code = """ +@micropython.viper +def f() -> int: + x = 0 + while x < 10: +""" + for i in range(COUNT): + code += " x += 1\n" + code += " return x" + exec(code) +except MemoryError: + print("SKIP-TOO-LARGE") + raise SystemExit + + +print(f()) diff --git a/tests/micropython/viper_large_jump.py.exp b/tests/micropython/viper_large_jump.py.exp new file mode 100644 index 0000000000000..e9f960cf4ac4e --- /dev/null +++ b/tests/micropython/viper_large_jump.py.exp @@ -0,0 +1 @@ +600 diff --git a/tests/micropython/viper_ptr16_load_boundary.py b/tests/micropython/viper_ptr16_load_boundary.py new file mode 100644 index 0000000000000..0d4c3105b685b --- /dev/null +++ b/tests/micropython/viper_ptr16_load_boundary.py @@ -0,0 +1,39 @@ +# Test boundary conditions for various architectures + +GET_TEMPLATE = """ +@micropython.viper +def get{off}(src: ptr16) -> uint: + return uint(src[{off}]) +print(hex(get{off}(buffer))) +""" + + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 2 + + +@micropython.viper +def get_index(src: ptr16, i: int) -> int: + return src[i] + + +def data(start, len): + output = bytearray(len) + for idx in range(len): + output[idx] = (start + idx) & 0xFF + return output + + +buffer = bytearray((((1 << max(BIT_THRESHOLDS)) + 1) // 1024) * 1024) +val = 0 +for bit in BIT_THRESHOLDS: + print("---", bit) + pre, idx, post = ((1 << bit) - (2 * SIZE), (1 << bit) - (1 * SIZE), 1 << bit) + buffer[pre:post] = data(val, 3 * SIZE) + val = val + (3 * SIZE) + + pre, idx, post = pre // SIZE, idx // SIZE, post // SIZE + print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post))) + exec(GET_TEMPLATE.format(off=pre)) + exec(GET_TEMPLATE.format(off=idx)) + exec(GET_TEMPLATE.format(off=post)) diff --git a/tests/micropython/viper_ptr16_load_boundary.py.exp b/tests/micropython/viper_ptr16_load_boundary.py.exp new file mode 100644 index 0000000000000..56f1d32290468 --- /dev/null +++ b/tests/micropython/viper_ptr16_load_boundary.py.exp @@ -0,0 +1,20 @@ +--- 5 +0x100 0x302 0x504 +0x100 +0x302 +0x504 +--- 8 +0x706 0x908 0xb0a +0x706 +0x908 +0xb0a +--- 11 +0xd0c 0xf0e 0x1110 +0xd0c +0xf0e +0x1110 +--- 12 +0x1312 0x1514 0x1716 +0x1312 +0x1514 +0x1716 diff --git a/tests/micropython/viper_ptr16_store_boundary.py b/tests/micropython/viper_ptr16_store_boundary.py new file mode 100644 index 0000000000000..7c774d4d1ca18 --- /dev/null +++ b/tests/micropython/viper_ptr16_store_boundary.py @@ -0,0 +1,63 @@ +# Test boundary conditions for various architectures + +SET_TEMPLATE = """ +@micropython.viper +def set{off}(dest: ptr16): + saved = dest + dest[{off}] = {val} + assert int(saved) == int(dest) +""" + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 2 +MASK = (1 << (8 * SIZE)) - 1 + +next_int = 1 +test_buffer = bytearray(SIZE) + + +def next_value() -> uint: + global next_int + global test_buffer + for index in range(1, SIZE): + test_buffer[index - 1] = test_buffer[index] + test_buffer[SIZE - 1] = next_int + next_int += 1 + output = 0 + for byte in test_buffer: + output = (output << 8) | byte + return output & MASK + + +def get_index(src, i): + return src[i * SIZE] + (src[(i * SIZE) + 1] << 8) + + +@micropython.viper +def set_index(dest: ptr16, i: int, val: uint): + saved = dest + dest[i] = val + assert int(saved) == int(dest) + + +try: + buffer = bytearray((((1 << max(BIT_THRESHOLDS)) // 1024) + 1) * 1024) + + for bit in BIT_THRESHOLDS: + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + exec(SET_TEMPLATE.format(off=(offset + index) // SIZE, val=next_value())) +except MemoryError: + print("SKIP-TOO-LARGE") + raise SystemExit + + +for bit in BIT_THRESHOLDS: + print("---", bit) + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + globals()["set{}".format((offset + index) // SIZE)](buffer) + print(hex(get_index(buffer, (offset + index) // SIZE))) + for index in range(0, 3 * SIZE, SIZE): + set_index(buffer, (offset + index) // SIZE, next_value()) + print(hex(get_index(buffer, (offset + index) // SIZE))) diff --git a/tests/micropython/viper_ptr16_store_boundary.py.exp b/tests/micropython/viper_ptr16_store_boundary.py.exp new file mode 100644 index 0000000000000..007a50b3edafb --- /dev/null +++ b/tests/micropython/viper_ptr16_store_boundary.py.exp @@ -0,0 +1,28 @@ +--- 5 +0x1 +0x102 +0x203 +0xc0d +0xd0e +0xe0f +--- 8 +0x304 +0x405 +0x506 +0xf10 +0x1011 +0x1112 +--- 11 +0x607 +0x708 +0x809 +0x1213 +0x1314 +0x1415 +--- 12 +0x90a +0xa0b +0xb0c +0x1516 +0x1617 +0x1718 diff --git a/tests/micropython/viper_ptr32_load_boundary.py b/tests/micropython/viper_ptr32_load_boundary.py new file mode 100644 index 0000000000000..971d1113c49bb --- /dev/null +++ b/tests/micropython/viper_ptr32_load_boundary.py @@ -0,0 +1,39 @@ +# Test boundary conditions for various architectures + +GET_TEMPLATE = """ +@micropython.viper +def get{off}(src: ptr32) -> uint: + return uint(src[{off}]) +print(hex(get{off}(buffer))) +""" + + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 4 + + +@micropython.viper +def get_index(src: ptr32, i: int) -> int: + return src[i] + + +def data(start, len): + output = bytearray(len) + for idx in range(len): + output[idx] = (start + idx) & 0xFF + return output + + +buffer = bytearray((((1 << max(BIT_THRESHOLDS)) + 1) // 1024) * 1024) +val = 0 +for bit in BIT_THRESHOLDS: + print("---", bit) + pre, idx, post = (((1 << bit) - (2 * SIZE)), ((1 << bit) - (1 * SIZE)), (1 << bit)) + buffer[pre:post] = data(val, 3 * SIZE) + val = val + (3 * SIZE) + + pre, idx, post = pre // SIZE, idx // SIZE, post // SIZE + print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post))) + exec(GET_TEMPLATE.format(off=pre)) + exec(GET_TEMPLATE.format(off=idx)) + exec(GET_TEMPLATE.format(off=post)) diff --git a/tests/micropython/viper_ptr32_load_boundary.py.exp b/tests/micropython/viper_ptr32_load_boundary.py.exp new file mode 100644 index 0000000000000..1e22a8b361333 --- /dev/null +++ b/tests/micropython/viper_ptr32_load_boundary.py.exp @@ -0,0 +1,20 @@ +--- 5 +0x3020100 0x7060504 0xb0a0908 +0x3020100 +0x7060504 +0xb0a0908 +--- 8 +0xf0e0d0c 0x13121110 0x17161514 +0xf0e0d0c +0x13121110 +0x17161514 +--- 11 +0x1b1a1918 0x1f1e1d1c 0x23222120 +0x1b1a1918 +0x1f1e1d1c +0x23222120 +--- 12 +0x27262524 0x2b2a2928 0x2f2e2d2c +0x27262524 +0x2b2a2928 +0x2f2e2d2c diff --git a/tests/micropython/viper_ptr32_store_boundary.py b/tests/micropython/viper_ptr32_store_boundary.py new file mode 100644 index 0000000000000..96ca74ad3ca70 --- /dev/null +++ b/tests/micropython/viper_ptr32_store_boundary.py @@ -0,0 +1,68 @@ +# Test boundary conditions for various architectures + +SET_TEMPLATE = """ +@micropython.viper +def set{off}(dest: ptr32): + saved = dest + dest[{off}] = {val} + assert int(saved) == int(dest) +""" + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 4 +MASK = (1 << (8 * SIZE)) - 1 + +next_int = 1 +test_buffer = bytearray(SIZE) + + +def next_value() -> uint: + global next_int + global test_buffer + for index in range(1, SIZE): + test_buffer[index - 1] = test_buffer[index] + test_buffer[SIZE - 1] = next_int + next_int += 1 + output = 0 + for byte in test_buffer: + output = (output << 8) | byte + return output & MASK + + +def get_index(src, i): + return ( + src[i * SIZE] + + (src[(i * SIZE) + 1] << 8) + + (src[(i * SIZE) + 2] << 16) + + (src[(i * SIZE) + 3] << 24) + ) + + +@micropython.viper +def set_index(dest: ptr32, i: int, val: uint): + saved = dest + dest[i] = val + assert int(dest) == int(saved) + + +try: + buffer = bytearray((((1 << max(BIT_THRESHOLDS)) // 1024) + 1) * 1024) + + for bit in BIT_THRESHOLDS: + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + exec(SET_TEMPLATE.format(off=(offset + index) // SIZE, val=next_value())) +except MemoryError: + print("SKIP-TOO-LARGE") + raise SystemExit + + +for bit in BIT_THRESHOLDS: + print("---", bit) + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + globals()["set{}".format((offset + index) // SIZE)](buffer) + print(hex(get_index(buffer, (offset + index) // SIZE))) + for index in range(0, 3 * SIZE, SIZE): + set_index(buffer, (offset + index) // SIZE, next_value()) + print(hex(get_index(buffer, (offset + index) // SIZE))) diff --git a/tests/micropython/viper_ptr32_store_boundary.py.exp b/tests/micropython/viper_ptr32_store_boundary.py.exp new file mode 100644 index 0000000000000..7a9a51624743e --- /dev/null +++ b/tests/micropython/viper_ptr32_store_boundary.py.exp @@ -0,0 +1,28 @@ +--- 5 +0x1 +0x102 +0x10203 +0xa0b0c0d +0xb0c0d0e +0xc0d0e0f +--- 8 +0x1020304 +0x2030405 +0x3040506 +0xd0e0f10 +0xe0f1011 +0xf101112 +--- 11 +0x4050607 +0x5060708 +0x6070809 +0x10111213 +0x11121314 +0x12131415 +--- 12 +0x708090a +0x8090a0b +0x90a0b0c +0x13141516 +0x14151617 +0x15161718 diff --git a/tests/micropython/viper_ptr8_load_boundary.py b/tests/micropython/viper_ptr8_load_boundary.py new file mode 100644 index 0000000000000..57e06da5709ab --- /dev/null +++ b/tests/micropython/viper_ptr8_load_boundary.py @@ -0,0 +1,38 @@ +# Test boundary conditions for various architectures + +GET_TEMPLATE = """ +@micropython.viper +def get{off}(src: ptr8) -> uint: + return uint(src[{off}]) +print(hex(get{off}(buffer))) +""" + + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 1 + + +@micropython.viper +def get_index(src: ptr8, i: int) -> int: + return src[i] + + +def data(start, len): + output = bytearray(len) + for idx in range(len): + output[idx] = (start + idx) & 0xFF + return output + + +buffer = bytearray((((1 << max(BIT_THRESHOLDS)) + 1) // 1024) * 1024) +val = 0 +for bit in BIT_THRESHOLDS: + print("---", bit) + pre, idx, post = (((1 << bit) - (2 * SIZE)), ((1 << bit) - (1 * SIZE)), (1 << bit)) + buffer[pre:post] = data(val, 3 * SIZE) + val = val + (3 * SIZE) + + print(hex(get_index(buffer, pre)), hex(get_index(buffer, idx)), hex(get_index(buffer, post))) + exec(GET_TEMPLATE.format(off=pre)) + exec(GET_TEMPLATE.format(off=idx)) + exec(GET_TEMPLATE.format(off=post)) diff --git a/tests/micropython/viper_ptr8_load_boundary.py.exp b/tests/micropython/viper_ptr8_load_boundary.py.exp new file mode 100644 index 0000000000000..a0e423686b8fa --- /dev/null +++ b/tests/micropython/viper_ptr8_load_boundary.py.exp @@ -0,0 +1,20 @@ +--- 5 +0x0 0x1 0x2 +0x0 +0x1 +0x2 +--- 8 +0x3 0x4 0x5 +0x3 +0x4 +0x5 +--- 11 +0x6 0x7 0x8 +0x6 +0x7 +0x8 +--- 12 +0x9 0xa 0xb +0x9 +0xa +0xb diff --git a/tests/micropython/viper_ptr8_store_boundary.py b/tests/micropython/viper_ptr8_store_boundary.py new file mode 100644 index 0000000000000..68b76fd598b88 --- /dev/null +++ b/tests/micropython/viper_ptr8_store_boundary.py @@ -0,0 +1,63 @@ +# Test boundary conditions for various architectures + +SET_TEMPLATE = """ +@micropython.viper +def set{off}(dest: ptr8): + saved = dest + dest[{off}] = {val} + assert int(saved) == int(dest) +""" + +BIT_THRESHOLDS = (5, 8, 11, 12) +SIZE = 1 +MASK = (1 << (8 * SIZE)) - 1 + +next_int = 1 +test_buffer = bytearray(SIZE) + + +def next_value() -> uint: + global next_int + global test_buffer + for index in range(1, SIZE): + test_buffer[index - 1] = test_buffer[index] + test_buffer[SIZE - 1] = next_int + next_int += 1 + output = 0 + for byte in test_buffer: + output = (output << 8) | byte + return output & MASK + + +def get_index(src: ptr8, i: int): + return src[i] + + +@micropython.viper +def set_index(dest: ptr8, i: int, val: uint): + saved = dest + dest[i] = val + assert int(dest) == int(saved) + + +try: + buffer = bytearray((((1 << max(BIT_THRESHOLDS)) // 1024) + 1) * 1024) + + for bit in BIT_THRESHOLDS: + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + exec(SET_TEMPLATE.format(off=(offset + index) // SIZE, val=next_value())) +except MemoryError: + print("SKIP-TOO-LARGE") + raise SystemExit + + +for bit in BIT_THRESHOLDS: + print("---", bit) + offset = (1 << bit) - (2 * SIZE) + for index in range(0, 3 * SIZE, SIZE): + globals()["set{}".format((offset + index) // SIZE)](buffer) + print(hex(get_index(buffer, (offset + index) // SIZE))) + for index in range(0, 3 * SIZE, SIZE): + set_index(buffer, (offset + index) // SIZE, next_value()) + print(hex(get_index(buffer, (offset + index) // SIZE))) diff --git a/tests/micropython/viper_ptr8_store_boundary.py.exp b/tests/micropython/viper_ptr8_store_boundary.py.exp new file mode 100644 index 0000000000000..621295d81a896 --- /dev/null +++ b/tests/micropython/viper_ptr8_store_boundary.py.exp @@ -0,0 +1,28 @@ +--- 5 +0x1 +0x2 +0x3 +0xd +0xe +0xf +--- 8 +0x4 +0x5 +0x6 +0x10 +0x11 +0x12 +--- 11 +0x7 +0x8 +0x9 +0x13 +0x14 +0x15 +--- 12 +0xa +0xb +0xc +0x16 +0x17 +0x18 diff --git a/tests/misc/non_compliant.py b/tests/misc/non_compliant.py index 31c9fa17c3bed..8608f2322f8e8 100644 --- a/tests/misc/non_compliant.py +++ b/tests/misc/non_compliant.py @@ -39,7 +39,7 @@ except NotImplementedError: print("NotImplementedError") -# uPy raises TypeError, should be ValueError +# MicroPython raises TypeError, should be ValueError try: "%c" % b"\x01\x02" except (TypeError, ValueError): @@ -100,10 +100,10 @@ print("NotImplementedError") # CIRCUITPY-CHANGE: We do check these. -# struct pack with too many args, not checked by uPy +# struct pack with too many args, not checked by MicroPython # print(struct.pack("bb", 1, 2, 3)) -# struct pack with too few args, not checked by uPy +# struct pack with too few args, not checked by MicroPython # print(struct.pack("bb", 1)) # array slice assignment with unsupported RHS diff --git a/tests/misc/non_compliant_lexer.py b/tests/misc/non_compliant_lexer.py index e1c21f3d713c4..04c605953e705 100644 --- a/tests/misc/non_compliant_lexer.py +++ b/tests/misc/non_compliant_lexer.py @@ -11,7 +11,7 @@ def test(code): print("NotImplementedError") -# uPy requires spaces between literal numbers and keywords, CPy doesn't +# MPy requires spaces between literal numbers and keywords, CPy doesn't try: eval("1and 0") except SyntaxError: diff --git a/tests/misc/print_exception.py.native.exp b/tests/misc/print_exception.py.native.exp new file mode 100644 index 0000000000000..59e856ae3c44a --- /dev/null +++ b/tests/misc/print_exception.py.native.exp @@ -0,0 +1,18 @@ +caught +Exception: msg + +caught +Exception: fail + +finally +caught +Exception: fail + +reraise +Exception: fail + +caught +Exception: fail + +AttributeError: 'function' object has no attribute 'X' + diff --git a/tests/misc/rge_sm.py b/tests/misc/rge_sm.py index 5e071687c495d..33aa4edb7427a 100644 --- a/tests/misc/rge_sm.py +++ b/tests/misc/rge_sm.py @@ -39,14 +39,6 @@ def solve(self, finishtime): if not self.iterate(): break - def solveNSteps(self, nSteps): - for i in range(nSteps): - if not self.iterate(): - break - - def series(self): - return zip(*self.Trajectory) - # 1-loop RGES for the main parameters of the SM # couplings are: g1, g2, g3 of U(1), SU(2), SU(3); yt (top Yukawa), lambda (Higgs quartic) @@ -55,70 +47,36 @@ def series(self): lambda *a: 41.0 / 96.0 / math.pi**2 * a[1] ** 3, # g1 lambda *a: -19.0 / 96.0 / math.pi**2 * a[2] ** 3, # g2 lambda *a: -42.0 / 96.0 / math.pi**2 * a[3] ** 3, # g3 - lambda *a: 1.0 - / 16.0 - / math.pi**2 - * ( - 9.0 / 2.0 * a[4] ** 3 - - 8.0 * a[3] ** 2 * a[4] - - 9.0 / 4.0 * a[2] ** 2 * a[4] - - 17.0 / 12.0 * a[1] ** 2 * a[4] + lambda *a: ( + 1.0 + / 16.0 + / math.pi**2 + * ( + 9.0 / 2.0 * a[4] ** 3 + - 8.0 * a[3] ** 2 * a[4] + - 9.0 / 4.0 * a[2] ** 2 * a[4] + - 17.0 / 12.0 * a[1] ** 2 * a[4] + ) ), # yt - lambda *a: 1.0 - / 16.0 - / math.pi**2 - * ( - 24.0 * a[5] ** 2 - + 12.0 * a[4] ** 2 * a[5] - - 9.0 * a[5] * (a[2] ** 2 + 1.0 / 3.0 * a[1] ** 2) - - 6.0 * a[4] ** 4 - + 9.0 / 8.0 * a[2] ** 4 - + 3.0 / 8.0 * a[1] ** 4 - + 3.0 / 4.0 * a[2] ** 2 * a[1] ** 2 + lambda *a: ( + 1.0 + / 16.0 + / math.pi**2 + * ( + 24.0 * a[5] ** 2 + + 12.0 * a[4] ** 2 * a[5] + - 9.0 * a[5] * (a[2] ** 2 + 1.0 / 3.0 * a[1] ** 2) + - 6.0 * a[4] ** 4 + + 9.0 / 8.0 * a[2] ** 4 + + 3.0 / 8.0 * a[1] ** 4 + + 3.0 / 4.0 * a[2] ** 2 * a[1] ** 2 + ) ), # lambda ) -def drange(start, stop, step): - r = start - while r < stop: - yield r - r += step - - -def phaseDiagram(system, trajStart, trajPlot, h=0.1, tend=1.0, range=1.0): - tstart = 0.0 - for i in drange(0, range, 0.1 * range): - for j in drange(0, range, 0.1 * range): - rk = RungeKutta(system, trajStart(i, j), tstart, h) - rk.solve(tend) - # draw the line - for tr in rk.Trajectory: - x, y = trajPlot(tr) - print(x, y) - print() - # draw the arrow - continue - l = (len(rk.Trajectory) - 1) / 3 - if l > 0 and 2 * l < len(rk.Trajectory): - p1 = rk.Trajectory[l] - p2 = rk.Trajectory[2 * l] - x1, y1 = trajPlot(p1) - x2, y2 = trajPlot(p2) - dx = -0.5 * (y2 - y1) # orthogonal to line - dy = 0.5 * (x2 - x1) # orthogonal to line - # l = math.sqrt(dx*dx + dy*dy) - # if abs(l) > 1e-3: - # l = 0.1 / l - # dx *= l - # dy *= l - print(x1 + dx, y1 + dy) - print(x2, y2) - print(x1 - dx, y1 - dy) - print() - - def singleTraj(system, trajStart, h=0.02, tend=1.0): + is_REPR_C = float("1.0000001") == float("1.0") tstart = 0.0 # compute the trajectory @@ -130,10 +88,15 @@ def singleTraj(system, trajStart, h=0.02, tend=1.0): for i in range(len(rk.Trajectory)): tr = rk.Trajectory[i] - print(" ".join(["{:.4f}".format(t) for t in tr])) - + tr_str = " ".join(["{:.4f}".format(t) for t in tr]) + if is_REPR_C: + # allow two small deviations for REPR_C + if tr_str == "1.0000 0.3559 0.6485 1.1944 0.9271 0.1083": + tr_str = "1.0000 0.3559 0.6485 1.1944 0.9272 0.1083" + if tr_str == "16.0000 0.3894 0.5793 0.7017 0.5686 -0.0168": + tr_str = "16.0000 0.3894 0.5793 0.7017 0.5686 -0.0167" + print(tr_str) -# phaseDiagram(sysSM, (lambda i, j: [0.354, 0.654, 1.278, 0.8 + 0.2 * i, 0.1 + 0.1 * j]), (lambda a: (a[4], a[5])), h=0.1, tend=math.log(10**17)) # initial conditions at M_Z singleTraj(sysSM, [0.354, 0.654, 1.278, 0.983, 0.131], h=0.5, tend=math.log(10**17)) # true values diff --git a/tests/misc/sys_exc_info.py b/tests/misc/sys_exc_info.py index d7e8a2d943b5e..c076dd572b07b 100644 --- a/tests/misc/sys_exc_info.py +++ b/tests/misc/sys_exc_info.py @@ -8,13 +8,14 @@ def f(): - print(sys.exc_info()[0:2]) + e = sys.exc_info() + print(e[0], e[1]) try: raise ValueError("value", 123) except: - print(sys.exc_info()[0:2]) + print(sys.exc_info()[0], sys.exc_info()[1]) f() # Outside except block, sys.exc_info() should be back to None's diff --git a/tests/misc/sys_settrace_cov.py b/tests/misc/sys_settrace_cov.py new file mode 100644 index 0000000000000..579c8a4a25e50 --- /dev/null +++ b/tests/misc/sys_settrace_cov.py @@ -0,0 +1,23 @@ +import sys + +try: + sys.settrace +except AttributeError: + print("SKIP") + raise SystemExit + + +def trace_tick_handler(frame, event, arg): + print("FRAME", frame) + print("LASTI", frame.f_lasti) + return None + + +def f(): + x = 3 + return x + + +sys.settrace(trace_tick_handler) +f() +sys.settrace(None) diff --git a/tests/misc/sys_settrace_cov.py.exp b/tests/misc/sys_settrace_cov.py.exp new file mode 100644 index 0000000000000..423d78ec42b89 --- /dev/null +++ b/tests/misc/sys_settrace_cov.py.exp @@ -0,0 +1,2 @@ +FRAME +LASTI \\d\+ diff --git a/tests/multi_extmod/machine_i2c_target_irq.py b/tests/multi_extmod/machine_i2c_target_irq.py new file mode 100644 index 0000000000000..eafd9dfdca838 --- /dev/null +++ b/tests/multi_extmod/machine_i2c_target_irq.py @@ -0,0 +1,137 @@ +# Test I2CTarget IRQs and clock stretching. +# +# Requires two instances with their SCL and SDA lines connected together. +# Any combination of the below supported boards can be used. +# +# Notes: +# - pull-up resistors may be needed +# - alif use 1.8V signalling + +import sys +import time +from machine import I2C, I2CTarget + +if not hasattr(I2CTarget, "IRQ_ADDR_MATCH_READ"): + print("SKIP") + raise SystemExit + +ADDR = 67 +clock_stretch_us = 200 + +# Configure pins based on the target. +if sys.platform == "alif": + i2c_args = (1,) # pins P3_7/P3_6 + i2c_kwargs = {} +elif sys.platform == "mimxrt": + i2c_args = (0,) # pins 19/18 on Teensy 4.x + i2c_kwargs = {} + clock_stretch_us = 50 # mimxrt cannot delay too long in the IRQ handler +elif sys.platform == "rp2": + i2c_args = (0,) + i2c_kwargs = {"scl": 9, "sda": 8} +elif sys.platform == "pyboard": + i2c_args = ("Y",) + i2c_kwargs = {} +elif sys.platform == "samd": + i2c_args = () # pins SCL/SDA + i2c_kwargs = {} +elif "zephyr-rpi_pico" in sys.implementation._machine: + i2c_args = ("i2c1",) # on gpio7/gpio6 + i2c_kwargs = {} +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def simple_irq(i2c_target): + flags = i2c_target.irq().flags() + if flags & I2CTarget.IRQ_ADDR_MATCH_READ: + print("IRQ_ADDR_MATCH_READ") + if flags & I2CTarget.IRQ_ADDR_MATCH_WRITE: + print("IRQ_ADDR_MATCH_WRITE") + + # Force clock stretching. + time.sleep_us(clock_stretch_us) + + +class I2CTargetMemory: + def __init__(self, i2c_target, mem): + self.buf1 = bytearray(1) + self.mem = mem + self.memaddr = 0 + self.state = 0 + i2c_target.irq( + self.irq, + I2CTarget.IRQ_ADDR_MATCH_WRITE | I2CTarget.IRQ_READ_REQ | I2CTarget.IRQ_WRITE_REQ, + hard=True, + ) + + def irq(self, i2c_target): + # Force clock stretching. + time.sleep_us(clock_stretch_us) + + flags = i2c_target.irq().flags() + if flags & I2CTarget.IRQ_ADDR_MATCH_WRITE: + self.state = 0 + if flags & I2CTarget.IRQ_READ_REQ: + self.buf1[0] = self.mem[self.memaddr] + self.memaddr += 1 + i2c_target.write(self.buf1) + if flags & I2CTarget.IRQ_WRITE_REQ: + i2c_target.readinto(self.buf1) + if self.state == 0: + self.state = 1 + self.memaddr = self.buf1[0] + else: + self.mem[self.memaddr] = self.buf1[0] + self.memaddr += 1 + self.memaddr %= len(self.mem) + + # Force clock stretching. + time.sleep_us(clock_stretch_us) + + +# I2C controller +def instance0(): + i2c = I2C(*i2c_args, **i2c_kwargs) + multitest.next() + for iteration in range(2): + print("controller iteration", iteration) + multitest.wait("target stage 1") + i2c.writeto_mem(ADDR, 2, "0123") + multitest.broadcast("controller stage 2") + multitest.wait("target stage 3") + print(i2c.readfrom_mem(ADDR, 2, 4)) + multitest.broadcast("controller stage 4") + print("done") + + +# I2C target +def instance1(): + multitest.next() + + for iteration in range(2): + print("target iteration", iteration) + buf = bytearray(b"--------") + if iteration == 0: + # Use built-in memory capability of I2CTarget. + i2c_target = I2CTarget(*i2c_args, **i2c_kwargs, addr=ADDR, mem=buf) + i2c_target.irq( + simple_irq, + I2CTarget.IRQ_ADDR_MATCH_READ | I2CTarget.IRQ_ADDR_MATCH_WRITE, + hard=True, + ) + else: + # Implement a memory device by hand. + i2c_target = I2CTarget(*i2c_args, **i2c_kwargs, addr=ADDR) + I2CTargetMemory(i2c_target, buf) + + multitest.broadcast("target stage 1") + multitest.wait("controller stage 2") + print(buf) + multitest.broadcast("target stage 3") + multitest.wait("controller stage 4") + + i2c_target.deinit() + + print("done") diff --git a/tests/multi_extmod/machine_i2c_target_irq.py.exp b/tests/multi_extmod/machine_i2c_target_irq.py.exp new file mode 100644 index 0000000000000..a17c8f43858b3 --- /dev/null +++ b/tests/multi_extmod/machine_i2c_target_irq.py.exp @@ -0,0 +1,15 @@ +--- instance0 --- +controller iteration 0 +b'0123' +controller iteration 1 +b'0123' +done +--- instance1 --- +target iteration 0 +IRQ_ADDR_MATCH_WRITE +bytearray(b'--0123--') +IRQ_ADDR_MATCH_WRITE +IRQ_ADDR_MATCH_READ +target iteration 1 +bytearray(b'--0123--') +done diff --git a/tests/multi_extmod/machine_i2c_target_memory.py b/tests/multi_extmod/machine_i2c_target_memory.py new file mode 100644 index 0000000000000..6b3f0d03eb7fe --- /dev/null +++ b/tests/multi_extmod/machine_i2c_target_memory.py @@ -0,0 +1,79 @@ +# Test basic use of I2CTarget and a memory buffer. +# +# Requires two instances with their SCL and SDA lines connected together. +# Any combination of the below supported boards can be used. +# +# Notes: +# - pull-up resistors may be needed +# - alif use 1.8V signalling + +import sys +from machine import I2C, I2CTarget + +ADDR = 67 + +# Configure pins based on the target. +if sys.platform == "alif": + i2c_args = (1,) # pins P3_7/P3_6 + i2c_kwargs = {} +elif sys.platform == "esp32": + i2c_args = (1,) # on pins 9/8 + i2c_kwargs = {} +elif sys.platform == "mimxrt": + i2c_args = (0,) # pins 19/18 on Teensy 4.x + i2c_kwargs = {} +elif sys.platform == "rp2": + i2c_args = (0,) + i2c_kwargs = {"scl": 9, "sda": 8} +elif sys.platform == "pyboard": + i2c_args = ("Y",) + i2c_kwargs = {} +elif sys.platform == "samd": + i2c_args = () # pins SCL/SDA + i2c_kwargs = {} +elif "zephyr-rpi_pico" in sys.implementation._machine: + i2c_args = ("i2c1",) # on gpio7/gpio6 + i2c_kwargs = {} +else: + print("Please add support for this test on this platform.") + raise SystemExit + + +def simple_irq(i2c_target): + flags = i2c_target.irq().flags() + if flags & I2CTarget.IRQ_END_READ: + print("IRQ_END_READ", i2c_target.memaddr) + if flags & I2CTarget.IRQ_END_WRITE: + print("IRQ_END_WRITE", i2c_target.memaddr) + + +# I2C controller +def instance0(): + i2c = I2C(*i2c_args, **i2c_kwargs) + multitest.next() + for iteration in range(2): + print("controller iteration", iteration) + multitest.wait("target stage 1") + i2c.writeto_mem(ADDR, 2 + iteration, "0123") + multitest.broadcast("controller stage 2") + multitest.wait("target stage 3") + print(i2c.readfrom_mem(ADDR, 2 + iteration, 4)) + multitest.broadcast("controller stage 4") + print("done") + + +# I2C target +def instance1(): + buf = bytearray(b"--------") + i2c_target = I2CTarget(*i2c_args, **i2c_kwargs, addr=ADDR, mem=buf) + i2c_target.irq(simple_irq) + multitest.next() + for iteration in range(2): + print("target iteration", iteration) + multitest.broadcast("target stage 1") + multitest.wait("controller stage 2") + print(buf) + multitest.broadcast("target stage 3") + multitest.wait("controller stage 4") + i2c_target.deinit() + print("done") diff --git a/tests/multi_extmod/machine_i2c_target_memory.py.exp b/tests/multi_extmod/machine_i2c_target_memory.py.exp new file mode 100644 index 0000000000000..71386cfe769d9 --- /dev/null +++ b/tests/multi_extmod/machine_i2c_target_memory.py.exp @@ -0,0 +1,16 @@ +--- instance0 --- +controller iteration 0 +b'0123' +controller iteration 1 +b'0123' +done +--- instance1 --- +target iteration 0 +IRQ_END_WRITE 2 +bytearray(b'--0123--') +IRQ_END_READ 2 +target iteration 1 +IRQ_END_WRITE 3 +bytearray(b'--00123-') +IRQ_END_READ 3 +done diff --git a/tests/perf_bench/bm_fft.py b/tests/perf_bench/bm_fft.py index 9a2d03d11b97c..e35c1216c139f 100644 --- a/tests/perf_bench/bm_fft.py +++ b/tests/perf_bench/bm_fft.py @@ -15,7 +15,7 @@ def reverse(x, bits): # Initialization n = len(vector) - levels = int(math.log(n) / math.log(2)) + levels = int(round(math.log(n) / math.log(2))) coef = (2 if inverse else -2) * cmath.pi / n exptable = [cmath.rect(1, i * coef) for i in range(n // 2)] vector = [vector[reverse(i, levels)] for i in range(n)] # Copy with bit-reversed permutation diff --git a/tests/perf_bench/bm_pidigits.py b/tests/perf_bench/bm_pidigits.py index bdaa73cec7e9f..c935f103c5b78 100644 --- a/tests/perf_bench/bm_pidigits.py +++ b/tests/perf_bench/bm_pidigits.py @@ -5,6 +5,12 @@ # This benchmark stresses big integer arithmetic. # Adapted from code on: http://benchmarksgame.alioth.debian.org/ +try: + int("0x10000000000000000", 16) +except: + print("SKIP") # No support for >64-bit integers + raise SystemExit + def compose(a, b): aq, ar, as_, at = a diff --git a/tests/perf_bench/core_import_mpy_multi.py b/tests/perf_bench/core_import_mpy_multi.py index 8affa157fa0c5..67deec0508801 100644 --- a/tests/perf_bench/core_import_mpy_multi.py +++ b/tests/perf_bench/core_import_mpy_multi.py @@ -6,7 +6,7 @@ print("SKIP") raise SystemExit -# This is the test.py file that is compiled to test.mpy below. +# This is the test.py file that is compiled to test.mpy below. mpy-cross must be invoked with `-msmall-int-bits=30`. """ class A: def __init__(self, arg): @@ -23,7 +23,7 @@ def f(): x = ("const tuple", None, False, True, 1, 2, 3) result = 123 """ -file_data = b'M\x06\x00\x1f\x14\x03\x0etest.py\x00\x0f\x02A\x00\x02f\x00\x0cresult\x00/-5#\x82I\x81{\x81w\x82/\x81\x05\x81\x17Iom\x82\x13\x06arg\x00\x05\x1cthis will be a string object\x00\x06\x1bthis will be a bytes object\x00\n\x07\x05\x0bconst tuple\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x81\\\x10\n\x01\x89\x07d`T2\x00\x10\x024\x02\x16\x022\x01\x16\x03"\x80{\x16\x04Qc\x02\x81d\x00\x08\x02(DD\x11\x05\x16\x06\x10\x02\x16\x072\x00\x16\x082\x01\x16\t2\x02\x16\nQc\x03`\x1a\x08\x08\x12\x13@\xb1\xb0\x18\x13Qc@\t\x08\t\x12` Qc@\t\x08\n\x12``Qc\x82@ \x0e\x03\x80\x08+)##\x12\x0b\x12\x0c\x12\r\x12\x0e*\x04Y\x12\x0f\x12\x10\x12\x11*\x03Y#\x00\xc0#\x01\xc0#\x02\xc0Qc' +file_data = b'M\x06\x00\x1e\x14\x03\x0etest.py\x00\x0f\x02A\x00\x02f\x00\x0cresult\x00/-5#\x82I\x81{\x81w\x82/\x81\x05\x81\x17Iom\x82\x13\x06arg\x00\x05\x1cthis will be a string object\x00\x06\x1bthis will be a bytes object\x00\n\x07\x05\x0bconst tuple\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x81\\\x10\n\x01\x89\x07d`T2\x00\x10\x024\x02\x16\x022\x01\x16\x03"\x80{\x16\x04Qc\x02\x81d\x00\x08\x02(DD\x11\x05\x16\x06\x10\x02\x16\x072\x00\x16\x082\x01\x16\t2\x02\x16\nQc\x03`\x1a\x08\x08\x12\x13@\xb1\xb0\x18\x13Qc@\t\x08\t\x12` Qc@\t\x08\n\x12``Qc\x82@ \x0e\x03\x80\x08+)##\x12\x0b\x12\x0c\x12\r\x12\x0e*\x04Y\x12\x0f\x12\x10\x12\x11*\x03Y#\x00\xc0#\x01\xc0#\x02\xc0Qc' class File(io.IOBase): diff --git a/tests/perf_bench/core_import_mpy_single.py b/tests/perf_bench/core_import_mpy_single.py index 4d9aa67bf2f0e..f472bb6476278 100644 --- a/tests/perf_bench/core_import_mpy_single.py +++ b/tests/perf_bench/core_import_mpy_single.py @@ -8,7 +8,7 @@ print("SKIP") raise SystemExit -# This is the test.py file that is compiled to test.mpy below. +# This is the test.py file that is compiled to test.mpy below. mpy-cross must be invoked with `-msmall-int-bits=30`. # Many known and unknown names/strings are included to test the linking process. """ class A0: @@ -78,7 +78,7 @@ def f1(): x = ("const tuple 9", None, False, True, 1, 2, 3) result = 123 """ -file_data = b"M\x06\x00\x1f\x81=\x1e\x0etest.py\x00\x0f\x04A0\x00\x04A1\x00\x04f0\x00\x04f1\x00\x0cresult\x00/-5\x04a0\x00\x04a1\x00\x04a2\x00\x04a3\x00\x13\x15\x17\x19\x1b\x1d\x1f!#%')+1379;=?ACEGIKMOQSUWY[]_acegikmoqsuwy{}\x7f\x81\x01\x81\x03\x81\x05\x81\x07\x81\t\x81\x0b\x81\r\x81\x0f\x81\x11\x81\x13\x81\x15\x81\x17\x81\x19\x81\x1b\x81\x1d\x81\x1f\x81!\x81#\x81%\x81'\x81)\x81+\x81-\x81/\x811\x813\x815\x817\x819\x81;\x81=\x81?\x81A\x81C\x81E\x81G\x81I\x81K\x81M\x81O\x81Q\x81S\x81U\x81W\x81Y\x81[\x81]\x81_\x81a\x81c\x81e\x81g\x81i\x81k\x81m\x81o\x81q\x81s\x81u\x81w\x81y\x81{\x81}\x81\x7f\x82\x01\x82\x03\x82\x05\x82\x07\x82\t\x82\x0b\x82\r\x82\x0f\x82\x11\x82\x13\x82\x15\x82\x17\x82\x19\x82\x1b\x82\x1d\x82\x1f\x82!\x82#\x82%\x82'\x82)\x82+\x82-\x82/\x821\x823\x825\x827\x829\x82;\x82=\x82?\x82A\x82E\x82G\x82I\x82K\nname0\x00\nname1\x00\nname2\x00\nname3\x00\nname4\x00\nname5\x00\nname6\x00\nname7\x00\nname8\x00\nname9\x00$quite_a_long_name0\x00$quite_a_long_name1\x00$quite_a_long_name2\x00$quite_a_long_name3\x00$quite_a_long_name4\x00$quite_a_long_name5\x00$quite_a_long_name6\x00$quite_a_long_name7\x00$quite_a_long_name8\x00$quite_a_long_name9\x00&quite_a_long_name10\x00&quite_a_long_name11\x00\x05\x1ethis will be a string object 0\x00\x05\x1ethis will be a string object 1\x00\x05\x1ethis will be a string object 2\x00\x05\x1ethis will be a string object 3\x00\x05\x1ethis will be a string object 4\x00\x05\x1ethis will be a string object 5\x00\x05\x1ethis will be a string object 6\x00\x05\x1ethis will be a string object 7\x00\x05\x1ethis will be a string object 8\x00\x05\x1ethis will be a string object 9\x00\x06\x1dthis will be a bytes object 0\x00\x06\x1dthis will be a bytes object 1\x00\x06\x1dthis will be a bytes object 2\x00\x06\x1dthis will be a bytes object 3\x00\x06\x1dthis will be a bytes object 4\x00\x06\x1dthis will be a bytes object 5\x00\x06\x1dthis will be a bytes object 6\x00\x06\x1dthis will be a bytes object 7\x00\x06\x1dthis will be a bytes object 8\x00\x06\x1dthis will be a bytes object 9\x00\n\x07\x05\rconst tuple 0\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 1\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 2\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 3\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 4\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 5\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 6\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 7\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 8\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 9\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x82d\x10\x12\x01i@i@\x84\x18\x84\x1fT2\x00\x10\x024\x02\x16\x02T2\x01\x10\x034\x02\x16\x032\x02\x16\x042\x03\x16\x05\"\x80{\x16\x06Qc\x04\x82\x0c\x00\n\x02($$$\x11\x07\x16\x08\x10\x02\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04@\t\x08\n\x81\x0b Qc@\t\x08\x0b\x81\x0b@Qc@\t\x08\x0c\x81\x0b`QcH\t\n\r\x81\x0b` Qc\x82\x14\x00\x0c\x03h`$$$\x11\x07\x16\x08\x10\x03\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04H\t\n\n\x81\x0b``QcH\t\n\x0b\x81\x0b\x80\x07QcH\t\n\x0c\x81\x0b\x80\x08QcH\t\n\r\x81\x0b\x80\tQc\xa08P:\x04\x80\x0b13///---997799<\x1f%\x1f\"\x1f%)\x1f\"//\x12\x0e\x12\x0f\x12\x10\x12\x11\x12\x12\x12\x13\x12\x14*\x07Y\x12\x15\x12\x16\x12\x17\x12\x18\x12\x19\x12\x1a\x12\x08\x12\x07*\x08Y\x12\x1b\x12\x1c\x12\t\x12\x1d\x12\x1e\x12\x1f*\x06Y\x12 \x12!\x12\"\x12#\x12$\x12%*\x06Y\x12&\x12'\x12(\x12)\x12*\x12+*\x06Y\x12,\x12-\x12.\x12/\x120*\x05Y\x121\x122\x123\x124\x125*\x05Y\x126\x127\x128\x129\x12:*\x05Y\x12;\x12<\x12=\x12>\x12?\x12@\x12A\x12B\x12C\x12D\x12E*\x0bY\x12F\x12G\x12H\x12I\x12J\x12K\x12L\x12M\x12N\x12O\x12P*\x0bY\x12Q\x12R\x12S\x12T\x12U\x12V\x12W\x12X\x12Y\x12Z*\nY\x12[\x12\\\x12]\x12^\x12_\x12`\x12a\x12b\x12c\x12d*\nY\x12e\x12f\x12g\x12h\x12i\x12j\x12k\x12l\x12m\x12n\x12o*\x0bY\x12p\x12q\x12r\x12s\x12t\x12u\x12v\x12w\x12x\x12y\x12z*\x0bY\x12{\x12|\x12}\x12~\x12\x7f\x12\x81\x00\x12\x81\x01\x12\x81\x02\x12\x81\x03\x12\x81\x04*\nY\x12\x81\x05\x12\x81\x06\x12\x81\x07\x12\x81\x08\x12\x81\t\x12\x81\n\x12\x81\x0b\x12\x81\x0c\x12\x81\r\x12\x81\x0e\x12\x81\x0f*\x0bY\x12\x81\x10\x12\x81\x11\x12\x81\x12\x12\x81\x13\x12\x81\x14\x12\x81\x15\x12\x81\x16\x12\x81\x17\x12\x81\x18\x12\x81\x19*\nY\x12\x81\x1a\x12\x81\x1b\x12\x81\x1c\x12\x81\x1d\x12\x81\x1e\x12\x81\x1f\x12\x81 \x12\x81!\x12\x81\"\x12\x81#\x12\x81$*\x0bY\x12\x81%\x12\x81&*\x02Y\x12\x81'\x12\x81(\x12\x81)\x12\x81*\x12\x81+\x12\x81,\x12\x81-\x12\x81.\x12\x81/\x12\x810*\nY\x12\x811\x12\x812\x12\x813\x12\x814*\x04Y\x12\x815\x12\x816\x12\x817\x12\x818*\x04Y\x12\x819\x12\x81:\x12\x81;\x12\x81<*\x04YQc\x87p\x08@\x05\x80###############################\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14\xc0#\x15\xc0#\x16\xc0#\x17\xc0#\x18\xc0#\x19\xc0#\x1a\xc0#\x1b\xc0#\x1c\xc0#\x1d\xc0Qc" +file_data = b"M\x06\x00\x1e\x81=\x1e\x0etest.py\x00\x0f\x04A0\x00\x04A1\x00\x04f0\x00\x04f1\x00\x0cresult\x00/-5\x04a0\x00\x04a1\x00\x04a2\x00\x04a3\x00\x13\x15\x17\x19\x1b\x1d\x1f!#%')+1379;=?ACEGIKMOQSUWY[]_acegikmoqsuwy{}\x7f\x81\x01\x81\x03\x81\x05\x81\x07\x81\t\x81\x0b\x81\r\x81\x0f\x81\x11\x81\x13\x81\x15\x81\x17\x81\x19\x81\x1b\x81\x1d\x81\x1f\x81!\x81#\x81%\x81'\x81)\x81+\x81-\x81/\x811\x813\x815\x817\x819\x81;\x81=\x81?\x81A\x81C\x81E\x81G\x81I\x81K\x81M\x81O\x81Q\x81S\x81U\x81W\x81Y\x81[\x81]\x81_\x81a\x81c\x81e\x81g\x81i\x81k\x81m\x81o\x81q\x81s\x81u\x81w\x81y\x81{\x81}\x81\x7f\x82\x01\x82\x03\x82\x05\x82\x07\x82\t\x82\x0b\x82\r\x82\x0f\x82\x11\x82\x13\x82\x15\x82\x17\x82\x19\x82\x1b\x82\x1d\x82\x1f\x82!\x82#\x82%\x82'\x82)\x82+\x82-\x82/\x821\x823\x825\x827\x829\x82;\x82=\x82?\x82A\x82E\x82G\x82I\x82K\nname0\x00\nname1\x00\nname2\x00\nname3\x00\nname4\x00\nname5\x00\nname6\x00\nname7\x00\nname8\x00\nname9\x00$quite_a_long_name0\x00$quite_a_long_name1\x00$quite_a_long_name2\x00$quite_a_long_name3\x00$quite_a_long_name4\x00$quite_a_long_name5\x00$quite_a_long_name6\x00$quite_a_long_name7\x00$quite_a_long_name8\x00$quite_a_long_name9\x00&quite_a_long_name10\x00&quite_a_long_name11\x00\x05\x1ethis will be a string object 0\x00\x05\x1ethis will be a string object 1\x00\x05\x1ethis will be a string object 2\x00\x05\x1ethis will be a string object 3\x00\x05\x1ethis will be a string object 4\x00\x05\x1ethis will be a string object 5\x00\x05\x1ethis will be a string object 6\x00\x05\x1ethis will be a string object 7\x00\x05\x1ethis will be a string object 8\x00\x05\x1ethis will be a string object 9\x00\x06\x1dthis will be a bytes object 0\x00\x06\x1dthis will be a bytes object 1\x00\x06\x1dthis will be a bytes object 2\x00\x06\x1dthis will be a bytes object 3\x00\x06\x1dthis will be a bytes object 4\x00\x06\x1dthis will be a bytes object 5\x00\x06\x1dthis will be a bytes object 6\x00\x06\x1dthis will be a bytes object 7\x00\x06\x1dthis will be a bytes object 8\x00\x06\x1dthis will be a bytes object 9\x00\n\x07\x05\rconst tuple 0\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 1\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 2\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 3\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 4\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 5\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 6\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 7\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 8\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\n\x07\x05\rconst tuple 9\x00\x01\x02\x03\x07\x011\x07\x012\x07\x013\x82d\x10\x12\x01i@i@\x84\x18\x84\x1fT2\x00\x10\x024\x02\x16\x02T2\x01\x10\x034\x02\x16\x032\x02\x16\x042\x03\x16\x05\"\x80{\x16\x06Qc\x04\x82\x0c\x00\n\x02($$$\x11\x07\x16\x08\x10\x02\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04@\t\x08\n\x81\x0b Qc@\t\x08\x0b\x81\x0b@Qc@\t\x08\x0c\x81\x0b`QcH\t\n\r\x81\x0b` Qc\x82\x14\x00\x0c\x03h`$$$\x11\x07\x16\x08\x10\x03\x16\t2\x00\x16\n2\x01\x16\x0b2\x02\x16\x0c2\x03\x16\rQc\x04H\t\n\n\x81\x0b``QcH\t\n\x0b\x81\x0b\x80\x07QcH\t\n\x0c\x81\x0b\x80\x08QcH\t\n\r\x81\x0b\x80\tQc\xa08P:\x04\x80\x0b13///---997799<\x1f%\x1f\"\x1f%)\x1f\"//\x12\x0e\x12\x0f\x12\x10\x12\x11\x12\x12\x12\x13\x12\x14*\x07Y\x12\x15\x12\x16\x12\x17\x12\x18\x12\x19\x12\x1a\x12\x08\x12\x07*\x08Y\x12\x1b\x12\x1c\x12\t\x12\x1d\x12\x1e\x12\x1f*\x06Y\x12 \x12!\x12\"\x12#\x12$\x12%*\x06Y\x12&\x12'\x12(\x12)\x12*\x12+*\x06Y\x12,\x12-\x12.\x12/\x120*\x05Y\x121\x122\x123\x124\x125*\x05Y\x126\x127\x128\x129\x12:*\x05Y\x12;\x12<\x12=\x12>\x12?\x12@\x12A\x12B\x12C\x12D\x12E*\x0bY\x12F\x12G\x12H\x12I\x12J\x12K\x12L\x12M\x12N\x12O\x12P*\x0bY\x12Q\x12R\x12S\x12T\x12U\x12V\x12W\x12X\x12Y\x12Z*\nY\x12[\x12\\\x12]\x12^\x12_\x12`\x12a\x12b\x12c\x12d*\nY\x12e\x12f\x12g\x12h\x12i\x12j\x12k\x12l\x12m\x12n\x12o*\x0bY\x12p\x12q\x12r\x12s\x12t\x12u\x12v\x12w\x12x\x12y\x12z*\x0bY\x12{\x12|\x12}\x12~\x12\x7f\x12\x81\x00\x12\x81\x01\x12\x81\x02\x12\x81\x03\x12\x81\x04*\nY\x12\x81\x05\x12\x81\x06\x12\x81\x07\x12\x81\x08\x12\x81\t\x12\x81\n\x12\x81\x0b\x12\x81\x0c\x12\x81\r\x12\x81\x0e\x12\x81\x0f*\x0bY\x12\x81\x10\x12\x81\x11\x12\x81\x12\x12\x81\x13\x12\x81\x14\x12\x81\x15\x12\x81\x16\x12\x81\x17\x12\x81\x18\x12\x81\x19*\nY\x12\x81\x1a\x12\x81\x1b\x12\x81\x1c\x12\x81\x1d\x12\x81\x1e\x12\x81\x1f\x12\x81 \x12\x81!\x12\x81\"\x12\x81#\x12\x81$*\x0bY\x12\x81%\x12\x81&*\x02Y\x12\x81'\x12\x81(\x12\x81)\x12\x81*\x12\x81+\x12\x81,\x12\x81-\x12\x81.\x12\x81/\x12\x810*\nY\x12\x811\x12\x812\x12\x813\x12\x814*\x04Y\x12\x815\x12\x816\x12\x817\x12\x818*\x04Y\x12\x819\x12\x81:\x12\x81;\x12\x81<*\x04YQc\x87p\x08@\x05\x80###############################\x00\xc0#\x01\xc0#\x02\xc0#\x03\xc0#\x04\xc0#\x05\xc0#\x06\xc0#\x07\xc0#\x08\xc0#\t\xc0#\n\xc0#\x0b\xc0#\x0c\xc0#\r\xc0#\x0e\xc0#\x0f\xc0#\x10\xc0#\x11\xc0#\x12\xc0#\x13\xc0#\x14\xc0#\x15\xc0#\x16\xc0#\x17\xc0#\x18\xc0#\x19\xc0#\x1a\xc0#\x1b\xc0#\x1c\xc0#\x1d\xc0Qc" class File(io.IOBase): diff --git a/tests/run-internalbench.py b/tests/run-internalbench.py index c9f783e474c9c..99c6304afe9d6 100755 --- a/tests/run-internalbench.py +++ b/tests/run-internalbench.py @@ -8,6 +8,10 @@ from glob import glob from collections import defaultdict +run_tests_module = __import__("run-tests") +sys.path.append(run_tests_module.base_path("../tools")) +import pyboard + if os.name == "nt": MICROPYTHON = os.getenv( "MICROPY_MICROPYTHON", "../ports/windows/build-standard/micropython.exe" @@ -15,13 +19,39 @@ else: MICROPYTHON = os.getenv("MICROPY_MICROPYTHON", "../ports/unix/build-standard/micropython") +injected_bench_code = b""" +import time + +class bench_class: + ITERS = 20000000 + + @staticmethod + def run(test): + t = time.ticks_us() + test(bench_class.ITERS) + t = time.ticks_diff(time.ticks_us(), t) + s, us = divmod(t, 1_000_000) + print("{}.{:06}".format(s, us)) + +import sys +sys.modules['bench'] = bench_class +""" + -def run_tests(pyb, test_dict): +def execbench(pyb, filename, iters): + with open(filename, "rb") as f: + pyfile = f.read() + code = (injected_bench_code + pyfile).replace(b"20000000", str(iters).encode("utf-8")) + return pyb.exec(code).replace(b"\r\n", b"\n") + + +def run_tests(pyb, test_dict, iters): test_count = 0 testcase_count = 0 for base_test, tests in sorted(test_dict.items()): print(base_test + ":") + baseline = None for test_file in tests: # run MicroPython if pyb is None: @@ -36,20 +66,25 @@ def run_tests(pyb, test_dict): # run on pyboard pyb.enter_raw_repl() try: - output_mupy = pyb.execfile(test_file).replace(b"\r\n", b"\n") + output_mupy = execbench(pyb, test_file[0], iters) except pyboard.PyboardError: output_mupy = b"CRASH" - output_mupy = float(output_mupy.strip()) + try: + output_mupy = float(output_mupy.strip()) + except ValueError: + output_mupy = -1 test_file[1] = output_mupy testcase_count += 1 - test_count += 1 - baseline = None - for t in tests: if baseline is None: - baseline = t[1] - print(" %.3fs (%+06.2f%%) %s" % (t[1], (t[1] * 100 / baseline) - 100, t[0])) + baseline = test_file[1] + print( + " %.3fs (%+06.2f%%) %s" + % (test_file[1], (test_file[1] * 100 / baseline) - 100, test_file[0]) + ) + + test_count += 1 print("{} tests performed ({} individual testcases)".format(test_count, testcase_count)) @@ -58,27 +93,47 @@ def run_tests(pyb, test_dict): def main(): - cmd_parser = argparse.ArgumentParser(description="Run tests for MicroPython.") - cmd_parser.add_argument("--pyboard", action="store_true", help="run the tests on the pyboard") + cmd_parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=f"""Run and manage tests for MicroPython. + +{run_tests_module.test_instance_description} +{run_tests_module.test_directory_description} +""", + epilog=run_tests_module.test_instance_epilog, + ) + cmd_parser.add_argument( + "-t", "--test-instance", default="unix", help="the MicroPython instance to test" + ) + cmd_parser.add_argument( + "-b", "--baudrate", default=115200, help="the baud rate of the serial device" + ) + cmd_parser.add_argument("-u", "--user", default="micro", help="the telnet login username") + cmd_parser.add_argument("-p", "--password", default="python", help="the telnet login password") + cmd_parser.add_argument( + "-d", "--test-dirs", nargs="*", help="input test directories (if no files given)" + ) + cmd_parser.add_argument( + "-I", + "--iters", + type=int, + default=200_000, + help="number of test iterations, only for remote instances (default 200,000)", + ) cmd_parser.add_argument("files", nargs="*", help="input test files") args = cmd_parser.parse_args() # Note pyboard support is copied over from run-tests.py, not tests, and likely needs revamping - if args.pyboard: - import pyboard - - pyb = pyboard.Pyboard("/dev/ttyACM0") - pyb.enter_raw_repl() - else: - pyb = None + pyb = run_tests_module.get_test_instance( + args.test_instance, args.baudrate, args.user, args.password + ) if len(args.files) == 0: - if pyb is None: - # run PC tests - test_dirs = ("internal_bench",) + if args.test_dirs: + test_dirs = tuple(args.test_dirs) else: - # run pyboard tests - test_dirs = ("basics", "float", "pyb") + test_dirs = ("internal_bench",) + tests = sorted( test_file for test_files in (glob("{}/*.py".format(dir)) for dir in test_dirs) @@ -95,7 +150,7 @@ def main(): continue test_dict[m.group(1)].append([t, None]) - if not run_tests(pyb, test_dict): + if not run_tests(pyb, test_dict, args.iters): sys.exit(1) diff --git a/tests/run-multitests.py b/tests/run-multitests.py index 387eec7018bb3..e5458ffe0d00c 100755 --- a/tests/run-multitests.py +++ b/tests/run-multitests.py @@ -15,6 +15,8 @@ import subprocess import tempfile +run_tests_module = __import__("run-tests") + test_dir = os.path.abspath(os.path.dirname(__file__)) if os.path.abspath(sys.path[0]) == test_dir: @@ -130,6 +132,11 @@ def get_host_ip(_ip_cache=[]): return _ip_cache[0] +def decode(output): + # Convenience function to convert raw process or serial output to ASCII + return str(output, "ascii", "backslashreplace") + + class PyInstance: def __init__(self): pass @@ -188,7 +195,7 @@ def run_script(self, script): output = p.stdout except subprocess.CalledProcessError as er: err = er - return str(output.strip(), "ascii"), err + return decode(output.strip()), err def start_script(self, script): self.popen = subprocess.Popen( @@ -215,7 +222,7 @@ def readline(self): self.finished = self.popen.poll() is not None return None, None else: - return str(out.rstrip(), "ascii"), None + return decode(out.rstrip()), None def write(self, data): self.popen.stdin.write(data) @@ -227,21 +234,12 @@ def is_finished(self): def wait_finished(self): self.popen.wait() out = self.popen.stdout.read() - return str(out, "ascii"), "" + return decode(out), "" class PyInstancePyboard(PyInstance): - @staticmethod - def map_device_shortcut(device): - if device[0] == "a" and device[1:].isdigit(): - return "/dev/ttyACM" + device[1:] - elif device[0] == "u" and device[1:].isdigit(): - return "/dev/ttyUSB" + device[1:] - else: - return device - def __init__(self, device): - device = self.map_device_shortcut(device) + device = device self.device = device self.pyb = pyboard.Pyboard(device) self.pyb.enter_raw_repl() @@ -262,7 +260,7 @@ def run_script(self, script): output = self.pyb.exec_(script) except pyboard.PyboardError as er: err = er - return str(output.strip(), "ascii"), err + return decode(output.strip()), err def start_script(self, script): self.pyb.enter_raw_repl() @@ -281,13 +279,13 @@ def readline(self): if out.endswith(b"\x04"): self.finished = True out = out[:-1] - err = str(self.pyb.read_until(1, b"\x04"), "ascii") + err = decode(self.pyb.read_until(1, b"\x04")) err = err[:-1] if not out and not err: return None, None else: err = None - return str(out.rstrip(), "ascii"), err + return decode(out.rstrip()), err def write(self, data): self.pyb.serial.write(data) @@ -297,7 +295,7 @@ def is_finished(self): def wait_finished(self): out, err = self.pyb.follow(10, None) - return str(out, "ascii"), str(err, "ascii") + return decode(out), decode(err) def prepare_test_file_list(test_files): @@ -488,9 +486,7 @@ def print_diff(a, b): def run_tests(test_files, instances_truth, instances_test): - skipped_tests = [] - passed_tests = [] - failed_tests = [] + test_results = [] for test_file, num_instances in test_files: instances_str = "|".join(str(instances_test[i]) for i in range(num_instances)) @@ -526,13 +522,13 @@ def run_tests(test_files, instances_truth, instances_test): # Print result of test if skip: print("skip") - skipped_tests.append(test_file) + test_results.append((test_file, "skip", "")) elif output_test == output_truth: print("pass") - passed_tests.append(test_file) + test_results.append((test_file, "pass", "")) else: print("FAIL") - failed_tests.append(test_file) + test_results.append((test_file, "fail", "")) if not cmd_args.show_output: print("### TEST ###") print(output_test, end="") @@ -549,15 +545,7 @@ def run_tests(test_files, instances_truth, instances_test): if cmd_args.show_output: print() - print("{} tests performed".format(len(skipped_tests) + len(passed_tests) + len(failed_tests))) - print("{} tests passed".format(len(passed_tests))) - - if skipped_tests: - print("{} tests skipped: {}".format(len(skipped_tests), " ".join(skipped_tests))) - if failed_tests: - print("{} tests failed: {}".format(len(failed_tests), " ".join(failed_tests))) - - return not failed_tests + return test_results def main(): @@ -565,16 +553,24 @@ def main(): cmd_parser = argparse.ArgumentParser( description="Run network tests for MicroPython", + epilog=( + run_tests_module.test_instance_epilog + + "Each instance arg can optionally have custom env provided, eg. ,ENV=VAR,ENV=VAR...\n" + ), formatter_class=argparse.RawTextHelpFormatter, ) cmd_parser.add_argument( "-s", "--show-output", action="store_true", help="show test output after running" ) cmd_parser.add_argument( - "-t", "--trace-output", action="store_true", help="trace test output while running" + "-c", "--trace-output", action="store_true", help="trace test output while running" ) cmd_parser.add_argument( - "-i", "--instance", action="append", default=[], help="instance(s) to run the tests on" + "-t", + "--test-instance", + action="append", + default=[], + help="instance(s) to run the tests on", ) cmd_parser.add_argument( "-p", @@ -583,13 +579,11 @@ def main(): default=1, help="repeat the test with this many permutations of the instance order", ) - cmd_parser.epilog = ( - "Supported instance types:\r\n" - " -i pyb: physical device (eg. pyboard) on provided repl port.\n" - " -i micropython unix micropython instance, path customised with MICROPY_MICROPYTHON env.\n" - " -i cpython desktop python3 instance, path customised with MICROPY_CPYTHON3 env.\n" - " -i exec: custom program run on provided path.\n" - "Each instance arg can optionally have custom env provided, eg. ,ENV=VAR,ENV=VAR...\n" + cmd_parser.add_argument( + "-r", + "--result-dir", + default=run_tests_module.base_path("results"), + help="directory for test results", ) cmd_parser.add_argument("files", nargs="+", help="input test files") cmd_args = cmd_parser.parse_args() @@ -603,33 +597,36 @@ def main(): instances_truth = [PyInstanceSubProcess([PYTHON_TRUTH]) for _ in range(max_instances)] instances_test = [] - for i in cmd_args.instance: + for i in cmd_args.test_instance: # Each instance arg is ,ENV=VAR,ENV=VAR... i = i.split(",") cmd = i[0] env = i[1:] if cmd.startswith("exec:"): instances_test.append(PyInstanceSubProcess([cmd[len("exec:") :]], env)) - elif cmd == "micropython": + elif cmd == "unix": instances_test.append(PyInstanceSubProcess([MICROPYTHON], env)) elif cmd == "cpython": instances_test.append(PyInstanceSubProcess([CPYTHON3], env)) - elif cmd.startswith("pyb:"): - instances_test.append(PyInstancePyboard(cmd[len("pyb:") :])) + elif cmd == "webassembly" or cmd.startswith("execpty:"): + print("unsupported instance string: {}".format(cmd), file=sys.stderr) + sys.exit(2) else: - print("unknown instance string: {}".format(cmd), file=sys.stderr) - sys.exit(1) + device = run_tests_module.convert_device_shortcut_to_real_device(cmd) + instances_test.append(PyInstancePyboard(device)) for _ in range(max_instances - len(instances_test)): instances_test.append(PyInstanceSubProcess([MICROPYTHON])) + os.makedirs(cmd_args.result_dir, exist_ok=True) all_pass = True try: for i, instances_test_permutation in enumerate(itertools.permutations(instances_test)): if i >= cmd_args.permutations: break - all_pass &= run_tests(test_files, instances_truth, instances_test_permutation) + test_results = run_tests(test_files, instances_truth, instances_test_permutation) + all_pass &= run_tests_module.create_test_report(cmd_args, test_results) finally: for i in instances_truth: diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index 340a7f004b061..6d2a975b82a0b 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -9,7 +9,7 @@ import sys import argparse -# CIRCUITPY-CHANGE: no pyboard +run_tests_module = __import__("run-tests") # Paths for host executables CPYTHON3 = os.getenv("MICROPY_CPYTHON3", "python3") @@ -72,6 +72,7 @@ def open(self, path, mode): # CIRCUITPY-CHANGE: no vfs, but still have os os.mount(__FS(), '/__remote') sys.path.insert(0, '/__remote') +{import_prelude} sys.modules['{}'] = __import__('__injected') """ @@ -108,7 +109,7 @@ def run_script(self, script): output = self.pyb.exec_(script) output = output.replace(b"\r\n", b"\n") return output, None - except pyboard.PyboardError as er: + except run_tests_module.pyboard.PyboardError as er: return b"", er @@ -131,7 +132,15 @@ def detect_architecture(target): return platform, arch, None -def run_tests(target_truth, target, args, stats, resolved_arch): +def run_tests(target_truth, target, args, resolved_arch): + global injected_import_hook_code + + prelude = "" + if args.begin: + prelude = args.begin.read() + injected_import_hook_code = injected_import_hook_code.replace("{import_prelude}", prelude) + + test_results = [] for test_file in args.files: # Find supported test test_file_basename = os.path.basename(test_file) @@ -154,9 +163,11 @@ def run_tests(target_truth, target, args, stats, resolved_arch): with open(NATMOD_EXAMPLE_DIR + test_mpy, "rb") as f: test_script += b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n" except OSError: - print("---- {} - mpy file not compiled".format(test_file)) + test_results.append((test_file, "skip", "mpy file not compiled")) + print("skip {} - mpy file not compiled".format(test_file)) continue test_script += bytes(injected_import_hook_code.format(test_module), "ascii") + test_script += b"print('START TEST')\n" test_script += test_file_data # Run test under MicroPython @@ -164,8 +175,18 @@ def run_tests(target_truth, target, args, stats, resolved_arch): # Work out result of test extra = "" + result_out = result_out.removeprefix(b"START TEST\n") if error is None and result_out == b"SKIP\n": result = "SKIP" + elif ( + error is not None + and error.args[0] == "exception" + and error.args[1] == b"" + and b"MemoryError" in error.args[2] + ): + # Test had a MemoryError before anything (should be at least "START TEST") + # was printed, so the test is too big for the target. + result = "LRGE" elif error is not None: result = "FAIL" extra = " - " + str(error) @@ -186,40 +207,63 @@ def run_tests(target_truth, target, args, stats, resolved_arch): result = "pass" # Accumulate statistics - stats["total"] += 1 if result == "pass": - stats["pass"] += 1 + test_results.append((test_file, "pass", "")) elif result == "SKIP": - stats["skip"] += 1 + test_results.append((test_file, "skip", "")) + elif result == "LRGE": + test_results.append((test_file, "skip", "too large")) else: - stats["fail"] += 1 + test_results.append((test_file, "fail", "")) # Print result print("{:4} {}{}".format(result, test_file, extra)) + return test_results + def main(): cmd_parser = argparse.ArgumentParser( - description="Run dynamic-native-module tests under MicroPython" + description="Run dynamic-native-module tests under MicroPython", + epilog=run_tests_module.test_instance_epilog, + formatter_class=argparse.RawDescriptionHelpFormatter, ) cmd_parser.add_argument( - "-p", "--pyboard", action="store_true", help="run tests via pyboard.py" + "-t", "--test-instance", default="unix", help="the MicroPython instance to test" ) + cmd_parser.add_argument("--baudrate", default=115200, help="baud rate of the serial device") + cmd_parser.add_argument("--user", default="micro", help="telnet login username") + cmd_parser.add_argument("--password", default="python", help="telnet login password") cmd_parser.add_argument( - "-d", "--device", default="/dev/ttyACM0", help="the device for pyboard.py" + "-a", "--arch", choices=AVAILABLE_ARCHS, help="override native architecture of the target" ) cmd_parser.add_argument( - "-a", "--arch", choices=AVAILABLE_ARCHS, help="override native architecture of the target" + "-b", + "--begin", + type=argparse.FileType("rt"), + default=None, + help="prologue python file to execute before module import", + ) + cmd_parser.add_argument( + "-r", + "--result-dir", + default=run_tests_module.base_path("results"), + help="directory for test results", ) cmd_parser.add_argument("files", nargs="*", help="input test files") args = cmd_parser.parse_args() target_truth = TargetSubprocess([CPYTHON3]) - if args.pyboard: - target = TargetPyboard(pyboard.Pyboard(args.device)) - else: + target = run_tests_module.get_test_instance( + args.test_instance, args.baudrate, args.user, args.password + ) + if target is None: + # Use the unix port of MicroPython. target = TargetSubprocess([MICROPYTHON]) + else: + # Use a remote target. + target = TargetPyboard(target) if hasattr(args, "arch") and args.arch is not None: target_arch = args.arch @@ -235,20 +279,14 @@ def main(): print("platform={} ".format(target_platform), end="") print("arch={}".format(target_arch)) - stats = {"total": 0, "pass": 0, "fail": 0, "skip": 0} - run_tests(target_truth, target, args, stats, target_arch) + os.makedirs(args.result_dir, exist_ok=True) + test_results = run_tests(target_truth, target, args, target_arch) + res = run_tests_module.create_test_report(args, test_results) target.close() target_truth.close() - print("{} tests performed".format(stats["total"])) - print("{} tests passed".format(stats["pass"])) - if stats["fail"]: - print("{} tests failed".format(stats["fail"])) - if stats["skip"]: - print("{} tests skipped".format(stats["skip"])) - - if stats["fail"]: + if not res: sys.exit(1) diff --git a/tests/run-perfbench.py b/tests/run-perfbench.py index 81d873c45997d..039d11a36111e 100755 --- a/tests/run-perfbench.py +++ b/tests/run-perfbench.py @@ -10,10 +10,9 @@ import argparse from glob import glob -sys.path.append("../tools") -import pyboard +run_tests_module = __import__("run-tests") -prepare_script_for_target = __import__("run-tests").prepare_script_for_target +prepare_script_for_target = run_tests_module.prepare_script_for_target # Paths for host executables if os.name == "nt": @@ -45,12 +44,12 @@ def run_script_on_target(target, script): output = b"" err = None - if isinstance(target, pyboard.Pyboard): + if hasattr(target, "enter_raw_repl"): # Run via pyboard interface try: target.enter_raw_repl() output = target.exec_(script) - except pyboard.PyboardError as er: + except run_tests_module.pyboard.PyboardError as er: err = er else: # Run local executable @@ -90,9 +89,9 @@ def run_benchmark_on_target(target, script): def run_benchmarks(args, target, param_n, param_m, n_average, test_list): + test_results = [] skip_complex = run_feature_test(target, "complex") != "complex" skip_native = run_feature_test(target, "native_check") != "native" - target_had_error = False for test_file in sorted(test_list): print(test_file + ": ", end="") @@ -105,6 +104,7 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): and test_file.find("viper_") != -1 ) if skip: + test_results.append((test_file, "skip", "")) print("SKIP") continue @@ -122,9 +122,10 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): f.write(test_script) # Process script through mpy-cross if needed - if isinstance(target, pyboard.Pyboard) or args.via_mpy: + if hasattr(target, "enter_raw_repl") or args.via_mpy: crash, test_script_target = prepare_script_for_target(args, script_text=test_script) if crash: + test_results.append((test_file, "fail", "preparation")) print("CRASH:", test_script_target) continue else: @@ -162,10 +163,13 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): error = "FAIL truth" if error is not None: - if not error.startswith("SKIP"): - target_had_error = True + if error.startswith("SKIP"): + test_results.append((test_file, "skip", error)) + else: + test_results.append((test_file, "fail", error)) print(error) else: + test_results.append((test_file, "pass", "")) t_avg, t_sd = compute_stats(times) s_avg, s_sd = compute_stats(scores) print( @@ -179,7 +183,7 @@ def run_benchmarks(args, target, param_n, param_m, n_average, test_list): sys.stdout.flush() - return target_had_error + return test_results def parse_output(filename): @@ -190,7 +194,13 @@ def parse_output(filename): m = int(m.split("=")[1]) data = [] for l in f: - if ": " in l and ": SKIP" not in l and "CRASH: " not in l: + if ( + ": " in l + and ": SKIP" not in l + and "CRASH: " not in l + and "skipped: " not in l + and "failed: " not in l + ): name, values = l.strip().split(": ") values = tuple(float(v) for v in values.split()) data.append((name,) + values) @@ -246,17 +256,17 @@ def compute_diff(file1, file2, diff_score): def main(): cmd_parser = argparse.ArgumentParser(description="Run benchmarks for MicroPython") cmd_parser.add_argument( - "-t", "--diff-time", action="store_true", help="diff time outputs from a previous run" + "-m", "--diff-time", action="store_true", help="diff time outputs from a previous run" ) cmd_parser.add_argument( "-s", "--diff-score", action="store_true", help="diff score outputs from a previous run" ) cmd_parser.add_argument( - "-p", "--pyboard", action="store_true", help="run tests via pyboard.py" - ) - cmd_parser.add_argument( - "-d", "--device", default="/dev/ttyACM0", help="the device for pyboard.py" + "-t", "--test-instance", default="unix", help="the MicroPython instance to test" ) + cmd_parser.add_argument("--baudrate", default=115200, help="baud rate of the serial device") + cmd_parser.add_argument("--user", default="micro", help="telnet login username") + cmd_parser.add_argument("--password", default="python", help="telnet login password") cmd_parser.add_argument("-a", "--average", default="8", help="averaging number") cmd_parser.add_argument( "--emit", default="bytecode", help="MicroPython emitter to use (bytecode or native)" @@ -264,6 +274,12 @@ def main(): cmd_parser.add_argument("--heapsize", help="heapsize to use (use default if not specified)") cmd_parser.add_argument("--via-mpy", action="store_true", help="compile code to .mpy first") cmd_parser.add_argument("--mpy-cross-flags", default="", help="flags to pass to mpy-cross") + cmd_parser.add_argument( + "-r", + "--result-dir", + default=run_tests_module.base_path("results"), + help="directory for test results", + ) cmd_parser.add_argument( "N", nargs=1, help="N parameter (approximate target CPU frequency in MHz)" ) @@ -282,15 +298,18 @@ def main(): M = int(args.M[0]) n_average = int(args.average) - if args.pyboard: - if not args.mpy_cross_flags: - args.mpy_cross_flags = "-march=armv7m" - target = pyboard.Pyboard(args.device) - target.enter_raw_repl() - else: + target = run_tests_module.get_test_instance( + args.test_instance, args.baudrate, args.user, args.password + ) + if target is None: + # Use the unix port of MicroPython. target = [MICROPYTHON, "-X", "emit=" + args.emit] if args.heapsize is not None: target.extend(["-X", "heapsize=" + args.heapsize]) + else: + # Use a remote target. + if not args.mpy_cross_flags: + args.mpy_cross_flags = "-march=armv7m" if len(args.files) == 0: tests_skip = ("benchrun.py",) @@ -307,13 +326,15 @@ def main(): print("N={} M={} n_average={}".format(N, M, n_average)) - target_had_error = run_benchmarks(args, target, N, M, n_average, tests) + os.makedirs(args.result_dir, exist_ok=True) + test_results = run_benchmarks(args, target, N, M, n_average, tests) + res = run_tests_module.create_test_report(args, test_results) - if isinstance(target, pyboard.Pyboard): + if hasattr(target, "exit_raw_repl"): target.exit_raw_repl() target.close() - if target_had_error: + if not res: sys.exit(1) diff --git a/tests/run-tests.py b/tests/run-tests.py index cb839ae23dcdf..71570292ce7f7 100755 --- a/tests/run-tests.py +++ b/tests/run-tests.py @@ -15,13 +15,15 @@ import threading import tempfile -# Maximum time to run a PC-based test, in seconds. -TEST_TIMEOUT = 30 +# Maximum time to run a single test, in seconds. +TEST_TIMEOUT = float(os.environ.get("MICROPY_TEST_TIMEOUT", 30)) # See stackoverflow.com/questions/2632199: __file__ nor sys.argv[0] # are guaranteed to always work, this one should though. BASEPATH = os.path.dirname(os.path.abspath(inspect.getsourcefile(lambda: None))) +RV32_ARCH_FLAGS = {"zba": 1 << 0} + def base_path(*p): return os.path.abspath(os.path.join(BASEPATH, *p)).replace("\\", "/") @@ -58,6 +60,23 @@ def base_path(*p): # Set PYTHONIOENCODING so that CPython will use utf-8 on systems which set another encoding in the locale os.environ["PYTHONIOENCODING"] = "utf-8" + +def normalize_newlines(data): + """Normalize newline variations to \\n. + + Only normalizes actual line endings, not literal \\r characters in strings. + Handles \\r\\r\\n and \\r\\n cases to ensure consistent comparison + across different platforms and terminals. + """ + if isinstance(data, bytes): + # Handle PTY double-newline issue first + data = data.replace(b"\r\r\n", b"\n") + # Then handle standard Windows line endings + data = data.replace(b"\r\n", b"\n") + # Don't convert standalone \r as it might be literal content + return data + + # Code to allow a target MicroPython to import an .mpy from RAM # Note: the module is named `__injected_test` but it needs to have `__name__` set to # `__main__` so that the test sees itself as the main module, eg so unittest works. @@ -88,31 +107,82 @@ def getcwd(self): return "" def stat(self, path): if path == '__injected_test.mpy': - return tuple(0 for _ in range(10)) + return (0,0,0,0,0,0,0,0,0,0) else: - raise OSError(-2) # ENOENT + raise OSError(2) # ENOENT def open(self, path, mode): + self.stat(path) return __File() vfs.mount(__FS(), '/__vfstest') os.chdir('/__vfstest') +{import_prologue} __import__('__injected_test') """ # Platforms associated with the unix port, values of `sys.platform`. PC_PLATFORMS = ("darwin", "linux", "win32") +# Mapping from `sys.platform` to the port name, for special cases. +# See `platform_to_port()` function. +platform_to_port_map = {"pyboard": "stm32", "WiPy": "cc3200"} +platform_to_port_map.update({p: "unix" for p in PC_PLATFORMS}) + +# Tests to skip for values of the `--via-mpy` argument. +via_mpy_tests_to_skip = { + # Skip the following when mpy is enabled. + True: ( + # These print out the filename and that's expected to match the .py name. + "import/import_file.py", + "io/argv.py", + "misc/sys_settrace_features.py", + "misc/sys_settrace_generator.py", + "misc/sys_settrace_loop.py", + ), +} + +# Tests to skip for specific emitters. +emitter_tests_to_skip = { + # Some tests are known to fail with native emitter. + # Remove them from the below when they work. + "native": ( + # These require raise_varargs. + "basics/gen_yield_from_close.py", + "basics/try_finally_return2.py", + "basics/try_reraise.py", + "basics/try_reraise2.py", + "misc/features.py", + # These require checking for unbound local. + "basics/annotate_var.py", + "basics/del_deref.py", + "basics/del_local.py", + "basics/scope_implicit.py", + "basics/unboundlocal.py", + # These require "raise from". + "basics/exception_chain.py", + # These require stack-allocated slice optimisation. + "micropython/heapalloc_slice.py", + # These require running the scheduler. + "micropython/schedule.py", + "extmod/asyncio_event_queue.py", + "extmod/asyncio_iterator_event.py", + # These require sys.exc_info(). + "misc/sys_exc_info.py", + # These require sys.settrace(). + "misc/sys_settrace_cov.py", + "misc/sys_settrace_features.py", + "misc/sys_settrace_generator.py", + "misc/sys_settrace_loop.py", + # These are bytecode-specific tests. + "stress/bytecode_limit.py", + ), +} + # Tests to skip on specific targets. # These are tests that are difficult to detect that they should not be run on the given target. platform_tests_to_skip = { - "esp8266": ( - "micropython/viper_args.py", # too large - "micropython/viper_binop_arith.py", # too large - "misc/rge_sm.py", # too large - ), "minimal": ( "basics/class_inplace_op.py", # all special methods not supported "basics/subclass_native_init.py", # native subclassing corner cases not support - "misc/rge_sm.py", # too large "micropython/opt_level.py", # don't assume line numbers are stored ), "nrf": ( @@ -140,14 +210,6 @@ def open(self, path, mode): "thread/thread_lock3.py", "thread/thread_shared2.py", ), - "qemu": ( - # Skip tests that require Cortex-M4. - "inlineasm/thumb/asmfpaddsub.py", - "inlineasm/thumb/asmfpcmp.py", - "inlineasm/thumb/asmfpldrstr.py", - "inlineasm/thumb/asmfpmuldiv.py", - "inlineasm/thumb/asmfpsqrt.py", - ), "webassembly": ( "basics/string_format_modulo.py", # can't print nulls to stdout "basics/string_strip.py", # can't print nulls to stdout @@ -162,6 +224,9 @@ def open(self, path, mode): "extmod/asyncio_new_event_loop.py", "extmod/asyncio_threadsafeflag.py", "extmod/asyncio_wait_for_fwd.py", + "extmod/asyncio_event_queue.py", + "extmod/asyncio_iterator_event.py", + "extmod/asyncio_wait_for_linked_task.py", "extmod/binascii_a2b_base64.py", "extmod/deflate_compress_memory_error.py", # tries to allocate unlimited memory "extmod/re_stack_overflow.py", @@ -184,6 +249,89 @@ def open(self, path, mode): ), } +# These tests don't test float explicitly but rather use it to perform the test. +tests_requiring_float = ( + "extmod/asyncio_basic.py", + "extmod/asyncio_basic2.py", + "extmod/asyncio_cancel_task.py", + "extmod/asyncio_event.py", + "extmod/asyncio_fair.py", + "extmod/asyncio_gather.py", + "extmod/asyncio_gather_notimpl.py", + "extmod/asyncio_get_event_loop.py", + "extmod/asyncio_iterator_event.py", + "extmod/asyncio_lock.py", + "extmod/asyncio_task_done.py", + "extmod/asyncio_wait_for.py", + "extmod/asyncio_wait_for_fwd.py", + "extmod/asyncio_wait_for_linked_task.py", + "extmod/asyncio_wait_task.py", + "extmod/json_dumps_float.py", + "extmod/json_loads_float.py", + "extmod/random_extra_float.py", + "extmod/select_poll_eintr.py", + "extmod/tls_threads.py", + "extmod/uctypes_le_float.py", + "extmod/uctypes_native_float.py", + "extmod/uctypes_sizeof_float.py", + "misc/rge_sm.py", + "ports/unix/ffi_float.py", + "ports/unix/ffi_float2.py", +) + +# These tests don't test slice explicitly but rather use it to perform the test. +tests_requiring_slice = ( + "basics/builtin_range.py", + "basics/bytearray1.py", + "basics/class_super.py", + "basics/containment.py", + "basics/errno1.py", + "basics/fun_str.py", + "basics/generator1.py", + "basics/globals_del.py", + "basics/memoryview1.py", + "basics/memoryview_gc.py", + "basics/object1.py", + "basics/python34.py", + "basics/struct_endian.py", + "extmod/btree1.py", + "extmod/deflate_decompress.py", + "extmod/framebuf16.py", + "extmod/framebuf4.py", + "extmod/machine1.py", + "extmod/time_mktime.py", + "extmod/time_res.py", + "extmod/tls_sslcontext_ciphers.py", + "extmod/vfs_fat_fileio1.py", + "extmod/vfs_fat_finaliser.py", + "extmod/vfs_fat_more.py", + "extmod/vfs_fat_ramdisk.py", + "extmod/vfs_fat_ramdisklarge.py", + "extmod/vfs_lfs.py", + "extmod/vfs_rom.py", + "float/string_format_modulo.py", + "micropython/builtin_execfile.py", + "micropython/extreme_exc.py", + "micropython/heapalloc_fail_bytearray.py", + "micropython/heapalloc_fail_list.py", + "micropython/heapalloc_fail_memoryview.py", + "micropython/import_mpy_invalid.py", + "micropython/import_mpy_native.py", + "micropython/import_mpy_native_gc.py", + "misc/non_compliant.py", + "misc/rge_sm.py", +) + +# Tests that require `import target_wiring` to work. +tests_requiring_target_wiring = ( + "extmod/machine_uart_irq_txidle.py", + "extmod/machine_uart_tx.py", + "extmod_hardware/machine_encoder.py", + "extmod_hardware/machine_uart_irq_break.py", + "extmod_hardware/machine_uart_irq_rx.py", + "extmod_hardware/machine_uart_irq_rxidle.py", +) + def rm_f(fname): if os.path.exists(fname): @@ -210,22 +358,31 @@ def convert_regex_escapes(line): return bytes("".join(cs), "utf8") +def platform_to_port(platform): + return platform_to_port_map.get(platform, platform) + + +def convert_device_shortcut_to_real_device(device): + if device.startswith("port:"): + return device.split(":", 1)[1] + elif device.startswith("a") and device[1:].isdigit(): + return "/dev/ttyACM" + device[1:] + elif device.startswith("u") and device[1:].isdigit(): + return "/dev/ttyUSB" + device[1:] + elif device.startswith("c") and device[1:].isdigit(): + return "COM" + device[1:] + else: + return device + + def get_test_instance(test_instance, baudrate, user, password): - if test_instance.startswith("port:"): - _, port = test_instance.split(":", 1) - elif test_instance == "unix": + if test_instance == "unix": return None elif test_instance == "webassembly": return PyboardNodeRunner() - elif test_instance.startswith("a") and test_instance[1:].isdigit(): - port = "/dev/ttyACM" + test_instance[1:] - elif test_instance.startswith("u") and test_instance[1:].isdigit(): - port = "/dev/ttyUSB" + test_instance[1:] - elif test_instance.startswith("c") and test_instance[1:].isdigit(): - port = "COM" + test_instance[1:] else: # Assume it's a device path. - port = test_instance + port = convert_device_shortcut_to_real_device(test_instance) global pyboard sys.path.append(base_path("../tools")) @@ -245,46 +402,114 @@ def detect_inline_asm_arch(pyb, args): return None +def map_rv32_arch_flags(flags): + mapped_flags = [] + for extension, bit in RV32_ARCH_FLAGS.items(): + if flags & bit: + mapped_flags.append(extension) + flags &= ~bit + if flags: + raise Exception("Unexpected flag bits set in value {}".format(flags)) + return mapped_flags + + def detect_test_platform(pyb, args): # Run a script to detect various bits of information about the target test instance. output = run_feature_check(pyb, args, "target_info.py") if output.endswith(b"CRASH"): raise ValueError("cannot detect platform: {}".format(output)) - platform, arch = str(output, "ascii").strip().split() + platform, arch, arch_flags, build, thread, float_prec, unicode = ( + str(output, "ascii").strip().split() + ) if arch == "None": arch = None inlineasm_arch = detect_inline_asm_arch(pyb, args) + if thread == "None": + thread = None + float_prec = int(float_prec) + unicode = unicode == "True" + if arch == "rv32imc": + arch_flags = map_rv32_arch_flags(int(arch_flags)) + else: + arch_flags = None args.platform = platform args.arch = arch + args.arch_flags = arch_flags if arch and not args.mpy_cross_flags: args.mpy_cross_flags = "-march=" + arch + if arch_flags: + args.mpy_cross_flags += " -march-flags=" + ",".join(arch_flags) args.inlineasm_arch = inlineasm_arch + args.build = build + args.thread = thread + args.float_prec = float_prec + args.unicode = unicode + # Print the detected information about the target. print("platform={}".format(platform), end="") if arch: print(" arch={}".format(arch), end="") + if arch_flags: + print(" arch_flags={}".format(",".join(arch_flags)), end="") if inlineasm_arch: print(" inlineasm={}".format(inlineasm_arch), end="") - print() - - -def prepare_script_for_target(args, *, script_filename=None, script_text=None, force_plain=False): + if thread: + print(" thread={}".format(thread), end="") + if float_prec: + print(" float={}-bit".format(float_prec), end="") + if unicode: + print(" unicode", end="") + + +def detect_target_wiring_script(pyb, args): + tw_data = b"" + tw_source = None + if args.target_wiring: + # A target_wiring path is explicitly provided, so use that. + tw_source = args.target_wiring + with open(tw_source, "rb") as f: + tw_data = f.read() + elif hasattr(pyb, "exec_raw") and pyb.exec_raw("import target_wiring") == (b"", b""): + # The board already has a target_wiring module available, so use that. + tw_source = "on-device" + else: + port = platform_to_port(args.platform) + build = args.build + tw_board_exact = None + tw_board_partial = None + tw_port = None + for file in os.listdir("target_wiring"): + file_base = file.removesuffix(".py") + if file_base == build: + # A file matching the target's board/build name. + tw_board_exact = file + elif file_base.endswith("x") and build.startswith(file_base.removesuffix("x")): + # A file with a partial match to the target's board/build name. + tw_board_partial = file + elif file_base == port: + # A file matching the target's port. + tw_port = file + tw_source = tw_board_exact or tw_board_partial or tw_port + if tw_source: + with open("target_wiring/" + tw_source, "rb") as f: + tw_data = f.read() + if tw_source: + print(" target_wiring={}".format(tw_source), end="") + pyb.target_wiring_script = tw_data + + +def prepare_script_for_target(args, *, script_text=None, force_plain=False): if force_plain or (not args.via_mpy and args.emit == "bytecode"): - if script_filename is not None: - with open(script_filename, "rb") as f: - script_text = f.read() + # A plain test to run as-is, no processing needed. + pass elif args.via_mpy: tempname = tempfile.mktemp(dir="") mpy_filename = tempname + ".mpy" - if script_filename is None: - script_filename = tempname + ".py" - cleanup_script_filename = True - with open(script_filename, "wb") as f: - f.write(script_text) - else: - cleanup_script_filename = False + script_filename = tempname + ".py" + with open(script_filename, "wb") as f: + f.write(script_text) try: subprocess.check_output( @@ -300,8 +525,7 @@ def prepare_script_for_target(args, *, script_filename=None, script_text=None, f script_text = b"__buf=" + bytes(repr(f.read()), "ascii") + b"\n" rm_f(mpy_filename) - if cleanup_script_filename: - rm_f(script_filename) + rm_f(script_filename) script_text += bytes(injected_import_hook_code, "ascii") else: @@ -312,34 +536,61 @@ def prepare_script_for_target(args, *, script_filename=None, script_text=None, f def run_script_on_remote_target(pyb, args, test_file, is_special): - had_crash, script = prepare_script_for_target( - args, script_filename=test_file, force_plain=is_special - ) + with open(test_file, "rb") as f: + script = f.read() + + # If the test is not a special test, prepend it with a print to indicate that it started. + # If the print does not execute this means that the test did not even start, eg it was + # too large for the target. + prepend_start_test = not is_special + if prepend_start_test: + if script.startswith(b"#"): + script = b"print('START TEST')" + script + else: + script = b"print('START TEST')\n" + script + + had_crash, script = prepare_script_for_target(args, script_text=script, force_plain=is_special) + if had_crash: return True, script try: had_crash = False pyb.enter_raw_repl() - output_mupy = pyb.exec_(script) + if test_file.endswith(tests_requiring_target_wiring) and pyb.target_wiring_script: + pyb.exec_( + "import sys;sys.modules['target_wiring']=__build_class__(lambda:exec(" + + repr(pyb.target_wiring_script) + + "),'target_wiring')" + ) + output_mupy = pyb.exec_(script, timeout=TEST_TIMEOUT) except pyboard.PyboardError as e: had_crash = True if not is_special and e.args[0] == "exception": - output_mupy = e.args[1] + e.args[2] + b"CRASH" + if prepend_start_test and e.args[1] == b"" and b"MemoryError" in e.args[2]: + output_mupy = b"SKIP-TOO-LARGE\n" + else: + output_mupy = e.args[1] + e.args[2] + b"CRASH" else: output_mupy = bytes(e.args[0], "ascii") + b"\nCRASH" + + if prepend_start_test: + if output_mupy.startswith(b"START TEST\r\n"): + output_mupy = output_mupy.removeprefix(b"START TEST\r\n") + else: + had_crash = True + return had_crash, output_mupy -special_tests = [ +tests_with_regex_output = [ base_path(file) for file in ( + # CIRCUITPY-CHANGE: removal and additions "micropython/meminfo.py", "basics/bytes_compare3.py", "basics/builtin_help.py", "thread/thread_exc2.py", - # CIRCUITPY-CHANGE: removal and additions - # REMOVE "esp32/partition_ota.py", "circuitpython/traceback_test.py", "circuitpython/traceback_test_chained.py", ) @@ -350,10 +601,7 @@ def run_micropython(pyb, args, test_file, test_file_abspath, is_special=False): had_crash = False if pyb is None: # run on PC - if ( - test_file_abspath.startswith((base_path("cmdline/"), base_path("feature_check/"))) - or test_file_abspath in special_tests - ): + if test_file_abspath.startswith((base_path("cmdline/"), base_path("feature_check/"))): # special handling for tests of the unix cmdline program is_special = True @@ -392,6 +640,10 @@ def get(required=False): return rv def send_get(what): + # Detect {\x00} pattern and convert to ctrl-key codes. + ctrl_code = lambda m: bytes([int(m.group(1))]) + what = re.sub(rb"{\\x(\d\d)}", ctrl_code, what) + os.write(master, what) return get() @@ -471,17 +723,17 @@ def send_get(what): ) # canonical form for all ports/platforms is to use \n for end-of-line - output_mupy = output_mupy.replace(b"\r\n", b"\n") + output_mupy = normalize_newlines(output_mupy) # don't try to convert the output if we should skip this test - if had_crash or output_mupy in (b"SKIP\n", b"CRASH"): + if had_crash or output_mupy in (b"SKIP\n", b"SKIP-TOO-LARGE\n", b"CRASH"): return output_mupy # skipped special tests will output "SKIP" surrounded by other interpreter debug output if is_special and not had_crash and b"\nSKIP\n" in output_mupy: return b"SKIP\n" - if is_special or test_file_abspath in special_tests: + if is_special or test_file_abspath in tests_with_regex_output: # convert parts of the output that are not stable across runs with open(test_file + ".exp", "rb") as f: lines_exp = [] @@ -603,30 +855,25 @@ def run_script_on_remote_target(self, args, test_file, is_special): def run_tests(pyb, tests, args, result_dir, num_threads=1): - test_count = ThreadSafeCounter() testcase_count = ThreadSafeCounter() - passed_count = ThreadSafeCounter() - failed_tests = ThreadSafeCounter([]) - skipped_tests = ThreadSafeCounter([]) + test_results = ThreadSafeCounter([]) skip_tests = set() skip_native = False skip_int_big = False + skip_int_64 = False skip_bytearray = False skip_set_type = False skip_slice = False skip_async = False skip_const = False skip_revops = False - skip_io_module = False skip_fstring = False skip_endian = False skip_inlineasm = False has_complex = True has_coverage = False - upy_float_precision = 32 - if True: # Even if we run completely different tests in a different directory, # we need to access feature_checks from the same directory as the @@ -642,6 +889,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if output != b"1000000000000000000000000000000000000000000000\n": skip_int_big = True + # Check if 'long long' precision integers are supported, even if arbitrary precision is not + output = run_feature_check(pyb, args, "int_64.py") + if output != b"4611686018427387904\n": + skip_int_64 = True + # Check if bytearray is supported, and skip such tests if it's not output = run_feature_check(pyb, args, "bytearray.py") if output != b"bytearray\n": @@ -672,11 +924,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if output == b"TypeError\n": skip_revops = True - # Check if io module exists, and skip such tests if it doesn't - output = run_feature_check(pyb, args, "io_module.py") - if output != b"io\n": - skip_io_module = True - # Check if fstring feature is enabled, and skip such tests if it doesn't output = run_feature_check(pyb, args, "fstring.py") if output != b"a=1\n": @@ -690,13 +937,20 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("inlineasm/thumb/asmbitops.py") skip_tests.add("inlineasm/thumb/asmconst.py") skip_tests.add("inlineasm/thumb/asmdiv.py") + skip_tests.add("inlineasm/thumb/asmit.py") + skip_tests.add("inlineasm/thumb/asmspecialregs.py") + if args.arch not in ("armv7emsp", "armv7emdp"): skip_tests.add("inlineasm/thumb/asmfpaddsub.py") skip_tests.add("inlineasm/thumb/asmfpcmp.py") skip_tests.add("inlineasm/thumb/asmfpldrstr.py") skip_tests.add("inlineasm/thumb/asmfpmuldiv.py") skip_tests.add("inlineasm/thumb/asmfpsqrt.py") - skip_tests.add("inlineasm/thumb/asmit.py") - skip_tests.add("inlineasm/thumb/asmspecialregs.py") + + if args.inlineasm_arch == "rv32": + # Check if @micropython.asm_rv32 supports Zba instructions, and skip such tests if it doesn't + output = run_feature_check(pyb, args, "inlineasm_rv32_zba.py") + if output != b"rv32_zba\n": + skip_tests.add("inlineasm/rv32/asmzba.py") # Check if emacs repl is supported, and skip such tests if it's not t = run_feature_check(pyb, args, "repl_emacs_check.py") @@ -709,11 +963,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("cmdline/repl_words_move.py") upy_byteorder = run_feature_check(pyb, args, "byteorder.py") - upy_float_precision = run_feature_check(pyb, args, "float.py") - try: - upy_float_precision = int(upy_float_precision) - except ValueError: - upy_float_precision = 0 has_complex = run_feature_check(pyb, args, "complex.py") == b"complex\n" has_coverage = run_feature_check(pyb, args, "coverage.py") == b"coverage\n" cpy_byteorder = subprocess.check_output( @@ -723,24 +972,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_inlineasm = args.inlineasm_arch is None - # These tests don't test slice explicitly but rather use it to perform the test - misc_slice_tests = ( - "builtin_range", - "bytearray1", - "class_super", - "containment", - "errno1", - "fun_str", - "generator1", - "globals_del", - "memoryview1", - "memoryview_gc", - "object1", - "python34", - "string_format_modulo", - "struct_endian", - ) - # Some tests shouldn't be run on GitHub Actions if os.getenv("GITHUB_ACTIONS") == "true": skip_tests.add("thread/stress_schedule.py") # has reliability issues @@ -749,30 +980,31 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # fails with stack overflow on Debug builds skip_tests.add("misc/sys_settrace_features.py") - if upy_float_precision == 0: - skip_tests.add("extmod/uctypes_le_float.py") - skip_tests.add("extmod/uctypes_native_float.py") - skip_tests.add("extmod/uctypes_sizeof_float.py") - skip_tests.add("extmod/json_dumps_float.py") - skip_tests.add("extmod/json_loads_float.py") - skip_tests.add("extmod/random_extra_float.py") - skip_tests.add("misc/rge_sm.py") - if upy_float_precision < 32: + if args.float_prec == 0: + skip_tests.update(tests_requiring_float) + if args.float_prec < 32: skip_tests.add( "float/float2int_intbig.py" ) # requires fp32, there's float2int_fp30_intbig.py instead skip_tests.add( - "float/string_format.py" - ) # requires fp32, there's string_format_fp30.py instead + "float/float_struct_e.py" + ) # requires fp32, there's float_struct_e_fp30.py instead skip_tests.add("float/bytes_construct.py") # requires fp32 skip_tests.add("float/bytearray_construct.py") # requires fp32 skip_tests.add("float/float_format_ints_power10.py") # requires fp32 - if upy_float_precision < 64: + if args.float_prec < 64: skip_tests.add("float/float_divmod.py") # tested by float/float_divmod_relaxed.py instead skip_tests.add("float/float2int_doubleprec_intbig.py") + skip_tests.add("float/float_struct_e_doubleprec.py") skip_tests.add("float/float_format_ints_doubleprec.py") skip_tests.add("float/float_parse_doubleprec.py") + if not args.unicode: + skip_tests.add("extmod/json_loads.py") # tests loading a utf-8 character + + if skip_slice: + skip_tests.update(tests_requiring_slice) + if not has_complex: skip_tests.add("float/complex1.py") skip_tests.add("float/complex1_intbig.py") @@ -788,8 +1020,8 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): skip_tests.add("cmdline/repl_sys_ps1_ps2.py") skip_tests.add("extmod/ssl_poll.py") - # Skip thread mutation tests on targets that don't have the GIL. - if args.platform in PC_PLATFORMS + ("rp2",): + # Skip thread mutation tests on targets that have unsafe threading behaviour. + if args.thread == "unsafe": for t in tests: if t.startswith("thread/mutate_"): skip_tests.add(t) @@ -798,6 +1030,15 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): if args.platform not in PC_PLATFORMS: skip_tests.add("basics/exception_chain.py") # warning is not printed skip_tests.add("micropython/meminfo.py") # output is very different to PC output + skip_tests.add("unicode/file1.py") # requires local file access + skip_tests.add("unicode/file2.py") # requires local file access + skip_tests.add("unicode/file_invalid.py") # requires local file access + + # Skip certain tests when going via a .mpy file. + skip_tests.update(via_mpy_tests_to_skip.get(args.via_mpy, ())) + + # Skip emitter-specific tests. + skip_tests.update(emitter_tests_to_skip.get(args.emit, ())) # Skip platform-specific tests. skip_tests.update(platform_tests_to_skip.get(args.platform, ())) @@ -812,49 +1053,6 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): # Works but CPython uses '\' path separator skip_tests.add("import/import_file.py") - # Some tests are known to fail with native emitter - # Remove them from the below when they work - if args.emit == "native": - skip_tests.add("basics/gen_yield_from_close.py") # require raise_varargs - skip_tests.update( - {"basics/%s.py" % t for t in "try_reraise try_reraise2".split()} - ) # require raise_varargs - skip_tests.add("basics/annotate_var.py") # requires checking for unbound local - skip_tests.add("basics/del_deref.py") # requires checking for unbound local - skip_tests.add("basics/del_local.py") # requires checking for unbound local - skip_tests.add("basics/exception_chain.py") # raise from is not supported - skip_tests.add("basics/scope_implicit.py") # requires checking for unbound local - skip_tests.add("basics/sys_tracebacklimit.py") # requires traceback info - skip_tests.add("basics/try_finally_return2.py") # requires raise_varargs - skip_tests.add("basics/unboundlocal.py") # requires checking for unbound local - skip_tests.add("misc/features.py") # requires raise_varargs - # CIRCUITPY-CHANGE - skip_tests.update( - ( - "basics/chained_exception.py", - "circuitpython/traceback_test.py", - "circuitpython/traceback_test_chained.py", - ) - ) # because native doesn't have proper traceback info - skip_tests.add( - "misc/print_exception.py" - ) # because native doesn't have proper traceback info - skip_tests.add("misc/sys_exc_info.py") # sys.exc_info() is not supported for native - skip_tests.add("misc/sys_settrace_features.py") # sys.settrace() not supported - skip_tests.add("misc/sys_settrace_generator.py") # sys.settrace() not supported - skip_tests.add("misc/sys_settrace_loop.py") # sys.settrace() not supported - skip_tests.add( - "micropython/emg_exc.py" - ) # because native doesn't have proper traceback info - skip_tests.add( - "micropython/heapalloc_traceback.py" - ) # because native doesn't have proper traceback info - skip_tests.add( - "micropython/opt_level_lineno.py" - ) # native doesn't have proper traceback info - skip_tests.add("micropython/schedule.py") # native code doesn't check pending events - skip_tests.add("stress/bytecode_limit.py") # bytecode specific test - def run_one_test(test_file): test_file = test_file.replace("\\", "/") test_file_abspath = os.path.abspath(test_file).replace("\\", "/") @@ -870,19 +1068,19 @@ def run_one_test(test_file): test_basename = test_file.replace("..", "_").replace("./", "").replace("/", "_") test_name = os.path.splitext(os.path.basename(test_file))[0] - is_native = ( - test_name.startswith("native_") - or test_name.startswith("viper_") - or args.emit == "native" - ) + is_native = test_name.startswith("native_") or test_name.startswith("viper_") is_endian = test_name.endswith("_endian") - is_int_big = test_name.startswith("int_big") or test_name.endswith("_intbig") + is_int_big = ( + test_name.startswith("int_big") + or test_name.endswith("_intbig") + or test_name.startswith("ffi_int") # these tests contain large integer literals + ) + is_int_64 = test_name.startswith("int_64") or test_name.endswith("_int64") is_bytearray = test_name.startswith("bytearray") or test_name.endswith("_bytearray") is_set_type = test_name.startswith(("set_", "frozenset")) or test_name.endswith("_set") - is_slice = test_name.find("slice") != -1 or test_name in misc_slice_tests - is_async = test_name.startswith(("async_", "asyncio_")) + is_slice = test_name.find("slice") != -1 + is_async = test_name.startswith(("async_", "asyncio_")) or test_name.endswith("_async") is_const = test_name.startswith("const") - is_io_module = test_name.startswith("io_") is_fstring = test_name.startswith("string_fstring") is_inlineasm = test_name.startswith("asm") @@ -890,19 +1088,19 @@ def run_one_test(test_file): skip_it |= skip_native and is_native skip_it |= skip_endian and is_endian skip_it |= skip_int_big and is_int_big + skip_it |= skip_int_64 and is_int_64 skip_it |= skip_bytearray and is_bytearray skip_it |= skip_set_type and is_set_type skip_it |= skip_slice and is_slice skip_it |= skip_async and is_async skip_it |= skip_const and is_const skip_it |= skip_revops and "reverse_op" in test_name - skip_it |= skip_io_module and is_io_module skip_it |= skip_fstring and is_fstring skip_it |= skip_inlineasm and is_inlineasm if skip_it: print("skip ", test_file) - skipped_tests.append(test_name) + test_results.append((test_file, "skip", "")) return # Run the test on the MicroPython target. @@ -917,7 +1115,11 @@ def run_one_test(test_file): # start-up code (eg boot.py) when preparing to run the next test. pyb.read_until(1, b"raw REPL; CTRL-B to exit\r\n") print("skip ", test_file) - skipped_tests.append(test_name) + test_results.append((test_file, "skip", "")) + return + elif output_mupy == b"SKIP-TOO-LARGE\n": + print("lrge ", test_file) + test_results.append((test_file, "skip", "too large")) return # Look at the output of the test to see if unittest was used. @@ -954,7 +1156,11 @@ def run_one_test(test_file): # Expected output is result of running unittest. output_expected = None else: - test_file_expected = test_file + ".exp" + # Prefer emitter-specific expected output. + test_file_expected = test_file + "." + args.emit + ".exp" + if not os.path.isfile(test_file_expected): + # Fall back to generic expected output. + test_file_expected = test_file + ".exp" if os.path.isfile(test_file_expected): # Expected output given by a file, so read that in. with open(test_file_expected, "rb") as f: @@ -1012,7 +1218,7 @@ def run_one_test(test_file): # Print test summary, update counters, and save .exp/.out files if needed. if test_passed: print("pass ", test_file, extra_info) - passed_count.increment() + test_results.append((test_file, "pass", "")) rm_f(filename_expected) rm_f(filename_mupy) else: @@ -1024,9 +1230,7 @@ def run_one_test(test_file): rm_f(filename_expected) # in case left over from previous failed run with open(filename_mupy, "wb") as f: f.write(output_mupy) - failed_tests.append((test_name, test_file)) - - test_count.increment() + test_results.append((test_file, "fail", "")) # Print a note if this looks like it might have been a misfired unittest if not uses_unittest and not test_passed: @@ -1053,17 +1257,49 @@ def run_one_test(test_file): print(line) sys.exit(1) - print( - "{} tests performed ({} individual testcases)".format( - test_count.value, testcase_count.value - ) + # Return test results. + return test_results.value, testcase_count.value + + +# Print a summary of the results and save them to a JSON file. +# Returns True if everything succeeded, False otherwise. +def create_test_report(args, test_results, testcase_count=None): + passed_tests = list(r for r in test_results if r[1] == "pass") + skipped_tests = list(r for r in test_results if r[1] == "skip" and r[2] != "too large") + skipped_tests_too_large = list( + r for r in test_results if r[1] == "skip" and r[2] == "too large" ) - print("{} tests passed".format(passed_count.value)) + failed_tests = list(r for r in test_results if r[1] == "fail") + + num_tests_performed = len(passed_tests) + len(failed_tests) + + testcase_count_info = "" + if testcase_count is not None: + testcase_count_info = " ({} individual testcases)".format(testcase_count) + print("{} tests performed{}".format(num_tests_performed, testcase_count_info)) + + print("{} tests passed".format(len(passed_tests))) - skipped_tests = sorted(skipped_tests.value) if len(skipped_tests) > 0: - print("{} tests skipped: {}".format(len(skipped_tests), " ".join(skipped_tests))) - failed_tests = sorted(failed_tests.value) + print( + "{} tests skipped: {}".format( + len(skipped_tests), " ".join(test[0] for test in skipped_tests) + ) + ) + + if len(skipped_tests_too_large) > 0: + print( + "{} tests skipped because they are too large: {}".format( + len(skipped_tests_too_large), " ".join(test[0] for test in skipped_tests_too_large) + ) + ) + + if len(failed_tests) > 0: + print( + "{} tests failed: {}".format( + len(failed_tests), " ".join(test[0] for test in failed_tests) + ) + ) # Serialize regex added by append_filter. def to_json(obj): @@ -1071,23 +1307,22 @@ def to_json(obj): return obj.pattern return obj - with open(os.path.join(result_dir, RESULTS_FILE), "w") as f: + with open(os.path.join(args.result_dir, RESULTS_FILE), "w") as f: json.dump( - {"args": vars(args), "failed_tests": [test[1] for test in failed_tests]}, + { + # The arguments passed on the command-line. + "args": vars(args), + # A list of all results of the form [(test, result, reason), ...]. + "results": list(test for test in test_results), + # A list of failed tests. This is deprecated, use the "results" above instead. + "failed_tests": [test[0] for test in failed_tests], + }, f, default=to_json, ) - if len(failed_tests) > 0: - print( - "{} tests failed: {}".format( - len(failed_tests), " ".join(test[0] for test in failed_tests) - ) - ) - return False - - # all tests succeeded - return True + # Return True only if all tests succeeded. + return len(failed_tests) == 0 class append_filter(argparse.Action): @@ -1104,27 +1339,11 @@ def __call__(self, parser, args, value, option): args.filters.append((option, re.compile(value))) -def main(): - cmd_parser = argparse.ArgumentParser( - formatter_class=argparse.RawDescriptionHelpFormatter, - description="""Run and manage tests for MicroPython. - +test_instance_description = """\ By default the tests are run against the unix port of MicroPython. To run it against something else, use the -t option. See below for details. - -Tests are discovered by scanning test directories for .py files or using the -specified test files. If test files nor directories are specified, the script -expects to be ran in the tests directory (where this file is located) and the -builtin tests suitable for the target platform are ran. - -When running tests, run-tests.py compares the MicroPython output of the test with the output -produced by running the test through CPython unless a .exp file is found, in which -case it is used as comparison. - -If a test fails, run-tests.py produces a pair of .out and .exp files in the result -directory with the MicroPython output and the expectations, respectively. -""", - epilog="""\ +""" +test_instance_epilog = """\ The -t option accepts the following for the test instance: - unix - use the unix port of MicroPython, specified by the MICROPY_MICROPYTHON environment variable (which defaults to the standard variant of either the unix @@ -1140,7 +1359,35 @@ def main(): - execpty: - execute a command and attach to the printed /dev/pts/ device - ... - connect to the given IPv4 address - anything else specifies a serial port +""" + +test_directory_description = """\ +Tests are discovered by scanning test directories for .py files or using the +specified test files. If test files nor directories are specified, the script +expects to be ran in the tests directory (where this file is located) and the +builtin tests suitable for the target platform are ran. +""" + +def main(): + global injected_import_hook_code + + cmd_parser = argparse.ArgumentParser( + formatter_class=argparse.RawDescriptionHelpFormatter, + description=f"""Run and manage tests for MicroPython. + +{test_instance_description} +{test_directory_description} + +When running tests, run-tests.py compares the MicroPython output of the test with the output +produced by running the test through CPython unless a .exp file is found (or a +.native.exp file when using the native emitter), in which case it is used as comparison. + +If a test fails, run-tests.py produces a pair of .out and .exp files in the result +directory with the MicroPython output and the expectations, respectively. +""", + epilog=f"""\ +{test_instance_epilog} Options -i and -e can be multiple and processed in the order given. Regex "search" (vs "match") operation is used. An action (include/exclude) of the last matching regex is used: @@ -1214,8 +1461,25 @@ def main(): action="store_true", help="re-run only the failed tests", ) + cmd_parser.add_argument( + "--begin", + metavar="PROLOGUE", + default=None, + help="prologue python file to execute before module import", + ) + cmd_parser.add_argument( + "--target-wiring", + default=None, + help="force the given script to be used as target_wiring.py", + ) args = cmd_parser.parse_args() + prologue = "" + if args.begin: + with open(args.begin, "rt") as source: + prologue = source.read() + injected_import_hook_code = injected_import_hook_code.replace("{import_prologue}", prologue) + if args.print_failures: for out in glob(os.path.join(args.result_dir, "*.out")): testbase = out[:-4] @@ -1256,7 +1520,7 @@ def main(): results_file = os.path.join(args.result_dir, RESULTS_FILE) if os.path.exists(results_file): with open(results_file, "r") as f: - tests = json.load(f)["failed_tests"] + tests = list(test[0] for test in json.load(f)["results"] if test[1] == "fail") else: tests = [] elif len(args.files) == 0: @@ -1267,47 +1531,27 @@ def main(): if args.test_dirs is None: test_dirs = ( "basics", - "circuitpython", # CIRCUITPY-CHANGE "micropython", "misc", "extmod", + "stress", ) if args.inlineasm_arch is not None: test_dirs += ("inlineasm/{}".format(args.inlineasm_arch),) - if args.platform == "pyboard": - # run pyboard tests - test_dirs += ("float", "stress", "ports/stm32") - elif args.platform == "mimxrt": - test_dirs += ("float", "stress") - elif args.platform == "renesas-ra": - test_dirs += ("float", "ports/renesas-ra") - elif args.platform == "rp2": - test_dirs += ("float", "stress", "thread", "ports/rp2") - elif args.platform == "esp32": - test_dirs += ("float", "stress", "thread") - elif args.platform in ("esp8266", "minimal", "samd", "nrf"): + if args.thread is not None: + test_dirs += ("thread",) + if args.float_prec > 0: test_dirs += ("float",) - elif args.platform == "WiPy": - # run WiPy tests - test_dirs += ("ports/cc3200",) - elif args.platform in PC_PLATFORMS: + if args.unicode: + test_dirs += ("unicode",) + port_specific_test_dir = "ports/{}".format(platform_to_port(args.platform)) + if os.path.isdir(port_specific_test_dir): + test_dirs += (port_specific_test_dir,) + if args.platform in PC_PLATFORMS: # run PC tests - test_dirs += ( - "float", - "import", - "io", - "stress", - "unicode", - "cmdline", - "ports/unix", - ) - elif args.platform == "qemu": - test_dirs += ( - "float", - "ports/qemu", - ) - elif args.platform == "webassembly": - test_dirs += ("float", "ports/webassembly") + test_dirs += ("import",) + if args.build != "minimal": + test_dirs += ("cmdline", "io") else: # run tests from these directories test_dirs = args.test_dirs @@ -1322,6 +1566,13 @@ def main(): # tests explicitly given tests = args.files + # If any tests need it, prepare the target_wiring script for the target. + if pyb and any(test.endswith(tests_requiring_target_wiring) for test in tests): + detect_target_wiring_script(pyb, args) + + # End the target information line. + print() + if not args.keep_path: # Clear search path to make sure tests use only builtin modules, those in # extmod, and a path to unittest in case it's needed. @@ -1342,7 +1593,8 @@ def main(): try: os.makedirs(args.result_dir, exist_ok=True) - res = run_tests(pyb, tests, args, args.result_dir, args.jobs) + test_results, testcase_count = run_tests(pyb, tests, args, args.result_dir, args.jobs) + res = create_test_report(args, test_results, testcase_count) finally: if pyb: pyb.close() diff --git a/tests/serial_test.py b/tests/serial_test.py new file mode 100755 index 0000000000000..3b5940d91a907 --- /dev/null +++ b/tests/serial_test.py @@ -0,0 +1,338 @@ +#!/usr/bin/env python +# +# Performance and reliability test for serial port communication. +# +# Basic usage: +# serial_test.py [-t serial-device] +# +# The `serial-device` will default to /dev/ttyACM0. + +import argparse +import random +import serial +import sys +import time + +run_tests_module = __import__("run-tests") + +echo_test_script = """ +import sys +bytes_min=%u +bytes_max=%u +repeat=%u +b=memoryview(bytearray(bytes_max)) +for n in range(bytes_min,bytes_max+1): + for _ in range(repeat): + n2 = sys.stdin.readinto(b[:n]) + sys.stdout.write(b[:n2]) +""" + +read_test_script = """ +bin = True +try: + wr=__import__("pyb").USB_VCP(0).send +except: + import sys + if hasattr(sys.stdout,'buffer'): + wr=sys.stdout.buffer.write + else: + wr=sys.stdout.write + bin = False +b=bytearray(%u) +if bin: + wr('BIN') + for i in range(len(b)): + b[i] = i & 0xff +else: + wr('TXT') + for i in range(len(b)): + b[i] = 0x20 + (i & 0x3f) +for _ in range(%d): + wr(b) +""" + + +write_test_script_verified = """ +import sys +try: + rd=__import__("pyb").USB_VCP(0).recv +except: + rd=sys.stdin.readinto +b=bytearray(%u) +for _ in range(%u): + n = rd(b) + fail = 0 + for i in range(n): + if b[i] != 32 + (i & 0x3f): + fail += 1 + if fail: + sys.stdout.write(b'ER%%05u' %% fail) + else: + sys.stdout.write(b'OK%%05u' %% n) +""" + +write_test_script_unverified = """ +import sys +try: + rd=__import__("pyb").USB_VCP(0).recv +except: + rd=sys.stdin.readinto +b=bytearray(%u) +for _ in range(%u): + n = rd(b) + if n != len(b): + sys.stdout.write(b'ER%%05u' %% n) + else: + sys.stdout.write(b'OK%%05u' %% n) +""" + + +class TestError(Exception): + pass + + +def drain_input(ser): + time.sleep(0.1) + while ser.inWaiting() > 0: + data = ser.read(ser.inWaiting()) + time.sleep(0.1) + + +def send_script(ser, script): + ser.write(b"\x03\x01\x04") # break, raw-repl, soft-reboot + drain_input(ser) + chunk_size = 32 + for i in range(0, len(script), chunk_size): + ser.write(script[i : i + chunk_size]) + time.sleep(0.01) + ser.write(b"\x04") # eof + ser.flush() + response = ser.read(2) + if response != b"OK": + response += ser.read(ser.inWaiting()) + raise TestError("could not send script", response) + + +def echo_test(ser_repl, ser_data): + global test_passed + + # Make the test data deterministic. + random.seed(0) + + # Set parameters for the test. + # Go just a bit above the size of a USB high-speed packet. + bytes_min = 1 + bytes_max = 520 + num_repeat = 1 + + # Load and run the write_test_script. + script = bytes(echo_test_script % (bytes_min, bytes_max, num_repeat), "ascii") + send_script(ser_repl, script) + + # A selection of printable bytes for echo data. + printable_bytes = list(range(48, 58)) + list(range(65, 91)) + list(range(97, 123)) + + # Write data to the device and record the echo'd data. + # Use a different selection of random printable characters for each + # echo, to make it easier to debug when the echo doesn't match. + num_errors = 0 + echo_results = [] + for num_bytes in range(bytes_min, bytes_max + 1): + print(f"DATA ECHO: {num_bytes} / {bytes_max}", end="\r") + for repeat in range(num_repeat): + rand_bytes = list(random.choice(printable_bytes) for _ in range(8)) + buf = bytes(random.choice(rand_bytes) for _ in range(num_bytes)) + ser_data.write(buf) + buf2 = ser_data.read(len(buf)) + match = buf == buf2 + num_errors += not match + echo_results.append((match, buf, buf2)) + if num_errors > 8: + # Stop early if there are too many errors. + break + ser_repl.write(b"\x03") + + # Print results. + if all(match for match, _, _ in echo_results): + print("DATA ECHO: OK for {}-{} bytes at a time".format(bytes_min, bytes_max)) + else: + test_passed = False + print("DATA ECHO: FAIL ") + for match, buf, buf2 in echo_results: + print(" sent", len(buf), buf) + if match: + print(" echo match") + else: + print(" echo", len(buf), buf2) + + +def read_test(ser_repl, ser_data, bufsize, nbuf): + global test_passed + + assert bufsize % 256 == 0 # for verify to work + + # how long to wait for data from device + # (if UART TX is also enabled then it can take 1.4s to send + # out a 16KB butter at 115200bps) + READ_TIMEOUT_S = 2 + + # Load and run the read_test_script. + script = bytes(read_test_script % (bufsize, nbuf), "ascii") + send_script(ser_repl, script) + + # Read from the device the type of data that it will send (BIN or TXT). + data_type = ser_data.read(3) + + # Read data from the device, check it is correct, and measure throughput. + n = 0 + last_byte = None + t_start = time.time() + remain = nbuf * bufsize + total_data = bytearray(remain) + while remain: + t0 = time.monotonic_ns() + while ser_data.inWaiting() == 0: + if time.monotonic_ns() - t0 > READ_TIMEOUT_S * 1e9: + # timeout waiting for data from device + break + time.sleep(0.0001) + if not ser_data.inWaiting(): + test_passed = False + print("ERROR: timeout waiting for data") + print(total_data[:n]) + return 0 + to_read = min(ser_data.inWaiting(), remain) + data = ser_data.read(to_read) + remain -= len(data) + print(f"{n} / {nbuf * bufsize}", end="\r") + total_data[n : n + len(data)] = data + n += len(data) + t_end = time.time() + for i in range(0, len(total_data)): + if data_type == b"BIN": + wanted = i & 0xFF + else: + wanted = 0x20 + (i & 0x3F) + if total_data[i] != wanted: + test_passed = False + print("ERROR: data mismatch:", i, wanted, total_data[i]) + ser_repl.write(b"\x03") # break + t = t_end - t_start + + # Print results. + print( + "DATA IN: bufsize=%u, read %u bytes in %.2f msec = %.2f kibytes/sec = %.2f MBits/sec" + % (bufsize, n, t * 1000, n / 1024 / t, n * 8 / 1000000 / t) + ) + + return n / t + + +def write_test(ser_repl, ser_data, bufsize, nbuf, verified): + global test_passed + + # Load and run the write_test_script. + if verified: + script = write_test_script_verified + else: + script = write_test_script_unverified + script = bytes(script % (bufsize, nbuf), "ascii") + send_script(ser_repl, script) + drain_input(ser_repl) + + # Write data to the device, check it is correct, and measure throughput. + n = 0 + t_start = time.time() + buf = bytearray(bufsize) + for i in range(len(buf)): + buf[i] = 32 + (i & 0x3F) # don't want to send ctrl chars! + for i in range(nbuf): + ser_data.write(buf) + n += len(buf) + print(f"{n} / {nbuf * bufsize}", end="\r") + response = ser_repl.read(7) + if response != b"OK%05u" % bufsize: + test_passed = False + print("ERROR: bad response, expecting OK%05u, got %r" % (bufsize, response)) + t_end = time.time() + ser_repl.write(b"\x03") # break + t = t_end - t_start + + # Print results. + print( + "DATA OUT: verify=%d, bufsize=%u, wrote %u bytes in %.2f msec = %.2f kibytes/sec = %.2f MBits/sec" + % (verified, bufsize, n, t * 1000, n / 1024 / t, n * 8 / 1000000 / t) + ) + + return n / t + + +def do_test(dev_repl, dev_data=None, time_per_subtest=1): + if dev_data is None: + print("REPL and data on", dev_repl) + ser_repl = serial.Serial(dev_repl, baudrate=115200, timeout=1) + ser_data = ser_repl + else: + print("REPL on", dev_repl) + print("data on", dev_data) + ser_repl = serial.Serial(dev_repl, baudrate=115200, timeout=1) + ser_data = serial.Serial(dev_data, baudrate=115200, timeout=1) + + # Do echo test first, and abort if it doesn't pass. + echo_test(ser_repl, ser_data) + if not test_passed: + return + + # Do read and write throughput test. + for test_func, test_args, bufsize in ( + (read_test, (), 256), + (write_test, (True,), 128), + (write_test, (False,), 128), + ): + nbuf = 128 + while bufsize <= 16384: + rate = test_func(ser_repl, ser_data, bufsize, nbuf, *test_args) + bufsize *= 2 + if rate: + # Adjust the amount of data based on the rate, to keep each subtest + # at around time_per_subtest seconds long. + nbuf = max(min(128, int(rate * time_per_subtest / bufsize)), 1) + + ser_repl.close() + ser_data.close() + + +def main(): + global test_passed + + cmd_parser = argparse.ArgumentParser( + description="Test performance and reliability of serial port communication.", + epilog=run_tests_module.test_instance_epilog, + formatter_class=argparse.RawTextHelpFormatter, + ) + cmd_parser.add_argument( + "-t", + "--test-instance", + default="a0", + help="MicroPython instance to test", + ) + cmd_parser.add_argument( + "--time-per-subtest", default="1", help="approximate time to take per subtest (in seconds)" + ) + args = cmd_parser.parse_args() + + dev_repl = run_tests_module.convert_device_shortcut_to_real_device(args.test_instance) + + test_passed = True + try: + do_test(dev_repl, None, float(args.time_per_subtest)) + except TestError as er: + test_passed = False + print("ERROR:", er) + + if not test_passed: + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/tests/stress/bytecode_limit.py b/tests/stress/bytecode_limit.py index 948d7668da551..0a72b66fa05b6 100644 --- a/tests/stress/bytecode_limit.py +++ b/tests/stress/bytecode_limit.py @@ -1,19 +1,29 @@ # Test the limits of bytecode generation. +import sys + +# Tune the test parameters based on the target's bytecode generator. +if hasattr(sys.implementation, "_mpy"): + # Target can load .mpy files so generated bytecode uses 1 byte per qstr. + number_of_body_copies = (433, 432, 431, 399) +else: + # Target can't load .mpy files so generated bytecode uses 2 bytes per qstr. + number_of_body_copies = (401, 400, 399, 398) + body = " with f()()() as a:\n try:\n f()()()\n except Exception:\n pass\n" # Test overflow of jump offset. # Print results at the end in case an intermediate value of n fails with MemoryError. results = [] -for n in (433, 432, 431, 430): +for n in number_of_body_copies: try: exec("cond = 0\nif cond:\n" + body * n + "else:\n print('cond false')\n") - results.append((n, "ok")) + results.append("ok") except MemoryError: print("SKIP") raise SystemExit - except RuntimeError: - results.append((n, "RuntimeError")) + except RuntimeError as er: + results.append(repr(er)) print(results) # Test changing size of code info (source line/bytecode mapping) due to changing diff --git a/tests/stress/bytecode_limit.py.exp b/tests/stress/bytecode_limit.py.exp index cda52b1b97348..50511665f0076 100644 --- a/tests/stress/bytecode_limit.py.exp +++ b/tests/stress/bytecode_limit.py.exp @@ -1,4 +1,4 @@ cond false cond false -[(433, 'RuntimeError'), (432, 'RuntimeError'), (431, 'ok'), (430, 'ok')] +["RuntimeError('bytecode overflow',)", "RuntimeError('bytecode overflow',)", 'ok', 'ok'] [123] diff --git a/tests/stress/dict_copy.py b/tests/stress/dict_copy.py index 73d3a5b51d601..f9b742e20f716 100644 --- a/tests/stress/dict_copy.py +++ b/tests/stress/dict_copy.py @@ -1,6 +1,11 @@ # copying a large dictionary -a = {i: 2 * i for i in range(1000)} +try: + a = {i: 2 * i for i in range(1000)} +except MemoryError: + print("SKIP") + raise SystemExit + b = a.copy() for i in range(1000): print(i, b[i]) diff --git a/tests/stress/dict_create.py b/tests/stress/dict_create.py index e9db40a8e6c5b..91a83a12f9d85 100644 --- a/tests/stress/dict_create.py +++ b/tests/stress/dict_create.py @@ -3,6 +3,10 @@ d = {} x = 1 while x < 1000: - d[x] = x + try: + d[x] = x + except MemoryError: + print("SKIP") + raise SystemExit x += 1 print(d[500]) diff --git a/tests/stress/fun_call_limit.py b/tests/stress/fun_call_limit.py index b802aadd558c0..69f8aa5aec413 100644 --- a/tests/stress/fun_call_limit.py +++ b/tests/stress/fun_call_limit.py @@ -16,14 +16,16 @@ def test(n): # If the port has at least 32-bits then this test should pass. -print(test(29)) +print(test(28)) # This test should fail on all ports (overflows a small int). print(test(70)) -# Check that there is a correct transition to the limit of too many args before *args. +# 28 is the biggest number that will pass on a 32-bit port using object +# representation B, which has 1<<28 still fitting in a positive small int. reached_limit = False -for i in range(30, 70): +any_test_succeeded = False +for i in range(28, 70): result = test(i) if reached_limit: if result != "SyntaxError": @@ -34,3 +36,5 @@ def test(n): else: if result != i + 4: print("FAIL") + any_test_succeeded = True +assert any_test_succeeded # At least one iteration must have passed diff --git a/tests/stress/fun_call_limit.py.exp b/tests/stress/fun_call_limit.py.exp index 53d2b28043015..491abbfa8be72 100644 --- a/tests/stress/fun_call_limit.py.exp +++ b/tests/stress/fun_call_limit.py.exp @@ -1,2 +1,2 @@ -33 +32 SyntaxError diff --git a/tests/stress/qstr_limit.py b/tests/stress/qstr_limit.py index 08b10a039f509..c7bd437f3adc1 100644 --- a/tests/stress/qstr_limit.py +++ b/tests/stress/qstr_limit.py @@ -12,8 +12,8 @@ def make_id(n, base="a"): var = make_id(l) try: exec(var + "=1", g) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) continue print(var in g) @@ -26,16 +26,16 @@ def f(**k): for l in range(254, 259): try: exec("f({}=1)".format(make_id(l))) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) # type construction for l in range(254, 259): id = make_id(l) try: - print(type(id, (), {}).__name__) - except RuntimeError: - print("RuntimeError", l) + print(type(id, (), {})) + except RuntimeError as er: + print("RuntimeError", er, l) # hasattr, setattr, getattr @@ -48,28 +48,20 @@ class A: a = A() try: setattr(a, id, 123) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) try: print(hasattr(a, id), getattr(a, id)) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) # format with keys for l in range(254, 259): id = make_id(l) try: print(("{" + id + "}").format(**{id: l})) - except RuntimeError: - print("RuntimeError", l) - -# modulo format with keys -for l in range(254, 259): - id = make_id(l) - try: - print(("%(" + id + ")d") % {id: l}) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) # import module # (different OS's have different results so only run those that are consistent) @@ -78,8 +70,8 @@ class A: __import__(make_id(l)) except ImportError: print("ok", l) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) # import package for l in (100, 101, 102, 128, 129): @@ -87,5 +79,5 @@ class A: exec("import " + make_id(l) + "." + make_id(l, "A")) except ImportError: print("ok", l) - except RuntimeError: - print("RuntimeError", l) + except RuntimeError as er: + print("RuntimeError", er, l) diff --git a/tests/stress/qstr_limit.py.exp b/tests/stress/qstr_limit.py.exp index 455761bc71e4a..2349adf220f98 100644 --- a/tests/stress/qstr_limit.py.exp +++ b/tests/stress/qstr_limit.py.exp @@ -1,43 +1,38 @@ True True -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 {'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst': 1} {'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu': 1} -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 -abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrst -abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstu -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 + + +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 True 123 True 123 -RuntimeError 256 -RuntimeError 256 -RuntimeError 257 -RuntimeError 257 -RuntimeError 258 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 257 +RuntimeError name too long 258 +RuntimeError name too long 258 254 255 -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 -254 -255 -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 ok 100 ok 101 -RuntimeError 256 -RuntimeError 257 -RuntimeError 258 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 ok 100 ok 101 ok 102 -RuntimeError 128 -RuntimeError 129 +RuntimeError name too long 128 +RuntimeError name too long 129 diff --git a/tests/stress/qstr_limit_str_modulo.py b/tests/stress/qstr_limit_str_modulo.py new file mode 100644 index 0000000000000..90b9f4364ec6f --- /dev/null +++ b/tests/stress/qstr_limit_str_modulo.py @@ -0,0 +1,21 @@ +# Test interning qstrs that go over the qstr length limit (255 bytes in default configuration). +# The tests here are specifically for str formatting with %. + +try: + "" % () +except TypeError: + print("SKIP") + raise SystemExit + + +def make_id(n, base="a"): + return "".join(chr(ord(base) + i % 26) for i in range(n)) + + +# modulo format with keys +for l in range(254, 259): + id = make_id(l) + try: + print(("%(" + id + ")d") % {id: l}) + except RuntimeError as er: + print("RuntimeError", er, l) diff --git a/tests/stress/qstr_limit_str_modulo.py.exp b/tests/stress/qstr_limit_str_modulo.py.exp new file mode 100644 index 0000000000000..3632c85bffea3 --- /dev/null +++ b/tests/stress/qstr_limit_str_modulo.py.exp @@ -0,0 +1,5 @@ +254 +255 +RuntimeError name too long 256 +RuntimeError name too long 257 +RuntimeError name too long 258 diff --git a/tests/stress/recursive_iternext.py b/tests/stress/recursive_iternext.py index bbc389e726237..c737f1e36d70a 100644 --- a/tests/stress/recursive_iternext.py +++ b/tests/stress/recursive_iternext.py @@ -1,4 +1,8 @@ # This tests that recursion with iternext doesn't lead to segfault. +# +# This test segfaults CPython, but that's not a bug as CPython doesn't enforce +# limits on C recursion - see +# https://github.com/python/cpython/issues/58218#issuecomment-1093570209 try: enumerate filter @@ -9,49 +13,25 @@ print("SKIP") raise SystemExit -# We need to pick an N that is large enough to hit the recursion -# limit, but not too large that we run out of heap memory. -try: - # large stack/heap, eg unix - [0] * 80000 - N = 5000 -except: - try: - # medium, eg pyboard - [0] * 10000 - N = 1000 - except: - # small, eg esp8266 - N = 100 - -try: - x = (1, 2) - for i in range(N): - x = enumerate(x) - tuple(x) -except RuntimeError: - print("RuntimeError") -try: +# Progressively build a bigger nested iterator structure (10 at a time for speed), +# and then try to evaluate it via tuple(x) which makes deep recursive function calls. +# +# Eventually this should raise a RuntimeError as MicroPython runs out of stack. +# It shouldn't ever raise a MemoryError, if it does then somehow MicroPython has +# run out of heap (for the nested structure) before running out of stack. +def recurse_iternext(nested_fn): x = (1, 2) - for i in range(N): - x = filter(None, x) - tuple(x) -except RuntimeError: - print("RuntimeError") + while True: + for _ in range(10): + x = nested_fn(x) + try: + tuple(x) + except RuntimeError: + print("RuntimeError") + break -try: - x = (1, 2) - for i in range(N): - x = map(max, x, ()) - tuple(x) -except RuntimeError: - print("RuntimeError") -try: - x = (1, 2) - for i in range(N): - x = zip(x) - tuple(x) -except RuntimeError: - print("RuntimeError") +# Test on various nested iterator structures +for nested_fn in [enumerate, lambda x: filter(None, x), lambda x: map(max, x, ()), zip]: + recurse_iternext(nested_fn) diff --git a/tests/target_wiring/EK_RA6M2.py b/tests/target_wiring/EK_RA6M2.py new file mode 100644 index 0000000000000..7d4a8cbbd6484 --- /dev/null +++ b/tests/target_wiring/EK_RA6M2.py @@ -0,0 +1,8 @@ +# Target wiring for EK_RA6M2. +# +# Connect: +# - P601 to P602 + +# UART(9) is on P602/P601. +uart_loopback_args = (9,) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/NUCLEO_WB55.py b/tests/target_wiring/NUCLEO_WB55.py new file mode 100644 index 0000000000000..ad7c120d3770e --- /dev/null +++ b/tests/target_wiring/NUCLEO_WB55.py @@ -0,0 +1,8 @@ +# Target wiring for NUCLEO_WB55. +# +# Connect: +# - PA2 to PA3 + +# LPUART(1) is on PA2/PA3. +uart_loopback_args = ("LP1",) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/PYBx.py b/tests/target_wiring/PYBx.py new file mode 100644 index 0000000000000..10ce520ef0af4 --- /dev/null +++ b/tests/target_wiring/PYBx.py @@ -0,0 +1,8 @@ +# Target wiring for PYBV10, PYBV11, PYBLITEV10, PYBD_SF2, PYBD_SF3, PYBD_SF6. +# +# Connect: +# - X1 to X2 + +# UART("XA") is on X1/X2 (usually UART(4) on PA0/PA1). +uart_loopback_args = ("XA",) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/ZEPHYR_NUCLEO_WB55RG.py b/tests/target_wiring/ZEPHYR_NUCLEO_WB55RG.py new file mode 100644 index 0000000000000..c7ca2ac26f794 --- /dev/null +++ b/tests/target_wiring/ZEPHYR_NUCLEO_WB55RG.py @@ -0,0 +1,7 @@ +# Target wiring for zephyr nucleo_wb55rg. +# +# Connect: +# - TX=PC0 to RX=PC1 + +uart_loopback_args = ("lpuart1",) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/alif.py b/tests/target_wiring/alif.py new file mode 100644 index 0000000000000..18f3cbe7e5e0d --- /dev/null +++ b/tests/target_wiring/alif.py @@ -0,0 +1,7 @@ +# Target wiring for general alif board. +# +# Connect: +# - UART1 TX and RX, usually P0_5 and P0_4 + +uart_loopback_args = (1,) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/esp32.py b/tests/target_wiring/esp32.py new file mode 100644 index 0000000000000..2767cd5acb2e8 --- /dev/null +++ b/tests/target_wiring/esp32.py @@ -0,0 +1,12 @@ +# Target wiring for general esp32 board. +# +# Connect: +# - GPIO4 to GPIO5 +# - GPIO12 to GPIO13 + +uart_loopback_args = (1,) +uart_loopback_kwargs = {"tx": 4, "rx": 5} + +encoder_loopback_id = 0 +encoder_loopback_out_pins = (4, 12) +encoder_loopback_in_pins = (5, 13) diff --git a/tests/target_wiring/mimxrt.py b/tests/target_wiring/mimxrt.py new file mode 100644 index 0000000000000..669e9095990db --- /dev/null +++ b/tests/target_wiring/mimxrt.py @@ -0,0 +1,7 @@ +# Target wiring for general mimxrt board. +# +# Connect: +# - UART1 TX and RX, usually D0 and D1 + +uart_loopback_args = (1,) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/nrf.py b/tests/target_wiring/nrf.py new file mode 100644 index 0000000000000..6979dd28ee595 --- /dev/null +++ b/tests/target_wiring/nrf.py @@ -0,0 +1,7 @@ +# Target wiring for general nrf board. +# +# Connect: +# - UART0 TX and RX + +uart_loopback_args = (0,) +uart_loopback_kwargs = {} diff --git a/tests/target_wiring/rp2.py b/tests/target_wiring/rp2.py new file mode 100644 index 0000000000000..cb0fa0d626339 --- /dev/null +++ b/tests/target_wiring/rp2.py @@ -0,0 +1,7 @@ +# Target wiring for general rp2 board. +# +# Connect: +# - GPIO0 to GPIO1 + +uart_loopback_args = (0,) +uart_loopback_kwargs = {"tx": "GPIO0", "rx": "GPIO1"} diff --git a/tests/target_wiring/samd.py b/tests/target_wiring/samd.py new file mode 100644 index 0000000000000..887c43a242f9b --- /dev/null +++ b/tests/target_wiring/samd.py @@ -0,0 +1,7 @@ +# Target wiring for general samd board. +# +# Connect: +# - D0 to D1 + +uart_loopback_args = () +uart_loopback_kwargs = {"tx": "D1", "rx": "D0"} diff --git a/tests/thread/disable_irq.py b/tests/thread/disable_irq.py index 3f1ac74f30877..596e3e477b9c8 100644 --- a/tests/thread/disable_irq.py +++ b/tests/thread/disable_irq.py @@ -1,8 +1,14 @@ # Ensure that disabling IRQs creates mutual exclusion between threads # (also tests nesting of disable_irq across threads) -import machine -import time -import _thread + +# CIRCUITPY-CHANGE: no machine +try: + import machine + import time + import _thread +except ImportError: + print("SKIP") + raise SystemExit if not hasattr(machine, "disable_irq"): print("SKIP") diff --git a/tests/thread/mutate_bytearray.py b/tests/thread/mutate_bytearray.py index b4664781a1522..7116d291cfeec 100644 --- a/tests/thread/mutate_bytearray.py +++ b/tests/thread/mutate_bytearray.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared bytearray @@ -36,7 +37,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check bytearray has correct contents print(len(ba)) diff --git a/tests/thread/mutate_dict.py b/tests/thread/mutate_dict.py index 3777af66248c6..dd5f69e6c5d66 100644 --- a/tests/thread/mutate_dict.py +++ b/tests/thread/mutate_dict.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared dict @@ -38,7 +39,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check dict has correct contents print(sorted(di.items())) diff --git a/tests/thread/mutate_instance.py b/tests/thread/mutate_instance.py index 306ad91c95cfa..63f7fb1e23df8 100644 --- a/tests/thread/mutate_instance.py +++ b/tests/thread/mutate_instance.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -40,7 +41,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check user instance has correct contents print(user.a, user.b, user.c) diff --git a/tests/thread/mutate_list.py b/tests/thread/mutate_list.py index 6f1e8812541a0..d7398a2f1e0b4 100644 --- a/tests/thread/mutate_list.py +++ b/tests/thread/mutate_list.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared list @@ -39,7 +40,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check list has correct contents li.sort() diff --git a/tests/thread/mutate_set.py b/tests/thread/mutate_set.py index 2d9a3e0ce9efd..7dcefa1d113f9 100644 --- a/tests/thread/mutate_set.py +++ b/tests/thread/mutate_set.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread # the shared set @@ -33,7 +34,7 @@ def th(n, lo, hi): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) # check set has correct contents print(sorted(se)) diff --git a/tests/thread/stress_aes.py b/tests/thread/stress_aes.py index d8d0acd568a7a..ca25f8ad2fd57 100644 --- a/tests/thread/stress_aes.py +++ b/tests/thread/stress_aes.py @@ -277,7 +277,7 @@ def thread_entry(n_loop): n_thread = 2 n_loop = 2 else: - n_thread = 20 + n_thread = 10 n_loop = 5 for i in range(n_thread): _thread.start_new_thread(thread_entry, (n_loop,)) diff --git a/tests/thread/stress_recurse.py b/tests/thread/stress_recurse.py index 73b3a40f33daa..ec8b43fe8fc22 100644 --- a/tests/thread/stress_recurse.py +++ b/tests/thread/stress_recurse.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -24,5 +25,5 @@ def thread_entry(): # busy wait for thread to finish while not finished: - pass + time.sleep(0) print("done") diff --git a/tests/thread/stress_schedule.py b/tests/thread/stress_schedule.py index 97876f0f77ca8..362d71aa12e38 100644 --- a/tests/thread/stress_schedule.py +++ b/tests/thread/stress_schedule.py @@ -27,6 +27,8 @@ def task(x): n += 1 +# This function must always use the bytecode emitter so it bounces the GIL when running. +@micropython.bytecode def thread(): while thread_run: try: @@ -46,7 +48,7 @@ def thread(): # Wait up to 10 seconds for 10000 tasks to be scheduled. t = time.ticks_ms() while n < _NUM_TASKS and time.ticks_diff(time.ticks_ms(), t) < _TIMEOUT_MS: - pass + time.sleep(0) # Stop all threads. thread_run = False diff --git a/tests/thread/thread_coop.py b/tests/thread/thread_coop.py index aefc4af074db5..85cda789c936e 100644 --- a/tests/thread/thread_coop.py +++ b/tests/thread/thread_coop.py @@ -7,6 +7,7 @@ import _thread import sys from time import ticks_ms, ticks_diff, sleep_ms +import micropython done = False @@ -21,6 +22,8 @@ MAX_DELTA = 100 +# This function must always use the bytecode emitter so the VM can bounce the GIL when running. +@micropython.bytecode def busy_thread(): while not done: pass diff --git a/tests/thread/thread_exc1.py b/tests/thread/thread_exc1.py index cd87740929103..cd6599983c141 100644 --- a/tests/thread/thread_exc1.py +++ b/tests/thread/thread_exc1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -34,5 +35,5 @@ def thread_entry(): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print("done") diff --git a/tests/thread/thread_exc2.py.native.exp b/tests/thread/thread_exc2.py.native.exp new file mode 100644 index 0000000000000..9b2e715ef8dfc --- /dev/null +++ b/tests/thread/thread_exc2.py.native.exp @@ -0,0 +1,3 @@ +Unhandled exception in thread started by +ValueError: +done diff --git a/tests/thread/thread_gc1.py b/tests/thread/thread_gc1.py index b36ea9d4c8421..45c17cc17bed3 100644 --- a/tests/thread/thread_gc1.py +++ b/tests/thread/thread_gc1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import gc import _thread @@ -44,6 +45,6 @@ def thread_entry(n): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print(n_correct == n_finished) diff --git a/tests/thread/thread_ident1.py b/tests/thread/thread_ident1.py index 2a3732eff53dc..08cfd3eb36e8a 100644 --- a/tests/thread/thread_ident1.py +++ b/tests/thread/thread_ident1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -27,6 +28,6 @@ def thread_entry(): new_tid = _thread.start_new_thread(thread_entry, ()) while not finished: - pass + time.sleep(0) print("done", type(new_tid) == int, new_tid == tid) diff --git a/tests/thread/thread_lock3.py b/tests/thread/thread_lock3.py index a927dc6829e15..c5acfa21b7dc6 100644 --- a/tests/thread/thread_lock3.py +++ b/tests/thread/thread_lock3.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread lock = _thread.allocate_lock() @@ -26,4 +27,4 @@ def thread_entry(idx): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) diff --git a/tests/thread/thread_lock4.py b/tests/thread/thread_lock4_intbig.py similarity index 100% rename from tests/thread/thread_lock4.py rename to tests/thread/thread_lock4_intbig.py diff --git a/tests/thread/thread_shared1.py b/tests/thread/thread_shared1.py index 251e26fae6ca3..c2e33abcec7ee 100644 --- a/tests/thread/thread_shared1.py +++ b/tests/thread/thread_shared1.py @@ -2,6 +2,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -40,5 +41,5 @@ def thread_entry(n, tup): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print(tup) diff --git a/tests/thread/thread_shared2.py b/tests/thread/thread_shared2.py index a1223c2b94f40..4ce9057ca017e 100644 --- a/tests/thread/thread_shared2.py +++ b/tests/thread/thread_shared2.py @@ -3,6 +3,7 @@ # # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd +import time import _thread @@ -31,5 +32,5 @@ def thread_entry(n, lst, idx): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print(lst) diff --git a/tests/thread/thread_stacksize1.py b/tests/thread/thread_stacksize1.py index 140d165cb3497..75e1da9642f95 100644 --- a/tests/thread/thread_stacksize1.py +++ b/tests/thread/thread_stacksize1.py @@ -3,6 +3,7 @@ # MIT license; Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd import sys +import time import _thread # different implementations have different minimum sizes @@ -51,5 +52,5 @@ def thread_entry(): # busy wait for threads to finish while n_finished < n_thread: - pass + time.sleep(0) print("done") diff --git a/tests/thread/thread_stdin.py b/tests/thread/thread_stdin.py index a469933f19b55..498b0a3a27022 100644 --- a/tests/thread/thread_stdin.py +++ b/tests/thread/thread_stdin.py @@ -5,6 +5,7 @@ # This is a regression test for https://github.com/micropython/micropython/issues/15230 # on rp2, but doubles as a general property to test across all ports. import sys +import time import _thread try: @@ -38,7 +39,7 @@ def is_done(self): # have run yet. The actual delay is <20ms but spinning here instead of # sleep(0.1) means the test can run on MP builds without float support. while not thread_waiter.is_done(): - pass + time.sleep(0) # The background thread should have completed its wait by now. print(thread_waiter.is_done()) diff --git a/tests/unix/extra_coverage.py b/tests/unix/extra_coverage.py index 0ea8f7886bfff..ec10a44ff39b3 100644 --- a/tests/unix/extra_coverage.py +++ b/tests/unix/extra_coverage.py @@ -6,6 +6,16 @@ import errno import io +import uctypes + +# create an int-like variable used for coverage of `mp_obj_get_ll` +buf = bytearray(b"\xde\xad\xbe\xef") +struct = uctypes.struct( + uctypes.addressof(buf), + {"f32": uctypes.UINT32 | 0}, + uctypes.BIG_ENDIAN, +) +deadbeef = struct.f32 data = extra_coverage() @@ -23,6 +33,7 @@ print(stream.read(1)) # read 1 byte encounters non-blocking error print(stream.readline()) # readline encounters non-blocking error print(stream.readinto(bytearray(10))) # readinto encounters non-blocking error +print(stream.readinto1(bytearray(10))) # readinto1 encounters non-blocking error print(stream.write(b"1")) # write encounters non-blocking error print(stream.write1(b"1")) # write1 encounters non-blocking error stream.set_buf(b"123") @@ -38,6 +49,28 @@ stream.set_error(0) print(stream.ioctl(0, bytearray(10))) # successful ioctl call +print("# stream.readinto") + +# stream.readinto will read 3 bytes then try to read more to fill the buffer, +# but on the second attempt will encounter EIO and should raise that error. +stream.set_error(errno.EIO) +stream.set_buf(b"123") +buf = bytearray(4) +try: + stream.readinto(buf) +except OSError as er: + print("OSError", er.errno == errno.EIO) +print(buf) + +# stream.readinto1 will read 3 bytes then should return them immediately, and +# not encounter the EIO. +stream.set_error(errno.EIO) +stream.set_buf(b"123") +buf = bytearray(4) +print(stream.readinto1(buf), buf) + +print("# stream textio") + stream2 = data[3] # is textio print(stream2.read(1)) # read 1 byte encounters non-blocking error with textio stream @@ -92,7 +125,7 @@ print(returns_NULL()) -# test for freeze_mpy +# test for freeze_mpy (importing prints several lines) import frozentest print(frozentest.__file__) diff --git a/tests/unix/extra_coverage.py.exp b/tests/unix/extra_coverage.py.exp index 4cd8b8666d270..cdb1145569497 100644 --- a/tests/unix/extra_coverage.py.exp +++ b/tests/unix/extra_coverage.py.exp @@ -2,25 +2,41 @@ -123 +123 123 -0123 123 -123 -1ABCDEF +123f +123F +7fffffffffffffff +7FFFFFFFFFFFFFFF +18446744073709551615 +789f +789F ab abc ' abc' ' True' 'Tru' false true (null) -2147483648 2147483648 -80000000 -80000000 +8000000f +8000000F abc % +.a . +<%> + + +<43690> +<43690> +<43690> + +<1000.000000> + +<9223372036854775807> # GC -0x0 -0x0 +0 +0 # GC part 2 pass # tracked allocation -m_tracked_head = 0x0 +m_tracked_head = 0 0 1 1 1 2 1 @@ -37,7 +53,7 @@ m_tracked_head = 0x0 5 1 6 1 7 1 -m_tracked_head = 0x0 +m_tracked_head = 0 # vstr tests sts @@ -91,6 +107,16 @@ data 12345 6 -1 +0 +1 +0 +0.000000 +deadbeef +c0ffee777c0ffee +deadbeef +0deadbeef +c0ffee +000c0ffee # runtime utils TypeError: unsupported type for __abs__: 'str' TypeError: unsupported types for __divmod__: 'str', 'str' @@ -99,12 +125,10 @@ TypeError: unsupported types for __divmod__: 'str', 'str' 2 OverflowError: overflow converting long int to machine word OverflowError: overflow converting long int to machine word +TypeError: can't convert NoneType to int +TypeError: can't convert NoneType to int ValueError: Warning: test -# format float -? -+1e+00 -+1e+00 # binary 123 456 @@ -124,6 +148,13 @@ unlocked KeyboardInterrupt: KeyboardInterrupt: 10 +loop +scheduled function +loop +scheduled function +loop +scheduled function +scheduled function # ringbuf 99 0 98 1 @@ -185,11 +216,17 @@ None None None None +None b'123' b'123' b'123' OSError 0 +# stream.readinto +OSError True +bytearray(b'123\x00') +3 bytearray(b'123\x00') +# stream textio None None cpp None @@ -215,7 +252,7 @@ b'\x00\xff' frzmpy4 1 frzmpy4 2 NULL -uPy +interned a long string that is not interned a string that has unicode αβγ chars b'bytes 1234\x01' diff --git a/tests/unix/ffi_float2.py b/tests/unix/ffi_float2.py index bbed6966627e1..eac6cd106cf43 100644 --- a/tests/unix/ffi_float2.py +++ b/tests/unix/ffi_float2.py @@ -29,4 +29,5 @@ def ffi_open(names): for fun in (tgammaf,): for val in (0.5, 1, 1.0, 1.5, 4, 4.0): - print("%.6f" % fun(val)) + # limit to 5 decimals in order to pass with REPR_C with FORMAT_IMPL_BASIC + print("%.5f" % fun(val)) diff --git a/tests/unix/ffi_float2.py.exp b/tests/unix/ffi_float2.py.exp index 58fc6a01acb07..4c750e223a3a6 100644 --- a/tests/unix/ffi_float2.py.exp +++ b/tests/unix/ffi_float2.py.exp @@ -1,6 +1,6 @@ -1.772454 -1.000000 -1.000000 -0.886227 -6.000000 -6.000000 +1.77245 +1.00000 +1.00000 +0.88623 +6.00000 +6.00000 diff --git a/tests/unix/mod_os.py b/tests/unix/mod_os.py index f69fa45b2b22a..468f6badd1e75 100644 --- a/tests/unix/mod_os.py +++ b/tests/unix/mod_os.py @@ -1,6 +1,9 @@ # This module is not entirely compatible with CPython import os +if not hasattr(os, "getenv"): + print("SKIP") + raise SystemExit os.putenv("TEST_VARIABLE", "TEST_VALUE") diff --git a/tests/unix/time_mktime_localtime.py b/tests/unix/time_mktime_localtime.py index d1c03c103d704..df5d6cda690a2 100644 --- a/tests/unix/time_mktime_localtime.py +++ b/tests/unix/time_mktime_localtime.py @@ -1,4 +1,8 @@ -import time +try: + import time +except ImportError: + print("SKIP") + raise SystemExit DAYS_PER_MONTH = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] diff --git a/tools/analyze_heap_dump.py b/tools/analyze_heap_dump.py index ac1bb0c8ce592..a7be95db7d9f3 100755 --- a/tools/analyze_heap_dump.py +++ b/tools/analyze_heap_dump.py @@ -82,7 +82,7 @@ help="Draw the ownership graph of blocks on the heap", ) @click.option("--analyze-snapshots", default="last", type=click.Choice(["all", "last"])) -def do_all_the_things( # noqa: C901: too complex +def do_all_the_things( # noqa: C901 too complex ram_filename, bin_filename, map_filename, diff --git a/tools/board_stubs/circuitpython_setboard/__init__.py b/tools/board_stubs/circuitpython_setboard/__init__.py index 3a73beb3119df..69a405405b800 100644 --- a/tools/board_stubs/circuitpython_setboard/__init__.py +++ b/tools/board_stubs/circuitpython_setboard/__init__.py @@ -12,7 +12,12 @@ import shutil from collections import defaultdict from importlib import resources -from importlib.abc import Traversable + +try: + from importlib.resources.abc import Traversable +except ModuleNotFoundError: + # 3.10 and earlier. + from importlib.abc import Traversable def get_definitions_or_exit(board: str) -> Traversable: diff --git a/tools/boardgen.py b/tools/boardgen.py index 39bedf71cd0c8..3723e7ce31b83 100644 --- a/tools/boardgen.py +++ b/tools/boardgen.py @@ -108,6 +108,10 @@ def add_board_pin_name(self, board_pin_name, hidden=False): ) ) + # Iterate over board pin names in consistent sorted order. + def board_pin_names(self): + return sorted(self._board_pin_names, key=lambda x: x[0]) + # Override this to handle an af specified in af.csv. def add_af(self, af_idx, af_name, af): raise NotImplementedError @@ -295,7 +299,7 @@ def print_board_locals_dict(self, out_source): file=out_source, ) for pin in self.available_pins(): - for board_pin_name, board_hidden in pin._board_pin_names: + for board_pin_name, board_hidden in pin.board_pin_names(): if board_hidden: # Don't include hidden pins in Pins.board. continue @@ -389,7 +393,7 @@ def print_defines(self, out_header, cpu=True, board=True): # #define pin_BOARDNAME (pin_CPUNAME) if board: - for board_pin_name, _board_hidden in pin._board_pin_names: + for board_pin_name, _board_hidden in pin.board_pin_names(): # Note: Hidden board pins are still available to C via the macro. # Note: The RHS isn't wrapped in (), which is necessary to make the # STATIC_AF_ macro work on STM32. diff --git a/tools/cc1 b/tools/cc1 index 827d5886a04f5..aa2534f01e7bb 100755 --- a/tools/cc1 +++ b/tools/cc1 @@ -23,8 +23,8 @@ import re # TODO somehow make them externally configurable # this is the path to the true C compiler -cc1_path = '/usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/cc1' -#cc1_path = '/usr/lib/gcc/arm-none-eabi/5.3.0/cc1' +cc1_path = "/usr/lib/gcc/x86_64-unknown-linux-gnu/5.3.0/cc1" +# cc1_path = '/usr/lib/gcc/arm-none-eabi/5.3.0/cc1' # this must be the same as MICROPY_QSTR_BYTES_IN_HASH bytes_in_qstr_hash = 2 @@ -41,11 +41,16 @@ print_debug = False ################################################################################ # precompile regexs -re_preproc_line = re.compile(r'# [0-9]+ ') -re_map_entry = re.compile(r'\{.+?\(MP_QSTR_([A-Za-z0-9_]+)\).+\},') -re_mp_obj_dict_t = re.compile(r'(?P(static )?const mp_obj_dict_t (?P[a-z0-9_]+) = \{ \.base = \{&mp_type_dict\}, \.map = \{ \.all_keys_are_qstrs = 1, \.is_fixed = 1, \.is_ordered = )1(?P, \.used = .+ };)$') -re_mp_map_t = re.compile(r'(?P(static )?const mp_map_t (?P[a-z0-9_]+) = \{ \.all_keys_are_qstrs = 1, \.is_fixed = 1, \.is_ordered = )1(?P, \.used = .+ };)$') -re_mp_rom_map_elem_t = re.compile(r'static const mp_rom_map_elem_t [a-z_0-9]+\[\] = {$') +re_preproc_line = re.compile(r"# [0-9]+ ") +re_map_entry = re.compile(r"\{.+?\(MP_QSTR_([A-Za-z0-9_]+)\).+\},") +re_mp_obj_dict_t = re.compile( + r"(?P(static )?const mp_obj_dict_t (?P[a-z0-9_]+) = \{ \.base = \{&mp_type_dict\}, \.map = \{ \.all_keys_are_qstrs = 1, \.is_fixed = 1, \.is_ordered = )1(?P, \.used = .+ };)$" +) +re_mp_map_t = re.compile( + r"(?P(static )?const mp_map_t (?P[a-z0-9_]+) = \{ \.all_keys_are_qstrs = 1, \.is_fixed = 1, \.is_ordered = )1(?P, \.used = .+ };)$" +) +re_mp_rom_map_elem_t = re.compile(r"static const mp_rom_map_elem_t [a-z_0-9]+\[\] = {$") + # this must match the equivalent function in qstr.c def compute_hash(qstr): @@ -55,18 +60,19 @@ def compute_hash(qstr): # Make sure that valid hash is never zero, zero means "hash not computed" return (hash & ((1 << (8 * bytes_in_qstr_hash)) - 1)) or 1 + # this algo must match the equivalent in map.c def hash_insert(map, key, value): hash = compute_hash(key) pos = hash % len(map) start_pos = pos if print_debug: - print(' insert %s: start at %u/%u -- ' % (key, pos, len(map)), end='') + print(" insert %s: start at %u/%u -- " % (key, pos, len(map)), end="") while True: if map[pos] is None: # found empty slot, so key is not in table if print_debug: - print('put at %u' % pos) + print("put at %u" % pos) map[pos] = (key, value) return else: @@ -76,6 +82,7 @@ def hash_insert(map, key, value): pos = (pos + 1) % len(map) assert pos != start_pos + def hash_find(map, key): hash = compute_hash(key) pos = hash % len(map) @@ -92,6 +99,7 @@ def hash_find(map, key): if pos == start_pos: return attempts, None + def process_map_table(file, line, output): output.append(line) @@ -101,7 +109,7 @@ def process_map_table(file, line, output): while True: line = file.readline() if len(line) == 0: - print('unexpected end of input') + print("unexpected end of input") sys.exit(1) line = line.strip() if len(line) == 0: @@ -110,38 +118,38 @@ def process_map_table(file, line, output): if re_preproc_line.match(line): # preprocessor line number comment continue - if line == '};': + if line == "};": # end of table (we assume it appears on a single line) break table_contents.append(line) # make combined string of entries - entries_str = ''.join(table_contents) + entries_str = "".join(table_contents) # split into individual entries entries = [] while entries_str: # look for single entry, by matching nested braces match = None - if entries_str[0] == '{': + if entries_str[0] == "{": nested_braces = 0 for i in range(len(entries_str)): - if entries_str[i] == '{': + if entries_str[i] == "{": nested_braces += 1 - elif entries_str[i] == '}': + elif entries_str[i] == "}": nested_braces -= 1 if nested_braces == 0: - match = re_map_entry.match(entries_str[:i + 2]) + match = re_map_entry.match(entries_str[: i + 2]) break if not match: - print('unknown line in table:', entries_str) + print("unknown line in table:", entries_str) sys.exit(1) # extract single entry line = match.group(0) qstr = match.group(1) - entries_str = entries_str[len(line):].lstrip() + entries_str = entries_str[len(line) :].lstrip() # add the qstr and the whole line to list of all entries entries.append((qstr, line)) @@ -164,28 +172,28 @@ def process_map_table(file, line, output): attempts, line = hash_find(map, qstr) assert line is not None if print_debug: - print(' %s lookup took %u attempts' % (qstr, attempts)) + print(" %s lookup took %u attempts" % (qstr, attempts)) total_attempts += attempts - if len(entries): + if entries: stats = len(map), len(entries) / len(map), total_attempts / len(entries) else: stats = 0, 0, 0 if print_debug: - print(' table stats: size=%d, load=%.2f, avg_lookups=%.1f' % stats) + print(" table stats: size=%d, load=%.2f, avg_lookups=%.1f" % stats) # output hash table for row in map: if row is None: - output.append('{ 0, 0 },\n') + output.append("{ 0, 0 },\n") else: - output.append(row[1] + '\n') - output.append('};\n') + output.append(row[1] + "\n") + output.append("};\n") # skip to next non-blank line while True: line = file.readline() if len(line) == 0: - print('unexpected end of input') + print("unexpected end of input") sys.exit(1) line = line.strip() if len(line) == 0: @@ -197,19 +205,20 @@ def process_map_table(file, line, output): if match is None: match = re_mp_map_t.match(line) if match is None: - print('expecting mp_obj_dict_t or mp_map_t definition') + print("expecting mp_obj_dict_t or mp_map_t definition") print(output[0]) print(line) sys.exit(1) - line = match.group('head') + '0' + match.group('tail') + '\n' + line = match.group("head") + "0" + match.group("tail") + "\n" output.append(line) - return (match.group('id'),) + stats + return (match.group("id"),) + stats + def process_file(filename): output = [] file_changed = False - with open(filename, 'rt') as f: + with open(filename, "rt") as f: while True: line = f.readline() if not line: @@ -218,39 +227,41 @@ def process_file(filename): file_changed = True stats = process_map_table(f, line, output) if print_stats: - print(' [%s: size=%d, load=%.2f, avg_lookups=%.1f]' % stats) + print(" [%s: size=%d, load=%.2f, avg_lookups=%.1f]" % stats) else: output.append(line) if file_changed: if print_debug: - print(' modifying static maps in', output[0].strip()) - with open(filename, 'wt') as f: + print(" modifying static maps in", output[0].strip()) + with open(filename, "wt") as f: for line in output: f.write(line) + def main(): # run actual C compiler # need to quote args that have special characters in them def quote(s): - if s.find('<') != -1 or s.find('>') != -1: + if s.find("<") != -1 or s.find(">") != -1: return "'" + s + "'" else: return s - ret = os.system(cc1_path + ' ' + ' '.join(quote(s) for s in sys.argv[1:])) + + ret = os.system(cc1_path + " " + " ".join(quote(s) for s in sys.argv[1:])) if ret != 0: - ret = (ret & 0x7f) or 127 # make it in range 0-127, but non-zero + ret = (ret & 0x7F) or 127 # make it in range 0-127, but non-zero sys.exit(ret) - if sys.argv[1] == '-E': + if sys.argv[1] == "-E": # CPP has been run, now do our processing stage for i, arg in enumerate(sys.argv): - if arg == '-o': + if arg == "-o": return process_file(sys.argv[i + 1]) print('%s: could not find "-o" option' % (sys.argv[0],)) sys.exit(1) - elif sys.argv[1] == '-fpreprocessed': + elif sys.argv[1] == "-fpreprocessed": # compiler has been run, nothing more to do return else: @@ -258,5 +269,6 @@ def main(): print('%s: unknown first option "%s"' % (sys.argv[0], sys.argv[1])) sys.exit(1) -if __name__ == '__main__': + +if __name__ == "__main__": main() diff --git a/tools/chart_code_size.py b/tools/chart_code_size.py index cd62074fd0d83..f3d9c5bdc40f5 100644 --- a/tools/chart_code_size.py +++ b/tools/chart_code_size.py @@ -24,7 +24,7 @@ def parse_hex(h): @click.command() @click.argument("elf_filename") -def do_all_the_things(elf_filename): # noqa: C901: too complex +def do_all_the_things(elf_filename): # noqa: C901 too complex symbol = None last_address = 0 all_symbols = {} diff --git a/tools/ci.sh b/tools/ci.sh index cfc9754837f76..132fbd8f81cf3 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -9,15 +9,20 @@ fi # Ensure known OPEN_MAX (NO_FILES) limit. ulimit -n 1024 +# Fail on some things which are warnings otherwise +export MICROPY_MAINTAINER_BUILD=1 + ######################################################################################## # general helper functions function ci_gcc_arm_setup { + sudo apt-get update sudo apt-get install gcc-arm-none-eabi libnewlib-arm-none-eabi arm-none-eabi-gcc --version } function ci_gcc_riscv_setup { + sudo apt-get update sudo apt-get install gcc-riscv64-unknown-elf picolibc-riscv64-unknown-elf riscv64-unknown-elf-gcc --version } @@ -35,6 +40,7 @@ function ci_picotool_setup { # c code formatting function ci_c_code_formatting_setup { + sudo apt-get update sudo apt-get install uncrustify uncrustify --version } @@ -74,42 +80,69 @@ function ci_code_size_setup { ci_picotool_setup } +function _ci_is_git_merge { + [[ $(git log -1 --format=%P "$1" | wc -w) > 1 ]] +} + function ci_code_size_build { # check the following ports for the change in their code size - PORTS_TO_CHECK=bmusxpdv + # Override the list by setting PORTS_TO_CHECK in the environment before invoking ci. + : ${PORTS_TO_CHECK:=bmusxpdv} + SUBMODULES="lib/asf4 lib/berkeley-db-1.xx lib/btstack lib/cyw43-driver lib/lwip lib/mbedtls lib/micropython-lib lib/nxp_driver lib/pico-sdk lib/stm32lib lib/tinyusb" # Default GitHub pull request sets HEAD to a generated merge commit # between PR branch (HEAD^2) and base branch (i.e. master) (HEAD^1). # # We want to compare this generated commit with the base branch, to see what - # the code size impact would be if we merged this PR. - REFERENCE=$(git rev-parse --short HEAD^1) - COMPARISON=$(git rev-parse --short HEAD) + # the code size impact would be if we merged this PR. During CI we are at a merge commit, + # so this tests the merged PR against its merge base. + # Override the refs by setting REFERENCE and/or COMPARISON in the environment before invoking ci. + : ${COMPARISON:=$(git rev-parse --short HEAD)} + : ${REFERENCE:=$(git rev-parse --short ${COMPARISON}^1)} echo "Comparing sizes of reference ${REFERENCE} to ${COMPARISON}..." git log --oneline $REFERENCE..$COMPARISON - function code_size_build_step { - COMMIT=$1 - OUTFILE=$2 - IGNORE_ERRORS=$3 - - echo "Building ${COMMIT}..." - git checkout --detach $COMMIT - git submodule update --init $SUBMODULES - git show -s - tools/metrics.py clean $PORTS_TO_CHECK - tools/metrics.py build $PORTS_TO_CHECK | tee $OUTFILE || $IGNORE_ERRORS - } - - # build reference, save to size0 - # ignore any errors with this build, in case master is failing - code_size_build_step $REFERENCE ~/size0 true - # build PR/branch, save to size1 - code_size_build_step $COMPARISON ~/size1 false - - unset -f code_size_build_step + OLD_BRANCH="$(git rev-parse --abbrev-ref HEAD)" + + ( # Execute in a subshell so the trap & code_size_build_step doesn't leak + function code_size_build_step { + if [ ! -z "$OLD_BRANCH" ]; then + trap 'git checkout "$OLD_BRANCH"' RETURN EXIT ERR + fi + + COMMIT=$1 + OUTFILE=$2 + IGNORE_ERRORS=$3 + + git checkout --detach $COMMIT + git submodule update --init $SUBMODULES + git show -s + tools/metrics.py clean "$PORTS_TO_CHECK" + # Allow errors from tools/metrics.py to propagate out of the pipe below. + set -o pipefail + tools/metrics.py build "$PORTS_TO_CHECK" | tee -a $OUTFILE || $IGNORE_ERRORS + return $? + } + + # build reference, save to size0 + # ignore any errors with this build, in case master is failing + echo "BUILDING $(git log --format='%s [%h]' -1 ${REFERENCE})" > ~/size0 + code_size_build_step $REFERENCE ~/size0 true + # build PR/branch, save to size1 + if _ci_is_git_merge "$COMPARISON"; then + echo "BUILDING $(git log --oneline -1 --format='%s [merge of %h]' ${COMPARISON}^2)" + else + echo "BUILDING $(git log --oneline -1 --formta='%s [%h]' ${COMPARISON})" + fi > ~/size1 + code_size_build_step $COMPARISON ~/size1 false + ) +} + +function ci_code_size_report { + # Allow errors from tools/metrics.py to propagate out of the pipe above. + (set -o pipefail; tools/metrics.py diff ~/size0 ~/size1 | tee diff) } ######################################################################################## @@ -117,15 +150,12 @@ function ci_code_size_build { function ci_mpy_format_setup { sudo apt-get update - sudo apt-get install python2.7 sudo pip3 install pyelftools - python2.7 --version python3 --version } function ci_mpy_format_test { # Test mpy-tool.py dump feature on bytecode - python2.7 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy python3 ./tools/mpy-tool.py -xd tests/frozen/frozentest.mpy # Build MicroPython @@ -143,6 +173,15 @@ function ci_mpy_format_test { $micropython ./tools/mpy-tool.py -x -d examples/natmod/features1/features1.mpy } +function ci_mpy_cross_debug_emitter { + make ${MAKEOPTS} -C mpy-cross + mpy_cross=./mpy-cross/build/mpy-cross + + # Make sure the debug emitter does not crash or fail for simple files + $mpy_cross -X emit=native -march=debug ./tests/basics/0prelim.py | \ + grep -E "ENTRY|EXIT" | wc -l | grep "^2$" +} + ######################################################################################## # ports/cc3200 @@ -158,14 +197,17 @@ function ci_cc3200_build { ######################################################################################## # ports/esp32 -# GitHub tag of ESP-IDF to use for CI (note: must be a tag or a branch) -IDF_VER=v5.2.2 +# GitHub tag of ESP-IDF to use for CI, extracted from the esp32 dependency lockfile +# This should end up as a tag name like vX.Y.Z +# (note: This hacky parsing can be replaced with 'yq' once Ubuntu >=24.04 is in use) +IDF_VER=v$(grep -A10 "idf:" ports/esp32/lockfiles/dependencies.lock.esp32 | grep "version:" | head -n1 | sed -E 's/ +version: //') PYTHON=$(command -v python3 2> /dev/null) PYTHON_VER=$(${PYTHON:-python} --version | cut -d' ' -f2) export IDF_CCACHE_ENABLE=1 function ci_esp32_idf_setup { + echo "Using ESP-IDF version $IDF_VER" git clone --depth 1 --branch $IDF_VER https://github.com/espressif/esp-idf.git # doing a treeless clone isn't quite as good as --shallow-submodules, but it # is smaller than full clones and works when the submodule commit isn't a head. @@ -204,13 +246,28 @@ function ci_esp32_build_s3_c3 { make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C3 } +function ci_esp32_build_c2_c5_c6 { + ci_esp32_build_common + + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C2 + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C5 + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_C6 +} + +function ci_esp32_build_p4 { + ci_esp32_build_common + + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_P4 + make ${MAKEOPTS} -C ports/esp32 BOARD=ESP32_GENERIC_P4 BOARD_VARIANT=C6_WIFI +} + ######################################################################################## # ports/esp8266 function ci_esp8266_setup { sudo pip3 install pyserial esptool==3.3.1 pyelftools ar - wget https://github.com/jepler/esp-open-sdk/releases/download/2018-06-10/xtensa-lx106-elf-standalone.tar.gz - zcat xtensa-lx106-elf-standalone.tar.gz | tar x + wget https://micropython.org/resources/xtensa-lx106-elf-standalone.tar.gz + (set -o pipefail; zcat xtensa-lx106-elf-standalone.tar.gz | tar x) # Remove this esptool.py so pip version is used instead rm xtensa-lx106-elf/bin/esptool.py } @@ -317,17 +374,52 @@ function ci_qemu_setup_rv32 { qemu-system-riscv32 --version } -function ci_qemu_build_arm { +function ci_qemu_setup_rv64 { + ci_gcc_riscv_setup + sudo apt-get update + sudo apt-get install qemu-system + qemu-system-riscv64 --version +} + +function ci_qemu_build_arm_prepare { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/qemu submodules +} + +function ci_qemu_build_arm_bigendian { + ci_qemu_build_arm_prepare make ${MAKEOPTS} -C ports/qemu CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 - make ${MAKEOPTS} -C ports/qemu clean - make ${MAKEOPTS} -C ports/qemu test_full +} + +function ci_qemu_build_arm_sabrelite { + ci_qemu_build_arm_prepare make ${MAKEOPTS} -C ports/qemu BOARD=SABRELITE test_full +} - # Test building and running native .mpy with armv7m architecture. +function ci_qemu_build_arm_thumb_softfp { + ci_qemu_build_arm_prepare + make BOARD=MPS2_AN385 ${MAKEOPTS} -C ports/qemu test_full + + # Test building native .mpy with ARM-M softfp architectures. + ci_native_mpy_modules_build armv6m ci_native_mpy_modules_build armv7m - make ${MAKEOPTS} -C ports/qemu test_natmod + + # Test running native .mpy with all ARM-M architectures. + make BOARD=MPS2_AN385 ${MAKEOPTS} -C ports/qemu test_natmod RUN_TESTS_EXTRA="--arch armv6m" + make BOARD=MPS2_AN385 ${MAKEOPTS} -C ports/qemu test_natmod RUN_TESTS_EXTRA="--arch armv7m" +} + +function ci_qemu_build_arm_thumb_hardfp { + ci_qemu_build_arm_prepare + make BOARD=MPS2_AN500 ${MAKEOPTS} -C ports/qemu test_full + + # Test building native .mpy with all ARM-M hardfp architectures. + ci_native_mpy_modules_build armv7emsp + ci_native_mpy_modules_build armv7emdp + + # Test running native .mpy with all ARM-M hardfp architectures. + make BOARD=MPS2_AN500 ${MAKEOPTS} -C ports/qemu test_natmod RUN_TESTS_EXTRA="--arch armv7emsp" + make BOARD=MPS2_AN500 ${MAKEOPTS} -C ports/qemu test_natmod RUN_TESTS_EXTRA="--arch armv7emdp" } function ci_qemu_build_rv32 { @@ -340,6 +432,12 @@ function ci_qemu_build_rv32 { make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 test_natmod } +function ci_qemu_build_rv64 { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 submodules + make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 test +} + ######################################################################################## # ports/renesas-ra @@ -403,13 +501,22 @@ function ci_samd_build { # ports/stm32 function ci_stm32_setup { - ci_gcc_arm_setup + # Use a recent version of the ARM toolchain, to work with Cortex-M55. + wget https://developer.arm.com/-/media/Files/downloads/gnu/14.3.rel1/binrel/arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi.tar.xz + xzcat arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi.tar.xz | tar x + pip3 install pyelftools pip3 install ar pip3 install pyhy } +function ci_stm32_path { + echo $(pwd)/arm-gnu-toolchain-14.3.rel1-x86_64-arm-none-eabi/bin +} + function ci_stm32_pyb_build { + # This function builds the following MCU families: F4, F7. + make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/stm32 MICROPY_PY_NETWORK_WIZNET5K=5200 submodules make ${MAKEOPTS} -C ports/stm32 BOARD=PYBD_SF2 submodules @@ -421,20 +528,18 @@ function ci_stm32_pyb_build { make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBV10 CFLAGS_EXTRA='-DMBOOT_FSLOAD=1 -DMBOOT_VFS_LFS2=1' make ${MAKEOPTS} -C ports/stm32/mboot BOARD=PYBD_SF6 make ${MAKEOPTS} -C ports/stm32/mboot BOARD=STM32F769DISC CFLAGS_EXTRA='-DMBOOT_ADDRESS_SPACE_64BIT=1 -DMBOOT_SDCARD_ADDR=0x100000000ULL -DMBOOT_SDCARD_BYTE_SIZE=0x400000000ULL -DMBOOT_FSLOAD=1 -DMBOOT_VFS_FAT=1' - - # Test building native .mpy with armv7emsp architecture. - git submodule update --init lib/berkeley-db-1.xx - ci_native_mpy_modules_build armv7emsp } function ci_stm32_nucleo_build { + # This function builds the following MCU families: F0, H5, H7, L0, L4, WB. + make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_H743ZI submodules git submodule update --init lib/mynewt-nimble # Test building various MCU families, some with additional options. make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_F091RC - make ${MAKEOPTS} -C ports/stm32 BOARD=STM32H573I_DK + make ${MAKEOPTS} -C ports/stm32 BOARD=STM32H573I_DK CFLAGS_EXTRA='-DMICROPY_HW_TINYUSB_STACK=1' make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_H743ZI COPT=-O2 CFLAGS_EXTRA='-DMICROPY_PY_THREAD=1' make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L073RZ make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L476RG DEBUG=1 @@ -454,21 +559,22 @@ function ci_stm32_nucleo_build { } function ci_stm32_misc_build { + # This function builds the following MCU families: G0, G4, H7, L1, N6, U5, WL. + make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/stm32 BOARD=ARDUINO_GIGA submodules make ${MAKEOPTS} -C ports/stm32 BOARD=ARDUINO_GIGA + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_G0B1RE + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_G474RE + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_L152RE + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_N657X0 + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_U5A5ZJ_Q + make ${MAKEOPTS} -C ports/stm32 BOARD=NUCLEO_WL55 } ######################################################################################## # ports/unix -CI_UNIX_OPTS_SYS_SETTRACE=( - MICROPY_PY_BTREE=0 - MICROPY_PY_FFI=0 - MICROPY_PY_SSL=0 - CFLAGS_EXTRA="-DMICROPY_PY_SYS_SETTRACE=1" -) - CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS=( MICROPY_PY_BTREE=0 MICROPY_PY_FFI=0 @@ -494,6 +600,26 @@ CI_UNIX_OPTS_QEMU_RISCV64=( MICROPY_STANDALONE=1 ) +CI_UNIX_OPTS_SANITIZE_ADDRESS=( + # Macro MP_ASAN allows detecting ASan on gcc<=13 + CFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0 -DMP_ASAN=1" + LDFLAGS_EXTRA="-fsanitize=address --param asan-use-after-return=0" +) + +CI_UNIX_OPTS_SANITIZE_UNDEFINED=( + # Macro MP_UBSAN allows detecting UBSan on gcc<=13 + CFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute -DMP_UBSAN=1" + LDFLAGS_EXTRA="-fsanitize=undefined -fno-sanitize=nonnull-attribute" +) + +CI_UNIX_OPTS_REPR_B=( + VARIANT=standard + CFLAGS_EXTRA="-DMICROPY_OBJ_REPR=MICROPY_OBJ_REPR_B -DMICROPY_PY_UCTYPES=0 -Dmp_int_t=int32_t -Dmp_uint_t=uint32_t" + MICROPY_FORCE_32BIT=1 + RUN_TESTS_MPY_CROSS_FLAGS="--mpy-cross-flags=\"-march=x86 -msmall-int-bits=30\"" + +) + function ci_unix_build_helper { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix "$@" submodules @@ -509,13 +635,26 @@ function ci_unix_run_tests_helper { make -C ports/unix "$@" test } +function ci_unix_run_tests_full_extra { + micropython=$1 + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-multitests.py multi_net/*.py) + (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-perfbench.py --average 1 1000 1000) +} + +function ci_unix_run_tests_full_no_native_helper { + variant=$1 + shift + micropython=../ports/unix/build-$variant/micropython + make -C ports/unix VARIANT=$variant "$@" test_full_no_native + ci_unix_run_tests_full_extra $micropython +} + function ci_unix_run_tests_full_helper { variant=$1 shift micropython=../ports/unix/build-$variant/micropython make -C ports/unix VARIANT=$variant "$@" test_full - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-multitests.py multi_net/*.py) - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=$micropython ./run-perfbench.py 1000 1000) + ci_unix_run_tests_full_extra $micropython } function ci_native_mpy_modules_build { @@ -524,40 +663,19 @@ function ci_native_mpy_modules_build { else arch=$1 fi - for natmod in features1 features3 features4 heapq re + for natmod in btree deflate features1 features3 features4 framebuf heapq random re do - make -C examples/natmod/$natmod clean + make -C examples/natmod/$natmod ARCH=$arch clean make -C examples/natmod/$natmod ARCH=$arch done - # deflate, framebuf, and random currently cannot build on xtensa due to - # some symbols that have been removed from the compiler's runtime, in - # favour of being provided from ROM. - if [ $arch != "xtensa" ]; then - for natmod in deflate framebuf random - do - make -C examples/natmod/$natmod clean - make -C examples/natmod/$natmod ARCH=$arch - done - fi - - # features2 requires soft-float on armv7m, rv32imc, and xtensa. On armv6m - # the compiler generates absolute relocations in the object file - # referencing soft-float functions, which is not supported at the moment. - make -C examples/natmod/features2 clean - if [ $arch = "rv32imc" ] || [ $arch = "armv7m" ] || [ $arch = "xtensa" ]; then + # features2 requires soft-float on rv32imc and xtensa. + make -C examples/natmod/features2 ARCH=$arch clean + if [ $arch = "rv32imc" ] || [ $arch = "xtensa" ]; then make -C examples/natmod/features2 ARCH=$arch MICROPY_FLOAT_IMPL=float - elif [ $arch != "armv6m" ]; then + else make -C examples/natmod/features2 ARCH=$arch fi - - # btree requires thread local storage support on rv32imc, whilst on xtensa - # it relies on symbols that are provided from ROM but not exposed to - # natmods at the moment. - if [ $arch != "rv32imc" ] && [ $arch != "xtensa" ]; then - make -C examples/natmod/btree clean - make -C examples/natmod/btree ARCH=$arch - fi } function ci_native_mpy_modules_32bit_build { @@ -569,7 +687,7 @@ function ci_unix_minimal_build { } function ci_unix_minimal_run_tests { - (cd tests && MICROPY_CPYTHON3=python3 MICROPY_MICROPYTHON=../ports/unix/build-minimal/micropython ./run-tests.py -e exception_chain -e self_type_check -e subclass_native_init -d basics) + make -C ports/unix VARIANT=minimal test } function ci_unix_standard_build { @@ -591,9 +709,9 @@ function ci_unix_standard_v2_run_tests { } function ci_unix_coverage_setup { - sudo pip3 install setuptools - sudo pip3 install pyelftools - sudo pip3 install ar + pip3 install setuptools + pip3 install pyelftools + pip3 install ar gcc --version python3 --version } @@ -604,7 +722,7 @@ function ci_unix_coverage_build { } function ci_unix_coverage_run_tests { - ci_unix_run_tests_full_helper coverage + MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage } function ci_unix_coverage_run_mpy_merge_tests { @@ -639,12 +757,11 @@ function ci_unix_coverage_run_native_mpy_tests { function ci_unix_32bit_setup { sudo dpkg --add-architecture i386 sudo apt-get update - sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 python2.7 + sudo apt-get install gcc-multilib g++-multilib libffi-dev:i386 sudo pip3 install setuptools sudo pip3 install pyelftools sudo pip3 install ar gcc --version - python2.7 --version python3 --version } @@ -662,13 +779,20 @@ function ci_unix_coverage_32bit_run_native_mpy_tests { } function ci_unix_nanbox_build { - # Use Python 2 to check that it can run the build scripts - ci_unix_build_helper PYTHON=python2.7 VARIANT=nanbox CFLAGS_EXTRA="-DMICROPY_PY_MATH_CONSTANTS=1" + ci_unix_build_helper VARIANT=nanbox CFLAGS_EXTRA="-DMICROPY_PY_MATH_CONSTANTS=1" ci_unix_build_ffi_lib_helper gcc -m32 } function ci_unix_nanbox_run_tests { - ci_unix_run_tests_full_helper nanbox PYTHON=python2.7 + ci_unix_run_tests_full_no_native_helper nanbox +} + +function ci_unix_longlong_build { + ci_unix_build_helper VARIANT=longlong "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" +} + +function ci_unix_longlong_run_tests { + ci_unix_run_tests_full_helper longlong } function ci_unix_float_build { @@ -681,7 +805,17 @@ function ci_unix_float_run_tests { ci_unix_run_tests_helper CFLAGS_EXTRA="-DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT" } +function ci_unix_gil_enabled_build { + ci_unix_build_helper VARIANT=standard MICROPY_PY_THREAD_GIL=1 + ci_unix_build_ffi_lib_helper gcc +} + +function ci_unix_gil_enabled_run_tests { + ci_unix_run_tests_full_helper standard MICROPY_PY_THREAD_GIL=1 +} + function ci_unix_clang_setup { + sudo apt-get update sudo apt-get install clang clang --version } @@ -693,7 +827,8 @@ function ci_unix_stackless_clang_build { } function ci_unix_stackless_clang_run_tests { - ci_unix_run_tests_helper CC=clang + # Timeout needs to be increased for thread/stress_aes.py test. + MICROPY_TEST_TIMEOUT=90 ci_unix_run_tests_helper CC=clang } function ci_unix_float_clang_build { @@ -706,24 +841,36 @@ function ci_unix_float_clang_run_tests { ci_unix_run_tests_helper CC=clang } -function ci_unix_settrace_build { +function ci_unix_settrace_stackless_build { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/unix submodules + make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" +} + +function ci_unix_settrace_stackless_run_tests { + ci_unix_run_tests_full_helper standard "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" +} + +function ci_unix_sanitize_undefined_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix submodules - make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE[@]}" + make ${MAKEOPTS} -C ports/unix VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" + ci_unix_build_ffi_lib_helper gcc } -function ci_unix_settrace_run_tests { - ci_unix_run_tests_full_helper standard "${CI_UNIX_OPTS_SYS_SETTRACE[@]}" +function ci_unix_sanitize_undefined_run_tests { + MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_UNDEFINED[@]}" } -function ci_unix_settrace_stackless_build { +function ci_unix_sanitize_address_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/unix submodules - make ${MAKEOPTS} -C ports/unix "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" + make ${MAKEOPTS} -C ports/unix VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_ADDRESS[@]}" + ci_unix_build_ffi_lib_helper gcc } -function ci_unix_settrace_stackless_run_tests { - ci_unix_run_tests_full_helper standard "${CI_UNIX_OPTS_SYS_SETTRACE_STACKLESS[@]}" +function ci_unix_sanitize_address_run_tests { + MICROPY_TEST_TIMEOUT=60 ci_unix_run_tests_full_helper coverage VARIANT=coverage "${CI_UNIX_OPTS_SANITIZE_ADDRESS[@]}" } function ci_unix_macos_build { @@ -740,7 +887,9 @@ function ci_unix_macos_run_tests { # Issues with macOS tests: # - float_parse and float_parse_doubleprec parse/print floats out by a few mantissa bits # - ffi_callback crashes for an unknown reason - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-standard/micropython ./run-tests.py --exclude '(float_parse|float_parse_doubleprec|ffi_callback).py') + # - thread/stress_heap.py is flaky + # - thread/thread_gc1.py is flaky + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-standard/micropython ./run-tests.py --exclude '(float_parse|float_parse_doubleprec|ffi_callback|thread/stress_heap|thread/thread_gc1).py') } function ci_unix_qemu_mips_setup { @@ -758,8 +907,12 @@ function ci_unix_qemu_mips_build { } function ci_unix_qemu_mips_run_tests { + # Issues with MIPS tests: + # - thread/stress_aes.py takes around 50 seconds + # - thread/stress_recurse.py is flaky + # - thread/thread_gc1.py is flaky file ./ports/unix/build-coverage/micropython - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py) + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=90 ./run-tests.py --exclude 'thread/stress_recurse.py|thread/thread_gc1.py') } function ci_unix_qemu_arm_setup { @@ -779,8 +932,11 @@ function ci_unix_qemu_arm_build { function ci_unix_qemu_arm_run_tests { # Issues with ARM tests: # - (i)listdir does not work, it always returns the empty list (it's an issue with the underlying C call) + # - thread/stress_aes.py takes around 70 seconds + # - thread/stress_recurse.py is flaky + # - thread/thread_gc1.py is flaky file ./ports/unix/build-coverage/micropython - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py --exclude 'vfs_posix.*\.py') + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=90 ./run-tests.py --exclude 'vfs_posix.*\.py|thread/stress_recurse.py|thread/thread_gc1.py') } function ci_unix_qemu_riscv64_setup { @@ -798,14 +954,30 @@ function ci_unix_qemu_riscv64_build { } function ci_unix_qemu_riscv64_run_tests { + # Issues with RISCV-64 tests: + # - thread/stress_aes.py takes around 140 seconds + # - thread/stress_recurse.py is flaky + # - thread/thread_gc1.py is flaky file ./ports/unix/build-coverage/micropython - (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython ./run-tests.py) + (cd tests && MICROPY_MICROPYTHON=../ports/unix/build-coverage/micropython MICROPY_TEST_TIMEOUT=180 ./run-tests.py --exclude 'thread/stress_recurse.py|thread/thread_gc1.py') +} + +function ci_unix_repr_b_build { + ci_unix_build_helper "${CI_UNIX_OPTS_REPR_B[@]}" + ci_unix_build_ffi_lib_helper gcc -m32 +} + +function ci_unix_repr_b_run_tests { + # ci_unix_run_tests_full_no_native_helper is not used due to + # https://github.com/micropython/micropython/issues/18105 + ci_unix_run_tests_helper "${CI_UNIX_OPTS_REPR_B[@]}" } ######################################################################################## # ports/windows function ci_windows_setup { + sudo apt-get update sudo apt-get install gcc-mingw-w64 } @@ -813,14 +985,15 @@ function ci_windows_build { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/windows submodules make ${MAKEOPTS} -C ports/windows CROSS_COMPILE=i686-w64-mingw32- + make ${MAKEOPTS} -C ports/windows CROSS_COMPILE=x86_64-w64-mingw32- BUILD=build-standard-w64 } ######################################################################################## # ports/zephyr -ZEPHYR_DOCKER_VERSION=v0.26.13 -ZEPHYR_SDK_VERSION=0.16.8 -ZEPHYR_VERSION=v3.7.0 +ZEPHYR_DOCKER_VERSION=v0.28.1 +ZEPHYR_SDK_VERSION=0.17.2 +ZEPHYR_VERSION=v4.2.0 function ci_zephyr_setup { IMAGE=ghcr.io/zephyrproject-rtos/ci:${ZEPHYR_DOCKER_VERSION} @@ -859,6 +1032,7 @@ function ci_zephyr_install { } function ci_zephyr_build { + git submodule update --init lib/micropython-lib docker exec zephyr-ci west build -p auto -b qemu_x86 -- -DCONF_FILE=prj_minimal.conf docker exec zephyr-ci west build -p auto -b frdm_k64f docker exec zephyr-ci west build -p auto -b mimxrt1050_evk @@ -867,9 +1041,7 @@ function ci_zephyr_build { function ci_zephyr_run_tests { docker exec zephyr-ci west build -p auto -b qemu_cortex_m3 -- -DCONF_FILE=prj_minimal.conf - # Issues with zephyr tests: - # - inf_nan_arith fails pow(-1, nan) test - (cd tests && ./run-tests.py -t execpty:"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -monitor null -serial pty -kernel ../ports/zephyr/build/zephyr/zephyr.elf" -d basics float --exclude inf_nan_arith) + (cd tests && ./run-tests.py -t execpty:"qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -monitor null -serial pty -kernel ../ports/zephyr/build/zephyr/zephyr.elf") } ######################################################################################## @@ -886,3 +1058,86 @@ function ci_alif_ae3_build { make ${MAKEOPTS} -C ports/alif BOARD=OPENMV_AE3 MCU_CORE=M55_DUAL make ${MAKEOPTS} -C ports/alif BOARD=ALIF_ENSEMBLE MCU_CORE=M55_DUAL } + +function _ci_help { + # Note: these lines must be indented with tab characters (required by bash <<-EOF) + cat <<-EOF + ci.sh: Script fragments used during CI + + When invoked as a script, runs a sequence of ci steps, + stopping after the first error. + + Usage: + ${BASH_SOURCE} step1 step2... + + Steps: + EOF + if type -path column > /dev/null 2>&1; then + grep '^function ci_' $0 | awk '{print $2}' | sed 's/^ci_//' | column + else + grep '^function ci_' $0 | awk '{print $2}' | sed 's/^ci_//' + fi + exit +} + +function _ci_bash_completion { + echo "alias ci=\"$(readlink -f "$0")\"; complete -W '$(grep '^function ci_' $0 | awk '{print $2}' | sed 's/^ci_//')' ci" +} + +function _ci_zsh_completion { + echo "alias ci=\"$(readlink -f "$0"\"); _complete_mpy_ci_zsh() { compadd $(grep '^function ci_' $0 | awk '{sub(/^ci_/,"",$2); print $2}' | tr '\n' ' ') }; autoload -Uz compinit; compinit; compdef _complete_mpy_ci_zsh $(readlink -f "$0")" +} + +function _ci_fish_completion { + echo "alias ci=\"$(readlink -f "$0"\"); complete -c ci -p $(readlink -f "$0") -f -a '$(grep '^function ci_' $(readlink -f "$0") | awk '{sub(/^ci_/,"",$2); print $2}' | tr '\n' ' ')'" +} + +function _ci_main { + case "$1" in + (-h|-?|--help) + _ci_help + ;; + (--bash-completion) + _ci_bash_completion + ;; + (--zsh-completion) + _ci_zsh_completion + ;; + (--fish-completion) + _ci_fish_completion + ;; + (-*) + echo "Unknown option: $1" 1>&2 + exit 1 + ;; + (*) + set -e + cd $(dirname "$0")/.. + while [ $# -ne 0 ]; do + ci_$1 + shift + done + ;; + esac +} + +# https://stackoverflow.com/questions/2683279/how-to-detect-if-a-script-is-being-sourced +sourced=0 +if [ -n "$ZSH_VERSION" ]; then + case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac +elif [ -n "$KSH_VERSION" ]; then + [ "$(cd -- "$(dirname -- "$0")" && pwd -P)/$(basename -- "$0")" != "$(cd -- "$(dirname -- "${.sh.file}")" && pwd -P)/$(basename -- "${.sh.file}")" ] && sourced=1 +elif [ -n "$BASH_VERSION" ]; then + (return 0 2>/dev/null) && sourced=1 +else # All other shells: examine $0 for known shell binary filenames. + # Detects `sh` and `dash`; add additional shell filenames as needed. + case ${0##*/} in sh|-sh|dash|-dash) sourced=1;; esac +fi + +if [ $sourced -eq 0 ]; then + # invoked as a command + if [ "$#" -eq 0 ]; then + set -- --help + fi + _ci_main "$@" +fi diff --git a/tools/ci_check_duplicate_usb_vid_pid.py b/tools/ci_check_duplicate_usb_vid_pid.py index 5361914af199b..38cce0f28be94 100644 --- a/tools/ci_check_duplicate_usb_vid_pid.py +++ b/tools/ci_check_duplicate_usb_vid_pid.py @@ -104,7 +104,7 @@ def check_vid_pid(files, clusterlist): """ usb_pattern = re.compile( - r"^CIRCUITPY_USB_DEVICE\s*=\s*0$|^IDF_TARGET = (esp32|esp32c2|esp32c3|esp32c6|esp32h2|esp32p4)$|^MCU_SERIES = MG24$", + r"^CIRCUITPY_USB_DEVICE\s*=\s*0$|^IDF_TARGET = (esp32|esp32c2|esp32c3|esp32c5|esp32c6|esp32c61|esp32h2|esp32p4)$|^MCU_SERIES = MG24$", flags=re.M, ) diff --git a/tools/ci_set_idf_constraint.py b/tools/ci_set_idf_constraint.py new file mode 100644 index 0000000000000..49c187f458782 --- /dev/null +++ b/tools/ci_set_idf_constraint.py @@ -0,0 +1,47 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 Adafruit Industries LLC +# +# SPDX-License-Identifier: MIT + +"""Set IDF_CONSTRAINT_FILE for CI. + +CI installs requirements-dev.txt for all ports, but on Espressif builds we must +also apply the matching ESP-IDF constraints file so pip does not upgrade shared +packages (for example click) beyond what ESP-IDF allows. This script derives the +active ESP-IDF major.minor version from version.cmake and exports the exact +constraints file path into GITHUB_ENV for later install steps. +""" + +import os +import pathlib +import re + +TOP = pathlib.Path(__file__).resolve().parent.parent + + +def main() -> None: + version_cmake = TOP / "ports" / "espressif" / "esp-idf" / "tools" / "cmake" / "version.cmake" + data = version_cmake.read_text(encoding="utf-8") + + major = re.search(r"IDF_VERSION_MAJOR\s+(\d+)", data) + minor = re.search(r"IDF_VERSION_MINOR\s+(\d+)", data) + if major is None or minor is None: + raise RuntimeError(f"Unable to parse IDF version from {version_cmake}") + + idf_tools_path = os.environ.get("IDF_TOOLS_PATH") + if not idf_tools_path: + raise RuntimeError("IDF_TOOLS_PATH is not set") + + constraint = ( + pathlib.Path(idf_tools_path) / f"espidf.constraints.v{major.group(1)}.{minor.group(1)}.txt" + ) + + github_env = os.environ.get("GITHUB_ENV") + if github_env: + with open(github_env, "a", encoding="utf-8") as f: + f.write(f"IDF_CONSTRAINT_FILE={constraint}\n") + + print(f"Set IDF_CONSTRAINT_FILE={constraint}") + + +if __name__ == "__main__": + main() diff --git a/tools/codeformat.py b/tools/codeformat.py index a648d401ec312..cf91049a73162 100644 --- a/tools/codeformat.py +++ b/tools/codeformat.py @@ -231,7 +231,7 @@ def batch(cmd, files, N=200, check=False): command = ["python3", "tools/ruff_bindings.py"] batch(command, bindings_files(), check=True) - # Format Python files with black. + # Format Python files with "ruff format" (using config in pyproject.toml). if format_py: command = ["ruff", "format"] if args.v: diff --git a/tools/file2h.py b/tools/file2h.py index df9cc02fdabba..2707d4a16e5cf 100644 --- a/tools/file2h.py +++ b/tools/file2h.py @@ -9,8 +9,6 @@ # ; # This script simply prints the escaped string straight to stdout -from __future__ import print_function - import sys # Can either be set explicitly, or left blank to auto-detect diff --git a/tools/gen_display_resources.py b/tools/gen_display_resources.py index 7b6de6d95d445..9ed829f7d3738 100644 --- a/tools/gen_display_resources.py +++ b/tools/gen_display_resources.py @@ -8,8 +8,8 @@ import struct import sys -sys.path.insert(0, "bitmap_font") -sys.path.insert(0, "../../tools/bitmap_font") +sys.path.insert(0, "tools/bitmap_font") # For running from root +sys.path.insert(0, "../../tools/bitmap_font") # For running from a port directory from adafruit_bitmap_font import bitmap_font diff --git a/tools/insert-usb-ids.py b/tools/insert-usb-ids.py index 4691d5a710c1e..1b043e1fef04f 100644 --- a/tools/insert-usb-ids.py +++ b/tools/insert-usb-ids.py @@ -6,8 +6,6 @@ # inserts those values into the template file specified by sys.argv[2], # printing the result to stdout -from __future__ import print_function - import sys import re import string diff --git a/tools/makemanifest.py b/tools/makemanifest.py index e076a03e0be3c..860935397af14 100644 --- a/tools/makemanifest.py +++ b/tools/makemanifest.py @@ -24,7 +24,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from __future__ import print_function import sys import os import subprocess diff --git a/tools/manifestfile.py b/tools/manifestfile.py index beaa36d0f5fc2..9c7a6e140f968 100644 --- a/tools/manifestfile.py +++ b/tools/manifestfile.py @@ -25,7 +25,6 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -from __future__ import print_function import contextlib import os import sys diff --git a/tools/metrics.py b/tools/metrics.py index f6189e65abb4d..8bb96ba119a23 100755 --- a/tools/metrics.py +++ b/tools/metrics.py @@ -43,20 +43,23 @@ """ -import collections, sys, re, subprocess +import collections, os, sys, re, shlex, subprocess, multiprocessing -MAKE_FLAGS = ["-j3", "CFLAGS_EXTRA=-DNDEBUG"] +MAKE_FLAGS = ["-j{}".format(multiprocessing.cpu_count()), "CFLAGS_EXTRA=-DNDEBUG"] class PortData: - def __init__(self, name, dir, output, make_flags=None): + def __init__(self, name, dir, output, make_flags=None, pre_cmd=None): self.name = name self.dir = dir self.output = output self.make_flags = make_flags self.needs_mpy_cross = dir not in ("bare-arm", "minimal") + self.pre_cmd = pre_cmd +mpy_cross_output = "mpy-cross/build/mpy-cross" + port_data = { "b": PortData("bare-arm", "bare-arm", "build/firmware.elf"), "m": PortData("minimal x86", "minimal", "build/firmware.elf"), @@ -65,7 +68,12 @@ def __init__(self, name, dir, output, make_flags=None): "s": PortData("stm32", "stm32", "build-PYBV10/firmware.elf", "BOARD=PYBV10"), "c": PortData("cc3200", "cc3200", "build/WIPY/release/application.axf", "BTARGET=application"), "8": PortData("esp8266", "esp8266", "build-ESP8266_GENERIC/firmware.elf"), - "3": PortData("esp32", "esp32", "build-ESP32_GENERIC/micropython.elf"), + "3": PortData( + "esp32", + "esp32", + "build-ESP32_GENERIC/micropython.elf", + pre_cmd=". esp-idf/export.sh", + ), "x": PortData("mimxrt", "mimxrt", "build-TEENSY40/firmware.elf"), "e": PortData("renesas-ra", "renesas-ra", "build-EK_RA6M2/firmware.elf"), "r": PortData("nrf", "nrf", "build-PCA10040/firmware.elf"), @@ -74,8 +82,15 @@ def __init__(self, name, dir, output, make_flags=None): "v": PortData("qemu rv32", "qemu", "build-VIRT_RV32/firmware.elf", "BOARD=VIRT_RV32"), } +for port_letter, port in port_data.items(): + port.pre_cmd = os.environ.get(f"PRE_CMD_{port_letter}", port.pre_cmd) + + +def quoted(args): + return " ".join(shlex.quote(word) for word in args) -def syscmd(*args): + +def syscmd(*args, pre_cmd=None): sys.stdout.flush() a2 = [] for a in args: @@ -83,6 +98,10 @@ def syscmd(*args): a2.append(a) elif a: a2.extend(a) + if pre_cmd is not None: + a2_quoted = quoted(a2) + a2 = ["bash", "-c", "{} && {}".format(pre_cmd, a2_quoted)] + print(a2) subprocess.check_call(a2) @@ -108,6 +127,8 @@ def read_build_log(filename): with open(filename) as f: for line in f: line = line.strip() + if line.startswith("BUILDING ") and "_ref" not in data: + data["_ref"] = line.removeprefix("BUILDING ") if line.strip() == "COMPUTING SIZES": found_sizes = True elif found_sizes: @@ -139,9 +160,15 @@ def do_diff(args): data1 = read_build_log(args[0]) data2 = read_build_log(args[1]) + ref1 = data1.pop("_ref", "(unknown ref)") + ref2 = data2.pop("_ref", "(unknown ref)") + print(f"Reference: {ref1}") + print(f"Comparison: {ref2}") max_delta = None for key, value1 in data1.items(): value2 = data2[key] + if key == mpy_cross_output: + name = "mpy-cross" for port in port_data.values(): if key == "ports/{}/{}".format(port.dir, port.output): name = port.name @@ -181,8 +208,19 @@ def do_clean(args): ports = parse_port_list(args) print("CLEANING") + + if any(port.needs_mpy_cross for port in ports): + syscmd("make", "-C", "mpy-cross", "clean") + for port in ports: - syscmd("make", "-C", "ports/{}".format(port.dir), port.make_flags, "clean") + syscmd( + "make", + "-C", + "ports/{}".format(port.dir), + port.make_flags, + "clean", + pre_cmd=port.pre_cmd, + ) def do_build(args): @@ -196,7 +234,14 @@ def do_build(args): print("BUILDING PORTS") for port in ports: - syscmd("make", "-C", "ports/{}".format(port.dir), MAKE_FLAGS, port.make_flags) + syscmd( + "make", + "-C", + "ports/{}".format(port.dir), + MAKE_FLAGS, + port.make_flags, + pre_cmd=port.pre_cmd, + ) do_sizes(args) @@ -207,6 +252,10 @@ def do_sizes(args): ports = parse_port_list(args) print("COMPUTING SIZES") + + if any(port.needs_mpy_cross for port in ports): + syscmd("size", mpy_cross_output) + for port in ports: syscmd("size", "ports/{}/{}".format(port.dir, port.output)) diff --git a/tools/mpy-tool.py b/tools/mpy-tool.py index 8849f2f1e5956..bf0b89018e4e7 100755 --- a/tools/mpy-tool.py +++ b/tools/mpy-tool.py @@ -24,40 +24,21 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. -# Python 2/3/MicroPython compatibility code -from __future__ import print_function +import io +import struct import sys +from binascii import hexlify -if sys.version_info[0] == 2: - from binascii import hexlify as hexlify_py2 - - str_cons = lambda val, enc=None: str(val) - bytes_cons = lambda val, enc=None: bytearray(val) - is_str_type = lambda o: isinstance(o, str) - is_bytes_type = lambda o: type(o) is bytearray - is_int_type = lambda o: isinstance(o, int) or isinstance(o, long) # noqa: F821 - - def hexlify_to_str(b): - x = hexlify_py2(b) - return ":".join(x[i : i + 2] for i in range(0, len(x), 2)) - -elif sys.version_info[0] == 3: # Also handles MicroPython - from binascii import hexlify - - str_cons = str - bytes_cons = bytes - is_str_type = lambda o: isinstance(o, str) - is_bytes_type = lambda o: isinstance(o, bytes) - is_int_type = lambda o: isinstance(o, int) - - def hexlify_to_str(b): - return str(hexlify(b, ":"), "ascii") +str_cons = str +bytes_cons = bytes +is_str_type = lambda o: isinstance(o, str) +is_bytes_type = lambda o: isinstance(o, bytes) +is_int_type = lambda o: isinstance(o, int) -# end compatibility code +def hexlify_to_str(b): + return str(hexlify(b, ":"), "ascii") -import sys -import struct sys.path.append(sys.path[0] + "/../py") import makeqstrdata as qstrutil @@ -114,6 +95,23 @@ class Config: MP_NATIVE_ARCH_XTENSA = 9 MP_NATIVE_ARCH_XTENSAWIN = 10 MP_NATIVE_ARCH_RV32IMC = 11 +MP_NATIVE_ARCH_RV64IMC = 12 + +MP_NATIVE_ARCH_NAMES = [ + "NONE", + "X86", + "X64", + "ARMV6", + "ARMV6M", + "ARMV7M", + "ARMV7EM", + "ARMV7EMSP", + "ARMV7EMDP", + "XTENSA", + "XTENSAWIN", + "RV32IMC", + "RV64IMC", +] MP_PERSISTENT_OBJ_FUN_TABLE = 0 MP_PERSISTENT_OBJ_NONE = 1 @@ -142,6 +140,8 @@ class Config: MP_BC_FORMAT_VAR_UINT = 2 MP_BC_FORMAT_OFFSET = 3 +MP_NATIVE_ARCH_FLAGS_PRESENT = 0x40 + mp_unary_op_method_name = ( "__pos__", "__neg__", @@ -306,6 +306,25 @@ class Opcode: MP_BC_POP_JUMP_IF_TRUE, MP_BC_POP_JUMP_IF_FALSE, ) + ALL_OFFSET = ( + MP_BC_UNWIND_JUMP, + MP_BC_JUMP, + MP_BC_POP_JUMP_IF_TRUE, + MP_BC_POP_JUMP_IF_FALSE, + MP_BC_JUMP_IF_TRUE_OR_POP, + MP_BC_JUMP_IF_FALSE_OR_POP, + MP_BC_SETUP_WITH, + MP_BC_SETUP_EXCEPT, + MP_BC_SETUP_FINALLY, + MP_BC_POP_EXCEPT_JUMP, + MP_BC_FOR_ITER, + ) + ALL_WITH_CHILD = ( + MP_BC_MAKE_FUNCTION, + MP_BC_MAKE_FUNCTION_DEFARGS, + MP_BC_MAKE_CLOSURE, + MP_BC_MAKE_CLOSURE_DEFARGS, + ) # Create a dict mapping opcode value to opcode name. mapping = ["unknown" for _ in range(256)] @@ -562,6 +581,7 @@ def __init__( mpy_source_file, mpy_segments, header, + arch_flags, qstr_table, obj_table, raw_code, @@ -574,6 +594,7 @@ def __init__( self.mpy_segments = mpy_segments self.source_file = qstr_table[0] self.header = header + self.arch_flags = arch_flags self.qstr_table = qstr_table self.obj_table = obj_table self.raw_code = raw_code @@ -651,6 +672,14 @@ def disassemble(self): print("mpy_source_file:", self.mpy_source_file) print("source_file:", self.source_file.str) print("header:", hexlify_to_str(self.header)) + arch_index = (self.header[2] >> 2) & 0x2F + if arch_index >= len(MP_NATIVE_ARCH_NAMES): + arch_name = "UNKNOWN" + else: + arch_name = MP_NATIVE_ARCH_NAMES[arch_index] + print("arch:", arch_name) + if self.header[2] & MP_NATIVE_ARCH_FLAGS_PRESENT != 0: + print("arch_flags:", hex(self.arch_flags)) print("qstr_table[%u]:" % len(self.qstr_table)) for q in self.qstr_table: print(" %s" % q.str) @@ -888,7 +917,7 @@ def __init__(self, parent_name, qstr_table, fun_data, prelude_offset, code_kind) self.escaped_name = unique_escaped_name def disassemble_children(self): - print(" children:", [rc.simple_name.str for rc in self.children]) + self.print_children_annotated() for rc in self.children: rc.disassemble() @@ -980,6 +1009,75 @@ def freeze_raw_code(self, prelude_ptr=None, type_sig=0): raw_code_count += 1 raw_code_content += 4 * 4 + @staticmethod + def decode_lineinfo(line_info: memoryview) -> "tuple[int, int, memoryview]": + c = line_info[0] + if (c & 0x80) == 0: + # 0b0LLBBBBB encoding + return (c & 0x1F), (c >> 5), line_info[1:] + else: + # 0b1LLLBBBB 0bLLLLLLLL encoding (l's LSB in second byte) + return (c & 0xF), (((c << 4) & 0x700) | line_info[1]), line_info[2:] + + def get_source_annotation(self, ip: int, file=None) -> dict: + bc_offset = ip - self.offset_opcodes + try: + line_info = memoryview(self.fun_data)[self.offset_line_info : self.offset_opcodes] + except AttributeError: + return {"file": file, "line": None} + + source_line = 1 + while line_info: + bc_increment, line_increment, line_info = self.decode_lineinfo(line_info) + if bc_offset >= bc_increment: + bc_offset -= bc_increment + source_line += line_increment + else: + break + + return {"file": file, "line": source_line} + + def get_label(self, ip: "int | None" = None, child_num: "int | None" = None) -> str: + if ip is not None: + assert child_num is None + return "%s.%d" % (self.escaped_name, ip) + elif child_num is not None: + return "%s.child%d" % (self.escaped_name, child_num) + else: + return "%s" % self.escaped_name + + def print_children_annotated(self) -> None: + """ + Equivalent to `print(" children:", [child.simple_name.str for child in self.children])`, + but also includes json markers for the start and end of each one's name in that line. + """ + + labels = ["%s.children" % self.escaped_name] + annotation_labels = [] + output = io.StringIO() + output.write(" children: [") + sep = ", " + for i, child in enumerate(self.children): + if i != 0: + output.write(sep) + start_col = output.tell() + 1 + output.write(child.simple_name.str) + end_col = output.tell() + 1 + labels.append(self.get_label(child_num=i)) + annotation_labels.append( + { + "name": self.get_label(child_num=i), + "target": child.get_label(), + "range": { + "startCol": start_col, + "endCol": end_col, + }, + }, + ) + output.write("]") + + print(output.getvalue(), annotations={"labels": annotation_labels}, labels=labels) + class RawCodeBytecode(RawCode): def __init__(self, parent_name, qstr_table, obj_table, fun_data): @@ -988,9 +1086,58 @@ def __init__(self, parent_name, qstr_table, obj_table, fun_data): parent_name, qstr_table, fun_data, 0, MP_CODE_BYTECODE ) + def get_opcode_annotations_labels( + self, opcode: int, ip: int, arg: int, sz: int, arg_pos: int, arg_len: int + ) -> "tuple[dict, list[str]]": + annotations = { + "source": self.get_source_annotation(ip), + "disassembly": Opcode.mapping[opcode], + } + labels = [self.get_label(ip)] + + if opcode in Opcode.ALL_OFFSET: + annotations["link"] = { + "offset": arg_pos, + "length": arg_len, + "to": ip + arg + sz, + } + annotations["labels"] = [ + { + "name": self.get_label(ip), + "target": self.get_label(ip + arg + sz), + "range": { + "startCol": arg_pos + 1, + "endCol": arg_pos + arg_len + 1, + }, + }, + ] + + elif opcode in Opcode.ALL_WITH_CHILD: + try: + child = self.children[arg] + except IndexError: + # link out-of-range child to the child array itself + target = "%s.children" % self.escaped_name + else: + # link resolvable child to the actual child + target = child.get_label() + + annotations["labels"] = [ + { + "name": self.get_label(ip), + "target": target, + "range": { + "startCol": arg_pos + 1, + "endCol": arg_pos + arg_len + 1, + }, + }, + ] + + return annotations, labels + def disassemble(self): bc = self.fun_data - print("simple_name:", self.simple_name.str) + print("simple_name:", self.simple_name.str, labels=[self.get_label()]) print(" raw bytecode:", len(bc), hexlify_to_str(bc)) print(" prelude:", self.prelude_signature) print(" args:", [self.qstr_table[i].str for i in self.names[1:]]) @@ -1006,9 +1153,22 @@ def disassemble(self): pass else: arg = "" - print( - " %-11s %s %s" % (hexlify_to_str(bc[ip : ip + sz]), Opcode.mapping[bc[ip]], arg) + + pre_arg_part = " %-11s %s" % ( + hexlify_to_str(bc[ip : ip + sz]), + Opcode.mapping[bc[ip]], + ) + arg_part = "%s" % arg + annotations, labels = self.get_opcode_annotations_labels( + opcode=bc[ip], + ip=ip, + arg=arg, + sz=sz, + arg_pos=len(pre_arg_part) + 1, + arg_len=len(arg_part), ) + + print(pre_arg_part, arg_part, annotations=annotations, labels=labels) ip += sz self.disassemble_children() @@ -1085,6 +1245,7 @@ def __init__( MP_NATIVE_ARCH_XTENSA, MP_NATIVE_ARCH_XTENSAWIN, MP_NATIVE_ARCH_RV32IMC, + MP_NATIVE_ARCH_RV64IMC, ): self.fun_data_attributes = '__attribute__((section(".text,\\"ax\\",@progbits # ")))' else: @@ -1102,13 +1263,13 @@ def __init__( self.fun_data_attributes += " __attribute__ ((aligned (4)))" elif ( MP_NATIVE_ARCH_ARMV6M <= config.native_arch <= MP_NATIVE_ARCH_ARMV7EMDP - ) or config.native_arch == MP_NATIVE_ARCH_RV32IMC: - # ARMVxxM or RV32IMC -- two byte align. + ) or MP_NATIVE_ARCH_RV32IMC <= config.native_arch <= MP_NATIVE_ARCH_RV64IMC: + # ARMVxxM or RV{32,64}IMC -- two byte align. self.fun_data_attributes += " __attribute__ ((aligned (2)))" def disassemble(self): fun_data = self.fun_data - print("simple_name:", self.simple_name.str) + print("simple_name:", self.simple_name.str, labels=[self.get_label()]) print( " raw data:", len(fun_data), @@ -1362,7 +1523,7 @@ def read_mpy(filename): if header[1] != config.MPY_VERSION: raise MPYReadError(filename, "incompatible .mpy version") feature_byte = header[2] - mpy_native_arch = feature_byte >> 2 + mpy_native_arch = (feature_byte >> 2) & 0x2F if mpy_native_arch != MP_NATIVE_ARCH_NONE: mpy_sub_version = feature_byte & 3 if mpy_sub_version != config.MPY_SUB_VERSION: @@ -1373,6 +1534,11 @@ def read_mpy(filename): raise MPYReadError(filename, "native architecture mismatch") config.mp_small_int_bits = header[3] + arch_flags = 0 + # Read the architecture-specific flag bits if present. + if (feature_byte & MP_NATIVE_ARCH_FLAGS_PRESENT) != 0: + arch_flags = reader.read_uint() + # Read number of qstrs, and number of objects. n_qstr = reader.read_uint() n_obj = reader.read_uint() @@ -1401,6 +1567,7 @@ def read_mpy(filename): filename, segments, header, + arch_flags, qstr_table, obj_table, raw_code, @@ -1696,26 +1863,40 @@ def merge_mpy(compiled_modules, output_file): merged_mpy.extend(f.read()) else: main_cm_idx = None + arch_flags = 0 for idx, cm in enumerate(compiled_modules): feature_byte = cm.header[2] - mpy_native_arch = feature_byte >> 2 + mpy_native_arch = (feature_byte >> 2) & 0x2F if mpy_native_arch: # Must use qstr_table and obj_table from this raw_code if main_cm_idx is not None: raise Exception("can't merge files when more than one contains native code") main_cm_idx = idx + arch_flags = cm.arch_flags if main_cm_idx is not None: # Shift main_cm to front of list. compiled_modules.insert(0, compiled_modules.pop(main_cm_idx)) + if config.arch_flags is not None: + arch_flags = config.arch_flags + header = bytearray(4) ## CIRCUITPY-CHANGE: "C" is used for CircuitPython header[0] = ord("C") header[1] = config.MPY_VERSION - header[2] = config.native_arch << 2 | config.MPY_SUB_VERSION if config.native_arch else 0 + header[2] = ( + (MP_NATIVE_ARCH_FLAGS_PRESENT if arch_flags != 0 else 0) + | config.native_arch << 2 + | config.MPY_SUB_VERSION + if config.native_arch + else 0 + ) header[3] = config.mp_small_int_bits merged_mpy.extend(header) + if arch_flags != 0: + merged_mpy.extend(mp_encode_uint(arch_flags)) + n_qstr = 0 n_obj = 0 for cm in compiled_modules: @@ -1771,6 +1952,138 @@ def copy_section(file, offset, offset2): f.write(merged_mpy) +def extract_segments(compiled_modules, basename, kinds_arg): + import re + + kind_str = ("META", "QSTR", "OBJ", "CODE") + kinds = set() + if kinds_arg is not None: + for kind in kinds_arg.upper().split(","): + if kind in kind_str: + kinds.add(kind) + else: + raise Exception('unknown segment kind "%s"' % (kind,)) + segments = [] + for module in compiled_modules: + for segment in module.mpy_segments: + if not kinds or kind_str[segment.kind] in kinds: + segments.append((module.mpy_source_file, module.source_file.str, segment)) + count_len = len(str(len(segments))) + sanitiser = re.compile("[^a-zA-Z0-9_.-]") + for counter, entry in enumerate(segments): + file_name, source_file, segment = entry + output_name = ( + basename + + "_" + + str(counter).rjust(count_len, "0") + + "_" + + sanitiser.sub("_", source_file) + + "_" + + kind_str[segment.kind] + + "_" + + sanitiser.sub("_", str(segment.name)) + + ".bin" + ) + with open(file_name, "rb") as source: + with open(output_name, "wb") as output: + source.seek(segment.start) + output.write(source.read(segment.end - segment.start)) + + +class PrintShim: + """Base class for interposing extra functionality onto the global `print` method.""" + + def __init__(self): + self.wrapped_print = None + + def __enter__(self): + global print + + if self.wrapped_print is not None: + raise RecursionError + + self.wrapped_print = print + print = self + + return self + + def __exit__(self, exc_type, exc_value, traceback): + global print + + if self.wrapped_print is None: + return + + print = self.wrapped_print + self.wrapped_print = None + + self.on_exit() + + def on_exit(self): + pass + + def __call__(self, *a, **k): + return self.wrapped_print(*a, **k) + + +class PrintIgnoreExtraArgs(PrintShim): + """Just strip the `annotations` and `labels` kwargs and pass down to the underlying print.""" + + def __call__(self, *a, annotations: dict = {}, labels: "list[str]" = (), **k): + return super().__call__(*a, **k) + + +class PrintJson(PrintShim): + """Output lines as godbolt-compatible JSON with extra annotation info from `annotations` and `labels`, rather than plain text.""" + + def __init__(self, fp=sys.stdout, language_id: str = "mpy"): + super().__init__() + self.fp = fp + self.asm = { + "asm": [], + "labelDefinitions": {}, + "languageId": language_id, + } + self.line_number: int = 0 + self.buf: "io.StringIO | None" = None + + def on_exit(self): + import json + + if self.buf is not None: + # flush last partial line + self.__call__() + + json.dump(self.asm, self.fp) + + def __call__(self, *a, annotations: dict = {}, labels: "list[str]" = (), **k): + # ignore prints directed to an explicit output + if "file" in k: + return super().__call__(*a, **k) + + if self.buf is None: + self.buf = io.StringIO() + + super().__call__(*a, file=sys.stderr, **k) + + if "end" in k: + # buffer partial-line prints to collect into a single AsmResultLine + return super().__call__(*a, file=self.buf, **k) + else: + retval = super().__call__(*a, file=self.buf, end="", **k) + output = self.buf.getvalue() + self.buf = None + + asm_line = {"text": output} + asm_line.update(annotations) + self.asm["asm"].append(asm_line) + + self.line_number += 1 + for label in labels: + self.asm["labelDefinitions"][label] = self.line_number + + return retval + + def main(args=None): global global_qstrs @@ -1784,9 +2097,23 @@ def main(args=None): "-d", "--disassemble", action="store_true", help="output disassembled contents of files" ) cmd_parser.add_argument("-f", "--freeze", action="store_true", help="freeze files") + cmd_parser.add_argument( + "-j", + "--json", + action="store_true", + help="output hexdump, disassembly, and frozen code as JSON with extra metadata", + ) cmd_parser.add_argument( "--merge", action="store_true", help="merge multiple .mpy files into one" ) + cmd_parser.add_argument( + "-e", "--extract", metavar="BASE", type=str, help="write segments into separate files" + ) + cmd_parser.add_argument( + "--extract-only", + metavar="KIND[,...]", + help="extract only segments of the given type (meta, qstr, obj, code)", + ) cmd_parser.add_argument("-q", "--qstr-header", help="qstr header file to freeze against") cmd_parser.add_argument( "-mlongint-impl", @@ -1801,6 +2128,12 @@ def main(args=None): default=16, help="mpz digit size used by target (default 16)", ) + cmd_parser.add_argument( + "-march-flags", + metavar="F", + type=int, + help="architecture flags value to set in the output file (strips existing flags if not present)", + ) cmd_parser.add_argument("-o", "--output", default=None, help="output file") cmd_parser.add_argument("files", nargs="+", help="input .mpy files") args = cmd_parser.parse_args(args) @@ -1813,6 +2146,7 @@ def main(args=None): }[args.mlongint_impl] config.MPZ_DIG_SIZE = args.mmpz_dig_size config.native_arch = MP_NATIVE_ARCH_NONE + config.arch_flags = args.march_flags # set config values for qstrs, and get the existing base set of qstrs # already in the firmware @@ -1836,24 +2170,40 @@ def main(args=None): print(er, file=sys.stderr) sys.exit(1) - if args.hexdump: - hexdump_mpy(compiled_modules) + if args.json: + if args.freeze: + print_shim = PrintJson(sys.stdout, language_id="c") + elif args.hexdump: + print_shim = PrintJson(sys.stdout, language_id="stderr") + elif args.disassemble: + print_shim = PrintJson(sys.stdout, language_id="mpy") + else: + print_shim = PrintJson(sys.stdout) + else: + print_shim = PrintIgnoreExtraArgs() - if args.disassemble: + with print_shim: if args.hexdump: - print() - disassemble_mpy(compiled_modules) + hexdump_mpy(compiled_modules) - if args.freeze: - try: - freeze_mpy(firmware_qstr_idents, compiled_modules) - except FreezeError as er: - print(er, file=sys.stderr) - sys.exit(1) + if args.disassemble: + if args.hexdump: + print() + disassemble_mpy(compiled_modules) + + if args.freeze: + try: + freeze_mpy(firmware_qstr_idents, compiled_modules) + except FreezeError as er: + print(er, file=sys.stderr) + sys.exit(1) if args.merge: merge_mpy(compiled_modules, args.output) + if args.extract: + extract_segments(compiled_modules, args.extract, args.extract_only) + if __name__ == "__main__": main() diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 219cd1a7468bc..26db07261631c 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -402,6 +402,7 @@ def __init__(self, arch): self.known_syms = {} # dict of symbols that are defined self.unresolved_syms = [] # list of unresolved symbols self.mpy_relocs = [] # list of relocations needed in the output .mpy file + self.externs = {} # dict of externally-defined symbols def check_arch(self, arch_name): if arch_name != self.arch.name: @@ -491,10 +492,14 @@ def populate_got(env): sym = got_entry.sym if hasattr(sym, "resolved"): sym = sym.resolved - sec = sym.section - addr = sym["st_value"] - got_entry.sec_name = sec.name - got_entry.link_addr += sec.addr + addr + if sym.name in env.externs: + got_entry.sec_name = ".external.fixed_addr" + got_entry.link_addr = env.externs[sym.name] + else: + sec = sym.section + addr = sym["st_value"] + got_entry.sec_name = sec.name + got_entry.link_addr += sec.addr + addr # Get sorted GOT, sorted by external, text, rodata, bss so relocations can be combined got_list = sorted( @@ -520,6 +525,9 @@ def populate_got(env): dest = int(got_entry.name.split("+")[1], 16) // env.arch.word_size elif got_entry.sec_name == ".external.mp_fun_table": dest = got_entry.sym.mp_fun_table_offset + elif got_entry.sec_name == ".external.fixed_addr": + # Fixed-address symbols should not be relocated. + continue elif got_entry.sec_name.startswith(".text"): dest = ".text" elif got_entry.sec_name.startswith(".rodata"): @@ -703,8 +711,9 @@ def do_relocation_text(env, text_addr, r): (addr, value) = process_riscv32_relocation(env, text_addr, r) elif env.arch.name == "EM_ARM" and r_info_type == R_ARM_ABS32: - # happens for soft-float on armv6m - raise ValueError("Absolute relocations not supported on ARM") + # Absolute relocation, handled as a data relocation. + do_relocation_data(env, text_addr, r) + return else: # Unknown/unsupported relocation @@ -773,9 +782,9 @@ def do_relocation_data(env, text_addr, r): ): # Relocation in data.rel.ro to internal/external symbol if env.arch.word_size == 4: - struct_type = "/). + + symbols = {} + + LINE_REGEX = re.compile( + r"^(?PPROVIDE\()?" # optional weak marker start + r"(?P[a-zA-Z_]\w*)" # symbol name + r"=0x(?P
[\da-fA-F]{1,8})*" # symbol address + r"(?(weak)\));$", # optional weak marker end and line terminator + re.ASCII, + ) + + inside_comment = False + for line in (line.strip() for line in source.readlines()): + if line.startswith("/*") and not inside_comment: + if not line.endswith("*/"): + inside_comment = True + continue + if inside_comment: + if line.endswith("*/"): + inside_comment = False + continue + if line.startswith("//"): + continue + match = LINE_REGEX.match("".join(line.split())) + if not match: + continue + tokens = match.groupdict() + symbol = tokens["symbol"] + address = int(tokens["address"], 16) + if symbol in symbols: + raise ValueError(f"Symbol {symbol} already defined") + symbols[symbol] = address + return symbols + + def main(): import argparse @@ -1501,6 +1569,13 @@ def main(): cmd_parser.add_argument( "--output", "-o", default=None, help="output .mpy file (default to input with .o->.mpy)" ) + cmd_parser.add_argument( + "--externs", + "-e", + type=argparse.FileType("rt"), + default=None, + help="linkerscript providing fixed-address symbols to augment symbol resolution", + ) cmd_parser.add_argument("files", nargs="+", help="input files") args = cmd_parser.parse_args() diff --git a/tools/pyboard.py b/tools/pyboard.py index 20310ba7081c0..c9f65d5d873c2 100755 --- a/tools/pyboard.py +++ b/tools/pyboard.py @@ -248,14 +248,22 @@ def inWaiting(self): class Pyboard: def __init__( - self, device, baudrate=115200, user="micro", password="python", wait=0, exclusive=True + self, + device, + baudrate=115200, + user="micro", + password="python", + wait=0, + exclusive=True, + timeout=None, + write_timeout=5, ): self.in_raw_repl = False self.use_raw_paste = True if device.startswith("exec:"): self.serial = ProcessToSerial(device[len("exec:") :]) elif device.startswith("execpty:"): - self.serial = ProcessPtyToTerminal(device[len("qemupty:") :]) + self.serial = ProcessPtyToTerminal(device[len("execpty:") :]) elif device and device[0].isdigit() and device[-1].isdigit() and device.count(".") == 3: # device looks like an IP address self.serial = TelnetToSerial(device, user, password, read_timeout=10) @@ -264,7 +272,12 @@ def __init__( import serial.tools.list_ports # Set options, and exclusive if pyserial supports it - serial_kwargs = {"baudrate": baudrate, "interCharTimeout": 1} + serial_kwargs = { + "baudrate": baudrate, + "timeout": timeout, + "write_timeout": write_timeout, + "interCharTimeout": 1, + } if serial.__version__ >= "3.3": serial_kwargs["exclusive"] = exclusive @@ -304,14 +317,25 @@ def __init__( def close(self): self.serial.close() - def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): - # if data_consumer is used then data is not accumulated and the ending must be 1 byte long + def read_until( + self, min_num_bytes, ending, timeout=10, data_consumer=None, timeout_overall=None + ): + """ + min_num_bytes: Obsolete. + ending: Return if 'ending' matches. + timeout [s]: Return if timeout between characters. None: Infinite timeout. + timeout_overall [s]: Return not later than timeout_overall. None: Infinite timeout. + data_consumer: Use callback for incoming characters. + If data_consumer is used then data is not accumulated and the ending must be 1 byte long + + It is not visible to the caller why the function returned. It could be ending or timeout. + """ assert data_consumer is None or len(ending) == 1 + assert isinstance(timeout, (type(None), int, float)) + assert isinstance(timeout_overall, (type(None), int, float)) - data = self.serial.read(min_num_bytes) - if data_consumer: - data_consumer(data) - timeout_count = 0 + data = b"" + begin_overall_s = begin_char_s = time.monotonic() while True: if data.endswith(ending): break @@ -322,15 +346,25 @@ def read_until(self, min_num_bytes, ending, timeout=10, data_consumer=None): data = new_data else: data = data + new_data - timeout_count = 0 + begin_char_s = time.monotonic() else: - timeout_count += 1 - if timeout is not None and timeout_count >= 100 * timeout: + if timeout is not None and time.monotonic() >= begin_char_s + timeout: + break + if ( + timeout_overall is not None + and time.monotonic() >= begin_overall_s + timeout_overall + ): break time.sleep(0.01) return data - def enter_raw_repl(self, soft_reset=True): + def enter_raw_repl(self, soft_reset=True, timeout_overall=10): + try: + self._enter_raw_repl_unprotected(soft_reset, timeout_overall) + except OSError as er: + raise PyboardError("could not enter raw repl: {}".format(er)) + + def _enter_raw_repl_unprotected(self, soft_reset, timeout_overall): self.serial.write(b"\r\x03") # ctrl-C: interrupt any running program # flush input (without relying on serial.flushInput()) @@ -342,7 +376,9 @@ def enter_raw_repl(self, soft_reset=True): self.serial.write(b"\r\x01") # ctrl-A: enter raw REPL if soft_reset: - data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n>") + data = self.read_until( + 1, b"raw REPL; CTRL-B to exit\r\n>", timeout_overall=timeout_overall + ) if not data.endswith(b"raw REPL; CTRL-B to exit\r\n>"): print(data) raise PyboardError("could not enter raw repl") @@ -352,12 +388,12 @@ def enter_raw_repl(self, soft_reset=True): # Waiting for "soft reboot" independently to "raw REPL" (done below) # allows boot.py to print, which will show up after "soft reboot" # and before "raw REPL". - data = self.read_until(1, b"soft reboot\r\n") + data = self.read_until(1, b"soft reboot\r\n", timeout_overall=timeout_overall) if not data.endswith(b"soft reboot\r\n"): print(data) raise PyboardError("could not enter raw repl") - data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n") + data = self.read_until(1, b"raw REPL; CTRL-B to exit\r\n", timeout_overall=timeout_overall) if not data.endswith(b"raw REPL; CTRL-B to exit\r\n"): print(data) raise PyboardError("could not enter raw repl") @@ -475,8 +511,8 @@ def eval(self, expression, parse=False): return ret # In Python3, call as pyboard.exec(), see the setattr call below. - def exec_(self, command, data_consumer=None): - ret, ret_err = self.exec_raw(command, data_consumer=data_consumer) + def exec_(self, command, timeout=10, data_consumer=None): + ret, ret_err = self.exec_raw(command, timeout, data_consumer) if ret_err: raise PyboardError("exception", ret, ret_err) return ret diff --git a/tools/pydfu.py b/tools/pydfu.py index cd7354818cdea..376c697cbd5eb 100755 --- a/tools/pydfu.py +++ b/tools/pydfu.py @@ -11,8 +11,6 @@ See document UM0391 for a description of the DFuse file. """ -from __future__ import print_function - import argparse import collections import inspect @@ -75,11 +73,7 @@ # USB DFU interface __DFU_INTERFACE = 0 -# Python 3 deprecated getargspec in favour of getfullargspec, but -# Python 2 doesn't have the latter, so detect which one to use -getargspec = getattr(inspect, "getfullargspec", getattr(inspect, "getargspec", None)) - -if "length" in getargspec(usb.util.get_string).args: +if "length" in inspect.getfullargspec(usb.util.get_string).args: # PyUSB 1.0.0.b1 has the length argument def get_string(dev, index): return usb.util.get_string(dev, 255, index) diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py index 67215d5c5d066..dba6ebd6de59a 100755 --- a/tools/verifygitlog.py +++ b/tools/verifygitlog.py @@ -96,20 +96,47 @@ def verify_message_body(raw_body, err): if len(subject_line) >= 73: err.error("Subject line must be 72 or fewer characters: " + subject_line) + # Do additional checks on the prefix of the subject line. + verify_subject_line_prefix(subject_line.split(": ")[0], err) + # Second one divides subject and body. if len(raw_body) > 1 and raw_body[1]: err.error("Second message line must be empty: " + raw_body[1]) # Message body lines. for line in raw_body[2:]: - # Long lines with URLs are exempt from the line length rule. - if len(line) >= 76 and "://" not in line: + # Long lines with URLs or human names are exempt from the line length rule. + if len(line) >= 76 and not ( + "://" in line + or line.startswith("Co-authored-by: ") + or line.startswith("Signed-off-by: ") + ): err.error("Message lines should be 75 or less characters: " + line) if not raw_body[-1].startswith("Signed-off-by: ") or "@" not in raw_body[-1]: err.error('Message must be signed-off. Use "git commit -s".') +def verify_subject_line_prefix(prefix, err): + ext = (".c", ".h", ".cpp", ".js", ".rst", ".md") + + if prefix.startswith((".", "/")): + err.error('Subject prefix cannot begin with "." or "/".') + + if prefix.endswith("/"): + err.error('Subject prefix cannot end with "/".') + + if prefix.startswith("ports/"): + err.error( + 'Subject prefix cannot begin with "ports/", start with the name of the port instead.' + ) + + if prefix.endswith(ext): + err.error( + "Subject prefix cannot end with a file extension, use the main part of the filename without the extension." + ) + + def run(args): verbose("run", *args)