Просмотр исходного кода

Merge branch 'feature/multi_target_support' into 'master'

Multi target support

See merge request espressif/esp-serial-flasher!20
Martin Válik 5 лет назад
Родитель
Сommit
965ce9dfc5

+ 2 - 3
.gitlab-ci.yml

@@ -41,9 +41,8 @@ build_with_idf:
     - *clone_and_setup_idf
     - cd $CI_PROJECT_DIR/examples/esp32_example
     # Build for all supported targets
-    - idf.py build -DTARGET_SOC=ESP32    -DMD5_ENABLED=1
-    - idf.py build -DTARGET_SOC=ESP8266  -DMD5_ENABLED=0
-    - idf.py build -DTARGET_SOC=ESP32_S2 -DMD5_ENABLED=1
+    - idf.py build -DMD5_ENABLED=1
+    - idf.py build -DMD5_ENABLED=0
 
     - *clone_and_setup_stm32
     - cd $CI_PROJECT_DIR/examples/stm32_example/build

+ 1 - 12
CMakeLists.txt

@@ -32,15 +32,4 @@ else()
 
 endif()
 
-
-if(TARGET_SOC STREQUAL "ESP32")
-    target_compile_definitions(${target} PUBLIC -DTARGET_ESP32)
-elseif(TARGET_SOC STREQUAL "ESP32_S2")
-    target_compile_definitions(${target} PUBLIC -DTARGET_ESP32_S2)
-elseif(TARGET_SOC STREQUAL "ESP8266")
-    target_compile_definitions(${target} PUBLIC -DTARGET_ESP8266)
-endif()
-
-if(DEFINED MD5_ENABLED)
-    target_compile_definitions(${target} PUBLIC -DMD5_ENABLED=${MD5_ENABLED})
-endif()
+target_compile_definitions(${target} PUBLIC -DMD5_ENABLED=1)

+ 3 - 9
README.md

