1
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
32
33
34
35
36
37
40
46
50
56
60
66
67
68
69
70
71
76
77
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
149
150
151
152
153
161
162
163
164
172
173
174
175
176
177
178
179
180
181
182
189
190
191
196
201
202
206
207
208
211
212
213
214
215
216
217
218
219
249
250
251
252
253
254
255
256
257
258
259
260
261
262
271
277
278
279
283
284
285
286
287
288
289
/* ... */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "ulp.h"
#include "ulp_common.h"
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "soc/sens_reg.h"
#include "sdkconfig.h"13 includes
static const char* TAG = "ulp";
typedef struct {
uint32_t label : 16;
uint32_t addr : 11;
uint32_t unused : 1;
uint32_t type : 4;
}{ ... } reloc_info_t;
#define RELOC_TYPE_LABEL 0
#define RELOC_TYPE_BRANCH 1
#define RELOC_TYPE_LABELPC 2
/* ... */
#define RELOC_INFO_LABEL(label_num, insn_addr) (reloc_info_t) { \
.label = label_num, \
.addr = insn_addr, \
.unused = 0, \
.type = RELOC_TYPE_LABEL }{...}
...
/* ... */
#define RELOC_INFO_BRANCH(label_num, insn_addr) (reloc_info_t) { \
.label = label_num, \
.addr = insn_addr, \
.unused = 0, \
.type = RELOC_TYPE_BRANCH }{...}
...
/* ... */
#define RELOC_INFO_LABELPC(label_num, insn_addr) (reloc_info_t) { \
.label = label_num, \
.addr = insn_addr, \
.unused = 0, \
.type = RELOC_TYPE_LABELPC }{...}
...
6 defines
static int reloc_sort_func(const void* p_lhs, const void* p_rhs)
{
const reloc_info_t lhs = *(const reloc_info_t*) p_lhs;
const reloc_info_t rhs = *(const reloc_info_t*) p_rhs;
if (lhs.label < rhs.label) {
return -1;
}{...} else if (lhs.label > rhs.label) {
return 1;
}{...}
if (lhs.type < rhs.type) {
return -1;
}{...} else if (lhs.type > rhs.type) {
return 1;
}{...}
return 0;
}{ ... }
/* ... */
static esp_err_t do_single_reloc(ulp_insn_t* program, uint32_t load_addr,
reloc_info_t label_info, reloc_info_t the_reloc)
{
size_t insn_offset = the_reloc.addr - load_addr;
ulp_insn_t* insn = &program[insn_offset];
switch (the_reloc.type) {
case RELOC_TYPE_BRANCH: {
assert(insn->b.opcode == OPCODE_BRANCH
&& "branch macro was applied to a non-branch instruction");
switch (insn->b.sub_opcode) {
case SUB_OPCODE_B:
case SUB_OPCODE_BS: {
int32_t offset = ((int32_t) label_info.addr) - ((int32_t) the_reloc.addr);
uint32_t abs_offset = abs(offset);
uint32_t sign = (offset >= 0) ? 0 : 1;
if (abs_offset > 127) {
ESP_LOGW(TAG, "target out of range: branch from %x to %x",
the_reloc.addr, label_info.addr);
return ESP_ERR_ULP_BRANCH_OUT_OF_RANGE;
}{...}
insn->b.offset = abs_offset;
insn->b.sign = sign;
break;
}{...}
... case SUB_OPCODE_BX: {
assert(insn->bx.reg == 0 &&
"relocation applied to a jump with offset in register");
insn->bx.addr = label_info.addr;
break;
}{...}
... default:
assert(false && "unexpected branch sub-opcode");...
}{...}
break;
}{...}
... case RELOC_TYPE_LABELPC: {
assert((insn->alu_imm.opcode == OPCODE_ALU && insn->alu_imm.sub_opcode == SUB_OPCODE_ALU_IMM && insn->alu_imm.sel == ALU_SEL_MOV)
&& "pc macro was applied to an incompatible instruction");
insn->alu_imm.imm = label_info.addr;
break;
}{...}
... default:
assert(false && "unknown reloc type");...
}{...}
return ESP_OK;
}{ ... }
esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* program, size_t* psize)
{
const ulp_insn_t* read_ptr = program;
const ulp_insn_t* end = program + *psize;
size_t macro_count = 0;
while (read_ptr < end) {
ulp_insn_t r_insn = *read_ptr;
if (r_insn.macro.opcode == OPCODE_MACRO) {
++macro_count;
}{...}
++read_ptr;
}{...}
size_t real_program_size = *psize - macro_count;
const size_t ulp_mem_end = CONFIG_ULP_COPROC_RESERVE_MEM / sizeof(ulp_insn_t);
if (load_addr > ulp_mem_end) {
ESP_LOGW(TAG, "invalid load address %"PRIx32", max is %x",
load_addr, ulp_mem_end);
return ESP_ERR_ULP_INVALID_LOAD_ADDR;
}{...}
if (real_program_size + load_addr > ulp_mem_end) {
ESP_LOGE(TAG, "program too big: %d words, max is %d words",
real_program_size, ulp_mem_end);
return ESP_ERR_ULP_SIZE_TOO_BIG;
}{...}
if (macro_count == 0) {
memcpy(((ulp_insn_t*) RTC_SLOW_MEM) + load_addr, program, *psize * sizeof(ulp_insn_t));
return ESP_OK;
}{...}
reloc_info_t* reloc_info =
(reloc_info_t*) malloc(sizeof(reloc_info_t) * macro_count);
if (reloc_info == NULL) {
return ESP_ERR_NO_MEM;
}{...}
read_ptr = program;
ulp_insn_t* output_program = ((ulp_insn_t*) RTC_SLOW_MEM) + load_addr;
ulp_insn_t* write_ptr = output_program;
uint32_t cur_insn_addr = load_addr;
reloc_info_t* cur_reloc = reloc_info;
while (read_ptr < end) {
ulp_insn_t r_insn = *read_ptr;
if (r_insn.macro.opcode == OPCODE_MACRO) {
switch (r_insn.macro.sub_opcode) {
case SUB_OPCODE_MACRO_LABEL:
*cur_reloc = RELOC_INFO_LABEL(r_insn.macro.label,
cur_insn_addr);
break;...
case SUB_OPCODE_MACRO_BRANCH:
*cur_reloc = RELOC_INFO_BRANCH(r_insn.macro.label,
cur_insn_addr);
break;...
case SUB_OPCODE_MACRO_LABELPC:
*cur_reloc = RELOC_INFO_LABELPC(r_insn.macro.label,
cur_insn_addr);
break;...
default:
assert(0 && "invalid sub_opcode for macro insn");...
}{...}
++read_ptr;
assert(read_ptr != end && "program can not end with macro insn");
++cur_reloc;
}{...} else {
*write_ptr = *read_ptr;
++read_ptr;
++write_ptr;
++cur_insn_addr;
}{...}
}{...}
qsort(reloc_info, macro_count, sizeof(reloc_info_t),
reloc_sort_func);
reloc_info_t* reloc_end = reloc_info + macro_count;
cur_reloc = reloc_info;
while (cur_reloc < reloc_end) {
reloc_info_t label_info = *cur_reloc;
assert(label_info.type == RELOC_TYPE_LABEL);
++cur_reloc;
while (cur_reloc < reloc_end) {
if (cur_reloc->type == RELOC_TYPE_LABEL) {
if (cur_reloc->label == label_info.label) {
ESP_LOGE(TAG, "duplicate label definition: %d",
label_info.label);
free(reloc_info);
return ESP_ERR_ULP_DUPLICATE_LABEL;
}{...}
break;
}{...}
if (cur_reloc->label != label_info.label) {
ESP_LOGE(TAG, "branch to an inexistent label: %d",
cur_reloc->label);
free(reloc_info);
return ESP_ERR_ULP_UNDEFINED_LABEL;
}{...}
esp_err_t rc = do_single_reloc(output_program, load_addr,
label_info, *cur_reloc);
if (rc != ESP_OK) {
free(reloc_info);
return rc;
}{...}
++cur_reloc;
}{...}
}{...}
free(reloc_info);
*psize = real_program_size;
return ESP_OK;
}{ ... }