Skip to content

Commit d2ae4c0

Browse files
committed
Formalize dynarray system
Break out the dynamic array macros to a re-usable component and fix the implementation.
1 parent 2c76668 commit d2ae4c0

File tree

8 files changed

+93
-47
lines changed

8 files changed

+93
-47
lines changed

pio-scripts/dynarray.py

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
# Add a section to the linker script to store our dynamic arrays
2-
# This is implemented as a pio post-script to ensure that our extra linker
3-
# script fragments are processed last, after the base platform scripts have
4-
# been loaded and all sections defined.
2+
# This is implemented as a pio post-script to ensure that we can
3+
# place our linker script at the correct point in the command arguments.
54
Import("env")
5+
from pathlib import Path
66

7-
if env.get("PIOPLATFORM") == "espressif8266":
8-
# Use a shim on this platform so we can share the same output blocks
9-
# names as used by later platforms (ESP32)
10-
env.Append(LINKFLAGS=["-Ttools/esp8266_rodata.ld"])
11-
12-
env.Append(LINKFLAGS=["-Ttools/dynarray.ld"])
7+
platform = env.get("PIOPLATFORM")
8+
script_file = Path(f"tools/dynarray_{platform}.ld")
9+
if script_file.is_file():
10+
linker_script = f"-T{script_file}"
11+
if platform == "espressif32":
12+
# For ESP32, the script must be added at the right point in the list
13+
linkflags = env.get("LINKFLAGS", [])
14+
idx = linkflags.index("memory.ld")
15+
linkflags.insert(idx+1, linker_script)
16+
env.Replace(LINKFLAGS=linkflags)
17+
else:
18+
# For other platforms, put it in last
19+
env.Append(LINKFLAGS=[linker_script])

pio-scripts/validate_modules.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from click import secho
55
from SCons.Script import Action, Exit
66
from platformio.builder.tools.piolib import LibBuilderBase
7-
7+
Import("env")
88

