ulp_macro.c 10 KB

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