Skip to content

Commit 447dd9f

Browse files
committed
ESP32: Modernize Build System Infrastructure (Step 1)
This PR introduces the core infrastructure for a data-driven build system for ESP32, aligning it further with the standard ArduPilot hwdef.dat approach used by ChibiOS. Key Infrastructure Changes: - Generalized esp32_hwdef.py to support both V1 (Current Master) and V2 (Standard-aligned) syntax via the ESP32_HWDEF_V2 flag. - Integrated auto-discovery in boards.py to support legacy headers and hwdef subdirectories in a unified 'boards/' location. - Automated sdkconfig generation and partition handling in esp32.py for V2-enabled boards. - Introduced feature-based capability defines (e.g., HAL_ESP32_HAS_MCPWM) to replace target-specific ifdefs. - Restored legacy board headers to provide a risk-free, staged migration path. - Added MIGRATION.md detailing the transition path from V1 to V2 syntax. This is Step 1 of a multi-stage refactor. Existing boards in master remain functional through the compatibility parser. Relates to #30709
1 parent 46ae785 commit 447dd9f

File tree

18 files changed

+2934
-692
lines changed

18 files changed

+2934
-692
lines changed

Tools/ardupilotwaf/boards.py

Lines changed: 101 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -681,18 +681,28 @@ def add_dynamic_boards_from_hwdef_dir(base_type, hwdef_dir):
681681
newclass = type(d, (base_type,), {'name': d})
682682

683683
def add_dynamic_boards_esp32():
684-
'''add boards based on existence of hwdef.dat in subdirectories for ESP32'''
685-
dirname, dirlist, filenames = next(os.walk('libraries/AP_HAL_ESP32/hwdef'))
686-
for d in dirlist:
687-
if d in _board_classes.keys():
688-
continue
689-
hwdef = os.path.join(dirname, d, 'hwdef.dat')
690-
if os.path.exists(hwdef):
691-
mcu_esp32s3 = True if (d[0:7] == "esp32s3") else False
692-
if mcu_esp32s3:
693-
newclass = type(d, (esp32s3,), {'name': d})
694-
else:
695-
newclass = type(d, (esp32,), {'name': d})
684+
'''add boards based on existence of hwdef.dat in hwdef subdirectories or legacy headers in boards directory'''
685+
hwdef_base = 'libraries/AP_HAL_ESP32/hwdef'
686+
if os.path.exists(hwdef_base):
687+
dirname, dirlist, filenames = next(os.walk(hwdef_base))
688+
for d in dirlist:
689+
if d in _board_classes.keys() or d == 'scripts':
690+
continue
691+
hwdef_path = os.path.join(dirname, d, 'hwdef.dat')
692+
if os.path.exists(hwdef_path):
693+
mcu_esp32s3 = True if (d[0:7] == "esp32s3") else False
694+
type(d, (esp32s3 if mcu_esp32s3 else esp32,), {'name': d, 'hwdef': hwdef_path})
695+
696+
# Check for legacy headers in boards directory
697+
legacy_base = 'libraries/AP_HAL_ESP32/boards'
698+
if os.path.exists(legacy_base):
699+
dirname, dirlist, filenames = next(os.walk(legacy_base))
700+
for f in filenames:
701+
if f.endswith('.h'):
702+
board_name = f[:-2]
703+
if board_name not in _board_classes:
704+
mcu_esp32s3 = True if (board_name[0:7] == "esp32s3") else False
705+
type(board_name, (esp32s3 if mcu_esp32s3 else esp32,), {'name': board_name})
696706

697707
def get_boards_names():
698708
add_dynamic_boards_chibios()
@@ -995,13 +1005,44 @@ class esp32(Board):
9951005
abstract = True
9961006
toolchain = 'xtensa-esp32-elf'
9971007

