Splitting ULP ASM files

MingShun
Posts: 3
Joined: Wed Sep 11, 2019 5:16 am

Splitting ULP ASM files

Postby MingShun » Thu Sep 12, 2019 4:15 am

Because it looked like a better way to do things, I want to use the new idf.py build system from ESP IDF 4.0 so I've been converting my code. I ran into a problem when trying to compile the assembly code which has been broken up into multiple sections for maintainability. It doesn't compile like it did under the legacy make system.

I'm going to outline a way to reproduce what I am up to. First I started with the ulp example in the esp-idf directory. Then I open up pulse_cnt.S, cut the wake_up label from the bottom and paste it into a new file called library1.S. Then I'll edit the entry in CMakeLists.txt and proceed to compile.

pulse_cnt.S (*abbreviated for brevity, cannot be directly compiled)

Code: Select all

#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/soc_ulp.h"

	/* Define variables, which go into .bss section (zero-initialized data) */
	.bss
	/* Next input signal edge expected: 0 (negative) or 1 (positive) */
	.global next_edge
next_edge:
	.long 0

.
.
.

	.global edge_detected
edge_detected:
	/* Reset debounce_counter to debounce_max_count */
	move r3, debounce_max_count
	move r2, debounce_counter
	ld r3, r3, 0
	st r3, r2, 0
	/* Flip next_edge */
	move r3, next_edge
	ld r2, r3, 0
	add r2, r2, 1
	and r2, r2, 1
	st r2, r3, 0
	/* Increment edge_count */
	move r3, edge_count
	ld r2, r3, 0
	add r2, r2, 1
	st r2, r3, 0
	/* Compare edge_count to edge_count_to_wake_up */
	move r3, edge_count_to_wake_up
	ld r3, r3, 0
	sub r3, r3, r2
	jump wake_up, eq
	/* Not yet. End program */
	halt
library1.S

Code: Select all

	
#include "soc/rtc_cntl_reg.h"
#include "soc/rtc_io_reg.h"
#include "soc/soc_ulp.h"

	.global wake_up
wake_up:
	/* Check if the system can be woken up */
	READ_RTC_FIELD(RTC_CNTL_LOW_POWER_ST_REG, RTC_CNTL_RDY_FOR_WAKEUP)
	and r0, r0, 1
	jump wake_up, eq

	/* Wake up the SoC, end program */
	wake
	halt
Modified CMakeLists.txt

Code: Select all

idf_component_register(SRCS "ulp_example_main.c"
                    INCLUDE_DIRS ""
                    REQUIRES soc nvs_flash ulp)
#
# ULP support additions to component CMakeLists.txt.
#
# 1. The ULP app name must be unique (if multiple components use ULP).
set(ulp_app_name ulp_${COMPONENT_NAME})
#
# 2. Specify all assembly source files.
#    Files should be placed into a separate directory (in this case, ulp/),
#    which should not be added to COMPONENT_SRCS.
set(ulp_s_sources  "ulp/library1.S" "ulp/pulse_cnt.S")
#
# 3. List all the component source files which include automatically
#    generated ULP export file, ${ulp_app_name}.h:
set(ulp_exp_dep_srcs "ulp_example_main.c")
#
# 4. Call function to build ULP binary and embed in project using the argument
#    values above.
ulp_embed_binary(${ulp_app_name} ${ulp_s_sources} ${ulp_exp_dep_srcs})
It doesn't compile due to missing dependencies. That being said, when I check the ulp_main.map and ulp_main.h file, it does not look like the code for pulse_cnt.S was included.

ulp_main.h

Code: Select all

// Variable definitions for ESP32ULP
// This file is generated automatically by esp32ulp_mapgen.py utility

#pragma once

extern uint32_t ulp_wake_up;
ulp_main.map

Code: Select all

Memory Configuration

Name             Origin             Length             Attributes
ram              0x0000000000000000 0x0000000000000400 rw
*default*        0x0000000000000000 0xffffffffffffffff

Linker script and memory map

LOAD CMakeFiles/ulp_main.dir/library1.ulp.S.obj

.text           0x0000000000000000       0x14 load address 0x000000000000000c
 *(.text)
 .text          0x0000000000000000       0x14 CMakeFiles/ulp_main.dir/library1.ulp.S.obj
                0x0000000000000000                wake_up

