cmake refactor
diff --git a/.github/workflows/build_arm.yml b/.github/workflows/build_arm.yml
index 3a2daab..a7c7fd1 100644
--- a/.github/workflows/build_arm.yml
+++ b/.github/workflows/build_arm.yml
@@ -46,7 +46,7 @@
         - 'stm32f0 stm32f1 stm32f2 stm32f3'
         - 'stm32f4'
         - 'stm32f7'
-        - 'stm32g4 stm32h7'
+        - 'stm32h7'
         - 'stm32l0 stm32l4 stm32u5 stm32wb'
         - 'tm4c123 xmc4000'
     steps:
diff --git a/.github/workflows/cmake_arm.yml b/.github/workflows/cmake_arm.yml
index 2e74c30..2ca8e32 100644
--- a/.github/workflows/cmake_arm.yml
+++ b/.github/workflows/cmake_arm.yml
@@ -38,6 +38,7 @@
           - 'imxrt'
           - 'rp2040'
           - 'stm32g0'
+          - 'stm32g4'
     steps:
     - name: Setup Python
       uses: actions/setup-python@v4
@@ -75,11 +76,11 @@
     # Upload binaries for hardware test with self-hosted
     - name: Prepare rp2040 Artifacts
       if: contains(matrix.family, 'rp2040') && github.repository_owner == 'hathach'
-      working-directory: ${{github.workspace}}/cmake-build-ci-raspberry_pi_pico
+      working-directory: ${{github.workspace}}/cmake-build/cmake-build-raspberry_pi_pico
       run: |
-        find device/ -name "*.elf" -exec mv {} ../ \;
-        # find host/ -name "*.elf" -exec mv {} ../ \;
-        # find dual/ -name "*.elf" -exec mv {} ../ \;
+        find device/ -name "*.elf" -exec mv {} ../../ \;
+        # find host/ -name "*.elf" -exec mv {} ../../ \;
+        # find dual/ -name "*.elf" -exec mv {} ../../ \;
 
     - name: Upload Artifacts for rp2040
       if: contains(matrix.family,'rp2040') && github.repository_owner == 'hathach'
diff --git a/hw/bsp/family_support.cmake b/hw/bsp/family_support.cmake
index d9b8bbb..fdc9580 100644
--- a/hw/bsp/family_support.cmake
+++ b/hw/bsp/family_support.cmake
@@ -84,8 +84,8 @@
 
 
 function(family_initialize_project PROJECT DIR)
-  # set output suffix to .elf (skip espressif)
-  if(NOT FAMILY STREQUAL "espressif")
+  # set output suffix to .elf (skip espressif and rp2040)
+  if(NOT FAMILY STREQUAL "espressif" AND NOT FAMILY STREQUAL "rp2040")
     set(CMAKE_EXECUTABLE_SUFFIX .elf PARENT_SCOPE)
   endif()
 
@@ -144,6 +144,11 @@
 endfunction()
 
 