99
def is_wled_module(env, dep: LibBuilderBase) -> bool:
1010
"""Returns true if the specified library is a wled module
@@ -37,11 +37,13 @@ def check_map_file_objects(map_file: list[str], dirs: Iterable[str]) -> set[str]
3737
found.add(m)
3838
return found
3939

40+
DYNARRAY_SECTION = ".dtors" if env.get("PIOPLATFORM") == "espressif8266" else ".dynarray"
41+
USERMODS_SECTION = f"{DYNARRAY_SECTION}.usermods.1"
4042

4143
def count_usermod_objects(map_file: list[str]) -> int:
4244
""" Returns the number of usermod objects in the usermod list """
4345
# Count the number of entries in the usermods table section
44-
return len([x for x in map_file if ".dynarray.usermods.1" in x])
46+
return len([x for x in map_file if USERMODS_SECTION in x])
4547

4648

4749
def validate_map_file(source, target, env):
@@ -75,6 +77,5 @@ def validate_map_file(source, target, env):
7577
Exit(1)
7678
return None
7779

78-
Import("env")
7980
env.Append(LINKFLAGS=[env.subst("-Wl,--Map=${BUILD_DIR}/${PROGNAME}.map")])
8081
env.AddPostAction("$BUILD_DIR/${PROGNAME}.elf", Action(validate_map_file, cmdstr='Checking linked optional modules (usermods) in map file'))

tools/dynarray.ld

Lines changed: 0 additions & 9 deletions
This file was deleted.

tools/dynarray_espressif32.ld

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* ESP32 linker script fragment to add dynamic array section to binary */
2+
SECTIONS
3+
{
4+
.dynarray :
5+
{
6+
. = ALIGN(0x10);
7+
KEEP(*(SORT_BY_INIT_PRIORITY(.dynarray.*)))
8+
} > default_rodata_seg
9+
}
10+
INSERT AFTER .flash.rodata;

tools/esp8266_rodata.ld

Lines changed: 0 additions & 2 deletions
This file was deleted.

wled00/dynarray.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/* dynarray.h
2+
3+
Macros for generating a "dynamic array", a static array of objects declared in different translation units
4+
5+
*/
6+
7+
#pragma once
8+
9+
// Declare the beginning and ending elements of a dynamic array of 'type'.
10+
// This must be used in only one translation unit in your program for any given array.
11+
#define DECLARE_DYNARRAY(type, array_name) \
12+
static type const DYNARRAY_BEGIN(array_name)[0] __attribute__((__section__(DYNARRAY_SECTION "." #array_name ".0"), unused)) = {}; \
13+
static type const DYNARRAY_END(array_name)[0] __attribute__((__section__(DYNARRAY_SECTION "." #array_name ".99999"), unused)) = {};
14+
15+
// Declare an object that is a member of a dynamic array. "member name" must be unique; "array_section" is an integer for ordering items.
16+
// It is legal to define multiple items with the same section name; the order of those items will be up to the linker.
17+
#define DYNARRAY_MEMBER(type, array_name, member_name, array_section) type const member_name __attribute__((__section__(DYNARRAY_SECTION "." #array_name "." #array_section), used))
18+
19+
#define DYNARRAY_BEGIN(array_name) array_name##_begin
20+
#define DYNARRAY_END(array_name) array_name##_end
21+
#define DYNARRAY_LENGTH(array_name) (&DYNARRAY_END(array_name)[0] - &DYNARRAY_BEGIN(array_name)[0])
22+
23+
#ifdef ESP8266
24+
// ESP8266 linker script cannot be extended with a unique section for dynamic arrays.
25+
// We instead pack them in the ".dtors" section, as it's sorted and uploaded to the flash
26+
// (but will never be used in the embedded system)
27+
#define DYNARRAY_SECTION ".dtors"
28+
29+
#else /* ESP8266 */
30+
31+
// Use a unique named section; the linker script must be extended to ensure it's correctly placed.
32+
#define DYNARRAY_SECTION ".dynarray"
33+
34+
#endif

wled00/fcn_declare.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#pragma once
22
#ifndef WLED_FCN_DECLARE_H
33
#define WLED_FCN_DECLARE_H
4+
#include <dynarray.h>
45

56
/*
67
* All globally accessible functions are declared here
@@ -366,7 +367,7 @@ namespace UsermodManager {
366367
};
367368

368369
// Register usermods by building a static list via a linker section
369-
#define REGISTER_USERMOD(x) Usermod* const um_##x __attribute__((__section__(".dynarray.usermods.1"), used)) = &x
370+
#define REGISTER_USERMOD(x) DYNARRAY_MEMBER(Usermod*, usermods, um_##x, 1) = &x
370371

371372
//usermod.cpp
372373
void userSetup();

wled00/um_manager.cpp

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,76 +10,80 @@
1010
// We stick them in the '.dtors' segment because it's always included by the linker scripts
1111
// even though it never gets called. Who calls exit() in an embedded program anyways?
1212
// If someone ever does, though, it'll explode as these aren't function pointers.
13-
static Usermod * const _usermod_table_begin[0] __attribute__((__section__(".dynarray.usermods.0"), unused)) = {};
14-
static Usermod * const _usermod_table_end[0] __attribute__((__section__(".dynarray.usermods.99"), unused)) = {};
13+
DECLARE_DYNARRAY(Usermod*, usermods);
1514

1615
static size_t getCount() {
17-
return &_usermod_table_end[0] - &_usermod_table_begin[0];
16+
return DYNARRAY_LENGTH(usermods);
1817
}
1918

2019

2120
//Usermod Manager internals
22-
void UsermodManager::setup() { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->setup(); }
23-
void UsermodManager::connected() { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->connected(); }
24-
void UsermodManager::loop() { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->loop(); }
25-
void UsermodManager::handleOverlayDraw() { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->handleOverlayDraw(); }
26-
void UsermodManager::appendConfigData(Print& dest) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->appendConfigData(dest); }
21+
void UsermodManager::setup() { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->setup(); }
22+
void UsermodManager::connected() { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->connected(); }
23+
void UsermodManager::loop() { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->loop(); }
24+
void UsermodManager::handleOverlayDraw() { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->handleOverlayDraw(); }
25+
void UsermodManager::appendConfigData(Print& dest) { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->appendConfigData(dest); }
2726
bool UsermodManager::handleButton(uint8_t b) {
2827
bool overrideIO = false;
29-
for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) {
28+
for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) {
3029
if ((*mod)->handleButton(b)) overrideIO = true;
3130
}
3231
return overrideIO;
3332
}
3433
bool UsermodManager::getUMData(um_data_t **data, uint8_t mod_id) {
35-
for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) {
34+
for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) {
3635
if (mod_id > 0 && (*mod)->getId() != mod_id) continue; // only get data form requested usermod if provided
3736
if ((*mod)->getUMData(data)) return true; // if usermod does provide data return immediately (only one usermod can provide data at one time)
3837
}
3938
return false;
4039
}
41-
void UsermodManager::addToJsonState(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->addToJsonState(obj); }
40+
void UsermodManager::addToJsonState(JsonObject& obj) { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->addToJsonState(obj); }
4241
void UsermodManager::addToJsonInfo(JsonObject& obj) {
4342
auto um_id_list = obj.createNestedArray("um");
44-
for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) {
43+
for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) {
4544
um_id_list.add((*mod)->getId());
4645
(*mod)->addToJsonInfo(obj);
4746
}
4847
}
49-
void UsermodManager::readFromJsonState(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->readFromJsonState(obj); }
50-
void UsermodManager::addToConfig(JsonObject& obj) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->addToConfig(obj); }
48+
void UsermodManager::readFromJsonState(JsonObject& obj) { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->readFromJsonState(obj); }
49+
void UsermodManager::addToConfig(JsonObject& obj) { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->addToConfig(obj); }
5150
bool UsermodManager::readFromConfig(JsonObject& obj) {
52-
bool allComplete = true;
53-
for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) {
51+
Serial.printf_P(PSTR("Mods: %d Begin: %08X End: %08X\n"), getCount(), (intptr_t) &DYNARRAY_BEGIN(usermods)[0], (intptr_t) &DYNARRAY_END(usermods)[0]);
52+
Usermod** volatile x = (Usermod**) DYNARRAY_BEGIN(usermods);
53+
Serial.printf_P(PSTR("X: %08X\n"), (intptr_t) x);
54+
Serial.printf_P(PSTR("*X: %08X\n"), (intptr_t) *x);
55+
56+
bool allComplete = true;
57+
for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) {
5458
if (!(*mod)->readFromConfig(obj)) allComplete = false;
5559
}
5660
return allComplete;
5761
}
5862
#ifndef WLED_DISABLE_MQTT
59-
void UsermodManager::onMqttConnect(bool sessionPresent) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->onMqttConnect(sessionPresent); }
63+
void UsermodManager::onMqttConnect(bool sessionPresent) { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->onMqttConnect(sessionPresent); }
6064
bool UsermodManager::onMqttMessage(char* topic, char* payload) {
61-
for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) if ((*mod)->onMqttMessage(topic, payload)) return true;
65+
for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) if ((*mod)->onMqttMessage(topic, payload)) return true;
6266
return false;
6367
}
6468
#endif
6569
#ifndef WLED_DISABLE_ESPNOW
6670
bool UsermodManager::onEspNowMessage(uint8_t* sender, uint8_t* payload, uint8_t len) {
67-
for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) if ((*mod)->onEspNowMessage(sender, payload, len)) return true;
71+
for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) if ((*mod)->onEspNowMessage(sender, payload, len)) return true;
6872
return false;
6973
}
7074
#endif
7175
bool UsermodManager::onUdpPacket(uint8_t* payload, size_t len) {
72-
for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) if ((*mod)->onUdpPacket(payload, len)) return true;
76+
for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) if ((*mod)->onUdpPacket(payload, len)) return true;
7377
return false;
7478
}
75-
void UsermodManager::onUpdateBegin(bool init) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->onUpdateBegin(init); } // notify usermods that update is to begin
76-
void UsermodManager::onStateChange(uint8_t mode) { for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) (*mod)->onStateChange(mode); } // notify usermods that WLED state changed
79+
void UsermodManager::onUpdateBegin(bool init) { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->onUpdateBegin(init); } // notify usermods that update is to begin
80+
void UsermodManager::onStateChange(uint8_t mode) { for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) (*mod)->onStateChange(mode); } // notify usermods that WLED state changed
7781

7882
/*
7983
* Enables usermods to lookup another Usermod.
8084
*/
8185
Usermod* UsermodManager::lookup(uint16_t mod_id) {
82-
for (auto mod = _usermod_table_begin; mod < _usermod_table_end; ++mod) {
86+
for (auto mod = DYNARRAY_BEGIN(usermods); mod < DYNARRAY_END(usermods); ++mod) {
8387
if ((*mod)->getId() == mod_id) {
8488
return *mod;
8589
}

0 commit comments

Comments
 (0)