Part of the CH32V pipeline audit (#1102).
What happens
For every CH32V20x-series board, the orchestrator links with the OpenWCH core's shared linker script:
crates/fbuild-build-mcu/src/ch32v/orchestrator.rs (~line 172):
let linker_script_path = framework_dir.join("system").join(&system_series)
.join("SRC").join("Ld").join("Link.ld");
At the pinned core commit (d7671623), system/CH32V20x/SRC/Ld/Link.ld is parameterized:
__flash_size = DEFINED(__FLASH_SIZE) ? __FLASH_SIZE : 64K;
__ram_size = DEFINED(__RAM_SIZE) ? __RAM_SIZE : 20K;
fbuild never passes --defsym __FLASH_SIZE=... / --defsym __RAM_SIZE=... (checked Ch32vLinker::link and configs/ch32v003.json linker_flags), so every V20x board links against 64K flash / 20K RAM regardless of what the chip actually has.
Why it matters — worked example
The script puts the startup stack at the top of the declared RAM: _eusrstack = ORIGIN(RAM) + LENGTH(RAM) = 0x2000_5000 with the 20K default.
- genericCH32V203C6T6 / genericCH32V203G6U6 are 32K-flash / 10K-RAM parts (board JSONs correctly say
ram: 10240, rom: 32768; matches the WCH datasheet and the commented-out CH32V203F6/G6/K6/C6 block in the same Link.ld). Physical RAM ends at 0x2000_2800. The startup code loads SP = 0x2000_5000 — the stack pointer points at memory that doesn't exist, so the very first push faults. Firmware built by fbuild for these two boards cannot run on real silicon, while the build and the size check both report success (size check compares against the board JSON's 32K/10K, which the link didn't enforce).
- genericCH32V203RBT6 / genericCH32V208WBU6 are 128K-flash / 64K-RAM parts. They link against 64K/20K: a 70K firmware that the size check says "fits in 128K" fails at link, and 44K of RAM is unusable.
Suggested fix
Pass the board JSON memory through at link time for the V20x family, e.g.:
-Wl,--defsym=__FLASH_SIZE=<board.rom>,--defsym=__RAM_SIZE=<board.ram>
(the other series' scripts hardcode MEMORY and don't read these symbols, so this is safe to gate on the script actually being parameterized, or just always define the symbols — DEFINED() only consults them where the script opts in). Add a build-time assertion that _eusrstack <= ORIGIN(RAM) + board.ram would also have caught this class of bug.
Severity: HIGH (silent runtime brick on C6/G6 parts; inconsistent size accounting on RB/208).
Part of the CH32V pipeline audit (#1102).
What happens
For every CH32V20x-series board, the orchestrator links with the OpenWCH core's shared linker script:
crates/fbuild-build-mcu/src/ch32v/orchestrator.rs(~line 172):At the pinned core commit (d7671623),
system/CH32V20x/SRC/Ld/Link.ldis parameterized:fbuild never passes
--defsym __FLASH_SIZE=.../--defsym __RAM_SIZE=...(checkedCh32vLinker::linkandconfigs/ch32v003.jsonlinker_flags), so every V20x board links against 64K flash / 20K RAM regardless of what the chip actually has.Why it matters — worked example
The script puts the startup stack at the top of the declared RAM:
_eusrstack = ORIGIN(RAM) + LENGTH(RAM)=0x2000_5000with the 20K default.ram: 10240,rom: 32768; matches the WCH datasheet and the commented-outCH32V203F6/G6/K6/C6block in the same Link.ld). Physical RAM ends at0x2000_2800. The startup code loads SP =0x2000_5000— the stack pointer points at memory that doesn't exist, so the very first push faults. Firmware built by fbuild for these two boards cannot run on real silicon, while the build and the size check both report success (size check compares against the board JSON's 32K/10K, which the link didn't enforce).Suggested fix
Pass the board JSON memory through at link time for the V20x family, e.g.:
(the other series' scripts hardcode MEMORY and don't read these symbols, so this is safe to gate on the script actually being parameterized, or just always define the symbols —
DEFINED()only consults them where the script opts in). Add a build-time assertion that_eusrstack <= ORIGIN(RAM) + board.ramwould also have caught this class of bug.Severity: HIGH (silent runtime brick on C6/G6 parts; inconsistent size accounting on RB/208).