diff --git a/.gitignore b/.gitignore index d9e4faace3..bf976c1f64 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,5 @@ CHANGELOG_GEN.md # Container Artifacts .pyusbip/ .cache/ + +CLAUDE.md \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c8a024c15..0e07d64a9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,16 @@ elseif(platform STREQUAL PULPOpen) elseif(platform STREQUAL GAP9) message(STATUS "Building for platform 'GAP9'") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) - set(ENV{KCONFIG_CONFIG} DeeployTest/Platforms/GAP9/sdk.config) + + # Select SDK config based on simulator type + if(SIMULATOR STREQUAL "board") + set(ENV{KCONFIG_CONFIG} DeeployTest/Platforms/GAP9/sdk_board.config) + message(STATUS "[GAP9] Using board SDK configuration") + else() + set(ENV{KCONFIG_CONFIG} DeeployTest/Platforms/GAP9/sdk_gvsoc.config) + message(STATUS "[GAP9] Using GVSoC SDK configuration") + endif() + include($ENV{GAP_SDK_HOME}/utils/cmake/setup.cmake) elseif(platform STREQUAL Generic) message(STATUS "Building for platform 'Generic'") @@ -225,6 +234,7 @@ endif() if(platform STREQUAL GAP9) project(${TESTNAME} LANGUAGES C ASM) include(${CMAKE_CURRENT_LIST_DIR}/cmake/gap9/gap9_gvsoc.cmake) + include(${CMAKE_CURRENT_LIST_DIR}/cmake/gap9/gap9_board.cmake) add_compile_options( -Wno-error=unknown-pragmas ) diff --git a/DeeployTest/Platforms/GAP9/CMakeLists.txt b/DeeployTest/Platforms/GAP9/CMakeLists.txt index db06a4e38f..cbb6382329 100644 --- a/DeeployTest/Platforms/GAP9/CMakeLists.txt +++ b/DeeployTest/Platforms/GAP9/CMakeLists.txt @@ -20,6 +20,11 @@ target_include_directories(${ProjectId} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/inc) target_link_libraries(${ProjectId} PRIVATE network deeploylib) target_compile_options(${ProjectId} INTERFACE network) add_gvsoc_emulation(${ProjectId} "gap9.evk") +add_board_deployment(${ProjectId} "gap9.evk") + +if(POWER_MEASUREMENT) + target_compile_definitions(${ProjectId} PRIVATE POWER_MEASUREMENT) +endif() # RW: Waive sign comparison warnings from pulp_nn_utils.h target_compile_options(network PRIVATE diff --git a/DeeployTest/Platforms/GAP9/sdk_board.config b/DeeployTest/Platforms/GAP9/sdk_board.config new file mode 100644 index 0000000000..9629eb9f66 --- /dev/null +++ b/DeeployTest/Platforms/GAP9/sdk_board.config @@ -0,0 +1,34 @@ +# SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + +CONFIG_BOARD_GAP9MOD_V1_0_B=y +CONFIG_BOARD_GAP9EVK_V1_3=y + +CONFIG_DRIVER_CLUSTER=y +CONFIG_DRIVER_CLUSTER_CONF_PROPERTY_ICACHE_CONF=0x7FF + +CONFIG_DRIVER_TYPE_FLASH=y +CONFIG_DRIVER_TYPE_RAM=y + +CONFIG_DRIVER_MRAM=y +CONFIG_READFS_FLASH_TYPE_OSPI=y + +CONFIG_DRIVER_READFS=y +# CONFIG_READFS_FLASH_TYPE_MRAM=y + +CONFIG_DRIVER_APS256XXN=y +CONFIG_DRIVER_RAM_API=y + +CONFIG_ENABLE_LIBMATH=y + +# OS float printf +CONFIG_IO_PRINTF_FLOAT_ENABLE=y +CONFIG_IO_PRINTF_FLOAT_EXPONENT_ENABLE=y + +# CONFIG_PLATFORM_GVSOC=y +CONFIG_PLATFORM_BOARD=y +# CONFIG_DRIVER_CLUSTERDECOMPRESSOR=n + +#CONFIG_CL_MASTER_CORE_STACK_SIZE=14000 +#CONFIG_CL_SLAVE_CORE_STACK_SIZE=1000 diff --git a/DeeployTest/Platforms/GAP9/sdk.config b/DeeployTest/Platforms/GAP9/sdk_gvsoc.config similarity index 92% rename from DeeployTest/Platforms/GAP9/sdk.config rename to DeeployTest/Platforms/GAP9/sdk_gvsoc.config index 8d6fb6b178..2f5bdf053c 100644 --- a/DeeployTest/Platforms/GAP9/sdk.config +++ b/DeeployTest/Platforms/GAP9/sdk_gvsoc.config @@ -27,9 +27,10 @@ CONFIG_IO_PRINTF_FLOAT_ENABLE=y CONFIG_IO_PRINTF_FLOAT_EXPONENT_ENABLE=y CONFIG_PLATFORM_GVSOC=y +# CONFIG_PLATFORM_BOARD=y # CONFIG_DRIVER_CLUSTERDECOMPRESSOR=n # GAP9 cluster stack size configuration # Uncomment and adjust these values if you need to modify stack sizes: # CONFIG_CL_MASTER_CORE_STACK_SIZE=14000 -# CONFIG_CL_SLAVE_CORE_STACK_SIZE=1000 \ No newline at end of file +# CONFIG_CL_SLAVE_CORE_STACK_SIZE=1000 diff --git a/DeeployTest/Platforms/GAP9/src/deeploytest.c b/DeeployTest/Platforms/GAP9/src/deeploytest.c index f4818e0104..77fe46a4e9 100644 --- a/DeeployTest/Platforms/GAP9/src/deeploytest.c +++ b/DeeployTest/Platforms/GAP9/src/deeploytest.c @@ -16,6 +16,11 @@ // RW: Remove MAINSTACKSIZE because gap9-sdk does not use it #define SLAVESTACKSIZE 3800 +#ifdef POWER_MEASUREMENT +unsigned int GPIOs = 89; +#define WRITE_GPIO(x) pi_gpio_pin_write(GPIOs, x) +#endif + struct pi_device cluster_dev; uint32_t total_cycles = 0; @@ -78,6 +83,13 @@ void RunNetworkWrapper(void *args) { } int main(void) { + +#ifdef POWER_MEASUREMENT + pi_pad_function_set(GPIOs, 1); + pi_gpio_pin_configure(GPIOs, PI_GPIO_OUTPUT); + pi_gpio_pin_write(GPIOs, 0); +#endif + #ifndef CI uint32_t core_id = pi_core_id(), cluster_id = pi_cluster_id(); printf("[%d %d] Hello World!\n", cluster_id, core_id); @@ -119,8 +131,17 @@ int main(void) { pi_cluster_task(&cluster_task, RunNetworkWrapper, NULL); cluster_task.slave_stack_size = SLAVESTACKSIZE; + +#ifdef POWER_MEASUREMENT + WRITE_GPIO(1); +#endif + pi_cluster_send_task_to_cl(&cluster_dev, &cluster_task); +#ifdef POWER_MEASUREMENT + WRITE_GPIO(0); +#endif + #ifndef CI printf("Output:\r\n"); #endif diff --git a/DeeployTest/deeployRunner_gap9.py b/DeeployTest/deeployRunner_gap9.py index ace2d8eb25..79446ae3ba 100644 --- a/DeeployTest/deeployRunner_gap9.py +++ b/DeeployTest/deeployRunner_gap9.py @@ -12,6 +12,10 @@ # Define parser setup callback to add GAP9-specific arguments def setup_parser(parser): parser.add_argument('--cores', type = int, default = 8, help = 'Number of cores (default: 8)\n') + parser.add_argument('--powerMeasurement', + action = 'store_true', + default = False, + help = 'Enable power measurement GPIO toggling\n') sys.exit( main(default_platform = "GAP9", diff --git a/DeeployTest/deeployRunner_tiled_gap9.py b/DeeployTest/deeployRunner_tiled_gap9.py index cde8e1e1d8..394522d7b8 100644 --- a/DeeployTest/deeployRunner_tiled_gap9.py +++ b/DeeployTest/deeployRunner_tiled_gap9.py @@ -12,6 +12,10 @@ # Define parser setup callback to add GAP9-specific arguments def setup_parser(parser): parser.add_argument('--cores', type = int, default = 8, help = 'Number of cores (default: 8)\n') + parser.add_argument('--powerMeasurement', + action = 'store_true', + default = False, + help = 'Enable power measurement GPIO toggling\n') sys.exit( main(default_platform = "GAP9", diff --git a/DeeployTest/testUtils/core/execution.py b/DeeployTest/testUtils/core/execution.py index 1dcddeea62..4c6c972679 100644 --- a/DeeployTest/testUtils/core/execution.py +++ b/DeeployTest/testUtils/core/execution.py @@ -3,9 +3,9 @@ # SPDX-License-Identifier: Apache-2.0 import os +import re import shutil import subprocess -import sys from pathlib import Path from Deeploy.Logging import DEFAULT_LOGGER as log @@ -148,6 +148,12 @@ def build_binary(config: DeeployTestConfig) -> None: raise RuntimeError(f"Build failed for {config.test_name}") +# Source: https://stackoverflow.com/a/38662876 +def escapeAnsi(line): + ansi_escape = re.compile(r'(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]') + return ansi_escape.sub('', line) + + def run_simulation(config: DeeployTestConfig, skip: bool = False) -> TestResult: """ Run simulation and parse output. @@ -191,15 +197,29 @@ def run_simulation(config: DeeployTestConfig, skip: bool = False) -> TestResult: log.debug(f"[Execution] Simulation command: {' '.join(cmd)}") - result = subprocess.run(cmd, capture_output = True, text = True, env = env) - - if result.stdout: - print(result.stdout, end = '') - if result.stderr: - print(result.stderr, end = '', file = sys.stderr) + cmd_str = " ".join(cmd) + with subprocess.Popen(cmd_str, + stdout = subprocess.PIPE, + stderr = subprocess.STDOUT, + shell = True, + encoding = 'utf-8') as process: + + with open('out.txt', 'a', encoding = 'utf-8') as fileHandle: + fileHandle.write( + f"################## Testing {config.test_dir} on {config.platform} Platform ##################\n") + + result = "" + while True: + output = process.stdout.readline() + if output == '' and process.poll() is not None: + break + if output: + print(output.strip()) + result += output + fileHandle.write(f"{escapeAnsi(output)}") # Parse output for error count and cycles - test_result = parse_test_output(result.stdout, result.stderr) + test_result = parse_test_output(result, "") if not test_result.success and test_result.error_count == -1: log.warning(f"Could not parse error count from output") diff --git a/DeeployTest/testUtils/deeployRunner.py b/DeeployTest/testUtils/deeployRunner.py index a5a8d70ef3..e94de4250c 100644 --- a/DeeployTest/testUtils/deeployRunner.py +++ b/DeeployTest/testUtils/deeployRunner.py @@ -395,7 +395,6 @@ def main(default_platform: Optional[str] = None, # Extract platform-specific CMake args from parsed args if available if platform_specific_cmake_args is None: platform_specific_cmake_args = [] - # Check for platform-specific arguments in args object and build CMake args if hasattr(args, 'cores'): platform_specific_cmake_args.append(f"-DNUM_CORES={args.cores}") @@ -405,6 +404,12 @@ def main(default_platform: Optional[str] = None, if hasattr(args, 'num_clusters'): platform_specific_cmake_args.append(f"-DNUM_CLUSTERS={args.num_clusters}") + if hasattr(args, 'powerMeasurement') and args.powerMeasurement: + platform_specific_cmake_args.append("-DPOWER_MEASUREMENT=ON") + + if platform == 'GAP9': + platform_specific_cmake_args.append("-D SIMULATOR=" + simulator) + config = create_config_from_args(args, platform, simulator, tiling_enabled, platform_specific_cmake_args) print_configuration(config) diff --git a/DeeployTest/testUtils/testRunner.py b/DeeployTest/testUtils/testRunner.py index 9578c2f26c..2d38cf6581 100644 --- a/DeeployTest/testUtils/testRunner.py +++ b/DeeployTest/testUtils/testRunner.py @@ -298,7 +298,7 @@ def __init__(self, gen_args: str = "", cmake_args: str = ""): - if simulator not in ['gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'host', 'none']: + if simulator not in ['gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'host', 'board', 'none']: raise ValueError( f"Invalid emulator {simulator} (valid options are 'gvsoc', 'banshee', 'qemu', 'vsim', 'vsim.gui', 'host', 'none')!" ) @@ -381,7 +381,7 @@ def configure_cmake_project(self): else: self.cmake_args += " -D gvsoc_simulation=OFF" - command = f"$CMAKE -D TOOLCHAIN={self._args.toolchain} -D GVSOC_INSTALL_DIR={self._dir_gvsoc} -D TOOLCHAIN_INSTALL_DIR={self._dir_toolchain} -D GENERATED_SOURCE={self._dir_gen} -D platform={self._platform} {self.cmake_args} -B {self._dir_build} -D TESTNAME={self._name_test} .." + command = f"$CMAKE -D TOOLCHAIN={self._args.toolchain} -D GVSOC_INSTALL_DIR={self._dir_gvsoc} -D TOOLCHAIN_INSTALL_DIR={self._dir_toolchain} -D GENERATED_SOURCE={self._dir_gen} -D platform={self._platform} -D SIMULATOR={self._simulator} {self.cmake_args} -B {self._dir_build} -D TESTNAME={self._name_test} .." if self._args.verbose >= 3: command = "VERBOSE=1 " + command + " --log-level debug" @@ -414,6 +414,8 @@ def run_simulation(self, out_file = 'out.txt'): if self._simulator == 'host': command = f"{self._dir_build}/bin/{self._name_test}" + elif self._simulator == 'board': + command = f"$CMAKE --build {self._dir_build} --target board_{self._name_test}" else: command = f"$CMAKE --build {self._dir_build} --target {self._simulator}_{self._name_test}" diff --git a/cmake/gap9/gap9_board.cmake b/cmake/gap9/gap9_board.cmake new file mode 100644 index 0000000000..d8db011578 --- /dev/null +++ b/cmake/gap9/gap9_board.cmake @@ -0,0 +1,76 @@ +# SPDX-FileCopyrightText: 2025 ETH Zurich and University of Bologna +# +# SPDX-License-Identifier: Apache-2.0 + + +macro(add_board_deployment name target) + + if(NOT DEFINED GVSOC_INSTALL_DIR) + message(FATAL_ERROR "Environment variable GVSOC_INSTALL_DIR not set") + endif() + + message(STATUS "[Deeploy GAP9] Running on Board") + + set(BOARD_WORKDIR ${CMAKE_BINARY_DIR}/board_workdir) + set(DEEPLOY_BINARY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${name}") + set(GAP9_SDK_HOME $ENV{GAP_SDK_HOME}) + set(GAPY "${GAP9_SDK_HOME}/utils/gapy_v2/bin/gapy") + set(FLASH_LAYOUT "${GAP9_SDK_HOME}/utils/layouts/default_layout_multi_readfs.json") + set(FSBL_BINARY "${GAP9_SDK_HOME}/install/target/bin/fsbl") + set(SSBL_BINARY "${GAP9_SDK_HOME}/install/target/bin/ssbl") + + make_directory(${BOARD_WORKDIR}) + + if(NOT DEFINED GAP9_SDK_HOME) + message(FATAL_ERROR "Environment variable GAP_SDK_HOME not set") + endif() + + # Command to run on board + set(GAPY_CMD + ${GAPY} + --target=gap9.evk + --platform=board + --target-property=boot.flash_device=mram + --target-property=boot.mode=flash + --target-dir=${GAP9_SDK_HOME}/utils/gapy_v2/targets + --openocd-cable=${GAP9_SDK_HOME}/utils/openocd_tools/tcl/gapuino_ftdi.cfg + --openocd-script=${GAP9_SDK_HOME}/utils/openocd_tools/tcl/gap9revb.tcl + --openocd-tools=${GAP9_SDK_HOME}/utils/openocd_tools + --binary=${DEEPLOY_BINARY} + --work-dir=${BOARD_WORKDIR} + --multi-flash-content=${FLASH_LAYOUT} + --flash-size=67108864 + --flash-property=${FSBL_BINARY}@mram:fsbl:binary + --flash-property=${SSBL_BINARY}@mram:ssbl:binary + --flash-property=${DEEPLOY_BINARY}@mram:app:binary run + --py-stack + ) + + # Add readfs files if provided + if(GAPY_RUNNER_ARGS) + list(LENGTH GAPY_RUNNER_ARGS num_readfs_files) + message(STATUS "[Deeploy GAP9] Adding ${num_readfs_files} readfs file(s)") + list(APPEND GAPY_CMD ${GAPY_RUNNER_ARGS}) + endif() + + # Convert list to string for printing + string(REPLACE ";" " " GAPY_CMD_STR "${GAPY_CMD}") + + add_custom_target(board_${name} + DEPENDS ${name} + WORKING_DIRECTORY ${BOARD_WORKDIR} + COMMAND bash -c "${CMAKE_COMMAND} -E copy_if_different ${CMAKE_BINARY_DIR}/*.bin ${BOARD_WORKDIR}/ 2>/dev/null || true" + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${GAP9_SDK_HOME}/utils/efuse/GAP9/efuse_hyper_preload.data + ${BOARD_WORKDIR}/chip.efuse_preload.data + COMMAND ${CMAKE_COMMAND} -E echo "==========================================" + COMMAND ${CMAKE_COMMAND} -E echo "[Deeploy GAP9] Executing gapy command to run on board:" + COMMAND ${CMAKE_COMMAND} -E echo "${GAPY_CMD_STR}" + COMMAND ${CMAKE_COMMAND} -E echo "==========================================" + COMMAND ${GAPY_CMD} + COMMENT "Running ${name} with gapy on GAP9 board" + POST_BUILD + USES_TERMINAL + VERBATIM + ) +endmacro() \ No newline at end of file diff --git a/cmake/gap9/gap9_gvsoc.cmake b/cmake/gap9/gap9_gvsoc.cmake index 5a6b205376..408a541e95 100644 --- a/cmake/gap9/gap9_gvsoc.cmake +++ b/cmake/gap9/gap9_gvsoc.cmake @@ -2,8 +2,6 @@ # # SPDX-License-Identifier: Apache-2.0 -# Mark that GAP9-specific gvsoc emulation is defined -set(GAP9_GVSOC_DEFINED TRUE) macro(add_gvsoc_emulation name target) @@ -19,6 +17,7 @@ macro(add_gvsoc_emulation name target) # Check if GAPY_RUNNER_ARGS is defined and non-empty (indicates L3 with readfs files) if(GAPY_RUNNER_ARGS) + # L3 mode: Use gapy with flash layout and readfs message(STATUS "[Deeploy GAP9] L3 mode: using gapy with readfs") @@ -81,7 +80,6 @@ macro(add_gvsoc_emulation name target) USES_TERMINAL VERBATIM ) - else() # L2 mode: Use traditional gvsoc command directly (no flash/readfs) message(STATUS "[Deeploy GAP9] L2 mode: using traditional gvsoc without flash") diff --git a/scripts/gap9-run.sh b/scripts/gap9-run.sh index af2dc20e0a..f3ad70c2dd 100755 --- a/scripts/gap9-run.sh +++ b/scripts/gap9-run.sh @@ -68,6 +68,45 @@ log_success() { echo -e "\033[0;32m[SUCCESS]\033[0m $*" } +# Resolve USBIP_HOST to an IP usable from the host network namespace. +# On Linux, host.docker.internal doesn't resolve natively (no Docker Desktop DNS). +resolve_usbip_host() { + local host="$1" + # Already an IP address — return as-is + if [[ "$host" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "$host" + return + fi + # On non-Linux (macOS/Windows), Docker Desktop resolves host.docker.internal natively + if [[ "$(uname -s)" != "Linux" ]]; then + echo "$host" + return + fi + # Linux: try standard DNS resolution + local ip + ip=$(getent hosts "$host" 2>/dev/null | awk '{print $1; exit}') + if [[ -n "$ip" ]]; then + echo "$ip" + return + fi + # Fallback for host.docker.internal on native Linux Docker: + # use the docker0 bridge interface or Docker bridge network gateway + if [[ "$host" == "host.docker.internal" ]]; then + ip=$(ip -4 addr show docker0 2>/dev/null | sed -n 's/.*inet \([0-9.]*\).*/\1/p' | head -1) + if [[ -n "$ip" ]]; then + echo "$ip" + return + fi + ip=$(docker network inspect bridge -f '{{range .IPAM.Config}}{{.Gateway}}{{end}}' 2>/dev/null | head -1) + if [[ -n "$ip" ]]; then + echo "$ip" + return + fi + fi + # Last resort: return hostname as-is + echo "$host" +} + # Display help message show_help() { cat <