Skip to content

Commit 66d1851

Browse files
committed
Migrate HAL to lib-board and add stack debug
Reorganize hardware abstraction by introducing lib-board and migrating many lib-hal components into it. Convert hal:: status/interfaces to board:: (notably statusled and reboot/init/run calls) and update includes accordingly across ArtNet, E1.31 and firmware mains. Move common headers (utc, global, firmwareversion) into common/include, simplify UTC API namespace and adjust Global to use utc:: helpers. Add stack usage debugging: implement debug_stack.h with runtime printing and a new stack_debug_init.cpp; wire it into GD32 firmware Rules.mk. Update Makefiles and library lists to use board instead of hal and add build metadata/config files for lib-board.
1 parent ad538fb commit 66d1851

74 files changed

Lines changed: 890 additions & 844 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

common/include/firmware/debug/debug_stack.h

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,90 @@
22
* @file debug_stack.h
33
*
44
*/
5+
/* Copyright (C) 2026 by Arjan van Vught mailto:info@gd32-dmx.org
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
525

626
#ifndef FIRMWARE_DEBUG_DEBUG_STACK_H_
727
#define FIRMWARE_DEBUG_DEBUG_STACK_H_
828

9-
namespace debug::stack
10-
{
11-
void Print();
12-
void Run();
29+
#include <cstdint>
30+
#include <cstdio>
31+
#include <cassert>
32+
33+
#include "timing.h"
34+
35+
extern unsigned char stack_low;
36+
extern unsigned char _sp; // NOLINT
37+
38+
namespace debug::stack {
39+
inline static constexpr uint32_t kMagicWord = 0xABCDABCD;
40+
41+
inline void Print() {
42+
static uint32_t s_used_bytes_previous;
43+
const auto* start = reinterpret_cast<uint32_t*>(&stack_low);
44+
const auto* end = reinterpret_cast<uint32_t*>(&_sp);
45+
assert(end > start);
46+
const auto kSize = static_cast<uint32_t>(end - start);
47+
48+
auto* p = start;
49+
50+
while (p < end) {
51+
if (*p != kMagicWord) {
52+
break;
53+
}
54+
p++;
55+
}
56+
57+
const auto kUsedBytes = static_cast<uint32_t>(4 * (end - p));
58+
const auto kFreeBytes = static_cast<uint32_t>(4 * (p - start));
59+
const auto kFreePct = (static_cast<uint32_t>(p - start) * 100U) / kSize;
60+
61+
if (s_used_bytes_previous != kUsedBytes) {
62+
s_used_bytes_previous = kUsedBytes;
63+
64+
if (kFreePct == 0) {
65+
printf("\x1b[31m");
66+
} else if (kFreePct == 1) {
67+
printf("\x1b[33m");
68+
} else {
69+
printf("\x1b[34m");
70+
}
71+
72+
#ifndef NDEBUG
73+
printf("Stack: Size %uKB, [%p:%p:%p], Used: %u, Free: %u [%u]", kSize / (1024 / 4), start, p, end, kUsedBytes, kFreeBytes, kFreePct);
74+
#else
75+
printf("Stack: Size %uKB, Used: %u, Free: %u", kSize / (1024 / 4), kUsedBytes, kFreeBytes);
76+
#endif
77+
printf("\x1b[39m\n");
78+
}
79+
}
80+
81+
inline void Run() {
82+
static uint32_t s_millis_previous;
83+
const auto kMillis = timing::Millis();
84+
if (kMillis - s_millis_previous >= 1000) {
85+
s_millis_previous = kMillis;
86+
Print();
87+
}
1388
}
89+
} // namespace debug::stack
1490

1591
#endif // FIRMWARE_DEBUG_DEBUG_STACK_H_
Lines changed: 10 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* which is used to get and set the current UTC offset. It also contains validation logic
77
* for standard and non-standard UTC offsets.
88
*/
9-
/* Copyright (C) 2025 by Arjan van Vught mailto:info@gd32-dmx.org
9+
/* Copyright (C) 2025-2026 by Arjan van Vught mailto:info@gd32-dmx.org
1010
*/
1111