+#------------------------------------
+# Main target configure
+#------------------------------------
+
+# Add common configuration to example
 function(family_configure_common TARGET)
   # run size after build
   add_custom_command(TARGET ${TARGET} POST_BUILD
@@ -158,6 +163,66 @@
 endfunction()
 
 
+# configure an executable target to link to tinyusb in device mode, and add the board implementation
+function(family_configure_device_example TARGET)
+  # default implementation is empty, the function should be redefined in the FAMILY/family.cmake
+endfunction()
+
+
+# configure an executable target to link to tinyusb in host mode, and add the board implementation
+function(family_configure_host_example TARGET)
+  # default implementation is empty, the function should be redefined in the FAMILY/family.cmake
+endfunction()
+
+
+# Add tinyusb to example
+function(family_add_tinyusb TARGET OPT_MCU)
+  # tinyusb target is built for each example since it depends on example's tusb_config.h
+  set(TINYUSB_TARGET_PREFIX ${TARGET}-)
+  add_library(${TARGET}-tinyusb_config INTERFACE)
+
+  target_include_directories(${TARGET}-tinyusb_config INTERFACE
+    ${CMAKE_CURRENT_SOURCE_DIR}/src
+    )
+  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
+    CFG_TUSB_MCU=${OPT_MCU}
+    )
+
+  # tinyusb's CMakeList.txt
+  add_subdirectory(${TOP}/src ${CMAKE_CURRENT_BINARY_DIR}/tinyusb)
+endfunction()
+
+
+# Add freeRTOS support to example
+function(family_add_freertos TARGET)
+  # freeros config
+  if (NOT TARGET freertos_config)
+    add_library(freertos_config INTERFACE)
+    target_include_directories(freertos_config SYSTEM INTERFACE
+      ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/${FAMILY}/FreeRTOSConfig
+      )
+  endif()
+
+  # freertos kernel should be generic as freertos_config however, CMAKE complains with missing variable
+  # such as CMAKE_C_COMPILE_OBJECT
+  if (NOT TARGET freertos_kernel)
+    add_subdirectory(${TOP}/lib/FreeRTOS-Kernel ${CMAKE_BINARY_DIR}/lib/freertos_kernel)
+  endif ()
+
+  # Add FreeRTOS option to tinyusb_config
+  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
+    CFG_TUSB_OS=OPT_OS_FREERTOS
+    )
+  # link tinyusb with freeRTOS kernel
+  target_link_libraries(${TARGET}-tinyusb PUBLIC
+    freertos_kernel
+    )
+  target_link_libraries(${TARGET} PUBLIC
+    freertos_kernel
+    )
+endfunction()
+
+
 # Add bin/hex output
 function(family_add_bin_hex TARGET)
   add_custom_command(TARGET ${TARGET} POST_BUILD
@@ -167,6 +232,10 @@
 endfunction()
 
 
+#----------------------------------
+# Flashing target
+#----------------------------------
+
 # Add flash jlink target
 function(family_flash_jlink TARGET)
   if (NOT DEFINED JLINKEXE)
@@ -233,48 +302,7 @@
 endfunction()
 
 
-# configure an executable target to link to tinyusb in device mode, and add the board implementation
-function(family_configure_device_example TARGET)
-  # default implementation is empty, the function should be redefined in the FAMILY/family.cmake
-endfunction()
-
-
-# configure an executable target to link to tinyusb in host mode, and add the board implementation
-function(family_configure_host_example TARGET)
-  # default implementation is empty, the function should be redefined in the FAMILY/family.cmake
-endfunction()
-
-
-# Add freeRTOS support to example, can be overridden by FAMILY/family.cmake
-function(family_add_freertos TARGET)
-  # freeros config
-  if (NOT TARGET freertos_config)
-    add_library(freertos_config INTERFACE)
-    target_include_directories(freertos_config SYSTEM INTERFACE
-      ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/${FAMILY}/FreeRTOSConfig
-      )
-  endif()
-
-  # freertos kernel should be generic as freertos_config however, CMAKE complains with missing variable
-  # such as CMAKE_C_COMPILE_OBJECT
-  if (NOT TARGET freertos_kernel)
-    add_subdirectory(${TOP}/lib/FreeRTOS-Kernel ${CMAKE_BINARY_DIR}/lib/freertos_kernel)
-  endif ()
-
-  # Add FreeRTOS option to tinyusb_config
-  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
-    CFG_TUSB_OS=OPT_OS_FREERTOS
-    )
-  # link tinyusb with freeRTOS kernel
-  target_link_libraries(${TARGET}-tinyusb PUBLIC
-    freertos_kernel
-    )
-  target_link_libraries(${TARGET} PUBLIC
-    freertos_kernel
-    )
-endfunction()
-
-
+# family specific: can override above functions
 include(${CMAKE_CURRENT_LIST_DIR}/${FAMILY}/family.cmake)
 
 if (NOT FAMILY_MCUS)
diff --git a/hw/bsp/imxrt/family.cmake b/hw/bsp/imxrt/family.cmake
index 49a4a92..3c15628 100644
--- a/hw/bsp/imxrt/family.cmake
+++ b/hw/bsp/imxrt/family.cmake
@@ -36,7 +36,6 @@
     ${SDK_DIR}/devices/${MCU_VARIANT}/drivers/fsl_clock.c
     )
   target_compile_definitions(${BOARD_TARGET} PUBLIC
-    CFG_TUSB_MCU=OPT_MCU_MIMXRT
     __ARMVFP__=0
     __ARMFPV5__=0
     XIP_EXTERNAL_FLASH=1
@@ -100,33 +99,16 @@
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
     )
 
