diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 000000000..7b36bed7c
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,92 @@
+version: 2.1
+
+commands:
+  setup-toolchain:
+    parameters:
+        toolchain:
+            type: string
+        toolchain_url:
+            type: string
+    steps:
+#      - run:
+#          name: Make toolchain cache key
+#          command: echo "<< parameters.toolchain >>-<< parameters.toolchain_url>>" > toolchain_key
+#      - restore_cache:
+#          name: Restore Toolchain Cache
+#          key: deps-{{ checksum "toolchain_key" }}
+#          paths:
+#            - ~/cache/<< parameters.toolchain >>
+      - run:
+          name: Install Toolchain
+          command: |
+            # Only download if folder does not exist (not cached)
+            if [ ! -d ~/cache/<< parameters.toolchain >> ]; then
+              mkdir -p ~/cache/<< parameters.toolchain >>
+              wget << parameters.toolchain_url>> -O toolchain.tar.gz
+              tar -C ~/cache/<< parameters.toolchain >> -xaf toolchain.tar.gz
+            fi
+#      - save_cache:
+#          name: Save Toolchain Cache
+#          key: deps-{{ checksum "toolchain_key" }}
+#          paths:
+#            - ~/cache/<< parameters.toolchain >>
+      - run:
+          name: Setup build environment
+          command: |
+            echo "export PATH=$PATH:`echo ~/cache/<< parameters.toolchain >>/*/bin`" >> $BASH_ENV
+            # Install Ninja
+            NINJA_URL=https://github.com/ninja-build/ninja/releases/download/v1.12.1/ninja-linux.zip
+            wget $NINJA_URL -O ninja-linux.zip
+            unzip ninja-linux.zip -d ~/bin
+
+  get-deps:
+    parameters:
+      family:
+        type: string
+    steps:
+      - run:
+          name: Get Dependencies
+          command: |
+            python tools/get_deps.py << parameters.family >>
+
+jobs:
+  arm-clang:
+    parameters:
+      family:
+        type: string
+      build-system:
+        type: string
+
+    docker:
+      - image: cimg/base:current
+    resource_class: medium
+    environment:
+      TOOLCHAIN_URL: https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-17.0.1/LLVMEmbeddedToolchainForArm-17.0.1-Linux-x86_64.tar.xz
+    steps:
+      - checkout
+      - setup-toolchain:
+          toolchain: clang
+          toolchain_url: $TOOLCHAIN_URL
+      - get-deps:
+          family: << parameters.family >>
+      - run:
+          name: Build
+          command: |
+            # Only build one board per family for non PRs i.e commit to master
+            ONE_PER_FAMILY=""
+            if [ -z "$CIRCLE_PULL_REQUEST" ]; then
+              ONE_PER_FAMILY="--one-per-family"
+            fi
+            python tools/build.py $ONE_PER_FAMILY -s << parameters.build-system >> --toolchain clang << parameters.family >>
+
+workflows:
+  build:
+    jobs:
+      - arm-clang:
+          matrix:
+            parameters:
+              build-system:
+                - cmake
+              #family: ['stm32f1']
+              #family: ['stm32f1', 'stm32f2']
+              family: ['imxrt', 'kinetis_k kinetis_kl kinetis_k32l2', 'lpc11 lpc13 lpc15', 'lpc17 lpc18 lpc40 lpc43', 'lpc51 lpc54 lpc55', 'nrf', 'samd11 samd21 saml2x', 'samd5x_e5x samg', 'stm32f0 stm32f1 stm32f2 stm32f3', 'stm32f4', 'stm32f7', 'stm32g0 stm32g4 stm32h5', 'stm32h7', 'stm32l4 stm32u5 stm32wb']
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 000000000..0fd168e5a
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,66 @@
+# Generated from CLion C/C++ Code Style settings
+BasedOnStyle: LLVM
+AccessModifierOffset: -2
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: None
+AlignOperands: Align
+AllowAllArgumentsOnNextLine: false
+AllowAllConstructorInitializersOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: Always
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Always
+AllowShortLambdasOnASingleLine: All
+AllowShortLoopsOnASingleLine: true
+AlwaysBreakAfterReturnType: None
+AlwaysBreakTemplateDeclarations: Yes
+BreakBeforeBraces: Custom
+BraceWrapping:
+  AfterCaseLabel: false
+  AfterClass: false
+  AfterControlStatement: Never
+  AfterEnum: false
+  AfterFunction: false
+  AfterNamespace: false
+  AfterUnion: false
+  BeforeCatch: false
+  BeforeElse: false
+  IndentBraces: false
+  SplitEmptyFunction: false
+  SplitEmptyRecord: true
+BreakBeforeBinaryOperators: None
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeColon
+BreakInheritanceList: BeforeColon
+ColumnLimit: 0
+CompactNamespaces: false
+ContinuationIndentWidth: 4
+IndentCaseLabels: true
+IndentPPDirectives: BeforeHash
+IndentWidth: 2
+KeepEmptyLinesAtTheStartOfBlocks: true
+MaxEmptyLinesToKeep: 2
+NamespaceIndentation: All
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: true
+PointerAlignment: Right
+ReflowComments: false
+SpaceAfterCStyleCast: true
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: false
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 0
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInContainerLiterals: true
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+TabWidth: 2
+UseTab: Never
diff --git a/.github/actions/get_deps/action.yml b/.github/actions/get_deps/action.yml
new file mode 100644
index 000000000..eea241c6c
--- /dev/null
+++ b/.github/actions/get_deps/action.yml
@@ -0,0 +1,29 @@
+name: Get dependencies
+
+inputs:
+  arg:
+    description: 'Arguments to get_deps.py'
+    required: true
+
+runs:
+  using: "composite"
+  steps:
+    - name: Checkout pico-sdk for rp2040
+      if: contains(inputs.arg, 'rp2040') || contains(inputs.arg, 'raspberry_pi_pico')
+      uses: actions/checkout@v4
+      with:
+        repository: raspberrypi/pico-sdk
+        ref: develop
+        path: pico-sdk
+
+    - name: Linux dependencies
+      if: runner.os == 'Linux'
+      run: |
+        sudo apt install -y ninja-build
+      shell: bash
+
+    - name: Get Dependencies
+      run: |
+        python3 tools/get_deps.py ${{ inputs.arg }}
+        echo "PICO_SDK_PATH=${{ github.workspace }}/pico-sdk" >> $GITHUB_ENV
+      shell: bash
diff --git a/.github/actions/setup_toolchain/action.yml b/.github/actions/setup_toolchain/action.yml
new file mode 100644
index 000000000..19fe28b0c
--- /dev/null
+++ b/.github/actions/setup_toolchain/action.yml
@@ -0,0 +1,53 @@
+name: Setup Toolchain
+
+inputs:
+  toolchain:
+    description: 'Toolchain name'
+    required: true
+  toolchain_url:
+    description: 'Toolchain URL or version'
+    required: false
+
+outputs:
+  build_option:
+    description: 'Build option for the toolchain e.g --toolchain clang'
+    value: ${{ steps.set-toolchain-option.outputs.build_option }}
+
+runs:
+  using: "composite"
+  steps:
+    - name: Install ARM GCC
+      if: inputs.toolchain == 'arm-gcc'
+      uses: carlosperate/arm-none-eabi-gcc-action@v1
+      with:
+        release: '12.3.Rel1'
+
+    - name: Pull ESP-IDF docker
+      if: inputs.toolchain == 'esp-idf'
+      uses: ./.github/actions/setup_toolchain/espressif
+      with:
+        toolchain: ${{ inputs.toolchain }}
+        toolchain_url: ${{ inputs.toolchain_url }}
+
+    - name: Download Toolchain
+      if: >-
+        inputs.toolchain != 'arm-gcc' &&
+        inputs.toolchain != 'arm-iar' &&
+        inputs.toolchain != 'esp-idf'
+      uses: ./.github/actions/setup_toolchain/download
+      with:
+        toolchain: ${{ inputs.toolchain }}
+        toolchain_url: ${{ inputs.toolchain_url }}
+
+    - name: Set toolchain option
+      id: set-toolchain-option
+      run: |
+          BUILD_OPTION=""
+          if [[ "${{ inputs.toolchain }}" == *"clang"* ]]; then
+            BUILD_OPTION="--toolchain clang"
+          elif [[ "${{ inputs.toolchain }}" == "arm-iar" ]]; then
+            BUILD_OPTION="--toolchain iar"
+          fi
+          echo "build_option=$BUILD_OPTION"
+          echo "build_option=$BUILD_OPTION" >> $GITHUB_OUTPUT
+      shell: bash
diff --git a/.github/actions/setup_toolchain/download/action.yml b/.github/actions/setup_toolchain/download/action.yml
new file mode 100644
index 000000000..813197208
--- /dev/null
+++ b/.github/actions/setup_toolchain/download/action.yml
@@ -0,0 +1,39 @@
+name: Download Toolchain
+
+inputs:
+  toolchain:
+    description: 'Toolchain name'
+    required: true
+  toolchain_url:
+    description: 'Toolchain URL'
+    required: true
+
+runs:
+  using: "composite"
+  steps:
+    - name: Cache Toolchain
+      if: ${{ !startsWith(inputs.toolchain_url, 'https://github.com') }}
+      uses: actions/cache@v4
+      id: cache-toolchain-download
+      with:
+        path: ~/cache/${{ inputs.toolchain }}
+        key: ${{ runner.os }}-${{ inputs.toolchain }}-${{ inputs.toolchain_url }}
+
+    - name: Install Toolchain
+      if: steps.cache-toolchain-download.outputs.cache-hit != 'true'
+      run: |
+        mkdir -p ~/cache/${{ inputs.toolchain }}
+        wget --progress=dot:giga ${{ inputs.toolchain_url }} -O toolchain.tar.gz
+        if [[ ${{ inputs.toolchain }} == rx-gcc ]]; then
+          mv toolchain.tar.gz toolchain.run
+          chmod +x toolchain.run
+          ./toolchain.run -p ~/cache/${{ inputs.toolchain }}/gnurx -y
+        else
+          tar -C ~/cache/${{ inputs.toolchain }} -xaf toolchain.tar.gz
+        fi
+      shell: bash
+
+    - name: Set Toolchain Path
+      run: |
+        echo >> $GITHUB_PATH `echo ~/cache/${{ inputs.toolchain }}/*/bin`
+      shell: bash
diff --git a/.github/actions/setup_toolchain/espressif/action.yml b/.github/actions/setup_toolchain/espressif/action.yml
new file mode 100644
index 000000000..46da02911
--- /dev/null
+++ b/.github/actions/setup_toolchain/espressif/action.yml
@@ -0,0 +1,41 @@
+name: Setup ESP-IDF Toolchain
+
+inputs:
+  toolchain:
+    description: 'Toolchain name'
+    required: true
+  toolchain_url:
+    description: 'Toolchain URL or version'
+    required: true
+
+runs:
+  using: "composite"
+  steps:
+    - name: Set DOCKER_ESP_IDF
+      run: |
+        DOCKER_ESP_IDF=$HOME/cache/${{ inputs.toolchain }}/docker_image.tar
+        echo "DOCKER_ESP_IDF=$DOCKER_ESP_IDF" >> $GITHUB_ENV
+      shell: bash
+
+    - name: Cache Docker Image
+      uses: actions/cache@v4
+      id: cache-toolchain-espressif
+      with:
+        path: ${{ env.DOCKER_ESP_IDF }}
+        key: ${{ inputs.toolchain }}-${{ inputs.toolchain_url }}
+
+    - name: Pull and Save Docker Image
+      if: steps.cache-toolchain-espressif.outputs.cache-hit != 'true'
+      run: |
+        docker pull espressif/idf:${{ inputs.toolchain_url }}
+        mkdir -p $(dirname $DOCKER_ESP_IDF)
+        docker save -o $DOCKER_ESP_IDF espressif/idf:${{ inputs.toolchain_url }}
+        du -sh $DOCKER_ESP_IDF
+      shell: bash
+
+    - name: Load Docker Image
+      if: steps.cache-toolchain-espressif.outputs.cache-hit == 'true'
+      run: |
+        du -sh $DOCKER_ESP_IDF
+        docker load --input $DOCKER_ESP_IDF
+      shell: bash
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..fda7f0294
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,160 @@
+name: Build
+
+on:
+  workflow_dispatch:
+  push:
+    paths:
+      - 'src/**'
+      - 'examples/**'
+      - 'lib/**'
+      - 'hw/**'
+      - 'tools/get_deps.py'
+      - 'tools/build.py'
+      - '.github/actions/**'
+      - '.github/workflows/build.yml'
+      - '.github/workflows/build_util.yml'
+      - '.github/workflows/ci_set_matrix.py'
+  pull_request:
+    branches: [ master ]
+    paths:
+      - 'src/**'
+      - 'examples/**'
+      - 'lib/**'
+      - 'hw/**'
+      - 'tools/get_deps.py'
+      - 'tools/build.py'
+      - '.github/actions/**'
+      - '.github/workflows/build.yml'
+      - '.github/workflows/build_util.yml'
+      - '.github/workflows/ci_set_matrix.py'
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+jobs:
+  set-matrix:
+    runs-on: ubuntu-latest
+    outputs:
+      json: ${{ steps.set-matrix-json.outputs.matrix }}
+    steps:
+      - name: Checkout TinyUSB
+        uses: actions/checkout@v4
+
+      - name: Generate matrix json
+        id: set-matrix-json
+        run: |
+          MATRIX_JSON=$(python .github/workflows/ci_set_matrix.py)
+          echo "matrix=$MATRIX_JSON"
+          echo "matrix=$MATRIX_JSON" >> $GITHUB_OUTPUT
+
+  # ---------------------------------------
+  # Build CMake
+  # ---------------------------------------
+  cmake:
+    # if: false
+    needs: set-matrix
+    uses: ./.github/workflows/build_util.yml
+    strategy:
+      fail-fast: false
+      matrix:
+        toolchain:
+          # - 'arm-clang' is built by circle-ci
+          - 'aarch64-gcc'
+          - 'arm-gcc'
+          - 'msp430-gcc'
+          - 'riscv-gcc'
+    with:
+      build-system: 'cmake'
+      toolchain: ${{ matrix.toolchain }}
+      toolchain_url: ${{ fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain].toolchain_url }}
+      build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain].family) }}
+      one-per-family: ${{ github.event_name != 'pull_request' }}
+
+  # ---------------------------------------
+  # Build Make
+  # ---------------------------------------
+  make:
+    # if: false
+    needs: set-matrix
+    uses: ./.github/workflows/build_util.yml
+    strategy:
+      fail-fast: false
+      matrix:
+        toolchain:
+          # 'arm-clang' is built by circle-ci
+          - 'aarch64-gcc'
+          - 'arm-gcc'
+          - 'msp430-gcc'
+          - 'riscv-gcc'
+          - 'rx-gcc'
+    with:
+      build-system: 'make'
+      toolchain: ${{ matrix.toolchain }}
+      toolchain_url: ${{ fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain].toolchain_url }}
+      build-args: ${{ toJSON(fromJSON(needs.set-matrix.outputs.json)[matrix.toolchain].family) }}
+      one-per-family: ${{ github.event_name != 'pull_request' }}
+
+  # ---------------------------------------
+  # Build Make on Windows/MacOS
+  # ---------------------------------------
+  make-os:
+    uses: ./.github/workflows/build_util.yml
+    strategy:
+      fail-fast: false
+      matrix:
+        os: [windows-latest, macos-latest]
+    with:
+      os: ${{ matrix.os }}
+      build-system: 'make'
+      toolchain: 'arm-gcc'
+      build-args: '["stm32h7"]'
+      one-per-family: true
+
+  # ---------------------------------------
+  # Build Espressif
+  # ---------------------------------------
+  espressif:
+    # if: false
+    uses: ./.github/workflows/build_util.yml
+    strategy:
+      fail-fast: false
+      matrix:
+        board:
+          - 'espressif_kaluga_1'
+          - 'espressif_s3_devkitm'
+    with:
+      build-system: 'cmake'
+      toolchain: 'esp-idf'
+      toolchain_url: 'v5.1.1'
+      build-args: '["-b${{ matrix.board }}"]'
+
+  # ---------------------------------------
+  # Build IAR on HFP self-hosted
+  # ---------------------------------------
+  arm-iar:
+    # if: false
+    if: github.repository_owner == 'hathach'
+    needs: set-matrix
+    runs-on: [self-hosted, Linux, X64, hifiphile]
+    env:
+      BUILD_ARGS: ${{ join(fromJSON(needs.set-matrix.outputs.json)['arm-iar'].family, ' ') }}
+    steps:
+      - name: Clean workspace
+        run: |
+          echo "Cleaning up previous run"
+          rm -rf "${{ github.workspace }}"
+          mkdir -p "${{ github.workspace }}"
+
+      - name: Checkout TinyUSB
+        uses: actions/checkout@v4
+
+      - name: Get Dependencies
+        run: python3 tools/get_deps.py $BUILD_ARGS
+
+      - name: Build
+        run: python3 tools/build.py --one-per-family --toolchain iar $BUILD_ARGS
+
+      - name: Test on actual hardware (hardware in the loop)
+        if: github.event_name == 'pull_request'
+        run: |
+          python3 test/hil/hil_test.py hfp.json
diff --git a/.github/workflows/build_aarch64.yml b/.github/workflows/build_aarch64.yml
deleted file mode 100644
index e5dbf9494..000000000
--- a/.github/workflows/build_aarch64.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-name: Build AArch64
-
-on:
-  workflow_dispatch:
-  push:
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - '.github/workflows/build_aarch64.yml'
-  pull_request:
-    branches: [ master ]
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - '.github/workflows/build_aarch64.yml'
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  # ---------------------------------------
-  # Build AARCH64 family
-  # ---------------------------------------
-  build-arm:
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        family:
-        # Alphabetical order
-        - 'broadcom_64bit'
-    steps:
-    - name: Setup Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-
-    - name: Checkout TinyUSB
-      uses: actions/checkout@v4
-
-    - name: Set Toolchain URL
-      run: echo >> $GITHUB_ENV TOOLCHAIN_URL=https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz
-
-    - name: Cache Toolchain
-      uses: actions/cache@v3
-      id: cache-toolchain
-      with:
-        path: ~/cache/
-        key: ${{ runner.os }}-21-11-02-${{ env.TOOLCHAIN_URL }}
-
-    - name: Install Toolchain
-      if: steps.cache-toolchain.outputs.cache-hit != 'true'
-      run: |
-        mkdir -p ~/cache/toolchain
-        wget --progress=dot:mega $TOOLCHAIN_URL -O toolchain.tar.gz
-        tar -C ~/cache/toolchain -xaf toolchain.tar.gz
-
-    - name: Set Toolchain Path
-      run: echo >> $GITHUB_PATH `echo ~/cache/toolchain/*/bin`
-
-    - name: Get Dependencies
-      run: python3 tools/get_deps.py ${{ matrix.family }}
-
-    - name: Build
-      run: python3 tools/build_make.py ${{ matrix.family }}
diff --git a/.github/workflows/build_arm.yml b/.github/workflows/build_arm.yml
deleted file mode 100644
index 9f3270c91..000000000
--- a/.github/workflows/build_arm.yml
+++ /dev/null
@@ -1,65 +0,0 @@
-name: Build ARM
-
-on:
-  workflow_dispatch:
-  push:
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/build_arm.yml'
-  pull_request:
-    branches: [ master ]
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/build_arm.yml'
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  # ---------------------------------------
-  # Build ARM family
-  # ---------------------------------------
-  build-arm:
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        family:
-        # Alphabetical order
-        - 'broadcom_32bit'
-        - 'kinetis_k32l2'
-        - 'lpc11 lpc13 lpc15'
-        - 'lpc51'
-        - 'mm32 msp432e4'
-        - 'samd11 same5x saml2x'
-        - 'stm32f2 stm32f3'
-        - 'stm32l0 stm32wb'
-        - 'tm4c123 xmc4000'
-    steps:
-    - name: Setup Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-
-    - name: Install ARM GCC
-      uses: carlosperate/arm-none-eabi-gcc-action@v1
-      with:
-        release: '11.2-2022.02'
-
-    - name: Checkout TinyUSB
-      uses: actions/checkout@v4
-
-    - name: Get Dependencies
-      run: python3 tools/get_deps.py ${{ matrix.family }}
-
-    - name: Build
-      run: python3 tools/build_make.py ${{ matrix.family }}
diff --git a/.github/workflows/build_esp.yml b/.github/workflows/build_esp.yml
deleted file mode 100644
index d283c6535..000000000
--- a/.github/workflows/build_esp.yml
+++ /dev/null
@@ -1,114 +0,0 @@
-name: Build ESP
-
-on:
-  workflow_dispatch:
-  push:
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'test/hil/**'
-      - '.github/workflows/build_esp.yml'
-  pull_request:
-    branches: [ master ]
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'test/hil/**'
-      - '.github/workflows/build_esp.yml'
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  build-esp:
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        board:
-        # ESP32-S2
-        - 'espressif_kaluga_1'
-        # ESP32-S3
-        - 'espressif_s3_devkitc'
-    steps:
-    - name: Setup Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-
-    - name: Pull ESP-IDF docker
-      run: docker pull espressif/idf:latest
-
-    - name: Checkout TinyUSB
-      uses: actions/checkout@v4
-
-    - name: Build
-      run: docker run --rm -v $PWD:/project -w /project espressif/idf:v5.1.1 python3 tools/build_esp32.py ${{ matrix.board }}
-
-    - name: Upload Artifacts for Hardware Testing
-      if: matrix.board == 'espressif_s3_devkitc' && github.repository_owner == 'hathach'
-      uses: actions/upload-artifact@v4
-      with:
-        name: ${{ matrix.board }}
-        path: |
-          cmake-build/cmake-build-${{ matrix.board }}/*/*/bootloader/bootloader.bin
-          cmake-build/cmake-build-${{ matrix.board }}/*/*/*.bin
-          cmake-build/cmake-build-${{ matrix.board }}/*/*/partition_table/partition-table.bin
-          cmake-build/cmake-build-${{ matrix.board }}/*/*/config.env
-          cmake-build/cmake-build-${{ matrix.board }}/*/*/flash_args
-
-  # ---------------------------------------
-  # Hardware in the loop (HIL)
-  # Current self-hosted instance is running on an RPI4. For attached hardware checkout hil_pi4.json
-  # ---------------------------------------
-  hil-test:
-    # run only with hathach's commit due to limited resource on RPI4
-    if: github.repository_owner == 'hathach'
-    needs: build-esp
-    runs-on: [self-hosted, esp32s3, hardware-in-the-loop]
-    strategy:
-      fail-fast: false
-      matrix:
-        board:
-          - 'espressif_s3_devkitc'
-    steps:
-      - name: Clean workspace
-        run: |
-          echo "Cleaning up previous run"
-          rm -rf "${{ github.workspace }}"
-          mkdir -p "${{ github.workspace }}"
-
-        # USB bus on rpi4 is not stable, reset it before testing
-      - name: Reset USB bus
-        run: |
-          lsusb
-          lsusb -t
-          # reset VIA Labs 2.0 hub
-          sudo usbreset 001/002
-
-          # legacy code
-          #for port in $(lspci | grep USB | cut -d' ' -f1); do
-          #    echo -n "0000:${port}"| sudo tee /sys/bus/pci/drivers/xhci_hcd/unbind;
-          #    sleep 0.1;
-          #    echo -n "0000:${port}" | sudo tee /sys/bus/pci/drivers/xhci_hcd/bind;
-          #done
-
-      - name: Checkout test/hil
-        uses: actions/checkout@v4
-        with:
-          sparse-checkout: test/hil
-
-      - name: Download Artifacts
-        uses: actions/download-artifact@v4
-        with:
-          name: ${{ matrix.board }}
-          path: cmake-build/cmake-build-${{ matrix.board }}
-
-      - name: Test on actual hardware
-        run: |
-          python3 test/hil/hil_test.py --board ${{ matrix.board }} hil_pi4.json
diff --git a/.github/workflows/build_iar.yml b/.github/workflows/build_iar.yml
deleted file mode 100644
index 499349904..000000000
--- a/.github/workflows/build_iar.yml
+++ /dev/null
@@ -1,59 +0,0 @@
-name: Build IAR
-
-on:
-  workflow_dispatch:
-  push:
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - 'test/hil/**'
-      - '.github/workflows/build_iar.yml'
-  pull_request:
-    branches: [ master ]
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - 'test/hil/**'
-      - '.github/workflows/build_iar.yml'
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  cmake:
-    if: github.repository_owner == 'hathach'
-    runs-on: [self-hosted, Linux, X64, hifiphile]
-    strategy:
-      fail-fast: false
-      matrix:
-        family:
-          # Alphabetical order
-          # Note: bundle multiple families into a matrix since there is only one self-hosted instance can
-          # run IAR build. Too many matrix can hurt due to setup/teardown overhead.
-          - 'lpc43 stm32f0 stm32f1 stm32f7 stm32g0 stm32g4 stm32l4'
-    steps:
-      - name: Clean workspace
-        run: |
-          echo "Cleaning up previous run"
-          rm -rf "${{ github.workspace }}"
-          mkdir -p "${{ github.workspace }}"
-
-      - name: Checkout TinyUSB
-        uses: actions/checkout@v4
-
-      - name: Get Dependencies
-        run: python3 tools/get_deps.py ${{ matrix.family }}
-
-      - name: Build
-        run: python3 tools/build_cmake.py ${{ matrix.family }} -DTOOLCHAIN=iar -DCMAKE_BUILD_TYPE=MinSizeRel
-
-      - name: Test on actual hardware (hardware in the loop)
-        run: |
-          python3 test/hil/hil_test.py hil_hfp.json
diff --git a/.github/workflows/build_msp430.yml b/.github/workflows/build_msp430.yml
deleted file mode 100644
index f913df913..000000000
--- a/.github/workflows/build_msp430.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-name: Build MSP430
-
-on:
-  workflow_dispatch:
-  push:
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/build_msp430.yml'
-  pull_request:
-    branches: [ master ]
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/build_msp430.yml'
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  build-msp430:
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        family:
-        # Alphabetical order
-        - 'msp430'
-
-    steps:
-    - name: Setup Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-
-    - name: Checkout TinyUSB
-      uses: actions/checkout@v4
-
-    - name: Set Toolchain URL
-      run: echo >> $GITHUB_ENV TOOLCHAIN_URL=http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/9_2_0_0/export/msp430-gcc-9.2.0.50_linux64.tar.bz2
-
-    - name: Cache Toolchain
-      uses: actions/cache@v3
-      id: cache-toolchain
-      with:
-        path: ~/cache/
-        key: ${{ runner.os }}-21-03-04-${{ env.TOOLCHAIN_URL }}
-
-    - name: Install Toolchain
-      if: steps.cache-toolchain.outputs.cache-hit != 'true'
-      run: |
-        mkdir -p ~/cache/toolchain
-        wget --progress=dot:mega $TOOLCHAIN_URL -O toolchain.tar.bz2
-        tar -C ~/cache/toolchain -xaf toolchain.tar.bz2
-
-    - name: Set Toolchain Path
-      run: echo >> $GITHUB_PATH `echo ~/cache/toolchain/*/bin`
-
-    - name: Get Dependencies
-      run: python3 tools/get_deps.py ${{ matrix.family }}
-
-    - name: Build
-      run: python3 tools/build_make.py ${{ matrix.family }}
diff --git a/.github/workflows/build_renesas.yml b/.github/workflows/build_renesas.yml
deleted file mode 100644
index ec06c9426..000000000
--- a/.github/workflows/build_renesas.yml
+++ /dev/null
@@ -1,70 +0,0 @@
-name: Build Renesas
-
-on:
-  workflow_dispatch:
-  push:
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/build_renesas.yml'
-  pull_request:
-    branches: [ master ]
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/build_renesas.yml'
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  build-rx:
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        family:
-        # Alphabetical order
-        - 'rx'
-    steps:
-    - name: Setup Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-
-    - name: Checkout TinyUSB
-      uses: actions/checkout@v4
-
-    - name: Set Toolchain URL
-      run: echo >> $GITHUB_ENV TOOLCHAIN_URL=http://gcc-renesas.com/downloads/get.php?f=rx/8.3.0.202004-gnurx/gcc-8.3.0.202004-GNURX-ELF.run
-
-    - name: Cache Toolchain
-      uses: actions/cache@v3
-      id: cache-toolchain
-      with:
-        path: ~/cache/
-        key: ${{ runner.os }}-21-03-30-${{ env.TOOLCHAIN_URL }}
-
-    - name: Install Toolchain
-      if: steps.cache-toolchain.outputs.cache-hit != 'true'
-      run: |
-        mkdir -p ~/cache/toolchain/gnurx
-        wget --progress=dot:mega $TOOLCHAIN_URL -O toolchain.run
-        chmod +x toolchain.run
-        ./toolchain.run -p ~/cache/toolchain/gnurx -y
-
-    - name: Set Toolchain Path
-      run: echo >> $GITHUB_PATH `echo ~/cache/toolchain/*/bin`
-
-    - name: Get Dependencies
-      run: python3 tools/get_deps.py ${{ matrix.family }}
-
-    - name: Build
-      run: python3 tools/build_make.py ${{ matrix.family }}
diff --git a/.github/workflows/build_riscv.yml b/.github/workflows/build_riscv.yml
deleted file mode 100644
index e891a3a51..000000000
--- a/.github/workflows/build_riscv.yml
+++ /dev/null
@@ -1,71 +0,0 @@
-name: Build RISC-V
-
-on:
-  workflow_dispatch:
-  push:
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/build_riscv.yml'
-  pull_request:
-    branches: [ master ]
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/build_riscv.yml'
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  build-riscv:
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        family:
-        # Alphabetical order
-        - 'ch32v307'
-        - 'fomu'
-        - 'gd32vf103'
-    steps:
-    - name: Setup Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-
-    - name: Checkout TinyUSB
-      uses: actions/checkout@v4
-
-    - name: Set Toolchain URL
-      run: echo >> $GITHUB_ENV TOOLCHAIN_URL=https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack/releases/download/v10.1.0-1.1/xpack-riscv-none-embed-gcc-10.1.0-1.1-linux-x64.tar.gz
-
-    - name: Cache Toolchain
-      uses: actions/cache@v3
-      id: cache-toolchain
-      with:
-        path: ~/cache/
-        key: ${{ runner.os }}-21-03-04-${{ env.TOOLCHAIN_URL }}
-
-    - name: Install Toolchain
-      if: steps.cache-toolchain.outputs.cache-hit != 'true'
-      run: |
-        mkdir -p ~/cache/toolchain
-        wget --progress=dot:mega $TOOLCHAIN_URL -O toolchain.tar.gz
-        tar -C ~/cache/toolchain -xaf toolchain.tar.gz
-
-    - name: Set Toolchain Path
-      run: echo >> $GITHUB_PATH `echo ~/cache/toolchain/*/bin`
-
-    - name: Get Dependencies
-      run: python3 tools/get_deps.py ${{ matrix.family }}
-
-    - name: Build
-      run: python3 tools/build_make.py ${{ matrix.family }}
diff --git a/.github/workflows/build_util.yml b/.github/workflows/build_util.yml
new file mode 100644
index 000000000..49a9feabd
--- /dev/null
+++ b/.github/workflows/build_util.yml
@@ -0,0 +1,68 @@
+name: Reusable build util
+
+on:
+  workflow_call:
+    inputs:
+      build-system:
+        required: true
+        type: string
+      toolchain:
+        required: true
+        type: string
+      toolchain_url:
+        required: false
+        type: string
+      build-args:
+        required: true
+        type: string
+      one-per-family:
+        required: false
+        default: false
+        type: boolean
+      os:
+        required: false
+        type: string
+        default: 'ubuntu-latest'
+
+jobs:
+  family:
+    runs-on: ${{ inputs.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        arg: ${{ fromJSON(inputs.build-args) }}
+    steps:
+      - name: Checkout TinyUSB
+        uses: actions/checkout@v4
+
+      - name: Setup Toolchain
+        id: setup-toolchain
+        uses: ./.github/actions/setup_toolchain
+        with:
+          toolchain: ${{ inputs.toolchain }}
+          toolchain_url: ${{ inputs.toolchain_url }}
+
+      - name: Get Dependencies
+        uses: ./.github/actions/get_deps
+        with:
+          arg: ${{ matrix.arg }}
+
+      - name: Set build one-per-family option
+        id: set-one-per-family
+        run: |
+          if [[ "${{ inputs.one-per-family }}" == "true" ]]; then
+            BUILD_OPTION="--one-per-family"
+          fi
+          echo "build_option=$BUILD_OPTION"
+          echo "build_option=$BUILD_OPTION" >> $GITHUB_OUTPUT
+        shell: bash
+
+      - name: Build
+        if: inputs.toolchain != 'esp-idf'
+        run: |
+          python tools/build.py -s ${{ inputs.build-system }} ${{ steps.setup-toolchain.outputs.build_option }} ${{ steps.set-one-per-family.outputs.build_option }} ${{ matrix.arg }}
+
+      - name: Build using ESP-IDF docker
+        if: inputs.toolchain == 'esp-idf'
+        run: |
+          docker run --rm -v $PWD:/project -w /project espressif/idf:${{ inputs.toolchain_url }} python3 tools/build.py ${{ matrix.arg }}
diff --git a/.github/workflows/build_win_mac.yml b/.github/workflows/build_win_mac.yml
deleted file mode 100644
index b33b5b593..000000000
--- a/.github/workflows/build_win_mac.yml
+++ /dev/null
@@ -1,54 +0,0 @@
-name: Build Windows/MacOS
-
-on:
-  workflow_dispatch:
-  push:
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - '.github/workflows/build_win_mac.yml'
-  pull_request:
-    branches: [ master ]
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - '.github/workflows/build_win_mac.yml'
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  # ---------------------------------------
-  # Build ARM family
-  # ---------------------------------------
-  build-arm:
-    strategy:
-      fail-fast: false
-      matrix:
-        os: [windows-latest, macos-latest]
-    runs-on: ${{ matrix.os }}
-
-    steps:
-    - name: Setup Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-
-    - name: Install ARM GCC
-      uses: carlosperate/arm-none-eabi-gcc-action@v1
-      with:
-        release: '10.3-2021.10'
-
-    - name: Checkout TinyUSB
-      uses: actions/checkout@v4
-
-    - name: Get Dependencies
-      run: python3 tools/get_deps.py stm32f4
-
-    - name: Build
-      run: python3 tools/build_make.py stm32f4 stm32f411disco
diff --git a/.github/workflows/ci_set_matrix.py b/.github/workflows/ci_set_matrix.py
new file mode 100644
index 000000000..2a00e5413
--- /dev/null
+++ b/.github/workflows/ci_set_matrix.py
@@ -0,0 +1,64 @@
+import json
+
+# toolchain, url
+toolchain_list = {
+    "aarch64-gcc": "https://developer.arm.com/-/media/Files/downloads/gnu-a/10.3-2021.07/binrel/gcc-arm-10.3-2021.07-x86_64-aarch64-none-elf.tar.xz",
+    "arm-clang": "https://github.com/ARM-software/LLVM-embedded-toolchain-for-Arm/releases/download/release-17.0.1/LLVMEmbeddedToolchainForArm-17.0.1-Linux-x86_64.tar.xz",
+    "arm-iar": "",
+    "arm-gcc": "",
+    "msp430-gcc": "http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/9_2_0_0/export/msp430-gcc-9.2.0.50_linux64.tar.bz2",
+    "riscv-gcc": "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v13.2.0-2/xpack-riscv-none-elf-gcc-13.2.0-2-linux-x64.tar.gz",
+    "rx-gcc": "http://gcc-renesas.com/downloads/get.php?f=rx/8.3.0.202004-gnurx/gcc-8.3.0.202004-GNURX-ELF.run",
+}
+
+# family: [supported toolchain]
+family_list = {
+    "broadcom_32bit": ["arm-gcc"],
+    "broadcom_64bit": ["aarch64-gcc"],
+    "ch32v10x ch32v20x ch32v307 fomu gd32vf103": ["riscv-gcc"],
+    "da1469x": ["arm-gcc"],
+    "imxrt": ["arm-gcc", "arm-clang"],
+    "kinetis_k kinetis_kl kinetis_k32l2": ["arm-gcc", "arm-clang"],
+    "lpc11 lpc13 lpc15": ["arm-gcc", "arm-clang"],
+    "lpc17 lpc18 lpc40 lpc43": ["arm-gcc", "arm-clang"],
+    "lpc51 lpc54 lpc55": ["arm-gcc", "arm-clang"],
+    "mcx": ["arm-gcc"],
+    "mm32": ["arm-gcc"],
+    "msp430": ["msp430-gcc"],
+    "msp432e4 tm4c": ["arm-gcc"],
+    "nrf": ["arm-gcc", "arm-clang"],
+    "ra": ["arm-gcc"],
+    "rp2040": ["arm-gcc"],
+    "rx": ["rx-gcc"],
+    "samd11 samd21 saml2x": ["arm-gcc", "arm-clang"],
+    "samd5x_e5x samg": ["arm-gcc", "arm-clang"],
+    "stm32f0 stm32f1 stm32f2 stm32f3": ["arm-gcc", "arm-clang", "arm-iar"],
+    "stm32f4": ["arm-gcc", "arm-clang", "arm-iar"],
+    "stm32f7": ["arm-gcc", "arm-clang", "arm-iar"],
+    "stm32g0 stm32g4 stm32h5": ["arm-gcc", "arm-clang", "arm-iar"],
+    "stm32h7": ["arm-gcc", "arm-clang", "arm-iar"],
+    "stm32l0 stm32l4 stm32u5 stm32wb": ["arm-gcc", "arm-clang", "arm-iar"],
+    "xmc4000": ["arm-gcc"],
+}
+
+
+def set_matrix_json():
+    matrix = {}
+    for toolchain in toolchain_list.keys():
+        filtered_families = [family for family, supported_toolchain in family_list.items() if
+                             toolchain in supported_toolchain]
+
+        # always add board in hfp.json for arm-iar
+        if toolchain == 'arm-iar':
+            with open('test/hil/hfp.json') as f:
+                hfp_data = json.load(f)
+            hfp_boards = [f"-b{board['name']}" for board in hfp_data['boards']]
+            filtered_families = filtered_families + hfp_boards
+
+        matrix[toolchain] = {"family": filtered_families, "toolchain_url": toolchain_list[toolchain]}
+
+    print(json.dumps(matrix))
+
+
+if __name__ == '__main__':
+    set_matrix_json()
diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml
index 5dc0f2764..d7f1fc066 100644
--- a/.github/workflows/cifuzz.yml
+++ b/.github/workflows/cifuzz.yml
@@ -20,12 +20,14 @@ jobs:
      with:
        oss-fuzz-project-name: 'tinyusb'
        language: c++
+
    - name: Run Fuzzers
      uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
      with:
        oss-fuzz-project-name: 'tinyusb'
        language: c++
-       fuzz-seconds: 600
+       fuzz-seconds: 400
+
    - name: Upload Crash
      uses: actions/upload-artifact@v4
      if: failure() && steps.build.outcome == 'success'
diff --git a/.github/workflows/cmake_arm.yml b/.github/workflows/cmake_arm.yml
deleted file mode 100644
index 1e5f61e0e..000000000
--- a/.github/workflows/cmake_arm.yml
+++ /dev/null
@@ -1,169 +0,0 @@
-name: CMake ARM
-
-on:
-  workflow_dispatch:
-  push:
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'test/hil/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/cmake_arm.yml'
-  pull_request:
-    branches: [ master ]
-    paths:
-      - 'src/**'
-      - 'examples/**'
-      - 'lib/**'
-      - 'hw/**'
-      - 'test/hil/**'
-      - 'tools/get_deps.py'
-      - '.github/workflows/cmake_arm.yml'
-
-concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
-  cancel-in-progress: true
-
-jobs:
-  # ---------------------------------------
-  # Build ARM family
-  # ---------------------------------------
-  build-arm:
-    runs-on: ubuntu-latest
-    strategy:
-      fail-fast: false
-      matrix:
-        family:
-          # Alphabetical order
-          - 'imxrt'
-          - 'kinetis_k kinetis_kl'
-          - 'lpc17 lpc18 lpc40 lpc43'
-          - 'lpc54 lpc55'
-          - 'mcx'
-          - 'nrf'
-          - 'ra'
-          - 'rp2040'
-          - 'samd21'
-          - 'samd51'
-          - 'stm32f0'
-          - 'stm32f1'
-          - 'stm32f4'
-          - 'stm32f7'
-          - 'stm32g0'
-          - 'stm32g4'
-          - 'stm32h5'
-          - 'stm32h7'
-          - 'stm32l4'
-          - 'stm32u5'
-    steps:
-    - name: Setup Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-
-    - name: Install ARM GCC
-      uses: carlosperate/arm-none-eabi-gcc-action@v1
-      with:
-        release: '11.2-2022.02'
-
-    - name: Install Ninja
-      run: sudo apt install -y ninja-build
-
-    - name: Checkout TinyUSB
-      uses: actions/checkout@v4
-
-    - name: Checkout pico-sdk for rp2040
-      if: matrix.family == 'rp2040'
-      uses: actions/checkout@v4
-      with:
-        repository: raspberrypi/pico-sdk
-        ref: develop
-        path: pico-sdk
-
-    - name: Get Dependencies
-      run: python3 tools/get_deps.py ${{ matrix.family }}
-
-    - name: Build
-      run: python tools/build_cmake.py ${{ matrix.family }} -DCMAKE_BUILD_TYPE=MinSizeRel
-      env:
-        # for rp2040, there is no harm if defined for other families
-        PICO_SDK_PATH: ${{ github.workspace }}/pico-sdk
-
-    - name: Upload Artifacts for Hardware Testing (rp2040)
-      if: matrix.family == 'rp2040' && github.repository_owner == 'hathach'
-      uses: actions/upload-artifact@v4
-      with:
-        name: raspberry_pi_pico
-        path: |
-          cmake-build/cmake-build-raspberry_pi_pico/*/*/*.elf
-
-    - name: Upload Artifacts for Hardware Testing (nRF)
-      if: matrix.family == 'nrf' && github.repository_owner == 'hathach'
-      uses: actions/upload-artifact@v4
-      with:
-        name: feather_nrf52840_express
-        path: |
-          cmake-build/cmake-build-feather_nrf52840_express/*/*/*.elf
-
-    - name: Upload Artifacts for Hardware Testing (samd51)
-      if: matrix.family == 'samd51' && github.repository_owner == 'hathach'
-      uses: actions/upload-artifact@v4
-      with:
-        name: itsybitsy_m4
-        path: |
-          cmake-build/cmake-build-itsybitsy_m4/*/*/*.bin
-
-  # ---------------------------------------
-  # Hardware in the loop (HIL)
-  # Current self-hosted instance is running on an RPI4. For attached hardware checkout hil_pi4.json
-  # ---------------------------------------
-  hil-test:
-    # run only with hathach's commit due to limited resource on RPI4
-    if: github.repository_owner == 'hathach'
-    needs: build-arm
-    runs-on: [self-hosted, rp2040, nrf52840, hardware-in-the-loop]
-    strategy:
-      fail-fast: false
-      matrix:
-        board:
-          - 'feather_nrf52840_express'
-          - 'itsybitsy_m4'
-          - 'raspberry_pi_pico'
-    steps:
-      - name: Clean workspace
-        run: |
-          echo "Cleaning up previous run"
-          rm -rf "${{ github.workspace }}"
-          mkdir -p "${{ github.workspace }}"
-
-        # USB bus on rpi4 is not stable, reset it before testing
-      - name: Reset USB bus
-        run: |
-          lsusb
-          lsusb -t
-          # reset VIA Labs 2.0 hub
-          sudo usbreset 001/002
-
-          # legacy code
-          #for port in $(lspci | grep USB | cut -d' ' -f1); do
-          #    echo -n "0000:${port}"| sudo tee /sys/bus/pci/drivers/xhci_hcd/unbind;
-          #    sleep 0.1;
-          #    echo -n "0000:${port}" | sudo tee /sys/bus/pci/drivers/xhci_hcd/bind;
-          #done
-
-      - name: Checkout test/hil
-        uses: actions/checkout@v4
-        with:
-          sparse-checkout: test/hil
-
-      - name: Download Artifacts
-        uses: actions/download-artifact@v4
-        with:
-          name: ${{ matrix.board }}
-          path: cmake-build/cmake-build-${{ matrix.board }}
-
-      - name: Test on actual hardware
-        run: |
-          python3 test/hil/hil_test.py --board ${{ matrix.board }} hil_pi4.json
diff --git a/.github/workflows/codeql-buildscript.sh b/.github/workflows/codeql-buildscript.sh
index 35e029922..272b55d22 100644
--- a/.github/workflows/codeql-buildscript.sh
+++ b/.github/workflows/codeql-buildscript.sh
@@ -1,5 +1,6 @@
 #!/usr/bin/env bash
 
 FAMILY=stm32l4
+pip install click
 python3 tools/get_deps.py $FAMILY
-python3 tools/build_make.py $FAMILY
+python3 tools/build.py -s make $FAMILY
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
index 1f7b60b9c..be4c2dd87 100644
--- a/.github/workflows/codeql.yml
+++ b/.github/workflows/codeql.yml
@@ -59,10 +59,10 @@ jobs:
     - name: Checkout repository
       uses: actions/checkout@v4
 
-    - name: Install ARM GCC
-      uses: carlosperate/arm-none-eabi-gcc-action@v1
+    - name: Setup Toolchain
+      uses: ./.github/actions/setup_toolchain
       with:
-        release: '11.2-2022.02'
+        toolchain: 'arm-gcc'
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
diff --git a/.github/workflows/hil_test.yml b/.github/workflows/hil_test.yml
new file mode 100644
index 000000000..a4573faf7
--- /dev/null
+++ b/.github/workflows/hil_test.yml
@@ -0,0 +1,161 @@
+name: Hardware Test
+
+on:
+  workflow_dispatch:
+  pull_request:
+    branches: [ master ]
+    paths:
+      - 'src/**'
+      - 'examples/**'
+      - 'lib/**'
+      - 'hw/**'
+      - 'test/hil/**'
+      - 'tools/get_deps.py'
+      - '.github/actions/**'
+      - '.github/workflows/hil_test.yml'
+concurrency:
+  group: ${{ github.workflow }}-${{ github.ref }}
+  cancel-in-progress: true
+
+env:
+  HIL_JSON: test/hil/rpi.json
+
+jobs:
+  # ---------------------------------------
+  # Build Non Espressif
+  # ---------------------------------------
+  build:
+    if: github.repository_owner == 'hathach'
+    runs-on: ubuntu-latest
+    outputs:
+      BOARDS_LIST: ${{ steps.parse_hil_json.outputs.BOARDS_LIST }}
+    steps:
+      - name: Checkout TinyUSB
+        uses: actions/checkout@v4
+
+      - name: Parse HIL json
+        id: parse_hil_json
+        run: |
+          sudo apt install -y jq
+
+          # Non-Espresif boards
+          BOARDS_LIST=$(jq -r '.boards[] | select(.flasher != "esptool") | "-b " + .name' ${{ env.HIL_JSON }} | tr '\n' ' ')
+          echo "BOARDS_LIST=$BOARDS_LIST"
+          echo "BOARDS_LIST=$BOARDS_LIST" >> $GITHUB_ENV
+          echo "BOARDS_LIST=$BOARDS_LIST" >> $GITHUB_OUTPUT
+
+      - name: Setup Toolchain
+        uses: ./.github/actions/setup_toolchain
+        with:
+          toolchain: 'arm-gcc'
+
+      - name: Get Dependencies
+        uses: ./.github/actions/get_deps
+        with:
+          arg: ${{ env.BOARDS_LIST }}
+
+      - name: Build
+        run: python tools/build.py $BOARDS_LIST
+
+      - name: Upload Artifacts for Hardware Testing
+        uses: actions/upload-artifact@v4
+        with:
+          name: hil_rpi
+          path: |
+            cmake-build/cmake-build-*/*/*/*.elf
+            cmake-build/cmake-build-*/*/*/*.bin
+
+  # ---------------------------------------
+  # Build Espressif (skipped since CP210x cause USB bus issue)
+  # cp210x ttyUSB0: usb_serial_generic_write_bulk_callback - nonzero urb status: -71
+  # ---------------------------------------
+  build-esp:
+    if: false
+    runs-on: ubuntu-latest
+    outputs:
+      BOARDS_LIST: ${{ steps.parse_hil_json.outputs.BOARDS_LIST }}
+    steps:
+      - name: Checkout TinyUSB
+        uses: actions/checkout@v4
+
+      - name: Parse HIL json
+        id: parse_hil_json
+        run: |
+          sudo apt install -y jq
+          # Espressif boards
+          BOARDS_LIST=$(jq -r '.boards[] | select(.flasher == "esptool") | "-b " + .name' ${{ env.HIL_JSON }} | tr '\n' ' ')
+          echo "BOARDS_LIST=$BOARDS_LIST"
+          echo "BOARDS_LIST=$BOARDS_LIST" >> $GITHUB_ENV
+          echo "BOARDS_LIST=$BOARDS_LIST" >> $GITHUB_OUTPUT
+
+      - name: Setup ESP-IDF
+        if: env.BOARDS_LIST != ''
+        uses: ./.github/actions/setup_toolchain
+        with:
+          toolchain: 'esp-idf'
+          toolchain_url: 'v5.1.1'
+
+      - name: Get Dependencies
+        uses: ./.github/actions/get_deps
+        with:
+          arg: ${{ env.BOARDS_LIST }}
+
+      - name: Build Espressif
+        if: env.BOARDS_LIST != ''
+        run: docker run --rm -v $PWD:/project -w /project espressif/idf:v5.1.1 python3 tools/build.py $BOARDS_LIST
+
+      - name: Upload Artifacts for Hardware Testing
+        uses: actions/upload-artifact@v4
+        with:
+          name: hil_rpi_esp
+          path: |
+            cmake-build/cmake-build-*/*/*/*.bin
+            cmake-build/cmake-build-*/*/*/bootloader/bootloader.bin
+            cmake-build/cmake-build-*/*/*/partition_table/partition-table.bin
+            cmake-build/cmake-build-*/*/*/config.env
+            cmake-build/cmake-build-*/*/*/flash_args
+
+  # ---------------------------------------
+  # Hardware in the loop (HIL)
+  # self-hosted running on an RPI. For attached hardware checkout test/hil/rpi.json
+  # ---------------------------------------
+  hil-rpi:
+    if: github.repository_owner == 'hathach'
+    needs:
+      - build
+      #- build-esp
+    runs-on: [self-hosted, ARM64, rpi, hardware-in-the-loop]
+    env:
+      BOARDS_LIST: "${{ needs.build-esp.outputs.BOARDS_LIST }} ${{ needs.build.outputs.BOARDS_LIST }}"
+    steps:
+      - name: Clean workspace
+        run: |
+          echo "Cleaning up previous run"
+          rm -rf "${{ github.workspace }}"
+          mkdir -p "${{ github.workspace }}"
+
+        # USB bus on rpi is not stable, reset it before testing
+#      - name: Reset USB bus
+#        run: |
+#          echo "1-2" | sudo tee /sys/bus/usb/drivers/usb/unbind
+#          sleep 5
+#          echo "1-2" | sudo tee /sys/bus/usb/drivers/usb/bind
+
+      - name: Checkout TinyUSB
+        uses: actions/checkout@v4
+        with:
+          sparse-checkout: test/hil
+
+      - name: Download Artifacts
+        uses: actions/download-artifact@v4
+        with:
+          path: cmake-build
+          merge-multiple: true
+
+      - name: Test on actual hardware
+        run: |
+          echo "BOARDS_LIST=$BOARDS_LIST"
+          echo "::group::{cmake-build contents}"
+          tree cmake-build
+          echo "::endgroup::"
+          python3 test/hil/hil_test.py $BOARDS_LIST ${{ env.HIL_JSON }}
diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml
index 947b08ca2..c3cc59d0d 100644
--- a/.github/workflows/labeler.yml
+++ b/.github/workflows/labeler.yml
@@ -3,13 +3,15 @@ name: Labeler
 on:
   issues:
     types: [opened]
-  pull_request:
+  pull_request_target:
     types: [opened]
 
 jobs:
   label-priority:
     runs-on: ubuntu-latest
-
+    permissions:
+      issues: write
+      pull-requests: write
     steps:
       - name: Label New Issue or PR
         uses: actions/github-script@v7
@@ -23,13 +25,13 @@ jobs:
             if (context.eventName === 'issues') {
               username = context.payload.issue.user.login;
               issueOrPrNumber = context.payload.issue.number;
-            } else if (context.eventName === 'pull_request') {
+            } else if (context.eventName === 'pull_request_target') {
               username = context.payload.pull_request.user.login;
               issueOrPrNumber = context.payload.pull_request.number;
             }
 
+            // Check if an Adafruit member
             try {
-              // Check for Adafruit membership
               const adafruitResponse = await github.rest.orgs.checkMembershipForUser({
                 org: 'adafruit',
                 username: username
@@ -38,8 +40,14 @@ jobs:
               if (adafruitResponse.status === 204) {
                 console.log('Adafruit Member');
                 label = 'Prio Urgent';
-              } else {
-                // If not a Adafruit member, check if the user is a contributor
+              }
+            } catch (error) {
+              console.log('Not an Adafruit member');
+            }
+
+            // Check if a contributor
+            if (label == '') {
+              try {
                 const collaboratorResponse = await github.rest.repos.checkCollaborator({
                   owner: context.repo.owner,
                   repo: context.repo.repo,
@@ -50,9 +58,9 @@ jobs:
                   console.log('Contributor');
                   label = 'Prio Higher';
                 }
+              } catch (error) {
+                console.log('Not a contributor');
               }
-            } catch (error) {
-              console.log(`Error processing user ${username}: ${error.message}`);
             }
 
             if (label !== '') {
diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
index e36259daa..379a22ee2 100644
--- a/.github/workflows/pre-commit.yml
+++ b/.github/workflows/pre-commit.yml
@@ -7,18 +7,13 @@ on:
     branches: [ master ]
 
 concurrency:
-  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+  group: ${{ github.workflow }}-${{ github.ref }}
   cancel-in-progress: true
 
 jobs:
   pre-commit:
     runs-on: ubuntu-latest
     steps:
-    - name: Setup Python
-      uses: actions/setup-python@v5
-      with:
-        python-version: '3.x'
-
     - name: Setup Ruby
       uses: ruby/setup-ruby@v1
       with:
diff --git a/.gitignore b/.gitignore
index e6ccec736..f2150a26f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -31,59 +31,4 @@ cov-int
 __pycache__
 cmake-build-*
 sdkconfig
-
-# submodules
-hw/mcu/allwinner
-hw/mcu/bridgetek/ft9xx/ft90x-sdk
-hw/mcu/broadcom
-hw/mcu/gd/nuclei-sdk
-hw/mcu/infineon/mtb-xmclib-cat3
-hw/mcu/microchip
-hw/mcu/mindmotion/mm32sdk
-hw/mcu/nordic/nrfx
-hw/mcu/nuvoton
-hw/mcu/nxp/lpcopen
-hw/mcu/nxp/mcux-sdk
-hw/mcu/nxp/nxp_sdk
-hw/mcu/raspberry_pi/Pico-PIO-USB
-hw/mcu/renesas/rx
-hw/mcu/silabs/cmsis-dfp-efm32gg12b
-hw/mcu/sony/cxd56/spresense-exported-sdk
-hw/mcu/st/cmsis_device_f0
-hw/mcu/st/cmsis_device_f1
-hw/mcu/st/cmsis_device_f2
-hw/mcu/st/cmsis_device_f3
-hw/mcu/st/cmsis_device_f4
-hw/mcu/st/cmsis_device_f7
-hw/mcu/st/cmsis_device_g0
-hw/mcu/st/cmsis_device_g4
-hw/mcu/st/cmsis_device_h7
-hw/mcu/st/cmsis_device_l0
-hw/mcu/st/cmsis_device_l1
-hw/mcu/st/cmsis_device_l4
-hw/mcu/st/cmsis_device_l5
-hw/mcu/st/cmsis_device_u5
-hw/mcu/st/cmsis_device_wb
-hw/mcu/st/stm32f0xx_hal_driver
-hw/mcu/st/stm32f1xx_hal_driver
-hw/mcu/st/stm32f2xx_hal_driver
-hw/mcu/st/stm32f3xx_hal_driver
-hw/mcu/st/stm32f4xx_hal_driver
-hw/mcu/st/stm32f7xx_hal_driver
-hw/mcu/st/stm32g0xx_hal_driver
-hw/mcu/st/stm32g4xx_hal_driver
-hw/mcu/st/stm32h7xx_hal_driver
-hw/mcu/st/stm32l0xx_hal_driver
-hw/mcu/st/stm32l1xx_hal_driver
-hw/mcu/st/stm32l4xx_hal_driver
-hw/mcu/st/stm32l5xx_hal_driver
-hw/mcu/st/stm32u5xx_hal_driver
-hw/mcu/st/stm32wbxx_hal_driver
-hw/mcu/ti
-hw/mcu/wch/ch32v307
-hw/mcu/wch/ch32f20x
-lib/CMSIS_5
-lib/FreeRTOS-Kernel
-lib/lwip
-lib/sct_neopixel
-tools/uf2
+.PVS-Studio
diff --git a/.idea/cmake.xml b/.idea/cmake.xml
index ebc6a0570..67e793016 100644
--- a/.idea/cmake.xml
+++ b/.idea/cmake.xml
@@ -2,8 +2,10 @@
 
   
     
-      
-      
+      
+      
+      
+      
       
       
         
@@ -40,48 +42,103 @@
           
         
       
+      
+        
+          
+            
+          
+        
+      
+      
+        
+          
+            
+          
+        
+      
+      
+        
+          
+            
+          
+        
+      
       
       
       
-      
-      
+      
+      
+      
+      
       
       
       
-      
+      
       
       
       
+      
       
       
       
-      
-      
-      
+      
+      
+      
+      
+      
       
+      
       
       
       
+      
+      
       
       
-      
-      
-      
-      
       
       
       
-      
-      
+      
+      
+      
+      
+      
+      
       
-      
-      
-      
-      
-      
+      
+      
+      
+      
+      
+      
       
       
-      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
+      
     
   
 
\ No newline at end of file
diff --git a/.idea/runConfigurations/kl25.xml b/.idea/runConfigurations/kl25.xml
index 5aace3a60..96c208dde 100644
--- a/.idea/runConfigurations/kl25.xml
+++ b/.idea/runConfigurations/kl25.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/lpc1857.xml b/.idea/runConfigurations/lpc1857.xml
index a60b481eb..a4764b9d6 100644
--- a/.idea/runConfigurations/lpc1857.xml
+++ b/.idea/runConfigurations/lpc1857.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/lpc4088.xml b/.idea/runConfigurations/lpc4088.xml
index 767d98602..9da975ef3 100644
--- a/.idea/runConfigurations/lpc4088.xml
+++ b/.idea/runConfigurations/lpc4088.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/lpc54628.xml b/.idea/runConfigurations/lpc54628.xml
index e425e2387..0c3877e94 100644
--- a/.idea/runConfigurations/lpc54628.xml
+++ b/.idea/runConfigurations/lpc54628.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/lpc55s69.xml b/.idea/runConfigurations/lpc55s69.xml
index 27743d980..2fa127d33 100644
--- a/.idea/runConfigurations/lpc55s69.xml
+++ b/.idea/runConfigurations/lpc55s69.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/mcx947.xml b/.idea/runConfigurations/mcx947.xml
index 2ff405739..12180a996 100644
--- a/.idea/runConfigurations/mcx947.xml
+++ b/.idea/runConfigurations/mcx947.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/nrf52840.xml b/.idea/runConfigurations/nrf52840.xml
index 8053d9b38..8e48a2b97 100644
--- a/.idea/runConfigurations/nrf52840.xml
+++ b/.idea/runConfigurations/nrf52840.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/nrf5340.xml b/.idea/runConfigurations/nrf5340.xml
index 4a5a91734..646f2d38c 100644
--- a/.idea/runConfigurations/nrf5340.xml
+++ b/.idea/runConfigurations/nrf5340.xml
@@ -1,5 +1,5 @@
 
-  
+  
     
       
     
diff --git a/.idea/runConfigurations/ra4m1.xml b/.idea/runConfigurations/ra4m1.xml
index a5c361a2a..72bc63d9b 100644
--- a/.idea/runConfigurations/ra4m1.xml
+++ b/.idea/runConfigurations/ra4m1.xml
@@ -1,5 +1,5 @@
 
-  
+  
     
       
     
diff --git a/.idea/runConfigurations/ra6m1.xml b/.idea/runConfigurations/ra6m1.xml
index 7db8e9815..ca8c7245a 100644
--- a/.idea/runConfigurations/ra6m1.xml
+++ b/.idea/runConfigurations/ra6m1.xml
@@ -1,5 +1,5 @@
 
-  
+  
     
       
     
diff --git a/.idea/runConfigurations/ra6m5.xml b/.idea/runConfigurations/ra6m5.xml
index 24e942fda..ecbdb21b7 100644
--- a/.idea/runConfigurations/ra6m5.xml
+++ b/.idea/runConfigurations/ra6m5.xml
@@ -1,5 +1,5 @@
 
-  
+  
     
       
     
diff --git a/.idea/runConfigurations/rp2040.xml b/.idea/runConfigurations/rp2040.xml
index 51ae689be..da5a8f1ee 100644
--- a/.idea/runConfigurations/rp2040.xml
+++ b/.idea/runConfigurations/rp2040.xml
@@ -1,5 +1,5 @@
 
-  
+  
     
       
     
diff --git a/.idea/runConfigurations/rt1010.xml b/.idea/runConfigurations/rt1010.xml
index 7929d56d8..f415c0676 100644
--- a/.idea/runConfigurations/rt1010.xml
+++ b/.idea/runConfigurations/rt1010.xml
@@ -1,5 +1,5 @@
 
-  
+  
     
       
     
diff --git a/.idea/runConfigurations/rt1060.xml b/.idea/runConfigurations/rt1060.xml
index f26dc7373..cc75aa62c 100644
--- a/.idea/runConfigurations/rt1060.xml
+++ b/.idea/runConfigurations/rt1060.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/samd21g18.xml b/.idea/runConfigurations/samd21g18.xml
index a922da648..f8aa6009d 100644
--- a/.idea/runConfigurations/samd21g18.xml
+++ b/.idea/runConfigurations/samd21g18.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/samd51j19.xml b/.idea/runConfigurations/samd51j19.xml
index a11baa3fd..694ea7dfd 100644
--- a/.idea/runConfigurations/samd51j19.xml
+++ b/.idea/runConfigurations/samd51j19.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/stlink.xml b/.idea/runConfigurations/stlink.xml
index c82f37d59..628f7910d 100644
--- a/.idea/runConfigurations/stlink.xml
+++ b/.idea/runConfigurations/stlink.xml
@@ -1,5 +1,5 @@
 
-  
+  
     
       
     
diff --git a/.idea/runConfigurations/stm32g474.xml b/.idea/runConfigurations/stm32g474.xml
index ad6209bc0..6d65e83c7 100644
--- a/.idea/runConfigurations/stm32g474.xml
+++ b/.idea/runConfigurations/stm32g474.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/stm32h743.xml b/.idea/runConfigurations/stm32h743.xml
index 1d0c0155d..aeaf9fb53 100644
--- a/.idea/runConfigurations/stm32h743.xml
+++ b/.idea/runConfigurations/stm32h743.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.idea/runConfigurations/uno_r4.xml b/.idea/runConfigurations/uno_r4.xml
index f3d1ccac6..98bf91812 100644
--- a/.idea/runConfigurations/uno_r4.xml
+++ b/.idea/runConfigurations/uno_r4.xml
@@ -1,6 +1,6 @@
 
-  
-    
+  
+    
       
     
     
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 4071ec326..bba217cc9 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -16,7 +16,9 @@ repos:
     exclude: |
       (?x)^(
         .idea/|
-        hw/bsp/mcx/sdk/
+        hw/bsp/mcx/sdk/|
+        docs/contributing/code_of_conduct.rst|
+        docs/info/contributors.rst
       )
   - id: forbid-submodules
 
diff --git a/README.rst b/README.rst
index 8c3145d33..1ae8c5375 100644
--- a/README.rst
+++ b/README.rst
@@ -122,42 +122,46 @@ Following CPUs are supported, check out `Supported Devices`_ for comprehensive l
 | GigaDevice   | GD32VF103                                                  |
 +--------------+------------------------------------------------------------+
 | Infineon     | XMC4500                                                    |
-+--------------+-----+------------------------------------------------------+
-| MicroChip    | SAM | D11, D21, D51, E5x, G55, L2x, E7x, S7x, V7x          |
-|              +-----+------------------------------------------------------+
-|              | PIC | 24, 32mm, 32mk, 32mx, 32mz, dsPIC33                  |
-+--------------+-----+------------------------------------------------------+
++--------------+------------------------------------------------------------+
+|              | SAM:  D11, D21, D51, E5x, G55, L2x, E7x, S7x, V7x          |
+|  MicroChip   |                                                            |
+|              | PIC:  24, 32mm, 32mk, 32mx, 32mz, dsPIC33                  |
++--------------+------------------------------------------------------------+
 | Mind Montion | mm32                                                       |
 +--------------+------------------------------------------------------------+
 | NordicSemi   | nRF52833, nRF52840, nRF5340                                |
 +--------------+------------------------------------------------------------+
 | Nuvoton      | NUC 120, 121, 125, 126, 505                                |
-+--------------+---------+--------------------------------------------------+
-| NXP          | iMXRT   | RT10xx, RT11xx                                   |
-|              +---------+--------------------------------------------------+
-|              | Kinetis | KL, K32L2                                        |
-|              +---------+--------------------------------------------------+
-|              | LPC     | 11u, 13, 15, 17, 18, 40, 43, 51u, 54, 55         |
-|              +---------+--------------------------------------------------+
-|              | MCX     | A15, N9                                          |
-+--------------+---------+--------------------------------------------------+
++--------------+------------------------------------------------------------+
+| NXP          | iMXRT: RT10xx, RT11xx                                      |
+|              |                                                            |
+|              | Kinetis: KL, K32L2                                         |
+|              |                                                            |
+|              | LPC: 11u, 13, 15, 17, 18, 40, 43, 51u, 54, 55              |
+|              |                                                            |
+|              | MCX: A15, N9                                               |
++--------------+------------------------------------------------------------+
 | Raspberry Pi | RP2040                                                     |
 +--------------+-----+------------------------------------------------------+
-| Renesas      | RX  | 63N, 65N, 72N                                        |
-+--------------+-----+------------------------------------------------------+
-|              | RA  | 4M1, 4M3, 6M1, 6M5                                   |
+| Renesas      | RA: 4M1, 4M3, 6M1, 6M5                                     |
+|              |                                                            |
+|              | RX: 63N, 65N, 72N                                          |
 +--------------+-----+------------------------------------------------------+
 | Silabs       | EFM32GG12                                                  |
 +--------------+------------------------------------------------------------+
 | Sony         | CXD56                                                      |
 +--------------+------------------------------------------------------------+
-| ST STM32     | F0, F1, F2, F3, F4, F7, H7, G0, G4, L0, L1, L4, L4+ U5, WB |
+| ST STM32     | F0, F1, F2, F3, F4, F7, G0, G4, H5, H7,                    |
+|              |                                                            |
+|              | L0, L1, L4, L4+, L5, U5, WB                                |
 +--------------+------------------------------------------------------------+
 | TI           | MSP430, MSP432E4, TM4C123                                  |
 +--------------+------------------------------------------------------------+
 | ValentyUSB   | eptri                                                      |
 +--------------+------------------------------------------------------------+
-| WCH          | CH32F20x, CH32V307,                                        |
+| WCH          | CH32F: F20x                                                |
+|              |                                                            |
+|              | CH32V: V20x, V307                                          |
 +--------------+------------------------------------------------------------+
 
 License
diff --git a/SConscript b/SConscript
new file mode 100644
index 000000000..b5043f437
--- /dev/null
+++ b/SConscript
@@ -0,0 +1,11 @@
+# RT-Thread building script for bridge
+
+import os
+from building import *
+
+objs = []
+cwd  = GetCurrentDir()
+
+objs = objs + SConscript(cwd + '/lib/rt-thread/SConscript')
+
+Return('objs')
diff --git a/docs/info/changelog.rst b/docs/info/changelog.rst
index b359ebb44..f7ccb39b9 100644
--- a/docs/info/changelog.rst
+++ b/docs/info/changelog.rst
@@ -2,6 +2,72 @@
 Changelog
 *********
 
+0.17.0 (WIP)
+============
+
+General
+-------
+
+- Improved continuous integration: build both cmake and make. Make use of circleci to build arm-clang
+
+
+Controller Driver (DCD & HCD)
+-----------------------------
+
+- WCH CH32
+
+  - Added support for USB OTG/FS and FSDev Driver. Update CH32V307 to allow manual select FS or HS driver.
+  - Fixed various bugs in CH32v307 usbhs driver: endpoint handling and data transfer management.
+
+- Fixed race conditions and other bugs in dcd_nrf5x and other drivers.
+- Implemented hcd abort transfer for Max3421 and rp2040
+- Added DWC2 Test Mode support.
+- stm32 fsdev: ISO EP buffer allocation improvements, implement dcd_edpt_close_all()
+- Added support for STM32G4 and STM32U5 microcontrollers.
+
+Device Stack
+------------
+
+- Added tud_deinit() to deinitialize TinyUSB device stack.
+- Added support for generic SOF callback.
+
+- Audio
+
+  - Add audio_test_freertos & audio_4_channel_mic_freertos
+  - Improved support for Audio Class 2.0 (UAC2) with various bug fixes.
+
+- HID
+
+  - Added missing key codes for keypad
+  - Added HID Lighting and Illumination functionality
+
+- Vendor: Added empty transfers for tud_vendor_n_write()
+- MSC: Added support for SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
+
+- CDC
+
+  - Add option to make CDC TX buffer persistent
+  - Add missing capability bit for CDC ACM serial break support
+
+- Net
+
+  - Rewrite of NCM device driver
+  - removed obsolete tud_network_link_state_cb()
+
+- Enhanced CDC class with better handling of large data transmissions.
+- Fixed issues in the HID class for more reliable device enumeration.
+- Video Added support for USB Video Class (UVC) with MJPEG.
+- USBTMC Added notification support
+
+Host Stack
+----------
+
+- Added tuh_deinit() to deinitialize TinyUSB host stack.
+- Added support for new USB mass storage class APIs.
+- Enhanced stability of CDC-ACM devices during enumeration.
+- Improved error handling and retry mechanisms for unstable devices.
+- Added support for multiple interfaces in UVC.
+
 0.16.0
 ======
 
diff --git a/docs/reference/dependencies.rst b/docs/reference/dependencies.rst
index 6ba6692e9..fd895519e 100644
--- a/docs/reference/dependencies.rst
+++ b/docs/reference/dependencies.rst
@@ -4,21 +4,21 @@ Dependencies
 
 MCU low-level peripheral driver and external libraries for building TinyUSB examples
 
-========================================  ==============================================================  ========================================  =======================================================================================================================================================================================================
+========================================  ==============================================================  ========================================  ==========================================================================================================================================================================================================================================================================================================================
 Local Path                                Repo                                                            Commit                                    Required by
-========================================  ==============================================================  ========================================  =======================================================================================================================================================================================================
+========================================  ==============================================================  ========================================  ==========================================================================================================================================================================================================================================================================================================================
 hw/mcu/allwinner                          https://github.com/hathach/allwinner_driver.git                 8e5e89e8e132c0fd90e72d5422e5d3d68232b756  fc100s
 hw/mcu/bridgetek/ft9xx/ft90x-sdk          https://github.com/BRTSG-FOSS/ft90x-sdk.git                     91060164afe239fcb394122e8bf9eb24d3194eb1  brtmm90x
 hw/mcu/broadcom                           https://github.com/adafruit/broadcom-peripherals.git            08370086080759ed54ac1136d62d2ad24c6fa267  broadcom_32bit broadcom_64bit
 hw/mcu/gd/nuclei-sdk                      https://github.com/Nuclei-Software/nuclei-sdk.git               7eb7bfa9ea4fbeacfafe1d5f77d5a0e6ed3922e7  gd32vf103
 hw/mcu/infineon/mtb-xmclib-cat3           https://github.com/Infineon/mtb-xmclib-cat3.git                 daf5500d03cba23e68c2f241c30af79cd9d63880  xmc4000
-hw/mcu/microchip                          https://github.com/hathach/microchip_driver.git                 9e8b37e307d8404033bb881623a113931e1edf27  sam3x samd11 samd21 samd51 same5x same7x saml2x samg
-hw/mcu/mindmotion/mm32sdk                 https://github.com/hathach/mm32sdk.git                          0b79559eb411149d36e073c1635c620e576308d4  mm32
-hw/mcu/nordic/nrfx                        https://github.com/NordicSemiconductor/nrfx.git                 2527e3c8449cfd38aee41598e8af8492f410ed15  nrf
+hw/mcu/microchip                          https://github.com/hathach/microchip_driver.git                 9e8b37e307d8404033bb881623a113931e1edf27  sam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg
+hw/mcu/mindmotion/mm32sdk                 https://github.com/hathach/mm32sdk.git                          b93e856211060ae825216c6a1d6aa347ec758843  mm32
+hw/mcu/nordic/nrfx                        https://github.com/NordicSemiconductor/nrfx.git                 7c47cc0a56ce44658e6da2458e86cd8783ccc4a2  nrf
 hw/mcu/nuvoton                            https://github.com/majbthrd/nuc_driver.git                      2204191ec76283371419fbcec207da02e1bc22fa  nuc
-hw/mcu/nxp/lpcopen                        https://github.com/hathach/nxp_lpcopen.git                      84e0bd3e43910aaf71eefd62075cf57495418312  lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43
-hw/mcu/nxp/mcux-sdk                       https://github.com/hathach/mcux-sdk.git                         950819b7de9b32f92c3edf396bc5ffb8d66e7009  kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx imxrt
-hw/mcu/raspberry_pi/Pico-PIO-USB          https://github.com/sekigon-gonnoc/Pico-PIO-USB.git              d00a10a8c425d0d40f81b87169102944b01f3bb3  rp2040
+hw/mcu/nxp/lpcopen                        https://github.com/hathach/nxp_lpcopen.git                      04bfe7a5f6ee74a89a28ad618d3367dcfcfb7d83  lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43
+hw/mcu/nxp/mcux-sdk                       https://github.com/hathach/mcux-sdk.git                         144f1eb7ea8c06512e12f12b27383601c0272410  kinetis_k kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx imxrt
+hw/mcu/raspberry_pi/Pico-PIO-USB          https://github.com/sekigon-gonnoc/Pico-PIO-USB.git              0f747aaa0c16f750bdfa2ba37ec25d6c8e1bc117  rp2040
 hw/mcu/renesas/fsp                        https://github.com/renesas/fsp.git                              d52e5a6a59b7c638da860c2bb309b6e78e752ff8  ra
 hw/mcu/renesas/rx                         https://github.com/kkitayam/rx_device.git                       706b4e0cf485605c32351e2f90f5698267996023  rx
 hw/mcu/silabs/cmsis-dfp-efm32gg12b        https://github.com/cmsis-packs/cmsis-dfp-efm32gg12b.git         f1c31b7887669cb230b3ea63f9b56769078960bc  efm32
@@ -28,15 +28,16 @@ hw/mcu/st/cmsis_device_f1                 https://github.com/STMicroelectronics/
 hw/mcu/st/cmsis_device_f2                 https://github.com/STMicroelectronics/cmsis_device_f2.git       182fcb3681ce116816feb41b7764f1b019ce796f  stm32f2
 hw/mcu/st/cmsis_device_f3                 https://github.com/STMicroelectronics/cmsis_device_f3.git       5e4ee5ed7a7b6c85176bb70a9fd3c72d6eb99f1b  stm32f3
 hw/mcu/st/cmsis_device_f4                 https://github.com/STMicroelectronics/cmsis_device_f4.git       2615e866fa48fe1ff1af9e31c348813f2b19e7ec  stm32f4
-hw/mcu/st/cmsis_device_f7                 https://github.com/STMicroelectronics/cmsis_device_f7.git       fc676ef1ad177eb874eaa06444d3d75395fc51f4  stm32f7
+hw/mcu/st/cmsis_device_f7                 https://github.com/STMicroelectronics/cmsis_device_f7.git       25b0463439303b7a38f0d27b161f7d2f3c096e79  stm32f7
 hw/mcu/st/cmsis_device_g0                 https://github.com/STMicroelectronics/cmsis_device_g0.git       3a23e1224417f3f2d00300ecd620495e363f2094  stm32g0
 hw/mcu/st/cmsis_device_g4                 https://github.com/STMicroelectronics/cmsis_device_g4.git       ce822adb1dc552b3aedd13621edbc7fdae124878  stm32g4
+hw/mcu/st/cmsis_device_h5                 https://github.com/STMicroelectronics/cmsis_device_h5.git       cd2d1d579743de57b88ccaf61a968b9c05848ffc  stm32h5
 hw/mcu/st/cmsis_device_h7                 https://github.com/STMicroelectronics/cmsis_device_h7.git       60dc2c913203dc8629dc233d4384dcc41c91e77f  stm32h7
-hw/mcu/st/cmsis_device_l0                 https://github.com/STMicroelectronics/cmsis_device_l0.git       06748ca1f93827befdb8b794402320d94d02004f  stm32l0
+hw/mcu/st/cmsis_device_l0                 https://github.com/STMicroelectronics/cmsis_device_l0.git       69cd5999fd40ae6e546d4905b21635c6ca1bcb92  stm32l0
 hw/mcu/st/cmsis_device_l1                 https://github.com/STMicroelectronics/cmsis_device_l1.git       7f16ec0a1c4c063f84160b4cc6bf88ad554a823e  stm32l1
 hw/mcu/st/cmsis_device_l4                 https://github.com/STMicroelectronics/cmsis_device_l4.git       6ca7312fa6a5a460b5a5a63d66da527fdd8359a6  stm32l4
 hw/mcu/st/cmsis_device_l5                 https://github.com/STMicroelectronics/cmsis_device_l5.git       d922865fc0326a102c26211c44b8e42f52c1e53d  stm32l5
-hw/mcu/st/cmsis_device_u5                 https://github.com/STMicroelectronics/cmsis_device_u5.git       06d7edade7167b0eafdd550bf77cfc4fa98eae2e  stm32u5
+hw/mcu/st/cmsis_device_u5                 https://github.com/STMicroelectronics/cmsis_device_u5.git       5ad9797c54ec3e55eff770fc9b3cd4a1aefc1309  stm32u5
 hw/mcu/st/cmsis_device_wb                 https://github.com/STMicroelectronics/cmsis_device_wb.git       9c5d1920dd9fabbe2548e10561d63db829bb744f  stm32wb
 hw/mcu/st/stm32f0xx_hal_driver            https://github.com/STMicroelectronics/stm32f0xx_hal_driver.git  0e95cd88657030f640a11e690a8a5186c7712ea5  stm32f0
 hw/mcu/st/stm32f1xx_hal_driver            https://github.com/STMicroelectronics/stm32f1xx_hal_driver.git  1dd9d3662fb7eb2a7f7d3bc0a4c1dc7537915a29  stm32f1
@@ -46,6 +47,7 @@ hw/mcu/st/stm32f4xx_hal_driver            https://github.com/STMicroelectronics/
 hw/mcu/st/stm32f7xx_hal_driver            https://github.com/STMicroelectronics/stm32f7xx_hal_driver.git  f7ffdf6bf72110e58b42c632b0a051df5997e4ee  stm32f7
 hw/mcu/st/stm32g0xx_hal_driver            https://github.com/STMicroelectronics/stm32g0xx_hal_driver.git  e911b12c7f67084d7f6b76157a4c0d4e2ec3779c  stm32g0
 hw/mcu/st/stm32g4xx_hal_driver            https://github.com/STMicroelectronics/stm32g4xx_hal_driver.git  8b4518417706d42eef5c14e56a650005abf478a8  stm32g4
+hw/mcu/st/stm32h5xx_hal_driver            https://github.com/STMicroelectronics/stm32h5xx_hal_driver.git  2cf77de584196d619cec1b4586c3b9e2820a254e  stm32h5
 hw/mcu/st/stm32h7xx_hal_driver            https://github.com/STMicroelectronics/stm32h7xx_hal_driver.git  d8461b980b59b1625207d8c4f2ce0a9c2a7a3b04  stm32h7
 hw/mcu/st/stm32l0xx_hal_driver            https://github.com/STMicroelectronics/stm32l0xx_hal_driver.git  fbdacaf6f8c82a4e1eb9bd74ba650b491e97e17b  stm32l0
 hw/mcu/st/stm32l1xx_hal_driver            https://github.com/STMicroelectronics/stm32l1xx_hal_driver.git  44efc446fa69ed8344e7fd966e68ed11043b35d9  stm32l1
@@ -53,12 +55,13 @@ hw/mcu/st/stm32l4xx_hal_driver            https://github.com/STMicroelectronics/
 hw/mcu/st/stm32l5xx_hal_driver            https://github.com/STMicroelectronics/stm32l5xx_hal_driver.git  675c32a75df37f39d50d61f51cb0dcf53f07e1cb  stm32l5
 hw/mcu/st/stm32u5xx_hal_driver            https://github.com/STMicroelectronics/stm32u5xx_hal_driver.git  4d93097a67928e9377e655ddd14622adc31b9770  stm32u5
 hw/mcu/st/stm32wbxx_hal_driver            https://github.com/STMicroelectronics/stm32wbxx_hal_driver.git  2c5f06638be516c1b772f768456ba637f077bac8  stm32wb
-hw/mcu/ti                                 https://github.com/hathach/ti_driver.git                        143ed6cc20a7615d042b03b21e070197d473e6e5  msp430 msp432e4 tm4c123
+hw/mcu/ti                                 https://github.com/hathach/ti_driver.git                        143ed6cc20a7615d042b03b21e070197d473e6e5  msp430 msp432e4 tm4c
 hw/mcu/wch/ch32f20x                       https://github.com/openwch/ch32f20x.git                         77c4095087e5ed2c548ec9058e655d0b8757663b  ch32f20x
+hw/mcu/wch/ch32v20x                       https://github.com/openwch/ch32v20x.git                         de6d68c654340d7f27b00cebbfc9aa2740a1abc2  ch32v20x
 hw/mcu/wch/ch32v307                       https://github.com/openwch/ch32v307.git                         17761f5cf9dbbf2dcf665b7c04934188add20082  ch32v307
-lib/CMSIS_5                               https://github.com/ARM-software/CMSIS_5.git                     20285262657d1b482d132d20d755c8c330d55c1f  imxrt kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx mm32 msp432e4 nrf ra saml2xstm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h7 stm32l0 stm32l1 stm32l4 stm32l5 stm32u5 stm32wb
-lib/FreeRTOS-Kernel                       https://github.com/FreeRTOS/FreeRTOS-Kernel.git                 4ff01a7a4a51f53b44496aefee1e3c0071b7b173  all
+lib/CMSIS_5                               https://github.com/ARM-software/CMSIS_5.git                     20285262657d1b482d132d20d755c8c330d55c1f  imxrt kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx mm32 msp432e4 nrf ra saml2xlpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h5stm32h7 stm32l0 stm32l1 stm32l4 stm32l5 stm32u5 stm32wbsam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samgtm4c
+lib/FreeRTOS-Kernel                       https://github.com/FreeRTOS/FreeRTOS-Kernel.git                 cc0e0707c0c748713485b870bb980852b210877f  all
 lib/lwip                                  https://github.com/lwip-tcpip/lwip.git                          159e31b689577dbf69cf0683bbaffbd71fa5ee10  all
 lib/sct_neopixel                          https://github.com/gsteiert/sct_neopixel.git                    e73e04ca63495672d955f9268e003cffe168fcd8  lpc55
 tools/uf2                                 https://github.com/microsoft/uf2.git                            19615407727073e36d81bf239c52108ba92e7660  all
-========================================  ==============================================================  ========================================  =======================================================================================================================================================================================================
+========================================  ==============================================================  ========================================  ==========================================================================================================================================================================================================================================================================================================================
diff --git a/docs/reference/supported.rst b/docs/reference/supported.rst
index 7e7be25a4..edb6b69ca 100644
--- a/docs/reference/supported.rst
+++ b/docs/reference/supported.rst
@@ -104,21 +104,33 @@ Supported MCUs
 |              +-----------------------+--------+------+-----------+-------------------+--------------+
 |              | F7                    | ✔      |      | ✔         | dwc2              |              |
 |              +-----------------------+--------+------+-----------+-------------------+--------------+
-|              | H7                    | ✔      |      | ✔         | dwc2              |              |
+|              | G0                    | ✔      |      | ✖         | stm32_fsdev       |              |
 |              +-----------------------+--------+------+-----------+-------------------+--------------+
 |              | G4                    | ✔      | ✖    | ✖         | stm32_fsdev       |              |
 |              +-----------------------+--------+------+-----------+-------------------+--------------+
-|              | L0, L1                | ✔      | ✖    | ✖         | stm32_fsdev       |              |
+|              | H5                    | ✔      |      | ✖         | stm32_fsdev       |              |
+|              +-----------------------+--------+------+-----------+-------------------+--------------+
+|              | H7                    | ✔      |      | ✔         | dwc2              |              |
+|              +-----------------------+--------+------+-----------+-------------------+--------------+
+|              | L0                    | ✔      | ✖    | ✖         | stm32_fsdev       |              |
+|              +-----------------------+--------+------+-----------+-------------------+--------------+
+|              | L1                    | ✔      | ✖    | ✖         | stm32_fsdev       |              |
 |              +----+------------------+--------+------+-----------+-------------------+--------------+
 |              | L4 | 4x2, 4x3         | ✔      | ✖    | ✖         | stm32_fsdev       |              |
 |              |    +------------------+--------+------+-----------+-------------------+--------------+
-|              |    | 4x5, 4x6         | ✔      |      |           | dwc2              |              |
+|              |    | 4x5, 4x6         | ✔      |      | ✖         | dwc2              |              |
 |              +----+------------------+--------+------+-----------+-------------------+--------------+
-|              | L4+                   | ✔      |      |           | dwc2              |              |
+|              | L4+                   | ✔      |      | ✖         | dwc2              |              |
 |              +-----------------------+--------+------+-----------+-------------------+--------------+
-|              | U5                    | ✔      |      | ✔         | dwc2              |              |
-|              +-----------------------+--------+------+-----------+-------------------+--------------+
-|              | WBx5                  | ✔      |      |           | stm32_fsdev       |              |
+|              | L5                    | ✔      | ✖    | ✖         | stm32_fsdev       |              |
+|              +----+------------------+--------+------+-----------+-------------------+--------------+
+|              | U5 | 535, 545         | ✔      |      | ✖         | stm32_fsdev       |              |
+|              |    +------------------+--------+------+-----------+-------------------+--------------+
+|              |    | 575, 585         | ✔      |      | ✖         | dwc2              |              |
+|              |    +------------------+--------+------+-----------+-------------------+--------------+
+|              |    | 59x,5Ax,5Fx,5Gx  | ✔      |      | ✔         | dwc2              |              |
+|              +----+------------------+--------+------+-----------+-------------------+--------------+
+|              | WBx5                  | ✔      | ✖    | ✖         | stm32_fsdev       |              |
 +--------------+-----------------------+--------+------+-----------+-------------------+--------------+
 | TI           | MSP430                | ✔      | ✖    | ✖         | msp430x5xx        |              |
 |              +-----------------------+--------+------+-----------+-------------------+--------------+
@@ -128,20 +140,23 @@ Supported MCUs
 +--------------+-----------------------+--------+------+-----------+-------------------+--------------+
 | ValentyUSB   | eptri                 | ✔      | ✖    | ✖         | eptri             |              |
 +--------------+-----------------------+--------+------+-----------+-------------------+--------------+
-| WCH          | CH32V307              | ✔      |      | ✔         | ch32v307          |              |
+| WCH          | CH32F20x              | ✔      |      | ✔         | ch32f205          |              |
 |              +-----------------------+--------+------+-----------+-------------------+--------------+
-|              | CH32F20x              | ✔      |      | ✔         | ch32f205          |              |
+|              | CH32V20x              | ✔      |      | ✖         | ch32v20x          |              |
+|              +-----------------------+--------+------+-----------+-------------------+--------------+
+|              | CH32V307              | ✔      |      | ✔         | ch32v307          |              |
 +--------------+-----------------------+--------+------+-----------+-------------------+--------------+
 
 
 Table Legend
 ------------
 
-= ===================
-✔ Supported
-âš  WIP/partial support
-✖ Not supported
-= ===================
+========= =========================
+✔         Supported
+âš          Partial support
+✖         Not supported by hardware
+\[empty\] Unknown
+========= =========================
 
 Supported Boards
 ================
diff --git a/examples/build_system/cmake/cpu/arm1176jzf-s.cmake b/examples/build_system/cmake/cpu/arm1176jzf-s.cmake
new file mode 100644
index 000000000..8029e3987
--- /dev/null
+++ b/examples/build_system/cmake/cpu/arm1176jzf-s.cmake
@@ -0,0 +1,21 @@
+if (TOOLCHAIN STREQUAL "gcc")
+  set(TOOLCHAIN_COMMON_FLAGS
+    -mcpu=arm1176jzf-s
+    -ffreestanding
+  )
+  # set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=arm1176jzf-s
+    -mfpu=none
+    -mfloat-abi=soft
+    -ffreestanding
+  )
+  #set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+  message(FATAL_ERROR "IAR not supported")
+
+endif ()
diff --git a/examples/build_system/cmake/cpu/arm926ej-s.cmake b/examples/build_system/cmake/cpu/arm926ej-s.cmake
new file mode 100644
index 000000000..c19b9f8a8
--- /dev/null
+++ b/examples/build_system/cmake/cpu/arm926ej-s.cmake
@@ -0,0 +1,21 @@
+if (TOOLCHAIN STREQUAL "gcc")
+  set(TOOLCHAIN_COMMON_FLAGS
+    -mcpu=arm926ej-s
+    -ffreestanding
+    )
+  # set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=arm926ej-s
+    -mfpu=none
+    -mfloat-abi=soft
+    -ffreestanding
+    )
+  #set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+  message(FATAL_ERROR "IAR not supported")
+
+endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-a53.cmake b/examples/build_system/cmake/cpu/cortex-a53.cmake
new file mode 100644
index 000000000..dde8c0a0c
--- /dev/null
+++ b/examples/build_system/cmake/cpu/cortex-a53.cmake
@@ -0,0 +1,17 @@
+if (TOOLCHAIN STREQUAL "gcc")
+	set(TOOLCHAIN_COMMON_FLAGS
+		-mcpu=cortex-a53
+		)
+	# set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "clang")
+	set(TOOLCHAIN_COMMON_FLAGS
+		--target=arm-none-eabi
+		-mcpu=cortex-a53
+		)
+	#set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+	message(FATAL_ERROR "IAR not supported")
+
+endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-a72.cmake b/examples/build_system/cmake/cpu/cortex-a72.cmake
new file mode 100644
index 000000000..a44324234
--- /dev/null
+++ b/examples/build_system/cmake/cpu/cortex-a72.cmake
@@ -0,0 +1,17 @@
+if (TOOLCHAIN STREQUAL "gcc")
+	set(TOOLCHAIN_COMMON_FLAGS
+		-mcpu=cortex-a72
+		)
+	# set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "clang")
+	set(TOOLCHAIN_COMMON_FLAGS
+		--target=arm-none-eabi
+		-mcpu=cortex-a72
+		)
+	#set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+	message(FATAL_ERROR "IAR not supported")
+
+endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m0.cmake b/examples/build_system/cmake/cpu/cortex-m0.cmake
index bc2257048..62019d90d 100644
--- a/examples/build_system/cmake/cpu/cortex-m0.cmake
+++ b/examples/build_system/cmake/cpu/cortex-m0.cmake
@@ -4,14 +4,19 @@ if (TOOLCHAIN STREQUAL "gcc")
     -mcpu=cortex-m0plus
     -mfloat-abi=soft
     )
+  set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
 
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=cortex-m0
+    )
   set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
 
 elseif (TOOLCHAIN STREQUAL "iar")
   set(TOOLCHAIN_COMMON_FLAGS
     --cpu cortex-m0
     )
-
   set(FREERTOS_PORT IAR_ARM_CM0 CACHE INTERNAL "")
 
 endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m0plus.cmake b/examples/build_system/cmake/cpu/cortex-m0plus.cmake
index bc2257048..ddf0f16af 100644
--- a/examples/build_system/cmake/cpu/cortex-m0plus.cmake
+++ b/examples/build_system/cmake/cpu/cortex-m0plus.cmake
@@ -4,14 +4,19 @@ if (TOOLCHAIN STREQUAL "gcc")
     -mcpu=cortex-m0plus
     -mfloat-abi=soft
     )
+  set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
 
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=cortex-m0plus
+    )
   set(FREERTOS_PORT GCC_ARM_CM0 CACHE INTERNAL "")
 
 elseif (TOOLCHAIN STREQUAL "iar")
   set(TOOLCHAIN_COMMON_FLAGS
     --cpu cortex-m0
     )
-
   set(FREERTOS_PORT IAR_ARM_CM0 CACHE INTERNAL "")
 
 endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m23.cmake b/examples/build_system/cmake/cpu/cortex-m23.cmake
index c7ce1d242..00382ed9b 100644
--- a/examples/build_system/cmake/cpu/cortex-m23.cmake
+++ b/examples/build_system/cmake/cpu/cortex-m23.cmake
@@ -4,14 +4,19 @@ if (TOOLCHAIN STREQUAL "gcc")
     -mcpu=cortex-m23
     -mfloat-abi=soft
     )
+  set(FREERTOS_PORT GCC_ARM_CM23_NTZ_NONSECURE CACHE INTERNAL "")
 
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=cortex-m23
+    )
   set(FREERTOS_PORT GCC_ARM_CM23_NTZ_NONSECURE CACHE INTERNAL "")
 
 elseif (TOOLCHAIN STREQUAL "iar")
   set(TOOLCHAIN_COMMON_FLAGS
     --cpu cortex-m23
     )
-
   set(FREERTOS_PORT IAR_ARM_CM23_NTZ_NONSECURE CACHE INTERNAL "")
 
 endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m3.cmake b/examples/build_system/cmake/cpu/cortex-m3.cmake
index f6f5a62f8..27888604e 100644
--- a/examples/build_system/cmake/cpu/cortex-m3.cmake
+++ b/examples/build_system/cmake/cpu/cortex-m3.cmake
@@ -3,14 +3,19 @@ if (TOOLCHAIN STREQUAL "gcc")
     -mthumb
     -mcpu=cortex-m3
     )
+  set(FREERTOS_PORT GCC_ARM_CM3 CACHE INTERNAL "")
 
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=cortex-m3
+    )
   set(FREERTOS_PORT GCC_ARM_CM3 CACHE INTERNAL "")
 
 elseif (TOOLCHAIN STREQUAL "iar")
   set(TOOLCHAIN_COMMON_FLAGS
     --cpu cortex-m3
     )
-
   set(FREERTOS_PORT IAR_ARM_CM3 CACHE INTERNAL "")
 
 endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m33-nodsp-nofp.cmake b/examples/build_system/cmake/cpu/cortex-m33-nodsp-nofp.cmake
index 01e6b979a..51fd70b0e 100644
--- a/examples/build_system/cmake/cpu/cortex-m33-nodsp-nofp.cmake
+++ b/examples/build_system/cmake/cpu/cortex-m33-nodsp-nofp.cmake
@@ -4,14 +4,15 @@ if (TOOLCHAIN STREQUAL "gcc")
     -mcpu=cortex-m33+nodsp
     -mfloat-abi=soft
     )
-
   set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
 
+elseif (TOOLCHAIN STREQUAL "clang")
+  message(FATAL_ERROR "Clang is not supported for this target")
+
 elseif (TOOLCHAIN STREQUAL "iar")
   set(TOOLCHAIN_COMMON_FLAGS
     --cpu cortex-m33+nodsp
     )
-
   set(FREERTOS_PORT IAR_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
 
 endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m33-nodsp.cmake b/examples/build_system/cmake/cpu/cortex-m33-nodsp.cmake
new file mode 100644
index 000000000..b3cd743fd
--- /dev/null
+++ b/examples/build_system/cmake/cpu/cortex-m33-nodsp.cmake
@@ -0,0 +1,25 @@
+if (TOOLCHAIN STREQUAL "gcc")
+  set(TOOLCHAIN_COMMON_FLAGS
+    -mthumb
+    -mcpu=cortex-m33+nodsp
+    -mfloat-abi=hard
+    -mfpu=fpv5-sp-d16
+    )
+  set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=cortex-m33+nodsp
+    -mfpu=fpv5-sp-d16
+    )
+  set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --cpu cortex-m33+nodsp
+    --fpu VFPv5-SP
+    )
+  set(FREERTOS_PORT IAR_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
+
+endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m33.cmake b/examples/build_system/cmake/cpu/cortex-m33.cmake
index 94c29099d..d56d07ebc 100644
--- a/examples/build_system/cmake/cpu/cortex-m33.cmake
+++ b/examples/build_system/cmake/cpu/cortex-m33.cmake
@@ -5,7 +5,14 @@ if (TOOLCHAIN STREQUAL "gcc")
     -mfloat-abi=hard
     -mfpu=fpv5-sp-d16
     )
+  set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
 
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=cortex-m33
+    -mfpu=fpv5-sp-d16
+    )
   set(FREERTOS_PORT GCC_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
 
 elseif (TOOLCHAIN STREQUAL "iar")
@@ -13,7 +20,6 @@ elseif (TOOLCHAIN STREQUAL "iar")
     --cpu cortex-m33
     --fpu VFPv5-SP
     )
-
   set(FREERTOS_PORT IAR_ARM_CM33_NTZ_NONSECURE CACHE INTERNAL "")
 
 endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m4.cmake b/examples/build_system/cmake/cpu/cortex-m4.cmake
index db308aa83..9cdadc6f8 100644
--- a/examples/build_system/cmake/cpu/cortex-m4.cmake
+++ b/examples/build_system/cmake/cpu/cortex-m4.cmake
@@ -5,7 +5,16 @@ if (TOOLCHAIN STREQUAL "gcc")
     -mfloat-abi=hard
     -mfpu=fpv4-sp-d16
     )
+  if (NOT DEFINED FREERTOS_PORT)
+    set(FREERTOS_PORT GCC_ARM_CM4F CACHE INTERNAL "")
+  endif ()
 
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=cortex-m4
+    -mfpu=fpv4-sp-d16
+    )
   if (NOT DEFINED FREERTOS_PORT)
     set(FREERTOS_PORT GCC_ARM_CM4F CACHE INTERNAL "")
   endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m7-fpsp.cmake b/examples/build_system/cmake/cpu/cortex-m7-fpsp.cmake
new file mode 100644
index 000000000..b10f00fc2
--- /dev/null
+++ b/examples/build_system/cmake/cpu/cortex-m7-fpsp.cmake
@@ -0,0 +1,25 @@
+if (TOOLCHAIN STREQUAL "gcc")
+  set(TOOLCHAIN_COMMON_FLAGS
+    -mthumb
+    -mcpu=cortex-m7
+    -mfloat-abi=hard
+    -mfpu=fpv5-sp-d16
+    )
+  set(FREERTOS_PORT GCC_ARM_CM7 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=cortex-m7
+    -mfpu=fpv5-sp-d16
+    )
+  set(FREERTOS_PORT GCC_ARM_CM7 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --cpu cortex-m7
+    --fpu VFPv5_sp
+    )
+  set(FREERTOS_PORT IAR_ARM_CM7 CACHE INTERNAL "")
+
+endif ()
diff --git a/examples/build_system/cmake/cpu/cortex-m7.cmake b/examples/build_system/cmake/cpu/cortex-m7.cmake
index 3e5f7f44b..b41dfded5 100644
--- a/examples/build_system/cmake/cpu/cortex-m7.cmake
+++ b/examples/build_system/cmake/cpu/cortex-m7.cmake
@@ -5,7 +5,14 @@ if (TOOLCHAIN STREQUAL "gcc")
     -mfloat-abi=hard
     -mfpu=fpv5-d16
     )
+  set(FREERTOS_PORT GCC_ARM_CM7 CACHE INTERNAL "")
 
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    --target=arm-none-eabi
+    -mcpu=cortex-m7
+    -mfpu=fpv5-d16
+    )
   set(FREERTOS_PORT GCC_ARM_CM7 CACHE INTERNAL "")
 
 elseif (TOOLCHAIN STREQUAL "iar")
@@ -13,7 +20,6 @@ elseif (TOOLCHAIN STREQUAL "iar")
     --cpu cortex-m7
     --fpu VFPv5_D16
     )
-
   set(FREERTOS_PORT IAR_ARM_CM7 CACHE INTERNAL "")
 
 endif ()
diff --git a/examples/build_system/cmake/cpu/msp430.cmake b/examples/build_system/cmake/cpu/msp430.cmake
new file mode 100644
index 000000000..584ec5741
--- /dev/null
+++ b/examples/build_system/cmake/cpu/msp430.cmake
@@ -0,0 +1,10 @@
+if (TOOLCHAIN STREQUAL "gcc")
+  set(FREERTOS_PORT GCC_MSP430F449 CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "clang")
+  message(FATAL_ERROR "Clang is not supported for this target")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+  set(FREERTOS_PORT IAR_MSP430 CACHE INTERNAL "")
+
+endif ()
diff --git a/examples/build_system/cmake/cpu/rv32i-ilp32.cmake b/examples/build_system/cmake/cpu/rv32i-ilp32.cmake
new file mode 100644
index 000000000..605c40ba1
--- /dev/null
+++ b/examples/build_system/cmake/cpu/rv32i-ilp32.cmake
@@ -0,0 +1,19 @@
+if (TOOLCHAIN STREQUAL "gcc")
+  set(TOOLCHAIN_COMMON_FLAGS
+    -march=rv32i_zicsr
+    -mabi=ilp32
+    )
+  set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    -march=rv32i_zicsr
+    -mabi=ilp32
+    )
+  set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+  message(FATAL_ERROR "IAR not supported")
+  set(FREERTOS_PORT IAR_RISC_V CACHE INTERNAL "")
+
+endif ()
diff --git a/examples/build_system/cmake/cpu/rv32imac-ilp32.cmake b/examples/build_system/cmake/cpu/rv32imac-ilp32.cmake
new file mode 100644
index 000000000..584d90519
--- /dev/null
+++ b/examples/build_system/cmake/cpu/rv32imac-ilp32.cmake
@@ -0,0 +1,18 @@
+if (TOOLCHAIN STREQUAL "gcc")
+  set(TOOLCHAIN_COMMON_FLAGS
+    -march=rv32imac_zicsr
+    -mabi=ilp32
+    )
+  set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "clang")
+  set(TOOLCHAIN_COMMON_FLAGS
+    -march=rv32imac_zicsr
+    -mabi=ilp32
+    )
+  set(FREERTOS_PORT GCC_RISC_V CACHE INTERNAL "")
+
+elseif (TOOLCHAIN STREQUAL "iar")
+  message(FATAL_ERROR "IAR not supported")
+  set(FREERTOS_PORT IAR_RISC_V CACHE INTERNAL "")
+endif ()
diff --git a/examples/build_system/cmake/toolchain/aarch64_gcc.cmake b/examples/build_system/cmake/toolchain/aarch64_gcc.cmake
new file mode 100644
index 000000000..2d30a0b71
--- /dev/null
+++ b/examples/build_system/cmake/toolchain/aarch64_gcc.cmake
@@ -0,0 +1,21 @@
+if (NOT DEFINED CMAKE_C_COMPILER)
+  set(CMAKE_C_COMPILER "aarch64-none-elf-gcc")
+endif ()
+
+if (NOT DEFINED CMAKE_CXX_COMPILER)
+  set(CMAKE_CXX_COMPILER "aarch64-none-elf-g++")
+endif ()
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+set(CMAKE_SIZE "aarch64-none-elf-size" CACHE FILEPATH "")
+set(CMAKE_OBJCOPY "aarch64-none-elf-objcopy" CACHE FILEPATH "")
+set(CMAKE_OBJDUMP "aarch64-none-elf-objdump" CACHE FILEPATH "")
+
+include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
+
+get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
+if (IS_IN_TRY_COMPILE)
+  set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
+  set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
+  cmake_print_variables(CMAKE_C_LINK_FLAGS)
+endif ()
diff --git a/examples/build_system/cmake/toolchain/arm_clang.cmake b/examples/build_system/cmake/toolchain/arm_clang.cmake
new file mode 100644
index 000000000..754d672fd
--- /dev/null
+++ b/examples/build_system/cmake/toolchain/arm_clang.cmake
@@ -0,0 +1,21 @@
+if (NOT DEFINED CMAKE_C_COMPILER)
+  set(CMAKE_C_COMPILER "clang")
+endif ()
+
+if (NOT DEFINED CMAKE_CXX_COMPILER)
+  set(CMAKE_CXX_COMPILER "clang++")
+endif ()
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+set(CMAKE_SIZE "llvm-size" CACHE FILEPATH "")
+set(CMAKE_OBJCOPY "llvm-objcopy" CACHE FILEPATH "")
+set(CMAKE_OBJDUMP "llvm-objdump" CACHE FILEPATH "")
+
+include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
+
+get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
+if (IS_IN_TRY_COMPILE)
+  set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
+  set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
+  cmake_print_variables(CMAKE_C_LINK_FLAGS)
+endif ()
diff --git a/examples/build_system/cmake/toolchain/arm_gcc.cmake b/examples/build_system/cmake/toolchain/arm_gcc.cmake
index d3fd5b557..d3d73c629 100644
--- a/examples/build_system/cmake/toolchain/arm_gcc.cmake
+++ b/examples/build_system/cmake/toolchain/arm_gcc.cmake
@@ -1,5 +1,3 @@
-set(CMAKE_SYSTEM_NAME Generic)
-
 if (NOT DEFINED CMAKE_C_COMPILER)
   set(CMAKE_C_COMPILER "arm-none-eabi-gcc")
 endif ()
@@ -9,44 +7,15 @@ if (NOT DEFINED CMAKE_CXX_COMPILER)
 endif ()
 
 set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
-
 set(CMAKE_SIZE "arm-none-eabi-size" CACHE FILEPATH "")
 set(CMAKE_OBJCOPY "arm-none-eabi-objcopy" CACHE FILEPATH "")
 set(CMAKE_OBJDUMP "arm-none-eabi-objdump" CACHE FILEPATH "")
 
-set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
+include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
 
-# Look for includes and libraries only in the target system prefix.
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-
-# pass TOOLCHAIN_CPU to
-set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_SYSTEM_PROCESSOR)
-
-include(${CMAKE_CURRENT_LIST_DIR}/../cpu/${CMAKE_SYSTEM_PROCESSOR}.cmake)
-
-# enable all possible warnings for building examples
-list(APPEND TOOLCHAIN_COMMON_FLAGS
-  -fdata-sections
-  -ffunction-sections
-  -fsingle-precision-constant
-  -fno-strict-aliasing
-  )
-
-list(APPEND TOOLCHAIN_EXE_LINKER_FLAGS
-  -Wl,--print-memory-usage
-  -Wl,--gc-sections
-  -Wl,--cref
-  )
-
-include(${CMAKE_CURRENT_LIST_DIR}/set_flags.cmake)
-
-# try_compile is cmake test compiling its own example,
-# pass -nostdlib to skip stdlib linking
 get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
 if (IS_IN_TRY_COMPILE)
   set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
   set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
+  cmake_print_variables(CMAKE_C_LINK_FLAGS)
 endif ()
diff --git a/examples/build_system/cmake/toolchain/arm_iar.cmake b/examples/build_system/cmake/toolchain/arm_iar.cmake
index 1482624e5..6d2219ca8 100644
--- a/examples/build_system/cmake/toolchain/arm_iar.cmake
+++ b/examples/build_system/cmake/toolchain/arm_iar.cmake
@@ -1,32 +1,17 @@
-set(CMAKE_SYSTEM_NAME Generic)
+if (NOT DEFINED CMAKE_C_COMPILER)
+  set(CMAKE_C_COMPILER "iccarm")
+endif()
 
-set(CMAKE_C_COMPILER "iccarm")
-set(CMAKE_CXX_COMPILER "iccarm")
-set(CMAKE_ASM_COMPILER "iasmarm")
+if (NOT DEFINED CMAKE_CXX_COMPILER)
+  set(CMAKE_CXX_COMPILER "iccarm")
+endif()
+
+if (NOT DEFINED CMAKE_ASM_COMPILER)
+  set(CMAKE_ASM_COMPILER "iasmarm")
+endif()
 
 set(CMAKE_SIZE "size" CACHE FILEPATH "")
 set(CMAKE_OBJCOPY "ielftool" CACHE FILEPATH "")
 set(CMAKE_OBJDUMP "iefdumparm" CACHE FILEPATH "")
 
-set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
-
-# Look for includes and libraries only in the target system prefix.
-set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
-set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
-
-# pass TOOLCHAIN_CPU to
-set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_SYSTEM_PROCESSOR)
-
-include(${CMAKE_CURRENT_LIST_DIR}/../cpu/${CMAKE_SYSTEM_PROCESSOR}.cmake)
-
-# enable all possible warnings for building examples
-list(APPEND TOOLCHAIN_COMMON_FLAGS
-  )
-
-list(APPEND TOOLCHAIN_EXE_LINKER_FLAGS
-  --diag_suppress=Li065
-  )
-
-include(${CMAKE_CURRENT_LIST_DIR}/set_flags.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
diff --git a/examples/build_system/cmake/toolchain/common.cmake b/examples/build_system/cmake/toolchain/common.cmake
new file mode 100644
index 000000000..688715914
--- /dev/null
+++ b/examples/build_system/cmake/toolchain/common.cmake
@@ -0,0 +1,64 @@
+include(CMakePrintHelpers)
+
+# ----------------------------------------------------------------------------
+# Common
+# ----------------------------------------------------------------------------
+set(CMAKE_SYSTEM_NAME Generic)
+set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE)
+
+# Look for includes and libraries only in the target system prefix.
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+
+# pass TOOLCHAIN_CPU to
+set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_SYSTEM_PROCESSOR)
+include(${CMAKE_CURRENT_LIST_DIR}/../cpu/${CMAKE_SYSTEM_PROCESSOR}.cmake)
+
+# ----------------------------------------------------------------------------
+# Compile flags
+# ----------------------------------------------------------------------------
+if (TOOLCHAIN STREQUAL "gcc")
+  list(APPEND TOOLCHAIN_COMMON_FLAGS
+    -fdata-sections
+    -ffunction-sections
+    -fsingle-precision-constant
+    -fno-strict-aliasing
+    )
+  list(APPEND TOOLCHAIN_EXE_LINKER_FLAGS
+    -Wl,--print-memory-usage
+    -Wl,--gc-sections
+    -Wl,--cref
+    )
+
+elseif (TOOLCHAIN STREQUAL "iar")
+  #list(APPEND TOOLCHAIN_COMMON_FLAGS)
+  list(APPEND TOOLCHAIN_EXE_LINKER_FLAGS
+    --diag_suppress=Li065
+    )
+
+elseif (TOOLCHAIN STREQUAL "clang")
+  list(APPEND TOOLCHAIN_COMMON_FLAGS
+    -fdata-sections
+    -ffunction-sections
+    -fno-strict-aliasing
+    )
+  list(APPEND TOOLCHAIN_EXE_LINKER_FLAGS
+    -Wl,--print-memory-usage
+    -Wl,--gc-sections
+    -Wl,--cref
+    )
+endif ()
+
+# join the toolchain flags into a single string
+list(JOIN TOOLCHAIN_COMMON_FLAGS " " TOOLCHAIN_COMMON_FLAGS)
+foreach (LANG IN ITEMS C CXX ASM)
+  set(CMAKE_${LANG}_FLAGS_INIT ${TOOLCHAIN_COMMON_FLAGS})
+  # optimization flags for LOG, LOGGER ?
+  #set(CMAKE_${LANG}_FLAGS_RELEASE_INIT "-Os")
+  #set(CMAKE_${LANG}_FLAGS_DEBUG_INIT "-O0")
+endforeach ()
+
+# Linker
+list(JOIN TOOLCHAIN_EXE_LINKER_FLAGS " " CMAKE_EXE_LINKER_FLAGS_INIT)
diff --git a/examples/build_system/cmake/toolchain/msp430_gcc.cmake b/examples/build_system/cmake/toolchain/msp430_gcc.cmake
new file mode 100644
index 000000000..73368adba
--- /dev/null
+++ b/examples/build_system/cmake/toolchain/msp430_gcc.cmake
@@ -0,0 +1,15 @@
+if (NOT DEFINED CMAKE_C_COMPILER)
+  set(CMAKE_C_COMPILER "msp430-elf-gcc")
+endif ()
+
+if (NOT DEFINED CMAKE_CXX_COMPILER)
+  set(CMAKE_CXX_COMPILER "msp430-elf-g++")
+endif ()
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+
+set(CMAKE_SIZE "msp430-elf-size" CACHE FILEPATH "")
+set(CMAKE_OBJCOPY "msp430-elf-objcopy" CACHE FILEPATH "")
+set(CMAKE_OBJDUMP "msp430-elf-objdump" CACHE FILEPATH "")
+
+include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
diff --git a/examples/build_system/cmake/toolchain/riscv_gcc.cmake b/examples/build_system/cmake/toolchain/riscv_gcc.cmake
new file mode 100644
index 000000000..d788df023
--- /dev/null
+++ b/examples/build_system/cmake/toolchain/riscv_gcc.cmake
@@ -0,0 +1,30 @@
+# default Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
+if (NOT DEFINED CROSS_COMPILE)
+  set(CROSS_COMPILE "riscv-none-elf-")
+endif ()
+
+if (NOT DEFINED CMAKE_C_COMPILER)
+  set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc)
+endif ()
+
+if (NOT DEFINED CMAKE_C_COMPILER)
+  set(CMAKE_C_COMPILER ${CROSS_COMPILE}gcc)
+endif ()
+
+if (NOT DEFINED CMAKE_CXX_COMPILER)
+  set(CMAKE_CXX_COMPILER ${CROSS_COMPILE}g++)
+endif ()
+
+set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
+set(CMAKE_SIZE ${CROSS_COMPILE}size CACHE FILEPATH "")
+set(CMAKE_OBJCOPY ${CROSS_COMPILE}objcopy CACHE FILEPATH "")
+set(CMAKE_OBJDUMP ${CROSS_COMPILE}objdump CACHE FILEPATH "")
+
+include(${CMAKE_CURRENT_LIST_DIR}/common.cmake)
+
+get_property(IS_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
+if (IS_IN_TRY_COMPILE)
+  set(CMAKE_C_LINK_FLAGS "${CMAKE_C_LINK_FLAGS} -nostdlib")
+  set(CMAKE_CXX_LINK_FLAGS "${CMAKE_CXX_LINK_FLAGS} -nostdlib")
+  cmake_print_variables(CMAKE_C_LINK_FLAGS)
+endif ()
diff --git a/examples/build_system/cmake/toolchain/set_flags.cmake b/examples/build_system/cmake/toolchain/set_flags.cmake
deleted file mode 100644
index 44930ef4d..000000000
--- a/examples/build_system/cmake/toolchain/set_flags.cmake
+++ /dev/null
@@ -1,17 +0,0 @@
-include(CMakePrintHelpers)
-
-# join the toolchain flags into a single string
-list(JOIN TOOLCHAIN_COMMON_FLAGS " " TOOLCHAIN_COMMON_FLAGS)
-
-foreach (LANG IN ITEMS C CXX ASM)
-  set(CMAKE_${LANG}_FLAGS_INIT ${TOOLCHAIN_COMMON_FLAGS})
-
-  #cmake_print_variables(CMAKE_${LANG}_FLAGS_INIT)
-
-  # optimization flags for LOG, LOGGER ?
-  #set(CMAKE_${LANG}_FLAGS_RELEASE_INIT "-Os")
-  #set(CMAKE_${LANG}_FLAGS_DEBUG_INIT "-O0")
-endforeach ()
-
-# Linker
-list(JOIN TOOLCHAIN_EXE_LINKER_FLAGS " " CMAKE_EXE_LINKER_FLAGS_INIT)
diff --git a/examples/build_system/make/cpu/arm1176.mk b/examples/build_system/make/cpu/arm1176jzf-s.mk
similarity index 100%
rename from examples/build_system/make/cpu/arm1176.mk
rename to examples/build_system/make/cpu/arm1176jzf-s.mk
diff --git a/examples/build_system/make/cpu/arm926ej-s.mk b/examples/build_system/make/cpu/arm926ej-s.mk
new file mode 100644
index 000000000..5b84f514f
--- /dev/null
+++ b/examples/build_system/make/cpu/arm926ej-s.mk
@@ -0,0 +1,9 @@
+ifeq ($(TOOLCHAIN),gcc)
+  CFLAGS += \
+    -mcpu=arm926ej-s \
+
+else ifeq ($(TOOLCHAIN),iar)
+	#CFLAGS += --cpu cortex-a53
+	#ASFLAGS += --cpu cortex-a53
+
+endif
diff --git a/examples/build_system/make/cpu/cortex-m0.mk b/examples/build_system/make/cpu/cortex-m0.mk
index feb0f395b..c2c33a2ee 100644
--- a/examples/build_system/make/cpu/cortex-m0.mk
+++ b/examples/build_system/make/cpu/cortex-m0.mk
@@ -4,10 +4,18 @@ ifeq ($(TOOLCHAIN),gcc)
     -mcpu=cortex-m0 \
     -mfloat-abi=soft \
 
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+	  --target=arm-none-eabi \
+	  -mcpu=cortex-m0 \
+
 else ifeq ($(TOOLCHAIN),iar)
   # IAR Flags
   CFLAGS += --cpu cortex-m0
   ASFLAGS += --cpu cortex-m0
+
+else
+  $(error "TOOLCHAIN is not supported")
 endif
 
 # For freeRTOS port source
diff --git a/examples/build_system/make/cpu/cortex-m0plus.mk b/examples/build_system/make/cpu/cortex-m0plus.mk
index b416b7a4a..fe8feb227 100644
--- a/examples/build_system/make/cpu/cortex-m0plus.mk
+++ b/examples/build_system/make/cpu/cortex-m0plus.mk
@@ -4,10 +4,18 @@ ifeq ($(TOOLCHAIN),gcc)
     -mcpu=cortex-m0plus \
     -mfloat-abi=soft \
 
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+	  --target=arm-none-eabi \
+	  -mcpu=cortex-m0plus \
+
 else ifeq ($(TOOLCHAIN),iar)
   # IAR Flags
   CFLAGS += --cpu cortex-m0+
   ASFLAGS += --cpu cortex-m0+
+
+else
+  $(error "TOOLCHAIN is not supported")
 endif
 
 # For freeRTOS port source
diff --git a/examples/build_system/make/cpu/cortex-m23.mk b/examples/build_system/make/cpu/cortex-m23.mk
index 29542d8e8..7ab758352 100644
--- a/examples/build_system/make/cpu/cortex-m23.mk
+++ b/examples/build_system/make/cpu/cortex-m23.mk
@@ -4,10 +4,18 @@ ifeq ($(TOOLCHAIN),gcc)
     -mcpu=cortex-m23 \
     -mfloat-abi=soft \
 
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+	  --target=arm-none-eabi \
+	  -mcpu=cortex-m23 \
+
 else ifeq ($(TOOLCHAIN),iar)
   # IAR Flags
   CFLAGS += --cpu cortex-m23
   ASFLAGS += --cpu cortex-m23
+
+else
+  $(error "TOOLCHAIN is not supported")
 endif
 
 # For freeRTOS port source
diff --git a/examples/build_system/make/cpu/cortex-m3.mk b/examples/build_system/make/cpu/cortex-m3.mk
index 7a34b9e04..b6325313f 100644
--- a/examples/build_system/make/cpu/cortex-m3.mk
+++ b/examples/build_system/make/cpu/cortex-m3.mk
@@ -4,13 +4,18 @@ ifeq ($(TOOLCHAIN),gcc)
     -mcpu=cortex-m3 \
     -mfloat-abi=soft \
 
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+	  --target=arm-none-eabi \
+	  -mcpu=cortex-m3 \
+
 else ifeq ($(TOOLCHAIN),iar)
   # IAR Flags
-  CFLAGS += \
-    --cpu cortex-m3 \
+  CFLAGS += --cpu cortex-m3
+  ASFLAGS += --cpu cortex-m3
 
-  ASFLAGS += \
-     --cpu cortex-m3
+else
+  $(error "TOOLCHAIN is not supported")
 endif
 
 # For freeRTOS port source
diff --git a/examples/build_system/make/cpu/cortex-m33-nodsp-nofp.mk b/examples/build_system/make/cpu/cortex-m33-nodsp-nofp.mk
index c65f24376..405053dd0 100644
--- a/examples/build_system/make/cpu/cortex-m33-nodsp-nofp.mk
+++ b/examples/build_system/make/cpu/cortex-m33-nodsp-nofp.mk
@@ -1,16 +1,24 @@
 ifeq ($(TOOLCHAIN),gcc)
-	CFLAGS += \
+  CFLAGS += \
 	-mthumb \
 	-mcpu=cortex-m33+nodsp \
 	-mfloat-abi=soft \
 
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+	  --target=arm-none-eabi \
+	  -mcpu=cortex-m33 \
+	  -mfpu=softvp \
+
 else ifeq ($(TOOLCHAIN),iar)
-	CFLAGS += \
+  CFLAGS += \
 		--cpu cortex-m33+nodsp \
 
-	ASFLAGS += \
+  ASFLAGS += \
 		--cpu cortex-m33+nodsp \
 
+else
+  $(error "TOOLCHAIN is not supported")
 endif
 
 FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM33_NTZ/non_secure
diff --git a/examples/build_system/make/cpu/cortex-m33.mk b/examples/build_system/make/cpu/cortex-m33.mk
index fe5b7b380..47b0eaecd 100644
--- a/examples/build_system/make/cpu/cortex-m33.mk
+++ b/examples/build_system/make/cpu/cortex-m33.mk
@@ -5,15 +5,23 @@ ifeq ($(TOOLCHAIN),gcc)
     -mfloat-abi=hard \
     -mfpu=fpv5-sp-d16 \
 
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+	  --target=arm-none-eabi \
+	  -mcpu=cortex-m33 \
+	  -mfpu=fpv5-sp-d16 \
+
 else ifeq ($(TOOLCHAIN),iar)
-	CFLAGS += \
+  CFLAGS += \
 		--cpu cortex-m33 \
 		--fpu VFPv5-SP \
 
-	ASFLAGS += \
+  ASFLAGS += \
 		--cpu cortex-m33 \
 		--fpu VFPv5-SP \
 
+else
+  $(error "TOOLCHAIN is not supported")
 endif
 
 FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM33_NTZ/non_secure
diff --git a/examples/build_system/make/cpu/cortex-m4.mk b/examples/build_system/make/cpu/cortex-m4.mk
index d8776b5d8..4e16819d1 100644
--- a/examples/build_system/make/cpu/cortex-m4.mk
+++ b/examples/build_system/make/cpu/cortex-m4.mk
@@ -5,9 +5,18 @@ ifeq ($(TOOLCHAIN),gcc)
     -mfloat-abi=hard \
     -mfpu=fpv4-sp-d16 \
 
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+	  --target=arm-none-eabi \
+	  -mcpu=cortex-m4 \
+	  -mfpu=fpv4-sp-d16 \
+
 else ifeq ($(TOOLCHAIN),iar)
   CFLAGS += --cpu cortex-m4 --fpu VFPv4
   ASFLAGS += --cpu cortex-m4 --fpu VFPv4
+
+else
+  $(error "TOOLCHAIN is not supported")
 endif
 
 FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM4F
diff --git a/examples/build_system/make/cpu/cortex-m7-fpsp.mk b/examples/build_system/make/cpu/cortex-m7-fpsp.mk
new file mode 100644
index 000000000..cd42c6fb8
--- /dev/null
+++ b/examples/build_system/make/cpu/cortex-m7-fpsp.mk
@@ -0,0 +1,27 @@
+ifeq ($(TOOLCHAIN),gcc)
+  CFLAGS += \
+    -mthumb \
+    -mcpu=cortex-m7 \
+    -mfloat-abi=hard \
+    -mfpu=fpv5-sp-d16 \
+
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+	  --target=arm-none-eabi \
+	  -mcpu=cortex-m7 \
+	  -mfpu=fpv5-sp-d16 \
+
+else ifeq ($(TOOLCHAIN),iar)
+  CFLAGS += \
+  	--cpu cortex-m7 \
+  	--fpu VFPv5_sp \
+
+  ASFLAGS += \
+		--cpu cortex-m7 \
+		--fpu VFPv5_sp \
+
+else
+  $(error "TOOLCHAIN is not supported")
+endif
+
+FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM7/r0p1
diff --git a/examples/build_system/make/cpu/cortex-m7.mk b/examples/build_system/make/cpu/cortex-m7.mk
index 0e3461787..3e6116179 100644
--- a/examples/build_system/make/cpu/cortex-m7.mk
+++ b/examples/build_system/make/cpu/cortex-m7.mk
@@ -5,6 +5,12 @@ ifeq ($(TOOLCHAIN),gcc)
     -mfloat-abi=hard \
     -mfpu=fpv5-d16 \
 
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+	  --target=arm-none-eabi \
+	  -mcpu=cortex-m7 \
+	  -mfpu=fpv5-d16 \
+
 else ifeq ($(TOOLCHAIN),iar)
   CFLAGS += \
   	--cpu cortex-m7 \
@@ -14,6 +20,8 @@ else ifeq ($(TOOLCHAIN),iar)
 		--cpu cortex-m7 \
 		--fpu VFPv5_D16 \
 
+else
+  $(error "TOOLCHAIN is not supported")
 endif
 
 FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/ARM_CM7/r0p1
diff --git a/examples/build_system/make/cpu/msp430.mk b/examples/build_system/make/cpu/msp430.mk
new file mode 100644
index 000000000..6daa2c38d
--- /dev/null
+++ b/examples/build_system/make/cpu/msp430.mk
@@ -0,0 +1,12 @@
+ifeq ($(TOOLCHAIN),gcc)
+  # nothing to add
+else ifeq ($(TOOLCHAIN),clang)
+  # nothing to add
+else ifeq ($(TOOLCHAIN),iar)
+  # nothing to add
+else
+  $(error "TOOLCHAIN is not supported")
+endif
+
+# For freeRTOS port source
+FREERTOS_PORTABLE_SRC ?= $(FREERTOS_PORTABLE_PATH)/GCC_MSP430F449
diff --git a/examples/build_system/make/cpu/rv32i-ilp32.mk b/examples/build_system/make/cpu/rv32i-ilp32.mk
new file mode 100644
index 000000000..af764afc5
--- /dev/null
+++ b/examples/build_system/make/cpu/rv32i-ilp32.mk
@@ -0,0 +1,16 @@
+ifeq ($(TOOLCHAIN),gcc)
+  CFLAGS += \
+    -march=rv32i_zicsr \
+    -mabi=ilp32 \
+
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+    -march=rv32i_zicsr \
+    -mabi=ilp32 \
+
+else ifeq ($(TOOLCHAIN),iar)
+  $(error not support)
+endif
+
+# For freeRTOS port source
+FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V
diff --git a/examples/build_system/make/cpu/rv32imac-ilp32.mk b/examples/build_system/make/cpu/rv32imac-ilp32.mk
new file mode 100644
index 000000000..19c322ebc
--- /dev/null
+++ b/examples/build_system/make/cpu/rv32imac-ilp32.mk
@@ -0,0 +1,17 @@
+ifeq ($(TOOLCHAIN),gcc)
+  CFLAGS += \
+    -march=rv32imac_zicsr \
+    -mabi=ilp32 \
+
+else ifeq ($(TOOLCHAIN),clang)
+  CFLAGS += \
+    -march=rv32imac_zicsr \
+    -mabi=ilp32 \
+
+else ifeq ($(TOOLCHAIN),iar)
+  $(error not support)
+
+endif
+
+# For freeRTOS port source
+FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V
diff --git a/examples/build_system/make/make.mk b/examples/build_system/make/make.mk
index 772befca4..1500a51e0 100644
--- a/examples/build_system/make/make.mk
+++ b/examples/build_system/make/make.mk
@@ -2,6 +2,24 @@
 # Common make definition for all examples
 # ---------------------------------------
 
+#-------------------------------------------------------------
+# Toolchain
+# Can be changed via TOOLCHAIN=gcc|iar or CC=arm-none-eabi-gcc|iccarm|clang
+#-------------------------------------------------------------
+
+ifneq (,$(findstring clang,$(CC)))
+  TOOLCHAIN = clang
+else ifneq (,$(findstring iccarm,$(CC)))
+  TOOLCHAIN = iar
+else ifneq (,$(findstring gcc,$(CC)))
+  TOOLCHAIN = gcc
+endif
+
+# Default to GCC
+ifndef TOOLCHAIN
+  TOOLCHAIN = gcc
+endif
+
 #-------------- TOP and CURRENT_PATH ------------
 
 # Set TOP to be the path to get from the current directory (where make was invoked) to the top of the tree.
@@ -15,6 +33,8 @@ TOP = $(abspath $(subst make.mk,../../..,$(THIS_MAKEFILE)))
 # Set CURRENT_PATH to the relative path from TOP to the current directory, ie examples/device/cdc_msc_freertos
 CURRENT_PATH = $(subst $(TOP)/,,$(abspath .))
 
+#-------------- Linux/Windows ------------
+
 # Detect whether shell style is windows or not
 # https://stackoverflow.com/questions/714100/os-detecting-makefile/52062069#52062069
 ifeq '$(findstring ;,$(PATH))' ';'
@@ -26,13 +46,18 @@ CMDEXE := 1
 SHELL := cmd.exe
 endif
 
-# Handy check parameter function
-check_defined = \
-    $(strip $(foreach 1,$1, \
-    $(call __check_defined,$1,$(strip $(value 2)))))
-__check_defined = \
-    $(if $(value $1),, \
-    $(error Undefined make flag: $1$(if $2, ($2))))
+ifeq ($(CMDEXE),1)
+  CP = copy
+  RM = del
+  MKDIR = mkdir
+  PYTHON = python
+else
+  CP = cp
+  RM = rm
+  MKDIR = mkdir
+  PYTHON = python3
+endif
+
 
 # Build directory
 BUILD := _build/$(BOARD)
@@ -44,8 +69,8 @@ BIN := $(TOP)/_bin/$(BOARD)/$(notdir $(CURDIR))
 
 # Board without family
 ifneq ($(wildcard $(TOP)/hw/bsp/$(BOARD)/board.mk),)
-BOARD_PATH := hw/bsp/$(BOARD)
-FAMILY :=
+  BOARD_PATH := hw/bsp/$(BOARD)
+  FAMILY :=
 endif
 
 # Board within family
@@ -68,31 +93,6 @@ else
   SRC_C += $(subst $(TOP)/,,$(wildcard $(TOP)/$(FAMILY_PATH)/*.c))
 endif
 
-#-------------- Toolchain  ------------
-
-# Supported toolchain: gcc, iar
-TOOLCHAIN ?= gcc
-
-# Can be set by board, default to ARM GCC
-CROSS_COMPILE ?= arm-none-eabi-
-
-ifeq ($(TOOLCHAIN),iar)
-CC := iccarm
-USE_IAR = 1
-endif
-
-ifeq ($(CMDEXE),1)
-  CP = copy
-  RM = del
-  MKDIR = mkdir
-  PYTHON = python
-else
-  CP = cp
-  RM = rm
-  MKDIR = mkdir
-  PYTHON = python3
-endif
-
 #-------------- Source files and compiler flags --------------
 # tinyusb makefile
 include $(TOP)/src/tinyusb.mk
@@ -109,6 +109,10 @@ INC += \
 BOARD_UPPER = $(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$(subst -,_,$(BOARD))))))))))))))))))))))))))))
 CFLAGS += -DBOARD_$(BOARD_UPPER)
 
+ifdef CFLAGS_CLI
+	CFLAGS += $(CFLAGS_CLI)
+endif
+
 # use max3421 as host controller
 ifeq (${MAX3421_HOST},1)
   SRC_C += src/portable/analog/max3421/hcd_max3421.c
@@ -124,7 +128,7 @@ endif
 
 # Logger: default is uart, can be set to rtt or swo
 ifneq ($(LOGGER),)
-	CMAKE_DEFSYM +=	-DLOGGER=$(LOGGER)
+  CMAKE_DEFSYM +=	-DLOGGER=$(LOGGER)
 endif
 
 ifeq ($(LOGGER),rtt)
@@ -143,3 +147,11 @@ endif
 
 # toolchain specific
 include ${TOP}/examples/build_system/make/toolchain/arm_$(TOOLCHAIN).mk
+
+# Handy check parameter function
+check_defined = \
+    $(strip $(foreach 1,$1, \
+    $(call __check_defined,$1,$(strip $(value 2)))))
+__check_defined = \
+    $(if $(value $1),, \
+    $(error Undefined make flag: $1$(if $2, ($2))))
diff --git a/examples/build_system/make/rules.mk b/examples/build_system/make/rules.mk
index b02665cdd..f322dbae6 100644
--- a/examples/build_system/make/rules.mk
+++ b/examples/build_system/make/rules.mk
@@ -9,19 +9,6 @@
 # ESP32-Sx and RP2040 has its own CMake build system
 ifeq (,$(findstring $(FAMILY),espressif rp2040))
 
-# ---------------------------------------
-# Compiler Flags
-# ---------------------------------------
-
-CFLAGS += $(addprefix -I,$(INC))
-
-# Verbose mode
-ifeq ("$(V)","1")
-$(info CFLAGS  $(CFLAGS) ) $(info )
-$(info LDFLAGS $(LDFLAGS)) $(info )
-$(info ASFLAGS $(ASFLAGS)) $(info )
-endif
-
 # ---------------------------------------
 # Rules
 # ---------------------------------------
@@ -37,7 +24,20 @@ vpath %.c . $(TOP)
 vpath %.s . $(TOP)
 vpath %.S . $(TOP)
 
-include ${TOP}/examples/build_system/make/toolchain/arm_$(TOOLCHAIN)_rules.mk
+include ${TOP}/examples/build_system/make/toolchain/$(TOOLCHAIN)_rules.mk
+
+# ---------------------------------------
+# Compiler Flags
+# ---------------------------------------
+
+CFLAGS += $(addprefix -I,$(INC))
+
+# Verbose mode
+ifeq ("$(V)","1")
+$(info CFLAGS  $(CFLAGS) ) $(info )
+$(info LDFLAGS $(LDFLAGS)) $(info )
+$(info ASFLAGS $(ASFLAGS)) $(info )
+endif
 
 
 OBJ_DIRS = $(sort $(dir $(OBJ)))
@@ -134,6 +134,23 @@ OPENOCD_OPTION ?=
 flash-openocd: $(BUILD)/$(PROJECT).elf
 	openocd $(OPENOCD_OPTION) -c "program $< verify reset exit"
 
+# --------------- openocd-wch -----------------
+# wch-linke is not supported yet in official openOCD yet. We need to either use
+# 1. download openocd as part of mounriver studio http://www.mounriver.com/download or
+# 2. compiled from https://github.com/hathach/riscv-openocd-wch or
+#    https://github.com/dragonlock2/miscboards/blob/main/wch/SDK/riscv-openocd.tar.xz
+#    with  ./configure --disable-werror --enable-wlinke --enable-ch347=no
+OPENOCD_WCH ?= /home/${USER}/app/riscv-openocd-wch/src/openocd
+OPENOCD_WCH_OPTION ?=
+flash-openocd-wch: $(BUILD)/$(PROJECT).elf
+	$(OPENOCD_WCH) $(OPENOCD_WCH_OPTION) -c init -c halt -c "flash write_image $<" -c reset -c exit
+
+# --------------- wlink-rs -----------------
+# flash with https://github.com/ch32-rs/wlink
+WLINK_RS ?= wlink
+flash-wlink-rs: $(BUILD)/$(PROJECT).elf
+	$(WLINK_RS) flash $<
+
 # --------------- dfu-util -----------------
 DFU_UTIL_OPTION ?= -a 0
 flash-dfu-util: $(BUILD)/$(PROJECT).bin
diff --git a/examples/build_system/make/toolchain/arm_clang.mk b/examples/build_system/make/toolchain/arm_clang.mk
new file mode 100644
index 000000000..97face39c
--- /dev/null
+++ b/examples/build_system/make/toolchain/arm_clang.mk
@@ -0,0 +1,10 @@
+CC = clang
+CXX = clang++
+AS = $(CC) -x assembler-with-cpp
+LD = $(CC)
+
+GDB = $(CROSS_COMPILE)gdb
+OBJCOPY = llvm-objcopy
+SIZE = llvm-size
+
+include ${TOP}/examples/build_system/make/toolchain/gcc_common.mk
diff --git a/examples/build_system/make/toolchain/arm_gcc.mk b/examples/build_system/make/toolchain/arm_gcc.mk
index ed5ddc970..a8576f8ee 100644
--- a/examples/build_system/make/toolchain/arm_gcc.mk
+++ b/examples/build_system/make/toolchain/arm_gcc.mk
@@ -1,5 +1,8 @@
 # makefile for arm gcc toolchain
 
+# Can be set by family, default to ARM GCC
+CROSS_COMPILE ?= arm-none-eabi-
+
 CC = $(CROSS_COMPILE)gcc
 CXX = $(CROSS_COMPILE)g++
 AS = $(CC) -x assembler-with-cpp
@@ -9,73 +12,9 @@ GDB = $(CROSS_COMPILE)gdb
 OBJCOPY = $(CROSS_COMPILE)objcopy
 SIZE = $(CROSS_COMPILE)size
 
-CC_VERSION := $(shell $(CC) -dumpversion)
-CC_VERSION_MAJOR = $(firstword $(subst ., ,$(CC_VERSION)))
-
-# ---------------------------------------
-# Compiler Flags
-# ---------------------------------------
 CFLAGS += \
-  -MD \
-  -ggdb \
-  -fdata-sections \
-  -ffunction-sections \
   -fsingle-precision-constant \
-  -fno-strict-aliasing \
-  -Wall \
-  -Wextra \
-  -Werror \
-  -Wfatal-errors \
-  -Wdouble-promotion \
-  -Wstrict-prototypes \
-  -Wstrict-overflow \
-  -Werror-implicit-function-declaration \
-  -Wfloat-equal \
-  -Wundef \
-  -Wshadow \
-  -Wwrite-strings \
-  -Wsign-compare \
-  -Wmissing-format-attribute \
-  -Wunreachable-code \
-  -Wcast-align \
-  -Wcast-function-type \
-  -Wcast-qual \
-  -Wnull-dereference \
-  -Wuninitialized \
-  -Wunused \
-  -Wreturn-type \
-  -Wredundant-decls \
 
-# conversion is too strict for most mcu driver, may be disable sign/int/arith-conversion
-#  -Wconversion
+LIBS += -lgcc -lm -lnosys
 
-# Size Optimization as default
-CFLAGS_OPTIMIZED ?= -Os
-
-# Debugging/Optimization
-ifeq ($(DEBUG), 1)
-  CFLAGS += -O0
-  NO_LTO = 1
-else
-  CFLAGS += $(CFLAGS_OPTIMIZED)
-endif
-
-# ---------------------------------------
-# Linker Flags
-# ---------------------------------------
-LDFLAGS += \
-  -Wl,-Map=$@.map \
-  -Wl,-cref \
-  -Wl,-gc-sections \
-
-# renesas rx does not support --print-memory-usage flags
-ifneq ($(FAMILY),rx)
-LDFLAGS += -Wl,--print-memory-usage
-endif
-
-# from version 12
-ifeq ($(strip $(if $(CMDEXE),\
-               $(shell if $(CC_VERSION_MAJOR) geq 12 (echo 1) else (echo 0)),\
-               $(shell expr $(CC_VERSION_MAJOR) \>= 12))), 1)
-LDFLAGS += -Wl,--no-warn-rwx-segment
-endif
+include ${TOP}/examples/build_system/make/toolchain/gcc_common.mk
diff --git a/examples/build_system/make/toolchain/arm_iar.mk b/examples/build_system/make/toolchain/arm_iar.mk
index 04c9f22b3..17967b41a 100644
--- a/examples/build_system/make/toolchain/arm_iar.mk
+++ b/examples/build_system/make/toolchain/arm_iar.mk
@@ -1,4 +1,6 @@
 # makefile for arm iar toolchain
+
+CC = iccarm
 AS = iasmarm
 LD = ilinkarm
 OBJCOPY = ielftool --silent
diff --git a/examples/build_system/make/toolchain/clang_rules.mk b/examples/build_system/make/toolchain/clang_rules.mk
new file mode 100644
index 000000000..341f705b1
--- /dev/null
+++ b/examples/build_system/make/toolchain/clang_rules.mk
@@ -0,0 +1 @@
+include ${TOP}/examples/build_system/make/toolchain/gcc_rules.mk
diff --git a/examples/build_system/make/toolchain/gcc_common.mk b/examples/build_system/make/toolchain/gcc_common.mk
new file mode 100644
index 000000000..6986d8bba
--- /dev/null
+++ b/examples/build_system/make/toolchain/gcc_common.mk
@@ -0,0 +1,71 @@
+# ---------------------------------------
+# Compiler Flags
+# ---------------------------------------
+CFLAGS += \
+  -MD \
+  -ggdb \
+  -fdata-sections \
+  -ffunction-sections \
+  -fno-strict-aliasing \
+  -Wall \
+  -Wextra \
+  -Werror \
+  -Wfatal-errors \
+  -Wdouble-promotion \
+  -Wstrict-prototypes \
+  -Wstrict-overflow \
+  -Werror-implicit-function-declaration \
+  -Wfloat-equal \
+  -Wundef \
+  -Wshadow \
+  -Wwrite-strings \
+  -Wsign-compare \
+  -Wmissing-format-attribute \
+  -Wunreachable-code \
+  -Wcast-align \
+  -Wcast-function-type \
+  -Wcast-qual \
+  -Wnull-dereference \
+  -Wuninitialized \
+  -Wunused \
+  -Wreturn-type \
+  -Wredundant-decls \
+
+# conversion is too strict for most mcu driver, may be disable sign/int/arith-conversion
+#  -Wconversion
+
+# Size Optimization as default
+CFLAGS_OPTIMIZED ?= -Os
+
+# Debugging/Optimization
+ifeq ($(DEBUG), 1)
+  CFLAGS += -O0
+  NO_LTO = 1
+else
+  CFLAGS += $(CFLAGS_OPTIMIZED)
+endif
+
+# ---------------------------------------
+# Linker Flags
+# ---------------------------------------
+LDFLAGS += \
+  -Wl,-Map=$@.map \
+  -Wl,--cref \
+  -Wl,-gc-sections \
+
+# renesas rx does not support --print-memory-usage flags
+ifneq ($(FAMILY),rx)
+LDFLAGS += -Wl,--print-memory-usage
+endif
+
+ifeq ($(TOOLCHAIN),gcc)
+CC_VERSION := $(shell $(CC) -dumpversion)
+CC_VERSION_MAJOR = $(firstword $(subst ., ,$(CC_VERSION)))
+
+# from version 12
+ifeq ($(strip $(if $(CMDEXE),\
+               $(shell if $(CC_VERSION_MAJOR) geq 12 (echo 1) else (echo 0)),\
+               $(shell expr $(CC_VERSION_MAJOR) \>= 12))), 1)
+LDFLAGS += -Wl,--no-warn-rwx-segment
+endif
+endif
diff --git a/examples/build_system/make/toolchain/arm_gcc_rules.mk b/examples/build_system/make/toolchain/gcc_rules.mk
similarity index 91%
rename from examples/build_system/make/toolchain/arm_gcc_rules.mk
rename to examples/build_system/make/toolchain/gcc_rules.mk
index d295879d9..fc5225503 100644
--- a/examples/build_system/make/toolchain/arm_gcc_rules.mk
+++ b/examples/build_system/make/toolchain/gcc_rules.mk
@@ -21,8 +21,14 @@ ifneq ($(CFLAGS_SKIP),)
 CFLAGS := $(filter-out $(CFLAGS_SKIP),$(CFLAGS))
 endif
 
+ifeq ($(TOOLCHAIN),clang)
+CFLAGS += $(CFLAGS_CLANG)
+LDFLAGS += $(CFLAGS) $(LDFLAGS_CLANG)
+else
 LDFLAGS += $(CFLAGS) $(LDFLAGS_GCC)
+endif
 
+# TODO should be removed after all examples are updated
 ifdef LD_FILE
 LDFLAGS += -Wl,-T,$(TOP)/$(LD_FILE)
 endif
@@ -33,11 +39,7 @@ endif
 
 ASFLAGS += $(CFLAGS)
 
-LIBS_GCC ?= -lgcc -lm -lnosys
-
 # libc
-LIBS += $(LIBS_GCC)
-
 ifneq ($(BOARD), spresense)
 LIBS += -lc
 endif
diff --git a/examples/build_system/make/toolchain/arm_iar_rules.mk b/examples/build_system/make/toolchain/iar_rules.mk
similarity index 100%
rename from examples/build_system/make/toolchain/arm_iar_rules.mk
rename to examples/build_system/make/toolchain/iar_rules.mk
diff --git a/examples/build_system/make/toolchain/riscv_gcc.mk b/examples/build_system/make/toolchain/riscv_gcc.mk
new file mode 100644
index 000000000..843aff38c
--- /dev/null
+++ b/examples/build_system/make/toolchain/riscv_gcc.mk
@@ -0,0 +1,20 @@
+# makefile for arm gcc toolchain
+
+# Can be set by family, default to ARM GCC
+CROSS_COMPILE ?= riscv-none-embed-
+
+CC = $(CROSS_COMPILE)gcc
+CXX = $(CROSS_COMPILE)g++
+AS = $(CC) -x assembler-with-cpp
+LD = $(CC)
+
+GDB = $(CROSS_COMPILE)gdb
+OBJCOPY = $(CROSS_COMPILE)objcopy
+SIZE = $(CROSS_COMPILE)size
+
+CFLAGS += \
+  -fsingle-precision-constant \
+
+LIBS += -lgcc -lm -lnosys
+
+include ${TOP}/examples/build_system/make/toolchain/gcc_common.mk
diff --git a/examples/device/CMakeLists.txt b/examples/device/CMakeLists.txt
index c3671da69..638440795 100644
--- a/examples/device/CMakeLists.txt
+++ b/examples/device/CMakeLists.txt
@@ -8,6 +8,8 @@ family_initialize_project(tinyusb_device_examples ${CMAKE_CURRENT_LIST_DIR})
 # family_add_subdirectory will filter what to actually add based on selected FAMILY
 family_add_subdirectory(audio_4_channel_mic)
 family_add_subdirectory(audio_test)
+family_add_subdirectory(audio_4_channel_mic_freertos)
+family_add_subdirectory(audio_test_freertos)
 family_add_subdirectory(audio_test_multi_rate)
 family_add_subdirectory(board_test)
 family_add_subdirectory(cdc_dual_ports)
diff --git a/examples/device/audio_4_channel_mic/src/main.c b/examples/device/audio_4_channel_mic/src/main.c
index 1de4f9dac..10e6fe88a 100644
--- a/examples/device/audio_4_channel_mic/src/main.c
+++ b/examples/device/audio_4_channel_mic/src/main.c
@@ -104,39 +104,39 @@ int main(void)
   // Generate dummy data
 #if CFG_TUD_AUDIO_ENABLE_ENCODING
   uint16_t * p_buff = i2s_dummy_buffer[0];
-  uint16_t dataVal = 1;
+  uint16_t dataVal = 0;
   for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
   {
     // CH0 saw wave
     *p_buff++ = dataVal;
     // CH1 inverted saw wave
-    *p_buff++ = 60 + AUDIO_SAMPLE_RATE/1000 - dataVal;
-    dataVal++;
+    *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
+    dataVal+= 32;
   }
   p_buff = i2s_dummy_buffer[1];
   for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
   {
     // CH3 square wave
-    *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 120:170;
+    *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
     // CH4 sinus wave
     float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
-    *p_buff++ = (uint16_t)(sinf(t) * 25) + 200;
+    *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000);
   }
 #else
   uint16_t * p_buff = i2s_dummy_buffer;
-  uint16_t dataVal = 1;
+  uint16_t dataVal = 0;
   for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
   {
     // CH0 saw wave
     *p_buff++ = dataVal;
     // CH1 inverted saw wave
-    *p_buff++ = 60 + AUDIO_SAMPLE_RATE/1000 - dataVal;
-    dataVal++;
+    *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
+    dataVal+= 32;
     // CH3 square wave
-    *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 120:170;
+    *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
     // CH4 sinus wave
     float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
-    *p_buff++ = (uint16_t)(sinf(t) * 25) + 200;
+    *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000);
   }
 #endif
 
diff --git a/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt b/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt
new file mode 100644
index 000000000..e8ca3751d
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/CMakeLists.txt
@@ -0,0 +1,39 @@
+cmake_minimum_required(VERSION 3.17)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+# gets PROJECT name for the example (e.g. -)
+family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+
+project(${PROJECT} C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+  return()
+endif()
+
+add_executable(${PROJECT})
+
+# Example source
+target_sources(${PROJECT} PUBLIC
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/freertos_hook.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+  )
+
+# Example include
+target_include_directories(${PROJECT} PUBLIC
+  ${CMAKE_CURRENT_SOURCE_DIR}/src
+  )
+
+# Add libm for GCC
+if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+  target_link_libraries(${PROJECT} PUBLIC m)
+endif()
+
+# Configure compilation flags and libraries for the example with FreeRTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_device_example(${PROJECT} freertos)
diff --git a/examples/device/audio_4_channel_mic_freertos/Makefile b/examples/device/audio_4_channel_mic_freertos/Makefile
new file mode 100644
index 000000000..df2cdef4e
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/Makefile
@@ -0,0 +1,38 @@
+include ../../build_system/make/make.mk
+
+FREERTOS_SRC = lib/FreeRTOS-Kernel
+FREERTOS_PORTABLE_PATH = $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC)
+
+INC += \
+	src \
+	src/FreeRTOSConfig \
+	$(TOP)/hw \
+	$(TOP)/$(FREERTOS_SRC)/include \
+	$(TOP)/$(FREERTOS_PORTABLE_SRC) \
+
+# Example source
+EXAMPLE_SOURCE = \
+	src/freertos_hook.c \
+	src/main.c \
+	src/usb_descriptors.c
+
+SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+
+# FreeRTOS source, all files in port folder
+SRC_C += \
+	$(FREERTOS_SRC)/list.c \
+	$(FREERTOS_SRC)/queue.c \
+	$(FREERTOS_SRC)/tasks.c \
+	$(FREERTOS_SRC)/timers.c \
+	$(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.c))
+
+SRC_S += \
+	$(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.s))
+
+# Suppress FreeRTOS warnings
+CFLAGS += -Wno-error=cast-qual -Wno-error=redundant-decls
+
+# FreeRTOS (lto + Os) linker issue
+LDFLAGS += -Wl,--undefined=vTaskSwitchContext
+
+include ../../build_system/make/rules.mk
diff --git a/examples/device/audio_4_channel_mic_freertos/README.md b/examples/device/audio_4_channel_mic_freertos/README.md
new file mode 100644
index 000000000..a99f28bc3
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/README.md
@@ -0,0 +1,19 @@
+# How to build example for Esp32s3
+1. Load idf environment variables (eg. using the esp-idf alias `get_idf` if configured)
+
+2. cd into examples directory
+```
+$ cd /tinyusb/examples/device/audio_4_channel_mic_freertos
+```
+
+3. Run cmake in project directory specifying the board
+```
+$ cmake -DBOARD=espressif_s3_devkitc -B build -G Ninja .
+$ ninja.exe -C build
+```
+
+4. Flash the binary onto the esp32-s3 by copy-paste of the full command output by the esp-idf build system replacing **(PORT)** with eg. /dev/ttyUSB0
+
+eg.
+
+> /home/kaspernyhus/.espressif/python_env/idf4.4_py3.8_env/bin/python ../../../../esp-idf/components/esptool_py/esptool/esptool.py -p /dev/ttyUSB0 -b 460800 --before default_reset --after hard_reset --chip esp32s3  write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x0 _build/espressif_s3_devkitc/bootloader/bootloader.bin 0x8000 _build/espressif_s3_devkitc/partition_table/partition-table.bin 0x10000 _build/espressif_s3_devkitc/audio_4_channel_mic_freertos.bin
diff --git a/examples/device/audio_4_channel_mic_freertos/sdkconfig.defaults b/examples/device/audio_4_channel_mic_freertos/sdkconfig.defaults
new file mode 100644
index 000000000..eaa9fb902
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/sdkconfig.defaults
@@ -0,0 +1,4 @@
+CONFIG_IDF_CMAKE=y
+CONFIG_FREERTOS_HZ=1000
+CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
+CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
diff --git a/examples/device/audio_4_channel_mic_freertos/skip.txt b/examples/device/audio_4_channel_mic_freertos/skip.txt
new file mode 100644
index 000000000..012ca4e74
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/skip.txt
@@ -0,0 +1,18 @@
+mcu:CH32V103
+mcu:CH32V20X
+mcu:CH32V307
+mcu:CXD56
+mcu:F1C100S
+mcu:GD32VF103
+mcu:MKL25ZXX
+mcu:MSP430x5xx
+mcu:RP2040
+mcu:SAMD11
+mcu:SAMX7X
+mcu:VALENTYUSB_EPTRI
+mcu:RAXXX
+mcu:STM32L0
+board:lpcxpresso11u37
+board:lpcxpresso1347
+family:broadcom_32bit
+family:broadcom_64bit
diff --git a/examples/device/audio_4_channel_mic_freertos/src/CMakeLists.txt b/examples/device/audio_4_channel_mic_freertos/src/CMakeLists.txt
new file mode 100644
index 000000000..cef2b46ee
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/src/CMakeLists.txt
@@ -0,0 +1,4 @@
+# This file is for ESP-IDF only
+idf_component_register(SRCS "main.c" "usb_descriptors.c"
+                    INCLUDE_DIRS "."
+                    REQUIRES boards tinyusb_src)
diff --git a/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..869500ad2
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,191 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+
+// Include MCU header
+#include "bsp/board_mcu.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
+  #error "ESP32-Sx should use IDF's FreeRTOSConfig.h"
+#endif
+
+// TODO fix later
+#if CFG_TUSB_MCU == OPT_MCU_MM32F327X
+  extern u32 SystemCoreClock;
+#else
+  // FIXME cause redundant-decls warnings
+  extern uint32_t SystemCoreClock;
+#endif
+
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU								        0
+#define configENABLE_FPU								        1
+#define configENABLE_TRUSTZONE					        0
+#define configMINIMAL_SECURE_STACK_SIZE					( 1024 )
+#define configRUN_FREERTOS_SECURE_ONLY          1
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      0
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+#ifdef __RX__
+/* Renesas RX series */
+#define vSoftwareInterruptISR					        INT_Excep_ICU_SWINT
+#define vTickISR								              INT_Excep_CMT0_CMI0
+#define configPERIPHERAL_CLOCK_HZ				      (configCPU_CLOCK_HZ/2)
+#define configKERNEL_INTERRUPT_PRIORITY			  1
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY	4
+
+#else
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+#if defined(__NVIC_PRIO_BITS)
+  // For Cortex-M specific: __NVIC_PRIO_BITS is defined in core_cmx.h
+	#define configPRIO_BITS       __NVIC_PRIO_BITS
+
+#elif defined(__ECLIC_INTCTLBITS)
+  // RISC-V Bumblebee core from nuclei
+  #define configPRIO_BITS       __ECLIC_INTCTLBITS
+
+#elif defined(__IASMARM__)
+  // FIXME: IAR Assembler cannot include mcu header directly to get __NVIC_PRIO_BITS.
+  // Therefore we will hard coded it to minimum value of 2 to get pass ci build.
+  // IAR user must update this to correct value of the target MCU
+  #message "configPRIO_BITS is hard coded to 2 to pass IAR build only. User should update it per MCU"
+  #define configPRIO_BITS       2
+
+#else
+  #error "FreeRTOS configPRIO_BITS to be defined"
+#endif
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<
+#include 
+#include 
+#include 
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+#if TUP_MCU_ESPRESSIF
+  // ESP-IDF need "freertos/" prefix in include path.
+  // CFG_TUSB_OS_INC_PATH should be defined accordingly.
+  #include "freertos/FreeRTOS.h"
+  #include "freertos/semphr.h"
+  #include "freertos/queue.h"
+  #include "freertos/task.h"
+  #include "freertos/timers.h"
+
+  #define USBD_STACK_SIZE     4096
+#else
+
+  #include "FreeRTOS.h"
+  #include "semphr.h"
+  #include "queue.h"
+  #include "task.h"
+  #include "timers.h"
+
+  // Increase stack size when debug log is enabled
+  #define USBD_STACK_SIZE    (4*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+#endif
+
+#define BLINKY_STACK_SIZE   configMINIMAL_STACK_SIZE
+#define AUDIO_STACK_SIZE    configMINIMAL_STACK_SIZE
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+#define AUDIO_SAMPLE_RATE   CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE
+
+/* Blink pattern
+ * - 250 ms  : device not mounted
+ * - 1000 ms : device mounted
+ * - 2500 ms : device is suspended
+ */
+enum  {
+  BLINK_NOT_MOUNTED = 250,
+  BLINK_MOUNTED = 1000,
+  BLINK_SUSPENDED = 2500,
+};
+
+// static task
+#if configSUPPORT_STATIC_ALLOCATION
+StackType_t  blinky_stack[BLINKY_STACK_SIZE];
+StaticTask_t blinky_taskdef;
+
+StackType_t  usb_device_stack[USBD_STACK_SIZE];
+StaticTask_t usb_device_taskdef;
+
+StackType_t  audio_stack[AUDIO_STACK_SIZE];
+StaticTask_t audio_taskdef;
+#endif
+
+static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
+
+// Audio controls
+// Current states
+bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; 				          // +1 for master channel 0
+uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; 					// +1 for master channel 0
+uint32_t sampFreq;
+uint8_t clkValid;
+
+// Range states
+audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; 			// Volume range state
+audio_control_range_4_n_t(1) sampleFreqRng; 						// Sample frequency range state
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING
+// Audio test data, each buffer contains 2 channels, buffer[0] for CH0-1, buffer[1] for CH1-2
+uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO][CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000/CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO];
+#else
+// Audio test data, 4 channels muxed together, buffer[0] for CH0, buffer[1] for CH1, buffer[2] for CH2, buffer[3] for CH3
+uint16_t i2s_dummy_buffer[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX*CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE/1000];
+#endif
+
+void led_blinking_task(void* param);
+void usb_device_task(void* param);
+void audio_task(void* param);
+
+/*------------- MAIN -------------*/
+int main(void)
+{
+  board_init();
+
+  // Init values
+  sampFreq = AUDIO_SAMPLE_RATE;
+  clkValid = 1;
+
+  sampleFreqRng.wNumSubRanges = 1;
+  sampleFreqRng.subrange[0].bMin = AUDIO_SAMPLE_RATE;
+  sampleFreqRng.subrange[0].bMax = AUDIO_SAMPLE_RATE;
+  sampleFreqRng.subrange[0].bRes = 0;
+
+  // Generate dummy data
+#if CFG_TUD_AUDIO_ENABLE_ENCODING
+  uint16_t * p_buff = i2s_dummy_buffer[0];
+  uint16_t dataVal = 0;
+  for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
+  {
+    // CH0 saw wave
+    *p_buff++ = dataVal;
+    // CH1 inverted saw wave
+    *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
+    dataVal+= 32;
+  }
+  p_buff = i2s_dummy_buffer[1];
+  for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
+  {
+    // CH3 square wave
+    *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
+    // CH4 sinus wave
+    float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
+    *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000);
+  }
+#else
+  uint16_t * p_buff = i2s_dummy_buffer;
+  uint16_t dataVal = 0;
+  for (uint16_t cnt = 0; cnt < AUDIO_SAMPLE_RATE/1000; cnt++)
+  {
+    // CH0 saw wave
+    *p_buff++ = dataVal;
+    // CH1 inverted saw wave
+    *p_buff++ = 3200 + AUDIO_SAMPLE_RATE/1000 - dataVal;
+    dataVal+= 32;
+    // CH3 square wave
+    *p_buff++ = cnt < (AUDIO_SAMPLE_RATE/1000/2) ? 3400:5000;
+    // CH4 sinus wave
+    float t = 2*3.1415f * cnt / (AUDIO_SAMPLE_RATE/1000);
+    *p_buff++ = (uint16_t)((int16_t)(sinf(t) * 750) + 6000);
+  }
+#endif
+
+#if configSUPPORT_STATIC_ALLOCATION
+  // blinky task
+  xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
+
+  // Create a task for tinyusb device stack
+  xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
+
+   // Create a task for audio
+  xTaskCreateStatic(audio_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES-1, audio_stack, &audio_taskdef);
+#else
+  xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
+  xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+  xTaskCreate(audio_task, "audio", AUDIO_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+#endif
+
+  // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
+  #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+    vTaskStartScheduler();
+  #endif
+
+  return 0;
+}
+
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+void app_main(void)
+{
+  main();
+}
+#endif
+
+// USB Device Driver task
+// This top level thread process all usb events and invoke callbacks
+void usb_device_task(void* param)
+{
+  (void) param;
+
+  // init device stack on configured roothub port
+  // This should be called after scheduler/kernel is started.
+  // Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
+  tud_init(BOARD_TUD_RHPORT);
+
+  if (board_init_after_tusb) {
+    board_init_after_tusb();
+  }
+
+  // RTOS forever loop
+  while (1)
+  {
+    // tinyusb device task
+    tud_task();
+  }
+}
+
+//--------------------------------------------------------------------+
+// Device callbacks
+//--------------------------------------------------------------------+
+
+// Invoked when device is mounted
+void tud_mount_cb(void)
+{
+  blink_interval_ms = BLINK_MOUNTED;
+}
+
+// Invoked when device is unmounted
+void tud_umount_cb(void)
+{
+  blink_interval_ms = BLINK_NOT_MOUNTED;
+}
+
+// Invoked when usb bus is suspended
+// remote_wakeup_en : if host allow us  to perform remote wakeup
+// Within 7ms, device must draw an average of current less than 2.5 mA from bus
+void tud_suspend_cb(bool remote_wakeup_en)
+{
+  (void) remote_wakeup_en;
+  blink_interval_ms = BLINK_SUSPENDED;
+}
+
+// Invoked when usb bus is resumed
+void tud_resume_cb(void)
+{
+  blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
+}
+
+//--------------------------------------------------------------------+
+// AUDIO Task
+//--------------------------------------------------------------------+
+
+void audio_task(void* param)
+{
+  (void) param;
+  // Yet to be filled - e.g. read audio from I2S buffer.
+  // Here we simulate a I2S receive callback every 1ms.
+  while (1) {
+    vTaskDelay(1);
+#if CFG_TUD_AUDIO_ENABLE_ENCODING
+  // Write I2S buffer into FIFO
+    for (uint8_t cnt=0; cnt < 2; cnt++)
+    {
+      tud_audio_write_support_ff(cnt, i2s_dummy_buffer[cnt], AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX);
+    }
+#else
+    tud_audio_write(i2s_dummy_buffer, AUDIO_SAMPLE_RATE/1000 * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX);
+#endif
+  }
+}
+
+//--------------------------------------------------------------------+
+// Application Callback API Implementations
+//--------------------------------------------------------------------+
+
+// Invoked when audio class specific set request received for an EP
+bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
+{
+  (void) rhport;
+  (void) pBuff;
+
+  // We do not support any set range requests here, only current value requests
+  TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+  (void) channelNum; (void) ctrlSel; (void) ep;
+
+  return false; 	// Yet not implemented
+}
+
+// Invoked when audio class specific set request received for an interface
+bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
+{
+  (void) rhport;
+  (void) pBuff;
+
+  // We do not support any set range requests here, only current value requests
+  TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t itf = TU_U16_LOW(p_request->wIndex);
+
+  (void) channelNum; (void) ctrlSel; (void) itf;
+
+  return false; 	// Yet not implemented
+}
+
+// Invoked when audio class specific set request received for an entity
+bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
+{
+  (void) rhport;
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t itf = TU_U16_LOW(p_request->wIndex);
+  uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+  (void) itf;
+
+  // We do not support any set range requests here, only current value requests
+  TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+
+  // If request is for our feature unit
+  if ( entityID == 2 )
+  {
+    switch ( ctrlSel )
+    {
+      case AUDIO_FU_CTRL_MUTE:
+        // Request uses format layout 1
+        TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
+
+        mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
+
+        TU_LOG2("    Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
+      return true;
+
+      case AUDIO_FU_CTRL_VOLUME:
+        // Request uses format layout 2
+        TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
+
+        volume[channelNum] = ((audio_control_cur_2_t*) pBuff)->bCur;
+
+        TU_LOG2("    Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
+      return true;
+
+        // Unknown/Unsupported control
+      default:
+        TU_BREAKPOINT();
+      return false;
+    }
+  }
+  return false;    // Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an EP
+bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  (void) rhport;
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+  (void) channelNum; (void) ctrlSel; (void) ep;
+
+  return false; 	// Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an interface
+bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  (void) rhport;
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t itf = TU_U16_LOW(p_request->wIndex);
+
+  (void) channelNum; (void) ctrlSel; (void) itf;
+
+  return false; 	// Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an entity
+bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  (void) rhport;
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  // uint8_t itf = TU_U16_LOW(p_request->wIndex); 			// Since we have only one audio function implemented, we do not need the itf value
+  uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+  // Input terminal (Microphone input)
+  if (entityID == 1)
+  {
+    switch ( ctrlSel )
+    {
+      case AUDIO_TE_CTRL_CONNECTOR:
+      {
+        // The terminal connector control only has a get request with only the CUR attribute.
+        audio_desc_channel_cluster_t ret;
+
+        // Those are dummy values for now
+        ret.bNrChannels = 1;
+        ret.bmChannelConfig = 0;
+        ret.iChannelNames = 0;
+
+        TU_LOG2("    Get terminal connector\r\n");
+
+        return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
+      }
+      break;
+
+        // Unknown/Unsupported control selector
+      default:
+        TU_BREAKPOINT();
+        return false;
+    }
+  }
+
+  // Feature unit
+  if (entityID == 2)
+  {
+    switch ( ctrlSel )
+    {
+      case AUDIO_FU_CTRL_MUTE:
+        // Audio control mute cur parameter block consists of only one byte - we thus can send it right away
+        // There does not exist a range parameter block for mute
+        TU_LOG2("    Get Mute of channel: %u\r\n", channelNum);
+        return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
+
+      case AUDIO_FU_CTRL_VOLUME:
+        switch ( p_request->bRequest )
+        {
+          case AUDIO_CS_REQ_CUR:
+            TU_LOG2("    Get Volume of channel: %u\r\n", channelNum);
+            return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
+
+          case AUDIO_CS_REQ_RANGE:
+            TU_LOG2("    Get Volume range of channel: %u\r\n", channelNum);
+
+            // Copy values - only for testing - better is version below
+            audio_control_range_2_n_t(1)
+            ret;
+
+            ret.wNumSubRanges = 1;
+            ret.subrange[0].bMin = -90;           // -90 dB
+            ret.subrange[0].bMax = 90;		// +90 dB
+            ret.subrange[0].bRes = 1; 		// 1 dB steps
+
+            return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
+
+            // Unknown/Unsupported control
+          default:
+            TU_BREAKPOINT();
+            return false;
+        }
+      break;
+
+        // Unknown/Unsupported control
+      default:
+        TU_BREAKPOINT();
+        return false;
+    }
+  }
+
+  // Clock Source unit
+  if ( entityID == 4 )
+  {
+    switch ( ctrlSel )
+    {
+      case AUDIO_CS_CTRL_SAM_FREQ:
+        // channelNum is always zero in this case
+        switch ( p_request->bRequest )
+        {
+          case AUDIO_CS_REQ_CUR:
+            TU_LOG2("    Get Sample Freq.\r\n");
+            // Buffered control transfer is needed for IN flow control to work
+            return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
+
+          case AUDIO_CS_REQ_RANGE:
+            TU_LOG2("    Get Sample Freq. range\r\n");
+            return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
+
+           // Unknown/Unsupported control
+          default:
+            TU_BREAKPOINT();
+            return false;
+        }
+      break;
+
+      case AUDIO_CS_CTRL_CLK_VALID:
+        // Only cur attribute exists for this request
+        TU_LOG2("    Get Sample Freq. valid\r\n");
+        return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
+
+      // Unknown/Unsupported control
+      default:
+        TU_BREAKPOINT();
+        return false;
+    }
+  }
+
+  TU_LOG2("  Unsupported entity: %d\r\n", entityID);
+  return false; 	// Yet not implemented
+}
+
+bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
+{
+  (void) rhport;
+  (void) itf;
+  (void) ep_in;
+  (void) cur_alt_setting;
+
+
+  // In read world application data flow is driven by I2S clock,
+  // both tud_audio_tx_done_pre_load_cb() & tud_audio_tx_done_post_load_cb() are hardly used.
+  // For example in your I2S receive callback:
+  // void I2S_Rx_Callback(int channel, const void* data, uint16_t samples)
+  // {
+  //    tud_audio_write_support_ff(channel, data, samples * N_BYTES_PER_SAMPLE * N_CHANNEL_PER_FIFO);
+  // }
+
+  return true;
+}
+
+bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
+{
+  (void) rhport;
+  (void) n_bytes_copied;
+  (void) itf;
+  (void) ep_in;
+  (void) cur_alt_setting;
+
+  return true;
+}
+
+bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  (void) rhport;
+  (void) p_request;
+
+  return true;
+}
+
+///--------------------------------------------------------------------+
+// BLINKING TASK
+//--------------------------------------------------------------------+
+void led_blinking_task(void* param) {
+  (void) param;
+  static uint32_t start_ms = 0;
+  static bool led_state = false;
+
+  while (1) {
+    // Blink every interval ms
+    vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
+    start_ms += blink_interval_ms;
+
+    board_led_write(led_state);
+    led_state = 1 - led_state; // toggle
+  }
+}
diff --git a/examples/device/audio_4_channel_mic_freertos/src/plot_audio_samples.py b/examples/device/audio_4_channel_mic_freertos/src/plot_audio_samples.py
new file mode 100644
index 000000000..8312b4e28
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/src/plot_audio_samples.py
@@ -0,0 +1,33 @@
+import sounddevice as sd
+import matplotlib.pyplot as plt
+import numpy as np
+import platform
+
+if __name__ == '__main__':
+
+    # If you got "ValueError: No input device matching", that is because your PC name example device
+    # differently from tested list below. Uncomment the next line to see full list and try to pick correct one
+    # print(sd.query_devices())
+
+    fs = 48000  		# Sample rate
+    duration = 100e-3   # Duration of recording
+
+    if platform.system() == 'Windows':
+        # WDM-KS is needed since there are more than one MicNode device APIs (at least in Windows)
+        device = 'Microphone (MicNode_4_Ch), Windows WDM-KS'
+    elif platform.system() == 'Darwin':
+        device = 'MicNode_4_Ch'
+    else:
+        device ='default'
+
+    myrecording = sd.rec(int(duration * fs), samplerate=fs, channels=4, dtype='int16', device=device)
+    print('Waiting...')
+    sd.wait()  # Wait until recording is finished
+    print('Done!')
+
+    time = np.arange(0, duration, 1 / fs)  # time vector
+    plt.plot(time, myrecording)
+    plt.xlabel('Time [s]')
+    plt.ylabel('Amplitude')
+    plt.title('MicNode 4 Channel')
+    plt.show()
diff --git a/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h
new file mode 100644
index 000000000..88f20278b
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/src/tusb_config.h
@@ -0,0 +1,148 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+// RHPort number used for device can be defined by board.mk, default to port 0
+#ifndef BOARD_TUD_RHPORT
+#define BOARD_TUD_RHPORT      0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUD_MAX_SPEED
+#define BOARD_TUD_MAX_SPEED   OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+// This examples use FreeRTOS
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS           OPT_OS_FREERTOS
+#endif
+
+// Espressif IDF requires "freertos/" prefix in include path
+#if TUP_MCU_ESPRESSIF
+#define CFG_TUSB_OS_INC_PATH  freertos/
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG            0
+#endif
+
+// Enable Device stack
+#define CFG_TUD_ENABLED           1
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUD_MAX_SPEED         BOARD_TUD_MAX_SPEED
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN        __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+#define CFG_TUD_ENDPOINT0_SIZE    64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_AUDIO             1
+#define CFG_TUD_CDC               0
+#define CFG_TUD_MSC               0
+#define CFG_TUD_HID               0
+#define CFG_TUD_MIDI              0
+#define CFG_TUD_VENDOR            0
+
+//--------------------------------------------------------------------
+// AUDIO CLASS DRIVER CONFIGURATION
+//--------------------------------------------------------------------
+
+// Have a look into audio_device.h for all configurations
+#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE              48000
+
+#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN                 TUD_AUDIO_MIC_FOUR_CH_DESC_LEN
+
+#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT                 1
+#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ              64
+
+#define CFG_TUD_AUDIO_ENABLE_EP_IN                    1
+#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX    2         // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
+#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX            4         // This value is not required by the driver, it parses this information from the descriptor once the alternate interface is set by the host - we use it for the setup
+#define CFG_TUD_AUDIO_EP_SZ_IN                        TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+
+#define CFG_TUD_AUDIO_ENABLE_ENCODING                 1
+#define CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL              1
+
+#if CFG_TUD_AUDIO_ENABLE_ENCODING
+
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX             CFG_TUD_AUDIO_EP_SZ_IN
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ          CFG_TUD_AUDIO_EP_SZ_IN
+
+#define CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING          1
+#define CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX      2         // One I2S stream contains two channels, each stream is saved within one support FIFO - this value is currently fixed, the driver does not support a changing value
+#define CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO        (CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX / CFG_TUD_AUDIO_FUNC_1_CHANNEL_PER_FIFO_TX)
+#define CFG_TUD_AUDIO_FUNC_1_TX_SUPP_SW_FIFO_SZ       (TUD_OPT_HIGH_SPEED ? 32 : 4) * (CFG_TUD_AUDIO_EP_SZ_IN / CFG_TUD_AUDIO_FUNC_1_N_TX_SUPP_SW_FIFO) // Example write FIFO every 1ms, so it should be 8 times larger for HS device
+
+#else
+
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX             CFG_TUD_AUDIO_EP_SZ_IN
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ          (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
+
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
new file mode 100644
index 000000000..728a5f9ce
--- /dev/null
+++ b/examples/device/audio_4_channel_mic_freertos/src/usb_descriptors.c
@@ -0,0 +1,179 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 "bsp/board_api.h"
+#include "tusb.h"
+
+/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
+ * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
+ *
+ * Auto ProductID layout's Bitmap:
+ *   [MSB]     AUDIO | MIDI | HID | MSC | CDC          [LSB]
+ */
+#define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) )
+#define USB_PID           (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
+    _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+tusb_desc_device_t const desc_device =
+{
+    .bLength            = sizeof(tusb_desc_device_t),
+    .bDescriptorType    = TUSB_DESC_DEVICE,
+    .bcdUSB             = 0x0200,
+
+    // Use Interface Association Descriptor (IAD) for Audio
+    // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
+    .bDeviceClass       = TUSB_CLASS_MISC,
+    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
+    .bDeviceProtocol    = MISC_PROTOCOL_IAD,
+    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
+
+    .idVendor           = 0xCafe,
+    .idProduct          = USB_PID,
+    .bcdDevice          = 0x0100,
+
+    .iManufacturer      = 0x01,
+    .iProduct           = 0x02,
+    .iSerialNumber      = 0x03,
+
+    .bNumConfigurations = 0x01
+};
+
+// Invoked when received GET DEVICE DESCRIPTOR
+// Application return pointer to descriptor
+uint8_t const * tud_descriptor_device_cb(void)
+{
+  return (uint8_t const *) &desc_device;
+}
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor
+//--------------------------------------------------------------------+
+enum
+{
+  ITF_NUM_AUDIO_CONTROL = 0,
+  ITF_NUM_AUDIO_STREAMING,
+  ITF_NUM_TOTAL
+};
+
+#define CONFIG_TOTAL_LEN    	(TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_FOUR_CH_DESC_LEN)
+
+#if TU_CHECK_MCU(OPT_MCU_LPC175X_6X, OPT_MCU_LPC177X_8X, OPT_MCU_LPC40XX)
+  // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+  // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
+  #define EPNUM_AUDIO   0x03
+
+#elif TU_CHECK_MCU(OPT_MCU_NRF5X)
+  // nRF5x ISO can only be endpoint 8
+  #define EPNUM_AUDIO   0x08
+
+#else
+  #define EPNUM_AUDIO   0x01
+#endif
+
+uint8_t const desc_configuration[] =
+{
+  // Config number, interface count, string index, total length, attribute, power in mA
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+  // Interface number, string index, EP Out & EP In address, EP size
+  TUD_AUDIO_MIC_FOUR_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
+};
+
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
+{
+  (void) index; // for multiple configurations
+  return desc_configuration;
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+// String Descriptor Index
+enum {
+  STRID_LANGID = 0,
+  STRID_MANUFACTURER,
+  STRID_PRODUCT,
+  STRID_SERIAL,
+};
+
+// array of pointer to string descriptors
+char const* string_desc_arr [] = {
+    (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
+    "PaniRCorp",                   // 1: Manufacturer
+    "MicNode_4_Ch",                // 2: Product
+    NULL,                          // 3: Serials will use unique ID if possible
+    "UAC2",                        // 4: Audio Interface
+};
+
+static uint16_t _desc_str[32 + 1];
+
+// Invoked when received GET STRING DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+  (void) langid;
+  size_t chr_count;
+
+  switch ( index ) {
+    case STRID_LANGID:
+      memcpy(&_desc_str[1], string_desc_arr[0], 2);
+      chr_count = 1;
+      break;
+
+    case STRID_SERIAL:
+      chr_count = board_usb_get_serial(_desc_str + 1, 32);
+      break;
+
+    default:
+      // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
+      // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
+
+      if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+
+      const char *str = string_desc_arr[index];
+
+      // Cap at max char
+      chr_count = strlen(str);
+      size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
+      if ( chr_count > max_count ) chr_count = max_count;
+
+      // Convert ASCII string into UTF-16
+      for ( size_t i = 0; i < chr_count; i++ ) {
+        _desc_str[1 + i] = str[i];
+      }
+      break;
+  }
+
+  // first byte is length (including header), second byte is string type
+  _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+
+  return _desc_str;
+}
diff --git a/examples/device/audio_test/src/main.c b/examples/device/audio_test/src/main.c
index 06783ccfb..f79bb4468 100644
--- a/examples/device/audio_test/src/main.c
+++ b/examples/device/audio_test/src/main.c
@@ -42,10 +42,6 @@
 // MACRO CONSTANT TYPEDEF PROTYPES
 //--------------------------------------------------------------------+
 
-#ifndef AUDIO_SAMPLE_RATE
-#define AUDIO_SAMPLE_RATE   48000
-#endif
-
 /* Blink pattern
  * - 250 ms  : device not mounted
  * - 1000 ms : device mounted
@@ -90,12 +86,12 @@ int main(void)
   }
 
   // Init values
-  sampFreq = AUDIO_SAMPLE_RATE;
+  sampFreq = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
   clkValid = 1;
 
   sampleFreqRng.wNumSubRanges = 1;
-  sampleFreqRng.subrange[0].bMin = AUDIO_SAMPLE_RATE;
-  sampleFreqRng.subrange[0].bMax = AUDIO_SAMPLE_RATE;
+  sampleFreqRng.subrange[0].bMin = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
+  sampleFreqRng.subrange[0].bMax = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
   sampleFreqRng.subrange[0].bRes = 0;
 
   while (1)
diff --git a/examples/device/audio_test/src/tusb_config.h b/examples/device/audio_test/src/tusb_config.h
index 9f38612a9..8c021e23c 100644
--- a/examples/device/audio_test/src/tusb_config.h
+++ b/examples/device/audio_test/src/tusb_config.h
@@ -106,6 +106,7 @@ extern "C" {
 //--------------------------------------------------------------------
 
 // Have a look into audio_device.h for all configurations
+#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE                              48000
 
 #define CFG_TUD_AUDIO_FUNC_1_DESC_LEN                                 TUD_AUDIO_MIC_ONE_CH_DESC_LEN
 #define CFG_TUD_AUDIO_FUNC_1_N_AS_INT                                 1                                       // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes)
@@ -114,9 +115,9 @@ extern "C" {
 #define CFG_TUD_AUDIO_ENABLE_EP_IN                                    1
 #define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX                    2                                       // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below
 #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX                            1                                       // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
-#define CFG_TUD_AUDIO_EP_SZ_IN                                        (48 + 1) * CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX * CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX      // 48 Samples (48 kHz) x 2 Bytes/Sample x CFG_TUD_AUDIO_N_CHANNELS_TX Channels - One extra sample is needed for asynchronous transfer adjustment, see feedback EP
-#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX                             CFG_TUD_AUDIO_EP_SZ_IN                  // Maximum EP IN size for all AS alternate settings used
-#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ                          CFG_TUD_AUDIO_EP_SZ_IN + 1
+#define CFG_TUD_AUDIO_EP_SZ_IN                                        TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX                             CFG_TUD_AUDIO_EP_SZ_IN
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ                          (TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
 
 #ifdef __cplusplus
 }
diff --git a/examples/device/audio_test_freertos/CMakeLists.txt b/examples/device/audio_test_freertos/CMakeLists.txt
new file mode 100644
index 000000000..eb22014fb
--- /dev/null
+++ b/examples/device/audio_test_freertos/CMakeLists.txt
@@ -0,0 +1,34 @@
+cmake_minimum_required(VERSION 3.17)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+# gets PROJECT name for the example (e.g. -)
+family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+
+project(${PROJECT} C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+  return()
+endif()
+
+add_executable(${PROJECT})
+
+# Example source
+target_sources(${PROJECT} PUBLIC
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/freertos_hook.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+  )
+
+# Example include
+target_include_directories(${PROJECT} PUBLIC
+  ${CMAKE_CURRENT_SOURCE_DIR}/src
+  )
+
+# Configure compilation flags and libraries for the example with FreeRTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_device_example(${PROJECT} freertos)
diff --git a/examples/device/audio_test_freertos/Makefile b/examples/device/audio_test_freertos/Makefile
new file mode 100644
index 000000000..df2cdef4e
--- /dev/null
+++ b/examples/device/audio_test_freertos/Makefile
@@ -0,0 +1,38 @@
+include ../../build_system/make/make.mk
+
+FREERTOS_SRC = lib/FreeRTOS-Kernel
+FREERTOS_PORTABLE_PATH = $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC)
+
+INC += \
+	src \
+	src/FreeRTOSConfig \
+	$(TOP)/hw \
+	$(TOP)/$(FREERTOS_SRC)/include \
+	$(TOP)/$(FREERTOS_PORTABLE_SRC) \
+
+# Example source
+EXAMPLE_SOURCE = \
+	src/freertos_hook.c \
+	src/main.c \
+	src/usb_descriptors.c
+
+SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+
+# FreeRTOS source, all files in port folder
+SRC_C += \
+	$(FREERTOS_SRC)/list.c \
+	$(FREERTOS_SRC)/queue.c \
+	$(FREERTOS_SRC)/tasks.c \
+	$(FREERTOS_SRC)/timers.c \
+	$(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.c))
+
+SRC_S += \
+	$(subst $(TOP)/,,$(wildcard $(TOP)/$(FREERTOS_PORTABLE_SRC)/*.s))
+
+# Suppress FreeRTOS warnings
+CFLAGS += -Wno-error=cast-qual -Wno-error=redundant-decls
+
+# FreeRTOS (lto + Os) linker issue
+LDFLAGS += -Wl,--undefined=vTaskSwitchContext
+
+include ../../build_system/make/rules.mk
diff --git a/examples/device/audio_test_freertos/README.md b/examples/device/audio_test_freertos/README.md
new file mode 100644
index 000000000..9477fcd78
--- /dev/null
+++ b/examples/device/audio_test_freertos/README.md
@@ -0,0 +1,19 @@
+# How to build example for Esp32s3
+1. Load idf environment variables (eg. using the esp-idf alias `get_idf` if configured)
+
+2. cd into examples directory
+```
+$ cd /tinyusb/examples/device/audio_test_freertos
+```
+
+3. Run cmake in project directory specifying the board
+```
+$ cmake -DBOARD=espressif_s3_devkitc -B build -G Ninja .
+$ ninja.exe -C build
+```
+
+4. Flash the binary onto the esp32-s3 by copy-paste of the full command output by the esp-idf build system replacing **(PORT)** with eg. /dev/ttyUSB0
+
+eg.
+
+> /home/kaspernyhus/.espressif/python_env/idf4.4_py3.8_env/bin/python ../../../../esp-idf/components/esptool_py/esptool/esptool.py -p /dev/ttyUSB0 -b 460800 --before default_reset --after hard_reset --chip esp32s3  write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x0 _build/espressif_s3_devkitc/bootloader/bootloader.bin 0x8000 _build/espressif_s3_devkitc/partition_table/partition-table.bin 0x10000 _build/espressif_s3_devkitc/audio_test_freertos.bin
diff --git a/examples/device/audio_test_freertos/sdkconfig.defaults b/examples/device/audio_test_freertos/sdkconfig.defaults
new file mode 100644
index 000000000..83871619e
--- /dev/null
+++ b/examples/device/audio_test_freertos/sdkconfig.defaults
@@ -0,0 +1,3 @@
+CONFIG_IDF_CMAKE=y
+CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
+CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y
diff --git a/examples/device/audio_test_freertos/skip.txt b/examples/device/audio_test_freertos/skip.txt
new file mode 100644
index 000000000..c13667f56
--- /dev/null
+++ b/examples/device/audio_test_freertos/skip.txt
@@ -0,0 +1,15 @@
+mcu:CH32V103
+mcu:CH32V20X
+mcu:CH32V307
+mcu:CXD56
+mcu:F1C100S
+mcu:GD32VF103
+mcu:MKL25ZXX
+mcu:MSP430x5xx
+mcu:RP2040
+mcu:SAMD11
+mcu:SAMX7X
+mcu:VALENTYUSB_EPTRI
+mcu:RAXXX
+family:broadcom_32bit
+family:broadcom_64bit
diff --git a/examples/device/audio_test_freertos/src/CMakeLists.txt b/examples/device/audio_test_freertos/src/CMakeLists.txt
new file mode 100644
index 000000000..cef2b46ee
--- /dev/null
+++ b/examples/device/audio_test_freertos/src/CMakeLists.txt
@@ -0,0 +1,4 @@
+# This file is for ESP-IDF only
+idf_component_register(SRCS "main.c" "usb_descriptors.c"
+                    INCLUDE_DIRS "."
+                    REQUIRES boards tinyusb_src)
diff --git a/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..869500ad2
--- /dev/null
+++ b/examples/device/audio_test_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,191 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+
+// Include MCU header
+#include "bsp/board_mcu.h"
+
+#if CFG_TUSB_MCU == OPT_MCU_ESP32S2 || CFG_TUSB_MCU == OPT_MCU_ESP32S3
+  #error "ESP32-Sx should use IDF's FreeRTOSConfig.h"
+#endif
+
+// TODO fix later
+#if CFG_TUSB_MCU == OPT_MCU_MM32F327X
+  extern u32 SystemCoreClock;
+#else
+  // FIXME cause redundant-decls warnings
+  extern uint32_t SystemCoreClock;
+#endif
+
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU								        0
+#define configENABLE_FPU								        1
+#define configENABLE_TRUSTZONE					        0
+#define configMINIMAL_SECURE_STACK_SIZE					( 1024 )
+#define configRUN_FREERTOS_SECURE_ONLY          1
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      0
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+#ifdef __RX__
+/* Renesas RX series */
+#define vSoftwareInterruptISR					        INT_Excep_ICU_SWINT
+#define vTickISR								              INT_Excep_CMT0_CMI0
+#define configPERIPHERAL_CLOCK_HZ				      (configCPU_CLOCK_HZ/2)
+#define configKERNEL_INTERRUPT_PRIORITY			  1
+#define configMAX_SYSCALL_INTERRUPT_PRIORITY	4
+
+#else
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+#if defined(__NVIC_PRIO_BITS)
+  // For Cortex-M specific: __NVIC_PRIO_BITS is defined in core_cmx.h
+	#define configPRIO_BITS       __NVIC_PRIO_BITS
+
+#elif defined(__ECLIC_INTCTLBITS)
+  // RISC-V Bumblebee core from nuclei
+  #define configPRIO_BITS       __ECLIC_INTCTLBITS
+
+#elif defined(__IASMARM__)
+  // FIXME: IAR Assembler cannot include mcu header directly to get __NVIC_PRIO_BITS.
+  // Therefore we will hard coded it to minimum value of 2 to get pass ci build.
+  // IAR user must update this to correct value of the target MCU
+  #message "configPRIO_BITS is hard coded to 2 to pass IAR build only. User should update it per MCU"
+  #define configPRIO_BITS       2
+
+#else
+  #error "FreeRTOS configPRIO_BITS to be defined"
+#endif
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<
+#include 
+#include 
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+#if TUP_MCU_ESPRESSIF
+  // ESP-IDF need "freertos/" prefix in include path.
+  // CFG_TUSB_OS_INC_PATH should be defined accordingly.
+  #include "freertos/FreeRTOS.h"
+  #include "freertos/semphr.h"
+  #include "freertos/queue.h"
+  #include "freertos/task.h"
+  #include "freertos/timers.h"
+
+  #define USBD_STACK_SIZE     4096
+#else
+
+  #include "FreeRTOS.h"
+  #include "semphr.h"
+  #include "queue.h"
+  #include "task.h"
+  #include "timers.h"
+
+  // Increase stack size when debug log is enabled
+  #define USBD_STACK_SIZE    (4*configMINIMAL_STACK_SIZE/2) * (CFG_TUSB_DEBUG ? 2 : 1)
+#endif
+
+#define BLINKY_STACK_SIZE   configMINIMAL_STACK_SIZE
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+
+/* Blink pattern
+ * - 250 ms  : device not mounted
+ * - 1000 ms : device mounted
+ * - 2500 ms : device is suspended
+ */
+enum  {
+  BLINK_NOT_MOUNTED = 250,
+  BLINK_MOUNTED = 1000,
+  BLINK_SUSPENDED = 2500,
+};
+
+// static task
+#if configSUPPORT_STATIC_ALLOCATION
+StackType_t blinky_stack[BLINKY_STACK_SIZE];
+StaticTask_t blinky_taskdef;
+
+StackType_t  usb_device_stack[USBD_STACK_SIZE];
+StaticTask_t usb_device_taskdef;
+#endif
+
+static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
+
+// Audio controls
+// Current states
+bool mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; 				          // +1 for master channel 0
+uint16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; 					// +1 for master channel 0
+uint32_t sampFreq;
+uint8_t clkValid;
+
+// Range states
+audio_control_range_2_n_t(1) volumeRng[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX+1]; 			// Volume range state
+audio_control_range_4_n_t(1) sampleFreqRng; 						// Sample frequency range state
+
+// Audio test data
+uint16_t test_buffer_audio[(CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2];
+uint16_t startVal = 0;
+
+void led_blinking_task(void* param);
+void usb_device_task(void* param);
+void audio_task(void);
+
+/*------------- MAIN -------------*/
+int main(void)
+{
+  board_init();
+
+  // Init values
+  sampFreq = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
+  clkValid = 1;
+
+  sampleFreqRng.wNumSubRanges = 1;
+  sampleFreqRng.subrange[0].bMin = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
+  sampleFreqRng.subrange[0].bMax = CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE;
+  sampleFreqRng.subrange[0].bRes = 0;
+
+#if configSUPPORT_STATIC_ALLOCATION
+  // blinky task
+  xTaskCreateStatic(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, blinky_stack, &blinky_taskdef);
+
+  // Create a task for tinyusb device stack
+  xTaskCreateStatic(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES-1, usb_device_stack, &usb_device_taskdef);
+#else
+  xTaskCreate(led_blinking_task, "blinky", BLINKY_STACK_SIZE, NULL, 1, NULL);
+  xTaskCreate(usb_device_task, "usbd", USBD_STACK_SIZE, NULL, configMAX_PRIORITIES - 1, NULL);
+#endif
+
+  // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
+  #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+    vTaskStartScheduler();
+  #endif
+
+  return 0;
+}
+
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+void app_main(void)
+{
+  main();
+}
+#endif
+
+// USB Device Driver task
+// This top level thread process all usb events and invoke callbacks
+void usb_device_task(void* param)
+{
+  (void) param;
+
+  // init device stack on configured roothub port
+  // This should be called after scheduler/kernel is started.
+  // Otherwise it could cause kernel issue since USB IRQ handler does use RTOS queue API.
+  tud_init(BOARD_TUD_RHPORT);
+
+  if (board_init_after_tusb) {
+    board_init_after_tusb();
+  }
+
+  // RTOS forever loop
+  while (1)
+  {
+    // tinyusb device task
+    tud_task();
+  }
+}
+
+//--------------------------------------------------------------------+
+// Device callbacks
+//--------------------------------------------------------------------+
+
+// Invoked when device is mounted
+void tud_mount_cb(void) {
+  blink_interval_ms = BLINK_MOUNTED;
+}
+
+// Invoked when device is unmounted
+void tud_umount_cb(void) {
+  blink_interval_ms = BLINK_NOT_MOUNTED;
+}
+
+// Invoked when usb bus is suspended
+// remote_wakeup_en : if host allow us  to perform remote wakeup
+// Within 7ms, device must draw an average of current less than 2.5 mA from bus
+void tud_suspend_cb(bool remote_wakeup_en) {
+  (void) remote_wakeup_en;
+  blink_interval_ms = BLINK_SUSPENDED;
+}
+
+// Invoked when usb bus is resumed
+void tud_resume_cb(void) {
+  blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
+}
+
+//--------------------------------------------------------------------+
+// AUDIO Task
+//--------------------------------------------------------------------+
+
+void audio_task(void)
+{
+  // Yet to be filled - e.g. put meas data into TX FIFOs etc.
+  // asm("nop");
+}
+
+//--------------------------------------------------------------------+
+// Application Callback API Implementations
+//--------------------------------------------------------------------+
+
+// Invoked when audio class specific set request received for an EP
+bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
+{
+  (void) rhport;
+  (void) pBuff;
+
+  // We do not support any set range requests here, only current value requests
+  TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+  (void) channelNum; (void) ctrlSel; (void) ep;
+
+  return false; 	// Yet not implemented
+}
+
+// Invoked when audio class specific set request received for an interface
+bool tud_audio_set_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
+{
+  (void) rhport;
+  (void) pBuff;
+
+  // We do not support any set range requests here, only current value requests
+  TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t itf = TU_U16_LOW(p_request->wIndex);
+
+  (void) channelNum; (void) ctrlSel; (void) itf;
+
+  return false; 	// Yet not implemented
+}
+
+// Invoked when audio class specific set request received for an entity
+bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request, uint8_t *pBuff)
+{
+  (void) rhport;
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t itf = TU_U16_LOW(p_request->wIndex);
+  uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+  (void) itf;
+
+  // We do not support any set range requests here, only current value requests
+  TU_VERIFY(p_request->bRequest == AUDIO_CS_REQ_CUR);
+
+  // If request is for our feature unit
+  if ( entityID == 2 )
+  {
+    switch ( ctrlSel )
+    {
+      case AUDIO_FU_CTRL_MUTE:
+        // Request uses format layout 1
+        TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_1_t));
+
+        mute[channelNum] = ((audio_control_cur_1_t*) pBuff)->bCur;
+
+        TU_LOG2("    Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
+      return true;
+
+      case AUDIO_FU_CTRL_VOLUME:
+        // Request uses format layout 2
+        TU_VERIFY(p_request->wLength == sizeof(audio_control_cur_2_t));
+
+        volume[channelNum] = (uint16_t) ((audio_control_cur_2_t*) pBuff)->bCur;
+
+        TU_LOG2("    Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
+      return true;
+
+        // Unknown/Unsupported control
+      default:
+        TU_BREAKPOINT();
+      return false;
+    }
+  }
+  return false;    // Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an EP
+bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  (void) rhport;
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t ep = TU_U16_LOW(p_request->wIndex);
+
+  (void) channelNum; (void) ctrlSel; (void) ep;
+
+  return false; 	// Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an interface
+bool tud_audio_get_req_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  (void) rhport;
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  uint8_t itf = TU_U16_LOW(p_request->wIndex);
+
+  (void) channelNum; (void) ctrlSel; (void) itf;
+
+  return false; 	// Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an entity
+bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  (void) rhport;
+
+  // Page 91 in UAC2 specification
+  uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+  uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+  // uint8_t itf = TU_U16_LOW(p_request->wIndex); 			// Since we have only one audio function implemented, we do not need the itf value
+  uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+  // Input terminal (Microphone input)
+  if (entityID == 1)
+  {
+    switch ( ctrlSel )
+    {
+      case AUDIO_TE_CTRL_CONNECTOR:
+      {
+        // The terminal connector control only has a get request with only the CUR attribute.
+        audio_desc_channel_cluster_t ret;
+
+        // Those are dummy values for now
+        ret.bNrChannels = 1;
+        ret.bmChannelConfig = (audio_channel_config_t) 0;
+        ret.iChannelNames = 0;
+
+        TU_LOG2("    Get terminal connector\r\n");
+
+        return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
+      }
+      break;
+
+        // Unknown/Unsupported control selector
+      default:
+        TU_BREAKPOINT();
+        return false;
+    }
+  }
+
+  // Feature unit
+  if (entityID == 2)
+  {
+    switch ( ctrlSel )
+    {
+      case AUDIO_FU_CTRL_MUTE:
+        // Audio control mute cur parameter block consists of only one byte - we thus can send it right away
+        // There does not exist a range parameter block for mute
+        TU_LOG2("    Get Mute of channel: %u\r\n", channelNum);
+        return tud_control_xfer(rhport, p_request, &mute[channelNum], 1);
+
+      case AUDIO_FU_CTRL_VOLUME:
+        switch ( p_request->bRequest )
+        {
+          case AUDIO_CS_REQ_CUR:
+            TU_LOG2("    Get Volume of channel: %u\r\n", channelNum);
+            return tud_control_xfer(rhport, p_request, &volume[channelNum], sizeof(volume[channelNum]));
+
+          case AUDIO_CS_REQ_RANGE:
+            TU_LOG2("    Get Volume range of channel: %u\r\n", channelNum);
+
+            // Copy values - only for testing - better is version below
+            audio_control_range_2_n_t(1)
+            ret;
+
+            ret.wNumSubRanges = 1;
+            ret.subrange[0].bMin = -90;    // -90 dB
+            ret.subrange[0].bMax = 90;		// +90 dB
+            ret.subrange[0].bRes = 1; 		// 1 dB steps
+
+            return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, (void*) &ret, sizeof(ret));
+
+            // Unknown/Unsupported control
+          default:
+            TU_BREAKPOINT();
+            return false;
+        }
+      break;
+
+        // Unknown/Unsupported control
+      default:
+        TU_BREAKPOINT();
+        return false;
+    }
+  }
+
+  // Clock Source unit
+  if ( entityID == 4 )
+  {
+    switch ( ctrlSel )
+    {
+      case AUDIO_CS_CTRL_SAM_FREQ:
+        // channelNum is always zero in this case
+        switch ( p_request->bRequest )
+        {
+          case AUDIO_CS_REQ_CUR:
+            TU_LOG2("    Get Sample Freq.\r\n");
+            return tud_control_xfer(rhport, p_request, &sampFreq, sizeof(sampFreq));
+
+          case AUDIO_CS_REQ_RANGE:
+            TU_LOG2("    Get Sample Freq. range\r\n");
+            return tud_control_xfer(rhport, p_request, &sampleFreqRng, sizeof(sampleFreqRng));
+
+           // Unknown/Unsupported control
+          default:
+            TU_BREAKPOINT();
+            return false;
+        }
+      break;
+
+      case AUDIO_CS_CTRL_CLK_VALID:
+        // Only cur attribute exists for this request
+        TU_LOG2("    Get Sample Freq. valid\r\n");
+        return tud_control_xfer(rhport, p_request, &clkValid, sizeof(clkValid));
+
+      // Unknown/Unsupported control
+      default:
+        TU_BREAKPOINT();
+        return false;
+    }
+  }
+
+  TU_LOG2("  Unsupported entity: %d\r\n", entityID);
+  return false; 	// Yet not implemented
+}
+
+bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
+{
+  (void) rhport;
+  (void) itf;
+  (void) ep_in;
+  (void) cur_alt_setting;
+
+  tud_audio_write ((uint8_t *)test_buffer_audio, CFG_TUD_AUDIO_EP_SZ_IN - 2);
+
+  return true;
+}
+
+bool tud_audio_tx_done_post_load_cb(uint8_t rhport, uint16_t n_bytes_copied, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting)
+{
+  (void) rhport;
+  (void) n_bytes_copied;
+  (void) itf;
+  (void) ep_in;
+  (void) cur_alt_setting;
+
+  for (size_t cnt = 0; cnt < (CFG_TUD_AUDIO_EP_SZ_IN - 2) / 2; cnt++)
+  {
+    test_buffer_audio[cnt] = startVal++;
+  }
+
+  return true;
+}
+
+bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const * p_request)
+{
+  (void) rhport;
+  (void) p_request;
+  startVal = 0;
+
+  return true;
+}
+
+//--------------------------------------------------------------------+
+// BLINKING TASK
+//--------------------------------------------------------------------+
+void led_blinking_task(void* param) {
+  (void) param;
+  static uint32_t start_ms = 0;
+  static bool led_state = false;
+
+  while (1) {
+    // Blink every interval ms
+    vTaskDelay(blink_interval_ms / portTICK_PERIOD_MS);
+    start_ms += blink_interval_ms;
+
+    board_led_write(led_state);
+    led_state = 1 - led_state; // toggle
+  }
+}
diff --git a/examples/device/audio_test_freertos/src/plot_audio_samples.py b/examples/device/audio_test_freertos/src/plot_audio_samples.py
new file mode 100644
index 000000000..304b2d5de
--- /dev/null
+++ b/examples/device/audio_test_freertos/src/plot_audio_samples.py
@@ -0,0 +1,33 @@
+import sounddevice as sd
+import matplotlib.pyplot as plt
+import numpy as np
+import platform
+
+if __name__ == '__main__':
+
+    # If you got "ValueError: No input device matching", that is because your PC name example device
+    # differently from tested list below. Uncomment the next line to see full list and try to pick correct one
+    # print(sd.query_devices())
+
+    fs = 48000           # Sample rate
+    duration = 1000e-3    # Duration of recording
+
+    if platform.system() == 'Windows':
+        # MME is needed since there are more than one MicNode device APIs (at least in Windows)
+        device = 'Microphone (MicNode) MME'
+    elif platform.system() == 'Darwin':
+        device = 'MicNode'
+    else:
+        device ='default'
+
+    myrecording = sd.rec(int(duration * fs), samplerate=fs, channels=1, dtype='int16', device=device)
+    print('Waiting...')
+    sd.wait()  # Wait until recording is finished
+    print('Done!')
+
+    time = np.arange(0, duration, 1 / fs)  # time vector
+    plt.plot(time, myrecording)
+    plt.xlabel('Time [s]')
+    plt.ylabel('Amplitude')
+    plt.title('MicNode')
+    plt.show()
diff --git a/examples/device/audio_test_freertos/src/tusb_config.h b/examples/device/audio_test_freertos/src/tusb_config.h
new file mode 100644
index 000000000..8b376a4c3
--- /dev/null
+++ b/examples/device/audio_test_freertos/src/tusb_config.h
@@ -0,0 +1,132 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+// RHPort number used for device can be defined by board.mk, default to port 0
+#ifndef BOARD_TUD_RHPORT
+#define BOARD_TUD_RHPORT      0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUD_MAX_SPEED
+#define BOARD_TUD_MAX_SPEED   OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+// This examples use FreeRTOS
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS                 OPT_OS_FREERTOS
+#endif
+
+// Espressif IDF requires "freertos/" prefix in include path
+#if TUP_MCU_ESPRESSIF
+#define CFG_TUSB_OS_INC_PATH  freertos/
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG              0
+#endif
+
+// Enable Device stack
+#define CFG_TUD_ENABLED       1
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUD_MAX_SPEED     BOARD_TUD_MAX_SPEED
+
+// CFG_TUSB_DEBUG is defined by compiler in DEBUG build
+// #define CFG_TUSB_DEBUG           0
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN          __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+#define CFG_TUD_ENDPOINT0_SIZE    64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_AUDIO             1
+#define CFG_TUD_CDC               0
+#define CFG_TUD_MSC               0
+#define CFG_TUD_HID               0
+#define CFG_TUD_MIDI              0
+#define CFG_TUD_VENDOR            0
+
+//--------------------------------------------------------------------
+// AUDIO CLASS DRIVER CONFIGURATION
+//--------------------------------------------------------------------
+
+// Have a look into audio_device.h for all configurations
+#define CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE                              48000
+
+#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN                                 TUD_AUDIO_MIC_ONE_CH_DESC_LEN
+#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT                                 1                                       // Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes)
+#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ                              64                                      // Size of control request buffer
+
+#define CFG_TUD_AUDIO_ENABLE_EP_IN                                    1
+#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX                    2                                       // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below
+#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX                            1                                       // Driver gets this info from the descriptors - we define it here to use it to setup the descriptors and to do calculations with it below - be aware: for different number of channels you need another descriptor!
+#define CFG_TUD_AUDIO_EP_SZ_IN                                        TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX)
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX                             CFG_TUD_AUDIO_EP_SZ_IN
+#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ                          (TUD_OPT_HIGH_SPEED ? 8 : 1) * CFG_TUD_AUDIO_EP_SZ_IN // Example write FIFO every 1ms, so it should be 8 times larger for HS device
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/examples/device/audio_test_freertos/src/usb_descriptors.c b/examples/device/audio_test_freertos/src/usb_descriptors.c
new file mode 100644
index 000000000..9864377f6
--- /dev/null
+++ b/examples/device/audio_test_freertos/src/usb_descriptors.c
@@ -0,0 +1,181 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 "bsp/board_api.h"
+#include "tusb.h"
+
+/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
+ * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
+ *
+ * Auto ProductID layout's Bitmap:
+ *   [MSB]     AUDIO | MIDI | HID | MSC | CDC          [LSB]
+ */
+#define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) )
+#define USB_PID           (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
+    _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) )
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+tusb_desc_device_t const desc_device =
+{
+    .bLength            = sizeof(tusb_desc_device_t),
+    .bDescriptorType    = TUSB_DESC_DEVICE,
+    .bcdUSB             = 0x0200,
+
+    // Use Interface Association Descriptor (IAD) for Audio
+    // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
+    .bDeviceClass       = TUSB_CLASS_MISC,
+    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
+    .bDeviceProtocol    = MISC_PROTOCOL_IAD,
+    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
+
+    .idVendor           = 0xCafe,
+    .idProduct          = USB_PID,
+    .bcdDevice          = 0x0100,
+
+    .iManufacturer      = 0x01,
+    .iProduct           = 0x02,
+    .iSerialNumber      = 0x03,
+
+    .bNumConfigurations = 0x01
+};
+
+// Invoked when received GET DEVICE DESCRIPTOR
+// Application return pointer to descriptor
+uint8_t const * tud_descriptor_device_cb(void)
+{
+  return (uint8_t const *) &desc_device;
+}
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor
+//--------------------------------------------------------------------+
+enum
+{
+  ITF_NUM_AUDIO_CONTROL = 0,
+  ITF_NUM_AUDIO_STREAMING,
+  ITF_NUM_TOTAL
+};
+
+#define CONFIG_TOTAL_LEN    	(TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_MIC_ONE_CH_DESC_LEN)
+
+#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
+  // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+  // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In etc ...
+  #define EPNUM_AUDIO   0x03
+
+#elif TU_CHECK_MCU(OPT_MCU_NRF5X)
+  // nRF5x ISO can only be endpoint 8
+  #define EPNUM_AUDIO   0x08
+
+#else
+  #define EPNUM_AUDIO   0x01
+#endif
+
+uint8_t const desc_configuration[] =
+{
+    // Config number, interface count, string index, total length, attribute, power in mA
+    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+    // Interface number, string index, EP Out & EP In address, EP size
+    TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_stridx*/ 0, /*_nBytesPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX, /*_nBitsUsedPerSample*/ CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX*8, /*_epin*/ 0x80 | EPNUM_AUDIO, /*_epsize*/ CFG_TUD_AUDIO_EP_SZ_IN)
+};
+
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
+{
+  (void) index; // for multiple configurations
+  return desc_configuration;
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+// String Descriptor Index
+enum {
+  STRID_LANGID = 0,
+  STRID_MANUFACTURER,
+  STRID_PRODUCT,
+  STRID_SERIAL,
+};
+
+// array of pointer to string descriptors
+char const* string_desc_arr [] =
+{
+    (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
+    "PaniRCorp",                   // 1: Manufacturer
+    "MicNode",                     // 2: Product
+    NULL,                          // 3: Serials will use unique ID if possible
+    "UAC2",                        // 4: Audio Interface
+
+};
+
+static uint16_t _desc_str[32 + 1];
+
+// Invoked when received GET STRING DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+  (void) langid;
+  size_t chr_count;
+
+  switch ( index ) {
+    case STRID_LANGID:
+      memcpy(&_desc_str[1], string_desc_arr[0], 2);
+      chr_count = 1;
+      break;
+
+    case STRID_SERIAL:
+      chr_count = board_usb_get_serial(_desc_str + 1, 32);
+      break;
+
+    default:
+      // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
+      // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
+
+      if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+
+      const char *str = string_desc_arr[index];
+
+      // Cap at max char
+      chr_count = strlen(str);
+      size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
+      if ( chr_count > max_count ) chr_count = max_count;
+
+      // Convert ASCII string into UTF-16
+      for ( size_t i = 0; i < chr_count; i++ ) {
+        _desc_str[1 + i] = str[i];
+      }
+      break;
+  }
+
+  // first byte is length (including header), second byte is string type
+  _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+
+  return _desc_str;
+}
diff --git a/examples/device/audio_test_multi_rate/src/main.c b/examples/device/audio_test_multi_rate/src/main.c
index 3e7f40dac..2ff7f10bd 100644
--- a/examples/device/audio_test_multi_rate/src/main.c
+++ b/examples/device/audio_test_multi_rate/src/main.c
@@ -272,7 +272,7 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *
 
         sampFreq = (uint32_t)((audio_control_cur_4_t *)pBuff)->bCur;
 
-        TU_LOG2("Clock set current freq: %lu\r\n", sampFreq);
+        TU_LOG2("Clock set current freq: %" PRIu32 "\r\n", sampFreq);
 
         return true;
       break;
diff --git a/examples/device/board_test/src/tusb_config.h b/examples/device/board_test/src/tusb_config.h
index 1a27cc87f..36eafe807 100644
--- a/examples/device/board_test/src/tusb_config.h
+++ b/examples/device/board_test/src/tusb_config.h
@@ -49,7 +49,7 @@
 #endif
 
 // Espressif IDF requires "freertos/" prefix in include path
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 #define CFG_TUSB_OS_INC_PATH  freertos/
 #endif
 
diff --git a/examples/device/cdc_dual_ports/src/main.c b/examples/device/cdc_dual_ports/src/main.c
index 1167a5d50..ef12186f2 100644
--- a/examples/device/cdc_dual_ports/src/main.c
+++ b/examples/device/cdc_dual_ports/src/main.c
@@ -119,6 +119,26 @@ static void cdc_task(void) {
   }
 }
 
+// Invoked when cdc when line state changed e.g connected/disconnected
+// Use to reset to DFU when disconnect with 1200 bps
+void tud_cdc_line_state_cb(uint8_t instance, bool dtr, bool rts) {
+  (void)rts;
+
+  // DTR = false is counted as disconnected
+  if (!dtr) {
+    // touch1200 only with first CDC instance (Serial)
+    if (instance == 0) {
+      cdc_line_coding_t coding;
+      tud_cdc_get_line_coding(&coding);
+      if (coding.bit_rate == 1200) {
+        if (board_reset_to_bootloader) {
+          board_reset_to_bootloader();
+        }
+      }
+    }
+  }
+}
+
 //--------------------------------------------------------------------+
 // BLINKING TASK
 //--------------------------------------------------------------------+
diff --git a/examples/device/cdc_msc/skip.txt b/examples/device/cdc_msc/skip.txt
index 833fd072c..b6252e405 100644
--- a/examples/device/cdc_msc/skip.txt
+++ b/examples/device/cdc_msc/skip.txt
@@ -1,2 +1,3 @@
 mcu:SAMD11
 family:espressif
+board:ch32v203g_r0_1v0
diff --git a/examples/device/cdc_msc/src/main.c b/examples/device/cdc_msc/src/main.c
index 0d3f97c8f..4581a3bab 100644
--- a/examples/device/cdc_msc/src/main.c
+++ b/examples/device/cdc_msc/src/main.c
@@ -23,10 +23,6 @@
  *
  */
 
-#include 
-#include 
-#include 
-
 #include "bsp/board_api.h"
 #include "tusb.h"
 
diff --git a/examples/device/cdc_msc_freertos/Makefile b/examples/device/cdc_msc_freertos/Makefile
index 13f336f99..fe3fb6482 100644
--- a/examples/device/cdc_msc_freertos/Makefile
+++ b/examples/device/cdc_msc_freertos/Makefile
@@ -1,7 +1,7 @@
 include ../../build_system/make/make.mk
 
 FREERTOS_SRC = lib/FreeRTOS-Kernel
-FREERTOS_PORTABLE_PATH= $(FREERTOS_SRC)/portable/$(if $(USE_IAR),IAR,GCC)
+FREERTOS_PORTABLE_PATH = $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC)
 
 INC += \
 	src \
diff --git a/examples/device/cdc_msc_freertos/skip.txt b/examples/device/cdc_msc_freertos/skip.txt
index a6f96b288..75a79aff4 100644
--- a/examples/device/cdc_msc_freertos/skip.txt
+++ b/examples/device/cdc_msc_freertos/skip.txt
@@ -1,3 +1,5 @@
+mcu:CH32V103
+mcu:CH32V20X
 mcu:CH32V307
 mcu:CXD56
 mcu:F1C100S
@@ -9,5 +11,6 @@ mcu:SAMD11
 mcu:SAMX7X
 mcu:VALENTYUSB_EPTRI
 mcu:RAXXX
+mcu:STM32L0
 family:broadcom_32bit
 family:broadcom_64bit
diff --git a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
index 6cc7a6577..869500ad2 100644
--- a/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
+++ b/examples/device/cdc_msc_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
@@ -67,6 +67,7 @@
 #define configENABLE_FPU								        1
 #define configENABLE_TRUSTZONE					        0
 #define configMINIMAL_SECURE_STACK_SIZE					( 1024 )
+#define configRUN_FREERTOS_SECURE_ONLY          1
 
 #define configUSE_PREEMPTION                    1
 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
diff --git a/examples/device/cdc_msc_freertos/src/freertos_hook.c b/examples/device/cdc_msc_freertos/src/freertos_hook.c
index ab885947c..4920e3fae 100644
--- a/examples/device/cdc_msc_freertos/src/freertos_hook.c
+++ b/examples/device/cdc_msc_freertos/src/freertos_hook.c
@@ -99,9 +99,10 @@ void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, Stack
 void vApplicationSetupTimerInterrupt(void)
 {
   /* Enable CMT0 */
+  unsigned short oldPRCR = SYSTEM.PRCR.WORD;
   SYSTEM.PRCR.WORD = (0xA5u<<8) | TU_BIT(1);
   MSTP(CMT0)       = 0;
-  SYSTEM.PRCR.WORD = (0xA5u<<8);
+  SYSTEM.PRCR.WORD = (0xA5u<<8) | oldPRCR;
 
   CMT0.CMCNT      = 0;
   CMT0.CMCOR      = (unsigned short)(((configPERIPHERAL_CLOCK_HZ/configTICK_RATE_HZ)-1)/128);
diff --git a/examples/device/cdc_msc_freertos/src/main.c b/examples/device/cdc_msc_freertos/src/main.c
index e94e8eaec..607d40270 100644
--- a/examples/device/cdc_msc_freertos/src/main.c
+++ b/examples/device/cdc_msc_freertos/src/main.c
@@ -30,7 +30,7 @@
 #include "bsp/board_api.h"
 #include "tusb.h"
 
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
   // ESP-IDF need "freertos/" prefix in include path.
   // CFG_TUSB_OS_INC_PATH should be defined accordingly.
   #include "freertos/FreeRTOS.h"
@@ -111,14 +111,14 @@ int main(void) {
 #endif
 
   // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
-#if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if !TUP_MCU_ESPRESSIF
   vTaskStartScheduler();
 #endif
 
   return 0;
 }
 
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 void app_main(void) {
   main();
 }
diff --git a/examples/device/cdc_msc_freertos/src/msc_disk.c b/examples/device/cdc_msc_freertos/src/msc_disk.c
index c8a04bb74..d2f8628f1 100644
--- a/examples/device/cdc_msc_freertos/src/msc_disk.c
+++ b/examples/device/cdc_msc_freertos/src/msc_disk.c
@@ -28,6 +28,9 @@
 
 #if CFG_TUD_MSC
 
+// whether host does safe-eject
+static bool ejected = false;
+
 // Some MCU doesn't have enough 8KB SRAM to store the whole disk
 // We will use Flash as read-only disk with board that has
 // CFG_EXAMPLE_MSC_READONLY defined
@@ -137,7 +140,14 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun)
 {
   (void) lun;
 
-  return true; // RAM disk is always ready
+  // RAM disk is ready until ejected
+  if (ejected) {
+    // Additional Sense 3A-00 is NOT_FOUND
+    tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
+    return false;
+  }
+
+  return true;
 }
 
 // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
@@ -166,6 +176,7 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo
     }else
     {
       // unload disk storage
+      ejected = true;
     }
   }
 
@@ -187,6 +198,17 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff
   return (int32_t) bufsize;
 }
 
+bool tud_msc_is_writable_cb (uint8_t lun)
+{
+  (void) lun;
+
+#ifdef CFG_EXAMPLE_MSC_READONLY
+  return false;
+#else
+  return true;
+#endif
+}
+
 // Callback invoked when received WRITE10 command.
 // Process data in buffer to disk's storage and return number of written bytes
 int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
diff --git a/examples/device/cdc_msc_freertos/src/tusb_config.h b/examples/device/cdc_msc_freertos/src/tusb_config.h
index 91efe7d40..e743c9148 100644
--- a/examples/device/cdc_msc_freertos/src/tusb_config.h
+++ b/examples/device/cdc_msc_freertos/src/tusb_config.h
@@ -59,7 +59,7 @@
 #endif
 
 // Espressif IDF requires "freertos/" prefix in include path
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 #define CFG_TUSB_OS_INC_PATH  freertos/
 #endif
 
diff --git a/examples/device/cdc_uac2/src/uac2_app.c b/examples/device/cdc_uac2/src/uac2_app.c
index 98659ea68..c57d98a1a 100644
--- a/examples/device/cdc_uac2/src/uac2_app.c
+++ b/examples/device/cdc_uac2/src/uac2_app.c
@@ -69,7 +69,7 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t
   {
     if (request->bRequest == AUDIO_CS_REQ_CUR)
     {
-      TU_LOG1("Clock get current freq %lu\r\n", current_sample_rate);
+      TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
 
       audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) };
       return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf));
@@ -118,7 +118,7 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t
 
     current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur;
 
-    TU_LOG1("Clock set current freq: %ld\r\n", current_sample_rate);
+    TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
 
     return true;
   }
diff --git a/examples/device/dynamic_configuration/skip.txt b/examples/device/dynamic_configuration/skip.txt
index 833fd072c..b6252e405 100644
--- a/examples/device/dynamic_configuration/skip.txt
+++ b/examples/device/dynamic_configuration/skip.txt
@@ -1,2 +1,3 @@
 mcu:SAMD11
 family:espressif
+board:ch32v203g_r0_1v0
diff --git a/examples/device/hid_composite_freertos/Makefile b/examples/device/hid_composite_freertos/Makefile
index add9e9814..88d602393 100644
--- a/examples/device/hid_composite_freertos/Makefile
+++ b/examples/device/hid_composite_freertos/Makefile
@@ -3,7 +3,7 @@ DEPS_SUBMODULES += lib/FreeRTOS-Kernel
 include ../../build_system/make/make.mk
 
 FREERTOS_SRC = lib/FreeRTOS-Kernel
-FREERTOS_PORTABLE_PATH= $(FREERTOS_SRC)/portable/$(if $(USE_IAR),IAR,GCC)
+FREERTOS_PORTABLE_PATH= $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC)
 
 INC += \
 	src \
diff --git a/examples/device/hid_composite_freertos/skip.txt b/examples/device/hid_composite_freertos/skip.txt
index a6f96b288..c13667f56 100644
--- a/examples/device/hid_composite_freertos/skip.txt
+++ b/examples/device/hid_composite_freertos/skip.txt
@@ -1,3 +1,5 @@
+mcu:CH32V103
+mcu:CH32V20X
 mcu:CH32V307
 mcu:CXD56
 mcu:F1C100S
diff --git a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h b/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
index 6cc7a6577..869500ad2 100644
--- a/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
+++ b/examples/device/hid_composite_freertos/src/FreeRTOSConfig/FreeRTOSConfig.h
@@ -67,6 +67,7 @@
 #define configENABLE_FPU								        1
 #define configENABLE_TRUSTZONE					        0
 #define configMINIMAL_SECURE_STACK_SIZE					( 1024 )
+#define configRUN_FREERTOS_SECURE_ONLY          1
 
 #define configUSE_PREEMPTION                    1
 #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
diff --git a/examples/device/hid_composite_freertos/src/freertos_hook.c b/examples/device/hid_composite_freertos/src/freertos_hook.c
index ab885947c..4920e3fae 100644
--- a/examples/device/hid_composite_freertos/src/freertos_hook.c
+++ b/examples/device/hid_composite_freertos/src/freertos_hook.c
@@ -99,9 +99,10 @@ void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, Stack
 void vApplicationSetupTimerInterrupt(void)
 {
   /* Enable CMT0 */
+  unsigned short oldPRCR = SYSTEM.PRCR.WORD;
   SYSTEM.PRCR.WORD = (0xA5u<<8) | TU_BIT(1);
   MSTP(CMT0)       = 0;
-  SYSTEM.PRCR.WORD = (0xA5u<<8);
+  SYSTEM.PRCR.WORD = (0xA5u<<8) | oldPRCR;
 
   CMT0.CMCNT      = 0;
   CMT0.CMCOR      = (unsigned short)(((configPERIPHERAL_CLOCK_HZ/configTICK_RATE_HZ)-1)/128);
diff --git a/examples/device/hid_composite_freertos/src/main.c b/examples/device/hid_composite_freertos/src/main.c
index ff2cb635e..2bd545bc2 100644
--- a/examples/device/hid_composite_freertos/src/main.c
+++ b/examples/device/hid_composite_freertos/src/main.c
@@ -31,7 +31,7 @@
 #include "tusb.h"
 #include "usb_descriptors.h"
 
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
   // ESP-IDF need "freertos/" prefix in include path.
   // CFG_TUSB_OS_INC_PATH should be defined accordingly.
   #include "freertos/FreeRTOS.h"
@@ -113,14 +113,14 @@ int main(void)
   xTimerStart(blinky_tm, 0);
 
   // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
-#if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if !TUP_MCU_ESPRESSIF
   vTaskStartScheduler();
 #endif
 
   return 0;
 }
 
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 void app_main(void)
 {
   main();
diff --git a/examples/device/hid_composite_freertos/src/tusb_config.h b/examples/device/hid_composite_freertos/src/tusb_config.h
index 3ba9bf311..0689e0f23 100644
--- a/examples/device/hid_composite_freertos/src/tusb_config.h
+++ b/examples/device/hid_composite_freertos/src/tusb_config.h
@@ -59,7 +59,7 @@
 #endif
 
 // Espressif IDF requires "freertos/" prefix in include path
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 #define CFG_TUSB_OS_INC_PATH  freertos/
 #endif
 
diff --git a/examples/device/msc_dual_lun/src/main.c b/examples/device/msc_dual_lun/src/main.c
index de402d3da..aabd0bf8a 100644
--- a/examples/device/msc_dual_lun/src/main.c
+++ b/examples/device/msc_dual_lun/src/main.c
@@ -39,7 +39,7 @@
  * - 1000 ms : device mounted
  * - 2500 ms : device is suspended
  */
-enum  {
+enum {
   BLINK_NOT_MOUNTED = 250,
   BLINK_MOUNTED = 1000,
   BLINK_SUSPENDED = 2500,
@@ -50,8 +50,7 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
 void led_blinking_task(void);
 
 /*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
   board_init();
 
   // init device stack on configured roothub port
@@ -61,8 +60,7 @@ int main(void)
     board_init_after_tusb();
   }
 
-  while (1)
-  {
+  while (1) {
     tud_task(); // tinyusb device task
     led_blinking_task();
   }
@@ -73,42 +71,37 @@ int main(void)
 //--------------------------------------------------------------------+
 
 // Invoked when device is mounted
-void tud_mount_cb(void)
-{
+void tud_mount_cb(void) {
   blink_interval_ms = BLINK_MOUNTED;
 }
 
 // Invoked when device is unmounted
-void tud_umount_cb(void)
-{
+void tud_umount_cb(void) {
   blink_interval_ms = BLINK_NOT_MOUNTED;
 }
 
 // Invoked when usb bus is suspended
 // remote_wakeup_en : if host allow us  to perform remote wakeup
 // Within 7ms, device must draw an average of current less than 2.5 mA from bus
-void tud_suspend_cb(bool remote_wakeup_en)
-{
+void tud_suspend_cb(bool remote_wakeup_en) {
   (void) remote_wakeup_en;
   blink_interval_ms = BLINK_SUSPENDED;
 }
 
 // Invoked when usb bus is resumed
-void tud_resume_cb(void)
-{
+void tud_resume_cb(void) {
   blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
 }
 
 //--------------------------------------------------------------------+
 // BLINKING TASK
 //--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
+void led_blinking_task(void) {
   static uint32_t start_ms = 0;
   static bool led_state = false;
 
   // Blink every interval ms
-  if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+  if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
   start_ms += blink_interval_ms;
 
   board_led_write(led_state);
diff --git a/examples/device/msc_dual_lun/src/msc_disk_dual.c b/examples/device/msc_dual_lun/src/msc_disk_dual.c
index 4f0f6410f..b44b77c6c 100644
--- a/examples/device/msc_dual_lun/src/msc_disk_dual.c
+++ b/examples/device/msc_dual_lun/src/msc_disk_dual.c
@@ -34,9 +34,13 @@
 // Some MCU doesn't have enough 8KB SRAM to store the whole disk
 // We will use Flash as read-only disk with board that has
 // CFG_EXAMPLE_MSC_READONLY defined
+#if defined(CFG_EXAMPLE_MSC_READONLY) || defined(CFG_EXAMPLE_MSC_DUAL_READONLY)
+  #define MSC_CONST const
+#else
+  #define MSC_CONST
+#endif
 
-enum
-{
+enum {
   DISK_BLOCK_NUM  = 16, // 8KB is the smallest size that windows allow to mount
   DISK_BLOCK_SIZE = 512
 };
@@ -51,10 +55,7 @@ If you find any bugs or get any questions, feel free to file an\r\n\
 issue at github.com/hathach/tinyusb"
 
 
-#ifdef CFG_EXAMPLE_MSC_READONLY
-const
-#endif
-uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
+MSC_CONST uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
 {
   //------------- Block0: Boot Sector -------------//
   // byte_per_sector    = DISK_BLOCK_SIZE; fat12_sector_num_16  = DISK_BLOCK_NUM;
@@ -132,10 +133,7 @@ uint8_t msc_disk0[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
 If you find any bugs or get any questions, feel free to file an\r\n\
 issue at github.com/hathach/tinyusb"
 
-#ifdef CFG_EXAMPLE_MSC_READONLY
-const
-#endif
-uint8_t msc_disk1[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
+MSC_CONST uint8_t msc_disk1[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
 {
   //------------- Block0: Boot Sector -------------//
   // byte_per_sector    = DISK_BLOCK_SIZE; fat12_sector_num_16  = DISK_BLOCK_NUM;
@@ -206,15 +204,13 @@ uint8_t msc_disk1[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] =
 };
 
 // Invoked to determine max LUN
-uint8_t tud_msc_get_maxlun_cb(void)
-{
+uint8_t tud_msc_get_maxlun_cb(void) {
   return 2; // dual LUN
 }
 
 // Invoked when received SCSI_CMD_INQUIRY
 // Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
-void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
-{
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
   (void) lun; // use same ID for both LUNs
 
   const char vid[] = "TinyUSB";
@@ -228,8 +224,7 @@ void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16
 
 // Invoked when received Test Unit Ready command.
 // return true allowing host to read/write this LUN e.g SD card inserted
-bool tud_msc_test_unit_ready_cb(uint8_t lun)
-{
+bool tud_msc_test_unit_ready_cb(uint8_t lun) {
   if ( lun == 1 && board_button_read() ) return false;
 
   return true; // RAM disk is always ready
@@ -237,8 +232,7 @@ bool tud_msc_test_unit_ready_cb(uint8_t lun)
 
 // Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
 // Application update block count and block size
-void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size)
-{
+void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) {
   (void) lun;
 
   *block_count = DISK_BLOCK_NUM;
@@ -248,18 +242,14 @@ void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_siz
 // Invoked when received Start Stop Unit command
 // - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
 // - Start = 1 : active mode, if load_eject = 1 : load disk storage
-bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
-{
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
   (void) lun;
   (void) power_condition;
 
-  if ( load_eject )
-  {
-    if (start)
-    {
+  if (load_eject) {
+    if (start) {
       // load disk storage
-    }else
-    {
+    } else {
       // unload disk storage
     }
   }
@@ -269,10 +259,9 @@ bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, boo
 
 // Callback invoked when received READ10 command.
 // Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
-int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
-{
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize) {
   // out of ramdisk
-  if ( lba >= DISK_BLOCK_NUM ) return -1;
+  if (lba >= DISK_BLOCK_NUM) return -1;
 
   uint8_t const* addr = (lun ? msc_disk1[lba] : msc_disk0[lba]) + offset;
   memcpy(buffer, addr, bufsize);
@@ -280,11 +269,10 @@ int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buff
   return (int32_t) bufsize;
 }
 
-bool tud_msc_is_writable_cb (uint8_t lun)
-{
+bool tud_msc_is_writable_cb(uint8_t lun) {
   (void) lun;
 
-#ifdef CFG_EXAMPLE_MSC_READONLY
+#if defined(CFG_EXAMPLE_MSC_READONLY) || defined(CFG_EXAMPLE_MSC_DUAL_READONLY)
   return false;
 #else
   return true;
@@ -293,16 +281,18 @@ bool tud_msc_is_writable_cb (uint8_t lun)
 
 // Callback invoked when received WRITE10 command.
 // Process data in buffer to disk's storage and return number of written bytes
-int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize)
-{
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t* buffer, uint32_t bufsize) {
   // out of ramdisk
-  if ( lba >= DISK_BLOCK_NUM ) return -1;
+  if (lba >= DISK_BLOCK_NUM) return -1;
 
-#ifndef CFG_EXAMPLE_MSC_READONLY
+#if defined(CFG_EXAMPLE_MSC_READONLY) || defined(CFG_EXAMPLE_MSC_DUAL_READONLY)
+  (void) lun;
+  (void) lba;
+  (void) offset;
+  (void) buffer;
+#else
   uint8_t* addr = (lun ? msc_disk1[lba] : msc_disk0[lba])  + offset;
   memcpy(addr, buffer, bufsize);
-#else
-  (void) lun; (void) lba; (void) offset; (void) buffer;
 #endif
 
   return (int32_t) bufsize;
@@ -310,38 +300,30 @@ int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t*
 
 // Callback invoked when received an SCSI command not in built-in list below
 // - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
-// - READ10 and WRITE10 has their own callbacks
-int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize)
-{
-  // read10 & write10 has their own callback and MUST not be handled here
-
+// - READ10 and WRITE10 has their own callbacks (MUST not be handled here)
+int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void* buffer, uint16_t bufsize) {
   void const* response = NULL;
   int32_t resplen = 0;
 
   // most scsi handled is input
   bool in_xfer = true;
 
-  switch (scsi_cmd[0])
-  {
+  switch (scsi_cmd[0]) {
     default:
       // Set Sense = Invalid Command Operation
       tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
 
       // negative means error -> tinyusb could stall and/or response with failed status
-      resplen = -1;
-    break;
+      return -1;
   }
 
   // return resplen must not larger than bufsize
-  if ( resplen > bufsize ) resplen = bufsize;
+  if (resplen > bufsize) resplen = bufsize;
 
-  if ( response && (resplen > 0) )
-  {
-    if(in_xfer)
-    {
+  if (response && (resplen > 0)) {
+    if (in_xfer) {
       memcpy(buffer, response, (size_t) resplen);
-    }else
-    {
+    } else {
       // SCSI output
     }
   }
diff --git a/examples/device/net_lwip_webserver/CMakeLists.txt b/examples/device/net_lwip_webserver/CMakeLists.txt
index a16b8bd71..13923b583 100644
--- a/examples/device/net_lwip_webserver/CMakeLists.txt
+++ b/examples/device/net_lwip_webserver/CMakeLists.txt
@@ -5,7 +5,14 @@ include(${CMAKE_CURRENT_LIST_DIR}/../../../hw/bsp/family_support.cmake)
 # gets PROJECT name for the example (e.g. -)
 family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
 
+# Prefer the tinyusb lwip
 set(LWIP ${TOP}/lib/lwip)
+
+# If we can't find one from tinyusb then check cmake var before giving up
+if (NOT EXISTS ${LWIP}/src)
+  set(LWIP ${TINYUSB_LWIP_PATH})
+endif()
+
 if (NOT EXISTS ${LWIP}/src)
   family_example_missing_dependency(${PROJECT} "lib/lwip")
   return()
@@ -74,6 +81,7 @@ target_sources(${PROJECT} PUBLIC
   ${LWIP}/src/netif/slipif.c
   ${LWIP}/src/apps/http/httpd.c
   ${LWIP}/src/apps/http/fs.c
+  ${LWIP}/src/apps/lwiperf/lwiperf.c
   )
 
 # due to warnings from other net source, we need to prevent error from some of the warnings options
diff --git a/examples/device/net_lwip_webserver/Makefile b/examples/device/net_lwip_webserver/Makefile
index 22426ba0d..141532466 100644
--- a/examples/device/net_lwip_webserver/Makefile
+++ b/examples/device/net_lwip_webserver/Makefile
@@ -63,6 +63,7 @@ SRC_C += \
   lib/lwip/src/netif/slipif.c \
   lib/lwip/src/apps/http/httpd.c \
   lib/lwip/src/apps/http/fs.c \
+  lib/lwip/src/apps/lwiperf/lwiperf.c \
   lib/networking/dhserver.c \
   lib/networking/dnserver.c \
   lib/networking/rndis_reports.c
diff --git a/examples/device/net_lwip_webserver/skip.txt b/examples/device/net_lwip_webserver/skip.txt
index 75aa2ef14..85c450e82 100644
--- a/examples/device/net_lwip_webserver/skip.txt
+++ b/examples/device/net_lwip_webserver/skip.txt
@@ -1,12 +1,18 @@
+mcu:CH32V103
+mcu:CH32V20X
 mcu:LPC11UXX
 mcu:LPC13XX
+mcu:LPC15XX
 mcu:MSP430x5xx
 mcu:NUC121
 mcu:SAMD11
 mcu:STM32L0
+mcu:STM32F0
 mcu:KINETIS_KL
 family:broadcom_64bit
 family:broadcom_32bit
+family:espressif
 board:curiosity_nano
 board:frdm_kl25z
-family:espressif
+# lpc55 has weird error 'ncm_interface' causes a section type conflict with 'ntb_parameters'
+family:lpc55
diff --git a/examples/device/net_lwip_webserver/src/lwipopts.h b/examples/device/net_lwip_webserver/src/lwipopts.h
index 336c9243d..41e8f0d67 100644
--- a/examples/device/net_lwip_webserver/src/lwipopts.h
+++ b/examples/device/net_lwip_webserver/src/lwipopts.h
@@ -48,8 +48,8 @@
 #define LWIP_IP_ACCEPT_UDP_PORT(p)      ((p) == PP_NTOHS(67))
 
 #define TCP_MSS                         (1500 /*mtu*/ - 20 /*iphdr*/ - 20 /*tcphhr*/)
-#define TCP_SND_BUF                     (2 * TCP_MSS)
-#define TCP_WND                         (TCP_MSS)
+#define TCP_SND_BUF                     (4 * TCP_MSS)
+#define TCP_WND                         (4 * TCP_MSS)
 
 #define ETHARP_SUPPORT_STATIC_ENTRIES   1
 
@@ -59,7 +59,7 @@
 
 #define LWIP_SINGLE_NETIF               1
 
-#define PBUF_POOL_SIZE                  2
+#define PBUF_POOL_SIZE                  4
 
 #define HTTPD_USE_CUSTOM_FSDATA         0
 
diff --git a/examples/device/net_lwip_webserver/src/main.c b/examples/device/net_lwip_webserver/src/main.c
index 7d98aacbc..791e09b4f 100644
--- a/examples/device/net_lwip_webserver/src/main.c
+++ b/examples/device/net_lwip_webserver/src/main.c
@@ -48,12 +48,17 @@ try changing the first byte of tud_network_mac_address[] below from 0x02 to 0x00
 
 #include "dhserver.h"
 #include "dnserver.h"
+#include "httpd.h"
+#include "lwip/ethip6.h"
 #include "lwip/init.h"
 #include "lwip/timeouts.h"
-#include "lwip/ethip6.h"
-#include "httpd.h"
 
-#define INIT_IP4(a,b,c,d) { PP_HTONL(LWIP_MAKEU32(a,b,c,d)) }
+#ifdef INCLUDE_IPERF
+  #include "lwip/apps/lwiperf.h"
+#endif
+
+#define INIT_IP4(a, b, c, d) \
+  { PP_HTONL(LWIP_MAKEU32(a, b, c, d)) }
 
 /* lwip context */
 static struct netif netif_data;
@@ -64,44 +69,40 @@ static struct pbuf *received_frame;
 /* this is used by this code, ./class/net/net_driver.c, and usb_descriptors.c */
 /* ideally speaking, this should be generated from the hardware's unique ID (if available) */
 /* it is suggested that the first byte is 0x02 to indicate a link-local address */
-uint8_t tud_network_mac_address[6] = {0x02,0x02,0x84,0x6A,0x96,0x00};
+uint8_t tud_network_mac_address[6] = {0x02, 0x02, 0x84, 0x6A, 0x96, 0x00};
 
 /* network parameters of this MCU */
-static const ip4_addr_t ipaddr  = INIT_IP4(192, 168, 7, 1);
+static const ip4_addr_t ipaddr = INIT_IP4(192, 168, 7, 1);
 static const ip4_addr_t netmask = INIT_IP4(255, 255, 255, 0);
 static const ip4_addr_t gateway = INIT_IP4(0, 0, 0, 0);
 
 /* database IP addresses that can be offered to the host; this must be in RAM to store assigned MAC addresses */
-static dhcp_entry_t entries[] =
-{
-    /* mac ip address                          lease time */
-    { {0}, INIT_IP4(192, 168, 7, 2), 24 * 60 * 60 },
-    { {0}, INIT_IP4(192, 168, 7, 3), 24 * 60 * 60 },
-    { {0}, INIT_IP4(192, 168, 7, 4), 24 * 60 * 60 },
+static dhcp_entry_t entries[] = {
+    /* mac ip address               lease time */
+    {{0}, INIT_IP4(192, 168, 7, 2), 24 * 60 * 60},
+    {{0}, INIT_IP4(192, 168, 7, 3), 24 * 60 * 60},
+    {{0}, INIT_IP4(192, 168, 7, 4), 24 * 60 * 60},
 };
 
-static const dhcp_config_t dhcp_config =
-{
-    .router = INIT_IP4(0, 0, 0, 0),            /* router address (if any) */
-    .port = 67,                                /* listen port */
-    .dns = INIT_IP4(192, 168, 7, 1),           /* dns server (if any) */
-    "usb",                                     /* dns suffix */
-    TU_ARRAY_SIZE(entries),                    /* num entry */
-    entries                                    /* entries */
+static const dhcp_config_t dhcp_config = {
+    .router = INIT_IP4(0, 0, 0, 0),  /* router address (if any) */
+    .port = 67,                      /* listen port */
+    .dns = INIT_IP4(192, 168, 7, 1), /* dns server (if any) */
+    "usb",                           /* dns suffix */
+    TU_ARRAY_SIZE(entries),          /* num entry */
+    entries                          /* entries */
 };
-static err_t linkoutput_fn(struct netif *netif, struct pbuf *p)
-{
-  (void)netif;
 
-  for (;;)
-  {
+static err_t linkoutput_fn(struct netif *netif, struct pbuf *p) {
+  (void) netif;
+
+  for (;;) {
     /* if TinyUSB isn't ready, we must signal back to lwip that there is nothing we can do */
     if (!tud_ready())
       return ERR_USE;
 
     /* if the network driver can accept another packet, we make it happen */
-    if (tud_network_can_xmit(p->tot_len))
-    {
+    if (tud_network_can_xmit(p->tot_len)) {
       tud_network_xmit(p, 0 /* unused for this example */);
       return ERR_OK;
     }
@@ -111,20 +112,17 @@ static err_t linkoutput_fn(struct netif *netif, struct pbuf *p)
   }
 }
 
-static err_t ip4_output_fn(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr)
-{
+static err_t ip4_output_fn(struct netif *netif, struct pbuf *p, const ip4_addr_t *addr) {
   return etharp_output(netif, p, addr);
 }
 
 #if LWIP_IPV6
-static err_t ip6_output_fn(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr)
-{
+static err_t ip6_output_fn(struct netif *netif, struct pbuf *p, const ip6_addr_t *addr) {
   return ethip6_output(netif, p, addr);
 }
 #endif
 
-static err_t netif_init_cb(struct netif *netif)
-{
+static err_t netif_init_cb(struct netif *netif) {
   LWIP_ASSERT("netif != NULL", (netif != NULL));
   netif->mtu = CFG_TUD_NET_MTU;
   netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP | NETIF_FLAG_UP;
@@ -139,8 +137,7 @@ static err_t netif_init_cb(struct netif *netif)
   return ERR_OK;
 }
 
-static void init_lwip(void)
-{
+static void init_lwip(void) {
   struct netif *netif = &netif_data;
 
   lwip_init();
@@ -158,28 +155,23 @@ static void init_lwip(void)
 }
 
 /* handle any DNS requests from dns-server */
-bool dns_query_proc(const char *name, ip4_addr_t *addr)
-{
-  if (0 == strcmp(name, "tiny.usb"))
-  {
+bool dns_query_proc(const char *name, ip4_addr_t *addr) {
+  if (0 == strcmp(name, "tiny.usb")) {
     *addr = ipaddr;
     return true;
   }
   return false;
 }
 
-bool tud_network_recv_cb(const uint8_t *src, uint16_t size)
-{
+bool tud_network_recv_cb(const uint8_t *src, uint16_t size) {
   /* this shouldn't happen, but if we get another packet before
   parsing the previous, we must signal our inability to accept it */
   if (received_frame) return false;
 
-  if (size)
-  {
+  if (size) {
     struct pbuf *p = pbuf_alloc(PBUF_RAW, size, PBUF_POOL);
 
-    if (p)
-    {
+    if (p) {
       /* pbuf_alloc() has already initialized struct; all we need to do is copy the data */
       memcpy(p->payload, src, size);
 
@@ -191,20 +183,17 @@ bool tud_network_recv_cb(const uint8_t *src, uint16_t size)
   return true;
 }
 
-uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg)
-{
-  struct pbuf *p = (struct pbuf *)ref;
+uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg) {
+  struct pbuf *p = (struct pbuf *) ref;
 
-  (void)arg; /* unused for this example */
+  (void) arg; /* unused for this example */
 
   return pbuf_copy_partial(p, dst, p->tot_len, 0);
 }
 
-static void service_traffic(void)
-{
+static void service_traffic(void) {
   /* handle any packet received by tud_network_recv_cb() */
-  if (received_frame)
-  {
+  if (received_frame) {
     ethernet_input(received_frame, &netif_data);
     pbuf_free(received_frame);
     received_frame = NULL;
@@ -214,18 +203,15 @@ static void service_traffic(void)
   sys_check_timeouts();
 }
 
-void tud_network_init_cb(void)
-{
+void tud_network_init_cb(void) {
   /* if the network is re-initializing and we have a leftover packet, we must do a cleanup */
-  if (received_frame)
-  {
+  if (received_frame) {
     pbuf_free(received_frame);
     received_frame = NULL;
   }
 }
 
-int main(void)
-{
+int main(void) {
   /* initialize TinyUSB */
   board_init();
 
@@ -243,8 +229,12 @@ int main(void)
   while (dnserv_init(IP_ADDR_ANY, 53, dns_query_proc) != ERR_OK);
   httpd_init();
 
-  while (1)
-  {
+#ifdef INCLUDE_IPERF
+  // test with: iperf -c 192.168.7.1 -e -i 1 -M 5000 -l 8192 -r
+  lwiperf_start_tcp_server_default(NULL, NULL);
+#endif
+
+  while (1) {
     tud_task();
     service_traffic();
   }
@@ -253,17 +243,14 @@ int main(void)
 }
 
 /* lwip has provision for using a mutex, when applicable */
-sys_prot_t sys_arch_protect(void)
-{
+sys_prot_t sys_arch_protect(void) {
   return 0;
 }
-void sys_arch_unprotect(sys_prot_t pval)
-{
-  (void)pval;
+void sys_arch_unprotect(sys_prot_t pval) {
+  (void) pval;
 }
 
 /* lwip needs a millisecond time source, and the TinyUSB board support code has one available */
-uint32_t sys_now(void)
-{
+uint32_t sys_now(void) {
   return board_millis();
 }
diff --git a/examples/device/net_lwip_webserver/src/tusb_config.h b/examples/device/net_lwip_webserver/src/tusb_config.h
index fe72ecdfe..d3e094517 100644
--- a/examples/device/net_lwip_webserver/src/tusb_config.h
+++ b/examples/device/net_lwip_webserver/src/tusb_config.h
@@ -27,21 +27,23 @@
 #define _TUSB_CONFIG_H_
 
 #ifdef __cplusplus
- extern "C" {
+extern "C" {
 #endif
 
+#include "lwipopts.h"
+
 //--------------------------------------------------------------------+
 // Board Specific Configuration
 //--------------------------------------------------------------------+
 
 // RHPort number used for device can be defined by board.mk, default to port 0
 #ifndef BOARD_TUD_RHPORT
-#define BOARD_TUD_RHPORT      0
+  #define BOARD_TUD_RHPORT 0
 #endif
 
 // RHPort max operational speed can defined by board.mk
 #ifndef BOARD_TUD_MAX_SPEED
-#define BOARD_TUD_MAX_SPEED   OPT_MODE_DEFAULT_SPEED
+  #define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED
 #endif
 
 //--------------------------------------------------------------------
@@ -50,22 +52,22 @@
 
 // defined by compiler flags for flexibility
 #ifndef CFG_TUSB_MCU
-#error CFG_TUSB_MCU must be defined
+  #error CFG_TUSB_MCU must be defined
 #endif
 
 #ifndef CFG_TUSB_OS
-#define CFG_TUSB_OS           OPT_OS_NONE
+  #define CFG_TUSB_OS OPT_OS_NONE
 #endif
 
 #ifndef CFG_TUSB_DEBUG
-#define CFG_TUSB_DEBUG        0
+  #define CFG_TUSB_DEBUG 0
 #endif
 
 // Enable Device stack
-#define CFG_TUD_ENABLED       1
+#define CFG_TUD_ENABLED 1
 
 // Default is max speed that hardware controller could support with on-chip PHY
-#define CFG_TUD_MAX_SPEED     BOARD_TUD_MAX_SPEED
+#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED
 
 /* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
  * Tinyusb use follows macros to declare transferring memory so that they can be put
@@ -75,11 +77,45 @@
  * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
  */
 #ifndef CFG_TUSB_MEM_SECTION
-#define CFG_TUSB_MEM_SECTION
+  #define CFG_TUSB_MEM_SECTION
 #endif
 
 #ifndef CFG_TUSB_MEM_ALIGN
-#define CFG_TUSB_MEM_ALIGN        __attribute__ ((aligned(4)))
+  #define CFG_TUSB_MEM_ALIGN __attribute__((aligned(4)))
+#endif
+
+// Use different configurations to test all net devices (also due to resource limitations)
+#if TU_CHECK_MCU(OPT_MCU_LPC15XX, OPT_MCU_LPC40XX, OPT_MCU_LPC51UXX, OPT_MCU_LPC54)
+  #define USE_ECM 1
+#elif TU_CHECK_MCU(OPT_MCU_SAMD21, OPT_MCU_SAML21, OPT_MCU_SAML22)
+  #define USE_ECM 1
+#elif TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32F1)
+  #define USE_ECM 1
+#else
+  #define USE_ECM 0
+  #define INCLUDE_IPERF
+#endif
+
+//--------------------------------------------------------------------
+// NCM CLASS CONFIGURATION, SEE "ncm.h" FOR PERFORMANCE TUNING
+//--------------------------------------------------------------------
+
+// Must be >> MTU
+// Can be set to 2048 without impact
+#define CFG_TUD_NCM_IN_NTB_MAX_SIZE (2 * TCP_MSS + 100)
+
+// Must be >> MTU
+// Can be set to smaller values if wNtbOutMaxDatagrams==1
+#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE (2 * TCP_MSS + 100)
+
+// Number of NCM transfer blocks for reception side
+#ifndef CFG_TUD_NCM_OUT_NTB_N
+  #define CFG_TUD_NCM_OUT_NTB_N 1
+#endif
+
+// Number of NCM transfer blocks for transmission side
+#ifndef CFG_TUD_NCM_IN_NTB_N
+  #define CFG_TUD_NCM_IN_NTB_N 1
 #endif
 
 //--------------------------------------------------------------------
@@ -87,18 +123,18 @@
 //--------------------------------------------------------------------
 
 #ifndef CFG_TUD_ENDPOINT0_SIZE
-#define CFG_TUD_ENDPOINT0_SIZE    64
+  #define CFG_TUD_ENDPOINT0_SIZE 64
 #endif
 
 //------------- CLASS -------------//
 
 // Network class has 2 drivers: ECM/RNDIS and NCM.
 // Only one of the drivers can be enabled
-#define CFG_TUD_ECM_RNDIS     1
-#define CFG_TUD_NCM           (1-CFG_TUD_ECM_RNDIS)
+#define CFG_TUD_ECM_RNDIS     USE_ECM
+#define CFG_TUD_NCM           (1 - CFG_TUD_ECM_RNDIS)
 
 #ifdef __cplusplus
- }
+}
 #endif
 
 #endif /* _TUSB_CONFIG_H_ */
diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c
index 35b7ac94b..0ea6e7025 100644
--- a/examples/device/uac2_headset/src/main.c
+++ b/examples/device/uac2_headset/src/main.c
@@ -35,11 +35,7 @@
 //--------------------------------------------------------------------+
 
 // List of supported sample rates
-#if defined(__RX__)
-  const uint32_t sample_rates[] = {44100, 48000};
-#else
-  const uint32_t sample_rates[] = {44100, 48000, 88200, 96000};
-#endif
+const uint32_t sample_rates[] = {44100, 48000};
 
 uint32_t current_sample_rate  = 44100;
 
@@ -161,7 +157,7 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t
   {
     if (request->bRequest == AUDIO_CS_REQ_CUR)
     {
-      TU_LOG1("Clock get current freq %lu\r\n", current_sample_rate);
+      TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
 
       audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_sample_rate) };
       return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf));
@@ -210,7 +206,7 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t
 
     current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur;
 
-    TU_LOG1("Clock set current freq: %ld\r\n", current_sample_rate);
+    TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
 
     return true;
   }
diff --git a/examples/device/uac2_headset/src/tusb_config.h b/examples/device/uac2_headset/src/tusb_config.h
index 4b08fa676..328e35f52 100644
--- a/examples/device/uac2_headset/src/tusb_config.h
+++ b/examples/device/uac2_headset/src/tusb_config.h
@@ -114,11 +114,9 @@ extern "C" {
 #define CFG_TUD_AUDIO_FUNC_1_N_FORMATS                               2
 
 // Audio format type I specifications
-#if defined(__RX__)
-#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE                         48000     // 16bit/48kHz is the best quality for Renesas RX
-#else
-#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE                         96000     // 24bit/96kHz is the best quality for full-speed, high-speed is needed beyond this
-#endif
+/* 24bit/48kHz is the best quality for headset or 24bit/96kHz for 2ch speaker,
+   high-speed is needed beyond this */
+#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE                         48000
 #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX                           1
 #define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX                           2
 
diff --git a/examples/device/video_capture/skip.txt b/examples/device/video_capture/skip.txt
index db64cc639..cb0c7d2e6 100644
--- a/examples/device/video_capture/skip.txt
+++ b/examples/device/video_capture/skip.txt
@@ -1,4 +1,5 @@
+mcu:CH32V103
+mcu:CH32V20X
 mcu:MSP430x5xx
 mcu:NUC121
 mcu:SAMD11
-family:espressif
diff --git a/examples/device/video_capture/src/main.c b/examples/device/video_capture/src/main.c
index 2a2b0961f..ddb51e03a 100644
--- a/examples/device/video_capture/src/main.c
+++ b/examples/device/video_capture/src/main.c
@@ -288,7 +288,7 @@ void led_blinking_task(void* param) {
 #define BLINKY_STACK_SIZE   configMINIMAL_STACK_SIZE
 #define VIDEO_STACK_SIZE    (configMINIMAL_STACK_SIZE*4)
 
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
   #define USBD_STACK_SIZE     4096
   int main(void);
   void app_main(void) {
@@ -344,7 +344,7 @@ void freertos_init_task(void) {
   #endif
 
   // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
-  #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+  #if !TUP_MCU_ESPRESSIF
   vTaskStartScheduler();
   #endif
 }
diff --git a/examples/device/video_capture/src/tusb_config.h b/examples/device/video_capture/src/tusb_config.h
index bdfc37d87..21483a2da 100644
--- a/examples/device/video_capture/src/tusb_config.h
+++ b/examples/device/video_capture/src/tusb_config.h
@@ -58,7 +58,7 @@
 #endif
 
 // Espressif IDF requires "freertos/" prefix in include path
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 #define CFG_TUSB_OS_INC_PATH  freertos/
 #endif
 
diff --git a/examples/device/video_capture_2ch/skip.txt b/examples/device/video_capture_2ch/skip.txt
index b36abf721..af3b0de04 100644
--- a/examples/device/video_capture_2ch/skip.txt
+++ b/examples/device/video_capture_2ch/skip.txt
@@ -2,7 +2,10 @@ mcu:MSP430x5xx
 mcu:NUC121
 mcu:SAMD11
 mcu:GD32VF103
+mcu:CH32V103
+mcu:CH32V20X
 mcu:CH32V307
+mcu:STM32L0
 family:espressif
 board:curiosity_nano
 board:kuiic
@@ -11,3 +14,5 @@ board:lpcxpresso11u68
 board:stm32f303disco
 board:stm32l412nucleo
 board:ek_tm4c123gxl
+board:uno_r4
+board:ra4m1_ek
diff --git a/examples/device/video_capture_2ch/src/main.c b/examples/device/video_capture_2ch/src/main.c
index 82bc7d6ca..e8c2ec6b0 100644
--- a/examples/device/video_capture_2ch/src/main.c
+++ b/examples/device/video_capture_2ch/src/main.c
@@ -184,7 +184,7 @@ size_t get_framebuf(uint_fast8_t ctl_idx, uint_fast8_t stm_idx, size_t fnum, voi
   if (idx == 0) {
     // stream 0 use uncompressed YUY2 frame
     #if defined(CFG_EXAMPLE_VIDEO_READONLY)
-    *fb = (void*)(uintptr_t ) framebuf_yuy2_readonly[(fnum % (FRAME_WIDTH / 2)) * 4];
+    *fb = (void*)(uintptr_t ) &framebuf_yuy2_readonly[(fnum % (FRAME_WIDTH / 2)) * 4];
     #else
     fill_color_bar(framebuf_yuy2, frame_num[idx]);
     *fb = framebuf_yuy2;
@@ -296,7 +296,7 @@ void led_blinking_task(void* param) {
 #define BLINKY_STACK_SIZE   configMINIMAL_STACK_SIZE
 #define VIDEO_STACK_SIZE    (configMINIMAL_STACK_SIZE*4)
 
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
   #define USBD_STACK_SIZE     4096
   int main(void);
   void app_main(void) {
@@ -352,7 +352,7 @@ void freertos_init_task(void) {
   #endif
 
   // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
-  #if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+  #if !TUP_MCU_ESPRESSIF
   vTaskStartScheduler();
   #endif
 }
diff --git a/examples/device/video_capture_2ch/src/tusb_config.h b/examples/device/video_capture_2ch/src/tusb_config.h
index fe66d63b5..43c7dfc90 100644
--- a/examples/device/video_capture_2ch/src/tusb_config.h
+++ b/examples/device/video_capture_2ch/src/tusb_config.h
@@ -58,7 +58,7 @@
 #endif
 
 // Espressif IDF requires "freertos/" prefix in include path
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 #define CFG_TUSB_OS_INC_PATH  freertos/
 #endif
 
diff --git a/examples/dual/CMakeLists.txt b/examples/dual/CMakeLists.txt
index 15081cf26..f11074e93 100644
--- a/examples/dual/CMakeLists.txt
+++ b/examples/dual/CMakeLists.txt
@@ -10,4 +10,5 @@ if (FAMILY STREQUAL "rp2040" AND NOT TARGET tinyusb_pico_pio_usb)
 else ()
   # family_add_subdirectory will filter what to actually add based on selected FAMILY
   family_add_subdirectory(host_hid_to_device_cdc)
+  family_add_subdirectory(host_info_to_device_cdc)
 endif ()
diff --git a/examples/dual/host_hid_to_device_cdc/src/main.c b/examples/dual/host_hid_to_device_cdc/src/main.c
index 96a2beff5..0b4165e38 100644
--- a/examples/dual/host_hid_to_device_cdc/src/main.c
+++ b/examples/dual/host_hid_to_device_cdc/src/main.c
@@ -55,14 +55,14 @@ const uint8_t colemak[128] = {
 };
 #endif
 
-static uint8_t const keycode2ascii[128][2] =  { HID_KEYCODE_TO_ASCII };
+static uint8_t const keycode2ascii[128][2] = {HID_KEYCODE_TO_ASCII};
 
 /* Blink pattern
  * - 250 ms  : device not mounted
  * - 1000 ms : device mounted
  * - 2500 ms : device is suspended
  */
-enum  {
+enum {
   BLINK_NOT_MOUNTED = 250,
   BLINK_MOUNTED = 1000,
   BLINK_SUSPENDED = 2500,
@@ -73,8 +73,7 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
 void led_blinking_task(void);
 
 /*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
   board_init();
 
   printf("TinyUSB Host HID <-> Device CDC Example\r\n");
@@ -87,8 +86,7 @@ int main(void)
     board_init_after_tusb();
   }
 
-  while (1)
-  {
+  while (1) {
     tud_task(); // tinyusb device task
     tuh_task(); // tinyusb host task
     led_blinking_task();
@@ -102,35 +100,30 @@ int main(void)
 //--------------------------------------------------------------------+
 
 // Invoked when device is mounted
-void tud_mount_cb(void)
-{
+void tud_mount_cb(void) {
   blink_interval_ms = BLINK_MOUNTED;
 }
 
 // Invoked when device is unmounted
-void tud_umount_cb(void)
-{
+void tud_umount_cb(void) {
   blink_interval_ms = BLINK_NOT_MOUNTED;
 }
 
 // Invoked when usb bus is suspended
 // remote_wakeup_en : if host allow us  to perform remote wakeup
 // Within 7ms, device must draw an average of current less than 2.5 mA from bus
-void tud_suspend_cb(bool remote_wakeup_en)
-{
+void tud_suspend_cb(bool remote_wakeup_en) {
   (void) remote_wakeup_en;
   blink_interval_ms = BLINK_SUSPENDED;
 }
 
 // Invoked when usb bus is resumed
-void tud_resume_cb(void)
-{
+void tud_resume_cb(void) {
   blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
 }
 
 // Invoked when CDC interface received data from host
-void tud_cdc_rx_cb(uint8_t itf)
-{
+void tud_cdc_rx_cb(uint8_t itf) {
   (void) itf;
 
   char buf[64];
@@ -149,38 +142,36 @@ void tud_cdc_rx_cb(uint8_t itf)
 // can be used to parse common/simple enough descriptor.
 // Note: if report descriptor length > CFG_TUH_ENUMERATION_BUFSIZE, it will be skipped
 // therefore report_desc = NULL, desc_len = 0
-void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len)
-{
-  (void)desc_report;
-  (void)desc_len;
+void tuh_hid_mount_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* desc_report, uint16_t desc_len) {
+  (void) desc_report;
+  (void) desc_len;
 
   // Interface protocol (hid_interface_protocol_enum_t)
-  const char* protocol_str[] = { "None", "Keyboard", "Mouse" };
+  const char* protocol_str[] = {"None", "Keyboard", "Mouse"};
   uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
 
   uint16_t vid, pid;
   tuh_vid_pid_get(dev_addr, &vid, &pid);
 
   char tempbuf[256];
-  int count = sprintf(tempbuf, "[%04x:%04x][%u] HID Interface%u, Protocol = %s\r\n", vid, pid, dev_addr, instance, protocol_str[itf_protocol]);
+  int count = sprintf(
+      tempbuf, "[%04x:%04x][%u] HID Interface%u, Protocol = %s\r\n", vid, pid, dev_addr, instance,
+      protocol_str[itf_protocol]);
 
   tud_cdc_write(tempbuf, (uint32_t) count);
   tud_cdc_write_flush();
 
   // Receive report from boot keyboard & mouse only
   // tuh_hid_report_received_cb() will be invoked when report is available
-  if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD || itf_protocol == HID_ITF_PROTOCOL_MOUSE)
-  {
-    if ( !tuh_hid_receive_report(dev_addr, instance) )
-    {
+  if (itf_protocol == HID_ITF_PROTOCOL_KEYBOARD || itf_protocol == HID_ITF_PROTOCOL_MOUSE) {
+    if (!tuh_hid_receive_report(dev_addr, instance)) {
       tud_cdc_write_str("Error: cannot request report\r\n");
     }
   }
 }
 
 // Invoked when device with hid interface is un-mounted
-void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
-{
+void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance) {
   char tempbuf[256];
   int count = sprintf(tempbuf, "[%u] HID Interface%u is unmounted\r\n", dev_addr, instance);
   tud_cdc_write(tempbuf, (uint32_t) count);
@@ -188,11 +179,9 @@ void tuh_hid_umount_cb(uint8_t dev_addr, uint8_t instance)
 }
 
 // look up new key in previous keys
-static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8_t keycode)
-{
-  for(uint8_t i=0; i<6; i++)
-  {
-    if (report->keycode[i] == keycode)  return true;
+static inline bool find_key_in_report(hid_keyboard_report_t const* report, uint8_t keycode) {
+  for (uint8_t i = 0; i < 6; i++) {
+    if (report->keycode[i] == keycode) return true;
   }
 
   return false;
@@ -200,22 +189,17 @@ static inline bool find_key_in_report(hid_keyboard_report_t const *report, uint8
 
 
 // convert hid keycode to ascii and print via usb device CDC (ignore non-printable)
-static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const *report)
-{
+static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const* report) {
   (void) dev_addr;
-  static hid_keyboard_report_t prev_report = { 0, 0, {0} }; // previous report to check key released
+  static hid_keyboard_report_t prev_report = {0, 0, {0}}; // previous report to check key released
   bool flush = false;
 
-  for(uint8_t i=0; i<6; i++)
-  {
+  for (uint8_t i = 0; i < 6; i++) {
     uint8_t keycode = report->keycode[i];
-    if ( keycode )
-    {
-      if ( find_key_in_report(&prev_report, keycode) )
-      {
+    if (keycode) {
+      if (find_key_in_report(&prev_report, keycode)) {
         // exist in previous report means the current key is holding
-      }else
-      {
+      } else {
         // not existed in previous report means the current key is pressed
 
         // remap the key code for Colemak layout
@@ -227,8 +211,7 @@ static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const *re
         bool const is_shift = report->modifier & (KEYBOARD_MODIFIER_LEFTSHIFT | KEYBOARD_MODIFIER_RIGHTSHIFT);
         uint8_t ch = keycode2ascii[keycode][is_shift ? 1 : 0];
 
-        if (ch)
-        {
+        if (ch) {
           if (ch == '\n') tud_cdc_write("\r", 1);
           tud_cdc_write(&ch, 1);
           flush = true;
@@ -244,13 +227,12 @@ static void process_kbd_report(uint8_t dev_addr, hid_keyboard_report_t const *re
 }
 
 // send mouse report to usb device CDC
-static void process_mouse_report(uint8_t dev_addr, hid_mouse_report_t const * report)
-{
+static void process_mouse_report(uint8_t dev_addr, hid_mouse_report_t const* report) {
   //------------- button state  -------------//
   //uint8_t button_changed_mask = report->buttons ^ prev_report.buttons;
-  char l = report->buttons & MOUSE_BUTTON_LEFT   ? 'L' : '-';
+  char l = report->buttons & MOUSE_BUTTON_LEFT ? 'L' : '-';
   char m = report->buttons & MOUSE_BUTTON_MIDDLE ? 'M' : '-';
-  char r = report->buttons & MOUSE_BUTTON_RIGHT  ? 'R' : '-';
+  char r = report->buttons & MOUSE_BUTTON_RIGHT ? 'R' : '-';
 
   char tempbuf[32];
   int count = sprintf(tempbuf, "[%u] %c%c%c %d %d %d\r\n", dev_addr, l, m, r, report->x, report->y, report->wheel);
@@ -260,27 +242,25 @@ static void process_mouse_report(uint8_t dev_addr, hid_mouse_report_t const * re
 }
 
 // Invoked when received report from device via interrupt endpoint
-void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
-{
+void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len) {
   (void) len;
   uint8_t const itf_protocol = tuh_hid_interface_protocol(dev_addr, instance);
 
-  switch(itf_protocol)
-  {
+  switch (itf_protocol) {
     case HID_ITF_PROTOCOL_KEYBOARD:
-      process_kbd_report(dev_addr, (hid_keyboard_report_t const*) report );
-    break;
+      process_kbd_report(dev_addr, (hid_keyboard_report_t const*) report);
+      break;
 
     case HID_ITF_PROTOCOL_MOUSE:
-      process_mouse_report(dev_addr, (hid_mouse_report_t const*) report );
-    break;
+      process_mouse_report(dev_addr, (hid_mouse_report_t const*) report);
+      break;
 
-    default: break;
+    default:
+      break;
   }
 
   // continue to request to receive report
-  if ( !tuh_hid_receive_report(dev_addr, instance) )
-  {
+  if (!tuh_hid_receive_report(dev_addr, instance)) {
     tud_cdc_write_str("Error: cannot request report\r\n");
   }
 }
@@ -288,13 +268,12 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t instance, uint8_t cons
 //--------------------------------------------------------------------+
 // Blinking Task
 //--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
+void led_blinking_task(void) {
   static uint32_t start_ms = 0;
   static bool led_state = false;
 
   // Blink every interval ms
-  if ( board_millis() - start_ms < blink_interval_ms) return; // not enough time
+  if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
   start_ms += blink_interval_ms;
 
   board_led_write(led_state);
diff --git a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h
index 8133ed418..2843e0b83 100644
--- a/examples/dual/host_hid_to_device_cdc/src/tusb_config.h
+++ b/examples/dual/host_hid_to_device_cdc/src/tusb_config.h
@@ -23,8 +23,8 @@
  *
  */
 
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
 
 #ifdef __cplusplus
  extern "C" {
@@ -144,4 +144,4 @@
  }
 #endif
 
-#endif /* _TUSB_CONFIG_H_ */
+#endif
diff --git a/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c b/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c
index 293620042..9d57737fb 100644
--- a/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c
+++ b/examples/dual/host_hid_to_device_cdc/src/usb_descriptors.c
@@ -42,44 +42,41 @@
 //--------------------------------------------------------------------+
 // Device Descriptors
 //--------------------------------------------------------------------+
-tusb_desc_device_t const desc_device =
-{
-  .bLength            = sizeof(tusb_desc_device_t),
-  .bDescriptorType    = TUSB_DESC_DEVICE,
-  .bcdUSB             = USB_BCD,
+tusb_desc_device_t const desc_device = {
+    .bLength            = sizeof(tusb_desc_device_t),
+    .bDescriptorType    = TUSB_DESC_DEVICE,
+    .bcdUSB             = USB_BCD,
 
-  // Use Interface Association Descriptor (IAD) for CDC
-  // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
-  .bDeviceClass       = TUSB_CLASS_MISC,
-  .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
-  .bDeviceProtocol    = MISC_PROTOCOL_IAD,
+    // Use Interface Association Descriptor (IAD) for CDC
+    // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
+    .bDeviceClass       = TUSB_CLASS_MISC,
+    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
+    .bDeviceProtocol    = MISC_PROTOCOL_IAD,
 
-  .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
+    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
 
-  .idVendor           = USB_VID,
-  .idProduct          = USB_PID,
-  .bcdDevice          = 0x0100,
+    .idVendor           = USB_VID,
+    .idProduct          = USB_PID,
+    .bcdDevice          = 0x0100,
 
-  .iManufacturer      = 0x01,
-  .iProduct           = 0x02,
-  .iSerialNumber      = 0x03,
+    .iManufacturer      = 0x01,
+    .iProduct           = 0x02,
+    .iSerialNumber      = 0x03,
 
-  .bNumConfigurations = 0x01
+    .bNumConfigurations = 0x01
 };
 
 // Invoked when received GET DEVICE DESCRIPTOR
 // Application return pointer to descriptor
-uint8_t const * tud_descriptor_device_cb(void)
-{
-  return (uint8_t const *) &desc_device;
+uint8_t const* tud_descriptor_device_cb(void) {
+  return (uint8_t const*) &desc_device;
 }
 
 //--------------------------------------------------------------------+
 // Configuration Descriptor
 //--------------------------------------------------------------------+
 
-enum
-{
+enum {
   ITF_NUM_CDC = 0,
   ITF_NUM_CDC_DATA,
   ITF_NUM_TOTAL
@@ -92,7 +89,7 @@ enum
   #define EPNUM_CDC_OUT     0x02
   #define EPNUM_CDC_IN      0x82
 
-#elif CFG_TUSB_MCU == OPT_MCU_SAMG  || CFG_TUSB_MCU ==  OPT_MCU_SAMX7X
+#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X
   // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT
   //    e.g EP1 OUT & EP1 IN cannot exist together
   #define EPNUM_CDC_NOTIF   0x81
@@ -109,7 +106,7 @@ enum
   #define EPNUM_CDC_IN      0x81
 
 #elif CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X
-  // FT9XX doesn't support a same endpoint number with different direction IN and OUT
+// FT9XX doesn't support a same endpoint number with different direction IN and OUT
   //    e.g EP1 OUT & EP1 IN cannot exist together
   #define EPNUM_CDC_NOTIF   0x81
   #define EPNUM_CDC_OUT     0x02
@@ -125,21 +122,19 @@ enum
 #define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
 
 // full speed configuration
-uint8_t const desc_fs_configuration[] =
-{
-  // Config number, interface count, string index, total length, attribute, power in mA
-  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+uint8_t const desc_fs_configuration[] = {
+    // Config number, interface count, string index, total length, attribute, power in mA
+    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
-  // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
-  TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
+    // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+    TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
 };
 
 #if TUD_OPT_HIGH_SPEED
 // Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
 
 // high speed configuration
-uint8_t const desc_hs_configuration[] =
-{
+uint8_t const desc_hs_configuration[] = {
   // Config number, interface count, string index, total length, attribute, power in mA
   TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
 
@@ -151,8 +146,7 @@ uint8_t const desc_hs_configuration[] =
 uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
 
 // device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
-tusb_desc_device_qualifier_t const desc_device_qualifier =
-{
+tusb_desc_device_qualifier_t const desc_device_qualifier = {
   .bLength            = sizeof(tusb_desc_device_qualifier_t),
   .bDescriptorType    = TUSB_DESC_DEVICE_QUALIFIER,
   .bcdUSB             = USB_BCD,
@@ -170,16 +164,14 @@ tusb_desc_device_qualifier_t const desc_device_qualifier =
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
 // device_qualifier descriptor describes information about a high-speed capable device that would
 // change if the device were operating at the other speed. If not highspeed capable stall this request.
-uint8_t const* tud_descriptor_device_qualifier_cb(void)
-{
+uint8_t const* tud_descriptor_device_qualifier_cb(void) {
   return (uint8_t const*) &desc_device_qualifier;
 }
 
 // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
 // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
-uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
-{
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
   (void) index; // for multiple configurations
 
   // if link speed is high return fullspeed config, and vice versa
@@ -199,8 +191,7 @@ uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index)
 // Invoked when received GET CONFIGURATION DESCRIPTOR
 // Application return pointer to descriptor
 // Descriptor contents must exist long enough for transfer to complete
-uint8_t const * tud_descriptor_configuration_cb(uint8_t index)
-{
+uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
   (void) index; // for multiple configurations
 
 #if TUD_OPT_HIGH_SPEED
@@ -224,24 +215,23 @@ enum {
 };
 
 // array of pointer to string descriptors
-char const *string_desc_arr[] =
-{
-  (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409)
-  "TinyUSB",                     // 1: Manufacturer
-  "TinyUSB Device",              // 2: Product
-  NULL,                          // 3: Serials will use unique ID if possible
-  "TinyUSB CDC",                 // 4: CDC Interface
+char const* string_desc_arr[] = {
+    (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409)
+    "TinyUSB",                     // 1: Manufacturer
+    "TinyUSB Device",              // 2: Product
+    NULL,                          // 3: Serials will use unique ID if possible
+    "TinyUSB CDC",                 // 4: CDC Interface
 };
 
 static uint16_t _desc_str[32 + 1];
 
 // Invoked when received GET STRING DESCRIPTOR request
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
-uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
   (void) langid;
   size_t chr_count;
 
-  switch ( index ) {
+  switch (index) {
     case STRID_LANGID:
       memcpy(&_desc_str[1], string_desc_arr[0], 2);
       chr_count = 1;
@@ -255,17 +245,17 @@ uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
       // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
       // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
 
-      if ( !(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0])) ) return NULL;
+      if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL;
 
-      const char *str = string_desc_arr[index];
+      const char* str = string_desc_arr[index];
 
       // Cap at max char
       chr_count = strlen(str);
       size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
-      if ( chr_count > max_count ) chr_count = max_count;
+      if (chr_count > max_count) chr_count = max_count;
 
       // Convert ASCII string into UTF-16
-      for ( size_t i = 0; i < chr_count; i++ ) {
+      for (size_t i = 0; i < chr_count; i++) {
         _desc_str[1 + i] = str[i];
       }
       break;
diff --git a/examples/dual/host_info_to_device_cdc/CMakeLists.txt b/examples/dual/host_info_to_device_cdc/CMakeLists.txt
new file mode 100644
index 000000000..a6557c2d0
--- /dev/null
+++ b/examples/dual/host_info_to_device_cdc/CMakeLists.txt
@@ -0,0 +1,40 @@
+cmake_minimum_required(VERSION 3.17)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+# gets PROJECT name for the example (e.g. -)
+family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+
+project(${PROJECT} C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+
+add_executable(${PROJECT})
+
+# Example source
+target_sources(${PROJECT} PUBLIC
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+  ${CMAKE_CURRENT_SOURCE_DIR}/src/usb_descriptors.c
+  )
+
+# Example include
+target_include_directories(${PROJECT} PUBLIC
+  ${CMAKE_CURRENT_SOURCE_DIR}/src
+  )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_dual_usb_example(${PROJECT} noos)
+
+# due to warnings from Pico-PIO-USB
+target_compile_options(${PROJECT} PUBLIC
+  -Wno-error=shadow
+  -Wno-error=cast-align
+  -Wno-error=cast-qual
+  -Wno-error=redundant-decls
+  -Wno-error=sign-conversion
+  -Wno-error=conversion
+  -Wno-error=sign-compare
+  -Wno-error=unused-function
+  )
diff --git a/examples/dual/host_info_to_device_cdc/Makefile b/examples/dual/host_info_to_device_cdc/Makefile
new file mode 100644
index 000000000..0ede79c17
--- /dev/null
+++ b/examples/dual/host_info_to_device_cdc/Makefile
@@ -0,0 +1,17 @@
+include ../../build_system/make/make.mk
+
+INC += \
+	src \
+	$(TOP)/hw \
+
+# Example source
+EXAMPLE_SOURCE += $(wildcard src/*.c)
+SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+
+CFLAGS += -Wno-error=cast-align -Wno-error=null-dereference
+
+SRC_C += \
+	src/host/hub.c \
+	src/host/usbh.c
+
+include ../../build_system/make/rules.mk
diff --git a/examples/dual/host_info_to_device_cdc/only.txt b/examples/dual/host_info_to_device_cdc/only.txt
new file mode 100644
index 000000000..cfc87eb4e
--- /dev/null
+++ b/examples/dual/host_info_to_device_cdc/only.txt
@@ -0,0 +1,6 @@
+board:mimxrt1060_evk
+board:mimxrt1064_evk
+board:mcb1800
+mcu:RP2040
+mcu:ra6m5
+mcu:MAX3421
diff --git a/examples/dual/host_info_to_device_cdc/src/main.c b/examples/dual/host_info_to_device_cdc/src/main.c
new file mode 100644
index 000000000..5be531098
--- /dev/null
+++ b/examples/dual/host_info_to_device_cdc/src/main.c
@@ -0,0 +1,285 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+
+/* Host example will get device descriptors of attached devices and print it out via device cdc as follows:
+ *    Device 1: ID 046d:c52f
+      Device Descriptor:
+        bLength             18
+        bDescriptorType     1
+        bcdUSB              0200
+        bDeviceClass        0
+        bDeviceSubClass     0
+        bDeviceProtocol     0
+        bMaxPacketSize0     8
+        idVendor            0x046d
+        idProduct           0xc52f
+        bcdDevice           2200
+        iManufacturer       1     Logitech
+        iProduct            2     USB Receiver
+        iSerialNumber       0
+        bNumConfigurations  1
+ *
+ */
+
+#include 
+#include 
+#include 
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+// Language ID: English
+#define LANGUAGE_ID 0x0409
+
+/* Blink pattern
+ * - 250 ms  : device not mounted
+ * - 1000 ms : device mounted
+ * - 2500 ms : device is suspended
+ */
+enum {
+  BLINK_NOT_MOUNTED = 250,
+  BLINK_MOUNTED = 1000,
+  BLINK_SUSPENDED = 2500,
+};
+
+static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
+
+static bool is_print[CFG_TUH_DEVICE_MAX+1] = { 0 };
+
+static void print_utf16(uint16_t *temp_buf, size_t buf_len);
+void led_blinking_task(void);
+void cdc_task(void);
+
+/*------------- MAIN -------------*/
+int main(void) {
+  board_init();
+
+  printf("TinyUSB Host Information -> Device CDC Example\r\n");
+
+  // init device and host stack on configured roothub port
+  tud_init(BOARD_TUD_RHPORT);
+  tuh_init(BOARD_TUH_RHPORT);
+
+  if (board_init_after_tusb) {
+    board_init_after_tusb();
+  }
+
+  while (1) {
+    tud_task(); // tinyusb device task
+    tuh_task(); // tinyusb host task
+    cdc_task();
+    led_blinking_task();
+  }
+
+  return 0;
+}
+
+//--------------------------------------------------------------------+
+// Device CDC
+//--------------------------------------------------------------------+
+
+// Invoked when device is mounted
+void tud_mount_cb(void) {
+  blink_interval_ms = BLINK_MOUNTED;
+}
+
+// Invoked when device is unmounted
+void tud_umount_cb(void) {
+  blink_interval_ms = BLINK_NOT_MOUNTED;
+}
+
+// Invoked when usb bus is suspended
+// remote_wakeup_en : if host allow us  to perform remote wakeup
+// Within 7ms, device must draw an average of current less than 2.5 mA from bus
+void tud_suspend_cb(bool remote_wakeup_en) {
+  (void) remote_wakeup_en;
+  blink_interval_ms = BLINK_SUSPENDED;
+}
+
+// Invoked when usb bus is resumed
+void tud_resume_cb(void) {
+  blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
+}
+
+#if 1
+#define cdc_printf(...)                          \
+  do {                                           \
+    char _tempbuf[256];                          \
+    int count = sprintf(_tempbuf, __VA_ARGS__);  \
+    tud_cdc_write(_tempbuf, (uint32_t) count);   \
+    tud_cdc_write_flush();                       \
+    tud_task();                                  \
+  } while(0)
+#endif
+
+//#define cdc_printf printf
+
+void print_device_info(uint8_t daddr) {
+  tusb_desc_device_t desc_device;
+  uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc_device, 18);
+  if (XFER_RESULT_SUCCESS != xfer_result) {
+    tud_cdc_write_str("Failed to get device descriptor\r\n");
+    return;
+  }
+
+  cdc_printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct);
+  cdc_printf("Device Descriptor:\r\n");
+  cdc_printf("  bLength             %u\r\n"     , desc_device.bLength);
+  cdc_printf("  bDescriptorType     %u\r\n"     , desc_device.bDescriptorType);
+  cdc_printf("  bcdUSB              %04x\r\n"   , desc_device.bcdUSB);
+  cdc_printf("  bDeviceClass        %u\r\n"     , desc_device.bDeviceClass);
+  cdc_printf("  bDeviceSubClass     %u\r\n"     , desc_device.bDeviceSubClass);
+  cdc_printf("  bDeviceProtocol     %u\r\n"     , desc_device.bDeviceProtocol);
+  cdc_printf("  bMaxPacketSize0     %u\r\n"     , desc_device.bMaxPacketSize0);
+  cdc_printf("  idVendor            0x%04x\r\n" , desc_device.idVendor);
+  cdc_printf("  idProduct           0x%04x\r\n" , desc_device.idProduct);
+  cdc_printf("  bcdDevice           %04x\r\n"   , desc_device.bcdDevice);
+
+  // Get String descriptor using Sync API
+  uint16_t buf[128];
+
+  cdc_printf("  iManufacturer       %u     "     , desc_device.iManufacturer);
+  xfer_result = tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf));
+  if (XFER_RESULT_SUCCESS == xfer_result ) {
+    print_utf16(buf, TU_ARRAY_SIZE(buf));
+  }
+  tud_cdc_write_str("\r\n");
+
+  cdc_printf("  iProduct            %u     "     , desc_device.iProduct);
+  xfer_result = tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf));
+  if (XFER_RESULT_SUCCESS == xfer_result) {
+    print_utf16(buf, TU_ARRAY_SIZE(buf));
+  }
+  tud_cdc_write_str("\r\n");
+
+  cdc_printf("  iSerialNumber       %u     "     , desc_device.iSerialNumber);
+  xfer_result = tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf));
+  if (XFER_RESULT_SUCCESS == xfer_result) {
+    print_utf16(buf, TU_ARRAY_SIZE(buf));
+  }
+  tud_cdc_write_str("\r\n");
+
+  cdc_printf("  bNumConfigurations  %u\r\n"     , desc_device.bNumConfigurations);
+}
+
+void cdc_task(void) {
+  if (tud_cdc_connected()) {
+    for (uint8_t daddr = 1; daddr <= CFG_TUH_DEVICE_MAX; daddr++) {
+      if (tuh_mounted(daddr)) {
+        if (is_print[daddr]) {
+          is_print[daddr] = false;
+          print_device_info(daddr);
+          tud_cdc_write_flush();
+        }
+      }
+    }
+  }
+}
+
+//--------------------------------------------------------------------+
+// Host Get device information
+//--------------------------------------------------------------------+
+void tuh_mount_cb(uint8_t daddr) {
+  printf("mounted device %u\r\n", daddr);
+  is_print[daddr] = true;
+}
+
+void tuh_umount_cb(uint8_t daddr) {
+  printf("unmounted device %u\r\n", daddr);
+  is_print[daddr] = false;
+}
+
+//--------------------------------------------------------------------+
+// Blinking Task
+//--------------------------------------------------------------------+
+void led_blinking_task(void) {
+  static uint32_t start_ms = 0;
+  static bool led_state = false;
+
+  // Blink every interval ms
+  if (board_millis() - start_ms < blink_interval_ms) return; // not enough time
+  start_ms += blink_interval_ms;
+
+  board_led_write(led_state);
+  led_state = 1 - led_state; // toggle
+}
+
+//--------------------------------------------------------------------+
+// String Descriptor Helper
+//--------------------------------------------------------------------+
+
+static void _convert_utf16le_to_utf8(const uint16_t *utf16, size_t utf16_len, uint8_t *utf8, size_t utf8_len) {
+  // TODO: Check for runover.
+  (void)utf8_len;
+  // Get the UTF-16 length out of the data itself.
+
+  for (size_t i = 0; i < utf16_len; i++) {
+    uint16_t chr = utf16[i];
+    if (chr < 0x80) {
+      *utf8++ = chr & 0xffu;
+    } else if (chr < 0x800) {
+      *utf8++ = (uint8_t)(0xC0 | (chr >> 6 & 0x1F));
+      *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F));
+    } else {
+      // TODO: Verify surrogate.
+      *utf8++ = (uint8_t)(0xE0 | (chr >> 12 & 0x0F));
+      *utf8++ = (uint8_t)(0x80 | (chr >> 6 & 0x3F));
+      *utf8++ = (uint8_t)(0x80 | (chr >> 0 & 0x3F));
+    }
+    // TODO: Handle UTF-16 code points that take two entries.
+  }
+}
+
+// Count how many bytes a utf-16-le encoded string will take in utf-8.
+static int _count_utf8_bytes(const uint16_t *buf, size_t len) {
+  size_t total_bytes = 0;
+  for (size_t i = 0; i < len; i++) {
+    uint16_t chr = buf[i];
+    if (chr < 0x80) {
+      total_bytes += 1;
+    } else if (chr < 0x800) {
+      total_bytes += 2;
+    } else {
+      total_bytes += 3;
+    }
+    // TODO: Handle UTF-16 code points that take two entries.
+  }
+  return (int) total_bytes;
+}
+
+static void print_utf16(uint16_t *temp_buf, size_t buf_len) {
+  if ((temp_buf[0] & 0xff) == 0) return;  // empty
+  size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
+  size_t utf8_len = (size_t) _count_utf8_bytes(temp_buf + 1, utf16_len);
+  _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len);
+  ((uint8_t*) temp_buf)[utf8_len] = '\0';
+
+  tud_cdc_write(temp_buf, utf8_len);
+  tud_cdc_write_flush();
+  tud_task();
+}
diff --git a/examples/dual/host_info_to_device_cdc/src/tusb_config.h b/examples/dual/host_info_to_device_cdc/src/tusb_config.h
new file mode 100644
index 000000000..bb47fbf4a
--- /dev/null
+++ b/examples/dual/host_info_to_device_cdc/src/tusb_config.h
@@ -0,0 +1,143 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------+
+// Board Specific Configuration
+//--------------------------------------------------------------------+
+
+// RHPort number used for device can be defined by board.mk, default to port 0
+#ifndef BOARD_TUD_RHPORT
+#define BOARD_TUD_RHPORT      0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUD_MAX_SPEED
+#define BOARD_TUD_MAX_SPEED   OPT_MODE_DEFAULT_SPEED
+#endif
+
+// RHPort number used for host can be defined by board.mk, default to port 1
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT      1
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED   OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS           OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG        0
+#endif
+
+// Enable Device stack, Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUD_ENABLED       1
+#define CFG_TUD_MAX_SPEED     BOARD_TUD_MAX_SPEED
+
+// Enable Host stack, Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_ENABLED       1
+#define CFG_TUH_MAX_SPEED     BOARD_TUH_MAX_SPEED
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+// Use pico-pio-usb as host controller for raspberry rp2040
+#define CFG_TUH_RPI_PIO_USB   1
+#endif
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUD_MEM_SECTION
+#define CFG_TUD_MEM_SECTION
+#endif
+
+#ifndef CFG_TUD_MEM_ALIGN
+#define CFG_TUD_MEM_ALIGN        __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+#define CFG_TUD_ENDPOINT0_SIZE    64
+#endif
+
+//------------- CLASS -------------//
+#define CFG_TUD_CDC              1
+
+// CDC FIFO size of TX and RX
+#define CFG_TUD_CDC_RX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
+#define CFG_TUD_CDC_TX_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+// CDC Endpoint transfer buffer size, more is faster
+#define CFG_TUD_CDC_EP_BUFSIZE   (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
+//--------------------------------------------------------------------
+// HOST CONFIGURATION
+//--------------------------------------------------------------------
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+#ifndef CFG_TUH_MEM_SECTION
+#define CFG_TUH_MEM_SECTION
+#endif
+
+#ifndef CFG_TUH_MEM_ALIGN
+#define CFG_TUH_MEM_ALIGN           __attribute__ ((aligned(4)))
+#endif
+
+#define CFG_TUH_HUB                 1
+// max device support (excluding hub device)
+#define CFG_TUH_DEVICE_MAX          (CFG_TUH_HUB ? 4 : 1) // hub typically has 4 ports
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c b/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c
new file mode 100644
index 000000000..9d57737fb
--- /dev/null
+++ b/examples/dual/host_info_to_device_cdc/src/usb_descriptors.c
@@ -0,0 +1,268 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 "bsp/board_api.h"
+#include "tusb.h"
+
+/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
+ * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
+ *
+ * Auto ProductID layout's Bitmap:
+ *   [MSB]         HID | MSC | CDC          [LSB]
+ */
+#define _PID_MAP(itf, n)  ( (CFG_TUD_##itf) << (n) )
+#define USB_PID           (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \
+                           _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) )
+
+#define USB_VID   0xCafe
+#define USB_BCD   0x0200
+
+//--------------------------------------------------------------------+
+// Device Descriptors
+//--------------------------------------------------------------------+
+tusb_desc_device_t const desc_device = {
+    .bLength            = sizeof(tusb_desc_device_t),
+    .bDescriptorType    = TUSB_DESC_DEVICE,
+    .bcdUSB             = USB_BCD,
+
+    // Use Interface Association Descriptor (IAD) for CDC
+    // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
+    .bDeviceClass       = TUSB_CLASS_MISC,
+    .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
+    .bDeviceProtocol    = MISC_PROTOCOL_IAD,
+
+    .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
+
+    .idVendor           = USB_VID,
+    .idProduct          = USB_PID,
+    .bcdDevice          = 0x0100,
+
+    .iManufacturer      = 0x01,
+    .iProduct           = 0x02,
+    .iSerialNumber      = 0x03,
+
+    .bNumConfigurations = 0x01
+};
+
+// Invoked when received GET DEVICE DESCRIPTOR
+// Application return pointer to descriptor
+uint8_t const* tud_descriptor_device_cb(void) {
+  return (uint8_t const*) &desc_device;
+}
+
+//--------------------------------------------------------------------+
+// Configuration Descriptor
+//--------------------------------------------------------------------+
+
+enum {
+  ITF_NUM_CDC = 0,
+  ITF_NUM_CDC_DATA,
+  ITF_NUM_TOTAL
+};
+
+#if CFG_TUSB_MCU == OPT_MCU_LPC175X_6X || CFG_TUSB_MCU == OPT_MCU_LPC177X_8X || CFG_TUSB_MCU == OPT_MCU_LPC40XX
+  // LPC 17xx and 40xx endpoint type (bulk/interrupt/iso) are fixed by its number
+  // 0 control, 1 In, 2 Bulk, 3 Iso, 4 In, 5 Bulk etc ...
+  #define EPNUM_CDC_NOTIF   0x81
+  #define EPNUM_CDC_OUT     0x02
+  #define EPNUM_CDC_IN      0x82
+
+#elif CFG_TUSB_MCU == OPT_MCU_SAMG || CFG_TUSB_MCU == OPT_MCU_SAMX7X
+  // SAMG & SAME70 don't support a same endpoint number with different direction IN and OUT
+  //    e.g EP1 OUT & EP1 IN cannot exist together
+  #define EPNUM_CDC_NOTIF   0x81
+  #define EPNUM_CDC_OUT     0x02
+  #define EPNUM_CDC_IN      0x83
+
+#elif CFG_TUSB_MCU == OPT_MCU_CXD56
+  // CXD56 doesn't support a same endpoint number with different direction IN and OUT
+  //    e.g EP1 OUT & EP1 IN cannot exist together
+  // CXD56 USB driver has fixed endpoint type (bulk/interrupt/iso) and direction (IN/OUT) by its number
+  // 0 control (IN/OUT), 1 Bulk (IN), 2 Bulk (OUT), 3 In (IN), 4 Bulk (IN), 5 Bulk (OUT), 6 In (IN)
+  #define EPNUM_CDC_NOTIF   0x83
+  #define EPNUM_CDC_OUT     0x02
+  #define EPNUM_CDC_IN      0x81
+
+#elif CFG_TUSB_MCU == OPT_MCU_FT90X || CFG_TUSB_MCU == OPT_MCU_FT93X
+// FT9XX doesn't support a same endpoint number with different direction IN and OUT
+  //    e.g EP1 OUT & EP1 IN cannot exist together
+  #define EPNUM_CDC_NOTIF   0x81
+  #define EPNUM_CDC_OUT     0x02
+  #define EPNUM_CDC_IN      0x83
+
+#else
+  #define EPNUM_CDC_NOTIF   0x81
+  #define EPNUM_CDC_OUT     0x02
+  #define EPNUM_CDC_IN      0x82
+
+#endif
+
+#define CONFIG_TOTAL_LEN    (TUD_CONFIG_DESC_LEN + TUD_CDC_DESC_LEN)
+
+// full speed configuration
+uint8_t const desc_fs_configuration[] = {
+    // Config number, interface count, string index, total length, attribute, power in mA
+    TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+    // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+    TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 64),
+};
+
+#if TUD_OPT_HIGH_SPEED
+// Per USB specs: high speed capable device must report device_qualifier and other_speed_configuration
+
+// high speed configuration
+uint8_t const desc_hs_configuration[] = {
+  // Config number, interface count, string index, total length, attribute, power in mA
+  TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100),
+
+  // Interface number, string index, EP notification address and size, EP data address (out, in) and size.
+  TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, 4, EPNUM_CDC_NOTIF, 8, EPNUM_CDC_OUT, EPNUM_CDC_IN, 512),
+};
+
+// other speed configuration
+uint8_t desc_other_speed_config[CONFIG_TOTAL_LEN];
+
+// device qualifier is mostly similar to device descriptor since we don't change configuration based on speed
+tusb_desc_device_qualifier_t const desc_device_qualifier = {
+  .bLength            = sizeof(tusb_desc_device_qualifier_t),
+  .bDescriptorType    = TUSB_DESC_DEVICE_QUALIFIER,
+  .bcdUSB             = USB_BCD,
+
+  .bDeviceClass       = TUSB_CLASS_MISC,
+  .bDeviceSubClass    = MISC_SUBCLASS_COMMON,
+  .bDeviceProtocol    = MISC_PROTOCOL_IAD,
+
+  .bMaxPacketSize0    = CFG_TUD_ENDPOINT0_SIZE,
+  .bNumConfigurations = 0x01,
+  .bReserved          = 0x00
+};
+
+// Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
+// device_qualifier descriptor describes information about a high-speed capable device that would
+// change if the device were operating at the other speed. If not highspeed capable stall this request.
+uint8_t const* tud_descriptor_device_qualifier_cb(void) {
+  return (uint8_t const*) &desc_device_qualifier;
+}
+
+// Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+// Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+  (void) index; // for multiple configurations
+
+  // if link speed is high return fullspeed config, and vice versa
+  // Note: the descriptor type is OHER_SPEED_CONFIG instead of CONFIG
+  memcpy(desc_other_speed_config,
+         (tud_speed_get() == TUSB_SPEED_HIGH) ? desc_fs_configuration : desc_hs_configuration,
+         CONFIG_TOTAL_LEN);
+
+  desc_other_speed_config[1] = TUSB_DESC_OTHER_SPEED_CONFIG;
+
+  return desc_other_speed_config;
+}
+
+#endif // highspeed
+
+
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const* tud_descriptor_configuration_cb(uint8_t index) {
+  (void) index; // for multiple configurations
+
+#if TUD_OPT_HIGH_SPEED
+  // Although we are highspeed, host may be fullspeed.
+  return (tud_speed_get() == TUSB_SPEED_HIGH) ?  desc_hs_configuration : desc_fs_configuration;
+#else
+  return desc_fs_configuration;
+#endif
+}
+
+//--------------------------------------------------------------------+
+// String Descriptors
+//--------------------------------------------------------------------+
+
+// String Descriptor Index
+enum {
+  STRID_LANGID = 0,
+  STRID_MANUFACTURER,
+  STRID_PRODUCT,
+  STRID_SERIAL,
+};
+
+// array of pointer to string descriptors
+char const* string_desc_arr[] = {
+    (const char[]) {0x09, 0x04}, // 0: is supported language is English (0x0409)
+    "TinyUSB",                     // 1: Manufacturer
+    "TinyUSB Device",              // 2: Product
+    NULL,                          // 3: Serials will use unique ID if possible
+    "TinyUSB CDC",                 // 4: CDC Interface
+};
+
+static uint16_t _desc_str[32 + 1];
+
+// Invoked when received GET STRING DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) {
+  (void) langid;
+  size_t chr_count;
+
+  switch (index) {
+    case STRID_LANGID:
+      memcpy(&_desc_str[1], string_desc_arr[0], 2);
+      chr_count = 1;
+      break;
+
+    case STRID_SERIAL:
+      chr_count = board_usb_get_serial(_desc_str + 1, 32);
+      break;
+
+    default:
+      // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors.
+      // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors
+
+      if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) return NULL;
+
+      const char* str = string_desc_arr[index];
+
+      // Cap at max char
+      chr_count = strlen(str);
+      size_t const max_count = sizeof(_desc_str) / sizeof(_desc_str[0]) - 1; // -1 for string type
+      if (chr_count > max_count) chr_count = max_count;
+
+      // Convert ASCII string into UTF-16
+      for (size_t i = 0; i < chr_count; i++) {
+        _desc_str[1 + i] = str[i];
+      }
+      break;
+  }
+
+  // first byte is length (including header), second byte is string type
+  _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8) | (2 * chr_count + 2));
+
+  return _desc_str;
+}
diff --git a/examples/host/CMakeLists.txt b/examples/host/CMakeLists.txt
index e6a2ece14..793f6ab08 100644
--- a/examples/host/CMakeLists.txt
+++ b/examples/host/CMakeLists.txt
@@ -9,5 +9,6 @@ family_initialize_project(tinyusb_host_examples ${CMAKE_CURRENT_LIST_DIR})
 family_add_subdirectory(bare_api)
 family_add_subdirectory(cdc_msc_hid)
 family_add_subdirectory(cdc_msc_hid_freertos)
+family_add_subdirectory(device_info)
 family_add_subdirectory(hid_controller)
 family_add_subdirectory(msc_file_explorer)
diff --git a/examples/host/bare_api/src/main.c b/examples/host/bare_api/src/main.c
index 14725996d..ae574c078 100644
--- a/examples/host/bare_api/src/main.c
+++ b/examples/host/bare_api/src/main.c
@@ -23,10 +23,6 @@
  *
  */
 
-/* This example current worked and tested with following controller
- * - Sony DualShock 4 [CUH-ZCT2x] VID = 0x054c, PID = 0x09cc
- */
-
 
 #include 
 #include 
@@ -34,6 +30,7 @@
 
 #include "bsp/board_api.h"
 #include "tusb.h"
+#include "class/hid/hid.h"
 
 // English
 #define LANGUAGE_ID 0x0409
@@ -420,5 +417,5 @@ static void print_utf16(uint16_t *temp_buf, size_t buf_len) {
     _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t *) temp_buf, sizeof(uint16_t) * buf_len);
     ((uint8_t*) temp_buf)[utf8_len] = '\0';
 
-    printf((char*)temp_buf);
+    printf("%s", (char*)temp_buf);
 }
diff --git a/examples/host/bare_api/src/tusb_config.h b/examples/host/bare_api/src/tusb_config.h
index 432446e94..fab233be0 100644
--- a/examples/host/bare_api/src/tusb_config.h
+++ b/examples/host/bare_api/src/tusb_config.h
@@ -23,8 +23,8 @@
  *
  */
 
-#ifndef _TUSB_CONFIG_H_
-#define _TUSB_CONFIG_H_
+#ifndef TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
 
 #ifdef __cplusplus
  extern "C" {
@@ -118,4 +118,4 @@
  }
 #endif
 
-#endif /* _TUSB_CONFIG_H_ */
+#endif
diff --git a/examples/host/cdc_msc_hid/src/cdc_app.c b/examples/host/cdc_msc_hid/src/cdc_app.c
index e275e7943..4a13f8b27 100644
--- a/examples/host/cdc_msc_hid/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid/src/cdc_app.c
@@ -72,7 +72,7 @@ void tuh_cdc_rx_cb(uint8_t idx) {
   uint32_t count = tuh_cdc_read(idx, buf, bufsize);
   buf[count] = 0;
 
-  printf((char*) buf);
+  printf("%s", (char*) buf);
 }
 
 // Invoked when a device with CDC interface is mounted
@@ -89,7 +89,7 @@ void tuh_cdc_mount_cb(uint8_t idx) {
   // while eneumerating new cdc device
   cdc_line_coding_t line_coding = {0};
   if (tuh_cdc_get_local_line_coding(idx, &line_coding)) {
-    printf("  Baudrate: %lu, Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
+    printf("  Baudrate: %" PRIu32 ", Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
     printf("  Parity  : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits);
   }
 #else
diff --git a/examples/host/cdc_msc_hid/src/hid_app.c b/examples/host/cdc_msc_hid/src/hid_app.c
index f0d42a08f..6bb3a2072 100644
--- a/examples/host/cdc_msc_hid/src/hid_app.c
+++ b/examples/host/cdc_msc_hid/src/hid_app.c
@@ -235,6 +235,7 @@ static void process_mouse_report(hid_mouse_report_t const * report)
 static void process_generic_report(uint8_t dev_addr, uint8_t instance, uint8_t const* report, uint16_t len)
 {
   (void) dev_addr;
+  (void) len;
 
   uint8_t const rpt_count = hid_info[instance].report_count;
   tuh_hid_report_info_t* rpt_info_arr = hid_info[instance].report_info;
diff --git a/examples/host/cdc_msc_hid/src/msc_app.c b/examples/host/cdc_msc_hid/src/msc_app.c
index e9c9676b8..1d7e18e6e 100644
--- a/examples/host/cdc_msc_hid/src/msc_app.c
+++ b/examples/host/cdc_msc_hid/src/msc_app.c
@@ -48,8 +48,8 @@ bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_da
   uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
   uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
 
-  printf("Disk Size: %lu MB\r\n", block_count / ((1024*1024)/block_size));
-  printf("Block Count = %lu, Block Size: %lu\r\n", block_count, block_size);
+  printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024*1024)/block_size));
+  printf("Block Count = %" PRIu32 ", Block Size: %" PRIu32 "\r\n", block_count, block_size);
 
   return true;
 }
diff --git a/examples/host/cdc_msc_hid_freertos/Makefile b/examples/host/cdc_msc_hid_freertos/Makefile
index 5351a6248..bf4725f47 100644
--- a/examples/host/cdc_msc_hid_freertos/Makefile
+++ b/examples/host/cdc_msc_hid_freertos/Makefile
@@ -1,7 +1,7 @@
 include ../../build_system/make/make.mk
 
 FREERTOS_SRC = lib/FreeRTOS-Kernel
-FREERTOS_PORTABLE_PATH= $(FREERTOS_SRC)/portable/$(if $(USE_IAR),IAR,GCC)
+FREERTOS_PORTABLE_PATH= $(FREERTOS_SRC)/portable/$(if $(findstring iar,$(TOOLCHAIN)),IAR,GCC)
 
 INC += \
 	src \
diff --git a/examples/host/cdc_msc_hid_freertos/only.txt b/examples/host/cdc_msc_hid_freertos/only.txt
index 81d993ffa..1e0e60075 100644
--- a/examples/host/cdc_msc_hid_freertos/only.txt
+++ b/examples/host/cdc_msc_hid_freertos/only.txt
@@ -8,5 +8,4 @@ mcu:MIMXRT10XX
 mcu:MIMXRT11XX
 mcu:MSP432E4
 mcu:RX65X
-mcu:RAXXX
 mcu:MAX3421
diff --git a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
index 6ff513be1..f3495ab28 100644
--- a/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/cdc_app.c
@@ -27,7 +27,7 @@
 #include "tusb.h"
 #include "bsp/board_api.h"
 
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 // ESP-IDF need "freertos/" prefix in include path.
 // CFG_TUSB_OS_INC_PATH should be defined accordingly.
   #include "freertos/FreeRTOS.h"
@@ -117,7 +117,7 @@ void tuh_cdc_rx_cb(uint8_t idx) {
   uint32_t count = tuh_cdc_read(idx, buf, bufsize);
   buf[count] = 0;
 
-  printf((char *) buf);
+  printf("%s", (char *) buf);
 }
 
 void tuh_cdc_mount_cb(uint8_t idx) {
@@ -131,7 +131,7 @@ void tuh_cdc_mount_cb(uint8_t idx) {
   // otherwise you need to call tuh_cdc_set_line_coding() first
   cdc_line_coding_t line_coding = { 0 };
   if (tuh_cdc_get_local_line_coding(idx, &line_coding)) {
-    printf("  Baudrate: %lu, Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
+    printf("  Baudrate: %" PRIu32 ", Stop Bits : %u\r\n", line_coding.bit_rate, line_coding.stop_bits);
     printf("  Parity  : %u, Data Width: %u\r\n", line_coding.parity, line_coding.data_bits);
   }
 #endif
diff --git a/examples/host/cdc_msc_hid_freertos/src/main.c b/examples/host/cdc_msc_hid_freertos/src/main.c
index 069cbdc90..fa799aede 100644
--- a/examples/host/cdc_msc_hid_freertos/src/main.c
+++ b/examples/host/cdc_msc_hid_freertos/src/main.c
@@ -30,7 +30,7 @@
 #include "bsp/board_api.h"
 #include "tusb.h"
 
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
   // ESP-IDF need "freertos/" prefix in include path.
   // CFG_TUSB_OS_INC_PATH should be defined accordingly.
   #include "freertos/FreeRTOS.h"
@@ -107,14 +107,14 @@ int main(void) {
   xTimerStart(blinky_tm, 0);
 
   // skip starting scheduler (and return) for ESP32-S2 or ESP32-S3
-#if !TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if !TUP_MCU_ESPRESSIF
   vTaskStartScheduler();
 #endif
 
   return 0;
 }
 
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 void app_main(void) {
   main();
 }
@@ -126,7 +126,10 @@ static void usb_host_task(void *param) {
   (void) param;
 
   // init host stack on configured roothub port
-  tuh_init(BOARD_TUH_RHPORT);
+  if (!tuh_init(BOARD_TUH_RHPORT)) {
+    printf("Failed to init USB Host Stack\r\n");
+    vTaskSuspend(NULL);
+  }
 
   if (board_init_after_tusb) {
     board_init_after_tusb();
diff --git a/examples/host/cdc_msc_hid_freertos/src/msc_app.c b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
index ee02ba917..9ffd5d965 100644
--- a/examples/host/cdc_msc_hid_freertos/src/msc_app.c
+++ b/examples/host/cdc_msc_hid_freertos/src/msc_app.c
@@ -47,8 +47,8 @@ bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const *cb_dat
   uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
   uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
 
-  printf("Disk Size: %lu MB\r\n", block_count / ((1024 * 1024) / block_size));
-  printf("Block Count = %lu, Block Size: %lu\r\n", block_count, block_size);
+  printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024 * 1024) / block_size));
+  printf("Block Count = %" PRIu32 ", Block Size: %" PRIu32 "\r\n", block_count, block_size);
 
   return true;
 }
diff --git a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
index 35de5ee50..0aab2cd2f 100644
--- a/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
+++ b/examples/host/cdc_msc_hid_freertos/src/tusb_config.h
@@ -44,7 +44,7 @@
 #endif
 
 // Espressif IDF requires "freertos/" prefix in include path
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
 #define CFG_TUSB_OS_INC_PATH  freertos/
 #endif
 
diff --git a/examples/host/device_info/CMakeLists.txt b/examples/host/device_info/CMakeLists.txt
new file mode 100644
index 000000000..76182d6fa
--- /dev/null
+++ b/examples/host/device_info/CMakeLists.txt
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 3.17)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/../../../hw/bsp/family_support.cmake)
+
+# gets PROJECT name for the example
+family_get_project_name(PROJECT ${CMAKE_CURRENT_LIST_DIR})
+
+project(${PROJECT} C CXX ASM)
+
+# Checks this example is valid for the family and initializes the project
+family_initialize_project(${PROJECT} ${CMAKE_CURRENT_LIST_DIR})
+
+# Espressif has its own cmake build system
+if(FAMILY STREQUAL "espressif")
+  return()
+endif()
+
+add_executable(${PROJECT})
+
+# Example source
+target_sources(${PROJECT} PUBLIC
+        ${CMAKE_CURRENT_SOURCE_DIR}/src/main.c
+        )
+
+# Example include
+target_include_directories(${PROJECT} PUBLIC
+        ${CMAKE_CURRENT_SOURCE_DIR}/src
+        )
+
+# Configure compilation flags and libraries for the example without RTOS.
+# See the corresponding function in hw/bsp/FAMILY/family.cmake for details.
+family_configure_host_example(${PROJECT} noos)
diff --git a/examples/host/device_info/Makefile b/examples/host/device_info/Makefile
new file mode 100644
index 000000000..0235e08c3
--- /dev/null
+++ b/examples/host/device_info/Makefile
@@ -0,0 +1,13 @@
+include ../../build_system/make/make.mk
+
+INC += \
+	src \
+	$(TOP)/hw \
+
+# Example source
+EXAMPLE_SOURCE += \
+	src/main.c
+
+SRC_C += $(addprefix $(CURRENT_PATH)/, $(EXAMPLE_SOURCE))
+
+include ../../build_system/make/rules.mk
diff --git a/examples/host/device_info/only.txt b/examples/host/device_info/only.txt
new file mode 100644
index 000000000..fee10f9e2
--- /dev/null
+++ b/examples/host/device_info/only.txt
@@ -0,0 +1,14 @@
+mcu:KINETIS_KL
+mcu:LPC175X_6X
+mcu:LPC177X_8X
+mcu:LPC18XX
+mcu:LPC40XX
+mcu:LPC43XX
+mcu:MIMXRT1XXX
+mcu:MIMXRT10XX
+mcu:MIMXRT11XX
+mcu:RP2040
+mcu:MSP432E4
+mcu:RX65X
+mcu:RAXXX
+mcu:MAX3421
diff --git a/examples/host/device_info/src/main.c b/examples/host/device_info/src/main.c
new file mode 100644
index 000000000..18beea663
--- /dev/null
+++ b/examples/host/device_info/src/main.c
@@ -0,0 +1,210 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+
+/* Host example will get device descriptors of attached devices and print it out via uart/rtt (logger) as follows:
+ *    Device 1: ID 046d:c52f
+      Device Descriptor:
+        bLength             18
+        bDescriptorType     1
+        bcdUSB              0200
+        bDeviceClass        0
+        bDeviceSubClass     0
+        bDeviceProtocol     0
+        bMaxPacketSize0     8
+        idVendor            0x046d
+        idProduct           0xc52f
+        bcdDevice           2200
+        iManufacturer       1     Logitech
+        iProduct            2     USB Receiver
+        iSerialNumber       0
+        bNumConfigurations  1
+ *
+ */
+
+#include 
+#include 
+#include 
+
+#include "bsp/board_api.h"
+#include "tusb.h"
+
+// English
+#define LANGUAGE_ID 0x0409
+
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+void led_blinking_task(void);
+static void print_utf16(uint16_t* temp_buf, size_t buf_len);
+
+/*------------- MAIN -------------*/
+int main(void) {
+  board_init();
+
+  printf("TinyUSB Device Info Example\r\n");
+
+  // init host stack on configured roothub port
+  tuh_init(BOARD_TUH_RHPORT);
+
+  if (board_init_after_tusb) {
+    board_init_after_tusb();
+  }
+
+  while (1) {
+    // tinyusb host task
+    tuh_task();
+    led_blinking_task();
+  }
+
+  return 0;
+}
+
+/*------------- TinyUSB Callbacks -------------*/
+
+// Invoked when device is mounted (configured)
+void tuh_mount_cb(uint8_t daddr) {
+  // Get Device Descriptor
+  tusb_desc_device_t desc_device;
+  uint8_t xfer_result = tuh_descriptor_get_device_sync(daddr, &desc_device, 18);
+  if (XFER_RESULT_SUCCESS != xfer_result) {
+    printf("Failed to get device descriptor\r\n");
+    return;
+  }
+
+  uint16_t buf[256];
+
+  printf("Device %u: ID %04x:%04x\r\n", daddr, desc_device.idVendor, desc_device.idProduct);
+  printf("Device Descriptor:\r\n");
+  printf("  bLength             %u\r\n", desc_device.bLength);
+  printf("  bDescriptorType     %u\r\n", desc_device.bDescriptorType);
+  printf("  bcdUSB              %04x\r\n", desc_device.bcdUSB);
+  printf("  bDeviceClass        %u\r\n", desc_device.bDeviceClass);
+  printf("  bDeviceSubClass     %u\r\n", desc_device.bDeviceSubClass);
+  printf("  bDeviceProtocol     %u\r\n", desc_device.bDeviceProtocol);
+  printf("  bMaxPacketSize0     %u\r\n", desc_device.bMaxPacketSize0);
+  printf("  idVendor            0x%04x\r\n", desc_device.idVendor);
+  printf("  idProduct           0x%04x\r\n", desc_device.idProduct);
+  printf("  bcdDevice           %04x\r\n", desc_device.bcdDevice);
+
+  // Get String descriptor using Sync API
+
+  printf("  iManufacturer       %u     ", desc_device.iManufacturer);
+  xfer_result = tuh_descriptor_get_manufacturer_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf));
+  if (XFER_RESULT_SUCCESS == xfer_result) {
+    print_utf16(buf, TU_ARRAY_SIZE(buf));
+  }
+  printf("\r\n");
+
+  printf("  iProduct            %u     ", desc_device.iProduct);
+  xfer_result = tuh_descriptor_get_product_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf));
+  if (XFER_RESULT_SUCCESS == xfer_result) {
+    print_utf16(buf, TU_ARRAY_SIZE(buf));
+  }
+  printf("\r\n");
+
+  printf("  iSerialNumber       %u     ", desc_device.iSerialNumber);
+  xfer_result = tuh_descriptor_get_serial_string_sync(daddr, LANGUAGE_ID, buf, sizeof(buf));
+  if (XFER_RESULT_SUCCESS == xfer_result) {
+    print_utf16(buf, TU_ARRAY_SIZE(buf));
+  }
+  printf("\r\n");
+
+  printf("  bNumConfigurations  %u\r\n", desc_device.bNumConfigurations);
+}
+
+/// Invoked when device is unmounted (bus reset/unplugged)
+void tuh_umount_cb(uint8_t daddr) {
+  printf("Device removed, address = %d\r\n", daddr);
+}
+
+//--------------------------------------------------------------------+
+// Blinking Task
+//--------------------------------------------------------------------+
+void led_blinking_task(void) {
+  const uint32_t interval_ms = 1000;
+  static uint32_t start_ms = 0;
+
+  static bool led_state = false;
+
+  // Blink every interval ms
+  if (board_millis() - start_ms < interval_ms) return; // not enough time
+  start_ms += interval_ms;
+
+  board_led_write(led_state);
+  led_state = 1 - led_state; // toggle
+}
+
+//--------------------------------------------------------------------+
+// String Descriptor Helper
+//--------------------------------------------------------------------+
+
+static void _convert_utf16le_to_utf8(const uint16_t* utf16, size_t utf16_len, uint8_t* utf8, size_t utf8_len) {
+  // TODO: Check for runover.
+  (void) utf8_len;
+  // Get the UTF-16 length out of the data itself.
+
+  for (size_t i = 0; i < utf16_len; i++) {
+    uint16_t chr = utf16[i];
+    if (chr < 0x80) {
+      *utf8++ = chr & 0xffu;
+    } else if (chr < 0x800) {
+      *utf8++ = (uint8_t) (0xC0 | (chr >> 6 & 0x1F));
+      *utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F));
+    } else {
+      // TODO: Verify surrogate.
+      *utf8++ = (uint8_t) (0xE0 | (chr >> 12 & 0x0F));
+      *utf8++ = (uint8_t) (0x80 | (chr >> 6 & 0x3F));
+      *utf8++ = (uint8_t) (0x80 | (chr >> 0 & 0x3F));
+    }
+    // TODO: Handle UTF-16 code points that take two entries.
+  }
+}
+
+// Count how many bytes a utf-16-le encoded string will take in utf-8.
+static int _count_utf8_bytes(const uint16_t* buf, size_t len) {
+  size_t total_bytes = 0;
+  for (size_t i = 0; i < len; i++) {
+    uint16_t chr = buf[i];
+    if (chr < 0x80) {
+      total_bytes += 1;
+    } else if (chr < 0x800) {
+      total_bytes += 2;
+    } else {
+      total_bytes += 3;
+    }
+    // TODO: Handle UTF-16 code points that take two entries.
+  }
+  return (int) total_bytes;
+}
+
+static void print_utf16(uint16_t* temp_buf, size_t buf_len) {
+  if ((temp_buf[0] & 0xff) == 0) return;  // empty
+  size_t utf16_len = ((temp_buf[0] & 0xff) - 2) / sizeof(uint16_t);
+  size_t utf8_len = (size_t) _count_utf8_bytes(temp_buf + 1, utf16_len);
+  _convert_utf16le_to_utf8(temp_buf + 1, utf16_len, (uint8_t*) temp_buf, sizeof(uint16_t) * buf_len);
+  ((uint8_t*) temp_buf)[utf8_len] = '\0';
+
+  printf("%s", (char*) temp_buf);
+}
diff --git a/examples/host/device_info/src/tusb_config.h b/examples/host/device_info/src/tusb_config.h
new file mode 100644
index 000000000..2e9731eaf
--- /dev/null
+++ b/examples/host/device_info/src/tusb_config.h
@@ -0,0 +1,115 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 TUSB_CONFIG_H_
+#define TUSB_CONFIG_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// Common Configuration
+//--------------------------------------------------------------------
+
+// defined by compiler flags for flexibility
+#ifndef CFG_TUSB_MCU
+#error CFG_TUSB_MCU must be defined
+#endif
+
+#ifndef CFG_TUSB_OS
+#define CFG_TUSB_OS           OPT_OS_NONE
+#endif
+
+#ifndef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG        0
+#endif
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUH_MEM_SECTION
+#define CFG_TUH_MEM_SECTION
+#endif
+
+#ifndef CFG_TUH_MEM_ALIGN
+#define CFG_TUH_MEM_ALIGN     __attribute__ ((aligned(4)))
+#endif
+
+//--------------------------------------------------------------------
+// Host Configuration
+//--------------------------------------------------------------------
+
+// Enable Host stack
+#define CFG_TUH_ENABLED       1
+
+#if CFG_TUSB_MCU == OPT_MCU_RP2040
+  // #define CFG_TUH_RPI_PIO_USB   1 // use pio-usb as host controller
+  // #define CFG_TUH_MAX3421       1 // use max3421 as host controller
+
+  // host roothub port is 1 if using either pio-usb or max3421
+  #if (defined(CFG_TUH_RPI_PIO_USB) && CFG_TUH_RPI_PIO_USB) || (defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421)
+    #define BOARD_TUH_RHPORT      1
+  #endif
+#endif
+
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED     BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT      0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED   OPT_MODE_DEFAULT_SPEED
+#endif
+
+//--------------------------------------------------------------------
+// Driver Configuration
+//--------------------------------------------------------------------
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+// only hub class is enabled
+#define CFG_TUH_HUB                 1
+
+// max device support (excluding hub device)
+// 1 hub typically has 4 ports
+#define CFG_TUH_DEVICE_MAX          (3*CFG_TUH_HUB + 1)
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/examples/host/hid_controller/src/hid_app.c b/examples/host/hid_controller/src/hid_app.c
index bff830ca2..6ffe0b6d4 100644
--- a/examples/host/hid_controller/src/hid_app.c
+++ b/examples/host/hid_controller/src/hid_app.c
@@ -253,6 +253,7 @@ bool diff_report(sony_ds4_report_t const* rpt1, sony_ds4_report_t const* rpt2)
 
 void process_sony_ds4(uint8_t const* report, uint16_t len)
 {
+  (void)len;
   const char* dpad_str[] = { "N", "NE", "E", "SE", "S", "SW", "W", "NW", "none" };
 
   // previous report used to compare for changes
diff --git a/examples/host/msc_file_explorer/src/main.c b/examples/host/msc_file_explorer/src/main.c
index 73f3e9eb5..f6b6810f5 100644
--- a/examples/host/msc_file_explorer/src/main.c
+++ b/examples/host/msc_file_explorer/src/main.c
@@ -72,8 +72,7 @@ extern bool msc_app_init(void);
 extern void msc_app_task(void);
 
 /*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
   board_init();
 
   printf("TinyUSB Host MassStorage Explorer Example\r\n");
@@ -87,8 +86,7 @@ int main(void)
 
   msc_app_init();
 
-  while (1)
-  {
+  while (1) {
     // tinyusb host task
     tuh_task();
 
@@ -103,28 +101,25 @@ int main(void)
 // TinyUSB Callbacks
 //--------------------------------------------------------------------+
 
-void tuh_mount_cb(uint8_t dev_addr)
-{
+void tuh_mount_cb(uint8_t dev_addr) {
   (void) dev_addr;
 }
 
-void tuh_umount_cb(uint8_t dev_addr)
-{
+void tuh_umount_cb(uint8_t dev_addr) {
   (void) dev_addr;
 }
 
 //--------------------------------------------------------------------+
 // Blinking Task
 //--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
+void led_blinking_task(void) {
   const uint32_t interval_ms = 1000;
   static uint32_t start_ms = 0;
 
   static bool led_state = false;
 
   // Blink every interval ms
-  if ( board_millis() - start_ms < interval_ms) return; // not enough time
+  if (board_millis() - start_ms < interval_ms) return; // not enough time
   start_ms += interval_ms;
 
   board_led_write(led_state);
diff --git a/examples/host/msc_file_explorer/src/msc_app.c b/examples/host/msc_file_explorer/src/msc_app.c
index ecea614a2..ddd39c674 100644
--- a/examples/host/msc_file_explorer/src/msc_app.c
+++ b/examples/host/msc_file_explorer/src/msc_app.c
@@ -95,7 +95,6 @@ void msc_app_task(void)
 //
 //--------------------------------------------------------------------+
 
-
 bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_data)
 {
   msc_cbw_t const* cbw = cb_data->cbw;
@@ -114,7 +113,7 @@ bool inquiry_complete_cb(uint8_t dev_addr, tuh_msc_complete_data_t const * cb_da
   uint32_t const block_count = tuh_msc_get_block_count(dev_addr, cbw->lun);
   uint32_t const block_size = tuh_msc_get_block_size(dev_addr, cbw->lun);
 
-  printf("Disk Size: %lu MB\r\n", block_count / ((1024*1024)/block_size));
+  printf("Disk Size: %" PRIu32 " MB\r\n", block_count / ((1024*1024)/block_size));
   // printf("Block Count = %lu, Block Size: %lu\r\n", block_count, block_size);
 
   // For simplicity: we only mount 1 LUN per device
@@ -541,10 +540,10 @@ void cli_cmd_ls(EmbeddedCli *cli, char *args, void *context)
         printf("%-40s", fno.fname);
         if (fno.fsize < 1024)
         {
-          printf("%lu B\r\n", fno.fsize);
+          printf("%" PRIu32 " B\r\n", fno.fsize);
         }else
         {
-          printf("%lu KB\r\n", fno.fsize / 1024);
+          printf("%" PRIu32 " KB\r\n", fno.fsize / 1024);
         }
       }
     }
diff --git a/hw/bsp/board.c b/hw/bsp/board.c
index 23b4b6628..bb339f613 100644
--- a/hw/bsp/board.c
+++ b/hw/bsp/board.c
@@ -108,6 +108,28 @@ TU_ATTR_USED int sys_read (int fhdl, char *buf, size_t count) {
 //  st->st_mode = S_IFCHR;
 //}
 
+// Clang use picolibc
+#if defined(__clang__)
+static int cl_putc(char c, FILE *f) {
+  (void) f;
+  return sys_write(0, &c, 1);
+}
+
+static int cl_getc(FILE* f) {
+  (void) f;
+  char c;
+  return sys_read(0, &c, 1) > 0 ? c : -1;
+}
+
+static FILE __stdio = FDEV_SETUP_STREAM(cl_putc, cl_getc, NULL, _FDEV_SETUP_RW);
+FILE *const stdin = &__stdio;
+__strong_reference(stdin, stdout);
+__strong_reference(stdin, stderr);
+#endif
+
+//--------------------------------------------------------------------+
+// Board API
+//--------------------------------------------------------------------+
 int board_getchar(void) {
   char c;
   return (sys_read(0, &c, 1) > 0) ? (int) c : (-1);
diff --git a/hw/bsp/board_api.h b/hw/bsp/board_api.h
index 3b9f211a2..a458a3fdc 100644
--- a/hw/bsp/board_api.h
+++ b/hw/bsp/board_api.h
@@ -24,19 +24,22 @@
  * This file is part of the TinyUSB stack.
  */
 
-#ifndef _BOARD_API_H_
-#define _BOARD_API_H_
+#ifndef BOARD_API_H_
+#define BOARD_API_H_
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #include 
+#include 
 #include 
+#include 
+
 #include "tusb.h"
 
 #if CFG_TUSB_OS == OPT_OS_FREERTOS
-#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+#if TUP_MCU_ESPRESSIF
   // ESP-IDF need "freertos/" prefix in include path.
   // CFG_TUSB_OS_INC_PATH should be defined accordingly.
   #include "freertos/FreeRTOS.h"
@@ -69,6 +72,9 @@ void board_init(void);
 // Init board after tinyusb is initialized
 void board_init_after_tusb(void) TU_ATTR_WEAK;
 
+// Jump to bootloader
+void board_reset_to_bootloader(void) TU_ATTR_WEAK;
+
 // Turn LED on or off
 void board_led_write(bool state);
 
@@ -139,6 +145,7 @@ static inline size_t board_usb_get_serial(uint16_t desc_str1[], size_t max_chars
   uint8_t uid[16] TU_ATTR_ALIGNED(4);
   size_t uid_len;
 
+  // TODO work with make, but not working with esp32s3 cmake
   if ( board_get_unique_id ) {
     uid_len = board_get_unique_id(uid, sizeof(uid));
   }else {
diff --git a/hw/bsp/broadcom_32bit/boards/raspberrypi_zero/board.cmake b/hw/bsp/broadcom_32bit/boards/raspberrypi_zero/board.cmake
new file mode 100644
index 000000000..2b8cc19e0
--- /dev/null
+++ b/hw/bsp/broadcom_32bit/boards/raspberrypi_zero/board.cmake
@@ -0,0 +1,8 @@
+set(CMAKE_SYSTEM_PROCESSOR arm1176jzf-s CACHE INTERNAL "System Processor")
+#set(SUFFIX "")
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    BCM_VERSION=2835
+    )
+endfunction()
diff --git a/hw/bsp/broadcom_32bit/boards/raspberrypi_zero_w/board.h b/hw/bsp/broadcom_32bit/boards/raspberrypi_zero/board.h
similarity index 100%
rename from hw/bsp/broadcom_32bit/boards/raspberrypi_zero_w/board.h
rename to hw/bsp/broadcom_32bit/boards/raspberrypi_zero/board.h
diff --git a/hw/bsp/broadcom_32bit/boards/raspberrypi_zero_w/board.mk b/hw/bsp/broadcom_32bit/boards/raspberrypi_zero/board.mk
similarity index 77%
rename from hw/bsp/broadcom_32bit/boards/raspberrypi_zero_w/board.mk
rename to hw/bsp/broadcom_32bit/boards/raspberrypi_zero/board.mk
index 052033230..7a248ed24 100644
--- a/hw/bsp/broadcom_32bit/boards/raspberrypi_zero_w/board.mk
+++ b/hw/bsp/broadcom_32bit/boards/raspberrypi_zero/board.mk
@@ -1,4 +1,4 @@
-CPU_CORE = arm1176
+CPU_CORE = arm1176jzf-s
 CFLAGS += -DBCM_VERSION=2835 \
           -DCFG_TUSB_MCU=OPT_MCU_BCM2835
 
diff --git a/hw/bsp/broadcom_32bit/family.c b/hw/bsp/broadcom_32bit/family.c
index 664b4dcaf..0062e2e83 100644
--- a/hw/bsp/broadcom_32bit/family.c
+++ b/hw/bsp/broadcom_32bit/family.c
@@ -27,6 +27,13 @@
 #include "bsp/board_api.h"
 #include "board.h"
 
+// Suppress warning caused by mcu driver
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
 #include "broadcom/cpu.h"
 #include "broadcom/gpio.h"
 #include "broadcom/interrupts.h"
@@ -34,6 +41,10 @@
 #include "broadcom/caches.h"
 #include "broadcom/vcmailbox.h"
 
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
 // LED
 #define LED_PIN               18
 #define LED_STATE_ON          1
@@ -44,8 +55,7 @@
 //--------------------------------------------------------------------+
 // Forward USB interrupt events to TinyUSB IRQ Handler
 //--------------------------------------------------------------------+
-void USB_IRQHandler(void)
-{
+void USB_IRQHandler(void) {
   tud_int_handler(0);
 }
 
@@ -56,8 +66,7 @@ void USB_IRQHandler(void)
 //--------------------------------------------------------------------+
 // Board porting API
 //--------------------------------------------------------------------+
-void board_init(void)
-{
+void board_init(void) {
   setup_mmu_flat_map();
   init_caches();
 
@@ -97,24 +106,21 @@ void board_init(void)
   BP_EnableIRQs();
 }
 
-void board_led_write(bool state)
-{
-  gpio_set_value(LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+void board_led_write(bool state) {
+  gpio_set_value(LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON));
 }
 
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
   return 0;
 }
 
-int board_uart_read(uint8_t* buf, int len)
-{
-  (void) buf; (void) len;
+int board_uart_read(uint8_t* buf, int len) {
+  (void) buf;
+  (void) len;
   return 0;
 }
 
-int board_uart_write(void const * buf, int len)
-{
+int board_uart_write(void const* buf, int len) {
   for (int i = 0; i < len; i++) {
     const char* cbuf = buf;
     while (!UART1->STAT_b.TX_READY) {}
@@ -127,30 +133,27 @@ int board_uart_write(void const * buf, int len)
   return len;
 }
 
-#if CFG_TUSB_OS  == OPT_OS_NONE
+#if CFG_TUSB_OS == OPT_OS_NONE
 volatile uint32_t system_ticks = 0;
 
-void TIMER_1_IRQHandler(void)
-{
+void TIMER_1_IRQHandler(void) {
   system_ticks++;
   SYSTMR->C1 += 977;
   SYSTMR->CS_b.M1 = 1;
 }
 
-uint32_t board_millis(void)
-{
+uint32_t board_millis(void) {
   return system_ticks;
 }
+
 #endif
 
-void HardFault_Handler (void)
-{
+void HardFault_Handler(void) {
   // asm("bkpt");
 }
 
 // Required by __libc_init_array in startup code if we are compiling using
 // -nostdlib/-nostartfiles.
-void _init(void)
-{
+void _init(void) {
 
 }
diff --git a/hw/bsp/broadcom_32bit/family.cmake b/hw/bsp/broadcom_32bit/family.cmake
new file mode 100644
index 000000000..6205d4e1b
--- /dev/null
+++ b/hw/bsp/broadcom_32bit/family.cmake
@@ -0,0 +1,108 @@
+include_guard()
+
+set(SDK_DIR ${TOP}/hw/mcu/broadcom)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS BCM2835 CACHE INTERNAL "")
+
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${SDK_DIR}/broadcom/link.ld)
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  set(STARTUP_FILE_GNU ${SDK_DIR}/broadcom/boot.s)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/broadcom/gen/interrupt_handlers.c
+    ${SDK_DIR}/broadcom/gpio.c
+    ${SDK_DIR}/broadcom/interrupts.c
+    ${SDK_DIR}/broadcom/mmu.c
+    ${SDK_DIR}/broadcom/caches.c
+    ${SDK_DIR}/broadcom/vcmailbox.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_compile_options(${BOARD_TARGET} PUBLIC
+    -O0
+    -ffreestanding
+    -mgeneral-regs-only
+    -fno-exceptions
+    -std=c17
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_DIR}
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      "LINKER:--entry=_start"
+      --specs=nosys.specs
+      -nostartfiles
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_Clang}"
+      "LINKER:--entry=_start"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_BCM2835 ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_flash_jlink(${TARGET})
+endfunction()
diff --git a/hw/bsp/broadcom_32bit/family.mk b/hw/bsp/broadcom_32bit/family.mk
index cf68e21ee..e15bd93f7 100644
--- a/hw/bsp/broadcom_32bit/family.mk
+++ b/hw/bsp/broadcom_32bit/family.mk
@@ -1,5 +1,4 @@
 MCU_DIR = hw/mcu/broadcom
-DEPS_SUBMODULES += $(MCU_DIR)
 
 include $(TOP)/$(BOARD_PATH)/board.mk
 
@@ -27,15 +26,13 @@ SRC_C += \
 	$(MCU_DIR)/broadcom/caches.c \
 	$(MCU_DIR)/broadcom/vcmailbox.c
 
-SKIP_NANOLIB = 1
-
 LD_FILE = $(MCU_DIR)/broadcom/link$(SUFFIX).ld
 
 INC += \
 	$(TOP)/$(BOARD_PATH) \
 	$(TOP)/$(MCU_DIR)
 
-SRC_S += $(MCU_DIR)/broadcom/boot$(SUFFIX).S
+SRC_S += $(MCU_DIR)/broadcom/boot$(SUFFIX).s
 
 $(BUILD)/kernel$(SUFFIX).img: $(BUILD)/$(PROJECT).elf
 	$(OBJCOPY) -O binary $^ $@
diff --git a/hw/bsp/broadcom_64bit/boards/raspberrypi_cm4/board.cmake b/hw/bsp/broadcom_64bit/boards/raspberrypi_cm4/board.cmake
new file mode 100644
index 000000000..919068f1d
--- /dev/null
+++ b/hw/bsp/broadcom_64bit/boards/raspberrypi_cm4/board.cmake
@@ -0,0 +1,5 @@
+set(CMAKE_SYSTEM_PROCESSOR cortex-a72 CACHE INTERNAL "System Processor")
+set(BCM_VERSION 2711)
+
+function(update_board TARGET)
+endfunction()
diff --git a/hw/bsp/broadcom_64bit/boards/raspberrypi_zero2/board.cmake b/hw/bsp/broadcom_64bit/boards/raspberrypi_zero2/board.cmake
new file mode 100644
index 000000000..85f84e947
--- /dev/null
+++ b/hw/bsp/broadcom_64bit/boards/raspberrypi_zero2/board.cmake
@@ -0,0 +1,5 @@
+set(CMAKE_SYSTEM_PROCESSOR cortex-a53 CACHE INTERNAL "System Processor")
+set(BCM_VERSION 2837)
+
+function(update_board TARGET)
+endfunction()
diff --git a/hw/bsp/broadcom_64bit/boards/raspberrypi_zero2w/board.h b/hw/bsp/broadcom_64bit/boards/raspberrypi_zero2/board.h
similarity index 100%
rename from hw/bsp/broadcom_64bit/boards/raspberrypi_zero2w/board.h
rename to hw/bsp/broadcom_64bit/boards/raspberrypi_zero2/board.h
diff --git a/hw/bsp/broadcom_64bit/boards/raspberrypi_zero2w/board.mk b/hw/bsp/broadcom_64bit/boards/raspberrypi_zero2/board.mk
similarity index 100%
rename from hw/bsp/broadcom_64bit/boards/raspberrypi_zero2w/board.mk
rename to hw/bsp/broadcom_64bit/boards/raspberrypi_zero2/board.mk
diff --git a/hw/bsp/broadcom_64bit/family.c b/hw/bsp/broadcom_64bit/family.c
index 664b4dcaf..0062e2e83 100644
--- a/hw/bsp/broadcom_64bit/family.c
+++ b/hw/bsp/broadcom_64bit/family.c
@@ -27,6 +27,13 @@
 #include "bsp/board_api.h"
 #include "board.h"
 
+// Suppress warning caused by mcu driver
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wcast-qual"
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
 #include "broadcom/cpu.h"
 #include "broadcom/gpio.h"
 #include "broadcom/interrupts.h"
@@ -34,6 +41,10 @@
 #include "broadcom/caches.h"
 #include "broadcom/vcmailbox.h"
 
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
 // LED
 #define LED_PIN               18
 #define LED_STATE_ON          1
@@ -44,8 +55,7 @@
 //--------------------------------------------------------------------+
 // Forward USB interrupt events to TinyUSB IRQ Handler
 //--------------------------------------------------------------------+
-void USB_IRQHandler(void)
-{
+void USB_IRQHandler(void) {
   tud_int_handler(0);
 }
 
@@ -56,8 +66,7 @@ void USB_IRQHandler(void)
 //--------------------------------------------------------------------+
 // Board porting API
 //--------------------------------------------------------------------+
-void board_init(void)
-{
+void board_init(void) {
   setup_mmu_flat_map();
   init_caches();
 
@@ -97,24 +106,21 @@ void board_init(void)
   BP_EnableIRQs();
 }
 
-void board_led_write(bool state)
-{
-  gpio_set_value(LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+void board_led_write(bool state) {
+  gpio_set_value(LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON));
 }
 
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
   return 0;
 }
 
-int board_uart_read(uint8_t* buf, int len)
-{
-  (void) buf; (void) len;
+int board_uart_read(uint8_t* buf, int len) {
+  (void) buf;
+  (void) len;
   return 0;
 }
 
-int board_uart_write(void const * buf, int len)
-{
+int board_uart_write(void const* buf, int len) {
   for (int i = 0; i < len; i++) {
     const char* cbuf = buf;
     while (!UART1->STAT_b.TX_READY) {}
@@ -127,30 +133,27 @@ int board_uart_write(void const * buf, int len)
   return len;
 }
 
-#if CFG_TUSB_OS  == OPT_OS_NONE
+#if CFG_TUSB_OS == OPT_OS_NONE
 volatile uint32_t system_ticks = 0;
 
-void TIMER_1_IRQHandler(void)
-{
+void TIMER_1_IRQHandler(void) {
   system_ticks++;
   SYSTMR->C1 += 977;
   SYSTMR->CS_b.M1 = 1;
 }
 
-uint32_t board_millis(void)
-{
+uint32_t board_millis(void) {
   return system_ticks;
 }
+
 #endif
 
-void HardFault_Handler (void)
-{
+void HardFault_Handler(void) {
   // asm("bkpt");
 }
 
 // Required by __libc_init_array in startup code if we are compiling using
 // -nostdlib/-nostartfiles.
-void _init(void)
-{
+void _init(void) {
 
 }
diff --git a/hw/bsp/broadcom_64bit/family.cmake b/hw/bsp/broadcom_64bit/family.cmake
new file mode 100644
index 000000000..f373dc633
--- /dev/null
+++ b/hw/bsp/broadcom_64bit/family.cmake
@@ -0,0 +1,115 @@
+include_guard()
+
+set(SDK_DIR ${TOP}/hw/mcu/broadcom)
+set(CMSIS_5 ${TOP}/lib/CMSIS_5)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/aarch64_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS BCM2711 BCM2835 CACHE INTERNAL "")
+
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${SDK_DIR}/broadcom/link8.ld)
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  set(STARTUP_FILE_GNU ${SDK_DIR}/broadcom/boot8.s)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/broadcom/gen/interrupt_handlers.c
+    ${SDK_DIR}/broadcom/gpio.c
+    ${SDK_DIR}/broadcom/interrupts.c
+    ${SDK_DIR}/broadcom/mmu.c
+    ${SDK_DIR}/broadcom/caches.c
+    ${SDK_DIR}/broadcom/vcmailbox.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_compile_options(${BOARD_TARGET} PUBLIC
+    -O0
+    -ffreestanding
+    -mgeneral-regs-only
+    -fno-exceptions
+    -std=c17
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    BCM_VERSION=${BCM_VERSION}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_DIR}
+    ${CMSIS_5}/CMSIS/Core_A/Include
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+#    target_compile_options(${BOARD_TARGET} PUBLIC
+#      )
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      "LINKER:--entry=_start"
+      --specs=nosys.specs
+      -nostartfiles
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_Clang}"
+      "LINKER:--entry=_start"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_BCM${BCM_VERSION} ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_flash_jlink(${TARGET})
+endfunction()
diff --git a/hw/bsp/broadcom_64bit/family.mk b/hw/bsp/broadcom_64bit/family.mk
index 97af6d64a..89f798f19 100644
--- a/hw/bsp/broadcom_64bit/family.mk
+++ b/hw/bsp/broadcom_64bit/family.mk
@@ -1,6 +1,4 @@
 MCU_DIR = hw/mcu/broadcom
-DEPS_SUBMODULES += $(MCU_DIR)
-
 include $(TOP)/$(BOARD_PATH)/board.mk
 
 CFLAGS += \
@@ -9,6 +7,7 @@ CFLAGS += \
 	-ffreestanding \
 	-nostdlib \
 	-nostartfiles \
+	--specs=nosys.specs \
 	-mgeneral-regs-only \
 	-std=c17
 
@@ -26,8 +25,6 @@ SRC_C += \
 	$(MCU_DIR)/broadcom/caches.c \
 	$(MCU_DIR)/broadcom/vcmailbox.c
 
-SKIP_NANOLIB = 1
-
 LD_FILE = $(MCU_DIR)/broadcom/link8.ld
 
 INC += \
@@ -35,7 +32,7 @@ INC += \
 	$(TOP)/$(MCU_DIR) \
 	$(TOP)/lib/CMSIS_5/CMSIS/Core_A/Include
 
-SRC_S += $(MCU_DIR)/broadcom/boot8.S
+SRC_S += $(MCU_DIR)/broadcom/boot8.s
 
 $(BUILD)/kernel8.img: $(BUILD)/$(PROJECT).elf
 	$(OBJCOPY) -O binary $^ $@
diff --git a/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.cmake b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.cmake
new file mode 100644
index 000000000..f6e47ba30
--- /dev/null
+++ b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.cmake
@@ -0,0 +1,8 @@
+set(LD_FLASH_SIZE 64K)
+set(LD_RAM_SIZE 20K)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    CFG_EXAMPLE_MSC_DUAL_READONLY
+    )
+endfunction()
diff --git a/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.h b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.h
new file mode 100644
index 000000000..3b1187c3a
--- /dev/null
+++ b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.h
@@ -0,0 +1,20 @@
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LED_PORT       GPIOA
+#define LED_PIN        GPIO_Pin_10
+#define LED_STATE_ON   0
+
+#define BUTTON_PORT           GPIOA
+#define BUTTON_PIN            GPIO_Pin_1
+#define BUTTON_STATE_ACTIVE   0
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.mk b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.mk
new file mode 100644
index 000000000..e594f42a7
--- /dev/null
+++ b/hw/bsp/ch32v10x/boards/ch32v103r_r1_1v0/board.mk
@@ -0,0 +1,5 @@
+CFLAGS += -DCFG_EXAMPLE_MSC_DUAL_READONLY
+
+LDFLAGS += \
+  -Wl,--defsym=__FLASH_SIZE=64K \
+  -Wl,--defsym=__RAM_SIZE=20K \
diff --git a/hw/bsp/ch32v10x/ch32v10x_conf.h b/hw/bsp/ch32v10x/ch32v10x_conf.h
new file mode 100644
index 000000000..939c0fbde
--- /dev/null
+++ b/hw/bsp/ch32v10x/ch32v10x_conf.h
@@ -0,0 +1,37 @@
+/********************************** (C) COPYRIGHT *******************************
+ * File Name          : ch32v10x_conf.h
+ * Author             : WCH
+ * Version            : V1.0.0
+ * Date               : 2020/04/30
+ * Description        : Library configuration file.
+*********************************************************************************
+* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
+* Attention: This software (modified or not) and binary are used for
+* microcontroller manufactured by Nanjing Qinheng Microelectronics.
+*******************************************************************************/
+#ifndef __CH32V10x_CONF_H
+#define __CH32V10x_CONF_H
+
+#include "ch32v10x_adc.h"
+#include "ch32v10x_bkp.h"
+#include "ch32v10x_crc.h"
+#include "ch32v10x_dbgmcu.h"
+#include "ch32v10x_dma.h"
+#include "ch32v10x_exti.h"
+#include "ch32v10x_flash.h"
+#include "ch32v10x_gpio.h"
+#include "ch32v10x_i2c.h"
+#include "ch32v10x_iwdg.h"
+#include "ch32v10x_pwr.h"
+#include "ch32v10x_rcc.h"
+#include "ch32v10x_rtc.h"
+#include "ch32v10x_spi.h"
+#include "ch32v10x_tim.h"
+#include "ch32v10x_usart.h"
+#include "ch32v10x_wwdg.h"
+#include "ch32v10x_usb.h"
+#include "ch32v10x_usb_host.h"
+#include "ch32v10x_it.h"
+#include "ch32v10x_misc.h"
+
+#endif /* __CH32V10x_CONF_H */
diff --git a/hw/bsp/ch32v10x/ch32v10x_it.h b/hw/bsp/ch32v10x/ch32v10x_it.h
new file mode 100644
index 000000000..13afc2412
--- /dev/null
+++ b/hw/bsp/ch32v10x/ch32v10x_it.h
@@ -0,0 +1,15 @@
+/********************************** (C) COPYRIGHT *******************************
+ * File Name          : ch32v10x_it.h
+ * Author             : WCH
+ * Version            : V1.0.0
+ * Date               : 2022/08/20
+ * Description        : This file contains the headers of the interrupt handlers.
+*********************************************************************************
+* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
+* Attention: This software (modified or not) and binary are used for
+* microcontroller manufactured by Nanjing Qinheng Microelectronics.
+*******************************************************************************/
+#ifndef __CH32V10x_IT_H
+#define __CH32V10x_IT_H
+
+#endif /* __CH32V10x_IT_H */
diff --git a/hw/bsp/ch32v10x/family.c b/hw/bsp/ch32v10x/family.c
new file mode 100644
index 000000000..15f754e11
--- /dev/null
+++ b/hw/bsp/ch32v10x/family.c
@@ -0,0 +1,148 @@
+#include 
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#include "ch32v10x.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#include "bsp/board_api.h"
+#include "board.h"
+
+__attribute__((interrupt)) __attribute__((used))
+void USBHD_IRQHandler(void) {
+  #if CFG_TUD_WCH_USBIP_USBFS
+  tud_int_handler(0);
+  #endif
+}
+
+__attribute__((interrupt)) __attribute__((used))
+void USBWakeUp_IRQHandler(void) {
+  #if CFG_TUD_WCH_USBIP_USBFS
+  tud_int_handler(0);
+  #endif
+}
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+volatile uint32_t system_ticks = 0;
+
+__attribute__((interrupt)) __attribute__((used))
+void SysTick_Handler(void) {
+  SysTick->CNTL0 = SysTick->CNTL1 = SysTick->CNTL2 = SysTick->CNTL3 = 0;
+  SysTick->CNTH0 = SysTick->CNTH1 = SysTick->CNTH2 = SysTick->CNTH3 = 0;
+  system_ticks++;
+}
+
+uint32_t SysTick_Config(uint32_t ticks) {
+  NVIC_EnableIRQ(SysTicK_IRQn);
+  SysTick->CTLR = 0;
+  SysTick->CNTL0 = SysTick->CNTL1 = SysTick->CNTL2 = SysTick->CNTL3 = 0;
+  SysTick->CNTH0 = SysTick->CNTH1 = SysTick->CNTH2 = SysTick->CNTH3 = 0;
+
+  SysTick->CMPLR0 = (u8)(ticks & 0xFF);
+  SysTick->CMPLR1 = (u8)(ticks >> 8);
+  SysTick->CMPLR2 = (u8)(ticks >> 16);
+  SysTick->CMPLR3 = (u8)(ticks >> 24);
+
+  SysTick->CMPHR0 = SysTick->CMPHR1 = SysTick->CMPHR2 =   SysTick->CMPHR3 = 0;
+  SysTick->CTLR = 1;
+  return 0;
+}
+
+uint32_t board_millis(void) {
+  return system_ticks;
+}
+#endif
+
+void board_init(void) {
+  __disable_irq();
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+  SysTick_Config(SystemCoreClock / 1000);
+#endif
+
+  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
+
+  EXTEN->EXTEN_CTR |= EXTEN_USBFS_IO_EN;
+  uint8_t usb_div;
+  switch (SystemCoreClock) {
+    case 48000000: usb_div = RCC_USBCLKSource_PLLCLK_Div1; break;
+    case 72000000: usb_div = RCC_USBCLKSource_PLLCLK_1Div5; break;
+    default: TU_ASSERT(0,); break;
+  }
+  RCC_USBCLKConfig(usb_div);
+  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBFS, ENABLE);
+
+  #ifdef LED_PIN
+  GPIO_InitTypeDef led_init = {
+      .GPIO_Pin = LED_PIN,
+      .GPIO_Mode = GPIO_Mode_Out_OD,
+      .GPIO_Speed = GPIO_Speed_50MHz,
+  };
+  GPIO_Init(LED_PORT, &led_init);
+  #endif
+
+  #ifdef BUTTON_PIN
+  GPIO_InitTypeDef button_init = {
+      .GPIO_Pin = BUTTON_PIN,
+      .GPIO_Mode = GPIO_Mode_IPU,
+      .GPIO_Speed = GPIO_Speed_50MHz,
+  };
+  GPIO_Init(BUTTON_PORT, &button_init);
+  #endif
+
+  // UART TX is PA9
+  RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
+  GPIO_InitTypeDef usart_init = {
+    .GPIO_Pin = GPIO_Pin_9,
+    .GPIO_Speed = GPIO_Speed_50MHz,
+    .GPIO_Mode = GPIO_Mode_AF_PP,
+  };
+  GPIO_Init(GPIOA, &usart_init);
+
+  USART_InitTypeDef usart = {
+    .USART_BaudRate = 115200,
+    .USART_WordLength = USART_WordLength_8b,
+    .USART_StopBits = USART_StopBits_1,
+    .USART_Parity = USART_Parity_No,
+    .USART_Mode = USART_Mode_Tx,
+    .USART_HardwareFlowControl = USART_HardwareFlowControl_None,
+  };
+  USART_Init(USART1, &usart);
+  USART_Cmd(USART1, ENABLE);
+
+  __enable_irq();
+
+  board_led_write(true);
+}
+
+void board_led_write(bool state) {
+  GPIO_WriteBit(LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+}
+
+uint32_t board_button_read(void) {
+  return BUTTON_STATE_ACTIVE == GPIO_ReadInputDataBit(BUTTON_PORT, BUTTON_PIN);
+}
+
+int board_uart_read(uint8_t *buf, int len) {
+  (void) buf;
+  (void) len;
+  return 0;
+}
+
+int board_uart_write(void const *buf, int len) {
+  const char *bufc = (const char *) buf;
+  for (int i = 0; i < len; i++) {
+    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET);
+    USART_SendData(USART1, *bufc++);
+  }
+
+  return len;
+}
diff --git a/hw/bsp/ch32v10x/family.cmake b/hw/bsp/ch32v10x/family.cmake
new file mode 100644
index 000000000..c0af0ef44
--- /dev/null
+++ b/hw/bsp/ch32v10x/family.cmake
@@ -0,0 +1,117 @@
+include_guard()
+
+#set(UF2_FAMILY_ID 0x699b62ec)
+set(CH32_FAMILY ch32v10x)
+set(SDK_DIR ${TOP}/hw/mcu/wch/ch32v103)
+set(SDK_SRC_DIR ${SDK_DIR}/EVT/EXAM/SRC)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR rv32imac-ilp32 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/riscv_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS CH32V103 CACHE INTERNAL "")
+set(OPENOCD_OPTION "-f ${CMAKE_CURRENT_LIST_DIR}/wch-riscv.cfg")
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/linker/${CH32_FAMILY}.ld)
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  if (NOT DEFINED STARTUP_FILE_GNU)
+    set(STARTUP_FILE_GNU ${SDK_SRC_DIR}/Startup/startup_${CH32_FAMILY}.S)
+  endif ()
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_SRC_DIR}/Core/core_riscv.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_gpio.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_misc.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_rcc.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_usart.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/system_${CH32_FAMILY}.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_SRC_DIR}/Core
+    ${SDK_SRC_DIR}/Peripheral/inc
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(${BOARD_TARGET} PUBLIC
+      -mcmodel=medany
+      )
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -Wl,--defsym=__FLASH_SIZE=${LD_FLASH_SIZE}
+      -Wl,--defsym=__RAM_SIZE=${LD_RAM_SIZE}
+      -nostartfiles
+      --specs=nosys.specs --specs=nano.specs
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    message(FATAL_ERROR "Clang is not supported for MSP432E4")
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_CH32V103 ${RTOS})
+
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/wch/dcd_ch32_usbfs.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_add_bin_hex(${TARGET})
+  family_flash_openocd_wch(${TARGET})
+
+  #family_add_uf2(${TARGET} ${UF2_FAMILY_ID})
+  #family_flash_uf2(${TARGET} ${UF2_FAMILY_ID})
+endfunction()
diff --git a/hw/bsp/ch32v10x/family.mk b/hw/bsp/ch32v10x/family.mk
new file mode 100644
index 000000000..d96d5012e
--- /dev/null
+++ b/hw/bsp/ch32v10x/family.mk
@@ -0,0 +1,53 @@
+# https://www.embecosm.com/resources/tool-chain-downloads/#riscv-stable
+#CROSS_COMPILE ?= riscv32-unknown-elf-
+
+# Toolchain from https://nucleisys.com/download.php
+#CROSS_COMPILE ?= riscv-nuclei-elf-
+
+# Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
+CROSS_COMPILE ?= riscv-none-elf-
+
+CH32_FAMILY = ch32v10x
+SDK_DIR = hw/mcu/wch/ch32v103
+SDK_SRC_DIR = $(SDK_DIR)/EVT/EXAM/SRC
+
+include $(TOP)/$(BOARD_PATH)/board.mk
+CPU_CORE ?= rv32imac-ilp32
+
+# Port0 use FSDev, Port1 use USBFS
+PORT ?= 0
+
+CFLAGS += \
+	-mcmodel=medany \
+	-ffat-lto-objects \
+	-flto \
+	-DCFG_TUSB_MCU=OPT_MCU_CH32V103
+
+# https://github.com/openwch/ch32v20x/pull/12
+CFLAGS += -Wno-error=strict-prototypes
+
+LDFLAGS_GCC += \
+	-nostdlib -nostartfiles \
+	--specs=nosys.specs --specs=nano.specs \
+
+LD_FILE = $(FAMILY_PATH)/linker/${CH32_FAMILY}.ld
+
+SRC_C += \
+	src/portable/wch/dcd_ch32_usbfs.c \
+	$(SDK_SRC_DIR)/Core/core_riscv.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_gpio.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_misc.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_rcc.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_usart.c \
+
+SRC_S += $(SDK_SRC_DIR)/Startup/startup_${CH32_FAMILY}.S
+
+INC += \
+	$(TOP)/$(BOARD_PATH) \
+	$(TOP)/$(SDK_SRC_DIR)/Core \
+	$(TOP)/$(SDK_SRC_DIR)/Peripheral/inc \
+
+FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V
+
+OPENOCD_WCH_OPTION=-f $(TOP)/$(FAMILY_PATH)/wch-riscv.cfg
+flash: flash-openocd-wch
diff --git a/hw/bsp/ch32v10x/linker/ch32v10x.ld b/hw/bsp/ch32v10x/linker/ch32v10x.ld
new file mode 100644
index 000000000..cd5c8dc17
--- /dev/null
+++ b/hw/bsp/ch32v10x/linker/ch32v10x.ld
@@ -0,0 +1,165 @@
+/* Define default values if not already defined */
+__FLASH_SIZE = DEFINED(__flash_size) ? __flash_size : 64K;
+__RAM_SIZE = DEFINED(__ram_size) ? __ram_size : 20K;
+
+MEMORY
+{
+	FLASH (rx) : ORIGIN = 0x00000000, LENGTH = __FLASH_SIZE
+	RAM (xrw) : ORIGIN = 0x20000000, LENGTH = __RAM_SIZE
+}
+
+ENTRY( _start )
+
+__stack_size = 2048;
+
+PROVIDE( _stack_size = __stack_size );
+
+SECTIONS
+{
+	.init :
+	{
+		_sinit = .;
+		. = ALIGN(4);
+		KEEP(*(SORT_NONE(.init)))
+		. = ALIGN(4);
+		_einit = .;
+	} >FLASH AT>FLASH
+
+  .vector :
+  {
+      *(.vector);
+	  . = ALIGN(64);
+  } >FLASH AT>FLASH
+
+	.text :
+	{
+		. = ALIGN(4);
+		*(.text)
+		*(.text.*)
+		*(.rodata)
+		*(.rodata*)
+		*(.gnu.linkonce.t.*)
+		. = ALIGN(4);
+	} >FLASH AT>FLASH
+
+	.fini :
+	{
+		KEEP(*(SORT_NONE(.fini)))
+		. = ALIGN(4);
+	} >FLASH AT>FLASH
+
+	PROVIDE( _etext = . );
+	PROVIDE( _eitcm = . );
+
+	.preinit_array  :
+	{
+	  PROVIDE_HIDDEN (__preinit_array_start = .);
+	  KEEP (*(.preinit_array))
+	  PROVIDE_HIDDEN (__preinit_array_end = .);
+	} >FLASH AT>FLASH
+
+	.init_array     :
+	{
+	  PROVIDE_HIDDEN (__init_array_start = .);
+	  KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+	  KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+	  PROVIDE_HIDDEN (__init_array_end = .);
+	} >FLASH AT>FLASH
+
+	.fini_array     :
+	{
+	  PROVIDE_HIDDEN (__fini_array_start = .);
+	  KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+	  KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+	  PROVIDE_HIDDEN (__fini_array_end = .);
+	} >FLASH AT>FLASH
+
+	.ctors          :
+	{
+	  /* gcc uses crtbegin.o to find the start of
+	     the constructors, so we make sure it is
+	     first.  Because this is a wildcard, it
+	     doesn't matter if the user does not
+	     actually link against crtbegin.o; the
+	     linker won't look for a file to match a
+	     wildcard.  The wildcard also means that it
+	     doesn't matter which directory crtbegin.o
+	     is in.  */
+	  KEEP (*crtbegin.o(.ctors))
+	  KEEP (*crtbegin?.o(.ctors))
+	  /* We don't want to include the .ctor section from
+	     the crtend.o file until after the sorted ctors.
+	     The .ctor section from the crtend file contains the
+	     end of ctors marker and it must be last */
+	  KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+	  KEEP (*(SORT(.ctors.*)))
+	  KEEP (*(.ctors))
+	} >FLASH AT>FLASH
+
+	.dtors          :
+	{
+	  KEEP (*crtbegin.o(.dtors))
+	  KEEP (*crtbegin?.o(.dtors))
+	  KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+	  KEEP (*(SORT(.dtors.*)))
+	  KEEP (*(.dtors))
+	} >FLASH AT>FLASH
+
+	.dalign :
+	{
+		. = ALIGN(4);
+		PROVIDE(_data_vma = .);
+	} >RAM AT>FLASH
+
+	.dlalign :
+	{
+		. = ALIGN(4);
+		PROVIDE(_data_lma = .);
+	} >FLASH AT>FLASH
+
+	.data :
+	{
+    	*(.gnu.linkonce.r.*)
+    	*(.data .data.*)
+    	*(.gnu.linkonce.d.*)
+		. = ALIGN(8);
+    	PROVIDE( __global_pointer$ = . + 0x800 );
+    	*(.sdata .sdata.*)
+		*(.sdata2.*)
+    	*(.gnu.linkonce.s.*)
+    	. = ALIGN(8);
+    	*(.srodata.cst16)
+    	*(.srodata.cst8)
+    	*(.srodata.cst4)
+    	*(.srodata.cst2)
+    	*(.srodata .srodata.*)
+    	. = ALIGN(4);
+		PROVIDE( _edata = .);
+	} >RAM AT>FLASH
+
+	.bss :
+	{
+		. = ALIGN(4);
+		PROVIDE( _sbss = .);
+  	    *(.sbss*)
+        *(.gnu.linkonce.sb.*)
+		*(.bss*)
+     	*(.gnu.linkonce.b.*)
+		*(COMMON*)
+		. = ALIGN(4);
+		PROVIDE( _ebss = .);
+	} >RAM AT>FLASH
+
+	PROVIDE( _end = _ebss);
+	PROVIDE( end = . );
+
+    .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :
+    {
+        PROVIDE( _heap_end = . );
+        . = ALIGN(4);
+        PROVIDE(_susrstack = . );
+        . = . + __stack_size;
+        PROVIDE( _eusrstack = .);
+    } >RAM
+
+}
diff --git a/hw/bsp/ch32v10x/system_ch32v10x.c b/hw/bsp/ch32v10x/system_ch32v10x.c
new file mode 100644
index 000000000..b083b83b4
--- /dev/null
+++ b/hw/bsp/ch32v10x/system_ch32v10x.c
@@ -0,0 +1,600 @@
+/********************************** (C) COPYRIGHT *******************************
+ * File Name          : system_ch32v10x.c
+ * Author             : WCH
+ * Version            : V1.0.0
+ * Date               : 2020/04/30
+ * Description        : CH32V10x Device Peripheral Access Layer System Source File.
+*********************************************************************************
+* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
+* Attention: This software (modified or not) and binary are used for
+* microcontroller manufactured by Nanjing Qinheng Microelectronics.
+*******************************************************************************/
+#include "ch32v10x.h"
+
+/*
+ * Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after
+ * reset the HSI is used as SYSCLK source).
+ * If none of the define below is enabled, the HSI is used as System clock source.
+ */
+//#define SYSCLK_FREQ_HSE    HSE_VALUE
+//#define SYSCLK_FREQ_48MHz_HSE  48000000
+//#define SYSCLK_FREQ_56MHz_HSE  56000000
+#define SYSCLK_FREQ_72MHz_HSE  72000000
+//#define SYSCLK_FREQ_HSI    HSI_VALUE
+//#define SYSCLK_FREQ_48MHz_HSI  48000000
+//#define SYSCLK_FREQ_56MHz_HSI  56000000
+//#define SYSCLK_FREQ_72MHz_HSI  72000000
+
+/* Clock Definitions */
+#ifdef SYSCLK_FREQ_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_HSE;              /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_48MHz_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_48MHz_HSE;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_56MHz_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_56MHz_HSE;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_72MHz_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz_HSE;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_48MHz_HSI
+uint32_t SystemCoreClock         = SYSCLK_FREQ_48MHz_HSI;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_56MHz_HSI
+uint32_t SystemCoreClock         = SYSCLK_FREQ_56MHz_HSI;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_72MHz_HSI
+uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz_HSI;        /* System Clock Frequency (Core Clock) */
+#else
+uint32_t SystemCoreClock         = HSI_VALUE;                    /* System Clock Frequency (Core Clock) */
+
+#endif
+
+__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+
+/* ch32v10x_system_private_function_proto_types */
+static void SetSysClock(void);
+
+#ifdef SYSCLK_FREQ_HSE
+static void SetSysClockToHSE( void );
+#elif defined SYSCLK_FREQ_48MHz_HSE
+static void SetSysClockTo48_HSE( void );
+#elif defined SYSCLK_FREQ_56MHz_HSE
+static void SetSysClockTo56_HSE( void );
+#elif defined SYSCLK_FREQ_72MHz_HSE
+static void SetSysClockTo72_HSE( void );
+#elif defined SYSCLK_FREQ_48MHz_HSI
+static void SetSysClockTo48_HSI( void );
+#elif defined SYSCLK_FREQ_56MHz_HSI
+static void SetSysClockTo56_HSI( void );
+#elif defined SYSCLK_FREQ_72MHz_HSI
+static void SetSysClockTo72_HSI( void );
+
+#endif
+
+/*********************************************************************
+ * @fn      SystemInit
+ *
+ * @brief   Setup the microcontroller system Initialize the Embedded Flash Interface,
+ *        the PLL and update the SystemCoreClock variable.
+ *
+ * @return  none
+ */
+void SystemInit(void)
+{
+    RCC->CTLR |= (uint32_t)0x00000001;
+    RCC->CFGR0 &= (uint32_t)0xF8FF0000;
+    RCC->CTLR &= (uint32_t)0xFEF6FFFF;
+    RCC->CTLR &= (uint32_t)0xFFFBFFFF;
+    RCC->CFGR0 &= (uint32_t)0xFF80FFFF;
+    RCC->INTR = 0x009F0000;
+    SetSysClock();
+}
+
+/*********************************************************************
+ * @fn      SystemCoreClockUpdate
+ *
+ * @brief   Update SystemCoreClock variable according to Clock Register Values.
+ *
+ * @return  none
+ */
+void SystemCoreClockUpdate(void)
+{
+    uint32_t tmp = 0, pllmull = 0, pllsource = 0;
+
+    tmp = RCC->CFGR0 & RCC_SWS;
+
+    switch(tmp)
+    {
+        case 0x00:
+            SystemCoreClock = HSI_VALUE;
+            break;
+        case 0x04:
+            SystemCoreClock = HSE_VALUE;
+            break;
+        case 0x08:
+            pllmull = RCC->CFGR0 & RCC_PLLMULL;
+            pllsource = RCC->CFGR0 & RCC_PLLSRC;
+            pllmull = (pllmull >> 18) + 2;
+            if(pllsource == 0x00)
+            {
+                if( EXTEN->EXTEN_CTR & EXTEN_PLL_HSI_PRE )
+                {
+                    SystemCoreClock = ( HSI_VALUE ) * pllmull;
+                }
+                else
+                {
+                    SystemCoreClock = ( HSI_VALUE >> 1 ) * pllmull;
+                }
+            }
+            else
+            {
+                if((RCC->CFGR0 & RCC_PLLXTPRE) != (uint32_t)RESET)
+                {
+                    SystemCoreClock = (HSE_VALUE >> 1) * pllmull;
+                }
+                else
+                {
+                    SystemCoreClock = HSE_VALUE * pllmull;
+                }
+            }
+            break;
+        default:
+            SystemCoreClock = HSI_VALUE;
+            break;
+    }
+
+    tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)];
+    SystemCoreClock >>= tmp;
+}
+
+/*********************************************************************
+ * @fn      SetSysClock
+ *
+ * @brief   Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClock(void)
+{
+    //GPIO_IPD_Unused();
+#ifdef SYSCLK_FREQ_HSE
+    SetSysClockToHSE();
+#elif defined SYSCLK_FREQ_48MHz_HSE
+    SetSysClockTo48_HSE();
+#elif defined SYSCLK_FREQ_56MHz_HSE
+    SetSysClockTo56_HSE();
+#elif defined SYSCLK_FREQ_72MHz_HSE
+    SetSysClockTo72_HSE();
+#elif defined SYSCLK_FREQ_48MHz_HSI
+    SetSysClockTo48_HSI();
+#elif defined SYSCLK_FREQ_56MHz_HSI
+    SetSysClockTo56_HSI();
+#elif defined SYSCLK_FREQ_72MHz_HSI
+    SetSysClockTo72_HSI();
+
+#endif
+
+    /* If none of the define above is enabled, the HSI is used as System clock
+     * source (default after reset)
+     */
+}
+
+#ifdef SYSCLK_FREQ_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockToHSE
+ *
+ * @brief   Sets HSE as System clock source and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockToHSE(void)
+{
+    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+    RCC->CTLR |= ((uint32_t)RCC_HSEON);
+
+    /* Wait till HSE is ready and if Time out is reached exit */
+    do
+    {
+        HSEStatus = RCC->CTLR & RCC_HSERDY;
+        StartUpCounter++;
+    } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+    if((RCC->CTLR & RCC_HSERDY) != RESET)
+    {
+        HSEStatus = (uint32_t)0x01;
+    }
+    else
+    {
+        HSEStatus = (uint32_t)0x00;
+    }
+
+    if(HSEStatus == (uint32_t)0x01)
+    {
+        FLASH->ACTLR |= FLASH_ACTLR_PRFTBE;
+        /* Flash 0 wait state */
+        FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
+        FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_0;
+
+        /* HCLK = SYSCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+        /* PCLK2 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+        /* PCLK1 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV1;
+
+        /* Select HSE as system clock source */
+        RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
+        RCC->CFGR0 |= (uint32_t)RCC_SW_HSE;
+
+        /* Wait till HSE is used as system clock source */
+        while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x04)
+        {
+        }
+    }
+    else
+    {
+        /* If HSE fails to start-up, the application will have wrong clock
+         * configuration. User can add here some code to deal with this error
+         */
+    }
+}
+
+
+#elif defined SYSCLK_FREQ_48MHz_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockTo48_HSE
+ *
+ * @brief   Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo48_HSE(void)
+{
+    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+    RCC->CTLR |= ((uint32_t)RCC_HSEON);
+    /* Wait till HSE is ready and if Time out is reached exit */
+    do
+    {
+        HSEStatus = RCC->CTLR & RCC_HSERDY;
+        StartUpCounter++;
+    } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+    if((RCC->CTLR & RCC_HSERDY) != RESET)
+    {
+        HSEStatus = (uint32_t)0x01;
+    }
+    else
+    {
+        HSEStatus = (uint32_t)0x00;
+    }
+
+    if(HSEStatus == (uint32_t)0x01)
+    {
+        /* Enable Prefetch Buffer */
+        FLASH->ACTLR |= FLASH_ACTLR_PRFTBE;
+
+        /* Flash 1 wait state */
+        FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
+        FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1;
+
+        /* HCLK = SYSCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+        /* PCLK2 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+        /* PCLK1 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+        /*  PLL configuration: PLLCLK = HSE * 6 = 48 MHz */
+        RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+        RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLMULL6);
+
+        /* Enable PLL */
+        RCC->CTLR |= RCC_PLLON;
+        /* Wait till PLL is ready */
+        while((RCC->CTLR & RCC_PLLRDY) == 0)
+        {
+        }
+        /* Select PLL as system clock source */
+        RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
+        RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+        /* Wait till PLL is used as system clock source */
+        while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+        {
+        }
+    }
+    else
+    {
+        /*
+         * If HSE fails to start-up, the application will have wrong clock
+         * configuration. User can add here some code to deal with this error
+         */
+    }
+}
+
+#elif defined SYSCLK_FREQ_56MHz_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockTo56_HSE
+ *
+ * @brief   Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo56_HSE(void)
+{
+    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+    RCC->CTLR |= ((uint32_t)RCC_HSEON);
+
+    /* Wait till HSE is ready and if Time out is reached exit */
+    do
+    {
+        HSEStatus = RCC->CTLR & RCC_HSERDY;
+        StartUpCounter++;
+    } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+    if((RCC->CTLR & RCC_HSERDY) != RESET)
+    {
+        HSEStatus = (uint32_t)0x01;
+    }
+    else
+    {
+        HSEStatus = (uint32_t)0x00;
+    }
+
+    if(HSEStatus == (uint32_t)0x01)
+    {
+        /* Enable Prefetch Buffer */
+        FLASH->ACTLR |= FLASH_ACTLR_PRFTBE;
+
+        /* Flash 2 wait state */
+        FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
+        FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_2;
+
+        /* HCLK = SYSCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+        /* PCLK2 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+        /* PCLK1 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+        /* PLL configuration: PLLCLK = HSE * 7 = 56 MHz */
+        RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+        RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLMULL7);
+        /* Enable PLL */
+        RCC->CTLR |= RCC_PLLON;
+        /* Wait till PLL is ready */
+        while((RCC->CTLR & RCC_PLLRDY) == 0)
+        {
+        }
+
+        /* Select PLL as system clock source */
+        RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
+        RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+        /* Wait till PLL is used as system clock source */
+        while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+        {
+        }
+    }
+    else
+    {
+        /*
+         * If HSE fails to start-up, the application will have wrong clock
+         * configuration. User can add here some code to deal with this error
+         */
+    }
+}
+
+#elif defined SYSCLK_FREQ_72MHz_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockTo72_HSE
+ *
+ * @brief   Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo72_HSE(void)
+{
+    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+    RCC->CTLR |= ((uint32_t)RCC_HSEON);
+
+    /* Wait till HSE is ready and if Time out is reached exit */
+    do
+    {
+        HSEStatus = RCC->CTLR & RCC_HSERDY;
+        StartUpCounter++;
+    } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+    if((RCC->CTLR & RCC_HSERDY) != RESET)
+    {
+        HSEStatus = (uint32_t)0x01;
+    }
+    else
+    {
+        HSEStatus = (uint32_t)0x00;
+    }
+
+    if(HSEStatus == (uint32_t)0x01)
+    {
+        /* Enable Prefetch Buffer */
+        FLASH->ACTLR |= FLASH_ACTLR_PRFTBE;
+
+        /* Flash 2 wait state */
+        FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
+        FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_2;
+
+        /* HCLK = SYSCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+        /* PCLK2 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+        /* PCLK1 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+        /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
+        RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE |
+                                              RCC_PLLMULL));
+        RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLMULL9);
+        /* Enable PLL */
+        RCC->CTLR |= RCC_PLLON;
+        /* Wait till PLL is ready */
+        while((RCC->CTLR & RCC_PLLRDY) == 0)
+        {
+        }
+        /* Select PLL as system clock source */
+        RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
+        RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+        /* Wait till PLL is used as system clock source */
+        while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+        {
+        }
+    }
+    else
+    {
+        /*
+         * If HSE fails to start-up, the application will have wrong clock
+         * configuration. User can add here some code to deal with this error
+         */
+    }
+}
+
+#elif defined SYSCLK_FREQ_48MHz_HSI
+
+/*********************************************************************
+ * @fn      SetSysClockTo48_HSI
+ *
+ * @brief   Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo48_HSI(void)
+{
+    EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
+
+    /* Enable Prefetch Buffer */
+    FLASH->ACTLR |= FLASH_ACTLR_PRFTBE;
+
+    /* Flash 1 wait state */
+    FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
+    FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1;
+
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  PLL configuration: PLLCLK = HSI * 6 = 48 MHz */
+    RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+    RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL6);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+}
+
+#elif defined SYSCLK_FREQ_56MHz_HSI
+
+/*********************************************************************
+ * @fn      SetSysClockTo56_HSI
+ *
+ * @brief   Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo56_HSI(void)
+{
+    EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
+
+    /* Enable Prefetch Buffer */
+    FLASH->ACTLR |= FLASH_ACTLR_PRFTBE;
+
+    /* Flash 1 wait state */
+    FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
+    FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1;
+
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  PLL configuration: PLLCLK = HSI * 7 = 56 MHz */
+    RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+    RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL7);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+}
+
+#elif defined SYSCLK_FREQ_72MHz_HSI
+
+/*********************************************************************
+ * @fn      SetSysClockTo72_HSI
+ *
+ * @brief   Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo72_HSI(void)
+{
+    EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
+
+    /* Enable Prefetch Buffer */
+    FLASH->ACTLR |= FLASH_ACTLR_PRFTBE;
+
+    /* Flash 1 wait state */
+    FLASH->ACTLR &= (uint32_t)((uint32_t)~FLASH_ACTLR_LATENCY);
+    FLASH->ACTLR |= (uint32_t)FLASH_ACTLR_LATENCY_1;
+
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  PLL configuration: PLLCLK = HSI * 9 = 72 MHz */
+    RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+    RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL9);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+}
+
+#endif
diff --git a/hw/bsp/ch32v10x/system_ch32v10x.h b/hw/bsp/ch32v10x/system_ch32v10x.h
new file mode 100644
index 000000000..d15624520
--- /dev/null
+++ b/hw/bsp/ch32v10x/system_ch32v10x.h
@@ -0,0 +1,29 @@
+/********************************** (C) COPYRIGHT *******************************
+ * File Name          : system_ch32v10x.h
+ * Author             : WCH
+ * Version            : V1.0.0
+ * Date               : 2020/04/30
+ * Description        : CH32V10x Device Peripheral Access Layer System Header File.
+*********************************************************************************
+* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
+* Attention: This software (modified or not) and binary are used for
+* microcontroller manufactured by Nanjing Qinheng Microelectronics.
+*******************************************************************************/
+#ifndef __SYSTEM_CH32V10x_H
+#define __SYSTEM_CH32V10x_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern uint32_t SystemCoreClock; /* System Clock Frequency (Core Clock) */
+
+/* System_Exported_Functions */
+extern void SystemInit(void);
+extern void SystemCoreClockUpdate(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__CH32V10x_SYSTEM_H */
diff --git a/hw/bsp/ch32v10x/wch-riscv.cfg b/hw/bsp/ch32v10x/wch-riscv.cfg
new file mode 100644
index 000000000..aa35aa9c5
--- /dev/null
+++ b/hw/bsp/ch32v10x/wch-riscv.cfg
@@ -0,0 +1,17 @@
+adapter driver wlinke
+adapter speed 6000
+transport select sdi
+
+wlink_set_address 0x00000000
+set _CHIPNAME wch_riscv
+sdi newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure  -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1
+set _FLASHNAME $_CHIPNAME.flash
+
+flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0
+
+echo "Ready for Remote Connections"
diff --git a/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.cmake b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.cmake
new file mode 100644
index 000000000..4aae6bdc2
--- /dev/null
+++ b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.cmake
@@ -0,0 +1,15 @@
+set(MCU_VARIANT D6)
+
+# 64KB zero-wait, 224KB total flash
+#set(LD_FLASH_SIZE 64K)
+set(LD_FLASH_SIZE 224K)
+set(LD_RAM_SIZE 20K)
+
+# set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/../../linker/${CH32_FAMILY}_tinyuf2.ld)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    SYSCLK_FREQ_144MHz_HSE=144000000
+    CFG_EXAMPLE_MSC_DUAL_READONLY
+    )
+endfunction()
diff --git a/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.h b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.h
new file mode 100644
index 000000000..692cf11bf
--- /dev/null
+++ b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.h
@@ -0,0 +1,21 @@
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LED_PORT       GPIOA
+#define LED_PIN        GPIO_Pin_0
+#define LED_STATE_ON   0
+
+#define UART_DEV        USART1
+#define UART_CLOCK_EN() RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE)
+#define UART_TX_PIN     GPIO_Pin_9
+#define UART_RX_PIN     GPIO_Pin_10
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.mk b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.mk
new file mode 100644
index 000000000..bdd15f737
--- /dev/null
+++ b/hw/bsp/ch32v20x/boards/ch32v203c_r0_1v0/board.mk
@@ -0,0 +1,11 @@
+MCU_VARIANT = D6
+
+CFLAGS += \
+  -DSYSCLK_FREQ_144MHz_HSE=144000000 \
+  -DCH32_FLASH_ENHANCE_READ_MODE=1 \
+  -DCFG_EXAMPLE_MSC_DUAL_READONLY \
+
+# 64KB zero-wait, 224KB total flash
+LDFLAGS += \
+  -Wl,--defsym=__FLASH_SIZE=224K \
+  -Wl,--defsym=__RAM_SIZE=20K \
diff --git a/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake
new file mode 100644
index 000000000..ecb8b378f
--- /dev/null
+++ b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.cmake
@@ -0,0 +1,13 @@
+set(MCU_VARIANT D6)
+
+# 32KB zero-wait, 224KB total flash
+#set(LD_FLASH_SIZE 32K)
+set(LD_FLASH_SIZE 224K)
+set(LD_RAM_SIZE 10K)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    SYSCLK_FREQ_144MHz_HSI=144000000
+    CFG_EXAMPLE_MSC_DUAL_READONLY
+    )
+endfunction()
diff --git a/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.h b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.h
new file mode 100644
index 000000000..783831edd
--- /dev/null
+++ b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.h
@@ -0,0 +1,21 @@
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LED_PORT       GPIOA
+#define LED_PIN        GPIO_Pin_0
+#define LED_STATE_ON   0
+
+#define UART_DEV        USART2
+#define UART_CLOCK_EN() RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE)
+#define UART_TX_PIN     GPIO_Pin_2
+#define UART_RX_PIN     GPIO_Pin_3
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.mk b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.mk
new file mode 100644
index 000000000..f71f53478
--- /dev/null
+++ b/hw/bsp/ch32v20x/boards/ch32v203g_r0_1v0/board.mk
@@ -0,0 +1,11 @@
+MCU_VARIANT = D6
+
+CFLAGS += \
+  -DSYSCLK_FREQ_144MHz_HSI=144000000 \
+  -DCH32_FLASH_ENHANCE_READ_MODE=1 \
+  -DCFG_EXAMPLE_MSC_DUAL_READONLY \
+
+# 32KB zero-wait, 224KB total flash
+LDFLAGS += \
+  -Wl,--defsym=__FLASH_SIZE=224K \
+  -Wl,--defsym=__RAM_SIZE=10K \
diff --git a/hw/bsp/ch32v20x/boards/nanoch32v203/board.cmake b/hw/bsp/ch32v20x/boards/nanoch32v203/board.cmake
new file mode 100644
index 000000000..a0bf12b5c
--- /dev/null
+++ b/hw/bsp/ch32v20x/boards/nanoch32v203/board.cmake
@@ -0,0 +1,13 @@
+set(MCU_VARIANT D6)
+
+# 64KB zero-wait, 224KB total flash
+#set(LD_FLASH_SIZE 64K)
+set(LD_FLASH_SIZE 224K)
+set(LD_RAM_SIZE 20K)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    SYSCLK_FREQ_144MHz_HSE=144000000
+    CFG_EXAMPLE_MSC_DUAL_READONLY
+    )
+endfunction()
diff --git a/hw/bsp/ch32v20x/boards/nanoch32v203/board.h b/hw/bsp/ch32v20x/boards/nanoch32v203/board.h
new file mode 100644
index 000000000..64eaf931e
--- /dev/null
+++ b/hw/bsp/ch32v20x/boards/nanoch32v203/board.h
@@ -0,0 +1,21 @@
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LED_PORT       GPIOA
+#define LED_PIN        GPIO_Pin_15
+#define LED_STATE_ON   0
+
+#define UART_DEV        USART1
+#define UART_CLOCK_EN() RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE)
+#define UART_TX_PIN     GPIO_Pin_9
+#define UART_RX_PIN     GPIO_Pin_10
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/hw/bsp/ch32v20x/boards/nanoch32v203/board.mk b/hw/bsp/ch32v20x/boards/nanoch32v203/board.mk
new file mode 100644
index 000000000..362aace47
--- /dev/null
+++ b/hw/bsp/ch32v20x/boards/nanoch32v203/board.mk
@@ -0,0 +1,11 @@
+MCU_VARIANT = D6
+
+CFLAGS += \
+  -DSYSCLK_FREQ_144MHz_HSE=144000000 \
+	-DCH32_FLASH_ENHANCE_READ_MODE=1 \
+  -DCFG_EXAMPLE_MSC_DUAL_READONLY \
+
+# 64KB zero-wait , 224KB total flash
+LDFLAGS += \
+  -Wl,--defsym=__FLASH_SIZE=224K \
+  -Wl,--defsym=__RAM_SIZE=20K \
diff --git a/hw/bsp/ch32v20x/ch32v20x_conf.h b/hw/bsp/ch32v20x/ch32v20x_conf.h
new file mode 100644
index 000000000..949297fe6
--- /dev/null
+++ b/hw/bsp/ch32v20x/ch32v20x_conf.h
@@ -0,0 +1,36 @@
+/********************************** (C) COPYRIGHT *******************************
+ * File Name          : ch32v20x_conf.h
+ * Author             : WCH
+ * Version            : V1.0.0
+ * Date               : 2021/06/06
+ * Description        : Library configuration file.
+*********************************************************************************
+* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
+* Attention: This software (modified or not) and binary are used for
+* microcontroller manufactured by Nanjing Qinheng Microelectronics.
+*******************************************************************************/
+#ifndef __CH32V20x_CONF_H
+#define __CH32V20x_CONF_H
+
+#include "ch32v20x_adc.h"
+#include "ch32v20x_bkp.h"
+#include "ch32v20x_can.h"
+#include "ch32v20x_crc.h"
+#include "ch32v20x_dbgmcu.h"
+#include "ch32v20x_dma.h"
+#include "ch32v20x_exti.h"
+#include "ch32v20x_flash.h"
+#include "ch32v20x_gpio.h"
+#include "ch32v20x_i2c.h"
+#include "ch32v20x_iwdg.h"
+#include "ch32v20x_pwr.h"
+#include "ch32v20x_rcc.h"
+#include "ch32v20x_rtc.h"
+#include "ch32v20x_spi.h"
+#include "ch32v20x_tim.h"
+#include "ch32v20x_usart.h"
+#include "ch32v20x_wwdg.h"
+#include "ch32v20x_it.h"
+#include "ch32v20x_misc.h"
+
+#endif /* __CH32V20x_CONF_H */
diff --git a/hw/bsp/ch32v20x/ch32v20x_it.h b/hw/bsp/ch32v20x/ch32v20x_it.h
new file mode 100644
index 000000000..e49c61ae2
--- /dev/null
+++ b/hw/bsp/ch32v20x/ch32v20x_it.h
@@ -0,0 +1,15 @@
+/********************************** (C) COPYRIGHT *******************************
+ * File Name          : ch32v20x_it.h
+ * Author             : WCH
+ * Version            : V1.0.0
+ * Date               : 2021/06/06
+ * Description        : This file contains the headers of the interrupt handlers.
+*********************************************************************************
+* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
+* Attention: This software (modified or not) and binary are used for
+* microcontroller manufactured by Nanjing Qinheng Microelectronics.
+*******************************************************************************/
+#ifndef __CH32V20x_IT_H
+#define __CH32V20x_IT_H
+
+#endif /* __CH32V20x_IT_H */
diff --git a/hw/bsp/ch32v20x/family.c b/hw/bsp/ch32v20x/family.c
new file mode 100644
index 000000000..43dd7e032
--- /dev/null
+++ b/hw/bsp/ch32v20x/family.c
@@ -0,0 +1,209 @@
+#include 
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#include "ch32v20x.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#include "bsp/board_api.h"
+#include "board.h"
+
+/* CH32v203 depending on variants can support 2 USB IPs: FSDEV and USBFS.
+ * By default, we use FSDEV, but you can explicitly select by define:
+ * - CFG_TUD_WCH_USBIP_FSDEV
+ * - CFG_TUD_WCH_USBIP_USBFS
+ */
+
+// USBFS
+__attribute__((interrupt)) __attribute__((used))
+void USBHD_IRQHandler(void) {
+  #if CFG_TUD_WCH_USBIP_USBFS
+  tud_int_handler(0);
+  #endif
+}
+
+__attribute__((interrupt)) __attribute__((used))
+void USBHDWakeUp_IRQHandler(void) {
+  #if CFG_TUD_WCH_USBIP_USBFS
+  tud_int_handler(0);
+  #endif
+}
+
+// USBD (fsdev)
+__attribute__((interrupt)) __attribute__((used))
+void USB_LP_CAN1_RX0_IRQHandler(void) {
+  #if CFG_TUD_WCH_USBIP_FSDEV
+  tud_int_handler(0);
+  #endif
+}
+
+__attribute__((interrupt)) __attribute__((used))
+void USB_HP_CAN1_TX_IRQHandler(void) {
+  #if CFG_TUD_WCH_USBIP_FSDEV
+  tud_int_handler(0);
+  #endif
+
+}
+
+__attribute__((interrupt)) __attribute__((used))
+void USBWakeUp_IRQHandler(void) {
+  #if CFG_TUD_WCH_USBIP_FSDEV
+  tud_int_handler(0);
+  #endif
+}
+
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+volatile uint32_t system_ticks = 0;
+
+__attribute__((interrupt))
+void SysTick_Handler(void) {
+  SysTick->SR = 0;
+  system_ticks++;
+}
+
+uint32_t SysTick_Config(uint32_t ticks) {
+  NVIC_EnableIRQ(SysTicK_IRQn);
+  SysTick->CTLR = 0;
+  SysTick->SR = 0;
+  SysTick->CNT = 0;
+  SysTick->CMP = ticks - 1;
+  SysTick->CTLR = 0xF;
+  return 0;
+}
+
+uint32_t board_millis(void) {
+  return system_ticks;
+}
+#endif
+
+void board_init(void) {
+  __disable_irq();
+
+#if CFG_TUSB_OS == OPT_OS_NONE
+  SysTick_Config(SystemCoreClock / 1000);
+#endif
+
+  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
+
+  GPIO_InitTypeDef GPIO_InitStructure = {
+    .GPIO_Pin = LED_PIN,
+    .GPIO_Mode = GPIO_Mode_Out_OD,
+    .GPIO_Speed = GPIO_Speed_10MHz,
+  };
+  GPIO_Init(LED_PORT, &GPIO_InitStructure);
+
+#ifdef UART_DEV
+  UART_CLOCK_EN();
+  GPIO_InitTypeDef usart_init = {
+    .GPIO_Pin = UART_TX_PIN,
+    .GPIO_Speed = GPIO_Speed_50MHz,
+    .GPIO_Mode = GPIO_Mode_AF_PP,
+  };
+  GPIO_Init(GPIOA, &usart_init);
+
+  USART_InitTypeDef usart = {
+    .USART_BaudRate = 115200,
+    .USART_WordLength = USART_WordLength_8b,
+    .USART_StopBits = USART_StopBits_1,
+    .USART_Parity = USART_Parity_No,
+    .USART_Mode = USART_Mode_Tx,
+    .USART_HardwareFlowControl = USART_HardwareFlowControl_None,
+  };
+  USART_Init(UART_DEV, &usart);
+  USART_Cmd(UART_DEV, ENABLE);
+#endif
+
+  // USB init
+  uint8_t usb_div;
+  switch (SystemCoreClock) {
+    case 48000000: usb_div = RCC_USBCLKSource_PLLCLK_Div1; break;
+    case 96000000: usb_div = RCC_USBCLKSource_PLLCLK_Div2; break;
+    case 144000000: usb_div = RCC_USBCLKSource_PLLCLK_Div3; break;
+    default: TU_ASSERT(0,); break;
+  }
+  RCC_USBCLKConfig(usb_div);
+  RCC_APB1PeriphClockCmd(RCC_APB1Periph_USB, ENABLE);  // FSDEV
+  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS, ENABLE); // USB FS
+
+  __enable_irq();
+}
+
+void board_reset_to_bootloader(void) {
+//   board_led_write(true);
+//
+//   __disable_irq();
+//
+// #if CFG_TUD_ENABLED
+//   tud_deinit(0);
+//   RCC_APB1PeriphResetCmd(RCC_APB1Periph_USB, ENABLE);
+//   RCC_APB1PeriphResetCmd(RCC_APB1Periph_USB, DISABLE);
+// #endif
+//
+//   SysTick->CTLR = 0;
+//   for (int i = WWDG_IRQn; i< DMA1_Channel8_IRQn; i++) {
+//     NVIC_DisableIRQ(i);
+//   }
+//
+//   __enable_irq();
+//
+//   // define function pointer to BOOT ROM address
+//   void (*bootloader_entry)(void) = (void (*)(void))0x1FFF8000;
+//
+//   bootloader_entry();
+//
+//   board_led_write(false);
+
+  // while(1) { }
+}
+
+void board_led_write(bool state) {
+  GPIO_WriteBit(LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+}
+
+uint32_t board_button_read(void) {
+  return false;
+}
+
+size_t board_get_unique_id(uint8_t id[], size_t max_len) {
+  (void) max_len;
+  volatile uint32_t* ch32_uuid = ((volatile uint32_t*) 0x1FFFF7E8UL);
+  uint32_t* serial_32 = (uint32_t*) (uintptr_t) id;
+  serial_32[0] = ch32_uuid[0];
+  serial_32[1] = ch32_uuid[1];
+  serial_32[2] = ch32_uuid[2];
+
+  return 12;
+}
+
+int board_uart_read(uint8_t *buf, int len) {
+  (void) buf;
+  (void) len;
+  return 0;
+}
+
+int board_uart_write(void const *buf, int len) {
+#ifdef UART_DEV
+  const char *bufc = (const char *) buf;
+  for (int i = 0; i < len; i++) {
+    while (USART_GetFlagStatus(UART_DEV, USART_FLAG_TC) == RESET);
+    USART_SendData(UART_DEV, *bufc++);
+  }
+#else
+  (void) buf; (void) len;
+#endif
+
+  return len;
+}
+
+//--------------------------------------------------------------------
+// Neopixel
+//--------------------------------------------------------------------
diff --git a/hw/bsp/ch32v20x/family.cmake b/hw/bsp/ch32v20x/family.cmake
new file mode 100644
index 000000000..380ef190d
--- /dev/null
+++ b/hw/bsp/ch32v20x/family.cmake
@@ -0,0 +1,144 @@
+include_guard()
+
+set(UF2_FAMILY_ID 0x699b62ec)
+set(CH32_FAMILY ch32v20x)
+set(SDK_DIR ${TOP}/hw/mcu/wch/${CH32_FAMILY})
+set(SDK_SRC_DIR ${SDK_DIR}/EVT/EXAM/SRC)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR rv32imac-ilp32 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/riscv_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS CH32V20X CACHE INTERNAL "")
+set(OPENOCD_OPTION "-f ${CMAKE_CURRENT_LIST_DIR}/wch-riscv.cfg")
+
+# Port0 use FSDev, Port1 use USBFS
+if (NOT DEFINED PORT)
+  set(PORT 0)
+endif()
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/linker/${CH32_FAMILY}.ld)
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  if (NOT DEFINED STARTUP_FILE_GNU)
+    set(STARTUP_FILE_GNU ${SDK_SRC_DIR}/Startup/startup_${CH32_FAMILY}_${MCU_VARIANT}.S)
+  endif ()
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_SRC_DIR}/Core/core_riscv.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_flash.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_gpio.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_misc.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_rcc.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_usart.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/system_${CH32_FAMILY}.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_SRC_DIR}/Core
+    ${SDK_SRC_DIR}/Peripheral/inc
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    CH32V20x_${MCU_VARIANT}
+    )
+
+  if (PORT EQUAL 0)
+    target_compile_definitions(${BOARD_TARGET} PUBLIC
+      CFG_TUD_WCH_USBIP_FSDEV=1
+      )
+  elseif (PORT EQUAL 1)
+    target_compile_definitions(${BOARD_TARGET} PUBLIC
+      CFG_TUD_WCH_USBIP_USBFS=1
+      )
+  else()
+    message(FATAL_ERROR "Invalid PORT ${PORT}")
+  endif()
+
+  update_board(${BOARD_TARGET})
+
+  if (LD_FLASH_SIZE STREQUAL 224K)
+    target_compile_definitions(${BOARD_TARGET} PUBLIC
+      CH32_FLASH_ENHANCE_READ_MODE=1
+      )
+  endif()
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(${BOARD_TARGET} PUBLIC
+      -mcmodel=medany
+      )
+    target_link_options(${BOARD_TARGET} PUBLIC
+      -nostartfiles
+      --specs=nosys.specs --specs=nano.specs
+      -Wl,--defsym=__FLASH_SIZE=${LD_FLASH_SIZE}
+      -Wl,--defsym=__RAM_SIZE=${LD_RAM_SIZE}
+      "LINKER:--script=${LD_FILE_GNU}"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    message(FATAL_ERROR "Clang is not supported for CH32v")
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_CH32V20X ${RTOS})
+
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/wch/dcd_ch32_usbfs.c
+    ${TOP}/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_add_bin_hex(${TARGET})
+  family_flash_openocd_wch(${TARGET})
+  family_flash_wlink_rs(${TARGET})
+
+  #family_add_uf2(${TARGET} ${UF2_FAMILY_ID})
+  #family_flash_uf2(${TARGET} ${UF2_FAMILY_ID})
+endfunction()
diff --git a/hw/bsp/ch32v20x/family.mk b/hw/bsp/ch32v20x/family.mk
new file mode 100644
index 000000000..08761dc0d
--- /dev/null
+++ b/hw/bsp/ch32v20x/family.mk
@@ -0,0 +1,64 @@
+# https://www.embecosm.com/resources/tool-chain-downloads/#riscv-stable
+#CROSS_COMPILE ?= riscv32-unknown-elf-
+
+# Toolchain from https://nucleisys.com/download.php
+#CROSS_COMPILE ?= riscv-nuclei-elf-
+
+# Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
+CROSS_COMPILE ?= riscv-none-elf-
+
+CH32_FAMILY = ch32v20x
+SDK_DIR = hw/mcu/wch/ch32v20x
+SDK_SRC_DIR = $(SDK_DIR)/EVT/EXAM/SRC
+
+include $(TOP)/$(BOARD_PATH)/board.mk
+CPU_CORE ?= rv32imac-ilp32
+
+# Port0 use FSDev, Port1 use USBFS
+PORT ?= 0
+
+CFLAGS += \
+	-mcmodel=medany \
+	-ffat-lto-objects \
+	-flto \
+	-DCH32V20x_${MCU_VARIANT} \
+	-DCFG_TUSB_MCU=OPT_MCU_CH32V20X
+
+# https://github.com/openwch/ch32v20x/pull/12
+CFLAGS += -Wno-error=strict-prototypes
+
+ifeq ($(PORT),0)
+  $(info "Using FSDEV driver")
+  CFLAGS += -DCFG_TUD_WCH_USBIP_FSDEV=1
+else
+  $(info "Using USBFS driver")
+  CFLAGS += -DCFG_TUD_WCH_USBIP_USBFS=1
+endif
+
+LDFLAGS_GCC += \
+	-nostdlib -nostartfiles \
+	--specs=nosys.specs --specs=nano.specs \
+
+LD_FILE = $(FAMILY_PATH)/linker/${CH32_FAMILY}.ld
+
+SRC_C += \
+	src/portable/wch/dcd_ch32_usbfs.c \
+	src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c \
+	$(SDK_SRC_DIR)/Core/core_riscv.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_gpio.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_misc.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_rcc.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_usart.c \
+
+SRC_S += $(SDK_SRC_DIR)/Startup/startup_${CH32_FAMILY}_${MCU_VARIANT}.S
+
+INC += \
+	$(TOP)/$(BOARD_PATH) \
+	$(TOP)/$(SDK_SRC_DIR)/Core \
+	$(TOP)/$(SDK_SRC_DIR)/Peripheral/inc \
+
+FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V
+
+OPENOCD_WCH_OPTION=-f $(TOP)/$(FAMILY_PATH)/wch-riscv.cfg
+flash: flash-wlink-rs
+#flash: flash-openocd-wch
diff --git a/hw/bsp/ch32v20x/linker/ch32v20x.ld b/hw/bsp/ch32v20x/linker/ch32v20x.ld
new file mode 100644
index 000000000..f84808b0d
--- /dev/null
+++ b/hw/bsp/ch32v20x/linker/ch32v20x.ld
@@ -0,0 +1,164 @@
+/* Define default values if not already defined */
+__flash_size = DEFINED(__FLASH_SIZE) ? __FLASH_SIZE : 64K;
+__ram_size = DEFINED(__RAM_SIZE) ? __RAM_SIZE : 20K;
+__stack_size = DEFINED(__STACK_SIZE) ? __STACK_SIZE : 2048;
+
+MEMORY
+{
+	FLASH (rx) : ORIGIN = 0x00000000, LENGTH = __flash_size
+	RAM (xrw) : ORIGIN = 0x20000000, LENGTH = __ram_size
+}
+
+ENTRY( _start )
+
+PROVIDE( _stack_size = __stack_size );
+
+SECTIONS
+{
+	.init :
+	{
+		_sinit = .;
+		. = ALIGN(4);
+		KEEP(*(SORT_NONE(.init)))
+		. = ALIGN(4);
+		_einit = .;
+	} >FLASH AT>FLASH
+
+  .vector :
+  {
+      *(.vector);
+	  . = ALIGN(64);
+  } >FLASH AT>FLASH
+
+	.text :
+	{
+		. = ALIGN(4);
+		*(.text)
+		*(.text.*)
+		*(.rodata)
+		*(.rodata*)
+		*(.gnu.linkonce.t.*)
+		. = ALIGN(4);
+	} >FLASH AT>FLASH
+
+	.fini :
+	{
+		KEEP(*(SORT_NONE(.fini)))
+		. = ALIGN(4);
+	} >FLASH AT>FLASH
+
+	PROVIDE( _etext = . );
+	PROVIDE( _eitcm = . );
+
+	.preinit_array  :
+	{
+	  PROVIDE_HIDDEN (__preinit_array_start = .);
+	  KEEP (*(.preinit_array))
+	  PROVIDE_HIDDEN (__preinit_array_end = .);
+	} >FLASH AT>FLASH
+
+	.init_array     :
+	{
+	  PROVIDE_HIDDEN (__init_array_start = .);
+	  KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*)))
+	  KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors))
+	  PROVIDE_HIDDEN (__init_array_end = .);
+	} >FLASH AT>FLASH
+
+	.fini_array     :
+	{
+	  PROVIDE_HIDDEN (__fini_array_start = .);
+	  KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*)))
+	  KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors))
+	  PROVIDE_HIDDEN (__fini_array_end = .);
+	} >FLASH AT>FLASH
+
+	.ctors          :
+	{
+	  /* gcc uses crtbegin.o to find the start of
+	     the constructors, so we make sure it is
+	     first.  Because this is a wildcard, it
+	     doesn't matter if the user does not
+	     actually link against crtbegin.o; the
+	     linker won't look for a file to match a
+	     wildcard.  The wildcard also means that it
+	     doesn't matter which directory crtbegin.o
+	     is in.  */
+	  KEEP (*crtbegin.o(.ctors))
+	  KEEP (*crtbegin?.o(.ctors))
+	  /* We don't want to include the .ctor section from
+	     the crtend.o file until after the sorted ctors.
+	     The .ctor section from the crtend file contains the
+	     end of ctors marker and it must be last */
+	  KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
+	  KEEP (*(SORT(.ctors.*)))
+	  KEEP (*(.ctors))
+	} >FLASH AT>FLASH
+
+	.dtors          :
+	{
+	  KEEP (*crtbegin.o(.dtors))
+	  KEEP (*crtbegin?.o(.dtors))
+	  KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
+	  KEEP (*(SORT(.dtors.*)))
+	  KEEP (*(.dtors))
+	} >FLASH AT>FLASH
+
+	.dalign :
+	{
+		. = ALIGN(4);
+		PROVIDE(_data_vma = .);
+	} >RAM AT>FLASH
+
+	.dlalign :
+	{
+		. = ALIGN(4);
+		PROVIDE(_data_lma = .);
+	} >FLASH AT>FLASH
+
+	.data :
+	{
+    	*(.gnu.linkonce.r.*)
+    	*(.data .data.*)
+    	*(.gnu.linkonce.d.*)
+		. = ALIGN(8);
+    	PROVIDE( __global_pointer$ = . + 0x800 );
+    	*(.sdata .sdata.*)
+		*(.sdata2.*)
+    	*(.gnu.linkonce.s.*)
+    	. = ALIGN(8);
+    	*(.srodata.cst16)
+    	*(.srodata.cst8)
+    	*(.srodata.cst4)
+    	*(.srodata.cst2)
+    	*(.srodata .srodata.*)
+    	. = ALIGN(4);
+		PROVIDE( _edata = .);
+	} >RAM AT>FLASH
+
+	.bss :
+	{
+		. = ALIGN(4);
+		PROVIDE( _sbss = .);
+  	    *(.sbss*)
+        *(.gnu.linkonce.sb.*)
+		*(.bss*)
+     	*(.gnu.linkonce.b.*)
+		*(COMMON*)
+		. = ALIGN(4);
+		PROVIDE( _ebss = .);
+	} >RAM AT>FLASH
+
+	PROVIDE( _end = _ebss);
+	PROVIDE( end = . );
+
+    .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :
+    {
+        PROVIDE( _heap_end = . );
+        . = ALIGN(4);
+        PROVIDE(_susrstack = . );
+        . = . + __stack_size;
+        PROVIDE( _eusrstack = .);
+    } >RAM
+
+}
diff --git a/hw/bsp/ch32v20x/system_ch32v20x.c b/hw/bsp/ch32v20x/system_ch32v20x.c
new file mode 100644
index 000000000..d32ee8d17
--- /dev/null
+++ b/hw/bsp/ch32v20x/system_ch32v20x.c
@@ -0,0 +1,986 @@
+/********************************** (C) COPYRIGHT *******************************
+ * File Name          : system_ch32v20x.c
+ * Author             : WCH
+ * Version            : V1.0.0
+ * Date               : 2021/06/06
+ * Description        : CH32V20x Device Peripheral Access Layer System Source File.
+ *                      For HSE = 32Mhz (CH32V208x/CH32V203RBT6)
+ *                      For HSE = 8Mhz (other CH32V203x)
+*********************************************************************************
+* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
+* Attention: This software (modified or not) and binary are used for
+* microcontroller manufactured by Nanjing Qinheng Microelectronics.
+*******************************************************************************/
+#include "ch32v20x.h"
+
+/*
+* Uncomment the line corresponding to the desired System clock (SYSCLK) frequency (after
+* reset the HSI is used as SYSCLK source).
+* If none of the define below is enabled, the HSI is used as System clock source.
+*/
+//#define SYSCLK_FREQ_HSE    HSE_VALUE
+// #define SYSCLK_FREQ_48MHz_HSE  48000000
+//#define SYSCLK_FREQ_56MHz_HSE  56000000
+// #define SYSCLK_FREQ_72MHz_HSE  72000000
+// #define SYSCLK_FREQ_96MHz_HSE  96000000
+//#define SYSCLK_FREQ_120MHz_HSE  120000000
+//#define SYSCLK_FREQ_144MHz_HSE  144000000
+//#define SYSCLK_FREQ_HSI    HSI_VALUE
+//#define SYSCLK_FREQ_48MHz_HSI  48000000
+//#define SYSCLK_FREQ_56MHz_HSI  56000000
+//#define SYSCLK_FREQ_72MHz_HSI  72000000
+//#define SYSCLK_FREQ_96MHz_HSI  96000000
+//#define SYSCLK_FREQ_120MHz_HSI  120000000
+//#define SYSCLK_FREQ_144MHz_HSI  144000000
+
+/* Clock Definitions */
+#ifdef SYSCLK_FREQ_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_HSE;              /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_48MHz_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_48MHz_HSE;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_56MHz_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_56MHz_HSE;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_72MHz_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz_HSE;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_96MHz_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_96MHz_HSE;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_120MHz_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_120MHz_HSE;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_144MHz_HSE
+uint32_t SystemCoreClock         = SYSCLK_FREQ_144MHz_HSE;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_48MHz_HSI
+uint32_t SystemCoreClock         = SYSCLK_FREQ_48MHz_HSI;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_56MHz_HSI
+uint32_t SystemCoreClock         = SYSCLK_FREQ_56MHz_HSI;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_72MHz_HSI
+uint32_t SystemCoreClock         = SYSCLK_FREQ_72MHz_HSI;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_96MHz_HSI
+uint32_t SystemCoreClock         = SYSCLK_FREQ_96MHz_HSI;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_120MHz_HSI
+uint32_t SystemCoreClock         = SYSCLK_FREQ_120MHz_HSI;        /* System Clock Frequency (Core Clock) */
+#elif defined SYSCLK_FREQ_144MHz_HSI
+uint32_t SystemCoreClock         = SYSCLK_FREQ_144MHz_HSI;        /* System Clock Frequency (Core Clock) */
+#else
+uint32_t SystemCoreClock         = HSI_VALUE;                    /* System Clock Frequency (Core Clock) */
+
+#endif
+
+__I uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
+
+/* system_private_function_proto_types */
+static void SetSysClock(void);
+
+#ifdef SYSCLK_FREQ_HSE
+static void SetSysClockToHSE( void );
+#elif defined SYSCLK_FREQ_48MHz_HSE
+static void SetSysClockTo48_HSE( void );
+#elif defined SYSCLK_FREQ_56MHz_HSE
+static void SetSysClockTo56_HSE( void );
+#elif defined SYSCLK_FREQ_72MHz_HSE
+static void SetSysClockTo72_HSE( void );
+#elif defined SYSCLK_FREQ_96MHz_HSE
+static void SetSysClockTo96_HSE( void );
+#elif defined SYSCLK_FREQ_120MHz_HSE
+static void SetSysClockTo120_HSE( void );
+#elif defined SYSCLK_FREQ_144MHz_HSE
+static void SetSysClockTo144_HSE( void );
+#elif defined SYSCLK_FREQ_48MHz_HSI
+static void SetSysClockTo48_HSI( void );
+#elif defined SYSCLK_FREQ_56MHz_HSI
+static void SetSysClockTo56_HSI( void );
+#elif defined SYSCLK_FREQ_72MHz_HSI
+static void SetSysClockTo72_HSI( void );
+#elif defined SYSCLK_FREQ_96MHz_HSI
+static void SetSysClockTo96_HSI( void );
+#elif defined SYSCLK_FREQ_120MHz_HSI
+static void SetSysClockTo120_HSI( void );
+#elif defined SYSCLK_FREQ_144MHz_HSI
+static void SetSysClockTo144_HSI( void );
+
+#endif
+
+/*********************************************************************
+ * @fn      SystemInit
+ *
+ * @brief   Setup the microcontroller system Initialize the Embedded Flash Interface,
+ *        the PLL and update the SystemCoreClock variable.
+ *
+ * @return  none
+ */
+void SystemInit (void)
+{
+  // Enable Flash enhance read mode for full 224KB
+#if defined(CH32_FLASH_ENHANCE_READ_MODE) && CH32_FLASH_ENHANCE_READ_MODE == 1
+  FLASH->KEYR = 0x45670123; // FLASH_Unlock_Fast();
+  FLASH->KEYR = 0xCDEF89AB;
+
+  FLASH->CTLR |= (1 << 24); // Enhanced Read Mode
+
+  FLASH->CTLR |= (1 << 15); // FLASH_Lock_Fast();
+#endif
+
+  RCC->CTLR |= (uint32_t)0x00000001;
+  RCC->CFGR0 &= (uint32_t)0xF8FF0000;
+  RCC->CTLR &= (uint32_t)0xFEF6FFFF;
+  RCC->CTLR &= (uint32_t)0xFFFBFFFF;
+  RCC->CFGR0 &= (uint32_t)0xFF80FFFF;
+  RCC->INTR = 0x009F0000;
+  SetSysClock();
+}
+
+/*********************************************************************
+ * @fn      SystemCoreClockUpdate
+ *
+ * @brief   Update SystemCoreClock variable according to Clock Register Values.
+ *
+ * @return  none
+ */
+void SystemCoreClockUpdate (void)
+{
+  uint32_t tmp = 0, pllmull = 0, pllsource = 0, Pll_6_5 = 0;
+
+  tmp = RCC->CFGR0 & RCC_SWS;
+
+  switch (tmp)
+  {
+    case 0x00:
+      SystemCoreClock = HSI_VALUE;
+      break;
+    case 0x04:
+      SystemCoreClock = HSE_VALUE;
+      break;
+    case 0x08:
+      pllmull = RCC->CFGR0 & RCC_PLLMULL;
+      pllsource = RCC->CFGR0 & RCC_PLLSRC;
+      pllmull = ( pllmull >> 18) + 2;
+
+      if(pllmull == 17) pllmull = 18;
+
+      if (pllsource == 0x00)
+      {
+          if(EXTEN->EXTEN_CTR & EXTEN_PLL_HSI_PRE){
+              SystemCoreClock = HSI_VALUE * pllmull;
+          }
+          else{
+              SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
+          }
+      }
+      else
+      {
+#if defined (CH32V20x_D8W)
+        if((RCC->CFGR0 & (3<<22)) == (3<<22))
+        {
+          SystemCoreClock = ((HSE_VALUE>>1)) * pllmull;
+        }
+        else
+#endif
+        if ((RCC->CFGR0 & RCC_PLLXTPRE) != (uint32_t)RESET)
+        {
+#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
+          SystemCoreClock = ((HSE_VALUE>>2) >> 1) * pllmull;
+#else
+          SystemCoreClock = (HSE_VALUE >> 1) * pllmull;
+#endif
+        }
+        else
+        {
+#if defined (CH32V20x_D8) || defined (CH32V20x_D8W)
+            SystemCoreClock = (HSE_VALUE>>2) * pllmull;
+#else
+          SystemCoreClock = HSE_VALUE * pllmull;
+#endif
+        }
+      }
+
+      if(Pll_6_5 == 1) SystemCoreClock = (SystemCoreClock / 2);
+
+      break;
+    default:
+      SystemCoreClock = HSI_VALUE;
+      break;
+  }
+
+  tmp = AHBPrescTable[((RCC->CFGR0 & RCC_HPRE) >> 4)];
+  SystemCoreClock >>= tmp;
+}
+
+/*********************************************************************
+ * @fn      SetSysClock
+ *
+ * @brief   Configures the System clock frequency, HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClock(void)
+{
+#ifdef SYSCLK_FREQ_HSE
+    SetSysClockToHSE();
+#elif defined SYSCLK_FREQ_48MHz_HSE
+    SetSysClockTo48_HSE();
+#elif defined SYSCLK_FREQ_56MHz_HSE
+    SetSysClockTo56_HSE();
+#elif defined SYSCLK_FREQ_72MHz_HSE
+    SetSysClockTo72_HSE();
+#elif defined SYSCLK_FREQ_96MHz_HSE
+    SetSysClockTo96_HSE();
+#elif defined SYSCLK_FREQ_120MHz_HSE
+    SetSysClockTo120_HSE();
+#elif defined SYSCLK_FREQ_144MHz_HSE
+    SetSysClockTo144_HSE();
+#elif defined SYSCLK_FREQ_48MHz_HSI
+    SetSysClockTo48_HSI();
+#elif defined SYSCLK_FREQ_56MHz_HSI
+    SetSysClockTo56_HSI();
+#elif defined SYSCLK_FREQ_72MHz_HSI
+    SetSysClockTo72_HSI();
+#elif defined SYSCLK_FREQ_96MHz_HSI
+    SetSysClockTo96_HSI();
+#elif defined SYSCLK_FREQ_120MHz_HSI
+    SetSysClockTo120_HSI();
+#elif defined SYSCLK_FREQ_144MHz_HSI
+    SetSysClockTo144_HSI();
+
+#endif
+
+ /* If none of the define above is enabled, the HSI is used as System clock
+  * source (default after reset)
+	*/
+}
+
+#ifdef SYSCLK_FREQ_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockToHSE
+ *
+ * @brief   Sets HSE as System clock source and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockToHSE(void)
+{
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+  RCC->CTLR |= ((uint32_t)RCC_HSEON);
+
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CTLR & RCC_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CTLR & RCC_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV1;
+
+    /* Select HSE as system clock source
+     *  CH32V20x_D6 (HSE=8MHZ)
+     *  CH32V20x_D8 (HSE=32MHZ)
+     *  CH32V20x_D8W (HSE=32MHZ)
+     */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_HSE;
+
+    /* Wait till HSE is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x04)
+    {
+    }
+  }
+  else
+  {
+		/* If HSE fails to start-up, the application will have wrong clock
+     * configuration. User can add here some code to deal with this error
+		 */
+  }
+}
+
+#elif defined SYSCLK_FREQ_48MHz_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockTo48_HSE
+ *
+ * @brief   Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo48_HSE(void)
+{
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+  RCC->CTLR |= ((uint32_t)RCC_HSEON);
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CTLR & RCC_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CTLR & RCC_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  CH32V20x_D6-PLL configuration: PLLCLK = HSE * 6 = 48 MHz (HSE=8MHZ)
+     *  CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 6 = 48 MHz (HSE=32MHZ)
+     *  CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 6 = 48 MHz (HSE=32MHZ)
+     */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+
+     RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL6);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+  }
+  else
+  {
+		/*
+		 * If HSE fails to start-up, the application will have wrong clock
+     * configuration. User can add here some code to deal with this error
+		 */
+  }
+}
+
+#elif defined SYSCLK_FREQ_56MHz_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockTo56_HSE
+ *
+ * @brief   Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo56_HSE(void)
+{
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+  RCC->CTLR |= ((uint32_t)RCC_HSEON);
+
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CTLR & RCC_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CTLR & RCC_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  CH32V20x_D6-PLL configuration: PLLCLK = HSE * 7 = 56 MHz (HSE=8MHZ)
+     *  CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 7 = 56 MHz (HSE=32MHZ)
+     *  CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 7 = 56 MHz (HSE=32MHZ)
+     */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+
+    RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL7);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+  }
+  else
+  {
+		/*
+		 * If HSE fails to start-up, the application will have wrong clock
+     * configuration. User can add here some code to deal with this error
+		 */
+  }
+}
+
+#elif defined SYSCLK_FREQ_72MHz_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockTo72_HSE
+ *
+ * @brief   Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo72_HSE(void)
+{
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+  RCC->CTLR |= ((uint32_t)RCC_HSEON);
+
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CTLR & RCC_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CTLR & RCC_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  CH32V20x_D6-PLL configuration: PLLCLK = HSE * 9 = 72 MHz (HSE=8MHZ)
+     *  CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 9 = 72 MHz (HSE=32MHZ)
+     *  CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 9 = 72 MHz (HSE=32MHZ)
+     */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
+                                        RCC_PLLMULL));
+
+    RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL9);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+  }
+  else
+  {
+		/*
+		 * If HSE fails to start-up, the application will have wrong clock
+     * configuration. User can add here some code to deal with this error
+		 */
+  }
+}
+
+#elif defined SYSCLK_FREQ_96MHz_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockTo96_HSE
+ *
+ * @brief   Sets System clock frequency to 96MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo96_HSE(void)
+{
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+  RCC->CTLR |= ((uint32_t)RCC_HSEON);
+
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CTLR & RCC_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CTLR & RCC_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  CH32V20x_D6-PLL configuration: PLLCLK = HSE * 12 = 96 MHz (HSE=8MHZ)
+     *  CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 12 = 96 MHz (HSE=32MHZ)
+     *  CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 12 = 96 MHz (HSE=32MHZ)
+     */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
+                                        RCC_PLLMULL));
+
+    RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL12);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+  }
+  else
+  {
+        /*
+         * If HSE fails to start-up, the application will have wrong clock
+     * configuration. User can add here some code to deal with this error
+         */
+  }
+}
+
+#elif defined SYSCLK_FREQ_120MHz_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockTo120_HSE
+ *
+ * @brief   Sets System clock frequency to 120MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo120_HSE(void)
+{
+    __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+    RCC->CTLR |= ((uint32_t)RCC_HSEON);
+
+    /* Wait till HSE is ready and if Time out is reached exit */
+    do
+    {
+        HSEStatus = RCC->CTLR & RCC_HSERDY;
+        StartUpCounter++;
+    } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+    if((RCC->CTLR & RCC_HSERDY) != RESET)
+    {
+        HSEStatus = (uint32_t)0x01;
+    }
+    else
+    {
+        HSEStatus = (uint32_t)0x00;
+    }
+
+    if(HSEStatus == (uint32_t)0x01)
+    {
+#if defined (CH32V20x_D8W)
+        RCC->CFGR0 |= (uint32_t)(3<<22);
+        /* HCLK = SYSCLK/2 */
+        RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV2;
+#else
+        /* HCLK = SYSCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+#endif
+        /* PCLK2 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+        /* PCLK1 = HCLK */
+        RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+        /*  CH32V20x_D6-PLL configuration: PLLCLK = HSE * 15 = 120 MHz (HSE=8MHZ)
+         *  CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 15 = 120 MHz (HSE=32MHZ)
+         *  CH32V20x_D8W-PLL configuration: PLLCLK = HSE/2 * 15 = 240 MHz (HSE=32MHZ)
+         */
+        RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE |
+                                              RCC_PLLMULL));
+
+        RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL15);
+
+        /* Enable PLL */
+        RCC->CTLR |= RCC_PLLON;
+        /* Wait till PLL is ready */
+        while((RCC->CTLR & RCC_PLLRDY) == 0)
+        {
+        }
+        /* Select PLL as system clock source */
+        RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
+        RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+        /* Wait till PLL is used as system clock source */
+        while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+        {
+        }
+    }
+    else
+    {
+        /*
+         * If HSE fails to start-up, the application will have wrong clock
+         * configuration. User can add here some code to deal with this error
+         */
+    }
+}
+#elif defined SYSCLK_FREQ_144MHz_HSE
+
+/*********************************************************************
+ * @fn      SetSysClockTo144_HSE
+ *
+ * @brief   Sets System clock frequency to 144MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo144_HSE(void)
+{
+  __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+  RCC->CTLR |= ((uint32_t)RCC_HSEON);
+
+  /* Wait till HSE is ready and if Time out is reached exit */
+  do
+  {
+    HSEStatus = RCC->CTLR & RCC_HSERDY;
+    StartUpCounter++;
+  } while((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+  if ((RCC->CTLR & RCC_HSERDY) != RESET)
+  {
+    HSEStatus = (uint32_t)0x01;
+  }
+  else
+  {
+    HSEStatus = (uint32_t)0x00;
+  }
+
+  if (HSEStatus == (uint32_t)0x01)
+  {
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  CH32V20x_D6-PLL configuration: PLLCLK = HSE * 18 = 144 MHz (HSE=8MHZ)
+     *  CH32V20x_D8-PLL configuration: PLLCLK = HSE/4 * 18 = 144 MHz (HSE=32MHZ)
+     *  CH32V20x_D8W-PLL configuration: PLLCLK = HSE/4 * 18 = 144 MHz (HSE=32MHZ)
+     */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE |
+                                        RCC_PLLMULL));
+
+    RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSE | RCC_PLLXTPRE_HSE | RCC_PLLMULL18);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+  }
+  else
+  {
+        /*
+         * If HSE fails to start-up, the application will have wrong clock
+     * configuration. User can add here some code to deal with this error
+         */
+  }
+}
+
+#elif defined SYSCLK_FREQ_48MHz_HSI
+
+/*********************************************************************
+ * @fn      SetSysClockTo48_HSI
+ *
+ * @brief   Sets System clock frequency to 48MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo48_HSI(void)
+{
+    EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
+
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  PLL configuration: PLLCLK = HSI * 6 = 48 MHz */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+
+     RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL6);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+}
+
+#elif defined SYSCLK_FREQ_56MHz_HSI
+
+/*********************************************************************
+ * @fn      SetSysClockTo56_HSI
+ *
+ * @brief   Sets System clock frequency to 56MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo56_HSI(void)
+{
+    EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
+
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  PLL configuration: PLLCLK = HSI * 7 = 48 MHz */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+
+     RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL7);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+}
+
+#elif defined SYSCLK_FREQ_72MHz_HSI
+
+/*********************************************************************
+ * @fn      SetSysClockTo72_HSI
+ *
+ * @brief   Sets System clock frequency to 72MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo72_HSI(void)
+{
+    EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
+
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  PLL configuration: PLLCLK = HSI * 9 = 72 MHz */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+
+     RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL9);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+}
+
+#elif defined SYSCLK_FREQ_96MHz_HSI
+
+/*********************************************************************
+ * @fn      SetSysClockTo96_HSI
+ *
+ * @brief   Sets System clock frequency to 96MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo96_HSI(void)
+{
+    EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
+
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  PLL configuration: PLLCLK = HSI * 12 = 96 MHz */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+
+     RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL12);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+}
+
+#elif defined SYSCLK_FREQ_120MHz_HSI
+
+/*********************************************************************
+ * @fn      SetSysClockTo120_HSI
+ *
+ * @brief   Sets System clock frequency to 120MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo120_HSI(void)
+{
+    EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
+
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  PLL configuration: PLLCLK = HSI * 15 = 120 MHz */
+    RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_PLLSRC | RCC_PLLXTPRE |
+                                          RCC_PLLMULL));
+
+    RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL15);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t) ~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+}
+#elif defined SYSCLK_FREQ_144MHz_HSI
+
+/*********************************************************************
+ * @fn      SetSysClockTo144_HSI
+ *
+ * @brief   Sets System clock frequency to 144MHz and configure HCLK, PCLK2 and PCLK1 prescalers.
+ *
+ * @return  none
+ */
+static void SetSysClockTo144_HSI(void)
+{
+    EXTEN->EXTEN_CTR |= EXTEN_PLL_HSI_PRE;
+
+    /* HCLK = SYSCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_HPRE_DIV1;
+    /* PCLK2 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE2_DIV1;
+    /* PCLK1 = HCLK */
+    RCC->CFGR0 |= (uint32_t)RCC_PPRE1_DIV2;
+
+    /*  PLL configuration: PLLCLK = HSI * 18 = 144 MHz */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_PLLSRC | RCC_PLLXTPRE | RCC_PLLMULL));
+
+     RCC->CFGR0 |= (uint32_t)(RCC_PLLSRC_HSI_Div2 | RCC_PLLMULL18);
+
+    /* Enable PLL */
+    RCC->CTLR |= RCC_PLLON;
+    /* Wait till PLL is ready */
+    while((RCC->CTLR & RCC_PLLRDY) == 0)
+    {
+    }
+    /* Select PLL as system clock source */
+    RCC->CFGR0 &= (uint32_t)((uint32_t)~(RCC_SW));
+    RCC->CFGR0 |= (uint32_t)RCC_SW_PLL;
+    /* Wait till PLL is used as system clock source */
+    while ((RCC->CFGR0 & (uint32_t)RCC_SWS) != (uint32_t)0x08)
+    {
+    }
+}
+
+#endif
diff --git a/hw/bsp/ch32v20x/system_ch32v20x.h b/hw/bsp/ch32v20x/system_ch32v20x.h
new file mode 100644
index 000000000..eec7ccb15
--- /dev/null
+++ b/hw/bsp/ch32v20x/system_ch32v20x.h
@@ -0,0 +1,29 @@
+/********************************** (C) COPYRIGHT *******************************
+ * File Name          : system_ch32v20x.h
+ * Author             : WCH
+ * Version            : V1.0.0
+ * Date               : 2021/06/06
+ * Description        : CH32V20x Device Peripheral Access Layer System Header File.
+*********************************************************************************
+* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
+* Attention: This software (modified or not) and binary are used for
+* microcontroller manufactured by Nanjing Qinheng Microelectronics.
+*******************************************************************************/
+#ifndef __SYSTEM_ch32v20x_H
+#define __SYSTEM_ch32v20x_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+extern uint32_t SystemCoreClock;          /* System Clock Frequency (Core Clock) */
+
+/* System_Exported_Functions */
+extern void SystemInit(void);
+extern void SystemCoreClockUpdate(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__CH32V20x_SYSTEM_H */
diff --git a/hw/bsp/ch32v20x/wch-riscv.cfg b/hw/bsp/ch32v20x/wch-riscv.cfg
new file mode 100644
index 000000000..aa35aa9c5
--- /dev/null
+++ b/hw/bsp/ch32v20x/wch-riscv.cfg
@@ -0,0 +1,17 @@
+adapter driver wlinke
+adapter speed 6000
+transport select sdi
+
+wlink_set_address 0x00000000
+set _CHIPNAME wch_riscv
+sdi newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001
+
+set _TARGETNAME $_CHIPNAME.cpu
+
+target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure  -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1
+set _FLASHNAME $_CHIPNAME.flash
+
+flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0
+
+echo "Ready for Remote Connections"
diff --git a/hw/bsp/ch32v307/boards/ch32v307v_r1_1v0/board.cmake b/hw/bsp/ch32v307/boards/ch32v307v_r1_1v0/board.cmake
new file mode 100644
index 000000000..9f5682042
--- /dev/null
+++ b/hw/bsp/ch32v307/boards/ch32v307v_r1_1v0/board.cmake
@@ -0,0 +1,4 @@
+function(update_board TARGET)
+#  target_compile_definitions(${TARGET} PUBLIC
+#    )
+endfunction()
diff --git a/hw/bsp/ch32v307/core_riscv.h b/hw/bsp/ch32v307/core_riscv.h
deleted file mode 100644
index a7ce10a00..000000000
--- a/hw/bsp/ch32v307/core_riscv.h
+++ /dev/null
@@ -1,379 +0,0 @@
-/********************************** (C) COPYRIGHT  *******************************
-* File Name          : core_riscv.h
-* Author             : WCH
-* Version            : V1.0.0
-* Date               : 2021/06/06
-* Description        : RISC-V Core Peripheral Access Layer Header File for CH32V30x
-* Copyright (c) 2021 Nanjing Qinheng Microelectronics Co., Ltd.
-* SPDX-License-Identifier: Apache-2.0
-*******************************************************************************/
-#ifndef __CORE_RISCV_H__
-#define __CORE_RISCV_H__
-
-/* IO definitions */
-#ifdef __cplusplus
-  #define     __I     volatile                /* defines 'read only' permissions */
-#else
-  #define     __I     volatile const          /* defines 'read only' permissions */
-#endif
-#define     __O     volatile                  /* defines 'write only' permissions */
-#define     __IO    volatile                  /* defines 'read / write' permissions */
-
-/* Standard Peripheral Library old types (maintained for legacy purpose) */
-typedef __I uint64_t vuc64;  /* Read Only */
-typedef __I uint32_t vuc32;  /* Read Only */
-typedef __I uint16_t vuc16;  /* Read Only */
-typedef __I uint8_t vuc8;   /* Read Only */
-
-typedef const uint64_t uc64;  /* Read Only */
-typedef const uint32_t uc32;  /* Read Only */
-typedef const uint16_t uc16;  /* Read Only */
-typedef const uint8_t uc8;   /* Read Only */
-
-typedef __I int64_t vsc64;  /* Read Only */
-typedef __I int32_t vsc32;  /* Read Only */
-typedef __I int16_t vsc16;  /* Read Only */
-typedef __I int8_t vsc8;   /* Read Only */
-
-typedef const int64_t sc64;  /* Read Only */
-typedef const int32_t sc32;  /* Read Only */
-typedef const int16_t sc16;  /* Read Only */
-typedef const int8_t sc8;   /* Read Only */
-
-typedef __IO uint64_t  vu64;
-typedef __IO uint32_t  vu32;
-typedef __IO uint16_t vu16;
-typedef __IO uint8_t  vu8;
-
-typedef uint64_t  u64;
-typedef uint32_t  u32;
-typedef uint16_t u16;
-typedef uint8_t  u8;
-
-typedef __IO int64_t  vs64;
-typedef __IO int32_t  vs32;
-typedef __IO int16_t  vs16;
-typedef __IO int8_t   vs8;
-
-typedef int64_t  s64;
-typedef int32_t  s32;
-typedef int16_t s16;
-typedef int8_t  s8;
-
-typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus;
-
-typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
-
-typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
-
-#define   RV_STATIC_INLINE  static  inline
-
-/* memory mapped structure for Program Fast Interrupt Controller (PFIC) */
-typedef struct{
-  __I  uint32_t ISR[8];
-  __I  uint32_t IPR[8];
-  __IO uint32_t ITHRESDR;
-  __IO uint32_t RESERVED;
-  __IO uint32_t CFGR;
-  __I  uint32_t GISR;
-  uint8_t VTFIDR[4];
-  uint8_t RESERVED0[12];
-  __IO uint32_t VTFADDR[4];
-  uint8_t RESERVED1[0x90];
-  __O  uint32_t IENR[8];
-  uint8_t RESERVED2[0x60];
-  __O  uint32_t IRER[8];
-  uint8_t RESERVED3[0x60];
-  __O  uint32_t IPSR[8];
-  uint8_t RESERVED4[0x60];
-  __O  uint32_t IPRR[8];
-  uint8_t RESERVED5[0x60];
-  __IO uint32_t IACTR[8];
-  uint8_t RESERVED6[0xE0];
-  __IO uint8_t IPRIOR[256];
-  uint8_t RESERVED7[0x810];
-  __IO uint32_t SCTLR;
-}PFIC_Type;
-
-/* memory mapped structure for SysTick */
-typedef struct
-{
-    __IO u32 CTLR;
-    __IO u32 SR;
-    __IO u64 CNT;
-    __IO u64 CMP;
-}SysTick_Type;
-
-
-#define PFIC            ((PFIC_Type *) 0xE000E000 )
-#define NVIC            PFIC
-#define NVIC_KEY1       ((uint32_t)0xFA050000)
-#define	NVIC_KEY2				((uint32_t)0xBCAF0000)
-#define	NVIC_KEY3				((uint32_t)0xBEEF0000)
-
-#define SysTick         ((SysTick_Type *) 0xE000F000)
-
-
-/*********************************************************************
- * @fn      __enable_irq
- *
- * @brief   Enable Global Interrupt
- *
- * @return  none
- */
-RV_STATIC_INLINE void __enable_irq(void)
-{
-  __asm volatile ("csrw 0x800, %0" : : "r" (0x6088) );
-}
-
-/*********************************************************************
- * @fn      __disable_irq
- *
- * @brief   Disable Global Interrupt
- *
- * @return  none
- */
-RV_STATIC_INLINE void __disable_irq(void)
-{
-  __asm volatile ("csrw 0x800, %0" : : "r" (0x6000) );
-}
-
-/*********************************************************************
- * @fn      __NOP
- *
- * @brief   nop
- *
- * @return  none
- */
-RV_STATIC_INLINE void __NOP(void)
-{
-  __asm volatile ("nop");
-}
-
-/*********************************************************************
- * @fn      NVIC_EnableIRQ
- *
- * @brief   Enable Interrupt
- *
- * @param   IRQn: Interrupt Numbers
- *
- * @return  none
- */
-RV_STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
-{
-  NVIC->IENR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F));
-}
-
-/*********************************************************************
- * @fn      NVIC_DisableIRQ
- *
- * @brief   Disable Interrupt
- *
- * @param   IRQn: Interrupt Numbers
- *
- * @return  none
- */
-RV_STATIC_INLINE void NVIC_DisableIRQ(IRQn_Type IRQn)
-{
-  NVIC->IRER[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F));
-}
-
-/*********************************************************************
- * @fn      NVIC_GetStatusIRQ
- *
- * @brief   Get Interrupt Enable State
- *
- * @param   IRQn: Interrupt Numbers
- *
- * @return  1 - Interrupt Enable
- *          0 - Interrupt Disable
- */
-RV_STATIC_INLINE uint32_t NVIC_GetStatusIRQ(IRQn_Type IRQn)
-{
-  return((uint32_t) ((NVIC->ISR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0));
-}
-
-/*********************************************************************
- * @fn      NVIC_GetPendingIRQ
- *
- * @brief   Get Interrupt Pending State
- *
- * @param   IRQn: Interrupt Numbers
- *
- * @return  1 - Interrupt Pending Enable
- *          0 - Interrupt Pending Disable
- */
-RV_STATIC_INLINE uint32_t NVIC_GetPendingIRQ(IRQn_Type IRQn)
-{
-  return((uint32_t) ((NVIC->IPR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0));
-}
-
-/*********************************************************************
- * @fn      NVIC_SetPendingIRQ
- *
- * @brief   Set Interrupt Pending
- *
- * @param   IRQn: Interrupt Numbers
- *
- * @return  None
- */
-RV_STATIC_INLINE void NVIC_SetPendingIRQ(IRQn_Type IRQn)
-{
-  NVIC->IPSR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F));
-}
-
-/*********************************************************************
- * @fn      NVIC_ClearPendingIRQ
- *
- * @brief   Clear Interrupt Pending
- *
- * @param   IRQn: Interrupt Numbers
- *
- * @return  None
- */
-RV_STATIC_INLINE void NVIC_ClearPendingIRQ(IRQn_Type IRQn)
-{
-  NVIC->IPRR[((uint32_t)(IRQn) >> 5)] = (1 << ((uint32_t)(IRQn) & 0x1F));
-}
-
-/*********************************************************************
- * @fn      NVIC_GetActive
- *
- * @brief   Get Interrupt Active State
- *
- * @param   IRQn: Interrupt Numbers
- *
- * @return  1 - Interrupt Active
- *          0 - Interrupt No Active
- */
-RV_STATIC_INLINE uint32_t NVIC_GetActive(IRQn_Type IRQn)
-{
-  return((uint32_t)((NVIC->IACTR[(uint32_t)(IRQn) >> 5] & (1 << ((uint32_t)(IRQn) & 0x1F)))?1:0));
-}
-
-/*********************************************************************
- * @fn      NVIC_SetPriority
- *
- * @brief   Set Interrupt Priority
- *
- * @param   IRQn - Interrupt Numbers
- *          priority -
- *              bit7 - pre-emption priority
- *              bit6~bit4 - subpriority
- * @return  None
- */
-RV_STATIC_INLINE void NVIC_SetPriority(IRQn_Type IRQn, uint8_t priority)
-{
-  NVIC->IPRIOR[(uint32_t)(IRQn)] = priority;
-}
-
-/*********************************************************************
- * @fn      __WFI
- *
- * @brief   Wait for Interrupt
- *
- * @return  None
- */
-__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __WFI(void)
-{
-  NVIC->SCTLR &= ~(1<<3);	// wfi
-  asm volatile ("wfi");
-}
-
-/*********************************************************************
- * @fn      __WFE
- *
- * @brief   Wait for Events
- *
- * @return  None
- */
-__attribute__( ( always_inline ) ) RV_STATIC_INLINE void __WFE(void)
-{
-  uint32_t t;
-
-  t = NVIC->SCTLR;
-  NVIC->SCTLR |= (1<<3)|(1<<5);		// (wfi->wfe)+(__sev)
-  NVIC->SCTLR = (NVIC->SCTLR & ~(1<<5)) | ( t & (1<<5));
-  asm volatile ("wfi");
-  asm volatile ("wfi");
-}
-
-/*********************************************************************
- * @fn      SetVTFIRQ
- *
- * @brief   Set VTF Interrupt
- *
- * @param   add - VTF interrupt service function base address.
- *          IRQn -Interrupt Numbers
- *          num - VTF Interrupt Numbers
- *          NewState - DISABLE or ENABLE
- * @return  None
- */
-RV_STATIC_INLINE void SetVTFIRQ(uint32_t addr, IRQn_Type IRQn, uint8_t num, FunctionalState NewState){
-  if(num > 3)  return ;
-
-  if (NewState != DISABLE)
-  {
-      NVIC->VTFIDR[num] = IRQn;
-      NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)|0x1);
-  }
-  else{
-      NVIC->VTFIDR[num] = IRQn;
-      NVIC->VTFADDR[num] = ((addr&0xFFFFFFFE)&(~0x1));
-  }
-}
-
-/*********************************************************************
- * @fn      NVIC_SystemReset
- *
- * @brief   Initiate a system reset request
- *
- * @return  None
- */
-RV_STATIC_INLINE void NVIC_SystemReset(void)
-{
-  NVIC->CFGR = NVIC_KEY3|(1<<7);
-}
-
-
-/* Core_Exported_Functions */
-extern uint32_t __get_FFLAGS(void);
-extern void __set_FFLAGS(uint32_t value);
-extern uint32_t __get_FRM(void);
-extern void __set_FRM(uint32_t value);
-extern uint32_t __get_FCSR(void);
-extern void __set_FCSR(uint32_t value);
-extern uint32_t __get_MSTATUS(void);
-extern void __set_MSTATUS(uint32_t value);
-extern uint32_t __get_MISA(void);
-extern void __set_MISA(uint32_t value);
-extern uint32_t __get_MIE(void);
-extern void __set_MIE(uint32_t value);
-extern uint32_t __get_MTVEC(void);
-extern void __set_MTVEC(uint32_t value);
-extern uint32_t __get_MSCRATCH(void);
-extern void __set_MSCRATCH(uint32_t value);
-extern uint32_t __get_MEPC(void);
-extern void __set_MEPC(uint32_t value);
-extern uint32_t __get_MCAUSE(void);
-extern void __set_MCAUSE(uint32_t value);
-extern uint32_t __get_MTVAL(void);
-extern void __set_MTVAL(uint32_t value);
-extern uint32_t __get_MIP(void);
-extern void __set_MIP(uint32_t value);
-extern uint32_t __get_MCYCLE(void);
-extern void __set_MCYCLE(uint32_t value);
-extern uint32_t __get_MCYCLEH(void);
-extern void __set_MCYCLEH(uint32_t value);
-extern uint32_t __get_MINSTRET(void);
-extern void __set_MINSTRET(uint32_t value);
-extern uint32_t __get_MINSTRETH(void);
-extern void __set_MINSTRETH(uint32_t value);
-extern uint32_t __get_MVENDORID(void);
-extern uint32_t __get_MARCHID(void);
-extern uint32_t __get_MIMPID(void);
-extern uint32_t __get_MHARTID(void);
-extern uint32_t __get_SP(void);
-
-
-#endif
diff --git a/hw/bsp/ch32v307/boards/ch32v307v_r1_1v0/debug_uart.c b/hw/bsp/ch32v307/debug_uart.c
similarity index 94%
rename from hw/bsp/ch32v307/boards/ch32v307v_r1_1v0/debug_uart.c
rename to hw/bsp/ch32v307/debug_uart.c
index db3551ca7..2fd3a9d64 100644
--- a/hw/bsp/ch32v307/boards/ch32v307v_r1_1v0/debug_uart.c
+++ b/hw/bsp/ch32v307/debug_uart.c
@@ -25,10 +25,19 @@
  */
 
 #include "debug_uart.h"
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
 #include 
 
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
 
-#define UART_RINGBUFFER_SIZE_TX 64
+#define UART_RINGBUFFER_SIZE_TX 128
 #define UART_RINGBUFFER_MASK_TX (UART_RINGBUFFER_SIZE_TX-1)
 
 static char tx_buf[UART_RINGBUFFER_SIZE_TX];
diff --git a/hw/bsp/ch32v307/boards/ch32v307v_r1_1v0/debug_uart.h b/hw/bsp/ch32v307/debug_uart.h
similarity index 100%
rename from hw/bsp/ch32v307/boards/ch32v307v_r1_1v0/debug_uart.h
rename to hw/bsp/ch32v307/debug_uart.h
diff --git a/hw/bsp/ch32v307/family.c b/hw/bsp/ch32v307/family.c
index 245fa5674..adf2dbea5 100644
--- a/hw/bsp/ch32v307/family.c
+++ b/hw/bsp/ch32v307/family.c
@@ -25,9 +25,22 @@
  */
 
 #include "stdio.h"
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
 #include "debug_uart.h"
 #include "ch32v30x.h"
 
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
 #include "bsp/board_api.h"
 #include "board.h"
 
@@ -35,29 +48,31 @@
 // Forward USB interrupt events to TinyUSB IRQ Handler
 //--------------------------------------------------------------------+
 
-void USBHS_IRQHandler (void) __attribute__((naked));
-void USBHS_IRQHandler (void)
-{
-  __asm volatile ("call USBHS_IRQHandler_impl; mret");
+// TODO maybe having FS as port0, HS as port1
+
+__attribute__((interrupt)) void USBHS_IRQHandler(void) {
+  #if CFG_TUD_WCH_USBIP_USBHS
+  tud_int_handler(0);
+  #endif
 }
 
-__attribute__ ((used)) void USBHS_IRQHandler_impl (void)
-{
+__attribute__((interrupt)) void OTG_FS_IRQHandler(void) {
+  #if CFG_TUD_WCH_USBIP_USBFS
   tud_int_handler(0);
+  #endif
 }
 
 //--------------------------------------------------------------------+
 // MACRO TYPEDEF CONSTANT ENUM
 //--------------------------------------------------------------------+
 
-uint32_t SysTick_Config(uint32_t ticks)
-{
+uint32_t SysTick_Config(uint32_t ticks) {
   NVIC_EnableIRQ(SysTicK_IRQn);
-  SysTick->CTLR=0;
-  SysTick->SR=0;
-  SysTick->CNT=0;
-  SysTick->CMP=ticks-1;
-  SysTick->CTLR=0xF;
+  SysTick->CTLR = 0;
+  SysTick->SR = 0;
+  SysTick->CNT = 0;
+  SysTick->CMP = ticks - 1;
+  SysTick->CTLR = 0xF;
   return 0;
 }
 
@@ -70,14 +85,28 @@ void board_init(void) {
   SysTick_Config(SystemCoreClock / 1000);
 #endif
 
-	usart_printf_init(115200);
+  usart_printf_init(CFG_BOARD_UART_BAUDRATE);
 
+#ifdef CH32V30x_D8C
+  // v305/v307: Highspeed USB
   RCC_USBCLK48MConfig(RCC_USBCLK48MCLKSource_USBPHY);
   RCC_USBHSPLLCLKConfig(RCC_HSBHSPLLCLKSource_HSE);
   RCC_USBHSConfig(RCC_USBPLL_Div2);
   RCC_USBHSPLLCKREFCLKConfig(RCC_USBHSPLLCKREFCLK_4M);
   RCC_USBHSPHYPLLALIVEcmd(ENABLE);
   RCC_AHBPeriphClockCmd(RCC_AHBPeriph_USBHS, ENABLE);
+#endif
+
+  // Fullspeed USB
+  uint8_t otg_div;
+  switch (SystemCoreClock) {
+    case 48000000:  otg_div = RCC_OTGFSCLKSource_PLLCLK_Div1; break;
+    case 96000000:  otg_div = RCC_OTGFSCLKSource_PLLCLK_Div2; break;
+    case 144000000: otg_div = RCC_OTGFSCLKSource_PLLCLK_Div3; break;
+    default: TU_ASSERT(0,); break;
+  }
+  RCC_OTGFSCLKConfig(otg_div);
+  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_OTG_FS, ENABLE);
 
   GPIO_InitTypeDef GPIO_InitStructure = {0};
 
@@ -102,24 +131,14 @@ void board_init(void) {
 }
 
 #if CFG_TUSB_OS == OPT_OS_NONE
-
 volatile uint32_t system_ticks = 0;
 
-/* Small workaround to support HW stack save/restore */
-void SysTick_Handler (void) __attribute__((naked));
-void SysTick_Handler (void)
-{
-  __asm volatile ("call SysTick_Handler_impl; mret");
-}
-
-__attribute__((used)) void SysTick_Handler_impl (void)
-{
+__attribute__((interrupt)) void SysTick_Handler(void) {
   SysTick->SR = 0;
   system_ticks++;
 }
 
-uint32_t board_millis (void)
-{
+uint32_t board_millis(void) {
   return system_ticks;
 }
 
@@ -129,36 +148,30 @@ uint32_t board_millis (void)
 // Board porting API
 //--------------------------------------------------------------------+
 
-void board_led_write (bool state)
-{
+void board_led_write(bool state) {
   GPIO_WriteBit(LED_PORT, LED_PIN, state);
 }
 
-uint32_t board_button_read (void)
-{
+uint32_t board_button_read(void) {
   return BUTTON_STATE_ACTIVE == GPIO_ReadInputDataBit(BUTTON_PORT, BUTTON_PIN);
 }
 
-int board_uart_read (uint8_t *buf, int len)
-{
+int board_uart_read(uint8_t* buf, int len) {
   (void) buf;
   (void) len;
   return 0;
 }
 
-int board_uart_write (void const *buf, int len)
-{
+int board_uart_write(void const* buf, int len) {
   int txsize = len;
-  while ( txsize-- )
-  {
-    uart_write(*(uint8_t const*) buf);
-    buf++;
+  const char* bufc = (const char*) buf;
+  while (txsize--) {
+    uart_write(*bufc++);
   }
+  uart_sync();
   return len;
 }
 
-
-
 #ifdef USE_FULL_ASSERT
 /**
  * @brief  Reports the name of the source file and the source line number
diff --git a/hw/bsp/ch32v307/family.cmake b/hw/bsp/ch32v307/family.cmake
new file mode 100644
index 000000000..d603af62d
--- /dev/null
+++ b/hw/bsp/ch32v307/family.cmake
@@ -0,0 +1,129 @@
+include_guard()
+
+set(CH32_FAMILY ch32v30x)
+set(SDK_DIR ${TOP}/hw/mcu/wch/ch32v307)
+set(SDK_SRC_DIR ${SDK_DIR}/EVT/EXAM/SRC)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR rv32imac-ilp32 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/riscv_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS CH32V307 CACHE INTERNAL "")
+set(OPENOCD_OPTION "-f ${CMAKE_CURRENT_LIST_DIR}/wch-riscv.cfg")
+
+# default to highspeed, used to select USBFS / USBHS driver
+if (NOT DEFINED SPEED)
+  set(SPEED high)
+endif()
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/ch32v307.ld)
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  if (NOT DEFINED STARTUP_FILE_GNU)
+    set(STARTUP_FILE_GNU ${SDK_SRC_DIR}/Startup/startup_${CH32_FAMILY}_D8C.S)
+  endif ()
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_SRC_DIR}/Core/core_riscv.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_gpio.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_misc.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_rcc.c
+    ${SDK_SRC_DIR}/Peripheral/src/${CH32_FAMILY}_usart.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/${CH32_FAMILY}_it.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/system_${CH32_FAMILY}.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_SRC_DIR}/Core
+    ${SDK_SRC_DIR}/Peripheral/inc
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    )
+  if (SPEED STREQUAL high)
+    target_compile_definitions(${BOARD_TARGET} PUBLIC
+      CFG_TUD_WCH_USBIP_USBHS=1
+#      BOARD_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED
+      )
+  else ()
+    target_compile_definitions(${BOARD_TARGET} PUBLIC
+      CFG_TUD_WCH_USBIP_USBFS=1
+      )
+  endif ()
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(${BOARD_TARGET} PUBLIC
+      -msmall-data-limit=8
+      -mno-save-restore
+      -fmessage-length=0
+      -fsigned-char
+      )
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -nostartfiles
+      --specs=nosys.specs --specs=nano.specs
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    message(FATAL_ERROR "Clang is not supported for MSP432E4")
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/debug_uart.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_CH32V307 ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/wch/dcd_ch32_usbhs.c
+    ${TOP}/src/portable/wch/dcd_ch32_usbfs.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_add_bin_hex(${TARGET})
+  family_flash_openocd_wch(${TARGET})
+endfunction()
diff --git a/hw/bsp/ch32v307/family.mk b/hw/bsp/ch32v307/family.mk
index 07e57f04c..bf2732106 100644
--- a/hw/bsp/ch32v307/family.mk
+++ b/hw/bsp/ch32v307/family.mk
@@ -1,66 +1,64 @@
 # https://www.embecosm.com/resources/tool-chain-downloads/#riscv-stable
 #CROSS_COMPILE ?= riscv32-unknown-elf-
 
-# Toolchain from https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack
-CROSS_COMPILE ?= riscv-none-embed-
+# Toolchain from https://nucleisys.com/download.php
+#CROSS_COMPILE ?= riscv-nuclei-elf-
 
-# Submodules
-CH32V307_SDK = hw/mcu/wch/ch32v307
-DEPS_SUBMODULES += $(CH32V307_SDK)
+# Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
+CROSS_COMPILE ?= riscv-none-elf-
 
-# WCH-SDK paths
-CH32V307_SDK_SRC = $(CH32V307_SDK)/EVT/EXAM/SRC
+CH32_FAMILY = ch32v30x
+SDK_DIR = hw/mcu/wch/ch32v307
+SDK_SRC_DIR = $(SDK_DIR)/EVT/EXAM/SRC
 
 include $(TOP)/$(BOARD_PATH)/board.mk
+CPU_CORE ?= rv32imac-ilp32
+
+# default to use high speed port, unless specified in board.mk or command line
+SPEED ?= high
 
 CFLAGS += \
 	-flto \
-	-march=rv32imac \
-	-mabi=ilp32 \
 	-msmall-data-limit=8 \
-	-mno-save-restore -Os \
+	-mno-save-restore \
 	-fmessage-length=0 \
 	-fsigned-char \
-	-ffunction-sections \
-	-fdata-sections \
-	-nostdlib -nostartfiles \
 	-DCFG_TUSB_MCU=OPT_MCU_CH32V307 \
-	-Xlinker --gc-sections \
-	-DBOARD_TUD_MAX_SPEED=OPT_MODE_HIGH_SPEED
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+# https://github.com/openwch/ch32v307/pull/90
+CFLAGS += -Wno-error=strict-prototypes
+
+ifeq ($(SPEED),high)
+  $(info "Using USBHS driver for HighSpeed mode")
+  CFLAGS += -DCFG_TUD_WCH_USBIP_USBHS=1
+else
+  $(info "Using USBFS driver for FullSpeed mode")
+  CFLAGS += -DCFG_TUD_WCH_USBIP_USBFS=1
+endif
+
+LDFLAGS_GCC += \
+	-nostdlib -nostartfiles \
+  --specs=nosys.specs --specs=nano.specs \
 
 SRC_C += \
 	src/portable/wch/dcd_ch32_usbhs.c \
-	$(CH32V307_SDK_SRC)/Core/core_riscv.c \
-	$(CH32V307_SDK_SRC)/Peripheral/src/ch32v30x_gpio.c \
-	$(CH32V307_SDK_SRC)/Peripheral/src/ch32v30x_misc.c \
-	$(CH32V307_SDK_SRC)/Peripheral/src/ch32v30x_rcc.c \
-	$(CH32V307_SDK_SRC)/Peripheral/src/ch32v30x_usart.c
+	src/portable/wch/dcd_ch32_usbfs.c \
+	$(SDK_SRC_DIR)/Core/core_riscv.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_gpio.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_misc.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_rcc.c \
+	$(SDK_SRC_DIR)/Peripheral/src/${CH32_FAMILY}_usart.c
 
 SRC_S += \
-	$(CH32V307_SDK_SRC)/Startup/startup_ch32v30x_D8C.S
+	$(SDK_SRC_DIR)/Startup/startup_${CH32_FAMILY}_D8C.S
 
 INC += \
 	$(TOP)/$(BOARD_PATH) \
-	$(TOP)/$(CH32V307_SDK_SRC)/Peripheral/inc
+	$(TOP)/$(SDK_SRC_DIR)/Core \
+	$(TOP)/$(SDK_SRC_DIR)/Peripheral/inc
 
 # For freeRTOS port source
 FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/RISC-V
 
-# wch-link is not supported yet in official openOCD yet. We need to either use
-# 1. download openocd as part of mounriver studio http://www.mounriver.com/download or
-# 2. compiled from modified source https://github.com/kprasadvnsi/riscv-openocd-wch
-#
-# Note: For Linux, somehow openocd in mounriver studio does not seem to have wch-link enable,
-# therefore we need to compile it from source as follows:
-# 	git clone https://github.com/kprasadvnsi/riscv-openocd-wch
-# 	cd riscv-openocd-wch
-#		./bootstrap
-#		./configure CFLAGS="-Wno-error" --enable-wlink
-#		make
-# openocd binaries will be generated in riscv-openocd-wch/src
-
-# flash target ROM bootloader
-flash: $(BUILD)/$(PROJECT).elf
-	openocd -f $(TOP)/$(FAMILY_PATH)/wch-riscv.cfg -c init -c halt -c "program $<" -c wlink_reset_resume -c exit
+OPENOCD_WCH_OPTION=-f $(TOP)/$(FAMILY_PATH)/wch-riscv.cfg
+flash: flash-openocd-wch
diff --git a/hw/bsp/ch32v307/wch-riscv.cfg b/hw/bsp/ch32v307/wch-riscv.cfg
index 0d24d16ca..aa35aa9c5 100644
--- a/hw/bsp/ch32v307/wch-riscv.cfg
+++ b/hw/bsp/ch32v307/wch-riscv.cfg
@@ -1,13 +1,15 @@
-#interface wlink
-adapter driver wlink
-wlink_set
-set _CHIPNAME riscv
-jtag newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001
+adapter driver wlinke
+adapter speed 6000
+transport select sdi
+
+wlink_set_address 0x00000000
+set _CHIPNAME wch_riscv
+sdi newtap $_CHIPNAME cpu -irlen 5 -expected-id 0x00001
 
 set _TARGETNAME $_CHIPNAME.cpu
 
-target create $_TARGETNAME.0 riscv -chain-position $_TARGETNAME
-$_TARGETNAME.0 configure  -work-area-phys 0x80000000 -work-area-size 10000 -work-area-backup 1
+target create $_TARGETNAME.0 wch_riscv -chain-position $_TARGETNAME
+$_TARGETNAME.0 configure  -work-area-phys 0x20000000 -work-area-size 10000 -work-area-backup 1
 set _FLASHNAME $_CHIPNAME.flash
 
 flash bank $_FLASHNAME wch_riscv 0x00000000 0 0 0 $_TARGETNAME.0
diff --git a/hw/bsp/da14695_dk_usb/board.mk b/hw/bsp/da14695_dk_usb/board.mk
deleted file mode 100644
index 980b1a361..000000000
--- a/hw/bsp/da14695_dk_usb/board.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-MCU_FAMILY_DIR = hw/mcu/dialog/da1469x
-
-CFLAGS += \
-  -flto \
-  -mthumb \
-  -mthumb-interwork \
-  -mabi=aapcs \
-  -mcpu=cortex-m33+nodsp \
-  -mfloat-abi=hard \
-  -mfpu=fpv5-sp-d16 \
-  -nostdlib \
-  -DCORE_M33 \
-  -DCFG_TUSB_MCU=OPT_MCU_DA1469X \
-  -DCFG_TUD_ENDPOINT0_SIZE=8\
-
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
-
-# All source paths should be relative to the top level.
-LD_FILE = hw/bsp/$(BOARD)/da1469x.ld
-
-# While this is for da1469x chip, there is chance that da1468x chip family will also work
-SRC_C += \
-	src/portable/dialog/da146xx/dcd_da146xx.c \
-	$(MCU_FAMILY_DIR)/src/system_da1469x.c \
-	$(MCU_FAMILY_DIR)/src/da1469x_clock.c \
-	$(MCU_FAMILY_DIR)/src/hal_gpio.c \
-
-SRC_S += hw/bsp/$(BOARD)/gcc_startup_da1469x.S
-
-INC += \
-	$(TOP)/hw/bsp/$(BOARD) \
-	$(TOP)/$(MCU_FAMILY_DIR)/include \
-	$(TOP)/$(MCU_FAMILY_DIR)/SDK_10.0.8.105/sdk/bsp/include
-
-# For freeRTOS port source
-FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/ARM_CM33_NTZ/non_secure
-
-# For flash-jlink target
-JLINK_DEVICE = DA14695
-
-# flash using jlink but with some twists
-flash: flash-dialog
-
-flash-dialog: $(BUILD)/$(PROJECT).bin
-	@echo '#define SW_VERSION "v_1.0.0.1"' >$(BUILD)/version.h
-	@echo '#define SW_VERSION_DATE "'`date +"%Y-%m-%d %H:%M"`'"' >>$(BUILD)/version.h
-	mkimage da1469x $(BUILD)/$(PROJECT).bin $(BUILD)/version.h $^.img
-	cp $(TOP)/hw/bsp/$(BOARD)/product_header.dump $(BUILD)/$(BOARD)-image.bin
-	cat $^.img >> $(BUILD)/$(BOARD)-image.bin
-	@echo r > $(BUILD)/$(BOARD).jlink
-	@echo halt >> $(BUILD)/$(BOARD).jlink
-	@echo loadfile $(BUILD)/$(BOARD)-image.bin 0x16000000 >> $(BUILD)/$(BOARD).jlink
-	@echo r >> $(BUILD)/$(BOARD).jlink
-	@echo go >> $(BUILD)/$(BOARD).jlink
-	@echo exit >> $(BUILD)/$(BOARD).jlink
-	$(JLINKEXE) -device $(JLINK_DEVICE) -if $(JLINK_IF) -JTAGConf -1,-1 -speed auto -CommandFile $(BUILD)/$(BOARD).jlink
diff --git a/hw/bsp/da14695_dk_usb/da14695_dk_usb.c b/hw/bsp/da14695_dk_usb/da14695_dk_usb.c
deleted file mode 100644
index 667b83de3..000000000
--- a/hw/bsp/da14695_dk_usb/da14695_dk_usb.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020 Jerzy Kasenberg
- *
- * 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 file is part of the TinyUSB stack.
- */
-
-#include "bsp/board_api.h"
-#include 
-#include 
-
-//--------------------------------------------------------------------+
-// Forward USB interrupt events to TinyUSB IRQ Handler
-//--------------------------------------------------------------------+
-void USB_IRQHandler(void)
-{
-  tud_int_handler(0);
-}
-
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM
-//--------------------------------------------------------------------+
-
-#define LED_PIN         33 // P1.1
-#define LED_STATE_ON    1
-#define LED_STATE_OFF   (1-LED_STATE_ON)
-
-#define BUTTON_PIN      6
-
-void UnhandledIRQ(void)
-{
-  CRG_TOP->SYS_CTRL_REG = 0x80;
-  __BKPT(1);
-  while(1);
-}
-
-// DA146xx driver function that must be called whenever VBUS changes.
-extern void tusb_vbus_changed(bool present);
-
-void board_init(void)
-{
-  // LED
-  hal_gpio_init_out(LED_PIN, LED_STATE_ON);
-
-  hal_gpio_init_out(1, 0);
-  hal_gpio_init_out(2, 0);
-  hal_gpio_init_out(3, 0);
-  hal_gpio_init_out(4, 0);
-  hal_gpio_init_out(5, 0);
-
-  // Button
-  hal_gpio_init_in(BUTTON_PIN, HAL_GPIO_PULL_DOWN);
-
-  // 1ms tick timer
-  SysTick_Config(SystemCoreClock / 1000);
-
-#if CFG_TUD_ENABLED
-  // This board is USB powered there is no need to monitor
-  // VBUS line.  Notify driver that VBUS is present.
-  tusb_vbus_changed(true);
-
-  /* Setup USB IRQ */
-  NVIC_SetPriority(USB_IRQn, 2);
-  NVIC_EnableIRQ(USB_IRQn);
-
-  /* Use PLL96 / 2 clock not HCLK */
-  CRG_TOP->CLK_CTRL_REG &= ~CRG_TOP_CLK_CTRL_REG_USB_CLK_SRC_Msk;
-
-  mcu_gpio_set_pin_function(14, MCU_GPIO_MODE_INPUT, MCU_GPIO_FUNC_USB);
-  mcu_gpio_set_pin_function(15, MCU_GPIO_MODE_INPUT, MCU_GPIO_FUNC_USB);
-#endif
-}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
-  hal_gpio_write(LED_PIN, state ? LED_STATE_ON : LED_STATE_OFF);
-}
-
-uint32_t board_button_read(void)
-{
-  // button is active HIGH
-  return hal_gpio_read(BUTTON_PIN);
-}
-
-int board_uart_read(uint8_t* buf, int len)
-{
-  (void)buf;
-  (void)len;
-  return 0;
-}
-
-int board_uart_write(void const * buf, int len)
-{
-  (void)buf;
-  (void)len;
-
-  return 0;
-}
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-void SysTick_Handler(void)
-{
-  system_ticks++;
-}
-
-uint32_t board_millis(void)
-{
-  return system_ticks;
-}
-#endif
diff --git a/hw/bsp/da1469x/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/da1469x/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..4d4379a6f
--- /dev/null
+++ b/hw/bsp/da1469x/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,150 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "DA1469xAB.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#define configENABLE_FPU                        1
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+#define configRUN_FREERTOS_SECURE_ONLY          1
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       4
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<
 #include 
 
+#define LED_STATE_OFF   (1-LED_STATE_ON)
+
 //--------------------------------------------------------------------+
 // Forward USB interrupt events to TinyUSB IRQ Handler
 //--------------------------------------------------------------------+
-void USB_IRQHandler(void)
-{
+void USB_IRQHandler(void) {
   tud_int_handler(0);
 }
 
-#if CFG_TUD_ENABLED
-// DA146xx driver function that must be called whenever VBUS changes
+// DA146xx driver function that must be called whenever VBUS changes.
 extern void tusb_vbus_changed(bool present);
 
+#if defined(NEED_VBUS_MONITOR) && CFG_TUD_ENABLED
 // VBUS change interrupt handler
-void VBUS_IRQHandler(void)
-{
+void VBUS_IRQHandler(void) {
   bool present = (CRG_TOP->ANA_STATUS_REG & CRG_TOP_ANA_STATUS_REG_VBUS_AVAILABLE_Msk) != 0;
   // Clear VBUS interrupt
   CRG_TOP->VBUS_IRQ_CLEAR_REG = 1;
@@ -54,22 +55,13 @@ void VBUS_IRQHandler(void)
 //--------------------------------------------------------------------+
 // MACRO TYPEDEF CONSTANT ENUM
 //--------------------------------------------------------------------+
-
-#define LED_PIN         33
-#define LED_STATE_ON    1
-#define LED_STATE_OFF   0
-
-#define BUTTON_PIN      6
-
-void UnhandledIRQ(void)
-{
+void UnhandledIRQ(void) {
   CRG_TOP->SYS_CTRL_REG = 0x80;
   __BKPT(1);
-  while(1);
+  while (1);
 }
 
-void board_init(void)
-{
+void board_init(void) {
   // LED
   hal_gpio_init_out(LED_PIN, LED_STATE_ON);
 
@@ -80,12 +72,13 @@ void board_init(void)
   hal_gpio_init_out(5, 0);
 
   // Button
-  hal_gpio_init_in(BUTTON_PIN, HAL_GPIO_PULL_UP);
+  hal_gpio_init_in(BUTTON_PIN, BUTTON_STATE_ACTIVE ? HAL_GPIO_PULL_DOWN : HAL_GPIO_PULL_UP);
 
   // 1ms tick timer
   SysTick_Config(SystemCoreClock / 1000);
 
 #if CFG_TUD_ENABLED
+  #ifdef NEED_VBUS_MONITOR
   // Setup interrupt for both connect and disconnect
   CRG_TOP->VBUS_IRQ_MASK_REG = CRG_TOP_VBUS_IRQ_MASK_REG_VBUS_IRQ_EN_FALL_Msk |
                                CRG_TOP_VBUS_IRQ_MASK_REG_VBUS_IRQ_EN_RISE_Msk;
@@ -94,6 +87,10 @@ void board_init(void)
   // otherwise it could go unnoticed.
   NVIC_SetPendingIRQ(VBUS_IRQn);
   NVIC_EnableIRQ(VBUS_IRQn);
+  #else
+  // This board is USB powered there is no need to monitor VBUS line.  Notify driver that VBUS is present.
+  tusb_vbus_changed(true);
+  #endif
 
   /* Setup USB IRQ */
   NVIC_SetPriority(USB_IRQn, 2);
@@ -111,41 +108,35 @@ void board_init(void)
 // Board porting API
 //--------------------------------------------------------------------+
 
-void board_led_write(bool state)
-{
+void board_led_write(bool state) {
   hal_gpio_write(LED_PIN, state ? LED_STATE_ON : LED_STATE_OFF);
 }
 
-uint32_t board_button_read(void)
-{
-  // button is active LOW
-  return hal_gpio_read(BUTTON_PIN) ^ 1;
+uint32_t board_button_read(void) {
+  return BUTTON_STATE_ACTIVE == hal_gpio_read(BUTTON_PIN);
 }
 
-int board_uart_read(uint8_t* buf, int len)
-{
-  (void)buf;
-  (void)len;
+int board_uart_read(uint8_t* buf, int len) {
+  (void) buf;
+  (void) len;
   return 0;
 }
 
-int board_uart_write(void const * buf, int len)
-{
-  (void)buf;
-  (void)len;
+int board_uart_write(void const* buf, int len) {
+  (void) buf;
+  (void) len;
 
   return 0;
 }
 
 #if CFG_TUSB_OS == OPT_OS_NONE
 volatile uint32_t system_ticks = 0;
-void SysTick_Handler(void)
-{
+
+void SysTick_Handler(void) {
   system_ticks++;
 }
 
-uint32_t board_millis(void)
-{
+uint32_t board_millis(void) {
   return system_ticks;
 }
 #endif
diff --git a/hw/bsp/da1469x/family.cmake b/hw/bsp/da1469x/family.cmake
new file mode 100644
index 000000000..8c89141fe
--- /dev/null
+++ b/hw/bsp/da1469x/family.cmake
@@ -0,0 +1,141 @@
+include_guard()
+
+set(MCU_DIR ${TOP}/hw/mcu/dialog/da1469x)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+set(CMAKE_SYSTEM_PROCESSOR cortex-m33-nodsp CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
+set(FAMILY_MCUS DA1469X CACHE INTERNAL "")
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/linker/da1469x.ld)
+  endif ()
+
+  if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
+    set(STARTUP_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/gcc_startup_da1469x.S)
+    set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+  endif ()
+
+  add_library(${BOARD_TARGET} STATIC
+    ${MCU_DIR}/src/system_da1469x.c
+    ${MCU_DIR}/src/da1469x_clock.c
+    ${MCU_DIR}/src/hal_gpio.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_compile_options(${BOARD_TARGET} PUBLIC -mthumb-interwork)
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    CORE_M33
+    CFG_TUD_ENDPOINT0_SIZE=8
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    ${MCU_DIR}/include
+    ${MCU_DIR}/SDK_10.0.8.105/sdk/bsp/include
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -L${NRFX_DIR}/mdk
+      --specs=nosys.specs --specs=nano.specs
+      -nostartfiles
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -L${NRFX_DIR}/mdk
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+
+function(family_flash_jlink_dialog TARGET)
+  set(JLINKEXE JLinkExe)
+  set(JLINK_IF swd)
+
+  # mkimage from sdk
+  set(MKIMAGE $ENV{HOME}/code/tinyusb-mcu-driver/dialog/SDK_10.0.8.105/binaries/mkimage)
+
+  file(GENERATE OUTPUT $/version.h
+    CONTENT "#define SW_VERSION \"v_1.0.0.1\"
+#define SW_VERSION_DATE \"2024-07-17 17:55\""
+    )
+
+  file(GENERATE OUTPUT $/${TARGET}.jlink
+    CONTENT "r
+halt
+loadfile $/${TARGET}-image.bin 0x16000000
+r
+go
+exit"
+  )
+
+  add_custom_target(${TARGET}-image
+    DEPENDS ${TARGET}
+    COMMAND ${MKIMAGE} da1469x $/${TARGET}.bin $/version.h $/${TARGET}.bin.img
+    COMMAND cp ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/product_header.dump $/${TARGET}-image.bin
+    COMMAND cat $/${TARGET}.bin.img >> $/${TARGET}-image.bin
+    )
+  add_custom_target(${TARGET}-jlink
+    DEPENDS ${TARGET}-image
+    COMMAND ${JLINKEXE} -device ${JLINK_DEVICE} -if ${JLINK_IF} -JTAGConf -1,-1 -speed auto -CommandFile $/${TARGET}.jlink
+    )
+endfunction()
+
+
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_DA1469X ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/dialog/da146xx/dcd_da146xx.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_add_bin_hex(${TARGET})
+  family_flash_jlink_dialog(${TARGET})
+endfunction()
diff --git a/hw/bsp/da1469x_dk_pro/board.mk b/hw/bsp/da1469x/family.mk
similarity index 52%
rename from hw/bsp/da1469x_dk_pro/board.mk
rename to hw/bsp/da1469x/family.mk
index 5282f93a3..f35fe2cb5 100644
--- a/hw/bsp/da1469x_dk_pro/board.mk
+++ b/hw/bsp/da1469x/family.mk
@@ -1,4 +1,6 @@
-MCU_FAMILY_DIR = hw/mcu/dialog/da1469x
+MCU_DIR = hw/mcu/dialog/da1469x
+
+include $(TOP)/$(BOARD_PATH)/board.mk
 
 CFLAGS += \
   -flto \
@@ -8,48 +10,52 @@ CFLAGS += \
   -mcpu=cortex-m33+nodsp \
   -mfloat-abi=hard \
   -mfpu=fpv5-sp-d16 \
-  -nostdlib \
   -DCORE_M33 \
   -DCFG_TUSB_MCU=OPT_MCU_DA1469X \
   -DCFG_TUD_ENDPOINT0_SIZE=8\
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += \
+  -nostdlib \
+  --specs=nosys.specs --specs=nano.specs
 
 # All source paths should be relative to the top level.
-LD_FILE = hw/bsp/$(BOARD)/da1469x.ld
+LD_FILE = $(FAMILY_PATH)/linker/da1469x.ld
 
 # While this is for da1469x chip, there is chance that da1468x chip family will also work
 SRC_C += \
 	src/portable/dialog/da146xx/dcd_da146xx.c \
-	$(MCU_FAMILY_DIR)/src/system_da1469x.c \
-	$(MCU_FAMILY_DIR)/src/da1469x_clock.c \
-	$(MCU_FAMILY_DIR)/src/hal_gpio.c \
+	${MCU_DIR}/src/system_da1469x.c \
+	${MCU_DIR}/src/da1469x_clock.c \
+	${MCU_DIR}/src/hal_gpio.c \
 
-SRC_S += hw/bsp/$(BOARD)/gcc_startup_da1469x.S
+SRC_S += $(FAMILY_PATH)/gcc_startup_da1469x.S
 
 INC += \
-	$(TOP)/hw/bsp/$(BOARD) \
-	$(TOP)/$(MCU_FAMILY_DIR)/include \
-	$(TOP)/$(MCU_FAMILY_DIR)/SDK_10.0.8.105/sdk/bsp/include
+	$(TOP)/$(BOARD_PATH) \
+	$(TOP)/${MCU_DIR}/include \
+	$(TOP)/${MCU_DIR}/SDK_10.0.8.105/sdk/bsp/include
 
 # For freeRTOS port source
 FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/ARM_CM33_NTZ/non_secure
 
-# For flash-jlink target
-JLINK_DEVICE = DA14699
-
 # flash using jlink but with some twists
 flash: flash-dialog
 
-flash-dialog: $(BUILD)/$(PROJECT).bin
+# SDK_BINARY_PATH is the path to the SDK binary files
+SDK_BINARY_PATH = $(HOME)/code/tinyusb-mcu-driver/dialog/SDK_10.0.8.105/binaries
+MKIMAGE = $(SDK_BINARY_PATH)/mkimage
+
+$(BUILD)/$(PROJECT)-image.bin: $(BUILD)/$(PROJECT).bin
 	@echo '#define SW_VERSION "v_1.0.0.1"' >$(BUILD)/version.h
-	@echo '#define SW_VERSION_DATE "'`date +"%Y-%m-%d %H:%M"`'"' >>$(BUILD)/version.h
-	mkimage da1469x $(BUILD)/$(PROJECT).bin $(BUILD)/version.h $^.img
-	cp $(TOP)/hw/bsp/$(BOARD)/product_header.dump $(BUILD)/$(BOARD)-image.bin
-	cat $^.img >> $(BUILD)/$(BOARD)-image.bin
+	@echo '#define SW_VERSION_DATE "'`date +"%Y-%m-%d %H:%M"`'"' >> $(BUILD)/version.h
+	$(MKIMAGE) da1469x $^ $(BUILD)/version.h $^.img
+	cp $(TOP)/$(FAMILY_PATH)/product_header.dump $(BUILD)/$(PROJECT)-image.bin
+	cat $^.img >> $(BUILD)/$(PROJECT)-image.bin
+
+flash-dialog: $(BUILD)/$(PROJECT)-image.bin
 	@echo r > $(BUILD)/$(BOARD).jlink
 	@echo halt >> $(BUILD)/$(BOARD).jlink
-	@echo loadfile $(BUILD)/$(BOARD)-image.bin 0x16000000 >> $(BUILD)/$(BOARD).jlink
+	@echo loadfile $^ 0x16000000 >> $(BUILD)/$(BOARD).jlink
 	@echo r >> $(BUILD)/$(BOARD).jlink
 	@echo go >> $(BUILD)/$(BOARD).jlink
 	@echo exit >> $(BUILD)/$(BOARD).jlink
diff --git a/hw/bsp/da14695_dk_usb/gcc_startup_da1469x.S b/hw/bsp/da1469x/gcc_startup_da1469x.S
similarity index 100%
rename from hw/bsp/da14695_dk_usb/gcc_startup_da1469x.S
rename to hw/bsp/da1469x/gcc_startup_da1469x.S
diff --git a/hw/bsp/da14695_dk_usb/da1469x.ld b/hw/bsp/da1469x/linker/da1469x.ld
similarity index 100%
rename from hw/bsp/da14695_dk_usb/da1469x.ld
rename to hw/bsp/da1469x/linker/da1469x.ld
diff --git a/hw/bsp/da14695_dk_usb/product_header.dump b/hw/bsp/da1469x/product_header.dump
similarity index 100%
rename from hw/bsp/da14695_dk_usb/product_header.dump
rename to hw/bsp/da1469x/product_header.dump
diff --git a/hw/bsp/da1469x_dk_pro/da1469x.ld b/hw/bsp/da1469x_dk_pro/da1469x.ld
deleted file mode 100644
index 8cc1d9d99..000000000
--- a/hw/bsp/da1469x_dk_pro/da1469x.ld
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-MEMORY
-{
-    /*
-     * Flash is remapped at 0x0 by 1st stage bootloader, but this is done with
-     * an offset derived from image header thus it is safer to use remapped
-     * address space at 0x0 instead of QSPI_M address space at 0x16000000.
-     * Bootloader partition is 32K, but 9K is currently reserved for product
-     * header (8K) and image header (1K).
-     * First 512 bytes of SYSRAM are remapped at 0x0 and used as ISR vector
-     * (there's no need to reallocate ISR vector) and thus cannot be used by
-     * application.
-     */
-
-    FLASH (r)  : ORIGIN = (0x00000000), LENGTH = (1024 * 1024)
-    RAM (rw)   : ORIGIN = (0x20000000), LENGTH = (512 * 1024)
-}
-
-OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
-
-/* Linker script to place sections and symbol values. Should be used together
- * with other linker script that defines memory regions FLASH and RAM.
- * It references following symbols, which must be defined in code:
- *   Reset_Handler : Entry of reset handler
- *
- * It defines following symbols, which code can use without definition:
- *   __exidx_start
- *   __exidx_end
- *   __etext
- *   __data_start__
- *   __preinit_array_start
- *   __preinit_array_end
- *   __init_array_start
- *   __init_array_end
- *   __fini_array_start
- *   __fini_array_end
- *   __data_end__
- *   __bss_start__
- *   __bss_end__
- *   __HeapBase
- *   __HeapLimit
- *   __StackLimit
- *   __StackTop
- *   __stack
- *   __bssnz_start__
- *   __bssnz_end__
- */
-ENTRY(Reset_Handler)
-
-SECTIONS
-{
-    __text = .;
-
-    .text :
-    {
-        __isr_vector_start = .;
-        KEEP(*(.isr_vector))
-        /* ISR vector shall have exactly 512 bytes */
-        . = __isr_vector_start + 0x200;
-        __isr_vector_end = .;
-
-        *(.text)
-        *(.text.*)
-
-        *(.libcmac.rom)
-
-        KEEP(*(.init))
-        KEEP(*(.fini))
-
-        /* .ctors */
-        *crtbegin.o(.ctors)
-        *crtbegin?.o(.ctors)
-        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
-        *(SORT(.ctors.*))
-        *(.ctors)
-
-        /* .dtors */
-        *crtbegin.o(.dtors)
-        *crtbegin?.o(.dtors)
-        *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
-        *(SORT(.dtors.*))
-        *(.dtors)
-
-        *(.rodata*)
-
-        *(.eh_frame*)
-        . = ALIGN(4);
-    } > FLASH
-
-    .ARM.extab :
-    {
-        *(.ARM.extab* .gnu.linkonce.armextab.*)
-        . = ALIGN(4);
-    } > FLASH
-
-    __exidx_start = .;
-    .ARM :
-    {
-        *(.ARM.exidx* .gnu.linkonce.armexidx.*)
-        . = ALIGN(4);
-    } > FLASH
-    __exidx_end = .;
-
-    .intvect :
-    {
-        . = ALIGN(4);
-        __intvect_start__ = .;
-        . = . + (__isr_vector_end - __isr_vector_start);
-        . = ALIGN(4);
-    } > RAM
-
-    .sleep_state (NOLOAD) :
-    {
-        . = ALIGN(4);
-        *(sleep_state)
-    } > RAM
-
-    /* This section will be zeroed by RTT package init */
-    .rtt (NOLOAD):
-    {
-        . = ALIGN(4);
-        *(.rtt)
-        . = ALIGN(4);
-    } > RAM
-
-    __text_ram_addr = LOADADDR(.text_ram);
-
-    .text_ram :
-    {
-        . = ALIGN(4);
-        __text_ram_start__ = .;
-        *(.text_ram*)
-        . = ALIGN(4);
-        __text_ram_end__ = .;
-    } > RAM AT > FLASH
-
-    __etext = LOADADDR(.data);
-
-    .data :
-    {
-        __data_start__ = .;
-        *(vtable)
-        *(.data*)
-
-        . = ALIGN(4);
-        /* preinit data */
-        PROVIDE_HIDDEN (__preinit_array_start = .);
-        *(.preinit_array)
-        PROVIDE_HIDDEN (__preinit_array_end = .);
-
-        . = ALIGN(4);
-        /* init data */
-        PROVIDE_HIDDEN (__init_array_start = .);
-        *(SORT(.init_array.*))
-        *(.init_array)
-        PROVIDE_HIDDEN (__init_array_end = .);
-
-
-        . = ALIGN(4);
-        /* finit data */
-        PROVIDE_HIDDEN (__fini_array_start = .);
-        *(SORT(.fini_array.*))
-        *(.fini_array)
-        PROVIDE_HIDDEN (__fini_array_end = .);
-
-        *(.jcr)
-        . = ALIGN(4);
-        /* All data end */
-        __data_end__ = .;
-    } > RAM AT > FLASH
-
-    .bssnz :
-    {
-        . = ALIGN(4);
-        __bssnz_start__ = .;
-        *(.bss.core.nz*)
-        . = ALIGN(4);
-        __bssnz_end__ = .;
-    } > RAM
-
-    .bss :
-    {
-        . = ALIGN(4);
-        __bss_start__ = .;
-        *(.bss*)
-        *(COMMON)
-        . = ALIGN(4);
-        __bss_end__ = .;
-    } > RAM
-
-    .cmac (NOLOAD) :
-    {
-        . = ALIGN(0x400);
-        *(.libcmac.ram)
-    } > RAM
-
-    /* Heap starts after BSS */
-    . = ALIGN(8);
-    __HeapBase = .;
-
-    /* .stack_dummy section doesn't contains any symbols. It is only
-     * used for linker to calculate size of stack sections, and assign
-     * values to stack symbols later */
-    .stack_dummy (COPY):
-    {
-        *(.stack*)
-    } > RAM
-
-    _ram_start = ORIGIN(RAM);
-
-    /* Set stack top to end of RAM, and stack limit move down by
-     * size of stack_dummy section */
-    __StackTop = ORIGIN(RAM) + LENGTH(RAM);
-    __StackLimit = __StackTop - SIZEOF(.stack_dummy);
-    PROVIDE(__stack = __StackTop);
-
-    /* Top of head is the bottom of the stack */
-    __HeapLimit = __StackLimit;
-    end = __HeapLimit;
-
-    /* Check if data + heap + stack exceeds RAM limit */
-    ASSERT(__HeapBase <= __HeapLimit, "region RAM overflowed with stack")
-
-    /* Check that intvect is at the beginning of RAM */
-    ASSERT(__intvect_start__ == ORIGIN(RAM), "intvect is not at beginning of RAM")
-}
diff --git a/hw/bsp/da1469x_dk_pro/gcc_startup_da1469x.S b/hw/bsp/da1469x_dk_pro/gcc_startup_da1469x.S
deleted file mode 100644
index d47fbcd97..000000000
--- a/hw/bsp/da1469x_dk_pro/gcc_startup_da1469x.S
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *  http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
- #include "syscfg/syscfg.h"
-
-    .syntax unified
-    .arch   armv7-m
-
-    .section .stack
-    .align  3
-#ifdef __STACK_SIZE
-    .equ    Stack_Size, __STACK_SIZE
-#else
-    .equ    Stack_Size, 0xC00
-#endif
-    .equ    SYS_CTRL_REG,       0x50000024
-    .equ    CACHE_FLASH_REG,    0x100C0040
-    .equ    RESET_STAT_REG,     0x500000BC
-
-    .globl  __StackTop
-    .globl  __StackLimit
-__StackLimit:
-    .space  Stack_Size
-    .size   __StackLimit, . - __StackLimit
-__StackTop:
-    .size   __StackTop, . - __StackTop
-
-    .section .heap
-    .align  3
-#ifdef __HEAP_SIZE
-    .equ    Heap_Size, __HEAP_SIZE
-#else
-    .equ    Heap_Size, 0
-#endif
-    .globl  __HeapBase
-    .globl  __HeapLimit
-__HeapBase:
-    .if     Heap_Size
-    .space  Heap_Size
-    .endif
-    .size   __HeapBase, . - __HeapBase
-__HeapLimit:
-    .size   __HeapLimit, . - __HeapLimit
-
-    .section .isr_vector
-    .align 2
-    .globl  __isr_vector
-__isr_vector:
-    .long   __StackTop
-    .long   Reset_Handler
-    /* Cortex-M33 interrupts */
-    .long   NMI_Handler
-    .long   HardFault_Handler
-    .long   MemoryManagement_Handler
-    .long   BusFault_Handler
-    .long   UsageFault_Handler
-    .long   SecureFault_Handler
-    .long   0                       /* Reserved */
-    .long   0                       /* Reserved */
-    .long   0                       /* Reserved */
-    .long   SVC_Handler
-    .long   DebugMonitor_Handler
-    .long   0                       /* Reserved */
-    .long   PendSV_Handler
-    .long   SysTick_Handler
-    /* DA1469x interrupts */
-    .long   SENSOR_NODE_IRQHandler
-    .long   DMA_IRQHandler
-    .long   CHARGER_STATE_IRQHandler
-    .long   CHARGER_ERROR_IRQHandler
-    .long   CMAC2SYS_IRQHandler
-    .long   UART_IRQHandler
-    .long   UART2_IRQHandler
-    .long   UART3_IRQHandler
-    .long   I2C_IRQHandler
-    .long   I2C2_IRQHandler
-    .long   SPI_IRQHandler
-    .long   SPI2_IRQHandler
-    .long   PCM_IRQHandler
-    .long   SRC_IN_IRQHandler
-    .long   SRC_OUT_IRQHandler
-    .long   USB_IRQHandler
-    .long   TIMER_IRQHandler
-    .long   TIMER2_IRQHandler
-    .long   RTC_IRQHandler
-    .long   KEY_WKUP_GPIO_IRQHandler
-    .long   PDC_IRQHandler
-    .long   VBUS_IRQHandler
-    .long   MRM_IRQHandler
-    .long   MOTOR_CONTROLLER_IRQHandler
-    .long   TRNG_IRQHandler
-    .long   DCDC_IRQHandler
-    .long   XTAL32M_RDY_IRQHandler
-    .long   ADC_IRQHandler
-    .long   ADC2_IRQHandler
-    .long   CRYPTO_IRQHandler
-    .long   CAPTIMER1_IRQHandler
-    .long   RFDIAG_IRQHandler
-    .long   LCD_CONTROLLER_IRQHandler
-    .long   PLL_LOCK_IRQHandler
-    .long   TIMER3_IRQHandler
-    .long   TIMER4_IRQHandler
-    .long   LRA_IRQHandler
-    .long   RTC_EVENT_IRQHandler
-    .long   GPIO_P0_IRQHandler
-    .long   GPIO_P1_IRQHandler
-    .long   0                       /* Reserved */
-    .long   0                       /* Reserved */
-    .long   0                       /* Reserved */
-    .long   0                       /* Reserved */
-    .long   0                       /* Reserved */
-    .long   0                       /* Reserved */
-    .long   0                       /* Reserved */
-    .long   0                       /* Reserved */
-    .size   __isr_vector, . - __isr_vector
-
-    .text
-    .thumb
-    .thumb_func
-    .align 2
-    .globl Reset_Handler
-    .type  Reset_Handler, %function
-Reset_Handler:
- /* Make sure interrupt vector is remapped at 0x0 */
-    ldr     r1, =SYS_CTRL_REG
-    ldrh    r2, [r1, #0]
-    orrs    r2, r2, #8
-    strh    r2, [r1, #0]
-
-#if !MYNEWT_VAL(RAM_RESIDENT)
-/*
- * Flash is remapped at 0x0 with an offset, i.e. 0x0 does not correspond to
- * 0x16000000 but to start of an image on flash. This is calculated from product
- * header by 1st state bootloader and configured in CACHE_FLASH_REG. We need to
- * retrieve proper offset value for calculations later.
- */
-    ldr     r1, =CACHE_FLASH_REG
-    ldr     r4, [r1, #0]
-    mov     r2, r4
-    mov     r3, #0xFFFF
-    bic     r4, r4, r3      /* CACHE_FLASH_REG[FLASH_REGION_BASE] */
-    mov     r3, #0xFFF0
-    and     r2, r2, r3      /* CACHE_FLASH_REG[FLASH_REGION_OFFSET] */
-    lsr     r2, r2, #2
-    orr     r4, r4, r2
-
-/* Copy ISR vector from flash to RAM */
-    ldr     r1, =__isr_vector_start     /* src ptr */
-    ldr     r2, =__isr_vector_end       /* src end */
-    ldr     r3, =__intvect_start__      /* dst ptr */
-/* Make sure we copy from QSPIC address range, not from remapped range */
-    cmp     r1, r4
-    itt     lt
-    addlt   r1, r1, r4
-    addlt   r2, r2, r4
-.loop_isr_copy:
-    cmp     r1, r2
-    ittt    lt
-    ldrlt   r0, [r1], #4
-    strlt   r0, [r3], #4
-    blt     .loop_isr_copy
-
-/* Copy QSPI code from flash to RAM */
-    ldr     r1, =__text_ram_addr        /* src ptr */
-    ldr     r2, =__text_ram_start__     /* ptr */
-    ldr     r3, =__text_ram_end__       /* dst end */
-.loop_code_text_ram_copy:
-    cmp     r2, r3
-    ittt    lt
-    ldrlt   r0, [r1], #4
-    strlt   r0, [r2], #4
-    blt     .loop_code_text_ram_copy
-
-/* Copy data from flash to RAM */
-    ldr     r1, =__etext                /* src ptr */
-    ldr     r2, =__data_start__         /* dst ptr */
-    ldr     r3, =__data_end__           /* dst end */
-.loop_data_copy:
-    cmp     r2, r3
-    ittt    lt
-    ldrlt   r0, [r1], #4
-    strlt   r0, [r2], #4
-    blt     .loop_data_copy
-#endif
-
-/* Clear BSS */
-    movs    r0, 0
-    ldr     r1, =__bss_start__
-    ldr     r2, =__bss_end__
-.loop_bss_clear:
-    cmp     r1, r2
-    itt     lt
-    strlt   r0, [r1], #4
-    blt     .loop_bss_clear
-
-    ldr     r0, =__HeapBase
-    ldr     r1, =__HeapLimit
-/* Call static constructors */
-    bl __libc_init_array
-
-    bl      SystemInit
-    bl      main
-
-    .pool
-    .size   Reset_Handler, . - Reset_Handler
-
-/* Default interrupt handler */
-    .type   Default_Handler, %function
-Default_Handler:
-    ldr     r1, =SYS_CTRL_REG
-    ldrh    r2, [r1, #0]
-    orrs    r2, r2, #0x80   /* DEBUGGER_ENABLE */
-    strh    r2, [r1, #0]
-    b       .
-
-    .size   Default_Handler, . - Default_Handler
-
-/* Default handlers for all interrupts */
-    .macro  IRQ handler
-    .weak   \handler
-    .set    \handler, Default_Handler
-    .endm
-
-    /* Cortex-M33 interrupts */
-    IRQ  NMI_Handler
-    IRQ  HardFault_Handler
-    IRQ  MemoryManagement_Handler
-    IRQ  BusFault_Handler
-    IRQ  UsageFault_Handler
-    IRQ  SecureFault_Handler
-    IRQ  SVC_Handler
-    IRQ  DebugMonitor_Handler
-    IRQ  PendSV_Handler
-    IRQ  SysTick_Handler
-    /* DA1469x interrupts */
-    IRQ  SENSOR_NODE_IRQHandler
-    IRQ  DMA_IRQHandler
-    IRQ  CHARGER_STATE_IRQHandler
-    IRQ  CHARGER_ERROR_IRQHandler
-    IRQ  CMAC2SYS_IRQHandler
-    IRQ  UART_IRQHandler
-    IRQ  UART2_IRQHandler
-    IRQ  UART3_IRQHandler
-    IRQ  I2C_IRQHandler
-    IRQ  I2C2_IRQHandler
-    IRQ  SPI_IRQHandler
-    IRQ  SPI2_IRQHandler
-    IRQ  PCM_IRQHandler
-    IRQ  SRC_IN_IRQHandler
-    IRQ  SRC_OUT_IRQHandler
-    IRQ  USB_IRQHandler
-    IRQ  TIMER_IRQHandler
-    IRQ  TIMER2_IRQHandler
-    IRQ  RTC_IRQHandler
-    IRQ  KEY_WKUP_GPIO_IRQHandler
-    IRQ  PDC_IRQHandler
-    IRQ  VBUS_IRQHandler
-    IRQ  MRM_IRQHandler
-    IRQ  MOTOR_CONTROLLER_IRQHandler
-    IRQ  TRNG_IRQHandler
-    IRQ  DCDC_IRQHandler
-    IRQ  XTAL32M_RDY_IRQHandler
-    IRQ  ADC_IRQHandler
-    IRQ  ADC2_IRQHandler
-    IRQ  CRYPTO_IRQHandler
-    IRQ  CAPTIMER1_IRQHandler
-    IRQ  RFDIAG_IRQHandler
-    IRQ  LCD_CONTROLLER_IRQHandler
-    IRQ  PLL_LOCK_IRQHandler
-    IRQ  TIMER3_IRQHandler
-    IRQ  TIMER4_IRQHandler
-    IRQ  LRA_IRQHandler
-    IRQ  RTC_EVENT_IRQHandler
-    IRQ  GPIO_P0_IRQHandler
-    IRQ  GPIO_P1_IRQHandler
-    IRQ  RESERVED40_IRQHandler
-    IRQ  RESERVED41_IRQHandler
-    IRQ  RESERVED42_IRQHandler
-    IRQ  RESERVED43_IRQHandler
-    IRQ  RESERVED44_IRQHandler
-    IRQ  RESERVED45_IRQHandler
-    IRQ  RESERVED46_IRQHandler
-    IRQ  RESERVED47_IRQHandler
-
-.end
diff --git a/hw/bsp/da1469x_dk_pro/product_header.dump b/hw/bsp/da1469x_dk_pro/product_header.dump
deleted file mode 100644
index ea4842242..000000000
Binary files a/hw/bsp/da1469x_dk_pro/product_header.dump and /dev/null differ
diff --git a/hw/bsp/espressif/boards/adafruit_feather_esp32_v2/board.cmake b/hw/bsp/espressif/boards/adafruit_feather_esp32_v2/board.cmake
new file mode 100644
index 000000000..18c956714
--- /dev/null
+++ b/hw/bsp/espressif/boards/adafruit_feather_esp32_v2/board.cmake
@@ -0,0 +1,2 @@
+# Apply board specific content here
+set(IDF_TARGET "esp32")
diff --git a/hw/bsp/espressif/boards/adafruit_feather_esp32_v2/board.h b/hw/bsp/espressif/boards/adafruit_feather_esp32_v2/board.h
new file mode 100644
index 000000000..0c53df06b
--- /dev/null
+++ b/hw/bsp/espressif/boards/adafruit_feather_esp32_v2/board.h
@@ -0,0 +1,53 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020, Ha Thach (tinyusb.org)
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#define NEOPIXEL_PIN          0
+#define NEOPIXEL_POWER_PIN    2
+#define NEOPIXEL_POWER_STATE  1
+
+#define BUTTON_PIN            38
+#define BUTTON_STATE_ACTIVE   0
+
+// SPI for USB host shield
+#define MAX3421_SPI_HOST SPI3_HOST
+#define MAX3421_SCK_PIN  5
+#define MAX3421_MOSI_PIN 19
+#define MAX3421_MISO_PIN 21
+#define MAX3421_CS_PIN   33
+#define MAX3421_INTR_PIN 15
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* BOARD_H_ */
diff --git a/hw/bsp/espressif/boards/espressif_c3_devkitc/board.cmake b/hw/bsp/espressif/boards/espressif_c3_devkitc/board.cmake
new file mode 100644
index 000000000..ab4bc1a06
--- /dev/null
+++ b/hw/bsp/espressif/boards/espressif_c3_devkitc/board.cmake
@@ -0,0 +1,2 @@
+# Apply board specific content here
+set(IDF_TARGET "esp32c3")
diff --git a/hw/bsp/espressif/boards/espressif_c3_devkitc/board.h b/hw/bsp/espressif/boards/espressif_c3_devkitc/board.h
new file mode 100644
index 000000000..243dd47f6
--- /dev/null
+++ b/hw/bsp/espressif/boards/espressif_c3_devkitc/board.h
@@ -0,0 +1,51 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020, Ha Thach (tinyusb.org)
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#define NEOPIXEL_PIN          8
+
+#define BUTTON_PIN            9
+#define BUTTON_STATE_ACTIVE   0
+
+// SPI for USB host shield
+#define MAX3421_SPI_HOST SPI2_HOST
+#define MAX3421_SCK_PIN  4
+#define MAX3421_MOSI_PIN 6
+#define MAX3421_MISO_PIN 5
+#define MAX3421_CS_PIN   10
+#define MAX3421_INTR_PIN 7
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* BOARD_H_ */
diff --git a/hw/bsp/espressif/boards/espressif_c6_devkitc/board.cmake b/hw/bsp/espressif/boards/espressif_c6_devkitc/board.cmake
new file mode 100644
index 000000000..50c7cb35d
--- /dev/null
+++ b/hw/bsp/espressif/boards/espressif_c6_devkitc/board.cmake
@@ -0,0 +1,2 @@
+# Apply board specific content here
+set(IDF_TARGET "esp32c6")
diff --git a/hw/bsp/espressif/boards/espressif_c6_devkitc/board.h b/hw/bsp/espressif/boards/espressif_c6_devkitc/board.h
new file mode 100644
index 000000000..243dd47f6
--- /dev/null
+++ b/hw/bsp/espressif/boards/espressif_c6_devkitc/board.h
@@ -0,0 +1,51 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020, Ha Thach (tinyusb.org)
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#define NEOPIXEL_PIN          8
+
+#define BUTTON_PIN            9
+#define BUTTON_STATE_ACTIVE   0
+
+// SPI for USB host shield
+#define MAX3421_SPI_HOST SPI2_HOST
+#define MAX3421_SCK_PIN  4
+#define MAX3421_MOSI_PIN 6
+#define MAX3421_MISO_PIN 5
+#define MAX3421_CS_PIN   10
+#define MAX3421_INTR_PIN 7
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* BOARD_H_ */
diff --git a/hw/bsp/espressif/boards/family.c b/hw/bsp/espressif/boards/family.c
index 5599d1504..02e478a0c 100644
--- a/hw/bsp/espressif/boards/family.c
+++ b/hw/bsp/espressif/boards/family.c
@@ -30,8 +30,12 @@
 #include "esp_rom_gpio.h"
 #include "esp_mac.h"
 #include "hal/gpio_ll.h"
+
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
 #include "hal/usb_hal.h"
 #include "soc/usb_periph.h"
+static void configure_pins(usb_hal_context_t* usb);
+#endif
 
 #include "driver/gpio.h"
 #include "driver/uart.h"
@@ -56,7 +60,6 @@ static led_strip_handle_t led_strip;
 static void max3421_init(void);
 #endif
 
-static void configure_pins(usb_hal_context_t* usb);
 
 //--------------------------------------------------------------------+
 // Implementation
@@ -100,7 +103,6 @@ void board_init(void) {
   };
 
   ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip));
-
   led_strip_clear(led_strip); // off
 #endif
 
@@ -109,6 +111,7 @@ void board_init(void) {
   gpio_set_direction(BUTTON_PIN, GPIO_MODE_INPUT);
   gpio_set_pull_mode(BUTTON_PIN, BUTTON_STATE_ACTIVE ? GPIO_PULLDOWN_ONLY : GPIO_PULLUP_ONLY);
 
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
   // USB Controller Hal init
   periph_module_reset(PERIPH_USB_MODULE);
   periph_module_enable(PERIPH_USB_MODULE);
@@ -118,12 +121,14 @@ void board_init(void) {
   };
   usb_hal_init(&hal);
   configure_pins(&hal);
+#endif
 
 #if CFG_TUH_ENABLED && CFG_TUH_MAX3421
   max3421_init();
 #endif
 }
 
+#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
 static void configure_pins(usb_hal_context_t* usb) {
   /* usb_periph_iopins currently configures USB_OTG as USB Device.
    * Introduce additional parameters in usb_hal_context_t when adding support
@@ -153,6 +158,7 @@ static void configure_pins(usb_hal_context_t* usb) {
     gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3);
   }
 }
+#endif
 
 //--------------------------------------------------------------------+
 // Board porting API
@@ -208,15 +214,12 @@ SemaphoreHandle_t max3421_intr_sem;
 
 static void IRAM_ATTR max3421_isr_handler(void* arg) {
   (void) arg; // arg is gpio num
-  gpio_set_level(13, 1);
 
   BaseType_t xHigherPriorityTaskWoken = pdFALSE;
   xSemaphoreGiveFromISR(max3421_intr_sem, &xHigherPriorityTaskWoken);
   if (xHigherPriorityTaskWoken) {
     portYIELD_FROM_ISR();
   }
-
-  gpio_set_level(13, 0);
 }
 
 static void max3421_intr_task(void* param) {
@@ -250,16 +253,12 @@ static void max3421_init(void) {
 
   spi_device_interface_config_t max3421_cfg = {
       .mode = 0,
-      .clock_speed_hz = 26000000,
+      .clock_speed_hz = 20000000, // S2/S3 can work with 26 Mhz, but esp32 seems only work up to 20 Mhz
       .spics_io_num = -1, // manual control CS
       .queue_size = 1
   };
   ESP_ERROR_CHECK(spi_bus_add_device(MAX3421_SPI_HOST, &max3421_cfg, &max3421_spi));
 
-  // debug
-  gpio_set_direction(13, GPIO_MODE_OUTPUT);
-  gpio_set_level(13, 0);
-
   // Interrupt pin
   max3421_intr_sem = xSemaphoreCreateBinary();
   xTaskCreate(max3421_intr_task, "max3421 intr", 2048, NULL, configMAX_PRIORITIES - 2, NULL);
diff --git a/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/dependencies.lock b/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/dependencies.lock
deleted file mode 100644
index 97840a153..000000000
--- a/hw/bsp/espressif/components/led_strip/examples/led_strip_rmt_ws2812/dependencies.lock
+++ /dev/null
@@ -1,15 +0,0 @@
-dependencies:
-  espressif/led_strip:
-    component_hash: null
-    source:
-      path: /home/hathach/code/idf-extra-components/led_strip
-      type: local
-    version: 2.5.2
-  idf:
-    component_hash: null
-    source:
-      type: idf
-    version: 5.1.1
-manifest_hash: 47d47762be26168b388cb0e6cbfee6b22c68d630ebf4b27a49c47c4c54191590
-target: esp32s3
-version: 1.0.0
diff --git a/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt b/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt
index abe276910..8bdb3802e 100644
--- a/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt
+++ b/hw/bsp/espressif/components/tinyusb_src/CMakeLists.txt
@@ -5,17 +5,7 @@ set(includes_public)
 set(compile_options)
 set(tusb_src "${CMAKE_CURRENT_SOURCE_DIR}/../../../../../src")
 
-if(target STREQUAL "esp32s3")
-  set(tusb_mcu "OPT_MCU_ESP32S3")
-elseif(target STREQUAL "esp32s2")
-  set(tusb_mcu "OPT_MCU_ESP32S2")
-else()
-  # CONFIG_TINYUSB dependency has been guaranteed by Kconfig logic,
-  # So it's not possible that cmake goes here
-  message(FATAL_ERROR "TinyUSB is not support on ${target}.")
-  return()
-endif()
-
+string(TOUPPER OPT_MCU_${target} tusb_mcu)
 list(APPEND compile_definitions
   CFG_TUSB_MCU=${tusb_mcu}
   CFG_TUSB_OS=OPT_OS_FREERTOS
diff --git a/hw/bsp/espressif/family.cmake b/hw/bsp/espressif/family.cmake
index 92a9bcb04..eb97401c3 100644
--- a/hw/bsp/espressif/family.cmake
+++ b/hw/bsp/espressif/family.cmake
@@ -3,11 +3,7 @@ cmake_minimum_required(VERSION 3.5)
 # Apply board specific content i.e IDF_TARGET must be set before project.cmake is included
 include("${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake")
 
-if(IDF_TARGET STREQUAL "esp32s2")
-  set(FAMILY_MCUS ESP32S2)
-elseif(IDF_TARGET STREQUAL "esp32s3")
-  set(FAMILY_MCUS ESP32S3)
-endif()
+string(TOUPPER ${IDF_TARGET} FAMILY_MCUS)
 
 # Add example src and bsp directories
 set(EXTRA_COMPONENT_DIRS "src" "${CMAKE_CURRENT_LIST_DIR}/boards" "${CMAKE_CURRENT_LIST_DIR}/components")
diff --git a/hw/bsp/f1c100s/board.h b/hw/bsp/f1c100s/board.h
deleted file mode 100644
index 0ef9a1700..000000000
--- a/hw/bsp/f1c100s/board.h
+++ /dev/null
@@ -1 +0,0 @@
-// Nothing valuable here
diff --git a/hw/bsp/f1c100s/board.mk b/hw/bsp/f1c100s/board.mk
deleted file mode 100644
index 3596e5414..000000000
--- a/hw/bsp/f1c100s/board.mk
+++ /dev/null
@@ -1,52 +0,0 @@
-MCU_DIR = hw/mcu/allwinner/f1c100s
-DEPS_SUBMODULES += hw/mcu/allwinner
-DEFINES += -D__ARM32_ARCH__=5 -D__ARM926EJS__
-
-CFLAGS += \
-  -ffreestanding \
-  -std=gnu99 \
-  -march=armv5te \
-  -mtune=arm926ej-s \
-  -mfloat-abi=soft \
-  -marm \
-  -mno-thumb-interwork \
-  -Wno-unused-parameter \
-  -Wno-float-equal \
-  -DCFG_TUSB_MCU=OPT_MCU_F1C100S \
-  -Wno-error=cast-align \
-  -Wno-error=address-of-packed-member \
-  $(DEFINES)
-
-LD_FILE = hw/mcu/allwinner/f1c100s/f1c100s.ld
-# TODO may skip nanolib
-LDFLAGS += -nostdlib -lgcc -specs=nosys.specs -specs=nano.specs
-
-SRC_C += \
-	src/portable/sunxi/dcd_sunxi_musb.c \
-	$(MCU_DIR)/machine/sys-uart.c \
-	$(MCU_DIR)/machine/exception.c \
-	$(MCU_DIR)/machine/sys-clock.c \
-	$(MCU_DIR)/machine/sys-copyself.c \
-	$(MCU_DIR)/machine/sys-dram.c \
-	$(MCU_DIR)/machine/sys-mmu.c \
-	$(MCU_DIR)/machine/sys-spi-flash.c \
-	$(MCU_DIR)/machine/f1c100s-intc.c \
-	$(MCU_DIR)/lib/malloc.c \
-	$(MCU_DIR)/lib/printf.c
-
-SRC_S += \
-  $(MCU_DIR)/machine/start.S \
-	$(MCU_DIR)/lib/memcpy.S \
-	$(MCU_DIR)/lib/memset.S
-
-INC += \
-	$(TOP)/$(MCU_DIR)/include \
-	$(TOP)/$(BOARD_PATH)
-
-# flash target using xfel
-flash: flash-xfel
-
-exec: $(BUILD)/$(PROJECT).bin
-	xfel ddr
-	xfel write 0x80000000 $<
-	xfel exec 0x80000000
diff --git a/hw/bsp/f1c100s/boards/f1c100s/board.cmake b/hw/bsp/f1c100s/boards/f1c100s/board.cmake
new file mode 100644
index 000000000..98ed56c57
--- /dev/null
+++ b/hw/bsp/f1c100s/boards/f1c100s/board.cmake
@@ -0,0 +1,3 @@
+function(update_board TARGET)
+  # nothing to do
+endfunction()
diff --git a/hw/bsp/f1c100s/boards/f1c100s/board.h b/hw/bsp/f1c100s/boards/f1c100s/board.h
new file mode 100644
index 000000000..3b56a3a57
--- /dev/null
+++ b/hw/bsp/f1c100s/boards/f1c100s/board.h
@@ -0,0 +1,6 @@
+#ifndef BOARD_H
+#define BOARD_H
+
+// Nothing valuable here
+
+#endif
diff --git a/hw/bsp/f1c100s/boards/f1c100s/board.mk b/hw/bsp/f1c100s/boards/f1c100s/board.mk
new file mode 100644
index 000000000..be830bd8c
--- /dev/null
+++ b/hw/bsp/f1c100s/boards/f1c100s/board.mk
@@ -0,0 +1 @@
+# nothing to do
diff --git a/hw/bsp/f1c100s/f1c100s.c b/hw/bsp/f1c100s/family.c
similarity index 78%
rename from hw/bsp/f1c100s/f1c100s.c
rename to hw/bsp/f1c100s/family.c
index 272b756f2..6df4a0ed8 100644
--- a/hw/bsp/f1c100s/f1c100s.c
+++ b/hw/bsp/f1c100s/family.c
@@ -39,10 +39,9 @@ extern void sys_uart_putc(char c);
 
 static void timer_init(void);
 
-void board_init(void)
-{
+void board_init(void) {
   arch_local_irq_disable();
-	do_init_mem_pool();
+  do_init_mem_pool();
   f1c100s_intc_init();
   timer_init();
   printf("Timer INIT done\n");
@@ -50,42 +49,38 @@ void board_init(void)
 }
 
 // No LED, no button
-void board_led_write(bool state)
-{
-
+void board_led_write(bool state) {
+  (void) state;
 }
 
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
   return 0;
 }
 
-int board_uart_read(uint8_t* buf, int len)
-{
+int board_uart_read(uint8_t* buf, int len) {
+  (void) buf;
+  (void) len;
   return 0;
 }
 
-int board_uart_write(void const * buf, int len)
-{
+int board_uart_write(void const* buf, int len) {
   int txsize = len;
   while (txsize--) {
-    sys_uart_putc(*(uint8_t const*)buf);
+    sys_uart_putc(*(uint8_t const*) buf);
     buf++;
   }
   return len;
 }
 
-#if CFG_TUSB_OS  == OPT_OS_NONE
+#if CFG_TUSB_OS == OPT_OS_NONE
 volatile uint32_t system_ticks = 0;
 
-uint32_t board_millis(void)
-{
+uint32_t board_millis(void) {
   return system_ticks;
 }
 
-static void timer_handler(void)
-{
-  volatile uint32_t *temp_addr = (uint32_t *)(0x01C20C00 + 0x04);
+static void timer_handler(void) {
+  volatile uint32_t* temp_addr = (uint32_t*) (0x01C20C00 + 0x04);
 
   /* clear timer */
   *temp_addr |= 0x01;
@@ -95,36 +90,37 @@ static void timer_handler(void)
 
 static void timer_init(void) {
   uint32_t temp;
-  volatile uint32_t *temp_addr;
+  volatile uint32_t* temp_addr;
 
   /* reload value */
   temp = 12000000 / 1000;
-  temp_addr = (uint32_t *)(0x01C20C00 + 0x14);
+  temp_addr = (uint32_t*) (0x01C20C00 + 0x14);
   *temp_addr = temp;
 
   /* continuous | /2 | 24Mhz |  reload*/
   temp = (0x00 << 7) | (0x01 << 4) | (0x01 << 2) | (0x00 << 1);
-  temp_addr = (uint32_t *)(0x01C20C00 + 0x10);
+  temp_addr = (uint32_t*) (0x01C20C00 + 0x10);
   *temp_addr &= 0xffffff00;
   *temp_addr |= temp;
 
   /* open timer irq */
   temp = 0x01 << 0;
-  temp_addr = (uint32_t *)(0x01C20C00);
+  temp_addr = (uint32_t*) (0x01C20C00);
   *temp_addr |= temp;
 
   /* set init value */
-  temp_addr = (uint32_t *)(0x01C20C00 + 0x18);
+  temp_addr = (uint32_t*) (0x01C20C00 + 0x18);
   *temp_addr = 0;
 
   /* begin run timer */
   temp = 0x01 << 0;
-  temp_addr = (uint32_t *)(0x01C20C00 + 0x10);
+  temp_addr = (uint32_t*) (0x01C20C00 + 0x10);
   *temp_addr |= temp;
 
   f1c100s_intc_set_isr(F1C100S_IRQ_TIMER0, timer_handler);
   f1c100s_intc_enable_irq(F1C100S_IRQ_TIMER0);
 }
+
 #else
 static void timer_init(void) { }
 #endif
diff --git a/hw/bsp/f1c100s/family.cmake b/hw/bsp/f1c100s/family.cmake
new file mode 100644
index 000000000..0903a0143
--- /dev/null
+++ b/hw/bsp/f1c100s/family.cmake
@@ -0,0 +1,114 @@
+include_guard()
+
+set(SDK_DIR ${TOP}/hw/mcu/allwinner/f1c100s)
+
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR arm926ej-s CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS F1C100S CACHE INTERNAL "")
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
+
+  # LD_FILE and STARTUP_FILE can be defined in board.cmake
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${SDK_DIR}/f1c100s.ld)
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  if (NOT DEFINED STARTUP_FILE_GNU)
+    set(STARTUP_FILE_GNU ${SDK_DIR}/machine/start.S)
+  endif ()
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/lib/malloc.c
+    ${SDK_DIR}/lib/printf.c
+    ${SDK_DIR}/lib/memcpy.S
+    ${SDK_DIR}/lib/memset.S
+    ${SDK_DIR}/machine/sys-uart.c
+    ${SDK_DIR}/machine/exception.c
+    ${SDK_DIR}/machine/sys-clock.c
+    ${SDK_DIR}/machine/sys-copyself.c
+    ${SDK_DIR}/machine/sys-dram.c
+    ${SDK_DIR}/machine/sys-mmu.c
+    ${SDK_DIR}/machine/sys-spi-flash.c
+    ${SDK_DIR}/machine/f1c100s-intc.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    __ARM32_ARCH__=5
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_DIR}/include
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -lgcc
+      --specs=nosys.specs --specs=nano.specs
+      "LINKER:--defsym=__bss_end__=__bss_end"
+      "LINKER:--defsym=__bss_start__=__bss_start"
+      "LINKER:--defsym=end=__bss_end"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_F1C100S ${RTOS})
+  target_sources(${TARGET}-tinyusb PRIVATE
+    ${TOP}/src/portable/sunxi/dcd_sunxi_musb.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_flash_jlink(${TARGET})
+endfunction()
diff --git a/hw/bsp/f1c100s/family.mk b/hw/bsp/f1c100s/family.mk
new file mode 100644
index 000000000..be416e72e
--- /dev/null
+++ b/hw/bsp/f1c100s/family.mk
@@ -0,0 +1,58 @@
+SDK_DIR = hw/mcu/allwinner/f1c100s
+
+include $(TOP)/$(BOARD_PATH)/board.mk
+CPU_CORE ?= arm926ej-s
+
+#CFLAGS += \
+#  -march=armv5te \
+#  -mtune=arm926ej-s \
+#  -mfloat-abi=soft \
+#  -marm \
+
+CFLAGS += \
+  -ffreestanding \
+  -std=gnu99 \
+  -mno-thumb-interwork \
+  -D__ARM32_ARCH__=5 \
+  -D__ARM926EJS__ \
+  -Wno-float-equal \
+  -Wno-unused-parameter \
+  -DCFG_TUSB_MCU=OPT_MCU_F1C100S \
+  -Wno-error=array-bounds \
+
+LD_FILE = ${SDK_DIR}/f1c100s.ld
+
+# TODO may skip nanolib
+LDFLAGS += \
+  -nostdlib -lgcc \
+  --specs=nosys.specs --specs=nano.specs \
+
+SRC_C += \
+	src/portable/sunxi/dcd_sunxi_musb.c \
+	${SDK_DIR}/machine/sys-uart.c \
+	${SDK_DIR}/machine/exception.c \
+	${SDK_DIR}/machine/sys-clock.c \
+	${SDK_DIR}/machine/sys-copyself.c \
+	${SDK_DIR}/machine/sys-dram.c \
+	${SDK_DIR}/machine/sys-mmu.c \
+	${SDK_DIR}/machine/sys-spi-flash.c \
+	${SDK_DIR}/machine/f1c100s-intc.c \
+	${SDK_DIR}/lib/malloc.c \
+	${SDK_DIR}/lib/printf.c
+
+SRC_S += \
+  ${SDK_DIR}/machine/start.S \
+	${SDK_DIR}/lib/memcpy.S \
+	${SDK_DIR}/lib/memset.S
+
+INC += \
+	$(TOP)/${SDK_DIR}/include \
+	$(TOP)/$(BOARD_PATH)
+
+# flash target using xfel
+flash: flash-xfel
+
+exec: $(BUILD)/$(PROJECT).bin
+	xfel ddr
+	xfel write 0x80000000 $<
+	xfel exec 0x80000000
diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake
index 4ccd6e4f2..9a7eaeb92 100644
--- a/hw/bsp/family_support.cmake
+++ b/hw/bsp/family_support.cmake
@@ -6,12 +6,35 @@ include(CMakePrintHelpers)
 set(TOP "${CMAKE_CURRENT_LIST_DIR}/../..")
 get_filename_component(TOP ${TOP} ABSOLUTE)
 
-# Default to gcc
+set(UF2CONV_PY ${TOP}/tools/uf2/utils/uf2conv.py)
+
+#-------------------------------------------------------------
+# Toolchain
+# Can be changed via -DTOOLCHAIN=gcc|iar or -DCMAKE_C_COMPILER=
+#-------------------------------------------------------------
+# Detect toolchain based on CMAKE_C_COMPILER
+if (DEFINED CMAKE_C_COMPILER)
+  string(FIND ${CMAKE_C_COMPILER} "iccarm" IS_IAR)
+  string(FIND ${CMAKE_C_COMPILER} "clang" IS_CLANG)
+  string(FIND ${CMAKE_C_COMPILER} "gcc" IS_GCC)
+
+  if (NOT IS_IAR EQUAL -1)
+    set(TOOLCHAIN iar)
+  elseif (NOT IS_CLANG EQUAL -1)
+    set(TOOLCHAIN clang)
+  elseif (NOT IS_GCC EQUAL -1)
+    set(TOOLCHAIN gcc)
+  endif ()
+endif ()
+
+# default to gcc
 if (NOT DEFINED TOOLCHAIN)
   set(TOOLCHAIN gcc)
 endif ()
 
-# FAMILY not defined, try to detect it from BOARD
+#-------------------------------------------------------------
+# FAMILY and BOARD
+#-------------------------------------------------------------
 if (NOT DEFINED FAMILY)
   if (NOT DEFINED BOARD)
     message(FATAL_ERROR "You must set a FAMILY variable for the build (e.g. rp2040, espressif).
@@ -46,6 +69,10 @@ if (NOT FAMILY STREQUAL rp2040)
   endif()
 endif()
 
+if (NOT NO_WARN_RWX_SEGMENTS_SUPPORTED)
+  set(NO_WARN_RWX_SEGMENTS_SUPPORTED 1)
+endif()
+
 set(WARNING_FLAGS_GNU
   -Wall
   -Wextra
@@ -74,6 +101,9 @@ set(WARNING_FLAGS_GNU
 
 set(WARNING_FLAGS_IAR "")
 
+#-------------------------------------------------------------
+# Functions
+#-------------------------------------------------------------
 
 # Filter example based on only.txt and skip.txt
 function(family_filter RESULT DIR)
@@ -114,7 +144,6 @@ function(family_filter RESULT DIR)
   endif()
 endfunction()
 
-
 function(family_add_subdirectory DIR)
   family_filter(SHOULD_ADD "${DIR}")
   if (SHOULD_ADD)
@@ -122,13 +151,11 @@ function(family_add_subdirectory DIR)
   endif()
 endfunction()
 
-
 function(family_get_project_name OUTPUT_NAME DIR)
   get_filename_component(SHORT_NAME ${DIR} NAME)
   set(${OUTPUT_NAME} ${TINYUSB_FAMILY_PROJECT_NAME_PREFIX}${SHORT_NAME} PARENT_SCOPE)
 endfunction()
 
-
 function(family_initialize_project PROJECT DIR)
   # set output suffix to .elf (skip espressif and rp2040)
   if(NOT FAMILY STREQUAL "espressif" AND NOT FAMILY STREQUAL "rp2040")
@@ -142,7 +169,6 @@ function(family_initialize_project PROJECT DIR)
   endif()
 endfunction()
 
-
 #-------------------------------------------------------------
 # Common Target Configure
 # Most families use these settings except rp2040 and espressif
@@ -168,7 +194,6 @@ function(family_add_rtos TARGET RTOS)
   endif ()
 endfunction()
 
-
 # Add common configuration to example
 function(family_configure_common TARGET RTOS)
   family_add_rtos(${TARGET} ${RTOS})
@@ -179,24 +204,22 @@ function(family_configure_common TARGET RTOS)
     BOARD_${BOARD_UPPER}
   )
 
-  # run size after build
-  find_program(SIZE_EXE ${CMAKE_SIZE})
-  if(NOT ${SIZE_EXE} STREQUAL SIZE_EXE-NOTFOUND)
-    add_custom_command(TARGET ${TARGET} POST_BUILD
-      COMMAND ${SIZE_EXE} $
-      )
-  endif ()
-  # Add warnings flags
+  # compile define from command line
+  if(DEFINED CFLAGS_CLI)
+    target_compile_options(${TARGET} PUBLIC ${CFLAGS_CLI})
+  endif()
+
   target_compile_options(${TARGET} PUBLIC ${WARNING_FLAGS_${CMAKE_C_COMPILER_ID}})
 
   # Generate linker map file
   if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
     target_link_options(${TARGET} PUBLIC "LINKER:-Map=$.map")
-    if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0)
+    if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0 AND NO_WARN_RWX_SEGMENTS_SUPPORTED)
       target_link_options(${TARGET} PUBLIC "LINKER:--no-warn-rwx-segments")
     endif ()
-  endif()
-  if (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+ elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${TARGET} PUBLIC "LINKER:-Map=$.map")
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
     target_link_options(${TARGET} PUBLIC "LINKER:--map=$.map")
   endif()
 
@@ -208,19 +231,25 @@ function(family_configure_common TARGET RTOS)
   # LOGGER option
   if (DEFINED LOGGER)
     target_compile_definitions(${TARGET} PUBLIC LOGGER_${LOGGER})
-
     # Add segger rtt to example
     if(LOGGER STREQUAL "RTT" OR LOGGER STREQUAL "rtt")
       if (NOT TARGET segger_rtt)
         add_library(segger_rtt STATIC ${TOP}/lib/SEGGER_RTT/RTT/SEGGER_RTT.c)
         target_include_directories(segger_rtt PUBLIC ${TOP}/lib/SEGGER_RTT/RTT)
-        #target_compile_definitions(segger_rtt PUBLIC SEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL)
+#        target_compile_definitions(segger_rtt PUBLIC SEGGER_RTT_MODE_DEFAULT=SEGGER_RTT_MODE_BLOCK_IF_FIFO_FULL)
       endif()
       target_link_libraries(${TARGET} PUBLIC segger_rtt)
     endif ()
   endif ()
-endfunction()
 
+  # run size after build
+  find_program(SIZE_EXE ${CMAKE_SIZE})
+  if(NOT ${SIZE_EXE} STREQUAL SIZE_EXE-NOTFOUND)
+    add_custom_command(TARGET ${TARGET} POST_BUILD
+      COMMAND ${SIZE_EXE} $
+      )
+  endif ()
+endfunction()
 
 # Add tinyusb to example
 function(family_add_tinyusb TARGET OPT_MCU RTOS)
@@ -262,7 +291,6 @@ function(family_add_tinyusb TARGET OPT_MCU RTOS)
 
 endfunction()
 
-
 # Add bin/hex output
 function(family_add_bin_hex TARGET)
   add_custom_command(TARGET ${TARGET} POST_BUILD
@@ -271,6 +299,13 @@ function(family_add_bin_hex TARGET)
     VERBATIM)
 endfunction()
 
+# Add uf2 output
+function(family_add_uf2 TARGET FAMILY_ID)
+  set(BIN_FILE $/${TARGET}.hex)
+  add_custom_command(TARGET ${TARGET} POST_BUILD
+    COMMAND python ${UF2CONV_PY} -f ${FAMILY_ID} -c -o $/${TARGET}.uf2 ${BIN_FILE}
+    VERBATIM)
+endfunction()
 
 #----------------------------------
 # Example Target Configure (Default rule)
@@ -329,7 +364,7 @@ function(family_add_default_example_warnings TARGET)
     )
 
   if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-    if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0)
+    if (CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL 12.0 AND NO_WARN_RWX_SEGMENTS_SUPPORTED)
       target_link_options(${TARGET} PUBLIC "LINKER:--no-warn-rwx-segments")
     endif()
 
@@ -360,6 +395,10 @@ function(family_flash_jlink TARGET)
     set(JLINKEXE JLinkExe)
   endif ()
 
+  if (NOT DEFINED JLINK_IF)
+    set(JLINK_IF swd)
+  endif ()
+
   file(GENERATE
     OUTPUT $/${TARGET}.jlink
     CONTENT "halt
@@ -371,7 +410,7 @@ exit"
 
   add_custom_target(${TARGET}-jlink
     DEPENDS ${TARGET}
-    COMMAND ${JLINKEXE} -device ${JLINK_DEVICE} -if swd -JTAGConf -1,-1 -speed auto -CommandFile $/${TARGET}.jlink
+    COMMAND ${JLINKEXE} -device ${JLINK_DEVICE} -if ${JLINK_IF} -JTAGConf -1,-1 -speed auto -CommandFile $/${TARGET}.jlink
     )
 endfunction()
 
@@ -389,22 +428,65 @@ function(family_flash_stlink TARGET)
 endfunction()
 
 
+# Add flash st-flash target
+function(family_flash_stflash TARGET)
+  if (NOT DEFINED ST_FLASH)
+    set(ST_FLASH st-flash)
+  endif ()
+
+  add_custom_target(${TARGET}-stflash
+    DEPENDS ${TARGET}
+    COMMAND ${ST_FLASH} write $/${TARGET}.bin 0x8000000
+    )
+endfunction()
+
+
 # Add flash openocd target
-function(family_flash_openocd TARGET CLI_OPTIONS)
+function(family_flash_openocd TARGET)
   if (NOT DEFINED OPENOCD)
     set(OPENOCD openocd)
   endif ()
 
-  separate_arguments(CLI_OPTIONS_LIST UNIX_COMMAND ${CLI_OPTIONS})
+  if (NOT DEFINED OPENOCD_OPTION2)
+    set(OPENOCD_OPTION2 "")
+  endif ()
+
+  separate_arguments(OPTION_LIST UNIX_COMMAND ${OPENOCD_OPTION})
+  separate_arguments(OPTION_LIST2 UNIX_COMMAND ${OPENOCD_OPTION2})
 
   # note skip verify since it has issue with rp2040
   add_custom_target(${TARGET}-openocd
     DEPENDS ${TARGET}
-    COMMAND ${OPENOCD} ${CLI_OPTIONS_LIST} -c "program $ reset exit"
+    COMMAND ${OPENOCD} ${OPTION_LIST} -c init -c halt -c "program $ reset" ${OPTION_LIST2} -c exit
     VERBATIM
     )
 endfunction()
 
+
+# Add flash openocd-wch target
+# compiled from https://github.com/hathach/riscv-openocd-wch or https://github.com/dragonlock2/miscboards/blob/main/wch/SDK/riscv-openocd.tar.xz
+function(family_flash_openocd_wch TARGET)
+  if (NOT DEFINED OPENOCD)
+    set(OPENOCD $ENV{HOME}/app/riscv-openocd-wch/src/openocd)
+  endif ()
+
+  family_flash_openocd(${TARGET})
+endfunction()
+
+
+# Add flash with https://github.com/ch32-rs/wlink
+function(family_flash_wlink_rs TARGET)
+  if (NOT DEFINED WLINK_RS)
+    set(WLINK_RS wlink)
+  endif ()
+
+  add_custom_target(${TARGET}-wlink-rs
+    DEPENDS ${TARGET}
+    COMMAND ${WLINK_RS} flash $
+    )
+endfunction()
+
+
 # Add flash pycod target
 function(family_flash_pyocd TARGET)
   if (NOT DEFINED PYOC)
@@ -418,6 +500,15 @@ function(family_flash_pyocd TARGET)
 endfunction()
 
 
+# Flash with UF2
+function(family_flash_uf2 TARGET FAMILY_ID)
+  add_custom_target(${TARGET}-uf2
+    DEPENDS ${TARGET}
+    COMMAND python ${UF2CONV_PY} -f ${FAMILY_ID} --deploy $/${TARGET}.uf2
+    )
+endfunction()
+
+
 # Add flash teensy_cli target
 function(family_flash_teensy TARGET)
   if (NOT DEFINED TEENSY_CLI)
@@ -430,6 +521,7 @@ function(family_flash_teensy TARGET)
     )
 endfunction()
 
+
 # Add flash using NXP's LinkServer (redserver)
 # https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/linkserver-for-microcontrollers:LINKERSERVER
 function(family_flash_nxplink TARGET)
@@ -460,6 +552,21 @@ function(family_flash_dfu_util TARGET OPTION)
     )
 endfunction()
 
+function(family_flash_msp430flasher TARGET)
+  if (NOT DEFINED MSP430Flasher)
+    set(MSP430FLASHER MSP430Flasher)
+  endif ()
+
+  # set LD_LIBRARY_PATH to find libmsp430.so (directory containing MSP430Flasher)
+  find_program(MSP430FLASHER_PATH MSP430Flasher)
+  get_filename_component(MSP430FLASHER_PARENT_DIR "${MSP430FLASHER_PATH}" DIRECTORY)
+  add_custom_target(${TARGET}-msp430flasher
+    DEPENDS ${TARGET}
+    COMMAND ${CMAKE_COMMAND} -E env LD_LIBRARY_PATH=${MSP430FLASHER_PARENT_DIR}
+            ${MSP430FLASHER} -w $/${TARGET}.hex -z [VCC]
+    )
+endfunction()
+
 #----------------------------------
 # Family specific
 #----------------------------------
diff --git a/hw/bsp/fomu/boards/fomu/board.cmake b/hw/bsp/fomu/boards/fomu/board.cmake
new file mode 100644
index 000000000..9f5682042
--- /dev/null
+++ b/hw/bsp/fomu/boards/fomu/board.cmake
@@ -0,0 +1,4 @@
+function(update_board TARGET)
+#  target_compile_definitions(${TARGET} PUBLIC
+#    )
+endfunction()
diff --git a/hw/bsp/fomu/fomu.c b/hw/bsp/fomu/family.c
similarity index 99%
rename from hw/bsp/fomu/fomu.c
rename to hw/bsp/fomu/family.c
index d155b743d..ccf2b12f4 100644
--- a/hw/bsp/fomu/fomu.c
+++ b/hw/bsp/fomu/family.c
@@ -26,10 +26,11 @@
 
 #include 
 #include 
-#include "../board_api.h"
 #include "csr.h"
 #include "irq.h"
 
+#include "bsp/board_api.h"
+
 //--------------------------------------------------------------------+
 // Board porting API
 //--------------------------------------------------------------------+
diff --git a/hw/bsp/fomu/family.cmake b/hw/bsp/fomu/family.cmake
new file mode 100644
index 000000000..8d5ab144c
--- /dev/null
+++ b/hw/bsp/fomu/family.cmake
@@ -0,0 +1,91 @@
+include_guard()
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR rv32i-ilp32 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/riscv_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS VALENTYUSB_EPTRI CACHE INTERNAL "")
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/fomu.ld)
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  if (NOT DEFINED STARTUP_FILE_GNU)
+    set(STARTUP_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/crt0-vexriscv.S)
+  endif ()
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/include
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -nostartfiles
+      --specs=nosys.specs --specs=nano.specs
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    message(FATAL_ERROR "Clang is not supported for MSP432E4")
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_VALENTYUSB_EPTRI ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/valentyusb/eptri/dcd_eptri.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_add_bin_hex(${TARGET})
+endfunction()
diff --git a/hw/bsp/fomu/family.mk b/hw/bsp/fomu/family.mk
index f8a3c9ebf..69a546964 100644
--- a/hw/bsp/fomu/family.mk
+++ b/hw/bsp/fomu/family.mk
@@ -1,14 +1,15 @@
-# Toolchain from https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack
-CROSS_COMPILE = riscv-none-embed-
+# Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
+CROSS_COMPILE = riscv-none-elf-
+
+CPU_CORE ?= rv32i-ilp32
 
 CFLAGS += \
   -flto \
-  -march=rv32i \
-  -mabi=ilp32 \
-  -nostdlib \
   -DCFG_TUSB_MCU=OPT_MCU_VALENTYUSB_EPTRI
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += \
+  -nostdlib \
+  --specs=nosys.specs --specs=nano.specs \
 
 # All source paths should be relative to the top level.
 LD_FILE = $(FAMILY_PATH)/fomu.ld
diff --git a/hw/bsp/fomu/fomu.ld b/hw/bsp/fomu/fomu.ld
index 13278d2ad..0b0cf2612 100644
--- a/hw/bsp/fomu/fomu.ld
+++ b/hw/bsp/fomu/fomu.ld
@@ -11,7 +11,7 @@ MEMORY {
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 /* Section Definitions */
 SECTIONS
diff --git a/hw/bsp/gd32vf103/boards/sipeed_longan_nano/board.cmake b/hw/bsp/gd32vf103/boards/sipeed_longan_nano/board.cmake
new file mode 100644
index 000000000..403ac08cf
--- /dev/null
+++ b/hw/bsp/gd32vf103/boards/sipeed_longan_nano/board.cmake
@@ -0,0 +1,13 @@
+set(JLINK_DEVICE gd32vf103cbt6)
+
+set(SDK_BSP_DIR ${SOC_DIR}/Board/gd32vf103c_longan_nano)
+set(LD_FILE_GNU ${SDK_BSP_DIR}/Source/GCC/gcc_gd32vf103xb_flashxip.ld)
+
+function(update_board TARGET)
+  target_sources(${TARGET} PUBLIC
+    ${SDK_BSP_DIR}/Source/gd32vf103c_longan_nano.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    ${SDK_BSP_DIR}/Include
+    )
+endfunction()
diff --git a/hw/bsp/gd32vf103/boards/sipeed_longan_nano/board.mk b/hw/bsp/gd32vf103/boards/sipeed_longan_nano/board.mk
index 3b8944452..fc49b7317 100644
--- a/hw/bsp/gd32vf103/boards/sipeed_longan_nano/board.mk
+++ b/hw/bsp/gd32vf103/boards/sipeed_longan_nano/board.mk
@@ -10,4 +10,3 @@ INC += $(TOP)/$(LONGAN_NANO_SDK_BSP)/Include
 
 # Longan Nano 128k ROM 32k RAM
 JLINK_DEVICE = gd32vf103cbt6
-#JLINK_DEVICE = gd32vf103c8t6 # Longan Nano Lite 64k ROM 20k RAM
diff --git a/hw/bsp/gd32vf103/family.c b/hw/bsp/gd32vf103/family.c
index 27d7e87bb..d4a819fb3 100644
--- a/hw/bsp/gd32vf103/family.c
+++ b/hw/bsp/gd32vf103/family.c
@@ -24,11 +24,11 @@
  * This file is part of the TinyUSB stack.
  */
 
-#include "board.h"
 #include "drv_usb_hw.h"
 #include "drv_usb_dev.h"
 
-#include "../board_api.h"
+#include "bsp/board_api.h"
+#include "board.h"
 
 //--------------------------------------------------------------------+
 // Forward USB interrupt events to TinyUSB IRQ Handler
diff --git a/hw/bsp/gd32vf103/family.cmake b/hw/bsp/gd32vf103/family.cmake
new file mode 100644
index 000000000..5f4a3da8d
--- /dev/null
+++ b/hw/bsp/gd32vf103/family.cmake
@@ -0,0 +1,119 @@
+include_guard()
+
+set(SDK_DIR ${TOP}/hw/mcu/gd/nuclei-sdk)
+set(SOC_DIR ${SDK_DIR}/SoC/gd32vf103)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR rv32imac-ilp32 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/riscv_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS GD32VF103 CACHE INTERNAL "")
+
+set(JLINK_IF jtag)
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    message(FATAL_ERROR "LD_FILE_GNU is not defined")
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  if (NOT DEFINED STARTUP_FILE_GNU)
+    set(STARTUP_FILE_GNU
+      ${SOC_DIR}/Common/Source/GCC/startup_gd32vf103.S
+      ${SOC_DIR}/Common/Source/GCC/intexc_gd32vf103.S
+      )
+  endif ()
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/system_gd32vf103.c
+    ${SOC_DIR}/Common/Source/Drivers/gd32vf103_rcu.c
+    ${SOC_DIR}/Common/Source/Drivers/gd32vf103_gpio.c
+    ${SOC_DIR}/Common/Source/Drivers/Usb/gd32vf103_usb_hw.c
+    ${SOC_DIR}/Common/Source/Drivers/gd32vf103_usart.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_DIR}/NMSIS/Core/Include
+    ${SOC_DIR}/Common/Include
+    ${SOC_DIR}/Common/Include/Usb
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    DOWNLOAD_MODE=DOWNLOAD_MODE_FLASHXIP
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(${BOARD_TARGET} PUBLIC
+      -mcmodel=medlow
+      -mstrict-align
+      )
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -nostartfiles
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    message(FATAL_ERROR "Clang is not supported for MSP432E4")
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    ${SOC_DIR}/Common/Source/Stubs/sbrk.c
+    ${SOC_DIR}/Common/Source/Stubs/close.c
+    ${SOC_DIR}/Common/Source/Stubs/isatty.c
+    ${SOC_DIR}/Common/Source/Stubs/fstat.c
+    ${SOC_DIR}/Common/Source/Stubs/lseek.c
+    ${SOC_DIR}/Common/Source/Stubs/read.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_GD32VF103 ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/synopsys/dwc2/dcd_dwc2.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_flash_jlink(${TARGET})
+endfunction()
diff --git a/hw/bsp/gd32vf103/family.mk b/hw/bsp/gd32vf103/family.mk
index 1725559c4..48588886c 100644
--- a/hw/bsp/gd32vf103/family.mk
+++ b/hw/bsp/gd32vf103/family.mk
@@ -4,12 +4,11 @@
 # Toolchain from https://nucleisys.com/download.php
 #CROSS_COMPILE ?= riscv-nuclei-elf-
 
-# Toolchain from https://github.com/xpack-dev-tools/riscv-none-embed-gcc-xpack
-CROSS_COMPILE ?= riscv-none-embed-
+# Toolchain from https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack
+CROSS_COMPILE ?= riscv-none-elf-
 
 # Submodules
 NUCLEI_SDK = hw/mcu/gd/nuclei-sdk
-DEPS_SUBMODULES += $(NUCLEI_SDK)
 
 # Nuclei-SDK paths
 GD32VF103_SDK_SOC = $(NUCLEI_SDK)/SoC/gd32vf103
@@ -18,12 +17,9 @@ LIBC_STUBS = $(GD32VF103_SDK_SOC)/Common/Source/Stubs
 STARTUP_ASM = $(GD32VF103_SDK_SOC)/Common/Source/GCC
 
 include $(TOP)/$(BOARD_PATH)/board.mk
-
-SKIP_NANOLIB = 1
+CPU_CORE ?= rv32imac-ilp32
 
 CFLAGS += \
-	-march=rv32imac \
-	-mabi=ilp32 \
 	-mcmodel=medlow \
 	-mstrict-align \
 	-nostdlib -nostartfiles \
@@ -35,10 +31,10 @@ CFLAGS += -Wno-error=unused-parameter
 
 SRC_C += \
 	src/portable/synopsys/dwc2/dcd_dwc2.c \
-	$(GD32VF103_SDK_DRIVER)/gd32vf103_rcu.c \
 	$(GD32VF103_SDK_DRIVER)/gd32vf103_gpio.c \
-	$(GD32VF103_SDK_DRIVER)/Usb/gd32vf103_usb_hw.c \
+	$(GD32VF103_SDK_DRIVER)/gd32vf103_rcu.c \
 	$(GD32VF103_SDK_DRIVER)/gd32vf103_usart.c \
+	$(GD32VF103_SDK_DRIVER)/Usb/gd32vf103_usb_hw.c \
 	$(LIBC_STUBS)/sbrk.c \
 	$(LIBC_STUBS)/close.c \
 	$(LIBC_STUBS)/isatty.c \
diff --git a/hw/bsp/imxrt/family.c b/hw/bsp/imxrt/family.c
index 8ed72aa19..2f305aa4d 100644
--- a/hw/bsp/imxrt/family.c
+++ b/hw/bsp/imxrt/family.c
@@ -40,6 +40,7 @@
 #include "fsl_iomuxc.h"
 #include "fsl_clock.h"
 #include "fsl_lpuart.h"
+#include "fsl_ocotp.h"
 
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
@@ -186,6 +187,29 @@ uint32_t board_button_read(void) {
   return BUTTON_STATE_ACTIVE == GPIO_PinRead(BUTTON_PORT, BUTTON_PIN);
 }
 
+size_t board_get_unique_id(uint8_t id[], size_t max_len) {
+  (void) max_len;
+
+  #if FSL_FEATURE_OCOTP_HAS_TIMING_CTRL
+  OCOTP_Init(OCOTP, CLOCK_GetFreq(kCLOCK_IpgClk));
+  #else
+  OCOTP_Init(OCOTP, 0u);
+  #endif
+
+  // Reads shadow registers 0x01 - 0x04 (Configuration and Manufacturing Info)
+  // into 8 bit wide destination, avoiding punning.
+  for (int i = 0; i < 4; ++i) {
+    uint32_t wr = OCOTP_ReadFuseShadowRegister(OCOTP, i + 1);
+    for (int j = 0; j < 4; j++) {
+      id[i*4+j] = wr & 0xff;
+      wr >>= 8;
+    }
+  }
+  OCOTP_Deinit(OCOTP);
+
+  return 16;
+}
+
 int board_uart_read(uint8_t* buf, int len) {
   int count = 0;
 
@@ -224,3 +248,22 @@ uint32_t board_millis(void) {
   return system_ticks;
 }
 #endif
+
+
+#ifndef __ICCARM__
+// Implement _start() since we use linker flag '-nostartfiles'.
+// Requires defined __STARTUP_CLEAR_BSS,
+extern int main(void);
+TU_ATTR_UNUSED void _start(void) {
+  // called by startup code
+  main();
+  while (1) {}
+}
+
+#ifdef __clang__
+void	_exit (int __status) {
+  while (1) {}
+}
+#endif
+
+#endif
diff --git a/hw/bsp/imxrt/family.cmake b/hw/bsp/imxrt/family.cmake
index 090014754..d917e9777 100644
--- a/hw/bsp/imxrt/family.cmake
+++ b/hw/bsp/imxrt/family.cmake
@@ -1,9 +1,5 @@
 include_guard()
 
-if (NOT BOARD)
-  message(FATAL_ERROR "BOARD not specified")
-endif ()
-
 set(SDK_DIR ${TOP}/hw/mcu/nxp/mcux-sdk)
 set(CMSIS_DIR ${TOP}/lib/CMSIS_5)
 
@@ -26,7 +22,21 @@ function(add_board_target BOARD_TARGET)
     return()
   endif ()
 
+  # LD_FILE and STARTUP_FILE can be defined in board.cmake
+  if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
+    set(LD_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_VARIANT}xxxxx${MCU_CORE}_flexspi_nor.ld)
+    #set(LD_FILE_IAR ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_VARIANT}xxxxx_flexspi_nor.ld)
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
+    set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_VARIANT_WITH_CORE}.S)
+    #set(STARTUP_FILE_IAR ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_VARIANT_WITH_CORE}.S)
+  endif ()
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
   add_library(${BOARD_TARGET} STATIC
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}/board/clock_config.c
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}/board/pin_mux.c
     ${SDK_DIR}/drivers/common/fsl_common.c
@@ -34,6 +44,7 @@ function(add_board_target BOARD_TARGET)
     ${SDK_DIR}/drivers/igpio/fsl_gpio.c
     ${SDK_DIR}/drivers/lpspi/fsl_lpspi.c
     ${SDK_DIR}/drivers/lpuart/fsl_lpuart.c
+    ${SDK_DIR}/drivers/ocotp/fsl_ocotp.c
     ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_VARIANT_WITH_CORE}.c
     ${SDK_DIR}/devices/${MCU_VARIANT}/xip/fsl_flexspi_nor_boot.c
     ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
@@ -52,6 +63,7 @@ function(add_board_target BOARD_TARGET)
     __ARMFPV5__=0
     XIP_EXTERNAL_FLASH=1
     XIP_BOOT_HEADER_ENABLE=1
+    __STARTUP_CLEAR_BSS
     )
   target_include_directories(${BOARD_TARGET} PUBLIC
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
@@ -64,35 +76,26 @@ function(add_board_target BOARD_TARGET)
     ${SDK_DIR}/drivers/igpio
     ${SDK_DIR}/drivers/lpspi
     ${SDK_DIR}/drivers/lpuart
+    ${SDK_DIR}/drivers/ocotp
     )
 
   update_board(${BOARD_TARGET})
 
-  # LD_FILE and STARTUP_FILE can be defined in board.cmake
-  if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
-    set(LD_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_VARIANT}xxxxx${MCU_CORE}_flexspi_nor.ld)
-    #set(LD_FILE_IAR ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_VARIANT}xxxxx_flexspi_nor.ld)
-  endif ()
-
-  if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
-    set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_VARIANT_WITH_CORE}.S)
-    #set(STARTUP_FILE_IAR ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_VARIANT_WITH_CORE}.S)
-  endif ()
-
-  target_sources(${BOARD_TARGET} PUBLIC
-    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
-    )
-
   if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
     target_link_options(${BOARD_TARGET} PUBLIC
       "LINKER:--script=${LD_FILE_GNU}"
-      # nanolib
-      --specs=nosys.specs
-      --specs=nano.specs
+      -nostartfiles
+      --specs=nosys.specs --specs=nano.specs
       # force linker to look for these symbols
       -Wl,-uimage_vector_table
       -Wl,-ug_boot_data
       )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -Wl,-uimage_vector_table
+      -Wl,-ug_boot_data
+      )
   elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
     target_link_options(${BOARD_TARGET} PUBLIC
       "LINKER:--config=${LD_FILE_IAR}"
diff --git a/hw/bsp/imxrt/family.mk b/hw/bsp/imxrt/family.mk
index c6adcba5a..f00afb6a4 100644
--- a/hw/bsp/imxrt/family.mk
+++ b/hw/bsp/imxrt/family.mk
@@ -11,6 +11,7 @@ MCU_DIR = $(SDK_DIR)/devices/$(MCU_VARIANT)
 CFLAGS += \
   -D__ARMVFP__=0 \
   -D__ARMFPV5__=0 \
+  -D__STARTUP_CLEAR_BSS \
   -DXIP_EXTERNAL_FLASH=1 \
   -DXIP_BOOT_HEADER_ENABLE=1 \
   -DCFG_TUSB_MCU=OPT_MCU_MIMXRT1XXX
@@ -26,7 +27,9 @@ endif
 # mcu driver cause following warnings
 CFLAGS += -Wno-error=unused-parameter -Wno-error=implicit-fallthrough -Wno-error=redundant-decls
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += \
+  -nostartfiles \
+  --specs=nosys.specs --specs=nano.specs
 
 # All source paths should be relative to the top level.
 LD_FILE ?= $(MCU_DIR)/gcc/$(MCU_VARIANT)xxxxx${MCU_CORE}_flexspi_nor.ld
@@ -47,7 +50,8 @@ SRC_C += \
 	$(SDK_DIR)/drivers/common/fsl_common.c \
 	$(SDK_DIR)/drivers/common/fsl_common_arm.c \
 	$(SDK_DIR)/drivers/igpio/fsl_gpio.c \
-	$(SDK_DIR)/drivers/lpuart/fsl_lpuart.c
+	$(SDK_DIR)/drivers/lpuart/fsl_lpuart.c \
+	$(SDK_DIR)/drivers/ocotp/fsl_ocotp.c \
 
 # Optional drivers: only available for some mcus: rt1160, rt1170
 ifneq (,$(wildcard ${TOP}/${MCU_DIR}/drivers/fsl_dcdc.c))
@@ -65,7 +69,8 @@ INC += \
 	$(TOP)/$(MCU_DIR)/drivers \
 	$(TOP)/$(SDK_DIR)/drivers/common \
 	$(TOP)/$(SDK_DIR)/drivers/igpio \
-	$(TOP)/$(SDK_DIR)/drivers/lpuart
+	$(TOP)/$(SDK_DIR)/drivers/lpuart \
+	$(TOP)/$(SDK_DIR)/drivers/ocotp \
 
 SRC_S += $(MCU_DIR)/gcc/startup_$(MCU_VARIANT_WITH_CORE).S
 
diff --git a/hw/bsp/kinetis_k/family.c b/hw/bsp/kinetis_k/family.c
index 6df29c1fc..30dfe6d76 100644
--- a/hw/bsp/kinetis_k/family.c
+++ b/hw/bsp/kinetis_k/family.c
@@ -140,5 +140,23 @@ void SysTick_Handler(void) {
 uint32_t board_millis(void) {
   return system_ticks;
 }
+#endif
+
+
+#ifndef __ICCARM__
+// Implement _start() since we use linker flag '-nostartfiles'.
+// Requires defined __STARTUP_CLEAR_BSS,
+extern int main(void);
+TU_ATTR_UNUSED void _start(void) {
+  // called by startup code
+  main();
+  while (1) {}
+}
+
+#ifdef __clang__
+void	_exit (int __status) {
+  while (1) {}
+}
+#endif
 
 #endif
diff --git a/hw/bsp/kinetis_k/family.cmake b/hw/bsp/kinetis_k/family.cmake
index 452ef4b26..771754ebd 100644
--- a/hw/bsp/kinetis_k/family.cmake
+++ b/hw/bsp/kinetis_k/family.cmake
@@ -22,48 +22,53 @@ set(FAMILY_MCUS KINETIS_K CACHE INTERNAL "")
 #------------------------------------
 # only need to be built ONCE for all examples
 function(add_board_target BOARD_TARGET)
-  if (NOT TARGET ${BOARD_TARGET})
-    add_library(${BOARD_TARGET} STATIC
-      # driver
-      ${SDK_DIR}/drivers/gpio/fsl_gpio.c
-      ${SDK_DIR}/drivers/uart/fsl_uart.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_VARIANT}.c
-      )
-    target_compile_definitions(${BOARD_TARGET} PUBLIC
-      )
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${CMSIS_DIR}/CMSIS/Core/Include
-      ${SDK_DIR}/devices/${MCU_VARIANT}
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
-      ${SDK_DIR}/drivers/common
-      ${SDK_DIR}/drivers/gpio
-      ${SDK_DIR}/drivers/port
-      ${SDK_DIR}/drivers/smc
-      ${SDK_DIR}/drivers/sysmpu
-      ${SDK_DIR}/drivers/uart
-      )
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
 
-    update_board(${BOARD_TARGET})
+  # LD_FILE and STARTUP_FILE can be defined in board.cmake
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+  set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_VARIANT}.S)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
 
-    # LD_FILE and STARTUP_FILE can be defined in board.cmake
-    set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_VARIANT}.S)
+  add_library(${BOARD_TARGET} STATIC
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    ${SDK_DIR}/drivers/gpio/fsl_gpio.c
+    ${SDK_DIR}/drivers/uart/fsl_uart.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_VARIANT}.c
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    __STARTUP_CLEAR_BSS
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMSIS_DIR}/CMSIS/Core/Include
+    ${SDK_DIR}/devices/${MCU_VARIANT}
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
+    ${SDK_DIR}/drivers/common
+    ${SDK_DIR}/drivers/gpio
+    ${SDK_DIR}/drivers/port
+    ${SDK_DIR}/drivers/smc
+    ${SDK_DIR}/drivers/sysmpu
+    ${SDK_DIR}/drivers/uart
+    )
 
-    target_sources(${BOARD_TARGET} PUBLIC
-      ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      --specs=nosys.specs --specs=nano.specs
+      -nostartfiles
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
       )
-
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--script=${LD_FILE_GNU}"
-        # nanolib
-        --specs=nosys.specs --specs=nano.specs
-        )
-    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--config=${LD_FILE_IAR}"
-        )
-    endif ()
   endif ()
 endfunction()
 
diff --git a/hw/bsp/kinetis_k/family.mk b/hw/bsp/kinetis_k/family.mk
index 995873eec..844ce332e 100644
--- a/hw/bsp/kinetis_k/family.mk
+++ b/hw/bsp/kinetis_k/family.mk
@@ -6,9 +6,12 @@ include $(TOP)/$(BOARD_PATH)/board.mk
 CPU_CORE ?= cortex-m4
 
 CFLAGS += \
+  -D__STARTUP_CLEAR_BSS \
   -DCFG_TUSB_MCU=OPT_MCU_KINETIS_K \
 
 LDFLAGS += \
+  -nostartfiles \
+  --specs=nosys.specs --specs=nano.specs \
   -Wl,--defsym,__stack_size__=0x400 \
   -Wl,--defsym,__heap_size__=0
 
diff --git a/hw/bsp/kinetis_k32l2/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/kinetis_k32l2/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..e6604d360
--- /dev/null
+++ b/hw/bsp/kinetis_k32l2/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,169 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "fsl_device_registers.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#if defined(__ARM_FP) && __ARM_FP >= 4
+  #define configENABLE_FPU                      1
+#else
+  #define configENABLE_FPU                      0
+#endif
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               2
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* Define to trap errors during development. */
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
+  #define configASSERT(_exp) \
+    do {\
+      if ( !(_exp) ) { \
+        volatile uint32_t* ARM_CM_DHCSR =  ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
+        if ( (*ARM_CM_DHCSR) & 1UL ) {  /* Only halt mcu if debugger is attached */ \
+          taskDISABLE_INTERRUPTS(); \
+           __asm("BKPT #0\n"); \
+        }\
+      }\
+    } while(0)
+#else
+  #define configASSERT( x )
+#endif
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       2
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<SOPT5 = ((SIM->SOPT5 &
+                 /* Mask bits to zero which are setting */
+                 (~(SIM_SOPT5_LPUART0TXSRC_MASK | SIM_SOPT5_LPUART0RXSRC_MASK)))
+                /* LPUART0 Transmit Data Source Select: LPUART0_TX pin. */
+                | SIM_SOPT5_LPUART0TXSRC(SOPT5_LPUART0TXSRC_LPUART_TX)
+                /* LPUART0 Receive Data Source Select: LPUART_RX pin. */
+                | SIM_SOPT5_LPUART0RXSRC(SOPT5_LPUART0RXSRC_LPUART_RX));
+
+  BOARD_BootClockRUN();
+  SystemCoreClockUpdate();
+  CLOCK_SetLpuart0Clock(1);
+}
 
 #endif /* BOARD_H_ */
diff --git a/hw/bsp/kinetis_k32l2/boards/frdm_k32l2b/clock_config.c b/hw/bsp/kinetis_k32l2/boards/frdm_k32l2b/clock_config.c
index e74000827..86eb42ef8 100644
--- a/hw/bsp/kinetis_k32l2/boards/frdm_k32l2b/clock_config.c
+++ b/hw/bsp/kinetis_k32l2/boards/frdm_k32l2b/clock_config.c
@@ -48,7 +48,7 @@ board: FRDM-K32L2B
  * Variables
  ******************************************************************************/
 /* System clock frequency. */
-extern uint32_t SystemCoreClock;
+//extern uint32_t SystemCoreClock;
 
 /*******************************************************************************
  ************************ BOARD_InitBootClocks function ************************
diff --git a/hw/bsp/kinetis_k32l2/boards/frdm_k32l2b/frdm_k32l2b.c b/hw/bsp/kinetis_k32l2/boards/frdm_k32l2b/frdm_k32l2b.c
deleted file mode 100644
index 3f99b0cbd..000000000
--- a/hw/bsp/kinetis_k32l2/boards/frdm_k32l2b/frdm_k32l2b.c
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2018, hathach (tinyusb.org)
- * Copyright (c) 2020, Koji Kitayama
- *
- * 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 file is part of the TinyUSB stack.
- */
-
-#include "bsp/board_api.h"
-#include "board.h"
-#include "fsl_gpio.h"
-#include "fsl_port.h"
-#include "fsl_clock.h"
-#include "fsl_lpuart.h"
-
-#include "clock_config.h"
-
-//--------------------------------------------------------------------+
-// Forward USB interrupt events to TinyUSB IRQ Handler
-//--------------------------------------------------------------------+
-void USB0_IRQHandler(void)
-{
-  tud_int_handler(0);
-}
-
-void board_init(void)
-{
-  /* Enable port clocks for UART/LED/Button pins */
-  CLOCK_EnableClock(UART_PIN_CLOCK);
-  CLOCK_EnableClock(LED_PIN_CLOCK);
-  CLOCK_EnableClock(BUTTON_PIN_CLOCK);
-
-  gpio_pin_config_t led_config = { kGPIO_DigitalOutput, 0 };
-  GPIO_PinInit(LED_GPIO, LED_PIN, &led_config);
-  PORT_SetPinMux(LED_PORT, LED_PIN, kPORT_MuxAsGpio);
-
-  gpio_pin_config_t button_config = { kGPIO_DigitalInput, 0 };
-  GPIO_PinInit(BUTTON_GPIO, BUTTON_PIN, &button_config);
-  const port_pin_config_t BUTTON_CFG = {
-    kPORT_PullUp,
-    kPORT_FastSlewRate,
-    kPORT_PassiveFilterDisable,
-    kPORT_LowDriveStrength,
-    kPORT_MuxAsGpio
-  };
-  PORT_SetPinConfig(BUTTON_PORT, BUTTON_PIN, &BUTTON_CFG);
-
-  /* PORTA1 (pin 23) is configured as LPUART0_RX */
-  PORT_SetPinMux(PORTA, 1U, kPORT_MuxAlt2);
-  /* PORTA2 (pin 24) is configured as LPUART0_TX */
-  PORT_SetPinMux(PORTA, 2U, kPORT_MuxAlt2);
-
-  SIM->SOPT5 = ((SIM->SOPT5 &
-               /* Mask bits to zero which are setting */
-               (~(SIM_SOPT5_LPUART0TXSRC_MASK | SIM_SOPT5_LPUART0RXSRC_MASK)))
-               /* LPUART0 Transmit Data Source Select: LPUART0_TX pin. */
-               | SIM_SOPT5_LPUART0TXSRC(SOPT5_LPUART0TXSRC_LPUART_TX)
-               /* LPUART0 Receive Data Source Select: LPUART_RX pin. */
-               | SIM_SOPT5_LPUART0RXSRC(SOPT5_LPUART0RXSRC_LPUART_RX));
-
-  BOARD_BootClockRUN();
-  SystemCoreClockUpdate();
-  CLOCK_SetLpuart0Clock(1);
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-  // 1ms tick timer
-  SysTick_Config(SystemCoreClock / 1000);
-#elif CFG_TUSB_OS == OPT_OS_FREERTOS
-  // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
-  NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
-#endif
-
-  lpuart_config_t uart_config;
-  LPUART_GetDefaultConfig(&uart_config);
-  uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE;
-  uart_config.enableTx = true;
-  uart_config.enableRx = true;
-  LPUART_Init(UART_PORT, &uart_config, CLOCK_GetFreq(kCLOCK_McgIrc48MClk));
-
-  // USB
-  CLOCK_EnableUsbfs0Clock(kCLOCK_UsbSrcIrc48M, 48000000U);
-}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
-  GPIO_PinWrite(LED_GPIO, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
-}
-
-uint32_t board_button_read(void)
-{
-  return BUTTON_STATE_ACTIVE == GPIO_PinRead(BUTTON_GPIO, BUTTON_PIN);
-}
-
-int board_uart_read(uint8_t* buf, int len)
-{
-  LPUART_ReadBlocking(UART_PORT, buf, len);
-  return len;
-}
-
-int board_uart_write(void const * buf, int len)
-{
-  LPUART_WriteBlocking(UART_PORT, (uint8_t const*) buf, len);
-  return len;
-}
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-void SysTick_Handler(void)
-{
-  system_ticks++;
-}
-
-uint32_t board_millis(void)
-{
-  return system_ticks;
-}
-#endif
diff --git a/hw/bsp/kinetis_k32l2/boards/kuiic/board.cmake b/hw/bsp/kinetis_k32l2/boards/kuiic/board.cmake
new file mode 100644
index 000000000..cf14000ac
--- /dev/null
+++ b/hw/bsp/kinetis_k32l2/boards/kuiic/board.cmake
@@ -0,0 +1,15 @@
+set(MCU_VARIANT K32L2B31A)
+
+set(JLINK_DEVICE K32L2B31xxxxA)
+set(PYOCD_TARGET K32L2B)
+
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/kuiic.ld)
+
+function(update_board TARGET)
+  target_sources(${TARGET} PUBLIC
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/clock_config.c
+    )
+  target_compile_definitions(${TARGET} PUBLIC
+    CPU_K32L2B31VLH0A
+    )
+endfunction()
diff --git a/hw/bsp/kinetis_k32l2/boards/kuiic/board.h b/hw/bsp/kinetis_k32l2/boards/kuiic/board.h
index 1e2d4f18b..ec3702376 100644
--- a/hw/bsp/kinetis_k32l2/boards/kuiic/board.h
+++ b/hw/bsp/kinetis_k32l2/boards/kuiic/board.h
@@ -30,6 +30,8 @@
 
 #include "fsl_device_registers.h"
 
+#define USB_CLOCK_SOURCE kCLOCK_UsbSrcIrc48M
+
 // LED
 #define LED_PIN_CLOCK         kCLOCK_PortA
 #define LED_GPIO              GPIOA
@@ -42,4 +44,22 @@
 #define UART_PIN_RX           3u
 #define UART_PIN_TX           0u
 
+#define UART_CLOCK_SOURCE_HZ  CLOCK_GetFreq(kCLOCK_McgIrc48MClk)
+
+static inline void BOARD_InitBootPins(void) {
+  /* PORTC3 is configured as LPUART0_RX */
+  PORT_SetPinMux(PORTC, 3U, kPORT_MuxAlt3);
+  /* PORTA2 (pin 24) is configured as LPUART0_TX */
+  PORT_SetPinMux(PORTE, 0U, kPORT_MuxAlt3);
+
+  SIM->SOPT5 = ((SIM->SOPT5 &
+                 /* Mask bits to zero which are setting */
+                 (~(SIM_SOPT5_LPUART1TXSRC_MASK | SIM_SOPT5_LPUART1RXSRC_MASK)))
+                /* LPUART0 Transmit Data Source Select: LPUART0_TX pin. */
+                | SIM_SOPT5_LPUART1TXSRC(SOPT5_LPUART1TXSRC_LPUART_TX)
+                /* LPUART0 Receive Data Source Select: LPUART_RX pin. */
+                | SIM_SOPT5_LPUART1RXSRC(SOPT5_LPUART1RXSRC_LPUART_RX));
+  CLOCK_SetLpuart1Clock(1);
+}
+
 #endif /* BOARD_H_ */
diff --git a/hw/bsp/kinetis_k32l2/boards/kuiic/board.mk b/hw/bsp/kinetis_k32l2/boards/kuiic/board.mk
index fc5bdeec8..2bc5b1e34 100644
--- a/hw/bsp/kinetis_k32l2/boards/kuiic/board.mk
+++ b/hw/bsp/kinetis_k32l2/boards/kuiic/board.mk
@@ -6,7 +6,7 @@ CFLAGS += -DCPU_K32L2B31VLH0A
 CFLAGS += -Wno-error=unused-parameter -Wno-error=redundant-decls
 
 # All source paths should be relative to the top level.
-LD_FILE = $(BOARD_PATH)/K32L2B31xxxxA_flash.ld
+LD_FILE = $(BOARD_PATH)/kuiic.ld
 
 # For flash-jlink target
 JLINK_DEVICE = K32L2B31xxxxA
diff --git a/hw/bsp/kinetis_k32l2/boards/kuiic/clock_config.c b/hw/bsp/kinetis_k32l2/boards/kuiic/clock_config.c
new file mode 100644
index 000000000..c1a6d1a8d
--- /dev/null
+++ b/hw/bsp/kinetis_k32l2/boards/kuiic/clock_config.c
@@ -0,0 +1,39 @@
+#include "clock_config.h"
+#include "fsl_clock.h"
+
+/*******************************************************************************
+ * Variables
+ ******************************************************************************/
+/* System clock frequency. */
+// extern uint32_t SystemCoreClock;
+
+/*******************************************************************************
+ * Variables for BOARD_BootClockRUN configuration
+ ******************************************************************************/
+const mcglite_config_t mcgliteConfig_BOARD_BootClockRUN = {
+    .outSrc          = kMCGLITE_ClkSrcHirc,  /* MCGOUTCLK source is HIRC */
+    .irclkEnableMode = kMCGLITE_IrclkEnable, /* MCGIRCLK enabled, MCGIRCLK disabled in STOP mode */
+    .ircs            = kMCGLITE_Lirc8M,      /* Slow internal reference (LIRC) 8 MHz clock selected */
+    .fcrdiv          = kMCGLITE_LircDivBy1,  /* Low-frequency Internal Reference Clock Divider: divided by 1 */
+    .lircDiv2        = kMCGLITE_LircDivBy1,  /* Second Low-frequency Internal Reference Clock Divider: divided by 1 */
+    .hircEnableInNotHircMode = true,         /* HIRC source is enabled */
+};
+const sim_clock_config_t simConfig_BOARD_BootClockRUN = {
+    .er32kSrc = SIM_OSC32KSEL_LPO_CLK,       /* OSC32KSEL select: LPO clock */
+    .clkdiv1  = 0x10000U,                    /* SIM_CLKDIV1 - OUTDIV1: /1, OUTDIV4: /2 */
+};
+
+/*******************************************************************************
+ * Code for BOARD_BootClockRUN configuration
+ ******************************************************************************/
+void BOARD_BootClockRUN(void)
+{
+  /* Set the system clock dividers in SIM to safe value. */
+  CLOCK_SetSimSafeDivs();
+  /* Set MCG to HIRC mode. */
+  CLOCK_SetMcgliteConfig(&mcgliteConfig_BOARD_BootClockRUN);
+  /* Set the clock configuration in SIM module. */
+  CLOCK_SetSimConfig(&simConfig_BOARD_BootClockRUN);
+  /* Set SystemCoreClock variable. */
+  SystemCoreClock = BOARD_BOOTCLOCKRUN_CORE_CLOCK;
+}
diff --git a/hw/bsp/kinetis_k32l2/boards/kuiic/clock_config.h b/hw/bsp/kinetis_k32l2/boards/kuiic/clock_config.h
new file mode 100644
index 000000000..920cad98f
--- /dev/null
+++ b/hw/bsp/kinetis_k32l2/boards/kuiic/clock_config.h
@@ -0,0 +1,14 @@
+#ifndef CLOCK_CONFIG_H
+#define CLOCK_CONFIG_H
+
+/*******************************************************************************
+ * Definitions
+ ******************************************************************************/
+#define SIM_OSC32KSEL_LPO_CLK         3U        /*!< OSC32KSEL select: LPO clock */
+#define SOPT5_LPUART1RXSRC_LPUART_RX  0x00u     /*!<@brief LPUART1 Receive Data Source Select: LPUART_RX pin */
+#define SOPT5_LPUART1TXSRC_LPUART_TX  0x00u     /*!<@brief LPUART1 Transmit Data Source Select: LPUART_TX pin */
+#define BOARD_BOOTCLOCKRUN_CORE_CLOCK 48000000U /*!< Core clock frequency: 48000000Hz */
+
+void BOARD_BootClockRUN(void);
+
+#endif
diff --git a/hw/bsp/kinetis_k32l2/boards/kuiic/kuiic.c b/hw/bsp/kinetis_k32l2/boards/kuiic/kuiic.c
deleted file mode 100644
index b83d5c820..000000000
--- a/hw/bsp/kinetis_k32l2/boards/kuiic/kuiic.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2018, hathach (tinyusb.org)
- * Copyright (c) 2020, Koji Kitayama
- *
- * 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 file is part of the TinyUSB stack.
- */
-
-#include "bsp/board_api.h"
-#include "board.h"
-#include "fsl_smc.h"
-#include "fsl_gpio.h"
-#include "fsl_port.h"
-#include "fsl_clock.h"
-#include "fsl_lpuart.h"
-
-/*******************************************************************************
- * Definitions
- ******************************************************************************/
-#define SIM_OSC32KSEL_LPO_CLK         3U        /*!< OSC32KSEL select: LPO clock */
-#define SOPT5_LPUART1RXSRC_LPUART_RX  0x00u     /*!<@brief LPUART1 Receive Data Source Select: LPUART_RX pin */
-#define SOPT5_LPUART1TXSRC_LPUART_TX  0x00u     /*!<@brief LPUART1 Transmit Data Source Select: LPUART_TX pin */
-#define BOARD_BOOTCLOCKRUN_CORE_CLOCK 48000000U /*!< Core clock frequency: 48000000Hz */
-
-/*******************************************************************************
- * Variables
- ******************************************************************************/
-/* System clock frequency. */
-// extern uint32_t SystemCoreClock;
-
-/*******************************************************************************
- * Variables for BOARD_BootClockRUN configuration
- ******************************************************************************/
-const mcglite_config_t mcgliteConfig_BOARD_BootClockRUN = {
-    .outSrc          = kMCGLITE_ClkSrcHirc,  /* MCGOUTCLK source is HIRC */
-    .irclkEnableMode = kMCGLITE_IrclkEnable, /* MCGIRCLK enabled, MCGIRCLK disabled in STOP mode */
-    .ircs            = kMCGLITE_Lirc8M,      /* Slow internal reference (LIRC) 8 MHz clock selected */
-    .fcrdiv          = kMCGLITE_LircDivBy1,  /* Low-frequency Internal Reference Clock Divider: divided by 1 */
-    .lircDiv2        = kMCGLITE_LircDivBy1,  /* Second Low-frequency Internal Reference Clock Divider: divided by 1 */
-    .hircEnableInNotHircMode = true,         /* HIRC source is enabled */
-};
-const sim_clock_config_t simConfig_BOARD_BootClockRUN = {
-    .er32kSrc = SIM_OSC32KSEL_LPO_CLK,       /* OSC32KSEL select: LPO clock */
-    .clkdiv1  = 0x10000U,                    /* SIM_CLKDIV1 - OUTDIV1: /1, OUTDIV4: /2 */
-};
-
-/*******************************************************************************
- * Code for BOARD_BootClockRUN configuration
- ******************************************************************************/
-void BOARD_BootClockRUN(void)
-{
-    /* Set the system clock dividers in SIM to safe value. */
-    CLOCK_SetSimSafeDivs();
-    /* Set MCG to HIRC mode. */
-    CLOCK_SetMcgliteConfig(&mcgliteConfig_BOARD_BootClockRUN);
-    /* Set the clock configuration in SIM module. */
-    CLOCK_SetSimConfig(&simConfig_BOARD_BootClockRUN);
-    /* Set SystemCoreClock variable. */
-    SystemCoreClock = BOARD_BOOTCLOCKRUN_CORE_CLOCK;
-}
-
-
-//--------------------------------------------------------------------+
-// Forward USB interrupt events to TinyUSB IRQ Handler
-//--------------------------------------------------------------------+
-void USB0_IRQHandler(void)
-{
-  tud_int_handler(0);
-}
-
-void board_init(void)
-{
-  /* Enable port clocks for GPIO pins */
-  CLOCK_EnableClock(kCLOCK_PortA);
-  CLOCK_EnableClock(kCLOCK_PortB);
-  CLOCK_EnableClock(kCLOCK_PortC);
-  CLOCK_EnableClock(kCLOCK_PortD);
-  CLOCK_EnableClock(kCLOCK_PortE);
-
-
-  gpio_pin_config_t led_config = { kGPIO_DigitalOutput, 1 };
-  GPIO_PinInit(GPIOA, 1U, &led_config);
-  PORT_SetPinMux(PORTA, 1U, kPORT_MuxAsGpio);
-  led_config.outputLogic = 0;
-  GPIO_PinInit(GPIOA, 2U, &led_config);
-  PORT_SetPinMux(PORTA, 2U, kPORT_MuxAsGpio);
-
-#ifdef BUTTON_PIN
-  gpio_pin_config_t button_config = { kGPIO_DigitalInput, 0 };
-  GPIO_PinInit(BUTTON_GPIO, BUTTON_PIN, &button_config);
-  const port_pin_config_t BUTTON_CFG = {
-    kPORT_PullUp,
-    kPORT_FastSlewRate,
-    kPORT_PassiveFilterDisable,
-    kPORT_LowDriveStrength,
-    kPORT_MuxAsGpio
-  };
-  PORT_SetPinConfig(BUTTON_PORT, BUTTON_PIN, &BUTTON_CFG);
-#endif
-
-  /* PORTC3 is configured as LPUART0_RX */
-  PORT_SetPinMux(PORTC, 3U, kPORT_MuxAlt3);
-  /* PORTA2 (pin 24) is configured as LPUART0_TX */
-  PORT_SetPinMux(PORTE, 0U, kPORT_MuxAlt3);
-
-  SIM->SOPT5 = ((SIM->SOPT5 &
-               /* Mask bits to zero which are setting */
-               (~(SIM_SOPT5_LPUART1TXSRC_MASK | SIM_SOPT5_LPUART1RXSRC_MASK)))
-               /* LPUART0 Transmit Data Source Select: LPUART0_TX pin. */
-               | SIM_SOPT5_LPUART1TXSRC(SOPT5_LPUART1TXSRC_LPUART_TX)
-               /* LPUART0 Receive Data Source Select: LPUART_RX pin. */
-               | SIM_SOPT5_LPUART1RXSRC(SOPT5_LPUART1RXSRC_LPUART_RX));
-
-  BOARD_BootClockRUN();
-  SystemCoreClockUpdate();
-  CLOCK_SetLpuart1Clock(1);
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-  // 1ms tick timer
-  SysTick_Config(SystemCoreClock / 1000);
-#elif CFG_TUSB_OS == OPT_OS_FREERTOS
-  // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
-  NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
-#endif
-
-  lpuart_config_t uart_config;
-  LPUART_GetDefaultConfig(&uart_config);
-  uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE;
-  uart_config.enableTx = true;
-  uart_config.enableRx = true;
-  LPUART_Init(UART_PORT, &uart_config, CLOCK_GetFreq(kCLOCK_McgIrc48MClk));
-
-  // USB
-  CLOCK_EnableUsbfs0Clock(kCLOCK_UsbSrcIrc48M, 48000000U);
-}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
-  if (state) {
-    LED_GPIO->PDDR |= GPIO_FIT_REG((1UL << LED_PIN));
-  } else {
-    LED_GPIO->PDDR &= GPIO_FIT_REG(~(1UL << LED_PIN));
-  }
-//  GPIO_PinWrite(GPIOA, 1, state ? LED_STATE_ON : (1-LED_STATE_ON) );
-//  GPIO_PinWrite(GPIOA, 2, state ? (1-LED_STATE_ON) : LED_STATE_ON );
-}
-
-uint32_t board_button_read(void)
-{
-#ifdef BUTTON_PIN
-  return BUTTON_STATE_ACTIVE == GPIO_PinRead(BUTTON_GPIO, BUTTON_PIN);
-#else
-  return 0;
-#endif
-}
-
-int board_uart_read(uint8_t* buf, int len)
-{
-  LPUART_ReadBlocking(UART_PORT, buf, len);
-  return len;
-}
-
-int board_uart_write(void const * buf, int len)
-{
-  LPUART_WriteBlocking(UART_PORT, (uint8_t const*) buf, len);
-  return len;
-}
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-void SysTick_Handler(void)
-{
-  system_ticks++;
-}
-
-uint32_t board_millis(void)
-{
-  return system_ticks;
-}
-#endif
diff --git a/hw/bsp/kinetis_k32l2/boards/kuiic/K32L2B31xxxxA_flash.ld b/hw/bsp/kinetis_k32l2/boards/kuiic/kuiic.ld
similarity index 100%
rename from hw/bsp/kinetis_k32l2/boards/kuiic/K32L2B31xxxxA_flash.ld
rename to hw/bsp/kinetis_k32l2/boards/kuiic/kuiic.ld
diff --git a/hw/bsp/kinetis_k32l2/boards/frdm_k32l2a4s/frdm_k32l2a4s.c b/hw/bsp/kinetis_k32l2/family.c
similarity index 60%
rename from hw/bsp/kinetis_k32l2/boards/frdm_k32l2a4s/frdm_k32l2a4s.c
rename to hw/bsp/kinetis_k32l2/family.c
index 39783b7e1..92f5ba6d3 100644
--- a/hw/bsp/kinetis_k32l2/boards/frdm_k32l2a4s/frdm_k32l2a4s.c
+++ b/hw/bsp/kinetis_k32l2/family.c
@@ -25,77 +25,57 @@
  * This file is part of the TinyUSB stack.
  */
 
-#include "bsp/board_api.h"
-#include "board.h"
 #include "fsl_gpio.h"
 #include "fsl_port.h"
 #include "fsl_clock.h"
 #include "fsl_lpuart.h"
 
 #include "clock_config.h"
+#include "bsp/board_api.h"
+#include "board.h"
+
 
 //--------------------------------------------------------------------+
 // Forward USB interrupt events to TinyUSB IRQ Handler
 //--------------------------------------------------------------------+
-void USB0_IRQHandler(void)
-{
+void USB0_IRQHandler(void) {
   tud_int_handler(0);
 }
 
-void board_init(void)
-{
-  /* Enable port clocks for UART/LED/Button pins */
-  CLOCK_EnableClock(UART_PIN_CLOCK);
-  CLOCK_EnableClock(LED_PIN_CLOCK);
-  CLOCK_EnableClock(BUTTON_PIN_CLOCK);
+void board_init(void) {
+  /* Enable port clocks for GPIO pins */
+  CLOCK_EnableClock(kCLOCK_PortA);
+  CLOCK_EnableClock(kCLOCK_PortB);
+  CLOCK_EnableClock(kCLOCK_PortC);
+  CLOCK_EnableClock(kCLOCK_PortD);
+  CLOCK_EnableClock(kCLOCK_PortE);
 
-  gpio_pin_config_t led_config = { kGPIO_DigitalOutput, 0 };
+  BOARD_InitBootPins();
+  BOARD_BootClockRUN();
+  SystemCoreClockUpdate();
+
+  gpio_pin_config_t led_config = {kGPIO_DigitalOutput, 0};
   GPIO_PinInit(LED_GPIO, LED_PIN, &led_config);
   PORT_SetPinMux(LED_PORT, LED_PIN, kPORT_MuxAsGpio);
 
-  gpio_pin_config_t button_config = { kGPIO_DigitalInput, 0 };
+#ifdef BUTTON_PIN
+  gpio_pin_config_t button_config = {kGPIO_DigitalInput, 0};
   GPIO_PinInit(BUTTON_GPIO, BUTTON_PIN, &button_config);
   const port_pin_config_t BUTTON_CFG = {
-    kPORT_PullUp,
-    kPORT_FastSlewRate,
-    kPORT_PassiveFilterDisable,
-    kPORT_OpenDrainDisable,
-    kPORT_LowDriveStrength,
-    kPORT_MuxAsGpio,
-    kPORT_UnlockRegister
+      kPORT_PullUp,
+      kPORT_FastSlewRate,
+      kPORT_PassiveFilterDisable,
+#if defined(FSL_FEATURE_PORT_HAS_OPEN_DRAIN) && FSL_FEATURE_PORT_HAS_OPEN_DRAIN
+      kPORT_OpenDrainDisable,
+#endif
+      kPORT_LowDriveStrength,
+      kPORT_MuxAsGpio,
+#if defined(FSL_FEATURE_PORT_HAS_PIN_CONTROL_LOCK) && FSL_FEATURE_PORT_HAS_PIN_CONTROL_LOCK
+      kPORT_UnlockRegister
+#endif
   };
   PORT_SetPinConfig(BUTTON_PORT, BUTTON_PIN, &BUTTON_CFG);
-
-  /*
-    Enable LPUART0 clock and configure port pins.
-    FIR clock is being used so the USB examples work.
-  */
-  PCC_LPUART0  = 0U;                          /* Clock must be off to set PCS */
-  PCC_LPUART0  = PCC_CLKCFG_PCS( 3U );        /* Select the clock. 1:OSCCLK/Bus Clock, 2:Slow IRC, 3: Fast IRC, 6: System PLL */
-  PCC_LPUART0 |= PCC_CLKCFG_CGC( 1U );        /* Enable LPUART */
-
-  /* PORTB16 (pin 62) is configured as LPUART0_RX */
-  gpio_pin_config_t const lpuart_config_rx = { kGPIO_DigitalInput, 0 };
-  GPIO_PinInit(UART_PIN_GPIO, UART_PIN_RX, &lpuart_config_rx);
-  const port_pin_config_t UART_CFG = {
-    kPORT_PullUp,
-    kPORT_FastSlewRate,
-    kPORT_PassiveFilterDisable,
-    kPORT_OpenDrainDisable,
-    kPORT_LowDriveStrength,
-    kPORT_MuxAsGpio,
-    kPORT_UnlockRegister
-  };
-  PORT_SetPinConfig(UART_PIN_PORT, UART_PIN_RX, &UART_CFG);
-  PORT_SetPinMux(   UART_PIN_PORT, UART_PIN_RX, kPORT_MuxAlt3);
-
-  /* PORTB17 (pin 63) is configured as LPUART0_TX */
-  gpio_pin_config_t const lpuart_config_tx = { kGPIO_DigitalOutput, 0 };
-  GPIO_PinInit(   UART_PIN_GPIO, UART_PIN_TX, &lpuart_config_tx);
-  PORT_SetPinMux( UART_PIN_PORT, UART_PIN_TX, kPORT_MuxAlt3);
-
-  BOARD_BootClockRUN();
-  SystemCoreClockUpdate();
+#endif
 
 #if CFG_TUSB_OS == OPT_OS_NONE
   // 1ms tick timer
@@ -110,28 +90,29 @@ void board_init(void)
   uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE;
   uart_config.enableTx = true;
   uart_config.enableRx = true;
-  LPUART_Init(UART_PORT, &uart_config, CLOCK_GetFreq(kCLOCK_ScgFircClk));
+  LPUART_Init(UART_PORT, &uart_config, UART_CLOCK_SOURCE_HZ);
 
   // USB
-  CLOCK_EnableUsbfs0Clock(kCLOCK_IpSrcFircAsync, 48000000U);
+  CLOCK_EnableUsbfs0Clock(USB_CLOCK_SOURCE, 48000000U);
 }
 
 //--------------------------------------------------------------------+
 // Board porting API
 //--------------------------------------------------------------------+
 
-void board_led_write(bool state)
-{
-  GPIO_PinWrite(LED_GPIO, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+void board_led_write(bool state) {
+  GPIO_PinWrite(LED_GPIO, LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON));
 }
 
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
+#ifdef BUTTON_PIN
   return BUTTON_STATE_ACTIVE == GPIO_PinRead(BUTTON_GPIO, BUTTON_PIN);
+#else
+  return 0;
+#endif
 }
 
-int board_uart_read(uint8_t* buf, int len)
-{
+int board_uart_read(uint8_t* buf, int len) {
 #if 0 /*
 	Use this version if want the LED to blink during BOARD=board_test,
 	without having to hit a key.
@@ -151,21 +132,39 @@ int board_uart_read(uint8_t* buf, int len)
 #endif
 }
 
-int board_uart_write(void const * buf, int len)
-{
+int board_uart_write(void const* buf, int len) {
   LPUART_WriteBlocking(UART_PORT, (uint8_t const*) buf, len);
   return len;
 }
 
 #if CFG_TUSB_OS == OPT_OS_NONE
 volatile uint32_t system_ticks = 0;
-void SysTick_Handler(void)
-{
+
+void SysTick_Handler(void) {
   system_ticks++;
 }
 
-uint32_t board_millis(void)
-{
+uint32_t board_millis(void) {
   return system_ticks;
 }
+
+#endif
+
+#ifndef __ICCARM__
+// Implement _start() since we use linker flag '-nostartfiles'.
+// Requires defined __STARTUP_CLEAR_BSS,
+extern int main(void);
+
+TU_ATTR_UNUSED void _start(void) {
+  // called by startup code
+  main();
+  while (1) {}
+}
+
+#ifdef __clang__
+void	_exit (int __status) {
+  while (1) {}
+}
+#endif
+
 #endif
diff --git a/hw/bsp/kinetis_k32l2/family.cmake b/hw/bsp/kinetis_k32l2/family.cmake
new file mode 100644
index 000000000..406ae99d3
--- /dev/null
+++ b/hw/bsp/kinetis_k32l2/family.cmake
@@ -0,0 +1,112 @@
+include_guard()
+
+set(SDK_DIR ${TOP}/hw/mcu/nxp/mcux-sdk)
+set(CMSIS_DIR ${TOP}/lib/CMSIS_5)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR cortex-m0plus CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS KINETIS_K32L CACHE INTERNAL "")
+
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
+
+  # LD_FILE and STARTUP_FILE can be defined in board.cmake
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+  set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_VARIANT}.S)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    ${SDK_DIR}/drivers/gpio/fsl_gpio.c
+    ${SDK_DIR}/drivers/lpuart/fsl_lpuart.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_VARIANT}.c
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    __STARTUP_CLEAR_BSS
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMSIS_DIR}/CMSIS/Core/Include
+    ${SDK_DIR}/devices/${MCU_VARIANT}
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
+    ${SDK_DIR}/drivers/common
+    ${SDK_DIR}/drivers/gpio
+    ${SDK_DIR}/drivers/lpuart
+    ${SDK_DIR}/drivers/port
+    ${SDK_DIR}/drivers/smc
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      --specs=nosys.specs --specs=nano.specs
+      -nostartfiles
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_KINETIS_K32L ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/nxp/khci/dcd_khci.c
+    ${TOP}/src/portable/nxp/khci/hcd_khci.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_flash_jlink(${TARGET})
+
+  if (DEFINED TEENSY_MCU)
+    family_add_bin_hex(${TARGET})
+    family_flash_teensy(${TARGET})
+  endif ()
+endfunction()
diff --git a/hw/bsp/kinetis_k32l2/family.mk b/hw/bsp/kinetis_k32l2/family.mk
index 0bfd57d29..e18348d4d 100644
--- a/hw/bsp/kinetis_k32l2/family.mk
+++ b/hw/bsp/kinetis_k32l2/family.mk
@@ -1,6 +1,5 @@
 UF2_FAMILY_ID = 0x7f83e793
 SDK_DIR = hw/mcu/nxp/mcux-sdk
-DEPS_SUBMODULES += $(SDK_DIR) lib/CMSIS_5
 MCU_DIR = $(SDK_DIR)/devices/$(MCU)
 
 include $(TOP)/$(BOARD_PATH)/board.mk
@@ -9,7 +8,9 @@ CPU_CORE ?= cortex-m0plus
 CFLAGS += \
 	-DCFG_TUSB_MCU=OPT_MCU_KINETIS_K32L
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += \
+  -nostartfiles \
+  -specs=nosys.specs -specs=nano.specs
 
 SRC_C += \
 	src/portable/nxp/khci/dcd_khci.c \
@@ -25,10 +26,10 @@ INC += \
 	$(TOP)/$(MCU_DIR) \
 	$(TOP)/$(MCU_DIR)/project_template \
 	$(TOP)/$(MCU_DIR)/drivers \
-	$(TOP)/$(SDK_DIR)/drivers/smc \
 	$(TOP)/$(SDK_DIR)/drivers/common \
 	$(TOP)/$(SDK_DIR)/drivers/gpio \
-	$(TOP)/$(SDK_DIR)/drivers/port \
 	$(TOP)/$(SDK_DIR)/drivers/lpuart \
+	$(TOP)/$(SDK_DIR)/drivers/port \
+	$(TOP)/$(SDK_DIR)/drivers/smc \
 
 SRC_S += $(MCU_DIR)/gcc/startup_$(MCU).S
diff --git a/hw/bsp/kinetis_kl/family.c b/hw/bsp/kinetis_kl/family.c
index c436be3e6..254a95176 100644
--- a/hw/bsp/kinetis_kl/family.c
+++ b/hw/bsp/kinetis_kl/family.c
@@ -141,3 +141,22 @@ uint32_t board_millis(void)
   return system_ticks;
 }
 #endif
+
+
+#ifndef __ICCARM__
+// Implement _start() since we use linker flag '-nostartfiles'.
+// Requires defined __STARTUP_CLEAR_BSS,
+extern int main(void);
+TU_ATTR_UNUSED void _start(void) {
+  // called by startup code
+  main();
+  while (1) {}
+}
+
+#ifdef __clang__
+void	_exit (int __status) {
+  while (1) {}
+}
+#endif
+
+#endif
diff --git a/hw/bsp/kinetis_kl/family.cmake b/hw/bsp/kinetis_kl/family.cmake
index 6d9b65df0..85a913d54 100644
--- a/hw/bsp/kinetis_kl/family.cmake
+++ b/hw/bsp/kinetis_kl/family.cmake
@@ -26,7 +26,12 @@ function(add_board_target BOARD_TARGET)
     return()
   endif ()
 
+  # LD_FILE and STARTUP_FILE can be defined in board.cmake
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
   add_library(${BOARD_TARGET} STATIC
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
     ${SDK_DIR}/drivers/gpio/fsl_gpio.c
     ${SDK_DIR}/drivers/lpsci/fsl_lpsci.c
     ${SDK_DIR}/drivers/uart/fsl_uart.c
@@ -34,6 +39,7 @@ function(add_board_target BOARD_TARGET)
     ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_VARIANT}.c
     )
   target_compile_definitions(${BOARD_TARGET} PUBLIC
+    __STARTUP_CLEAR_BSS
     )
   target_include_directories(${BOARD_TARGET} PUBLIC
     ${CMSIS_DIR}/CMSIS/Core/Include
@@ -48,16 +54,15 @@ function(add_board_target BOARD_TARGET)
     )
   update_board(${BOARD_TARGET})
 
-  # LD_FILE and STARTUP_FILE can be defined in board.cmake
-  target_sources(${BOARD_TARGET} PUBLIC
-    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
-    )
-
   if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
     target_link_options(${BOARD_TARGET} PUBLIC
       "LINKER:--script=${LD_FILE_GNU}"
-      # nanolib
       --specs=nosys.specs --specs=nano.specs
+      -nostartfiles
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
       )
   elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
     target_link_options(${BOARD_TARGET} PUBLIC
@@ -103,5 +108,4 @@ function(family_configure_example TARGET RTOS)
 
   # Flashing
   family_flash_jlink(${TARGET})
-  #family_flash_nxplink(${TARGET})
 endfunction()
diff --git a/hw/bsp/kinetis_kl/family.mk b/hw/bsp/kinetis_kl/family.mk
index e331d82b7..1fdce981a 100644
--- a/hw/bsp/kinetis_kl/family.mk
+++ b/hw/bsp/kinetis_kl/family.mk
@@ -6,9 +6,11 @@ include $(TOP)/$(BOARD_PATH)/board.mk
 CPU_CORE ?= cortex-m0plus
 
 CFLAGS += \
+  -D__STARTUP_CLEAR_BSS \
   -DCFG_TUSB_MCU=OPT_MCU_KINETIS_KL \
 
 LDFLAGS += \
+  -nostartfiles \
   -specs=nosys.specs -specs=nano.specs \
   -Wl,--defsym,__stack_size__=0x400 \
   -Wl,--defsym,__heap_size__=0
diff --git a/hw/bsp/lpc11/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/lpc11/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..27b2d76f5
--- /dev/null
+++ b/hw/bsp/lpc11/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,178 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #ifdef __GNUC__
+    #pragma GCC diagnostic push
+    #pragma GCC diagnostic ignored "-Wunused-parameter"
+  #endif
+
+  #include "chip.h"
+
+  #ifdef __GNUC__
+    #pragma GCC diagnostic pop
+  #endif
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#if defined(__ARM_FP) && __ARM_FP >= 4
+  #define configENABLE_FPU                      1
+#else
+  #define configENABLE_FPU                      0
+#endif
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               2
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* Define to trap errors during development. */
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
+  #define configASSERT(_exp) \
+    do {\
+      if ( !(_exp) ) { \
+        volatile uint32_t* ARM_CM_DHCSR =  ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
+        if ( (*ARM_CM_DHCSR) & 1UL ) {  /* Only halt mcu if debugger is attached */ \
+          taskDISABLE_INTERRUPTS(); \
+           __asm("BKPT #0\n"); \
+        }\
+      }\
+    } while(0)
+#else
+  #define configASSERT( x )
+#endif
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       2
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<= 4
+  #define configENABLE_FPU                      1
+#else
+  #define configENABLE_FPU                      0
+#endif
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               2
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* Define to trap errors during development. */
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
+  #define configASSERT(_exp) \
+    do {\
+      if ( !(_exp) ) { \
+        volatile uint32_t* ARM_CM_DHCSR =  ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
+        if ( (*ARM_CM_DHCSR) & 1UL ) {  /* Only halt mcu if debugger is attached */ \
+          taskDISABLE_INTERRUPTS(); \
+           __asm("BKPT #0\n"); \
+        }\
+      }\
+    } while(0)
+#else
+  #define configASSERT( x )
+#endif
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       3
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<= 4
+  #define configENABLE_FPU                      1
+#else
+  #define configENABLE_FPU                      0
+#endif
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               2
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* Define to trap errors during development. */
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
+  #define configASSERT(_exp) \
+    do {\
+      if ( !(_exp) ) { \
+        volatile uint32_t* ARM_CM_DHCSR =  ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
+        if ( (*ARM_CM_DHCSR) & 1UL ) {  /* Only halt mcu if debugger is attached */ \
+          taskDISABLE_INTERRUPTS(); \
+           __asm("BKPT #0\n"); \
+        }\
+      }\
+    } while(0)
+#else
+  #define configASSERT( x )
+#endif
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       3
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1< RamLoc32
 
     /* NOINIT section for RamAHB32 */
@@ -159,18 +159,36 @@ SECTIONS
          . = ALIGN(4) ;
         _end_noinit = .;
     } > RamLoc32
-    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);
+
+    /* hathach add heap section for clang */
+    .heap (NOLOAD): {
+        __heap_start = .;
+        __HeapBase = .;
+        __heap_base = .;
+        __end = .;
+        PROVIDE(end = .);
+        PROVIDE(_end = .);
+        PROVIDE(__end__ = .);
+        KEEP(*(.heap*))
+        __HeapLimit = .;
+        __heap_limit = .;
+        __heap_end = .;
+    } > RamLoc32
+
+/*    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);*/
     PROVIDE(_vStackTop = DEFINED(__user_stack_top) ? __user_stack_top : __top_RamLoc32 - 0);
 
     /* ## Create checksum value (used in startup) ## */
+    /* This cause issue with clang linker, so it is disabled */
+    /* MemManage_Handler, BusFault_Handler, UsageFault_Handler may not be defined */
     PROVIDE(__valid_user_code_checksum = 0 -
                                          (_vStackTop
                                          + (ResetISR + 1)
                                          + (NMI_Handler + 1)
                                          + (HardFault_Handler + 1)
-                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)   /* MemManage_Handler may not be defined */
-                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)     /* BusFault_Handler may not be defined */
-                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1) /* UsageFault_Handler may not be defined */
+                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)
+                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)
+                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1)
                                          ) );
 
     /* Provide basic symbols giving location and size of main text
diff --git a/hw/bsp/lpc17/boards/mbed1768/lpc1768.ld b/hw/bsp/lpc17/boards/mbed1768/lpc1768.ld
index 095bd3d92..a6c35c157 100644
--- a/hw/bsp/lpc17/boards/mbed1768/lpc1768.ld
+++ b/hw/bsp/lpc17/boards/mbed1768/lpc1768.ld
@@ -140,7 +140,7 @@ SECTIONS
         *(COMMON)
         . = ALIGN(4) ;
         _ebss = .;
-        PROVIDE(end = .);
+/*        PROVIDE(end = .);*/
     } > RamLoc32
 
     /* NOINIT section for RamAHB32 */
@@ -159,18 +159,36 @@ SECTIONS
          . = ALIGN(4) ;
         _end_noinit = .;
     } > RamLoc32
-    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);
+
+    /* hathach add heap section for clang */
+    .heap (NOLOAD): {
+        __heap_start = .;
+        __HeapBase = .;
+        __heap_base = .;
+        __end = .;
+        PROVIDE(end = .);
+        PROVIDE(_end = .);
+        PROVIDE(__end__ = .);
+        KEEP(*(.heap*))
+        __HeapLimit = .;
+        __heap_limit = .;
+        __heap_end = .;
+    } > RamLoc32
+
+/*    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);*/
     PROVIDE(_vStackTop = DEFINED(__user_stack_top) ? __user_stack_top : __top_RamLoc32 - 0);
 
     /* ## Create checksum value (used in startup) ## */
+    /* This cause issue with clang linker, so it is disabled */
+    /* MemManage_Handler, BusFault_Handler, UsageFault_Handler may not be defined */
     PROVIDE(__valid_user_code_checksum = 0 -
                                          (_vStackTop
                                          + (ResetISR + 1)
                                          + (NMI_Handler + 1)
                                          + (HardFault_Handler + 1)
-                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)   /* MemManage_Handler may not be defined */
-                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)     /* BusFault_Handler may not be defined */
-                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1) /* UsageFault_Handler may not be defined */
+                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)
+                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)
+                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1)
                                          ) );
 
     /* Provide basic symbols giving location and size of main text
diff --git a/hw/bsp/lpc17/family.cmake b/hw/bsp/lpc17/family.cmake
index 63ac3149c..cccfdac9f 100644
--- a/hw/bsp/lpc17/family.cmake
+++ b/hw/bsp/lpc17/family.cmake
@@ -1,10 +1,7 @@
 include_guard()
 
-if (NOT BOARD)
-  message(FATAL_ERROR "BOARD not specified")
-endif ()
-
 set(SDK_DIR ${TOP}/hw/mcu/nxp/lpcopen/lpc175x_6x/lpc_chip_175x_6x)
+set(CMSIS_DIR ${TOP}/lib/CMSIS_5)
 
 # include board specific
 include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
@@ -21,42 +18,46 @@ set(FAMILY_MCUS LPC175X_6X CACHE INTERNAL "")
 #------------------------------------
 # only need to be built ONCE for all examples
 function(add_board_target BOARD_TARGET)
-  if (NOT TARGET ${BOARD_TARGET})
-    add_library(${BOARD_TARGET} STATIC
-      ${SDK_DIR}/../gcc/cr_startup_lpc175x_6x.c
-      ${SDK_DIR}/src/chip_17xx_40xx.c
-      ${SDK_DIR}/src/clock_17xx_40xx.c
-      ${SDK_DIR}/src/gpio_17xx_40xx.c
-      ${SDK_DIR}/src/iocon_17xx_40xx.c
-      ${SDK_DIR}/src/sysctl_17xx_40xx.c
-      ${SDK_DIR}/src/sysinit_17xx_40xx.c
-      ${SDK_DIR}/src/uart_17xx_40xx.c
-      )
-    target_compile_options(${BOARD_TARGET} PUBLIC
-      -nostdlib
-      )
-    target_compile_definitions(${BOARD_TARGET} PUBLIC
-      __USE_LPCOPEN
-      CORE_M3
-      RTC_EV_SUPPORT=0
-      )
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${SDK_DIR}/inc
-      )
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
 
-    update_board(${BOARD_TARGET})
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/../gcc/cr_startup_lpc175x_6x.c
+    ${SDK_DIR}/src/chip_17xx_40xx.c
+    ${SDK_DIR}/src/clock_17xx_40xx.c
+    ${SDK_DIR}/src/gpio_17xx_40xx.c
+    ${SDK_DIR}/src/iocon_17xx_40xx.c
+    ${SDK_DIR}/src/sysctl_17xx_40xx.c
+    ${SDK_DIR}/src/sysinit_17xx_40xx.c
+    ${SDK_DIR}/src/uart_17xx_40xx.c
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    __USE_LPCOPEN
+    CORE_M3
+    RTC_EV_SUPPORT=0
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_DIR}/inc
+    ${CMSIS_DIR}/CMSIS/Core/Include
+    )
 
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--script=${LD_FILE_GNU}"
-        # nanolib
-        --specs=nosys.specs --specs=nano.specs
-        )
-    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--config=${LD_FILE_IAR}"
-        )
-    endif ()
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(${BOARD_TARGET} PUBLIC -nostdlib)
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      --specs=nosys.specs --specs=nano.specs
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
   endif ()
 endfunction()
 
diff --git a/hw/bsp/lpc17/family.mk b/hw/bsp/lpc17/family.mk
index 694b6cccf..d719a47b7 100644
--- a/hw/bsp/lpc17/family.mk
+++ b/hw/bsp/lpc17/family.mk
@@ -18,12 +18,12 @@ CFLAGS += -Wno-error=strict-prototypes -Wno-error=cast-qual
 # caused by freeRTOS port !!
 CFLAGS += -Wno-error=maybe-uninitialized
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += --specs=nosys.specs --specs=nano.specs
 
 SRC_C += \
 	src/portable/nxp/lpc17_40/dcd_lpc17_40.c \
-	src/portable/ohci/ohci.c \
 	src/portable/nxp/lpc17_40/hcd_lpc17_40.c \
+	src/portable/ohci/ohci.c \
 	$(MCU_DIR)/../gcc/cr_startup_lpc175x_6x.c \
 	$(MCU_DIR)/src/chip_17xx_40xx.c \
 	$(MCU_DIR)/src/clock_17xx_40xx.c \
@@ -36,3 +36,4 @@ SRC_C += \
 INC += \
 	$(TOP)/$(BOARD_PATH) \
 	$(TOP)/$(MCU_DIR)/inc \
+	$(TOP)/lib/CMSIS_5/CMSIS/Core/Include \
diff --git a/hw/bsp/lpc18/boards/lpcxpresso18s37/lpc1837.ld b/hw/bsp/lpc18/boards/lpcxpresso18s37/lpc1837.ld
index f1a3add8f..0dd971433 100644
--- a/hw/bsp/lpc18/boards/lpcxpresso18s37/lpc1837.ld
+++ b/hw/bsp/lpc18/boards/lpcxpresso18s37/lpc1837.ld
@@ -308,9 +308,24 @@ SECTIONS
         _ebss = .;
         PROVIDE(__end_bss_RAM = .) ;
         PROVIDE(__end_bss_RamLoc32 = .) ;
-        PROVIDE(end = .);
+/*        PROVIDE(end = .);*/
     } > RamLoc40 AT> RamLoc40 /* > RamLoc32 AT> RamLoc32 */
 
+    /* hathach add heap section for clang */
+    .heap (NOLOAD): {
+        __heap_start = .;
+        __HeapBase = .;
+        __heap_base = .;
+        __end = .;
+        PROVIDE(end = .);
+        PROVIDE(_end = .);
+        PROVIDE(__end__ = .);
+        KEEP(*(.heap*))
+        __HeapLimit = .;
+        __heap_limit = .;
+        __heap_end = .;
+    } > RamLoc40
+
     /* NOINIT section for RamLoc40 */
     .noinit_RAM2 (NOLOAD) : ALIGN(4)
     {
@@ -379,19 +394,21 @@ SECTIONS
        PROVIDE(__end_noinit_RAM = .) ;
        PROVIDE(__end_noinit_RamLoc32 = .) ;
     } > RamLoc32 AT> RamLoc32
-    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);
+/*    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);*/
     PROVIDE(_vStackTop = DEFINED(__user_stack_top) ? __user_stack_top : __top_RamLoc32 - 0);
 
     /* ## Create checksum value (used in startup) ## */
-    PROVIDE(__valid_user_code_checksum = 0 -
-                                         (_vStackTop
-                                         + (ResetISR + 1)
-                                         + (NMI_Handler + 1)
-                                         + (HardFault_Handler + 1)
-                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)   /* MemManage_Handler may not be defined */
-                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)     /* BusFault_Handler may not be defined */
-                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1) /* UsageFault_Handler may not be defined */
-                                         ) );
+    /* This cause issue with clang linker, so it is disabled */
+    /* MemManage_Handler, BusFault_Handler, UsageFault_Handler may not be defined */
+/*    PROVIDE(__valid_user_code_checksum = 0 -*/
+/*                                         (_vStackTop*/
+/*                                         + (ResetISR + 1)*/
+/*                                         + (NMI_Handler + 1)*/
+/*                                         + (HardFault_Handler + 1)*/
+/*                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)*/
+/*                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)*/
+/*                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1)*/
+/*                                         ) );*/
 
     /* Provide basic symbols giving location and size of main text
      * block, including initial values of RW data sections. Note that
diff --git a/hw/bsp/lpc18/boards/mcb1800/lpc1857.ld b/hw/bsp/lpc18/boards/mcb1800/lpc1857.ld
index 837dec364..143aa11ec 100644
--- a/hw/bsp/lpc18/boards/mcb1800/lpc1857.ld
+++ b/hw/bsp/lpc18/boards/mcb1800/lpc1857.ld
@@ -255,7 +255,7 @@ SECTIONS
         *(COMMON)
         . = ALIGN(4) ;
         _ebss = .;
-        PROVIDE(end = .);
+        /* PROVIDE(end = .); */
     } > RamLoc40 /* RamLoc32 */
 
     /* NOINIT section for RamLoc40 */
@@ -266,6 +266,21 @@ SECTIONS
        . = ALIGN(4) ;
     } > RamLoc40
 
+    /* hathach add heap section for clang */
+    .heap (NOLOAD): {
+        __heap_start = .;
+        __HeapBase = .;
+        __heap_base = .;
+        __end = .;
+        PROVIDE(end = .);
+        PROVIDE(_end = .);
+        PROVIDE(__end__ = .);
+        KEEP(*(.heap*))
+        __HeapLimit = .;
+        __heap_limit = .;
+        __heap_end = .;
+    } > RamLoc40
+
     /* NOINIT section for RamAHB32 */
     .noinit_RAM3 (NOLOAD) : ALIGN(4)
     {
@@ -298,19 +313,21 @@ SECTIONS
          . = ALIGN(4) ;
         _end_noinit = .;
     } > RamLoc32
-    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);
+/*    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);*/
     PROVIDE(_vStackTop = DEFINED(__user_stack_top) ? __user_stack_top : __top_RamLoc32 - 0);
 
     /* ## Create checksum value (used in startup) ## */
-    PROVIDE(__valid_user_code_checksum = 0 -
-                                         (_vStackTop
-                                         + (ResetISR + 1)
-                                         + (NMI_Handler + 1)
-                                         + (HardFault_Handler + 1)
-                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)   /* MemManage_Handler may not be defined */
-                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)     /* BusFault_Handler may not be defined */
-                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1) /* UsageFault_Handler may not be defined */
-                                         ) );
+    /* This cause issue with clang linker, so it is disabled */
+    /* MemManage_Handler, BusFault_Handler, UsageFault_Handler may not be defined */
+/*    PROVIDE(__valid_user_code_checksum = 0 -*/
+/*                                         (_vStackTop*/
+/*                                         + (ResetISR + 1)*/
+/*                                         + (NMI_Handler + 1)*/
+/*                                         + (HardFault_Handler + 1)*/
+/*                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)*/
+/*                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)*/
+/*                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1)*/
+/*                                         ) );*/
 
     /* Provide basic symbols giving location and size of main text
      * block, including initial values of RW data sections. Note that
diff --git a/hw/bsp/lpc18/family.cmake b/hw/bsp/lpc18/family.cmake
index 78f4c510b..309186667 100644
--- a/hw/bsp/lpc18/family.cmake
+++ b/hw/bsp/lpc18/family.cmake
@@ -1,10 +1,7 @@
 include_guard()
 
-if (NOT BOARD)
-  message(FATAL_ERROR "BOARD not specified")
-endif ()
-
 set(SDK_DIR ${TOP}/hw/mcu/nxp/lpcopen/lpc18xx/lpc_chip_18xx)
+set(CMSIS_5 ${TOP}/lib/CMSIS_5)
 
 # include board specific
 include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
@@ -21,41 +18,44 @@ set(FAMILY_MCUS LPC18XX CACHE INTERNAL "")
 #------------------------------------
 # only need to be built ONCE for all examples
 function(add_board_target BOARD_TARGET)
-  if (NOT TARGET ${BOARD_TARGET})
-    add_library(${BOARD_TARGET} STATIC
-      ${SDK_DIR}/../gcc/cr_startup_lpc18xx.c
-      ${SDK_DIR}/src/chip_18xx_43xx.c
-      ${SDK_DIR}/src/clock_18xx_43xx.c
-      ${SDK_DIR}/src/gpio_18xx_43xx.c
-      ${SDK_DIR}/src/sysinit_18xx_43xx.c
-      ${SDK_DIR}/src/uart_18xx_43xx.c
-      )
-    target_compile_options(${BOARD_TARGET} PUBLIC
-      -nostdlib
-      )
-    target_compile_definitions(${BOARD_TARGET} PUBLIC
-      __USE_LPCOPEN
-      CORE_M3
-      )
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${SDK_DIR}/inc
-      ${SDK_DIR}/inc/config_18xx
-      )
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
 
-    update_board(${BOARD_TARGET})
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/../gcc/cr_startup_lpc18xx.c
+    ${SDK_DIR}/src/chip_18xx_43xx.c
+    ${SDK_DIR}/src/clock_18xx_43xx.c
+    ${SDK_DIR}/src/gpio_18xx_43xx.c
+    ${SDK_DIR}/src/sysinit_18xx_43xx.c
+    ${SDK_DIR}/src/uart_18xx_43xx.c
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    __USE_LPCOPEN
+    CORE_M3
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_DIR}/inc
+    ${SDK_DIR}/inc/config_18xx
+    ${CMSIS_5}/CMSIS/Core/Include
+    )
 
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--script=${LD_FILE_GNU}"
-        # nanolib
-        --specs=nosys.specs
-        --specs=nano.specs
-        )
-    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--config=${LD_FILE_IAR}"
-        )
-    endif ()
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(${BOARD_TARGET} PUBLIC -nostdlib)
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      --specs=nosys.specs --specs=nano.specs
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
   endif ()
 endfunction()
 
@@ -97,5 +97,4 @@ function(family_configure_example TARGET RTOS)
 
   # Flashing
   family_flash_jlink(${TARGET})
-  #family_flash_nxplink(${TARGET})
 endfunction()
diff --git a/hw/bsp/lpc18/family.mk b/hw/bsp/lpc18/family.mk
index c36c903ae..f625e926a 100644
--- a/hw/bsp/lpc18/family.mk
+++ b/hw/bsp/lpc18/family.mk
@@ -14,7 +14,7 @@ CFLAGS += \
 # mcu driver cause following warnings
 CFLAGS += -Wno-error=unused-parameter -Wno-error=strict-prototypes -Wno-error=cast-qual
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += --specs=nosys.specs --specs=nano.specs
 
 SRC_C += \
 	src/portable/chipidea/ci_hs/dcd_ci_hs.c \
@@ -30,4 +30,5 @@ SRC_C += \
 INC += \
 	$(TOP)/$(BOARD_PATH) \
 	$(TOP)/$(MCU_DIR)/inc \
-	$(TOP)/$(MCU_DIR)/inc/config_18xx
+	$(TOP)/$(MCU_DIR)/inc/config_18xx \
+	$(TOP)/lib/CMSIS_5/CMSIS/Core/Include \
diff --git a/hw/bsp/lpc40/boards/ea4088_quickstart/lpc4088.ld b/hw/bsp/lpc40/boards/ea4088_quickstart/lpc4088.ld
index 5897707f6..55b60e1e7 100644
--- a/hw/bsp/lpc40/boards/ea4088_quickstart/lpc4088.ld
+++ b/hw/bsp/lpc40/boards/ea4088_quickstart/lpc4088.ld
@@ -140,7 +140,7 @@ SECTIONS
         *(COMMON)
         . = ALIGN(4) ;
         _ebss = .;
-        PROVIDE(end = .);
+/*        PROVIDE(end = .);*/
     } > RamLoc64
 
     /* NOINIT section for RamPeriph32 */
@@ -159,7 +159,23 @@ SECTIONS
          . = ALIGN(4) ;
         _end_noinit = .;
     } > RamLoc64
-    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);
+
+    /* hathach add heap section for clang */
+    .heap (NOLOAD): {
+        __heap_start = .;
+        __HeapBase = .;
+        __heap_base = .;
+        __end = .;
+        PROVIDE(end = .);
+        PROVIDE(_end = .);
+        PROVIDE(__end__ = .);
+        KEEP(*(.heap*))
+        __HeapLimit = .;
+        __heap_limit = .;
+        __heap_end = .;
+    } > RamLoc64
+
+/*    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);*/
     PROVIDE(_vStackTop = DEFINED(__user_stack_top) ? __user_stack_top : __top_RamLoc64 - 0);
 
     /* ## Create checksum value (used in startup) ## */
diff --git a/hw/bsp/lpc40/family.cmake b/hw/bsp/lpc40/family.cmake
index fce9772b6..4c14da8a7 100644
--- a/hw/bsp/lpc40/family.cmake
+++ b/hw/bsp/lpc40/family.cmake
@@ -1,6 +1,7 @@
 include_guard()
 
 set(SDK_DIR ${TOP}/hw/mcu/nxp/lpcopen/lpc40xx/lpc_chip_40xx)
+set(CMSIS_DIR ${TOP}/lib/CMSIS_5)
 
 # include board specific
 include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
@@ -9,7 +10,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
 set(CMAKE_SYSTEM_PROCESSOR cortex-m4 CACHE INTERNAL "System Processor")
 set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
 
-set(FAMILY_MCUS LPC18XX CACHE INTERNAL "")
+set(FAMILY_MCUS LPC40XX CACHE INTERNAL "")
 
 
 #------------------------------------
@@ -32,9 +33,6 @@ function(add_board_target BOARD_TARGET)
     ${SDK_DIR}/src/sysinit_17xx_40xx.c
     ${SDK_DIR}/src/uart_17xx_40xx.c
     )
-  target_compile_options(${BOARD_TARGET} PUBLIC
-    -nostdlib
-    )
   target_compile_definitions(${BOARD_TARGET} PUBLIC
     __USE_LPCOPEN
     CORE_M4
@@ -42,16 +40,20 @@ function(add_board_target BOARD_TARGET)
     )
   target_include_directories(${BOARD_TARGET} PUBLIC
     ${SDK_DIR}/inc
+    ${CMSIS_DIR}/CMSIS/Core/Include
     )
 
   update_board(${BOARD_TARGET})
 
   if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_compile_options(${BOARD_TARGET} PUBLIC -nostdlib)
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      --specs=nosys.specs --specs=nano.specs
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
     target_link_options(${BOARD_TARGET} PUBLIC
       "LINKER:--script=${LD_FILE_GNU}"
-      # nanolib
-      --specs=nosys.specs
-      --specs=nano.specs
       )
   elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
     target_link_options(${BOARD_TARGET} PUBLIC
diff --git a/hw/bsp/lpc40/family.mk b/hw/bsp/lpc40/family.mk
index fa76789e3..ef9fe57b2 100644
--- a/hw/bsp/lpc40/family.mk
+++ b/hw/bsp/lpc40/family.mk
@@ -15,11 +15,13 @@ CFLAGS += \
 # mcu driver cause following warnings
 CFLAGS += -Wno-error=strict-prototypes -Wno-error=unused-parameter -Wno-error=cast-qual
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += --specs=nosys.specs --specs=nano.specs
 
 # All source paths should be relative to the top level.
 SRC_C += \
 	src/portable/nxp/lpc17_40/dcd_lpc17_40.c \
+	src/portable/nxp/lpc17_40/hcd_lpc17_40.c \
+	src/portable/ohci/ohci.c \
 	$(MCU_DIR)/../gcc/cr_startup_lpc40xx.c \
 	$(MCU_DIR)/src/chip_17xx_40xx.c \
 	$(MCU_DIR)/src/clock_17xx_40xx.c \
@@ -32,4 +34,5 @@ SRC_C += \
 
 INC += \
 	$(TOP)/$(MCU_DIR)/inc \
+	$(TOP)/lib/CMSIS_5/CMSIS/Core/Include \
 	$(TOP)/$(BOARD_PATH)
diff --git a/hw/bsp/lpc43/boards/ea4357/lpc4357.ld b/hw/bsp/lpc43/boards/ea4357/lpc4357.ld
index 53343f6a9..002dcaaed 100644
--- a/hw/bsp/lpc43/boards/ea4357/lpc4357.ld
+++ b/hw/bsp/lpc43/boards/ea4357/lpc4357.ld
@@ -256,7 +256,7 @@ SECTIONS
         *(COMMON)
         . = ALIGN(4) ;
         _ebss = .;
-        PROVIDE(end = .);
+        /* PROVIDE(end = .); */
     } > RamLoc40 /* RamLoc32 */
 
     /* NOINIT section for RamLoc40 */
@@ -267,6 +267,21 @@ SECTIONS
        . = ALIGN(4) ;
     } > RamLoc40
 
+    /* hathach add heap section for clang */
+    .heap (NOLOAD): {
+        __heap_start = .;
+        __HeapBase = .;
+        __heap_base = .;
+        __end = .;
+        PROVIDE(end = .);
+        PROVIDE(_end = .);
+        PROVIDE(__end__ = .);
+        KEEP(*(.heap*))
+        __HeapLimit = .;
+        __heap_limit = .;
+        __heap_end = .;
+    } > RamLoc40
+
     /* NOINIT section for RamAHB32 */
     .noinit_RAM3 (NOLOAD) : ALIGN(4)
     {
@@ -299,19 +314,21 @@ SECTIONS
          . = ALIGN(4) ;
         _end_noinit = .;
     } > RamLoc32
-    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);
+/*    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);*/
     PROVIDE(_vStackTop = DEFINED(__user_stack_top) ? __user_stack_top : __top_RamLoc32 - 0);
 
     /* ## Create checksum value (used in startup) ## */
-    PROVIDE(__valid_user_code_checksum = 0 -
-                                         (_vStackTop
-                                         + (ResetISR + 1)
-                                         + (NMI_Handler + 1)
-                                         + (HardFault_Handler + 1)
-                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)   /* MemManage_Handler may not be defined */
-                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)     /* BusFault_Handler may not be defined */
-                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1) /* UsageFault_Handler may not be defined */
-                                         ) );
+    /* This cause issue with clang linker, so it is disabled */
+    /* MemManage_Handler, BusFault_Handler, UsageFault_Hander may not be defined */
+/*    PROVIDE(__valid_user_code_checksum = 0 -*/
+/*                                         (_vStackTop*/
+/*                                         + (ResetISR + 1)*/
+/*                                         + (NMI_Handler + 1)*/
+/*                                         + (HardFault_Handler + 1)*/
+/*                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)*/
+/*                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)*/
+/*                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1)*/
+/*                                         ) );*/
 
     /* Provide basic symbols giving location and size of main text
      * block, including initial values of RW data sections. Note that
diff --git a/hw/bsp/lpc43/boards/lpcxpresso43s67/lpc4367.ld b/hw/bsp/lpc43/boards/lpcxpresso43s67/lpc4367.ld
index 99276fb47..f19350e00 100644
--- a/hw/bsp/lpc43/boards/lpcxpresso43s67/lpc4367.ld
+++ b/hw/bsp/lpc43/boards/lpcxpresso43s67/lpc4367.ld
@@ -304,9 +304,24 @@ SECTIONS
         _ebss = .;
         PROVIDE(__end_bss_RAM = .) ;
         PROVIDE(__end_bss_RamLoc32 = .) ;
-        PROVIDE(end = .);
+/*        PROVIDE(end = .);*/
     } > RamLoc40 AT> RamLoc40 /* > RamLoc32 AT> RamLoc32 */
 
+    /* hathach add heap section for clang */
+    .heap (NOLOAD): {
+        __heap_start = .;
+        __HeapBase = .;
+        __heap_base = .;
+        __end = .;
+        PROVIDE(end = .);
+        PROVIDE(_end = .);
+        PROVIDE(__end__ = .);
+        KEEP(*(.heap*))
+        __HeapLimit = .;
+        __heap_limit = .;
+        __heap_end = .;
+    } > RamLoc40
+
     /* NOINIT section for RamLoc40 */
     .noinit_RAM2 (NOLOAD) : ALIGN(4)
     {
@@ -376,20 +391,22 @@ SECTIONS
        PROVIDE(__end_noinit_RamLoc32 = .) ;
     } > RamLoc32 AT> RamLoc32
 
-    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);
+/*    PROVIDE(_pvHeapStart = DEFINED(__user_heap_base) ? __user_heap_base : .);*/
 
     PROVIDE(_vStackTop = DEFINED(__user_stack_top) ? __user_stack_top : __top_RamLoc32 - 0);
 
     /* ## Create checksum value (used in startup) ## */
-    PROVIDE(__valid_user_code_checksum = 0 -
-                                         (_vStackTop
-                                         + (ResetISR + 1)
-                                         + (NMI_Handler + 1)
-                                         + (HardFault_Handler + 1)
-                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)   /* MemManage_Handler may not be defined */
-                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)     /* BusFault_Handler may not be defined */
-                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1) /* UsageFault_Handler may not be defined */
-                                         ) );
+    /* This cause issue with clang linker, so it is disabled */
+    /* MemManage_Handler, BusFault_Handler, UsageFault_Hander may not be defined */
+/*    PROVIDE(__valid_user_code_checksum = 0 -*/
+/*                                         (_vStackTop*/
+/*                                         + (ResetISR + 1)*/
+/*                                         + (NMI_Handler + 1)*/
+/*                                         + (HardFault_Handler + 1)*/
+/*                                         + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)*/
+/*                                         + (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)*/
+/*                                         + (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1)*/
+/*                                         ) );*/
 
     /* Provide basic symbols giving location and size of main text
      * block, including initial values of RW data sections. Note that
diff --git a/hw/bsp/lpc43/family.cmake b/hw/bsp/lpc43/family.cmake
index f66da98aa..2bacd9ea4 100644
--- a/hw/bsp/lpc43/family.cmake
+++ b/hw/bsp/lpc43/family.cmake
@@ -1,6 +1,7 @@
 include_guard()
 
 set(SDK_DIR ${TOP}/hw/mcu/nxp/lpcopen/lpc43xx/lpc_chip_43xx)
+set(CMSIS_5 ${TOP}/lib/CMSIS_5)
 
 # include board specific
 include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
@@ -23,6 +24,7 @@ function(add_board_target BOARD_TARGET)
 
   # Startup & Linker script
   set(STARTUP_FILE_GNU ${SDK_DIR}/../gcc/cr_startup_lpc43xx.c)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
   set(STARTUP_FILE_IAR ${SDK_DIR}/../iar/iar_startup_lpc18xx43xx.s)
   set(LD_FILE_IAR ${SDK_DIR}/../iar/linker/lpc18xx_43xx_ldscript_iflash.icf)
 
@@ -43,6 +45,7 @@ function(add_board_target BOARD_TARGET)
   target_include_directories(${BOARD_TARGET} PUBLIC
     ${SDK_DIR}/inc
     ${SDK_DIR}/inc/config_43xx
+    ${CMSIS_5}/CMSIS/Core/Include
     )
 
   update_board(${BOARD_TARGET})
@@ -51,9 +54,11 @@ function(add_board_target BOARD_TARGET)
     target_compile_options(${BOARD_TARGET} PUBLIC -nostdlib)
     target_link_options(${BOARD_TARGET} PUBLIC
       "LINKER:--script=${LD_FILE_GNU}"
-      # nanolib
-      --specs=nosys.specs
-      --specs=nano.specs
+      --specs=nosys.specs --specs=nano.specs
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
       )
   elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
     target_link_options(${BOARD_TARGET} PUBLIC
diff --git a/hw/bsp/lpc43/family.mk b/hw/bsp/lpc43/family.mk
index f24385139..84e7c30b3 100644
--- a/hw/bsp/lpc43/family.mk
+++ b/hw/bsp/lpc43/family.mk
@@ -18,7 +18,7 @@ CFLAGS += \
   -Wno-error=cast-qual \
   -Wno-error=incompatible-pointer-types \
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += --specs=nosys.specs --specs=nano.specs
 
 SRC_C += \
 	src/portable/chipidea/ci_hs/dcd_ci_hs.c \
@@ -36,4 +36,5 @@ SRC_C += \
 INC += \
   $(TOP)/$(BOARD_PATH) \
 	${TOP}/${SDK_DIR}/inc \
-	${TOP}/${SDK_DIR}/inc/config_43xx
+	${TOP}/${SDK_DIR}/inc/config_43xx \
+	$(TOP)/lib/CMSIS_5/CMSIS/Core/Include \
diff --git a/hw/bsp/lpc51/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/lpc51/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..e6604d360
--- /dev/null
+++ b/hw/bsp/lpc51/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,169 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "fsl_device_registers.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#if defined(__ARM_FP) && __ARM_FP >= 4
+  #define configENABLE_FPU                      1
+#else
+  #define configENABLE_FPU                      0
+#endif
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               2
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* Define to trap errors during development. */
+// Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__)
+  #define configASSERT(_exp) \
+    do {\
+      if ( !(_exp) ) { \
+        volatile uint32_t* ARM_CM_DHCSR =  ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
+        if ( (*ARM_CM_DHCSR) & 1UL ) {  /* Only halt mcu if debugger is attached */ \
+          taskDISABLE_INTERRUPTS(); \
+           __asm("BKPT #0\n"); \
+        }\
+      }\
+    } while(0)
+#else
+  #define configASSERT( x )
+#endif
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       2
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<CTRL &= ~1U;
+
   // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
   NVIC_SetPriority(USB0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
+  NVIC_SetPriority(USB1_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY );
 #endif
 
   // Init all GPIO ports
@@ -138,7 +139,7 @@ void board_init(void)
 
   // LED
   IOCON_PinMuxSet(IOCON, LED_PORT, LED_PIN, IOCON_PIO_DIG_FUNC0_EN);
-  gpio_pin_config_t const led_config = { kGPIO_DigitalOutput, 1};
+  gpio_pin_config_t const led_config = {kGPIO_DigitalOutput, 1};
   GPIO_PinInit(GPIO, LED_PORT, LED_PIN, &led_config);
 
   board_led_write(0);
@@ -157,7 +158,7 @@ void board_init(void)
 
   // Button
   IOCON_PinMuxSet(IOCON, BUTTON_PORT, BUTTON_PIN, IOCON_PIO_DIG_FUNC0_EN);
-  gpio_pin_config_t const button_config = { kGPIO_DigitalInput, 0};
+  gpio_pin_config_t const button_config = {kGPIO_DigitalInput, 0};
   GPIO_PinInit(GPIO, BUTTON_PORT, BUTTON_PIN, &button_config);
 
 #ifdef UART_DEV
@@ -170,8 +171,8 @@ void board_init(void)
   usart_config_t uart_config;
   USART_GetDefaultConfig(&uart_config);
   uart_config.baudRate_Bps = CFG_BOARD_UART_BAUDRATE;
-  uart_config.enableTx     = true;
-  uart_config.enableRx     = true;
+  uart_config.enableTx = true;
+  uart_config.enableRx = true;
   USART_Init(UART_DEV, &uart_config, 12000000);
 #endif
 
@@ -243,6 +244,10 @@ void board_init(void)
 //  phytx &= ~(USBPHY_TX_D_CAL_MASK | USBPHY_TX_TXCAL45DM_MASK | USBPHY_TX_TXCAL45DP_MASK);
 //  phytx |= USBPHY_TX_D_CAL(0x0C) | USBPHY_TX_TXCAL45DP(0x06) | USBPHY_TX_TXCAL45DM(0x06);
 //  USBPHY->TX = phytx;
+
+    ARM_MPU_SetMemAttr(0, 0x44); // Normal memory, non-cacheable (inner and outer)
+    ARM_MPU_SetRegion(0, ARM_MPU_RBAR(0x40100000, ARM_MPU_SH_NON, 0, 1, 1), ARM_MPU_RLAR(0x40104000, 0));
+    ARM_MPU_Enable(MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_HFNMIENA_Msk);
 #endif
 }
 
@@ -250,9 +255,8 @@ void board_init(void)
 // Board porting API
 //--------------------------------------------------------------------+
 
-void board_led_write(bool state)
-{
-  GPIO_PinWrite(GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+void board_led_write(bool state) {
+  GPIO_PinWrite(GPIO, LED_PORT, LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON));
 
 #ifdef NEOPIXEL_PIN
   if (state) {
@@ -266,33 +270,50 @@ void board_led_write(bool state)
 #endif
 }
 
-uint32_t board_button_read(void)
-{
+uint32_t board_button_read(void) {
   // active low
   return BUTTON_STATE_ACTIVE == GPIO_PinRead(GPIO, BUTTON_PORT, BUTTON_PIN);
 }
 
-int board_uart_read(uint8_t* buf, int len)
-{
-  (void) buf; (void) len;
+int board_uart_read(uint8_t* buf, int len) {
+  (void) buf;
+  (void) len;
   return 0;
 }
 
-int board_uart_write(void const * buf, int len)
-{
-  USART_WriteBlocking(UART_DEV, (uint8_t const *) buf, len);
+int board_uart_write(void const* buf, int len) {
+  USART_WriteBlocking(UART_DEV, (uint8_t const*) buf, len);
   return len;
 }
 
 #if CFG_TUSB_OS == OPT_OS_NONE
 volatile uint32_t system_ticks = 0;
-void SysTick_Handler(void)
-{
+
+void SysTick_Handler(void) {
   system_ticks++;
 }
 
-uint32_t board_millis(void)
-{
+uint32_t board_millis(void) {
   return system_ticks;
 }
 #endif
+
+
+#ifndef __ICCARM__
+// Implement _start() since we use linker flag '-nostartfiles'.
+// Requires defined __STARTUP_CLEAR_BSS,
+extern int main(void);
+
+TU_ATTR_UNUSED void _start(void) {
+  // called by startup code
+  main();
+  while (1) {}
+}
+
+#ifdef __clang__
+void	_exit (int __status) {
+  while (1) {}
+}
+#endif
+
+#endif
diff --git a/hw/bsp/lpc55/family.cmake b/hw/bsp/lpc55/family.cmake
index 75dabfe5a..21c57fc1f 100644
--- a/hw/bsp/lpc55/family.cmake
+++ b/hw/bsp/lpc55/family.cmake
@@ -1,9 +1,5 @@
 include_guard()
 
-if (NOT BOARD)
-  message(FATAL_ERROR "BOARD not specified")
-endif ()
-
 set(SDK_DIR ${TOP}/hw/mcu/nxp/mcux-sdk)
 set(CMSIS_DIR ${TOP}/lib/CMSIS_5)
 
@@ -30,9 +26,20 @@ set(HOST_PORT $)
 function(add_board_target BOARD_TARGET)
   if (TARGET ${BOARD_TARGET})
     return()
+  endif()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_CORE}_flash.ld)
   endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  if (NOT DEFINED STARTUP_FILE_GNU)
+    set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_CORE}.S)
+  endif ()
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
 
   add_library(${BOARD_TARGET} STATIC
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
     # driver
     ${SDK_DIR}/drivers/lpc_gpio/fsl_gpio.c
     ${SDK_DIR}/drivers/common/fsl_common_arm.c
@@ -44,12 +51,27 @@ function(add_board_target BOARD_TARGET)
     ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_power.c
     ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_reset.c
     )
-
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${TOP}/lib/sct_neopixel
+    # driver
+    ${SDK_DIR}/drivers/common
+    ${SDK_DIR}/drivers/flexcomm
+    ${SDK_DIR}/drivers/lpc_iocon
+    ${SDK_DIR}/drivers/lpc_gpio
+    ${SDK_DIR}/drivers/lpuart
+    ${SDK_DIR}/drivers/sctimer
+    # mcu
+    ${SDK_DIR}/devices/${MCU_VARIANT}
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
+    ${CMSIS_DIR}/CMSIS/Core/Include
+    )
   target_compile_definitions(${BOARD_TARGET} PUBLIC
     CFG_TUSB_MEM_ALIGN=TU_ATTR_ALIGNED\(64\)
     BOARD_TUD_RHPORT=${PORT}
     BOARD_TUH_RHPORT=${HOST_PORT}
+    __STARTUP_CLEAR_BSS
     )
+
   # Port 0 is Fullspeed, Port 1 is Highspeed. Port1 controller can only access USB_SRAM
   if (PORT EQUAL 1)
     target_compile_definitions(${BOARD_TARGET} PUBLIC
@@ -65,42 +87,17 @@ function(add_board_target BOARD_TARGET)
       )
   endif ()
 
-  target_include_directories(${BOARD_TARGET} PUBLIC
-    ${TOP}/lib/sct_neopixel
-    # driver
-    ${SDK_DIR}/drivers/common
-    ${SDK_DIR}/drivers/flexcomm
-    ${SDK_DIR}/drivers/lpc_iocon
-    ${SDK_DIR}/drivers/lpc_gpio
-    ${SDK_DIR}/drivers/lpuart
-    ${SDK_DIR}/drivers/sctimer
-    # mcu
-    ${CMSIS_DIR}/CMSIS/Core/Include
-    ${SDK_DIR}/devices/${MCU_VARIANT}
-    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
-    )
-
   update_board(${BOARD_TARGET})
 
-  if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
-    set(LD_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_CORE}_flash.ld)
-  endif ()
-
-  if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
-    set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_CORE}.S)
-  endif ()
-
-  target_sources(${BOARD_TARGET} PUBLIC
-    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
-    )
-
   if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
     target_link_options(${BOARD_TARGET} PUBLIC
-      # linker file
       "LINKER:--script=${LD_FILE_GNU}"
-      # nanolib
-      --specs=nosys.specs
-      --specs=nano.specs
+      --specs=nosys.specs --specs=nano.specs
+      -nostartfiles
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_Clang}"
       )
   elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
     target_link_options(${BOARD_TARGET} PUBLIC
diff --git a/hw/bsp/lpc55/family.mk b/hw/bsp/lpc55/family.mk
index 1b97c43e4..d82e85904 100644
--- a/hw/bsp/lpc55/family.mk
+++ b/hw/bsp/lpc55/family.mk
@@ -11,9 +11,10 @@ PORT ?= 1
 
 CFLAGS += \
   -flto \
+  -D__STARTUP_CLEAR_BSS \
   -DCFG_TUSB_MCU=OPT_MCU_LPC55XX \
   -DCFG_TUSB_MEM_ALIGN='__attribute__((aligned(64)))' \
-  -DBOARD_TUD_RHPORT=$(PORT)
+  -DBOARD_TUD_RHPORT=$(PORT) \
 
 ifeq ($(PORT), 1)
   $(info "PORT1 High Speed")
@@ -28,7 +29,9 @@ endif
 # mcu driver cause following warnings
 CFLAGS += -Wno-error=unused-parameter -Wno-error=float-equal
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += \
+  -nostartfiles \
+  --specs=nosys.specs --specs=nano.specs \
 
 # All source paths should be relative to the top level.
 LD_FILE ?= $(MCU_DIR)/gcc/$(MCU_CORE)_flash.ld
diff --git a/hw/bsp/mcx/family.cmake b/hw/bsp/mcx/family.cmake
index c8591b569..223afb9ec 100644
--- a/hw/bsp/mcx/family.cmake
+++ b/hw/bsp/mcx/family.cmake
@@ -1,9 +1,5 @@
 include_guard()
 
-if (NOT BOARD)
-  message(FATAL_ERROR "BOARD not specified")
-endif ()
-
 set(SDK_DIR ${TOP}/hw/mcu/nxp/mcux-sdk)
 set(CMSIS_DIR ${TOP}/lib/CMSIS_5)
 
@@ -28,60 +24,63 @@ set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOL
 #------------------------------------
 # only need to be built ONCE for all examples
 function(add_board_target BOARD_TARGET)
-  if (NOT TARGET ${BOARD_TARGET})
-    add_library(${BOARD_TARGET} STATIC
-      # external driver
-      #lib/sct_neopixel/sct_neopixel.c
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
 
-      # driver
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_gpio.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_common_arm.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_lpuart.c
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_CORE}_flash.ld)
+  endif ()
+  set(LD_FILE_Clang ${LD_FILE_GNU})
 
-      # mcu
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_reset.c
-      ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_CORE}.c
+  if (NOT DEFINED STARTUP_FILE_GNU)
+    set(STARTUP_FILE_GNU ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_CORE}.S)
+  endif()
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    # driver
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_gpio.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_common_arm.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_lpuart.c
+    # mcu
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_reset.c
+    ${SDK_DIR}/devices/${MCU_VARIANT}/system_${MCU_CORE}.c
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMSIS_DIR}/CMSIS/Core/Include
+    ${SDK_DIR}/devices/${MCU_VARIANT}
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
+    )
+
+  if (${FAMILY_MCUS} STREQUAL "MCXN9")
+    target_sources(${BOARD_TARGET} PRIVATE
+      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_lpflexcomm.c
+    )
+  elseif(${FAMILY_MCUS} STREQUAL "MCXA15")
+    target_sources(${BOARD_TARGET} PRIVATE
+    ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_spc.c
+  )
+  endif()
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      --specs=nosys.specs --specs=nano.specs
+      #-nostartfiles
       )
-
-      if (${FAMILY_MCUS} STREQUAL "MCXN9")
-        target_sources(${BOARD_TARGET} PRIVATE
-          ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_lpflexcomm.c
-        )
-      elseif(${FAMILY_MCUS} STREQUAL "MCXA15")
-        target_sources(${BOARD_TARGET} PRIVATE
-        ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_spc.c
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_Clang}"
       )
-      endif()
-
-    #  target_compile_definitions(${BOARD_TARGET} PUBLIC
-    #    )
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      # driver
-      # mcu
-      ${CMSIS_DIR}/CMSIS/Core/Include
-      ${SDK_DIR}/devices/${MCU_VARIANT}
-      ${SDK_DIR}/devices/${MCU_VARIANT}/drivers
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
       )
-
-    update_board(${BOARD_TARGET})
-
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_sources(${BOARD_TARGET} PUBLIC
-        ${SDK_DIR}/devices/${MCU_VARIANT}/gcc/startup_${MCU_CORE}.S
-        )
-      target_link_options(${BOARD_TARGET} PUBLIC
-        # linker file
-        "LINKER:--script=${SDK_DIR}/devices/${MCU_VARIANT}/gcc/${MCU_CORE}_flash.ld"
-        # nanolib
-        --specs=nosys.specs
-        --specs=nano.specs
-        )
-    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--config=${LD_FILE_IAR}"
-        )
-    endif ()
   endif ()
 endfunction()
 
diff --git a/hw/bsp/mm32/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/mm32/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..6622cf801
--- /dev/null
+++ b/hw/bsp/mm32/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,150 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "mm32_device.h"
+  extern u32 SystemCoreClock;
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#define configENABLE_FPU                        0
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       4
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<CFGR &= ~(0x3 << 22);
-  RCC->CFGR |= (0x1 << 22);
-
-  /* Enable USB clock */
-  RCC->AHB2ENR |= 0x1 << 7;
-}
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
-// LED
-
-extern u32 SystemCoreClock;
-const int baudrate = 115200;
-
-void board_init (void)
-{
-//   usb clock
-  USB_DeviceClockInit();
-
-  if ( SysTick_Config(SystemCoreClock / 1000) )
-  {
-    while ( 1 )
-      ;
-  }
-  NVIC_SetPriority(SysTick_IRQn, 0x0);
-
-  // LED on PB2
-  GPIO_InitTypeDef GPIO_InitStruct;
-  RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);
-  GPIO_StructInit(&GPIO_InitStruct);
-
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2;
-  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
-  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
-  GPIO_Init(GPIOB, &GPIO_InitStruct);
-
-  board_led_write(true);
-
-  // KEY on PA0
-  RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
-  GPIO_StructInit(&GPIO_InitStruct);
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
-  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
-  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
-  GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-  // UART
-  UART_InitTypeDef UART_InitStruct;
-
-  RCC_APB2PeriphClockCmd(RCC_APB2ENR_UART1, ENABLE);    //enableUART1,GPIOAclock
-  RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);      //
-  //UART initialset
-
-  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_7);
-  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_7);
-
-  UART_StructInit(&UART_InitStruct);
-  UART_InitStruct.UART_BaudRate = baudrate;
-  UART_InitStruct.UART_WordLength = UART_WordLength_8b;
-  UART_InitStruct.UART_StopBits = UART_StopBits_1;    //one stopbit
-  UART_InitStruct.UART_Parity = UART_Parity_No;    //none odd-even  verify bit
-  UART_InitStruct.UART_HardwareFlowControl = UART_HardwareFlowControl_None;    //No hardware flow control
-  UART_InitStruct.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;    // receive and sent  mode
-
-  UART_Init(UART1, &UART_InitStruct);    //initial uart 1
-  UART_Cmd(UART1, ENABLE);                    //enable uart 1
-
-  //UART1_TX   GPIOA.9
-  GPIO_StructInit(&GPIO_InitStruct);
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
-  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
-  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
-  GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-  //UART1_RX    GPIOA.10
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
-  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
-  GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-}
-
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write (bool state)
-{
-  state ? (GPIO_ResetBits(GPIOB, GPIO_Pin_2)) : (GPIO_SetBits(GPIOB, GPIO_Pin_2));
-}
-
-uint32_t board_button_read (void)
-{
-  uint32_t key = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_SET;
-  return key;
-}
-
-int board_uart_read (uint8_t *buf, int len)
-{
-  (void) buf;
-  (void) len;
-  return 0;
-}
-
-int board_uart_write (void const *buf, int len)
-{
-  const char *buff = buf;
-  while ( len )
-  {
-    while ( (UART1->CSR & UART_IT_TXIEN) == 0 )
-      ;    //The loop is sent until it is finished
-    UART1->TDR = (*buff & 0xFF);
-    buff++;
-    len--;
-  }
-  return len;
-}
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-void SysTick_Handler (void)
-{
-  system_ticks++;
-}
-
-uint32_t board_millis (void)
-{
-  return system_ticks;
-}
-#endif
-
-// Required by __libc_init_array in startup code if we are compiling using
-// -nostdlib/-nostartfiles.
-void _init(void)
-{
-
-}
diff --git a/hw/bsp/mm32/boards/mm32f327x_mb39/board.cmake b/hw/bsp/mm32/boards/mm32f327x_mb39/board.cmake
new file mode 100644
index 000000000..4f3d145cf
--- /dev/null
+++ b/hw/bsp/mm32/boards/mm32f327x_mb39/board.cmake
@@ -0,0 +1,10 @@
+set(MCU_VARIANT mm32f327x)
+set(JLINK_DEVICE MM32F3273G9P)
+
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/flash.ld)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    HSE_VALUE=8000000
+    )
+endfunction()
diff --git a/hw/bsp/mm32/boards/mm32f327x_mb39/board.h b/hw/bsp/mm32/boards/mm32f327x_mb39/board.h
new file mode 100644
index 000000000..3ac048cf1
--- /dev/null
+++ b/hw/bsp/mm32/boards/mm32f327x_mb39/board.h
@@ -0,0 +1,19 @@
+#ifndef BOARD_H
+#define BOARD_H
+
+// GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_15); //Disable JTDI   AF to  AF15
+#define LED_PORT              GPIOA
+#define LED_PIN               GPIO_Pin_15
+#define LED_STATE_ON          1
+
+//#define BUTTON_PORT           GPIOC
+//#define BUTTON_PIN            GPIO_PIN_13
+//#define BUTTON_STATE_ACTIVE   1
+
+#define UART_DEV              UART1
+#define UART_GPIO_PORT        GPIOA
+#define UART_GPIO_AF          GPIO_AF_7
+#define UART_TX_PIN           9
+#define UART_RX_PIN           10
+
+#endif
diff --git a/hw/bsp/mm32/boards/mm32f327x_mb39/board.mk b/hw/bsp/mm32/boards/mm32f327x_mb39/board.mk
index a0d92d1c7..f6d18315d 100644
--- a/hw/bsp/mm32/boards/mm32f327x_mb39/board.mk
+++ b/hw/bsp/mm32/boards/mm32f327x_mb39/board.mk
@@ -1,12 +1,9 @@
+MCU_VARIANT = mm32f327x
 CFLAGS += \
 	-DHSE_VALUE=8000000
 
+JLINK_DEVICE = MM32F3273G9P
+
 LD_FILE = $(BOARD_PATH)/flash.ld
-SRC_S += $(SDK_DIR)/mm32f327x/MM32F327x/Source/GCC_StartAsm/startup_mm32m3ux_u_gcc.S
 
-
-# For flash-jlink target
-#JLINK_DEVICE = stm32f411ve
-
-# flash target using on-board stlink
-#flash: flash-jlink
+flash: flash-jlink
diff --git a/hw/bsp/mm32/boards/mm32f327x_mb39/mm32f327x_mb39.c b/hw/bsp/mm32/boards/mm32f327x_mb39/mm32f327x_mb39.c
deleted file mode 100644
index 086532179..000000000
--- a/hw/bsp/mm32/boards/mm32f327x_mb39/mm32f327x_mb39.c
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020 MM32 SE TEAM
- *
- * 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 file is part of the TinyUSB stack.
- */
-
-#include "mm32_device.h"
-#include "hal_conf.h"
-#include "tusb.h"
-#include "bsp/board_api.h"
-
-//--------------------------------------------------------------------+
-// Forward USB interrupt events to TinyUSB IRQ Handler
-//--------------------------------------------------------------------+
-void OTG_FS_IRQHandler (void)
-{
-  tud_int_handler(0);
-
-}
-void USB_DeviceClockInit (void)
-{
-  /* Select USBCLK source */
-  //  RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div1);
-  RCC->CFGR &= ~(0x3 << 22);
-  RCC->CFGR |= (0x1 << 22);
-
-  /* Enable USB clock */
-  RCC->AHB2ENR |= 0x1 << 7;
-}
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
-// LED
-
-extern u32 SystemCoreClock;
-const int baudrate = 115200;
-
-void board_init (void)
-{
-//   usb clock
-  USB_DeviceClockInit();
-
-  if ( SysTick_Config(SystemCoreClock / 1000) )
-  {
-    while ( 1 )
-      ;
-  }
-  NVIC_SetPriority(SysTick_IRQn, 0x0);
-
-  // LED
-  GPIO_InitTypeDef GPIO_InitStruct;
-  RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
-  GPIO_StructInit(&GPIO_InitStruct);
-  GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_15);                      //Disable JTDI   AF to  AF15
-
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
-  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
-  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
-  GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-  board_led_write(true);
-
-  // UART
-  UART_InitTypeDef UART_InitStruct;
-
-  RCC_APB2PeriphClockCmd(RCC_APB2ENR_UART1, ENABLE);    //enableUART1,GPIOAclock
-  RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);    //
-  //UART initialset
-
-  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_7);
-  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_7);
-
-  UART_StructInit(&UART_InitStruct);
-  UART_InitStruct.UART_BaudRate = baudrate;
-  UART_InitStruct.UART_WordLength = UART_WordLength_8b;
-  UART_InitStruct.UART_StopBits = UART_StopBits_1;    //one stopbit
-  UART_InitStruct.UART_Parity = UART_Parity_No;    //none odd-even  verify bit
-  UART_InitStruct.UART_HardwareFlowControl = UART_HardwareFlowControl_None;    //No hardware flow control
-  UART_InitStruct.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;    // receive and sent  mode
-
-  UART_Init(UART1, &UART_InitStruct);    //initial uart 1
-  UART_Cmd(UART1, ENABLE);                    //enable uart 1
-
-  //UART1_TX   GPIOA.9
-  GPIO_StructInit(&GPIO_InitStruct);
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
-  GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
-  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
-  GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-  //UART1_RX    GPIOA.10
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
-  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
-  GPIO_Init(GPIOA, &GPIO_InitStruct);
-
-}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write (bool state)
-{
-  state ? (GPIO_ResetBits(GPIOA, GPIO_Pin_15)) : (GPIO_SetBits(GPIOA, GPIO_Pin_15));
-}
-
-uint32_t board_button_read (void)
-{
-  return 0;
-}
-
-int board_uart_read (uint8_t *buf, int len)
-{
-  (void) buf;
-  (void) len;
-  return 0;
-}
-
-int board_uart_write (void const *buf, int len)
-{
-  const char *buff = buf;
-  while ( len )
-  {
-    while ( (UART1->CSR & UART_IT_TXIEN) == 0 )
-      ;    //The loop is sent until it is finished
-    UART1->TDR = (*buff & 0xFF);
-    buff++;
-    len--;
-  }
-  return len;
-}
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-void SysTick_Handler (void)
-{
-  system_ticks++;
-}
-
-uint32_t board_millis (void)
-{
-  return system_ticks;
-}
-#endif
-
-// Required by __libc_init_array in startup code if we are compiling using
-// -nostdlib/-nostartfiles.
-void _init(void)
-{
-
-}
diff --git a/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.cmake b/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.cmake
new file mode 100644
index 000000000..4de25e2c4
--- /dev/null
+++ b/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.cmake
@@ -0,0 +1,10 @@
+set(MCU_VARIANT mm32f327x)
+set(JLINK_DEVICE MM32F3273G8P)
+
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/flash.ld)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    HSE_VALUE=12000000
+    )
+endfunction()
diff --git a/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.h b/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.h
new file mode 100644
index 000000000..2b3f54a60
--- /dev/null
+++ b/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.h
@@ -0,0 +1,14 @@
+#ifndef BOARD_H
+#define BOARD_H
+
+// GPIO_PinAFConfig(GPIOA, GPIO_PinSource15, GPIO_AF_15); //Disable JTDI   AF to  AF15
+#define LED_PORT              GPIOA
+#define LED_PIN               GPIO_Pin_1
+#define LED_STATE_ON          1
+
+#define BUTTON_PORT           GPIOA
+#define BUTTON_PIN            GPIO_Pin_0
+#define BUTTON_STATE_ACTIVE   0
+
+
+#endif
diff --git a/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.mk b/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.mk
index a778e749f..dbcd314c8 100644
--- a/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.mk
+++ b/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/board.mk
@@ -1,11 +1,12 @@
+MCU_VARIANT = mm32f327x
+
 CFLAGS += \
 	-DHSE_VALUE=12000000
 
 LD_FILE = $(BOARD_PATH)/flash.ld
-SRC_S += $(SDK_DIR)/mm32f327x/MM32F327x/Source/GCC_StartAsm/startup_mm32m3ux_u_gcc.S
 
 # For flash-jlink target
-#JLINK_DEVICE = MM32F3273G8P
+JLINK_DEVICE = MM32F3273G8P
 
 # flash target using on-board stlink
 #flash: flash-jlink
diff --git a/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/mm32f327x_pitaya_lite.c b/hw/bsp/mm32/family.c
similarity index 61%
rename from hw/bsp/mm32/boards/mm32f327x_pitaya_lite/mm32f327x_pitaya_lite.c
rename to hw/bsp/mm32/family.c
index bd2d36ae0..f0fd6d334 100644
--- a/hw/bsp/mm32/boards/mm32f327x_pitaya_lite/mm32f327x_pitaya_lite.c
+++ b/hw/bsp/mm32/family.c
@@ -24,23 +24,35 @@
  * This file is part of the TinyUSB stack.
  */
 
-/* DshanMCU Pitaya Lite with MM32F3273 */
-
-#include "mm32_device.h"
 #include "hal_conf.h"
-#include "tusb.h"
+#include "mm32_device.h"
+
 #include "bsp/board_api.h"
+#include "board.h"
+
+//--------------------------------------------------------------------+
+// MACRO TYPEDEF CONSTANT ENUM DECLARATION
+//--------------------------------------------------------------------+
+
+#ifdef __GNUC__ // caused by extra declaration of SystemCoreClock in freeRTOSConfig.h
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
+extern u32 SystemCoreClock;
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
 
 //--------------------------------------------------------------------+
 // Forward USB interrupt events to TinyUSB IRQ Handler
 //--------------------------------------------------------------------+
-void OTG_FS_IRQHandler (void)
-{
+void OTG_FS_IRQHandler(void) {
   tud_int_handler(0);
-
 }
-void USB_DeviceClockInit (void)
-{
+
+void USB_DeviceClockInit(void) {
   /* Select USBCLK source */
   //  RCC_USBCLKConfig(RCC_USBCLKSource_PLLCLK_Div1);
   RCC->CFGR &= ~(0x3 << 22);
@@ -49,134 +61,117 @@ void USB_DeviceClockInit (void)
   /* Enable USB clock */
   RCC->AHB2ENR |= 0x1 << 7;
 }
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
-// LED
 
-extern u32 SystemCoreClock;
-const int baudrate = 115200;
-
-void board_init (void)
-{
+void board_init(void) {
 //   usb clock
-// requires SYSCLK_FREQ_XXMHz  (HSE_VALUE*8) in system_mm32f327x.c
   USB_DeviceClockInit();
 
-  if ( SysTick_Config(SystemCoreClock / 1000) )
-  {
-    while ( 1 )
-      ;
-  }
+  SysTick_Config(SystemCoreClock / 1000);
   NVIC_SetPriority(SysTick_IRQn, 0x0);
 
-  // LED on PA1
-  GPIO_InitTypeDef GPIO_InitStruct;
   RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);
-  GPIO_StructInit(&GPIO_InitStruct);
 
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
+  // LED
+  GPIO_InitTypeDef GPIO_InitStruct;
+  GPIO_StructInit(&GPIO_InitStruct);
+  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_15;
   GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
   GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
   GPIO_Init(GPIOA, &GPIO_InitStruct);
 
   board_led_write(true);
 
-  // KEY on PA0
+  #ifdef BUTTON_PORT
   GPIO_StructInit(&GPIO_InitStruct);
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
+  GPIO_InitStruct.GPIO_Pin = BUTTON_PIN;
   GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
-  GPIO_InitStruct.GPIO_Mode = GPIO_Mode_FLOATING;
-  GPIO_Init(GPIOA, &GPIO_InitStruct);
+  GPIO_InitStruct.GPIO_Mode = BUTTON_STATE_ACTIVE ? GPIO_Mode_IPD : GPIO_Mode_IPU;
+  GPIO_Init(BUTTON_PORT, &GPIO_InitStruct);
+  #endif
 
+  #ifdef UART_DEV
   // UART
   UART_InitTypeDef UART_InitStruct;
 
-  RCC_APB2PeriphClockCmd(RCC_APB2ENR_UART1, ENABLE);    //enableUART1,GPIOAclock
-  RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA, ENABLE);      //
-  //UART initialset
-
-  GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_7);
-  GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_7);
+  RCC_APB2PeriphClockCmd(RCC_APB2ENR_UART1, ENABLE);  //enableUART1,GPIOAclock
+  GPIO_PinAFConfig(GPIOA, UART_TX_PIN, UART_GPIO_AF);
+  GPIO_PinAFConfig(GPIOA, UART_RX_PIN, UART_GPIO_AF);
 
   UART_StructInit(&UART_InitStruct);
-  UART_InitStruct.UART_BaudRate = baudrate;
+  UART_InitStruct.UART_BaudRate = CFG_BOARD_UART_BAUDRATE;
   UART_InitStruct.UART_WordLength = UART_WordLength_8b;
-  UART_InitStruct.UART_StopBits = UART_StopBits_1;    //one stopbit
-  UART_InitStruct.UART_Parity = UART_Parity_No;    //none odd-even  verify bit
-  UART_InitStruct.UART_HardwareFlowControl = UART_HardwareFlowControl_None;    //No hardware flow control
-  UART_InitStruct.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;    // receive and sent  mode
+  UART_InitStruct.UART_StopBits = UART_StopBits_1;
+  UART_InitStruct.UART_Parity = UART_Parity_No;
+  UART_InitStruct.UART_HardwareFlowControl = UART_HardwareFlowControl_None;
+  UART_InitStruct.UART_Mode = UART_Mode_Rx | UART_Mode_Tx;
 
-  UART_Init(UART1, &UART_InitStruct);    //initial uart 1
-  UART_Cmd(UART1, ENABLE);                    //enable uart 1
+  UART_Init(UART_DEV, &UART_InitStruct);
+  UART_Cmd(UART_DEV, ENABLE);
 
-  //UART1_TX   GPIOA.9
   GPIO_StructInit(&GPIO_InitStruct);
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
+  GPIO_InitStruct.GPIO_Pin = 1 << UART_TX_PIN;
   GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
   GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
-  GPIO_Init(GPIOA, &GPIO_InitStruct);
+  GPIO_Init(UART_GPIO_PORT, &GPIO_InitStruct);
 
-  //UART1_RX    GPIOA.10
-  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_5;
+  GPIO_InitStruct.GPIO_Pin = 1 << UART_RX_PIN;
   GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
-  GPIO_Init(GPIOA, &GPIO_InitStruct);
-
+  GPIO_Init(UART_GPIO_PORT, &GPIO_InitStruct);
+  #endif
 }
 
-
 //--------------------------------------------------------------------+
 // Board porting API
 //--------------------------------------------------------------------+
 
-void board_led_write (bool state)
-{
-  state ? (GPIO_ResetBits(GPIOA, GPIO_Pin_1)) : (GPIO_SetBits(GPIOA, GPIO_Pin_1));
+void board_led_write(bool state) {
+  GPIO_WriteBit(LED_PORT, LED_PIN, state ? LED_STATE_ON : (1 - LED_STATE_ON));
 }
 
-uint32_t board_button_read (void)
-{
-  uint32_t key = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0) == Bit_RESET;
-  return key;
+uint32_t board_button_read(void) {
+#ifdef BUTTON_PORT
+  return GPIO_ReadInputDataBit(BUTTON_PORT, BUTTON_PIN) == BUTTON_STATE_ACTIVE;
+#else
+  return 0;
+#endif
 }
 
-int board_uart_read (uint8_t *buf, int len)
-{
+int board_uart_read(uint8_t* buf, int len) {
   (void) buf;
   (void) len;
   return 0;
 }
 
-int board_uart_write (void const *buf, int len)
-{
-  const char *buff = buf;
-  while ( len )
-  {
-    while ( (UART1->CSR & UART_IT_TXIEN) == 0 )
-      ;    //The loop is sent until it is finished
+int board_uart_write(void const* buf, int len) {
+  #ifdef UART_DEV
+  const char* buff = buf;
+  while (len) {
+    while ((UART1->CSR & UART_IT_TXIEN) == 0);    //The loop is sent until it is finished
     UART1->TDR = (*buff & 0xFF);
     buff++;
     len--;
   }
   return len;
+  #else
+  (void) buf;
+  (void) len;
+  return 0;
+  #endif
 }
 
 #if CFG_TUSB_OS == OPT_OS_NONE
 volatile uint32_t system_ticks = 0;
-void SysTick_Handler (void)
-{
+
+void SysTick_Handler(void) {
   system_ticks++;
 }
 
-uint32_t board_millis (void)
-{
+uint32_t board_millis(void) {
   return system_ticks;
 }
 #endif
 
 // Required by __libc_init_array in startup code if we are compiling using
 // -nostdlib/-nostartfiles.
-void _init(void)
-{
-
+void _init(void) {
 }
diff --git a/hw/bsp/mm32/family.cmake b/hw/bsp/mm32/family.cmake
new file mode 100644
index 000000000..bf315acaa
--- /dev/null
+++ b/hw/bsp/mm32/family.cmake
@@ -0,0 +1,101 @@
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+string(REPLACE "mm32f" "MM32F" MCU_VARIANT_UPPER ${MCU_VARIANT})
+set(SDK_DIR ${TOP}/hw/mcu/mindmotion/mm32sdk/${MCU_VARIANT_UPPER})
+set(CMSIS_5 ${TOP}/lib/CMSIS_5)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR cortex-m3 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS MM32F327X CACHE INTERNAL "")
+
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
+
+  # Startup & Linker script
+  set(STARTUP_FILE_GNU ${SDK_DIR}/Source/GCC_StartAsm/startup_${MCU_VARIANT}_gcc.s)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+  set(STARTUP_FILE_IAR ${SDK_DIR}/Source/IAR_StartAsm/startup_${MCU_VARIANT}_iar.s)
+
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+  # set(LD_FILE_IAR )
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/Source/system_${MCU_VARIANT}.c
+    ${SDK_DIR}/HAL_Lib/Src/hal_gpio.c
+    ${SDK_DIR}/HAL_Lib/Src/hal_rcc.c
+    ${SDK_DIR}/HAL_Lib/Src/hal_uart.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMSIS_5}/CMSIS/Core/Include
+    ${SDK_DIR}/Include
+    ${SDK_DIR}/HAL_Lib/Inc
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -nostartfiles
+      --specs=nosys.specs --specs=nano.specs
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_Clang}"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_MM32F327X ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_flash_jlink(${TARGET})
+endfunction()
diff --git a/hw/bsp/mm32/family.mk b/hw/bsp/mm32/family.mk
index 3981e4e41..a790663ab 100644
--- a/hw/bsp/mm32/family.mk
+++ b/hw/bsp/mm32/family.mk
@@ -1,32 +1,33 @@
 UF2_FAMILY_ID = 0x0
-SDK_DIR = hw/mcu/mindmotion/mm32sdk
-DEPS_SUBMODULES += lib/CMSIS_5 $(SDK_DIR)
-
 include $(TOP)/$(BOARD_PATH)/board.mk
+
+MCU_VARIANT_UPPER = $(subst mm32f,MM32F,${MCU_VARIANT})
+SDK_DIR = hw/mcu/mindmotion/mm32sdk/${MCU_VARIANT_UPPER}
+
 CPU_CORE ?= cortex-m3
 
 CFLAGS += \
   -flto \
-  -nostdlib -nostartfiles \
-  -DCFG_TUSB_MCU=OPT_MCU_MM32F327X
+  -DCFG_TUSB_MCU=OPT_MCU_MM32F327X \
 
 # suppress warning caused by vendor mcu driver
 CFLAGS += -Wno-error=unused-parameter -Wno-error=maybe-uninitialized -Wno-error=cast-qual
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += \
+  -nostdlib -nostartfiles \
+  -specs=nosys.specs -specs=nano.specs \
 
 SRC_C += \
 	src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c \
-	$(SDK_DIR)/mm32f327x/MM32F327x/Source/system_mm32f327x.c \
-	$(SDK_DIR)/mm32f327x/MM32F327x/HAL_Lib/Src/hal_gpio.c \
-	$(SDK_DIR)/mm32f327x/MM32F327x/HAL_Lib/Src/hal_rcc.c \
-	$(SDK_DIR)/mm32f327x/MM32F327x/HAL_Lib/Src/hal_uart.c \
+	$(SDK_DIR)/Source/system_${MCU_VARIANT}.c \
+	$(SDK_DIR)/HAL_Lib/Src/hal_gpio.c \
+	$(SDK_DIR)/HAL_Lib/Src/hal_rcc.c \
+	$(SDK_DIR)/HAL_Lib/Src/hal_uart.c \
+
+SRC_S += ${SDK_DIR}/Source/GCC_StartAsm/startup_${MCU_VARIANT}_gcc.s
 
 INC += \
 	$(TOP)/$(BOARD_PATH) \
 	$(TOP)/lib/CMSIS_5/CMSIS/Core/Include \
-	$(TOP)/$(SDK_DIR)/mm32f327x/MM32F327x/Include \
-	$(TOP)/$(SDK_DIR)/mm32f327x/MM32F327x/HAL_Lib/Inc
-
-# flash target using on-board
-flash: flash-jlink
+	$(TOP)/$(SDK_DIR)/Include \
+	$(TOP)/$(SDK_DIR)/HAL_Lib/Inc
diff --git a/hw/bsp/msp430/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/msp430/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..4049fba65
--- /dev/null
+++ b/hw/bsp/msp430/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,80 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "msp430.h"
+#endif
+
+#define configUSE_PREEMPTION		1
+#define configUSE_IDLE_HOOK			1
+#define configUSE_TICK_HOOK			0
+#define configUSE_MALLOC_FAILED_HOOK           0
+#define configCPU_CLOCK_HZ			( ( unsigned long ) 7995392 ) /* Clock setup from main.c in the demo application. */
+#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )
+#define configMAX_PRIORITIES		( 4 )
+#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 50 )
+#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 1700 ) )
+#define configMAX_TASK_NAME_LEN		( 8 )
+#define configUSE_TRACE_FACILITY	0
+#define configUSE_16_BIT_TICKS		1
+#define configIDLE_SHOULD_YIELD		1
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES 		0
+#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )
+
+/* Set the following definitions to 1 to include the API function, or zero to exclude the API function. */
+
+#define INCLUDE_vTaskPrioritySet		0
+#define INCLUDE_uxTaskPriorityGet		0
+#define INCLUDE_vTaskDelete				1
+#define INCLUDE_vTaskCleanUpResources	0
+#define INCLUDE_vTaskSuspend			0
+#define INCLUDE_vTaskDelayUntil			1
+#define INCLUDE_vTaskDelay				1
+
+#endif /* FREERTOS_CONFIG_H */
diff --git a/hw/bsp/msp430/boards/msp_exp430f5529lp/board.cmake b/hw/bsp/msp430/boards/msp_exp430f5529lp/board.cmake
new file mode 100644
index 000000000..59f591263
--- /dev/null
+++ b/hw/bsp/msp430/boards/msp_exp430f5529lp/board.cmake
@@ -0,0 +1,9 @@
+set(MCU_VARIANT msp430f5529)
+set(LD_FILE_GNU ${SDK_DIR}/msp430f5529.ld)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} INTERFACE
+    __MSP430F5529__
+    )
+
+endfunction()
diff --git a/hw/bsp/msp430/boards/msp_exp430f5529lp/board.mk b/hw/bsp/msp430/boards/msp_exp430f5529lp/board.mk
new file mode 100644
index 000000000..b45c62e83
--- /dev/null
+++ b/hw/bsp/msp430/boards/msp_exp430f5529lp/board.mk
@@ -0,0 +1,4 @@
+CFLAGS += \
+  -D__MSP430F5529__ \
+
+LD_FILE = ${SDK_DIR}/msp430f5529.ld
diff --git a/hw/bsp/msp430/family.cmake b/hw/bsp/msp430/family.cmake
new file mode 100644
index 000000000..e0b4ed28a
--- /dev/null
+++ b/hw/bsp/msp430/family.cmake
@@ -0,0 +1,84 @@
+include_guard()
+
+set(SDK_DIR ${TOP}/hw/mcu/ti/msp430/msp430-gcc-support-files/include)
+
+# include board specific
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR msp430 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/msp430_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS MSP430x5xx CACHE INTERNAL "")
+
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (NOT TARGET ${BOARD_TARGET})
+    add_library(${BOARD_TARGET} INTERFACE)
+    target_compile_definitions(${BOARD_TARGET} INTERFACE
+      CFG_TUD_ENDPOINT0_SIZE=8
+      CFG_EXAMPLE_VIDEO_READONLY
+      CFG_EXAMPLE_MSC_READONLY
+      )
+    target_include_directories(${BOARD_TARGET} INTERFACE
+      ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+      ${SDK_DIR}
+      )
+
+    update_board(${BOARD_TARGET})
+
+    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+      target_link_options(${BOARD_TARGET} INTERFACE
+        "LINKER:--script=${LD_FILE_GNU}"
+        -L${SDK_DIR}
+        )
+    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+      target_link_options(${BOARD_TARGET} INTERFACE
+        "LINKER:--config=${LD_FILE_IAR}"
+        )
+    endif ()
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_MSP430x5xx ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_add_bin_hex(${TARGET})
+  family_flash_msp430flasher(${TARGET})
+endfunction()
diff --git a/hw/bsp/msp430/family.mk b/hw/bsp/msp430/family.mk
index ceafa6ec1..06508ab2c 100644
--- a/hw/bsp/msp430/family.mk
+++ b/hw/bsp/msp430/family.mk
@@ -2,21 +2,21 @@ CROSS_COMPILE = msp430-elf-
 DEPS_SUBMODULES += hw/mcu/ti
 SKIP_NANOLIB = 1
 
+SDK_DIR = hw/mcu/ti/msp430/msp430-gcc-support-files/include
+
+include $(TOP)/$(BOARD_PATH)/board.mk
+
 CFLAGS += \
-  -D__MSP430F5529__ \
   -DCFG_TUSB_MCU=OPT_MCU_MSP430x5xx \
 	-DCFG_EXAMPLE_MSC_READONLY \
 	-DCFG_TUD_ENDPOINT0_SIZE=8
 
-# All source paths should be relative to the top level.
-LD_FILE = hw/mcu/ti/msp430/msp430-gcc-support-files/include/msp430f5529.ld
-LDINC += $(TOP)/hw/mcu/ti/msp430/msp430-gcc-support-files/include
-LDFLAGS += $(addprefix -L,$(LDINC))
+LDFLAGS += -L${TOP}/${SDK_DIR}
 
 SRC_C += src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
 
 INC += \
-	$(TOP)/hw/mcu/ti/msp430/msp430-gcc-support-files/include \
+	${TOP}/${SDK_DIR} \
 	$(TOP)/$(BOARD_PATH)
 
 # export for libmsp430.so to same installation
diff --git a/hw/bsp/msp432e4/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/msp432e4/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..3dd5a1ee1
--- /dev/null
+++ b/hw/bsp/msp432e4/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,149 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "msp.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#define configENABLE_FPU                        1
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       3
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<= (10000*MDK_MAJOR_VERSION + 100*MDK_MINOR_VERSION + MDK_MICRO_VERSION)
+  // note MDK 8.53.1 is also used by nrfx v3.0.0, just skip this version and use later 3.x
+  #define NRFX_VER 2
+#else
+  #define NRFX_VER 3
+#endif
+
 //--------------------------------------------------------------------+
 // Forward USB interrupt events to TinyUSB IRQ Handler
 //--------------------------------------------------------------------+
@@ -98,8 +107,12 @@ TU_ATTR_UNUSED static void power_event_handler(nrfx_power_usb_evt_t event) {
 #if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
 static void max3421_init(void);
 static nrfx_spim_t _spi = NRFX_SPIM_INSTANCE(1);
+
+#if NRFX_VER > 2
+static nrfx_gpiote_t _gpiote = NRFX_GPIOTE_INSTANCE(0);
 #endif
 
+#endif
 
 //--------------------------------------------------------------------+
 //
@@ -124,6 +137,7 @@ void board_init(void) {
   SysTick_Config(SystemCoreClock / 1000);
 
   // UART
+  #if NRFX_VER <= 2
   nrfx_uarte_config_t uart_cfg = {
       .pseltxd   = UART_TX_PIN,
       .pselrxd   = UART_RX_PIN,
@@ -137,6 +151,21 @@ void board_init(void) {
           .parity    = NRF_UARTE_PARITY_EXCLUDED,
       }
   };
+  #else
+  nrfx_uarte_config_t uart_cfg = {
+      .txd_pin   = UART_TX_PIN,
+      .rxd_pin   = UART_RX_PIN,
+      .rts_pin   = NRF_UARTE_PSEL_DISCONNECTED,
+      .cts_pin   = NRF_UARTE_PSEL_DISCONNECTED,
+      .p_context = NULL,
+      .baudrate  = NRF_UARTE_BAUDRATE_115200, // CFG_BOARD_UART_BAUDRATE
+      .interrupt_priority = 7,
+      .config = {
+          .hwfc      = NRF_UARTE_HWFC_DISABLED,
+          .parity    = NRF_UARTE_PARITY_EXCLUDED,
+      }
+  };
+  #endif
 
   nrfx_uarte_init(&_uart_id, &uart_cfg, NULL); //uart_handler);
 
@@ -224,11 +253,17 @@ int board_uart_read(uint8_t* buf, int len) {
   (void) buf;
   (void) len;
   return 0;
-//  return NRFX_SUCCESS == nrfx_uart_rx(&_uart_id, buf, (size_t) len) ? len : 0;
+//  nrfx_err_t err = nrfx_uarte_rx(&_uart_id, buf, (size_t) len);
+//  return NRFX_SUCCESS == err ? len : 0;
 }
 
 int board_uart_write(void const* buf, int len) {
-  return (NRFX_SUCCESS == nrfx_uarte_tx(&_uart_id, (uint8_t const*) buf, (size_t) len)) ? len : 0;
+  nrfx_err_t err = nrfx_uarte_tx(&_uart_id, (uint8_t const*) buf, (size_t) len
+                                 #if NRFX_VER > 2
+                                 ,0
+                                 #endif
+                                );
+  return (NRFX_SUCCESS == err) ? len : 0;
 }
 
 #if CFG_TUSB_OS == OPT_OS_NONE
@@ -241,9 +276,22 @@ void SysTick_Handler(void) {
 uint32_t board_millis(void) {
   return system_ticks;
 }
-
 #endif
 
+#ifndef __ICCARM__
+// Implement _start() since we use linker flag '-nostartfiles'.
+// Requires defined __STARTUP_CLEAR_BSS,
+extern int main(void);
+TU_ATTR_UNUSED void _start(void) {
+  // called by startup code
+  main();
+  while (1) {}
+}
+#endif
+
+//--------------------------------------------------------------------+
+// Softdevice running
+//--------------------------------------------------------------------+
 #ifdef SOFTDEVICE_PRESENT
 // process SOC event from SD
 uint32_t proc_soc(void) {
@@ -286,8 +334,16 @@ void nrf_error_cb(uint32_t id, uint32_t pc, uint32_t info) {
 //--------------------------------------------------------------------+
 #if CFG_TUH_ENABLED && defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421
 
-void max3421_int_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action) {
-  if (!(pin == MAX3421_INTR_PIN && action == NRF_GPIOTE_POLARITY_HITOLO)) return;
+#if NRFX_VER <= 2
+void max3421_int_handler(nrfx_gpiote_pin_t pin, nrf_gpiote_polarity_t action ) {
+  if (action != NRF_GPIOTE_POLARITY_HITOLO) return;
+#else
+void max3421_int_handler(nrfx_gpiote_pin_t pin, nrfx_gpiote_trigger_t action, void* p_context) {
+  (void) p_context;
+  if (action != NRFX_GPIOTE_TRIGGER_HITOLO) return;
+#endif
+
+  if (pin != MAX3421_INTR_PIN) return;
   tuh_int_handler(1, true);
 }
 
@@ -303,28 +359,55 @@ static void max3421_init(void) {
       .sck_pin        = MAX3421_SCK_PIN,
       .mosi_pin       = MAX3421_MOSI_PIN,
       .miso_pin       = MAX3421_MISO_PIN,
+  #if NRFX_VER <= 2
       .ss_pin         = NRFX_SPIM_PIN_NOT_USED,
+      .frequency      = NRF_SPIM_FREQ_4M,
+  #else
+      .ss_pin         = NRF_SPIM_PIN_NOT_CONNECTED,
+      .frequency      = 4000000u,
+  #endif
       .ss_active_high = false,
       .irq_priority   = 3,
       .orc            = 0xFF,
       // default setting 4 Mhz, Mode 0, MSB first
-      .frequency      = NRF_SPIM_FREQ_4M,
       .mode           = NRF_SPIM_MODE_0,
       .bit_order      = NRF_SPIM_BIT_ORDER_MSB_FIRST,
+      .miso_pull      = NRF_GPIO_PIN_NOPULL,
   };
 
   // no handler --> blocking
-  nrfx_spim_init(&_spi, &cfg, NULL, NULL);
+  TU_ASSERT(NRFX_SUCCESS == nrfx_spim_init(&_spi, &cfg, NULL, NULL), );
 
   // max3421e interrupt pin
+  #if NRFX_VER <= 2
   nrfx_gpiote_init(1);
   nrfx_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
   in_config.pull = NRF_GPIO_PIN_PULLUP;
-
   NVIC_SetPriority(GPIOTE_IRQn, 2);
-
   nrfx_gpiote_in_init(MAX3421_INTR_PIN, &in_config, max3421_int_handler);
   nrfx_gpiote_trigger_enable(MAX3421_INTR_PIN, true);
+  #else
+  nrf_gpio_pin_pull_t intr_pull = NRF_GPIO_PIN_PULLUP;
+  nrfx_gpiote_trigger_config_t intr_trigger = {
+      .trigger = NRFX_GPIOTE_TRIGGER_HITOLO,
+      .p_in_channel = NULL, // sensing mechanism
+  };
+  nrfx_gpiote_handler_config_t intr_handler = {
+      .handler = max3421_int_handler,
+      .p_context = NULL,
+  };
+  nrfx_gpiote_input_pin_config_t intr_config = {
+      .p_pull_config = &intr_pull,
+      .p_trigger_config = &intr_trigger,
+      .p_handler_config = &intr_handler,
+  };
+
+  nrfx_gpiote_init(&_gpiote, 1);
+  NVIC_SetPriority(GPIOTE_IRQn, 2);
+
+  nrfx_gpiote_input_configure(&_gpiote, MAX3421_INTR_PIN, &intr_config);
+  nrfx_gpiote_trigger_enable(&_gpiote, MAX3421_INTR_PIN, true);
+  #endif
 }
 
 // API to enable/disable MAX3421 INTR pin interrupt
diff --git a/hw/bsp/nrf/family.cmake b/hw/bsp/nrf/family.cmake
index 2c4620b97..9e86374dc 100644
--- a/hw/bsp/nrf/family.cmake
+++ b/hw/bsp/nrf/family.cmake
@@ -1,9 +1,5 @@
 include_guard()
 
-if (NOT BOARD)
-  message(FATAL_ERROR "BOARD not specified")
-endif ()
-
 set(NRFX_DIR ${TOP}/hw/mcu/nordic/nrfx)
 set(CMSIS_DIR ${TOP}/lib/CMSIS_5)
 
@@ -29,68 +25,75 @@ set(FAMILY_MCUS NRF5X CACHE INTERNAL "")
 #------------------------------------
 # only need to be built ONCE for all examples
 function(add_board_target BOARD_TARGET)
-  if (NOT TARGET ${BOARD_TARGET})
-    add_library(${BOARD_TARGET} STATIC
-      # driver
-      ${NRFX_DIR}/helpers/nrfx_flag32_allocator.c
-      ${NRFX_DIR}/drivers/src/nrfx_gpiote.c
-      ${NRFX_DIR}/drivers/src/nrfx_power.c
-      ${NRFX_DIR}/drivers/src/nrfx_spim.c
-      ${NRFX_DIR}/drivers/src/nrfx_uarte.c
-      # mcu
-      ${NRFX_DIR}/mdk/system_${MCU_VARIANT}.c
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
+
+  if (MCU_VARIANT STREQUAL "nrf5340_application")
+    set(MCU_VARIANT_XXAA "nrf5340_xxaa_application")
+  else ()
+    set(MCU_VARIANT_XXAA "${MCU_VARIANT}_xxaa")
+  endif ()
+
+  if (NOT DEFINED LD_FILE_GNU)
+    set(LD_FILE_GNU ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/linker/${MCU_VARIANT_XXAA}.ld)
+  endif ()
+
+  if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
+    set(STARTUP_FILE_GNU ${NRFX_DIR}/mdk/gcc_startup_${MCU_VARIANT}.S)
+    set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+  endif ()
+
+  add_library(${BOARD_TARGET} STATIC
+    ${NRFX_DIR}/helpers/nrfx_flag32_allocator.c
+    ${NRFX_DIR}/drivers/src/nrfx_gpiote.c
+    ${NRFX_DIR}/drivers/src/nrfx_power.c
+    ${NRFX_DIR}/drivers/src/nrfx_spim.c
+    ${NRFX_DIR}/drivers/src/nrfx_uarte.c
+    ${NRFX_DIR}/mdk/system_${MCU_VARIANT}.c
+    ${NRFX_DIR}/soc/nrfx_atomic.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  string(TOUPPER "${MCU_VARIANT_XXAA}" MCU_VARIANT_XXAA_UPPER)
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    __STARTUP_CLEAR_BSS
+    CONFIG_GPIO_AS_PINRESET
+    ${MCU_VARIANT_XXAA_UPPER}
+    )
+
+  if (TRACE_ETM STREQUAL "1")
+    # ENABLE_TRACE will cause system_nrf5x.c to set up ETM trace
+    target_compile_definitions(${BOARD_TARGET} PUBLIC ENABLE_TRACE)
+  endif ()
+
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${NRFX_DIR}
+    ${NRFX_DIR}/mdk
+    ${NRFX_DIR}/hal
+    ${NRFX_DIR}/drivers/include
+    ${NRFX_DIR}/drivers/src
+    ${CMSIS_DIR}/CMSIS/Core/Include
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -L${NRFX_DIR}/mdk
+      --specs=nosys.specs --specs=nano.specs
+      -nostartfiles
       )
-    target_compile_definitions(${BOARD_TARGET} PUBLIC CONFIG_GPIO_AS_PINRESET)
-
-    if (MCU_VARIANT STREQUAL "nrf52840")
-      target_compile_definitions(${BOARD_TARGET} PUBLIC NRF52840_XXAA)
-    elseif (MCU_VARIANT STREQUAL "nrf52833")
-      target_compile_definitions(${BOARD_TARGET} PUBLIC NRF52833_XXAA)
-    elseif (MCU_VARIANT STREQUAL "nrf5340_application")
-      target_compile_definitions(${BOARD_TARGET} PUBLIC NRF5340_XXAA NRF5340_XXAA_APPLICATION)
-    endif ()
-
-    if (TRACE_ETM STREQUAL "1")
-      # ENABLE_TRACE will cause system_nrf5x.c to set up ETM trace
-      target_compile_definitions(${BOARD_TARGET} PUBLIC ENABLE_TRACE)
-    endif ()
-
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
-      ${NRFX_DIR}
-      ${NRFX_DIR}/mdk
-      ${NRFX_DIR}/hal
-      ${NRFX_DIR}/drivers/include
-      ${NRFX_DIR}/drivers/src
-      ${CMSIS_DIR}/CMSIS/Core/Include
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -L${NRFX_DIR}/mdk
       )
-
-    update_board(${BOARD_TARGET})
-
-    if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
-      set(LD_FILE_GNU ${NRFX_DIR}/mdk/${MCU_VARIANT}_xxaa.ld)
-    endif ()
-
-    if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
-      set(STARTUP_FILE_GNU ${NRFX_DIR}/mdk/gcc_startup_${MCU_VARIANT}.S)
-    endif ()
-
-    target_sources(${BOARD_TARGET} PUBLIC
-      ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
       )
-
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        # linker file
-        "LINKER:--script=${LD_FILE_GNU}"
-        -L${NRFX_DIR}/mdk
-        --specs=nosys.specs --specs=nano.specs
-        )
-    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--config=${LD_FILE_IAR}"
-        )
-    endif ()
   endif ()
 endfunction()
 
@@ -98,6 +101,15 @@ endfunction()
 #------------------------------------
 # Functions
 #------------------------------------
+
+#function(family_flash_adafruit_nrfutil TARGET)
+#  add_custom_target(${TARGET}-adafruit-nrfutil
+#    DEPENDS ${TARGET}
+#    COMMAND adafruit-nrfutil --verbose dfu serial --package $^ -p /dev/ttyACM0 -b 115200 --singlebank --touch 1200
+#    )
+#endfunction()
+
+
 function(family_configure_example TARGET RTOS)
   family_configure_common(${TARGET} ${RTOS})
 
@@ -130,4 +142,5 @@ function(family_configure_example TARGET RTOS)
 
   # Flashing
   family_flash_jlink(${TARGET})
+#  family_flash_adafruit_nrfutil(${TARGET})
 endfunction()
diff --git a/hw/bsp/nrf/family.mk b/hw/bsp/nrf/family.mk
index 29802dc37..b3c05e6db 100644
--- a/hw/bsp/nrf/family.mk
+++ b/hw/bsp/nrf/family.mk
@@ -8,25 +8,31 @@ include $(TOP)/$(BOARD_PATH)/board.mk
 CPU_CORE ?= cortex-m4
 
 CFLAGS += \
-  -flto \
   -DCFG_TUSB_MCU=OPT_MCU_NRF5X \
-  -DCONFIG_GPIO_AS_PINRESET
+  -DCONFIG_GPIO_AS_PINRESET \
+  -D__STARTUP_CLEAR_BSS
 
 #CFLAGS += -nostdlib
 #CFLAGS += -D__START=main
 
 # suppress warning caused by vendor mcu driver
-CFLAGS += \
+CFLAGS_GCC += \
+  -flto \
   -Wno-error=undef \
   -Wno-error=unused-parameter \
+  -Wno-error=unused-variable \
   -Wno-error=cast-align \
   -Wno-error=cast-qual \
-  -Wno-error=redundant-decls
+  -Wno-error=redundant-decls \
 
-LDFLAGS += \
-  -specs=nosys.specs -specs=nano.specs \
+LDFLAGS_GCC += \
+  -nostartfiles \
+  --specs=nosys.specs --specs=nano.specs \
   -L$(TOP)/${NRFX_DIR}/mdk
 
+LDFLAGS_CLANG += \
+  -L$(TOP)/${NRFX_DIR}/mdk \
+
 SRC_C += \
   src/portable/nordic/nrf5x/dcd_nrf5x.c \
 	${NRFX_DIR}/helpers/nrfx_flag32_allocator.c \
@@ -34,7 +40,8 @@ SRC_C += \
   ${NRFX_DIR}/drivers/src/nrfx_power.c \
   ${NRFX_DIR}/drivers/src/nrfx_spim.c \
   ${NRFX_DIR}/drivers/src/nrfx_uarte.c \
-  ${NRFX_DIR}/mdk/system_$(MCU_VARIANT).c
+  ${NRFX_DIR}/mdk/system_$(MCU_VARIANT).c \
+  ${NRFX_DIR}/soc/nrfx_atomic.c
 
 INC += \
   $(TOP)/$(BOARD_PATH) \
diff --git a/hw/bsp/nrf/linker/nrf52833_xxaa.ld b/hw/bsp/nrf/linker/nrf52833_xxaa.ld
new file mode 100644
index 000000000..ae4d0e5b3
--- /dev/null
+++ b/hw/bsp/nrf/linker/nrf52833_xxaa.ld
@@ -0,0 +1,20 @@
+/* Linker script to configure memory regions. */
+
+SEARCH_DIR(.)
+/*GROUP(-lgcc -lc -lnosys) not compatible with clang*/
+
+MEMORY
+{
+  FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x80000
+  RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x20000
+  CODE_RAM (rwx) : ORIGIN = 0x800000, LENGTH = 0x20000
+}
+
+
+INCLUDE "nrf_common.ld"
+
+/* nrfx v2 linker does not define __tbss_start/end__ __sbss_start/end__*/
+__tbss_start__ = __tbss_start;
+__tbss_end__ = __tbss_end;
+__sbss_start__ = __sbss_start;
+__sbss_end__ = __sbss_end;
diff --git a/hw/bsp/nrf/linker/nrf52840_s140_v6.ld b/hw/bsp/nrf/linker/nrf52840_s140_v6.ld
index e27fa1c91..037a14196 100644
--- a/hw/bsp/nrf/linker/nrf52840_s140_v6.ld
+++ b/hw/bsp/nrf/linker/nrf52840_s140_v6.ld
@@ -1,11 +1,11 @@
 /* Linker script to configure memory regions. */
 
 SEARCH_DIR(.)
-GROUP(-lgcc -lc -lnosys)
+/*GROUP(-lgcc -lc -lnosys) not compatible with clang*/
 
 MEMORY
 {
-  FLASH (rx)     : ORIGIN = 0x26000, LENGTH = 0xED000 - 0x26000
+  FLASH (rx) : ORIGIN = 0x26000, LENGTH = 0xED000 - 0x26000
 
   /* SRAM required by S132 depend on
    * - Attribute Table Size
@@ -14,7 +14,7 @@ MEMORY
    * - Concurrent connection peripheral + central + secure links
    * - Event Len, HVN queue, Write CMD queue
    */
-  RAM (rwx) :  ORIGIN = 0x20003400, LENGTH = 0x20040000 - 0x20003400
+  RAM (rwx) : ORIGIN = 0x20003400, LENGTH = 0x20040000 - 0x20003400
 }
 
 SECTIONS
@@ -36,3 +36,9 @@ SECTIONS
 } INSERT AFTER .data;
 
 INCLUDE "nrf_common.ld"
+
+/* nrfx v2 linker does not define __tbss_start/end__ __sbss_start/end__*/
+__tbss_start__ = __tbss_start;
+__tbss_end__ = __tbss_end;
+__sbss_start__ = __sbss_start;
+__sbss_end__ = __sbss_end;
diff --git a/hw/bsp/nrf/linker/nrf52840_xxaa.ld b/hw/bsp/nrf/linker/nrf52840_xxaa.ld
new file mode 100644
index 000000000..2d20ba7ac
--- /dev/null
+++ b/hw/bsp/nrf/linker/nrf52840_xxaa.ld
@@ -0,0 +1,20 @@
+/* Linker script to configure memory regions. */
+
+SEARCH_DIR(.)
+/*GROUP(-lgcc -lc -lnosys) not compatible with clang*/
+
+MEMORY
+{
+  FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x100000
+  EXTFLASH (rx) : ORIGIN = 0x12000000, LENGTH = 0x8000000
+  RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x40000
+  CODE_RAM (rwx) : ORIGIN = 0x800000, LENGTH = 0x40000
+}
+
+INCLUDE "nrf_common.ld"
+
+/* nrfx v2 linker does not define __tbss_start/end__ __sbss_start/end__*/
+__tbss_start__ = __tbss_start;
+__tbss_end__ = __tbss_end;
+__sbss_start__ = __sbss_start;
+__sbss_end__ = __sbss_end;
diff --git a/hw/bsp/nrf/linker/nrf5340_xxaa_application.ld b/hw/bsp/nrf/linker/nrf5340_xxaa_application.ld
new file mode 100644
index 000000000..31762d0b2
--- /dev/null
+++ b/hw/bsp/nrf/linker/nrf5340_xxaa_application.ld
@@ -0,0 +1,21 @@
+/* Linker script to configure memory regions. */
+
+SEARCH_DIR(.)
+/*GROUP(-lgcc -lc) not compatible with clang*/
+
+MEMORY
+{
+  FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x100000
+  EXTFLASH (rx) : ORIGIN = 0x10000000, LENGTH = 0x8000000
+  RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x40000
+  RAM1 (rwx) : ORIGIN = 0x20040000, LENGTH = 0x3F000
+}
+
+
+INCLUDE "nrf_common.ld"
+
+/* nrfx v2 linker does not define __tbss_start/end__ __sbss_start/end__*/
+__tbss_start__ = __tbss_start;
+__tbss_end__ = __tbss_end;
+__sbss_start__ = __sbss_start;
+__sbss_end__ = __sbss_end;
diff --git a/hw/bsp/nrf/nrfx_config.h b/hw/bsp/nrf/nrfx_config.h
index 95ef33ce4..fbec4192b 100644
--- a/hw/bsp/nrf/nrfx_config.h
+++ b/hw/bsp/nrf/nrfx_config.h
@@ -6,6 +6,7 @@
 
 #define NRFX_CLOCK_ENABLED   0
 #define NRFX_GPIOTE_ENABLED  1
+#define NRFX_GPIOTE0_ENABLED 1
 
 #define NRFX_UARTE_ENABLED   1
 #define NRFX_UARTE0_ENABLED  1
diff --git a/hw/bsp/ra/boards/ra2a1_ek/board.h b/hw/bsp/ra/boards/ra2a1_ek/board.h
index c132387bc..1c2b666d2 100644
--- a/hw/bsp/ra/boards/ra2a1_ek/board.h
+++ b/hw/bsp/ra/boards/ra2a1_ek/board.h
@@ -31,10 +31,10 @@
 extern "C" {
 #endif
 
-#define LED1          BSP_IO_PORT_01_PIN_06
+#define LED1          BSP_IO_PORT_02_PIN_05
 #define LED_STATE_ON  1
 
-#define SW1                   BSP_IO_PORT_01_PIN_05
+#define SW1                   BSP_IO_PORT_02_PIN_06
 #define BUTTON_STATE_ACTIVE   0
 
 static const ioport_pin_cfg_t board_pin_cfg[] = {
diff --git a/hw/bsp/ra/family.c b/hw/bsp/ra/family.c
index 16332be17..a8b4ebcc7 100644
--- a/hw/bsp/ra/family.c
+++ b/hw/bsp/ra/family.c
@@ -64,8 +64,11 @@ BSP_DONT_REMOVE BSP_PLACE_IN_SECTION(BSP_SECTION_APPLICATION_VECTORS)
 const fsp_vector_t g_vector_table[BSP_ICU_VECTOR_MAX_ENTRIES] = {
     [0] = usbfs_interrupt_handler, /* USBFS INT (USBFS interrupt) */
     [1] = usbfs_resume_handler,    /* USBFS RESUME (USBFS resume interrupt) */
+
+#ifndef BSP_MCU_GROUP_RA2A1
     [2] = usbfs_d0fifo_handler,    /* USBFS FIFO 0 (DMA transfer request 0) */
     [3] = usbfs_d1fifo_handler,    /* USBFS FIFO 1 (DMA transfer request 1) */
+#endif
 
 #ifdef BOARD_HAS_USB_HIGHSPEED
     [4] = usbhs_interrupt_handler, /* USBHS INT (USBHS interrupt) */
@@ -144,6 +147,13 @@ uint32_t board_button_read(void) {
   return lvl == BUTTON_STATE_ACTIVE;
 }
 
+size_t board_get_unique_id(uint8_t id[], size_t max_len) {
+  max_len = tu_min32(max_len, sizeof(bsp_unique_id_t));
+  bsp_unique_id_t const *uid = R_BSP_UniqueIdGet();
+  memcpy(id, uid->unique_id_bytes, max_len);
+  return max_len;
+}
+
 int board_uart_read(uint8_t *buf, int len) {
   (void) buf;
   (void) len;
@@ -166,7 +176,6 @@ void SysTick_Handler(void) {
 uint32_t board_millis(void) {
   return system_ticks;
 }
-
 #endif
 
 //--------------------------------------------------------------------+
diff --git a/hw/bsp/ra/vector_data.h b/hw/bsp/ra/vector_data.h
index ca667faa3..2b3b7d837 100644
--- a/hw/bsp/ra/vector_data.h
+++ b/hw/bsp/ra/vector_data.h
@@ -9,8 +9,11 @@ extern "C" {
 /* ISR prototypes */
 void usbfs_interrupt_handler(void);
 void usbfs_resume_handler(void);
+
+#ifndef BSP_MCU_GROUP_RA2A1
 void usbfs_d0fifo_handler(void);
 void usbfs_d1fifo_handler(void);
+#endif
 
 #ifdef BOARD_HAS_USB_HIGHSPEED
 void usbhs_interrupt_handler(void);
diff --git a/hw/bsp/rp2040/family.c b/hw/bsp/rp2040/family.c
index cffb632f3..9360f7f57 100644
--- a/hw/bsp/rp2040/family.c
+++ b/hw/bsp/rp2040/family.c
@@ -31,6 +31,7 @@
 #include "hardware/gpio.h"
 #include "hardware/sync.h"
 #include "hardware/resets.h"
+#include "hardware/clocks.h"
 #include "hardware/structs/ioqspi.h"
 #include "hardware/structs/sio.h"
 
diff --git a/hw/bsp/rp2040/family.cmake b/hw/bsp/rp2040/family.cmake
index 861d20bdd..d91821ffa 100644
--- a/hw/bsp/rp2040/family.cmake
+++ b/hw/bsp/rp2040/family.cmake
@@ -6,6 +6,12 @@ if (NOT BOARD)
 	set(BOARD pico_sdk)
 endif()
 
+if (TOOLCHAIN STREQUAL "clang")
+	set(PICO_COMPILER "pico_arm_clang")
+else()
+	set(PICO_COMPILER "pico_arm_gcc")
+endif()
+
 # add the SDK in case we are standalone tinyusb example (noop if already present)
 include(${CMAKE_CURRENT_LIST_DIR}/pico_sdk_import.cmake)
 
@@ -30,13 +36,13 @@ endif()
 add_library(tinyusb_common_base INTERFACE)
 
 target_sources(tinyusb_common_base INTERFACE
-		${TOP}/src/tusb.c
-		${TOP}/src/common/tusb_fifo.c
-		)
+	${TOP}/src/tusb.c
+	${TOP}/src/common/tusb_fifo.c
+	)
 
 target_include_directories(tinyusb_common_base INTERFACE
-		${TOP}/src
-		)
+	${TOP}/src
+	)
 
 if(DEFINED LOG)
 	set(TINYUSB_DEBUG_LEVEL ${LOG})
@@ -48,9 +54,9 @@ else ()
 endif()
 
 target_compile_definitions(tinyusb_common_base INTERFACE
-		CFG_TUSB_MCU=OPT_MCU_RP2040
-		CFG_TUSB_OS=${TINYUSB_OPT_OS}
-		CFG_TUSB_DEBUG=${TINYUSB_DEBUG_LEVEL}
+	CFG_TUSB_MCU=OPT_MCU_RP2040
+	CFG_TUSB_OS=${TINYUSB_OPT_OS}
+	CFG_TUSB_DEBUG=${TINYUSB_DEBUG_LEVEL}
 )
 
 target_link_libraries(tinyusb_common_base INTERFACE
@@ -127,7 +133,10 @@ target_sources(tinyusb_bsp INTERFACE
 target_include_directories(tinyusb_bsp INTERFACE
 	${TOP}/hw
 	)
-target_link_libraries(tinyusb_bsp	INTERFACE pico_unique_id)
+target_link_libraries(tinyusb_bsp INTERFACE
+	pico_unique_id
+	hardware_clocks
+	)
 
 # tinyusb_additions will hold our extra settings for examples
 add_library(tinyusb_additions INTERFACE)
@@ -169,11 +178,16 @@ function(family_configure_target TARGET RTOS)
 	# export RTOS_SUFFIX to parent scope
 	set(RTOS_SUFFIX ${RTOS_SUFFIX} PARENT_SCOPE)
 
+	# compile define from command line
+	if(DEFINED CFLAGS_CLI)
+		target_compile_options(${TARGET} PUBLIC ${CFLAGS_CLI})
+	endif()
+
 	pico_add_extra_outputs(${TARGET})
 	pico_enable_stdio_uart(${TARGET} 1)
 	target_link_libraries(${TARGET} PUBLIC pico_stdlib tinyusb_board${RTOS_SUFFIX} tinyusb_additions)
 
-	family_flash_openocd(${TARGET} ${OPENOCD_OPTION})
+	family_flash_openocd(${TARGET})
 	family_flash_jlink(${TARGET})
 endfunction()
 
diff --git a/hw/bsp/rx/boards/gr_citrus/gr_citrus.c b/hw/bsp/rx/boards/gr_citrus/gr_citrus.c
index 5078c7569..26ad4a6aa 100644
--- a/hw/bsp/rx/boards/gr_citrus/gr_citrus.c
+++ b/hw/bsp/rx/boards/gr_citrus/gr_citrus.c
@@ -203,9 +203,10 @@ void board_init(void)
   IEN(SCI0, TEI0) = 1;
 
   /* Enable USB0 */
+  unsigned short oldPRCR = SYSTEM.PRCR.WORD;
   SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1;
   MSTP(USB0) = 0;
-  SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY;
+  SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | oldPRCR;
 }
 
 //--------------------------------------------------------------------+
diff --git a/hw/bsp/rx/boards/rx65n_target/rx65n_target.c b/hw/bsp/rx/boards/rx65n_target/rx65n_target.c
index 032dac810..66a319541 100644
--- a/hw/bsp/rx/boards/rx65n_target/rx65n_target.c
+++ b/hw/bsp/rx/boards/rx65n_target/rx65n_target.c
@@ -249,9 +249,10 @@ void board_init(void)
   EN(SCI5, TEI5)     = 1;
 
   /* Enable USB0 */
+  unsigned short oldPRCR = SYSTEM.PRCR.WORD;
   SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | SYSTEM_PRCR_PRC1;
   MSTP(USB0) = 0;
-  SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY;
+  SYSTEM.PRCR.WORD = SYSTEM_PRCR_PRKEY | oldPRCR;
 
   /* setup USBI0 interrupt. */
   IR(USB0, USBI0)  = 0;
@@ -277,6 +278,7 @@ int board_uart_read(uint8_t* buf, int len)
   sci_buf[1].buf = buf;
   sci_buf[1].cnt = len;
   SCI5.SCR.BYTE |= SCI_SCR_RE | SCI_SCR_RIE;
+  // TODO change to non blocking, return -1 immediately if no data
   while (SCI5.SCR.BIT.RE) ;
   return len - sci_buf[1].cnt;
 }
diff --git a/hw/bsp/samd11/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/samd11/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..6c9ecae2d
--- /dev/null
+++ b/hw/bsp/samd11/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,153 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "sam.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#if defined(__ARM_FP) && __ARM_FP >= 4
+  #define configENABLE_FPU                      1
+#else
+  #define configENABLE_FPU                      0
+#endif
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       2
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<= 4
+  #define configENABLE_FPU                      1
+#else
+  #define configENABLE_FPU                      0
+#endif
 #define configENABLE_TRUSTZONE                  0
 #define configMINIMAL_SECURE_STACK_SIZE         (1024)
 
diff --git a/hw/bsp/samd21/boards/atsamd21_xpro/samd21j18a_flash.ld b/hw/bsp/samd21/boards/atsamd21_xpro/samd21j18a_flash.ld
index e2f93416d..d9b086f9c 100644
--- a/hw/bsp/samd21/boards/atsamd21_xpro/samd21j18a_flash.ld
+++ b/hw/bsp/samd21/boards/atsamd21_xpro/samd21j18a_flash.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 /* Section Definitions */
 SECTIONS
diff --git a/hw/bsp/samd21/boards/circuitplayground_express/circuitplayground_express.ld b/hw/bsp/samd21/boards/circuitplayground_express/circuitplayground_express.ld
index f0c93340c..ce7aff80b 100644
--- a/hw/bsp/samd21/boards/circuitplayground_express/circuitplayground_express.ld
+++ b/hw/bsp/samd21/boards/circuitplayground_express/circuitplayground_express.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd21/boards/curiosity_nano/samd21g17a_flash.ld b/hw/bsp/samd21/boards/curiosity_nano/samd21g17a_flash.ld
index 153f0cbb9..e03a4ce60 100644
--- a/hw/bsp/samd21/boards/curiosity_nano/samd21g17a_flash.ld
+++ b/hw/bsp/samd21/boards/curiosity_nano/samd21g17a_flash.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x1000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x1000;
 
 /* Section Definitions */
 SECTIONS
diff --git a/hw/bsp/samd21/boards/cynthion_d21/samd21g18a_flash.ld b/hw/bsp/samd21/boards/cynthion_d21/samd21g18a_flash.ld
index ecb6b8523..95d71b9e3 100644
--- a/hw/bsp/samd21/boards/cynthion_d21/samd21g18a_flash.ld
+++ b/hw/bsp/samd21/boards/cynthion_d21/samd21g18a_flash.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 /* Section Definitions */
 SECTIONS
diff --git a/hw/bsp/samd21/boards/feather_m0_express/feather_m0_express.ld b/hw/bsp/samd21/boards/feather_m0_express/feather_m0_express.ld
index f0c93340c..ce7aff80b 100644
--- a/hw/bsp/samd21/boards/feather_m0_express/feather_m0_express.ld
+++ b/hw/bsp/samd21/boards/feather_m0_express/feather_m0_express.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd21/boards/itsybitsy_m0/itsybitsy_m0.ld b/hw/bsp/samd21/boards/itsybitsy_m0/itsybitsy_m0.ld
index f0c93340c..ce7aff80b 100644
--- a/hw/bsp/samd21/boards/itsybitsy_m0/itsybitsy_m0.ld
+++ b/hw/bsp/samd21/boards/itsybitsy_m0/itsybitsy_m0.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd21/boards/metro_m0_express/metro_m0_express.ld b/hw/bsp/samd21/boards/metro_m0_express/metro_m0_express.ld
index f0c93340c..ce7aff80b 100644
--- a/hw/bsp/samd21/boards/metro_m0_express/metro_m0_express.ld
+++ b/hw/bsp/samd21/boards/metro_m0_express/metro_m0_express.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd21/boards/qtpy/qtpy.ld b/hw/bsp/samd21/boards/qtpy/qtpy.ld
index f0c93340c..ce7aff80b 100644
--- a/hw/bsp/samd21/boards/qtpy/qtpy.ld
+++ b/hw/bsp/samd21/boards/qtpy/qtpy.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd21/boards/seeeduino_xiao/seeeduino_xiao.ld b/hw/bsp/samd21/boards/seeeduino_xiao/seeeduino_xiao.ld
index cf11c4c35..0d0c4e6c4 100644
--- a/hw/bsp/samd21/boards/seeeduino_xiao/seeeduino_xiao.ld
+++ b/hw/bsp/samd21/boards/seeeduino_xiao/seeeduino_xiao.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd21/boards/sparkfun_samd21_mini_usb/sparkfun_samd21_mini_usb.ld b/hw/bsp/samd21/boards/sparkfun_samd21_mini_usb/sparkfun_samd21_mini_usb.ld
index f5d2ad151..0754a8e9c 100644
--- a/hw/bsp/samd21/boards/sparkfun_samd21_mini_usb/sparkfun_samd21_mini_usb.ld
+++ b/hw/bsp/samd21/boards/sparkfun_samd21_mini_usb/sparkfun_samd21_mini_usb.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd21/boards/trinket_m0/trinket_m0.ld b/hw/bsp/samd21/boards/trinket_m0/trinket_m0.ld
index f0c93340c..ce7aff80b 100644
--- a/hw/bsp/samd21/boards/trinket_m0/trinket_m0.ld
+++ b/hw/bsp/samd21/boards/trinket_m0/trinket_m0.ld
@@ -40,7 +40,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x2000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd21/family.cmake b/hw/bsp/samd21/family.cmake
index a79a25b54..c836b85d9 100644
--- a/hw/bsp/samd21/family.cmake
+++ b/hw/bsp/samd21/family.cmake
@@ -17,52 +17,57 @@ set(OPENOCD_OPTION "-f interface/cmsis-dap.cfg -c \"transport select swd\" -f ta
 #------------------------------------
 # only need to be built ONCE for all examples
 function(add_board_target BOARD_TARGET)
-  if (NOT TARGET ${BOARD_TARGET})
-    add_library(${BOARD_TARGET} STATIC
-      ${SDK_DIR}/gcc/system_samd21.c
-      ${SDK_DIR}/hpl/gclk/hpl_gclk.c
-      ${SDK_DIR}/hpl/pm/hpl_pm.c
-      ${SDK_DIR}/hpl/sysctrl/hpl_sysctrl.c
-      ${SDK_DIR}/hal/src/hal_atomic.c
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
+
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+  if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
+    message(FATAL_ERROR "LD_FILE_${CMAKE_C_COMPILER_ID} not defined")
+  endif ()
+
+  set(STARTUP_FILE_GNU ${SDK_DIR}/gcc/gcc/startup_samd21.c)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/gcc/system_samd21.c
+    ${SDK_DIR}/hpl/gclk/hpl_gclk.c
+    ${SDK_DIR}/hpl/pm/hpl_pm.c
+    ${SDK_DIR}/hpl/sysctrl/hpl_sysctrl.c
+    ${SDK_DIR}/hal/src/hal_atomic.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_DIR}
+    ${SDK_DIR}/config
+    ${SDK_DIR}/include
+    ${SDK_DIR}/hal/include
+    ${SDK_DIR}/hal/utils/include
+    ${SDK_DIR}/hpl/pm
+    ${SDK_DIR}/hpl/port
+    ${SDK_DIR}/hri
+    ${SDK_DIR}/CMSIS/Include
+    )
+  target_compile_definitions(${BOARD_TARGET} PUBLIC
+    CONF_DFLL_OVERWRITE_CALIBRATION=0
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -nostartfiles
+      --specs=nosys.specs --specs=nano.specs
       )
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${SDK_DIR}
-      ${SDK_DIR}/config
-      ${SDK_DIR}/include
-      ${SDK_DIR}/hal/include
-      ${SDK_DIR}/hal/utils/include
-      ${SDK_DIR}/hpl/pm
-      ${SDK_DIR}/hpl/port
-      ${SDK_DIR}/hri
-      ${SDK_DIR}/CMSIS/Include
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_Clang}"
       )
-    target_compile_definitions(${BOARD_TARGET} PUBLIC CONF_DFLL_OVERWRITE_CALIBRATION=0)
-
-    update_board(${BOARD_TARGET})
-
-    if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
-      message(FATAL_ERROR "LD_FILE_${CMAKE_C_COMPILER_ID} not defined")
-    endif ()
-
-    if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
-      set(STARTUP_FILE_GNU ${SDK_DIR}/gcc/gcc/startup_samd21.c)
-    endif ()
-
-    target_sources(${BOARD_TARGET} PRIVATE
-      ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
       )
-
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--script=${LD_FILE_GNU}"
-        -nostartfiles
-        --specs=nosys.specs --specs=nano.specs
-        )
-    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--config=${LD_FILE_IAR}"
-        )
-    endif ()
   endif ()
 endfunction()
 
@@ -101,6 +106,7 @@ function(family_configure_example TARGET RTOS)
   target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
 
   # Flashing
+  family_add_bin_hex(${TARGET})
   family_flash_jlink(${TARGET})
-  #family_flash_openocd(${TARGET} ${OPENOCD_OPTION})
+  #family_flash_openocd(${TARGET})
 endfunction()
diff --git a/hw/bsp/samd21/family.mk b/hw/bsp/samd21/family.mk
index 3302aade5..08c5c5b0e 100644
--- a/hw/bsp/samd21/family.mk
+++ b/hw/bsp/samd21/family.mk
@@ -6,7 +6,6 @@ CPU_CORE ?= cortex-m0plus
 
 CFLAGS += \
   -flto \
-  -nostdlib -nostartfiles \
   -DCONF_DFLL_OVERWRITE_CALIBRATION=0 \
   -DCFG_TUSB_MCU=OPT_MCU_SAMD21
 
@@ -16,20 +15,24 @@ CFLAGS += -Wno-error=redundant-decls
 # SAM driver is flooded with -Wcast-qual which slow down complication significantly
 CFLAGS_SKIP += -Wcast-qual
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += \
+  -nostdlib -nostartfiles \
+  --specs=nosys.specs --specs=nano.specs \
+
+LDFLAGS_CLANG +=
 
 SRC_C += \
 	src/portable/microchip/samd/dcd_samd.c \
 	${SDK_DIR}/gcc/gcc/startup_samd21.c \
 	${SDK_DIR}/gcc/system_samd21.c \
+	${SDK_DIR}/hal/src/hal_atomic.c \
 	${SDK_DIR}/hpl/gclk/hpl_gclk.c \
 	${SDK_DIR}/hpl/pm/hpl_pm.c \
 	${SDK_DIR}/hpl/sysctrl/hpl_sysctrl.c \
-	${SDK_DIR}/hal/src/hal_atomic.c
 
 INC += \
 	$(TOP)/$(BOARD_PATH) \
-	$(TOP)/${SDK_DIR}/ \
+	$(TOP)/${SDK_DIR} \
 	$(TOP)/${SDK_DIR}/config \
 	$(TOP)/${SDK_DIR}/include \
 	$(TOP)/${SDK_DIR}/hal/include \
@@ -37,7 +40,7 @@ INC += \
 	$(TOP)/${SDK_DIR}/hpl/pm/ \
 	$(TOP)/${SDK_DIR}/hpl/port \
 	$(TOP)/${SDK_DIR}/hri \
-	$(TOP)/${SDK_DIR}/CMSIS/Include
+	$(TOP)/lib/CMSIS_5/CMSIS/Core/Include \
 
 # flash using bossac at least version 1.8
 # can be found in arduino15/packages/arduino/tools/bossac/
diff --git a/hw/bsp/samd51/boards/pyportal/board.cmake b/hw/bsp/samd51/boards/pyportal/board.cmake
deleted file mode 100644
index d83211d9e..000000000
--- a/hw/bsp/samd51/boards/pyportal/board.cmake
+++ /dev/null
@@ -1,8 +0,0 @@
-set(JLINK_DEVICE ATSAMD51J19)
-set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/${BOARD}.ld)
-
-function(update_board TARGET)
-  target_compile_definitions(${TARGET} PUBLIC
-    __SAMD51J19A__
-    )
-endfunction()
diff --git a/hw/bsp/samd51/family.mk b/hw/bsp/samd51/family.mk
deleted file mode 100644
index 94ca68705..000000000
--- a/hw/bsp/samd51/family.mk
+++ /dev/null
@@ -1,45 +0,0 @@
-UF2_FAMILY_ID = 0x55114460
-DEPS_SUBMODULES += hw/mcu/microchip
-
-include $(TOP)/$(BOARD_PATH)/board.mk
-CPU_CORE ?= cortex-m4
-
-CFLAGS += \
-  -flto \
-  -nostdlib -nostartfiles \
-  -DCFG_TUSB_MCU=OPT_MCU_SAMD51
-
-# SAM driver is flooded with -Wcast-qual which slow down complication significantly
-CFLAGS_SKIP += -Wcast-qual
-
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
-
-SRC_C += \
-	src/portable/microchip/samd/dcd_samd.c \
-	hw/mcu/microchip/samd51/gcc/gcc/startup_samd51.c \
-	hw/mcu/microchip/samd51/gcc/system_samd51.c \
-	hw/mcu/microchip/samd51/hpl/gclk/hpl_gclk.c \
-	hw/mcu/microchip/samd51/hpl/mclk/hpl_mclk.c \
-	hw/mcu/microchip/samd51/hpl/osc32kctrl/hpl_osc32kctrl.c \
-	hw/mcu/microchip/samd51/hpl/oscctrl/hpl_oscctrl.c \
-	hw/mcu/microchip/samd51/hal/src/hal_atomic.c
-
-INC += \
-	$(TOP)/$(BOARD_PATH) \
-	$(TOP)/hw/mcu/microchip/samd51/ \
-	$(TOP)/hw/mcu/microchip/samd51/config \
-	$(TOP)/hw/mcu/microchip/samd51/include \
-	$(TOP)/hw/mcu/microchip/samd51/hal/include \
-	$(TOP)/hw/mcu/microchip/samd51/hal/utils/include \
-	$(TOP)/hw/mcu/microchip/samd51/hpl/port \
-	$(TOP)/hw/mcu/microchip/samd51/hri \
-	$(TOP)/hw/mcu/microchip/samd51/CMSIS/Include
-
-# flash using bossac at least version 1.8
-# can be found in arduino15/packages/arduino/tools/bossac/
-# Add it to your PATH or change BOSSAC variable to match your installation
-BOSSAC = bossac
-
-flash-bossac: $(BUILD)/$(PROJECT).bin
-	@:$(call check_defined, SERIAL, example: SERIAL=/dev/ttyACM0)
-	$(BOSSAC) --port=$(SERIAL) -U -i --offset=0x4000 -e -w $^ -R
diff --git a/hw/bsp/samd51/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/samd5x_e5x/FreeRTOSConfig/FreeRTOSConfig.h
similarity index 100%
rename from hw/bsp/samd51/FreeRTOSConfig/FreeRTOSConfig.h
rename to hw/bsp/samd5x_e5x/FreeRTOSConfig/FreeRTOSConfig.h
diff --git a/hw/bsp/samd5x_e5x/boards/d5035_01/board.cmake b/hw/bsp/samd5x_e5x/boards/d5035_01/board.cmake
new file mode 100644
index 000000000..adf4f6a6d
--- /dev/null
+++ b/hw/bsp/samd5x_e5x/boards/d5035_01/board.cmake
@@ -0,0 +1,13 @@
+set(SAM_FAMILY same51)
+
+set(JLINK_DEVICE ATSAME51J19)
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/same51j19a_flash.ld)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    __SAME51J19A__
+    SVC_Handler=SVCall_Handler
+    CONF_CPU_FREQUENCY=80000000
+    CONF_GCLK_USB_FREQUENCY=48000000
+    )
+endfunction()
diff --git a/hw/bsp/samd5x_e5x/boards/d5035_01/board.h b/hw/bsp/samd5x_e5x/boards/d5035_01/board.h
new file mode 100644
index 000000000..2cf59f5d1
--- /dev/null
+++ b/hw/bsp/samd5x_e5x/boards/d5035_01/board.h
@@ -0,0 +1,198 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020, Ha Thach (tinyusb.org)
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// LED
+#define LED_PIN               PIN_PA02
+#define LED_STATE_ON          1
+
+// Button: no button
+
+// UART: HWREV < 3: SERCOM5 on PB02, otherwise SERCOM0 on PA08
+// XTAL configure is also different for HWREV as well
+
+#if 0
+static inline void init_clock(void) {
+  /* AUTOWS is enabled by default in REG_NVMCTRL_CTRLA - no need to change the number of wait states when changing the core clock */
+#if HWREV == 1
+  /* configure XOSC1 for a 16MHz crystal connected to XIN1/XOUT1 */
+  OSCCTRL->XOSCCTRL[1].reg =
+    OSCCTRL_XOSCCTRL_STARTUP(6) |    // 1,953 ms
+    OSCCTRL_XOSCCTRL_RUNSTDBY |
+    OSCCTRL_XOSCCTRL_ENALC |
+    OSCCTRL_XOSCCTRL_IMULT(4) |
+    OSCCTRL_XOSCCTRL_IPTAT(3) |
+    OSCCTRL_XOSCCTRL_XTALEN |
+    OSCCTRL_XOSCCTRL_ENABLE;
+  while(0 == OSCCTRL->STATUS.bit.XOSCRDY1);
+
+  OSCCTRL->Dpll[0].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(3) | OSCCTRL_DPLLCTRLB_REFCLK(OSCCTRL_DPLLCTRLB_REFCLK_XOSC1_Val); /* pre-scaler = 8, input = XOSC1 */
+  OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0x0) | OSCCTRL_DPLLRATIO_LDR(39); /* multiply by 40 -> 80 MHz */
+  OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_RUNSTDBY | OSCCTRL_DPLLCTRLA_ENABLE;
+  while(0 == OSCCTRL->Dpll[0].DPLLSTATUS.bit.CLKRDY); /* wait for the PLL0 to be ready */
+
+  OSCCTRL->Dpll[1].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(7) | OSCCTRL_DPLLCTRLB_REFCLK(OSCCTRL_DPLLCTRLB_REFCLK_XOSC1_Val); /* pre-scaler = 16, input = XOSC1 */
+  OSCCTRL->Dpll[1].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0x0) | OSCCTRL_DPLLRATIO_LDR(47); /* multiply by 48 -> 48 MHz */
+  OSCCTRL->Dpll[1].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_RUNSTDBY | OSCCTRL_DPLLCTRLA_ENABLE;
+  while(0 == OSCCTRL->Dpll[1].DPLLSTATUS.bit.CLKRDY); /* wait for the PLL1 to be ready */
+#else // HWREV >= 1
+  /* configure XOSC0 for a 16MHz crystal connected to XIN0/XOUT0 */
+  OSCCTRL->XOSCCTRL[0].reg =
+      OSCCTRL_XOSCCTRL_STARTUP(6) |    // 1,953 ms
+      OSCCTRL_XOSCCTRL_RUNSTDBY |
+      OSCCTRL_XOSCCTRL_ENALC |
+      OSCCTRL_XOSCCTRL_IMULT(4) |
+      OSCCTRL_XOSCCTRL_IPTAT(3) |
+      OSCCTRL_XOSCCTRL_XTALEN |
+      OSCCTRL_XOSCCTRL_ENABLE;
+  while (0 == OSCCTRL->STATUS.bit.XOSCRDY0);
+
+  OSCCTRL->Dpll[0].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(3) | OSCCTRL_DPLLCTRLB_REFCLK(
+      OSCCTRL_DPLLCTRLB_REFCLK_XOSC0_Val); /* pre-scaler = 8, input = XOSC1 */
+  OSCCTRL->Dpll[0].DPLLRATIO.reg =
+      OSCCTRL_DPLLRATIO_LDRFRAC(0x0) | OSCCTRL_DPLLRATIO_LDR(39); /* multiply by 40 -> 80 MHz */
+  OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_RUNSTDBY | OSCCTRL_DPLLCTRLA_ENABLE;
+  while (0 == OSCCTRL->Dpll[0].DPLLSTATUS.bit.CLKRDY); /* wait for the PLL0 to be ready */
+
+  OSCCTRL->Dpll[1].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(7) | OSCCTRL_DPLLCTRLB_REFCLK(
+      OSCCTRL_DPLLCTRLB_REFCLK_XOSC0_Val); /* pre-scaler = 16, input = XOSC1 */
+  OSCCTRL->Dpll[1].DPLLRATIO.reg =
+      OSCCTRL_DPLLRATIO_LDRFRAC(0x0) | OSCCTRL_DPLLRATIO_LDR(47); /* multiply by 48 -> 48 MHz */
+  OSCCTRL->Dpll[1].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_RUNSTDBY | OSCCTRL_DPLLCTRLA_ENABLE;
+  while (0 == OSCCTRL->Dpll[1].DPLLSTATUS.bit.CLKRDY); /* wait for the PLL1 to be ready */
+#endif // HWREV
+
+  /* configure clock-generator 0 to use DPLL0 as source -> GCLK0 is used for the core */
+  GCLK->GENCTRL[0].reg =
+      GCLK_GENCTRL_DIV(0) |
+      GCLK_GENCTRL_RUNSTDBY |
+      GCLK_GENCTRL_GENEN |
+      GCLK_GENCTRL_SRC_DPLL0 |  /* DPLL0 */
+      GCLK_GENCTRL_IDC;
+  while (1 == GCLK->SYNCBUSY.bit.GENCTRL0); /* wait for the synchronization between clock domains to be complete */
+
+  /* configure clock-generator 1 to use DPLL1 as source -> for use with some peripheral */
+  GCLK->GENCTRL[1].reg =
+      GCLK_GENCTRL_DIV(0) |
+      GCLK_GENCTRL_RUNSTDBY |
+      GCLK_GENCTRL_GENEN |
+      GCLK_GENCTRL_SRC_DPLL1 |
+      GCLK_GENCTRL_IDC;
+  while (1 == GCLK->SYNCBUSY.bit.GENCTRL1); /* wait for the synchronization between clock domains to be complete */
+
+  /* configure clock-generator 2 to use DPLL0 as source -> for use with SERCOM */
+  GCLK->GENCTRL[2].reg =
+      GCLK_GENCTRL_DIV(1) |  /* 80MHz */
+      GCLK_GENCTRL_RUNSTDBY |
+      GCLK_GENCTRL_GENEN |
+      GCLK_GENCTRL_SRC_DPLL0 |
+      GCLK_GENCTRL_IDC;
+  while (1 == GCLK->SYNCBUSY.bit.GENCTRL2); /* wait for the synchronization between clock domains to be complete */
+}
+
+static inline void uart_init(void) {
+#if HWREV < 3
+  /* configure SERCOM5 on PB02 */
+  PORT->Group[1].WRCONFIG.reg =
+      PORT_WRCONFIG_WRPINCFG |
+      PORT_WRCONFIG_WRPMUX |
+      PORT_WRCONFIG_PMUX(3) |    /* function D */
+      PORT_WRCONFIG_DRVSTR |
+      PORT_WRCONFIG_PINMASK(0x0004) | /* PB02 */
+      PORT_WRCONFIG_PMUXEN;
+
+  MCLK->APBDMASK.bit.SERCOM5_ = 1;
+  GCLK->PCHCTRL[SERCOM5_GCLK_ID_CORE].reg =
+      GCLK_PCHCTRL_GEN_GCLK2 | GCLK_PCHCTRL_CHEN; /* setup SERCOM to use GLCK2 -> 80MHz */
+
+  SERCOM5->USART.CTRLA.reg = 0x00; /* disable SERCOM -> enable config */
+  while (SERCOM5->USART.SYNCBUSY.bit.ENABLE);
+
+  SERCOM5->USART.CTRLA.reg =  /* CMODE = 0 -> async, SAMPA = 0, FORM = 0 -> USART frame, SMPR = 0 -> arithmetic baud rate */
+      SERCOM_USART_CTRLA_SAMPR(1) | /* 0 = 16x / arithmetic baud rate, 1 = 16x / fractional baud rate */
+      //    SERCOM_USART_CTRLA_FORM(0) | /* 0 = USART Frame, 2 = LIN Master */
+      SERCOM_USART_CTRLA_DORD | /* LSB first */
+      SERCOM_USART_CTRLA_MODE(1) | /* 0 = Asynchronous, 1 = USART with internal clock */
+      SERCOM_USART_CTRLA_RXPO(1) | /* SERCOM PAD[1] is used for data reception */
+      SERCOM_USART_CTRLA_TXPO(0); /* SERCOM PAD[0] is used for data transmission */
+
+  SERCOM5->USART.CTRLB.reg = /* RXEM = 0 -> receiver disabled, LINCMD = 0 -> normal USART transmission, SFDE = 0 -> start-of-frame detection disabled, SBMODE = 0 -> one stop bit, CHSIZE = 0 -> 8 bits */
+      SERCOM_USART_CTRLB_TXEN; /* transmitter enabled */
+  SERCOM5->USART.CTRLC.reg = 0x00;
+  // 21.701388889 @ baud rate of 230400 bit/s, table 33-2, p 918 of DS60001507E
+  SERCOM5->USART.BAUD.reg = SERCOM_USART_BAUD_FRAC_FP(7) | SERCOM_USART_BAUD_FRAC_BAUD(21);
+
+//  SERCOM5->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC;
+  SERCOM5->SPI.CTRLA.bit.ENABLE = 1; /* activate SERCOM */
+  while (SERCOM5->USART.SYNCBUSY.bit.ENABLE); /* wait for SERCOM to be ready */
+#else
+  /* configure SERCOM0 on PA08 */
+    PORT->Group[0].WRCONFIG.reg =
+      PORT_WRCONFIG_WRPINCFG |
+      PORT_WRCONFIG_WRPMUX |
+      PORT_WRCONFIG_PMUX(2) |    /* function C */
+      PORT_WRCONFIG_DRVSTR |
+      PORT_WRCONFIG_PINMASK(0x0100) | /* PA08 */
+      PORT_WRCONFIG_PMUXEN;
+
+    MCLK->APBAMASK.bit.SERCOM0_ = 1;
+    GCLK->PCHCTRL[SERCOM0_GCLK_ID_CORE].reg = GCLK_PCHCTRL_GEN_GCLK2 | GCLK_PCHCTRL_CHEN; /* setup SERCOM to use GLCK2 -> 80MHz */
+
+    SERCOM0->USART.CTRLA.reg = 0x00; /* disable SERCOM -> enable config */
+    while(SERCOM0->USART.SYNCBUSY.bit.ENABLE);
+
+    SERCOM0->USART.CTRLA.reg  =  /* CMODE = 0 -> async, SAMPA = 0, FORM = 0 -> USART frame, SMPR = 0 -> arithmetic baud rate */
+      SERCOM_USART_CTRLA_SAMPR(1) | /* 0 = 16x / arithmetic baud rate, 1 = 16x / fractional baud rate */
+  //    SERCOM_USART_CTRLA_FORM(0) | /* 0 = USART Frame, 2 = LIN Master */
+      SERCOM_USART_CTRLA_DORD | /* LSB first */
+      SERCOM_USART_CTRLA_MODE(1) | /* 0 = Asynchronous, 1 = USART with internal clock */
+      SERCOM_USART_CTRLA_RXPO(1) | /* SERCOM PAD[1] is used for data reception */
+      SERCOM_USART_CTRLA_TXPO(0); /* SERCOM PAD[0] is used for data transmission */
+
+    SERCOM0->USART.CTRLB.reg = /* RXEM = 0 -> receiver disabled, LINCMD = 0 -> normal USART transmission, SFDE = 0 -> start-of-frame detection disabled, SBMODE = 0 -> one stop bit, CHSIZE = 0 -> 8 bits */
+      SERCOM_USART_CTRLB_TXEN; /* transmitter enabled */
+    SERCOM0->USART.CTRLC.reg = 0x00;
+    // 21.701388889 @ baud rate of 230400 bit/s, table 33-2, p 918 of DS60001507E
+    SERCOM0->USART.BAUD.reg = SERCOM_USART_BAUD_FRAC_FP(7) | SERCOM_USART_BAUD_FRAC_BAUD(21);
+
+  //  SERCOM0->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC;
+    SERCOM0->SPI.CTRLA.bit.ENABLE = 1; /* activate SERCOM */
+    while(SERCOM0->USART.SYNCBUSY.bit.ENABLE); /* wait for SERCOM to be ready */
+#endif
+}
+#endif
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* BOARD_H_ */
diff --git a/hw/bsp/same5x/boards/d5035_01/board.mk b/hw/bsp/samd5x_e5x/boards/d5035_01/board.mk
similarity index 95%
rename from hw/bsp/same5x/boards/d5035_01/board.mk
rename to hw/bsp/samd5x_e5x/boards/d5035_01/board.mk
index c53411bb8..4aa6b89fe 100644
--- a/hw/bsp/same5x/boards/d5035_01/board.mk
+++ b/hw/bsp/samd5x_e5x/boards/d5035_01/board.mk
@@ -1,4 +1,4 @@
-MCU = same51
+SAM_FAMILY = same51
 
 HWREV ?= 1
 
diff --git a/hw/bsp/same5x/boards/d5035_01/same51j19a_flash.ld b/hw/bsp/samd5x_e5x/boards/d5035_01/same51j19a_flash.ld
similarity index 97%
rename from hw/bsp/same5x/boards/d5035_01/same51j19a_flash.ld
rename to hw/bsp/samd5x_e5x/boards/d5035_01/same51j19a_flash.ld
index a8dd44336..59afb604b 100644
--- a/hw/bsp/same5x/boards/d5035_01/same51j19a_flash.ld
+++ b/hw/bsp/samd5x_e5x/boards/d5035_01/same51j19a_flash.ld
@@ -42,7 +42,9 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x1000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x1000;
+
+ENTRY(Reset_Handler)
 
 /* Section Definitions */
 SECTIONS
diff --git a/hw/bsp/samd51/boards/metro_m4_express/board.cmake b/hw/bsp/samd5x_e5x/boards/feather_m4_express/board.cmake
similarity index 89%
rename from hw/bsp/samd51/boards/metro_m4_express/board.cmake
rename to hw/bsp/samd5x_e5x/boards/feather_m4_express/board.cmake
index d83211d9e..86d12ca24 100644
--- a/hw/bsp/samd51/boards/metro_m4_express/board.cmake
+++ b/hw/bsp/samd5x_e5x/boards/feather_m4_express/board.cmake
@@ -1,3 +1,5 @@
+set(SAM_FAMILY samd51)
+
 set(JLINK_DEVICE ATSAMD51J19)
 set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/${BOARD}.ld)
 
diff --git a/hw/bsp/samd51/boards/feather_m4_express/board.h b/hw/bsp/samd5x_e5x/boards/feather_m4_express/board.h
similarity index 100%
rename from hw/bsp/samd51/boards/feather_m4_express/board.h
rename to hw/bsp/samd5x_e5x/boards/feather_m4_express/board.h
diff --git a/hw/bsp/samd51/boards/pyportal/board.mk b/hw/bsp/samd5x_e5x/boards/feather_m4_express/board.mk
similarity index 86%
rename from hw/bsp/samd51/boards/pyportal/board.mk
rename to hw/bsp/samd5x_e5x/boards/feather_m4_express/board.mk
index a8a98a987..811c5f4e3 100644
--- a/hw/bsp/samd51/boards/pyportal/board.mk
+++ b/hw/bsp/samd5x_e5x/boards/feather_m4_express/board.mk
@@ -1,3 +1,5 @@
+SAM_FAMILY = samd51
+
 CFLAGS += -D__SAMD51J19A__
 
 LD_FILE = $(BOARD_PATH)/$(BOARD).ld
diff --git a/hw/bsp/samd51/boards/feather_m4_express/feather_m4_express.ld b/hw/bsp/samd5x_e5x/boards/feather_m4_express/feather_m4_express.ld
similarity index 97%
rename from hw/bsp/samd51/boards/feather_m4_express/feather_m4_express.ld
rename to hw/bsp/samd5x_e5x/boards/feather_m4_express/feather_m4_express.ld
index f1a021d75..1408c3018 100644
--- a/hw/bsp/samd51/boards/feather_m4_express/feather_m4_express.ld
+++ b/hw/bsp/samd5x_e5x/boards/feather_m4_express/feather_m4_express.ld
@@ -42,7 +42,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd51/boards/feather_m4_express/board.cmake b/hw/bsp/samd5x_e5x/boards/itsybitsy_m4/board.cmake
similarity index 89%
rename from hw/bsp/samd51/boards/feather_m4_express/board.cmake
rename to hw/bsp/samd5x_e5x/boards/itsybitsy_m4/board.cmake
index d83211d9e..86d12ca24 100644
--- a/hw/bsp/samd51/boards/feather_m4_express/board.cmake
+++ b/hw/bsp/samd5x_e5x/boards/itsybitsy_m4/board.cmake
@@ -1,3 +1,5 @@
+set(SAM_FAMILY samd51)
+
 set(JLINK_DEVICE ATSAMD51J19)
 set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/${BOARD}.ld)
 
diff --git a/hw/bsp/samd51/boards/itsybitsy_m4/board.h b/hw/bsp/samd5x_e5x/boards/itsybitsy_m4/board.h
similarity index 100%
rename from hw/bsp/samd51/boards/itsybitsy_m4/board.h
rename to hw/bsp/samd5x_e5x/boards/itsybitsy_m4/board.h
diff --git a/hw/bsp/samd51/boards/itsybitsy_m4/board.mk b/hw/bsp/samd5x_e5x/boards/itsybitsy_m4/board.mk
similarity index 90%
rename from hw/bsp/samd51/boards/itsybitsy_m4/board.mk
rename to hw/bsp/samd5x_e5x/boards/itsybitsy_m4/board.mk
index 57a680e91..eba7070c1 100644
--- a/hw/bsp/samd51/boards/itsybitsy_m4/board.mk
+++ b/hw/bsp/samd5x_e5x/boards/itsybitsy_m4/board.mk
@@ -1,3 +1,5 @@
+SAM_FAMILY = samd51
+
 CFLAGS += -D__SAMD51J19A__
 
 # All source paths should be relative to the top level.
diff --git a/hw/bsp/samd51/boards/itsybitsy_m4/itsybitsy_m4.ld b/hw/bsp/samd5x_e5x/boards/itsybitsy_m4/itsybitsy_m4.ld
similarity index 97%
rename from hw/bsp/samd51/boards/itsybitsy_m4/itsybitsy_m4.ld
rename to hw/bsp/samd5x_e5x/boards/itsybitsy_m4/itsybitsy_m4.ld
index f1a021d75..1408c3018 100644
--- a/hw/bsp/samd51/boards/itsybitsy_m4/itsybitsy_m4.ld
+++ b/hw/bsp/samd5x_e5x/boards/itsybitsy_m4/itsybitsy_m4.ld
@@ -42,7 +42,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd51/boards/pybadge/board.cmake b/hw/bsp/samd5x_e5x/boards/metro_m4_express/board.cmake
similarity index 89%
rename from hw/bsp/samd51/boards/pybadge/board.cmake
rename to hw/bsp/samd5x_e5x/boards/metro_m4_express/board.cmake
index d83211d9e..86d12ca24 100644
--- a/hw/bsp/samd51/boards/pybadge/board.cmake
+++ b/hw/bsp/samd5x_e5x/boards/metro_m4_express/board.cmake
@@ -1,3 +1,5 @@
+set(SAM_FAMILY samd51)
+
 set(JLINK_DEVICE ATSAMD51J19)
 set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/${BOARD}.ld)
 
diff --git a/hw/bsp/samd51/boards/metro_m4_express/board.h b/hw/bsp/samd5x_e5x/boards/metro_m4_express/board.h
similarity index 100%
rename from hw/bsp/samd51/boards/metro_m4_express/board.h
rename to hw/bsp/samd5x_e5x/boards/metro_m4_express/board.h
diff --git a/hw/bsp/samd51/boards/metro_m4_express/board.mk b/hw/bsp/samd5x_e5x/boards/metro_m4_express/board.mk
similarity index 90%
rename from hw/bsp/samd51/boards/metro_m4_express/board.mk
rename to hw/bsp/samd5x_e5x/boards/metro_m4_express/board.mk
index 57a680e91..eba7070c1 100644
--- a/hw/bsp/samd51/boards/metro_m4_express/board.mk
+++ b/hw/bsp/samd5x_e5x/boards/metro_m4_express/board.mk
@@ -1,3 +1,5 @@
+SAM_FAMILY = samd51
+
 CFLAGS += -D__SAMD51J19A__
 
 # All source paths should be relative to the top level.
diff --git a/hw/bsp/samd51/boards/metro_m4_express/metro_m4_express.ld b/hw/bsp/samd5x_e5x/boards/metro_m4_express/metro_m4_express.ld
similarity index 97%
rename from hw/bsp/samd51/boards/metro_m4_express/metro_m4_express.ld
rename to hw/bsp/samd5x_e5x/boards/metro_m4_express/metro_m4_express.ld
index f1a021d75..1408c3018 100644
--- a/hw/bsp/samd51/boards/metro_m4_express/metro_m4_express.ld
+++ b/hw/bsp/samd5x_e5x/boards/metro_m4_express/metro_m4_express.ld
@@ -42,7 +42,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd51/boards/itsybitsy_m4/board.cmake b/hw/bsp/samd5x_e5x/boards/pybadge/board.cmake
similarity index 89%
rename from hw/bsp/samd51/boards/itsybitsy_m4/board.cmake
rename to hw/bsp/samd5x_e5x/boards/pybadge/board.cmake
index d83211d9e..86d12ca24 100644
--- a/hw/bsp/samd51/boards/itsybitsy_m4/board.cmake
+++ b/hw/bsp/samd5x_e5x/boards/pybadge/board.cmake
@@ -1,3 +1,5 @@
+set(SAM_FAMILY samd51)
+
 set(JLINK_DEVICE ATSAMD51J19)
 set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/${BOARD}.ld)
 
diff --git a/hw/bsp/samd51/boards/pybadge/board.h b/hw/bsp/samd5x_e5x/boards/pybadge/board.h
similarity index 100%
rename from hw/bsp/samd51/boards/pybadge/board.h
rename to hw/bsp/samd5x_e5x/boards/pybadge/board.h
diff --git a/hw/bsp/samd51/boards/pybadge/board.mk b/hw/bsp/samd5x_e5x/boards/pybadge/board.mk
similarity index 86%
rename from hw/bsp/samd51/boards/pybadge/board.mk
rename to hw/bsp/samd5x_e5x/boards/pybadge/board.mk
index a8a98a987..811c5f4e3 100644
--- a/hw/bsp/samd51/boards/pybadge/board.mk
+++ b/hw/bsp/samd5x_e5x/boards/pybadge/board.mk
@@ -1,3 +1,5 @@
+SAM_FAMILY = samd51
+
 CFLAGS += -D__SAMD51J19A__
 
 LD_FILE = $(BOARD_PATH)/$(BOARD).ld
diff --git a/hw/bsp/samd51/boards/pybadge/pybadge.ld b/hw/bsp/samd5x_e5x/boards/pybadge/pybadge.ld
similarity index 97%
rename from hw/bsp/samd51/boards/pybadge/pybadge.ld
rename to hw/bsp/samd5x_e5x/boards/pybadge/pybadge.ld
index f1a021d75..1408c3018 100644
--- a/hw/bsp/samd51/boards/pybadge/pybadge.ld
+++ b/hw/bsp/samd5x_e5x/boards/pybadge/pybadge.ld
@@ -42,7 +42,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd5x_e5x/boards/pyportal/board.cmake b/hw/bsp/samd5x_e5x/boards/pyportal/board.cmake
new file mode 100644
index 000000000..86d12ca24
--- /dev/null
+++ b/hw/bsp/samd5x_e5x/boards/pyportal/board.cmake
@@ -0,0 +1,10 @@
+set(SAM_FAMILY samd51)
+
+set(JLINK_DEVICE ATSAMD51J19)
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/${BOARD}.ld)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    __SAMD51J19A__
+    )
+endfunction()
diff --git a/hw/bsp/samd51/boards/pyportal/board.h b/hw/bsp/samd5x_e5x/boards/pyportal/board.h
similarity index 100%
rename from hw/bsp/samd51/boards/pyportal/board.h
rename to hw/bsp/samd5x_e5x/boards/pyportal/board.h
diff --git a/hw/bsp/samd51/boards/feather_m4_express/board.mk b/hw/bsp/samd5x_e5x/boards/pyportal/board.mk
similarity index 86%
rename from hw/bsp/samd51/boards/feather_m4_express/board.mk
rename to hw/bsp/samd5x_e5x/boards/pyportal/board.mk
index a8a98a987..811c5f4e3 100644
--- a/hw/bsp/samd51/boards/feather_m4_express/board.mk
+++ b/hw/bsp/samd5x_e5x/boards/pyportal/board.mk
@@ -1,3 +1,5 @@
+SAM_FAMILY = samd51
+
 CFLAGS += -D__SAMD51J19A__
 
 LD_FILE = $(BOARD_PATH)/$(BOARD).ld
diff --git a/hw/bsp/samd51/boards/pyportal/pyportal.ld b/hw/bsp/samd5x_e5x/boards/pyportal/pyportal.ld
similarity index 97%
rename from hw/bsp/samd51/boards/pyportal/pyportal.ld
rename to hw/bsp/samd5x_e5x/boards/pyportal/pyportal.ld
index f1a021d75..1408c3018 100644
--- a/hw/bsp/samd51/boards/pyportal/pyportal.ld
+++ b/hw/bsp/samd5x_e5x/boards/pyportal/pyportal.ld
@@ -42,7 +42,7 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0xC000;
 
 ENTRY(Reset_Handler)
 
diff --git a/hw/bsp/samd5x_e5x/boards/same54_xplained/board.cmake b/hw/bsp/samd5x_e5x/boards/same54_xplained/board.cmake
new file mode 100644
index 000000000..4d98205bc
--- /dev/null
+++ b/hw/bsp/samd5x_e5x/boards/same54_xplained/board.cmake
@@ -0,0 +1,10 @@
+set(SAM_FAMILY same54)
+
+set(JLINK_DEVICE ATSAME54P20)
+set(LD_FILE_GNU ${CMAKE_CURRENT_LIST_DIR}/same54p20a_flash.ld)
+
+function(update_board TARGET)
+  target_compile_definitions(${TARGET} PUBLIC
+    __SAME54P20A__
+    )
+endfunction()
diff --git a/hw/bsp/samd5x_e5x/boards/same54_xplained/board.h b/hw/bsp/samd5x_e5x/boards/same54_xplained/board.h
new file mode 100644
index 000000000..faaa52b8e
--- /dev/null
+++ b/hw/bsp/samd5x_e5x/boards/same54_xplained/board.h
@@ -0,0 +1,50 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020, Ha Thach (tinyusb.org)
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+// LED
+#define LED_PIN               PIN_PC18
+#define LED_STATE_ON          1
+
+// Button: D5
+#define BUTTON_PIN            PIN_PB31
+#define BUTTON_STATE_ACTIVE   0
+
+// UART: SERCOM2
+//#define UART_TX_PIN           23
+//#define UART_RX_PIN           22
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* BOARD_H_ */
diff --git a/hw/bsp/same5x/boards/same54_xplained/board.mk b/hw/bsp/samd5x_e5x/boards/same54_xplained/board.mk
similarity index 54%
rename from hw/bsp/same5x/boards/same54_xplained/board.mk
rename to hw/bsp/samd5x_e5x/boards/same54_xplained/board.mk
index 41cf95bfc..d10e9e34c 100644
--- a/hw/bsp/same5x/boards/same54_xplained/board.mk
+++ b/hw/bsp/samd5x_e5x/boards/same54_xplained/board.mk
@@ -1,9 +1,6 @@
-MCU = same54
+SAM_FAMILY = same54
 
-CFLAGS += \
-  -DCONF_CPU_FREQUENCY=48000000 \
-  -D__SAME54P20A__ \
-  -DBOARD_NAME="\"Microchip SAM E54 Xplained Pro\""
+CFLAGS += -D__SAME54P20A__
 
 # All source paths should be relative to the top level.
 LD_FILE = $(BOARD_PATH)/same54p20a_flash.ld
diff --git a/hw/bsp/same5x/boards/same54_xplained/same54p20a_flash.ld b/hw/bsp/samd5x_e5x/boards/same54_xplained/same54p20a_flash.ld
similarity index 97%
rename from hw/bsp/same5x/boards/same54_xplained/same54p20a_flash.ld
rename to hw/bsp/samd5x_e5x/boards/same54_xplained/same54p20a_flash.ld
index 1f427a066..8a9fd7d9f 100644
--- a/hw/bsp/same5x/boards/same54_xplained/same54p20a_flash.ld
+++ b/hw/bsp/samd5x_e5x/boards/same54_xplained/same54p20a_flash.ld
@@ -42,7 +42,9 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x10000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x10000;
+
+ENTRY(Reset_Handler)
 
 /* Section Definitions */
 SECTIONS
@@ -160,4 +162,5 @@ SECTIONS
 
     . = ALIGN(4);
     _end = . ;
+    end = .;
 }
diff --git a/hw/bsp/same5x/boards/same54_xplained/same54p20a_sram.ld b/hw/bsp/samd5x_e5x/boards/same54_xplained/same54p20a_sram.ld
similarity index 97%
rename from hw/bsp/same5x/boards/same54_xplained/same54p20a_sram.ld
rename to hw/bsp/samd5x_e5x/boards/same54_xplained/same54p20a_sram.ld
index e6e33ec48..c88617729 100644
--- a/hw/bsp/same5x/boards/same54_xplained/same54p20a_sram.ld
+++ b/hw/bsp/samd5x_e5x/boards/same54_xplained/same54p20a_sram.ld
@@ -41,7 +41,9 @@ MEMORY
 }
 
 /* The stack size used by the application. NOTE: you need to adjust according to your application. */
-STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x10000;
+STACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x10000;
+
+ENTRY(Reset_Handler)
 
 /* Section Definitions */
 SECTIONS
@@ -159,4 +161,5 @@ SECTIONS
 
     . = ALIGN(4);
     _end = . ;
+    end = .;
 }
diff --git a/hw/bsp/samd51/family.c b/hw/bsp/samd5x_e5x/family.c
similarity index 83%
rename from hw/bsp/samd51/family.c
rename to hw/bsp/samd5x_e5x/family.c
index 170c88191..abaee353b 100644
--- a/hw/bsp/samd51/family.c
+++ b/hw/bsp/samd5x_e5x/family.c
@@ -34,8 +34,8 @@
 #pragma GCC diagnostic ignored "-Wcast-qual"
 #endif
 
-#include "hal/include/hal_gpio.h"
-#include "hal/include/hal_init.h"
+#include "hal_gpio.h"
+#include "hal_init.h"
 #include "hpl/gclk/hpl_gclk_base.h"
 #include "hpl_mclk_config.h"
 
@@ -106,9 +106,11 @@ void board_init(void) {
   gpio_set_pin_direction(LED_PIN, GPIO_DIRECTION_OUT);
   gpio_set_pin_level(LED_PIN, 0);
 
+#ifdef BUTTON_PIN
   // Button init
   gpio_set_pin_direction(BUTTON_PIN, GPIO_DIRECTION_IN);
   gpio_set_pin_pull_mode(BUTTON_PIN, GPIO_PULL_UP);
+#endif
 
 #if CFG_TUSB_OS == OPT_OS_FREERTOS
   // If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
@@ -154,7 +156,11 @@ void board_led_write(bool state) {
 
 uint32_t board_button_read(void) {
   // button is active low
+  #ifdef BUTTON_PIN
   return gpio_get_pin_level(BUTTON_PIN) ? 0 : 1;
+  #else
+  return 0;
+  #endif
 }
 
 size_t board_get_unique_id(uint8_t id[], size_t max_len) {
@@ -194,6 +200,53 @@ uint32_t board_millis(void) {
   return system_ticks;
 }
 
+#if 0
+/* Initialize SERCOM2 for 115200 bps 8N1 using a 48 MHz clock */
+static inline void uart_init(void) {
+  gpio_set_pin_function(PIN_PB24, PINMUX_PB24D_SERCOM2_PAD1);
+  gpio_set_pin_function(PIN_PB25, PINMUX_PB25D_SERCOM2_PAD0);
+
+  MCLK->APBBMASK.bit.SERCOM2_ = 1;
+  GCLK->PCHCTRL[SERCOM2_GCLK_ID_CORE].reg = GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN;
+
+  BOARD_SERCOM->USART.CTRLA.bit.SWRST = 1; /* reset and disable SERCOM -> enable configuration */
+  while (BOARD_SERCOM->USART.SYNCBUSY.bit.SWRST);
+
+  BOARD_SERCOM->USART.CTRLA.reg =
+      SERCOM_USART_CTRLA_SAMPR(0) | /* 0 = 16x / arithmetic baud rate, 1 = 16x / fractional baud rate */
+      SERCOM_USART_CTRLA_SAMPA(0) | /* 16x over sampling */
+      SERCOM_USART_CTRLA_FORM(0) | /* 0x0 USART frame, 0x1 USART frame with parity, ... */
+      SERCOM_USART_CTRLA_DORD | /* LSB first */
+      SERCOM_USART_CTRLA_MODE(1) | /* 0x0 USART with external clock, 0x1 USART with internal clock */
+      SERCOM_USART_CTRLA_RXPO(1) | /* SERCOM PAD[1] is used for data reception */
+      SERCOM_USART_CTRLA_TXPO(0); /* SERCOM PAD[0] is used for data transmission */
+
+  BOARD_SERCOM->USART.CTRLB.reg = /* RXEM = 0 -> receiver disabled, LINCMD = 0 -> normal USART transmission, SFDE = 0 -> start-of-frame detection disabled, SBMODE = 0 -> one stop bit, CHSIZE = 0 -> 8 bits */
+      SERCOM_USART_CTRLB_TXEN | /* transmitter enabled */
+      SERCOM_USART_CTRLB_RXEN; /* receiver enabled */
+  // BOARD_SERCOM->USART.BAUD.reg = SERCOM_USART_BAUD_FRAC_FP(0) | SERCOM_USART_BAUD_FRAC_BAUD(26); /* 48000000/(16*115200) = 26.041666667 */
+  BOARD_SERCOM->USART.BAUD.reg = SERCOM_USART_BAUD_BAUD(63019); /* 65536*(1−16*115200/48000000) */
+
+  BOARD_SERCOM->USART.CTRLA.bit.ENABLE = 1; /* activate SERCOM */
+  while (BOARD_SERCOM->USART.SYNCBUSY.bit.ENABLE); /* wait for SERCOM to be ready */
+}
+
+static inline void uart_send_buffer(uint8_t const* text, size_t len) {
+  for (size_t i = 0; i < len; ++i) {
+    BOARD_SERCOM->USART.DATA.reg = text[i];
+    while ((BOARD_SERCOM->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) == 0);
+  }
+}
+
+static inline void uart_send_str(const char* text) {
+  while (*text) {
+    BOARD_SERCOM->USART.DATA.reg = *text++;
+    while ((BOARD_SERCOM->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) == 0);
+  }
+}
+
+#endif
+
 #endif
 
 //--------------------------------------------------------------------+
@@ -388,3 +441,8 @@ bool tuh_max3421_spi_xfer_api(uint8_t rhport, uint8_t const* tx_buf, uint8_t* rx
 }
 
 #endif
+
+void HardFault_Handler(void) {
+  __BKPT(0);
+  while (1);
+}
diff --git a/hw/bsp/samd51/family.cmake b/hw/bsp/samd5x_e5x/family.cmake
similarity index 53%
rename from hw/bsp/samd51/family.cmake
rename to hw/bsp/samd5x_e5x/family.cmake
index 4d91bf1a6..fd95ce10e 100644
--- a/hw/bsp/samd51/family.cmake
+++ b/hw/bsp/samd5x_e5x/family.cmake
@@ -1,15 +1,15 @@
 include_guard()
 
-set(SDK_DIR ${TOP}/hw/mcu/microchip/samd51)
-
-# include board specific
 include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
 
+set(SDK_DIR ${TOP}/hw/mcu/microchip/${SAM_FAMILY})
+set(CMSIS_5 ${TOP}/lib/CMSIS_5)
+
 # toolchain set up
 set(CMAKE_SYSTEM_PROCESSOR cortex-m4 CACHE INTERNAL "System Processor")
 set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
 
-set(FAMILY_MCUS SAMD51 CACHE INTERNAL "")
+set(FAMILY_MCUS SAMD51 SAME54 CACHE INTERNAL "")
 set(OPENOCD_OPTION "-f interface/cmsis-dap.cfg -c \"transport select swd\" -c \"set CHIPNAME samd51\" -f target/atsame5x.cfg")
 
 #------------------------------------
@@ -17,51 +17,54 @@ set(OPENOCD_OPTION "-f interface/cmsis-dap.cfg -c \"transport select swd\" -c \"
 #------------------------------------
 # only need to be built ONCE for all examples
 function(add_board_target BOARD_TARGET)
-  if (NOT TARGET ${BOARD_TARGET})
-    add_library(${BOARD_TARGET} STATIC
-      ${SDK_DIR}/gcc/system_samd51.c
-      ${SDK_DIR}/hpl/gclk/hpl_gclk.c
-      ${SDK_DIR}/hpl/mclk/hpl_mclk.c
-      ${SDK_DIR}/hpl/osc32kctrl/hpl_osc32kctrl.c
-      ${SDK_DIR}/hpl/oscctrl/hpl_oscctrl.c
-      ${SDK_DIR}/hal/src/hal_atomic.c
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif ()
+
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+  if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
+    message(FATAL_ERROR "LD_FILE_${CMAKE_C_COMPILER_ID} not defined")
+  endif ()
+
+  set(STARTUP_FILE_GNU ${SDK_DIR}/gcc/gcc/startup_${SAM_FAMILY}.c)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/gcc/system_${SAM_FAMILY}.c
+    ${SDK_DIR}/hal/src/hal_atomic.c
+    ${SDK_DIR}/hpl/gclk/hpl_gclk.c
+    ${SDK_DIR}/hpl/mclk/hpl_mclk.c
+    ${SDK_DIR}/hpl/osc32kctrl/hpl_osc32kctrl.c
+    ${SDK_DIR}/hpl/oscctrl/hpl_oscctrl.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_DIR}
+    ${SDK_DIR}/config
+    ${SDK_DIR}/include
+    ${SDK_DIR}/hal/include
+    ${SDK_DIR}/hal/utils/include
+    ${SDK_DIR}/hpl/port
+    ${SDK_DIR}/hri
+    ${CMSIS_5}/CMSIS/Core/Include
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -nostartfiles
+      --specs=nosys.specs --specs=nano.specs
       )
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${SDK_DIR}/
-      ${SDK_DIR}/config
-      ${SDK_DIR}/include
-      ${SDK_DIR}/hal/include
-      ${SDK_DIR}/hal/utils/include
-      ${SDK_DIR}/hpl/port
-      ${SDK_DIR}/hri
-      ${SDK_DIR}/CMSIS/Include
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_Clang}"
       )
-
-    update_board(${BOARD_TARGET})
-
-    if (NOT DEFINED LD_FILE_${CMAKE_C_COMPILER_ID})
-      message(FATAL_ERROR "LD_FILE_${CMAKE_C_COMPILER_ID} not defined")
-    endif ()
-
-    if (NOT DEFINED STARTUP_FILE_${CMAKE_C_COMPILER_ID})
-      set(STARTUP_FILE_GNU ${SDK_DIR}/gcc/gcc/startup_samd51.c)
-    endif ()
-
-    target_sources(${BOARD_TARGET} PRIVATE
-      ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
       )
-
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--script=${LD_FILE_GNU}"
-        -nostartfiles
-        --specs=nosys.specs --specs=nano.specs
-        )
-    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--config=${LD_FILE_IAR}"
-        )
-    endif ()
   endif ()
 endfunction()
 
@@ -102,5 +105,5 @@ function(family_configure_example TARGET RTOS)
   # Flashing
   family_add_bin_hex(${TARGET})
   family_flash_jlink(${TARGET})
-  #family_flash_openocd(${TARGET} ${OPENOCD_OPTION})
+  #family_flash_openocd(${TARGET})
 endfunction()
diff --git a/hw/bsp/samd5x_e5x/family.mk b/hw/bsp/samd5x_e5x/family.mk
new file mode 100644
index 000000000..9b1a23db4
--- /dev/null
+++ b/hw/bsp/samd5x_e5x/family.mk
@@ -0,0 +1,51 @@
+UF2_FAMILY_ID = 0x55114460
+
+include $(TOP)/$(BOARD_PATH)/board.mk
+CPU_CORE ?= cortex-m4
+
+SDK_DIR = hw/mcu/microchip/${SAM_FAMILY}
+
+CFLAGS += \
+  -flto \
+  -DCFG_TUSB_MCU=OPT_MCU_SAMD51
+
+# SAM driver is flooded with -Wcast-qual which slow down complication significantly
+CFLAGS_SKIP += -Wcast-qual
+
+LDFLAGS_GCC += \
+  -nostdlib -nostartfiles \
+  --specs=nosys.specs --specs=nano.specs
+
+SRC_C += \
+	src/portable/microchip/samd/dcd_samd.c \
+	${SDK_DIR}/gcc/gcc/startup_${SAM_FAMILY}.c \
+	${SDK_DIR}/gcc/system_${SAM_FAMILY}.c \
+	${SDK_DIR}/hpl/gclk/hpl_gclk.c \
+	${SDK_DIR}/hpl/mclk/hpl_mclk.c \
+	${SDK_DIR}/hpl/osc32kctrl/hpl_osc32kctrl.c \
+	${SDK_DIR}/hpl/oscctrl/hpl_oscctrl.c \
+	${SDK_DIR}/hal/src/hal_atomic.c
+
+INC += \
+	$(TOP)/$(BOARD_PATH) \
+	$(TOP)/${SDK_DIR} \
+	$(TOP)/${SDK_DIR}/config \
+	$(TOP)/${SDK_DIR}/include \
+	$(TOP)/${SDK_DIR}/hal/include \
+	$(TOP)/${SDK_DIR}/hal/utils/include \
+	$(TOP)/${SDK_DIR}/hpl/port \
+	$(TOP)/${SDK_DIR}/hri \
+	$(TOP)/lib/CMSIS_5/CMSIS/Core/Include \
+
+# flash using bossac at least version 1.8
+# can be found in arduino15/packages/arduino/tools/bossac/
+# Add it to your PATH or change BOSSAC variable to match your installation
+BOSSAC = bossac
+
+flash-bossac: $(BUILD)/$(PROJECT).bin
+	@:$(call check_defined, SERIAL, example: SERIAL=/dev/ttyACM0)
+	$(BOSSAC) --port=$(SERIAL) -U -i --offset=0x4000 -e -w $^ -R
+
+# flash using edbg from https://github.com/ataradov/edbg
+flash-edbg: $(BUILD)/$(PROJECT).bin
+	edbg --verbose -t $(MCU) -pv -f $<
diff --git a/hw/bsp/same5x/boards/d5035_01/d5035_01.c b/hw/bsp/same5x/boards/d5035_01/d5035_01.c
deleted file mode 100644
index eb5768d0d..000000000
--- a/hw/bsp/same5x/boards/d5035_01/d5035_01.c
+++ /dev/null
@@ -1,353 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020 Jean Gressmann 
- *
- * 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 "bsp/board_api.h"
-
-#include 
-
-#if CONF_CPU_FREQUENCY != 80000000
-#	error "CONF_CPU_FREQUENCY" must 80000000
-#endif
-
-#if CONF_GCLK_USB_FREQUENCY != 48000000
-#	error "CONF_GCLK_USB_FREQUENCY" must 48000000
-#endif
-
-#if !defined(HWREV)
-#	error Define "HWREV"
-#endif
-
-//--------------------------------------------------------------------+
-// Forward USB interrupt events to TinyUSB IRQ Handler
-//--------------------------------------------------------------------+
-void USB_0_Handler (void)
-{
-	tud_int_handler(0);
-}
-
-void USB_1_Handler (void)
-{
-	tud_int_handler(0);
-}
-
-void USB_2_Handler (void)
-{
-	tud_int_handler(0);
-}
-
-void USB_3_Handler (void)
-{
-	tud_int_handler(0);
-}
-
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
-#define LED_PIN PIN_PA02
-
-#if HWREV < 3
-# define BOARD_SERCOM SERCOM5
-#else
-# define BOARD_SERCOM SERCOM0
-#endif
-
-static inline void init_clock(void)
-{
-	/* AUTOWS is enabled by default in REG_NVMCTRL_CTRLA - no need to change the number of wait states when changing the core clock */
-#if HWREV == 1
-	/* configure XOSC1 for a 16MHz crystal connected to XIN1/XOUT1 */
-	OSCCTRL->XOSCCTRL[1].reg =
-		OSCCTRL_XOSCCTRL_STARTUP(6) |    // 1,953 ms
-		OSCCTRL_XOSCCTRL_RUNSTDBY |
-		OSCCTRL_XOSCCTRL_ENALC |
-		OSCCTRL_XOSCCTRL_IMULT(4) |
-		OSCCTRL_XOSCCTRL_IPTAT(3) |
-		OSCCTRL_XOSCCTRL_XTALEN |
-		OSCCTRL_XOSCCTRL_ENABLE;
-	while(0 == OSCCTRL->STATUS.bit.XOSCRDY1);
-
-	OSCCTRL->Dpll[0].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(3) | OSCCTRL_DPLLCTRLB_REFCLK(OSCCTRL_DPLLCTRLB_REFCLK_XOSC1_Val); /* pre-scaler = 8, input = XOSC1 */
-	OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0x0) | OSCCTRL_DPLLRATIO_LDR(39); /* multiply by 40 -> 80 MHz */
-	OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_RUNSTDBY | OSCCTRL_DPLLCTRLA_ENABLE;
-	while(0 == OSCCTRL->Dpll[0].DPLLSTATUS.bit.CLKRDY); /* wait for the PLL0 to be ready */
-
-	OSCCTRL->Dpll[1].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(7) | OSCCTRL_DPLLCTRLB_REFCLK(OSCCTRL_DPLLCTRLB_REFCLK_XOSC1_Val); /* pre-scaler = 16, input = XOSC1 */
-	OSCCTRL->Dpll[1].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0x0) | OSCCTRL_DPLLRATIO_LDR(47); /* multiply by 48 -> 48 MHz */
-	OSCCTRL->Dpll[1].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_RUNSTDBY | OSCCTRL_DPLLCTRLA_ENABLE;
-	while(0 == OSCCTRL->Dpll[1].DPLLSTATUS.bit.CLKRDY); /* wait for the PLL1 to be ready */
-#else // HWREV >= 1
-	/* configure XOSC0 for a 16MHz crystal connected to XIN0/XOUT0 */
-	OSCCTRL->XOSCCTRL[0].reg =
-		OSCCTRL_XOSCCTRL_STARTUP(6) |    // 1,953 ms
-		OSCCTRL_XOSCCTRL_RUNSTDBY |
-		OSCCTRL_XOSCCTRL_ENALC |
-		OSCCTRL_XOSCCTRL_IMULT(4) |
-		OSCCTRL_XOSCCTRL_IPTAT(3) |
-		OSCCTRL_XOSCCTRL_XTALEN |
-		OSCCTRL_XOSCCTRL_ENABLE;
-	while(0 == OSCCTRL->STATUS.bit.XOSCRDY0);
-
-	OSCCTRL->Dpll[0].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(3) | OSCCTRL_DPLLCTRLB_REFCLK(OSCCTRL_DPLLCTRLB_REFCLK_XOSC0_Val); /* pre-scaler = 8, input = XOSC1 */
-	OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0x0) | OSCCTRL_DPLLRATIO_LDR(39); /* multiply by 40 -> 80 MHz */
-	OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_RUNSTDBY | OSCCTRL_DPLLCTRLA_ENABLE;
-	while(0 == OSCCTRL->Dpll[0].DPLLSTATUS.bit.CLKRDY); /* wait for the PLL0 to be ready */
-
-	OSCCTRL->Dpll[1].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(7) | OSCCTRL_DPLLCTRLB_REFCLK(OSCCTRL_DPLLCTRLB_REFCLK_XOSC0_Val); /* pre-scaler = 16, input = XOSC1 */
-	OSCCTRL->Dpll[1].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0x0) | OSCCTRL_DPLLRATIO_LDR(47); /* multiply by 48 -> 48 MHz */
-	OSCCTRL->Dpll[1].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_RUNSTDBY | OSCCTRL_DPLLCTRLA_ENABLE;
-	while(0 == OSCCTRL->Dpll[1].DPLLSTATUS.bit.CLKRDY); /* wait for the PLL1 to be ready */
-#endif // HWREV
-
-	/* configure clock-generator 0 to use DPLL0 as source -> GCLK0 is used for the core */
-	GCLK->GENCTRL[0].reg =
-		GCLK_GENCTRL_DIV(0) |
-		GCLK_GENCTRL_RUNSTDBY |
-		GCLK_GENCTRL_GENEN |
-		GCLK_GENCTRL_SRC_DPLL0 |  /* DPLL0 */
-		GCLK_GENCTRL_IDC ;
-	while(1 == GCLK->SYNCBUSY.bit.GENCTRL0); /* wait for the synchronization between clock domains to be complete */
-
-	/* configure clock-generator 1 to use DPLL1 as source -> for use with some peripheral */
-	GCLK->GENCTRL[1].reg =
-		GCLK_GENCTRL_DIV(0) |
-		GCLK_GENCTRL_RUNSTDBY |
-		GCLK_GENCTRL_GENEN |
-		GCLK_GENCTRL_SRC_DPLL1 |
-		GCLK_GENCTRL_IDC ;
-	while(1 == GCLK->SYNCBUSY.bit.GENCTRL1); /* wait for the synchronization between clock domains to be complete */
-
-	/* configure clock-generator 2 to use DPLL0 as source -> for use with SERCOM */
-	GCLK->GENCTRL[2].reg =
-		GCLK_GENCTRL_DIV(1) |	/* 80MHz */
-		GCLK_GENCTRL_RUNSTDBY |
-		GCLK_GENCTRL_GENEN |
-		GCLK_GENCTRL_SRC_DPLL0 |
-		GCLK_GENCTRL_IDC ;
-	while(1 == GCLK->SYNCBUSY.bit.GENCTRL2); /* wait for the synchronization between clock domains to be complete */
-}
-
-static inline void uart_init(void)
-{
-#if HWREV < 3
-	/* configure SERCOM5 on PB02 */
-	PORT->Group[1].WRCONFIG.reg =
-		PORT_WRCONFIG_WRPINCFG |
-		PORT_WRCONFIG_WRPMUX |
-		PORT_WRCONFIG_PMUX(3) |    /* function D */
-		PORT_WRCONFIG_DRVSTR |
-		PORT_WRCONFIG_PINMASK(0x0004) | /* PB02 */
-		PORT_WRCONFIG_PMUXEN;
-
-	MCLK->APBDMASK.bit.SERCOM5_ = 1;
-	GCLK->PCHCTRL[SERCOM5_GCLK_ID_CORE].reg = GCLK_PCHCTRL_GEN_GCLK2 | GCLK_PCHCTRL_CHEN; /* setup SERCOM to use GLCK2 -> 80MHz */
-
-	SERCOM5->USART.CTRLA.reg = 0x00; /* disable SERCOM -> enable config */
-	while(SERCOM5->USART.SYNCBUSY.bit.ENABLE);
-
-	SERCOM5->USART.CTRLA.reg  =  /* CMODE = 0 -> async, SAMPA = 0, FORM = 0 -> USART frame, SMPR = 0 -> arithmetic baud rate */
-		SERCOM_USART_CTRLA_SAMPR(1) | /* 0 = 16x / arithmetic baud rate, 1 = 16x / fractional baud rate */
-//    SERCOM_USART_CTRLA_FORM(0) | /* 0 = USART Frame, 2 = LIN Master */
-		SERCOM_USART_CTRLA_DORD | /* LSB first */
-		SERCOM_USART_CTRLA_MODE(1) | /* 0 = Asynchronous, 1 = USART with internal clock */
-		SERCOM_USART_CTRLA_RXPO(1) | /* SERCOM PAD[1] is used for data reception */
-		SERCOM_USART_CTRLA_TXPO(0); /* SERCOM PAD[0] is used for data transmission */
-
-	SERCOM5->USART.CTRLB.reg = /* RXEM = 0 -> receiver disabled, LINCMD = 0 -> normal USART transmission, SFDE = 0 -> start-of-frame detection disabled, SBMODE = 0 -> one stop bit, CHSIZE = 0 -> 8 bits */
-		SERCOM_USART_CTRLB_TXEN; /* transmitter enabled */
-	SERCOM5->USART.CTRLC.reg = 0x00;
-	// 21.701388889 @ baud rate of 230400 bit/s, table 33-2, p 918 of DS60001507E
-	SERCOM5->USART.BAUD.reg = SERCOM_USART_BAUD_FRAC_FP(7) | SERCOM_USART_BAUD_FRAC_BAUD(21);
-
-//  SERCOM5->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC;
-	SERCOM5->SPI.CTRLA.bit.ENABLE = 1; /* activate SERCOM */
-	while(SERCOM5->USART.SYNCBUSY.bit.ENABLE); /* wait for SERCOM to be ready */
-#else
-/* configure SERCOM0 on PA08 */
-	PORT->Group[0].WRCONFIG.reg =
-		PORT_WRCONFIG_WRPINCFG |
-		PORT_WRCONFIG_WRPMUX |
-		PORT_WRCONFIG_PMUX(2) |    /* function C */
-		PORT_WRCONFIG_DRVSTR |
-		PORT_WRCONFIG_PINMASK(0x0100) | /* PA08 */
-		PORT_WRCONFIG_PMUXEN;
-
-	MCLK->APBAMASK.bit.SERCOM0_ = 1;
-	GCLK->PCHCTRL[SERCOM0_GCLK_ID_CORE].reg = GCLK_PCHCTRL_GEN_GCLK2 | GCLK_PCHCTRL_CHEN; /* setup SERCOM to use GLCK2 -> 80MHz */
-
-	SERCOM0->USART.CTRLA.reg = 0x00; /* disable SERCOM -> enable config */
-	while(SERCOM0->USART.SYNCBUSY.bit.ENABLE);
-
-	SERCOM0->USART.CTRLA.reg  =  /* CMODE = 0 -> async, SAMPA = 0, FORM = 0 -> USART frame, SMPR = 0 -> arithmetic baud rate */
-		SERCOM_USART_CTRLA_SAMPR(1) | /* 0 = 16x / arithmetic baud rate, 1 = 16x / fractional baud rate */
-//    SERCOM_USART_CTRLA_FORM(0) | /* 0 = USART Frame, 2 = LIN Master */
-		SERCOM_USART_CTRLA_DORD | /* LSB first */
-		SERCOM_USART_CTRLA_MODE(1) | /* 0 = Asynchronous, 1 = USART with internal clock */
-		SERCOM_USART_CTRLA_RXPO(1) | /* SERCOM PAD[1] is used for data reception */
-		SERCOM_USART_CTRLA_TXPO(0); /* SERCOM PAD[0] is used for data transmission */
-
-	SERCOM0->USART.CTRLB.reg = /* RXEM = 0 -> receiver disabled, LINCMD = 0 -> normal USART transmission, SFDE = 0 -> start-of-frame detection disabled, SBMODE = 0 -> one stop bit, CHSIZE = 0 -> 8 bits */
-		SERCOM_USART_CTRLB_TXEN; /* transmitter enabled */
-	SERCOM0->USART.CTRLC.reg = 0x00;
-	// 21.701388889 @ baud rate of 230400 bit/s, table 33-2, p 918 of DS60001507E
-	SERCOM0->USART.BAUD.reg = SERCOM_USART_BAUD_FRAC_FP(7) | SERCOM_USART_BAUD_FRAC_BAUD(21);
-
-//  SERCOM0->USART.INTENSET.reg = SERCOM_USART_INTENSET_TXC;
-	SERCOM0->SPI.CTRLA.bit.ENABLE = 1; /* activate SERCOM */
-	while(SERCOM0->USART.SYNCBUSY.bit.ENABLE); /* wait for SERCOM to be ready */
-#endif
-}
-
-static inline void uart_send_buffer(uint8_t const *text, size_t len)
-{
-	for (size_t i = 0; i < len; ++i) {
-		BOARD_SERCOM->USART.DATA.reg = text[i];
-		while((BOARD_SERCOM->USART.INTFLAG.reg & SERCOM_SPI_INTFLAG_TXC) == 0);
-	}
-}
-
-static inline void uart_send_str(const char* text)
-{
-	while (*text) {
-		BOARD_SERCOM->USART.DATA.reg = *text++;
-		while((BOARD_SERCOM->USART.INTFLAG.reg & SERCOM_SPI_INTFLAG_TXC) == 0);
-	}
-}
-
-
-void board_init(void)
-{
-	init_clock();
-
-	SystemCoreClock = CONF_CPU_FREQUENCY;
-
-#if CFG_TUSB_OS  == OPT_OS_NONE
-	SysTick_Config(CONF_CPU_FREQUENCY / 1000);
-#endif
-
-	uart_init();
-#if CFG_TUSB_DEBUG >= 2
-	uart_send_str(BOARD_NAME " UART initialized\n");
-	tu_printf(BOARD_NAME " reset cause %#02x\n", RSTC->RCAUSE.reg);
-#endif
-
-	// Led init
-	gpio_set_pin_direction(LED_PIN, GPIO_DIRECTION_OUT);
-	gpio_set_pin_level(LED_PIN, 0);
-
-#if CFG_TUSB_DEBUG >= 2
-	uart_send_str(BOARD_NAME " LED pin configured\n");
-#endif
-
-#if CFG_TUSB_OS == OPT_OS_FREERTOS
-	// If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
-	NVIC_SetPriority(USB_0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
-	NVIC_SetPriority(USB_1_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
-	NVIC_SetPriority(USB_2_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
-	NVIC_SetPriority(USB_3_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
-#endif
-
-
-#if CFG_TUD_ENABLED
-#if CFG_TUSB_DEBUG >= 2
-	uart_send_str(BOARD_NAME " USB device enabled\n");
-#endif
-
-	/* USB clock init
-	 * The USB module requires a GCLK_USB of 48 MHz ~ 0.25% clock
-	 * for low speed and full speed operation. */
-	hri_gclk_write_PCHCTRL_reg(GCLK, USB_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK1_Val | GCLK_PCHCTRL_CHEN);
-	hri_mclk_set_AHBMASK_USB_bit(MCLK);
-	hri_mclk_set_APBBMASK_USB_bit(MCLK);
-
-	// USB pin init
-	gpio_set_pin_direction(PIN_PA24, GPIO_DIRECTION_OUT);
-	gpio_set_pin_level(PIN_PA24, false);
-	gpio_set_pin_pull_mode(PIN_PA24, GPIO_PULL_OFF);
-	gpio_set_pin_direction(PIN_PA25, GPIO_DIRECTION_OUT);
-	gpio_set_pin_level(PIN_PA25, false);
-	gpio_set_pin_pull_mode(PIN_PA25, GPIO_PULL_OFF);
-
-	gpio_set_pin_function(PIN_PA24, PINMUX_PA24H_USB_DM);
-	gpio_set_pin_function(PIN_PA25, PINMUX_PA25H_USB_DP);
-
-
-#if CFG_TUSB_DEBUG >= 2
-	uart_send_str(BOARD_NAME " USB device configured\n");
-#endif
-#endif
-}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
-	gpio_set_pin_level(LED_PIN, state);
-}
-
-uint32_t board_button_read(void)
-{
-	// this board has no button
-	return 0;
-}
-
-int board_uart_read(uint8_t* buf, int len)
-{
-	(void) buf; (void) len;
-	return 0;
-}
-
-int board_uart_write(void const * buf, int len)
-{
-	if (len < 0) {
-		uart_send_str(buf);
-	} else {
-		uart_send_buffer(buf, len);
-	}
-	return len;
-}
-
-#if CFG_TUSB_OS  == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-
-void SysTick_Handler(void)
-{
-	system_ticks++;
-}
-
-uint32_t board_millis(void)
-{
-	return system_ticks;
-}
-#endif
-
-// Required by __libc_init_array in startup code if we are compiling using
-// -nostdlib/-nostartfiles.
-void _init(void)
-{
-
-}
diff --git a/hw/bsp/same5x/boards/same54_xplained/same54_xplained.c b/hw/bsp/same5x/boards/same54_xplained/same54_xplained.c
deleted file mode 100644
index 93adea63e..000000000
--- a/hw/bsp/same5x/boards/same54_xplained/same54_xplained.c
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2021 Jean Gressmann 
- *
- * 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 "bsp/board_api.h"
-
-#include 
-
-
-//--------------------------------------------------------------------+
-// Forward USB interrupt events to TinyUSB IRQ Handler
-//--------------------------------------------------------------------+
-void USB_0_Handler(void)
-{
-	tud_int_handler(0);
-}
-
-void USB_1_Handler(void)
-{
-	tud_int_handler(0);
-}
-
-void USB_2_Handler(void)
-{
-	tud_int_handler(0);
-}
-
-void USB_3_Handler(void)
-{
-	tud_int_handler(0);
-}
-
-//--------------------------------------------------------------------+
-// MACRO TYPEDEF CONSTANT ENUM DECLARATION
-//--------------------------------------------------------------------+
-#define LED_PIN PIN_PC18
-#define BUTTON_PIN PIN_PB31
-#define BOARD_SERCOM SERCOM2
-
-/** Initializes the clocks from the external 12 MHz crystal
- *
- * The goal of this setup is to preserve the second PLL
- * for the application code while still having a reasonable
- * 48 MHz clock for USB / UART.
- *
- * GCLK0:   CONF_CPU_FREQUENCY (default 120 MHz) from PLL0
- * GCLK1:   unused
- * GCLK2:   12 MHz from XOSC1
- * DFLL48M: closed loop from GLCK2
- * GCLK3:   48 MHz
- */
-static inline void init_clock_xtal(void)
-{
-	/* configure for a 12MHz crystal connected to XIN1/XOUT1 */
-	OSCCTRL->XOSCCTRL[1].reg =
-		OSCCTRL_XOSCCTRL_STARTUP(6) | // 1.953 ms
-		OSCCTRL_XOSCCTRL_RUNSTDBY |
-		OSCCTRL_XOSCCTRL_ENALC |
-		OSCCTRL_XOSCCTRL_IMULT(4) | OSCCTRL_XOSCCTRL_IPTAT(3) | // 8MHz to 16MHz
-		OSCCTRL_XOSCCTRL_XTALEN |
-		OSCCTRL_XOSCCTRL_ENABLE;
-	while(0 == OSCCTRL->STATUS.bit.XOSCRDY1);
-
-	OSCCTRL->Dpll[0].DPLLCTRLB.reg = OSCCTRL_DPLLCTRLB_DIV(2) | OSCCTRL_DPLLCTRLB_REFCLK_XOSC1; /* 12MHz / 6 = 2Mhz, input = XOSC1 */
-	OSCCTRL->Dpll[0].DPLLRATIO.reg = OSCCTRL_DPLLRATIO_LDRFRAC(0x0) | OSCCTRL_DPLLRATIO_LDR((CONF_CPU_FREQUENCY / 1000000 / 2) - 1); /* multiply to get CONF_CPU_FREQUENCY (default = 120MHz) */
-	OSCCTRL->Dpll[0].DPLLCTRLA.reg = OSCCTRL_DPLLCTRLA_RUNSTDBY | OSCCTRL_DPLLCTRLA_ENABLE;
-	while(0 == OSCCTRL->Dpll[0].DPLLSTATUS.bit.CLKRDY); /* wait for the PLL0 to be ready */
-
-	/* configure clock-generator 0 to use DPLL0 as source -> GCLK0 is used for the core */
-	GCLK->GENCTRL[0].reg =
-		GCLK_GENCTRL_DIV(0) |
-		GCLK_GENCTRL_RUNSTDBY |
-		GCLK_GENCTRL_GENEN |
-		GCLK_GENCTRL_SRC_DPLL0 |
-		GCLK_GENCTRL_IDC;
-	while(1 == GCLK->SYNCBUSY.bit.GENCTRL0); /* wait for the synchronization between clock domains to be complete */
-
-	// configure GCLK2 for 12MHz from XOSC1
-	GCLK->GENCTRL[2].reg =
-		GCLK_GENCTRL_DIV(0) |
-		GCLK_GENCTRL_RUNSTDBY |
-		GCLK_GENCTRL_GENEN |
-		GCLK_GENCTRL_SRC_XOSC1 |
-		GCLK_GENCTRL_IDC;
-	while(1 == GCLK->SYNCBUSY.bit.GENCTRL2); /* wait for the synchronization between clock domains to be complete */
-
-	 /* setup DFLL48M to use GLCK2 */
-	GCLK->PCHCTRL[OSCCTRL_GCLK_ID_DFLL48].reg = GCLK_PCHCTRL_GEN_GCLK2 | GCLK_PCHCTRL_CHEN;
-
-	OSCCTRL->DFLLCTRLA.reg = 0;
-	while(1 == OSCCTRL->DFLLSYNC.bit.ENABLE);
-
-	OSCCTRL->DFLLCTRLB.reg = OSCCTRL_DFLLCTRLB_MODE | OSCCTRL_DFLLCTRLB_WAITLOCK;
-	OSCCTRL->DFLLMUL.bit.MUL = 4; // 4 * 12MHz -> 48MHz
-
-	OSCCTRL->DFLLCTRLA.reg =
-		OSCCTRL_DFLLCTRLA_ENABLE |
-		OSCCTRL_DFLLCTRLA_RUNSTDBY;
-	while(1 == OSCCTRL->DFLLSYNC.bit.ENABLE);
-
-	// setup 48 MHz GCLK3 from DFLL48M
-	GCLK->GENCTRL[3].reg =
-		GCLK_GENCTRL_DIV(0) |
-		GCLK_GENCTRL_RUNSTDBY |
-		GCLK_GENCTRL_GENEN |
-		GCLK_GENCTRL_SRC_DFLL |
-		GCLK_GENCTRL_IDC;
-	while(1 == GCLK->SYNCBUSY.bit.GENCTRL3);
-}
-
-/* Initialize SERCOM2 for 115200 bps 8N1 using a 48 MHz clock */
-static inline void uart_init(void)
-{
-	gpio_set_pin_function(PIN_PB24, PINMUX_PB24D_SERCOM2_PAD1);
-	gpio_set_pin_function(PIN_PB25, PINMUX_PB25D_SERCOM2_PAD0);
-
-	MCLK->APBBMASK.bit.SERCOM2_ = 1;
-	GCLK->PCHCTRL[SERCOM2_GCLK_ID_CORE].reg = GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN;
-
-	BOARD_SERCOM->USART.CTRLA.bit.SWRST = 1; /* reset and disable SERCOM -> enable configuration */
-	while (BOARD_SERCOM->USART.SYNCBUSY.bit.SWRST);
-
-	BOARD_SERCOM->USART.CTRLA.reg  =
-		SERCOM_USART_CTRLA_SAMPR(0) | /* 0 = 16x / arithmetic baud rate, 1 = 16x / fractional baud rate */
-		SERCOM_USART_CTRLA_SAMPA(0) | /* 16x over sampling */
-		SERCOM_USART_CTRLA_FORM(0) | /* 0x0 USART frame, 0x1 USART frame with parity, ... */
-		SERCOM_USART_CTRLA_DORD | /* LSB first */
-		SERCOM_USART_CTRLA_MODE(1) | /* 0x0 USART with external clock, 0x1 USART with internal clock */
-		SERCOM_USART_CTRLA_RXPO(1) | /* SERCOM PAD[1] is used for data reception */
-		SERCOM_USART_CTRLA_TXPO(0); /* SERCOM PAD[0] is used for data transmission */
-
-	BOARD_SERCOM->USART.CTRLB.reg = /* RXEM = 0 -> receiver disabled, LINCMD = 0 -> normal USART transmission, SFDE = 0 -> start-of-frame detection disabled, SBMODE = 0 -> one stop bit, CHSIZE = 0 -> 8 bits */
-		SERCOM_USART_CTRLB_TXEN | /* transmitter enabled */
-		SERCOM_USART_CTRLB_RXEN; /* receiver enabled */
-	// BOARD_SERCOM->USART.BAUD.reg = SERCOM_USART_BAUD_FRAC_FP(0) | SERCOM_USART_BAUD_FRAC_BAUD(26); /* 48000000/(16*115200) = 26.041666667 */
-	BOARD_SERCOM->USART.BAUD.reg = SERCOM_USART_BAUD_BAUD(63019); /* 65536*(1−16*115200/48000000) */
-
-	BOARD_SERCOM->USART.CTRLA.bit.ENABLE = 1; /* activate SERCOM */
-	while (BOARD_SERCOM->USART.SYNCBUSY.bit.ENABLE); /* wait for SERCOM to be ready */
-}
-
-static inline void uart_send_buffer(uint8_t const *text, size_t len)
-{
-	for (size_t i = 0; i < len; ++i) {
-		BOARD_SERCOM->USART.DATA.reg = text[i];
-		while((BOARD_SERCOM->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) == 0);
-	}
-}
-
-static inline void uart_send_str(const char* text)
-{
-	while (*text) {
-		BOARD_SERCOM->USART.DATA.reg = *text++;
-		while((BOARD_SERCOM->USART.INTFLAG.reg & SERCOM_USART_INTFLAG_TXC) == 0);
-	}
-}
-
-
-void board_init(void)
-{
-	// Uncomment this line and change the GCLK for UART/USB to run off the XTAL.
-	// init_clock_xtal();
-
-	SystemCoreClock = CONF_CPU_FREQUENCY;
-
-#if CFG_TUSB_OS  == OPT_OS_NONE
-	SysTick_Config(CONF_CPU_FREQUENCY / 1000);
-#endif
-
-	uart_init();
-
-#if CFG_TUSB_DEBUG >= 2
-	uart_send_str(BOARD_NAME " UART initialized\n");
-	tu_printf(BOARD_NAME " reset cause %#02x\n", RSTC->RCAUSE.reg);
-#endif
-
-	// LED0 init
-	gpio_set_pin_function(LED_PIN, GPIO_PIN_FUNCTION_OFF);
-	gpio_set_pin_direction(LED_PIN, GPIO_DIRECTION_OUT);
-	board_led_write(0);
-
-#if CFG_TUSB_DEBUG >= 2
-	uart_send_str(BOARD_NAME " LED pin configured\n");
-#endif
-
-	// BTN0 init
-	gpio_set_pin_function(BUTTON_PIN, GPIO_PIN_FUNCTION_OFF);
-	gpio_set_pin_direction(BUTTON_PIN, GPIO_DIRECTION_IN);
-	gpio_set_pin_pull_mode(BUTTON_PIN, GPIO_PULL_UP);
-
-#if CFG_TUSB_DEBUG >= 2
-	uart_send_str(BOARD_NAME " Button pin configured\n");
-#endif
-
-#if CFG_TUSB_OS == OPT_OS_FREERTOS
-	// If freeRTOS is used, IRQ priority is limit by max syscall ( smaller is higher )
-	NVIC_SetPriority(USB_0_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
-	NVIC_SetPriority(USB_1_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
-	NVIC_SetPriority(USB_2_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
-	NVIC_SetPriority(USB_3_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
-#endif
-
-
-#if CFG_TUD_ENABLED
-#if CFG_TUSB_DEBUG >= 2
-	uart_send_str(BOARD_NAME " USB device enabled\n");
-#endif
-
-	/* USB clock init
-	 * The USB module requires a GCLK_USB of 48 MHz ~ 0.25% clock
-	 * for low speed and full speed operation.
-	 */
-	hri_gclk_write_PCHCTRL_reg(GCLK, USB_GCLK_ID, GCLK_PCHCTRL_GEN_GCLK0_Val | GCLK_PCHCTRL_CHEN);
-	hri_mclk_set_AHBMASK_USB_bit(MCLK);
-	hri_mclk_set_APBBMASK_USB_bit(MCLK);
-
-	// USB pin init
-	gpio_set_pin_direction(PIN_PA24, GPIO_DIRECTION_OUT);
-	gpio_set_pin_level(PIN_PA24, false);
-	gpio_set_pin_pull_mode(PIN_PA24, GPIO_PULL_OFF);
-	gpio_set_pin_direction(PIN_PA25, GPIO_DIRECTION_OUT);
-	gpio_set_pin_level(PIN_PA25, false);
-	gpio_set_pin_pull_mode(PIN_PA25, GPIO_PULL_OFF);
-
-	gpio_set_pin_function(PIN_PA24, PINMUX_PA24H_USB_DM);
-	gpio_set_pin_function(PIN_PA25, PINMUX_PA25H_USB_DP);
-
-
-#if CFG_TUSB_DEBUG >= 2
-	uart_send_str(BOARD_NAME " USB device configured\n");
-#endif
-#endif
-}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
-	gpio_set_pin_level(LED_PIN, !state);
-}
-
-uint32_t board_button_read(void)
-{
-	return (PORT->Group[1].IN.reg & 0x80000000) != 0x80000000;
-}
-
-int board_uart_read(uint8_t* buf, int len)
-{
-  (void) buf; (void) len;
-  return 0;
-}
-
-int board_uart_write(void const * buf, int len)
-{
-	if (len < 0) {
-		uart_send_str(buf);
-	} else {
-		uart_send_buffer(buf, len);
-	}
-	return len;
-}
-
-#if CFG_TUSB_OS  == OPT_OS_NONE
-volatile uint32_t system_ticks = 0;
-
-void SysTick_Handler(void)
-{
-	system_ticks++;
-}
-
-uint32_t board_millis(void)
-{
-	return system_ticks;
-}
-#endif
-
-// Required by __libc_init_array in startup code if we are compiling using
-// -nostdlib/-nostartfiles.
-void _init(void)
-{
-
-}
diff --git a/hw/bsp/same5x/family.mk b/hw/bsp/same5x/family.mk
deleted file mode 100644
index b2bf0d359..000000000
--- a/hw/bsp/same5x/family.mk
+++ /dev/null
@@ -1,38 +0,0 @@
-DEPS_SUBMODULES += hw/mcu/microchip
-
-SDK_DIR = hw/mcu/microchip/$(MCU)
-include $(TOP)/$(BOARD_PATH)/board.mk
-CPU_CORE ?= cortex-m4
-
-CFLAGS += \
-  -mthumb \
-  -mlong-calls \
-  -nostdlib -nostartfiles \
-  -DCFG_TUSB_MCU=OPT_MCU_SAME5X
-
-# SAM driver is flooded with -Wcast-qual which slow down complication significantly
-CFLAGS_SKIP += -Wcast-qual
-
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
-
-SRC_C += \
-  src/portable/microchip/samd/dcd_samd.c \
-  $(SDK_DIR)/gcc/gcc/startup_$(MCU).c \
-  $(SDK_DIR)/gcc/system_$(MCU).c \
-  $(SDK_DIR)/hal/utils/src/utils_syscalls.c
-
-INC += \
-	$(TOP)/$(SDK_DIR) \
-	$(TOP)/$(SDK_DIR)/config \
-	$(TOP)/$(SDK_DIR)/include \
-	$(TOP)/$(SDK_DIR)/hal/include \
-	$(TOP)/$(SDK_DIR)/hal/utils/include \
-	$(TOP)/$(SDK_DIR)/hpl/port \
-	$(TOP)/$(SDK_DIR)/hri \
-	$(TOP)/$(SDK_DIR)/CMSIS/Include
-
-# flash using edbg from https://github.com/ataradov/edbg
-flash-edbg: $(BUILD)/$(PROJECT).bin
-	edbg --verbose -t $(MCU) -pv -f $<
-
-flash: flash-edbg
diff --git a/hw/bsp/samg/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/samg/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..02223f766
--- /dev/null
+++ b/hw/bsp/samg/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,149 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  extern uint32_t SystemCoreClock;
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#define configENABLE_FPU                        1
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*6*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       4
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1< USB Clock Source
 // <0=> USB Clock Controller (USB_48M)
diff --git a/hw/bsp/samg55xplained/board.mk b/hw/bsp/samg55xplained/board.mk
deleted file mode 100644
index a9328be11..000000000
--- a/hw/bsp/samg55xplained/board.mk
+++ /dev/null
@@ -1,56 +0,0 @@
-DEPS_SUBMODULES += hw/mcu/microchip
-ASF_DIR = hw/mcu/microchip/samg55
-
-CFLAGS += \
-  -flto \
-  -mthumb \
-  -mabi=aapcs \
-  -mcpu=cortex-m4 \
-  -mfloat-abi=hard \
-  -mfpu=fpv4-sp-d16 \
-  -nostdlib -nostartfiles \
-  -D__SAMG55J19__ \
-  -DCFG_TUSB_MCU=OPT_MCU_SAMG
-
-# suppress following warnings from mcu driver
-CFLAGS += -Wno-error=undef -Wno-error=null-dereference -Wno-error=redundant-decls
-
-# SAM driver is flooded with -Wcast-qual which slow down complication significantly
-CFLAGS_SKIP += -Wcast-qual
-
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
-
-# All source paths should be relative to the top level.
-LD_FILE = hw/bsp/$(BOARD)/samg55j19_flash.ld
-
-SRC_C += \
-	src/portable/microchip/samg/dcd_samg.c \
-	$(ASF_DIR)/samg55/gcc/gcc/startup_samg55.c \
-	$(ASF_DIR)/samg55/gcc/system_samg55.c \
-	$(ASF_DIR)/hpl/core/hpl_init.c \
-	$(ASF_DIR)/hpl/usart/hpl_usart.c \
-	$(ASF_DIR)/hpl/pmc/hpl_pmc.c \
-	$(ASF_DIR)/hal/src/hal_atomic.c
-
-INC += \
-  $(TOP)/hw/bsp/$(BOARD) \
-	$(TOP)/$(ASF_DIR) \
-	$(TOP)/$(ASF_DIR)/config \
-	$(TOP)/$(ASF_DIR)/samg55/include \
-	$(TOP)/$(ASF_DIR)/hal/include \
-	$(TOP)/$(ASF_DIR)/hal/utils/include \
-	$(TOP)/$(ASF_DIR)/hpl/core \
-	$(TOP)/$(ASF_DIR)/hpl/pio \
-	$(TOP)/$(ASF_DIR)/hpl/pmc \
-	$(TOP)/$(ASF_DIR)/hri \
-	$(TOP)/$(ASF_DIR)/CMSIS/Core/Include
-
-# For freeRTOS port source
-FREERTOS_PORTABLE_SRC = $(FREERTOS_PORTABLE_PATH)/ARM_CM4F
-
-# For flash-jlink target
-JLINK_DEVICE = ATSAMG55J19
-
-# flash using edbg from https://github.com/ataradov/edbg
-flash: $(BUILD)/$(PROJECT).bin
-	edbg --verbose -t samg55 -pv -f $<
diff --git a/hw/bsp/saml2x/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/saml2x/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..6c9ecae2d
--- /dev/null
+++ b/hw/bsp/saml2x/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,153 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "sam.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#if defined(__ARM_FP) && __ARM_FP >= 4
+  #define configENABLE_FPU                      1
+#else
+  #define configENABLE_FPU                      0
+#endif
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       2
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    . = ALIGN(4);
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM.extab : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+    . = ALIGN(4);
+  } >FLASH
+
+  .preinit_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .init_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .fini_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32h5/linker/STM32H523xx_FLASH.ld b/hw/bsp/stm32h5/linker/STM32H523xx_FLASH.ld
new file mode 100644
index 000000000..b799892c6
--- /dev/null
+++ b/hw/bsp/stm32h5/linker/STM32H523xx_FLASH.ld
@@ -0,0 +1,187 @@
+/*
+ ******************************************************************************
+ **
+ ** @file        : LinkerScript.ld
+ **
+ ** @author      : Auto-generated by STM32CubeIDE
+ **
+ ** @brief       : Linker script for STM32H523xx Device from STM32H5 series
+ **                      512Kbytes FLASH
+ **                      272Kbytes RAM
+ **
+ **                Set heap size, stack size and stack location according
+ **                to application requirements.
+ **
+ **                Set memory bank area and size if external memory is used
+ **
+ **  Target      : STMicroelectronics STM32
+ **
+ **  Distribution: The file is distributed as is, without any warranty
+ **                of any kind.
+ **
+ ******************************************************************************
+ ** @attention
+ **
+ ** Copyright (c) 2023 STMicroelectronics.
+ ** All rights reserved.
+ **
+ ** This software is licensed under terms that can be found in the LICENSE file
+ ** in the root directory of this software component.
+ ** If no LICENSE file comes with this software, it is provided AS-IS.
+ **
+ ******************************************************************************
+ */
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 272K
+  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 512K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200; /* required amount of heap */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    . = ALIGN(4);
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM.extab : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+    . = ALIGN(4);
+  } >FLASH
+
+  .preinit_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .init_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .fini_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32h5/linker/STM32H533xx_FLASH.ld b/hw/bsp/stm32h5/linker/STM32H533xx_FLASH.ld
new file mode 100644
index 000000000..dece7a003
--- /dev/null
+++ b/hw/bsp/stm32h5/linker/STM32H533xx_FLASH.ld
@@ -0,0 +1,187 @@
+/*
+ ******************************************************************************
+ **
+ ** @file        : LinkerScript.ld
+ **
+ ** @author      : Auto-generated by STM32CubeIDE
+ **
+ ** @brief       : Linker script for STM32H533xx Device from STM32H5 series
+ **                      512Kbytes FLASH
+ **                      272Kbytes RAM
+ **
+ **                Set heap size, stack size and stack location according
+ **                to application requirements.
+ **
+ **                Set memory bank area and size if external memory is used
+ **
+ **  Target      : STMicroelectronics STM32
+ **
+ **  Distribution: The file is distributed as is, without any warranty
+ **                of any kind.
+ **
+ ******************************************************************************
+ ** @attention
+ **
+ ** Copyright (c) 2023 STMicroelectronics.
+ ** All rights reserved.
+ **
+ ** This software is licensed under terms that can be found in the LICENSE file
+ ** in the root directory of this software component.
+ ** If no LICENSE file comes with this software, it is provided AS-IS.
+ **
+ ******************************************************************************
+ */
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 272K
+  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 512K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200; /* required amount of heap */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    . = ALIGN(4);
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM.extab : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+    . = ALIGN(4);
+  } >FLASH
+
+  .preinit_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .init_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .fini_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32h5/linker/STM32H562xx_FLASH.ld b/hw/bsp/stm32h5/linker/STM32H562xx_FLASH.ld
new file mode 100644
index 000000000..aee2774a4
--- /dev/null
+++ b/hw/bsp/stm32h5/linker/STM32H562xx_FLASH.ld
@@ -0,0 +1,187 @@
+/*
+ ******************************************************************************
+ **
+ ** @file        : LinkerScript.ld
+ **
+ ** @author      : Auto-generated by STM32CubeIDE
+ **
+ ** @brief       : Linker script for STM32H562xx Device from STM32H5 series
+ **                      2048Kbytes FLASH
+ **                      640Kbytes RAM
+ **
+ **                Set heap size, stack size and stack location according
+ **                to application requirements.
+ **
+ **                Set memory bank area and size if external memory is used
+ **
+ **  Target      : STMicroelectronics STM32
+ **
+ **  Distribution: The file is distributed as is, without any warranty
+ **                of any kind.
+ **
+ ******************************************************************************
+ ** @attention
+ **
+ ** Copyright (c) 2023 STMicroelectronics.
+ ** All rights reserved.
+ **
+ ** This software is licensed under terms that can be found in the LICENSE file
+ ** in the root directory of this software component.
+ ** If no LICENSE file comes with this software, it is provided AS-IS.
+ **
+ ******************************************************************************
+ */
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 640K
+  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 2048K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200; /* required amount of heap */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    . = ALIGN(4);
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM.extab : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+    . = ALIGN(4);
+  } >FLASH
+
+  .preinit_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .init_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .fini_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32h5/linker/STM32H563xx_FLASH.ld b/hw/bsp/stm32h5/linker/STM32H563xx_FLASH.ld
new file mode 100644
index 000000000..129ed5170
--- /dev/null
+++ b/hw/bsp/stm32h5/linker/STM32H563xx_FLASH.ld
@@ -0,0 +1,187 @@
+/*
+ ******************************************************************************
+ **
+ ** @file        : LinkerScript.ld
+ **
+ ** @author      : Auto-generated by STM32CubeIDE
+ **
+ ** @brief       : Linker script for STM32H563xx Device from STM32H5 series
+ **                      2048Kbytes FLASH
+ **                      640Kbytes RAM
+ **
+ **                Set heap size, stack size and stack location according
+ **                to application requirements.
+ **
+ **                Set memory bank area and size if external memory is used
+ **
+ **  Target      : STMicroelectronics STM32
+ **
+ **  Distribution: The file is distributed as is, without any warranty
+ **                of any kind.
+ **
+ ******************************************************************************
+ ** @attention
+ **
+ ** Copyright (c) 2023 STMicroelectronics.
+ ** All rights reserved.
+ **
+ ** This software is licensed under terms that can be found in the LICENSE file
+ ** in the root directory of this software component.
+ ** If no LICENSE file comes with this software, it is provided AS-IS.
+ **
+ ******************************************************************************
+ */
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 640K
+  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 2048K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200; /* required amount of heap */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    . = ALIGN(4);
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM.extab : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+    . = ALIGN(4);
+  } >FLASH
+
+  .preinit_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .init_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .fini_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32h5/linker/STM32H573xx_FLASH.ld b/hw/bsp/stm32h5/linker/STM32H573xx_FLASH.ld
new file mode 100644
index 000000000..eb98f3163
--- /dev/null
+++ b/hw/bsp/stm32h5/linker/STM32H573xx_FLASH.ld
@@ -0,0 +1,187 @@
+/*
+ ******************************************************************************
+ **
+ ** @file        : LinkerScript.ld
+ **
+ ** @author      : Auto-generated by STM32CubeIDE
+ **
+ ** @brief       : Linker script for STM32H573xx Device from STM32H5 series
+ **                      2048Kbytes FLASH
+ **                      640Kbytes RAM
+ **
+ **                Set heap size, stack size and stack location according
+ **                to application requirements.
+ **
+ **                Set memory bank area and size if external memory is used
+ **
+ **  Target      : STMicroelectronics STM32
+ **
+ **  Distribution: The file is distributed as is, without any warranty
+ **                of any kind.
+ **
+ ******************************************************************************
+ ** @attention
+ **
+ ** Copyright (c) 2023 STMicroelectronics.
+ ** All rights reserved.
+ **
+ ** This software is licensed under terms that can be found in the LICENSE file
+ ** in the root directory of this software component.
+ ** If no LICENSE file comes with this software, it is provided AS-IS.
+ **
+ ******************************************************************************
+ */
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM    (xrw)    : ORIGIN = 0x20000000,   LENGTH = 640K
+  FLASH    (rx)    : ORIGIN = 0x8000000,   LENGTH = 2048K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200; /* required amount of heap */
+_Min_Stack_Size = 0x400; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    . = ALIGN(4);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(4);
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    . = ALIGN(4);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(4);
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    . = ALIGN(4);
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM.extab : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+    . = ALIGN(4);
+  } >FLASH
+
+  .ARM : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+    . = ALIGN(4);
+  } >FLASH
+
+  .preinit_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .init_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  .fini_array : /* The READONLY keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
+  {
+    . = ALIGN(4);
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+    . = ALIGN(4);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    . = ALIGN(4);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    . = ALIGN(4);
+    _edata = .;        /* define a global symbol at data end */
+
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  . = ALIGN(4);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(4);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32h7/boards/daisyseed/stm32h750ibkx_flash.ld b/hw/bsp/stm32h7/boards/daisyseed/stm32h750ibkx_flash.ld
index 3588ada5b..e2bde9338 100644
--- a/hw/bsp/stm32h7/boards/daisyseed/stm32h750ibkx_flash.ld
+++ b/hw/bsp/stm32h7/boards/daisyseed/stm32h750ibkx_flash.ld
@@ -34,12 +34,6 @@
 /* Entry Point */
 ENTRY(Reset_Handler)
 
-/* Highest address of the user mode stack */
-_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1);    /* end of RAM */
-/* Generate a link error if heap and stack don't fit into RAM */
-_Min_Heap_Size = 0x2000 ;      /* required amount of heap  */
-_Min_Stack_Size = 0x4000 ; /* required amount of stack */
-
 /* Specify the memory areas */
 MEMORY
 {
@@ -51,6 +45,12 @@ MEMORY
   ITCMRAM (xrw)  : ORIGIN = 0x00000000, LENGTH = 64K
 }
 
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1);    /* end of RAM */
+/* Generate a link error if heap and stack don't fit into RAM */
+_Min_Heap_Size = 0x2000 ;      /* required amount of heap  */
+_Min_Stack_Size = 0x4000 ; /* required amount of stack */
+
 /* Define output sections */
 SECTIONS
 {
diff --git a/hw/bsp/stm32h7/boards/daisyseed/stm32h750ibkx_ram.ld b/hw/bsp/stm32h7/boards/daisyseed/stm32h750ibkx_ram.ld
index 16f48b10a..03d9aaba3 100644
--- a/hw/bsp/stm32h7/boards/daisyseed/stm32h750ibkx_ram.ld
+++ b/hw/bsp/stm32h7/boards/daisyseed/stm32h750ibkx_ram.ld
@@ -34,12 +34,6 @@
 /* Entry Point */
 ENTRY(Reset_Handler)
 
-/* Highest address of the user mode stack */
-_estack = ORIGIN(DTCMRAM) + LENGTH(DTCMRAM);    /* end of RAM */
-/* Generate a link error if heap and stack don't fit into RAM */
-_Min_Heap_Size = 0x2000 ;      /* required amount of heap  */
-_Min_Stack_Size = 0x4000 ; /* required amount of stack */
-
 /* Specify the memory areas */
 MEMORY
 {
@@ -50,6 +44,12 @@ MEMORY
   ITCMRAM (xrw)   : ORIGIN = 0x00000000, LENGTH = 64K
 }
 
+/* Highest address of the user mode stack */
+_estack = ORIGIN(DTCMRAM) + LENGTH(DTCMRAM);    /* end of RAM */
+/* Generate a link error if heap and stack don't fit into RAM */
+_Min_Heap_Size = 0x2000 ;      /* required amount of heap  */
+_Min_Stack_Size = 0x4000 ; /* required amount of stack */
+
 /* Define output sections */
 SECTIONS
 {
diff --git a/hw/bsp/stm32h7/family.cmake b/hw/bsp/stm32h7/family.cmake
index 6174dfda3..026c7c3df 100644
--- a/hw/bsp/stm32h7/family.cmake
+++ b/hw/bsp/stm32h7/family.cmake
@@ -1,9 +1,5 @@
 include_guard()
 
-if (NOT BOARD)
-  message(FATAL_ERROR "BOARD not specified")
-endif ()
-
 set(ST_FAMILY h7)
 set(ST_PREFIX stm32${ST_FAMILY}xx)
 
@@ -26,53 +22,59 @@ set(FAMILY_MCUS STM32H7 CACHE INTERNAL "")
 #------------------------------------
 # only need to be built ONCE for all examples
 function(add_board_target BOARD_TARGET)
-  if (NOT TARGET ${BOARD_TARGET})
-    # Startup & Linker script
-    set(STARTUP_FILE_GNU ${ST_CMSIS}/Source/Templates/gcc/startup_${MCU_VARIANT}.s)
-    set(STARTUP_FILE_IAR ${ST_CMSIS}/Source/Templates/iar/startup_${MCU_VARIANT}.s)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
 
-    if(NOT DEFINED LD_FILE_IAR)
-      set(LD_FILE_IAR ${ST_CMSIS}/Source/Templates/iar/linker/${MCU_VARIANT}_flash.icf)
-    endif()
+  # Startup & Linker script
+  set(STARTUP_FILE_GNU ${ST_CMSIS}/Source/Templates/gcc/startup_${MCU_VARIANT}.s)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+  set(STARTUP_FILE_IAR ${ST_CMSIS}/Source/Templates/iar/startup_${MCU_VARIANT}.s)
 
-    add_library(${BOARD_TARGET} STATIC
-      ${ST_CMSIS}/Source/Templates/system_${ST_PREFIX}.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_cortex.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_dma.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_gpio.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_pwr.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_pwr_ex.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_rcc.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_rcc_ex.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_uart.c
-      ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_uart_ex.c
-      ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
-      )
-    target_include_directories(${BOARD_TARGET} PUBLIC
-      ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
-      ${CMSIS_5}/CMSIS/Core/Include
-      ${ST_CMSIS}/Include
-      ${ST_HAL_DRIVER}/Inc
-      )
-    target_compile_options(${BOARD_TARGET} PUBLIC
-      )
-    target_compile_definitions(${BOARD_TARGET} PUBLIC
-      )
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+  if(NOT DEFINED LD_FILE_IAR)
+    set(LD_FILE_IAR ${ST_CMSIS}/Source/Templates/iar/linker/${MCU_VARIANT}_flash.icf)
+  endif()
 
-    update_board(${BOARD_TARGET})
+  add_library(${BOARD_TARGET} STATIC
+    ${ST_CMSIS}/Source/Templates/system_${ST_PREFIX}.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_cortex.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_dma.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_gpio.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_pwr.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_pwr_ex.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_rcc.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_rcc_ex.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_uart.c
+    ${ST_HAL_DRIVER}/Src/${ST_PREFIX}_hal_uart_ex.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMSIS_5}/CMSIS/Core/Include
+    ${ST_CMSIS}/Include
+    ${ST_HAL_DRIVER}/Inc
+    )
+  #target_compile_options(${BOARD_TARGET} PUBLIC)
+  #target_compile_definitions(${BOARD_TARGET} PUBLIC)
 
-    if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--script=${LD_FILE_GNU}"
-        -nostartfiles
-        --specs=nosys.specs --specs=nano.specs
-        )
-    elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
-      target_link_options(${BOARD_TARGET} PUBLIC
-        "LINKER:--config=${LD_FILE_IAR}"
-        )
-    endif ()
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      -nostartfiles
+      --specs=nosys.specs --specs=nano.specs
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_Clang}"
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
   endif ()
 endfunction()
 
diff --git a/hw/bsp/stm32h7/family.mk b/hw/bsp/stm32h7/family.mk
index 0777bb9c2..40df190db 100644
--- a/hw/bsp/stm32h7/family.mk
+++ b/hw/bsp/stm32h7/family.mk
@@ -30,12 +30,15 @@ endif
 # GCC Flags
 CFLAGS_GCC += \
   -flto \
-  -nostdlib -nostartfiles
 
 # suppress warning caused by vendor mcu driver
-CFLAGS_GCC += -Wno-error=maybe-uninitialized -Wno-error=cast-align -Wno-error=unused-parameter
+CFLAGS_GCC += \
+  -Wno-error=cast-align \
+  -Wno-error=unused-parameter \
 
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
+LDFLAGS_GCC += \
+  -nostdlib -nostartfiles \
+  --specs=nosys.specs --specs=nano.specs
 
 # -----------------
 # Sources & Include
diff --git a/hw/bsp/stm32h7/linker/stm32h723xx_flash.ld b/hw/bsp/stm32h7/linker/stm32h723xx_flash.ld
index 05e0d4e26..b779c0d35 100644
--- a/hw/bsp/stm32h7/linker/stm32h723xx_flash.ld
+++ b/hw/bsp/stm32h7/linker/stm32h723xx_flash.ld
@@ -34,12 +34,6 @@
 /* Entry Point */
 ENTRY(Reset_Handler)
 
-/* Highest address of the user mode stack */
-_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1);    /* end of RAM */
-/* Generate a link error if heap and stack don't fit into RAM */
-_Min_Heap_Size = 0x200 ;      /* required amount of heap  */
-_Min_Stack_Size = 0x400 ; /* required amount of stack */
-
 /* Specify the memory areas */
 MEMORY
 {
@@ -51,6 +45,12 @@ MEMORY
   RAM_D3  (xrw)    : ORIGIN = 0x38000000,   LENGTH = 16K
 }
 
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM_D1) + LENGTH(RAM_D1);    /* end of RAM */
+/* Generate a link error if heap and stack don't fit into RAM */
+_Min_Heap_Size = 0x200 ;      /* required amount of heap  */
+_Min_Stack_Size = 0x400 ; /* required amount of stack */
+
 /* Define output sections */
 SECTIONS
 {
diff --git a/hw/bsp/stm32l0/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/stm32l0/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..40146e73a
--- /dev/null
+++ b/hw/bsp/stm32l0/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,149 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "stm32l0xx.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#define configENABLE_FPU                        0
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       2
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<FLASH
 
-  /* The program code and other data goes into FLASH */
+  /* The program code and other data into "FLASH" Rom type memory */
   .text :
   {
-    . = ALIGN(4);
     *(.text)           /* .text sections (code) */
     *(.text*)          /* .text* sections (code) */
     *(.glue_7)         /* glue arm to thumb code */
@@ -65,32 +70,35 @@ SECTIONS
     KEEP (*(.init))
     KEEP (*(.fini))
 
-    . = ALIGN(4);
     _etext = .;        /* define a global symbols at end of code */
   } >FLASH
 
-  /* Constant data goes into FLASH */
+  /* Constant data into "FLASH" Rom type memory */
   .rodata :
   {
-    . = ALIGN(4);
     *(.rodata)         /* .rodata sections (constants, strings, etc.) */
     *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
-    . = ALIGN(4);
   } >FLASH
 
-  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
-  .ARM : {
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } >FLASH
+
+  .ARM :
+  {
     __exidx_start = .;
     *(.ARM.exidx*)
     __exidx_end = .;
   } >FLASH
 
-  .preinit_array     :
+  .preinit_array :
   {
     PROVIDE_HIDDEN (__preinit_array_start = .);
     KEEP (*(.preinit_array*))
     PROVIDE_HIDDEN (__preinit_array_end = .);
   } >FLASH
+
   .init_array :
   {
     PROVIDE_HIDDEN (__init_array_start = .);
@@ -98,6 +106,7 @@ SECTIONS
     KEEP (*(.init_array*))
     PROVIDE_HIDDEN (__init_array_end = .);
   } >FLASH
+
   .fini_array :
   {
     PROVIDE_HIDDEN (__fini_array_start = .);
@@ -106,24 +115,22 @@ SECTIONS
     PROVIDE_HIDDEN (__fini_array_end = .);
   } >FLASH
 
-  /* used by the startup to initialize data */
+  /* Used by the startup to initialize data */
   _sidata = LOADADDR(.data);
 
-  /* Initialized data sections goes into RAM, load LMA copy after code */
+  /* Initialized data sections into "RAM" Ram type memory */
   .data :
   {
-    . = ALIGN(4);
     _sdata = .;        /* create a global symbol at data start */
     *(.data)           /* .data sections */
     *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
 
-    . = ALIGN(4);
     _edata = .;        /* define a global symbol at data end */
   } >RAM AT> FLASH
 
-
-  /* Uninitialized data section */
-  . = ALIGN(4);
+  /* Uninitialized data section into "RAM" Ram type memory */
   .bss :
   {
     /* This is used by the startup in order to initialize the .bss section */
@@ -133,12 +140,11 @@ SECTIONS
     *(.bss*)
     *(COMMON)
 
-    . = ALIGN(4);
     _ebss = .;         /* define a global symbol at bss end */
     __bss_end__ = _ebss;
   } >RAM
 
-  /* User_heap_stack section, used to check that there is enough RAM left */
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
   ._user_heap_stack :
   {
     . = ALIGN(8);
@@ -149,9 +155,7 @@ SECTIONS
     . = ALIGN(8);
   } >RAM
 
-
-
-  /* Remove information from the standard libraries */
+  /* Remove information from the compiler libraries */
   /DISCARD/ :
   {
     libc.a ( * )
diff --git a/hw/bsp/stm32u5/linker/STM32U545xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U545xx_FLASH.ld
new file mode 100644
index 000000000..ce370c643
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U545xx_FLASH.ld
@@ -0,0 +1,167 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author      : STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U545xE Device from STM32U5 series
+**                      512Kbytes FLASH
+**                      272Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** Copyright (c) 2022 STMicroelectronics.
+** All rights reserved.
+**
+** This software is licensed under terms that can be found in the LICENSE file
+** in the root directory of this software component.
+** If no LICENSE file comes with this software, it is provided AS-IS.
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM	(xrw)	: ORIGIN = 0x20000000,	LENGTH = 256K
+  SRAM4	(xrw)	: ORIGIN = 0x28000000,	LENGTH = 16K
+  FLASH	(rx)	: ORIGIN = 0x08000000,	LENGTH = 512K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM);	/* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200 ;	/* required amount of heap  */
+_Min_Stack_Size = 0x400 ;	/* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    KEEP(*(.isr_vector)) /* Startup code */
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+  } >FLASH
+
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } >FLASH
+
+  .ARM :
+  {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } >FLASH
+
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32u5/linker/STM32U575xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U575xx_FLASH.ld
new file mode 100644
index 000000000..b24c533de
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U575xx_FLASH.ld
@@ -0,0 +1,185 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author		: Auto-generated by STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U575xx Device from STM32U5 series
+**                      2048Kbytes ROM
+**                      784Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** © Copyright (c) 2021 STMicroelectronics.
+** All rights reserved.
+**
+** This software component is licensed by ST under BSD 3-Clause license,
+** the "License"; You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at:
+**                        opensource.org/licenses/BSD-3-Clause
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM	(xrw)	: ORIGIN = 0x20000000,	LENGTH = 768K
+  ROM	(rx)	: ORIGIN = 0x08000000,	LENGTH = 2048K
+  SRAM4	(xrw)	: ORIGIN = 0x28000000,	LENGTH = 16K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM);	/* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200;	/* required amount of heap  */
+_Min_Stack_Size = 0x400;	/* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "ROM" Rom type memory */
+  .isr_vector :
+  {
+    . = ALIGN(8);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(8);
+  } >ROM
+
+  /* The program code and other data into "ROM" Rom type memory */
+  .text :
+  {
+    . = ALIGN(8);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(8);
+    _etext = .;        /* define a global symbols at end of code */
+  } >ROM
+
+  /* Constant data into "ROM" Rom type memory */
+  .rodata :
+  {
+    . = ALIGN(8);
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    . = ALIGN(8);
+  } >ROM
+
+  .ARM.extab   : {
+    . = ALIGN(8);
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+    . = ALIGN(8);
+  } >ROM
+
+  .ARM : {
+    . = ALIGN(8);
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+    . = ALIGN(8);
+  } >ROM
+
+  .preinit_array     :
+  {
+    . = ALIGN(8);
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+    . = ALIGN(8);
+  } >ROM
+
+  .init_array :
+  {
+    . = ALIGN(8);
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+    . = ALIGN(8);
+  } >ROM
+
+  .fini_array :
+  {
+    . = ALIGN(8);
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+    . = ALIGN(8);
+  } >ROM
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    . = ALIGN(8);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+
+    . = ALIGN(8);
+    _edata = .;        /* define a global symbol at data end */
+
+  } >RAM AT> ROM
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  . = ALIGN(8);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(8);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32u5/linker/STM32U585xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U585xx_FLASH.ld
new file mode 100644
index 000000000..15b8054bc
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U585xx_FLASH.ld
@@ -0,0 +1,185 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author		: Auto-generated by STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U585xx Device from STM32U5 series
+**                      2048Kbytes ROM
+**                      784Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** © Copyright (c) 2021 STMicroelectronics.
+** All rights reserved.
+**
+** This software component is licensed by ST under BSD 3-Clause license,
+** the "License"; You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at:
+**                        opensource.org/licenses/BSD-3-Clause
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM	(xrw)	: ORIGIN = 0x20000000,	LENGTH = 768K
+  ROM	(rx)	: ORIGIN = 0x08000000,	LENGTH = 2048K
+  SRAM4 (xrw)   : ORIGIN = 0x28000000,  LENGTH = 16K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM);	/* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200;	/* required amount of heap  */
+_Min_Stack_Size = 0x400;	/* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "ROM" Rom type memory */
+  .isr_vector :
+  {
+    . = ALIGN(8);
+    KEEP(*(.isr_vector)) /* Startup code */
+    . = ALIGN(8);
+  } >ROM
+
+  /* The program code and other data into "ROM" Rom type memory */
+  .text :
+  {
+    . = ALIGN(8);
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    . = ALIGN(8);
+    _etext = .;        /* define a global symbols at end of code */
+  } >ROM
+
+  /* Constant data into "ROM" Rom type memory */
+  .rodata :
+  {
+    . = ALIGN(8);
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+    . = ALIGN(8);
+  } >ROM
+
+  .ARM.extab   : {
+    . = ALIGN(8);
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+    . = ALIGN(8);
+  } >ROM
+
+  .ARM : {
+    . = ALIGN(8);
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+    . = ALIGN(8);
+  } >ROM
+
+  .preinit_array     :
+  {
+    . = ALIGN(8);
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+    . = ALIGN(8);
+  } >ROM
+
+  .init_array :
+  {
+    . = ALIGN(8);
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+    . = ALIGN(8);
+  } >ROM
+
+  .fini_array :
+  {
+    . = ALIGN(8);
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+    . = ALIGN(8);
+  } >ROM
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    . = ALIGN(8);
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+
+    . = ALIGN(8);
+    _edata = .;        /* define a global symbol at data end */
+
+  } >RAM AT> ROM
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  . = ALIGN(8);
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    . = ALIGN(8);
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram  type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32u5/linker/STM32U595xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U595xx_FLASH.ld
new file mode 100644
index 000000000..100ee14a5
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U595xx_FLASH.ld
@@ -0,0 +1,168 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author      : STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U595xJ Device from STM32U5 series
+**                      4096Kbytes FLASH
+**                      2528Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** © Copyright (c) 2021 STMicroelectronics.
+** All rights reserved.
+**
+** This software component is licensed by ST under BSD 3-Clause license,
+** the "License"; You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at:
+**                        opensource.org/licenses/BSD-3-Clause
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM   (xrw) : ORIGIN = 0x20000000, LENGTH = 2496K
+  SRAM4 (xrw) : ORIGIN = 0x28000000, LENGTH = 16K
+  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 4096K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200 ; /* required amount of heap */
+_Min_Stack_Size = 0x400 ; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    KEEP(*(.isr_vector)) /* Startup code */
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+  } >FLASH
+
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } >FLASH
+
+  .ARM :
+  {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } >FLASH
+
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32u5/linker/STM32U599xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U599xx_FLASH.ld
new file mode 100644
index 000000000..55997bf40
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U599xx_FLASH.ld
@@ -0,0 +1,168 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author      : STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U599xJ Device from STM32U5 series
+**                      4096Kbytes FLASH
+**                      2528Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** © Copyright (c) 2021 STMicroelectronics.
+** All rights reserved.
+**
+** This software component is licensed by ST under BSD 3-Clause license,
+** the "License"; You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at:
+**                        opensource.org/licenses/BSD-3-Clause
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM (xrw)   : ORIGIN = 0x20000000, LENGTH = 2496K
+  SRAM4 (xrw) : ORIGIN = 0x28000000, LENGTH = 16K
+  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 4096K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200 ; /* required amount of heap */
+_Min_Stack_Size = 0x400 ; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    KEEP(*(.isr_vector)) /* Startup code */
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+  } >FLASH
+
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } >FLASH
+
+  .ARM :
+  {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } >FLASH
+
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32u5/linker/STM32U5A9xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U5A9xx_FLASH.ld
new file mode 100644
index 000000000..a5f7d3405
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U5A9xx_FLASH.ld
@@ -0,0 +1,168 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author      : STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U5A9xJ Device from STM32U5 series
+**                      4096Kbytes FLASH
+**                      2528Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** © Copyright (c) 2021 STMicroelectronics.
+** All rights reserved.
+**
+** This software component is licensed by ST under BSD 3-Clause license,
+** the "License"; You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at:
+**                        opensource.org/licenses/BSD-3-Clause
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM (xrw)   : ORIGIN = 0x20000000, LENGTH = 2496K
+  SRAM4 (xrw) : ORIGIN = 0x28000000, LENGTH = 16K
+  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 4096K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200 ; /* required amount of heap */
+_Min_Stack_Size = 0x400 ; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    KEEP(*(.isr_vector)) /* Startup code */
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+  } >FLASH
+
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } >FLASH
+
+  .ARM :
+  {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } >FLASH
+
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32u5/linker/STM32U5F7xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U5F7xx_FLASH.ld
new file mode 100644
index 000000000..bb9953440
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U5F7xx_FLASH.ld
@@ -0,0 +1,168 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author      : STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U5F7xJ Device from STM32U5 series
+**                      4096Kbytes FLASH
+**                      2528Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** © Copyright (c) 2023 STMicroelectronics.
+** All rights reserved.
+**
+** This software component is licensed by ST under BSD 3-Clause license,
+** the "License"; You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at:
+**                        opensource.org/licenses/BSD-3-Clause
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM (xrw)   : ORIGIN = 0x20000000, LENGTH = 2496K
+  SRAM4 (xrw) : ORIGIN = 0x28000000, LENGTH = 16K
+  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 4096K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200 ; /* required amount of heap */
+_Min_Stack_Size = 0x400 ; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    KEEP(*(.isr_vector)) /* Startup code */
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+  } >FLASH
+
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } >FLASH
+
+  .ARM :
+  {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } >FLASH
+
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32u5/linker/STM32U5F9xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U5F9xx_FLASH.ld
new file mode 100644
index 000000000..d8f1f4c5f
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U5F9xx_FLASH.ld
@@ -0,0 +1,168 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author      : STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U5F9xJ Device from STM32U5 series
+**                      4096Kbytes FLASH
+**                      2528Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** © Copyright (c) 2023 STMicroelectronics.
+** All rights reserved.
+**
+** This software component is licensed by ST under BSD 3-Clause license,
+** the "License"; You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at:
+**                        opensource.org/licenses/BSD-3-Clause
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM (xrw)   : ORIGIN = 0x20000000, LENGTH = 2496K
+  SRAM4 (xrw) : ORIGIN = 0x28000000, LENGTH = 16K
+  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 4096K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200 ; /* required amount of heap */
+_Min_Stack_Size = 0x400 ; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    KEEP(*(.isr_vector)) /* Startup code */
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+  } >FLASH
+
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } >FLASH
+
+  .ARM :
+  {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } >FLASH
+
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32u5/linker/STM32U5G7xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U5G7xx_FLASH.ld
new file mode 100644
index 000000000..d02d6ebf1
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U5G7xx_FLASH.ld
@@ -0,0 +1,168 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author      : STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U5G7xJ Device from STM32U5 series
+**                      4096Kbytes FLASH
+**                      2528Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** © Copyright (c) 2023 STMicroelectronics.
+** All rights reserved.
+**
+** This software component is licensed by ST under BSD 3-Clause license,
+** the "License"; You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at:
+**                        opensource.org/licenses/BSD-3-Clause
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM (xrw)   : ORIGIN = 0x20000000, LENGTH = 2496K
+  SRAM4 (xrw) : ORIGIN = 0x28000000, LENGTH = 16K
+  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 4096K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200 ; /* required amount of heap */
+_Min_Stack_Size = 0x400 ; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    KEEP(*(.isr_vector)) /* Startup code */
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+  } >FLASH
+
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } >FLASH
+
+  .ARM :
+  {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } >FLASH
+
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32u5/linker/STM32U5G9xx_FLASH.ld b/hw/bsp/stm32u5/linker/STM32U5G9xx_FLASH.ld
new file mode 100644
index 000000000..1b072fdd4
--- /dev/null
+++ b/hw/bsp/stm32u5/linker/STM32U5G9xx_FLASH.ld
@@ -0,0 +1,168 @@
+/*
+******************************************************************************
+**
+**  File        : LinkerScript.ld
+**
+**  Author      : STM32CubeIDE
+**
+**  Abstract    : Linker script for STM32U5G9xJ Device from STM32U5 series
+**                      4096Kbytes FLASH
+**                      2528Kbytes RAM
+**
+**                Set heap size, stack size and stack location according
+**                to application requirements.
+**
+**                Set memory bank area and size if external memory is used.
+**
+**  Target      : STMicroelectronics STM32
+**
+**  Distribution: The file is distributed as is without any warranty
+**                of any kind.
+**
+*****************************************************************************
+** @attention
+**
+** © Copyright (c) 2023 STMicroelectronics.
+** All rights reserved.
+**
+** This software component is licensed by ST under BSD 3-Clause license,
+** the "License"; You may not use this file except in compliance with the
+** License. You may obtain a copy of the License at:
+**                        opensource.org/licenses/BSD-3-Clause
+**
+*****************************************************************************
+*/
+
+/* Entry Point */
+ENTRY(Reset_Handler)
+
+/* Memories definition */
+MEMORY
+{
+  RAM (xrw)   : ORIGIN = 0x20000000, LENGTH = 2496K
+  SRAM4 (xrw) : ORIGIN = 0x28000000, LENGTH = 16K
+  FLASH (rx)  : ORIGIN = 0x08000000, LENGTH = 4096K
+}
+
+/* Highest address of the user mode stack */
+_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
+
+_Min_Heap_Size = 0x200 ; /* required amount of heap */
+_Min_Stack_Size = 0x400 ; /* required amount of stack */
+
+/* Sections */
+SECTIONS
+{
+  /* The startup code into "FLASH" Rom type memory */
+  .isr_vector :
+  {
+    KEEP(*(.isr_vector)) /* Startup code */
+  } >FLASH
+
+  /* The program code and other data into "FLASH" Rom type memory */
+  .text :
+  {
+    *(.text)           /* .text sections (code) */
+    *(.text*)          /* .text* sections (code) */
+    *(.glue_7)         /* glue arm to thumb code */
+    *(.glue_7t)        /* glue thumb to arm code */
+    *(.eh_frame)
+
+    KEEP (*(.init))
+    KEEP (*(.fini))
+
+    _etext = .;        /* define a global symbols at end of code */
+  } >FLASH
+
+  /* Constant data into "FLASH" Rom type memory */
+  .rodata :
+  {
+    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
+    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
+  } >FLASH
+
+  .ARM.extab :
+  {
+    *(.ARM.extab* .gnu.linkonce.armextab.*)
+  } >FLASH
+
+  .ARM :
+  {
+    __exidx_start = .;
+    *(.ARM.exidx*)
+    __exidx_end = .;
+  } >FLASH
+
+  .preinit_array :
+  {
+    PROVIDE_HIDDEN (__preinit_array_start = .);
+    KEEP (*(.preinit_array*))
+    PROVIDE_HIDDEN (__preinit_array_end = .);
+  } >FLASH
+
+  .init_array :
+  {
+    PROVIDE_HIDDEN (__init_array_start = .);
+    KEEP (*(SORT(.init_array.*)))
+    KEEP (*(.init_array*))
+    PROVIDE_HIDDEN (__init_array_end = .);
+  } >FLASH
+
+  .fini_array :
+  {
+    PROVIDE_HIDDEN (__fini_array_start = .);
+    KEEP (*(SORT(.fini_array.*)))
+    KEEP (*(.fini_array*))
+    PROVIDE_HIDDEN (__fini_array_end = .);
+  } >FLASH
+
+  /* Used by the startup to initialize data */
+  _sidata = LOADADDR(.data);
+
+  /* Initialized data sections into "RAM" Ram type memory */
+  .data :
+  {
+    _sdata = .;        /* create a global symbol at data start */
+    *(.data)           /* .data sections */
+    *(.data*)          /* .data* sections */
+    *(.RamFunc)        /* .RamFunc sections */
+    *(.RamFunc*)       /* .RamFunc* sections */
+
+    _edata = .;        /* define a global symbol at data end */
+  } >RAM AT> FLASH
+
+  /* Uninitialized data section into "RAM" Ram type memory */
+  .bss :
+  {
+    /* This is used by the startup in order to initialize the .bss section */
+    _sbss = .;         /* define a global symbol at bss start */
+    __bss_start__ = _sbss;
+    *(.bss)
+    *(.bss*)
+    *(COMMON)
+
+    _ebss = .;         /* define a global symbol at bss end */
+    __bss_end__ = _ebss;
+  } >RAM
+
+  /* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
+  ._user_heap_stack :
+  {
+    . = ALIGN(8);
+    PROVIDE ( end = . );
+    PROVIDE ( _end = . );
+    . = . + _Min_Heap_Size;
+    . = . + _Min_Stack_Size;
+    . = ALIGN(8);
+  } >RAM
+
+  /* Remove information from the compiler libraries */
+  /DISCARD/ :
+  {
+    libc.a ( * )
+    libm.a ( * )
+    libgcc.a ( * )
+  }
+
+  .ARM.attributes 0 : { *(.ARM.attributes) }
+}
diff --git a/hw/bsp/stm32wb/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/stm32wb/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..6ad115f60
--- /dev/null
+++ b/hw/bsp/stm32wb/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,153 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "stm32wbxx.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#if defined(__ARM_FP) && __ARM_FP >= 4
+  #define configENABLE_FPU                      1
+#else
+  #define configENABLE_FPU                      0
+#endif
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       4
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<FLASH
@@ -52,14 +53,14 @@ SECTIONS
         . = ALIGN(4);
     }>SRAM
 
-  /* User_heap_stack section, used to check that there is enough RAM left */
-  ._user_heap_stack :
-  {
-    . = ALIGN(8);
-    PROVIDE ( end = . );
-    PROVIDE ( _end = . );
-    . = . + _Min_Heap_Size;
-    . = . + _Min_Stack_Size;
-    . = ALIGN(8);
-  } >SRAM
+    /* User_heap_stack section, used to check that there is enough RAM left */
+    ._user_heap_stack :
+    {
+        . = ALIGN(8);
+        PROVIDE ( end = . );
+        PROVIDE ( _end = . );
+        . = . + _Min_Heap_Size;
+        . = . + _Min_Stack_Size;
+        . = ALIGN(8);
+    } >SRAM
 }
diff --git a/hw/bsp/tm4c123/family.c b/hw/bsp/tm4c/family.c
similarity index 100%
rename from hw/bsp/tm4c123/family.c
rename to hw/bsp/tm4c/family.c
diff --git a/hw/bsp/tm4c/family.cmake b/hw/bsp/tm4c/family.cmake
new file mode 100644
index 000000000..86db985d6
--- /dev/null
+++ b/hw/bsp/tm4c/family.cmake
@@ -0,0 +1,95 @@
+include_guard()
+
+include(${CMAKE_CURRENT_LIST_DIR}/boards/${BOARD}/board.cmake)
+
+set(MCU_VARIANT tm4c${MCU_SUB_VARIANT})
+set(MCU_VARIANT_UPPER TM4C${MCU_SUB_VARIANT})
+
+set(SDK_DIR ${TOP}/hw/mcu/ti/${MCU_VARIANT}xx)
+set(CMSIS_DIR ${TOP}/lib/CMSIS_5)
+
+# toolchain set up
+set(CMAKE_SYSTEM_PROCESSOR cortex-m4 CACHE INTERNAL "System Processor")
+set(CMAKE_TOOLCHAIN_FILE ${TOP}/examples/build_system/cmake/toolchain/arm_${TOOLCHAIN}.cmake)
+
+set(FAMILY_MCUS TM4C123 CACHE INTERNAL "")
+
+#------------------------------------
+# BOARD_TARGET
+#------------------------------------
+# only need to be built ONCE for all examples
+function(add_board_target BOARD_TARGET)
+  if (TARGET ${BOARD_TARGET})
+    return()
+  endif()
+
+  set(LD_FILE_Clang ${LD_FILE_GNU})
+
+  set(STARTUP_FILE_GNU ${SDK_DIR}/Source/GCC/${MCU_VARIANT}_startup.c)
+  set(STARTUP_FILE_Clang ${STARTUP_FILE_GNU})
+
+  add_library(${BOARD_TARGET} STATIC
+    ${SDK_DIR}/Source/system_${MCU_VARIANT_UPPER}.c
+    ${STARTUP_FILE_${CMAKE_C_COMPILER_ID}}
+    )
+  target_include_directories(${BOARD_TARGET} PUBLIC
+    ${SDK_DIR}/Include/${MCU_VARIANT_UPPER}
+    ${CMSIS_DIR}/CMSIS/Core/Include
+    )
+
+  update_board(${BOARD_TARGET})
+
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--script=${LD_FILE_GNU}"
+      --specs=nosys.specs --specs=nano.specs
+      -uvectors
+      )
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "Clang")
+    message(FATAL_ERROR "Clang is not supported for MSP432E4")
+  elseif (CMAKE_C_COMPILER_ID STREQUAL "IAR")
+    target_link_options(${BOARD_TARGET} PUBLIC
+      "LINKER:--config=${LD_FILE_IAR}"
+      )
+  endif ()
+endfunction()
+
+
+#------------------------------------
+# Functions
+#------------------------------------
+function(family_configure_example TARGET RTOS)
+  family_configure_common(${TARGET} ${RTOS})
+
+  # Board target
+  add_board_target(board_${BOARD})
+
+  #---------- Port Specific ----------
+  # These files are built for each example since it depends on example's tusb_config.h
+  target_sources(${TARGET} PUBLIC
+    # BSP
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/family.c
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../board.c
+    )
+  target_include_directories(${TARGET} PUBLIC
+    # family, hw, board
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../../
+    ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
+    )
+
+  # Add TinyUSB target and port source
+  family_add_tinyusb(${TARGET} OPT_MCU_TM4C123 ${RTOS})
+  target_sources(${TARGET}-tinyusb PUBLIC
+    ${TOP}/src/portable/mentor/musb/dcd_musb.c
+    ${TOP}/src/portable/mentor/musb/hcd_musb.c
+    )
+  target_link_libraries(${TARGET}-tinyusb PUBLIC board_${BOARD})
+
+  # Link dependencies
+  target_link_libraries(${TARGET} PUBLIC board_${BOARD} ${TARGET}-tinyusb)
+
+  # Flashing
+  family_add_bin_hex(${TARGET})
+  family_flash_openocd(${TARGET})
+endfunction()
diff --git a/hw/bsp/tm4c/family.mk b/hw/bsp/tm4c/family.mk
new file mode 100644
index 000000000..76ae785b2
--- /dev/null
+++ b/hw/bsp/tm4c/family.mk
@@ -0,0 +1,28 @@
+include $(TOP)/$(BOARD_PATH)/board.mk
+CPU_CORE ?= cortex-m4
+
+MCU_VARIANT = tm4c${MCU_SUB_VARIANT}
+MCU_VARIANT_UPPER = TM4C${MCU_SUB_VARIANT}
+
+SDK_DIR = hw/mcu/ti/${MCU_VARIANT}xx
+
+CFLAGS += \
+  -flto \
+  -DCFG_TUSB_MCU=OPT_MCU_TM4C123 \
+  -uvectors \
+
+# mcu driver cause following warnings
+CFLAGS += -Wno-error=strict-prototypes -Wno-error=cast-qual
+
+LDFLAGS_GCC += --specs=nosys.specs --specs=nano.specs
+
+INC += \
+	$(TOP)/lib/CMSIS_5/CMSIS/Core/Include \
+	$(TOP)/$(SDK_DIR)/Include/${MCU_VARIANT_UPPER} \
+	$(TOP)/$(BOARD_PATH)
+
+SRC_C += \
+	src/portable/mentor/musb/dcd_musb.c \
+	src/portable/mentor/musb/hcd_musb.c \
+	$(SDK_DIR)/Source/system_${MCU_VARIANT_UPPER}.c \
+	$(SDK_DIR)/Source/GCC/${MCU_VARIANT}_startup.c
diff --git a/hw/bsp/tm4c123/family.mk b/hw/bsp/tm4c123/family.mk
deleted file mode 100644
index 49e39f6a0..000000000
--- a/hw/bsp/tm4c123/family.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-DEPS_SUBMODULES += hw/mcu/ti
-MCU_DIR=hw/mcu/ti/tm4c123xx
-
-include $(TOP)/$(BOARD_PATH)/board.mk
-CPU_CORE ?= cortex-m4
-
-CFLAGS += \
-  -flto \
-  -DCFG_TUSB_MCU=OPT_MCU_TM4C123 \
-  -uvectors \
-  -DTM4C123GH6PM
-
-# mcu driver cause following warnings
-CFLAGS += -Wno-error=strict-prototypes -Wno-error=cast-qual
-
-LDFLAGS_GCC += -specs=nosys.specs -specs=nano.specs
-
-# All source paths should be relative to the top level.
-LD_FILE = $(BOARD_PATH)/tm4c123.ld
-
-INC += \
-	$(TOP)/$(MCU_DIR)/CMSIS/5.7.0/CMSIS/Include \
-	$(TOP)/$(MCU_DIR)/Include/TM4C123 \
-	$(TOP)/$(BOARD_PATH)
-
-SRC_C += \
-	src/portable/mentor/musb/dcd_musb.c \
-	src/portable/mentor/musb/hcd_musb.c \
-	$(MCU_DIR)/Source/system_TM4C123.c \
-	$(MCU_DIR)/Source/GCC/tm4c123_startup.c
diff --git a/hw/bsp/xmc4000/FreeRTOSConfig/FreeRTOSConfig.h b/hw/bsp/xmc4000/FreeRTOSConfig/FreeRTOSConfig.h
new file mode 100644
index 000000000..76eacea39
--- /dev/null
+++ b/hw/bsp/xmc4000/FreeRTOSConfig/FreeRTOSConfig.h
@@ -0,0 +1,149 @@
+/*
+ * FreeRTOS Kernel V10.0.0
+ * Copyright (C) 2017 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * 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. If you wish to use our Amazon
+ * FreeRTOS name, please do so in a fair use way that does not cause confusion.
+ *
+ * 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.
+ *
+ * http://www.FreeRTOS.org
+ * http://aws.amazon.com/freertos
+ *
+ * 1 tab == 4 spaces!
+ */
+
+
+#ifndef FREERTOS_CONFIG_H
+#define FREERTOS_CONFIG_H
+
+/*-----------------------------------------------------------
+ * Application specific definitions.
+ *
+ * These definitions should be adjusted for your particular hardware and
+ * application requirements.
+ *
+ * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
+ * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE.
+ *
+ * See http://www.freertos.org/a00110.html.
+ *----------------------------------------------------------*/
+
+// skip if included from IAR assembler
+#ifndef __IASMARM__
+  #include "xmc_device.h"
+#endif
+
+/* Cortex M23/M33 port configuration. */
+#define configENABLE_MPU                        0
+#define configENABLE_FPU                        1
+#define configENABLE_TRUSTZONE                  0
+#define configMINIMAL_SECURE_STACK_SIZE         (1024)
+
+#define configUSE_PREEMPTION                    1
+#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0
+#define configCPU_CLOCK_HZ                      SystemCoreClock
+#define configTICK_RATE_HZ                      ( 1000 )
+#define configMAX_PRIORITIES                    ( 5 )
+#define configMINIMAL_STACK_SIZE                ( 128 )
+#define configTOTAL_HEAP_SIZE                   ( configSUPPORT_DYNAMIC_ALLOCATION*4*1024 )
+#define configMAX_TASK_NAME_LEN                 16
+#define configUSE_16_BIT_TICKS                  0
+#define configIDLE_SHOULD_YIELD                 1
+#define configUSE_MUTEXES                       1
+#define configUSE_RECURSIVE_MUTEXES             1
+#define configUSE_COUNTING_SEMAPHORES           1
+#define configQUEUE_REGISTRY_SIZE               4
+#define configUSE_QUEUE_SETS                    0
+#define configUSE_TIME_SLICING                  0
+#define configUSE_NEWLIB_REENTRANT              0
+#define configENABLE_BACKWARD_COMPATIBILITY     1
+#define configSTACK_ALLOCATION_FROM_SEPARATE_HEAP   0
+
+#define configSUPPORT_STATIC_ALLOCATION         1
+#define configSUPPORT_DYNAMIC_ALLOCATION        0
+
+/* Hook function related definitions. */
+#define configUSE_IDLE_HOOK                    0
+#define configUSE_TICK_HOOK                    0
+#define configUSE_MALLOC_FAILED_HOOK           0 // cause nested extern warning
+#define configCHECK_FOR_STACK_OVERFLOW         2
+#define configCHECK_HANDLER_INSTALLATION       0
+
+/* Run time and task stats gathering related definitions. */
+#define configGENERATE_RUN_TIME_STATS          0
+#define configRECORD_STACK_HIGH_ADDRESS        1
+#define configUSE_TRACE_FACILITY               1 // legacy trace
+#define configUSE_STATS_FORMATTING_FUNCTIONS   0
+
+/* Co-routine definitions. */
+#define configUSE_CO_ROUTINES                  0
+#define configMAX_CO_ROUTINE_PRIORITIES        2
+
+/* Software timer related definitions. */
+#define configUSE_TIMERS                       1
+#define configTIMER_TASK_PRIORITY              (configMAX_PRIORITIES-2)
+#define configTIMER_QUEUE_LENGTH               32
+#define configTIMER_TASK_STACK_DEPTH           configMINIMAL_STACK_SIZE
+
+/* Optional functions - most linkers will remove unused functions anyway. */
+#define INCLUDE_vTaskPrioritySet               0
+#define INCLUDE_uxTaskPriorityGet              0
+#define INCLUDE_vTaskDelete                    0
+#define INCLUDE_vTaskSuspend                   1 // required for queue, semaphore, mutex to be blocked indefinitely with portMAX_DELAY
+#define INCLUDE_xResumeFromISR                 0
+#define INCLUDE_vTaskDelayUntil                1
+#define INCLUDE_vTaskDelay                     1
+#define INCLUDE_xTaskGetSchedulerState         0
+#define INCLUDE_xTaskGetCurrentTaskHandle      1
+#define INCLUDE_uxTaskGetStackHighWaterMark    0
+#define INCLUDE_xTaskGetIdleTaskHandle         0
+#define INCLUDE_xTimerGetTimerDaemonTaskHandle 0
+#define INCLUDE_pcTaskGetTaskName              0
+#define INCLUDE_eTaskGetState                  0
+#define INCLUDE_xEventGroupSetBitFromISR       0
+#define INCLUDE_xTimerPendFunctionCall         0
+
+/* FreeRTOS hooks to NVIC vectors */
+#define xPortPendSVHandler    PendSV_Handler
+#define xPortSysTickHandler   SysTick_Handler
+#define vPortSVCHandler       SVC_Handler
+
+//--------------------------------------------------------------------+
+// Interrupt nesting behavior configuration.
+//--------------------------------------------------------------------+
+
+// For Cortex-M specific: __NVIC_PRIO_BITS is defined in mcu header
+#define configPRIO_BITS       6
+
+/* The lowest interrupt priority that can be used in a call to a "set priority" function. */
+#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY			  ((1<core, args->savename, &size);
+  if(elf->data) {
+      free(elf->data);
+  }
   free(elf);
 
+
   c = cipher_init(vmk, NULL);
   cipher_calc_cmac(c, spkimage, size, (uint8_t *) spkimage + size);
   cipher_deinit(c);
diff --git a/lib/embedded-cli/embedded_cli.h b/lib/embedded-cli/embedded_cli.h
index 33354cd84..91c96f3a1 100644
--- a/lib/embedded-cli/embedded_cli.h
+++ b/lib/embedded-cli/embedded_cli.h
@@ -743,7 +743,7 @@ EmbeddedCli *embeddedCliNew(EmbeddedCliConfig *config) {
 
     bool allocated = false;
     if (config->cliBuffer == NULL) {
-        config->cliBuffer = (CLI_UINT *) malloc(totalSize); // malloc guarantees alignment.
+//        config->cliBuffer = (CLI_UINT *) malloc(totalSize); // malloc guarantees alignment.
         if (config->cliBuffer == NULL)
             return NULL;
         allocated = true;
@@ -887,7 +887,7 @@ void embeddedCliFree(EmbeddedCli *cli) {
     PREPARE_IMPL(cli);
     if (IS_FLAG_SET(impl->flags, CLI_FLAG_ALLOCATED)) {
         // allocation is done in single call to malloc, so need only single free
-        free(cli);
+//        free(cli);
     }
 }
 
diff --git a/lib/networking/dnserver.c b/lib/networking/dnserver.c
index 6d15fee02..25bbf4875 100644
--- a/lib/networking/dnserver.c
+++ b/lib/networking/dnserver.c
@@ -192,7 +192,7 @@ err_t dnserv_init(const ip_addr_t *bind, uint16_t port, dns_query_proc_t qp)
 	return ERR_OK;
 }
 
-void dnserv_free()
+void dnserv_free(void)
 {
 	if (pcb == NULL) return;
 	udp_remove(pcb);
diff --git a/lib/rt-thread/SConscript b/lib/rt-thread/SConscript
new file mode 100644
index 000000000..34399fd45
--- /dev/null
+++ b/lib/rt-thread/SConscript
@@ -0,0 +1,56 @@
+import rtconfig
+from building import *
+
+cwd     = GetCurrentDir()
+src     = Split("""
+../../src/tusb.c
+../../src/common/tusb_fifo.c
+./tusb_rt_thread_port.c
+""")
+path = [cwd, cwd + "/../../src"]
+
+LOCAL_CFLAGS = ''
+
+# for device stack
+if GetDepend(["PKG_TINYUSB_DEVICE_ENABLE"]):
+    src += ["../../src/device/usbd.c",
+            "../../src/device/usbd_control.c"]
+    # BSP
+    if GetDepend(["SOC_FAMILY_STM32"]):
+        src += ["../../src/portable/synopsys/dwc2/dcd_dwc2.c",
+                "../../src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c"]
+
+    if GetDepend(["SOC_NRF52840"]):
+        src += ["../../src/portable/nordic/nrf5x/dcd_nrf5x.c"]
+
+    if GetDepend(["SOC_FAMILY_RENESAS"]):
+        src += ["../../src/portable/renesas/rusb2/dcd_rusb2.c",
+                "../../src/portable/renesas/rusb2/rusb2_common.c"]
+
+    # Device class
+    if GetDepend(["PKG_TINYUSB_DEVICE_UVC"]):
+        src += ["../../src/class/video/video_device.c"]
+    if GetDepend(["PKG_TINYUSB_DEVICE_CDC"]):
+        src += ["../../src/class/cdc/cdc_device.c"]
+    if GetDepend(["PKG_TINYUSB_DEVICE_MSC"]):
+        src += ["../../src/class/msc/msc_device.c", "port/msc_device_port.c"]
+    if GetDepend(["PKG_TINYUSB_DEVICE_HID"]):
+        src += ["../../src/class/hid/hid_device.c"]
+
+# for host stack
+if GetDepend(["PKG_TINYUSB_HOST_ENABLE"]):
+    src += ["../../src/host/usbh.c", "../../src/host/hub.c"]
+
+    if GetDepend(["SOC_FAMILY_RENESAS"]):
+        src += ["../../src/portable/renesas/rusb2/hcd_rusb2.c",
+                "../../src/portable/renesas/rusb2/rusb2_common.c"]
+
+
+if rtconfig.PLATFORM == 'gcc' or rtconfig.PLATFORM == 'armclang': # GCC or Keil AC6
+    LOCAL_CFLAGS += ' -std=c99'
+elif rtconfig.PLATFORM == 'armcc': # Keil AC5
+    LOCAL_CFLAGS += ' --c99 --gnu'
+
+group = DefineGroup('TinyUSB', src, depend = ['PKG_USING_TINYUSB'], CPPPATH = path, LOCAL_CFLAGS = LOCAL_CFLAGS)
+
+Return('group')
diff --git a/lib/rt-thread/port/msc_device_port.c b/lib/rt-thread/port/msc_device_port.c
new file mode 100644
index 000000000..c7e8c508e
--- /dev/null
+++ b/lib/rt-thread/port/msc_device_port.c
@@ -0,0 +1,171 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+#ifdef __RTTHREAD__
+#include 
+#include 
+
+#include 
+#include 
+
+static bool ejected = false;
+static rt_device_t flash_device;
+static struct rt_device_blk_geometry blk_geom;
+
+#ifdef __CC_ARM
+uint16_t __builtin_bswap16(uint16_t x)
+{
+    return (x << 8) | (x >> 8);
+}
+#endif
+
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
+{
+    (void) lun;
+
+    const char vid[] = PKG_TINYUSB_DEVICE_MSC_VID;
+    const char pid[] = PKG_TINYUSB_DEVICE_MSC_PID;
+    const char rev[] = PKG_TINYUSB_DEVICE_MSC_REV;
+
+    memcpy(vendor_id, vid, strlen(vid));
+    memcpy(product_id, pid, strlen(pid));
+    memcpy(product_rev, rev, strlen(rev));
+}
+
+bool tud_msc_test_unit_ready_cb(uint8_t lun)
+{
+    (void) lun;
+
+    if (ejected)
+    {
+        tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
+        return false;
+    }
+
+    if (flash_device == NULL)
+    {
+        flash_device = rt_device_find(PKG_TINYUSB_DEVICE_MSC_NAME);
+    }
+    if (flash_device != NULL)
+    {
+        static uint8_t open_flg = 0;
+        if (!open_flg)
+        {
+            open_flg = 1;
+            rt_device_open(flash_device, 0);
+        }
+
+        rt_device_control(flash_device, RT_DEVICE_CTRL_BLK_GETGEOME, &blk_geom);
+        return true;
+    }
+
+    return false;
+}
+
+void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size)
+{
+    (void) lun;
+
+    *block_count = blk_geom.sector_count;
+    *block_size  = blk_geom.bytes_per_sector;
+}
+
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
+{
+    (void) lun;
+    (void) power_condition;
+
+    if (load_eject)
+    {
+        if (start)
+        {
+            ejected = false;
+        } else {
+            // unload disk storage
+            ejected = true;
+        }
+    }
+
+    return true;
+}
+
+int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
+{
+    (void) lun;
+    (void) offset;
+    (void) bufsize;
+
+    return (int32_t) rt_device_read(flash_device, (rt_off_t) lba, buffer, 1) * blk_geom.bytes_per_sector;
+}
+
+int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize)
+{
+    (void) lun;
+    (void) offset;
+    (void) bufsize;
+
+    return (int32_t) rt_device_write(flash_device, (rt_off_t) lba, buffer, 1) * blk_geom.bytes_per_sector;
+}
+
+int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize)
+{
+    void const *response = NULL;
+    uint16_t resplen = 0;
+
+    // most scsi handled is input
+    bool in_xfer = true;
+
+    switch (scsi_cmd[0])
+    {
+        case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+            // Host is about to read/write etc ... better not to disconnect disk
+            resplen = 0;
+            break;
+
+        default:
+            // Set Sense = Invalid Command Operation
+            tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
+
+            // negative means error -> tinyusb could stall and/or response with failed status
+            resplen = -1;
+            break;
+    }
+
+    // return resplen must not larger than bufsize
+    if (resplen > bufsize) resplen = bufsize;
+
+    if (response && (resplen > 0))
+    {
+        if (in_xfer)
+        {
+            memcpy(buffer, response, resplen);
+        } else {
+            // SCSI output
+        }
+    }
+
+    return resplen;
+}
+#endif /*__RTTHREAD__*/
diff --git a/lib/rt-thread/tusb_config.h b/lib/rt-thread/tusb_config.h
new file mode 100644
index 000000000..11dc21983
--- /dev/null
+++ b/lib/rt-thread/tusb_config.h
@@ -0,0 +1,218 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+
+#ifndef _TUSB_CONFIG_H_
+#define _TUSB_CONFIG_H_
+
+#ifdef __RTTHREAD__
+#include 
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------------------------
+// COMMON CONFIGURATION
+//--------------------------------------------------------------------
+
+#if   defined(SOC_SERIES_STM32F0)
+#define CFG_TUSB_MCU    OPT_MCU_STM32F0
+#elif defined(SOC_SERIES_STM32F1)
+#define CFG_TUSB_MCU    OPT_MCU_STM32F1
+#elif defined(SOC_SERIES_STM32F2)
+#define CFG_TUSB_MCU    OPT_MCU_STM32F2
+#elif defined(SOC_SERIES_STM32F3)
+#define CFG_TUSB_MCU    OPT_MCU_STM32F3
+#elif defined(SOC_SERIES_STM32F4)
+#define CFG_TUSB_MCU    OPT_MCU_STM32F4
+#elif defined(SOC_SERIES_STM32F7)
+#define CFG_TUSB_MCU    OPT_MCU_STM32F7
+#elif defined(SOC_SERIES_STM32H7)
+#define CFG_TUSB_MCU    OPT_MCU_STM32H7
+#elif defined(SOC_SERIES_STM32L0)
+#define CFG_TUSB_MCU    OPT_MCU_STM32L0
+#elif defined(SOC_SERIES_STM32L1)
+#define CFG_TUSB_MCU    OPT_MCU_STM32L1
+#elif defined(SOC_SERIES_STM32L4)
+#define CFG_TUSB_MCU    OPT_MCU_STM32L4
+#elif defined(SOC_NRF52840)
+#define CFG_TUSB_MCU    OPT_MCU_NRF5X
+#elif defined(SOC_HPM6000)
+#define CFG_TUSB_MCU    OPT_MCU_HPM
+#elif defined(SOC_RP2040)
+#define CFG_TUSB_MCU    OPT_MCU_RP2040
+#elif defined(SOC_FAMILY_RENESAS)
+#define CFG_TUSB_MCU    OPT_MCU_RAXXX
+#else
+#error "Not support for current MCU"
+#endif
+
+#define CFG_TUSB_OS OPT_OS_RTTHREAD
+
+//--------------------------------------------------------------------
+// DEBUG CONFIGURATION
+//--------------------------------------------------------------------
+#ifdef CFG_TUSB_DEBUG
+#define CFG_TUSB_DEBUG_PRINTF rt_kprintf
+#endif /* CFG_TUSB_DEBUG */
+
+#ifndef BOARD_DEVICE_RHPORT_NUM
+#define BOARD_DEVICE_RHPORT_NUM     PKG_TINYUSB_RHPORT_NUM
+#endif
+
+#ifndef BOARD_DEVICE_RHPORT_SPEED
+#define BOARD_DEVICE_RHPORT_SPEED   PKG_TINYUSB_DEVICE_PORT_SPEED
+#endif
+
+#if   BOARD_DEVICE_RHPORT_NUM == 0
+#define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
+#elif BOARD_DEVICE_RHPORT_NUM == 1
+#define CFG_TUSB_RHPORT1_MODE     (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED)
+#else
+  #error "Incorrect RHPort configuration"
+#endif
+
+/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
+ * Tinyusb use follows macros to declare transferring memory so that they can be put
+ * into those specific section.
+ * e.g
+ * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
+ * - CFG_TUSB_MEM_ALIGN   : __attribute__ ((aligned(4)))
+ */
+#ifndef CFG_TUSB_MEM_SECTION
+#define CFG_TUSB_MEM_SECTION        rt_section(PKG_TINYUSB_MEM_SECTION)
+#endif
+
+#ifndef CFG_TUSB_MEM_ALIGN
+#define CFG_TUSB_MEM_ALIGN          rt_align(PKG_TINYUSB_MEM_ALIGN)
+#endif
+
+//--------------------------------------------------------------------
+// DEVICE CONFIGURATION
+//--------------------------------------------------------------------
+#if defined(PKG_TINYUSB_DEVICE_ENABLE)
+  #define CFG_TUD_ENABLED             (1)
+#else
+  #define CFG_TUD_ENABLED             (0)
+#endif
+
+#ifndef CFG_TUD_ENDPOINT0_SIZE
+#define CFG_TUD_ENDPOINT0_SIZE        PKG_TINYUSB_EDPT0_SIZE
+#endif
+
+// CDC FIFO size of TX and RX
+#define CFG_TUD_CDC_RX_BUFSIZE        PKG_TINYUSB_DEVICE_CDC_RX_BUFSIZE
+#define CFG_TUD_CDC_TX_BUFSIZE        PKG_TINYUSB_DEVICE_CDC_TX_BUFSIZE
+
+#define CFG_TUD_MSC_EP_BUFSIZE        PKG_TINYUSB_DEVICE_MSC_EP_BUFSIZE
+
+#define CFG_TUD_HID_EP_BUFSIZE        PKG_TINYUSB_DEVICE_HID_EP_BUFSIZE
+
+#ifndef PKG_TINYUSB_DEVICE_CDC_STRING
+#define PKG_TINYUSB_DEVICE_CDC_STRING ""
+#endif
+
+#ifndef PKG_TINYUSB_DEVICE_MSC_STRING
+#define PKG_TINYUSB_DEVICE_MSC_STRING ""
+#endif
+
+#ifndef PKG_TINYUSB_DEVICE_HID_STRING
+#define PKG_TINYUSB_DEVICE_HID_STRING ""
+#endif
+
+//--------------------------------------------------------------------
+// HOST CONFIGURATION
+//--------------------------------------------------------------------
+#if defined(PKG_TINYUSB_HOST_ENABLE)
+  #define CFG_TUH_ENABLED             (1)
+#else
+  #define CFG_TUH_ENABLED             (0)
+#endif
+
+#if (PKG_TINYUSB_HOST_PORT == 0) && defined(PKG_TINYUSB_HOST_ENABLE)
+#undef CFG_TUSB_RHPORT0_MODE
+#define CFG_TUSB_RHPORT0_MODE     (OPT_MODE_HOST | PKG_TINYUSB_HOST_PORT_SPEED)
+#endif
+
+#if (PKG_TINYUSB_HOST_PORT == 1) && defined(PKG_TINYUSB_HOST_ENABLE)
+#undef CFG_TUSB_RHPORT1_MODE
+#define CFG_TUSB_RHPORT1_MODE     (OPT_MODE_HOST | PKG_TINYUSB_HOST_PORT_SPEED)
+#endif
+
+#define BOARD_TUH_RHPORT            PKG_TINYUSB_HOST_PORT // FULL SPEED
+#define BOARD_TUH_MAX_SPEED         PKG_TINYUSB_HOST_PORT_SPEED
+// Default is max speed that hardware controller could support with on-chip PHY
+#define CFG_TUH_MAX_SPEED           BOARD_TUH_MAX_SPEED
+
+//------------------------- Board Specific --------------------------
+
+// RHPort number used for host can be defined by board.mk, default to port 0
+#ifndef BOARD_TUH_RHPORT
+#define BOARD_TUH_RHPORT      0
+#endif
+
+// RHPort max operational speed can defined by board.mk
+#ifndef BOARD_TUH_MAX_SPEED
+#define BOARD_TUH_MAX_SPEED   OPT_MODE_DEFAULT_SPEED
+#endif
+
+// Size of buffer to hold descriptors and other data used for enumeration
+#define CFG_TUH_ENUMERATION_BUFSIZE 256
+
+#define CFG_TUH_HUB                 2 // number of supported hubs
+#define CFG_TUH_CDC                 0 // CDC ACM
+#define CFG_TUH_CDC_FTDI            0 // FTDI Serial.  FTDI is not part of CDC class, only to re-use CDC driver API
+#define CFG_TUH_CDC_CP210X          0 // CP210x Serial. CP210X is not part of CDC class, only to re-use CDC driver API
+#define CFG_TUH_CDC_CH34X           0 // CH340 or CH341 Serial. CH34X is not part of CDC class, only to re-use CDC driver API
+#define CFG_TUH_HID                 0 // typical keyboard + mouse device can have 3-4 HID interfaces
+#define CFG_TUH_MSC                 0
+//#define CFG_TUH_VENDOR              3
+
+// max device support (excluding hub device): 1 hub typically has 4 ports
+#define CFG_TUH_DEVICE_MAX          (3*CFG_TUH_HUB + 1)
+
+//------------- HID -------------//
+#define CFG_TUH_HID_EPIN_BUFSIZE    64
+#define CFG_TUH_HID_EPOUT_BUFSIZE   64
+
+//------------- CDC -------------//
+
+// Set Line Control state on enumeration/mounted:
+// DTR ( bit 0), RTS (bit 1)
+#define CFG_TUH_CDC_LINE_CONTROL_ON_ENUM    0x03
+
+// Set Line Coding on enumeration/mounted, value for cdc_line_coding_t
+// bit rate = 115200, 1 stop bit, no parity, 8 bit data width
+#define CFG_TUH_CDC_LINE_CODING_ON_ENUM   { 115200, CDC_LINE_CODING_STOP_BITS_1, CDC_LINE_CODING_PARITY_NONE, 8 }
+
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /*__RTTHREAD__*/
+
+#endif /* _TUSB_CONFIG_H_ */
diff --git a/lib/rt-thread/tusb_rt_thread_port.c b/lib/rt-thread/tusb_rt_thread_port.c
new file mode 100644
index 000000000..7e04d2453
--- /dev/null
+++ b/lib/rt-thread/tusb_rt_thread_port.c
@@ -0,0 +1,86 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+#ifdef __RTTHREAD__
+#include 
+
+#define  DBG_TAG  "TinyUSB"
+#define  DBG_LVL  DBG_INFO
+#include 
+#include 
+
+#ifndef RT_USING_HEAP
+/* if there is not enable heap, we should use static thread and stack. */
+static rt_uint8_t tusb_stack[PKG_TINYUSB_STACK_SIZE];
+static struct rt_thread tusb_thread;
+#endif /* RT_USING_HEAP */
+
+extern int tusb_board_init(void);
+
+static void tusb_thread_entry(void *parameter)
+{
+    (void) parameter;
+    while (1)
+    {
+#if CFG_TUH_ENABLED
+        tuh_task();
+#endif
+#if CFG_TUD_ENABLED
+        tud_task();
+#endif
+    }
+}
+
+static int init_tinyusb(void)
+{
+    rt_thread_t tid;
+
+    tusb_board_init();
+    tusb_init();
+
+#ifdef RT_USING_HEAP
+    tid = rt_thread_create("tusb", tusb_thread_entry, RT_NULL,
+                           PKG_TINYUSB_STACK_SIZE,
+                           PKG_TINYUSB_THREAD_PRIORITY, 10);
+    if (tid == RT_NULL)
+#else
+    rt_err_t result;
+
+    tid = &tusb_thread;
+    result = rt_thread_init(tid, "tusb", tusb_thread_entry, RT_NULL,
+                            tusb_stack, sizeof(tusb_stack), 4, 10);
+    if (result != RT_EOK)
+#endif /* RT_USING_HEAP */
+    {
+        LOG_E("Fail to create TinyUSB thread");
+        return -1;
+    }
+
+    rt_thread_startup(tid);
+
+    return 0;
+}
+INIT_APP_EXPORT(init_tinyusb);
+#endif /*__RTTHREAD__*/
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 076e1e1eb..272962332 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -41,7 +41,7 @@ function(add_tinyusb TARGET)
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/../lib/networking
     )
 
-  if (CMAKE_C_COMPILER_ID STREQUAL "GNU")
+  if (CMAKE_C_COMPILER_ID STREQUAL "GNU" OR CMAKE_C_COMPILER_ID STREQUAL "Clang")
     target_compile_options(${TARGET} PRIVATE
       -Wall
       -Wextra
diff --git a/src/class/audio/audio.h b/src/class/audio/audio.h
index d6f3e22e2..2f97c0f23 100644
--- a/src/class/audio/audio.h
+++ b/src/class/audio/audio.h
@@ -489,7 +489,7 @@ typedef enum
   AUDIO_DATA_FORMAT_TYPE_I_IEEE_FLOAT     = (uint32_t) (1 << 2),
   AUDIO_DATA_FORMAT_TYPE_I_ALAW           = (uint32_t) (1 << 3),
   AUDIO_DATA_FORMAT_TYPE_I_MULAW          = (uint32_t) (1 << 4),
-  AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA       = 0x80000000,
+  AUDIO_DATA_FORMAT_TYPE_I_RAW_DATA       = 0x80000000u,
 } audio_data_format_type_I_t;
 
 /// All remaining definitions are taken from the descriptor descriptions in the UAC2 main specification
@@ -640,7 +640,7 @@ typedef enum
   AUDIO_CHANNEL_CONFIG_BOTTOM_CENTER              = 0x01000000,
   AUDIO_CHANNEL_CONFIG_BACK_LEFT_OF_CENTER        = 0x02000000,
   AUDIO_CHANNEL_CONFIG_BACK_RIGHT_OF_CENTER       = 0x04000000,
-  AUDIO_CHANNEL_CONFIG_RAW_DATA                   = 0x80000000,
+  AUDIO_CHANNEL_CONFIG_RAW_DATA                   = 0x80000000u,
 } audio_channel_config_t;
 
 /// AUDIO Channel Cluster Descriptor (4.1)
diff --git a/src/class/audio/audio_device.c b/src/class/audio/audio_device.c
index 9050b462a..ed380424f 100644
--- a/src/class/audio/audio_device.c
+++ b/src/class/audio/audio_device.c
@@ -304,19 +304,6 @@ typedef struct
 
   uint16_t desc_length;         // Length of audio function descriptor
 
-  // Buffer for control requests
-  uint8_t * ctrl_buf;
-  uint8_t ctrl_buf_sz;
-
-  // Current active alternate settings
-  uint8_t * alt_setting;   // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
-
-  // EP Transfer buffers and FIFOs
-#if CFG_TUD_AUDIO_ENABLE_EP_OUT
-#if !CFG_TUD_AUDIO_ENABLE_DECODING
-  tu_fifo_t ep_out_ff;
-#endif
-
 #if CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
   struct {
     CFG_TUSB_MEM_ALIGN uint32_t value;  // Feedback value for asynchronous mode (in 16.16 format).
@@ -346,17 +333,6 @@ typedef struct
   } feedback;
 #endif // CFG_TUD_AUDIO_ENABLE_FEEDBACK_EP
 
-#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
-
-#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
-  tu_fifo_t ep_in_ff;
-#endif
-
-  // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74)
-#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
-  CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[6];
-#endif
-
   // Decoding parameters - parameters are set when alternate AS interface is set by host
   // Coding is currently only supported for EP. Software coding corresponding to AS interfaces without EPs are not supported currently.
 #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
@@ -365,8 +341,7 @@ typedef struct
 
 #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
   audio_data_format_type_I_t format_type_I_rx;
-  uint8_t n_bytes_per_sampe_rx;
-  uint8_t n_channels_per_ff_rx;
+  uint8_t n_bytes_per_sample_rx;
   uint8_t n_ff_used_rx;
 #endif
 #endif
@@ -382,26 +357,57 @@ typedef struct
 #if CFG_TUD_AUDIO_ENABLE_EP_IN && (CFG_TUD_AUDIO_ENABLE_ENCODING || CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL)
   audio_format_type_t format_type_tx;
   uint8_t n_channels_tx;
-  uint8_t n_bytes_per_sampe_tx;
+  uint8_t n_bytes_per_sample_tx;
 
 #if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
   audio_data_format_type_I_t format_type_I_tx;
-  uint8_t n_channels_per_ff_tx;
   uint8_t n_ff_used_tx;
 #endif
 #endif
 
+  // Buffer for control requests
+  uint8_t * ctrl_buf;
+  uint8_t ctrl_buf_sz;
+
+  // Current active alternate settings
+  uint8_t * alt_setting;   // We need to save the current alternate setting this way, because it is possible that there are AS interfaces which do not have an EP!
+
+  // EP Transfer buffers and FIFOs
+#if CFG_TUD_AUDIO_ENABLE_EP_OUT
+#if !CFG_TUD_AUDIO_ENABLE_DECODING
+  tu_fifo_t ep_out_ff;
+#endif
+
+
+#endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
+
+#if CFG_TUD_AUDIO_ENABLE_EP_IN && !CFG_TUD_AUDIO_ENABLE_ENCODING
+  tu_fifo_t ep_in_ff;
+#endif
+
+  // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2 specification (p. 74)
+#if CFG_TUD_AUDIO_ENABLE_INTERRUPT_EP
+  CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[6];
+#endif
+
+
   // Support FIFOs for software encoding and decoding
 #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
   tu_fifo_t * rx_supp_ff;
   uint8_t n_rx_supp_ff;
   uint16_t rx_supp_ff_sz_max;
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
+  uint8_t n_channels_per_ff_rx;
+#endif
 #endif
 
 #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_ENABLE_ENCODING
   tu_fifo_t * tx_supp_ff;
   uint8_t n_tx_supp_ff;
   uint16_t tx_supp_ff_sz_max;
+#if CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
+  uint8_t n_channels_per_ff_tx;
+#endif
 #endif
 
   // Linear buffer in case target MCU is not capable of handling a ring buffer FIFO e.g. no hardware buffer is available or driver is would need to be changed dramatically OR the support FIFOs are used
@@ -707,16 +713,16 @@ static bool audiod_decode_type_I_pcm(uint8_t rhport, audiod_function_t* audio, u
     if (info.len_lin != 0)
     {
       info.len_lin = tu_min16(nBytesPerFFToRead, info.len_lin);
-      src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sampe_rx];
+      src = &audio->lin_buf_out[cnt_ff*audio->n_channels_per_ff_rx * audio->n_bytes_per_sample_rx];
       dst_end = info.ptr_lin + info.len_lin;
-      src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_lin, dst_end, src, n_ff_used);
+      src = audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_rx, info.ptr_lin, dst_end, src, n_ff_used);
 
       // Handle wrapped part of FIFO
       info.len_wrap = tu_min16(nBytesPerFFToRead - info.len_lin, info.len_wrap);
       if (info.len_wrap != 0)
       {
         dst_end = info.ptr_wrap + info.len_wrap;
-        audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sampe_rx, info.ptr_wrap, dst_end, src, n_ff_used);
+        audiod_interleaved_copy_bytes_fast_decode(audio->n_bytes_per_sample_rx, info.ptr_wrap, dst_end, src, n_ff_used);
       }
       tu_fifo_advance_write_pointer(&audio->rx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
     }
@@ -1024,7 +1030,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
   // Limit to maximum sample number - THIS IS A POSSIBLE ERROR SOURCE IF TOO MANY SAMPLE WOULD NEED TO BE SENT BUT CAN NOT!
   nBytesPerFFToSend = tu_min16(nBytesPerFFToSend, audio->ep_in_sz / n_ff_used);
   // Round to full number of samples (flooring)
-  uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx;
+  uint16_t const nSlotSize = audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx;
   nBytesPerFFToSend = (nBytesPerFFToSend / nSlotSize) * nSlotSize;
 #endif
 
@@ -1036,7 +1042,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
 
   for (cnt_ff = 0; cnt_ff < n_ff_used; cnt_ff++)
   {
-    dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sampe_tx];
+    dst = &audio->lin_buf_in[cnt_ff*audio->n_channels_per_ff_tx*audio->n_bytes_per_sample_tx];
 
     tu_fifo_get_read_info(&audio->tx_supp_ff[cnt_ff], &info);
 
@@ -1044,7 +1050,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
     {
       info.len_lin = tu_min16(nBytesPerFFToSend, info.len_lin);       // Limit up to desired length
       src_end = (uint8_t *)info.ptr_lin + info.len_lin;
-      dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_lin, src_end, dst, n_ff_used);
+      dst = audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_tx, info.ptr_lin, src_end, dst, n_ff_used);
 
       // Limit up to desired length
       info.len_wrap = tu_min16(nBytesPerFFToSend - info.len_lin, info.len_wrap);
@@ -1053,7 +1059,7 @@ static uint16_t audiod_encode_type_I_pcm(uint8_t rhport, audiod_function_t* audi
       if (info.len_wrap != 0)
       {
         src_end = (uint8_t *)info.ptr_wrap + info.len_wrap;
-        audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sampe_tx, info.ptr_wrap, src_end, dst, n_ff_used);
+        audiod_interleaved_copy_bytes_fast_encode(audio->n_bytes_per_sample_tx, info.ptr_wrap, src_end, dst, n_ff_used);
       }
 
       tu_fifo_advance_read_pointer(&audio->tx_supp_ff[cnt_ff], info.len_lin + info.len_wrap);
@@ -1490,7 +1496,8 @@ uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uin
   #endif
         uint8_t const *p_desc = _audiod_fct[i].p_desc;
         uint8_t const *p_desc_end = p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
-        while (p_desc < p_desc_end)
+        // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+        while (p_desc_end - p_desc > 0)
         {
           if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
           {
@@ -1740,7 +1747,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
   uint8_t const *p_desc_end = audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
 
   // p_desc starts at required interface with alternate setting zero
-  while (p_desc < p_desc_end)
+  // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+  while (p_desc_end - p_desc > 0)
   {
     // Find correct interface
     if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bInterfaceNumber == itf && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == alt)
@@ -1750,7 +1758,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
 #endif
       // From this point forward follow the EP descriptors associated to the current alternate setting interface - Open EPs if necessary
       uint8_t foundEPs = 0, nEps = ((tusb_desc_interface_t const * )p_desc)->bNumEndpoints;
-      while (foundEPs < nEps && p_desc < p_desc_end)
+      // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+      while (foundEPs < nEps && (p_desc_end - p_desc > 0))
       {
         if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT)
         {
@@ -1779,8 +1788,8 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
 
             // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
     #if CFG_TUD_AUDIO_ENABLE_ENCODING && CFG_TUD_AUDIO_ENABLE_TYPE_I_ENCODING
-            const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / (audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx))
-               * (audio->n_channels_per_ff_tx * audio->n_bytes_per_sampe_tx));
+            const uint16_t active_fifo_depth = (uint16_t) ((audio->tx_supp_ff_sz_max / (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx))
+               * (audio->n_channels_per_ff_tx * audio->n_bytes_per_sample_tx));
             for (uint8_t cnt = 0; cnt < audio->n_tx_supp_ff; cnt++)
             {
               tu_fifo_config(&audio->tx_supp_ff[cnt], audio->tx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
@@ -1810,7 +1819,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
 
             // Reconfigure size of support FIFOs - this is necessary to avoid samples to get split in case of a wrap
     #if CFG_TUD_AUDIO_ENABLE_TYPE_I_DECODING
-            const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sampe_rx) * audio->n_bytes_per_sampe_rx;
+            const uint16_t active_fifo_depth = (audio->rx_supp_ff_sz_max / audio->n_bytes_per_sample_rx) * audio->n_bytes_per_sample_rx;
             for (uint8_t cnt = 0; cnt < audio->n_rx_supp_ff; cnt++)
             {
               tu_fifo_config(&audio->rx_supp_ff[cnt], audio->rx_supp_ff[cnt].buffer, active_fifo_depth, 1, true);
@@ -1835,7 +1844,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
             audio->feedback.frame_shift = desc_ep->bInterval -1;
 
             // Enable SOF interrupt if callback is implemented
-            if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, true);
+            if (tud_audio_feedback_interval_isr) usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, true);
           }
   #endif
 #endif // CFG_TUD_AUDIO_ENABLE_EP_OUT
@@ -1909,7 +1918,7 @@ static bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *
       break;
     }
   }
-  if (disable) usbd_sof_enable(rhport, false);
+  if (disable) usbd_sof_enable(rhport, SOF_CONSUMER_AUDIO, false);
 #endif
 
 #if CFG_TUD_AUDIO_ENABLE_EP_IN && CFG_TUD_AUDIO_EP_IN_FLOW_CONTROL
@@ -2015,7 +2024,10 @@ static bool audiod_control_request(uint8_t rhport, tusb_control_request_t const
       case TUSB_REQ_SET_INTERFACE:
         return audiod_set_interface(rhport, p_request);
 
-        // Unknown/Unsupported request
+      case TUSB_REQ_CLEAR_FEATURE:
+        return true;
+
+      // Unknown/Unsupported request
       default: TU_BREAKPOINT(); return false;
     }
   }
@@ -2391,7 +2403,8 @@ static bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t * audio
     p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
 
     uint8_t tmp = 0;
-    while (p_desc < p_desc_end)
+    // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+    while (p_desc_end - p_desc > 0)
     {
       // We assume the number of alternate settings is increasing thus we return the index of alternate setting zero!
       if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const * )p_desc)->bAlternateSetting == 0)
@@ -2444,7 +2457,8 @@ static bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *
       uint8_t const *p_desc_end = ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength + p_desc;
       p_desc = tu_desc_next(p_desc);                                                                            // Get past CS AC descriptor
 
-      while (p_desc < p_desc_end)
+      // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+      while (p_desc_end - p_desc > 0)
       {
         if (p_desc[3] == entityID)  // Entity IDs are always at offset 3
         {
@@ -2468,8 +2482,8 @@ static bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id)
       // Get pointer at beginning and end
       uint8_t const *p_desc = _audiod_fct[i].p_desc;
       uint8_t const *p_desc_end = _audiod_fct[i].p_desc + _audiod_fct[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
-
-      while (p_desc < p_desc_end)
+      // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+      while (p_desc_end - p_desc > 0)
       {
         if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && ((tusb_desc_interface_t const *)_audiod_fct[i].p_desc)->bInterfaceNumber == itf)
         {
@@ -2497,7 +2511,8 @@ static bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
       uint8_t const *p_desc = tu_desc_next(_audiod_fct[i].p_desc);
       p_desc += ((audio_desc_cs_ac_interface_t const *)p_desc)->wTotalLength;
 
-      while (p_desc < p_desc_end)
+      // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+      while (p_desc_end - p_desc > 0)
       {
         if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT && ((tusb_desc_endpoint_t const * )p_desc)->bEndpointAddress == ep)
         {
@@ -2528,8 +2543,8 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
 #endif
 
   p_desc = tu_desc_next(p_desc);    // Exclude standard AS interface descriptor of current alternate interface descriptor
-
-  while (p_desc < p_desc_end)
+  // Condition modified from p_desc < p_desc_end to prevent gcc>=12 strict-overflow warning
+  while (p_desc_end - p_desc > 0)
   {
     // Abort if follow up descriptor is a new standard interface descriptor - indicates the last AS descriptor was already finished
     if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) break;
@@ -2578,14 +2593,14 @@ static void audiod_parse_for_AS_params(audiod_function_t* audio, uint8_t const *
 #if CFG_TUD_AUDIO_ENABLE_EP_IN
       if (as_itf == audio->ep_in_as_intf_num)
       {
-        audio->n_bytes_per_sampe_tx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+        audio->n_bytes_per_sample_tx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
       }
 #endif
 
 #if CFG_TUD_AUDIO_ENABLE_EP_OUT && CFG_TUD_AUDIO_ENABLE_DECODING
       if (as_itf == audio->ep_out_as_intf_num)
       {
-        audio->n_bytes_per_sampe_rx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
+        audio->n_bytes_per_sample_rx = ((audio_desc_type_I_format_t const * )p_desc)->bSubslotSize;
       }
 #endif
     }
@@ -2604,7 +2619,7 @@ static bool audiod_calc_tx_packet_sz(audiod_function_t* audio)
 {
   TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I);
   TU_VERIFY(audio->n_channels_tx);
-  TU_VERIFY(audio->n_bytes_per_sampe_tx);
+  TU_VERIFY(audio->n_bytes_per_sample_tx);
   TU_VERIFY(audio->interval_tx);
   TU_VERIFY(audio->sample_rate_tx);
 
@@ -2613,9 +2628,9 @@ static bool audiod_calc_tx_packet_sz(audiod_function_t* audio)
   const uint16_t sample_normimal = (uint16_t)(audio->sample_rate_tx * interval / ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
   const uint16_t sample_reminder = (uint16_t)(audio->sample_rate_tx * interval % ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
 
-  const uint16_t packet_sz_tx_min = (uint16_t)((sample_normimal - 1) * audio->n_channels_tx * audio->n_bytes_per_sampe_tx);
-  const uint16_t packet_sz_tx_norm = (uint16_t)(sample_normimal * audio->n_channels_tx * audio->n_bytes_per_sampe_tx);
-  const uint16_t packet_sz_tx_max = (uint16_t)((sample_normimal + 1) * audio->n_channels_tx * audio->n_bytes_per_sampe_tx);
+  const uint16_t packet_sz_tx_min = (uint16_t)((sample_normimal - 1) * audio->n_channels_tx * audio->n_bytes_per_sample_tx);
+  const uint16_t packet_sz_tx_norm = (uint16_t)(sample_normimal * audio->n_channels_tx * audio->n_bytes_per_sample_tx);
+  const uint16_t packet_sz_tx_max = (uint16_t)((sample_normimal + 1) * audio->n_channels_tx * audio->n_bytes_per_sample_tx);
 
   // Endpoint size must larger than packet size
   TU_ASSERT(packet_sz_tx_max <= audio->ep_in_sz);
diff --git a/src/class/cdc/cdc_device.c b/src/class/cdc/cdc_device.c
index 7ef0530ec..ebc408f5a 100644
--- a/src/class/cdc/cdc_device.c
+++ b/src/class/cdc/cdc_device.c
@@ -43,13 +43,9 @@
 //--------------------------------------------------------------------+
 // MACRO CONSTANT TYPEDEF
 //--------------------------------------------------------------------+
-enum
-{
-  BULK_PACKET_SIZE = (TUD_OPT_HIGH_SPEED ? 512 : 64)
-};
+#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
 
-typedef struct
-{
+typedef struct {
   uint8_t itf_num;
   uint8_t ep_notif;
   uint8_t ep_in;
@@ -59,7 +55,7 @@ typedef struct
   uint8_t line_state;
 
   /*------------- From this point, data is not cleared by bus reset -------------*/
-  char    wanted_char;
+  char wanted_char;
   TU_ATTR_ALIGNED(4) cdc_line_coding_t line_coding;
 
   // FIFO
@@ -75,19 +71,22 @@ typedef struct
   // Endpoint Transfer buffer
   CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_CDC_EP_BUFSIZE];
   CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_CDC_EP_BUFSIZE];
-
-}cdcd_interface_t;
+} cdcd_interface_t;
 
 #define ITF_MEM_RESET_SIZE   offsetof(cdcd_interface_t, wanted_char)
 
 //--------------------------------------------------------------------+
 // INTERNAL OBJECT & FUNCTION DECLARATION
 //--------------------------------------------------------------------+
-CFG_TUD_MEM_SECTION tu_static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+CFG_TUD_MEM_SECTION static cdcd_interface_t _cdcd_itf[CFG_TUD_CDC];
+static tud_cdc_configure_fifo_t _cdcd_fifo_cfg;
 
-static bool _prep_out_transaction (cdcd_interface_t* p_cdc)
-{
+static bool _prep_out_transaction (cdcd_interface_t* p_cdc) {
   uint8_t const rhport = 0;
+
+  // Skip if usb is not ready yet
+  TU_VERIFY(tud_ready() && p_cdc->ep_out);
+
   uint16_t available = tu_fifo_remaining(&p_cdc->rx_ff);
 
   // Prepare for incoming data but only allow what we can store in the ring buffer.
@@ -102,14 +101,11 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc)
   // fifo can be changed before endpoint is claimed
   available = tu_fifo_remaining(&p_cdc->rx_ff);
 
-  if ( available >= sizeof(p_cdc->epout_buf) )
-  {
+  if ( available >= sizeof(p_cdc->epout_buf) ) {
     return usbd_edpt_xfer(rhport, p_cdc->ep_out, p_cdc->epout_buf, sizeof(p_cdc->epout_buf));
-  }else
-  {
+  }else {
     // Release endpoint since we don't make any transfer
     usbd_edpt_release(rhport, p_cdc->ep_out);
-
     return false;
   }
 }
@@ -117,51 +113,53 @@ static bool _prep_out_transaction (cdcd_interface_t* p_cdc)
 //--------------------------------------------------------------------+
 // APPLICATION API
 //--------------------------------------------------------------------+
-bool tud_cdc_n_connected(uint8_t itf)
-{
+
+bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg) {
+  TU_VERIFY(cfg);
+  _cdcd_fifo_cfg = (*cfg);
+  return true;
+}
+
+bool tud_cdc_n_ready(uint8_t itf) {
+  return tud_ready() && _cdcd_itf[itf].ep_in != 0 && _cdcd_itf[itf].ep_out != 0;
+}
+
+bool tud_cdc_n_connected(uint8_t itf) {
   // DTR (bit 0) active  is considered as connected
   return tud_ready() && tu_bit_test(_cdcd_itf[itf].line_state, 0);
 }
 
-uint8_t tud_cdc_n_get_line_state (uint8_t itf)
-{
+uint8_t tud_cdc_n_get_line_state(uint8_t itf) {
   return _cdcd_itf[itf].line_state;
 }
 
-void tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding)
-{
+void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding) {
   (*coding) = _cdcd_itf[itf].line_coding;
 }
 
-void tud_cdc_n_set_wanted_char (uint8_t itf, char wanted)
-{
+void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted) {
   _cdcd_itf[itf].wanted_char = wanted;
 }
 
-
 //--------------------------------------------------------------------+
 // READ API
 //--------------------------------------------------------------------+
-uint32_t tud_cdc_n_available(uint8_t itf)
-{
+uint32_t tud_cdc_n_available(uint8_t itf) {
   return tu_fifo_count(&_cdcd_itf[itf].rx_ff);
 }
 
-uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize)
-{
+uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize) {
   cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
   uint32_t num_read = tu_fifo_read_n(&p_cdc->rx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
   _prep_out_transaction(p_cdc);
   return num_read;
 }
 
-bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr)
-{
+bool tud_cdc_n_peek(uint8_t itf, uint8_t* chr) {
   return tu_fifo_peek(&_cdcd_itf[itf].rx_ff, chr);
 }
 
-void tud_cdc_n_read_flush (uint8_t itf)
-{
+void tud_cdc_n_read_flush(uint8_t itf) {
   cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
   tu_fifo_clear(&p_cdc->rx_ff);
   _prep_out_transaction(p_cdc);
@@ -170,45 +168,43 @@ void tud_cdc_n_read_flush (uint8_t itf)
 //--------------------------------------------------------------------+
 // WRITE API
 //--------------------------------------------------------------------+
-uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize)
-{
+uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize) {
   cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
   uint16_t ret = tu_fifo_write_n(&p_cdc->tx_ff, buffer, (uint16_t) TU_MIN(bufsize, UINT16_MAX));
 
   // flush if queue more than packet size
-  // may need to suppress -Wunreachable-code since most of the time CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
-  if ( (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE) || ((CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE) && tu_fifo_full(&p_cdc->tx_ff)) )
-  {
+  if (tu_fifo_count(&p_cdc->tx_ff) >= BULK_PACKET_SIZE
+      #if CFG_TUD_CDC_TX_BUFSIZE < BULK_PACKET_SIZE
+      || tu_fifo_full(&p_cdc->tx_ff) // check full if fifo size is less than packet size
+      #endif
+      ) {
     tud_cdc_n_write_flush(itf);
   }
 
   return ret;
 }
 
-uint32_t tud_cdc_n_write_flush (uint8_t itf)
-{
+uint32_t tud_cdc_n_write_flush(uint8_t itf) {
   cdcd_interface_t* p_cdc = &_cdcd_itf[itf];
 
   // Skip if usb is not ready yet
-  TU_VERIFY( tud_ready(), 0 );
+  TU_VERIFY(tud_ready(), 0);
 
   // No data to send
-  if ( !tu_fifo_count(&p_cdc->tx_ff) ) return 0;
+  if (!tu_fifo_count(&p_cdc->tx_ff)) return 0;
 
   uint8_t const rhport = 0;
 
   // Claim the endpoint
-  TU_VERIFY( usbd_edpt_claim(rhport, p_cdc->ep_in), 0 );
+  TU_VERIFY(usbd_edpt_claim(rhport, p_cdc->ep_in), 0);
 
   // Pull data from FIFO
   uint16_t const count = tu_fifo_read_n(&p_cdc->tx_ff, p_cdc->epin_buf, sizeof(p_cdc->epin_buf));
 
-  if ( count )
-  {
-    TU_ASSERT( usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0 );
+  if (count) {
+    TU_ASSERT(usbd_edpt_xfer(rhport, p_cdc->ep_in, p_cdc->epin_buf, count), 0);
     return count;
-  }else
-  {
+  } else {
     // Release endpoint since we don't make any transfer
     // Note: data is dropped if terminal is not connected
     usbd_edpt_release(rhport, p_cdc->ep_in);
@@ -216,33 +212,30 @@ uint32_t tud_cdc_n_write_flush (uint8_t itf)
   }
 }
 
-uint32_t tud_cdc_n_write_available (uint8_t itf)
-{
+uint32_t tud_cdc_n_write_available(uint8_t itf) {
   return tu_fifo_remaining(&_cdcd_itf[itf].tx_ff);
 }
 
-bool tud_cdc_n_write_clear (uint8_t itf)
-{
+bool tud_cdc_n_write_clear(uint8_t itf) {
   return tu_fifo_clear(&_cdcd_itf[itf].tx_ff);
 }
 
 //--------------------------------------------------------------------+
 // USBD Driver API
 //--------------------------------------------------------------------+
-void cdcd_init(void)
-{
+void cdcd_init(void) {
   tu_memclr(_cdcd_itf, sizeof(_cdcd_itf));
+  tu_memclr(&_cdcd_fifo_cfg, sizeof(_cdcd_fifo_cfg));
 
-  for(uint8_t i=0; iwanted_char = (char) -1;
 
     // default line coding is : stop bit = 1, parity = none, data bits = 8
-    p_cdc->line_coding.bit_rate  = 115200;
+    p_cdc->line_coding.bit_rate = 115200;
     p_cdc->line_coding.stop_bits = 0;
-    p_cdc->line_coding.parity    = 0;
+    p_cdc->line_coding.parity = 0;
     p_cdc->line_coding.data_bits = 8;
 
     // Config RX fifo
@@ -286,33 +279,28 @@ bool cdcd_deinit(void) {
   return true;
 }
 
-void cdcd_reset(uint8_t rhport)
-{
+void cdcd_reset(uint8_t rhport) {
   (void) rhport;
 
-  for(uint8_t i=0; irx_ff);
-    tu_fifo_clear(&p_cdc->tx_ff);
+    if (!_cdcd_fifo_cfg.rx_persistent) tu_fifo_clear(&p_cdc->rx_ff);
+    if (!_cdcd_fifo_cfg.tx_persistent) tu_fifo_clear(&p_cdc->tx_ff);
     tu_fifo_set_overwritable(&p_cdc->tx_ff, true);
   }
 }
 
-uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
+uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len) {
   // Only support ACM subclass
   TU_VERIFY( TUSB_CLASS_CDC                           == itf_desc->bInterfaceClass &&
              CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL == itf_desc->bInterfaceSubClass, 0);
 
   // Find available interface
-  cdcd_interface_t * p_cdc = NULL;
-  for(uint8_t cdc_id=0; cdc_iditf_num = itf_desc->bInterfaceNumber;
 
   uint16_t drv_len = sizeof(tusb_desc_interface_t);
-  uint8_t const * p_desc = tu_desc_next( itf_desc );
+  uint8_t const* p_desc = tu_desc_next(itf_desc);
 
   // Communication Functional Descriptors
-  while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
-  {
+  while (TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len) {
     drv_len += tu_desc_len(p_desc);
-    p_desc   = tu_desc_next(p_desc);
+    p_desc = tu_desc_next(p_desc);
   }
 
-  if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
-  {
+  if (TUSB_DESC_ENDPOINT == tu_desc_type(p_desc)) {
     // notification endpoint
-    tusb_desc_endpoint_t const * desc_ep = (tusb_desc_endpoint_t const *) p_desc;
+    tusb_desc_endpoint_t const* desc_ep = (tusb_desc_endpoint_t const*) p_desc;
 
-    TU_ASSERT( usbd_edpt_open(rhport, desc_ep), 0 );
+    TU_ASSERT(usbd_edpt_open(rhport, desc_ep), 0);
     p_cdc->ep_notif = desc_ep->bEndpointAddress;
 
     drv_len += tu_desc_len(p_desc);
-    p_desc   = tu_desc_next(p_desc);
+    p_desc = tu_desc_next(p_desc);
   }
 
   //------------- Data Interface (if any) -------------//
-  if ( (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
-       (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const *) p_desc)->bInterfaceClass) )
-  {
+  if ((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) &&
+      (TUSB_CLASS_CDC_DATA == ((tusb_desc_interface_t const*) p_desc)->bInterfaceClass)) {
     // next to endpoint descriptor
     drv_len += tu_desc_len(p_desc);
-    p_desc   = tu_desc_next(p_desc);
+    p_desc = tu_desc_next(p_desc);
 
     // Open endpoint pair
-    TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0 );
+    TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &p_cdc->ep_out, &p_cdc->ep_in), 0);
 
-    drv_len += 2*sizeof(tusb_desc_endpoint_t);
+    drv_len += 2 * sizeof(tusb_desc_endpoint_t);
   }
 
   // Prepare for incoming data
@@ -367,8 +352,7 @@ uint16_t cdcd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint1
 // Invoked when a control transfer occurred on an interface of this class
 // Driver response accordingly to the request and the transfer stage (setup/data/ack)
 // return false to stall control endpoint (e.g unsupported request)
-bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
+bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
   // Handle class request only
   TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
 
@@ -376,42 +360,33 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
   cdcd_interface_t* p_cdc = _cdcd_itf;
 
   // Identify which interface to use
-  for ( ; ; itf++, p_cdc++)
-  {
+  for (;; itf++, p_cdc++) {
     if (itf >= TU_ARRAY_SIZE(_cdcd_itf)) return false;
 
-    if ( p_cdc->itf_num == request->wIndex ) break;
+    if (p_cdc->itf_num == request->wIndex) break;
   }
 
-  switch ( request->bRequest )
-  {
+  switch (request->bRequest) {
     case CDC_REQUEST_SET_LINE_CODING:
-      if (stage == CONTROL_STAGE_SETUP)
-      {
+      if (stage == CONTROL_STAGE_SETUP) {
         TU_LOG_DRV("  Set Line Coding\r\n");
         tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
+      } else if (stage == CONTROL_STAGE_ACK) {
+        if (tud_cdc_line_coding_cb) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
       }
-      else if ( stage == CONTROL_STAGE_ACK)
-      {
-        if ( tud_cdc_line_coding_cb ) tud_cdc_line_coding_cb(itf, &p_cdc->line_coding);
-      }
-    break;
+      break;
 
     case CDC_REQUEST_GET_LINE_CODING:
-      if (stage == CONTROL_STAGE_SETUP)
-      {
+      if (stage == CONTROL_STAGE_SETUP) {
         TU_LOG_DRV("  Get Line Coding\r\n");
         tud_control_xfer(rhport, request, &p_cdc->line_coding, sizeof(cdc_line_coding_t));
       }
-    break;
+      break;
 
     case CDC_REQUEST_SET_CONTROL_LINE_STATE:
-      if (stage == CONTROL_STAGE_SETUP)
-      {
+      if (stage == CONTROL_STAGE_SETUP) {
         tud_control_status(rhport, request);
-      }
-      else if (stage == CONTROL_STAGE_ACK)
-      {
+      } else if (stage == CONTROL_STAGE_ACK) {
         // CDC PSTN v1.2 section 6.3.12
         // Bit 0: Indicates if DTE is present or not.
         //        This signal corresponds to V.24 signal 108/2 and RS-232 signal DTR (Data Terminal Ready)
@@ -428,61 +403,54 @@ bool cdcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t
         TU_LOG_DRV("  Set Control Line State: DTR = %d, RTS = %d\r\n", dtr, rts);
 
         // Invoke callback
-        if ( tud_cdc_line_state_cb ) tud_cdc_line_state_cb(itf, dtr, rts);
+        if (tud_cdc_line_state_cb) tud_cdc_line_state_cb(itf, dtr, rts);
       }
-    break;
-    case CDC_REQUEST_SEND_BREAK:
-      if (stage == CONTROL_STAGE_SETUP)
-      {
-        tud_control_status(rhport, request);
-      }
-      else if (stage == CONTROL_STAGE_ACK)
-      {
-        TU_LOG_DRV("  Send Break\r\n");
-        if ( tud_cdc_send_break_cb ) tud_cdc_send_break_cb(itf, request->wValue);
-      }
-    break;
+      break;
 
-    default: return false; // stall unsupported request
+    case CDC_REQUEST_SEND_BREAK:
+      if (stage == CONTROL_STAGE_SETUP) {
+        tud_control_status(rhport, request);
+      } else if (stage == CONTROL_STAGE_ACK) {
+        TU_LOG_DRV("  Send Break\r\n");
+        if (tud_cdc_send_break_cb) tud_cdc_send_break_cb(itf, request->wValue);
+      }
+      break;
+
+    default:
+      return false; // stall unsupported request
   }
 
   return true;
 }
 
-bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
+bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
   (void) result;
 
   uint8_t itf;
   cdcd_interface_t* p_cdc;
 
   // Identify which interface to use
-  for (itf = 0; itf < CFG_TUD_CDC; itf++)
-  {
+  for (itf = 0; itf < CFG_TUD_CDC; itf++) {
     p_cdc = &_cdcd_itf[itf];
-    if ( ( ep_addr == p_cdc->ep_out ) || ( ep_addr == p_cdc->ep_in ) ) break;
+    if ((ep_addr == p_cdc->ep_out) || (ep_addr == p_cdc->ep_in)) break;
   }
   TU_ASSERT(itf < CFG_TUD_CDC);
 
   // Received new data
-  if ( ep_addr == p_cdc->ep_out )
-  {
+  if (ep_addr == p_cdc->ep_out) {
     tu_fifo_write_n(&p_cdc->rx_ff, p_cdc->epout_buf, (uint16_t) xferred_bytes);
 
     // Check for wanted char and invoke callback if needed
-    if ( tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1) )
-    {
-      for ( uint32_t i = 0; i < xferred_bytes; i++ )
-      {
-        if ( (p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff) )
-        {
+    if (tud_cdc_rx_wanted_cb && (((signed char) p_cdc->wanted_char) != -1)) {
+      for (uint32_t i = 0; i < xferred_bytes; i++) {
+        if ((p_cdc->wanted_char == p_cdc->epout_buf[i]) && !tu_fifo_empty(&p_cdc->rx_ff)) {
           tud_cdc_rx_wanted_cb(itf, p_cdc->wanted_char);
         }
       }
     }
 
     // invoke receive callback (if there is still data)
-    if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff) ) tud_cdc_rx_cb(itf);
+    if (tud_cdc_rx_cb && !tu_fifo_empty(&p_cdc->rx_ff)) tud_cdc_rx_cb(itf);
 
     // prepare for OUT transaction
     _prep_out_transaction(p_cdc);
@@ -491,19 +459,15 @@ bool cdcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_
   // Data sent to host, we continue to fetch from tx fifo to send.
   // Note: This will cause incorrect baudrate set in line coding.
   //       Though maybe the baudrate is not really important !!!
-  if ( ep_addr == p_cdc->ep_in )
-  {
+  if (ep_addr == p_cdc->ep_in) {
     // invoke transmit callback to possibly refill tx fifo
-    if ( tud_cdc_tx_complete_cb ) tud_cdc_tx_complete_cb(itf);
+    if (tud_cdc_tx_complete_cb) tud_cdc_tx_complete_cb(itf);
 
-    if ( 0 == tud_cdc_n_write_flush(itf) )
-    {
+    if (0 == tud_cdc_n_write_flush(itf)) {
       // If there is no data left, a ZLP should be sent if
       // xferred_bytes is multiple of EP Packet size and not zero
-      if ( !tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) )
-      {
-        if ( usbd_edpt_claim(rhport, p_cdc->ep_in) )
-        {
+      if (!tu_fifo_count(&p_cdc->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE - 1)))) {
+        if (usbd_edpt_claim(rhport, p_cdc->ep_in)) {
           usbd_edpt_xfer(rhport, p_cdc->ep_in, NULL, 0);
         }
       }
diff --git a/src/class/cdc/cdc_device.h b/src/class/cdc/cdc_device.h
index 20e908451..3ad7c8baf 100644
--- a/src/class/cdc/cdc_device.h
+++ b/src/class/cdc/cdc_device.h
@@ -24,8 +24,8 @@
  * This file is part of the TinyUSB stack.
  */
 
-#ifndef _TUSB_CDC_DEVICE_H_
-#define _TUSB_CDC_DEVICE_H_
+#ifndef TUSB_CDC_DEVICE_H_
+#define TUSB_CDC_DEVICE_H_
 
 #include "cdc.h"
 
@@ -45,84 +45,144 @@
  extern "C" {
 #endif
 
-/** \addtogroup CDC_Serial Serial
- *  @{
- *  \defgroup   CDC_Serial_Device Device
- *  @{ */
+//--------------------------------------------------------------------+
+// Driver Configuration
+//--------------------------------------------------------------------+
+
+typedef struct TU_ATTR_PACKED {
+  uint8_t rx_persistent : 1; // keep rx fifo on bus reset or disconnect
+  uint8_t tx_persistent : 1; // keep tx fifo on bus reset or disconnect
+} tud_cdc_configure_fifo_t;
+
+// Configure CDC FIFOs behavior
+bool tud_cdc_configure_fifo(tud_cdc_configure_fifo_t const* cfg);
 
 //--------------------------------------------------------------------+
-// Application API (Multiple Ports)
-// CFG_TUD_CDC > 1
+// Application API (Multiple Ports) i.e. CFG_TUD_CDC > 1
 //--------------------------------------------------------------------+
 
+// Check if interface is ready
+bool tud_cdc_n_ready(uint8_t itf);
+
 // Check if terminal is connected to this port
-bool     tud_cdc_n_connected       (uint8_t itf);
+bool tud_cdc_n_connected(uint8_t itf);
 
 // Get current line state. Bit 0:  DTR (Data Terminal Ready), Bit 1: RTS (Request to Send)
-uint8_t  tud_cdc_n_get_line_state  (uint8_t itf);
+uint8_t tud_cdc_n_get_line_state(uint8_t itf);
 
 // Get current line encoding: bit rate, stop bits parity etc ..
-void     tud_cdc_n_get_line_coding (uint8_t itf, cdc_line_coding_t* coding);
+void tud_cdc_n_get_line_coding(uint8_t itf, cdc_line_coding_t* coding);
 
 // Set special character that will trigger tud_cdc_rx_wanted_cb() callback on receiving
-void     tud_cdc_n_set_wanted_char (uint8_t itf, char wanted);
+void tud_cdc_n_set_wanted_char(uint8_t itf, char wanted);
 
 // Get the number of bytes available for reading
-uint32_t tud_cdc_n_available       (uint8_t itf);
+uint32_t tud_cdc_n_available(uint8_t itf);
 
 // Read received bytes
-uint32_t tud_cdc_n_read            (uint8_t itf, void* buffer, uint32_t bufsize);
+uint32_t tud_cdc_n_read(uint8_t itf, void* buffer, uint32_t bufsize);
 
 // Read a byte, return -1 if there is none
-static inline
-int32_t  tud_cdc_n_read_char       (uint8_t itf);
+TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_n_read_char(uint8_t itf) {
+  uint8_t ch;
+  return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1;
+}
 
 // Clear the received FIFO
-void     tud_cdc_n_read_flush      (uint8_t itf);
+void tud_cdc_n_read_flush(uint8_t itf);
 
 // Get a byte from FIFO without removing it
-bool     tud_cdc_n_peek            (uint8_t itf, uint8_t* ui8);
+bool tud_cdc_n_peek(uint8_t itf, uint8_t* ui8);
 
 // Write bytes to TX FIFO, data may remain in the FIFO for a while
-uint32_t tud_cdc_n_write           (uint8_t itf, void const* buffer, uint32_t bufsize);
+uint32_t tud_cdc_n_write(uint8_t itf, void const* buffer, uint32_t bufsize);
 
 // Write a byte
-static inline
-uint32_t tud_cdc_n_write_char      (uint8_t itf, char ch);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch) {
+  return tud_cdc_n_write(itf, &ch, 1);
+}
 
 // Write a null-terminated string
-static inline
-uint32_t tud_cdc_n_write_str       (uint8_t itf, char const* str);
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_n_write_str(uint8_t itf, char const* str) {
+  return tud_cdc_n_write(itf, str, strlen(str));
+}
 
 // Force sending data if possible, return number of forced bytes
-uint32_t tud_cdc_n_write_flush     (uint8_t itf);
+uint32_t tud_cdc_n_write_flush(uint8_t itf);
 
 // Return the number of bytes (characters) available for writing to TX FIFO buffer in a single n_write operation.
-uint32_t tud_cdc_n_write_available (uint8_t itf);
+uint32_t tud_cdc_n_write_available(uint8_t itf);
 
 // Clear the transmit FIFO
-bool tud_cdc_n_write_clear (uint8_t itf);
+bool tud_cdc_n_write_clear(uint8_t itf);
 
 //--------------------------------------------------------------------+
 // Application API (Single Port)
 //--------------------------------------------------------------------+
-static inline bool     tud_cdc_connected       (void);
-static inline uint8_t  tud_cdc_get_line_state  (void);
-static inline void     tud_cdc_get_line_coding (cdc_line_coding_t* coding);
-static inline void     tud_cdc_set_wanted_char (char wanted);
 
-static inline uint32_t tud_cdc_available       (void);
-static inline int32_t  tud_cdc_read_char       (void);
-static inline uint32_t tud_cdc_read            (void* buffer, uint32_t bufsize);
-static inline void     tud_cdc_read_flush      (void);
-static inline bool     tud_cdc_peek            (uint8_t* ui8);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_ready(void) {
+  return tud_cdc_n_ready(0);
+}
 
-static inline uint32_t tud_cdc_write_char      (char ch);
-static inline uint32_t tud_cdc_write           (void const* buffer, uint32_t bufsize);
-static inline uint32_t tud_cdc_write_str       (char const* str);
-static inline uint32_t tud_cdc_write_flush     (void);
-static inline uint32_t tud_cdc_write_available (void);
-static inline bool     tud_cdc_write_clear     (void);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_connected(void) {
+  return tud_cdc_n_connected(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_cdc_get_line_state(void) {
+  return tud_cdc_n_get_line_state(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_get_line_coding(cdc_line_coding_t* coding) {
+  tud_cdc_n_get_line_coding(0, coding);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_set_wanted_char(char wanted) {
+  tud_cdc_n_set_wanted_char(0, wanted);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_available(void) {
+  return tud_cdc_n_available(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline int32_t tud_cdc_read_char(void) {
+  return tud_cdc_n_read_char(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_read(void* buffer, uint32_t bufsize) {
+  return tud_cdc_n_read(0, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void tud_cdc_read_flush(void) {
+  tud_cdc_n_read_flush(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_peek(uint8_t* ui8) {
+  return tud_cdc_n_peek(0, ui8);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_char(char ch) {
+  return tud_cdc_n_write_char(0, ch);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write(void const* buffer, uint32_t bufsize) {
+  return tud_cdc_n_write(0, buffer, bufsize);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_str(char const* str) {
+  return tud_cdc_n_write_str(0, str);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_flush(void) {
+  return tud_cdc_n_write_flush(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t tud_cdc_write_available(void) {
+  return tud_cdc_n_write_available(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_cdc_write_clear(void) {
+  return tud_cdc_n_write_clear(0);
+}
 
 //--------------------------------------------------------------------+
 // Application Callback API (weak is optional)
@@ -146,103 +206,6 @@ TU_ATTR_WEAK void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p
 // Invoked when received send break
 TU_ATTR_WEAK void tud_cdc_send_break_cb(uint8_t itf, uint16_t duration_ms);
 
-//--------------------------------------------------------------------+
-// Inline Functions
-//--------------------------------------------------------------------+
-static inline int32_t tud_cdc_n_read_char (uint8_t itf)
-{
-  uint8_t ch;
-  return tud_cdc_n_read(itf, &ch, 1) ? (int32_t) ch : -1;
-}
-
-static inline uint32_t tud_cdc_n_write_char(uint8_t itf, char ch)
-{
-  return tud_cdc_n_write(itf, &ch, 1);
-}
-
-static inline uint32_t tud_cdc_n_write_str (uint8_t itf, char const* str)
-{
-  return tud_cdc_n_write(itf, str, strlen(str));
-}
-
-static inline bool tud_cdc_connected (void)
-{
-  return tud_cdc_n_connected(0);
-}
-
-static inline uint8_t tud_cdc_get_line_state (void)
-{
-  return tud_cdc_n_get_line_state(0);
-}
-
-static inline void tud_cdc_get_line_coding (cdc_line_coding_t* coding)
-{
-  tud_cdc_n_get_line_coding(0, coding);
-}
-
-static inline void tud_cdc_set_wanted_char (char wanted)
-{
-  tud_cdc_n_set_wanted_char(0, wanted);
-}
-
-static inline uint32_t tud_cdc_available (void)
-{
-  return tud_cdc_n_available(0);
-}
-
-static inline int32_t tud_cdc_read_char (void)
-{
-  return tud_cdc_n_read_char(0);
-}
-
-static inline uint32_t tud_cdc_read (void* buffer, uint32_t bufsize)
-{
-  return tud_cdc_n_read(0, buffer, bufsize);
-}
-
-static inline void tud_cdc_read_flush (void)
-{
-  tud_cdc_n_read_flush(0);
-}
-
-static inline bool tud_cdc_peek (uint8_t* ui8)
-{
-  return tud_cdc_n_peek(0, ui8);
-}
-
-static inline uint32_t tud_cdc_write_char (char ch)
-{
-  return tud_cdc_n_write_char(0, ch);
-}
-
-static inline uint32_t tud_cdc_write (void const* buffer, uint32_t bufsize)
-{
-  return tud_cdc_n_write(0, buffer, bufsize);
-}
-
-static inline uint32_t tud_cdc_write_str (char const* str)
-{
-  return tud_cdc_n_write_str(0, str);
-}
-
-static inline uint32_t tud_cdc_write_flush (void)
-{
-  return tud_cdc_n_write_flush(0);
-}
-
-static inline uint32_t tud_cdc_write_available(void)
-{
-  return tud_cdc_n_write_available(0);
-}
-
-static inline bool tud_cdc_write_clear(void)
-{
-  return tud_cdc_n_write_clear(0);
-}
-
-/** @} */
-/** @} */
-
 //--------------------------------------------------------------------+
 // INTERNAL USBD-CLASS DRIVER API
 //--------------------------------------------------------------------+
diff --git a/src/class/cdc/cdc_host.c b/src/class/cdc/cdc_host.c
index 5bdd41f1f..133a10f6e 100644
--- a/src/class/cdc/cdc_host.c
+++ b/src/class/cdc/cdc_host.c
@@ -1079,7 +1079,7 @@ static uint32_t ftdi_232bm_baud_to_divisor(uint32_t baud) {
 
 static bool ftdi_sio_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
   uint16_t const divisor = (uint16_t) ftdi_232bm_baud_to_divisor(baudrate);
-  TU_LOG_DRV("CDC FTDI Set BaudRate = %lu, divisor = 0x%04x\r\n", baudrate, divisor);
+  TU_LOG_DRV("CDC FTDI Set BaudRate = %" PRIu32 ", divisor = 0x%04x\r\n", baudrate, divisor);
 
   p_cdc->user_control_cb = complete_cb;
   p_cdc->requested_line_coding.bit_rate = baudrate;
@@ -1222,7 +1222,7 @@ static bool cp210x_set_line_coding(cdch_interface_t* p_cdc, cdc_line_coding_t co
 }
 
 static bool cp210x_set_baudrate(cdch_interface_t* p_cdc, uint32_t baudrate, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
-  TU_LOG_DRV("CDC CP210x Set BaudRate = %lu\r\n", baudrate);
+  TU_LOG_DRV("CDC CP210x Set BaudRate = %" PRIu32 "\r\n", baudrate);
   uint32_t baud_le = tu_htole32(baudrate);
   p_cdc->user_control_cb = complete_cb;
   return cp210x_set_request(p_cdc, CP210X_SET_BAUDRATE, 0, (uint8_t *) &baud_le, 4,
diff --git a/src/class/hid/hid.h b/src/class/hid/hid.h
index fcaab7a50..c2b5a8a48 100644
--- a/src/class/hid/hid.h
+++ b/src/class/hid/hid.h
@@ -366,177 +366,224 @@ typedef enum
 //--------------------------------------------------------------------+
 // HID KEYCODE
 //--------------------------------------------------------------------+
-#define HID_KEY_NONE                      0x00
-#define HID_KEY_A                         0x04
-#define HID_KEY_B                         0x05
-#define HID_KEY_C                         0x06
-#define HID_KEY_D                         0x07
-#define HID_KEY_E                         0x08
-#define HID_KEY_F                         0x09
-#define HID_KEY_G                         0x0A
-#define HID_KEY_H                         0x0B
-#define HID_KEY_I                         0x0C
-#define HID_KEY_J                         0x0D
-#define HID_KEY_K                         0x0E
-#define HID_KEY_L                         0x0F
-#define HID_KEY_M                         0x10
-#define HID_KEY_N                         0x11
-#define HID_KEY_O                         0x12
-#define HID_KEY_P                         0x13
-#define HID_KEY_Q                         0x14
-#define HID_KEY_R                         0x15
-#define HID_KEY_S                         0x16
-#define HID_KEY_T                         0x17
-#define HID_KEY_U                         0x18
-#define HID_KEY_V                         0x19
-#define HID_KEY_W                         0x1A
-#define HID_KEY_X                         0x1B
-#define HID_KEY_Y                         0x1C
-#define HID_KEY_Z                         0x1D
-#define HID_KEY_1                         0x1E
-#define HID_KEY_2                         0x1F
-#define HID_KEY_3                         0x20
-#define HID_KEY_4                         0x21
-#define HID_KEY_5                         0x22
-#define HID_KEY_6                         0x23
-#define HID_KEY_7                         0x24
-#define HID_KEY_8                         0x25
-#define HID_KEY_9                         0x26
-#define HID_KEY_0                         0x27
-#define HID_KEY_ENTER                     0x28
-#define HID_KEY_ESCAPE                    0x29
-#define HID_KEY_BACKSPACE                 0x2A
-#define HID_KEY_TAB                       0x2B
-#define HID_KEY_SPACE                     0x2C
-#define HID_KEY_MINUS                     0x2D
-#define HID_KEY_EQUAL                     0x2E
-#define HID_KEY_BRACKET_LEFT              0x2F
-#define HID_KEY_BRACKET_RIGHT             0x30
-#define HID_KEY_BACKSLASH                 0x31
-#define HID_KEY_EUROPE_1                  0x32
-#define HID_KEY_SEMICOLON                 0x33
-#define HID_KEY_APOSTROPHE                0x34
-#define HID_KEY_GRAVE                     0x35
-#define HID_KEY_COMMA                     0x36
-#define HID_KEY_PERIOD                    0x37
-#define HID_KEY_SLASH                     0x38
-#define HID_KEY_CAPS_LOCK                 0x39
-#define HID_KEY_F1                        0x3A
-#define HID_KEY_F2                        0x3B
-#define HID_KEY_F3                        0x3C
-#define HID_KEY_F4                        0x3D
-#define HID_KEY_F5                        0x3E
-#define HID_KEY_F6                        0x3F
-#define HID_KEY_F7                        0x40
-#define HID_KEY_F8                        0x41
-#define HID_KEY_F9                        0x42
-#define HID_KEY_F10                       0x43
-#define HID_KEY_F11                       0x44
-#define HID_KEY_F12                       0x45
-#define HID_KEY_PRINT_SCREEN              0x46
-#define HID_KEY_SCROLL_LOCK               0x47
-#define HID_KEY_PAUSE                     0x48
-#define HID_KEY_INSERT                    0x49
-#define HID_KEY_HOME                      0x4A
-#define HID_KEY_PAGE_UP                   0x4B
-#define HID_KEY_DELETE                    0x4C
-#define HID_KEY_END                       0x4D
-#define HID_KEY_PAGE_DOWN                 0x4E
-#define HID_KEY_ARROW_RIGHT               0x4F
-#define HID_KEY_ARROW_LEFT                0x50
-#define HID_KEY_ARROW_DOWN                0x51
-#define HID_KEY_ARROW_UP                  0x52
-#define HID_KEY_NUM_LOCK                  0x53
-#define HID_KEY_KEYPAD_DIVIDE             0x54
-#define HID_KEY_KEYPAD_MULTIPLY           0x55
-#define HID_KEY_KEYPAD_SUBTRACT           0x56
-#define HID_KEY_KEYPAD_ADD                0x57
-#define HID_KEY_KEYPAD_ENTER              0x58
-#define HID_KEY_KEYPAD_1                  0x59
-#define HID_KEY_KEYPAD_2                  0x5A
-#define HID_KEY_KEYPAD_3                  0x5B
-#define HID_KEY_KEYPAD_4                  0x5C
-#define HID_KEY_KEYPAD_5                  0x5D
-#define HID_KEY_KEYPAD_6                  0x5E
-#define HID_KEY_KEYPAD_7                  0x5F
-#define HID_KEY_KEYPAD_8                  0x60
-#define HID_KEY_KEYPAD_9                  0x61
-#define HID_KEY_KEYPAD_0                  0x62
-#define HID_KEY_KEYPAD_DECIMAL            0x63
-#define HID_KEY_EUROPE_2                  0x64
-#define HID_KEY_APPLICATION               0x65
-#define HID_KEY_POWER                     0x66
-#define HID_KEY_KEYPAD_EQUAL              0x67
-#define HID_KEY_F13                       0x68
-#define HID_KEY_F14                       0x69
-#define HID_KEY_F15                       0x6A
-#define HID_KEY_F16                       0x6B
-#define HID_KEY_F17                       0x6C
-#define HID_KEY_F18                       0x6D
-#define HID_KEY_F19                       0x6E
-#define HID_KEY_F20                       0x6F
-#define HID_KEY_F21                       0x70
-#define HID_KEY_F22                       0x71
-#define HID_KEY_F23                       0x72
-#define HID_KEY_F24                       0x73
-#define HID_KEY_EXECUTE                   0x74
-#define HID_KEY_HELP                      0x75
-#define HID_KEY_MENU                      0x76
-#define HID_KEY_SELECT                    0x77
-#define HID_KEY_STOP                      0x78
-#define HID_KEY_AGAIN                     0x79
-#define HID_KEY_UNDO                      0x7A
-#define HID_KEY_CUT                       0x7B
-#define HID_KEY_COPY                      0x7C
-#define HID_KEY_PASTE                     0x7D
-#define HID_KEY_FIND                      0x7E
-#define HID_KEY_MUTE                      0x7F
-#define HID_KEY_VOLUME_UP                 0x80
-#define HID_KEY_VOLUME_DOWN               0x81
-#define HID_KEY_LOCKING_CAPS_LOCK         0x82
-#define HID_KEY_LOCKING_NUM_LOCK          0x83
-#define HID_KEY_LOCKING_SCROLL_LOCK       0x84
-#define HID_KEY_KEYPAD_COMMA              0x85
-#define HID_KEY_KEYPAD_EQUAL_SIGN         0x86
-#define HID_KEY_KANJI1                    0x87
-#define HID_KEY_KANJI2                    0x88
-#define HID_KEY_KANJI3                    0x89
-#define HID_KEY_KANJI4                    0x8A
-#define HID_KEY_KANJI5                    0x8B
-#define HID_KEY_KANJI6                    0x8C
-#define HID_KEY_KANJI7                    0x8D
-#define HID_KEY_KANJI8                    0x8E
-#define HID_KEY_KANJI9                    0x8F
-#define HID_KEY_LANG1                     0x90
-#define HID_KEY_LANG2                     0x91
-#define HID_KEY_LANG3                     0x92
-#define HID_KEY_LANG4                     0x93
-#define HID_KEY_LANG5                     0x94
-#define HID_KEY_LANG6                     0x95
-#define HID_KEY_LANG7                     0x96
-#define HID_KEY_LANG8                     0x97
-#define HID_KEY_LANG9                     0x98
-#define HID_KEY_ALTERNATE_ERASE           0x99
-#define HID_KEY_SYSREQ_ATTENTION          0x9A
-#define HID_KEY_CANCEL                    0x9B
-#define HID_KEY_CLEAR                     0x9C
-#define HID_KEY_PRIOR                     0x9D
-#define HID_KEY_RETURN                    0x9E
-#define HID_KEY_SEPARATOR                 0x9F
-#define HID_KEY_OUT                       0xA0
-#define HID_KEY_OPER                      0xA1
-#define HID_KEY_CLEAR_AGAIN               0xA2
-#define HID_KEY_CRSEL_PROPS               0xA3
-#define HID_KEY_EXSEL                     0xA4
-// RESERVED					                      0xA5-DF
-#define HID_KEY_CONTROL_LEFT              0xE0
-#define HID_KEY_SHIFT_LEFT                0xE1
-#define HID_KEY_ALT_LEFT                  0xE2
-#define HID_KEY_GUI_LEFT                  0xE3
-#define HID_KEY_CONTROL_RIGHT             0xE4
-#define HID_KEY_SHIFT_RIGHT               0xE5
-#define HID_KEY_ALT_RIGHT                 0xE6
-#define HID_KEY_GUI_RIGHT                 0xE7
+#define HID_KEY_NONE                        0x00
+#define HID_KEY_A                           0x04
+#define HID_KEY_B                           0x05
+#define HID_KEY_C                           0x06
+#define HID_KEY_D                           0x07
+#define HID_KEY_E                           0x08
+#define HID_KEY_F                           0x09
+#define HID_KEY_G                           0x0A
+#define HID_KEY_H                           0x0B
+#define HID_KEY_I                           0x0C
+#define HID_KEY_J                           0x0D
+#define HID_KEY_K                           0x0E
+#define HID_KEY_L                           0x0F
+#define HID_KEY_M                           0x10
+#define HID_KEY_N                           0x11
+#define HID_KEY_O                           0x12
+#define HID_KEY_P                           0x13
+#define HID_KEY_Q                           0x14
+#define HID_KEY_R                           0x15
+#define HID_KEY_S                           0x16
+#define HID_KEY_T                           0x17
+#define HID_KEY_U                           0x18
+#define HID_KEY_V                           0x19
+#define HID_KEY_W                           0x1A
+#define HID_KEY_X                           0x1B
+#define HID_KEY_Y                           0x1C
+#define HID_KEY_Z                           0x1D
+#define HID_KEY_1                           0x1E
+#define HID_KEY_2                           0x1F
+#define HID_KEY_3                           0x20
+#define HID_KEY_4                           0x21
+#define HID_KEY_5                           0x22
+#define HID_KEY_6                           0x23
+#define HID_KEY_7                           0x24
+#define HID_KEY_8                           0x25
+#define HID_KEY_9                           0x26
+#define HID_KEY_0                           0x27
+#define HID_KEY_ENTER                       0x28
+#define HID_KEY_ESCAPE                      0x29
+#define HID_KEY_BACKSPACE                   0x2A
+#define HID_KEY_TAB                         0x2B
+#define HID_KEY_SPACE                       0x2C
+#define HID_KEY_MINUS                       0x2D
+#define HID_KEY_EQUAL                       0x2E
+#define HID_KEY_BRACKET_LEFT                0x2F
+#define HID_KEY_BRACKET_RIGHT               0x30
+#define HID_KEY_BACKSLASH                   0x31
+#define HID_KEY_EUROPE_1                    0x32
+#define HID_KEY_SEMICOLON                   0x33
+#define HID_KEY_APOSTROPHE                  0x34
+#define HID_KEY_GRAVE                       0x35
+#define HID_KEY_COMMA                       0x36
+#define HID_KEY_PERIOD                      0x37
+#define HID_KEY_SLASH                       0x38
+#define HID_KEY_CAPS_LOCK                   0x39
+#define HID_KEY_F1                          0x3A
+#define HID_KEY_F2                          0x3B
+#define HID_KEY_F3                          0x3C
+#define HID_KEY_F4                          0x3D
+#define HID_KEY_F5                          0x3E
+#define HID_KEY_F6                          0x3F
+#define HID_KEY_F7                          0x40
+#define HID_KEY_F8                          0x41
+#define HID_KEY_F9                          0x42
+#define HID_KEY_F10                         0x43
+#define HID_KEY_F11                         0x44
+#define HID_KEY_F12                         0x45
+#define HID_KEY_PRINT_SCREEN                0x46
+#define HID_KEY_SCROLL_LOCK                 0x47
+#define HID_KEY_PAUSE                       0x48
+#define HID_KEY_INSERT                      0x49
+#define HID_KEY_HOME                        0x4A
+#define HID_KEY_PAGE_UP                     0x4B
+#define HID_KEY_DELETE                      0x4C
+#define HID_KEY_END                         0x4D
+#define HID_KEY_PAGE_DOWN                   0x4E
+#define HID_KEY_ARROW_RIGHT                 0x4F
+#define HID_KEY_ARROW_LEFT                  0x50
+#define HID_KEY_ARROW_DOWN                  0x51
+#define HID_KEY_ARROW_UP                    0x52
+#define HID_KEY_NUM_LOCK                    0x53
+#define HID_KEY_KEYPAD_DIVIDE               0x54
+#define HID_KEY_KEYPAD_MULTIPLY             0x55
+#define HID_KEY_KEYPAD_SUBTRACT             0x56
+#define HID_KEY_KEYPAD_ADD                  0x57
+#define HID_KEY_KEYPAD_ENTER                0x58
+#define HID_KEY_KEYPAD_1                    0x59
+#define HID_KEY_KEYPAD_2                    0x5A
+#define HID_KEY_KEYPAD_3                    0x5B
+#define HID_KEY_KEYPAD_4                    0x5C
+#define HID_KEY_KEYPAD_5                    0x5D
+#define HID_KEY_KEYPAD_6                    0x5E
+#define HID_KEY_KEYPAD_7                    0x5F
+#define HID_KEY_KEYPAD_8                    0x60
+#define HID_KEY_KEYPAD_9                    0x61
+#define HID_KEY_KEYPAD_0                    0x62
+#define HID_KEY_KEYPAD_DECIMAL              0x63
+#define HID_KEY_EUROPE_2                    0x64
+#define HID_KEY_APPLICATION                 0x65
+#define HID_KEY_POWER                       0x66
+#define HID_KEY_KEYPAD_EQUAL                0x67
+#define HID_KEY_F13                         0x68
+#define HID_KEY_F14                         0x69
+#define HID_KEY_F15                         0x6A
+#define HID_KEY_F16                         0x6B
+#define HID_KEY_F17                         0x6C
+#define HID_KEY_F18                         0x6D
+#define HID_KEY_F19                         0x6E
+#define HID_KEY_F20                         0x6F
+#define HID_KEY_F21                         0x70
+#define HID_KEY_F22                         0x71
+#define HID_KEY_F23                         0x72
+#define HID_KEY_F24                         0x73
+#define HID_KEY_EXECUTE                     0x74
+#define HID_KEY_HELP                        0x75
+#define HID_KEY_MENU                        0x76
+#define HID_KEY_SELECT                      0x77
+#define HID_KEY_STOP                        0x78
+#define HID_KEY_AGAIN                       0x79
+#define HID_KEY_UNDO                        0x7A
+#define HID_KEY_CUT                         0x7B
+#define HID_KEY_COPY                        0x7C
+#define HID_KEY_PASTE                       0x7D
+#define HID_KEY_FIND                        0x7E
+#define HID_KEY_MUTE                        0x7F
+#define HID_KEY_VOLUME_UP                   0x80
+#define HID_KEY_VOLUME_DOWN                 0x81
+#define HID_KEY_LOCKING_CAPS_LOCK           0x82
+#define HID_KEY_LOCKING_NUM_LOCK            0x83
+#define HID_KEY_LOCKING_SCROLL_LOCK         0x84
+#define HID_KEY_KEYPAD_COMMA                0x85
+#define HID_KEY_KEYPAD_EQUAL_SIGN           0x86
+#define HID_KEY_KANJI1                      0x87
+#define HID_KEY_KANJI2                      0x88
+#define HID_KEY_KANJI3                      0x89
+#define HID_KEY_KANJI4                      0x8A
+#define HID_KEY_KANJI5                      0x8B
+#define HID_KEY_KANJI6                      0x8C
+#define HID_KEY_KANJI7                      0x8D
+#define HID_KEY_KANJI8                      0x8E
+#define HID_KEY_KANJI9                      0x8F
+#define HID_KEY_LANG1                       0x90
+#define HID_KEY_LANG2                       0x91
+#define HID_KEY_LANG3                       0x92
+#define HID_KEY_LANG4                       0x93
+#define HID_KEY_LANG5                       0x94
+#define HID_KEY_LANG6                       0x95
+#define HID_KEY_LANG7                       0x96
+#define HID_KEY_LANG8                       0x97
+#define HID_KEY_LANG9                       0x98
+#define HID_KEY_ALTERNATE_ERASE             0x99
+#define HID_KEY_SYSREQ_ATTENTION            0x9A
+#define HID_KEY_CANCEL                      0x9B
+#define HID_KEY_CLEAR                       0x9C
+#define HID_KEY_PRIOR                       0x9D
+#define HID_KEY_RETURN                      0x9E
+#define HID_KEY_SEPARATOR                   0x9F
+#define HID_KEY_OUT                         0xA0
+#define HID_KEY_OPER                        0xA1
+#define HID_KEY_CLEAR_AGAIN                 0xA2
+#define HID_KEY_CRSEL_PROPS                 0xA3
+#define HID_KEY_EXSEL                       0xA4
+// RESERVED					                        0xA5-AF
+#define HID_KEY_KEYPAD_00                   0xB0
+#define HID_KEY_KEYPAD_000                  0xB1
+#define HID_KEY_THOUSANDS_SEPARATOR         0xB2
+#define HID_KEY_DECIMAL_SEPARATOR           0xB3
+#define HID_KEY_CURRENCY_UNIT               0xB4
+#define HID_KEY_CURRENCY_SUBUNIT            0xB5
+#define HID_KEY_KEYPAD_LEFT_PARENTHESIS     0xB6
+#define HID_KEY_KEYPAD_RIGHT_PARENTHESIS    0xB7
+#define HID_KEY_KEYPAD_LEFT_BRACE           0xB8
+#define HID_KEY_KEYPAD_RIGHT_BRACE          0xB9
+#define HID_KEY_KEYPAD_TAB                  0xBA
+#define HID_KEY_KEYPAD_BACKSPACE            0xBB
+#define HID_KEY_KEYPAD_A                    0xBC
+#define HID_KEY_KEYPAD_B                    0xBD
+#define HID_KEY_KEYPAD_C                    0xBE
+#define HID_KEY_KEYPAD_D                    0xBF
+#define HID_KEY_KEYPAD_E                    0xC0
+#define HID_KEY_KEYPAD_F                    0xC1
+#define HID_KEY_KEYPAD_XOR                  0xC2
+#define HID_KEY_KEYPAD_CARET                0xC3
+#define HID_KEY_KEYPAD_PERCENT              0xC4
+#define HID_KEY_KEYPAD_LESS_THAN            0xC5
+#define HID_KEY_KEYPAD_GREATER_THAN         0xC6
+#define HID_KEY_KEYPAD_AMPERSAND            0xC7
+#define HID_KEY_KEYPAD_DOUBLE_AMPERSAND     0xC8
+#define HID_KEY_KEYPAD_VERTICAL_BAR         0xC9
+#define HID_KEY_KEYPAD_DOUBLE_VERTICAL_BAR  0xCA
+#define HID_KEY_KEYPAD_COLON                0xCB
+#define HID_KEY_KEYPAD_HASH                 0xCC
+#define HID_KEY_KEYPAD_SPACE                0xCD
+#define HID_KEY_KEYPAD_AT                   0xCE
+#define HID_KEY_KEYPAD_EXCLAMATION          0xCF
+#define HID_KEY_KEYPAD_MEMORY_STORE         0xD0
+#define HID_KEY_KEYPAD_MEMORY_RECALL        0xD1
+#define HID_KEY_KEYPAD_MEMORY_CLEAR         0xD2
+#define HID_KEY_KEYPAD_MEMORY_ADD           0xD3
+#define HID_KEY_KEYPAD_MEMORY_SUBTRACT      0xD4
+#define HID_KEY_KEYPAD_MEMORY_MULTIPLY      0xD5
+#define HID_KEY_KEYPAD_MEMORY_DIVIDE        0xD6
+#define HID_KEY_KEYPAD_PLUS_MINUS           0xD7
+#define HID_KEY_KEYPAD_CLEAR                0xD8
+#define HID_KEY_KEYPAD_CLEAR_ENTRY          0xD9
+#define HID_KEY_KEYPAD_BINARY               0xDA
+#define HID_KEY_KEYPAD_OCTAL                0xDB
+#define HID_KEY_KEYPAD_DECIMAL_2            0xDC
+#define HID_KEY_KEYPAD_HEXADECIMAL          0xDD
+// RESERVED					                        0xDE-DF
+#define HID_KEY_CONTROL_LEFT                0xE0
+#define HID_KEY_SHIFT_LEFT                  0xE1
+#define HID_KEY_ALT_LEFT                    0xE2
+#define HID_KEY_GUI_LEFT                    0xE3
+#define HID_KEY_CONTROL_RIGHT               0xE4
+#define HID_KEY_SHIFT_RIGHT                 0xE5
+#define HID_KEY_ALT_RIGHT                   0xE6
+#define HID_KEY_GUI_RIGHT                   0xE7
 
 
 //--------------------------------------------------------------------+
@@ -697,32 +744,33 @@ enum {
 
 /// HID Usage Table - Table 1: Usage Page Summary
 enum {
-  HID_USAGE_PAGE_DESKTOP         = 0x01,
-  HID_USAGE_PAGE_SIMULATE        = 0x02,
-  HID_USAGE_PAGE_VIRTUAL_REALITY = 0x03,
-  HID_USAGE_PAGE_SPORT           = 0x04,
-  HID_USAGE_PAGE_GAME            = 0x05,
-  HID_USAGE_PAGE_GENERIC_DEVICE  = 0x06,
-  HID_USAGE_PAGE_KEYBOARD        = 0x07,
-  HID_USAGE_PAGE_LED             = 0x08,
-  HID_USAGE_PAGE_BUTTON          = 0x09,
-  HID_USAGE_PAGE_ORDINAL         = 0x0a,
-  HID_USAGE_PAGE_TELEPHONY       = 0x0b,
-  HID_USAGE_PAGE_CONSUMER        = 0x0c,
-  HID_USAGE_PAGE_DIGITIZER       = 0x0d,
-  HID_USAGE_PAGE_PID             = 0x0f,
-  HID_USAGE_PAGE_UNICODE         = 0x10,
-  HID_USAGE_PAGE_ALPHA_DISPLAY   = 0x14,
-  HID_USAGE_PAGE_MEDICAL         = 0x40,
-  HID_USAGE_PAGE_MONITOR         = 0x80, //0x80 - 0x83
-  HID_USAGE_PAGE_POWER           = 0x84, // 0x084 - 0x87
-  HID_USAGE_PAGE_BARCODE_SCANNER = 0x8c,
-  HID_USAGE_PAGE_SCALE           = 0x8d,
-  HID_USAGE_PAGE_MSR             = 0x8e,
-  HID_USAGE_PAGE_CAMERA          = 0x90,
-  HID_USAGE_PAGE_ARCADE          = 0x91,
-  HID_USAGE_PAGE_FIDO            = 0xF1D0, // FIDO alliance HID usage page
-  HID_USAGE_PAGE_VENDOR          = 0xFF00 // 0xFF00 - 0xFFFF
+  HID_USAGE_PAGE_DESKTOP                   = 0x01,
+  HID_USAGE_PAGE_SIMULATE                  = 0x02,
+  HID_USAGE_PAGE_VIRTUAL_REALITY           = 0x03,
+  HID_USAGE_PAGE_SPORT                     = 0x04,
+  HID_USAGE_PAGE_GAME                      = 0x05,
+  HID_USAGE_PAGE_GENERIC_DEVICE            = 0x06,
+  HID_USAGE_PAGE_KEYBOARD                  = 0x07,
+  HID_USAGE_PAGE_LED                       = 0x08,
+  HID_USAGE_PAGE_BUTTON                    = 0x09,
+  HID_USAGE_PAGE_ORDINAL                   = 0x0a,
+  HID_USAGE_PAGE_TELEPHONY                 = 0x0b,
+  HID_USAGE_PAGE_CONSUMER                  = 0x0c,
+  HID_USAGE_PAGE_DIGITIZER                 = 0x0d,
+  HID_USAGE_PAGE_PID                       = 0x0f,
+  HID_USAGE_PAGE_UNICODE                   = 0x10,
+  HID_USAGE_PAGE_ALPHA_DISPLAY             = 0x14,
+  HID_USAGE_PAGE_MEDICAL                   = 0x40,
+  HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION = 0x59,
+  HID_USAGE_PAGE_MONITOR                   = 0x80, // 0x80 - 0x83
+  HID_USAGE_PAGE_POWER                     = 0x84, // 0x084 - 0x87
+  HID_USAGE_PAGE_BARCODE_SCANNER           = 0x8c,
+  HID_USAGE_PAGE_SCALE                     = 0x8d,
+  HID_USAGE_PAGE_MSR                       = 0x8e,
+  HID_USAGE_PAGE_CAMERA                    = 0x90,
+  HID_USAGE_PAGE_ARCADE                    = 0x91,
+  HID_USAGE_PAGE_FIDO                      = 0xF1D0, // FIDO alliance HID usage page
+  HID_USAGE_PAGE_VENDOR                    = 0xFF00  // 0xFF00 - 0xFFFF
 };
 
 /// HID Usage Table - Table 6: Generic Desktop Page
@@ -801,8 +849,7 @@ enum {
 
 /// HID Usage Table: Consumer Page (0x0C)
 /// Only contains controls that supported by Windows (whole list is too long)
-enum
-{
+enum {
   // Generic Control
   HID_USAGE_CONSUMER_CONTROL                           = 0x0001,
 
@@ -858,9 +905,45 @@ enum
   HID_USAGE_CONSUMER_AC_PAN                            = 0x0238,
 };
 
+/// HID Usage Table - Lighting And Illumination Page (0x59)
+enum {
+  HID_USAGE_LIGHTING_LAMP_ARRAY                          = 0x01,
+  HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT        = 0x02,
+  HID_USAGE_LIGHTING_LAMP_COUNT                          = 0x03,
+  HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS   = 0x04,
+  HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS  = 0x05,
+  HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS   = 0x06,
+  HID_USAGE_LIGHTING_LAMP_ARRAY_KIND                     = 0x07,
+  HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS = 0x08,
+  HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT      = 0x20,
+  HID_USAGE_LIGHTING_LAMP_ID                             = 0x21,
+  HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT     = 0x22,
+  HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS           = 0x23,
+  HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS           = 0x24,
+  HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS           = 0x25,
+  HID_USAGE_LIGHTING_LAMP_PURPOSES                       = 0x26,
+  HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS      = 0x27,
+  HID_USAGE_LIGHTING_RED_LEVEL_COUNT                     = 0x28,
+  HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT                   = 0x29,
+  HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT                    = 0x2A,
+  HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT               = 0x2B,
+  HID_USAGE_LIGHTING_IS_PROGRAMMABLE                     = 0x2C,
+  HID_USAGE_LIGHTING_INPUT_BINDING                       = 0x2D,
+  HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT            = 0x50,
+  HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL                  = 0x51,
+  HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL                = 0x52,
+  HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL                 = 0x53,
+  HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL            = 0x54,
+  HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS                   = 0x55,
+  HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT            = 0x60,
+  HID_USAGE_LIGHTING_LAMP_ID_START                       = 0x61,
+  HID_USAGE_LIGHTING_LAMP_ID_END                         = 0x62,
+  HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT           = 0x70,
+  HID_USAGE_LIGHTING_AUTONOMOUS_MODE                     = 0x71,
+};
+
 /// HID Usage Table: FIDO Alliance Page (0xF1D0)
-enum
-{
+enum {
   HID_USAGE_FIDO_U2FHID   = 0x01, // U2FHID usage for top-level collection
   HID_USAGE_FIDO_DATA_IN  = 0x20, // Raw IN data report
   HID_USAGE_FIDO_DATA_OUT = 0x21  // Raw OUT data report
diff --git a/src/class/hid/hid_device.c b/src/class/hid/hid_device.c
index 5cc5a2488..00f6fc474 100644
--- a/src/class/hid/hid_device.c
+++ b/src/class/hid/hid_device.c
@@ -39,92 +39,109 @@
 //--------------------------------------------------------------------+
 // MACRO CONSTANT TYPEDEF
 //--------------------------------------------------------------------+
-typedef struct
-{
+typedef struct {
   uint8_t itf_num;
   uint8_t ep_in;
-  uint8_t ep_out;        // optional Out endpoint
-  uint8_t itf_protocol;  // Boot mouse or keyboard
+  uint8_t ep_out;       // optional Out endpoint
+  uint8_t itf_protocol; // Boot mouse or keyboard
 
+  uint16_t report_desc_len;
   uint8_t protocol_mode; // Boot (0) or Report protocol (1)
   uint8_t idle_rate;     // up to application to handle idle rate
-  uint16_t report_desc_len;
-
-  CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
-  CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
 
   // TODO save hid descriptor since host can specifically request this after enumeration
   // Note: HID descriptor may be not available from application after enumeration
-  tusb_hid_descriptor_hid_t const * hid_descriptor;
+  tusb_hid_descriptor_hid_t const *hid_descriptor;
+
+  uint8_t ctrl_buf[CFG_TUD_HID_EP_BUFSIZE];
+  CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_HID_EP_BUFSIZE];
+  CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_HID_EP_BUFSIZE];
 } hidd_interface_t;
 
 CFG_TUD_MEM_SECTION tu_static hidd_interface_t _hidd_itf[CFG_TUD_HID];
 
 /*------------- Helpers -------------*/
-static inline uint8_t get_index_by_itfnum(uint8_t itf_num)
-{
-	for (uint8_t i=0; i < CFG_TUD_HID; i++ )
-	{
-		if ( itf_num == _hidd_itf[i].itf_num ) return i;
-	}
+TU_ATTR_ALWAYS_INLINE static inline uint8_t get_index_by_itfnum(uint8_t itf_num) {
+  for (uint8_t i = 0; i < CFG_TUD_HID; i++) {
+    if (itf_num == _hidd_itf[i].itf_num) {
+      return i;
+    }
+  }
+  return 0xFF;
+}
 
-	return 0xFF;
+//--------------------------------------------------------------------+
+// Weak stubs: invoked if no strong implementation is available
+//--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol) {
+  (void) instance;
+  (void) protocol;
+}
+
+TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate) {
+  (void) instance;
+  (void) idle_rate;
+  return true;
+}
+
+TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len) {
+  (void) instance;
+  (void) report;
+  (void) len;
+}
+
+// Invoked when a transfer wasn't successful
+TU_ATTR_WEAK void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes) {
+  (void) instance;
+  (void) report_type;
+  (void) report;
+  (void) xferred_bytes;
 }
 
 //--------------------------------------------------------------------+
 // APPLICATION API
 //--------------------------------------------------------------------+
-bool tud_hid_n_ready(uint8_t instance)
-{
+bool tud_hid_n_ready(uint8_t instance) {
   uint8_t const rhport = 0;
   uint8_t const ep_in = _hidd_itf[instance].ep_in;
   return tud_ready() && (ep_in != 0) && !usbd_edpt_busy(rhport, ep_in);
 }
 
-bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const* report, uint16_t len)
-{
+bool tud_hid_n_report(uint8_t instance, uint8_t report_id, void const *report, uint16_t len) {
   uint8_t const rhport = 0;
-  hidd_interface_t * p_hid = &_hidd_itf[instance];
+  hidd_interface_t *p_hid = &_hidd_itf[instance];
 
   // claim endpoint
-  TU_VERIFY( usbd_edpt_claim(rhport, p_hid->ep_in) );
+  TU_VERIFY(usbd_edpt_claim(rhport, p_hid->ep_in));
 
   // prepare data
-  if (report_id)
-  {
+  if (report_id) {
     p_hid->epin_buf[0] = report_id;
-    TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf+1, CFG_TUD_HID_EP_BUFSIZE-1, report, len));
+    TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf + 1, CFG_TUD_HID_EP_BUFSIZE - 1, report, len));
     len++;
-  }else
-  {
+  } else {
     TU_VERIFY(0 == tu_memcpy_s(p_hid->epin_buf, CFG_TUD_HID_EP_BUFSIZE, report, len));
   }
 
   return usbd_edpt_xfer(rhport, p_hid->ep_in, p_hid->epin_buf, len);
 }
 
-uint8_t tud_hid_n_interface_protocol(uint8_t instance)
-{
+uint8_t tud_hid_n_interface_protocol(uint8_t instance) {
   return _hidd_itf[instance].itf_protocol;
 }
 
-uint8_t tud_hid_n_get_protocol(uint8_t instance)
-{
+uint8_t tud_hid_n_get_protocol(uint8_t instance) {
   return _hidd_itf[instance].protocol_mode;
 }
 
-bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
-{
+bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modifier, uint8_t keycode[6]) {
   hid_keyboard_report_t report;
-
   report.modifier = modifier;
   report.reserved = 0;
 
-  if ( keycode )
-  {
+  if (keycode) {
     memcpy(report.keycode, keycode, sizeof(report.keycode));
-  }else
-  {
+  } else {
     tu_memclr(report.keycode, 6);
   }
 
@@ -132,45 +149,41 @@ bool tud_hid_n_keyboard_report(uint8_t instance, uint8_t report_id, uint8_t modi
 }
 
 bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id,
-                            uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
-{
-  hid_mouse_report_t report =
-  {
+                            uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) {
+  hid_mouse_report_t report = {
     .buttons = buttons,
-    .x       = x,
-    .y       = y,
-    .wheel   = vertical,
-    .pan     = horizontal
+    .x = x,
+    .y = y,
+    .wheel = vertical,
+    .pan = horizontal
   };
 
   return tud_hid_n_report(instance, report_id, &report, sizeof(report));
 }
 
-bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal)
-{
-  hid_abs_mouse_report_t report =
-  {
+bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id,
+                                uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
+  hid_abs_mouse_report_t report = {
     .buttons = buttons,
-    .x       = x,
-    .y       = y,
-    .wheel   = vertical,
-    .pan     = horizontal
+    .x = x,
+    .y = y,
+    .wheel = vertical,
+    .pan = horizontal
   };
   return tud_hid_n_report(instance, report_id, &report, sizeof(report));
 }
 
 bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id,
                               int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) {
-  hid_gamepad_report_t report =
-  {
-    .x       = x,
-    .y       = y,
-    .z       = z,
-    .rz      = rz,
-    .rx      = rx,
-    .ry      = ry,
-    .hat     = hat,
-    .buttons = buttons,
+  hid_gamepad_report_t report = {
+      .x = x,
+      .y = y,
+      .z = z,
+      .rz = rz,
+      .rx = rx,
+      .ry = ry,
+      .hat = hat,
+      .buttons = buttons,
   };
 
   return tud_hid_n_report(instance, report_id, &report, sizeof(report));
@@ -187,59 +200,54 @@ bool hidd_deinit(void) {
   return true;
 }
 
-void hidd_reset(uint8_t rhport)
-{
-  (void) rhport;
+void hidd_reset(uint8_t rhport) {
+  (void)rhport;
   tu_memclr(_hidd_itf, sizeof(_hidd_itf));
 }
 
-uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
- {
+uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const *desc_itf, uint16_t max_len) {
   TU_VERIFY(TUSB_CLASS_HID == desc_itf->bInterfaceClass, 0);
 
   // len = interface + hid + n*endpoints
-  uint16_t const drv_len =
-      (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
+  uint16_t const drv_len = (uint16_t) (sizeof(tusb_desc_interface_t) + sizeof(tusb_hid_descriptor_hid_t) +
                                        desc_itf->bNumEndpoints * sizeof(tusb_desc_endpoint_t));
   TU_ASSERT(max_len >= drv_len, 0);
 
   // Find available interface
-  hidd_interface_t * p_hid = NULL;
+  hidd_interface_t *p_hid = NULL;
   uint8_t hid_id;
-  for(hid_id=0; hid_idhid_descriptor = (tusb_hid_descriptor_hid_t const *) p_desc;
+  p_hid->hid_descriptor = (tusb_hid_descriptor_hid_t const *)p_desc;
 
   //------------- Endpoint Descriptor -------------//
   p_desc = tu_desc_next(p_desc);
   TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_INTERRUPT, &p_hid->ep_out, &p_hid->ep_in), 0);
 
-  if ( desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT ) p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
+  if (desc_itf->bInterfaceSubClass == HID_SUBCLASS_BOOT) {
+    p_hid->itf_protocol = desc_itf->bInterfaceProtocol;
+  }
 
   p_hid->protocol_mode = HID_PROTOCOL_REPORT; // Per Specs: default is report mode
-  p_hid->itf_num       = desc_itf->bInterfaceNumber;
+  p_hid->itf_num = desc_itf->bInterfaceNumber;
 
   // Use offsetof to avoid pointer to the odd/misaligned address
-  p_hid->report_desc_len = tu_unaligned_read16((uint8_t const*) p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
+  p_hid->report_desc_len = tu_unaligned_read16((uint8_t const *)p_hid->hid_descriptor + offsetof(tusb_hid_descriptor_hid_t, wReportLength));
 
   // Prepare for output endpoint
-  if (p_hid->ep_out)
-  {
-    if ( !usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)) )
-    {
+  if (p_hid->ep_out) {
+    if (!usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf))) {
       TU_LOG_FAILED();
       TU_BREAKPOINT();
     }
@@ -251,177 +259,146 @@ uint16_t hidd_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint1
 // Invoked when a control transfer occurred on an interface of this class
 // Driver response accordingly to the request and the transfer stage (setup/data/ack)
 // return false to stall control endpoint (e.g unsupported request)
-bool hidd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
+bool hidd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
   TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
 
-  uint8_t const hid_itf = get_index_by_itfnum((uint8_t) request->wIndex);
+  uint8_t const hid_itf = get_index_by_itfnum((uint8_t)request->wIndex);
   TU_VERIFY(hid_itf < CFG_TUD_HID);
 
-  hidd_interface_t* p_hid = &_hidd_itf[hid_itf];
+  hidd_interface_t *p_hid = &_hidd_itf[hid_itf];
 
-  if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD)
-  {
+  if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
     //------------- STD Request -------------//
-    if ( stage == CONTROL_STAGE_SETUP )
-    {
-      uint8_t const desc_type  = tu_u16_high(request->wValue);
-      //uint8_t const desc_index = tu_u16_low (request->wValue);
+    if (stage == CONTROL_STAGE_SETUP) {
+      uint8_t const desc_type = tu_u16_high(request->wValue);
+      // uint8_t const desc_index = tu_u16_low (request->wValue);
 
-      if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID)
-      {
+      if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_HID) {
         TU_VERIFY(p_hid->hid_descriptor);
-        TU_VERIFY(tud_control_xfer(rhport, request, (void*)(uintptr_t) p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
-      }
-      else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT)
-      {
-        uint8_t const * desc_report = tud_hid_descriptor_report_cb(hid_itf);
-        tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_report, p_hid->report_desc_len);
-      }
-      else
-      {
+        TU_VERIFY(tud_control_xfer(rhport, request, (void *)(uintptr_t)p_hid->hid_descriptor, p_hid->hid_descriptor->bLength));
+      } else if (request->bRequest == TUSB_REQ_GET_DESCRIPTOR && desc_type == HID_DESC_TYPE_REPORT) {
+        uint8_t const *desc_report = tud_hid_descriptor_report_cb(hid_itf);
+        tud_control_xfer(rhport, request, (void *)(uintptr_t)desc_report, p_hid->report_desc_len);
+      } else {
         return false; // stall unsupported request
       }
     }
-  }
-  else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS)
-  {
+  } else if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
     //------------- Class Specific Request -------------//
-    switch( request->bRequest )
-    {
+    switch (request->bRequest) {
       case HID_REQ_CONTROL_GET_REPORT:
-        if ( stage == CONTROL_STAGE_SETUP )
-        {
+        if (stage == CONTROL_STAGE_SETUP) {
           uint8_t const report_type = tu_u16_high(request->wValue);
-          uint8_t const report_id   = tu_u16_low(request->wValue);
+          uint8_t const report_id = tu_u16_low(request->wValue);
 
-          uint8_t* report_buf = p_hid->epin_buf;
+          uint8_t* report_buf = p_hid->ctrl_buf;
           uint16_t req_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
-
           uint16_t xferlen = 0;
 
           // If host request a specific Report ID, add ID to as 1 byte of response
-          if ( (report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1) )
-          {
+          if ((report_id != HID_REPORT_TYPE_INVALID) && (req_len > 1)) {
             *report_buf++ = report_id;
             req_len--;
-
             xferlen++;
           }
 
           xferlen += tud_hid_get_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, req_len);
-          TU_ASSERT( xferlen > 0 );
+          TU_ASSERT(xferlen > 0);
 
-          tud_control_xfer(rhport, request, p_hid->epin_buf, xferlen);
+          tud_control_xfer(rhport, request, p_hid->ctrl_buf, xferlen);
         }
-      break;
+        break;
 
-      case  HID_REQ_CONTROL_SET_REPORT:
-        if ( stage == CONTROL_STAGE_SETUP )
-        {
-          TU_VERIFY(request->wLength <= sizeof(p_hid->epout_buf));
-          tud_control_xfer(rhport, request, p_hid->epout_buf, request->wLength);
-        }
-        else if ( stage == CONTROL_STAGE_ACK )
-        {
+      case HID_REQ_CONTROL_SET_REPORT:
+        if (stage == CONTROL_STAGE_SETUP) {
+          TU_VERIFY(request->wLength <= sizeof(p_hid->ctrl_buf));
+          tud_control_xfer(rhport, request, p_hid->ctrl_buf, request->wLength);
+        } else if (stage == CONTROL_STAGE_ACK) {
           uint8_t const report_type = tu_u16_high(request->wValue);
-          uint8_t const report_id   = tu_u16_low(request->wValue);
+          uint8_t const report_id = tu_u16_low(request->wValue);
 
-          uint8_t const* report_buf = p_hid->epout_buf;
+          uint8_t const* report_buf = p_hid->ctrl_buf;
           uint16_t report_len = tu_min16(request->wLength, CFG_TUD_HID_EP_BUFSIZE);
 
           // If host request a specific Report ID, extract report ID in buffer before invoking callback
-          if ( (report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0]) )
-          {
+          if ((report_id != HID_REPORT_TYPE_INVALID) && (report_len > 1) && (report_id == report_buf[0])) {
             report_buf++;
             report_len--;
           }
 
           tud_hid_set_report_cb(hid_itf, report_id, (hid_report_type_t) report_type, report_buf, report_len);
         }
-      break;
+        break;
 
       case HID_REQ_CONTROL_SET_IDLE:
-        if ( stage == CONTROL_STAGE_SETUP )
-        {
+        if (stage == CONTROL_STAGE_SETUP) {
           p_hid->idle_rate = tu_u16_high(request->wValue);
-          if ( tud_hid_set_idle_cb )
-          {
-            // stall request if callback return false
-            TU_VERIFY( tud_hid_set_idle_cb( hid_itf, p_hid->idle_rate) );
-          }
-
+          TU_VERIFY(tud_hid_set_idle_cb(hid_itf, p_hid->idle_rate)); // stall if false
           tud_control_status(rhport, request);
         }
-      break;
+        break;
 
       case HID_REQ_CONTROL_GET_IDLE:
-        if ( stage == CONTROL_STAGE_SETUP )
-        {
+        if (stage == CONTROL_STAGE_SETUP) {
           // TODO idle rate of report
           tud_control_xfer(rhport, request, &p_hid->idle_rate, 1);
         }
-      break;
+        break;
 
       case HID_REQ_CONTROL_GET_PROTOCOL:
-        if ( stage == CONTROL_STAGE_SETUP )
-        {
+        if (stage == CONTROL_STAGE_SETUP) {
           tud_control_xfer(rhport, request, &p_hid->protocol_mode, 1);
         }
-      break;
+        break;
 
       case HID_REQ_CONTROL_SET_PROTOCOL:
-        if ( stage == CONTROL_STAGE_SETUP )
-        {
+        if (stage == CONTROL_STAGE_SETUP) {
           tud_control_status(rhport, request);
-        }
-        else if ( stage == CONTROL_STAGE_ACK )
-        {
+        } else if (stage == CONTROL_STAGE_ACK) {
           p_hid->protocol_mode = (uint8_t) request->wValue;
-          if (tud_hid_set_protocol_cb)
-          {
-            tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
-          }
+          tud_hid_set_protocol_cb(hid_itf, p_hid->protocol_mode);
         }
-      break;
+        break;
 
-      default: return false; // stall unsupported request
+      default:
+        return false; // stall unsupported request
     }
-  }else
-  {
+  } else {
     return false; // stall unsupported request
   }
 
   return true;
 }
 
-bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
-  (void) result;
-
+bool hidd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
   uint8_t instance = 0;
-  hidd_interface_t * p_hid = _hidd_itf;
+  hidd_interface_t *p_hid = _hidd_itf;
 
   // Identify which interface to use
-  for (instance = 0; instance < CFG_TUD_HID; instance++)
-  {
+  for (instance = 0; instance < CFG_TUD_HID; instance++) {
     p_hid = &_hidd_itf[instance];
-    if ( (ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in) ) break;
+    if ((ep_addr == p_hid->ep_out) || (ep_addr == p_hid->ep_in)) {
+      break;
+    }
   }
   TU_ASSERT(instance < CFG_TUD_HID);
 
-  // Sent report successfully
-  if (ep_addr == p_hid->ep_in)
-  {
-    if (tud_hid_report_complete_cb)
-    {
+  if (ep_addr == p_hid->ep_in) {
+    // Input report
+    if (XFER_RESULT_SUCCESS == result) {
       tud_hid_report_complete_cb(instance, p_hid->epin_buf, (uint16_t) xferred_bytes);
+    } else {
+      tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_INPUT, p_hid->epin_buf, (uint16_t) xferred_bytes);
     }
-  }
-  // Received report
-  else if (ep_addr == p_hid->ep_out)
-  {
-    tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_INVALID, p_hid->epout_buf, (uint16_t) xferred_bytes);
+  } else {
+    // Output report
+    if (XFER_RESULT_SUCCESS == result) {
+      tud_hid_set_report_cb(instance, 0, HID_REPORT_TYPE_OUTPUT, p_hid->epout_buf, (uint16_t)xferred_bytes);
+    } else {
+      tud_hid_report_failed_cb(instance, HID_REPORT_TYPE_OUTPUT, p_hid->epout_buf, (uint16_t) xferred_bytes);
+    }
+
+    // prepare for new transfer
     TU_ASSERT(usbd_edpt_xfer(rhport, p_hid->ep_out, p_hid->epout_buf, sizeof(p_hid->epout_buf)));
   }
 
diff --git a/src/class/hid/hid_device.h b/src/class/hid/hid_device.h
index 040cad162..89c28e061 100644
--- a/src/class/hid/hid_device.h
+++ b/src/class/hid/hid_device.h
@@ -24,8 +24,8 @@
  * This file is part of the TinyUSB stack.
  */
 
-#ifndef _TUSB_HID_DEVICE_H_
-#define _TUSB_HID_DEVICE_H_
+#ifndef TUSB_HID_DEVICE_H_
+#define TUSB_HID_DEVICE_H_
 
 #include "hid.h"
 
@@ -48,8 +48,7 @@
 #endif
 
 //--------------------------------------------------------------------+
-// Application API (Multiple Instances)
-// CFG_TUD_HID > 1
+// Application API (Multiple Instances) i.e. CFG_TUD_HID > 1
 //--------------------------------------------------------------------+
 
 // Check if the interface is ready to use
@@ -76,12 +75,6 @@ bool tud_hid_n_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons
 // use template layout report as defined by hid_abs_mouse_report_t
 bool tud_hid_n_abs_mouse_report(uint8_t instance, uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal);
 
-
-static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal)
-{
-  return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
-}
-
 // Gamepad: convenient helper to send gamepad report if application
 // use template layout report TUD_HID_REPORT_DESC_GAMEPAD
 bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
@@ -89,16 +82,40 @@ bool tud_hid_n_gamepad_report(uint8_t instance, uint8_t report_id, int8_t x, int
 //--------------------------------------------------------------------+
 // Application API (Single Port)
 //--------------------------------------------------------------------+
-static inline bool    tud_hid_ready(void);
-static inline uint8_t tud_hid_interface_protocol(void);
-static inline uint8_t tud_hid_get_protocol(void);
-static inline bool    tud_hid_report(uint8_t report_id, void const* report, uint16_t len);
-static inline bool    tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]);
-static inline bool    tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
-static inline bool    tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons);
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_ready(void) {
+  return tud_hid_n_ready(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_interface_protocol(void) {
+  return tud_hid_n_interface_protocol(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint8_t tud_hid_get_protocol(void) {
+  return tud_hid_n_get_protocol(0);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len) {
+  return tud_hid_n_report(0, report_id, report, len);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6]) {
+  return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal) {
+  return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_abs_mouse_report(uint8_t report_id, uint8_t buttons, int16_t x, int16_t y, int8_t vertical, int8_t horizontal) {
+  return tud_hid_n_abs_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons) {
+  return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons);
+}
 
 //--------------------------------------------------------------------+
-// Callbacks (Weak is optional)
+// Application Callbacks
 //--------------------------------------------------------------------+
 
 // Invoked when received GET HID REPORT DESCRIPTOR request
@@ -111,61 +128,25 @@ uint8_t const * tud_hid_descriptor_report_cb(uint8_t instance);
 uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen);
 
 // Invoked when received SET_REPORT control request or
-// received data on OUT endpoint ( Report ID = 0, Type = 0 )
+// received data on OUT endpoint (Report ID = 0, Type = OUTPUT)
 void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize);
 
 // Invoked when received SET_PROTOCOL request
 // protocol is either HID_PROTOCOL_BOOT (0) or HID_PROTOCOL_REPORT (1)
-TU_ATTR_WEAK void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol);
+void tud_hid_set_protocol_cb(uint8_t instance, uint8_t protocol);
 
 // Invoked when received SET_IDLE request. return false will stall the request
-// - Idle Rate = 0 : only send report if there is changes, i.e skip duplication
+// - Idle Rate = 0 : only send report if there is changes, i.e. skip duplication
 // - Idle Rate > 0 : skip duplication, but send at least 1 report every idle rate (in unit of 4 ms).
-TU_ATTR_WEAK bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate);
+bool tud_hid_set_idle_cb(uint8_t instance, uint8_t idle_rate);
 
 // Invoked when sent REPORT successfully to host
 // Application can use this to send the next report
 // Note: For composite reports, report[0] is report ID
-TU_ATTR_WEAK void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len);
+void tud_hid_report_complete_cb(uint8_t instance, uint8_t const* report, uint16_t len);
 
-
-//--------------------------------------------------------------------+
-// Inline Functions
-//--------------------------------------------------------------------+
-static inline bool tud_hid_ready(void)
-{
-  return tud_hid_n_ready(0);
-}
-
-static inline uint8_t tud_hid_interface_protocol(void)
-{
-  return tud_hid_n_interface_protocol(0);
-}
-
-static inline uint8_t tud_hid_get_protocol(void)
-{
-  return tud_hid_n_get_protocol(0);
-}
-
-static inline bool tud_hid_report(uint8_t report_id, void const* report, uint16_t len)
-{
-  return tud_hid_n_report(0, report_id, report, len);
-}
-
-static inline bool tud_hid_keyboard_report(uint8_t report_id, uint8_t modifier, uint8_t keycode[6])
-{
-  return tud_hid_n_keyboard_report(0, report_id, modifier, keycode);
-}
-
-static inline bool tud_hid_mouse_report(uint8_t report_id, uint8_t buttons, int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
-{
-  return tud_hid_n_mouse_report(0, report_id, buttons, x, y, vertical, horizontal);
-}
-
-static inline bool  tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y, int8_t z, int8_t rz, int8_t rx, int8_t ry, uint8_t hat, uint32_t buttons)
-{
-  return tud_hid_n_gamepad_report(0, report_id, x, y, z, rz, rx, ry, hat, buttons);
-}
+// Invoked when a transfer wasn't successful
+void tud_hid_report_failed_cb(uint8_t instance, hid_report_type_t report_type, uint8_t const* report, uint16_t xferred_bytes);
 
 /* --------------------------------------------------------------------+
  * HID Report Descriptor Template
@@ -461,6 +442,178 @@ static inline bool  tud_hid_gamepad_report(uint8_t report_id, int8_t x, int8_t y
       HID_OUTPUT      ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE  ),\
     HID_COLLECTION_END \
 
+// HID Lighting and Illumination Report Descriptor Template
+// - 1st parameter is report id (required)
+//   Creates 6 report ids for lighting HID usages in the following order:
+//     report_id+0: HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT
+//     report_id+1: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT
+//     report_id+2: HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT
+//     report_id+3: HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT
+//     report_id+4: HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT
+//     report_id+5: HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT
+#define TUD_HID_REPORT_DESC_LIGHTING(report_id) \
+  HID_USAGE_PAGE ( HID_USAGE_PAGE_LIGHTING_AND_ILLUMINATION ),\
+  HID_USAGE      ( HID_USAGE_LIGHTING_LAMP_ARRAY            ),\
+  HID_COLLECTION ( HID_COLLECTION_APPLICATION               ),\
+    /* Lamp Array Attributes Report */ \
+    HID_REPORT_ID (report_id                                    ) \
+    HID_USAGE ( HID_USAGE_LIGHTING_LAMP_ARRAY_ATTRIBUTES_REPORT ),\
+    HID_COLLECTION ( HID_COLLECTION_LOGICAL                     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_COUNT                          ),\
+      HID_LOGICAL_MIN   ( 0                                                      ),\
+      HID_LOGICAL_MAX_N ( 65535, 3                                               ),\
+      HID_REPORT_SIZE   ( 16                                                     ),\
+      HID_REPORT_COUNT  ( 1                                                      ),\
+      HID_FEATURE       ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE             ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BOUNDING_BOX_WIDTH_IN_MICROMETERS   ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BOUNDING_BOX_HEIGHT_IN_MICROMETERS  ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BOUNDING_BOX_DEPTH_IN_MICROMETERS   ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_ARRAY_KIND                     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_MIN_UPDATE_INTERVAL_IN_MICROSECONDS ),\
+      HID_LOGICAL_MIN   ( 0                                                      ),\
+      HID_LOGICAL_MAX_N ( 2147483647, 3                                          ),\
+      HID_REPORT_SIZE   ( 32                                                     ),\
+      HID_REPORT_COUNT  ( 5                                                      ),\
+      HID_FEATURE       ( HID_CONSTANT | HID_VARIABLE | HID_ABSOLUTE             ),\
+    HID_COLLECTION_END ,\
+    /* Lamp Attributes Request Report */ \
+    HID_REPORT_ID       ( report_id + 1                                     ) \
+    HID_USAGE           ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_REQUEST_REPORT ),\
+    HID_COLLECTION      ( HID_COLLECTION_LOGICAL                            ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_ID             ),\
+      HID_LOGICAL_MIN   ( 0                                      ),\
+      HID_LOGICAL_MAX_N ( 65535, 3                               ),\
+      HID_REPORT_SIZE   ( 16                                     ),\
+      HID_REPORT_COUNT  ( 1                                      ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+    HID_COLLECTION_END ,\
+    /* Lamp Attributes Response Report */ \
+    HID_REPORT_ID       ( report_id + 2                                      ) \
+    HID_USAGE           ( HID_USAGE_LIGHTING_LAMP_ATTRIBUTES_RESPONSE_REPORT ),\
+    HID_COLLECTION      ( HID_COLLECTION_LOGICAL                             ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_ID                        ),\
+      HID_LOGICAL_MIN   ( 0                                                 ),\
+      HID_LOGICAL_MAX_N ( 65535, 3                                          ),\
+      HID_REPORT_SIZE   ( 16                                                ),\
+      HID_REPORT_COUNT  ( 1                                                 ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE            ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_POSITION_X_IN_MICROMETERS      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_POSITION_Y_IN_MICROMETERS      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_POSITION_Z_IN_MICROMETERS      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_UPDATE_LATENCY_IN_MICROSECONDS ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_PURPOSES                  ),\
+      HID_LOGICAL_MIN   ( 0                                                 ),\
+      HID_LOGICAL_MAX_N ( 2147483647, 3                                     ),\
+      HID_REPORT_SIZE   ( 32                                                ),\
+      HID_REPORT_COUNT  ( 5                                                 ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE            ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_LEVEL_COUNT                ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_LEVEL_COUNT              ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_LEVEL_COUNT               ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_LEVEL_COUNT          ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_IS_PROGRAMMABLE                ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INPUT_BINDING                  ),\
+      HID_LOGICAL_MIN   ( 0                                                 ),\
+      HID_LOGICAL_MAX_N ( 255, 2                                            ),\
+      HID_REPORT_SIZE   ( 8                                                 ),\
+      HID_REPORT_COUNT  ( 6                                                 ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE            ),\
+    HID_COLLECTION_END ,\
+    /* Lamp Multi-Update Report */ \
+    HID_REPORT_ID       ( report_id + 3                               ) \
+    HID_USAGE           ( HID_USAGE_LIGHTING_LAMP_MULTI_UPDATE_REPORT ),\
+    HID_COLLECTION      ( HID_COLLECTION_LOGICAL                      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_COUNT               ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS        ),\
+      HID_LOGICAL_MIN   ( 0                                           ),\
+      HID_LOGICAL_MAX   ( 8                                           ),\
+      HID_REPORT_SIZE   ( 8                                           ),\
+      HID_REPORT_COUNT  ( 2                                           ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_ID                  ),\
+      HID_LOGICAL_MIN   ( 0                                           ),\
+      HID_LOGICAL_MAX_N ( 65535, 3                                    ),\
+      HID_REPORT_SIZE   ( 16                                          ),\
+      HID_REPORT_COUNT  ( 8                                           ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL       ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL       ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL       ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL       ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL       ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL       ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL       ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL       ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+      HID_LOGICAL_MIN   ( 0                                           ),\
+      HID_LOGICAL_MAX_N ( 255, 2                                      ),\
+      HID_REPORT_SIZE   ( 8                                           ),\
+      HID_REPORT_COUNT  ( 32                                          ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE      ),\
+    HID_COLLECTION_END ,\
+    /* Lamp Range Update Report */ \
+    HID_REPORT_ID       ( report_id + 4 ) \
+    HID_USAGE           ( HID_USAGE_LIGHTING_LAMP_RANGE_UPDATE_REPORT ),\
+    HID_COLLECTION      ( HID_COLLECTION_LOGICAL                      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_UPDATE_FLAGS        ),\
+      HID_LOGICAL_MIN   ( 0                                           ),\
+      HID_LOGICAL_MAX   ( 8                                           ),\
+      HID_REPORT_SIZE   ( 8                                           ),\
+      HID_REPORT_COUNT  ( 1                                           ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_ID_START            ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_LAMP_ID_END              ),\
+      HID_LOGICAL_MIN   ( 0                                           ),\
+      HID_LOGICAL_MAX_N ( 65535, 3                                    ),\
+      HID_REPORT_SIZE   ( 16                                          ),\
+      HID_REPORT_COUNT  ( 2                                           ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_RED_UPDATE_CHANNEL       ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_GREEN_UPDATE_CHANNEL     ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_BLUE_UPDATE_CHANNEL      ),\
+      HID_USAGE         ( HID_USAGE_LIGHTING_INTENSITY_UPDATE_CHANNEL ),\
+      HID_LOGICAL_MIN   ( 0                                           ),\
+      HID_LOGICAL_MAX_N ( 255, 2                                      ),\
+      HID_REPORT_SIZE   ( 8                                           ),\
+      HID_REPORT_COUNT  ( 4                                           ),\
+      HID_FEATURE       ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE      ),\
+    HID_COLLECTION_END ,\
+    /* Lamp Array Control Report */ \
+    HID_REPORT_ID      ( report_id + 5                                ) \
+    HID_USAGE          ( HID_USAGE_LIGHTING_LAMP_ARRAY_CONTROL_REPORT ),\
+    HID_COLLECTION     ( HID_COLLECTION_LOGICAL                       ),\
+      HID_USAGE        ( HID_USAGE_LIGHTING_AUTONOMOUS_MODE     ),\
+      HID_LOGICAL_MIN  ( 0                                      ),\
+      HID_LOGICAL_MAX  ( 1                                      ),\
+      HID_REPORT_SIZE  ( 8                                      ),\
+      HID_REPORT_COUNT ( 1                                      ),\
+      HID_FEATURE      ( HID_DATA | HID_VARIABLE | HID_ABSOLUTE ),\
+    HID_COLLECTION_END ,\
+  HID_COLLECTION_END \
+
 //--------------------------------------------------------------------+
 // Internal Class Driver API
 //--------------------------------------------------------------------+
@@ -475,4 +628,4 @@ bool     hidd_xfer_cb         (uint8_t rhport, uint8_t ep_addr, xfer_result_t ev
  }
 #endif
 
-#endif /* _TUSB_HID_DEVICE_H_ */
+#endif
diff --git a/src/class/hid/hid_host.c b/src/class/hid/hid_host.c
index 621fb2a55..7639a8fc6 100644
--- a/src/class/hid/hid_host.c
+++ b/src/class/hid/hid_host.c
@@ -222,6 +222,50 @@ bool tuh_hid_set_protocol(uint8_t daddr, uint8_t idx, uint8_t protocol) {
   return _hidh_set_protocol(daddr, p_hid->itf_num, protocol, set_protocol_complete, 0);
 }
 
+static void get_report_complete(tuh_xfer_t* xfer) {
+  TU_LOG_DRV("HID Get Report complete\r\n");
+
+  if (tuh_hid_get_report_complete_cb) {
+    uint8_t const itf_num = (uint8_t) tu_le16toh(xfer->setup->wIndex);
+    uint8_t const idx = tuh_hid_itf_get_index(xfer->daddr, itf_num);
+
+    uint8_t const report_type = tu_u16_high(xfer->setup->wValue);
+    uint8_t const report_id = tu_u16_low(xfer->setup->wValue);
+
+    tuh_hid_get_report_complete_cb(xfer->daddr, idx, report_id, report_type,
+                                   (xfer->result == XFER_RESULT_SUCCESS) ? xfer->setup->wLength : 0);
+  }
+}
+
+bool tuh_hid_get_report(uint8_t daddr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len) {
+  hidh_interface_t* p_hid = get_hid_itf(daddr, idx);
+  TU_VERIFY(p_hid);
+  TU_LOG_DRV("HID Get Report: id = %u, type = %u, len = %u\r\n", report_id, report_type, len);
+
+  tusb_control_request_t const request = {
+      .bmRequestType_bit = {
+          .recipient = TUSB_REQ_RCPT_INTERFACE,
+          .type      = TUSB_REQ_TYPE_CLASS,
+          .direction = TUSB_DIR_IN
+      },
+      .bRequest = HID_REQ_CONTROL_GET_REPORT,
+      .wValue   = tu_htole16(tu_u16(report_type, report_id)),
+      .wIndex   = tu_htole16((uint16_t) p_hid->itf_num),
+      .wLength  = len
+  };
+
+  tuh_xfer_t xfer = {
+      .daddr       = daddr,
+      .ep_addr     = 0,
+      .setup       = &request,
+      .buffer      = report,
+      .complete_cb = get_report_complete,
+      .user_data   = 0
+  };
+
+  return tuh_control_xfer(&xfer);
+}
+
 static void set_report_complete(tuh_xfer_t* xfer) {
   TU_LOG_DRV("HID Set Report complete\r\n");
 
@@ -613,7 +657,9 @@ uint8_t tuh_hid_parse_report_descriptor(tuh_hid_report_info_t* report_info_arr,
     uint8_t const data8 = desc_report[0];
 
     TU_LOG(3, "tag = %d, type = %d, size = %d, data = ", tag, type, size);
-    for (uint32_t i = 0; i < size; i++) TU_LOG(3, "%02X ", desc_report[i]);
+    for (uint32_t i = 0; i < size; i++) {
+      TU_LOG(3, "%02X ", desc_report[i]);
+    }
     TU_LOG(3, "\r\n");
 
     switch (type) {
diff --git a/src/class/hid/hid_host.h b/src/class/hid/hid_host.h
index 0902bf1af..9681c704b 100644
--- a/src/class/hid/hid_host.h
+++ b/src/class/hid/hid_host.h
@@ -105,6 +105,10 @@ void tuh_hid_set_default_protocol(uint8_t protocol);
 // This function is only supported by Boot interface (tuh_n_hid_interface_protocol() != NONE)
 bool tuh_hid_set_protocol(uint8_t dev_addr, uint8_t idx, uint8_t protocol);
 
+// Get Report using control endpoint
+// report_type is either Input, Output or Feature, (value from hid_report_type_t)
+bool tuh_hid_get_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, void* report, uint16_t len);
+
 // Set Report using control endpoint
 // report_type is either Input, Output or Feature, (value from hid_report_type_t)
 bool tuh_hid_set_report(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type,
@@ -153,6 +157,10 @@ void tuh_hid_report_received_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* re
 // Invoked when sent report to device successfully via interrupt endpoint
 TU_ATTR_WEAK void tuh_hid_report_sent_cb(uint8_t dev_addr, uint8_t idx, uint8_t const* report, uint16_t len);
 
+// Invoked when Get Report to device via either control endpoint
+// len = 0 indicate there is error in the transfer e.g stalled response
+TU_ATTR_WEAK void tuh_hid_get_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
+
 // Invoked when Sent Report to device via either control endpoint
 // len = 0 indicate there is error in the transfer e.g stalled response
 TU_ATTR_WEAK void tuh_hid_set_report_complete_cb(uint8_t dev_addr, uint8_t idx, uint8_t report_id, uint8_t report_type, uint16_t len);
diff --git a/src/class/msc/msc_device.c b/src/class/msc/msc_device.c
index 588fcaaca..447560b4d 100644
--- a/src/class/msc/msc_device.c
+++ b/src/class/msc/msc_device.c
@@ -689,6 +689,24 @@ static int32_t proc_builtin_scsi(uint8_t lun, uint8_t const scsi_cmd[16], uint8_
       }
     break;
 
+    case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
+      resplen = 0;
+
+      if (tud_msc_prevent_allow_medium_removal_cb)
+      {
+         scsi_prevent_allow_medium_removal_t const * prevent_allow = (scsi_prevent_allow_medium_removal_t const *) scsi_cmd;
+        if ( !tud_msc_prevent_allow_medium_removal_cb(lun, prevent_allow->prohibit_removal, prevent_allow->control) )
+        {
+          // Failed status response
+          resplen = - 1;
+
+          // set default sense if not set by callback
+          if ( p_msc->sense_key == 0 ) set_sense_medium_not_present(lun);
+        }
+      }
+    break;
+
+
     case SCSI_CMD_READ_CAPACITY_10:
     {
       uint32_t block_count;
diff --git a/src/class/msc/msc_device.h b/src/class/msc/msc_device.h
index 4247a40bd..29acd280a 100644
--- a/src/class/msc/msc_device.h
+++ b/src/class/msc/msc_device.h
@@ -131,6 +131,9 @@ TU_ATTR_WEAK uint8_t tud_msc_get_maxlun_cb(void);
 // - Start = 1 : active mode, if load_eject = 1 : load disk storage
 TU_ATTR_WEAK bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject);
 
+//Invoked when we receive the Prevent / Allow Medium Removal command
+TU_ATTR_WEAK bool tud_msc_prevent_allow_medium_removal_cb(uint8_t lun, uint8_t prohibit_removal, uint8_t control);
+
 // Invoked when received REQUEST_SENSE
 TU_ATTR_WEAK int32_t tud_msc_request_sense_cb(uint8_t lun, void* buffer, uint16_t bufsize);
 
diff --git a/src/class/net/ncm.h b/src/class/net/ncm.h
index 96ba11fbc..1b987fca0 100644
--- a/src/class/net/ncm.h
+++ b/src/class/net/ncm.h
@@ -2,6 +2,7 @@
  * The MIT License (MIT)
  *
  * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ * Copyright (c) 2024, Hardy Griech
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -24,22 +25,58 @@
  * This file is part of the TinyUSB stack.
  */
 
-
 #ifndef _TUSB_NCM_H_
 #define _TUSB_NCM_H_
 
 #include "common/tusb_common.h"
 
-#ifdef __cplusplus
- extern "C" {
+// NTB buffers size for reception side, must be >> MTU to avoid TCP retransmission (driver issue ?)
+// Linux use 2048 as minimal size
+#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
+  #define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
 #endif
 
-// Table 4.3 Data Class Interface Protocol Codes
-typedef enum
-{
-  NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
-} ncm_data_interface_protocol_code_t;
+// NTB buffers size for reception side, must be > MTU
+// Linux use 2048 as minimal size
+#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
+  #define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
+#endif
 
+// Number of NTB buffers for reception side
+// Depending on the configuration, this parameter could be increased with the cost of additional RAM requirements
+// On Full-Speed (RP2040) :
+//    1  - good performance
+//    2  - up to 30% more performance with iperf with small packets
+//    >2 - no performance gain
+// On High-Speed (STM32F7) :
+//    No performance gain
+#ifndef CFG_TUD_NCM_OUT_NTB_N
+  #define CFG_TUD_NCM_OUT_NTB_N 1
+#endif
+
+// Number of NTB buffers for transmission side
+// Depending on the configuration, this parameter could be increased with the cost of additional RAM requirements
+// On Full-Speed (RP2040) :
+//    1 - good performance but SystemView shows lost events (on load test)
+//    2 - up to 50% more performance with iperf with small packets, "tud_network_can_xmit: request blocked"
+//        happens from time to time with SystemView
+//    3 - "tud_network_can_xmit: request blocked" never happens
+//    >3 - no performance gain
+// On High-Speed (STM32F7) :
+//    No performance gain
+#ifndef CFG_TUD_NCM_IN_NTB_N
+  #define CFG_TUD_NCM_IN_NTB_N 1
+#endif
+
+// How many datagrams it is allowed to put into an NTB for transmission side
+#ifndef CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB
+  #define CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB 8
+#endif
+
+// This tells the host how many datagrams it is allowed to put into an NTB
+#ifndef CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB
+  #define CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB 6
+#endif
 
 // Table 6.2 Class-Specific Request Codes for Network Control Model subclass
 typedef enum
@@ -62,8 +99,65 @@ typedef enum
   NCM_SET_CRC_MODE                                 = 0x8A,
 } ncm_request_code_t;
 
-#ifdef __cplusplus
- }
-#endif
+#define NTH16_SIGNATURE 0x484D434E
+#define NDP16_SIGNATURE_NCM0 0x304D434E
+#define NDP16_SIGNATURE_NCM1 0x314D434E
+
+typedef struct TU_ATTR_PACKED {
+  uint16_t wLength;
+  uint16_t bmNtbFormatsSupported;
+  uint32_t dwNtbInMaxSize;
+  uint16_t wNdbInDivisor;
+  uint16_t wNdbInPayloadRemainder;
+  uint16_t wNdbInAlignment;
+  uint16_t wReserved;
+  uint32_t dwNtbOutMaxSize;
+  uint16_t wNdbOutDivisor;
+  uint16_t wNdbOutPayloadRemainder;
+  uint16_t wNdbOutAlignment;
+  uint16_t wNtbOutMaxDatagrams;
+} ntb_parameters_t;
+
+typedef struct TU_ATTR_PACKED {
+  uint32_t dwSignature;
+  uint16_t wHeaderLength;
+  uint16_t wSequence;
+  uint16_t wBlockLength;
+  uint16_t wNdpIndex;
+} nth16_t;
+
+typedef struct TU_ATTR_PACKED {
+  uint16_t wDatagramIndex;
+  uint16_t wDatagramLength;
+} ndp16_datagram_t;
+
+typedef struct TU_ATTR_PACKED {
+  uint32_t dwSignature;
+  uint16_t wLength;
+  uint16_t wNextNdpIndex;
+  //ndp16_datagram_t datagram[];
+} ndp16_t;
+
+typedef union TU_ATTR_PACKED {
+  struct {
+    nth16_t nth;
+    ndp16_t ndp;
+    ndp16_datagram_t ndp_datagram[CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB + 1];
+  };
+  uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
+} xmit_ntb_t;
+
+typedef union TU_ATTR_PACKED {
+  struct {
+    nth16_t nth;
+    // only the header is at a guaranteed position
+  };
+  uint8_t data[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
+} recv_ntb_t;
+
+struct ncm_notify_t {
+  tusb_control_request_t header;
+  uint32_t downlink, uplink;
+};
 
 #endif
diff --git a/src/class/net/ncm_device.c b/src/class/net/ncm_device.c
index f84bd9f73..90d747185 100644
--- a/src/class/net/ncm_device.c
+++ b/src/class/net/ncm_device.c
@@ -1,9 +1,10 @@
 /*
  * The MIT License (MIT)
  *
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ * Copyright (c) 2024 Hardy Griech
  * Copyright (c) 2020 Jacob Berg Potter
  * Copyright (c) 2020 Peter Lawrence
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -26,12 +27,37 @@
  * This file is part of the TinyUSB stack.
  */
 
+/**
+ * Small Glossary (from the spec)
+ * --------------
+ * Datagram - A collection of bytes forming a single item of information, passed as a unit from source to destination.
+ * NCM      - Network Control Model
+ * NDP      - NCM Datagram Pointer: NTB structure that delineates Datagrams (typically Ethernet frames) within an NTB
+ * NTB      - NCM Transfer Block: a data structure for efficient USB encapsulation of one or more datagrams
+ *            Each NTB is designed to be a single USB transfer
+ * NTH      - NTB Header: a data structure at the front of each NTB, which provides the information needed to validate
+ *            the NTB and begin decoding
+ *
+ * Some explanations
+ * -----------------
+ * - rhport        is the USB port of the device, in most cases "0"
+ * - itf_data_alt  if != 0 -> data xmit/recv are allowed (see spec)
+ * - ep_in         IN endpoints take data from the device intended to go in to the host (the device transmits)
+ * - ep_out        OUT endpoints send data out of the host to the device (the device receives)
+ */
+
 #include "tusb_option.h"
 
-#if ( CFG_TUD_ENABLED && CFG_TUD_NCM )
+#if (CFG_TUD_ENABLED && CFG_TUD_NCM)
+
+#include 
+#include 
+#include 
 
 #include "device/usbd.h"
 #include "device/usbd_pvt.h"
+
+#include "ncm.h"
 #include "net_device.h"
 
 // Level where CFG_TUSB_DEBUG must be at least for this driver is logged
@@ -41,482 +67,829 @@
 
 #define TU_LOG_DRV(...)   TU_LOG(CFG_TUD_NCM_LOG_LEVEL, __VA_ARGS__)
 
-//--------------------------------------------------------------------+
-// MACRO CONSTANT TYPEDEF
-//--------------------------------------------------------------------+
+// Alignment must be 4
+#define TUD_NCM_ALIGNMENT   4
+// calculate alignment of xmit datagrams within an NTB
+#define XMIT_ALIGN_OFFSET(x) ((TUD_NCM_ALIGNMENT - ((x) & (TUD_NCM_ALIGNMENT - 1))) & (TUD_NCM_ALIGNMENT - 1))
 
-#define NTH16_SIGNATURE      0x484D434E
-#define NDP16_SIGNATURE_NCM0 0x304D434E
-#define NDP16_SIGNATURE_NCM1 0x314D434E
+//-----------------------------------------------------------------------------
+//
+// Module global things
+//
+#define XMIT_NTB_N CFG_TUD_NCM_IN_NTB_N
+#define RECV_NTB_N CFG_TUD_NCM_OUT_NTB_N
 
-typedef struct TU_ATTR_PACKED
-{
-  uint16_t wLength;
-  uint16_t bmNtbFormatsSupported;
-  uint32_t dwNtbInMaxSize;
-  uint16_t wNdbInDivisor;
-  uint16_t wNdbInPayloadRemainder;
-  uint16_t wNdbInAlignment;
-  uint16_t wReserved;
-  uint32_t dwNtbOutMaxSize;
-  uint16_t wNdbOutDivisor;
-  uint16_t wNdbOutPayloadRemainder;
-  uint16_t wNdbOutAlignment;
-  uint16_t wNtbOutMaxDatagrams;
-} ntb_parameters_t;
+typedef struct {
+  // general
+  uint8_t ep_in;        // endpoint for outgoing datagrams (naming is a little bit confusing)
+  uint8_t ep_out;       // endpoint for incoming datagrams (naming is a little bit confusing)
+  uint8_t ep_notif;     // endpoint for notifications
+  uint8_t itf_num;      // interface number
+  uint8_t itf_data_alt; // ==0 -> no endpoints, i.e. no network traffic, ==1 -> normal operation with two endpoints (spec, chapter 5.3)
+  uint8_t rhport;       // storage of \a rhport because some callbacks are done without it
 
-typedef struct TU_ATTR_PACKED
-{
-  uint32_t dwSignature;
-  uint16_t wHeaderLength;
-  uint16_t wSequence;
-  uint16_t wBlockLength;
-  uint16_t wNdpIndex;
-} nth16_t;
+  // recv handling
+  CFG_TUSB_MEM_ALIGN recv_ntb_t recv_ntb[RECV_NTB_N];   // actual recv NTBs
+  recv_ntb_t *recv_free_ntb[RECV_NTB_N];                // free list of recv NTBs
+  recv_ntb_t *recv_ready_ntb[RECV_NTB_N];               // NTBs waiting for transmission to glue logic
+  recv_ntb_t *recv_tinyusb_ntb;                         // buffer for the running transfer TinyUSB -> driver
+  recv_ntb_t *recv_glue_ntb;                            // buffer for the running transfer driver -> glue logic
+  uint16_t recv_glue_ntb_datagram_ndx;                  // index into \a recv_glue_ntb_datagram
 
-typedef struct TU_ATTR_PACKED
-{
-  uint16_t wDatagramIndex;
-  uint16_t wDatagramLength;
-} ndp16_datagram_t;
-
-typedef struct TU_ATTR_PACKED
-{
-  uint32_t dwSignature;
-  uint16_t wLength;
-  uint16_t wNextNdpIndex;
-  ndp16_datagram_t datagram[];
-} ndp16_t;
-
-typedef union TU_ATTR_PACKED {
-  struct {
-    nth16_t nth;
-    ndp16_t ndp;
-  };
-  uint8_t data[CFG_TUD_NCM_IN_NTB_MAX_SIZE];
-} transmit_ntb_t;
-
-struct ecm_notify_struct
-{
-  tusb_control_request_t header;
-  uint32_t downlink, uplink;
-};
-
-typedef struct
-{
-  uint8_t itf_num;      // Index number of Management Interface, +1 for Data Interface
-  uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
-
-  uint8_t ep_notif;
-  uint8_t ep_in;
-  uint8_t ep_out;
-
-  const ndp16_t *ndp;
-  uint8_t num_datagrams, current_datagram_index;
+  // xmit handling
+  CFG_TUSB_MEM_ALIGN xmit_ntb_t xmit_ntb[XMIT_NTB_N];   // actual xmit NTBs
+  xmit_ntb_t *xmit_free_ntb[XMIT_NTB_N];                // free list of xmit NTBs
+  xmit_ntb_t *xmit_ready_ntb[XMIT_NTB_N];               // NTBs waiting for transmission to TinyUSB
+  xmit_ntb_t *xmit_tinyusb_ntb;                         // buffer for the running transfer driver -> TinyUSB
+  xmit_ntb_t *xmit_glue_ntb;                            // buffer for the running transfer glue logic -> driver
+  uint16_t xmit_sequence;                               // NTB sequence counter
+  uint16_t xmit_glue_ntb_datagram_ndx;                  // index into \a xmit_glue_ntb_datagram
 
+  // notification handling
   enum {
-    REPORT_SPEED,
-    REPORT_CONNECTED,
-    REPORT_DONE
-  } report_state;
-  bool report_pending;
-
-  uint8_t  current_ntb;           // Index in transmit_ntb[] that is currently being filled with datagrams
-  uint8_t  datagram_count;        // Number of datagrams in transmit_ntb[current_ntb]
-  uint16_t next_datagram_offset;  // Offset in transmit_ntb[current_ntb].data to place the next datagram
-  uint16_t ntb_in_size;           // Maximum size of transmitted (IN to host) NTBs; initially CFG_TUD_NCM_IN_NTB_MAX_SIZE
-  uint8_t  max_datagrams_per_ntb; // Maximum number of datagrams per NTB; initially CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
-
-  uint16_t nth_sequence;          // Sequence number counter for transmitted NTBs
-
-  bool transferring;
-
+    NOTIFICATION_SPEED,
+    NOTIFICATION_CONNECTED,
+    NOTIFICATION_DONE
+  } notification_xmit_state;            // state of notification transmission
+  bool notification_xmit_is_running;    // notification is currently transmitted
 } ncm_interface_t;
 
-//--------------------------------------------------------------------+
-// INTERNAL OBJECT & FUNCTION DECLARATION
-//--------------------------------------------------------------------+
+CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static ncm_interface_t ncm_interface;
 
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters = {
-    .wLength                 = sizeof(ntb_parameters_t),
-    .bmNtbFormatsSupported   = 0x01,
-    .dwNtbInMaxSize          = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
-    .wNdbInDivisor           = 4,
-    .wNdbInPayloadRemainder  = 0,
-    .wNdbInAlignment         = CFG_TUD_NCM_ALIGNMENT,
-    .wReserved               = 0,
-    .dwNtbOutMaxSize         = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
-    .wNdbOutDivisor          = 4,
-    .wNdbOutPayloadRemainder = 0,
-    .wNdbOutAlignment        = CFG_TUD_NCM_ALIGNMENT,
-    .wNtbOutMaxDatagrams     = 0
+/**
+ * This is the NTB parameter structure
+ *
+ * \attention
+ *     We are lucky, that byte order is correct
+ */
+CFG_TUD_MEM_SECTION CFG_TUD_MEM_ALIGN tu_static const ntb_parameters_t ntb_parameters = {
+  .wLength                  = sizeof(ntb_parameters_t),
+  .bmNtbFormatsSupported    = 0x01,// 16-bit NTB supported
+  .dwNtbInMaxSize           = CFG_TUD_NCM_IN_NTB_MAX_SIZE,
+  .wNdbInDivisor            = 1,
+  .wNdbInPayloadRemainder   = 0,
+  .wNdbInAlignment          = TUD_NCM_ALIGNMENT,
+  .wReserved                = 0,
+  .dwNtbOutMaxSize          = CFG_TUD_NCM_OUT_NTB_MAX_SIZE,
+  .wNdbOutDivisor           = 1,
+  .wNdbOutPayloadRemainder  = 0,
+  .wNdbOutAlignment         = TUD_NCM_ALIGNMENT,
+  .wNtbOutMaxDatagrams      = CFG_TUD_NCM_OUT_MAX_DATAGRAMS_PER_NTB,
 };
 
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static transmit_ntb_t transmit_ntb[2];
+// Some confusing remarks about wNtbOutMaxDatagrams...
+//      ==1 -> SystemView packets/s goes up to 2000 and events are lost during startup
+//      ==0 -> SystemView runs fine, iperf shows in wireshark a lot of error
+//      ==6 -> SystemView runs fine, iperf also
+//      >6  -> iperf starts to show errors
+//      -> 6 seems to be the best value.  Why?  Don't know, perhaps only on my system?
+//
+//      iperf:    for MSS in 100 200 400 800 1200 1450 1500; do iperf -c 192.168.14.1 -e -i 1 -M $MSS -l 8192 -P 1; sleep 2; done
+//      sysview:  SYSTICKS_PER_SEC=35000, IDLE_US=1000, PRINT_MOD=1000
+//
 
-CFG_TUD_MEM_SECTION CFG_TUSB_MEM_ALIGN tu_static uint8_t receive_ntb[CFG_TUD_NCM_OUT_NTB_MAX_SIZE];
-
-tu_static ncm_interface_t ncm_interface;
-
-/*
- * Set up the NTB state in ncm_interface to be ready to add datagrams.
- */
-static void ncm_prepare_for_tx(void) {
-  ncm_interface.datagram_count = 0;
-  // datagrams start after all the headers
-  ncm_interface.next_datagram_offset = sizeof(nth16_t) + sizeof(ndp16_t)
-      + ((CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB + 1) * sizeof(ndp16_datagram_t));
-}
-
-/*
- * If not already transmitting, start sending the current NTB to the host and swap buffers
- * to start filling the other one with datagrams.
- */
-static void ncm_start_tx(void) {
-  if (ncm_interface.transferring) {
-    return;
-  }
-
-  transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
-  size_t ntb_length = ncm_interface.next_datagram_offset;
-
-  // Fill in NTB header
-  ntb->nth.dwSignature = NTH16_SIGNATURE;
-  ntb->nth.wHeaderLength = sizeof(nth16_t);
-  ntb->nth.wSequence = ncm_interface.nth_sequence++;
-  ntb->nth.wBlockLength = ntb_length;
-  ntb->nth.wNdpIndex = sizeof(nth16_t);
-
-  // Fill in NDP16 header and terminator
-  ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
-  ntb->ndp.wLength = sizeof(ndp16_t) + (ncm_interface.datagram_count + 1) * sizeof(ndp16_datagram_t);
-  ntb->ndp.wNextNdpIndex = 0;
-  ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = 0;
-  ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = 0;
-
-  // Kick off an endpoint transfer
-  usbd_edpt_xfer(0, ncm_interface.ep_in, ntb->data, ntb_length);
-  ncm_interface.transferring = true;
-
-  // Swap to the other NTB and clear it out
-  ncm_interface.current_ntb = 1 - ncm_interface.current_ntb;
-  ncm_prepare_for_tx();
-}
-
-tu_static struct ecm_notify_struct ncm_notify_connected =
-{
+//-----------------------------------------------------------------------------
+//
+// everything about notifications
+//
+tu_static struct ncm_notify_t ncm_notify_connected = {
     .header = {
         .bmRequestType_bit = {
-          .recipient = TUSB_REQ_RCPT_INTERFACE,
-          .type      = TUSB_REQ_TYPE_CLASS,
-          .direction = TUSB_DIR_IN
-        },
-        .bRequest      = CDC_NOTIF_NETWORK_CONNECTION,
-        .wValue        = 1 /* Connected */,
-        .wLength       = 0,
+            .recipient = TUSB_REQ_RCPT_INTERFACE,
+            .type = TUSB_REQ_TYPE_CLASS,
+            .direction = TUSB_DIR_IN},
+        .bRequest = CDC_NOTIF_NETWORK_CONNECTION,
+        .wValue = 1 /* Connected */,
+        .wLength = 0,
     },
 };
 
-tu_static struct ecm_notify_struct ncm_notify_speed_change =
-{
+tu_static struct ncm_notify_t ncm_notify_speed_change = {
     .header = {
         .bmRequestType_bit = {
-          .recipient = TUSB_REQ_RCPT_INTERFACE,
-          .type      = TUSB_REQ_TYPE_CLASS,
-          .direction = TUSB_DIR_IN
-        },
+            .recipient = TUSB_REQ_RCPT_INTERFACE,
+            .type = TUSB_REQ_TYPE_CLASS,
+            .direction = TUSB_DIR_IN},
         .bRequest = CDC_NOTIF_CONNECTION_SPEED_CHANGE,
         .wLength = 8,
     },
-    .downlink = 10000000,
-    .uplink = 10000000,
+    .downlink = TUD_OPT_HIGH_SPEED ? 480000000 : 12000000,
+    .uplink = TUD_OPT_HIGH_SPEED ? 480000000 : 12000000,
 };
 
-void tud_network_recv_renew(void)
-{
-  if (!ncm_interface.num_datagrams)
-  {
-    usbd_edpt_xfer(0, ncm_interface.ep_out, receive_ntb, sizeof(receive_ntb));
+/**
+ * Transmit next notification to the host (if appropriate).
+ * Notifications are transferred to the host once during connection setup.
+ */
+static void notification_xmit(uint8_t rhport, bool force_next) {
+  TU_LOG_DRV("notification_xmit(%d, %d) - %d %d\n", force_next, rhport, ncm_interface.notification_xmit_state, ncm_interface.notification_xmit_is_running);
+
+  if (!force_next && ncm_interface.notification_xmit_is_running) {
     return;
   }
 
-  const ndp16_t *ndp = ncm_interface.ndp;
-  const int i = ncm_interface.current_datagram_index;
-  ncm_interface.current_datagram_index++;
-  ncm_interface.num_datagrams--;
+  if (ncm_interface.notification_xmit_state == NOTIFICATION_SPEED) {
+    TU_LOG_DRV("  NOTIFICATION_SPEED\n");
+    ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num;
+    usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change));
+    ncm_interface.notification_xmit_state = NOTIFICATION_CONNECTED;
+    ncm_interface.notification_xmit_is_running = true;
+  } else if (ncm_interface.notification_xmit_state == NOTIFICATION_CONNECTED) {
+    TU_LOG_DRV("  NOTIFICATION_CONNECTED\n");
+    ncm_notify_connected.header.wIndex = ncm_interface.itf_num;
+    usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_connected, sizeof(ncm_notify_connected));
+    ncm_interface.notification_xmit_state = NOTIFICATION_DONE;
+    ncm_interface.notification_xmit_is_running = true;
+  } else {
+    TU_LOG_DRV("  NOTIFICATION_FINISHED\n");
+  }
+} // notification_xmit
 
-  tud_network_recv_cb(receive_ntb + ndp->datagram[i].wDatagramIndex, ndp->datagram[i].wDatagramLength);
-}
+//-----------------------------------------------------------------------------
+//
+// everything about packet transmission (driver -> TinyUSB)
+//
 
-//--------------------------------------------------------------------+
-// USBD Driver API
-//--------------------------------------------------------------------+
+/**
+ * Put NTB into the transmitter free list.
+ */
+static void xmit_put_ntb_into_free_list(xmit_ntb_t *free_ntb) {
+  TU_LOG_DRV("xmit_put_ntb_into_free_list() - %p\n", ncm_interface.xmit_tinyusb_ntb);
 
-void netd_init(void)
-{
-  tu_memclr(&ncm_interface, sizeof(ncm_interface));
-  ncm_interface.ntb_in_size = CFG_TUD_NCM_IN_NTB_MAX_SIZE;
-  ncm_interface.max_datagrams_per_ntb = CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB;
-  ncm_prepare_for_tx();
-}
+  if (free_ntb == NULL) { // can happen due to ZLPs
+    return;
+  }
 
+  for (int i = 0; i < XMIT_NTB_N; ++i) {
+    if (ncm_interface.xmit_free_ntb[i] == NULL) {
+      ncm_interface.xmit_free_ntb[i] = free_ntb;
+      return;
+    }
+  }
+  TU_LOG_DRV("(EE) xmit_put_ntb_into_free_list - no entry in free list\n");// this should not happen
+} // xmit_put_ntb_into_free_list
+
+/**
+ * Get an NTB from the free list
+ */
+static xmit_ntb_t *xmit_get_free_ntb(void) {
+  TU_LOG_DRV("xmit_get_free_ntb()\n");
+
+  for (int i = 0; i < XMIT_NTB_N; ++i) {
+    if (ncm_interface.xmit_free_ntb[i] != NULL) {
+      xmit_ntb_t *free = ncm_interface.xmit_free_ntb[i];
+      ncm_interface.xmit_free_ntb[i] = NULL;
+      return free;
+    }
+  }
+  return NULL;
+} // xmit_get_free_ntb
+
+/**
+ * Put a filled NTB into the ready list
+ */
+static void xmit_put_ntb_into_ready_list(xmit_ntb_t *ready_ntb) {
+  TU_LOG_DRV("xmit_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
+
+  for (int i = 0; i < XMIT_NTB_N; ++i) {
+    if (ncm_interface.xmit_ready_ntb[i] == NULL) {
+      ncm_interface.xmit_ready_ntb[i] = ready_ntb;
+      return;
+    }
+  }
+  TU_LOG_DRV("(EE) xmit_put_ntb_into_ready_list: ready list full\n");// this should not happen
+} // xmit_put_ntb_into_ready_list
+
+/**
+ * Get the next NTB from the ready list (and remove it from the list).
+ * If the ready list is empty, return NULL.
+ */
+static xmit_ntb_t *xmit_get_next_ready_ntb(void) {
+  xmit_ntb_t *r = NULL;
+
+  r = ncm_interface.xmit_ready_ntb[0];
+  memmove(ncm_interface.xmit_ready_ntb + 0, ncm_interface.xmit_ready_ntb + 1, sizeof(ncm_interface.xmit_ready_ntb) - sizeof(ncm_interface.xmit_ready_ntb[0]));
+  ncm_interface.xmit_ready_ntb[XMIT_NTB_N - 1] = NULL;
+
+  TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
+  return r;
+} // xmit_get_next_ready_ntb
+
+/**
+ * Transmit a ZLP if required
+ *
+ * \note
+ *    Insertion of the ZLPs is a little bit different then described in the spec.
+ *    But the below implementation actually works.  Don't know if this is a spec
+ *    or TinyUSB issue.
+ *
+ * \pre
+ *    This must be called from netd_xfer_cb() so that ep_in is ready
+ */
+static bool xmit_insert_required_zlp(uint8_t rhport, uint32_t xferred_bytes) {
+  TU_LOG_DRV("xmit_insert_required_zlp(%d,%ld)\n", rhport, xferred_bytes);
+
+  if (xferred_bytes == 0 || xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE != 0) {
+    return false;
+  }
+
+  TU_ASSERT(ncm_interface.itf_data_alt == 1, false);
+  TU_ASSERT(!usbd_edpt_busy(rhport, ncm_interface.ep_in), false);
+
+  TU_LOG_DRV("xmit_insert_required_zlp! (%u)\n", (unsigned) xferred_bytes);
+
+  // start transmission of the ZLP
+  usbd_edpt_xfer(rhport, ncm_interface.ep_in, NULL, 0);
+
+  return true;
+} // xmit_insert_required_zlp
+
+/**
+ * Start transmission if it there is a waiting packet and if can be done from interface side.
+ */
+static void xmit_start_if_possible(uint8_t rhport) {
+  TU_LOG_DRV("xmit_start_if_possible()\n");
+
+  if (ncm_interface.xmit_tinyusb_ntb != NULL) {
+    TU_LOG_DRV("  !xmit_start_if_possible 1\n");
+    return;
+  }
+  if (ncm_interface.itf_data_alt != 1) {
+    TU_LOG_DRV("(EE) !xmit_start_if_possible 2\n");
+    return;
+  }
+  if (usbd_edpt_busy(rhport, ncm_interface.ep_in)) {
+    TU_LOG_DRV("  !xmit_start_if_possible 3\n");
+    return;
+  }
+
+  ncm_interface.xmit_tinyusb_ntb = xmit_get_next_ready_ntb();
+  if (ncm_interface.xmit_tinyusb_ntb == NULL) {
+    if (ncm_interface.xmit_glue_ntb == NULL || ncm_interface.xmit_glue_ntb_datagram_ndx == 0) {
+      // -> really nothing is waiting
+      return;
+    }
+    ncm_interface.xmit_tinyusb_ntb = ncm_interface.xmit_glue_ntb;
+    ncm_interface.xmit_glue_ntb = NULL;
+  }
+
+  #if CFG_TUD_NCM_LOG_LEVEL >= 3
+  {
+    uint16_t len = ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength;
+    TU_LOG_BUF(3, ncm_interface.xmit_tinyusb_ntb->data[i], len);
+  }
+  #endif
+
+  if (ncm_interface.xmit_glue_ntb_datagram_ndx != 1) {
+    TU_LOG_DRV(">> %d %d\n", ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength, ncm_interface.xmit_glue_ntb_datagram_ndx);
+  }
+
+  // Kick off an endpoint transfer
+  usbd_edpt_xfer(0, ncm_interface.ep_in, ncm_interface.xmit_tinyusb_ntb->data, ncm_interface.xmit_tinyusb_ntb->nth.wBlockLength);
+} // xmit_start_if_possible
+
+/**
+ * check if a new datagram fits into the current NTB
+ */
+static bool xmit_requested_datagram_fits_into_current_ntb(uint16_t datagram_size) {
+  TU_LOG_DRV("xmit_requested_datagram_fits_into_current_ntb(%d) - %p %p\n", datagram_size, ncm_interface.xmit_tinyusb_ntb, ncm_interface.xmit_glue_ntb);
+
+  if (ncm_interface.xmit_glue_ntb == NULL) {
+    return false;
+  }
+  if (ncm_interface.xmit_glue_ntb_datagram_ndx >= CFG_TUD_NCM_IN_MAX_DATAGRAMS_PER_NTB) {
+    return false;
+  }
+  if (ncm_interface.xmit_glue_ntb->nth.wBlockLength + datagram_size + XMIT_ALIGN_OFFSET(datagram_size) > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+    return false;
+  }
+  return true;
+} // xmit_requested_datagram_fits_into_current_ntb
+
+/**
+ * Setup an NTB for the glue logic
+ */
+static bool xmit_setup_next_glue_ntb(void) {
+  TU_LOG_DRV("xmit_setup_next_glue_ntb - %p\n", ncm_interface.xmit_glue_ntb);
+
+  if (ncm_interface.xmit_glue_ntb != NULL) {
+    // put NTB into waiting list (the new datagram did not fit in)
+    xmit_put_ntb_into_ready_list(ncm_interface.xmit_glue_ntb);
+  }
+
+  ncm_interface.xmit_glue_ntb = xmit_get_free_ntb();// get next buffer (if any)
+  if (ncm_interface.xmit_glue_ntb == NULL) {
+    TU_LOG_DRV("  xmit_setup_next_glue_ntb - nothing free\n");// should happen rarely
+    return false;
+  }
+
+  ncm_interface.xmit_glue_ntb_datagram_ndx = 0;
+
+  xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
+
+  // Fill in NTB header
+  ntb->nth.dwSignature = NTH16_SIGNATURE;
+  ntb->nth.wHeaderLength = sizeof(ntb->nth);
+  ntb->nth.wSequence = ncm_interface.xmit_sequence++;
+  ntb->nth.wBlockLength = sizeof(ntb->nth) + sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
+  ntb->nth.wNdpIndex = sizeof(ntb->nth);
+
+  // Fill in NDP16 header and terminator
+  ntb->ndp.dwSignature = NDP16_SIGNATURE_NCM0;
+  ntb->ndp.wLength = sizeof(ntb->ndp) + sizeof(ntb->ndp_datagram);
+  ntb->ndp.wNextNdpIndex = 0;
+
+  memset(ntb->ndp_datagram, 0, sizeof(ntb->ndp_datagram));
+  return true;
+} // xmit_setup_next_glue_ntb
+
+//-----------------------------------------------------------------------------
+//
+// all the recv_*() stuff (TinyUSB -> driver -> glue logic)
+//
+
+/**
+ * Return pointer to an available receive buffer or NULL.
+ * Returned buffer (if any) has the size \a CFG_TUD_NCM_OUT_NTB_MAX_SIZE.
+ */
+static recv_ntb_t *recv_get_free_ntb(void) {
+  TU_LOG_DRV("recv_get_free_ntb()\n");
+
+  for (int i = 0; i < RECV_NTB_N; ++i) {
+    if (ncm_interface.recv_free_ntb[i] != NULL) {
+      recv_ntb_t *free = ncm_interface.recv_free_ntb[i];
+      ncm_interface.recv_free_ntb[i] = NULL;
+      return free;
+    }
+  }
+  return NULL;
+} // recv_get_free_ntb
+
+/**
+ * Get the next NTB from the ready list (and remove it from the list).
+ * If the ready list is empty, return NULL.
+ */
+static recv_ntb_t *recv_get_next_ready_ntb(void) {
+  recv_ntb_t *r = NULL;
+
+  r = ncm_interface.recv_ready_ntb[0];
+  memmove(ncm_interface.recv_ready_ntb + 0, ncm_interface.recv_ready_ntb + 1, sizeof(ncm_interface.recv_ready_ntb) - sizeof(ncm_interface.recv_ready_ntb[0]));
+  ncm_interface.recv_ready_ntb[RECV_NTB_N - 1] = NULL;
+
+  TU_LOG_DRV("recv_get_next_ready_ntb: %p\n", r);
+  return r;
+} // recv_get_next_ready_ntb
+
+/**
+ * Put NTB into the receiver free list.
+ */
+static void recv_put_ntb_into_free_list(recv_ntb_t *free_ntb) {
+  TU_LOG_DRV("recv_put_ntb_into_free_list(%p)\n", free_ntb);
+
+  for (int i = 0; i < RECV_NTB_N; ++i) {
+    if (ncm_interface.recv_free_ntb[i] == NULL) {
+      ncm_interface.recv_free_ntb[i] = free_ntb;
+      return;
+    }
+  }
+  TU_LOG_DRV("(EE) recv_put_ntb_into_free_list - no entry in free list\n");// this should not happen
+} // recv_put_ntb_into_free_list
+
+/**
+ * \a ready_ntb holds a validated NTB,
+ * put this buffer into the waiting list.
+ */
+static void recv_put_ntb_into_ready_list(recv_ntb_t *ready_ntb) {
+  TU_LOG_DRV("recv_put_ntb_into_ready_list(%p) %d\n", ready_ntb, ready_ntb->nth.wBlockLength);
+
+  for (int i = 0; i < RECV_NTB_N; ++i) {
+    if (ncm_interface.recv_ready_ntb[i] == NULL) {
+      ncm_interface.recv_ready_ntb[i] = ready_ntb;
+      return;
+    }
+  }
+  TU_LOG_DRV("(EE) recv_put_ntb_into_ready_list: ready list full\n");// this should not happen
+} // recv_put_ntb_into_ready_list
+
+/**
+ * If possible, start a new reception TinyUSB -> driver.
+ */
+static void recv_try_to_start_new_reception(uint8_t rhport) {
+  TU_LOG_DRV("recv_try_to_start_new_reception(%d)\n", rhport);
+
+  if (ncm_interface.itf_data_alt != 1) {
+    return;
+  }
+  if (ncm_interface.recv_tinyusb_ntb != NULL) {
+    return;
+  }
+  if (usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
+    return;
+  }
+
+  ncm_interface.recv_tinyusb_ntb = recv_get_free_ntb();
+  if (ncm_interface.recv_tinyusb_ntb == NULL) {
+    return;
+  }
+
+  // initiate transfer
+  TU_LOG_DRV("  start reception\n");
+  bool r = usbd_edpt_xfer(rhport, ncm_interface.ep_out, ncm_interface.recv_tinyusb_ntb->data, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
+  if (!r) {
+    recv_put_ntb_into_free_list(ncm_interface.recv_tinyusb_ntb);
+    ncm_interface.recv_tinyusb_ntb = NULL;
+  }
+} // recv_try_to_start_new_reception
+
+/**
+ * Validate incoming datagram.
+ * \return true if valid
+ *
+ * \note
+ *    \a ndp16->wNextNdpIndex != 0 is not supported
+ */
+static bool recv_validate_datagram(const recv_ntb_t *ntb, uint32_t len) {
+  const nth16_t *nth16 = &(ntb->nth);
+
+  TU_LOG_DRV("recv_validate_datagram(%p, %d)\n", ntb, (int) len);
+
+  // check header
+  if (nth16->wHeaderLength != sizeof(nth16_t)) {
+    TU_LOG_DRV("(EE) ill nth16 length: %d\n", nth16->wHeaderLength);
+    return false;
+  }
+  if (nth16->dwSignature != NTH16_SIGNATURE) {
+    TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) nth16->dwSignature);
+    return false;
+  }
+  if (len < sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
+    TU_LOG_DRV("(EE) ill min len: %lu\n", len);
+    return false;
+  }
+  if (nth16->wBlockLength > len) {
+    TU_LOG_DRV("(EE) ill block length: %d > %lu\n", nth16->wBlockLength, len);
+    return false;
+  }
+  if (nth16->wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+    TU_LOG_DRV("(EE) ill block length2: %d > %d\n", nth16->wBlockLength, CFG_TUD_NCM_OUT_NTB_MAX_SIZE);
+    return false;
+  }
+  if (nth16->wNdpIndex < sizeof(nth16) || nth16->wNdpIndex > len - (sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t))) {
+    TU_LOG_DRV("(EE) ill position of first ndp: %d (%lu)\n", nth16->wNdpIndex, len);
+    return false;
+  }
+
+  // check (first) NDP(16)
+  const ndp16_t *ndp16 = (const ndp16_t *) (ntb->data + nth16->wNdpIndex);
+
+  if (ndp16->wLength < sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)) {
+    TU_LOG_DRV("(EE) ill ndp16 length: %d\n", ndp16->wLength);
+    return false;
+  }
+  if (ndp16->dwSignature != NDP16_SIGNATURE_NCM0 && ndp16->dwSignature != NDP16_SIGNATURE_NCM1) {
+    TU_LOG_DRV("(EE) ill signature: 0x%08x\n", (unsigned) ndp16->dwSignature);
+    return false;
+  }
+  if (ndp16->wNextNdpIndex != 0) {
+    TU_LOG_DRV("(EE) cannot handle wNextNdpIndex!=0 (%d)\n", ndp16->wNextNdpIndex);
+    return false;
+  }
+
+  const ndp16_datagram_t *ndp16_datagram = (const ndp16_datagram_t *) (ntb->data + nth16->wNdpIndex + sizeof(ndp16_t));
+  int ndx = 0;
+  uint16_t max_ndx = (uint16_t) ((ndp16->wLength - sizeof(ndp16_t)) / sizeof(ndp16_datagram_t));
+
+  if (max_ndx > 2) { // number of datagrams in NTB > 1
+    TU_LOG_DRV("<< %d (%d)\n", max_ndx - 1, ntb->nth.wBlockLength);
+  }
+  if (ndp16_datagram[max_ndx - 1].wDatagramIndex != 0 || ndp16_datagram[max_ndx - 1].wDatagramLength != 0) {
+    TU_LOG_DRV("  max_ndx != 0\n");
+    return false;
+  }
+  while (ndp16_datagram[ndx].wDatagramIndex != 0 && ndp16_datagram[ndx].wDatagramLength != 0) {
+    TU_LOG_DRV("  << %d %d\n", ndp16_datagram[ndx].wDatagramIndex, ndp16_datagram[ndx].wDatagramLength);
+    if (ndp16_datagram[ndx].wDatagramIndex > len) {
+      TU_LOG_DRV("(EE) ill start of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex, len);
+      return false;
+    }
+    if (ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength > len) {
+      TU_LOG_DRV("(EE) ill end of datagram[%d]: %d (%lu)\n", ndx, ndp16_datagram[ndx].wDatagramIndex + ndp16_datagram[ndx].wDatagramLength, len);
+      return false;
+    }
+    ++ndx;
+  }
+
+  #if CFG_TUD_NCM_LOG_LEVEL >= 3
+  TU_LOG_BUF(3, ntb->data[i], len);
+  #endif
+
+  // -> ntb contains a valid packet structure
+  //    ok... I did not check for garbage within the datagram indices...
+  return true;
+} // recv_validate_datagram
+
+/**
+ * Transfer the next (pending) datagram to the glue logic and return receive buffer if empty.
+ */
+static void recv_transfer_datagram_to_glue_logic(void) {
+  TU_LOG_DRV("recv_transfer_datagram_to_glue_logic()\n");
+
+  if (ncm_interface.recv_glue_ntb == NULL) {
+    ncm_interface.recv_glue_ntb = recv_get_next_ready_ntb();
+    TU_LOG_DRV("  new buffer for glue logic: %p\n", ncm_interface.recv_glue_ntb);
+    ncm_interface.recv_glue_ntb_datagram_ndx = 0;
+  }
+
+  if (ncm_interface.recv_glue_ntb != NULL) {
+    const ndp16_datagram_t *ndp16_datagram = (ndp16_datagram_t *) (ncm_interface.recv_glue_ntb->data + ncm_interface.recv_glue_ntb->nth.wNdpIndex + sizeof(ndp16_t));
+
+    if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex == 0) {
+      TU_LOG_DRV("(EE) SOMETHING WENT WRONG 1\n");
+    } else if (ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength == 0) {
+      TU_LOG_DRV("(EE) SOMETHING WENT WRONG 2\n");
+    } else {
+      uint16_t datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramIndex;
+      uint16_t datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx].wDatagramLength;
+
+      TU_LOG_DRV("  recv[%d] - %d %d\n", ncm_interface.recv_glue_ntb_datagram_ndx, datagramIndex, datagramLength);
+      if (tud_network_recv_cb(ncm_interface.recv_glue_ntb->data + datagramIndex, datagramLength)) {
+        // send datagram successfully to glue logic
+        TU_LOG_DRV("    OK\n");
+        datagramIndex = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramIndex;
+        datagramLength = ndp16_datagram[ncm_interface.recv_glue_ntb_datagram_ndx + 1].wDatagramLength;
+
+        if (datagramIndex != 0 && datagramLength != 0) {
+          // -> next datagram
+          ++ncm_interface.recv_glue_ntb_datagram_ndx;
+        } else {
+          // end of datagrams reached
+          recv_put_ntb_into_free_list(ncm_interface.recv_glue_ntb);
+          ncm_interface.recv_glue_ntb = NULL;
+        }
+      }
+    }
+  }
+} // recv_transfer_datagram_to_glue_logic
+
+//-----------------------------------------------------------------------------
+//
+// all the tud_network_*() stuff (glue logic -> driver)
+//
+
+/**
+ * Check if the glue logic is allowed to call tud_network_xmit().
+ * This function also fetches a next buffer if required, so that tud_network_xmit() is ready for copy
+ * and transmission operation.
+ */
+bool tud_network_can_xmit(uint16_t size) {
+  TU_LOG_DRV("tud_network_can_xmit(%d)\n", size);
+
+  TU_ASSERT(size <= CFG_TUD_NCM_OUT_NTB_MAX_SIZE - (sizeof(nth16_t) + sizeof(ndp16_t) + 2 * sizeof(ndp16_datagram_t)), false);
+
+  if (xmit_requested_datagram_fits_into_current_ntb(size) || xmit_setup_next_glue_ntb()) {
+    // -> everything is fine
+    return true;
+  }
+  xmit_start_if_possible(ncm_interface.rhport);
+  TU_LOG_DRV("(II) tud_network_can_xmit: request blocked\n");// could happen if all xmit buffers are full (but should happen rarely)
+  return false;
+} // tud_network_can_xmit
+
+/**
+ * Put a datagram into a waiting NTB.
+ * If currently no transmission is started, then initiate transmission.
+ */
+void tud_network_xmit(void *ref, uint16_t arg) {
+  TU_LOG_DRV("tud_network_xmit(%p, %d)\n", ref, arg);
+
+  if (ncm_interface.xmit_glue_ntb == NULL) {
+    TU_LOG_DRV("(EE) tud_network_xmit: no buffer\n");// must not happen (really)
+    return;
+  }
+
+  xmit_ntb_t *ntb = ncm_interface.xmit_glue_ntb;
+
+  // copy new datagram to the end of the current NTB
+  uint16_t size = tud_network_xmit_cb(ntb->data + ntb->nth.wBlockLength, ref, arg);
+
+  // correct NTB internals
+  ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramIndex = ntb->nth.wBlockLength;
+  ntb->ndp_datagram[ncm_interface.xmit_glue_ntb_datagram_ndx].wDatagramLength = size;
+  ncm_interface.xmit_glue_ntb_datagram_ndx += 1;
+
+  ntb->nth.wBlockLength += (uint16_t) (size + XMIT_ALIGN_OFFSET(size));
+
+  if (ntb->nth.wBlockLength > CFG_TUD_NCM_OUT_NTB_MAX_SIZE) {
+    TU_LOG_DRV("(EE) tud_network_xmit: buffer overflow\n"); // must not happen (really)
+    return;
+  }
+
+  xmit_start_if_possible(ncm_interface.rhport);
+} // tud_network_xmit
+
+/**
+ * Keep the receive logic busy and transfer pending packets to the glue logic.
+ */
+void tud_network_recv_renew(void) {
+  TU_LOG_DRV("tud_network_recv_renew()\n");
+
+  recv_transfer_datagram_to_glue_logic();
+  recv_try_to_start_new_reception(ncm_interface.rhport);
+} // tud_network_recv_renew
+
+/**
+ * Same as tud_network_recv_renew() but knows \a rhport
+ */
+void tud_network_recv_renew_r(uint8_t rhport) {
+  TU_LOG_DRV("tud_network_recv_renew_r(%d)\n", rhport);
+
+  ncm_interface.rhport = rhport;
+  tud_network_recv_renew();
+} // tud_network_recv_renew
+
+//-----------------------------------------------------------------------------
+//
+// all the netd_*() stuff (interface TinyUSB -> driver)
+//
+/**
+ * Initialize the driver data structures.
+ * Might be called several times.
+ */
+void netd_init(void) {
+  TU_LOG_DRV("netd_init()\n");
+
+  memset(&ncm_interface, 0, sizeof(ncm_interface));
+
+  for (int i = 0; i < XMIT_NTB_N; ++i) {
+    ncm_interface.xmit_free_ntb[i] = ncm_interface.xmit_ntb + i;
+  }
+  for (int i = 0; i < RECV_NTB_N; ++i) {
+    ncm_interface.recv_free_ntb[i] = ncm_interface.recv_ntb + i;
+  }
+} // netd_init
+
+/**
+ * Deinit driver
+ */
 bool netd_deinit(void) {
   return true;
 }
 
-void netd_reset(uint8_t rhport)
-{
+/**
+ * Resets the port.
+ * In this driver this is the same as netd_init()
+ */
+void netd_reset(uint8_t rhport) {
   (void) rhport;
 
   netd_init();
-}
+} // netd_reset
 
-uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
-{
-  // confirm interface hasn't already been allocated
-  TU_ASSERT(0 == ncm_interface.ep_notif, 0);
+/**
+ * Open the USB interface.
+ * - parse the USB descriptor \a TUD_CDC_NCM_DESCRIPTOR for itfnum and endpoints
+ * - a specific order of elements in the descriptor is tested.
+ *
+ * \note
+ *   Actually all of the information could be read directly from \a itf_desc, because the
+ *   structure and the values are well known.  But we do it this way.
+ *
+ * \post
+ * - \a itf_num set
+ * - \a ep_notif, \a ep_in and \a ep_out are set
+ * - USB interface is open
+ */
+uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) {
+  TU_ASSERT(ncm_interface.ep_notif == 0, 0);// assure that the interface is only opened once
 
-  //------------- Management Interface -------------//
-  ncm_interface.itf_num = itf_desc->bInterfaceNumber;
+  ncm_interface.itf_num = itf_desc->bInterfaceNumber;// management interface
 
+  // skip the two first entries and the following TUSB_DESC_CS_INTERFACE entries
   uint16_t drv_len = sizeof(tusb_desc_interface_t);
-  uint8_t const * p_desc = tu_desc_next( itf_desc );
-
-  // Communication Functional Descriptors
-  while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
-  {
+  uint8_t const *p_desc = tu_desc_next(itf_desc);
+  while (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE && drv_len <= max_len) {
     drv_len += tu_desc_len(p_desc);
-    p_desc   = tu_desc_next(p_desc);
+    p_desc = tu_desc_next(p_desc);
   }
 
-  // notification endpoint (if any)
-  if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
-  {
-    TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
+  // get notification endpoint
+  TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
+  TU_ASSERT(usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0);
+  ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+  drv_len += tu_desc_len(p_desc);
+  p_desc = tu_desc_next(p_desc);
 
-    ncm_interface.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
+  // skip the following TUSB_DESC_INTERFACE entries (which must be TUSB_CLASS_CDC_DATA)
+  while (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE && drv_len <= max_len) {
+    tusb_desc_interface_t const *data_itf_desc = (tusb_desc_interface_t const *) p_desc;
+    TU_ASSERT(data_itf_desc->bInterfaceClass == TUSB_CLASS_CDC_DATA, 0);
 
     drv_len += tu_desc_len(p_desc);
-    p_desc   = tu_desc_next(p_desc);
+    p_desc = tu_desc_next(p_desc);
   }
 
-  //------------- Data Interface -------------//
-  // - CDC-NCM data interface has 2 alternate settings
-  //   - 0 : zero endpoints for inactive (default)
-  //   - 1 : IN & OUT endpoints for transfer of NTBs
-  TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
-
-  do
-  {
-    tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
-    TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
-
-    drv_len += tu_desc_len(p_desc);
-    p_desc   = tu_desc_next(p_desc);
-  } while((TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len));
-
-  // Pair of endpoints
-  TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
-
-  TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in) );
-
-  drv_len += 2*sizeof(tusb_desc_endpoint_t);
+  // a TUSB_DESC_ENDPOINT (actually two) must follow, open these endpoints
+  TU_ASSERT(tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT, 0);
+  TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &ncm_interface.ep_out, &ncm_interface.ep_in));
+  drv_len += 2 * sizeof(tusb_desc_endpoint_t);
 
   return drv_len;
-}
+} // netd_open
 
-static void ncm_report(void)
-{
-  uint8_t const rhport = 0;
-  if (ncm_interface.report_state == REPORT_SPEED) {
-    ncm_notify_speed_change.header.wIndex = ncm_interface.itf_num;
-    usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_speed_change, sizeof(ncm_notify_speed_change));
-    ncm_interface.report_state = REPORT_CONNECTED;
-    ncm_interface.report_pending = true;
-  } else if (ncm_interface.report_state == REPORT_CONNECTED) {
-    ncm_notify_connected.header.wIndex = ncm_interface.itf_num;
-    usbd_edpt_xfer(rhport, ncm_interface.ep_notif, (uint8_t *) &ncm_notify_connected, sizeof(ncm_notify_connected));
-    ncm_interface.report_state = REPORT_DONE;
-    ncm_interface.report_pending = true;
+/**
+ * Handle TinyUSB requests to process transfer events.
+ */
+bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
+  (void) result;
+
+  if (ep_addr == ncm_interface.ep_out) {
+    // new NTB received
+    // - make the NTB valid
+    // - if ready transfer datagrams to the glue logic for further processing
+    // - if there is a free receive buffer, initiate reception
+    if (!recv_validate_datagram(ncm_interface.recv_tinyusb_ntb, xferred_bytes)) {
+      // verification failed: ignore NTB and return it to free
+      TU_LOG_DRV("(EE) VALIDATION FAILED. WHAT CAN WE DO IN THIS CASE?\n");
+    } else {
+      // packet ok -> put it into ready list
+      recv_put_ntb_into_ready_list(ncm_interface.recv_tinyusb_ntb);
+    }
+    ncm_interface.recv_tinyusb_ntb = NULL;
+    tud_network_recv_renew_r(rhport);
+  } else if (ep_addr == ncm_interface.ep_in) {
+    // transmission of an NTB finished
+    // - free the transmitted NTB buffer
+    // - insert ZLPs when necessary
+    // - if there is another transmit NTB waiting, try to start transmission
+    xmit_put_ntb_into_free_list(ncm_interface.xmit_tinyusb_ntb);
+    ncm_interface.xmit_tinyusb_ntb = NULL;
+    if (!xmit_insert_required_zlp(rhport, xferred_bytes)) {
+      xmit_start_if_possible(rhport);
+    }
+  } else if (ep_addr == ncm_interface.ep_notif) {
+    // next transfer on notification channel
+    notification_xmit(rhport, true);
   }
-}
 
-TU_ATTR_WEAK void tud_network_link_state_cb(bool state)
-{
-  (void)state;
-}
+  return true;
+} // netd_xfer_cb
 
-// Handle class control request
-// return false to stall control endpoint (e.g unsupported request)
-bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
-{
-  if ( stage != CONTROL_STAGE_SETUP ) return true;
+/**
+ * Respond to TinyUSB control requests.
+ * At startup transmission of notification packets are done here.
+ */
+bool netd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request) {
+  if (stage != CONTROL_STAGE_SETUP) {
+    return true;
+  }
 
-  switch ( request->bmRequestType_bit.type )
-  {
+  switch (request->bmRequestType_bit.type) {
     case TUSB_REQ_TYPE_STANDARD:
-      switch ( request->bRequest )
-      {
-        case TUSB_REQ_GET_INTERFACE:
-        {
-          uint8_t const req_itfnum = (uint8_t) request->wIndex;
-          TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum);
+
+      switch (request->bRequest) {
+        case TUSB_REQ_GET_INTERFACE: {
+          TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex, false);
 
           tud_control_xfer(rhport, request, &ncm_interface.itf_data_alt, 1);
-        }
-          break;
+        } break;
 
-        case TUSB_REQ_SET_INTERFACE:
-        {
-          uint8_t const req_itfnum = (uint8_t) request->wIndex;
-          uint8_t const req_alt    = (uint8_t) request->wValue;
+        case TUSB_REQ_SET_INTERFACE: {
+          TU_VERIFY(ncm_interface.itf_num + 1 == request->wIndex && request->wValue < 2, false);
 
-          // Only valid for Data Interface with Alternate is either 0 or 1
-          TU_VERIFY(ncm_interface.itf_num + 1 == req_itfnum && req_alt < 2);
+          ncm_interface.itf_data_alt = (uint8_t) request->wValue;
 
-          if (req_alt != ncm_interface.itf_data_alt) {
-            ncm_interface.itf_data_alt = req_alt;
-
-            if (ncm_interface.itf_data_alt) {
-              if (!usbd_edpt_busy(rhport, ncm_interface.ep_out)) {
-                tud_network_recv_renew(); // prepare for incoming datagrams
-              }
-              if (!ncm_interface.report_pending) {
-                ncm_report();
-              }
-            }
-
-            tud_network_link_state_cb(ncm_interface.itf_data_alt);
+          if (ncm_interface.itf_data_alt == 1) {
+            tud_network_recv_renew_r(rhport);
+            notification_xmit(rhport, false);
           }
-
           tud_control_status(rhport, request);
-        }
-          break;
+        } break;
 
-          // unsupported request
-        default: return false;
+        // unsupported request
+        default:
+          return false;
       }
       break;
 
     case TUSB_REQ_TYPE_CLASS:
-      TU_VERIFY (ncm_interface.itf_num == request->wIndex);
+      TU_VERIFY(ncm_interface.itf_num == request->wIndex, false);
+      switch (request->bRequest) {
+        case NCM_GET_NTB_PARAMETERS: {
+          // transfer NTB parameters to host.
+          tud_control_xfer(rhport, request, (void *) (uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
+        } break;
 
-      if (NCM_GET_NTB_PARAMETERS == request->bRequest)
-      {
-        tud_control_xfer(rhport, request, (void*)(uintptr_t) &ntb_parameters, sizeof(ntb_parameters));
+          // unsupported request
+        default:
+          return false;
       }
-
       break;
-
       // unsupported request
-    default: return false;
+    default:
+      return false;
   }
 
   return true;
-}
+} // netd_control_xfer_cb
 
-static void handle_incoming_datagram(uint32_t len)
-{
-  uint32_t size = len;
-
-  if (len == 0) {
-    return;
-  }
-
-  TU_ASSERT(size >= sizeof(nth16_t), );
-
-  const nth16_t *hdr = (const nth16_t *)receive_ntb;
-  TU_ASSERT(hdr->dwSignature == NTH16_SIGNATURE, );
-  TU_ASSERT(hdr->wNdpIndex >= sizeof(nth16_t) && (hdr->wNdpIndex + sizeof(ndp16_t)) <= len, );
-
-  const ndp16_t *ndp = (const ndp16_t *)(receive_ntb + hdr->wNdpIndex);
-  TU_ASSERT(ndp->dwSignature == NDP16_SIGNATURE_NCM0 || ndp->dwSignature == NDP16_SIGNATURE_NCM1, );
-  TU_ASSERT(hdr->wNdpIndex + ndp->wLength <= len, );
-
-  int num_datagrams = (ndp->wLength - 12) / 4;
-  ncm_interface.current_datagram_index = 0;
-  ncm_interface.num_datagrams = 0;
-  ncm_interface.ndp = ndp;
-  for (int i = 0; i < num_datagrams && ndp->datagram[i].wDatagramIndex && ndp->datagram[i].wDatagramLength; i++)
-  {
-    ncm_interface.num_datagrams++;
-  }
-
-  tud_network_recv_renew();
-}
-
-bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
-{
-  (void) rhport;
-  (void) result;
-
-  /* new datagram receive_ntb */
-  if (ep_addr == ncm_interface.ep_out )
-  {
-    handle_incoming_datagram(xferred_bytes);
-  }
-
-  /* data transmission finished */
-  if (ep_addr == ncm_interface.ep_in )
-  {
-    if (ncm_interface.transferring) {
-      ncm_interface.transferring = false;
-    }
-
-    // If there are datagrams queued up that we tried to send while this NTB was being emitted, send them now
-    if (ncm_interface.datagram_count && ncm_interface.itf_data_alt == 1) {
-      ncm_start_tx();
-    }
-  }
-
-  if (ep_addr == ncm_interface.ep_notif )
-  {
-    ncm_interface.report_pending = false;
-    ncm_report();
-  }
-
-  return true;
-}
-
-// poll network driver for its ability to accept another packet to transmit
-bool tud_network_can_xmit(uint16_t size)
-{
-  TU_VERIFY(ncm_interface.itf_data_alt == 1);
-
-  if (ncm_interface.datagram_count >= ncm_interface.max_datagrams_per_ntb) {
-    TU_LOG_DRV("NTB full [by count]\r\n");
-    return false;
-  }
-
-  size_t next_datagram_offset = ncm_interface.next_datagram_offset;
-  if (next_datagram_offset + size > ncm_interface.ntb_in_size) {
-    TU_LOG_DRV("ntb full [by size]\r\n");
-    return false;
-  }
-
-  return true;
-}
-
-void tud_network_xmit(void *ref, uint16_t arg)
-{
-  transmit_ntb_t *ntb = &transmit_ntb[ncm_interface.current_ntb];
-  size_t next_datagram_offset = ncm_interface.next_datagram_offset;
-
-  uint16_t size = tud_network_xmit_cb(ntb->data + next_datagram_offset, ref, arg);
-
-  ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramIndex = ncm_interface.next_datagram_offset;
-  ntb->ndp.datagram[ncm_interface.datagram_count].wDatagramLength = size;
-
-  ncm_interface.datagram_count++;
-  next_datagram_offset += size;
-
-  // round up so the next datagram is aligned correctly
-  next_datagram_offset += (CFG_TUD_NCM_ALIGNMENT - 1);
-  next_datagram_offset -= (next_datagram_offset % CFG_TUD_NCM_ALIGNMENT);
-
-  ncm_interface.next_datagram_offset = next_datagram_offset;
-
-  ncm_start_tx();
-}
-
-#endif
+#endif // ( CFG_TUD_ENABLED && CFG_TUD_NCM )
diff --git a/src/class/net/net_device.h b/src/class/net/net_device.h
index da1a7b1e8..4c9a92f2d 100644
--- a/src/class/net/net_device.h
+++ b/src/class/net/net_device.h
@@ -28,14 +28,13 @@
 #ifndef _TUSB_NET_DEVICE_H_
 #define _TUSB_NET_DEVICE_H_
 
+#include 
 #include "class/cdc/cdc.h"
 
 #if CFG_TUD_ECM_RNDIS && CFG_TUD_NCM
 #error "Cannot enable both ECM_RNDIS and NCM network drivers"
 #endif
 
-#include "ncm.h"
-
 /* declared here, NOT in usb_descriptors.c, so that the driver can intelligently ZLP as needed */
 #define CFG_TUD_NET_ENDPOINT_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
 
@@ -44,21 +43,13 @@
 #define CFG_TUD_NET_MTU           1514
 #endif
 
-#ifndef CFG_TUD_NCM_IN_NTB_MAX_SIZE
-#define CFG_TUD_NCM_IN_NTB_MAX_SIZE 3200
-#endif
 
-#ifndef CFG_TUD_NCM_OUT_NTB_MAX_SIZE
-#define CFG_TUD_NCM_OUT_NTB_MAX_SIZE 3200
-#endif
+// Table 4.3 Data Class Interface Protocol Codes
+typedef enum
+{
+  NCM_DATA_PROTOCOL_NETWORK_TRANSFER_BLOCK = 0x01
+} ncm_data_interface_protocol_code_t;
 
-#ifndef CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB
-#define CFG_TUD_NCM_MAX_DATAGRAMS_PER_NTB 8
-#endif
-
-#ifndef CFG_TUD_NCM_ALIGNMENT
-#define CFG_TUD_NCM_ALIGNMENT 4
-#endif
 
 #ifdef __cplusplus
  extern "C" {
@@ -96,11 +87,6 @@ void tud_network_init_cb(void);
 // TODO removed later since it is not part of tinyusb stack
 extern uint8_t tud_network_mac_address[6];
 
-//------------- NCM -------------//
-
-// callback to client providing optional indication of internal state of network driver
-void tud_network_link_state_cb(bool state);
-
 //--------------------------------------------------------------------+
 // INTERNAL USBD-CLASS DRIVER API
 //--------------------------------------------------------------------+
diff --git a/src/class/usbtmc/usbtmc.h b/src/class/usbtmc/usbtmc.h
index 090ab3c4a..327de087c 100644
--- a/src/class/usbtmc/usbtmc.h
+++ b/src/class/usbtmc/usbtmc.h
@@ -183,6 +183,23 @@ typedef enum {
 
 } usmtmc_request_type_enum;
 
+typedef enum {
+  // The last and first valid bNotify1 for use by the USBTMC class specification.
+  USBTMC_bNOTIFY1_USBTMC_FIRST          = 0x00,
+  USBTMC_bNOTIFY1_USBTMC_LAST           = 0x3F,
+
+  // The last and first valid bNotify1 for use by vendors.
+  USBTMC_bNOTIFY1_VENDOR_SPECIFIC_FIRST = 0x40,
+  USBTMC_bNOTIFY1_VENDOR_SPECIFIC_LAST  = 0x7F,
+
+  // The last and first valid bNotify1 for use by USBTMC subclass specifications.
+  USBTMC_bNOTIFY1_SUBCLASS_FIRST        = 0x80,
+  USBTMC_bNOTIFY1_SUBCLASS_LAST         = 0xFF,
+
+  // From the USB488 Subclass Specification, Section 3.4.
+  USB488_bNOTIFY1_SRQ                   = 0x81,
+} usbtmc_int_in_payload_format;
+
 typedef enum {
   USBTMC_STATUS_SUCCESS = 0x01,
   USBTMC_STATUS_PENDING = 0x02,
@@ -303,6 +320,14 @@ typedef struct TU_ATTR_PACKED
 
 TU_VERIFY_STATIC(sizeof(usbtmc_read_stb_rsp_488_t) == 3u, "struct wrong length");
 
+typedef struct TU_ATTR_PACKED
+{
+  uint8_t bNotify1; // Must be USB488_bNOTIFY1_SRQ
+  uint8_t StatusByte;
+} usbtmc_srq_interrupt_488_t;
+
+TU_VERIFY_STATIC(sizeof(usbtmc_srq_interrupt_488_t) == 2u, "struct wrong length");
+
 typedef struct TU_ATTR_PACKED
 {
   struct TU_ATTR_PACKED
diff --git a/src/class/usbtmc/usbtmc_device.c b/src/class/usbtmc/usbtmc_device.c
index 286cbe108..129ff465d 100644
--- a/src/class/usbtmc/usbtmc_device.c
+++ b/src/class/usbtmc/usbtmc_device.c
@@ -86,6 +86,11 @@ tu_static char logMsg[150];
 // imposes a minimum buffer size of 32 bytes.
 #define USBTMCD_BUFFER_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
 
+// Interrupt endpoint buffer size, default to 2 bytes as USB488 specification.
+#ifndef CFG_TUD_USBTMC_INT_EP_SIZE
+#define CFG_TUD_USBTMC_INT_EP_SIZE 2
+#endif
+
 /*
  * The state machine does not allow simultaneous reading and writing. This is
  * consistent with USBTMC.
@@ -124,13 +129,15 @@ typedef struct
   uint8_t ep_bulk_in;
   uint8_t ep_bulk_out;
   uint8_t ep_int_in;
+  uint32_t ep_bulk_in_wMaxPacketSize;
+  uint32_t ep_bulk_out_wMaxPacketSize;
   // IN buffer is only used for first packet, not the remainder
   // in order to deal with prepending header
   CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_in_buf[USBTMCD_BUFFER_SIZE];
-  uint32_t ep_bulk_in_wMaxPacketSize;
   // OUT buffer receives one packet at a time
   CFG_TUSB_MEM_ALIGN uint8_t ep_bulk_out_buf[USBTMCD_BUFFER_SIZE];
-  uint32_t ep_bulk_out_wMaxPacketSize;
+  // Buffer int msg to ensure alignment and placement correctness
+  CFG_TUSB_MEM_ALIGN uint8_t ep_int_in_buf[CFG_TUD_USBTMC_INT_EP_SIZE];
 
   uint32_t transfer_size_remaining; // also used for requested length for bulk IN.
   uint32_t transfer_size_sent;      // To keep track of data bytes that have been queued in FIFO (not header bytes)
@@ -240,6 +247,19 @@ bool tud_usbtmc_transmit_dev_msg_data(
   return true;
 }
 
+bool tud_usbtmc_transmit_notification_data(const void * data, size_t len)
+{
+#ifndef NDEBUG
+  TU_ASSERT(len > 0);
+  TU_ASSERT(usbtmc_state.ep_int_in != 0);
+#endif
+  TU_VERIFY(usbd_edpt_busy(usbtmc_state.rhport, usbtmc_state.ep_int_in));
+
+  TU_VERIFY(tu_memcpy_s(usbtmc_state.ep_int_in_buf, sizeof(usbtmc_state.ep_int_in_buf), data, len) == 0);
+  TU_VERIFY(usbd_edpt_xfer(usbtmc_state.rhport, usbtmc_state.ep_int_in, usbtmc_state.ep_int_in_buf, (uint16_t)len));
+  return true;
+}
+
 void usbtmcd_init_cb(void)
 {
   usbtmc_state.capabilities = tud_usbtmc_get_capabilities_cb();
@@ -360,7 +380,7 @@ uint16_t usbtmcd_open_cb(uint8_t rhport, tusb_desc_interface_t const * itf_desc,
 // processing a command (such as a clear). Returns true if it was
 // in the NAK state and successfully transitioned to the ACK wait
 // state.
-bool tud_usbtmc_start_bus_read()
+bool tud_usbtmc_start_bus_read(void)
 {
   usbtmcd_state_enum oldState = usbtmc_state.state;
   switch(oldState)
@@ -547,9 +567,10 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
     case STATE_TX_INITIATED:
       if(usbtmc_state.transfer_size_remaining >= sizeof(usbtmc_state.ep_bulk_in_buf))
       {
-        // FIXME! This removes const below!
+        // Copy buffer to ensure alignment correctness
+        memcpy(usbtmc_state.ep_bulk_in_buf, usbtmc_state.devInBuffer, sizeof(usbtmc_state.ep_bulk_in_buf));
         TU_VERIFY( usbd_edpt_xfer(rhport, usbtmc_state.ep_bulk_in,
-            (void*)(uintptr_t) usbtmc_state.devInBuffer, sizeof(usbtmc_state.ep_bulk_in_buf)));
+            usbtmc_state.ep_bulk_in_buf, sizeof(usbtmc_state.ep_bulk_in_buf)));
         usbtmc_state.devInBuffer += sizeof(usbtmc_state.ep_bulk_in_buf);
         usbtmc_state.transfer_size_remaining -= sizeof(usbtmc_state.ep_bulk_in_buf);
         usbtmc_state.transfer_size_sent += sizeof(usbtmc_state.ep_bulk_in_buf);
@@ -585,7 +606,9 @@ bool usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
     }
   }
   else if (ep_addr == usbtmc_state.ep_int_in) {
-    // Good?
+    if (tud_usbtmc_notification_complete_cb) {
+      TU_VERIFY(tud_usbtmc_notification_complete_cb());
+    }
     return true;
   }
   return false;
diff --git a/src/class/usbtmc/usbtmc_device.h b/src/class/usbtmc/usbtmc_device.h
index 3299a36eb..b85ef12b5 100644
--- a/src/class/usbtmc/usbtmc_device.h
+++ b/src/class/usbtmc/usbtmc_device.h
@@ -73,6 +73,10 @@ bool tud_usbtmc_check_abort_bulk_in_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
 bool tud_usbtmc_check_abort_bulk_out_cb(usbtmc_check_abort_bulk_rsp_t *rsp);
 bool tud_usbtmc_check_clear_cb(usbtmc_get_clear_status_rsp_t *rsp);
 
+// The interrupt-IN endpoint buffer was transmitted to the host. Use
+// tud_usbtmc_transmit_notification_data to send another notification.
+TU_ATTR_WEAK bool tud_usbtmc_notification_complete_cb(void);
+
 // Indicator pulse should be 0.5 to 1.0 seconds long
 TU_ATTR_WEAK bool tud_usbtmc_indicator_pulse_cb(tusb_control_request_t const * msg, uint8_t *tmcResult);
 
@@ -82,17 +86,23 @@ TU_ATTR_WEAK bool tud_usbtmc_msg_trigger_cb(usbtmc_msg_generic_t* msg);
 //TU_ATTR_WEAK bool tud_usbtmc_app_go_to_local_cb();
 #endif
 
-/*******************************************
- * Called from app
- *
- * We keep a reference to the buffer, so it MUST not change until the app is
- * notified that the transfer is complete.
- ******************************************/
-
+// Called from app
+//
+// We keep a reference to the buffer, so it MUST not change until the app is
+// notified that the transfer is complete.
 bool tud_usbtmc_transmit_dev_msg_data(
     const void * data, size_t len,
     bool endOfMessage, bool usingTermChar);
 
+// Buffers a notification to be sent to the host. The data starts
+// with the bNotify1 field, see the USBTMC Specification, Table 13.
+//
+// If the previous notification data has not yet been sent, this
+// returns false.
+//
+// Requires an interrupt endpoint in the interface.
+bool tud_usbtmc_transmit_notification_data(const void * data, size_t len);
+
 bool tud_usbtmc_start_bus_read(void);
 
 
@@ -105,9 +115,4 @@ void     usbtmcd_reset_cb(uint8_t rhport);
 bool     usbtmcd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes);
 bool     usbtmcd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
 
-/************************************************************
- * USBTMC Descriptor Templates
- *************************************************************/
-
-
 #endif /* CLASS_USBTMC_USBTMC_DEVICE_H_ */
diff --git a/src/class/vendor/vendor_device.c b/src/class/vendor/vendor_device.c
index dcd629965..06c663f1b 100644
--- a/src/class/vendor/vendor_device.c
+++ b/src/class/vendor/vendor_device.c
@@ -36,6 +36,8 @@
 //--------------------------------------------------------------------+
 // MACRO CONSTANT TYPEDEF
 //--------------------------------------------------------------------+
+#define BULK_PACKET_SIZE (TUD_OPT_HIGH_SPEED ? 512 : 64)
+
 typedef struct
 {
   uint8_t itf_num;
@@ -331,7 +333,6 @@ uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, ui
 
 bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
 {
-  (void) rhport;
   (void) result;
 
   uint8_t itf = 0;
@@ -364,7 +365,18 @@ bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint
     if (tud_vendor_tx_cb) tud_vendor_tx_cb(itf, (uint16_t) xferred_bytes);
 #if CFG_TUD_VENDOR_TX_BUFSIZE > 0
     // Send complete, try to send more if possible
-    tud_vendor_n_write_flush(itf);
+    if ( 0 == tud_vendor_n_write_flush(itf) )
+    {
+      // If there is no data left, a ZLP should be sent if
+      // xferred_bytes is multiple of EP Packet size and not zero
+      if ( !tu_fifo_count(&p_itf->tx_ff) && xferred_bytes && (0 == (xferred_bytes & (BULK_PACKET_SIZE-1))) )
+      {
+        if ( usbd_edpt_claim(rhport, p_itf->ep_in) )
+        {
+          usbd_edpt_xfer(rhport, p_itf->ep_in, NULL, 0);
+        }
+      }
+    }
 #endif
   }
 
diff --git a/src/class/video/video_device.c b/src/class/video/video_device.c
index d0a88eb17..4218835aa 100644
--- a/src/class/video/video_device.c
+++ b/src/class/video/video_device.c
@@ -763,8 +763,8 @@ static bool _open_vs_itf(uint8_t rhport, videod_streaming_interface_t *stm, uint
     tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
     /* Only ISO endpoints needs to be closed */
     if(ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
-      usbd_edpt_close(rhport, ep->bEndpointAddress);
       stm->desc.ep[i] = 0;
+      usbd_edpt_close(rhport, ep->bEndpointAddress);
       TU_LOG_DRV("    close EP%02x\r\n", ep->bEndpointAddress);
     }
   }
@@ -1186,6 +1186,16 @@ bool tud_video_n_streaming(uint_fast8_t ctl_idx, uint_fast8_t stm_idx)
   videod_streaming_interface_t *stm = _get_instance_streaming(ctl_idx, stm_idx);
   if (!stm || !stm->desc.ep[0]) return false;
   if (stm->state == VS_STATE_PROBING) return false;
+
+#ifdef TUP_DCD_EDPT_ISO_ALLOC
+  uint8_t const *desc = _videod_itf[stm->index_vc].beg;
+  uint_fast16_t ofs_ep = stm->desc.ep[0];
+  tusb_desc_endpoint_t const *ep = (tusb_desc_endpoint_t const*)(desc + ofs_ep);
+  if (ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
+    if (stm->state == VS_STATE_COMMITTED) return false;
+  }
+#endif
+
   return true;
 }
 
diff --git a/src/common/tusb_common.h b/src/common/tusb_common.h
index 1f08ce4ed..0d4082c03 100644
--- a/src/common/tusb_common.h
+++ b/src/common/tusb_common.h
@@ -65,6 +65,7 @@
 // Standard Headers
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
diff --git a/src/common/tusb_compiler.h b/src/common/tusb_compiler.h
index 6f07bdd53..ce5566ffe 100644
--- a/src/common/tusb_compiler.h
+++ b/src/common/tusb_compiler.h
@@ -56,7 +56,7 @@
 #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
   #define TU_VERIFY_STATIC   _Static_assert
 #elif defined(__CCRX__)
-  #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(Line, __LINE__)[(const_expr) ? 1 : 0];
+  #define TU_VERIFY_STATIC(const_expr, _mess) typedef char TU_XSTRCAT(_verify_static_, _TU_COUNTER_)[(const_expr) ? 1 : 0];
 #else
   #define TU_VERIFY_STATIC(const_expr, _mess) enum { TU_XSTRCAT(_verify_static_, _TU_COUNTER_) = 1/(!!(const_expr)) }
 #endif
@@ -128,6 +128,7 @@
   #define TU_ATTR_SECTION(sec_name)     __attribute__ ((section(#sec_name)))
   #define TU_ATTR_PACKED                __attribute__ ((packed))
   #define TU_ATTR_WEAK                  __attribute__ ((weak))
+  // #define TU_ATTR_WEAK_ALIAS(f)         __attribute__ ((weak, alias(#f))
   #ifndef TU_ATTR_ALWAYS_INLINE // allow to override for debug
     #define TU_ATTR_ALWAYS_INLINE       __attribute__ ((always_inline))
   #endif
diff --git a/src/common/tusb_fifo.c b/src/common/tusb_fifo.c
index 76696396b..8a0fd4417 100644
--- a/src/common/tusb_fifo.c
+++ b/src/common/tusb_fifo.c
@@ -62,7 +62,9 @@ TU_ATTR_ALWAYS_INLINE static inline void _ff_unlock(osal_mutex_t mutex)
 typedef enum
 {
   TU_FIFO_COPY_INC,            ///< Copy from/to an increasing source/destination address - default mode
+#ifdef TUP_MEM_CONST_ADDR
   TU_FIFO_COPY_CST_FULL_WORDS, ///< Copy from/to a constant source/destination address - required for e.g. STM32 to write into USB hardware FIFO
+#endif
 } tu_fifo_copy_mode_t;
 
 bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable)
@@ -92,6 +94,7 @@ bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_si
 // Pull & Push
 //--------------------------------------------------------------------+
 
+#ifdef TUP_MEM_CONST_ADDR
 // Intended to be used to read from hardware USB FIFO in e.g. STM32 where all data is read from a constant address
 // Code adapted from dcd_synopsys.c
 // TODO generalize with configurable 1 byte or 4 byte each read
@@ -140,6 +143,7 @@ static void _ff_pull_const_addr(void * app_buf, const uint8_t * ff_buf, uint16_t
     *reg_tx = tmp32;
   }
 }
+#endif
 
 // send one item to fifo WITHOUT updating write pointer
 static inline void _ff_push(tu_fifo_t* f, void const * app_buf, uint16_t rel)
@@ -179,7 +183,7 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
         memcpy(f->buffer, ((uint8_t const*) app_buf) + lin_bytes, wrap_bytes);
       }
       break;
-
+#ifdef TUP_MEM_CONST_ADDR
     case TU_FIFO_COPY_CST_FULL_WORDS:
       // Intended for hardware buffers from which it can be read word by word only
       if(n <= lin_count)
@@ -224,6 +228,7 @@ static void _ff_push_n(tu_fifo_t* f, void const * app_buf, uint16_t n, uint16_t
         if (wrap_bytes > 0) _ff_push_const_addr(ff_buf, app_buf, wrap_bytes);
       }
       break;
+#endif
     default: break;
   }
 }
@@ -265,7 +270,7 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr,
         memcpy((uint8_t*) app_buf + lin_bytes, f->buffer, wrap_bytes);
       }
     break;
-
+#ifdef TUP_MEM_CONST_ADDR
     case TU_FIFO_COPY_CST_FULL_WORDS:
       if ( n <= lin_count )
       {
@@ -310,6 +315,7 @@ static void _ff_pull_n(tu_fifo_t* f, void* app_buf, uint16_t n, uint16_t rd_ptr,
         // Read data wrapped part
         if (wrap_bytes > 0) _ff_pull_const_addr(app_buf, ff_buf, wrap_bytes);
       }
+#endif
     break;
 
     default: break;
@@ -727,10 +733,29 @@ uint16_t tu_fifo_read_n(tu_fifo_t* f, void * buffer, uint16_t n)
   return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_INC);
 }
 
+#ifdef TUP_MEM_CONST_ADDR
+/******************************************************************************/
+/*!
+    @brief This function will read n elements from the array index specified by
+    the read pointer and increment the read index.
+    This function checks for an overflow and corrects read pointer if required.
+    The dest address will not be incremented which is useful for writing to registers.
+
+    @param[in]  f
+                Pointer to the FIFO buffer to manipulate
+    @param[in]  buffer
+                The pointer to data location
+    @param[in]  n
+                Number of element that buffer can afford
+
+    @returns number of items read from the FIFO
+ */
+/******************************************************************************/
 uint16_t tu_fifo_read_n_const_addr_full_words(tu_fifo_t* f, void * buffer, uint16_t n)
 {
   return _tu_fifo_read_n(f, buffer, n, TU_FIFO_COPY_CST_FULL_WORDS);
 }
+#endif
 
 /******************************************************************************/
 /*!
@@ -839,6 +864,7 @@ uint16_t tu_fifo_write_n(tu_fifo_t* f, const void * data, uint16_t n)
   return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_INC);
 }
 
+#ifdef TUP_MEM_CONST_ADDR
 /******************************************************************************/
 /*!
     @brief This function will write n elements into the array index specified by
@@ -858,6 +884,7 @@ uint16_t tu_fifo_write_n_const_addr_full_words(tu_fifo_t* f, const void * data,
 {
   return _tu_fifo_write_n(f, data, n, TU_FIFO_COPY_CST_FULL_WORDS);
 }
+#endif
 
 /******************************************************************************/
 /*!
diff --git a/src/common/tusb_fifo.h b/src/common/tusb_fifo.h
index 2d9f5e667..6c0efb509 100644
--- a/src/common/tusb_fifo.h
+++ b/src/common/tusb_fifo.h
@@ -145,22 +145,26 @@ bool tu_fifo_clear(tu_fifo_t *f);
 bool tu_fifo_config(tu_fifo_t *f, void* buffer, uint16_t depth, uint16_t item_size, bool overwritable);
 
 #if OSAL_MUTEX_REQUIRED
-  TU_ATTR_ALWAYS_INLINE static inline
-  void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) {
-    f->mutex_wr = wr_mutex;
-    f->mutex_rd = rd_mutex;
-  }
+TU_ATTR_ALWAYS_INLINE static inline
+void tu_fifo_config_mutex(tu_fifo_t *f, osal_mutex_t wr_mutex, osal_mutex_t rd_mutex) {
+  f->mutex_wr = wr_mutex;
+  f->mutex_rd = rd_mutex;
+}
 #else
-  #define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex)
+#define tu_fifo_config_mutex(_f, _wr_mutex, _rd_mutex)
 #endif
 
 bool     tu_fifo_write                  (tu_fifo_t* f, void const * p_data);
 uint16_t tu_fifo_write_n                (tu_fifo_t* f, void const * p_data, uint16_t n);
+#ifdef TUP_MEM_CONST_ADDR
 uint16_t tu_fifo_write_n_const_addr_full_words    (tu_fifo_t* f, const void * data, uint16_t n);
+#endif
 
 bool     tu_fifo_read                   (tu_fifo_t* f, void * p_buffer);
 uint16_t tu_fifo_read_n                 (tu_fifo_t* f, void * p_buffer, uint16_t n);
+#ifdef TUP_MEM_CONST_ADDR
 uint16_t tu_fifo_read_n_const_addr_full_words     (tu_fifo_t* f, void * buffer, uint16_t n);
+#endif
 
 bool     tu_fifo_peek                   (tu_fifo_t* f, void * p_buffer);
 uint16_t tu_fifo_peek_n                 (tu_fifo_t* f, void * p_buffer, uint16_t n);
diff --git a/src/common/tusb_mcu.h b/src/common/tusb_mcu.h
index 0ae2573cd..637281bfc 100644
--- a/src/common/tusb_mcu.h
+++ b/src/common/tusb_mcu.h
@@ -269,16 +269,23 @@
   #define TUP_DCD_ENDPOINT_MAX    8
 
 #elif TU_CHECK_MCU(OPT_MCU_STM32U5)
-  #define TUP_USBIP_DWC2
-  #define TUP_USBIP_DWC2_STM32
+  #if defined (STM32U535xx) || defined (STM32U545xx)
+    #define TUP_USBIP_FSDEV
+    #define TUP_USBIP_FSDEV_STM32
+    #define TUP_DCD_ENDPOINT_MAX    8
 
-  // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY
-  #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \
-      defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx)
-    #define TUP_DCD_ENDPOINT_MAX  9
-    #define TUP_RHPORT_HIGHSPEED  1
   #else
-    #define TUP_DCD_ENDPOINT_MAX  6
+    #define TUP_USBIP_DWC2
+    #define TUP_USBIP_DWC2_STM32
+
+    // U59x/5Ax/5Fx/5Gx are highspeed with built-in HS PHY
+    #if defined(STM32U595xx) || defined(STM32U599xx) || defined(STM32U5A5xx) || defined(STM32U5A9xx) || \
+        defined(STM32U5F7xx) || defined(STM32U5F9xx) || defined(STM32U5G7xx) || defined(STM32U5G9xx)
+      #define TUP_DCD_ENDPOINT_MAX  9
+      #define TUP_RHPORT_HIGHSPEED  1
+    #else
+      #define TUP_DCD_ENDPOINT_MAX  6
+    #endif
   #endif
 
 #elif TU_CHECK_MCU(OPT_MCU_STM32L5)
@@ -329,6 +336,9 @@
   #define TUP_USBIP_DWC2
   #define TUP_DCD_ENDPOINT_MAX    6
 
+#elif TU_CHECK_MCU(OPT_MCU_ESP32, OPT_MCU_ESP32C2, OPT_MCU_ESP32C3, OPT_MCU_ESP32C6, OPT_MCU_ESP32H2) && (CFG_TUD_ENABLED || !(defined(CFG_TUH_MAX3421) && CFG_TUH_MAX3421))
+  #error "MCUs are only supported with CFG_TUH_MAX3421 enabled"
+
 //--------------------------------------------------------------------+
 // Dialog
 //--------------------------------------------------------------------+
@@ -396,16 +406,66 @@
 #elif TU_CHECK_MCU(OPT_MCU_F1C100S)
   #define TUP_DCD_ENDPOINT_MAX    4
 
-//------------- WCH -------------//
-#elif TU_CHECK_MCU(OPT_MCU_CH32V307)
-  #define TUP_DCD_ENDPOINT_MAX    16
-  #define TUP_RHPORT_HIGHSPEED    1
-
+//--------------------------------------------------------------------+
+// WCH
+//--------------------------------------------------------------------+
 #elif TU_CHECK_MCU(OPT_MCU_CH32F20X)
-  #define TUP_DCD_ENDPOINT_MAX    16
-  #define TUP_RHPORT_HIGHSPEED    1
-#endif
+  #define TUP_USBIP_WCH_USBHS
+  #define TUP_USBIP_WCH_USBFS
 
+  #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+  #define CFG_TUD_WCH_USBIP_USBFS 0
+  #endif
+
+  #if !defined(CFG_TUD_WCH_USBIP_USBHS)
+  #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
+  #endif
+
+  #define TUP_RHPORT_HIGHSPEED    CFG_TUD_WCH_USBIP_USBHS
+  #define TUP_DCD_ENDPOINT_MAX    (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V103)
+  #define TUP_USBIP_WCH_USBFS
+
+  #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+  #define CFG_TUD_WCH_USBIP_USBFS 1
+  #endif
+
+  #define TUP_DCD_ENDPOINT_MAX    8
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V20X)
+  // v20x support both FSDEV (USBD) and USBFS, default to FSDEV
+  #define TUP_USBIP_WCH_USBFS
+  #define TUP_USBIP_FSDEV
+  #define TUP_USBIP_FSDEV_CH32
+
+  #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+  #define CFG_TUD_WCH_USBIP_USBFS 0
+  #endif
+
+  #if !defined(CFG_TUD_WCH_USBIP_FSDEV)
+  #define CFG_TUD_WCH_USBIP_FSDEV  (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
+  #endif
+
+  #define TUP_DCD_ENDPOINT_MAX    8
+
+#elif TU_CHECK_MCU(OPT_MCU_CH32V307)
+  // v307 support both FS and HS, default to HS
+  #define TUP_USBIP_WCH_USBHS
+  #define TUP_USBIP_WCH_USBFS
+
+  #if !defined(CFG_TUD_WCH_USBIP_USBFS)
+  #define CFG_TUD_WCH_USBIP_USBFS 0
+  #endif
+
+  #if !defined(CFG_TUD_WCH_USBIP_USBHS)
+  #define CFG_TUD_WCH_USBIP_USBHS (CFG_TUD_WCH_USBIP_USBFS ? 0 : 1)
+  #endif
+
+  #define TUP_RHPORT_HIGHSPEED    CFG_TUD_WCH_USBIP_USBHS
+  #define TUP_DCD_ENDPOINT_MAX    (CFG_TUD_WCH_USBIP_USBHS ? 16 : 8)
+
+#endif
 
 //--------------------------------------------------------------------+
 // External USB controller
@@ -426,7 +486,7 @@
 #define TUP_MCU_MULTIPLE_CORE 0
 #endif
 
-#ifndef TUP_DCD_ENDPOINT_MAX
+#if !defined(TUP_DCD_ENDPOINT_MAX) && defined(CFG_TUD_ENABLED) && CFG_TUD_ENABLED
   #warning "TUP_DCD_ENDPOINT_MAX is not defined for this MCU, default to 8"
   #define TUP_DCD_ENDPOINT_MAX    8
 #endif
@@ -445,4 +505,8 @@
   #define TUP_DCD_EDPT_ISO_ALLOC
 #endif
 
+#if defined(TUP_USBIP_DWC2)
+  #define TUP_MEM_CONST_ADDR
+#endif
+
 #endif
diff --git a/src/common/tusb_types.h b/src/common/tusb_types.h
index 2c5dce723..1501a5af6 100644
--- a/src/common/tusb_types.h
+++ b/src/common/tusb_types.h
@@ -24,12 +24,8 @@
  * This file is part of the TinyUSB stack.
  */
 
-/** \ingroup group_usb_definitions
- *  \defgroup USBDef_Type USB Types
- *  @{ */
-
-#ifndef _TUSB_TYPES_H_
-#define _TUSB_TYPES_H_
+#ifndef TUSB_TYPES_H_
+#define TUSB_TYPES_H_
 
 #include 
 #include 
@@ -212,12 +208,21 @@ typedef enum {
 } device_capability_type_t;
 
 enum {
-  TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = TU_BIT(5),
-  TUSB_DESC_CONFIG_ATT_SELF_POWERED  = TU_BIT(6),
+  TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP = 1u << 5,
+  TUSB_DESC_CONFIG_ATT_SELF_POWERED  = 1u << 6,
 };
 
 #define TUSB_DESC_CONFIG_POWER_MA(x)  ((x)/2)
 
+// USB 2.0 Spec Table 9-7: Test Mode Selectors
+typedef enum {
+  TUSB_FEATURE_TEST_J = 1,
+  TUSB_FEATURE_TEST_K,
+  TUSB_FEATURE_TEST_SE0_NAK,
+  TUSB_FEATURE_TEST_PACKET,
+  TUSB_FEATURE_TEST_FORCE_ENABLE,
+} tusb_feature_test_mode_t;
+
 //--------------------------------------------------------------------+
 //
 //--------------------------------------------------------------------+
@@ -274,11 +279,11 @@ TU_ATTR_BIT_FIELD_ORDER_BEGIN
 typedef struct TU_ATTR_PACKED {
   uint8_t  bLength            ; ///< Size of this descriptor in bytes.
   uint8_t  bDescriptorType    ; ///< DEVICE Descriptor Type.
-  uint16_t bcdUSB             ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H). This field identifies the release of the USB Specification with which the device and its descriptors are compliant.
+  uint16_t bcdUSB             ; ///< BUSB Specification Release Number in Binary-Coded Decimal (i.e., 2.10 is 210H).
 
-  uint8_t  bDeviceClass       ; ///< Class code (assigned by the USB-IF). \li If this field is reset to zero, each interface within a configuration specifies its own class information and the various interfaces operate independently. \li If this field is set to a value between 1 and FEH, the device supports different class specifications on different interfaces and the interfaces may not operate independently. This value identifies the class definition used for the aggregate interfaces. \li If this field is set to FFH, the device class is vendor-specific.
-  uint8_t  bDeviceSubClass    ; ///< Subclass code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass field. \li If the bDeviceClass field is reset to zero, this field must also be reset to zero. \li If the bDeviceClass field is not set to FFH, all values are reserved for assignment by the USB-IF.
-  uint8_t  bDeviceProtocol    ; ///< Protocol code (assigned by the USB-IF). These codes are qualified by the value of the bDeviceClass and the bDeviceSubClass fields. If a device supports class-specific protocols on a device basis as opposed to an interface basis, this code identifies the protocols that the device uses as defined by the specification of the device class. \li If this field is reset to zero, the device does not use class-specific protocols on a device basis. However, it may use classspecific protocols on an interface basis. \li If this field is set to FFH, the device uses a vendor-specific protocol on a device basis.
+  uint8_t  bDeviceClass       ; ///< Class code (assigned by the USB-IF).
+  uint8_t  bDeviceSubClass    ; ///< Subclass code (assigned by the USB-IF).
+  uint8_t  bDeviceProtocol    ; ///< Protocol code (assigned by the USB-IF).
   uint8_t  bMaxPacketSize0    ; ///< Maximum packet size for endpoint zero (only 8, 16, 32, or 64 are valid). For HS devices is fixed to 64.
 
   uint16_t idVendor           ; ///< Vendor ID (assigned by the USB-IF).
@@ -493,15 +498,10 @@ TU_ATTR_ALWAYS_INLINE static inline uint8_t tu_edpt_addr(uint8_t num, uint8_t di
 }
 
 TU_ATTR_ALWAYS_INLINE static inline uint16_t tu_edpt_packet_size(tusb_desc_endpoint_t const* desc_ep) {
-  return tu_le16toh(desc_ep->wMaxPacketSize) & TU_GENMASK(10, 0);
+  return tu_le16toh(desc_ep->wMaxPacketSize) & 0x7FF;
 }
 
 #if CFG_TUSB_DEBUG
-TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_dir_str(tusb_dir_t dir) {
-  tu_static const char *str[] = {"out", "in"};
-  return str[dir];
-}
-
 TU_ATTR_ALWAYS_INLINE static inline const char *tu_edpt_type_str(tusb_xfer_type_t t) {
   tu_static const char *str[] = {"control", "isochronous", "bulk", "interrupt"};
   return str[t];
@@ -541,6 +541,4 @@ uint8_t const * tu_desc_find3(uint8_t const* desc, uint8_t const* end, uint8_t b
  }
 #endif
 
-#endif /* _TUSB_TYPES_H_ */
-
-/** @} */
+#endif // TUSB_TYPES_H_
diff --git a/src/common/tusb_verify.h b/src/common/tusb_verify.h
index 8aa66b4df..4344575b7 100644
--- a/src/common/tusb_verify.h
+++ b/src/common/tusb_verify.h
@@ -56,8 +56,8 @@
  *   #define TU_VERIFY(cond)                  if(cond) return false;
  *   #define TU_VERIFY(cond,ret)              if(cond) return ret;
  *
- *   #define TU_ASSERT(cond)                  if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return false;}
- *   #define TU_ASSERT(cond,ret)              if(cond) {_MESS_FAILED(); TU_BREAKPOINT(), return ret;}
+ *   #define TU_ASSERT(cond)                  if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return false;}
+ *   #define TU_ASSERT(cond,ret)              if(cond) {TU_MESS_FAILED(); TU_BREAKPOINT(), return ret;}
  *------------------------------------------------------------------*/
 
 #ifdef __cplusplus
@@ -70,20 +70,20 @@
 
 #if CFG_TUSB_DEBUG
   #include 
-  #define _MESS_FAILED()    tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
+  #define TU_MESS_FAILED()    tu_printf("%s %d: ASSERT FAILED\r\n", __func__, __LINE__)
 #else
-  #define _MESS_FAILED() do {} while (0)
+  #define TU_MESS_FAILED() do {} while (0)
 #endif
 
 // Halt CPU (breakpoint) when hitting error, only apply for Cortex M3, M4, M7, M33. M55
-#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__)
-  #define TU_BREAKPOINT() do                                                                                \
-  {                                                                                                         \
+#if defined(__ARM_ARCH_7M__) || defined (__ARM_ARCH_7EM__) || defined(__ARM_ARCH_8M_MAIN__) || defined(__ARM_ARCH_8_1M_MAIN__) || \
+    defined(__ARM7M__) || defined (__ARM7EM__) || defined(__ARM8M_MAINLINE__) || defined(__ARM8EM_MAINLINE__)
+  #define TU_BREAKPOINT() do {                                                                              \
     volatile uint32_t* ARM_CM_DHCSR =  ((volatile uint32_t*) 0xE000EDF0UL); /* Cortex M CoreDebug->DHCSR */ \
     if ( (*ARM_CM_DHCSR) & 1UL ) __asm("BKPT #0\n"); /* Only halt mcu if debugger is attached */            \
   } while(0)
 
-#elif defined(__riscv)
+#elif defined(__riscv) && !TUP_MCU_ESPRESSIF
   #define TU_BREAKPOINT() do { __asm("ebreak\n"); } while(0)
 
 #elif defined(_mips)
@@ -119,7 +119,7 @@
  *------------------------------------------------------------------*/
 #define TU_ASSERT_DEFINE(_cond, _ret)                                 \
   do {                                                                \
-    if ( !(_cond) ) { _MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \
+    if ( !(_cond) ) { TU_MESS_FAILED(); TU_BREAKPOINT(); return _ret; } \
   } while(0)
 
 #define TU_ASSERT_1ARGS(_cond)         TU_ASSERT_DEFINE(_cond, false)
diff --git a/src/device/dcd.h b/src/device/dcd.h
index d4f105aa3..41e0fbee3 100644
--- a/src/device/dcd.h
+++ b/src/device/dcd.h
@@ -24,8 +24,8 @@
  * This file is part of the TinyUSB stack.
  */
 
-#ifndef _TUSB_DCD_H_
-#define _TUSB_DCD_H_
+#ifndef TUSB_DCD_H_
+#define TUSB_DCD_H_
 
 #include "common/tusb_common.h"
 #include "osal/osal.h"
@@ -35,32 +35,20 @@
  extern "C" {
 #endif
 
-//--------------------------------------------------------------------+
-// Configuration
-//--------------------------------------------------------------------+
-
-#ifndef CFG_TUD_ENDPPOINT_MAX
-  #define CFG_TUD_ENDPPOINT_MAX   TUP_DCD_ENDPOINT_MAX
-#endif
-
 //--------------------------------------------------------------------+
 // MACRO CONSTANT TYPEDEF PROTYPES
 //--------------------------------------------------------------------+
 
 typedef enum {
-  DCD_EVENT_INVALID = 0,
-  DCD_EVENT_BUS_RESET,
-  DCD_EVENT_UNPLUGGED,
-  DCD_EVENT_SOF,
-  DCD_EVENT_SUSPEND, // TODO LPM Sleep L1 support
-  DCD_EVENT_RESUME,
-
-  DCD_EVENT_SETUP_RECEIVED,
-  DCD_EVENT_XFER_COMPLETE,
-
-  // Not an DCD event, just a convenient way to defer ISR function
-  USBD_EVENT_FUNC_CALL,
-
+  DCD_EVENT_INVALID = 0,    // 0
+  DCD_EVENT_BUS_RESET,      // 1
+  DCD_EVENT_UNPLUGGED,      // 2
+  DCD_EVENT_SOF,            // 3
+  DCD_EVENT_SUSPEND,        // 4 TODO LPM Sleep L1 support
+  DCD_EVENT_RESUME,         // 5
+  DCD_EVENT_SETUP_RECEIVED, // 6
+  DCD_EVENT_XFER_COMPLETE,  // 7
+  USBD_EVENT_FUNC_CALL,     // 8 Not an DCD event, just a convenient way to defer ISR function
   DCD_EVENT_COUNT
 } dcd_eventid_t;
 
@@ -141,14 +129,18 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr);
 void dcd_remote_wakeup(uint8_t rhport);
 
 // Connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(uint8_t rhport) TU_ATTR_WEAK;
+void dcd_connect(uint8_t rhport);
 
 // Disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(uint8_t rhport) TU_ATTR_WEAK;
+void dcd_disconnect(uint8_t rhport);
 
 // Enable/Disable Start-of-frame interrupt. Default is disabled
 void dcd_sof_enable(uint8_t rhport, bool en);
 
+#if CFG_TUD_TEST_MODE
+// Put device into a test mode (needs power cycle to quit)
+void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector);
+#endif
 //--------------------------------------------------------------------+
 // Endpoint API
 //--------------------------------------------------------------------+
@@ -188,7 +180,7 @@ void dcd_edpt_clear_stall     (uint8_t rhport, uint8_t ep_addr);
 TU_ATTR_WEAK bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size);
 
 // Configure and enable an ISO endpoint according to descriptor
-TU_ATTR_WEAK bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc);
+TU_ATTR_WEAK bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const * desc_ep);
 
 //--------------------------------------------------------------------+
 // Event API (implemented by stack)
@@ -239,4 +231,4 @@ TU_ATTR_ALWAYS_INLINE static inline void dcd_event_sof(uint8_t rhport, uint32_t
  }
 #endif
 
-#endif /* _TUSB_DCD_H_ */
+#endif
diff --git a/src/device/usbd.c b/src/device/usbd.c
index 96982c30b..6d27a3735 100644
--- a/src/device/usbd.c
+++ b/src/device/usbd.c
@@ -45,15 +45,60 @@
 //--------------------------------------------------------------------+
 // Weak stubs: invoked if no strong implementation is available
 //--------------------------------------------------------------------+
+TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
+  (void) rhport;
+  (void) eventid;
+  (void) in_isr;
+}
+
+TU_ATTR_WEAK void tud_sof_cb(uint32_t frame_count) {
+  (void) frame_count;
+}
+
+TU_ATTR_WEAK uint8_t const* tud_descriptor_bos_cb(void) {
+  return NULL;
+}
+
+TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void) {
+  return NULL;
+}
+
+TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index) {
+  (void) index;
+  return NULL;
+}
+
+TU_ATTR_WEAK void tud_mount_cb(void) {
+}
+
+TU_ATTR_WEAK void tud_umount_cb(void) {
+}
+
+TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en) {
+  (void) remote_wakeup_en;
+}
+
+TU_ATTR_WEAK void tud_resume_cb(void) {
+}
+
+TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const* request) {
+  (void) rhport;
+  (void) stage;
+  (void) request;
+  return false;
+}
+
 TU_ATTR_WEAK bool dcd_deinit(uint8_t rhport) {
   (void) rhport;
   return false;
 }
 
-TU_ATTR_WEAK void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr) {
-  (void)rhport;
-  (void)eventid;
-  (void)in_isr;
+TU_ATTR_WEAK void dcd_connect(uint8_t rhport) {
+  (void) rhport;
+}
+
+TU_ATTR_WEAK void dcd_disconnect(uint8_t rhport) {
+  (void) rhport;
 }
 
 //--------------------------------------------------------------------+
@@ -75,7 +120,7 @@ typedef struct {
   };
   volatile uint8_t cfg_num; // current active configuration (0x00 is not configured)
   uint8_t speed;
-  volatile uint8_t setup_count;
+  volatile uint8_t sof_consumer;
 
   uint8_t itf2drv[CFG_TUD_INTERFACE_MAX];   // map interface number to driver (0xff is invalid)
   uint8_t ep2drv[CFG_TUD_ENDPPOINT_MAX][2]; // map endpoint to driver ( 0xff is invalid ), can use only 4-bit each
@@ -85,21 +130,22 @@ typedef struct {
 }usbd_device_t;
 
 tu_static usbd_device_t _usbd_dev;
+static volatile uint8_t _usbd_queued_setup;
 
 //--------------------------------------------------------------------+
 // Class Driver
 //--------------------------------------------------------------------+
 #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
-  #define DRIVER_NAME(_name)    .name = _name,
+  #define DRIVER_NAME(_name)  _name
 #else
-  #define DRIVER_NAME(_name)
+  #define DRIVER_NAME(_name)  NULL
 #endif
 
 // Built-in class drivers
 tu_static usbd_class_driver_t const _usbd_driver[] = {
     #if CFG_TUD_CDC
     {
-        DRIVER_NAME("CDC")
+        .name             = DRIVER_NAME("CDC"),
         .init             = cdcd_init,
         .deinit           = cdcd_deinit,
         .reset            = cdcd_reset,
@@ -112,7 +158,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_MSC
     {
-        DRIVER_NAME("MSC")
+        .name             = DRIVER_NAME("MSC"),
         .init             = mscd_init,
         .deinit           = NULL,
         .reset            = mscd_reset,
@@ -125,7 +171,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_HID
     {
-        DRIVER_NAME("HID")
+        .name             = DRIVER_NAME("HID"),
         .init             = hidd_init,
         .deinit           = hidd_deinit,
         .reset            = hidd_reset,
@@ -138,7 +184,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_AUDIO
     {
-        DRIVER_NAME("AUDIO")
+        .name             = DRIVER_NAME("AUDIO"),
         .init             = audiod_init,
         .deinit           = audiod_deinit,
         .reset            = audiod_reset,
@@ -151,7 +197,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_VIDEO
     {
-        DRIVER_NAME("VIDEO")
+        .name             = DRIVER_NAME("VIDEO"),
         .init             = videod_init,
         .deinit           = videod_deinit,
         .reset            = videod_reset,
@@ -164,7 +210,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_MIDI
     {
-        DRIVER_NAME("MIDI")
+        .name             = DRIVER_NAME("MIDI"),
         .init             = midid_init,
         .deinit           = midid_deinit,
         .open             = midid_open,
@@ -177,7 +223,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_VENDOR
     {
-        DRIVER_NAME("VENDOR")
+        .name             = DRIVER_NAME("VENDOR"),
         .init             = vendord_init,
         .deinit           = vendord_deinit,
         .reset            = vendord_reset,
@@ -190,7 +236,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_USBTMC
     {
-        DRIVER_NAME("TMC")
+        .name             = DRIVER_NAME("TMC"),
         .init             = usbtmcd_init_cb,
         .deinit           = usbtmcd_deinit,
         .reset            = usbtmcd_reset_cb,
@@ -203,7 +249,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_DFU_RUNTIME
     {
-        DRIVER_NAME("DFU-RUNTIME")
+        .name             = DRIVER_NAME("DFU-RUNTIME"),
         .init             = dfu_rtd_init,
         .deinit           = dfu_rtd_deinit,
         .reset            = dfu_rtd_reset,
@@ -216,7 +262,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_DFU
     {
-        DRIVER_NAME("DFU")
+        .name             = DRIVER_NAME("DFU"),
         .init             = dfu_moded_init,
         .deinit           = dfu_moded_deinit,
         .reset            = dfu_moded_reset,
@@ -229,7 +275,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_ECM_RNDIS || CFG_TUD_NCM
     {
-        DRIVER_NAME("NET")
+        .name             = DRIVER_NAME("NET"),
         .init             = netd_init,
         .deinit           = netd_deinit,
         .reset            = netd_reset,
@@ -242,7 +288,7 @@ tu_static usbd_class_driver_t const _usbd_driver[] = {
 
     #if CFG_TUD_BTH
     {
-        DRIVER_NAME("BTH")
+        .name             = DRIVER_NAME("BTH"),
         .init             = btd_init,
         .deinit           = btd_deinit,
         .reset            = btd_reset,
@@ -275,6 +321,7 @@ TU_ATTR_ALWAYS_INLINE static inline usbd_class_driver_t const * get_driver(uint8
   return driver;
 }
 
+
 //--------------------------------------------------------------------+
 // DCD Event
 //--------------------------------------------------------------------+
@@ -296,9 +343,9 @@ tu_static osal_queue_t _usbd_q;
 #endif
 
 TU_ATTR_ALWAYS_INLINE static inline bool queue_event(dcd_event_t const * event, bool in_isr) {
-  bool ret = osal_queue_send(_usbd_q, event, in_isr);
+  TU_ASSERT(osal_queue_send(_usbd_q, event, in_isr));
   tud_event_hook_cb(event->rhport, event->event_id, in_isr);
-  return ret;
+  return true;
 }
 
 //--------------------------------------------------------------------+
@@ -308,6 +355,16 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
 static bool process_set_config(uint8_t rhport, uint8_t cfg_num);
 static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const * p_request);
 
+#if CFG_TUD_TEST_MODE
+static bool process_test_mode_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request) {
+  TU_VERIFY(CONTROL_STAGE_ACK == stage);
+  uint8_t const selector = tu_u16_high(request->wIndex);
+  TU_LOG_USBD("    Enter Test Mode (test selector index: %d)\r\n", selector);
+  dcd_enter_test_mode(rhport, (tusb_feature_test_mode_t) selector);
+  return true;
+}
+#endif
+
 // from usbd_control.c
 void usbd_control_reset(void);
 void usbd_control_set_request(tusb_control_request_t const *request);
@@ -371,17 +428,19 @@ bool tud_remote_wakeup(void) {
 }
 
 bool tud_disconnect(void) {
-  TU_VERIFY(dcd_disconnect);
   dcd_disconnect(_usbd_rhport);
   return true;
 }
 
 bool tud_connect(void) {
-  TU_VERIFY(dcd_connect);
   dcd_connect(_usbd_rhport);
   return true;
 }
 
+void tud_sof_cb_enable(bool en) {
+  usbd_sof_enable(_usbd_rhport, SOF_CONSUMER_USER, en);
+}
+
 //--------------------------------------------------------------------+
 // USBD Task
 //--------------------------------------------------------------------+
@@ -393,13 +452,14 @@ bool tud_init(uint8_t rhport) {
   // skip if already initialized
   if (tud_inited()) return true;
 
-  TU_LOG_USBD("USBD init on controller %u\r\n", rhport);
+  TU_LOG_USBD("USBD init on controller %u, Highspeed = %u\r\n", rhport, TUD_OPT_HIGH_SPEED);
   TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(usbd_device_t));
   TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(dcd_event_t));
   TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_fifo_t));
   TU_LOG_INT(CFG_TUD_LOG_LEVEL, sizeof(tu_edpt_stream_t));
 
   tu_varclr(&_usbd_dev);
+  _usbd_queued_setup = 0;
 
 #if OSAL_MUTEX_REQUIRED
   // Init device mutex
@@ -531,13 +591,14 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
       case DCD_EVENT_UNPLUGGED:
         TU_LOG_USBD("\r\n");
         usbd_reset(event.rhport);
-        if (tud_umount_cb) tud_umount_cb();
+        tud_umount_cb();
         break;
 
       case DCD_EVENT_SETUP_RECEIVED:
-        _usbd_dev.setup_count--;
+        TU_ASSERT(_usbd_queued_setup > 0,);
+        _usbd_queued_setup--;
         TU_LOG_BUF(CFG_TUD_LOG_LEVEL, &event.setup_received, 8);
-        if (_usbd_dev.setup_count) {
+        if (_usbd_queued_setup) {
           TU_LOG_USBD("  Skipped since there is other SETUP in queue\r\n");
           break;
         }
@@ -591,7 +652,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
         // e.g suspend -> resume -> unplug/plug. Skip suspend/resume if not connected
         if (_usbd_dev.connected) {
           TU_LOG_USBD(": Remote Wakeup = %u\r\n", _usbd_dev.remote_wakeup_en);
-          if (tud_suspend_cb) tud_suspend_cb(_usbd_dev.remote_wakeup_en);
+          tud_suspend_cb(_usbd_dev.remote_wakeup_en);
         } else {
           TU_LOG_USBD(" Skipped\r\n");
         }
@@ -600,7 +661,7 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
       case DCD_EVENT_RESUME:
         if (_usbd_dev.connected) {
           TU_LOG_USBD("\r\n");
-          if (tud_resume_cb) tud_resume_cb();
+          tud_resume_cb();
         } else {
           TU_LOG_USBD(" Skipped\r\n");
         }
@@ -612,6 +673,12 @@ void tud_task_ext(uint32_t timeout_ms, bool in_isr) {
         break;
 
       case DCD_EVENT_SOF:
+        if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) {
+          TU_LOG_USBD("\r\n");
+          tud_sof_cb(event.sof.frame_count);
+        }
+      break;
+
       default:
         TU_BREAKPOINT();
         break;
@@ -636,15 +703,13 @@ static bool invoke_class_control(uint8_t rhport, usbd_class_driver_t const * dri
 }
 
 // This handles the actual request and its response.
-// return false will cause its caller to stall control endpoint
+// Returns false if unable to complete the request, causing caller to stall control endpoints.
 static bool process_control_request(uint8_t rhport, tusb_control_request_t const * p_request) {
   usbd_control_set_complete_callback(NULL);
   TU_ASSERT(p_request->bmRequestType_bit.type < TUSB_REQ_TYPE_INVALID);
 
   // Vendor request
   if ( p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_VENDOR ) {
-    TU_VERIFY(tud_vendor_control_xfer_cb);
-
     usbd_control_set_complete_callback(tud_vendor_control_xfer_cb);
     return tud_vendor_control_xfer_cb(rhport, CONTROL_STAGE_SETUP, p_request);
   }
@@ -671,7 +736,7 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
       }
 
       if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) {
-        // Non standard request is not supported
+        // Non-standard request is not supported
         TU_BREAKPOINT();
         return false;
       }
@@ -702,6 +767,9 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
               // already configured: need to clear all endpoints and driver first
               TU_LOG_USBD("  Clear current Configuration (%u) before switching\r\n", _usbd_dev.cfg_num);
 
+              // disable SOF
+              dcd_sof_enable(rhport, false);
+
               // close all non-control endpoints, cancel all pending transfers if any
               dcd_edpt_close_all(rhport);
 
@@ -712,17 +780,23 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
               _usbd_dev.speed = speed; // restore speed
             }
 
+            _usbd_dev.cfg_num = cfg_num;
+
             // Handle the new configuration and execute the corresponding callback
             if ( cfg_num ) {
               // switch to new configuration if not zero
-              TU_ASSERT( process_set_config(rhport, cfg_num) );
-              if ( tud_mount_cb ) tud_mount_cb();
+              if (!process_set_config(rhport, cfg_num)) {
+                TU_MESS_FAILED();
+                TU_BREAKPOINT();
+                _usbd_dev.cfg_num = 0;
+                return false;
+              }
+              tud_mount_cb();
             } else {
-              if ( tud_umount_cb ) tud_umount_cb();
+              tud_umount_cb();
             }
           }
 
-          _usbd_dev.cfg_num = cfg_num;
           tud_control_status(rhport, p_request);
         }
         break;
@@ -732,14 +806,31 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const
         break;
 
         case TUSB_REQ_SET_FEATURE:
-          // Only support remote wakeup for device feature
-          TU_VERIFY(TUSB_REQ_FEATURE_REMOTE_WAKEUP == p_request->wValue);
+          switch(p_request->wValue) {
+            case TUSB_REQ_FEATURE_REMOTE_WAKEUP:
+              TU_LOG_USBD("    Enable Remote Wakeup\r\n");
+              // Host may enable remote wake up before suspending especially HID device
+              _usbd_dev.remote_wakeup_en = true;
+              tud_control_status(rhport, p_request);
+            break;
 
-          TU_LOG_USBD("    Enable Remote Wakeup\r\n");
+            #if CFG_TUD_TEST_MODE
+            case TUSB_REQ_FEATURE_TEST_MODE: {
+              // Only handle the test mode if supported and valid
+              TU_VERIFY(0 == tu_u16_low(p_request->wIndex));
 
-          // Host may enable remote wake up before suspending especially HID device
-          _usbd_dev.remote_wakeup_en = true;
-          tud_control_status(rhport, p_request);
+              uint8_t const selector = tu_u16_high(p_request->wIndex);
+              TU_VERIFY(TUSB_FEATURE_TEST_J <= selector && selector <= TUSB_FEATURE_TEST_FORCE_ENABLE);
+
+              usbd_control_set_complete_callback(process_test_mode_cb);
+              tud_control_status(rhport, p_request);
+              break;
+            }
+            #endif /* CFG_TUD_TEST_MODE */
+
+            // Stall unsupported feature selector
+            default: return false;
+          }
         break;
 
         case TUSB_REQ_CLEAR_FEATURE:
@@ -969,39 +1060,34 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
 
   switch(desc_type)
   {
-    case TUSB_DESC_DEVICE:
-    {
+    case TUSB_DESC_DEVICE: {
       TU_LOG_USBD(" Device\r\n");
 
       void* desc_device = (void*) (uintptr_t) tud_descriptor_device_cb();
+      TU_ASSERT(desc_device);
 
       // Only response with exactly 1 Packet if: not addressed and host requested more data than device descriptor has.
       // This only happens with the very first get device descriptor and EP0 size = 8 or 16.
       if ((CFG_TUD_ENDPOINT0_SIZE < sizeof(tusb_desc_device_t)) && !_usbd_dev.addressed &&
-          ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t))
-      {
+          ((tusb_control_request_t const*) p_request)->wLength > sizeof(tusb_desc_device_t)) {
         // Hack here: we modify the request length to prevent usbd_control response with zlp
         // since we are responding with 1 packet & less data than wLength.
         tusb_control_request_t mod_request = *p_request;
         mod_request.wLength = CFG_TUD_ENDPOINT0_SIZE;
 
         return tud_control_xfer(rhport, &mod_request, desc_device, CFG_TUD_ENDPOINT0_SIZE);
-      }else
-      {
+      }else {
         return tud_control_xfer(rhport, p_request, desc_device, sizeof(tusb_desc_device_t));
       }
     }
     // break; // unreachable
 
-    case TUSB_DESC_BOS:
-    {
+    case TUSB_DESC_BOS: {
       TU_LOG_USBD(" BOS\r\n");
 
       // requested by host if USB > 2.0 ( i.e 2.1 or 3.x )
-      if (!tud_descriptor_bos_cb) return false;
-
       uintptr_t desc_bos = (uintptr_t) tud_descriptor_bos_cb();
-      TU_ASSERT(desc_bos);
+      TU_VERIFY(desc_bos);
 
       // Use offsetof to avoid pointer to the odd/misaligned address
       uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_bos + offsetof(tusb_desc_bos_t, wTotalLength))) );
@@ -1011,24 +1097,20 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
     // break; // unreachable
 
     case TUSB_DESC_CONFIGURATION:
-    case TUSB_DESC_OTHER_SPEED_CONFIG:
-    {
+    case TUSB_DESC_OTHER_SPEED_CONFIG: {
       uintptr_t desc_config;
 
-      if ( desc_type == TUSB_DESC_CONFIGURATION )
-      {
+      if ( desc_type == TUSB_DESC_CONFIGURATION ) {
         TU_LOG_USBD(" Configuration[%u]\r\n", desc_index);
         desc_config = (uintptr_t) tud_descriptor_configuration_cb(desc_index);
-      }else
-      {
+        TU_ASSERT(desc_config);
+      }else {
         // Host only request this after getting Device Qualifier descriptor
         TU_LOG_USBD(" Other Speed Configuration\r\n");
-        TU_VERIFY( tud_descriptor_other_speed_configuration_cb );
         desc_config = (uintptr_t) tud_descriptor_other_speed_configuration_cb(desc_index);
+        TU_VERIFY(desc_config);
       }
 
-      TU_ASSERT(desc_config);
-
       // Use offsetof to avoid pointer to the odd/misaligned address
       uint16_t const total_len = tu_le16toh( tu_unaligned_read16((const void*) (desc_config + offsetof(tusb_desc_configuration_t, wTotalLength))) );
 
@@ -1049,16 +1131,10 @@ static bool process_get_descriptor(uint8_t rhport, tusb_control_request_t const
     }
     // break; // unreachable
 
-    case TUSB_DESC_DEVICE_QUALIFIER:
-    {
+    case TUSB_DESC_DEVICE_QUALIFIER: {
       TU_LOG_USBD(" Device Qualifier\r\n");
-
-      TU_VERIFY( tud_descriptor_device_qualifier_cb );
-
       uint8_t const* desc_qualifier = tud_descriptor_device_qualifier_cb();
       TU_VERIFY(desc_qualifier);
-
-      // first byte of descriptor is its size
       return tud_control_xfer(rhport, p_request, (void*) (uintptr_t) desc_qualifier, tu_desc_len(desc_qualifier));
     }
     // break; // unreachable
@@ -1101,6 +1177,14 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr)
       break;
 
     case DCD_EVENT_SOF:
+      // SOF driver handler in ISR context
+      for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
+        usbd_class_driver_t const* driver = get_driver(i);
+        if (driver && driver->sof) {
+          driver->sof(event->rhport, event->sof.frame_count);
+        }
+      }
+
       // Some MCUs after running dcd_remote_wakeup() does not have way to detect the end of remote wakeup
       // which last 1-15 ms. DCD can use SOF as a clear indicator that bus is back to operational
       if (_usbd_dev.suspended) {
@@ -1110,19 +1194,15 @@ TU_ATTR_FAST_FUNC void dcd_event_handler(dcd_event_t const* event, bool in_isr)
         queue_event(&event_resume, in_isr);
       }
 
-      // SOF driver handler in ISR context
-      for (uint8_t i = 0; i < TOTAL_DRIVER_COUNT; i++) {
-        usbd_class_driver_t const* driver = get_driver(i);
-        if (driver && driver->sof) {
-          driver->sof(event->rhport, event->sof.frame_count);
-        }
+      if (tu_bit_test(_usbd_dev.sof_consumer, SOF_CONSUMER_USER)) {
+        dcd_event_t const event_sof = {.rhport = event->rhport, .event_id = DCD_EVENT_SOF, .sof.frame_count = event->sof.frame_count};
+        queue_event(&event_sof, in_isr);
       }
-
-      // skip osal queue for SOF in usbd task
       break;
 
     case DCD_EVENT_SETUP_RECEIVED:
-      _usbd_dev.setup_count++;
+      // TU_ASSERT(event->setup_received.bRequest != 0,); // for catching issue with ch32v203 and windows with -O0/-Og
+      _usbd_queued_setup++;
       send = true;
       break;
 
@@ -1306,12 +1386,10 @@ void usbd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
   uint8_t const dir = tu_edpt_dir(ep_addr);
 
   // only stalled if currently cleared
-  if (!_usbd_dev.ep_status[epnum][dir].stalled) {
-    TU_LOG_USBD("    Stall EP %02X\r\n", ep_addr);
-    dcd_edpt_stall(rhport, ep_addr);
-    _usbd_dev.ep_status[epnum][dir].stalled = 1;
-    _usbd_dev.ep_status[epnum][dir].busy = 1;
-  }
+  TU_LOG_USBD("    Stall EP %02X\r\n", ep_addr);
+  dcd_edpt_stall(rhport, ep_addr);
+  _usbd_dev.ep_status[epnum][dir].stalled = 1;
+  _usbd_dev.ep_status[epnum][dir].busy = 1;
 }
 
 void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
@@ -1321,12 +1399,10 @@ void usbd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
   uint8_t const dir = tu_edpt_dir(ep_addr);
 
   // only clear if currently stalled
-  if (_usbd_dev.ep_status[epnum][dir].stalled) {
-    TU_LOG_USBD("    Clear Stall EP %02X\r\n", ep_addr);
-    dcd_edpt_clear_stall(rhport, ep_addr);
-    _usbd_dev.ep_status[epnum][dir].stalled = 0;
-    _usbd_dev.ep_status[epnum][dir].busy = 0;
-  }
+  TU_LOG_USBD("    Clear Stall EP %02X\r\n", ep_addr);
+  dcd_edpt_clear_stall(rhport, ep_addr);
+  _usbd_dev.ep_status[epnum][dir].stalled = 0;
+  _usbd_dev.ep_status[epnum][dir].busy = 0;
 }
 
 bool usbd_edpt_stalled(uint8_t rhport, uint8_t ep_addr) {
@@ -1359,12 +1435,21 @@ void usbd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
   return;
 }
 
-void usbd_sof_enable(uint8_t rhport, bool en) {
+void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en) {
   rhport = _usbd_rhport;
 
-  // TODO: Check needed if all drivers including the user sof_cb does not need an active SOF ISR any more.
-  // Only if all drivers switched off SOF calls the SOF interrupt may be disabled
-  dcd_sof_enable(rhport, en);
+  uint8_t consumer_old = _usbd_dev.sof_consumer;
+  // Keep track how many class instances need the SOF interrupt
+  if (en) {
+    _usbd_dev.sof_consumer |= (uint8_t)(1 << consumer);
+  } else {
+    _usbd_dev.sof_consumer &= (uint8_t)(~(1 << consumer));
+  }
+
+  // Test logically unequal
+  if(!_usbd_dev.sof_consumer != !consumer_old) {
+    dcd_sof_enable(rhport, _usbd_dev.sof_consumer);
+  }
 }
 
 bool usbd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
diff --git a/src/device/usbd.h b/src/device/usbd.h
index 0197628e2..3d3e83f41 100644
--- a/src/device/usbd.h
+++ b/src/device/usbd.h
@@ -60,7 +60,7 @@ void tud_task (void) {
 // Check if there is pending events need processing by tud_task()
 bool tud_task_event_ready(void);
 
-#ifndef _TUSB_DCD_H_
+#ifndef TUSB_DCD_H_
 extern void dcd_int_handler(uint8_t rhport);
 #endif
 
@@ -97,6 +97,9 @@ bool tud_disconnect(void);
 // Return false on unsupported MCUs
 bool tud_connect(void);
 
+// Enable or disable the Start Of Frame callback support
+void tud_sof_cb_enable(bool en);
+
 // Carry out Data and Status stage of control transfer
 // - If len = 0, it is equivalent to sending status only
 // - If len > wLength : it will be truncated
@@ -106,7 +109,7 @@ bool tud_control_xfer(uint8_t rhport, tusb_control_request_t const * request, vo
 bool tud_control_status(uint8_t rhport, tusb_control_request_t const * request);
 
 //--------------------------------------------------------------------+
-// Application Callbacks (WEAK is optional)
+// Application Callbacks
 //--------------------------------------------------------------------+
 
 // Invoked when received GET DEVICE DESCRIPTOR request
@@ -123,37 +126,40 @@ uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid);
 
 // Invoked when received GET BOS DESCRIPTOR request
 // Application return pointer to descriptor
-TU_ATTR_WEAK uint8_t const * tud_descriptor_bos_cb(void);
+uint8_t const * tud_descriptor_bos_cb(void);
 
 // Invoked when received GET DEVICE QUALIFIER DESCRIPTOR request
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete.
 // device_qualifier descriptor describes information about a high-speed capable device that would
 // change if the device were operating at the other speed. If not highspeed capable stall this request.
-TU_ATTR_WEAK uint8_t const* tud_descriptor_device_qualifier_cb(void);
+uint8_t const* tud_descriptor_device_qualifier_cb(void);
 
 // Invoked when received GET OTHER SEED CONFIGURATION DESCRIPTOR request
 // Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
 // Configuration descriptor in the other speed e.g if high speed then this is for full speed and vice versa
-TU_ATTR_WEAK uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index);
+uint8_t const* tud_descriptor_other_speed_configuration_cb(uint8_t index);
 
 // Invoked when device is mounted (configured)
-TU_ATTR_WEAK void tud_mount_cb(void);
+void tud_mount_cb(void);
 
 // Invoked when device is unmounted
-TU_ATTR_WEAK void tud_umount_cb(void);
+void tud_umount_cb(void);
 
 // Invoked when usb bus is suspended
 // Within 7ms, device must draw an average of current less than 2.5 mA from bus
-TU_ATTR_WEAK void tud_suspend_cb(bool remote_wakeup_en);
+void tud_suspend_cb(bool remote_wakeup_en);
 
 // Invoked when usb bus is resumed
-TU_ATTR_WEAK void tud_resume_cb(void);
+void tud_resume_cb(void);
 
 // Invoked when there is a new usb event, which need to be processed by tud_task()/tud_task_ext()
 void tud_event_hook_cb(uint8_t rhport, uint32_t eventid, bool in_isr);
 
+// Invoked when a new (micro) frame started
+void tud_sof_cb(uint32_t frame_count);
+
 // Invoked when received control request with VENDOR TYPE
-TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
+bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
 
 //--------------------------------------------------------------------+
 // Binary Device Object Store (BOS) Descriptor Templates
@@ -221,8 +227,8 @@ TU_ATTR_WEAK bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb
   5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_HEADER, U16_TO_U8S_LE(0x0120),\
   /* CDC Call */\
   5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_CALL_MANAGEMENT, 0, (uint8_t)((_itfnum) + 1),\
-  /* CDC ACM: support line request */\
-  4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 2,\
+  /* CDC ACM: support line request + send break */\
+  4, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, 6,\
   /* CDC Union */\
   5, TUSB_DESC_CS_INTERFACE, CDC_FUNC_DESC_UNION, _itfnum, (uint8_t)((_itfnum) + 1),\
   /* Endpoint Notification */\
diff --git a/src/device/usbd_pvt.h b/src/device/usbd_pvt.h
index 47752f32c..335d46cd8 100644
--- a/src/device/usbd_pvt.h
+++ b/src/device/usbd_pvt.h
@@ -23,8 +23,8 @@
  *
  * This file is part of the TinyUSB stack.
  */
-#ifndef _TUSB_USBD_PVT_H_
-#define _TUSB_USBD_PVT_H_
+#ifndef TUSB_USBD_PVT_H_
+#define TUSB_USBD_PVT_H_
 
 #include "osal/osal.h"
 #include "common/tusb_fifo.h"
@@ -35,15 +35,21 @@
 
 #define TU_LOG_USBD(...)   TU_LOG(CFG_TUD_LOG_LEVEL, __VA_ARGS__)
 
+//--------------------------------------------------------------------+
+// MACRO CONSTANT TYPEDEF PROTYPES
+//--------------------------------------------------------------------+
+
+typedef enum {
+  SOF_CONSUMER_USER = 0,
+  SOF_CONSUMER_AUDIO,
+} sof_consumer_t;
+
 //--------------------------------------------------------------------+
 // Class Driver API
 //--------------------------------------------------------------------+
 
 typedef struct {
-  #if CFG_TUSB_DEBUG >= CFG_TUD_LOG_LEVEL
   char const* name;
-  #endif
-
   void     (* init             ) (void);
   bool     (* deinit           ) (void);
   void     (* reset            ) (uint8_t rhport);
@@ -111,7 +117,7 @@ bool usbd_edpt_ready(uint8_t rhport, uint8_t ep_addr) {
 }
 
 // Enable SOF interrupt
-void usbd_sof_enable(uint8_t rhport, bool en);
+void usbd_sof_enable(uint8_t rhport, sof_consumer_t consumer, bool en);
 
 /*------------------------------------------------------------------*/
 /* Helper
diff --git a/src/host/usbh.c b/src/host/usbh.c
index 6a5491998..8c488aaa2 100644
--- a/src/host/usbh.c
+++ b/src/host/usbh.c
@@ -288,9 +288,9 @@ TU_ATTR_WEAK void osal_task_delay(uint32_t msec) {
 #endif
 
 TU_ATTR_ALWAYS_INLINE static inline bool queue_event(hcd_event_t const * event, bool in_isr) {
-  bool ret = osal_queue_send(_usbh_q, event, in_isr);
+  TU_ASSERT(osal_queue_send(_usbh_q, event, in_isr));
   tuh_event_hook_cb(event->rhport, event->event_id, in_isr);
-  return ret;
+  return true;
 }
 
 //--------------------------------------------------------------------+
@@ -704,7 +704,7 @@ static bool usbh_control_xfer_cb (uint8_t daddr, uint8_t ep_addr, xfer_result_t
   tusb_control_request_t const * request = &_ctrl_xfer.request;
 
   if (XFER_RESULT_SUCCESS != result) {
-    TU_LOG_USBH("[%u:%u] Control %s, xferred_bytes = %lu\r\n", rhport, daddr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
+    TU_LOG_USBH("[%u:%u] Control %s, xferred_bytes = %" PRIu32 "\r\n", rhport, daddr, result == XFER_RESULT_STALLED ? "STALLED" : "FAILED", xferred_bytes);
     TU_LOG_BUF_USBH(request, 8);
 
     // terminate transfer if any stage failed
@@ -1113,7 +1113,7 @@ bool tuh_interface_set(uint8_t daddr, uint8_t itf_num, uint8_t itf_alt,
   TU_LOG_USBH("Set Interface %u Alternate %u\r\n", itf_num, itf_alt);
   tusb_control_request_t const request = {
       .bmRequestType_bit = {
-          .recipient = TUSB_REQ_RCPT_DEVICE,
+          .recipient = TUSB_REQ_RCPT_INTERFACE,
           .type      = TUSB_REQ_TYPE_STANDARD,
           .direction = TUSB_DIR_OUT
       },
@@ -1421,6 +1421,9 @@ static void process_enumeration(tuh_xfer_t* xfer) {
       break;
 
     case ENUM_GET_DEVICE_DESC: {
+      // Allow 2ms for address recovery time, Ref USB Spec 9.2.6.3
+      osal_task_delay(2);
+
       uint8_t const new_addr = (uint8_t) tu_le16toh(xfer->setup->wValue);
 
       usbh_device_t* new_dev = get_device(new_addr);
diff --git a/src/osal/osal_freertos.h b/src/osal/osal_freertos.h
index f1f05f353..a3a0f3a3f 100644
--- a/src/osal/osal_freertos.h
+++ b/src/osal/osal_freertos.h
@@ -78,7 +78,7 @@ typedef struct
 // _int_set is not used with an RTOS
 #define OSAL_QUEUE_DEF(_int_set, _name, _depth, _type) \
   static _type _name##_##buf[_depth];\
-  osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, _OSAL_Q_NAME(_name) };
+  osal_queue_def_t _name = { .depth = _depth, .item_sz = sizeof(_type), .buf = _name##_##buf, _OSAL_Q_NAME(_name) }
 
 //--------------------------------------------------------------------+
 // TASK API
diff --git a/src/osal/osal_none.h b/src/osal/osal_none.h
index c954fcfe8..c93f7a86c 100644
--- a/src/osal/osal_none.h
+++ b/src/osal/osal_none.h
@@ -180,7 +180,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void
     _osal_q_unlock(qhdl);
   }
 
-  TU_ASSERT(success);
   return success;
 }
 
diff --git a/src/osal/osal_pico.h b/src/osal/osal_pico.h
index 00c589ef9..315de0950 100644
--- a/src/osal/osal_pico.h
+++ b/src/osal/osal_pico.h
@@ -145,7 +145,6 @@ TU_ATTR_ALWAYS_INLINE static inline bool osal_queue_send(osal_queue_t qhdl, void
   bool success = tu_fifo_write(&qhdl->ff, data);
   critical_section_exit(&qhdl->critsec);
 
-  TU_ASSERT(success);
   return success;
 }
 
diff --git a/src/portable/analog/max3421/hcd_max3421.c b/src/portable/analog/max3421/hcd_max3421.c
index 39afb7505..d9b8e1fc3 100644
--- a/src/portable/analog/max3421/hcd_max3421.c
+++ b/src/portable/analog/max3421/hcd_max3421.c
@@ -174,7 +174,8 @@ enum {
 enum {
   EP_STATE_IDLE        = 0,
   EP_STATE_COMPLETE    = 1,
-  EP_STATE_ATTEMPT_1   = 2, // pending 1st attempt
+  EP_STATE_ABORTING    = 2,
+  EP_STATE_ATTEMPT_1   = 3, // pending 1st attempt
   EP_STATE_ATTEMPT_MAX = 15
 };
 
@@ -459,6 +460,7 @@ bool hcd_configure(uint8_t rhport, uint32_t cfg_id, const void* cfg_param) {
 
   tuh_configure_param_t const* cfg = (tuh_configure_param_t const*) cfg_param;
   _tuh_cfg = cfg->max3421;
+  _tuh_cfg.max_nak = tu_min8(_tuh_cfg.max_nak, EP_STATE_ATTEMPT_MAX-EP_STATE_ATTEMPT_1);
   return true;
 }
 
@@ -479,13 +481,15 @@ bool hcd_init(uint8_t rhport) {
   _hcd_data.spi_mutex = osal_mutex_create(&_hcd_data.spi_mutexdef);
 #endif
 
+  // NOTE: driver does not seem to work without nRST pin signal
+
   // full duplex, interrupt negative edge
   reg_write(rhport, PINCTL_ADDR, _tuh_cfg.pinctl | PINCTL_FDUPSPI, false);
 
   // v1 is 0x01, v2 is 0x12, v3 is 0x13
   uint8_t const revision = reg_read(rhport, REVISION_ADDR, false);
-  TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false);
   TU_LOG2_HEX(revision);
+  TU_ASSERT(revision == 0x01 || revision == 0x12 || revision == 0x13, false);
 
   // reset
   reg_write(rhport, USBCTL_ADDR, USBCTL_CHIPRES, false);
@@ -676,7 +680,6 @@ static void xact_generic(uint8_t rhport, max3421_ep_t *ep, bool switch_ep, bool
 bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buffer, uint16_t buflen) {
   uint8_t const ep_num = tu_edpt_number(ep_addr);
   uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
-
   max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
   TU_VERIFY(ep);
 
@@ -700,14 +703,19 @@ bool hcd_edpt_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr, uint8_t * buf
   return true;
 }
 
-// Abort a queued transfer. Note: it can only abort transfer that has not been started
-// Return true if a queued transfer is aborted, false if there is no transfer to abort
-bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
-  (void) rhport;
-  (void) dev_addr;
-  (void) ep_addr;
+bool hcd_edpt_abort_xfer(uint8_t rhport, uint8_t daddr, uint8_t ep_addr) {
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  uint8_t const ep_dir = (uint8_t) tu_edpt_dir(ep_addr);
+  max3421_ep_t* ep = find_opened_ep(daddr, ep_num, ep_dir);
+  TU_VERIFY(ep);
 
-  return false;
+  if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) {
+    hcd_int_disable(rhport);
+    ep->state = EP_STATE_ABORTING;
+    hcd_int_enable(rhport);
+  }
+
+  return true;
 }
 
 // Submit a special transfer to send 8-byte Setup Packet, when complete hcd_event_xfer_complete() must be invoked
@@ -817,7 +825,6 @@ static void xfer_complete_isr(uint8_t rhport, max3421_ep_t *ep, xfer_result_t re
 static void handle_xfer_done(uint8_t rhport, bool in_isr) {
   uint8_t const hrsl = reg_read(rhport, HRSL_ADDR, in_isr);
   uint8_t const hresult = hrsl & HRSL_RESULT_MASK;
-
   uint8_t const ep_num = _hcd_data.hxfr & HXFR_EPNUM_MASK;
   uint8_t const hxfr_type = _hcd_data.hxfr & 0xf0;
   uint8_t const ep_dir = ((hxfr_type & HXFR_SETUP) || (hxfr_type & HXFR_OUT_NIN)) ? 0 : 1;
@@ -827,6 +834,37 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
 
   xfer_result_t xfer_result;
   switch(hresult) {
+    case HRSL_NAK:
+      if (ep->state == EP_STATE_ABORTING) {
+        ep->state = EP_STATE_IDLE;
+      } else {
+        if (ep_num == 0) {
+          // control endpoint -> retry immediately and return
+          hxfr_write(rhport, _hcd_data.hxfr, in_isr);
+          return;
+        } else if (EP_STATE_ATTEMPT_1 <= ep->state && ep->state < EP_STATE_ATTEMPT_MAX) {
+          ep->state++;
+        }
+      }
+
+      max3421_ep_t * next_ep = find_next_pending_ep(ep);
+      if (ep == next_ep) {
+        // this endpoint is only one pending -> retry immediately
+        hxfr_write(rhport, _hcd_data.hxfr, in_isr);
+      } else if (next_ep) {
+        // switch to next pending endpoint
+        // TODO could have issue with double buffered if not clear previously out data
+        xact_generic(rhport, next_ep, true, in_isr);
+      } else {
+        // no more pending in this frame -> clear busy
+        atomic_flag_clear(&_hcd_data.busy);
+      }
+      return;
+
+    case HRSL_BAD_REQ:
+      // occurred when initialized without any pending transfer. Skip for now
+      return;
+
     case HRSL_SUCCESS:
       xfer_result = XFER_RESULT_SUCCESS;
       break;
@@ -835,33 +873,6 @@ static void handle_xfer_done(uint8_t rhport, bool in_isr) {
       xfer_result = XFER_RESULT_STALLED;
       break;
 
-    case HRSL_NAK:
-      if (ep_num == 0) {
-        // control endpoint -> retry immediately
-        hxfr_write(rhport, _hcd_data.hxfr, in_isr);
-      } else {
-        if (ep->state < EP_STATE_ATTEMPT_MAX) {
-          ep->state++;
-        }
-
-        max3421_ep_t * next_ep = find_next_pending_ep(ep);
-        if (ep == next_ep) {
-          // this endpoint is only one pending -> retry immediately
-          hxfr_write(rhport, _hcd_data.hxfr, in_isr);
-        } else if (next_ep) {
-          // switch to next pending endpoint TODO could have issue with double buffered if not clear previously out data
-          xact_generic(rhport, next_ep, true, in_isr);
-        } else {
-          // no more pending in this frame -> clear busy
-          atomic_flag_clear(&_hcd_data.busy);
-        }
-      }
-      return;
-
-    case HRSL_BAD_REQ:
-      // occurred when initialized without any pending transfer. Skip for now
-      return;
-
     default:
       TU_LOG3("HRSL: %02X\r\n", hrsl);
       xfer_result = XFER_RESULT_FAILED;
@@ -940,9 +951,8 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
   if (hirq & HIRQ_FRAME_IRQ) {
     _hcd_data.frame_count++;
 
+    // reset all endpoints nak counter, retry with 1st pending ep.
     max3421_ep_t* ep_retry = NULL;
-
-    // reset all endpoints attempt counter
     for (size_t i = 0; i < CFG_TUH_MAX3421_ENDPOINT_TOTAL; i++) {
       max3421_ep_t* ep = &_hcd_data.ep[i];
       if (ep->packet_size && ep->state > EP_STATE_ATTEMPT_1) {
@@ -1002,7 +1012,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
 
   // clear all interrupt except SNDBAV_IRQ (never clear by us). Note RCVDAV_IRQ, HXFRDN_IRQ already clear while processing
   hirq &= (uint8_t) ~HIRQ_SNDBAV_IRQ;
-  if ( hirq ) {
+  if (hirq) {
     hirq_write(rhport, hirq, in_isr);
   }
 }
diff --git a/src/portable/chipidea/ci_hs/dcd_ci_hs.c b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
index f9ec666e5..93e1d78dd 100644
--- a/src/portable/chipidea/ci_hs/dcd_ci_hs.c
+++ b/src/portable/chipidea/ci_hs/dcd_ci_hs.c
@@ -309,10 +309,12 @@ void dcd_disconnect(uint8_t rhport)
 
 void dcd_sof_enable(uint8_t rhport, bool en)
 {
-  (void) rhport;
-  (void) en;
-
-  // TODO implement later
+  ci_hs_regs_t* dcd_reg = CI_HS_REG(rhport);
+  if (en) {
+      dcd_reg->USBINTR |= INTR_SOF;
+  } else {
+      dcd_reg->USBINTR &= ~INTR_SOF;
+  }
 }
 
 //--------------------------------------------------------------------+
@@ -679,7 +681,8 @@ void dcd_int_handler(uint8_t rhport)
 
   if (int_status & INTR_SOF)
   {
-    dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+    const uint32_t frame = dcd_reg->FRINDEX;
+    dcd_event_sof(rhport, frame, true);
   }
 }
 
diff --git a/src/portable/ehci/ehci.c b/src/portable/ehci/ehci.c
index e145cbb1b..01bbf62bf 100644
--- a/src/portable/ehci/ehci.c
+++ b/src/portable/ehci/ehci.c
@@ -92,7 +92,7 @@ CFG_TUH_MEM_SECTION TU_ATTR_ALIGNED(4096) static ehci_data_t ehci_data;
 //--------------------------------------------------------------------+
 // Debug
 //--------------------------------------------------------------------+
-#if CFG_TUSB_DEBUG >= (EHCI_DBG + 1)
+#if 0 && CFG_TUSB_DEBUG >= (EHCI_DBG + 1)
 static inline void print_portsc(ehci_registers_t* regs) {
   TU_LOG_HEX(EHCI_DBG, regs->portsc);
   TU_LOG(EHCI_DBG, "  Connect Status : %u\r\n", regs->portsc_bm.current_connect_status);
diff --git a/src/portable/espressif/esp32sx/dcd_esp32sx.c b/src/portable/espressif/esp32sx/dcd_esp32sx.c
index bfc0baa56..f912bef89 100644
--- a/src/portable/espressif/esp32sx/dcd_esp32sx.c
+++ b/src/portable/espressif/esp32sx/dcd_esp32sx.c
@@ -31,7 +31,8 @@
 #if (((CFG_TUSB_MCU == OPT_MCU_ESP32S2) ||  (CFG_TUSB_MCU == OPT_MCU_ESP32S3)) && CFG_TUD_ENABLED)
 
 // Espressif
-#include "xtensa_api.h"
+#include "xtensa/xtensa_api.h"
+
 #include "esp_intr_alloc.h"
 #include "esp_log.h"
 #include "soc/dport_reg.h"
diff --git a/src/portable/microchip/samd/dcd_samd.c b/src/portable/microchip/samd/dcd_samd.c
index 976d3dfd0..005b63faf 100644
--- a/src/portable/microchip/samd/dcd_samd.c
+++ b/src/portable/microchip/samd/dcd_samd.c
@@ -183,9 +183,12 @@ void dcd_connect(uint8_t rhport)
 void dcd_sof_enable(uint8_t rhport, bool en)
 {
   (void) rhport;
-  (void) en;
 
-  // TODO implement later
+  if (en) {
+    USB->DEVICE.INTENSET.bit.SOF = 1;
+  } else {
+    USB->DEVICE.INTENCLR.bit.SOF = 1;
+  }
 }
 
 /*------------------------------------------------------------------*/
@@ -374,7 +377,9 @@ void dcd_int_handler (uint8_t rhport)
   if ( int_status & USB_DEVICE_INTFLAG_SOF )
   {
     USB->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_SOF;
-    dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+    const uint32_t frame = USB->DEVICE.FNUM.bit.FNUM;
+    dcd_event_sof(0, frame, true);
+    //dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
   }
 
   // SAMD doesn't distinguish between Suspend and Disconnect state.
diff --git a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
index c3d0c7297..d5c0daaeb 100644
--- a/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
+++ b/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
@@ -283,7 +283,18 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
   /* Response with status first before changing device address */
   dcd_edpt_xfer(rhport, tu_edpt_addr(0, TUSB_DIR_IN), NULL, 0);
 }
+
+#ifdef __GNUC__ // caused by extra declaration of SystemCoreClock in freeRTOSConfig.h
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wredundant-decls"
+#endif
+
 extern u32 SystemCoreClock;
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
 void dcd_remote_wakeup(uint8_t rhport)
 {
   (void) rhport;
diff --git a/src/portable/nordic/nrf5x/dcd_nrf5x.c b/src/portable/nordic/nrf5x/dcd_nrf5x.c
index 80a285ef0..1cc5c1836 100644
--- a/src/portable/nordic/nrf5x/dcd_nrf5x.c
+++ b/src/portable/nordic/nrf5x/dcd_nrf5x.c
@@ -40,7 +40,6 @@
 
 #include "nrf.h"
 #include "nrf_clock.h"
-#include "nrf_power.h"
 #include "nrfx_usbd_errata.h"
 
 #ifdef __GNUC__
@@ -57,28 +56,46 @@
 #include "mcu/mcu.h"
 #endif
 
+/* Try to detect nrfx version if not configured with CFG_TUD_NRF_NRFX_VERSION
+ * nrfx v1 and v2 are concurrently developed. There is no NRFX_VERSION only MDK VERSION which is as follows:
+ * - v3.0.0: 8.53.1 (conflict with v2.11.0), v3.1.0: 8.55.0 ...
+ * - v2.11.0: 8.53.1, v2.6.0: 8.44.1, v2.5.0: 8.40.2, v2.4.0: 8.37.0, v2.3.0: 8.35.0, v2.2.0: 8.32.1, v2.1.0: 8.30.2, v2.0.0: 8.29.0
+ * - v1.9.0: 8.40.3, v1.8.6: 8.35.0 (conflict with v2.3.0), v1.8.5: 8.32.3, v1.8.4: 8.32.1 (conflict with v2.2.0),
+ *   v1.8.2: 8.32.1 (conflict with v2.2.0), v1.8.1: 8.27.1
+ * Therefore the check for v1 would be:
+ * - MDK < 8.29.0 (v2.0), MDK == 8.32.3, 8.40.3
+ * - in case of conflict User of those version must upgrade to other 1.x version or set CFG_TUD_NRF_NRFX_VERSION
+*/
+#ifndef CFG_TUD_NRF_NRFX_VERSION
+  #define _MDK_VERSION  (10000*MDK_MAJOR_VERSION + 100*MDK_MINOR_VERSION + MDK_MICRO_VERSION)
+
+  #if _MDK_VERSION < 82900 || _MDK_VERSION == 83203 || _MDK_VERSION == 84003
+    // nrfx <= 1.8.1, or 1.8.5 or 1.9.0
+    #define CFG_TUD_NRF_NRFX_VERSION 1
+  #else
+    #define CFG_TUD_NRF_NRFX_VERSION 2
+  #endif
+#endif
+
 /*------------------------------------------------------------------*/
 /* MACRO TYPEDEF CONSTANT ENUM
  *------------------------------------------------------------------*/
-enum
-{
+enum {
   // Max allowed by USB specs
-  MAX_PACKET_SIZE   = 64,
+  MAX_PACKET_SIZE = 64,
 
   // Mask of all END event (IN & OUT) for all endpoints. ENDEPIN0-7, ENDEPOUT0-7, ENDISOIN, ENDISOOUT
   EDPT_END_ALL_MASK = (0xff << USBD_INTEN_ENDEPIN0_Pos) | (0xff << USBD_INTEN_ENDEPOUT0_Pos) |
                       USBD_INTENCLR_ENDISOIN_Msk | USBD_INTEN_ENDISOOUT_Msk
 };
 
-enum
-{
-  EP_ISO_NUM   = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN
+enum {
+  EP_ISO_NUM = 8, // Endpoint number is fixed (8) for ISOOUT and ISOIN
   EP_CBI_COUNT = 8  // Control Bulk Interrupt endpoints count
 };
 
 // Transfer Descriptor
-typedef struct
-{
+typedef struct {
   uint8_t* buffer;
   uint16_t total_len;
   volatile uint16_t actual_len;
@@ -96,113 +113,85 @@ typedef struct
 } xfer_td_t;
 
 // Data for managing dcd
-static struct
-{
+static struct {
   // All 8 endpoints including control IN & OUT (offset 1)
   // +1 for ISO endpoints
   xfer_td_t xfer[EP_CBI_COUNT + 1][2];
 
   // nRF can only carry one DMA at a time, this is used to guard the access to EasyDMA
-  atomic_bool dma_running;
-}_dcd;
+  atomic_flag dma_running;
+
+  // Track whether sof has been manually enabled
+  bool sof_enabled;
+} _dcd;
 
 /*------------------------------------------------------------------*/
 /* Control / Bulk / Interrupt (CBI) Transfer
  *------------------------------------------------------------------*/
 
-// NVIC_GetEnableIRQ is only available in CMSIS v5
-#ifndef NVIC_GetEnableIRQ
-static inline uint32_t NVIC_GetEnableIRQ(IRQn_Type IRQn)
-{
-  if ((int32_t)(IRQn) >= 0)
-  {
-    return((uint32_t)(((NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));
-  }
-  else
-  {
-    return(0U);
-  }
-}
-#endif
-
 // check if we are in ISR
-TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_in_isr(void) {
   return (SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) ? true : false;
 }
 
 // helper to start DMA
-static void start_dma(volatile uint32_t* reg_startep)
-{
+static void start_dma(volatile uint32_t* reg_startep) {
   (*reg_startep) = 1;
-  __ISB(); __DSB();
+  __ISB();
+  __DSB();
 
   // TASKS_EP0STATUS, TASKS_EP0RCVOUT seem to need EasyDMA to be available
   // However these don't trigger any DMA transfer and got ENDED event subsequently
   // Therefore dma_pending is corrected right away
-  if ( (reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT) )
-  {
+  if ((reg_startep == &NRF_USBD->TASKS_EP0STATUS) || (reg_startep == &NRF_USBD->TASKS_EP0RCVOUT)) {
     atomic_flag_clear(&_dcd.dma_running);
   }
 }
 
-static void edpt_dma_start(volatile uint32_t* reg_startep)
-{
-  if ( atomic_flag_test_and_set(&_dcd.dma_running) )
-  {
-    usbd_defer_func((osal_task_func_t) edpt_dma_start, (void*) (uintptr_t) reg_startep, true);
-  }else
-  {
+static void edpt_dma_start(volatile uint32_t* reg_startep) {
+  if (atomic_flag_test_and_set(&_dcd.dma_running)) {
+    usbd_defer_func((osal_task_func_t)(uintptr_t ) edpt_dma_start, (void*) (uintptr_t) reg_startep, is_in_isr());
+  } else {
     start_dma(reg_startep);
   }
 }
 
 // DMA is complete
-static void edpt_dma_end(void)
-{
-  TU_ASSERT(_dcd.dma_running, );
+static void edpt_dma_end(void) {
   atomic_flag_clear(&_dcd.dma_running);
 }
 
 // helper getting td
-static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir)
-{
+static inline xfer_td_t* get_td(uint8_t epnum, uint8_t dir) {
   return &_dcd.xfer[epnum][dir];
 }
 
 static void xact_out_dma(uint8_t epnum);
+
 // Function wraps xact_out_dma which wants uint8_t while usbd_defer_func wants void (*)(void *)
-static void xact_out_dma_wrapper(void *epnum)
-{
-  xact_out_dma((uint8_t)((uintptr_t)epnum));
+static void xact_out_dma_wrapper(void* epnum) {
+  xact_out_dma((uint8_t) ((uintptr_t) epnum));
 }
 
 // Start DMA to move data from Endpoint -> RAM
-static void xact_out_dma(uint8_t epnum)
-{
+static void xact_out_dma(uint8_t epnum) {
   xfer_td_t* xfer = get_td(epnum, TUSB_DIR_OUT);
   uint32_t xact_len;
 
   // DMA can't be active during read of SIZE.EPOUT or SIZE.ISOOUT, so try to lock,
   // If already running defer call regardless if it was called from ISR or task,
-  if ( atomic_flag_test_and_set(&_dcd.dma_running) )
-  {
-    usbd_defer_func((osal_task_func_t)xact_out_dma_wrapper, (void *)(uint32_t)epnum, is_in_isr());
+  if (atomic_flag_test_and_set(&_dcd.dma_running)) {
+    usbd_defer_func((osal_task_func_t) xact_out_dma_wrapper, (void*) (uint32_t) epnum, is_in_isr());
     return;
   }
-  if (epnum == EP_ISO_NUM)
-  {
+  if (epnum == EP_ISO_NUM) {
     xact_len = NRF_USBD->SIZE.ISOOUT;
     // If ZERO bit is set, ignore ISOOUT length
-    if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk)
-    {
+    if (xact_len & USBD_SIZE_ISOOUT_ZERO_Msk) {
       xact_len = 0;
       atomic_flag_clear(&_dcd.dma_running);
-    }
-    else
-    {
-      if (xfer->started)
-      {
+    } else {
+      if (xfer->started) {
         // Trigger DMA move data from Endpoint -> SRAM
         NRF_USBD->ISOOUT.PTR = (uint32_t) xfer->buffer;
         NRF_USBD->ISOOUT.MAXCNT = xact_len;
@@ -212,9 +201,7 @@ static void xact_out_dma(uint8_t epnum)
         atomic_flag_clear(&_dcd.dma_running);
       }
     }
-  }
-  else
-  {
+  } else {
     // limit xact len to remaining length
     xact_len = tu_min16((uint16_t) NRF_USBD->SIZE.EPOUT[epnum], xfer->total_len - xfer->actual_len);
 
@@ -228,14 +215,13 @@ static void xact_out_dma(uint8_t epnum)
 
 // Prepare for a CBI transaction IN, call at the start
 // it start DMA to transfer data from RAM -> Endpoint
-static void xact_in_dma(uint8_t epnum)
-{
+static void xact_in_dma(uint8_t epnum) {
   xfer_td_t* xfer = get_td(epnum, TUSB_DIR_IN);
 
   // Each transaction is up to Max Packet Size
   uint16_t const xact_len = tu_min16(xfer->total_len - xfer->actual_len, xfer->mps);
 
-  NRF_USBD->EPIN[epnum].PTR    = (uint32_t) xfer->buffer;
+  NRF_USBD->EPIN[epnum].PTR = (uint32_t) xfer->buffer;
   NRF_USBD->EPIN[epnum].MAXCNT = xact_len;
 
   edpt_dma_start(&NRF_USBD->TASKS_STARTEPIN[epnum]);
@@ -244,26 +230,22 @@ static void xact_in_dma(uint8_t epnum)
 //--------------------------------------------------------------------+
 // Controller API
 //--------------------------------------------------------------------+
-void dcd_init (uint8_t rhport)
-{
+void dcd_init(uint8_t rhport) {
   TU_LOG2("dcd init\r\n");
   (void) rhport;
 }
 
-void dcd_int_enable(uint8_t rhport)
-{
+void dcd_int_enable(uint8_t rhport) {
   (void) rhport;
   NVIC_EnableIRQ(USBD_IRQn);
 }
 
-void dcd_int_disable(uint8_t rhport)
-{
+void dcd_int_disable(uint8_t rhport) {
   (void) rhport;
   NVIC_DisableIRQ(USBD_IRQn);
 }
 
-void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
-{
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
   (void) rhport;
   (void) dev_addr;
   // Set Address is automatically update by hw controller, nothing to do
@@ -278,8 +260,7 @@ void dcd_set_address (uint8_t rhport, uint8_t dev_addr)
   NRF_USBD->INTENSET = USBD_INTEN_USBEVENT_Msk;
 }
 
-void dcd_remote_wakeup(uint8_t rhport)
-{
+void dcd_remote_wakeup(uint8_t rhport) {
   (void) rhport;
 
   // Bring controller out of low power mode
@@ -288,8 +269,7 @@ void dcd_remote_wakeup(uint8_t rhport)
 }
 
 // disconnect by disabling internal pull-up resistor on D+/D-
-void dcd_disconnect(uint8_t rhport)
-{
+void dcd_disconnect(uint8_t rhport) {
   (void) rhport;
   NRF_USBD->USBPULLUP = 0;
 
@@ -299,56 +279,51 @@ void dcd_disconnect(uint8_t rhport)
 }
 
 // connect by enabling internal pull-up resistor on D+/D-
-void dcd_connect(uint8_t rhport)
-{
+void dcd_connect(uint8_t rhport) {
   (void) rhport;
   NRF_USBD->USBPULLUP = 1;
 }
 
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
   (void) rhport;
-  (void) en;
-
-  // TODO implement later
+  if (en) {
+    _dcd.sof_enabled = true;
+    NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk;
+  } else {
+    _dcd.sof_enabled = false;
+    NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
+  }
 }
 
 //--------------------------------------------------------------------+
 // Endpoint API
 //--------------------------------------------------------------------+
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
-{
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
   (void) rhport;
 
   uint8_t const ep_addr = desc_edpt->bEndpointAddress;
-  uint8_t const epnum   = tu_edpt_number(ep_addr);
-  uint8_t const dir     = tu_edpt_dir(ep_addr);
+  uint8_t const epnum = tu_edpt_number(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
 
   _dcd.xfer[epnum][dir].mps = tu_edpt_packet_size(desc_edpt);
 
-  if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS)
-  {
-    if (dir == TUSB_DIR_OUT)
-    {
+  if (desc_edpt->bmAttributes.xfer != TUSB_XFER_ISOCHRONOUS) {
+    if (dir == TUSB_DIR_OUT) {
       NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
       NRF_USBD->EPOUTEN |= TU_BIT(epnum);
 
       // Write any value to SIZE register will allow nRF to ACK/accept data
       NRF_USBD->SIZE.EPOUT[epnum] = 0;
-    }else
-    {
+    } else {
       NRF_USBD->INTENSET = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
-      NRF_USBD->EPINEN  |= TU_BIT(epnum);
+      NRF_USBD->EPINEN |= TU_BIT(epnum);
     }
     // clear stall and reset DataToggle
     NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_UnStall << USBD_EPSTALL_STALL_Pos) | ep_addr;
     NRF_USBD->DTOGGLE = (USBD_DTOGGLE_VALUE_Data0 << USBD_DTOGGLE_VALUE_Pos) | ep_addr;
-  }
-  else
-  {
+  } else {
     TU_ASSERT(epnum == EP_ISO_NUM);
-    if (dir == TUSB_DIR_OUT)
-    {
+    if (dir == TUSB_DIR_OUT) {
       // SPLIT ISO buffer when ISO IN endpoint is already opened.
       if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps) NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_HalfIN;
 
@@ -361,9 +336,7 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
       // Enable SOF and ISOOUT interrupts, and ISOOUT endpoint.
       NRF_USBD->INTENSET = USBD_INTENSET_ENDISOOUT_Msk | USBD_INTENSET_SOF_Msk;
       NRF_USBD->EPOUTEN |= USBD_EPOUTEN_ISOOUT_Msk;
-    }
-    else
-    {
+    } else {
       NRF_USBD->EVENTS_ENDISOIN = 0;
 
       // SPLIT ISO buffer when ISO OUT endpoint is already opened.
@@ -374,39 +347,38 @@ bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * desc_edpt)
 
       // Enable SOF and ISOIN interrupts, and ISOIN endpoint.
       NRF_USBD->INTENSET = USBD_INTENSET_ENDISOIN_Msk | USBD_INTENSET_SOF_Msk;
-      NRF_USBD->EPINEN  |= USBD_EPINEN_ISOIN_Msk;
+      NRF_USBD->EPINEN |= USBD_EPINEN_ISOIN_Msk;
     }
   }
 
-  __ISB(); __DSB();
+  __ISB();
+  __DSB();
 
   return true;
 }
 
-void dcd_edpt_close_all (uint8_t rhport)
-{
+void dcd_edpt_close_all(uint8_t rhport) {
   // disable interrupt to prevent race condition
   dcd_int_disable(rhport);
 
   // disable all non-control (bulk + interrupt) endpoints
-  for ( uint8_t ep = 1; ep < EP_CBI_COUNT; ep++ )
-  {
+  for (uint8_t ep = 1; ep < EP_CBI_COUNT; ep++) {
     NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + ep) | TU_BIT(USBD_INTEN_ENDEPIN0_Pos + ep);
 
     NRF_USBD->TASKS_STARTEPIN[ep] = 0;
     NRF_USBD->TASKS_STARTEPOUT[ep] = 0;
 
-    tu_memclr(_dcd.xfer[ep], 2*sizeof(xfer_td_t));
+    tu_memclr(_dcd.xfer[ep], 2 * sizeof(xfer_td_t));
   }
 
   // disable both ISO
   NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk | USBD_INTENCLR_ENDISOOUT_Msk | USBD_INTENCLR_ENDISOIN_Msk;
   NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
 
-  NRF_USBD->TASKS_STARTISOIN  = 0;
+  NRF_USBD->TASKS_STARTISOIN = 0;
   NRF_USBD->TASKS_STARTISOOUT = 0;
 
-  tu_memclr(_dcd.xfer[EP_ISO_NUM], 2*sizeof(xfer_td_t));
+  tu_memclr(_dcd.xfer[EP_ISO_NUM], 2 * sizeof(xfer_td_t));
 
   // de-activate all non-control
   NRF_USBD->EPOUTEN = 1UL;
@@ -415,107 +387,89 @@ void dcd_edpt_close_all (uint8_t rhport)
   dcd_int_enable(rhport);
 }
 
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
   (void) rhport;
 
   uint8_t const epnum = tu_edpt_number(ep_addr);
-  uint8_t const dir   = tu_edpt_dir(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
 
-  if (epnum != EP_ISO_NUM)
-  {
+  if (epnum != EP_ISO_NUM) {
     // CBI
-    if (dir == TUSB_DIR_OUT)
-    {
+    if (dir == TUSB_DIR_OUT) {
       NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPOUT0_Pos + epnum);
       NRF_USBD->EPOUTEN &= ~TU_BIT(epnum);
-    }
-    else
-    {
+    } else {
       NRF_USBD->INTENCLR = TU_BIT(USBD_INTEN_ENDEPIN0_Pos + epnum);
       NRF_USBD->EPINEN &= ~TU_BIT(epnum);
     }
-  }
-  else
-  {
+  } else {
     _dcd.xfer[EP_ISO_NUM][dir].mps = 0;
     // ISO
-    if (dir == TUSB_DIR_OUT)
-    {
+    if (dir == TUSB_DIR_OUT) {
       NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOOUT_Msk;
       NRF_USBD->EPOUTEN &= ~USBD_EPOUTEN_ISOOUT_Msk;
       NRF_USBD->EVENTS_ENDISOOUT = 0;
-    }
-    else
-    {
+    } else {
       NRF_USBD->INTENCLR = USBD_INTENCLR_ENDISOIN_Msk;
       NRF_USBD->EPINEN &= ~USBD_EPINEN_ISOIN_Msk;
     }
     // One of the ISO endpoints closed, no need to split buffers any more.
     NRF_USBD->ISOSPLIT = USBD_ISOSPLIT_SPLIT_OneDir;
     // When both ISO endpoint are close there is no need for SOF any more.
-    if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0) NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
+    if (_dcd.xfer[EP_ISO_NUM][TUSB_DIR_IN].mps + _dcd.xfer[EP_ISO_NUM][TUSB_DIR_OUT].mps == 0)
+      NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
   }
   _dcd.xfer[epnum][dir].started = false;
-  __ISB(); __DSB();
+  __ISB();
+  __DSB();
 }
 
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
   (void) rhport;
 
   uint8_t const epnum = tu_edpt_number(ep_addr);
-  uint8_t const dir   = tu_edpt_dir(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
 
   xfer_td_t* xfer = get_td(epnum, dir);
 
   TU_ASSERT(!xfer->started);
-  xfer->buffer     = buffer;
-  xfer->total_len  = total_bytes;
+  xfer->buffer = buffer;
+  xfer->total_len = total_bytes;
   xfer->actual_len = 0;
 
   // Control endpoint with zero-length packet and opposite direction to 1st request byte --> status stage
   bool const control_status = (epnum == 0 && total_bytes == 0 && dir != tu_edpt_dir(NRF_USBD->BMREQUESTTYPE));
 
-  if ( control_status )
-  {
+  if (control_status) {
     // Status Phase also requires EasyDMA has to be available as well !!!!
     edpt_dma_start(&NRF_USBD->TASKS_EP0STATUS);
 
     // The nRF doesn't interrupt on status transmit so we queue up a success response.
     dcd_event_xfer_complete(0, ep_addr, 0, XFER_RESULT_SUCCESS, is_in_isr());
-  }
-  else if ( dir == TUSB_DIR_OUT )
-  {
+  } else if (dir == TUSB_DIR_OUT) {
     xfer->started = true;
-    if ( epnum == 0 )
-    {
+    if (epnum == 0) {
       // Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
       edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
-    }else
-    {
+    } else {
       // started just set, it could start DMA transfer if interrupt was trigger after this line
       // code only needs to start transfer (from Endpoint to RAM) when data_received was set
       // before started was set. If started is NOT set but data_received is, it means that
       // current transfer was already finished and next data is already present in endpoint and
       // can be consumed by future transfer
-      __ISB(); __DSB();
-      if ( xfer->data_received && xfer->started )
-      {
+      __ISB();
+      __DSB();
+      if (xfer->data_received && xfer->started) {
         // Data is already received previously
         // start DMA to copy to SRAM
         xfer->data_received = false;
         xact_out_dma(epnum);
-      }
-      else
-      {
+      } else {
         // nRF auto accept next Bulk/Interrupt OUT packet
         // nothing to do
       }
     }
-  }
-  else
-  {
+  } else {
     // Start DMA to copy data from RAM -> Endpoint
     xact_in_dma(epnum);
   }
@@ -523,42 +477,37 @@ bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t
   return true;
 }
 
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
   (void) rhport;
 
   uint8_t const epnum = tu_edpt_number(ep_addr);
-  uint8_t const dir   = tu_edpt_dir(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
 
   xfer_td_t* xfer = get_td(epnum, dir);
 
-  if ( epnum == 0 )
-  {
+  if (epnum == 0) {
     NRF_USBD->TASKS_EP0STALL = 1;
-  }else if (epnum != EP_ISO_NUM)
-  {
+  } else if (epnum != EP_ISO_NUM) {
     NRF_USBD->EPSTALL = (USBD_EPSTALL_STALL_Stall << USBD_EPSTALL_STALL_Pos) | ep_addr;
 
     // Note: nRF can auto ACK packet OUT before get stalled.
     // There maybe data in endpoint fifo already, we need to pull it out
-    if ( (dir == TUSB_DIR_OUT) && xfer->data_received )
-    {
+    if ((dir == TUSB_DIR_OUT) && xfer->data_received) {
       xfer->data_received = false;
       xact_out_dma(epnum);
     }
   }
 
-  __ISB(); __DSB();
+  __ISB();
+  __DSB();
 }
 
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
   (void) rhport;
   uint8_t const epnum = tu_edpt_number(ep_addr);
-  uint8_t const dir   = tu_edpt_dir(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
 
-  if ( epnum != 0 && epnum != EP_ISO_NUM )
-  {
+  if (epnum != 0 && epnum != EP_ISO_NUM) {
     // reset data toggle to DATA0
     // First write this register with VALUE=Nop to select the endpoint, then either read it to get the status from
     // VALUE, or write it again with VALUE=Data0 or Data1
@@ -571,26 +520,25 @@ void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
     // Write any value to SIZE register will allow nRF to ACK/accept data
     if (dir == TUSB_DIR_OUT) NRF_USBD->SIZE.EPOUT[epnum] = 0;
 
-    __ISB(); __DSB();
+    __ISB();
+    __DSB();
   }
 }
 
 /*------------------------------------------------------------------*/
 /* Interrupt Handler
  *------------------------------------------------------------------*/
-void bus_reset(void)
-{
+void bus_reset(void) {
   // 6.35.6 USB controller automatically disabled all endpoints (except control)
   NRF_USBD->EPOUTEN = 1UL;
   NRF_USBD->EPINEN = 1UL;
 
-  for(int i=0; i<8; i++)
-  {
+  for (int i = 0; i < 8; i++) {
     NRF_USBD->TASKS_STARTEPIN[i] = 0;
     NRF_USBD->TASKS_STARTEPOUT[i] = 0;
   }
 
-  NRF_USBD->TASKS_STARTISOIN  = 0;
+  NRF_USBD->TASKS_STARTISOIN = 0;
   NRF_USBD->TASKS_STARTISOOUT = 0;
 
   // Clear USB Event Interrupt
@@ -600,43 +548,40 @@ void bus_reset(void)
   // Reset interrupt
   NRF_USBD->INTENCLR = NRF_USBD->INTEN;
   NRF_USBD->INTENSET = USBD_INTEN_USBRESET_Msk | USBD_INTEN_USBEVENT_Msk | USBD_INTEN_EPDATA_Msk |
-          USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk | USBD_INTEN_ENDEPOUT0_Msk;
+                       USBD_INTEN_EP0SETUP_Msk | USBD_INTEN_EP0DATADONE_Msk | USBD_INTEN_ENDEPIN0_Msk |
+                       USBD_INTEN_ENDEPOUT0_Msk;
 
   tu_varclr(&_dcd);
   _dcd.xfer[0][TUSB_DIR_IN].mps = MAX_PACKET_SIZE;
   _dcd.xfer[0][TUSB_DIR_OUT].mps = MAX_PACKET_SIZE;
 }
 
-void dcd_int_handler(uint8_t rhport)
-{
+void dcd_int_handler(uint8_t rhport) {
   (void) rhport;
 
-  uint32_t const inten  = NRF_USBD->INTEN;
+  uint32_t const inten = NRF_USBD->INTEN;
   uint32_t int_status = 0;
 
   volatile uint32_t* regevt = &NRF_USBD->EVENTS_USBRESET;
 
-  for(uint8_t i=0; iactual_len = NRF_USBD->ISOIN.AMOUNT;
@@ -645,13 +590,11 @@ void dcd_int_handler(uint8_t rhport)
     xfer->iso_in_transfer_ready = true;
   }
 
-  if ( int_status & USBD_INTEN_SOF_Msk )
-  {
+  if (int_status & USBD_INTEN_SOF_Msk) {
     bool iso_enabled = false;
 
     // ISOOUT: Transfer data gathered in previous frame from buffer to RAM
-    if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk)
-    {
+    if (NRF_USBD->EPOUTEN & USBD_EPOUTEN_ISOOUT_Msk) {
       iso_enabled = true;
       // Transfer from endpoint to RAM only if data is not corrupted
       if ((int_status & USBD_INTEN_USBEVENT_Msk) == 0 ||
@@ -661,38 +604,39 @@ void dcd_int_handler(uint8_t rhport)
     }
 
     // ISOIN: Notify client that data was transferred
-    if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk)
-    {
+    if (NRF_USBD->EPINEN & USBD_EPINEN_ISOIN_Msk) {
       iso_enabled = true;
 
       xfer_td_t* xfer = get_td(EP_ISO_NUM, TUSB_DIR_IN);
-      if ( xfer->iso_in_transfer_ready )
-      {
+      if (xfer->iso_in_transfer_ready) {
         xfer->iso_in_transfer_ready = false;
         dcd_event_xfer_complete(0, EP_ISO_NUM | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
       }
     }
 
-    if ( !iso_enabled )
-    {
-      // ISO endpoint is not used, SOF is only enabled one-time for remote wakeup
-      // so we disable it now
-      NRF_USBD->INTENCLR = USBD_INTENSET_SOF_Msk;
+    if (!iso_enabled && !_dcd.sof_enabled) {
+      // SOF interrupt not manually enabled and ISO endpoint is not used,
+      // SOF is only enabled one-time for remote wakeup so we disable it now
+
+      NRF_USBD->INTENCLR = USBD_INTENCLR_SOF_Msk;
     }
 
-    dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
+    const uint32_t frame = NRF_USBD->FRAMECNTR;
+    dcd_event_sof(0, frame, true);
+    //dcd_event_bus_signal(0, DCD_EVENT_SOF, true);
   }
 
-  if ( int_status & USBD_INTEN_USBEVENT_Msk )
-  {
-    TU_LOG(3, "EVENTCAUSE = 0x%04lX\r\n", NRF_USBD->EVENTCAUSE);
+  if (int_status & USBD_INTEN_USBEVENT_Msk) {
+    TU_LOG(3, "EVENTCAUSE = 0x%04" PRIX32 "\r\n", NRF_USBD->EVENTCAUSE);
 
-    enum { EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk | USBD_EVENTCAUSE_ISOOUTCRC_Msk };
+    enum {
+      EVT_CAUSE_MASK = USBD_EVENTCAUSE_SUSPEND_Msk | USBD_EVENTCAUSE_RESUME_Msk | USBD_EVENTCAUSE_USBWUALLOWED_Msk |
+                       USBD_EVENTCAUSE_ISOOUTCRC_Msk
+    };
     uint32_t const evt_cause = NRF_USBD->EVENTCAUSE & EVT_CAUSE_MASK;
     NRF_USBD->EVENTCAUSE = evt_cause; // clear interrupt
 
-    if ( evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk )
-    {
+    if (evt_cause & USBD_EVENTCAUSE_SUSPEND_Msk) {
       // Put controller into low power mode
       // Leave HFXO disable to application, since it may be used by other peripherals
       NRF_USBD->LOWPOWER = 1;
@@ -700,8 +644,7 @@ void dcd_int_handler(uint8_t rhport)
       dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
     }
 
-    if ( evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk )
-    {
+    if (evt_cause & USBD_EVENTCAUSE_USBWUALLOWED_Msk) {
       // USB is out of low power mode, and wakeup is allowed
       // Initiate RESUME signal
       NRF_USBD->DPDMVALUE = USBD_DPDMVALUE_STATE_Resume;
@@ -713,34 +656,29 @@ void dcd_int_handler(uint8_t rhport)
       NRF_USBD->INTENSET = USBD_INTENSET_SOF_Msk;
     }
 
-    if ( evt_cause & USBD_EVENTCAUSE_RESUME_Msk )
-    {
+    if (evt_cause & USBD_EVENTCAUSE_RESUME_Msk) {
       dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
     }
   }
 
   // Setup tokens are specific to the Control endpoint.
-  if ( int_status & USBD_INTEN_EP0SETUP_Msk )
-  {
-    uint8_t const setup[8] =
-    {
-      NRF_USBD->BMREQUESTTYPE , NRF_USBD->BREQUEST, NRF_USBD->WVALUEL , NRF_USBD->WVALUEH,
-      NRF_USBD->WINDEXL       , NRF_USBD->WINDEXH , NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH
+  if (int_status & USBD_INTEN_EP0SETUP_Msk) {
+    uint8_t const setup[8] = {
+        NRF_USBD->BMREQUESTTYPE, NRF_USBD->BREQUEST, NRF_USBD->WVALUEL, NRF_USBD->WVALUEH,
+        NRF_USBD->WINDEXL, NRF_USBD->WINDEXH, NRF_USBD->WLENGTHL, NRF_USBD->WLENGTHH
     };
 
     // nrf5x hw auto handle set address, there is no need to inform usb stack
-    tusb_control_request_t const * request = (tusb_control_request_t const *) setup;
+    tusb_control_request_t const* request = (tusb_control_request_t const*) setup;
 
-    if ( !(TUSB_REQ_RCPT_DEVICE   == request->bmRequestType_bit.recipient &&
-           TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
-           TUSB_REQ_SET_ADDRESS   == request->bRequest) )
-    {
+    if (!(TUSB_REQ_RCPT_DEVICE == request->bmRequestType_bit.recipient &&
+          TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
+          TUSB_REQ_SET_ADDRESS == request->bRequest)) {
       dcd_event_setup_received(0, setup, true);
     }
   }
 
-  if ( int_status & EDPT_END_ALL_MASK )
-  {
+  if (int_status & EDPT_END_ALL_MASK) {
     // DMA complete move data from SRAM <-> Endpoint
     // Must before endpoint transfer handling
     edpt_dma_end();
@@ -782,30 +720,24 @@ void dcd_int_handler(uint8_t rhport)
    * len if Host decides to sent fewer bytes, it this case transaction is also
    * complete and next transfer is not initiated here like for CBI.
    */
-  for(uint8_t epnum=0; epnumEPOUT[epnum].AMOUNT;
 
-      xfer->buffer     += xact_len;
+      xfer->buffer += xact_len;
       xfer->actual_len += xact_len;
 
       // Transfer complete if transaction len < Max Packet Size or total len is transferred
-      if ( (epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len) )
-      {
-        if ( epnum == 0 )
-        {
+      if ((epnum != EP_ISO_NUM) && (xact_len == xfer->mps) && (xfer->actual_len < xfer->total_len)) {
+        if (epnum == 0) {
           // Accept next Control Out packet. TASKS_EP0RCVOUT also require EasyDMA
           edpt_dma_start(&NRF_USBD->TASKS_EP0RCVOUT);
-        }else
-        {
+        } else {
           // nRF auto accept next Bulk/Interrupt OUT packet
           // nothing to do
         }
-      }else
-      {
+      } else {
         TU_ASSERT(xfer->started,);
         xfer->total_len = xfer->actual_len;
         xfer->started = false;
@@ -819,11 +751,11 @@ void dcd_int_handler(uint8_t rhport)
   }
 
   // Endpoint <-> Host ( In & OUT )
-  if ( int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk) )
-  {
+  if (int_status & (USBD_INTEN_EPDATA_Msk | USBD_INTEN_EP0DATADONE_Msk)) {
     uint32_t data_status = NRF_USBD->EPDATASTATUS;
     NRF_USBD->EPDATASTATUS = data_status;
-    __ISB(); __DSB();
+    __ISB();
+    __DSB();
 
     // EP0DATADONE is set with either Control Out on IN Data
     // Since EPDATASTATUS cannot be used to determine whether it is control OUT or IN.
@@ -832,22 +764,18 @@ void dcd_int_handler(uint8_t rhport)
     bool const is_control_out = (int_status & USBD_INTEN_EP0DATADONE_Msk) && !(NRF_USBD->BMREQUESTTYPE & TUSB_DIR_IN_MASK);
 
     // CBI In: Endpoint -> Host (transaction complete)
-    for(uint8_t epnum=0; epnumEPIN[epnum].AMOUNT;
 
-        xfer->buffer     += xact_len;
+        xfer->buffer += xact_len;
         xfer->actual_len += xact_len;
 
-        if ( xfer->actual_len < xfer->total_len )
-        {
+        if (xfer->actual_len < xfer->total_len) {
           // Start DMA to copy next data packet
           xact_in_dma(epnum);
-        } else
-        {
+        } else {
           // CBI IN complete
           dcd_event_xfer_complete(0, epnum | TUSB_DIR_IN_MASK, xfer->actual_len, XFER_RESULT_SUCCESS, true);
         }
@@ -855,17 +783,13 @@ void dcd_int_handler(uint8_t rhport)
     }
 
     // CBI OUT: Host -> Endpoint
-    for(uint8_t epnum=0; epnumstarted && xfer->actual_len < xfer->total_len )
-        {
+        if (xfer->started && xfer->actual_len < xfer->total_len) {
           xact_out_dma(epnum);
-        }else
-        {
+        } else {
           // Data overflow !!! Nah, nRF will auto accept next Bulk/Interrupt OUT packet
           // Mark this endpoint with data received
           xfer->data_received = true;
@@ -889,76 +813,80 @@ void dcd_int_handler(uint8_t rhport)
   #define SD_MAGIC_NUMBER   0x51B1E5DB
 #endif
 
-static inline bool is_sd_existed(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_sd_existed(void) {
   return *((uint32_t*)(SOFTDEVICE_INFO_STRUCT_ADDRESS+4)) == SD_MAGIC_NUMBER;
 }
 
 // check if SD is existed and enabled
-static inline bool is_sd_enabled(void)
-{
+TU_ATTR_ALWAYS_INLINE static inline bool is_sd_enabled(void) {
   if ( !is_sd_existed() ) return false;
-
   uint8_t sd_en = false;
   (void) sd_softdevice_is_enabled(&sd_en);
   return sd_en;
 }
 #endif
 
-static bool hfclk_running(void)
-{
+static bool hfclk_running(void) {
 #ifdef SOFTDEVICE_PRESENT
-  if ( is_sd_enabled() )
-  {
+  if ( is_sd_enabled() ) {
     uint32_t is_running = 0;
     (void) sd_clock_hfclk_is_running(&is_running);
     return (is_running ? true : false);
   }
 #endif
 
+#if CFG_TUD_NRF_NRFX_VERSION == 1
+  return nrf_clock_hf_is_running(NRF_CLOCK_HFCLK_HIGH_ACCURACY);
+#else
   return nrf_clock_hf_is_running(NRF_CLOCK, NRF_CLOCK_HFCLK_HIGH_ACCURACY);
+#endif
 }
 
-static void hfclk_enable(void)
-{
+static void hfclk_enable(void) {
 #if CFG_TUSB_OS == OPT_OS_MYNEWT
   usb_clock_request();
   return;
 #else
 
   // already running, nothing to do
-  if ( hfclk_running() ) return;
+  if (hfclk_running()) return;
 
 #ifdef SOFTDEVICE_PRESENT
-  if ( is_sd_enabled() )
-  {
+  if ( is_sd_enabled() ) {
     (void)sd_clock_hfclk_request();
     return;
   }
 #endif
 
+#if CFG_TUD_NRF_NRFX_VERSION == 1
+  nrf_clock_event_clear(NRF_CLOCK_EVENT_HFCLKSTARTED);
+  nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTART);
+#else
   nrf_clock_event_clear(NRF_CLOCK, NRF_CLOCK_EVENT_HFCLKSTARTED);
   nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTART);
 #endif
+#endif
 }
 
-static void hfclk_disable(void)
-{
+static void hfclk_disable(void) {
 #if CFG_TUSB_OS == OPT_OS_MYNEWT
   usb_clock_release();
   return;
 #else
 
 #ifdef SOFTDEVICE_PRESENT
-  if ( is_sd_enabled() )
-  {
+  if ( is_sd_enabled() ) {
     (void)sd_clock_hfclk_release();
     return;
   }
 #endif
 
+#if CFG_TUD_NRF_NRFX_VERSION == 1
+  nrf_clock_task_trigger(NRF_CLOCK_TASK_HFCLKSTOP);
+#else
   nrf_clock_task_trigger(NRF_CLOCK, NRF_CLOCK_TASK_HFCLKSTOP);
 #endif
+#endif
 }
 
 // Power & Clock Peripheral on nRF5x to manage USB
@@ -971,8 +899,7 @@ static void hfclk_disable(void)
 // Therefore this function must be called to handle USB power event by
 // - nrfx_power_usbevt_init() : if Softdevice is not used or enabled
 // - SoftDevice SOC event : if SD is used and enabled
-void tusb_hal_nrf_power_event (uint32_t event)
-{
+void tusb_hal_nrf_power_event(uint32_t event) {
   // Value is chosen to be as same as NRFX_POWER_USB_EVT_* in nrfx_power.h
   enum {
     USB_EVT_DETECTED = 0,
@@ -980,51 +907,42 @@ void tusb_hal_nrf_power_event (uint32_t event)
     USB_EVT_READY = 2
   };
 
-#if CFG_TUSB_DEBUG >= 2
-  const char* const power_evt_str[] = { "Detected", "Removed", "Ready" };
-  TU_LOG(2, "Power USB event: %s\r\n", power_evt_str[event]);
+#if CFG_TUSB_DEBUG >= 3
+  const char* const power_evt_str[] = {"Detected", "Removed", "Ready"};
+  TU_LOG(3, "Power USB event: %s\r\n", power_evt_str[event]);
 #endif
 
-  switch ( event )
-  {
+  switch (event) {
     case USB_EVT_DETECTED:
-      if ( !NRF_USBD->ENABLE )
-      {
+      if (!NRF_USBD->ENABLE) {
         // Prepare for receiving READY event: disable interrupt since we will blocking wait
         NRF_USBD->INTENCLR = USBD_INTEN_USBEVENT_Msk;
         NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
-        __ISB(); __DSB(); // for sync
+        __ISB();
+        __DSB(); // for sync
 
 #ifdef NRF52_SERIES // NRF53 does not need this errata
         // ERRATA 171, 187, 166
-        if ( nrfx_usbd_errata_187() )
-        {
+        if (nrfx_usbd_errata_187()) {
           // CRITICAL_REGION_ENTER();
-          if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
-          {
-            *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
-            *((volatile uint32_t *) (0x4006ED14)) = 0x00000003;
-            *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
-          }
-          else
-          {
-            *((volatile uint32_t *) (0x4006ED14)) = 0x00000003;
+          if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+            *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+            *((volatile uint32_t*) (0x4006ED14)) = 0x00000003;
+            *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+          } else {
+            *((volatile uint32_t*) (0x4006ED14)) = 0x00000003;
           }
           // CRITICAL_REGION_EXIT();
         }
 
-        if ( nrfx_usbd_errata_171() )
-        {
+        if (nrfx_usbd_errata_171()) {
           // CRITICAL_REGION_ENTER();
-          if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
-          {
-            *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
-            *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0;
-            *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
-          }
-          else
-          {
-            *((volatile uint32_t *) (0x4006EC14)) = 0x000000C0;
+          if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+            *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+            *((volatile uint32_t*) (0x4006EC14)) = 0x000000C0;
+            *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+          } else {
+            *((volatile uint32_t*) (0x4006EC14)) = 0x000000C0;
           }
           // CRITICAL_REGION_EXIT();
         }
@@ -1032,64 +950,58 @@ void tusb_hal_nrf_power_event (uint32_t event)
 
         // Enable the peripheral (will cause Ready event)
         NRF_USBD->ENABLE = 1;
-        __ISB(); __DSB(); // for sync
+        __ISB();
+        __DSB(); // for sync
 
         // Enable HFCLK
         hfclk_enable();
       }
-    break;
+      break;
 
     case USB_EVT_READY:
       // Skip if pull-up is enabled and HCLK is already running.
       // Application probably call this more than necessary.
-      if ( NRF_USBD->USBPULLUP && hfclk_running() ) break;
+      if (NRF_USBD->USBPULLUP && hfclk_running()) break;
 
       // Waiting for USBD peripheral enabled
-      while ( !(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE) ) { }
+      while (!(USBD_EVENTCAUSE_READY_Msk & NRF_USBD->EVENTCAUSE)) {}
 
       NRF_USBD->EVENTCAUSE = USBD_EVENTCAUSE_READY_Msk;
-      __ISB(); __DSB(); // for sync
+      __ISB();
+      __DSB(); // for sync
 
 #ifdef NRF52_SERIES
-      if ( nrfx_usbd_errata_171() )
-      {
+      if (nrfx_usbd_errata_171()) {
         // CRITICAL_REGION_ENTER();
-        if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
-        {
-          *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
-          *((volatile uint32_t *) (0x4006EC14)) = 0x00000000;
-          *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
-        }
-        else
-        {
-          *((volatile uint32_t *) (0x4006EC14)) = 0x00000000;
+        if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+          *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+          *((volatile uint32_t*) (0x4006EC14)) = 0x00000000;
+          *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+        } else {
+          *((volatile uint32_t*) (0x4006EC14)) = 0x00000000;
         }
 
         // CRITICAL_REGION_EXIT();
       }
 
-      if ( nrfx_usbd_errata_187() )
-      {
+      if (nrfx_usbd_errata_187()) {
         // CRITICAL_REGION_ENTER();
-        if ( *((volatile uint32_t *) (0x4006EC00)) == 0x00000000 )
-        {
-          *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
-          *((volatile uint32_t *) (0x4006ED14)) = 0x00000000;
-          *((volatile uint32_t *) (0x4006EC00)) = 0x00009375;
-        }
-        else
-        {
-          *((volatile uint32_t *) (0x4006ED14)) = 0x00000000;
+        if (*((volatile uint32_t*) (0x4006EC00)) == 0x00000000) {
+          *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+          *((volatile uint32_t*) (0x4006ED14)) = 0x00000000;
+          *((volatile uint32_t*) (0x4006EC00)) = 0x00009375;
+        } else {
+          *((volatile uint32_t*) (0x4006ED14)) = 0x00000000;
         }
         // CRITICAL_REGION_EXIT();
       }
 
-      if ( nrfx_usbd_errata_166() )
-      {
-        *((volatile uint32_t *) (NRF_USBD_BASE + 0x800)) = 0x7E3;
-        *((volatile uint32_t *) (NRF_USBD_BASE + 0x804)) = 0x40;
+      if (nrfx_usbd_errata_166()) {
+        *((volatile uint32_t*) (NRF_USBD_BASE + 0x800)) = 0x7E3;
+        *((volatile uint32_t*) (NRF_USBD_BASE + 0x804)) = 0x40;
 
-        __ISB(); __DSB();
+        __ISB();
+        __DSB();
       }
 #endif
 
@@ -1101,30 +1013,31 @@ void tusb_hal_nrf_power_event (uint32_t event)
 
       // Enable interrupt, priorities should be set by application
       NVIC_ClearPendingIRQ(USBD_IRQn);
+
       // Don't enable USBD interrupt yet, if dcd_init() did not finish yet
       // Interrupt will be enabled by tud_init(), when USB stack is ready
       // to handle interrupts.
-      if (tud_inited())
-      {
+      if (tud_inited()) {
         NVIC_EnableIRQ(USBD_IRQn);
       }
 
       // Wait for HFCLK
-      while ( !hfclk_running() ) { }
+      while (!hfclk_running()) {}
 
       // Enable pull up
       NRF_USBD->USBPULLUP = 1;
-      __ISB(); __DSB(); // for sync
-    break;
+      __ISB();
+      __DSB(); // for sync
+      break;
 
     case USB_EVT_REMOVED:
-      if ( NRF_USBD->ENABLE )
-      {
+      if (NRF_USBD->ENABLE) {
         // Abort all transfers
 
         // Disable pull up
         NRF_USBD->USBPULLUP = 0;
-        __ISB(); __DSB(); // for sync
+        __ISB();
+        __DSB(); // for sync
 
         // Disable Interrupt
         NVIC_DisableIRQ(USBD_IRQn);
@@ -1133,15 +1046,17 @@ void tusb_hal_nrf_power_event (uint32_t event)
         NRF_USBD->INTENCLR = NRF_USBD->INTEN;
 
         NRF_USBD->ENABLE = 0;
-        __ISB(); __DSB(); // for sync
+        __ISB();
+        __DSB(); // for sync
 
         hfclk_disable();
 
         dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, is_in_isr());
       }
-    break;
+      break;
 
-    default: break;
+    default:
+      break;
   }
 }
 
diff --git a/src/portable/nxp/khci/dcd_khci.c b/src/portable/nxp/khci/dcd_khci.c
index dc71117b3..ef61146aa 100644
--- a/src/portable/nxp/khci/dcd_khci.c
+++ b/src/portable/nxp/khci/dcd_khci.c
@@ -540,7 +540,7 @@ void dcd_int_handler(uint8_t rhport)
   }
 
   if (is & USB_ISTAT_SLEEP_MASK) {
-    // TU_LOG2("Suspend: "); TU_LOG2_HEX(is);
+    // TU_LOG3("Suspend: "); TU_LOG2_HEX(is);
 
     // Note Host usually has extra delay after bus reset (without SOF), which could falsely
     // detected as Sleep event. Though usbd has debouncing logic so we are good
diff --git a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
index f4ed09d83..cc18cf59b 100644
--- a/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
+++ b/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
@@ -42,7 +42,18 @@
 
 #if TU_CHECK_MCU(OPT_MCU_LPC11UXX, OPT_MCU_LPC13XX, OPT_MCU_LPC15XX)
   // LPCOpen
+  #ifdef __GNUC__
+  #pragma GCC diagnostic push
+  #pragma GCC diagnostic ignored "-Wunused-parameter"
+  #pragma GCC diagnostic ignored "-Wstrict-prototypes"
+  #endif
+
   #include "chip.h"
+
+  #ifdef __GNUC__
+  #pragma GCC diagnostic pop
+  #endif
+
 #else
   // SDK
   #include "fsl_device_registers.h"
@@ -239,7 +250,7 @@ TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(ep_cmd_sts_t* ep_cs, bool is_
   return is_highspeed ? (ep_cs[0].cmd_sts.type && !ep_cs[0].cmd_sts.rf_tv) : ep_cs->cmd_sts.type;
 }
 
-TU_ATTR_ALWAYS_INLINE static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) {
+TU_ATTR_ALWAYS_INLINE TU_ATTR_UNUSED static inline bool ep_is_bulk(ep_cmd_sts_t* ep_cs) {
   return (ep_cs[0].cmd_sts.type == 0) && (ep_cs[0].cmd_sts.rf_tv == 0);
 }
 
diff --git a/src/portable/raspberrypi/rp2040/rp2040_usb.c b/src/portable/raspberrypi/rp2040/rp2040_usb.c
index 1ca711c77..43f48da39 100644
--- a/src/portable/raspberrypi/rp2040/rp2040_usb.c
+++ b/src/portable/raspberrypi/rp2040/rp2040_usb.c
@@ -53,6 +53,14 @@ TU_ATTR_ALWAYS_INLINE static inline bool is_host_mode(void) {
 //--------------------------------------------------------------------+
 // Implementation
 //--------------------------------------------------------------------+
+// Provide own byte by byte memcpy as not all copies are aligned
+static void unaligned_memcpy(void *dst, const void *src, size_t n) {
+  uint8_t *dst_byte = (uint8_t*)dst;
+  const uint8_t *src_byte = (const uint8_t*)src;
+  while (n--) {
+    *dst_byte++ = *src_byte++;
+  }
+}
 
 void rp2040_usb_init(void) {
   // Reset usb controller
@@ -67,7 +75,6 @@ void rp2040_usb_init(void) {
 #pragma GCC diagnostic ignored "-Wstringop-overflow"
 #endif
 #endif
-  memset(usb_hw, 0, sizeof(*usb_hw));
   memset(usb_dpram, 0, sizeof(*usb_dpram));
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
@@ -125,7 +132,7 @@ static uint32_t __tusb_irq_path_func(prepare_ep_buffer)(struct hw_endpoint* ep,
 
   if (!ep->rx) {
     // Copy data from user buffer to hw buffer
-    memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen);
+    unaligned_memcpy(ep->hw_data_buf + buf_id * 64, ep->user_buf, buflen);
     ep->user_buf += buflen;
 
     // Mark as full
@@ -230,7 +237,7 @@ static uint16_t __tusb_irq_path_func(sync_ep_buffer)(struct hw_endpoint* ep, uin
     // we have received AFTER we have copied it to the user buffer at the appropriate offset
     assert(buf_ctrl & USB_BUF_CTRL_FULL);
 
-    memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes);
+    unaligned_memcpy(ep->user_buf, ep->hw_data_buf + buf_id * 64, xferred_bytes);
     ep->xferred_len = (uint16_t) (ep->xferred_len + xferred_bytes);
     ep->user_buf += xferred_bytes;
   }
diff --git a/src/portable/renesas/rusb2/dcd_rusb2.c b/src/portable/renesas/rusb2/dcd_rusb2.c
index 24edc30e7..7c6044ca0 100644
--- a/src/portable/renesas/rusb2/dcd_rusb2.c
+++ b/src/portable/renesas/rusb2/dcd_rusb2.c
@@ -29,10 +29,6 @@
 
 #if CFG_TUD_ENABLED && defined(TUP_USBIP_RUSB2)
 
-// Since TinyUSB doesn't use SOF for now, and this interrupt too often (1ms interval)
-// We disable SOF for now until needed later on
-#define USE_SOF     0
-
 #include "device/dcd.h"
 #include "rusb2_type.h"
 
@@ -57,30 +53,25 @@
 //--------------------------------------------------------------------+
 // MACRO TYPEDEF CONSTANT ENUM
 //--------------------------------------------------------------------+
+enum {
+  PIPE_COUNT = 10,
+};
 
-/* Start of definition of packed structs (used by the CCRX toolchain) */
-TU_ATTR_PACKED_BEGIN
-TU_ATTR_BIT_FIELD_ORDER_BEGIN
-
-typedef struct TU_ATTR_PACKED
-{
+typedef struct {
   void      *buf;      /* the start address of a transfer data buffer */
   uint16_t  length;    /* the number of bytes in the buffer */
   uint16_t  remaining; /* the number of bytes remaining in the buffer */
-  struct {
-    uint32_t ep  : 8;  /* an assigned endpoint address */
-    uint32_t ff  : 1;  /* `buf` is TU_FUFO or POD */
-    uint32_t     : 0;
-  };
-} pipe_state_t;
 
-TU_ATTR_PACKED_END  // End of definition of packed structs (used by the CCRX toolchain)
-TU_ATTR_BIT_FIELD_ORDER_END
+  uint8_t ep; /* an assigned endpoint address */
+  uint8_t ff; /* `buf` is TU_FUFO or POD */
+} pipe_state_t;
 
 typedef struct
 {
-  pipe_state_t pipe[10];
+  pipe_state_t pipe[PIPE_COUNT];
   uint8_t ep[2][16];   /* a lookup table for a pipe index from an endpoint address */
+  // Track whether sof has been manually enabled
+  bool sof_enabled;
 } dcd_data_t;
 
 static dcd_data_t _dcd;
@@ -89,52 +80,44 @@ static dcd_data_t _dcd;
 // INTERNAL OBJECT & FUNCTION DECLARATION
 //--------------------------------------------------------------------+
 
-// Transfer conditions specifiable for each pipe:
+
+// Transfer conditions specifiable for each pipe for most MCUs
 // - Pipe 0: Control transfer with 64-byte single buffer
-// - Pipes 1 and 2: Bulk isochronous transfer continuous transfer mode with programmable buffer size up
-//   to 2 KB and optional double buffer
-// - Pipes 3 to 5: Bulk transfer continuous transfer mode with programmable buffer size up to 2 KB and
-//   optional double buffer
-// - Pipes 6 to 9: Interrupt transfer with 64-byte single buffer
-enum {
-  PIPE_1ST_BULK = 3,
-  PIPE_1ST_INTERRUPT = 6,
-  PIPE_COUNT = 10,
-};
+// - Pipes 1 and 2: Bulk or ISO
+// - Pipes 3 to 5: Bulk
+// - Pipes 6 to 9: Interrupt
+//
+// Note: for small mcu such as
+// - RA2A1: only pipe 4-7 are available, and no support for ISO
+static unsigned find_pipe(unsigned xfer_type) {
+  #if defined(BSP_MCU_GROUP_RA2A1)
+  const uint8_t pipe_idx_arr[4][2] = {
+      { 0, 0 }, // Control
+      { 0, 0 }, // Isochronous not supported
+      { 4, 5 }, // Bulk
+      { 6, 7 }, // Interrupt
+  };
+  #else
+  const uint8_t pipe_idx_arr[4][2] = {
+      { 0, 0 }, // Control
+      { 1, 2 }, // Isochronous
+      { 1, 5 }, // Bulk
+      { 6, 9 }, // Interrupt
+  };
+  #endif
 
-static unsigned find_pipe(unsigned xfer)
-{
-  switch (xfer) {
-    case TUSB_XFER_ISOCHRONOUS:
-      for (int i = 1; i < PIPE_1ST_BULK; ++i) {
-        if (0 == _dcd.pipe[i].ep) return i;
-      }
-      break;
+  // find backward since only pipe 1, 2 support ISO
+  const uint8_t idx_first = pipe_idx_arr[xfer_type][0];
+  const uint8_t idx_last  = pipe_idx_arr[xfer_type][1];
 
-    case TUSB_XFER_BULK:
-      for (int i = PIPE_1ST_BULK; i < PIPE_1ST_INTERRUPT; ++i) {
-        if (0 == _dcd.pipe[i].ep) return i;
-      }
-      for (int i = 1; i < PIPE_1ST_BULK; ++i) {
-        if (0 == _dcd.pipe[i].ep) return i;
-      }
-      break;
-
-    case TUSB_XFER_INTERRUPT:
-      for (int i = PIPE_1ST_INTERRUPT; i < PIPE_COUNT; ++i) {
-        if (0 == _dcd.pipe[i].ep) return i;
-      }
-      break;
-
-    default:
-      /* No support for control transfer */
-      break;
+  for (int i = idx_last; i >= idx_first; i--) {
+    if (0 == _dcd.pipe[i].ep) return i;
   }
+
   return 0;
 }
 
-static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num)
-{
+static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num) {
   if (num) {
     return (volatile uint16_t*)&(rusb->PIPE_CTR[num - 1]);
   } else {
@@ -142,8 +125,7 @@ static volatile uint16_t* get_pipectr(rusb2_reg_t *rusb, unsigned num)
   }
 }
 
-static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num)
-{
+static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num) {
   volatile reg_pipetre_t* tre = NULL;
   if ((1 <= num) && (num <= 5)) {
     tre = (volatile reg_pipetre_t*)&(rusb->PIPE_TR[num - 1].E);
@@ -151,8 +133,7 @@ static volatile reg_pipetre_t* get_pipetre(rusb2_reg_t *rusb, unsigned num)
   return tre;
 }
 
-static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr)
-{
+static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr) {
   rusb2_reg_t *rusb = RUSB2_REG(rhport);
   const unsigned epn = tu_edpt_number(ep_addr);
 
@@ -165,19 +146,16 @@ static volatile uint16_t* ep_addr_to_pipectr(uint8_t rhport, unsigned ep_addr)
   }
 }
 
-static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb)
-{
+static uint16_t edpt0_max_packet_size(rusb2_reg_t* rusb) {
   return rusb->DCPMAXP_b.MXPS;
 }
 
-static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num)
-{
+static uint16_t edpt_max_packet_size(rusb2_reg_t *rusb, unsigned num) {
   rusb->PIPESEL = num;
   return rusb->PIPEMAXP;
 }
 
-static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num)
-{
+static inline void pipe_wait_for_ready(rusb2_reg_t * rusb, unsigned num) {
   while ( rusb->D0FIFOSEL_b.CURPIPE != num ) {}
   while ( !rusb->D0FIFOCTR_b.FRDY ) {}
 }
@@ -266,14 +244,6 @@ static void pipe_read_packet_ff(rusb2_reg_t * rusb, tu_fifo_t *f, volatile void
   tu_fifo_advance_write_pointer(f, count);
 }
 
-
-static bool wait_pipe_fifo_empty(rusb2_reg_t* rusb, uint8_t num) {
-  TU_ASSERT(num);
-  while( (rusb->PIPE_CTR[num-1] & RUSB2_PIPE_CTR_INBUFM_Msk) > 0 ) {}
-  return true;
-}
-
-
 //--------------------------------------------------------------------+
 // Pipe Transfer
 //--------------------------------------------------------------------+
@@ -347,7 +317,6 @@ static bool pipe_xfer_in(rusb2_reg_t* rusb, unsigned num)
   const unsigned rem  = pipe->remaining;
 
   if (!rem) {
-    wait_pipe_fifo_empty(rusb, num);
     pipe->buf = NULL;
     return true;
   }
@@ -693,6 +662,10 @@ void dcd_init(uint8_t rhport)
   rusb2_reg_t* rusb = RUSB2_REG(rhport);
   rusb2_module_start(rhport, true);
 
+// We disable SOF for now until needed later on.
+// Since TinyUSB doesn't use SOF for now, and this interrupt often (1ms interval)
+_dcd.sof_enabled = false;
+
 #ifdef RUSB2_SUPPORT_HIGHSPEED
   if ( rusb2_is_highspeed_rhport(rhport) ) {
     rusb->SYSCFG_b.HSE = 1;
@@ -737,7 +710,7 @@ void dcd_init(uint8_t rhport)
 
   rusb->INTSTS0 = 0;
   rusb->INTENB0 = RUSB2_INTSTS0_VBINT_Msk | RUSB2_INTSTS0_BRDY_Msk | RUSB2_INTSTS0_BEMP_Msk |
-                  RUSB2_INTSTS0_DVST_Msk | RUSB2_INTSTS0_CTRT_Msk | (USE_SOF ? RUSB2_INTSTS0_SOFR_Msk : 0) |
+                  RUSB2_INTSTS0_DVST_Msk | RUSB2_INTSTS0_CTRT_Msk | (_dcd.sof_enabled ? RUSB2_INTSTS0_SOFR_Msk : 0) |
                   RUSB2_INTSTS0_RESM_Msk;
   rusb->BEMPENB = 1;
   rusb->BRDYENB = 1;
@@ -785,10 +758,9 @@ void dcd_disconnect(uint8_t rhport)
 
 void dcd_sof_enable(uint8_t rhport, bool en)
 {
-  (void) rhport;
-  (void) en;
-
-  // TODO implement later
+  rusb2_reg_t* rusb = RUSB2_REG(rhport);
+  _dcd.sof_enabled = en;
+  rusb->INTENB0_b.SOFE = en ? 1: 0;
 }
 
 //--------------------------------------------------------------------+
@@ -844,7 +816,7 @@ bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const * ep_desc)
   }
 
   rusb->PIPECFG = cfg;
-  rusb->BRDYSTS = 0x1FFu ^ TU_BIT(num);
+  rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num);
   rusb->BRDYENB |= TU_BIT(num);
 
   if (dir || (xfer != TUSB_XFER_BULK)) {
@@ -944,6 +916,18 @@ void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr)
 //--------------------------------------------------------------------+
 // ISR
 //--------------------------------------------------------------------+
+
+#if defined(__CCRX__)
+TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) {
+  unsigned int count = 0;
+  while ((value & 1) == 0) {
+    value >>= 1;
+    count++;
+  }
+  return count;
+}
+#endif
+
 void dcd_int_handler(uint8_t rhport)
 {
   rusb2_reg_t* rusb = RUSB2_REG(rhport);
@@ -966,18 +950,19 @@ void dcd_int_handler(uint8_t rhport)
   // Resumed
   if ( is0 & RUSB2_INTSTS0_RESM_Msk ) {
     dcd_event_bus_signal(rhport, DCD_EVENT_RESUME, true);
-#if (0 == USE_SOF)
-    rusb->INTENB0_b.SOFE = 0;
-#endif
+    if (!_dcd.sof_enabled) {
+      rusb->INTENB0_b.SOFE = 0;
+    }
   }
 
   // SOF received
   if ( (is0 & RUSB2_INTSTS0_SOFR_Msk) && rusb->INTENB0_b.SOFE ) {
     // USBD will exit suspended mode when SOF event is received
-    dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
-#if (0 == USE_SOF)
-    rusb->INTENB0_b.SOFE = 0;
-#endif
+    const uint32_t frame = rusb->FRMNUM_b.FRNM;
+    dcd_event_sof(rhport, frame, true);
+    if (!_dcd.sof_enabled) {
+      rusb->INTENB0_b.SOFE = 0;
+    }
   }
 
   // Device state changes
@@ -996,9 +981,9 @@ void dcd_int_handler(uint8_t rhport)
       case RUSB2_INTSTS0_DVSQ_STATE_SUSP2:
       case RUSB2_INTSTS0_DVSQ_STATE_SUSP3:
         dcd_event_bus_signal(rhport, DCD_EVENT_SUSPEND, true);
-#if (0 == USE_SOF)
-        rusb->INTENB0_b.SOFE = 1;
-#endif
+        if (!_dcd.sof_enabled) {
+          rusb->INTENB0_b.SOFE = 1;
+        }
 
       default: break;
     }
@@ -1035,17 +1020,7 @@ void dcd_int_handler(uint8_t rhport)
     /* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
     rusb->BRDYSTS = ~s;
     while (s) {
-#if defined(__CCRX__)
-      static const int Mod37BitPosition[] = {
-        -1, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
-        7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
-        20, 8, 19, 18
-      };
-
-      const unsigned num = Mod37BitPosition[(-s & s) % 37];
-#else
       const unsigned num = __builtin_ctz(s);
-#endif
       process_pipe_brdy(rhport, num);
       s &= ~TU_BIT(num);
     }
diff --git a/src/portable/renesas/rusb2/hcd_rusb2.c b/src/portable/renesas/rusb2/hcd_rusb2.c
index bf95be707..f140da690 100644
--- a/src/portable/renesas/rusb2/hcd_rusb2.c
+++ b/src/portable/renesas/rusb2/hcd_rusb2.c
@@ -45,6 +45,9 @@
 //--------------------------------------------------------------------+
 // MACRO TYPEDEF CONSTANT ENUM DECLARATION
 //--------------------------------------------------------------------+
+enum {
+  PIPE_COUNT = 10,
+};
 
 TU_ATTR_PACKED_BEGIN
 TU_ATTR_BIT_FIELD_ORDER_BEGIN
@@ -75,7 +78,7 @@ TU_ATTR_BIT_FIELD_ORDER_END
 typedef struct
 {
   bool         need_reset; /* The device has not been reset after connection. */
-  pipe_state_t pipe[10];
+  pipe_state_t pipe[PIPE_COUNT];
   uint8_t ep[4][2][15];   /* a lookup table for a pipe index from an endpoint address */
   uint8_t      ctl_mps[5]; /* EP0 max packet size for each device */
 } hcd_data_t;
@@ -86,46 +89,30 @@ typedef struct
 static hcd_data_t _hcd;
 
 // TODO merged with DCD
-// Transfer conditions specifiable for each pipe:
+// Transfer conditions specifiable for each pipe for most MCUs
 // - Pipe 0: Control transfer with 64-byte single buffer
-// - Pipes 1 and 2: Bulk isochronous transfer continuous transfer mode with programmable buffer size up
-//   to 2 KB and optional double buffer
-// - Pipes 3 to 5: Bulk transfer continuous transfer mode with programmable buffer size up to 2 KB and
-//   optional double buffer
-// - Pipes 6 to 9: Interrupt transfer with 64-byte single buffer
-enum {
-  PIPE_1ST_BULK = 3,
-  PIPE_1ST_INTERRUPT = 6,
-  PIPE_COUNT = 10,
-};
+// - Pipes 1 and 2: Bulk or ISO
+// - Pipes 3 to 5: Bulk
+// - Pipes 6 to 9: Interrupt
+//
+// Note: for small mcu such as
+// - RA2A1: only pipe 4-7 are available, and no support for ISO
+static unsigned find_pipe(unsigned xfer_type) {
+  const uint8_t pipe_idx_arr[4][2] = {
+      { 0, 0 }, // Control
+      { 1, 2 }, // Isochronous
+      { 1, 5 }, // Bulk
+      { 6, 9 }, // Interrupt
+  };
 
-static unsigned find_pipe(unsigned xfer) {
-  switch ( xfer ) {
-    case TUSB_XFER_ISOCHRONOUS:
-      for (int i = 1; i < PIPE_1ST_BULK; ++i) {
-        if ( 0 == _hcd.pipe[i].ep ) return i;
-      }
-      break;
+  // find backward since only pipe 1, 2 support ISO
+  const uint8_t idx_first = pipe_idx_arr[xfer_type][0];
+  const uint8_t idx_last  = pipe_idx_arr[xfer_type][1];
 
-    case TUSB_XFER_BULK:
-      for (int i = PIPE_1ST_BULK; i < PIPE_1ST_INTERRUPT; ++i) {
-        if ( 0 == _hcd.pipe[i].ep ) return i;
-      }
-      for (int i = 1; i < PIPE_1ST_BULK; ++i) {
-        if ( 0 == _hcd.pipe[i].ep ) return i;
-      }
-      break;
-
-    case TUSB_XFER_INTERRUPT:
-      for (int i = PIPE_1ST_INTERRUPT; i < PIPE_COUNT; ++i) {
-        if ( 0 == _hcd.pipe[i].ep ) return i;
-      }
-      break;
-
-    default:
-      /* No support for control transfer */
-      break;
+  for (int i = idx_last; i >= idx_first; i--) {
+    if (0 == _hcd.pipe[i].ep) return i;
   }
+
   return 0;
 }
 
@@ -718,7 +705,7 @@ bool hcd_edpt_open(uint8_t rhport, uint8_t dev_addr, tusb_desc_endpoint_t const
   }
 
   rusb->PIPECFG = cfg;
-  rusb->BRDYSTS = 0x1FFu ^ TU_BIT(num);
+  rusb->BRDYSTS = 0x3FFu ^ TU_BIT(num);
   rusb->NRDYENB |= TU_BIT(num);
   rusb->BRDYENB |= TU_BIT(num);
 
@@ -771,6 +758,17 @@ bool hcd_edpt_clear_stall(uint8_t rhport, uint8_t dev_addr, uint8_t ep_addr) {
 //--------------------------------------------------------------------+
 // ISR
 //--------------------------------------------------------------------+
+#if defined(__CCRX__)
+TU_ATTR_ALWAYS_INLINE static inline unsigned __builtin_ctz(unsigned int value) {
+  unsigned int count = 0;
+  while ((value & 1) == 0) {
+    value >>= 1;
+    count++;
+  }
+  return count;
+}
+#endif
+
 void hcd_int_handler(uint8_t rhport, bool in_isr) {
   (void) in_isr;
 
@@ -820,23 +818,12 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
     }
   }
 
-#if defined(__CCRX__)
-  static const int Mod37BitPosition[] = {
-    -1, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4,
-    7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5,
-    20, 8, 19, 18};
-#endif
-
   if (is0 & RUSB2_INTSTS0_NRDY_Msk) {
     const unsigned m = rusb->NRDYENB;
     unsigned s = rusb->NRDYSTS & m;
     rusb->NRDYSTS = ~s;
     while (s) {
-#if defined(__CCRX__)
-      const unsigned num = Mod37BitPosition[(-s & s) % 37];
-#else
       const unsigned num = __builtin_ctz(s);
-#endif
       process_pipe_nrdy(rhport, num);
       s &= ~TU_BIT(num);
     }
@@ -847,11 +834,7 @@ void hcd_int_handler(uint8_t rhport, bool in_isr) {
     /* clear active bits (don't write 0 to already cleared bits according to the HW manual) */
     rusb->BRDYSTS = ~s;
     while (s) {
-#if defined(__CCRX__)
-      const unsigned num = Mod37BitPosition[(-s & s) % 37];
-#else
       const unsigned num = __builtin_ctz(s);
-#endif
       process_pipe_brdy(rhport, num);
       s &= ~TU_BIT(num);
     }
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
index 7bf726f3f..cff328418 100644
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+++ b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
@@ -37,14 +37,20 @@
  * It also should work with minimal changes for any ST MCU with an "USB A"/"PCD"/"HCD" peripheral. This
  *  covers:
  *
- * F04x, F072, F078, 070x6/B      1024 byte buffer
+ * F04x, F072, F078, F070x6/B     1024 byte buffer
  * F102, F103                      512 byte buffer; no internal D+ pull-up (maybe many more changes?)
  * F302xB/C, F303xB/C, F373        512 byte buffer; no internal D+ pull-up
  * F302x6/8, F302xD/E2, F303xD/E  1024 byte buffer; no internal D+ pull-up
+ * G0                             2048 byte buffer; 32-bit bus; host mode
+ * G4                             1024 byte buffer
+ * H5                             2048 byte buffer; 32-bit bus; host mode
  * L0x2, L0x3                     1024 byte buffer
  * L1                              512 byte buffer
  * L4x2, L4x3                     1024 byte buffer
- * G0                             2048 byte buffer
+ * L5                             1024 byte buffer
+ * U0                             1024 byte buffer; 32-bit bus
+ * U535, U545                     2048 byte buffer; 32-bit bus; host mode
+ * WB35, WB55                     1024 byte buffer
  *
  * To use this driver, you must:
  * - If you are using a device with crystal-less USB, set up the clock recovery system (CRS)
@@ -102,74 +108,53 @@
 
 #include "tusb_option.h"
 
-#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV)
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_FSDEV) && \
+    !(defined(TUP_USBIP_FSDEV_CH32) && CFG_TUD_WCH_USBIP_FSDEV == 0)
 
 #include "device/dcd.h"
 
-#ifdef TUP_USBIP_FSDEV_STM32
+#if defined(TUP_USBIP_FSDEV_STM32)
   // Undefine to reduce the dependence on HAL
   #undef USE_HAL_DRIVER
-  #include "portable/st/stm32_fsdev/dcd_stm32_fsdev.h"
+  #include "fsdev_stm32.h"
+#elif defined(TUP_USBIP_FSDEV_CH32)
+  #include "fsdev_ch32.h"
+#else
+  #error "Unknown USB IP"
 #endif
 
-/*****************************************************
- * Configuration
- *****************************************************/
+#include "fsdev_type.h"
 
-// HW supports max of 8 bidirectional endpoints, but this can be reduced to save RAM
-// (8u here would mean 8 IN and 8 OUT)
-#ifndef MAX_EP_COUNT
-#  define MAX_EP_COUNT 8U
-#endif
+//--------------------------------------------------------------------+
+// Configuration
+//--------------------------------------------------------------------+
 
-// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it
-// Both of these MUST be a multiple of 2, and are in byte units.
-#ifndef DCD_STM32_BTABLE_BASE
-#  define DCD_STM32_BTABLE_BASE 0U
-#endif
 
-#ifndef DCD_STM32_BTABLE_SIZE
-#  define DCD_STM32_BTABLE_SIZE (FSDEV_PMA_SIZE - DCD_STM32_BTABLE_BASE)
-#endif
-
-/***************************************************
- * Checks, structs, defines, function definitions, etc.
- */
-
-TU_VERIFY_STATIC((MAX_EP_COUNT) <= STFSDEV_EP_COUNT, "Only 8 endpoints supported on the hardware");
-TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) + (DCD_STM32_BTABLE_SIZE)) <= (FSDEV_PMA_SIZE), "BTABLE does not fit in PMA RAM");
-TU_VERIFY_STATIC(((DCD_STM32_BTABLE_BASE) % 8) == 0, "BTABLE base must be aligned to 8 bytes");
 
 //--------------------------------------------------------------------+
 // MACRO CONSTANT TYPEDEF
 //--------------------------------------------------------------------+
 
 // One of these for every EP IN & OUT, uses a bit of RAM....
-typedef struct
-{
-  uint8_t * buffer;
-  tu_fifo_t * ff;
+typedef struct {
+  uint8_t *buffer;
+  tu_fifo_t *ff;
   uint16_t total_len;
   uint16_t queued_len;
-  uint16_t pma_ptr;
   uint16_t max_packet_size;
-  uint16_t pma_alloc_size;
-  uint8_t ep_idx; // index for USB_EPnR register
+  uint8_t ep_idx;   // index for USB_EPnR register
+  bool iso_in_sending; // Workaround for ISO IN EP doesn't have interrupt mask
 } xfer_ctl_t;
 
 // EP allocator
-typedef struct
-{
+typedef struct {
   uint8_t ep_num;
   uint8_t ep_type;
   bool allocated[2];
 } ep_alloc_t;
 
-static xfer_ctl_t xfer_status[MAX_EP_COUNT][2];
-
-static ep_alloc_t ep_alloc_status[STFSDEV_EP_COUNT];
-
-static TU_ATTR_ALIGNED(4) uint32_t _setup_packet[6];
+static xfer_ctl_t xfer_status[CFG_TUD_ENDPPOINT_MAX][2];
+static ep_alloc_t ep_alloc_status[FSDEV_EP_COUNT];
 
 static uint8_t remoteWakeCountdown; // When wake is requested
 
@@ -178,267 +163,86 @@ static uint8_t remoteWakeCountdown; // When wake is requested
 //--------------------------------------------------------------------+
 
 // into the stack.
-static void dcd_handle_bus_reset(void);
-static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix);
-static void dcd_ep_ctr_handler(void);
+static void handle_bus_reset(uint8_t rhport);
+static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix);
+static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, uint8_t dir);
 
 // PMA allocation/access
-static uint8_t open_ep_count;
 static uint16_t ep_buf_ptr; ///< Points to first free memory location
-static void dcd_pma_alloc_reset(void);
-static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length);
-static void dcd_pma_free(uint8_t ep_addr);
-static void dcd_ep_free(uint8_t ep_addr);
+static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf);
 static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type);
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes);
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes);
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes);
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes);
 
-static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes);
-static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes);
+static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes);
+static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes);
+
+static void edpt0_open(uint8_t rhport);
 
 //--------------------------------------------------------------------+
 // Inline helper
 //--------------------------------------------------------------------+
 
-TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t* xfer_ctl_ptr(uint32_t ep_addr) {
-  uint8_t epnum = tu_edpt_number(ep_addr);
-  uint8_t dir = tu_edpt_dir(ep_addr);
-  // Fix -Werror=null-dereference
-  TU_ASSERT(epnum < MAX_EP_COUNT, &xfer_status[0][0]);
-
+TU_ATTR_ALWAYS_INLINE static inline xfer_ctl_t *xfer_ctl_ptr(uint8_t epnum, uint8_t dir) {
   return &xfer_status[epnum][dir];
 }
 
 //--------------------------------------------------------------------+
 // Controller API
 //--------------------------------------------------------------------+
-
-void dcd_init (uint8_t rhport)
-{
-  /* Clocks should already be enabled */
-  /* Use __HAL_RCC_USB_CLK_ENABLE(); to enable the clocks before calling this function */
-
-  /* The RM mentions to use a special ordering of PDWN and FRES, but this isn't done in HAL.
-   * Here, the RM is followed. */
-
-  for(uint32_t i = 0; i<200; i++) // should be a few us
-  {
+void dcd_init(uint8_t rhport) {
+  // Follow the RM mentions to use a special ordering of PDWN and FRES
+  for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
     asm("NOP");
   }
+
   // Perform USB peripheral reset
-  USB->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
-  for(uint32_t i = 0; i<200; i++) // should be a few us
-  {
+  FSDEV_REG->CNTR = USB_CNTR_FRES | USB_CNTR_PDWN;
+  for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
     asm("NOP");
   }
 
-  USB->CNTR &= ~USB_CNTR_PDWN;
+  FSDEV_REG->CNTR &= ~USB_CNTR_PDWN;
 
   // Wait startup time, for F042 and F070, this is <= 1 us.
-  for(uint32_t i = 0; i<200; i++) // should be a few us
-  {
+  for (volatile uint32_t i = 0; i < 200; i++) { // should be a few us
     asm("NOP");
   }
-  USB->CNTR = 0; // Enable USB
+  FSDEV_REG->CNTR = 0; // Enable USB
 
-#if !defined(STM32G0) && !defined(STM32H5)  // BTABLE register does not exist any more on STM32G0, it is fixed to USB SRAM base address
-  USB->BTABLE = DCD_STM32_BTABLE_BASE;
+#if !defined(FSDEV_BUS_32BIT)
+  // BTABLE register does not exist any more on 32-bit bus devices
+  FSDEV_REG->BTABLE = FSDEV_BTABLE_BASE;
 #endif
-  USB->ISTR = 0; // Clear pending interrupts
+
+  FSDEV_REG->ISTR = 0; // Clear pending interrupts
 
   // Reset endpoints to disabled
-  for(uint32_t i=0; iCNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
-  dcd_handle_bus_reset();
+  FSDEV_REG->CNTR |= USB_CNTR_RESETM | USB_CNTR_ESOFM | USB_CNTR_CTRM | USB_CNTR_SUSPM | USB_CNTR_WKUPM;
+  handle_bus_reset(rhport);
 
   // Enable pull-up if supported
-  if ( dcd_connect ) dcd_connect(rhport);
+  dcd_connect(rhport);
 }
 
-// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
-#if defined(USB_BCDR_DPPU)
-
-// Disable internal D+ PU
-void dcd_disconnect(uint8_t rhport)
-{
-  (void) rhport;
-  USB->BCDR &= ~(USB_BCDR_DPPU);
-}
-
-// Enable internal D+ PU
-void dcd_connect(uint8_t rhport)
-{
-  (void) rhport;
-  USB->BCDR |= USB_BCDR_DPPU;
-}
-
-#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151
-// Disable internal D+ PU
-void dcd_disconnect(uint8_t rhport)
-{
-  (void) rhport;
-  SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
-}
-
-// Enable internal D+ PU
-void dcd_connect(uint8_t rhport)
-{
-  (void) rhport;
-  SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
-}
-#endif
-
-void dcd_sof_enable(uint8_t rhport, bool en)
-{
-  (void) rhport;
-  (void) en;
-
-  if (en)
-  {
-    USB->CNTR |= USB_CNTR_SOFM;
-  }
-  else
-  {
-    USB->CNTR &= ~USB_CNTR_SOFM;
-  }
-}
-
-// Enable device interrupt
-void dcd_int_enable (uint8_t rhport)
-{
-  (void)rhport;
-  // Member here forces write to RAM before allowing ISR to execute
-  __DSB();
-  __ISB();
-#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \
-    CFG_TUSB_MCU == OPT_MCU_STM32L4
-  NVIC_EnableIRQ(USB_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
-  NVIC_EnableIRQ(USB_LP_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
-  // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
-  // shared USB/CAN IRQs to separate CAN and USB IRQs.
-  // This dynamically checks if this remap is active to enable the right IRQs.
-  #ifdef SYSCFG_CFGR1_USB_IT_RMP
-  if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
-  {
-    NVIC_EnableIRQ(USB_HP_IRQn);
-    NVIC_EnableIRQ(USB_LP_IRQn);
-    NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
-  }
-  else
-  #endif
-  {
-    NVIC_EnableIRQ(USB_HP_CAN_TX_IRQn);
-    NVIC_EnableIRQ(USB_LP_CAN_RX0_IRQn);
-    NVIC_EnableIRQ(USBWakeUp_IRQn);
-  }
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
-  NVIC_EnableIRQ(USB_HP_CAN1_TX_IRQn);
-  NVIC_EnableIRQ(USB_LP_CAN1_RX0_IRQn);
-  NVIC_EnableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
-  NVIC_EnableIRQ(USB_HP_IRQn);
-  NVIC_EnableIRQ(USB_LP_IRQn);
-  NVIC_EnableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
-  #ifdef STM32G0B0xx
-    NVIC_EnableIRQ(USB_IRQn);
-  #else
-    NVIC_EnableIRQ(USB_UCPD1_2_IRQn);
-  #endif
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
-    NVIC_EnableIRQ(USB_DRD_FS_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
-  NVIC_EnableIRQ(USB_HP_IRQn);
-  NVIC_EnableIRQ(USB_LP_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
-  NVIC_EnableIRQ(USB_FS_IRQn);
-
-#else
-  #error Unknown arch in USB driver
-#endif
-}
-
-// Disable device interrupt
-void dcd_int_disable(uint8_t rhport)
-{
+void dcd_sof_enable(uint8_t rhport, bool en) {
   (void)rhport;
 
-#if CFG_TUSB_MCU == OPT_MCU_STM32F0 || CFG_TUSB_MCU == OPT_MCU_STM32L0 || \
-    CFG_TUSB_MCU == OPT_MCU_STM32L4
-  NVIC_DisableIRQ(USB_IRQn);
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
-  NVIC_DisableIRQ(USB_LP_IRQn);
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F3
-  // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
-  // shared USB/CAN IRQs to separate CAN and USB IRQs.
-  // This dynamically checks if this remap is active to disable the right IRQs.
-  #ifdef SYSCFG_CFGR1_USB_IT_RMP
-  if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP)
-  {
-    NVIC_DisableIRQ(USB_HP_IRQn);
-    NVIC_DisableIRQ(USB_LP_IRQn);
-    NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
+  if (en) {
+    FSDEV_REG->CNTR |= USB_CNTR_SOFM;
+  } else {
+    FSDEV_REG->CNTR &= ~USB_CNTR_SOFM;
   }
-  else
-  #endif
-  {
-    NVIC_DisableIRQ(USB_HP_CAN_TX_IRQn);
-    NVIC_DisableIRQ(USB_LP_CAN_RX0_IRQn);
-    NVIC_DisableIRQ(USBWakeUp_IRQn);
-  }
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
-  NVIC_DisableIRQ(USB_HP_CAN1_TX_IRQn);
-  NVIC_DisableIRQ(USB_LP_CAN1_RX0_IRQn);
-  NVIC_DisableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
-  NVIC_DisableIRQ(USB_HP_IRQn);
-  NVIC_DisableIRQ(USB_LP_IRQn);
-  NVIC_DisableIRQ(USBWakeUp_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
-  #ifdef STM32G0B0xx
-    NVIC_DisableIRQ(USB_IRQn);
-  #else
-    NVIC_DisableIRQ(USB_UCPD1_2_IRQn);
-  #endif
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
-    NVIC_DisableIRQ(USB_DRD_FS_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
-  NVIC_DisableIRQ(USB_HP_IRQn);
-  NVIC_DisableIRQ(USB_LP_IRQn);
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
-  NVIC_DisableIRQ(USB_FS_IRQn);
-
-#else
-  #error Unknown arch in USB driver
-#endif
-
-  // CMSIS has a membar after disabling interrupts
 }
 
 // Receive Set Address request, mcu port must also include status IN response
-void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
-{
-  (void) rhport;
-  (void) dev_addr;
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+  (void)dev_addr;
 
   // Respond with status
   dcd_edpt_xfer(rhport, TUSB_DIR_IN_MASK | 0x00, NULL, 0);
@@ -447,46 +251,17 @@ void dcd_set_address(uint8_t rhport, uint8_t dev_addr)
   // do it at dcd_edpt0_status_complete()
 }
 
-void dcd_remote_wakeup(uint8_t rhport)
-{
-  (void) rhport;
+void dcd_remote_wakeup(uint8_t rhport) {
+  (void)rhport;
 
-  USB->CNTR |= USB_CNTR_RESUME;
+  FSDEV_REG->CNTR |= USB_CNTR_RESUME;
   remoteWakeCountdown = 4u; // required to be 1 to 15 ms, ESOF should trigger every 1ms.
 }
 
-static const tusb_desc_endpoint_t ep0OUT_desc =
-{
-  .bLength          = sizeof(tusb_desc_endpoint_t),
-  .bDescriptorType  = TUSB_DESC_ENDPOINT,
-
-  .bEndpointAddress = 0x00,
-  .bmAttributes     = { .xfer = TUSB_XFER_CONTROL },
-  .wMaxPacketSize   = CFG_TUD_ENDPOINT0_SIZE,
-  .bInterval        = 0
-};
-
-static const tusb_desc_endpoint_t ep0IN_desc =
-{
-  .bLength          = sizeof(tusb_desc_endpoint_t),
-  .bDescriptorType  = TUSB_DESC_ENDPOINT,
-
-  .bEndpointAddress = 0x80,
-  .bmAttributes     = { .xfer = TUSB_XFER_CONTROL },
-  .wMaxPacketSize   = CFG_TUD_ENDPOINT0_SIZE,
-  .bInterval        = 0
-};
-
-static void dcd_handle_bus_reset(void)
-{
-  //__IO uint16_t * const epreg = &(EPREG(0));
-  USB->DADDR = 0u; // disable USB peripheral by clearing the EF flag
-
-  for(uint32_t i=0; iDADDR = 0u; // disable USB Function
 
+  for (uint32_t i = 0; i < FSDEV_EP_COUNT; i++) {
     // Clear EP allocation status
     ep_alloc_status[i].ep_num = 0xFF;
     ep_alloc_status[i].ep_type = 0xFF;
@@ -494,47 +269,52 @@ static void dcd_handle_bus_reset(void)
     ep_alloc_status[i].allocated[1] = false;
   }
 
-  dcd_pma_alloc_reset();
-  dcd_edpt_open (0, &ep0OUT_desc);
-  dcd_edpt_open (0, &ep0IN_desc);
+  // Reset PMA allocation
+  ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * FSDEV_EP_COUNT;
 
-  USB->DADDR = USB_DADDR_EF; // Set enable flag, and leaving the device address as zero.
+  edpt0_open(rhport); // open control endpoint (both IN & OUT)
+
+  FSDEV_REG->DADDR = USB_DADDR_EF; // Enable USB Function
 }
 
 // Handle CTR interrupt for the TX/IN direction
-//
-// Upon call, (wIstr & USB_ISTR_DIR) == 0U
-static void dcd_ep_ctr_tx_handler(uint32_t wIstr)
-{
-  uint32_t EPindex = wIstr & USB_ISTR_EP_ID;
-  uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex);
-  uint8_t ep_addr = (wEPRegVal & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK;
+static void handle_ctr_tx(uint32_t ep_id) {
+  uint32_t ep_reg = ep_read(ep_id) & USB_EPREG_MASK;
 
-  // Verify the CTR_TX bit is set. This was in the ST Micro code,
-  // but I'm not sure it's actually necessary?
-  if((wEPRegVal & USB_EP_CTR_TX) == 0U)
-  {
-    return;
+  // Verify the CTR bit is set. This was in the ST Micro code, but I'm not sure it's actually necessary?
+  TU_VERIFY(ep_reg & USB_EP_CTR_TX, );
+
+  uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD;
+  uint8_t ep_addr = (ep_reg & USB_EPADDR_FIELD) | TUSB_DIR_IN_MASK;
+  xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_IN);
+
+  if (ep_is_iso(ep_reg)) {
+    // Ignore spurious interrupts that we don't schedule
+    // host can send IN token while there is no data to send, since ISO does not have NAK
+    // this will result to zero length packet --> trigger interrupt (which cannot be masked)
+    if (!xfer->iso_in_sending) {
+      return;
+    }
+    xfer->iso_in_sending = false;
+    uint8_t buf_id = (ep_reg & USB_EP_DTOG_TX) ? 0 : 1;
+    btable_set_count(ep_id, buf_id, 0);
   }
 
-  /* clear int flag */
-  pcd_clear_tx_ep_ctr(USB, EPindex);
-
-  xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
-  if((xfer->total_len != xfer->queued_len)) /* TX not complete */
-  {
-    dcd_transmit_packet(xfer, EPindex);
-  }
-  else /* TX Complete */
-  {
+  if (xfer->total_len != xfer->queued_len) {
+    dcd_transmit_packet(xfer, ep_id); // also clear CTR bit
+  } else {
     dcd_event_xfer_complete(0, ep_addr, xfer->total_len, XFER_RESULT_SUCCESS, true);
+
+    // Clear CTR TX and reserved CTR RX
+    ep_reg = (ep_reg & ~USB_EP_CTR_TX) | USB_EP_CTR_RX;
+
+    ep_write(ep_id, ep_reg);
   }
 }
 
 // Handle CTR interrupt for the RX/OUT direction
-// Upon call, (wIstr & USB_ISTR_DIR) == 0U
-static void dcd_ep_ctr_rx_handler(uint32_t wIstr) {
-  #ifdef FSDEV_BUS_32BIT
+static void handle_ctr_rx(uint32_t ep_id) {
+#ifdef FSDEV_BUS_32BIT
   /* https://www.st.com/resource/en/errata_sheet/es0561-stm32h503cbebkbrb-device-errata-stmicroelectronics.pdf
    * From STM32H503 errata 2.15.1: Buffer description table update completes after CTR interrupt triggers
    * Description:
@@ -544,200 +324,157 @@ static void dcd_ep_ctr_rx_handler(uint32_t wIstr) {
    * - Software should ensure that a small delay is included before accessing the SRAM contents. This delay
    * should be 800 ns in Full Speed mode and 6.4 μs in Low Speed mode
    * - Since H5 can run up to 250Mhz -> 1 cycle = 4ns. Per errata, we need to wait 200 cycles. Though executing code
-   * also takes time, so we'll wait 40 cycles (count = 20).
+   * also takes time, so we'll wait 60 cycles (count = 20).
    * - Since Low Speed mode is not supported/popular, we will ignore it for now.
    *
    * Note: this errata also seems to apply to G0, U5, H5 etc.
    */
   volatile uint32_t cycle_count = 20; // defined as PCD_RX_PMA_CNT in stm32 hal_driver
   while (cycle_count > 0U) {
-    cycle_count--; // each count take 2 cycle (1 cycle for sub, 1 cycle for compare/jump)
+    cycle_count--; // each count take 3 cycles (1 for sub, jump, and compare)
   }
-  #endif
-
-  uint32_t EPindex = wIstr & USB_ISTR_EP_ID;
-  uint32_t wEPRegVal = pcd_get_endpoint(USB, EPindex);
-  uint8_t ep_addr = wEPRegVal & USB_EPADDR_FIELD;
-
-  xfer_ctl_t *xfer = xfer_ctl_ptr(ep_addr);
-
-  // Verify the CTR_RX bit is set. This was in the ST Micro code,
-  // but I'm not sure it's actually necessary?
-  if((wEPRegVal & USB_EP_CTR_RX) == 0U) {
-    return;
-  }
-
-  if((ep_addr == 0U) && ((wEPRegVal & USB_EP_SETUP) != 0U)) /* Setup packet */
-  {
-    uint32_t count = pcd_get_ep_rx_cnt(USB, EPindex);
-    /* Get SETUP Packet*/
-    if(count == 8) // Setup packet should always be 8 bytes. If not, ignore it, and try again.
-    {
-      // Must reset EP to NAK (in case it had been stalling) (though, maybe too late here)
-      pcd_set_ep_rx_status(USB,0u,USB_EP_RX_NAK);
-      pcd_set_ep_tx_status(USB,0u,USB_EP_TX_NAK);
-#ifdef FSDEV_BUS_32BIT
-      dcd_event_setup_received(0, (uint8_t*)(USB_PMAADDR + pcd_get_ep_rx_address(USB, EPindex)), true);
-#else
-      // The setup_received function uses memcpy, so this must first copy the setup data into
-      // user memory, to allow for the 32-bit access that memcpy performs.
-      uint8_t userMemBuf[8];
-      dcd_read_packet_memory(userMemBuf, pcd_get_ep_rx_address(USB,EPindex), 8);
-      dcd_event_setup_received(0, (uint8_t*)userMemBuf, true);
 #endif
-    }
-  }
-  else
-  {
-    uint32_t count;
-    /* Read from correct register when ISOCHRONOUS (double buffered) */
-    if ( (wEPRegVal & USB_EP_DTOG_RX) && ( (wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) ) {
-      count = pcd_get_ep_tx_cnt(USB, EPindex);
+
+  uint32_t ep_reg = ep_read(ep_id);
+
+  // Verify the CTR bit is set. This was in the ST Micro code, but I'm not sure it's actually necessary?
+  TU_VERIFY(ep_reg & USB_EP_CTR_RX, );
+  ep_reg = (ep_reg & ~USB_EP_CTR_RX) | USB_EP_CTR_TX; // Clear CTR RX and reserved CTR TX
+
+  uint8_t const ep_num = ep_reg & USB_EPADDR_FIELD;
+
+  if (ep_reg & USB_EP_SETUP) {
+    uint32_t count = btable_get_count(ep_id, BTABLE_BUF_RX);
+    // Setup packet should always be 8 bytes. If not, ignore it, and try again.
+    if (count == 8) {
+      uint16_t rx_addr = btable_get_addr(ep_id, BTABLE_BUF_RX);
+      uint32_t setup_packet[2];
+      dcd_read_packet_memory(setup_packet, rx_addr, 8);
+      dcd_event_setup_received(0, (uint8_t*) setup_packet, true);
+
+      // Reset EP to NAK (in case it had been stalling)
+      ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_NAK);
+      ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_NAK);
+
+      ep_reg = ep_add_dtog(ep_reg, TUSB_DIR_IN, 1);
+      ep_reg = ep_add_dtog(ep_reg, TUSB_DIR_OUT, 1);
     } else {
-      count = pcd_get_ep_rx_cnt(USB, EPindex);
+      ep_reg &= USB_EPREG_MASK; // reversed all toggle
     }
+  } else {
+    ep_reg &= USB_EPRX_STAT | USB_EPREG_MASK; // reversed all toggle except RX Status
 
-    TU_ASSERT(count <= xfer->max_packet_size, /**/);
+    bool const is_iso = ep_is_iso(ep_reg);
+    xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, TUSB_DIR_OUT);
 
-    // Clear RX CTR interrupt flag
-    if(ep_addr != 0u)
-    {
-      pcd_clear_rx_ep_ctr(USB, EPindex);
+    uint8_t buf_id;
+    if (is_iso) {
+      buf_id = (ep_reg & USB_EP_DTOG_RX) ? 0 : 1; // ISO are double buffered
+    } else {
+      buf_id = BTABLE_BUF_RX;
     }
+    uint32_t rx_count = btable_get_count(ep_id, buf_id);
+    uint16_t pma_addr = (uint16_t) btable_get_addr(ep_id, buf_id);
 
-    if (count != 0U)
-    {
-      uint16_t addr = pcd_get_ep_rx_address(USB, EPindex);
-
-      if (xfer->ff)
-      {
-        dcd_read_packet_memory_ff(xfer->ff, addr, count);
-      }
-      else
-      {
-        dcd_read_packet_memory(&(xfer->buffer[xfer->queued_len]), addr, count);
-      }
-
-      xfer->queued_len = (uint16_t)(xfer->queued_len + count);
-    }
-
-    if ((count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len))
-    {
-      /* RX COMPLETE */
-      dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true);
-      // Though the host could still send, we don't know.
-      // Does the bulk pipe need to be reset to valid to allow for a ZLP?
-    }
-    else
-    {
-      uint32_t remaining = (uint32_t)xfer->total_len - (uint32_t)xfer->queued_len;
-      if(remaining >= xfer->max_packet_size) {
-        pcd_set_ep_rx_bufsize(USB, EPindex,xfer->max_packet_size);
+    if (rx_count != 0) {
+      if (xfer->ff) {
+        dcd_read_packet_memory_ff(xfer->ff, pma_addr, rx_count);
       } else {
-        pcd_set_ep_rx_bufsize(USB, EPindex,remaining);
+        dcd_read_packet_memory(xfer->buffer + xfer->queued_len, pma_addr, rx_count);
       }
 
-      if (!((wEPRegVal & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS)) {
-        /* Set endpoint active again for receiving more data.
-         * Note that isochronous endpoints stay active always */
-        pcd_set_ep_rx_status(USB, EPindex, USB_EP_RX_VALID);
-      }
+      xfer->queued_len = (uint16_t)(xfer->queued_len + rx_count);
     }
-  }
 
-  // For EP0, prepare to receive another SETUP packet.
-  // Clear CTR last so that a new packet does not overwrite the packing being read.
-  // (Based on the docs, it seems SETUP will always be accepted after CTR is cleared)
-  if(ep_addr == 0u)
-  {
-    // Always be prepared for a status packet...
-    pcd_set_ep_rx_bufsize(USB, EPindex, CFG_TUD_ENDPOINT0_SIZE);
-    pcd_clear_rx_ep_ctr(USB, EPindex);
-  }
-}
+    if ((rx_count < xfer->max_packet_size) || (xfer->queued_len == xfer->total_len)) {
+      uint8_t const ep_addr = ep_num;
+      // all bytes received or short packet
+      dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true);
 
-static void dcd_ep_ctr_handler(void) {
-  uint32_t wIstr;
-
-  /* stay in loop while pending interrupts */
-  while (((wIstr = USB->ISTR) & USB_ISTR_CTR) != 0U) {
-    if ((wIstr & USB_ISTR_DIR) == 0U) {
-      /* TX/IN */
-      dcd_ep_ctr_tx_handler(wIstr);
+      if (ep_num == 0) {
+        // prepared for status packet
+        btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, CFG_TUD_ENDPOINT0_SIZE);
+      }
+      ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_NAK);
     } else {
-      /* RX/OUT*/
-      dcd_ep_ctr_rx_handler(wIstr);
+      // Set endpoint active again for receiving more data. Note that isochronous endpoints stay active always
+      if (!is_iso) {
+        uint16_t const cnt = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
+        btable_set_rx_bufsize(ep_id, BTABLE_BUF_RX, cnt);
+      }
+      ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_VALID);
     }
   }
+
+  ep_write(ep_id, ep_reg);
 }
 
 void dcd_int_handler(uint8_t rhport) {
-
-  (void) rhport;
-
-  uint32_t int_status = USB->ISTR;
-  //const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP
-  //    | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF;
-  // unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ )
+  uint32_t int_status = FSDEV_REG->ISTR;
+  // const uint32_t handled_ints = USB_ISTR_CTR | USB_ISTR_RESET | USB_ISTR_WKUP
+  //     | USB_ISTR_SUSP | USB_ISTR_SOF | USB_ISTR_ESOF;
+  //  unused IRQs: (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_L1REQ )
 
   // The ST driver loops here on the CTR bit, but that loop has been moved into the
   // dcd_ep_ctr_handler(), so less need to loop here. The other interrupts shouldn't
   // be triggered repeatedly.
 
   /* Put SOF flag at the beginning of ISR in case to get least amount of jitter if it is used for timing purposes */
-  if(int_status & USB_ISTR_SOF) {
-    USB->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
-    dcd_event_sof(0, USB->FNR & USB_FNR_FN, true);
+  if (int_status & USB_ISTR_SOF) {
+    FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SOF;
+    dcd_event_sof(0, FSDEV_REG->FNR & USB_FNR_FN, true);
   }
 
-  if(int_status & USB_ISTR_RESET) {
+  if (int_status & USB_ISTR_RESET) {
     // USBRST is start of reset.
-    USB->ISTR = (fsdev_bus_t)~USB_ISTR_RESET;
-    dcd_handle_bus_reset();
+    FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_RESET;
+    handle_bus_reset(rhport);
     dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
     return; // Don't do the rest of the things here; perhaps they've been cleared?
   }
 
-  if (int_status & USB_ISTR_CTR)
-  {
-    /* servicing of the endpoint correct transfer interrupt */
-    /* clear of the CTR flag into the sub */
-    dcd_ep_ctr_handler();
-  }
+  if (int_status & USB_ISTR_WKUP) {
+    FSDEV_REG->CNTR &= ~USB_CNTR_LPMODE;
+    FSDEV_REG->CNTR &= ~USB_CNTR_FSUSP;
 
-  if (int_status & USB_ISTR_WKUP)
-  {
-    USB->CNTR &= ~USB_CNTR_LPMODE;
-    USB->CNTR &= ~USB_CNTR_FSUSP;
-
-    USB->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP;
+    FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_WKUP;
     dcd_event_bus_signal(0, DCD_EVENT_RESUME, true);
   }
 
-  if (int_status & USB_ISTR_SUSP)
-  {
+  if (int_status & USB_ISTR_SUSP) {
     /* Suspend is asserted for both suspend and unplug events. without Vbus monitoring,
      * these events cannot be differentiated, so we only trigger suspend. */
 
     /* Force low-power mode in the macrocell */
-    USB->CNTR |= USB_CNTR_FSUSP;
-    USB->CNTR |= USB_CNTR_LPMODE;
+    FSDEV_REG->CNTR |= USB_CNTR_FSUSP;
+    FSDEV_REG->CNTR |= USB_CNTR_LPMODE;
 
     /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */
-    USB->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP;
+    FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_SUSP;
     dcd_event_bus_signal(0, DCD_EVENT_SUSPEND, true);
   }
 
-  if(int_status & USB_ISTR_ESOF) {
-    if(remoteWakeCountdown == 1u)
-    {
-      USB->CNTR &= ~USB_CNTR_RESUME;
+  if (int_status & USB_ISTR_ESOF) {
+    if (remoteWakeCountdown == 1u) {
+      FSDEV_REG->CNTR &= ~USB_CNTR_RESUME;
     }
-    if(remoteWakeCountdown > 0u)
-    {
+    if (remoteWakeCountdown > 0u) {
       remoteWakeCountdown--;
     }
-    USB->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF;
+    FSDEV_REG->ISTR = (fsdev_bus_t)~USB_ISTR_ESOF;
+  }
+
+  // loop to handle all pending CTR interrupts
+  while (int_status & USB_ISTR_CTR) {
+    uint32_t const ep_id = int_status & USB_ISTR_EP_ID;
+
+    if ((int_status & USB_ISTR_DIR) == 0U) {
+      handle_ctr_tx(ep_id); // TX/IN
+    } else {
+      handle_ctr_rx(ep_id); // RX/OUT or both (RX/TX !!)
+    }
+
+    int_status = FSDEV_REG->ISTR;
   }
 }
 
@@ -747,137 +484,67 @@ void dcd_int_handler(uint8_t rhport) {
 
 // Invoked when a control transfer's status stage is complete.
 // May help DCD to prepare for next control transfer, this API is optional.
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const * request)
-{
-  (void) rhport;
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) {
+  (void)rhport;
 
   if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
       request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
-      request->bRequest == TUSB_REQ_SET_ADDRESS )
-  {
-    uint8_t const dev_addr = (uint8_t) request->wValue;
-
-    // Setting new address after the whole request is complete
-    USB->DADDR &= ~USB_DADDR_ADD;
-    USB->DADDR |= dev_addr; // leave the enable bit set
-  }
-}
-
-static void dcd_pma_alloc_reset(void)
-{
-  open_ep_count = 0;
-  ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT; // 8 bytes per endpoint (two TX and two RX words, each)
-  //TU_LOG2("dcd_pma_alloc_reset()\r\n");
-  for(uint32_t i=0; ipma_alloc_size = 0U;
-    xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_alloc_size = 0U;
-    xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_ptr = 0U;
-    xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_ptr = 0U;
+      request->bRequest == TUSB_REQ_SET_ADDRESS) {
+    uint8_t const dev_addr = (uint8_t)request->wValue;
+    FSDEV_REG->DADDR = (USB_DADDR_EF | dev_addr);
   }
 }
 
 /***
  * Allocate a section of PMA
- *
- * If the EP number has already been allocated, and the new allocation
- * is larger than the old allocation, then this will fail with a TU_ASSERT.
- * (This is done to simplify the code. More complicated algorithms could be used)
- *
+ * In case of double buffering, high 16bit is the address of 2nd buffer
  * During failure, TU_ASSERT is used. If this happens, rework/reallocate memory manually.
  */
-static uint16_t dcd_pma_alloc(uint8_t ep_addr, uint16_t length)
+static uint32_t dcd_pma_alloc(uint16_t len, bool dbuf)
 {
-  xfer_ctl_t* epXferCtl = xfer_ctl_ptr(ep_addr);
+  uint8_t blsize, num_block;
+  uint16_t aligned_len = pma_align_buffer_size(len, &blsize, &num_block);
+  (void) blsize;
+  (void) num_block;
 
-  if(epXferCtl->pma_alloc_size != 0U)
-  {
-    //TU_LOG2("dcd_pma_alloc(%x,%x)=%x (cached)\r\n",ep_addr,length,epXferCtl->pma_ptr);
-    // Previously allocated
-    TU_ASSERT(length <= epXferCtl->pma_alloc_size, 0xFFFF);  // Verify no larger than previous alloc
-    return epXferCtl->pma_ptr;
+  uint32_t addr = ep_buf_ptr;
+  ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer
+
+  if (dbuf) {
+    addr |= ((uint32_t)ep_buf_ptr) << 16;
+    ep_buf_ptr = (uint16_t)(ep_buf_ptr + aligned_len); // increment buffer pointer
   }
 
-  // Ensure allocated buffer is aligned
-#ifdef FSDEV_BUS_32BIT
-  length = (length + 3) & ~0x03;
-#else
-  length = (length + 1) & ~0x01;
-#endif
-
-  open_ep_count++;
-
-  uint16_t addr = ep_buf_ptr;
-  ep_buf_ptr = (uint16_t)(ep_buf_ptr + length); // increment buffer pointer
-
-  // Verify no overflow
+  // Verify packet buffer is not overflowed
   TU_ASSERT(ep_buf_ptr <= FSDEV_PMA_SIZE, 0xFFFF);
 
-  epXferCtl->pma_ptr = addr;
-  epXferCtl->pma_alloc_size = length;
-  //TU_LOG1("dcd_pma_alloc(%x,%x)=%x\r\n",ep_addr,length,addr);
-
   return addr;
 }
 
-/***
- * Free a block of PMA space
- */
-static void dcd_pma_free(uint8_t ep_addr)
-{
-  // Presently, this should never be called for EP0 IN/OUT
-  TU_ASSERT(open_ep_count > 2, /**/);
-  TU_ASSERT(xfer_ctl_ptr(ep_addr)->max_packet_size != 0, /**/);
-  open_ep_count--;
-
-  // If count is 2, only EP0 should be open, so allocations can be mostly reset.
-
-  if(open_ep_count == 2)
-  {
-    ep_buf_ptr = DCD_STM32_BTABLE_BASE + 8*MAX_EP_COUNT + 2*CFG_TUD_ENDPOINT0_SIZE; // 8 bytes per endpoint (two TX and two RX words, each), and EP0
-
-    // Skip EP0
-    for(uint32_t i=1; ipma_alloc_size = 0U;
-      xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_alloc_size = 0U;
-      xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_OUT))->pma_ptr = 0U;
-      xfer_ctl_ptr(tu_edpt_addr(i,TUSB_DIR_IN))->pma_ptr = 0U;
-    }
-  }
-}
-
 /***
  * Allocate hardware endpoint
  */
 static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type)
 {
   uint8_t const epnum = tu_edpt_number(ep_addr);
-  uint8_t const dir   = tu_edpt_dir(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
 
-  for(uint8_t i = 0; i < STFSDEV_EP_COUNT; i++)
-  {
+  for (uint8_t i = 0; i < FSDEV_EP_COUNT; i++) {
     // Check if already allocated
-    if(ep_alloc_status[i].allocated[dir] &&
-       ep_alloc_status[i].ep_type == ep_type &&
-       ep_alloc_status[i].ep_num == epnum)
-    {
+    if (ep_alloc_status[i].allocated[dir] &&
+        ep_alloc_status[i].ep_type == ep_type &&
+        ep_alloc_status[i].ep_num == epnum) {
       return i;
     }
 
     // If EP of current direction is not allocated
     // Except for ISO endpoint, both direction should be free
-    if(!ep_alloc_status[i].allocated[dir] &&
-       (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1]))
-    {
+    if (!ep_alloc_status[i].allocated[dir] &&
+        (ep_type != TUSB_XFER_ISOCHRONOUS || !ep_alloc_status[i].allocated[dir ^ 1])) {
       // Check if EP number is the same
-      if(ep_alloc_status[i].ep_num == 0xFF ||
-         ep_alloc_status[i].ep_num == epnum)
-      {
+      if (ep_alloc_status[i].ep_num == 0xFF || ep_alloc_status[i].ep_num == epnum) {
         // One EP pair has to be the same type
-        if(ep_alloc_status[i].ep_type == 0xFF ||
-           ep_alloc_status[i].ep_type == ep_type)
-        {
+        if (ep_alloc_status[i].ep_type == 0xFF || ep_alloc_status[i].ep_type == ep_type) {
           ep_alloc_status[i].ep_num = epnum;
           ep_alloc_status[i].ep_type = ep_type;
           ep_alloc_status[i].allocated[dir] = true;
@@ -892,469 +559,420 @@ static uint8_t dcd_ep_alloc(uint8_t ep_addr, uint8_t ep_type)
   TU_ASSERT(0);
 }
 
-/***
- * Free hardware endpoint
- */
-static void dcd_ep_free(uint8_t ep_addr)
-{
-  uint8_t const epnum = tu_edpt_number(ep_addr);
-  uint8_t const dir   = tu_edpt_dir(ep_addr);
+void edpt0_open(uint8_t rhport) {
+  (void) rhport;
 
-  for(uint8_t i = 0; i < STFSDEV_EP_COUNT; i++)
-  {
-    // Check if EP number & dir are the same
-    if(ep_alloc_status[i].ep_num == epnum &&
-       ep_alloc_status[i].allocated[dir] == dir)
-    {
-      ep_alloc_status[i].allocated[dir] = false;
-      // Reset entry if ISO endpoint or both direction are free
-      if(ep_alloc_status[i].ep_type == TUSB_XFER_ISOCHRONOUS ||
-         !ep_alloc_status[i].allocated[dir ^ 1])
-      {
-        ep_alloc_status[i].ep_num = 0xFF;
-        ep_alloc_status[i].ep_type = 0xFF;
+  dcd_ep_alloc(0x0, TUSB_XFER_CONTROL);
+  dcd_ep_alloc(0x80, TUSB_XFER_CONTROL);
 
-        return;
-      }
-    }
-  }
+  xfer_status[0][0].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
+  xfer_status[0][0].ep_idx = 0;
+
+  xfer_status[0][1].max_packet_size = CFG_TUD_ENDPOINT0_SIZE;
+  xfer_status[0][1].ep_idx = 0;
+
+  uint16_t pma_addr0 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
+  uint16_t pma_addr1 = dcd_pma_alloc(CFG_TUD_ENDPOINT0_SIZE, false);
+
+  btable_set_addr(0, BTABLE_BUF_RX, pma_addr0);
+  btable_set_addr(0, BTABLE_BUF_TX, pma_addr1);
+
+  uint32_t ep_reg = FSDEV_REG->ep[0].reg & ~USB_EPREG_MASK;
+  ep_reg |= USB_EP_CONTROL;
+  ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_NAK);
+  ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_NAK);
+  // no need to explicitly set DTOG bits since we aren't masked DTOG bit
+
+  // prepare for setup packet
+  btable_set_rx_bufsize(0, BTABLE_BUF_RX, CFG_TUD_ENDPOINT0_SIZE);
+
+  ep_write(0, ep_reg);
 }
 
-// The STM32F0 doesn't seem to like |= or &= to manipulate the EP#R registers,
-// so I'm using the #define from HAL here, instead.
-
-bool dcd_edpt_open (uint8_t rhport, tusb_desc_endpoint_t const * p_endpoint_desc)
-{
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
   (void)rhport;
-  uint8_t const ep_idx = dcd_ep_alloc(p_endpoint_desc->bEndpointAddress, p_endpoint_desc->bmAttributes.xfer);
-  uint8_t const dir   = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
-  const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc);
-  const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size);
-  uint16_t pma_addr;
-  uint32_t wType;
+  uint8_t const ep_addr = desc_ep->bEndpointAddress;
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
+  const uint16_t packet_size = tu_edpt_packet_size(desc_ep);
+  uint8_t const ep_idx = dcd_ep_alloc(ep_addr, desc_ep->bmAttributes.xfer);
+  TU_ASSERT(ep_idx < FSDEV_EP_COUNT);
 
-  TU_ASSERT(ep_idx < STFSDEV_EP_COUNT);
-  TU_ASSERT(buffer_size <= 1024);
+  uint32_t ep_reg = FSDEV_REG->ep[ep_idx].reg & ~USB_EPREG_MASK;
+  ep_reg |= tu_edpt_number(ep_addr) | USB_EP_CTR_RX | USB_EP_CTR_TX;
 
   // Set type
-  switch(p_endpoint_desc->bmAttributes.xfer) {
-  case TUSB_XFER_CONTROL:
-    wType = USB_EP_CONTROL;
-    break;
-  case TUSB_XFER_ISOCHRONOUS:
-    wType = USB_EP_ISOCHRONOUS;
-    break;
-  case TUSB_XFER_BULK:
-    wType = USB_EP_CONTROL;
-    break;
+  switch (desc_ep->bmAttributes.xfer) {
+    case TUSB_XFER_BULK:
+      ep_reg |= USB_EP_BULK;
+      break;
+    case TUSB_XFER_INTERRUPT:
+      ep_reg |= USB_EP_INTERRUPT;
+      break;
 
-  case TUSB_XFER_INTERRUPT:
-    wType = USB_EP_INTERRUPT;
-    break;
-
-  default:
-    TU_ASSERT(false);
+    default:
+      // Note: ISO endpoint should use alloc / active functions
+      TU_ASSERT(false);
   }
 
-  pcd_set_eptype(USB, ep_idx, wType);
-  pcd_set_ep_address(USB, ep_idx, tu_edpt_number(p_endpoint_desc->bEndpointAddress));
-  // Be normal, for now, instead of only accepting zero-byte packets (on control endpoint)
-  // or being double-buffered (bulk endpoints)
-  pcd_clear_ep_kind(USB,0);
+  /* Create a packet memory buffer area. */
+  uint16_t pma_addr = dcd_pma_alloc(packet_size, false);
+  btable_set_addr(ep_idx, dir == TUSB_DIR_IN ? BTABLE_BUF_TX : BTABLE_BUF_RX, pma_addr);
 
-  /* Create a packet memory buffer area. For isochronous endpoints,
-   * use the same buffer as the double buffer, essentially disabling double buffering */
-  pma_addr = dcd_pma_alloc(p_endpoint_desc->bEndpointAddress, buffer_size);
+  xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
+  xfer->max_packet_size = packet_size;
+  xfer->ep_idx = ep_idx;
 
-  if( (dir == TUSB_DIR_IN) || (wType == USB_EP_ISOCHRONOUS) )
-  {
-    pcd_set_ep_tx_address(USB, ep_idx, pma_addr);
-    pcd_set_ep_tx_bufsize(USB, ep_idx, buffer_size);
-    pcd_clear_tx_dtog(USB, ep_idx);
+  ep_reg = ep_add_status(ep_reg, dir, EP_STAT_NAK);
+  ep_reg = ep_add_dtog(ep_reg, dir, 0);
+
+  // reserve other direction toggle bits
+  if (dir == TUSB_DIR_IN) {
+    ep_reg &= ~(USB_EPRX_STAT | USB_EP_DTOG_RX);
+  } else {
+    ep_reg &= ~(USB_EPTX_STAT | USB_EP_DTOG_TX);
   }
 
-  if( (dir == TUSB_DIR_OUT) || (wType == USB_EP_ISOCHRONOUS) )
-  {
-    pcd_set_ep_rx_address(USB, ep_idx, pma_addr);
-    pcd_set_ep_rx_bufsize(USB, ep_idx, buffer_size);
-    pcd_clear_rx_dtog(USB, ep_idx);
-  }
-
-  /* Enable endpoint */
-  if (dir == TUSB_DIR_IN)
-  {
-    if(wType == USB_EP_ISOCHRONOUS) {
-      pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS);
-    } else {
-      pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK);
-    }
-  } else
-  {
-    if(wType == USB_EP_ISOCHRONOUS) {
-      pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS);
-    } else {
-      pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK);
-    }
-  }
-
-  xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->max_packet_size = packet_size;
-  xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->ep_idx = ep_idx;
+  ep_write(ep_idx, ep_reg);
 
   return true;
 }
 
-void dcd_edpt_close_all (uint8_t rhport)
-{
-  (void) rhport;
-  // TODO implement dcd_edpt_close_all()
-}
-
-/**
- * Close an endpoint.
- *
- * This function may be called with interrupts enabled or disabled.
- *
- * This also clears transfers in progress, should there be any.
- */
-void dcd_edpt_close (uint8_t rhport, uint8_t ep_addr)
+void dcd_edpt_close_all(uint8_t rhport)
 {
   (void)rhport;
 
-  xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
-  uint8_t const ep_idx = xfer->ep_idx;
-  uint8_t const dir    = tu_edpt_dir(ep_addr);
-
-  if(dir == TUSB_DIR_IN)
-  {
-    pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS);
-  }
-  else
-  {
-    pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS);
+  for (uint32_t i = 1; i < FSDEV_EP_COUNT; i++) {
+    // Reset endpoint
+    ep_write(i, 0);
+    // Clear EP allocation status
+    ep_alloc_status[i].ep_num = 0xFF;
+    ep_alloc_status[i].ep_type = 0xFF;
+    ep_alloc_status[i].allocated[0] = false;
+    ep_alloc_status[i].allocated[1] = false;
   }
 
-  dcd_ep_free(ep_addr);
-
-  dcd_pma_free(ep_addr);
+  // Reset PMA allocation
+  ep_buf_ptr = FSDEV_BTABLE_BASE + 8 * CFG_TUD_ENDPPOINT_MAX + 2 * CFG_TUD_ENDPOINT0_SIZE;
 }
 
-bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size)
-{
+bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
   (void)rhport;
 
-  TU_ASSERT(largest_packet_size <= 1024);
-
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
   uint8_t const ep_idx = dcd_ep_alloc(ep_addr, TUSB_XFER_ISOCHRONOUS);
-  const uint16_t buffer_size = pcd_aligned_buffer_size(largest_packet_size);
 
-  /* Create a packet memory buffer area. For isochronous endpoints,
-   * use the same buffer as the double buffer, essentially disabling double buffering */
-  uint16_t pma_addr = dcd_pma_alloc(ep_addr, buffer_size);
+  /* Create a packet memory buffer area. Enable double buffering for devices with 2048 bytes PMA,
+     for smaller devices double buffering occupy too much space. */
+  uint32_t pma_addr = dcd_pma_alloc(largest_packet_size, true);
+#if FSDEV_PMA_SIZE > 1024u
+  uint16_t pma_addr2 = pma_addr >> 16;
+#else
+  uint16_t pma_addr2 = pma_addr;
+#endif
 
-  xfer_ctl_ptr(ep_addr)->ep_idx = ep_idx;
+  btable_set_addr(ep_idx, 0, pma_addr);
+  btable_set_addr(ep_idx, 1, pma_addr2);
 
-  pcd_set_eptype(USB, ep_idx, USB_EP_ISOCHRONOUS);
-
-  pcd_set_ep_tx_address(USB, ep_idx, pma_addr);
-  pcd_set_ep_rx_address(USB, ep_idx, pma_addr);
+  xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir);
+  xfer->ep_idx = ep_idx;
 
   return true;
 }
 
-bool dcd_edpt_iso_activate(uint8_t rhport,  tusb_desc_endpoint_t const * p_endpoint_desc)
-{
+bool dcd_edpt_iso_activate(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep) {
   (void)rhport;
-  uint8_t const ep_idx = xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->ep_idx;
-  uint8_t const dir    = tu_edpt_dir(p_endpoint_desc->bEndpointAddress);
-  const uint16_t packet_size = tu_edpt_packet_size(p_endpoint_desc);
-  const uint16_t buffer_size = pcd_aligned_buffer_size(packet_size);
+  uint8_t const ep_addr = desc_ep->bEndpointAddress;
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
+  xfer_ctl_t* xfer = xfer_ctl_ptr(ep_num, dir);
 
-  /* Disable endpoint */
-  if(dir == TUSB_DIR_IN)
-  {
-    pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_DIS);
-  }
-  else
-  {
-    pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_DIS);
-  }
+  uint8_t const ep_idx = xfer->ep_idx;
 
-  pcd_set_ep_address(USB, ep_idx, tu_edpt_number(p_endpoint_desc->bEndpointAddress));
-  // Be normal, for now, instead of only accepting zero-byte packets (on control endpoint)
-  // or being double-buffered (bulk endpoints)
-  pcd_clear_ep_kind(USB,0);
+  xfer->max_packet_size = tu_edpt_packet_size(desc_ep);
 
-  pcd_set_ep_tx_bufsize(USB, ep_idx, buffer_size);
-  pcd_set_ep_rx_bufsize(USB, ep_idx, buffer_size);
-  pcd_clear_tx_dtog(USB, ep_idx);
-  pcd_clear_rx_dtog(USB, ep_idx);
+  uint32_t ep_reg = FSDEV_REG->ep[0].reg & ~USB_EPREG_MASK;
+  ep_reg |= tu_edpt_number(ep_addr) | USB_EP_ISOCHRONOUS | USB_EP_CTR_RX | USB_EP_CTR_TX;
+  ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_DISABLED);
+  ep_reg = ep_add_status(ep_reg, TUSB_DIR_OUT, EP_STAT_DISABLED);
+  ep_reg = ep_add_dtog(ep_reg, dir, 0);
+  ep_reg = ep_add_dtog(ep_reg, 1-dir, 1);
 
-  xfer_ctl_ptr(p_endpoint_desc->bEndpointAddress)->max_packet_size = packet_size;
+  ep_write(ep_idx, ep_reg);
 
   return true;
 }
 
 // Currently, single-buffered, and only 64 bytes at a time (max)
+static void dcd_transmit_packet(xfer_ctl_t *xfer, uint16_t ep_ix) {
+  uint16_t len = tu_min16(xfer->total_len - xfer->queued_len, xfer->max_packet_size);
+  uint16_t ep_reg = ep_read(ep_ix) | EP_CTR_TXRX;
+  bool const is_iso = ep_is_iso(ep_reg);
 
-static void dcd_transmit_packet(xfer_ctl_t * xfer, uint16_t ep_ix)
-{
-  uint16_t len = (uint16_t)(xfer->total_len - xfer->queued_len);
-
-  if(len > xfer->max_packet_size) // max packet size for FS transfer
-  {
-    len = xfer->max_packet_size;
+  uint8_t buf_id;
+  if (is_iso) {
+    buf_id = (ep_reg & USB_EP_DTOG_TX) ? 1 : 0;
+  } else {
+    buf_id = BTABLE_BUF_TX;
   }
+  uint16_t addr_ptr = (uint16_t) btable_get_addr(ep_ix, buf_id);
+  btable_set_count(ep_ix, buf_id, len);
 
-  uint16_t ep_reg = pcd_get_endpoint(USB, ep_ix);
-  uint16_t addr_ptr = pcd_get_ep_tx_address(USB, ep_ix);
-
-  if (xfer->ff)
-  {
+  if (xfer->ff) {
     dcd_write_packet_memory_ff(xfer->ff, addr_ptr, len);
-  }
-  else
-  {
+  } else {
     dcd_write_packet_memory(addr_ptr, &(xfer->buffer[xfer->queued_len]), len);
   }
   xfer->queued_len = (uint16_t)(xfer->queued_len + len);
 
-  /* Write into correct register when ISOCHRONOUS (double buffered) */
-  if ( (ep_reg & USB_EP_DTOG_TX) && ( (ep_reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS) ) {
-    pcd_set_ep_rx_cnt(USB, ep_ix, len);
-  } else {
-    pcd_set_ep_tx_cnt(USB, ep_ix, len);
-  }
+  ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(TUSB_DIR_IN);
+  ep_reg = ep_add_status(ep_reg, TUSB_DIR_IN, EP_STAT_VALID);
+  ep_reg = ep_clear_ctr(ep_reg, TUSB_DIR_IN);
 
-  pcd_set_ep_tx_status(USB, ep_ix, USB_EP_TX_VALID);
+  dcd_int_disable(0);
+  ep_write(ep_ix, ep_reg);
+  if (is_iso) {
+    xfer->iso_in_sending = true;
+  }
+  dcd_int_enable(0);
 }
 
-bool dcd_edpt_xfer (uint8_t rhport, uint8_t ep_addr, uint8_t * buffer, uint16_t total_bytes)
-{
+static bool edpt_xfer(uint8_t rhport, uint8_t ep_num, uint8_t dir) {
   (void) rhport;
 
-  xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
+  xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
   uint8_t const ep_idx = xfer->ep_idx;
-  uint8_t const dir    = tu_edpt_dir(ep_addr);
+
+  if (dir == TUSB_DIR_IN) {
+    dcd_transmit_packet(xfer, ep_idx);
+  } else {
+    uint32_t cnt = (uint32_t) tu_min16(xfer->total_len, xfer->max_packet_size);
+    uint16_t ep_reg = ep_read(ep_idx) | USB_EP_CTR_TX;
+    ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir); // keep CTR TX, clear CTR RX
+    ep_reg = ep_add_status(ep_reg, dir, EP_STAT_VALID);
+
+    if (ep_is_iso(ep_reg)) {
+      btable_set_rx_bufsize(ep_idx, 0, cnt);
+      btable_set_rx_bufsize(ep_idx, 1, cnt);
+    } else {
+      btable_set_rx_bufsize(ep_idx, BTABLE_BUF_RX, cnt);
+    }
+
+    ep_write(ep_idx, ep_reg);
+  }
+
+  return true;
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) {
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
+  xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
 
   xfer->buffer = buffer;
-  xfer->ff     = NULL;
+  xfer->ff = NULL;
   xfer->total_len = total_bytes;
   xfer->queued_len = 0;
 
-  if ( dir == TUSB_DIR_OUT )
-  {
-    // A setup token can occur immediately after an OUT STATUS packet so make sure we have a valid
-    // buffer for the control endpoint.
-    if (ep_idx == 0 && buffer == NULL)
-    {
-        xfer->buffer = (uint8_t*)_setup_packet;
-    }
-
-    if(total_bytes > xfer->max_packet_size)
-    {
-      pcd_set_ep_rx_bufsize(USB,ep_idx,xfer->max_packet_size);
-    } else {
-      pcd_set_ep_rx_bufsize(USB,ep_idx,total_bytes);
-    }
-    pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_VALID);
-  }
-  else // IN
-  {
-    dcd_transmit_packet(xfer,ep_idx);
-  }
-  return true;
+  return edpt_xfer(rhport, ep_num, dir);
 }
 
-bool dcd_edpt_xfer_fifo (uint8_t rhport, uint8_t ep_addr, tu_fifo_t * ff, uint16_t total_bytes)
-{
-  (void) rhport;
-
-  xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
-  uint8_t const epnum = xfer->ep_idx;
-  uint8_t const dir   = tu_edpt_dir(ep_addr);
+bool dcd_edpt_xfer_fifo(uint8_t rhport, uint8_t ep_addr, tu_fifo_t *ff, uint16_t total_bytes) {
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
+  xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
 
   xfer->buffer = NULL;
-  xfer->ff     = ff;
+  xfer->ff = ff;
   xfer->total_len = total_bytes;
   xfer->queued_len = 0;
 
-  if ( dir == TUSB_DIR_OUT )
-  {
-    if(total_bytes > xfer->max_packet_size)
-    {
-      pcd_set_ep_rx_bufsize(USB,epnum,xfer->max_packet_size);
-    } else {
-      pcd_set_ep_rx_bufsize(USB,epnum,total_bytes);
-    }
-    pcd_set_ep_rx_status(USB, epnum, USB_EP_RX_VALID);
-  }
-  else // IN
-  {
-    dcd_transmit_packet(xfer,epnum);
-  }
-  return true;
+  return edpt_xfer(rhport, ep_num, dir);
 }
 
-void dcd_edpt_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
   (void)rhport;
-
-  xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
+  xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
   uint8_t const ep_idx = xfer->ep_idx;
-  uint8_t const dir    = tu_edpt_dir(ep_addr);
 
-  if (dir == TUSB_DIR_IN)
-  { // IN
-    pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_STALL);
-  }
-  else
-  { // OUT
-    pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_STALL);
-  }
+  uint32_t ep_reg = ep_read(ep_idx);
+  ep_reg |= USB_EP_CTR_RX | USB_EP_CTR_TX; // reserve CTR bits
+  ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir);
+  ep_reg = ep_add_status(ep_reg, dir, EP_STAT_STALL);
+
+  ep_write(ep_idx, ep_reg);
 }
 
-void dcd_edpt_clear_stall (uint8_t rhport, uint8_t ep_addr)
-{
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
   (void)rhport;
 
-  xfer_ctl_t * xfer = xfer_ctl_ptr(ep_addr);
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  uint8_t const dir = tu_edpt_dir(ep_addr);
+  xfer_ctl_t *xfer = xfer_ctl_ptr(ep_num, dir);
   uint8_t const ep_idx = xfer->ep_idx;
-  uint8_t const dir    = tu_edpt_dir(ep_addr);
 
-  if (dir == TUSB_DIR_IN)
-  { // IN
-    if (pcd_get_eptype(USB, ep_idx) !=  USB_EP_ISOCHRONOUS) {
-      pcd_set_ep_tx_status(USB, ep_idx, USB_EP_TX_NAK);
-    }
+  uint32_t ep_reg = ep_read(ep_idx) | EP_CTR_TXRX;
+  ep_reg &= USB_EPREG_MASK | EP_STAT_MASK(dir) | EP_DTOG_MASK(dir);
 
-    /* Reset to DATA0 if clearing stall condition. */
-    pcd_clear_tx_dtog(USB, ep_idx);
-  }
-  else
-  { // OUT
-    if (pcd_get_eptype(USB, ep_idx) !=  USB_EP_ISOCHRONOUS) {
-      pcd_set_ep_rx_status(USB, ep_idx, USB_EP_RX_NAK);
-    }
-    /* Reset to DATA0 if clearing stall condition. */
-    pcd_clear_rx_dtog(USB, ep_idx);
+  if (!ep_is_iso(ep_reg)) {
+    ep_reg = ep_add_status(ep_reg, dir, EP_STAT_NAK);
   }
+  ep_reg = ep_add_dtog(ep_reg, dir, 0); // Reset to DATA0
+
+  ep_write(ep_idx, ep_reg);
 }
 
 #ifdef FSDEV_BUS_32BIT
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes)
-{
-  const uint8_t* srcVal = src;
-  volatile uint32_t* dst32 = (volatile uint32_t*)(USB_PMAADDR + dst);
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes) {
+  const uint8_t *src8 = src;
+  volatile uint32_t *pma32 = (volatile uint32_t *)(USB_PMAADDR + dst);
 
   for (uint32_t n = wNBytes / 4; n > 0; --n) {
-    *dst32++ = tu_unaligned_read32(srcVal);
-    srcVal += 4;
+    *pma32++ = tu_unaligned_read32(src8);
+    src8 += 4;
   }
 
-  wNBytes = wNBytes & 0x03;
-  if (wNBytes)
-  {
-    uint32_t wrVal = *srcVal;
-    wNBytes--;
+  uint16_t odd = wNBytes & 0x03;
+  if (odd) {
+    uint32_t wrVal = *src8;
+    odd--;
 
-    if (wNBytes)
-    {
-      wrVal |= *++srcVal << 8;
-      wNBytes--;
+    if (odd) {
+      wrVal |= *++src8 << 8;
+      odd--;
 
-      if (wNBytes)
-      {
-        wrVal |= *++srcVal << 16;
+      if (odd) {
+        wrVal |= *++src8 << 16;
       }
     }
 
-    *dst32 = wrVal;
+    *pma32 = wrVal;
   }
 
   return true;
 }
+
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes) {
+  uint8_t *dst8 = dst;
+  volatile uint32_t *src32 = (volatile uint32_t *)(USB_PMAADDR + src);
+
+  for (uint32_t n = wNBytes / 4; n > 0; --n) {
+    tu_unaligned_write32(dst8, *src32++);
+    dst8 += 4;
+  }
+
+  uint16_t odd = wNBytes & 0x03;
+  if (odd) {
+    uint32_t rdVal = *src32;
+
+    *dst8 = tu_u32_byte0(rdVal);
+    odd--;
+
+    if (odd) {
+      *++dst8 = tu_u32_byte1(rdVal);
+      odd--;
+
+      if (odd) {
+        *++dst8 = tu_u32_byte2(rdVal);
+      }
+    }
+  }
+
+  return true;
+}
+
 #else
 // Packet buffer access can only be 8- or 16-bit.
 /**
-  * @brief Copy a buffer from user memory area to packet memory area (PMA).
-  *        This uses byte-access for user memory (so support non-aligned buffers)
-  *        and 16-bit access for packet memory.
-  * @param   dst, byte address in PMA; must be 16-bit aligned
-  * @param   src pointer to user memory area.
-  * @param   wPMABufAddr address into PMA.
-  * @param   wNBytes no. of bytes to be copied.
-  * @retval None
-  */
-static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t wNBytes)
-{
-  uint32_t n = (uint32_t)wNBytes >> 1U;
-  uint16_t temp1, temp2;
-  const uint8_t * srcVal;
+ * @brief Copy a buffer from user memory area to packet memory area (PMA).
+ *        This uses un-aligned for user memory and 16-bit access for packet memory.
+ * @param   dst, byte address in PMA; must be 16-bit aligned
+ * @param   src pointer to user memory area.
+ * @param   wPMABufAddr address into PMA.
+ * @param   nbytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_write_packet_memory(uint16_t dst, const void *__restrict src, uint16_t nbytes) {
+  uint32_t n16 = (uint32_t)nbytes >> 1U;
+  const uint8_t *src8 = src;
+  fsdev_pma16_t* pma16 = (fsdev_pma16_t*) (USB_PMAADDR + FSDEV_PMA_STRIDE * dst);
 
-  // The GCC optimizer will combine access to 32-bit sizes if we let it. Force
-  // it volatile so that it won't do that.
-  __IO uint16_t *pdwVal;
-
-  srcVal = src;
-  pdwVal = &pma[FSDEV_PMA_STRIDE * (dst >> 1)];
-
-  while (n--)
-  {
-    temp1 = (uint16_t)*srcVal;
-    srcVal++;
-    temp2 = temp1 | ((uint16_t)(((uint16_t)(*srcVal)) << 8U)) ;
-    *pdwVal = temp2;
-    pdwVal += FSDEV_PMA_STRIDE;
-    srcVal++;
+  while (n16--) {
+    pma16->u16 = tu_unaligned_read16(src8);
+    src8 += 2;
+    pma16++;
   }
 
-  if (wNBytes)
-  {
-    temp1 = *srcVal;
-    *pdwVal = temp1;
+  if (nbytes & 0x01) {
+    pma16->u16 = (uint16_t) *src8;
   }
 
   return true;
 }
+
+/**
+ * @brief Copy a buffer from packet memory area (PMA) to user memory area.
+ *        Uses unaligned for system memory and 16-bit access of packet memory
+ * @param   nbytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t nbytes) {
+  uint32_t n16 = (uint32_t)nbytes >> 1U;
+  fsdev_pma16_t* pma16 = (fsdev_pma16_t*) (USB_PMAADDR + FSDEV_PMA_STRIDE * src);
+  uint8_t *dst8 = (uint8_t *)dst;
+
+  while (n16--) {
+    uint16_t temp16 = pma16->u16;
+    tu_unaligned_write16(dst8, temp16);
+    dst8 += 2;
+    pma16++;
+  }
+
+  if (nbytes & 0x01) {
+    *dst8++ = tu_u16_low(pma16->u16);
+  }
+
+  return true;
+}
+
 #endif
 
 /**
-  * @brief Copy from FIFO to packet memory area (PMA).
-  *        Uses byte-access of system memory and 16-bit access of packet memory
-  * @param   wNBytes no. of bytes to be copied.
-  * @retval None
-  */
-static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wNBytes)
-{
+ * @brief Copy from FIFO to packet memory area (PMA).
+ *        Uses byte-access of system memory and 16-bit access of packet memory
+ * @param   wNBytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_write_packet_memory_ff(tu_fifo_t *ff, uint16_t dst, uint16_t wNBytes) {
   // Since we copy from a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
   tu_fifo_buffer_info_t info;
   tu_fifo_get_read_info(ff, &info);
 
-  uint16_t cnt_lin =  TU_MIN(wNBytes, info.len_lin);
+  uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin);
   uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap);
 
   // We want to read from the FIFO and write it into the PMA, if LIN part is ODD and has WRAPPED part,
   // last lin byte will be combined with wrapped part
   // To ensure PMA is always access aligned (dst aligned to 16 or 32 bit)
 #ifdef FSDEV_BUS_32BIT
-  if((cnt_lin & 0x03) && cnt_wrap)
-  {
+  if ((cnt_lin & 0x03) && cnt_wrap) {
     // Copy first linear part
-    dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin &~0x03);
-    dst += cnt_lin &~0x03;
+    dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x03);
+    dst += cnt_lin & ~0x03;
 
     // Copy last linear bytes & first wrapped bytes to buffer
     uint32_t i;
     uint8_t tmp[4];
-    for (i = 0; i < (cnt_lin & 0x03); i++)
-    {
-      tmp[i] = ((uint8_t*)info.ptr_lin)[(cnt_lin &~0x03) + i];
+    for (i = 0; i < (cnt_lin & 0x03); i++) {
+      tmp[i] = ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i];
     }
     uint32_t wCnt = cnt_wrap;
-    for (; i < 4 && wCnt > 0; i++, wCnt--)
-    {
-      tmp[i] = *(uint8_t*)info.ptr_wrap;
-      info.ptr_wrap = (uint8_t*)info.ptr_wrap + 1;
+    for (; i < 4 && wCnt > 0; i++, wCnt--) {
+      tmp[i] = *(uint8_t *)info.ptr_wrap;
+      info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1;
     }
 
     // Write unaligned buffer
@@ -1362,33 +980,31 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
     dst += 4;
 
     // Copy rest of wrapped byte
-    if (wCnt)
-        dcd_write_packet_memory(dst, info.ptr_wrap, wCnt);
+    if (wCnt) {
+      dcd_write_packet_memory(dst, info.ptr_wrap, wCnt);
+    }
   }
 #else
-  if((cnt_lin & 0x01) && cnt_wrap)
-  {
+  if ((cnt_lin & 0x01) && cnt_wrap) {
     // Copy first linear part
-    dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin &~0x01);
-    dst += cnt_lin &~0x01;
+    dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin & ~0x01);
+    dst += cnt_lin & ~0x01;
 
     // Copy last linear byte & first wrapped byte
-    uint16_t tmp = ((uint8_t*)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t*)info.ptr_wrap)[0]) << 8U);
+    uint16_t tmp = ((uint8_t *)info.ptr_lin)[cnt_lin - 1] | ((uint16_t)(((uint8_t *)info.ptr_wrap)[0]) << 8U);
     dcd_write_packet_memory(dst, &tmp, 2);
     dst += 2;
 
     // Copy rest of wrapped byte
-    dcd_write_packet_memory(dst, ((uint8_t*)info.ptr_wrap) + 1, cnt_wrap - 1);
+    dcd_write_packet_memory(dst, ((uint8_t *)info.ptr_wrap) + 1, cnt_wrap - 1);
   }
 #endif
-  else
-  {
+  else {
     // Copy linear part
     dcd_write_packet_memory(dst, info.ptr_lin, cnt_lin);
     dst += info.len_lin;
 
-    if(info.len_wrap)
-    {
+    if (info.len_wrap) {
       // Copy wrapped byte
       dcd_write_packet_memory(dst, info.ptr_wrap, cnt_wrap);
     }
@@ -1399,101 +1015,29 @@ static bool dcd_write_packet_memory_ff(tu_fifo_t * ff, uint16_t dst, uint16_t wN
   return true;
 }
 
-#ifdef FSDEV_BUS_32BIT
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes)
-{
-  uint8_t* dstVal = dst;
-  volatile uint32_t* src32 = (volatile uint32_t*)(USB_PMAADDR + src);
-
-  for (uint32_t n = wNBytes / 4; n > 0; --n) {
-    tu_unaligned_write32(dstVal, *src32++);
-    dstVal += 4;
-  }
-
-  wNBytes = wNBytes & 0x03;
-  if (wNBytes)
-  {
-    uint32_t rdVal = *src32;
-
-    *dstVal = tu_u32_byte0(rdVal);
-    wNBytes--;
-
-    if (wNBytes)
-    {
-      *++dstVal = tu_u32_byte1(rdVal);
-      wNBytes--;
-
-      if (wNBytes)
-      {
-        *++dstVal = tu_u32_byte2(rdVal);
-      }
-    }
-  }
-
-  return true;
-}
-#else
 /**
-  * @brief Copy a buffer from packet memory area (PMA) to user memory area.
-  *        Uses byte-access of system memory and 16-bit access of packet memory
-  * @param   wNBytes no. of bytes to be copied.
-  * @retval None
-  */
-static bool dcd_read_packet_memory(void *__restrict dst, uint16_t src, uint16_t wNBytes)
-{
-  uint32_t n = (uint32_t)wNBytes >> 1U;
-  // The GCC optimizer will combine access to 32-bit sizes if we let it. Force
-  // it volatile so that it won't do that.
-  __IO const uint16_t *pdwVal;
-  uint32_t temp;
-
-  pdwVal = &pma[FSDEV_PMA_STRIDE * (src >> 1)];
-  uint8_t *dstVal = (uint8_t*)dst;
-
-  while (n--)
-  {
-    temp = *pdwVal;
-    pdwVal += FSDEV_PMA_STRIDE;
-    *dstVal++ = ((temp >> 0) & 0xFF);
-    *dstVal++ = ((temp >> 8) & 0xFF);
-  }
-
-  if (wNBytes & 0x01)
-  {
-    temp = *pdwVal;
-    pdwVal += FSDEV_PMA_STRIDE;
-    *dstVal++ = ((temp >> 0) & 0xFF);
-  }
-  return true;
-}
-#endif
-
-/**
-  * @brief Copy a buffer from user packet memory area (PMA) to FIFO.
-  *        Uses byte-access of system memory and 16-bit access of packet memory
-  * @param   wNBytes no. of bytes to be copied.
-  * @retval None
-  */
-static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNBytes)
-{
+ * @brief Copy a buffer from user packet memory area (PMA) to FIFO.
+ *        Uses byte-access of system memory and 16-bit access of packet memory
+ * @param   wNBytes no. of bytes to be copied.
+ * @retval None
+ */
+static bool dcd_read_packet_memory_ff(tu_fifo_t *ff, uint16_t src, uint16_t wNBytes) {
   // Since we copy into a ring buffer FIFO, a wrap might occur making it necessary to conduct two copies
   // Check for first linear part
   tu_fifo_buffer_info_t info;
-  tu_fifo_get_write_info(ff, &info);  // We want to read from the FIFO
+  tu_fifo_get_write_info(ff, &info); // We want to read from the FIFO
 
-  uint16_t cnt_lin =  TU_MIN(wNBytes, info.len_lin);
+  uint16_t cnt_lin = TU_MIN(wNBytes, info.len_lin);
   uint16_t cnt_wrap = TU_MIN(wNBytes - cnt_lin, info.len_wrap);
 
-
   // We want to read from PMA and write it into the FIFO, if LIN part is ODD and has WRAPPED part,
   // last lin byte will be combined with wrapped part
   // To ensure PMA is always access aligned (src aligned to 16 or 32 bit)
 #ifdef FSDEV_BUS_32BIT
-  if((cnt_lin & 0x03) && cnt_wrap)
-  {
+  if ((cnt_lin & 0x03) && cnt_wrap) {
     // Copy first linear part
-    dcd_read_packet_memory(info.ptr_lin, src, cnt_lin &~0x03);
-    src += cnt_lin &~0x03;
+    dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x03);
+    src += cnt_lin & ~0x03;
 
     // Copy last linear bytes & first wrapped bytes
     uint8_t tmp[4];
@@ -1501,48 +1045,44 @@ static bool dcd_read_packet_memory_ff(tu_fifo_t * ff, uint16_t src, uint16_t wNB
     src += 4;
 
     uint32_t i;
-    for (i = 0; i < (cnt_lin & 0x03); i++)
-    {
-      ((uint8_t*)info.ptr_lin)[(cnt_lin &~0x03) + i] = tmp[i];
+    for (i = 0; i < (cnt_lin & 0x03); i++) {
+      ((uint8_t *)info.ptr_lin)[(cnt_lin & ~0x03) + i] = tmp[i];
     }
     uint32_t wCnt = cnt_wrap;
-    for (; i < 4 && wCnt > 0; i++, wCnt--)
-    {
-      *(uint8_t*)info.ptr_wrap = tmp[i];
-      info.ptr_wrap = (uint8_t*)info.ptr_wrap + 1;
+    for (; i < 4 && wCnt > 0; i++, wCnt--) {
+      *(uint8_t *)info.ptr_wrap = tmp[i];
+      info.ptr_wrap = (uint8_t *)info.ptr_wrap + 1;
     }
 
     // Copy rest of wrapped byte
-    if (wCnt)
+    if (wCnt) {
       dcd_read_packet_memory(info.ptr_wrap, src, wCnt);
+    }
   }
 #else
-  if((cnt_lin & 0x01) && cnt_wrap)
-  {
+  if ((cnt_lin & 0x01) && cnt_wrap) {
     // Copy first linear part
-    dcd_read_packet_memory(info.ptr_lin, src, cnt_lin &~0x01);
-    src += cnt_lin &~0x01;
+    dcd_read_packet_memory(info.ptr_lin, src, cnt_lin & ~0x01);
+    src += cnt_lin & ~0x01;
 
     // Copy last linear byte & first wrapped byte
     uint8_t tmp[2];
     dcd_read_packet_memory(tmp, src, 2);
     src += 2;
 
-    ((uint8_t*)info.ptr_lin)[cnt_lin - 1] = tmp[0];
-    ((uint8_t*)info.ptr_wrap)[0] = tmp[1];
+    ((uint8_t *)info.ptr_lin)[cnt_lin - 1] = tmp[0];
+    ((uint8_t *)info.ptr_wrap)[0] = tmp[1];
 
     // Copy rest of wrapped byte
-    dcd_read_packet_memory(((uint8_t*)info.ptr_wrap) + 1, src, cnt_wrap - 1);
+    dcd_read_packet_memory(((uint8_t *)info.ptr_wrap) + 1, src, cnt_wrap - 1);
   }
 #endif
-  else
-  {
+  else {
     // Copy linear part
     dcd_read_packet_memory(info.ptr_lin, src, cnt_lin);
     src += cnt_lin;
 
-    if(info.len_wrap)
-    {
+    if (info.len_wrap) {
       // Copy wrapped byte
       dcd_read_packet_memory(info.ptr_wrap, src, cnt_wrap);
     }
diff --git a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h b/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h
deleted file mode 100644
index 946ad2c7c..000000000
--- a/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h
+++ /dev/null
@@ -1,538 +0,0 @@
-/*
- * Copyright(c) 2016 STMicroelectronics
- * Copyright(c) N Conrad
- * Copyright (c) 2019 Ha Thach (tinyusb.org)
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *   1. Redistributions of source code must retain the above copyright notice,
- *      this list of conditions and the following disclaimer.
- *   2. Redistributions in binary form must reproduce the above copyright notice,
- *      this list of conditions and the following disclaimer in the documentation
- *      and/or other materials provided with the distribution.
- *   3. Neither the name of STMicroelectronics nor the names of its contributors
- *      may be used to endorse or promote products derived from this software
- *      without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
- * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
- * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-// This file contains source copied from ST's HAL, and thus should have their copyright statement.
-
-// FSDEV_PMA_SIZE is PMA buffer size in bytes.
-// On 512-byte devices, access with a stride of two words (use every other 16-bit address)
-// On 1024-byte devices, access with a stride of one word (use every 16-bit address)
-
-#ifndef PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_
-#define PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_
-
-#if CFG_TUSB_MCU == OPT_MCU_STM32F0
-  #include "stm32f0xx.h"
-  #define FSDEV_PMA_SIZE (1024u)
-  // F0x2 models are crystal-less
-  // All have internal D+ pull-up
-  // 070RB:    2 x 16 bits/word memory     LPM Support, BCD Support
-  // PMA dedicated to USB (no sharing with CAN)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
-  #include "stm32f1xx.h"
-  #define FSDEV_PMA_SIZE (512u)
-  // NO internal Pull-ups
-  //         *B, and *C:    2 x 16 bits/word
-
-  // F1 names this differently from the rest
-  #define USB_CNTR_LPMODE   USB_CNTR_LP_MODE
-
-#elif defined(STM32F302xB) || defined(STM32F302xC) || \
-      defined(STM32F303xB) || defined(STM32F303xC) || \
-      defined(STM32F373xC)
-  #include "stm32f3xx.h"
-  #define FSDEV_PMA_SIZE (512u)
-  // NO internal Pull-ups
-  //         *B, and *C:    1 x 16 bits/word
-  // PMA dedicated to USB (no sharing with CAN)
-
-#elif defined(STM32F302x6) || defined(STM32F302x8) || \
-      defined(STM32F302xD) || defined(STM32F302xE) || \
-      defined(STM32F303xD) || defined(STM32F303xE)
-  #include "stm32f3xx.h"
-  #define FSDEV_PMA_SIZE (1024u)
-  // NO internal Pull-ups
-  // *6, *8, *D, and *E:    2 x 16 bits/word     LPM Support
-  // When CAN clock is enabled, USB can use first 768 bytes ONLY.
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
-  #include "stm32l0xx.h"
-  #define FSDEV_PMA_SIZE (1024u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
-  #include "stm32l1xx.h"
-  #define FSDEV_PMA_SIZE (512u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
-  #include "stm32g4xx.h"
-  #define FSDEV_PMA_SIZE (1024u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
-  #include "stm32g0xx.h"
-  #define FSDEV_BUS_32BIT
-  #define FSDEV_PMA_SIZE (2048u)
-  #undef USB_PMAADDR
-  #define USB_PMAADDR USB_DRD_PMAADDR
-  #define USB_TypeDef USB_DRD_TypeDef
-  #define EP0R CHEP0R
-  #define USB_EP_CTR_RX USB_EP_VTRX
-  #define USB_EP_CTR_TX USB_EP_VTTX
-  #define USB_EP_T_FIELD USB_CHEP_UTYPE
-  #define USB_EPREG_MASK USB_CHEP_REG_MASK
-  #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
-  #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
-  #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
-  #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
-  #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
-  #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
-  #define USB_EPRX_STAT USB_CH_RX_VALID
-  #define USB_EPKIND_MASK USB_EP_KIND_MASK
-  #define USB USB_DRD_FS
-  #define USB_CNTR_FRES USB_CNTR_USBRST
-  #define USB_CNTR_RESUME USB_CNTR_L2RES
-  #define USB_ISTR_EP_ID USB_ISTR_IDN
-  #define USB_EPADDR_FIELD USB_CHEP_ADDR
-  #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
-  #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
-  #include "stm32h5xx.h"
-  #define FSDEV_BUS_32BIT
-
-  #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE)
-  #define USB_DRD_BASE USB_DRD_FS_BASE
-  #endif
-
-  #define FSDEV_PMA_SIZE (2048u)
-  #undef USB_PMAADDR
-  #define USB_PMAADDR USB_DRD_PMAADDR
-  #define USB_TypeDef USB_DRD_TypeDef
-  #define EP0R CHEP0R
-  #define USB_EP_CTR_RX USB_EP_VTRX
-  #define USB_EP_CTR_TX USB_EP_VTTX
-  #define USB_EP_T_FIELD USB_CHEP_UTYPE
-  #define USB_EPREG_MASK USB_CHEP_REG_MASK
-  #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
-  #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
-  #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
-  #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
-  #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
-  #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
-  #define USB_EPRX_STAT USB_CH_RX_VALID
-  #define USB_EPKIND_MASK USB_EP_KIND_MASK
-  #define USB USB_DRD_FS
-  #define USB_CNTR_FRES USB_CNTR_USBRST
-  #define USB_CNTR_RESUME USB_CNTR_L2RES
-  #define USB_ISTR_EP_ID USB_ISTR_IDN
-  #define USB_EPADDR_FIELD USB_CHEP_ADDR
-  #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
-  #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
-  #include "stm32wbxx.h"
-  #define FSDEV_PMA_SIZE (1024u)
-  /* ST provided header has incorrect value */
-  #undef USB_PMAADDR
-  #define USB_PMAADDR USB1_PMAADDR
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
-  #include "stm32l4xx.h"
-  #define FSDEV_PMA_SIZE (1024u)
-
-#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
-  #include "stm32l5xx.h"
-  #define FSDEV_PMA_SIZE (1024u)
-
-  #ifndef USB_PMAADDR
-    #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
-  #endif
-
-#else
-  #error You are using an untested or unimplemented STM32 variant. Please update the driver.
-  // This includes L1x0, L1x1, L1x2, L4x2 and L4x3, G1x1, G1x3, and G1x4
-#endif
-
-// For purposes of accessing the packet
-#if ((FSDEV_PMA_SIZE) == 512u)
-  #define FSDEV_PMA_STRIDE  (2u)
-#elif ((FSDEV_PMA_SIZE) == 1024u)
-  #define FSDEV_PMA_STRIDE  (1u)
-#endif
-
-// The fsdev_bus_t type can be used for both register and PMA access necessities
-// For type-safety create a new macro for the volatile address of PMAADDR
-// The compiler should warn us if we cast it to a non-volatile type?
-#ifdef FSDEV_BUS_32BIT
-typedef uint32_t fsdev_bus_t;
-static __IO uint32_t * const pma32 = (__IO uint32_t*)USB_PMAADDR;
-
-#else
-typedef uint16_t fsdev_bus_t;
-// Volatile is also needed to prevent the optimizer from changing access to 32-bit (as 32-bit access is forbidden)
-static __IO uint16_t * const pma = (__IO uint16_t*)USB_PMAADDR;
-
-TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t * pcd_btable_word_ptr(USB_TypeDef * USBx, size_t x) {
-  size_t total_word_offset = (((USBx)->BTABLE)>>1) + x;
-  total_word_offset *= FSDEV_PMA_STRIDE;
-  return &(pma[total_word_offset]);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_tx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) {
-  return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 1u);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline __IO uint16_t* pcd_ep_rx_cnt_ptr(USB_TypeDef * USBx, uint32_t bEpIdx) {
-  return pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 3u);
-}
-#endif
-
-/* Aligned buffer size according to hardware */
-TU_ATTR_ALWAYS_INLINE static inline uint16_t pcd_aligned_buffer_size(uint16_t size) {
-  /* The STM32 full speed USB peripheral supports only a limited set of
-   * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */
-  uint16_t blocksize = (size > 62) ? 32 : 2;
-
-  // Round up while dividing requested size by blocksize
-  uint16_t numblocks = (size + blocksize - 1) / blocksize ;
-
-  return numblocks * blocksize;
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wRegValue) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  __O uint32_t *reg = (__O uint32_t *)(USB_DRD_BASE + bEpIdx*4);
-  *reg = wRegValue;
-#else
-  __O uint16_t *reg = (__O uint16_t *)((&USBx->EP0R) + bEpIdx*2u);
-  *reg = (uint16_t)wRegValue;
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_endpoint(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  __I uint32_t *reg = (__I uint32_t *)(USB_DRD_BASE + bEpIdx*4);
-#else
-  __I uint16_t *reg = (__I uint16_t *)((&USBx->EP0R) + bEpIdx*2u);
-#endif
-  return *reg;
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_eptype(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wType) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= (uint32_t)USB_EP_T_MASK;
-  regVal |= wType;
-  regVal |= USB_EP_CTR_RX | USB_EP_CTR_TX; // These clear on write0, so must set high
-  pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_eptype(USB_TypeDef * USBx, uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= USB_EP_T_FIELD;
-  return regVal;
-}
-
-/**
-  * @brief  Clears bit CTR_RX / CTR_TX in the endpoint register.
-  * @param  USBx USB peripheral instance register address.
-  * @param  bEpIdx Endpoint Number.
-  * @retval None
-  */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= USB_EPREG_MASK;
-  regVal &= ~USB_EP_CTR_RX;
-  regVal |= USB_EP_CTR_TX; // preserve CTR_TX (clears on writing 0)
-  pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_ep_ctr(USB_TypeDef * USBx, uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= USB_EPREG_MASK;
-  regVal &= ~USB_EP_CTR_TX;
-  regVal |= USB_EP_CTR_RX; // preserve CTR_RX (clears on writing 0)
-  pcd_set_endpoint(USBx, bEpIdx,regVal);
-}
-
-/**
-  * @brief  gets counter of the tx buffer.
-  * @param  USBx USB peripheral instance register address.
-  * @param  bEpIdx Endpoint Number.
-  * @retval Counter value
-  */
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  return (pma32[2*bEpIdx] & 0x03FF0000) >> 16;
-#else
-  __I uint16_t *regPtr = pcd_ep_tx_cnt_ptr(USBx, bEpIdx);
-  return *regPtr & 0x3ffU;
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  return (pma32[2*bEpIdx + 1] & 0x03FF0000) >> 16;
-#else
-  __I uint16_t *regPtr = pcd_ep_rx_cnt_ptr(USBx, bEpIdx);
-  return *regPtr & 0x3ffU;
-#endif
-}
-
-/**
-  * @brief  Sets address in an endpoint register.
-  * @param  USBx USB peripheral instance register address.
-  * @param  bEpIdx Endpoint Number.
-  * @param  bAddr Address.
-  * @retval None
-  */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_address(USB_TypeDef * USBx,  uint32_t bEpIdx, uint32_t bAddr) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= USB_EPREG_MASK;
-  regVal |= bAddr;
-  regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
-  pcd_set_endpoint(USBx, bEpIdx,regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  return pma32[2*bEpIdx] & 0x0000FFFFu ;
-#else
-  return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  return pma32[2*bEpIdx + 1] & 0x0000FFFFu;
-#else
-  return *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  pma32[2*bEpIdx] = (pma32[2*bEpIdx] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
-#else
-  *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 0u) = addr;
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_address(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t addr) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & 0xFFFF0000u) | (addr & 0x0000FFFCu);
-#else
-  *pcd_btable_word_ptr(USBx,(bEpIdx)*4u + 2u) = addr;
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  pma32[2*bEpIdx] = (pma32[2*bEpIdx] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
-#else
-  __IO uint16_t * reg = pcd_ep_tx_cnt_ptr(USBx, bEpIdx);
-  *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_cnt(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  pma32[2*bEpIdx + 1] = (pma32[2*bEpIdx + 1] & ~0x03FF0000u) | ((wCount & 0x3FFu) << 16);
-#else
-  __IO uint16_t * reg = pcd_ep_rx_cnt_ptr(USBx, bEpIdx);
-  *reg = (uint16_t) (*reg & (uint16_t) ~0x3FFU) | (wCount & 0x3FFU);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_blsize_num_blocks(USB_TypeDef * USBx, uint32_t rxtx_idx,
-                                                                      uint32_t blocksize, uint32_t numblocks) {
-  /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
-#ifdef FSDEV_BUS_32BIT
-  (void) USBx;
-  pma32[rxtx_idx] = (pma32[rxtx_idx] & 0x0000FFFFu) | (blocksize << 31) | ((numblocks - blocksize) << 26);
-#else
-  __IO uint16_t *pdwReg = pcd_btable_word_ptr(USBx, rxtx_idx*2u + 1u);
-  *pdwReg = (blocksize << 15) | ((numblocks - blocksize) << 10);
-#endif
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_bufsize(USB_TypeDef * USBx, uint32_t rxtx_idx, uint32_t wCount) {
-  wCount = pcd_aligned_buffer_size(wCount);
-
-  /* We assume that the buffer size is already aligned to hardware requirements. */
-  uint16_t blocksize = (wCount > 62) ? 1 : 0;
-  uint16_t numblocks = wCount / (blocksize ? 32 : 2);
-
-  /* There should be no remainder in the above calculation */
-  TU_ASSERT((wCount - (numblocks * (blocksize ? 32 : 2))) == 0, /**/);
-
-  /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
-  pcd_set_ep_blsize_num_blocks(USBx, rxtx_idx, blocksize, numblocks);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
-  pcd_set_ep_bufsize(USBx, 2*bEpIdx, wCount);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_bufsize(USB_TypeDef * USBx, uint32_t bEpIdx, uint32_t wCount) {
-  pcd_set_ep_bufsize(USBx, 2*bEpIdx + 1, wCount);
-}
-
-/**
-  * @brief  sets the status for tx transfer (bits STAT_TX[1:0]).
-  * @param  USBx USB peripheral instance register address.
-  * @param  bEpIdx Endpoint Number.
-  * @param  wState new state
-  * @retval None
-  */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_tx_status(USB_TypeDef * USBx,  uint32_t bEpIdx, uint32_t wState) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= USB_EPTX_DTOGMASK;
-
-  /* toggle first bit ? */
-  if((USB_EPTX_DTOG1 & (wState))!= 0U)
-  {
-    regVal ^= USB_EPTX_DTOG1;
-  }
-  /* toggle second bit ?  */
-  if((USB_EPTX_DTOG2 & ((uint32_t)(wState)))!= 0U)
-  {
-    regVal ^= USB_EPTX_DTOG2;
-  }
-
-  regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
-  pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-/**
-  * @brief  sets the status for rx transfer (bits STAT_TX[1:0])
-  * @param  USBx USB peripheral instance register address.
-  * @param  bEpIdx Endpoint Number.
-  * @param  wState new state
-  * @retval None
-  */
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_rx_status(USB_TypeDef * USBx,  uint32_t bEpIdx, uint32_t wState) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= USB_EPRX_DTOGMASK;
-
-  /* toggle first bit ? */
-  if((USB_EPRX_DTOG1 & wState)!= 0U) {
-    regVal ^= USB_EPRX_DTOG1;
-  }
-  /* toggle second bit ? */
-  if((USB_EPRX_DTOG2 & wState)!= 0U) {
-    regVal ^= USB_EPRX_DTOG2;
-  }
-
-  regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
-  pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline uint32_t pcd_get_ep_rx_status(USB_TypeDef * USBx,  uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  return (regVal & USB_EPRX_STAT) >> (12u);
-}
-
-
-/**
-  * @brief  Toggles DTOG_RX / DTOG_TX bit in the endpoint register.
-  * @param  USBx USB peripheral instance register address.
-  * @param  bEpIdx Endpoint Number.
-  * @retval None
-  */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_rx_dtog(USB_TypeDef * USBx,  uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= USB_EPREG_MASK;
-  regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_RX;
-  pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_tx_dtog(USB_TypeDef * USBx,  uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= USB_EPREG_MASK;
-  regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX|USB_EP_DTOG_TX;
-  pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-/**
-  * @brief  Clears DTOG_RX / DTOG_TX bit in the endpoint register.
-  * @param  USBx USB peripheral instance register address.
-  * @param  bEpIdx Endpoint Number.
-  * @retval None
-  */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_rx_dtog(USB_TypeDef * USBx,  uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  if((regVal & USB_EP_DTOG_RX) != 0) {
-    pcd_rx_dtog(USBx,bEpIdx);
-  }
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_tx_dtog(USB_TypeDef * USBx,  uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  if((regVal & USB_EP_DTOG_TX) != 0) {
-    pcd_tx_dtog(USBx,bEpIdx);
-  }
-}
-
-/**
-  * @brief  set & clear EP_KIND bit.
-  * @param  USBx USB peripheral instance register address.
-  * @param  bEpIdx Endpoint Number.
-  * @retval None
-  */
-TU_ATTR_ALWAYS_INLINE static inline void pcd_set_ep_kind(USB_TypeDef * USBx,  uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal |= USB_EP_KIND;
-  regVal &= USB_EPREG_MASK;
-  regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
-  pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-TU_ATTR_ALWAYS_INLINE static inline void pcd_clear_ep_kind(USB_TypeDef * USBx, uint32_t bEpIdx) {
-  uint32_t regVal = pcd_get_endpoint(USBx, bEpIdx);
-  regVal &= USB_EPKIND_MASK;
-  regVal |= USB_EP_CTR_RX|USB_EP_CTR_TX;
-  pcd_set_endpoint(USBx, bEpIdx, regVal);
-}
-
-// This checks if the device has "LPM"
-#if defined(USB_ISTR_L1REQ)
-#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ)
-#else
-#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U)
-#endif
-
-#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \
-     USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED )
-
-// Number of endpoints in hardware
-// TODO should use TUP_DCD_ENDPOINT_MAX
-#define STFSDEV_EP_COUNT (8u)
-
-#endif /* PORTABLE_ST_STM32F0_DCD_STM32F0_FSDEV_PVT_ST_H_ */
diff --git a/src/portable/st/stm32_fsdev/fsdev_ch32.h b/src/portable/st/stm32_fsdev/fsdev_ch32.h
new file mode 100644
index 000000000..8c0961bb9
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_ch32.h
@@ -0,0 +1,220 @@
+/*
+* The MIT License (MIT)
+ *
+ * Copyright (c) 2024, hathach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+/** © Copyright (c) 2016 STMicroelectronics.
+  * All rights reserved.
+  *
+  * This software component is licensed by ST under BSD 3-Clause license,
+  * the "License"; You may not use this file except in compliance with the
+  * License. You may obtain a copy of the License at:
+  *                        opensource.org/licenses/BSD-3-Clause
+  */
+
+#ifndef TUSB_FSDEV_CH32_H
+#define TUSB_FSDEV_CH32_H
+
+#include "common/tusb_compiler.h"
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32F20X
+  #include 
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X
+  #include 
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#define FSDEV_PMA_SIZE (512u)
+#define FSDEV_REG_BASE  0x40005C00UL
+
+#define USB_BASE            (APB1PERIPH_BASE + 0x00005C00UL) /*!< USB_IP Peripheral Registers base address */
+#define USB_PMAADDR         (APB1PERIPH_BASE + 0x00006000UL) /*!< USB_IP Packet Memory Area base address */
+#define USB                 ((USB_TypeDef *)USB_BASE)
+
+/******************************************************************************/
+/*                                                                            */
+/*                         USB Device General registers                       */
+/*                                                                            */
+/******************************************************************************/
+#define USB_CNTR                             (USB_BASE + 0x40U)             /*!< Control register */
+#define USB_ISTR                             (USB_BASE + 0x44U)             /*!< Interrupt status register */
+#define USB_FNR                              (USB_BASE + 0x48U)             /*!< Frame number register */
+#define USB_DADDR                            (USB_BASE + 0x4CU)             /*!< Device address register */
+#define USB_BTABLE                           (USB_BASE + 0x50U)             /*!< Buffer Table address register */
+
+/****************************  ISTR interrupt events  *************************/
+#define USB_ISTR_CTR                         ((uint16_t)0x8000U)               /*!< Correct TRansfer (clear-only bit) */
+#define USB_ISTR_PMAOVR                      ((uint16_t)0x4000U)               /*!< DMA OVeR/underrun (clear-only bit) */
+#define USB_ISTR_ERR                         ((uint16_t)0x2000U)               /*!< ERRor (clear-only bit) */
+#define USB_ISTR_WKUP                        ((uint16_t)0x1000U)               /*!< WaKe UP (clear-only bit) */
+#define USB_ISTR_SUSP                        ((uint16_t)0x0800U)               /*!< SUSPend (clear-only bit) */
+#define USB_ISTR_RESET                       ((uint16_t)0x0400U)               /*!< RESET (clear-only bit) */
+#define USB_ISTR_SOF                         ((uint16_t)0x0200U)               /*!< Start Of Frame (clear-only bit) */
+#define USB_ISTR_ESOF                        ((uint16_t)0x0100U)               /*!< Expected Start Of Frame (clear-only bit) */
+#define USB_ISTR_DIR                         ((uint16_t)0x0010U)               /*!< DIRection of transaction (read-only bit)  */
+#define USB_ISTR_EP_ID                       ((uint16_t)0x000FU)               /*!< EndPoint IDentifier (read-only bit)  */
+
+/* Legacy defines */
+#define USB_ISTR_PMAOVRM USB_ISTR_PMAOVR
+
+#define USB_CLR_CTR                          (~USB_ISTR_CTR)             /*!< clear Correct TRansfer bit */
+#define USB_CLR_PMAOVR                       (~USB_ISTR_PMAOVR)          /*!< clear DMA OVeR/underrun bit*/
+#define USB_CLR_ERR                          (~USB_ISTR_ERR)             /*!< clear ERRor bit */
+#define USB_CLR_WKUP                         (~USB_ISTR_WKUP)            /*!< clear WaKe UP bit */
+#define USB_CLR_SUSP                         (~USB_ISTR_SUSP)            /*!< clear SUSPend bit */
+#define USB_CLR_RESET                        (~USB_ISTR_RESET)           /*!< clear RESET bit */
+#define USB_CLR_SOF                          (~USB_ISTR_SOF)             /*!< clear Start Of Frame bit */
+#define USB_CLR_ESOF                         (~USB_ISTR_ESOF)            /*!< clear Expected Start Of Frame bit */
+
+/* Legacy defines */
+#define USB_CLR_PMAOVRM USB_CLR_PMAOVR
+
+/*************************  CNTR control register bits definitions  ***********/
+#define USB_CNTR_CTRM                        ((uint16_t)0x8000U)               /*!< Correct TRansfer Mask */
+#define USB_CNTR_PMAOVR                      ((uint16_t)0x4000U)               /*!< DMA OVeR/underrun Mask */
+#define USB_CNTR_ERRM                        ((uint16_t)0x2000U)               /*!< ERRor Mask */
+#define USB_CNTR_WKUPM                       ((uint16_t)0x1000U)               /*!< WaKe UP Mask */
+#define USB_CNTR_SUSPM                       ((uint16_t)0x0800U)               /*!< SUSPend Mask */
+#define USB_CNTR_RESETM                      ((uint16_t)0x0400U)               /*!< RESET Mask   */
+#define USB_CNTR_SOFM                        ((uint16_t)0x0200U)               /*!< Start Of Frame Mask */
+#define USB_CNTR_ESOFM                       ((uint16_t)0x0100U)               /*!< Expected Start Of Frame Mask */
+#define USB_CNTR_RESUME                      ((uint16_t)0x0010U)               /*!< RESUME request */
+#define USB_CNTR_FSUSP                       ((uint16_t)0x0008U)               /*!< Force SUSPend */
+#define USB_CNTR_LPMODE                      ((uint16_t)0x0004U)               /*!< Low-power MODE */
+#define USB_CNTR_PDWN                        ((uint16_t)0x0002U)               /*!< Power DoWN */
+#define USB_CNTR_FRES                        ((uint16_t)0x0001U)               /*!< Force USB RESet */
+
+/* Legacy defines */
+#define USB_CNTR_PMAOVRM USB_CNTR_PMAOVR
+#define USB_CNTR_LP_MODE USB_CNTR_LPMODE
+
+/********************  FNR Frame Number Register bit definitions   ************/
+#define USB_FNR_RXDP                         ((uint16_t)0x8000U)               /*!< status of D+ data line */
+#define USB_FNR_RXDM                         ((uint16_t)0x4000U)               /*!< status of D- data line */
+#define USB_FNR_LCK                          ((uint16_t)0x2000U)               /*!< LoCKed */
+#define USB_FNR_LSOF                         ((uint16_t)0x1800U)               /*!< Lost SOF */
+#define USB_FNR_FN                           ((uint16_t)0x07FFU)               /*!< Frame Number */
+
+/********************  DADDR Device ADDRess bit definitions    ****************/
+#define USB_DADDR_EF                         ((uint8_t)0x80U)                  /*!< USB device address Enable Function */
+#define USB_DADDR_ADD                        ((uint8_t)0x7FU)                  /*!< USB device address */
+
+/******************************  Endpoint register    *************************/
+#define USB_EP0R                             USB_BASE                    /*!< endpoint 0 register address */
+#define USB_EP1R                             (USB_BASE + 0x04U)           /*!< endpoint 1 register address */
+#define USB_EP2R                             (USB_BASE + 0x08U)           /*!< endpoint 2 register address */
+#define USB_EP3R                             (USB_BASE + 0x0CU)           /*!< endpoint 3 register address */
+#define USB_EP4R                             (USB_BASE + 0x10U)           /*!< endpoint 4 register address */
+#define USB_EP5R                             (USB_BASE + 0x14U)           /*!< endpoint 5 register address */
+#define USB_EP6R                             (USB_BASE + 0x18U)           /*!< endpoint 6 register address */
+#define USB_EP7R                             (USB_BASE + 0x1CU)           /*!< endpoint 7 register address */
+/* bit positions */
+#define USB_EP_CTR_RX                        ((uint16_t)0x8000U)               /*!<  EndPoint Correct TRansfer RX */
+#define USB_EP_DTOG_RX                       ((uint16_t)0x4000U)               /*!<  EndPoint Data TOGGLE RX */
+#define USB_EPRX_STAT                        ((uint16_t)0x3000U)               /*!<  EndPoint RX STATus bit field */
+#define USB_EP_SETUP                         ((uint16_t)0x0800U)               /*!<  EndPoint SETUP */
+#define USB_EP_T_FIELD                       ((uint16_t)0x0600U)               /*!<  EndPoint TYPE */
+#define USB_EP_KIND                          ((uint16_t)0x0100U)               /*!<  EndPoint KIND */
+#define USB_EP_CTR_TX                        ((uint16_t)0x0080U)               /*!<  EndPoint Correct TRansfer TX */
+#define USB_EP_DTOG_TX                       ((uint16_t)0x0040U)               /*!<  EndPoint Data TOGGLE TX */
+#define USB_EPTX_STAT                        ((uint16_t)0x0030U)               /*!<  EndPoint TX STATus bit field */
+#define USB_EPADDR_FIELD                     ((uint16_t)0x000FU)               /*!<  EndPoint ADDRess FIELD */
+
+/* EndPoint REGister MASK (no toggle fields) */
+#define USB_EPREG_MASK     (USB_EP_CTR_RX|USB_EP_SETUP|USB_EP_T_FIELD|USB_EP_KIND|USB_EP_CTR_TX|USB_EPADDR_FIELD)
+                                                                               /*!< EP_TYPE[1:0] EndPoint TYPE */
+#define USB_EP_TYPE_MASK                     ((uint16_t)0x0600U)               /*!< EndPoint TYPE Mask */
+#define USB_EP_BULK                          ((uint16_t)0x0000U)               /*!< EndPoint BULK */
+#define USB_EP_CONTROL                       ((uint16_t)0x0200U)               /*!< EndPoint CONTROL */
+#define USB_EP_ISOCHRONOUS                   ((uint16_t)0x0400U)               /*!< EndPoint ISOCHRONOUS */
+#define USB_EP_INTERRUPT                     ((uint16_t)0x0600U)               /*!< EndPoint INTERRUPT */
+#define USB_EP_T_MASK                        ((uint16_t) ~USB_EP_T_FIELD & USB_EPREG_MASK)
+
+#define USB_EPKIND_MASK                      ((uint16_t) ~USB_EP_KIND & USB_EPREG_MASK)            /*!< EP_KIND EndPoint KIND */
+                                                                               /*!< STAT_TX[1:0] STATus for TX transfer */
+#define USB_EP_TX_DIS                        ((uint16_t)0x0000U)               /*!< EndPoint TX DISabled */
+#define USB_EP_TX_STALL                      ((uint16_t)0x0010U)               /*!< EndPoint TX STALLed */
+#define USB_EP_TX_NAK                        ((uint16_t)0x0020U)               /*!< EndPoint TX NAKed */
+#define USB_EP_TX_VALID                      ((uint16_t)0x0030U)               /*!< EndPoint TX VALID */
+#define USB_EPTX_DTOG1                       ((uint16_t)0x0010U)               /*!< EndPoint TX Data TOGgle bit1 */
+#define USB_EPTX_DTOG2                       ((uint16_t)0x0020U)               /*!< EndPoint TX Data TOGgle bit2 */
+#define USB_EPTX_DTOGMASK  (USB_EPTX_STAT|USB_EPREG_MASK)
+                                                                               /*!< STAT_RX[1:0] STATus for RX transfer */
+#define USB_EP_RX_DIS                        ((uint16_t)0x0000U)               /*!< EndPoint RX DISabled */
+#define USB_EP_RX_STALL                      ((uint16_t)0x1000U)               /*!< EndPoint RX STALLed */
+#define USB_EP_RX_NAK                        ((uint16_t)0x2000U)               /*!< EndPoint RX NAKed */
+#define USB_EP_RX_VALID                      ((uint16_t)0x3000U)               /*!< EndPoint RX VALID */
+#define USB_EPRX_DTOG1                       ((uint16_t)0x1000U)               /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOG2                       ((uint16_t)0x2000U)               /*!< EndPoint RX Data TOGgle bit1 */
+#define USB_EPRX_DTOGMASK  (USB_EPRX_STAT|USB_EPREG_MASK)
+
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32V20X
+static const IRQn_Type fsdev_irq[] = {
+  USB_HP_CAN1_TX_IRQn,
+  USB_LP_CAN1_RX0_IRQn,
+  USBWakeUp_IRQn
+};
+enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
+#else
+  #error "Unsupported MCU"
+#endif
+
+void dcd_int_enable(uint8_t rhport) {
+  (void)rhport;
+  for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+    NVIC_EnableIRQ(fsdev_irq[i]);
+  }
+}
+
+void dcd_int_disable(uint8_t rhport) {
+  (void)rhport;
+  for(uint8_t i=0; i < FSDEV_IRQ_NUM; i++) {
+    NVIC_DisableIRQ(fsdev_irq[i]);
+  }
+}
+
+void dcd_disconnect(uint8_t rhport) {
+  (void) rhport;
+  EXTEN->EXTEN_CTR &= ~EXTEN_USBD_PU_EN;
+}
+
+void dcd_connect(uint8_t rhport) {
+  (void) rhport;
+  EXTEN->EXTEN_CTR |= EXTEN_USBD_PU_EN;
+}
+
+#endif
diff --git a/src/portable/st/stm32_fsdev/fsdev_stm32.h b/src/portable/st/stm32_fsdev/fsdev_stm32.h
new file mode 100644
index 000000000..bb2c72fd1
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_stm32.h
@@ -0,0 +1,334 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright(c) N Conrad
+ * Copyright (c) 2019 Ha Thach (tinyusb.org)
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *   1. Redistributions of source code must retain the above copyright notice,
+ *      this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright notice,
+ *      this list of conditions and the following disclaimer in the documentation
+ *      and/or other materials provided with the distribution.
+ *   3. Neither the name of STMicroelectronics nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef TUSB_FSDEV_STM32_H
+#define TUSB_FSDEV_STM32_H
+
+#if CFG_TUSB_MCU == OPT_MCU_STM32F0
+  #include "stm32f0xx.h"
+  #define FSDEV_PMA_SIZE (1024u)
+  #define FSDEV_REG_BASE USB_BASE
+  // F0x2 models are crystal-less
+  // All have internal D+ pull-up
+  // 070RB:    2 x 16 bits/word memory     LPM Support, BCD Support
+  // PMA dedicated to USB (no sharing with CAN)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32F1
+  #include "stm32f1xx.h"
+  #define FSDEV_PMA_SIZE (512u)
+  // NO internal Pull-ups
+  //         *B, and *C:    2 x 16 bits/word
+
+  // F1 names this differently from the rest
+  #define USB_CNTR_LPMODE   USB_CNTR_LP_MODE
+
+#elif defined(STM32F302xB) || defined(STM32F302xC) || \
+      defined(STM32F303xB) || defined(STM32F303xC) || \
+      defined(STM32F373xC)
+  #include "stm32f3xx.h"
+  #define FSDEV_PMA_SIZE (512u)
+  // NO internal Pull-ups
+  //         *B, and *C:    1 x 16 bits/word
+  // PMA dedicated to USB (no sharing with CAN)
+
+#elif defined(STM32F302x6) || defined(STM32F302x8) || \
+      defined(STM32F302xD) || defined(STM32F302xE) || \
+      defined(STM32F303xD) || defined(STM32F303xE)
+  #include "stm32f3xx.h"
+  #define FSDEV_PMA_SIZE (1024u)
+  // NO internal Pull-ups
+  // *6, *8, *D, and *E:    2 x 16 bits/word     LPM Support
+  // When CAN clock is enabled, USB can use first 768 bytes ONLY.
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L0
+  #include "stm32l0xx.h"
+  #define FSDEV_PMA_SIZE (1024u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L1
+  #include "stm32l1xx.h"
+  #define FSDEV_PMA_SIZE (512u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32G4
+  #include "stm32g4xx.h"
+  #define FSDEV_PMA_SIZE (1024u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32G0
+  #include "stm32g0xx.h"
+  #define FSDEV_BUS_32BIT
+  #define FSDEV_PMA_SIZE (2048u)
+  #undef USB_PMAADDR
+  #define USB_PMAADDR USB_DRD_PMAADDR
+  #define USB_TypeDef USB_DRD_TypeDef
+  #define EP0R CHEP0R
+  #define USB_EP_CTR_RX USB_EP_VTRX
+  #define USB_EP_CTR_TX USB_EP_VTTX
+  #define USB_EP_T_FIELD USB_CHEP_UTYPE
+  #define USB_EPREG_MASK USB_CHEP_REG_MASK
+  #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+  #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+  #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+  #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+  #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+  #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+  #define USB_EPRX_STAT USB_CH_RX_VALID
+  #define USB_EPKIND_MASK USB_EP_KIND_MASK
+  #define USB USB_DRD_FS
+  #define USB_CNTR_FRES USB_CNTR_USBRST
+  #define USB_CNTR_RESUME USB_CNTR_L2RES
+  #define USB_ISTR_EP_ID USB_ISTR_IDN
+  #define USB_EPADDR_FIELD USB_CHEP_ADDR
+  #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+  #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32H5
+  #include "stm32h5xx.h"
+  #define FSDEV_BUS_32BIT
+
+  #if !defined(USB_DRD_BASE) && defined(USB_DRD_FS_BASE)
+  #define USB_DRD_BASE USB_DRD_FS_BASE
+  #endif
+
+  #define FSDEV_PMA_SIZE (2048u)
+  #undef USB_PMAADDR
+  #define USB_PMAADDR USB_DRD_PMAADDR
+  #define USB_TypeDef USB_DRD_TypeDef
+  #define EP0R CHEP0R
+  #define USB_EP_CTR_RX USB_EP_VTRX
+  #define USB_EP_CTR_TX USB_EP_VTTX
+  #define USB_EP_T_FIELD USB_CHEP_UTYPE
+  #define USB_EPREG_MASK USB_CHEP_REG_MASK
+  #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+  #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+  #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+  #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+  #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+  #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+  #define USB_EPRX_STAT USB_CH_RX_VALID
+  #define USB_EPKIND_MASK USB_EP_KIND_MASK
+  #define USB USB_DRD_FS
+  #define USB_CNTR_FRES USB_CNTR_USBRST
+  #define USB_CNTR_RESUME USB_CNTR_L2RES
+  #define USB_ISTR_EP_ID USB_ISTR_IDN
+  #define USB_EPADDR_FIELD USB_CHEP_ADDR
+  #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+  #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32WB
+  #include "stm32wbxx.h"
+  #define FSDEV_PMA_SIZE (1024u)
+  /* ST provided header has incorrect value */
+  #undef USB_PMAADDR
+  #define USB_PMAADDR USB1_PMAADDR
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L4
+  #include "stm32l4xx.h"
+  #define FSDEV_PMA_SIZE (1024u)
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32L5
+  #include "stm32l5xx.h"
+  #define FSDEV_PMA_SIZE (1024u)
+
+  #ifndef USB_PMAADDR
+    #define USB_PMAADDR (USB_BASE + (USB_PMAADDR_NS - USB_BASE_NS))
+  #endif
+
+#elif CFG_TUSB_MCU == OPT_MCU_STM32U5
+  #include "stm32u5xx.h"
+  #define FSDEV_BUS_32BIT
+
+  #define FSDEV_PMA_SIZE (2048u)
+  #undef USB_PMAADDR
+  #define USB_PMAADDR USB_DRD_PMAADDR
+  #define USB_TypeDef USB_DRD_TypeDef
+  #define EP0R CHEP0R
+  #define USB_EP_CTR_RX USB_EP_VTRX
+  #define USB_EP_CTR_TX USB_EP_VTTX
+  #define USB_EP_T_FIELD USB_CHEP_UTYPE
+  #define USB_EPREG_MASK USB_CHEP_REG_MASK
+  #define USB_EPTX_DTOGMASK USB_CHEP_TX_DTOGMASK
+  #define USB_EPRX_DTOGMASK USB_CHEP_RX_DTOGMASK
+  #define USB_EPTX_DTOG1 USB_CHEP_TX_DTOG1
+  #define USB_EPTX_DTOG2 USB_CHEP_TX_DTOG2
+  #define USB_EPRX_DTOG1 USB_CHEP_RX_DTOG1
+  #define USB_EPRX_DTOG2 USB_CHEP_RX_DTOG2
+  #define USB_EPRX_STAT USB_CH_RX_VALID
+  #define USB_EPKIND_MASK USB_EP_KIND_MASK
+  #define USB USB_DRD_FS
+  #define USB_CNTR_FRES USB_CNTR_USBRST
+  #define USB_CNTR_RESUME USB_CNTR_L2RES
+  #define USB_ISTR_EP_ID USB_ISTR_IDN
+  #define USB_EPADDR_FIELD USB_CHEP_ADDR
+  #define USB_CNTR_LPMODE USB_CNTR_SUSPRDY
+  #define USB_CNTR_FSUSP USB_CNTR_SUSPEN
+
+#else
+  #error You are using an untested or unimplemented STM32 variant. Please update the driver.
+  // This includes U0
+#endif
+
+#if defined(USB_BASE)
+  #define FSDEV_REG_BASE USB_BASE
+#elif defined(USB_DRD_BASE)
+  #define FSDEV_REG_BASE USB_DRD_BASE
+#elif defined(USB_DRD_FS_BASE)
+  #define FSDEV_REG_BASE USB_DRD_FS_BASE
+#else
+  #error "FSDEV_REG_BASE not defined"
+#endif
+
+// This checks if the device has "LPM"
+#if defined(USB_ISTR_L1REQ)
+#define USB_ISTR_L1REQ_FORCED (USB_ISTR_L1REQ)
+#else
+#define USB_ISTR_L1REQ_FORCED ((uint16_t)0x0000U)
+#endif
+
+#define USB_ISTR_ALL_EVENTS (USB_ISTR_PMAOVR | USB_ISTR_ERR | USB_ISTR_WKUP | USB_ISTR_SUSP | \
+     USB_ISTR_RESET | USB_ISTR_SOF | USB_ISTR_ESOF | USB_ISTR_L1REQ_FORCED )
+
+//--------------------------------------------------------------------+
+//
+//--------------------------------------------------------------------+
+
+#if TU_CHECK_MCU(OPT_MCU_STM32L1) && !defined(USBWakeUp_IRQn)
+  #define USBWakeUp_IRQn USB_FS_WKUP_IRQn
+#endif
+
+static const IRQn_Type fsdev_irq[] = {
+  #if TU_CHECK_MCU(OPT_MCU_STM32F0, OPT_MCU_STM32L0, OPT_MCU_STM32L4)
+    USB_IRQn,
+  #elif CFG_TUSB_MCU == OPT_MCU_STM32F1
+    USB_HP_CAN1_TX_IRQn,
+    USB_LP_CAN1_RX0_IRQn,
+    USBWakeUp_IRQn,
+  #elif CFG_TUSB_MCU == OPT_MCU_STM32F3
+    // USB remap handles dcd functions
+    USB_HP_CAN_TX_IRQn,
+    USB_LP_CAN_RX0_IRQn,
+    USBWakeUp_IRQn,
+  #elif CFG_TUSB_MCU == OPT_MCU_STM32G0
+    #ifdef STM32G0B0xx
+    USB_IRQn,
+    #else
+    USB_UCPD1_2_IRQn,
+    #endif
+  #elif TU_CHECK_MCU(OPT_MCU_STM32G4, OPT_MCU_STM32L1)
+    USB_HP_IRQn,
+    USB_LP_IRQn,
+    USBWakeUp_IRQn,
+  #elif CFG_TUSB_MCU == OPT_MCU_STM32H5
+    USB_DRD_FS_IRQn,
+  #elif CFG_TUSB_MCU == OPT_MCU_STM32L5
+    USB_FS_IRQn,
+  #elif CFG_TUSB_MCU == OPT_MCU_STM32WB
+    USB_HP_IRQn,
+    USB_LP_IRQn,
+  #elif CFG_TUSB_MCU == OPT_MCU_STM32U5
+    USB_IRQn,
+  #else
+    #error Unknown arch in USB driver
+  #endif
+};
+enum { FSDEV_IRQ_NUM = TU_ARRAY_SIZE(fsdev_irq) };
+
+void dcd_int_enable(uint8_t rhport) {
+  (void)rhport;
+
+  // forces write to RAM before allowing ISR to execute
+  __DSB(); __ISB();
+
+  #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP)
+  // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+  // shared USB/CAN IRQs to separate CAN and USB IRQs.
+  // This dynamically checks if this remap is active to enable the right IRQs.
+  if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
+    NVIC_EnableIRQ(USB_HP_IRQn);
+    NVIC_EnableIRQ(USB_LP_IRQn);
+    NVIC_EnableIRQ(USBWakeUp_RMP_IRQn);
+  } else
+  #endif
+  {
+    for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) {
+      NVIC_EnableIRQ(fsdev_irq[i]);
+    }
+  }
+}
+
+void dcd_int_disable(uint8_t rhport) {
+  (void)rhport;
+
+  #if CFG_TUSB_MCU == OPT_MCU_STM32F3 && defined(SYSCFG_CFGR1_USB_IT_RMP)
+  // Some STM32F302/F303 devices allow to remap the USB interrupt vectors from
+  // shared USB/CAN IRQs to separate CAN and USB IRQs.
+  // This dynamically checks if this remap is active to enable the right IRQs.
+  if (SYSCFG->CFGR1 & SYSCFG_CFGR1_USB_IT_RMP) {
+    NVIC_DisableIRQ(USB_HP_IRQn);
+    NVIC_DisableIRQ(USB_LP_IRQn);
+    NVIC_DisableIRQ(USBWakeUp_RMP_IRQn);
+  } else
+  #endif
+  {
+    for (uint8_t i = 0; i < FSDEV_IRQ_NUM; i++) {
+      NVIC_DisableIRQ(fsdev_irq[i]);
+    }
+  }
+
+  // CMSIS has a membar after disabling interrupts
+}
+
+// Define only on MCU with internal pull-up. BSP can define on MCU without internal PU.
+#if defined(USB_BCDR_DPPU)
+
+void dcd_disconnect(uint8_t rhport) {
+  (void)rhport;
+  USB->BCDR &= ~(USB_BCDR_DPPU);
+}
+
+void dcd_connect(uint8_t rhport) {
+  (void)rhport;
+  USB->BCDR |= USB_BCDR_DPPU;
+}
+
+#elif defined(SYSCFG_PMC_USB_PU) // works e.g. on STM32L151
+
+void dcd_disconnect(uint8_t rhport) {
+  (void)rhport;
+  SYSCFG->PMC &= ~(SYSCFG_PMC_USB_PU);
+}
+
+void dcd_connect(uint8_t rhport) {
+  (void)rhport;
+  SYSCFG->PMC |= SYSCFG_PMC_USB_PU;
+}
+#endif
+
+
+#endif /* TUSB_FSDEV_STM32_H */
diff --git a/src/portable/st/stm32_fsdev/fsdev_type.h b/src/portable/st/stm32_fsdev/fsdev_type.h
new file mode 100644
index 000000000..e4a0f3f28
--- /dev/null
+++ b/src/portable/st/stm32_fsdev/fsdev_type.h
@@ -0,0 +1,282 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright(c) 2016 STMicroelectronics
+ * Copyright(c) N Conrad
+ * Copyright (c) 2024, hathach (tinyusb.org)
+ *
+ * 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 TUSB_FSDEV_TYPE_H
+#define TUSB_FSDEV_TYPE_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#include "stdint.h"
+
+// If sharing with CAN, one can set this to be non-zero to give CAN space where it wants it
+// Both of these MUST be a multiple of 2, and are in byte units.
+#ifndef FSDEV_BTABLE_BASE
+#define FSDEV_BTABLE_BASE 0U
+#endif
+
+TU_VERIFY_STATIC(FSDEV_BTABLE_BASE % 8 == 0, "BTABLE base must be aligned to 8 bytes");
+
+// FSDEV_PMA_SIZE is PMA buffer size in bytes.
+// - 512-byte devices, access with a stride of two words (use every other 16-bit address)
+// - 1024-byte devices, access with a stride of one word (use every 16-bit address)
+// - 2048-byte devices, access with 32-bit address
+
+// For purposes of accessing the packet
+#if FSDEV_PMA_SIZE == 512
+  #define FSDEV_PMA_STRIDE  (2u) // 1x16 bit access scheme
+  #define pma_aligned TU_ATTR_ALIGNED(4)
+#elif FSDEV_PMA_SIZE == 1024
+  #define FSDEV_PMA_STRIDE  (1u) // 2x16 bit access scheme
+  #define pma_aligned
+#elif FSDEV_PMA_SIZE == 2048
+  #ifndef FSDEV_BUS_32BIT
+    #warning "FSDEV_PMA_SIZE is 2048, but FSDEV_BUS_32BIT is not defined"
+  #endif
+  #define FSDEV_PMA_STRIDE  (1u) // 32 bit access scheme
+  #define pma_aligned
+#endif
+
+//--------------------------------------------------------------------+
+// BTable Typedef
+//--------------------------------------------------------------------+
+enum {
+  BTABLE_BUF_TX = 0,
+  BTABLE_BUF_RX = 1
+};
+
+// hardware limit endpoint
+#define FSDEV_EP_COUNT 8
+
+// Buffer Table is located in Packet Memory Area (PMA) and therefore its address access is forced to either
+// 16-bit or 32-bit depending on FSDEV_BUS_32BIT.
+typedef union {
+  // 0: TX (IN), 1: RX (OUT)
+
+  // strictly 16-bit access (could be 32-bit aligned)
+  struct {
+    volatile pma_aligned uint16_t addr;
+    volatile pma_aligned uint16_t count;
+  } ep16[FSDEV_EP_COUNT][2];
+
+  // strictly 32-bit access
+  struct {
+    volatile uint32_t count_addr;
+  } ep32[FSDEV_EP_COUNT][2];
+} fsdev_btable_t;
+
+TU_VERIFY_STATIC(sizeof(fsdev_btable_t) == FSDEV_EP_COUNT*8*FSDEV_PMA_STRIDE, "size is not correct");
+TU_VERIFY_STATIC(FSDEV_BTABLE_BASE + FSDEV_EP_COUNT*8 <= FSDEV_PMA_SIZE, "BTABLE does not fit in PMA RAM");
+
+#define FSDEV_BTABLE ((volatile fsdev_btable_t*) (USB_PMAADDR+FSDEV_BTABLE_BASE))
+
+typedef struct {
+  volatile pma_aligned uint16_t u16;
+} fsdev_pma16_t;
+
+//--------------------------------------------------------------------+
+// Registers Typedef
+//--------------------------------------------------------------------+
+
+// volatile 32-bit aligned
+#define _va32     volatile TU_ATTR_ALIGNED(4)
+
+// The fsdev_bus_t type can be used for both register and PMA access necessities
+#ifdef FSDEV_BUS_32BIT
+typedef uint32_t fsdev_bus_t;
+#else
+typedef uint16_t fsdev_bus_t;
+#endif
+
+typedef struct {
+  struct {
+    _va32 fsdev_bus_t reg;
+  }ep[FSDEV_EP_COUNT];
+
+  _va32 uint32_t RESERVED7[8];       // Reserved
+  _va32 fsdev_bus_t CNTR;            // 40: Control register
+  _va32 fsdev_bus_t ISTR;            // 44: Interrupt status register
+  _va32 fsdev_bus_t FNR;             // 48: Frame number register
+  _va32 fsdev_bus_t DADDR;           // 4C: Device address register
+  _va32 fsdev_bus_t BTABLE;          // 50: Buffer Table address register (16-bit only)
+  _va32 fsdev_bus_t LPMCSR;          // 54: LPM Control and Status Register (32-bit only)
+  _va32 fsdev_bus_t BCDR;            // 58: Battery Charging Detector Register (32-bit only)
+} fsdev_regs_t;
+
+TU_VERIFY_STATIC(offsetof(fsdev_regs_t, CNTR) == 0x40, "Wrong offset");
+TU_VERIFY_STATIC(sizeof(fsdev_regs_t) == 0x5C, "Size is not correct");
+
+#define FSDEV_REG ((fsdev_regs_t*) FSDEV_REG_BASE)
+
+
+#ifndef USB_EPTX_STAT
+#define USB_EPTX_STAT 0x0030U
+#endif
+
+#ifndef USB_EPRX_STAT
+#define USB_EPRX_STAT 0x3000U
+#endif
+
+#ifndef USB_EPTX_STAT_Pos
+#define USB_EPTX_STAT_Pos    4u
+#endif
+
+#ifndef USB_EP_DTOG_TX_Pos
+#define USB_EP_DTOG_TX_Pos   6u
+#endif
+
+#ifndef USB_EP_CTR_TX_Pos
+#define USB_EP_CTR_TX_Pos    7u
+#endif
+
+
+#define EP_CTR_TXRX (USB_EP_CTR_TX | USB_EP_CTR_RX)
+
+typedef enum {
+  EP_STAT_DISABLED = 0,
+  EP_STAT_STALL = 1,
+  EP_STAT_NAK = 2,
+  EP_STAT_VALID = 3
+}ep_stat_t;
+
+#define EP_STAT_MASK(_dir)  (3u << (USB_EPTX_STAT_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
+#define EP_DTOG_MASK(_dir)  (1u << (USB_EP_DTOG_TX_Pos + ((_dir) == TUSB_DIR_IN ? 0 : 8)))
+
+//--------------------------------------------------------------------+
+// Endpoint Helper
+// - CTR is write 0 to clear
+// - DTOG and STAT are write 1 to toggle
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline void ep_write(uint32_t ep_id, uint32_t value) {
+  FSDEV_REG->ep[ep_id].reg = (fsdev_bus_t) value;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_read(uint32_t ep_id) {
+  return FSDEV_REG->ep[ep_id].reg;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_add_status(uint32_t reg, tusb_dir_t dir, ep_stat_t state) {
+  return reg ^ (state << (USB_EPTX_STAT_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_add_dtog(uint32_t reg, tusb_dir_t dir, uint8_t state) {
+  return reg ^ (state << (USB_EP_DTOG_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t ep_clear_ctr(uint32_t reg, tusb_dir_t dir) {
+  return reg & ~(1 << (USB_EP_CTR_TX_Pos + (dir == TUSB_DIR_IN ? 0 : 8)));
+}
+
+TU_ATTR_ALWAYS_INLINE static inline bool ep_is_iso(uint32_t reg) {
+  return (reg & USB_EP_TYPE_MASK) == USB_EP_ISOCHRONOUS;
+}
+
+//--------------------------------------------------------------------+
+// BTable Helper
+//--------------------------------------------------------------------+
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_addr(uint32_t ep_id, uint8_t buf_id) {
+#ifdef FSDEV_BUS_32BIT
+  return FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr & 0x0000FFFFu;
+#else
+  return FSDEV_BTABLE->ep16[ep_id][buf_id].addr;
+#endif
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void btable_set_addr(uint32_t ep_id, uint8_t buf_id, uint16_t addr) {
+#ifdef FSDEV_BUS_32BIT
+  uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+  count_addr = (count_addr & 0xFFFF0000u) | (addr & 0x0000FFFCu);
+  FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+  FSDEV_BTABLE->ep16[ep_id][buf_id].addr = addr;
+#endif
+}
+
+TU_ATTR_ALWAYS_INLINE static inline uint32_t btable_get_count(uint32_t ep_id, uint8_t buf_id) {
+  uint16_t count;
+#ifdef FSDEV_BUS_32BIT
+  count = (FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr >> 16);
+#else
+  count = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
+#endif
+  return count & 0x3FFU;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void btable_set_count(uint32_t ep_id, uint8_t buf_id, uint16_t byte_count) {
+#ifdef FSDEV_BUS_32BIT
+  uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+  count_addr = (count_addr & ~0x03FF0000u) | ((byte_count & 0x3FFu) << 16);
+  FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+  uint16_t cnt = FSDEV_BTABLE->ep16[ep_id][buf_id].count;
+  cnt = (cnt & ~0x3FFU) | (byte_count & 0x3FFU);
+  FSDEV_BTABLE->ep16[ep_id][buf_id].count = cnt;
+#endif
+}
+
+/* Aligned buffer size according to hardware */
+TU_ATTR_ALWAYS_INLINE static inline uint16_t pma_align_buffer_size(uint16_t size, uint8_t* blsize, uint8_t* num_block) {
+  /* The STM32 full speed USB peripheral supports only a limited set of
+   * buffer sizes given by the RX buffer entry format in the USB_BTABLE. */
+  uint16_t block_in_bytes;
+  if (size > 62) {
+    block_in_bytes = 32;
+    *blsize = 1;
+  } else {
+    block_in_bytes = 2;
+    *blsize = 0;
+  }
+
+  *num_block = tu_div_ceil(size, block_in_bytes);
+
+  return (*num_block) * block_in_bytes;
+}
+
+TU_ATTR_ALWAYS_INLINE static inline void btable_set_rx_bufsize(uint32_t ep_id, uint8_t buf_id, uint32_t wCount) {
+  uint8_t blsize, num_block;
+  (void) pma_align_buffer_size(wCount, &blsize, &num_block);
+
+  /* Encode into register. When BLSIZE==1, we need to subtract 1 block count */
+  uint16_t bl_nb = (blsize << 15) | ((num_block - blsize) << 10);
+
+#ifdef FSDEV_BUS_32BIT
+  uint32_t count_addr = FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr;
+  count_addr = (bl_nb << 16) | (count_addr & 0x0000FFFFu);
+  FSDEV_BTABLE->ep32[ep_id][buf_id].count_addr = count_addr;
+#else
+  FSDEV_BTABLE->ep16[ep_id][buf_id].count = bl_nb;
+#endif
+}
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/src/portable/sunxi/dcd_sunxi_musb.c b/src/portable/sunxi/dcd_sunxi_musb.c
index 6cc1975a8..6f36ad441 100644
--- a/src/portable/sunxi/dcd_sunxi_musb.c
+++ b/src/portable/sunxi/dcd_sunxi_musb.c
@@ -35,7 +35,9 @@
 #include 
 #include 
 #include "musb_def.h"
-#include "bsp/board.h"
+
+//#include "bsp/board_api.h"
+extern uint32_t board_millis(void); // TODO remove
 
 typedef uint32_t u32;
 typedef uint16_t u16;
@@ -58,7 +60,7 @@ typedef struct TU_ATTR_PACKED
 
 typedef struct
 {
-  tusb_control_request_t setup_packet;
+  CFG_TUD_MEM_ALIGN tusb_control_request_t setup_packet;
   uint16_t     remaining_ctrl; /* The number of bytes remaining in data stage of control transfer. */
   int8_t       status_out;
   pipe_state_t pipe0;
@@ -350,7 +352,7 @@ static void USBC_INT_DisableRxEp(u8 ep_index)
  * INTERNAL FUNCTION DECLARATION
  *------------------------------------------------------------------*/
 
-static dcd_data_t _dcd;
+CFG_TUD_MEM_ALIGN static dcd_data_t _dcd;
 
 static inline free_block_t *find_containing_block(free_block_t *beg, free_block_t *end, uint_fast16_t addr)
 {
@@ -560,7 +562,7 @@ static void pipe_read_write_packet_ff(tu_fifo_t *f, volatile void *fifo, unsigne
 
 static void process_setup_packet(uint8_t rhport)
 {
-  uint32_t *p = (uint32_t*)&_dcd.setup_packet;
+  uint32_t *p = (uint32_t*)(uintptr_t) &_dcd.setup_packet;
   p[0]        = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE));
   p[1]        = USBC_Readl(USBC_REG_EPFIFO0(USBC0_BASE));
 
@@ -594,7 +596,7 @@ static bool handle_xfer_in(uint_fast8_t ep_addr)
   if (len) {
     volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2));
     if (_dcd.pipe_buf_is_fifo[TUSB_DIR_IN] & TU_BIT(epnum_minus1)) {
-      pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_IN);
+      pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t) buf, addr, len, TUSB_DIR_IN);
     } else {
       pipe_write_packet(buf, addr, len);
       pipe->buf       = buf + len;
@@ -622,7 +624,7 @@ static bool handle_xfer_out(uint_fast8_t ep_addr)
   if (len) {
     volatile void* addr = (volatile void*)(USBC_REG_EPFIFO1(USBC0_BASE) + (epnum_minus1 << 2));
     if (_dcd.pipe_buf_is_fifo[TUSB_DIR_OUT] & TU_BIT(epnum_minus1)) {
-      pipe_read_write_packet_ff((tu_fifo_t *)buf, addr, len, TUSB_DIR_OUT);
+      pipe_read_write_packet_ff((tu_fifo_t *)(uintptr_t )buf, addr, len, TUSB_DIR_OUT);
     } else {
       pipe_read_packet(buf, addr, len);
       pipe->buf       = buf + len;
diff --git a/src/portable/synopsys/dwc2/dcd_dwc2.c b/src/portable/synopsys/dwc2/dcd_dwc2.c
index 8b13b0753..9a38b46dc 100644
--- a/src/portable/synopsys/dwc2/dcd_dwc2.c
+++ b/src/portable/synopsys/dwc2/dcd_dwc2.c
@@ -112,6 +112,17 @@ static inline uint16_t calc_grxfsiz(uint16_t max_ep_size, uint8_t ep_count) {
   return 15 + 2 * (max_ep_size / 4) + 2 * ep_count;
 }
 
+TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_tx(dwc2_regs_t* dwc2, uint8_t epnum) {
+  // flush TX fifo and wait for it cleared
+  dwc2->grstctl = GRSTCTL_TXFFLSH | (epnum << GRSTCTL_TXFNUM_Pos);
+  while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
+}
+TU_ATTR_ALWAYS_INLINE static inline void fifo_flush_rx(dwc2_regs_t* dwc2) {
+  // flush RX fifo and wait for it cleared
+  dwc2->grstctl = GRSTCTL_RXFFLSH;
+  while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+}
+
 static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
   dwc2_regs_t* dwc2 = DWC2_REG(rhport);
   uint8_t const ep_count = _dwc2_controller[rhport].ep_count;
@@ -120,7 +131,7 @@ static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
 
   TU_ASSERT(epnum < ep_count);
 
-  uint16_t const fifo_size = tu_div_ceil(packet_size, 4);
+  uint16_t fifo_size = tu_div_ceil(packet_size, 4);
 
   // "USB Data FIFOs" section in reference manual
   // Peripheral FIFO architecture
@@ -154,10 +165,16 @@ static bool fifo_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t packet_size) {
       dwc2->grxfsiz = sz;
     }
   } else {
+    // Note if The TXFELVL is configured as half empty. In order
+    // to be able to write a packet at that point, the fifo must be twice the max_size.
+    if ((dwc2->gahbcfg & GAHBCFG_TXFELVL) == 0) {
+      fifo_size *= 2;
+    }
+
     // Check if free space is available
     TU_ASSERT(_allocated_fifo_words_tx + fifo_size + dwc2->grxfsiz <= _dwc2_controller[rhport].ep_fifo_size / 4);
     _allocated_fifo_words_tx += fifo_size;
-    TU_LOG(DWC2_DEBUG, "    Allocated %u bytes at offset %lu", fifo_size * 4,
+    TU_LOG(DWC2_DEBUG, "    Allocated %u bytes at offset %" PRIu32, fifo_size * 4,
            _dwc2_controller[rhport].ep_fifo_size - _allocated_fifo_words_tx * 4);
 
     // DIEPTXF starts at FIFO #1.
@@ -185,10 +202,10 @@ static void edpt_activate(uint8_t rhport, tusb_desc_endpoint_t const * p_endpoin
                            (xfer->max_size << DOEPCTL_MPSIZ_Pos);
 
   if (dir == TUSB_DIR_OUT) {
-    dwc2->epout[epnum].doepctl |= dxepctl;
+    dwc2->epout[epnum].doepctl = dxepctl;
     dwc2->daintmsk |= TU_BIT(DAINTMSK_OEPM_Pos + epnum);
   } else {
-    dwc2->epin[epnum].diepctl |= dxepctl | (epnum << DIEPCTL_TXFNUM_Pos);
+    dwc2->epin[epnum].diepctl = dxepctl | (epnum << DIEPCTL_TXFNUM_Pos);
     dwc2->daintmsk |= (1 << (DAINTMSK_IEPM_Pos + epnum));
   }
 }
@@ -219,8 +236,7 @@ static void edpt_disable(uint8_t rhport, uint8_t ep_addr, bool stall) {
     }
 
     // Flush the FIFO, and wait until we have confirmed it cleared.
-    dwc2->grstctl = ((epnum << GRSTCTL_TXFNUM_Pos) | GRSTCTL_TXFFLSH);
-    while ((dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) != 0) {}
+    fifo_flush_tx(dwc2, epnum);
   } else {
     dwc2_epout_t* epout = dwc2->epout;
 
@@ -264,15 +280,17 @@ static void bus_reset(uint8_t rhport) {
     dwc2->epout[n].doepctl |= DOEPCTL_SNAK;
   }
 
-  // flush all TX fifo and wait for it cleared
-  dwc2->grstctl = GRSTCTL_TXFFLSH | (0x10u << GRSTCTL_TXFNUM_Pos);
-  while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
+  // 2. Disable all IN endpoints
+  for (uint8_t n = 0; n < ep_count; n++) {
+    if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
+      dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
+    }
+  }
 
-  // flush RX fifo and wait for it cleared
-  dwc2->grstctl = GRSTCTL_RXFFLSH;
-  while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+  fifo_flush_tx(dwc2, 0x10); // all tx fifo
+  fifo_flush_rx(dwc2);
 
-  // 2. Set up interrupt mask
+  // 3. Set up interrupt mask
   dwc2->daintmsk = TU_BIT(DAINTMSK_OEPM_Pos) | TU_BIT(DAINTMSK_IEPM_Pos);
   dwc2->doepmsk = DOEPMSK_STUPM | DOEPMSK_XFRCM;
   dwc2->diepmsk = DIEPMSK_TOM | DIEPMSK_XFRCM;
@@ -408,9 +426,9 @@ void print_dwc2_info(dwc2_regs_t* dwc2) {
   volatile uint32_t const* p = (volatile uint32_t const*) &dwc2->guid;
   TU_LOG(DWC2_DEBUG, "guid, gsnpsid, ghwcfg1, ghwcfg2, ghwcfg3, ghwcfg4\r\n");
   for (size_t i = 0; i < 5; i++) {
-    TU_LOG(DWC2_DEBUG, "0x%08lX, ", p[i]);
+    TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 ", ", p[i]);
   }
-  TU_LOG(DWC2_DEBUG, "0x%08lX\r\n", p[5]);
+  TU_LOG(DWC2_DEBUG, "0x%08" PRIX32 "\r\n", p[5]);
 }
 #endif
 
@@ -429,11 +447,15 @@ static void reset_core(dwc2_regs_t* dwc2) {
 }
 
 static bool phy_hs_supported(dwc2_regs_t* dwc2) {
-  // note: esp32 incorrect report its hs_phy_type as utmi
+  (void) dwc2;
+
 #if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3)
+  // note: esp32 incorrect report its hs_phy_type as utmi
+  return false;
+#elif !TUD_OPT_HIGH_SPEED
   return false;
 #else
-  return TUD_OPT_HIGH_SPEED && dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
+  return dwc2->ghwcfg2_bm.hs_phy_type != HS_PHY_TYPE_NONE;
 #endif
 }
 
@@ -578,13 +600,8 @@ void dcd_init(uint8_t rhport) {
   // (non zero-length packet), send STALL back and discard.
   dwc2->dcfg |= DCFG_NZLSOHSK;
 
-  // flush all TX fifo and wait for it cleared
-  dwc2->grstctl = GRSTCTL_TXFFLSH | (0x10u << GRSTCTL_TXFNUM_Pos);
-  while (dwc2->grstctl & GRSTCTL_TXFFLSH_Msk) {}
-
-  // flush RX fifo and wait for it cleared
-  dwc2->grstctl = GRSTCTL_RXFFLSH;
-  while (dwc2->grstctl & GRSTCTL_RXFFLSH_Msk) {}
+  fifo_flush_tx(dwc2, 0x10); // all tx fifo
+  fifo_flush_rx(dwc2);
 
   // Clear all interrupts
   uint32_t int_mask = dwc2->gintsts;
@@ -596,6 +613,9 @@ void dcd_init(uint8_t rhport) {
   dwc2->gintmsk = GINTMSK_OTGINT | GINTMSK_RXFLVLM |
                   GINTMSK_USBSUSPM | GINTMSK_USBRST | GINTMSK_ENUMDNEM | GINTMSK_WUIM;
 
+  // Configure TX FIFO empty level for interrupt. Default is complete empty
+  dwc2->gahbcfg |= GAHBCFG_TXFELVL;
+
   // Enable global interrupt
   dwc2->gahbcfg |= GAHBCFG_GINT;
 
@@ -691,16 +711,25 @@ void dcd_edpt_close_all(uint8_t rhport) {
 
   for (uint8_t n = 1; n < ep_count; n++) {
     // disable OUT endpoint
-    dwc2->epout[n].doepctl = 0;
+    if (dwc2->epout[n].doepctl & DOEPCTL_EPENA) {
+      dwc2->epout[n].doepctl |= DOEPCTL_SNAK | DOEPCTL_EPDIS;
+    }
     xfer_status[n][TUSB_DIR_OUT].max_size = 0;
 
     // disable IN endpoint
-    dwc2->epin[n].diepctl = 0;
+    if (dwc2->epin[n].diepctl & DIEPCTL_EPENA) {
+      dwc2->epin[n].diepctl |= DIEPCTL_SNAK | DIEPCTL_EPDIS;
+    }
     xfer_status[n][TUSB_DIR_IN].max_size = 0;
   }
 
+  // reset allocated fifo OUT
+  dwc2->grxfsiz = calc_grxfsiz(64, ep_count);
   // reset allocated fifo IN
   _allocated_fifo_words_tx = 16;
+
+  fifo_flush_tx(dwc2, 0x10); // all tx fifo
+  fifo_flush_rx(dwc2);
 }
 
 bool dcd_edpt_iso_alloc(uint8_t rhport, uint8_t ep_addr, uint16_t largest_packet_size) {
@@ -1122,16 +1151,14 @@ void dcd_int_handler(uint8_t rhport) {
 
   if(int_status & GINTSTS_SOF) {
     dwc2->gintsts = GINTSTS_SOF;
+    const uint32_t frame = (dwc2->dsts & DSTS_FNSOF) >> DSTS_FNSOF_Pos;
 
-    if (_sof_en) {
-      uint32_t frame = (dwc2->dsts & (DSTS_FNSOF)) >> 8;
-      dcd_event_sof(rhport, frame, true);
-    } else {
-      // Disable SOF interrupt if SOF was not explicitly enabled. SOF was used for remote wakeup detection
+    // Disable SOF interrupt if SOF was not explicitly enabled since SOF was used for remote wakeup detection
+    if (!_sof_en) {
       dwc2->gintmsk &= ~GINTMSK_SOFM;
     }
 
-    dcd_event_bus_signal(rhport, DCD_EVENT_SOF, true);
+    dcd_event_sof(rhport, frame, true);
   }
 
   // RxFIFO non-empty interrupt handling.
@@ -1144,7 +1171,7 @@ void dcd_int_handler(uint8_t rhport) {
     // Loop until all available packets were handled
     do {
       handle_rxflvl_irq(rhport);
-    } while (dwc2->gotgint & GINTSTS_RXFLVL);
+    } while(dwc2->gintsts & GINTSTS_RXFLVL);
 
     dwc2->gintmsk |= GINTMSK_RXFLVLM;
   }
@@ -1168,4 +1195,13 @@ void dcd_int_handler(uint8_t rhport) {
   //  }
 }
 
+#if CFG_TUD_TEST_MODE
+void dcd_enter_test_mode(uint8_t rhport, tusb_feature_test_mode_t test_selector) {
+  dwc2_regs_t* dwc2 = DWC2_REG(rhport);
+
+  // Enable the test mode
+  dwc2->dctl = (dwc2->dctl & ~DCTL_TCTL_Msk) | (((uint8_t) test_selector) << DCTL_TCTL_Pos);
+}
+#endif
+
 #endif
diff --git a/src/portable/synopsys/dwc2/dwc2_esp32.h b/src/portable/synopsys/dwc2/dwc2_esp32.h
index c50dd66b8..932f52f40 100644
--- a/src/portable/synopsys/dwc2/dwc2_esp32.h
+++ b/src/portable/synopsys/dwc2/dwc2_esp32.h
@@ -35,6 +35,7 @@
 #include "esp_intr_alloc.h"
 #include "soc/periph_defs.h"
 //#include "soc/usb_periph.h"
+#include "freertos/task.h"
 
 #define DWC2_REG_BASE       0x60080000UL
 #define DWC2_EP_MAX         6             // USB_OUT_EP_NUM. TODO ESP32Sx only has 5 tx fifo (5 endpoint IN)
diff --git a/src/portable/synopsys/dwc2/dwc2_type.h b/src/portable/synopsys/dwc2/dwc2_type.h
index c15771237..cb694b326 100644
--- a/src/portable/synopsys/dwc2/dwc2_type.h
+++ b/src/portable/synopsys/dwc2/dwc2_type.h
@@ -1,17 +1,34 @@
-/**
-  * @author  MCD Application Team
-  *          Ha Thach (tinyusb.org)
-  *
-  * @attention
-  *
-  * © Copyright (c) 2019 STMicroelectronics.
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024, hathach (tinyusb.org)
+ *
+ * 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.
+ *
+ */
+/** © Copyright (c) 2019 STMicroelectronics.
   * All rights reserved.
   *
   * This software component is licensed by ST under BSD 3-Clause license,
   * the "License"; You may not use this file except in compliance with the
   * License. You may obtain a copy of the License at:
   *                        opensource.org/licenses/BSD-3-Clause
-  *
   */
 
 #ifndef _TUSB_DWC2_TYPES_H_
diff --git a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
index b4dfda575..8005f5f7b 100644
--- a/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
+++ b/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
@@ -94,7 +94,8 @@ static void bus_reset(void)
   USBOEPCNT_0 &= ~NAK;
   USBIEPCNT_0 &= ~NAK;
 
-  USBCTL |= FEN; // Enable responding to packets.
+  // Enable responding to packets.
+  USBCTL |= FEN;
 
   // Dedicated buffers in hardware for SETUP and EP0, no setup needed.
   // Now safe to respond to SETUP packets.
@@ -103,6 +104,28 @@ static void bus_reset(void)
   USBKEYPID = 0;
 }
 
+// Controls reset behavior of the USB module on receipt of a bus reset event.
+// - enable: When true, bus reset events will cause a reset the USB module.
+static void enable_functional_reset(const bool enable)
+{
+  // Check whether or not the USB configuration registers were
+  // locked prior to this function being called so that, if
+  // necessary, the lock state can be restored on exit.
+  bool unlocked = (USBKEYPID == 0xA528) ? true : false;
+
+  if(!unlocked) USBKEYPID = USBKEY;
+
+  if(enable)
+  {
+    USBCTL |= FRSTE;
+  }
+  else
+  {
+    USBCTL &= ~FRSTE;
+  }
+
+  if(!unlocked) USBKEYPID = 0;
+}
 
 /*------------------------------------------------------------------*/
 /* Controller API
@@ -131,11 +154,14 @@ void dcd_init (uint8_t rhport)
 
   USBVECINT = 0;
 
-  // Enable reset and wait for it before continuing.
-  USBIE |= RSTRIE;
-
-  // Enable pullup.
-  USBCNF |= PUR_EN;
+  if(USBPWRCTL & USBBGVBV) {// Bus power detected?
+    USBPWRCTL |= VBOFFIE;   // Enable bus-power-removed interrupt.
+    USBIE |= RSTRIE;        // Enable reset and wait for it before continuing.
+    USBCNF |= PUR_EN;       // Enable pullup.
+  } else {
+    USBPWRCTL |= VBONIE;    // Enable bus-power-applied interrupt.
+    USBCNF &= ~USB_EN;      // Disable USB module until bus power is detected.
+  }
 
   USBKEYPID = 0;
 }
@@ -610,14 +636,76 @@ static void handle_setup_packet(void)
     _setup_packet[i] = setup_buf[i];
   }
 
-  // Clearing SETUPIFG by reading USBVECINT does not set NAK, so now that we
-  // have a SETUP packet, force NAKs until tinyusb can handle the SETUP
-  // packet and prepare for a new xfer.
+  // Force NAKs until tinyusb can handle the SETUP packet and prepare for a new xfer.
   USBIEPCNT_0 |= NAK;
   USBOEPCNT_0 |= NAK;
+
+  // Clear SETUPIFG to avoid handling in the USBVECINT switch statement.
+  // When handled there the NAKs applied to the endpoints above are
+  // cleared by hardware and the host will receive stale/duplicate data.
+  //
+  // Excerpt from MSP430x5xx and MSP430x6xx Family User's Guide:
+  //
+  // "...the SETUPIFG is cleared upon reading USBIV. In addition, the NAK on
+  // input endpoint 0 and output endpoint 0 is also cleared."
+  USBIEPCNF_0 &= ~UBME; // Errata USB10 workaround.
+  USBOEPCNF_0 &= ~UBME; // Errata USB10 workaround.
+  USBIFG &= ~SETUPIFG;
+  USBIEPCNF_0 |= UBME;  // Errata USB10 workaround.
+  USBOEPCNF_0 |= UBME;  // Errata USB10 workaround.
   dcd_event_setup_received(0, (uint8_t*) &_setup_packet[0], true);
 }
 
+#if CFG_TUSB_OS == OPT_OS_NONE
+TU_ATTR_ALWAYS_INLINE static inline void tu_delay(uint32_t ms) {
+  // msp430 can run up to 25Mhz -> 40ns per cycle. 1 ms = 25000 cycles
+  // each loop need 4 cycle: 1 sub, 1 cmp, 1 jump, 1 nop
+  volatile uint32_t cycles = (25000 * ms) >> 2;
+  while (cycles > 0) {
+    cycles--;
+    asm("nop");
+  }
+}
+#else
+#define tu_delay(ms) osal_task_delay(ms)
+#endif
+
+static void handle_bus_power_event(void *param) {
+  (void) param;
+
+  tu_delay(5);                 // Bus power settling delay.
+
+  USBKEYPID = USBKEY;
+
+  if(USBPWRCTL & USBBGVBV) {          // Event caused by application of bus power.
+    USBPWRCTL |= VBOFFIE;             // Enable bus-power-removed interrupt.
+    USBPLLDIVB = USBPLLDIVB;          // For some reason the PLL will *NOT* lock unless the divider
+                                      // register is re-written. The assumption here is that this
+                                      // register was already properly configured during board-level
+                                      // initialization.
+    USBPLLCTL |= (UPLLEN | UPFDEN);   // Enable the PLL.
+
+    uint16_t attempts = 0;
+    do {                              // Poll the PLL, checking for a successful lock.
+      USBPLLIR = 0;
+      tu_delay(1);
+      attempts++;
+    } while ((attempts < 10) && (USBPLLIR != 0));
+
+    // A successful lock is indicated by all PLL-related interrupt flags being cleared.
+    if(!USBPLLIR) {
+      dcd_init(0);                    // Re-initialize the USB module.
+    }
+  } else {                            // Event caused by removal of bus power.
+    USBPWRCTL |= VBONIE;              // Enable bus-power-applied interrupt.
+    USBPLLCTL &= ~(UPLLEN | UPFDEN);  // Disable the PLL.
+    USBCNF = 0;                       // Disable the USB module.
+    dcd_event_bus_signal(0, DCD_EVENT_UNPLUGGED, false);
+  }
+
+  USBKEYPID = 0;
+}
+
 void dcd_int_handler(uint8_t rhport)
 {
   (void) rhport;
@@ -628,6 +716,7 @@ void dcd_int_handler(uint8_t rhport)
 
   if(setup_status)
   {
+    enable_functional_reset(true);
     handle_setup_packet();
   }
 
@@ -646,11 +735,32 @@ void dcd_int_handler(uint8_t rhport)
 
   switch(curr_vector)
   {
+    case USBVECINT_NONE:
+      break;
+
     case USBVECINT_RSTR:
+      enable_functional_reset(false); // Errata USB4 workaround.
       bus_reset();
       dcd_event_bus_reset(0, TUSB_SPEED_FULL, true);
       break;
 
+    case USBVECINT_PWR_VBUSOn:
+    case USBVECINT_PWR_VBUSOff: {
+      USBKEYPID = USBKEY;
+      // Prevent (possibly) unstable power from generating spurious interrupts.
+      USBPWRCTL &= ~(VBONIE | VBOFFIE);
+      USBKEYPID = 0;
+
+      dcd_event_t event;
+
+      event.rhport = 0;
+      event.event_id = USBD_EVENT_FUNC_CALL;
+      event.func_call.func = handle_bus_power_event;
+
+      dcd_event_handler(&event, true);
+      }
+      break;
+
     // Clear the (hardware-enforced) NAK on EP 0 after a SETUP packet
     // is received. At this point, even though the hardware is no longer
     // forcing NAKs, the EP0 NAK bits should still be set to avoid
@@ -675,10 +785,12 @@ void dcd_int_handler(uint8_t rhport)
       break;
 
     case USBVECINT_INPUT_ENDPOINT0:
+      enable_functional_reset(true);
       transmit_packet(0);
       break;
 
     case USBVECINT_OUTPUT_ENDPOINT0:
+      enable_functional_reset(true);
       receive_packet(0);
       break;
 
@@ -710,7 +822,6 @@ void dcd_int_handler(uint8_t rhport)
 
     default:
       while(true);
-      break;
   }
 
 }
diff --git a/src/portable/wch/ch32_usbfs_reg.h b/src/portable/wch/ch32_usbfs_reg.h
new file mode 100644
index 000000000..68be64f5e
--- /dev/null
+++ b/src/portable/wch/ch32_usbfs_reg.h
@@ -0,0 +1,175 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Matthew Tran
+ * Copyright (c) 2024 hathach
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+
+#ifndef USB_CH32_USBFS_REG_H
+#define USB_CH32_USBFS_REG_H
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
+#endif
+
+#if CFG_TUSB_MCU == OPT_MCU_CH32F20X
+  #include 
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V103
+  #include 
+  typedef struct
+  {
+    __IO uint8_t  BASE_CTRL;
+    __IO uint8_t  UDEV_CTRL;
+    __IO uint8_t  INT_EN;
+    __IO uint8_t  DEV_ADDR;
+    __IO uint8_t  Reserve0;
+    __IO uint8_t  MIS_ST;
+    __IO uint8_t  INT_FG;
+    __IO uint8_t  INT_ST;
+    __IO uint32_t RX_LEN;
+    __IO uint8_t  UEP4_1_MOD;
+    __IO uint8_t  UEP2_3_MOD;
+    __IO uint8_t  UEP5_6_MOD;
+    __IO uint8_t  UEP7_MOD;
+    __IO uint32_t UEP0_DMA;
+    __IO uint32_t UEP1_DMA;
+    __IO uint32_t UEP2_DMA;
+    __IO uint32_t UEP3_DMA;
+    __IO uint32_t UEP4_DMA;
+    __IO uint32_t UEP5_DMA;
+    __IO uint32_t UEP6_DMA;
+    __IO uint32_t UEP7_DMA;
+    __IO uint16_t UEP0_TX_LEN;
+    __IO uint8_t  UEP0_TX_CTRL;
+    __IO uint8_t  UEP0_RX_CTRL;
+    __IO uint16_t UEP1_TX_LEN;
+    __IO uint8_t  UEP1_TX_CTRL;
+    __IO uint8_t  UEP1_RX_CTRL;
+    __IO uint16_t UEP2_TX_LEN;
+    __IO uint8_t  UEP2_TX_CTRL;
+    __IO uint8_t  UEP2_RX_CTRL;
+    __IO uint16_t UEP3_TX_LEN;
+    __IO uint8_t  UEP3_TX_CTRL;
+    __IO uint8_t  UEP3_RX_CTRL;
+    __IO uint16_t UEP4_TX_LEN;
+    __IO uint8_t  UEP4_TX_CTRL;
+    __IO uint8_t  UEP4_RX_CTRL;
+    __IO uint16_t UEP5_TX_LEN;
+    __IO uint8_t  UEP5_TX_CTRL;
+    __IO uint8_t  UEP5_RX_CTRL;
+    __IO uint16_t UEP6_TX_LEN;
+    __IO uint8_t  UEP6_TX_CTRL;
+    __IO uint8_t  UEP6_RX_CTRL;
+    __IO uint16_t UEP7_TX_LEN;
+    __IO uint8_t  UEP7_TX_CTRL;
+    __IO uint8_t  UEP7_RX_CTRL;
+    __IO uint32_t Reserve1;
+    __IO uint32_t OTG_CR;
+    __IO uint32_t OTG_SR;
+  } USBOTG_FS_TypeDef;
+
+  #define USBOTG_FS ((USBOTG_FS_TypeDef *) 0x40023400)
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V20X
+  #include 
+#elif CFG_TUSB_MCU == OPT_MCU_CH32V307
+  #include 
+  #define USBHD_IRQn OTG_FS_IRQn
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+// CTRL
+#define USBFS_CTRL_DMA_EN    (1 << 0)
+#define USBFS_CTRL_CLR_ALL   (1 << 1)
+#define USBFS_CTRL_RESET_SIE (1 << 2)
+#define USBFS_CTRL_INT_BUSY  (1 << 3)
+#define USBFS_CTRL_SYS_CTRL  (1 << 4)
+#define USBFS_CTRL_DEV_PUEN  (1 << 5)
+#define USBFS_CTRL_LOW_SPEED (1 << 6)
+#define USBFS_CTRL_HOST_MODE (1 << 7)
+
+// INT_EN
+#define USBFS_INT_EN_BUS_RST  (1 << 0)
+#define USBFS_INT_EN_DETECT   (1 << 0)
+#define USBFS_INT_EN_TRANSFER (1 << 1)
+#define USBFS_INT_EN_SUSPEND  (1 << 2)
+#define USBFS_INT_EN_HST_SOF  (1 << 3)
+#define USBFS_INT_EN_FIFO_OV  (1 << 4)
+#define USBFS_INT_EN_DEV_NAK  (1 << 6)
+#define USBFS_INT_EN_DEV_SOF  (1 << 7)
+
+// INT_FG
+#define USBFS_INT_FG_BUS_RST  (1 << 0)
+#define USBFS_INT_FG_DETECT   (1 << 0)
+#define USBFS_INT_FG_TRANSFER (1 << 1)
+#define USBFS_INT_FG_SUSPEND  (1 << 2)
+#define USBFS_INT_FG_HST_SOF  (1 << 3)
+#define USBFS_INT_FG_FIFO_OV  (1 << 4)
+#define USBFS_INT_FG_SIE_FREE (1 << 5)
+#define USBFS_INT_FG_TOG_OK   (1 << 6)
+#define USBFS_INT_FG_IS_NAK   (1 << 7)
+
+// INT_ST
+#define USBFS_INT_ST_MASK_UIS_ENDP(x)  (((x) >> 0) & 0x0F)
+#define USBFS_INT_ST_MASK_UIS_TOKEN(x) (((x) >> 4) & 0x03)
+
+// UDEV_CTRL
+#define USBFS_UDEV_CTRL_PORT_EN   (1 << 0)
+#define USBFS_UDEV_CTRL_GP_BIT    (1 << 1)
+#define USBFS_UDEV_CTRL_LOW_SPEED (1 << 2)
+#define USBFS_UDEV_CTRL_DM_PIN    (1 << 4)
+#define USBFS_UDEV_CTRL_DP_PIN    (1 << 5)
+#define USBFS_UDEV_CTRL_PD_DIS    (1 << 7)
+
+// TX_CTRL
+#define USBFS_EP_T_RES_MASK (3 << 0)
+#define USBFS_EP_T_TOG      (1 << 2)
+#define USBFS_EP_T_AUTO_TOG (1 << 3)
+
+#define USBFS_EP_T_RES_ACK   (0 << 0)
+#define USBFS_EP_T_RES_NYET  (1 << 0)
+#define USBFS_EP_T_RES_NAK   (2 << 0)
+#define USBFS_EP_T_RES_STALL (3 << 0)
+
+// RX_CTRL
+#define USBFS_EP_R_RES_MASK (3 << 0)
+#define USBFS_EP_R_TOG      (1 << 2)
+#define USBFS_EP_R_AUTO_TOG (1 << 3)
+
+#define USBFS_EP_R_RES_ACK   (0 << 0)
+#define USBFS_EP_R_RES_NYET  (1 << 0)
+#define USBFS_EP_R_RES_NAK   (2 << 0)
+#define USBFS_EP_R_RES_STALL (3 << 0)
+
+// token PID
+#define PID_OUT   0
+#define PID_SOF   1
+#define PID_IN    2
+#define PID_SETUP 3
+
+#endif // USB_CH32_USBFS_REG_H
diff --git a/src/portable/wch/ch32_usbhs_reg.h b/src/portable/wch/ch32_usbhs_reg.h
index 9b956231f..87300b497 100644
--- a/src/portable/wch/ch32_usbhs_reg.h
+++ b/src/portable/wch/ch32_usbhs_reg.h
@@ -1,12 +1,51 @@
-#ifndef _USB_CH32_USBHS_REG_H
-#define _USB_CH32_USBHS_REG_H
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Matthew Tran
+ * Copyright (c) 2024 hathach
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
 
-#if (CFG_TUSB_MCU == OPT_MCU_CH32V307)
-#include 
-#elif (CFG_TUSB_MCU == OPT_MCU_CH32F20X)
-#include 
+#ifndef USB_CH32_USBHS_REG_H
+#define USB_CH32_USBHS_REG_H
+
+// https://github.com/openwch/ch32v307/pull/90
+// https://github.com/openwch/ch32v20x/pull/12
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-prototypes"
 #endif
 
+#if CFG_TUSB_MCU == OPT_MCU_CH32V307
+  #include 
+#elif CFG_TUSB_MCU == OPT_MCU_CH32F20X
+  #include 
+#endif
+
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+
 /******************* GLOBAL ******************/
 
 // USB CONTROL
@@ -36,8 +75,13 @@
 
 // USB DEV AD
 #define USBHS_DEV_AD_OFFSET 0x03
+
 // USB FRAME_NO
 #define USBHS_FRAME_NO_OFFSET 0x04
+#define USBHS_FRAME_NO_NUM_MASK (0x7FF)
+#define USBHS_FRAME_NO_MICROFRAME_SHIFT (11)
+#define USBHS_FRAME_NO_MICROFRAME_MASK  (0x7 << USBHS_FRAME_NO_MICROFRAME_SHIFT)
+
 // USB SUSPEND
 #define USBHS_SUSPEND_OFFSET    0x06
 #define USBHS_DEV_REMOTE_WAKEUP (1 << 2)
@@ -47,7 +91,10 @@
 
 // USB SPEED TYPE
 #define USBHS_SPEED_TYPE_OFFSET 0x08
-#define USBSPEED_MASK           (0x03)
+#define USBHS_SPEED_TYPE_MASK    0x03
+#define USBHS_SPEED_TYPE_FULL    0
+#define USBHS_SPEED_TYPE_HIGH    1
+#define USBHS_SPEED_TYPE_LOW     2
 
 // USB_MIS_ST
 #define USBHS_MIS_ST_OFFSET 0x09
@@ -72,12 +119,16 @@
 #define USBHS_ISO_ACT_FLAG    (1 << 6)
 
 // INT_ST
-#define USBHS_INT_ST_OFFSET  0x0B
-#define USBHS_DEV_UIS_IS_NAK (1 << 7)
-#define USBHS_DEV_UIS_TOG_OK (1 << 6)
-#define MASK_UIS_TOKEN       (3 << 4)
-#define MASK_UIS_ENDP        (0x0F)
-#define MASK_UIS_H_RES       (0x0F)
+#define USBHS_INT_ST_OFFSET   0x0B
+#define USBHS_DEV_UIS_IS_NAK  (1 << 7)
+#define USBHS_DEV_UIS_TOG_OK  (1 << 6)
+#define MASK_UIS_TOKEN        (3 << 4)
+#define USBHS_TOKEN_PID_OUT   (0 << 4)
+#define USBHS_TOKEN_PID_SOF   (1 << 4)
+#define USBHS_TOKEN_PID_IN    (2 << 4)
+#define USBHS_TOKEN_PID_SETUP (3 << 4)
+#define MASK_UIS_ENDP         (0x0F)
+#define MASK_UIS_H_RES        (0x0F)
 
 #define USBHS_TOGGLE_OK (0x40)
 #define USBHS_HOST_RES  (0x0f)
@@ -340,10 +391,5 @@
 #define USBHS_UH_T_TOG_AUTO (1 << 5)
 #define USBHS_UH_T_DATA_NO  (1 << 6)
 
-// 00: OUT, 01:SOF, 10:IN, 11:SETUP
-#define PID_OUT   0
-#define PID_SOF   1
-#define PID_IN    2
-#define PID_SETUP 3
 
 #endif
diff --git a/src/portable/wch/dcd_ch32_usbfs.c b/src/portable/wch/dcd_ch32_usbfs.c
new file mode 100644
index 000000000..9ed370b40
--- /dev/null
+++ b/src/portable/wch/dcd_ch32_usbfs.c
@@ -0,0 +1,344 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2024 Matthew Tran
+ * Copyright (c) 2024 hathach
+ *
+ * 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 file is part of the TinyUSB stack.
+ */
+
+#include "tusb_option.h"
+
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBFS) && CFG_TUD_WCH_USBIP_USBFS
+
+#include "device/dcd.h"
+#include "ch32_usbfs_reg.h"
+
+/* private defines */
+#define EP_MAX (8)
+
+#define EP_DMA(ep)     ((&USBOTG_FS->UEP0_DMA)[ep])
+#define EP_TX_LEN(ep)  ((&USBOTG_FS->UEP0_TX_LEN)[2 * ep])
+#define EP_TX_CTRL(ep) ((&USBOTG_FS->UEP0_TX_CTRL)[4 * ep])
+#define EP_RX_CTRL(ep) ((&USBOTG_FS->UEP0_RX_CTRL)[4 * ep])
+
+/* private data */
+struct usb_xfer {
+  bool valid;
+  uint8_t* buffer;
+  size_t len;
+  size_t processed_len;
+  size_t max_size;
+};
+
+static struct {
+  bool ep0_tog;
+  bool isochronous[EP_MAX];
+  struct usb_xfer xfer[EP_MAX][2];
+  TU_ATTR_ALIGNED(4) uint8_t buffer[EP_MAX][2][64];
+  TU_ATTR_ALIGNED(4) struct {
+    // OUT transfers >64 bytes will overwrite queued IN data!
+    uint8_t out[64];
+    uint8_t in[1023];
+    uint8_t pad;
+  } ep3_buffer;
+} data;
+
+/* private helpers */
+static void update_in(uint8_t rhport, uint8_t ep, bool force) {
+  struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_IN];
+  if (xfer->valid) {
+    if (force || xfer->len) {
+      size_t len = TU_MIN(xfer->max_size, xfer->len);
+      if (ep == 0) {
+        memcpy(data.buffer[ep][TUSB_DIR_OUT], xfer->buffer, len); // ep0 uses same chunk
+      } else if (ep == 3) {
+        memcpy(data.ep3_buffer.in, xfer->buffer, len);
+      } else {
+        memcpy(data.buffer[ep][TUSB_DIR_IN], xfer->buffer, len);
+      }
+      xfer->buffer += len;
+      xfer->len -= len;
+      xfer->processed_len += len;
+
+      EP_TX_LEN(ep) = len;
+      if (ep == 0) {
+        EP_TX_CTRL(0) = USBFS_EP_T_RES_ACK | (data.ep0_tog ? USBFS_EP_T_TOG : 0);
+        data.ep0_tog = !data.ep0_tog;
+      } else if (data.isochronous[ep]) {
+        EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NYET;
+      } else {
+        EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_ACK;
+      }
+    } else {
+      xfer->valid = false;
+      EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK)) | USBFS_EP_T_RES_NAK;
+      dcd_event_xfer_complete(
+          rhport, ep | TUSB_DIR_IN_MASK, xfer->processed_len,
+          XFER_RESULT_SUCCESS, true);
+    }
+  }
+}
+
+static void update_out(uint8_t rhport, uint8_t ep, size_t rx_len) {
+  struct usb_xfer* xfer = &data.xfer[ep][TUSB_DIR_OUT];
+  if (xfer->valid) {
+    size_t len = TU_MIN(xfer->max_size, TU_MIN(xfer->len, rx_len));
+    if (ep == 3) {
+      memcpy(xfer->buffer, data.ep3_buffer.out, len);
+    } else {
+      memcpy(xfer->buffer, data.buffer[ep][TUSB_DIR_OUT], len);
+    }
+    xfer->buffer += len;
+    xfer->len -= len;
+    xfer->processed_len += len;
+
+    if (xfer->len == 0 || len < xfer->max_size) {
+      xfer->valid = false;
+      dcd_event_xfer_complete(rhport, ep, xfer->processed_len, XFER_RESULT_SUCCESS, true);
+    }
+
+    if (ep == 0) {
+      EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+    }
+  }
+}
+
+/* public functions */
+void dcd_init(uint8_t rhport) {
+  // init registers
+  USBOTG_FS->BASE_CTRL = USBFS_CTRL_SYS_CTRL | USBFS_CTRL_INT_BUSY | USBFS_CTRL_DMA_EN;
+  USBOTG_FS->UDEV_CTRL = USBFS_UDEV_CTRL_PD_DIS | USBFS_UDEV_CTRL_PORT_EN;
+  USBOTG_FS->DEV_ADDR = 0x00;
+
+  USBOTG_FS->INT_FG = 0xFF;
+  USBOTG_FS->INT_EN = USBFS_INT_EN_BUS_RST | USBFS_INT_EN_TRANSFER | USBFS_INT_EN_SUSPEND;
+
+  // setup endpoint 0
+  EP_DMA(0) = (uint32_t) &data.buffer[0][0];
+  EP_TX_LEN(0) = 0;
+  EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+  EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+  // enable other endpoints but NAK everything
+  USBOTG_FS->UEP4_1_MOD = 0xCC;
+  USBOTG_FS->UEP2_3_MOD = 0xCC;
+  USBOTG_FS->UEP5_6_MOD = 0xCC;
+  USBOTG_FS->UEP7_MOD = 0x0C;
+
+  for (uint8_t ep = 1; ep < EP_MAX; ep++) {
+    EP_DMA(ep) = (uint32_t) &data.buffer[ep][0];
+    EP_TX_LEN(ep) = 0;
+    EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
+    EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NAK;
+  }
+  EP_DMA(3) = (uint32_t) &data.ep3_buffer.out[0];
+
+  dcd_connect(rhport);
+}
+
+void dcd_int_handler(uint8_t rhport) {
+  (void) rhport;
+  uint8_t status = USBOTG_FS->INT_FG;
+  if (status & USBFS_INT_FG_TRANSFER) {
+    uint8_t ep = USBFS_INT_ST_MASK_UIS_ENDP(USBOTG_FS->INT_ST);
+    uint8_t token = USBFS_INT_ST_MASK_UIS_TOKEN(USBOTG_FS->INT_ST);
+
+    switch (token) {
+      case PID_OUT: {
+        uint16_t rx_len = USBOTG_FS->RX_LEN;
+        update_out(rhport, ep, rx_len);
+        break;
+      }
+
+      case PID_IN:
+        update_in(rhport, ep, false);
+        break;
+
+      case PID_SETUP:
+        // setup clears stall
+        EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+        EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+        data.ep0_tog = true;
+        dcd_event_setup_received(rhport, &data.buffer[0][TUSB_DIR_OUT][0], true);
+        break;
+    }
+
+    USBOTG_FS->INT_FG = USBFS_INT_FG_TRANSFER;
+  } else if (status & USBFS_INT_FG_BUS_RST) {
+    data.ep0_tog = true;
+    data.xfer[0][TUSB_DIR_OUT].max_size = 64;
+    data.xfer[0][TUSB_DIR_IN].max_size = 64;
+
+    dcd_event_bus_signal(rhport, DCD_EVENT_BUS_RESET, true);
+
+    USBOTG_FS->DEV_ADDR = 0x00;
+    EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+
+    USBOTG_FS->INT_FG = USBFS_INT_FG_BUS_RST;
+  } else if (status & USBFS_INT_FG_SUSPEND) {
+    dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND};
+    dcd_event_handler(&event, true);
+    USBOTG_FS->INT_FG = USBFS_INT_FG_SUSPEND;
+  }
+}
+
+void dcd_int_enable(uint8_t rhport) {
+  (void) rhport;
+  NVIC_EnableIRQ(USBHD_IRQn);
+}
+
+void dcd_int_disable(uint8_t rhport) {
+  (void) rhport;
+  NVIC_DisableIRQ(USBHD_IRQn);
+}
+
+void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
+  (void) dev_addr;
+  dcd_edpt_xfer(rhport, 0x80, NULL, 0); // zlp status response
+}
+
+void dcd_remote_wakeup(uint8_t rhport) {
+  (void) rhport;
+  // TODO optional
+}
+
+void dcd_connect(uint8_t rhport) {
+  (void) rhport;
+  USBOTG_FS->BASE_CTRL |= USBFS_CTRL_DEV_PUEN;
+}
+
+void dcd_disconnect(uint8_t rhport) {
+  (void) rhport;
+  USBOTG_FS->BASE_CTRL &= ~USBFS_CTRL_DEV_PUEN;
+}
+
+void dcd_sof_enable(uint8_t rhport, bool en) {
+  (void) rhport;
+  (void) en;
+
+  // TODO implement later
+}
+
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+  (void) rhport;
+  if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+      request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+      request->bRequest == TUSB_REQ_SET_ADDRESS) {
+    USBOTG_FS->DEV_ADDR = (uint8_t) request->wValue;
+  }
+  EP_TX_CTRL(0) = USBFS_EP_T_RES_NAK;
+  EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+}
+
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_ep) {
+  (void) rhport;
+  uint8_t ep = tu_edpt_number(desc_ep->bEndpointAddress);
+  uint8_t dir = tu_edpt_dir(desc_ep->bEndpointAddress);
+  TU_ASSERT(ep < EP_MAX);
+
+  data.isochronous[ep] = desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS;
+  data.xfer[ep][dir].max_size = tu_edpt_packet_size(desc_ep);
+
+  if (ep != 0) {
+    if (dir == TUSB_DIR_OUT) {
+      if (data.isochronous[ep]) {
+        EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_NYET;
+      } else {
+        EP_RX_CTRL(ep) = USBFS_EP_R_AUTO_TOG | USBFS_EP_R_RES_ACK;
+      }
+    } else {
+      EP_TX_LEN(ep) = 0;
+      EP_TX_CTRL(ep) = USBFS_EP_T_AUTO_TOG | USBFS_EP_T_RES_NAK;
+    }
+  }
+  return true;
+}
+
+void dcd_edpt_close_all(uint8_t rhport) {
+  (void) rhport;
+  // TODO optional
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+  (void) rhport;
+  (void) ep_addr;
+  // TODO optional
+}
+
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
+  (void) rhport;
+  uint8_t ep = tu_edpt_number(ep_addr);
+  uint8_t dir = tu_edpt_dir(ep_addr);
+
+  struct usb_xfer* xfer = &data.xfer[ep][dir];
+  dcd_int_disable(rhport);
+  xfer->valid = true;
+  xfer->buffer = buffer;
+  xfer->len = total_bytes;
+  xfer->processed_len = 0;
+  dcd_int_enable(rhport);
+
+  if (dir == TUSB_DIR_IN) {
+    update_in(rhport, ep, true);
+  }
+  return true;
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+  (void) rhport;
+  uint8_t ep = tu_edpt_number(ep_addr);
+  uint8_t dir = tu_edpt_dir(ep_addr);
+  if (ep == 0) {
+    if (dir == TUSB_DIR_OUT) {
+      EP_RX_CTRL(0) = USBFS_EP_R_RES_STALL;
+    } else {
+      EP_TX_LEN(0) = 0;
+      EP_TX_CTRL(0) = USBFS_EP_T_RES_STALL;
+    }
+  } else {
+    if (dir == TUSB_DIR_OUT) {
+      EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~USBFS_EP_R_RES_MASK) | USBFS_EP_R_RES_STALL;
+    } else {
+      EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~USBFS_EP_T_RES_MASK) | USBFS_EP_T_RES_STALL;
+    }
+  }
+}
+
+void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
+  (void) rhport;
+  uint8_t ep = tu_edpt_number(ep_addr);
+  uint8_t dir = tu_edpt_dir(ep_addr);
+  if (ep == 0) {
+    if (dir == TUSB_DIR_OUT) {
+      EP_RX_CTRL(0) = USBFS_EP_R_RES_ACK;
+    }
+  } else {
+    if (dir == TUSB_DIR_OUT) {
+      EP_RX_CTRL(ep) = (EP_RX_CTRL(ep) & ~(USBFS_EP_R_RES_MASK | USBFS_EP_R_TOG)) | USBFS_EP_R_RES_ACK;
+    } else {
+      EP_TX_CTRL(ep) = (EP_TX_CTRL(ep) & ~(USBFS_EP_T_RES_MASK | USBFS_EP_T_TOG)) | USBFS_EP_T_RES_NAK;
+    }
+  }
+}
+
+#endif
diff --git a/src/portable/wch/dcd_ch32_usbhs.c b/src/portable/wch/dcd_ch32_usbhs.c
index 1f1c0b876..622f9c508 100644
--- a/src/portable/wch/dcd_ch32_usbhs.c
+++ b/src/portable/wch/dcd_ch32_usbhs.c
@@ -2,6 +2,7 @@
  * The MIT License (MIT)
  *
  * Copyright (c) 2022 Greg Davill
+ * Copyright (c) 2023 Denis Krasutski
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
  * of this software and associated documentation files (the "Software"), to deal
@@ -26,366 +27,394 @@
 
 #include "tusb_option.h"
 
-#if CFG_TUD_ENABLED && ((CFG_TUSB_MCU == OPT_MCU_CH32V307) || (CFG_TUSB_MCU == OPT_MCU_CH32F20X))
-#include "device/dcd.h"
-
+#if CFG_TUD_ENABLED && defined(TUP_USBIP_WCH_USBHS) && CFG_TUD_WCH_USBIP_USBHS
 #include "ch32_usbhs_reg.h"
 
+#include "device/dcd.h"
 
 // Max number of bi-directional endpoints including EP0
-#define EP_MAX 16
+#define EP_MAX  16
 
 typedef struct {
-    uint8_t *buffer;
-    // tu_fifo_t * ff; // TODO support dcd_edpt_xfer_fifo API
-    uint16_t total_len;
-    uint16_t queued_len;
-    uint16_t max_size;
-    bool short_packet;
+  uint8_t* buffer;
+  uint16_t total_len;
+  uint16_t queued_len;
+  uint16_t max_size;
+  bool is_last_packet;
+  bool is_iso;
 } xfer_ctl_t;
 
+typedef enum {
+  EP_RESPONSE_ACK,
+  EP_RESPONSE_NAK,
+} ep_response_list_t;
+
 #define XFER_CTL_BASE(_ep, _dir) &xfer_status[_ep][_dir]
 static xfer_ctl_t xfer_status[EP_MAX][2];
 
-#define EP_TX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_TX_LEN) + (ep)*2)
-#define EP_TX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_TX_CTRL) + (ep)*4)
-#define EP_RX_CTRL(ep) *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_RX_CTRL) + (ep)*4)
-#define EP_RX_MAX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_MAX_LEN) + (ep)*2)
+#define EP_TX_LEN(ep)     *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_TX_LEN) + (ep) * 2)
+#define EP_TX_CTRL(ep)    *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_TX_CTRL) + (ep) * 4)
+#define EP_RX_CTRL(ep)    *(volatile uint8_t *)((volatile uint8_t *)&(USBHSD->UEP0_RX_CTRL) + (ep) * 4)
+#define EP_RX_MAX_LEN(ep) *(volatile uint16_t *)((volatile uint16_t *)&(USBHSD->UEP0_MAX_LEN) + (ep) * 2)
 
 #define EP_TX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_TX_DMA) + (ep - 1))
 #define EP_RX_DMA_ADDR(ep) *(volatile uint32_t *)((volatile uint32_t *)&(USBHSD->UEP1_RX_DMA) + (ep - 1))
 
 /* Endpoint Buffer */
-TU_ATTR_ALIGNED(4) uint8_t EP0_DatabufHD[64];  // ep0(64)
+TU_ATTR_ALIGNED(4) static uint8_t ep0_buffer[CFG_TUD_ENDPOINT0_SIZE];
 
-volatile uint8_t USBHS_Dev_Endp0_Tog = 0x01;
+static void ep_set_response_and_toggle(uint8_t ep_num, tusb_dir_t ep_dir, ep_response_list_t response_type) {
+  if (ep_dir == TUSB_DIR_IN) {
+    uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_T_RES_ACK : USBHS_EP_T_RES_NAK;
+    if (ep_num == 0) {
+      if (response_type == EP_RESPONSE_ACK) {
+        if (EP_TX_LEN(ep_num) == 0) {
+          EP_TX_CTRL(ep_num) |= USBHS_EP_T_TOG_1;
+        } else {
+          EP_TX_CTRL(ep_num) ^= USBHS_EP_T_TOG_1;
+        }
+      }
+    }
+    if (xfer_status[ep_num][TUSB_DIR_IN].is_iso == true) {
+      EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG;
+    } else {
+      EP_TX_CTRL(ep_num) = (EP_TX_CTRL(ep_num) & ~(USBHS_EP_T_RES_MASK)) | response;
+    }
+  } else {
+    uint8_t response = (response_type == EP_RESPONSE_ACK) ? USBHS_EP_R_RES_ACK : USBHS_EP_R_RES_NAK;
+    if (ep_num == 0) {
+      if (response_type == EP_RESPONSE_ACK) {
+        if (xfer_status[ep_num][TUSB_DIR_OUT].queued_len == 0) {
+          EP_RX_CTRL(ep_num) |= USBHS_EP_R_TOG_1;
+        }
+      } else {
+        EP_RX_CTRL(ep_num) ^= USBHS_EP_R_TOG_1;
+      }
+    }
+    EP_RX_CTRL(ep_num) = (EP_RX_CTRL(ep_num) & ~(USBHS_EP_R_RES_MASK)) | response;
+  }
+}
 
-void dcd_init(uint8_t rhport) {
-    (void)rhport;
+static void xfer_data_packet(uint8_t ep_num, tusb_dir_t ep_dir, xfer_ctl_t* xfer) {
+  if (ep_dir == TUSB_DIR_IN) {
+    uint16_t remaining = xfer->total_len - xfer->queued_len;
+    uint16_t next_tx_size = TU_MIN(remaining, xfer->max_size);
 
-    memset(&xfer_status, 0, sizeof(xfer_status));
-
-    USBHSD->HOST_CTRL = 0x00;
-    USBHSD->HOST_CTRL = USBHS_PHY_SUSPENDM;
-
-    USBHSD->CONTROL = 0;
-
-#if TUD_OPT_HIGH_SPEED
-    USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED;
-#else
-    #error OPT_MODE_FULL_SPEED not currently supported on CH32
-    USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED;
-#endif
-
-    USBHSD->INT_EN = 0;
-    USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_DETECT_EN | USBHS_SUSPEND_EN;
-
-    /* ALL endpoint enable */
-    USBHSD->ENDP_CONFIG = 0xffffffff;
-
-    USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN;
-    USBHSD->ENDP_TYPE = 0x00;
-    USBHSD->BUF_MODE = 0x00;
-
-    USBHSD->UEP0_MAX_LEN = 64;
-
-    USBHSD->UEP0_DMA = (uint32_t)EP0_DatabufHD;
-
-    USBHSD->UEP0_TX_LEN = 0;
-    USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_NAK;
-    USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK;
-
-    for (int ep = 1; ep < EP_MAX; ep++) {
-        EP_TX_LEN(ep) = 0;
-        EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK;
-        EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
-
-        EP_RX_MAX_LEN(ep) = 512;
+    if (ep_num == 0) {
+      memcpy(ep0_buffer, &xfer->buffer[xfer->queued_len], next_tx_size);
+    } else {
+      EP_TX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len];
     }
 
-    USBHSD->DEV_AD = 0;
-    USBHSD->CONTROL |= USBHS_DEV_PU_EN;
+    EP_TX_LEN(ep_num) = next_tx_size;
+    xfer->queued_len += next_tx_size;
+    if (xfer->queued_len == xfer->total_len) {
+      xfer->is_last_packet = true;
+    }
+    if (xfer->is_iso == true) {
+      /* Enable EP to generate ISA_ACT interrupt */
+      USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num);
+    }
+  } else { /* TUSB_DIR_OUT */
+    uint16_t left_to_receive = xfer->total_len - xfer->queued_len;
+    uint16_t max_possible_rx_size = TU_MIN(xfer->max_size, left_to_receive);
+
+    if (max_possible_rx_size == left_to_receive) {
+      xfer->is_last_packet = true;
+    }
+
+    if (ep_num > 0) {
+      EP_RX_DMA_ADDR(ep_num) = (uint32_t) &xfer->buffer[xfer->queued_len];
+      EP_RX_MAX_LEN(ep_num) = max_possible_rx_size;
+    }
+  }
+  ep_set_response_and_toggle(ep_num, ep_dir, USBHS_EP_R_RES_ACK);
+}
+
+void dcd_init(uint8_t rhport) {
+  (void) rhport;
+
+  memset(&xfer_status, 0, sizeof(xfer_status));
+
+  USBHSD->HOST_CTRL = 0x00;
+  USBHSD->HOST_CTRL = USBHS_PHY_SUSPENDM;
+
+  USBHSD->CONTROL = 0;
+
+#if TUD_OPT_HIGH_SPEED
+  USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_HIGH_SPEED;
+#else
+  #error OPT_MODE_FULL_SPEED not currently supported on CH32
+  USBHSD->CONTROL = USBHS_DMA_EN | USBHS_INT_BUSY_EN | USBHS_FULL_SPEED;
+#endif
+
+  USBHSD->INT_EN = 0;
+  USBHSD->INT_EN = USBHS_SETUP_ACT_EN | USBHS_TRANSFER_EN | USBHS_BUS_RST_EN | USBHS_SUSPEND_EN | USBHS_ISO_ACT_EN;
+
+  USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN;
+  USBHSD->ENDP_TYPE = 0x00;
+  USBHSD->BUF_MODE = 0x00;
+
+  for (int ep = 0; ep < EP_MAX; ep++) {
+    EP_TX_LEN(ep) = 0;
+    EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK;
+    EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+
+    EP_RX_MAX_LEN(ep) = 0;
+  }
+
+  USBHSD->UEP0_DMA = (uint32_t) ep0_buffer;
+  USBHSD->UEP0_MAX_LEN = CFG_TUD_ENDPOINT0_SIZE;
+  xfer_status[0][TUSB_DIR_OUT].max_size = CFG_TUD_ENDPOINT0_SIZE;
+  xfer_status[0][TUSB_DIR_IN].max_size = CFG_TUD_ENDPOINT0_SIZE;
+
+  USBHSD->DEV_AD = 0;
+  USBHSD->CONTROL |= USBHS_DEV_PU_EN;
 }
 
 void dcd_int_enable(uint8_t rhport) {
-    (void)rhport;
-
-    NVIC_EnableIRQ(USBHS_IRQn);
+  (void) rhport;
+  NVIC_EnableIRQ(USBHS_IRQn);
 }
 
 void dcd_int_disable(uint8_t rhport) {
-    (void)rhport;
-
-    NVIC_DisableIRQ(USBHS_IRQn);
+  (void) rhport;
+  NVIC_DisableIRQ(USBHS_IRQn);
 }
 
 void dcd_edpt_close_all(uint8_t rhport) {
-    (void)rhport;
+  (void) rhport;
+
+  for (size_t ep = 1; ep < EP_MAX; ep++) {
+    EP_TX_LEN(ep) = 0;
+    EP_TX_CTRL(ep) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK;
+    EP_RX_CTRL(ep) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+
+    EP_RX_MAX_LEN(ep) = 0;
+  }
+
+  USBHSD->ENDP_CONFIG = USBHS_EP0_T_EN | USBHS_EP0_R_EN;
 }
 
 void dcd_set_address(uint8_t rhport, uint8_t dev_addr) {
-    (void)dev_addr;
+  (void) dev_addr;
 
-    // Response with zlp status
-    dcd_edpt_xfer(rhport, 0x80, NULL, 0);
+  // Response with zlp status
+  dcd_edpt_xfer(rhport, 0x80, NULL, 0);
 }
 
-void dcd_remote_wakeup(uint8_t rhport)
-{
+void dcd_remote_wakeup(uint8_t rhport) {
   (void) rhport;
 }
 
-void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const *request) {
-    (void)rhport;
-
-    if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
-        request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
-        request->bRequest == TUSB_REQ_SET_ADDRESS) {
-        USBHSD->DEV_AD = (uint8_t)request->wValue;
-    }
-
-    EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK;
-    EP_RX_CTRL(0) = USBHS_EP_R_RES_ACK;
+void dcd_sof_enable(uint8_t rhport, bool en) {
+  (void) rhport;
+  if (en) {
+    USBHSD->INT_EN |= USBHS_SOF_ACT_EN;
+  } else {
+    USBHSD->INT_EN &= ~(USBHS_SOF_ACT_EN);
+  }
 }
 
-bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const *desc_edpt) {
-    (void)rhport;
+void dcd_edpt0_status_complete(uint8_t rhport, tusb_control_request_t const* request) {
+  (void) rhport;
 
-    uint8_t const epnum = tu_edpt_number(desc_edpt->bEndpointAddress);
-    uint8_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
+  if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE &&
+      request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD &&
+      request->bRequest == TUSB_REQ_SET_ADDRESS) {
+    USBHSD->DEV_AD = (uint8_t) request->wValue;
+  }
 
-    TU_ASSERT(epnum < EP_MAX);
+  EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+  EP_RX_CTRL(0) = USBHS_EP_R_RES_NAK | USBHS_EP_R_TOG_0;
+}
 
-    xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
-    xfer->max_size = tu_edpt_packet_size(desc_edpt);
+bool dcd_edpt_open(uint8_t rhport, tusb_desc_endpoint_t const* desc_edpt) {
+  (void) rhport;
 
-    if (epnum != 0) {
-        if (tu_edpt_dir(desc_edpt->bEndpointAddress) == TUSB_DIR_OUT) {
-            EP_RX_CTRL(epnum) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_ACK;
-        } else {
-            EP_TX_LEN(epnum) = 0;
-            EP_TX_CTRL(epnum) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
-        }
-    }
+  uint8_t const ep_num = tu_edpt_number(desc_edpt->bEndpointAddress);
+  tusb_dir_t const dir = tu_edpt_dir(desc_edpt->bEndpointAddress);
 
+  TU_ASSERT(ep_num < EP_MAX);
+
+  if (ep_num == 0) {
     return true;
-}
+  }
 
-int usbd_ep_close(const uint8_t ep) {
-    (void)ep;
+  xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, dir);
+  xfer->max_size = tu_edpt_packet_size(desc_edpt);
 
-    return 0;
-}
-void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
-    (void)rhport;
-
-    uint8_t const epnum = tu_edpt_number(ep_addr);
-    uint8_t const dir = tu_edpt_dir(ep_addr);
-
-    if (epnum == 0) {
-        if (dir == TUSB_DIR_OUT) {
-            USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_STALL;
-        } else {
-            USBHSD->UEP0_TX_LEN = 0;
-            USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_STALL;
-        }
-    } else {
-        if (dir == TUSB_DIR_OUT) {
-            EP_RX_CTRL(epnum) = (EP_RX_CTRL(epnum) & ~USBHS_EP_R_RES_MASK) | USBHS_EP_R_RES_STALL;
-
-        } else {
-            EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~USBHS_EP_T_RES_MASK) | USBHS_EP_T_RES_STALL;
-        }
+  xfer->is_iso = (desc_edpt->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS);
+  if (dir == TUSB_DIR_OUT) {
+    USBHSD->ENDP_CONFIG |= (USBHS_EP0_R_EN << ep_num);
+    EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+    if (xfer->is_iso == true) {
+      USBHSD->ENDP_TYPE |= (USBHS_EP0_R_TYP << ep_num);
     }
+    EP_RX_MAX_LEN(ep_num) = xfer->max_size;
+  } else {
+    if (xfer->is_iso == true) {
+      USBHSD->ENDP_TYPE |= (USBHS_EP0_T_TYP << ep_num);
+    } else {
+      /* Enable all types except Isochronous to avoid ISO_ACT interrupt generation */
+      USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << ep_num);
+    }
+    EP_TX_LEN(ep_num) = 0;
+    EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+  }
+
+  return true;
+}
+
+void dcd_edpt_close(uint8_t rhport, uint8_t ep_addr) {
+  (void) rhport;
+
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+
+  if (dir == TUSB_DIR_OUT) {
+    EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+    EP_RX_MAX_LEN(ep_num) = 0;
+    USBHSD->ENDP_TYPE &= ~(USBHS_EP0_R_TYP << ep_num);
+    USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_R_EN << ep_num);
+  } else {  // TUSB_DIR_IN
+    EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+    EP_TX_LEN(ep_num) = 0;
+    USBHSD->ENDP_TYPE &= ~(USBHS_EP0_T_TYP << ep_num);
+    USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num);
+  }
+}
+
+void dcd_edpt_stall(uint8_t rhport, uint8_t ep_addr) {
+  (void) rhport;
+
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  tusb_dir_t const dir = tu_edpt_dir(ep_addr);
+
+  if (dir == TUSB_DIR_OUT) {
+    EP_RX_CTRL(ep_num) = USBHS_EP_R_RES_STALL;
+  } else {
+    EP_TX_LEN(0) = 0;
+    EP_TX_CTRL(ep_num) = USBHS_EP_T_RES_STALL;
+  }
 }
 
 void dcd_edpt_clear_stall(uint8_t rhport, uint8_t ep_addr) {
-    (void)rhport;
+  (void) rhport;
 
-    uint8_t const epnum = tu_edpt_number(ep_addr);
-    uint8_t const dir = tu_edpt_dir(ep_addr);
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  tusb_dir_t const dir = tu_edpt_dir(ep_addr);
 
-    if (epnum == 0) {
-        if (dir == TUSB_DIR_OUT) {
-            USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK;
-        } else {
-        }
-    } else {
-        if (dir == TUSB_DIR_OUT) {
-            EP_RX_CTRL(epnum) = (EP_RX_CTRL(epnum) & ~(USBHS_EP_R_RES_MASK | USBHS_EP_T_TOG_MASK)) | USBHS_EP_T_RES_ACK;
-
-        } else {
-            EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK | USBHS_EP_T_TOG_MASK)) | USBHS_EP_T_RES_NAK;
-        }
-    }
+  if (dir == TUSB_DIR_OUT) {
+    EP_RX_CTRL(ep_num) = USBHS_EP_R_AUTOTOG | USBHS_EP_R_RES_NAK;
+  } else {
+    EP_TX_CTRL(ep_num) = USBHS_EP_T_AUTOTOG | USBHS_EP_R_RES_NAK;
+  }
 }
 
-bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t total_bytes) {
-    (void)rhport;
-    uint8_t const epnum = tu_edpt_number(ep_addr);
-    uint8_t const dir = tu_edpt_dir(ep_addr);
+bool dcd_edpt_xfer(uint8_t rhport, uint8_t ep_addr, uint8_t* buffer, uint16_t total_bytes) {
+  (void) rhport;
+  uint8_t const ep_num = tu_edpt_number(ep_addr);
+  tusb_dir_t const dir = tu_edpt_dir(ep_addr);
 
-    xfer_ctl_t *xfer = XFER_CTL_BASE(epnum, dir);
-    xfer->buffer = buffer;
-    // xfer->ff           = NULL; // TODO support dcd_edpt_xfer_fifo API
-    xfer->total_len = total_bytes;
-    xfer->queued_len = 0;
-    xfer->short_packet = false;
+  xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, dir);
+  xfer->buffer = buffer;
+  xfer->total_len = total_bytes;
+  xfer->queued_len = 0;
+  xfer->is_last_packet = false;
 
-    // uint16_t num_packets = (total_bytes / xfer->max_size);
-    uint16_t short_packet_size = total_bytes % (xfer->max_size + 1);
+  xfer_data_packet(ep_num, dir, xfer);
 
-    // Zero-size packet is special case.
-    if (short_packet_size == 0 || (total_bytes == 0)) {
-        xfer->short_packet = true;
-    }
-
-    if (tu_edpt_dir(ep_addr) == TUSB_DIR_IN) {
-        if (!total_bytes) {
-            xfer->short_packet = true;
-            if (epnum == 0) {
-                USBHSD->UEP0_TX_LEN = 0;
-                USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_ACK | (USBHS_Dev_Endp0_Tog ? USBHS_EP_T_TOG_1 : USBHS_EP_T_TOG_0);
-                USBHS_Dev_Endp0_Tog ^= 1;
-            } else {
-                EP_TX_LEN(epnum) = 0;
-                EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_ACK;
-            }
-        } else {
-            if (epnum == 0) {
-                xfer->queued_len += short_packet_size;
-                memcpy(&EP0_DatabufHD[0], buffer, short_packet_size);
-
-                USBHSD->UEP0_TX_LEN = short_packet_size;
-                USBHSD->UEP0_TX_CTRL = USBHS_EP_T_RES_ACK | (USBHS_Dev_Endp0_Tog ? USBHS_EP_T_TOG_1 : USBHS_EP_T_TOG_0);
-                USBHS_Dev_Endp0_Tog ^= 1;
-            } else {
-                xfer->queued_len += short_packet_size;
-
-                EP_TX_DMA_ADDR(epnum) = (uint32_t)buffer;
-                USBHSD->ENDP_CONFIG |= (USBHS_EP0_T_EN << epnum);
-                EP_TX_LEN(epnum) = short_packet_size;
-                EP_TX_CTRL(epnum) = (EP_TX_CTRL(epnum) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_ACK;
-            }
-        }
-    } else { /* TUSB_DIR_OUT */
-        if (epnum == 0) {
-            uint32_t read_count = USBHSD->RX_LEN;
-            read_count = TU_MIN(read_count, total_bytes);
-
-            if ((total_bytes == 8)) {
-                read_count = 8;
-                memcpy(buffer, &EP0_DatabufHD[0], 8);
-            } else {
-                memcpy(buffer, &EP0_DatabufHD[0], read_count);
-            }
-        } else {
-            EP_RX_DMA_ADDR(epnum) = (uint32_t)xfer->buffer;
-            USBHSD->ENDP_CONFIG |= (USBHS_EP0_R_EN << epnum);
-        }
-
-        // usbd_ep_read(ep_addr, buffer, total_bytes, &ret_bytes);
-    }
-    return true;
-}
-
-
-static void receive_packet(xfer_ctl_t *xfer, uint16_t xfer_size) {
-    // xfer->queued_len = xfer->total_len - remaining;
-
-    uint16_t remaining = xfer->total_len - xfer->queued_len;
-    uint16_t to_recv_size;
-
-    if (remaining <= xfer->max_size) {
-        // Avoid buffer overflow.
-        to_recv_size = (xfer_size > remaining) ? remaining : xfer_size;
-    } else {
-        // Room for full packet, choose recv_size based on what the microcontroller
-        // claims.
-        to_recv_size = (xfer_size > xfer->max_size) ? xfer->max_size : xfer_size;
-    }
-
-    if (to_recv_size) {
-    }
-
-    xfer->queued_len += xfer_size;
-
-    // Per USB spec, a short OUT packet (including length 0) is always
-    // indicative of the end of a transfer (at least for ctl, bulk, int).
-    xfer->short_packet = (xfer_size < xfer->max_size);
+  return true;
 }
 
 void dcd_int_handler(uint8_t rhport) {
-    (void)rhport;
+  (void) rhport;
 
-    uint32_t end_num, rx_token;
-    uint8_t intflag = 0;
+  uint8_t int_flag = USBHSD->INT_FG;
+  uint8_t int_status = USBHSD->INT_ST;
 
-    intflag = USBHSD->INT_FG;
+  if (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)) {
+    uint8_t const token = int_status & MASK_UIS_TOKEN;
 
-    if (intflag & USBHS_TRANSFER_FLAG) {
+    if (token == USBHS_TOKEN_PID_SOF) {
+      uint32_t frame_count = USBHSD->FRAME_NO & USBHS_FRAME_NO_NUM_MASK;
+      dcd_event_sof(rhport, frame_count, true);
+    }else {
+      uint8_t const ep_num = int_status & MASK_UIS_ENDP;
+      tusb_dir_t const ep_dir = (token == USBHS_TOKEN_PID_IN) ? TUSB_DIR_IN : TUSB_DIR_OUT;
+      uint8_t const ep_addr = tu_edpt_addr(ep_num, ep_dir);
+      xfer_ctl_t* xfer = XFER_CTL_BASE(ep_num, ep_dir);
 
-        end_num = (USBHSD->INT_ST) & MASK_UIS_ENDP;
-        rx_token = (((USBHSD->INT_ST) & MASK_UIS_TOKEN) >> 4) & 0x03;
+      if (token == USBHS_TOKEN_PID_OUT) {
+        uint16_t rx_len = USBHSD->RX_LEN;
 
-        uint8_t endp = end_num | (rx_token == PID_IN ? TUSB_DIR_IN_MASK : 0);
-
-        xfer_ctl_t *xfer = XFER_CTL_BASE(end_num, tu_edpt_dir(endp));
-
-        if (rx_token == PID_OUT) {
-            uint16_t rx_len = USBHSD->RX_LEN;
-
-            receive_packet(xfer, rx_len);
-
-            if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
-                xfer->short_packet = false;
-
-                dcd_event_xfer_complete(0, endp, xfer->queued_len, XFER_RESULT_SUCCESS, true);
-            }
-
-            if (end_num == 0) {
-                USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0;
-            }
-
-        } else if (rx_token == PID_IN) {
-            if (xfer->short_packet || (xfer->queued_len == xfer->total_len)) {
-                xfer->short_packet = false;
-                xfer->total_len = 0;
-                dcd_event_xfer_complete(0, endp, xfer->queued_len, XFER_RESULT_SUCCESS, true);
-
-                EP_TX_CTRL(end_num) = (EP_TX_CTRL(end_num) & ~(USBHS_EP_T_RES_MASK)) | USBHS_EP_T_RES_NAK;
-
-                if (end_num == 0) {
-                }
-            } else {
-                dcd_edpt_xfer(0, endp, xfer->buffer + xfer->queued_len, xfer->total_len - xfer->queued_len);
-            }
+        if (ep_num == 0) {
+          memcpy(&xfer->buffer[xfer->queued_len], ep0_buffer, rx_len);
         }
 
-        USBHSD->INT_FG = USBHS_TRANSFER_FLAG; /* Clear flag */
-    } else if (intflag & USBHS_SETUP_FLAG) {
-        USBHS_Dev_Endp0_Tog = 1;
-        dcd_event_setup_received(0, EP0_DatabufHD, true);
+        xfer->queued_len += rx_len;
+        if (rx_len < xfer->max_size) {
+          xfer->is_last_packet = true;
+        }
+      } else if (token == USBHS_TOKEN_PID_IN) {
+        if (xfer->is_iso && xfer->is_last_packet) {
+          /* Disable EP to avoid ISO_ACT interrupt generation */
+          USBHSD->ENDP_CONFIG &= ~(USBHS_EP0_T_EN << ep_num);
+        } else {
+          // Do nothing, no need to update xfer->is_last_packet, it is already updated in xfer_data_packet
+        }
+      }
 
-        USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */
-    } else if (intflag & USBHS_DETECT_FLAG) {
-        USBHS_Dev_Endp0_Tog = 1;
-
-        xfer_status[0][TUSB_DIR_OUT].max_size = 64;
-        xfer_status[0][TUSB_DIR_IN].max_size = 64;
-
-        dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true);
-
-        USBHSD->DEV_AD = 0;
-        USBHSD->UEP0_RX_CTRL = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0;
-
-        USBHSD->INT_FG = USBHS_DETECT_FLAG; /* Clear flag */
-    } else if (intflag & USBHS_SUSPEND_FLAG) {
-        dcd_event_t event = { .rhport = rhport, .event_id = DCD_EVENT_SUSPEND };
-        dcd_event_handler(&event, true);
-
-        USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */
+      if (xfer->is_last_packet == true) {
+        ep_set_response_and_toggle(ep_num, ep_dir, EP_RESPONSE_NAK);
+        dcd_event_xfer_complete(0, ep_addr, xfer->queued_len, XFER_RESULT_SUCCESS, true);
+      } else {
+        /* prepare next part of packet to xref */
+        xfer_data_packet(ep_num, ep_dir, xfer);
+      }
     }
+
+    USBHSD->INT_FG = (int_flag & (USBHS_ISO_ACT_FLAG | USBHS_TRANSFER_FLAG)); /* Clear flag */
+  } else if (int_flag & USBHS_SETUP_FLAG) {
+    ep_set_response_and_toggle(0, TUSB_DIR_IN, EP_RESPONSE_NAK);
+    ep_set_response_and_toggle(0, TUSB_DIR_OUT, EP_RESPONSE_NAK);
+    dcd_event_setup_received(0, ep0_buffer, true);
+
+    USBHSD->INT_FG = USBHS_SETUP_FLAG; /* Clear flag */
+  } else if (int_flag & USBHS_BUS_RST_FLAG) {
+    // TODO CH32 does not detect actual speed at this time (should be known at end of reset)
+    // This interrupt probably triggered at start of bus reset
+//    tusb_speed_t actual_speed;
+//    switch(USBHSD->SPEED_TYPE & USBHS_SPEED_TYPE_MASK){
+//      case USBHS_SPEED_TYPE_HIGH:
+//        actual_speed = TUSB_SPEED_HIGH;
+//        break;
+//      case USBHS_SPEED_TYPE_FULL:
+//        actual_speed = TUSB_SPEED_FULL;
+//        break;
+//      case USBHS_SPEED_TYPE_LOW:
+//        actual_speed = TUSB_SPEED_LOW;
+//        break;
+//      default:
+//        TU_ASSERT(0,);
+//        break;
+//    }
+//    dcd_event_bus_reset(0, actual_speed, true);
+
+    dcd_event_bus_reset(0, TUSB_SPEED_HIGH, true);
+
+    USBHSD->DEV_AD = 0;
+    EP_RX_CTRL(0) = USBHS_EP_R_RES_ACK | USBHS_EP_R_TOG_0;
+    EP_TX_CTRL(0) = USBHS_EP_T_RES_NAK | USBHS_EP_T_TOG_0;
+
+    USBHSD->INT_FG = USBHS_BUS_RST_FLAG; /* Clear flag */
+  } else if (int_flag & USBHS_SUSPEND_FLAG) {
+    dcd_event_t event = {.rhport = rhport, .event_id = DCD_EVENT_SUSPEND};
+    dcd_event_handler(&event, true);
+
+    USBHSD->INT_FG = USBHS_SUSPEND_FLAG; /* Clear flag */
+  }
 }
 
 #endif
diff --git a/src/tusb.c b/src/tusb.c
index 0092267a1..860e8ac35 100644
--- a/src/tusb.c
+++ b/src/tusb.c
@@ -398,7 +398,7 @@ static void dump_str_line(uint8_t const* buf, uint16_t count) {
   tu_printf("  |");
   // each line is 16 bytes
   for (uint16_t i = 0; i < count; i++) {
-    const char ch = buf[i];
+    int ch = buf[i];
     tu_printf("%c", isprint(ch) ? ch : '.');
   }
   tu_printf("|\r\n");
diff --git a/src/tusb.h b/src/tusb.h
index c9d56d3c3..4f69a1414 100644
--- a/src/tusb.h
+++ b/src/tusb.h
@@ -38,8 +38,6 @@
 #include "osal/osal.h"
 #include "common/tusb_fifo.h"
 
-#include "class/hid/hid.h"
-
 //------------- TypeC -------------//
 #if CFG_TUC_ENABLED
   #include "typec/usbc.h"
diff --git a/src/tusb_option.h b/src/tusb_option.h
index cbcb69edd..f2cc284dc 100644
--- a/src/tusb_option.h
+++ b/src/tusb_option.h
@@ -29,14 +29,12 @@
 
 #include "common/tusb_compiler.h"
 
-// Version is release as major.minor.revision eg 1.0.0. though there could be notable APIs before a new release.
-// For notable API changes within a release, we increase the build number.
+// Version is release as major.minor.revision eg 1.0.0
 #define TUSB_VERSION_MAJOR     0
-#define TUSB_VERSION_MINOR     16
+#define TUSB_VERSION_MINOR     17
 #define TUSB_VERSION_REVISION  0
-#define TUSB_VERSION_BUILD     3
 
-#define TUSB_VERSION_NUMBER    (TUSB_VERSION_MAJOR << 24 | TUSB_VERSION_MINOR << 16 | TUSB_VERSION_REVISION << 8 | TUSB_VERSION_BUILD)
+#define TUSB_VERSION_NUMBER    (TUSB_VERSION_MAJOR * 10000 + TUSB_VERSION_MINOR * 100 + TUSB_VERSION_REVISION)
 #define TUSB_VERSION_STRING    TU_STRING(TUSB_VERSION_MAJOR) "." TU_STRING(TUSB_VERSION_MINOR) "." TU_STRING(TUSB_VERSION_REVISION)
 
 //--------------------------------------------------------------------+
@@ -55,7 +53,8 @@
 #define OPT_MCU_LPC18XX             6 ///< NXP LPC18xx
 #define OPT_MCU_LPC40XX             7 ///< NXP LPC40xx
 #define OPT_MCU_LPC43XX             8 ///< NXP LPC43xx
-#define OPT_MCU_LPC51UXX            9 ///< NXP LPC51U6x
+#define OPT_MCU_LPC51               9 ///< NXP LPC51
+#define OPT_MCU_LPC51UXX            OPT_MCU_LPC51 ///< NXP LPC51
 #define OPT_MCU_LPC54              10 ///< NXP LPC54
 #define OPT_MCU_LPC55              11 ///< NXP LPC55
 // legacy naming
@@ -119,6 +118,12 @@
 // Espressif
 #define OPT_MCU_ESP32S2           900 ///< Espressif ESP32-S2
 #define OPT_MCU_ESP32S3           901 ///< Espressif ESP32-S3
+#define OPT_MCU_ESP32             902 ///< Espressif ESP32 (for host max3421e)
+#define OPT_MCU_ESP32C3           903 ///< Espressif ESP32-C3
+#define OPT_MCU_ESP32C6           904 ///< Espressif ESP32-C6
+#define OPT_MCU_ESP32C2           905 ///< Espressif ESP32-C2
+#define OPT_MCU_ESP32H2           906 ///< Espressif ESP32-H2
+#define TUP_MCU_ESPRESSIF         (CFG_TUSB_MCU >= 900 && CFG_TUSB_MCU < 1000) // check if Espressif MCU
 
 // Dialog
 #define OPT_MCU_DA1469X          1000 ///< Dialog Semiconductor DA1469x
@@ -176,7 +181,8 @@
 // WCH
 #define OPT_MCU_CH32V307         2200 ///< WCH CH32V307
 #define OPT_MCU_CH32F20X         2210 ///< WCH CH32F20x
-
+#define OPT_MCU_CH32V20X         2220 ///< WCH CH32V20X
+#define OPT_MCU_CH32V103         2230 ///< WCH CH32V103
 
 // NXP LPC MCX
 #define OPT_MCU_MCXN9            2300  ///< NXP MCX N9 Series
@@ -199,19 +205,9 @@
 #define OPT_OS_RTTHREAD   6  ///< RT-Thread
 #define OPT_OS_RTX4       7  ///< Keil RTX 4
 
-// Allow to use command line to change the config name/location
-#ifdef CFG_TUSB_CONFIG_FILE
-  #include CFG_TUSB_CONFIG_FILE
-#else
-  #include "tusb_config.h"
-#endif
-
-#include "common/tusb_mcu.h"
-
-//--------------------------------------------------------------------
-// RootHub Mode Configuration
-// CFG_TUSB_RHPORTx_MODE contains operation mode and speed for that port
-//--------------------------------------------------------------------
+//--------------------------------------------------------------------+
+// Mode and Speed
+//--------------------------------------------------------------------+
 
 // Low byte is operational mode
 #define OPT_MODE_NONE           0x0000 ///< Disabled
@@ -225,7 +221,24 @@
 #define OPT_MODE_HIGH_SPEED     0x0400 ///< High Speed
 #define OPT_MODE_SPEED_MASK     0xff00
 
-//------------- Roothub as Device -------------//
+//--------------------------------------------------------------------+
+// Include tusb_config.h and tusb_mcu.h
+//--------------------------------------------------------------------+
+
+// Allow to use command line to change the config name/location
+#ifdef CFG_TUSB_CONFIG_FILE
+  #include CFG_TUSB_CONFIG_FILE
+#else
+  #include "tusb_config.h"
+#endif
+
+#include "common/tusb_mcu.h"
+
+//--------------------------------------------------------------------
+// RootHub Mode detection
+//--------------------------------------------------------------------
+
+//------------- Root hub as Device -------------//
 
 #if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_DEVICE)
   #define TUD_RHPORT_MODE     (CFG_TUSB_RHPORT0_MODE)
@@ -253,7 +266,7 @@
 // highspeed support indicator
 #define TUD_OPT_HIGH_SPEED    (CFG_TUD_MAX_SPEED ? (CFG_TUD_MAX_SPEED & OPT_MODE_HIGH_SPEED) : TUP_RHPORT_HIGHSPEED)
 
-//------------- Roothub as Host -------------//
+//------------- Root hub as Host -------------//
 
 #if defined(CFG_TUSB_RHPORT0_MODE) && ((CFG_TUSB_RHPORT0_MODE) & OPT_MODE_HOST)
   #define TUH_RHPORT_MODE  (CFG_TUSB_RHPORT0_MODE)
@@ -359,6 +372,20 @@
   #define CFG_TUD_INTERFACE_MAX   16
 #endif
 
+// default to max hardware endpoint, but can be smaller to save RAM
+#ifndef CFG_TUD_ENDPPOINT_MAX
+  #define CFG_TUD_ENDPPOINT_MAX   TUP_DCD_ENDPOINT_MAX
+#endif
+
+#if CFG_TUD_ENDPPOINT_MAX > TUP_DCD_ENDPOINT_MAX
+  #error "CFG_TUD_ENDPPOINT_MAX must be less than or equal to TUP_DCD_ENDPOINT_MAX"
+#endif
+
+// USB 2.0 7.1.20: compliance test mode support
+#ifndef CFG_TUD_TEST_MODE
+  #define CFG_TUD_TEST_MODE       0
+#endif
+
 //------------- Device Class Driver -------------//
 #ifndef CFG_TUD_BTH
   #define CFG_TUD_BTH             0
@@ -454,26 +481,26 @@
   #define CFG_TUH_CDC    0
 #endif
 
+// FTDI is not part of CDC class, only to re-use CDC driver API
 #ifndef CFG_TUH_CDC_FTDI
-  // FTDI is not part of CDC class, only to re-use CDC driver API
   #define CFG_TUH_CDC_FTDI 0
 #endif
 
+// List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID
 #ifndef CFG_TUH_CDC_FTDI_VID_PID_LIST
-  // List of product IDs that can use the FTDI CDC driver. 0x0403 is FTDI's VID
   #define CFG_TUH_CDC_FTDI_VID_PID_LIST \
     {0x0403, 0x6001}, {0x0403, 0x6006}, {0x0403, 0x6010}, {0x0403, 0x6011}, \
     {0x0403, 0x6014}, {0x0403, 0x6015}, {0x0403, 0x8372}, {0x0403, 0xFBFA}, \
     {0x0403, 0xCD18}
 #endif
 
+// CP210X is not part of CDC class, only to re-use CDC driver API
 #ifndef CFG_TUH_CDC_CP210X
-  // CP210X is not part of CDC class, only to re-use CDC driver API
   #define CFG_TUH_CDC_CP210X 0
 #endif
 
+// List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
 #ifndef CFG_TUH_CDC_CP210X_VID_PID_LIST
-  // List of product IDs that can use the CP210X CDC driver. 0x10C4 is Silicon Labs' VID
   #define CFG_TUH_CDC_CP210X_VID_PID_LIST \
     {0x10C4, 0xEA60}, {0x10C4, 0xEA70}
 #endif
@@ -483,8 +510,8 @@
   #define CFG_TUH_CDC_CH34X 0
 #endif
 
+// List of product IDs that can use the CH34X CDC driver
 #ifndef CFG_TUH_CDC_CH34X_VID_PID_LIST
-  // List of product IDs that can use the CH34X CDC driver
   #define CFG_TUH_CDC_CH34X_VID_PID_LIST \
     { 0x1a86, 0x5523 }, /* ch341 chip */ \
     { 0x1a86, 0x7522 }, /* ch340k chip */ \
diff --git a/src/typec/tcd.h b/src/typec/tcd.h
index 86499c951..bcbdab8ed 100644
--- a/src/typec/tcd.h
+++ b/src/typec/tcd.h
@@ -63,7 +63,7 @@ typedef struct TU_ATTR_PACKED {
     } xfer_complete;
   };
 
-} tcd_event_t;;
+} tcd_event_t;
 
 //--------------------------------------------------------------------+
 //
diff --git a/test/fuzz/net_fuzz.cc b/test/fuzz/net_fuzz.cc
index 63cd1ac98..468437b0c 100644
--- a/test/fuzz/net_fuzz.cc
+++ b/test/fuzz/net_fuzz.cc
@@ -69,14 +69,6 @@ void tud_network_init_cb(void) {
 // TODO removed later since it is not part of tinyusb stack
 uint8_t tud_network_mac_address[6] = {0};
 
-//------------- NCM -------------//
-
-// callback to client providing optional indication of internal state of network
-// driver
-void tud_network_link_state_cb(bool state) {
-  (void)state;
-  // NoOp.
-}
-}
+}   // extern "C"
 
 #endif
diff --git a/test/hil/hil_hfp.json b/test/hil/hfp.json
similarity index 100%
rename from test/hil/hil_hfp.json
rename to test/hil/hfp.json
diff --git a/test/hil/hil_pi4.json b/test/hil/hil_pi4.json
deleted file mode 100644
index 17d9ff1fa..000000000
--- a/test/hil/hil_pi4.json
+++ /dev/null
@@ -1,37 +0,0 @@
-{
-    "boards": [
-        {
-            "name": "raspberry_pi_pico",
-            "uid": "E6614C311B764A37",
-            "flasher": "openocd",
-            "flasher_sn": "E6614103E72C1D2F",
-            "flasher_args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
-        },
-        {
-            "name": "espressif_s3_devkitc",
-            "uid": "7CDFA1E073CC",
-            "tests": [
-                "cdc_msc_freertos", "hid_composite_freertos"
-            ],
-            "flasher": "esptool",
-            "flasher_sn": "461cb8d7decdeb119be9b506e93fd3f1",
-            "flasher_args": "-b 1500000"
-        },
-        {
-            "name": "feather_nrf52840_express",
-            "uid": "1F0479CD0F764471",
-            "flasher": "jlink",
-            "flasher_sn": "000682804350",
-            "flasher_args": "-device nrf52840_xxaa"
-        },
-        {
-            "name": "itsybitsy_m4",
-            "uid": "D784B28C5338533335202020FF044726",
-            "flasher": "bossac",
-            "flashser_vendor": "Adafruit Industries",
-            "flasher_product": "ItsyBitsy M4 Express",
-            "flasher_reset_pin": "2",
-            "flasher_args": "--offset 0x4000"
-        }
-    ]
-}
diff --git a/test/hil/hil_test.py b/test/hil/hil_test.py
index 7f03ce43f..bdc48591e 100644
--- a/test/hil/hil_test.py
+++ b/test/hil/hil_test.py
@@ -25,23 +25,25 @@
 # ACTION=="add", SUBSYSTEM=="tty", SUBSYSTEMS=="usb", MODE="0666", PROGRAM="/bin/sh -c 'echo $$ID_SERIAL_SHORT | rev | cut -c -8 | rev'", SYMLINK+="ttyUSB_%c.%s{bInterfaceNumber}"
 # ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", ENV{ID_FS_USAGE}=="filesystem", MODE="0666", PROGRAM="/bin/sh -c 'echo $$ID_SERIAL_SHORT | rev | cut -c -8 | rev'", RUN{program}+="/usr/bin/systemd-mount --no-block --automount=yes --collect $devnode /media/blkUSB_%c.%s{bInterfaceNumber}"
 
+import argparse
 import os
 import sys
 import time
-import click
 import serial
 import subprocess
 import json
 import glob
+import platform
 
 # for RPI double reset
-try:
-    import gpiozero
-except ImportError:
-    pass
+if platform.machine() == 'aarch64':
+    try:
+        import gpiozero
+    except ImportError:
+        pass
 
 
-ENUM_TIMEOUT = 10
+ENUM_TIMEOUT = 30
 
 
 # get usb serial by id
@@ -111,27 +113,48 @@ def read_disk_file(id, fname):
 # -------------------------------------------------------------
 # Flashing firmware
 # -------------------------------------------------------------
+def run_cmd(cmd):
+    #print(cmd)
+    r = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    if r.returncode != 0:
+        title = 'command error'
+        if os.getenv('CI'):
+            print(f"::group::{title}")
+            print(r.stdout.decode("utf-8"))
+            print(f"::endgroup::")
+        else:
+            print(title)
+            print(r.stdout.decode("utf-8"))
+    return r
+
+
 def flash_jlink(board, firmware):
-    script = ['halt', 'r', f'loadfile {firmware}', 'r', 'go', 'exit']
+    script = ['halt', 'r', f'loadfile {firmware}.elf', 'r', 'go', 'exit']
     with open('flash.jlink', 'w') as f:
         f.writelines(f'{s}\n' for s in script)
-    ret = subprocess.run(
-        f'JLinkExe -USB {board["flasher_sn"]} {board["flasher_args"]} -if swd -JTAGConf -1,-1 -speed auto -NoGui 1 -ExitOnError 1 -CommandFile flash.jlink',
-        shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    ret = run_cmd(f'JLinkExe -USB {board["flasher_sn"]} {board["flasher_args"]} -if swd -JTAGConf -1,-1 -speed auto -NoGui 1 -ExitOnError 1 -CommandFile flash.jlink')
     os.remove('flash.jlink')
     return ret
 
 
+def flash_stlink(board, firmware):
+    ret = run_cmd(f'STM32_Programmer_CLI --connect port=swd sn={board["flasher_sn"]} --write {firmware}.elf --go')
+    return ret
+
+
+def flash_stflash(board, firmware):
+    ret = run_cmd(f'st-flash --serial {board["flasher_sn"]} write {firmware}.bin 0x8000000')
+    return ret
+
+
 def flash_openocd(board, firmware):
-    ret = subprocess.run(
-        f'openocd -c "adapter serial {board["flasher_sn"]}" {board["flasher_args"]} -c "program {firmware} reset exit"',
-        shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    ret = run_cmd(f'openocd -c "adapter serial {board["flasher_sn"]}" {board["flasher_args"]} -c "program {firmware}.elf reset exit"')
     return ret
 
 
 def flash_esptool(board, firmware):
     port = get_serial_dev(board["flasher_sn"], None, None, 0)
-    dir = os.path.dirname(firmware)
+    dir = os.path.dirname(f'{firmware}.bin')
     with open(f'{dir}/config.env') as f:
         IDF_TARGET = json.load(f)['IDF_TARGET']
     with open(f'{dir}/flash_args') as f:
@@ -142,21 +165,23 @@ def flash_esptool(board, firmware):
     return ret
 
 
-def doublereset_with_rpi_gpio(board):
+def doublereset_with_rpi_gpio(pin):
     # Off = 0 = Reset
-    led = gpiozero.LED(board["flasher_reset_pin"])
+    nrst = gpiozero.LED(pin)
 
-    led.off()
+    nrst.off()
     time.sleep(0.1)
-    led.on()
+    nrst.on()
     time.sleep(0.1)
-    led.off()
+    nrst.off()
     time.sleep(0.1)
-    led.on()
+    nrst.on()
+
 
 def flash_bossac(board, firmware):
     # double reset to enter bootloader
-    doublereset_with_rpi_gpio(board)
+    if platform.machine() == 'aarch64':
+        doublereset_with_rpi_gpio(board["flasher_reset_pin"])
 
     port = get_serial_dev(board["uid"], board["flashser_vendor"], board["flasher_product"], 0)
     timeout = ENUM_TIMEOUT
@@ -169,8 +194,7 @@ def flash_bossac(board, firmware):
     assert timeout, 'bossac bootloader is not available'
     # sleep a bit more for bootloader to be ready
     time.sleep(0.5)
-    ret = subprocess.run(f'bossac --port {port} {board["flasher_args"]} -U -i -R -e -w {firmware}', shell=True, stdout=subprocess.PIPE,
-                         stderr=subprocess.STDOUT)
+    ret = run_cmd(f'bossac --port {port} {board["flasher_args"]} -U -i -R -e -w {firmware}.bin')
     return ret
 
 # -------------------------------------------------------------
@@ -303,39 +327,50 @@ def test_hid_composite_freertos(id):
 # -------------------------------------------------------------
 # Main
 # -------------------------------------------------------------
-@click.command()
-@click.argument('config_file')
-@click.option('-b', '--board', multiple=True, default=None, help='Boards to test, all if not specified')
-def main(config_file, board):
+def main():
     """
     Hardware test on specified boards
     """
-    config_file = os.path.join(os.path.dirname(__file__), config_file)
+    parser = argparse.ArgumentParser()
+    parser.add_argument('config_file', help='Configuration JSON file')
+    parser.add_argument('-b', '--board', action='append', default=[], help='Boards to test, all if not specified')
+    args = parser.parse_args()
+
+    config_file = args.config_file
+    boards = args.board
+
+    # if config file is not found, try to find it in the same directory as this script
+    if not os.path.exists(config_file):
+        config_file = os.path.join(os.path.dirname(__file__), config_file)
     with open(config_file) as f:
         config = json.load(f)
 
-    # all possible tests
+    # all possible tests: board_test is added last to disable board's usb
     all_tests = [
-        'cdc_dual_ports', 'cdc_msc', 'dfu', 'dfu_runtime', 'hid_boot_interface',
+        'cdc_dual_ports',
+        'cdc_msc',
+        'cdc_msc_freertos',
+        'dfu',
+        'dfu_runtime',
+        'hid_boot_interface',
+        'board_test'
     ]
 
-    if len(board) == 0:
+    if len(boards) == 0:
         config_boards = config['boards']
     else:
-        config_boards = [e for e in config['boards'] if e['name'] in board]
+        config_boards = [e for e in config['boards'] if e['name'] in boards]
 
     for item in config_boards:
-        print(f'Testing board:{item["name"]}')
+        name = item['name']
+        print(f'Testing board:{name}')
         flasher = item['flasher'].lower()
 
         # default to all tests
         if 'tests' in item:
-            test_list = item['tests']
+            test_list = item['tests'] + ['board_test']
         else:
-            test_list = all_tests
-
-        # board_test is added last to disable board's usb
-        test_list.append('board_test')
+            test_list = list(all_tests)
 
         # remove skip_tests
         if 'tests_skip' in item:
@@ -344,29 +379,20 @@ def main(config_file, board):
                     test_list.remove(skip)
 
         for test in test_list:
-            fw_list = [
-                # cmake: esp32 & samd51 use .bin file
-                f'cmake-build/cmake-build-{item["name"]}/device/{test}/{test}.elf',
-                f'cmake-build/cmake-build-{item["name"]}/device/{test}/{test}.bin',
-                # make
-                f'examples/device/{test}/_build/{item["name"]}/{test}.elf'
-            ]
+            fw_dir = f'cmake-build/cmake-build-{name}/device/{test}'
+            if not os.path.exists(fw_dir):
+                fw_dir = f'examples/cmake-build-{name}/device/{test}'
+            fw_name = f'{fw_dir}/{test}'
+            print(f'  {test} ... ', end='')
+            sys.stdout.flush()
 
-            fw = None
-            for f in fw_list:
-                if os.path.isfile(f):
-                    fw = f
-                    break
-
-            if fw is None:
-                print(f'Cannot find binary file for {test}')
-                sys.exit(-1)
-
-            print(f'  {test} ...', end='')
+            if not os.path.exists(fw_dir):
+                print('Skip')
+                continue
 
             # flash firmware. It may fail randomly, retry a few times
             for i in range(3):
-                ret = globals()[f'flash_{flasher}'](item, fw)
+                ret = globals()[f'flash_{flasher}'](item, fw_name)
                 if ret.returncode == 0:
                     break
                 else:
diff --git a/test/hil/rpi.json b/test/hil/rpi.json
new file mode 100644
index 000000000..9520f30e2
--- /dev/null
+++ b/test/hil/rpi.json
@@ -0,0 +1,74 @@
+{
+    "boards": [
+        {
+            "name": "feather_nrf52840_express",
+            "uid": "1F0479CD0F764471",
+            "flasher": "jlink",
+            "flasher_sn": "000682804350",
+            "flasher_args": "-device nrf52840_xxaa"
+        },
+        {
+            "name": "itsybitsy_m4",
+            "uid": "D784B28C5338533335202020FF044726",
+            "flasher": "openocd",
+            "flasher_sn": "E6614C311B597D32",
+            "flasher_args": "-f interface/cmsis-dap.cfg -f target/atsame5x.cfg -c \"adapter speed 5000\""
+        },
+        {
+            "name": "metro_m7_1011",
+            "uid": "9CE8715DD71137363E00005002004200",
+            "flasher": "jlink",
+            "flasher_sn": "000611000000",
+            "flasher_args": "-device MIMXRT1011xxx5A"
+        },
+        {
+            "name": "lpcxpresso11u37",
+            "uid": "17121919",
+            "flasher": "jlink",
+            "flasher_sn": "000724441579",
+            "flasher_args": "-device LPC11U37/401"
+        },
+        {
+            "name": "ra4m1_ek",
+            "uid": "152E163038303131393346E46F26574B",
+            "tests_skip": ["cdc_msc", "cdc_msc_freertos"],
+            "comment": "MSC is slow to enumerated #2602",
+            "flasher": "jlink",
+            "flasher_sn": "000831174392",
+            "flasher_args": "-device R7FA4M1AB"
+        },
+        {
+            "name": "raspberry_pi_pico",
+            "uid": "E6614C311B764A37",
+            "flasher": "openocd",
+            "flasher_sn": "E6614103E72C1D2F",
+            "flasher_args": "-f interface/cmsis-dap.cfg -f target/rp2040.cfg -c \"adapter speed 5000\""
+        },
+        {
+            "name": "stm32f072disco",
+            "uid": "3A001A001357364230353532",
+            "flasher": "jlink",
+            "flasher_sn": "779541626",
+            "flasher_args": "-device stm32f072rb"
+        },
+        {
+            "name": "stm32g0b1nucleo",
+            "uid": "4D0038000450434E37343120",
+            "flasher": "openocd",
+            "flasher_sn": "066FFF495087534867063844",
+            "flasher_args": "-f interface/stlink.cfg -f target/stm32g0x.cfg"
+        }
+    ],
+    "boards-skip": [
+        {
+            "name": "espressif_s3_devkitm",
+            "uid": "84F703C084E4",
+            "tests": [
+                "cdc_msc_freertos", "hid_composite_freertos"
+            ],
+            "flasher": "esptool",
+            "flasher_sn": "3ea619acd1cdeb11a0a0b806e93fd3f1",
+            "flasher_args": "-b 921600"
+        }
+    ]
+}
diff --git a/test/unit-test/project.yml b/test/unit-test/project.yml
index 82528f68c..7fbbabfe6 100644
--- a/test/unit-test/project.yml
+++ b/test/unit-test/project.yml
@@ -81,20 +81,20 @@
 
 :tools:
   :test_compiler:
-     :executable: clang
-     :name: 'clang compiler'
+     :executable: gcc
+     :name: 'gcc compiler'
      :arguments:
         - -I"$": COLLECTION_PATHS_TEST_TOOLCHAIN_INCLUDE               #expands to -I search paths
         - -I"$": COLLECTION_PATHS_TEST_SUPPORT_SOURCE_INCLUDE_VENDOR   #expands to -I search paths
         - -D$: COLLECTION_DEFINES_TEST_AND_VENDOR  #expands to all -D defined symbols
-        - -fsanitize=address
+        #- -fsanitize=address
         - -c ${1}                       #source code input file (Ruby method call param list sub)
         - -o ${2}                       #object file output (Ruby method call param list sub)
   :test_linker:
-     :executable: clang
-     :name: 'clang linker'
+     :executable: gcc
+     :name: 'gcc linker'
      :arguments:
-        - -fsanitize=address
+        #- -fsanitize=address
         - ${1}               #list of object files to link (Ruby method call param list sub)
         - -o ${2}            #executable file output (Ruby method call param list sub)
 
diff --git a/tools/build.py b/tools/build.py
new file mode 100644
index 000000000..b937a7342
--- /dev/null
+++ b/tools/build.py
@@ -0,0 +1,219 @@
+import argparse
+import random
+import os
+import sys
+import time
+import subprocess
+from pathlib import Path
+from multiprocessing import Pool
+
+import build_utils
+
+SUCCEEDED = "\033[32msucceeded\033[0m"
+FAILED = "\033[31mfailed\033[0m"
+
+build_separator = '-' * 106
+
+
+def run_cmd(cmd):
+    #print(cmd)
+    r = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+    title = 'command error'
+    if r.returncode != 0:
+        # print build output if failed
+        if os.getenv('CI'):
+            print(f"::group::{title}")
+            print(r.stdout.decode("utf-8"))
+            print(f"::endgroup::")
+        else:
+            print(title)
+            print(r.stdout.decode("utf-8"))
+    return r
+
+def find_family(board):
+    bsp_dir = Path("hw/bsp")
+    for family_dir in bsp_dir.iterdir():
+        if family_dir.is_dir():
+            board_dir = family_dir / 'boards' / board
+            if board_dir.exists():
+                return family_dir.name
+    return None
+
+
+def get_examples(family):
+    all_examples = []
+    for d in os.scandir("examples"):
+        if d.is_dir() and 'cmake' not in d.name and 'build_system' not in d.name:
+            for entry in os.scandir(d.path):
+                if entry.is_dir() and 'cmake' not in entry.name:
+                    if family != 'espressif' or 'freertos' in entry.name:
+                        all_examples.append(d.name + '/' + entry.name)
+
+    if family == 'espressif':
+        all_examples.append('device/board_test')
+        all_examples.append('device/video_capture')
+    all_examples.sort()
+    return all_examples
+
+
+def build_board_cmake(board, toolchain):
+    start_time = time.monotonic()
+    ret = [0, 0, 0]
+
+    build_dir = f"cmake-build/cmake-build-{board}"
+    family = find_family(board)
+    if family == 'espressif':
+        # for espressif, we have to build example individually
+        all_examples = get_examples(family)
+        for example in all_examples:
+            r = run_cmd(f'cmake examples/{example} -B {build_dir}/{example} -G "Ninja" -DBOARD={board} -DMAX3421_HOST=1')
+            if r.returncode == 0:
+                r = run_cmd(f'cmake --build {build_dir}/{example}')
+            if r.returncode == 0:
+                ret[0] += 1
+            else:
+                ret[1] += 1
+    else:
+        r = run_cmd(f'cmake examples -B {build_dir} -G "Ninja" -DBOARD={board} -DCMAKE_BUILD_TYPE=MinSizeRel -DTOOLCHAIN={toolchain}')
+        if r.returncode == 0:
+            r = run_cmd(f"cmake --build {build_dir}")
+        if r.returncode == 0:
+            ret[0] += 1
+        else:
+            ret[1] += 1
+
+    duration = time.monotonic() - start_time
+
+    if ret[1] == 0:
+        status = SUCCEEDED
+    else:
+        status = FAILED
+
+    flash_size = "-"
+    sram_size = "-"
+    example = 'all'
+    title = build_utils.build_format.format(example, board, status, "{:.2f}s".format(duration), flash_size, sram_size)
+    print(title)
+    return ret
+
+
+def build_board_make_all_examples(board, toolchain, all_examples):
+    start_time = time.monotonic()
+    ret = [0, 0, 0]
+
+    with Pool(processes=os.cpu_count()) as pool:
+        pool_args = list((map(lambda e, b=board, o=f"TOOLCHAIN={toolchain}": [e, b, o], all_examples)))
+        r = pool.starmap(build_utils.build_example, pool_args)
+        # sum all element of same index (column sum)
+        rsum = list(map(sum, list(zip(*r))))
+        ret[0] += rsum[0]
+        ret[1] += rsum[1]
+        ret[2] += rsum[2]
+    duration = time.monotonic() - start_time
+    if ret[1] == 0:
+        status = SUCCEEDED
+    else:
+        status = FAILED
+
+    flash_size = "-"
+    sram_size = "-"
+    example = 'all'
+    title = build_utils.build_format.format(example, board, status, "{:.2f}s".format(duration), flash_size, sram_size)
+    print(title)
+    return ret
+
+
+def build_family(family, toolchain, build_system, one_per_family, boards):
+    all_boards = []
+    for entry in os.scandir(f"hw/bsp/{family}/boards"):
+        if entry.is_dir() and entry.name != 'pico_sdk':
+            all_boards.append(entry.name)
+    all_boards.sort()
+
+    ret = [0, 0, 0]
+
+    # If only-one flag is set, select one random board
+    if one_per_family:
+        for b in boards:
+            # skip if -b already specify one in this family
+            if find_family(b) == family:
+                return ret
+        all_boards = [random.choice(all_boards)]
+
+    # success, failed, skipped
+    all_examples = get_examples(family)
+    for board in all_boards:
+        r = [0, 0, 0]
+        if build_system == 'cmake':
+            r = build_board_cmake(board, toolchain)
+        elif build_system == 'make':
+            r = build_board_make_all_examples(board, toolchain, all_examples)
+        ret[0] += r[0]
+        ret[1] += r[1]
+        ret[2] += r[2]
+
+    return ret
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('families', nargs='*', default=[], help='Families to build')
+    parser.add_argument('-b', '--board', action='append', default=[], help='Boards to build')
+    parser.add_argument('-t', '--toolchain', default='gcc', help='Toolchain to use, default is gcc')
+    parser.add_argument('-s', '--build-system', default='cmake', help='Build system to use, default is cmake')
+    parser.add_argument('-1', '--one-per-family', action='store_true', default=False, help='Build only one random board inside a family')
+    args = parser.parse_args()
+
+    families = args.families
+    boards = args.board
+    toolchain = args.toolchain
+    build_system = args.build_system
+    one_per_family = args.one_per_family
+
+    if len(families) == 0 and len(boards) == 0:
+        print("Please specify families or board to build")
+        return 1
+
+    print(build_separator)
+    print(build_utils.build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', 'Flash', 'SRAM'))
+    total_time = time.monotonic()
+    result = [0, 0, 0]
+
+    # build families
+    all_families = []
+    if 'all' in families:
+        for entry in os.scandir("hw/bsp"):
+            if entry.is_dir() and entry.name != 'espressif' and os.path.isfile(entry.path + "/family.cmake"):
+                all_families.append(entry.name)
+    else:
+        all_families = list(families)
+    all_families.sort()
+
+    # succeeded, failed
+    for f in all_families:
+        fret = build_family(f, toolchain, build_system, one_per_family, boards)
+        result[0] += fret[0]
+        result[1] += fret[1]
+        result[2] += fret[2]
+
+    # build boards
+    for b in boards:
+        r = [0, 0, 0]
+        if build_system == 'cmake':
+            r = build_board_cmake(b, toolchain)
+        elif build_system == 'make':
+            all_examples = get_examples(find_family(b))
+            r = build_board_make_all_examples(b, toolchain, all_examples)
+        result[0] += r[0]
+        result[1] += r[1]
+        result[2] += r[2]
+
+    total_time = time.monotonic() - total_time
+    print(build_separator)
+    print(f"Build Summary: {result[0]} {SUCCEEDED}, {result[1]} {FAILED} and took {total_time:.2f}s")
+    print(build_separator)
+    return result[1]
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/tools/build_board.py b/tools/build_board.py
deleted file mode 100644
index 13376d126..000000000
--- a/tools/build_board.py
+++ /dev/null
@@ -1,69 +0,0 @@
-import os
-import sys
-import time
-import subprocess
-from multiprocessing import Pool
-
-import build_utils
-
-SUCCEEDED = "\033[32msucceeded\033[0m"
-FAILED = "\033[31mfailed\033[0m"
-SKIPPED = "\033[33mskipped\033[0m"
-
-build_separator = '-' * 106
-
-
-def filter_with_input(mylist):
-    if len(sys.argv) > 1:
-        input_args = list(set(mylist).intersection(sys.argv))
-        if len(input_args) > 0:
-            mylist[:] = input_args
-
-
-if __name__ == '__main__':
-    # If examples are not specified in arguments, build all
-    all_examples = []
-    for dir1 in os.scandir("examples"):
-        if dir1.is_dir():
-            for entry in os.scandir(dir1.path):
-                if entry.is_dir():
-                    all_examples.append(dir1.name + '/' + entry.name)
-    filter_with_input(all_examples)
-    all_examples.sort()
-
-    # If boards are not specified in arguments, build all
-    all_boards = []
-    for entry in os.scandir("hw/bsp"):
-        if entry.is_dir() and os.path.exists(entry.path + "/board.mk"):
-            all_boards.append(entry.name)
-    filter_with_input(all_boards)
-    all_boards.sort()
-
-    # Get dependencies
-    for b in all_boards:
-        subprocess.run("make -C examples/device/board_test BOARD={} get-deps".format(b), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-    print(build_separator)
-    print(build_utils.build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', 'Flash', 'SRAM'))
-    total_time = time.monotonic()
-
-    # succeeded, failed, skipped
-    total_result = [0, 0, 0]
-    for example in all_examples:
-        print(build_separator)
-        with Pool(processes=os.cpu_count()) as pool:
-            pool_args = list((map(lambda b, e=example, o='': [e, b, o], all_boards)))
-            result = pool.starmap(build_utils.build_example, pool_args)
-            # sum all element of same index (column sum)
-            result = list(map(sum, list(zip(*result))))
-
-            # add to total result
-            total_result = list(map(lambda x, y: x + y, total_result, result))
-
-    total_time = time.monotonic() - total_time
-    print(build_separator)
-    print("Build Summary: {} {}, {} {}, {} {} and took {:.2f}s".format(total_result[0], SUCCEEDED, total_result[1],
-                                                                       FAILED, total_result[2], SKIPPED, total_time))
-    print(build_separator)
-
-    sys.exit(total_result[1])
diff --git a/tools/build_cmake.py b/tools/build_cmake.py
deleted file mode 100644
index e539b9f94..000000000
--- a/tools/build_cmake.py
+++ /dev/null
@@ -1,105 +0,0 @@
-import os
-import sys
-import time
-import subprocess
-import pathlib
-from multiprocessing import Pool
-
-import build_utils
-
-SUCCEEDED = "\033[32msucceeded\033[0m"
-FAILED = "\033[31mfailed\033[0m"
-SKIPPED = "\033[33mskipped\033[0m"
-
-build_separator = '-' * 106
-
-def filter_with_input(mylist):
-    if len(sys.argv) > 1:
-        input_args = list(set(mylist).intersection(sys.argv))
-        if len(input_args) > 0:
-            mylist[:] = input_args
-
-
-def build_family(family, cmake_option):
-    all_boards = []
-    for entry in os.scandir("hw/bsp/{}/boards".format(family)):
-        if entry.is_dir() and entry.name != 'pico_sdk':
-            all_boards.append(entry.name)
-    all_boards.sort()
-
-    # success, failed, skipped
-    ret = [0, 0, 0]
-    for board in all_boards:
-        start_time = time.monotonic()
-
-        build_dir = f"cmake-build/cmake-build-{board}"
-
-        # Generate build
-        r = subprocess.run(f"cmake examples -B {build_dir} -G \"Ninja\" -DFAMILY={family} -DBOARD"
-                           f"={board} {cmake_option}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-        # Build
-        if r.returncode == 0:
-            r = subprocess.run(f"cmake --build {build_dir}", shell=True, stdout=subprocess.PIPE,
-                               stderr=subprocess.STDOUT)
-
-        duration = time.monotonic() - start_time
-
-        if r.returncode == 0:
-            status = SUCCEEDED
-            ret[0] += 1
-        else:
-            status = FAILED
-            ret[1] += 1
-
-        flash_size = "-"
-        sram_size = "-"
-        example = 'all'
-        title = build_utils.build_format.format(example, board, status, "{:.2f}s".format(duration), flash_size, sram_size)
-
-        if os.getenv('CI'):
-            # always print build output if in CI
-            print(f"::group::{title}")
-            print(r.stdout.decode("utf-8"))
-            print(f"::endgroup::")
-        else:
-            # print build output if failed
-            print(title)
-            if r.returncode != 0:
-                print(r.stdout.decode("utf-8"))
-
-    return ret
-
-
-if __name__ == '__main__':
-    cmake_options = ''
-    for a in sys.argv[1:]:
-        if a.startswith('-'):
-            cmake_options += ' ' + a
-
-    # If family are not specified in arguments, build all supported
-    all_families = []
-    for entry in os.scandir("hw/bsp"):
-        if entry.is_dir() and entry.name != 'espressif' and os.path.isfile(entry.path + "/family.cmake"):
-            all_families.append(entry.name)
-    filter_with_input(all_families)
-    all_families.sort()
-
-    print(build_separator)
-    print(build_utils.build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', 'Flash', 'SRAM'))
-    total_time = time.monotonic()
-
-    # succeeded, failed, skipped
-    total_result = [0, 0, 0]
-    for family in all_families:
-        fret = build_family(family, cmake_options)
-        if len(fret) == len(total_result):
-            total_result = [total_result[i] + fret[i] for i in range(len(fret))]
-
-    total_time = time.monotonic() - total_time
-    print(build_separator)
-    print("Build Summary: {} {}, {} {}, {} {} and took {:.2f}s".format(total_result[0], SUCCEEDED, total_result[1],
-                                                                       FAILED, total_result[2], SKIPPED, total_time))
-    print(build_separator)
-
-    sys.exit(total_result[1])
diff --git a/tools/build_esp32.py b/tools/build_esp32.py
deleted file mode 100644
index 951467c23..000000000
--- a/tools/build_esp32.py
+++ /dev/null
@@ -1,106 +0,0 @@
-import os
-import glob
-import sys
-import subprocess
-import time
-
-import build_utils
-
-SUCCEEDED = "\033[32msucceeded\033[0m"
-FAILED = "\033[31mfailed\033[0m"
-SKIPPED = "\033[33mskipped\033[0m"
-
-success_count = 0
-fail_count = 0
-skip_count = 0
-exit_status = 0
-
-total_time = time.monotonic()
-
-build_format = '| {:30} | {:30} | {:18} | {:7} | {:6} | {:6} |'
-build_separator = '-' * 107
-
-def filter_with_input(mylist):
-    if len(sys.argv) > 1:
-        input_args = list(set(mylist).intersection(sys.argv))
-        if len(input_args) > 0:
-            mylist[:] = input_args
-
-
-# Build all examples if not specified
-all_examples = [entry.replace('examples/', '') for entry in glob.glob("examples/*/*_freertos")]
-filter_with_input(all_examples)
-all_examples.append('device/board_test')
-all_examples.sort()
-
-# Build all boards if not specified
-all_boards = []
-for entry in os.scandir("hw/bsp/espressif/boards"):
-    if entry.is_dir():
-        all_boards.append(entry.name)
-filter_with_input(all_boards)
-all_boards.sort()
-
-def build_board(example, board):
-    global success_count, fail_count, skip_count, exit_status
-    start_time = time.monotonic()
-
-    # Check if board is skipped
-    build_dir = f"cmake-build/cmake-build-{board}/{example}"
-
-    # Generate and build
-    r = subprocess.run(f"cmake examples/{example} -B {build_dir} -G \"Ninja\" -DBOARD={board} -DMAX3421_HOST=1",
-                       shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-    if r.returncode == 0:
-        r = subprocess.run(f"cmake --build {build_dir}", shell=True, stdout=subprocess.PIPE,
-                           stderr=subprocess.STDOUT)
-    build_duration = time.monotonic() - start_time
-    flash_size = "-"
-    sram_size = "-"
-
-    if r.returncode == 0:
-        success = SUCCEEDED
-        success_count += 1
-        #(flash_size, sram_size) = build_size(example, board)
-    else:
-        exit_status = r.returncode
-        success = FAILED
-        fail_count += 1
-
-    title = build_format.format(example, board, success, "{:.2f}s".format(build_duration), flash_size, sram_size)
-    if os.getenv('CI'):
-        # always print build output if in CI
-        print(f"::group::{title}")
-        print(r.stdout.decode("utf-8"))
-        print(f"::endgroup::")
-    else:
-        # print build output if failed
-        print(title)
-        if r.returncode != 0:
-            print(r.stdout.decode("utf-8"))
-
-
-def build_size(example, board):
-    #elf_file = 'examples/device/{}/_build/{}/{}-firmware.elf'.format(example, board, board)
-    elf_file = 'examples/device/{}/_build/{}/*.elf'.format(example, board)
-    size_output = subprocess.run('size {}'.format(elf_file), shell=True, stdout=subprocess.PIPE).stdout.decode("utf-8")
-    size_list = size_output.split('\n')[1].split('\t')
-    flash_size = int(size_list[0])
-    sram_size = int(size_list[1]) + int(size_list[2])
-    return (flash_size, sram_size)
-
-
-print(build_separator)
-print(build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', 'Flash', 'SRAM'))
-print(build_separator)
-
-for example in all_examples:
-    for board in all_boards:
-        build_board(example, board)
-
-total_time = time.monotonic() - total_time
-print(build_separator)
-print("Build Summary: {} {}, {} {}, {} {} and took {:.2f}s".format(success_count, SUCCEEDED, fail_count, FAILED, skip_count, SKIPPED, total_time))
-print(build_separator)
-
-sys.exit(exit_status)
diff --git a/tools/build_make.py b/tools/build_make.py
deleted file mode 100644
index f79a452e4..000000000
--- a/tools/build_make.py
+++ /dev/null
@@ -1,80 +0,0 @@
-import os
-import sys
-import time
-from multiprocessing import Pool
-
-import build_utils
-
-SUCCEEDED = "\033[32msucceeded\033[0m"
-FAILED = "\033[31mfailed\033[0m"
-SKIPPED = "\033[33mskipped\033[0m"
-
-build_separator = '-' * 106
-
-make_iar_option = 'TOOLCHAIN=iar'
-
-def filter_with_input(mylist):
-    if len(sys.argv) > 1:
-        input_args = list(set(mylist).intersection(sys.argv))
-        if len(input_args) > 0:
-            mylist[:] = input_args
-
-
-def build_family(example, family, make_option):
-    all_boards = []
-    for entry in os.scandir("hw/bsp/{}/boards".format(family)):
-        if entry.is_dir() and entry.name != 'pico_sdk':
-            all_boards.append(entry.name)
-    filter_with_input(all_boards)
-    all_boards.sort()
-
-    with Pool(processes=os.cpu_count()) as pool:
-        pool_args = list((map(lambda b, e=example, o=make_option: [e, b, o], all_boards)))
-        result = pool.starmap(build_utils.build_example, pool_args)
-        # sum all element of same index (column sum)
-        return list(map(sum, list(zip(*result))))
-
-
-if __name__ == '__main__':
-    # IAR CC
-    if make_iar_option not in sys.argv:
-        make_iar_option = ''
-
-    # If examples are not specified in arguments, build all
-    all_examples = []
-    for d in os.scandir("examples"):
-        if d.is_dir() and 'cmake' not in d.name and 'build_system' not in d.name:
-            for entry in os.scandir(d.path):
-                if entry.is_dir() and 'cmake' not in entry.name:
-                    all_examples.append(d.name + '/' + entry.name)
-    filter_with_input(all_examples)
-    all_examples.sort()
-
-    # If family are not specified in arguments, build all
-    all_families = []
-    for entry in os.scandir("hw/bsp"):
-        if entry.is_dir() and os.path.isdir(entry.path + "/boards") and entry.name != 'espressif':
-            all_families.append(entry.name)
-    filter_with_input(all_families)
-    all_families.sort()
-
-    print(build_separator)
-    print(build_utils.build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', 'Flash', 'SRAM'))
-    total_time = time.monotonic()
-
-    # succeeded, failed, skipped
-    total_result = [0, 0, 0]
-    for example in all_examples:
-        print(build_separator)
-        for family in all_families:
-            fret = build_family(example, family, make_iar_option)
-            if len(fret) == len(total_result):
-                total_result = [total_result[i] + fret[i] for i in range(len(fret))]
-
-    total_time = time.monotonic() - total_time
-    print(build_separator)
-    print("Build Summary: {} {}, {} {}, {} {} and took {:.2f}s".format(total_result[0], SUCCEEDED, total_result[1],
-                                                                       FAILED, total_result[2], SKIPPED, total_time))
-    print(build_separator)
-
-    sys.exit(total_result[1])
diff --git a/tools/gen_doc.py b/tools/gen_doc.py
index c63294588..668c77ef6 100644
--- a/tools/gen_doc.py
+++ b/tools/gen_doc.py
@@ -7,9 +7,9 @@ from get_deps import deps_all
 TOP = Path(__file__).parent.parent.resolve()
 
 
-###########################################
+# -----------------------------------------
 # Dependencies
-###########################################
+# -----------------------------------------
 
 def gen_deps_doc():
     deps_rst = Path(TOP) / "docs/reference/dependencies.rst"
diff --git a/tools/get_deps.py b/tools/get_deps.py
index 85c8c2126..7fbde0e02 100644
--- a/tools/get_deps.py
+++ b/tools/get_deps.py
@@ -1,3 +1,4 @@
+import argparse
 import sys
 import subprocess
 from pathlib import Path
@@ -13,7 +14,7 @@ deps_mandatory = {
                  '159e31b689577dbf69cf0683bbaffbd71fa5ee10',
                  'all'],
     'tools/uf2': ['https://github.com/microsoft/uf2.git',
-                  '19615407727073e36d81bf239c52108ba92e7660',
+                  'c594542b2faa01cc33a2b97c9fbebc38549df80a',
                   'all'],
 }
 
@@ -37,18 +38,18 @@ deps_optional = {
                                         'xmc4000'],
     'hw/mcu/microchip': ['https://github.com/hathach/microchip_driver.git',
                          '9e8b37e307d8404033bb881623a113931e1edf27',
-                         'sam3x samd11 samd21 samd51 same5x same7x saml2x samg'],
+                         'sam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg'],
     'hw/mcu/mindmotion/mm32sdk': ['https://github.com/hathach/mm32sdk.git',
-                                  '0b79559eb411149d36e073c1635c620e576308d4',
+                                  'b93e856211060ae825216c6a1d6aa347ec758843',
                                   'mm32'],
     'hw/mcu/nordic/nrfx': ['https://github.com/NordicSemiconductor/nrfx.git',
-                           '2527e3c8449cfd38aee41598e8af8492f410ed15',
+                           '7c47cc0a56ce44658e6da2458e86cd8783ccc4a2',
                            'nrf'],
     'hw/mcu/nuvoton': ['https://github.com/majbthrd/nuc_driver.git',
                        '2204191ec76283371419fbcec207da02e1bc22fa',
                        'nuc'],
     'hw/mcu/nxp/lpcopen': ['https://github.com/hathach/nxp_lpcopen.git',
-                           '84e0bd3e43910aaf71eefd62075cf57495418312',
+                           'b41cf930e65c734d8ec6de04f1d57d46787c76ae',
                            'lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43'],
     'hw/mcu/nxp/mcux-sdk': ['https://github.com/hathach/mcux-sdk.git',
                             '144f1eb7ea8c06512e12f12b27383601c0272410',
@@ -84,7 +85,7 @@ deps_optional = {
                                   '2615e866fa48fe1ff1af9e31c348813f2b19e7ec',
                                   'stm32f4'],
     'hw/mcu/st/cmsis_device_f7': ['https://github.com/STMicroelectronics/cmsis_device_f7.git',
-                                  'fc676ef1ad177eb874eaa06444d3d75395fc51f4',
+                                  '25b0463439303b7a38f0d27b161f7d2f3c096e79',
                                   'stm32f7'],
     'hw/mcu/st/cmsis_device_g0': ['https://github.com/STMicroelectronics/cmsis_device_g0.git',
                                   '3a23e1224417f3f2d00300ecd620495e363f2094',
@@ -96,10 +97,10 @@ deps_optional = {
                                   '60dc2c913203dc8629dc233d4384dcc41c91e77f',
                                   'stm32h7'],
     'hw/mcu/st/cmsis_device_h5': ['https://github.com/STMicroelectronics/cmsis_device_h5.git',
-                                  '62b2cb0fbfe10c5791ee469bbde7b397c2fea8f5',
+                                  'cd2d1d579743de57b88ccaf61a968b9c05848ffc',
                                   'stm32h5'],
     'hw/mcu/st/cmsis_device_l0': ['https://github.com/STMicroelectronics/cmsis_device_l0.git',
-                                  '06748ca1f93827befdb8b794402320d94d02004f',
+                                  '69cd5999fd40ae6e546d4905b21635c6ca1bcb92',
                                   'stm32l0'],
     'hw/mcu/st/cmsis_device_l1': ['https://github.com/STMicroelectronics/cmsis_device_l1.git',
                                   '7f16ec0a1c4c063f84160b4cc6bf88ad554a823e',
@@ -111,7 +112,7 @@ deps_optional = {
                                   'd922865fc0326a102c26211c44b8e42f52c1e53d',
                                   'stm32l5'],
     'hw/mcu/st/cmsis_device_u5': ['https://github.com/STMicroelectronics/cmsis_device_u5.git',
-                                  '06d7edade7167b0eafdd550bf77cfc4fa98eae2e',
+                                  '5ad9797c54ec3e55eff770fc9b3cd4a1aefc1309',
                                   'stm32u5'],
     'hw/mcu/st/cmsis_device_wb': ['https://github.com/STMicroelectronics/cmsis_device_wb.git',
                                   '9c5d1920dd9fabbe2548e10561d63db829bb744f',
@@ -166,9 +167,15 @@ deps_optional = {
                                        'stm32wb'],
     'hw/mcu/ti': ['https://github.com/hathach/ti_driver.git',
                   '143ed6cc20a7615d042b03b21e070197d473e6e5',
-                  'msp430 msp432e4 tm4c123'],
+                  'msp430 msp432e4 tm4c'],
+    'hw/mcu/wch/ch32v103': ['https://github.com/openwch/ch32v103.git',
+                            '7578cae0b21f86dd053a1f781b2fc6ab99d0ec17',
+                            'ch32v10x'],
+    'hw/mcu/wch/ch32v20x': ['https://github.com/openwch/ch32v20x.git',
+                            'c4c38f507e258a4e69b059ccc2dc27dde33cea1b',
+                            'ch32v20x'],
     'hw/mcu/wch/ch32v307': ['https://github.com/openwch/ch32v307.git',
-                            '17761f5cf9dbbf2dcf665b7c04934188add20082',
+                            '184f21b852cb95eed58e86e901837bc9fff68775',
                             'ch32v307'],
     'hw/mcu/wch/ch32f20x': ['https://github.com/openwch/ch32f20x.git',
                             '77c4095087e5ed2c548ec9058e655d0b8757663b',
@@ -176,8 +183,11 @@ deps_optional = {
     'lib/CMSIS_5': ['https://github.com/ARM-software/CMSIS_5.git',
                     '20285262657d1b482d132d20d755c8c330d55c1f',
                     'imxrt kinetis_k32l2 kinetis_kl lpc51 lpc54 lpc55 mcx mm32 msp432e4 nrf ra saml2x'
+                    'lpc11 lpc13 lpc15 lpc17 lpc18 lpc40 lpc43'
                     'stm32f0 stm32f1 stm32f2 stm32f3 stm32f4 stm32f7 stm32g0 stm32g4 stm32h5'
-                    'stm32h7 stm32l0 stm32l1 stm32l4 stm32l5 stm32u5 stm32wb'],
+                    'stm32h7 stm32l0 stm32l1 stm32l4 stm32l5 stm32u5 stm32wb'
+                    'sam3x samd11 samd21 samd51 samd5x_e5x same5x same7x saml2x samg'
+                    'tm4c'],
     'lib/sct_neopixel': ['https://github.com/gsteiert/sct_neopixel.git',
                          'e73e04ca63495672d955f9268e003cffe168fcd8',
                          'lpc55'],
@@ -224,27 +234,59 @@ def get_a_dep(d):
     return 0
 
 
-# Arguments can be
-# - family name
-# - specific deps path
-# - all
-if __name__ == "__main__":
+def find_family(board):
+    bsp_dir = Path(TOP / "hw/bsp")
+    for family_dir in bsp_dir.iterdir():
+        if family_dir.is_dir():
+            board_dir = family_dir / 'boards' / board
+            if board_dir.exists():
+                return family_dir.name
+    return None
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('families', nargs='*', default=[], help='Families to fetch')
+    parser.add_argument('-b', '--board', action='append', default=[], help='Boards to fetch')
+    parser.add_argument('--print', action='store_true', help='Print commit hash only')
+    args = parser.parse_args()
+
+    families = args.families
+    boards = args.board
+    print_only = args.print
+
     status = 0
     deps = list(deps_mandatory.keys())
-    # get all if  'all' is argument
-    if len(sys.argv) == 2 and sys.argv[1] == 'all':
+
+    if 'all' in families:
         deps += deps_optional.keys()
     else:
-        for arg in sys.argv[1:]:
-            if arg in deps_all.keys():
-                # if arg is a dep, add it
-                deps.append(arg)
-            else:
-                # arg is a family name, add all deps of that family
-                for d in deps_optional:
-                    if arg in deps_optional[d][2]:
-                        deps.append(d)
+        families = list(families)
+        if boards is not None:
+            for b in boards:
+                f = find_family(b)
+                if f is not None:
+                    families.append(f)
 
-    with Pool() as pool:
-        status = sum(pool.map(get_a_dep, deps))
-    sys.exit(status)
+        for f in families:
+            for d in deps_optional:
+                if d not in deps and f in deps_optional[d][2]:
+                    deps.append(d)
+
+    if print_only:
+        pvalue = {}
+        # print only without arguments, always add CMSIS_5
+        if len(families) == 0 and len(boards) == 0:
+            deps.append('lib/CMSIS_5')
+        for d in deps:
+            commit = deps_all[d][1]
+            pvalue[d] = commit
+        print(pvalue)
+    else:
+        with Pool() as pool:
+            status = sum(pool.map(get_a_dep, deps))
+    return status
+
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/tools/iar_gen.py b/tools/iar_gen.py
index 264dd9a58..ebcfa1423 100644
--- a/tools/iar_gen.py
+++ b/tools/iar_gen.py
@@ -56,6 +56,7 @@ def Main():
 def ListPath(path, blacklist=[]):
     # Get all .c files
     files = glob.glob(f'../{path}/**/*.c', recursive=True)
+    files.extend(glob.glob(f'../{path}/**/*.h', recursive=True))
     # Filter
     files = [x for x in files if all(y not in x for y in blacklist)]
     # Get common dir list
@@ -77,6 +78,8 @@ def List():
     ListPath('lib/SEGGER_RTT')
 
 if __name__ == "__main__":
+    if os.path.dirname(os.getcwd()) != 'tools':
+        os.chdir('tools')
     if (len(sys.argv) > 1):
         if (sys.argv[1] == 'l'):
             List()
diff --git a/tools/iar_template.ipcf b/tools/iar_template.ipcf
index c3683c3d7..33a6ef045 100644
--- a/tools/iar_template.ipcf
+++ b/tools/iar_template.ipcf
@@ -10,57 +10,101 @@
 	
         
             $TUSB_DIR$/src/tusb.c
+            $TUSB_DIR$/src/tusb.h
+            $TUSB_DIR$/src/tusb_option.h
         
         
             $TUSB_DIR$/src/class/audio/audio_device.c
+            $TUSB_DIR$/src/class/audio/audio.h
+            $TUSB_DIR$/src/class/audio/audio_device.h
         
         
             $TUSB_DIR$/src/class/bth/bth_device.c
+            $TUSB_DIR$/src/class/bth/bth_device.h
         
         
             $TUSB_DIR$/src/class/cdc/cdc_device.c
             $TUSB_DIR$/src/class/cdc/cdc_host.c
             $TUSB_DIR$/src/class/cdc/cdc_rndis_host.c
+            $TUSB_DIR$/src/class/cdc/cdc.h
+            $TUSB_DIR$/src/class/cdc/cdc_device.h
+            $TUSB_DIR$/src/class/cdc/cdc_host.h
+            $TUSB_DIR$/src/class/cdc/cdc_rndis.h
+            $TUSB_DIR$/src/class/cdc/cdc_rndis_host.h
         
         
             $TUSB_DIR$/src/class/dfu/dfu_device.c
             $TUSB_DIR$/src/class/dfu/dfu_rt_device.c
+            $TUSB_DIR$/src/class/dfu/dfu.h
+            $TUSB_DIR$/src/class/dfu/dfu_device.h
+            $TUSB_DIR$/src/class/dfu/dfu_rt_device.h
         
         
             $TUSB_DIR$/src/class/hid/hid_device.c
             $TUSB_DIR$/src/class/hid/hid_host.c
+            $TUSB_DIR$/src/class/hid/hid.h
+            $TUSB_DIR$/src/class/hid/hid_device.h
+            $TUSB_DIR$/src/class/hid/hid_host.h
         
         
             $TUSB_DIR$/src/class/midi/midi_device.c
+            $TUSB_DIR$/src/class/midi/midi.h
+            $TUSB_DIR$/src/class/midi/midi_device.h
         
         
             $TUSB_DIR$/src/class/msc/msc_device.c
             $TUSB_DIR$/src/class/msc/msc_host.c
+            $TUSB_DIR$/src/class/msc/msc.h
+            $TUSB_DIR$/src/class/msc/msc_device.h
+            $TUSB_DIR$/src/class/msc/msc_host.h
         
         
             $TUSB_DIR$/src/class/net/ecm_rndis_device.c
             $TUSB_DIR$/src/class/net/ncm_device.c
+            $TUSB_DIR$/src/class/net/ncm.h
+            $TUSB_DIR$/src/class/net/net_device.h
         
         
             $TUSB_DIR$/src/class/usbtmc/usbtmc_device.c
+            $TUSB_DIR$/src/class/usbtmc/usbtmc.h
+            $TUSB_DIR$/src/class/usbtmc/usbtmc_device.h
         
         
             $TUSB_DIR$/src/class/vendor/vendor_device.c
             $TUSB_DIR$/src/class/vendor/vendor_host.c
+            $TUSB_DIR$/src/class/vendor/vendor_device.h
+            $TUSB_DIR$/src/class/vendor/vendor_host.h
         
         
             $TUSB_DIR$/src/class/video/video_device.c
+            $TUSB_DIR$/src/class/video/video.h
+            $TUSB_DIR$/src/class/video/video_device.h
         
         
             $TUSB_DIR$/src/common/tusb_fifo.c
+            $TUSB_DIR$/src/common/tusb_common.h
+            $TUSB_DIR$/src/common/tusb_compiler.h
+            $TUSB_DIR$/src/common/tusb_debug.h
+            $TUSB_DIR$/src/common/tusb_fifo.h
+            $TUSB_DIR$/src/common/tusb_mcu.h
+            $TUSB_DIR$/src/common/tusb_private.h
+            $TUSB_DIR$/src/common/tusb_types.h
+            $TUSB_DIR$/src/common/tusb_verify.h
         
         
             $TUSB_DIR$/src/device/usbd.c
             $TUSB_DIR$/src/device/usbd_control.c
+            $TUSB_DIR$/src/device/dcd.h
+            $TUSB_DIR$/src/device/usbd.h
+            $TUSB_DIR$/src/device/usbd_pvt.h
         
         
             $TUSB_DIR$/src/host/hub.c
             $TUSB_DIR$/src/host/usbh.c
+            $TUSB_DIR$/src/host/hcd.h
+            $TUSB_DIR$/src/host/hub.h
+            $TUSB_DIR$/src/host/usbh.h
+            $TUSB_DIR$/src/host/usbh_pvt.h
         
         
             $TUSB_DIR$/src/portable/analog/max3421/hcd_max3421.c
@@ -70,26 +114,39 @@
         
         
             $TUSB_DIR$/src/portable/chipidea/ci_fs/dcd_ci_fs.c
+            $TUSB_DIR$/src/portable/chipidea/ci_fs/ci_fs_kinetis.h
+            $TUSB_DIR$/src/portable/chipidea/ci_fs/ci_fs_mcx.h
+            $TUSB_DIR$/src/portable/chipidea/ci_fs/ci_fs_type.h
         
         
             $TUSB_DIR$/src/portable/chipidea/ci_hs/dcd_ci_hs.c
             $TUSB_DIR$/src/portable/chipidea/ci_hs/hcd_ci_hs.c
+            $TUSB_DIR$/src/portable/chipidea/ci_hs/ci_hs_imxrt.h
+            $TUSB_DIR$/src/portable/chipidea/ci_hs/ci_hs_lpc18_43.h
+            $TUSB_DIR$/src/portable/chipidea/ci_hs/ci_hs_mcx.h
+            $TUSB_DIR$/src/portable/chipidea/ci_hs/ci_hs_type.h
         
         
             $TUSB_DIR$/src/portable/dialog/da146xx/dcd_da146xx.c
         
         
             $TUSB_DIR$/src/portable/ehci/ehci.c
+            $TUSB_DIR$/src/portable/ehci/ehci.h
+            $TUSB_DIR$/src/portable/ehci/ehci_api.h
         
         
             $TUSB_DIR$/src/portable/mentor/musb/dcd_musb.c
             $TUSB_DIR$/src/portable/mentor/musb/hcd_musb.c
+            $TUSB_DIR$/src/portable/mentor/musb/musb_msp432e.h
+            $TUSB_DIR$/src/portable/mentor/musb/musb_tm4c.h
+            $TUSB_DIR$/src/portable/mentor/musb/musb_type.h
         
         
             $TUSB_DIR$/src/portable/microchip/pic/dcd_pic.c
         
         
             $TUSB_DIR$/src/portable/microchip/pic32mz/dcd_pic32mz.c
+            $TUSB_DIR$/src/portable/microchip/pic32mz/usbhs_registers.h
         
         
             $TUSB_DIR$/src/portable/microchip/samd/dcd_samd.c
@@ -99,6 +156,7 @@
         
         
             $TUSB_DIR$/src/portable/microchip/samx7x/dcd_samx7x.c
+            $TUSB_DIR$/src/portable/microchip/samx7x/common_usb_regs.h
         
         
             $TUSB_DIR$/src/portable/mindmotion/mm32/dcd_mm32f327x_otg.c
@@ -122,12 +180,14 @@
         
             $TUSB_DIR$/src/portable/nxp/lpc17_40/dcd_lpc17_40.c
             $TUSB_DIR$/src/portable/nxp/lpc17_40/hcd_lpc17_40.c
+            $TUSB_DIR$/src/portable/nxp/lpc17_40/dcd_lpc17_40.h
         
         
             $TUSB_DIR$/src/portable/nxp/lpc_ip3511/dcd_lpc_ip3511.c
         
         
             $TUSB_DIR$/src/portable/ohci/ohci.c
+            $TUSB_DIR$/src/portable/ohci/ohci.h
         
         
             $TUSB_DIR$/src/portable/raspberrypi/pio_usb/dcd_pio_usb.c
@@ -137,42 +197,79 @@
             $TUSB_DIR$/src/portable/raspberrypi/rp2040/dcd_rp2040.c
             $TUSB_DIR$/src/portable/raspberrypi/rp2040/hcd_rp2040.c
             $TUSB_DIR$/src/portable/raspberrypi/rp2040/rp2040_usb.c
+            $TUSB_DIR$/src/portable/raspberrypi/rp2040/rp2040_usb.h
         
         
             $TUSB_DIR$/src/portable/renesas/rusb2/dcd_rusb2.c
             $TUSB_DIR$/src/portable/renesas/rusb2/hcd_rusb2.c
             $TUSB_DIR$/src/portable/renesas/rusb2/rusb2_common.c
+            $TUSB_DIR$/src/portable/renesas/rusb2/rusb2_ra.h
+            $TUSB_DIR$/src/portable/renesas/rusb2/rusb2_rx.h
+            $TUSB_DIR$/src/portable/renesas/rusb2/rusb2_type.h
         
         
             $TUSB_DIR$/src/portable/sony/cxd56/dcd_cxd56.c
         
         
             $TUSB_DIR$/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.c
+            $TUSB_DIR$/src/portable/st/stm32_fsdev/dcd_stm32_fsdev.h
         
         
             $TUSB_DIR$/src/portable/st/typec/typec_stm32.c
         
         
             $TUSB_DIR$/src/portable/sunxi/dcd_sunxi_musb.c
+            $TUSB_DIR$/src/portable/sunxi/musb_def.h
         
         
             $TUSB_DIR$/src/portable/synopsys/dwc2/dcd_dwc2.c
+            $TUSB_DIR$/src/portable/synopsys/dwc2/dwc2_bcm.h
+            $TUSB_DIR$/src/portable/synopsys/dwc2/dwc2_efm32.h
+            $TUSB_DIR$/src/portable/synopsys/dwc2/dwc2_esp32.h
+            $TUSB_DIR$/src/portable/synopsys/dwc2/dwc2_gd32.h
+            $TUSB_DIR$/src/portable/synopsys/dwc2/dwc2_stm32.h
+            $TUSB_DIR$/src/portable/synopsys/dwc2/dwc2_type.h
+            $TUSB_DIR$/src/portable/synopsys/dwc2/dwc2_xmc.h
         
         
             $TUSB_DIR$/src/portable/ti/msp430x5xx/dcd_msp430x5xx.c
         
         
             $TUSB_DIR$/src/portable/valentyusb/eptri/dcd_eptri.c
+            $TUSB_DIR$/src/portable/valentyusb/eptri/dcd_eptri.h
         
         
+            $TUSB_DIR$/src/portable/wch/dcd_ch32_usbfs.c
             $TUSB_DIR$/src/portable/wch/dcd_ch32_usbhs.c
+            $TUSB_DIR$/src/portable/wch/ch32_usbhs_reg.h
         
         
             $TUSB_DIR$/src/typec/usbc.c
+            $TUSB_DIR$/src/typec/pd_types.h
+            $TUSB_DIR$/src/typec/tcd.h
+            $TUSB_DIR$/src/typec/usbc.h
+        
+        
+            $TUSB_DIR$/src/class/cdc/serial/ch34x.h
+            $TUSB_DIR$/src/class/cdc/serial/cp210x.h
+            $TUSB_DIR$/src/class/cdc/serial/ftdi_sio.h
+        
+        
+            $TUSB_DIR$/src/osal/osal.h
+            $TUSB_DIR$/src/osal/osal_freertos.h
+            $TUSB_DIR$/src/osal/osal_mynewt.h
+            $TUSB_DIR$/src/osal/osal_none.h
+            $TUSB_DIR$/src/osal/osal_pico.h
+            $TUSB_DIR$/src/osal/osal_rtthread.h
+            $TUSB_DIR$/src/osal/osal_rtx4.h
         
         
             $TUSB_DIR$/lib/SEGGER_RTT/RTT/SEGGER_RTT.c
             $TUSB_DIR$/lib/SEGGER_RTT/RTT/SEGGER_RTT_printf.c
+            $TUSB_DIR$/lib/SEGGER_RTT/RTT/SEGGER_RTT.h
+        
+        
+            $TUSB_DIR$/lib/SEGGER_RTT/Config/SEGGER_RTT_Conf.h
         
     
 
diff --git a/tools/make_release.py b/tools/make_release.py
index 256ca8f21..126e07292 100644
--- a/tools/make_release.py
+++ b/tools/make_release.py
@@ -1,4 +1,5 @@
 import re
+import gen_doc
 
 version = '0.16.0'
 
@@ -46,4 +47,6 @@ with open(f_library_json, 'w') as f:
 # docs/info/changelog.rst
 ###################
 
+gen_doc.gen_deps_doc()
+
 print("Update docs/info/changelog.rst")