Przeglądaj źródła

Merge pull request #600 from hathach/refactor-bsp

Refactor bsp
Ha Thach 5 lat temu
rodzic
commit
469ca2f155

+ 70 - 24
.github/workflows/build.yml

@@ -20,8 +20,50 @@ jobs:
         cd test
         ceedling test:all
 
-  # Build most of the ports
-  build:
+  # build all example for each family
+  build-family:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        family:
+        - 'imxrt'
+        - 'nrf'
+        #- 'rp2040'
+        - 'samd21'
+        - 'samd51'
+        - 'stm32f4'
+        - 'stm32f7'
+    steps:
+    - name: Setup Python
+      uses: actions/setup-python@v2
+
+    - name: Setup Node.js
+      uses: actions/setup-node@v1
+
+    - name: Install Toolchains
+      run: |
+        # ARM GCC from xpack
+        npm install --global xpm
+        xpm install --global @xpack-dev-tools/arm-none-eabi-gcc@latest
+        echo `echo $HOME/opt/xPacks/@xpack-dev-tools/arm-none-eabi-gcc/*/.content/bin` >> $GITHUB_PATH
+
+    - name: Checkout TinyUSB
+      uses: actions/checkout@v2
+      with:
+        submodules: 'true'
+
+    - name: Checkout Sub-Submodules
+      run: |
+        # some submodule has it own submodules that need to be fetched as well
+        git submodule update --init --recursive hw/mcu/microchip
+        git submodule update --init --recursive lib/FreeRTOS
+
+    - name: Build
+      run: python3 tools/build_family.py ${{ matrix.family }}
+
+  # Build all no-family (opharned) boards
+  build-board:
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
@@ -40,28 +82,36 @@ jobs:
         - 'device/midi_test'
         - 'device/msc_dual_lun'
         - 'device/net_lwip_webserver'
+        - 'device/uac2_headset'
         - 'device/usbtmc'
         - 'device/webusb_serial'
         - 'host/cdc_msc_hid'
 
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v1
+      uses: actions/setup-python@v2
 
     - name: Setup Node.js
       uses: actions/setup-node@v1
 
     - name: Cache MSP430 Toolchain
+      uses: actions/cache@v2
       id: cache-msp430
-      uses: actions/cache@v1
       with:
         path: /tmp/dl/
         # Increment gcc version number at end when updating downloads
-        key: msp430-${{ runner.os }}-9.2.0.50
+        key: ${{ runner.os }}-msp430-9.2.0.50
 
-    - name: Install Toolchains
+    - name: Install MSP430 Toolchain
+      if: steps.cache-msp430.outputs.cache-hit != 'true'
       env:
-        MSP430GCC_URL: http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/9_2_0_0/export/msp430-gcc-9.2.0.50_linux64.tar.bz2
+        MSP430GCC_URL: http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/9_2_0_0/export/msp430-gcc-9.2.0.50_linux64.tar.bz2      
+      run: |
+        mkdir -p /tmp/dl/
+        [ -f "/tmp/dl/msp430-gcc.tar.bz2" ] || wget --progress=dot:mega $MSP430GCC_URL -O /tmp/dl/msp430-gcc.tar.bz2
+        tar -C $HOME -xaf /tmp/dl/msp430-gcc.tar.bz2
+
+    - name: Install Toolchains
       run: |
         # ARM & RISC-V GCC from xpack
         npm install --global xpm
@@ -71,39 +121,35 @@ jobs:
         echo `echo $HOME/opt/xPacks/@xpack-dev-tools/riscv-none-embed-gcc/*/.content/bin` >> $GITHUB_PATH
 
         # TI MSP430 GCC
-        mkdir -p /tmp/dl/
-        [ -f "/tmp/dl/msp430-gcc.tar.bz2" ] || wget --progress=dot:mega $MSP430GCC_URL -O /tmp/dl/msp430-gcc.tar.bz2
-        tar -C $HOME -xaf /tmp/dl/msp430-gcc.tar.bz2
         echo `echo $HOME/msp430-gcc-*_linux64/bin` >> $GITHUB_PATH
 
     - name: Checkout TinyUSB
       uses: actions/checkout@v2
       with:
-        # Cannot do submodule checkout here since LWIP's git server cannot checkout unadventised commits (it must use tags)
-        submodules: 'false'
+        submodules: 'true'
 
-    - name: Checkout Submodules
+    - name: Checkout Sub-Submodules
       run: |
-        git submodule sync --recursive
-        # Special case LWIP since GNU's Savannah can't do shallow checkout of non-tagged commits
-        git submodule update --init --recursive lib/lwip
-        git submodule update --init --recursive --depth 1
+        # some submodule has it own submodules that need to be fetched as well
+        git submodule update --init --recursive hw/mcu/microchip
+        git submodule update --init --recursive lib/FreeRTOS
 
     - name: Build
-      run: |
-        python3 tools/build_all.py ${{ matrix.example }}
+      run: python3 tools/build_board.py ${{ matrix.example }}
 
-  # Build ESP32S
-  build-esp32s:
+  # Build ESP32S2
+  build-esp32s2:
     runs-on: ubuntu-latest
     strategy:
       fail-fast: false
       matrix:
-        example: ['board_test', 'cdc_msc_freertos', 'hid_composite_freertos']
+        board:
+        - 'espressif_kaluga_1'
+        - 'espressif_saola_1'
 
     steps:
     - name: Setup Python
-      uses: actions/setup-python@v1
+      uses: actions/setup-python@v2
 
     - name: Pull ESP-IDF docker
       run: docker pull espressif/idf:latest
@@ -114,4 +160,4 @@ jobs:
         submodules: 'false'
 
     - name: Build
-      run: docker run --rm -v $PWD:/project -w /project espressif/idf:latest python3 tools/build_esp32s.py ${{ matrix.example }}
+      run: docker run --rm -v $PWD:/project -w /project espressif/idf:latest python3 tools/build_esp32s2.py ${{ matrix.board }}

