spi_flash: mocking should be possible now

The following three headers will be mockes:
* esp_flash.h
* esp_spi_flash.h
* esp_partition.h
* counter functions live in own header
* add spi_flash sim dir for esp_err.h to Unity
* modified gen_esp_err_to_name.py to ignore
  sim/ dir in spi_flash component

Add cmock .yaml config file
Add spi hal header until soc can mock the hal
  layer as well.
This commit is contained in:
Jakob Hasse 2020-09-01 17:06:22 +08:00 committed by Fu Hanxi
parent 52093fa4ef
commit c233ce0449
8 changed files with 491 additions and 93 deletions

View File

@ -1,63 +1,125 @@
if(BOOTLOADER_BUILD)
if(CONFIG_IDF_TARGET_ESP32)
# ESP32 Bootloader needs SPIUnlock from this file, but doesn't
# need other parts of this component
set(srcs "esp32/spi_flash_rom_patch.c")
elseif(CONFIG_IDF_TARGET_ESP32S2)
set(srcs "esp32s2/spi_flash_rom_patch.c")
elseif(CONFIG_IDF_TARGET_ESP32S3)
set(srcs "esp32s3/spi_flash_rom_patch.c")
idf_build_get_property(spi_flash_mock CONFIG_SPI_FLASH_MOCK)
idf_build_get_property(target IDF_TARGET)
if(${spi_flash_mock})
message(STATUS "building SPI FLASH MOCKS")
set(IDF_PATH $ENV{IDF_PATH})
set(CMOCK_DIR "${IDF_PATH}/components/cmock/CMock")
set(MOCK_GEN_DIR "${CMAKE_CURRENT_BINARY_DIR}/mocks")
file(MAKE_DIRECTORY ${MOCK_GEN_DIR})
set(MOCK_OUTPUT
"${MOCK_GEN_DIR}/Mockesp_partition.c" "${MOCK_GEN_DIR}/Mockesp_partition.h"
"${MOCK_GEN_DIR}/Mockesp_flash.c" "${MOCK_GEN_DIR}/Mockesp_flash.h"
"${MOCK_GEN_DIR}/Mockesp_spi_flash.c" "${MOCK_GEN_DIR}/Mockesp_spi_flash.h")
set(MOCK_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/include/esp_partition.h
${CMAKE_CURRENT_SOURCE_DIR}/include/esp_flash.h
${CMAKE_CURRENT_SOURCE_DIR}/include/esp_spi_flash.h
)
set(ENV{UNITY_DIR} "$ENV{IDF_PATH}/components/cmock/CMock")
set(include_dirs
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${MOCK_GEN_DIR}")
set(srcs "${MOCK_GEN_DIR}/Mockesp_partition.c"
"${MOCK_GEN_DIR}/Mockesp_spi_flash.c"
"${MOCK_GEN_DIR}/Mockesp_flash.c")
if(${target} STREQUAL "linux")
list(APPEND include_dirs
"${CMAKE_CURRENT_SOURCE_DIR}/../spi_flash/sim/stubs/soc/include"
"${CMAKE_CURRENT_SOURCE_DIR}/../spi_flash/sim/stubs/xtensa")
endif()
set(cache_srcs "")
set(priv_requires bootloader_support soc)
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS ${include_dirs}
REQUIRES cmock)
# This command builds the mocks.
# First, environment variable UNITY_DIR is set. This is necessary to prevent unity from looking in its own submodule
# which doesn't work in our CI yet...
# The rest is a straight forward call to cmock.rb, consult cmock's documentation for more information.
add_custom_command(
OUTPUT ${MOCK_OUTPUT}
COMMAND ${CMAKE_COMMAND} -E env "UNITY_DIR=${IDF_PATH}/components/unity/unity"
ruby
${CMOCK_DIR}/lib/cmock.rb
-o${CMAKE_CURRENT_SOURCE_DIR}/mock/mock_config.yaml
${MOCK_HEADERS}
)
else()
set(cache_srcs
"cache_utils.c"
"flash_mmap.c"
"flash_ops.c"
"${IDF_TARGET}/flash_ops_${IDF_TARGET}.c"
)
set(srcs
"partition.c")
if(BOOTLOADER_BUILD)
if(CONFIG_IDF_TARGET_ESP32)
# ESP32 Bootloader needs SPIUnlock from this file, but doesn't
# need other parts of this component
set(srcs "esp32/spi_flash_rom_patch.c")
elseif(CONFIG_IDF_TARGET_ESP32S2)
set(srcs "esp32s2/spi_flash_rom_patch.c")
elseif(CONFIG_IDF_TARGET_ESP32S3)
set(srcs "esp32s3/spi_flash_rom_patch.c")
else()
# but on other platforms no source files are needed for bootloader
set(srcs)
endif()
set(cache_srcs "")
set(priv_requires bootloader_support soc)
else()
set(cache_srcs
"cache_utils.c"
"flash_mmap.c"
"flash_ops.c"
"${IDF_TARGET}/flash_ops_${IDF_TARGET}.c"
)
set(srcs
"partition.c")
if(CONFIG_IDF_TARGET_ESP32)
if(CONFIG_IDF_TARGET_ESP32)
list(APPEND srcs
"esp32/spi_flash_rom_patch.c")
elseif(CONFIG_IDF_TARGET_ESP32S2)
list(APPEND srcs
"esp32s2/spi_flash_rom_patch.c")
elseif(CONFIG_IDF_TARGET_ESP32S3)
list(APPEND srcs
"esp32s3/spi_flash_rom_patch.c")
endif()
# New implementation after IDF v4.0
list(APPEND srcs
"esp32/spi_flash_rom_patch.c")
elseif(CONFIG_IDF_TARGET_ESP32S2)
list(APPEND srcs
"esp32s2/spi_flash_rom_patch.c")
elseif(CONFIG_IDF_TARGET_ESP32S3)
list(APPEND srcs
"esp32s3/spi_flash_rom_patch.c")
"spi_flash_chip_drivers.c"
"spi_flash_chip_generic.c"
"spi_flash_chip_issi.c"
"spi_flash_chip_mxic.c"
"spi_flash_chip_gd.c"
"spi_flash_chip_winbond.c"
"memspi_host_driver.c")
list(APPEND cache_srcs
"esp_flash_api.c"
"esp_flash_spi_init.c"
"spi_flash_os_func_app.c"
"spi_flash_os_func_noos.c")
list(APPEND srcs ${cache_srcs})
set(priv_requires bootloader_support app_update soc esp_ipc)
endif()
# New implementation after IDF v4.0
list(APPEND srcs
"spi_flash_chip_drivers.c"
"spi_flash_chip_generic.c"
"spi_flash_chip_issi.c"
"spi_flash_chip_mxic.c"
"spi_flash_chip_gd.c"
"spi_flash_chip_winbond.c"
"memspi_host_driver.c")
idf_component_register(SRCS "${srcs}"
REQUIRES hal
PRIV_REQUIRES "${priv_requires}"
INCLUDE_DIRS include
PRIV_INCLUDE_DIRS private_include
LDFRAGMENTS linker.lf)
list(APPEND cache_srcs
"esp_flash_api.c"
"esp_flash_spi_init.c"
"spi_flash_os_func_app.c"
"spi_flash_os_func_noos.c")
# Avoid cache miss by unexpected inlineing when built by -Os
set_source_files_properties(${cache_srcs} PROPERTIES COMPILE_FLAGS
"-fno-inline-functions -fno-inline-small-functions -fno-inline-functions-called-once")
list(APPEND srcs ${cache_srcs})
set(priv_requires bootloader_support app_update soc esp_ipc)
endif()
idf_component_register(SRCS "${srcs}"
REQUIRES hal
PRIV_REQUIRES "${priv_requires}"
INCLUDE_DIRS include
PRIV_INCLUDE_DIRS private_include
LDFRAGMENTS linker.lf)
# Avoid cache miss by unexpected inlineing when built by -Os
set_source_files_properties(${cache_srcs} PROPERTIES COMPILE_FLAGS
"-fno-inline-functions -fno-inline-small-functions -fno-inline-functions-called-once")

