From db780952f1d0f43cc30c28bb19e1bbc2f8f19b4f Mon Sep 17 00:00:00 2001 From: Neil Kolban Date: Sun, 5 Aug 2018 19:08:29 -0500 Subject: [PATCH] Sync --- cpp_utils/AWS.cpp | 123 +++++++++ cpp_utils/AWS.h | 1 + cpp_utils/Console.cpp | 200 ++++++++++++++ cpp_utils/Console.h | 149 +++++++++++ cpp_utils/GeneralUtils.cpp | 21 +- cpp_utils/MMU.cpp | 127 +++++++++ cpp_utils/MMU.h | 22 ++ cpp_utils/System.cpp | 433 +++++++++++++++++++++++++++++++ cpp_utils/System.h | 2 + cpp_utils/Task.cpp | 2 +- cpp_utils/Task.h | 2 +- tasks/watchdogs/README.md | 6 + tools/bootloaderExamine/main.cpp | 6 + 13 files changed, 1082 insertions(+), 12 deletions(-) create mode 100644 cpp_utils/Console.cpp create mode 100644 cpp_utils/Console.h create mode 100644 cpp_utils/MMU.cpp create mode 100644 cpp_utils/MMU.h create mode 100644 tasks/watchdogs/README.md diff --git a/cpp_utils/AWS.cpp b/cpp_utils/AWS.cpp index 561162c3..7687be21 100644 --- a/cpp_utils/AWS.cpp +++ b/cpp_utils/AWS.cpp @@ -19,6 +19,129 @@ AWS::AWS() { AWS::~AWS() { } +/** + * Convert an AWS IoT error code to a string representation. + * @param err The error code to be mapped. + * @return A string representation of the error code. + */ +/* static */ std::string AWS::errorToString(IoT_Error_t err) { + switch(err) { + case NETWORK_PHYSICAL_LAYER_CONNECTED : + return "NETWORK_PHYSICAL_LAYER_CONNECTED"; + case NETWORK_MANUALLY_DISCONNECTED : + return "NETWORK_MANUALLY_DISCONNECTED"; + case NETWORK_ATTEMPTING_RECONNECT: + return "NETWORK_ATTEMPTING_RECONNECT"; + case NETWORK_RECONNECTED: + return "NETWORK_RECONNECTED"; + case MQTT_NOTHING_TO_READ : + return "MQTT_NOTHING_TO_READ"; + case MQTT_CONNACK_CONNECTION_ACCEPTED: + return "MQTT_CONNACK_CONNECTION_ACCEPTED"; + case SUCCESS : + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + case NULL_VALUE_ERROR : + return "NULL_VALUE_ERROR"; + case TCP_CONNECTION_ERROR : + return "TCP_CONNECTION_ERROR"; + case SSL_CONNECTION_ERROR: + return "SSL_CONNECTION_ERROR"; + case TCP_SETUP_ERROR : + return "TCP_SETUP_ERROR"; + case NETWORK_SSL_CONNECT_TIMEOUT_ERROR : + return "NETWORK_SSL_CONNECT_TIMEOUT_ERROR"; + case NETWORK_SSL_WRITE_ERROR : + return "NETWORK_SSL_WRITE_ERROR"; + case NETWORK_SSL_INIT_ERROR : + return "NETWORK_SSL_INIT_ERROR"; + case NETWORK_SSL_CERT_ERROR : + return "NETWORK_SSL_CERT_ERROR"; + case NETWORK_SSL_WRITE_TIMEOUT_ERROR : + return "NETWORK_SSL_WRITE_TIMEOUT_ERROR"; + case NETWORK_SSL_READ_TIMEOUT_ERROR : + return "NETWORK_SSL_READ_TIMEOUT_ERROR"; + case NETWORK_SSL_READ_ERROR : + return "NETWORK_SSL_READ_ERROR"; + case NETWORK_DISCONNECTED_ERROR : + return "NETWORK_DISCONNECTED_ERROR"; + case NETWORK_RECONNECT_TIMED_OUT_ERROR: + return "NETWORK_RECONNECT_TIMED_OUT_ERROR"; + case NETWORK_ALREADY_CONNECTED_ERROR : + return "NETWORK_ALREADY_CONNECTED_ERROR"; + case NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED : + return "NETWORK_MBEDTLS_ERR_CTR_DRBG_ENTROPY_SOURCE_FAILED"; + case NETWORK_SSL_UNKNOWN_ERROR : + return "NETWORK_SSL_UNKNOWN_ERROR"; + case NETWORK_PHYSICAL_LAYER_DISCONNECTED : + return "NETWORK_PHYSICAL_LAYER_DISCONNECTED"; + case NETWORK_X509_ROOT_CRT_PARSE_ERROR : + return "NETWORK_X509_ROOT_CRT_PARSE_ERROR"; + case NETWORK_X509_DEVICE_CRT_PARSE_ERROR : + return "NETWORK_X509_DEVICE_CRT_PARSE_ERROR"; + case NETWORK_PK_PRIVATE_KEY_PARSE_ERROR : + return "NETWORK_PK_PRIVATE_KEY_PARSE_ERROR"; + case NETWORK_ERR_NET_SOCKET_FAILED : + return "NETWORK_ERR_NET_SOCKET_FAILED"; + case NETWORK_ERR_NET_UNKNOWN_HOST : + return "NETWORK_ERR_NET_UNKNOWN_HOST"; + case NETWORK_ERR_NET_CONNECT_FAILED : + return "NETWORK_ERR_NET_CONNECT_FAILED"; + case NETWORK_SSL_NOTHING_TO_READ : + return "NETWORK_SSL_NOTHING_TO_READ"; + case MQTT_CONNECTION_ERROR : + return "MQTT_CONNECTION_ERROR"; + case MQTT_CONNECT_TIMEOUT_ERROR : + return "MQTT_CONNECT_TIMEOUT_ERROR"; + case MQTT_REQUEST_TIMEOUT_ERROR: + return "MQTT_REQUEST_TIMEOUT_ERROR"; + case MQTT_UNEXPECTED_CLIENT_STATE_ERROR : + return "MQTT_UNEXPECTED_CLIENT_STATE_ERROR"; + case MQTT_CLIENT_NOT_IDLE_ERROR : + return "MQTT_CLIENT_NOT_IDLE_ERROR"; + case MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR : + return "MQTT_RX_MESSAGE_PACKET_TYPE_INVALID_ERROR"; + case MQTT_RX_BUFFER_TOO_SHORT_ERROR : + return "MQTT_RX_BUFFER_TOO_SHORT_ERROR"; + case MQTT_TX_BUFFER_TOO_SHORT_ERROR : + return "MQTT_TX_BUFFER_TOO_SHORT_ERROR"; + case MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR : + return "MQTT_MAX_SUBSCRIPTIONS_REACHED_ERROR"; + case MQTT_DECODE_REMAINING_LENGTH_ERROR : + return "MQTT_DECODE_REMAINING_LENGTH_ERROR"; + case MQTT_CONNACK_UNKNOWN_ERROR : + return "MQTT_CONNACK_UNKNOWN_ERROR"; + case MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR : + return "MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR"; + case MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR: + return "MQTT_CONNACK_IDENTIFIER_REJECTED_ERROR"; + case MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR : + return "MQTT_CONNACK_SERVER_UNAVAILABLE_ERROR"; + case MQTT_CONNACK_BAD_USERDATA_ERROR: + return "MQTT_CONNACK_BAD_USERDATA_ERROR"; + case MQTT_CONNACK_NOT_AUTHORIZED_ERROR : + return "MQTT_CONNACK_NOT_AUTHORIZED_ERROR"; + case JSON_PARSE_ERROR : + return "JSON_PARSE_ERROR"; + case SHADOW_WAIT_FOR_PUBLISH : + return "SHADOW_WAIT_FOR_PUBLISH"; + case SHADOW_JSON_BUFFER_TRUNCATED : + return "SHADOW_JSON_BUFFER_TRUNCATED"; + case SHADOW_JSON_ERROR : + return "SHADOW_JSON_ERROR"; + case MUTEX_INIT_ERROR : + return "MUTEX_INIT_ERROR"; + case MUTEX_LOCK_ERROR: + return "MUTEX_LOCK_ERROR"; + case MUTEX_UNLOCK_ERROR : + return "MUTEX_UNLOCK_ERROR"; + case MUTEX_DESTROY_ERROR : + return "MUTEX_DESTROY_ERROR"; + default: + return "Unknown error!"; + } +} // AWS#errorToString /** * @brief Connect to the AWS IoT service. diff --git a/cpp_utils/AWS.h b/cpp_utils/AWS.h index 5d2c891c..abbd0613 100644 --- a/cpp_utils/AWS.h +++ b/cpp_utils/AWS.h @@ -24,6 +24,7 @@ class AWS { void connect(std::string clientId); void disconnect(); + static std::string errorToString(IoT_Error_t err); // Convert an AWS IoT error code to a string representation. void init(std::string host=CONFIG_AWS_IOT_MQTT_HOST, uint16_t port=CONFIG_AWS_IOT_MQTT_PORT); void publish(std::string topic, std::string payload, QoS qos = QOS0); void subscribe(std::string topic); diff --git a/cpp_utils/Console.cpp b/cpp_utils/Console.cpp new file mode 100644 index 00000000..c88d8372 --- /dev/null +++ b/cpp_utils/Console.cpp @@ -0,0 +1,200 @@ +/* + * Console.cpp + * + * Created on: Jun 15, 2018 + * Author: kolban + */ + +/** + * Example: + * Argtable argtable; + * argtable.addString("myparam", "l", "list", "Get Listings"); + * argtable.parse(argc, argv); + * ArgTableEntry_String* pStr = (ArgTableEntry_String*)argTable.get("myparam"); + * pStr->getValue(); + */ +#include "Console.h" + +/** + * Argtable instance constructor. + */ +ArgTable::ArgTable() { + m_argtable = nullptr; + m_argEnd = nullptr; +} // ArgTable#ArgTable + + +/** + * Argtable instance destructor. + */ +ArgTable::~ArgTable() { + freeArgtable(); // Release any resources associated with the argtable. +} // ArgTable#~ArgTable + + +/** + * Build the ArgTable that will be used for parsing. + */ +/* private */ void ArgTable::build() { + /* + * The m_argTableEntries is a std::list that contains the argtable entries in the form of a std::pair. We + * allocate storage for the ArgTable and then populate it. The last entry of the argtable must be an end marker. + */ + int size = m_argTableEntries.size(); + m_argtable = new void*[size + 1]; + int i=0; + for (auto it = m_argTableEntries.begin(); it != m_argTableEntries.end(); ++it) { + m_argtable[i] = it->second->getEntry(); + i++; + } + m_argEnd = arg_end(10); + m_argtable[i] = m_argEnd; +} // ArgTable#build + + +ArgTableEntry_Date ArgTable::addDate(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Date* pDate = new ArgTableEntry_Date(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pDate)); + return *pDate; +} // ArgTable#addDate + + +ArgTableEntry_Double ArgTable::addDouble(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Double* pDouble = new ArgTableEntry_Double(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pDouble)); + return *pDouble; +} // ArgTable#addDouble + + +ArgTableEntry_File ArgTable::addFile(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_File* pFile = new ArgTableEntry_File(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pFile)); + return *pFile; +} // ArgTable#addFile + + +ArgTableEntry_Int ArgTable::addInt(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Int* pInt = new ArgTableEntry_Int(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pInt)); + return *pInt; +} // ArgTable#addInt + + +ArgTableEntry_Lit ArgTable::addLit(std::string name, std::string shortopts, std::string longopts, std::string glossary) { + ArgTableEntry_Lit* pLit = new ArgTableEntry_Lit(shortopts, longopts, glossary); + m_argTableEntries.push_back(std::make_pair(name, pLit)); + return *pLit; +} // ArgTable#addLit + + +ArgTableEntry_String ArgTable::addString(std::string name, std::string shortopts, std::string longopts, std::string glossary, int min, int max) { + ArgTableEntry_String* pStr = new ArgTableEntry_String(shortopts, longopts, glossary, min, max); + m_argTableEntries.push_back(std::make_pair(name, pStr)); + return *pStr; +} // ArgTable#addString + + +/** + * Parse the input and output parameters against this argtable. + * @param argc A count of the number of parameters. + * @param argv An array of string parameters. + * @return The number of errors detected. + */ +int ArgTable::parse(int argc, char* argv[]) { + if (m_argtable == nullptr) { // If we don't have an argtable, build it. + build(); + } + int nErrors = arg_parse(argc, argv, m_argtable); + return nErrors; +} // ArgTable#parse + + +/** + * Print any errors associated with the parsing. + */ +void ArgTable::printErrors(FILE* fp, std::string progName) { + if (m_argEnd != nullptr) { + arg_print_errors(fp, m_argEnd, progName.c_str()); + } +} // ArgTable#printErrors + + +/** + * Release the argtable data. + */ +/* private */void ArgTable::freeArgtable() { + if (m_argtable != nullptr) { + arg_free(m_argtable); + m_argtable = nullptr; + m_argEnd = nullptr; + } +} // ArgTable#freeArgtable + + +ArgTableEntry_Date::ArgTableEntry_Date(std::string shortopts, std::string longopts, std::string glossary) { + m_argDate = arg_daten(shortopts.c_str(), longopts.c_str(), "", "", 0, 1, glossary.c_str()); + m_type = ArgType_t::DATE; +} // ArgTableEntry_Date#ArgTableEntry_Date + + +int ArgTableEntry_Date::getCount() { + return m_argDate->count; +} // ArgTableEntry_Date#getCount + + +ArgTableEntry_Double::ArgTableEntry_Double(std::string shortopts, std::string longopts, std::string glossary) { + m_argDbl = arg_dbln(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); + m_type = ArgType_t::DBL; +} + +int ArgTableEntry_Double::getCount() { + return m_argDbl->count; +} + +ArgTableEntry_File::ArgTableEntry_File(std::string shortopts, std::string longopts, std::string glossary) { + m_argFile = arg_filen(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); + m_type = ArgType_t::FILE; +} + +int ArgTableEntry_File::getCount() { + return m_argFile->count; +} + +ArgTableEntry_Int::ArgTableEntry_Int(std::string shortopts, std::string longopts, std::string glossary) { + m_argInt = arg_intn(shortopts.c_str(), longopts.c_str(), "", 0, 1, glossary.c_str()); + m_type = ArgType_t::INT; +} + +int ArgTableEntry_Int::getCount() { + return m_argInt->count; +} + +ArgTableEntry_Lit::ArgTableEntry_Lit(std::string shortopts, std::string longopts, std::string glossary) { + m_argLit = arg_litn(shortopts.c_str(), longopts.c_str(), 0, 1, glossary.c_str()); + m_type = ArgType_t::LIT; +} + +int ArgTableEntry_Lit::getCount() { + return m_argLit->count; +} + +int ArgTableEntry_Regex::getCount() { + return m_argRex->count; +} + +ArgTableEntry_String::ArgTableEntry_String(std::string shortopts, std::string longopts, std::string glossary, int min, int max) { + m_argStr = arg_strn(shortopts.c_str(), longopts.c_str(), "", min, max, glossary.c_str()); + m_type = ArgType_t::STR; +} + +int ArgTableEntry_String::getCount() { + return m_argStr->count; +} + + +Console::Console() { +} + +Console::~Console() { +} + diff --git a/cpp_utils/Console.h b/cpp_utils/Console.h new file mode 100644 index 00000000..2ce40fa2 --- /dev/null +++ b/cpp_utils/Console.h @@ -0,0 +1,149 @@ +/* + * Console.h + * + * Created on: Jun 15, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_CONSOLE_H_ +#define COMPONENTS_CPP_UTILS_CONSOLE_H_ + +#include +#include +#include +#include + +class Console { +public: + Console(); + virtual ~Console(); +}; + + +enum class ArgType_t { LIT, INT, DBL, STR, REX, FILE, DATE }; + + +class ArgTableEntry_Generic { +protected: + ArgType_t m_type; +public: + virtual int getCount(); + bool hasValue() { + return getCount() > 0; + } + virtual void* getEntry() = 0; +}; + + +class ArgTableEntry_Lit : public ArgTableEntry_Generic { +private: + struct arg_lit* m_argLit; +public: + int getCount(); + ArgTableEntry_Lit(std::string shortopts, std::string longopts, std::string glossary); + void* getEntry() { + return m_argLit; + } +}; + +class ArgTableEntry_Int : public ArgTableEntry_Generic { +private: + struct arg_int* m_argInt; +public: + ArgTableEntry_Int(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + int getValue(int index=0); + void* getEntry() { + return m_argInt; + } +}; + + +class ArgTableEntry_Double : public ArgTableEntry_Generic { +private: + struct arg_dbl* m_argDbl; +public: + ArgTableEntry_Double(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + double getValue(int index=0); + void* getEntry() { + return m_argDbl; + } +}; + + +class ArgTableEntry_String : public ArgTableEntry_Generic { +private: + struct arg_str* m_argStr; +public: + ArgTableEntry_String(std::string shortopts, std::string longopts, std::string glossary, int min, int max); + int getCount(); + std::string getValue(int index=0); + void* getEntry() { + return m_argStr; + } +}; + + +class ArgTableEntry_Regex : public ArgTableEntry_Generic { +private: + struct arg_rex* m_argRex; +public: + int getCount(); + std::string getValue(int index=0); + void* getEntry() { + return m_argRex; + } +}; + + +class ArgTableEntry_File : public ArgTableEntry_Generic { +private: + struct arg_file* m_argFile; +public: + ArgTableEntry_File(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + std::string getFilename(int index=0); + std::string getBasename(int index=0); + std::string getExtension(int index=0); + void* getEntry() { + return m_argFile; + } +}; + + +class ArgTableEntry_Date : public ArgTableEntry_Generic { +private: + struct arg_date* m_argDate; +public: + ArgTableEntry_Date(std::string shortopts, std::string longopts, std::string glossary); + int getCount(); + struct tm* getValue(int index=0); + void* getEntry() { + return m_argDate; + } +}; + + +class ArgTable { +private: + void** m_argtable; + struct arg_end* m_argEnd; + std::list> m_argTableEntries; + void build(); + void freeArgtable(); + +public: + ArgTable(); + ~ArgTable(); + ArgTableEntry_Date addDate(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_Double addDouble(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_File addFile(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_Int addInt(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_Lit addLit(std::string name, std::string shortopts, std::string longopts, std::string glossary); + ArgTableEntry_String addString(std::string name, std::string shortopts, std::string longopts, std::string glossary, int min, int max); + int parse(int argc, char* argv[]); + void printErrors(FILE* fp, std::string progName=""); +}; + +#endif /* COMPONENTS_CPP_UTILS_CONSOLE_H_ */ diff --git a/cpp_utils/GeneralUtils.cpp b/cpp_utils/GeneralUtils.cpp index 960f3172..e26d84c9 100644 --- a/cpp_utils/GeneralUtils.cpp +++ b/cpp_utils/GeneralUtils.cpp @@ -296,7 +296,8 @@ void GeneralUtils::hexDump(const uint8_t* pData, uint32_t length) { char tempBuf[80]; uint32_t lineNumber = 0; - ESP_LOGD(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f ----------------"); + ESP_LOGD(LOG_TAG, " 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"); + ESP_LOGD(LOG_TAG, " -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); strcpy(ascii, ""); strcpy(hex, ""); uint32_t index=0; @@ -365,23 +366,23 @@ std::vector GeneralUtils::split(std::string source, char delimiter) const char* GeneralUtils::errorToString(esp_err_t errCode) { switch(errCode) { case ESP_OK: - return "OK"; + return "ESP_OK"; case ESP_FAIL: - return "Fail"; + return "ESP_FAIL"; case ESP_ERR_NO_MEM: - return "No memory"; + return "ESP_ERR_NO_MEM"; case ESP_ERR_INVALID_ARG: - return "Invalid argument"; + return "ESP_ERR_INVALID_ARG"; case ESP_ERR_INVALID_SIZE: - return "Invalid state"; + return "ESP_ERR_INVALID_SIZE"; case ESP_ERR_INVALID_STATE: - return "Invalid state"; + return "ESP_ERR_INVALID_STATE"; case ESP_ERR_NOT_FOUND: - return "Not found"; + return "ESP_ERR_NOT_FOUND"; case ESP_ERR_NOT_SUPPORTED: - return "Not supported"; + return "ESP_ERR_NOT_SUPPORTED"; case ESP_ERR_TIMEOUT: - return "Timeout"; + return "ESP_ERR_TIMEOUT"; case ESP_ERR_NVS_NOT_INITIALIZED: return "ESP_ERR_NVS_NOT_INITIALIZED"; case ESP_ERR_NVS_NOT_FOUND: diff --git a/cpp_utils/MMU.cpp b/cpp_utils/MMU.cpp new file mode 100644 index 00000000..e43328fb --- /dev/null +++ b/cpp_utils/MMU.cpp @@ -0,0 +1,127 @@ +/* + * MMU.cpp + * + * Created on: Jun 30, 2018 + * Author: kolban + */ + +#include "MMU.h" +#include +#include +#include + +// The following functions are provided by spi_flash.h +extern "C" { + extern void spi_flash_disable_interrupts_caches_and_other_cpu(); + + // Enable cache, enable interrupts (to be added in future), resume scheduler + extern void spi_flash_enable_interrupts_caches_and_other_cpu(); +} + + +typedef struct { + uint32_t low; + uint32_t high; +} addressRange_t; + +static addressRange_t entryNumberToAddressRange(uint32_t entryNumber) { + addressRange_t ret; + if (entryNumber < 64) { + ret.low = 0x3F400000 + 64*1024*entryNumber; + ret.high = 0x3F400000 + 64*1024*(entryNumber+1)-1; + return ret; + } + ret.low = 0x40000000 + 64*1024*(entryNumber-64); + ret.high = 0x40000000 + 64*1024*(entryNumber+1-64)-1; + return ret; +} + + +static uint32_t flashPageToOffset(uint32_t page) { + return page*64*1024; +} + + + +/* static */ void MMU::dump() { + const uint32_t mappingInvalid = 1 << 8; + + printf("PRO CPU MMU\n"); + for (int i=0; i<256; i++) { + if (!(DPORT_PRO_FLASH_MMU_TABLE[i] & mappingInvalid)) { + addressRange_t addressRange = entryNumberToAddressRange(i); + printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", + i, + addressRange.low, addressRange.high, + DPORT_PRO_FLASH_MMU_TABLE[i] & 0xff, + flashPageToOffset(DPORT_PRO_FLASH_MMU_TABLE[i] & 0xff)); + } + } + printf("\n"); + printf("APP CPU MMU\n"); + for (int i=0; i<256; i++) { + if (!(DPORT_APP_FLASH_MMU_TABLE[i] & mappingInvalid)) { + addressRange_t addressRange = entryNumberToAddressRange(i); + printf("Entry: %2d (0x%8.8x - 0x%8.8x), Page: %d - offset: 0x%x\n", + i, + addressRange.low, addressRange.high, + DPORT_APP_FLASH_MMU_TABLE[i] & 0xff, + flashPageToOffset(DPORT_APP_FLASH_MMU_TABLE[i] & 0xff)); + } + } +} // MMU#dumpMMU + +extern "C" { + static void IRAM_ATTR mapFlashToVMA_Internal(uint32_t flashOffset, void* vma, size_t size); +} + +static void IRAM_ATTR mapFlashToVMA_Internal(uint32_t flashOffset, void* vma, size_t size) { + printf(">> MMU::mapFlashToVMA: flash offset: 0x%x, VMA: 0x%x, size: %d\n", flashOffset, (uint32_t)vma, size); + uint32_t mmuEntryStart; // The MMU table entry to start mapping. + uint32_t mmuEntryEnd; // The MMU table entry to end mapping. + + if ((uint32_t)vma >= 0x40000000 && (uint32_t)vma < 0x40C00000) { + mmuEntryStart = (((uint32_t)vma - 0x40000000)/(64*1024)) + 64; + mmuEntryEnd = (((uint32_t)vma - 0x40000000 + size)/(64*1024)) + 64; + } + else if ((uint32_t)vma >= 0x3F400000 && (uint32_t)vma < 0x3F800000) { + mmuEntryStart = (((uint32_t)vma - 0x3F400000)/(64*1024)); + mmuEntryEnd = (((uint32_t)vma - 0x3F400000 + size)/(64*1024)); + } + else { + printf(" - Unable to map from flash to VMA."); + return; + } + + // At this point we have populated mmuEntryStart and mmuEntryEnd which are the MMU table entries. + uint32_t pFlashStart = flashOffset; + uint32_t pFlashEnd = flashOffset + size; + + printf(" - Mapping flash to VMA via MMU. MMU entries start: %d, end: %d, mapping flash 0x%x (flash page: %d) to 0x%x (flash page: %d)\n", + mmuEntryStart, mmuEntryEnd, pFlashStart, pFlashStart/(64*1024), pFlashEnd, pFlashEnd/(64*1024)); + + uint32_t flashRegion = pFlashStart / (64*1024); // Determine the 64K chunk of flash to be mapped (we map in units of 64K). + + spi_flash_disable_interrupts_caches_and_other_cpu(); + + // For each of the mapping entries, map it to the corresponding flash region. + for (uint32_t i=mmuEntryStart; i<=mmuEntryEnd; i++) { + DPORT_PRO_FLASH_MMU_TABLE[i] = flashRegion; // There are two tables. One for the PRO CPU and one for the APP CPU. + DPORT_APP_FLASH_MMU_TABLE[i] = flashRegion; // Map both of them to the flash region. + flashRegion++; + } + + Cache_Flush(0); + Cache_Flush(1); + spi_flash_enable_interrupts_caches_and_other_cpu(); +} // mapFlashToVMA + +/** + * Map an area of flash memory into VMA. + * @param flashOffset The offset in flash of the start of data. + * @param vma Where in VMA the data should appear. + * @size How much data to map. + */ +void IRAM_ATTR MMU::mapFlashToVMA(uint32_t flashOffset, void* vma, size_t size) { + mapFlashToVMA_Internal(flashOffset, vma, size); +} diff --git a/cpp_utils/MMU.h b/cpp_utils/MMU.h new file mode 100644 index 00000000..d8ba6b64 --- /dev/null +++ b/cpp_utils/MMU.h @@ -0,0 +1,22 @@ +/* + * MMU.h + * + * Created on: Jun 30, 2018 + * Author: kolban + */ + +#ifndef COMPONENTS_CPP_UTILS_MMU_H_ +#define COMPONENTS_CPP_UTILS_MMU_H_ +#include +#include +#include + +class MMU { +public: + MMU(); + virtual ~MMU(); + static void dump(); + static void mapFlashToVMA(uint32_t flashOffset, void* vma, size_t size); +}; + +#endif /* COMPONENTS_CPP_UTILS_MMU_H_ */ diff --git a/cpp_utils/System.cpp b/cpp_utils/System.cpp index 1993be4c..e9e7c525 100644 --- a/cpp_utils/System.cpp +++ b/cpp_utils/System.cpp @@ -7,11 +7,133 @@ #include "System.h" #include +#include +#include extern "C" { #include } +typedef volatile struct { + union { + struct { + uint32_t mcu_oe: 1; + uint32_t slp_sel: 1; + uint32_t mcu_wpd: 1; + uint32_t mcu_wpu: 1; + uint32_t mcu_ie: 1; + uint32_t mcu_drb: 2; + uint32_t func_wpd: 1; + uint32_t func_wpu: 1; + uint32_t func_ie: 1; + uint32_t func_drv: 2; + uint32_t mcu_sel: 3; + uint32_t reserved15: 17; + }; + uint32_t val; + }; +} io_mux_reg_t; + + +typedef volatile struct { + union { + struct { + uint32_t clk1: 4; + uint32_t clk2: 4; + uint32_t clk3: 4; + uint32_t reserved12: 20; + }; + uint32_t val; + } pin_ctrl; + + // The 36 exposed pads. + io_mux_reg_t pad_gpio36; // GPIO36 + io_mux_reg_t pad_gpio37; // GPIO37 + io_mux_reg_t pad_gpio38; // GPIO38 + io_mux_reg_t pad_gpio39; // GPIO39 + io_mux_reg_t pad_gpio34; // GPIO34 + io_mux_reg_t pad_gpio35; // GPIO35 + io_mux_reg_t pad_gpio32; // GPIO32 + io_mux_reg_t pad_gpio33; // GPIO33 + io_mux_reg_t pad_gpio25; // GPIO25 + io_mux_reg_t pad_gpio26; // GPIO26 + io_mux_reg_t pad_gpio27; // GPIO27 + io_mux_reg_t pad_mtms; // GPIO14 + io_mux_reg_t pad_mtdi; // GPIO12 + io_mux_reg_t pad_mtck; // GPIO13 + io_mux_reg_t pad_mtdo; // GPIO15 + io_mux_reg_t pad_gpio2; // GPIO2 + io_mux_reg_t pad_gpio0; // GPIO0 + io_mux_reg_t pad_gpio4; // GPIO4 + io_mux_reg_t pad_gpio16; // GPIO16 + io_mux_reg_t pad_gpio17; // GPIO17 + io_mux_reg_t pad_sd_data2; // GPIO9 + io_mux_reg_t pad_sd_data3; // GPIO10 + io_mux_reg_t pad_sd_cmd; // GPIO11 + io_mux_reg_t pad_sd_clk; // GPIO6 + io_mux_reg_t pad_sd_data0; // GPIO7 + io_mux_reg_t pad_sd_data1; // GPIO8 + io_mux_reg_t pad_gpio5; // GPIO5 + io_mux_reg_t pad_gpio18; // GPIO18 + io_mux_reg_t pad_gpio19; // GPIO19 + io_mux_reg_t pad_gpio20; // GPIO20 + io_mux_reg_t pad_gpio21; // GPIO21 + io_mux_reg_t pad_gpio22; // GPIO22 + io_mux_reg_t pad_u0rxd; // GPIO3 + io_mux_reg_t pad_u0txd; // GPIO1 + io_mux_reg_t pad_gpio23; // GPIO23 + io_mux_reg_t pad_gpio24; // GPIO24 +} io_mux_dev_t; + +static io_mux_dev_t* IO_MUX = (io_mux_dev_t*)0x3ff49000; + +static const io_mux_reg_t* io_mux_translate[] = { + &IO_MUX->pad_gpio0, // 0 + &IO_MUX->pad_u0txd, // 1 + &IO_MUX->pad_gpio2, // 2 + &IO_MUX->pad_u0rxd, // 3 + &IO_MUX->pad_gpio4, // 4 + &IO_MUX->pad_gpio5, // 5 + &IO_MUX->pad_sd_clk, // 6 + &IO_MUX->pad_sd_data0, // 7 + &IO_MUX->pad_sd_data1, // 8 + &IO_MUX->pad_sd_data2, // 9 + &IO_MUX->pad_sd_data3, // 10 + &IO_MUX->pad_sd_cmd, // 11 + &IO_MUX->pad_mtdi, // 12 + &IO_MUX->pad_mtck, // 13 + &IO_MUX->pad_mtms, // 14 + &IO_MUX->pad_mtdo, // 15 + &IO_MUX->pad_gpio16, // 16 + &IO_MUX->pad_gpio17, // 17 + &IO_MUX->pad_gpio18, // 18 + &IO_MUX->pad_gpio19, // 19 + &IO_MUX->pad_gpio20, // 20 + &IO_MUX->pad_gpio21, // 21 + &IO_MUX->pad_gpio22, // 22 + &IO_MUX->pad_gpio23, // 23 + &IO_MUX->pad_gpio24, // 24 + &IO_MUX->pad_gpio25, // 25 + &IO_MUX->pad_gpio26, // 26 + &IO_MUX->pad_gpio27, // 27 + nullptr, // 28 + nullptr, // 29 + nullptr, // 30 + nullptr, // 31 + &IO_MUX->pad_gpio32, // 32 + &IO_MUX->pad_gpio33, // 33 + &IO_MUX->pad_gpio34, // 34 + &IO_MUX->pad_gpio35, // 35 + &IO_MUX->pad_gpio36, // 36 + &IO_MUX->pad_gpio37, // 37 + &IO_MUX->pad_gpio38, // 38 + &IO_MUX->pad_gpio39 // 39 +}; + +static const io_mux_reg_t* gpioToIoMux(int gpio) { + return io_mux_translate[gpio]; +} + System::System() { // TODO Auto-generated constructor stub @@ -21,6 +143,317 @@ System::~System() { // TODO Auto-generated destructor stub } +const static char* outSignalStrings[] = { + "SPICLK_out", // 0 + "SPIQ_out", // 1 + "SPID_out", // 2 + "SPIHD_out", //3 + "SPIWP_out", // 4 + "SPICS0_out", // 5 + "SPICS1_out", // 6 + "SPICS2_out", // 7 + "HSPICLK_out", // 8 + "HSPIQ_out", // 9 + "HSPID_out", // 10 + "HSPICS0_out", // 11 + "HSPIHD_out", // 12 + "HSPIWP_out", // 13 + "U0TXD_out", // 14 + "U0RTS_out", // 15 + "U0DTR_out", // 16 + "U1TXD_out", // 17 + "U1RTS_out", // 18 + "", // 19 + "", // 20 + "", // 21 + "", // 22 + "I2S0O_BCK_out", // 23 + "I2S1O_BCK_out", // 24 + "I2S0O_WS_out", // 25 + "I2S1O_WS_out", // 26 + "I2S0I_BCK_out", // 27 + "I2S0I_WS_out", // 28 + "I2CEXT0_SCL_out", // 29 + "I2CEXT0_SDA_out", // 30 + "sdio_tohost_int_out", // 31 + "pwm0_out0a", // 32 + "pwm0_out0b", // 33 + "pwm0_out1a", // 34 + "pwm0_out1b", // 35 + "pwm0_out2a", // 36 + "pwm0_out2b", //37 + "", // 38 + "", // 39 + "", // 40 + "", // 41 + "", // 42 + "", // 43 + "", // 44 + "", // 45 + "", // 46 + "", // 47 + "", // 48 + "", // 49 + "", // 50 + "", // 51 + "", // 52 + "", // 53 + "", // 54 + "", // 55 + "", // 56 + "", // 57 + "", // 58 + "", // 59 + "", // 60 + "HSPICS1_out", // 61 + "HSPICS2_out", // 62 + "VSPICLK_out_mux", // 63 + "VSPIQ_out", // 64 + "VSPID_out", // 65 + "VSPIHD_out", // 66 + "VSPIWP_out", // 67 + "VSPICS0_out", // 68 + "VSPICS1_out", // 69 + "VSPICS2_out", // 70 + "ledc_hs_sig_out0", // 71 + "ledc_hs_sig_out1", // 72 + "ledc_hs_sig_out2", // 73 + "ledc_hs_sig_out3", // 74 + "ledc_hs_sig_out4", // 75 + "ledc_hs_sig_out5", // 76 + "ledc_hs_sig_out6", // 77 + "ledc_hs_sig_out7", // 78 + "edc_ls_sig_out0", // 79 + "ledc_ls_sig_out1", // 80 + "ledc_ls_sig_out2", // 81 + "ledc_ls_sig_out3", // 82 + "ledc_ls_sig_out4", // 83 + "ledc_ls_sig_out5", // 84 + "ledc_ls_sig_out6", // 85 + "ledc_ls_sig_out7", // 86 + "rmt_sig_out0", // 87 + "rmt_sig_out1", // 88 + "rmt_sig_out2", // 89 + "rmt_sig_out3", // 90 + "rmt_sig_out4", // 91 + "rmt_sig_out5", // 92 + "rmt_sig_out6", // 93 + "rmt_sig_out7", // 94 + "I2CEXT1_SCL_out", // 95 + "I2CEXT1_SCL_out", // 96 + "host_ccmd_od_pullup_en_n", // 97 + "host_rst_n_1", // 98 + "host_rst_n_2", // 99 + "gpio_sd0_out", // 100 + "gpio_sd1_out", // 101 + "gpio_sd2_out", // 102 + "gpio_sd3_out", // 103 + "gpio_sd4_out", // 104 + "gpio_sd5_out", // 105 + "gpio_sd6_out", // 106 + "gpio_sd7_out", // 107 + "pwm1_out0a", // 108 + "pwm1_out0b", // 109 + "pwm1_out1a", // 110 + "pwm1_out1b", // 111 + "pwm1_out2a", // 112 + "pwm1_out2b", // 113 + "pwm2_out1h", // 114 + "pwm2_out1l", // 115 + "pwm2_out2h", // 116 + "pwm2_out2l", // 117 + "pwm2_out3h", // 118 + "pwm2_out3l", // 119 + "pwm2_out4h", // 120 + "pwm2_out4l", // 121 + "", // 122 + "", // 123 + "", // 124 + "", // 125 + "", // 126 + "", // 127 + "", // 128 + "", // 129 + "", // 130 + "", // 131 + "", // 132 + "", // 133 + "", // 134 + "", // 135 + "", // 136 + "", // 137 + "", // 138 + "", // 139 + "I2S0O_DATA_out0", // 140 + "I2S0O_DATA_out1", // 141 + "I2S0O_DATA_out2", // 142 + "I2S0O_DATA_out3", // 143 + "I2S0O_DATA_out4", // 144 + "I2S0O_DATA_out5", // 145 + "I2S0O_DATA_out6", // 146 + "I2S0O_DATA_out7", // 147 + "I2S0O_DATA_out8", // 148 + "I2S0O_DATA_out9", // 149 + "I2S0O_DATA_out10", // 150 + "I2S0O_DATA_out11", // 151 + "I2S0O_DATA_out12", // 152 + "I2S0O_DATA_out13", // 153 + "I2S0O_DATA_out14", // 154 + "I2S0O_DATA_out15", // 155 + "I2S0O_DATA_out16", // 156 + "I2S0O_DATA_out17", // 157 + "I2S0O_DATA_out18", // 158 + "I2S0O_DATA_out19", // 159 + "I2S0O_DATA_out20", // 160 + "I2S0O_DATA_out21", // 161 + "I2S0O_DATA_out22", // 162 + "I2S0O_DATA_out23", // 163 + "I2S1I_BCK_out", // 164 + "I2S1I_WS_out", // 165 + "I2S1O_DATA_out0", // 166 + "I2S1O_DATA_out1", // 167 + "I2S1O_DATA_out2", // 168 + "I2S1O_DATA_out3", // 169 + "I2S1O_DATA_out4", // 170 + "I2S1O_DATA_out5", // 171 + "I2S1O_DATA_out6", // 172 + "I2S1O_DATA_out7", // 173 + "I2S1O_DATA_out8", // 174 + "I2S1O_DATA_out9", // 175 + "I2S1O_DATA_out10", // 176 + "I2S1O_DATA_out11", // 177 + "I2S1O_DATA_out12", // 178 + "I2S1O_DATA_out13", // 179 + "I2S1O_DATA_out14", // 180 + "I2S1O_DATA_out15", // 181 + "I2S1O_DATA_out16", // 182 + "I2S1O_DATA_out17", // 183 + "I2S1O_DATA_out18", // 184 + "I2S1O_DATA_out19", // 185 + "I2S1O_DATA_out20", // 186 + "I2S1O_DATA_out21", // 187 + "I2S1O_DATA_out22", // 188 + "I2S1O_DATA_out23", // 189 + "pwm3_out1h", // 190 + "pwm3_out1l", // 191 + "pwm3_out2h", // 192 + "pwm3_out2l", // 193 + "pwm3_out3h", // 194 + "pwm3_out3l", // 195 + "pwm3_out4h", // 196 + "pwm3_out4l", // 197 + "U2TXD_out", // 198 + "U2RTS_out", // 199 + "emac_mdc_o", // 200 + "emac_mdo_o", // 201 + "emac_crs_o", // 202 + "emac_col_o", // 203 + "bt_audio0_irq", // 204 + "bt_audio1_irq", // 205 + "bt_audio2_irq", // 206 + "ble_audio0_irq", // 207 + "ble_audio1_irq", // 208 + "ble_audio2_irq", // 209 + "pcmfsync_out", // 210 + "pcmclk_out", // 211 + "pcmdout", // 212 + "ble_audio_sync0_p", // 213 + "ble_audio_sync1_p", // 214 + "ble_audio_sync2_p", // 215 + "", // 216 + "", // 217 + "", // 218 + "", // 219 + "", // 220 + "", // 221 + "", // 222 + "", // 223 + "sig_in_func224", // 224 + "sig_in_func225", // 225 + "sig_in_func226", // 226 + "sig_in_func227", // 227 + "sig_in_func228", // 228 + "", // 229 + "", // 230 + "", // 231 + "", // 232 + "", // 233 + "", // 234 + "", // 235 + "", // 236 + "", // 237 + "", // 238 + "", // 239 + "", // 240 + "", // 241 + "", // 242 + "", // 243 + "", // 244 + "", // 245 + "", // 246 + "", // 247 + "", // 248 + "", // 249 + "", // 250 + "", // 251 + "", // 252 + "", // 253 + "", // 254 + "", // 255 +}; + + +/** + * Dump the mappings for GPIO pins. + */ +/* static */ void System::dumpPinMapping() { + const int numPins = 40; + printf("GPIO_FUNCn_OUT_SEL_CFG_REG\n"); + printf("--------------------------\n"); + printf("%3s %4s\n", "Pin", "Func"); + for (int i=0; imcu_sel + 1, io_mux->func_ie); + } + } + +} // System#dumpPinMapping + + +/** + * Dump the storage stats for the heap. + */ +/* static */ void System::dumpHeapInfo() { + multi_heap_info_t heapInfo; + + printf(" %10s %10s %10s %10s %13s %11s %12s\n", "Free", "Allocated", "Largest", "Minimum", "Alloc Blocks", "Free Blocks", "Total Blocks"); + heap_caps_get_info(&heapInfo, MALLOC_CAP_EXEC); + printf("EXEC %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_32BIT); + printf("32BIT %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_8BIT); + printf("8BIT %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_DMA); + printf("DMA %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_SPIRAM); + printf("SPISRAM %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_INTERNAL); + printf("INTERNAL %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); + heap_caps_get_info(&heapInfo, MALLOC_CAP_DEFAULT); + printf("DEFAULT %10d %10d %10d %10d %13d %11d %12d\n", heapInfo.total_free_bytes, heapInfo.total_allocated_bytes, heapInfo.largest_free_block, heapInfo.minimum_free_bytes, heapInfo.allocated_blocks, heapInfo.free_blocks, heapInfo.total_blocks); +} // System#dumpHeapInfo + + /** * @brief Get the information about the device. * @param [out] info The structure to be populated on return. diff --git a/cpp_utils/System.h b/cpp_utils/System.h index 6459509d..50337c32 100644 --- a/cpp_utils/System.h +++ b/cpp_utils/System.h @@ -18,6 +18,8 @@ class System { public: System(); virtual ~System(); + static void dumpPinMapping(); // Dump the mappings of pins to functions. + static void dumpHeapInfo(); static void getChipInfo(esp_chip_info_t *info); static size_t getFreeHeapSize(); static std::string getIDFVersion(); diff --git a/cpp_utils/Task.cpp b/cpp_utils/Task.cpp index a8ba9a37..2177b888 100644 --- a/cpp_utils/Task.cpp +++ b/cpp_utils/Task.cpp @@ -43,7 +43,7 @@ Task::~Task() { * @return N/A. */ -void Task::delay(int ms) { +/* static */ void Task::delay(int ms) { ::vTaskDelay(ms/portTICK_PERIOD_MS); } // delay diff --git a/cpp_utils/Task.h b/cpp_utils/Task.h index 0d58f222..f7d88754 100644 --- a/cpp_utils/Task.h +++ b/cpp_utils/Task.h @@ -51,7 +51,7 @@ class Task { * @param [in] data The data passed in to the newly started task. */ virtual void run(void *data) = 0; // Make run pure virtual - void delay(int ms); + static void delay(int ms); private: xTaskHandle m_handle; diff --git a/tasks/watchdogs/README.md b/tasks/watchdogs/README.md new file mode 100644 index 00000000..85c7c5c9 --- /dev/null +++ b/tasks/watchdogs/README.md @@ -0,0 +1,6 @@ +#Watchdogs +This is a sample application that illustrates the capabilities of watchdogs and watchdog processing. + +A related YouTube video is available here: + +https://www.youtube.com/watch?v=C2xF3O6qkbg \ No newline at end of file diff --git a/tools/bootloaderExamine/main.cpp b/tools/bootloaderExamine/main.cpp index 22e8a5aa..60472e30 100644 --- a/tools/bootloaderExamine/main.cpp +++ b/tools/bootloaderExamine/main.cpp @@ -1,5 +1,6 @@ #include #include +#include /* Main header of binary image */ typedef struct { @@ -120,6 +121,11 @@ int main(int argc, char *argv[]) { return 0; } + if (header.magic != 0xE9) { + printf("Failed to find magic number (0xE9) in BIN file header.\n"); + return 0; + } + printf("Dump of ESP32 binary file: %s\n", fileName); printf("magic: 0x%x, segment_count: %d, entry_addr: 0x%x - %s, hash_appended: %d\n", header.magic, header.segment_count, header.entry_addr, area(header.entry_addr), header.hash_appended);