+ 6 - 1
.gitmodules

@@ -21,7 +21,7 @@
 	url = https://github.com/majbthrd/nuc_driver.git
 [submodule "lib/lwip"]
 	path = lib/lwip
-	url = https://git.savannah.nongnu.org/git/lwip.git
+	url = https://github.com/lwip-tcpip/lwip.git
 [submodule "lib/FreeRTOS"]
 	path = lib/FreeRTOS
 	url = https://github.com/FreeRTOS/FreeRTOS.git
@@ -112,3 +112,8 @@
 [submodule "lib/sct_neopixel"]
 	path = lib/sct_neopixel
 	url = https://github.com/gsteiert/sct_neopixel
+[submodule "hw/mcu/raspberrypi/pico-sdk"]
+	path = hw/mcu/raspberrypi/pico-sdk
+	url = https://github.com/raspberrypi/pico-sdk.git
+	fetchRecurseSubmodules = false
+

+ 4 - 2
README.md

@@ -39,6 +39,7 @@ The stack supports the following MCUs:
   - iMX RT Series: RT1011, RT1015, RT1021, RT1052, RT1062, RT1064
   - Kinetis: KL25
   - LPC Series: 11Uxx, 13xx, 175x_6x, 177x_8x, 18xx, 40xx, 43xx, 51Uxx, 54xxx, 55xx
+- **Raspberry Pi:** RP2040
 - **Sony:** CXD56
 - **ST:** STM32 series: L0, F0, F1, F2, F3, F4, F7, H7 both FullSpeed and HighSpeed
 - **TI:** MSP430