-  #---------- TinyUSB ----------
-  # tinyusb target is built for each example since it depends on example's tusb_config.h
-  set(TINYUSB_TARGET_PREFIX ${TARGET}-)
-  add_library(${TARGET}-tinyusb_config INTERFACE)
-
-  target_include_directories(${TARGET}-tinyusb_config INTERFACE
-    ${CMAKE_CURRENT_SOURCE_DIR}/src
-    )
-  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
-    CFG_TUSB_MCU=OPT_MCU_MIMXRT
-    )
-
-  # tinyusb's CMakeList.txt
-  add_subdirectory(${TOP}/src ${CMAKE_CURRENT_BINARY_DIR}/tinyusb)
+  # Add TinyUSB
+  family_add_tinyusb(${TARGET} OPT_MCU_MIMXRT)
 
   # Link dependencies
   target_link_libraries(${TARGET} PUBLIC ${BOARD_TARGET} ${TARGET}-tinyusb)
 
-  # group target (not yet supported by clion)
-  set_target_properties(${TARGET}-tinyusb ${TARGET}-tinyusb_config
-    PROPERTIES FOLDER ${TARGET}_sub
-    )
-
-  #---------- Flash ----------
+  # Flashing
   family_flash_jlink(${TARGET})
   family_flash_nxplink(${TARGET})
-  family_flash_pyocd(${TARGET})
+  #family_flash_pyocd(${TARGET})
 endfunction()
 
 
diff --git a/hw/bsp/lpc18/family.cmake b/hw/bsp/lpc18/family.cmake
index 515348e..a133e72 100644
--- a/hw/bsp/lpc18/family.cmake
+++ b/hw/bsp/lpc18/family.cmake
@@ -83,30 +83,13 @@
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
     )
 
-  #---------- TinyUSB ----------
-  # tinyusb target is built for each example since it depends on example's tusb_config.h
-  set(TINYUSB_TARGET_PREFIX ${TARGET}-)
-  add_library(${TARGET}-tinyusb_config INTERFACE)
-
-  target_include_directories(${TARGET}-tinyusb_config INTERFACE
-    ${CMAKE_CURRENT_SOURCE_DIR}/src
-    )
-  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
-    CFG_TUSB_MCU=OPT_MCU_LPC18XX
-    )
-
-  # tinyusb's CMakeList.txt
-  add_subdirectory(${TOP}/src ${CMAKE_CURRENT_BINARY_DIR}/tinyusb)
+  # Add TinyUSB
+  family_add_tinyusb(${TARGET} OPT_MCU_LPC18XX)
 
   # Link dependencies
   target_link_libraries(${TARGET} PUBLIC ${BOARD_TARGET} ${TARGET}-tinyusb)
 
-  # group target (not yet supported by clion)
-  set_target_properties(${TARGET}-tinyusb ${TARGET}-tinyusb_config
-    PROPERTIES FOLDER ${TARGET}_sub
-    )
-
-  #---------- Flash ----------
+  # Flashing
   family_flash_jlink(${TARGET})
 endfunction()
 
diff --git a/hw/bsp/lpc55/family.cmake b/hw/bsp/lpc55/family.cmake
index eb163bd..25eed48 100644
--- a/hw/bsp/lpc55/family.cmake
+++ b/hw/bsp/lpc55/family.cmake
@@ -109,33 +109,16 @@
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
     )
 
-  #---------- TinyUSB ----------
-  # tinyusb target is built for each example since it depends on example's tusb_config.h
-  set(TINYUSB_TARGET_PREFIX ${TARGET}-)
-  add_library(${TARGET}-tinyusb_config INTERFACE)
-
-  target_include_directories(${TARGET}-tinyusb_config INTERFACE
-    ${CMAKE_CURRENT_SOURCE_DIR}/src
-    )
-  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
-    CFG_TUSB_MCU=OPT_MCU_LPC55XX
-    )
-
-  # tinyusb's CMakeList.txt
-  add_subdirectory(${TOP}/src ${CMAKE_CURRENT_BINARY_DIR}/tinyusb)
+  # Add TinyUSB
+  family_add_tinyusb(${TARGET} OPT_MCU_LPC55XX)
 
   # Link dependencies
   target_link_libraries(${TARGET} PUBLIC ${BOARD_TARGET} ${TARGET}-tinyusb)
 
-  # group target (not yet supported by clion)
-  set_target_properties(${TARGET}-tinyusb ${TARGET}-tinyusb_config
-    PROPERTIES FOLDER ${TARGET}_sub
-    )
-
-  #---------- Flash ----------
+  # Flashing
   family_flash_jlink(${TARGET})
   family_flash_nxplink(${TARGET})
-  family_flash_pyocd(${TARGET})
+  #family_flash_pyocd(${TARGET})
 endfunction()
 
 
