README.rst
1 Programming ULP coprocessor using C macros (legacy) 2 =================================================== 3 4 In addition to the existing binutils port for the ESP32 ULP coprocessor, it is possible to generate programs for the ULP by embedding assembly-like macros into an ESP32 application. Here is an example how this can be done:: 5 6 const ulp_insn_t program[] = { 7 I_MOVI(R3, 16), // R3 <- 16 8 I_LD(R0, R3, 0), // R0 <- RTC_SLOW_MEM[R3 + 0] 9 I_LD(R1, R3, 1), // R1 <- RTC_SLOW_MEM[R3 + 1] 10 I_ADDR(R2, R0, R1), // R2 <- R0 + R1 11 I_ST(R2, R3, 2), // R2 -> RTC_SLOW_MEM[R2 + 2] 12 I_HALT() 13 }; 14 size_t load_addr = 0; 15 size_t size = sizeof(program)/sizeof(ulp_insn_t); 16 ulp_process_macros_and_load(load_addr, program, &size); 17 ulp_run(load_addr); 18 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 21 .. note:: 22 23 Because some of the instruction macros expand to inline function calls, defining such array in global scope will cause the compiler to produce an "initializer element is not constant" error. To fix this error, move the definition of instructions array into local scope. 24 25 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). 26 27 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. 28 29 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. 30 31 Here is an example of using labels and branches:: 32 33 const ulp_insn_t program[] = { 34 I_MOVI(R0, 34), // R0 <- 34 35 M_LABEL(1), // label_1 36 I_MOVI(R1, 32), // R1 <- 32 37 I_LD(R1, R1, 0), // R1 <- RTC_SLOW_MEM[R1] 38 I_MOVI(R2, 33), // R2 <- 33 39 I_LD(R2, R2, 0), // R2 <- RTC_SLOW_MEM[R2] 40 I_SUBR(R3, R1, R2), // R3 <- R1 - R2 41 I_ST(R3, R0, 0), // R3 -> RTC_SLOW_MEM[R0 + 0] 42 I_ADDI(R0, R0, 1), // R0++ 43 M_BL(1, 64), // if (R0 < 64) goto label_1 44 I_HALT(), 45 }; 46 RTC_SLOW_MEM[32] = 42; 47 RTC_SLOW_MEM[33] = 18; 48 size_t load_addr = 0; 49 size_t size = sizeof(program)/sizeof(ulp_insn_t); 50 ulp_process_macros_and_load(load_addr, program, &size); 51 ulp_run(load_addr); 52 53 54 Application Example 55 ------------------- 56 57 Demonstration of entering into deep sleep mode and waking up using several wake up sources: :example:`system/deep_sleep`. 58 59 60 API Reference 61 ------------- 62 63 Header File 64 ^^^^^^^^^^^ 65 66 .. list:: 67 68 :esp32: - :component_file:`ulp/include/esp32/ulp.h` 69 :esp32s2: - :component_file:`ulp/include/esp32s2/ulp.h` 70 71 Functions 72 ^^^^^^^^^ 73 74 .. doxygenfunction:: ulp_process_macros_and_load 75 .. doxygenfunction:: ulp_run 76 77 Error codes 78 ^^^^^^^^^^^ 79 80 .. doxygendefine:: ESP_ERR_ULP_BASE 81 .. doxygendefine:: ESP_ERR_ULP_SIZE_TOO_BIG 82 .. doxygendefine:: ESP_ERR_ULP_INVALID_LOAD_ADDR 83 .. doxygendefine:: ESP_ERR_ULP_DUPLICATE_LABEL 84 .. doxygendefine:: ESP_ERR_ULP_UNDEFINED_LABEL 85 .. doxygendefine:: ESP_ERR_ULP_BRANCH_OUT_OF_RANGE 86 87 ULP coprocessor registers 88 ^^^^^^^^^^^^^^^^^^^^^^^^^ 89 90 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. 91 92 These definitions can be used for all instructions which require a register. 93 94 .. doxygengroup:: ulp_registers 95 :content-only: 96 97 ULP coprocessor instruction defines 98 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 99 100 .. doxygendefine:: I_DELAY 101 .. doxygendefine:: I_HALT 102 .. doxygendefine:: I_END 103 .. doxygendefine:: I_ST 104 .. doxygendefine:: I_LD 105 .. doxygendefine:: I_WR_REG 106 .. doxygendefine:: I_RD_REG 107 .. doxygendefine:: I_BL 108 .. doxygendefine:: I_BGE 109 .. doxygendefine:: I_BXR 110 .. doxygendefine:: I_BXI 111 .. doxygendefine:: I_BXZR 112 .. doxygendefine:: I_BXZI 113 .. doxygendefine:: I_BXFR 114 .. doxygendefine:: I_BXFI 115 .. doxygendefine:: I_ADDR 116 .. doxygendefine:: I_SUBR 117 .. doxygendefine:: I_ANDR 118 .. doxygendefine:: I_ORR 119 .. doxygendefine:: I_MOVR 120 .. doxygendefine:: I_LSHR 121 .. doxygendefine:: I_RSHR 122 .. doxygendefine:: I_ADDI 123 .. doxygendefine:: I_SUBI 124 .. doxygendefine:: I_ANDI 125 .. doxygendefine:: I_ORI 126 .. doxygendefine:: I_MOVI 127 .. doxygendefine:: I_LSHI 128 .. doxygendefine:: I_RSHI 129 .. doxygendefine:: M_LABEL 130 .. doxygendefine:: M_BL 131 .. doxygendefine:: M_BGE 132 .. doxygendefine:: M_BX 133 .. doxygendefine:: M_BXZ 134 .. doxygendefine:: M_BXF 135 136 Defines 137 ^^^^^^^ 138 139 .. doxygendefine:: RTC_SLOW_MEM 140