ccomp_timer_impl_xtensa.c 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /*
  2. * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdint.h>
  7. #include <string.h>
  8. #include "ccomp_timer_impl.h"
  9. #include "esp_intr_alloc.h"
  10. #include "esp_log.h"
  11. #include "esp_attr.h"
  12. #include "eri.h"
  13. #include "freertos/FreeRTOS.h"
  14. #include "esp_freertos_hooks.h"
  15. #include "perfmon.h"
  16. #include "xtensa/core-macros.h"
  17. #include "xtensa/xt_perf_consts.h"
  18. #include "xtensa-debug-module.h"
  19. #include "esp_private/esp_clk.h"
  20. #define D_STALL_COUNTER_ID 0
  21. #define I_STALL_COUNTER_ID 1
  22. typedef enum
  23. {
  24. PERF_TIMER_UNINIT = 0, // timer has not been initialized yet
  25. PERF_TIMER_IDLE, // timer has been initialized but is not tracking elapsed time
  26. PERF_TIMER_ACTIVE // timer is tracking elapsed time
  27. } ccomp_timer_state_t;
  28. typedef struct
  29. {
  30. int i_ovfl; // number of times instruction stall counter has overflowed
  31. int d_ovfl; // number of times data stall counter has overflowed
  32. uint32_t last_ccount; // last CCOUNT value, updated every os tick
  33. ccomp_timer_state_t state; // state of the timer
  34. intr_handle_t intr_handle; // handle to allocated handler for perfmon counter overflows, so that it can be freed during deinit
  35. int64_t ccount; // accumulated processors cycles during the time when timer is active
  36. } ccomp_timer_status_t;
  37. // Each core has its independent timer
  38. ccomp_timer_status_t s_status[] = {
  39. (ccomp_timer_status_t){
  40. .i_ovfl = 0,
  41. .d_ovfl = 0,
  42. .ccount = 0,
  43. .last_ccount = 0,
  44. .state = PERF_TIMER_UNINIT,
  45. .intr_handle = NULL,
  46. },
  47. (ccomp_timer_status_t){
  48. .i_ovfl = 0,
  49. .d_ovfl = 0,
  50. .ccount = 0,
  51. .last_ccount = 0,
  52. .state = PERF_TIMER_UNINIT,
  53. .intr_handle = NULL
  54. }
  55. };
  56. static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED;
  57. static void IRAM_ATTR update_ccount(void)
  58. {
  59. if (s_status[xPortGetCoreID()].state == PERF_TIMER_ACTIVE) {
  60. int64_t new_ccount = xthal_get_ccount();
  61. if (new_ccount > s_status[xPortGetCoreID()].last_ccount) {
  62. s_status[xPortGetCoreID()].ccount += new_ccount - s_status[xPortGetCoreID()].last_ccount;
  63. } else {
  64. // CCOUNT has wrapped around
  65. s_status[xPortGetCoreID()].ccount += new_ccount + (UINT32_MAX - s_status[xPortGetCoreID()].last_ccount);
  66. }
  67. s_status[xPortGetCoreID()].last_ccount = new_ccount;
  68. }
  69. }
  70. static void inline update_overflow(int id, int *cnt)
  71. {
  72. uint32_t pmstat = eri_read(ERI_PERFMON_PMSTAT0 + id * sizeof(int32_t));
  73. if (pmstat & PMSTAT_OVFL) {
  74. *cnt += 1;
  75. // Clear overflow and PerfMonInt asserted bits. The only valid bits in PMSTAT is the ones we're trying to clear. So it should be
  76. // ok to just modify the whole register.
  77. eri_write(ERI_PERFMON_PMSTAT0 + id, ~0x0);
  78. }
  79. }
  80. static void IRAM_ATTR perf_counter_overflow_handler(void *args)
  81. {
  82. update_overflow(D_STALL_COUNTER_ID, &s_status[xPortGetCoreID()].d_ovfl);
  83. update_overflow(I_STALL_COUNTER_ID, &s_status[xPortGetCoreID()].i_ovfl);
  84. }
  85. static void set_perfmon_interrupt(bool enable)
  86. {
  87. uint32_t d_pmctrl = eri_read(ERI_PERFMON_PMCTRL0 + D_STALL_COUNTER_ID * sizeof(int32_t));
  88. uint32_t i_pmctrl = eri_read(ERI_PERFMON_PMCTRL0 + I_STALL_COUNTER_ID * sizeof(int32_t));
  89. if (enable) {
  90. d_pmctrl |= PMCTRL_INTEN;
  91. i_pmctrl |= PMCTRL_INTEN;
  92. }
  93. else {
  94. d_pmctrl &= ~PMCTRL_INTEN;
  95. i_pmctrl &= ~PMCTRL_INTEN;
  96. }
  97. eri_write(ERI_PERFMON_PMCTRL0 + D_STALL_COUNTER_ID * sizeof(int32_t), d_pmctrl);
  98. eri_write(ERI_PERFMON_PMCTRL0 + I_STALL_COUNTER_ID * sizeof(int32_t), i_pmctrl);
  99. }
  100. esp_err_t ccomp_timer_impl_init(void)
  101. {
  102. // Keep track of how many times each counter has overflowed.
  103. esp_err_t err = esp_intr_alloc(ETS_INTERNAL_PROFILING_INTR_SOURCE, 0,
  104. perf_counter_overflow_handler, NULL, &s_status[xPortGetCoreID()].intr_handle);
  105. if (err != ESP_OK) {
  106. return err;
  107. }
  108. xtensa_perfmon_init(D_STALL_COUNTER_ID,
  109. XTPERF_CNT_D_STALL,
  110. XTPERF_MASK_D_STALL_BUSY, 0, -1);
  111. xtensa_perfmon_init(I_STALL_COUNTER_ID,
  112. XTPERF_CNT_I_STALL,
  113. XTPERF_MASK_I_STALL_BUSY, 0, -1);
  114. set_perfmon_interrupt(true);
  115. s_status[xPortGetCoreID()].state = PERF_TIMER_IDLE;
  116. return ESP_OK;
  117. }
  118. esp_err_t ccomp_timer_impl_deinit(void)
  119. {
  120. set_perfmon_interrupt(false);
  121. esp_err_t err = esp_intr_free(s_status[xPortGetCoreID()].intr_handle);
  122. if (err != ESP_OK) {
  123. return err;
  124. }
  125. s_status[xPortGetCoreID()].intr_handle = NULL;
  126. s_status[xPortGetCoreID()].state = PERF_TIMER_UNINIT;
  127. return ESP_OK;
  128. }
  129. esp_err_t ccomp_timer_impl_start(void)
  130. {
  131. s_status[xPortGetCoreID()].state = PERF_TIMER_ACTIVE;
  132. s_status[xPortGetCoreID()].last_ccount = xthal_get_ccount();
  133. // Update elapsed cycles every OS tick
  134. esp_register_freertos_tick_hook_for_cpu(update_ccount, xPortGetCoreID());
  135. xtensa_perfmon_start();
  136. return ESP_OK;
  137. }
  138. esp_err_t IRAM_ATTR ccomp_timer_impl_stop(void)
  139. {
  140. xtensa_perfmon_stop();
  141. esp_deregister_freertos_tick_hook_for_cpu(update_ccount, xPortGetCoreID());
  142. update_ccount();
  143. s_status[xPortGetCoreID()].state = PERF_TIMER_IDLE;
  144. return ESP_OK;
  145. }
  146. int64_t IRAM_ATTR ccomp_timer_impl_get_time(void)
  147. {
  148. update_ccount();
  149. int64_t d_stalls = xtensa_perfmon_value(D_STALL_COUNTER_ID) +
  150. s_status[xPortGetCoreID()].d_ovfl * (1 << sizeof(int32_t));
  151. int64_t i_stalls = xtensa_perfmon_value(I_STALL_COUNTER_ID) +
  152. s_status[xPortGetCoreID()].i_ovfl * (1 << sizeof(int32_t));
  153. int64_t stalls = d_stalls + i_stalls;
  154. int64_t cycles = s_status[xPortGetCoreID()].ccount;
  155. return ((cycles - stalls) * 1000000) / esp_clk_cpu_freq();
  156. }
  157. esp_err_t ccomp_timer_impl_reset(void)
  158. {
  159. xtensa_perfmon_reset(D_STALL_COUNTER_ID);
  160. xtensa_perfmon_reset(I_STALL_COUNTER_ID);
  161. s_status[xPortGetCoreID()].d_ovfl = 0;
  162. s_status[xPortGetCoreID()].i_ovfl = 0;
  163. s_status[xPortGetCoreID()].ccount = 0;
  164. s_status[xPortGetCoreID()].last_ccount = 0;
  165. return ESP_OK;
  166. }
  167. bool ccomp_timer_impl_is_init(void)
  168. {
  169. return s_status[xPortGetCoreID()].state != PERF_TIMER_UNINIT;
  170. }
  171. bool IRAM_ATTR ccomp_timer_impl_is_active(void)
  172. {
  173. return s_status[xPortGetCoreID()].state == PERF_TIMER_ACTIVE;
  174. }
  175. void IRAM_ATTR ccomp_timer_impl_lock(void)
  176. {
  177. portENTER_CRITICAL(&s_lock);
  178. }
  179. void IRAM_ATTR ccomp_timer_impl_unlock(void)
  180. {
  181. portEXIT_CRITICAL(&s_lock);
  182. }