README.rst 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. ULP coprocessor programming
  2. ===========================
  3. .. warning:: ULP coprocessor programming approach described here is experimental. It is probable that once binutils support for ULP is done, this preprocessor-based approach may be deprecated. We welcome discussion about and contributions to ULP programming tools.
  4. ULP coprocessor is a simple FSM which is designed to perform measurements using ADC, temperature sensor, and external I2C sensors, while main processors are in deep sleep mode. ULP coprocessor can access RTC_SLOW_MEM memory region, and registers in RTC_CNTL, RTC_IO, and SARADC peripherals. ULP coprocessor uses fixed-width 32-bit instructions, 32-bit memory addressing, and has 4 general purpose 16-bit registers.
  5. ULP coprocessor doesn't have a dedicated binutils port yet. Programming ULP coprocessor is possible by embedding assembly-like macros into an ESP32 application.
  6. Here is an example how this can be done::
  7. const ulp_insn_t program[] = {
  8. I_MOVI(R3, 16), // R3 <- 16
  9. I_LD(R0, R3, 0), // R0 <- RTC_SLOW_MEM[R3 + 0]
  10. I_LD(R1, R3, 1), // R1 <- RTC_SLOW_MEM[R3 + 1]
  11. I_ADDR(R2, R0, R1), // R2 <- R0 + R1
  12. I_ST(R2, R3, 2), // R2 -> RTC_SLOW_MEM[R2 + 2]
  13. I_HALT()
  14. };
  15. size_t load_addr = 0;
  16. size_t size = sizeof(program)/sizeof(ulp_insn_t);
  17. ulp_process_macros_and_load(load_addr, program, &size);
  18. ulp_run(load_addr);
  19. The ``program`` array is an array of ``ulp_insn_t``, i.e. ULP coprocessor instructions. Each ``I_XXX`` preprocessor define translates into a single 32-bit instruction. Arguments of these preprocessor defines can be register numbers (``R0 — R3``) and literal constants. See `ULP coprocessor instruction defines`_ section for descriptions of instructions and arguments they take.
  20. Load and store instructions use addresses expressed in 32-bit words. Address 0 corresponds to the first word of ``RTC_SLOW_MEM`` (which is address 0x50000000 as seen by the main CPUs).
  21. To generate branch instructions, special ``M_`` preprocessor defines are used. ``M_LABEL`` define can be used to define a branch target. Label identifier is a 16-bit integer. ``M_Bxxx`` defines can be used to generate branch instructions with target set to a particular label.
  22. Implementation note: these ``M_`` preprocessor defines will be translated into two ``ulp_insn_t`` values: one is a token value which contains label number, and the other is the actual instruction. ``ulp_process_macros_and_load`` function resolves the label number to the address, modifies the branch instruction to use the correct address, and removes the the extra ``ulp_insn_t`` token which contains the label numer.
  23. Here is an example of using labels and branches::
  24. const ulp_insn_t program[] = {
  25. I_MOVI(R0, 34), // R0 <- 34
  26. M_LABEL(1), // label_1
  27. I_MOVI(R1, 32), // R1 <- 32
  28. I_LD(R1, R1, 0), // R1 <- RTC_SLOW_MEM[R1]
  29. I_MOVI(R2, 33), // R2 <- 33
  30. I_LD(R2, R2, 0), // R2 <- RTC_SLOW_MEM[R2]
  31. I_SUBR(R3, R1, R2), // R3 <- R1 - R2
  32. I_ST(R3, R0, 0), // R3 -> RTC_SLOW_MEM[R0 + 0]
  33. I_ADDI(R0, R0, 1), // R0++
  34. M_BL(1, 64), // if (R0 < 64) goto label_1
  35. I_HALT(),
  36. };
  37. RTC_SLOW_MEM[32] = 42;
  38. RTC_SLOW_MEM[33] = 18;
  39. size_t load_addr = 0;
  40. size_t size = sizeof(program)/sizeof(ulp_insn_t);
  41. ulp_process_macros_and_load(load_addr, program, &size);
  42. ulp_run(load_addr);
  43. Functions
  44. ^^^^^^^^^
  45. .. doxygenfunction:: ulp_process_macros_and_load
  46. .. doxygenfunction:: ulp_run
  47. Error codes
  48. ^^^^^^^^^^^
  49. .. doxygendefine:: ESP_ERR_ULP_BASE
  50. .. doxygendefine:: ESP_ERR_ULP_SIZE_TOO_BIG
  51. .. doxygendefine:: ESP_ERR_ULP_INVALID_LOAD_ADDR
  52. .. doxygendefine:: ESP_ERR_ULP_DUPLICATE_LABEL
  53. .. doxygendefine:: ESP_ERR_ULP_UNDEFINED_LABEL
  54. .. doxygendefine:: ESP_ERR_ULP_BRANCH_OUT_OF_RANGE
  55. ULP coprocessor registers
  56. ^^^^^^^^^^^^^^^^^^^^^^^^^
  57. ULP co-processor has 4 16-bit general purpose registers. All registers have same functionality, with one exception. R0 register is used by some of the compare-and-branch instructions as a source register.
  58. These definitions can be used for all instructions which require a register.
  59. .. doxygengroup:: ulp_registers
  60. :content-only:
  61. ULP coprocessor instruction defines
  62. ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  63. .. doxygendefine:: I_DELAY
  64. .. doxygendefine:: I_HALT
  65. .. doxygendefine:: I_END
  66. .. doxygendefine:: I_ST
  67. .. doxygendefine:: I_LD
  68. .. doxygendefine:: I_WR_REG
  69. .. doxygendefine:: I_RD_REG
  70. .. doxygendefine:: I_BL
  71. .. doxygendefine:: I_BGE
  72. .. doxygendefine:: I_BXR
  73. .. doxygendefine:: I_BXI
  74. .. doxygendefine:: I_BXZR
  75. .. doxygendefine:: I_BXZI
  76. .. doxygendefine:: I_BXFR
  77. .. doxygendefine:: I_BXFI
  78. .. doxygendefine:: I_ADDR
  79. .. doxygendefine:: I_SUBR
  80. .. doxygendefine:: I_ANDR
  81. .. doxygendefine:: I_ORR
  82. .. doxygendefine:: I_MOVR
  83. .. doxygendefine:: I_LSHR
  84. .. doxygendefine:: I_RSHR
  85. .. doxygendefine:: I_ADDI
  86. .. doxygendefine:: I_SUBI
  87. .. doxygendefine:: I_ANDI
  88. .. doxygendefine:: I_ORI
  89. .. doxygendefine:: I_MOVI
  90. .. doxygendefine:: I_LSHI
  91. .. doxygendefine:: I_RSHI
  92. .. doxygendefine:: M_LABEL
  93. .. doxygendefine:: M_BL
  94. .. doxygendefine:: M_BGE
  95. .. doxygendefine:: M_BX
  96. .. doxygendefine:: M_BXZ
  97. .. doxygendefine:: M_BXF
  98. Defines
  99. ^^^^^^^
  100. .. doxygendefine:: RTC_SLOW_MEM