@@ -98,8 +99,9 @@ TinyUSB is currently used by these other projects:
 - [Adafruit nRF52 Bootloader](https://github.com/adafruit/Adafruit_nRF52_Bootloader)
 - [Adafruit SAMD Arduino](https://github.com/adafruit/ArduinoCore-samd)
 - [CircuitPython](https://github.com/adafruit/circuitpython)
+- [Espressif IDF](https://github.com/espressif/esp-idf)
 - [MicroPython](https://github.com/micropython/micropython)
 - [mynewt](https://mynewt.apache.org)
+- [Raspberry pico-sdk](https://github.com/raspberrypi/pico-sdk)
+- [TinyUF2 Bootloader](https://github.com/adafruit/tinyuf2)
 - [TinyUSB Arduino Library](https://github.com/adafruit/Adafruit_TinyUSB_Arduino)
-
-Let me know if your project also uses TinyUSB and want to share.

+ 4 - 0
docs/boards.md

@@ -88,6 +88,10 @@ This code base already had supported for a handful of following boards (sorted a
 - [NGX LPC4330-Xplorer](https://www.nxp.com/design/designs/lpc4330-xplorer-board:OM13027)
 - [Double M33 Express](https://www.crowdsupply.com/steiert-solutions/double-m33-express)
 
+### Raspberry PI RP2040
+
+- [Raspberry PI PICO](https://www.raspberrypi.org/products/raspberry-pi-pico/)
+
 ### Sony
 
 - [Sony Spresense CXD5602](https://developer.sony.com/develop/spresense)

+ 57 - 7
examples/device/board_test/CMakeLists.txt

@@ -1,15 +1,65 @@
-# The following five lines of boilerplate have to be in your project's
-# CMakeLists in this exact order for cmake to work correctly
 cmake_minimum_required(VERSION 3.5)
 
+# use directory name for project id
+get_filename_component(PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME)
+
 # TOP is absolute path to root directory of TinyUSB git repo
 set(TOP "../../..")
 get_filename_component(TOP "${TOP}" REALPATH)
 
-# Add example src and bsp directories
-set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s2/boards" "${TOP}/hw/bsp/esp32s2/components")
+# Check for -DFAMILY=
+if(NOT DEFINED FAMILY)
+  message(FATAL_ERROR "Invalid FAMILY specified")
+endif()
+
+include(${TOP}/hw/bsp/${FAMILY}/family.cmake)
+project(${PROJECT})
+
+if(FAMILY STREQUAL "rp2040")
+
+pico_sdk_init()
+
+add_executable(${PROJECT})
+
+# TinyUSB Stack source
+set(SRC_TINYUSB
+	${TOP}/src/tusb.c
+	${TOP}/src/common/tusb_fifo.c
+	${TOP}/src/device/usbd.c
+	${TOP}/src/device/usbd_control.c
+	${TOP}/src/class/audio/audio_device.c
+	${TOP}/src/class/cdc/cdc_device.c
+	${TOP}/src/class/dfu/dfu_rt_device.c
+	${TOP}/src/class/hid/hid_device.c
+	${TOP}/src/class/midi/midi_device.c
+	${TOP}/src/class/msc/msc_device.c
+	${TOP}/src/class/net/net_device.c
+	${TOP}/src/class/usbtmc/usbtmc_device.c
+	${TOP}/src/class/vendor/vendor_device.c
+	${TOP}/src/portable/raspberrypi/${FAMILY}/dcd_rp2040.c
+	${TOP}/src/portable/raspberrypi/${FAMILY}/rp2040_usb.c
+)
+
+target_sources(${PROJECT} PUBLIC
+  src/main.c
+  ${TOP}/hw/bsp/${FAMILY}/family.c
+  ${SRC_TINYUSB}
+)
+
+target_include_directories(${PROJECT} PUBLIC
+  src/
+  ${TOP}/hw
+  ${TOP}/src
+  ${TOP}/hw/bsp/${FAMILY}/boards/${BOARD}
+)
+
+target_compile_definitions(${PROJECT} PUBLIC
+  CFG_TUSB_MCU=OPT_MCU_RP2040
+  CFG_TUSB_OS=OPT_OS_PICO
+)
+
+target_link_libraries(${PROJECT} pico_stdlib)
 
-include($ENV{IDF_PATH}/tools/cmake/project.cmake)
-set(SUPPORTED_TARGETS esp32s2)
+pico_add_extra_outputs(${PROJECT})
 
-project(board_test)
+endif()

+ 72 - 0
examples/device/cdc_msc/CMakeLists.txt

@@ -0,0 +1,72 @@
+cmake_minimum_required(VERSION 3.5)
+
+# use directory name for project id
+get_filename_component(PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME)
+
+# TOP is absolute path to root directory of TinyUSB git repo
+set(TOP "../../..")
+get_filename_component(TOP "${TOP}" REALPATH)
+
+# Check for -DFAMILY=
+if(NOT DEFINED FAMILY)
+  message(FATAL_ERROR "Invalid FAMILY specified")
+endif()
+
+include(${TOP}/hw/bsp/${FAMILY}/family.cmake)
+project(${PROJECT})
+
+if(FAMILY STREQUAL "rp2040")
+
+pico_sdk_init()
+
+add_executable(${PROJECT})
+
+# TinyUSB Stack source
+set(SRC_TINYUSB
+	${TOP}/src/tusb.c
+	${TOP}/src/common/tusb_fifo.c
+	${TOP}/src/device/usbd.c
+	${TOP}/src/device/usbd_control.c
+	${TOP}/src/class/audio/audio_device.c
+	${TOP}/src/class/cdc/cdc_device.c
+	${TOP}/src/class/dfu/dfu_rt_device.c
+	${TOP}/src/class/hid/hid_device.c
+	${TOP}/src/class/midi/midi_device.c
+	${TOP}/src/class/msc/msc_device.c
+	${TOP}/src/class/net/net_device.c
+	${TOP}/src/class/usbtmc/usbtmc_device.c
+	${TOP}/src/class/vendor/vendor_device.c
+	${TOP}/src/portable/raspberrypi/${FAMILY}/dcd_rp2040.c
+	${TOP}/src/portable/raspberrypi/${FAMILY}/rp2040_usb.c
+)
+
+# Example source
+set(SRC_EXAMPLE
+  src/main.c
+  src/msc_disk.c
+  src/usb_descriptors.c
+)
+
+target_sources(${PROJECT} PUBLIC
+  ${SRC_EXAMPLE}
+  ${TOP}/hw/bsp/${FAMILY}/family.c
+  ${SRC_TINYUSB}
+)
+
+target_include_directories(${PROJECT} PUBLIC
+  src/
+  ${TOP}/hw
+  ${TOP}/src
+  ${TOP}/hw/bsp/${FAMILY}/boards/${BOARD}
+)
+
+target_compile_definitions(${PROJECT} PUBLIC
+  CFG_TUSB_MCU=OPT_MCU_RP2040
+  CFG_TUSB_OS=OPT_OS_PICO
+)
+
+target_link_libraries(${PROJECT} pico_stdlib)
+
+pico_add_extra_outputs(${PROJECT})
+
+endif()

+ 2 - 0
examples/device/cdc_msc/src/tusb_config.h

@@ -65,7 +65,9 @@
 #endif
 
 // This example doesn't use an RTOS
+#ifndef CFG_TUSB_OS
 #define CFG_TUSB_OS               OPT_OS_NONE
+#endif
 
 // CFG_TUSB_DEBUG is defined by compiler in DEBUG build
 // #define CFG_TUSB_DEBUG           0

+ 9 - 6
examples/device/cdc_msc_freertos/CMakeLists.txt

@@ -1,15 +1,18 @@
-# The following five lines of boilerplate have to be in your project's
-# CMakeLists in this exact order for cmake to work correctly
 cmake_minimum_required(VERSION 3.5)
 
+# use directory name for project id
+get_filename_component(PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME)
+
 # TOP is absolute path to root directory of TinyUSB git repo
 set(TOP "../../..")
 get_filename_component(TOP "${TOP}" REALPATH)
 
-# Add example src and bsp directories
-set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s2/boards" "${TOP}/hw/bsp/esp32s2/components")
+# Check for -DFAMILY=
+if(NOT DEFINED FAMILY)
+  message(FATAL_ERROR "Invalid FAMILY specified")
+endif()
 
-include($ENV{IDF_PATH}/tools/cmake/project.cmake)
-set(SUPPORTED_TARGETS esp32s2)
+include(${TOP}/hw/bsp/${FAMILY}/family.cmake)
+project(${PROJECT})
 
 project(cdc_msc_freertos)

+ 9 - 6
examples/device/hid_composite_freertos/CMakeLists.txt

@@ -1,15 +1,18 @@
-# The following five lines of boilerplate have to be in your project's
-# CMakeLists in this exact order for cmake to work correctly
 cmake_minimum_required(VERSION 3.5)
 
+# use directory name for project id
+get_filename_component(PROJECT ${CMAKE_CURRENT_SOURCE_DIR} NAME)
+
 # TOP is absolute path to root directory of TinyUSB git repo
 set(TOP "../../..")
 get_filename_component(TOP "${TOP}" REALPATH)
 
-# Add example src and bsp directories
-set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s2/boards" "${TOP}/hw/bsp/esp32s2/components")
+# Check for -DFAMILY=
+if(NOT DEFINED FAMILY)
+  message(FATAL_ERROR "Invalid FAMILY specified")
+endif()
 
-include($ENV{IDF_PATH}/tools/cmake/project.cmake)
-set(SUPPORTED_TARGETS esp32s2)
+include(${TOP}/hw/bsp/${FAMILY}/family.cmake)
+project(${PROJECT})
 
 project(hid_composite_freertos)

+ 0 - 0
examples/device/uac2_headset/.skip.MCU_SAMD11


+ 0 - 0
examples/device/uac2_headset/.skip.MCU_SAME5X


+ 0 - 0
examples/device/uac2_headset/.skip.MCU_SAMG


+ 1 - 1
examples/make.mk

@@ -40,7 +40,7 @@ ifeq ($(FAMILY),)
   include $(TOP)/hw/bsp/$(BOARD)/board.mk
 else
   # Include Family and Board specific defs
-  include $(TOP)/$(FAMILY_PATH)/family.mk
+  -include $(TOP)/$(FAMILY_PATH)/family.mk
 
   SRC_C += $(subst $(TOP)/,,$(wildcard $(TOP)/$(FAMILY_PATH)/*.c))
 endif

+ 20 - 8
examples/rules.mk

@@ -2,34 +2,46 @@
 # Common make rules for all examples
 # ---------------------------------------
 
-ifeq ($(CROSS_COMPILE),xtensa-esp32s2-elf-)
+ifeq ($(FAMILY),esp32s2)
 # Espressif IDF use CMake build system, this add wrapper target to call idf.py
 
 .PHONY: all clean flash
 .DEFAULT_GOAL := all
 
 all:
-	idf.py -B$(BUILD) -DBOARD=$(BOARD) build
+	idf.py -B$(BUILD) -DFAMILY=$(FAMILY) -DBOARD=$(BOARD) build
 
 build: all
 
 clean:
-	idf.py -B$(BUILD) -DBOARD=$(BOARD) clean
+	idf.py -B$(BUILD) -DFAMILY=$(FAMILY) -DBOARD=$(BOARD) clean
+
+fullclean:
+	idf.py -B$(BUILD) -DFAMILY=$(FAMILY) -DBOARD=$(BOARD) fullclean
 
 flash:
-	idf.py -B$(BUILD) -DBOARD=$(BOARD) flash
+	idf.py -B$(BUILD) -DFAMILY=$(FAMILY) -DBOARD=$(BOARD) flash
 
 bootloader-flash:
-	idf.py -B$(BUILD) -DBOARD=$(BOARD) bootloader-flash
+	idf.py -B$(BUILD) -DFAMILY=$(FAMILY) -DBOARD=$(BOARD) bootloader-flash
 
 app-flash:
-	idf.py -B$(BUILD) -DBOARD=$(BOARD) app-flash
+	idf.py -B$(BUILD) -DFAMILY=$(FAMILY) -DBOARD=$(BOARD) app-flash
 
 erase:
-	idf.py -B$(BUILD) -DBOARD=$(BOARD) erase_flash
+	idf.py -B$(BUILD) -DFAMILY=$(FAMILY) -DBOARD=$(BOARD) erase_flash
 
 monitor:
-	idf.py -B$(BUILD) -DBOARD=$(BOARD) monitor
+	idf.py -B$(BUILD) -DFAMILY=$(FAMILY) -DBOARD=$(BOARD) monitor
+
+else ifeq ($(FAMILY),rp2040)
+
+all:
+	[ -d $(BUILD) ] || cmake -S . -B $(BUILD) -DFAMILY=$(FAMILY) -DBOARD=$(BOARD) -DPICO_BUILD_DOCS=0
+	$(MAKE) -C $(BUILD)
+
+clean:
+	$(RM) -rf $(BUILD)
 
 else
 # GNU Make build system

+ 0 - 0
hw/bsp/esp32s2/boards/esp32s2_kaluga_1/board.h → hw/bsp/esp32s2/boards/espressif_kaluga_1/board.h


+ 0 - 0
hw/bsp/esp32s2/boards/esp32s2_saola_1/board.h → hw/bsp/esp32s2/boards/espressif_saola_1/board.h


+ 6 - 0
hw/bsp/esp32s2/family.cmake

@@ -0,0 +1,6 @@
+cmake_minimum_required(VERSION 3.5)
+
+# Add example src and bsp directories
+set(EXTRA_COMPONENT_DIRS "src" "${TOP}/hw/bsp/esp32s2/boards" "${TOP}/hw/bsp/esp32s2/components")  
+include($ENV{IDF_PATH}/tools/cmake/project.cmake)
+set(SUPPORTED_TARGETS esp32s2)

+ 0 - 2
hw/bsp/esp32s2/family.mk

@@ -1,2 +0,0 @@
-# Cross Compiler for ESP32
-CROSS_COMPILE = xtensa-esp32s2-elf-

+ 0 - 99
hw/bsp/raspberry_pi_pico/board_raspberry_pi_pico.c

@@ -1,99 +0,0 @@
-/*
- * The MIT License (MIT)
- *
- * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * This file is part of the TinyUSB stack.
- */
-
-#include "pico/stdlib.h"
-#include "../board.h"
-
-#ifndef LED_PIN
-#define LED_PIN PICO_DEFAULT_LED_PIN
-#endif
-
-void board_init(void)
-{
-    setup_default_uart();
-    gpio_init(LED_PIN);
-    gpio_set_dir(LED_PIN, 1);
-
-    // Button
-
-    // todo probably set up device mode?
-
-#if TUSB_OPT_HOST_ENABLED
-    // set portfunc to host !!!
-#endif
-}
-
-////--------------------------------------------------------------------+
-//// USB Interrupt Handler
-////--------------------------------------------------------------------+
-//void USB_IRQHandler(void)
-//{
-//#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_HOST
-//    tuh_isr(0);
-//#endif
-//
-//#if CFG_TUSB_RHPORT0_MODE & OPT_MODE_DEVICE
-//    tud_int_handler(0);
-//#endif
-//}
-
-//--------------------------------------------------------------------+
-// Board porting API
-//--------------------------------------------------------------------+
-
-void board_led_write(bool state)
-{
-    gpio_put(LED_PIN, state);
-}
-
-uint32_t board_button_read(void)
-{
-    return 0;
-}
-
-int board_uart_read(uint8_t* buf, int len)
-{
-    for(int i=0;i<len;i++) {
-        buf[i] = uart_getc(uart_default);
-    }
-    return 0;
-}
-
-int board_uart_write(void const * buf, int len)
-{
-//  UART_Send(BOARD_UART_PORT, &c, 1, BLOCKING);
-    for(int i=0;i<len;i++) {
-        uart_putc(uart_default, ((char *)buf)[i]);
-    }
-    return 0;
-}
-
-#if CFG_TUSB_OS == OPT_OS_NONE
-uint32_t board_millis(void)
-{
-    return to_ms_since_boot(get_absolute_time());
-}
-#endif

+ 46 - 0
hw/bsp/rp2040/boards/raspberry_pi_pico/board.h

@@ -0,0 +1,46 @@
+/* 
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#ifndef BOARD_H_
+#define BOARD_H_
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#define LED_PIN               PICO_DEFAULT_LED_PIN
+#define LED_STATE_ON          1
+
+// Button pin is BOOTSEL which is flash CS pin
+
+#define BUTTON_BOOTSEL
+#define BUTTON_STATE_ACTIVE   0
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* BOARD_H_ */

+ 137 - 0
hw/bsp/rp2040/family.c

@@ -0,0 +1,137 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
+ * Copyright (c) 2021, Ha Thach (tinyusb.org)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ *
+ * This file is part of the TinyUSB stack.
+ */
+
+#include "pico/stdlib.h"
+#include "hardware/gpio.h"
+#include "hardware/sync.h"
+#include "hardware/structs/ioqspi.h"
+#include "hardware/structs/sio.h"
+
+#include "bsp/board.h"
+#include "board.h"
+
+#ifdef BUTTON_BOOTSEL
+// This example blinks the Picoboard LED when the BOOTSEL button is pressed.
+//
+// Picoboard has a button attached to the flash CS pin, which the bootrom
+// checks, and jumps straight to the USB bootcode if the button is pressed
+// (pulling flash CS low). We can check this pin in by jumping to some code in
+// SRAM (so that the XIP interface is not required), floating the flash CS
+// pin, and observing whether it is pulled low.
+//
+// This doesn't work if others are trying to access flash at the same time,
+// e.g. XIP streamer, or the other core.
+bool __no_inline_not_in_flash_func(get_bootsel_button)() {
+    const uint CS_PIN_INDEX = 1;
+
+    // Must disable interrupts, as interrupt handlers may be in flash, and we
+    // are about to temporarily disable flash access!
+    uint32_t flags = save_and_disable_interrupts();
+
+    // Set chip select to Hi-Z
+    hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
+                    GPIO_OVERRIDE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
+                    IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
+
+    // Note we can't call into any sleep functions in flash right now
+    for (volatile int i = 0; i < 1000; ++i);
+
+    // The HI GPIO registers in SIO can observe and control the 6 QSPI pins.
+    // Note the button pulls the pin *low* when pressed.
+    bool button_state = (sio_hw->gpio_hi_in & (1u << CS_PIN_INDEX));
+
+    // Need to restore the state of chip select, else we are going to have a
+    // bad time when we return to code in flash!
+    hw_write_masked(&ioqspi_hw->io[CS_PIN_INDEX].ctrl,
+                    GPIO_OVERRIDE_NORMAL << IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_LSB,
+                    IO_QSPI_GPIO_QSPI_SS_CTRL_OEOVER_BITS);
+
+    restore_interrupts(flags);
+
+    return button_state;
+}
+#endif
+
+void board_init(void)
+{
+  setup_default_uart();
+  gpio_init(LED_PIN);
+  gpio_set_dir(LED_PIN, GPIO_OUT);
+
+  // Button
+#ifndef BUTTON_BOOTSEL
+#endif
+
+  // todo probably set up device mode?
+#if TUSB_OPT_DEVICE_ENABLED
+
+#endif
+
+#if TUSB_OPT_HOST_ENABLED
+  // set portfunc to host !!!
+#endif
+}
+
+//--------------------------------------------------------------------+
+// Board porting API
+//--------------------------------------------------------------------+
+
+void board_led_write(bool state)
+{
+  gpio_put(LED_PIN, state ? LED_STATE_ON : (1-LED_STATE_ON));
+}
+
+uint32_t board_button_read(void)
+{
+#ifdef BUTTON_BOOTSEL
+  return BUTTON_STATE_ACTIVE == get_bootsel_button();
+#else
+  return 0;
+#endif
+}
+
+int board_uart_read(uint8_t* buf, int len)
+{
+  for(int i=0;i<len;i++) {
+    buf[i] = uart_getc(uart_default);
+  }
+  return 0;
+}
+
+int board_uart_write(void const * buf, int len)
+{
+  for(int i=0;i<len;i++) {
+    uart_putc(uart_default, ((char *)buf)[i]);
+  }
+  return 0;
+}
+
+//--------------------------------------------------------------------+
+// USB Interrupt Handler
+// rp2040 implementation will install approriate handler when initializing
+// tinyusb. There is no need to forward IRQ from application
+//--------------------------------------------------------------------+

+ 3 - 0
hw/bsp/rp2040/family.cmake

@@ -0,0 +1,3 @@
+cmake_minimum_required(VERSION 3.12)
+set(PICO_SDK_PATH ${TOP}/hw/mcu/raspberrypi/pico-sdk)
+include(${PICO_SDK_PATH}/pico_sdk_init.cmake)

+ 1 - 0
hw/bsp/rp2040/family_extra.cmake

@@ -0,0 +1 @@
+pico_sdk_init()

+ 1 - 0
hw/mcu/raspberrypi/pico-sdk

@@ -0,0 +1 @@
+Subproject commit ef38b746efaeb0aa1354406198a76da29302aada

+ 1 - 1
src/host/usbh.c

@@ -154,7 +154,7 @@ tusb_device_state_t tuh_device_get_state (uint8_t const dev_addr)
 tusb_speed_t tuh_device_get_speed (uint8_t const dev_addr)
 {
   TU_ASSERT( dev_addr <= CFG_TUSB_HOST_DEVICE_MAX, TUSB_DEVICE_STATE_UNPLUG);
-  return _usbh_devices[dev_addr].speed;
+  return (tusb_speed_t) _usbh_devices[dev_addr].speed;
 }
 
 void osal_task_delay(uint32_t msec)

+ 39 - 59
tools/build_all.py → tools/build_board.py

@@ -18,48 +18,60 @@ total_time = time.monotonic()
 build_format = '| {:29} | {:30} | {:18} | {:7} | {:6} | {:6} |'
 build_separator = '-' * 106
 
+def filter_with_input(mylist):
+    if len(sys.argv) > 1:
+        input_args = list(set(mylist).intersection(sys.argv))
+        if len(input_args) > 0:
+            mylist[:] = input_args
+
 # If examples are not specified in arguments, build all
 all_examples = []
-
 for entry in os.scandir("examples/device"):
     if entry.is_dir():
         all_examples.append("device/" + entry.name)
-
 for entry in os.scandir("examples/host"):
     if entry.is_dir():
         all_examples.append("host/" + entry.name)
-
-if len(sys.argv) > 1:
-    input_examples = list(set(all_examples).intersection(sys.argv))
-    if len(input_examples) > 0:
-        all_examples = input_examples
-
+filter_with_input(all_examples)
 all_examples.sort()
 
 # If boards are not specified in arguments, build all
 all_boards = []
-
 for entry in os.scandir("hw/bsp"):
-    if entry.is_dir() and entry.name != "esp32s2":
-        if os.path.isdir(entry.path + "/boards"):
-            # family directory
-            for subentry in os.scandir(entry.path + "/boards"):
-                if subentry.is_dir(): all_boards.append(subentry.name)
-        else:
-            all_boards.append(entry.name)
+    if entry.is_dir() and os.path.exists(entry.path + "/board.mk"):
+        all_boards.append(entry.name)
+filter_with_input(all_boards)
+all_boards.sort()
 
-if len(sys.argv) > 1:
-    input_boards = list(set(all_boards).intersection(sys.argv))
-    if len(input_boards) > 0:
-        all_boards = input_boards
+def build_board(example, board):
+    global success_count, fail_count, skip_count
+    start_time = time.monotonic()
+    flash_size = "-"
+    sram_size = "-"
+
+    # Check if board is skipped
+    if skip_example(example, board):
+        success = SKIPPED
+        skip_count += 1
+        print(build_format.format(example, board, success, '-', flash_size, sram_size))
+    else:
+        subprocess.run("make -C examples/{} BOARD={} clean".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        build_result = subprocess.run("make -j -C examples/{} BOARD={} all".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+        if build_result.returncode == 0:
+            success = SUCCEEDED
+            success_count += 1
+            (flash_size, sram_size) = build_size(example, board)
+        else:
+            exit_status = build_result.returncode
+            success = FAILED
+            fail_count += 1
 
-all_boards.sort()
+        build_duration = time.monotonic() - start_time
+        print(build_format.format(example, board, success, "{:.2f}s".format(build_duration), flash_size, sram_size))
 
-def build_example(example, board):
-    subprocess.run("make -C examples/{} BOARD={} clean".format(example, board), shell=True,
-                   stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-    return subprocess.run("make -j -C examples/{} BOARD={} all".format(example, board), shell=True,
-                          stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        if build_result.returncode != 0:
+            print(build_result.stdout.decode("utf-8"))
 
 def build_size(example, board):
     #elf_file = 'examples/device/{}/_build/build-{}/{}-firmware.elf'.format(example, board, board)
@@ -72,11 +84,7 @@ def build_size(example, board):
 
 def skip_example(example, board):
     ex_dir = 'examples/' + example
-
     board_mk = 'hw/bsp/{}/board.mk'.format(board)
-    if not os.path.exists(board_mk):
-        board_mk = list(glob.iglob('hw/bsp/*/boards/{}/../../family.mk'.format(board)))[0]
-
     with open(board_mk) as mk:
         mk_contents = mk.read()
 
@@ -107,35 +115,7 @@ print(build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', '
 for example in all_examples:
     print(build_separator)
     for board in all_boards:
-        start_time = time.monotonic()
-
-        flash_size = "-"
-        sram_size = "-"
-
-        # Check if board is skipped
-        if skip_example(example, board):
-            success = SKIPPED
-            skip_count += 1
-            print(build_format.format(example, board, success, '-', flash_size, sram_size))
-        else:
-            build_result = build_example(example, board)
-
-            if build_result.returncode == 0:
-                success = SUCCEEDED
-                success_count += 1
-                (flash_size, sram_size) = build_size(example, board)
-            else:
-                exit_status = build_result.returncode
-                success = FAILED
-                fail_count += 1
-
-            build_duration = time.monotonic() - start_time
-            print(build_format.format(example, board, success, "{:.2f}s".format(build_duration), flash_size, sram_size))
-
-            if build_result.returncode != 0:
-                print(build_result.stdout.decode("utf-8"))
-
-
+        build_board(example, board)
 
 total_time = time.monotonic() - total_time
 print(build_separator)

+ 0 - 102
tools/build_esp32s.py

@@ -1,102 +0,0 @@
-import os
-import glob
-import sys
-import subprocess
-import time
-
-SUCCEEDED = "\033[32msucceeded\033[0m"
-FAILED = "\033[31mfailed\033[0m"
-SKIPPED = "\033[33mskipped\033[0m"
-
-success_count = 0
-fail_count = 0
-skip_count = 0
-exit_status = 0
-
-total_time = time.monotonic()
-
-build_format = '| {:23} | {:30} | {:18} | {:7} | {:6} | {:6} |'
-build_separator = '-' * 100
-
-# 1st Argument is Example, build all examples if not existed
-all_examples = []
-if len(sys.argv) > 1:
-    all_examples.append(sys.argv[1])
-else:
-    for entry in os.scandir("examples/device"):
-        # Only includes example with CMakeLists.txt for esp32s
-        if entry.is_dir() and os.path.exists(entry.path + "/CMakeLists.txt"):
-            all_examples.append(entry.name)
-all_examples.sort()
-
-# 2nd Argument is Board, build all boards if not existed
-all_boards = []
-if len(sys.argv) > 2:
-    all_boards.append(sys.argv[2])
-else:
-    for entry in os.scandir("hw/bsp/esp32s2/boards"):
-        if entry.is_dir():
-            all_boards.append(entry.name)
-                                
-all_boards.sort()
-
-def build_example(example, board):
-    subprocess.run("make -C examples/device/{} BOARD={} clean".format(example, board), shell=True,
-                   stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-    return subprocess.run("make -C examples/device/{} BOARD={} all".format(example, board), shell=True,
-                          stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
-
-def build_size(example, board):
-    #elf_file = 'examples/device/{}/_build/build-{}/{}-firmware.elf'.format(example, board, board)
-    elf_file = 'examples/device/{}/_build/build-{}/*.elf'.format(example, board)
-    size_output = subprocess.run('size {}'.format(elf_file), shell=True, stdout=subprocess.PIPE).stdout.decode("utf-8")
-    size_list = size_output.split('\n')[1].split('\t')
-    flash_size = int(size_list[0])
-    sram_size = int(size_list[1]) + int(size_list[2])
-    return (flash_size, sram_size)
-
-def skip_example(example, board):
-    return 0
-
-print(build_separator)
-print(build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', 'Flash', 'SRAM'))
-print(build_separator)
-
-for example in all_examples:
-    for board in all_boards:
-        start_time = time.monotonic()
-
-        flash_size = "-"
-        sram_size = "-"
-
-        # Check if board is skipped
-        if skip_example(example, board):
-            success = SKIPPED
-            skip_count += 1
-            print(build_format.format(example, board, success, '-', flash_size, sram_size))
-        else:
-            build_result = build_example(example, board)
-
-            if build_result.returncode == 0:
-                success = SUCCEEDED
-                success_count += 1
-                (flash_size, sram_size) = build_size(example, board)
-            else:
-                exit_status = build_result.returncode
-                success = FAILED
-                fail_count += 1
-
-            build_duration = time.monotonic() - start_time
-            print(build_format.format(example, board, success, "{:.2f}s".format(build_duration), flash_size, sram_size))
-
-            if build_result.returncode != 0:
-                print(build_result.stdout.decode("utf-8"))
-
-
-
-total_time = time.monotonic() - total_time
-print(build_separator)
-print("Build Summary: {} {}, {} {}, {} {} and took {:.2f}s".format(success_count, SUCCEEDED, fail_count, FAILED, skip_count, SKIPPED, total_time))
-print(build_separator)
-
-sys.exit(exit_status)

+ 99 - 0
tools/build_esp32s2.py

@@ -0,0 +1,99 @@
+import os
+import glob
+import sys
+import subprocess
+import time
+
+SUCCEEDED = "\033[32msucceeded\033[0m"
+FAILED = "\033[31mfailed\033[0m"
+SKIPPED = "\033[33mskipped\033[0m"
+
+success_count = 0
+fail_count = 0
+skip_count = 0
+exit_status = 0
+
+total_time = time.monotonic()
+
+build_format = '| {:23} | {:30} | {:18} | {:7} | {:6} | {:6} |'
+build_separator = '-' * 100
+
+def filter_with_input(mylist):
+    if len(sys.argv) > 1:
+        input_args = list(set(mylist).intersection(sys.argv))
+        if len(input_args) > 0:
+            mylist[:] = input_args
+
+# Build all examples if not specified
+all_examples = []
+for entry in os.scandir("examples/device"):
+    # Only includes example with CMakeLists.txt for esp32s
+    if entry.is_dir() and os.path.exists(entry.path + "/sdkconfig.defaults"):
+        all_examples.append(entry.name)
+filter_with_input(all_examples)
+all_examples.sort()
+
+# Build all boards if not specified
+all_boards = []
+for entry in os.scandir("hw/bsp/esp32s2/boards"):
+    if entry.is_dir():
+        all_boards.append(entry.name)
+filter_with_input(all_boards)
+all_boards.sort()
+
+def build_board(example, board):
+    global success_count, fail_count, skip_count
+    start_time = time.monotonic()
+    flash_size = "-"
+    sram_size = "-"
+
+    # Check if board is skipped
+    if skip_example(example, board):
+        success = SKIPPED
+        skip_count += 1
+        print(build_format.format(example, board, success, '-', flash_size, sram_size))
+    else:
+        subprocess.run("make -C examples/device/{} BOARD={} clean".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        build_result = subprocess.run("make -j -C examples/device/{} BOARD={} all".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+        if build_result.returncode == 0:
+            success = SUCCEEDED
+            success_count += 1
+            (flash_size, sram_size) = build_size(example, board)
+        else:
+            exit_status = build_result.returncode
+            success = FAILED
+            fail_count += 1
+
+        build_duration = time.monotonic() - start_time
+        print(build_format.format(example, board, success, "{:.2f}s".format(build_duration), flash_size, sram_size))
+
+        if build_result.returncode != 0:
+            print(build_result.stdout.decode("utf-8"))
+
+def build_size(example, board):
+    #elf_file = 'examples/device/{}/_build/build-{}/{}-firmware.elf'.format(example, board, board)
+    elf_file = 'examples/device/{}/_build/build-{}/*.elf'.format(example, board)
+    size_output = subprocess.run('size {}'.format(elf_file), shell=True, stdout=subprocess.PIPE).stdout.decode("utf-8")
+    size_list = size_output.split('\n')[1].split('\t')
+    flash_size = int(size_list[0])
+    sram_size = int(size_list[1]) + int(size_list[2])
+    return (flash_size, sram_size)
+
+def skip_example(example, board):
+    return 0
+
+print(build_separator)
+print(build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', 'Flash', 'SRAM'))
+print(build_separator)
+
+for example in all_examples:
+    for board in all_boards:
+        build_board(example, board)
+
+total_time = time.monotonic() - total_time
+print(build_separator)
+print("Build Summary: {} {}, {} {}, {} {} and took {:.2f}s".format(success_count, SUCCEEDED, fail_count, FAILED, skip_count, SKIPPED, total_time))
+print(build_separator)
+
+sys.exit(exit_status)

+ 136 - 0
tools/build_family.py

@@ -0,0 +1,136 @@
+import os
+import glob
+import sys
+import subprocess
+import time
+
+SUCCEEDED = "\033[32msucceeded\033[0m"
+FAILED = "\033[31mfailed\033[0m"
+SKIPPED = "\033[33mskipped\033[0m"
+
+success_count = 0
+fail_count = 0
+skip_count = 0
+exit_status = 0
+
+total_time = time.monotonic()
+
+build_format = '| {:29} | {:30} | {:18} | {:7} | {:6} | {:6} |'
+build_separator = '-' * 106
+
+def filter_with_input(mylist):
+    if len(sys.argv) > 1:
+        input_args = list(set(mylist).intersection(sys.argv))
+        if len(input_args) > 0:
+            mylist[:] = input_args
+
+# If examples are not specified in arguments, build all
+all_examples = []
+for entry in os.scandir("examples/device"):
+    if entry.is_dir():
+        all_examples.append("device/" + entry.name)
+for entry in os.scandir("examples/host"):
+    if entry.is_dir():
+        all_examples.append("host/" + entry.name)
+filter_with_input(all_examples)
+all_examples.sort()
+
+# If family are not specified in arguments, build all
+all_families = []
+for entry in os.scandir("hw/bsp"):
+    if entry.is_dir() and os.path.isdir(entry.path + "/boards") and entry.name != "esp32s2":
+        all_families.append(entry.name)
+            
+filter_with_input(all_families)
+all_families.sort()
+
+def build_family(example, family):
+    all_boards = []
+    for entry in os.scandir("hw/bsp/{}/boards".format(family)):
+        if entry.is_dir():
+            all_boards.append(entry.name)
+    all_boards.sort()
+    
+    for board in all_boards:
+        build_board(example, board)
+    
+def build_board(example, board):
+    global success_count, fail_count, skip_count
+    start_time = time.monotonic()
+    flash_size = "-"
+    sram_size = "-"
+
+    # Check if board is skipped
+    if skip_example(example, board):
+        success = SKIPPED
+        skip_count += 1
+        print(build_format.format(example, board, success, '-', flash_size, sram_size))
+    else:   
+        subprocess.run("make -C examples/{} BOARD={} clean".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+        build_result = subprocess.run("make -j -C examples/{} BOARD={} all".format(example, board), shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+
+        if build_result.returncode == 0:
+            success = SUCCEEDED
+            success_count += 1
+            (flash_size, sram_size) = build_size(example, board)
+        else:
+            exit_status = build_result.returncode
+            success = FAILED
+            fail_count += 1
+
+        build_duration = time.monotonic() - start_time
+        print(build_format.format(example, board, success, "{:.2f}s".format(build_duration), flash_size, sram_size))
+
+        if build_result.returncode != 0:
+            print(build_result.stdout.decode("utf-8"))            
+
+def build_size(example, board):
+    #elf_file = 'examples/device/{}/_build/build-{}/{}-firmware.elf'.format(example, board, board)
+    elf_file = 'examples/{}/_build/build-{}/*.elf'.format(example, board)
+    size_output = subprocess.run('size {}'.format(elf_file), shell=True, stdout=subprocess.PIPE).stdout.decode("utf-8")
+    size_list = size_output.split('\n')[1].split('\t')
+    flash_size = int(size_list[0])
+    sram_size = int(size_list[1]) + int(size_list[2])
+    return (flash_size, sram_size)
+
+def skip_example(example, board):
+    ex_dir = 'examples/' + example
+    board_mk = 'hw/bsp/{}/family.mk'.format(family)
+
+    with open(board_mk) as mk:
+        mk_contents = mk.read()
+
+        # Skip all OPT_MCU_NONE these are WIP port
+        if '-DCFG_TUSB_MCU=OPT_MCU_NONE' in mk_contents:
+            return 1
+
+        # Skip if CFG_TUSB_MCU in board.mk to match skip file
+        for skip_file in glob.iglob(ex_dir + '/.skip.MCU_*'):
+            mcu_cflag = '-DCFG_TUSB_MCU=OPT_' + os.path.basename(skip_file).split('.')[2]
+            if mcu_cflag in mk_contents:
+                return 1
+
+        # Build only list, if exists only these MCU are built
+        only_list = list(glob.iglob(ex_dir + '/.only.MCU_*'))
+        if len(only_list) > 0:
+            for only_file in only_list:
+                mcu_cflag = '-DCFG_TUSB_MCU=OPT_' + os.path.basename(only_file).split('.')[2]
+                if mcu_cflag in mk_contents:
+                    return 0
+            return 1
+    return 0
+
+print(build_separator)
+print(build_format.format('Example', 'Board', '\033[39mResult\033[0m', 'Time', 'Flash', 'SRAM'))
+
+for example in all_examples:
+    print(build_separator)
+    for family in all_families:
+        build_family(example, family)
+
+total_time = time.monotonic() - total_time
+print(build_separator)
+print("Build Summary: {} {}, {} {}, {} {} and took {:.2f}s".format(success_count, SUCCEEDED, fail_count, FAILED, skip_count, SKIPPED, total_time))
+print(build_separator)
+
+sys.exit(exit_status)