From fca10e7b99ce2ce7ce274cdcb2ba5b4759f3bbe1 Mon Sep 17 00:00:00 2001 From: Jiu-xiao <2592509183@qq.com> Date: Sat, 10 Oct 2026 12:58:51 +0100 Subject: [PATCH] Accept the unnumbered DMA trigger macro of an MSPM0 instance SysConfig writes _INST_DMA_TRIGGER without a number while an instance has one DMA trigger, and _0, _1 from two on. _header_dma_channel() matched only the numbered form, so an instance whose .syscfg names no DMA channel (only DMA_CHANNEL_TX.peripheral.$assign, as in the uart_tx_multibyte_fifo_dma_interrupts example of the SDK) found nothing and generation stopped with "no DMA TX channel". Both forms are accepted now. An SPI reaches the same lookup and is covered by the same change. The error message names both forms, and the tests cover the two lookups, an SPI with one event, and the example end to end. --- src/libxr/generator_code_mspm0.py | 10 +- src/libxr/peripheral_analyzer_mspm0.py | 10 +- tests/data/mspm0/uart_tx_dma.syscfg | 57 ++++++ .../data/mspm0/uart_tx_dma_ti_msp_dl_config.c | 171 ++++++++++++++++++ .../data/mspm0/uart_tx_dma_ti_msp_dl_config.h | 135 ++++++++++++++ tests/test_generator_code_mspm0.py | 77 +++++++- 6 files changed, 446 insertions(+), 14 deletions(-) create mode 100644 tests/data/mspm0/uart_tx_dma.syscfg create mode 100644 tests/data/mspm0/uart_tx_dma_ti_msp_dl_config.c create mode 100644 tests/data/mspm0/uart_tx_dma_ti_msp_dl_config.h diff --git a/src/libxr/generator_code_mspm0.py b/src/libxr/generator_code_mspm0.py index 35eeac1..458d5ce 100644 --- a/src/libxr/generator_code_mspm0.py +++ b/src/libxr/generator_code_mspm0.py @@ -607,13 +607,15 @@ def _uart_section(uarts: list, extend: set[str]) -> list[str]: tr( f"{instance}: no DMA TX channel in the SysConfig project (UART > DMA " "Configuration: Enable DMA TX with the TX trigger, which also gives the " - f"{instance}_INST_DMA_TRIGGER_n and _CHAN_ID macros); the MSPM0 UART " + f"_CHAN_ID and {instance}_INST_DMA_TRIGGER macros, the latter " + "numbered _0, _1 when the instance has more than one trigger); the MSPM0 UART " "driver constructs only with DMA TX. Enable it and run `libxr parse` again, " "or drop the UART", f"{instance}:SysConfig 工程里没有 DMA TX 通道(UART > DMA Configuration:" - f"启用 DMA TX 并选 TX 触发,SysConfig 随之生成 {instance}_INST_DMA_TRIGGER_n " - "和 <通道>_CHAN_ID 宏);MSPM0 的 UART 驱动只支持带 DMA TX 的构造。请启用后" - "重新运行 `libxr parse`,或删去这个 UART", + f"启用 DMA TX 并选 TX 触发,SysConfig 随之生成 <通道>_CHAN_ID 和 " + f"{instance}_INST_DMA_TRIGGER 宏,实例有多个触发时带编号 _0、_1);MSPM0 的 " + "UART 驱动只支持带 DMA TX 的构造。请启用后重新运行 `libxr parse`,或删去" + "这个 UART", ) ) settings = _instance_settings("UART", name, instance in extend) diff --git a/src/libxr/peripheral_analyzer_mspm0.py b/src/libxr/peripheral_analyzer_mspm0.py index fd18079..3cb5e61 100644 --- a/src/libxr/peripheral_analyzer_mspm0.py +++ b/src/libxr/peripheral_analyzer_mspm0.py @@ -616,16 +616,18 @@ def _gpio_entries( def _header_dma_channel(name: str, direction: str, defines: dict[str, str]) -> str | None: """SysConfig 输出里实例 name 在 direction(TX/RX)方向的 DMA 通道名:每个 - _INST_DMA_TRIGGER_n 前面紧挨着它的通道的 <通道>_CHAN_ID。没有时为 None。 + _INST_DMA_TRIGGER 或 _INST_DMA_TRIGGER_n 前面紧挨着它的通道的 <通道>_CHAN_ID + (一个实例只有一个触发时 SysConfig 不写编号,两个及以上才写 _0、_1)。没有时为 None。 The DMA channel name of the instance name in direction (TX/RX) in the SysConfig output: - every _INST_DMA_TRIGGER_n directly follows the _CHAN_ID of its channel. None - when there is none. + every _INST_DMA_TRIGGER or _INST_DMA_TRIGGER_n directly follows the + _CHAN_ID of its channel (SysConfig writes no number while the instance has one + trigger, and _0, _1 from two on). None when there is none. """ channel = None for macro, value in defines.items(): if macro.endswith("_CHAN_ID"): channel = macro[: -len("_CHAN_ID")] - elif re.fullmatch(rf"{re.escape(name)}_INST_DMA_TRIGGER_\d+", macro): + elif re.fullmatch(rf"{re.escape(name)}_INST_DMA_TRIGGER(?:_\d+)?", macro): trigger = DMA_TRIGGER.fullmatch(value) if trigger and trigger.group(1) == direction and channel: return channel diff --git a/tests/data/mspm0/uart_tx_dma.syscfg b/tests/data/mspm0/uart_tx_dma.syscfg new file mode 100644 index 0000000..9470a09 --- /dev/null +++ b/tests/data/mspm0/uart_tx_dma.syscfg @@ -0,0 +1,57 @@ +/** + * These arguments were used when this file was generated. They will be automatically applied on subsequent loads + * via the GUI or CLI. Run CLI with '--help' for additional information on how to override these arguments. + */ +//@cliArgs --device "MSPM0G350X" --package "LQFP-64(PM)" --part "Default" +//@v2CliArgs --device "MSPM0G3507" --package "LQFP-64(PM)" +// @cliArgs --board /ti/boards/LP_MSPM0G3507 --rtos nortos + +/** + * Import the modules used in this configuration. + */ +const UART = scripting.addModule("/ti/driverlib/UART", {}, false); +const UART1 = UART.addInstance(); + +const GPIO = scripting.addModule("/ti/driverlib/GPIO", {}, false); +const GPIO1 = GPIO.addInstance(); + +const SYSCTL = scripting.addModule("/ti/driverlib/SYSCTL"); + +/** + * Write custom configuration values to the imported modules. + */ + +UART1.$name = "UART_0"; +UART1.profile = "CONFIG_PROFILE_1"; +UART1.peripheral.$assign = "UART0"; +UART1.targetBaudRate = 9600; +UART1.enableInternalLoopback = false; +UART1.enabledInterrupts = ["DMA_DONE_TX", "EOT_DONE"]; +UART1.enableFIFO = true; +UART1.txFifoThreshold = "DL_UART_TX_FIFO_LEVEL_ONE_ENTRY"; +UART1.enabledDMATXTriggers = "DL_UART_DMA_INTERRUPT_TX"; +UART1.enableDMATX = true; + +UART1.peripheral.rxPin.$assign = "PA11"; +UART1.peripheral.txPin.$assign = "PA10"; + +UART1.DMA_CHANNEL_TX.peripheral.$assign = "DMA_CH0"; +UART1.DMA_CHANNEL_TX.addressMode = "b2f"; +UART1.DMA_CHANNEL_TX.srcIncDec = "INCREMENT"; +UART1.DMA_CHANNEL_TX.dstLength = "BYTE"; +UART1.DMA_CHANNEL_TX.srcLength = "BYTE"; + +GPIO1.$name = "GPIO_LEDS"; +GPIO1.associatedPins.create(2); +GPIO1.associatedPins[0].$name = "USER_LED_1"; +GPIO1.associatedPins[0].assignedPort = "PORTA"; +GPIO1.associatedPins[0].initialValue = "SET"; +GPIO1.associatedPins[0].assignedPin = "0"; +GPIO1.associatedPins[0].pin.$assign = "PA0"; +GPIO1.associatedPins[1].$name = "USER_TEST"; +GPIO1.associatedPins[1].assignedPort = "PORTA"; +GPIO1.associatedPins[1].assignedPin = "15"; +GPIO1.associatedPins[1].initialValue = "SET"; + +SYSCTL.clockTreeEn = true; +SYSCTL.forceDefaultClkConfig = true; diff --git a/tests/data/mspm0/uart_tx_dma_ti_msp_dl_config.c b/tests/data/mspm0/uart_tx_dma_ti_msp_dl_config.c new file mode 100644 index 0000000..7c1aa00 --- /dev/null +++ b/tests/data/mspm0/uart_tx_dma_ti_msp_dl_config.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2023, Texas Instruments Incorporated + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Texas Instruments Incorporated nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ============ ti_msp_dl_config.c ============= + * Configured MSPM0 DriverLib module definitions + * + * DO NOT EDIT - This file is generated for the LP_MSPM0G3507 + * by the SysConfig tool. + */ + +#include "ti_msp_dl_config.h" + +/* + * ======== SYSCFG_DL_init ======== + * Perform any initialization needed before using any board APIs + */ +SYSCONFIG_WEAK void SYSCFG_DL_init(void) +{ + SYSCFG_DL_initPower(); + SYSCFG_DL_GPIO_init(); + /* Module-Specific Initializations*/ + SYSCFG_DL_SYSCTL_init(); + SYSCFG_DL_UART_0_init(); + SYSCFG_DL_DMA_init(); +} + + + +SYSCONFIG_WEAK void SYSCFG_DL_initPower(void) +{ + DL_GPIO_reset(GPIOA); + DL_GPIO_reset(GPIOB); + DL_UART_Main_reset(UART_0_INST); + + + DL_GPIO_enablePower(GPIOA); + DL_GPIO_enablePower(GPIOB); + DL_UART_Main_enablePower(UART_0_INST); + + delay_cycles(POWER_STARTUP_DELAY); +} + +SYSCONFIG_WEAK void SYSCFG_DL_GPIO_init(void) +{ + + DL_GPIO_initPeripheralOutputFunction( + GPIO_UART_0_IOMUX_TX, GPIO_UART_0_IOMUX_TX_FUNC); + DL_GPIO_initPeripheralInputFunction( + GPIO_UART_0_IOMUX_RX, GPIO_UART_0_IOMUX_RX_FUNC); + + DL_GPIO_initDigitalOutput(GPIO_LEDS_USER_LED_1_IOMUX); + + DL_GPIO_initDigitalOutput(GPIO_LEDS_USER_TEST_IOMUX); + + DL_GPIO_setPins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN | + GPIO_LEDS_USER_TEST_PIN); + DL_GPIO_enableOutput(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN | + GPIO_LEDS_USER_TEST_PIN); + +} + + + +SYSCONFIG_WEAK void SYSCFG_DL_SYSCTL_init(void) +{ + + //Low Power Mode is configured to be SLEEP0 + DL_SYSCTL_setBORThreshold(DL_SYSCTL_BOR_THRESHOLD_LEVEL_0); + + + DL_SYSCTL_setSYSOSCFreq(DL_SYSCTL_SYSOSC_FREQ_BASE); + /* Set default configuration */ + DL_SYSCTL_disableHFXT(); + DL_SYSCTL_disableSYSPLL(); + +} + + +static const DL_UART_Main_ClockConfig gUART_0ClockConfig = { + .clockSel = DL_UART_MAIN_CLOCK_BUSCLK, + .divideRatio = DL_UART_MAIN_CLOCK_DIVIDE_RATIO_1 +}; + +static const DL_UART_Main_Config gUART_0Config = { + .mode = DL_UART_MAIN_MODE_NORMAL, + .direction = DL_UART_MAIN_DIRECTION_TX_RX, + .flowControl = DL_UART_MAIN_FLOW_CONTROL_NONE, + .parity = DL_UART_MAIN_PARITY_NONE, + .wordLength = DL_UART_MAIN_WORD_LENGTH_8_BITS, + .stopBits = DL_UART_MAIN_STOP_BITS_ONE +}; + +SYSCONFIG_WEAK void SYSCFG_DL_UART_0_init(void) +{ + DL_UART_Main_setClockConfig(UART_0_INST, (DL_UART_Main_ClockConfig *) &gUART_0ClockConfig); + + DL_UART_Main_init(UART_0_INST, (DL_UART_Main_Config *) &gUART_0Config); + /* + * Configure baud rate by setting oversampling and baud rate divisors. + * Target baud rate: 9600 + * Actual baud rate: 9600.24 + */ + DL_UART_Main_setOversampling(UART_0_INST, DL_UART_OVERSAMPLING_RATE_16X); + DL_UART_Main_setBaudRateDivisor(UART_0_INST, UART_0_IBRD_32_MHZ_9600_BAUD, UART_0_FBRD_32_MHZ_9600_BAUD); + + + /* Configure Interrupts */ + DL_UART_Main_enableInterrupt(UART_0_INST, + DL_UART_MAIN_INTERRUPT_DMA_DONE_TX | + DL_UART_MAIN_INTERRUPT_EOT_DONE); + + /* Configure DMA Transmit Event */ + DL_UART_Main_enableDMATransmitEvent(UART_0_INST); + /* Configure FIFOs */ + DL_UART_Main_enableFIFOs(UART_0_INST); + DL_UART_Main_setRXFIFOThreshold(UART_0_INST, DL_UART_RX_FIFO_LEVEL_ONE_ENTRY); + DL_UART_Main_setTXFIFOThreshold(UART_0_INST, DL_UART_TX_FIFO_LEVEL_ONE_ENTRY); + + DL_UART_Main_enable(UART_0_INST); +} + +static const DL_DMA_Config gDMA_CH0Config = { + .transferMode = DL_DMA_SINGLE_TRANSFER_MODE, + .extendedMode = DL_DMA_NORMAL_MODE, + .destIncrement = DL_DMA_ADDR_UNCHANGED, + .srcIncrement = DL_DMA_ADDR_INCREMENT, + .destWidth = DL_DMA_WIDTH_BYTE, + .srcWidth = DL_DMA_WIDTH_BYTE, + .trigger = UART_0_INST_DMA_TRIGGER, + .triggerType = DL_DMA_TRIGGER_TYPE_EXTERNAL, +}; + +SYSCONFIG_WEAK void SYSCFG_DL_DMA_CH0_init(void) +{ + DL_DMA_initChannel(DMA, DMA_CH0_CHAN_ID , (DL_DMA_Config *) &gDMA_CH0Config); +} +SYSCONFIG_WEAK void SYSCFG_DL_DMA_init(void){ + SYSCFG_DL_DMA_CH0_init(); +} + + diff --git a/tests/data/mspm0/uart_tx_dma_ti_msp_dl_config.h b/tests/data/mspm0/uart_tx_dma_ti_msp_dl_config.h new file mode 100644 index 0000000..3655e66 --- /dev/null +++ b/tests/data/mspm0/uart_tx_dma_ti_msp_dl_config.h @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2023, Texas Instruments Incorporated - http://www.ti.com + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Texas Instruments Incorporated nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * ============ ti_msp_dl_config.h ============= + * Configured MSPM0 DriverLib module declarations + * + * DO NOT EDIT - This file is generated for the LP_MSPM0G3507 + * by the SysConfig tool. + */ +#ifndef ti_msp_dl_config_h +#define ti_msp_dl_config_h + +#define CONFIG_LP_MSPM0G3507 +#define CONFIG_MSPM0G3507 + +#if defined(__ti_version__) || defined(__TI_COMPILER_VERSION__) +#define SYSCONFIG_WEAK __attribute__((weak)) +#elif defined(__IAR_SYSTEMS_ICC__) +#define SYSCONFIG_WEAK __weak +#elif defined(__GNUC__) +#define SYSCONFIG_WEAK __attribute__((weak)) +#endif + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * ======== SYSCFG_DL_init ======== + * Perform all required MSP DL initialization + * + * This function should be called once at a point before any use of + * MSP DL. + */ + + +/* clang-format off */ + +#define POWER_STARTUP_DELAY (16) + + + +#define CPUCLK_FREQ 32000000 + + + +/* Defines for UART_0 */ +#define UART_0_INST UART0 +#define UART_0_INST_FREQUENCY 32000000 +#define UART_0_INST_IRQHandler UART0_IRQHandler +#define UART_0_INST_INT_IRQN UART0_INT_IRQn +#define GPIO_UART_0_RX_PORT GPIOA +#define GPIO_UART_0_TX_PORT GPIOA +#define GPIO_UART_0_RX_PIN DL_GPIO_PIN_11 +#define GPIO_UART_0_TX_PIN DL_GPIO_PIN_10 +#define GPIO_UART_0_IOMUX_RX (IOMUX_PINCM22) +#define GPIO_UART_0_IOMUX_TX (IOMUX_PINCM21) +#define GPIO_UART_0_IOMUX_RX_FUNC IOMUX_PINCM22_PF_UART0_RX +#define GPIO_UART_0_IOMUX_TX_FUNC IOMUX_PINCM21_PF_UART0_TX +#define UART_0_BAUD_RATE (9600) +#define UART_0_IBRD_32_MHZ_9600_BAUD (208) +#define UART_0_FBRD_32_MHZ_9600_BAUD (21) + + + + + +/* Defines for DMA_CH0 */ +#define DMA_CH0_CHAN_ID (0) +#define UART_0_INST_DMA_TRIGGER (DMA_UART0_TX_TRIG) + + +/* Port definition for Pin Group GPIO_LEDS */ +#define GPIO_LEDS_PORT (GPIOA) + +/* Defines for USER_LED_1: GPIOA.0 with pinCMx 1 on package pin 33 */ +#define GPIO_LEDS_USER_LED_1_PIN (DL_GPIO_PIN_0) +#define GPIO_LEDS_USER_LED_1_IOMUX (IOMUX_PINCM1) +/* Defines for USER_TEST: GPIOA.15 with pinCMx 37 on package pin 8 */ +#define GPIO_LEDS_USER_TEST_PIN (DL_GPIO_PIN_15) +#define GPIO_LEDS_USER_TEST_IOMUX (IOMUX_PINCM37) + + +/* clang-format on */ + +void SYSCFG_DL_init(void); +void SYSCFG_DL_initPower(void); +void SYSCFG_DL_GPIO_init(void); +void SYSCFG_DL_SYSCTL_init(void); +void SYSCFG_DL_UART_0_init(void); +void SYSCFG_DL_DMA_init(void); + + +bool SYSCFG_DL_saveConfiguration(void); +bool SYSCFG_DL_restoreConfiguration(void); + +#ifdef __cplusplus +} +#endif + +#endif /* ti_msp_dl_config_h */ diff --git a/tests/test_generator_code_mspm0.py b/tests/test_generator_code_mspm0.py index 16b2d9c..4964e85 100644 --- a/tests/test_generator_code_mspm0.py +++ b/tests/test_generator_code_mspm0.py @@ -8,16 +8,19 @@ 没有 SysConfig,输出随工程一起提交),以及一个覆盖 BSP 没用到的 SysConfig 写法的 variants 工程 (SPI 用 MFCLK 分频和 RX timeout DMA 触发、外设模式的 SPI、只有目标模式的 I2C、默认实例名且 引脚只有建议值的 PWM、端口加引脚号的 GPIO、默认名 PIN_0 的引脚、收发都有 DMA 的 UART),它的 -输出同样由 SysConfig 1.28.1 生成。测试把固件复制进临时工程,使生成输出的修改时间比 .syscfg 新, -解析直接复用它;测试期间不设置 SYSCONFIG_TOOL 和 MSPM0_SDK_INSTALL_DIR。 +输出同样由 SysConfig 1.28.1 生成,以及 TI SDK 的 uart_tx_multibyte_fifo_dma_interrupts 例程 +(一个实例只有一个 DMA 触发,输出的触发宏不带编号)。测试把固件复制进临时工程,使生成输出的 +修改时间比 .syscfg 新,解析直接复用它;测试期间不设置 SYSCONFIG_TOOL 和 MSPM0_SDK_INSTALL_DIR。 The fixtures are the real project files of the two BSPs: the .syscfg plus the ti_msp_dl_config.h/.c that SysConfig generated (CI has no SysConfig, so the output is committed -with the project), and a variants project with the SysConfig setups the BSPs do not use (an SPI +with the project), a variants project with the SysConfig setups the BSPs do not use (an SPI on MFCLK with a divider and an RX timeout DMA trigger, an SPI in peripheral mode, an I2C in target mode only, a PWM with the default instance name and only suggested pins, GPIO pins given as port plus pin number, a pin with the default name PIN_0, a UART with DMA on both sides), -whose output SysConfig 1.28.1 generated as well. A test copies the fixtures into a temporary -project, so the generated output is newer than the .syscfg and parsing reuses it; +whose output SysConfig 1.28.1 generated as well, and the uart_tx_multibyte_fifo_dma_interrupts +example of the TI SDK (an instance with a single DMA trigger, so its trigger macro carries no +number). A test copies the fixtures into a temporary project, so the generated output is newer +than the .syscfg and parsing reuses it; SYSCONFIG_TOOL and MSPM0_SDK_INSTALL_DIR are unset during the tests. """ @@ -51,6 +54,7 @@ def bsp(name: str) -> str: "g3507": "mspm0g3507_minidb48.syscfg", "g3519": "mspm0g3519_minidb48.syscfg", "variants": "mspm0_variants.syscfg", + "uart_tx_dma": "uart_tx_multibyte_fifo_dma_interrupts.syscfg", }[name] @@ -168,6 +172,17 @@ def test_the_second_uart_of_the_other_board_is_parsed_as_well(self): self.assertEqual(peripherals["UART"]["UART_7"]["DMA_TX"], "DMA_CH_UART7_TX") self.assertEqual(peripherals["UART"]["UART_7"]["PowerDomain"], 0) + def test_a_single_dma_trigger_of_the_output_names_the_channel(self): + # TI 的 uart_tx_multibyte_fifo_dma_interrupts 例程:.syscfg 只写了 + # DMA_CHANNEL_TX.peripheral.$assign,输出里的触发宏只有一个,因而不带编号 + # (UART_0_INST_DMA_TRIGGER)。 + # TI's uart_tx_multibyte_fifo_dma_interrupts example: the .syscfg names only + # DMA_CHANNEL_TX.peripheral.$assign, and the output holds a single trigger macro, + # which therefore carries no number (UART_0_INST_DMA_TRIGGER). + uart = self.parse("uart_tx_dma")["Peripherals"]["UART"]["UART_0"] + self.assertEqual(uart["DMA_TX"], "DMA_CH0") + self.assertEqual(uart["Peripheral"], "UART0") + class ParsingVariants(MSPM0TestCase): """libxr parse 对 BSP 没用到的 SysConfig 写法(variants 工程,审查 2026-10-07 的 H1-H6、 @@ -780,10 +795,24 @@ def test_a_uart_without_dma_tx_names_the_sysconfig_option(self): self.project_yaml(root, lambda data: data["Peripherals"]["UART"]["UART_0"].pop("DMA_TX")) self.assertIn( "UART_0: no DMA TX channel in the SysConfig project (UART > DMA Configuration: " - "Enable DMA TX", + "Enable DMA TX with the TX trigger, which also gives the _CHAN_ID and " + "UART_0_INST_DMA_TRIGGER macros, the latter numbered _0, _1 when the instance has " + "more than one trigger)", self.failure(root), ) + def test_the_ti_example_with_one_dma_trigger_generates(self): + # TI 例程端到端:.syscfg 加 SysConfig 输出解析出 DMA_CH0(触发宏不带编号),生成出来的 + # UART 用它构造。 + # The TI example end to end: its .syscfg and SysConfig output parse to DMA_CH0 (the + # trigger macro carries no number), and the generated UART is constructed with it. + root = self.project("uart_tx_dma") + parse_project(str(root), summary=False) + (root / "User").mkdir() + code = self.regenerate(root) + self.assertIn("MSPM0_UART_MAIN_INIT(UART_0, DMA_CH0,", code) + self.assertIn("#define DMA_CH0_LIBXR_UART_IRQN UART_0_INST_INT_IRQN", code) + def test_different_frequencies_of_one_timer_are_a_warning(self): # L7 root, _code = self.generate("variants") @@ -926,3 +955,39 @@ def test_the_clock_configurations_of_every_kind_are_read(self): sorted(clocks), ["I2C_0", "I2C_TARGET", "PWM_0", "SPI_1", "SPI_PERIPH", "UART_0"] ) self.assertEqual(clocks["UART_0"]["clockSel"], "DL_UART_MAIN_CLOCK_BUSCLK") + + def test_a_dma_trigger_without_a_number_names_the_channel_too(self): + # SysConfig 在一个实例只有一个 DMA 触发时不写编号;通道名是触发宏前面紧挨着的 + # <通道>_CHAN_ID。 + # SysConfig writes no number while an instance has one DMA trigger; the channel is the + # _CHAN_ID directly before the trigger macro. + defines = { + "DMA_CH0_CHAN_ID": "(0)", + "UART_0_INST_DMA_TRIGGER": "(DMA_UART0_TX_TRIG)", + } + self.assertEqual(analyzer._header_dma_channel("UART_0", "TX", defines), "DMA_CH0") + self.assertIsNone(analyzer._header_dma_channel("UART_0", "RX", defines)) + + def test_an_spi_with_one_dma_event_uses_the_same_lookup(self): + # SPI 和 UART 走同一个查找:只开一个 DMA 事件时它的触发宏也不带编号。 + # An SPI uses the same lookup as a UART: with a single DMA event its trigger macro + # carries no number either. + defines = { + "DMA_CH_SPI1_RX_CHAN_ID": "(3)", + "SPI_1_INST_DMA_TRIGGER": "(DMA_SPI1_RX_TRIG)", + } + self.assertEqual(analyzer._header_dma_channel("SPI_1", "RX", defines), "DMA_CH_SPI1_RX") + self.assertIsNone(analyzer._header_dma_channel("SPI_1", "TX", defines)) + + def test_a_numbered_dma_trigger_names_the_channel_as_before(self): + # 两个及以上触发时宏带编号,两个方向各取自己前面的通道。 + # From two triggers on the macros carry numbers, and each direction takes the channel + # before its own trigger. + defines = { + "DMA_CH_UART0_TX_CHAN_ID": "(1)", + "UART_0_INST_DMA_TRIGGER_0": "(DMA_UART0_TX_TRIG)", + "DMA_CH_UART0_RX_CHAN_ID": "(0)", + "UART_0_INST_DMA_TRIGGER_1": "(DMA_UART0_RX_TRIG)", + } + self.assertEqual(analyzer._header_dma_channel("UART_0", "TX", defines), "DMA_CH_UART0_TX") + self.assertEqual(analyzer._header_dma_channel("UART_0", "RX", defines), "DMA_CH_UART0_RX")