diff --git a/hw/bsp/mcx/family.cmake b/hw/bsp/mcx/family.cmake
index d5a17f5..17f54a5 100644
--- a/hw/bsp/mcx/family.cmake
+++ b/hw/bsp/mcx/family.cmake
@@ -91,30 +91,13 @@
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
     )
 
-  #---------- TinyUSB ----------
-  # tinyusb target is built for each example since it depends on example's tusb_config.h
-  set(TINYUSB_TARGET_PREFIX ${TARGET}-)
-  add_library(${TARGET}-tinyusb_config INTERFACE)
-
-  target_include_directories(${TARGET}-tinyusb_config INTERFACE
-    ${CMAKE_CURRENT_SOURCE_DIR}/src
-    )
-  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
-    CFG_TUSB_MCU=OPT_MCU_MCXN9
-    )
-
-  # tinyusb's CMakeList.txt
-  add_subdirectory(${TOP}/src ${CMAKE_CURRENT_BINARY_DIR}/tinyusb)
+  # Add TinyUSB
+  family_add_tinyusb(${TARGET} OPT_MCU_MCXN9)
 
   # Link dependencies
   target_link_libraries(${TARGET} PUBLIC ${BOARD_TARGET} ${TARGET}-tinyusb)
 
-  # group target (not yet supported by clion)
-  set_target_properties(${TARGET}-tinyusb ${TARGET}-tinyusb_config
-    PROPERTIES FOLDER ${TARGET}_sub
-    )
-
-  #---------- Flash ----------
+  # Flashing
   family_flash_jlink(${TARGET})
   #family_flash_nxplink(${TARGET})
   #family_flash_pyocd(${TARGET})
diff --git a/hw/bsp/nrf/family.cmake b/hw/bsp/nrf/family.cmake
index 71067c8..2c5d7a4 100644
--- a/hw/bsp/nrf/family.cmake
+++ b/hw/bsp/nrf/family.cmake
@@ -98,30 +98,13 @@
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
     )
 
-  #---------- TinyUSB ----------
-  # tinyusb target is built for each example since it depends on example's tusb_config.h
-  set(TINYUSB_TARGET_PREFIX ${TARGET}-)
-  add_library(${TARGET}-tinyusb_config INTERFACE)
-
-  target_include_directories(${TARGET}-tinyusb_config INTERFACE
-    ${CMAKE_CURRENT_SOURCE_DIR}/src
-    )
-  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
-    CFG_TUSB_MCU=OPT_MCU_NRF5X
-    )
-
-  # tinyusb's CMakeList.txt
-  add_subdirectory(${TOP}/src ${CMAKE_CURRENT_BINARY_DIR}/tinyusb)
+  # Add TinyUSB
+  family_add_tinyusb(${TARGET} OPT_MCU_NRF5X)
 
   # Link dependencies
   target_link_libraries(${TARGET} PUBLIC ${BOARD_TARGET} ${TARGET}-tinyusb)
 
-  # group target (not yet supported by clion)
-  set_target_properties(${TARGET}-tinyusb ${TARGET}-tinyusb_config
-    PROPERTIES FOLDER ${TARGET}_sub
-    )
-
-  #---------- Flash ----------
+  # Flashing
   family_flash_jlink(${TARGET})
 endfunction()
 
diff --git a/hw/bsp/stm32g0/family.cmake b/hw/bsp/stm32g0/family.cmake
index 9674bc7..184dc60 100644
--- a/hw/bsp/stm32g0/family.cmake
+++ b/hw/bsp/stm32g0/family.cmake
@@ -96,30 +96,13 @@
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
     )
 
-  #---------- TinyUSB ----------
-  # tinyusb target is built for each example since it depends on example's tusb_config.h
-  set(TINYUSB_TARGET_PREFIX ${TARGET}-)
-  add_library(${TARGET}-tinyusb_config INTERFACE)
-
-  target_include_directories(${TARGET}-tinyusb_config INTERFACE
-    ${CMAKE_CURRENT_SOURCE_DIR}/src
-    )
-  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
-    CFG_TUSB_MCU=OPT_MCU_STM32G0
-    )
-
-  # tinyusb's CMakeList.txt
-  add_subdirectory(${TOP}/src ${CMAKE_CURRENT_BINARY_DIR}/tinyusb)
+  # Add TinyUSB
+  family_add_tinyusb(${TARGET} OPT_MCU_STM32G0)
 
   # Link dependencies
   target_link_libraries(${TARGET} PUBLIC ${BOARD_TARGET} ${TARGET}-tinyusb)
 
