diff --git a/build/platform-id.mk b/build/platform-id.mk index a8254d97ae..443c23f43f 100644 --- a/build/platform-id.mk +++ b/build/platform-id.mk @@ -120,8 +120,6 @@ PLATFORM_MCU=gcc PLATFORM_NET=gcc ARCH=gcc PRODUCT_DESC=GCC xcompile -# explicitly exclude platform headers -SPARK_NO_PLATFORM=1 DEFAULT_PRODUCT_ID=3 endif diff --git a/communication/makefile b/communication/makefile index d5d32ddd60..4c9029ff1e 100644 --- a/communication/makefile +++ b/communication/makefile @@ -7,6 +7,6 @@ TARGET_TYPE = a BUILD_PATH_EXT=$(COMMUNICATION_BUILD_PATH_EXT) -DEPENDENCIES = hal dynalib services wiring crypto +DEPENDENCIES = hal dynalib services wiring crypto platform include ../build/arm-tlm.mk diff --git a/communication/src/communication_dynalib.cpp b/communication/src/communication_dynalib.cpp index 941271f453..f4d05159fc 100644 --- a/communication/src/communication_dynalib.cpp +++ b/communication/src/communication_dynalib.cpp @@ -17,7 +17,7 @@ #define DYNALIB_EXPORT #include "communication_dynalib.h" -#define INTERRUPTS_HAL_EXCLUDE_PLATFORM_HEADERS +// #define INTERRUPTS_HAL_EXCLUDE_PLATFORM_HEADERS #include "core_hal.h" #if PARTICLE_PROTOCOL diff --git a/hal/inc/concurrent_hal.h b/hal/inc/concurrent_hal.h index b5fc24d7c7..71806e63bf 100644 --- a/hal/inc/concurrent_hal.h +++ b/hal/inc/concurrent_hal.h @@ -131,6 +131,8 @@ os_result_t os_thread_cleanup(os_thread_t thread); */ os_result_t os_thread_yield(void); +os_thread_t os_thread_current(void); + /** * Delays the current task until a specified time to set up periodic tasks * @param previousWakeTime The time the thread last woke up. May not be NULL. @@ -229,6 +231,12 @@ int os_timer_change(os_timer_t timer, os_timer_change_t change, bool fromISR, un int os_timer_destroy(os_timer_t timer, void* reserved); int os_timer_is_active(os_timer_t timer, void* reserved); +/** + * Thread local storage + */ +os_result_t os_thread_storage_set(os_thread_t thread, int idx, void* data, void* reserved); +void* os_thread_storage_get(os_thread_t thread, int idx, void* reserved); + #ifdef __cplusplus } #endif diff --git a/hal/inc/hal_dynalib_concurrent.h b/hal/inc/hal_dynalib_concurrent.h index 989945f6db..8b356820f9 100644 --- a/hal/inc/hal_dynalib_concurrent.h +++ b/hal/inc/hal_dynalib_concurrent.h @@ -61,6 +61,9 @@ DYNALIB_FN(24, hal_concurrent, os_queue_destroy, int(os_queue_t, void*)) DYNALIB_FN(25, hal_concurrent, os_queue_put, int(os_queue_t, const void* item, system_tick_t, void*)) DYNALIB_FN(26, hal_concurrent, os_queue_take, int(os_queue_t, void* item, system_tick_t, void*)) DYNALIB_FN(27, hal_concurrent, os_thread_exit, os_result_t(os_thread_t)) +DYNALIB_FN(28, hal_concurrent, os_thread_current, os_thread_t(void)) +DYNALIB_FN(29, hal_concurrent, os_thread_storage_set, os_result_t(os_thread_t, int, void*, void*)) +DYNALIB_FN(30, hal_concurrent, os_thread_storage_get, void*(os_thread_t, int, void*)) #endif // PLATFORM_THREADING DYNALIB_END(hal_concurrent) diff --git a/hal/inc/watchdog_hal.h b/hal/inc/watchdog_hal.h index ea2314efd3..0926f182fb 100644 --- a/hal/inc/watchdog_hal.h +++ b/hal/inc/watchdog_hal.h @@ -24,11 +24,12 @@ */ #ifndef WATCHDOG_HAL_H -#define WATCHDOG_HAL_H +#define WATCHDOG_HAL_H #include +#include -#ifdef __cplusplus +#ifdef __cplusplus extern "C" { #endif @@ -36,10 +37,88 @@ bool HAL_watchdog_reset_flagged(); void HAL_Notify_WDT(); +#define HAL_WATCHDOG_CAPABILITY_NONE (0x00) +// Watchdog timer will reset the CPU +#define HAL_WATCHDOG_CAPABILITY_CPU_RESET (0x01) +// Watchdog is clocked from an independent clock source +#define HAL_WATCHDOG_CAPABILITY_INDEPENDENT (0x02) +// Watchdog counter needs to be reset within a configured window +#define HAL_WATCHDOG_CAPABILITY_WINDOWED (0x04) +// Watchdog timer can be reconfigured +#define HAL_WATCHDOG_CAPABILITY_RECONFIGURABLE (0x08) +// Watchdog timer can somehow notify about its expiration +#define HAL_WATCHDOG_CAPABILITY_NOTIFY (0x10) +// Watchdog timer can be stopped +#define HAL_WATCHDOG_CAPABILITY_STOPPABLE (0x20) -#ifdef __cplusplus +typedef struct { + uint16_t size; + uint16_t version; + + uint8_t running; + uint32_t capabilities; + uint32_t period_us; + uint32_t window_us; +} hal_watchdog_status_t; + +typedef struct { + uint16_t size; + uint16_t version; + + uint32_t capabilities; + uint32_t period_us; + uint32_t window_us; + + void (*notify)(void*); + void* notify_arg; +} hal_watchdog_config_t; + +typedef struct { + uint16_t size; + uint16_t version; + + uint32_t capabilities; + uint32_t capabilities_required; + + uint32_t min_period_us; + uint32_t max_period_us; +} hal_watchdog_info_t; + +/** + * Queries the capabilities of a specific hardware watchdog + */ +int hal_watchdog_query(int idx, hal_watchdog_info_t* info, void* reserved); + +/** + * Starts hardware watchdog (optionally configures it if conf is provided) + */ +int hal_watchdog_start(int idx, hal_watchdog_config_t* conf, void* reserved); + +/** + * Configures specified hardware watchdog + */ +int hal_watchdog_configure(int idx, hal_watchdog_config_t* conf, void* reserved); + +/** + * Stops specified hardware watchdog + */ +int hal_watchdog_stop(int idx, void* reserved); + +/** + * Retrieves running status of specified watchdog + */ +int hal_watchdog_get_status(int idx, hal_watchdog_status_t* status, void* reserved); + +/** + * Kicks specified watchdog, or all of them if idx = -1 + */ +int hal_watchdog_kick(int idx, void* reserved); + +#ifdef __cplusplus } #endif -#endif /* WATCHDOG_HAL_H */ +#include "watchdog_hal_impl.h" + +#endif /* WATCHDOG_HAL_H */ diff --git a/hal/src/core/watchdog_hal_impl.h b/hal/src/core/watchdog_hal_impl.h new file mode 100644 index 0000000000..eeeb7b23c8 --- /dev/null +++ b/hal/src/core/watchdog_hal_impl.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef WATCHDOG_HAL_IMPL_H +#define WATCHDOG_HAL_IMPL_H + +#endif /* WATCHDOG_HAL_IMPL_H */ diff --git a/hal/src/electron/core_hal.c b/hal/src/electron/core_hal.c index 0b13a4f263..5e01fe7476 100644 --- a/hal/src/electron/core_hal.c +++ b/hal/src/electron/core_hal.c @@ -66,7 +66,7 @@ IDX[x] = added IRQ handler 14 [x] PendSV_Handler 15 [x] SysTick_Handler // External Interrupts ---------------- -16 [ ] WWDG_IRQHandler // Window WatchDog +16 [x] WWDG_IRQHandler // Window WatchDog 17 [ ] PVD_IRQHandler // PVD through EXTI Line detection 18 [ ] TAMP_STAMP_IRQHandler // Tamper and TimeStamps through the EXTI line 19 [ ] RTC_WKUP_IRQHandler // RTC Wakeup through the EXTI line @@ -480,7 +480,6 @@ void EXTI15_10_IRQHandler(void) * them for debugging purposes to break on entry and let us know * which interrupt we are not currently handling. */ -void WWDG_IRQHandler(void) {__ASM("bkpt 0");} void PVD_IRQHandler(void) {__ASM("bkpt 0");} void TAMP_STAMP_IRQHandler(void) {__ASM("bkpt 0");} void RTC_WKUP_IRQHandler(void) {__ASM("bkpt 0");} diff --git a/hal/src/electron/modem/mdm_hal.cpp b/hal/src/electron/modem/mdm_hal.cpp index e7622b5916..37fa3523f2 100644 --- a/hal/src/electron/modem/mdm_hal.cpp +++ b/hal/src/electron/modem/mdm_hal.cpp @@ -38,6 +38,7 @@ #include #include "net_hal.h" #include +#include "monitor_service.h" std::recursive_mutex mdm_mutex; @@ -368,7 +369,7 @@ int MDMParser::waitFinalResp(_CALLBACKPTR cb /* = NULL*/, if (type == TYPE_ABORTED) return RESP_ABORTED; // This means the current command was ABORTED, so retry your command if critical. } - // relax a bit + SYSTEM_MONITOR_KICK_CURRENT(); HAL_Delay_Milliseconds(10); } while (!TIMEOUT(start, timeout_ms) && !_cancel_all_operations); @@ -793,9 +794,16 @@ bool MDMParser::registerNet(NetStatus* status /*= NULL*/, system_tick_t timeout_ goto failure; // Now check every 15 seconds for 5 minutes to see if we're connected to the tower (GSM and GPRS) system_tick_t start = HAL_Timer_Get_Milli_Seconds(); + system_tick_t wd_timeout = SYSTEM_MONITOR_GET_TIMEOUT_CURRENT(); while (!checkNetStatus(status) && !TIMEOUT(start, timeout_ms) && !_cancel_all_operations) { system_tick_t start = HAL_Timer_Get_Milli_Seconds(); - while ((HAL_Timer_Get_Milli_Seconds() - start < 15000UL) && !_cancel_all_operations); // just wait + system_tick_t wd_last = 0; + while ((HAL_Timer_Get_Milli_Seconds() - start < 15000UL) && !_cancel_all_operations) { + if (wd_timeout > 0 && (HAL_Timer_Get_Milli_Seconds() - wd_last) > (wd_timeout / 2)) { + SYSTEM_MONITOR_KICK_CURRENT(); + wd_last = HAL_Timer_Get_Milli_Seconds(); + } + } //HAL_Delay_Milliseconds(15000); } if (_net.csd == REG_DENIED) MDM_ERROR("CSD Registration Denied\r\n"); diff --git a/hal/src/gcc/interrupts_irq.h b/hal/src/gcc/interrupts_irq.h new file mode 100644 index 0000000000..0db661571b --- /dev/null +++ b/hal/src/gcc/interrupts_irq.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef INTERRUPTS_IRQ_H +#define INTERRUPTS_IRQ_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int32_t IRQn_Type; + +typedef enum hal_irq_t { + __Last_irq = 0 +} hal_irq_t; + +#ifdef __cplusplus +} +#endif + + +#endif /* INTERRUPTS_IRQ_H */ diff --git a/hal/src/gcc/watchdog_hal_impl.h b/hal/src/gcc/watchdog_hal_impl.h new file mode 100644 index 0000000000..eeeb7b23c8 --- /dev/null +++ b/hal/src/gcc/watchdog_hal_impl.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef WATCHDOG_HAL_IMPL_H +#define WATCHDOG_HAL_IMPL_H + +#endif /* WATCHDOG_HAL_IMPL_H */ diff --git a/hal/src/newhal/watchdog_hal_impl.h b/hal/src/newhal/watchdog_hal_impl.h new file mode 100644 index 0000000000..eeeb7b23c8 --- /dev/null +++ b/hal/src/newhal/watchdog_hal_impl.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef WATCHDOG_HAL_IMPL_H +#define WATCHDOG_HAL_IMPL_H + +#endif /* WATCHDOG_HAL_IMPL_H */ diff --git a/hal/src/photon/core_hal.c b/hal/src/photon/core_hal.c index 3a22f5ab90..f082ffd8cc 100644 --- a/hal/src/photon/core_hal.c +++ b/hal/src/photon/core_hal.c @@ -30,6 +30,7 @@ #include "module_info.h" #include "flash_mal.h" #include "delay_hal.h" +#include "stm32f2xx_wwdg.h" #include void SysTickChain(); @@ -42,6 +43,8 @@ extern char link_interrupt_vectors_location; extern char link_ram_interrupt_vectors_location; extern char link_ram_interrupt_vectors_location_end; +extern void WWDG_IRQHandler(void); + const HAL_InterruptOverrideEntry hal_interrupt_overrides[] = { {HardFault_IRQn, HardFault_Handler}, {MemoryManagement_IRQn, MemManage_Handler}, @@ -56,7 +59,8 @@ const HAL_InterruptOverrideEntry hal_interrupt_overrides[] = { {CAN2_TX_IRQn, CAN2_TX_irq}, {CAN2_RX0_IRQn, CAN2_RX0_irq}, {CAN2_RX1_IRQn, CAN2_RX1_irq}, - {CAN2_SCE_IRQn, CAN2_SCE_irq} + {CAN2_SCE_IRQn, CAN2_SCE_irq}, + {WWDG_IRQn, WWDG_IRQHandler} }; /** diff --git a/hal/src/photon/dct_hal.c b/hal/src/photon/dct_hal.cpp similarity index 89% rename from hal/src/photon/dct_hal.c rename to hal/src/photon/dct_hal.cpp index 8f1c0a9fb4..c6ce9df295 100644 --- a/hal/src/photon/dct_hal.c +++ b/hal/src/photon/dct_hal.cpp @@ -6,6 +6,8 @@ #include #include +#include "monitor_service.h" + extern uint32_t Compute_CRC32(const uint8_t *pBuffer, uint32_t bufferSize); // Compatibility crc32 function for WICED @@ -48,6 +50,10 @@ const void* dct_read_app_data(uint32_t offset) { } int dct_write_app_data(const void* data, uint32_t offset, uint32_t size) { + // Erase of 16kB sector may take up to 800ms according to the datasheet + // 1 write operation takes at most 100us + // (16384 / 4) * 100us = 409ms + SYSTEM_MONITOR_EXPECT_STALL(1300); return wiced_dct_write(data, DCT_APP_SECTION, offset, size); } diff --git a/hal/src/photon/inet_hal.cpp b/hal/src/photon/inet_hal.cpp index 8c1d663b1d..db0575d100 100644 --- a/hal/src/photon/inet_hal.cpp +++ b/hal/src/photon/inet_hal.cpp @@ -25,11 +25,14 @@ #include "inet_hal.h" #include "wiced_tcpip.h" +#include "monitor_service.h" int inet_gethostbyname(const char* hostname, uint16_t hostnameLen, HAL_IPAddress* out_ip_addr, network_interface_t nif, void* reserved) { wiced_ip_address_t address; - wiced_result_t result = wiced_hostname_lookup (hostname, &address, 5000); + const uint32_t timeout = 5000; + SYSTEM_MONITOR_MODIFY_TIMEOUT(timeout * 4 / 3); + wiced_result_t result = wiced_hostname_lookup (hostname, &address, timeout); if (result == WICED_SUCCESS) { HAL_IPV4_SET(out_ip_addr, GET_IPV4_ADDRESS(address)); } @@ -46,6 +49,7 @@ int inet_ping(const HAL_IPAddress* address, network_interface_t nif, uint8_t nTr int count = 0; for (int i=0; iconnected(); } else { @@ -759,10 +763,13 @@ int read_packet_and_dispose(tcp_packet_t& packet, void* buffer, int len, wiced_t int bytes_read = 0; if (!packet.packet) { packet.offset = 0; - wiced_result_t result = wiced_tcp_receive(tcp_socket, &packet.packet, _timeout); - if (result!=WICED_SUCCESS && result!=WICED_TIMEOUT) { - DEBUG("Socket %d receive fail %d", (int)(int)tcp_socket->socket, int(result)); - return -result; + { + SYSTEM_MONITOR_MODIFY_TIMEOUT(_timeout * 4 / 3); + wiced_result_t result = wiced_tcp_receive(tcp_socket, &packet.packet, _timeout); + if (result!=WICED_SUCCESS && result!=WICED_TIMEOUT) { + DEBUG("Socket %d receive fail %d", (int)(int)tcp_socket->socket, int(result)); + return -result; + } } } uint8_t* data; diff --git a/hal/src/photon/softap.cpp b/hal/src/photon/softap.cpp index 720e63e730..58642046e4 100644 --- a/hal/src/photon/softap.cpp +++ b/hal/src/photon/softap.cpp @@ -20,6 +20,7 @@ #include "spark_wiring_wifi_credentials.h" #include "mbedtls/aes.h" #include "device_code.h" +#include "monitor_service.h" #if SOFTAP_HTTP # include "http_server.h" @@ -918,6 +919,10 @@ class SoftAPController { { if (memcmp(&expected, soft_ap, sizeof(expected))) { wiced_dct_read_unlock( soft_ap, WICED_FALSE ); + // Erase of 16kB sector may take up to 800ms according to the datasheet + // 1 write operation takes at most 100us + // (16384 / 4) * 100us = 409ms + SYSTEM_MONITOR_EXPECT_STALL(1500); result = wiced_dct_write(&expected, DCT_WIFI_CONFIG_SECTION, OFFSETOF(platform_dct_wifi_config_t, soft_ap_settings), sizeof(wiced_config_soft_ap_t)); } else { wiced_dct_read_unlock( soft_ap, WICED_FALSE ); diff --git a/hal/src/photon/watchdog_hal.c b/hal/src/photon/watchdog_hal.c index b7e072996a..a343346877 100644 --- a/hal/src/photon/watchdog_hal.c +++ b/hal/src/photon/watchdog_hal.c @@ -25,7 +25,9 @@ /* Includes -----------------------------------------------------------------*/ #include "watchdog_hal.h" +#include "platform_config.h" #include "wiced.h" +#include "monitor_service.h" /* Private typedef ----------------------------------------------------------*/ @@ -50,5 +52,8 @@ bool HAL_watchdog_reset_flagged() void HAL_Notify_WDT() { - platform_watchdog_kick(); } + +// platform_result_t platform_watchdog_kick(void) { +// return 0; +// } diff --git a/hal/src/photon/wlan_hal.cpp b/hal/src/photon/wlan_hal.cpp index 218abeb50b..b897c5e25b 100644 --- a/hal/src/photon/wlan_hal.cpp +++ b/hal/src/photon/wlan_hal.cpp @@ -52,6 +52,7 @@ #include "wiced_security.h" #include "mbedtls_util.h" #include "system_error.h" +#include "monitor_service.h" uint64_t tls_host_get_time_ms_local() { uint64_t time_ms; @@ -76,6 +77,18 @@ LOG_SOURCE_CATEGORY("hal.wlan"); #define WLAN_EAP_SAVE_TLS_SESSION 0 #endif +#ifndef DEFAULT_JOIN_ATTEMPT_TIMEOUT +#define DEFAULT_JOIN_ATTEMPT_TIMEOUT (7000) +#endif /* DEFAULT_JOIN_ATTEMPT_TIMEOUT */ + +#ifndef EAPOL_PACKET_TIMEOUT +#define EAPOL_PACKET_TIMEOUT (5000) +#endif /* EAPOL_PACKET_TIMEOUT */ + +#ifndef SUPPLICANT_HANDSHAKE_ATTEMPT_TIMEOUT +#define SUPPLICANT_HANDSHAKE_ATTEMPT_TIMEOUT (30000) +#endif /* SUPPLICANT_HANDSHAKE_ATTEMPT_TIMEOUT */ + int wlan_clear_enterprise_credentials(); int wlan_set_enterprise_credentials(WLanCredentials* c); @@ -206,9 +219,14 @@ wiced_result_t wlan_initialize_dct() if (result == WICED_SUCCESS) { // the storage may not have been initialized, so device_configured will be 0xFF - if (initialize_dct(wifi_config)) + if (initialize_dct(wifi_config)) { + // Erase of 16kB sector may take up to 800ms according to the datasheet + // 1 write operation takes at most 100us + // (16384 / 4) * 100us = 409ms + SYSTEM_MONITOR_EXPECT_STALL(1300); result = wiced_dct_write((const void*)wifi_config, DCT_WIFI_CONFIG_SECTION, 0, sizeof(*wifi_config)); + } wiced_dct_read_unlock(wifi_config, WICED_TRUE); } return result; @@ -222,7 +240,7 @@ static_ip_config_t* wlan_fetch_saved_ip_config(static_ip_config_t* config) uint32_t HAL_NET_SetNetWatchDog(uint32_t timeOutInMS) { - wiced_watchdog_kick(); + //wiced_watchdog_kick(); return 0; } @@ -242,6 +260,10 @@ int wlan_clear_credentials() if (!result) { memset(wifi_config->stored_ap_list, 0, sizeof(wifi_config->stored_ap_list)); + // Erase of 16kB sector may take up to 800ms according to the datasheet + // 1 write operation takes at most 100us + // (16384 / 4) * 100us = 409ms + SYSTEM_MONITOR_EXPECT_STALL(1300); result = wiced_dct_write((const void*)wifi_config, DCT_WIFI_CONFIG_SECTION, 0, sizeof(*wifi_config)); wiced_dct_read_unlock(wifi_config, WICED_TRUE); @@ -521,6 +543,7 @@ int wlan_supplicant_start() int wlan_supplicant_cancel(int isr) { if (eap_context.supplicant_running) { + SYSTEM_MONITOR_MODIFY_TIMEOUT(EAPOL_PACKET_TIMEOUT * 4 / 3); besl_supplicant_cancel(eap_context.supplicant_workspace, isr); } return 0; @@ -532,6 +555,7 @@ int wlan_supplicant_stop() if (eap_context.initialized) { if (eap_context.supplicant_initialized) { + SYSTEM_MONITOR_MODIFY_TIMEOUT(SUPPLICANT_HANDSHAKE_ATTEMPT_TIMEOUT * 4 / 3); besl_supplicant_deinit(eap_context.supplicant_workspace); eap_context.supplicant_initialized = false; } @@ -578,6 +602,7 @@ static wiced_result_t wlan_join() { while (attempt-- && !wiced_network_up_cancel) { for (unsigned i = 0; i < CONFIG_AP_LIST_SIZE; i++) { + SYSTEM_MONITOR_KICK_CURRENT(); const wiced_config_ap_entry_t& ap = wifi_config->stored_ap_list[i]; if (ap.details.SSID.length == 0) { @@ -615,7 +640,11 @@ static wiced_result_t wlan_join() { LOG(INFO, "Joining %s", ssid_name); HAL_Core_Runtime_Info(&info, NULL); LOG(TRACE, "Free RAM connect: %lu", info.freeheap); - result = wiced_join_ap_specific((wiced_ap_info_t*)&ap.details, ap.security_key_length, ap.security_key); + + { + SYSTEM_MONITOR_MODIFY_TIMEOUT((!suppl ? DEFAULT_JOIN_ATTEMPT_TIMEOUT : SUPPLICANT_HANDSHAKE_ATTEMPT_TIMEOUT) * 4 / 3); + result = wiced_join_ap_specific((wiced_ap_info_t*)&ap.details, ap.security_key_length, ap.security_key); + } if (result != WICED_SUCCESS) { LOG(ERROR, "wiced_join_ap_specific(), result: %d", (int)result); } @@ -635,6 +664,7 @@ static wiced_result_t wlan_join() { LOG(TRACE, "Free RAM after suppl stop: %lu", info.freeheap); if (!result) { + SYSTEM_MONITOR_KICK_CURRENT(); // Workaround. After successfully authenticating to Enterprise access point, TCP/IP stack // gets into a weird state. Reinitializing LwIP etc mitigates the issue. // ¯\_(ツ)_/¯ @@ -644,6 +674,7 @@ static wiced_result_t wlan_join() { } if (result == (wiced_result_t)WWD_SET_BLOCK_ACK_WINDOW_FAIL) { + SYSTEM_MONITOR_KICK_CURRENT(); // Reset wireless module wlan_restart(NULL); HAL_Core_Runtime_Info(&info, NULL); @@ -732,6 +763,7 @@ wlan_result_t wlan_connect_finalize() break; default: INFO("Bringing WiFi interface up with DHCP"); + SYSTEM_MONITOR_MODIFY_TIMEOUT(WICED_DHCP_IP_ADDRESS_RESOLUTION_TIMEOUT + 100); result = wiced_network_up(WICED_STA_INTERFACE, WICED_USE_EXTERNAL_DHCP_SERVER, NULL); break; } @@ -1102,8 +1134,14 @@ wiced_result_t add_wiced_wifi_credentials(const char *ssid, uint16_t ssidLen, co } entry.details.security = security; entry.details.channel = channel; - result = wiced_dct_write((const void*)wifi_config, DCT_WIFI_CONFIG_SECTION, 0, - sizeof(*wifi_config)); + // Erase of 16kB sector may take up to 800ms according to the datasheet + // 1 write operation takes at most 100us + // (16384 / 4) * 100us = 409ms + { + SYSTEM_MONITOR_EXPECT_STALL(1300); + result = wiced_dct_write((const void*)wifi_config, DCT_WIFI_CONFIG_SECTION, 0, + sizeof(*wifi_config)); + } if (!result) wifi_creds_changed = true; wiced_dct_read_unlock(wifi_config, WICED_TRUE); @@ -1165,8 +1203,13 @@ int wlan_set_enterprise_credentials(WLanCredentials* c) memcpy(sec->private_key, c->private_key, c->private_key_len); } } - - wiced_dct_write(sec, DCT_SECURITY_SECTION, 0, sizeof(platform_dct_security_t)); + // Erase of 16kB sector may take up to 800ms according to the datasheet + // 1 write operation takes at most 100us + // (16384 / 4) * 100us = 409ms + { + SYSTEM_MONITOR_EXPECT_STALL(1300); + wiced_dct_write(sec, DCT_SECURITY_SECTION, 0, sizeof(platform_dct_security_t)); + } free(sec); sec = NULL; @@ -1260,6 +1303,7 @@ void wlan_smart_config_init() softap_config config; config.softap_complete = HAL_WLAN_notify_simple_config_done; wlan_disconnect_now(); + SYSTEM_MONITOR_KICK_CURRENT(); wlan_restart(NULL); current_softap_handle = softap_start(&config); } @@ -1271,6 +1315,7 @@ bool wlan_smart_config_finalize() { softap_stop(current_softap_handle); wlan_disconnect_now(); // force a full refresh + SYSTEM_MONITOR_KICK_CURRENT(); wlan_restart(NULL); HAL_Delay_Milliseconds(5); wlan_activate(); diff --git a/hal/src/stm32f2xx/concurrent_hal.cpp b/hal/src/stm32f2xx/concurrent_hal.cpp index 3f6ed94e2d..857dce1322 100644 --- a/hal/src/stm32f2xx/concurrent_hal.cpp +++ b/hal/src/stm32f2xx/concurrent_hal.cpp @@ -105,6 +105,14 @@ os_result_t os_thread_yield(void) return 0; } +os_thread_t os_thread_current() +{ + if (!HAL_IsISR()) { + return xTaskGetCurrentTaskHandle(); + } + return OS_THREAD_INVALID_HANDLE; +} + /** * Determines if the thread stack is still within the allocated region. * @param thread The thread to check @@ -519,3 +527,24 @@ void __flash_release() { } flash_lock.unlock(); } + +os_result_t os_thread_storage_set(os_thread_t thread, int idx, void* data, void* reserved) +{ +#if configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 + if (idx >= 0 && idx < configNUM_THREAD_LOCAL_STORAGE_POINTERS) { + vTaskSetThreadLocalStoragePointer(thread, (BaseType_t)idx, data); + return 0; + } +#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 */ + return 1; +} + +void* os_thread_storage_get(os_thread_t thread, int idx, void* reserved) +{ +#if configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 + if (idx >= 0 && idx < configNUM_THREAD_LOCAL_STORAGE_POINTERS) { + return pvTaskGetThreadLocalStoragePointer(thread, (BaseType_t)idx); + } +#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 */ + return nullptr; +} diff --git a/hal/src/stm32f2xx/ota_flash_hal_stm32f2xx.cpp b/hal/src/stm32f2xx/ota_flash_hal_stm32f2xx.cpp index f88019118c..e7df473042 100644 --- a/hal/src/stm32f2xx/ota_flash_hal_stm32f2xx.cpp +++ b/hal/src/stm32f2xx/ota_flash_hal_stm32f2xx.cpp @@ -41,6 +41,8 @@ // For ATOMIC_BLOCK #include "spark_wiring_interrupts.h" +#include "monitor_service.h" + #define OTA_CHUNK_SIZE (512) #define BOOTLOADER_RANDOM_BACKOFF_MIN (200) #define BOOTLOADER_RANDOM_BACKOFF_MAX (1000) @@ -238,12 +240,17 @@ uint16_t HAL_OTA_ChunkSize() bool HAL_FLASH_Begin(uint32_t address, uint32_t length, void* reserved) { + // Erase of 128k sector may take up to 4 seconds + // Worst case scenario we may be erasing 2 128k sectors + SYSTEM_MONITOR_EXPECT_STALL(8100); FLASH_Begin(address, length); return true; } int HAL_FLASH_Update(const uint8_t *pBuffer, uint32_t address, uint32_t length, void* reserved) { + // Every write operation takes 100us at most + SYSTEM_MONITOR_EXPECT_STALL(10 + (length / 4 * 100) / 1000); return FLASH_Update(pBuffer, address, length); } @@ -311,6 +318,9 @@ hal_update_complete_t HAL_FLASH_End(hal_module_t* mod) // bootloader is copied directly if (function==MODULE_FUNCTION_BOOTLOADER) { + // Erase: 2400ms max + // Every write operation takes 100us at most + SYSTEM_MONITOR_EXPECT_STALL(2500 + (moduleLength / 4 * 100) / 1000); result = flash_bootloader(&module, moduleLength); } else diff --git a/hal/src/stm32f2xx/watchdog_hal.c b/hal/src/stm32f2xx/watchdog_hal.c new file mode 100644 index 0000000000..bb314b0a04 --- /dev/null +++ b/hal/src/stm32f2xx/watchdog_hal.c @@ -0,0 +1,322 @@ +/* + * Copyright (c) 2017 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include +#include "watchdog_hal.h" +#include "platform_config.h" +#include "hal_irq_flag.h" +#include "stm32f2xx_wwdg.h" +#include "stm32f2xx_iwdg.h" +#include "timer_hal.h" + +#define HAL_WATCHDOG_WWDG (0) +#define HAL_WATCHDOG_IWDG (1) + +#define HAL_WATCHDOG_WWDG_CAPABILITIES (HAL_WATCHDOG_CAPABILITY_WINDOWED | \ + HAL_WATCHDOG_CAPABILITY_RECONFIGURABLE | \ + HAL_WATCHDOG_CAPABILITY_NOTIFY | \ + HAL_WATCHDOG_CAPABILITY_CPU_RESET | \ + HAL_WATCHDOG_CAPABILITY_STOPPABLE) +#define HAL_WATCHDOG_WWDG_MIN_PERIOD_US (137UL) // 136.53us +#define HAL_WATCHDOG_WWDG_MAX_PERIOD_US (69910UL) // 69.91ms + +#define HAL_WATCHDOG_IWDG_CAPABILITIES (HAL_WATCHDOG_CAPABILITY_INDEPENDENT | \ + HAL_WATCHDOG_CAPABILITY_CPU_RESET |\ + HAL_WATCHDOG_CAPABILITY_RECONFIGURABLE) +#define HAL_WATCHDOG_IWDG_MIN_PERIOD_US (125UL) // 125us +#define HAL_WATCHDOG_IWDG_MAX_PERIOD_US (32700000UL) // 32.7s + +typedef struct { + uint8_t running; + uint32_t capabilities; + uint32_t period_us; + uint32_t window_us; + uint32_t counter; + void (*notify)(void*); + void* notify_arg; + uint32_t last_kick; +} hal_watchdog_runtime_info_t; + +static hal_watchdog_runtime_info_t s_watchdogs[HAL_WATCHDOG_COUNT] = {}; + +static uint32_t next_power_of_two(uint32_t v) { + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +int hal_watchdog_query(int idx, hal_watchdog_info_t* info, void* reserved) { + if (info == NULL) { + return -1; + } + + if (idx == HAL_WATCHDOG_WWDG) { + info->capabilities = info->capabilities_required = HAL_WATCHDOG_WWDG_CAPABILITIES; + info->min_period_us = HAL_WATCHDOG_WWDG_MIN_PERIOD_US; + info->max_period_us = HAL_WATCHDOG_WWDG_MAX_PERIOD_US; + return 0; + } else if (idx == HAL_WATCHDOG_IWDG) { + info->capabilities = info->capabilities_required = HAL_WATCHDOG_IWDG_CAPABILITIES; + info->min_period_us = HAL_WATCHDOG_IWDG_MIN_PERIOD_US; + info->max_period_us = HAL_WATCHDOG_IWDG_MAX_PERIOD_US; + return 0; + } + + return -1; +} + +int hal_watchdog_configure(int idx, hal_watchdog_config_t* conf, void* reserved) { + hal_watchdog_runtime_info_t* info = &s_watchdogs[idx]; + + if (idx == HAL_WATCHDOG_WWDG) { + RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE); + + RCC_ClocksTypeDef clocks = {}; + RCC_GetClocksFreq(&clocks); + + static const uint32_t wwdg_max_counter_value = 63; + + // Calculate prescaler + // prescaler = PCLK1 / (counter / timeout) + // Calculate with counter = 63 (maximum) for the best resolution + uint32_t prescaler = 1 + ((clocks.PCLK1_Frequency / ((wwdg_max_counter_value * 1000000UL) / conf->period_us)) - 1) / 4096; + + if (prescaler <= 1) { + prescaler = 1; + } else if (prescaler <= 2) { + prescaler = 2; + } else if (prescaler <= 4) { + prescaler = 4; + } else if (prescaler > 4) { + prescaler = 8; + } + + // Calculate counter + // counter = timeout * clock + uint32_t clock = clocks.PCLK1_Frequency / 4096 / prescaler; + uint32_t counter = (conf->period_us * clock) / 1000000; + if (counter > wwdg_max_counter_value) { + counter = wwdg_max_counter_value; + } + + // Calculate window counter value + uint32_t window = (conf->period_us - conf->window_us) * clock / 1000000; + if (window > wwdg_max_counter_value) { + window = wwdg_max_counter_value; + } + + switch (prescaler) { + case 1: { + prescaler = WWDG_Prescaler_1; + break; + } + case 2: { + prescaler = WWDG_Prescaler_2; + break; + } + case 4: { + prescaler = WWDG_Prescaler_4; + break; + } + case 8: { + prescaler = WWDG_Prescaler_8; + break; + } + } + + WWDG_SetPrescaler(prescaler); + WWDG_SetWindowValue(0x40 | window); + WWDG_SetCounter(0x40 | counter); + + info->period_us = ((counter + 1) * 1000000) / clock; + info->window_us = ((window + 1) * 1000000) / clock; + info->counter = counter; + + if (conf->capabilities & HAL_WATCHDOG_CAPABILITY_NOTIFY) { + int32_t state = HAL_disable_irq(); + if (conf->notify != NULL) { + info->notify = conf->notify; + info->notify_arg = conf->notify_arg; + } + HAL_enable_irq(state); + } + } else if (idx == HAL_WATCHDOG_IWDG) { + // Calculate prescaler + // prescaler = lsi_clock / (counter / timeout) + static const uint32_t lsi_frequency = 32000; + static const uint32_t iwdg_max_counter_value = 0x0fff; + // Calculate with counter = iwdg_max_counter_value for the best resolution + uint32_t prescaler = (lsi_frequency / ((iwdg_max_counter_value * 1000000UL) / conf->period_us)); + + prescaler = next_power_of_two(prescaler); + + if (prescaler < 4) { + prescaler = 4; + } + + if (prescaler > 256) { + prescaler = 256; + } + + // Calculate counter + // counter = timeout * clock + uint32_t iwdg_clock = lsi_frequency / prescaler; + uint32_t counter = (conf->period_us * iwdg_clock) / 1000000; + + switch (prescaler) { + case 4: { + prescaler = IWDG_Prescaler_4; + break; + } + case 8: { + prescaler = IWDG_Prescaler_8; + break; + } + case 16: { + prescaler = IWDG_Prescaler_16; + break; + } + case 32: { + prescaler = IWDG_Prescaler_32; + break; + } + case 64: { + prescaler = IWDG_Prescaler_64; + break; + } + case 128: { + prescaler = IWDG_Prescaler_128; + break; + } + case 256: { + prescaler = IWDG_Prescaler_256; + break; + } + } + + IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable); + if (info->running) { + while(IWDG_GetFlagStatus(IWDG_FLAG_RVU) == SET); + } + IWDG_SetReload(counter); + if (info->running) { + while(IWDG_GetFlagStatus(IWDG_FLAG_PVU) == SET); + } + IWDG_SetPrescaler(prescaler); + IWDG_ReloadCounter(); + + info->period_us = (counter * 1000000) / iwdg_clock; + info->counter = counter; + } else { + return -1; + } + + info->capabilities = conf->capabilities; + + return 0; +} + +int hal_watchdog_start(int idx, hal_watchdog_config_t* conf, void* reserved) { + hal_watchdog_runtime_info_t* info = &s_watchdogs[idx]; + + if (info->running == 1) { + return -1; + } + + if (idx == HAL_WATCHDOG_WWDG) { + if (conf != NULL) { + hal_watchdog_configure(idx, conf, reserved); + } + + WWDG_ClearFlag(); + WWDG_EnableIT(); + // Enable watchdog + WWDG->CR |= WWDG_CR_WDGA; + // Maximum priority + NVIC_SetPriority(WWDG_IRQn, 0); + NVIC_EnableIRQ(WWDG_IRQn); + info->running = 1; + } else if (idx == HAL_WATCHDOG_IWDG) { + if (conf != NULL) { + hal_watchdog_configure(idx, conf, reserved); + } + IWDG_Enable(); + info->running = 1; + } else { + return -1; + } + + return 0; +} + +int hal_watchdog_stop(int idx, void* reserved) { + if (idx != HAL_WATCHDOG_WWDG) { + return -1; + } + + WWDG_DeInit(); + + s_watchdogs[idx].running = 0; + + return 0; +} + +int hal_watchdog_get_status(int idx, hal_watchdog_status_t* status, void* reserved) { + if (idx >= HAL_WATCHDOG_COUNT) { + return -1; + } + + if (status == NULL || status->size == 0) { + return -1; + } + + const hal_watchdog_runtime_info_t* info = &s_watchdogs[idx]; + + status->running = info->running; + status->capabilities = info->capabilities; + status->period_us = info->period_us; + status->window_us = info->window_us; + + return 0; +} + +int hal_watchdog_kick(int idx, void* reserved) { + if ((idx == HAL_WATCHDOG_WWDG || idx < 0) && s_watchdogs[HAL_WATCHDOG_WWDG].running) { + WWDG_SetCounter(0x40 | s_watchdogs[HAL_WATCHDOG_WWDG].counter); + s_watchdogs[HAL_WATCHDOG_WWDG].last_kick = HAL_Timer_Get_Milli_Seconds(); + } + if ((idx == HAL_WATCHDOG_IWDG || idx < 0) && s_watchdogs[HAL_WATCHDOG_IWDG].running) { + IWDG_ReloadCounter(); + s_watchdogs[HAL_WATCHDOG_IWDG].last_kick = HAL_Timer_Get_Milli_Seconds(); + } + + return 0; +} + +void WWDG_IRQHandler(void) { + // hal_watchdog_kick(HAL_WATCHDOG_WWDG, NULL); + if (s_watchdogs[HAL_WATCHDOG_WWDG].notify != NULL) { + s_watchdogs[HAL_WATCHDOG_WWDG].notify(s_watchdogs[HAL_WATCHDOG_WWDG].notify_arg); + } + WWDG_ClearFlag(); +} + diff --git a/hal/src/stm32f2xx/watchdog_hal_impl.h b/hal/src/stm32f2xx/watchdog_hal_impl.h new file mode 100644 index 0000000000..fb6fb91213 --- /dev/null +++ b/hal/src/stm32f2xx/watchdog_hal_impl.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef WATCHDOG_HAL_IMPL_H +#define WATCHDOG_HAL_IMPL_H + +#define HAL_WATCHDOG_COUNT (2) +#define HAL_WATCHDOG_DEFAULT (0) +#define HAL_WATCHDOG_INDEPENDENT (1) + +#endif /* WATCHDOG_HAL_IMPL_H */ diff --git a/modules/electron/system-part1/makefile b/modules/electron/system-part1/makefile index 5ed49c397b..210b33d792 100644 --- a/modules/electron/system-part1/makefile +++ b/modules/electron/system-part1/makefile @@ -19,6 +19,7 @@ LIBS += $(LIB_DEPENDENCIES) LIB_DEPS += $(SERVICES_DYNALIB_LIB_DEP) $(RT_DYNALIB_LIB_DEP) $(PLATFORM_LIB_DEP) LIB_DIRS += $(dir $(LIB_DEPS)) +GLOBAL_DEFINES += SYSTEM_MONITOR_USE_CALLBACKS2 TARGET=elf bin lst hex size diff --git a/modules/electron/system-part1/module_system_part3_export.ld b/modules/electron/system-part1/module_system_part3_export.ld index 690a5ed1df..aa1c61e6bc 100644 --- a/modules/electron/system-part1/module_system_part3_export.ld +++ b/modules/electron/system-part1/module_system_part3_export.ld @@ -1,8 +1,8 @@ system_part3_start = 0x8060000; -system_part3_ram_end = 0x2001D800 /* 0x20200000-10K */; -system_part3_ram_start = 0x2001c000 /* end of SRAM - 16K */; +system_part3_ram_end = 0x2001D800 - 1K /* 0x20200000-10K-1K */; +system_part3_ram_start = 0x2001c000 - 1K /* end of SRAM - 16K - 1K */; system_part3_module_info_size = 24; @@ -27,3 +27,4 @@ PROVIDE ( dynalib_location_hal_usb = system_part3_module_table + 4 ); PROVIDE ( dynalib_location_hal_cellular = system_part3_module_table + 8 ); PROVIDE ( dynalib_location_hal_socket = system_part3_module_table + 12 ); PROVIDE ( dynalib_location_hal_bootloader = system_part3_module_table + 16 ); +PROVIDE ( dynalib_location_services2 = system_part3_module_table + 20 ); diff --git a/modules/electron/system-part1/src/export_services2.c b/modules/electron/system-part1/src/export_services2.c new file mode 100644 index 0000000000..c1d5a124ab --- /dev/null +++ b/modules/electron/system-part1/src/export_services2.c @@ -0,0 +1,4 @@ +#define DYNALIB_EXPORT +#define TRACER_SKIP_PLATFORM +#include "monitor_service.h" +#include "services2_dynalib.h" diff --git a/modules/electron/system-part1/src/module_system_part3.cpp b/modules/electron/system-part1/src/module_system_part3.cpp index 2635383be6..51b528dc9d 100644 --- a/modules/electron/system-part1/src/module_system_part3.cpp +++ b/modules/electron/system-part1/src/module_system_part3.cpp @@ -13,6 +13,7 @@ DYNALIB_TABLE_EXTERN(hal_usb); DYNALIB_TABLE_EXTERN(hal_cellular); DYNALIB_TABLE_EXTERN(hal_socket); DYNALIB_TABLE_EXTERN(hal_bootloader); +DYNALIB_TABLE_EXTERN(services2); /** * The order of these declarations MUST MATCH the order of declarations in @@ -23,6 +24,7 @@ extern "C" __attribute__((externally_visible)) const void* const system_part3_mo DYNALIB_TABLE_NAME(hal_usb), DYNALIB_TABLE_NAME(hal_cellular), DYNALIB_TABLE_NAME(hal_socket), - DYNALIB_TABLE_NAME(hal_bootloader) + DYNALIB_TABLE_NAME(hal_bootloader), + DYNALIB_TABLE_NAME(services2) }; diff --git a/modules/electron/system-part1/src/services2.cpp b/modules/electron/system-part1/src/services2.cpp new file mode 100644 index 0000000000..b3595ab62c --- /dev/null +++ b/modules/electron/system-part1/src/services2.cpp @@ -0,0 +1 @@ +#include "../../../../services/src/monitor_service.cpp" diff --git a/modules/electron/system-part2/makefile b/modules/electron/system-part2/makefile index 603183093e..e0829f98cd 100644 --- a/modules/electron/system-part2/makefile +++ b/modules/electron/system-part2/makefile @@ -15,6 +15,7 @@ LIBS += $(MAKE_DEPENDENCIES) LIB_DEPS += $(COMMUNICATION_LIB_DEP) $(HAL_DYNALIB_LIB_DEP) $(SERVICES_LIB_DEP) $(PLATFORM_LIB_DEP) $(CRYPTO_LIB_DEP) LIB_DIRS += $(dir $(LIB_DEPS)) +GLOBAL_DEFINES += SYSTEM_MONITOR_USE_CALLBACKS TARGET=elf bin lst hex size diff --git a/modules/electron/system-part3/linker.ld b/modules/electron/system-part3/linker.ld index 7e958284e3..5149357202 100644 --- a/modules/electron/system-part3/linker.ld +++ b/modules/electron/system-part3/linker.ld @@ -13,7 +13,7 @@ MEMORY The value given here is the sum of system_static_ram_size and stack_size */ - SRAM (rwx) : ORIGIN = 0x20020000 - 10K, LENGTH = 10K + SRAM (rwx) : ORIGIN = 0x20020000 - 11K, LENGTH = 11K INCLUDE backup_ram_memory.ld } diff --git a/modules/electron/system-part3/module_system_part2_export.ld b/modules/electron/system-part3/module_system_part2_export.ld index f6477df097..72b4e2eb83 100644 --- a/modules/electron/system-part3/module_system_part2_export.ld +++ b/modules/electron/system-part3/module_system_part2_export.ld @@ -42,7 +42,7 @@ min_heap_size = 16K; * Can be expanded or reduced as necessary. The heap dynamically expands from * it's static base to the heap top, which is the bottom of this module's static ram. */ -system_part2_ram_size = 6K; +system_part2_ram_size = 7K; /* Place system part2 static RAM immediately below stack. */ system_part2_ram_start = ALIGN( stack_start - system_part2_ram_size, 512); diff --git a/modules/electron/system-part3/src/import_services2.c b/modules/electron/system-part3/src/import_services2.c new file mode 100644 index 0000000000..4d2a3aa1e4 --- /dev/null +++ b/modules/electron/system-part3/src/import_services2.c @@ -0,0 +1,2 @@ +#define DYNALIB_IMPORT +#include "services2_dynalib.h" diff --git a/modules/electron/user-part/module_user_memory.ld b/modules/electron/user-part/module_user_memory.ld index 903c27a8bd..943d173908 100644 --- a/modules/electron/user-part/module_user_memory.ld +++ b/modules/electron/user-part/module_user_memory.ld @@ -5,4 +5,4 @@ user_module_app_flash_length = 128K; /* The SRAM Origin is system_part1_module_ram_end, and extends to system_static_ram_start */ user_module_sram_origin = 0x20000400; -user_module_sram_length = 0x20000 - 0x400 - 16K; +user_module_sram_length = 0x20000 - 0x400 - 16K - 1K; diff --git a/modules/photon/system-part1/makefile b/modules/photon/system-part1/makefile index 3f5ff167e5..03788168aa 100644 --- a/modules/photon/system-part1/makefile +++ b/modules/photon/system-part1/makefile @@ -17,6 +17,8 @@ LIBS += $(filter-out hal,$(MAKE_DEPENDENCIES)) LIB_DEPS += $(COMMUNICATION_LIB_DEP) $(HAL_DYNALIB_LIB_DEP) $(SERVICES_LIB_DEP) $(PLATFORM_LIB_DEP) $(CRYPTO_LIB_DEP) LIB_DIRS += $(dir $(LIB_DEPS)) +GLOBAL_DEFINES += SYSTEM_MONITOR_USE_CALLBACKS + TARGET=elf bin lst hex size include $(PROJECT_ROOT)/build/arm-tlm.mk diff --git a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/dct.h b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/dct.h index d97184ab4a..7f13f5f159 100644 --- a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/dct.h +++ b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/dct.h @@ -221,7 +221,6 @@ int dct_read_app_data_unlock(uint32_t offset); int dct_write_app_data( const void* data, uint32_t offset, uint32_t size ); - #ifdef __cplusplus } #endif diff --git a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/flash_storage_impl.h b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/flash_storage_impl.h index 37c90cac0e..c6cbf5b007 100644 --- a/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/flash_storage_impl.h +++ b/platform/MCU/STM32F2xx/SPARK_Firmware_Driver/inc/flash_storage_impl.h @@ -19,6 +19,7 @@ #include "flash_mal.h" #include "string.h" +#include "monitor_service.h" /** * Implements access to the internal flash, providing the interface expected by dcd.h @@ -28,6 +29,8 @@ class InternalFlashStore public: int eraseSector(unsigned address) { + // Erase of 64kB sector may take up to 2400ms according to the datasheet + SYSTEM_MONITOR_EXPECT_STALL(2500); return !FLASH_EraseMemory(FLASH_INTERNAL, address, 1); } @@ -39,6 +42,9 @@ class InternalFlashStore FLASH_Unlock(); FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); + // Allow up to 1700ms: 1 write operation takes at most 100us + // (65536 / 4) * 100us = 1648.4ms + SYSTEM_MONITOR_EXPECT_STALL(500); while (data_ptr < end_ptr) { FLASH_Status status; diff --git a/platform/MCU/STM32F2xx/STM32_StdPeriph_Driver/src/sources.mk b/platform/MCU/STM32F2xx/STM32_StdPeriph_Driver/src/sources.mk index 16d94ca2bf..d7d42a285a 100644 --- a/platform/MCU/STM32F2xx/STM32_StdPeriph_Driver/src/sources.mk +++ b/platform/MCU/STM32F2xx/STM32_StdPeriph_Driver/src/sources.mk @@ -19,6 +19,7 @@ CSRC += $(TARGET_STDPERIPH_SRC_PATH)/stm32f2xx_flash.c CSRC += $(TARGET_STDPERIPH_SRC_PATH)/stm32f2xx_gpio.c CSRC += $(TARGET_STDPERIPH_SRC_PATH)/stm32f2xx_i2c.c CSRC += $(TARGET_STDPERIPH_SRC_PATH)/stm32f2xx_iwdg.c +CSRC += $(TARGET_STDPERIPH_SRC_PATH)/stm32f2xx_wwdg.c CSRC += $(TARGET_STDPERIPH_SRC_PATH)/stm32f2xx_pwr.c CSRC += $(TARGET_STDPERIPH_SRC_PATH)/stm32f2xx_rcc.c CSRC += $(TARGET_STDPERIPH_SRC_PATH)/stm32f2xx_rng.c diff --git a/services/inc/monitor_service.h b/services/inc/monitor_service.h new file mode 100644 index 0000000000..c9a37b03b7 --- /dev/null +++ b/services/inc/monitor_service.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2017 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef SERVICES_MONITOR_SERVICE_H +#define SERVICES_MONITOR_SERVICE_H + +#include +#include "system_tick_hal.h" + +#ifndef SPARK_PLATFORM +#define SYSTEM_MONITOR_SKIP_PLATFORM +#endif /* SPARK_PLATFORM */ + +#include "module_info.h" +#include "watchdog_hal.h" + +#if defined(HAL_WATCHDOG_COUNT) && PLATFORM_THREADING && MODULE_FUNCTION != MOD_FUNC_BOOTLOADER +# define SYSTEM_MONITOR_ENABLED (1) +#else +# define SYSTEM_MONITOR_ENABLED (0) +#endif /* defined(PLATFORM_WATCHDOG_COUNT) && PLATFORM_THREADING */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef int (*system_monitor_kick_current_callback_t)(void*); +typedef system_tick_t (*system_monitor_get_timeout_current_callback_t)(void*); +typedef int (*system_monitor_set_timeout_current_callback_t)(system_tick_t, void*); +typedef int (*system_monitor_suspend_callback_t)(system_tick_t, void*); +typedef int (*system_monitor_resume_callback_t)(void*); + +typedef struct { + uint16_t size; + uint16_t version; + + system_monitor_kick_current_callback_t system_monitor_kick_current; + system_monitor_get_timeout_current_callback_t system_monitor_get_timeout_current; + system_monitor_set_timeout_current_callback_t system_monitor_set_timeout_current; + system_monitor_suspend_callback_t system_monitor_suspend; + system_monitor_resume_callback_t system_monitor_resume; +} system_monitor_callbacks_t; + +int system_monitor_set_callbacks(system_monitor_callbacks_t* callbacks, void* reserved); +int system_monitor_set_callbacks_(system_monitor_callbacks_t* callbacks, void* reserved); + +int system_monitor_kick_current(void* reserved); +int system_monitor_kick_current_(void* reserved); +int system_monitor_kick_current__(void* reserved); + +system_tick_t system_monitor_get_timeout_current(void* reserved); +int system_monitor_set_timeout_current(system_tick_t timeout, void* reserved); + +system_tick_t system_monitor_get_timeout_current_(void* reserved); +int system_monitor_set_timeout_current_(system_tick_t timeout, void* reserved); + +system_tick_t system_monitor_get_timeout_current__(void* reserved); +int system_monitor_set_timeout_current__(system_tick_t timeout, void* reserved); + +int system_monitor_suspend(system_tick_t timeout, void* reserved); +int system_monitor_suspend_(system_tick_t timeout, void* reserved); +int system_monitor_suspend__(system_tick_t timeout, void* reserved); + +int system_monitor_resume(void* reserved); +int system_monitor_resume_(void* reserved); +int system_monitor_resume__(void* reserved); + +#if SYSTEM_MONITOR_ENABLED == 1 +# if defined(SYSTEM_MONITOR_USE_CALLBACKS) +# define SYSTEM_MONITOR_KICK_CURRENT() system_monitor_kick_current_(NULL) +# define SYSTEM_MONITOR_GET_TIMEOUT_CURRENT() system_monitor_get_timeout_current_(NULL) +# define SYSTEM_MONITOR_SET_TIMEOUT_CURRENT(timeout) system_monitor_set_timeout_current_(timeout, NULL) +# define SYSTEM_MONITOR_SUSPEND(timeout) system_monitor_suspend_(timeout, NULL) +# define SYSTEM_MONITOR_RESUME() system_monitor_resume_(NULL) +# elif defined(SYSTEM_MONITOR_USE_CALLBACKS2) +# define SYSTEM_MONITOR_KICK_CURRENT() system_monitor_kick_current__(NULL) +# define SYSTEM_MONITOR_GET_TIMEOUT_CURRENT() system_monitor_get_timeout_current__(NULL) +# define SYSTEM_MONITOR_SET_TIMEOUT_CURRENT(timeout) system_monitor_set_timeout_current__(timeout, NULL) +# define SYSTEM_MONITOR_SUSPEND(timeout) system_monitor_suspend__(timeout, NULL) +# define SYSTEM_MONITOR_RESUME() system_monitor_resume__(NULL) +# else +# define SYSTEM_MONITOR_KICK_CURRENT() system_monitor_kick_current(NULL) +# define SYSTEM_MONITOR_GET_TIMEOUT_CURRENT() system_monitor_get_timeout_current(NULL) +# define SYSTEM_MONITOR_SET_TIMEOUT_CURRENT(timeout) system_monitor_set_timeout_current(timeout, NULL) +# define SYSTEM_MONITOR_SUSPEND(timeout) system_monitor_suspend(timeout, NULL) +# define SYSTEM_MONITOR_RESUME() system_monitor_resume(NULL) +# endif +#else /* SYSTEM_MONITOR_ENABLED == 1 */ +# define SYSTEM_MONITOR_KICK_CURRENT() +# define SYSTEM_MONITOR_GET_TIMEOUT_CURRENT() (0) +# define SYSTEM_MONITOR_SET_TIMEOUT_CURRENT(timeout) +# define SYSTEM_MONITOR_SUSPEND(timeout) +# define SYSTEM_MONITOR_RESUME() +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#ifdef __cplusplus + +#if SYSTEM_MONITOR_ENABLED == 1 +struct SystemMonitorTimeoutHelper { + SystemMonitorTimeoutHelper(system_tick_t timeout, bool stall = false) { + saved_ = SYSTEM_MONITOR_GET_TIMEOUT_CURRENT(); + stall_ = stall; + if (timeout > 0) { + SYSTEM_MONITOR_SET_TIMEOUT_CURRENT(timeout); + } + SYSTEM_MONITOR_KICK_CURRENT(); + if (stall_) { + SYSTEM_MONITOR_SUSPEND(timeout); + } + } + + ~SystemMonitorTimeoutHelper() { + SYSTEM_MONITOR_KICK_CURRENT(); + if (saved_ != 0) { + SYSTEM_MONITOR_SET_TIMEOUT_CURRENT(saved_); + } + if (stall_) { + SYSTEM_MONITOR_RESUME(); + } + } + +private: + system_tick_t saved_ = 0; + bool stall_ = false; +}; + +# define SYSTEM_MONITOR_MODIFY_TIMEOUT(timeout) SystemMonitorTimeoutHelper helper ## __COUNTER__ (timeout) +# define SYSTEM_MONITOR_EXPECT_STALL(timeout) SystemMonitorTimeoutHelper helper ## __COUNTER__ (timeout, true) + +#else + +# define SYSTEM_MONITOR_MODIFY_TIMEOUT(timeout) +# define SYSTEM_MONITOR_EXPECT_STALL(timeout) + +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ + +#endif /* __cplusplus */ + +#endif /* SERVICES_MONITOR_SERVICE_H */ diff --git a/services/inc/services2_dynalib.h b/services/inc/services2_dynalib.h new file mode 100644 index 0000000000..e31f189b32 --- /dev/null +++ b/services/inc/services2_dynalib.h @@ -0,0 +1,12 @@ +#ifndef SERVICES2_DYNALIB_H +#define SERVICES2_DYNALIB_H + +#include "dynalib.h" + +DYNALIB_BEGIN(services2) + +DYNALIB_FN(0, services2, system_monitor_set_callbacks_, int(system_monitor_callbacks_t*, void*)) + +DYNALIB_END(services2) + +#endif /* SERVICES2_DYNALIB_H */ diff --git a/services/inc/services_dynalib.h b/services/inc/services_dynalib.h index fc7194b0f2..dcb308c85c 100644 --- a/services/inc/services_dynalib.h +++ b/services/inc/services_dynalib.h @@ -74,8 +74,13 @@ DYNALIB_FN(39, services, diag_command, int(int, void*, void*)) // Export only on Photon and P1 #if PLATFORM_ID == 6 || PLATFORM_ID == 8 DYNALIB_FN(40, services, _printf_float, int(struct _reent*, struct _prt_data_t*, FILE*, int(*pfunc)(struct _reent* , FILE*, const char*, size_t), va_list*)) +#define BASE_IDX 41 +#else +#define BASE_IDX 40 #endif +DYNALIB_FN(BASE_IDX + 0, services, system_monitor_set_callbacks, int(system_monitor_callbacks_t*, void*)) + DYNALIB_END(services) #endif /* SERVICES_DYNALIB_H */ diff --git a/services/makefile b/services/makefile index 052e1f3069..1bc847691c 100644 --- a/services/makefile +++ b/services/makefile @@ -5,6 +5,6 @@ SERVICES_MODULE_PATH=. # Target this makefile is building. TARGET_TYPE = a BUILD_PATH_EXT=$(SERVICES_BUILD_PATH_EXT) -DEPENDENCIES = hal wiring dynalib +DEPENDENCIES = hal wiring dynalib platform include ../build/arm-tlm.mk diff --git a/services/src/led_service.cpp b/services/src/led_service.cpp index 4f8ddbe456..78d22c63ff 100644 --- a/services/src/led_service.cpp +++ b/services/src/led_service.cpp @@ -24,7 +24,7 @@ // TODO: Move synchronization macros to some header file #if PLATFORM_ID != 3 -#define INTERRUPTS_HAL_EXCLUDE_PLATFORM_HEADERS +// #define INTERRUPTS_HAL_EXCLUDE_PLATFORM_HEADERS #include "spark_wiring_interrupts.h" #define LED_SERVICE_DECLARE_LOCK(name) diff --git a/services/src/monitor_service.cpp b/services/src/monitor_service.cpp new file mode 100644 index 0000000000..03deec2f46 --- /dev/null +++ b/services/src/monitor_service.cpp @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2017 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#define SYSTEM_MONITOR_SKIP_PLATFORM +#include "monitor_service.h" + +#if defined(SYSTEM_MONITOR_USE_CALLBACKS) + +static system_monitor_callbacks_t s_callbacks = {}; + +int system_monitor_set_callbacks(system_monitor_callbacks_t* cb, void* reserved) { + if (cb != nullptr) { + s_callbacks = *cb; + return 0; + } + + return 1; +} + +int system_monitor_kick_current_(void* reserved) { + if (s_callbacks.system_monitor_kick_current != nullptr) { + return s_callbacks.system_monitor_kick_current(reserved); + } + + return 1; +} + +system_tick_t system_monitor_get_timeout_current_(void* reserved) { + if (s_callbacks.system_monitor_get_timeout_current != nullptr) { + return s_callbacks.system_monitor_get_timeout_current(reserved); + } + + return 1; +} + +int system_monitor_set_timeout_current_(system_tick_t timeout, void* reserved) { + if (s_callbacks.system_monitor_set_timeout_current != nullptr) { + return s_callbacks.system_monitor_set_timeout_current(timeout, reserved); + } + + return 1; +} + +int system_monitor_suspend_(system_tick_t timeout, void* reserved) { + if (s_callbacks.system_monitor_suspend != nullptr) { + return s_callbacks.system_monitor_suspend(timeout, reserved); + } + + return 1; +} + +int system_monitor_resume_(void* reserved) { + if (s_callbacks.system_monitor_resume != nullptr) { + return s_callbacks.system_monitor_resume(reserved); + } + + return 1; +} + +#endif /* defined(SYSTEM_MONITOR_USE_CALLBACKS) */ + +#if defined(SYSTEM_MONITOR_USE_CALLBACKS2) + +static system_monitor_callbacks_t s_callbacks2 = {}; + +int system_monitor_set_callbacks_(system_monitor_callbacks_t* cb, void* reserved) { + if (cb != nullptr) { + s_callbacks2 = *cb; + return 0; + } + + return 1; +} + +int system_monitor_kick_current__(void* reserved) { + if (s_callbacks2.system_monitor_kick_current != nullptr) { + return s_callbacks2.system_monitor_kick_current(reserved); + } + + return 1; +} + +system_tick_t system_monitor_get_timeout_current__(void* reserved) { + if (s_callbacks2.system_monitor_get_timeout_current != nullptr) { + return s_callbacks2.system_monitor_get_timeout_current(reserved); + } + + return 1; +} + +int system_monitor_set_timeout_current__(system_tick_t timeout, void* reserved) { + if (s_callbacks2.system_monitor_set_timeout_current != nullptr) { + return s_callbacks2.system_monitor_set_timeout_current(timeout, reserved); + } + + return 1; +} + +int system_monitor_suspend__(system_tick_t timeout, void* reserved) { + if (s_callbacks2.system_monitor_suspend != nullptr) { + return s_callbacks2.system_monitor_suspend(timeout, reserved); + } + + return 1; +} + +int system_monitor_resume__(void* reserved) { + if (s_callbacks2.system_monitor_resume != nullptr) { + return s_callbacks2.system_monitor_resume(reserved); + } + + return 1; +} + +#endif /* defined(SYSTEM_MONITOR_USE_CALLBACKS2) */ diff --git a/services/src/panic.c b/services/src/panic.c index aae4dfc4bf..a1421d88c1 100644 --- a/services/src/panic.c +++ b/services/src/panic.c @@ -5,7 +5,7 @@ * Author: david_s5 */ -#define INTERRUPTS_HAL_EXCLUDE_PLATFORM_HEADERS +// #define INTERRUPTS_HAL_EXCLUDE_PLATFORM_HEADERS #include #include "spark_macros.h" #include "panic.h" @@ -16,7 +16,7 @@ #include "interrupts_hal.h" #include "hal_platform.h" #include "core_hal.h" - +#include "monitor_service.h" #define LOOPSPERMSEC 5483 @@ -39,6 +39,10 @@ static const flash_codes_t flash_codes[] = { void panic_(ePanicCode code, void* extraInfo, void (*HAL_Delay_Microseconds)(uint32_t)) { + // FIXME: + // Suspend indefinitely + SYSTEM_MONITOR_SUSPEND(0); + HAL_disable_irq(); // Flush any serial message to help the poor bugger debug this; flash_codes_t pcd = flash_codes[code]; diff --git a/services/src/services_dynalib.c b/services/src/services_dynalib.c index 312fb22c7c..814f9c8fe9 100644 --- a/services/src/services_dynalib.c +++ b/services/src/services_dynalib.c @@ -30,4 +30,6 @@ #include "led_service.h" #include "diagnostics.h" #include "printf_float.h" +#define SYSTEM_MONITOR_SKIP_PLATFORM +#include "monitor_service.h" #include "services_dynalib.h" diff --git a/system/inc/active_object.h b/system/inc/active_object.h index 99a2812a2c..191dd6d36f 100644 --- a/system/inc/active_object.h +++ b/system/inc/active_object.h @@ -30,6 +30,7 @@ #include "channel.h" #include "concurrent_hal.h" +#include "monitor_service.h" /** * Configuratino data for an active object. @@ -142,7 +143,27 @@ template class AbstractPromise : public AbstractTask. + */ + +#ifndef SYSTEM_MONITOR_H +#define SYSTEM_MONITOR_H + +#include +#include "concurrent_hal.h" +#include "system_tick_hal.h" +#include "monitor_service.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct { + uint16_t size; + uint16_t version; + + union { + uint32_t flags; + uint32_t enabled : 1; + uint32_t iwdg : 1; + }; +} system_monitor_configuration_t; + +// Enables monitoring for the specified thread with a specified period +int system_monitor_enable(os_thread_t thread, system_tick_t timeout, void* reserved); + +// Disables monitoring for the specified thread +int system_monitor_disable(os_thread_t thread, void* reserved); + +// Reports that the specified thread is alive +int system_monitor_kick(os_thread_t thread, void* reserved); + +// Reports that the current thread is alive +int system_monitor_kick_current(void* reserved); + +// Gets the timeout for the specified thread. Returns 0 if monitoring is not +// enabled for the specified thread. +system_tick_t system_monitor_get_thread_timeout(os_thread_t thread, void* reserved); + +// Changes the timeout for the specified thread. Returns an error if monitoring is not +// enabled for the specified thread. +int system_monitor_set_thread_timeout(os_thread_t thread, system_tick_t timeout, void* reserved); + +// Gets the timeout for the current thread. Returns 0 if monitoring is not +// enabled for the current thread. +system_tick_t system_monitor_get_timeout_current(void* reserved); + +// Changes the timeout for the current thread. Returns an error if monitoring is not +// enabled for the current thread. +int system_monitor_set_timeout_current(system_tick_t timeout, void* reserved); + +// Returns maximum amount of time the device may sleep, or 0 if indefinitely +system_tick_t system_monitor_get_max_sleep_time(void* reserved, void* reserved1); + +// Updates monitoring settings +int system_monitor_configure(system_monitor_configuration_t* conf, void* reserved); + +// Suspend hardware watchdogs for a period of time +int system_monitor_suspend(system_tick_t timeout, void* reserved); + +// Resume normal operation +int system_monitor_resume(void* reserved); + +// Notify system monitor that the device is going into a sleep mode +int system_monitor_sleep(bool state, system_tick_t timeout_ms, void* reserved); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SYSTEM_MONITOR_H */ diff --git a/system/src/build.mk b/system/src/build.mk index 3b77729fa9..13dd3bf97b 100644 --- a/system/src/build.mk +++ b/system/src/build.mk @@ -17,12 +17,12 @@ ASRC += CPPFLAGS += -std=gnu++11 -ifeq ($(PLATFORM_ID),6) -CFLAGS += -DLOG_COMPILE_TIME_LEVEL=LOG_LEVEL_NONE -endif +# ifeq ($(PLATFORM_ID),6) +# CFLAGS += -DLOG_COMPILE_TIME_LEVEL=LOG_LEVEL_NONE +# endif -ifeq ($(PLATFORM_ID),8) -CFLAGS += -DLOG_COMPILE_TIME_LEVEL=LOG_LEVEL_NONE -endif +# ifeq ($(PLATFORM_ID),8) +# CFLAGS += -DLOG_COMPILE_TIME_LEVEL=LOG_LEVEL_NONE +# endif LOG_MODULE_CATEGORY = system diff --git a/system/src/main.cpp b/system/src/main.cpp index 9b1997e2fa..8baa869e83 100644 --- a/system/src/main.cpp +++ b/system/src/main.cpp @@ -55,6 +55,7 @@ #include "spark_wiring_system.h" #include "system_power.h" #include "spark_wiring_wifi.h" +#include "system_monitor_internal.h" #if PLATFORM_ID == 3 // Application loop uses std::this_thread::sleep_for() to workaround 100% CPU usage on the GCC platform @@ -589,6 +590,11 @@ void app_setup_and_loop(void) system_part2_post_init(); HAL_Core_Init(); main_thread_current(NULL); + +#if SYSTEM_MONITOR_ENABLED + SystemMonitor::instance()->init(); +#endif /* SYSTEM_MONITOR_ENABLED */ + // We have running firmware, otherwise we wouldn't have gotten here DECLARE_SYS_HEALTH(ENTERED_Main); @@ -632,6 +638,9 @@ void app_setup_and_loop(void) if (threaded) { SystemThread.start(); + SystemThread.invoke_async(FFL([](){ + system_monitor_enable(os_thread_current(), 0, nullptr); + })); ApplicationThread.start(); } else diff --git a/system/src/system_cloud.cpp b/system/src/system_cloud.cpp index 1b91ac01eb..abc7ab4ecc 100644 --- a/system/src/system_cloud.cpp +++ b/system/src/system_cloud.cpp @@ -37,6 +37,7 @@ #include "events.h" #include "deviceid_hal.h" #include "system_mode.h" +#include "monitor_service.h" extern void (*random_seed_from_cloud_handler)(unsigned int); @@ -186,6 +187,7 @@ void spark_process(void) if (system_thread_get_state(NULL) && APPLICATION_THREAD_CURRENT()) { ApplicationThread.process(); + SYSTEM_MONITOR_KICK_CURRENT(); return; } #endif diff --git a/system/src/system_monitor.cpp b/system/src/system_monitor.cpp new file mode 100644 index 0000000000..a870fd28de --- /dev/null +++ b/system/src/system_monitor.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2017 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "system_monitor.h" +#include "system_monitor_internal.h" + +using namespace particle; + +#if PLATFORM_THREADING + +int system_monitor_enable(os_thread_t thread, system_tick_t timeout, void* reserved) { + return SystemMonitor::instance()->enable(thread, timeout); +} + +int system_monitor_disable(os_thread_t thread, void* reserved) { + return SystemMonitor::instance()->disable(thread); +} + +int system_monitor_kick(os_thread_t thread, void* reserved) { + return SystemMonitor::instance()->kick(thread); +} + +int system_monitor_kick_current(void* reserved) { + return system_monitor_kick(os_thread_current(), reserved); +} + +system_tick_t system_monitor_get_thread_timeout(os_thread_t thread, void* reserved) { + return SystemMonitor::instance()->getThreadTimeout(thread); +} + +int system_monitor_set_thread_timeout(os_thread_t thread, system_tick_t timeout, void* reserved) { + return SystemMonitor::instance()->setThreadTimeout(thread, timeout); +} + +system_tick_t system_monitor_get_max_sleep_time(void* reserved, void* reserved1) { + return SystemMonitor::instance()->getMaximumSleepTime(); +} + +int system_monitor_configure(system_monitor_configuration_t* conf, void* reserved) { + return SystemMonitor::instance()->configure(conf); +} + +system_tick_t system_monitor_get_timeout_current(void* reserved) { + return system_monitor_get_thread_timeout(os_thread_current(), reserved); +} + +int system_monitor_set_timeout_current(system_tick_t timeout, void* reserved) { + return system_monitor_set_thread_timeout(os_thread_current(), timeout, reserved); +} + +int system_monitor_suspend(system_tick_t timeout, void* reserved) { + return SystemMonitor::instance()->suspend(timeout); +} + +int system_monitor_resume(void* reserved) { + return SystemMonitor::instance()->resume(); +} + +int system_monitor_sleep(bool state, system_tick_t timeout_ms, void* reserved) { + // TODO + (void)timeout_ms; + return SystemMonitor::instance()->sleep(state); +} + +#endif /* PLATFORM_THREADING */ diff --git a/system/src/system_monitor_internal.cpp b/system/src/system_monitor_internal.cpp new file mode 100644 index 0000000000..de8f3110b2 --- /dev/null +++ b/system/src/system_monitor_internal.cpp @@ -0,0 +1,534 @@ +/* + * Copyright (c) 2017 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#include "system_monitor_internal.h" +#include "spark_wiring_interrupts.h" +#include "system_error.h" +#include "timer_hal.h" +#include "core_hal.h" +#include "debug.h" +#include + +#if PLATFORM_THREADING + +namespace { +// 100ms +const system_tick_t WATCHDOG_DEFAULT_TIMEOUT_MS = 100; +// 60ms +const system_tick_t SYSTEM_MONITOR_DEFAULT_TICK_RATE_MS = 60; +// 1 second +const system_tick_t WATCHDOG_INDEPENDENT_TIMEOUT_MS = 1000; + +const system_tick_t SYSTEM_MONITOR_DEFAULT_THREAD_TIMEOUT_MS = 1000; + +} /* anonymous namespace */ + +namespace particle { + +SystemMonitor::Event::Event(SystemMonitor::EventType t, void* d) + : type(t), + data(d) { + if (!HAL_IsISR()) { + os_semaphore_create(&sem, 1, 0); + } +} + +SystemMonitor::Event::~Event() { + if (sem != nullptr) { + os_semaphore_destroy(sem); + } +} + +void SystemMonitor::Event::wait() { + os_semaphore_take(sem, CONCURRENT_WAIT_FOREVER, false); +} + +void SystemMonitor::Event::notify() { + if (sem != nullptr) { + os_semaphore_give(sem, true); + } +} + +void SystemMonitor::Event::clear() { + sem = nullptr; +} + +SystemMonitor* SystemMonitor::instance() { + static SystemMonitor mon; + return &mon; +} + +SystemMonitor::SystemMonitor() { + system_monitor_callbacks_t cb = {}; + cb.size = sizeof(cb); + cb.system_monitor_kick_current = &system_monitor_kick_current; + cb.system_monitor_get_timeout_current = &system_monitor_get_timeout_current; + cb.system_monitor_set_timeout_current = &system_monitor_set_timeout_current; + cb.system_monitor_suspend = &system_monitor_suspend; + cb.system_monitor_resume = &system_monitor_resume; +// #if defined(MODULE_HAS_SYSTEM_PART1) && MODULE_HAS_SYSTEM_PART1 == 1 + system_monitor_set_callbacks(&cb, nullptr); +// #endif /* defined(MODULE_HAS_SYSTEM_PART1) && MODULE_HAS_SYSTEM_PART1 == 1 */ +#if defined(MODULE_HAS_SYSTEM_PART3) && MODULE_HAS_SYSTEM_PART3 == 1 + system_monitor_set_callbacks_(&cb, nullptr); +#endif /* defined(MODULE_HAS_SYSTEM_PART3) && MODULE_HAS_SYSTEM_PART3 == 1 */ + tickRate_ = SYSTEM_MONITOR_DEFAULT_TICK_RATE_MS; +} + +int SystemMonitor::init() { + if (!enabled_) { + return -1; + } + + os_queue_create(&queue_, sizeof(Event), 1, nullptr); + SPARK_ASSERT(queue_ != nullptr); + + + os_thread_create(&thread_, "monitor", OS_THREAD_PRIORITY_CRITICAL, [](void* arg) { + SystemMonitor* self = (SystemMonitor*)arg; + + SPARK_ASSERT(self->watchdogInit()); + + while(true) { + Event event; + os_queue_take(self->queue_, &event, self->tickRate_, nullptr); + + if (event.type != MONITOR_EVENT_TYPE_NONE) { + self->handleEvent(event); + } + + bool kick = true; + for(const auto& entry: self->entries_) { + if (entry.taken.load() == true && entry.thread.load() != OS_THREAD_INVALID_HANDLE) { + system_tick_t timeout = entry.timeout.load(); + system_tick_t last = entry.last.load(); + system_tick_t ms = HAL_Timer_Get_Milli_Seconds(); + if (ms - last > timeout) { + kick = false; + break; + } + } + } + + if (kick) { + self->watchdogKick(); + } else { + if (self->manualReset_) { + HAL_Core_System_Reset(); + } + } + } + + }, + this, 512); + + SPARK_ASSERT(thread_ != nullptr); + + return 0; +} + +void SystemMonitor::handleEvent(Event& event) { + int32_t state = HAL_disable_irq(); + switch (event.type) { + case MONITOR_EVENT_TYPE_SLEEP: { + SleepEventData data = static_cast(event.data); + sleepImpl(*data); + break; + } + + case MONITOR_EVENT_TYPE_SUSPEND: { + SuspendEventData data = static_cast(event.data); + suspendImpl(true, *data); + break; + } + + case MONITOR_EVENT_TYPE_RESUME: { + suspendImpl(false); + } + } + HAL_enable_irq(state); + + event.notify(); + event.clear(); +} + +void SystemMonitor::notifyCallback(void* arg) { + SystemMonitor* self = (SystemMonitor*)arg; + // FIXME: correct id + self->notify(0); +} + +void SystemMonitor::notify(int idx) { + // TODO: perform a stacktrace dump here +} + +void SystemMonitor::postEvent(EventType type, void* data) { + if (!enabled_) { + return; + } + Event ev(type, data); + if (!HAL_IsISR()) { + os_queue_put(queue_, &ev, CONCURRENT_WAIT_FOREVER, nullptr); + ev.wait(); + } else { + handleEvent(ev); + } +} + +int SystemMonitor::sleep(bool state) { + postEvent(MONITOR_EVENT_TYPE_SLEEP, &state); + return 0; +} + +int SystemMonitor::suspend(system_tick_t timeout) { + postEvent(MONITOR_EVENT_TYPE_SUSPEND, &timeout); + return 0; +} + +int SystemMonitor::resume() { + postEvent(MONITOR_EVENT_TYPE_RESUME, nullptr); + return 0; +} + +void SystemMonitor::suspendImpl(bool state, system_tick_t timeout) { + const system_tick_t timeout_us = timeout * 1000; + + if (state) { + // Suspend + ++suspendRefCount_; + + watchdogKick(); + +#ifdef HAL_WATCHDOG_DEFAULT + SPARK_ASSERT(watchdogSuspend(HAL_WATCHDOG_DEFAULT, timeout_us)); +#endif /* HAL_WATCHDOG_DEFAULT */ + +#ifdef HAL_WATCHDOG_INDEPENDENT + SPARK_ASSERT(watchdogSuspend(HAL_WATCHDOG_INDEPENDENT, timeout_us)); +#endif /* HAL_WATCHDOG_INDEPENDENT */ + + watchdogKick(); + } else { + // Resume + if (--suspendRefCount_ == 0) { + // Restore default settings + watchdogKick(); + watchdogInit(); + watchdogKick(); + } + } +} + +void SystemMonitor::sleepImpl(bool state, system_tick_t timeout) { +#ifdef HAL_WATCHDOG_INDEPENDENT + hal_watchdog_config_t conf = {}; + conf.size = sizeof(conf); + if (state) { + // Going into sleep + // Reconfigure independent watchdog with the maximum period + watchdogKick(); + watchdogConfigure(HAL_WATCHDOG_INDEPENDENT, std::numeric_limits::max(), false, true); + watchdogKick(); + } else { + // Waking up + // Reconfigure watchdogs with the normal period + watchdogKick(); + watchdogInit(); + watchdogKick(); + } +#else + (void)state; +#endif /* HAL_WATCHDOG_INDEPENDENT */ +} + +bool SystemMonitor::watchdogConfigure(int idx, system_tick_t period_us, bool ifGreater, bool closest) { + { + bool ok = false; + auto status = watchdogStatus(idx, ok); + + if (ok && status.running && !(status.capabilities & HAL_WATCHDOG_CAPABILITY_RECONFIGURABLE)) { + return false; + } + + if (status.running && ifGreater) { + if (period_us < status.period_us) { + return false; + } + } + } + + bool ok = false; + + auto info = watchdogInfo(idx, ok); + if (!ok) { + return false; + } + + hal_watchdog_config_t conf = {}; + conf.size = sizeof(conf); + + conf.capabilities = info.capabilities; + if (period_us <= info.max_period_us || closest) { + conf.period_us = std::max(std::min(period_us, info.max_period_us), info.min_period_us); + } else { + return false; + } + + if (info.capabilities & HAL_WATCHDOG_CAPABILITY_WINDOWED) { + // Window is disabled + conf.window_us = 0; + } + + if (info.capabilities & HAL_WATCHDOG_CAPABILITY_NOTIFY) { + conf.notify_arg = this; + conf.notify = SystemMonitor::notifyCallback; + } + + hal_watchdog_configure(idx, &conf, nullptr); + + ok = false; + auto status = watchdogStatus(idx, ok); + + SPARK_ASSERT(ok == true && status.period_us != 0); + + if (manualReset_ == true) { + if (info.capabilities & HAL_WATCHDOG_CAPABILITY_CPU_RESET) { + manualReset_ = false; + } + } + + const system_tick_t tickRate = (status.period_us / 1000) / 2; + + if (tickRate_ > tickRate) { + tickRate_ = tickRate; + } + + return true; +} + +bool SystemMonitor::watchdogSuspend(int idx, system_tick_t timeout_us) { + if (timeout_us == 0) { + // Indefinite + if (!watchdogStop(idx)) { + // If watchdog cannot be stopped, reconfigure it with the longest timeout + return watchdogConfigure(idx, std::numeric_limits::max(), true, true); + } + } else { + if (!watchdogConfigure(idx, timeout_us, true, false)) { + // If watchdog cannot be suspended for such a period of time - attempt to stop it + return watchdogStop(idx); + } + } + + return true; +} + +bool SystemMonitor::watchdogInit() { +#ifdef HAL_WATCHDOG_DEFAULT + watchdogConfigure(HAL_WATCHDOG_DEFAULT, WATCHDOG_DEFAULT_TIMEOUT_MS * 1000); +#endif /* HAL_WATCHDOG_DEFAULT */ + +#ifdef HAL_WATCHDOG_INDEPENDENT + if (independentEnabled_) { + watchdogConfigure(HAL_WATCHDOG_INDEPENDENT, WATCHDOG_INDEPENDENT_TIMEOUT_MS * 1000); + } +#endif /* HAL_WATCHDOG_DEFAULT */ + + initialized_ = true; + +#ifdef HAL_WATCHDOG_DEFAULT + hal_watchdog_start(HAL_WATCHDOG_DEFAULT, nullptr, nullptr); +#endif /* HAL_WATCHDOG_DEFAULT */ + +#ifdef HAL_WATCHDOG_INDEPENDENT + if (independentEnabled_) { + hal_watchdog_start(HAL_WATCHDOG_INDEPENDENT, nullptr, nullptr); + } +#endif /* HAL_WATCHDOG_INDEPENDENT */ + + return true; +} + +void SystemMonitor::watchdogKick() { +#ifdef HAL_WATCHDOG_DEFAULT + hal_watchdog_kick(HAL_WATCHDOG_DEFAULT, nullptr); +#endif /* HAL_WATCHDOG_DEFAULT */ +#ifdef HAL_WATCHDOG_INDEPENDENT + if (independentEnabled_) { + hal_watchdog_kick(HAL_WATCHDOG_INDEPENDENT, nullptr); + } +#endif /* HAL_WATCHDOG_INDEPENDENT */ +} + +bool SystemMonitor::watchdogStart(int idx) { + return hal_watchdog_start(idx, nullptr, nullptr) == 0; +} + +bool SystemMonitor::watchdogStop(int idx) { + return hal_watchdog_stop(idx, nullptr) == 0; +} + +hal_watchdog_info_t SystemMonitor::watchdogInfo(int idx, bool& ok) const { + hal_watchdog_info_t info = {}; + info.size = sizeof(info); + + ok = hal_watchdog_query(idx, &info, nullptr) == 0; + + return info; +} + +hal_watchdog_status_t SystemMonitor::watchdogStatus(int idx, bool& ok) const { + hal_watchdog_status_t status = {}; + status.size = sizeof(status); + + ok = hal_watchdog_get_status(idx, &status, nullptr) == 0; + + return status; +} + +int SystemMonitor::enable(os_thread_t thread, system_tick_t timeout) { + bool set = false; + + if (timeout == 0) { + timeout = SYSTEM_MONITOR_DEFAULT_THREAD_TIMEOUT_MS; + } + + for(auto& entry: entries_) { + bool taken = false; + if (entry.taken.compare_exchange_strong(taken, true)) { + set = true; + + entry.timeout.store(timeout); + entry.last.store(HAL_Timer_Get_Milli_Seconds()); + + entry.thread.store(thread); + break; + } + } + return set ? SYSTEM_ERROR_NONE : SYSTEM_ERROR_LIMIT_EXCEEDED; +} + +int SystemMonitor::disable(os_thread_t thread) { + if (thread == OS_THREAD_INVALID_HANDLE) { + return SYSTEM_ERROR_INVALID_ARGUMENT; + } + + bool set = false; + for(auto& entry: entries_) { + os_thread_t t = thread; + if (entry.taken.load() == true) { + if (entry.thread.compare_exchange_strong(t, OS_THREAD_INVALID_HANDLE)) { + set = true; + + entry.last.store(0); + entry.timeout.store(0); + + entry.taken.store(false); + break; + } + } + } + + return set ? SYSTEM_ERROR_NONE : SYSTEM_ERROR_NOT_FOUND; +} + +int SystemMonitor::kick(os_thread_t thread) { + if (thread == OS_THREAD_INVALID_HANDLE) { + if (HAL_IsISR()) { + // Directly kick hardware watchdogs + watchdogKick(); + return SYSTEM_ERROR_NONE; + } + return SYSTEM_ERROR_INVALID_ARGUMENT; + } + + bool set = false; + + for(auto& entry : entries_) { + if (entry.thread.load() == thread) { + set = true; + entry.last.store(HAL_Timer_Get_Milli_Seconds()); + break; + } + } + + return set ? SYSTEM_ERROR_NONE : SYSTEM_ERROR_NOT_FOUND; +} + +system_tick_t SystemMonitor::getThreadTimeout(os_thread_t thread) const { + system_tick_t timeout = 0; + + if (thread == OS_THREAD_INVALID_HANDLE) { + return timeout; + } + + for (const auto& entry: entries_) { + if (entry.thread.load() == thread) { + timeout = entry.timeout.load(); + break; + } + } + + return timeout; +} + +int SystemMonitor::setThreadTimeout(os_thread_t thread, system_tick_t timeout) { + bool set = false; + + if (thread == OS_THREAD_INVALID_HANDLE) { + return SYSTEM_ERROR_INVALID_ARGUMENT; + } + + for(auto& entry: entries_) { + if (entry.thread.load() == thread) { + entry.timeout.store(timeout); + set = true; + break; + } + } + return set ? SYSTEM_ERROR_NONE : SYSTEM_ERROR_NOT_FOUND; +} + +system_tick_t SystemMonitor::getMaximumSleepTime() const { +#ifdef HAL_WATCHDOG_INDEPENDENT + if (enabled_ && independentEnabled_ == true) { + bool ok = false; + auto info = watchdogInfo(HAL_WATCHDOG_INDEPENDENT, ok); + if (ok) { + return info.max_period_us / 1000; + } + } +#endif /* HAL_WATCHDOG_INDEPENDENT */ + + return 0; +} + +int SystemMonitor::configure(system_monitor_configuration_t* conf) { +#ifdef HAL_WATCHDOG_INDEPENDENT + if (conf && conf->iwdg == 1 && !initialized_) { + independentEnabled_ = true; + } + if (conf && conf->enabled == 1 && !initialized_) { + enabled_ = true; + } +#endif /* HAL_WATCHDOG_INDEPENDENT */ + return SYSTEM_ERROR_NONE; +} + +} /* namespace particle */ + +#endif /* PLATFORM_THREADING */ diff --git a/system/src/system_monitor_internal.h b/system/src/system_monitor_internal.h new file mode 100644 index 0000000000..c4b0705a96 --- /dev/null +++ b/system/src/system_monitor_internal.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2017 Particle Industries, Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ + +#ifndef SYSTEM_MONITOR_INTERNAL_H +#define SYSTEM_MONITOR_INTERNAL_H + +#include "system_monitor.h" +#include "watchdog_hal.h" +#include + +namespace particle { + +const size_t SYSTEM_MONITOR_MAX_THREADS = 5; + +class SystemMonitor { +public: + static SystemMonitor* instance(); + + int init(); + + int enable(os_thread_t thread, system_tick_t timeout); + int disable(os_thread_t thread); + int kick(os_thread_t thread); + system_tick_t getThreadTimeout(os_thread_t thread) const; + int setThreadTimeout(os_thread_t thread, system_tick_t timeout); + system_tick_t getMaximumSleepTime() const; + int configure(system_monitor_configuration_t* conf); + + int sleep(bool state = true); + int wakeup() { + return sleep(false); + } + + int suspend(system_tick_t timeout = 0); + int resume(); + +protected: + SystemMonitor(); + + void notify(int idx); + static void notifyCallback(void* arg); + + struct ThreadEntry { + std::atomic_bool taken; + std::atomic thread; + std::atomic timeout; + std::atomic last; + }; + + enum EventType { + MONITOR_EVENT_TYPE_NONE = 0, + MONITOR_EVENT_TYPE_SLEEP, + MONITOR_EVENT_TYPE_SUSPEND, + MONITOR_EVENT_TYPE_RESUME + }; + + struct Event { + Event() = default; + Event(EventType type, void* data); + ~Event(); + + void notify(); + void wait(); + void clear(); + + EventType type = MONITOR_EVENT_TYPE_NONE; + os_semaphore_t sem = nullptr; + void* data = nullptr; + }; + + using SleepEventData = bool*; + using SuspendEventData = system_tick_t*; + using ResumeEventData = void*; + +private: + bool watchdogInit(); + bool watchdogConfigure(int idx, system_tick_t period_us, bool ifGreater = false, bool closest = true); + bool watchdogStart(int idx); + bool watchdogStop(int idx); + bool watchdogSuspend(int idx, system_tick_t timeout); + bool watchdogResume(int idx); + void watchdogKick(); + hal_watchdog_info_t watchdogInfo(int idx, bool& ok) const; + hal_watchdog_status_t watchdogStatus(int idx, bool& ok) const; + + void postEvent(EventType type, void* data); + void handleEvent(Event& event); + + void sleepImpl(bool state, system_tick_t timeout = 0); + void suspendImpl(bool state, system_tick_t timeout = 0); + +private: + os_thread_t thread_ = {}; + os_queue_t queue_ = {}; + ThreadEntry entries_[SYSTEM_MONITOR_MAX_THREADS] = {}; + system_tick_t tickRate_ = 0; + bool enabled_ = false; + bool initialized_ = false; + bool independentEnabled_ = false; + int suspendRefCount_ = 0; + bool manualReset_ = true; +}; + +} /* namespace particle */ + +#endif /* SYSTEM_MONITOR_INTERNAL_H */ diff --git a/system/src/system_network_internal.h b/system/src/system_network_internal.h index c81661b699..6612d09f6f 100644 --- a/system/src/system_network_internal.h +++ b/system/src/system_network_internal.h @@ -30,6 +30,7 @@ #include "system_threading.h" #include "system_mode.h" #include "system_power.h" +#include "monitor_service.h" using namespace particle; @@ -242,6 +243,7 @@ class ManagedNetworkInterface : public NetworkInterface /* Wait for SmartConfig/SerialConfig to finish */ while (WLAN_SMART_CONFIG_ACTIVE && !WLAN_SMART_CONFIG_FINISHED && !WLAN_SERIAL_CONFIG_DONE) { + SYSTEM_MONITOR_KICK_CURRENT(); if (WLAN_DELETE_PROFILES) { // Get base color used for the listening mode indication diff --git a/system/src/system_power_manager.cpp b/system/src/system_power_manager.cpp index 8e1044a599..31cfe2b8e1 100644 --- a/system/src/system_power_manager.cpp +++ b/system/src/system_power_manager.cpp @@ -27,7 +27,7 @@ PowerManager* PowerManager::instance() { } void PowerManager::init() { - os_thread_create(&thread_, "pwr", OS_THREAD_PRIORITY_CRITICAL, &PowerManager::loop, nullptr, + os_thread_create(&thread_, "pwr", OS_THREAD_PRIORITY_CRITICAL - 1, &PowerManager::loop, nullptr, #if defined(DEBUG_BUILD) 4 * 1024); #else diff --git a/system/src/system_sleep.cpp b/system/src/system_sleep.cpp index 4b920d2192..00e52555dd 100644 --- a/system/src/system_sleep.cpp +++ b/system/src/system_sleep.cpp @@ -33,6 +33,7 @@ #include "spark_wiring_system.h" #include "spark_wiring_platform.h" #include "system_power.h" +#include "system_monitor.h" #if PLATFORM_ID==PLATFORM_ELECTRON_PRODUCTION # include "parser.h" @@ -137,6 +138,9 @@ int system_sleep_impl(Spark_Sleep_TypeDef sleepMode, long seconds, uint32_t para network_off(0, 0, 0, NULL); } system_power_management_sleep(); +#if SYSTEM_MONITOR_ENABLED == 1 + system_monitor_sleep(true, seconds * 1000, nullptr); +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ HAL_Core_Enter_Standby_Mode(seconds, nullptr); break; @@ -146,6 +150,9 @@ int system_sleep_impl(Spark_Sleep_TypeDef sleepMode, long seconds, uint32_t para network_off(0, 0, 0, NULL); sleep_fuel_gauge(); system_power_management_sleep(); +#if SYSTEM_MONITOR_ENABLED == 1 + system_monitor_sleep(true, seconds * 1000, nullptr); +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ HAL_Core_Enter_Standby_Mode(seconds, nullptr); break; #endif @@ -178,7 +185,13 @@ int system_sleep_pin_impl(uint16_t wakeUpPin, uint16_t edgeTriggerMode, long sec led_set_update_enabled(0, nullptr); // Disable background LED updates LED_Off(LED_RGB); system_power_management_sleep(); +#if SYSTEM_MONITOR_ENABLED == 1 + system_monitor_sleep(true, seconds * 1000, nullptr); +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ HAL_Core_Enter_Stop_Mode(wakeUpPin, edgeTriggerMode, seconds); +#if SYSTEM_MONITOR_ENABLED == 1 + system_monitor_sleep(false, seconds * 1000, nullptr); +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ led_set_update_enabled(1, nullptr); // Enable background LED updates #if PLATFORM_ID==PLATFORM_ELECTRON_PRODUCTION diff --git a/system/src/system_task.cpp b/system/src/system_task.cpp index 76862aee58..23c44d93ea 100644 --- a/system/src/system_task.cpp +++ b/system/src/system_task.cpp @@ -423,7 +423,7 @@ extern void system_handle_button_clicks(bool isIsr); void Spark_Idle_Events(bool force_events/*=false*/) { - HAL_Notify_WDT(); + SYSTEM_MONITOR_KICK_CURRENT(); ON_EVENT_DELTA(); spark_loop_total_millis = 0; @@ -467,7 +467,7 @@ void system_delay_pump(unsigned long ms, bool force_no_background_loop=false) while (1) { - HAL_Notify_WDT(); + SYSTEM_MONITOR_KICK_CURRENT(); system_tick_t elapsed_millis = HAL_Timer_Get_Milli_Seconds() - start_millis; diff --git a/wiring/inc/spark_wiring_system.h b/wiring/inc/spark_wiring_system.h index 90884b605d..81b3e4bb79 100644 --- a/wiring/inc/spark_wiring_system.h +++ b/wiring/inc/spark_wiring_system.h @@ -36,6 +36,7 @@ #include "core_hal.h" #include "system_user.h" #include "system_version.h" +#include "spark_wiring_watchdog.h" #if defined(SPARK_PLATFORM) && PLATFORM_ID!=3 #define SYSTEM_HW_TICKS 1 @@ -215,6 +216,10 @@ class SystemClass { static bool enableFeature(const WiFiTesterFeature feature); +#if SYSTEM_MONITOR_ENABLED == 1 + static bool enableFeature(spark::WatchdogFeature feature); +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ + String version() { SystemVersionInfo info; diff --git a/wiring/inc/spark_wiring_watchdog.h b/wiring/inc/spark_wiring_watchdog.h index 194825aded..71708ba29e 100644 --- a/wiring/inc/spark_wiring_watchdog.h +++ b/wiring/inc/spark_wiring_watchdog.h @@ -22,10 +22,11 @@ #include "spark_wiring_thread.h" #include "delay_hal.h" #include "timer_hal.h" +#include "system_task.h" +#include "system_monitor.h" #if PLATFORM_THREADING - class ApplicationWatchdog { volatile system_tick_t timeout; @@ -90,7 +91,91 @@ class ApplicationWatchdog }; -inline void application_checkin() { ApplicationWatchdog::checkin(); } +#if SYSTEM_MONITOR_ENABLED == 1 + +namespace spark { + +const system_tick_t DEFAULT_WATCHDOG_TIMEOUT = 1000; + +enum WatchdogFeature { + WATCHDOG_FEATURE = 0x01, + WATCHDOG_FEATURE_IWDG = 0x02 +}; + +class WatchdogClass { +public: + // Enables monitoring for the calling thread with a specified period + static bool enable(system_tick_t timeout = DEFAULT_WATCHDOG_TIMEOUT) { + return system_monitor_enable(os_thread_current(), timeout, nullptr) == 0; + } + // Disable monitoring for the calling thread + static bool disable() { + return system_monitor_disable(os_thread_current(), nullptr) == 0; + } + + // Reports that the calling thread is alive + static bool kick() { + return system_monitor_kick(os_thread_current(), nullptr) == 0; + } + + // Gets the timeout for the calling thread. Returns 0 if monitoring is not + // enabled for the calling thread. + static system_tick_t getTimeout() { + return getTimeout(os_thread_current()); + } + // Gets the timeout for a specified thread. Returns 0 if monitoring is not + // enabled for the specified thread. + static system_tick_t getTimeout(os_thread_t thread) { + return system_monitor_get_thread_timeout(thread, nullptr); + } + // Changes the timeout for the calling thread. Returns false if monitoring is not + // enabled for the calling thread. + static bool setTimeout(system_tick_t timeout) { + return setTimeout(os_thread_current(), timeout); + } + // Changes the timeout for the specified thread. Returns false if monitoring is not + // enabled for the specified thread. + static bool setTimeout(os_thread_t thread, system_tick_t timeout) { + return system_monitor_set_thread_timeout(thread, timeout, nullptr) == 0; + } + + // Returns maximum amount of time the device may sleep, or 0 if indefinitely + static system_tick_t getMaximumSleepTime() { + return system_monitor_get_max_sleep_time(nullptr, nullptr); + } + + // There should be the same System.enableFeature() overload for convenience + // WatchdogFeature: + // - WATCHDOG_FEATURE_IWDG - enables IWDG (independent watchdog) + static bool enableFeature(WatchdogFeature feature) { + system_monitor_configuration_t config = {}; + config.size = sizeof(config); + // config.version = 0; + if (feature == WATCHDOG_FEATURE_IWDG) { + config.iwdg = 1; + } + if (feature == WATCHDOG_FEATURE) { + config.enabled = 1; + } + return system_monitor_configure(&config, nullptr) == 0; + } +}; + +} /* namespace spark */ + +extern spark::WatchdogClass Watchdog; + +#define WATCHDOG_MODIFY_TIMEOUT(timeout) SYSTEM_MONITOR_MODIFY_TIMEOUT(timeout) + +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ + +inline void application_checkin() { + SPARK_ASSERT(application_thread_current(nullptr)); +#if SYSTEM_MONITOR_ENABLED == 1 + spark::WatchdogClass::kick(); +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ + ApplicationWatchdog::checkin(); +} #else diff --git a/wiring/src/spark_wiring_system.cpp b/wiring/src/spark_wiring_system.cpp index cd8baea21d..829b226e10 100644 --- a/wiring/src/spark_wiring_system.cpp +++ b/wiring/src/spark_wiring_system.cpp @@ -71,3 +71,9 @@ bool SystemClass::enableFeature(const WiFiTesterFeature feature) { WiFiTester::init(); return true; } + +#if SYSTEM_MONITOR_ENABLED == 1 +bool SystemClass::enableFeature(spark::WatchdogFeature feature) { + return Watchdog.enableFeature(feature); +} +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ diff --git a/wiring_globals/makefile b/wiring_globals/makefile index 71ade82a3f..3952e43f26 100644 --- a/wiring_globals/makefile +++ b/wiring_globals/makefile @@ -7,6 +7,6 @@ TARGET_TYPE = a BUILD_PATH_EXT = $(BUILD_TARGET_PLATFORM) # the only true dependency is Wiring, the others are transitive dependencies -DEPENDENCIES = wiring hal services system platform communication +DEPENDENCIES = wiring hal services system platform communication dynalib include ../build/arm-tlm.mk diff --git a/wiring_globals/src/spark_wiring_watchdog.cpp b/wiring_globals/src/spark_wiring_watchdog.cpp index b19114df58..fb78bf8532 100644 --- a/wiring_globals/src/spark_wiring_watchdog.cpp +++ b/wiring_globals/src/spark_wiring_watchdog.cpp @@ -28,5 +28,8 @@ void ApplicationWatchdog::loop() } } +#if SYSTEM_MONITOR_ENABLED == 1 +spark::WatchdogClass Watchdog; +#endif /* SYSTEM_MONITOR_ENABLED == 1 */ #endif