ulp_macro.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright 2010-2016 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. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include <stdlib.h>
  17. #include "esp_attr.h"
  18. #include "esp_err.h"
  19. #include "esp_log.h"
  20. #include "esp32/ulp.h"
  21. #include "ulp_private.h"
  22. #include "soc/soc.h"
  23. #include "soc/rtc_cntl_reg.h"
  24. #include "soc/sens_reg.h"
  25. #include "sdkconfig.h"
  26. static const char* TAG = "ulp";
  27. typedef struct {
  28. uint32_t label : 16;
  29. uint32_t addr : 11;
  30. uint32_t unused : 1;
  31. uint32_t type : 4;
  32. } reloc_info_t;
  33. #define RELOC_TYPE_LABEL 0
  34. #define RELOC_TYPE_BRANCH 1
  35. /* This record means: there is a label at address
  36. * insn_addr, with number label_num.
  37. */
  38. #define RELOC_INFO_LABEL(label_num, insn_addr) (reloc_info_t) { \
  39. .label = label_num, \
  40. .addr = insn_addr, \
  41. .unused = 0, \
  42. .type = RELOC_TYPE_LABEL }
  43. /* This record means: there is a branch instruction at
  44. * insn_addr, it needs to be changed to point to address
  45. * of label label_num.
  46. */
  47. #define RELOC_INFO_BRANCH(label_num, insn_addr) (reloc_info_t) { \
  48. .label = label_num, \
  49. .addr = insn_addr, \
  50. .unused = 0, \
  51. .type = RELOC_TYPE_BRANCH }
  52. /* Comparison function used to sort the relocations array */
  53. static int reloc_sort_func(const void* p_lhs, const void* p_rhs)
  54. {
  55. const reloc_info_t lhs = *(const reloc_info_t*) p_lhs;
  56. const reloc_info_t rhs = *(const reloc_info_t*) p_rhs;
  57. if (lhs.label < rhs.label) {
  58. return -1;
  59. } else if (lhs.label > rhs.label) {
  60. return 1;
  61. }
  62. // label numbers are equal
  63. if (lhs.type < rhs.type) {
  64. return -1;
  65. } else if (lhs.type > rhs.type) {
  66. return 1;
  67. }
  68. // both label number and type are equal
  69. return 0;
  70. }
  71. /* Processing branch and label macros involves four steps:
  72. *
  73. * 1. Iterate over program and count all instructions
  74. * with "macro" opcode. Allocate relocations array
  75. * with number of entries equal to number of macro
  76. * instructions.
  77. *
  78. * 2. Remove all fake instructions with "macro" opcode
  79. * and record their locations into relocations array.
  80. * Removal is done using two pointers. Instructions
  81. * are read from read_ptr, and written to write_ptr.
  82. * When a macro instruction is encountered,
  83. * its contents are recorded into the appropriate
  84. * table, and then read_ptr is advanced again.
  85. * When a real instruction is encountered, it is
  86. * read via read_ptr and written to write_ptr.
  87. * In the end, all macro instructions are removed,
  88. * size of the program (expressed in words) is
  89. * reduced by the total number of macro instructions
  90. * which were present.
  91. *
  92. * 3. Sort relocations array by label number, and then
  93. * by type ("label" or "branch") if label numbers
  94. * match. This is done to simplify lookup on the next
  95. * step.
  96. *
  97. * 4. Iterate over entries of relocations table.
  98. * For each label number, label entry comes first
  99. * because the array was sorted at the previous step.
  100. * Label address is recorded, and all subsequent
  101. * "branch" entries which point to the same label number
  102. * are processed. For each branch entry, correct offset
  103. * or absolute address is calculated, depending on branch
  104. * type, and written into the appropriate field of
  105. * the instruction.
  106. *
  107. */
  108. static esp_err_t do_single_reloc(ulp_insn_t* program, uint32_t load_addr,
  109. reloc_info_t label_info, reloc_info_t branch_info)
  110. {
  111. size_t insn_offset = branch_info.addr - load_addr;
  112. ulp_insn_t* insn = &program[insn_offset];
  113. // B and BX have the same layout of opcode/sub_opcode fields,
  114. // and share the same opcode
  115. assert(insn->b.opcode == OPCODE_BRANCH
  116. && "branch macro was applied to a non-branch instruction");
  117. switch (insn->b.sub_opcode) {
  118. case SUB_OPCODE_B: {
  119. int32_t offset = ((int32_t) label_info.addr) - ((int32_t) branch_info.addr);
  120. uint32_t abs_offset = abs(offset);
  121. uint32_t sign = (offset >= 0) ? 0 : 1;
  122. if (abs_offset > 127) {
  123. ESP_LOGW(TAG, "target out of range: branch from %x to %x",
  124. branch_info.addr, label_info.addr);
  125. return ESP_ERR_ULP_BRANCH_OUT_OF_RANGE;
  126. }
  127. insn->b.offset = abs_offset;
  128. insn->b.sign = sign;
  129. break;
  130. }
  131. case SUB_OPCODE_BX: {
  132. assert(insn->bx.reg == 0 &&
  133. "relocation applied to a jump with offset in register");
  134. insn->bx.addr = label_info.addr;
  135. break;
  136. }
  137. default:
  138. assert(false && "unexpected sub-opcode");
  139. }
  140. return ESP_OK;
  141. }
  142. esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* program, size_t* psize)
  143. {
  144. const ulp_insn_t* read_ptr = program;
  145. const ulp_insn_t* end = program + *psize;
  146. size_t macro_count = 0;
  147. // step 1: calculate number of macros
  148. while (read_ptr < end) {
  149. ulp_insn_t r_insn = *read_ptr;
  150. if (r_insn.macro.opcode == OPCODE_MACRO) {
  151. ++macro_count;
  152. }
  153. ++read_ptr;
  154. }
  155. size_t real_program_size = *psize - macro_count;
  156. const size_t ulp_mem_end = ULP_RESERVE_MEM / sizeof(ulp_insn_t);
  157. if (load_addr > ulp_mem_end) {
  158. ESP_LOGW(TAG, "invalid load address %x, max is %x",
  159. load_addr, ulp_mem_end);
  160. return ESP_ERR_ULP_INVALID_LOAD_ADDR;
  161. }
  162. if (real_program_size + load_addr > ulp_mem_end) {
  163. ESP_LOGE(TAG, "program too big: %d words, max is %d words",
  164. real_program_size, ulp_mem_end);
  165. return ESP_ERR_ULP_SIZE_TOO_BIG;
  166. }
  167. // If no macros found, copy the program and return.
  168. if (macro_count == 0) {
  169. memcpy(((ulp_insn_t*) RTC_SLOW_MEM) + load_addr, program, *psize * sizeof(ulp_insn_t));
  170. return ESP_OK;
  171. }
  172. reloc_info_t* reloc_info =
  173. (reloc_info_t*) malloc(sizeof(reloc_info_t) * macro_count);
  174. if (reloc_info == NULL) {
  175. return ESP_ERR_NO_MEM;
  176. }
  177. // step 2: record macros into reloc_info array
  178. // and remove them from then program
  179. read_ptr = program;
  180. ulp_insn_t* output_program = ((ulp_insn_t*) RTC_SLOW_MEM) + load_addr;
  181. ulp_insn_t* write_ptr = output_program;
  182. uint32_t cur_insn_addr = load_addr;
  183. reloc_info_t* cur_reloc = reloc_info;
  184. while (read_ptr < end) {
  185. ulp_insn_t r_insn = *read_ptr;
  186. if (r_insn.macro.opcode == OPCODE_MACRO) {
  187. switch(r_insn.macro.sub_opcode) {
  188. case SUB_OPCODE_MACRO_LABEL:
  189. *cur_reloc = RELOC_INFO_LABEL(r_insn.macro.label,
  190. cur_insn_addr);
  191. break;
  192. case SUB_OPCODE_MACRO_BRANCH:
  193. *cur_reloc = RELOC_INFO_BRANCH(r_insn.macro.label,
  194. cur_insn_addr);
  195. break;
  196. default:
  197. assert(0 && "invalid sub_opcode for macro insn");
  198. }
  199. ++read_ptr;
  200. assert(read_ptr != end && "program can not end with macro insn");
  201. ++cur_reloc;
  202. } else {
  203. // normal instruction (not a macro)
  204. *write_ptr = *read_ptr;
  205. ++read_ptr;
  206. ++write_ptr;
  207. ++cur_insn_addr;
  208. }
  209. }
  210. // step 3: sort relocations array
  211. qsort(reloc_info, macro_count, sizeof(reloc_info_t),
  212. reloc_sort_func);
  213. // step 4: walk relocations array and fix instructions
  214. reloc_info_t* reloc_end = reloc_info + macro_count;
  215. cur_reloc = reloc_info;
  216. while(cur_reloc < reloc_end) {
  217. reloc_info_t label_info = *cur_reloc;
  218. assert(label_info.type == RELOC_TYPE_LABEL);
  219. ++cur_reloc;
  220. while (cur_reloc < reloc_end) {
  221. if (cur_reloc->type == RELOC_TYPE_LABEL) {
  222. if(cur_reloc->label == label_info.label) {
  223. ESP_LOGE(TAG, "duplicate label definition: %d",
  224. label_info.label);
  225. free(reloc_info);
  226. return ESP_ERR_ULP_DUPLICATE_LABEL;
  227. }
  228. break;
  229. }
  230. if (cur_reloc->label != label_info.label) {
  231. ESP_LOGE(TAG, "branch to an inexistent label: %d",
  232. cur_reloc->label);
  233. free(reloc_info);
  234. return ESP_ERR_ULP_UNDEFINED_LABEL;
  235. }
  236. esp_err_t rc = do_single_reloc(output_program, load_addr,
  237. label_info, *cur_reloc);
  238. if (rc != ESP_OK) {
  239. free(reloc_info);
  240. return rc;
  241. }
  242. ++cur_reloc;
  243. }
  244. }
  245. free(reloc_info);
  246. *psize = real_program_size;
  247. return ESP_OK;
  248. }