1008+
def __init__(self):
1009+
super().__init__()
1010+
self.with_can = True
1011+
9981012
def configure(self, cfg):
1013+
# run hwdef to get MCU if this is an hwdef board
1014+
hwdef_dat = getattr(self, 'hwdef', None)
1015+
if hwdef_dat:
1016+
sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), '../../libraries/AP_HAL_ESP32/hwdef/scripts'))
1017+
import esp32_hwdef
1018+
if not os.path.exists(cfg.bldnode.abspath()):
1019+
os.makedirs(cfg.bldnode.abspath())
1020+
eh = esp32_hwdef.ESP32HWDef(
1021+
outdir=cfg.bldnode.abspath(),
1022+
hwdef=[hwdef_dat],
1023+
quiet=True,
1024+
)
1025+
eh.run()
1026+
mcu = eh.mcu
1027+
cfg.env.MCU = mcu
1028+
cfg.env.append_value('DEFINES', 'HAL_ESP32_HWDEF_BUILD=1')
1029+
else:
1030+
# Fallback for legacy boards
1031+
mcu = 'esp32s3' if 'esp32s3' in self.name else 'esp32'
1032+
cfg.env.MCU = mcu
1033+
cfg.env.append_value('DEFINES', 'HAL_ESP32_HWDEF_BUILD=0')
1034+
cfg.env.append_value('DEFINES', f'HAL_BOARD_NAME_{self.name.upper()}=1')
1035+
1036+
# Select toolchain based on MCU architecture (Xtensa vs RISC-V)
1037+
mcu_lower = mcu.lower()
1038+
if mcu_lower in ['esp32c3', 'esp32c6', 'esp32h2', 'esp32p4']:
1039+
self.toolchain = 'riscv32-esp-elf'
1040+
else:
1041+
self.toolchain = 'xtensa-%s-elf' % mcu_lower
1042+
9991043
super(esp32, self).configure(cfg)
10001044
if cfg.env.TOOLCHAIN:
10011045
self.toolchain = cfg.env.TOOLCHAIN
1002-
else:
1003-
# default tool-chain for esp32-based boards:
1004-
self.toolchain = 'xtensa-esp32-elf'
10051046

10061047
def configure_env(self, cfg, env):
10071048
env.BOARD_CLASS = "ESP32"
@@ -1057,13 +1098,33 @@ def expand_path(p):
10571098
'-fdata-sections',
10581099
'-fno-exceptions',
10591100
'-fno-rtti',
1060-
'-nostdlib',
10611101
'-fstrict-volatile-bitfields',
10621102
'-Wno-sign-compare',
10631103
'-fno-inline-functions',
10641104
'-mlongcalls',
1065-
'-fsingle-precision-constant', # force const vals to be float , not double. so 100.0 means 100.0f
1105+
'-fsingle-precision-constant', # force const vals to be float , not double. so 100.0 means 100.0f
10661106
'-fno-threadsafe-statics']
1107+
# Detect IDF version from the IDF_PATH set during configure()
1108+
idf_major = 5
1109+
idf_version_h = os.path.join(os.environ.get('IDF_PATH', ''),
1110+
'components/esp_common/include/esp_idf_version.h')
1111+
if os.path.exists(idf_version_h):
1112+
with open(idf_version_h, 'r') as f:
1113+
for line in f:
1114+
m = re.match(r'#define\s+ESP_IDF_VERSION_MAJOR\s+(\d+)', line)
1115+
if m:
1116+
idf_major = int(m.group(1))
1117+
break
1118+
if idf_major >= 6:
1119+
# IDF 6.0 uses PicoLibC which is incompatible with -nostdlib.
1120+
# The -specs=picolibc.specs flag is needed so the compiler finds
1121+
# PicoLibC headers. The cmath std:: namespace fix is handled in
1122+
# libraries/AP_Common/missing/cmath via ESP_PLATFORM guard.
1123+
env.CFLAGS += ['-specs=picolibc.specs']
1124+
env.CXXFLAGS += ['-specs=picolibc.specs']
1125+
else:
1126+
# IDF 5.x uses Newlib which works with -nostdlib
1127+
env.CXXFLAGS += ['-nostdlib']
10671128
env.CXXFLAGS.remove('-Werror=undef')
10681129
env.CXXFLAGS.remove('-Werror=shadow')
10691130

@@ -1079,6 +1140,29 @@ def expand_path(p):
10791140
HAL_PARAM_DEFAULTS_PATH='"@ROMFS/defaults.parm"',
10801141
)
10811142

1143+
# Use generated board headers instead of static ones
1144+
env.INCLUDES += [
1145+
cfg.bldnode.find_or_declare('boards').abspath(),
1146+
]
1147+
1148+
# Include hwdef.h in compilation for ESP32 builds
1149+
hwdef_h = cfg.bldnode.find_or_declare('hwdef.h').abspath()
1150+
env.CFLAGS += ['-include', hwdef_h]
1151+
env.CXXFLAGS += ['-include', hwdef_h]
1152+
1153+
# Parse hwdef.dat for APA102 pins to set environment variables
1154+
hwdef_path = 'libraries/AP_HAL_ESP32/hwdef/%s/hwdef.dat' % self.get_name()
1155+
if os.path.exists(hwdef_path):
1156+
with open(hwdef_path, 'r') as f:
1157+
for line in f:
1158+
line = line.strip()
1159+
if line.startswith('APA102_DATA_PIN='):
1160+
pin_num = line.split('=')[1]
1161+
env.APA102_DATA = pin_num
1162+
elif line.startswith('APA102_CLOCK_PIN='):
1163+
pin_num = line.split('=')[1]
1164+
env.APA102_CLOCK = pin_num
1165+
10821166
env.AP_PROGRAM_AS_STLIB = True
10831167
#if cfg.options.enable_profile:
10841168
# env.CXXFLAGS += ['-pg',

0 commit comments

Comments
 (0)