1 // Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
22 #include "esp32/ulp.h"
25 #include "soc/rtc_cntl_reg.h"
26 #include "soc/sens_reg.h"
28 #include "sdkconfig.h"
30 static const char* TAG = "ulp";
39 #define RELOC_TYPE_LABEL 0
40 #define RELOC_TYPE_BRANCH 1
42 /* This record means: there is a label at address
43 * insn_addr, with number label_num.
45 #define RELOC_INFO_LABEL(label_num, insn_addr) (reloc_info_t) { \
49 .type = RELOC_TYPE_LABEL }
51 /* This record means: there is a branch instruction at
52 * insn_addr, it needs to be changed to point to address
55 #define RELOC_INFO_BRANCH(label_num, insn_addr) (reloc_info_t) { \
59 .type = RELOC_TYPE_BRANCH }
62 /* Processing branch and label macros involves four steps:
64 * 1. Iterate over program and count all instructions
65 * with "macro" opcode. Allocate relocations array
66 * with number of entries equal to number of macro
69 * 2. Remove all fake instructions with "macro" opcode
70 * and record their locations into relocations array.
71 * Removal is done using two pointers. Instructions
72 * are read from read_ptr, and written to write_ptr.
73 * When a macro instruction is encountered,
74 * its contents are recorded into the appropriate
75 * table, and then read_ptr is advanced again.
76 * When a real instruction is encountered, it is
77 * read via read_ptr and written to write_ptr.
78 * In the end, all macro instructions are removed,
79 * size of the program (expressed in words) is
80 * reduced by the total number of macro instructions
83 * 3. Sort relocations array by label number, and then
84 * by type ("label" or "branch") if label numbers
85 * match. This is done to simplify lookup on the next
88 * 4. Iterate over entries of relocations table.
89 * For each label number, label entry comes first
90 * because the array was sorted at the previous step.
91 * Label address is recorded, and all subsequent
92 * "branch" entries which point to the same label number
93 * are processed. For each branch entry, correct offset
94 * or absolute address is calculated, depending on branch
95 * type, and written into the appropriate field of
100 static esp_err_t do_single_reloc(ulp_insn_t* program, uint32_t load_addr,
101 reloc_info_t label_info, reloc_info_t branch_info)
103 size_t insn_offset = branch_info.addr - load_addr;
104 ulp_insn_t* insn = &program[insn_offset];
105 // B and BX have the same layout of opcode/sub_opcode fields,
106 // and share the same opcode
107 assert(insn->b.opcode == OPCODE_BRANCH
108 && "branch macro was applied to a non-branch instruction");
109 switch (insn->b.sub_opcode) {
111 int32_t offset = ((int32_t) label_info.addr) - ((int32_t) branch_info.addr);
112 uint32_t abs_offset = abs(offset);
113 uint32_t sign = (offset >= 0) ? 0 : 1;
114 if (abs_offset > 127) {
115 ESP_LOGW(TAG, "target out of range: branch from %x to %x",
116 branch_info.addr, label_info.addr);
117 return ESP_ERR_ULP_BRANCH_OUT_OF_RANGE;
119 insn->b.offset = abs_offset;
123 case SUB_OPCODE_BX: {
124 assert(insn->bx.reg == 0 &&
125 "relocation applied to a jump with offset in register");
126 insn->bx.addr = label_info.addr;
130 assert(false && "unexpected sub-opcode");
135 esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* program, size_t* psize)
137 const ulp_insn_t* read_ptr = program;
138 const ulp_insn_t* end = program + *psize;
139 size_t macro_count = 0;
140 // step 1: calculate number of macros
141 while (read_ptr < end) {
142 ulp_insn_t r_insn = *read_ptr;
143 if (r_insn.macro.opcode == OPCODE_MACRO) {
148 size_t real_program_size = *psize - macro_count;
149 const size_t ulp_mem_end = CONFIG_ULP_COPROC_RESERVE_MEM / sizeof(ulp_insn_t);
150 if (load_addr > ulp_mem_end) {
151 ESP_LOGW(TAG, "invalid load address %x, max is %x",
152 load_addr, ulp_mem_end);
153 return ESP_ERR_ULP_INVALID_LOAD_ADDR;
155 if (real_program_size + load_addr > ulp_mem_end) {
156 ESP_LOGE(TAG, "program too big: %d words, max is %d words",
157 real_program_size, ulp_mem_end);
158 return ESP_ERR_ULP_SIZE_TOO_BIG;
160 // If no macros found, copy the program and return.
161 if (macro_count == 0) {
162 memcpy(((ulp_insn_t*) RTC_SLOW_MEM) + load_addr, program, *psize * sizeof(ulp_insn_t));
165 reloc_info_t* reloc_info =
166 (reloc_info_t*) malloc(sizeof(reloc_info_t) * macro_count);
167 if (reloc_info == NULL) {
168 return ESP_ERR_NO_MEM;
171 // step 2: record macros into reloc_info array
172 // and remove them from then program
174 ulp_insn_t* output_program = ((ulp_insn_t*) RTC_SLOW_MEM) + load_addr;
175 ulp_insn_t* write_ptr = output_program;
176 uint32_t cur_insn_addr = load_addr;
177 reloc_info_t* cur_reloc = reloc_info;
178 while (read_ptr < end) {
179 ulp_insn_t r_insn = *read_ptr;
180 if (r_insn.macro.opcode == OPCODE_MACRO) {
181 switch(r_insn.macro.sub_opcode) {
182 case SUB_OPCODE_MACRO_LABEL:
183 *cur_reloc = RELOC_INFO_LABEL(r_insn.macro.label,
186 case SUB_OPCODE_MACRO_BRANCH:
187 *cur_reloc = RELOC_INFO_BRANCH(r_insn.macro.label,
191 assert(0 && "invalid sub_opcode for macro insn");
194 assert(read_ptr != end && "program can not end with macro insn");
197 // normal instruction (not a macro)
198 *write_ptr = *read_ptr;
205 // step 3: sort relocations array
206 int reloc_sort_func(const void* p_lhs, const void* p_rhs) {
207 const reloc_info_t lhs = *(const reloc_info_t*) p_lhs;
208 const reloc_info_t rhs = *(const reloc_info_t*) p_rhs;
209 if (lhs.label < rhs.label) {
211 } else if (lhs.label > rhs.label) {
214 // label numbers are equal
215 if (lhs.type < rhs.type) {
217 } else if (lhs.type > rhs.type) {
221 // both label number and type are equal
224 qsort(reloc_info, macro_count, sizeof(reloc_info_t),
227 // step 4: walk relocations array and fix instructions
228 reloc_info_t* reloc_end = reloc_info + macro_count;
229 cur_reloc = reloc_info;
230 while(cur_reloc < reloc_end) {
231 reloc_info_t label_info = *cur_reloc;
232 assert(label_info.type == RELOC_TYPE_LABEL);
234 while (cur_reloc < reloc_end) {
235 if (cur_reloc->type == RELOC_TYPE_LABEL) {
236 if(cur_reloc->label == label_info.label) {
237 ESP_LOGE(TAG, "duplicate label definition: %d",
240 return ESP_ERR_ULP_DUPLICATE_LABEL;
244 if (cur_reloc->label != label_info.label) {
245 ESP_LOGE(TAG, "branch to an inexistent label: %d",
248 return ESP_ERR_ULP_UNDEFINED_LABEL;
250 esp_err_t rc = do_single_reloc(output_program, load_addr,
251 label_info, *cur_reloc);
260 *psize = real_program_size;