@@ -28,21 +28,15 @@ Please refer to ports in `port` directory. Currently, only ports for [ESP32 port
 
 ## Configuration
 
-Apart from writing port (if not available), user has to provide few configuration options:
-* TARGET_SOC: one of Espressif SoCs can be selected as target to be flashed. Currently, following SoCs are supported:
-    * ESP32
-    * ESP32_S2
-    * ESP8266
-* MD5_ENABLED: if enabled, serial flasher will verify flash integrity after writing to memory.
-
+Apart from writing port (if not available), user can enable flash integrity check functionality by setting MD5_ENABLED option. If enabled, serial flasher is capable of verify flash integrity after writing to memory.
 
 Configuration can be passed to `cmake` via command line:
 
 ```
-cmake -DTARGET_SOC=ESP32 -DMD5_ENABLED=1 .. && cmake --build .
+cmake -DMD5_ENABLED=1 .. && cmake --build .
 ```
 
-Note: in case, no compile definitions are povided, ESP32 is set as target and MD5 check is enabled by default. As ROM bootloader of ESP8266 does not support MD5_CHECK, `-DMD5_ENABLED=0` has to be passed to command line.
+Note: in case, no compile definitions are provided, ESP32 is set as target and MD5 check is enabled by default. As ROM bootloader of ESP8266 does not support MD5_CHECK, `-DMD5_ENABLED=0` has to be passed to command line.
 
 ### STM32 support
 

+ 0 - 0
examples/binaries/ESP32/bootloader.bin → examples/binaries/Hello-world/ESP32/bootloader.bin


+ 0 - 0
examples/binaries/ESP32/hello-world.bin → examples/binaries/Hello-world/ESP32/hello-world.bin


+ 0 - 0
examples/binaries/ESP32/partition-table.bin → examples/binaries/Hello-world/ESP32/partition-table.bin


+ 0 - 0
examples/binaries/ESP32_S2/bootloader.bin → examples/binaries/Hello-world/ESP32_S2/bootloader.bin


+ 0 - 0
examples/binaries/ESP32_S2/hello-world.bin → examples/binaries/Hello-world/ESP32_S2/hello-world.bin


+ 0 - 0
examples/binaries/ESP32_S2/partition-table.bin → examples/binaries/Hello-world/ESP32_S2/partition-table.bin


+ 0 - 0
examples/binaries/ESP8266/bootloader.bin → examples/binaries/Hello-world/ESP8266/bootloader.bin


+ 0 - 0
examples/binaries/ESP8266/hello-world.bin → examples/binaries/Hello-world/ESP8266/hello-world.bin


+ 0 - 0
examples/binaries/ESP8266/partition-table.bin → examples/binaries/Hello-world/ESP8266/partition-table.bin


+ 8 - 8
examples/common/bin2array.cmake

@@ -1,20 +1,20 @@
-# Creates C resources file from files in given directory
+# Creates C resources file from files in given directory recursively
 function(create_resources dir output)
     # Create empty output file
-    file(WRITE ${output} "")
+    file(WRITE ${output} "#include <stdint.h>\n\n")
     # Collect input files
-    file(GLOB bins ${dir}/*)
+    file(GLOB_RECURSE bin_paths ${dir}/*)
     # Iterate through input files
-    foreach(bin ${bins})
-        # Get short filenames
-        string(REGEX MATCH "([^/]+)$" filename ${bin})
+    foreach(bin ${bin_paths})
+        # Get short filenames, by discarding relative path
+        file(GLOB name RELATIVE ${dir} ${bin})
         # Replace filename spaces & extension separator for C compatibility
-        string(REGEX REPLACE "\\.| |-" "_" filename ${filename})
+        string(REGEX REPLACE "[\\./-]" "_" filename ${name})
         # Read hex data from file
         file(READ ${bin} filedata HEX)
         # Convert hex data for C compatibility
         string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," filedata ${filedata})
         # Append data to output file
-        file(APPEND ${output} "const unsigned char ${filename}[] = {${filedata}};\nconst unsigned ${filename}_size = sizeof(${filename});\n")
+        file(APPEND ${output} "const uint8_t  ${filename}[] = {${filedata}};\nconst uint32_t ${filename}_size = sizeof(${filename});\n")
     endforeach()
 endfunction()

+ 84 - 15
examples/common/example_common.c

@@ -20,6 +20,71 @@
 #include "esp_loader.h"
 #include "example_common.h"
 
+#ifndef SINGLE_TARGET_SUPPORT
+
+#define BOOTLOADER_ADDRESS_8266  0x1000
+#define BOOTLOADER_ADDRESS  0x1000
+#define PARTITION_ADDRESS   0x8000
+#define APPLICATION_ADDRESS 0x10000
+
+extern const uint8_t  ESP32_bootloader_bin[];
+extern const uint32_t ESP32_bootloader_bin_size;
+extern const uint8_t  ESP32_hello_world_bin[];
+extern const uint32_t ESP32_hello_world_bin_size;
+extern const uint8_t  ESP32_partition_table_bin[];
+extern const uint32_t ESP32_partition_table_bin_size;
+
+extern const uint8_t  ESP32_S2_bootloader_bin[];
+extern const uint32_t ESP32_S2_bootloader_bin_size;
+extern const uint8_t  ESP32_S2_hello_world_bin[];
+extern const uint32_t ESP32_S2_hello_world_bin_size;
+extern const uint8_t  ESP32_S2_partition_table_bin[];
+extern const uint32_t ESP32_S2_partition_table_bin_size;
+
+extern const uint8_t  ESP8266_bootloader_bin[];
+extern const uint32_t ESP8266_bootloader_bin_size;
+extern const uint8_t  ESP8266_hello_world_bin[];
+extern const uint32_t ESP8266_hello_world_bin_size;
+extern const uint8_t  ESP8266_partition_table_bin[];
+extern const uint32_t ESP8266_partition_table_bin_size;
+
+void get_example_binaries(target_chip_t target, example_binaries_t *bins)
+{
+    if (target == ESP8266_CHIP) {
+        bins->boot.data = ESP8266_bootloader_bin;
+        bins->boot.size = ESP8266_bootloader_bin_size;
+        bins->boot.addr = BOOTLOADER_ADDRESS_8266;
+        bins->part.data = ESP8266_partition_table_bin;
+        bins->part.size = ESP8266_partition_table_bin_size;
+        bins->part.addr = PARTITION_ADDRESS;
+        bins->app.data  = ESP8266_hello_world_bin;
+        bins->app.size  = ESP8266_hello_world_bin_size;
+        bins->app.addr  = APPLICATION_ADDRESS;
+    } else if (target == ESP32_CHIP) {
+        bins->boot.data = ESP32_bootloader_bin;
+        bins->boot.size = ESP32_bootloader_bin_size;
+        bins->boot.addr = BOOTLOADER_ADDRESS;
+        bins->part.data = ESP32_partition_table_bin;
+        bins->part.size = ESP32_partition_table_bin_size;
+        bins->part.addr = PARTITION_ADDRESS;
+        bins->app.data  = ESP32_hello_world_bin;
+        bins->app.size  = ESP32_hello_world_bin_size;
+        bins->app.addr  = APPLICATION_ADDRESS;
+    } else {
+        bins->boot.data = ESP32_S2_bootloader_bin;
+        bins->boot.size = ESP32_S2_bootloader_bin_size;
+        bins->boot.addr = BOOTLOADER_ADDRESS;
+        bins->part.data = ESP32_S2_partition_table_bin;
+        bins->part.size = ESP32_S2_partition_table_bin_size;
+        bins->part.addr = PARTITION_ADDRESS;
+        bins->app.data  = ESP32_S2_hello_world_bin;
+        bins->app.size  = ESP32_S2_hello_world_bin_size;
+        bins->app.addr  = APPLICATION_ADDRESS;
+    }
+}
+
+#endif
+
 esp_loader_error_t connect_to_target(uint32_t higrer_baudrate)
 {
     esp_loader_connect_args_t connect_config = ESP_LOADER_CONNECT_DEFAULT();
@@ -31,32 +96,33 @@ esp_loader_error_t connect_to_target(uint32_t higrer_baudrate)
     }
     printf("Connected to target\n");
 
-#ifndef TARGET_ESP8266
-    if (higrer_baudrate) {
+    if (higrer_baudrate && esp_loader_get_target() != ESP8266_CHIP) {
         err = esp_loader_change_baudrate(higrer_baudrate);
-        if (err != ESP_LOADER_SUCCESS) {
-            printf("Unable to change baud rate on target.");
+        if (err == ESP_LOADER_ERROR_UNSUPPORTED_FUNC) {
+            printf("ESP8266 does not support change baudrate command.");
             return err;
-        }
-
-        err = loader_port_change_baudrate(higrer_baudrate);
-        if (err != ESP_LOADER_SUCCESS) {
-            printf("Unable to change baud rate.");
+        } else if (err != ESP_LOADER_SUCCESS) {
+            printf("Unable to change baud rate on target.");
             return err;
+        } else {
+            err = loader_port_change_baudrate(higrer_baudrate);
+            if (err != ESP_LOADER_SUCCESS) {
+                printf("Unable to change baud rate.");
+                return err;
+            }
+            printf("Baudrate changed\n");
         }
-        printf("Baudrate changed\n");
     }
-#endif
 
     return ESP_LOADER_SUCCESS;
 }
 
 
-esp_loader_error_t flash_binary(const unsigned char *bin, size_t size, size_t address)
+esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address)
 {
     esp_loader_error_t err;
     static uint8_t payload[1024];
-    const unsigned char *bin_addr = bin;
+    const uint8_t *bin_addr = bin;
 
     printf("Erasing flash (this may take a while)...\n");
     err = esp_loader_flash_start(address, size, sizeof(payload));
@@ -67,7 +133,7 @@ esp_loader_error_t flash_binary(const unsigned char *bin, size_t size, size_t ad
     printf("Start programming\n");
 
     size_t binary_size = size;
-    size_t written = 0; 
+    size_t written = 0;
 
     while (size > 0) {
         size_t to_read = MIN(size, sizeof(payload));
@@ -92,7 +158,10 @@ esp_loader_error_t flash_binary(const unsigned char *bin, size_t size, size_t ad
 
 #if MD5_ENABLED
     err = esp_loader_flash_verify();
-    if (err != ESP_LOADER_SUCCESS) {
+    if (err == ESP_LOADER_ERROR_UNSUPPORTED_FUNC) {
+        printf("ESP8266 does not support flash verify command.");
+        return err;
+    } else if (err != ESP_LOADER_SUCCESS) {
         printf("MD5 does not match. err: %d\n", err);
         return err;
     }

+ 12 - 15
examples/common/example_common.h

@@ -15,21 +15,18 @@
 
 #pragma once
 
-#ifndef TARGET_ESP8266
-    #define BOOTLOADER_ADDRESS 0x1000
-#else
-    #define BOOTLOADER_ADDRESS 0x0
-#endif
+typedef struct {
+    const uint8_t *data;
+    uint32_t size;
+    uint32_t addr;
+} partition_attr_t;
 
-#define PARTITION_ADDRESS   0x8000
-#define APPLICATION_ADDRESS 0x10000
-
-extern const unsigned char bootloader_bin[];
-extern const unsigned bootloader_bin_size;
-extern const unsigned char hello_world_bin[];
-extern const unsigned hello_world_bin_size;
-extern const unsigned char partition_table_bin[];
-extern const unsigned partition_table_bin_size;
+typedef struct {
+    partition_attr_t boot;
+    partition_attr_t part;
+    partition_attr_t app;
+} example_binaries_t;
 
+void get_example_binaries(target_chip_t target, example_binaries_t *binaries);
 esp_loader_error_t connect_to_target(uint32_t higrer_baudrate);
-esp_loader_error_t flash_binary(const unsigned char *bin, size_t size, size_t address);
+esp_loader_error_t flash_binary(const uint8_t *bin, size_t size, size_t address);

+ 2 - 2
examples/esp32_example/README.md

@@ -57,9 +57,9 @@ For details about available configuration option, please refer to top level [REA
 Compile definitions can be specified on command line when running `idf.py`, for example:
 
 ```
-idf.py build -DTARGET_SOC=ESP32_S2 -DMD5_ENABLED=1
+idf.py build -DMD5_ENABLED=1
 ```
-Binaries to be flashed are placed in separate folder for each target and converted to C-array for any given SoC based on `TARGET_SOC` configuration option passed to command line. By default, example is compiled for ESP32 target with MD5 check enabled.
+Binaries to be flashed are placed in separate folder (binaries.c) for each possible target and converted to C-array. Without explicitly enabling MD5 check, flash integrity verification is disabled by default.
 
 ## Example output
 

+ 2 - 10
examples/esp32_example/main/CMakeLists.txt

@@ -1,17 +1,9 @@
 idf_component_register(SRCS "main.c" "binaries.c" "../../common/example_common.c"
-                       INCLUDE_DIRS ".""../../common")
+                       INCLUDE_DIRS "." "../../common")
 
 set_property(SOURCE binaries.c PROPERTY GENERATED 1)
 
 include(${CMAKE_CURRENT_LIST_DIR}/../../common/bin2array.cmake)
 
-if(TARGET_SOC STREQUAL "ESP32_S2")
-    set(binary_path ${CMAKE_CURRENT_LIST_DIR}/../../binaries/ESP32_S2)
-elseif(TARGET_SOC STREQUAL "ESP8266")
-    set(binary_path ${CMAKE_CURRENT_LIST_DIR}/../../binaries/ESP8266)
-else()
-    set(binary_path ${CMAKE_CURRENT_LIST_DIR}/../../binaries/ESP32)
-endif()
-
-create_resources(${binary_path} binaries.c)
+create_resources(${CMAKE_CURRENT_LIST_DIR}/../../binaries/Hello-world binaries.c)
 

+ 9 - 5
examples/esp32_example/main/main.c

@@ -15,13 +15,14 @@
 #include "driver/gpio.h"
 #include "serial_io.h"
 #include "esp_loader.h"
-#include "loader_config.h"
 #include "example_common.h"
 
 #define HIGHER_BAUDRATE 230400
 
 void app_main(void)
 {
+    example_binaries_t bin;
+
     const loader_serial_config_t config = {
         .baud_rate = 115200,
         .uart_port = UART_NUM_1,
@@ -37,8 +38,11 @@ void app_main(void)
     }
 
     if (connect_to_target(HIGHER_BAUDRATE) == ESP_LOADER_SUCCESS) {
-        flash_binary(bootloader_bin, bootloader_bin_size, BOOTLOADER_ADDRESS);
-        flash_binary(partition_table_bin, partition_table_bin_size, PARTITION_ADDRESS);
-        flash_binary(hello_world_bin, hello_world_bin_size, APPLICATION_ADDRESS);
+
+        get_example_binaries(esp_loader_get_target(), &bin);
+
+        flash_binary(bin.boot.data, bin.boot.size, bin.boot.addr);
+        flash_binary(bin.part.data, bin.part.size, bin.part.addr);
+        flash_binary(bin.app.data,  bin.app.size,  bin.app.addr);
     }
-}
+}

+ 4 - 1
examples/raspberry_example/CMakeLists.txt

@@ -11,4 +11,7 @@ target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ../common/)
 
 add_subdirectory(${FLASHER_DIR} ${CMAKE_BINARY_DIR}/flasher)
 
-target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE flasher)
+target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE flasher)
+
+# This example only supports one target (ESP32) and uses specific binary to be flashed.
+target_compile_definitions(${CMAKE_PROJECT_NAME} PRIVATE -DSINGLE_TARGET_SUPPORT=1)

+ 1 - 9
examples/stm32_example/CMakeLists.txt

@@ -14,15 +14,7 @@ project(stm32_flasher)
     
 enable_language(ASM)
 
-if(TARGET_SOC STREQUAL "ESP32_S2")
-    set(binary_path ${CMAKE_CURRENT_LIST_DIR}/../binaries/ESP32_S2)
-elseif(TARGET_SOC STREQUAL "ESP8266")
-    set(binary_path ${CMAKE_CURRENT_LIST_DIR}/../binaries/ESP8266)
-else()
-    set(binary_path ${CMAKE_CURRENT_LIST_DIR}/../binaries/ESP32)
-endif()
-
-create_resources(${binary_path} Src/binaries.c)
+create_resources(${CMAKE_CURRENT_LIST_DIR}/../binaries/Hello-world Src/binaries.c)
 
 include_directories(${CMAKE_CURRENT_SOURCE_DIR} Inc)
  

+ 1 - 1
examples/stm32_example/README.md

@@ -45,7 +45,7 @@ Run cmake (with appropriate parameters) and build:
 cmake -DTOOLCHAIN_PREFIX="/path_to_toolchain" -DSTM32Cube_DIR="path_to_stm32Cube" -DSTM32_CHIP="STM32F407VG" -DPORT="STM32" .. && cmake --build .
 ```
 
-Binaries to be flashed are placed in separate folder for each target and converted to C-array for any given SoC based on `TARGET_SOC` configuration option passed to command line. By default, example is compiled for ESP32 target with MD5 check enabled.
+Binaries to be flashed are placed in separate folder (binaries.c) for each possible target and converted to C-array. Without explicitly enabling MD5 check, flash integrity verification is disabled by default.
 
 For more details regarding to esp_serial_flasher configuration and STM32 support, please refer to top level [README.md](../../README.md).
 

+ 8 - 3
examples/stm32_example/Src/main.c

@@ -44,6 +44,8 @@ void loader_port_stm32_init(UART_HandleTypeDef *huart,
 
 int main(void)
 {
+    example_binaries_t bin;
+
     HAL_Init();
     SystemClock_Config();
     MX_GPIO_Init();
@@ -53,9 +55,12 @@ int main(void)
     loader_port_stm32_init(&huart1, GPIOB, TARGET_IO0_Pin, GPIOB, TARGET_RST_Pin);
     
     if (connect_to_target(HIGHER_BAUDRATE) == ESP_LOADER_SUCCESS) {
-        flash_binary(bootloader_bin, bootloader_bin_size, BOOTLOADER_ADDRESS);
-        flash_binary(partition_table_bin, partition_table_bin_size, PARTITION_ADDRESS);
-        flash_binary(hello_world_bin, hello_world_bin_size, APPLICATION_ADDRESS);
+        
+        get_example_binaries(esp_loader_get_target(), &bin);
+
+        flash_binary(bin.boot.data, bin.boot.size, bin.boot.addr);
+        flash_binary(bin.part.data, bin.part.size, bin.part.addr);
+        flash_binary(bin.app.data,  bin.app.size,  bin.app.addr);
     }
 
     while (1) { }

+ 39 - 19
include/esp_loader.h

@@ -17,7 +17,6 @@
 
 #include <stdint.h>
 #include <stdbool.h>
-#include "loader_config.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -26,8 +25,7 @@ extern "C" {
 /**
  * @brief Error codes
  */
-typedef enum
-{
+typedef enum {
     ESP_LOADER_SUCCESS,                /*!< Success */
     ESP_LOADER_ERROR_FAIL,             /*!< Unspecified error */
     ESP_LOADER_ERROR_TIMEOUT,          /*!< Timeout elapsed */
@@ -36,9 +34,21 @@ typedef enum
     ESP_LOADER_ERROR_INVALID_PARAM,    /*!< Invalid parameter passed to function */
     ESP_LOADER_ERROR_INVALID_TARGET,   /*!< Connected target is invalid */
     ESP_LOADER_ERROR_UNSUPPORTED_CHIP, /*!< Attached chip is not supported */
+    ESP_LOADER_ERROR_UNSUPPORTED_FUNC, /*!< Function is not supported on attached target */
     ESP_LOADER_ERROR_INVALID_RESPONSE  /*!< Internal error */
 } esp_loader_error_t;
 
+/**
+ * @brief Supported targets
+ */
+typedef enum {
+    ESP8266_CHIP = 0,
+    ESP32_CHIP   = 1,
+    ESP32S2_CHIP = 2,
+    ESP_MAX_CHIP = 3,
+    ESP_UNKNOWN_CHIP = 3 
+} target_chip_t;
+
 /**
  * @brief SPI pin configuration arguments
  */
@@ -57,21 +67,20 @@ typedef union {
 /**
  * @brief Connection arguments
  */
-typedef struct 
-{
-  uint32_t sync_timeout;  /*!< Maximum time to wait for response from serial interface. */
-  int32_t trials;         /*!< Number of trials to connect to target. If greater than 1,
+typedef struct {
+    uint32_t sync_timeout;  /*!< Maximum time to wait for response from serial interface. */
+    int32_t trials;         /*!< Number of trials to connect to target. If greater than 1,
                                100 millisecond delay is inserted after each try. */
-  esp_loader_spi_config_t spi_pin_config;  /*!< Determine which SPI peripheral and pins should be used to
-                                                connect to SPI flash. By setting spi_pin_config.val to zero, 
-                                                default configuration will be used. For more detailed 
+    esp_loader_spi_config_t spi_pin_config;  /*!< Determine which SPI peripheral and pins should be used to
+                                                connect to SPI flash. By setting spi_pin_config.val to zero,
+                                                default configuration will be used. For more detailed
                                                 information refer to serial protocol of esptool */
 } esp_loader_connect_args_t;
 
 #define ESP_LOADER_CONNECT_DEFAULT() { \
-  .sync_timeout = 100,  \
-  .trials = 10,  \
-  .spi_pin_config = { .val = 0 },  \
+  .sync_timeout = 100, \
+  .trials = 10, \
+  .spi_pin_config = { .val = 0 } \
 }
 
 #define ESP_LOADER_SPI_CONFIG_ESP32PICOD4() (esp_loader_spi_config_t) { \
@@ -95,6 +104,16 @@ typedef struct
   */
 esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args);
 
+/**
+  * @brief   Returns attached target chip.
+  *
+  * @warning This function can only be called after connection with target 
+  *          has been successfully established by calling esp_loader_connect().
+  *
+  * @return  One of target_chip_t
+  */
+target_chip_t esp_loader_get_target(void);
+
 /**
   * @brief Initiates flash operation
   *
@@ -102,7 +121,7 @@ esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args);
   * @param image_size[in]   Size of the whole binary to be loaded into flash.
   * @param block_size[in]   Size of buffer used in subsequent calls to esp_loader_flash_write.
   *
-  * @note  image_size is size of the whole image, whereas, block_size is chunk of data sent 
+  * @note  image_size is size of the whole image, whereas, block_size is chunk of data sent
   *        to the target, each time esp_loader_flash_write function is called.
   *
   * @return
@@ -119,7 +138,7 @@ esp_loader_error_t esp_loader_flash_start(uint32_t offset, uint32_t image_size,
   * @param size[in]         Size of payload in bytes.
   *
   * @note  size must not be greater that block_size supplied to previously called
-  *        esp_loader_flash_start function. If size is less than block_size, 
+  *        esp_loader_flash_start function. If size is less than block_size,
   *        remaining bytes of payload buffer will be padded with 0xff.
   *        Therefore, size of payload buffer has to be equal or greater than block_size.
   *
@@ -180,16 +199,15 @@ esp_loader_error_t esp_loader_read_register(uint32_t address, uint32_t *reg_valu
   *     - ESP_LOADER_SUCCESS Success
   *     - ESP_LOADER_ERROR_TIMEOUT Timeout
   *     - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error
+  *     - ESP_LOADER_ERROR_UNSUPPORTED_FUNC Unsupported on the target
   */
-#ifndef TARGET_ESP8266
 esp_loader_error_t esp_loader_change_baudrate(uint32_t baudrate);
-#endif
 
 /**
   * @brief Verify target's flash integrity by checking MD5.
   *        MD5 checksum is computed from data pushed to target's memory by calling
   *        esp_loader_flash_write() function and compared against target's MD5.
-  *        Target computes checksum based on offset and image_size passed to 
+  *        Target computes checksum based on offset and image_size passed to
   *        esp_loader_flash_start() function.
   *
   * @note  This function is only available if MD5_ENABLED is set.
@@ -199,9 +217,11 @@ esp_loader_error_t esp_loader_change_baudrate(uint32_t baudrate);
   *     - ESP_LOADER_ERROR_INVALID_MD5 MD5 does not match
   *     - ESP_LOADER_ERROR_TIMEOUT Timeout
   *     - ESP_LOADER_ERROR_INVALID_RESPONSE Internal error
+  *     - ESP_LOADER_ERROR_UNSUPPORTED_FUNC Unsupported on the target
   */
+#if MD5_ENABLED
 esp_loader_error_t esp_loader_flash_verify(void);
-
+#endif
 /**
   * @brief Toggles reset pin.
   */

+ 0 - 40
include/loader_config.h

@@ -1,40 +0,0 @@
-/* Copyright 2020 Espressif Systems (Shanghai) PTE LTD
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// MD5 checksum can be used to check, if new image was flashed successfully.
-// When enabled, esp_loader_flash_verify() function can be called to to verify
-// flash integrity. In case verification is unnecessary, this option can be 
-// disabled in order to reduce code size.
-#if !defined MD5_ENABLED
-#define MD5_ENABLED  1
-#endif
-
-#if !defined TARGET_ESP32_S2 && !defined TARGET_ESP8266
-#define TARGET_ESP32 1
-#endif
-
-#if MD5_ENABLED && defined TARGET_ESP8266
-#error "ESP8266 ROM loader does not support MD5 check"
-#endif
-
-#ifdef __cplusplus
-}
-#endif

+ 1 - 1
private_include/serial_comm.h

@@ -34,7 +34,7 @@ extern "C" {
     }                                   \
 } while(0)
 
-esp_loader_error_t loader_flash_begin_cmd(uint32_t offset, uint32_t erase_size, uint32_t block_size, uint32_t blocks_to_write);
+esp_loader_error_t loader_flash_begin_cmd(uint32_t offset, uint32_t erase_size, uint32_t block_size, uint32_t blocks_to_write, target_chip_t target);
 
 esp_loader_error_t loader_flash_data_cmd(const uint8_t *data, uint32_t size);
 

+ 0 - 7
private_include/serial_comm_prv.h

@@ -17,7 +17,6 @@
 
 #include <stdint.h>
 #include <stdbool.h>
-#include "loader_config.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -79,9 +78,7 @@ typedef struct __attribute__((packed))
     uint32_t packet_count;
     uint32_t packet_size;
     uint32_t offset;
-#ifdef TARGET_ESP32_S2
     uint32_t encrypted;
-#endif
 } begin_command_t;
 
 typedef struct __attribute__((packed))
@@ -162,10 +159,6 @@ typedef struct __attribute__((packed))
 {
     uint8_t failed;
     uint8_t error;
-#ifndef TARGET_ESP8266
-    uint8_t reserved_0;
-    uint8_t reserved_1;
-#endif
 } response_status_t;
 
 typedef struct __attribute__((packed))

+ 130 - 89
src/esp_loader.c

@@ -13,7 +13,6 @@
  * limitations under the License.
  */
 
-#include "loader_config.h"
 #include "serial_comm_prv.h"
 #include "serial_comm.h"
 #include "serial_io.h"
@@ -30,34 +29,11 @@
 #define MIN(a, b) ((a) < (b)) ? (a) : (b)
 #endif
 
-typedef struct {
-    uint32_t reg_1;
-    uint32_t reg_2;
-} date_registers_t;
-
-static const date_registers_t date_regs = {
-#if defined TARGET_ESP8266
-    .reg_1 = 0x00062000,
-    .reg_2 = 0
-#elif defined TARGET_ESP32
-    .reg_1 = 0x15122500,
-    .reg_2 = 0
-#elif defined TARGET_ESP32_S2
-    .reg_1 = 0x00000500,
-    .reg_2 = 0x19031400
-#endif
-};
-
-static const uint32_t UART_DATE_REG_ADDR = 0x60000078;    // used to differentiate ESP8266 vs ESP32*
-static const uint32_t UART_DATE_REG2_ADDR = 0x3f400074;   // used to differentiate ESP32-S2 vs other models
-
-static const uint32_t DEFAULT_TIMEOUT = 500;
+static const uint32_t DEFAULT_TIMEOUT = 1000;
 static const uint32_t DEFAULT_FLASH_TIMEOUT = 3000;       // timeout for most flash operations
 static const uint32_t ERASE_REGION_TIMEOUT_PER_MB = 3000; // timeout (per megabyte) for erasing a region
 static const uint8_t  PADDING_PATTERN = 0xFF;
 
-static uint32_t s_flash_write_size = 0;
-
 #define MEGABYTE  1024 * 1024
 
 size_t size_id_to_flash_size[] = {
@@ -74,34 +50,83 @@ typedef enum {
     SPI_FLASH_READ_ID = 0x9F
 } spi_flash_cmd_t;
 
-#if defined TARGET_ESP8266
-#define SPI_REG_BASE    0x60000200
-#define SPI_CMD_REG     SPI_REG_BASE + 0x00
-#define SPI_USR_REG     SPI_REG_BASE + 0x1c
-#define SPI_USR1_REG    SPI_REG_BASE + 0x20
-#define SPI_USR2_REG    SPI_REG_BASE + 0x24
-#define SPI_W0_REG      SPI_REG_BASE + 0x40
-#define SPI_MOSI_DLEN_REG 0
-#define SPI_MISO_DLEN_REG 0
-#elif defined TARGET_ESP32_S2
-#define SPI_REG_BASE    0x3f402000
-#define SPI_CMD_REG     SPI_REG_BASE + 0x00
-#define SPI_USR_REG     SPI_REG_BASE + 0x18
-#define SPI_USR1_REG    SPI_REG_BASE + 0x1c
-#define SPI_USR2_REG    SPI_REG_BASE + 0x20
-#define SPI_W0_REG      SPI_REG_BASE + 0x58
-#define SPI_MOSI_DLEN_REG SPI_REG_BASE + 0x24
-#define SPI_MISO_DLEN_REG SPI_REG_BASE + 0x28
-#elif defined TARGET_ESP32
-#define SPI_REG_BASE    0x60002000
-#define SPI_CMD_REG     SPI_REG_BASE + 0x00
-#define SPI_USR_REG     SPI_REG_BASE + 0x1c
-#define SPI_USR1_REG    SPI_REG_BASE + 0x20
-#define SPI_USR2_REG    SPI_REG_BASE + 0x24
-#define SPI_W0_REG      SPI_REG_BASE + 0x80
-#define SPI_MOSI_DLEN_REG SPI_REG_BASE + 0x28
-#define SPI_MISO_DLEN_REG SPI_REG_BASE + 0x2c
-#endif
+typedef struct {
+    uint32_t cmd;
+    uint32_t usr;
+    uint32_t usr1;
+    uint32_t usr2;
+    uint32_t w0;
+    uint32_t mosi_dlen;
+    uint32_t miso_dlen;
+} target_registers_t;
+
+typedef struct {
+    uint32_t reg_1;
+    uint32_t reg_2;
+} date_registers_t;
+
+
+static const uint32_t UART_DATE_REG_ADDR  = 0x60000078;   // used to differentiate ESP8266 vs ESP32*
+static const uint32_t UART_DATE_REG2_ADDR = 0x3f400074;   // used to differentiate ESP32-S2 vs other models
+
+#define ESP8266_SPI_REG_BASE 0x60000200
+#define ESP32S2_SPI_REG_BASE 0x3f402000
+#define ESP32_SPI_REG_BASE   0x60002000
+
+static const target_registers_t registers[ESP_MAX_CHIP] = {
+    // ESP8266
+    {
+        .cmd  = ESP8266_SPI_REG_BASE + 0x00,
+        .usr  = ESP8266_SPI_REG_BASE + 0x1c,
+        .usr1 = ESP8266_SPI_REG_BASE + 0x20,
+        .usr2 = ESP8266_SPI_REG_BASE + 0x24,
+        .w0   = ESP8266_SPI_REG_BASE + 0x40,
+        .mosi_dlen = 0,
+        .miso_dlen = 0,
+    },
+    // ESP32
+    {
+        .cmd  = ESP32_SPI_REG_BASE + 0x00,
+        .usr  = ESP32_SPI_REG_BASE + 0x1c,
+        .usr1 = ESP32_SPI_REG_BASE + 0x20,
+        .usr2 = ESP32_SPI_REG_BASE + 0x24,
+        .w0   = ESP32_SPI_REG_BASE + 0x80,
+        .mosi_dlen = ESP32_SPI_REG_BASE + 0x28,
+        .miso_dlen = ESP32_SPI_REG_BASE + 0x2c,
+    },
+    // ESP32S2
+    {
+        .cmd  = ESP32S2_SPI_REG_BASE + 0x00,
+        .usr  = ESP32S2_SPI_REG_BASE + 0x18,
+        .usr1 = ESP32S2_SPI_REG_BASE + 0x1c,
+        .usr2 = ESP32S2_SPI_REG_BASE + 0x20,
+        .w0   = ESP32S2_SPI_REG_BASE + 0x58,
+        .mosi_dlen = ESP32S2_SPI_REG_BASE + 0x24,
+        .miso_dlen = ESP32S2_SPI_REG_BASE + 0x28,
+    }
+};
+
+static uint32_t s_flash_write_size = 0;
+static target_chip_t s_target = ESP_UNKNOWN_CHIP;
+static const target_registers_t *s_reg = &registers[ESP32S2_CHIP];
+
+static const date_registers_t s_date_regs[ESP_MAX_CHIP] = {
+    // ESP8266
+    {
+        .reg_1 = 0x00062000,
+        .reg_2 = 0,
+    },
+    // ESP32
+    {
+        .reg_1 = 0x15122500,
+        .reg_2 = 0,
+    },
+    // ESP32S2
+    {
+        .reg_1 = 0x00000500,
+        .reg_2 = 0x19031400,
+    }
+};
 
 #if MD5_ENABLED
 
@@ -142,20 +167,25 @@ static uint32_t timeout_per_mb(uint32_t size_bytes, uint32_t time_per_mb)
     return MAX(timeout, DEFAULT_FLASH_TIMEOUT);
 }
 
-static esp_loader_error_t detect_chip(void)
+static esp_loader_error_t detect_chip(target_chip_t *target)
 {
     uint32_t reg_1, reg_2;
 
     RETURN_ON_ERROR( esp_loader_read_register(UART_DATE_REG_ADDR,  &reg_1) );
     RETURN_ON_ERROR( esp_loader_read_register(UART_DATE_REG2_ADDR, &reg_2) );
 
-    if (date_regs.reg_1 == reg_1 && (date_regs.reg_2 == 0 || date_regs.reg_2 == reg_2)) {
-        return ESP_LOADER_SUCCESS;
-    } else {
-        return ESP_LOADER_ERROR_INVALID_TARGET;
+    for (int chip = 0; chip < ESP_MAX_CHIP; chip++) {
+        const date_registers_t *r = &s_date_regs[chip];
+        if (r->reg_1 == reg_1 && (r->reg_2 == 0 || r->reg_2 == reg_2)) {
+            *target = (target_chip_t)chip;
+            return ESP_LOADER_SUCCESS;
+        }
     }
+
+    return ESP_LOADER_ERROR_INVALID_TARGET;
 }
 
+
 esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args)
 {
     esp_loader_error_t err;
@@ -176,35 +206,38 @@ esp_loader_error_t esp_loader_connect(esp_loader_connect_args_t *connect_args)
         }
     } while (err != ESP_LOADER_SUCCESS);
 
-    RETURN_ON_ERROR( detect_chip() );
+    RETURN_ON_ERROR( detect_chip(&s_target) );
 
-#ifndef TARGET_ESP8266
-    loader_port_start_timer(DEFAULT_TIMEOUT);
-    err = loader_spi_attach_cmd(connect_args->spi_pin_config.val);
-#else
-    err = loader_flash_begin_cmd(0, 0, 0, 0);
-#endif
+    s_reg = &registers[s_target];
+
+    if (s_target == ESP8266_CHIP) {
+        err = loader_flash_begin_cmd(0, 0, 0, 0, s_target);
+    } else {
+        loader_port_start_timer(DEFAULT_TIMEOUT);
+        err = loader_spi_attach_cmd(connect_args->spi_pin_config.val);
+    }
 
     return err;
 }
 
