Repository: FastLED/FastLED

Issue #1571: On RP2040, multiple calls to addLeds() results in "Error: invalid offset, value too big"
Created: 2023-12-07T20:34:08Z
Compiling any of the examples in the "Multiple" subdirectory results in an error similar to this:
{standard input}: Assembler messages:
{standard input}:1100: Error: invalid offset, value too big (0x00000494)

Commenting out all but one of the calls to addLeds() results in a successful compilation in each case.

Duplicate issue #1481: "Error: invalid offset, value too big" when building for Raspberry Pi Pico (RP2040)
Created: 2023-02-20T23:19:56Z
I've been trying to get FastLED compiling with the PicoSDK for a custom RP2040 based board.
I've got it mostly working but I'm getting what I think is a bug in the m0 clockless assembly implementation.
I was originally working on this build chain in a pico-sdk issue, if you care to look at that.
To summarize:
The main CMakeLists.txt, setup mostly as the pico-sdk prescribes:
cmake_minimum_required(VERSION 3.13)

include(lib/SDK/pico_sdk_init.cmake)

project(my_project)

# initialize the Raspberry Pi Pico SDK
pico_sdk_init()

# TODO: Use `add_subdirectory(src)` instead of the following
add_executable(my_executable
    src/main.cpp
)

target_link_libraries(my_executable FastLED)

target_link_libraries(my_executable pico_stdlib)

target_link_libraries(my_executable
    pico_stdio
    hardware_spi
    # hardware_uart
    hardware_i2c
    hardware_gpio
)
 
add_subdirectory(lib/FastLED)

# create map/bin/hex/uf2 file in addition to ELF.
pico_add_extra_outputs(my_executable)
A lib/FastLED/CMakeLists.txt for the FastLED "INTERFACE" library:
add_library(FastLED INTERFACE)

target_sources(FastLED INTERFACE
    compat/FastLED.cpp
)

# Required pico SDK libraries
target_link_libraries(FastLED INTERFACE
    hardware_sync
    hardware_gpio
    hardware_clocks
    hardware_dma
    hardware_pio
    cmsis_core
    pico_malloc
    pico_time
)

# Ensure our compatiblity header is in the include search path before FastLED's sources
target_include_directories(FastLED INTERFACE
    compat/include
)

target_compile_definitions(FastLED INTERFACE
    # FASTLED_RP2040_CLOCKLESS_PIO=1
)
Notably, since FastLED expects to be built in the Arduino environment, some functions need to be declared (and defined inline).
We can declare and implement what's needed with compat/Adapter.hpp:
#include <stdlib.h>
#include <pico/time.h>
#include <RP2040.h>

// FastLED tests for this to enable RP2040 platform
#define ARDUINO_ARCH_RP2040

static inline uint32_t micros(void) { return to_us_since_boot(get_absolute_time()); }
static inline uint32_t millis(void) { return to_ms_since_boot(get_absolute_time()); }
static inline void yield(void) {};
Note: include guards shouldn't be needed since this should only be included by the two compatibility files
Instead of building the FastLED sources directly with -include compat/Adapter.hpp, I build a single compat/FastLED.cpp that brings in the compatibility layer and all the sources into a single translation unit. This was easier and, imho, cleaner than setting compile flags in CMake for only specific files:
#include "Adapter.hpp"

#undef FASTLED_INTERNAL
#include "../FastLED/src/bitswap.cpp"

#undef FASTLED_INTERNAL
#include "../FastLED/src/colorpalettes.cpp"

#undef FASTLED_INTERNAL
#include "../FastLED/src/colorutils.cpp"

#undef FASTLED_INTERNAL
#include "../FastLED/src/FastLED.cpp"

#undef FASTLED_INTERNAL
#include "../FastLED/src/hsv2rgb.cpp"

#undef FASTLED_INTERNAL
#include "../FastLED/src/lib8tion.cpp"

#undef FASTLED_INTERNAL
#include "../FastLED/src/platforms.cpp"

#undef FASTLED_INTERNAL
#include "../FastLED/src/noise.cpp"

