dport_access.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. // Copyright 2010-2017 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. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software
  9. // distributed under the License is distributed on an "AS IS" BASIS,
  10. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. // See the License for the specific language governing permissions and
  12. // limitations under the License.
  13. /*
  14. * DPORT access is used for do protection when dual core access DPORT internal register and APB register via DPORT simultaneously
  15. * This function will be initialize after FreeRTOS startup.
  16. * When cpu0 want to access DPORT register, it should notify cpu1 enter in high-priority interrupt for be mute. When cpu1 already in high-priority interrupt,
  17. * cpu0 can access DPORT register. Currently, cpu1 will wait for cpu0 finish access and exit high-priority interrupt.
  18. */
  19. #include <stdint.h>
  20. #include <string.h>
  21. #include "esp_attr.h"
  22. #include "esp_err.h"
  23. #include "esp_intr_alloc.h"
  24. #include "soc/cpu.h"
  25. #include "soc/dport_reg.h"
  26. #include "soc/spi_periph.h"
  27. #include "hal/cpu_hal.h"
  28. #include "freertos/FreeRTOS.h"
  29. #include "freertos/task.h"
  30. #include "freertos/semphr.h"
  31. #include "freertos/queue.h"
  32. #include "sdkconfig.h"
  33. #ifndef CONFIG_FREERTOS_UNICORE
  34. static portMUX_TYPE g_dport_mux = portMUX_INITIALIZER_UNLOCKED;
  35. #define DPORT_CORE_STATE_IDLE 0
  36. #define DPORT_CORE_STATE_RUNNING 1
  37. static uint32_t volatile dport_core_state[portNUM_PROCESSORS]; //cpu is already run
  38. /* these global variables are accessed from interrupt vector, hence not declared as static */
  39. uint32_t volatile dport_access_start[portNUM_PROCESSORS]; //dport register could be accessed
  40. uint32_t volatile dport_access_end[portNUM_PROCESSORS]; //dport register is accessed over
  41. static uint32_t volatile dport_access_ref[portNUM_PROCESSORS]; //dport access reference
  42. #ifdef DPORT_ACCESS_BENCHMARK
  43. #define DPORT_ACCESS_BENCHMARK_STORE_NUM
  44. static uint32_t ccount_start[portNUM_PROCESSORS];
  45. static uint32_t ccount_end[portNUM_PROCESSORS];
  46. static uint32_t ccount_margin[portNUM_PROCESSORS][DPORT_ACCESS_BENCHMARK_STORE_NUM];
  47. static uint32_t ccount_margin_cnt;
  48. #endif
  49. static BaseType_t oldInterruptLevel[2];
  50. #endif // CONFIG_FREERTOS_UNICORE
  51. /* stall other cpu that this cpu is pending to access dport register start */
  52. void IRAM_ATTR esp_dport_access_stall_other_cpu_start(void)
  53. {
  54. #ifndef CONFIG_FREERTOS_UNICORE
  55. if (dport_core_state[0] == DPORT_CORE_STATE_IDLE
  56. || dport_core_state[1] == DPORT_CORE_STATE_IDLE) {
  57. return;
  58. }
  59. BaseType_t intLvl = portENTER_CRITICAL_NESTED();
  60. int cpu_id = xPortGetCoreID();
  61. #ifdef DPORT_ACCESS_BENCHMARK
  62. ccount_start[cpu_id] = cpu_hal_get_cycle_count();
  63. #endif
  64. if (dport_access_ref[cpu_id] == 0) {
  65. portENTER_CRITICAL_ISR(&g_dport_mux);
  66. oldInterruptLevel[cpu_id]=intLvl;
  67. dport_access_start[cpu_id] = 0;
  68. dport_access_end[cpu_id] = 0;
  69. if (cpu_id == 0) {
  70. _DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_3_REG, DPORT_CPU_INTR_FROM_CPU_3); //interrupt on cpu1
  71. } else {
  72. _DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_2_REG, DPORT_CPU_INTR_FROM_CPU_2); //interrupt on cpu0
  73. }
  74. while (!dport_access_start[cpu_id]) {};
  75. REG_READ(SPI_DATE_REG(3)); //just read a APB register sure that the APB-bus is idle
  76. }
  77. dport_access_ref[cpu_id]++;
  78. if (dport_access_ref[cpu_id] > 1) {
  79. /* Interrupts are already disabled by the parent, we're nested here. */
  80. portEXIT_CRITICAL_NESTED(intLvl);
  81. }
  82. #endif /* CONFIG_FREERTOS_UNICORE */
  83. }
  84. /* stall other cpu that this cpu is pending to access dport register end */
  85. void IRAM_ATTR esp_dport_access_stall_other_cpu_end(void)
  86. {
  87. #ifndef CONFIG_FREERTOS_UNICORE
  88. int cpu_id = xPortGetCoreID();
  89. if (dport_core_state[0] == DPORT_CORE_STATE_IDLE
  90. || dport_core_state[1] == DPORT_CORE_STATE_IDLE) {
  91. return;
  92. }
  93. if (dport_access_ref[cpu_id] == 0) {
  94. assert(0);
  95. }
  96. dport_access_ref[cpu_id]--;
  97. if (dport_access_ref[cpu_id] == 0) {
  98. dport_access_end[cpu_id] = 1;
  99. portEXIT_CRITICAL_ISR(&g_dport_mux);
  100. portEXIT_CRITICAL_NESTED(oldInterruptLevel[cpu_id]);
  101. }
  102. #ifdef DPORT_ACCESS_BENCHMARK
  103. ccount_end[cpu_id] = cpu_hal_get_cycle_count();
  104. ccount_margin[cpu_id][ccount_margin_cnt] = ccount_end[cpu_id] - ccount_start[cpu_id];
  105. ccount_margin_cnt = (ccount_margin_cnt + 1)&(DPORT_ACCESS_BENCHMARK_STORE_NUM - 1);
  106. #endif
  107. #endif /* CONFIG_FREERTOS_UNICORE */
  108. }
  109. #ifndef CONFIG_FREERTOS_UNICORE
  110. static void dport_access_init_core(void *arg)
  111. {
  112. int core_id = 0;
  113. uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE;
  114. core_id = xPortGetCoreID();
  115. if (core_id == 1) {
  116. intr_source = ETS_FROM_CPU_INTR3_SOURCE;
  117. }
  118. ESP_INTR_DISABLE(ETS_DPORT_INUM);
  119. intr_matrix_set(core_id, intr_source, ETS_DPORT_INUM);
  120. ESP_INTR_ENABLE(ETS_DPORT_INUM);
  121. dport_access_ref[core_id] = 0;
  122. dport_access_start[core_id] = 0;
  123. dport_access_end[core_id] = 0;
  124. dport_core_state[core_id] = DPORT_CORE_STATE_RUNNING;
  125. /* If this fails then the minimum stack size for this config is too close to running out */
  126. assert(uxTaskGetStackHighWaterMark(NULL) > 128);
  127. vTaskDelete(NULL);
  128. }
  129. #endif
  130. /* Defer initialisation until after scheduler is running */
  131. void esp_dport_access_int_init(void)
  132. {
  133. #ifndef CONFIG_FREERTOS_UNICORE
  134. portBASE_TYPE res = xTaskCreatePinnedToCore(&dport_access_init_core, "dport", configMINIMAL_STACK_SIZE, NULL, 5, NULL, xPortGetCoreID());
  135. assert(res == pdTRUE);
  136. #endif
  137. }
  138. void IRAM_ATTR esp_dport_access_int_pause(void)
  139. {
  140. #ifndef CONFIG_FREERTOS_UNICORE
  141. portENTER_CRITICAL_ISR(&g_dport_mux);
  142. dport_core_state[0] = DPORT_CORE_STATE_IDLE;
  143. dport_core_state[1] = DPORT_CORE_STATE_IDLE;
  144. portEXIT_CRITICAL_ISR(&g_dport_mux);
  145. #endif
  146. }
  147. //Used in panic code: the enter_critical stuff may be messed up so we just stop everything without checking the mux.
  148. void IRAM_ATTR esp_dport_access_int_abort(void)
  149. {
  150. #ifndef CONFIG_FREERTOS_UNICORE
  151. dport_core_state[0] = DPORT_CORE_STATE_IDLE;
  152. dport_core_state[1] = DPORT_CORE_STATE_IDLE;
  153. #endif
  154. }
  155. void IRAM_ATTR esp_dport_access_int_resume(void)
  156. {
  157. #ifndef CONFIG_FREERTOS_UNICORE
  158. portENTER_CRITICAL_ISR(&g_dport_mux);
  159. dport_core_state[0] = DPORT_CORE_STATE_RUNNING;
  160. dport_core_state[1] = DPORT_CORE_STATE_RUNNING;
  161. portEXIT_CRITICAL_ISR(&g_dport_mux);
  162. #endif
  163. }
  164. /**
  165. * @brief Read a sequence of DPORT registers to the buffer, SMP-safe version.
  166. *
  167. * This implementation uses a method of the pre-reading of the APB register
  168. * before reading the register of the DPORT, without stall other CPU.
  169. * There is disable/enable interrupt.
  170. *
  171. * @param[out] buff_out Contains the read data.
  172. * @param[in] address Initial address for reading registers.
  173. * @param[in] num_words The number of words.
  174. */
  175. void IRAM_ATTR esp_dport_access_read_buffer(uint32_t *buff_out, uint32_t address, uint32_t num_words)
  176. {
  177. DPORT_INTERRUPT_DISABLE();
  178. for (uint32_t i = 0; i < num_words; ++i) {
  179. buff_out[i] = DPORT_SEQUENCE_REG_READ(address + i * 4);
  180. }
  181. DPORT_INTERRUPT_RESTORE();
  182. }
  183. /**
  184. * @brief Read value from register, SMP-safe version.
  185. *
  186. * This method uses the pre-reading of the APB register before reading the register of the DPORT.
  187. * This implementation is useful for reading DORT registers for single reading without stall other CPU.
  188. * There is disable/enable interrupt.
  189. *
  190. * @param reg Register address
  191. * @return Value
  192. */
  193. uint32_t IRAM_ATTR esp_dport_access_reg_read(uint32_t reg)
  194. {
  195. #if defined(BOOTLOADER_BUILD) || !defined(CONFIG_ESP32_DPORT_WORKAROUND) || !defined(ESP_PLATFORM)
  196. return _DPORT_REG_READ(reg);
  197. #else
  198. uint32_t apb;
  199. unsigned int intLvl;
  200. __asm__ __volatile__ (\
  201. "rsil %[LVL], "XTSTR(CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL)"\n"\
  202. "movi %[APB], "XTSTR(0x3ff40078)"\n"\
  203. "l32i %[APB], %[APB], 0\n"\
  204. "l32i %[REG], %[REG], 0\n"\
  205. "wsr %[LVL], "XTSTR(PS)"\n"\
  206. "rsync\n"\
  207. : [APB]"=a"(apb), [REG]"+a"(reg), [LVL]"=a"(intLvl)\
  208. : \
  209. : "memory" \
  210. );
  211. return reg;
  212. #endif
  213. }
  214. /**
  215. * @brief Read value from register, NOT SMP-safe version.
  216. *
  217. * This method uses the pre-reading of the APB register before reading the register of the DPORT.
  218. * There is not disable/enable interrupt.
  219. * The difference from DPORT_REG_READ() is that the user himself must disable interrupts while DPORT reading.
  220. * This implementation is useful for reading DORT registers in loop without stall other CPU. Note the usage example.
  221. * The recommended way to read registers sequentially without stall other CPU
  222. * is to use the method esp_dport_read_buffer(buff_out, address, num_words). It allows you to read registers in the buffer.
  223. *
  224. * \code{c}
  225. * // This example shows how to use it.
  226. * { // Use curly brackets to limit the visibility of variables in macros DPORT_INTERRUPT_DISABLE/RESTORE.
  227. * DPORT_INTERRUPT_DISABLE(); // Disable interrupt only on current CPU.
  228. * for (i = 0; i < max; ++i) {
  229. * array[i] = esp_dport_access_sequence_reg_read(Address + i * 4); // reading DPORT registers
  230. * }
  231. * DPORT_INTERRUPT_RESTORE(); // restore the previous interrupt level
  232. * }
  233. * \endcode
  234. *
  235. * @param reg Register address
  236. * @return Value
  237. */
  238. uint32_t IRAM_ATTR esp_dport_access_sequence_reg_read(uint32_t reg)
  239. {
  240. #if defined(BOOTLOADER_BUILD) || !defined(CONFIG_ESP32_DPORT_WORKAROUND) || !defined(ESP_PLATFORM)
  241. return _DPORT_REG_READ(reg);
  242. #else
  243. uint32_t apb;
  244. __asm__ __volatile__ (\
  245. "movi %[APB], "XTSTR(0x3ff40078)"\n"\
  246. "l32i %[APB], %[APB], 0\n"\
  247. "l32i %[REG], %[REG], 0\n"\
  248. : [APB]"=a"(apb), [REG]"+a"(reg)\
  249. : \
  250. : "memory" \
  251. );
  252. return reg;
  253. #endif
  254. }