-#ifndef TARGET_ESP8266
+target_chip_t esp_loader_get_target(void)
+{
+    return s_target;
+}
 
 static esp_loader_error_t spi_set_data_lengths(size_t mosi_bits, size_t miso_bits)
 {
     if (mosi_bits > 0) {
-        RETURN_ON_ERROR( esp_loader_write_register(SPI_MOSI_DLEN_REG, mosi_bits - 1) );
+        RETURN_ON_ERROR( esp_loader_write_register(s_reg->mosi_dlen, mosi_bits - 1) );
     }
     if (miso_bits > 0) {
-        RETURN_ON_ERROR( esp_loader_write_register(SPI_MISO_DLEN_REG, miso_bits - 1) );
+        RETURN_ON_ERROR( esp_loader_write_register(s_reg->miso_dlen, miso_bits - 1) );
     }
 
     return ESP_LOADER_SUCCESS;
 }
 
-#else
-
-static esp_loader_error_t spi_set_data_lengths(size_t mosi_bits, size_t miso_bits)
+static esp_loader_error_t spi_set_data_lengths_8266(size_t mosi_bits, size_t miso_bits)
 {
     uint32_t mosi_bitlen_shift = 17;
     uint32_t miso_bitlen_shift = 8;
@@ -212,11 +245,9 @@ static esp_loader_error_t spi_set_data_lengths(size_t mosi_bits, size_t miso_bit
     uint32_t miso_mask = (miso_bits == 0) ? 0 : miso_bits - 1;
     uint32_t usr_reg = (miso_mask << miso_bitlen_shift) | (mosi_mask << mosi_bitlen_shift);
 
-    return esp_loader_write_register(SPI_USR1_REG, usr_reg);
+    return esp_loader_write_register(s_reg->usr1, usr_reg);
 }
 
-#endif
-
 static esp_loader_error_t spi_flash_command(spi_flash_cmd_t cmd, void *data_tx, size_t tx_size, void *data_rx, size_t rx_size)
 {
     assert(rx_size <= 32); // Reading more than 32 bits back from a SPI flash operation is unsupported
@@ -231,10 +262,14 @@ static esp_loader_error_t spi_flash_command(spi_flash_cmd_t cmd, void *data_tx,
     // Save SPI configuration
     uint32_t old_spi_usr;
     uint32_t old_spi_usr2;
-    RETURN_ON_ERROR( esp_loader_read_register(SPI_USR_REG, &old_spi_usr) );
-    RETURN_ON_ERROR( esp_loader_read_register(SPI_USR2_REG, &old_spi_usr2) );
+    RETURN_ON_ERROR( esp_loader_read_register(s_reg->usr, &old_spi_usr) );
+    RETURN_ON_ERROR( esp_loader_read_register(s_reg->usr2, &old_spi_usr2) );
 
-    RETURN_ON_ERROR( spi_set_data_lengths(tx_size, rx_size) );
+    if (s_target == ESP8266_CHIP) {
+        RETURN_ON_ERROR( spi_set_data_lengths_8266(tx_size, rx_size) );
+    } else {
+        RETURN_ON_ERROR( spi_set_data_lengths(tx_size, rx_size) );
+    }
 
     uint32_t usr_reg_2 = (7 << CMD_LEN_SHIFT) | cmd;
     uint32_t usr_reg = SPI_USR_CMD;
@@ -245,16 +280,16 @@ static esp_loader_error_t spi_flash_command(spi_flash_cmd_t cmd, void *data_tx,
         usr_reg |= SPI_USR_MOSI;
     }
 
-    RETURN_ON_ERROR( esp_loader_write_register(SPI_USR_REG, usr_reg) );
-    RETURN_ON_ERROR( esp_loader_write_register(SPI_USR2_REG, usr_reg_2 ) );
+    RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr, usr_reg) );
+    RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr2, usr_reg_2 ) );
 
     if (tx_size == 0) {
         // clear data register before we read it
-        RETURN_ON_ERROR( esp_loader_write_register(SPI_W0_REG, 0) );
+        RETURN_ON_ERROR( esp_loader_write_register(s_reg->w0, 0) );
     } else {
         uint32_t *data = (uint32_t *)data_tx;
         uint32_t words_to_write = MIN((tx_size + 31) / 8 * 4, 1);
-        uint32_t data_reg_addr = SPI_W0_REG;
+        uint32_t data_reg_addr = s_reg->w0;
 
         while (words_to_write--) {
             uint32_t word = *data++;
@@ -263,12 +298,12 @@ static esp_loader_error_t spi_flash_command(spi_flash_cmd_t cmd, void *data_tx,
         }
     }
 
-    RETURN_ON_ERROR( esp_loader_write_register(SPI_CMD_REG, SPI_CMD_USR) );
+    RETURN_ON_ERROR( esp_loader_write_register(s_reg->cmd, SPI_CMD_USR) );
 
     uint32_t trials = 10;
     while (trials--) {
         uint32_t cmd_reg;
-        RETURN_ON_ERROR( esp_loader_read_register(SPI_CMD_REG, &cmd_reg) );
+        RETURN_ON_ERROR( esp_loader_read_register(s_reg->cmd, &cmd_reg) );
         if ((cmd_reg & SPI_CMD_USR) == 0) {
             break;
         }
@@ -278,11 +313,11 @@ static esp_loader_error_t spi_flash_command(spi_flash_cmd_t cmd, void *data_tx,
         return ESP_LOADER_ERROR_TIMEOUT;
     }
 
-    RETURN_ON_ERROR( esp_loader_read_register(SPI_W0_REG, data_rx) );
+    RETURN_ON_ERROR( esp_loader_read_register(s_reg->w0, data_rx) );
 
     // Restore SPI configuration
-    RETURN_ON_ERROR( esp_loader_write_register(SPI_USR_REG, old_spi_usr) );
-    RETURN_ON_ERROR( esp_loader_write_register(SPI_USR2_REG, old_spi_usr2) );
+    RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr, old_spi_usr) );
+    RETURN_ON_ERROR( esp_loader_write_register(s_reg->usr2, old_spi_usr2) );
 
     return ESP_LOADER_SUCCESS;
 }
