ulp_macro.c 10.0 KB

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