#undef FASTLED_INTERNAL
#include "../FastLED/src/power_mgt.cpp"
When library consumers #include <FastLED.h>, they need to include a shim first. So we make compat/include/FastLED.h:
#pragma once

// This file is used to include the FastLED library in a way that is compatible with the Pico SDK

// Include the compability layer
#include "../Adapter.hpp"

// Prevent warnings about SPI support
#define FASTLED_INTERNAL

#include "../../FastLED/src/FastLED.h"

Onto the issue at hand.
After setting up CMake to build FastLED, I'm getting an error whenever I include FastLED.addLeds<NEOPIXEL, 6>(leds, NUM_LEDS); in my main():
/usr/bin/arm-none-eabi-g++ -DLIB_CMSIS_CORE=1 -DLIB_PICO_BIT_OPS=1 -DLIB_PICO_BIT_OPS_PICO=1 -DLIB_PICO_DIVIDER=1 -DLIB_PICO_DIVIDER_HARDWARE=1 -DLIB_PICO_DOUBLE=1 -DLIB_PICO_DOUBLE_PICO=1 -DLIB_PICO_FLOAT=1 -DLIB_PICO_FLOAT_PICO=1 -DLIB_PICO_INT64_OPS=1 -DLIB_PICO_INT64_OPS_PICO=1 -DLIB_PICO_MALLOC=1 -DLIB_PICO_MEM_OPS=1 -DLIB_PICO_MEM_OPS_PICO=1 -DLIB_PICO_PLATFORM=1 -DLIB_PICO_PRINTF=1 -DLIB_PICO_PRINTF_PICO=1 -DLIB_PICO_RUNTIME=1 -DLIB_PICO_STANDARD_LINK=1 -DLIB_PICO_STDIO=1 -DLIB_PICO_STDIO_UART=1 -DLIB_PICO_STDLIB=1 -DLIB_PICO_SYNC=1 -DLIB_PICO_SYNC_CRITICAL_SECTION=1 -DLIB_PICO_SYNC_MUTEX=1 -DLIB_PICO_SYNC_SEM=1 -DLIB_PICO_TIME=1 -DLIB_PICO_UTIL=1 -DPICO_BOARD=\"pico\" -DPICO_BUILD=1 -DPICO_CMAKE_BUILD_TYPE=\"Release\" -DPICO_COPY_TO_RAM=0 -DPICO_CXX_ENABLE_EXCEPTIONS=0 -DPICO_NO_FLASH=0 -DPICO_NO_HARDWARE=0 -DPICO_ON_DEVICE=1 -DPICO_TARGET_NAME=\"my_executable\" -DPICO_USE_BLOCKED_RAM=0 -I../lib/SDK/src/common/pico_stdlib/include -I../lib/SDK/src/rp2_common/hardware_gpio/include -I../lib/SDK/src/common/pico_base/include -Igenerated/pico_base -I../lib/SDK/src/boards/include -I../lib/SDK/src/rp2_common/pico_platform/include -I../lib/SDK/src/rp2040/hardware_regs/include -I../lib/SDK/src/rp2_common/hardware_base/include -I../lib/SDK/src/rp2040/hardware_structs/include -I../lib/SDK/src/rp2_common/hardware_claim/include -I../lib/SDK/src/rp2_common/hardware_sync/include -I../lib/SDK/src/rp2_common/hardware_irq/include -I../lib/SDK/src/common/pico_sync/include -I../lib/SDK/src/common/pico_time/include -I../lib/SDK/src/rp2_common/hardware_timer/include -I../lib/SDK/src/common/pico_util/include -I../lib/SDK/src/rp2_common/hardware_uart/include -I../lib/SDK/src/rp2_common/hardware_resets/include -I../lib/SDK/src/rp2_common/hardware_clocks/include -I../lib/SDK/src/rp2_common/hardware_pll/include -I../lib/SDK/src/rp2_common/hardware_vreg/include -I../lib/SDK/src/rp2_common/hardware_watchdog/include -I../lib/SDK/src/rp2_common/hardware_xosc/include -I../lib/SDK/src/rp2_common/hardware_divider/include -I../lib/SDK/src/rp2_common/pico_runtime/include -I../lib/SDK/src/rp2_common/pico_printf/include -I../lib/SDK/src/common/pico_bit_ops/include -I../lib/SDK/src/common/pico_divider/include -I../lib/SDK/src/rp2_common/pico_double/include -I../lib/SDK/src/rp2_common/pico_float/include -I../lib/SDK/src/rp2_common/pico_malloc/include -I../lib/SDK/src/rp2_common/pico_bootrom/include -I../lib/SDK/src/common/pico_binary_info/include -I../lib/SDK/src/rp2_common/pico_stdio/include -I../lib/SDK/src/rp2_common/pico_stdio_uart/include -I../lib/SDK/src/rp2_common/pico_int64_ops/include -I../lib/SDK/src/rp2_common/pico_mem_ops/include -I../lib/SDK/src/rp2_common/boot_stage2/include -I../lib/SDK/src/rp2_common/hardware_spi/include -I../lib/SDK/src/rp2_common/hardware_i2c/include -I../lib/FastLED/compat -I../lib/FastLED/FastLED/src -I../lib/SDK/src/rp2_common/hardware_dma/include -I../lib/SDK/src/rp2_common/hardware_pio/include -I../lib/SDK/src/rp2_common/cmsis/stub/CMSIS/Core/Include -I../lib/SDK/src/rp2_common/cmsis/stub/CMSIS/Device/RaspberryPi/RP2040/Include -mcpu=cortex-m0plus -mthumb -O3 -DNDEBUG -ffunction-sections -fdata-sections -fno-exceptions -fno-unwind-tables -fno-rtti -fno-use-cxa-atexit -MD -MT CMakeFiles/my_executable.dir/src/main.cpp.obj -MF CMakeFiles/my_executable.dir/src/main.cpp.obj.d -o CMakeFiles/my_executable.dir/src/main.cpp.obj -c ../src/main.cpp
/tmp/ccasthV7.s: Assembler messages:
/tmp/ccasthV7.s:812: Error: invalid offset, value too big (0x00000454)