@@ -323,7 +358,7 @@ esp_loader_error_t esp_loader_flash_start(uint32_t offset, uint32_t image_size,
     init_md5(offset, image_size);
 
     loader_port_start_timer(timeout_per_mb(erase_size, ERASE_REGION_TIMEOUT_PER_MB));
-    return loader_flash_begin_cmd(offset, erase_size, block_size, blocks_to_write);
+    return loader_flash_begin_cmd(offset, erase_size, block_size, blocks_to_write, s_target);
 }
 
 
@@ -368,14 +403,16 @@ esp_loader_error_t esp_loader_write_register(uint32_t address, uint32_t reg_valu
     return loader_write_reg_cmd(address, reg_value, 0xFFFFFFFF, 0);
 }
 
-#ifndef TARGET_ESP8266
 esp_loader_error_t esp_loader_change_baudrate(uint32_t baudrate)
 {
+    if (s_target == ESP8266_CHIP) {
+        return ESP_LOADER_ERROR_UNSUPPORTED_FUNC;
+    }
+
     loader_port_start_timer(DEFAULT_TIMEOUT);
 
     return loader_change_baudrate_cmd(baudrate);
 }
-#endif
 
 #if MD5_ENABLED
 
@@ -399,6 +436,10 @@ static void hexify(const uint8_t raw_md5[16], uint8_t hex_md5_out[32])
 
 esp_loader_error_t esp_loader_flash_verify(void)
 {
+    if (s_target == ESP8266_CHIP) {
+        return ESP_LOADER_ERROR_UNSUPPORTED_FUNC;
+    }
+
     uint8_t raw_md5[16];
     uint8_t hex_md5[MD5_SIZE + 1];
     uint8_t received_md5[MD5_SIZE + 1];

+ 13 - 9
src/serial_comm.c

@@ -94,11 +94,10 @@ static esp_loader_error_t SLIP_receive_packet(uint8_t *buff, uint32_t size)
 
     RETURN_ON_ERROR( SLIP_receive_data(&buff[1], size - 1) );
 
-    // Delimiter
-    RETURN_ON_ERROR( serial_read(&ch, 1) );
-    if (ch != DELIMITER) {
-        return ESP_LOADER_ERROR_INVALID_RESPONSE;
-    }
+    // Wait for delimiter
+    do {
+        RETURN_ON_ERROR( serial_read(&ch, 1) );
+    } while (ch != DELIMITER);
 
     return ESP_LOADER_SUCCESS;
 }
