HPM Code Generation
The CodeGenerator's libxr hpm setup command brings an HPM Pinmux Tool project to LibXR: it parses the project and generates C++ initialization code. The pin muxing and clocks are configured by the code the Pinmux Tool exports, whose pinmux functions the root main.c calls, and main.c stays BSP-owned; the generated code is the app_main under User/, which constructs the LibXR objects from HPM SDK macros.
Project Recognition
The project is a directory with an app.yaml at its root and exactly one .hpmpc file under boards/<board>/. The selected signals come from the selectPins of the .hpmpc and the pinmux functions that main in the root main.c calls outside preprocessor conditions; functions present in the .hpmpc but never called by main do not take part. Every signal is checked, as the macro name of hpm_iomux.h, against the layout of the SoC package, and a pad the package does not have, or a signal its pad cannot carry, is warned about.
When the gpiom settings assign a pin to a controller other than GPIO0 (gpioController other than 1; unset means GPIO0), no object is generated for it, one warning per pin.
Pinmux functions that main calls only inside preprocessor conditions are not read either (the generated objects must not depend on build options), one warning per function; when every call sits inside a condition, no pin is read. When main calls no pinmux function at all, the parser falls back to init_bsp_pins, and when the project has no such function, no pin is read.
Quick Start
Run in the project root:
libxr hpm setup -d .
The command performs these steps:
- When the project directory has no
.gitignoreyet, create one that ignoresbuild,.history,.cache,CMakeFilesand.config.yaml; an existing.gitignorestays unchanged; - parse the project into
.config.yaml; - generate
app_main.cpp,app_main.handlibxr_config.yamlunderUser/.
When neither --xrobot nor --no-xrobot is given, the output file keeps its choice and a new file does not use XRobot; with XRobot the command generates the XR_REGISTER registrations and XROBOT_MAIN();, and before it ends it suggests running xrobot gen to bring User/xrobot_main.hpp up to date. When the existing app_main.cpp was not generated by libxr gen, setup prints a warning that lists the lines to be replaced; everything outside the User Code regions is regenerated.
Example Output
The following is the first run on a copy of bsp-hpm5301evklite with the three generated files under User/ removed:
$ libxr hpm setup -d . --xrobot
[INFO] Configuration exported to: .\.config.yaml
[INFO] System: bare metal
[INFO] .\User\libxr_config.yaml does not exist; creating it with the default settings
[INFO] Generated User: wrote app_main.cpp, app_main.h, libxr_config.yaml
[INFO] Next: run `xrobot gen` to bring User/xrobot_main.hpp up to date; libxr setup does not update it
===== [Configuration Summary] =====
SoC: HPM5301 (QFN48)
GPIO (2 pins):
pa10: PA10
pa3: PA03
Active Peripherals:
I2C: 1 instance(s)
I2C3
PWM: 1 instance(s)
GPTMR0
Other: 7 instance(s)
UART0
UART3
SPI1
ADC0
ACMP
USBPHY
USB0
Later runs rewrite only the files whose content changes. Other in the summary lists the configurations LibXR has no driver for, see Generated Objects and Naming.
Output Layout
After the run, the project gains or has these files modified:
.
├── .config.yaml # the configuration file written by parsing
├── app.yaml # the HPM SDK project description (BSP-provided)
├── main.c # BSP-provided: calls app_main() after init_bsp_pins() and friends
├── boards/<board>/
│ └── tool_config.hpmpc # the Pinmux Tool project (BSP-provided)
├── User/
│ ├── app_main.cpp # the entry source file
│ ├── app_main.h # the header of the entry source file
│ └── libxr_config.yaml # the LibXR configuration file
└── libxr/ # Git submodule: LibXR itself
app_main.cpp
app_main.cpp contains the LibXR initialization function app_main(). Code between /* User Code Begin N */ and /* User Code End N */ survives regeneration; the rest is regenerated every time. The generated peripheral objects are static and stay valid after app_main() returns; long-lived local objects are declared static. A peripheral object can be passed to a thread or a module as a base-class pointer, see Integrate with XRobot.
The following is the app_main.cpp of that first run:
// Generated by `libxr gen`; do not edit by hand.
// Generated from boards/hpm5301evklite/tool_config.hpmpc (HPM5301); the Pinmux Tool
// configures the pins via init_bsp_pins() in main.c and main.c stays BSP-owned. Put
// application code between the "User Code Begin" and "User Code End" markers.
#include "app_main.h"
#include "libxr.hpp"
#include "hpm_gpio.hpp"
#include "hpm_i2c.hpp"
#include "hpm_power.hpp"
#include "hpm_pwm.hpp"
#include "hpm_soc.h"
#include "hpm_timebase.hpp"
#include "xrobot_main.hpp"
using namespace LibXR;
/* User Code Begin 1 */
/* User Code End 1 */
extern "C" void app_main(void)
{
/* User Code Begin 2 */
/* User Code End 2 */
// Timebase and platform
static HPMTimebase timebase;
PlatformInit();
static HPMPowerManager power_manager;
// GPIO: init_bsp_pins() configured pa10 (PA10) as an output pin, pa3 (PA03) as an input
// pin with a pull-down. The interrupt edge comes from the pinmux and every object
// carries its port IRQ; RegisterCallback and EnableInterrupt belong to the consumer (a
// Module or the User Code).
static HPMGPIO pa10(HPM_GPIO0, GPIO_DI_GPIOA, 10, IRQn_GPIO0_A);
static HPMGPIO pa3(HPM_GPIO0, GPIO_DI_GPIOA, 3, IRQn_GPIO0_A);
// I2C: blocking transfers; the DMA-backed background path runs on dma_mgr.
static HPMI2C i2c3(HPM_I2C3, clock_i2c3, {100000U});
// PWM on a GPTMR comparator: the SoC has no PWM peripheral, so HPMPWM takes its GPTMR
// fallback path; the duty comparator is fixed at 0 and SetConfig is called when
// libxr_config sets a frequency.
static HPMPWM pwm_gptmr0_ch1(reinterpret_cast<LibXRHpmPwmType*>(HPM_GPTMR0),
clock_gptmr0, 1, 0, HPMPWM::Polarity::NORMAL);
// Hardware registration
XR_REGISTER(power_manager, LibXR::PowerManager);
XR_REGISTER(pa10, LibXR::GPIO);
XR_REGISTER(pa3, LibXR::GPIO);
XR_REGISTER(pwm_gptmr0_ch1, LibXR::PWM);
XR_REGISTER(i2c3, LibXR::I2C);
/* User Code Begin 3 */
/* User Code End 3 */
XROBOT_MAIN();
}
The pins and clocks are configured by the pinmux functions main.c calls, and the construction arguments reference SDK macros (HPM_GPIO0, GPIO_DI_GPIOA, IRQn_GPIO0_A, HPM_I2C3, clock_i2c3). A GPIO object carries the IRQ of its port, the port's interrupt service routine comes from the LibXR driver, the edge comes from the pinmux, and registering the callback (RegisterCallback and EnableInterrupt) belongs to the consumer. The HPM drivers of LibXR depend on the dma_mgr component of the SDK, and the project's CMakeLists.txt sets CONFIG_DMA_MGR, as both BSPs do.
Generated Objects and Naming
Objects are generated for the configurations LibXR has drivers for: one object per GPIO pin and one per I2C that selects both SCL and SDA; PWM on a GPTMR is described below. The remaining configurations (UART, SPI, MCAN, ADC, ACMP, USB, an I2C without SCL or SDA, a GPTMR with capture inputs only and so on) are listed under Other in the summary for display only.
The output always contains static HPMPowerManager power_manager; after PlatformInit(); with XRobot it is registered as PowerManager, ahead of the other objects. power_manager is a reserved name, so a GPIO that uses it makes generation fail. The generated code needs a LibXR that carries PowerManager, that is v2.0.1 or newer. The power reset|shutdown|bootloader commands are registered by user code through power_manager.RegisterCommand(ramfs); the generator does not emit that call.
The objects are named:
| Configuration | Object name | Example |
|---|---|---|
| GPIO | p, the lower-case port letter and the line without leading zeros, renamed in the GPIO section | pa10, pa3 (PA03) |
| I2C | the lower-case peripheral name | i2c3 |
| PWM on a GPTMR | pwm_<lower-case instance>_ch<comparator> | pwm_gptmr0_ch1 |
Every comparator (COMP_n) of a GPTMR gets a PWM object through the GPTMR path of HPMPWM; objects are generated only when the SoC has no PWM peripheral, decided from whether the chip's pin data has any PWM signal: HPM5301 has none, HPM5361 has some, and on the latter the GPTMRs are listed under Other for display only. The duty comparator is fixed at 0 and the polarity is NORMAL; the objects and their registrations are generated unconditionally, and wrapping them in a preprocessor condition makes XRobot's generator reject the file.
libxr_config.yaml
User/libxr_config.yaml stores the settings of code generation. A missing file is created with the defaults; later runs fill in the missing settings and keep the values already present. The .hpmpc records pins only, and the speed and frequency come from these settings.
| Section | Keys and defaults | Notes |
|---|---|---|
I2C | speed: 100000 | the bus speed in Hz, one entry per instance keyed by the lower-case instance name; the value must be 100000, 400000 or 1000000 (the steps the HPM I2C driver supports), anything else stops generation with an error |
PWM | frequency (default null) | one entry per object; null calls no SetConfig, a given value must be a positive integer (Hz) |
GPIO | the key is a pin name, the value is the new name | renames a pin; a null value keeps the default name. The section lists every pin the project read |
A null value takes the default; a number may also be written as a string holding an integer literal (such as '0x61A80'). Settings keys match without regard to case, and the spelling already in the file is kept. A new GPIO name must be a valid C++ identifier that is no C++ keyword or reserved identifier and does not collide with an HPM SDK macro, a name of the generated code or another object; every name that breaks this is listed at once and generation fails. When a pin leaves the project, its rename entry is removed if its value is null, and kept with a warning if it holds a name.
The HPM generator creates no UART (the HPM UART driver of LibXR has not been merged yet), so no terminal is generated; a terminal_source written in the file prints a warning.
The following is the User/libxr_config.yaml generated on bsp-hpm5301evklite the first time:
generator: 6.0.1
I2C:
i2c3:
speed: 100000
PWM:
pwm_gptmr0_ch1:
frequency:
GPIO:
pa10:
pa3:
After changing the value of pa10 in the GPIO section to LED and regenerating, the object becomes static HPMGPIO LED(HPM_GPIO0, GPIO_DI_GPIOA, 10, IRQn_GPIO0_A); with the registration updated the same way.
Supported SoCs and Packages
The data currently covers HPM5301 (QFN48) and HPM5361 (LQFP100, LQFP64, QFN48); HPM53M1 is not supported yet. The pin data comes from the hpm_iomux.h and hpm_soc_ip.h of the HPM SDK and the pin tables of the HPM5300 datasheet.
Regenerating Code
After changing the .hpmpc, main.c or User/libxr_config.yaml, run libxr hpm setup -d . again at the project root, or run the two steps:
libxr parse -d .
libxr gen -i .config.yaml -o User/app_main.cpp
.config.yaml is written by libxr parse; a freshly cloned project does not have it, so run libxr parse first. Without --xrobot and --no-xrobot, libxr gen keeps the choice of the output file; afterwards xrobot gen brings User/xrobot_main.hpp up to date. The Generate LibXR Code action of the VS Code extension runs the two commands in sequence, see VS Code Extension.
Options
The options of libxr hpm setup are listed below; libxr hpm setup --help lists the same.
| Option | Description |
|---|---|
-d, --directory | the project directory: an app.yaml at its root and one .hpmpc under boards/ (default: the current directory) |
-o, --output | the output C++ file (default: User/app_main.cpp in DIRECTORY) |
--xrobot / --no-xrobot | generate or skip the XR_REGISTER registrations and XROBOT_MAIN();, see Integrate with XRobot; when neither is given the output file keeps its choice, and a new file does not use XRobot |
--libxr-config | path or URL of libxr_config.yaml (default: the libxr_config.yaml next to the output) |
--verbose | enable debug logging |
Commands
| Command | Description |
|---|---|
libxr parse | parse the project into .config.yaml |
libxr gen | generate the entry source file and app_main.h from .config.yaml, and update libxr_config.yaml |
libxr pins | print the package and pin layout of a SoC, see Pin Layout |
The options and defaults of every command are listed by libxr <command> --help.