View File

@ -20,6 +20,7 @@
#include <stddef.h>
#include "esp_err.h"
#include "sdkconfig.h"
#include "esp_spi_flash_counters.h"
#ifdef __cplusplus
extern "C" {
@ -419,43 +420,6 @@ extern const spi_flash_guard_funcs_t g_flash_guard_default_ops;
*/
extern const spi_flash_guard_funcs_t g_flash_guard_no_os_ops;
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
/**
* Structure holding statistics for one type of operation
*/
typedef struct {
uint32_t count; // number of times operation was executed
uint32_t time; // total time taken, in microseconds
uint32_t bytes; // total number of bytes
} spi_flash_counter_t;
typedef struct {
spi_flash_counter_t read;
spi_flash_counter_t write;
spi_flash_counter_t erase;
} spi_flash_counters_t;
/**
* @brief Reset SPI flash operation counters
*/
void spi_flash_reset_counters(void);
/**
* @brief Print SPI flash operation counters
*/
void spi_flash_dump_counters(void);
/**
* @brief Return current SPI flash operation counters
*
* @return pointer to the spi_flash_counters_t structure holding values
* of the operation counters
*/
const spi_flash_counters_t* spi_flash_get_counters(void);
#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,66 @@
// Copyright 2015-2016 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
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
#include "sdkconfig.h"
#if CONFIG_SPI_FLASH_ENABLE_COUNTERS
#ifdef __cplusplus
extern "C" {
#endif
/**
* Structure holding statistics for one type of operation
*/
typedef struct {
uint32_t count; // number of times operation was executed
uint32_t time; // total time taken, in microseconds
uint32_t bytes; // total number of bytes
} spi_flash_counter_t;
typedef struct {
spi_flash_counter_t read;
spi_flash_counter_t write;
spi_flash_counter_t erase;
} spi_flash_counters_t;
/**
* @brief Reset SPI flash operation counters
*/
void spi_flash_reset_counters(void);
/**
* @brief Print SPI flash operation counters
*/
void spi_flash_dump_counters(void);
/**
* @brief Return current SPI flash operation counters
*
* @return pointer to the spi_flash_counters_t structure holding values
* of the operation counters
*/
const spi_flash_counters_t* spi_flash_get_counters(void);
#ifdef __cplusplus
}
#endif
#endif //CONFIG_SPI_FLASH_ENABLE_COUNTERS

View File

@ -0,0 +1,7 @@
:cmock:
:plugins:
- expect
- expect_any_args
- return_thru_ptr
- array
- callback

View File

@ -0,0 +1,149 @@
// Copyright 2015-2016 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
#include <stdint.h>
#include <stdio.h>
#include <assert.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef int32_t esp_err_t;
/* Definitions for error constants. */
#define ESP_OK 0 /*!< esp_err_t value indicating success (no error) */
#define ESP_FAIL -1 /*!< Generic esp_err_t code indicating failure */
#define ESP_ERR_NO_MEM 0x101 /*!< Out of memory */
#define ESP_ERR_INVALID_ARG 0x102 /*!< Invalid argument */
#define ESP_ERR_INVALID_STATE 0x103 /*!< Invalid state */
#define ESP_ERR_INVALID_SIZE 0x104 /*!< Invalid size */
#define ESP_ERR_NOT_FOUND 0x105 /*!< Requested resource not found */
#define ESP_ERR_NOT_SUPPORTED 0x106 /*!< Operation or feature not supported */
#define ESP_ERR_TIMEOUT 0x107 /*!< Operation timed out */
#define ESP_ERR_INVALID_RESPONSE 0x108 /*!< Received response was invalid */
#define ESP_ERR_INVALID_CRC 0x109 /*!< CRC or checksum was invalid */
#define ESP_ERR_INVALID_VERSION 0x10A /*!< Version was invalid */
#define ESP_ERR_INVALID_MAC 0x10B /*!< MAC address was invalid */
#define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */
#define ESP_ERR_MESH_BASE 0x4000 /*!< Starting number of MESH error codes */
#define ESP_ERR_FLASH_BASE 0x6000 /*!< Starting number of flash error codes */
/**
* @brief Returns string for esp_err_t error codes
*
* This function finds the error code in a pre-generated lookup-table and
* returns its string representation.
*
* The function is generated by the Python script
* tools/gen_esp_err_to_name.py which should be run each time an esp_err_t
* error is modified, created or removed from the IDF project.
*
* @param code esp_err_t error code
* @return string error message
*/
const char *esp_err_to_name(esp_err_t code);
/**
* @brief Returns string for esp_err_t and system error codes
*
* This function finds the error code in a pre-generated lookup-table of
* esp_err_t errors and returns its string representation. If the error code
* is not found then it is attempted to be found among system errors.
*
* The function is generated by the Python script
* tools/gen_esp_err_to_name.py which should be run each time an esp_err_t
* error is modified, created or removed from the IDF project.
*
* @param code esp_err_t error code
* @param[out] buf buffer where the error message should be written
* @param buflen Size of buffer buf. At most buflen bytes are written into the buf buffer (including the terminating null byte).
* @return buf containing the string error message
*/
const char *esp_err_to_name_r(esp_err_t code, char *buf, size_t buflen);
/** @cond */
void _esp_error_check_failed(esp_err_t rc, const char *file, int line, const char *function, const char *expression) __attribute__((noreturn));
/** @cond */
void _esp_error_check_failed_without_abort(esp_err_t rc, const char *file, int line, const char *function, const char *expression);
#ifndef __ASSERT_FUNC
/* This won't happen on IDF, which defines __ASSERT_FUNC in assert.h, but it does happen when building on the host which
uses /usr/include/assert.h or equivalent.
*/
#ifdef __ASSERT_FUNCTION
#define __ASSERT_FUNC __ASSERT_FUNCTION /* used in glibc assert.h */
#else
#define __ASSERT_FUNC "??"
#endif
#endif
/** @endcond */
/**
* Macro which can be used to check the error code,
* and terminate the program in case the code is not ESP_OK.
* Prints the error code, error location, and the failed statement to serial output.
*
* Disabled if assertions are disabled.
*/
#ifdef NDEBUG
#define ESP_ERROR_CHECK(x) do { \
esp_err_t __err_rc = (x); \
(void) sizeof(__err_rc); \
} while(0)
#elif defined(CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT)
#define ESP_ERROR_CHECK(x) do { \
esp_err_t __err_rc = (x); \
if (__err_rc != ESP_OK) { \
abort(); \
} \
} while(0)
#else
#define ESP_ERROR_CHECK(x) do { \
esp_err_t __err_rc = (x); \
if (__err_rc != ESP_OK) { \
_esp_error_check_failed(__err_rc, __FILE__, __LINE__, \
__ASSERT_FUNC, #x); \
} \
} while(0)
#endif
/**
* Macro which can be used to check the error code. Prints the error code, error location, and the failed statement to
* serial output.
* In comparison with ESP_ERROR_CHECK(), this prints the same error message but isn't terminating the program.
*/
#ifdef NDEBUG
#define ESP_ERROR_CHECK_WITHOUT_ABORT(x) ({ \
esp_err_t __err_rc = (x); \
__err_rc; \
})
#else
#define ESP_ERROR_CHECK_WITHOUT_ABORT(x) ({ \
esp_err_t __err_rc = (x); \
if (__err_rc != ESP_OK) { \
_esp_error_check_failed_without_abort(__err_rc, __FILE__, __LINE__, \
__ASSERT_FUNC, #x); \
} \
__err_rc; \
})
#endif //NDEBUG
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,148 @@
// Copyright 2010-2019 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
/** Definition of a common transaction. Also holds the return value. */
typedef struct {
uint8_t command; ///< Command to send, always 8bits
uint8_t mosi_len; ///< Output data length, in bytes
uint8_t miso_len; ///< Input data length, in bytes
uint8_t address_bitlen; ///< Length of address in bits, set to 0 if command does not need an address
uint32_t address; ///< Address to perform operation on
const uint8_t *mosi_data; ///< Output data to salve
uint8_t *miso_data; ///< [out] Input data from slave, little endian
} spi_flash_trans_t;
/** @brief Mode used for reading from SPI flash */
typedef enum {
SPI_FLASH_SLOWRD = 0, ///< Data read using single I/O, some limits on speed
SPI_FLASH_FASTRD, ///< Data read using single I/O, no limit on speed
SPI_FLASH_DOUT, ///< Data read using dual I/O
SPI_FLASH_DIO, ///< Both address & data transferred using dual I/O
SPI_FLASH_QOUT, ///< Data read using quad I/O
SPI_FLASH_QIO, ///< Both address & data transferred using quad I/O
SPI_FLASH_READ_MODE_MAX, ///< The fastest io mode supported by the host is ``ESP_FLASH_READ_MODE_MAX-1``.
} esp_flash_io_mode_t;
struct spi_flash_host_driver_s;
typedef struct spi_flash_host_driver_s spi_flash_host_driver_t;
/** SPI Flash Host driver instance */
typedef struct {
const struct spi_flash_host_driver_s* driver; ///< Pointer to the implementation function table
// Implementations can wrap this structure into their own ones, and append other data here
} spi_flash_host_inst_t ;
/** Host driver configuration and context structure. */
struct spi_flash_host_driver_s {
/**
* Configure the device-related register before transactions. This saves
* some time to re-configure those registers when we send continuously
*/
esp_err_t (*dev_config)(spi_flash_host_inst_t *host);
/**
* Send an user-defined spi transaction to the device.
*/
esp_err_t (*common_command)(spi_flash_host_inst_t *host, spi_flash_trans_t *t);
/**
* Read flash ID.
*/
esp_err_t (*read_id)(spi_flash_host_inst_t *host, uint32_t *id);
/**
* Erase whole flash chip.
*/
void (*erase_chip)(spi_flash_host_inst_t *host);
/**
* Erase a specific sector by its start address.
*/
void (*erase_sector)(spi_flash_host_inst_t *host, uint32_t start_address);
/**
* Erase a specific block by its start address.
*/
void (*erase_block)(spi_flash_host_inst_t *host, uint32_t start_address);
/**
* Read the status of the flash chip.
*/
esp_err_t (*read_status)(spi_flash_host_inst_t *host, uint8_t *out_sr);
/**
* Disable write protection.
*/
esp_err_t (*set_write_protect)(spi_flash_host_inst_t *host, bool wp);
/**
* Program a page of the flash. Check ``max_write_bytes`` for the maximum allowed writing length.
*/
void (*program_page)(spi_flash_host_inst_t *host, const void *buffer, uint32_t address, uint32_t length);
/** Check whether given buffer can be directly used to write */
bool (*supports_direct_write)(spi_flash_host_inst_t *host, const void *p);
/**
* Slicer for write data. The `program_page` should be called iteratively with the return value
* of this function.
*
* @param address Beginning flash address to write
* @param len Length request to write
* @param align_addr Output of the aligned address to write to
* @param page_size Physical page size of the flash chip
* @return Length that can be actually written in one `program_page` call
*/
int (*write_data_slicer)(spi_flash_host_inst_t *host, uint32_t address, uint32_t len, uint32_t *align_addr,
uint32_t page_size);
/**
* Read data from the flash. Check ``max_read_bytes`` for the maximum allowed reading length.
*/
esp_err_t (*read)(spi_flash_host_inst_t *host, void *buffer, uint32_t address, uint32_t read_len);
/** Check whether given buffer can be directly used to read */
bool (*supports_direct_read)(spi_flash_host_inst_t *host, const void *p);
/**
* Slicer for read data. The `read` should be called iteratively with the return value
* of this function.
*
* @param address Beginning flash address to read
* @param len Length request to read
* @param align_addr Output of the aligned address to read
* @param page_size Physical page size of the flash chip
* @return Length that can be actually read in one `read` call
*/
int (*read_data_slicer)(spi_flash_host_inst_t *host, uint32_t address, uint32_t len, uint32_t *align_addr, uint32_t page_size);
/**
* Check whether the host is idle to perform new operations.
*/
bool (*host_idle)(spi_flash_host_inst_t *host);
/**
* Configure the host to work at different read mode. Responsible to compensate the timing and set IO mode.
*/
esp_err_t (*configure_host_io_mode)(spi_flash_host_inst_t *host, uint32_t command,
uint32_t addr_bitlen, int dummy_bitlen_base,
esp_flash_io_mode_t io_mode);
/**
* Internal use, poll the HW until the last operation is done.
*/
void (*poll_cmd_done)(spi_flash_host_inst_t *host);
/**
* For some host (SPI1), they are shared with a cache. When the data is
* modified, the cache needs to be flushed. Left NULL if not supported.
*/
esp_err_t (*flush_cache)(spi_flash_host_inst_t* host, uint32_t addr, uint32_t size);
};
///Slowest io mode supported by ESP32, currently SlowRd
#define SPI_FLASH_READ_MODE_MIN SPI_FLASH_SLOWRD
#ifdef __cplusplus
}
#endif

View File

@ -27,7 +27,7 @@ else()
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include" "unity/src" "unity/extras/fixture/src")
INCLUDE_DIRS ${includes})
if(NOT ${IDF_TARGET} STREQUAL "linux")
target_compile_definitions(${COMPONENT_LIB} PUBLIC

View File

@ -46,7 +46,9 @@ ignore_files = [os.path.join('components', 'mdns', 'test_afl_fuzz_host', 'esp32_
]
# add directories here which should not be parsed, this is a tuple since it will be used with *.startswith()
ignore_dirs = (os.path.join('examples'), os.path.join('components', 'cmock', 'CMock', 'test'))
ignore_dirs = (os.path.join('examples'),
os.path.join('components', 'cmock', 'CMock', 'test'),
os.path.join('components', 'spi_flash', 'sim'))
# macros from here have higher priorities in case of collisions
priority_headers = [os.path.join('components', 'esp_common', 'include', 'esp_err.h')]