@@ -237,24 +236,29 @@ static esp_loader_error_t check_response(command_t cmd, uint32_t *reg_value, voi
 esp_loader_error_t loader_flash_begin_cmd(uint32_t offset,
                                           uint32_t erase_size,
                                           uint32_t block_size,
-                                          uint32_t blocks_to_write)
+                                          uint32_t blocks_to_write,
+                                          target_chip_t target)
 {
+    size_t encription = target == ESP32S2_CHIP ? 0 : sizeof(uint32_t);
+
     begin_command_t begin_cmd = {
         .common = {
             .direction = WRITE_DIRECTION,
             .command = FLASH_BEGIN,
-            .size = CMD_SIZE(begin_cmd),
+            .size = CMD_SIZE(begin_cmd) - encription,
             .checksum = 0
         },
         .erase_size = erase_size,
         .packet_count = blocks_to_write,
         .packet_size = block_size,
-        .offset = offset
+        .offset = offset,
+        .encrypted = 0
     };
 
     s_sequence_number = 0;
 
-    return send_cmd(&begin_cmd, sizeof(begin_cmd), NULL);
+
+    return send_cmd(&begin_cmd, sizeof(begin_cmd) - encription, NULL);
 }
 
 

+ 4 - 0
test/CMakeLists.txt

@@ -14,3 +14,7 @@ if( QEMU_TEST )
 else()
     target_sources(${PROJECT_NAME} PRIVATE serial_io_mock.cpp test.cpp)
 endif()
+
+if(DEFINED MD5_ENABLED)
+    target_compile_definitions(${PROJECT_NAME} PRIVATE -DMD5_ENABLED=${MD5_ENABLED})
+endif()

+ 2 - 2
test/README.md

@@ -28,12 +28,12 @@ sh run_qemu.sh
 
 Run qemu test
 ```
-cmake .. -DQEMU_TEST=True && make && ./serial_flasher_test
+cmake .. -DQEMU_TEST=True && cmake --build . && ./serial_flasher_test
 ```
 
 ### Host test
 
 Run host test
 ```
-cmake .. -DQEMU_TEST=False && make && ./serial_flasher_test
+cmake .. -DQEMU_TEST=False && cmake --build . && ./serial_flasher_test
 ```

+ 1 - 0
test/qemu_test.cpp

@@ -34,6 +34,7 @@ TEST_CASE( "Can connect " )
     esp_loader_connect_args_t connect_config = ESP_LOADER_CONNECT_DEFAULT();
 
     ESP_ERR_CHECK( esp_loader_connect(&connect_config) );
+    REQUIRE( esp_loader_get_target() == ESP32_CHIP );
 }
 
 

+ 70 - 188
test/test.cpp

@@ -33,15 +33,25 @@ using namespace std;
 #define SLIP_ENCODED_PACKET 0xdb, 0xdd, 'a', 'b', 'c', 0xdb, 0xdc, 0xdb, \
                             0xdd, 'd', 'e', 0xdb, 0xdc, 'f', 0xdb, 0xdd
 
+#define REQUIRE_SUCCESS(exp) REQUIRE( (exp) == ESP_LOADER_SUCCESS )
 
-// Helper function for debugging.  
+// Helper function for debugging.
 __attribute__((unused))
-static void arrays_match(int8_t *array_1, int8_t *array_2, size_t size)
+static void arrays_match(void *array_1, void *array_2, size_t size)
 {
+    int8_t *arr_1 = (int8_t *)array_1;
+    int8_t *arr_2 = (int8_t *)array_2;
+
     for (size_t i = 0; i < size; i++) {
-        if (array_1[i] != array_2[i]) {
-            printf("\nArrays do NOT match on index: %lu, with values %0x, %0x \n",
-                   i, array_1[i], array_2[i]);
+        if (arr_1[i] != arr_2[i]) {
+            printf("\nArrays do NOT match on index: %lu, with values %02hhx, %02hhx \n",
+                   i, arr_1[i], arr_2[i]);
+
+            printf("\nExpected: ");
+            for (uint32_t j = 0; j < size; j++) { printf("%02hhx ", arr_1[j]); }
+            printf("\nActual:   ");
+            for (uint32_t j = 0; j < size; j++) { printf("%02hhx ", arr_2[j]); }
+
             return;
         }
     }
@@ -59,8 +69,6 @@ struct __attribute__((packed)) expected_response {
         data.common.value = 0;
         data.status.failed = STATUS_SUCCESS;
         data.status.error = 0;
-        data.status.reserved_0 = 0;
-        data.status.reserved_1 = 0;
     }
 
     response_t data;
@@ -82,106 +90,6 @@ expected_response read_reg_response(READ_REG);
 expected_response attach_response(SPI_ATTACH);
 expected_response sync_response(SYNC);
 
-
-struct __attribute__((packed)) flash_start_frame {
-    
-    uint8_t delimiter_1 = 0xc0;
-    write_spi_command_t write_spi_command = {
-        .common = {
-            .direction = WRITE_DIRECTION,
-            .command = SPI_SET_PARAMS,
-            .size = 24,
-            .checksum = 0
-        },
-        .id = 0,
-        .total_size = 0x00400000, // Assume 4MB flash size
-        .block_size = 64 * 1024,
-        .sector_size = 4 * 1024,
-        .page_size = 0x100,
-        .status_mask = 0xFFFF,
-    };
-    uint8_t delimiter_2 = 0xc0;
-
-    uint8_t delimiter_3 = 0xc0;
-    begin_command_t begin_cmd  = {
-        .common = {
-            .direction = WRITE_DIRECTION,
-            .command = FLASH_BEGIN,
-            .size = 16,
-            .checksum = 0,
-        },
-        .erase_size = 0,
-        .packet_count = 0,
-        .packet_size = 0,
-        .offset = 0,
-    };
-    uint8_t delimiter_4 = 0xc0;
-
-    flash_start_frame(uint32_t offset, uint32_t image_size, uint32_t block_size)
-    {
-        uint32_t blocks_to_write = (image_size + block_size - 1) / block_size;
-
-        begin_cmd.packet_count = blocks_to_write;
-        begin_cmd.erase_size = blocks_to_write * block_size;
-        begin_cmd.packet_size = block_size;
-        begin_cmd.offset = offset;
-    }
-};
-
-
-struct __attribute__((packed)) flash_finish_frame {
-    uint8_t delimiter_1 = 0xc0;
-    flash_end_command_t end_cmd = {
-        .common = {
-            .direction = WRITE_DIRECTION,
-            .command = FLASH_END,
-            .size = 4,
-            .checksum = 0,
-        },
-        .stay_in_loader = 1,
-    };
-    uint8_t delimiter_2 = 0xc0;
-};
-
-
-template<size_t PAYLOAD_SIZE>
-struct __attribute__((packed)) flash_write_frame {
-    uint8_t delimiter_1 = 0xc0;
-    data_command_t data_cmd = {
-        .common = {
-            .direction = WRITE_DIRECTION,
-            .command = FLASH_DATA,
-            .size = 16 + PAYLOAD_SIZE,
-            .checksum = 0xef,
-        },
-        .data_size = PAYLOAD_SIZE,
-        .sequence_number = 0,
-        .zero_0 = 0,
-        .zero_1 = 0,
-    };
-    array<uint8_t, PAYLOAD_SIZE> payload;
-    uint8_t delimiter_2 = 0xc0;
-
-    flash_write_frame()
-    {
-        payload.fill(0xFF);
-        data_cmd.sequence_number = seq_num++;
-    }
-
-    ~flash_write_frame()
-    {
-        seq_num--;
-    }
-
-    void fill(uint8_t data, size_t size = PAYLOAD_SIZE)
-    {
-        fill_n(payload.data(), size, data);
-    }
-
-    static uint32_t seq_num;
-};
-
-
 const uint32_t reg_address = 0x1000;
 const uint32_t reg_value = 55;
 
@@ -203,87 +111,32 @@ struct __attribute__((packed)) write_reg_cmd_response {
 };
 
 
-template<size_t PAYLOAD_SIZE>
-uint32_t flash_write_frame<PAYLOAD_SIZE>::seq_num = 0;
-
-
-size_t queue_responses_to_ignore()
-{
-    auto flash_id_response = read_reg_response;
-    flash_id_response.data.common.value = 0x16 << 16; // emulate 4MB flash
-
-    queue_response(read_reg_response);  // Save SPI_USR_REG 
-    queue_response(read_reg_response);  // Save SPI_USR2_REG
-    queue_response(write_reg_response); // SPI_MISO_DLEN_REG
-    queue_response(write_reg_response); // Set new SPI_USR_REG
-    queue_response(write_reg_response); // Set new SPI_USR2_REG
-    queue_response(write_reg_response); // Zero out SPI_W0_REG
-    queue_response(write_reg_response); // SPI_CMD_REG Start transaction
-    queue_response(read_reg_response);  // SPI_CMD_REG Transaction done
-    queue_response(flash_id_response);  // SPI_W0_REG Read data
-    queue_response(write_reg_response); // Restore SPI_USR_REG
-    queue_response(write_reg_response); // Restore SPI_USR2_REG
- 
-    // Delimiters are added manually to every packet (+2).
-    size_t bytes_to_ignore = (sizeof(write_reg_command_t) + 2) * 7 + (sizeof(read_reg_command_t) + 2) * 4 ;
-
-    return bytes_to_ignore;
-}
-
-TEST_CASE( "Large payload that does not fit BLOCK_SIZE is split into \
-            multiple data frames. Last data frame is padded with 0xFF" )
-{
-    const uint32_t BLOCK_SIZE = 1024;
-
-    uint8_t data[BLOCK_SIZE];
-    memset(data, 0x11, BLOCK_SIZE);
-
-    flash_write_frame<BLOCK_SIZE> expected_data[3];
-    expected_data[0].fill(0x11);
-    expected_data[1].fill(0x11);
-    expected_data[2].fill(0x11, 200);
-
-    flash_start_frame expected_start(0, sizeof(data) * 3, BLOCK_SIZE);
-
-    // Check flash start operation 
-    clear_buffers();
-    size_t bytes_to_ignore = queue_responses_to_ignore();
-
-    queue_response(set_params_response);
-    queue_response(flash_begin_response);
-
-    REQUIRE ( esp_loader_flash_start(0, sizeof(data) * 3, BLOCK_SIZE) == ESP_LOADER_SUCCESS );
-    // Ignore read/write register commands in this test.
-    REQUIRE( memcmp(write_buffer_data() + bytes_to_ignore, &expected_start, sizeof(expected_start)) == 0 );
-
-
-    // Check flash write operation 
-    clear_buffers();
-    queue_response(flash_data_response);
-    queue_response(flash_data_response);
-    queue_response(flash_data_response);
-
-    REQUIRE( esp_loader_flash_write(data, sizeof(data)) == ESP_LOADER_SUCCESS );
-    REQUIRE( esp_loader_flash_write(data, sizeof(data)) == ESP_LOADER_SUCCESS );
-    REQUIRE( esp_loader_flash_write(data, 200) == ESP_LOADER_SUCCESS );
-
-    REQUIRE( memcmp(write_buffer_data(), &expected_data, sizeof(expected_data)) == 0 );
-}
-
-
-TEST_CASE( "Can connect within specified time " )
+void queue_connect_response(uint32_t date_reg_1 = 0x15122500,
+                            uint32_t date_reg_2 = 0, 
+                            target_chip_t target = ESP32_CHIP)
 {
     // Set date registers used for detection of attached chip
     auto uart_date_reg_1 = read_reg_response;
     auto uart_date_reg_2 = read_reg_response;
-    uart_date_reg_1.data.common.value = 0x15122500;
-    uart_date_reg_2.data.common.value = 0;
+    uart_date_reg_1.data.common.value = date_reg_1;
+    uart_date_reg_2.data.common.value = date_reg_2;
 
     clear_buffers();
     queue_response(sync_response);
     queue_response(uart_date_reg_1);
     queue_response(uart_date_reg_2);
-    queue_response(attach_response);
+
+    if (target == ESP8266_CHIP) {
+        queue_response(flash_begin_response);
+    } else {
+        queue_response(attach_response);
+    }
+
+}
+
+TEST_CASE( "Can connect within specified time " )
+{
+    queue_connect_response();
 
     esp_loader_connect_args_t connect_config = {
         .sync_timeout = 10,
@@ -292,7 +145,7 @@ TEST_CASE( "Can connect within specified time " )
 
     SECTION( "Can connect" ) {
         serial_set_time_delay(5);
-        REQUIRE ( esp_loader_connect(&connect_config) == ESP_LOADER_SUCCESS );
+        REQUIRE_SUCCESS( esp_loader_connect(&connect_config) );
     }
 
     SECTION( "Timeout error is returned when timeout expires" ) {
@@ -303,15 +156,44 @@ TEST_CASE( "Can connect within specified time " )
     SECTION( "Can connect after several trials within specified time" ) {
         connect_config.trials = 5;
         serial_set_time_delay(40);
-        REQUIRE ( esp_loader_connect(&connect_config) == ESP_LOADER_SUCCESS );
+        REQUIRE_SUCCESS( esp_loader_connect(&connect_config) );
 
         serial_set_time_delay(60);
-        REQUIRE ( esp_loader_connect(&connect_config) == ESP_LOADER_ERROR_TIMEOUT );
+        REQUIRE( esp_loader_connect(&connect_config) == ESP_LOADER_ERROR_TIMEOUT );
     }
 
     serial_set_time_delay(0);
 }
 
+
+TEST_CASE( "Can detect attached target" )
+{
+    esp_loader_connect_args_t connect_config = ESP_LOADER_CONNECT_DEFAULT();
+
+    SECTION( "Can detect ESP32" ) {
+        queue_connect_response(0x15122500, 0);
+        REQUIRE_SUCCESS( esp_loader_connect(&connect_config) );
+        REQUIRE( esp_loader_get_target() == ESP32_CHIP );
+    }
+
+    SECTION( "Can detect ESP32S2" ) {
+        queue_connect_response(0x00000500, 0x19031400);
+        REQUIRE_SUCCESS( esp_loader_connect(&connect_config) );
+        REQUIRE( esp_loader_get_target() == ESP32S2_CHIP );
+    }
+
+    SECTION( "Can detect ESP8266" ) {
+        queue_connect_response(0x00062000, 0, ESP8266_CHIP);
+        REQUIRE_SUCCESS( esp_loader_connect(&connect_config) );
+        REQUIRE( esp_loader_get_target() == ESP8266_CHIP );
+    }
+
+    SECTION( "Can detect unknown chip" ) {
+        queue_connect_response(0xaa, 0xbb);
+        REQUIRE( esp_loader_connect(&connect_config) == ESP_LOADER_ERROR_INVALID_TARGET);
+    }
+}
+
 TEST_CASE( "Register can be read correctly" )
 {
     clear_buffers();
@@ -320,7 +202,7 @@ TEST_CASE( "Register can be read correctly" )
 
     queue_response(read_reg_response);
 
-    REQUIRE( esp_loader_read_register(0, &reg_value) == ESP_LOADER_SUCCESS );
+    REQUIRE_SUCCESS( esp_loader_read_register(0, &reg_value) );
 
     REQUIRE( reg_value == 55 );
 }
@@ -334,7 +216,7 @@ TEST_CASE( "Register can be written correctly" )
     clear_buffers();
     queue_response(write_reg_response);
 
-    REQUIRE( esp_loader_write_register(reg_address, reg_value) == ESP_LOADER_SUCCESS );
+    REQUIRE_SUCCESS( esp_loader_write_register(reg_address, reg_value) );
 
     REQUIRE( memcmp(write_buffer_data(), &expected, sizeof(expected)) == 0 );
 }
@@ -343,7 +225,7 @@ TEST_CASE( "Register can be written correctly" )
 
 TEST_CASE ( "SLIP is encoded correctly" )
 {
-    loader_flash_begin_cmd(0, 0, 0, 0); // To reset sequence number counter
+    loader_flash_begin_cmd(0, 0, 0, 0, ESP32_CHIP); // To reset sequence number counter
 
     uint8_t data[] = { TEST_SLIP_PACKET };
 
@@ -367,7 +249,7 @@ TEST_CASE ( "SLIP is encoded correctly" )
     clear_buffers();
     queue_response(flash_data_response);
 
-    REQUIRE( loader_flash_data_cmd(data, sizeof(data)) == ESP_LOADER_SUCCESS );
+    REQUIRE_SUCCESS( loader_flash_data_cmd(data, sizeof(data)) );
 
     REQUIRE( memcmp(write_buffer_data(), expected, sizeof(expected)) == 0 );
 }
@@ -392,7 +274,7 @@ TEST_CASE( "Sync command is constructed correctly" )
     clear_buffers();
     queue_response(sync_response);
 
-    REQUIRE( loader_sync_cmd() == ESP_LOADER_SUCCESS );
+    REQUIRE_SUCCESS( loader_sync_cmd() );
 
     REQUIRE( memcmp(write_buffer_data(), expected, sizeof(expected)) == 0 );
 }
@@ -412,7 +294,7 @@ TEST_CASE( "Register can be read and decoded correctly" )
 TEST_CASE( "Received response (in SLIP format) is decoded correctly" )
 {
     clear_buffers();
-    read_reg_response.data.common.value = 0xC0BD; // C0, BD has to be replaced 
+    read_reg_response.data.common.value = 0xC0BD; // C0, BD has to be replaced
     queue_response(read_reg_response);
 
     uint32_t reg_value = 0;