ipc.rst 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. 处理器间调用 (IPC)
  2. ====================
  3. :link_to_translation:`en:[English]`
  4. .. note::
  5. IPC 指 **处理器间调用** (Inter-Processor Call),而不是其他操作系统中所指的 **进程间通信** (Inter-Process Communication)。
  6. 概述
  7. --------
  8. 由于 {IDF_TARGET_NAME} 的双核特性,在某些情况下,某个回调必须在特定的内核上下文中运行。例如:
  9. - 为特定内核的中断源分配 ISR,或释放特定内核的中断源时
  10. - 在特定芯片(如 ESP32)上访问某个内核独有的内存,如 RTC Fast Memory 时
  11. - 读取另一个内核的寄存器或状态时
  12. IPC 功能允许一个特定的内核(下文称“调用内核”)触发另一个内核(下文称“目标内核”)的回调函数执行,并允许目标内核在任务上下文或中断上下文中执行回调函数。在不同的上下文中,回调函数的实现限制有所不同。
  13. 任务上下文中的 IPC
  14. -------------------
  15. 在应用程序启动时,IPC 功能为每个内核创建一个 IPC 任务,从而在任务上下文中执行回调。当调用内核需要在目标内核中执行回调时,回调将在目标内核的 IPC 任务的上下文中执行。
  16. 在任务上下文中使用 IPC 功能时,需考虑以下几点:
  17. - IPC 回调应该尽可能简短。 **IPC 回调决不能阻塞或让出**。
  18. - IPC 任务是以尽可能高的优先级创建的(即 ``configMAX_PRIORITIES - 1``)。
  19. - 如果启用了 :ref:`CONFIG_ESP_IPC_USES_CALLERS_PRIORITY`,执行回调前会降低目标内核的 IPC 任务优先级,使其等于调用内核的优先级。
  20. - 如果禁用了 :ref:`CONFIG_ESP_IPC_USES_CALLERS_PRIORITY`,目标内核将始终以尽可能高的优先级执行回调。
  21. - 如果回调较为复杂,用户可能需要通过 :ref:`CONFIG_ESP_IPC_TASK_STACK_SIZE` 来配置 IPC 任务的堆栈大小。
  22. - IPC 功能受内部互斥锁保护。因此,如果同时收到来自两个或多个调用内核的 IPC 请求,将按照“先到先得”的原则按顺序处理。
  23. API 用法
  24. ^^^^^^^^^
  25. 任务上下文中的 IPC 回调具有以下限制:
  26. - 回调类型必须是 :cpp:type:`esp_ipc_func_t`。
  27. - 回调 **决不能阻塞或让出**,以免导致目标内核的 IPC 任务阻塞或让出。
  28. - 回调必须避免改变 IPC 任务的任何状态,例如,不能调用 ``vTaskPrioritySet(NULL, x)``。
  29. IPC 功能提供了以下 API,用于在目标内核的任务上下文中执行回调。这两个 API 允许调用内核在回调执行完成前处于阻塞,或者在回调开始执行后立即返回。
  30. - :cpp:func:`esp_ipc_call` 会在目标内核上触发一个 IPC 调用。在目标内核的 IPC 任务 **开始** 执行回调前,此函数会一直处于阻塞状态。
  31. - :cpp:func:`esp_ipc_call_blocking` 会在目标内核上触发一个 IPC。在目标内核的 IPC 任务 **完成** 回调执行前,此函数会一直处于阻塞状态。
  32. 中断上下文中的 IPC
  33. ------------------------
  34. 在某些情况下,我们需要快速获取另一个内核的状态,如在核心转储、GDB Stub、各种单元测试和绕过硬件错误的过程中。IPC ISR 功能会在每个内核上保留一个高优先级中断供 IPC 使用,从而在高优先级中断上下文中执行回调。当调用内核需要在目标内核上执行回调时,该回调将在目标内核的高优先级中断的上下文中执行。
  35. .. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
  36. 在这种情况下,IPC ISR 功能支持在 :doc:`高优先级中断 </api-guides/hlinterrupts>` 上下文中执行回调。
  37. 在高优先级中断上下文中使用 IPC 时,需要考虑以下几点:
  38. .. list::
  39. :CONFIG_IDF_TARGET_ARCH_XTENSA: - 由于回调是在高优先级中断上下文中执行的,因此,回调必须完全用汇编语言编写。如需了解更多关于用汇编语言编写回调的内容,请参阅下文的 API 使用介绍。
  40. - 保留的高优先级中断的优先级取决于 :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` 选项。
  41. 当回调执行时,需考虑以下几点:
  42. .. list::
  43. - 调用内核会禁用 3 级及以下优先级的中断。
  44. :CONFIG_IDF_TARGET_ARCH_XTENSA: - 虽然保留中断的优先级取决于 :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL`,但是在执行 IPC ISR 回调期间,无论 :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` 如何设置,目标内核都会禁用 5 级及以下优先级的中断。
  45. :CONFIG_IDF_TARGET_ARCH_RISCV: - 虽然保留中断的优先级取决于 :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL`,但是在执行 IPC ISR 回调期间,目标内核会禁用所有的中断。
  46. API 用法
  47. ^^^^^^^^^
  48. .. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
  49. 高优先级中断的 IPC 回调具有以下限制:
  50. - 回调必须是 :cpp:type:`esp_ipc_isr_func_t` 类型,但必须完全用汇编语言实现。
  51. - 回调通过 ``CALLX0`` 指令调用,同时禁用寄存器窗口,因此,该回调:
  52. - 不得调用任何与寄存器窗口相关的指令,例如 ``entry`` 和 ``retw``。
  53. - 由于禁用了寄存器窗口,不得调用其他 C 函数。
  54. - 回调应放在 4 字节对齐的 IRAM 地址上。
  55. - 在调用或返回回调后,寄存器 ``a2``,``a3``,和 ``a4`` 会被自动保存或恢复,从而在回调中使用。回调 **只能使用这些寄存器**。
  56. - ``a2`` 包含回调的 ``void *arg``。
  57. - ``a3/a4`` 可以作为临时寄存器使用。
  58. .. only:: CONFIG_IDF_TARGET_ARCH_RISCV
  59. 高优先级中断 IPC 回调函数的类型必须是 :cpp:type:`esp_ipc_isr_func_t`,其限制条件与常规中断处理程序相同。回调函数可以用 C 语言编写。
  60. IPC 功能提供了下列 API,以在高优先级中断的上下文中执行回调:
  61. - :cpp:func:`esp_ipc_isr_call` 能够在目标内核上触发一个 IPC 调用。在目标内核 **开始** 执行回调前,此函数将一直处于忙等待。
  62. - :cpp:func:`esp_ipc_isr_call_blocking` 能够在目标内核上触发一个 IPC 调用。在目标内核 **完成** 回调执行前,此函数将一直处于忙等待。
  63. .. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
  64. 以下示例代码用汇编语言编写了一个高优先级中断 IPC 回调,该回调的作用为读取目标内核的周期计数:
  65. .. code-block:: asm
  66. /* esp_test_ipc_isr_get_cycle_count_other_cpu(void *arg) */
  67. // 此函数读取目标内核的 CCOUNT,并将其存储到 arg 中。
  68. // 此处仅使用 a2、a3 和 a4 寄存器。
  69. .section .iram1, "ax"
  70. .align 4
  71. .global esp_test_ipc_isr_get_cycle_count_other_cpu
  72. .type esp_test_ipc_isr_get_cycle_count_other_cpu, @function
  73. // Args:
  74. // a2 - void* arg
  75. esp_test_ipc_isr_get_cycle_count_other_cpu:
  76. rsr.ccount a3
  77. s32i a3, a2, 0
  78. ret
  79. .. code-block:: c
  80. unit32_t cycle_count;
  81. esp_ipc_isr_call_blocking(esp_test_ipc_isr_get_cycle_count_other_cpu, (void *)cycle_count);
  82. .. note::
  83. 对于大多数简单用例,可用的临时寄存器数量是足够的。如果需要在回调函数中使用更多的临时寄存器,可以用 ``void *arg`` 指向一个用作寄存器保存区域的 buffer,使回调函数保存或恢复更多的寄存器。前往 :example:`system/ipc/ipc_isr` 查看示例。
  84. .. note::
  85. 如需查看更多高优先级中断 IPC 回调的示例,请前往 :idf_file:`components/esp_system/port/arch/xtensa/esp_ipc_isr_routines.S` 和 :idf_file:`components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/xtensa/test_ipc_isr.S`。
  86. .. only:: CONFIG_IDF_TARGET_ARCH_RISCV
  87. 请前往 :idf_file:`examples/system/ipc/ipc_isr/riscv/main/main.c` 查看使用示例。
  88. .. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
  89. 请前往 :idf_file:`examples/system/ipc/ipc_isr/xtensa/main/main.c` 查看使用示例。
  90. 高优先级中断 IPC API 还提供了以下便利函数,这些函数可以暂停或恢复目标内核的执行。这些 API 利用高优先级中断 IPC,但同时提供了自己的内部回调函数:
  91. .. list::
  92. :CONFIG_IDF_TARGET_ARCH_RISCV: - :cpp:func:`esp_ipc_isr_stall_other_cpu`:暂停目标内核。调用内核禁用 3 级及以下级别的中断,而目标内核将在所有中断被禁用的情况下进入忙等待。在调用 :cpp:func:`esp_ipc_isr_release_other_cpu` 前,目标内核会保持忙等待。
  93. :CONFIG_IDF_TARGET_ARCH_XTENSA: - :cpp:func:`esp_ipc_isr_stall_other_cpu`:暂停目标内核。调用内核禁用 3 级及以下级别的中断,而目标内核将在 5 级及以下的中断被禁用的情况下进入忙等待。在调用 :cpp:func:`esp_ipc_isr_release_other_cpu` 前,目标内核会保持忙等待。
  94. - :cpp:func:`esp_ipc_isr_release_other_cpu`:恢复目标内核。
  95. API 参考
  96. -------------
  97. .. include-build-file:: inc/esp_ipc.inc
  98. .. include-build-file:: inc/esp_ipc_isr.inc