diff --git a/README.md b/README.md index 1130017..11fa423 100644 --- a/README.md +++ b/README.md @@ -723,12 +723,14 @@ Active Peripherals: 缓冲区大小等设置在 `libxr_config.yaml` 中。GPIO 以引脚标签命名(`LED1`),UART、I2C、SPI 以小写的外设名 命名(`uart0`、`i2c0`),PWM 为 `pwm_tima1_c0`;`terminal_source` 指定终端使用的串口,未设置时用第一个 UART。 +生成总是包含 `static MSPM0PowerManager power_manager;`,使用 XRobot 时把它登记为 `PowerManager`。 流程、设置与约束的完整说明见 [MSPM0 代码生成](https://xrobot.work/docs/code_gen/mspm0)。 The settings such as the buffer sizes live in `libxr_config.yaml`. GPIO objects are named after the pin labels (`LED1`), UART, I2C and SPI after their lowercase peripheral names (`uart0`, `i2c0`), and a PWM is `pwm_tima1_c0`; `terminal_source` names the UART the terminal uses, the first one when it is not -set. The flow, the settings and their constraints are described in +set. The generation always emits `static MSPM0PowerManager power_manager;`, registered as a +`PowerManager` with XRobot. The flow, the settings and their constraints are described in [MSPM0 code generation](https://xrobot.work/en/docs/code_gen/mspm0). --- @@ -782,11 +784,13 @@ Active Peripherals: ``` 支持 HPM5301(QFN48)和 HPM5361(LQFP100、LQFP64、QFN48)。UART、SPI 等外设目前只识别,生成时列入 -Other。流程、命名与设置的完整说明见 [HPM 代码生成](https://xrobot.work/docs/code_gen/hpm)。 +Other。生成总是包含 `static HPMPowerManager power_manager;`,使用 XRobot 时把它登记为 +`PowerManager`。流程、命名与设置的完整说明见 [HPM 代码生成](https://xrobot.work/docs/code_gen/hpm)。 HPM5301 (QFN48) and HPM5361 (LQFP100, LQFP64, QFN48) are supported. Peripherals such as UART and -SPI are recognized for now and listed under Other in the generation. The flow, the naming and the -settings are described in [HPM code generation](https://xrobot.work/en/docs/code_gen/hpm). +SPI are recognized for now and listed under Other in the generation. The generation always emits +`static HPMPowerManager power_manager;`, registered as a `PowerManager` with XRobot. The flow, the +naming and the settings are described in [HPM code generation](https://xrobot.work/en/docs/code_gen/hpm). --- diff --git a/src/libxr/generator_code_hpm.py b/src/libxr/generator_code_hpm.py index 842c417..4d9b566 100644 --- a/src/libxr/generator_code_hpm.py +++ b/src/libxr/generator_code_hpm.py @@ -93,11 +93,13 @@ "app_main", "main", "timebase", + "power_manager", "PlatformInit", "LibXR", "Thread", "UINT32_MAX", "HPMTimebase", + "HPMPowerManager", "HPMGPIO", "HPMI2C", "HPMPWM", @@ -522,6 +524,7 @@ def _sections( f"{INDENT}// Timebase and platform", f"{INDENT}static HPMTimebase timebase;", f"{INDENT}PlatformInit();", + f"{INDENT}static HPMPowerManager power_manager;", ], _gpio_section(project_data, gpio), _i2c_section(peripherals.get("I2C", [])), @@ -578,7 +581,7 @@ def _includes(project_data: dict, use_xrobot: bool) -> list[str]: and, with XRobot, xrobot_main.hpp; all in alphabetical order. """ peripherals = project_data.get("Peripherals", {}) - drivers = ["hpm_soc.h", "hpm_timebase.hpp"] + drivers = ["hpm_soc.h", "hpm_timebase.hpp", "hpm_power.hpp"] if project_data.get("GPIO"): drivers.append("hpm_gpio.hpp") for kind in ("I2C", "PWM"): @@ -655,14 +658,18 @@ def _check_soc(project_data: dict) -> None: _fail(str(error)) -def initialize_registry() -> None: - """清空生成对象的登记表,使同一次进程中的下一次生成不带上次的对象。 +def initialize_registry(use_xrobot: bool) -> None: + """清空生成对象的登记表,使同一次进程中的下一次生成不带上次的对象;use_xrobot 为真时先 + 登记 power_manager(PowerManager)。 Clear the registry of generated objects, so the next generation in the same process - carries no objects over. + carries no objects over; with use_xrobot, power_manager (PowerManager) is registered + first. """ stm32.registered_devices.clear() stm32.registered_origins.clear() stm32.used_headers.clear() + if use_xrobot: + stm32._register_device("power_manager", "PowerManager", tr("power manager", "电源管理器")) def reset_settings() -> None: @@ -708,7 +715,7 @@ def generate( stm32._settings("I2C") stm32._settings("PWM") stm32._settings("GPIO") - initialize_registry() + initialize_registry(use_xrobot) existing_code = "" if os.path.exists(output_path): diff --git a/src/libxr/generator_code_mspm0.py b/src/libxr/generator_code_mspm0.py index ff3fc91..35eeac1 100644 --- a/src/libxr/generator_code_mspm0.py +++ b/src/libxr/generator_code_mspm0.py @@ -541,7 +541,7 @@ def _includes(project_data: dict, use_xrobot: bool) -> list[str]: XRobot, xrobot_main.hpp; all in alphabetical order. """ peripherals = project_data.get("Peripherals", {}) - drivers = ["mspm0_timebase.hpp"] + drivers = ["mspm0_timebase.hpp", "mspm0_power.hpp"] if project_data.get("GPIO"): drivers.append("mspm0_gpio.hpp") for kind in ("I2C", "PWM", "SPI", "UART"): @@ -853,6 +853,7 @@ def _sections( f"{INDENT}// Timebase and platform", f"{INDENT}static MSPM0Timebase timebase;", f"{INDENT}PlatformInit();", + f"{INDENT}static MSPM0PowerManager power_manager;", ], _gpio_section(gpio), _uart_section(peripherals.get("UART", []), extend), @@ -999,14 +1000,18 @@ def _warn_hand_written(path: str, existing_code: str, generated_code: str) -> No ) -def initialize_registry() -> None: - """清空生成对象的登记表和本次生成用到的头文件,使同一次进程中的下一次生成不带上次的对象。 +def initialize_registry(use_xrobot: bool) -> None: + """清空生成对象的登记表和本次生成用到的头文件,使同一次进程中的下一次生成不带上次的对象; + use_xrobot 为真时先登记 power_manager(PowerManager)。 Clear the registry of generated objects and the headers of this generation, so the next - generation in the same process carries no objects over. + generation in the same process carries no objects over; with use_xrobot, power_manager + (PowerManager) is registered first. """ stm32.registered_devices.clear() stm32.registered_origins.clear() stm32.used_headers.clear() + if use_xrobot: + stm32._register_device("power_manager", "PowerManager", tr("power manager", "电源管理器")) def reset_settings() -> None: @@ -1049,7 +1054,7 @@ def generate( stm32.libxr_settings.pop("SYSTEM", None) stm32._settings("UART") stm32._settings("PWM") - initialize_registry() + initialize_registry(use_xrobot) existing_code = "" if os.path.exists(output_path): diff --git a/tests/data/hpm/hpm5301evklite_expected_app_main.cpp b/tests/data/hpm/hpm5301evklite_expected_app_main.cpp index e7a30b9..f3962ac 100644 --- a/tests/data/hpm/hpm5301evklite_expected_app_main.cpp +++ b/tests/data/hpm/hpm5301evklite_expected_app_main.cpp @@ -7,6 +7,7 @@ #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" @@ -25,6 +26,7 @@ extern "C" void app_main(void) // 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 @@ -43,6 +45,8 @@ extern "C" void app_main(void) 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); diff --git a/tests/data/hpm/rmcs_slave_lite_expected_app_main.cpp b/tests/data/hpm/rmcs_slave_lite_expected_app_main.cpp index 2bca9fc..6855d7b 100644 --- a/tests/data/hpm/rmcs_slave_lite_expected_app_main.cpp +++ b/tests/data/hpm/rmcs_slave_lite_expected_app_main.cpp @@ -6,6 +6,7 @@ #include "libxr.hpp" #include "hpm_gpio.hpp" +#include "hpm_power.hpp" #include "hpm_soc.h" #include "hpm_timebase.hpp" #include "xrobot_main.hpp" @@ -23,6 +24,7 @@ extern "C" void app_main(void) // Timebase and platform static HPMTimebase timebase; PlatformInit(); + static HPMPowerManager power_manager; // GPIO: init_bsp_pins() configured pb11 (PB11) as an output pin, pb13 (PB13) as an // output pin, pb12 (PB12) as an input pin, pb10 (PB10) as an input pin, pa31 (PA31) as @@ -36,6 +38,8 @@ extern "C" void app_main(void) static HPMGPIO pa31(HPM_GPIO0, GPIO_DI_GPIOA, 31, IRQn_GPIO0_A); // Hardware registration + XR_REGISTER(power_manager, LibXR::PowerManager); + XR_REGISTER(pb11, LibXR::GPIO); XR_REGISTER(pb13, LibXR::GPIO); XR_REGISTER(pb12, LibXR::GPIO); diff --git a/tests/data/mspm0/g3507_expected_app_main.cpp b/tests/data/mspm0/g3507_expected_app_main.cpp index 6bdd62c..37117b7 100644 --- a/tests/data/mspm0/g3507_expected_app_main.cpp +++ b/tests/data/mspm0/g3507_expected_app_main.cpp @@ -7,6 +7,7 @@ #include "libxr.hpp" #include "mspm0_gpio.hpp" #include "mspm0_i2c.hpp" +#include "mspm0_power.hpp" #include "mspm0_pwm.hpp" #include "mspm0_spi.hpp" #include "mspm0_timebase.hpp" @@ -63,6 +64,7 @@ extern "C" void app_main(void) // Timebase and platform static MSPM0Timebase timebase; PlatformInit(); + static MSPM0PowerManager power_manager; // GPIO: SysConfig configured LED1 (PB8), LED2 (PA16) as output pins and KEY1 (PB24), // KEY2 (PB20) as input pins. The interrupt edge comes from SysConfig; RegisterCallback @@ -105,6 +107,8 @@ extern "C" void app_main(void) Timer::Start(terminal_task); // Hardware registration + XR_REGISTER(power_manager, LibXR::PowerManager); + XR_REGISTER(LED1, LibXR::GPIO); XR_REGISTER(LED2, LibXR::GPIO); XR_REGISTER(KEY1, LibXR::GPIO); diff --git a/tests/data/mspm0/g3519_expected_app_main.cpp b/tests/data/mspm0/g3519_expected_app_main.cpp index ade37a7..7f54a69 100644 --- a/tests/data/mspm0/g3519_expected_app_main.cpp +++ b/tests/data/mspm0/g3519_expected_app_main.cpp @@ -7,6 +7,7 @@ #include "libxr.hpp" #include "mspm0_gpio.hpp" #include "mspm0_i2c.hpp" +#include "mspm0_power.hpp" #include "mspm0_pwm.hpp" #include "mspm0_spi.hpp" #include "mspm0_timebase.hpp" @@ -66,6 +67,7 @@ extern "C" void app_main(void) // Timebase and platform static MSPM0Timebase timebase; PlatformInit(); + static MSPM0PowerManager power_manager; // GPIO: SysConfig configured LED1 (PA25), LED2 (PA16) as output pins and KEY1 (PB24), // KEY2 (PB20) as input pins. The interrupt edge comes from SysConfig; RegisterCallback @@ -108,6 +110,8 @@ extern "C" void app_main(void) Timer::Start(terminal_task); // Hardware registration + XR_REGISTER(power_manager, LibXR::PowerManager); + XR_REGISTER(LED1, LibXR::GPIO); XR_REGISTER(LED2, LibXR::GPIO); XR_REGISTER(KEY1, LibXR::GPIO); diff --git a/tests/data/mspm0/variants_expected_app_main.cpp b/tests/data/mspm0/variants_expected_app_main.cpp index 3fbf03d..1dc1da8 100644 --- a/tests/data/mspm0/variants_expected_app_main.cpp +++ b/tests/data/mspm0/variants_expected_app_main.cpp @@ -7,6 +7,7 @@ #include "libxr.hpp" #include "mspm0_gpio.hpp" #include "mspm0_i2c.hpp" +#include "mspm0_power.hpp" #include "mspm0_pwm.hpp" #include "mspm0_spi.hpp" #include "mspm0_timebase.hpp" @@ -55,6 +56,7 @@ extern "C" void app_main(void) // Timebase and platform static MSPM0Timebase timebase; PlatformInit(); + static MSPM0PowerManager power_manager; // GPIO: SysConfig configured USER_LED_1 (PA26), USER_LED_2 (PA27) as output pins and // gpio_btn_pin_0 (PA7) as input pins. The interrupt edge comes from SysConfig; @@ -96,6 +98,8 @@ extern "C" void app_main(void) Timer::Start(terminal_task); // Hardware registration + XR_REGISTER(power_manager, LibXR::PowerManager); + XR_REGISTER(USER_LED_1, LibXR::GPIO); XR_REGISTER(USER_LED_2, LibXR::GPIO); XR_REGISTER(gpio_btn_pin_0, LibXR::GPIO); diff --git a/tests/test_generator_code_hpm.py b/tests/test_generator_code_hpm.py index d48527a..3226c70 100644 --- a/tests/test_generator_code_hpm.py +++ b/tests/test_generator_code_hpm.py @@ -114,7 +114,7 @@ class Generation(TestCase): def setUp(self): super().setUp() - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() def generate(self, name: str, use_xrobot: bool = True) -> tuple[Path, str]: @@ -233,6 +233,27 @@ def test_without_xrobot_the_entry_sleeps_and_registers_nothing(self): # The objects are still generated. self.assertIn("static HPMPWM pwm_gptmr0_ch1(", code) + def test_power_manager_is_generated_always_and_registered_only_with_xrobot(self): + # power_manager 对象总是生成,登记只在使用 XRobot 时;GPIO 不能改叫 power_manager。 + # The power_manager object is always generated; it is registered only with XRobot, and + # a GPIO cannot be renamed to power_manager. + root, code = self.generate("hpm5301evklite", use_xrobot=False) + self.assertIn('#include "hpm_power.hpp"', code) + self.assertIn("static HPMPowerManager power_manager;", code) + self.assertNotIn("XR_REGISTER", code) + output = root / "User" / "app_main.cpp" + generator.generate(str(root / ".config.yaml"), str(output), True, "") + self.assertIn( + "XR_REGISTER(power_manager, LibXR::PowerManager);", + output.read_text(encoding="utf-8"), + ) + (root / "User" / "libxr_config.yaml").write_text( + "GPIO:\n pa10: power_manager\n", encoding="utf-8" + ) + with self.assertLogs(level="ERROR") as logs, self.assertRaises(SystemExit): + generator.generate(str(root / ".config.yaml"), str(output), True, "") + self.assertIn("a name of the HPM SDK or of the generated code", "\n".join(logs.output)) + class Notices(TestCase): """解析的两条提示(P3 遗留):main.c 不调用 pinmux 函数,两个活动函数选中同一个 pad。 @@ -478,7 +499,7 @@ class GenerationChecks(TestCase): def setUp(self): super().setUp() - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() def generate(self, config: str | None = None, root: Path | None = None) -> tuple[Path, str]: @@ -546,7 +567,7 @@ def test_unusable_gpio_names_are_listed_together(self): ("1led", "not a valid C++ identifier"), ): with self.subTest(name=name): - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() self.assertGenerationFails(f"GPIO:\n pa10: '{name}'\n", problem) @@ -555,13 +576,13 @@ def test_sdk_macros_and_generated_names_are_refused_and_plain_names_pass(self): # Review C1: function-like macros and names the generated code uses (NORMAL is the # polarity of the PWM) are refused; gpio_led is fine. self.assertGenerationFails("GPIO:\n pa10: MAX\n", "GPIO.pa10 'MAX'") - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() self.assertGenerationFails( "GPIO:\n pa10: NORMAL\n", "the generated code uses the names for something else: NORMAL", ) - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() _root, code = self.generate("GPIO:\n pa10: gpio_led\n pa3: board_key\n") self.assertIn("static HPMGPIO gpio_led(", code) @@ -571,7 +592,7 @@ def test_a_null_or_literal_speed_is_read_as_on_the_other_platforms(self): # Review C3: null takes the default, a string is read as an integer literal. _root, code = self.generate("I2C:\n i2c3:\n speed:\n") self.assertIn("{100000U}", code) - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() _root, code = self.generate("I2C:\n i2c3:\n speed: '0x61A80'\n") self.assertIn("{400000U}", code) @@ -582,7 +603,7 @@ def test_a_gpio_rename_must_be_a_name(self): def test_a_pwm_frequency_must_be_a_positive_integer(self): for value in ("0", "-5", "abc", "1.5", "true"): with self.subTest(value=value): - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() self.assertGenerationFails( f"PWM:\n pwm_gptmr0_ch1:\n frequency: {value}\n", diff --git a/tests/test_generator_code_mspm0.py b/tests/test_generator_code_mspm0.py index fbb4e9a..16b2d9c 100644 --- a/tests/test_generator_code_mspm0.py +++ b/tests/test_generator_code_mspm0.py @@ -445,7 +445,7 @@ class Generation(MSPM0TestCase): def setUp(self): super().setUp() - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() def generate(self, name: str, use_xrobot: bool = True, edit=None) -> tuple[Path, str]: @@ -509,7 +509,7 @@ def failure(self, root: Path) -> str: def test_the_generated_source_matches_the_reviewed_output(self): for name in ("g3507", "g3519", "variants"): with self.subTest(name=name): - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() _root, code = self.generate(name) expected = (DATA / "mspm0" / f"{name}_expected_app_main.cpp").read_text( @@ -617,7 +617,7 @@ def test_terminal_source_none_or_empty_stays_off(self): # picks the first UART. for value in ("none", ""): with self.subTest(value=value): - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() root, code = self.generate("variants") self.assertIn("// Terminal on uart0", code) @@ -706,6 +706,24 @@ def test_a_gpio_named_like_a_generated_object_is_an_error(self): self.failure(root), ) + def test_power_manager_is_generated_always_and_registered_only_with_xrobot(self): + # power_manager 对象总是生成,登记只在使用 XRobot 时;GPIO 不能再叫 power_manager。 + # The power_manager object is always generated; it is registered only with XRobot, and + # a GPIO cannot take the name power_manager. + root, code = self.generate("g3507", use_xrobot=False) + self.assertIn('#include "mspm0_power.hpp"', code) + self.assertIn("static MSPM0PowerManager power_manager;", code) + self.assertNotIn("XR_REGISTER", code) + code = self.regenerate(root) + self.assertIn("XR_REGISTER(power_manager, LibXR::PowerManager);", code) + self.project_yaml( + root, lambda data: data["GPIO"].update(power_manager=data["GPIO"].pop("LED1")) + ) + self.assertIn( + "GPIO name power_manager is already used by the PowerManager object", + self.failure(root), + ) + def test_dma_rx_on_a_uart_main_is_a_warning(self): root, _code = self.generate("variants") self.project_yaml( @@ -823,7 +841,7 @@ class Setup(MSPM0TestCase): def setUp(self): super().setUp() - generator.initialize_registry() + generator.initialize_registry(False) generator.reset_settings() def setup(self, root: Path, *options) -> tuple[int, str]: