task_wdt.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. // Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <stdint.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <stdbool.h>
  19. #include "sdkconfig.h"
  20. #include "freertos/FreeRTOSConfig.h"
  21. #include "freertos/FreeRTOS.h"
  22. #include "freertos/task.h"
  23. #include "freertos/queue.h"
  24. #include "freertos/semphr.h"
  25. #include <esp_types.h>
  26. #include "esp_err.h"
  27. #include "esp_intr.h"
  28. #include "esp_intr_alloc.h"
  29. #include "esp_attr.h"
  30. #include "esp_freertos_hooks.h"
  31. #include "soc/timer_group_struct.h"
  32. #include "soc/timer_group_reg.h"
  33. #include "esp_log.h"
  34. #include "driver/timer.h"
  35. #include "driver/periph_ctrl.h"
  36. #include "esp_task_wdt.h"
  37. //Assertion macro where, if 'cond' is false, will exit the critical section and return 'ret'
  38. #define ASSERT_EXIT_CRIT_RETURN(cond, ret) ({ \
  39. if(!(cond)){ \
  40. portEXIT_CRITICAL(&twdt_spinlock); \
  41. return ret; \
  42. } \
  43. })
  44. //Empty define used in ASSERT_EXIT_CRIT_RETURN macro when returning in void
  45. #define VOID_RETURN
  46. //Structure used for each subscribed task
  47. typedef struct twdt_task_t twdt_task_t;
  48. struct twdt_task_t {
  49. TaskHandle_t task_handle;
  50. bool has_reset;
  51. twdt_task_t *next;
  52. };
  53. //Structure used to hold run time configuration of the TWDT
  54. typedef struct twdt_config_t twdt_config_t;
  55. struct twdt_config_t {
  56. twdt_task_t *list; //Linked list of subscribed tasks
  57. uint32_t timeout; //Timeout period of TWDT
  58. bool panic; //Flag to trigger panic when TWDT times out
  59. intr_handle_t intr_handle;
  60. };
  61. static twdt_config_t *twdt_config = NULL;
  62. static portMUX_TYPE twdt_spinlock = portMUX_INITIALIZER_UNLOCKED;
  63. /*
  64. * Idle hook callback for Idle Tasks to reset the TWDT. This callback will only
  65. * be registered to the Idle Hook of a particular core when the corresponding
  66. * Idle Task subscribes to the TWDT.
  67. */
  68. static bool idle_hook_cb(void)
  69. {
  70. esp_task_wdt_reset();
  71. return true;
  72. }
  73. /*
  74. * Internal function that looks for the target task in the TWDT task list.
  75. * Returns the list item if found and returns null if not found. Also checks if
  76. * all the other tasks have reset. Should be called within critical.
  77. */
  78. static twdt_task_t *find_task_in_twdt_list(TaskHandle_t handle, bool *all_reset)
  79. {
  80. twdt_task_t *target = NULL;
  81. *all_reset = true;
  82. for(twdt_task_t *task = twdt_config->list; task != NULL; task = task->next){
  83. if(task->task_handle == handle){
  84. target = task; //Get pointer to target task list member
  85. }else{
  86. if(task->has_reset == false){ //If a task has yet to reset
  87. *all_reset = false;
  88. }
  89. }
  90. }
  91. return target;
  92. }
  93. /*
  94. * Resets the hardware timer and has_reset flags of each task on the list.
  95. * Called within critical
  96. */
  97. static void reset_hw_timer()
  98. {
  99. //All tasks have reset; time to reset the hardware timer.
  100. TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
  101. TIMERG0.wdt_feed=1;
  102. TIMERG0.wdt_wprotect=0;
  103. //Clear all has_reset flags in list
  104. for (twdt_task_t *task = twdt_config->list; task != NULL; task = task->next){
  105. task->has_reset=false;
  106. }
  107. }
  108. /*
  109. * ISR for when TWDT times out. Checks for which tasks have not reset. Also
  110. * triggers panic if configured to do so
  111. */
  112. static void task_wdt_isr(void *arg)
  113. {
  114. portENTER_CRITICAL(&twdt_spinlock);
  115. twdt_task_t *twdttask;
  116. const char *cpu;
  117. //Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset)
  118. TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
  119. TIMERG0.wdt_feed=1;
  120. TIMERG0.wdt_wprotect=0;
  121. //Acknowledge interrupt
  122. TIMERG0.int_clr_timers.wdt=1;
  123. //We are taking a spinlock while doing I/O (ets_printf) here. Normally, that is a pretty
  124. //bad thing, possibly (temporarily) hanging up the 2nd core and stopping FreeRTOS. In this case,
  125. //something bad already happened and reporting this is considered more important
  126. //than the badness caused by a spinlock here.
  127. //Return immediately if no tasks have been added to task list
  128. ASSERT_EXIT_CRIT_RETURN((twdt_config->list != NULL), VOID_RETURN);
  129. //Watchdog got triggered because at least one task did not reset in time.
  130. ets_printf("Task watchdog got triggered. The following tasks did not reset the watchdog in time:\n");
  131. for (twdttask=twdt_config->list; twdttask!=NULL; twdttask=twdttask->next) {
  132. if (!twdttask->has_reset) {
  133. cpu=xTaskGetAffinity(twdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1");
  134. if (xTaskGetAffinity(twdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1");
  135. ets_printf(" - %s (%s)\n", pcTaskGetTaskName(twdttask->task_handle), cpu);
  136. }
  137. }
  138. ets_printf(DRAM_STR("Tasks currently running:\n"));
  139. for (int x=0; x<portNUM_PROCESSORS; x++) {
  140. ets_printf("CPU %d: %s\n", x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
  141. }
  142. if (twdt_config->panic){ //Trigger Panic if configured to do so
  143. ets_printf("Aborting.\n");
  144. portEXIT_CRITICAL(&twdt_spinlock);
  145. abort();
  146. }
  147. portEXIT_CRITICAL(&twdt_spinlock);
  148. }
  149. /*
  150. * Initializes the TWDT by allocating memory for the config data
  151. * structure, obtaining the idle task handles/registering idle hooks, and
  152. * setting the hardware timer registers. If reconfiguring, it will just modify
  153. * wdt_config and reset the hardware timer.
  154. */
  155. esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic)
  156. {
  157. portENTER_CRITICAL(&twdt_spinlock);
  158. if(twdt_config == NULL){ //TWDT not initialized yet
  159. //Allocate memory for wdt_config
  160. twdt_config = calloc(1, sizeof(twdt_config_t));
  161. ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NO_MEM);
  162. twdt_config->list = NULL;
  163. twdt_config->timeout = timeout;
  164. twdt_config->panic = panic;
  165. //Register Interrupt and ISR
  166. ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &twdt_config->intr_handle))
  167. //Configure hardware timer
  168. periph_module_enable(PERIPH_TIMG0_MODULE);
  169. TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; //Disable write protection
  170. TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
  171. TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
  172. TIMERG0.wdt_config0.level_int_en=1;
  173. TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT; //1st stage timeout: interrupt
  174. TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
  175. TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
  176. TIMERG0.wdt_config2=twdt_config->timeout*2000; //Set timeout before interrupt
  177. TIMERG0.wdt_config3=twdt_config->timeout*4000; //Set timeout before reset
  178. TIMERG0.wdt_config0.en=1;
  179. TIMERG0.wdt_feed=1;
  180. TIMERG0.wdt_wprotect=0; //Enable write protection
  181. }else{ //twdt_config previously initialized
  182. //Reconfigure task wdt
  183. twdt_config->panic = panic;
  184. twdt_config->timeout = timeout;
  185. //Reconfigure hardware timer
  186. TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; //Disable write protection
  187. TIMERG0.wdt_config0.en=0; //Disable timer
  188. TIMERG0.wdt_config2=twdt_config->timeout*2000; //Set timeout before interrupt
  189. TIMERG0.wdt_config3=twdt_config->timeout*4000; //Set timeout before reset
  190. TIMERG0.wdt_config0.en=1; //Renable timer
  191. TIMERG0.wdt_feed=1; //Reset timer
  192. TIMERG0.wdt_wprotect=0; //Enable write protection
  193. }
  194. portEXIT_CRITICAL(&twdt_spinlock);
  195. return ESP_OK;
  196. }
  197. esp_err_t esp_task_wdt_deinit()
  198. {
  199. portENTER_CRITICAL(&twdt_spinlock);
  200. //TWDT must already be initialized
  201. ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NOT_FOUND);
  202. //Task list must be empty
  203. ASSERT_EXIT_CRIT_RETURN((twdt_config->list == NULL), ESP_ERR_INVALID_STATE);
  204. //Disable hardware timer
  205. TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; //Disable write protection
  206. TIMERG0.wdt_config0.en=0; //Disable timer
  207. TIMERG0.wdt_wprotect=0; //Enable write protection
  208. ESP_ERROR_CHECK(esp_intr_free(twdt_config->intr_handle)) //Unregister interrupt
  209. free(twdt_config); //Free twdt_config
  210. twdt_config = NULL;
  211. portEXIT_CRITICAL(&twdt_spinlock);
  212. return ESP_OK;
  213. }
  214. esp_err_t esp_task_wdt_add(TaskHandle_t handle)
  215. {
  216. portENTER_CRITICAL(&twdt_spinlock);
  217. //TWDT must already be initialized
  218. ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE);
  219. twdt_task_t *target_task;
  220. bool all_reset;
  221. if (handle == NULL){ //Get handle of current task if none is provided
  222. handle = xTaskGetCurrentTaskHandle();
  223. }
  224. //Check if tasks exists in task list, and if all other tasks have reset
  225. target_task = find_task_in_twdt_list(handle, &all_reset);
  226. //task cannot be already subscribed
  227. ASSERT_EXIT_CRIT_RETURN((target_task == NULL), ESP_ERR_INVALID_ARG);
  228. //Add target task to TWDT task list
  229. target_task = calloc(1,sizeof(twdt_task_t));
  230. ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_NO_MEM);
  231. target_task->task_handle = handle;
  232. target_task->has_reset = true;
  233. target_task->next = NULL;
  234. if (twdt_config->list == NULL) { //Adding to empty list
  235. twdt_config->list = target_task;
  236. } else { //Adding to tail of list
  237. twdt_task_t *task;
  238. for (task = twdt_config->list; task->next != NULL; task = task->next){
  239. ; //point task to current tail of TWDT task list
  240. }
  241. task->next = target_task;
  242. }
  243. //If idle task, register the idle hook callback to appropriate core
  244. for(int i = 0; i < portNUM_PROCESSORS; i++){
  245. if(handle == xTaskGetIdleTaskHandleForCPU(i)){
  246. ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, i))
  247. break;
  248. }
  249. }
  250. if(all_reset){ //Reset hardware timer if all other tasks in list have reset in
  251. reset_hw_timer();
  252. }
  253. portEXIT_CRITICAL(&twdt_spinlock); //Nested critical if Legacy
  254. return ESP_OK;
  255. }
  256. esp_err_t esp_task_wdt_reset()
  257. {
  258. portENTER_CRITICAL(&twdt_spinlock);
  259. //TWDT must already be initialized
  260. ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE);
  261. TaskHandle_t handle = xTaskGetCurrentTaskHandle();
  262. twdt_task_t *target_task;
  263. bool all_reset;
  264. //Check if task exists in task list, and if all other tasks have reset
  265. target_task = find_task_in_twdt_list(handle, &all_reset);
  266. //Return error if trying to reset task that is not on the task list
  267. ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_NOT_FOUND);
  268. target_task->has_reset = true; //Reset the task if it's on the task list
  269. if(all_reset){ //Reset if all other tasks in list have reset in
  270. reset_hw_timer();
  271. }
  272. portEXIT_CRITICAL(&twdt_spinlock);
  273. return ESP_OK;
  274. }
  275. esp_err_t esp_task_wdt_delete(TaskHandle_t handle)
  276. {
  277. if(handle == NULL){
  278. handle = xTaskGetCurrentTaskHandle();
  279. }
  280. portENTER_CRITICAL(&twdt_spinlock);
  281. //Return error if twdt has not been initialized
  282. ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NOT_FOUND);
  283. twdt_task_t *target_task;
  284. bool all_reset;
  285. target_task = find_task_in_twdt_list(handle, &all_reset);
  286. //Task doesn't exist on list. Return error
  287. ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_INVALID_ARG);
  288. if(target_task == twdt_config->list){ //target_task is head of list. Delete
  289. twdt_config->list = target_task->next;
  290. free(target_task);
  291. }else{ //target_task not head of list. Delete
  292. twdt_task_t *prev;
  293. for (prev = twdt_config->list; prev->next != target_task; prev = prev->next){
  294. ; //point prev to task preceding target_task
  295. }
  296. prev->next = target_task->next;
  297. free(target_task);
  298. }
  299. //If idle task, deregister idle hook callback form appropriate core
  300. for(int i = 0; i < portNUM_PROCESSORS; i++){
  301. if(handle == xTaskGetIdleTaskHandleForCPU(i)){
  302. esp_deregister_freertos_idle_hook_for_cpu(idle_hook_cb, i);
  303. break;
  304. }
  305. }
  306. if(all_reset){ //Reset hardware timer if all remaining tasks have reset
  307. reset_hw_timer();
  308. }
  309. portEXIT_CRITICAL(&twdt_spinlock);
  310. return ESP_OK;
  311. }
  312. esp_err_t esp_task_wdt_status(TaskHandle_t handle)
  313. {
  314. if(handle == NULL){
  315. handle = xTaskGetCurrentTaskHandle();
  316. }
  317. portENTER_CRITICAL(&twdt_spinlock);
  318. //Return if TWDT is not initialized
  319. ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE);
  320. twdt_task_t *task;
  321. for(task = twdt_config->list; task!=NULL; task=task->next){
  322. //Return ESP_OK if task is found
  323. ASSERT_EXIT_CRIT_RETURN((task->task_handle != handle), ESP_OK);
  324. }
  325. //Task could not be found
  326. portEXIT_CRITICAL(&twdt_spinlock);
  327. return ESP_ERR_NOT_FOUND;
  328. }
  329. void esp_task_wdt_feed()
  330. {
  331. portENTER_CRITICAL(&twdt_spinlock);
  332. //Return immediately if TWDT has not been initialized
  333. ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), VOID_RETURN);
  334. //Check if task is on list
  335. TaskHandle_t handle = xTaskGetCurrentTaskHandle();
  336. bool all_reset;
  337. twdt_task_t *target_task = find_task_in_twdt_list(handle, &all_reset);
  338. //reset the task if it's on the list, then return
  339. if(target_task != NULL){
  340. target_task->has_reset = true;
  341. if(all_reset){
  342. reset_hw_timer(); //Reset hardware timer if all other tasks have reset
  343. }
  344. portEXIT_CRITICAL(&twdt_spinlock);
  345. return;
  346. }
  347. //Add task if it's has not on list
  348. target_task = calloc(1, sizeof(twdt_task_t));
  349. ASSERT_EXIT_CRIT_RETURN((target_task != NULL), VOID_RETURN); //If calloc failed
  350. target_task->task_handle = handle;
  351. target_task->has_reset = true;
  352. target_task->next = NULL;
  353. if (twdt_config->list == NULL) { //Adding to empty list
  354. twdt_config->list = target_task;
  355. } else { //Adding to tail of list
  356. twdt_task_t *task;
  357. for (task = twdt_config->list; task->next != NULL; task = task->next){
  358. ; //point task to current tail of wdt task list
  359. }
  360. task->next = target_task;
  361. }
  362. portEXIT_CRITICAL(&twdt_spinlock);
  363. }