| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297 |
- // Copyright 2010-2016 Espressif Systems (Shanghai) PTE LTD
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
- #include "esp_attr.h"
- #include "esp_err.h"
- #include "esp_log.h"
- #include "esp32/ulp.h"
- #include "ulp_private.h"
- #include "soc/soc.h"
- #include "soc/rtc_cntl_reg.h"
- #include "soc/sens_reg.h"
- #include "sdkconfig.h"
- 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
- /* This record means: there is a label at address
- * insn_addr, with number label_num.
- */
- #define RELOC_INFO_LABEL(label_num, insn_addr) (reloc_info_t) { \
- .label = label_num, \
- .addr = insn_addr, \
- .unused = 0, \
- .type = RELOC_TYPE_LABEL }
- /* This record means: there is a branch instruction at
- * insn_addr, it needs to be changed to point to address
- * of label label_num.
- */
- #define RELOC_INFO_BRANCH(label_num, insn_addr) (reloc_info_t) { \
- .label = label_num, \
- .addr = insn_addr, \
- .unused = 0, \
- .type = RELOC_TYPE_BRANCH }
- /* This record means: there is a move instruction at insn_addr,
- * imm needs to be changed to the program counter of the instruction
- * at label label_num.
- */
- #define RELOC_INFO_LABELPC(label_num, insn_addr) (reloc_info_t) { \
- .label = label_num, \
- .addr = insn_addr, \
- .unused = 0, \
- .type = RELOC_TYPE_LABELPC }
- /* Comparison function used to sort the relocations array */
- 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;
- }
- // label numbers are equal
- if (lhs.type < rhs.type) {
- return -1;
- } else if (lhs.type > rhs.type) {
- return 1;
- }
- // both label number and type are equal
- return 0;
- }
- /* Processing branch and label macros involves four steps:
- *
- * 1. Iterate over program and count all instructions
- * with "macro" opcode. Allocate relocations array
- * with number of entries equal to number of macro
- * instructions.
- *
- * 2. Remove all fake instructions with "macro" opcode
- * and record their locations into relocations array.
- * Removal is done using two pointers. Instructions
- * are read from read_ptr, and written to write_ptr.
- * When a macro instruction is encountered,
- * its contents are recorded into the appropriate
- * table, and then read_ptr is advanced again.
- * When a real instruction is encountered, it is
- * read via read_ptr and written to write_ptr.
- * In the end, all macro instructions are removed,
- * size of the program (expressed in words) is
- * reduced by the total number of macro instructions
- * which were present.
- *
- * 3. Sort relocations array by label number, and then
- * by type ("label" or "branch") if label numbers
- * match. This is done to simplify lookup on the next
- * step.
- *
- * 4. Iterate over entries of relocations table.
- * For each label number, label entry comes first
- * because the array was sorted at the previous step.
- * Label address is recorded, and all subsequent
- * entries which point to the same label number
- * are processed. For each entry, correct offset
- * or absolute address is calculated, depending on
- * type and subtype, and written into the appropriate
- * field of the instruction.
- *
- */
- 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: {
- // B, BS and BX have the same layout of opcode/sub_opcode fields,
- // and share the same opcode. B and BS also have the same layout of
- // offset and sign fields.
- 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->bs.offset = abs_offset;
- insn->b.sign = sign; //== insn->bs.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;
- // step 1: calculate number of macros
- 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 = ULP_RESERVE_MEM / sizeof(ulp_insn_t);
- if (load_addr > ulp_mem_end) {
- ESP_LOGW(TAG, "invalid load address %x, 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 no macros found, copy the program and return.
- 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;
- }
- // step 2: record macros into reloc_info array
- // and remove them from then program
- 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 {
- // normal instruction (not a macro)
- *write_ptr = *read_ptr;
- ++read_ptr;
- ++write_ptr;
- ++cur_insn_addr;
- }
- }
- // step 3: sort relocations array
- qsort(reloc_info, macro_count, sizeof(reloc_info_t),
- reloc_sort_func);
- // step 4: walk relocations array and fix instructions
- 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;
- }
|