.data           0x0000000000000014        0x0 load address 0x0000000000000020
                0x0000000000000014                . = ALIGN (0x4)
 *(.data)
 .data          0x0000000000000014        0x0 CMakeFiles/ulp_main.dir/library1.ulp.S.obj

.bss            0x0000000000000014        0x0 load address 0x0000000000000020
                0x0000000000000014                . = ALIGN (0x4)
 *(.bss)
 .bss           0x0000000000000014        0x0 CMakeFiles/ulp_main.dir/library1.ulp.S.obj

.header         0x0000000000000014        0xc load address 0x0000000000000000
                0x0000000000000014        0x4 LONG 0x706c75
                0x0000000000000018        0x2 SHORT 0xc LOADADDR (.text)
                0x000000000000001a        0x2 SHORT 0x14 SIZEOF (.text)
                0x000000000000001c        0x2 SHORT 0x0 SIZEOF (.data)
                0x000000000000001e        0x2 SHORT 0x0 SIZEOF (.bss)
OUTPUT(ulp_main elf32-esp32ulp)
I've tried multiple variations to ulp_s_sources in CMakeLists.txt ranging from sharing a string, to removing the double quotes, to separating by semicolon but the results are the same. Is there a bug and the toolchain does not properly handle multiple .S files unless you use #include, or did I misconfigure something?
Last edited by MingShun on Fri Sep 13, 2019 3:13 am, edited 1 time in total.

MingShun
Posts: 3
Joined: Wed Sep 11, 2019 5:16 am

Re: Splitting ULP ASM files

Postby MingShun » Fri Sep 13, 2019 3:11 am

After further research, I've come to the conclusion that the current code is bugged.

1. After doing a google search for "ulp_embed_binary", I came across this code which I subsequently used to replace ulp_embed_binary line in my CMakeLists.txt file:

Code: Select all

spaces2list(s_sources)
        foreach(source ${s_sources})
            get_filename_component(source ${source} ABSOLUTE BASE_DIR ${CMAKE_CURRENT_LIST_DIR})
            list(APPEND sources ${source})
        endforeach()

        foreach(source ${sources})
            get_filename_component(ps_source ${source} NAME_WE)
            set(ps_output ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${ps_source}.ulp.S)
            list(APPEND ps_sources ${ps_output})
        endforeach()

        set(ulp_artifacts_prefix ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name})

        set(ulp_artifacts ${ulp_artifacts_prefix}.bin
                            ${ulp_artifacts_prefix}.ld
                            ${ulp_artifacts_prefix}.h)

        set(ulp_artifacts_extras ${ulp_artifacts_prefix}.map
                            ${ulp_artifacts_prefix}.sym
                            ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/esp32.ulp.ld)

        # Replace the separator for the list of ULP source files that will be passed to
        # the external ULP project. This is a workaround to the bug https://public.kitware.com/Bug/view.php?id=16137.
        string(REPLACE ";" "|" ulp_s_sources "${ulp_s_sources}")

        idf_build_get_property(sdkconfig_header SDKCONFIG_HEADER)
        idf_build_get_property(idf_path IDF_PATH)
        idf_build_get_property(python PYTHON)
        externalproject_add(${app_name}
            SOURCE_DIR ${idf_path}/components/ulp/cmake
            BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${app_name}
            INSTALL_COMMAND ""
            CMAKE_ARGS  -DCMAKE_GENERATOR=${CMAKE_GENERATOR}
                        -DCMAKE_TOOLCHAIN_FILE=${idf_path}/components/ulp/cmake/toolchain-ulp.cmake
                        -DULP_S_SOURCES=${sources} -DULP_APP_NAME=${app_name}
                        -DCOMPONENT_DIR=${COMPONENT_DIR}
                        # Even though this resolves to a ';' separated list, this is fine. This must be special behavior
                        # for generator expressions.
                        -DCOMPONENT_INCLUDES=$<TARGET_PROPERTY:${COMPONENT_TARGET},INTERFACE_INCLUDE_DIRECTORIES>
                        -DIDF_PATH=${idf_path}
                        -DSDKCONFIG=${SDKCONFIG_HEADER}
            BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/${app_name} --target build
            BUILD_BYPRODUCTS ${ulp_artifacts} ${ulp_artifacts_extras} ${ulp_ps_sources}
                            ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}
            BUILD_ALWAYS 1
            LIST_SEPARATOR |
            )

        spaces2list(exp_dep_srcs)
        set_source_files_properties(${exp_dep_srcs} PROPERTIES OBJECT_DEPENDS ${ulp_artifacts})

        include_directories(${CMAKE_CURRENT_BINARY_DIR}/${app_name})

        add_custom_target(${app_name}_artifacts DEPENDS ${app_name})

        add_dependencies(${COMPONENT_LIB} ${app_name}_artifacts)

        target_linker_script(${COMPONENT_LIB} INTERFACE ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}.ld)
        target_add_binary_data(${COMPONENT_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}.bin BINARY)
