-
Notifications
You must be signed in to change notification settings - Fork 94
Adding the example for the sensor based data collection and running on a riscv device #250
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
08a1422
cdffaa3
15e633a
cdf960b
9c3a920
711fa01
434635d
39ed301
26d1747
0c792dd
6278731
a2160d7
05fb789
84d8402
32544aa
be89ac9
0d61be2
bef370a
0a8753f
ce3e999
5252fef
4a7eaa3
052d59d
d524d8e
d4b793c
e71f44e
f648c6c
0451e98
aba9757
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| # This file adds the necessary configurations to cross compile | ||
| # mlpack for embedded systems. You need to set the following variables | ||
| # from the command line: CMAKE_SYSROOT and TOOLCHAIN_PREFIX. | ||
| # This file will compile OpenBLAS if it is downloaded and it is not | ||
| # available on your system in order to find the BLAS library. If OpenBLAS will | ||
| # be compiled, the OPENBLAS_TARGET variable must be set. This can be done | ||
| # by, e.g., setting ARCH_NAME (which will set OPENBLAS_TARGET in | ||
| # `flags-config.cmake`). | ||
|
|
||
| # Apply a list of patch files to an OpenBLAS source tree before it is built. | ||
| # `srcDir` is the unpacked OpenBLAS directory; `patches` is a ;-list of patch | ||
| # files (each `patch -p1`-compatible). Applying is idempotent: a patch that is | ||
| # already applied (detected via a reverse dry-run) is skipped, so re-running | ||
| # CMake against an existing build tree is safe. A patch that neither applies | ||
| # cleanly nor is already applied is a hard error -- unlike an in-place `sed`, it | ||
| # never silently does nothing when the upstream source has changed. | ||
| function(apply_openblas_patches srcDir patches) | ||
| find_program(PATCH_EXECUTABLE patch) | ||
| if(NOT PATCH_EXECUTABLE) | ||
| message(FATAL_ERROR "The 'patch' tool is required to apply OPENBLAS_PATCHES " | ||
| "but was not found on PATH.") | ||
| endif() | ||
|
|
||
| foreach(patchFile IN LISTS patches) | ||
| if(NOT IS_ABSOLUTE "${patchFile}") | ||
| set(patchFile "${CMAKE_CURRENT_LIST_DIR}/${patchFile}") | ||
| endif() | ||
| if(NOT EXISTS "${patchFile}") | ||
| message(FATAL_ERROR "OpenBLAS patch not found: ${patchFile}") | ||
| endif() | ||
|
|
||
| get_filename_component(patchName "${patchFile}" NAME) | ||
|
|
||
| # Already applied? `patch -R --dry-run` succeeds only if the reverse patch | ||
| # would apply, i.e. the forward patch is already in place. | ||
| execute_process( | ||
| COMMAND ${PATCH_EXECUTABLE} -p1 -R --dry-run --force | ||
| --input=${patchFile} | ||
| WORKING_DIRECTORY ${srcDir} | ||
| RESULT_VARIABLE alreadyApplied | ||
| OUTPUT_QUIET ERROR_QUIET) | ||
| if(alreadyApplied EQUAL 0) | ||
| message(STATUS "OpenBLAS patch already applied, skipping: ${patchName}") | ||
| continue() | ||
| endif() | ||
|
|
||
| execute_process( | ||
| COMMAND ${PATCH_EXECUTABLE} -p1 --forward --input=${patchFile} | ||
| WORKING_DIRECTORY ${srcDir} | ||
| RESULT_VARIABLE patchResult | ||
| OUTPUT_VARIABLE patchOutput ERROR_VARIABLE patchOutput) | ||
| if(NOT patchResult EQUAL 0) | ||
| message(FATAL_ERROR | ||
| "Failed to apply OpenBLAS patch ${patchName}:\n${patchOutput}") | ||
| endif() | ||
| message(STATUS "Applied OpenBLAS patch: ${patchName}") | ||
| endforeach() | ||
| endfunction() | ||
|
|
||
| if (CMAKE_CROSSCOMPILING) | ||
| include(CMake/crosscompile-arch-config.cmake) | ||
| if (NOT CMAKE_SYSROOT AND (NOT TOOLCHAIN_PREFIX)) | ||
| message(FATAL_ERROR "Neither CMAKE_SYSROOT nor TOOLCHAIN_PREFIX are set; please set both of them and try again.") | ||
| elseif(NOT CMAKE_SYSROOT) | ||
| message(FATAL_ERROR "Cannot configure: CMAKE_SYSROOT must be set when performing cross-compiling!") | ||
| elseif(NOT TOOLCHAIN_PREFIX) | ||
| message(FATAL_ERROR "Cannot configure: TOOLCHAIN_PREFIX must be set when performing cross-compiling!") | ||
| endif() | ||
|
|
||
| # Now make sure that we can still compile a simple test program. | ||
| # (This ensures we didn't add any bad CXXFLAGS.) | ||
| # Note that OUTPUT_VARIABLE is only available in newer versions of CMake! | ||
| # CMake 3.23 (silently) introduced the variable. | ||
| include(CheckCXXSourceCompiles) | ||
| if (CMAKE_VERSION VERSION_LESS "3.22.0") | ||
| check_cxx_source_compiles("int main() { return 0; }" COMPILE_SUCCESS) | ||
| if (NOT COMPILE_SUCCESS) | ||
| message(FATAL_ERROR "The C++ cross-compiler at ${CMAKE_CXX_COMPILER} is " | ||
| "not able to compile a trivial test program. Check the CXXFLAGS!") | ||
| endif () | ||
| else () | ||
| check_cxx_source_compiles("int main() { return 0; }" COMPILE_SUCCESS | ||
| OUTPUT_VARIABLE COMPILE_OUTPUT) | ||
| if (NOT COMPILE_SUCCESS) | ||
| message(FATAL_ERROR "The C++ cross-compiler at ${CMAKE_CXX_COMPILER} is " | ||
| "not able to compile a trivial test program. Compiler output:\n\n" | ||
| "${COMPILE_OUTPUT}") | ||
| endif () | ||
| endif () | ||
| endif() | ||
|
|
||
| macro(search_openblas version) | ||
| set(BLA_STATIC ON) | ||
| find_package(BLAS) | ||
| if (NOT BLAS_FOUND OR (NOT BLAS_LIBRARIES)) | ||
| if(NOT OPENBLAS_TARGET) | ||
| message(FATAL_ERROR "Cannot compile OpenBLAS: OPENBLAS_TARGET is not set. Either set that variable, or set BOARD_NAME correctly!") | ||
| endif() | ||
| get_deps(https://github.com/xianyi/OpenBLAS/releases/download/v${version}/OpenBLAS-${version}.tar.gz OpenBLAS OpenBLAS-${version}.tar.gz) | ||
| if (NOT MSVC) | ||
| if (NOT EXISTS "${CMAKE_BINARY_DIR}/deps/OpenBLAS-${version}/libopenblas.a") | ||
| set(ENV{COMMON_OPT} "${CMAKE_OPENBLAS_FLAGS}") # Pass our flags to OpenBLAS | ||
|
|
||
| # NN-on-device memory fit (riscv64). OpenBLAS lazily allocates a per-GEMM | ||
| # scratch buffer (BUFFER_SIZE -- 32 MB on riscv64) sized for its default | ||
| # N-block (SGEMM_DEFAULT_R = 12288). That single 32 MB allocation does | ||
| # not fit on a ~28 MB device, so the first f32 matrix-multiply -- e.g. the | ||
| # neural network's dense layers -- is OOM-killed at startup. (Random | ||
| # forest and KNN avoid that big GEMM path, which is why only the NN | ||
| # failed.) The fix is a patch that shrinks the N-block to 2048 and the | ||
| # buffer to 8 MB; see CMake/patches/openblas-riscv64-low-memory.patch and | ||
| # README.md / BINARY_SIZE.md for the full investigation. | ||
| # | ||
| # For the generic riscv64 target we apply that patch by default; a caller | ||
| # can override or extend the list with -DOPENBLAS_PATCHES="a.patch;b.patch" | ||
| # (absolute paths, or relative to this CMake/ directory). | ||
| if(NOT DEFINED OPENBLAS_PATCHES AND OPENBLAS_TARGET STREQUAL "RISCV64_GENERIC") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should definitely not have RISC-V-specific code in this file at all. It should go entirely into If you really think this approach is better, and there is something I overlooked, I'm totally open to a discussion about it, but, I am pretty sure this was something Claude chose and I disagree pretty strongly with it. |
||
| set(OPENBLAS_PATCHES | ||
| "${CMAKE_CURRENT_LIST_DIR}/patches/openblas-riscv64-low-memory.patch") | ||
| endif() | ||
| apply_openblas_patches("${CMAKE_BINARY_DIR}/deps/OpenBLAS-${version}" | ||
| "${OPENBLAS_PATCHES}") | ||
| # USE_THREAD=0 / NUM_THREADS=1 / USE_OPENMP=0: build a single-threaded | ||
| # OpenBLAS. On a single-core, 64 MB target (Milk-V Duo) the threaded | ||
| # build spawns worker threads that busy-wait (spin) at startup, which | ||
| # starves the main thread on one core and hangs the program before it | ||
| # even runs. Single-threaded also removes the per-thread GEMM buffers. | ||
| execute_process(COMMAND make TARGET=${OPENBLAS_TARGET} BINARY=${OPENBLAS_BINARY} HOSTCC=gcc CC=${CMAKE_C_COMPILER} FC=${CMAKE_FORTRAN_COMPILER} NO_SHARED=1 USE_THREAD=0 NUM_THREADS=1 USE_OPENMP=0 | ||
| WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/deps/OpenBLAS-${version}) | ||
| endif() | ||
| file(GLOB OPENBLAS_LIBRARIES "${CMAKE_BINARY_DIR}/deps/OpenBLAS-${version}/libopenblas.a") | ||
| set(BLAS_openblas_LIBRARY ${OPENBLAS_LIBRARIES}) | ||
| set(LAPACK_openblas_LIBRARY ${OPENBLAS_LIBRARIES}) | ||
| set(BLA_VENDOR OpenBLAS) | ||
| set(BLAS_FOUND ON) | ||
| endif() | ||
| endif() | ||
| find_library(GFORTRAN NAMES libgfortran.a) | ||
| find_library(PTHREAD NAMES libpthread.a) | ||
| set(CROSS_COMPILE_SUPPORT_LIBRARIES ${GFORTRAN} ${PTHREAD}) | ||
| endmacro() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| # This function provides a set of specific flags for each supported board | ||
| # depending on the processor type. The objective is to optimize for size. | ||
| # Thus, all of the following flags are chosen carefully to reduce binary | ||
| # footprints. | ||
|
|
||
| # Set generic minimization flags for all platforms. | ||
| # These flags are the same for all cross-compilation cases and they are | ||
| # mainly to reduce the binary footprint. | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s -fdata-sections -ffunction-sections") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer -fno-unwind-tables") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-asynchronous-unwind-tables -fvisibility=hidden") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fshort-enums -finline-small-functions") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -findirect-inlining -fno-common") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fmerge-all-constants -fno-ident") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-unroll-loops -fno-math-errno") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-stack-protector") | ||
| set(CMAKE_OPENBLAS_FLAGS "${CMAKE_CXX_FLAGS}") # OpenBLAS does not supoport flto | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto") | ||
| set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--hash-style=gnu -Wl,--build-id=none") | ||
| set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,norelro") | ||
| set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gc-sections") | ||
| ## Keep the following flag in comment, they might be relevant in the case of MCU's | ||
| #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,-nmagic,-Bsymbolic -nostartfiles") | ||
|
|
||
| # BOARD_NAME is deprecated and will be removed in mlpack 5. | ||
| # please use ARCH_NAME instead. | ||
| set(BOARD_NAME "" CACHE STRING "Specify Board name to optimize for.") | ||
| set(ARCH_NAME "" CACHE STRING "Name of embedded architecture to optimize for.") | ||
|
|
||
| if (BOARD_NAME) | ||
| set(ARCH_NAME "${BOARD_NAME}") | ||
| endif() | ||
|
|
||
| string(TOUPPER ${ARCH_NAME} ARCH) | ||
|
|
||
| # Set specific platforms CMAKE CXX flags. | ||
| if(ARCH STREQUAL "RPI2" OR ARCH STREQUAL "CORTEXA7") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a7") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon-vfpv4") | ||
| set(OPENBLAS_TARGET "ARMV7") | ||
| set(OPENBLAS_BINARY "32") | ||
| elseif(ARCH STREQUAL "CORTEXA8") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a8") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon") | ||
| set(OPENBLAS_TARGET "ARMV7") | ||
| set(OPENBLAS_BINARY "32") | ||
| elseif(ARCH STREQUAL "CORTEXA9") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a9") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon") | ||
| set(OPENBLAS_TARGET "CORTEXA9") | ||
| set(OPENBLAS_BINARY "32") | ||
| elseif(ARCH STREQUAL "CORTEXA15") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a15") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=hard -mfpu=neon") | ||
| set(OPENBLAS_TARGET "CORTEXA15") | ||
| set(OPENBLAS_BINARY "32") | ||
| elseif(ARCH STREQUAL "RPI3" OR ARCH STREQUAL "CORTEXA53") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a53 -ftree-vectorize") | ||
| set(OPENBLAS_TARGET "CORTEXA53") | ||
| set(OPENBLAS_BINARY "64") | ||
| elseif(ARCH STREQUAL "RPI4" OR ARCH STREQUAL "CORTEXA72") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8.2-a+crypto+fp16+rcpc+dotprod -fasynchronous-unwind-tables") | ||
| set(OPENBLAS_TARGET "CORTEXA72") | ||
| set(OPENBLAS_BINARY "64") | ||
| elseif(ARCH STREQUAL "JETSONAGX" OR ARCH STREQUAL "CORTEXA76") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=cortex-a76 -ftree-vectorize") | ||
| set(OPENBLAS_TARGET "CORTEXA76") | ||
| set(OPENBLAS_BINARY "64") | ||
| elseif(ARCH STREQUAL "BV") | ||
| set(OPENBLAS_TARGET "RISCV64_GENERIC") | ||
| set(OPENBLAS_BINARY "64") | ||
| elseif(ARCH STREQUAL "RV64GCV") | ||
| # RV64GCV is the ISA (e.g. the T-Head C906 core); -mtune=thead-c906 is the | ||
| # matching GCC tuning flag (there is no -mtune=rv64gcv). | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=thead-c906") | ||
| set(OPENBLAS_TARGET "RISCV64_GENERIC") | ||
| set(OPENBLAS_BINARY "64") | ||
| elseif(ARCH STREQUAL "x280") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mtune=sifive-x280") | ||
| set(OPENBLAS_TARGET "x280") | ||
| set(OPENBLAS_BINARY "64") | ||
| elseif(ARCH STREQUAL "KATAMI") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium3") | ||
| set(OPENBLAS_TARGET "KATAMI") | ||
| set(OPENBLAS_BINARY "32") | ||
| elseif(ARCH STREQUAL "COPPERMINE") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium3") | ||
| set(OPENBLAS_TARGET "COPPERMINE") | ||
| set(OPENBLAS_BINARY "32") | ||
| elseif(ARCH STREQUAL "NORTHWOOD") | ||
| set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=pentium4") | ||
| set(OPENBLAS_TARGET "NORTHWOOD") | ||
| set(OPENBLAS_BINARY "32") | ||
| elseif(ARCH) | ||
| ## TODO: update documentation with a list of the supported boards. | ||
| message(FATAL_ERROR "Given ARCH_NAME is not known; please choose a supported board from the list") | ||
| endif() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| ## This file handles cross-compilation configurations for any architecture. | ||
| ## The objective of this file is to find and assign cross-compiler and the | ||
| ## entire toolchain. | ||
| ## | ||
| ## This configuration works best with the buildroot toolchain. When using this | ||
| ## file, be sure to set the TOOLCHAIN_PREFIX and CMAKE_SYSROOT variables, | ||
| ## preferably via the CMake configuration command (e.g. `-DCMAKE_SYSROOT=<...>`). | ||
| ## | ||
| ## You can use any toochain to produce the cross compiled binaries. However, | ||
| ## we recommend using buildroot toolchain for cross-compilation. Here is the | ||
| ## link to download the toolchains: https://toolchains.bootlin.com/ | ||
|
|
||
| set(CMAKE_SYSTEM_NAME Linux) | ||
| set(CMAKE_SYSROOT) | ||
| set(TOOLCHAIN_PREFIX "" CACHE STRING "Path for toolchain for cross compiler and other compilation tools.") | ||
|
|
||
| # Ensure that CMake tries to build static libraries when testing the compiler. | ||
| set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) | ||
|
|
||
| set(CMAKE_AR "${TOOLCHAIN_PREFIX}gcc-ar" CACHE FILEPATH "" FORCE) | ||
| set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc) | ||
| set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++) | ||
| set(CMAKE_LINKER ${TOOLCHAIN_PREFIX}ld) | ||
| set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> qcs <TARGET> <LINK_FLAGS> <OBJECTS>") | ||
| set(CMAKE_C_ARCHIVE_FINISH true) | ||
| set(CMAKE_FORTRAN_COMPILER ${TOOLCHAIN_PREFIX}gfortran) | ||
| set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER}) | ||
| set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy CACHE INTERNAL "objcopy tool") | ||
| set(CMAKE_SIZE_UTIL ${TOOLCHAIN_PREFIX}size CACHE INTERNAL "size tool") | ||
|
|
||
| ## Here are the standard ROOT_PATH if you are using the standard toolchain | ||
| ## if you are using a different toolchain you have to specify that too. | ||
| set(CMAKE_FIND_ROOT_PATH "${CMAKE_SYSROOT}") | ||
|
|
||
| set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --sysroot=${CMAKE_SYSROOT}" CACHE INTERNAL "" FORCE) | ||
|
|
||
| set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) | ||
| set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) | ||
| set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) | ||
| set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an insanely overcomplicated patch applier that is basically impossible to quickly understand because it tries to do so much error handling. It would be better without any of the error handling. I wrote something that did this in the Milk-V CI PR:
https://github.com/mlpack/mlpack/pull/4234/changes#diff-3e58bfa6d751b0ea06062ba1f3becd2b8d27ac6df3a820b3ec3516802c04bad7
Take a look at line 657 onwards of
mlpack.cmake.Every little bit of extra error handling and extra complexity is us accepting responsibility for a user problem. Here, we try to find whether the patch will apply cleanly or not and skip it if it's already been applied. That sounds nice, but why? We should only be compiling OpenBLAS once, at the moment of download, and if the patch does not apply cleanly then, then something is clearly wrong and we should throw a failure instead of ignoring it.
What is the cost of this extra code? It might look like nothing since it took Claude five seconds, but, when we accept the responsibility for the user problem (patch does not apply cleanly because something external to our code changed), we accept the responsibility for maintaining this code when something goes wrong, and that takes way way more than five seconds.
You might think, "but this code looks good! The likelihood of it failing in the future is low because it will not be changed!" But somewhere in all the changes for applying patches to OpenBLAS, there is a bug. I'll leave it up to you to find it... see how long it takes to decide what the right fix is (since Claude might be able to find the bug I am talking about quickly... but it also might not, there is no guarantee...), then decide whether it is really worth it to let Claude blast out code like this. :)