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