- Extracted from https://github.com/espressif/esp-idf/bl ... lude.cmake

2. Because I unrolled the function call, I had to cast the original argument names into the ulp_embed_binary parameter names:

Code: Select all

set(s_sources ${ulp_s_sources})
set(app_name ${ulp_app_name})
set(exp_dep_srcs ${ulp_exp_dep_src})
3. I then replaced "sources" with "ulp_s_sources" in this line:
-DULP_S_SOURCES=${ulp_s_sources} -DULP_APP_NAME=${app_name}

4. and changed set(ulp_s_sources ...) to this:

Code: Select all

set(ulp_s_sources
/home/user1/esp/projects/ulp-test/main/ulp/pulse_cnt.S
/home/user1/esp/projects/ulp-test/main/ulp/library1.S)
And now, instead of just the first file, it looks like I can compile and link both asm files.

Code: Select all

Memory Configuration

Name             Origin             Length             Attributes
ram              0x0000000000000000 0x0000000000000400 rw
*default*        0x0000000000000000 0xffffffffffffffff

Linker script and memory map

LOAD CMakeFiles/ulp_main.dir/pulse_cnt.ulp.S.obj
LOAD CMakeFiles/ulp_main.dir/library1.ulp.S.obj

.text           0x0000000000000000       0xcc load address 0x000000000000000c
 *(.text)
 .text          0x0000000000000000       0xb8 CMakeFiles/ulp_main.dir/pulse_cnt.ulp.S.obj
                0x0000000000000000                entry
                0x0000000000000054                changed
                0x0000000000000070                edge_detected
 .text          0x00000000000000b8       0x14 CMakeFiles/ulp_main.dir/library1.ulp.S.obj
                0x00000000000000b8                wake_up

.data           0x00000000000000cc        0x0 load address 0x00000000000000d8
                0x00000000000000cc                . = ALIGN (0x4)
 *(.data)
 .data          0x00000000000000cc        0x0 CMakeFiles/ulp_main.dir/pulse_cnt.ulp.S.obj
 .data          0x00000000000000cc        0x0 CMakeFiles/ulp_main.dir/library1.ulp.S.obj

.bss            0x00000000000000cc       0x1c load address 0x00000000000000d8
                0x00000000000000cc                . = ALIGN (0x4)
 *(.bss)
 .bss           0x00000000000000cc       0x18 CMakeFiles/ulp_main.dir/pulse_cnt.ulp.S.obj
                0x00000000000000cc                next_edge
                0x00000000000000d0                debounce_counter
                0x00000000000000d4                debounce_max_count
                0x00000000000000d8                edge_count
                0x00000000000000dc                edge_count_to_wake_up
                0x00000000000000e0                io_number
 .bss           0x00000000000000e4        0x4 CMakeFiles/ulp_main.dir/library1.ulp.S.obj
                0x00000000000000e4                veml_counter

.header         0x00000000000000e8        0xc load address 0x0000000000000000
                0x00000000000000e8        0x4 LONG 0x706c75
                0x00000000000000ec        0x2 SHORT 0xc LOADADDR (.text)
                0x00000000000000ee        0x2 SHORT 0xcc SIZEOF (.text)
                0x00000000000000f0        0x2 SHORT 0x0 SIZEOF (.data)
                0x00000000000000f2        0x2 SHORT 0x1c SIZEOF (.bss)
OUTPUT(ulp_main elf32-esp32ulp)

Who is online

Users browsing this forum: Majestic-12 [Bot] and 73 guests