ulp_macro.c 10 KB

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