1212
#ifndef GLOBAL_H_
@@ -20,8 +20,7 @@
2020
* @namespace global
2121
* @brief Contains global variables related to time configuration.
2222
*/
23-
namespace global
24-
{
23+
namespace global {
2524
extern int32_t g_utc_offset;
2625
}
2726

@@ -32,15 +31,13 @@ extern int32_t g_utc_offset;
3231
* The Global class provides methods to set and get the UTC offset in seconds.
3332
* It also includes validation logic for standard UTC time zones.
3433
*/
35-
class Global
36-
{
34+
class Global {
3735
public:
3836
/**
3937
* @brief Get the singleton instance of the Global class.
4038
* @return Reference to the singleton Global object.
4139
*/
42-
static Global& Instance()
43-
{
40+
static Global& Instance() {
4441
static Global instance;
4542
return instance;
4643
}
@@ -56,17 +53,15 @@ class Global
5653
* @param[out] hours Signed hour component.
5754
* @param[out] minutes Unsigned minute component.
5855
*/
59-
inline void GetUtcOffset(int32_t& hours, uint32_t& minutes) { hal::utc::SplitOffset(global::g_utc_offset, hours, minutes); }
56+
inline void GetUtcOffset(int32_t& hours, uint32_t& minutes) { utc::SplitOffset(global::g_utc_offset, hours, minutes); }
6057

6158
/**
6259
* @brief Sets the global UTC offset if the value is valid.
6360
* @param utc_offset_seconds Offset in seconds
6461
* @return true if successfully set; false otherwise
6562
*/
66-
inline bool SetUtcOffsetIfValid(int32_t utc_offset_seconds)
67-
{
68-
if (hal::utc::IsValidOffset(utc_offset_seconds))
69-
{
63+
inline bool SetUtcOffsetIfValid(int32_t utc_offset_seconds) {
64+
if (utc::IsValidOffset(utc_offset_seconds)) {
7065
::global::g_utc_offset = utc_offset_seconds;
7166
return true;
7267
}
@@ -79,11 +74,9 @@ class Global
7974
* @param minutes Unsigned minute component
8075
* @return true if valid and set; false otherwise
8176
*/
82-
inline bool SetUtcOffsetIfValid(int32_t hours, uint32_t minutes)
83-
{
77+
inline bool SetUtcOffsetIfValid(int32_t hours, uint32_t minutes) {
8478
int32_t offset_seconds;
85-
if (hal::utc::ValidateOffset(hours, minutes, offset_seconds))
86-
{
79+
if (utc::ValidateOffset(hours, minutes, offset_seconds)) {
8780
return SetUtcOffsetIfValid(offset_seconds);
8881
}
8982
return false;
@@ -98,4 +91,4 @@ class Global
9891
Global& operator=(Global&&) = delete;
9992
};
10093

101-
#endif // GLOBAL_H_
94+
#endif // GLOBAL_H_
Lines changed: 21 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* @brief UTC offset handling utilities (validation, conversion, parsing).
44
*
55
*/
6-
/* Copyright (C) 2019-2025 by Arjan van Vught mailto:info@gd32-dmx.org
6+
/* Copyright (C) 2019-2026 by Arjan van Vught mailto:info@gd32-dmx.org
77
*
88
* Permission is hereby granted, free of charge, to any person obtaining a copy
99
* of this software and associated documentation files (the "Software"), to deal
@@ -32,16 +32,14 @@
3232
* @namespace global
3333
* @brief Holds global UTC offset variable used system-wide.
3434
*/
35-
namespace global
36-
{
35+
namespace global {
3736
/**
3837
* @brief The current UTC offset in seconds.
3938
*/
4039
extern int32_t g_utc_offset;
4140
} // namespace global
4241

43-
namespace hal::utc
44-
{
42+
namespace utc {
4543

4644
constexpr int32_t kUtcOffsetMin = -12; ///< Minimum valid UTC offset (hours)
4745
constexpr int32_t kUtcOffsetMax = 14; ///< Maximum valid UTC offset (hours)
@@ -50,8 +48,7 @@ constexpr int32_t kUtcOffsetMax = 14; ///< Maximum valid UTC offset (hours)
5048
* @struct Offset
5149
* @brief Represents a fractional UTC offset.
5250
*/
53-
struct Offset
54-
{
51+
struct Offset {
5552
int32_t hours; ///< Signed hour component
5653
uint32_t minutes; ///< Unsigned minute component
5754
};
@@ -68,19 +65,14 @@ constexpr Offset kValidOffsets[] = {{-9, 30}, {-3, 30}, {3, 30}, {4, 30}, {5, 30
6865
* @param[out] utc_offset_seconds Resulting offset in seconds
6966
* @return true if valid; false otherwise
7067
*/
71-
inline bool ValidateOffset(int32_t hours, uint32_t minutes, int32_t& utc_offset_seconds)
72-
{
73-
if (hours >= kUtcOffsetMin && hours <= kUtcOffsetMax)
74-
{
75-
if (minutes == 0)
76-
{
68+
inline bool ValidateOffset(int32_t hours, uint32_t minutes, int32_t& utc_offset_seconds) {
69+
if (hours >= kUtcOffsetMin && hours <= kUtcOffsetMax) {
70+
if (minutes == 0) {
7771
utc_offset_seconds = hours * 3600;
7872
return true;
7973
}
80-
for (const auto& offset : kValidOffsets)
81-
{
82-
if (offset.hours == hours && offset.minutes == minutes)
83-
{
74+
for (const auto& offset : kValidOffsets) {
75+
if (offset.hours == hours && offset.minutes == minutes) {
8476
utc_offset_seconds = (hours >= 0) ? (hours * 3600 + static_cast<int32_t>(minutes) * 60) : (hours * 3600 - static_cast<int32_t>(minutes) * 60);
8577
return true;
8678
}
@@ -94,22 +86,17 @@ inline bool ValidateOffset(int32_t hours, uint32_t minutes, int32_t& utc_offset_
9486
* @param utc_offset_seconds UTC offset in seconds
9587
* @return true if offset is valid; false otherwise
9688
*/
97-
inline bool IsValidOffset(int32_t utc_offset_seconds)
98-
{
89+
inline bool IsValidOffset(int32_t utc_offset_seconds) {
9990
if (utc_offset_seconds == 0) return true;
10091
int32_t hours = utc_offset_seconds / 3600;
101-
uint32_t minutes = (utc_offset_seconds >= 0) ? static_cast<uint32_t>(utc_offset_seconds - hours * 3600) / 60
102-
: static_cast<uint32_t>((hours * 3600 - utc_offset_seconds)) / 60;
92+
uint32_t minutes = (utc_offset_seconds >= 0) ? static_cast<uint32_t>(utc_offset_seconds - hours * 3600) / 60 : static_cast<uint32_t>((hours * 3600 - utc_offset_seconds)) / 60;
10393

104-
if (minutes == 0 && hours >= kUtcOffsetMin && hours <= kUtcOffsetMax)
105-
{
94+
if (minutes == 0 && hours >= kUtcOffsetMin && hours <= kUtcOffsetMax) {
10695
return true;
10796
}
10897

109-
for (const auto& offset : kValidOffsets)
110-
{
111-
int32_t offset_seconds = (offset.hours >= 0) ? offset.hours * 3600 + static_cast<int32_t>(offset.minutes * 60)
112-
: offset.hours * 3600 - static_cast<int32_t>(offset.minutes * 60);
98+
for (const auto& offset : kValidOffsets) {
99+
int32_t offset_seconds = (offset.hours >= 0) ? offset.hours * 3600 + static_cast<int32_t>(offset.minutes * 60) : offset.hours * 3600 - static_cast<int32_t>(offset.minutes * 60);
113100
if (utc_offset_seconds == offset_seconds) return true;
114101
}
115102
return false;
@@ -121,15 +108,11 @@ inline bool IsValidOffset(int32_t utc_offset_seconds)
121108
* @param[out] hours Signed hour component
122109
* @param[out] minutes Unsigned minute component
123110
*/
124-
inline void SplitOffset(int32_t utc_offset_seconds, int32_t& hours, uint32_t& minutes)
125-
{
111+
inline void SplitOffset(int32_t utc_offset_seconds, int32_t& hours, uint32_t& minutes) {
126112
hours = utc_offset_seconds / 3600;
127-
if (utc_offset_seconds >= 0)
128-
{
113+
if (utc_offset_seconds >= 0) {
129114
minutes = static_cast<uint32_t>((utc_offset_seconds - hours * 3600) / 60);
130-
}
131-
else
132-
{
115+
} else {
133116
minutes = static_cast<uint32_t>(((hours * 3600) - utc_offset_seconds) / 60);
134117
}
135118
}
@@ -142,15 +125,12 @@ inline void SplitOffset(int32_t utc_offset_seconds, int32_t& hours, uint32_t& mi
142125
* @param[out] minutes Unsigned minute component
143126
* @return true if parse was successful and valid; false otherwise
144127
*/
145-
inline bool ParseOffset(const char* buffer, uint32_t buffer_length, int32_t& hours, uint32_t& minutes)
146-
{
128+
inline bool ParseOffset(const char* buffer, uint32_t buffer_length, int32_t& hours, uint32_t& minutes) {
147129
if (buffer == nullptr) return false;
148130

149-
if (buffer_length == 5)
150-
{
131+
if (buffer_length == 5) {
151132
static constexpr const char kZeroOffset[5] = {'0', '0', ':', '0', '0'};
152-
if (memcmp(kZeroOffset, buffer, sizeof(kZeroOffset)) == 0)
153-
{
133+
if (memcmp(kZeroOffset, buffer, sizeof(kZeroOffset)) == 0) {
154134
hours = 0;
155135
minutes = 0;
156136
return true;
@@ -179,6 +159,6 @@ inline bool ParseOffset(const char* buffer, uint32_t buffer_length, int32_t& hou
179159
int32_t dummy;
180160
return ValidateOffset(hours, minutes, dummy);
181161
}
182-
} // namespace hal::utc
162+
} // namespace utc
183163

184164
#endif // UTC_H_

common/make/gd32/Includes.mk

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@ endif
3232

3333
ifdef USB_HOST
3434
INCLUDES+=-I../lib-gd32/device/usb
35-
INCLUDES+=-I../lib-hal/device/usb/host/gd32
3635
endif
3736

3837
ifdef USB_DEVICE
3938
INCLUDES+=-I../lib-gd32/device/usb
40-
INCLUDES+=-I../lib-hal/device/usb/device/gd32
4139
endif
4240

4341
ifeq ($(findstring gd32f20x,$(FAMILY)), gd32f20x)

firmware-template-gd32/Rules.mk

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,11 @@ $(BUILD)startup_$(LINE).o : $(FIRMWARE_DIR)/startup_$(LINE).S
127127
$(BUILD)hardfault_handler.o : $(FIRMWARE_DIR)/hardfault_handler.cpp
128128
$(CPP) $(COPS) $(CPPOPS) -c $(FIRMWARE_DIR)/hardfault_handler.cpp -o $(BUILD)hardfault_handler.o
129129

130-
$(BUILD)main.elf: Makefile.GD32 $(LINKER) $(BUILD)startup_$(LINE).o $(BUILD)hardfault_handler.o $(OBJECTS) $(LIBDEP)
131-
$(LD) $(BUILD)startup_$(LINE).o $(BUILD)hardfault_handler.o $(OBJECTS) -Map $(MAP) -T $(LINKER) $(LDOPS) -o $(BUILD)main.elf $(LIBGD32) $(LDLIBS) $(PLATFORM_LIBGCC) -lgcc
130+
$(BUILD)stack_debug_init.o : $(FIRMWARE_DIR)/stack_debug_init.cpp
131+
$(CPP) $(COPS) $(CPPOPS) -c $(FIRMWARE_DIR)/stack_debug_init.cpp -o $(BUILD)stack_debug_init.o
132+
133+
$(BUILD)main.elf: Makefile.GD32 $(LINKER) $(BUILD)startup_$(LINE).o $(BUILD)hardfault_handler.o $(BUILD)stack_debug_init.o $(OBJECTS) $(LIBDEP)
134+
$(LD) $(BUILD)startup_$(LINE).o $(BUILD)hardfault_handler.o $(OBJECTS) $(BUILD)stack_debug_init.o -Map $(MAP) -T $(LINKER) $(LDOPS) -o $(BUILD)main.elf $(LIBGD32) $(LDLIBS) $(PLATFORM_LIBGCC) -lgcc
132135
$(PREFIX)objdump -D $(BUILD)main.elf | $(PREFIX)c++filt > $(LIST)
133136
$(PREFIX)size -A -x $(BUILD)main.elf
134137

firmware-template-gd32/lib/Rules.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ include ../common/make/gd32/Includes.mk
2828
include ../common/make/Artnet.mk
2929
include ../common/make/gd32/Validate.mk
3030

31-
INCLUDES+=-I../lib-configstore/include -I../lib-device/include -I../lib-display/include -I../lib-flash/include -I../lib-flashcode/include -I../lib-hal/include -I../lib-network/include
31+
INCLUDES+=-I../lib-configstore/include -I../lib-device/include -I../lib-display/include -I../lib-flash/include -I../lib-flashcode/include -I../lib-board/include -I../lib-network/include
3232

3333
COPS=-DGD32 -D$(FAMILY_UCA) -D$(LINE_UC) -D$(MCU) -D$(BOARD) -DPHY_TYPE=$(ENET_PHY)
3434
COPS+=$(strip $(DEFINES) $(MAKE_FLAGS) $(VALIDATE_FLAGS) $(INCLUDES))
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <stdint.h>
2+
3+
#include "firmware/debug/debug_stack.h"
4+
5+
extern unsigned char stack_low;
6+
extern unsigned char _sp; // NOLINT
7+
8+
extern "C" void stack_debug_init() { // NOLINT
9+
auto* start = reinterpret_cast<uint32_t*>(&stack_low);
10+
auto* end = reinterpret_cast<uint32_t*>(&_sp);
11+
12+
while (start < end) {
13+
*start = debug::stack::kMagicWord;
14+
start++;
15+
}
16+
}

firmware-template/libs.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,6 @@ ifeq ($(findstring DISPLAY_UDF,$(DEFINES)),DISPLAY_UDF)
7171
LIBS+=displayudf
7272
endif
7373

74-
LIBS+=flash display device superloop hal hwclock
74+
LIBS+=flash display device superloop board hwclock
7575

7676
$(info $$LIBS [${LIBS}])

0 commit comments

Comments
 (0)