-  # group target (not yet supported by clion)
-  set_target_properties(${TARGET}-tinyusb ${TARGET}-tinyusb_config
-    PROPERTIES FOLDER ${TARGET}_sub
-    )
-
-  #---------- Flash ----------
+  # Flashing
   family_flash_stlink(${TARGET})
   #family_flash_jlink(${TARGET})
 endfunction()
diff --git a/hw/bsp/stm32g4/family.cmake b/hw/bsp/stm32g4/family.cmake
index 1de6fd6..5578a9b 100644
--- a/hw/bsp/stm32g4/family.cmake
+++ b/hw/bsp/stm32g4/family.cmake
@@ -12,7 +12,7 @@
 set(CMSIS_5 ${TOP}/lib/CMSIS_5)
 
 # enable LTO
-#set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
+set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
 
 # toolchain set up
 set(CMAKE_SYSTEM_PROCESSOR cortex-m4 CACHE INTERNAL "System Processor")
@@ -96,30 +96,13 @@
     ${CMAKE_CURRENT_FUNCTION_LIST_DIR}/boards/${BOARD}
     )
 
-  #---------- TinyUSB ----------
-  # tinyusb target is built for each example since it depends on example's tusb_config.h
-  set(TINYUSB_TARGET_PREFIX ${TARGET}-)
-  add_library(${TARGET}-tinyusb_config INTERFACE)
-
-  target_include_directories(${TARGET}-tinyusb_config INTERFACE
-    ${CMAKE_CURRENT_SOURCE_DIR}/src
-    )
-  target_compile_definitions(${TARGET}-tinyusb_config INTERFACE
-    CFG_TUSB_MCU=OPT_MCU_STM32G4
-    )
-
-  # tinyusb's CMakeList.txt
-  add_subdirectory(${TOP}/src ${CMAKE_CURRENT_BINARY_DIR}/tinyusb)
+  # Add TinyUSB
+  family_add_tinyusb(${TARGET} OPT_MCU_STM32G4)
 
   # Link dependencies
   target_link_libraries(${TARGET} PUBLIC ${BOARD_TARGET} ${TARGET}-tinyusb)
 
-  # group target (not yet supported by clion)
-  set_target_properties(${TARGET}-tinyusb ${TARGET}-tinyusb_config
-    PROPERTIES FOLDER ${TARGET}_sub
-    )
-
-  #---------- Flash ----------
+  # Flashing
   family_flash_stlink(${TARGET})
   #family_flash_jlink(${TARGET})
 endfunction()
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 396737e..ad9fbc7 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -96,3 +96,7 @@
 target_link_libraries(${TINYUSB_TARGET} PUBLIC
   ${TINYUSB_CONFIG_TARGET}
   )
+
+# export target name
+# set(TINYUSB_TARGET ${TINYUSB_TARGET} PARENT_SCOPE)
+# set(TINYUSB_CONFIG_TARGET ${TINYUSB_CONFIG_TARGET} PARENT_SCOPE)
diff --git a/tools/build_cmake.py b/tools/build_cmake.py
index 88d27df..d857262 100644
--- a/tools/build_cmake.py
+++ b/tools/build_cmake.py
@@ -34,13 +34,15 @@
     for board in all_boards:
         start_time = time.monotonic()
 
+        build_dir = f"cmake-build/cmake-build-{board}"
+
         # Generate build
-        r = subprocess.run(f"cmake examples -B cmake-build-ci-{board} -G \"Ninja\" -DFAMILY={family} -DBOARD"
+        r = subprocess.run(f"cmake examples -B {build_dir} -G \"Ninja\" -DFAMILY={family} -DBOARD"
                            f"={board}", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
 
         # Build
         if r.returncode == 0:
-            r = subprocess.run(f"cmake --build cmake-build-ci-{board}", shell=True, stdout=subprocess.PIPE,
+            r = subprocess.run(f"cmake --build {build_dir}", shell=True, stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
 
         duration = time.monotonic() - start_time
@@ -71,10 +73,10 @@
     if make_iar_option not in sys.argv:
         make_iar_option = ''
 
-    # If family are not specified in arguments, build all
+    # If family are not specified in arguments, build all supported
     all_families = []
     for entry in os.scandir("hw/bsp"):
-        if entry.is_dir() and os.path.isdir(entry.path + "/boards") and entry.name != 'espressif':
+        if entry.is_dir() and entry.name != 'espressif' and os.path.isfile(entry.path + "/family.cmake"):
             all_families.append(entry.name)
     filter_with_input(all_families)
     all_families.sort()