Skip to content

am243x: add PRU-EQEP example #74

am243x: add PRU-EQEP example

am243x: add PRU-EQEP example #74

Workflow file for this run

# SPDX-License-Identifier: BSD-3-Clause
# Copyright (C) 2026 Texas Instruments Incorporated - http://www.ti.com/
#
# CCS Build CI — imports and builds all CCS projectspecs for each device.
# Complements the make-based CI (makefile.yml) by verifying that projectspecs
# can be imported, product path variables resolve correctly, postBuildStep
# scripts (hexpru header generation) run successfully, and R5F projects can
# find the generated firmware headers.
#
# Triggered only on files that affect CCS import or project build
# configuration; source-only changes are already covered by makefile.yml.
name: CCS Build
on:
pull_request:
paths:
- '**/example.projectspec'
- '.metadata/**'
- '**/linker.cmd'
- '**/example.syscfg'
- '**/makefile_ccs_bootimage_gen'
push:
branches: [ main, master ]
paths:
- '**/example.projectspec'
- '.metadata/**'
- '**/linker.cmd'
- '**/example.syscfg'
- '**/makefile_ccs_bootimage_gen'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
jobs:
install-toolchain:
# Installs CCS and shared compiler tools once and caches them.
# MCU+ SDKs are intentionally omitted — each ccs-build job installs
# only the SDK it needs, keeping the shared cache small.
name: Install shared toolchain
runs-on: ubuntu-latest
steps:
- name: Install OS dependencies
run: |
sudo tee -a /etc/apt/sources.list >/dev/null <<'EOF'
deb http://security.ubuntu.com/ubuntu focal-security main universe
EOF
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libx11-6 libxext6 libxrender1 libxtst6 libxi6 libxt6 libxinerama1 \
libgtk2.0-0 libncurses5 libnss3 python3-pip \
unzip xz-utils wget ca-certificates xvfb
pip3 install pyserial xmodem tqdm pyelftools construct
- name: Restore toolchain cache
id: cache
uses: actions/cache/restore@v4
with:
path: |
~/ti/ccs_20.3.1
~/ti/ti-cgt-pru_2.3.3
~/ti/ti-cgt-armllvm_4.0.3.LTS
~/ti/sysconfig_1.27.0
key: toolchain-ccs-${{ runner.os }}-v5-ccs20_3_1-sysconfig1_27_0-pru2_3_3-armclang4_0_3
- name: Install CCS, SYSCONFIG, TIARMCLANG and PRU CGT if missing
if: success() && steps.cache.outputs.cache-hit != 'true'
run: |
set -e
mkdir -p "$HOME/ti/downloads"
cd "$HOME/ti"
# Install CCS 20.3.1
if [ ! -d "$HOME/ti/ccs_20.3.1" ]; then
cd "$HOME/ti/downloads"
wget -q --retry-connrefused --waitretry=1 --tries=5 --timeout=30 \
https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-J1VdearkvK/20.3.1/CCS_20.3.1.00005_linux.zip
cd "$HOME/ti"
unzip -t "$HOME/ti/downloads/CCS_20.3.1.00005_linux.zip" > /dev/null
unzip -q "$HOME/ti/downloads/CCS_20.3.1.00005_linux.zip"
cd CCS_20.3.1.00005_linux
./ccs_setup_20.3.1.00005.run --mode unattended --enable-components PF_ARM_MPU,PF_SITARA_MCU --prefix "$HOME/ti/ccs_20.3.1"
fi
# Install SYSCONFIG 1.27.0
if [ ! -d "$HOME/ti/ccs_20.3.1/ccs/utils/sysconfig_1.27.0" ] && [ ! -d "$HOME/ti/sysconfig_1.27.0" ]; then
cd "$HOME/ti/downloads"
wget -q --retry-connrefused --waitretry=1 --tries=5 --timeout=30 \
https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-nsUM6f7Vvb/1.27.0.4565/sysconfig-1.27.0_4565-setup.run
chmod +x sysconfig-1.27.0_4565-setup.run
./sysconfig-1.27.0_4565-setup.run --mode unattended --prefix "$HOME/ti/sysconfig_1.27.0"
chmod -R +x "$HOME/ti/sysconfig_1.27.0"
fi
# Install ARM CLANG CGT 4.0.3
if [ ! -d "$HOME/ti/ccs_20.3.1/ccs/tools/compiler/ti-cgt-armllvm_4.0.3.LTS" ] && [ ! -d "$HOME/ti/ti-cgt-armllvm_4.0.3.LTS" ]; then
cd "$HOME/ti/downloads"
wget -q --retry-connrefused --waitretry=1 --tries=5 --timeout=30 \
https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-ayxs93eZNN/4.0.3.LTS/ti_cgt_armllvm_4.0.3.LTS_linux-x64_installer.bin
chmod +x ti_cgt_armllvm_4.0.3.LTS_linux-x64_installer.bin
./ti_cgt_armllvm_4.0.3.LTS_linux-x64_installer.bin --mode unattended --prefix "$HOME/ti/"
fi
# Install PRU CGT 2.3.3
if [ ! -d "$HOME/ti/ccs_20.3.1/ccs/tools/compiler/ti-cgt-pru_2.3.3" ] && [ ! -d "$HOME/ti/ti-cgt-pru_2.3.3" ]; then
cd "$HOME/ti/downloads"
wget -q --retry-connrefused --waitretry=1 --tries=5 --timeout=30 \
https://dr-download.ti.com/software-development/ide-configuration-compiler-or-debugger/MD-FaNNGkDH7s/2.3.3/ti_cgt_pru_2.3.3_linux_installer_x86.bin
chmod +x ti_cgt_pru_2.3.3_linux_installer_x86.bin
./ti_cgt_pru_2.3.3_linux_installer_x86.bin --mode unattended --prefix "$HOME/ti"
fi
test -x "$HOME/ti/ti-cgt-armllvm_4.0.3.LTS/bin/tiarmclang" && \
"$HOME/ti/ti-cgt-armllvm_4.0.3.LTS/bin/tiarmclang" --version || \
ls -l "$HOME/ti/ti-cgt-armllvm_4.0.3.LTS"
# Remove large temporaries — do NOT cache installer files or extracted zip
rm -rf "$HOME/ti/downloads"
rm -rf "$HOME/ti/CCS_20.3.1.00005_linux"
# Strip bundled compilers for architectures not used by OpenPRU.
# rm -rf is silent if directory is absent (e.g. already excluded by
# --enable-components), so this is safe regardless of install flags.
rm -rf "$HOME/ti/ccs_20.3.1"/ccs/tools/compiler/ti-cgt-c2000_*
rm -rf "$HOME/ti/ccs_20.3.1"/ccs/tools/compiler/ti-cgt-c29_*
rm -rf "$HOME/ti/ccs_20.3.1"/ccs/tools/compiler/ti-cgt-c6000_*
rm -rf "$HOME/ti/ccs_20.3.1"/ccs/tools/compiler/ti-cgt-c7000_*
rm -rf "$HOME/ti/ccs_20.3.1"/ccs/tools/compiler/ti-cgt-msp430_*
# Strip Theia web IDE — not used by ccs-server-cli.sh headless mode
rm -rf "$HOME/ti/ccs_20.3.1/ccs/theia"
# Strip cloud agent and uninstallers — not needed in CI
rm -rf "$HOME/ti/ccs_20.3.1/ccs/ccs_base/cloudagent"
rm -rf "$HOME/ti/ccs_20.3.1/ccs/uninstallers"
echo "=== Toolchain sizes after cleanup ==="
du -sh "$HOME/ti/ccs_20.3.1" "$HOME/ti/ti-cgt-pru_2.3.3" \
"$HOME/ti/ti-cgt-armllvm_4.0.3.LTS" "$HOME/ti/sysconfig_1.27.0" 2>/dev/null || true
- name: Save toolchain cache
if: success() && steps.cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
path: |
~/ti/ccs_20.3.1
~/ti/ti-cgt-pru_2.3.3
~/ti/ti-cgt-armllvm_4.0.3.LTS
~/ti/sysconfig_1.27.0
key: toolchain-ccs-${{ runner.os }}-v5-ccs20_3_1-sysconfig1_27_0-pru2_3_3-armclang4_0_3
ccs-build:
name: CCS Build (${{ matrix.device }})
needs: [install-toolchain]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- device: am243x
mcu_plus_sdk: mcu_plus_sdk_am243x_12_00_00_26
sdk_installer: mcu_plus_sdk_am243x_12_00_00_26-linux-x64-installer.run
sdk_url: https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-ouHbHEm1PK/12.00.00.26/mcu_plus_sdk_am243x_12_00_00_26-linux-x64-installer.run
- device: am261x
mcu_plus_sdk: mcu_plus_sdk_am261x_26_00_00_06
sdk_installer: mcu_plus_sdk_am261x_26_00_00_06-linux-x64-installer.run
sdk_url: https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-h64OlWnQrg/26.00.00.06.STS/mcu_plus_sdk_am261x_26_00_00_06-linux-x64-installer.run
- device: am263px
mcu_plus_sdk: mcu_plus_sdk_am263px_26_00_00_06
sdk_installer: mcu_plus_sdk_am263px_26_00_00_06-linux-x64-installer.run
sdk_url: https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-JVtW1V4WfA/26.00.00.06.STS/mcu_plus_sdk_am263px_26_00_00_06-linux-x64-installer.run
- device: am263x
mcu_plus_sdk: mcu_plus_sdk_am263x_26_00_00_06
sdk_installer: mcu_plus_sdk_am263x_26_00_00_06-linux-x64-installer.run
sdk_url: https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-r5FY9rRaGv/26.00.00.06.STS/mcu_plus_sdk_am263x_26_00_00_06-linux-x64-installer.run
- device: am64x
mcu_plus_sdk: mcu_plus_sdk_am64x_12_00_00_27
sdk_installer: mcu_plus_sdk_am64x_12_00_00_27-linux-x64-installer.run
sdk_url: https://dr-download.ti.com/software-development/software-development-kit-sdk/MD-SfkcjYAjGS/12.00.00.27/mcu_plus_sdk_am64x_12_00_00_27-linux-x64-installer.run
- device: am62x
mcu_plus_sdk: ""
sdk_installer: ""
sdk_url: ""
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Install OS dependencies
run: |
sudo tee -a /etc/apt/sources.list >/dev/null <<'EOF'
deb http://security.ubuntu.com/ubuntu focal-security main universe
EOF
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libx11-6 libxext6 libxrender1 libxtst6 libxi6 libxt6 libxinerama1 \
libgtk2.0-0 libncurses5 libnss3 python3-pip \
unzip xz-utils wget ca-certificates xvfb
pip3 install pyserial xmodem tqdm pyelftools construct
- name: Restore toolchain cache
uses: actions/cache/restore@v4
with:
path: |
~/ti/ccs_20.3.1
~/ti/ti-cgt-pru_2.3.3
~/ti/ti-cgt-armllvm_4.0.3.LTS
~/ti/sysconfig_1.27.0
key: toolchain-ccs-${{ runner.os }}-v5-ccs20_3_1-sysconfig1_27_0-pru2_3_3-armclang4_0_3
fail-on-cache-miss: true
- name: Install device MCU+ SDK
if: matrix.mcu_plus_sdk != ''
run: |
set -e
mkdir -p "$HOME/ti/downloads"
cd "$HOME/ti/downloads"
wget -q --retry-connrefused --waitretry=1 --tries=5 --timeout=30 \
"${{ matrix.sdk_url }}"
chmod +x "${{ matrix.sdk_installer }}"
"./${{ matrix.sdk_installer }}" --mode unattended --prefix "$HOME/ti/"
- name: Generate imports.mak for CI
# imports.mak is gitignored (developer-configured). CCS R5F post-build steps
# invoke makefile_ccs_bootimage_gen, which unconditionally includes
# $(OPEN_PRU_PATH)/imports.mak before any ifeq guard. Without this file the
# include fails and CCS counts the project as having errors.
# override CCS_IDE_MODE := cloud ensures the ifeq($(CCS_IDE_MODE),cloud)
# guard in makefile_ccs_bootimage_gen skips boot-image generation even when
# CCS headless passes CCS_IDE_MODE= (empty) on the command line.
# Boot image generation is not needed for compilation CI.
run: |
cat > imports.mak << 'EOF'
# Auto-generated by ccs_build.yml — do not edit.
# Provides Linux utility variables for makefile_ccs_bootimage_gen.
# override beats command-line CCS_IDE_MODE= so post-build is skipped.
override CCS_IDE_MODE := cloud
DEVICE_TYPE ?= GP
ENC_ENABLED ?= no
export MKDIR := mkdir -p
export RMDIR := rm -rf
export RM := rm -f
export COPY := cp
export MOVE := mv
export CAT := cat
export SED := sed
export TOUCH := touch
export PATHSEP := /
export CHMOD := chmod
CCS_NODE = $(CCS_PATH)/tools/node/node
EOF
- name: Patch metadata for SysConfig version
# Temporarily encodes the SysConfig version into OpenPRU's package.tirex.json
# and product.json so CCS selects the correct SysConfig version when
# initializing the workspace. See test_ccs_import.sh for background.
# No backup/restore needed — CI workspace is ephemeral.
run: |
SYSCFG_VERSION="1.27.0"
OPENPRU_VERSION="1.${SYSCFG_VERSION}"
sed -i "s/^ \"version\": \"[^\"]*\"/ \"version\": \"${OPENPRU_VERSION}\"/" \
.metadata/.tirex/package.tirex.json
sed -i "/\"packageId\": \"sysconfig\"/s/\"version\": \"[^\"]*\"/\"version\": \"${SYSCFG_VERSION}\"/" \
.metadata/.tirex/package.tirex.json
sed -i "s/\"minToolVersion\" : \"[^\"]*\"/\"minToolVersion\" : \"${SYSCFG_VERSION}\"/" \
.metadata/product.json
- name: Start Xvfb
run: |
Xvfb :99 -screen 0 1024x768x24 &
echo "DISPLAY=:99" >> $GITHUB_ENV
- name: Initialize CCS workspace
run: |
set -eo pipefail
DEVICE="${{ matrix.device }}"
WORKSPACE="${HOME}/workspace_ccs_${DEVICE}"
CCS_CLI="$HOME/ti/ccs_20.3.1/ccs/eclipse/ccs-server-cli.sh"
PRODUCT_PATHS="${GITHUB_WORKSPACE};$HOME/ti/sysconfig_1.27.0"
if [ -d "$HOME/ti/ccs_20.3.1/ccs/utils/sysconfig_1.27.0" ]; then
PRODUCT_PATHS="${GITHUB_WORKSPACE};$HOME/ti/ccs_20.3.1/ccs/utils/sysconfig_1.27.0"
fi
if [ -n "${{ matrix.mcu_plus_sdk }}" ]; then
PRODUCT_PATHS="${PRODUCT_PATHS};$HOME/ti/${{ matrix.mcu_plus_sdk }}"
fi
# PRU CGT: prefer the copy bundled inside CCS, fall back to standalone
PRU_CGT_PATH="$HOME/ti/ccs_20.3.1/ccs/tools/compiler/ti-cgt-pru_2.3.3"
[ -d "${PRU_CGT_PATH}" ] || PRU_CGT_PATH="$HOME/ti/ti-cgt-pru_2.3.3"
mkdir -p "${WORKSPACE}"
"${CCS_CLI}" -noSplash \
-workspace "${WORKSPACE}" \
-application com.ti.ccs.apps.initialize \
-ccs.productDiscoveryPath "${PRODUCT_PATHS}" \
-ccs.toolDiscoveryPath "${PRU_CGT_PATH}" \
2>&1 | tee workspace_init.log
INIT_EXIT="${PIPESTATUS[0]}"
if [ "${INIT_EXIT}" -ne 0 ]; then
echo "ERROR: CCS workspace initialization failed (exit=${INIT_EXIT})"
exit 1
fi
- name: Import all projectspecs
run: |
DEVICE="${{ matrix.device }}"
WORKSPACE="${HOME}/workspace_ccs_${DEVICE}"
CCS_CLI="$HOME/ti/ccs_20.3.1/ccs/eclipse/ccs-server-cli.sh"
mapfile -t SPECS < <(find . -name "example.projectspec" | grep "/${DEVICE}-" | sort)
echo "Found ${#SPECS[@]} projectspecs for ${DEVICE}"
CMD=("${CCS_CLI}" -noSplash
-workspace "${WORKSPACE}"
-application com.ti.ccs.apps.importProject)
for spec in "${SPECS[@]}"; do CMD+=(-ccs.location "${spec}"); done
"${CMD[@]}" 2>&1 | tee import.log
IMPORT_EXIT="${PIPESTATUS[0]}"
# CCS may exit 0 even when individual imports fail; check log as well.
ERRORS=$(grep -c '!ERROR:' import.log || true)
if [ "${IMPORT_EXIT}" -ne 0 ] || [ "${ERRORS}" -gt 0 ]; then
echo "ERROR: CCS import failed (exit=${IMPORT_EXIT}, errors=${ERRORS})"
exit 1
fi
- name: Build pass 1 — PRU firmware (ti-pru-cgt)
# All PRU projects are built first so that hexpru postBuildStep headers
# are written to the workspace before any R5F project tries to include them.
run: |
DEVICE="${{ matrix.device }}"
WORKSPACE="${HOME}/workspace_ccs_${DEVICE}"
CCS_CLI="$HOME/ti/ccs_20.3.1/ccs/eclipse/ccs-server-cli.sh"
mapfile -t SPECS < <(find . -name "example.projectspec" | grep "/${DEVICE}-" | sort)
mapfile -t PRU_SPECS < <(printf '%s\n' "${SPECS[@]}" | grep '/ti-pru-cgt/')
if [ "${#PRU_SPECS[@]}" -gt 0 ]; then
PRU_CMD=("${CCS_CLI}" -noSplash
-workspace "${WORKSPACE}"
-application com.ti.ccs.apps.buildProject
-ccs.configuration Release
-ccs.projects)
for spec in "${PRU_SPECS[@]}"; do
PRU_CMD+=("$(grep -oP '(?<=name = ")[^"]+' "${spec}" | head -1)")
done
"${PRU_CMD[@]}" 2>&1 | tee build_pru.log || true
else
echo "No ti-pru-cgt projects found for ${DEVICE}, skipping PRU build pass"
fi
- name: Build pass 2 — R5F/ARM (ti-arm-clang)
run: |
DEVICE="${{ matrix.device }}"
WORKSPACE="${HOME}/workspace_ccs_${DEVICE}"
CCS_CLI="$HOME/ti/ccs_20.3.1/ccs/eclipse/ccs-server-cli.sh"
mapfile -t SPECS < <(find . -name "example.projectspec" | grep "/${DEVICE}-" | sort)
mapfile -t R5F_SPECS < <(printf '%s\n' "${SPECS[@]}" | grep -v '/ti-pru-cgt/')
if [ "${#R5F_SPECS[@]}" -gt 0 ]; then
R5F_CMD=("${CCS_CLI}" -noSplash
-workspace "${WORKSPACE}"
-application com.ti.ccs.apps.buildProject
-ccs.configuration Release
-ccs.projects)
for spec in "${R5F_SPECS[@]}"; do
R5F_CMD+=("$(grep -oP '(?<=name = ")[^"]+' "${spec}" | head -1)")
done
"${R5F_CMD[@]}" 2>&1 | tee build_r5f.log || true
else
echo "No ti-arm-clang projects found for ${DEVICE}, skipping R5F build pass"
fi
- name: Check build results
run: |
DEVICE="${{ matrix.device }}"
# Recompute expected project counts (same find as build steps).
mapfile -t SPECS < <(find . -name "example.projectspec" | grep "/${DEVICE}-" | sort)
mapfile -t PRU_SPECS < <(printf '%s\n' "${SPECS[@]}" | grep '/ti-pru-cgt/')
mapfile -t R5F_SPECS < <(printf '%s\n' "${SPECS[@]}" | grep -v '/ti-pru-cgt/')
declare -A EXPECTED_COUNT
EXPECTED_COUNT[build_pru.log]="${#PRU_SPECS[@]}"
EXPECTED_COUNT[build_r5f.log]="${#R5F_SPECS[@]}"
TOTAL_ERRORS=0
BUILD_OK=true
for logfile in build_pru.log build_r5f.log; do
[ -f "${logfile}" ] || continue
# Primary: parse "N out of M projects have errors" summary lines.
E=$(grep -oP '\d+(?= out of \d+ projects have errors)' "${logfile}" | \
awk '{s+=$1} END{print s+0}')
# Secondary: sum per-project error counts from "# Problem summary"
# lines. Catches failures if the overall summary line is absent.
P=$(grep '# Problem summary' "${logfile}" | \
grep -oP '\d+(?= errors)' | awk '{s+=$1} END{print s+0}')
[ "${P}" -gt "${E}" ] && E="${P}"
# Tertiary: catch Eclipse "project not open" race — these do not
# always appear in the Problem summary count.
NOT_OPEN=$(grep -c 'is not open' "${logfile}" || true)
if [ "${NOT_OPEN}" -gt 0 ]; then
echo "ERROR: ${NOT_OPEN} 'project not open' error(s) in ${logfile}:"
grep 'is not open' "${logfile}" || true
fi
E=$((E + NOT_OPEN))
TOTAL_ERRORS=$((TOTAL_ERRORS + E))
if ! grep -q 'headless build complete' "${logfile}"; then
echo "ERROR: no 'headless build complete' line in ${logfile}"
BUILD_OK=false
fi
# Verify CCS built every project that was found on disk.
EXPECTED="${EXPECTED_COUNT[${logfile}]}"
BUILT=$(grep -oP '(?<=out of )\d+(?= projects have errors)' "${logfile}" | head -1)
BUILT="${BUILT:-0}"
if [ "${EXPECTED}" -gt 0 ] && [ "${BUILT}" -ne "${EXPECTED}" ]; then
echo "ERROR: ${logfile}: expected ${EXPECTED} projects, CCS built ${BUILT}"
BUILD_OK=false
fi
done
if [ "${TOTAL_ERRORS}" -gt 0 ]; then
echo "ERROR: ${TOTAL_ERRORS} build error(s) detected"
BUILD_OK=false
fi
if [ "${BUILD_OK}" != "true" ]; then
exit 1
fi
echo "CCS build passed for ${{ matrix.device }}"
- name: Upload logs
if: always()
uses: actions/upload-artifact@v4
with:
name: ccs-logs-${{ matrix.device }}
path: |
workspace_init.log
import.log
build_pru.log
build_r5f.log
if-no-files-found: ignore