Coregen
Coregen turns a project’s declarative config.json into the C headers and init code that wire your chosen Core’s pins, clocks, buses, and tiles — so you write application logic, not boilerplate.
Overview
Coregen (tools/coregen/coregen.py) reads two inputs — a Core’s canonical definition (the hardware description, from definitions/) and your project’s config.json — and emits the headers and init source that configure GPIO, clocks, peripherals (I²C / SPI / USART / timers / ADC / DAC), and tile drivers.
config.json is the single source of truth for a project’s hardware setup. You never hand-edit generated files — change the config and re-run. Coregen runs automatically during make (or on its own with make generate), validating the config against the target Core’s capabilities first and exiting with an actionable error — bad pad, unavailable function, unreachable clock speed — before any compile.
MCU_DB inside coregen.py. The core field takes a public name — Core.ST.L0.1, Core.ST.L4.1, Core.ST.L4.2, Core.ST.W5, Core.ST.H5.1 — which resolves to the matching definition file.What it generates
Three baseline headers come from the Core definition alone; the rest are emitted when you pass a config.json (the normal build path), and the tile outputs only when the project declares tiles:
core_pads.h—PAD_n_PORT/PAD_n_PIN(and timer-pad info) for every pad.core_board.h— board-level defines (LED pad, power rails, debug).core_interfaces.h— per-signal alternate-function constants (e.g.I2C1_CLK_AF).core_config.h—SYSCLK_MHZ, PLL M/N/R, and the resolved pad assignments.core_init.c/core_init.h—core_clock_init(),core_pads_init(), and per-peripheral + tile init.core.h— the master include (pulls in all of the above); written next to yourmain.c.tile_handles.h+core_drivers.mk— tile handle variables and the driver source list (only withtiles).
tile_handles.h is smart-merged — anything you add outside its coregen:begin/coregen:end markers is preserved.config.json
Every project carries a config.json in its directory (the filename is literal; the project’s name comes from the directory). A representative config:
{
"core": "Core.ST.L4.2", // public Core name (resolves to a definition)
"clock": "max", // "default" or a tile clock option (low/medium/high/max)
"pads": { // pad number → function from the Core's definition
"4": "I2C1.CLK",
"5": "I2C1.DAT",
"9": "GPIO.OUT",
"12": "ADC7",
"13": "TIM2.1"
},
"gpio": { // optional per-pad overrides
"9": { "default": "high", "output_type": "push-pull" },
"5": { "pull": "up", "exti": "falling" }
},
"interfaces": { // tuning for the buses your pads imply
"I2C1": { "speed": 400000, "pullups": true },
"TIM2": { "freq": 1000 }
},
"tiles": [ // tile drivers bound to a configured bus
{ "tile": "Sense.BP", "bus": "I2C1", "instance": 0 }
],
"bootloader": "none" // "none" | "custom" | "rom"
}The fields
core— the public Core name (resolves to adefinitions/<stem>.json).clock— a performance level the Core defines ("default"picks its schema default); coregen solves the PLL to hit it and applies any required voltage scaling.pads— pad number → a function the Core’s definition exposes, plus the syntheticGPIO.OUT/GPIO.IN. Coregen infers which peripherals are in use from these.gpio— optional per-pad overrides (pull,output_type,speed,exti,default).interfaces— tuning per bus:I2Cn.speed/pullups,SPIn.mode/prescaler,USARTn.baud,TIMn.freq.tiles— each entry binds a tile (tile) to an enabledbuswith aninstance(SPI tiles also takecs_pad).bootloader—"none"(SWD) /"custom"/"rom"; drives the flash path. See Bootloading.
Build flow
Coregen is step one of the build:
- coregen validates
config.jsonagainst the Core and writes the generated files (exits non-zero on any error). - compiler builds
main.c+ SDK HAL +core_init.c+ the tile drivers fromcore_drivers.mk. - linker uses the MCU-specific linker script from
sdk/device/.
make generate # run coregen only, no compile
make # full build for the default Core
make TILE=Core.ST.H5.1 PROJECT=my-proj # specific Core + projectExtending coregen
Coregen is one Python file plus Jinja2 templates. The pieces you touch most:
MCU_DB— add a new MCU (define, family, CPU flags, PLL ranges).- the
build_*_config()functions — one per config section (pads, clock, I²C/SPI/USART/timer/ADC, tiles) +validate_project_config(). TILE_DRIVER_MAP— register a tile driver so atiles[].tilevalue resolves to its header/source.- templates in
tools/coregen/templates/— the context comes from thectxdict ingenerate().
To expose a driver’s functions to Studio and the docs, annotate its header — see the @studio annotation reference.

