ESP32: как сгенерировать код ассемблера из кода C |
![]() |
Добавил(а) microsin |
Компилятор riscv32-esp-elf-gcc умеет генерировать ассемблера, если указать ему опцию -S. Однако в реальном проекте кроме опции -S компилятору нужно указать еще много чего - где искать заголовочные файлы, указать тип кристалла, тип оптимизации и т. п. Предположим, что Вы хотите оптимизировать какой-нибудь обработчик прерывания путем правки его кода на ассемблере. Для этой цели надо из кода C получить код ассемблера. Как это сделать, процесс по шагам: 1. Создайте для оптимизируемого кода отдельный модуль на языке C. Уберите из него по максимуму все лишнее, чтобы потом было проще разобраться. Добейтесь, чтобы этот модуль нормально компилировался в составе проекта. 2. Внесите в этот модуль специально какую-либо ошибку. Это можно сделать с помощью директивы #error, например: #include "esp_attr.h"
#include "driver/timer.h"
#include "driver/ledc.h"
#include "soundISR.h"
#include "pins.h"
volatile TPlayState soundmode = PLAY_IDLE; volatile int32_t wavidx; int32_t wavlength;
uint8_t *pstart; static void pwmduty(uint8_t v8) { ledc_set_duty(LEDC_MODE, LEDC_CHANNEL, v8); ledc_update_duty(LEDC_MODE, LEDC_CHANNEL); } /* Обработчик прерывания таймера */
void IRAM_ATTR timer0_ISR(void *ptr) { #error This is dummy error. TESTLED(1); *(uint32_t*)(TIMG_INT_CLR_TIMERS_REG(TIMER_GROUP)) = 1; *(uint32_t*)(TIMG_T0CONFIG_REG(TIMER_GROUP)) |= TIMG_T0_ALARM_EN; if (PLAY_FILE != soundmode) return; if(wavidx < wavlength) { pwmduty(pstart[wavidx]); wavidx++; } else { soundmode = PLAY_IDLE; } TESTLED(0); } 3. Скомпилируйте проект. Компилятор увидит ошибку, и вместе с выводом ошибки выдаст полную командную строку, с которой модуль компилировался. Например, если компилируется модуль soundISR.c, то получится вывод наподобие следующего: /home/имяпользователя/.espressif/tools/riscv32-esp-elf/esp-2021r2-patch3-8.4.0/riscv32-esp-elf/bin/riscv32-esp-elf-gcc
-DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\" -Iconfig -I/home/имяпользователя/каталогпроекта
-I/home/имяпользователя/каталогпроекта/include -I/home/имяпользователя/esp/esp-idf/components/newlib/platform_include
-I/home/имяпользователя/esp/esp-idf/components/freertos/include/esp_additions/freertos
-I/home/имяпользователя/esp/esp-idf/components/freertos/include
.. другие пути поиска подключаемых файлов ..
-march=rv32imc -ffunction-sections -fdata-sections
-Wall -Werror=all -Wno-error=unused-function -Wno-error=unused-variable -Wno-error=deprecated-declarations -Wextra
-Wno-unused-parameter -Wno-sign-compare -ggdb -Wno-error=format= -nostartfiles -Wno-format
-O2
-fmacro-prefix-map=/home/имяпользователя/каталогпроекта=.
-fmacro-prefix-map=/home/имяпользователя/esp/esp-idf=IDF -fstrict-volatile-bitfields
-Wno-error=unused-but-set-variable -fno-jump-tables -fno-tree-switch-conversion
-std=gnu99 -Wno-old-style-declaration -D_GNU_SOURCE -DIDF_VER=\"v4.4.1-dirty\"
-DESP_PLATFORM -D_POSIX_READER_WRITER_LOCKS -MD -MT esp-idf/espidf/CMakeFiles/__idf_espidf.dir/__/src/soundISR.c.obj
-MF esp-idf/espidf/CMakeFiles/__idf_espidf.dir/__/src/soundISR.c.obj.d
-o esp-idf/espidf/CMakeFiles/__idf_espidf.dir/__/src/soundISR.c.obj
-c /home/имяпользователя/каталогпроекта/src/soundISR.c
/home/имяпользователя/каталогпроекта/src/soundISR.c: In function 'timer0_ISR':
/home/имяпользователя/каталогпроекта/src/soundISR.c:21:2: error: #error This is dummy error.
#error This is dummy error.
^~~~~
Теперь нам остается только немного модифицировать эту командную строку, чтобы добавить опцию -S и имя выходного генерируемого файла ассемблера. 4. Чтобы не вбивать каждый раз огромную командную строку для компилятора, целесообразно для компиляции отдельного модуля создать специальный bash-скрипт, примерно такой: #!/bin/bash
# Файл скрипта toasm.sh, предназначенный для генерации кода ассемблера
# из модуля исходного кода на языке C. Как использовать:
#
# 1. Измените переменные GCC, HC, PRJFOLDER, BUILD, чтобы они соответствовали вашей
# рабочей среде компиляции.
# 2. Для генерации кода ассемблера (*.S) из кода C (*.c) запустите скрипт командой:
# $ ./toasm.sh имямодуля.c
#
# Здесь вместо имямодуля.c может быть полный или относительный путь до компилируемого
# модуля на языке C. Файл на языке ассемблера появится в том же каталоге.
GCC=/.espressif/tools/riscv32-esp-elf/esp-2021r2-patch3-8.4.0/riscv32-esp-elf/bin/riscv32-esp-elf-gcc HC=/home/имяпользователя PRJFOLDER=/каталогпроекта BUILD=$HC/каталогпроекта/build/esp-idf/espidf/CMakeFiles/__idf_espidf.dir/__/src INC=-I$HC/каталогпроекта/build/config INC="$INC -I$HC$PRJFOLDER" INC="$INC -I$HC$PRJFOLDER/include" INC="$INC -I$HC/esp/esp-idf/components/newlib/platform_include" INC="$INC -I$HC/esp/esp-idf/components/freertos/include" INC="$INC -I$HC/esp/esp-idf/components/freertos/include/esp_additions" INC="$INC -I$HC/esp/esp-idf/components/freertos/include/esp_additions/freertos" INC="$INC -I$HC/esp/esp-idf/components/freertos/port/riscv/include" INC="$INC -I$HC/esp/esp-idf/components/esp_hw_support/include" INC="$INC -I$HC/esp/esp-idf/components/esp_hw_support/include/soc" INC="$INC -I$HC/esp/esp-idf/components/esp_hw_support/include/soc/esp32c3" INC="$INC -I$HC/esp/esp-idf/components/esp_hw_support/port/esp32c3/." INC="$INC -I$HC/esp/esp-idf/components/esp_hw_support/port/esp32c3/private_include" INC="$INC -I$HC/esp/esp-idf/components/heap/include" INC="$INC -I$HC/esp/esp-idf/components/log/include" INC="$INC -I$HC/esp/esp-idf/components/lwip/include/apps" INC="$INC -I$HC/esp/esp-idf/components/lwip/include/apps/sntp" INC="$INC -I$HC/esp/esp-idf/components/lwip/lwip/src/include" INC="$INC -I$HC/esp/esp-idf/components/lwip/port/esp32/include" INC="$INC -I$HC/esp/esp-idf/components/lwip/port/esp32/include/arch" INC="$INC -I$HC/esp/esp-idf/components/soc/include" INC="$INC -I$HC/esp/esp-idf/components/soc/esp32c3/." INC="$INC -I$HC/esp/esp-idf/components/soc/esp32c3/include" INC="$INC -I$HC/esp/esp-idf/components/hal/esp32c3/include" INC="$INC -I$HC/esp/esp-idf/components/hal/include" INC="$INC -I$HC/esp/esp-idf/components/hal/platform_port/include" INC="$INC -I$HC/esp/esp-idf/components/esp_rom/include" INC="$INC -I$HC/esp/esp-idf/components/esp_rom/include/esp32c3" INC="$INC -I$HC/esp/esp-idf/components/esp_rom/esp32c3" INC="$INC -I$HC/esp/esp-idf/components/esp_common/include" INC="$INC -I$HC/esp/esp-idf/components/esp_system/include" INC="$INC -I$HC/esp/esp-idf/components/esp_system/port/soc" INC="$INC -I$HC/esp/esp-idf/components/esp_system/port/include/riscv" INC="$INC -I$HC/esp/esp-idf/components/esp_system/port/public_compat" INC="$INC -I$HC/esp/esp-idf/components/riscv/include" INC="$INC -I$HC/esp/esp-idf/components/driver/include" INC="$INC -I$HC/esp/esp-idf/components/driver/esp32c3/include" INC="$INC -I$HC/esp/esp-idf/components/esp_pm/include" INC="$INC -I$HC/esp/esp-idf/components/esp_ringbuf/include" INC="$INC -I$HC/esp/esp-idf/components/efuse/include" INC="$INC -I$HC/esp/esp-idf/components/efuse/esp32c3/include" INC="$INC -I$HC/esp/esp-idf/components/vfs/include" INC="$INC -I$HC/esp/esp-idf/components/esp_wifi/include" INC="$INC -I$HC/esp/esp-idf/components/esp_event/include" INC="$INC -I$HC/esp/esp-idf/components/esp_netif/include" INC="$INC -I$HC/esp/esp-idf/components/tcpip_adapter/include" INC="$INC -I$HC/esp/esp-idf/components/esp_phy/include" INC="$INC -I$HC/esp/esp-idf/components/esp_phy/esp32c3/include" INC="$INC -I$HC/esp/esp-idf/components/esp_ipc/include" INC="$INC -I$HC/esp/esp-idf/components/app_trace/include" INC="$INC -I$HC/esp/esp-idf/components/esp_timer/include" INC="$INC -I$HC/esp/esp-idf/components/esp-tls" INC="$INC -I$HC/esp/esp-idf/components/esp-tls/esp-tls-crypto" INC="$INC -I$HC/esp/esp-idf/components/mbedtls/port/include" INC="$INC -I$HC/esp/esp-idf/components/mbedtls/mbedtls/include" INC="$INC -I$HC/esp/esp-idf/components/mbedtls/esp_crt_bundle/include" INC="$INC -I$HC/esp/esp-idf/components/nvs_flash/include" INC="$INC -I$HC/esp/esp-idf/components/spi_flash/include" WARN=-Wall WARN="$WARN -Werror=all" WARN="$WARN -Wno-error=unused-function" WARN="$WARN -Wno-error=unused-variable" WARN="$WARN -Wno-error=deprecated-declarations" WARN="$WARN -Wextra" WARN="$WARN -Wno-unused-parameter" WARN="$WARN -Wno-sign-compare" WARN="$WARN -Wno-error=format=" WARN="$WARN -Wno-format" WARN="$WARN -Wno-error=unused-but-set-variable" WARN="$WARN -Wno-old-style-declaration" OUTPUTASM=$1.S rm $BUILD/*.*$GCC -DMBEDTLS_CONFIG_FILE=\"mbedtls/esp_config.h\" $INC -march=rv32imc -ffunction-sections -fdata-sections $WARN -nostartfiles -O2 -fmacro-prefix-map=$HC$PRJFOLDER=. -fmacro-prefix-map=$HC/esp/esp-idf=IDF -fstrict-volatile-bitfields -fno-jump-tables -fno-tree-switch-conversion -std=gnu99 -D_GNU_SOURCE -DIDF_VER=\"v4.4.1-dirty\" -DESP_PLATFORM -D_POSIX_READER_WRITER_LOCKS -MD -c $1 -o $OUTPUTASM -S . rm $1.d
5. Запустите скрипт командой: $ ./toasm.sh путьдомодуля/имямодуля.C
В результате в файле путьдомодуля/имямодуля.S будет сгенерирован код на ассемблере. 6. Теперь можно изменять по своему усмотрению код ассемблера имямодуля.S. Чтобы он использовался в проекте вместо имямодуля.C, подправьте список компилируемых модулей проекта. Например, если для компиляции Вы используете скрипт idf.py, то нужно исправить содержимое файла sources.cmake или CMakeLists.txt проекта. Например, надо в файле CMakeLists.txt заменить soundISR.c на soundISR.c.S: idf_component_register(REQUIRES "driver" "console" "cmd_system" "cmd_nvs" "esp_wifi" "fatfs" SRCS "cmd_wifi.c" "console_example_main.c" "OneButton.c" #"soundISR.c" "soundISR.c.S" "pwm.c" "wav.c" "sleep.c" "../esp_idf_components/console/commands.c" "cmd_system.c" EMBED_FILES ../wav/32k8bit.wav ../wav/optan.wav INCLUDE_DIRS ".") [Ссылки] 1. GCC online documentation site:gcc.gnu.org. |