diff --git a/.github/workflows/_runner-gap9-tiled.yml b/.github/workflows/_runner-gap9-tiled.yml index d456f9f353..a5c8b3ac98 100644 --- a/.github/workflows/_runner-gap9-tiled.yml +++ b/.github/workflows/_runner-gap9-tiled.yml @@ -45,7 +45,6 @@ jobs: source /app/install/gap9-sdk/.gap9-venv/bin/activate source /app/install/gap9-sdk/configs/gap9_evk_audio.sh || true export GVSOC_INSTALL_DIR=/app/install/gap9-sdk/install/workstation - export GAP_RISCV_GCC_TOOLCHAIN=/app/install/gcc/gap9 cd DeeployTest mkdir -p /app/.ccache export CCACHE_DIR=/app/.ccache diff --git a/.github/workflows/_runner-gap9.yml b/.github/workflows/_runner-gap9.yml index d5d8d8e4c0..e1d6e452a6 100644 --- a/.github/workflows/_runner-gap9.yml +++ b/.github/workflows/_runner-gap9.yml @@ -45,7 +45,6 @@ jobs: source /app/install/gap9-sdk/.gap9-venv/bin/activate source /app/install/gap9-sdk/configs/gap9_evk_audio.sh || true export GVSOC_INSTALL_DIR=/app/install/gap9-sdk/install/workstation - export GAP_RISCV_GCC_TOOLCHAIN=/app/install/gcc/gap9 cd DeeployTest mkdir -p /app/.ccache export CCACHE_DIR=/app/.ccache diff --git a/.github/workflows/ci-platform-gap9-tiled.yml b/.github/workflows/ci-platform-gap9-tiled.yml index 0043f8d3e9..4a3afc717b 100644 --- a/.github/workflows/ci-platform-gap9-tiled.yml +++ b/.github/workflows/ci-platform-gap9-tiled.yml @@ -25,6 +25,7 @@ concurrency: jobs: select-env: + if: github.repository == 'pulp-platform/Deeploy' uses: ./.github/workflows/_select-env.yml with: docker_image_deeploy: ${{ github.event.inputs.docker_image_deeploy || github.repository == 'pulp-platform/Deeploy' && 'ghcr.io/pulp-platform/deeploy-gap9:latest'}} diff --git a/.github/workflows/ci-platform-gap9.yml b/.github/workflows/ci-platform-gap9.yml index 079f13c2a5..e9a59b4927 100644 --- a/.github/workflows/ci-platform-gap9.yml +++ b/.github/workflows/ci-platform-gap9.yml @@ -26,6 +26,7 @@ concurrency: jobs: select-env: + if: github.repository == 'pulp-platform/Deeploy' uses: ./.github/workflows/_select-env.yml with: docker_image_deeploy: ${{ github.event.inputs.docker_image_deeploy || (github.repository == 'pulp-platform/Deeploy' && 'ghcr.io/pulp-platform/deeploy-gap9:latest') }} diff --git a/.github/workflows/docker-build-deeploy-gap9.yml b/.github/workflows/docker-build-deeploy-gap9.yml new file mode 100644 index 0000000000..e3dac03274 --- /dev/null +++ b/.github/workflows/docker-build-deeploy-gap9.yml @@ -0,0 +1,150 @@ +# SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +--- +name: Docker • Build Deeploy GAP9 Container + +"on": + workflow_dispatch: + inputs: + docker_image_deeploy: + description: "Deeploy Image to use" + required: false + default: "ghcr.io/pulp-platform/deeploy:latest" + +jobs: + prepare: + name: Fetch branch name or tag + runs-on: ubuntu-latest + outputs: + docker_tag: ${{ steps.generate_tag.outputs.docker_tag }} + steps: + - uses: actions/checkout@v4 + + - name: Set up environment variables + run: | + echo "BRANCH_NAME=${GITHUB_REF##*/}" >> $GITHUB_ENV + echo "TAG_NAME=${GITHUB_REF##*/}" >> $GITHUB_ENV + echo "IS_TAG=${GITHUB_REF_TYPE}" >> $GITHUB_ENV + + - name: Set Docker tag + id: generate_tag + run: | + if [[ "${{ env.IS_TAG }}" == "tag" ]]; then + echo "docker_tag=${{ env.TAG_NAME }}" >> $GITHUB_OUTPUT + else + echo "docker_tag=${{ env.BRANCH_NAME }}" >> $GITHUB_OUTPUT + fi + + build-deeploy-gap9: + name: Build Deeploy GAP9 Image + needs: [prepare] + runs-on: ${{ matrix.runner }} + outputs: + digest-amd64: ${{ steps.digest.outputs.digest-amd64 }} + digest-arm64: ${{ steps.digest.outputs.digest-arm64 }} + strategy: + fail-fast: false + matrix: + platform: [amd64, arm64] + include: + - platform: amd64 + runner: ubuntu-latest + - platform: arm64 + runner: ubuntu-22.04-arm + steps: + - uses: actions/checkout@v4 + + - name: Free up disk space + uses: jlumbroso/free-disk-space@v1.3.1 + with: + tool-cache: true + android: true + dotnet: true + haskell: true + large-packages: true + + - uses: docker/setup-buildx-action@v3 + + - name: GHCR Log-in + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build Cache for Docker + id: cache + uses: actions/cache@v4 + with: + path: var-ccache + key: ${{ runner.os }}-${{ matrix.platform }}-build-cache-deeploy-gap9 + + - name: Inject build-cache + uses: reproducible-containers/buildkit-cache-dance@v3.1.0 + with: + cache-map: | + { + "var-ccache": "/ccache" + } + skip-extraction: ${{ steps.cache.outputs.cache-hit }} + + - name: Lower Case Repository Name + run: | + echo "OWNER_LC=${OWNER,,}" >>${GITHUB_ENV} + env: + OWNER: "${{ github.repository_owner }}" + + - name: Load SSH key + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Build and push final Deeploy image + id: build + uses: docker/build-push-action@v6 + with: + platforms: linux/${{ matrix.platform }} + context: . + cache-from: type=gha + cache-to: type=gha,mode=min + file: Container/Dockerfile.deeploy-gap9 + push: true + build-args: | + DEEPLOY_IMAGE=${{ github.event.inputs.docker_image_deeploy }} + ssh: default + outputs: type=image,name=ghcr.io/${{ env.OWNER_LC }}/deeploy-gap9,annotation-index=true,name-canonical=true,push=true + + - name: Extract image digest + id: digest + run: echo "digest-${{ matrix.platform }}=${{ steps.build.outputs.digest }}" >> $GITHUB_OUTPUT + + merge-deeploy-gap9-images: + name: Merge Deeploy GAP9 Images + runs-on: ubuntu-latest + needs: [prepare, build-deeploy-gap9] + steps: + - name: GHCR Log-in + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Lower Case Repository Name + run: | + echo "OWNER_LC=${OWNER,,}" >>${GITHUB_ENV} + env: + OWNER: "${{ github.repository_owner }}" + + - name: Merge Deeploy GAP9 Images + uses: Noelware/docker-manifest-action@v1 + with: + inputs: | + ghcr.io/${{ env.OWNER_LC }}/deeploy-gap9@${{ needs.build-deeploy-gap9.outputs.digest-amd64 }}, + ghcr.io/${{ env.OWNER_LC }}/deeploy-gap9@${{ needs.build-deeploy-gap9.outputs.digest-arm64 }} + tags: | + ghcr.io/${{ env.OWNER_LC }}/deeploy-gap9:latest, + ghcr.io/${{ env.OWNER_LC }}/deeploy-gap9:${{ needs.prepare.outputs.docker_tag }} + push: true diff --git a/.github/workflows/docker-build-deeploy.yml b/.github/workflows/docker-build-deeploy.yml index a4d0221e1f..ff2b507405 100644 --- a/.github/workflows/docker-build-deeploy.yml +++ b/.github/workflows/docker-build-deeploy.yml @@ -109,7 +109,7 @@ jobs: env: OWNER: "${{ github.repository_owner }}" - - name: Build and push final deploy image + - name: Build and push final Deeploy image id: build uses: docker/build-push-action@v6 with: diff --git a/.github/workflows/infra-generate-ccache-gap9.yml b/.github/workflows/infra-generate-ccache-gap9.yml index b189bfd708..038789ce40 100644 --- a/.github/workflows/infra-generate-ccache-gap9.yml +++ b/.github/workflows/infra-generate-ccache-gap9.yml @@ -18,6 +18,7 @@ name: Infrastructure • Generate CCache GAP9 jobs: generate-ccache-gap9: + if: github.repository == 'pulp-platform/Deeploy' runs-on: ubuntu-latest container: image: ${{ github.event.inputs.docker_image_deeploy || 'ghcr.io/pulp-platform/deeploy-gap9:latest' }} @@ -39,7 +40,6 @@ jobs: source /app/install/gap9-sdk/.gap9-venv/bin/activate source /app/install/gap9-sdk/configs/gap9_evk_audio.sh || true export GVSOC_INSTALL_DIR=/app/install/gap9-sdk/install/workstation - export GAP_RISCV_GCC_TOOLCHAIN=/app/install/gcc/gap9 cd DeeployTest mkdir -p /app/.ccache export CCACHE_DIR=/app/.ccache diff --git a/.gitignore b/.gitignore index dc93328e4a..d9e4faace3 100644 --- a/.gitignore +++ b/.gitignore @@ -24,6 +24,7 @@ install/ compile_commands.json toolchain/**/*/ + # Node package.json package-lock.json @@ -52,3 +53,7 @@ DeeployTest/Tests/**/generateTest.py DeeployTest/out.txt CHANGELOG_GEN.md + +# Container Artifacts +.pyusbip/ +.cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f1d58d3265..a8f323faf5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,6 +13,7 @@ exclude: | | .*TEST_.* | .*TestFiles.* | .*runtime.* + | .*\.patch | .*prebuilt/.* ) @@ -71,3 +72,7 @@ repos: - id: yamllint name: Lint YAML Files stages: [pre-commit, pre-merge-commit, pre-push, manual] + - repo: https://github.com/scop/pre-commit-shfmt + rev: v3.12.0-2 + hooks: + - id: shfmt diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f4e08ba71..a277d2361d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ This file contains the changelog for the Deeploy project. The changelog is divid ### List of Pull Requests +- Add GAP9 Container Support [#163](https://github.com/pulp-platform/Deeploy/pull/163) - Extend Codeowners [#164](https://github.com/pulp-platform/Deeploy/pull/164) - Support for MaxPool1D and RQSConv1D for PULPOpen [#146](https://github.com/pulp-platform/Deeploy/pull/146) - Use Pre-Commit in CI [#159](https://github.com/pulp-platform/Deeploy/pull/159) @@ -12,10 +13,14 @@ This file contains the changelog for the Deeploy project. The changelog is divid - Update CLI interface Across Project, Fix Tutorial, and Remove Legacy Test [#157](https://github.com/pulp-platform/Deeploy/pull/157) ### Added +- GAP9 Container Support with ARM64 architecture support +- `zsh` and `oh-my-zsh` plugin installation in containers +- Shell Format pre-commit hook - Add integer MaxPool1D for Generic platform and RQSConv1D support for PULPOpen, with corresponding kernel tests. - Added GAP9 Platform Support: Deployer, Bindings, Templates, Tiler, DMA (L3Dma/MchanDma), target library, CI workflows ### Changed +- Cleaned up Docker flow to use a temporary build folder - Switch CI to use pre-commit for linting - Update `pulp-nnx` and `pulp-nn-mixed` submodules to their latest versions - PULP-NN moved to TargetLibraries third-party folder diff --git a/Container/.zshrc b/Container/.zshrc new file mode 100644 index 0000000000..c690527643 --- /dev/null +++ b/Container/.zshrc @@ -0,0 +1,116 @@ +# SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +# If you come from bash you might have to change your $PATH. +# export PATH=$HOME/bin:$HOME/.local/bin:/usr/local/bin:$PATH + +# Path to your Oh My Zsh installation. +export ZSH="$HOME/.oh-my-zsh" + +# Set name of the theme to load --- if set to "random", it will +# load a random theme each time Oh My Zsh is loaded, in which case, +# to know which specific one was loaded, run: echo $RANDOM_THEME +# See https://github.com/ohmyzsh/ohmyzsh/wiki/Themes +ZSH_THEME="cypher" + +# Set list of themes to pick from when loading at random +# Setting this variable when ZSH_THEME=random will cause zsh to load +# a theme from this variable instead of looking in $ZSH/themes/ +# If set to an empty array, this variable will have no effect. +# ZSH_THEME_RANDOM_CANDIDATES=( "robbyrussell" "agnoster" ) + +# Uncomment the following line to use case-sensitive completion. +# CASE_SENSITIVE="true" + +# Uncomment the following line to use hyphen-insensitive completion. +# Case-sensitive completion must be off. _ and - will be interchangeable. +# HYPHEN_INSENSITIVE="true" + +# Uncomment one of the following lines to change the auto-update behavior +# zstyle ':omz:update' mode disabled # disable automatic updates +# zstyle ':omz:update' mode auto # update automatically without asking +# zstyle ':omz:update' mode reminder # just remind me to update when it's time + +# Uncomment the following line to change how often to auto-update (in days). +# zstyle ':omz:update' frequency 13 + +# Uncomment the following line if pasting URLs and other text is messed up. +# DISABLE_MAGIC_FUNCTIONS="true" + +# Uncomment the following line to disable colors in ls. +# DISABLE_LS_COLORS="true" + +# Uncomment the following line to disable auto-setting terminal title. +# DISABLE_AUTO_TITLE="true" + +# Uncomment the following line to enable command auto-correction. +# ENABLE_CORRECTION="true" + +# Uncomment the following line to display red dots whilst waiting for completion. +# You can also set it to another string to have that shown instead of the default red dots. +# e.g. COMPLETION_WAITING_DOTS="%F{yellow}waiting...%f" +# Caution: this setting can cause issues with multiline prompts in zsh < 5.7.1 (see #5765) +# COMPLETION_WAITING_DOTS="true" + +# Uncomment the following line if you want to disable marking untracked files +# under VCS as dirty. This makes repository status check for large repositories +# much, much faster. +# DISABLE_UNTRACKED_FILES_DIRTY="true" + +# Uncomment the following line if you want to change the command execution time +# stamp shown in the history command output. +# You can set one of the optional three formats: +# "mm/dd/yyyy"|"dd.mm.yyyy"|"yyyy-mm-dd" +# or set a custom format using the strftime function format specifications, +# see 'man strftime' for details. +# HIST_STAMPS="mm/dd/yyyy" + +# Would you like to use another custom folder than $ZSH/custom? +# ZSH_CUSTOM=/path/to/new-custom-folder + +# Which plugins would you like to load? +# Standard plugins can be found in $ZSH/plugins/ +# Custom plugins may be added to $ZSH_CUSTOM/plugins/ +# Example format: plugins=(rails git textmate ruby lighthouse) +# Add wisely, as too many plugins slow down shell startup. +plugins=(git) + +source $ZSH/oh-my-zsh.sh + +# User configuration + +# export MANPATH="/usr/local/man:$MANPATH" + +# You may need to manually set your language environment +# export LANG=en_US.UTF-8 + +# Preferred editor for local and remote sessions +# if [[ -n $SSH_CONNECTION ]]; then +# export EDITOR='vim' +# else +# export EDITOR='nvim' +# fi + +# Compilation flags +# export ARCHFLAGS="-arch $(uname -m)" + +# Set personal aliases, overriding those provided by Oh My Zsh libs, +# plugins, and themes. Aliases can be placed here, though Oh My Zsh +# users are encouraged to define aliases within a top-level file in +# the $ZSH_CUSTOM folder, with .zsh extension. Examples: +# - $ZSH_CUSTOM/aliases.zsh +# - $ZSH_CUSTOM/macos.zsh +# For a full list of active aliases, run `alias`. +# +# Example aliases +alias zshconfig="nano ~/.zshrc" +# alias ohmyzsh="mate ~/.oh-my-zsh" + +unsetopt HIST_SAVE_BY_COPY +setopt APPEND_HISTORY +setopt SHARE_HISTORY +setopt HIST_IGNORE_ALL_DUPS + +# Make sure the gap command of the GAP9 SDK works +unalias gap diff --git a/Container/Dockerfile.deeploy b/Container/Dockerfile.deeploy index ee5adc5e4f..2b717e49ec 100644 --- a/Container/Dockerfile.deeploy +++ b/Container/Dockerfile.deeploy @@ -14,15 +14,17 @@ ARG TARGETPLATFORM ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 ENV PATH="/app/install/bender:${PATH}" +ENV DEEPLOY_INSTALL_DIR=/app/install ENV LLVM_INSTALL_DIR=/app/install/llvm -WORKDIR /app +WORKDIR /app/build # Make sure updates in the repo are reflected in the image COPY toolchain/*.patch toolchain/ COPY Makefile ./ -COPY requirements-dev.txt ./ # Compile emulators # WIESEP: We need to already clean up some space, otherwise the GitHub runners run out of disk space @@ -30,8 +32,8 @@ RUN --mount=type=cache,target=/ccache \ ccache -z && \ make pulp-sdk chimera-sdk qemu mempool banshee xtensor && \ make gvsoc && \ - cp -r /app/toolchain/gvsoc/core/requirements.txt /app/core-requirements.txt && \ - cp -r /app/toolchain/gvsoc/gapy/requirements.txt /app/gapy-requirements.txt && \ + cp -r toolchain/gvsoc/core/requirements.txt /app/core-requirements.txt && \ + cp -r toolchain/gvsoc/gapy/requirements.txt /app/gapy-requirements.txt && \ rm -rf toolchain/pulp-sdk toolchain/qemu toolchain/mempool toolchain/banshee toolchain/xtensor toolchain/xtl toolchain/xsimd toolchain/gvsoc && \ ccache -s @@ -54,13 +56,15 @@ fi # Compile Snitch Runtime ENV CC="gcc" ENV CXX="g++" +ENV PATH=/app/install/bender:${PATH} RUN --mount=type=cache,target=/ccache \ ccache -z && \ make snitch_runtime && \ ccache -s # Remove toolchain to make the container lighter -RUN rm -rf toolchain +WORKDIR /app +RUN rm -rf /app/build ########## Stage 2: Lightweight image with precompiled toolchain and emulators ########## FROM ubuntu:22.04 AS deeploy @@ -68,9 +72,12 @@ FROM ubuntu:22.04 AS deeploy ARG TARGETPLATFORM ARG DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 # Export symbols necessary for Deeploy's build flow ENV CMAKE=/usr/bin/cmake +ENV DEEPLOY_INSTALL_DIR=/app/install ENV PULP_SDK_HOME=/app/install/pulp-sdk ENV SNITCH_HOME=/app/install/snitch_cluster ENV CHIMERA_SDK_HOME=/app/install/chimera-sdk @@ -83,7 +90,7 @@ ENV MEMPOOL_HOME=/app/install/mempool ENV BENDER_INSTALL_DIR=/app/install/bender ENV PATH=/app/install/qemu/bin:/app/install/banshee:/app/install/bender:$PATH -WORKDIR /app +WORKDIR /app/build COPY pyproject.toml ./ @@ -99,7 +106,8 @@ RUN apt-get update && \ python-is-python3 \ python3.10-venv \ python3.10-distutils \ - gcc && \ + gcc \ + zsh && \ curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \ python get-pip.py && \ rm get-pip.py && \ @@ -118,10 +126,19 @@ elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ ./cmake-3.24.0-linux-aarch64.sh --prefix=/usr --skip-license; \ fi +COPY Container/.zshrc ./ +# # Install Oh My ZSH +RUN sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" "" --unattended && \ + cp .zshrc /root/.zshrc + COPY --from=toolchain /app/core-requirements.txt ./core-requirements.txt COPY --from=toolchain /app/gapy-requirements.txt ./gapy-requirements.txt -COPY --from=toolchain /app/requirements-dev.txt ./ +COPY requirements-dev.txt ./ RUN pip install -r requirements-dev.txt -r core-requirements.txt -r gapy-requirements.txt # Copy pre-built toolchains and emulators -COPY --from=toolchain /app/install ./install +COPY --from=toolchain /app/install /app/install + +# Remove unused files and clean up to reduce image size +WORKDIR /app +RUN rm -rf /app/build diff --git a/Container/Dockerfile.deeploy-gap9 b/Container/Dockerfile.deeploy-gap9 new file mode 100644 index 0000000000..21724019b4 --- /dev/null +++ b/Container/Dockerfile.deeploy-gap9 @@ -0,0 +1,91 @@ +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +ARG DEEPLOY_IMAGE=ghcr.io/pulp-platform/deeploy:latest +FROM ${DEEPLOY_IMAGE} AS deeploy + +ARG TARGETPLATFORM +ARG DEBIAN_FRONTEND=noninteractive + +ENV GAP_RISCV_GCC_TOOLCHAIN=/app/install/gcc/gap9 +ENV GAP_SDK_HOME=/app/install/gap9-sdk +ENV GAP9_SDK_INSTALL_DIR=/app/install/gap9-sdk +ENV GAP_RISCV_GCC_INSTALL_DIR=/app/install/gcc/gap9 + +WORKDIR /app/build + +# Install SSH keys to access private repositories +RUN mkdir -p -m 0700 ~/.ssh && \ + ssh-keyscan iis-git.ee.ethz.ch >> ~/.ssh/known_hosts && \ + ssh-keyscan github.com >> ~/.ssh/known_hosts + +COPY toolchain/*.patch toolchain/ +COPY Makefile ./ + +RUN apt-get update && \ + apt-get upgrade -y && \ + apt-get install -y \ + autoconf \ + automake \ + bison \ + ccache \ + clang-format \ + curl \ + device-tree-compiler \ + doxygen \ + flex \ + g++ \ + gcc \ + gcc-x86-64-linux-gnu \ + gdb-multiarch \ + git-lfs \ + gtkwave \ + ibglib2.0-dev \ + libftdi-dev \ + libftdi1 \ + libpixman-1-dev \ + libsdl2-dev \ + libsdl2-ttf-dev \ + libsndfile1-dev \ + libtool \ + libusb-1.0-0-dev \ + nano \ + ninja-build \ + pkg-config \ + protobuf-compiler \ + python-is-python3 \ + python3 \ + python3-dev \ + python3-pip \ + python3.10-distutils \ + python3.10-venv \ + rsync \ + scons \ + ssh \ + sudo \ + telnet \ + texinfo \ + usbutils \ + wget \ + zsh && \ + rm -rf /var/lib/apt/lists/* + +RUN --mount=type=cache,target=/ccache \ + ccache -z && make gap9-toolchain + +COPY Container/gap9-amd64.patch Container/gap9-arm64.patch ./ +COPY scripts/gap9-build_sdk.sh ./scripts/ +RUN --mount=type=ssh \ + --mount=type=cache,target=/ccache \ + --mount=type=cache,target=/root/.cache/pip \ + ccache -z && \ + make gap9-sdk && \ + ccache -s && \ + rm -rf /app/install/gap9-sdk/build && \ + rm -rf /app/install/gap9-sdk/.git + + +# Remove unused files and clean up to reduce image size +WORKDIR /app +RUN rm -rf /app/build \ No newline at end of file diff --git a/Container/Dockerfile.toolchain b/Container/Dockerfile.toolchain index 3bd03b14da..be36a5edf5 100644 --- a/Container/Dockerfile.toolchain +++ b/Container/Dockerfile.toolchain @@ -10,12 +10,15 @@ ARG TARGETPLATFORM ENV DEBIAN_FRONTEND=noninteractive ENV TZ=Etc/UTC +ENV LANG=C.UTF-8 +ENV LC_ALL=C.UTF-8 ENV CC="ccache gcc" ENV CXX="ccache g++" ENV CCACHE_DIR=/ccache +ENV DEEPLOY_INSTALL_DIR=/app/install # Change the working directory -WORKDIR /app +WORKDIR /app/build # Install dependencies RUN apt-get update && \ @@ -84,7 +87,5 @@ RUN --mount=type=cache,target=/ccache \ make picolibc-riscv && ccache -s # Remove unecessary files -RUN rm -rf cmake-* && \ - rm -rf toolchain/llvm-project && \ - rm -rf toolchain/minimalloc && \ - rm -rf toolchain/picolibc \ No newline at end of file +WORKDIR /app +RUN rm -rf build diff --git a/Container/Makefile b/Container/Makefile index cd07d8632b..e2ba85a65d 100644 --- a/Container/Makefile +++ b/Container/Makefile @@ -4,39 +4,63 @@ # Variables TOOLCHAIN_IMAGE ?= ghcr.io/pulp-platform/deeploy-toolchain:latest -DEEPOY_IMAGE ?= ghcr.io/pulp-platform/deeploy:latest +DEEPLOY_IMAGE ?= ghcr.io/pulp-platform/deeploy:latest +DEEPLOY_GAP9_IMAGE ?= ghcr.io/pulp-platform/deeploy-gap9:latest -.PHONY: all toolchain deeploy push clean help +SSH_PRIVATE_KEY ?= ~/.ssh/id_ed25519 + +.PHONY: all toolchain deeploy deeploy-gap9 push clean help help: @echo "Makefile for building Deeploy Docker images" @echo "" @echo "Usage:" - @echo " make toolchain # Build only the toolchain image" - @echo " make deeploy # Build only the deploy image" - @echo " make all # Build both images" - @echo " make clean # Remove all images (optional and dangerous)" + @echo " make toolchain # Build only the toolchain image" + @echo " make deeploy # Build only the deeploy image" + @echo " make deeploy-gap9 # Build only the GAP9 deeploy image" + @echo " make all # Build all images" + @echo " make clean # Remove all images (optional and dangerous)" @echo "" @echo "Build Variables:" - @echo " TOOLCHAIN_IMAGE # Name of the toolchain image (default: $(TOOLCHAIN_IMAGE))" - @echo " DEEPOY_IMAGE # Name of the deploy image (default: $(DEEPOY_IMAGE))" + @echo " TOOLCHAIN_IMAGE # Name of the toolchain image (default: $(TOOLCHAIN_IMAGE))" + @echo " DEEPLOY_IMAGE # Name of the deeploy image (default: $(DEEPLOY_IMAGE))" + @echo " DEEPLOY_GAP9_IMAGE # Name of the GAP9 deeploy image (default: $(DEEPLOY_GAP9_IMAGE))" + @echo " SSH_PRIVATE_KEY # Path to SSH private key for GAP9 build (default: $(SSH_PRIVATE_KEY))" @echo "" @echo "Example Usage:" @echo " make toolchain TOOLCHAIN_IMAGE=my-toolchain:latest" - @echo " make deeploy DEEPOY_IMAGE=my-deploy:latest" - @echo " make all TOOLCHAIN_IMAGE=my-toolchain:latest DEEPOY_IMAGE=my-deploy:latest" + @echo " make deeploy DEEPLOY_IMAGE=my-deeploy:latest" + @echo " make deeploy-gap9 DEEPLOY_GAP9_IMAGE=my-deeploy-gap9:latest" + @echo " make all TOOLCHAIN_IMAGE=my-toolchain:latest DEEPLOY_IMAGE=my-deeploy:latest" + @echo " make deeploy-gap9 SSH_PRIVATE_KEY=/path/to/key" # Build only the toolchain image toolchain: - DOCKER_BUILDKIT=1 docker build -f Dockerfile.toolchain -t $(TOOLCHAIN_IMAGE) .. + DOCKER_BUILDKIT=1 docker build \ + -f Dockerfile.toolchain \ + -t $(TOOLCHAIN_IMAGE) .. -# Build the final deploy image using the toolchain image +# Build the final Deeploy image using the toolchain image deeploy: - DOCKER_BUILDKIT=1 docker build -f Dockerfile.deeploy --build-arg BASE_IMAGE=$(TOOLCHAIN_IMAGE) -t $(DEEPOY_IMAGE) .. + DOCKER_BUILDKIT=1 docker build \ + --ssh default \ + -f Dockerfile.deeploy \ + --build-arg BASE_IMAGE=$(TOOLCHAIN_IMAGE) \ + -t $(DEEPLOY_IMAGE) .. + +# Build the GAP9 Deeploy image +deeploy-gap9: + eval $$(ssh-agent) && \ + ssh-add $(SSH_PRIVATE_KEY) && \ + DOCKER_BUILDKIT=1 docker build \ + --ssh default \ + -f Dockerfile.deeploy-gap9 \ + --build-arg DEEPLOY_IMAGE=$(DEEPLOY_IMAGE) \ + -t $(DEEPLOY_GAP9_IMAGE) .. -# Build both -all: toolchain deeploy +# Build all images +all: toolchain deeploy deeploy-gap9 # Clean all images (optional and dangerous) clean: - docker rmi $(TOOLCHAIN_IMAGE) $(DEEPOY_IMAGE) || true + docker rmi $(TOOLCHAIN_IMAGE) $(DEEPLOY_IMAGE) $(DEEPLOY_GAP9_IMAGE) || true diff --git a/Container/gap9-amd64.patch b/Container/gap9-amd64.patch new file mode 100644 index 0000000000..ef90f76938 --- /dev/null +++ b/Container/gap9-amd64.patch @@ -0,0 +1,62 @@ +diff --git a/.gitignore b/.gitignore +index 6e751e203..d9091f1b1 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -57,3 +57,5 @@ gaptest_reports/ + # Release script dry-run output + release_dry_run/ + artifact.txt ++ ++.gap9-venv +diff --git a/install_python_deps.sh b/install_python_deps.sh +index cac3e1885..578bb5f71 100755 +--- a/install_python_deps.sh ++++ b/install_python_deps.sh +@@ -69,7 +69,7 @@ then + python -m pip install -r tools/nntool/tests/requirements.txt + fi + +- python -m pip install -r tools/audio-framework/requirements.txt ++ # python -m pip install -r tools/audio-framework/requirements.txt + python -m pip install -r utils/gapy_v2/requirements.txt + python -m pip install -r doc/requirements.txt + +diff --git a/utils/gapy_v2/bin/gapylib/chips/gap/gap9_v2/board_runner.py b/utils/gapy_v2/bin/gapylib/chips/gap/gap9_v2/board_runner.py +index 62ee5cac2..d872073f7 100644 +--- a/utils/gapy_v2/bin/gapylib/chips/gap/gap9_v2/board_runner.py ++++ b/utils/gapy_v2/bin/gapylib/chips/gap/gap9_v2/board_runner.py +@@ -342,7 +342,7 @@ class Openocd(): + else: + raise RuntimeError('Failed to connect to openocd after 30 retries') + +- self.telnet = pexpect.spawn(f'telnet localhost {port}', encoding='utf-8', echo=False) ++ self.telnet = pexpect.spawn(f'telnet host.docker.internal {port}', encoding='utf-8', echo=False) + match = self.telnet.expect(['Open On-Chip Debugger'], timeout=None) + + def write(self, addr: int, value: int): +diff --git a/utils/openocd_tools/tcl/gap9revb.tcl b/utils/openocd_tools/tcl/gap9revb.tcl +index 9e67f67c7..697483112 100644 +--- a/utils/openocd_tools/tcl/gap9revb.tcl ++++ b/utils/openocd_tools/tcl/gap9revb.tcl +@@ -24,8 +24,8 @@ proc jtag_init {} { + #gap_reset 0 100 + + # wait for jtag ready +- poll_confreg 0x1 +- echo "INIT: confreg polling done" ++ # poll_confreg 0x1 ++ # echo "INIT: confreg polling done" + + #UNCOMMENT IF YOU UNCOMMENT gap_init 1 + #gap_start 1 +@@ -39,8 +39,8 @@ proc init_reset {mode} { + #targets $::_FC + gap_reset 1 100 + # wait for jtag ready +- poll_confreg 0x1 +- echo "RESET: confreg polling done" ++ # poll_confreg 0x1 ++ # echo "RESET: confreg polling done" + jtag arp_init + } + diff --git a/Container/gap9-arm64.patch b/Container/gap9-arm64.patch new file mode 100644 index 0000000000..4f6c146ba8 --- /dev/null +++ b/Container/gap9-arm64.patch @@ -0,0 +1,85 @@ +diff --git a/.gitignore b/.gitignore +index 6e751e203..d9091f1b1 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -57,3 +57,5 @@ gaptest_reports/ + # Release script dry-run output + release_dry_run/ + artifact.txt ++ ++.gap9-venv +diff --git a/Makefile b/Makefile +index 1dbab3578..60467d1d6 100644 +--- a/Makefile ++++ b/Makefile +@@ -119,7 +119,7 @@ openocd.checkout: + fi + + openocd.build: openocd.checkout +- cd utils/openocd && ./bootstrap && ./configure --enable-jtag_dpi --disable-werror --prefix=$(INSTALL_DIR)/openocd && $(MAKE) && $(MAKE) install ++ cd utils/openocd && ./bootstrap && ./configure --enable-jtag_dpi --enable-ftdi --disable-werror --prefix=$(INSTALL_DIR)/openocd --build=aarch64-unknown-linux-gnu && $(MAKE) && $(MAKE) install + + + openocd.checkout.gap9.5: +diff --git a/configs/common.sh b/configs/common.sh +index bd1f6ffea..e63faefa0 100644 +--- a/configs/common.sh ++++ b/configs/common.sh +@@ -178,3 +178,5 @@ if [ -n "$GAP_SDK_CI_BUILD" -a -z "$GAP_SDK_CI_DISABLE_GITHUB_ENV" ]; then + fi + # There must a newline here since the build version is appended in the release process + export GAP_SDK_VERSION=release-v5.21.1 ++ ++export WITH_EMPTY_SFU=1 +diff --git a/install_python_deps.sh b/install_python_deps.sh +index cac3e1885..578bb5f71 100755 +--- a/install_python_deps.sh ++++ b/install_python_deps.sh +@@ -69,7 +69,7 @@ then + python -m pip install -r tools/nntool/tests/requirements.txt + fi + +- python -m pip install -r tools/audio-framework/requirements.txt ++ # python -m pip install -r tools/audio-framework/requirements.txt + python -m pip install -r utils/gapy_v2/requirements.txt + python -m pip install -r doc/requirements.txt + +diff --git a/utils/gapy_v2/bin/gapylib/chips/gap/gap9_v2/board_runner.py b/utils/gapy_v2/bin/gapylib/chips/gap/gap9_v2/board_runner.py +index 62ee5cac2..d872073f7 100644 +--- a/utils/gapy_v2/bin/gapylib/chips/gap/gap9_v2/board_runner.py ++++ b/utils/gapy_v2/bin/gapylib/chips/gap/gap9_v2/board_runner.py +@@ -342,7 +342,7 @@ class Openocd(): + else: + raise RuntimeError('Failed to connect to openocd after 30 retries') + +- self.telnet = pexpect.spawn(f'telnet localhost {port}', encoding='utf-8', echo=False) ++ self.telnet = pexpect.spawn(f'telnet host.docker.internal {port}', encoding='utf-8', echo=False) + match = self.telnet.expect(['Open On-Chip Debugger'], timeout=None) + + def write(self, addr: int, value: int): +diff --git a/utils/openocd_tools/tcl/gap9revb.tcl b/utils/openocd_tools/tcl/gap9revb.tcl +index 9e67f67c7..697483112 100644 +--- a/utils/openocd_tools/tcl/gap9revb.tcl ++++ b/utils/openocd_tools/tcl/gap9revb.tcl +@@ -24,8 +24,8 @@ proc jtag_init {} { + #gap_reset 0 100 + + # wait for jtag ready +- poll_confreg 0x1 +- echo "INIT: confreg polling done" ++ # poll_confreg 0x1 ++ # echo "INIT: confreg polling done" + + #UNCOMMENT IF YOU UNCOMMENT gap_init 1 + #gap_start 1 +@@ -39,8 +39,8 @@ proc init_reset {mode} { + #targets $::_FC + gap_reset 1 100 + # wait for jtag ready +- poll_confreg 0x1 +- echo "RESET: confreg polling done" ++ # poll_confreg 0x1 ++ # echo "RESET: confreg polling done" + jtag arp_init + } + diff --git a/DeeployTest/Platforms/GAP9/CMakeLists.txt b/DeeployTest/Platforms/GAP9/CMakeLists.txt index 0a7fde9c00..db06a4e38f 100644 --- a/DeeployTest/Platforms/GAP9/CMakeLists.txt +++ b/DeeployTest/Platforms/GAP9/CMakeLists.txt @@ -29,4 +29,7 @@ target_compile_options(network PRIVATE -Wno-error ) +target_link_options(${ProjectId} PRIVATE + -Wl,--print-memory-usage +) link_compile_dump(${TESTNAME}) diff --git a/Makefile b/Makefile index d40a49da11..44a74542ff 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,9 @@ PICOLIBC_RV32IMA_INSTALL_DIR ?= ${LLVM_INSTALL_DIR}/picolibc/riscv/rv32ima PICOLIBC_RV32IMAFD_INSTALL_DIR ?= ${LLVM_INSTALL_DIR}/picolibc/riscv/rv32imafd PICOLIBC_RV32IMF_INSTALL_DIR ?= ${LLVM_INSTALL_DIR}/picolibc/riscv/rv32imf +GCC_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/gcc +GAP_RISCV_GCC_INSTALL_DIR ?= ${GCC_INSTALL_DIR}/gap9 + CHIMERA_SDK_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/chimera-sdk PULP_SDK_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/pulp-sdk SNITCH_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/snitch_cluster @@ -36,6 +39,7 @@ MINIMALLOC_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/minimalloc XTL_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/xtl XSIMD_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/xsimd XTENSOR_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/xtensor +GAP9_SDK_INSTALL_DIR ?= ${DEEPLOY_INSTALL_DIR}/gap9-sdk CMAKE ?= cmake @@ -52,19 +56,27 @@ XTL_VERSION ?= 0.7.5 XSIMD_VERSION ?= 13.2.0 XTENSOR_VERSION ?= 0.25.0 +GAP_RISCV_GCC_COMMIT_HASH ?= fbb9fa450d01c1c170f94af817490f41c5ef7971 +# GAP9_SDK_COMMIT_HASH ?= 1796873cec9ca1feb352a6fe980b627df979bdd1 # v5.21.1 +GAP9_SDK_COMMIT_HASH ?= 8c42b65338e554ac73c749f94ecddd23a9ee5490 # v5.21.1-staging-1 +GAP_SDK_URL ?= git@github.com:pulp-platform/gap-sdk.git + OS := $(shell uname -s) ARCH:= $(shell uname -m) ifeq ($(OS),Linux) ifeq ($(ARCH),x86_64) - TARGET := x86_64-unknown-linux-gnu + TARGET_BANSHEE := x86_64-unknown-linux-gnu + TARGET_GAP9 := amd64 else ifeq ($(ARCH),aarch64) - TARGET := aarch64-unknown-linux-gnu + TARGET_BANSHEE := aarch64-unknown-linux-gnu + TARGET_GAP9 := arm64 else $(error unsupported Linux architecture $(ARCH)) endif else ifeq ($(OS),Darwin) - TARGET := aarch64-apple-darwin + TARGET_BANSHEE := aarch64-apple-darwin + $(warning "Deeploy is not fully supported on macOS, some components such as the GAP9 GCC toolchain are not available. Please use Linux (or Docker) for the best experience.") else $(error unsupported platform $(OS)) endif @@ -77,6 +89,8 @@ echo-bash: @echo "The following symbols need to be exported for Deeploy to work properly:" @echo "export MINIMALLOC_INSTALL_DIR=${MINIMALLOC_INSTALL_DIR}" @echo "export PULP_SDK_HOME=${PULP_SDK_INSTALL_DIR}" + @echo "export GAP_SDK_HOME=${GAP9_SDK_INSTALL_DIR}" + @echo "export GAP_RISCV_GCC_TOOLCHAIN=${GAP_RISCV_GCC_INSTALL_DIR}" @echo "export CHIMERA_SDK_HOME=${CHIMERA_SDK_INSTALL_DIR}" @echo "export SNITCH_HOME=${SNITCH_INSTALL_DIR}" @echo "export GVSOC_INSTALL_DIR=${GVSOC_INSTALL_DIR}" @@ -97,9 +111,11 @@ emulators: snitch_runtime pulp-sdk qemu banshee mempool ${TOOLCHAIN_DIR}/llvm-project: cd ${TOOLCHAIN_DIR} && \ - git clone https://github.com/pulp-platform/llvm-project.git \ - -b main && \ - cd ${TOOLCHAIN_DIR}/llvm-project && git checkout ${LLVM_COMMIT_HASH} && \ + git init llvm-project && \ + cd ${TOOLCHAIN_DIR}/llvm-project && \ + git remote add origin https://github.com/pulp-platform/llvm-project.git && \ + git fetch --depth=1 origin ${LLVM_COMMIT_HASH} && \ + git checkout ${LLVM_COMMIT_HASH} && \ git submodule update --init --recursive ${LLVM_INSTALL_DIR}: ${TOOLCHAIN_DIR}/llvm-project @@ -410,6 +426,23 @@ ${PULP_SDK_INSTALL_DIR}: ${TOOLCHAIN_DIR}/pulp-sdk pulp-sdk: ${PULP_SDK_INSTALL_DIR} +${GAP_RISCV_GCC_INSTALL_DIR}: + mkdir -p ${GAP_RISCV_GCC_INSTALL_DIR} && cd ${GAP_RISCV_GCC_INSTALL_DIR} && \ + curl -LO https://github.com/pulp-platform/gap-riscv-gnu-toolchain/releases/download/v0.0.1/gap9-gcc-ubuntu22.04-$(TARGET_GAP9).tar.gz && \ + tar -xzf gap9-gcc-ubuntu22.04-$(TARGET_GAP9).tar.gz --strip-components=1 -C . && \ + rm gap9-gcc-ubuntu22.04-$(TARGET_GAP9).tar.gz + +gap9-toolchain: ${GAP_RISCV_GCC_INSTALL_DIR} + +.PHONY: gap9-sdk +gap9-sdk: + @echo "Cloning and building GAP9 SDK..." + GAP9_SDK_INSTALL_DIR=${GAP9_SDK_INSTALL_DIR} \ + GAP9_SDK_COMMIT_HASH=${GAP9_SDK_COMMIT_HASH} \ + GAP_SDK_URL=${GAP_SDK_URL} \ + ROOT_DIR=${ROOT_DIR} \ + bash ${ROOT_DIR}/scripts/gap9-build_sdk.sh + ${TOOLCHAIN_DIR}/snitch_cluster: cd ${TOOLCHAIN_DIR} && \ git clone https://github.com/pulp-platform/snitch_cluster.git && \ @@ -510,18 +543,12 @@ ${QEMU_INSTALL_DIR}: ${TOOLCHAIN_DIR}/qemu qemu: ${QEMU_INSTALL_DIR} -${TOOLCHAIN_DIR}/banshee: - cd ${TOOLCHAIN_DIR} && \ - git clone https://github.com/pulp-platform/banshee.git && \ - cd ${TOOLCHAIN_DIR}/banshee && git checkout ${BANSHEE_COMMIT_HASH} && \ - git submodule update --init --recursive && \ - git apply ${TOOLCHAIN_DIR}/banshee.patch - ${BANSHEE_INSTALL_DIR}: export LLVM_SYS_150_PREFIX=${LLVM_INSTALL_DIR} && \ mkdir -p ${BANSHEE_INSTALL_DIR} && cd ${BANSHEE_INSTALL_DIR} && \ - curl -LO https://github.com/pulp-platform/banshee/releases/download/v0.5.0-prebuilt/banshee-0.5.0-$(TARGET).tar.gz && \ - tar -xzf banshee-0.5.0-$(TARGET).tar.gz --strip-components=1 -C . + curl -LO https://github.com/pulp-platform/banshee/releases/download/v0.5.0-prebuilt/banshee-0.5.0-$(TARGET_BANSHEE).tar.gz && \ + tar -xzf banshee-0.5.0-$(TARGET_BANSHEE).tar.gz --strip-components=1 -C . && \ + rm banshee-0.5.0-$(TARGET_BANSHEE).tar.gz banshee: ${BANSHEE_INSTALL_DIR} diff --git a/README_GAP9.md b/README_GAP9.md new file mode 100644 index 0000000000..d649fbb6e9 --- /dev/null +++ b/README_GAP9.md @@ -0,0 +1,90 @@ +## Using Deeploy with GAP9 + +> ⚠️ **IMPORTANT NOTE** +> This is a work in progress. The GAP9 support in Deeploy is experimental and may not be fully functional. + +To use Deeploy with GAP9, a custom Docker container is required because the official Deeploy Docker image does not yet include the necessary SDKs and dependencies for GAP9 development, because they are not publicly available. + +### Build The Docker Container + +To use SSH keys for accessing private repositories during the Docker build process, make sure you have an SSH key pair set up on your local machine. By default, the Makefile uses the key located at `~/.ssh/id_ed25519`. If your key is located elsewhere, you can specify its path using the `SSH_PRIVATE_KEY` variable when invoking the make command. + +To build a local version of the Deeploy Docker image with GAP9 support using the upstream toolchain image, run: +```sh +cd Container + +# Build the Deeploy image with the upstream toolchain image +make deeploy-gap9 DEEPLOY_GAP9_IMAGE=deeploy-gap9:latest + +# If you want to specify a custom SSH key path, use: +make deeploy-gap9 DEEPLOY_GAP9_IMAGE=deeploy-gap9:latest SSH_PRIVATE_KEY=/path/to/your/private/key +``` + +Or, to build the toolchain, Deeploy and GAP9 images locally, use: +```sh +cd Container + +# To build the Deeploy container with the local toolchain image +make deeploy TOOLCHAIN_IMAGE=deeploy-toolchain:gap9 DEEPLOY_IMAGE=deeploy:gap9 + +# To build the Deeploy GAP9 container with the local toolchain image +make deeploy-gap9 TOOLCHAIN_IMAGE=deeploy-toolchain:gap9 DEEPLOY_IMAGE=deeploy:gap9 DEEPLOY_GAP9_IMAGE=deeploy-gap9:latest +``` + +### Use The Docker Container + +Once the image is built, you can create and start the container in interactive mode with: + +```sh +docker run -it --name deeploy_gap9 -v $(pwd):/app/Deeploy deeploy-gap9:latest +``` + +Before running tests, you need to set up the GAP9 environment inside the container: +```sh +source /app/install/gap9-sdk/.gap9-venv/bin/activate +source /app/install/gap9-sdk/configs/gap9_evk_audio.sh +export GVSOC_INSTALL_DIR=/app/install/gap9-sdk/install/workstation +``` +Install Deeploy inside the container in editable mode: + +```sh +cd /app/Deeploy +pip install -e . +``` + +```sh +cd /app/Deeploy/DeeployTest +python deeployRunner_gap9.py -t ./Tests/Kernels/FP32/MatMul +python deeployRunner_tiled_gap9.py -t ./Tests/Kernels/FP32/MatMul +``` + +### Use A Real GAP9 Board (USB/IP via gap9-run.sh) + +For board access, use the orchestration script in [scripts/gap9-run.sh](scripts/gap9-run.sh). It manages: +- host-side USB/IP server (pyusbip) +- usbip device manager container +- GAP9 SDK container with the correct mounts + +#### Prerequisites +- Docker installed and running +- A working SSH key for BuildKit (if you are building the image locally) +- USB/IP host support (the script can set up pyusbip on the host) + + +#### Start the board workflow (recommended) +This launches a tmux session with three panes: one for the host USB/IP server, one for the GAP9 container and one terminal on the host to manage the USB/IP devices. + +```sh +./scripts/gap9-run.sh start-tmux +``` + +#### Start manually (two terminals) +Terminal 1 (host USB/IP server): +```sh +./scripts/gap9-run.sh start-usbip-host +``` + +Terminal 2 (containers + attach device): +```sh +./scripts/gap9-run.sh start +``` \ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index 6ca3d33c6f..88b86d601c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -70,6 +70,7 @@ ["main", "https://pulp-platform.github.io/Deeploy"], ["devel", "https://pulp-platform.github.io/Deeploy/branch/devel"], ["v0.2.0", "https://pulp-platform.github.io/Deeploy/tag/v0.2.0"], + ["v0.2.1", "https://pulp-platform.github.io/Deeploy/tag/v0.2.1"], ], } diff --git a/scripts/gap9-build_sdk.sh b/scripts/gap9-build_sdk.sh new file mode 100755 index 0000000000..21c832927e --- /dev/null +++ b/scripts/gap9-build_sdk.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +# gap9-build_sdk.sh +# Helper script to clone, patch and build the GAP9 SDK. Intended to be +# invoked from the Makefile with environment variables set: +# GAP9_SDK_INSTALL_DIR (required) +# GAP9_SDK_COMMIT_HASH (optional, fallback provided) +# ROOT_DIR (optional, defaults to script dir) + +ROOT_DIR="${ROOT_DIR:-$(cd "$(dirname "$0")" && pwd)}" +GAP9_SDK_INSTALL_DIR="${GAP9_SDK_INSTALL_DIR:?GAP9_SDK_INSTALL_DIR must be set}" +GAP9_SDK_COMMIT_HASH="${GAP9_SDK_COMMIT_HASH:-897955d7ab326bd31684429eb16a2e485ab89afb}" +GAP_SDK_URL="${GAP_SDK_URL:-git@iis-git.ee.ethz.ch:wiesep/gap9_sdk.git}" + +echo "Preparing GAP9 SDK in: ${GAP9_SDK_INSTALL_DIR}" + +if [ -d "${GAP9_SDK_INSTALL_DIR}/.git" ]; then + echo "Directory ${GAP9_SDK_INSTALL_DIR} already exists and looks like a git repo. Updating remote URL and fetching latest changes..." + cd "${GAP9_SDK_INSTALL_DIR}" || exit 1 + git remote set-url origin "${GAP_SDK_URL}" || true +else + echo "Cloning GAP9 SDK..." + git clone "${GAP_SDK_URL}" "${GAP9_SDK_INSTALL_DIR}" +fi + +cd "${GAP9_SDK_INSTALL_DIR}" || exit 1 +echo "Checking out commit ${GAP9_SDK_COMMIT_HASH} (stash and fetch if necessary)" +git fetch --all --tags || true +git stash || true +git checkout "${GAP9_SDK_COMMIT_HASH}" +git submodule update --init --recursive + +# Platform specific patch +ARCH=$(dpkg --print-architecture 2>/dev/null || true) +if [ -z "$ARCH" ]; then + ARCH=$(uname -m) +fi +case "$ARCH" in +amd64 | x86_64) PATCH=gap9-amd64.patch ;; +arm64 | aarch64) PATCH=gap9-arm64.patch ;; +*) PATCH= ;; +esac + +set -e # Enable strict error handling for the build process +if [ -n "$PATCH" ] && [ -f "${ROOT_DIR}/${PATCH}" ]; then + echo "Applying platform patch: $PATCH" + git apply "${ROOT_DIR}/${PATCH}" +else + echo "No platform-specific patch to apply for architecture '$ARCH' (looked for ${ROOT_DIR}/${PATCH})" +fi +set +e # Disable strict error handling to allow deactivation even if build fails + +echo "Setting up Python virtual environment and installing dependencies" +python -m venv .gap9-venv +. .gap9-venv/bin/activate +pip install "numpy<2.0.0" +echo "Sourcing GAP9 SDK environment" +. configs/gap9_evk_audio.sh || true + +echo "Invoking make install_dependency cmake_sdk.build" +set -e # Enable strict error handling for the build process +make install_dependency cmake_sdk.build openocd.all autotiler.all +set +e # Disable strict error handling to allow deactivation even if build fails + +deactivate + +echo "GAP9 SDK ready at: ${GAP9_SDK_INSTALL_DIR}" + +exit 0 diff --git a/scripts/gap9-run.sh b/scripts/gap9-run.sh new file mode 100755 index 0000000000..fa01424df1 --- /dev/null +++ b/scripts/gap9-run.sh @@ -0,0 +1,540 @@ +#!/bin/bash + +# SPDX-FileCopyrightText: 2026 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +# gap9-run.sh - Docker orchestration script for GAP9 SDK with USB/IP device passthrough +# +# +# This script manages the containerized GAP9 development environment, including: +# - Building the GAP9 Docker image +# - Running the usbip device manager container (for USB device passthrough) +# - Starting the GAP9 SDK container with mounted volumes +# +# Prerequisites: +# - Docker installed and running +# - SSH key configured for BuildKit (if building image) +# - pyusbip server running on host (for USB passthrough) +# + +set -euo pipefail + +######################################################################### +# Configuration & Defaults +######################################################################### + +# Image and container names +GAP9_IMAGE="ghcr.io/pulp-platform/deeploy-gap9" +USBIP_IMAGE="jonathanberi/devmgr" + +DOCKER_PLATFORM="auto" +DOCKER_SHELL="/bin/zsh" + +# USB/IP device settings +USBIP_HOST="host.docker.internal" +USBIP_VENDOR="15ba" +USBIP_PRODUCT="002b" + +# SDK and cache directories +WORK_DIR="." +CACHE_FOLDER=".cache" + +# SSH key gap9 container +SSH_PRIVATE_KEY="~/.ssh/id_ed25519" + +# pyusbip configuration +PYUSBIP_REPO="https://github.com/tumayt/pyusbip" +PYUSBIP_DIR=".pyusbip" + +######################################################################### +# Utility Functions +######################################################################### + +# Print colored output +log_info() { + echo -e "\033[0;36m[INFO ]\033[0m $*" +} + +log_error() { + echo -e "\033[0;31m[ERROR ]\033[0m $*" >&2 +} + +log_warn() { + echo -e "\033[0;33m[WARN ]\033[0m $*" >&2 +} + +log_success() { + echo -e "\033[0;32m[SUCCESS]\033[0m $*" +} + +# Display help message +show_help() { + cat < [options] + +GAP9 Docker Orchestration Script + +Commands: + start Start usbip daemon and GAP9 container + start-tmux Start everything in a tmux session with split panes + stop Stop containers + start-usbip-host Setup and run host-side USB/IP server (in separate terminal) + start-gap9 Start only the GAP9 container + start-usbip-daemon Start only the usbip device manager container + attach-usbip Attach USB device to usbip daemon + detach-usbip Detach USB device from usbip daemon + setup-usbip-host One-time setup for host-side USB/IP server + help Display this help message + +Options: + -i, --image NAME Docker image name (default: $GAP9_IMAGE) + -d, --work-dir PATH Path to working directory (default: $WORK_DIR) + -c, --cache-dir PATH Cache directory (default: $CACHE_FOLDER) + -k, --ssh-key PATH SSH private key (default: $SSH_PRIVATE_KEY) + -h, --host ADDR usbip host address (default: $USBIP_HOST) + -v, --vendor ID USB vendor ID (default: $USBIP_VENDOR) + -p, --product ID USB product ID (default: $USBIP_PRODUCT) + --platform PLATFORM Docker platform (default: $DOCKER_PLATFORM) + --shell SHELL Shell to use in container (default: $DOCKER_SHELL) + +Examples: + # Start everything in tmux (recommended) + $0 start-tmux + + # Start containers with USB device passthrough (manual terminals) + $0 start-usbip-host # In terminal 1 + $0 start # In terminal 2 + + # Custom working directory + $0 -d /path/to/workdir start + + # Stop everything + $0 stop + +EOF +} + +# Parse command-line arguments (collect options first, then run command) +command="" +opts=() +args=("$@") +idx=0 +while [[ $idx -lt ${#args[@]} ]]; do + case "${args[$idx]}" in + -i | --image) + GAP9_IMAGE="${args[$((idx + 1))]}" + opts+=("${args[$idx]}" "${args[$((idx + 1))]}") + idx=$((idx + 2)) + ;; + -d | --work-dir) + WORK_DIR="${args[$((idx + 1))]}" + opts+=("${args[$idx]}" "${args[$((idx + 1))]}") + idx=$((idx + 2)) + ;; + -c | --cache-dir) + CACHE_FOLDER="${args[$((idx + 1))]}" + opts+=("${args[$idx]}" "${args[$((idx + 1))]}") + idx=$((idx + 2)) + ;; + -k | --ssh-key) + SSH_PRIVATE_KEY="${args[$((idx + 1))]}" + opts+=("${args[$idx]}" "${args[$((idx + 1))]}") + idx=$((idx + 2)) + ;; + -h | --host) + USBIP_HOST="${args[$((idx + 1))]}" + opts+=("${args[$idx]}" "${args[$((idx + 1))]}") + idx=$((idx + 2)) + ;; + -v | --vendor) + USBIP_VENDOR="${args[$((idx + 1))]}" + opts+=("${args[$idx]}" "${args[$((idx + 1))]}") + idx=$((idx + 2)) + ;; + -p | --product) + USBIP_PRODUCT="${args[$((idx + 1))]}" + opts+=("${args[$idx]}" "${args[$((idx + 1))]}") + idx=$((idx + 2)) + ;; + --platform) + DOCKER_PLATFORM="${args[$((idx + 1))]}" + opts+=("${args[$idx]}" "${args[$((idx + 1))]}") + idx=$((idx + 2)) + ;; + --shell) + DOCKER_SHELL="${args[$((idx + 1))]}" + opts+=("${args[$idx]}" "${args[$((idx + 1))]}") + idx=$((idx + 2)) + ;; + help | --help) + show_help + exit 0 + ;; + start | start-tmux | stop | start-gap9 | start-usbip-daemon | attach-usbip | detach-usbip | stop-usbip-daemon | start-usbip-host | setup-usbip-host) + if [[ -n "$command" ]]; then + log_error "Multiple commands provided: $command and ${args[$idx]}" + show_help + exit 1 + fi + command="${args[$idx]}" + idx=$((idx + 1)) + ;; + *) + log_error "Unknown option or command: ${args[$idx]}" + show_help + exit 1 + ;; + esac +done + +# Expand path of SSH private key +SSH_PRIVATE_KEY="$(eval echo "$SSH_PRIVATE_KEY")" + +## Print configuration +log_info "Configuration:" +log_info " GAP9 Docker Image: $GAP9_IMAGE" +log_info " Working Directory: $WORK_DIR" +log_info " Cache Directory: $CACHE_FOLDER" +log_info " SSH Private Key: $SSH_PRIVATE_KEY" +log_info " USB/IP Host: $USBIP_HOST" +log_info " USB Vendor ID: $USBIP_VENDOR" +log_info " USB Product ID: $USBIP_PRODUCT" +log_info " Docker Platform: $DOCKER_PLATFORM" +log_info " Docker Shell: $DOCKER_SHELL" + +######################################################################### +# usbip Host Setup Functions +######################################################################### + +cmd_setup_usbip_host() { + log_info "Setting up host-side USB/IP server..." + + # Clone pyusbip if not present + if [ ! -d "$PYUSBIP_DIR" ]; then + log_info "Cloning pyusbip into $PYUSBIP_DIR..." + git clone "$PYUSBIP_REPO" "$PYUSBIP_DIR" + else + log_info "pyusbip directory $PYUSBIP_DIR already exists, skipping clone" + fi + + # Create virtual environment if needed + if [ ! -d "$PYUSBIP_DIR/.venv" ]; then + log_info "Creating Python virtual environment..." + python3 -m venv "$PYUSBIP_DIR/.venv" + + log_info "Installing pyusbip dependencies..." + . "$PYUSBIP_DIR/.venv/bin/activate" + pip install --upgrade pip + pip install libusb1 + else + log_info "Virtual environment already exists, skipping setup" + fi + + log_success "Host-side USB/IP setup complete" +} + +cmd_start_usbip_host() { + cmd_setup_usbip_host + + log_info "Starting host-side USB/IP server (pyusbip)..." + log_info "This process will run in the foreground. Press Ctrl+C to stop." + + cd "$PYUSBIP_DIR" && + . .venv/bin/activate && + python pyusbip.py +} + +######################################################################### +# usbip Daemon Functions +######################################################################### + +# Wait for usbip server to be ready +wait_for_usbip_server() { + local max_retries=20 + local retry_count=0 + local retry_interval=1 + + log_info "Waiting for pyusbip server to be ready..." + + while [ $retry_count -lt $max_retries ]; do + if pgrep -f "python.*pyusbip\.py" >/dev/null; then + log_success "pyusbip server is ready" + return 0 + fi + + retry_count=$((retry_count + 1)) + log_info " Attempt $retry_count/$max_retries: pyusbip not ready yet, retrying in ${retry_interval}s..." + sleep "$retry_interval" + done + + log_error "Timeout waiting for pyusbip server to start (${max_retries}s)" + return 1 +} + +cmd_start_usbip_daemon() { + # Wait for pyusbip server to be ready + if ! wait_for_usbip_server; then + log_error "pyusbip server did not start in time" + log_error "Please run '$0 start-usbip-host' in a separate terminal first" + exit 1 + fi + + # Check if container already running + if [ -n "$(docker ps -q -f name=usbip-devmgr)" ]; then + log_info "usbip-devmgr container already running" + return 0 + fi + + log_info "Starting usbip-devmgr container..." + docker run -d --rm \ + --privileged \ + --pid=host \ + --name usbip-devmgr \ + -e USBIP_HOST="$USBIP_HOST" \ + -e USBIP_VENDOR="$USBIP_VENDOR" \ + -e USBIP_PRODUCT="$USBIP_PRODUCT" \ + "$USBIP_IMAGE" \ + /bin/sh -lc 'nsenter -t1 -m sh -lc "tail -f /dev/null"' + + log_success "usbip-devmgr container started" +} + +cmd_attach_usbip() { + # First detach any existing attachment + cmd_detach_usbip || true + + log_info "Attaching USB device to usbip-devmgr..." + docker exec -it usbip-devmgr /bin/sh -lc 'nsenter -t1 -m sh -lc " + usbip list -r \"$USBIP_HOST\" || { echo \"usbip list failed\"; exit 1; } + BUSID=\$(usbip list -r \"$USBIP_HOST\" \ + | grep \"$USBIP_VENDOR:$USBIP_PRODUCT\" \ + | head -n1 \ + | cut -d\":\" -f1 \ + | xargs) + if [ -z \"\$BUSID\" ]; then + exit 1 + fi + usbip attach -r \"$USBIP_HOST\" -b \"\$BUSID\" + "' + + if [ $? -ne 0 ]; then + log_error "Failed to attach USB device" + else + log_success "USB device attached successfully" + fi +} + +cmd_detach_usbip() { + log_info "Detaching USB device..." + docker run --rm \ + --privileged \ + --pid=host \ + -e USBIP_HOST="$USBIP_HOST" \ + -e USBIP_VENDOR="$USBIP_VENDOR" \ + -e USBIP_PRODUCT="$USBIP_PRODUCT" \ + "$USBIP_IMAGE" \ + /bin/sh -lc 'nsenter -t1 -m sh -lc " + PORT=\$(usbip port \ + | grep \"$USBIP_VENDOR:$USBIP_PRODUCT\" -B 1 \ + | head -n1 \ + | sed -E \"s/^Port ([0-9]+):.*/\1/\" \ + | xargs) + if [ -z \"\$PORT\" ]; then + exit 1 + fi + usbip detach -p \"\$PORT\" + "' >/dev/null 2>&1 + + if [ $? -ne 0 ]; then + log_warn "Failed to detach USB device (it may not have been attached)" + else + log_success "USB device detached (or not attached)" + fi +} + +cmd_stop_usbip_daemon() { + log_info "Stopping usbip-devmgr container..." + + # First detach the device + cmd_detach_usbip || true + + # Stop the container + if [ -n "$(docker ps -q -f name=usbip-devmgr)" ]; then + docker stop usbip-devmgr >/dev/null 2>&1 + log_success "usbip-devmgr container stopped" + else + log_info "usbip-devmgr container not running" + fi +} + +######################################################################### +# GAP9 Container Functions +######################################################################### + +cmd_start_gap9() { + log_info "Starting GAP9 container..." + + # Prepare cache directory + mkdir -p "$CACHE_FOLDER" + touch "$CACHE_FOLDER/.zsh_history" + + # Validate WORK_DIR exists + if [ ! -d "$WORK_DIR" ]; then + log_error "WORK_DIR not found: $WORK_DIR" + log_error "Use -d/--work-dir to set the SDK path" + exit 1 + fi + + log_info "Press Ctrl+D or type 'exit' to exit container" + + # Build docker run command with optional platform argument + local docker_run_args=( + -it --rm + --privileged + -v /dev/bus/usb:/dev/bus/usb + -v "$SSH_PRIVATE_KEY":/root/.ssh/id_ed25519:ro + -v "$WORK_DIR/":/app/work/ + -v "$CACHE_FOLDER/.zsh_history":/root/.zsh_history + -v "$CACHE_FOLDER/ccache":/ccache + -e CCACHE_DIR=/ccache + ) + + # Add platform argument if not 'auto' + if [[ "$DOCKER_PLATFORM" != "auto" ]]; then + docker_run_args+=(--platform "$DOCKER_PLATFORM") + fi + + docker_run_args+=("$GAP9_IMAGE" "$DOCKER_SHELL" -c "cd /app/work && $DOCKER_SHELL") + + docker run "${docker_run_args[@]}" +} + +######################################################################### +# Orchestration Functions +######################################################################### + +cmd_start() { + log_info "Starting GAP9 orchestration (usbip daemon + GAP9 container)..." + cmd_start_usbip_daemon + cmd_attach_usbip + cmd_start_gap9 +} + +cmd_stop() { + log_info "Stopping all containers..." + cmd_stop_usbip_daemon + cmd_stop_tmux + log_success "All containers stopped" +} + +######################################################################### +# Tmux Orchestration +######################################################################### + +cmd_start_tmux() { + local session_name="gap9-dev" + local script_path="$0" + local opts_escaped="" + + for opt in ${opts[@]+"${opts[@]}"}; do + if [[ -n "$opt" ]]; then + printf -v opt '%q' "$opt" + opts_escaped+=" $opt" + fi + done + + # Check if tmux is installed + if ! command -v tmux &>/dev/null; then + log_error "tmux is not installed. Please install tmux first." + log_error "On macOS: brew install tmux" + log_error "On Linux: sudo apt-get install tmux" + exit 1 + fi + + # Kill any existing session with the same name + tmux kill-session -t "$session_name" 2>/dev/null || true + + log_info "Creating tmux session: $session_name" + + # Create new session with three panes (usbip-host, usbip-daemon, gap9) + tmux new-session -d -s "$session_name" -x 200 -y 50 + + # First pane: run pyusbip server + + # Second pane: run main orchestration (with delay to let server start) + tmux split-window -t "$session_name:0" -h + tmux split-window -t "$session_name:0" -v + tmux send-keys -t "$session_name:0.1" "alias stop='$script_path$opts_escaped stop'" Enter + tmux send-keys -t "$session_name:0.1" "$script_path$opts_escaped start-usbip-host" Enter + tmux send-keys -t "$session_name:0.2" "alias stop='$script_path$opts_escaped stop'" Enter + tmux send-keys -t "$session_name:0.2" "$script_path$opts_escaped start-usbip-daemon" Enter + tmux send-keys -t "$session_name:0.2" "$script_path$opts_escaped attach-usbip" Enter + tmux send-keys -t "$session_name:0.0" "alias stop='$script_path$opts_escaped stop'" Enter + tmux send-keys -t "$session_name:0.0" "$script_path$opts_escaped start-gap9" Enter + + # Select the first pane + tmux select-pane -t "$session_name:0.0" + + log_success "tmux session created: $session_name" + log_info "Attaching to session..." + log_info "To detach: Ctrl+B then D" + log_info "To kill session: tmux kill-session -t $session_name" + + # Attach to the session + tmux attach-session -t "$session_name" +} + +cmd_stop_tmux() { + local session_name="gap9-dev" + log_info "Stopping tmux session: $session_name" + tmux kill-session -t "$session_name" 2>/dev/null || log_info "tmux session $session_name not running" +} + +######################################################################### +# Main Script Logic +######################################################################### + +if [[ -z "$command" ]]; then + cmd_start_tmux + exit 0 +fi + +# Execute the command after options are parsed +case "$command" in +start) + cmd_start + ;; +start-tmux) + cmd_start_tmux + ;; +stop) + cmd_stop + ;; +start-gap9) + cmd_start_gap9 + ;; +start-usbip-daemon) + cmd_start_usbip_daemon + ;; +attach-usbip) + cmd_attach_usbip + ;; +detach-usbip) + cmd_detach_usbip + ;; +stop-usbip-daemon) + cmd_stop_usbip_daemon + ;; +start-usbip-host) + cmd_start_usbip_host + ;; +setup-usbip-host) + cmd_setup_usbip_host + ;; +*) + log_error "Unknown command: $command" + show_help + exit 1 + ;; +esac