]> granicus.if.org Git - esp-idf/blob - components/ulp/ulp_macro.c
ethernet: add iperf example to test real bandwidth
[esp-idf] / components / ulp / ulp_macro.c
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 #include <stdio.h>
16 #include <string.h>
17 #include <stdlib.h>
18
19 #include "esp_attr.h"
20 #include "esp_err.h"
21 #include "esp_log.h"
22 #include "esp32/ulp.h"
23
24 #include "soc/soc.h"
25 #include "soc/rtc_cntl_reg.h"
26 #include "soc/sens_reg.h"
27
28 #include "sdkconfig.h"
29
30 static const char* TAG = "ulp";
31
32 typedef struct {
33     uint32_t label : 16;
34     uint32_t addr : 11;
35     uint32_t unused : 1;
36     uint32_t type : 4;
37 } reloc_info_t;
38
39 #define RELOC_TYPE_LABEL   0
40 #define RELOC_TYPE_BRANCH  1
41
42 /* This record means: there is a label at address
43  * insn_addr, with number label_num.
44  */
45 #define RELOC_INFO_LABEL(label_num, insn_addr) (reloc_info_t) { \
46     .label = label_num, \
47     .addr = insn_addr, \
48     .unused = 0, \
49     .type = RELOC_TYPE_LABEL }
50
51 /* This record means: there is a branch instruction at
52  * insn_addr, it needs to be changed to point to address
53  * of label label_num.
54  */
55 #define RELOC_INFO_BRANCH(label_num, insn_addr) (reloc_info_t) { \
56     .label = label_num, \
57     .addr = insn_addr, \
58     .unused = 0, \
59     .type = RELOC_TYPE_BRANCH }
60
61
62 /* Processing branch and label macros involves four steps:
63  *
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
67  *    instructions.
68  *
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
81  *    which were present.
82  *
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
86  *    step.
87  *
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
96  *    the instruction.
97  *
98  */
99
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)
102 {
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) {
110         case SUB_OPCODE_B: {
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;
118             }
119             insn->b.offset = abs_offset;
120             insn->b.sign = sign;
121             break;
122         }
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;
127             break;
128         }
129         default:
130             assert(false && "unexpected sub-opcode");
131     }
132     return ESP_OK;
133 }
134
135 esp_err_t ulp_process_macros_and_load(uint32_t load_addr, const ulp_insn_t* program, size_t* psize)
136 {
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) {
144             ++macro_count;
145         }
146         ++read_ptr;
147     }
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;
154     }
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;
159     }
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));
163         return ESP_OK;
164     }
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;
169     }
170
171     // step 2: record macros into reloc_info array
172     // and remove them from then program
173     read_ptr = 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,
184                             cur_insn_addr);
185                     break;
186                 case SUB_OPCODE_MACRO_BRANCH:
187                     *cur_reloc = RELOC_INFO_BRANCH(r_insn.macro.label,
188                             cur_insn_addr);
189                     break;
190                 default:
191                     assert(0 && "invalid sub_opcode for macro insn");
192             }
193             ++read_ptr;
194             assert(read_ptr != end && "program can not end with macro insn");
195             ++cur_reloc;
196         } else {
197             // normal instruction (not a macro)
198             *write_ptr = *read_ptr;
199             ++read_ptr;
200             ++write_ptr;
201             ++cur_insn_addr;
202         }
203     }
204
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) {
210             return -1;
211         } else if (lhs.label > rhs.label) {
212             return 1;
213         }
214         // label numbers are equal
215         if (lhs.type < rhs.type) {
216             return -1;
217         } else if (lhs.type > rhs.type) {
218             return 1;
219         }
220
221         // both label number and type are equal
222         return 0;
223     }
224     qsort(reloc_info, macro_count, sizeof(reloc_info_t),
225             reloc_sort_func);
226
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);
233         ++cur_reloc;
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",
238                             label_info.label);
239                     free(reloc_info);
240                     return ESP_ERR_ULP_DUPLICATE_LABEL;
241                 }
242                 break;
243             }
244             if (cur_reloc->label != label_info.label) {
245                 ESP_LOGE(TAG, "branch to an inexistent label: %d",
246                         cur_reloc->label);
247                 free(reloc_info);
248                 return ESP_ERR_ULP_UNDEFINED_LABEL;
249             }
250             esp_err_t rc = do_single_reloc(output_program, load_addr,
251                     label_info, *cur_reloc);
252             if (rc != ESP_OK) {
253                 free(reloc_info);
254                 return rc;
255             }
256             ++cur_reloc;
257         }
258     }
259     free(reloc_info);
260     *psize = real_program_size;
261     return ESP_OK;
262 }