With --save-temps I'm able to see the intermediate .s file that is being compiled and can inspect around line 812:
; ...
	.syntax divided
@ 279 "../lib/FastLED/compat/../FastLED/src/platforms/arm/rp2040/../common/m0clockless.h" 1
	  loadleds3 ip, r4, #1 ,r3;  loaddither7 r4, r5, r6, #1;  dither5 r4, r5;  scale4 r4, r6, #12, r3;  adjdither7 r6,r5,#1,#4,r3;  swapbbn1 r2, r4;
@ 0 "" 2
	.thumb
	.syntax unified
	ldr	r6, .L131                     ; <------- Line 812: Error: invalid offset, value too big
	movs	r0, r2
	str	r6, [sp, #8]
	ldr	r6, [r6]
	mov	r2, ip
	str	r6, [sp]
	b	.L124
.L130:
	cmp	r1, #0
	bne	.LCB824
	b	.L129	@long jump
.LCB824:
.L124:
	mov	r10, r2
	mov	r7, r8
	ldr	r2, [sp, #4]
	mov	r6, r9
	.syntax divided
; ...
This tells me something from m0clockless.h isn't generating valid assembly output...
Looking at that code, I'm not very familiar with ARM assembly and marcos, so I'm not sure what's going on.
In any case, is this a FastLED bug? Or am I doing something stupid? Or is this a "won't fix" because this isn't Arduino?

Closing comment (2023-12-16T15:18:58Z): Having the same issue using arduino-pico building with PlatformIO (same as #1571), I am using -O3 optimizations, and I'm only trying to add a single LED strip:
CRGB leds[72];
...
FastLED.addLeds<NEOPIXEL, 7>(leds, 72);

I would rather not use -Os, and I don't even want this assembly code to be built into my firmware because I'm planning to use the RP2040 clockless controller that uses PIO and DMA.
I would've hoped/expected that it would not compile in the common ARM implementation, if I'm using #define FASTLED_RP2040_CLOCKLESS_PIO 1