From 79bb2e8f9852d7771142229f45ea06ce3cef8d28 Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Mon, 1 Jun 2026 18:15:29 +0200 Subject: [PATCH 01/11] Add STM32 device headers via RIM Import the STM32F4 and STM32G4 CMSIS device headers from STMicroelectronics' dedicated RIM-tracked repositories. Keep only the F413 and G474 headers needed by the STM32 MCU foundation and record the unused upstream headers as RIM ignores. --- NOTICE.md | 1 + .../bsp/bspMcu/include/3rdparty/st/LICENSE | 202 + .../include/3rdparty/st/stm32f4/.riminfo | 15 + .../include/3rdparty/st/stm32f4/stm32f413xx.h | 15466 +++++++++++++ .../include/3rdparty/st/stm32f4/stm32f4xx.h | 301 + .../3rdparty/st/stm32f4/system_stm32f4xx.h | 104 + .../include/3rdparty/st/stm32g4/.riminfo | 15 + .../include/3rdparty/st/stm32g4/stm32g474xx.h | 18129 ++++++++++++++++ .../include/3rdparty/st/stm32g4/stm32g4xx.h | 269 + .../3rdparty/st/stm32g4/system_stm32g4xx.h | 104 + 10 files changed, 34606 insertions(+) create mode 100644 platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE create mode 100644 platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/.riminfo create mode 100644 platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/stm32f413xx.h create mode 100644 platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/stm32f4xx.h create mode 100644 platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/system_stm32f4xx.h create mode 100644 platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32g4/.riminfo create mode 100644 platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32g4/stm32g474xx.h create mode 100644 platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32g4/stm32g4xx.h create mode 100644 platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32g4/system_stm32g4xx.h diff --git a/NOTICE.md b/NOTICE.md index ca162642261..9be29d742b7 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -47,6 +47,7 @@ We recommend to read their licenses, as their terms may differ from the terms de | ThreadX Linux Port | 6.4.3 | MIT | ``platforms/posix/3rdparty/threadx/LICENSE.md`` | | CMSIS | 6.1.0 | Apache v2 | ``libs/3rdparty/cmsis/LICENSE`` | | NXP S32K148 Headers | 1.1a | BSD-3 | ``platforms/s32k1xx/bsp/bspMcu/include/3rdparty/nxp/*.h`` | +| ST STM32 Device Headers | 2.6.11 / 1.2.6 | Apache v2 | ``platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE`` | | CodeCoverage | | BSD-3 | ``cmake/modules/CodeCoverage.cmake`` | ## MISRA diff --git a/platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE new file mode 100644 index 00000000000..7a4a3ea2424 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/.riminfo b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/.riminfo new file mode 100644 index 00000000000..25fdf37d82b --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/.riminfo @@ -0,0 +1,15 @@ +efaecd478788406b1d98ba8e37809c6ff9957ebe + +RIM Info file. You're welcome to read but don't write it. +Instead, use RIM commands to do the things you want to do. +BEWARE: Any manual modification will invalidate the file! + +remote_url : https://github.com/STMicroelectronics/cmsis_device_f4.git +revision_sha1 : b364e5ad14523994ebb30f9e854a7c28537b0c28 +target_revision: v2.6.11 +ignores : stm32f401xc.h,stm32f401xe.h,stm32f405xx.h,stm32f407xx.h,stm32f410cx.h,stm32f410rx.h,stm32f410tx.h,stm32f411xe.h,stm32f412cx.h,stm32f412rx.h,stm32f412vx.h,stm32f412zx.h,stm32f415xx.h,stm32f417xx.h,stm32f423xx.h,stm32f427xx.h,stm32f429xx.h,stm32f437xx.h,stm32f439xx.h,stm32f446xx.h,stm32f469xx.h,stm32f479xx.h +checksum : d95d4934b47fee01f36cd076c1bb8af7ce8a228e +subdir : Include + +committer_date : 2025-05-20 15:35:16 +0100 + diff --git a/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/stm32f413xx.h b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/stm32f413xx.h new file mode 100644 index 00000000000..7a5ed4e7cef --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/include/3rdparty/st/stm32f4/stm32f413xx.h @@ -0,0 +1,15466 @@ +/** + ****************************************************************************** + * @file stm32f413xx.h + * @author MCD Application Team + * @brief CMSIS STM32F413xx Device Peripheral Access Layer Header File. + * + * This file contains: + * - Data structures and the address mapping for all peripherals + * - peripherals registers declarations and bits definition + * - Macros to access peripheral's registers hardware + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32f413xx + * @{ + */ + +#ifndef __STM32F413xx_H +#define __STM32F413xx_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief Configuration of the Cortex-M4 Processor and Core Peripherals + */ +#define __CM4_REV 0x0001U /*!< Core revision r0p1 */ +#define __MPU_PRESENT 1U /*!< STM32F4XX provides an MPU */ +#define __NVIC_PRIO_BITS 4U /*!< STM32F4XX uses 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1U /*!< FPU present */ + +/** + * @} + */ + +/** @addtogroup Peripheral_interrupt_number_definition + * @{ + */ + +/** + * @brief STM32F4XX Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ + /****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */ + /****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_IRQn = 8, /*!< EXTI Line2 Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Stream0_IRQn = 11, /*!< DMA1 Stream 0 global Interrupt */ + DMA1_Stream1_IRQn = 12, /*!< DMA1 Stream 1 global Interrupt */ + DMA1_Stream2_IRQn = 13, /*!< DMA1 Stream 2 global Interrupt */ + DMA1_Stream3_IRQn = 14, /*!< DMA1 Stream 3 global Interrupt */ + DMA1_Stream4_IRQn = 15, /*!< DMA1 Stream 4 global Interrupt */ + DMA1_Stream5_IRQn = 16, /*!< DMA1 Stream 5 global Interrupt */ + DMA1_Stream6_IRQn = 17, /*!< DMA1 Stream 6 global Interrupt */ + ADC_IRQn = 18, /*!< ADC1, ADC2 and ADC3 global Interrupts */ + CAN1_TX_IRQn = 19, /*!< CAN1 TX Interrupt */ + CAN1_RX0_IRQn = 20, /*!< CAN1 RX0 Interrupt */ + CAN1_RX1_IRQn = 21, /*!< CAN1 RX1 Interrupt */ + CAN1_SCE_IRQn = 22, /*!< CAN1 SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM9_IRQn = 24, /*!< TIM1 Break interrupt and TIM9 global interrupt */ + TIM1_UP_TIM10_IRQn = 25, /*!< TIM1 Update Interrupt and TIM10 global interrupt */ + TIM1_TRG_COM_TIM11_IRQn = 26, /*!< TIM1 Trigger and Commutation Interrupt and TIM11 global interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt */ + USART2_IRQn = 38, /*!< USART2 global Interrupt */ + USART3_IRQn = 39, /*!< USART3 global Interrupt */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line Interrupt */ + OTG_FS_WKUP_IRQn = 42, /*!< USB OTG FS Wakeup through EXTI line interrupt */ + TIM8_BRK_TIM12_IRQn = 43, /*!< TIM8 Break Interrupt and TIM12 global interrupt */ + TIM8_UP_TIM13_IRQn = 44, /*!< TIM8 Update Interrupt and TIM13 global interrupt */ + TIM8_TRG_COM_TIM14_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt and TIM14 global interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare global interrupt */ + DMA1_Stream7_IRQn = 47, /*!< DMA1 Stream7 Interrupt */ + SDIO_IRQn = 49, /*!< SDIO global Interrupt */ + TIM5_IRQn = 50, /*!< TIM5 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt */ + UART5_IRQn = 53, /*!< UART5 global Interrupt */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC1&2 underrun error interrupts */ + TIM7_IRQn = 55, /*!< TIM7 global interrupt */ + DMA2_Stream0_IRQn = 56, /*!< DMA2 Stream 0 global Interrupt */ + DMA2_Stream1_IRQn = 57, /*!< DMA2 Stream 1 global Interrupt */ + DMA2_Stream2_IRQn = 58, /*!< DMA2 Stream 2 global Interrupt */ + DMA2_Stream3_IRQn = 59, /*!< DMA2 Stream 3 global Interrupt */ + DMA2_Stream4_IRQn = 60, /*!< DMA2 Stream 4 global Interrupt */ + DFSDM1_FLT0_IRQn = 61, /*!< DFSDM1 Filter 0 global Interrupt */ + DFSDM1_FLT1_IRQn = 62, /*!< DFSDM1 Filter 1 global Interrupt */ + CAN2_TX_IRQn = 63, /*!< CAN2 TX Interrupt */ + CAN2_RX0_IRQn = 64, /*!< CAN2 RX0 Interrupt */ + CAN2_RX1_IRQn = 65, /*!< CAN2 RX1 Interrupt */ + CAN2_SCE_IRQn = 66, /*!< CAN2 SCE Interrupt */ + OTG_FS_IRQn = 67, /*!< USB OTG FS global Interrupt */ + DMA2_Stream5_IRQn = 68, /*!< DMA2 Stream 5 global interrupt */ + DMA2_Stream6_IRQn = 69, /*!< DMA2 Stream 6 global interrupt */ + DMA2_Stream7_IRQn = 70, /*!< DMA2 Stream 7 global interrupt */ + USART6_IRQn = 71, /*!< USART6 global interrupt */ + I2C3_EV_IRQn = 72, /*!< I2C3 event interrupt */ + I2C3_ER_IRQn = 73, /*!< I2C3 error interrupt */ + CAN3_TX_IRQn = 74, /*!< CAN3 TX Interrupt */ + CAN3_RX0_IRQn = 75, /*!< CAN3 RX0 Interrupt */ + CAN3_RX1_IRQn = 76, /*!< CAN3 RX1 Interrupt */ + CAN3_SCE_IRQn = 77, /*!< CAN3 SCE Interrupt */ + RNG_IRQn = 80, /*!< RNG global Interrupt */ + FPU_IRQn = 81, /*!< FPU global interrupt */ + UART7_IRQn = 82, /*!< UART7 global interrupt */ + UART8_IRQn = 83, /*!< UART8 global interrupt */ + SPI4_IRQn = 84, /*!< SPI4 global Interrupt */ + SPI5_IRQn = 85, /*!< SPI5 global Interrupt */ + SAI1_IRQn = 87, /*!< SAI1 global Interrupt */ + UART9_IRQn = 88, /*!< UART9 global Interrupt */ + UART10_IRQn = 89, /*!< UART10 global Interrupt */ + QUADSPI_IRQn = 92, /*!< QuadSPI global Interrupt */ + FMPI2C1_EV_IRQn = 95, /*!< FMPI2C1 Event Interrupt */ + FMPI2C1_ER_IRQn = 96, /*!< FMPI2C1 Error Interrupt */ + LPTIM1_IRQn = 97, /*!< LP TIM1 interrupt */ + DFSDM2_FLT0_IRQn = 98, /*!< DFSDM2 Filter 0 global Interrupt */ + DFSDM2_FLT1_IRQn = 99, /*!< DFSDM2 Filter 1 global Interrupt */ + DFSDM2_FLT2_IRQn = 100, /*!< DFSDM2 Filter 2 global Interrupt */ + DFSDM2_FLT3_IRQn = 101 /*!< DFSDM2 Filter 3 global Interrupt */ +} IRQn_Type; + +/** + * @} + */ + +#include "core_cm4.h" /* Cortex-M4 processor and core peripherals */ +#include "system_stm32f4xx.h" +#include + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< ADC status register, Address offset: 0x00 */ + __IO uint32_t CR1; /*!< ADC control register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< ADC control register 2, Address offset: 0x08 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x0C */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x10 */ + __IO uint32_t JOFR1; /*!< ADC injected channel data offset register 1, Address offset: 0x14 */ + __IO uint32_t JOFR2; /*!< ADC injected channel data offset register 2, Address offset: 0x18 */ + __IO uint32_t JOFR3; /*!< ADC injected channel data offset register 3, Address offset: 0x1C */ + __IO uint32_t JOFR4; /*!< ADC injected channel data offset register 4, Address offset: 0x20 */ + __IO uint32_t HTR; /*!< ADC watchdog higher threshold register, Address offset: 0x24 */ + __IO uint32_t LTR; /*!< ADC watchdog lower threshold register, Address offset: 0x28 */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x2C */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x30 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x34 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x38*/ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x3C */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x40 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x44 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x48 */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x4C */ +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1 base address + 0x300 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1 base address + 0x304 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1 base address + 0x308 */ +} ADC_Common_TypeDef; + + +/** + * @brief Controller Area Network TxMailBox + */ + +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ + +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ + +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ + +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ +} CRC_TypeDef; + +/** + * @brief DFSDM module registers + */ +typedef struct +{ + __IO uint32_t FLTCR1; /*!< DFSDM control register1, Address offset: 0x100 */ + __IO uint32_t FLTCR2; /*!< DFSDM control register2, Address offset: 0x104 */ + __IO uint32_t FLTISR; /*!< DFSDM interrupt and status register, Address offset: 0x108 */ + __IO uint32_t FLTICR; /*!< DFSDM interrupt flag clear register, Address offset: 0x10C */ + __IO uint32_t FLTJCHGR; /*!< DFSDM injected channel group selection register, Address offset: 0x110 */ + __IO uint32_t FLTFCR; /*!< DFSDM filter control register, Address offset: 0x114 */ + __IO uint32_t FLTJDATAR; /*!< DFSDM data register for injected group, Address offset: 0x118 */ + __IO uint32_t FLTRDATAR; /*!< DFSDM data register for regular group, Address offset: 0x11C */ + __IO uint32_t FLTAWHTR; /*!< DFSDM analog watchdog high threshold register, Address offset: 0x120 */ + __IO uint32_t FLTAWLTR; /*!< DFSDM analog watchdog low threshold register, Address offset: 0x124 */ + __IO uint32_t FLTAWSR; /*!< DFSDM analog watchdog status register Address offset: 0x128 */ + __IO uint32_t FLTAWCFR; /*!< DFSDM analog watchdog clear flag register Address offset: 0x12C */ + __IO uint32_t FLTEXMAX; /*!< DFSDM extreme detector maximum register, Address offset: 0x130 */ + __IO uint32_t FLTEXMIN; /*!< DFSDM extreme detector minimum register Address offset: 0x134 */ + __IO uint32_t FLTCNVTIMR; /*!< DFSDM conversion timer, Address offset: 0x138 */ +} DFSDM_Filter_TypeDef; + +/** + * @brief DFSDM channel configuration registers + */ +typedef struct +{ + __IO uint32_t CHCFGR1; /*!< DFSDM channel configuration register1, Address offset: 0x00 */ + __IO uint32_t CHCFGR2; /*!< DFSDM channel configuration register2, Address offset: 0x04 */ + __IO uint32_t CHAWSCDR; /*!< DFSDM channel analog watchdog and + short circuit detector register, Address offset: 0x08 */ + __IO uint32_t CHWDATAR; /*!< DFSDM channel watchdog filter data register, Address offset: 0x0C */ + __IO uint32_t CHDATINR; /*!< DFSDM channel data input register, Address offset: 0x10 */ +} DFSDM_Channel_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +} DBGMCU_TypeDef; + + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DMA stream x configuration register */ + __IO uint32_t NDTR; /*!< DMA stream x number of data register */ + __IO uint32_t PAR; /*!< DMA stream x peripheral address register */ + __IO uint32_t M0AR; /*!< DMA stream x memory 0 address register */ + __IO uint32_t M1AR; /*!< DMA stream x memory 1 address register */ + __IO uint32_t FCR; /*!< DMA stream x FIFO control register */ +} DMA_Stream_TypeDef; + +typedef struct +{ + __IO uint32_t LISR; /*!< DMA low interrupt status register, Address offset: 0x00 */ + __IO uint32_t HISR; /*!< DMA high interrupt status register, Address offset: 0x04 */ + __IO uint32_t LIFCR; /*!< DMA low interrupt flag clear register, Address offset: 0x08 */ + __IO uint32_t HIFCR; /*!< DMA high interrupt flag clear register, Address offset: 0x0C */ +} DMA_TypeDef; + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; /*!< EXTI Interrupt mask register, Address offset: 0x00 */ + __IO uint32_t EMR; /*!< EXTI Event mask register, Address offset: 0x04 */ + __IO uint32_t RTSR; /*!< EXTI Rising trigger selection register, Address offset: 0x08 */ + __IO uint32_t FTSR; /*!< EXTI Falling trigger selection register, Address offset: 0x0C */ + __IO uint32_t SWIER; /*!< EXTI Software interrupt event register, Address offset: 0x10 */ + __IO uint32_t PR; /*!< EXTI Pending register, Address offset: 0x14 */ +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t KEYR; /*!< FLASH key register, Address offset: 0x04 */ + __IO uint32_t OPTKEYR; /*!< FLASH option key register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< FLASH status register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FLASH control register, Address offset: 0x10 */ + __IO uint32_t OPTCR; /*!< FLASH option control register , Address offset: 0x14 */ + __IO uint32_t OPTCR1; /*!< FLASH option control register 1, Address offset: 0x18 */ +} FLASH_TypeDef; + + + +/** + * @brief Flexible Static Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ +} FSMC_Bank1_TypeDef; + +/** + * @brief Flexible Static Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FSMC_Bank1E_TypeDef; +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ +} GPIO_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + __IO uint32_t MEMRMP; /*!< SYSCFG memory remap register, Address offset: 0x00 */ + __IO uint32_t PMC; /*!< SYSCFG peripheral mode configuration register, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + uint32_t RESERVED; /*!< Reserved, 0x18 */ + __IO uint32_t CFGR2; /*!< SYSCFG Configuration register2, Address offset: 0x1C */ + __IO uint32_t CMPCR; /*!< SYSCFG Compensation cell control register, Address offset: 0x20 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x24-0x28 */ + __IO uint32_t CFGR; /*!< SYSCFG Configuration register, Address offset: 0x2C */ + __IO uint32_t MCHDLYCR; /*!< SYSCFG multi-channel delay register, Address offset: 0x30 */ +} SYSCFG_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address register 1, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address register 2, Address offset: 0x0C */ + __IO uint32_t DR; /*!< I2C Data register, Address offset: 0x10 */ + __IO uint32_t SR1; /*!< I2C Status register 1, Address offset: 0x14 */ + __IO uint32_t SR2; /*!< I2C Status register 2, Address offset: 0x18 */ + __IO uint32_t CCR; /*!< I2C Clock control register, Address offset: 0x1C */ + __IO uint32_t TRISE; /*!< I2C TRISE register, Address offset: 0x20 */ + __IO uint32_t FLTR; /*!< I2C FLTR register, Address offset: 0x24 */ +} I2C_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< FMPI2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< FMPI2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< FMPI2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< FMPI2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< FMPI2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< FMPI2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< FMPI2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< FMPI2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< FMPI2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< FMPI2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< FMPI2C Transmit data register, Address offset: 0x28 */ +} FMPI2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ +} IWDG_TypeDef; + + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< PWR power control register, Address offset: 0x00 */ + __IO uint32_t CSR; /*!< PWR power control/status register, Address offset: 0x04 */ +} PWR_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t PLLCFGR; /*!< RCC PLL configuration register, Address offset: 0x04 */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ + __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x0C */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x10 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x14 */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved, 0x1C */ + __IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x20 */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x24 */ + uint32_t RESERVED1[2]; /*!< Reserved, 0x28-0x2C */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clock register, Address offset: 0x30 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clock register, Address offset: 0x34 */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clock register, Address offset: 0x38 */ + uint32_t RESERVED2; /*!< Reserved, 0x3C */ + __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x40 */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x44 */ + uint32_t RESERVED3[2]; /*!< Reserved, 0x48-0x4C */ + __IO uint32_t AHB1LPENR; /*!< RCC AHB1 peripheral clock enable in low power mode register, Address offset: 0x50 */ + __IO uint32_t AHB2LPENR; /*!< RCC AHB2 peripheral clock enable in low power mode register, Address offset: 0x54 */ + __IO uint32_t AHB3LPENR; /*!< RCC AHB3 peripheral clock enable in low power mode register, Address offset: 0x58 */ + uint32_t RESERVED4; /*!< Reserved, 0x5C */ + __IO uint32_t APB1LPENR; /*!< RCC APB1 peripheral clock enable in low power mode register, Address offset: 0x60 */ + __IO uint32_t APB2LPENR; /*!< RCC APB2 peripheral clock enable in low power mode register, Address offset: 0x64 */ + uint32_t RESERVED5[2]; /*!< Reserved, 0x68-0x6C */ + __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x70 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x74 */ + uint32_t RESERVED6[2]; /*!< Reserved, 0x78-0x7C */ + __IO uint32_t SSCGR; /*!< RCC spread spectrum clock generation register, Address offset: 0x80 */ + __IO uint32_t PLLI2SCFGR; /*!< RCC PLLI2S configuration register, Address offset: 0x84 */ + uint32_t RESERVED7; /*!< Reserved, 0x84 */ + __IO uint32_t DCKCFGR; /*!< RCC Dedicated Clocks configuration register, Address offset: 0x8C */ + __IO uint32_t CKGATENR; /*!< RCC Clocks Gated ENable Register, Address offset: 0x90 */ + __IO uint32_t DCKCFGR2; /*!< RCC Dedicated Clocks configuration register 2, Address offset: 0x94 */ +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ + +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x08 */ + __IO uint32_t ISR; /*!< RTC initialization and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + __IO uint32_t CALIBR; /*!< RTC calibration register, Address offset: 0x18 */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x1C */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x3C */ + __IO uint32_t TAFCR; /*!< RTC tamper and alternate function configuration register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR;/*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBSSR;/*!< RTC alarm B sub second register, Address offset: 0x48 */ + uint32_t RESERVED7; /*!< Reserved, 0x4C */ + __IO uint32_t BKP0R; /*!< RTC backup register 1, Address offset: 0x50 */ + __IO uint32_t BKP1R; /*!< RTC backup register 1, Address offset: 0x54 */ + __IO uint32_t BKP2R; /*!< RTC backup register 2, Address offset: 0x58 */ + __IO uint32_t BKP3R; /*!< RTC backup register 3, Address offset: 0x5C */ + __IO uint32_t BKP4R; /*!< RTC backup register 4, Address offset: 0x60 */ + __IO uint32_t BKP5R; /*!< RTC backup register 5, Address offset: 0x64 */ + __IO uint32_t BKP6R; /*!< RTC backup register 6, Address offset: 0x68 */ + __IO uint32_t BKP7R; /*!< RTC backup register 7, Address offset: 0x6C */ + __IO uint32_t BKP8R; /*!< RTC backup register 8, Address offset: 0x70 */ + __IO uint32_t BKP9R; /*!< RTC backup register 9, Address offset: 0x74 */ + __IO uint32_t BKP10R; /*!< RTC backup register 10, Address offset: 0x78 */ + __IO uint32_t BKP11R; /*!< RTC backup register 11, Address offset: 0x7C */ + __IO uint32_t BKP12R; /*!< RTC backup register 12, Address offset: 0x80 */ + __IO uint32_t BKP13R; /*!< RTC backup register 13, Address offset: 0x84 */ + __IO uint32_t BKP14R; /*!< RTC backup register 14, Address offset: 0x88 */ + __IO uint32_t BKP15R; /*!< RTC backup register 15, Address offset: 0x8C */ + __IO uint32_t BKP16R; /*!< RTC backup register 16, Address offset: 0x90 */ + __IO uint32_t BKP17R; /*!< RTC backup register 17, Address offset: 0x94 */ + __IO uint32_t BKP18R; /*!< RTC backup register 18, Address offset: 0x98 */ + __IO uint32_t BKP19R; /*!< RTC backup register 19, Address offset: 0x9C */ +} RTC_TypeDef; + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + __IO uint32_t GCR; /*!< SAI global configuration register, Address offset: 0x00 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + +/** + * @brief SD host Interface + */ + +typedef struct +{ + __IO uint32_t POWER; /*!< SDIO power control register, Address offset: 0x00 */ + __IO uint32_t CLKCR; /*!< SDI clock control register, Address offset: 0x04 */ + __IO uint32_t ARG; /*!< SDIO argument register, Address offset: 0x08 */ + __IO uint32_t CMD; /*!< SDIO command register, Address offset: 0x0C */ + __IO const uint32_t RESPCMD; /*!< SDIO command response register, Address offset: 0x10 */ + __IO const uint32_t RESP1; /*!< SDIO response 1 register, Address offset: 0x14 */ + __IO const uint32_t RESP2; /*!< SDIO response 2 register, Address offset: 0x18 */ + __IO const uint32_t RESP3; /*!< SDIO response 3 register, Address offset: 0x1C */ + __IO const uint32_t RESP4; /*!< SDIO response 4 register, Address offset: 0x20 */ + __IO uint32_t DTIMER; /*!< SDIO data timer register, Address offset: 0x24 */ + __IO uint32_t DLEN; /*!< SDIO data length register, Address offset: 0x28 */ + __IO uint32_t DCTRL; /*!< SDIO data control register, Address offset: 0x2C */ + __IO const uint32_t DCOUNT; /*!< SDIO data counter register, Address offset: 0x30 */ + __IO const uint32_t STA; /*!< SDIO status register, Address offset: 0x34 */ + __IO uint32_t ICR; /*!< SDIO interrupt clear register, Address offset: 0x38 */ + __IO uint32_t MASK; /*!< SDIO mask register, Address offset: 0x3C */ + uint32_t RESERVED0[2]; /*!< Reserved, 0x40-0x44 */ + __IO const uint32_t FIFOCNT; /*!< SDIO FIFO counter register, Address offset: 0x48 */ + uint32_t RESERVED1[13]; /*!< Reserved, 0x4C-0x7C */ + __IO uint32_t FIFO; /*!< SDIO data FIFO register, Address offset: 0x80 */ +} SDIO_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI control register 1 (not used in I2S mode), Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI control register 2, Address offset: 0x04 */ + __IO uint32_t SR; /*!< SPI status register, Address offset: 0x08 */ + __IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */ + __IO uint32_t CRCPR; /*!< SPI CRC polynomial register (not used in I2S mode), Address offset: 0x10 */ + __IO uint32_t RXCRCR; /*!< SPI RX CRC register (not used in I2S mode), Address offset: 0x14 */ + __IO uint32_t TXCRCR; /*!< SPI TX CRC register (not used in I2S mode), Address offset: 0x18 */ + __IO uint32_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */ + __IO uint32_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */ +} SPI_TypeDef; + +/** + * @brief QUAD Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */ + __IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */ + __IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */ + __IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */ + __IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */ + __IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */ + __IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */ + __IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */ + __IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */ + __IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */ +} QUADSPI_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x48 */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x4C */ + __IO uint32_t OR; /*!< TIM option register, Address offset: 0x50 */ +} TIM_TypeDef; + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ + +typedef struct +{ + __IO uint32_t SR; /*!< USART Status register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< USART Data register, Address offset: 0x04 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x08 */ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x0C */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x10 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x14 */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x18 */ +} USART_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + +/** + * @brief RNG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ +} RNG_TypeDef; + +/** + * @brief USB_OTG_Core_Registers + */ +typedef struct +{ + __IO uint32_t GOTGCTL; /*!< USB_OTG Control and Status Register 000h */ + __IO uint32_t GOTGINT; /*!< USB_OTG Interrupt Register 004h */ + __IO uint32_t GAHBCFG; /*!< Core AHB Configuration Register 008h */ + __IO uint32_t GUSBCFG; /*!< Core USB Configuration Register 00Ch */ + __IO uint32_t GRSTCTL; /*!< Core Reset Register 010h */ + __IO uint32_t GINTSTS; /*!< Core Interrupt Register 014h */ + __IO uint32_t GINTMSK; /*!< Core Interrupt Mask Register 018h */ + __IO uint32_t GRXSTSR; /*!< Receive Sts Q Read Register 01Ch */ + __IO uint32_t GRXSTSP; /*!< Receive Sts Q Read & POP Register 020h */ + __IO uint32_t GRXFSIZ; /*!< Receive FIFO Size Register 024h */ + __IO uint32_t DIEPTXF0_HNPTXFSIZ; /*!< EP0 / Non Periodic Tx FIFO Size Register 028h */ + __IO uint32_t HNPTXSTS; /*!< Non Periodic Tx FIFO/Queue Sts reg 02Ch */ + uint32_t Reserved30[2]; /*!< Reserved 030h */ + __IO uint32_t GCCFG; /*!< General Purpose IO Register 038h */ + __IO uint32_t CID; /*!< User ID Register 03Ch */ + uint32_t Reserved5[3]; /*!< Reserved 040h-048h */ + __IO uint32_t GHWCFG3; /*!< User HW config3 04Ch */ + uint32_t Reserved6; /*!< Reserved 050h */ + __IO uint32_t GLPMCFG; /*!< LPM Register 054h */ + uint32_t Reserved; /*!< Reserved 058h */ + __IO uint32_t GDFIFOCFG; /*!< DFIFO Software Config Register 05Ch */ + uint32_t Reserved43[40]; /*!< Reserved 058h-0FFh */ + __IO uint32_t HPTXFSIZ; /*!< Host Periodic Tx FIFO Size Reg 100h */ + __IO uint32_t DIEPTXF[0x0F]; /*!< dev Periodic Transmit FIFO */ +} USB_OTG_GlobalTypeDef; + +/** + * @brief USB_OTG_device_Registers + */ +typedef struct +{ + __IO uint32_t DCFG; /*!< dev Configuration Register 800h */ + __IO uint32_t DCTL; /*!< dev Control Register 804h */ + __IO uint32_t DSTS; /*!< dev Status Register (RO) 808h */ + uint32_t Reserved0C; /*!< Reserved 80Ch */ + __IO uint32_t DIEPMSK; /*!< dev IN Endpoint Mask 810h */ + __IO uint32_t DOEPMSK; /*!< dev OUT Endpoint Mask 814h */ + __IO uint32_t DAINT; /*!< dev All Endpoints Itr Reg 818h */ + __IO uint32_t DAINTMSK; /*!< dev All Endpoints Itr Mask 81Ch */ + uint32_t Reserved20; /*!< Reserved 820h */ + uint32_t Reserved9; /*!< Reserved 824h */ + __IO uint32_t DVBUSDIS; /*!< dev VBUS discharge Register 828h */ + __IO uint32_t DVBUSPULSE; /*!< dev VBUS Pulse Register 82Ch */ + __IO uint32_t DTHRCTL; /*!< dev threshold 830h */ + __IO uint32_t DIEPEMPMSK; /*!< dev empty msk 834h */ + __IO uint32_t DEACHINT; /*!< dedicated EP interrupt 838h */ + __IO uint32_t DEACHMSK; /*!< dedicated EP msk 83Ch */ + uint32_t Reserved40; /*!< dedicated EP mask 840h */ + __IO uint32_t DINEP1MSK; /*!< dedicated EP mask 844h */ + uint32_t Reserved44[15]; /*!< Reserved 844-87Ch */ + __IO uint32_t DOUTEP1MSK; /*!< dedicated EP msk 884h */ +} USB_OTG_DeviceTypeDef; + +/** + * @brief USB_OTG_IN_Endpoint-Specific_Register + */ +typedef struct +{ + __IO uint32_t DIEPCTL; /*!< dev IN Endpoint Control Reg 900h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved 900h + (ep_num * 20h) + 04h */ + __IO uint32_t DIEPINT; /*!< dev IN Endpoint Itr Reg 900h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved 900h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DIEPTSIZ; /*!< IN Endpoint Txfer Size 900h + (ep_num * 20h) + 10h */ + __IO uint32_t DIEPDMA; /*!< IN Endpoint DMA Address Reg 900h + (ep_num * 20h) + 14h */ + __IO uint32_t DTXFSTS; /*!< IN Endpoint Tx FIFO Status Reg 900h + (ep_num * 20h) + 18h */ + uint32_t Reserved18; /*!< Reserved 900h+(ep_num*20h)+1Ch-900h+ (ep_num * 20h) + 1Ch */ +} USB_OTG_INEndpointTypeDef; + +/** + * @brief USB_OTG_OUT_Endpoint-Specific_Registers + */ +typedef struct +{ + __IO uint32_t DOEPCTL; /*!< dev OUT Endpoint Control Reg B00h + (ep_num * 20h) + 00h */ + uint32_t Reserved04; /*!< Reserved B00h + (ep_num * 20h) + 04h */ + __IO uint32_t DOEPINT; /*!< dev OUT Endpoint Itr Reg B00h + (ep_num * 20h) + 08h */ + uint32_t Reserved0C; /*!< Reserved B00h + (ep_num * 20h) + 0Ch */ + __IO uint32_t DOEPTSIZ; /*!< dev OUT Endpoint Txfer Size B00h + (ep_num * 20h) + 10h */ + __IO uint32_t DOEPDMA; /*!< dev OUT Endpoint DMA Address B00h + (ep_num * 20h) + 14h */ + uint32_t Reserved18[2]; /*!< Reserved B00h + (ep_num * 20h) + 18h - B00h + (ep_num * 20h) + 1Ch */ +} USB_OTG_OUTEndpointTypeDef; + +/** + * @brief USB_OTG_Host_Mode_Register_Structures + */ +typedef struct +{ + __IO uint32_t HCFG; /*!< Host Configuration Register 400h */ + __IO uint32_t HFIR; /*!< Host Frame Interval Register 404h */ + __IO uint32_t HFNUM; /*!< Host Frame Nbr/Frame Remaining 408h */ + uint32_t Reserved40C; /*!< Reserved 40Ch */ + __IO uint32_t HPTXSTS; /*!< Host Periodic Tx FIFO/ Queue Status 410h */ + __IO uint32_t HAINT; /*!< Host All Channels Interrupt Register 414h */ + __IO uint32_t HAINTMSK; /*!< Host All Channels Interrupt Mask 418h */ +} USB_OTG_HostTypeDef; + +/** + * @brief USB_OTG_Host_Channel_Specific_Registers + */ +typedef struct +{ + __IO uint32_t HCCHAR; /*!< Host Channel Characteristics Register 500h */ + __IO uint32_t HCSPLT; /*!< Host Channel Split Control Register 504h */ + __IO uint32_t HCINT; /*!< Host Channel Interrupt Register 508h */ + __IO uint32_t HCINTMSK; /*!< Host Channel Interrupt Mask Register 50Ch */ + __IO uint32_t HCTSIZ; /*!< Host Channel Transfer Size Register 510h */ + __IO uint32_t HCDMA; /*!< Host Channel DMA Address Register 514h */ + uint32_t Reserved[2]; /*!< Reserved */ +} USB_OTG_HostChannelTypeDef; + +/** + * @brief LPTIMER + */ +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ + __IO uint32_t OR; /*!< LPTIM Option register, Address offset: 0x20 */ +} LPTIM_TypeDef; + +/** + * @} + */ + +/** @addtogroup Peripheral_memory_map + * @{ + */ +#define FLASH_BASE 0x08000000UL /*!< FLASH (up to 1.5 MB) base address in the alias region */ +#define SRAM1_BASE 0x20000000UL /*!< SRAM1(256 KB) base address in the alias region */ +#define SRAM2_BASE 0x20040000UL /*!< SRAM2(64 KB) base address in the alias region */ +#define PERIPH_BASE 0x40000000UL /*!< Peripheral base address in the alias region */ +#define FSMC_R_BASE 0xA0000000UL /*!< FSMC registers base address */ +#define QSPI_R_BASE 0xA0001000UL /*!< QuadSPI registers base address */ +#define SRAM1_BB_BASE 0x22000000UL /*!< SRAM1(256 KB) base address in the bit-band region */ +#define SRAM2_BB_BASE 0x22800000UL /*!< SRAM2(64 KB) base address in the bit-band region */ +#define PERIPH_BB_BASE 0x42000000UL /*!< Peripheral base address in the bit-band region */ +#define FLASH_END 0x0817FFFFUL /*!< FLASH end address */ +#define FLASH_OTP_BASE 0x1FFF7800UL /*!< Base address of : (up to 528 Bytes) embedded FLASH OTP Area */ +#define FLASH_OTP_END 0x1FFF7A0FUL /*!< End address of : (up to 528 Bytes) embedded FLASH OTP Area */ + +/* Legacy defines */ +#define SRAM_BASE SRAM1_BASE +#define SRAM_BB_BASE SRAM1_BB_BASE + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x10000000UL) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400UL) +#define TIM12_BASE (APB1PERIPH_BASE + 0x1800UL) +#define TIM13_BASE (APB1PERIPH_BASE + 0x1C00UL) +#define TIM14_BASE (APB1PERIPH_BASE + 0x2000UL) +#define LPTIM1_BASE (APB1PERIPH_BASE + 0x2400UL) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800UL) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00UL) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000UL) +#define I2S2ext_BASE (APB1PERIPH_BASE + 0x3400UL) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800UL) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00UL) +#define I2S3ext_BASE (APB1PERIPH_BASE + 0x4000UL) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400UL) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800UL) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00UL) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000UL) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400UL) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800UL) +#define I2C3_BASE (APB1PERIPH_BASE + 0x5C00UL) +#define FMPI2C1_BASE (APB1PERIPH_BASE + 0x6000UL) +#define CAN1_BASE (APB1PERIPH_BASE + 0x6400UL) +#define CAN2_BASE (APB1PERIPH_BASE + 0x6800UL) +#define CAN3_BASE (APB1PERIPH_BASE + 0x6C00UL) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000UL) +#define DAC_BASE (APB1PERIPH_BASE + 0x7400UL) +#define UART7_BASE (APB1PERIPH_BASE + 0x7800UL) +#define UART8_BASE (APB1PERIPH_BASE + 0x7C00UL) + +/*!< APB2 peripherals */ +#define TIM1_BASE (APB2PERIPH_BASE + 0x0000UL) +#define TIM8_BASE (APB2PERIPH_BASE + 0x0400UL) +#define USART1_BASE (APB2PERIPH_BASE + 0x1000UL) +#define USART6_BASE (APB2PERIPH_BASE + 0x1400UL) +#define UART9_BASE (APB2PERIPH_BASE + 0x1800UL) +#define UART10_BASE (APB2PERIPH_BASE + 0x1C00UL) +#define ADC1_BASE (APB2PERIPH_BASE + 0x2000UL) +#define ADC1_COMMON_BASE (APB2PERIPH_BASE + 0x2300UL) +/* Legacy define */ +#define ADC_BASE ADC1_COMMON_BASE +#define SDIO_BASE (APB2PERIPH_BASE + 0x2C00UL) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000UL) +#define SPI4_BASE (APB2PERIPH_BASE + 0x3400UL) +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x3800UL) +#define EXTI_BASE (APB2PERIPH_BASE + 0x3C00UL) +#define TIM9_BASE (APB2PERIPH_BASE + 0x4000UL) +#define TIM10_BASE (APB2PERIPH_BASE + 0x4400UL) +#define TIM11_BASE (APB2PERIPH_BASE + 0x4800UL) +#define SPI5_BASE (APB2PERIPH_BASE + 0x5000UL) +#define DFSDM1_BASE (APB2PERIPH_BASE + 0x6000UL) +#define DFSDM2_BASE (APB2PERIPH_BASE + 0x6400UL) +#define DFSDM1_Channel0_BASE (DFSDM1_BASE + 0x00UL) +#define DFSDM1_Channel1_BASE (DFSDM1_BASE + 0x20UL) +#define DFSDM1_Channel2_BASE (DFSDM1_BASE + 0x40UL) +#define DFSDM1_Channel3_BASE (DFSDM1_BASE + 0x60UL) +#define DFSDM1_Filter0_BASE (DFSDM1_BASE + 0x100UL) +#define DFSDM1_Filter1_BASE (DFSDM1_BASE + 0x180UL) +#define DFSDM2_Channel0_BASE (DFSDM2_BASE + 0x00UL) +#define DFSDM2_Channel1_BASE (DFSDM2_BASE + 0x20UL) +#define DFSDM2_Channel2_BASE (DFSDM2_BASE + 0x40UL) +#define DFSDM2_Channel3_BASE (DFSDM2_BASE + 0x60UL) +#define DFSDM2_Channel4_BASE (DFSDM2_BASE + 0x80UL) +#define DFSDM2_Channel5_BASE (DFSDM2_BASE + 0xA0UL) +#define DFSDM2_Channel6_BASE (DFSDM2_BASE + 0xC0UL) +#define DFSDM2_Channel7_BASE (DFSDM2_BASE + 0xE0UL) +#define DFSDM2_Filter0_BASE (DFSDM2_BASE + 0x100UL) +#define DFSDM2_Filter1_BASE (DFSDM2_BASE + 0x180UL) +#define DFSDM2_Filter2_BASE (DFSDM2_BASE + 0x200UL) +#define DFSDM2_Filter3_BASE (DFSDM2_BASE + 0x280UL) +#define SAI1_BASE (APB2PERIPH_BASE + 0x5800UL) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x004UL) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x024UL) + +/*!< AHB1 peripherals */ +#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000UL) +#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400UL) +#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800UL) +#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00UL) +#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000UL) +#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400UL) +#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800UL) +#define GPIOH_BASE (AHB1PERIPH_BASE + 0x1C00UL) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000UL) +#define RCC_BASE (AHB1PERIPH_BASE + 0x3800UL) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x3C00UL) +#define DMA1_BASE (AHB1PERIPH_BASE + 0x6000UL) +#define DMA1_Stream0_BASE (DMA1_BASE + 0x010UL) +#define DMA1_Stream1_BASE (DMA1_BASE + 0x028UL) +#define DMA1_Stream2_BASE (DMA1_BASE + 0x040UL) +#define DMA1_Stream3_BASE (DMA1_BASE + 0x058UL) +#define DMA1_Stream4_BASE (DMA1_BASE + 0x070UL) +#define DMA1_Stream5_BASE (DMA1_BASE + 0x088UL) +#define DMA1_Stream6_BASE (DMA1_BASE + 0x0A0UL) +#define DMA1_Stream7_BASE (DMA1_BASE + 0x0B8UL) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x6400UL) +#define DMA2_Stream0_BASE (DMA2_BASE + 0x010UL) +#define DMA2_Stream1_BASE (DMA2_BASE + 0x028UL) +#define DMA2_Stream2_BASE (DMA2_BASE + 0x040UL) +#define DMA2_Stream3_BASE (DMA2_BASE + 0x058UL) +#define DMA2_Stream4_BASE (DMA2_BASE + 0x070UL) +#define DMA2_Stream5_BASE (DMA2_BASE + 0x088UL) +#define DMA2_Stream6_BASE (DMA2_BASE + 0x0A0UL) +#define DMA2_Stream7_BASE (DMA2_BASE + 0x0B8UL) + +/*!< AHB2 peripherals */ +#define RNG_BASE (AHB2PERIPH_BASE + 0x60800UL) + + +/*!< FSMC Bankx registers base address */ +#define FSMC_Bank1_R_BASE (FSMC_R_BASE + 0x0000UL) +#define FSMC_Bank1E_R_BASE (FSMC_R_BASE + 0x0104UL) + +/*!< Debug MCU registers base address */ +#define DBGMCU_BASE 0xE0042000UL +/*!< USB registers base address */ +#define USB_OTG_FS_PERIPH_BASE 0x50000000UL + +#define USB_OTG_GLOBAL_BASE 0x000UL +#define USB_OTG_DEVICE_BASE 0x800UL +#define USB_OTG_IN_ENDPOINT_BASE 0x900UL +#define USB_OTG_OUT_ENDPOINT_BASE 0xB00UL +#define USB_OTG_EP_REG_SIZE 0x20UL +#define USB_OTG_HOST_BASE 0x400UL +#define USB_OTG_HOST_PORT_BASE 0x440UL +#define USB_OTG_HOST_CHANNEL_BASE 0x500UL +#define USB_OTG_HOST_CHANNEL_SIZE 0x20UL +#define USB_OTG_PCGCCTL_BASE 0xE00UL +#define USB_OTG_FIFO_BASE 0x1000UL +#define USB_OTG_FIFO_SIZE 0x1000UL + +#define UID_BASE 0x1FFF7A10UL /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE 0x1FFF7A22UL /*!< FLASH Size register base address */ +#define PACKAGE_BASE 0x1FFF7BF0UL /*!< Package size register base address */ +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define TIM12 ((TIM_TypeDef *) TIM12_BASE) +#define TIM13 ((TIM_TypeDef *) TIM13_BASE) +#define TIM14 ((TIM_TypeDef *) TIM14_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define I2S2ext ((SPI_TypeDef *) I2S2ext_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define I2S3ext ((SPI_TypeDef *) I2S3ext_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define FMPI2C1 ((FMPI2C_TypeDef *) FMPI2C1_BASE) +#define CAN1 ((CAN_TypeDef *) CAN1_BASE) +#define CAN2 ((CAN_TypeDef *) CAN2_BASE) +#define CAN3 ((CAN_TypeDef *) CAN3_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define DAC1 ((DAC_TypeDef *) DAC_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) /* Kept for legacy purpose */ +#define UART7 ((USART_TypeDef *) UART7_BASE) +#define UART8 ((USART_TypeDef *) UART8_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define USART6 ((USART_TypeDef *) USART6_BASE) +#define UART9 ((USART_TypeDef *) UART9_BASE) +#define UART10 ((USART_TypeDef *) UART10_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC1_COMMON ((ADC_Common_TypeDef *) ADC1_COMMON_BASE) +/* Legacy define */ +#define ADC ADC1_COMMON +#define SDIO ((SDIO_TypeDef *) SDIO_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define TIM9 ((TIM_TypeDef *) TIM9_BASE) +#define TIM10 ((TIM_TypeDef *) TIM10_BASE) +#define TIM11 ((TIM_TypeDef *) TIM11_BASE) +#define SPI5 ((SPI_TypeDef *) SPI5_BASE) +#define DFSDM1_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel0_BASE) +#define DFSDM1_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel1_BASE) +#define DFSDM1_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel2_BASE) +#define DFSDM1_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM1_Channel3_BASE) +#define DFSDM1_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter0_BASE) +#define DFSDM1_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM1_Filter1_BASE) +#define DFSDM2_Channel0 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel0_BASE) +#define DFSDM2_Channel1 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel1_BASE) +#define DFSDM2_Channel2 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel2_BASE) +#define DFSDM2_Channel3 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel3_BASE) +#define DFSDM2_Channel4 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel4_BASE) +#define DFSDM2_Channel5 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel5_BASE) +#define DFSDM2_Channel6 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel6_BASE) +#define DFSDM2_Channel7 ((DFSDM_Channel_TypeDef *) DFSDM2_Channel7_BASE) +#define DFSDM2_Filter0 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter0_BASE) +#define DFSDM2_Filter1 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter1_BASE) +#define DFSDM2_Filter2 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter2_BASE) +#define DFSDM2_Filter3 ((DFSDM_Filter_TypeDef *) DFSDM2_Filter3_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define GPIOH ((GPIO_TypeDef *) GPIOH_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA1_Stream0 ((DMA_Stream_TypeDef *) DMA1_Stream0_BASE) +#define DMA1_Stream1 ((DMA_Stream_TypeDef *) DMA1_Stream1_BASE) +#define DMA1_Stream2 ((DMA_Stream_TypeDef *) DMA1_Stream2_BASE) +#define DMA1_Stream3 ((DMA_Stream_TypeDef *) DMA1_Stream3_BASE) +#define DMA1_Stream4 ((DMA_Stream_TypeDef *) DMA1_Stream4_BASE) +#define DMA1_Stream5 ((DMA_Stream_TypeDef *) DMA1_Stream5_BASE) +#define DMA1_Stream6 ((DMA_Stream_TypeDef *) DMA1_Stream6_BASE) +#define DMA1_Stream7 ((DMA_Stream_TypeDef *) DMA1_Stream7_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMA2_Stream0 ((DMA_Stream_TypeDef *) DMA2_Stream0_BASE) +#define DMA2_Stream1 ((DMA_Stream_TypeDef *) DMA2_Stream1_BASE) +#define DMA2_Stream2 ((DMA_Stream_TypeDef *) DMA2_Stream2_BASE) +#define DMA2_Stream3 ((DMA_Stream_TypeDef *) DMA2_Stream3_BASE) +#define DMA2_Stream4 ((DMA_Stream_TypeDef *) DMA2_Stream4_BASE) +#define DMA2_Stream5 ((DMA_Stream_TypeDef *) DMA2_Stream5_BASE) +#define DMA2_Stream6 ((DMA_Stream_TypeDef *) DMA2_Stream6_BASE) +#define DMA2_Stream7 ((DMA_Stream_TypeDef *) DMA2_Stream7_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) +#define FSMC_Bank1 ((FSMC_Bank1_TypeDef *) FSMC_Bank1_R_BASE) +#define FSMC_Bank1E ((FSMC_Bank1E_TypeDef *) FSMC_Bank1E_R_BASE) +#define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) +#define USB_OTG_FS ((USB_OTG_GlobalTypeDef *) USB_OTG_FS_PERIPH_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + +/** @addtogroup Hardware_Constant_Definition + * @{ + */ +#define LSI_STARTUP_TIME 40U /*!< LSI Maximum startup time in us */ +/** + * @} + */ + +/** @addtogroup Peripheral_Registers_Bits_Definition +* @{ +*/ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ + +/******************** Bit definition for ADC_SR register ********************/ +#define ADC_SR_AWD_Pos (0U) +#define ADC_SR_AWD_Msk (0x1UL << ADC_SR_AWD_Pos) /*!< 0x00000001 */ +#define ADC_SR_AWD ADC_SR_AWD_Msk /*! + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t ISR; /*!< ADC interrupt and status register, Address offset: 0x00 */ + __IO uint32_t IER; /*!< ADC interrupt enable register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< ADC configuration register 1, Address offset: 0x0C */ + __IO uint32_t CFGR2; /*!< ADC configuration register 2, Address offset: 0x10 */ + __IO uint32_t SMPR1; /*!< ADC sampling time register 1, Address offset: 0x14 */ + __IO uint32_t SMPR2; /*!< ADC sampling time register 2, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved, 0x1C */ + __IO uint32_t TR1; /*!< ADC analog watchdog 1 threshold register, Address offset: 0x20 */ + __IO uint32_t TR2; /*!< ADC analog watchdog 2 threshold register, Address offset: 0x24 */ + __IO uint32_t TR3; /*!< ADC analog watchdog 3 threshold register, Address offset: 0x28 */ + uint32_t RESERVED2; /*!< Reserved, 0x2C */ + __IO uint32_t SQR1; /*!< ADC group regular sequencer register 1, Address offset: 0x30 */ + __IO uint32_t SQR2; /*!< ADC group regular sequencer register 2, Address offset: 0x34 */ + __IO uint32_t SQR3; /*!< ADC group regular sequencer register 3, Address offset: 0x38 */ + __IO uint32_t SQR4; /*!< ADC group regular sequencer register 4, Address offset: 0x3C */ + __IO uint32_t DR; /*!< ADC group regular data register, Address offset: 0x40 */ + uint32_t RESERVED3; /*!< Reserved, 0x44 */ + uint32_t RESERVED4; /*!< Reserved, 0x48 */ + __IO uint32_t JSQR; /*!< ADC group injected sequencer register, Address offset: 0x4C */ + uint32_t RESERVED5[4]; /*!< Reserved, 0x50 - 0x5C */ + __IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */ + __IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */ + __IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */ + __IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */ + uint32_t RESERVED6[4]; /*!< Reserved, 0x70 - 0x7C */ + __IO uint32_t JDR1; /*!< ADC group injected rank 1 data register, Address offset: 0x80 */ + __IO uint32_t JDR2; /*!< ADC group injected rank 2 data register, Address offset: 0x84 */ + __IO uint32_t JDR3; /*!< ADC group injected rank 3 data register, Address offset: 0x88 */ + __IO uint32_t JDR4; /*!< ADC group injected rank 4 data register, Address offset: 0x8C */ + uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */ + __IO uint32_t AWD2CR; /*!< ADC analog watchdog 2 configuration register, Address offset: 0xA0 */ + __IO uint32_t AWD3CR; /*!< ADC analog watchdog 3 Configuration Register, Address offset: 0xA4 */ + uint32_t RESERVED8; /*!< Reserved, 0x0A8 */ + uint32_t RESERVED9; /*!< Reserved, 0x0AC */ + __IO uint32_t DIFSEL; /*!< ADC differential mode selection register, Address offset: 0xB0 */ + __IO uint32_t CALFACT; /*!< ADC calibration factors, Address offset: 0xB4 */ + uint32_t RESERVED10[2];/*!< Reserved, 0x0B8 - 0x0BC */ + __IO uint32_t GCOMP; /*!< ADC calibration factors, Address offset: 0xC0 */ +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC common status register, Address offset: 0x300 + 0x00 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x300 + 0x04 */ + __IO uint32_t CCR; /*!< ADC common configuration register, Address offset: 0x300 + 0x08 */ + __IO uint32_t CDR; /*!< ADC common group regular data register Address offset: 0x300 + 0x0C */ +} ADC_Common_TypeDef; + +/** + * @brief FD Controller Area Network + */ + +typedef struct +{ + __IO uint32_t CREL; /*!< FDCAN Core Release register, Address offset: 0x000 */ + __IO uint32_t ENDN; /*!< FDCAN Endian register, Address offset: 0x004 */ + uint32_t RESERVED1; /*!< Reserved, 0x008 */ + __IO uint32_t DBTP; /*!< FDCAN Data Bit Timing & Prescaler register, Address offset: 0x00C */ + __IO uint32_t TEST; /*!< FDCAN Test register, Address offset: 0x010 */ + __IO uint32_t RWD; /*!< FDCAN RAM Watchdog register, Address offset: 0x014 */ + __IO uint32_t CCCR; /*!< FDCAN CC Control register, Address offset: 0x018 */ + __IO uint32_t NBTP; /*!< FDCAN Nominal Bit Timing & Prescaler register, Address offset: 0x01C */ + __IO uint32_t TSCC; /*!< FDCAN Timestamp Counter Configuration register, Address offset: 0x020 */ + __IO uint32_t TSCV; /*!< FDCAN Timestamp Counter Value register, Address offset: 0x024 */ + __IO uint32_t TOCC; /*!< FDCAN Timeout Counter Configuration register, Address offset: 0x028 */ + __IO uint32_t TOCV; /*!< FDCAN Timeout Counter Value register, Address offset: 0x02C */ + uint32_t RESERVED2[4]; /*!< Reserved, 0x030 - 0x03C */ + __IO uint32_t ECR; /*!< FDCAN Error Counter register, Address offset: 0x040 */ + __IO uint32_t PSR; /*!< FDCAN Protocol Status register, Address offset: 0x044 */ + __IO uint32_t TDCR; /*!< FDCAN Transmitter Delay Compensation register, Address offset: 0x048 */ + uint32_t RESERVED3; /*!< Reserved, 0x04C */ + __IO uint32_t IR; /*!< FDCAN Interrupt register, Address offset: 0x050 */ + __IO uint32_t IE; /*!< FDCAN Interrupt Enable register, Address offset: 0x054 */ + __IO uint32_t ILS; /*!< FDCAN Interrupt Line Select register, Address offset: 0x058 */ + __IO uint32_t ILE; /*!< FDCAN Interrupt Line Enable register, Address offset: 0x05C */ + uint32_t RESERVED4[8]; /*!< Reserved, 0x060 - 0x07C */ + __IO uint32_t RXGFC; /*!< FDCAN Global Filter Configuration register, Address offset: 0x080 */ + __IO uint32_t XIDAM; /*!< FDCAN Extended ID AND Mask register, Address offset: 0x084 */ + __IO uint32_t HPMS; /*!< FDCAN High Priority Message Status register, Address offset: 0x088 */ + uint32_t RESERVED5; /*!< Reserved, 0x08C */ + __IO uint32_t RXF0S; /*!< FDCAN Rx FIFO 0 Status register, Address offset: 0x090 */ + __IO uint32_t RXF0A; /*!< FDCAN Rx FIFO 0 Acknowledge register, Address offset: 0x094 */ + __IO uint32_t RXF1S; /*!< FDCAN Rx FIFO 1 Status register, Address offset: 0x098 */ + __IO uint32_t RXF1A; /*!< FDCAN Rx FIFO 1 Acknowledge register, Address offset: 0x09C */ + uint32_t RESERVED6[8]; /*!< Reserved, 0x0A0 - 0x0BC */ + __IO uint32_t TXBC; /*!< FDCAN Tx Buffer Configuration register, Address offset: 0x0C0 */ + __IO uint32_t TXFQS; /*!< FDCAN Tx FIFO/Queue Status register, Address offset: 0x0C4 */ + __IO uint32_t TXBRP; /*!< FDCAN Tx Buffer Request Pending register, Address offset: 0x0C8 */ + __IO uint32_t TXBAR; /*!< FDCAN Tx Buffer Add Request register, Address offset: 0x0CC */ + __IO uint32_t TXBCR; /*!< FDCAN Tx Buffer Cancellation Request register, Address offset: 0x0D0 */ + __IO uint32_t TXBTO; /*!< FDCAN Tx Buffer Transmission Occurred register, Address offset: 0x0D4 */ + __IO uint32_t TXBCF; /*!< FDCAN Tx Buffer Cancellation Finished register, Address offset: 0x0D8 */ + __IO uint32_t TXBTIE; /*!< FDCAN Tx Buffer Transmission Interrupt Enable register, Address offset: 0x0DC */ + __IO uint32_t TXBCIE; /*!< FDCAN Tx Buffer Cancellation Finished Interrupt Enable register, Address offset: 0x0E0 */ + __IO uint32_t TXEFS; /*!< FDCAN Tx Event FIFO Status register, Address offset: 0x0E4 */ + __IO uint32_t TXEFA; /*!< FDCAN Tx Event FIFO Acknowledge register, Address offset: 0x0E8 */ +} FDCAN_GlobalTypeDef; + +/** + * @brief FD Controller Area Network Configuration + */ + +typedef struct +{ + __IO uint32_t CKDIV; /*!< FDCAN clock divider register, Address offset: 0x100 + 0x000 */ +} FDCAN_Config_TypeDef; + +/** + * @brief Comparator + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, Address offset: 0x00 */ +} COMP_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint32_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED0; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + +/** + * @brief Clock Recovery System + */ +typedef struct +{ + __IO uint32_t CR; /*!< CRS ccontrol register, Address offset: 0x00 */ + __IO uint32_t CFGR; /*!< CRS configuration register, Address offset: 0x04 */ + __IO uint32_t ISR; /*!< CRS interrupt and status register, Address offset: 0x08 */ + __IO uint32_t ICR; /*!< CRS interrupt flag clear register, Address offset: 0x0C */ +} CRS_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ + __IO uint32_t CCR; /*!< DAC calibration control register, Address offset: 0x38 */ + __IO uint32_t MCR; /*!< DAC mode control register, Address offset: 0x3C */ + __IO uint32_t SHSR1; /*!< DAC Sample and Hold sample time register 1, Address offset: 0x40 */ + __IO uint32_t SHSR2; /*!< DAC Sample and Hold sample time register 2, Address offset: 0x44 */ + __IO uint32_t SHHR; /*!< DAC Sample and Hold hold time register, Address offset: 0x48 */ + __IO uint32_t SHRR; /*!< DAC Sample and Hold refresh time register, Address offset: 0x4C */ + __IO uint32_t RESERVED[2]; + __IO uint32_t STR1; /*!< DAC Sawtooth register, Address offset: 0x58 */ + __IO uint32_t STR2; /*!< DAC Sawtooth register, Address offset: 0x5C */ + __IO uint32_t STMODR; /*!< DAC Sawtooth Mode register, Address offset: 0x60 */ +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZR1; /*!< Debug MCU APB1 freeze register 1, Address offset: 0x08 */ + __IO uint32_t APB1FZR2; /*!< Debug MCU APB1 freeze register 2, Address offset: 0x0C */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x10 */ +} DBGMCU_TypeDef; + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA channel x configuration register */ + __IO uint32_t CNDTR; /*!< DMA channel x number of data register */ + __IO uint32_t CPAR; /*!< DMA channel x peripheral address register */ + __IO uint32_t CMAR; /*!< DMA channel x memory address register */ +} DMA_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t ISR; /*!< DMA interrupt status register, Address offset: 0x00 */ + __IO uint32_t IFCR; /*!< DMA interrupt flag clear register, Address offset: 0x04 */ +} DMA_TypeDef; + +/** + * @brief DMA Multiplexer + */ + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA Multiplexer Channel x Control Register Address offset: 0x0004 * (channel x) */ +}DMAMUX_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< DMA Channel Status Register Address offset: 0x0080 */ + __IO uint32_t CFR; /*!< DMA Channel Clear Flag Register Address offset: 0x0084 */ +}DMAMUX_ChannelStatus_TypeDef; + +typedef struct +{ + __IO uint32_t RGCR; /*!< DMA Request Generator x Control Register Address offset: 0x0100 + 0x0004 * (Req Gen x) */ +}DMAMUX_RequestGen_TypeDef; + +typedef struct +{ + __IO uint32_t RGSR; /*!< DMA Request Generator Status Register Address offset: 0x0140 */ + __IO uint32_t RGCFR; /*!< DMA Request Generator Clear Flag Register Address offset: 0x0144 */ +}DMAMUX_RequestGenStatus_TypeDef; + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR1; /*!< EXTI Interrupt mask register 1, Address offset: 0x00 */ + __IO uint32_t EMR1; /*!< EXTI Event mask register 1, Address offset: 0x04 */ + __IO uint32_t RTSR1; /*!< EXTI Rising trigger selection register 1, Address offset: 0x08 */ + __IO uint32_t FTSR1; /*!< EXTI Falling trigger selection register 1, Address offset: 0x0C */ + __IO uint32_t SWIER1; /*!< EXTI Software interrupt event register 1, Address offset: 0x10 */ + __IO uint32_t PR1; /*!< EXTI Pending register 1, Address offset: 0x14 */ + uint32_t RESERVED1; /*!< Reserved, 0x18 */ + uint32_t RESERVED2; /*!< Reserved, 0x1C */ + __IO uint32_t IMR2; /*!< EXTI Interrupt mask register 2, Address offset: 0x20 */ + __IO uint32_t EMR2; /*!< EXTI Event mask register 2, Address offset: 0x24 */ + __IO uint32_t RTSR2; /*!< EXTI Rising trigger selection register 2, Address offset: 0x28 */ + __IO uint32_t FTSR2; /*!< EXTI Falling trigger selection register 2, Address offset: 0x2C */ + __IO uint32_t SWIER2; /*!< EXTI Software interrupt event register 2, Address offset: 0x30 */ + __IO uint32_t PR2; /*!< EXTI Pending register 2, Address offset: 0x34 */ +} EXTI_TypeDef; + +/** + * @brief FLASH Registers + */ + +typedef struct +{ + __IO uint32_t ACR; /*!< FLASH access control register, Address offset: 0x00 */ + __IO uint32_t PDKEYR; /*!< FLASH power down key register, Address offset: 0x04 */ + __IO uint32_t KEYR; /*!< FLASH key register, Address offset: 0x08 */ + __IO uint32_t OPTKEYR; /*!< FLASH option key register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< FLASH status register, Address offset: 0x10 */ + __IO uint32_t CR; /*!< FLASH control register, Address offset: 0x14 */ + __IO uint32_t ECCR; /*!< FLASH ECC register, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved1, Address offset: 0x1C */ + __IO uint32_t OPTR; /*!< FLASH option register, Address offset: 0x20 */ + __IO uint32_t PCROP1SR; /*!< FLASH bank1 PCROP start address register, Address offset: 0x24 */ + __IO uint32_t PCROP1ER; /*!< FLASH bank1 PCROP end address register, Address offset: 0x28 */ + __IO uint32_t WRP1AR; /*!< FLASH bank1 WRP area A address register, Address offset: 0x2C */ + __IO uint32_t WRP1BR; /*!< FLASH bank1 WRP area B address register, Address offset: 0x30 */ + uint32_t RESERVED2[4]; /*!< Reserved2, Address offset: 0x34 */ + __IO uint32_t PCROP2SR; /*!< FLASH bank2 PCROP start address register, Address offset: 0x44 */ + __IO uint32_t PCROP2ER; /*!< FLASH bank2 PCROP end address register, Address offset: 0x48 */ + __IO uint32_t WRP2AR; /*!< FLASH bank2 WRP area A address register, Address offset: 0x4C */ + __IO uint32_t WRP2BR; /*!< FLASH bank2 WRP area B address register, Address offset: 0x50 */ + uint32_t RESERVED3[7]; /*!< Reserved3, Address offset: 0x54 */ + __IO uint32_t SEC1R; /*!< FLASH Securable memory register bank1, Address offset: 0x70 */ + __IO uint32_t SEC2R; /*!< FLASH Securable memory register bank2, Address offset: 0x74 */ +} FLASH_TypeDef; + +/** + * @brief FMAC + */ +typedef struct +{ + __IO uint32_t X1BUFCFG; /*!< FMAC X1 Buffer Configuration register, Address offset: 0x00 */ + __IO uint32_t X2BUFCFG; /*!< FMAC X2 Buffer Configuration register, Address offset: 0x04 */ + __IO uint32_t YBUFCFG; /*!< FMAC Y Buffer Configuration register, Address offset: 0x08 */ + __IO uint32_t PARAM; /*!< FMAC Parameter register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< FMAC Control register, Address offset: 0x10 */ + __IO uint32_t SR; /*!< FMAC Status register, Address offset: 0x14 */ + __IO uint32_t WDATA; /*!< FMAC Write Data register, Address offset: 0x18 */ + __IO uint32_t RDATA; /*!< FMAC Read Data register, Address offset: 0x1C */ +} FMAC_TypeDef; + +/** + * @brief Flexible Memory Controller + */ + +typedef struct +{ + __IO uint32_t BTCR[8]; /*!< NOR/PSRAM chip-select control register(BCR) and chip-select timing register(BTR), Address offset: 0x00-1C */ + __IO uint32_t PCSCNTR; /*!< PSRAM chip-select counter register, Address offset: 0x20 */ +} FMC_Bank1_TypeDef; + +/** + * @brief Flexible Memory Controller Bank1E + */ + +typedef struct +{ + __IO uint32_t BWTR[7]; /*!< NOR/PSRAM write timing registers, Address offset: 0x104-0x11C */ +} FMC_Bank1E_TypeDef; + +/** + * @brief Flexible Memory Controller Bank3 + */ + +typedef struct +{ + __IO uint32_t PCR; /*!< NAND Flash control register, Address offset: 0x80 */ + __IO uint32_t SR; /*!< NAND Flash FIFO status and interrupt register, Address offset: 0x84 */ + __IO uint32_t PMEM; /*!< NAND Flash Common memory space timing register, Address offset: 0x88 */ + __IO uint32_t PATT; /*!< NAND Flash Attribute memory space timing register, Address offset: 0x8C */ + uint32_t RESERVED0; /*!< Reserved, 0x90 */ + __IO uint32_t ECCR; /*!< NAND Flash ECC result registers, Address offset: 0x94 */ +} FMC_Bank3_TypeDef; + +/** + * @brief General Purpose I/O + */ + +typedef struct +{ + __IO uint32_t MODER; /*!< GPIO port mode register, Address offset: 0x00 */ + __IO uint32_t OTYPER; /*!< GPIO port output type register, Address offset: 0x04 */ + __IO uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset: 0x08 */ + __IO uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset: 0x0C */ + __IO uint32_t IDR; /*!< GPIO port input data register, Address offset: 0x10 */ + __IO uint32_t ODR; /*!< GPIO port output data register, Address offset: 0x14 */ + __IO uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset: 0x18 */ + __IO uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset: 0x1C */ + __IO uint32_t AFR[2]; /*!< GPIO alternate function registers, Address offset: 0x20-0x24 */ + __IO uint32_t BRR; /*!< GPIO Bit Reset register, Address offset: 0x28 */ +} GPIO_TypeDef; + +/** + * @brief Inter-integrated Circuit Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< I2C Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< I2C Control register 2, Address offset: 0x04 */ + __IO uint32_t OAR1; /*!< I2C Own address 1 register, Address offset: 0x08 */ + __IO uint32_t OAR2; /*!< I2C Own address 2 register, Address offset: 0x0C */ + __IO uint32_t TIMINGR; /*!< I2C Timing register, Address offset: 0x10 */ + __IO uint32_t TIMEOUTR; /*!< I2C Timeout register, Address offset: 0x14 */ + __IO uint32_t ISR; /*!< I2C Interrupt and status register, Address offset: 0x18 */ + __IO uint32_t ICR; /*!< I2C Interrupt clear register, Address offset: 0x1C */ + __IO uint32_t PECR; /*!< I2C PEC register, Address offset: 0x20 */ + __IO uint32_t RXDR; /*!< I2C Receive data register, Address offset: 0x24 */ + __IO uint32_t TXDR; /*!< I2C Transmit data register, Address offset: 0x28 */ +} I2C_TypeDef; + +/** + * @brief Independent WATCHDOG + */ + +typedef struct +{ + __IO uint32_t KR; /*!< IWDG Key register, Address offset: 0x00 */ + __IO uint32_t PR; /*!< IWDG Prescaler register, Address offset: 0x04 */ + __IO uint32_t RLR; /*!< IWDG Reload register, Address offset: 0x08 */ + __IO uint32_t SR; /*!< IWDG Status register, Address offset: 0x0C */ + __IO uint32_t WINR; /*!< IWDG Window register, Address offset: 0x10 */ +} IWDG_TypeDef; + +/** + * @brief LPTIMER + */ + +typedef struct +{ + __IO uint32_t ISR; /*!< LPTIM Interrupt and Status register, Address offset: 0x00 */ + __IO uint32_t ICR; /*!< LPTIM Interrupt Clear register, Address offset: 0x04 */ + __IO uint32_t IER; /*!< LPTIM Interrupt Enable register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< LPTIM Configuration register, Address offset: 0x0C */ + __IO uint32_t CR; /*!< LPTIM Control register, Address offset: 0x10 */ + __IO uint32_t CMP; /*!< LPTIM Compare register, Address offset: 0x14 */ + __IO uint32_t ARR; /*!< LPTIM Autoreload register, Address offset: 0x18 */ + __IO uint32_t CNT; /*!< LPTIM Counter register, Address offset: 0x1C */ + __IO uint32_t OR; /*!< LPTIM Option register, Address offset: 0x20 */ +} LPTIM_TypeDef; + +/** + * @brief Operational Amplifier (OPAMP) + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< OPAMP control/status register, Address offset: 0x00 */ + __IO uint32_t RESERVED[5]; /*!< OPAMP offset trimming register for normal mode, Address offset: 0x04 */ + __IO uint32_t TCMR; /*!< OPAMP timer controlled mux mode register, Address offset: 0x18 */ +} OPAMP_TypeDef; + +/** + * @brief Power Control + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< PWR power control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< PWR power control register 2, Address offset: 0x04 */ + __IO uint32_t CR3; /*!< PWR power control register 3, Address offset: 0x08 */ + __IO uint32_t CR4; /*!< PWR power control register 4, Address offset: 0x0C */ + __IO uint32_t SR1; /*!< PWR power status register 1, Address offset: 0x10 */ + __IO uint32_t SR2; /*!< PWR power status register 2, Address offset: 0x14 */ + __IO uint32_t SCR; /*!< PWR power status reset register, Address offset: 0x18 */ + uint32_t RESERVED; /*!< Reserved, Address offset: 0x1C */ + __IO uint32_t PUCRA; /*!< Pull_up control register of portA, Address offset: 0x20 */ + __IO uint32_t PDCRA; /*!< Pull_Down control register of portA, Address offset: 0x24 */ + __IO uint32_t PUCRB; /*!< Pull_up control register of portB, Address offset: 0x28 */ + __IO uint32_t PDCRB; /*!< Pull_Down control register of portB, Address offset: 0x2C */ + __IO uint32_t PUCRC; /*!< Pull_up control register of portC, Address offset: 0x30 */ + __IO uint32_t PDCRC; /*!< Pull_Down control register of portC, Address offset: 0x34 */ + __IO uint32_t PUCRD; /*!< Pull_up control register of portD, Address offset: 0x38 */ + __IO uint32_t PDCRD; /*!< Pull_Down control register of portD, Address offset: 0x3C */ + __IO uint32_t PUCRE; /*!< Pull_up control register of portE, Address offset: 0x40 */ + __IO uint32_t PDCRE; /*!< Pull_Down control register of portE, Address offset: 0x44 */ + __IO uint32_t PUCRF; /*!< Pull_up control register of portF, Address offset: 0x48 */ + __IO uint32_t PDCRF; /*!< Pull_Down control register of portF, Address offset: 0x4C */ + __IO uint32_t PUCRG; /*!< Pull_up control register of portG, Address offset: 0x50 */ + __IO uint32_t PDCRG; /*!< Pull_Down control register of portG, Address offset: 0x54 */ + uint32_t RESERVED1[10]; /*!< Reserved Address offset: 0x58 - 0x7C */ + __IO uint32_t CR5; /*!< PWR power control register 5, Address offset: 0x80 */ +} PWR_TypeDef; + +/** + * @brief QUAD Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR; /*!< QUADSPI Control register, Address offset: 0x00 */ + __IO uint32_t DCR; /*!< QUADSPI Device Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< QUADSPI Status register, Address offset: 0x08 */ + __IO uint32_t FCR; /*!< QUADSPI Flag Clear register, Address offset: 0x0C */ + __IO uint32_t DLR; /*!< QUADSPI Data Length register, Address offset: 0x10 */ + __IO uint32_t CCR; /*!< QUADSPI Communication Configuration register, Address offset: 0x14 */ + __IO uint32_t AR; /*!< QUADSPI Address register, Address offset: 0x18 */ + __IO uint32_t ABR; /*!< QUADSPI Alternate Bytes register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< QUADSPI Data register, Address offset: 0x20 */ + __IO uint32_t PSMKR; /*!< QUADSPI Polling Status Mask register, Address offset: 0x24 */ + __IO uint32_t PSMAR; /*!< QUADSPI Polling Status Match register, Address offset: 0x28 */ + __IO uint32_t PIR; /*!< QUADSPI Polling Interval register, Address offset: 0x2C */ + __IO uint32_t LPTR; /*!< QUADSPI Low Power Timeout register, Address offset: 0x30 */ +} QUADSPI_TypeDef; + +/** + * @brief Reset and Clock Control + */ + +typedef struct +{ + __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ + __IO uint32_t ICSCR; /*!< RCC internal clock sources calibration register, Address offset: 0x04 */ + __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ + __IO uint32_t PLLCFGR; /*!< RCC system PLL configuration register, Address offset: 0x0C */ + uint32_t RESERVED0; /*!< Reserved, Address offset: 0x10 */ + uint32_t RESERVED1; /*!< Reserved, Address offset: 0x14 */ + __IO uint32_t CIER; /*!< RCC clock interrupt enable register, Address offset: 0x18 */ + __IO uint32_t CIFR; /*!< RCC clock interrupt flag register, Address offset: 0x1C */ + __IO uint32_t CICR; /*!< RCC clock interrupt clear register, Address offset: 0x20 */ + uint32_t RESERVED2; /*!< Reserved, Address offset: 0x24 */ + __IO uint32_t AHB1RSTR; /*!< RCC AHB1 peripheral reset register, Address offset: 0x28 */ + __IO uint32_t AHB2RSTR; /*!< RCC AHB2 peripheral reset register, Address offset: 0x2C */ + __IO uint32_t AHB3RSTR; /*!< RCC AHB3 peripheral reset register, Address offset: 0x30 */ + uint32_t RESERVED3; /*!< Reserved, Address offset: 0x34 */ + __IO uint32_t APB1RSTR1; /*!< RCC APB1 peripheral reset register 1, Address offset: 0x38 */ + __IO uint32_t APB1RSTR2; /*!< RCC APB1 peripheral reset register 2, Address offset: 0x3C */ + __IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x40 */ + uint32_t RESERVED4; /*!< Reserved, Address offset: 0x44 */ + __IO uint32_t AHB1ENR; /*!< RCC AHB1 peripheral clocks enable register, Address offset: 0x48 */ + __IO uint32_t AHB2ENR; /*!< RCC AHB2 peripheral clocks enable register, Address offset: 0x4C */ + __IO uint32_t AHB3ENR; /*!< RCC AHB3 peripheral clocks enable register, Address offset: 0x50 */ + uint32_t RESERVED5; /*!< Reserved, Address offset: 0x54 */ + __IO uint32_t APB1ENR1; /*!< RCC APB1 peripheral clocks enable register 1, Address offset: 0x58 */ + __IO uint32_t APB1ENR2; /*!< RCC APB1 peripheral clocks enable register 2, Address offset: 0x5C */ + __IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clocks enable register, Address offset: 0x60 */ + uint32_t RESERVED6; /*!< Reserved, Address offset: 0x64 */ + __IO uint32_t AHB1SMENR; /*!< RCC AHB1 peripheral clocks enable in sleep and stop modes register, Address offset: 0x68 */ + __IO uint32_t AHB2SMENR; /*!< RCC AHB2 peripheral clocks enable in sleep and stop modes register, Address offset: 0x6C */ + __IO uint32_t AHB3SMENR; /*!< RCC AHB3 peripheral clocks enable in sleep and stop modes register, Address offset: 0x70 */ + uint32_t RESERVED7; /*!< Reserved, Address offset: 0x74 */ + __IO uint32_t APB1SMENR1; /*!< RCC APB1 peripheral clocks enable in sleep mode and stop modes register 1, Address offset: 0x78 */ + __IO uint32_t APB1SMENR2; /*!< RCC APB1 peripheral clocks enable in sleep mode and stop modes register 2, Address offset: 0x7C */ + __IO uint32_t APB2SMENR; /*!< RCC APB2 peripheral clocks enable in sleep mode and stop modes register, Address offset: 0x80 */ + uint32_t RESERVED8; /*!< Reserved, Address offset: 0x84 */ + __IO uint32_t CCIPR; /*!< RCC peripherals independent clock configuration register, Address offset: 0x88 */ + uint32_t RESERVED9; /*!< Reserved, Address offset: 0x8C */ + __IO uint32_t BDCR; /*!< RCC backup domain control register, Address offset: 0x90 */ + __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x94 */ + __IO uint32_t CRRCR; /*!< RCC clock recovery RC register, Address offset: 0x98 */ + __IO uint32_t CCIPR2; /*!< RCC peripherals independent clock configuration register 2, Address offset: 0x9C */ +} RCC_TypeDef; + +/** + * @brief Real-Time Clock + */ +/* +* @brief Specific device feature definitions +*/ +#define RTC_TAMP_INT_6_SUPPORT +#define RTC_TAMP_INT_NB 4u + +#define RTC_TAMP_NB 3u +#define RTC_BACKUP_NB 32u + + +typedef struct +{ + __IO uint32_t TR; /*!< RTC time register, Address offset: 0x00 */ + __IO uint32_t DR; /*!< RTC date register, Address offset: 0x04 */ + __IO uint32_t SSR; /*!< RTC sub second register, Address offset: 0x08 */ + __IO uint32_t ICSR; /*!< RTC initialization control and status register, Address offset: 0x0C */ + __IO uint32_t PRER; /*!< RTC prescaler register, Address offset: 0x10 */ + __IO uint32_t WUTR; /*!< RTC wakeup timer register, Address offset: 0x14 */ + __IO uint32_t CR; /*!< RTC control register, Address offset: 0x18 */ + uint32_t RESERVED0; /*!< Reserved Address offset: 0x1C */ + uint32_t RESERVED1; /*!< Reserved Address offset: 0x20 */ + __IO uint32_t WPR; /*!< RTC write protection register, Address offset: 0x24 */ + __IO uint32_t CALR; /*!< RTC calibration register, Address offset: 0x28 */ + __IO uint32_t SHIFTR; /*!< RTC shift control register, Address offset: 0x2C */ + __IO uint32_t TSTR; /*!< RTC time stamp time register, Address offset: 0x30 */ + __IO uint32_t TSDR; /*!< RTC time stamp date register, Address offset: 0x34 */ + __IO uint32_t TSSSR; /*!< RTC time-stamp sub second register, Address offset: 0x38 */ + uint32_t RESERVED2; /*!< Reserved Address offset: 0x3C */ + __IO uint32_t ALRMAR; /*!< RTC alarm A register, Address offset: 0x40 */ + __IO uint32_t ALRMASSR; /*!< RTC alarm A sub second register, Address offset: 0x44 */ + __IO uint32_t ALRMBR; /*!< RTC alarm B register, Address offset: 0x48 */ + __IO uint32_t ALRMBSSR; /*!< RTC alarm B sub second register, Address offset: 0x4C */ + __IO uint32_t SR; /*!< RTC Status register, Address offset: 0x50 */ + __IO uint32_t MISR; /*!< RTC Masked Interrupt Status register, Address offset: 0x54 */ + uint32_t RESERVED3; /*!< Reserved Address offset: 0x58 */ + __IO uint32_t SCR; /*!< RTC Status Clear register, Address offset: 0x5C */ +} RTC_TypeDef; + +/** + * @brief Tamper and backup registers + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TAMP configuration register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TAMP configuration register 2, Address offset: 0x04 */ + uint32_t RESERVED0; /*!< no configuration register 3, Address offset: 0x08 */ + __IO uint32_t FLTCR; /*!< TAMP filter control register, Address offset: 0x0C */ + uint32_t RESERVED1[6]; /*!< Reserved Address offset: 0x10 - 0x24 */ + uint32_t RESERVED2; /*!< Reserved Address offset: 0x28 */ + __IO uint32_t IER; /*!< TAMP Interrupt enable register, Address offset: 0x2C */ + __IO uint32_t SR; /*!< TAMP Status register, Address offset: 0x30 */ + __IO uint32_t MISR; /*!< TAMP Masked Interrupt Status register Address offset: 0x34 */ + uint32_t RESERVED3; /*!< Reserved Address offset: 0x38 */ + __IO uint32_t SCR; /*!< TAMP Status clear register, Address offset: 0x3C */ + uint32_t RESERVED4[48]; /*!< Reserved Address offset: 0x040 - 0xFC */ + __IO uint32_t BKP0R; /*!< TAMP backup register 0, Address offset: 0x100 */ + __IO uint32_t BKP1R; /*!< TAMP backup register 1, Address offset: 0x104 */ + __IO uint32_t BKP2R; /*!< TAMP backup register 2, Address offset: 0x108 */ + __IO uint32_t BKP3R; /*!< TAMP backup register 3, Address offset: 0x10C */ + __IO uint32_t BKP4R; /*!< TAMP backup register 4, Address offset: 0x110 */ + __IO uint32_t BKP5R; /*!< TAMP backup register 5, Address offset: 0x114 */ + __IO uint32_t BKP6R; /*!< TAMP backup register 6, Address offset: 0x118 */ + __IO uint32_t BKP7R; /*!< TAMP backup register 7, Address offset: 0x11C */ + __IO uint32_t BKP8R; /*!< TAMP backup register 8, Address offset: 0x120 */ + __IO uint32_t BKP9R; /*!< TAMP backup register 9, Address offset: 0x124 */ + __IO uint32_t BKP10R; /*!< TAMP backup register 10, Address offset: 0x128 */ + __IO uint32_t BKP11R; /*!< TAMP backup register 11, Address offset: 0x12C */ + __IO uint32_t BKP12R; /*!< TAMP backup register 12, Address offset: 0x130 */ + __IO uint32_t BKP13R; /*!< TAMP backup register 13, Address offset: 0x134 */ + __IO uint32_t BKP14R; /*!< TAMP backup register 14, Address offset: 0x138 */ + __IO uint32_t BKP15R; /*!< TAMP backup register 15, Address offset: 0x13C */ + __IO uint32_t BKP16R; /*!< TAMP backup register 16, Address offset: 0x140 */ + __IO uint32_t BKP17R; /*!< TAMP backup register 17, Address offset: 0x144 */ + __IO uint32_t BKP18R; /*!< TAMP backup register 18, Address offset: 0x148 */ + __IO uint32_t BKP19R; /*!< TAMP backup register 19, Address offset: 0x14C */ + __IO uint32_t BKP20R; /*!< TAMP backup register 20, Address offset: 0x150 */ + __IO uint32_t BKP21R; /*!< TAMP backup register 21, Address offset: 0x154 */ + __IO uint32_t BKP22R; /*!< TAMP backup register 22, Address offset: 0x158 */ + __IO uint32_t BKP23R; /*!< TAMP backup register 23, Address offset: 0x15C */ + __IO uint32_t BKP24R; /*!< TAMP backup register 24, Address offset: 0x160 */ + __IO uint32_t BKP25R; /*!< TAMP backup register 25, Address offset: 0x164 */ + __IO uint32_t BKP26R; /*!< TAMP backup register 26, Address offset: 0x168 */ + __IO uint32_t BKP27R; /*!< TAMP backup register 27, Address offset: 0x16C */ + __IO uint32_t BKP28R; /*!< TAMP backup register 28, Address offset: 0x170 */ + __IO uint32_t BKP29R; /*!< TAMP backup register 29, Address offset: 0x174 */ + __IO uint32_t BKP30R; /*!< TAMP backup register 30, Address offset: 0x178 */ + __IO uint32_t BKP31R; /*!< TAMP backup register 31, Address offset: 0x17C */ +} TAMP_TypeDef; + +/** + * @brief Serial Audio Interface + */ + +typedef struct +{ + uint32_t RESERVED[17]; /*!< Reserved, Address offset: 0x00 to 0x40 */ + __IO uint32_t PDMCR; /*!< SAI PDM control register, Address offset: 0x44 */ + __IO uint32_t PDMDLY; /*!< SAI PDM delay register, Address offset: 0x48 */ +} SAI_TypeDef; + +typedef struct +{ + __IO uint32_t CR1; /*!< SAI block x configuration register 1, Address offset: 0x04 */ + __IO uint32_t CR2; /*!< SAI block x configuration register 2, Address offset: 0x08 */ + __IO uint32_t FRCR; /*!< SAI block x frame configuration register, Address offset: 0x0C */ + __IO uint32_t SLOTR; /*!< SAI block x slot register, Address offset: 0x10 */ + __IO uint32_t IMR; /*!< SAI block x interrupt mask register, Address offset: 0x14 */ + __IO uint32_t SR; /*!< SAI block x status register, Address offset: 0x18 */ + __IO uint32_t CLRFR; /*!< SAI block x clear flag register, Address offset: 0x1C */ + __IO uint32_t DR; /*!< SAI block x data register, Address offset: 0x20 */ +} SAI_Block_TypeDef; + +/** + * @brief Serial Peripheral Interface + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< SPI Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< SPI Control register 2, Address offset: 0x04 */ + __IO uint32_t SR; /*!< SPI Status register, Address offset: 0x08 */ + __IO uint32_t DR; /*!< SPI data register, Address offset: 0x0C */ + __IO uint32_t CRCPR; /*!< SPI CRC polynomial register, Address offset: 0x10 */ + __IO uint32_t RXCRCR; /*!< SPI Rx CRC register, Address offset: 0x14 */ + __IO uint32_t TXCRCR; /*!< SPI Tx CRC register, Address offset: 0x18 */ + __IO uint32_t I2SCFGR; /*!< SPI_I2S configuration register, Address offset: 0x1C */ + __IO uint32_t I2SPR; /*!< SPI_I2S prescaler register, Address offset: 0x20 */ +} SPI_TypeDef; + +/** + * @brief System configuration controller + */ + +typedef struct +{ + __IO uint32_t MEMRMP; /*!< SYSCFG memory remap register, Address offset: 0x00 */ + __IO uint32_t CFGR1; /*!< SYSCFG configuration register 1, Address offset: 0x04 */ + __IO uint32_t EXTICR[4]; /*!< SYSCFG external interrupt configuration registers, Address offset: 0x08-0x14 */ + __IO uint32_t SCSR; /*!< SYSCFG CCMSRAM control and status register, Address offset: 0x18 */ + __IO uint32_t CFGR2; /*!< SYSCFG configuration register 2, Address offset: 0x1C */ + __IO uint32_t SWPR; /*!< SYSCFG CCMSRAM write protection register, Address offset: 0x20 */ + __IO uint32_t SKR; /*!< SYSCFG CCMSRAM Key Register, Address offset: 0x24 */ +} SYSCFG_TypeDef; + +/** + * @brief TIM + */ + +typedef struct +{ + __IO uint32_t CR1; /*!< TIM control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< TIM control register 2, Address offset: 0x04 */ + __IO uint32_t SMCR; /*!< TIM slave mode control register, Address offset: 0x08 */ + __IO uint32_t DIER; /*!< TIM DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t SR; /*!< TIM status register, Address offset: 0x10 */ + __IO uint32_t EGR; /*!< TIM event generation register, Address offset: 0x14 */ + __IO uint32_t CCMR1; /*!< TIM capture/compare mode register 1, Address offset: 0x18 */ + __IO uint32_t CCMR2; /*!< TIM capture/compare mode register 2, Address offset: 0x1C */ + __IO uint32_t CCER; /*!< TIM capture/compare enable register, Address offset: 0x20 */ + __IO uint32_t CNT; /*!< TIM counter register, Address offset: 0x24 */ + __IO uint32_t PSC; /*!< TIM prescaler, Address offset: 0x28 */ + __IO uint32_t ARR; /*!< TIM auto-reload register, Address offset: 0x2C */ + __IO uint32_t RCR; /*!< TIM repetition counter register, Address offset: 0x30 */ + __IO uint32_t CCR1; /*!< TIM capture/compare register 1, Address offset: 0x34 */ + __IO uint32_t CCR2; /*!< TIM capture/compare register 2, Address offset: 0x38 */ + __IO uint32_t CCR3; /*!< TIM capture/compare register 3, Address offset: 0x3C */ + __IO uint32_t CCR4; /*!< TIM capture/compare register 4, Address offset: 0x40 */ + __IO uint32_t BDTR; /*!< TIM break and dead-time register, Address offset: 0x44 */ + __IO uint32_t CCR5; /*!< TIM capture/compare register 5, Address offset: 0x48 */ + __IO uint32_t CCR6; /*!< TIM capture/compare register 6, Address offset: 0x4C */ + __IO uint32_t CCMR3; /*!< TIM capture/compare mode register 3, Address offset: 0x50 */ + __IO uint32_t DTR2; /*!< TIM deadtime register 2, Address offset: 0x54 */ + __IO uint32_t ECR; /*!< TIM encoder control register, Address offset: 0x58 */ + __IO uint32_t TISEL; /*!< TIM Input Selection register, Address offset: 0x5C */ + __IO uint32_t AF1; /*!< TIM alternate function option register 1, Address offset: 0x60 */ + __IO uint32_t AF2; /*!< TIM alternate function option register 2, Address offset: 0x64 */ + __IO uint32_t OR ; /*!< TIM option register, Address offset: 0x68 */ + uint32_t RESERVED0[220];/*!< Reserved, Address offset: 0x6C */ + __IO uint32_t DCR; /*!< TIM DMA control register, Address offset: 0x3DC */ + __IO uint32_t DMAR; /*!< TIM DMA address for full transfer, Address offset: 0x3E0 */ +} TIM_TypeDef; + +/** + * @brief Universal Synchronous Asynchronous Receiver Transmitter + */ +typedef struct +{ + __IO uint32_t CR1; /*!< USART Control register 1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< USART Control register 2, Address offset: 0x04 */ + __IO uint32_t CR3; /*!< USART Control register 3, Address offset: 0x08 */ + __IO uint32_t BRR; /*!< USART Baud rate register, Address offset: 0x0C */ + __IO uint32_t GTPR; /*!< USART Guard time and prescaler register, Address offset: 0x10 */ + __IO uint32_t RTOR; /*!< USART Receiver Timeout register, Address offset: 0x14 */ + __IO uint32_t RQR; /*!< USART Request register, Address offset: 0x18 */ + __IO uint32_t ISR; /*!< USART Interrupt and status register, Address offset: 0x1C */ + __IO uint32_t ICR; /*!< USART Interrupt flag Clear register, Address offset: 0x20 */ + __IO uint32_t RDR; /*!< USART Receive Data register, Address offset: 0x24 */ + __IO uint32_t TDR; /*!< USART Transmit Data register, Address offset: 0x28 */ + __IO uint32_t PRESC; /*!< USART Prescaler register, Address offset: 0x2C */ +} USART_TypeDef; + +/** + * @brief Universal Serial Bus Full Speed Device + */ + +typedef struct +{ + __IO uint16_t EP0R; /*!< USB Endpoint 0 register, Address offset: 0x00 */ + __IO uint16_t RESERVED0; /*!< Reserved */ + __IO uint16_t EP1R; /*!< USB Endpoint 1 register, Address offset: 0x04 */ + __IO uint16_t RESERVED1; /*!< Reserved */ + __IO uint16_t EP2R; /*!< USB Endpoint 2 register, Address offset: 0x08 */ + __IO uint16_t RESERVED2; /*!< Reserved */ + __IO uint16_t EP3R; /*!< USB Endpoint 3 register, Address offset: 0x0C */ + __IO uint16_t RESERVED3; /*!< Reserved */ + __IO uint16_t EP4R; /*!< USB Endpoint 4 register, Address offset: 0x10 */ + __IO uint16_t RESERVED4; /*!< Reserved */ + __IO uint16_t EP5R; /*!< USB Endpoint 5 register, Address offset: 0x14 */ + __IO uint16_t RESERVED5; /*!< Reserved */ + __IO uint16_t EP6R; /*!< USB Endpoint 6 register, Address offset: 0x18 */ + __IO uint16_t RESERVED6; /*!< Reserved */ + __IO uint16_t EP7R; /*!< USB Endpoint 7 register, Address offset: 0x1C */ + __IO uint16_t RESERVED7[17]; /*!< Reserved */ + __IO uint16_t CNTR; /*!< Control register, Address offset: 0x40 */ + __IO uint16_t RESERVED8; /*!< Reserved */ + __IO uint16_t ISTR; /*!< Interrupt status register, Address offset: 0x44 */ + __IO uint16_t RESERVED9; /*!< Reserved */ + __IO uint16_t FNR; /*!< Frame number register, Address offset: 0x48 */ + __IO uint16_t RESERVEDA; /*!< Reserved */ + __IO uint16_t DADDR; /*!< Device address register, Address offset: 0x4C */ + __IO uint16_t RESERVEDB; /*!< Reserved */ + __IO uint16_t BTABLE; /*!< Buffer Table address register, Address offset: 0x50 */ + __IO uint16_t RESERVEDC; /*!< Reserved */ + __IO uint16_t LPMCSR; /*!< LPM Control and Status register, Address offset: 0x54 */ + __IO uint16_t RESERVEDD; /*!< Reserved */ + __IO uint16_t BCDR; /*!< Battery Charging detector register, Address offset: 0x58 */ + __IO uint16_t RESERVEDE; /*!< Reserved */ +} USB_TypeDef; + +/** + * @brief VREFBUF + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< VREFBUF control and status register, Address offset: 0x00 */ + __IO uint32_t CCR; /*!< VREFBUF calibration and control register, Address offset: 0x04 */ +} VREFBUF_TypeDef; + +/** + * @brief Window WATCHDOG + */ + +typedef struct +{ + __IO uint32_t CR; /*!< WWDG Control register, Address offset: 0x00 */ + __IO uint32_t CFR; /*!< WWDG Configuration register, Address offset: 0x04 */ + __IO uint32_t SR; /*!< WWDG Status register, Address offset: 0x08 */ +} WWDG_TypeDef; + + +/** + * @brief RNG + */ +typedef struct +{ + __IO uint32_t CR; /*!< RNG control register, Address offset: 0x00 */ + __IO uint32_t SR; /*!< RNG status register, Address offset: 0x04 */ + __IO uint32_t DR; /*!< RNG data register, Address offset: 0x08 */ +} RNG_TypeDef; + +/** + * @brief CORDIC + */ + +typedef struct +{ + __IO uint32_t CSR; /*!< CORDIC control and status register, Address offset: 0x00 */ + __IO uint32_t WDATA; /*!< CORDIC argument register, Address offset: 0x04 */ + __IO uint32_t RDATA; /*!< CORDIC result register, Address offset: 0x08 */ +} CORDIC_TypeDef; + +/** + * @brief UCPD + */ + +typedef struct +{ + __IO uint32_t CFG1; /*!< UCPD configuration register 1, Address offset: 0x00 */ + __IO uint32_t CFG2; /*!< UCPD configuration register 2, Address offset: 0x04 */ + __IO uint32_t RESERVED0; /*!< UCPD reserved register, Address offset: 0x08 */ + __IO uint32_t CR; /*!< UCPD control register, Address offset: 0x0C */ + __IO uint32_t IMR; /*!< UCPD interrupt mask register, Address offset: 0x10 */ + __IO uint32_t SR; /*!< UCPD status register, Address offset: 0x14 */ + __IO uint32_t ICR; /*!< UCPD interrupt flag clear register Address offset: 0x18 */ + __IO uint32_t TX_ORDSET; /*!< UCPD Tx ordered set type register, Address offset: 0x1C */ + __IO uint32_t TX_PAYSZ; /*!< UCPD Tx payload size register, Address offset: 0x20 */ + __IO uint32_t TXDR; /*!< UCPD Tx data register, Address offset: 0x24 */ + __IO uint32_t RX_ORDSET; /*!< UCPD Rx ordered set type register, Address offset: 0x28 */ + __IO uint32_t RX_PAYSZ; /*!< UCPD Rx payload size register, Address offset: 0x2C */ + __IO uint32_t RXDR; /*!< UCPD Rx data register, Address offset: 0x30 */ + __IO uint32_t RX_ORDEXT1; /*!< UCPD Rx ordered set extension 1 register, Address offset: 0x34 */ + __IO uint32_t RX_ORDEXT2; /*!< UCPD Rx ordered set extension 2 register, Address offset: 0x38 */ +} UCPD_TypeDef; + +/** + * @brief High resolution Timer (HRTIM) + */ + +#define c7amba_hrtim1_v2_0 + +/* HRTIM master registers definition */ +typedef struct +{ + __IO uint32_t MCR; /*!< HRTIM Master Timer control register, Address offset: 0x00 */ + __IO uint32_t MISR; /*!< HRTIM Master Timer interrupt status register, Address offset: 0x04 */ + __IO uint32_t MICR; /*!< HRTIM Master Timer interrupt clear register, Address offset: 0x08 */ + __IO uint32_t MDIER; /*!< HRTIM Master Timer DMA/interrupt enable register Address offset: 0x0C */ + __IO uint32_t MCNTR; /*!< HRTIM Master Timer counter register, Address offset: 0x10 */ + __IO uint32_t MPER; /*!< HRTIM Master Timer period register, Address offset: 0x14 */ + __IO uint32_t MREP; /*!< HRTIM Master Timer repetition register, Address offset: 0x18 */ + __IO uint32_t MCMP1R; /*!< HRTIM Master Timer compare 1 register, Address offset: 0x1C */ + uint32_t RESERVED0; /*!< Reserved, 0x20 */ + __IO uint32_t MCMP2R; /*!< HRTIM Master Timer compare 2 register, Address offset: 0x24 */ + __IO uint32_t MCMP3R; /*!< HRTIM Master Timer compare 3 register, Address offset: 0x28 */ + __IO uint32_t MCMP4R; /*!< HRTIM Master Timer compare 4 register, Address offset: 0x2C */ + uint32_t RESERVED1[20]; /*!< Reserved, 0x30..0x7C */ +}HRTIM_Master_TypeDef; + +/* HRTIM Timer A to F registers definition */ +typedef struct +{ + __IO uint32_t TIMxCR; /*!< HRTIM Timerx control register, Address offset: 0x00 */ + __IO uint32_t TIMxISR; /*!< HRTIM Timerx interrupt status register, Address offset: 0x04 */ + __IO uint32_t TIMxICR; /*!< HRTIM Timerx interrupt clear register, Address offset: 0x08 */ + __IO uint32_t TIMxDIER; /*!< HRTIM Timerx DMA/interrupt enable register, Address offset: 0x0C */ + __IO uint32_t CNTxR; /*!< HRTIM Timerx counter register, Address offset: 0x10 */ + __IO uint32_t PERxR; /*!< HRTIM Timerx period register, Address offset: 0x14 */ + __IO uint32_t REPxR; /*!< HRTIM Timerx repetition register, Address offset: 0x18 */ + __IO uint32_t CMP1xR; /*!< HRTIM Timerx compare 1 register, Address offset: 0x1C */ + __IO uint32_t CMP1CxR; /*!< HRTIM Timerx compare 1 compound register, Address offset: 0x20 */ + __IO uint32_t CMP2xR; /*!< HRTIM Timerx compare 2 register, Address offset: 0x24 */ + __IO uint32_t CMP3xR; /*!< HRTIM Timerx compare 3 register, Address offset: 0x28 */ + __IO uint32_t CMP4xR; /*!< HRTIM Timerx compare 4 register, Address offset: 0x2C */ + __IO uint32_t CPT1xR; /*!< HRTIM Timerx capture 1 register, Address offset: 0x30 */ + __IO uint32_t CPT2xR; /*!< HRTIM Timerx capture 2 register, Address offset: 0x34 */ + __IO uint32_t DTxR; /*!< HRTIM Timerx dead time register, Address offset: 0x38 */ + __IO uint32_t SETx1R; /*!< HRTIM Timerx output 1 set register, Address offset: 0x3C */ + __IO uint32_t RSTx1R; /*!< HRTIM Timerx output 1 reset register, Address offset: 0x40 */ + __IO uint32_t SETx2R; /*!< HRTIM Timerx output 2 set register, Address offset: 0x44 */ + __IO uint32_t RSTx2R; /*!< HRTIM Timerx output 2 reset register, Address offset: 0x48 */ + __IO uint32_t EEFxR1; /*!< HRTIM Timerx external event filtering 1 register, Address offset: 0x4C */ + __IO uint32_t EEFxR2; /*!< HRTIM Timerx external event filtering 2 register, Address offset: 0x50 */ + __IO uint32_t RSTxR; /*!< HRTIM Timerx Reset register, Address offset: 0x54 */ + __IO uint32_t CHPxR; /*!< HRTIM Timerx Chopper register, Address offset: 0x58 */ + __IO uint32_t CPT1xCR; /*!< HRTIM Timerx Capture 1 register, Address offset: 0x5C */ + __IO uint32_t CPT2xCR; /*!< HRTIM Timerx Capture 2 register, Address offset: 0x60 */ + __IO uint32_t OUTxR; /*!< HRTIM Timerx Output register, Address offset: 0x64 */ + __IO uint32_t FLTxR; /*!< HRTIM Timerx Fault register, Address offset: 0x68 */ + __IO uint32_t TIMxCR2; /*!< HRTIM Timerx Control register 2, Address offset: 0x6C */ + __IO uint32_t EEFxR3; /*!< HRTIM Timerx external event filtering 3 register, Address offset: 0x70 */ + uint32_t RESERVED0[3]; /*!< Reserved, 0x74..0x7C */ +}HRTIM_Timerx_TypeDef; + +/* HRTIM common register definition */ +typedef struct +{ + __IO uint32_t CR1; /*!< HRTIM control register1, Address offset: 0x00 */ + __IO uint32_t CR2; /*!< HRTIM control register2, Address offset: 0x04 */ + __IO uint32_t ISR; /*!< HRTIM interrupt status register, Address offset: 0x08 */ + __IO uint32_t ICR; /*!< HRTIM interrupt clear register, Address offset: 0x0C */ + __IO uint32_t IER; /*!< HRTIM interrupt enable register, Address offset: 0x10 */ + __IO uint32_t OENR; /*!< HRTIM Output enable register, Address offset: 0x14 */ + __IO uint32_t ODISR; /*!< HRTIM Output disable register, Address offset: 0x18 */ + __IO uint32_t ODSR; /*!< HRTIM Output disable status register, Address offset: 0x1C */ + __IO uint32_t BMCR; /*!< HRTIM Burst mode control register, Address offset: 0x20 */ + __IO uint32_t BMTRGR; /*!< HRTIM Busrt mode trigger register, Address offset: 0x24 */ + __IO uint32_t BMCMPR; /*!< HRTIM Burst mode compare register, Address offset: 0x28 */ + __IO uint32_t BMPER; /*!< HRTIM Burst mode period register, Address offset: 0x2C */ + __IO uint32_t EECR1; /*!< HRTIM Timer external event control register1, Address offset: 0x30 */ + __IO uint32_t EECR2; /*!< HRTIM Timer external event control register2, Address offset: 0x34 */ + __IO uint32_t EECR3; /*!< HRTIM Timer external event control register3, Address offset: 0x38 */ + __IO uint32_t ADC1R; /*!< HRTIM ADC Trigger 1 register, Address offset: 0x3C */ + __IO uint32_t ADC2R; /*!< HRTIM ADC Trigger 2 register, Address offset: 0x40 */ + __IO uint32_t ADC3R; /*!< HRTIM ADC Trigger 3 register, Address offset: 0x44 */ + __IO uint32_t ADC4R; /*!< HRTIM ADC Trigger 4 register, Address offset: 0x48 */ + __IO uint32_t DLLCR; /*!< HRTIM DLL control register, Address offset: 0x4C */ + __IO uint32_t FLTINR1; /*!< HRTIM Fault input register1, Address offset: 0x50 */ + __IO uint32_t FLTINR2; /*!< HRTIM Fault input register2, Address offset: 0x54 */ + __IO uint32_t BDMUPR; /*!< HRTIM Burst DMA Master Timer update register, Address offset: 0x58 */ + __IO uint32_t BDTAUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x5C */ + __IO uint32_t BDTBUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x60 */ + __IO uint32_t BDTCUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x64 */ + __IO uint32_t BDTDUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x68 */ + __IO uint32_t BDTEUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x6C */ + __IO uint32_t BDMADR; /*!< HRTIM Burst DMA Master Data register, Address offset: 0x70 */ + __IO uint32_t BDTFUPR; /*!< HRTIM Burst DMA Timerx update register, Address offset: 0x74 */ + __IO uint32_t ADCER; /*!< HRTIM ADC Extended Trigger register, Address offset: 0x78 */ + __IO uint32_t ADCUR; /*!< HRTIM ADC Trigger Update register, Address offset: 0x7C */ + __IO uint32_t ADCPS1; /*!< HRTIM ADC Post Scaler Register 1, Address offset: 0x80 */ + __IO uint32_t ADCPS2; /*!< HRTIM ADC Post Scaler Register 2, Address offset: 0x84 */ + __IO uint32_t FLTINR3; /*!< HRTIM Fault input register3, Address offset: 0x88 */ + __IO uint32_t FLTINR4; /*!< HRTIM Fault input register4, Address offset: 0x8C */ +}HRTIM_Common_TypeDef; + +/* HRTIM register definition */ +typedef struct { + HRTIM_Master_TypeDef sMasterRegs; + HRTIM_Timerx_TypeDef sTimerxRegs[6]; + HRTIM_Common_TypeDef sCommonRegs; +}HRTIM_TypeDef; + +/** + * @} + */ + +/** @addtogroup Peripheral_memory_map + * @{ + */ + +#define FLASH_BASE (0x08000000UL) /*!< FLASH (up to 512 kB) base address */ +#define SRAM1_BASE (0x20000000UL) /*!< SRAM1(up to 80 KB) base address */ +#define SRAM2_BASE (0x20014000UL) /*!< SRAM2(16 KB) base address */ +#define CCMSRAM_BASE (0x10000000UL) /*!< CCMSRAM(32 KB) base address */ +#define PERIPH_BASE (0x40000000UL) /*!< Peripheral base address */ +#define FMC_BASE (0x60000000UL) /*!< FMC base address */ +#define QSPI_BASE (0x90000000UL) /*!< QUADSPI memories accessible over AHB base address */ + +#define FMC_R_BASE (0xA0000000UL) /*!< FMC control registers base address */ +#define QSPI_R_BASE (0xA0001000UL) /*!< QUADSPI control registers base address */ +#define SRAM1_BB_BASE (0x22000000UL) /*!< SRAM1(80 KB) base address in the bit-band region */ +#define SRAM2_BB_BASE (0x22280000UL) /*!< SRAM2(16 KB) base address in the bit-band region */ +#define CCMSRAM_BB_BASE (0x22300000UL) /*!< CCMSRAM(32 KB) base address in the bit-band region */ +#define PERIPH_BB_BASE (0x42000000UL) /*!< Peripheral base address in the bit-band region */ +/* Legacy defines */ +#define SRAM_BASE SRAM1_BASE +#define SRAM_BB_BASE SRAM1_BB_BASE + +#define SRAM1_SIZE_MAX (0x00014000UL) /*!< maximum SRAM1 size (up to 80 KBytes) */ +#define SRAM2_SIZE (0x00004000UL) /*!< SRAM2 size (16 KBytes) */ +#define CCMSRAM_SIZE (0x00008000UL) /*!< CCMSRAM size (32 KBytes) */ + +/*!< Peripheral memory map */ +#define APB1PERIPH_BASE PERIPH_BASE +#define APB2PERIPH_BASE (PERIPH_BASE + 0x00010000UL) +#define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000UL) +#define AHB2PERIPH_BASE (PERIPH_BASE + 0x08000000UL) + +#define FMC_BANK1 FMC_BASE +#define FMC_BANK1_1 FMC_BANK1 +#define FMC_BANK1_2 (FMC_BANK1 + 0x04000000UL) +#define FMC_BANK1_3 (FMC_BANK1 + 0x08000000UL) +#define FMC_BANK1_4 (FMC_BANK1 + 0x0C000000UL) +#define FMC_BANK3 (FMC_BASE + 0x20000000UL) + +/*!< APB1 peripherals */ +#define TIM2_BASE (APB1PERIPH_BASE + 0x0000UL) +#define TIM3_BASE (APB1PERIPH_BASE + 0x0400UL) +#define TIM4_BASE (APB1PERIPH_BASE + 0x0800UL) +#define TIM5_BASE (APB1PERIPH_BASE + 0x0C00UL) +#define TIM6_BASE (APB1PERIPH_BASE + 0x1000UL) +#define TIM7_BASE (APB1PERIPH_BASE + 0x1400UL) +#define CRS_BASE (APB1PERIPH_BASE + 0x2000UL) +#define TAMP_BASE (APB1PERIPH_BASE + 0x2400UL) +#define RTC_BASE (APB1PERIPH_BASE + 0x2800UL) +#define WWDG_BASE (APB1PERIPH_BASE + 0x2C00UL) +#define IWDG_BASE (APB1PERIPH_BASE + 0x3000UL) +#define SPI2_BASE (APB1PERIPH_BASE + 0x3800UL) +#define SPI3_BASE (APB1PERIPH_BASE + 0x3C00UL) +#define USART2_BASE (APB1PERIPH_BASE + 0x4400UL) +#define USART3_BASE (APB1PERIPH_BASE + 0x4800UL) +#define UART4_BASE (APB1PERIPH_BASE + 0x4C00UL) +#define UART5_BASE (APB1PERIPH_BASE + 0x5000UL) +#define I2C1_BASE (APB1PERIPH_BASE + 0x5400UL) +#define I2C2_BASE (APB1PERIPH_BASE + 0x5800UL) +#define USB_BASE (APB1PERIPH_BASE + 0x5C00UL) /*!< USB_IP Peripheral Registers base address */ +#define USB_PMAADDR (APB1PERIPH_BASE + 0x6000UL) /*!< USB_IP Packet Memory Area base address */ +#define FDCAN1_BASE (APB1PERIPH_BASE + 0x6400UL) +#define FDCAN_CONFIG_BASE (APB1PERIPH_BASE + 0x6500UL) /*!< FDCAN configuration registers base address */ +#define FDCAN2_BASE (APB1PERIPH_BASE + 0x6800UL) +#define FDCAN3_BASE (APB1PERIPH_BASE + 0x6C00UL) +#define PWR_BASE (APB1PERIPH_BASE + 0x7000UL) +#define I2C3_BASE (APB1PERIPH_BASE + 0x7800UL) +#define LPTIM1_BASE (APB1PERIPH_BASE + 0x7C00UL) +#define LPUART1_BASE (APB1PERIPH_BASE + 0x8000UL) +#define I2C4_BASE (APB1PERIPH_BASE + 0x8400UL) +#define UCPD1_BASE (APB1PERIPH_BASE + 0xA000UL) +#define SRAMCAN_BASE (APB1PERIPH_BASE + 0xA400UL) + +/*!< APB2 peripherals */ +#define SYSCFG_BASE (APB2PERIPH_BASE + 0x0000UL) +#define VREFBUF_BASE (APB2PERIPH_BASE + 0x0030UL) +#define COMP1_BASE (APB2PERIPH_BASE + 0x0200UL) +#define COMP2_BASE (APB2PERIPH_BASE + 0x0204UL) +#define COMP3_BASE (APB2PERIPH_BASE + 0x0208UL) +#define COMP4_BASE (APB2PERIPH_BASE + 0x020CUL) +#define COMP5_BASE (APB2PERIPH_BASE + 0x0210UL) +#define COMP6_BASE (APB2PERIPH_BASE + 0x0214UL) +#define COMP7_BASE (APB2PERIPH_BASE + 0x0218UL) +#define OPAMP_BASE (APB2PERIPH_BASE + 0x0300UL) +#define OPAMP1_BASE (APB2PERIPH_BASE + 0x0300UL) +#define OPAMP2_BASE (APB2PERIPH_BASE + 0x0304UL) +#define OPAMP3_BASE (APB2PERIPH_BASE + 0x0308UL) +#define OPAMP4_BASE (APB2PERIPH_BASE + 0x030CUL) +#define OPAMP5_BASE (APB2PERIPH_BASE + 0x0310UL) +#define OPAMP6_BASE (APB2PERIPH_BASE + 0x0314UL) + +#define EXTI_BASE (APB2PERIPH_BASE + 0x0400UL) +#define TIM1_BASE (APB2PERIPH_BASE + 0x2C00UL) +#define SPI1_BASE (APB2PERIPH_BASE + 0x3000UL) +#define TIM8_BASE (APB2PERIPH_BASE + 0x3400UL) +#define USART1_BASE (APB2PERIPH_BASE + 0x3800UL) +#define SPI4_BASE (APB2PERIPH_BASE + 0x3C00UL) +#define TIM15_BASE (APB2PERIPH_BASE + 0x4000UL) +#define TIM16_BASE (APB2PERIPH_BASE + 0x4400UL) +#define TIM17_BASE (APB2PERIPH_BASE + 0x4800UL) +#define TIM20_BASE (APB2PERIPH_BASE + 0x5000UL) +#define SAI1_BASE (APB2PERIPH_BASE + 0x5400UL) +#define SAI1_Block_A_BASE (SAI1_BASE + 0x0004UL) +#define SAI1_Block_B_BASE (SAI1_BASE + 0x0024UL) +#define HRTIM1_BASE (APB2PERIPH_BASE + 0x6800UL) +#define HRTIM1_TIMA_BASE (HRTIM1_BASE + 0x0080UL) +#define HRTIM1_TIMB_BASE (HRTIM1_BASE + 0x0100UL) +#define HRTIM1_TIMC_BASE (HRTIM1_BASE + 0x0180UL) +#define HRTIM1_TIMD_BASE (HRTIM1_BASE + 0x0200UL) +#define HRTIM1_TIME_BASE (HRTIM1_BASE + 0x0280UL) +#define HRTIM1_TIMF_BASE (HRTIM1_BASE + 0x0300UL) +#define HRTIM1_COMMON_BASE (HRTIM1_BASE + 0x0380UL) + +/*!< AHB1 peripherals */ +#define DMA1_BASE (AHB1PERIPH_BASE) +#define DMA2_BASE (AHB1PERIPH_BASE + 0x0400UL) +#define DMAMUX1_BASE (AHB1PERIPH_BASE + 0x0800UL) +#define CORDIC_BASE (AHB1PERIPH_BASE + 0x0C00UL) +#define RCC_BASE (AHB1PERIPH_BASE + 0x1000UL) +#define FMAC_BASE (AHB1PERIPH_BASE + 0x1400UL) +#define FLASH_R_BASE (AHB1PERIPH_BASE + 0x2000UL) +#define CRC_BASE (AHB1PERIPH_BASE + 0x3000UL) + +#define DMA1_Channel1_BASE (DMA1_BASE + 0x0008UL) +#define DMA1_Channel2_BASE (DMA1_BASE + 0x001CUL) +#define DMA1_Channel3_BASE (DMA1_BASE + 0x0030UL) +#define DMA1_Channel4_BASE (DMA1_BASE + 0x0044UL) +#define DMA1_Channel5_BASE (DMA1_BASE + 0x0058UL) +#define DMA1_Channel6_BASE (DMA1_BASE + 0x006CUL) +#define DMA1_Channel7_BASE (DMA1_BASE + 0x0080UL) +#define DMA1_Channel8_BASE (DMA1_BASE + 0x0094UL) + +#define DMA2_Channel1_BASE (DMA2_BASE + 0x0008UL) +#define DMA2_Channel2_BASE (DMA2_BASE + 0x001CUL) +#define DMA2_Channel3_BASE (DMA2_BASE + 0x0030UL) +#define DMA2_Channel4_BASE (DMA2_BASE + 0x0044UL) +#define DMA2_Channel5_BASE (DMA2_BASE + 0x0058UL) +#define DMA2_Channel6_BASE (DMA2_BASE + 0x006CUL) +#define DMA2_Channel7_BASE (DMA2_BASE + 0x0080UL) +#define DMA2_Channel8_BASE (DMA2_BASE + 0x0094UL) + +#define DMAMUX1_Channel0_BASE (DMAMUX1_BASE) +#define DMAMUX1_Channel1_BASE (DMAMUX1_BASE + 0x0004UL) +#define DMAMUX1_Channel2_BASE (DMAMUX1_BASE + 0x0008UL) +#define DMAMUX1_Channel3_BASE (DMAMUX1_BASE + 0x000CUL) +#define DMAMUX1_Channel4_BASE (DMAMUX1_BASE + 0x0010UL) +#define DMAMUX1_Channel5_BASE (DMAMUX1_BASE + 0x0014UL) +#define DMAMUX1_Channel6_BASE (DMAMUX1_BASE + 0x0018UL) +#define DMAMUX1_Channel7_BASE (DMAMUX1_BASE + 0x001CUL) +#define DMAMUX1_Channel8_BASE (DMAMUX1_BASE + 0x0020UL) +#define DMAMUX1_Channel9_BASE (DMAMUX1_BASE + 0x0024UL) +#define DMAMUX1_Channel10_BASE (DMAMUX1_BASE + 0x0028UL) +#define DMAMUX1_Channel11_BASE (DMAMUX1_BASE + 0x002CUL) +#define DMAMUX1_Channel12_BASE (DMAMUX1_BASE + 0x0030UL) +#define DMAMUX1_Channel13_BASE (DMAMUX1_BASE + 0x0034UL) +#define DMAMUX1_Channel14_BASE (DMAMUX1_BASE + 0x0038UL) +#define DMAMUX1_Channel15_BASE (DMAMUX1_BASE + 0x003CUL) +#define DMAMUX1_RequestGenerator0_BASE (DMAMUX1_BASE + 0x0100UL) +#define DMAMUX1_RequestGenerator1_BASE (DMAMUX1_BASE + 0x0104UL) +#define DMAMUX1_RequestGenerator2_BASE (DMAMUX1_BASE + 0x0108UL) +#define DMAMUX1_RequestGenerator3_BASE (DMAMUX1_BASE + 0x010CUL) + +#define DMAMUX1_ChannelStatus_BASE (DMAMUX1_BASE + 0x0080UL) +#define DMAMUX1_RequestGenStatus_BASE (DMAMUX1_BASE + 0x0140UL) + +/*!< AHB2 peripherals */ +#define GPIOA_BASE (AHB2PERIPH_BASE + 0x0000UL) +#define GPIOB_BASE (AHB2PERIPH_BASE + 0x0400UL) +#define GPIOC_BASE (AHB2PERIPH_BASE + 0x0800UL) +#define GPIOD_BASE (AHB2PERIPH_BASE + 0x0C00UL) +#define GPIOE_BASE (AHB2PERIPH_BASE + 0x1000UL) +#define GPIOF_BASE (AHB2PERIPH_BASE + 0x1400UL) +#define GPIOG_BASE (AHB2PERIPH_BASE + 0x1800UL) + +#define ADC1_BASE (AHB2PERIPH_BASE + 0x08000000UL) +#define ADC2_BASE (AHB2PERIPH_BASE + 0x08000100UL) +#define ADC12_COMMON_BASE (AHB2PERIPH_BASE + 0x08000300UL) +#define ADC3_BASE (AHB2PERIPH_BASE + 0x08000400UL) +#define ADC4_BASE (AHB2PERIPH_BASE + 0x08000500UL) +#define ADC5_BASE (AHB2PERIPH_BASE + 0x08000600UL) +#define ADC345_COMMON_BASE (AHB2PERIPH_BASE + 0x08000700UL) + +#define DAC_BASE (AHB2PERIPH_BASE + 0x08000800UL) +#define DAC1_BASE (AHB2PERIPH_BASE + 0x08000800UL) +#define DAC2_BASE (AHB2PERIPH_BASE + 0x08000C00UL) +#define DAC3_BASE (AHB2PERIPH_BASE + 0x08001000UL) +#define DAC4_BASE (AHB2PERIPH_BASE + 0x08001400UL) + +/*!< FMC Banks registers base address */ +#define FMC_Bank1_R_BASE (FMC_R_BASE + 0x0000UL) +#define FMC_Bank1E_R_BASE (FMC_R_BASE + 0x0104UL) +#define FMC_Bank3_R_BASE (FMC_R_BASE + 0x0080UL) +#define RNG_BASE (AHB2PERIPH_BASE + 0x08060800UL) +/* Debug MCU registers base address */ +#define DBGMCU_BASE (0xE0042000UL) + +#define PACKAGE_BASE (0x1FFF7500UL) /*!< Package data register base address */ +#define UID_BASE (0x1FFF7590UL) /*!< Unique device ID register base address */ +#define FLASHSIZE_BASE (0x1FFF75E0UL) /*!< Flash size data register base address */ +/** + * @} + */ + +/** @addtogroup Peripheral_declaration + * @{ + */ +#define TIM2 ((TIM_TypeDef *) TIM2_BASE) +#define TIM3 ((TIM_TypeDef *) TIM3_BASE) +#define TIM4 ((TIM_TypeDef *) TIM4_BASE) +#define TIM5 ((TIM_TypeDef *) TIM5_BASE) +#define TIM6 ((TIM_TypeDef *) TIM6_BASE) +#define TIM7 ((TIM_TypeDef *) TIM7_BASE) +#define CRS ((CRS_TypeDef *) CRS_BASE) +#define TAMP ((TAMP_TypeDef *) TAMP_BASE) +#define RTC ((RTC_TypeDef *) RTC_BASE) +#define WWDG ((WWDG_TypeDef *) WWDG_BASE) +#define IWDG ((IWDG_TypeDef *) IWDG_BASE) +#define SPI2 ((SPI_TypeDef *) SPI2_BASE) +#define SPI3 ((SPI_TypeDef *) SPI3_BASE) +#define USART2 ((USART_TypeDef *) USART2_BASE) +#define USART3 ((USART_TypeDef *) USART3_BASE) +#define UART4 ((USART_TypeDef *) UART4_BASE) +#define UART5 ((USART_TypeDef *) UART5_BASE) +#define I2C1 ((I2C_TypeDef *) I2C1_BASE) +#define I2C2 ((I2C_TypeDef *) I2C2_BASE) +#define USB ((USB_TypeDef *) USB_BASE) +#define FDCAN1 ((FDCAN_GlobalTypeDef *) FDCAN1_BASE) +#define FDCAN_CONFIG ((FDCAN_Config_TypeDef *) FDCAN_CONFIG_BASE) +#define FDCAN2 ((FDCAN_GlobalTypeDef *) FDCAN2_BASE) +#define FDCAN3 ((FDCAN_GlobalTypeDef *) FDCAN3_BASE) +#define PWR ((PWR_TypeDef *) PWR_BASE) +#define I2C3 ((I2C_TypeDef *) I2C3_BASE) +#define LPTIM1 ((LPTIM_TypeDef *) LPTIM1_BASE) +#define LPUART1 ((USART_TypeDef *) LPUART1_BASE) +#define I2C4 ((I2C_TypeDef *) I2C4_BASE) +#define UCPD1 ((UCPD_TypeDef *) UCPD1_BASE) + +#define SYSCFG ((SYSCFG_TypeDef *) SYSCFG_BASE) +#define VREFBUF ((VREFBUF_TypeDef *) VREFBUF_BASE) +#define COMP1 ((COMP_TypeDef *) COMP1_BASE) +#define COMP2 ((COMP_TypeDef *) COMP2_BASE) +#define COMP3 ((COMP_TypeDef *) COMP3_BASE) +#define COMP4 ((COMP_TypeDef *) COMP4_BASE) +#define COMP5 ((COMP_TypeDef *) COMP5_BASE) +#define COMP6 ((COMP_TypeDef *) COMP6_BASE) +#define COMP7 ((COMP_TypeDef *) COMP7_BASE) + +#define OPAMP ((OPAMP_TypeDef *) OPAMP_BASE) +#define OPAMP1 ((OPAMP_TypeDef *) OPAMP1_BASE) +#define OPAMP2 ((OPAMP_TypeDef *) OPAMP2_BASE) +#define OPAMP3 ((OPAMP_TypeDef *) OPAMP3_BASE) +#define OPAMP4 ((OPAMP_TypeDef *) OPAMP4_BASE) +#define OPAMP5 ((OPAMP_TypeDef *) OPAMP5_BASE) +#define OPAMP6 ((OPAMP_TypeDef *) OPAMP6_BASE) + +#define EXTI ((EXTI_TypeDef *) EXTI_BASE) +#define TIM1 ((TIM_TypeDef *) TIM1_BASE) +#define SPI1 ((SPI_TypeDef *) SPI1_BASE) +#define TIM8 ((TIM_TypeDef *) TIM8_BASE) +#define USART1 ((USART_TypeDef *) USART1_BASE) +#define SPI4 ((SPI_TypeDef *) SPI4_BASE) +#define TIM15 ((TIM_TypeDef *) TIM15_BASE) +#define TIM16 ((TIM_TypeDef *) TIM16_BASE) +#define TIM17 ((TIM_TypeDef *) TIM17_BASE) +#define TIM20 ((TIM_TypeDef *) TIM20_BASE) +#define SAI1 ((SAI_TypeDef *) SAI1_BASE) +#define SAI1_Block_A ((SAI_Block_TypeDef *)SAI1_Block_A_BASE) +#define SAI1_Block_B ((SAI_Block_TypeDef *)SAI1_Block_B_BASE) +#define HRTIM1 ((HRTIM_TypeDef *) HRTIM1_BASE) +#define HRTIM1_TIMA ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMA_BASE) +#define HRTIM1_TIMB ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMB_BASE) +#define HRTIM1_TIMC ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMC_BASE) +#define HRTIM1_TIMD ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMD_BASE) +#define HRTIM1_TIME ((HRTIM_Timerx_TypeDef *) HRTIM1_TIME_BASE) +#define HRTIM1_TIMF ((HRTIM_Timerx_TypeDef *) HRTIM1_TIMF_BASE) +#define HRTIM1_COMMON ((HRTIM_Common_TypeDef *) HRTIM1_COMMON_BASE) +#define DMA1 ((DMA_TypeDef *) DMA1_BASE) +#define DMA2 ((DMA_TypeDef *) DMA2_BASE) +#define DMAMUX1 ((DMAMUX_Channel_TypeDef *) DMAMUX1_BASE) +#define CORDIC ((CORDIC_TypeDef *) CORDIC_BASE) +#define RCC ((RCC_TypeDef *) RCC_BASE) +#define FMAC ((FMAC_TypeDef *) FMAC_BASE) +#define FLASH ((FLASH_TypeDef *) FLASH_R_BASE) +#define CRC ((CRC_TypeDef *) CRC_BASE) + +#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE) +#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) +#define GPIOC ((GPIO_TypeDef *) GPIOC_BASE) +#define GPIOD ((GPIO_TypeDef *) GPIOD_BASE) +#define GPIOE ((GPIO_TypeDef *) GPIOE_BASE) +#define GPIOF ((GPIO_TypeDef *) GPIOF_BASE) +#define GPIOG ((GPIO_TypeDef *) GPIOG_BASE) +#define ADC1 ((ADC_TypeDef *) ADC1_BASE) +#define ADC2 ((ADC_TypeDef *) ADC2_BASE) +#define ADC12_COMMON ((ADC_Common_TypeDef *) ADC12_COMMON_BASE) +#define ADC3 ((ADC_TypeDef *) ADC3_BASE) +#define ADC4 ((ADC_TypeDef *) ADC4_BASE) +#define ADC5 ((ADC_TypeDef *) ADC5_BASE) +#define ADC345_COMMON ((ADC_Common_TypeDef *) ADC345_COMMON_BASE) +#define DAC ((DAC_TypeDef *) DAC_BASE) +#define DAC1 ((DAC_TypeDef *) DAC1_BASE) +#define DAC2 ((DAC_TypeDef *) DAC2_BASE) +#define DAC3 ((DAC_TypeDef *) DAC3_BASE) +#define DAC4 ((DAC_TypeDef *) DAC4_BASE) +#define RNG ((RNG_TypeDef *) RNG_BASE) + +#define DMA1_Channel1 ((DMA_Channel_TypeDef *) DMA1_Channel1_BASE) +#define DMA1_Channel2 ((DMA_Channel_TypeDef *) DMA1_Channel2_BASE) +#define DMA1_Channel3 ((DMA_Channel_TypeDef *) DMA1_Channel3_BASE) +#define DMA1_Channel4 ((DMA_Channel_TypeDef *) DMA1_Channel4_BASE) +#define DMA1_Channel5 ((DMA_Channel_TypeDef *) DMA1_Channel5_BASE) +#define DMA1_Channel6 ((DMA_Channel_TypeDef *) DMA1_Channel6_BASE) +#define DMA1_Channel7 ((DMA_Channel_TypeDef *) DMA1_Channel7_BASE) +#define DMA1_Channel8 ((DMA_Channel_TypeDef *) DMA1_Channel8_BASE) + +#define DMA2_Channel1 ((DMA_Channel_TypeDef *) DMA2_Channel1_BASE) +#define DMA2_Channel2 ((DMA_Channel_TypeDef *) DMA2_Channel2_BASE) +#define DMA2_Channel3 ((DMA_Channel_TypeDef *) DMA2_Channel3_BASE) +#define DMA2_Channel4 ((DMA_Channel_TypeDef *) DMA2_Channel4_BASE) +#define DMA2_Channel5 ((DMA_Channel_TypeDef *) DMA2_Channel5_BASE) +#define DMA2_Channel6 ((DMA_Channel_TypeDef *) DMA2_Channel6_BASE) +#define DMA2_Channel7 ((DMA_Channel_TypeDef *) DMA2_Channel7_BASE) +#define DMA2_Channel8 ((DMA_Channel_TypeDef *) DMA2_Channel8_BASE) + +#define DMAMUX1_Channel0 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel0_BASE) +#define DMAMUX1_Channel1 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel1_BASE) +#define DMAMUX1_Channel2 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel2_BASE) +#define DMAMUX1_Channel3 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel3_BASE) +#define DMAMUX1_Channel4 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel4_BASE) +#define DMAMUX1_Channel5 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel5_BASE) +#define DMAMUX1_Channel6 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel6_BASE) +#define DMAMUX1_Channel7 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel7_BASE) +#define DMAMUX1_Channel8 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel8_BASE) +#define DMAMUX1_Channel9 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel9_BASE) +#define DMAMUX1_Channel10 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel10_BASE) +#define DMAMUX1_Channel11 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel11_BASE) +#define DMAMUX1_Channel12 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel12_BASE) +#define DMAMUX1_Channel13 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel13_BASE) +#define DMAMUX1_Channel14 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel14_BASE) +#define DMAMUX1_Channel15 ((DMAMUX_Channel_TypeDef *) DMAMUX1_Channel15_BASE) + +#define DMAMUX1_RequestGenerator0 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator0_BASE) +#define DMAMUX1_RequestGenerator1 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator1_BASE) +#define DMAMUX1_RequestGenerator2 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator2_BASE) +#define DMAMUX1_RequestGenerator3 ((DMAMUX_RequestGen_TypeDef *) DMAMUX1_RequestGenerator3_BASE) + +#define DMAMUX1_ChannelStatus ((DMAMUX_ChannelStatus_TypeDef *) DMAMUX1_ChannelStatus_BASE) +#define DMAMUX1_RequestGenStatus ((DMAMUX_RequestGenStatus_TypeDef *) DMAMUX1_RequestGenStatus_BASE) + +#define FMC_Bank1_R ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE) +#define FMC_Bank1E_R ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE) +#define FMC_Bank3_R ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE) + +#define QUADSPI ((QUADSPI_TypeDef *) QSPI_R_BASE) + +#define DBGMCU ((DBGMCU_TypeDef *) DBGMCU_BASE) + +/** + * @} + */ + +/** @addtogroup Exported_constants + * @{ + */ + + /** @addtogroup Hardware_Constant_Definition + * @{ + */ +#define LSI_STARTUP_TIME 130U /*!< LSI Maximum startup time in us */ + + /** + * @} + */ + +/** @addtogroup Peripheral_Registers_Bits_Definition + * @{ + */ + +/******************************************************************************/ +/* Peripheral Registers_Bits_Definition */ +/******************************************************************************/ + +/******************************************************************************/ +/* */ +/* Analog to Digital Converter */ +/* */ +/******************************************************************************/ + +/* + * @brief Specific device feature definitions (not present on all devices in the STM32G4 series) + */ +#define ADC_MULTIMODE_SUPPORT /*!< ADC feature available only on specific devices: multimode available on devices with several ADC instances */ + +/******************** Bit definition for ADC_ISR register *******************/ +#define ADC_ISR_ADRDY_Pos (0U) +#define ADC_ISR_ADRDY_Msk (0x1UL << ADC_ISR_ADRDY_Pos) /*!< 0x00000001 */ +#define ADC_ISR_ADRDY ADC_ISR_ADRDY_Msk /*!< ADC ready flag */ +#define ADC_ISR_EOSMP_Pos (1U) +#define ADC_ISR_EOSMP_Msk (0x1UL << ADC_ISR_EOSMP_Pos) /*!< 0x00000002 */ +#define ADC_ISR_EOSMP ADC_ISR_EOSMP_Msk /*!< ADC group regular end of sampling flag */ +#define ADC_ISR_EOC_Pos (2U) +#define ADC_ISR_EOC_Msk (0x1UL << ADC_ISR_EOC_Pos) /*!< 0x00000004 */ +#define ADC_ISR_EOC ADC_ISR_EOC_Msk /*!< ADC group regular end of unitary conversion flag */ +#define ADC_ISR_EOS_Pos (3U) +#define ADC_ISR_EOS_Msk (0x1UL << ADC_ISR_EOS_Pos) /*!< 0x00000008 */ +#define ADC_ISR_EOS ADC_ISR_EOS_Msk /*!< ADC group regular end of sequence conversions flag */ +#define ADC_ISR_OVR_Pos (4U) +#define ADC_ISR_OVR_Msk (0x1UL << ADC_ISR_OVR_Pos) /*!< 0x00000010 */ +#define ADC_ISR_OVR ADC_ISR_OVR_Msk /*!< ADC group regular overrun flag */ +#define ADC_ISR_JEOC_Pos (5U) +#define ADC_ISR_JEOC_Msk (0x1UL << ADC_ISR_JEOC_Pos) /*!< 0x00000020 */ +#define ADC_ISR_JEOC ADC_ISR_JEOC_Msk /*!< ADC group injected end of unitary conversion flag */ +#define ADC_ISR_JEOS_Pos (6U) +#define ADC_ISR_JEOS_Msk (0x1UL << ADC_ISR_JEOS_Pos) /*!< 0x00000040 */ +#define ADC_ISR_JEOS ADC_ISR_JEOS_Msk /*!< ADC group injected end of sequence conversions flag */ +#define ADC_ISR_AWD1_Pos (7U) +#define ADC_ISR_AWD1_Msk (0x1UL << ADC_ISR_AWD1_Pos) /*!< 0x00000080 */ +#define ADC_ISR_AWD1 ADC_ISR_AWD1_Msk /*!< ADC analog watchdog 1 flag */ +#define ADC_ISR_AWD2_Pos (8U) +#define ADC_ISR_AWD2_Msk (0x1UL << ADC_ISR_AWD2_Pos) /*!< 0x00000100 */ +#define ADC_ISR_AWD2 ADC_ISR_AWD2_Msk /*!< ADC analog watchdog 2 flag */ +#define ADC_ISR_AWD3_Pos (9U) +#define ADC_ISR_AWD3_Msk (0x1UL << ADC_ISR_AWD3_Pos) /*!< 0x00000200 */ +#define ADC_ISR_AWD3 ADC_ISR_AWD3_Msk /*!< ADC analog watchdog 3 flag */ +#define ADC_ISR_JQOVF_Pos (10U) +#define ADC_ISR_JQOVF_Msk (0x1UL << ADC_ISR_JQOVF_Pos) /*!< 0x00000400 */ +#define ADC_ISR_JQOVF ADC_ISR_JQOVF_Msk /*!< ADC group injected contexts queue overflow flag */ + +/******************** Bit definition for ADC_IER register *******************/ +#define ADC_IER_ADRDYIE_Pos (0U) +#define ADC_IER_ADRDYIE_Msk (0x1UL << ADC_IER_ADRDYIE_Pos) /*!< 0x00000001 */ +#define ADC_IER_ADRDYIE ADC_IER_ADRDYIE_Msk /*!< ADC ready interrupt */ +#define ADC_IER_EOSMPIE_Pos (1U) +#define ADC_IER_EOSMPIE_Msk (0x1UL << ADC_IER_EOSMPIE_Pos) /*!< 0x00000002 */ +#define ADC_IER_EOSMPIE ADC_IER_EOSMPIE_Msk /*!< ADC group regular end of sampling interrupt */ +#define ADC_IER_EOCIE_Pos (2U) +#define ADC_IER_EOCIE_Msk (0x1UL << ADC_IER_EOCIE_Pos) /*!< 0x00000004 */ +#define ADC_IER_EOCIE ADC_IER_EOCIE_Msk /*!< ADC group regular end of unitary conversion interrupt */ +#define ADC_IER_EOSIE_Pos (3U) +#define ADC_IER_EOSIE_Msk (0x1UL << ADC_IER_EOSIE_Pos) /*!< 0x00000008 */ +#define ADC_IER_EOSIE ADC_IER_EOSIE_Msk /*!< ADC group regular end of sequence conversions interrupt */ +#define ADC_IER_OVRIE_Pos (4U) +#define ADC_IER_OVRIE_Msk (0x1UL << ADC_IER_OVRIE_Pos) /*!< 0x00000010 */ +#define ADC_IER_OVRIE ADC_IER_OVRIE_Msk /*!< ADC group regular overrun interrupt */ +#define ADC_IER_JEOCIE_Pos (5U) +#define ADC_IER_JEOCIE_Msk (0x1UL << ADC_IER_JEOCIE_Pos) /*!< 0x00000020 */ +#define ADC_IER_JEOCIE ADC_IER_JEOCIE_Msk /*!< ADC group injected end of unitary conversion interrupt */ +#define ADC_IER_JEOSIE_Pos (6U) +#define ADC_IER_JEOSIE_Msk (0x1UL << ADC_IER_JEOSIE_Pos) /*!< 0x00000040 */ +#define ADC_IER_JEOSIE ADC_IER_JEOSIE_Msk /*!< ADC group injected end of sequence conversions interrupt */ +#define ADC_IER_AWD1IE_Pos (7U) +#define ADC_IER_AWD1IE_Msk (0x1UL << ADC_IER_AWD1IE_Pos) /*!< 0x00000080 */ +#define ADC_IER_AWD1IE ADC_IER_AWD1IE_Msk /*!< ADC analog watchdog 1 interrupt */ +#define ADC_IER_AWD2IE_Pos (8U) +#define ADC_IER_AWD2IE_Msk (0x1UL << ADC_IER_AWD2IE_Pos) /*!< 0x00000100 */ +#define ADC_IER_AWD2IE ADC_IER_AWD2IE_Msk /*!< ADC analog watchdog 2 interrupt */ +#define ADC_IER_AWD3IE_Pos (9U) +#define ADC_IER_AWD3IE_Msk (0x1UL << ADC_IER_AWD3IE_Pos) /*!< 0x00000200 */ +#define ADC_IER_AWD3IE ADC_IER_AWD3IE_Msk /*!< ADC analog watchdog 3 interrupt */ +#define ADC_IER_JQOVFIE_Pos (10U) +#define ADC_IER_JQOVFIE_Msk (0x1UL << ADC_IER_JQOVFIE_Pos) /*!< 0x00000400 */ +#define ADC_IER_JQOVFIE ADC_IER_JQOVFIE_Msk /*!< ADC group injected contexts queue overflow interrupt */ + +/******************** Bit definition for ADC_CR register ********************/ +#define ADC_CR_ADEN_Pos (0U) +#define ADC_CR_ADEN_Msk (0x1UL << ADC_CR_ADEN_Pos) /*!< 0x00000001 */ +#define ADC_CR_ADEN ADC_CR_ADEN_Msk /*!< ADC enable */ +#define ADC_CR_ADDIS_Pos (1U) +#define ADC_CR_ADDIS_Msk (0x1UL << ADC_CR_ADDIS_Pos) /*!< 0x00000002 */ +#define ADC_CR_ADDIS ADC_CR_ADDIS_Msk /*!< ADC disable */ +#define ADC_CR_ADSTART_Pos (2U) +#define ADC_CR_ADSTART_Msk (0x1UL << ADC_CR_ADSTART_Pos) /*!< 0x00000004 */ +#define ADC_CR_ADSTART ADC_CR_ADSTART_Msk /*!< ADC group regular conversion start */ +#define ADC_CR_JADSTART_Pos (3U) +#define ADC_CR_JADSTART_Msk (0x1UL << ADC_CR_JADSTART_Pos) /*!< 0x00000008 */ +#define ADC_CR_JADSTART ADC_CR_JADSTART_Msk /*!< ADC group injected conversion start */ +#define ADC_CR_ADSTP_Pos (4U) +#define ADC_CR_ADSTP_Msk (0x1UL << ADC_CR_ADSTP_Pos) /*!< 0x00000010 */ +#define ADC_CR_ADSTP ADC_CR_ADSTP_Msk /*!< ADC group regular conversion stop */ +#define ADC_CR_JADSTP_Pos (5U) +#define ADC_CR_JADSTP_Msk (0x1UL << ADC_CR_JADSTP_Pos) /*!< 0x00000020 */ +#define ADC_CR_JADSTP ADC_CR_JADSTP_Msk /*!< ADC group injected conversion stop */ +#define ADC_CR_ADVREGEN_Pos (28U) +#define ADC_CR_ADVREGEN_Msk (0x1UL << ADC_CR_ADVREGEN_Pos) /*!< 0x10000000 */ +#define ADC_CR_ADVREGEN ADC_CR_ADVREGEN_Msk /*!< ADC voltage regulator enable */ +#define ADC_CR_DEEPPWD_Pos (29U) +#define ADC_CR_DEEPPWD_Msk (0x1UL << ADC_CR_DEEPPWD_Pos) /*!< 0x20000000 */ +#define ADC_CR_DEEPPWD ADC_CR_DEEPPWD_Msk /*!< ADC deep power down enable */ +#define ADC_CR_ADCALDIF_Pos (30U) +#define ADC_CR_ADCALDIF_Msk (0x1UL << ADC_CR_ADCALDIF_Pos) /*!< 0x40000000 */ +#define ADC_CR_ADCALDIF ADC_CR_ADCALDIF_Msk /*!< ADC differential mode for calibration */ +#define ADC_CR_ADCAL_Pos (31U) +#define ADC_CR_ADCAL_Msk (0x1UL << ADC_CR_ADCAL_Pos) /*!< 0x80000000 */ +#define ADC_CR_ADCAL ADC_CR_ADCAL_Msk /*!< ADC calibration */ + +/******************** Bit definition for ADC_CFGR register ******************/ +#define ADC_CFGR_DMAEN_Pos (0U) +#define ADC_CFGR_DMAEN_Msk (0x1UL << ADC_CFGR_DMAEN_Pos) /*!< 0x00000001 */ +#define ADC_CFGR_DMAEN ADC_CFGR_DMAEN_Msk /*!< ADC DMA transfer enable */ +#define ADC_CFGR_DMACFG_Pos (1U) +#define ADC_CFGR_DMACFG_Msk (0x1UL << ADC_CFGR_DMACFG_Pos) /*!< 0x00000002 */ +#define ADC_CFGR_DMACFG ADC_CFGR_DMACFG_Msk /*!< ADC DMA transfer configuration */ + +#define ADC_CFGR_RES_Pos (3U) +#define ADC_CFGR_RES_Msk (0x3UL << ADC_CFGR_RES_Pos) /*!< 0x00000018 */ +#define ADC_CFGR_RES ADC_CFGR_RES_Msk /*!< ADC data resolution */ +#define ADC_CFGR_RES_0 (0x1UL << ADC_CFGR_RES_Pos) /*!< 0x00000008 */ +#define ADC_CFGR_RES_1 (0x2UL << ADC_CFGR_RES_Pos) /*!< 0x00000010 */ + +#define ADC_CFGR_EXTSEL_Pos (5U) +#define ADC_CFGR_EXTSEL_Msk (0x1FUL << ADC_CFGR_EXTSEL_Pos) /*!< 0x000003E0 */ +#define ADC_CFGR_EXTSEL ADC_CFGR_EXTSEL_Msk /*!< ADC group regular external trigger source */ +#define ADC_CFGR_EXTSEL_0 (0x1UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000020 */ +#define ADC_CFGR_EXTSEL_1 (0x2UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000040 */ +#define ADC_CFGR_EXTSEL_2 (0x4UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000080 */ +#define ADC_CFGR_EXTSEL_3 (0x8UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000100 */ +#define ADC_CFGR_EXTSEL_4 (0x10UL << ADC_CFGR_EXTSEL_Pos) /*!< 0x00000200 */ + +#define ADC_CFGR_EXTEN_Pos (10U) +#define ADC_CFGR_EXTEN_Msk (0x3UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000C00 */ +#define ADC_CFGR_EXTEN ADC_CFGR_EXTEN_Msk /*!< ADC group regular external trigger polarity */ +#define ADC_CFGR_EXTEN_0 (0x1UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000400 */ +#define ADC_CFGR_EXTEN_1 (0x2UL << ADC_CFGR_EXTEN_Pos) /*!< 0x00000800 */ + +#define ADC_CFGR_OVRMOD_Pos (12U) +#define ADC_CFGR_OVRMOD_Msk (0x1UL << ADC_CFGR_OVRMOD_Pos) /*!< 0x00001000 */ +#define ADC_CFGR_OVRMOD ADC_CFGR_OVRMOD_Msk /*!< ADC group regular overrun configuration */ +#define ADC_CFGR_CONT_Pos (13U) +#define ADC_CFGR_CONT_Msk (0x1UL << ADC_CFGR_CONT_Pos) /*!< 0x00002000 */ +#define ADC_CFGR_CONT ADC_CFGR_CONT_Msk /*!< ADC group regular continuous conversion mode */ +#define ADC_CFGR_AUTDLY_Pos (14U) +#define ADC_CFGR_AUTDLY_Msk (0x1UL << ADC_CFGR_AUTDLY_Pos) /*!< 0x00004000 */ +#define ADC_CFGR_AUTDLY ADC_CFGR_AUTDLY_Msk /*!< ADC low power auto wait */ +#define ADC_CFGR_ALIGN_Pos (15U) +#define ADC_CFGR_ALIGN_Msk (0x1UL << ADC_CFGR_ALIGN_Pos) /*!< 0x00008000 */ +#define ADC_CFGR_ALIGN ADC_CFGR_ALIGN_Msk /*!< ADC data alignment */ +#define ADC_CFGR_DISCEN_Pos (16U) +#define ADC_CFGR_DISCEN_Msk (0x1UL << ADC_CFGR_DISCEN_Pos) /*!< 0x00010000 */ +#define ADC_CFGR_DISCEN ADC_CFGR_DISCEN_Msk /*!< ADC group regular sequencer discontinuous mode */ + +#define ADC_CFGR_DISCNUM_Pos (17U) +#define ADC_CFGR_DISCNUM_Msk (0x7UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x000E0000 */ +#define ADC_CFGR_DISCNUM ADC_CFGR_DISCNUM_Msk /*!< ADC group regular sequencer discontinuous number of ranks */ +#define ADC_CFGR_DISCNUM_0 (0x1UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00020000 */ +#define ADC_CFGR_DISCNUM_1 (0x2UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00040000 */ +#define ADC_CFGR_DISCNUM_2 (0x4UL << ADC_CFGR_DISCNUM_Pos) /*!< 0x00080000 */ + +#define ADC_CFGR_JDISCEN_Pos (20U) +#define ADC_CFGR_JDISCEN_Msk (0x1UL << ADC_CFGR_JDISCEN_Pos) /*!< 0x00100000 */ +#define ADC_CFGR_JDISCEN ADC_CFGR_JDISCEN_Msk /*!< ADC group injected sequencer discontinuous mode */ +#define ADC_CFGR_JQM_Pos (21U) +#define ADC_CFGR_JQM_Msk (0x1UL << ADC_CFGR_JQM_Pos) /*!< 0x00200000 */ +#define ADC_CFGR_JQM ADC_CFGR_JQM_Msk /*!< ADC group injected contexts queue mode */ +#define ADC_CFGR_AWD1SGL_Pos (22U) +#define ADC_CFGR_AWD1SGL_Msk (0x1UL << ADC_CFGR_AWD1SGL_Pos) /*!< 0x00400000 */ +#define ADC_CFGR_AWD1SGL ADC_CFGR_AWD1SGL_Msk /*!< ADC analog watchdog 1 monitoring a single channel or all channels */ +#define ADC_CFGR_AWD1EN_Pos (23U) +#define ADC_CFGR_AWD1EN_Msk (0x1UL << ADC_CFGR_AWD1EN_Pos) /*!< 0x00800000 */ +#define ADC_CFGR_AWD1EN ADC_CFGR_AWD1EN_Msk /*!< ADC analog watchdog 1 enable on scope ADC group regular */ +#define ADC_CFGR_JAWD1EN_Pos (24U) +#define ADC_CFGR_JAWD1EN_Msk (0x1UL << ADC_CFGR_JAWD1EN_Pos) /*!< 0x01000000 */ +#define ADC_CFGR_JAWD1EN ADC_CFGR_JAWD1EN_Msk /*!< ADC analog watchdog 1 enable on scope ADC group injected */ +#define ADC_CFGR_JAUTO_Pos (25U) +#define ADC_CFGR_JAUTO_Msk (0x1UL << ADC_CFGR_JAUTO_Pos) /*!< 0x02000000 */ +#define ADC_CFGR_JAUTO ADC_CFGR_JAUTO_Msk /*!< ADC group injected automatic trigger mode */ + +#define ADC_CFGR_AWD1CH_Pos (26U) +#define ADC_CFGR_AWD1CH_Msk (0x1FUL << ADC_CFGR_AWD1CH_Pos) /*!< 0x7C000000 */ +#define ADC_CFGR_AWD1CH ADC_CFGR_AWD1CH_Msk /*!< ADC analog watchdog 1 monitored channel selection */ +#define ADC_CFGR_AWD1CH_0 (0x01UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x04000000 */ +#define ADC_CFGR_AWD1CH_1 (0x02UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x08000000 */ +#define ADC_CFGR_AWD1CH_2 (0x04UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x10000000 */ +#define ADC_CFGR_AWD1CH_3 (0x08UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x20000000 */ +#define ADC_CFGR_AWD1CH_4 (0x10UL << ADC_CFGR_AWD1CH_Pos) /*!< 0x40000000 */ + +#define ADC_CFGR_JQDIS_Pos (31U) +#define ADC_CFGR_JQDIS_Msk (0x1UL << ADC_CFGR_JQDIS_Pos) /*!< 0x80000000 */ +#define ADC_CFGR_JQDIS ADC_CFGR_JQDIS_Msk /*!< ADC group injected contexts queue disable */ + +/******************** Bit definition for ADC_CFGR2 register *****************/ +#define ADC_CFGR2_ROVSE_Pos (0U) +#define ADC_CFGR2_ROVSE_Msk (0x1UL << ADC_CFGR2_ROVSE_Pos) /*!< 0x00000001 */ +#define ADC_CFGR2_ROVSE ADC_CFGR2_ROVSE_Msk /*!< ADC oversampler enable on scope ADC group regular */ +#define ADC_CFGR2_JOVSE_Pos (1U) +#define ADC_CFGR2_JOVSE_Msk (0x1UL << ADC_CFGR2_JOVSE_Pos) /*!< 0x00000002 */ +#define ADC_CFGR2_JOVSE ADC_CFGR2_JOVSE_Msk /*!< ADC oversampler enable on scope ADC group injected */ + +#define ADC_CFGR2_OVSR_Pos (2U) +#define ADC_CFGR2_OVSR_Msk (0x7UL << ADC_CFGR2_OVSR_Pos) /*!< 0x0000001C */ +#define ADC_CFGR2_OVSR ADC_CFGR2_OVSR_Msk /*!< ADC oversampling ratio */ +#define ADC_CFGR2_OVSR_0 (0x1UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00000004 */ +#define ADC_CFGR2_OVSR_1 (0x2UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00000008 */ +#define ADC_CFGR2_OVSR_2 (0x4UL << ADC_CFGR2_OVSR_Pos) /*!< 0x00000010 */ + +#define ADC_CFGR2_OVSS_Pos (5U) +#define ADC_CFGR2_OVSS_Msk (0xFUL << ADC_CFGR2_OVSS_Pos) /*!< 0x000001E0 */ +#define ADC_CFGR2_OVSS ADC_CFGR2_OVSS_Msk /*!< ADC oversampling shift */ +#define ADC_CFGR2_OVSS_0 (0x1UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000020 */ +#define ADC_CFGR2_OVSS_1 (0x2UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000040 */ +#define ADC_CFGR2_OVSS_2 (0x4UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000080 */ +#define ADC_CFGR2_OVSS_3 (0x8UL << ADC_CFGR2_OVSS_Pos) /*!< 0x00000100 */ + +#define ADC_CFGR2_TROVS_Pos (9U) +#define ADC_CFGR2_TROVS_Msk (0x1UL << ADC_CFGR2_TROVS_Pos) /*!< 0x00000200 */ +#define ADC_CFGR2_TROVS ADC_CFGR2_TROVS_Msk /*!< ADC oversampling discontinuous mode (triggered mode) for ADC group regular */ +#define ADC_CFGR2_ROVSM_Pos (10U) +#define ADC_CFGR2_ROVSM_Msk (0x1UL << ADC_CFGR2_ROVSM_Pos) /*!< 0x00000400 */ +#define ADC_CFGR2_ROVSM ADC_CFGR2_ROVSM_Msk /*!< ADC oversampling mode managing interlaced conversions of ADC group regular and group injected */ + +#define ADC_CFGR2_GCOMP_Pos (16U) +#define ADC_CFGR2_GCOMP_Msk (0x1UL << ADC_CFGR2_GCOMP_Pos) /*!< 0x00010000 */ +#define ADC_CFGR2_GCOMP ADC_CFGR2_GCOMP_Msk /*!< ADC Gain Compensation mode */ + +#define ADC_CFGR2_SWTRIG_Pos (25U) +#define ADC_CFGR2_SWTRIG_Msk (0x1UL << ADC_CFGR2_SWTRIG_Pos) /*!< 0x02000000 */ +#define ADC_CFGR2_SWTRIG ADC_CFGR2_SWTRIG_Msk /*!< ADC Software Trigger Bit for Sample time control trigger mode */ +#define ADC_CFGR2_BULB_Pos (26U) +#define ADC_CFGR2_BULB_Msk (0x1UL << ADC_CFGR2_BULB_Pos) /*!< 0x04000000 */ +#define ADC_CFGR2_BULB ADC_CFGR2_BULB_Msk /*!< ADC Bulb sampling mode */ +#define ADC_CFGR2_SMPTRIG_Pos (27U) +#define ADC_CFGR2_SMPTRIG_Msk (0x1UL << ADC_CFGR2_SMPTRIG_Pos) /*!< 0x08000000 */ +#define ADC_CFGR2_SMPTRIG ADC_CFGR2_SMPTRIG_Msk /*!< ADC Sample Time Control Trigger mode */ + +/******************** Bit definition for ADC_SMPR1 register *****************/ +#define ADC_SMPR1_SMP0_Pos (0U) +#define ADC_SMPR1_SMP0_Msk (0x7UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000007 */ +#define ADC_SMPR1_SMP0 ADC_SMPR1_SMP0_Msk /*!< ADC channel 0 sampling time selection */ +#define ADC_SMPR1_SMP0_0 (0x1UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000001 */ +#define ADC_SMPR1_SMP0_1 (0x2UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000002 */ +#define ADC_SMPR1_SMP0_2 (0x4UL << ADC_SMPR1_SMP0_Pos) /*!< 0x00000004 */ + +#define ADC_SMPR1_SMP1_Pos (3U) +#define ADC_SMPR1_SMP1_Msk (0x7UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000038 */ +#define ADC_SMPR1_SMP1 ADC_SMPR1_SMP1_Msk /*!< ADC channel 1 sampling time selection */ +#define ADC_SMPR1_SMP1_0 (0x1UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000008 */ +#define ADC_SMPR1_SMP1_1 (0x2UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000010 */ +#define ADC_SMPR1_SMP1_2 (0x4UL << ADC_SMPR1_SMP1_Pos) /*!< 0x00000020 */ + +#define ADC_SMPR1_SMP2_Pos (6U) +#define ADC_SMPR1_SMP2_Msk (0x7UL << ADC_SMPR1_SMP2_Pos) /*!< 0x000001C0 */ +#define ADC_SMPR1_SMP2 ADC_SMPR1_SMP2_Msk /*!< ADC channel 2 sampling time selection */ +#define ADC_SMPR1_SMP2_0 (0x1UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000040 */ +#define ADC_SMPR1_SMP2_1 (0x2UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000080 */ +#define ADC_SMPR1_SMP2_2 (0x4UL << ADC_SMPR1_SMP2_Pos) /*!< 0x00000100 */ + +#define ADC_SMPR1_SMP3_Pos (9U) +#define ADC_SMPR1_SMP3_Msk (0x7UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000E00 */ +#define ADC_SMPR1_SMP3 ADC_SMPR1_SMP3_Msk /*!< ADC channel 3 sampling time selection */ +#define ADC_SMPR1_SMP3_0 (0x1UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000200 */ +#define ADC_SMPR1_SMP3_1 (0x2UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000400 */ +#define ADC_SMPR1_SMP3_2 (0x4UL << ADC_SMPR1_SMP3_Pos) /*!< 0x00000800 */ + +#define ADC_SMPR1_SMP4_Pos (12U) +#define ADC_SMPR1_SMP4_Msk (0x7UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00007000 */ +#define ADC_SMPR1_SMP4 ADC_SMPR1_SMP4_Msk /*!< ADC channel 4 sampling time selection */ +#define ADC_SMPR1_SMP4_0 (0x1UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00001000 */ +#define ADC_SMPR1_SMP4_1 (0x2UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00002000 */ +#define ADC_SMPR1_SMP4_2 (0x4UL << ADC_SMPR1_SMP4_Pos) /*!< 0x00004000 */ + +#define ADC_SMPR1_SMP5_Pos (15U) +#define ADC_SMPR1_SMP5_Msk (0x7UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00038000 */ +#define ADC_SMPR1_SMP5 ADC_SMPR1_SMP5_Msk /*!< ADC channel 5 sampling time selection */ +#define ADC_SMPR1_SMP5_0 (0x1UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00008000 */ +#define ADC_SMPR1_SMP5_1 (0x2UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00010000 */ +#define ADC_SMPR1_SMP5_2 (0x4UL << ADC_SMPR1_SMP5_Pos) /*!< 0x00020000 */ + +#define ADC_SMPR1_SMP6_Pos (18U) +#define ADC_SMPR1_SMP6_Msk (0x7UL << ADC_SMPR1_SMP6_Pos) /*!< 0x001C0000 */ +#define ADC_SMPR1_SMP6 ADC_SMPR1_SMP6_Msk /*!< ADC channel 6 sampling time selection */ +#define ADC_SMPR1_SMP6_0 (0x1UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00040000 */ +#define ADC_SMPR1_SMP6_1 (0x2UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00080000 */ +#define ADC_SMPR1_SMP6_2 (0x4UL << ADC_SMPR1_SMP6_Pos) /*!< 0x00100000 */ + +#define ADC_SMPR1_SMP7_Pos (21U) +#define ADC_SMPR1_SMP7_Msk (0x7UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00E00000 */ +#define ADC_SMPR1_SMP7 ADC_SMPR1_SMP7_Msk /*!< ADC channel 7 sampling time selection */ +#define ADC_SMPR1_SMP7_0 (0x1UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00200000 */ +#define ADC_SMPR1_SMP7_1 (0x2UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00400000 */ +#define ADC_SMPR1_SMP7_2 (0x4UL << ADC_SMPR1_SMP7_Pos) /*!< 0x00800000 */ + +#define ADC_SMPR1_SMP8_Pos (24U) +#define ADC_SMPR1_SMP8_Msk (0x7UL << ADC_SMPR1_SMP8_Pos) /*!< 0x07000000 */ +#define ADC_SMPR1_SMP8 ADC_SMPR1_SMP8_Msk /*!< ADC channel 8 sampling time selection */ +#define ADC_SMPR1_SMP8_0 (0x1UL << ADC_SMPR1_SMP8_Pos) /*!< 0x01000000 */ +#define ADC_SMPR1_SMP8_1 (0x2UL << ADC_SMPR1_SMP8_Pos) /*!< 0x02000000 */ +#define ADC_SMPR1_SMP8_2 (0x4UL << ADC_SMPR1_SMP8_Pos) /*!< 0x04000000 */ + +#define ADC_SMPR1_SMP9_Pos (27U) +#define ADC_SMPR1_SMP9_Msk (0x7UL << ADC_SMPR1_SMP9_Pos) /*!< 0x38000000 */ +#define ADC_SMPR1_SMP9 ADC_SMPR1_SMP9_Msk /*!< ADC channel 9 sampling time selection */ +#define ADC_SMPR1_SMP9_0 (0x1UL << ADC_SMPR1_SMP9_Pos) /*!< 0x08000000 */ +#define ADC_SMPR1_SMP9_1 (0x2UL << ADC_SMPR1_SMP9_Pos) /*!< 0x10000000 */ +#define ADC_SMPR1_SMP9_2 (0x4UL << ADC_SMPR1_SMP9_Pos) /*!< 0x20000000 */ + +#define ADC_SMPR1_SMPPLUS_Pos (31U) +#define ADC_SMPR1_SMPPLUS_Msk (0x1UL << ADC_SMPR1_SMPPLUS_Pos) /*!< 0x80000000 */ +#define ADC_SMPR1_SMPPLUS ADC_SMPR1_SMPPLUS_Msk /*!< ADC channels sampling time additional setting */ + +/******************** Bit definition for ADC_SMPR2 register *****************/ +#define ADC_SMPR2_SMP10_Pos (0U) +#define ADC_SMPR2_SMP10_Msk (0x7UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000007 */ +#define ADC_SMPR2_SMP10 ADC_SMPR2_SMP10_Msk /*!< ADC channel 10 sampling time selection */ +#define ADC_SMPR2_SMP10_0 (0x1UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000001 */ +#define ADC_SMPR2_SMP10_1 (0x2UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000002 */ +#define ADC_SMPR2_SMP10_2 (0x4UL << ADC_SMPR2_SMP10_Pos) /*!< 0x00000004 */ + +#define ADC_SMPR2_SMP11_Pos (3U) +#define ADC_SMPR2_SMP11_Msk (0x7UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000038 */ +#define ADC_SMPR2_SMP11 ADC_SMPR2_SMP11_Msk /*!< ADC channel 11 sampling time selection */ +#define ADC_SMPR2_SMP11_0 (0x1UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000008 */ +#define ADC_SMPR2_SMP11_1 (0x2UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000010 */ +#define ADC_SMPR2_SMP11_2 (0x4UL << ADC_SMPR2_SMP11_Pos) /*!< 0x00000020 */ + +#define ADC_SMPR2_SMP12_Pos (6U) +#define ADC_SMPR2_SMP12_Msk (0x7UL << ADC_SMPR2_SMP12_Pos) /*!< 0x000001C0 */ +#define ADC_SMPR2_SMP12 ADC_SMPR2_SMP12_Msk /*!< ADC channel 12 sampling time selection */ +#define ADC_SMPR2_SMP12_0 (0x1UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000040 */ +#define ADC_SMPR2_SMP12_1 (0x2UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000080 */ +#define ADC_SMPR2_SMP12_2 (0x4UL << ADC_SMPR2_SMP12_Pos) /*!< 0x00000100 */ + +#define ADC_SMPR2_SMP13_Pos (9U) +#define ADC_SMPR2_SMP13_Msk (0x7UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000E00 */ +#define ADC_SMPR2_SMP13 ADC_SMPR2_SMP13_Msk /*!< ADC channel 13 sampling time selection */ +#define ADC_SMPR2_SMP13_0 (0x1UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000200 */ +#define ADC_SMPR2_SMP13_1 (0x2UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000400 */ +#define ADC_SMPR2_SMP13_2 (0x4UL << ADC_SMPR2_SMP13_Pos) /*!< 0x00000800 */ + +#define ADC_SMPR2_SMP14_Pos (12U) +#define ADC_SMPR2_SMP14_Msk (0x7UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00007000 */ +#define ADC_SMPR2_SMP14 ADC_SMPR2_SMP14_Msk /*!< ADC channel 14 sampling time selection */ +#define ADC_SMPR2_SMP14_0 (0x1UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00001000 */ +#define ADC_SMPR2_SMP14_1 (0x2UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00002000 */ +#define ADC_SMPR2_SMP14_2 (0x4UL << ADC_SMPR2_SMP14_Pos) /*!< 0x00004000 */ + +#define ADC_SMPR2_SMP15_Pos (15U) +#define ADC_SMPR2_SMP15_Msk (0x7UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00038000 */ +#define ADC_SMPR2_SMP15 ADC_SMPR2_SMP15_Msk /*!< ADC channel 15 sampling time selection */ +#define ADC_SMPR2_SMP15_0 (0x1UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00008000 */ +#define ADC_SMPR2_SMP15_1 (0x2UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00010000 */ +#define ADC_SMPR2_SMP15_2 (0x4UL << ADC_SMPR2_SMP15_Pos) /*!< 0x00020000 */ + +#define ADC_SMPR2_SMP16_Pos (18U) +#define ADC_SMPR2_SMP16_Msk (0x7UL << ADC_SMPR2_SMP16_Pos) /*!< 0x001C0000 */ +#define ADC_SMPR2_SMP16 ADC_SMPR2_SMP16_Msk /*!< ADC channel 16 sampling time selection */ +#define ADC_SMPR2_SMP16_0 (0x1UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00040000 */ +#define ADC_SMPR2_SMP16_1 (0x2UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00080000 */ +#define ADC_SMPR2_SMP16_2 (0x4UL << ADC_SMPR2_SMP16_Pos) /*!< 0x00100000 */ + +#define ADC_SMPR2_SMP17_Pos (21U) +#define ADC_SMPR2_SMP17_Msk (0x7UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00E00000 */ +#define ADC_SMPR2_SMP17 ADC_SMPR2_SMP17_Msk /*!< ADC channel 17 sampling time selection */ +#define ADC_SMPR2_SMP17_0 (0x1UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00200000 */ +#define ADC_SMPR2_SMP17_1 (0x2UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00400000 */ +#define ADC_SMPR2_SMP17_2 (0x4UL << ADC_SMPR2_SMP17_Pos) /*!< 0x00800000 */ + +#define ADC_SMPR2_SMP18_Pos (24U) +#define ADC_SMPR2_SMP18_Msk (0x7UL << ADC_SMPR2_SMP18_Pos) /*!< 0x07000000 */ +#define ADC_SMPR2_SMP18 ADC_SMPR2_SMP18_Msk /*!< ADC channel 18 sampling time selection */ +#define ADC_SMPR2_SMP18_0 (0x1UL << ADC_SMPR2_SMP18_Pos) /*!< 0x01000000 */ +#define ADC_SMPR2_SMP18_1 (0x2UL << ADC_SMPR2_SMP18_Pos) /*!< 0x02000000 */ +#define ADC_SMPR2_SMP18_2 (0x4UL << ADC_SMPR2_SMP18_Pos) /*!< 0x04000000 */ + +/******************** Bit definition for ADC_TR1 register *******************/ +#define ADC_TR1_LT1_Pos (0U) +#define ADC_TR1_LT1_Msk (0xFFFUL << ADC_TR1_LT1_Pos) /*!< 0x00000FFF */ +#define ADC_TR1_LT1 ADC_TR1_LT1_Msk /*!< ADC analog watchdog 1 threshold low */ + +#define ADC_TR1_AWDFILT_Pos (12U) +#define ADC_TR1_AWDFILT_Msk (0x7UL << ADC_TR1_AWDFILT_Pos) /*!< 0x00007000 */ +#define ADC_TR1_AWDFILT ADC_TR1_AWDFILT_Msk /*!< ADC analog watchdog filtering parameter */ +#define ADC_TR1_AWDFILT_0 (0x1UL << ADC_TR1_AWDFILT_Pos) /*!< 0x00001000 */ +#define ADC_TR1_AWDFILT_1 (0x2UL << ADC_TR1_AWDFILT_Pos) /*!< 0x00002000 */ +#define ADC_TR1_AWDFILT_2 (0x4UL << ADC_TR1_AWDFILT_Pos) /*!< 0x00004000 */ + +#define ADC_TR1_HT1_Pos (16U) +#define ADC_TR1_HT1_Msk (0xFFFUL << ADC_TR1_HT1_Pos) /*!< 0x0FFF0000 */ +#define ADC_TR1_HT1 ADC_TR1_HT1_Msk /*!< ADC analog watchdog 1 threshold high */ + +/******************** Bit definition for ADC_TR2 register *******************/ +#define ADC_TR2_LT2_Pos (0U) +#define ADC_TR2_LT2_Msk (0xFFUL << ADC_TR2_LT2_Pos) /*!< 0x000000FF */ +#define ADC_TR2_LT2 ADC_TR2_LT2_Msk /*!< ADC analog watchdog 2 threshold low */ + +#define ADC_TR2_HT2_Pos (16U) +#define ADC_TR2_HT2_Msk (0xFFUL << ADC_TR2_HT2_Pos) /*!< 0x00FF0000 */ +#define ADC_TR2_HT2 ADC_TR2_HT2_Msk /*!< ADC analog watchdog 2 threshold high */ + +/******************** Bit definition for ADC_TR3 register *******************/ +#define ADC_TR3_LT3_Pos (0U) +#define ADC_TR3_LT3_Msk (0xFFUL << ADC_TR3_LT3_Pos) /*!< 0x000000FF */ +#define ADC_TR3_LT3 ADC_TR3_LT3_Msk /*!< ADC analog watchdog 3 threshold low */ + +#define ADC_TR3_HT3_Pos (16U) +#define ADC_TR3_HT3_Msk (0xFFUL << ADC_TR3_HT3_Pos) /*!< 0x00FF0000 */ +#define ADC_TR3_HT3 ADC_TR3_HT3_Msk /*!< ADC analog watchdog 3 threshold high */ + +/******************** Bit definition for ADC_SQR1 register ******************/ +#define ADC_SQR1_L_Pos (0U) +#define ADC_SQR1_L_Msk (0xFUL << ADC_SQR1_L_Pos) /*!< 0x0000000F */ +#define ADC_SQR1_L ADC_SQR1_L_Msk /*!< ADC group regular sequencer scan length */ +#define ADC_SQR1_L_0 (0x1UL << ADC_SQR1_L_Pos) /*!< 0x00000001 */ +#define ADC_SQR1_L_1 (0x2UL << ADC_SQR1_L_Pos) /*!< 0x00000002 */ +#define ADC_SQR1_L_2 (0x4UL << ADC_SQR1_L_Pos) /*!< 0x00000004 */ +#define ADC_SQR1_L_3 (0x8UL << ADC_SQR1_L_Pos) /*!< 0x00000008 */ + +#define ADC_SQR1_SQ1_Pos (6U) +#define ADC_SQR1_SQ1_Msk (0x1FUL << ADC_SQR1_SQ1_Pos) /*!< 0x000007C0 */ +#define ADC_SQR1_SQ1 ADC_SQR1_SQ1_Msk /*!< ADC group regular sequencer rank 1 */ +#define ADC_SQR1_SQ1_0 (0x01UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000040 */ +#define ADC_SQR1_SQ1_1 (0x02UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000080 */ +#define ADC_SQR1_SQ1_2 (0x04UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000100 */ +#define ADC_SQR1_SQ1_3 (0x08UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000200 */ +#define ADC_SQR1_SQ1_4 (0x10UL << ADC_SQR1_SQ1_Pos) /*!< 0x00000400 */ + +#define ADC_SQR1_SQ2_Pos (12U) +#define ADC_SQR1_SQ2_Msk (0x1FUL << ADC_SQR1_SQ2_Pos) /*!< 0x0001F000 */ +#define ADC_SQR1_SQ2 ADC_SQR1_SQ2_Msk /*!< ADC group regular sequencer rank 2 */ +#define ADC_SQR1_SQ2_0 (0x01UL << ADC_SQR1_SQ2_Pos) /*!< 0x00001000 */ +#define ADC_SQR1_SQ2_1 (0x02UL << ADC_SQR1_SQ2_Pos) /*!< 0x00002000 */ +#define ADC_SQR1_SQ2_2 (0x04UL << ADC_SQR1_SQ2_Pos) /*!< 0x00004000 */ +#define ADC_SQR1_SQ2_3 (0x08UL << ADC_SQR1_SQ2_Pos) /*!< 0x00008000 */ +#define ADC_SQR1_SQ2_4 (0x10UL << ADC_SQR1_SQ2_Pos) /*!< 0x00010000 */ + +#define ADC_SQR1_SQ3_Pos (18U) +#define ADC_SQR1_SQ3_Msk (0x1FUL << ADC_SQR1_SQ3_Pos) /*!< 0x007C0000 */ +#define ADC_SQR1_SQ3 ADC_SQR1_SQ3_Msk /*!< ADC group regular sequencer rank 3 */ +#define ADC_SQR1_SQ3_0 (0x01UL << ADC_SQR1_SQ3_Pos) /*!< 0x00040000 */ +#define ADC_SQR1_SQ3_1 (0x02UL << ADC_SQR1_SQ3_Pos) /*!< 0x00080000 */ +#define ADC_SQR1_SQ3_2 (0x04UL << ADC_SQR1_SQ3_Pos) /*!< 0x00100000 */ +#define ADC_SQR1_SQ3_3 (0x08UL << ADC_SQR1_SQ3_Pos) /*!< 0x00200000 */ +#define ADC_SQR1_SQ3_4 (0x10UL<< ADC_SQR1_SQ3_Pos) /*!< 0x00400000 */ + +#define ADC_SQR1_SQ4_Pos (24U) +#define ADC_SQR1_SQ4_Msk (0x1FUL << ADC_SQR1_SQ4_Pos) /*!< 0x1F000000 */ +#define ADC_SQR1_SQ4 ADC_SQR1_SQ4_Msk /*!< ADC group regular sequencer rank 4 */ +#define ADC_SQR1_SQ4_0 (0x01UL << ADC_SQR1_SQ4_Pos) /*!< 0x01000000 */ +#define ADC_SQR1_SQ4_1 (0x02UL << ADC_SQR1_SQ4_Pos) /*!< 0x02000000 */ +#define ADC_SQR1_SQ4_2 (0x04UL << ADC_SQR1_SQ4_Pos) /*!< 0x04000000 */ +#define ADC_SQR1_SQ4_3 (0x08UL << ADC_SQR1_SQ4_Pos) /*!< 0x08000000 */ +#define ADC_SQR1_SQ4_4 (0x10UL << ADC_SQR1_SQ4_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR2 register ******************/ +#define ADC_SQR2_SQ5_Pos (0U) +#define ADC_SQR2_SQ5_Msk (0x1FUL << ADC_SQR2_SQ5_Pos) /*!< 0x0000001F */ +#define ADC_SQR2_SQ5 ADC_SQR2_SQ5_Msk /*!< ADC group regular sequencer rank 5 */ +#define ADC_SQR2_SQ5_0 (0x01UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000001 */ +#define ADC_SQR2_SQ5_1 (0x02UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000002 */ +#define ADC_SQR2_SQ5_2 (0x04UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000004 */ +#define ADC_SQR2_SQ5_3 (0x08UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000008 */ +#define ADC_SQR2_SQ5_4 (0x10UL << ADC_SQR2_SQ5_Pos) /*!< 0x00000010 */ + +#define ADC_SQR2_SQ6_Pos (6U) +#define ADC_SQR2_SQ6_Msk (0x1FUL << ADC_SQR2_SQ6_Pos) /*!< 0x000007C0 */ +#define ADC_SQR2_SQ6 ADC_SQR2_SQ6_Msk /*!< ADC group regular sequencer rank 6 */ +#define ADC_SQR2_SQ6_0 (0x01UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000040 */ +#define ADC_SQR2_SQ6_1 (0x02UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000080 */ +#define ADC_SQR2_SQ6_2 (0x04UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000100 */ +#define ADC_SQR2_SQ6_3 (0x08UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000200 */ +#define ADC_SQR2_SQ6_4 (0x10UL << ADC_SQR2_SQ6_Pos) /*!< 0x00000400 */ + +#define ADC_SQR2_SQ7_Pos (12U) +#define ADC_SQR2_SQ7_Msk (0x1FUL << ADC_SQR2_SQ7_Pos) /*!< 0x0001F000 */ +#define ADC_SQR2_SQ7 ADC_SQR2_SQ7_Msk /*!< ADC group regular sequencer rank 7 */ +#define ADC_SQR2_SQ7_0 (0x01UL << ADC_SQR2_SQ7_Pos) /*!< 0x00001000 */ +#define ADC_SQR2_SQ7_1 (0x02UL << ADC_SQR2_SQ7_Pos) /*!< 0x00002000 */ +#define ADC_SQR2_SQ7_2 (0x04UL << ADC_SQR2_SQ7_Pos) /*!< 0x00004000 */ +#define ADC_SQR2_SQ7_3 (0x08UL << ADC_SQR2_SQ7_Pos) /*!< 0x00008000 */ +#define ADC_SQR2_SQ7_4 (0x10UL << ADC_SQR2_SQ7_Pos) /*!< 0x00010000 */ + +#define ADC_SQR2_SQ8_Pos (18U) +#define ADC_SQR2_SQ8_Msk (0x1FUL << ADC_SQR2_SQ8_Pos) /*!< 0x007C0000 */ +#define ADC_SQR2_SQ8 ADC_SQR2_SQ8_Msk /*!< ADC group regular sequencer rank 8 */ +#define ADC_SQR2_SQ8_0 (0x01UL << ADC_SQR2_SQ8_Pos) /*!< 0x00040000 */ +#define ADC_SQR2_SQ8_1 (0x02UL << ADC_SQR2_SQ8_Pos) /*!< 0x00080000 */ +#define ADC_SQR2_SQ8_2 (0x04UL << ADC_SQR2_SQ8_Pos) /*!< 0x00100000 */ +#define ADC_SQR2_SQ8_3 (0x08UL << ADC_SQR2_SQ8_Pos) /*!< 0x00200000 */ +#define ADC_SQR2_SQ8_4 (0x10UL << ADC_SQR2_SQ8_Pos) /*!< 0x00400000 */ + +#define ADC_SQR2_SQ9_Pos (24U) +#define ADC_SQR2_SQ9_Msk (0x1FUL << ADC_SQR2_SQ9_Pos) /*!< 0x1F000000 */ +#define ADC_SQR2_SQ9 ADC_SQR2_SQ9_Msk /*!< ADC group regular sequencer rank 9 */ +#define ADC_SQR2_SQ9_0 (0x01UL << ADC_SQR2_SQ9_Pos) /*!< 0x01000000 */ +#define ADC_SQR2_SQ9_1 (0x02UL << ADC_SQR2_SQ9_Pos) /*!< 0x02000000 */ +#define ADC_SQR2_SQ9_2 (0x04UL << ADC_SQR2_SQ9_Pos) /*!< 0x04000000 */ +#define ADC_SQR2_SQ9_3 (0x08UL << ADC_SQR2_SQ9_Pos) /*!< 0x08000000 */ +#define ADC_SQR2_SQ9_4 (0x10UL << ADC_SQR2_SQ9_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR3 register ******************/ +#define ADC_SQR3_SQ10_Pos (0U) +#define ADC_SQR3_SQ10_Msk (0x1FUL << ADC_SQR3_SQ10_Pos) /*!< 0x0000001F */ +#define ADC_SQR3_SQ10 ADC_SQR3_SQ10_Msk /*!< ADC group regular sequencer rank 10 */ +#define ADC_SQR3_SQ10_0 (0x01UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000001 */ +#define ADC_SQR3_SQ10_1 (0x02UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000002 */ +#define ADC_SQR3_SQ10_2 (0x04UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000004 */ +#define ADC_SQR3_SQ10_3 (0x08UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000008 */ +#define ADC_SQR3_SQ10_4 (0x10UL << ADC_SQR3_SQ10_Pos) /*!< 0x00000010 */ + +#define ADC_SQR3_SQ11_Pos (6U) +#define ADC_SQR3_SQ11_Msk (0x1FUL << ADC_SQR3_SQ11_Pos) /*!< 0x000007C0 */ +#define ADC_SQR3_SQ11 ADC_SQR3_SQ11_Msk /*!< ADC group regular sequencer rank 11 */ +#define ADC_SQR3_SQ11_0 (0x01UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000040 */ +#define ADC_SQR3_SQ11_1 (0x02UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000080 */ +#define ADC_SQR3_SQ11_2 (0x04UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000100 */ +#define ADC_SQR3_SQ11_3 (0x08UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000200 */ +#define ADC_SQR3_SQ11_4 (0x10UL << ADC_SQR3_SQ11_Pos) /*!< 0x00000400 */ + +#define ADC_SQR3_SQ12_Pos (12U) +#define ADC_SQR3_SQ12_Msk (0x1FUL << ADC_SQR3_SQ12_Pos) /*!< 0x0001F000 */ +#define ADC_SQR3_SQ12 ADC_SQR3_SQ12_Msk /*!< ADC group regular sequencer rank 12 */ +#define ADC_SQR3_SQ12_0 (0x01UL << ADC_SQR3_SQ12_Pos) /*!< 0x00001000 */ +#define ADC_SQR3_SQ12_1 (0x02UL << ADC_SQR3_SQ12_Pos) /*!< 0x00002000 */ +#define ADC_SQR3_SQ12_2 (0x04UL << ADC_SQR3_SQ12_Pos) /*!< 0x00004000 */ +#define ADC_SQR3_SQ12_3 (0x08UL << ADC_SQR3_SQ12_Pos) /*!< 0x00008000 */ +#define ADC_SQR3_SQ12_4 (0x10UL << ADC_SQR3_SQ12_Pos) /*!< 0x00010000 */ + +#define ADC_SQR3_SQ13_Pos (18U) +#define ADC_SQR3_SQ13_Msk (0x1FUL << ADC_SQR3_SQ13_Pos) /*!< 0x007C0000 */ +#define ADC_SQR3_SQ13 ADC_SQR3_SQ13_Msk /*!< ADC group regular sequencer rank 13 */ +#define ADC_SQR3_SQ13_0 (0x01UL << ADC_SQR3_SQ13_Pos) /*!< 0x00040000 */ +#define ADC_SQR3_SQ13_1 (0x02UL << ADC_SQR3_SQ13_Pos) /*!< 0x00080000 */ +#define ADC_SQR3_SQ13_2 (0x04UL << ADC_SQR3_SQ13_Pos) /*!< 0x00100000 */ +#define ADC_SQR3_SQ13_3 (0x08UL << ADC_SQR3_SQ13_Pos) /*!< 0x00200000 */ +#define ADC_SQR3_SQ13_4 (0x10UL << ADC_SQR3_SQ13_Pos) /*!< 0x00400000 */ + +#define ADC_SQR3_SQ14_Pos (24U) +#define ADC_SQR3_SQ14_Msk (0x1FUL << ADC_SQR3_SQ14_Pos) /*!< 0x1F000000 */ +#define ADC_SQR3_SQ14 ADC_SQR3_SQ14_Msk /*!< ADC group regular sequencer rank 14 */ +#define ADC_SQR3_SQ14_0 (0x01UL << ADC_SQR3_SQ14_Pos) /*!< 0x01000000 */ +#define ADC_SQR3_SQ14_1 (0x02UL << ADC_SQR3_SQ14_Pos) /*!< 0x02000000 */ +#define ADC_SQR3_SQ14_2 (0x04UL << ADC_SQR3_SQ14_Pos) /*!< 0x04000000 */ +#define ADC_SQR3_SQ14_3 (0x08UL << ADC_SQR3_SQ14_Pos) /*!< 0x08000000 */ +#define ADC_SQR3_SQ14_4 (0x10UL << ADC_SQR3_SQ14_Pos) /*!< 0x10000000 */ + +/******************** Bit definition for ADC_SQR4 register ******************/ +#define ADC_SQR4_SQ15_Pos (0U) +#define ADC_SQR4_SQ15_Msk (0x1FUL << ADC_SQR4_SQ15_Pos) /*!< 0x0000001F */ +#define ADC_SQR4_SQ15 ADC_SQR4_SQ15_Msk /*!< ADC group regular sequencer rank 15 */ +#define ADC_SQR4_SQ15_0 (0x01UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000001 */ +#define ADC_SQR4_SQ15_1 (0x02UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000002 */ +#define ADC_SQR4_SQ15_2 (0x04UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000004 */ +#define ADC_SQR4_SQ15_3 (0x08UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000008 */ +#define ADC_SQR4_SQ15_4 (0x10UL << ADC_SQR4_SQ15_Pos) /*!< 0x00000010 */ + +#define ADC_SQR4_SQ16_Pos (6U) +#define ADC_SQR4_SQ16_Msk (0x1FUL << ADC_SQR4_SQ16_Pos) /*!< 0x000007C0 */ +#define ADC_SQR4_SQ16 ADC_SQR4_SQ16_Msk /*!< ADC group regular sequencer rank 16 */ +#define ADC_SQR4_SQ16_0 (0x01UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000040 */ +#define ADC_SQR4_SQ16_1 (0x02UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000080 */ +#define ADC_SQR4_SQ16_2 (0x04UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000100 */ +#define ADC_SQR4_SQ16_3 (0x08UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000200 */ +#define ADC_SQR4_SQ16_4 (0x10UL << ADC_SQR4_SQ16_Pos) /*!< 0x00000400 */ + +/******************** Bit definition for ADC_DR register ********************/ +#define ADC_DR_RDATA_Pos (0U) +#define ADC_DR_RDATA_Msk (0xFFFFUL << ADC_DR_RDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_DR_RDATA ADC_DR_RDATA_Msk /*!< ADC group regular conversion data */ + +/******************** Bit definition for ADC_JSQR register ******************/ +#define ADC_JSQR_JL_Pos (0U) +#define ADC_JSQR_JL_Msk (0x3UL << ADC_JSQR_JL_Pos) /*!< 0x00000003 */ +#define ADC_JSQR_JL ADC_JSQR_JL_Msk /*!< ADC group injected sequencer scan length */ +#define ADC_JSQR_JL_0 (0x1UL << ADC_JSQR_JL_Pos) /*!< 0x00000001 */ +#define ADC_JSQR_JL_1 (0x2UL << ADC_JSQR_JL_Pos) /*!< 0x00000002 */ + +#define ADC_JSQR_JEXTSEL_Pos (2U) +#define ADC_JSQR_JEXTSEL_Msk (0x1FUL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x0000007C */ +#define ADC_JSQR_JEXTSEL ADC_JSQR_JEXTSEL_Msk /*!< ADC group injected external trigger source */ +#define ADC_JSQR_JEXTSEL_0 (0x1UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000004 */ +#define ADC_JSQR_JEXTSEL_1 (0x2UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000008 */ +#define ADC_JSQR_JEXTSEL_2 (0x4UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000010 */ +#define ADC_JSQR_JEXTSEL_3 (0x8UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000020 */ +#define ADC_JSQR_JEXTSEL_4 (0x10UL << ADC_JSQR_JEXTSEL_Pos) /*!< 0x00000040 */ + +#define ADC_JSQR_JEXTEN_Pos (7U) +#define ADC_JSQR_JEXTEN_Msk (0x3UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000180 */ +#define ADC_JSQR_JEXTEN ADC_JSQR_JEXTEN_Msk /*!< ADC group injected external trigger polarity */ +#define ADC_JSQR_JEXTEN_0 (0x1UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000080 */ +#define ADC_JSQR_JEXTEN_1 (0x2UL << ADC_JSQR_JEXTEN_Pos) /*!< 0x00000100 */ + +#define ADC_JSQR_JSQ1_Pos (9U) +#define ADC_JSQR_JSQ1_Msk (0x1FUL << ADC_JSQR_JSQ1_Pos) /*!< 0x00003E00 */ +#define ADC_JSQR_JSQ1 ADC_JSQR_JSQ1_Msk /*!< ADC group injected sequencer rank 1 */ +#define ADC_JSQR_JSQ1_0 (0x01UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000200 */ +#define ADC_JSQR_JSQ1_1 (0x02UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000400 */ +#define ADC_JSQR_JSQ1_2 (0x04UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00000800 */ +#define ADC_JSQR_JSQ1_3 (0x08UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00001000 */ +#define ADC_JSQR_JSQ1_4 (0x10UL << ADC_JSQR_JSQ1_Pos) /*!< 0x00002000 */ + +#define ADC_JSQR_JSQ2_Pos (15U) +#define ADC_JSQR_JSQ2_Msk (0x1FUL << ADC_JSQR_JSQ2_Pos) /*!< 0x0007C000 */ +#define ADC_JSQR_JSQ2 ADC_JSQR_JSQ2_Msk /*!< ADC group injected sequencer rank 2 */ +#define ADC_JSQR_JSQ2_0 (0x01UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00004000 */ +#define ADC_JSQR_JSQ2_1 (0x02UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00008000 */ +#define ADC_JSQR_JSQ2_2 (0x04UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00010000 */ +#define ADC_JSQR_JSQ2_3 (0x08UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00020000 */ +#define ADC_JSQR_JSQ2_4 (0x10UL << ADC_JSQR_JSQ2_Pos) /*!< 0x00040000 */ + +#define ADC_JSQR_JSQ3_Pos (21U) +#define ADC_JSQR_JSQ3_Msk (0x1FUL << ADC_JSQR_JSQ3_Pos) /*!< 0x03E00000 */ +#define ADC_JSQR_JSQ3 ADC_JSQR_JSQ3_Msk /*!< ADC group injected sequencer rank 3 */ +#define ADC_JSQR_JSQ3_0 (0x01UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00200000 */ +#define ADC_JSQR_JSQ3_1 (0x02UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00400000 */ +#define ADC_JSQR_JSQ3_2 (0x04UL << ADC_JSQR_JSQ3_Pos) /*!< 0x00800000 */ +#define ADC_JSQR_JSQ3_3 (0x08UL << ADC_JSQR_JSQ3_Pos) /*!< 0x01000000 */ +#define ADC_JSQR_JSQ3_4 (0x10UL << ADC_JSQR_JSQ3_Pos) /*!< 0x02000000 */ + +#define ADC_JSQR_JSQ4_Pos (27U) +#define ADC_JSQR_JSQ4_Msk (0x1FUL << ADC_JSQR_JSQ4_Pos) /*!< 0xF8000000 */ +#define ADC_JSQR_JSQ4 ADC_JSQR_JSQ4_Msk /*!< ADC group injected sequencer rank 4 */ +#define ADC_JSQR_JSQ4_0 (0x01UL << ADC_JSQR_JSQ4_Pos) /*!< 0x08000000 */ +#define ADC_JSQR_JSQ4_1 (0x02UL << ADC_JSQR_JSQ4_Pos) /*!< 0x10000000 */ +#define ADC_JSQR_JSQ4_2 (0x04UL << ADC_JSQR_JSQ4_Pos) /*!< 0x20000000 */ +#define ADC_JSQR_JSQ4_3 (0x08UL << ADC_JSQR_JSQ4_Pos) /*!< 0x40000000 */ +#define ADC_JSQR_JSQ4_4 (0x10UL << ADC_JSQR_JSQ4_Pos) /*!< 0x80000000 */ + +/******************** Bit definition for ADC_OFR1 register ******************/ +#define ADC_OFR1_OFFSET1_Pos (0U) +#define ADC_OFR1_OFFSET1_Msk (0xFFFUL << ADC_OFR1_OFFSET1_Pos) /*!< 0x00000FFF */ +#define ADC_OFR1_OFFSET1 ADC_OFR1_OFFSET1_Msk /*!< ADC offset number 1 offset level */ + +#define ADC_OFR1_OFFSETPOS_Pos (24U) +#define ADC_OFR1_OFFSETPOS_Msk (0x1UL << ADC_OFR1_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC_OFR1_OFFSETPOS ADC_OFR1_OFFSETPOS_Msk /*!< ADC offset number 1 positive */ +#define ADC_OFR1_SATEN_Pos (25U) +#define ADC_OFR1_SATEN_Msk (0x1UL << ADC_OFR1_SATEN_Pos) /*!< 0x02000000 */ +#define ADC_OFR1_SATEN ADC_OFR1_SATEN_Msk /*!< ADC offset number 1 saturation enable */ + +#define ADC_OFR1_OFFSET1_CH_Pos (26U) +#define ADC_OFR1_OFFSET1_CH_Msk (0x1FUL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR1_OFFSET1_CH ADC_OFR1_OFFSET1_CH_Msk /*!< ADC offset number 1 channel selection */ +#define ADC_OFR1_OFFSET1_CH_0 (0x01UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR1_OFFSET1_CH_1 (0x02UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR1_OFFSET1_CH_2 (0x04UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR1_OFFSET1_CH_3 (0x08UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR1_OFFSET1_CH_4 (0x10UL << ADC_OFR1_OFFSET1_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR1_OFFSET1_EN_Pos (31U) +#define ADC_OFR1_OFFSET1_EN_Msk (0x1UL << ADC_OFR1_OFFSET1_EN_Pos) /*!< 0x80000000 */ +#define ADC_OFR1_OFFSET1_EN ADC_OFR1_OFFSET1_EN_Msk /*!< ADC offset number 1 enable */ + +/******************** Bit definition for ADC_OFR2 register ******************/ +#define ADC_OFR2_OFFSET2_Pos (0U) +#define ADC_OFR2_OFFSET2_Msk (0xFFFUL << ADC_OFR2_OFFSET2_Pos) /*!< 0x00000FFF */ +#define ADC_OFR2_OFFSET2 ADC_OFR2_OFFSET2_Msk /*!< ADC offset number 2 offset level */ + +#define ADC_OFR2_OFFSETPOS_Pos (24U) +#define ADC_OFR2_OFFSETPOS_Msk (0x1UL << ADC_OFR2_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC_OFR2_OFFSETPOS ADC_OFR2_OFFSETPOS_Msk /*!< ADC offset number 2 positive */ +#define ADC_OFR2_SATEN_Pos (25U) +#define ADC_OFR2_SATEN_Msk (0x1UL << ADC_OFR2_SATEN_Pos) /*!< 0x02000000 */ +#define ADC_OFR2_SATEN ADC_OFR2_SATEN_Msk /*!< ADC offset number 2 saturation enable */ + +#define ADC_OFR2_OFFSET2_CH_Pos (26U) +#define ADC_OFR2_OFFSET2_CH_Msk (0x1FUL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR2_OFFSET2_CH ADC_OFR2_OFFSET2_CH_Msk /*!< ADC offset number 2 channel selection */ +#define ADC_OFR2_OFFSET2_CH_0 (0x01UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR2_OFFSET2_CH_1 (0x02UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR2_OFFSET2_CH_2 (0x04UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR2_OFFSET2_CH_3 (0x08UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR2_OFFSET2_CH_4 (0x10UL << ADC_OFR2_OFFSET2_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR2_OFFSET2_EN_Pos (31U) +#define ADC_OFR2_OFFSET2_EN_Msk (0x1UL << ADC_OFR2_OFFSET2_EN_Pos) /*!< 0x80000000 */ +#define ADC_OFR2_OFFSET2_EN ADC_OFR2_OFFSET2_EN_Msk /*!< ADC offset number 2 enable */ + +/******************** Bit definition for ADC_OFR3 register ******************/ +#define ADC_OFR3_OFFSET3_Pos (0U) +#define ADC_OFR3_OFFSET3_Msk (0xFFFUL << ADC_OFR3_OFFSET3_Pos) /*!< 0x00000FFF */ +#define ADC_OFR3_OFFSET3 ADC_OFR3_OFFSET3_Msk /*!< ADC offset number 3 offset level */ + +#define ADC_OFR3_OFFSETPOS_Pos (24U) +#define ADC_OFR3_OFFSETPOS_Msk (0x1UL << ADC_OFR3_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC_OFR3_OFFSETPOS ADC_OFR3_OFFSETPOS_Msk /*!< ADC offset number 3 positive */ +#define ADC_OFR3_SATEN_Pos (25U) +#define ADC_OFR3_SATEN_Msk (0x1UL << ADC_OFR3_SATEN_Pos) /*!< 0x02000000 */ +#define ADC_OFR3_SATEN ADC_OFR3_SATEN_Msk /*!< ADC offset number 3 saturation enable */ + +#define ADC_OFR3_OFFSET3_CH_Pos (26U) +#define ADC_OFR3_OFFSET3_CH_Msk (0x1FUL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR3_OFFSET3_CH ADC_OFR3_OFFSET3_CH_Msk /*!< ADC offset number 3 channel selection */ +#define ADC_OFR3_OFFSET3_CH_0 (0x01UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR3_OFFSET3_CH_1 (0x02UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR3_OFFSET3_CH_2 (0x04UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR3_OFFSET3_CH_3 (0x08UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR3_OFFSET3_CH_4 (0x10UL << ADC_OFR3_OFFSET3_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR3_OFFSET3_EN_Pos (31U) +#define ADC_OFR3_OFFSET3_EN_Msk (0x1UL << ADC_OFR3_OFFSET3_EN_Pos) /*!< 0x80000000 */ +#define ADC_OFR3_OFFSET3_EN ADC_OFR3_OFFSET3_EN_Msk /*!< ADC offset number 3 enable */ + +/******************** Bit definition for ADC_OFR4 register ******************/ +#define ADC_OFR4_OFFSET4_Pos (0U) +#define ADC_OFR4_OFFSET4_Msk (0xFFFUL << ADC_OFR4_OFFSET4_Pos) /*!< 0x00000FFF */ +#define ADC_OFR4_OFFSET4 ADC_OFR4_OFFSET4_Msk /*!< ADC offset number 4 offset level */ + +#define ADC_OFR4_OFFSETPOS_Pos (24U) +#define ADC_OFR4_OFFSETPOS_Msk (0x1UL << ADC_OFR4_OFFSETPOS_Pos) /*!< 0x01000000 */ +#define ADC_OFR4_OFFSETPOS ADC_OFR4_OFFSETPOS_Msk /*!< ADC offset number 4 positive */ +#define ADC_OFR4_SATEN_Pos (25U) +#define ADC_OFR4_SATEN_Msk (0x1UL << ADC_OFR4_SATEN_Pos) /*!< 0x02000000 */ +#define ADC_OFR4_SATEN ADC_OFR4_SATEN_Msk /*!< ADC offset number 4 saturation enable */ + +#define ADC_OFR4_OFFSET4_CH_Pos (26U) +#define ADC_OFR4_OFFSET4_CH_Msk (0x1FUL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x7C000000 */ +#define ADC_OFR4_OFFSET4_CH ADC_OFR4_OFFSET4_CH_Msk /*!< ADC offset number 4 channel selection */ +#define ADC_OFR4_OFFSET4_CH_0 (0x01UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x04000000 */ +#define ADC_OFR4_OFFSET4_CH_1 (0x02UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x08000000 */ +#define ADC_OFR4_OFFSET4_CH_2 (0x04UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x10000000 */ +#define ADC_OFR4_OFFSET4_CH_3 (0x08UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x20000000 */ +#define ADC_OFR4_OFFSET4_CH_4 (0x10UL << ADC_OFR4_OFFSET4_CH_Pos) /*!< 0x40000000 */ + +#define ADC_OFR4_OFFSET4_EN_Pos (31U) +#define ADC_OFR4_OFFSET4_EN_Msk (0x1UL << ADC_OFR4_OFFSET4_EN_Pos) /*!< 0x80000000 */ +#define ADC_OFR4_OFFSET4_EN ADC_OFR4_OFFSET4_EN_Msk /*!< ADC offset number 4 enable */ + +/******************** Bit definition for ADC_JDR1 register ******************/ +#define ADC_JDR1_JDATA_Pos (0U) +#define ADC_JDR1_JDATA_Msk (0xFFFFUL << ADC_JDR1_JDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_JDR1_JDATA ADC_JDR1_JDATA_Msk /*!< ADC group injected sequencer rank 1 conversion data */ + +/******************** Bit definition for ADC_JDR2 register ******************/ +#define ADC_JDR2_JDATA_Pos (0U) +#define ADC_JDR2_JDATA_Msk (0xFFFFUL << ADC_JDR2_JDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_JDR2_JDATA ADC_JDR2_JDATA_Msk /*!< ADC group injected sequencer rank 2 conversion data */ + +/******************** Bit definition for ADC_JDR3 register ******************/ +#define ADC_JDR3_JDATA_Pos (0U) +#define ADC_JDR3_JDATA_Msk (0xFFFFUL << ADC_JDR3_JDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_JDR3_JDATA ADC_JDR3_JDATA_Msk /*!< ADC group injected sequencer rank 3 conversion data */ + +/******************** Bit definition for ADC_JDR4 register ******************/ +#define ADC_JDR4_JDATA_Pos (0U) +#define ADC_JDR4_JDATA_Msk (0xFFFFUL << ADC_JDR4_JDATA_Pos) /*!< 0x0000FFFF */ +#define ADC_JDR4_JDATA ADC_JDR4_JDATA_Msk /*!< ADC group injected sequencer rank 4 conversion data */ + +/******************** Bit definition for ADC_AWD2CR register ****************/ +#define ADC_AWD2CR_AWD2CH_Pos (0U) +#define ADC_AWD2CR_AWD2CH_Msk (0x7FFFFUL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x0007FFFF */ +#define ADC_AWD2CR_AWD2CH ADC_AWD2CR_AWD2CH_Msk /*!< ADC analog watchdog 2 monitored channel selection */ +#define ADC_AWD2CR_AWD2CH_0 (0x00001UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000001 */ +#define ADC_AWD2CR_AWD2CH_1 (0x00002UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000002 */ +#define ADC_AWD2CR_AWD2CH_2 (0x00004UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000004 */ +#define ADC_AWD2CR_AWD2CH_3 (0x00008UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000008 */ +#define ADC_AWD2CR_AWD2CH_4 (0x00010UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000010 */ +#define ADC_AWD2CR_AWD2CH_5 (0x00020UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000020 */ +#define ADC_AWD2CR_AWD2CH_6 (0x00040UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000040 */ +#define ADC_AWD2CR_AWD2CH_7 (0x00080UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000080 */ +#define ADC_AWD2CR_AWD2CH_8 (0x00100UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000100 */ +#define ADC_AWD2CR_AWD2CH_9 (0x00200UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000200 */ +#define ADC_AWD2CR_AWD2CH_10 (0x00400UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000400 */ +#define ADC_AWD2CR_AWD2CH_11 (0x00800UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00000800 */ +#define ADC_AWD2CR_AWD2CH_12 (0x01000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00001000 */ +#define ADC_AWD2CR_AWD2CH_13 (0x02000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00002000 */ +#define ADC_AWD2CR_AWD2CH_14 (0x04000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00004000 */ +#define ADC_AWD2CR_AWD2CH_15 (0x08000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00008000 */ +#define ADC_AWD2CR_AWD2CH_16 (0x10000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00010000 */ +#define ADC_AWD2CR_AWD2CH_17 (0x20000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00020000 */ +#define ADC_AWD2CR_AWD2CH_18 (0x40000UL << ADC_AWD2CR_AWD2CH_Pos) /*!< 0x00040000 */ + +/******************** Bit definition for ADC_AWD3CR register ****************/ +#define ADC_AWD3CR_AWD3CH_Pos (0U) +#define ADC_AWD3CR_AWD3CH_Msk (0x7FFFFUL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x0007FFFF */ +#define ADC_AWD3CR_AWD3CH ADC_AWD3CR_AWD3CH_Msk /*!< ADC analog watchdog 3 monitored channel selection */ +#define ADC_AWD3CR_AWD3CH_0 (0x00001UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000001 */ +#define ADC_AWD3CR_AWD3CH_1 (0x00002UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000002 */ +#define ADC_AWD3CR_AWD3CH_2 (0x00004UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000004 */ +#define ADC_AWD3CR_AWD3CH_3 (0x00008UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000008 */ +#define ADC_AWD3CR_AWD3CH_4 (0x00010UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000010 */ +#define ADC_AWD3CR_AWD3CH_5 (0x00020UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000020 */ +#define ADC_AWD3CR_AWD3CH_6 (0x00040UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000040 */ +#define ADC_AWD3CR_AWD3CH_7 (0x00080UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000080 */ +#define ADC_AWD3CR_AWD3CH_8 (0x00100UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000100 */ +#define ADC_AWD3CR_AWD3CH_9 (0x00200UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000200 */ +#define ADC_AWD3CR_AWD3CH_10 (0x00400UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000400 */ +#define ADC_AWD3CR_AWD3CH_11 (0x00800UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00000800 */ +#define ADC_AWD3CR_AWD3CH_12 (0x01000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00001000 */ +#define ADC_AWD3CR_AWD3CH_13 (0x02000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00002000 */ +#define ADC_AWD3CR_AWD3CH_14 (0x04000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00004000 */ +#define ADC_AWD3CR_AWD3CH_15 (0x08000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00008000 */ +#define ADC_AWD3CR_AWD3CH_16 (0x10000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00010000 */ +#define ADC_AWD3CR_AWD3CH_17 (0x20000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00020000 */ +#define ADC_AWD3CR_AWD3CH_18 (0x40000UL << ADC_AWD3CR_AWD3CH_Pos) /*!< 0x00040000 */ + +/******************** Bit definition for ADC_DIFSEL register ****************/ +#define ADC_DIFSEL_DIFSEL_Pos (0U) +#define ADC_DIFSEL_DIFSEL_Msk (0x7FFFFUL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x0007FFFF */ +#define ADC_DIFSEL_DIFSEL ADC_DIFSEL_DIFSEL_Msk /*!< ADC channel differential or single-ended mode */ +#define ADC_DIFSEL_DIFSEL_0 (0x00001UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000001 */ +#define ADC_DIFSEL_DIFSEL_1 (0x00002UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000002 */ +#define ADC_DIFSEL_DIFSEL_2 (0x00004UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000004 */ +#define ADC_DIFSEL_DIFSEL_3 (0x00008UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000008 */ +#define ADC_DIFSEL_DIFSEL_4 (0x00010UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000010 */ +#define ADC_DIFSEL_DIFSEL_5 (0x00020UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000020 */ +#define ADC_DIFSEL_DIFSEL_6 (0x00040UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000040 */ +#define ADC_DIFSEL_DIFSEL_7 (0x00080UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000080 */ +#define ADC_DIFSEL_DIFSEL_8 (0x00100UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000100 */ +#define ADC_DIFSEL_DIFSEL_9 (0x00200UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000200 */ +#define ADC_DIFSEL_DIFSEL_10 (0x00400UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000400 */ +#define ADC_DIFSEL_DIFSEL_11 (0x00800UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00000800 */ +#define ADC_DIFSEL_DIFSEL_12 (0x01000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00001000 */ +#define ADC_DIFSEL_DIFSEL_13 (0x02000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00002000 */ +#define ADC_DIFSEL_DIFSEL_14 (0x04000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00004000 */ +#define ADC_DIFSEL_DIFSEL_15 (0x08000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00008000 */ +#define ADC_DIFSEL_DIFSEL_16 (0x10000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00010000 */ +#define ADC_DIFSEL_DIFSEL_17 (0x20000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00020000 */ +#define ADC_DIFSEL_DIFSEL_18 (0x40000UL << ADC_DIFSEL_DIFSEL_Pos) /*!< 0x00040000 */ + +/******************** Bit definition for ADC_CALFACT register ***************/ +#define ADC_CALFACT_CALFACT_S_Pos (0U) +#define ADC_CALFACT_CALFACT_S_Msk (0x7FUL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x0000007F */ +#define ADC_CALFACT_CALFACT_S ADC_CALFACT_CALFACT_S_Msk /*!< ADC calibration factor in single-ended mode */ +#define ADC_CALFACT_CALFACT_S_0 (0x01UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000001 */ +#define ADC_CALFACT_CALFACT_S_1 (0x02UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000002 */ +#define ADC_CALFACT_CALFACT_S_2 (0x04UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000004 */ +#define ADC_CALFACT_CALFACT_S_3 (0x08UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000008 */ +#define ADC_CALFACT_CALFACT_S_4 (0x10UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000010 */ +#define ADC_CALFACT_CALFACT_S_5 (0x20UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000020 */ +#define ADC_CALFACT_CALFACT_S_6 (0x40UL << ADC_CALFACT_CALFACT_S_Pos) /*!< 0x00000030 */ + +#define ADC_CALFACT_CALFACT_D_Pos (16U) +#define ADC_CALFACT_CALFACT_D_Msk (0x7FUL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x007F0000 */ +#define ADC_CALFACT_CALFACT_D ADC_CALFACT_CALFACT_D_Msk /*!< ADC calibration factor in differential mode */ +#define ADC_CALFACT_CALFACT_D_0 (0x01UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00010000 */ +#define ADC_CALFACT_CALFACT_D_1 (0x02UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00020000 */ +#define ADC_CALFACT_CALFACT_D_2 (0x04UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00040000 */ +#define ADC_CALFACT_CALFACT_D_3 (0x08UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00080000 */ +#define ADC_CALFACT_CALFACT_D_4 (0x10UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00100000 */ +#define ADC_CALFACT_CALFACT_D_5 (0x20UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00200000 */ +#define ADC_CALFACT_CALFACT_D_6 (0x40UL << ADC_CALFACT_CALFACT_D_Pos) /*!< 0x00300000 */ + +/******************** Bit definition for ADC_GCOMP register *****************/ +#define ADC_GCOMP_GCOMPCOEFF_Pos (0U) +#define ADC_GCOMP_GCOMPCOEFF_Msk (0x3FFFUL << ADC_GCOMP_GCOMPCOEFF_Pos) /*!< 0x00003FFF */ +#define ADC_GCOMP_GCOMPCOEFF ADC_GCOMP_GCOMPCOEFF_Msk /*!< ADC Gain Compensation Coefficient */ + +/************************* ADC Common registers *****************************/ +/******************** Bit definition for ADC_CSR register *******************/ +#define ADC_CSR_ADRDY_MST_Pos (0U) +#define ADC_CSR_ADRDY_MST_Msk (0x1UL << ADC_CSR_ADRDY_MST_Pos) /*!< 0x00000001 */ +#define ADC_CSR_ADRDY_MST ADC_CSR_ADRDY_MST_Msk /*!< ADC multimode master ready flag */ +#define ADC_CSR_EOSMP_MST_Pos (1U) +#define ADC_CSR_EOSMP_MST_Msk (0x1UL << ADC_CSR_EOSMP_MST_Pos) /*!< 0x00000002 */ +#define ADC_CSR_EOSMP_MST ADC_CSR_EOSMP_MST_Msk /*!< ADC multimode master group regular end of sampling flag */ +#define ADC_CSR_EOC_MST_Pos (2U) +#define ADC_CSR_EOC_MST_Msk (0x1UL << ADC_CSR_EOC_MST_Pos) /*!< 0x00000004 */ +#define ADC_CSR_EOC_MST ADC_CSR_EOC_MST_Msk /*!< ADC multimode master group regular end of unitary conversion flag */ +#define ADC_CSR_EOS_MST_Pos (3U) +#define ADC_CSR_EOS_MST_Msk (0x1UL << ADC_CSR_EOS_MST_Pos) /*!< 0x00000008 */ +#define ADC_CSR_EOS_MST ADC_CSR_EOS_MST_Msk /*!< ADC multimode master group regular end of sequence conversions flag */ +#define ADC_CSR_OVR_MST_Pos (4U) +#define ADC_CSR_OVR_MST_Msk (0x1UL << ADC_CSR_OVR_MST_Pos) /*!< 0x00000010 */ +#define ADC_CSR_OVR_MST ADC_CSR_OVR_MST_Msk /*!< ADC multimode master group regular overrun flag */ +#define ADC_CSR_JEOC_MST_Pos (5U) +#define ADC_CSR_JEOC_MST_Msk (0x1UL << ADC_CSR_JEOC_MST_Pos) /*!< 0x00000020 */ +#define ADC_CSR_JEOC_MST ADC_CSR_JEOC_MST_Msk /*!< ADC multimode master group injected end of unitary conversion flag */ +#define ADC_CSR_JEOS_MST_Pos (6U) +#define ADC_CSR_JEOS_MST_Msk (0x1UL << ADC_CSR_JEOS_MST_Pos) /*!< 0x00000040 */ +#define ADC_CSR_JEOS_MST ADC_CSR_JEOS_MST_Msk /*!< ADC multimode master group injected end of sequence conversions flag */ +#define ADC_CSR_AWD1_MST_Pos (7U) +#define ADC_CSR_AWD1_MST_Msk (0x1UL << ADC_CSR_AWD1_MST_Pos) /*!< 0x00000080 */ +#define ADC_CSR_AWD1_MST ADC_CSR_AWD1_MST_Msk /*!< ADC multimode master analog watchdog 1 flag */ +#define ADC_CSR_AWD2_MST_Pos (8U) +#define ADC_CSR_AWD2_MST_Msk (0x1UL << ADC_CSR_AWD2_MST_Pos) /*!< 0x00000100 */ +#define ADC_CSR_AWD2_MST ADC_CSR_AWD2_MST_Msk /*!< ADC multimode master analog watchdog 2 flag */ +#define ADC_CSR_AWD3_MST_Pos (9U) +#define ADC_CSR_AWD3_MST_Msk (0x1UL << ADC_CSR_AWD3_MST_Pos) /*!< 0x00000200 */ +#define ADC_CSR_AWD3_MST ADC_CSR_AWD3_MST_Msk /*!< ADC multimode master analog watchdog 3 flag */ +#define ADC_CSR_JQOVF_MST_Pos (10U) +#define ADC_CSR_JQOVF_MST_Msk (0x1UL << ADC_CSR_JQOVF_MST_Pos) /*!< 0x00000400 */ +#define ADC_CSR_JQOVF_MST ADC_CSR_JQOVF_MST_Msk /*!< ADC multimode master group injected contexts queue overflow flag */ + +#define ADC_CSR_ADRDY_SLV_Pos (16U) +#define ADC_CSR_ADRDY_SLV_Msk (0x1UL << ADC_CSR_ADRDY_SLV_Pos) /*!< 0x00010000 */ +#define ADC_CSR_ADRDY_SLV ADC_CSR_ADRDY_SLV_Msk /*!< ADC multimode slave ready flag */ +#define ADC_CSR_EOSMP_SLV_Pos (17U) +#define ADC_CSR_EOSMP_SLV_Msk (0x1UL << ADC_CSR_EOSMP_SLV_Pos) /*!< 0x00020000 */ +#define ADC_CSR_EOSMP_SLV ADC_CSR_EOSMP_SLV_Msk /*!< ADC multimode slave group regular end of sampling flag */ +#define ADC_CSR_EOC_SLV_Pos (18U) +#define ADC_CSR_EOC_SLV_Msk (0x1UL << ADC_CSR_EOC_SLV_Pos) /*!< 0x00040000 */ +#define ADC_CSR_EOC_SLV ADC_CSR_EOC_SLV_Msk /*!< ADC multimode slave group regular end of unitary conversion flag */ +#define ADC_CSR_EOS_SLV_Pos (19U) +#define ADC_CSR_EOS_SLV_Msk (0x1UL << ADC_CSR_EOS_SLV_Pos) /*!< 0x00080000 */ +#define ADC_CSR_EOS_SLV ADC_CSR_EOS_SLV_Msk /*!< ADC multimode slave group regular end of sequence conversions flag */ +#define ADC_CSR_OVR_SLV_Pos (20U) +#define ADC_CSR_OVR_SLV_Msk (0x1UL << ADC_CSR_OVR_SLV_Pos) /*!< 0x00100000 */ +#define ADC_CSR_OVR_SLV ADC_CSR_OVR_SLV_Msk /*!< ADC multimode slave group regular overrun flag */ +#define ADC_CSR_JEOC_SLV_Pos (21U) +#define ADC_CSR_JEOC_SLV_Msk (0x1UL << ADC_CSR_JEOC_SLV_Pos) /*!< 0x00200000 */ +#define ADC_CSR_JEOC_SLV ADC_CSR_JEOC_SLV_Msk /*!< ADC multimode slave group injected end of unitary conversion flag */ +#define ADC_CSR_JEOS_SLV_Pos (22U) +#define ADC_CSR_JEOS_SLV_Msk (0x1UL << ADC_CSR_JEOS_SLV_Pos) /*!< 0x00400000 */ +#define ADC_CSR_JEOS_SLV ADC_CSR_JEOS_SLV_Msk /*!< ADC multimode slave group injected end of sequence conversions flag */ +#define ADC_CSR_AWD1_SLV_Pos (23U) +#define ADC_CSR_AWD1_SLV_Msk (0x1UL << ADC_CSR_AWD1_SLV_Pos) /*!< 0x00800000 */ +#define ADC_CSR_AWD1_SLV ADC_CSR_AWD1_SLV_Msk /*!< ADC multimode slave analog watchdog 1 flag */ +#define ADC_CSR_AWD2_SLV_Pos (24U) +#define ADC_CSR_AWD2_SLV_Msk (0x1UL << ADC_CSR_AWD2_SLV_Pos) /*!< 0x01000000 */ +#define ADC_CSR_AWD2_SLV ADC_CSR_AWD2_SLV_Msk /*!< ADC multimode slave analog watchdog 2 flag */ +#define ADC_CSR_AWD3_SLV_Pos (25U) +#define ADC_CSR_AWD3_SLV_Msk (0x1UL << ADC_CSR_AWD3_SLV_Pos) /*!< 0x02000000 */ +#define ADC_CSR_AWD3_SLV ADC_CSR_AWD3_SLV_Msk /*!< ADC multimode slave analog watchdog 3 flag */ +#define ADC_CSR_JQOVF_SLV_Pos (26U) +#define ADC_CSR_JQOVF_SLV_Msk (0x1UL << ADC_CSR_JQOVF_SLV_Pos) /*!< 0x04000000 */ +#define ADC_CSR_JQOVF_SLV ADC_CSR_JQOVF_SLV_Msk /*!< ADC multimode slave group injected contexts queue overflow flag */ + +/******************** Bit definition for ADC_CCR register *******************/ +#define ADC_CCR_DUAL_Pos (0U) +#define ADC_CCR_DUAL_Msk (0x1FUL << ADC_CCR_DUAL_Pos) /*!< 0x0000001F */ +#define ADC_CCR_DUAL ADC_CCR_DUAL_Msk /*!< ADC multimode mode selection */ +#define ADC_CCR_DUAL_0 (0x01UL << ADC_CCR_DUAL_Pos) /*!< 0x00000001 */ +#define ADC_CCR_DUAL_1 (0x02UL << ADC_CCR_DUAL_Pos) /*!< 0x00000002 */ +#define ADC_CCR_DUAL_2 (0x04UL << ADC_CCR_DUAL_Pos) /*!< 0x00000004 */ +#define ADC_CCR_DUAL_3 (0x08UL << ADC_CCR_DUAL_Pos) /*!< 0x00000008 */ +#define ADC_CCR_DUAL_4 (0x10UL << ADC_CCR_DUAL_Pos) /*!< 0x00000010 */ + +#define ADC_CCR_DELAY_Pos (8U) +#define ADC_CCR_DELAY_Msk (0xFUL << ADC_CCR_DELAY_Pos) /*!< 0x00000F00 */ +#define ADC_CCR_DELAY ADC_CCR_DELAY_Msk /*!< ADC multimode delay between 2 sampling phases */ +#define ADC_CCR_DELAY_0 (0x1UL << ADC_CCR_DELAY_Pos) /*!< 0x00000100 */ +#define ADC_CCR_DELAY_1 (0x2UL << ADC_CCR_DELAY_Pos) /*!< 0x00000200 */ +#define ADC_CCR_DELAY_2 (0x4UL << ADC_CCR_DELAY_Pos) /*!< 0x00000400 */ +#define ADC_CCR_DELAY_3 (0x8UL << ADC_CCR_DELAY_Pos) /*!< 0x00000800 */ + +#define ADC_CCR_DMACFG_Pos (13U) +#define ADC_CCR_DMACFG_Msk (0x1UL << ADC_CCR_DMACFG_Pos) /*!< 0x00002000 */ +#define ADC_CCR_DMACFG ADC_CCR_DMACFG_Msk /*!< ADC multimode DMA transfer configuration */ + +#define ADC_CCR_MDMA_Pos (14U) +#define ADC_CCR_MDMA_Msk (0x3UL << ADC_CCR_MDMA_Pos) /*!< 0x0000C000 */ +#define ADC_CCR_MDMA ADC_CCR_MDMA_Msk /*!< ADC multimode DMA transfer enable */ +#define ADC_CCR_MDMA_0 (0x1UL << ADC_CCR_MDMA_Pos) /*!< 0x00004000 */ +#define ADC_CCR_MDMA_1 (0x2UL << ADC_CCR_MDMA_Pos) /*!< 0x00008000 */ + +#define ADC_CCR_CKMODE_Pos (16U) +#define ADC_CCR_CKMODE_Msk (0x3UL << ADC_CCR_CKMODE_Pos) /*!< 0x00030000 */ +#define ADC_CCR_CKMODE ADC_CCR_CKMODE_Msk /*!< ADC common clock source and prescaler (prescaler only for clock source synchronous) */ +#define ADC_CCR_CKMODE_0 (0x1UL << ADC_CCR_CKMODE_Pos) /*!< 0x00010000 */ +#define ADC_CCR_CKMODE_1 (0x2UL << ADC_CCR_CKMODE_Pos) /*!< 0x00020000 */ + +#define ADC_CCR_PRESC_Pos (18U) +#define ADC_CCR_PRESC_Msk (0xFUL << ADC_CCR_PRESC_Pos) /*!< 0x003C0000 */ +#define ADC_CCR_PRESC ADC_CCR_PRESC_Msk /*!< ADC common clock prescaler, only for clock source asynchronous */ +#define ADC_CCR_PRESC_0 (0x1UL << ADC_CCR_PRESC_Pos) /*!< 0x00040000 */ +#define ADC_CCR_PRESC_1 (0x2UL << ADC_CCR_PRESC_Pos) /*!< 0x00080000 */ +#define ADC_CCR_PRESC_2 (0x4UL << ADC_CCR_PRESC_Pos) /*!< 0x00100000 */ +#define ADC_CCR_PRESC_3 (0x8UL << ADC_CCR_PRESC_Pos) /*!< 0x00200000 */ + +#define ADC_CCR_VREFEN_Pos (22U) +#define ADC_CCR_VREFEN_Msk (0x1UL << ADC_CCR_VREFEN_Pos) /*!< 0x00400000 */ +#define ADC_CCR_VREFEN ADC_CCR_VREFEN_Msk /*!< ADC internal path to VrefInt enable */ +#define ADC_CCR_VSENSESEL_Pos (23U) +#define ADC_CCR_VSENSESEL_Msk (0x1UL << ADC_CCR_VSENSESEL_Pos) /*!< 0x00800000 */ +#define ADC_CCR_VSENSESEL ADC_CCR_VSENSESEL_Msk /*!< ADC internal path to temperature sensor enable */ +#define ADC_CCR_VBATSEL_Pos (24U) +#define ADC_CCR_VBATSEL_Msk (0x1UL << ADC_CCR_VBATSEL_Pos) /*!< 0x01000000 */ +#define ADC_CCR_VBATSEL ADC_CCR_VBATSEL_Msk /*!< ADC internal path to battery voltage enable */ + +/******************** Bit definition for ADC_CDR register *******************/ +#define ADC_CDR_RDATA_MST_Pos (0U) +#define ADC_CDR_RDATA_MST_Msk (0xFFFFUL << ADC_CDR_RDATA_MST_Pos) /*!< 0x0000FFFF */ +#define ADC_CDR_RDATA_MST ADC_CDR_RDATA_MST_Msk /*!< ADC multimode master group regular conversion data */ + +#define ADC_CDR_RDATA_SLV_Pos (16U) +#define ADC_CDR_RDATA_SLV_Msk (0xFFFFUL << ADC_CDR_RDATA_SLV_Pos) /*!< 0xFFFF0000 */ +#define ADC_CDR_RDATA_SLV ADC_CDR_RDATA_SLV_Msk /*!< ADC multimode slave group regular conversion data */ + + +/******************************************************************************/ +/* */ +/* Analog Comparators (COMP) */ +/* */ +/******************************************************************************/ +/********************** Bit definition for COMP_CSR register ****************/ +#define COMP_CSR_EN_Pos (0U) +#define COMP_CSR_EN_Msk (0x1UL << COMP_CSR_EN_Pos) /*!< 0x00000001 */ +#define COMP_CSR_EN COMP_CSR_EN_Msk /*!< Comparator enable */ + +#define COMP_CSR_INMSEL_Pos (4U) +#define COMP_CSR_INMSEL_Msk (0xFUL << COMP_CSR_INMSEL_Pos) /*!< 0x00000070 */ +#define COMP_CSR_INMSEL COMP_CSR_INMSEL_Msk /*!< Comparator input minus selection */ +#define COMP_CSR_INMSEL_0 (0x1UL << COMP_CSR_INMSEL_Pos) /*!< 0x00000010 */ +#define COMP_CSR_INMSEL_1 (0x2UL << COMP_CSR_INMSEL_Pos) /*!< 0x00000020 */ +#define COMP_CSR_INMSEL_2 (0x4UL << COMP_CSR_INMSEL_Pos) /*!< 0x00000040 */ +#define COMP_CSR_INMSEL_3 (0x8UL << COMP_CSR_INMSEL_Pos) /*!< 0x00000080 */ + +#define COMP_CSR_INPSEL_Pos (8U) +#define COMP_CSR_INPSEL_Msk (0x1UL << COMP_CSR_INPSEL_Pos) /*!< 0x00000100 */ +#define COMP_CSR_INPSEL COMP_CSR_INPSEL_Msk /*!< Comparator input plus selection */ + +#define COMP_CSR_POLARITY_Pos (15U) +#define COMP_CSR_POLARITY_Msk (0x1UL << COMP_CSR_POLARITY_Pos) /*!< 0x00008000 */ +#define COMP_CSR_POLARITY COMP_CSR_POLARITY_Msk /*!< Comparator output polarity */ + +#define COMP_CSR_HYST_Pos (16U) +#define COMP_CSR_HYST_Msk (0x7UL << COMP_CSR_HYST_Pos) /*!< 0x00070000 */ +#define COMP_CSR_HYST COMP_CSR_HYST_Msk /*!< Comparator hysteresis */ +#define COMP_CSR_HYST_0 (0x1UL << COMP_CSR_HYST_Pos) /*!< 0x00010000 */ +#define COMP_CSR_HYST_1 (0x2UL << COMP_CSR_HYST_Pos) /*!< 0x00020000 */ +#define COMP_CSR_HYST_2 (0x4UL << COMP_CSR_HYST_Pos) /*!< 0x00040000 */ + +#define COMP_CSR_BLANKING_Pos (19U) +#define COMP_CSR_BLANKING_Msk (0x7UL << COMP_CSR_BLANKING_Pos) /*!< 0x00380000 */ +#define COMP_CSR_BLANKING COMP_CSR_BLANKING_Msk /*!< Comparator blanking source */ +#define COMP_CSR_BLANKING_0 (0x1UL << COMP_CSR_BLANKING_Pos) /*!< 0x00080000 */ +#define COMP_CSR_BLANKING_1 (0x2UL << COMP_CSR_BLANKING_Pos) /*!< 0x00100000 */ +#define COMP_CSR_BLANKING_2 (0x4UL << COMP_CSR_BLANKING_Pos) /*!< 0x00200000 */ + +#define COMP_CSR_BRGEN_Pos (22U) +#define COMP_CSR_BRGEN_Msk (0x1UL << COMP_CSR_BRGEN_Pos) /*!< 0x00400000 */ +#define COMP_CSR_BRGEN COMP_CSR_BRGEN_Msk /*!< Comparator scaler bridge enable */ + +#define COMP_CSR_SCALEN_Pos (23U) +#define COMP_CSR_SCALEN_Msk (0x1UL << COMP_CSR_SCALEN_Pos) /*!< 0x00800000 */ +#define COMP_CSR_SCALEN COMP_CSR_SCALEN_Msk /*!< Comparator voltage scaler enable */ + +#define COMP_CSR_VALUE_Pos (30U) +#define COMP_CSR_VALUE_Msk (0x1UL << COMP_CSR_VALUE_Pos) /*!< 0x40000000 */ +#define COMP_CSR_VALUE COMP_CSR_VALUE_Msk /*!< Comparator output level */ + +#define COMP_CSR_LOCK_Pos (31U) +#define COMP_CSR_LOCK_Msk (0x1UL << COMP_CSR_LOCK_Pos) /*!< 0x80000000 */ +#define COMP_CSR_LOCK COMP_CSR_LOCK_Msk /*!< Comparator lock */ + +/******************************************************************************/ +/* */ +/* CORDIC calculation unit */ +/* */ +/******************************************************************************/ +/******************* Bit definition for CORDIC_CSR register *****************/ +#define CORDIC_CSR_FUNC_Pos (0U) +#define CORDIC_CSR_FUNC_Msk (0xFUL << CORDIC_CSR_FUNC_Pos) /*!< 0x0000000F */ +#define CORDIC_CSR_FUNC CORDIC_CSR_FUNC_Msk /*!< Function */ +#define CORDIC_CSR_FUNC_0 (0x1UL << CORDIC_CSR_FUNC_Pos) /*!< 0x00000001 */ +#define CORDIC_CSR_FUNC_1 (0x2UL << CORDIC_CSR_FUNC_Pos) /*!< 0x00000002 */ +#define CORDIC_CSR_FUNC_2 (0x4UL << CORDIC_CSR_FUNC_Pos) /*!< 0x00000004 */ +#define CORDIC_CSR_FUNC_3 (0x8UL << CORDIC_CSR_FUNC_Pos) /*!< 0x00000008 */ +#define CORDIC_CSR_PRECISION_Pos (4U) +#define CORDIC_CSR_PRECISION_Msk (0xFUL << CORDIC_CSR_PRECISION_Pos) /*!< 0x000000F0 */ +#define CORDIC_CSR_PRECISION CORDIC_CSR_PRECISION_Msk /*!< Precision */ +#define CORDIC_CSR_PRECISION_0 (0x1UL << CORDIC_CSR_PRECISION_Pos) /*!< 0x00000010 */ +#define CORDIC_CSR_PRECISION_1 (0x2UL << CORDIC_CSR_PRECISION_Pos) /*!< 0x00000020 */ +#define CORDIC_CSR_PRECISION_2 (0x4UL << CORDIC_CSR_PRECISION_Pos) /*!< 0x00000040 */ +#define CORDIC_CSR_PRECISION_3 (0x8UL << CORDIC_CSR_PRECISION_Pos) /*!< 0x00000080 */ +#define CORDIC_CSR_SCALE_Pos (8U) +#define CORDIC_CSR_SCALE_Msk (0x7UL << CORDIC_CSR_SCALE_Pos) /*!< 0x00000700 */ +#define CORDIC_CSR_SCALE CORDIC_CSR_SCALE_Msk /*!< Scaling factor */ +#define CORDIC_CSR_SCALE_0 (0x1UL << CORDIC_CSR_SCALE_Pos) /*!< 0x00000100 */ +#define CORDIC_CSR_SCALE_1 (0x2UL << CORDIC_CSR_SCALE_Pos) /*!< 0x00000200 */ +#define CORDIC_CSR_SCALE_2 (0x4UL << CORDIC_CSR_SCALE_Pos) /*!< 0x00000400 */ +#define CORDIC_CSR_IEN_Pos (16U) +#define CORDIC_CSR_IEN_Msk (0x1UL << CORDIC_CSR_IEN_Pos) /*!< 0x00010000 */ +#define CORDIC_CSR_IEN CORDIC_CSR_IEN_Msk /*!< Interrupt Enable */ +#define CORDIC_CSR_DMAREN_Pos (17U) +#define CORDIC_CSR_DMAREN_Msk (0x1UL << CORDIC_CSR_DMAREN_Pos) /*!< 0x00020000 */ +#define CORDIC_CSR_DMAREN CORDIC_CSR_DMAREN_Msk /*!< DMA Read channel Enable */ +#define CORDIC_CSR_DMAWEN_Pos (18U) +#define CORDIC_CSR_DMAWEN_Msk (0x1UL << CORDIC_CSR_DMAWEN_Pos) /*!< 0x00040000 */ +#define CORDIC_CSR_DMAWEN CORDIC_CSR_DMAWEN_Msk /*!< DMA Write channel Enable */ +#define CORDIC_CSR_NRES_Pos (19U) +#define CORDIC_CSR_NRES_Msk (0x1UL << CORDIC_CSR_NRES_Pos) /*!< 0x00080000 */ +#define CORDIC_CSR_NRES CORDIC_CSR_NRES_Msk /*!< Number of results in WDATA register */ +#define CORDIC_CSR_NARGS_Pos (20U) +#define CORDIC_CSR_NARGS_Msk (0x1UL << CORDIC_CSR_NARGS_Pos) /*!< 0x00100000 */ +#define CORDIC_CSR_NARGS CORDIC_CSR_NARGS_Msk /*!< Number of arguments in RDATA register */ +#define CORDIC_CSR_RESSIZE_Pos (21U) +#define CORDIC_CSR_RESSIZE_Msk (0x1UL << CORDIC_CSR_RESSIZE_Pos) /*!< 0x00200000 */ +#define CORDIC_CSR_RESSIZE CORDIC_CSR_RESSIZE_Msk /*!< Width of output data */ +#define CORDIC_CSR_ARGSIZE_Pos (22U) +#define CORDIC_CSR_ARGSIZE_Msk (0x1UL << CORDIC_CSR_ARGSIZE_Pos) /*!< 0x00400000 */ +#define CORDIC_CSR_ARGSIZE CORDIC_CSR_ARGSIZE_Msk /*!< Width of input data */ +#define CORDIC_CSR_RRDY_Pos (31U) +#define CORDIC_CSR_RRDY_Msk (0x1UL << CORDIC_CSR_RRDY_Pos) /*!< 0x80000000 */ +#define CORDIC_CSR_RRDY CORDIC_CSR_RRDY_Msk /*!< Result Ready Flag */ + +/******************* Bit definition for CORDIC_WDATA register ***************/ +#define CORDIC_WDATA_ARG_Pos (0U) +#define CORDIC_WDATA_ARG_Msk (0xFFFFFFFFUL << CORDIC_WDATA_ARG_Pos) /*!< 0xFFFFFFFF */ +#define CORDIC_WDATA_ARG CORDIC_WDATA_ARG_Msk /*!< Input Argument */ + +/******************* Bit definition for CORDIC_RDATA register ***************/ +#define CORDIC_RDATA_RES_Pos (0U) +#define CORDIC_RDATA_RES_Msk (0xFFFFFFFFUL << CORDIC_RDATA_RES_Pos) /*!< 0xFFFFFFFF */ +#define CORDIC_RDATA_RES CORDIC_RDATA_RES_Msk /*!< Output Result */ + +/******************************************************************************/ +/* */ +/* CRC calculation unit */ +/* */ +/******************************************************************************/ +/******************* Bit definition for CRC_DR register *********************/ +#define CRC_DR_DR_Pos (0U) +#define CRC_DR_DR_Msk (0xFFFFFFFFUL << CRC_DR_DR_Pos) /*!< 0xFFFFFFFF */ +#define CRC_DR_DR CRC_DR_DR_Msk /*!< Data register bits */ + +/******************* Bit definition for CRC_IDR register ********************/ +#define CRC_IDR_IDR_Pos (0U) +#define CRC_IDR_IDR_Msk (0xFFFFFFFFUL << CRC_IDR_IDR_Pos) /*!< 0xFFFFFFFF */ +#define CRC_IDR_IDR CRC_IDR_IDR_Msk /*!< General-purpose 32-bit data register bits */ + +/******************** Bit definition for CRC_CR register ********************/ +#define CRC_CR_RESET_Pos (0U) +#define CRC_CR_RESET_Msk (0x1UL << CRC_CR_RESET_Pos) /*!< 0x00000001 */ +#define CRC_CR_RESET CRC_CR_RESET_Msk /*!< RESET the CRC computation unit bit */ +#define CRC_CR_POLYSIZE_Pos (3U) +#define CRC_CR_POLYSIZE_Msk (0x3UL << CRC_CR_POLYSIZE_Pos) /*!< 0x00000018 */ +#define CRC_CR_POLYSIZE CRC_CR_POLYSIZE_Msk /*!< Polynomial size bits */ +#define CRC_CR_POLYSIZE_0 (0x1UL << CRC_CR_POLYSIZE_Pos) /*!< 0x00000008 */ +#define CRC_CR_POLYSIZE_1 (0x2UL << CRC_CR_POLYSIZE_Pos) /*!< 0x00000010 */ +#define CRC_CR_REV_IN_Pos (5U) +#define CRC_CR_REV_IN_Msk (0x3UL << CRC_CR_REV_IN_Pos) /*!< 0x00000060 */ +#define CRC_CR_REV_IN CRC_CR_REV_IN_Msk /*!< REV_IN Reverse Input Data bits */ +#define CRC_CR_REV_IN_0 (0x1UL << CRC_CR_REV_IN_Pos) /*!< 0x00000020 */ +#define CRC_CR_REV_IN_1 (0x2UL << CRC_CR_REV_IN_Pos) /*!< 0x00000040 */ +#define CRC_CR_REV_OUT_Pos (7U) +#define CRC_CR_REV_OUT_Msk (0x1UL << CRC_CR_REV_OUT_Pos) /*!< 0x00000080 */ +#define CRC_CR_REV_OUT CRC_CR_REV_OUT_Msk /*!< REV_OUT Reverse Output Data bits */ + +/******************* Bit definition for CRC_INIT register *******************/ +#define CRC_INIT_INIT_Pos (0U) +#define CRC_INIT_INIT_Msk (0xFFFFFFFFUL << CRC_INIT_INIT_Pos) /*!< 0xFFFFFFFF */ +#define CRC_INIT_INIT CRC_INIT_INIT_Msk /*!< Initial CRC value bits */ + +/******************* Bit definition for CRC_POL register ********************/ +#define CRC_POL_POL_Pos (0U) +#define CRC_POL_POL_Msk (0xFFFFFFFFUL << CRC_POL_POL_Pos) /*!< 0xFFFFFFFF */ +#define CRC_POL_POL CRC_POL_POL_Msk /*!< Coefficients of the polynomial */ + +/******************************************************************************/ +/* */ +/* CRS Clock Recovery System */ +/******************************************************************************/ + +/******************* Bit definition for CRS_CR register *********************/ +#define CRS_CR_SYNCOKIE_Pos (0U) +#define CRS_CR_SYNCOKIE_Msk (0x1UL << CRS_CR_SYNCOKIE_Pos) /*!< 0x00000001 */ +#define CRS_CR_SYNCOKIE CRS_CR_SYNCOKIE_Msk /*!< SYNC event OK interrupt enable */ +#define CRS_CR_SYNCWARNIE_Pos (1U) +#define CRS_CR_SYNCWARNIE_Msk (0x1UL << CRS_CR_SYNCWARNIE_Pos) /*!< 0x00000002 */ +#define CRS_CR_SYNCWARNIE CRS_CR_SYNCWARNIE_Msk /*!< SYNC warning interrupt enable */ +#define CRS_CR_ERRIE_Pos (2U) +#define CRS_CR_ERRIE_Msk (0x1UL << CRS_CR_ERRIE_Pos) /*!< 0x00000004 */ +#define CRS_CR_ERRIE CRS_CR_ERRIE_Msk /*!< SYNC error or trimming error interrupt enable */ +#define CRS_CR_ESYNCIE_Pos (3U) +#define CRS_CR_ESYNCIE_Msk (0x1UL << CRS_CR_ESYNCIE_Pos) /*!< 0x00000008 */ +#define CRS_CR_ESYNCIE CRS_CR_ESYNCIE_Msk /*!< Expected SYNC interrupt enable */ +#define CRS_CR_CEN_Pos (5U) +#define CRS_CR_CEN_Msk (0x1UL << CRS_CR_CEN_Pos) /*!< 0x00000020 */ +#define CRS_CR_CEN CRS_CR_CEN_Msk /*!< Frequency error counter enable */ +#define CRS_CR_AUTOTRIMEN_Pos (6U) +#define CRS_CR_AUTOTRIMEN_Msk (0x1UL << CRS_CR_AUTOTRIMEN_Pos) /*!< 0x00000040 */ +#define CRS_CR_AUTOTRIMEN CRS_CR_AUTOTRIMEN_Msk /*!< Automatic trimming enable */ +#define CRS_CR_SWSYNC_Pos (7U) +#define CRS_CR_SWSYNC_Msk (0x1UL << CRS_CR_SWSYNC_Pos) /*!< 0x00000080 */ +#define CRS_CR_SWSYNC CRS_CR_SWSYNC_Msk /*!< Generate software SYNC event */ +#define CRS_CR_TRIM_Pos (8U) +#define CRS_CR_TRIM_Msk (0x7FUL << CRS_CR_TRIM_Pos) /*!< 0x00007F00 */ +#define CRS_CR_TRIM CRS_CR_TRIM_Msk /*!< HSI48 oscillator smooth trimming */ + +/******************* Bit definition for CRS_CFGR register *********************/ +#define CRS_CFGR_RELOAD_Pos (0U) +#define CRS_CFGR_RELOAD_Msk (0xFFFFUL << CRS_CFGR_RELOAD_Pos) /*!< 0x0000FFFF */ +#define CRS_CFGR_RELOAD CRS_CFGR_RELOAD_Msk /*!< Counter reload value */ +#define CRS_CFGR_FELIM_Pos (16U) +#define CRS_CFGR_FELIM_Msk (0xFFUL << CRS_CFGR_FELIM_Pos) /*!< 0x00FF0000 */ +#define CRS_CFGR_FELIM CRS_CFGR_FELIM_Msk /*!< Frequency error limit */ + +#define CRS_CFGR_SYNCDIV_Pos (24U) +#define CRS_CFGR_SYNCDIV_Msk (0x7UL << CRS_CFGR_SYNCDIV_Pos) /*!< 0x07000000 */ +#define CRS_CFGR_SYNCDIV CRS_CFGR_SYNCDIV_Msk /*!< SYNC divider */ +#define CRS_CFGR_SYNCDIV_0 (0x1UL << CRS_CFGR_SYNCDIV_Pos) /*!< 0x01000000 */ +#define CRS_CFGR_SYNCDIV_1 (0x2UL << CRS_CFGR_SYNCDIV_Pos) /*!< 0x02000000 */ +#define CRS_CFGR_SYNCDIV_2 (0x4UL << CRS_CFGR_SYNCDIV_Pos) /*!< 0x04000000 */ + +#define CRS_CFGR_SYNCSRC_Pos (28U) +#define CRS_CFGR_SYNCSRC_Msk (0x3UL << CRS_CFGR_SYNCSRC_Pos) /*!< 0x30000000 */ +#define CRS_CFGR_SYNCSRC CRS_CFGR_SYNCSRC_Msk /*!< SYNC signal source selection */ +#define CRS_CFGR_SYNCSRC_0 (0x1UL << CRS_CFGR_SYNCSRC_Pos) /*!< 0x10000000 */ +#define CRS_CFGR_SYNCSRC_1 (0x2UL << CRS_CFGR_SYNCSRC_Pos) /*!< 0x20000000 */ + +#define CRS_CFGR_SYNCPOL_Pos (31U) +#define CRS_CFGR_SYNCPOL_Msk (0x1UL << CRS_CFGR_SYNCPOL_Pos) /*!< 0x80000000 */ +#define CRS_CFGR_SYNCPOL CRS_CFGR_SYNCPOL_Msk /*!< SYNC polarity selection */ + +/******************* Bit definition for CRS_ISR register *********************/ +#define CRS_ISR_SYNCOKF_Pos (0U) +#define CRS_ISR_SYNCOKF_Msk (0x1UL << CRS_ISR_SYNCOKF_Pos) /*!< 0x00000001 */ +#define CRS_ISR_SYNCOKF CRS_ISR_SYNCOKF_Msk /*!< SYNC event OK flag */ +#define CRS_ISR_SYNCWARNF_Pos (1U) +#define CRS_ISR_SYNCWARNF_Msk (0x1UL << CRS_ISR_SYNCWARNF_Pos) /*!< 0x00000002 */ +#define CRS_ISR_SYNCWARNF CRS_ISR_SYNCWARNF_Msk /*!< SYNC warning flag */ +#define CRS_ISR_ERRF_Pos (2U) +#define CRS_ISR_ERRF_Msk (0x1UL << CRS_ISR_ERRF_Pos) /*!< 0x00000004 */ +#define CRS_ISR_ERRF CRS_ISR_ERRF_Msk /*!< Error flag */ +#define CRS_ISR_ESYNCF_Pos (3U) +#define CRS_ISR_ESYNCF_Msk (0x1UL << CRS_ISR_ESYNCF_Pos) /*!< 0x00000008 */ +#define CRS_ISR_ESYNCF CRS_ISR_ESYNCF_Msk /*!< Expected SYNC flag */ +#define CRS_ISR_SYNCERR_Pos (8U) +#define CRS_ISR_SYNCERR_Msk (0x1UL << CRS_ISR_SYNCERR_Pos) /*!< 0x00000100 */ +#define CRS_ISR_SYNCERR CRS_ISR_SYNCERR_Msk /*!< SYNC error */ +#define CRS_ISR_SYNCMISS_Pos (9U) +#define CRS_ISR_SYNCMISS_Msk (0x1UL << CRS_ISR_SYNCMISS_Pos) /*!< 0x00000200 */ +#define CRS_ISR_SYNCMISS CRS_ISR_SYNCMISS_Msk /*!< SYNC missed */ +#define CRS_ISR_TRIMOVF_Pos (10U) +#define CRS_ISR_TRIMOVF_Msk (0x1UL << CRS_ISR_TRIMOVF_Pos) /*!< 0x00000400 */ +#define CRS_ISR_TRIMOVF CRS_ISR_TRIMOVF_Msk /*!< Trimming overflow or underflow */ +#define CRS_ISR_FEDIR_Pos (15U) +#define CRS_ISR_FEDIR_Msk (0x1UL << CRS_ISR_FEDIR_Pos) /*!< 0x00008000 */ +#define CRS_ISR_FEDIR CRS_ISR_FEDIR_Msk /*!< Frequency error direction */ +#define CRS_ISR_FECAP_Pos (16U) +#define CRS_ISR_FECAP_Msk (0xFFFFUL << CRS_ISR_FECAP_Pos) /*!< 0xFFFF0000 */ +#define CRS_ISR_FECAP CRS_ISR_FECAP_Msk /*!< Frequency error capture */ + +/******************* Bit definition for CRS_ICR register *********************/ +#define CRS_ICR_SYNCOKC_Pos (0U) +#define CRS_ICR_SYNCOKC_Msk (0x1UL << CRS_ICR_SYNCOKC_Pos) /*!< 0x00000001 */ +#define CRS_ICR_SYNCOKC CRS_ICR_SYNCOKC_Msk /*!< SYNC event OK clear flag */ +#define CRS_ICR_SYNCWARNC_Pos (1U) +#define CRS_ICR_SYNCWARNC_Msk (0x1UL << CRS_ICR_SYNCWARNC_Pos) /*!< 0x00000002 */ +#define CRS_ICR_SYNCWARNC CRS_ICR_SYNCWARNC_Msk /*!< SYNC warning clear flag */ +#define CRS_ICR_ERRC_Pos (2U) +#define CRS_ICR_ERRC_Msk (0x1UL << CRS_ICR_ERRC_Pos) /*!< 0x00000004 */ +#define CRS_ICR_ERRC CRS_ICR_ERRC_Msk /*!< Error clear flag */ +#define CRS_ICR_ESYNCC_Pos (3U) +#define CRS_ICR_ESYNCC_Msk (0x1UL << CRS_ICR_ESYNCC_Pos) /*!< 0x00000008 */ +#define CRS_ICR_ESYNCC CRS_ICR_ESYNCC_Msk /*!< Expected SYNC clear flag */ + +/******************************************************************************/ +/* */ +/* Digital to Analog Converter */ +/* */ +/******************************************************************************/ +/* + * @brief Specific device feature definitions (not present on all devices in the STM32G4 series) + */ +#define DAC_CHANNEL2_SUPPORT /*!< DAC feature available only on specific devices: DAC channel 2 available */ + +/******************** Bit definition for DAC_CR register ********************/ +#define DAC_CR_EN1_Pos (0U) +#define DAC_CR_EN1_Msk (0x1UL << DAC_CR_EN1_Pos) /*!< 0x00000001 */ +#define DAC_CR_EN1 DAC_CR_EN1_Msk /*!*/ +#define DAC_CR_CEN1_Pos (14U) +#define DAC_CR_CEN1_Msk (0x1UL << DAC_CR_CEN1_Pos) /*!< 0x00004000 */ +#define DAC_CR_CEN1 DAC_CR_CEN1_Msk /*!*/ + +#define DAC_CR_HFSEL_Pos (15U) +#define DAC_CR_HFSEL_Msk (0x1UL << DAC_CR_HFSEL_Pos) /*!< 0x00008000 */ +#define DAC_CR_HFSEL DAC_CR_HFSEL_Msk /*!*/ + +#define DAC_CR_EN2_Pos (16U) +#define DAC_CR_EN2_Msk (0x1UL << DAC_CR_EN2_Pos) /*!< 0x00010000 */ +#define DAC_CR_EN2 DAC_CR_EN2_Msk /*!*/ +#define DAC_CR_CEN2_Pos (30U) +#define DAC_CR_CEN2_Msk (0x1UL << DAC_CR_CEN2_Pos) /*!< 0x40000000 */ +#define DAC_CR_CEN2 DAC_CR_CEN2_Msk /*!*/ + +/***************** Bit definition for DAC_SWTRIGR register ******************/ +#define DAC_SWTRIGR_SWTRIG1_Pos (0U) +#define DAC_SWTRIGR_SWTRIG1_Msk (0x1UL << DAC_SWTRIGR_SWTRIG1_Pos) /*!< 0x00000001 */ +#define DAC_SWTRIGR_SWTRIG1 DAC_SWTRIGR_SWTRIG1_Msk /*! */ + +/******************* Bit definition for HRTIM_CPT2R register ****************/ +#define HRTIM_CPT2R_CPT2R_Pos (0U) +#define HRTIM_CPT2R_CPT2R_Msk (0x0000FFFFUL << HRTIM_CPT2R_CPT2R_Pos) /*!< 0x0000FFFF */ +#define HRTIM_CPT2R_CPT2R HRTIM_CPT2R_CPT2R_Msk /*!< Capture 2 Value */ +#define HRTIM_CPT2R_DIR_Pos (16U) +#define HRTIM_CPT2R_DIR_Msk (0x1UL << HRTIM_CPT2R_DIR_Pos) /*!< 0x00010000 */ +#define HRTIM_CPT2R_DIR HRTIM_CPT2R_DIR_Msk /*!< Capture 2 direction */ + +/******************** Bit definition for Slave Deadtime register **************/ +#define HRTIM_DTR_DTR_Pos (0U) +#define HRTIM_DTR_DTR_Msk (0x1FFUL << HRTIM_DTR_DTR_Pos) /*!< 0x000001FF */ +#define HRTIM_DTR_DTR HRTIM_DTR_DTR_Msk /*!< Dead time rising value */ +#define HRTIM_DTR_DTR_0 (0x001UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000001 */ +#define HRTIM_DTR_DTR_1 (0x002UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000002 */ +#define HRTIM_DTR_DTR_2 (0x004UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000004 */ +#define HRTIM_DTR_DTR_3 (0x008UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000008 */ +#define HRTIM_DTR_DTR_4 (0x010UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000010 */ +#define HRTIM_DTR_DTR_5 (0x020UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000020 */ +#define HRTIM_DTR_DTR_6 (0x040UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000040 */ +#define HRTIM_DTR_DTR_7 (0x080UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000080 */ +#define HRTIM_DTR_DTR_8 (0x100UL << HRTIM_DTR_DTR_Pos) /*!< 0x00000100 */ +#define HRTIM_DTR_SDTR_Pos (9U) +#define HRTIM_DTR_SDTR_Msk (0x1UL << HRTIM_DTR_SDTR_Pos) /*!< 0x00000200 */ +#define HRTIM_DTR_SDTR HRTIM_DTR_SDTR_Msk /*!< Sign dead time rising value */ +#define HRTIM_DTR_DTPRSC_Pos (10U) +#define HRTIM_DTR_DTPRSC_Msk (0x7UL << HRTIM_DTR_DTPRSC_Pos) /*!< 0x00001C00 */ +#define HRTIM_DTR_DTPRSC HRTIM_DTR_DTPRSC_Msk /*!< Dead time prescaler */ +#define HRTIM_DTR_DTPRSC_0 (0x1UL << HRTIM_DTR_DTPRSC_Pos) /*!< 0x00000400 */ +#define HRTIM_DTR_DTPRSC_1 (0x2UL << HRTIM_DTR_DTPRSC_Pos) /*!< 0x00000800 */ +#define HRTIM_DTR_DTPRSC_2 (0x4UL << HRTIM_DTR_DTPRSC_Pos) /*!< 0x00001000 */ +#define HRTIM_DTR_DTRSLK_Pos (14U) +#define HRTIM_DTR_DTRSLK_Msk (0x1UL << HRTIM_DTR_DTRSLK_Pos) /*!< 0x00004000 */ +#define HRTIM_DTR_DTRSLK HRTIM_DTR_DTRSLK_Msk /*!< Dead time rising sign lock */ +#define HRTIM_DTR_DTRLK_Pos (15U) +#define HRTIM_DTR_DTRLK_Msk (0x1UL << HRTIM_DTR_DTRLK_Pos) /*!< 0x00008000 */ +#define HRTIM_DTR_DTRLK HRTIM_DTR_DTRLK_Msk /*!< Dead time rising lock */ +#define HRTIM_DTR_DTF_Pos (16U) +#define HRTIM_DTR_DTF_Msk (0x1FFUL << HRTIM_DTR_DTF_Pos) /*!< 0x01FF0000 */ +#define HRTIM_DTR_DTF HRTIM_DTR_DTF_Msk /*!< Dead time falling value */ +#define HRTIM_DTR_DTF_0 (0x001UL << HRTIM_DTR_DTF_Pos) /*!< 0x00010000 */ +#define HRTIM_DTR_DTF_1 (0x002UL << HRTIM_DTR_DTF_Pos) /*!< 0x00020000 */ +#define HRTIM_DTR_DTF_2 (0x004UL << HRTIM_DTR_DTF_Pos) /*!< 0x00040000 */ +#define HRTIM_DTR_DTF_3 (0x008UL << HRTIM_DTR_DTF_Pos) /*!< 0x00080000 */ +#define HRTIM_DTR_DTF_4 (0x010UL << HRTIM_DTR_DTF_Pos) /*!< 0x00100000 */ +#define HRTIM_DTR_DTF_5 (0x020UL << HRTIM_DTR_DTF_Pos) /*!< 0x00200000 */ +#define HRTIM_DTR_DTF_6 (0x040UL << HRTIM_DTR_DTF_Pos) /*!< 0x00400000 */ +#define HRTIM_DTR_DTF_7 (0x080UL << HRTIM_DTR_DTF_Pos) /*!< 0x00800000 */ +#define HRTIM_DTR_DTF_8 (0x100UL << HRTIM_DTR_DTF_Pos) /*!< 0x01000000 */ +#define HRTIM_DTR_SDTF_Pos (25U) +#define HRTIM_DTR_SDTF_Msk (0x1UL << HRTIM_DTR_SDTF_Pos) /*!< 0x02000000 */ +#define HRTIM_DTR_SDTF HRTIM_DTR_SDTF_Msk /*!< Sign dead time falling value */ +#define HRTIM_DTR_DTFSLK_Pos (30U) +#define HRTIM_DTR_DTFSLK_Msk (0x1UL << HRTIM_DTR_DTFSLK_Pos) /*!< 0x40000000 */ +#define HRTIM_DTR_DTFSLK HRTIM_DTR_DTFSLK_Msk /*!< Dead time falling sign lock */ +#define HRTIM_DTR_DTFLK_Pos (31U) +#define HRTIM_DTR_DTFLK_Msk (0x1UL << HRTIM_DTR_DTFLK_Pos) /*!< 0x80000000 */ +#define HRTIM_DTR_DTFLK HRTIM_DTR_DTFLK_Msk /*!< Dead time falling lock */ + +/**** Bit definition for Slave Output 1 set register **************************/ +#define HRTIM_SET1R_SST_Pos (0U) +#define HRTIM_SET1R_SST_Msk (0x1UL << HRTIM_SET1R_SST_Pos) /*!< 0x00000001 */ +#define HRTIM_SET1R_SST HRTIM_SET1R_SST_Msk /*!< software set trigger */ +#define HRTIM_SET1R_RESYNC_Pos (1U) +#define HRTIM_SET1R_RESYNC_Msk (0x1UL << HRTIM_SET1R_RESYNC_Pos) /*!< 0x00000002 */ +#define HRTIM_SET1R_RESYNC HRTIM_SET1R_RESYNC_Msk /*!< Timer A resynchronization */ +#define HRTIM_SET1R_PER_Pos (2U) +#define HRTIM_SET1R_PER_Msk (0x1UL << HRTIM_SET1R_PER_Pos) /*!< 0x00000004 */ +#define HRTIM_SET1R_PER HRTIM_SET1R_PER_Msk /*!< Timer A period */ +#define HRTIM_SET1R_CMP1_Pos (3U) +#define HRTIM_SET1R_CMP1_Msk (0x1UL << HRTIM_SET1R_CMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_SET1R_CMP1 HRTIM_SET1R_CMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_SET1R_CMP2_Pos (4U) +#define HRTIM_SET1R_CMP2_Msk (0x1UL << HRTIM_SET1R_CMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_SET1R_CMP2 HRTIM_SET1R_CMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_SET1R_CMP3_Pos (5U) +#define HRTIM_SET1R_CMP3_Msk (0x1UL << HRTIM_SET1R_CMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_SET1R_CMP3 HRTIM_SET1R_CMP3_Msk /*!< Timer A compare 3 */ +#define HRTIM_SET1R_CMP4_Pos (6U) +#define HRTIM_SET1R_CMP4_Msk (0x1UL << HRTIM_SET1R_CMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_SET1R_CMP4 HRTIM_SET1R_CMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_SET1R_MSTPER_Pos (7U) +#define HRTIM_SET1R_MSTPER_Msk (0x1UL << HRTIM_SET1R_MSTPER_Pos) /*!< 0x00000080 */ +#define HRTIM_SET1R_MSTPER HRTIM_SET1R_MSTPER_Msk /*!< Master period */ +#define HRTIM_SET1R_MSTCMP1_Pos (8U) +#define HRTIM_SET1R_MSTCMP1_Msk (0x1UL << HRTIM_SET1R_MSTCMP1_Pos) /*!< 0x00000100 */ +#define HRTIM_SET1R_MSTCMP1 HRTIM_SET1R_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_SET1R_MSTCMP2_Pos (9U) +#define HRTIM_SET1R_MSTCMP2_Msk (0x1UL << HRTIM_SET1R_MSTCMP2_Pos) /*!< 0x00000200 */ +#define HRTIM_SET1R_MSTCMP2 HRTIM_SET1R_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_SET1R_MSTCMP3_Pos (10U) +#define HRTIM_SET1R_MSTCMP3_Msk (0x1UL << HRTIM_SET1R_MSTCMP3_Pos) /*!< 0x00000400 */ +#define HRTIM_SET1R_MSTCMP3 HRTIM_SET1R_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_SET1R_MSTCMP4_Pos (11U) +#define HRTIM_SET1R_MSTCMP4_Msk (0x1UL << HRTIM_SET1R_MSTCMP4_Pos) /*!< 0x00000800 */ +#define HRTIM_SET1R_MSTCMP4 HRTIM_SET1R_MSTCMP4_Msk /*!< Master compare 4 */ + +#define HRTIM_SET1R_TIMEVNT1_Pos (12U) +#define HRTIM_SET1R_TIMEVNT1_Msk (0x1UL << HRTIM_SET1R_TIMEVNT1_Pos) /*!< 0x00001000 */ +#define HRTIM_SET1R_TIMEVNT1 HRTIM_SET1R_TIMEVNT1_Msk /*!< Timer event 1 */ +#define HRTIM_SET1R_TIMEVNT2_Pos (13U) +#define HRTIM_SET1R_TIMEVNT2_Msk (0x1UL << HRTIM_SET1R_TIMEVNT2_Pos) /*!< 0x00002000 */ +#define HRTIM_SET1R_TIMEVNT2 HRTIM_SET1R_TIMEVNT2_Msk /*!< Timer event 2 */ +#define HRTIM_SET1R_TIMEVNT3_Pos (14U) +#define HRTIM_SET1R_TIMEVNT3_Msk (0x1UL << HRTIM_SET1R_TIMEVNT3_Pos) /*!< 0x00004000 */ +#define HRTIM_SET1R_TIMEVNT3 HRTIM_SET1R_TIMEVNT3_Msk /*!< Timer event 3 */ +#define HRTIM_SET1R_TIMEVNT4_Pos (15U) +#define HRTIM_SET1R_TIMEVNT4_Msk (0x1UL << HRTIM_SET1R_TIMEVNT4_Pos) /*!< 0x00008000 */ +#define HRTIM_SET1R_TIMEVNT4 HRTIM_SET1R_TIMEVNT4_Msk /*!< Timer event 4 */ +#define HRTIM_SET1R_TIMEVNT5_Pos (16U) +#define HRTIM_SET1R_TIMEVNT5_Msk (0x1UL << HRTIM_SET1R_TIMEVNT5_Pos) /*!< 0x00010000 */ +#define HRTIM_SET1R_TIMEVNT5 HRTIM_SET1R_TIMEVNT5_Msk /*!< Timer event 5 */ +#define HRTIM_SET1R_TIMEVNT6_Pos (17U) +#define HRTIM_SET1R_TIMEVNT6_Msk (0x1UL << HRTIM_SET1R_TIMEVNT6_Pos) /*!< 0x00020000 */ +#define HRTIM_SET1R_TIMEVNT6 HRTIM_SET1R_TIMEVNT6_Msk /*!< Timer event 6 */ +#define HRTIM_SET1R_TIMEVNT7_Pos (18U) +#define HRTIM_SET1R_TIMEVNT7_Msk (0x1UL << HRTIM_SET1R_TIMEVNT7_Pos) /*!< 0x00040000 */ +#define HRTIM_SET1R_TIMEVNT7 HRTIM_SET1R_TIMEVNT7_Msk /*!< Timer event 7 */ +#define HRTIM_SET1R_TIMEVNT8_Pos (19U) +#define HRTIM_SET1R_TIMEVNT8_Msk (0x1UL << HRTIM_SET1R_TIMEVNT8_Pos) /*!< 0x00080000 */ +#define HRTIM_SET1R_TIMEVNT8 HRTIM_SET1R_TIMEVNT8_Msk /*!< Timer event 8 */ +#define HRTIM_SET1R_TIMEVNT9_Pos (20U) +#define HRTIM_SET1R_TIMEVNT9_Msk (0x1UL << HRTIM_SET1R_TIMEVNT9_Pos) /*!< 0x00100000 */ +#define HRTIM_SET1R_TIMEVNT9 HRTIM_SET1R_TIMEVNT9_Msk /*!< Timer event 9 */ + +#define HRTIM_SET1R_EXTVNT1_Pos (21U) +#define HRTIM_SET1R_EXTVNT1_Msk (0x1UL << HRTIM_SET1R_EXTVNT1_Pos) /*!< 0x00200000 */ +#define HRTIM_SET1R_EXTVNT1 HRTIM_SET1R_EXTVNT1_Msk /*!< External event 1 */ +#define HRTIM_SET1R_EXTVNT2_Pos (22U) +#define HRTIM_SET1R_EXTVNT2_Msk (0x1UL << HRTIM_SET1R_EXTVNT2_Pos) /*!< 0x00400000 */ +#define HRTIM_SET1R_EXTVNT2 HRTIM_SET1R_EXTVNT2_Msk /*!< External event 2 */ +#define HRTIM_SET1R_EXTVNT3_Pos (23U) +#define HRTIM_SET1R_EXTVNT3_Msk (0x1UL << HRTIM_SET1R_EXTVNT3_Pos) /*!< 0x00800000 */ +#define HRTIM_SET1R_EXTVNT3 HRTIM_SET1R_EXTVNT3_Msk /*!< External event 3 */ +#define HRTIM_SET1R_EXTVNT4_Pos (24U) +#define HRTIM_SET1R_EXTVNT4_Msk (0x1UL << HRTIM_SET1R_EXTVNT4_Pos) /*!< 0x01000000 */ +#define HRTIM_SET1R_EXTVNT4 HRTIM_SET1R_EXTVNT4_Msk /*!< External event 4 */ +#define HRTIM_SET1R_EXTVNT5_Pos (25U) +#define HRTIM_SET1R_EXTVNT5_Msk (0x1UL << HRTIM_SET1R_EXTVNT5_Pos) /*!< 0x02000000 */ +#define HRTIM_SET1R_EXTVNT5 HRTIM_SET1R_EXTVNT5_Msk /*!< External event 5 */ +#define HRTIM_SET1R_EXTVNT6_Pos (26U) +#define HRTIM_SET1R_EXTVNT6_Msk (0x1UL << HRTIM_SET1R_EXTVNT6_Pos) /*!< 0x04000000 */ +#define HRTIM_SET1R_EXTVNT6 HRTIM_SET1R_EXTVNT6_Msk /*!< External event 6 */ +#define HRTIM_SET1R_EXTVNT7_Pos (27U) +#define HRTIM_SET1R_EXTVNT7_Msk (0x1UL << HRTIM_SET1R_EXTVNT7_Pos) /*!< 0x08000000 */ +#define HRTIM_SET1R_EXTVNT7 HRTIM_SET1R_EXTVNT7_Msk /*!< External event 7 */ +#define HRTIM_SET1R_EXTVNT8_Pos (28U) +#define HRTIM_SET1R_EXTVNT8_Msk (0x1UL << HRTIM_SET1R_EXTVNT8_Pos) /*!< 0x10000000 */ +#define HRTIM_SET1R_EXTVNT8 HRTIM_SET1R_EXTVNT8_Msk /*!< External event 8 */ +#define HRTIM_SET1R_EXTVNT9_Pos (29U) +#define HRTIM_SET1R_EXTVNT9_Msk (0x1UL << HRTIM_SET1R_EXTVNT9_Pos) /*!< 0x20000000 */ +#define HRTIM_SET1R_EXTVNT9 HRTIM_SET1R_EXTVNT9_Msk /*!< External event 9 */ +#define HRTIM_SET1R_EXTVNT10_Pos (30U) +#define HRTIM_SET1R_EXTVNT10_Msk (0x1UL << HRTIM_SET1R_EXTVNT10_Pos) /*!< 0x40000000 */ +#define HRTIM_SET1R_EXTVNT10 HRTIM_SET1R_EXTVNT10_Msk /*!< External event 10 */ + +#define HRTIM_SET1R_UPDATE_Pos (31U) +#define HRTIM_SET1R_UPDATE_Msk (0x1UL << HRTIM_SET1R_UPDATE_Pos) /*!< 0x80000000 */ +#define HRTIM_SET1R_UPDATE HRTIM_SET1R_UPDATE_Msk /*!< Register update (transfer preload to active) */ + +/**** Bit definition for Slave Output 1 reset register ************************/ +#define HRTIM_RST1R_SRT_Pos (0U) +#define HRTIM_RST1R_SRT_Msk (0x1UL << HRTIM_RST1R_SRT_Pos) /*!< 0x00000001 */ +#define HRTIM_RST1R_SRT HRTIM_RST1R_SRT_Msk /*!< software reset trigger */ +#define HRTIM_RST1R_RESYNC_Pos (1U) +#define HRTIM_RST1R_RESYNC_Msk (0x1UL << HRTIM_RST1R_RESYNC_Pos) /*!< 0x00000002 */ +#define HRTIM_RST1R_RESYNC HRTIM_RST1R_RESYNC_Msk /*!< Timer A resynchronization */ +#define HRTIM_RST1R_PER_Pos (2U) +#define HRTIM_RST1R_PER_Msk (0x1UL << HRTIM_RST1R_PER_Pos) /*!< 0x00000004 */ +#define HRTIM_RST1R_PER HRTIM_RST1R_PER_Msk /*!< Timer A period */ +#define HRTIM_RST1R_CMP1_Pos (3U) +#define HRTIM_RST1R_CMP1_Msk (0x1UL << HRTIM_RST1R_CMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_RST1R_CMP1 HRTIM_RST1R_CMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RST1R_CMP2_Pos (4U) +#define HRTIM_RST1R_CMP2_Msk (0x1UL << HRTIM_RST1R_CMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_RST1R_CMP2 HRTIM_RST1R_CMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RST1R_CMP3_Pos (5U) +#define HRTIM_RST1R_CMP3_Msk (0x1UL << HRTIM_RST1R_CMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_RST1R_CMP3 HRTIM_RST1R_CMP3_Msk /*!< Timer A compare 3 */ +#define HRTIM_RST1R_CMP4_Pos (6U) +#define HRTIM_RST1R_CMP4_Msk (0x1UL << HRTIM_RST1R_CMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_RST1R_CMP4 HRTIM_RST1R_CMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RST1R_MSTPER_Pos (7U) +#define HRTIM_RST1R_MSTPER_Msk (0x1UL << HRTIM_RST1R_MSTPER_Pos) /*!< 0x00000080 */ +#define HRTIM_RST1R_MSTPER HRTIM_RST1R_MSTPER_Msk /*!< Master period */ +#define HRTIM_RST1R_MSTCMP1_Pos (8U) +#define HRTIM_RST1R_MSTCMP1_Msk (0x1UL << HRTIM_RST1R_MSTCMP1_Pos) /*!< 0x00000100 */ +#define HRTIM_RST1R_MSTCMP1 HRTIM_RST1R_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_RST1R_MSTCMP2_Pos (9U) +#define HRTIM_RST1R_MSTCMP2_Msk (0x1UL << HRTIM_RST1R_MSTCMP2_Pos) /*!< 0x00000200 */ +#define HRTIM_RST1R_MSTCMP2 HRTIM_RST1R_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_RST1R_MSTCMP3_Pos (10U) +#define HRTIM_RST1R_MSTCMP3_Msk (0x1UL << HRTIM_RST1R_MSTCMP3_Pos) /*!< 0x00000400 */ +#define HRTIM_RST1R_MSTCMP3 HRTIM_RST1R_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_RST1R_MSTCMP4_Pos (11U) +#define HRTIM_RST1R_MSTCMP4_Msk (0x1UL << HRTIM_RST1R_MSTCMP4_Pos) /*!< 0x00000800 */ +#define HRTIM_RST1R_MSTCMP4 HRTIM_RST1R_MSTCMP4_Msk /*!< Master compare 4 */ + +#define HRTIM_RST1R_TIMEVNT1_Pos (12U) +#define HRTIM_RST1R_TIMEVNT1_Msk (0x1UL << HRTIM_RST1R_TIMEVNT1_Pos) /*!< 0x00001000 */ +#define HRTIM_RST1R_TIMEVNT1 HRTIM_RST1R_TIMEVNT1_Msk /*!< Timer event 1 */ +#define HRTIM_RST1R_TIMEVNT2_Pos (13U) +#define HRTIM_RST1R_TIMEVNT2_Msk (0x1UL << HRTIM_RST1R_TIMEVNT2_Pos) /*!< 0x00002000 */ +#define HRTIM_RST1R_TIMEVNT2 HRTIM_RST1R_TIMEVNT2_Msk /*!< Timer event 2 */ +#define HRTIM_RST1R_TIMEVNT3_Pos (14U) +#define HRTIM_RST1R_TIMEVNT3_Msk (0x1UL << HRTIM_RST1R_TIMEVNT3_Pos) /*!< 0x00004000 */ +#define HRTIM_RST1R_TIMEVNT3 HRTIM_RST1R_TIMEVNT3_Msk /*!< Timer event 3 */ +#define HRTIM_RST1R_TIMEVNT4_Pos (15U) +#define HRTIM_RST1R_TIMEVNT4_Msk (0x1UL << HRTIM_RST1R_TIMEVNT4_Pos) /*!< 0x00008000 */ +#define HRTIM_RST1R_TIMEVNT4 HRTIM_RST1R_TIMEVNT4_Msk /*!< Timer event 4 */ +#define HRTIM_RST1R_TIMEVNT5_Pos (16U) +#define HRTIM_RST1R_TIMEVNT5_Msk (0x1UL << HRTIM_RST1R_TIMEVNT5_Pos) /*!< 0x00010000 */ +#define HRTIM_RST1R_TIMEVNT5 HRTIM_RST1R_TIMEVNT5_Msk /*!< Timer event 5 */ +#define HRTIM_RST1R_TIMEVNT6_Pos (17U) +#define HRTIM_RST1R_TIMEVNT6_Msk (0x1UL << HRTIM_RST1R_TIMEVNT6_Pos) /*!< 0x00020000 */ +#define HRTIM_RST1R_TIMEVNT6 HRTIM_RST1R_TIMEVNT6_Msk /*!< Timer event 6 */ +#define HRTIM_RST1R_TIMEVNT7_Pos (18U) +#define HRTIM_RST1R_TIMEVNT7_Msk (0x1UL << HRTIM_RST1R_TIMEVNT7_Pos) /*!< 0x00040000 */ +#define HRTIM_RST1R_TIMEVNT7 HRTIM_RST1R_TIMEVNT7_Msk /*!< Timer event 7 */ +#define HRTIM_RST1R_TIMEVNT8_Pos (19U) +#define HRTIM_RST1R_TIMEVNT8_Msk (0x1UL << HRTIM_RST1R_TIMEVNT8_Pos) /*!< 0x00080000 */ +#define HRTIM_RST1R_TIMEVNT8 HRTIM_RST1R_TIMEVNT8_Msk /*!< Timer event 8 */ +#define HRTIM_RST1R_TIMEVNT9_Pos (20U) +#define HRTIM_RST1R_TIMEVNT9_Msk (0x1UL << HRTIM_RST1R_TIMEVNT9_Pos) /*!< 0x00100000 */ +#define HRTIM_RST1R_TIMEVNT9 HRTIM_RST1R_TIMEVNT9_Msk /*!< Timer event 9 */ + +#define HRTIM_RST1R_EXTVNT1_Pos (21U) +#define HRTIM_RST1R_EXTVNT1_Msk (0x1UL << HRTIM_RST1R_EXTVNT1_Pos) /*!< 0x00200000 */ +#define HRTIM_RST1R_EXTVNT1 HRTIM_RST1R_EXTVNT1_Msk /*!< External event 1 */ +#define HRTIM_RST1R_EXTVNT2_Pos (22U) +#define HRTIM_RST1R_EXTVNT2_Msk (0x1UL << HRTIM_RST1R_EXTVNT2_Pos) /*!< 0x00400000 */ +#define HRTIM_RST1R_EXTVNT2 HRTIM_RST1R_EXTVNT2_Msk /*!< External event 2 */ +#define HRTIM_RST1R_EXTVNT3_Pos (23U) +#define HRTIM_RST1R_EXTVNT3_Msk (0x1UL << HRTIM_RST1R_EXTVNT3_Pos) /*!< 0x00800000 */ +#define HRTIM_RST1R_EXTVNT3 HRTIM_RST1R_EXTVNT3_Msk /*!< External event 3 */ +#define HRTIM_RST1R_EXTVNT4_Pos (24U) +#define HRTIM_RST1R_EXTVNT4_Msk (0x1UL << HRTIM_RST1R_EXTVNT4_Pos) /*!< 0x01000000 */ +#define HRTIM_RST1R_EXTVNT4 HRTIM_RST1R_EXTVNT4_Msk /*!< External event 4 */ +#define HRTIM_RST1R_EXTVNT5_Pos (25U) +#define HRTIM_RST1R_EXTVNT5_Msk (0x1UL << HRTIM_RST1R_EXTVNT5_Pos) /*!< 0x02000000 */ +#define HRTIM_RST1R_EXTVNT5 HRTIM_RST1R_EXTVNT5_Msk /*!< External event 5 */ +#define HRTIM_RST1R_EXTVNT6_Pos (26U) +#define HRTIM_RST1R_EXTVNT6_Msk (0x1UL << HRTIM_RST1R_EXTVNT6_Pos) /*!< 0x04000000 */ +#define HRTIM_RST1R_EXTVNT6 HRTIM_RST1R_EXTVNT6_Msk /*!< External event 6 */ +#define HRTIM_RST1R_EXTVNT7_Pos (27U) +#define HRTIM_RST1R_EXTVNT7_Msk (0x1UL << HRTIM_RST1R_EXTVNT7_Pos) /*!< 0x08000000 */ +#define HRTIM_RST1R_EXTVNT7 HRTIM_RST1R_EXTVNT7_Msk /*!< External event 7 */ +#define HRTIM_RST1R_EXTVNT8_Pos (28U) +#define HRTIM_RST1R_EXTVNT8_Msk (0x1UL << HRTIM_RST1R_EXTVNT8_Pos) /*!< 0x10000000 */ +#define HRTIM_RST1R_EXTVNT8 HRTIM_RST1R_EXTVNT8_Msk /*!< External event 8 */ +#define HRTIM_RST1R_EXTVNT9_Pos (29U) +#define HRTIM_RST1R_EXTVNT9_Msk (0x1UL << HRTIM_RST1R_EXTVNT9_Pos) /*!< 0x20000000 */ +#define HRTIM_RST1R_EXTVNT9 HRTIM_RST1R_EXTVNT9_Msk /*!< External event 9 */ +#define HRTIM_RST1R_EXTVNT10_Pos (30U) +#define HRTIM_RST1R_EXTVNT10_Msk (0x1UL << HRTIM_RST1R_EXTVNT10_Pos) /*!< 0x40000000 */ +#define HRTIM_RST1R_EXTVNT10 HRTIM_RST1R_EXTVNT10_Msk /*!< External event 10 */ +#define HRTIM_RST1R_UPDATE_Pos (31U) +#define HRTIM_RST1R_UPDATE_Msk (0x1UL << HRTIM_RST1R_UPDATE_Pos) /*!< 0x80000000 */ +#define HRTIM_RST1R_UPDATE HRTIM_RST1R_UPDATE_Msk /*!< Register update (transfer preload to active) */ + +/**** Bit definition for Slave Output 2 set register **************************/ +#define HRTIM_SET2R_SST_Pos (0U) +#define HRTIM_SET2R_SST_Msk (0x1UL << HRTIM_SET2R_SST_Pos) /*!< 0x00000001 */ +#define HRTIM_SET2R_SST HRTIM_SET2R_SST_Msk /*!< software set trigger */ +#define HRTIM_SET2R_RESYNC_Pos (1U) +#define HRTIM_SET2R_RESYNC_Msk (0x1UL << HRTIM_SET2R_RESYNC_Pos) /*!< 0x00000002 */ +#define HRTIM_SET2R_RESYNC HRTIM_SET2R_RESYNC_Msk /*!< Timer A resynchronization */ +#define HRTIM_SET2R_PER_Pos (2U) +#define HRTIM_SET2R_PER_Msk (0x1UL << HRTIM_SET2R_PER_Pos) /*!< 0x00000004 */ +#define HRTIM_SET2R_PER HRTIM_SET2R_PER_Msk /*!< Timer A period */ +#define HRTIM_SET2R_CMP1_Pos (3U) +#define HRTIM_SET2R_CMP1_Msk (0x1UL << HRTIM_SET2R_CMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_SET2R_CMP1 HRTIM_SET2R_CMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_SET2R_CMP2_Pos (4U) +#define HRTIM_SET2R_CMP2_Msk (0x1UL << HRTIM_SET2R_CMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_SET2R_CMP2 HRTIM_SET2R_CMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_SET2R_CMP3_Pos (5U) +#define HRTIM_SET2R_CMP3_Msk (0x1UL << HRTIM_SET2R_CMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_SET2R_CMP3 HRTIM_SET2R_CMP3_Msk /*!< Timer A compare 3 */ +#define HRTIM_SET2R_CMP4_Pos (6U) +#define HRTIM_SET2R_CMP4_Msk (0x1UL << HRTIM_SET2R_CMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_SET2R_CMP4 HRTIM_SET2R_CMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_SET2R_MSTPER_Pos (7U) +#define HRTIM_SET2R_MSTPER_Msk (0x1UL << HRTIM_SET2R_MSTPER_Pos) /*!< 0x00000080 */ +#define HRTIM_SET2R_MSTPER HRTIM_SET2R_MSTPER_Msk /*!< Master period */ +#define HRTIM_SET2R_MSTCMP1_Pos (8U) +#define HRTIM_SET2R_MSTCMP1_Msk (0x1UL << HRTIM_SET2R_MSTCMP1_Pos) /*!< 0x00000100 */ +#define HRTIM_SET2R_MSTCMP1 HRTIM_SET2R_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_SET2R_MSTCMP2_Pos (9U) +#define HRTIM_SET2R_MSTCMP2_Msk (0x1UL << HRTIM_SET2R_MSTCMP2_Pos) /*!< 0x00000200 */ +#define HRTIM_SET2R_MSTCMP2 HRTIM_SET2R_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_SET2R_MSTCMP3_Pos (10U) +#define HRTIM_SET2R_MSTCMP3_Msk (0x1UL << HRTIM_SET2R_MSTCMP3_Pos) /*!< 0x00000400 */ +#define HRTIM_SET2R_MSTCMP3 HRTIM_SET2R_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_SET2R_MSTCMP4_Pos (11U) +#define HRTIM_SET2R_MSTCMP4_Msk (0x1UL << HRTIM_SET2R_MSTCMP4_Pos) /*!< 0x00000800 */ +#define HRTIM_SET2R_MSTCMP4 HRTIM_SET2R_MSTCMP4_Msk /*!< Master compare 4 */ + +#define HRTIM_SET2R_TIMEVNT1_Pos (12U) +#define HRTIM_SET2R_TIMEVNT1_Msk (0x1UL << HRTIM_SET2R_TIMEVNT1_Pos) /*!< 0x00001000 */ +#define HRTIM_SET2R_TIMEVNT1 HRTIM_SET2R_TIMEVNT1_Msk /*!< Timer event 1 */ +#define HRTIM_SET2R_TIMEVNT2_Pos (13U) +#define HRTIM_SET2R_TIMEVNT2_Msk (0x1UL << HRTIM_SET2R_TIMEVNT2_Pos) /*!< 0x00002000 */ +#define HRTIM_SET2R_TIMEVNT2 HRTIM_SET2R_TIMEVNT2_Msk /*!< Timer event 2 */ +#define HRTIM_SET2R_TIMEVNT3_Pos (14U) +#define HRTIM_SET2R_TIMEVNT3_Msk (0x1UL << HRTIM_SET2R_TIMEVNT3_Pos) /*!< 0x00004000 */ +#define HRTIM_SET2R_TIMEVNT3 HRTIM_SET2R_TIMEVNT3_Msk /*!< Timer event 3 */ +#define HRTIM_SET2R_TIMEVNT4_Pos (15U) +#define HRTIM_SET2R_TIMEVNT4_Msk (0x1UL << HRTIM_SET2R_TIMEVNT4_Pos) /*!< 0x00008000 */ +#define HRTIM_SET2R_TIMEVNT4 HRTIM_SET2R_TIMEVNT4_Msk /*!< Timer event 4 */ +#define HRTIM_SET2R_TIMEVNT5_Pos (16U) +#define HRTIM_SET2R_TIMEVNT5_Msk (0x1UL << HRTIM_SET2R_TIMEVNT5_Pos) /*!< 0x00010000 */ +#define HRTIM_SET2R_TIMEVNT5 HRTIM_SET2R_TIMEVNT5_Msk /*!< Timer event 5 */ +#define HRTIM_SET2R_TIMEVNT6_Pos (17U) +#define HRTIM_SET2R_TIMEVNT6_Msk (0x1UL << HRTIM_SET2R_TIMEVNT6_Pos) /*!< 0x00020000 */ +#define HRTIM_SET2R_TIMEVNT6 HRTIM_SET2R_TIMEVNT6_Msk /*!< Timer event 6 */ +#define HRTIM_SET2R_TIMEVNT7_Pos (18U) +#define HRTIM_SET2R_TIMEVNT7_Msk (0x1UL << HRTIM_SET2R_TIMEVNT7_Pos) /*!< 0x00040000 */ +#define HRTIM_SET2R_TIMEVNT7 HRTIM_SET2R_TIMEVNT7_Msk /*!< Timer event 7 */ +#define HRTIM_SET2R_TIMEVNT8_Pos (19U) +#define HRTIM_SET2R_TIMEVNT8_Msk (0x1UL << HRTIM_SET2R_TIMEVNT8_Pos) /*!< 0x00080000 */ +#define HRTIM_SET2R_TIMEVNT8 HRTIM_SET2R_TIMEVNT8_Msk /*!< Timer event 8 */ +#define HRTIM_SET2R_TIMEVNT9_Pos (20U) +#define HRTIM_SET2R_TIMEVNT9_Msk (0x1UL << HRTIM_SET2R_TIMEVNT9_Pos) /*!< 0x00100000 */ +#define HRTIM_SET2R_TIMEVNT9 HRTIM_SET2R_TIMEVNT9_Msk /*!< Timer event 9 */ + +#define HRTIM_SET2R_EXTVNT1_Pos (21U) +#define HRTIM_SET2R_EXTVNT1_Msk (0x1UL << HRTIM_SET2R_EXTVNT1_Pos) /*!< 0x00200000 */ +#define HRTIM_SET2R_EXTVNT1 HRTIM_SET2R_EXTVNT1_Msk /*!< External event 1 */ +#define HRTIM_SET2R_EXTVNT2_Pos (22U) +#define HRTIM_SET2R_EXTVNT2_Msk (0x1UL << HRTIM_SET2R_EXTVNT2_Pos) /*!< 0x00400000 */ +#define HRTIM_SET2R_EXTVNT2 HRTIM_SET2R_EXTVNT2_Msk /*!< External event 2 */ +#define HRTIM_SET2R_EXTVNT3_Pos (23U) +#define HRTIM_SET2R_EXTVNT3_Msk (0x1UL << HRTIM_SET2R_EXTVNT3_Pos) /*!< 0x00800000 */ +#define HRTIM_SET2R_EXTVNT3 HRTIM_SET2R_EXTVNT3_Msk /*!< External event 3 */ +#define HRTIM_SET2R_EXTVNT4_Pos (24U) +#define HRTIM_SET2R_EXTVNT4_Msk (0x1UL << HRTIM_SET2R_EXTVNT4_Pos) /*!< 0x01000000 */ +#define HRTIM_SET2R_EXTVNT4 HRTIM_SET2R_EXTVNT4_Msk /*!< External event 4 */ +#define HRTIM_SET2R_EXTVNT5_Pos (25U) +#define HRTIM_SET2R_EXTVNT5_Msk (0x1UL << HRTIM_SET2R_EXTVNT5_Pos) /*!< 0x02000000 */ +#define HRTIM_SET2R_EXTVNT5 HRTIM_SET2R_EXTVNT5_Msk /*!< External event 5 */ +#define HRTIM_SET2R_EXTVNT6_Pos (26U) +#define HRTIM_SET2R_EXTVNT6_Msk (0x1UL << HRTIM_SET2R_EXTVNT6_Pos) /*!< 0x04000000 */ +#define HRTIM_SET2R_EXTVNT6 HRTIM_SET2R_EXTVNT6_Msk /*!< External event 6 */ +#define HRTIM_SET2R_EXTVNT7_Pos (27U) +#define HRTIM_SET2R_EXTVNT7_Msk (0x1UL << HRTIM_SET2R_EXTVNT7_Pos) /*!< 0x08000000 */ +#define HRTIM_SET2R_EXTVNT7 HRTIM_SET2R_EXTVNT7_Msk /*!< External event 7 */ +#define HRTIM_SET2R_EXTVNT8_Pos (28U) +#define HRTIM_SET2R_EXTVNT8_Msk (0x1UL << HRTIM_SET2R_EXTVNT8_Pos) /*!< 0x10000000 */ +#define HRTIM_SET2R_EXTVNT8 HRTIM_SET2R_EXTVNT8_Msk /*!< External event 8 */ +#define HRTIM_SET2R_EXTVNT9_Pos (29U) +#define HRTIM_SET2R_EXTVNT9_Msk (0x1UL << HRTIM_SET2R_EXTVNT9_Pos) /*!< 0x20000000 */ +#define HRTIM_SET2R_EXTVNT9 HRTIM_SET2R_EXTVNT9_Msk /*!< External event 9 */ +#define HRTIM_SET2R_EXTVNT10_Pos (30U) +#define HRTIM_SET2R_EXTVNT10_Msk (0x1UL << HRTIM_SET2R_EXTVNT10_Pos) /*!< 0x40000000 */ +#define HRTIM_SET2R_EXTVNT10 HRTIM_SET2R_EXTVNT10_Msk /*!< External event 10 */ + +#define HRTIM_SET2R_UPDATE_Pos (31U) +#define HRTIM_SET2R_UPDATE_Msk (0x1UL << HRTIM_SET2R_UPDATE_Pos) /*!< 0x80000000 */ +#define HRTIM_SET2R_UPDATE HRTIM_SET2R_UPDATE_Msk /*!< Register update (transfer preload to active) */ + +/**** Bit definition for Slave Output 2 reset register ************************/ +#define HRTIM_RST2R_SRT_Pos (0U) +#define HRTIM_RST2R_SRT_Msk (0x1UL << HRTIM_RST2R_SRT_Pos) /*!< 0x00000001 */ +#define HRTIM_RST2R_SRT HRTIM_RST2R_SRT_Msk /*!< software reset trigger */ +#define HRTIM_RST2R_RESYNC_Pos (1U) +#define HRTIM_RST2R_RESYNC_Msk (0x1UL << HRTIM_RST2R_RESYNC_Pos) /*!< 0x00000002 */ +#define HRTIM_RST2R_RESYNC HRTIM_RST2R_RESYNC_Msk /*!< Timer A resynchronization */ +#define HRTIM_RST2R_PER_Pos (2U) +#define HRTIM_RST2R_PER_Msk (0x1UL << HRTIM_RST2R_PER_Pos) /*!< 0x00000004 */ +#define HRTIM_RST2R_PER HRTIM_RST2R_PER_Msk /*!< Timer A period */ +#define HRTIM_RST2R_CMP1_Pos (3U) +#define HRTIM_RST2R_CMP1_Msk (0x1UL << HRTIM_RST2R_CMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_RST2R_CMP1 HRTIM_RST2R_CMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RST2R_CMP2_Pos (4U) +#define HRTIM_RST2R_CMP2_Msk (0x1UL << HRTIM_RST2R_CMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_RST2R_CMP2 HRTIM_RST2R_CMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RST2R_CMP3_Pos (5U) +#define HRTIM_RST2R_CMP3_Msk (0x1UL << HRTIM_RST2R_CMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_RST2R_CMP3 HRTIM_RST2R_CMP3_Msk /*!< Timer A compare 3 */ +#define HRTIM_RST2R_CMP4_Pos (6U) +#define HRTIM_RST2R_CMP4_Msk (0x1UL << HRTIM_RST2R_CMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_RST2R_CMP4 HRTIM_RST2R_CMP4_Msk /*!< Timer A compare 4 */ +#define HRTIM_RST2R_MSTPER_Pos (7U) +#define HRTIM_RST2R_MSTPER_Msk (0x1UL << HRTIM_RST2R_MSTPER_Pos) /*!< 0x00000080 */ +#define HRTIM_RST2R_MSTPER HRTIM_RST2R_MSTPER_Msk /*!< Master period */ +#define HRTIM_RST2R_MSTCMP1_Pos (8U) +#define HRTIM_RST2R_MSTCMP1_Msk (0x1UL << HRTIM_RST2R_MSTCMP1_Pos) /*!< 0x00000100 */ +#define HRTIM_RST2R_MSTCMP1 HRTIM_RST2R_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_RST2R_MSTCMP2_Pos (9U) +#define HRTIM_RST2R_MSTCMP2_Msk (0x1UL << HRTIM_RST2R_MSTCMP2_Pos) /*!< 0x00000200 */ +#define HRTIM_RST2R_MSTCMP2 HRTIM_RST2R_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_RST2R_MSTCMP3_Pos (10U) +#define HRTIM_RST2R_MSTCMP3_Msk (0x1UL << HRTIM_RST2R_MSTCMP3_Pos) /*!< 0x00000400 */ +#define HRTIM_RST2R_MSTCMP3 HRTIM_RST2R_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_RST2R_MSTCMP4_Pos (11U) +#define HRTIM_RST2R_MSTCMP4_Msk (0x1UL << HRTIM_RST2R_MSTCMP4_Pos) /*!< 0x00000800 */ +#define HRTIM_RST2R_MSTCMP4 HRTIM_RST2R_MSTCMP4_Msk /*!< Master compare 4 */ + +#define HRTIM_RST2R_TIMEVNT1_Pos (12U) +#define HRTIM_RST2R_TIMEVNT1_Msk (0x1UL << HRTIM_RST2R_TIMEVNT1_Pos) /*!< 0x00001000 */ +#define HRTIM_RST2R_TIMEVNT1 HRTIM_RST2R_TIMEVNT1_Msk /*!< Timer event 1 */ +#define HRTIM_RST2R_TIMEVNT2_Pos (13U) +#define HRTIM_RST2R_TIMEVNT2_Msk (0x1UL << HRTIM_RST2R_TIMEVNT2_Pos) /*!< 0x00002000 */ +#define HRTIM_RST2R_TIMEVNT2 HRTIM_RST2R_TIMEVNT2_Msk /*!< Timer event 2 */ +#define HRTIM_RST2R_TIMEVNT3_Pos (14U) +#define HRTIM_RST2R_TIMEVNT3_Msk (0x1UL << HRTIM_RST2R_TIMEVNT3_Pos) /*!< 0x00004000 */ +#define HRTIM_RST2R_TIMEVNT3 HRTIM_RST2R_TIMEVNT3_Msk /*!< Timer event 3 */ +#define HRTIM_RST2R_TIMEVNT4_Pos (15U) +#define HRTIM_RST2R_TIMEVNT4_Msk (0x1UL << HRTIM_RST2R_TIMEVNT4_Pos) /*!< 0x00008000 */ +#define HRTIM_RST2R_TIMEVNT4 HRTIM_RST2R_TIMEVNT4_Msk /*!< Timer event 4 */ +#define HRTIM_RST2R_TIMEVNT5_Pos (16U) +#define HRTIM_RST2R_TIMEVNT5_Msk (0x1UL << HRTIM_RST2R_TIMEVNT5_Pos) /*!< 0x00010000 */ +#define HRTIM_RST2R_TIMEVNT5 HRTIM_RST2R_TIMEVNT5_Msk /*!< Timer event 5 */ +#define HRTIM_RST2R_TIMEVNT6_Pos (17U) +#define HRTIM_RST2R_TIMEVNT6_Msk (0x1UL << HRTIM_RST2R_TIMEVNT6_Pos) /*!< 0x00020000 */ +#define HRTIM_RST2R_TIMEVNT6 HRTIM_RST2R_TIMEVNT6_Msk /*!< Timer event 6 */ +#define HRTIM_RST2R_TIMEVNT7_Pos (18U) +#define HRTIM_RST2R_TIMEVNT7_Msk (0x1UL << HRTIM_RST2R_TIMEVNT7_Pos) /*!< 0x00040000 */ +#define HRTIM_RST2R_TIMEVNT7 HRTIM_RST2R_TIMEVNT7_Msk /*!< Timer event 7 */ +#define HRTIM_RST2R_TIMEVNT8_Pos (19U) +#define HRTIM_RST2R_TIMEVNT8_Msk (0x1UL << HRTIM_RST2R_TIMEVNT8_Pos) /*!< 0x00080000 */ +#define HRTIM_RST2R_TIMEVNT8 HRTIM_RST2R_TIMEVNT8_Msk /*!< Timer event 8 */ +#define HRTIM_RST2R_TIMEVNT9_Pos (20U) +#define HRTIM_RST2R_TIMEVNT9_Msk (0x1UL << HRTIM_RST2R_TIMEVNT9_Pos) /*!< 0x00100000 */ +#define HRTIM_RST2R_TIMEVNT9 HRTIM_RST2R_TIMEVNT9_Msk /*!< Timer event 9 */ + +#define HRTIM_RST2R_EXTVNT1_Pos (21U) +#define HRTIM_RST2R_EXTVNT1_Msk (0x1UL << HRTIM_RST2R_EXTVNT1_Pos) /*!< 0x00200000 */ +#define HRTIM_RST2R_EXTVNT1 HRTIM_RST2R_EXTVNT1_Msk /*!< External event 1 */ +#define HRTIM_RST2R_EXTVNT2_Pos (22U) +#define HRTIM_RST2R_EXTVNT2_Msk (0x1UL << HRTIM_RST2R_EXTVNT2_Pos) /*!< 0x00400000 */ +#define HRTIM_RST2R_EXTVNT2 HRTIM_RST2R_EXTVNT2_Msk /*!< External event 2 */ +#define HRTIM_RST2R_EXTVNT3_Pos (23U) +#define HRTIM_RST2R_EXTVNT3_Msk (0x1UL << HRTIM_RST2R_EXTVNT3_Pos) /*!< 0x00800000 */ +#define HRTIM_RST2R_EXTVNT3 HRTIM_RST2R_EXTVNT3_Msk /*!< External event 3 */ +#define HRTIM_RST2R_EXTVNT4_Pos (24U) +#define HRTIM_RST2R_EXTVNT4_Msk (0x1UL << HRTIM_RST2R_EXTVNT4_Pos) /*!< 0x01000000 */ +#define HRTIM_RST2R_EXTVNT4 HRTIM_RST2R_EXTVNT4_Msk /*!< External event 4 */ +#define HRTIM_RST2R_EXTVNT5_Pos (25U) +#define HRTIM_RST2R_EXTVNT5_Msk (0x1UL << HRTIM_RST2R_EXTVNT5_Pos) /*!< 0x02000000 */ +#define HRTIM_RST2R_EXTVNT5 HRTIM_RST2R_EXTVNT5_Msk /*!< External event 5 */ +#define HRTIM_RST2R_EXTVNT6_Pos (26U) +#define HRTIM_RST2R_EXTVNT6_Msk (0x1UL << HRTIM_RST2R_EXTVNT6_Pos) /*!< 0x04000000 */ +#define HRTIM_RST2R_EXTVNT6 HRTIM_RST2R_EXTVNT6_Msk /*!< External event 6 */ +#define HRTIM_RST2R_EXTVNT7_Pos (27U) +#define HRTIM_RST2R_EXTVNT7_Msk (0x1UL << HRTIM_RST2R_EXTVNT7_Pos) /*!< 0x08000000 */ +#define HRTIM_RST2R_EXTVNT7 HRTIM_RST2R_EXTVNT7_Msk /*!< External event 7 */ +#define HRTIM_RST2R_EXTVNT8_Pos (28U) +#define HRTIM_RST2R_EXTVNT8_Msk (0x1UL << HRTIM_RST2R_EXTVNT8_Pos) /*!< 0x10000000 */ +#define HRTIM_RST2R_EXTVNT8 HRTIM_RST2R_EXTVNT8_Msk /*!< External event 8 */ +#define HRTIM_RST2R_EXTVNT9_Pos (29U) +#define HRTIM_RST2R_EXTVNT9_Msk (0x1UL << HRTIM_RST2R_EXTVNT9_Pos) /*!< 0x20000000 */ +#define HRTIM_RST2R_EXTVNT9 HRTIM_RST2R_EXTVNT9_Msk /*!< External event 9 */ +#define HRTIM_RST2R_EXTVNT10_Pos (30U) +#define HRTIM_RST2R_EXTVNT10_Msk (0x1UL << HRTIM_RST2R_EXTVNT10_Pos) /*!< 0x40000000 */ +#define HRTIM_RST2R_EXTVNT10 HRTIM_RST2R_EXTVNT10_Msk /*!< External event 10 */ +#define HRTIM_RST2R_UPDATE_Pos (31U) +#define HRTIM_RST2R_UPDATE_Msk (0x1UL << HRTIM_RST2R_UPDATE_Pos) /*!< 0x80000000 */ +#define HRTIM_RST2R_UPDATE HRTIM_RST2R_UPDATE_Msk /*!< Register update (transfer preload to active) */ + +/**** Bit definition for Slave external event filtering register 1 ***********/ +#define HRTIM_EEFR1_EE1LTCH_Pos (0U) +#define HRTIM_EEFR1_EE1LTCH_Msk (0x1UL << HRTIM_EEFR1_EE1LTCH_Pos) /*!< 0x00000001 */ +#define HRTIM_EEFR1_EE1LTCH HRTIM_EEFR1_EE1LTCH_Msk /*!< External Event 1 latch */ +#define HRTIM_EEFR1_EE1FLTR_Pos (1U) +#define HRTIM_EEFR1_EE1FLTR_Msk (0xFUL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x0000001E */ +#define HRTIM_EEFR1_EE1FLTR HRTIM_EEFR1_EE1FLTR_Msk /*!< External Event 1 filter mask */ +#define HRTIM_EEFR1_EE1FLTR_0 (0x1UL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x00000002 */ +#define HRTIM_EEFR1_EE1FLTR_1 (0x2UL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x00000004 */ +#define HRTIM_EEFR1_EE1FLTR_2 (0x4UL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x00000008 */ +#define HRTIM_EEFR1_EE1FLTR_3 (0x8UL << HRTIM_EEFR1_EE1FLTR_Pos) /*!< 0x00000010 */ + +#define HRTIM_EEFR1_EE2LTCH_Pos (6U) +#define HRTIM_EEFR1_EE2LTCH_Msk (0x1UL << HRTIM_EEFR1_EE2LTCH_Pos) /*!< 0x00000040 */ +#define HRTIM_EEFR1_EE2LTCH HRTIM_EEFR1_EE2LTCH_Msk /*!< External Event 2 latch */ +#define HRTIM_EEFR1_EE2FLTR_Pos (7U) +#define HRTIM_EEFR1_EE2FLTR_Msk (0xFUL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000780 */ +#define HRTIM_EEFR1_EE2FLTR HRTIM_EEFR1_EE2FLTR_Msk /*!< External Event 2 filter mask */ +#define HRTIM_EEFR1_EE2FLTR_0 (0x1UL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000080 */ +#define HRTIM_EEFR1_EE2FLTR_1 (0x2UL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000100 */ +#define HRTIM_EEFR1_EE2FLTR_2 (0x4UL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000200 */ +#define HRTIM_EEFR1_EE2FLTR_3 (0x8UL << HRTIM_EEFR1_EE2FLTR_Pos) /*!< 0x00000400 */ + +#define HRTIM_EEFR1_EE3LTCH_Pos (12U) +#define HRTIM_EEFR1_EE3LTCH_Msk (0x1UL << HRTIM_EEFR1_EE3LTCH_Pos) /*!< 0x00001000 */ +#define HRTIM_EEFR1_EE3LTCH HRTIM_EEFR1_EE3LTCH_Msk /*!< External Event 3 latch */ +#define HRTIM_EEFR1_EE3FLTR_Pos (13U) +#define HRTIM_EEFR1_EE3FLTR_Msk (0xFUL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x0001E000 */ +#define HRTIM_EEFR1_EE3FLTR HRTIM_EEFR1_EE3FLTR_Msk /*!< External Event 3 filter mask */ +#define HRTIM_EEFR1_EE3FLTR_0 (0x1UL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x00002000 */ +#define HRTIM_EEFR1_EE3FLTR_1 (0x2UL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x00004000 */ +#define HRTIM_EEFR1_EE3FLTR_2 (0x4UL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x00008000 */ +#define HRTIM_EEFR1_EE3FLTR_3 (0x8UL << HRTIM_EEFR1_EE3FLTR_Pos) /*!< 0x00010000 */ + +#define HRTIM_EEFR1_EE4LTCH_Pos (18U) +#define HRTIM_EEFR1_EE4LTCH_Msk (0x1UL << HRTIM_EEFR1_EE4LTCH_Pos) /*!< 0x00040000 */ +#define HRTIM_EEFR1_EE4LTCH HRTIM_EEFR1_EE4LTCH_Msk /*!< External Event 4 latch */ +#define HRTIM_EEFR1_EE4FLTR_Pos (19U) +#define HRTIM_EEFR1_EE4FLTR_Msk (0xFUL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00780000 */ +#define HRTIM_EEFR1_EE4FLTR HRTIM_EEFR1_EE4FLTR_Msk /*!< External Event 4 filter mask */ +#define HRTIM_EEFR1_EE4FLTR_0 (0x1UL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00080000 */ +#define HRTIM_EEFR1_EE4FLTR_1 (0x2UL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00100000 */ +#define HRTIM_EEFR1_EE4FLTR_2 (0x4UL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00200000 */ +#define HRTIM_EEFR1_EE4FLTR_3 (0x8UL << HRTIM_EEFR1_EE4FLTR_Pos) /*!< 0x00400000 */ + +#define HRTIM_EEFR1_EE5LTCH_Pos (24U) +#define HRTIM_EEFR1_EE5LTCH_Msk (0x1UL << HRTIM_EEFR1_EE5LTCH_Pos) /*!< 0x01000000 */ +#define HRTIM_EEFR1_EE5LTCH HRTIM_EEFR1_EE5LTCH_Msk /*!< External Event 5 latch */ +#define HRTIM_EEFR1_EE5FLTR_Pos (25U) +#define HRTIM_EEFR1_EE5FLTR_Msk (0xFUL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x1E000000 */ +#define HRTIM_EEFR1_EE5FLTR HRTIM_EEFR1_EE5FLTR_Msk /*!< External Event 5 filter mask */ +#define HRTIM_EEFR1_EE5FLTR_0 (0x1UL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x02000000 */ +#define HRTIM_EEFR1_EE5FLTR_1 (0x2UL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x04000000 */ +#define HRTIM_EEFR1_EE5FLTR_2 (0x4UL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x08000000 */ +#define HRTIM_EEFR1_EE5FLTR_3 (0x8UL << HRTIM_EEFR1_EE5FLTR_Pos) /*!< 0x10000000 */ + +/**** Bit definition for Slave external event filtering register 2 ***********/ +#define HRTIM_EEFR2_EE6LTCH_Pos (0U) +#define HRTIM_EEFR2_EE6LTCH_Msk (0x1UL << HRTIM_EEFR2_EE6LTCH_Pos) /*!< 0x00000001 */ +#define HRTIM_EEFR2_EE6LTCH HRTIM_EEFR2_EE6LTCH_Msk /*!< External Event 6 latch */ +#define HRTIM_EEFR2_EE6FLTR_Pos (1U) +#define HRTIM_EEFR2_EE6FLTR_Msk (0xFUL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x0000001E */ +#define HRTIM_EEFR2_EE6FLTR HRTIM_EEFR2_EE6FLTR_Msk /*!< External Event 6 filter mask */ +#define HRTIM_EEFR2_EE6FLTR_0 (0x1UL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x00000002 */ +#define HRTIM_EEFR2_EE6FLTR_1 (0x2UL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x00000004 */ +#define HRTIM_EEFR2_EE6FLTR_2 (0x4UL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x00000008 */ +#define HRTIM_EEFR2_EE6FLTR_3 (0x8UL << HRTIM_EEFR2_EE6FLTR_Pos) /*!< 0x00000010 */ + +#define HRTIM_EEFR2_EE7LTCH_Pos (6U) +#define HRTIM_EEFR2_EE7LTCH_Msk (0x1UL << HRTIM_EEFR2_EE7LTCH_Pos) /*!< 0x00000040 */ +#define HRTIM_EEFR2_EE7LTCH HRTIM_EEFR2_EE7LTCH_Msk /*!< External Event 7 latch */ +#define HRTIM_EEFR2_EE7FLTR_Pos (7U) +#define HRTIM_EEFR2_EE7FLTR_Msk (0xFUL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000780 */ +#define HRTIM_EEFR2_EE7FLTR HRTIM_EEFR2_EE7FLTR_Msk /*!< External Event 7 filter mask */ +#define HRTIM_EEFR2_EE7FLTR_0 (0x1UL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000080 */ +#define HRTIM_EEFR2_EE7FLTR_1 (0x2UL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000100 */ +#define HRTIM_EEFR2_EE7FLTR_2 (0x4UL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000200 */ +#define HRTIM_EEFR2_EE7FLTR_3 (0x8UL << HRTIM_EEFR2_EE7FLTR_Pos) /*!< 0x00000400 */ + +#define HRTIM_EEFR2_EE8LTCH_Pos (12U) +#define HRTIM_EEFR2_EE8LTCH_Msk (0x1UL << HRTIM_EEFR2_EE8LTCH_Pos) /*!< 0x00001000 */ +#define HRTIM_EEFR2_EE8LTCH HRTIM_EEFR2_EE8LTCH_Msk /*!< External Event 8 latch */ +#define HRTIM_EEFR2_EE8FLTR_Pos (13U) +#define HRTIM_EEFR2_EE8FLTR_Msk (0xFUL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x0001E000 */ +#define HRTIM_EEFR2_EE8FLTR HRTIM_EEFR2_EE8FLTR_Msk /*!< External Event 8 filter mask */ +#define HRTIM_EEFR2_EE8FLTR_0 (0x1UL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x00002000 */ +#define HRTIM_EEFR2_EE8FLTR_1 (0x2UL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x00004000 */ +#define HRTIM_EEFR2_EE8FLTR_2 (0x4UL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x00008000 */ +#define HRTIM_EEFR2_EE8FLTR_3 (0x8UL << HRTIM_EEFR2_EE8FLTR_Pos) /*!< 0x00010000 */ + +#define HRTIM_EEFR2_EE9LTCH_Pos (18U) +#define HRTIM_EEFR2_EE9LTCH_Msk (0x1UL << HRTIM_EEFR2_EE9LTCH_Pos) /*!< 0x00040000 */ +#define HRTIM_EEFR2_EE9LTCH HRTIM_EEFR2_EE9LTCH_Msk /*!< External Event 9 latch */ +#define HRTIM_EEFR2_EE9FLTR_Pos (19U) +#define HRTIM_EEFR2_EE9FLTR_Msk (0xFUL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00780000 */ +#define HRTIM_EEFR2_EE9FLTR HRTIM_EEFR2_EE9FLTR_Msk /*!< External Event 9 filter mask */ +#define HRTIM_EEFR2_EE9FLTR_0 (0x1UL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00080000 */ +#define HRTIM_EEFR2_EE9FLTR_1 (0x2UL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00100000 */ +#define HRTIM_EEFR2_EE9FLTR_2 (0x4UL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00200000 */ +#define HRTIM_EEFR2_EE9FLTR_3 (0x8UL << HRTIM_EEFR2_EE9FLTR_Pos) /*!< 0x00400000 */ + +#define HRTIM_EEFR2_EE10LTCH_Pos (24U) +#define HRTIM_EEFR2_EE10LTCH_Msk (0x1UL << HRTIM_EEFR2_EE10LTCH_Pos) /*!< 0x01000000 */ +#define HRTIM_EEFR2_EE10LTCH HRTIM_EEFR2_EE10LTCH_Msk /*!< External Event 10 latch */ +#define HRTIM_EEFR2_EE10FLTR_Pos (25U) +#define HRTIM_EEFR2_EE10FLTR_Msk (0xFUL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x1E000000 */ +#define HRTIM_EEFR2_EE10FLTR HRTIM_EEFR2_EE10FLTR_Msk /*!< External Event 10 filter mask */ +#define HRTIM_EEFR2_EE10FLTR_0 (0x1UL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x02000000 */ +#define HRTIM_EEFR2_EE10FLTR_1 (0x2UL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x04000000 */ +#define HRTIM_EEFR2_EE10FLTR_2 (0x4UL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x08000000 */ +#define HRTIM_EEFR2_EE10FLTR_3 (0x8UL << HRTIM_EEFR2_EE10FLTR_Pos) /*!< 0x10000000 */ + +/**** Bit definition for Slave Timer reset register ***************************/ + +#define HRTIM_RSTR_TIMFCMP1_Pos (0U) +#define HRTIM_RSTR_TIMFCMP1_Msk (0x1UL << HRTIM_RSTR_TIMFCMP1_Pos) /*!< 0x00000001 */ +#define HRTIM_RSTR_TIMFCMP1 HRTIM_RSTR_TIMFCMP1_Msk /*!< Timer F compare 1 */ +#define HRTIM_RSTR_UPDATE_Pos (1U) +#define HRTIM_RSTR_UPDATE_Msk (0x1UL << HRTIM_RSTR_UPDATE_Pos) /*!< 0x00000002 */ +#define HRTIM_RSTR_UPDATE HRTIM_RSTR_UPDATE_Msk /*!< Timer update */ +#define HRTIM_RSTR_CMP2_Pos (2U) +#define HRTIM_RSTR_CMP2_Msk (0x1UL << HRTIM_RSTR_CMP2_Pos) /*!< 0x00000004 */ +#define HRTIM_RSTR_CMP2 HRTIM_RSTR_CMP2_Msk /*!< Timer compare2 */ +#define HRTIM_RSTR_CMP4_Pos (3U) +#define HRTIM_RSTR_CMP4_Msk (0x1UL << HRTIM_RSTR_CMP4_Pos) /*!< 0x00000008 */ +#define HRTIM_RSTR_CMP4 HRTIM_RSTR_CMP4_Msk /*!< Timer compare4 */ +#define HRTIM_RSTR_MSTPER_Pos (4U) +#define HRTIM_RSTR_MSTPER_Msk (0x1UL << HRTIM_RSTR_MSTPER_Pos) /*!< 0x00000010 */ +#define HRTIM_RSTR_MSTPER HRTIM_RSTR_MSTPER_Msk /*!< Master period */ +#define HRTIM_RSTR_MSTCMP1_Pos (5U) +#define HRTIM_RSTR_MSTCMP1_Msk (0x1UL << HRTIM_RSTR_MSTCMP1_Pos) /*!< 0x00000020 */ +#define HRTIM_RSTR_MSTCMP1 HRTIM_RSTR_MSTCMP1_Msk /*!< Master compare1 */ +#define HRTIM_RSTR_MSTCMP2_Pos (6U) +#define HRTIM_RSTR_MSTCMP2_Msk (0x1UL << HRTIM_RSTR_MSTCMP2_Pos) /*!< 0x00000040 */ +#define HRTIM_RSTR_MSTCMP2 HRTIM_RSTR_MSTCMP2_Msk /*!< Master compare2 */ +#define HRTIM_RSTR_MSTCMP3_Pos (7U) +#define HRTIM_RSTR_MSTCMP3_Msk (0x1UL << HRTIM_RSTR_MSTCMP3_Pos) /*!< 0x00000080 */ +#define HRTIM_RSTR_MSTCMP3 HRTIM_RSTR_MSTCMP3_Msk /*!< Master compare3 */ +#define HRTIM_RSTR_MSTCMP4_Pos (8U) +#define HRTIM_RSTR_MSTCMP4_Msk (0x1UL << HRTIM_RSTR_MSTCMP4_Pos) /*!< 0x00000100 */ +#define HRTIM_RSTR_MSTCMP4 HRTIM_RSTR_MSTCMP4_Msk /*!< Master compare4 */ +#define HRTIM_RSTR_EXTEVNT1_Pos (9U) +#define HRTIM_RSTR_EXTEVNT1_Msk (0x1UL << HRTIM_RSTR_EXTEVNT1_Pos) /*!< 0x00000200 */ +#define HRTIM_RSTR_EXTEVNT1 HRTIM_RSTR_EXTEVNT1_Msk /*!< External event 1 */ +#define HRTIM_RSTR_EXTEVNT2_Pos (10U) +#define HRTIM_RSTR_EXTEVNT2_Msk (0x1UL << HRTIM_RSTR_EXTEVNT2_Pos) /*!< 0x00000400 */ +#define HRTIM_RSTR_EXTEVNT2 HRTIM_RSTR_EXTEVNT2_Msk /*!< External event 2 */ +#define HRTIM_RSTR_EXTEVNT3_Pos (11U) +#define HRTIM_RSTR_EXTEVNT3_Msk (0x1UL << HRTIM_RSTR_EXTEVNT3_Pos) /*!< 0x00000800 */ +#define HRTIM_RSTR_EXTEVNT3 HRTIM_RSTR_EXTEVNT3_Msk /*!< External event 3 */ +#define HRTIM_RSTR_EXTEVNT4_Pos (12U) +#define HRTIM_RSTR_EXTEVNT4_Msk (0x1UL << HRTIM_RSTR_EXTEVNT4_Pos) /*!< 0x00001000 */ +#define HRTIM_RSTR_EXTEVNT4 HRTIM_RSTR_EXTEVNT4_Msk /*!< External event 4 */ +#define HRTIM_RSTR_EXTEVNT5_Pos (13U) +#define HRTIM_RSTR_EXTEVNT5_Msk (0x1UL << HRTIM_RSTR_EXTEVNT5_Pos) /*!< 0x00002000 */ +#define HRTIM_RSTR_EXTEVNT5 HRTIM_RSTR_EXTEVNT5_Msk /*!< External event 5 */ +#define HRTIM_RSTR_EXTEVNT6_Pos (14U) +#define HRTIM_RSTR_EXTEVNT6_Msk (0x1UL << HRTIM_RSTR_EXTEVNT6_Pos) /*!< 0x00004000 */ +#define HRTIM_RSTR_EXTEVNT6 HRTIM_RSTR_EXTEVNT6_Msk /*!< External event 6 */ +#define HRTIM_RSTR_EXTEVNT7_Pos (15U) +#define HRTIM_RSTR_EXTEVNT7_Msk (0x1UL << HRTIM_RSTR_EXTEVNT7_Pos) /*!< 0x00008000 */ +#define HRTIM_RSTR_EXTEVNT7 HRTIM_RSTR_EXTEVNT7_Msk /*!< External event 7 */ +#define HRTIM_RSTR_EXTEVNT8_Pos (16U) +#define HRTIM_RSTR_EXTEVNT8_Msk (0x1UL << HRTIM_RSTR_EXTEVNT8_Pos) /*!< 0x00010000 */ +#define HRTIM_RSTR_EXTEVNT8 HRTIM_RSTR_EXTEVNT8_Msk /*!< External event 8 */ +#define HRTIM_RSTR_EXTEVNT9_Pos (17U) +#define HRTIM_RSTR_EXTEVNT9_Msk (0x1UL << HRTIM_RSTR_EXTEVNT9_Pos) /*!< 0x00020000 */ +#define HRTIM_RSTR_EXTEVNT9 HRTIM_RSTR_EXTEVNT9_Msk /*!< External event 9 */ +#define HRTIM_RSTR_EXTEVNT10_Pos (18U) +#define HRTIM_RSTR_EXTEVNT10_Msk (0x1UL << HRTIM_RSTR_EXTEVNT10_Pos) /*!< 0x00040000 */ +#define HRTIM_RSTR_EXTEVNT10 HRTIM_RSTR_EXTEVNT10_Msk /*!< External event 10 */ + +/* Slave Timer A reset enable bits upon other slave timers events */ +#define HRTIM_RSTR_TIMBCMP1_Pos (19U) +#define HRTIM_RSTR_TIMBCMP1_Msk (0x1UL << HRTIM_RSTR_TIMBCMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTR_TIMBCMP1 HRTIM_RSTR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTR_TIMBCMP2_Pos (20U) +#define HRTIM_RSTR_TIMBCMP2_Msk (0x1UL << HRTIM_RSTR_TIMBCMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTR_TIMBCMP2 HRTIM_RSTR_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTR_TIMBCMP4_Pos (21U) +#define HRTIM_RSTR_TIMBCMP4_Msk (0x1UL << HRTIM_RSTR_TIMBCMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTR_TIMBCMP4 HRTIM_RSTR_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTR_TIMCCMP1_Pos (22U) +#define HRTIM_RSTR_TIMCCMP1_Msk (0x1UL << HRTIM_RSTR_TIMCCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTR_TIMCCMP1 HRTIM_RSTR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTR_TIMCCMP2_Pos (23U) +#define HRTIM_RSTR_TIMCCMP2_Msk (0x1UL << HRTIM_RSTR_TIMCCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTR_TIMCCMP2 HRTIM_RSTR_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTR_TIMCCMP4_Pos (24U) +#define HRTIM_RSTR_TIMCCMP4_Msk (0x1UL << HRTIM_RSTR_TIMCCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTR_TIMCCMP4 HRTIM_RSTR_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTR_TIMDCMP1_Pos (25U) +#define HRTIM_RSTR_TIMDCMP1_Msk (0x1UL << HRTIM_RSTR_TIMDCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTR_TIMDCMP1 HRTIM_RSTR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTR_TIMDCMP2_Pos (26U) +#define HRTIM_RSTR_TIMDCMP2_Msk (0x1UL << HRTIM_RSTR_TIMDCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTR_TIMDCMP2 HRTIM_RSTR_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTR_TIMDCMP4_Pos (27U) +#define HRTIM_RSTR_TIMDCMP4_Msk (0x1UL << HRTIM_RSTR_TIMDCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTR_TIMDCMP4 HRTIM_RSTR_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTR_TIMECMP1_Pos (28U) +#define HRTIM_RSTR_TIMECMP1_Msk (0x1UL << HRTIM_RSTR_TIMECMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTR_TIMECMP1 HRTIM_RSTR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_RSTR_TIMECMP2_Pos (29U) +#define HRTIM_RSTR_TIMECMP2_Msk (0x1UL << HRTIM_RSTR_TIMECMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTR_TIMECMP2 HRTIM_RSTR_TIMECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_RSTR_TIMECMP4_Pos (30U) +#define HRTIM_RSTR_TIMECMP4_Msk (0x1UL << HRTIM_RSTR_TIMECMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTR_TIMECMP4 HRTIM_RSTR_TIMECMP4_Msk /*!< Timer E compare 4 */ + +#define HRTIM_RSTR_TIMFCMP2_Pos (31U) +#define HRTIM_RSTR_TIMFCMP2_Msk (0x1UL << HRTIM_RSTR_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTR_TIMFCMP2 HRTIM_RSTR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer B reset enable bits upon other slave timers events */ +#define HRTIM_RSTBR_TIMACMP1_Pos (19U) +#define HRTIM_RSTBR_TIMACMP1_Msk (0x1UL << HRTIM_RSTBR_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTBR_TIMACMP1 HRTIM_RSTBR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTBR_TIMACMP2_Pos (20U) +#define HRTIM_RSTBR_TIMACMP2_Msk (0x1UL << HRTIM_RSTBR_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTBR_TIMACMP2 HRTIM_RSTBR_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTBR_TIMACMP4_Pos (21U) +#define HRTIM_RSTBR_TIMACMP4_Msk (0x1UL << HRTIM_RSTBR_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTBR_TIMACMP4 HRTIM_RSTBR_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTBR_TIMCCMP1_Pos (22U) +#define HRTIM_RSTBR_TIMCCMP1_Msk (0x1UL << HRTIM_RSTBR_TIMCCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTBR_TIMCCMP1 HRTIM_RSTBR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTBR_TIMCCMP2_Pos (23U) +#define HRTIM_RSTBR_TIMCCMP2_Msk (0x1UL << HRTIM_RSTBR_TIMCCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTBR_TIMCCMP2 HRTIM_RSTBR_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTBR_TIMCCMP4_Pos (24U) +#define HRTIM_RSTBR_TIMCCMP4_Msk (0x1UL << HRTIM_RSTBR_TIMCCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTBR_TIMCCMP4 HRTIM_RSTBR_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTBR_TIMDCMP1_Pos (25U) +#define HRTIM_RSTBR_TIMDCMP1_Msk (0x1UL << HRTIM_RSTBR_TIMDCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTBR_TIMDCMP1 HRTIM_RSTBR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTBR_TIMDCMP2_Pos (26U) +#define HRTIM_RSTBR_TIMDCMP2_Msk (0x1UL << HRTIM_RSTBR_TIMDCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTBR_TIMDCMP2 HRTIM_RSTBR_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTBR_TIMDCMP4_Pos (27U) +#define HRTIM_RSTBR_TIMDCMP4_Msk (0x1UL << HRTIM_RSTBR_TIMDCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTBR_TIMDCMP4 HRTIM_RSTBR_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTBR_TIMECMP1_Pos (28U) +#define HRTIM_RSTBR_TIMECMP1_Msk (0x1UL << HRTIM_RSTBR_TIMECMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTBR_TIMECMP1 HRTIM_RSTBR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_RSTBR_TIMECMP2_Pos (29U) +#define HRTIM_RSTBR_TIMECMP2_Msk (0x1UL << HRTIM_RSTBR_TIMECMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTBR_TIMECMP2 HRTIM_RSTBR_TIMECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_RSTBR_TIMECMP4_Pos (30U) +#define HRTIM_RSTBR_TIMECMP4_Msk (0x1UL << HRTIM_RSTBR_TIMECMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTBR_TIMECMP4 HRTIM_RSTBR_TIMECMP4_Msk /*!< Timer E compare 4 */ + +#define HRTIM_RSTBR_TIMFCMP2_Pos (31U) +#define HRTIM_RSTBR_TIMFCMP2_Msk (0x1UL << HRTIM_RSTBR_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTBR_TIMFCMP2 HRTIM_RSTBR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer C reset enable bits upon other slave timers events */ +#define HRTIM_RSTCR_TIMACMP1_Pos (19U) +#define HRTIM_RSTCR_TIMACMP1_Msk (0x1UL << HRTIM_RSTCR_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTCR_TIMACMP1 HRTIM_RSTCR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTCR_TIMACMP2_Pos (20U) +#define HRTIM_RSTCR_TIMACMP2_Msk (0x1UL << HRTIM_RSTCR_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTCR_TIMACMP2 HRTIM_RSTCR_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTCR_TIMACMP4_Pos (21U) +#define HRTIM_RSTCR_TIMACMP4_Msk (0x1UL << HRTIM_RSTCR_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTCR_TIMACMP4 HRTIM_RSTCR_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTCR_TIMBCMP1_Pos (22U) +#define HRTIM_RSTCR_TIMBCMP1_Msk (0x1UL << HRTIM_RSTCR_TIMBCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTCR_TIMBCMP1 HRTIM_RSTCR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTCR_TIMBCMP2_Pos (23U) +#define HRTIM_RSTCR_TIMBCMP2_Msk (0x1UL << HRTIM_RSTCR_TIMBCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTCR_TIMBCMP2 HRTIM_RSTCR_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTCR_TIMBCMP4_Pos (24U) +#define HRTIM_RSTCR_TIMBCMP4_Msk (0x1UL << HRTIM_RSTCR_TIMBCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTCR_TIMBCMP4 HRTIM_RSTCR_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTCR_TIMDCMP1_Pos (25U) +#define HRTIM_RSTCR_TIMDCMP1_Msk (0x1UL << HRTIM_RSTCR_TIMDCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTCR_TIMDCMP1 HRTIM_RSTCR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTCR_TIMDCMP2_Pos (26U) +#define HRTIM_RSTCR_TIMDCMP2_Msk (0x1UL << HRTIM_RSTCR_TIMDCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTCR_TIMDCMP2 HRTIM_RSTCR_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTCR_TIMDCMP4_Pos (27U) +#define HRTIM_RSTCR_TIMDCMP4_Msk (0x1UL << HRTIM_RSTCR_TIMDCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTCR_TIMDCMP4 HRTIM_RSTCR_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTCR_TIMECMP1_Pos (28U) +#define HRTIM_RSTCR_TIMECMP1_Msk (0x1UL << HRTIM_RSTCR_TIMECMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTCR_TIMECMP1 HRTIM_RSTCR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_RSTCR_TIMECMP2_Pos (29U) +#define HRTIM_RSTCR_TIMECMP2_Msk (0x1UL << HRTIM_RSTCR_TIMECMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTCR_TIMECMP2 HRTIM_RSTCR_TIMECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_RSTCR_TIMECMP4_Pos (30U) +#define HRTIM_RSTCR_TIMECMP4_Msk (0x1UL << HRTIM_RSTCR_TIMECMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTCR_TIMECMP4 HRTIM_RSTCR_TIMECMP4_Msk /*!< Timer E compare 4 */ + +#define HRTIM_RSTCR_TIMFCMP2_Pos (31U) +#define HRTIM_RSTCR_TIMFCMP2_Msk (0x1UL << HRTIM_RSTCR_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTCR_TIMFCMP2 HRTIM_RSTCR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer D reset enable bits upon other slave timers events */ +#define HRTIM_RSTDR_TIMACMP1_Pos (19U) +#define HRTIM_RSTDR_TIMACMP1_Msk (0x1UL << HRTIM_RSTDR_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTDR_TIMACMP1 HRTIM_RSTDR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTDR_TIMACMP2_Pos (20U) +#define HRTIM_RSTDR_TIMACMP2_Msk (0x1UL << HRTIM_RSTDR_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTDR_TIMACMP2 HRTIM_RSTDR_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTDR_TIMACMP4_Pos (21U) +#define HRTIM_RSTDR_TIMACMP4_Msk (0x1UL << HRTIM_RSTDR_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTDR_TIMACMP4 HRTIM_RSTDR_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTDR_TIMBCMP1_Pos (22U) +#define HRTIM_RSTDR_TIMBCMP1_Msk (0x1UL << HRTIM_RSTDR_TIMBCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTDR_TIMBCMP1 HRTIM_RSTDR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTDR_TIMBCMP2_Pos (23U) +#define HRTIM_RSTDR_TIMBCMP2_Msk (0x1UL << HRTIM_RSTDR_TIMBCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTDR_TIMBCMP2 HRTIM_RSTDR_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTDR_TIMBCMP4_Pos (24U) +#define HRTIM_RSTDR_TIMBCMP4_Msk (0x1UL << HRTIM_RSTDR_TIMBCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTDR_TIMBCMP4 HRTIM_RSTDR_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTDR_TIMCCMP1_Pos (25U) +#define HRTIM_RSTDR_TIMCCMP1_Msk (0x1UL << HRTIM_RSTDR_TIMCCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTDR_TIMCCMP1 HRTIM_RSTDR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTDR_TIMCCMP2_Pos (26U) +#define HRTIM_RSTDR_TIMCCMP2_Msk (0x1UL << HRTIM_RSTDR_TIMCCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTDR_TIMCCMP2 HRTIM_RSTDR_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTDR_TIMCCMP4_Pos (27U) +#define HRTIM_RSTDR_TIMCCMP4_Msk (0x1UL << HRTIM_RSTDR_TIMCCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTDR_TIMCCMP4 HRTIM_RSTDR_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTDR_TIMECMP1_Pos (28U) +#define HRTIM_RSTDR_TIMECMP1_Msk (0x1UL << HRTIM_RSTDR_TIMECMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTDR_TIMECMP1 HRTIM_RSTDR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_RSTDR_TIMECMP2_Pos (29U) +#define HRTIM_RSTDR_TIMECMP2_Msk (0x1UL << HRTIM_RSTDR_TIMECMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTDR_TIMECMP2 HRTIM_RSTDR_TIMECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_RSTDR_TIMECMP4_Pos (30U) +#define HRTIM_RSTDR_TIMECMP4_Msk (0x1UL << HRTIM_RSTDR_TIMECMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTDR_TIMECMP4 HRTIM_RSTDR_TIMECMP4_Msk /*!< Timer E compare 4 */ + +#define HRTIM_RSTDR_TIMFCMP2_Pos (31U) +#define HRTIM_RSTDR_TIMFCMP2_Msk (0x1UL << HRTIM_RSTDR_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTDR_TIMFCMP2 HRTIM_RSTDR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer E reset enable bits upon other slave timers events */ +#define HRTIM_RSTER_TIMACMP1_Pos (19U) +#define HRTIM_RSTER_TIMACMP1_Msk (0x1UL << HRTIM_RSTER_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTER_TIMACMP1 HRTIM_RSTER_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTER_TIMACMP2_Pos (20U) +#define HRTIM_RSTER_TIMACMP2_Msk (0x1UL << HRTIM_RSTER_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTER_TIMACMP2 HRTIM_RSTER_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTER_TIMACMP4_Pos (21U) +#define HRTIM_RSTER_TIMACMP4_Msk (0x1UL << HRTIM_RSTER_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTER_TIMACMP4 HRTIM_RSTER_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTER_TIMBCMP1_Pos (22U) +#define HRTIM_RSTER_TIMBCMP1_Msk (0x1UL << HRTIM_RSTER_TIMBCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTER_TIMBCMP1 HRTIM_RSTER_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTER_TIMBCMP2_Pos (23U) +#define HRTIM_RSTER_TIMBCMP2_Msk (0x1UL << HRTIM_RSTER_TIMBCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTER_TIMBCMP2 HRTIM_RSTER_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTER_TIMBCMP4_Pos (24U) +#define HRTIM_RSTER_TIMBCMP4_Msk (0x1UL << HRTIM_RSTER_TIMBCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTER_TIMBCMP4 HRTIM_RSTER_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTER_TIMCCMP1_Pos (25U) +#define HRTIM_RSTER_TIMCCMP1_Msk (0x1UL << HRTIM_RSTER_TIMCCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTER_TIMCCMP1 HRTIM_RSTER_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTER_TIMCCMP2_Pos (26U) +#define HRTIM_RSTER_TIMCCMP2_Msk (0x1UL << HRTIM_RSTER_TIMCCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTER_TIMCCMP2 HRTIM_RSTER_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTER_TIMCCMP4_Pos (27U) +#define HRTIM_RSTER_TIMCCMP4_Msk (0x1UL << HRTIM_RSTER_TIMCCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTER_TIMCCMP4 HRTIM_RSTER_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTER_TIMDCMP1_Pos (28U) +#define HRTIM_RSTER_TIMDCMP1_Msk (0x1UL << HRTIM_RSTER_TIMDCMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTER_TIMDCMP1 HRTIM_RSTER_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTER_TIMDCMP2_Pos (29U) +#define HRTIM_RSTER_TIMDCMP2_Msk (0x1UL << HRTIM_RSTER_TIMDCMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTER_TIMDCMP2 HRTIM_RSTER_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTER_TIMDCMP4_Pos (30U) +#define HRTIM_RSTER_TIMDCMP4_Msk (0x1UL << HRTIM_RSTER_TIMDCMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTER_TIMDCMP4 HRTIM_RSTER_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTER_TIMFCMP2_Pos (31U) +#define HRTIM_RSTER_TIMFCMP2_Msk (0x1UL << HRTIM_RSTER_TIMFCMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTER_TIMFCMP2 HRTIM_RSTER_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +/* Slave Timer F reset enable bits upon other slave timers events */ +#define HRTIM_RSTFR_TIMACMP1_Pos (19U) +#define HRTIM_RSTFR_TIMACMP1_Msk (0x1UL << HRTIM_RSTFR_TIMACMP1_Pos) /*!< 0x00080000 */ +#define HRTIM_RSTFR_TIMACMP1 HRTIM_RSTFR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_RSTFR_TIMACMP2_Pos (20U) +#define HRTIM_RSTFR_TIMACMP2_Msk (0x1UL << HRTIM_RSTFR_TIMACMP2_Pos) /*!< 0x00100000 */ +#define HRTIM_RSTFR_TIMACMP2 HRTIM_RSTFR_TIMACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_RSTFR_TIMACMP4_Pos (21U) +#define HRTIM_RSTFR_TIMACMP4_Msk (0x1UL << HRTIM_RSTFR_TIMACMP4_Pos) /*!< 0x00200000 */ +#define HRTIM_RSTFR_TIMACMP4 HRTIM_RSTFR_TIMACMP4_Msk /*!< Timer A compare 4 */ + +#define HRTIM_RSTFR_TIMBCMP1_Pos (22U) +#define HRTIM_RSTFR_TIMBCMP1_Msk (0x1UL << HRTIM_RSTFR_TIMBCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_RSTFR_TIMBCMP1 HRTIM_RSTFR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_RSTFR_TIMBCMP2_Pos (23U) +#define HRTIM_RSTFR_TIMBCMP2_Msk (0x1UL << HRTIM_RSTFR_TIMBCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_RSTFR_TIMBCMP2 HRTIM_RSTFR_TIMBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_RSTFR_TIMBCMP4_Pos (24U) +#define HRTIM_RSTFR_TIMBCMP4_Msk (0x1UL << HRTIM_RSTFR_TIMBCMP4_Pos) /*!< 0x01000000 */ +#define HRTIM_RSTFR_TIMBCMP4 HRTIM_RSTFR_TIMBCMP4_Msk /*!< Timer B compare 4 */ + +#define HRTIM_RSTFR_TIMCCMP1_Pos (25U) +#define HRTIM_RSTFR_TIMCCMP1_Msk (0x1UL << HRTIM_RSTFR_TIMCCMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_RSTFR_TIMCCMP1 HRTIM_RSTFR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_RSTFR_TIMCCMP2_Pos (26U) +#define HRTIM_RSTFR_TIMCCMP2_Msk (0x1UL << HRTIM_RSTFR_TIMCCMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_RSTFR_TIMCCMP2 HRTIM_RSTFR_TIMCCMP2_Msk /*!< Timer C compare 2 */ +#define HRTIM_RSTFR_TIMCCMP4_Pos (27U) +#define HRTIM_RSTFR_TIMCCMP4_Msk (0x1UL << HRTIM_RSTFR_TIMCCMP4_Pos) /*!< 0x08000000 */ +#define HRTIM_RSTFR_TIMCCMP4 HRTIM_RSTFR_TIMCCMP4_Msk /*!< Timer C compare 4 */ + +#define HRTIM_RSTFR_TIMDCMP1_Pos (28U) +#define HRTIM_RSTFR_TIMDCMP1_Msk (0x1UL << HRTIM_RSTFR_TIMDCMP1_Pos) /*!< 0x10000000 */ +#define HRTIM_RSTFR_TIMDCMP1 HRTIM_RSTFR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_RSTFR_TIMDCMP2_Pos (29U) +#define HRTIM_RSTFR_TIMDCMP2_Msk (0x1UL << HRTIM_RSTFR_TIMDCMP2_Pos) /*!< 0x20000000 */ +#define HRTIM_RSTFR_TIMDCMP2 HRTIM_RSTFR_TIMDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_RSTFR_TIMDCMP4_Pos (30U) +#define HRTIM_RSTFR_TIMDCMP4_Msk (0x1UL << HRTIM_RSTFR_TIMDCMP4_Pos) /*!< 0x40000000 */ +#define HRTIM_RSTFR_TIMDCMP4 HRTIM_RSTFR_TIMDCMP4_Msk /*!< Timer D compare 4 */ + +#define HRTIM_RSTFR_TIMECMP2_Pos (31U) +#define HRTIM_RSTFR_TIMECMP2_Msk (0x1UL << HRTIM_RSTFR_TIMECMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_RSTFR_TIMECMP2 HRTIM_RSTFR_TIMECMP2_Msk /*!< Timer E compare 2 */ + +/**** Bit definition for Slave Timer Chopper register *************************/ +#define HRTIM_CHPR_CARFRQ_Pos (0U) +#define HRTIM_CHPR_CARFRQ_Msk (0xFUL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x0000000F */ +#define HRTIM_CHPR_CARFRQ HRTIM_CHPR_CARFRQ_Msk /*!< Timer carrier frequency value */ +#define HRTIM_CHPR_CARFRQ_0 (0x1UL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x00000001 */ +#define HRTIM_CHPR_CARFRQ_1 (0x2UL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x00000002 */ +#define HRTIM_CHPR_CARFRQ_2 (0x4UL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x00000004 */ +#define HRTIM_CHPR_CARFRQ_3 (0x8UL << HRTIM_CHPR_CARFRQ_Pos) /*!< 0x00000008 */ + +#define HRTIM_CHPR_CARDTY_Pos (4U) +#define HRTIM_CHPR_CARDTY_Msk (0x7UL << HRTIM_CHPR_CARDTY_Pos) /*!< 0x00000070 */ +#define HRTIM_CHPR_CARDTY HRTIM_CHPR_CARDTY_Msk /*!< Timer chopper duty cycle value */ +#define HRTIM_CHPR_CARDTY_0 (0x1UL << HRTIM_CHPR_CARDTY_Pos) /*!< 0x00000010 */ +#define HRTIM_CHPR_CARDTY_1 (0x2UL << HRTIM_CHPR_CARDTY_Pos) /*!< 0x00000020 */ +#define HRTIM_CHPR_CARDTY_2 (0x4UL << HRTIM_CHPR_CARDTY_Pos) /*!< 0x00000040 */ + +#define HRTIM_CHPR_STRPW_Pos (7U) +#define HRTIM_CHPR_STRPW_Msk (0xFUL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000780 */ +#define HRTIM_CHPR_STRPW HRTIM_CHPR_STRPW_Msk /*!< Timer start pulse width value */ +#define HRTIM_CHPR_STRPW_0 (0x1UL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000080 */ +#define HRTIM_CHPR_STRPW_1 (0x2UL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000100 */ +#define HRTIM_CHPR_STRPW_2 (0x4UL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000200 */ +#define HRTIM_CHPR_STRPW_3 (0x8UL << HRTIM_CHPR_STRPW_Pos) /*!< 0x00000400 */ + +/**** Bit definition for Slave Timer Capture 1 control register ***************/ +#define HRTIM_CPT1CR_SWCPT_Pos (0U) +#define HRTIM_CPT1CR_SWCPT_Msk (0x1UL << HRTIM_CPT1CR_SWCPT_Pos) /*!< 0x00000001 */ +#define HRTIM_CPT1CR_SWCPT HRTIM_CPT1CR_SWCPT_Msk /*!< Software capture */ +#define HRTIM_CPT1CR_UPDCPT_Pos (1U) +#define HRTIM_CPT1CR_UPDCPT_Msk (0x1UL << HRTIM_CPT1CR_UPDCPT_Pos) /*!< 0x00000002 */ +#define HRTIM_CPT1CR_UPDCPT HRTIM_CPT1CR_UPDCPT_Msk /*!< Update capture */ +#define HRTIM_CPT1CR_EXEV1CPT_Pos (2U) +#define HRTIM_CPT1CR_EXEV1CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV1CPT_Pos) /*!< 0x00000004 */ +#define HRTIM_CPT1CR_EXEV1CPT HRTIM_CPT1CR_EXEV1CPT_Msk /*!< External event 1 capture */ +#define HRTIM_CPT1CR_EXEV2CPT_Pos (3U) +#define HRTIM_CPT1CR_EXEV2CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV2CPT_Pos) /*!< 0x00000008 */ +#define HRTIM_CPT1CR_EXEV2CPT HRTIM_CPT1CR_EXEV2CPT_Msk /*!< External event 2 capture */ +#define HRTIM_CPT1CR_EXEV3CPT_Pos (4U) +#define HRTIM_CPT1CR_EXEV3CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV3CPT_Pos) /*!< 0x00000010 */ +#define HRTIM_CPT1CR_EXEV3CPT HRTIM_CPT1CR_EXEV3CPT_Msk /*!< External event 3 capture */ +#define HRTIM_CPT1CR_EXEV4CPT_Pos (5U) +#define HRTIM_CPT1CR_EXEV4CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV4CPT_Pos) /*!< 0x00000020 */ +#define HRTIM_CPT1CR_EXEV4CPT HRTIM_CPT1CR_EXEV4CPT_Msk /*!< External event 4 capture */ +#define HRTIM_CPT1CR_EXEV5CPT_Pos (6U) +#define HRTIM_CPT1CR_EXEV5CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV5CPT_Pos) /*!< 0x00000040 */ +#define HRTIM_CPT1CR_EXEV5CPT HRTIM_CPT1CR_EXEV5CPT_Msk /*!< External event 5 capture */ +#define HRTIM_CPT1CR_EXEV6CPT_Pos (7U) +#define HRTIM_CPT1CR_EXEV6CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV6CPT_Pos) /*!< 0x00000080 */ +#define HRTIM_CPT1CR_EXEV6CPT HRTIM_CPT1CR_EXEV6CPT_Msk /*!< External event 6 capture */ +#define HRTIM_CPT1CR_EXEV7CPT_Pos (8U) +#define HRTIM_CPT1CR_EXEV7CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV7CPT_Pos) /*!< 0x00000100 */ +#define HRTIM_CPT1CR_EXEV7CPT HRTIM_CPT1CR_EXEV7CPT_Msk /*!< External event 7 capture */ +#define HRTIM_CPT1CR_EXEV8CPT_Pos (9U) +#define HRTIM_CPT1CR_EXEV8CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV8CPT_Pos) /*!< 0x00000200 */ +#define HRTIM_CPT1CR_EXEV8CPT HRTIM_CPT1CR_EXEV8CPT_Msk /*!< External event 8 capture */ +#define HRTIM_CPT1CR_EXEV9CPT_Pos (10U) +#define HRTIM_CPT1CR_EXEV9CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV9CPT_Pos) /*!< 0x00000400 */ +#define HRTIM_CPT1CR_EXEV9CPT HRTIM_CPT1CR_EXEV9CPT_Msk /*!< External event 9 capture */ +#define HRTIM_CPT1CR_EXEV10CPT_Pos (11U) +#define HRTIM_CPT1CR_EXEV10CPT_Msk (0x1UL << HRTIM_CPT1CR_EXEV10CPT_Pos) /*!< 0x00000800 */ +#define HRTIM_CPT1CR_EXEV10CPT HRTIM_CPT1CR_EXEV10CPT_Msk /*!< External event 10 capture */ + +#define HRTIM_CPT1CR_TF1SET_Pos (0U) +#define HRTIM_CPT1CR_TF1SET_Msk (0x1UL << HRTIM_CPT1CR_TF1SET_Pos) /*!< 0x00000001 */ +#define HRTIM_CPT1CR_TF1SET HRTIM_CPT1CR_TF1SET_Msk /*!< Timer F output 1 set */ +#define HRTIM_CPT1CR_TF1RST_Pos (1U) +#define HRTIM_CPT1CR_TF1RST_Msk (0x1UL << HRTIM_CPT1CR_TF1RST_Pos) /*!< 0x00000002 */ +#define HRTIM_CPT1CR_TF1RST HRTIM_CPT1CR_TF1RST_Msk /*!< Timer F output 1 reset */ +#define HRTIM_CPT1CR_TIMFCMP1_Pos (2U) +#define HRTIM_CPT1CR_TIMFCMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMFCMP1_Pos) /*!< 0x00000004 */ +#define HRTIM_CPT1CR_TIMFCMP1 HRTIM_CPT1CR_TIMFCMP1_Msk /*!< Timer F compare 1 */ +#define HRTIM_CPT1CR_TIMFCMP2_Pos (3U) +#define HRTIM_CPT1CR_TIMFCMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMFCMP2_Pos) /*!< 0x00000008 */ +#define HRTIM_CPT1CR_TIMFCMP2 HRTIM_CPT1CR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +#define HRTIM_CPT1CR_TA1SET_Pos (12U) +#define HRTIM_CPT1CR_TA1SET_Msk (0x1UL << HRTIM_CPT1CR_TA1SET_Pos) /*!< 0x00001000 */ +#define HRTIM_CPT1CR_TA1SET HRTIM_CPT1CR_TA1SET_Msk /*!< Timer A output 1 set */ +#define HRTIM_CPT1CR_TA1RST_Pos (13U) +#define HRTIM_CPT1CR_TA1RST_Msk (0x1UL << HRTIM_CPT1CR_TA1RST_Pos) /*!< 0x00002000 */ +#define HRTIM_CPT1CR_TA1RST HRTIM_CPT1CR_TA1RST_Msk /*!< Timer A output 1 reset */ +#define HRTIM_CPT1CR_TIMACMP1_Pos (14U) +#define HRTIM_CPT1CR_TIMACMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMACMP1_Pos) /*!< 0x00004000 */ +#define HRTIM_CPT1CR_TIMACMP1 HRTIM_CPT1CR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_CPT1CR_TIMACMP2_Pos (15U) +#define HRTIM_CPT1CR_TIMACMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMACMP2_Pos) /*!< 0x00008000 */ +#define HRTIM_CPT1CR_TIMACMP2 HRTIM_CPT1CR_TIMACMP2_Msk /*!< Timer A compare 2 */ + +#define HRTIM_CPT1CR_TB1SET_Pos (16U) +#define HRTIM_CPT1CR_TB1SET_Msk (0x1UL << HRTIM_CPT1CR_TB1SET_Pos) /*!< 0x00010000 */ +#define HRTIM_CPT1CR_TB1SET HRTIM_CPT1CR_TB1SET_Msk /*!< Timer B output 1 set */ +#define HRTIM_CPT1CR_TB1RST_Pos (17U) +#define HRTIM_CPT1CR_TB1RST_Msk (0x1UL << HRTIM_CPT1CR_TB1RST_Pos) /*!< 0x00020000 */ +#define HRTIM_CPT1CR_TB1RST HRTIM_CPT1CR_TB1RST_Msk /*!< Timer B output 1 reset */ +#define HRTIM_CPT1CR_TIMBCMP1_Pos (18U) +#define HRTIM_CPT1CR_TIMBCMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMBCMP1_Pos) /*!< 0x00040000 */ +#define HRTIM_CPT1CR_TIMBCMP1 HRTIM_CPT1CR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_CPT1CR_TIMBCMP2_Pos (19U) +#define HRTIM_CPT1CR_TIMBCMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMBCMP2_Pos) /*!< 0x00080000 */ +#define HRTIM_CPT1CR_TIMBCMP2 HRTIM_CPT1CR_TIMBCMP2_Msk /*!< Timer B compare 2 */ + +#define HRTIM_CPT1CR_TC1SET_Pos (20U) +#define HRTIM_CPT1CR_TC1SET_Msk (0x1UL << HRTIM_CPT1CR_TC1SET_Pos) /*!< 0x00100000 */ +#define HRTIM_CPT1CR_TC1SET HRTIM_CPT1CR_TC1SET_Msk /*!< Timer C output 1 set */ +#define HRTIM_CPT1CR_TC1RST_Pos (21U) +#define HRTIM_CPT1CR_TC1RST_Msk (0x1UL << HRTIM_CPT1CR_TC1RST_Pos) /*!< 0x00200000 */ +#define HRTIM_CPT1CR_TC1RST HRTIM_CPT1CR_TC1RST_Msk /*!< Timer C output 1 reset */ +#define HRTIM_CPT1CR_TIMCCMP1_Pos (22U) +#define HRTIM_CPT1CR_TIMCCMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMCCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_CPT1CR_TIMCCMP1 HRTIM_CPT1CR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_CPT1CR_TIMCCMP2_Pos (23U) +#define HRTIM_CPT1CR_TIMCCMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMCCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_CPT1CR_TIMCCMP2 HRTIM_CPT1CR_TIMCCMP2_Msk /*!< Timer C compare 2 */ + +#define HRTIM_CPT1CR_TD1SET_Pos (24U) +#define HRTIM_CPT1CR_TD1SET_Msk (0x1UL << HRTIM_CPT1CR_TD1SET_Pos) /*!< 0x01000000 */ +#define HRTIM_CPT1CR_TD1SET HRTIM_CPT1CR_TD1SET_Msk /*!< Timer D output 1 set */ +#define HRTIM_CPT1CR_TD1RST_Pos (25U) +#define HRTIM_CPT1CR_TD1RST_Msk (0x1UL << HRTIM_CPT1CR_TD1RST_Pos) /*!< 0x02000000 */ +#define HRTIM_CPT1CR_TD1RST HRTIM_CPT1CR_TD1RST_Msk /*!< Timer D output 1 reset */ +#define HRTIM_CPT1CR_TIMDCMP1_Pos (26U) +#define HRTIM_CPT1CR_TIMDCMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMDCMP1_Pos) /*!< 0x04000000 */ +#define HRTIM_CPT1CR_TIMDCMP1 HRTIM_CPT1CR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_CPT1CR_TIMDCMP2_Pos (27U) +#define HRTIM_CPT1CR_TIMDCMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMDCMP2_Pos) /*!< 0x08000000 */ +#define HRTIM_CPT1CR_TIMDCMP2 HRTIM_CPT1CR_TIMDCMP2_Msk /*!< Timer D compare 2 */ + +#define HRTIM_CPT1CR_TE1SET_Pos (28U) +#define HRTIM_CPT1CR_TE1SET_Msk (0x1UL << HRTIM_CPT1CR_TE1SET_Pos) /*!< 0x10000000 */ +#define HRTIM_CPT1CR_TE1SET HRTIM_CPT1CR_TE1SET_Msk /*!< Timer E output 1 set */ +#define HRTIM_CPT1CR_TE1RST_Pos (29U) +#define HRTIM_CPT1CR_TE1RST_Msk (0x1UL << HRTIM_CPT1CR_TE1RST_Pos) /*!< 0x20000000 */ +#define HRTIM_CPT1CR_TE1RST HRTIM_CPT1CR_TE1RST_Msk /*!< Timer E output 1 reset */ +#define HRTIM_CPT1CR_TIMECMP1_Pos (30U) +#define HRTIM_CPT1CR_TIMECMP1_Msk (0x1UL << HRTIM_CPT1CR_TIMECMP1_Pos) /*!< 0x40000000 */ +#define HRTIM_CPT1CR_TIMECMP1 HRTIM_CPT1CR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_CPT1CR_TIMECMP2_Pos (31U) +#define HRTIM_CPT1CR_TIMECMP2_Msk (0x1UL << HRTIM_CPT1CR_TIMECMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_CPT1CR_TIMECMP2 HRTIM_CPT1CR_TIMECMP2_Msk /*!< Timer E compare 2 */ + +/**** Bit definition for Slave Timer Capture 2 control register ***************/ +#define HRTIM_CPT2CR_SWCPT_Pos (0U) +#define HRTIM_CPT2CR_SWCPT_Msk (0x1UL << HRTIM_CPT2CR_SWCPT_Pos) /*!< 0x00000001 */ +#define HRTIM_CPT2CR_SWCPT HRTIM_CPT2CR_SWCPT_Msk /*!< Software capture */ +#define HRTIM_CPT2CR_UPDCPT_Pos (1U) +#define HRTIM_CPT2CR_UPDCPT_Msk (0x1UL << HRTIM_CPT2CR_UPDCPT_Pos) /*!< 0x00000002 */ +#define HRTIM_CPT2CR_UPDCPT HRTIM_CPT2CR_UPDCPT_Msk /*!< Update capture */ +#define HRTIM_CPT2CR_EXEV1CPT_Pos (2U) +#define HRTIM_CPT2CR_EXEV1CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV1CPT_Pos) /*!< 0x00000004 */ +#define HRTIM_CPT2CR_EXEV1CPT HRTIM_CPT2CR_EXEV1CPT_Msk /*!< External event 1 capture */ +#define HRTIM_CPT2CR_EXEV2CPT_Pos (3U) +#define HRTIM_CPT2CR_EXEV2CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV2CPT_Pos) /*!< 0x00000008 */ +#define HRTIM_CPT2CR_EXEV2CPT HRTIM_CPT2CR_EXEV2CPT_Msk /*!< External event 2 capture */ +#define HRTIM_CPT2CR_EXEV3CPT_Pos (4U) +#define HRTIM_CPT2CR_EXEV3CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV3CPT_Pos) /*!< 0x00000010 */ +#define HRTIM_CPT2CR_EXEV3CPT HRTIM_CPT2CR_EXEV3CPT_Msk /*!< External event 3 capture */ +#define HRTIM_CPT2CR_EXEV4CPT_Pos (5U) +#define HRTIM_CPT2CR_EXEV4CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV4CPT_Pos) /*!< 0x00000020 */ +#define HRTIM_CPT2CR_EXEV4CPT HRTIM_CPT2CR_EXEV4CPT_Msk /*!< External event 4 capture */ +#define HRTIM_CPT2CR_EXEV5CPT_Pos (6U) +#define HRTIM_CPT2CR_EXEV5CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV5CPT_Pos) /*!< 0x00000040 */ +#define HRTIM_CPT2CR_EXEV5CPT HRTIM_CPT2CR_EXEV5CPT_Msk /*!< External event 5 capture */ +#define HRTIM_CPT2CR_EXEV6CPT_Pos (7U) +#define HRTIM_CPT2CR_EXEV6CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV6CPT_Pos) /*!< 0x00000080 */ +#define HRTIM_CPT2CR_EXEV6CPT HRTIM_CPT2CR_EXEV6CPT_Msk /*!< External event 6 capture */ +#define HRTIM_CPT2CR_EXEV7CPT_Pos (8U) +#define HRTIM_CPT2CR_EXEV7CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV7CPT_Pos) /*!< 0x00000100 */ +#define HRTIM_CPT2CR_EXEV7CPT HRTIM_CPT2CR_EXEV7CPT_Msk /*!< External event 7 capture */ +#define HRTIM_CPT2CR_EXEV8CPT_Pos (9U) +#define HRTIM_CPT2CR_EXEV8CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV8CPT_Pos) /*!< 0x00000200 */ +#define HRTIM_CPT2CR_EXEV8CPT HRTIM_CPT2CR_EXEV8CPT_Msk /*!< External event 8 capture */ +#define HRTIM_CPT2CR_EXEV9CPT_Pos (10U) +#define HRTIM_CPT2CR_EXEV9CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV9CPT_Pos) /*!< 0x00000400 */ +#define HRTIM_CPT2CR_EXEV9CPT HRTIM_CPT2CR_EXEV9CPT_Msk /*!< External event 9 capture */ +#define HRTIM_CPT2CR_EXEV10CPT_Pos (11U) +#define HRTIM_CPT2CR_EXEV10CPT_Msk (0x1UL << HRTIM_CPT2CR_EXEV10CPT_Pos) /*!< 0x00000800 */ +#define HRTIM_CPT2CR_EXEV10CPT HRTIM_CPT2CR_EXEV10CPT_Msk /*!< External event 10 capture */ + +#define HRTIM_CPT2CR_TF1SET_Pos (0U) +#define HRTIM_CPT2CR_TF1SET_Msk (0x1UL << HRTIM_CPT2CR_TF1SET_Pos) /*!< 0x00000001 */ +#define HRTIM_CPT2CR_TF1SET HRTIM_CPT2CR_TF1SET_Msk /*!< Timer F output 1 set */ +#define HRTIM_CPT2CR_TF1RST_Pos (1U) +#define HRTIM_CPT2CR_TF1RST_Msk (0x1UL << HRTIM_CPT2CR_TF1RST_Pos) /*!< 0x00000002 */ +#define HRTIM_CPT2CR_TF1RST HRTIM_CPT2CR_TF1RST_Msk /*!< Timer F output 1 reset */ +#define HRTIM_CPT2CR_TIMFCMP1_Pos (2U) +#define HRTIM_CPT2CR_TIMFCMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMFCMP1_Pos) /*!< 0x00000004 */ +#define HRTIM_CPT2CR_TIMFCMP1 HRTIM_CPT2CR_TIMFCMP1_Msk /*!< Timer F compare 1 */ +#define HRTIM_CPT2CR_TIMFCMP2_Pos (3U) +#define HRTIM_CPT2CR_TIMFCMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMFCMP2_Pos) /*!< 0x00000008 */ +#define HRTIM_CPT2CR_TIMFCMP2 HRTIM_CPT2CR_TIMFCMP2_Msk /*!< Timer F compare 2 */ + +#define HRTIM_CPT2CR_TA1SET_Pos (12U) +#define HRTIM_CPT2CR_TA1SET_Msk (0x1UL << HRTIM_CPT2CR_TA1SET_Pos) /*!< 0x00001000 */ +#define HRTIM_CPT2CR_TA1SET HRTIM_CPT2CR_TA1SET_Msk /*!< Timer A output 1 set */ +#define HRTIM_CPT2CR_TA1RST_Pos (13U) +#define HRTIM_CPT2CR_TA1RST_Msk (0x1UL << HRTIM_CPT2CR_TA1RST_Pos) /*!< 0x00002000 */ +#define HRTIM_CPT2CR_TA1RST HRTIM_CPT2CR_TA1RST_Msk /*!< Timer A output 1 reset */ +#define HRTIM_CPT2CR_TIMACMP1_Pos (14U) +#define HRTIM_CPT2CR_TIMACMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMACMP1_Pos) /*!< 0x00004000 */ +#define HRTIM_CPT2CR_TIMACMP1 HRTIM_CPT2CR_TIMACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_CPT2CR_TIMACMP2_Pos (15U) +#define HRTIM_CPT2CR_TIMACMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMACMP2_Pos) /*!< 0x00008000 */ +#define HRTIM_CPT2CR_TIMACMP2 HRTIM_CPT2CR_TIMACMP2_Msk /*!< Timer A compare 2 */ + +#define HRTIM_CPT2CR_TB1SET_Pos (16U) +#define HRTIM_CPT2CR_TB1SET_Msk (0x1UL << HRTIM_CPT2CR_TB1SET_Pos) /*!< 0x00010000 */ +#define HRTIM_CPT2CR_TB1SET HRTIM_CPT2CR_TB1SET_Msk /*!< Timer B output 1 set */ +#define HRTIM_CPT2CR_TB1RST_Pos (17U) +#define HRTIM_CPT2CR_TB1RST_Msk (0x1UL << HRTIM_CPT2CR_TB1RST_Pos) /*!< 0x00020000 */ +#define HRTIM_CPT2CR_TB1RST HRTIM_CPT2CR_TB1RST_Msk /*!< Timer B output 1 reset */ +#define HRTIM_CPT2CR_TIMBCMP1_Pos (18U) +#define HRTIM_CPT2CR_TIMBCMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMBCMP1_Pos) /*!< 0x00040000 */ +#define HRTIM_CPT2CR_TIMBCMP1 HRTIM_CPT2CR_TIMBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_CPT2CR_TIMBCMP2_Pos (19U) +#define HRTIM_CPT2CR_TIMBCMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMBCMP2_Pos) /*!< 0x00080000 */ +#define HRTIM_CPT2CR_TIMBCMP2 HRTIM_CPT2CR_TIMBCMP2_Msk /*!< Timer B compare 2 */ + +#define HRTIM_CPT2CR_TC1SET_Pos (20U) +#define HRTIM_CPT2CR_TC1SET_Msk (0x1UL << HRTIM_CPT2CR_TC1SET_Pos) /*!< 0x00100000 */ +#define HRTIM_CPT2CR_TC1SET HRTIM_CPT2CR_TC1SET_Msk /*!< Timer C output 1 set */ +#define HRTIM_CPT2CR_TC1RST_Pos (21U) +#define HRTIM_CPT2CR_TC1RST_Msk (0x1UL << HRTIM_CPT2CR_TC1RST_Pos) /*!< 0x00200000 */ +#define HRTIM_CPT2CR_TC1RST HRTIM_CPT2CR_TC1RST_Msk /*!< Timer C output 1 reset */ +#define HRTIM_CPT2CR_TIMCCMP1_Pos (22U) +#define HRTIM_CPT2CR_TIMCCMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMCCMP1_Pos) /*!< 0x00400000 */ +#define HRTIM_CPT2CR_TIMCCMP1 HRTIM_CPT2CR_TIMCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_CPT2CR_TIMCCMP2_Pos (23U) +#define HRTIM_CPT2CR_TIMCCMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMCCMP2_Pos) /*!< 0x00800000 */ +#define HRTIM_CPT2CR_TIMCCMP2 HRTIM_CPT2CR_TIMCCMP2_Msk /*!< Timer C compare 2 */ + +#define HRTIM_CPT2CR_TD1SET_Pos (24U) +#define HRTIM_CPT2CR_TD1SET_Msk (0x1UL << HRTIM_CPT2CR_TD1SET_Pos) /*!< 0x01000000 */ +#define HRTIM_CPT2CR_TD1SET HRTIM_CPT2CR_TD1SET_Msk /*!< Timer D output 1 set */ +#define HRTIM_CPT2CR_TD1RST_Pos (25U) +#define HRTIM_CPT2CR_TD1RST_Msk (0x1UL << HRTIM_CPT2CR_TD1RST_Pos) /*!< 0x02000000 */ +#define HRTIM_CPT2CR_TD1RST HRTIM_CPT2CR_TD1RST_Msk /*!< Timer D output 1 reset */ +#define HRTIM_CPT2CR_TIMDCMP1_Pos (26U) +#define HRTIM_CPT2CR_TIMDCMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMDCMP1_Pos) /*!< 0x04000000 */ +#define HRTIM_CPT2CR_TIMDCMP1 HRTIM_CPT2CR_TIMDCMP1_Msk /*!< Timer D compare 1 */ +#define HRTIM_CPT2CR_TIMDCMP2_Pos (27U) +#define HRTIM_CPT2CR_TIMDCMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMDCMP2_Pos) /*!< 0x08000000 */ +#define HRTIM_CPT2CR_TIMDCMP2 HRTIM_CPT2CR_TIMDCMP2_Msk /*!< Timer D compare 2 */ + +#define HRTIM_CPT2CR_TE1SET_Pos (28U) +#define HRTIM_CPT2CR_TE1SET_Msk (0x1UL << HRTIM_CPT2CR_TE1SET_Pos) /*!< 0x10000000 */ +#define HRTIM_CPT2CR_TE1SET HRTIM_CPT2CR_TE1SET_Msk /*!< Timer E output 1 set */ +#define HRTIM_CPT2CR_TE1RST_Pos (29U) +#define HRTIM_CPT2CR_TE1RST_Msk (0x1UL << HRTIM_CPT2CR_TE1RST_Pos) /*!< 0x20000000 */ +#define HRTIM_CPT2CR_TE1RST HRTIM_CPT2CR_TE1RST_Msk /*!< Timer E output 1 reset */ +#define HRTIM_CPT2CR_TIMECMP1_Pos (30U) +#define HRTIM_CPT2CR_TIMECMP1_Msk (0x1UL << HRTIM_CPT2CR_TIMECMP1_Pos) /*!< 0x40000000 */ +#define HRTIM_CPT2CR_TIMECMP1 HRTIM_CPT2CR_TIMECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_CPT2CR_TIMECMP2_Pos (31U) +#define HRTIM_CPT2CR_TIMECMP2_Msk (0x1UL << HRTIM_CPT2CR_TIMECMP2_Pos) /*!< 0x80000000 */ +#define HRTIM_CPT2CR_TIMECMP2 HRTIM_CPT2CR_TIMECMP2_Msk /*!< Timer E compare 2 */ + +/**** Bit definition for Slave Timer Output register **************************/ +#define HRTIM_OUTR_POL1_Pos (1U) +#define HRTIM_OUTR_POL1_Msk (0x1UL << HRTIM_OUTR_POL1_Pos) /*!< 0x00000002 */ +#define HRTIM_OUTR_POL1 HRTIM_OUTR_POL1_Msk /*!< Slave output 1 polarity */ +#define HRTIM_OUTR_IDLM1_Pos (2U) +#define HRTIM_OUTR_IDLM1_Msk (0x1UL << HRTIM_OUTR_IDLM1_Pos) /*!< 0x00000004 */ +#define HRTIM_OUTR_IDLM1 HRTIM_OUTR_IDLM1_Msk /*!< Slave output 1 idle mode */ +#define HRTIM_OUTR_IDLES1_Pos (3U) +#define HRTIM_OUTR_IDLES1_Msk (0x1UL << HRTIM_OUTR_IDLES1_Pos) /*!< 0x00000008 */ +#define HRTIM_OUTR_IDLES1 HRTIM_OUTR_IDLES1_Msk /*!< Slave output 1 idle state */ +#define HRTIM_OUTR_FAULT1_Pos (4U) +#define HRTIM_OUTR_FAULT1_Msk (0x3UL << HRTIM_OUTR_FAULT1_Pos) /*!< 0x00000030 */ +#define HRTIM_OUTR_FAULT1 HRTIM_OUTR_FAULT1_Msk /*!< Slave output 1 fault state */ +#define HRTIM_OUTR_FAULT1_0 (0x1UL << HRTIM_OUTR_FAULT1_Pos) /*!< 0x00000010 */ +#define HRTIM_OUTR_FAULT1_1 (0x2UL << HRTIM_OUTR_FAULT1_Pos) /*!< 0x00000020 */ +#define HRTIM_OUTR_CHP1_Pos (6U) +#define HRTIM_OUTR_CHP1_Msk (0x1UL << HRTIM_OUTR_CHP1_Pos) /*!< 0x00000040 */ +#define HRTIM_OUTR_CHP1 HRTIM_OUTR_CHP1_Msk /*!< Slave output 1 chopper enable */ +#define HRTIM_OUTR_DIDL1_Pos (7U) +#define HRTIM_OUTR_DIDL1_Msk (0x1UL << HRTIM_OUTR_DIDL1_Pos) /*!< 0x00000080 */ +#define HRTIM_OUTR_DIDL1 HRTIM_OUTR_DIDL1_Msk /*!< Slave output 1 dead time idle */ + +#define HRTIM_OUTR_DTEN_Pos (8U) +#define HRTIM_OUTR_DTEN_Msk (0x1UL << HRTIM_OUTR_DTEN_Pos) /*!< 0x00000100 */ +#define HRTIM_OUTR_DTEN HRTIM_OUTR_DTEN_Msk /*!< Slave output deadtime enable */ +#define HRTIM_OUTR_DLYPRTEN_Pos (9U) +#define HRTIM_OUTR_DLYPRTEN_Msk (0x1UL << HRTIM_OUTR_DLYPRTEN_Pos) /*!< 0x00000200 */ +#define HRTIM_OUTR_DLYPRTEN HRTIM_OUTR_DLYPRTEN_Msk /*!< Slave output delay protection enable */ +#define HRTIM_OUTR_DLYPRT_Pos (10U) +#define HRTIM_OUTR_DLYPRT_Msk (0x7UL << HRTIM_OUTR_DLYPRT_Pos) /*!< 0x00001C00 */ +#define HRTIM_OUTR_DLYPRT HRTIM_OUTR_DLYPRT_Msk /*!< Slave output delay protection */ +#define HRTIM_OUTR_DLYPRT_0 (0x1UL << HRTIM_OUTR_DLYPRT_Pos) /*!< 0x00000400 */ +#define HRTIM_OUTR_DLYPRT_1 (0x2UL << HRTIM_OUTR_DLYPRT_Pos) /*!< 0x00000800 */ +#define HRTIM_OUTR_DLYPRT_2 (0x4UL << HRTIM_OUTR_DLYPRT_Pos) /*!< 0x00001000 */ +#define HRTIM_OUTR_BIAR_Pos (14U) +#define HRTIM_OUTR_BIAR_Msk (0x1UL << HRTIM_OUTR_BIAR_Pos) /*!< 0x00004000 */ +#define HRTIM_OUTR_BIAR HRTIM_OUTR_BIAR_Msk /*!< Slave output Balanced Idle Automatic resume */ +#define HRTIM_OUTR_POL2_Pos (17U) +#define HRTIM_OUTR_POL2_Msk (0x1UL << HRTIM_OUTR_POL2_Pos) /*!< 0x00020000 */ +#define HRTIM_OUTR_POL2 HRTIM_OUTR_POL2_Msk /*!< Slave output 2 polarity */ +#define HRTIM_OUTR_IDLM2_Pos (18U) +#define HRTIM_OUTR_IDLM2_Msk (0x1UL << HRTIM_OUTR_IDLM2_Pos) /*!< 0x00040000 */ +#define HRTIM_OUTR_IDLM2 HRTIM_OUTR_IDLM2_Msk /*!< Slave output 2 idle mode */ +#define HRTIM_OUTR_IDLES2_Pos (19U) +#define HRTIM_OUTR_IDLES2_Msk (0x1UL << HRTIM_OUTR_IDLES2_Pos) /*!< 0x00080000 */ +#define HRTIM_OUTR_IDLES2 HRTIM_OUTR_IDLES2_Msk /*!< Slave output 2 idle state */ +#define HRTIM_OUTR_FAULT2_Pos (20U) +#define HRTIM_OUTR_FAULT2_Msk (0x3UL << HRTIM_OUTR_FAULT2_Pos) /*!< 0x00300000 */ +#define HRTIM_OUTR_FAULT2 HRTIM_OUTR_FAULT2_Msk /*!< Slave output 2 fault state */ +#define HRTIM_OUTR_FAULT2_0 (0x1UL << HRTIM_OUTR_FAULT2_Pos) /*!< 0x00100000 */ +#define HRTIM_OUTR_FAULT2_1 (0x2UL << HRTIM_OUTR_FAULT2_Pos) /*!< 0x00200000 */ +#define HRTIM_OUTR_CHP2_Pos (22U) +#define HRTIM_OUTR_CHP2_Msk (0x1UL << HRTIM_OUTR_CHP2_Pos) /*!< 0x00400000 */ +#define HRTIM_OUTR_CHP2 HRTIM_OUTR_CHP2_Msk /*!< Slave output 2 chopper enable */ +#define HRTIM_OUTR_DIDL2_Pos (23U) +#define HRTIM_OUTR_DIDL2_Msk (0x1UL << HRTIM_OUTR_DIDL2_Pos) /*!< 0x00800000 */ +#define HRTIM_OUTR_DIDL2 HRTIM_OUTR_DIDL2_Msk /*!< Slave output 2 dead time idle */ + +/**** Bit definition for Timerx Fault register ***************************/ +#define HRTIM_FLTR_FLT1EN_Pos (0U) +#define HRTIM_FLTR_FLT1EN_Msk (0x1UL << HRTIM_FLTR_FLT1EN_Pos) /*!< 0x00000001 */ +#define HRTIM_FLTR_FLT1EN HRTIM_FLTR_FLT1EN_Msk /*!< Fault 1 enable */ +#define HRTIM_FLTR_FLT2EN_Pos (1U) +#define HRTIM_FLTR_FLT2EN_Msk (0x1UL << HRTIM_FLTR_FLT2EN_Pos) /*!< 0x00000002 */ +#define HRTIM_FLTR_FLT2EN HRTIM_FLTR_FLT2EN_Msk /*!< Fault 2 enable */ +#define HRTIM_FLTR_FLT3EN_Pos (2U) +#define HRTIM_FLTR_FLT3EN_Msk (0x1UL << HRTIM_FLTR_FLT3EN_Pos) /*!< 0x00000004 */ +#define HRTIM_FLTR_FLT3EN HRTIM_FLTR_FLT3EN_Msk /*!< Fault 3 enable */ +#define HRTIM_FLTR_FLT4EN_Pos (3U) +#define HRTIM_FLTR_FLT4EN_Msk (0x1UL << HRTIM_FLTR_FLT4EN_Pos) /*!< 0x00000008 */ +#define HRTIM_FLTR_FLT4EN HRTIM_FLTR_FLT4EN_Msk /*!< Fault 4 enable */ +#define HRTIM_FLTR_FLT5EN_Pos (4U) +#define HRTIM_FLTR_FLT5EN_Msk (0x1UL << HRTIM_FLTR_FLT5EN_Pos) /*!< 0x00000010 */ +#define HRTIM_FLTR_FLT5EN HRTIM_FLTR_FLT5EN_Msk /*!< Fault 5 enable */ +#define HRTIM_FLTR_FLT6EN_Pos (5U) +#define HRTIM_FLTR_FLT6EN_Msk (0x1UL << HRTIM_FLTR_FLT6EN_Pos) /*!< 0x00000020 */ +#define HRTIM_FLTR_FLT6EN HRTIM_FLTR_FLT6EN_Msk /*!< Fault 6 enable */ +#define HRTIM_FLTR_FLTLCK_Pos (31U) +#define HRTIM_FLTR_FLTLCK_Msk (0x1UL << HRTIM_FLTR_FLTLCK_Pos) /*!< 0x80000000 */ +#define HRTIM_FLTR_FLTLCK HRTIM_FLTR_FLTLCK_Msk /*!< Fault sources lock */ + +/**** Bit definition for HRTIM Timerx control register 2 ****************/ +#define HRTIM_TIMCR2_DCDE_Pos (0U) +#define HRTIM_TIMCR2_DCDE_Msk (0x1UL << HRTIM_TIMCR2_DCDE_Pos) /*!< 0x00000001 */ +#define HRTIM_TIMCR2_DCDE HRTIM_TIMCR2_DCDE_Msk /*!< Dual Channel DAC trigger enable */ +#define HRTIM_TIMCR2_DCDS_Pos (1U) +#define HRTIM_TIMCR2_DCDS_Msk (0x1UL << HRTIM_TIMCR2_DCDS_Pos) /*!< 0x00000002 */ +#define HRTIM_TIMCR2_DCDS HRTIM_TIMCR2_DCDS_Msk /*!< Dual Channel DAC step trigger */ +#define HRTIM_TIMCR2_DCDR_Pos (2U) +#define HRTIM_TIMCR2_DCDR_Msk (0x1UL << HRTIM_TIMCR2_DCDR_Pos) /*!< 0x00000004 */ +#define HRTIM_TIMCR2_DCDR HRTIM_TIMCR2_DCDR_Msk /*!< Dual Channel DAC reset trigger */ +#define HRTIM_TIMCR2_UDM_Pos (4U) +#define HRTIM_TIMCR2_UDM_Msk (0x1UL << HRTIM_TIMCR2_UDM_Pos) /*!< 0x00000010 */ +#define HRTIM_TIMCR2_UDM HRTIM_TIMCR2_UDM_Msk /*!< Up-Down Mode*/ +#define HRTIM_TIMCR2_ROM_Pos (6U) +#define HRTIM_TIMCR2_ROM_Msk (0x3UL << HRTIM_TIMCR2_ROM_Pos) /*!< 0x000000C0 */ +#define HRTIM_TIMCR2_ROM HRTIM_TIMCR2_ROM_Msk /*!< Roll-over Mode */ +#define HRTIM_TIMCR2_ROM_0 (0x1UL << HRTIM_TIMCR2_ROM_Pos) /*!< 0x00000040 */ +#define HRTIM_TIMCR2_ROM_1 (0x2UL << HRTIM_TIMCR2_ROM_Pos) /*!< 0x00000080 */ +#define HRTIM_TIMCR2_OUTROM_Pos (8U) +#define HRTIM_TIMCR2_OUTROM_Msk (0x3UL << HRTIM_TIMCR2_OUTROM_Pos) /*!< 0x00000300 */ +#define HRTIM_TIMCR2_OUTROM HRTIM_TIMCR2_OUTROM_Msk /*!< Output Roll-Over Mode */ +#define HRTIM_TIMCR2_OUTROM_0 (0x1UL << HRTIM_TIMCR2_OUTROM_Pos) /*!< 0x00000100 */ +#define HRTIM_TIMCR2_OUTROM_1 (0x2UL << HRTIM_TIMCR2_OUTROM_Pos) /*!< 0x00000200 */ +#define HRTIM_TIMCR2_ADROM_Pos (10U) +#define HRTIM_TIMCR2_ADROM_Msk (0x3UL << HRTIM_TIMCR2_ADROM_Pos) /*!< 0x00000C00 */ +#define HRTIM_TIMCR2_ADROM HRTIM_TIMCR2_ADROM_Msk /*!< ADC Roll-Over Mode */ +#define HRTIM_TIMCR2_ADROM_0 (0x1UL << HRTIM_TIMCR2_ADROM_Pos) /*!< 0x00000400 */ +#define HRTIM_TIMCR2_ADROM_1 (0x2UL << HRTIM_TIMCR2_ADROM_Pos) /*!< 0x00000800 */ +#define HRTIM_TIMCR2_BMROM_Pos (12U) +#define HRTIM_TIMCR2_BMROM_Msk (0x3UL << HRTIM_TIMCR2_BMROM_Pos) /*!< 0x00003000 */ +#define HRTIM_TIMCR2_BMROM HRTIM_TIMCR2_BMROM_Msk /*!< Burst Mode Rollover Mode */ +#define HRTIM_TIMCR2_BMROM_0 (0x1UL << HRTIM_TIMCR2_BMROM_Pos) /*!< 0x00001000 */ +#define HRTIM_TIMCR2_BMROM_1 (0x2UL << HRTIM_TIMCR2_BMROM_Pos) /*!< 0x00002000 */ +#define HRTIM_TIMCR2_FEROM_Pos (14U) +#define HRTIM_TIMCR2_FEROM_Msk (0x3UL << HRTIM_TIMCR2_FEROM_Pos) /*!< 0x0000C000 */ +#define HRTIM_TIMCR2_FEROM HRTIM_TIMCR2_FEROM_Msk /*!< Fault and Event Rollover Mode */ +#define HRTIM_TIMCR2_FEROM_0 (0x1UL << HRTIM_TIMCR2_FEROM_Pos) /*!< 0x00004000 */ +#define HRTIM_TIMCR2_FEROM_1 (0x2UL << HRTIM_TIMCR2_FEROM_Pos) /*!< 0x00008000 */ +#define HRTIM_TIMCR2_GTCMP1_Pos (16U) +#define HRTIM_TIMCR2_GTCMP1_Msk (0x1UL << HRTIM_TIMCR2_GTCMP1_Pos) /*!< 0x00010000 */ +#define HRTIM_TIMCR2_GTCMP1 HRTIM_TIMCR2_GTCMP1_Msk /*!< Greater than Compare 1 PWM mode */ +#define HRTIM_TIMCR2_GTCMP3_Pos (17U) +#define HRTIM_TIMCR2_GTCMP3_Msk (0x1UL << HRTIM_TIMCR2_GTCMP3_Pos) /*!< 0x00020000 */ +#define HRTIM_TIMCR2_GTCMP3 HRTIM_TIMCR2_GTCMP3_Msk /*!< Greater than Compare 3 PWM mode */ +#define HRTIM_TIMCR2_TRGHLF_Pos (20U) +#define HRTIM_TIMCR2_TRGHLF_Msk (0x1UL << HRTIM_TIMCR2_TRGHLF_Pos) /*!< 0x00100000 */ +#define HRTIM_TIMCR2_TRGHLF HRTIM_TIMCR2_TRGHLF_Msk /*!< Triggered-Half mode */ + +/**** Bit definition for Slave external event filtering register 3 ***********/ +#define HRTIM_EEFR3_EEVACE_Pos (0U) +#define HRTIM_EEFR3_EEVACE_Msk (0x1UL << HRTIM_EEFR3_EEVACE_Pos) /*!< 0x00000001 */ +#define HRTIM_EEFR3_EEVACE HRTIM_EEFR3_EEVACE_Msk /*!< External Event A Counter Enable */ +#define HRTIM_EEFR3_EEVACRES_Pos (1U) +#define HRTIM_EEFR3_EEVACRES_Msk (0x1UL << HRTIM_EEFR3_EEVACRES_Pos) /*!< 0x00000002 */ +#define HRTIM_EEFR3_EEVACRES HRTIM_EEFR3_EEVACRES_Msk /*!< External Event A Counter Reset */ +#define HRTIM_EEFR3_EEVARSTM_Pos (2U) +#define HRTIM_EEFR3_EEVARSTM_Msk (0x1UL << HRTIM_EEFR3_EEVARSTM_Pos) /*!< 0x00000004 */ +#define HRTIM_EEFR3_EEVARSTM HRTIM_EEFR3_EEVARSTM_Msk /*!< External Event A Counter Reset Mode */ +#define HRTIM_EEFR3_EEVASEL_Pos (4U) +#define HRTIM_EEFR3_EEVASEL_Msk (0xFUL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x000000F0 */ +#define HRTIM_EEFR3_EEVASEL HRTIM_EEFR3_EEVASEL_Msk /*!< External Event A Selection */ +#define HRTIM_EEFR3_EEVASEL_0 (0x1UL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x00000010 */ +#define HRTIM_EEFR3_EEVASEL_1 (0x2UL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x00000020 */ +#define HRTIM_EEFR3_EEVASEL_2 (0x4UL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x00000040 */ +#define HRTIM_EEFR3_EEVASEL_3 (0x8UL << HRTIM_EEFR3_EEVASEL_Pos) /*!< 0x00000080 */ +#define HRTIM_EEFR3_EEVACNT_Pos (8U) +#define HRTIM_EEFR3_EEVACNT_Msk (0x3FUL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00003F00 */ +#define HRTIM_EEFR3_EEVACNT HRTIM_EEFR3_EEVACNT_Msk /*!< External Event A Selection */ +#define HRTIM_EEFR3_EEVACNT_0 (0x1UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00000100 */ +#define HRTIM_EEFR3_EEVACNT_1 (0x2UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00000200 */ +#define HRTIM_EEFR3_EEVACNT_2 (0x4UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00000400 */ +#define HRTIM_EEFR3_EEVACNT_3 (0x8UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00000800 */ +#define HRTIM_EEFR3_EEVACNT_4 (0x10UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00001000 */ +#define HRTIM_EEFR3_EEVACNT_5 (0x20UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x00002000 */ +#define HRTIM_EEFR3_EEVBCE_Pos (16U) +#define HRTIM_EEFR3_EEVBCE_Msk (0x1UL << HRTIM_EEFR3_EEVBCE_Pos) /*!< 0x00010000 */ +#define HRTIM_EEFR3_EEVBCE HRTIM_EEFR3_EEVBCE_Msk /*!< External Event B Counter Enable */ +#define HRTIM_EEFR3_EEVBCRES_Pos (17U) +#define HRTIM_EEFR3_EEVBCRES_Msk (0x1UL << HRTIM_EEFR3_EEVBCRES_Pos) /*!< 0x00020000 */ +#define HRTIM_EEFR3_EEVBCRES HRTIM_EEFR3_EEVBCRES_Msk /*!< External Event B Counter Reset */ +#define HRTIM_EEFR3_EEVBRSTM_Pos (18U) +#define HRTIM_EEFR3_EEVBRSTM_Msk (0x1UL << HRTIM_EEFR3_EEVBRSTM_Pos) /*!< 0x00040000 */ +#define HRTIM_EEFR3_EEVBRSTM HRTIM_EEFR3_EEVBRSTM_Msk /*!< External Event B Counter Reset Mode */ +#define HRTIM_EEFR3_EEVBSEL_Pos (20U) +#define HRTIM_EEFR3_EEVBSEL_Msk (0xFUL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00F00000 */ +#define HRTIM_EEFR3_EEVBSEL HRTIM_EEFR3_EEVBSEL_Msk /*!< External Event B Selection */ +#define HRTIM_EEFR3_EEVBSEL_0 (0x1UL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00100000 */ +#define HRTIM_EEFR3_EEVBSEL_1 (0x2UL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00200000 */ +#define HRTIM_EEFR3_EEVBSEL_2 (0x4UL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00400000 */ +#define HRTIM_EEFR3_EEVBSEL_3 (0x8UL << HRTIM_EEFR3_EEVBSEL_Pos) /*!< 0x00800000 */ +#define HRTIM_EEFR3_EEVBCNT_Pos (24U) +#define HRTIM_EEFR3_EEVBCNT_Msk (0x3FUL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x3F000000 */ +#define HRTIM_EEFR3_EEVBCNT HRTIM_EEFR3_EEVBCNT_Msk /*!< External Event B Counter */ +#define HRTIM_EEFR3_EEVBCNT_0 (0x1UL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x01000000 */ +#define HRTIM_EEFR3_EEVBCNT_1 (0x2UL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x02000000 */ +#define HRTIM_EEFR3_EEVBCNT_2 (0x4UL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x04000000 */ +#define HRTIM_EEFR3_EEVBCNT_3 (0x8UL << HRTIM_EEFR3_EEVBCNT_Pos) /*!< 0x08000000 */ +#define HRTIM_EEFR3_EEVBCNT_4 (0x10UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x10000000 */ +#define HRTIM_EEFR3_EEVBCNT_5 (0x20UL << HRTIM_EEFR3_EEVACNT_Pos) /*!< 0x20000000 */ + +/**** Bit definition for Common HRTIM Timer control register 1 ****************/ +#define HRTIM_CR1_MUDIS_Pos (0U) +#define HRTIM_CR1_MUDIS_Msk (0x1UL << HRTIM_CR1_MUDIS_Pos) /*!< 0x00000001 */ +#define HRTIM_CR1_MUDIS HRTIM_CR1_MUDIS_Msk /*!< Master update disable*/ +#define HRTIM_CR1_TAUDIS_Pos (1U) +#define HRTIM_CR1_TAUDIS_Msk (0x1UL << HRTIM_CR1_TAUDIS_Pos) /*!< 0x00000002 */ +#define HRTIM_CR1_TAUDIS HRTIM_CR1_TAUDIS_Msk /*!< Timer A update disable*/ +#define HRTIM_CR1_TBUDIS_Pos (2U) +#define HRTIM_CR1_TBUDIS_Msk (0x1UL << HRTIM_CR1_TBUDIS_Pos) /*!< 0x00000004 */ +#define HRTIM_CR1_TBUDIS HRTIM_CR1_TBUDIS_Msk /*!< Timer B update disable*/ +#define HRTIM_CR1_TCUDIS_Pos (3U) +#define HRTIM_CR1_TCUDIS_Msk (0x1UL << HRTIM_CR1_TCUDIS_Pos) /*!< 0x00000008 */ +#define HRTIM_CR1_TCUDIS HRTIM_CR1_TCUDIS_Msk /*!< Timer C update disable*/ +#define HRTIM_CR1_TDUDIS_Pos (4U) +#define HRTIM_CR1_TDUDIS_Msk (0x1UL << HRTIM_CR1_TDUDIS_Pos) /*!< 0x00000010 */ +#define HRTIM_CR1_TDUDIS HRTIM_CR1_TDUDIS_Msk /*!< Timer D update disable*/ +#define HRTIM_CR1_TEUDIS_Pos (5U) +#define HRTIM_CR1_TEUDIS_Msk (0x1UL << HRTIM_CR1_TEUDIS_Pos) /*!< 0x00000020 */ +#define HRTIM_CR1_TEUDIS HRTIM_CR1_TEUDIS_Msk /*!< Timer E update disable*/ +#define HRTIM_CR1_TFUDIS_Pos (6U) +#define HRTIM_CR1_TFUDIS_Msk (0x1UL << HRTIM_CR1_TFUDIS_Pos) /*!< 0x00000040 */ +#define HRTIM_CR1_TFUDIS HRTIM_CR1_TFUDIS_Msk /*!< Timer F update disable*/ +#define HRTIM_CR1_ADC1USRC_Pos (16U) +#define HRTIM_CR1_ADC1USRC_Msk (0x7UL << HRTIM_CR1_ADC1USRC_Pos) /*!< 0x00070000 */ +#define HRTIM_CR1_ADC1USRC HRTIM_CR1_ADC1USRC_Msk /*!< ADC Trigger 1 update source */ +#define HRTIM_CR1_ADC1USRC_0 (0x1UL << HRTIM_CR1_ADC1USRC_Pos) /*!< 0x00010000 */ +#define HRTIM_CR1_ADC1USRC_1 (0x2UL << HRTIM_CR1_ADC1USRC_Pos) /*!< 0x00020000 */ +#define HRTIM_CR1_ADC1USRC_2 (0x4UL << HRTIM_CR1_ADC1USRC_Pos) /*!< 0x00040000 */ +#define HRTIM_CR1_ADC2USRC_Pos (19U) +#define HRTIM_CR1_ADC2USRC_Msk (0x7UL << HRTIM_CR1_ADC2USRC_Pos) /*!< 0x00380000 */ +#define HRTIM_CR1_ADC2USRC HRTIM_CR1_ADC2USRC_Msk /*!< ADC Trigger 2 update source */ +#define HRTIM_CR1_ADC2USRC_0 (0x1UL << HRTIM_CR1_ADC2USRC_Pos) /*!< 0x00080000 */ +#define HRTIM_CR1_ADC2USRC_1 (0x2UL << HRTIM_CR1_ADC2USRC_Pos) /*!< 0x00100000 */ +#define HRTIM_CR1_ADC2USRC_2 (0x4UL << HRTIM_CR1_ADC2USRC_Pos) /*!< 0x00200000 */ +#define HRTIM_CR1_ADC3USRC_Pos (22U) +#define HRTIM_CR1_ADC3USRC_Msk (0x7UL << HRTIM_CR1_ADC3USRC_Pos) /*!< 0x01C00000 */ +#define HRTIM_CR1_ADC3USRC HRTIM_CR1_ADC3USRC_Msk /*!< ADC Trigger 3 update source */ +#define HRTIM_CR1_ADC3USRC_0 (0x1UL << HRTIM_CR1_ADC3USRC_Pos) /*!< 0x00400000 */ +#define HRTIM_CR1_ADC3USRC_1 (0x2UL << HRTIM_CR1_ADC3USRC_Pos) /*!< 0x00800000 */ +#define HRTIM_CR1_ADC3USRC_2 (0x4UL << HRTIM_CR1_ADC3USRC_Pos) /*!< 0x01000000 */ +#define HRTIM_CR1_ADC4USRC_Pos (25U) +#define HRTIM_CR1_ADC4USRC_Msk (0x7UL << HRTIM_CR1_ADC4USRC_Pos) /*!< 0x0E000000 */ +#define HRTIM_CR1_ADC4USRC HRTIM_CR1_ADC4USRC_Msk /*!< ADC Trigger 4 update source */ +#define HRTIM_CR1_ADC4USRC_0 (0x1UL << HRTIM_CR1_ADC4USRC_Pos) /*!< 0x02000000 */ +#define HRTIM_CR1_ADC4USRC_1 (0x2UL << HRTIM_CR1_ADC4USRC_Pos) /*!< 0x04000000 */ +#define HRTIM_CR1_ADC4USRC_2 (0x0UL << HRTIM_CR1_ADC4USRC_Pos) /*!< 0x0800000 */ + +/**** Bit definition for Common HRTIM Timer control register 2 ****************/ +#define HRTIM_CR2_MSWU_Pos (0U) +#define HRTIM_CR2_MSWU_Msk (0x1UL << HRTIM_CR2_MSWU_Pos) /*!< 0x00000001 */ +#define HRTIM_CR2_MSWU HRTIM_CR2_MSWU_Msk /*!< Master software update */ +#define HRTIM_CR2_TASWU_Pos (1U) +#define HRTIM_CR2_TASWU_Msk (0x1UL << HRTIM_CR2_TASWU_Pos) /*!< 0x00000002 */ +#define HRTIM_CR2_TASWU HRTIM_CR2_TASWU_Msk /*!< Timer A software update */ +#define HRTIM_CR2_TBSWU_Pos (2U) +#define HRTIM_CR2_TBSWU_Msk (0x1UL << HRTIM_CR2_TBSWU_Pos) /*!< 0x00000004 */ +#define HRTIM_CR2_TBSWU HRTIM_CR2_TBSWU_Msk /*!< Timer B software update */ +#define HRTIM_CR2_TCSWU_Pos (3U) +#define HRTIM_CR2_TCSWU_Msk (0x1UL << HRTIM_CR2_TCSWU_Pos) /*!< 0x00000008 */ +#define HRTIM_CR2_TCSWU HRTIM_CR2_TCSWU_Msk /*!< Timer C software update */ +#define HRTIM_CR2_TDSWU_Pos (4U) +#define HRTIM_CR2_TDSWU_Msk (0x1UL << HRTIM_CR2_TDSWU_Pos) /*!< 0x00000010 */ +#define HRTIM_CR2_TDSWU HRTIM_CR2_TDSWU_Msk /*!< Timer D software update */ +#define HRTIM_CR2_TESWU_Pos (5U) +#define HRTIM_CR2_TESWU_Msk (0x1UL << HRTIM_CR2_TESWU_Pos) /*!< 0x00000020 */ +#define HRTIM_CR2_TESWU HRTIM_CR2_TESWU_Msk /*!< Timer E software update */ +#define HRTIM_CR2_TFSWU_Pos (6U) +#define HRTIM_CR2_TFSWU_Msk (0x1UL << HRTIM_CR2_TFSWU_Pos) /*!< 0x00000040 */ +#define HRTIM_CR2_TFSWU HRTIM_CR2_TFSWU_Msk /*!< Timer F software update */ +#define HRTIM_CR2_MRST_Pos (8U) +#define HRTIM_CR2_MRST_Msk (0x1UL << HRTIM_CR2_MRST_Pos) /*!< 0x00000100 */ +#define HRTIM_CR2_MRST HRTIM_CR2_MRST_Msk /*!< Master count software reset */ +#define HRTIM_CR2_TARST_Pos (9U) +#define HRTIM_CR2_TARST_Msk (0x1UL << HRTIM_CR2_TARST_Pos) /*!< 0x00000200 */ +#define HRTIM_CR2_TARST HRTIM_CR2_TARST_Msk /*!< Timer A count software reset */ +#define HRTIM_CR2_TBRST_Pos (10U) +#define HRTIM_CR2_TBRST_Msk (0x1UL << HRTIM_CR2_TBRST_Pos) /*!< 0x00000400 */ +#define HRTIM_CR2_TBRST HRTIM_CR2_TBRST_Msk /*!< Timer B count software reset */ +#define HRTIM_CR2_TCRST_Pos (11U) +#define HRTIM_CR2_TCRST_Msk (0x1UL << HRTIM_CR2_TCRST_Pos) /*!< 0x00000800 */ +#define HRTIM_CR2_TCRST HRTIM_CR2_TCRST_Msk /*!< Timer C count software reset */ +#define HRTIM_CR2_TDRST_Pos (12U) +#define HRTIM_CR2_TDRST_Msk (0x1UL << HRTIM_CR2_TDRST_Pos) /*!< 0x00001000 */ +#define HRTIM_CR2_TDRST HRTIM_CR2_TDRST_Msk /*!< Timer D count software reset */ +#define HRTIM_CR2_TERST_Pos (13U) +#define HRTIM_CR2_TERST_Msk (0x1UL << HRTIM_CR2_TERST_Pos) /*!< 0x00002000 */ +#define HRTIM_CR2_TERST HRTIM_CR2_TERST_Msk /*!< Timer E count software reset */ +#define HRTIM_CR2_TFRST_Pos (14U) +#define HRTIM_CR2_TFRST_Msk (0x1UL << HRTIM_CR2_TFRST_Pos) /*!< 0x00004000 */ +#define HRTIM_CR2_TFRST HRTIM_CR2_TFRST_Msk /*!< Timer F count software reset */ +#define HRTIM_CR2_SWPA_Pos (16U) +#define HRTIM_CR2_SWPA_Msk (0x1UL << HRTIM_CR2_SWPA_Pos) /*!< 0x00010000 */ +#define HRTIM_CR2_SWPA HRTIM_CR2_SWPA_Msk /*!< Timer A swap outputs */ +#define HRTIM_CR2_SWPB_Pos (17U) +#define HRTIM_CR2_SWPB_Msk (0x1UL << HRTIM_CR2_SWPB_Pos) /*!< 0x00020000 */ +#define HRTIM_CR2_SWPB HRTIM_CR2_SWPB_Msk /*!< Timer B swap outputs */ +#define HRTIM_CR2_SWPC_Pos (18U) +#define HRTIM_CR2_SWPC_Msk (0x1UL << HRTIM_CR2_SWPC_Pos) /*!< 0x00040000 */ +#define HRTIM_CR2_SWPC HRTIM_CR2_SWPC_Msk /*!< Timer C swap outputs */ +#define HRTIM_CR2_SWPD_Pos (19U) +#define HRTIM_CR2_SWPD_Msk (0x1UL << HRTIM_CR2_SWPD_Pos) /*!< 0x00080000 */ +#define HRTIM_CR2_SWPD HRTIM_CR2_SWPD_Msk /*!< Timer D swap outputs */ +#define HRTIM_CR2_SWPE_Pos (20U) +#define HRTIM_CR2_SWPE_Msk (0x1UL << HRTIM_CR2_SWPE_Pos) /*!< 0x00100000 */ +#define HRTIM_CR2_SWPE HRTIM_CR2_SWPE_Msk /*!< Timer E swap outputs */ +#define HRTIM_CR2_SWPF_Pos (21U) +#define HRTIM_CR2_SWPF_Msk (0x1UL << HRTIM_CR2_SWPF_Pos) /*!< 0x00200000 */ +#define HRTIM_CR2_SWPF HRTIM_CR2_SWPF_Msk /*!< Timer F swap outputs */ + +/**** Bit definition for Common HRTIM Timer interrupt status register *********/ +#define HRTIM_ISR_FLT1_Pos (0U) +#define HRTIM_ISR_FLT1_Msk (0x1UL << HRTIM_ISR_FLT1_Pos) /*!< 0x00000001 */ +#define HRTIM_ISR_FLT1 HRTIM_ISR_FLT1_Msk /*!< Fault 1 interrupt flag */ +#define HRTIM_ISR_FLT2_Pos (1U) +#define HRTIM_ISR_FLT2_Msk (0x1UL << HRTIM_ISR_FLT2_Pos) /*!< 0x00000002 */ +#define HRTIM_ISR_FLT2 HRTIM_ISR_FLT2_Msk /*!< Fault 2 interrupt flag */ +#define HRTIM_ISR_FLT3_Pos (2U) +#define HRTIM_ISR_FLT3_Msk (0x1UL << HRTIM_ISR_FLT3_Pos) /*!< 0x00000004 */ +#define HRTIM_ISR_FLT3 HRTIM_ISR_FLT3_Msk /*!< Fault 3 interrupt flag */ +#define HRTIM_ISR_FLT4_Pos (3U) +#define HRTIM_ISR_FLT4_Msk (0x1UL << HRTIM_ISR_FLT4_Pos) /*!< 0x00000008 */ +#define HRTIM_ISR_FLT4 HRTIM_ISR_FLT4_Msk /*!< Fault 4 interrupt flag */ +#define HRTIM_ISR_FLT5_Pos (4U) +#define HRTIM_ISR_FLT5_Msk (0x1UL << HRTIM_ISR_FLT5_Pos) /*!< 0x00000010 */ +#define HRTIM_ISR_FLT5 HRTIM_ISR_FLT5_Msk /*!< Fault 5 interrupt flag */ +#define HRTIM_ISR_SYSFLT_Pos (5U) +#define HRTIM_ISR_SYSFLT_Msk (0x1UL << HRTIM_ISR_SYSFLT_Pos) /*!< 0x00000020 */ +#define HRTIM_ISR_SYSFLT HRTIM_ISR_SYSFLT_Msk /*!< System Fault interrupt flag */ +#define HRTIM_ISR_FLT6_Pos (6U) +#define HRTIM_ISR_FLT6_Msk (0x1UL << HRTIM_ISR_FLT6_Pos) /*!< 0x00000040 */ +#define HRTIM_ISR_FLT6 HRTIM_ISR_FLT6_Msk /*!< Fault 6 interrupt flag */ +#define HRTIM_ISR_DLLRDY_Pos (16U) +#define HRTIM_ISR_DLLRDY_Msk (0x1UL << HRTIM_ISR_DLLRDY_Pos) /*!< 0x00010000 */ +#define HRTIM_ISR_DLLRDY HRTIM_ISR_DLLRDY_Msk /*!< DLL ready interrupt flag */ +#define HRTIM_ISR_BMPER_Pos (17U) +#define HRTIM_ISR_BMPER_Msk (0x1UL << HRTIM_ISR_BMPER_Pos) /*!< 0x00020000 */ +#define HRTIM_ISR_BMPER HRTIM_ISR_BMPER_Msk /*!< Burst mode period interrupt flag */ + +/**** Bit definition for Common HRTIM Timer interrupt clear register **********/ +#define HRTIM_ICR_FLT1C_Pos (0U) +#define HRTIM_ICR_FLT1C_Msk (0x1UL << HRTIM_ICR_FLT1C_Pos) /*!< 0x00000001 */ +#define HRTIM_ICR_FLT1C HRTIM_ICR_FLT1C_Msk /*!< Fault 1 interrupt flag clear */ +#define HRTIM_ICR_FLT2C_Pos (1U) +#define HRTIM_ICR_FLT2C_Msk (0x1UL << HRTIM_ICR_FLT2C_Pos) /*!< 0x00000002 */ +#define HRTIM_ICR_FLT2C HRTIM_ICR_FLT2C_Msk /*!< Fault 2 interrupt flag clear */ +#define HRTIM_ICR_FLT3C_Pos (2U) +#define HRTIM_ICR_FLT3C_Msk (0x1UL << HRTIM_ICR_FLT3C_Pos) /*!< 0x00000004 */ +#define HRTIM_ICR_FLT3C HRTIM_ICR_FLT3C_Msk /*!< Fault 3 interrupt flag clear */ +#define HRTIM_ICR_FLT4C_Pos (3U) +#define HRTIM_ICR_FLT4C_Msk (0x1UL << HRTIM_ICR_FLT4C_Pos) /*!< 0x00000008 */ +#define HRTIM_ICR_FLT4C HRTIM_ICR_FLT4C_Msk /*!< Fault 4 interrupt flag clear */ +#define HRTIM_ICR_FLT5C_Pos (4U) +#define HRTIM_ICR_FLT5C_Msk (0x1UL << HRTIM_ICR_FLT5C_Pos) /*!< 0x00000010 */ +#define HRTIM_ICR_FLT5C HRTIM_ICR_FLT5C_Msk /*!< Fault 5 interrupt flag clear */ +#define HRTIM_ICR_SYSFLTC_Pos (5U) +#define HRTIM_ICR_SYSFLTC_Msk (0x1UL << HRTIM_ICR_SYSFLTC_Pos) /*!< 0x00000020 */ +#define HRTIM_ICR_SYSFLTC HRTIM_ICR_SYSFLTC_Msk /*!< System Fault interrupt flag clear */ + +#define HRTIM_ICR_FLT6C_Pos (6U) +#define HRTIM_ICR_FLT6C_Msk (0x1UL << HRTIM_ICR_FLT6C_Pos) /*!< 0x00000040 */ +#define HRTIM_ICR_FLT6C HRTIM_ICR_FLT6C_Msk /*!< Fault 6 interrupt flag clear */ + +#define HRTIM_ICR_DLLRDYC_Pos (16U) +#define HRTIM_ICR_DLLRDYC_Msk (0x1UL << HRTIM_ICR_DLLRDYC_Pos) /*!< 0x00010000 */ +#define HRTIM_ICR_DLLRDYC HRTIM_ICR_DLLRDYC_Msk /*!< DLL ready interrupt flag clear */ +#define HRTIM_ICR_BMPERC_Pos (17U) +#define HRTIM_ICR_BMPERC_Msk (0x1UL << HRTIM_ICR_BMPERC_Pos) /*!< 0x00020000 */ +#define HRTIM_ICR_BMPERC HRTIM_ICR_BMPERC_Msk /*!< Burst mode period interrupt flag clear */ + +/**** Bit definition for Common HRTIM Timer interrupt enable register *********/ +#define HRTIM_IER_FLT1_Pos (0U) +#define HRTIM_IER_FLT1_Msk (0x1UL << HRTIM_IER_FLT1_Pos) /*!< 0x00000001 */ +#define HRTIM_IER_FLT1 HRTIM_IER_FLT1_Msk /*!< Fault 1 interrupt enable */ +#define HRTIM_IER_FLT2_Pos (1U) +#define HRTIM_IER_FLT2_Msk (0x1UL << HRTIM_IER_FLT2_Pos) /*!< 0x00000002 */ +#define HRTIM_IER_FLT2 HRTIM_IER_FLT2_Msk /*!< Fault 2 interrupt enable */ +#define HRTIM_IER_FLT3_Pos (2U) +#define HRTIM_IER_FLT3_Msk (0x1UL << HRTIM_IER_FLT3_Pos) /*!< 0x00000004 */ +#define HRTIM_IER_FLT3 HRTIM_IER_FLT3_Msk /*!< Fault 3 interrupt enable */ +#define HRTIM_IER_FLT4_Pos (3U) +#define HRTIM_IER_FLT4_Msk (0x1UL << HRTIM_IER_FLT4_Pos) /*!< 0x00000008 */ +#define HRTIM_IER_FLT4 HRTIM_IER_FLT4_Msk /*!< Fault 4 interrupt enable */ +#define HRTIM_IER_FLT5_Pos (4U) +#define HRTIM_IER_FLT5_Msk (0x1UL << HRTIM_IER_FLT5_Pos) /*!< 0x00000010 */ +#define HRTIM_IER_FLT5 HRTIM_IER_FLT5_Msk /*!< Fault 5 interrupt enable */ +#define HRTIM_IER_SYSFLT_Pos (5U) +#define HRTIM_IER_SYSFLT_Msk (0x1UL << HRTIM_IER_SYSFLT_Pos) /*!< 0x00000020 */ +#define HRTIM_IER_SYSFLT HRTIM_IER_SYSFLT_Msk /*!< System Fault interrupt enable */ +#define HRTIM_IER_FLT6_Pos (6U) +#define HRTIM_IER_FLT6_Msk (0x1UL << HRTIM_IER_FLT6_Pos) /*!< 0x00000040 */ +#define HRTIM_IER_FLT6 HRTIM_IER_FLT6_Msk /*!< Fault 6 interrupt enable */ + +#define HRTIM_IER_DLLRDY_Pos (16U) +#define HRTIM_IER_DLLRDY_Msk (0x1UL << HRTIM_IER_DLLRDY_Pos) /*!< 0x00010000 */ +#define HRTIM_IER_DLLRDY HRTIM_IER_DLLRDY_Msk /*!< DLL ready interrupt enable */ +#define HRTIM_IER_BMPER_Pos (17U) +#define HRTIM_IER_BMPER_Msk (0x1UL << HRTIM_IER_BMPER_Pos) /*!< 0x00020000 */ +#define HRTIM_IER_BMPER HRTIM_IER_BMPER_Msk /*!< Burst mode period interrupt enable */ + +/**** Bit definition for Common HRTIM Timer output enable register ************/ +#define HRTIM_OENR_TA1OEN_Pos (0U) +#define HRTIM_OENR_TA1OEN_Msk (0x1UL << HRTIM_OENR_TA1OEN_Pos) /*!< 0x00000001 */ +#define HRTIM_OENR_TA1OEN HRTIM_OENR_TA1OEN_Msk /*!< Timer A Output 1 enable */ +#define HRTIM_OENR_TA2OEN_Pos (1U) +#define HRTIM_OENR_TA2OEN_Msk (0x1UL << HRTIM_OENR_TA2OEN_Pos) /*!< 0x00000002 */ +#define HRTIM_OENR_TA2OEN HRTIM_OENR_TA2OEN_Msk /*!< Timer A Output 2 enable */ +#define HRTIM_OENR_TB1OEN_Pos (2U) +#define HRTIM_OENR_TB1OEN_Msk (0x1UL << HRTIM_OENR_TB1OEN_Pos) /*!< 0x00000004 */ +#define HRTIM_OENR_TB1OEN HRTIM_OENR_TB1OEN_Msk /*!< Timer B Output 1 enable */ +#define HRTIM_OENR_TB2OEN_Pos (3U) +#define HRTIM_OENR_TB2OEN_Msk (0x1UL << HRTIM_OENR_TB2OEN_Pos) /*!< 0x00000008 */ +#define HRTIM_OENR_TB2OEN HRTIM_OENR_TB2OEN_Msk /*!< Timer B Output 2 enable */ +#define HRTIM_OENR_TC1OEN_Pos (4U) +#define HRTIM_OENR_TC1OEN_Msk (0x1UL << HRTIM_OENR_TC1OEN_Pos) /*!< 0x00000010 */ +#define HRTIM_OENR_TC1OEN HRTIM_OENR_TC1OEN_Msk /*!< Timer C Output 1 enable */ +#define HRTIM_OENR_TC2OEN_Pos (5U) +#define HRTIM_OENR_TC2OEN_Msk (0x1UL << HRTIM_OENR_TC2OEN_Pos) /*!< 0x00000020 */ +#define HRTIM_OENR_TC2OEN HRTIM_OENR_TC2OEN_Msk /*!< Timer C Output 2 enable */ +#define HRTIM_OENR_TD1OEN_Pos (6U) +#define HRTIM_OENR_TD1OEN_Msk (0x1UL << HRTIM_OENR_TD1OEN_Pos) /*!< 0x00000040 */ +#define HRTIM_OENR_TD1OEN HRTIM_OENR_TD1OEN_Msk /*!< Timer D Output 1 enable */ +#define HRTIM_OENR_TD2OEN_Pos (7U) +#define HRTIM_OENR_TD2OEN_Msk (0x1UL << HRTIM_OENR_TD2OEN_Pos) /*!< 0x00000080 */ +#define HRTIM_OENR_TD2OEN HRTIM_OENR_TD2OEN_Msk /*!< Timer D Output 2 enable */ +#define HRTIM_OENR_TE1OEN_Pos (8U) +#define HRTIM_OENR_TE1OEN_Msk (0x1UL << HRTIM_OENR_TE1OEN_Pos) /*!< 0x00000100 */ +#define HRTIM_OENR_TE1OEN HRTIM_OENR_TE1OEN_Msk /*!< Timer E Output 1 enable */ +#define HRTIM_OENR_TE2OEN_Pos (9U) +#define HRTIM_OENR_TE2OEN_Msk (0x1UL << HRTIM_OENR_TE2OEN_Pos) /*!< 0x00000200 */ +#define HRTIM_OENR_TE2OEN HRTIM_OENR_TE2OEN_Msk /*!< Timer E Output 2 enable */ +#define HRTIM_OENR_TF1OEN_Pos (10U) +#define HRTIM_OENR_TF1OEN_Msk (0x1UL << HRTIM_OENR_TF1OEN_Pos) /*!< 0x00000400 */ +#define HRTIM_OENR_TF1OEN HRTIM_OENR_TF1OEN_Msk /*!< Timer F Output 1 enable */ +#define HRTIM_OENR_TF2OEN_Pos (11U) +#define HRTIM_OENR_TF2OEN_Msk (0x1UL << HRTIM_OENR_TF2OEN_Pos) /*!< 0x00000800 */ +#define HRTIM_OENR_TF2OEN HRTIM_OENR_TF2OEN_Msk /*!< Timer F Output 2 enable */ + +/**** Bit definition for Common HRTIM Timer output disable register ***********/ +#define HRTIM_ODISR_TA1ODIS_Pos (0U) +#define HRTIM_ODISR_TA1ODIS_Msk (0x1UL << HRTIM_ODISR_TA1ODIS_Pos) /*!< 0x00000001 */ +#define HRTIM_ODISR_TA1ODIS HRTIM_ODISR_TA1ODIS_Msk /*!< Timer A Output 1 disable */ +#define HRTIM_ODISR_TA2ODIS_Pos (1U) +#define HRTIM_ODISR_TA2ODIS_Msk (0x1UL << HRTIM_ODISR_TA2ODIS_Pos) /*!< 0x00000002 */ +#define HRTIM_ODISR_TA2ODIS HRTIM_ODISR_TA2ODIS_Msk /*!< Timer A Output 2 disable */ +#define HRTIM_ODISR_TB1ODIS_Pos (2U) +#define HRTIM_ODISR_TB1ODIS_Msk (0x1UL << HRTIM_ODISR_TB1ODIS_Pos) /*!< 0x00000004 */ +#define HRTIM_ODISR_TB1ODIS HRTIM_ODISR_TB1ODIS_Msk /*!< Timer B Output 1 disable */ +#define HRTIM_ODISR_TB2ODIS_Pos (3U) +#define HRTIM_ODISR_TB2ODIS_Msk (0x1UL << HRTIM_ODISR_TB2ODIS_Pos) /*!< 0x00000008 */ +#define HRTIM_ODISR_TB2ODIS HRTIM_ODISR_TB2ODIS_Msk /*!< Timer B Output 2 disable */ +#define HRTIM_ODISR_TC1ODIS_Pos (4U) +#define HRTIM_ODISR_TC1ODIS_Msk (0x1UL << HRTIM_ODISR_TC1ODIS_Pos) /*!< 0x00000010 */ +#define HRTIM_ODISR_TC1ODIS HRTIM_ODISR_TC1ODIS_Msk /*!< Timer C Output 1 disable */ +#define HRTIM_ODISR_TC2ODIS_Pos (5U) +#define HRTIM_ODISR_TC2ODIS_Msk (0x1UL << HRTIM_ODISR_TC2ODIS_Pos) /*!< 0x00000020 */ +#define HRTIM_ODISR_TC2ODIS HRTIM_ODISR_TC2ODIS_Msk /*!< Timer C Output 2 disable */ +#define HRTIM_ODISR_TD1ODIS_Pos (6U) +#define HRTIM_ODISR_TD1ODIS_Msk (0x1UL << HRTIM_ODISR_TD1ODIS_Pos) /*!< 0x00000040 */ +#define HRTIM_ODISR_TD1ODIS HRTIM_ODISR_TD1ODIS_Msk /*!< Timer D Output 1 disable */ +#define HRTIM_ODISR_TD2ODIS_Pos (7U) +#define HRTIM_ODISR_TD2ODIS_Msk (0x1UL << HRTIM_ODISR_TD2ODIS_Pos) /*!< 0x00000080 */ +#define HRTIM_ODISR_TD2ODIS HRTIM_ODISR_TD2ODIS_Msk /*!< Timer D Output 2 disable */ +#define HRTIM_ODISR_TE1ODIS_Pos (8U) +#define HRTIM_ODISR_TE1ODIS_Msk (0x1UL << HRTIM_ODISR_TE1ODIS_Pos) /*!< 0x00000100 */ +#define HRTIM_ODISR_TE1ODIS HRTIM_ODISR_TE1ODIS_Msk /*!< Timer E Output 1 disable */ +#define HRTIM_ODISR_TE2ODIS_Pos (9U) +#define HRTIM_ODISR_TE2ODIS_Msk (0x1UL << HRTIM_ODISR_TE2ODIS_Pos) /*!< 0x00000200 */ +#define HRTIM_ODISR_TE2ODIS HRTIM_ODISR_TE2ODIS_Msk /*!< Timer E Output 2 disable */ +#define HRTIM_ODISR_TF1ODIS_Pos (10U) +#define HRTIM_ODISR_TF1ODIS_Msk (0x1UL << HRTIM_ODISR_TF1ODIS_Pos) /*!< 0x00000100 */ +#define HRTIM_ODISR_TF1ODIS HRTIM_ODISR_TF1ODIS_Msk /*!< Timer F Output 1 disable */ +#define HRTIM_ODISR_TF2ODIS_Pos (11U) +#define HRTIM_ODISR_TF2ODIS_Msk (0x1UL << HRTIM_ODISR_TF2ODIS_Pos) /*!< 0x00000200 */ +#define HRTIM_ODISR_TF2ODIS HRTIM_ODISR_TF2ODIS_Msk /*!< Timer F Output 2 disable */ + +/**** Bit definition for Common HRTIM Timer output disable status register *****/ +#define HRTIM_ODSR_TA1ODS_Pos (0U) +#define HRTIM_ODSR_TA1ODS_Msk (0x1UL << HRTIM_ODSR_TA1ODS_Pos) /*!< 0x00000001 */ +#define HRTIM_ODSR_TA1ODS HRTIM_ODSR_TA1ODS_Msk /*!< Timer A Output 1 disable status */ +#define HRTIM_ODSR_TA2ODS_Pos (1U) +#define HRTIM_ODSR_TA2ODS_Msk (0x1UL << HRTIM_ODSR_TA2ODS_Pos) /*!< 0x00000002 */ +#define HRTIM_ODSR_TA2ODS HRTIM_ODSR_TA2ODS_Msk /*!< Timer A Output 2 disable status */ +#define HRTIM_ODSR_TB1ODS_Pos (2U) +#define HRTIM_ODSR_TB1ODS_Msk (0x1UL << HRTIM_ODSR_TB1ODS_Pos) /*!< 0x00000004 */ +#define HRTIM_ODSR_TB1ODS HRTIM_ODSR_TB1ODS_Msk /*!< Timer B Output 1 disable status */ +#define HRTIM_ODSR_TB2ODS_Pos (3U) +#define HRTIM_ODSR_TB2ODS_Msk (0x1UL << HRTIM_ODSR_TB2ODS_Pos) /*!< 0x00000008 */ +#define HRTIM_ODSR_TB2ODS HRTIM_ODSR_TB2ODS_Msk /*!< Timer B Output 2 disable status */ +#define HRTIM_ODSR_TC1ODS_Pos (4U) +#define HRTIM_ODSR_TC1ODS_Msk (0x1UL << HRTIM_ODSR_TC1ODS_Pos) /*!< 0x00000010 */ +#define HRTIM_ODSR_TC1ODS HRTIM_ODSR_TC1ODS_Msk /*!< Timer C Output 1 disable status */ +#define HRTIM_ODSR_TC2ODS_Pos (5U) +#define HRTIM_ODSR_TC2ODS_Msk (0x1UL << HRTIM_ODSR_TC2ODS_Pos) /*!< 0x00000020 */ +#define HRTIM_ODSR_TC2ODS HRTIM_ODSR_TC2ODS_Msk /*!< Timer C Output 2 disable status */ +#define HRTIM_ODSR_TD1ODS_Pos (6U) +#define HRTIM_ODSR_TD1ODS_Msk (0x1UL << HRTIM_ODSR_TD1ODS_Pos) /*!< 0x00000040 */ +#define HRTIM_ODSR_TD1ODS HRTIM_ODSR_TD1ODS_Msk /*!< Timer D Output 1 disable status */ +#define HRTIM_ODSR_TD2ODS_Pos (7U) +#define HRTIM_ODSR_TD2ODS_Msk (0x1UL << HRTIM_ODSR_TD2ODS_Pos) /*!< 0x00000080 */ +#define HRTIM_ODSR_TD2ODS HRTIM_ODSR_TD2ODS_Msk /*!< Timer D Output 2 disable status */ +#define HRTIM_ODSR_TE1ODS_Pos (8U) +#define HRTIM_ODSR_TE1ODS_Msk (0x1UL << HRTIM_ODSR_TE1ODS_Pos) /*!< 0x00000100 */ +#define HRTIM_ODSR_TE1ODS HRTIM_ODSR_TE1ODS_Msk /*!< Timer E Output 1 disable status */ +#define HRTIM_ODSR_TE2ODS_Pos (9U) +#define HRTIM_ODSR_TE2ODS_Msk (0x1UL << HRTIM_ODSR_TE2ODS_Pos) /*!< 0x00000200 */ +#define HRTIM_ODSR_TE2ODS HRTIM_ODSR_TE2ODS_Msk /*!< Timer E Output 2 disable status */ +#define HRTIM_ODSR_TF1ODS_Pos (10U) +#define HRTIM_ODSR_TF1ODS_Msk (0x1UL << HRTIM_ODSR_TF1ODS_Pos) /*!< 0x00000100 */ +#define HRTIM_ODSR_TF1ODS HRTIM_ODSR_TF1ODS_Msk /*!< Timer F Output 1 disable status */ +#define HRTIM_ODSR_TF2ODS_Pos (11U) +#define HRTIM_ODSR_TF2ODS_Msk (0x1UL << HRTIM_ODSR_TF2ODS_Pos) /*!< 0x00000200 */ +#define HRTIM_ODSR_TF2ODS HRTIM_ODSR_TF2ODS_Msk /*!< Timer F Output 2 disable status */ + +/**** Bit definition for Common HRTIM Timer Burst mode control register ********/ +#define HRTIM_BMCR_BME_Pos (0U) +#define HRTIM_BMCR_BME_Msk (0x1UL << HRTIM_BMCR_BME_Pos) /*!< 0x00000001 */ +#define HRTIM_BMCR_BME HRTIM_BMCR_BME_Msk /*!< Burst mode enable */ +#define HRTIM_BMCR_BMOM_Pos (1U) +#define HRTIM_BMCR_BMOM_Msk (0x1UL << HRTIM_BMCR_BMOM_Pos) /*!< 0x00000002 */ +#define HRTIM_BMCR_BMOM HRTIM_BMCR_BMOM_Msk /*!< Burst mode operating mode */ +#define HRTIM_BMCR_BMCLK_Pos (2U) +#define HRTIM_BMCR_BMCLK_Msk (0xFUL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x0000003C */ +#define HRTIM_BMCR_BMCLK HRTIM_BMCR_BMCLK_Msk /*!< Burst mode clock source */ +#define HRTIM_BMCR_BMCLK_0 (0x1UL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x00000004 */ +#define HRTIM_BMCR_BMCLK_1 (0x2UL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x00000008 */ +#define HRTIM_BMCR_BMCLK_2 (0x4UL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x00000010 */ +#define HRTIM_BMCR_BMCLK_3 (0x8UL << HRTIM_BMCR_BMCLK_Pos) /*!< 0x00000020 */ +#define HRTIM_BMCR_BMPRSC_Pos (6U) +#define HRTIM_BMCR_BMPRSC_Msk (0xFUL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x000003C0 */ +#define HRTIM_BMCR_BMPRSC HRTIM_BMCR_BMPRSC_Msk /*!< Burst mode prescaler */ +#define HRTIM_BMCR_BMPRSC_0 (0x1UL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x00000040 */ +#define HRTIM_BMCR_BMPRSC_1 (0x2UL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x00000080 */ +#define HRTIM_BMCR_BMPRSC_2 (0x4UL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x00000100 */ +#define HRTIM_BMCR_BMPRSC_3 (0x8UL << HRTIM_BMCR_BMPRSC_Pos) /*!< 0x00000200 */ +#define HRTIM_BMCR_BMPREN_Pos (10U) +#define HRTIM_BMCR_BMPREN_Msk (0x1UL << HRTIM_BMCR_BMPREN_Pos) /*!< 0x00000400 */ +#define HRTIM_BMCR_BMPREN HRTIM_BMCR_BMPREN_Msk /*!< Burst mode Preload bit */ +#define HRTIM_BMCR_MTBM_Pos (16U) +#define HRTIM_BMCR_MTBM_Msk (0x1UL << HRTIM_BMCR_MTBM_Pos) /*!< 0x00010000 */ +#define HRTIM_BMCR_MTBM HRTIM_BMCR_MTBM_Msk /*!< Master Timer Burst mode */ +#define HRTIM_BMCR_TABM_Pos (17U) +#define HRTIM_BMCR_TABM_Msk (0x1UL << HRTIM_BMCR_TABM_Pos) /*!< 0x00020000 */ +#define HRTIM_BMCR_TABM HRTIM_BMCR_TABM_Msk /*!< Timer A Burst mode */ +#define HRTIM_BMCR_TBBM_Pos (18U) +#define HRTIM_BMCR_TBBM_Msk (0x1UL << HRTIM_BMCR_TBBM_Pos) /*!< 0x00040000 */ +#define HRTIM_BMCR_TBBM HRTIM_BMCR_TBBM_Msk /*!< Timer B Burst mode */ +#define HRTIM_BMCR_TCBM_Pos (19U) +#define HRTIM_BMCR_TCBM_Msk (0x1UL << HRTIM_BMCR_TCBM_Pos) /*!< 0x00080000 */ +#define HRTIM_BMCR_TCBM HRTIM_BMCR_TCBM_Msk /*!< Timer C Burst mode */ +#define HRTIM_BMCR_TDBM_Pos (20U) +#define HRTIM_BMCR_TDBM_Msk (0x1UL << HRTIM_BMCR_TDBM_Pos) /*!< 0x00100000 */ +#define HRTIM_BMCR_TDBM HRTIM_BMCR_TDBM_Msk /*!< Timer D Burst mode */ +#define HRTIM_BMCR_TEBM_Pos (21U) +#define HRTIM_BMCR_TEBM_Msk (0x1UL << HRTIM_BMCR_TEBM_Pos) /*!< 0x00200000 */ +#define HRTIM_BMCR_TEBM HRTIM_BMCR_TEBM_Msk /*!< Timer E Burst mode */ + +#define HRTIM_BMCR_TFBM_Pos (22U) +#define HRTIM_BMCR_TFBM_Msk (0x1UL << HRTIM_BMCR_TFBM_Pos) /*!< 0x00400000 */ +#define HRTIM_BMCR_TFBM HRTIM_BMCR_TFBM_Msk /*!< Timer F Burst mode */ + +#define HRTIM_BMCR_BMSTAT_Pos (31U) +#define HRTIM_BMCR_BMSTAT_Msk (0x1UL << HRTIM_BMCR_BMSTAT_Pos) /*!< 0x80000000 */ +#define HRTIM_BMCR_BMSTAT HRTIM_BMCR_BMSTAT_Msk /*!< Burst mode status */ + +/**** Bit definition for Common HRTIM Timer Burst mode Trigger register *******/ +#define HRTIM_BMTRGR_SW_Pos (0U) +#define HRTIM_BMTRGR_SW_Msk (0x1UL << HRTIM_BMTRGR_SW_Pos) /*!< 0x00000001 */ +#define HRTIM_BMTRGR_SW HRTIM_BMTRGR_SW_Msk /*!< Software start */ +#define HRTIM_BMTRGR_MSTRST_Pos (1U) +#define HRTIM_BMTRGR_MSTRST_Msk (0x1UL << HRTIM_BMTRGR_MSTRST_Pos) /*!< 0x00000002 */ +#define HRTIM_BMTRGR_MSTRST HRTIM_BMTRGR_MSTRST_Msk /*!< Master reset */ +#define HRTIM_BMTRGR_MSTREP_Pos (2U) +#define HRTIM_BMTRGR_MSTREP_Msk (0x1UL << HRTIM_BMTRGR_MSTREP_Pos) /*!< 0x00000004 */ +#define HRTIM_BMTRGR_MSTREP HRTIM_BMTRGR_MSTREP_Msk /*!< Master repetition */ +#define HRTIM_BMTRGR_MSTCMP1_Pos (3U) +#define HRTIM_BMTRGR_MSTCMP1_Msk (0x1UL << HRTIM_BMTRGR_MSTCMP1_Pos) /*!< 0x00000008 */ +#define HRTIM_BMTRGR_MSTCMP1 HRTIM_BMTRGR_MSTCMP1_Msk /*!< Master compare 1 */ +#define HRTIM_BMTRGR_MSTCMP2_Pos (4U) +#define HRTIM_BMTRGR_MSTCMP2_Msk (0x1UL << HRTIM_BMTRGR_MSTCMP2_Pos) /*!< 0x00000010 */ +#define HRTIM_BMTRGR_MSTCMP2 HRTIM_BMTRGR_MSTCMP2_Msk /*!< Master compare 2 */ +#define HRTIM_BMTRGR_MSTCMP3_Pos (5U) +#define HRTIM_BMTRGR_MSTCMP3_Msk (0x1UL << HRTIM_BMTRGR_MSTCMP3_Pos) /*!< 0x00000020 */ +#define HRTIM_BMTRGR_MSTCMP3 HRTIM_BMTRGR_MSTCMP3_Msk /*!< Master compare 3 */ +#define HRTIM_BMTRGR_MSTCMP4_Pos (6U) +#define HRTIM_BMTRGR_MSTCMP4_Msk (0x1UL << HRTIM_BMTRGR_MSTCMP4_Pos) /*!< 0x00000040 */ +#define HRTIM_BMTRGR_MSTCMP4 HRTIM_BMTRGR_MSTCMP4_Msk /*!< Master compare 4 */ +#define HRTIM_BMTRGR_TARST_Pos (7U) +#define HRTIM_BMTRGR_TARST_Msk (0x1UL << HRTIM_BMTRGR_TARST_Pos) /*!< 0x00000080 */ +#define HRTIM_BMTRGR_TARST HRTIM_BMTRGR_TARST_Msk /*!< Timer A reset */ +#define HRTIM_BMTRGR_TAREP_Pos (8U) +#define HRTIM_BMTRGR_TAREP_Msk (0x1UL << HRTIM_BMTRGR_TAREP_Pos) /*!< 0x00000100 */ +#define HRTIM_BMTRGR_TAREP HRTIM_BMTRGR_TAREP_Msk /*!< Timer A repetition */ +#define HRTIM_BMTRGR_TACMP1_Pos (9U) +#define HRTIM_BMTRGR_TACMP1_Msk (0x1UL << HRTIM_BMTRGR_TACMP1_Pos) /*!< 0x00000200 */ +#define HRTIM_BMTRGR_TACMP1 HRTIM_BMTRGR_TACMP1_Msk /*!< Timer A compare 1 */ +#define HRTIM_BMTRGR_TACMP2_Pos (10U) +#define HRTIM_BMTRGR_TACMP2_Msk (0x1UL << HRTIM_BMTRGR_TACMP2_Pos) /*!< 0x00000400 */ +#define HRTIM_BMTRGR_TACMP2 HRTIM_BMTRGR_TACMP2_Msk /*!< Timer A compare 2 */ +#define HRTIM_BMTRGR_TBRST_Pos (11U) +#define HRTIM_BMTRGR_TBRST_Msk (0x1UL << HRTIM_BMTRGR_TBRST_Pos) /*!< 0x00000800 */ +#define HRTIM_BMTRGR_TBRST HRTIM_BMTRGR_TBRST_Msk /*!< Timer B reset */ +#define HRTIM_BMTRGR_TBREP_Pos (12U) +#define HRTIM_BMTRGR_TBREP_Msk (0x1UL << HRTIM_BMTRGR_TBREP_Pos) /*!< 0x00001000 */ +#define HRTIM_BMTRGR_TBREP HRTIM_BMTRGR_TBREP_Msk /*!< Timer B repetition */ +#define HRTIM_BMTRGR_TBCMP1_Pos (13U) +#define HRTIM_BMTRGR_TBCMP1_Msk (0x1UL << HRTIM_BMTRGR_TBCMP1_Pos) /*!< 0x00002000 */ +#define HRTIM_BMTRGR_TBCMP1 HRTIM_BMTRGR_TBCMP1_Msk /*!< Timer B compare 1 */ +#define HRTIM_BMTRGR_TBCMP2_Pos (14U) +#define HRTIM_BMTRGR_TBCMP2_Msk (0x1UL << HRTIM_BMTRGR_TBCMP2_Pos) /*!< 0x00004000 */ +#define HRTIM_BMTRGR_TBCMP2 HRTIM_BMTRGR_TBCMP2_Msk /*!< Timer B compare 2 */ +#define HRTIM_BMTRGR_TCRST_Pos (15U) +#define HRTIM_BMTRGR_TCRST_Msk (0x1UL << HRTIM_BMTRGR_TCRST_Pos) /*!< 0x00008000 */ +#define HRTIM_BMTRGR_TCRST HRTIM_BMTRGR_TCRST_Msk /*!< Timer C reset */ +#define HRTIM_BMTRGR_TCREP_Pos (16U) +#define HRTIM_BMTRGR_TCREP_Msk (0x1UL << HRTIM_BMTRGR_TCREP_Pos) /*!< 0x00010000 */ +#define HRTIM_BMTRGR_TCREP HRTIM_BMTRGR_TCREP_Msk /*!< Timer C repetition */ +#define HRTIM_BMTRGR_TCCMP1_Pos (17U) +#define HRTIM_BMTRGR_TCCMP1_Msk (0x1UL << HRTIM_BMTRGR_TCCMP1_Pos) /*!< 0x00020000 */ +#define HRTIM_BMTRGR_TCCMP1 HRTIM_BMTRGR_TCCMP1_Msk /*!< Timer C compare 1 */ +#define HRTIM_BMTRGR_TFRST_Pos (18U) +#define HRTIM_BMTRGR_TFRST_Msk (0x1UL << HRTIM_BMTRGR_TFRST_Pos) /*!< 0x00040000 */ +#define HRTIM_BMTRGR_TFRST HRTIM_BMTRGR_TFRST_Msk /*!< Timer F reset */ +#define HRTIM_BMTRGR_TDRST_Pos (19U) +#define HRTIM_BMTRGR_TDRST_Msk (0x1UL << HRTIM_BMTRGR_TDRST_Pos) /*!< 0x00080000 */ +#define HRTIM_BMTRGR_TDRST HRTIM_BMTRGR_TDRST_Msk /*!< Timer D reset */ +#define HRTIM_BMTRGR_TDREP_Pos (20U) +#define HRTIM_BMTRGR_TDREP_Msk (0x1UL << HRTIM_BMTRGR_TDREP_Pos) /*!< 0x00100000 */ +#define HRTIM_BMTRGR_TDREP HRTIM_BMTRGR_TDREP_Msk /*!< Timer D repetition */ +#define HRTIM_BMTRGR_TFREP_Pos (21U) +#define HRTIM_BMTRGR_TFREP_Msk (0x1UL << HRTIM_BMTRGR_TFREP_Pos) /*!< 0x00200000 */ +#define HRTIM_BMTRGR_TFREP HRTIM_BMTRGR_TFREP_Msk /*!< Timer F repetition*/ +#define HRTIM_BMTRGR_TDCMP2_Pos (22U) +#define HRTIM_BMTRGR_TDCMP2_Msk (0x1UL << HRTIM_BMTRGR_TDCMP2_Pos) /*!< 0x00400000 */ +#define HRTIM_BMTRGR_TDCMP2 HRTIM_BMTRGR_TDCMP2_Msk /*!< Timer D compare 2 */ +#define HRTIM_BMTRGR_TFCMP1_Pos (23U) +#define HRTIM_BMTRGR_TFCMP1_Msk (0x1UL << HRTIM_BMTRGR_TFCMP1_Pos) /*!< 0x00800000 */ +#define HRTIM_BMTRGR_TFCMP1 HRTIM_BMTRGR_TFCMP1_Msk /*!< Timer F compare 1 */ +#define HRTIM_BMTRGR_TEREP_Pos (24U) +#define HRTIM_BMTRGR_TEREP_Msk (0x1UL << HRTIM_BMTRGR_TEREP_Pos) /*!< 0x01000000 */ +#define HRTIM_BMTRGR_TEREP HRTIM_BMTRGR_TEREP_Msk /*!< Timer E repetition */ +#define HRTIM_BMTRGR_TECMP1_Pos (25U) +#define HRTIM_BMTRGR_TECMP1_Msk (0x1UL << HRTIM_BMTRGR_TECMP1_Pos) /*!< 0x02000000 */ +#define HRTIM_BMTRGR_TECMP1 HRTIM_BMTRGR_TECMP1_Msk /*!< Timer E compare 1 */ +#define HRTIM_BMTRGR_TECMP2_Pos (26U) +#define HRTIM_BMTRGR_TECMP2_Msk (0x1UL << HRTIM_BMTRGR_TECMP2_Pos) /*!< 0x04000000 */ +#define HRTIM_BMTRGR_TECMP2 HRTIM_BMTRGR_TECMP2_Msk /*!< Timer E compare 2 */ +#define HRTIM_BMTRGR_TAEEV7_Pos (27U) +#define HRTIM_BMTRGR_TAEEV7_Msk (0x1UL << HRTIM_BMTRGR_TAEEV7_Pos) /*!< 0x08000000 */ +#define HRTIM_BMTRGR_TAEEV7 HRTIM_BMTRGR_TAEEV7_Msk /*!< Timer A period following External Event7 */ +#define HRTIM_BMTRGR_TDEEV8_Pos (28U) +#define HRTIM_BMTRGR_TDEEV8_Msk (0x1UL << HRTIM_BMTRGR_TDEEV8_Pos) /*!< 0x10000000 */ +#define HRTIM_BMTRGR_TDEEV8 HRTIM_BMTRGR_TDEEV8_Msk /*!< Timer D period following External Event8 */ +#define HRTIM_BMTRGR_EEV7_Pos (29U) +#define HRTIM_BMTRGR_EEV7_Msk (0x1UL << HRTIM_BMTRGR_EEV7_Pos) /*!< 0x20000000 */ +#define HRTIM_BMTRGR_EEV7 HRTIM_BMTRGR_EEV7_Msk /*!< External Event 7 */ +#define HRTIM_BMTRGR_EEV8_Pos (30U) +#define HRTIM_BMTRGR_EEV8_Msk (0x1UL << HRTIM_BMTRGR_EEV8_Pos) /*!< 0x40000000 */ +#define HRTIM_BMTRGR_EEV8 HRTIM_BMTRGR_EEV8_Msk /*!< External Event 8 */ +#define HRTIM_BMTRGR_OCHPEV_Pos (31U) +#define HRTIM_BMTRGR_OCHPEV_Msk (0x1UL << HRTIM_BMTRGR_OCHPEV_Pos) /*!< 0x80000000 */ +#define HRTIM_BMTRGR_OCHPEV HRTIM_BMTRGR_OCHPEV_Msk /*!< on-chip Event */ + +/******************* Bit definition for HRTIM_BMCMPR register ***************/ +#define HRTIM_BMCMPR_BMCMPR_Pos (0U) +#define HRTIM_BMCMPR_BMCMPR_Msk (0xFFFFUL << HRTIM_BMCMPR_BMCMPR_Pos) /*!< 0x0000FFFF */ +#define HRTIM_BMCMPR_BMCMPR HRTIM_BMCMPR_BMCMPR_Msk /*! Date: Mon, 1 Jun 2026 18:15:29 +0200 Subject: [PATCH 02/11] Add STM32 MCU foundation Add the STM32 platform entry point, chip CMake files, bspMcu startup code, a software reset wrapper, and the STM32 unit-test presets. The platform reuses the shared CMSIS core from libs/3rdparty/cmsis instead of carrying a local copy. --- CMakeLists.txt | 4 + CMakePresets.json | 54 +++ NOTICE.md | 3 +- doc/dev/index.rst | 1 + doc/dev/modules/stm32.rst | 22 + platforms/stm32/CMakeLists.txt | 20 + platforms/stm32/bsp/CMakeLists.txt | 1 + platforms/stm32/bsp/bspMcu/CMakeLists.txt | 18 + platforms/stm32/bsp/bspMcu/doc/index.rst | 22 + platforms/stm32/bsp/bspMcu/include/mcu/mcu.h | 24 + .../include/reset/softwareSystemReset.h | 22 + platforms/stm32/bsp/bspMcu/module.spec | 8 + .../bspMcu/src/reset/softwareSystemReset.cpp | 18 + .../bsp/bspMcu/startup/startup_stm32f413xx.s | 435 +++++++++++++++++ .../bsp/bspMcu/startup/startup_stm32g474xx.s | 449 ++++++++++++++++++ platforms/stm32/cmake/stm32f413zh.cmake | 15 + platforms/stm32/cmake/stm32g474re.cmake | 15 + platforms/stm32/unitTest/CMakeLists.txt | 1 + 18 files changed, 1131 insertions(+), 1 deletion(-) create mode 100644 doc/dev/modules/stm32.rst create mode 100644 platforms/stm32/CMakeLists.txt create mode 100644 platforms/stm32/bsp/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspMcu/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspMcu/doc/index.rst create mode 100644 platforms/stm32/bsp/bspMcu/include/mcu/mcu.h create mode 100644 platforms/stm32/bsp/bspMcu/include/reset/softwareSystemReset.h create mode 100644 platforms/stm32/bsp/bspMcu/module.spec create mode 100644 platforms/stm32/bsp/bspMcu/src/reset/softwareSystemReset.cpp create mode 100644 platforms/stm32/bsp/bspMcu/startup/startup_stm32f413xx.s create mode 100644 platforms/stm32/bsp/bspMcu/startup/startup_stm32g474xx.s create mode 100644 platforms/stm32/cmake/stm32f413zh.cmake create mode 100644 platforms/stm32/cmake/stm32g474re.cmake create mode 100644 platforms/stm32/unitTest/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index c9d893a6a9a..840515f077c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,10 @@ if (BUILD_EXECUTABLE STREQUAL "unitTest") add_subdirectory(platforms/s32k1xx/bsp/canflex2Transceiver/test) add_subdirectory(platforms/s32k1xx/bsp/bspUart/test) + elseif (OPENBSW_PLATFORM STREQUAL "stm32") + + add_subdirectory(platforms/stm32/unitTest EXCLUDE_FROM_ALL) + else () message( FATAL_ERROR diff --git a/CMakePresets.json b/CMakePresets.json index ec6272009d2..19f96ee79a4 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -61,6 +61,32 @@ "OPENBSW_PLATFORM": "s32k1xx" } }, + { + "name": "tests-stm32-debug", + "displayName": "Configure for testing STM32 modules (debug)", + "description": "Configure for testing STM32 modules (debug)", + "inherits": "_config-base", + "binaryDir": "${sourceDir}/build/tests/stm32/Debug", + "cacheVariables": { + "CMAKE_DEFAULT_BUILD_TYPE": "Debug", + "CMAKE_CONFIGURATION_TYPES": "Debug", + "BUILD_EXECUTABLE": "unitTest", + "OPENBSW_PLATFORM": "stm32" + } + }, + { + "name": "tests-stm32-release", + "displayName": "Configure for testing STM32 modules (release)", + "description": "Configure for testing STM32 modules (release)", + "inherits": "_config-base", + "binaryDir": "${sourceDir}/build/tests/stm32/Release", + "cacheVariables": { + "CMAKE_DEFAULT_BUILD_TYPE": "Release", + "CMAKE_CONFIGURATION_TYPES": "Release", + "BUILD_EXECUTABLE": "unitTest", + "OPENBSW_PLATFORM": "stm32" + } + }, { "name": "posix-freertos", "displayName": "POSIX-FREERTOS compliant configuration", @@ -228,6 +254,20 @@ "configurePreset": "tests-s32k1xx-release", "configuration": "Release" }, + { + "name": "tests-stm32-debug", + "displayName": "Build tests of STM32 modules (debug)", + "description": "Build tests of STM32 modules (debug)", + "configurePreset": "tests-stm32-debug", + "configuration": "Debug" + }, + { + "name": "tests-stm32-release", + "displayName": "Build tests of STM32 modules (release)", + "description": "Build tests of STM32 modules (release)", + "configurePreset": "tests-stm32-release", + "configuration": "Release" + }, { "name": "posix-freertos", "displayName": "build POSIX-FREERTOS", @@ -305,6 +345,20 @@ "description": "Run tests of S32K1XX modules (release)", "configuration": "Release", "configurePreset": "tests-s32k1xx-release" + }, + { + "name": "tests-stm32-debug", + "displayName": "Run tests of STM32 modules (debug)", + "description": "Run tests of STM32 modules (debug)", + "configuration": "Debug", + "configurePreset": "tests-stm32-debug" + }, + { + "name": "tests-stm32-release", + "displayName": "Run tests of STM32 modules (release)", + "description": "Run tests of STM32 modules (release)", + "configuration": "Release", + "configurePreset": "tests-stm32-release" } ] } diff --git a/NOTICE.md b/NOTICE.md index 9be29d742b7..fcd5577cd22 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -47,7 +47,8 @@ We recommend to read their licenses, as their terms may differ from the terms de | ThreadX Linux Port | 6.4.3 | MIT | ``platforms/posix/3rdparty/threadx/LICENSE.md`` | | CMSIS | 6.1.0 | Apache v2 | ``libs/3rdparty/cmsis/LICENSE`` | | NXP S32K148 Headers | 1.1a | BSD-3 | ``platforms/s32k1xx/bsp/bspMcu/include/3rdparty/nxp/*.h`` | -| ST STM32 Device Headers | 2.6.11 / 1.2.6 | Apache v2 | ``platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE`` | +| ST STM32F4 Device Headers | 2.6.11 | Apache v2 | ``platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE`` | +| ST STM32G4 Device Headers | 1.2.6 | Apache v2 | ``platforms/stm32/bsp/bspMcu/include/3rdparty/st/LICENSE`` | | CodeCoverage | | BSD-3 | ``cmake/modules/CodeCoverage.cmake`` | ## MISRA diff --git a/doc/dev/index.rst b/doc/dev/index.rst index 00d174535f8..da03693a06d 100644 --- a/doc/dev/index.rst +++ b/doc/dev/index.rst @@ -178,6 +178,7 @@ Eclipse OpenBSW is a trademark of the Eclipse Foundation. modules/common modules/posix modules/s32k1xx + modules/stm32 modules/executables modules/mocks diff --git a/doc/dev/modules/stm32.rst b/doc/dev/modules/stm32.rst new file mode 100644 index 00000000000..66660f2761b --- /dev/null +++ b/doc/dev/modules/stm32.rst @@ -0,0 +1,22 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +STM32 +===== + +BSP +--- + +.. toctree:: + :maxdepth: 1 + :glob: + + ../../../platforms/stm32/bsp/**/doc/index diff --git a/platforms/stm32/CMakeLists.txt b/platforms/stm32/CMakeLists.txt new file mode 100644 index 00000000000..dbe6c9fd805 --- /dev/null +++ b/platforms/stm32/CMakeLists.txt @@ -0,0 +1,20 @@ +if (NOT BUILD_EXECUTABLE STREQUAL "unitTest") + if (NOT DEFINED STM32_CHIP) + message( + FATAL_ERROR + "STM32_CHIP must be defined (e.g., STM32F413ZH or STM32G474RE)") + endif () + + if (STM32_CHIP STREQUAL "STM32F413ZH") + include(${CMAKE_CURRENT_LIST_DIR}/cmake/stm32f413zh.cmake) + elseif (STM32_CHIP STREQUAL "STM32G474RE") + include(${CMAKE_CURRENT_LIST_DIR}/cmake/stm32g474re.cmake) + else () + message( + FATAL_ERROR + "Unsupported STM32_CHIP: ${STM32_CHIP}. Supported: STM32F413ZH, STM32G474RE" + ) + endif () + + add_subdirectory(bsp) +endif () diff --git a/platforms/stm32/bsp/CMakeLists.txt b/platforms/stm32/bsp/CMakeLists.txt new file mode 100644 index 00000000000..c377df0854c --- /dev/null +++ b/platforms/stm32/bsp/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(bspMcu) diff --git a/platforms/stm32/bsp/bspMcu/CMakeLists.txt b/platforms/stm32/bsp/bspMcu/CMakeLists.txt new file mode 100644 index 00000000000..f931f420073 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/CMakeLists.txt @@ -0,0 +1,18 @@ +enable_language(ASM) +add_library(bspMcu src/reset/softwareSystemReset.cpp ${STM32_STARTUP_ASM}) + +target_include_directories( + bspMcu PUBLIC include ${CMAKE_SOURCE_DIR}/libs/3rdparty/cmsis + ${CMAKE_SOURCE_DIR}/libs/3rdparty/cmsis/m-profile) + +if (STM32_FAMILY STREQUAL "F4") + target_include_directories(bspMcu PUBLIC include/3rdparty/st/stm32f4) +elseif (STM32_FAMILY STREQUAL "G4") + target_include_directories(bspMcu PUBLIC include/3rdparty/st/stm32g4) +endif () + +target_compile_definitions( + bspMcu PUBLIC ${STM32_DEVICE_UPPER} STM32_FAMILY_${STM32_FAMILY} + CAN_TYPE_${CAN_TYPE}) + +target_link_libraries(bspMcu PUBLIC platform) diff --git a/platforms/stm32/bsp/bspMcu/doc/index.rst b/platforms/stm32/bsp/bspMcu/doc/index.rst new file mode 100644 index 00000000000..e0ee61f3bf1 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/doc/index.rst @@ -0,0 +1,22 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspMcu +====== + +Overview +-------- + +The module ``bspMcu`` provides the MCU API for STM32F413xx and STM32G474xx. +``mcu/mcu.h`` selects the matching ST CMSIS device header from +``include/3rdparty/st`` and reuses the shared CMSIS core headers from +``libs/3rdparty/cmsis``. The module also contains the chip startup files and +a function for software system reset. diff --git a/platforms/stm32/bsp/bspMcu/include/mcu/mcu.h b/platforms/stm32/bsp/bspMcu/include/mcu/mcu.h new file mode 100644 index 00000000000..70616c1fe78 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/include/mcu/mcu.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +// Required by the local core_cm4.h include guard. +#define INCLUDE_CORE_CM4_IN_MCU_H + +#if defined(STM32F413xx) +#include "stm32f413xx.h" +#elif defined(STM32G474xx) +#include "stm32g474xx.h" +#else +#error "Unsupported STM32 device - define STM32F413xx or STM32G474xx" +#endif + +#undef INCLUDE_CORE_CM4_IN_MCU_H diff --git a/platforms/stm32/bsp/bspMcu/include/reset/softwareSystemReset.h b/platforms/stm32/bsp/bspMcu/include/reset/softwareSystemReset.h new file mode 100644 index 00000000000..726097e3ba7 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/include/reset/softwareSystemReset.h @@ -0,0 +1,22 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +[[noreturn]] extern void softwareSystemReset(void); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/platforms/stm32/bsp/bspMcu/module.spec b/platforms/stm32/bsp/bspMcu/module.spec new file mode 100644 index 00000000000..3b4161c23d4 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/module.spec @@ -0,0 +1,8 @@ +maturity: raw +unit_test: false +format_check_exclude: + - "include/3rdparty/st/*/*.h" +sca_exclude: + '*': + - "include/3rdparty/st/*/*.h" +oss: true diff --git a/platforms/stm32/bsp/bspMcu/src/reset/softwareSystemReset.cpp b/platforms/stm32/bsp/bspMcu/src/reset/softwareSystemReset.cpp new file mode 100644 index 00000000000..65e31e40a04 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/src/reset/softwareSystemReset.cpp @@ -0,0 +1,18 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "reset/softwareSystemReset.h" + +#include "mcu/mcu.h" + +extern "C" +{ +[[noreturn]] void softwareSystemReset(void) { NVIC_SystemReset(); } +} diff --git a/platforms/stm32/bsp/bspMcu/startup/startup_stm32f413xx.s b/platforms/stm32/bsp/bspMcu/startup/startup_stm32f413xx.s new file mode 100644 index 00000000000..59c93de614e --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/startup/startup_stm32f413xx.s @@ -0,0 +1,435 @@ +/******************************************************************************** + * Copyright (c) 2017 STMicroelectronics + * Copyright (c) 2026 An Dao + * + * Startup for STM32F413xx: vector table and Reset_Handler, based on the + * STMicroelectronics CMSIS device startup template for STM32F4. Vector + * table layout per reference manual RM0430. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + + .syntax unified + .cpu cortex-m4 + .fpu fpv4-sp-d16 + .thumb + +.global g_pfnVectors +.global Default_Handler + +.word _sidata +.word _sdata +.word _edata +.word _sbss +.word _ebss + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + ldr sp, =_estack + +/* Copy .data from FLASH to SRAM */ + ldr r0, =_sdata + ldr r1, =_edata + ldr r2, =_sidata + movs r3, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r4, [r2, r3] + str r4, [r0, r3] + adds r3, r3, #4 + +LoopCopyDataInit: + adds r4, r0, r3 + cmp r4, r1 + bcc CopyDataInit + +/* Zero fill .bss */ + ldr r2, =_sbss + ldr r4, =_ebss + movs r3, #0 + b LoopFillZerobss + +FillZerobss: + str r3, [r2] + adds r2, r2, #4 + +LoopFillZerobss: + cmp r2, r4 + bcc FillZerobss + +/* Enable FPU: set CP10/CP11 full access (required for ThreadX - FreeRTOS + enables this in port.c, but ThreadX does not) */ + ldr r0, =0xE000ED88 + ldr r1, [r0] + orr r1, r1, #(0xF << 20) + str r1, [r0] + dsb + isb + +/* Call SystemInit, C++ constructors, and main */ + bl SystemInit + bl __libc_init_array + bl main + bx lr + +.size Reset_Handler, .-Reset_Handler + + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler + +/****************************************************************************** + * Vector table for STM32F413xx (102 external interrupts, RM0430 Table 40) + ******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + +g_pfnVectors: + /* Cortex-M4 system exceptions */ + .word _estack /* Top of Stack */ + .word Reset_Handler /* Reset Handler */ + .word NMI_Handler /* NMI Handler */ + .word HardFault_Handler /* Hard Fault Handler */ + .word MemManage_Handler /* MPU Fault Handler */ + .word BusFault_Handler /* Bus Fault Handler */ + .word UsageFault_Handler /* Usage Fault Handler */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word SVC_Handler /* SVCall Handler */ + .word DebugMon_Handler /* Debug Monitor Handler */ + .word 0 /* Reserved */ + .word PendSV_Handler /* PendSV Handler */ + .word SysTick_Handler /* SysTick Handler */ + + /* External interrupts - STM32F413xx (IRQn 0–101) */ + .word WWDG_IRQHandler /* 0: Window Watchdog */ + .word PVD_IRQHandler /* 1: PVD through EXTI */ + .word TAMP_STAMP_IRQHandler /* 2: Tamper and TimeStamp via EXTI */ + .word RTC_WKUP_IRQHandler /* 3: RTC Wakeup via EXTI */ + .word FLASH_IRQHandler /* 4: Flash global */ + .word RCC_IRQHandler /* 5: RCC global */ + .word EXTI0_IRQHandler /* 6: EXTI Line 0 */ + .word EXTI1_IRQHandler /* 7: EXTI Line 1 */ + .word EXTI2_IRQHandler /* 8: EXTI Line 2 */ + .word EXTI3_IRQHandler /* 9: EXTI Line 3 */ + .word EXTI4_IRQHandler /* 10: EXTI Line 4 */ + .word DMA1_Stream0_IRQHandler /* 11: DMA1 Stream 0 */ + .word DMA1_Stream1_IRQHandler /* 12: DMA1 Stream 1 */ + .word DMA1_Stream2_IRQHandler /* 13: DMA1 Stream 2 */ + .word DMA1_Stream3_IRQHandler /* 14: DMA1 Stream 3 */ + .word DMA1_Stream4_IRQHandler /* 15: DMA1 Stream 4 */ + .word DMA1_Stream5_IRQHandler /* 16: DMA1 Stream 5 */ + .word DMA1_Stream6_IRQHandler /* 17: DMA1 Stream 6 */ + .word ADC_IRQHandler /* 18: ADC1, ADC2, ADC3 */ + .word CAN1_TX_IRQHandler /* 19: CAN1 TX */ + .word CAN1_RX0_IRQHandler /* 20: CAN1 RX0 */ + .word CAN1_RX1_IRQHandler /* 21: CAN1 RX1 */ + .word CAN1_SCE_IRQHandler /* 22: CAN1 SCE */ + .word EXTI9_5_IRQHandler /* 23: EXTI Lines [9:5] */ + .word TIM1_BRK_TIM9_IRQHandler /* 24: TIM1 Break and TIM9 */ + .word TIM1_UP_TIM10_IRQHandler /* 25: TIM1 Update and TIM10 */ + .word TIM1_TRG_COM_TIM11_IRQHandler /* 26: TIM1 Trigger/Commut and TIM11 */ + .word TIM1_CC_IRQHandler /* 27: TIM1 Capture Compare */ + .word TIM2_IRQHandler /* 28: TIM2 */ + .word TIM3_IRQHandler /* 29: TIM3 */ + .word TIM4_IRQHandler /* 30: TIM4 */ + .word I2C1_EV_IRQHandler /* 31: I2C1 Event */ + .word I2C1_ER_IRQHandler /* 32: I2C1 Error */ + .word I2C2_EV_IRQHandler /* 33: I2C2 Event */ + .word I2C2_ER_IRQHandler /* 34: I2C2 Error */ + .word SPI1_IRQHandler /* 35: SPI1 */ + .word SPI2_IRQHandler /* 36: SPI2 */ + .word USART1_IRQHandler /* 37: USART1 */ + .word USART2_IRQHandler /* 38: USART2 */ + .word USART3_IRQHandler /* 39: USART3 */ + .word EXTI15_10_IRQHandler /* 40: EXTI Lines [15:10] */ + .word RTC_Alarm_IRQHandler /* 41: RTC Alarm (A and B) via EXTI */ + .word OTG_FS_WKUP_IRQHandler /* 42: USB OTG FS Wakeup via EXTI */ + .word TIM8_BRK_TIM12_IRQHandler /* 43: TIM8 Break and TIM12 */ + .word TIM8_UP_TIM13_IRQHandler /* 44: TIM8 Update and TIM13 */ + .word TIM8_TRG_COM_TIM14_IRQHandler /* 45: TIM8 Trigger/Commut and TIM14 */ + .word TIM8_CC_IRQHandler /* 46: TIM8 Capture Compare */ + .word DMA1_Stream7_IRQHandler /* 47: DMA1 Stream 7 */ + .word 0 /* 48: Reserved (FSMC) */ + .word SDIO_IRQHandler /* 49: SDIO */ + .word TIM5_IRQHandler /* 50: TIM5 */ + .word SPI3_IRQHandler /* 51: SPI3 */ + .word UART4_IRQHandler /* 52: UART4 */ + .word UART5_IRQHandler /* 53: UART5 */ + .word TIM6_DAC_IRQHandler /* 54: TIM6 and DAC1&2 underrun */ + .word TIM7_IRQHandler /* 55: TIM7 */ + .word DMA2_Stream0_IRQHandler /* 56: DMA2 Stream 0 */ + .word DMA2_Stream1_IRQHandler /* 57: DMA2 Stream 1 */ + .word DMA2_Stream2_IRQHandler /* 58: DMA2 Stream 2 */ + .word DMA2_Stream3_IRQHandler /* 59: DMA2 Stream 3 */ + .word DMA2_Stream4_IRQHandler /* 60: DMA2 Stream 4 */ + .word DFSDM1_FLT0_IRQHandler /* 61: DFSDM1 Filter 0 */ + .word DFSDM1_FLT1_IRQHandler /* 62: DFSDM1 Filter 1 */ + .word CAN2_TX_IRQHandler /* 63: CAN2 TX */ + .word CAN2_RX0_IRQHandler /* 64: CAN2 RX0 */ + .word CAN2_RX1_IRQHandler /* 65: CAN2 RX1 */ + .word CAN2_SCE_IRQHandler /* 66: CAN2 SCE */ + .word OTG_FS_IRQHandler /* 67: USB OTG FS */ + .word DMA2_Stream5_IRQHandler /* 68: DMA2 Stream 5 */ + .word DMA2_Stream6_IRQHandler /* 69: DMA2 Stream 6 */ + .word DMA2_Stream7_IRQHandler /* 70: DMA2 Stream 7 */ + .word USART6_IRQHandler /* 71: USART6 */ + .word I2C3_EV_IRQHandler /* 72: I2C3 Event */ + .word I2C3_ER_IRQHandler /* 73: I2C3 Error */ + .word CAN3_TX_IRQHandler /* 74: CAN3 TX */ + .word CAN3_RX0_IRQHandler /* 75: CAN3 RX0 */ + .word CAN3_RX1_IRQHandler /* 76: CAN3 RX1 */ + .word CAN3_SCE_IRQHandler /* 77: CAN3 SCE */ + .word 0 /* 78: Reserved */ + .word 0 /* 79: Reserved */ + .word RNG_IRQHandler /* 80: RNG */ + .word FPU_IRQHandler /* 81: FPU */ + .word UART7_IRQHandler /* 82: UART7 */ + .word UART8_IRQHandler /* 83: UART8 */ + .word SPI4_IRQHandler /* 84: SPI4 */ + .word SPI5_IRQHandler /* 85: SPI5 */ + .word 0 /* 86: Reserved */ + .word SAI1_IRQHandler /* 87: SAI1 */ + .word UART9_IRQHandler /* 88: UART9 */ + .word UART10_IRQHandler /* 89: UART10 */ + .word 0 /* 90: Reserved */ + .word 0 /* 91: Reserved */ + .word QUADSPI_IRQHandler /* 92: QuadSPI */ + .word 0 /* 93: Reserved */ + .word 0 /* 94: Reserved */ + .word FMPI2C1_EV_IRQHandler /* 95: FMPI2C1 Event */ + .word FMPI2C1_ER_IRQHandler /* 96: FMPI2C1 Error */ + .word LPTIM1_IRQHandler /* 97: LPTIM1 */ + .word DFSDM2_FLT0_IRQHandler /* 98: DFSDM2 Filter 0 */ + .word DFSDM2_FLT1_IRQHandler /* 99: DFSDM2 Filter 1 */ + .word DFSDM2_FLT2_IRQHandler /* 100: DFSDM2 Filter 2 */ + .word DFSDM2_FLT3_IRQHandler /* 101: DFSDM2 Filter 3 */ + + .size g_pfnVectors, .-g_pfnVectors + +/******************************************************************************* + * Weak aliases - all handlers default to infinite loop unless overridden + *******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + .weak TAMP_STAMP_IRQHandler + .thumb_set TAMP_STAMP_IRQHandler,Default_Handler + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + .weak DMA1_Stream0_IRQHandler + .thumb_set DMA1_Stream0_IRQHandler,Default_Handler + .weak DMA1_Stream1_IRQHandler + .thumb_set DMA1_Stream1_IRQHandler,Default_Handler + .weak DMA1_Stream2_IRQHandler + .thumb_set DMA1_Stream2_IRQHandler,Default_Handler + .weak DMA1_Stream3_IRQHandler + .thumb_set DMA1_Stream3_IRQHandler,Default_Handler + .weak DMA1_Stream4_IRQHandler + .thumb_set DMA1_Stream4_IRQHandler,Default_Handler + .weak DMA1_Stream5_IRQHandler + .thumb_set DMA1_Stream5_IRQHandler,Default_Handler + .weak DMA1_Stream6_IRQHandler + .thumb_set DMA1_Stream6_IRQHandler,Default_Handler + .weak ADC_IRQHandler + .thumb_set ADC_IRQHandler,Default_Handler + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + .weak TIM1_BRK_TIM9_IRQHandler + .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler + .weak TIM1_UP_TIM10_IRQHandler + .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler + .weak TIM1_TRG_COM_TIM11_IRQHandler + .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + .weak OTG_FS_WKUP_IRQHandler + .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + .weak DMA1_Stream7_IRQHandler + .thumb_set DMA1_Stream7_IRQHandler,Default_Handler + .weak SDIO_IRQHandler + .thumb_set SDIO_IRQHandler,Default_Handler + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + .weak DMA2_Stream0_IRQHandler + .thumb_set DMA2_Stream0_IRQHandler,Default_Handler + .weak DMA2_Stream1_IRQHandler + .thumb_set DMA2_Stream1_IRQHandler,Default_Handler + .weak DMA2_Stream2_IRQHandler + .thumb_set DMA2_Stream2_IRQHandler,Default_Handler + .weak DMA2_Stream3_IRQHandler + .thumb_set DMA2_Stream3_IRQHandler,Default_Handler + .weak DMA2_Stream4_IRQHandler + .thumb_set DMA2_Stream4_IRQHandler,Default_Handler + .weak DFSDM1_FLT0_IRQHandler + .thumb_set DFSDM1_FLT0_IRQHandler,Default_Handler + .weak DFSDM1_FLT1_IRQHandler + .thumb_set DFSDM1_FLT1_IRQHandler,Default_Handler + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler,Default_Handler + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler,Default_Handler + .weak CAN2_SCE_IRQHandler + .thumb_set CAN2_SCE_IRQHandler,Default_Handler + .weak OTG_FS_IRQHandler + .thumb_set OTG_FS_IRQHandler,Default_Handler + .weak DMA2_Stream5_IRQHandler + .thumb_set DMA2_Stream5_IRQHandler,Default_Handler + .weak DMA2_Stream6_IRQHandler + .thumb_set DMA2_Stream6_IRQHandler,Default_Handler + .weak DMA2_Stream7_IRQHandler + .thumb_set DMA2_Stream7_IRQHandler,Default_Handler + .weak USART6_IRQHandler + .thumb_set USART6_IRQHandler,Default_Handler + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler + .weak CAN3_TX_IRQHandler + .thumb_set CAN3_TX_IRQHandler,Default_Handler + .weak CAN3_RX0_IRQHandler + .thumb_set CAN3_RX0_IRQHandler,Default_Handler + .weak CAN3_RX1_IRQHandler + .thumb_set CAN3_RX1_IRQHandler,Default_Handler + .weak CAN3_SCE_IRQHandler + .thumb_set CAN3_SCE_IRQHandler,Default_Handler + .weak RNG_IRQHandler + .thumb_set RNG_IRQHandler,Default_Handler + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + .weak UART7_IRQHandler + .thumb_set UART7_IRQHandler,Default_Handler + .weak UART8_IRQHandler + .thumb_set UART8_IRQHandler,Default_Handler + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler + .weak SPI5_IRQHandler + .thumb_set SPI5_IRQHandler,Default_Handler + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler + .weak UART9_IRQHandler + .thumb_set UART9_IRQHandler,Default_Handler + .weak UART10_IRQHandler + .thumb_set UART10_IRQHandler,Default_Handler + .weak QUADSPI_IRQHandler + .thumb_set QUADSPI_IRQHandler,Default_Handler + .weak FMPI2C1_EV_IRQHandler + .thumb_set FMPI2C1_EV_IRQHandler,Default_Handler + .weak FMPI2C1_ER_IRQHandler + .thumb_set FMPI2C1_ER_IRQHandler,Default_Handler + .weak LPTIM1_IRQHandler + .thumb_set LPTIM1_IRQHandler,Default_Handler + .weak DFSDM2_FLT0_IRQHandler + .thumb_set DFSDM2_FLT0_IRQHandler,Default_Handler + .weak DFSDM2_FLT1_IRQHandler + .thumb_set DFSDM2_FLT1_IRQHandler,Default_Handler + .weak DFSDM2_FLT2_IRQHandler + .thumb_set DFSDM2_FLT2_IRQHandler,Default_Handler + .weak DFSDM2_FLT3_IRQHandler + .thumb_set DFSDM2_FLT3_IRQHandler,Default_Handler + .section .text.SystemInit,"ax",%progbits + .weak SystemInit + .type SystemInit,%function +SystemInit: + bx lr + .size SystemInit, .-SystemInit diff --git a/platforms/stm32/bsp/bspMcu/startup/startup_stm32g474xx.s b/platforms/stm32/bsp/bspMcu/startup/startup_stm32g474xx.s new file mode 100644 index 00000000000..f5a4389c886 --- /dev/null +++ b/platforms/stm32/bsp/bspMcu/startup/startup_stm32g474xx.s @@ -0,0 +1,449 @@ +/******************************************************************************** + * Copyright (c) 2019 STMicroelectronics + * Copyright (c) 2026 An Dao + * + * Startup for STM32G474xx: vector table and Reset_Handler, based on the + * STMicroelectronics CMSIS device startup template for STM32G4. Vector + * table layout per reference manual RM0440. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + + .syntax unified + .cpu cortex-m4 + .fpu fpv4-sp-d16 + .thumb + +.global g_pfnVectors +.global Default_Handler + +.word _sidata +.word _sdata +.word _edata +.word _sbss +.word _ebss + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + ldr sp, =_estack + +/* Copy .data from FLASH to SRAM */ + ldr r0, =_sdata + ldr r1, =_edata + ldr r2, =_sidata + movs r3, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r4, [r2, r3] + str r4, [r0, r3] + adds r3, r3, #4 + +LoopCopyDataInit: + adds r4, r0, r3 + cmp r4, r1 + bcc CopyDataInit + +/* Zero fill .bss */ + ldr r2, =_sbss + ldr r4, =_ebss + movs r3, #0 + b LoopFillZerobss + +FillZerobss: + str r3, [r2] + adds r2, r2, #4 + +LoopFillZerobss: + cmp r2, r4 + bcc FillZerobss + +/* Enable FPU: set CP10/CP11 full access (required for ThreadX - FreeRTOS + enables this in port.c, but ThreadX does not) */ + ldr r0, =0xE000ED88 + ldr r1, [r0] + orr r1, r1, #(0xF << 20) + str r1, [r0] + dsb + isb + +/* Call SystemInit, C++ constructors, and main */ + bl SystemInit + bl __libc_init_array + bl main + bx lr + +.size Reset_Handler, .-Reset_Handler + + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler + +/****************************************************************************** + * Vector table for STM32G474xx (102 external interrupts, RM0440 Table 97) + ******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + +g_pfnVectors: + /* Cortex-M4 system exceptions */ + .word _estack /* Top of Stack */ + .word Reset_Handler /* Reset Handler */ + .word NMI_Handler /* NMI Handler */ + .word HardFault_Handler /* Hard Fault Handler */ + .word MemManage_Handler /* MPU Fault Handler */ + .word BusFault_Handler /* Bus Fault Handler */ + .word UsageFault_Handler /* Usage Fault Handler */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word 0 /* Reserved */ + .word SVC_Handler /* SVCall Handler */ + .word DebugMon_Handler /* Debug Monitor Handler */ + .word 0 /* Reserved */ + .word PendSV_Handler /* PendSV Handler */ + .word SysTick_Handler /* SysTick Handler */ + + /* External interrupts - STM32G474xx (IRQn 0–101) */ + .word WWDG_IRQHandler /* 0: Window Watchdog */ + .word PVD_PVM_IRQHandler /* 1: PVD/PVM through EXTI */ + .word RTC_TAMP_LSECSS_IRQHandler /* 2: RTC Tamper, TimeStamp, LSE CSS */ + .word RTC_WKUP_IRQHandler /* 3: RTC Wakeup through EXTI */ + .word FLASH_IRQHandler /* 4: Flash global */ + .word RCC_IRQHandler /* 5: RCC global */ + .word EXTI0_IRQHandler /* 6: EXTI Line 0 */ + .word EXTI1_IRQHandler /* 7: EXTI Line 1 */ + .word EXTI2_IRQHandler /* 8: EXTI Line 2 */ + .word EXTI3_IRQHandler /* 9: EXTI Line 3 */ + .word EXTI4_IRQHandler /* 10: EXTI Line 4 */ + .word DMA1_Channel1_IRQHandler /* 11: DMA1 Channel 1 */ + .word DMA1_Channel2_IRQHandler /* 12: DMA1 Channel 2 */ + .word DMA1_Channel3_IRQHandler /* 13: DMA1 Channel 3 */ + .word DMA1_Channel4_IRQHandler /* 14: DMA1 Channel 4 */ + .word DMA1_Channel5_IRQHandler /* 15: DMA1 Channel 5 */ + .word DMA1_Channel6_IRQHandler /* 16: DMA1 Channel 6 */ + .word DMA1_Channel7_IRQHandler /* 17: DMA1 Channel 7 */ + .word ADC1_2_IRQHandler /* 18: ADC1 and ADC2 */ + .word USB_HP_IRQHandler /* 19: USB High Priority */ + .word USB_LP_IRQHandler /* 20: USB Low Priority */ + .word FDCAN1_IT0_IRQHandler /* 21: FDCAN1 IT0 */ + .word FDCAN1_IT1_IRQHandler /* 22: FDCAN1 IT1 */ + .word EXTI9_5_IRQHandler /* 23: EXTI Lines [9:5] */ + .word TIM1_BRK_TIM15_IRQHandler /* 24: TIM1 Break and TIM15 */ + .word TIM1_UP_TIM16_IRQHandler /* 25: TIM1 Update and TIM16 */ + .word TIM1_TRG_COM_TIM17_IRQHandler /* 26: TIM1 Trigger/Commut and TIM17 */ + .word TIM1_CC_IRQHandler /* 27: TIM1 Capture Compare */ + .word TIM2_IRQHandler /* 28: TIM2 */ + .word TIM3_IRQHandler /* 29: TIM3 */ + .word TIM4_IRQHandler /* 30: TIM4 */ + .word I2C1_EV_IRQHandler /* 31: I2C1 Event */ + .word I2C1_ER_IRQHandler /* 32: I2C1 Error */ + .word I2C2_EV_IRQHandler /* 33: I2C2 Event */ + .word I2C2_ER_IRQHandler /* 34: I2C2 Error */ + .word SPI1_IRQHandler /* 35: SPI1 */ + .word SPI2_IRQHandler /* 36: SPI2 */ + .word USART1_IRQHandler /* 37: USART1 */ + .word USART2_IRQHandler /* 38: USART2 */ + .word USART3_IRQHandler /* 39: USART3 */ + .word EXTI15_10_IRQHandler /* 40: EXTI Lines [15:10] */ + .word RTC_Alarm_IRQHandler /* 41: RTC Alarm (A and B) via EXTI */ + .word USBWakeUp_IRQHandler /* 42: USB Wakeup via EXTI */ + .word TIM8_BRK_IRQHandler /* 43: TIM8 Break */ + .word TIM8_UP_IRQHandler /* 44: TIM8 Update */ + .word TIM8_TRG_COM_IRQHandler /* 45: TIM8 Trigger/Commutation */ + .word TIM8_CC_IRQHandler /* 46: TIM8 Capture Compare */ + .word ADC3_IRQHandler /* 47: ADC3 */ + .word FMC_IRQHandler /* 48: FMC */ + .word LPTIM1_IRQHandler /* 49: LPTIM1 */ + .word TIM5_IRQHandler /* 50: TIM5 */ + .word SPI3_IRQHandler /* 51: SPI3 */ + .word UART4_IRQHandler /* 52: UART4 */ + .word UART5_IRQHandler /* 53: UART5 */ + .word TIM6_DAC_IRQHandler /* 54: TIM6 and DAC1&3 underrun */ + .word TIM7_DAC_IRQHandler /* 55: TIM7 and DAC2&4 underrun */ + .word DMA2_Channel1_IRQHandler /* 56: DMA2 Channel 1 */ + .word DMA2_Channel2_IRQHandler /* 57: DMA2 Channel 2 */ + .word DMA2_Channel3_IRQHandler /* 58: DMA2 Channel 3 */ + .word DMA2_Channel4_IRQHandler /* 59: DMA2 Channel 4 */ + .word DMA2_Channel5_IRQHandler /* 60: DMA2 Channel 5 */ + .word ADC4_IRQHandler /* 61: ADC4 */ + .word ADC5_IRQHandler /* 62: ADC5 */ + .word UCPD1_IRQHandler /* 63: UCPD1 */ + .word COMP1_2_3_IRQHandler /* 64: COMP1, COMP2, COMP3 */ + .word COMP4_5_6_IRQHandler /* 65: COMP4, COMP5, COMP6 */ + .word COMP7_IRQHandler /* 66: COMP7 */ + .word HRTIM1_Master_IRQHandler /* 67: HRTIM Master Timer */ + .word HRTIM1_TIMA_IRQHandler /* 68: HRTIM Timer A */ + .word HRTIM1_TIMB_IRQHandler /* 69: HRTIM Timer B */ + .word HRTIM1_TIMC_IRQHandler /* 70: HRTIM Timer C */ + .word HRTIM1_TIMD_IRQHandler /* 71: HRTIM Timer D */ + .word HRTIM1_TIME_IRQHandler /* 72: HRTIM Timer E */ + .word HRTIM1_FLT_IRQHandler /* 73: HRTIM Fault */ + .word HRTIM1_TIMF_IRQHandler /* 74: HRTIM Timer F */ + .word CRS_IRQHandler /* 75: CRS */ + .word SAI1_IRQHandler /* 76: SAI1 */ + .word TIM20_BRK_IRQHandler /* 77: TIM20 Break */ + .word TIM20_UP_IRQHandler /* 78: TIM20 Update */ + .word TIM20_TRG_COM_IRQHandler /* 79: TIM20 Trigger/Commutation */ + .word TIM20_CC_IRQHandler /* 80: TIM20 Capture Compare */ + .word FPU_IRQHandler /* 81: FPU */ + .word I2C4_EV_IRQHandler /* 82: I2C4 Event */ + .word I2C4_ER_IRQHandler /* 83: I2C4 Error */ + .word SPI4_IRQHandler /* 84: SPI4 */ + .word 0 /* 85: Reserved */ + .word FDCAN2_IT0_IRQHandler /* 86: FDCAN2 IT0 */ + .word FDCAN2_IT1_IRQHandler /* 87: FDCAN2 IT1 */ + .word FDCAN3_IT0_IRQHandler /* 88: FDCAN3 IT0 */ + .word FDCAN3_IT1_IRQHandler /* 89: FDCAN3 IT1 */ + .word RNG_IRQHandler /* 90: RNG */ + .word LPUART1_IRQHandler /* 91: LPUART1 */ + .word I2C3_EV_IRQHandler /* 92: I2C3 Event */ + .word I2C3_ER_IRQHandler /* 93: I2C3 Error */ + .word DMAMUX_OVR_IRQHandler /* 94: DMAMUX overrun */ + .word QUADSPI_IRQHandler /* 95: QUADSPI */ + .word DMA1_Channel8_IRQHandler /* 96: DMA1 Channel 8 */ + .word DMA2_Channel6_IRQHandler /* 97: DMA2 Channel 6 */ + .word DMA2_Channel7_IRQHandler /* 98: DMA2 Channel 7 */ + .word DMA2_Channel8_IRQHandler /* 99: DMA2 Channel 8 */ + .word CORDIC_IRQHandler /* 100: CORDIC */ + .word FMAC_IRQHandler /* 101: FMAC */ + + .size g_pfnVectors, .-g_pfnVectors + +/******************************************************************************* + * Weak aliases - all handlers default to infinite loop unless overridden + *******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + .weak PVD_PVM_IRQHandler + .thumb_set PVD_PVM_IRQHandler,Default_Handler + .weak RTC_TAMP_LSECSS_IRQHandler + .thumb_set RTC_TAMP_LSECSS_IRQHandler,Default_Handler + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + .weak DMA1_Channel1_IRQHandler + .thumb_set DMA1_Channel1_IRQHandler,Default_Handler + .weak DMA1_Channel2_IRQHandler + .thumb_set DMA1_Channel2_IRQHandler,Default_Handler + .weak DMA1_Channel3_IRQHandler + .thumb_set DMA1_Channel3_IRQHandler,Default_Handler + .weak DMA1_Channel4_IRQHandler + .thumb_set DMA1_Channel4_IRQHandler,Default_Handler + .weak DMA1_Channel5_IRQHandler + .thumb_set DMA1_Channel5_IRQHandler,Default_Handler + .weak DMA1_Channel6_IRQHandler + .thumb_set DMA1_Channel6_IRQHandler,Default_Handler + .weak DMA1_Channel7_IRQHandler + .thumb_set DMA1_Channel7_IRQHandler,Default_Handler + .weak ADC1_2_IRQHandler + .thumb_set ADC1_2_IRQHandler,Default_Handler + .weak USB_HP_IRQHandler + .thumb_set USB_HP_IRQHandler,Default_Handler + .weak USB_LP_IRQHandler + .thumb_set USB_LP_IRQHandler,Default_Handler + .weak FDCAN1_IT0_IRQHandler + .thumb_set FDCAN1_IT0_IRQHandler,Default_Handler + .weak FDCAN1_IT1_IRQHandler + .thumb_set FDCAN1_IT1_IRQHandler,Default_Handler + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + .weak TIM1_BRK_TIM15_IRQHandler + .thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler + .weak TIM1_UP_TIM16_IRQHandler + .thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler + .weak TIM1_TRG_COM_TIM17_IRQHandler + .thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + .weak USBWakeUp_IRQHandler + .thumb_set USBWakeUp_IRQHandler,Default_Handler + .weak TIM8_BRK_IRQHandler + .thumb_set TIM8_BRK_IRQHandler,Default_Handler + .weak TIM8_UP_IRQHandler + .thumb_set TIM8_UP_IRQHandler,Default_Handler + .weak TIM8_TRG_COM_IRQHandler + .thumb_set TIM8_TRG_COM_IRQHandler,Default_Handler + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + .weak ADC3_IRQHandler + .thumb_set ADC3_IRQHandler,Default_Handler + .weak FMC_IRQHandler + .thumb_set FMC_IRQHandler,Default_Handler + .weak LPTIM1_IRQHandler + .thumb_set LPTIM1_IRQHandler,Default_Handler + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + .weak TIM7_DAC_IRQHandler + .thumb_set TIM7_DAC_IRQHandler,Default_Handler + .weak DMA2_Channel1_IRQHandler + .thumb_set DMA2_Channel1_IRQHandler,Default_Handler + .weak DMA2_Channel2_IRQHandler + .thumb_set DMA2_Channel2_IRQHandler,Default_Handler + .weak DMA2_Channel3_IRQHandler + .thumb_set DMA2_Channel3_IRQHandler,Default_Handler + .weak DMA2_Channel4_IRQHandler + .thumb_set DMA2_Channel4_IRQHandler,Default_Handler + .weak DMA2_Channel5_IRQHandler + .thumb_set DMA2_Channel5_IRQHandler,Default_Handler + .weak ADC4_IRQHandler + .thumb_set ADC4_IRQHandler,Default_Handler + .weak ADC5_IRQHandler + .thumb_set ADC5_IRQHandler,Default_Handler + .weak UCPD1_IRQHandler + .thumb_set UCPD1_IRQHandler,Default_Handler + .weak COMP1_2_3_IRQHandler + .thumb_set COMP1_2_3_IRQHandler,Default_Handler + .weak COMP4_5_6_IRQHandler + .thumb_set COMP4_5_6_IRQHandler,Default_Handler + .weak COMP7_IRQHandler + .thumb_set COMP7_IRQHandler,Default_Handler + .weak HRTIM1_Master_IRQHandler + .thumb_set HRTIM1_Master_IRQHandler,Default_Handler + .weak HRTIM1_TIMA_IRQHandler + .thumb_set HRTIM1_TIMA_IRQHandler,Default_Handler + .weak HRTIM1_TIMB_IRQHandler + .thumb_set HRTIM1_TIMB_IRQHandler,Default_Handler + .weak HRTIM1_TIMC_IRQHandler + .thumb_set HRTIM1_TIMC_IRQHandler,Default_Handler + .weak HRTIM1_TIMD_IRQHandler + .thumb_set HRTIM1_TIMD_IRQHandler,Default_Handler + .weak HRTIM1_TIME_IRQHandler + .thumb_set HRTIM1_TIME_IRQHandler,Default_Handler + .weak HRTIM1_FLT_IRQHandler + .thumb_set HRTIM1_FLT_IRQHandler,Default_Handler + .weak HRTIM1_TIMF_IRQHandler + .thumb_set HRTIM1_TIMF_IRQHandler,Default_Handler + .weak CRS_IRQHandler + .thumb_set CRS_IRQHandler,Default_Handler + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler + .weak TIM20_BRK_IRQHandler + .thumb_set TIM20_BRK_IRQHandler,Default_Handler + .weak TIM20_UP_IRQHandler + .thumb_set TIM20_UP_IRQHandler,Default_Handler + .weak TIM20_TRG_COM_IRQHandler + .thumb_set TIM20_TRG_COM_IRQHandler,Default_Handler + .weak TIM20_CC_IRQHandler + .thumb_set TIM20_CC_IRQHandler,Default_Handler + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + .weak I2C4_EV_IRQHandler + .thumb_set I2C4_EV_IRQHandler,Default_Handler + .weak I2C4_ER_IRQHandler + .thumb_set I2C4_ER_IRQHandler,Default_Handler + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler + .weak FDCAN2_IT0_IRQHandler + .thumb_set FDCAN2_IT0_IRQHandler,Default_Handler + .weak FDCAN2_IT1_IRQHandler + .thumb_set FDCAN2_IT1_IRQHandler,Default_Handler + .weak FDCAN3_IT0_IRQHandler + .thumb_set FDCAN3_IT0_IRQHandler,Default_Handler + .weak FDCAN3_IT1_IRQHandler + .thumb_set FDCAN3_IT1_IRQHandler,Default_Handler + .weak RNG_IRQHandler + .thumb_set RNG_IRQHandler,Default_Handler + .weak LPUART1_IRQHandler + .thumb_set LPUART1_IRQHandler,Default_Handler + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler + .weak DMAMUX_OVR_IRQHandler + .thumb_set DMAMUX_OVR_IRQHandler,Default_Handler + .weak QUADSPI_IRQHandler + .thumb_set QUADSPI_IRQHandler,Default_Handler + .weak DMA1_Channel8_IRQHandler + .thumb_set DMA1_Channel8_IRQHandler,Default_Handler + .weak DMA2_Channel6_IRQHandler + .thumb_set DMA2_Channel6_IRQHandler,Default_Handler + .weak DMA2_Channel7_IRQHandler + .thumb_set DMA2_Channel7_IRQHandler,Default_Handler + .weak DMA2_Channel8_IRQHandler + .thumb_set DMA2_Channel8_IRQHandler,Default_Handler + .weak CORDIC_IRQHandler + .thumb_set CORDIC_IRQHandler,Default_Handler + .weak FMAC_IRQHandler + .thumb_set FMAC_IRQHandler,Default_Handler + .section .text.SystemInit,"ax",%progbits + .weak SystemInit + .type SystemInit,%function +SystemInit: + bx lr + .size SystemInit, .-SystemInit diff --git a/platforms/stm32/cmake/stm32f413zh.cmake b/platforms/stm32/cmake/stm32f413zh.cmake new file mode 100644 index 00000000000..4c52f9a54a4 --- /dev/null +++ b/platforms/stm32/cmake/stm32f413zh.cmake @@ -0,0 +1,15 @@ +# ******************************************************************************* +# Copyright (c) 2026 An Dao +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +set(STM32_FAMILY "F4") +set(STM32_DEVICE_UPPER "STM32F413xx") +set(CAN_TYPE "BXCAN") +set(STM32_STARTUP_ASM + "${CMAKE_CURRENT_LIST_DIR}/../bsp/bspMcu/startup/startup_stm32f413xx.s") diff --git a/platforms/stm32/cmake/stm32g474re.cmake b/platforms/stm32/cmake/stm32g474re.cmake new file mode 100644 index 00000000000..64a70fabdc2 --- /dev/null +++ b/platforms/stm32/cmake/stm32g474re.cmake @@ -0,0 +1,15 @@ +# ******************************************************************************* +# Copyright (c) 2026 An Dao +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +set(STM32_FAMILY "G4") +set(STM32_DEVICE_UPPER "STM32G474xx") +set(CAN_TYPE "FDCAN") +set(STM32_STARTUP_ASM + "${CMAKE_CURRENT_LIST_DIR}/../bsp/bspMcu/startup/startup_stm32g474xx.s") diff --git a/platforms/stm32/unitTest/CMakeLists.txt b/platforms/stm32/unitTest/CMakeLists.txt new file mode 100644 index 00000000000..d0b1953a923 --- /dev/null +++ b/platforms/stm32/unitTest/CMakeLists.txt @@ -0,0 +1 @@ +# add STM32 specific configuration modules and settings here From e5a7dd943a72e94cd78740fde5129b6dece7c166 Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Tue, 2 Jun 2026 08:10:50 +0200 Subject: [PATCH 03/11] Add STM32 BSP peripheral drivers Add STM32 BSP modules for clock, UART, GPIO, timer, ADC, and EEPROM, plus interrupt handling primitives and the ETL platform glue. --- doc/dev/modules/stm32.rst | 2 +- platforms/stm32/CMakeLists.txt | 3 + platforms/stm32/bsp/CMakeLists.txt | 18 ++ platforms/stm32/bsp/bspAdc/CMakeLists.txt | 6 + platforms/stm32/bsp/bspAdc/include/adc/Adc.h | 54 ++++ platforms/stm32/bsp/bspAdc/module.spec | 2 + platforms/stm32/bsp/bspAdc/src/adc/Adc.cpp | 179 +++++++++++ platforms/stm32/bsp/bspClock/CMakeLists.txt | 10 + platforms/stm32/bsp/bspClock/doc/index.rst | 43 +++ .../bsp/bspClock/include/clock/clockConfig.h | 24 ++ platforms/stm32/bsp/bspClock/module.spec | 2 + .../stm32/bsp/bspClock/src/clockConfig_f4.cpp | 68 ++++ .../stm32/bsp/bspClock/src/clockConfig_g4.cpp | 101 ++++++ .../stm32/bsp/bspEepromDriver/CMakeLists.txt | 6 + .../include/eeprom/FlashEepromDriver.h | 59 ++++ .../stm32/bsp/bspEepromDriver/module.spec | 1 + .../src/eeprom/FlashEepromDriver.cpp | 303 ++++++++++++++++++ .../bsp/bspInterruptsImpl/CMakeLists.txt | 18 ++ .../stm32/bsp/bspInterruptsImpl/doc/index.rst | 111 +++++++ .../interrupts/suspendResumeAllInterrupts.h | 37 +++ .../interrupts/disableEnableAllInterrupts.h | 35 ++ .../stm32/bsp/bspInterruptsImpl/module.spec | 2 + .../bspInterruptsImpl/src/interrupt_manager.c | 14 + .../interrupts/suspendResumeAllInterrupts.h | 34 ++ platforms/stm32/bsp/bspIo/CMakeLists.txt | 6 + platforms/stm32/bsp/bspIo/include/io/Gpio.h | 80 +++++ .../bsp/bspIo/include/io/GpioPinConfig.h | 77 +++++ platforms/stm32/bsp/bspIo/module.spec | 2 + platforms/stm32/bsp/bspIo/src/io/Gpio.cpp | 175 ++++++++++ platforms/stm32/bsp/bspTimer/CMakeLists.txt | 3 + platforms/stm32/bsp/bspTimer/doc/index.rst | 35 ++ platforms/stm32/bsp/bspTimer/module.spec | 2 + .../src/bsp/SystemTimer/SystemTimer.cpp | 95 ++++++ platforms/stm32/bsp/bspUart/CMakeLists.txt | 13 + platforms/stm32/bsp/bspUart/doc/index.rst | 41 +++ .../stm32/bsp/bspUart/include/bsp/Uart.h | 43 +++ .../bsp/bspUart/include/bsp/UartParams.h | 34 ++ platforms/stm32/bsp/bspUart/module.spec | 2 + platforms/stm32/bsp/bspUart/src/Uart.cpp | 127 ++++++++ platforms/stm32/etlImpl/CMakeLists.txt | 3 + platforms/stm32/etlImpl/src/clocks.cpp | 30 ++ platforms/stm32/etlImpl/src/print.cpp | 14 + 42 files changed, 1913 insertions(+), 1 deletion(-) create mode 100644 platforms/stm32/bsp/bspAdc/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspAdc/include/adc/Adc.h create mode 100644 platforms/stm32/bsp/bspAdc/module.spec create mode 100644 platforms/stm32/bsp/bspAdc/src/adc/Adc.cpp create mode 100644 platforms/stm32/bsp/bspClock/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspClock/doc/index.rst create mode 100644 platforms/stm32/bsp/bspClock/include/clock/clockConfig.h create mode 100644 platforms/stm32/bsp/bspClock/module.spec create mode 100644 platforms/stm32/bsp/bspClock/src/clockConfig_f4.cpp create mode 100644 platforms/stm32/bsp/bspClock/src/clockConfig_g4.cpp create mode 100644 platforms/stm32/bsp/bspEepromDriver/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspEepromDriver/include/eeprom/FlashEepromDriver.h create mode 100644 platforms/stm32/bsp/bspEepromDriver/module.spec create mode 100644 platforms/stm32/bsp/bspEepromDriver/src/eeprom/FlashEepromDriver.cpp create mode 100644 platforms/stm32/bsp/bspInterruptsImpl/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspInterruptsImpl/doc/index.rst create mode 100644 platforms/stm32/bsp/bspInterruptsImpl/freertos/include/interrupts/suspendResumeAllInterrupts.h create mode 100644 platforms/stm32/bsp/bspInterruptsImpl/include/interrupts/disableEnableAllInterrupts.h create mode 100644 platforms/stm32/bsp/bspInterruptsImpl/module.spec create mode 100644 platforms/stm32/bsp/bspInterruptsImpl/src/interrupt_manager.c create mode 100644 platforms/stm32/bsp/bspInterruptsImpl/threadx/include/interrupts/suspendResumeAllInterrupts.h create mode 100644 platforms/stm32/bsp/bspIo/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspIo/include/io/Gpio.h create mode 100644 platforms/stm32/bsp/bspIo/include/io/GpioPinConfig.h create mode 100644 platforms/stm32/bsp/bspIo/module.spec create mode 100644 platforms/stm32/bsp/bspIo/src/io/Gpio.cpp create mode 100644 platforms/stm32/bsp/bspTimer/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspTimer/doc/index.rst create mode 100644 platforms/stm32/bsp/bspTimer/module.spec create mode 100644 platforms/stm32/bsp/bspTimer/src/bsp/SystemTimer/SystemTimer.cpp create mode 100644 platforms/stm32/bsp/bspUart/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspUart/doc/index.rst create mode 100644 platforms/stm32/bsp/bspUart/include/bsp/Uart.h create mode 100644 platforms/stm32/bsp/bspUart/include/bsp/UartParams.h create mode 100644 platforms/stm32/bsp/bspUart/module.spec create mode 100644 platforms/stm32/bsp/bspUart/src/Uart.cpp create mode 100644 platforms/stm32/etlImpl/CMakeLists.txt create mode 100644 platforms/stm32/etlImpl/src/clocks.cpp create mode 100644 platforms/stm32/etlImpl/src/print.cpp diff --git a/doc/dev/modules/stm32.rst b/doc/dev/modules/stm32.rst index 66660f2761b..3c7b5eb2891 100644 --- a/doc/dev/modules/stm32.rst +++ b/doc/dev/modules/stm32.rst @@ -19,4 +19,4 @@ BSP :maxdepth: 1 :glob: - ../../../platforms/stm32/bsp/**/doc/index + ../../../platforms/stm32/**/doc/index diff --git a/platforms/stm32/CMakeLists.txt b/platforms/stm32/CMakeLists.txt index dbe6c9fd805..d49dd6f1f71 100644 --- a/platforms/stm32/CMakeLists.txt +++ b/platforms/stm32/CMakeLists.txt @@ -17,4 +17,7 @@ if (NOT BUILD_EXECUTABLE STREQUAL "unitTest") endif () add_subdirectory(bsp) + + # ETL implementation + add_subdirectory(etlImpl) endif () diff --git a/platforms/stm32/bsp/CMakeLists.txt b/platforms/stm32/bsp/CMakeLists.txt index c377df0854c..41c252d17db 100644 --- a/platforms/stm32/bsp/CMakeLists.txt +++ b/platforms/stm32/bsp/CMakeLists.txt @@ -1 +1,19 @@ add_subdirectory(bspMcu) +add_subdirectory(bspClock) +add_subdirectory(bspInterruptsImpl) +add_subdirectory(bspUart) +add_subdirectory(bspTimer) +add_subdirectory(bspIo) +add_subdirectory(bspAdc) +add_subdirectory(bspEepromDriver) + +# Aggregated BSP target +add_library(socBsp INTERFACE) +target_link_libraries( + socBsp + INTERFACE bspClock + bspInterruptsImpl + bspIo + bspMcu + bspTimer + bspUart) diff --git a/platforms/stm32/bsp/bspAdc/CMakeLists.txt b/platforms/stm32/bsp/bspAdc/CMakeLists.txt new file mode 100644 index 00000000000..31856b6f58d --- /dev/null +++ b/platforms/stm32/bsp/bspAdc/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(bspAdc src/adc/Adc.cpp) +target_include_directories(bspAdc PUBLIC include) +target_link_libraries( + bspAdc + PUBLIC bspMcu + PRIVATE platform) diff --git a/platforms/stm32/bsp/bspAdc/include/adc/Adc.h b/platforms/stm32/bsp/bspAdc/include/adc/Adc.h new file mode 100644 index 00000000000..343d51920b7 --- /dev/null +++ b/platforms/stm32/bsp/bspAdc/include/adc/Adc.h @@ -0,0 +1,54 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include + +namespace bios +{ + +enum class AdcResolution : uint8_t +{ + BITS_12 = 0U, + BITS_10 = 1U, + BITS_8 = 2U, + BITS_6 = 3U +}; + +struct AdcConfig +{ + ADC_TypeDef* peripheral; + AdcResolution resolution; + uint8_t samplingTime; // SMPR code (0-7) +}; + +class Adc +{ +public: + explicit Adc(AdcConfig const& config); + + void init(); + uint16_t readChannel(uint8_t channel); + uint16_t readTemperature(); + uint16_t readVrefint(); + +private: + AdcConfig const fConfig; + bool fInitialized; + + void enableClock(); + void calibrate(); + void configureChannel(uint8_t channel); + uint16_t startAndRead(); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bspAdc/module.spec b/platforms/stm32/bsp/bspAdc/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspAdc/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspAdc/src/adc/Adc.cpp b/platforms/stm32/bsp/bspAdc/src/adc/Adc.cpp new file mode 100644 index 00000000000..a8a65ae661f --- /dev/null +++ b/platforms/stm32/bsp/bspAdc/src/adc/Adc.cpp @@ -0,0 +1,179 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +namespace bios +{ + +Adc::Adc(AdcConfig const& config) : fConfig(config), fInitialized(false) {} + +void Adc::enableClock() +{ +#if defined(STM32G474xx) + RCC->AHB2ENR |= RCC_AHB2ENR_ADC12EN; + uint32_t volatile dummy = RCC->AHB2ENR; + (void)dummy; + + // Select system clock as ADC clock (ADCSEL = 01 in CCIPR) + RCC->CCIPR = (RCC->CCIPR & ~(3U << 28U)) | (1U << 28U); +#elif defined(STM32F413xx) + RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; + uint32_t volatile dummy = RCC->APB2ENR; + (void)dummy; +#endif +} + +void Adc::calibrate() +{ +#if defined(STM32G474xx) + ADC_TypeDef* adc = fConfig.peripheral; + + adc->CR &= ~ADC_CR_ADEN; + adc->CR &= ~ADC_CR_DEEPPWD; + adc->CR |= ADC_CR_ADVREGEN; + + // Wait for regulator startup (~20us) + for (uint32_t volatile i = 0U; i < 10000U; i++) {} + + adc->CR &= ~ADC_CR_ADCALDIF; + adc->CR |= ADC_CR_ADCAL; +#if !defined(UNIT_TEST) + while ((adc->CR & ADC_CR_ADCAL) != 0U) {} +#else + adc->CR &= ~ADC_CR_ADCAL; // Simulate calibration complete +#endif +#elif defined(STM32F413xx) + // STM32F4 ADC has no hardware calibration sequence + fConfig.peripheral->CR2 &= ~ADC_CR2_ADON; +#endif +} + +void Adc::init() +{ + enableClock(); + calibrate(); + + ADC_TypeDef* adc = fConfig.peripheral; + +#if defined(STM32G474xx) + adc->CFGR = (adc->CFGR & ~ADC_CFGR_RES) | (static_cast(fConfig.resolution) << 3U); + adc->CFGR &= ~(ADC_CFGR_CONT | ADC_CFGR_EXTEN); + adc->CFGR &= ~ADC_CFGR_ALIGN; + + adc->ISR |= ADC_ISR_ADRDY; // Write-1-to-clear ready flag + adc->CR |= ADC_CR_ADEN; + while ((adc->ISR & ADC_ISR_ADRDY) == 0U) {} +#elif defined(STM32F413xx) + adc->CR1 = (adc->CR1 & ~ADC_CR1_RES) | (static_cast(fConfig.resolution) << 24U); + adc->CR2 &= ~(ADC_CR2_CONT | ADC_CR2_ALIGN); + + ADC_Common_TypeDef* common = ADC123_COMMON; + common->CCR = (common->CCR & ~ADC_CCR_ADCPRE) | (1U << 16U); // PCLK2/4 + common->CCR |= ADC_CCR_TSVREFE; + + adc->CR2 |= ADC_CR2_ADON; +#endif + + fInitialized = true; +} + +void Adc::configureChannel(uint8_t channel) +{ + ADC_TypeDef* adc = fConfig.peripheral; + +#if defined(STM32G474xx) + adc->SQR1 = (adc->SQR1 & ~(ADC_SQR1_L | ADC_SQR1_SQ1)) + | (static_cast(channel) << 6U); // L=0 (1 conv), SQ1=channel + + if (channel < 10U) + { + uint32_t pos = channel * 3U; + adc->SMPR1 + = (adc->SMPR1 & ~(7U << pos)) | (static_cast(fConfig.samplingTime) << pos); + } + else + { + uint32_t pos = (channel - 10U) * 3U; + adc->SMPR2 + = (adc->SMPR2 & ~(7U << pos)) | (static_cast(fConfig.samplingTime) << pos); + } +#elif defined(STM32F413xx) + adc->SQR1 &= ~ADC_SQR1_L; // L=0 (1 conv) + adc->SQR3 = (adc->SQR3 & ~0x1FU) | (channel & 0x1FU); // SQ1=channel + + // F4: SMPR2 covers channels 0-9, SMPR1 covers 10-18 (reversed vs G4) + if (channel < 10U) + { + uint32_t pos = channel * 3U; + adc->SMPR2 + = (adc->SMPR2 & ~(7U << pos)) | (static_cast(fConfig.samplingTime) << pos); + } + else + { + uint32_t pos = (channel - 10U) * 3U; + adc->SMPR1 + = (adc->SMPR1 & ~(7U << pos)) | (static_cast(fConfig.samplingTime) << pos); + } +#endif +} + +uint16_t Adc::startAndRead() +{ + ADC_TypeDef* adc = fConfig.peripheral; + +#if defined(STM32G474xx) + adc->ISR |= ADC_ISR_EOC; // Write-1-to-clear EOC + adc->CR |= ADC_CR_ADSTART; + while ((adc->ISR & ADC_ISR_EOC) == 0U) {} + return static_cast(adc->DR); +#elif defined(STM32F413xx) + adc->SR &= ~ADC_SR_EOC; + adc->CR2 |= ADC_CR2_SWSTART; + while ((adc->SR & ADC_SR_EOC) == 0U) {} + return static_cast(adc->DR); +#else + return 0U; +#endif +} + +uint16_t Adc::readChannel(uint8_t channel) +{ + if (!fInitialized) + { + return 0U; + } + configureChannel(channel); + return startAndRead(); +} + +uint16_t Adc::readTemperature() +{ +#if defined(STM32G474xx) + return readChannel(16U); // VSENSE on ADC1 ch16 +#elif defined(STM32F413xx) + return readChannel(18U); // VSENSE on ADC1 ch18 +#else + return 0U; +#endif +} + +uint16_t Adc::readVrefint() +{ +#if defined(STM32G474xx) + return readChannel(18U); // VREFINT on ADC1 ch18 +#elif defined(STM32F413xx) + return readChannel(17U); // VREFINT on ADC1 ch17 +#else + return 0U; +#endif +} + +} // namespace bios diff --git a/platforms/stm32/bsp/bspClock/CMakeLists.txt b/platforms/stm32/bsp/bspClock/CMakeLists.txt new file mode 100644 index 00000000000..ee439b596a5 --- /dev/null +++ b/platforms/stm32/bsp/bspClock/CMakeLists.txt @@ -0,0 +1,10 @@ +# Clock configuration - chip-specific source selected by STM32_FAMILY +if (STM32_FAMILY STREQUAL "F4") + add_library(bspClock src/clockConfig_f4.cpp) +elseif (STM32_FAMILY STREQUAL "G4") + add_library(bspClock src/clockConfig_g4.cpp) +endif () + +target_include_directories(bspClock PUBLIC include) + +target_link_libraries(bspClock PRIVATE bspMcu) diff --git a/platforms/stm32/bsp/bspClock/doc/index.rst b/platforms/stm32/bsp/bspClock/doc/index.rst new file mode 100644 index 00000000000..6a423da77db --- /dev/null +++ b/platforms/stm32/bsp/bspClock/doc/index.rst @@ -0,0 +1,43 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspClock +======== + +Overview +-------- + +The ``bspClock`` module configures the system clock PLL for STM32F4 and STM32G4 +targets. Each family has its own translation unit (``clockConfig_f4.cpp``, +``clockConfig_g4.cpp``); the public API is a single ``extern "C"`` function +``configurePll()`` declared in ``clock/clockConfig.h``. It is called from the +startup code before ``main()`` and must not use heap, RTOS primitives, or BSW +services. On a PLL or oscillator timeout the system stays on HSI 16 MHz and +``SystemCoreClock`` is not updated. + +STM32F4 (STM32F413ZH) -- 96 MHz +------------------------------- + +- Clock source: HSE 8 MHz bypass (ST-LINK MCO on NUCLEO-F413ZH) +- PLL: M = 8, N = 384, P = 4 -> SYSCLK = 96 MHz +- AHB = 96 MHz, APB1 = 48 MHz (max 50 MHz per datasheet), APB2 = 96 MHz +- Flash latency: 3 wait states at 3.3 V, prefetch and caches enabled + +STM32G4 (STM32G474RE) -- 170 MHz +-------------------------------- + +- Clock source: HSI 16 MHz. HSE is not used because solder bridge SB15 may + not route the ST-LINK V3 MCO to PH0/OSC_IN on all NUCLEO-G474RE revisions. +- PLL: M = 4, N = 85, R = 2 -> SYSCLK = 170 MHz (voltage Range 1 boost mode) +- AHB = APB1 = APB2 = 170 MHz +- Flash latency: 4 wait states; ``FLASH_ACR`` is written read-modify-write to + preserve ``DBG_SWEN``. The switch above 150 MHz follows the AHB prescaler + transition sequence from RM0440 section 6.1.5. diff --git a/platforms/stm32/bsp/bspClock/include/clock/clockConfig.h b/platforms/stm32/bsp/bspClock/include/clock/clockConfig.h new file mode 100644 index 00000000000..2b7e0694111 --- /dev/null +++ b/platforms/stm32/bsp/bspClock/include/clock/clockConfig.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#ifdef __cplusplus +extern "C" +{ +#endif + +// Implemented per target in clockConfig_*.cpp. Called from startup code +// before main(); must not use heap, RTOS primitives, or BSW services. +void configurePll(void); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/stm32/bsp/bspClock/module.spec b/platforms/stm32/bsp/bspClock/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspClock/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspClock/src/clockConfig_f4.cpp b/platforms/stm32/bsp/bspClock/src/clockConfig_f4.cpp new file mode 100644 index 00000000000..5c131eb55ac --- /dev/null +++ b/platforms/stm32/bsp/bspClock/src/clockConfig_f4.cpp @@ -0,0 +1,68 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +uint32_t SystemCoreClock = 16000000U; // Default HSI, updated by configurePll() + +// Clock stabilization timeout (~100 ms at 16 MHz HSI = 1.6M cycles). +static constexpr uint32_t CLK_TIMEOUT = 1600000U; + +extern "C" void configurePll() +{ + // Enable HSE bypass (8 MHz from ST-LINK) + RCC->CR |= RCC_CR_HSEBYP | RCC_CR_HSEON; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CR & RCC_CR_HSERDY) == 0U) + { + if (--t == 0U) + { + return; + } + } + } + + // Configure PLL: HSE / M=8 * N=384 / P=4 = 96 MHz + RCC->PLLCFGR = (8U << RCC_PLLCFGR_PLLM_Pos) // M = 8 -> VCO input = 1 MHz + | (384U << RCC_PLLCFGR_PLLN_Pos) // N = 384 -> VCO = 384 MHz + | (1U << RCC_PLLCFGR_PLLP_Pos) // P = 4 (register value 1) -> 96 MHz + | RCC_PLLCFGR_PLLSRC_HSE; + + RCC->CR |= RCC_CR_PLLON; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CR & RCC_CR_PLLRDY) == 0U) + { + if (--t == 0U) + { + return; + } + } + } + + // Flash latency 3 WS for 96 MHz @ 3.3V + FLASH->ACR = FLASH_ACR_LATENCY_3WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN; + + // AHB = SYSCLK, APB1 = SYSCLK/2, APB2 = SYSCLK + RCC->CFGR = RCC_CFGR_PPRE1_DIV2 | RCC_CFGR_SW_PLL; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) + { + if (--t == 0U) + { + return; + } + } + } + + SystemCoreClock = 96000000U; +} diff --git a/platforms/stm32/bsp/bspClock/src/clockConfig_g4.cpp b/platforms/stm32/bsp/bspClock/src/clockConfig_g4.cpp new file mode 100644 index 00000000000..e3cd334e376 --- /dev/null +++ b/platforms/stm32/bsp/bspClock/src/clockConfig_g4.cpp @@ -0,0 +1,101 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +uint32_t SystemCoreClock = 16000000U; // Default HSI, updated by configurePll() + +// Clock stabilization timeout (~100 ms at 16 MHz HSI = 1.6M cycles). +// On timeout the system stays on HSI 16 MHz and SystemCoreClock is NOT +// updated, indicating to downstream code that PLL init failed. +static constexpr uint32_t CLK_TIMEOUT = 1600000U; + +extern "C" void configurePll() +{ + RCC->APB1ENR1 |= RCC_APB1ENR1_PWREN; + uint32_t volatile dummy = RCC->APB1ENR1; // Read-back for clock enable delay + (void)dummy; + + // Enable Range 1 boost mode (required for >150 MHz) + PWR->CR5 &= ~PWR_CR5_R1MODE; + // Wait for voltage scaling to stabilize + { + uint32_t t = CLK_TIMEOUT; + while ((PWR->SR2 & PWR_SR2_VOSF) != 0U) + { + if (--t == 0U) + { + return; + } + } + } + + // Configure PLL: HSI / M=4 * N=85 / R=2 = 170 MHz. + // HSI instead of HSE: NUCLEO-G474RE solder bridge SB15 may not route + // ST-LINK V3 MCO to PH0/OSC_IN on all board revisions. + RCC->PLLCFGR = (3U << RCC_PLLCFGR_PLLM_Pos) // M = 4 (register value M-1 = 3) + | (85U << RCC_PLLCFGR_PLLN_Pos) // N = 85 + | RCC_PLLCFGR_PLLSRC_HSI + | RCC_PLLCFGR_PLLREN; // Enable PLL R output (SYSCLK source) + + RCC->CR |= RCC_CR_PLLON; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CR & RCC_CR_PLLRDY) == 0U) + { + if (--t == 0U) + { + return; + } + } + } + + // Flash latency 4 WS for 170 MHz @ 3.3V boost mode + // Preserve DBG_SWEN (bit 18) - clearing it kills SWD debug flash access + { + uint32_t acr = FLASH->ACR; + acr &= ~FLASH_ACR_LATENCY_Msk; // Clear only latency bits, keep DBG_SWEN etc + acr |= FLASH_ACR_LATENCY_4WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN; + FLASH->ACR = acr; + } + // Verify flash latency readback before switching clock (RM0440 section 3.3.3) + { + uint32_t t = CLK_TIMEOUT; + while ((FLASH->ACR & FLASH_ACR_LATENCY) != FLASH_ACR_LATENCY_4WS) + { + if (--t == 0U) + { + return; + } + } + } + + // Boost-mode >150 MHz transition: set AHB prescaler /2 before clock switch + RCC->CFGR = RCC_CFGR_HPRE_DIV2 | RCC_CFGR_SW_PLL; + { + uint32_t t = CLK_TIMEOUT; + while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL) + { + if (--t == 0U) + { + return; + } + } + } + + // Wait >= 1 us for voltage regulator to settle at new frequency + // At 170/2 = 85 MHz, 100 cycles > 1 us + for (uint32_t volatile i = 0U; i < 100U; i++) {} + + // Restore AHB prescaler to /1 + RCC->CFGR = (RCC->CFGR & ~RCC_CFGR_HPRE_Msk) | RCC_CFGR_HPRE_DIV1; + + SystemCoreClock = 170000000U; +} diff --git a/platforms/stm32/bsp/bspEepromDriver/CMakeLists.txt b/platforms/stm32/bsp/bspEepromDriver/CMakeLists.txt new file mode 100644 index 00000000000..4b70b48601c --- /dev/null +++ b/platforms/stm32/bsp/bspEepromDriver/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(bspEepromDriver src/eeprom/FlashEepromDriver.cpp) +target_include_directories(bspEepromDriver PUBLIC include) +target_link_libraries( + bspEepromDriver + PUBLIC bspMcu bsp + PRIVATE platform) diff --git a/platforms/stm32/bsp/bspEepromDriver/include/eeprom/FlashEepromDriver.h b/platforms/stm32/bsp/bspEepromDriver/include/eeprom/FlashEepromDriver.h new file mode 100644 index 00000000000..65add360aeb --- /dev/null +++ b/platforms/stm32/bsp/bspEepromDriver/include/eeprom/FlashEepromDriver.h @@ -0,0 +1,59 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace eeprom +{ + +// EEPROM emulation in internal flash using two pages in a ping-pong scheme: +// the active page holds current data behind a magic marker, the inactive +// page is erased and reprogrammed on each write, then the roles swap. +class FlashEepromDriver : public IEepromDriver +{ +public: + struct Config + { + uintptr_t page0Address; + uintptr_t page1Address; + uint32_t pageSize; + }; + + explicit FlashEepromDriver(Config const& config); + + bsp::BspReturnCode init() override; + bsp::BspReturnCode write(uint32_t address, uint8_t const* buffer, uint32_t length) override; + bsp::BspReturnCode read(uint32_t address, uint8_t* buffer, uint32_t length) override; + + bsp::BspReturnCode erase(); + +private: + static uint32_t const MAGIC = 0xEE50AA55U; // Page validity marker + + Config const fConfig; + uint8_t fActivePage; + + uintptr_t activePageAddress() const; + uintptr_t inactivePageAddress() const; + + bool isPageValid(uintptr_t pageAddr) const; + bsp::BspReturnCode erasePage(uintptr_t pageAddr); + bsp::BspReturnCode programPage(uintptr_t destAddr, uint8_t const* data, uint32_t length); + + bsp::BspReturnCode unlockFlash(); + void lockFlash(); + bsp::BspReturnCode waitForFlash(); +}; + +} // namespace eeprom diff --git a/platforms/stm32/bsp/bspEepromDriver/module.spec b/platforms/stm32/bsp/bspEepromDriver/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/platforms/stm32/bsp/bspEepromDriver/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/platforms/stm32/bsp/bspEepromDriver/src/eeprom/FlashEepromDriver.cpp b/platforms/stm32/bsp/bspEepromDriver/src/eeprom/FlashEepromDriver.cpp new file mode 100644 index 00000000000..db96d3c9e45 --- /dev/null +++ b/platforms/stm32/bsp/bspEepromDriver/src/eeprom/FlashEepromDriver.cpp @@ -0,0 +1,303 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +#include +#include + +namespace eeprom +{ + +FlashEepromDriver::FlashEepromDriver(Config const& config) : fConfig(config), fActivePage(0U) {} + +bsp::BspReturnCode FlashEepromDriver::init() +{ + if (isPageValid(fConfig.page0Address)) + { + fActivePage = 0U; + } + else if (isPageValid(fConfig.page1Address)) + { + fActivePage = 1U; + } + else + { + // Neither page valid - format page 0 + if (erasePage(fConfig.page0Address) != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + + if (unlockFlash() != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + uint32_t const magic = MAGIC; + if (programPage(fConfig.page0Address, reinterpret_cast(&magic), 4U) + != bsp::BSP_OK) + { + lockFlash(); + return bsp::BSP_ERROR; + } + lockFlash(); + fActivePage = 0U; + } + + return bsp::BSP_OK; +} + +bsp::BspReturnCode FlashEepromDriver::read(uint32_t address, uint8_t* buffer, uint32_t length) +{ + uint32_t dataOffset = 4U; // Skip magic word + if ((address + length) > (fConfig.pageSize - dataOffset)) + { + return bsp::BSP_ERROR; + } + + uintptr_t srcAddr = activePageAddress() + dataOffset + address; + std::memcpy(buffer, reinterpret_cast(srcAddr), length); + return bsp::BSP_OK; +} + +bsp::BspReturnCode +FlashEepromDriver::write(uint32_t address, uint8_t const* buffer, uint32_t length) +{ + uint32_t dataOffset = 4U; + uint32_t dataSize = fConfig.pageSize - dataOffset; + + if ((address + length) > dataSize) + { + return bsp::BSP_ERROR; + } + + // Static buffer for page copy - avoids stack allocation in the safety task. + // Sized for G474RE (2KB pages). F413ZH uses 128KB sectors which are too + // large for this ping-pong scheme; use sector-level storage instead. + static uint8_t pageBuf[2048]; + if (dataSize > sizeof(pageBuf)) + { + return bsp::BSP_ERROR; + } + + std::memcpy(pageBuf, reinterpret_cast(activePageAddress() + dataOffset), dataSize); + std::memcpy(&pageBuf[address], buffer, length); + + uintptr_t inactiveAddr = inactivePageAddress(); + if (erasePage(inactiveAddr) != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + + if (unlockFlash() != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + + uint32_t const magic = MAGIC; + if (programPage(inactiveAddr, reinterpret_cast(&magic), 4U) != bsp::BSP_OK) + { + lockFlash(); + return bsp::BSP_ERROR; + } + + if (programPage(inactiveAddr + dataOffset, pageBuf, dataSize) != bsp::BSP_OK) + { + lockFlash(); + return bsp::BSP_ERROR; + } + + lockFlash(); + + uintptr_t oldActiveAddr = activePageAddress(); + (void)erasePage(oldActiveAddr); + + fActivePage = (fActivePage == 0U) ? 1U : 0U; + + return bsp::BSP_OK; +} + +bsp::BspReturnCode FlashEepromDriver::erase() +{ + if (erasePage(fConfig.page0Address) != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + if (erasePage(fConfig.page1Address) != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + return init(); +} + +uintptr_t FlashEepromDriver::activePageAddress() const +{ + return (fActivePage == 0U) ? fConfig.page0Address : fConfig.page1Address; +} + +uintptr_t FlashEepromDriver::inactivePageAddress() const +{ + return (fActivePage == 0U) ? fConfig.page1Address : fConfig.page0Address; +} + +bool FlashEepromDriver::isPageValid(uintptr_t pageAddr) const +{ + uint32_t const magic = *reinterpret_cast(pageAddr); + return magic == MAGIC; +} + +bsp::BspReturnCode FlashEepromDriver::unlockFlash() +{ + if ((FLASH->CR & FLASH_CR_LOCK) != 0U) + { + FLASH->KEYR = 0x45670123U; + FLASH->KEYR = 0xCDEF89ABU; + } + if ((FLASH->CR & FLASH_CR_LOCK) != 0U) + { + return bsp::BSP_ERROR; + } + return bsp::BSP_OK; +} + +void FlashEepromDriver::lockFlash() { FLASH->CR |= FLASH_CR_LOCK; } + +bsp::BspReturnCode FlashEepromDriver::waitForFlash() +{ + uint32_t timeout = 0xFFFFFFU; + while ((FLASH->SR & FLASH_SR_BSY) != 0U) + { + if (--timeout == 0U) + { + return bsp::BSP_ERROR; + } + } + return bsp::BSP_OK; +} + +bsp::BspReturnCode FlashEepromDriver::erasePage(uintptr_t pageAddr) +{ + if (unlockFlash() != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + + if (waitForFlash() != bsp::BSP_OK) + { + lockFlash(); + return bsp::BSP_ERROR; + } + +#if defined(STM32G474xx) + // STM32G4: page erase + uint32_t pageNum = (pageAddr - 0x08000000U) / fConfig.pageSize; + FLASH->CR = (FLASH->CR & ~(FLASH_CR_PNB_Msk)) | (pageNum << FLASH_CR_PNB_Pos) | FLASH_CR_PER; + FLASH->CR |= FLASH_CR_STRT; +#elif defined(STM32F413xx) + // STM32F4: sector erase - determine sector from address + // Simplified: assume pageAddr maps directly to a sector number + // For sectors 12-15 (dual bank), SNB field encodes sector + bank + uint32_t sector = 0U; + if (pageAddr >= 0x08100000U) + { + sector = ((pageAddr - 0x08100000U) / 0x4000U) + 16U; // Bank 2 + } + else + { + sector = (pageAddr - 0x08000000U) / 0x20000U; // 128KB sectors (simplified) + } + FLASH->CR = (FLASH->CR & ~(FLASH_CR_SNB_Msk)) | (sector << FLASH_CR_SNB_Pos) | FLASH_CR_SER; + FLASH->CR |= FLASH_CR_STRT; +#endif + + bsp::BspReturnCode result = waitForFlash(); + +#if defined(STM32G474xx) + FLASH->CR &= ~(FLASH_CR_PER | FLASH_CR_STRT); +#elif defined(STM32F413xx) + FLASH->CR &= ~(FLASH_CR_SER | FLASH_CR_STRT); +#endif + + lockFlash(); + return result; +} + +bsp::BspReturnCode +FlashEepromDriver::programPage(uintptr_t destAddr, uint8_t const* data, uint32_t length) +{ + if (waitForFlash() != bsp::BSP_OK) + { + return bsp::BSP_ERROR; + } + +#if defined(STM32G474xx) + // STM32G4 flash requires double-word (64-bit) programming + FLASH->CR |= FLASH_CR_PG; + + uint32_t i = 0U; + while (i < length) + { + uint32_t word0 = 0xFFFFFFFFU; + uint32_t word1 = 0xFFFFFFFFU; + + uint32_t remaining = length - i; + if (remaining > 0U) + { + std::memcpy(&word0, &data[i], (remaining >= 4U) ? 4U : remaining); + } + if (remaining > 4U) + { + uint32_t r2 = remaining - 4U; + std::memcpy(&word1, &data[i + 4U], (r2 >= 4U) ? 4U : r2); + } + + *reinterpret_cast(destAddr + i) = word0; + *reinterpret_cast(destAddr + i + 4U) = word1; + + if (waitForFlash() != bsp::BSP_OK) + { + FLASH->CR &= ~FLASH_CR_PG; + return bsp::BSP_ERROR; + } + + i += 8U; + } + + FLASH->CR &= ~FLASH_CR_PG; +#elif defined(STM32F413xx) + // STM32F4: word (32-bit) programming, PSIZE = x32 + FLASH->CR |= FLASH_CR_PG; + FLASH->CR = (FLASH->CR & ~FLASH_CR_PSIZE) | FLASH_CR_PSIZE_1; + + uint32_t i = 0U; + while (i < length) + { + uint32_t word = 0xFFFFFFFFU; + uint32_t remaining = length - i; + std::memcpy(&word, &data[i], (remaining >= 4U) ? 4U : remaining); + + *reinterpret_cast(destAddr + i) = word; + + if (waitForFlash() != bsp::BSP_OK) + { + FLASH->CR &= ~FLASH_CR_PG; + return bsp::BSP_ERROR; + } + + i += 4U; + } + + FLASH->CR &= ~FLASH_CR_PG; +#endif + + return bsp::BSP_OK; +} + +} // namespace eeprom diff --git a/platforms/stm32/bsp/bspInterruptsImpl/CMakeLists.txt b/platforms/stm32/bsp/bspInterruptsImpl/CMakeLists.txt new file mode 100644 index 00000000000..a2f0521d556 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/CMakeLists.txt @@ -0,0 +1,18 @@ +if (NOT BUILD_EXECUTABLE STREQUAL "unitTest") + set(bspInterruptsImplName "bspInterruptsImpl") +else () + set(bspInterruptsImplName "bspInterruptsImplUt") +endif () + +add_library(${bspInterruptsImplName} src/interrupt_manager.c) + +target_include_directories(${bspInterruptsImplName} PUBLIC include) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + target_include_directories(${bspInterruptsImplName} PUBLIC freertos/include) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + target_include_directories(${bspInterruptsImplName} PUBLIC threadx/include) + target_link_libraries(${bspInterruptsImplName} PUBLIC threadX) +endif () + +target_link_libraries(${bspInterruptsImplName} PRIVATE bspMcu platform) diff --git a/platforms/stm32/bsp/bspInterruptsImpl/doc/index.rst b/platforms/stm32/bsp/bspInterruptsImpl/doc/index.rst new file mode 100644 index 00000000000..023a6c52388 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/doc/index.rst @@ -0,0 +1,111 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspInterruptsImpl +================= + +Overview +-------- + +The ``bspInterruptsImpl`` module provides interrupt management for STM32 +Cortex-M4 targets. It implements global interrupt disable/enable primitives and +integrates with FreeRTOS for context-safe critical sections. + +Global Interrupt Disable / Enable +--------------------------------- + +The header ``interrupts/disableEnableAllInterrupts.h`` provides two inline +assembly functions: + +``disableAllInterrupts()`` + Executes ``CPSID I`` to set the ``PRIMASK`` register, masking all + interrupts except NMI and HardFault. Followed by ``ISB``, ``DSB``, and + ``DMB`` barrier instructions to ensure the mask takes effect before any + subsequent memory access or instruction fetch. + +``enableAllInterrupts()`` + Executes ``ISB``, ``DSB``, and ``DMB`` barriers first, then ``CPSIE I`` to + clear ``PRIMASK`` and re-enable interrupts. The barrier-before-enable + ordering ensures all pending memory operations complete before interrupts + can fire. + +Both functions are declared ``static inline`` with +``__attribute__((always_inline))`` to guarantee zero-overhead inlining at every +call site. + +PRIMASK vs BASEPRI +------------------ + +The Cortex-M4 offers two mechanisms for interrupt masking: + +**PRIMASK (bare-metal variant)** + Setting PRIMASK to 1 disables all configurable interrupts (priority levels + 0--15). Only NMI (priority -2) and HardFault (priority -1) remain active. + This is the approach used by ``disableAllInterrupts()`` / + ``enableAllInterrupts()`` and is suitable for bare-metal critical sections + where no RTOS is running. + +**BASEPRI (FreeRTOS variant)** + FreeRTOS uses ``BASEPRI`` instead of ``PRIMASK`` for its critical sections. + Setting ``BASEPRI`` to ``configMAX_SYSCALL_INTERRUPT_PRIORITY`` masks only + interrupts at or below that priority threshold, leaving higher-priority + (lower numeric value) interrupts unmasked. This allows time-critical ISRs + (e.g., motor control, safety watchdog) to preempt FreeRTOS critical + sections. + + FreeRTOS-aware critical section macros (``taskENTER_CRITICAL`` / + ``taskEXIT_CRITICAL``) use this BASEPRI approach internally. The + ``bspInterruptsImpl`` module provides the ``SuspendResumeAllInterruptsScopedLock`` + RAII wrapper for C++ code that needs interrupt-safe sections compatible with + both variants. + +NVIC Priority Configuration +--------------------------- + +The STM32 Cortex-M4 implements 4-bit priority grouping (``__NVIC_PRIO_BITS = 4`` +from the CMSIS device header), yielding 16 priority levels (0 = highest, +15 = lowest). + +Priority assignment guidelines: + +- Priorities 0--3: Reserved for time-critical hardware ISRs that must not be + blocked by FreeRTOS critical sections (above + ``configMAX_SYSCALL_INTERRUPT_PRIORITY``). +- Priorities 4--15: Available for ISRs that may call FreeRTOS API functions + (``xSemaphoreGiveFromISR``, ``xQueueSendFromISR``, etc.). + +The FreeRTOS Cortex-M4 port layer uses the CMSIS ``__enable_irq()`` / +``__disable_irq()`` intrinsics directly for global interrupt control. + +Scoped Lock +----------- + +The ``SuspendResumeAllInterruptsScopedLock`` class (from the ``interrupts`` +namespace) provides RAII-style interrupt locking: + +.. code-block:: cpp + + { + const ESR_UNUSED interrupts::SuspendResumeAllInterruptsScopedLock lock; + // Critical section -- interrupts disabled + } + // Interrupts restored to previous state + +This pattern is used throughout the BSP (e.g., ``bspTimer`` tick accumulation) +to ensure interrupt state is always restored, even if an early return or +exception occurs. + +Dependencies +------------ + +- ARM Cortex-M4 ``PRIMASK`` and ``BASEPRI`` special registers +- CMSIS ``__enable_irq()`` / ``__disable_irq()`` intrinsics +- FreeRTOS ``configMAX_SYSCALL_INTERRUPT_PRIORITY`` (when FreeRTOS is active) diff --git a/platforms/stm32/bsp/bspInterruptsImpl/freertos/include/interrupts/suspendResumeAllInterrupts.h b/platforms/stm32/bsp/bspInterruptsImpl/freertos/include/interrupts/suspendResumeAllInterrupts.h new file mode 100644 index 00000000000..f07607f40c3 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/freertos/include/interrupts/suspendResumeAllInterrupts.h @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "platform/estdint.h" + +typedef int32_t OldIntEnabledStatusValueType; +#define getOldIntEnabledStatusValueAndSuspendAllInterrupts \ + getMachineStateRegisterValueAndSuspendAllInterrupts + +// clang-format off +static inline __attribute__((always_inline)) +uint32_t getMachineStateRegisterValueAndSuspendAllInterrupts(void) +{ + uint32_t _PRIMASK; + __asm volatile (" mrs %0, PRIMASK\n" + " cpsid i\n" + : "=r" (_PRIMASK)); + return(_PRIMASK); +} +static inline __attribute__((always_inline)) +void resumeAllInterrupts(uint32_t oldMachineStateRegisterValue) +{ + __asm volatile (" msr PRIMASK,%[Input]\n" + ::[Input] "r" (oldMachineStateRegisterValue) + ); +} + +// clang-format on diff --git a/platforms/stm32/bsp/bspInterruptsImpl/include/interrupts/disableEnableAllInterrupts.h b/platforms/stm32/bsp/bspInterruptsImpl/include/interrupts/disableEnableAllInterrupts.h new file mode 100644 index 00000000000..3ebce505af9 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/include/interrupts/disableEnableAllInterrupts.h @@ -0,0 +1,35 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +// clang-format off +static inline __attribute__((always_inline)) +void disableAllInterrupts(void) +{ +asm( +"cpsid i;" +"ISB;" +"DSB;" +"DMB;" +); +} +static inline __attribute__((always_inline)) +void enableAllInterrupts(void) +{ +asm ( +"ISB;" +"DSB;" +"DMB;" +"cpsie i;" +); +} + +// clang-format on diff --git a/platforms/stm32/bsp/bspInterruptsImpl/module.spec b/platforms/stm32/bsp/bspInterruptsImpl/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspInterruptsImpl/src/interrupt_manager.c b/platforms/stm32/bsp/bspInterruptsImpl/src/interrupt_manager.c new file mode 100644 index 00000000000..279d556a09d --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/src/interrupt_manager.c @@ -0,0 +1,14 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +// Placeholder - interrupt manager for STM32. +// NVIC priorities are configured by the application during board bring-up. diff --git a/platforms/stm32/bsp/bspInterruptsImpl/threadx/include/interrupts/suspendResumeAllInterrupts.h b/platforms/stm32/bsp/bspInterruptsImpl/threadx/include/interrupts/suspendResumeAllInterrupts.h new file mode 100644 index 00000000000..435f9d524c9 --- /dev/null +++ b/platforms/stm32/bsp/bspInterruptsImpl/threadx/include/interrupts/suspendResumeAllInterrupts.h @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +#include + +static inline __attribute__((always_inline)) void setThreadXInitialized(){}; + +typedef uint32_t OldIntEnabledStatusValueType; + +#define getMachineStateRegisterValueAndSuspendAllInterrupts \ + getOldIntEnabledStatusValueAndSuspendAllInterrupts + +static inline __attribute__((always_inline)) OldIntEnabledStatusValueType +getOldIntEnabledStatusValueAndSuspendAllInterrupts(void) +{ + return tx_interrupt_control(TX_INT_DISABLE); +} + +static inline __attribute__((always_inline)) void +resumeAllInterrupts(OldIntEnabledStatusValueType const oldIntEnabledStatusValue) +{ + tx_interrupt_control(oldIntEnabledStatusValue); +} diff --git a/platforms/stm32/bsp/bspIo/CMakeLists.txt b/platforms/stm32/bsp/bspIo/CMakeLists.txt new file mode 100644 index 00000000000..882c15e6af6 --- /dev/null +++ b/platforms/stm32/bsp/bspIo/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(bspIo src/io/Gpio.cpp) +target_include_directories(bspIo PUBLIC include) +target_link_libraries( + bspIo + PUBLIC bspMcu + PRIVATE platform) diff --git a/platforms/stm32/bsp/bspIo/include/io/Gpio.h b/platforms/stm32/bsp/bspIo/include/io/Gpio.h new file mode 100644 index 00000000000..245a2870500 --- /dev/null +++ b/platforms/stm32/bsp/bspIo/include/io/Gpio.h @@ -0,0 +1,80 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include + +namespace bios +{ + +// MODER register values +enum class GpioMode : uint8_t +{ + INPUT = 0U, + OUTPUT = 1U, + ALTERNATE = 2U, + ANALOG = 3U +}; + +// OTYPER register values +enum class GpioOutputType : uint8_t +{ + PUSH_PULL = 0U, + OPEN_DRAIN = 1U +}; + +// OSPEEDR register values +enum class GpioSpeed : uint8_t +{ + LOW = 0U, + MEDIUM = 1U, + HIGH = 2U, + VERY_HIGH = 3U +}; + +// PUPDR register values +enum class GpioPull : uint8_t +{ + NONE = 0U, + UP = 1U, + DOWN = 2U +}; + +struct GpioConfig +{ + GPIO_TypeDef* port; + uint8_t pin; // 0-15 + GpioMode mode; + GpioOutputType otype; + GpioSpeed speed; + GpioPull pull; + uint8_t af; // Alternate function number (0-15) +}; + +class Gpio +{ +public: + Gpio() = delete; + + static void enablePortClock(GPIO_TypeDef* port); + static void configure(GpioConfig const& cfg); + static void setMode(GPIO_TypeDef* port, uint8_t pin, GpioMode mode); + static void setOutputType(GPIO_TypeDef* port, uint8_t pin, GpioOutputType otype); + static void setSpeed(GPIO_TypeDef* port, uint8_t pin, GpioSpeed speed); + static void setPull(GPIO_TypeDef* port, uint8_t pin, GpioPull pull); + static void setAlternateFunction(GPIO_TypeDef* port, uint8_t pin, uint8_t af); + static bool readPin(GPIO_TypeDef* port, uint8_t pin); + static void writePin(GPIO_TypeDef* port, uint8_t pin, bool high); + static void togglePin(GPIO_TypeDef* port, uint8_t pin); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bspIo/include/io/GpioPinConfig.h b/platforms/stm32/bsp/bspIo/include/io/GpioPinConfig.h new file mode 100644 index 00000000000..c540ba7e2e2 --- /dev/null +++ b/platforms/stm32/bsp/bspIo/include/io/GpioPinConfig.h @@ -0,0 +1,77 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace bios +{ +namespace pins +{ + +#if defined(STM32F413xx) +// NUCLEO-F413ZH board pins + +// LD1 (green LED) +static constexpr GpioConfig LED1 + = {GPIOB, 0U, GpioMode::OUTPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +// LD2 (blue LED) +static constexpr GpioConfig LED2 + = {GPIOB, 7U, GpioMode::OUTPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +// LD3 (red LED) +static constexpr GpioConfig LED3 + = {GPIOB, 14U, GpioMode::OUTPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +// User button B1 (external pull-down on board) +static constexpr GpioConfig USER_BUTTON + = {GPIOC, 13U, GpioMode::INPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +static constexpr GpioConfig CAN1_RX = { + GPIOD, 0U, GpioMode::ALTERNATE, GpioOutputType::PUSH_PULL, GpioSpeed::HIGH, GpioPull::UP, 9U}; + +static constexpr GpioConfig CAN1_TX + = {GPIOD, + 1U, + GpioMode::ALTERNATE, + GpioOutputType::PUSH_PULL, + GpioSpeed::VERY_HIGH, + GpioPull::NONE, + 9U}; + +#elif defined(STM32G474xx) +// NUCLEO-G474RE board pins + +// LD2 (green LED) +static constexpr GpioConfig LED2 + = {GPIOA, 5U, GpioMode::OUTPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +// User button B1 (external pull-up on board) +static constexpr GpioConfig USER_BUTTON + = {GPIOC, 13U, GpioMode::INPUT, GpioOutputType::PUSH_PULL, GpioSpeed::LOW, GpioPull::NONE, 0U}; + +static constexpr GpioConfig FDCAN1_RX = { + GPIOA, 11U, GpioMode::ALTERNATE, GpioOutputType::PUSH_PULL, GpioSpeed::HIGH, GpioPull::UP, 9U}; + +static constexpr GpioConfig FDCAN1_TX + = {GPIOA, + 12U, + GpioMode::ALTERNATE, + GpioOutputType::PUSH_PULL, + GpioSpeed::VERY_HIGH, + GpioPull::NONE, + 9U}; + +#endif + +} // namespace pins +} // namespace bios diff --git a/platforms/stm32/bsp/bspIo/module.spec b/platforms/stm32/bsp/bspIo/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspIo/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspIo/src/io/Gpio.cpp b/platforms/stm32/bsp/bspIo/src/io/Gpio.cpp new file mode 100644 index 00000000000..0c05aef10f2 --- /dev/null +++ b/platforms/stm32/bsp/bspIo/src/io/Gpio.cpp @@ -0,0 +1,175 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +namespace bios +{ + +void Gpio::enablePortClock(GPIO_TypeDef* port) +{ +#if defined(STM32G474xx) + if (port == GPIOA) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; + } + else if (port == GPIOB) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOBEN; + } + else if (port == GPIOC) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN; + } +#if defined(GPIOD) + else if (port == GPIOD) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIODEN; + } +#endif +#if defined(GPIOE) + else if (port == GPIOE) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOEEN; + } +#endif +#if defined(GPIOF) + else if (port == GPIOF) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOFEN; + } +#endif +#if defined(GPIOG) + else if (port == GPIOG) + { + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOGEN; + } +#endif +#elif defined(STM32F413xx) + if (port == GPIOA) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; + } + else if (port == GPIOB) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; + } + else if (port == GPIOC) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; + } + else if (port == GPIOD) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; + } +#if defined(GPIOE) + else if (port == GPIOE) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; + } +#endif +#if defined(GPIOF) + else if (port == GPIOF) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; + } +#endif +#if defined(GPIOG) + else if (port == GPIOG) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN; + } +#endif +#if defined(GPIOH) + else if (port == GPIOH) + { + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOHEN; + } +#endif +#endif + // Read-back for clock stabilization + uint32_t volatile dummy; + (void)dummy; +#if defined(STM32G474xx) + dummy = RCC->AHB2ENR; +#elif defined(STM32F413xx) + dummy = RCC->AHB1ENR; +#endif + (void)dummy; +} + +void Gpio::configure(GpioConfig const& cfg) +{ + enablePortClock(cfg.port); + setMode(cfg.port, cfg.pin, cfg.mode); + setOutputType(cfg.port, cfg.pin, cfg.otype); + setSpeed(cfg.port, cfg.pin, cfg.speed); + setPull(cfg.port, cfg.pin, cfg.pull); + if ((cfg.mode == GpioMode::ALTERNATE) && (cfg.af <= 15U)) + { + setAlternateFunction(cfg.port, cfg.pin, cfg.af); + } +} + +void Gpio::setMode(GPIO_TypeDef* port, uint8_t pin, GpioMode mode) +{ + uint32_t pos = pin * 2U; + port->MODER = (port->MODER & ~(3U << pos)) | (static_cast(mode) << pos); +} + +void Gpio::setOutputType(GPIO_TypeDef* port, uint8_t pin, GpioOutputType otype) +{ + if (otype == GpioOutputType::OPEN_DRAIN) + { + port->OTYPER |= (1U << pin); + } + else + { + port->OTYPER &= ~(1U << pin); + } +} + +void Gpio::setSpeed(GPIO_TypeDef* port, uint8_t pin, GpioSpeed speed) +{ + uint32_t pos = pin * 2U; + port->OSPEEDR = (port->OSPEEDR & ~(3U << pos)) | (static_cast(speed) << pos); +} + +void Gpio::setPull(GPIO_TypeDef* port, uint8_t pin, GpioPull pull) +{ + uint32_t pos = pin * 2U; + port->PUPDR = (port->PUPDR & ~(3U << pos)) | (static_cast(pull) << pos); +} + +void Gpio::setAlternateFunction(GPIO_TypeDef* port, uint8_t pin, uint8_t af) +{ + uint8_t afrIdx = (pin < 8U) ? 0U : 1U; + uint8_t afrPin = (pin < 8U) ? pin : (pin - 8U); + uint32_t pos = afrPin * 4U; + port->AFR[afrIdx] = (port->AFR[afrIdx] & ~(0xFU << pos)) | (static_cast(af) << pos); +} + +bool Gpio::readPin(GPIO_TypeDef* port, uint8_t pin) { return (port->IDR & (1U << pin)) != 0U; } + +void Gpio::writePin(GPIO_TypeDef* port, uint8_t pin, bool high) +{ + if (high) + { + port->BSRR = (1U << pin); + } + else + { + port->BSRR = (1U << (pin + 16U)); + } +} + +void Gpio::togglePin(GPIO_TypeDef* port, uint8_t pin) { port->ODR ^= (1U << pin); } + +} // namespace bios diff --git a/platforms/stm32/bsp/bspTimer/CMakeLists.txt b/platforms/stm32/bsp/bspTimer/CMakeLists.txt new file mode 100644 index 00000000000..26b7596d8f5 --- /dev/null +++ b/platforms/stm32/bsp/bspTimer/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(bspTimer src/bsp/SystemTimer/SystemTimer.cpp) + +target_link_libraries(bspTimer PUBLIC bsp bspMcu bspInterrupts) diff --git a/platforms/stm32/bsp/bspTimer/doc/index.rst b/platforms/stm32/bsp/bspTimer/doc/index.rst new file mode 100644 index 00000000000..8e87df71638 --- /dev/null +++ b/platforms/stm32/bsp/bspTimer/doc/index.rst @@ -0,0 +1,35 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspTimer +======== + +Overview +-------- + +The ``bspTimer`` module implements the system timer for STM32 Cortex-M4 +targets using the ARM Data Watchpoint and Trace (DWT) cycle counter +(``CYCCNT``) -- a core debug unit register available on all Cortex-M4 devices +-- so no chip-specific peripheral timer is required. + +The 32-bit ``CYCCNT`` is extended to a 64-bit monotonic microsecond counter; +updates run under ``SuspendResumeAllInterruptsScopedLock``. The core clock +frequency used for the cycles-to-microseconds conversion is fixed at compile +time: + +- 96 MHz for STM32F4 (STM32F413ZH) +- 170 MHz for STM32G4 (STM32G474RE) + +The family is selected via the ``STM32_FAMILY_F4`` / ``STM32_FAMILY_G4`` +compile definitions from the bspMcu CMake configuration. + +The DWT timer is independent of SysTick: FreeRTOS uses SysTick for its tick +interrupt while the BSP system timer uses DWT for high-resolution timestamps. diff --git a/platforms/stm32/bsp/bspTimer/module.spec b/platforms/stm32/bsp/bspTimer/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspTimer/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspTimer/src/bsp/SystemTimer/SystemTimer.cpp b/platforms/stm32/bsp/bspTimer/src/bsp/SystemTimer/SystemTimer.cpp new file mode 100644 index 00000000000..e46be2a0123 --- /dev/null +++ b/platforms/stm32/bsp/bspTimer/src/bsp/SystemTimer/SystemTimer.cpp @@ -0,0 +1,95 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "bsp/timer/SystemTimer.h" + +#include "interrupts/SuspendResumeAllInterruptsScopedLock.h" +#include "mcu/mcu.h" + +namespace +{ +#if defined(STM32_FAMILY_F4) +uint32_t const DWT_FREQ_MHZ = 96U; // F413ZH: 96 MHz HCLK +#elif defined(STM32_FAMILY_G4) +uint32_t const DWT_FREQ_MHZ = 170U; // G474RE: 170 MHz HCLK +#else +#error "Define STM32_FAMILY_F4 or STM32_FAMILY_G4" +#endif + +uint32_t const TICK_FREQ_MHZ = 1U; // 1 tick = 1 us + +struct +{ + uint64_t ticks; + uint32_t lastDwt; +} state = {0, 0}; + +// DWT registers (ARMv7-M architecture reference) +uint32_t volatile& DWT_CTRL = *reinterpret_cast(0xE0001000U); +uint32_t volatile& DWT_CYCCNT = *reinterpret_cast(0xE0001004U); +uint32_t volatile& DEMCR = *reinterpret_cast(0xE000EDFCU); + +uint64_t updateTicks() +{ + const ESR_UNUSED interrupts::SuspendResumeAllInterruptsScopedLock lock; + uint32_t const curDwt = DWT_CYCCNT; + state.ticks += static_cast((curDwt - state.lastDwt) / DWT_FREQ_MHZ); + state.lastDwt = curDwt; + return state.ticks; +} + +} // namespace + +extern "C" +{ +void initSystemTimer() +{ + const ESR_UNUSED interrupts::SuspendResumeAllInterruptsScopedLock lock; + + DEMCR = DEMCR | 0x01000000U; // TRCENA + DWT_CYCCNT = 0; + DWT_CTRL = DWT_CTRL | 0x00000001U; // CYCCNTENA + + state.ticks = 0; + state.lastDwt = 0; +} + +uint64_t getSystemTicks(void) { return updateTicks(); } + +uint32_t getSystemTicks32Bit(void) { return static_cast(updateTicks()); } + +uint64_t getSystemTimeNs(void) { return updateTicks() * 1000U / TICK_FREQ_MHZ; } + +uint64_t getSystemTimeUs(void) { return updateTicks() / TICK_FREQ_MHZ; } + +uint32_t getSystemTimeUs32Bit(void) { return static_cast(updateTicks() / TICK_FREQ_MHZ); } + +uint64_t getSystemTimeMs(void) { return updateTicks() / TICK_FREQ_MHZ / 1000U; } + +uint32_t getSystemTimeMs32Bit(void) +{ + return static_cast(updateTicks() / TICK_FREQ_MHZ / 1000U); +} + +uint64_t systemTicksToTimeUs(uint64_t const ticks) { return ticks / TICK_FREQ_MHZ; } + +uint64_t systemTicksToTimeNs(uint64_t const ticks) { return ticks * 1000U / TICK_FREQ_MHZ; } + +uint32_t getFastTicks(void) { return DWT_CYCCNT; } + +uint32_t getFastTicksPerSecond(void) { return DWT_FREQ_MHZ * 1000000U; } + +void sysDelayUs(uint32_t const delay) +{ + uint64_t const start = getSystemTimeUs(); + while (getSystemTimeUs() < start + delay) {} +} + +} // extern "C" diff --git a/platforms/stm32/bsp/bspUart/CMakeLists.txt b/platforms/stm32/bsp/bspUart/CMakeLists.txt new file mode 100644 index 00000000000..2498a80301b --- /dev/null +++ b/platforms/stm32/bsp/bspUart/CMakeLists.txt @@ -0,0 +1,13 @@ +if (BUILD_EXECUTABLE STREQUAL "unitTest") + # Header-only for unit tests (no hardware-dependent Uart.cpp) + add_library(bspUart INTERFACE) + target_include_directories(bspUart INTERFACE include) + target_link_libraries(bspUart INTERFACE bsp) +else () + add_library(bspUart src/Uart.cpp) + target_include_directories(bspUart PUBLIC include) + target_link_libraries( + bspUart + PUBLIC bsp + PRIVATE bspMcu) +endif () diff --git a/platforms/stm32/bsp/bspUart/doc/index.rst b/platforms/stm32/bsp/bspUart/doc/index.rst new file mode 100644 index 00000000000..fdaf963be51 --- /dev/null +++ b/platforms/stm32/bsp/bspUart/doc/index.rst @@ -0,0 +1,41 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspUart Driver +============== + +Overview +-------- + +The ``bspUart`` module provides a polling-mode UART driver for debug console +output on STM32 targets. It is used by the logger subsystem and the command +shell for human-readable output over the ST-LINK Virtual COM Port (VCP). The +driver is the ``bsp::Uart`` class and satisfies the ``UartConcept`` +compile-time interface check (``BSP_UART_CONCEPT_CHECKER``). + +All I/O is synchronous polling; no DMA or interrupts are used, which avoids +NVIC configuration dependencies at the cost of blocking the caller during +transmission. + +Configuration +------------- + +Each UART instance is described by a ``UartConfig`` struct in a compile-time +table (``_uartConfigs[]``): USART peripheral, GPIO port and TX/RX pins, +alternate function number, and baud rate register value (typically 115200 +baud for the debug console). + +Board configuration: + +- **NUCLEO-F413ZH** -- USART3 on PD8 (TX) / PD9 (RX), AF7. VCP via ST-LINK + V2-1. +- **NUCLEO-G474RE** -- USART2 on PA2 (TX) / PA3 (RX), AF7. VCP via ST-LINK + V3. diff --git a/platforms/stm32/bsp/bspUart/include/bsp/Uart.h b/platforms/stm32/bsp/bspUart/include/bsp/Uart.h new file mode 100644 index 00000000000..6923323216a --- /dev/null +++ b/platforms/stm32/bsp/bspUart/include/bsp/Uart.h @@ -0,0 +1,43 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace bsp +{ + +class Uart +{ +public: + enum class Id : size_t; + + size_t write(::etl::span const data); + size_t read(::etl::span data); + void init(); + bool isInitialized() const; + bool waitForTxReady(); + + static Uart& getInstance(Id id); + + struct UartConfig; + Uart(Uart::Id id); + +private: + bool writeByte(uint8_t data); + + UartConfig const& _uartConfig; + static UartConfig const _uartConfigs[]; +}; + +BSP_UART_CONCEPT_CHECKER(Uart) + +} // namespace bsp diff --git a/platforms/stm32/bsp/bspUart/include/bsp/UartParams.h b/platforms/stm32/bsp/bspUart/include/bsp/UartParams.h new file mode 100644 index 00000000000..a21f14706ce --- /dev/null +++ b/platforms/stm32/bsp/bspUart/include/bsp/UartParams.h @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "bsp/Uart.h" + +#include + +namespace bsp +{ + +struct Uart::UartConfig +{ + USART_TypeDef* usart; + GPIO_TypeDef* gpioPort; + uint8_t txPin; + uint8_t rxPin; + uint8_t af; + uint32_t brr; + uint32_t rccGpioEnBit; + uint32_t rccUsartEnBit; + uint32_t volatile* rccGpioEnReg; + uint32_t volatile* rccUsartEnReg; +}; + +} // namespace bsp diff --git a/platforms/stm32/bsp/bspUart/module.spec b/platforms/stm32/bsp/bspUart/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspUart/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspUart/src/Uart.cpp b/platforms/stm32/bsp/bspUart/src/Uart.cpp new file mode 100644 index 00000000000..e10084bc992 --- /dev/null +++ b/platforms/stm32/bsp/bspUart/src/Uart.cpp @@ -0,0 +1,127 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "bsp/UartParams.h" + +using bsp::Uart; + +static uint32_t const WRITE_TIMEOUT = 100000U; + +// STM32F4 uses SR/DR registers, STM32G4 uses ISR/TDR/RDR registers +#if defined(STM32F413xx) +#define UART_TX_EMPTY_FLAG USART_SR_TXE +#define UART_RX_READY_FLAG USART_SR_RXNE +#define UART_GET_STATUS(u) ((u)->SR) +#define UART_WRITE_DATA(u, d) ((u)->DR = (d)) +#define UART_READ_DATA(u) ((u)->DR & 0xFFU) +#elif defined(STM32G474xx) +#define UART_TX_EMPTY_FLAG USART_ISR_TXE +#define UART_RX_READY_FLAG USART_ISR_RXNE_RXFNE +#define UART_GET_STATUS(u) ((u)->ISR) +#define UART_WRITE_DATA(u, d) ((u)->TDR = (d)) +#define UART_READ_DATA(u) ((u)->RDR & 0xFFU) +#endif + +static void configureGpio(Uart::UartConfig const& cfg) +{ + *cfg.rccGpioEnReg |= cfg.rccGpioEnBit; + + cfg.gpioPort->MODER &= ~(3U << (cfg.txPin * 2U)); + cfg.gpioPort->MODER |= (2U << (cfg.txPin * 2U)); + uint32_t const txAfrIdx = cfg.txPin / 8U; + uint32_t const txAfrPos = (cfg.txPin % 8U) * 4U; + cfg.gpioPort->AFR[txAfrIdx] &= ~(0xFU << txAfrPos); + cfg.gpioPort->AFR[txAfrIdx] |= (static_cast(cfg.af) << txAfrPos); + + cfg.gpioPort->MODER &= ~(3U << (cfg.rxPin * 2U)); + cfg.gpioPort->MODER |= (2U << (cfg.rxPin * 2U)); + uint32_t const rxAfrIdx = cfg.rxPin / 8U; + uint32_t const rxAfrPos = (cfg.rxPin % 8U) * 4U; + cfg.gpioPort->AFR[rxAfrIdx] &= ~(0xFU << rxAfrPos); + cfg.gpioPort->AFR[rxAfrIdx] |= (static_cast(cfg.af) << rxAfrPos); +} + +Uart::Uart(Uart::Id id) : _uartConfig(_uartConfigs[static_cast(id)]) {} + +void Uart::init() +{ + configureGpio(_uartConfig); + + *_uartConfig.rccUsartEnReg |= _uartConfig.rccUsartEnBit; + + // BRR must be written while UE is cleared + _uartConfig.usart->CR1 = 0; + _uartConfig.usart->BRR = _uartConfig.brr; + _uartConfig.usart->CR1 = USART_CR1_TE | USART_CR1_RE | USART_CR1_UE; +} + +bool Uart::isInitialized() const +{ + return (_uartConfig.usart->CR1 & (USART_CR1_TE | USART_CR1_RE)) != 0; +} + +bool Uart::waitForTxReady() +{ + uint32_t count = 0U; + + if (!isInitialized()) + { + init(); + } + + while ((UART_GET_STATUS(_uartConfig.usart) & UART_TX_EMPTY_FLAG) == 0) + { + if (++count > WRITE_TIMEOUT) + { + return false; + } + } + return true; +} + +bool Uart::writeByte(uint8_t data) +{ + if ((UART_GET_STATUS(_uartConfig.usart) & UART_TX_EMPTY_FLAG) != 0) + { + UART_WRITE_DATA(_uartConfig.usart, static_cast(data) & 0xFFU); + return waitForTxReady(); + } + return false; +} + +size_t Uart::write(::etl::span const data) +{ + size_t counter = 0; + + while (counter < data.size()) + { + if (!writeByte(data[counter])) + { + break; + } + counter++; + } + + return counter; +} + +size_t Uart::read(::etl::span data) +{ + size_t bytesRead = 0; + + while ((UART_GET_STATUS(_uartConfig.usart) & UART_RX_READY_FLAG) != 0 + && bytesRead < data.size()) + { + data[bytesRead] = static_cast(UART_READ_DATA(_uartConfig.usart)); + bytesRead++; + } + + return bytesRead; +} diff --git a/platforms/stm32/etlImpl/CMakeLists.txt b/platforms/stm32/etlImpl/CMakeLists.txt new file mode 100644 index 00000000000..c3f4d59b292 --- /dev/null +++ b/platforms/stm32/etlImpl/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(etlImpl src/print.cpp src/clocks.cpp) + +target_link_libraries(etlImpl PRIVATE etl bsp util) diff --git a/platforms/stm32/etlImpl/src/clocks.cpp b/platforms/stm32/etlImpl/src/clocks.cpp new file mode 100644 index 00000000000..fa1ab9ae743 --- /dev/null +++ b/platforms/stm32/etlImpl/src/clocks.cpp @@ -0,0 +1,30 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include +#include + +extern "C" +{ +etl::chrono::high_resolution_clock::rep etl_get_high_resolution_clock() +{ + return etl::chrono::high_resolution_clock::rep{static_cast(getSystemTimeNs())}; +} + +etl::chrono::system_clock::rep etl_get_system_clock() +{ + return etl::chrono::system_clock::rep(static_cast(getSystemTimeUs())); +} + +etl::chrono::steady_clock::rep etl_get_steady_clock() +{ + return etl::chrono::steady_clock::rep(static_cast(getSystemTimeMs() / 1000)); +} +} diff --git a/platforms/stm32/etlImpl/src/print.cpp b/platforms/stm32/etlImpl/src/print.cpp new file mode 100644 index 00000000000..9d14f512b7b --- /dev/null +++ b/platforms/stm32/etlImpl/src/print.cpp @@ -0,0 +1,14 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include +#include + +extern "C" void etl_putchar(int c) { putByteToStdout(static_cast(c)); } From 494ae7841d7434ed97d5da1f498e59e9bc472d79 Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Tue, 2 Jun 2026 08:16:56 +0200 Subject: [PATCH 04/11] Add STM32 CAN drivers and bxCAN transceiver Add bxCAN and FDCAN device drivers with STM32 CAN tests. Add the bxCAN transceiver adapter and unit-test registration. --- CMakeLists.txt | 11 +- platforms/stm32/CMakeLists.txt | 3 + platforms/stm32/bsp/CMakeLists.txt | 49 +- platforms/stm32/bsp/bspCan/CMakeLists.txt | 16 + platforms/stm32/bsp/bspCan/doc/index.rst | 70 + .../bsp/bspCan/include/can/BxCanDevice.h | 215 + .../bsp/bspCan/include/can/FdCanDevice.h | 215 + platforms/stm32/bsp/bspCan/module.spec | 2 + .../stm32/bsp/bspCan/src/can/BxCanDevice.cpp | 382 ++ .../stm32/bsp/bspCan/src/can/FdCanDevice.cpp | 485 ++ .../stm32/bsp/bspCan/test/CMakeLists.txt | 15 + .../stm32/bsp/bspCan/test/include/mcu/mcu.h | 12 + .../bspCan/test/src/can/BxCanDeviceTest.cpp | 4110 +++++++++++++++++ .../bsp/bspCan/test/src/can/FakeHwHelpers.h | 170 + .../bspCan/test/src/can/FdCanDeviceTest.cpp | 3657 +++++++++++++++ .../stm32/bsp/bxCanTransceiver/CMakeLists.txt | 21 + .../stm32/bsp/bxCanTransceiver/doc/index.rst | 45 + .../can/transceiver/bxcan/BxCanTransceiver.h | 105 + .../stm32/bsp/bxCanTransceiver/module.spec | 2 + .../transceiver/bxcan/BxCanTransceiver.cpp | 310 ++ .../bsp/bxCanTransceiver/test/CMakeLists.txt | 15 + .../test/mock/include/can/BxCanDevice.h | 66 + .../test/src/can/BxCanTransceiverTest.cpp | 943 ++++ 23 files changed, 10897 insertions(+), 22 deletions(-) create mode 100644 platforms/stm32/bsp/bspCan/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspCan/doc/index.rst create mode 100644 platforms/stm32/bsp/bspCan/include/can/BxCanDevice.h create mode 100644 platforms/stm32/bsp/bspCan/include/can/FdCanDevice.h create mode 100644 platforms/stm32/bsp/bspCan/module.spec create mode 100644 platforms/stm32/bsp/bspCan/src/can/BxCanDevice.cpp create mode 100644 platforms/stm32/bsp/bspCan/src/can/FdCanDevice.cpp create mode 100644 platforms/stm32/bsp/bspCan/test/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bspCan/test/include/mcu/mcu.h create mode 100644 platforms/stm32/bsp/bspCan/test/src/can/BxCanDeviceTest.cpp create mode 100644 platforms/stm32/bsp/bspCan/test/src/can/FakeHwHelpers.h create mode 100644 platforms/stm32/bsp/bspCan/test/src/can/FdCanDeviceTest.cpp create mode 100644 platforms/stm32/bsp/bxCanTransceiver/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bxCanTransceiver/doc/index.rst create mode 100644 platforms/stm32/bsp/bxCanTransceiver/include/can/transceiver/bxcan/BxCanTransceiver.h create mode 100644 platforms/stm32/bsp/bxCanTransceiver/module.spec create mode 100644 platforms/stm32/bsp/bxCanTransceiver/src/can/transceiver/bxcan/BxCanTransceiver.cpp create mode 100644 platforms/stm32/bsp/bxCanTransceiver/test/CMakeLists.txt create mode 100644 platforms/stm32/bsp/bxCanTransceiver/test/mock/include/can/BxCanDevice.h create mode 100644 platforms/stm32/bsp/bxCanTransceiver/test/src/can/BxCanTransceiverTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 840515f077c..2e3aceca614 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,6 +187,13 @@ if (BUILD_EXECUTABLE STREQUAL "unitTest") add_subdirectory(platforms/posix/bsp/bspEepromDriver/test) add_subdirectory(platforms/posix/bsp/socketCanTransceiver/test) + elseif (OPENBSW_PLATFORM STREQUAL "stm32") + + add_subdirectory(platforms/stm32/unitTest EXCLUDE_FROM_ALL) + + add_subdirectory(platforms/stm32/bsp/bspCan/test) + add_subdirectory(platforms/stm32/bsp/bxCanTransceiver/test) + elseif (OPENBSW_PLATFORM STREQUAL "s32k1xx") add_subdirectory(platforms/s32k1xx/unitTest EXCLUDE_FROM_ALL) @@ -197,10 +204,6 @@ if (BUILD_EXECUTABLE STREQUAL "unitTest") add_subdirectory(platforms/s32k1xx/bsp/canflex2Transceiver/test) add_subdirectory(platforms/s32k1xx/bsp/bspUart/test) - elseif (OPENBSW_PLATFORM STREQUAL "stm32") - - add_subdirectory(platforms/stm32/unitTest EXCLUDE_FROM_ALL) - else () message( FATAL_ERROR diff --git a/platforms/stm32/CMakeLists.txt b/platforms/stm32/CMakeLists.txt index d49dd6f1f71..7315f7f7653 100644 --- a/platforms/stm32/CMakeLists.txt +++ b/platforms/stm32/CMakeLists.txt @@ -20,4 +20,7 @@ if (NOT BUILD_EXECUTABLE STREQUAL "unitTest") # ETL implementation add_subdirectory(etlImpl) +else () + # Unit-test builds compile the transceivers against mock devices. + add_subdirectory(bsp) endif () diff --git a/platforms/stm32/bsp/CMakeLists.txt b/platforms/stm32/bsp/CMakeLists.txt index 41c252d17db..0fdb650d9bd 100644 --- a/platforms/stm32/bsp/CMakeLists.txt +++ b/platforms/stm32/bsp/CMakeLists.txt @@ -1,19 +1,32 @@ -add_subdirectory(bspMcu) -add_subdirectory(bspClock) -add_subdirectory(bspInterruptsImpl) -add_subdirectory(bspUart) -add_subdirectory(bspTimer) -add_subdirectory(bspIo) -add_subdirectory(bspAdc) -add_subdirectory(bspEepromDriver) +if (BUILD_EXECUTABLE STREQUAL "unitTest") + # Unit test builds: only compile transceivers (with mock devices). Skip + # hardware-dependent modules (bspMcu, bspClock, bspCan, etc.). + add_subdirectory(bxCanTransceiver) + add_subdirectory(bspUart) +else () + add_subdirectory(bspMcu) + add_subdirectory(bspClock) + add_subdirectory(bspInterruptsImpl) + add_subdirectory(bspUart) + add_subdirectory(bspTimer) + add_subdirectory(bspIo) + add_subdirectory(bspAdc) + add_subdirectory(bspEepromDriver) + add_subdirectory(bspCan) -# Aggregated BSP target -add_library(socBsp INTERFACE) -target_link_libraries( - socBsp - INTERFACE bspClock - bspInterruptsImpl - bspIo - bspMcu - bspTimer - bspUart) + # CAN transceiver - selected by chip family + if (CAN_TYPE STREQUAL "BXCAN") + add_subdirectory(bxCanTransceiver) + endif () + + # Aggregated BSP target + add_library(socBsp INTERFACE) + target_link_libraries( + socBsp + INTERFACE bspClock + bspInterruptsImpl + bspIo + bspMcu + bspTimer + bspUart) +endif () diff --git a/platforms/stm32/bsp/bspCan/CMakeLists.txt b/platforms/stm32/bsp/bspCan/CMakeLists.txt new file mode 100644 index 00000000000..17b17e1549b --- /dev/null +++ b/platforms/stm32/bsp/bspCan/CMakeLists.txt @@ -0,0 +1,16 @@ +# Low-level CAN device drivers - chip-specific source selected by CAN_TYPE +if (CAN_TYPE STREQUAL "BXCAN") + add_library(bspCan src/can/BxCanDevice.cpp) + target_include_directories(bspCan PUBLIC include) + target_link_libraries( + bspCan + PUBLIC bspMcu cpp2can etl + PRIVATE platform) +elseif (CAN_TYPE STREQUAL "FDCAN") + add_library(bspCan src/can/FdCanDevice.cpp) + target_include_directories(bspCan PUBLIC include) + target_link_libraries( + bspCan + PUBLIC bspMcu cpp2can etl + PRIVATE platform) +endif () diff --git a/platforms/stm32/bsp/bspCan/doc/index.rst b/platforms/stm32/bsp/bspCan/doc/index.rst new file mode 100644 index 00000000000..8a03bcac5cb --- /dev/null +++ b/platforms/stm32/bsp/bspCan/doc/index.rst @@ -0,0 +1,70 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bspCan +====== + +Overview +-------- + +The ``bspCan`` module provides low-level register drivers for the two CAN +peripherals found across the STM32 family: + +- ``BxCanDevice`` -- bare-metal driver for the **bxCAN** controller on STM32F4 + (CAN1/CAN2/CAN3). +- ``FdCanDevice`` -- bare-metal driver for the **FDCAN** controller on STM32G4 + (FDCAN1/FDCAN2/FDCAN3). + +Both classes expose the same logical interface -- ``init()``, ``start()``, +``stop()``, ``transmit()``, ``receiveISR()`` -- so the transceiver layer can +wrap either one behind the OpenBSW ``AbstractCANTransceiver`` API. All CAN +communication is classic CAN at 500 kbps; the FDCAN peripheral is CAN FD +capable but is configured with ``FDOE = 0`` and ``BRSE = 0``. + +BxCanDevice (STM32F4) +--------------------- + +- ``Config`` carries the peripheral base address, bit timing (``prescaler``, + ``bs1``, ``bs2``, ``sjw``, written to ``CAN->BTR`` with ``-1`` encoding) and + the TX/RX GPIO port/pin/alternate-function mapping. +- ``init()`` enables the APB1 clock, configures the GPIO pins, enters init + mode, sets ``ABOM`` and ``TXFP``, programs bit timing and installs an + accept-all filter. ``start()`` leaves init mode and enables ``FMPIE0``. +- TX uses the 3 hardware mailboxes; ``transmit()`` returns ``false`` when all + are full. ``TMEIE`` is enabled while TX is pending and masked again by + ``transmitISR()`` once all mailboxes are idle. +- RX drains hardware FIFO0 (3 deep) into a 32-entry circular software queue, + optionally applying a software bit-field filter. +- Filters: bank 0 in 32-bit mask mode (accept all) or banks 0..13 in 32-bit + identifier-list mode (2 standard IDs per bank, max 28 IDs). + +FdCanDevice (STM32G4) +--------------------- + +- ``Config`` carries the peripheral base address, nominal bit timing + (``prescaler``, ``nts1``, ``nts2``, ``nsjw``, written to ``FDCAN->NBTP``) + and the TX/RX GPIO mapping. The FDCAN kernel clock is set to PCLK1. +- The message RAM layout is fixed in hardware. Each instance owns 212 words + (FDCAN1 at ``SRAMCAN_BASE + 0x000``, FDCAN2 at ``+0x350``, FDCAN3 at + ``+0x6A0``); RX/TX elements are spaced 18 words (72 bytes) apart: + + - Standard ID filters: 28 x 1 word, ``0x000``--``0x06F`` + - Extended ID filters: 8 x 2 words, ``0x070``--``0x0AF`` + - RX FIFO 0: 3 x 18 words, ``0x0B0``--``0x187`` + - RX FIFO 1: 3 x 18 words, ``0x188``--``0x25F`` + - TX event FIFO: 3 x 2 words, ``0x260``--``0x277`` + - TX buffers: 3 x 18 words, ``0x278``--``0x34F`` + +- ``transmit()`` writes the TX element at ``ramBase + 0x278 + putIdx * 72``; + ``receiveISR()`` snapshots the RX FIFO 0 fill level once and drains exactly + that many elements into the same 32-entry software queue as ``BxCanDevice``. +- Filters: accept-all via ``RXGFC`` (``ANFS = 0``, ``ANFE = 0``) or up to 28 + exact-match standard-ID filter elements, rejecting non-matching frames. diff --git a/platforms/stm32/bsp/bspCan/include/can/BxCanDevice.h b/platforms/stm32/bsp/bspCan/include/can/BxCanDevice.h new file mode 100644 index 00000000000..a87a9a65e8c --- /dev/null +++ b/platforms/stm32/bsp/bspCan/include/can/BxCanDevice.h @@ -0,0 +1,215 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include + +namespace bios +{ + +/** + * \brief Low-level bxCAN hardware abstraction for STM32F4. + * + * Manages a bxCAN peripheral: initialization, bit timing, TX mailboxes, + * RX FIFOs, filter banks, and error counters. Does not implement the + * OpenBSW transceiver interface - that is BxCanTransceiver's job. + */ +class BxCanDevice +{ +public: + struct Config + { + CAN_TypeDef* baseAddress; + uint32_t prescaler; + uint32_t bs1; + uint32_t bs2; + uint32_t sjw; + GPIO_TypeDef* rxGpioPort; + uint8_t rxPin; + uint8_t rxAf; + GPIO_TypeDef* txGpioPort; + uint8_t txPin; + uint8_t txAf; + }; + + static constexpr uint32_t RX_QUEUE_SIZE = 32U; + + /** + * \brief Construct a BxCanDevice from a hardware configuration. + * \param config Peripheral base address, bit-timing, and GPIO pin mapping. + */ + explicit BxCanDevice(Config const& config); + + /** + * \brief Initialise the bxCAN peripheral. + * + * Enables the APB1 clock, configures TX/RX GPIO pins, enters init mode, + * programs bit timing, and installs an accept-all hardware filter. + * Must be called before start(). + * \return true on success, false if hardware timeout. + * \note Thread context only - not safe to call from ISR. + */ + bool init(); + + /** + * \brief Leave init mode and begin normal CAN operation. + * + * Drains any stale FIFO0 frames, clears the overrun flag, and enables + * the FMPIE0 (FIFO-message-pending) RX interrupt. + * \return true on success, false if hardware timeout. + * \note Requires a preceding init() call; returns false if not initialised. + */ + bool start(); + + /** + * \brief Disable CAN interrupts and re-enter init mode. + * + * Masks both FMPIE0 (RX) and TMEIE (TX-mailbox-empty) interrupts, + * then requests hardware init mode. + */ + void stop(); + + /** + * \brief Queue a CAN frame for transmission. + * + * Scans TX mailboxes 0-2 for an empty slot, writes the frame's ID, DLC, + * and payload, then sets TXRQ to trigger hardware transmission. + * Enables TMEIE so that transmitISR() fires on completion. + * + * \param frame The CAN frame to transmit (standard or extended ID). + * \return true if the frame was placed in a mailbox, false if all 3 are full. + * + * \note Thread context - must not be called concurrently with transmitISR() + * without external locking. + */ + bool transmit(::can::CANFrame const& frame); + + /** + * \brief ISR handler: drain hardware RX FIFO0 into the software queue. + * + * Reads all pending frames from FIFO0. Each frame is optionally checked + * against a software bit-field filter before being stored in the circular + * RX queue. If the queue is full the FIFO entry is released without storing. + * + * \param filterBitField Byte array indexed by (CAN-ID / 8); bit (CAN-ID % 8) + * set means "accept". Pass nullptr to accept all. + * \return Number of frames actually enqueued. + * + * \note ISR context - called from CAN1_RX0_IRQHandler. + */ + uint8_t receiveISR(uint8_t const* filterBitField); + + /** + * \brief ISR handler: acknowledge completed transmissions. + * + * Clears RQCP flags for all 3 mailboxes. If every mailbox is now empty, + * disables TMEIE to avoid spurious TX interrupts. + * + * \note ISR context - called from CAN1_TX_IRQHandler. + */ + void transmitISR(); + + /** + * \brief Check whether the CAN controller is in bus-off state. + * \return true if the BOFF bit in CAN->ESR is set. + */ + bool isBusOff() const; + + /** + * \brief Read the transmit error counter from CAN->ESR. + * \return TEC value (0-255). + */ + uint8_t getTxErrorCounter() const; + + /** + * \brief Read the receive error counter from CAN->ESR. + * \return REC value (0-255). + */ + uint8_t getRxErrorCounter() const; + + /** + * \brief Configure filter bank 0 in 32-bit mask mode to accept all frames. + * + * Enters filter init mode, sets mask = 0 / id = 0 (accept everything), + * assigns to FIFO0, and leaves filter init mode. + * + * \note Must be called while the peripheral is in init mode. + */ + void configureAcceptAllFilter(); + + /** + * \brief Configure filter banks in 32-bit identifier-list mode. + * + * Packs up to 2 standard IDs per filter bank (banks 0..13). + * If count is odd the last bank duplicates the final ID. + * + * \param idList Array of standard 11-bit CAN IDs to accept. + * \param count Number of entries in idList (max 28). + * + * \note Must be called while the peripheral is in init mode. + */ + void configureFilterList(uint32_t const* idList, uint8_t count); + + /** + * \brief Mask the FMPIE0 interrupt (FIFO0 message-pending). + * + * Used by the transceiver layer to create a critical section around + * RX queue access so that receiveISR() cannot modify the queue + * concurrently. + * + * \note Thread context - called before reading the RX queue. + */ + void disableRxInterrupt(); + + /** + * \brief Re-enable the FMPIE0 interrupt after queue access is complete. + * \note Thread context - called after reading the RX queue. + */ + void enableRxInterrupt(); + + /** + * \brief Access a received frame by index within the circular queue. + * \param index Zero-based offset from the queue head (0 .. getRxCount()-1). + * \return Const reference to the CANFrame at the given position. + */ + ::can::CANFrame const& getRxFrame(uint8_t index) const; + + /** + * \brief Return the number of unread frames in the software RX queue. + * \return Frame count (0 .. RX_QUEUE_SIZE). + */ + uint8_t getRxCount() const; + + /** + * \brief Advance the queue head past all current frames, resetting count to 0. + * + * \note Must be called with the RX interrupt disabled (disableRxInterrupt()) + * to avoid a race with receiveISR(). + */ + void clearRxQueue(); + +private: + Config const fConfig; + ::can::CANFrame fRxQueue[RX_QUEUE_SIZE]; + uint8_t fRxHead; + uint8_t fRxCount; + bool fInitialized; + + bool enterInitMode(); + bool leaveInitMode(); + void configureBitTiming(); + void configureGpio(); + void enablePeripheralClock(); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bspCan/include/can/FdCanDevice.h b/platforms/stm32/bsp/bspCan/include/can/FdCanDevice.h new file mode 100644 index 00000000000..f2328ebb0b9 --- /dev/null +++ b/platforms/stm32/bsp/bspCan/include/can/FdCanDevice.h @@ -0,0 +1,215 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace bios +{ + +/** + * \brief Low-level FDCAN hardware abstraction for STM32G4. + * + * Manages an FDCAN peripheral: initialization, bit timing, TX FIFO, + * RX FIFOs, message-RAM filter elements, and error counters. Used in + * classic CAN mode. Does not implement the OpenBSW transceiver interface. + */ +class FdCanDevice +{ +public: + struct Config + { + FDCAN_GlobalTypeDef* baseAddress; + uint32_t prescaler; + uint32_t nts1; + uint32_t nts2; + uint32_t nsjw; + GPIO_TypeDef* rxGpioPort; + uint8_t rxPin; + uint8_t rxAf; + GPIO_TypeDef* txGpioPort; + uint8_t txPin; + uint8_t txAf; + }; + + /// Maximum number of frames buffered in the software RX queue. + static constexpr uint32_t RX_QUEUE_SIZE = 32U; + + /// HW filter ID list (set before open/start, nullptr = accept all) + uint32_t const* fFilterIds = nullptr; + uint8_t fFilterCount = 0U; + + /** + * \brief Construct an FdCanDevice from a hardware configuration. + * \param config Peripheral base address, bit timing, and GPIO pin map. + */ + explicit FdCanDevice(Config const& config); + + /** + * \brief Construct with a TX-complete callback delegate (matches FlexCANDevice pattern). + * \param config Hardware configuration. + * \param frameSentCallback Delegate invoked from transmitISR() when a listener TX completes. + */ + FdCanDevice(Config const& config, ::etl::delegate frameSentCallback); + + /** + * \brief Initialise the FDCAN peripheral (clock, GPIO, bit timing, message RAM). + * + * Leaves the peripheral in init mode; call start() to begin bus communication. + * \return true on success, false if hardware timeout (e.g. init mode not entered). + * \note Must be called from thread context before any other method. + */ + bool init(); + + /** + * \brief Configure interrupts and leave init mode to start bus communication. + * + * Enables RF0NE (RX FIFO 0 new element) interrupt at startup. TCE (TX + * complete) is managed per-TX by transmit(frame, true) and disabled by + * transmitISR(), matching S32K's selective interrupt pattern. All + * interrupts route to line 0 (ILS=0). Clears INIT to join the bus. + * \return true on success, false if hardware timeout. + * \note Must be called after init(). + */ + bool start(); + + /** + * \brief Disable CAN interrupts and re-enter init mode, detaching from the bus. + */ + void stop(); + + /** + * \brief Queue a CAN frame for transmission via the hardware TX FIFO. + * \param frame Classic CAN frame (standard or extended ID, up to 8 bytes). + * \return true if the frame was placed in the TX FIFO, false if FIFO is full. + * \note Safe to call from thread context. The actual transmission completes + * asynchronously; transmitISR() clears the completion flag. + */ + bool transmit(::can::CANFrame const& frame); + + /** + * \brief Queue a CAN frame with optional TX-complete interrupt control. + * \param frame Classic CAN frame to transmit. + * \param txInterruptNeeded If true, enable TCE before TX (for listener callback). + * If false, do not enable TCE (fire-and-forget). + * \return true if queued, false if FIFO full. + * \note Matches FlexCANDevice::transmit(frame, bufIdx, txInterruptNeeded) contract. + */ + bool transmit(::can::CANFrame const& frame, bool txInterruptNeeded); + + /** + * \brief Drain RX FIFO 0 into the software queue. Called from the RX ISR. + * + * Takes a snapshot of the current fill level and drains only that many + * elements, preventing an infinite loop on a busy bus. Frames whose + * CAN ID is not set in @p filterBitField are acknowledged but discarded. + * + * \param filterBitField Bit-field indexed by CAN ID; a set bit means + * "accept this ID". Pass nullptr to accept all. + * \return Number of frames actually stored in the software RX queue. + * \note ISR context only. Clears RF0N, RF0F, and RF0L interrupt flags. + */ + uint8_t receiveISR(uint8_t const* filterBitField); + + /** + * \brief Handle TX-complete interrupt: disable TCE, clear TC flag, invoke + * callback delegate. Matches FlexCANDevice::transmitISR() contract. + * \note ISR context only (interrupt line 0, ILS=0). + */ + void transmitISR(); + + /** + * \brief Check whether the FDCAN peripheral is in bus-off state. + * \return true if the BO bit in FDCAN->PSR is set. + */ + bool isBusOff() const; + + /** + * \brief Read the transmit error counter from FDCAN->ECR. + * \return Current TEC value (0-255). + */ + uint8_t getTxErrorCounter() const; + + /** + * \brief Read the receive error counter from FDCAN->ECR. + * \return Current REC value (0-127). + */ + uint8_t getRxErrorCounter() const; + + /** + * \brief Configure the global filter to accept all standard and extended IDs + * into RX FIFO 0 (no hardware filtering). + * \note Must be called while the peripheral is in init mode. + */ + void configureAcceptAllFilter(); + + /** + * \brief Program standard-ID filter elements in message RAM for exact-match + * acceptance, rejecting all non-matching frames. + * \param idList Array of 11-bit standard CAN IDs to accept. + * \param count Number of entries in @p idList (max 28). + * \note Must be called while the peripheral is in init mode. + */ + void configureFilterList(uint32_t const* idList, uint8_t count); + + /** + * \brief Retrieve a received frame from the software RX queue by index. + * \param index Logical index (0 .. getRxCount()-1), relative to fRxHead. + * \return Const reference to the CANFrame at the given queue position. + */ + ::can::CANFrame const& getRxFrame(uint8_t index) const; + + /** + * \brief Return the number of frames currently buffered in the software RX queue. + * \return Frame count (0 .. RX_QUEUE_SIZE). + */ + uint8_t getRxCount() const; + + /** + * \brief Advance the queue head past all currently stored frames and reset count. + * \note Call from thread context after processing all frames returned by getRxFrame(). + */ + void clearRxQueue(); + + /** + * \brief Mask the RF0NE interrupt (RX FIFO 0 new element). + * \note Used to prevent ISR re-entry while the thread drains the queue. + */ + void disableRxInterrupt(); + + /** + * \brief Unmask the RF0NE interrupt (RX FIFO 0 new element). + */ + void enableRxInterrupt(); + + /// Returns the number of pending frames in the hardware RX FIFO0. + uint32_t getHwFifoFillLevel() const; + +private: + Config const fConfig; + ::can::CANFrame fRxQueue[RX_QUEUE_SIZE]; + uint8_t fRxHead; + uint8_t fRxCount; + bool fInitialized; + ::etl::delegate fFrameSentCallback; + + bool enterInitMode(); + bool leaveInitMode(); + void configureBitTiming(); + void configureMessageRam(); + void configureGpio(); + void enablePeripheralClock(); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bspCan/module.spec b/platforms/stm32/bsp/bspCan/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bspCan/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bspCan/src/can/BxCanDevice.cpp b/platforms/stm32/bsp/bspCan/src/can/BxCanDevice.cpp new file mode 100644 index 00000000000..578e3287de2 --- /dev/null +++ b/platforms/stm32/bsp/bspCan/src/can/BxCanDevice.cpp @@ -0,0 +1,382 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +namespace bios +{ + +BxCanDevice::BxCanDevice(Config const& config) +: fConfig(config), fRxQueue{}, fRxHead(0U), fRxCount(0U), fInitialized(false) +{} + +void BxCanDevice::enablePeripheralClock() +{ + // Enable CAN1 clock on APB1 + RCC->APB1ENR |= RCC_APB1ENR_CAN1EN; + // Small delay for clock stabilization + uint32_t volatile dummy = RCC->APB1ENR; + (void)dummy; +} + +void BxCanDevice::configureGpio() +{ + GPIO_TypeDef* txPort = fConfig.txGpioPort; + GPIO_TypeDef* rxPort = fConfig.rxGpioPort; + + // TX pin: AF mode, push-pull, high speed + txPort->MODER &= ~(3U << (fConfig.txPin * 2U)); + txPort->MODER |= (2U << (fConfig.txPin * 2U)); + txPort->OSPEEDR |= (3U << (fConfig.txPin * 2U)); + if (fConfig.txPin < 8U) + { + txPort->AFR[0] &= ~(0xFU << (fConfig.txPin * 4U)); + txPort->AFR[0] |= (static_cast(fConfig.txAf) << (fConfig.txPin * 4U)); + } + else + { + txPort->AFR[1] &= ~(0xFU << ((fConfig.txPin - 8U) * 4U)); + txPort->AFR[1] |= (static_cast(fConfig.txAf) << ((fConfig.txPin - 8U) * 4U)); + } + + // RX pin: AF mode, pull-up + rxPort->MODER &= ~(3U << (fConfig.rxPin * 2U)); + rxPort->MODER |= (2U << (fConfig.rxPin * 2U)); + rxPort->PUPDR &= ~(3U << (fConfig.rxPin * 2U)); + rxPort->PUPDR |= (1U << (fConfig.rxPin * 2U)); // Pull-up + if (fConfig.rxPin < 8U) + { + rxPort->AFR[0] &= ~(0xFU << (fConfig.rxPin * 4U)); + rxPort->AFR[0] |= (static_cast(fConfig.rxAf) << (fConfig.rxPin * 4U)); + } + else + { + rxPort->AFR[1] &= ~(0xFU << ((fConfig.rxPin - 8U) * 4U)); + rxPort->AFR[1] |= (static_cast(fConfig.rxAf) << ((fConfig.rxPin - 8U) * 4U)); + } +} + +bool BxCanDevice::enterInitMode() +{ + fConfig.baseAddress->MCR |= CAN_MCR_INRQ; +#if !defined(UNIT_TEST) + uint32_t timeout = 100000U; + while ((fConfig.baseAddress->MSR & CAN_MSR_INAK) == 0U) + { + if (--timeout == 0U) + { + return false; + } + } +#else + // In unit tests, MSR doesn't auto-track MCR. Simulate HW behavior. + fConfig.baseAddress->MSR |= CAN_MSR_INAK; +#endif + return true; +} + +bool BxCanDevice::leaveInitMode() +{ + fConfig.baseAddress->MCR &= ~CAN_MCR_INRQ; +#if !defined(UNIT_TEST) + uint32_t timeout = 100000U; + while ((fConfig.baseAddress->MSR & CAN_MSR_INAK) != 0U) + { + if (--timeout == 0U) + { + return false; + } + } +#else + fConfig.baseAddress->MSR &= ~CAN_MSR_INAK; +#endif + return true; +} + +void BxCanDevice::configureBitTiming() +{ + // BTR: SJW | BS2 | BS1 | BRP (prescaler - 1) + fConfig.baseAddress->BTR = ((fConfig.sjw - 1U) << CAN_BTR_SJW_Pos) + | ((fConfig.bs2 - 1U) << CAN_BTR_TS2_Pos) + | ((fConfig.bs1 - 1U) << CAN_BTR_TS1_Pos) | (fConfig.prescaler - 1U); +} + +bool BxCanDevice::init() +{ + enablePeripheralClock(); + configureGpio(); + + if (!enterInitMode()) + { + return false; + } + + // Exit sleep mode + fConfig.baseAddress->MCR &= ~CAN_MCR_SLEEP; + + // Configure: auto bus-off management, auto retransmission, TX FIFO priority + fConfig.baseAddress->MCR |= CAN_MCR_ABOM | CAN_MCR_TXFP; + + configureBitTiming(); + configureAcceptAllFilter(); + + fInitialized = true; + return true; +} + +bool BxCanDevice::start() +{ + if (!fInitialized) + { + return false; + } + + if (!leaveInitMode()) + { + return false; + } + + // Drain any frames that arrived while in init mode +#if !defined(UNIT_TEST) + while ((fConfig.baseAddress->RF0R & CAN_RF0R_FMP0) != 0U) + { + fConfig.baseAddress->RF0R |= CAN_RF0R_RFOM0; + } +#endif + // Clear overrun flag + fConfig.baseAddress->RF0R |= CAN_RF0R_FOVR0; + + fConfig.baseAddress->IER |= CAN_IER_FMPIE0; + return true; +} + +void BxCanDevice::stop() +{ + fConfig.baseAddress->IER &= ~(CAN_IER_FMPIE0 | CAN_IER_TMEIE); + enterInitMode(); +} + +bool BxCanDevice::transmit(::can::CANFrame const& frame) +{ + CAN_TypeDef* can = fConfig.baseAddress; + + // Find an empty TX mailbox + uint8_t mailbox = 0xFFU; + if ((can->TSR & CAN_TSR_TME0) != 0U) + { + mailbox = 0U; + } + else if ((can->TSR & CAN_TSR_TME1) != 0U) + { + mailbox = 1U; + } + else if ((can->TSR & CAN_TSR_TME2) != 0U) + { + mailbox = 2U; + } + else + { + return false; // All mailboxes full + } + + // Set ID (standard 11-bit) + uint32_t id = frame.getId(); + if ((id & 0x80000000U) != 0U) + { + // Extended ID + can->sTxMailBox[mailbox].TIR = ((id & 0x1FFFFFFFU) << CAN_TI0R_EXID_Pos) | CAN_TI0R_IDE; + } + else + { + // Standard ID + can->sTxMailBox[mailbox].TIR = ((id & 0x7FFU) << CAN_TI0R_STID_Pos); + } + + // Set DLC + uint8_t dlc = frame.getPayloadLength(); + can->sTxMailBox[mailbox].TDTR = (dlc & 0xFU); + + // Set data bytes + uint8_t const* data = frame.getPayload(); + can->sTxMailBox[mailbox].TDLR + = static_cast(data[0]) | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) | (static_cast(data[3]) << 24U); + can->sTxMailBox[mailbox].TDHR + = static_cast(data[4]) | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) | (static_cast(data[7]) << 24U); + + // Request transmission + can->sTxMailBox[mailbox].TIR |= CAN_TI0R_TXRQ; + + // Enable TX mailbox empty interrupt now that we have pending TX + can->IER |= CAN_IER_TMEIE; + + return true; +} + +uint8_t BxCanDevice::receiveISR(uint8_t const* filterBitField) +{ + CAN_TypeDef* can = fConfig.baseAddress; + uint8_t received = 0U; + + // Snapshot fill level (same pattern as FDCAN receiveISR). + // Prevents infinite loop if HW latches new frames during drain. + uint8_t toDrain = static_cast(can->RF0R & CAN_RF0R_FMP0); + while (toDrain > 0U) + { + toDrain--; + if (fRxCount >= RX_QUEUE_SIZE) + { + // Queue full - release FIFO entry without storing + can->RF0R |= CAN_RF0R_RFOM0; + continue; + } + + // Read ID + uint32_t rir = can->sFIFOMailBox[0].RIR; + uint32_t id; + if ((rir & CAN_RI0R_IDE) != 0U) + { + id = ((rir >> CAN_RI0R_EXID_Pos) & 0x1FFFFFFFU) | 0x80000000U; + } + else + { + id = (rir >> CAN_RI0R_STID_Pos) & 0x7FFU; + } + + // Filter check using BitFieldFilter byte array (if provided) + if (filterBitField != nullptr) + { + uint32_t byteIndex = id / 8U; + uint32_t bitIndex = id % 8U; + if ((filterBitField[byteIndex] & (1U << bitIndex)) == 0U) + { + can->RF0R |= CAN_RF0R_RFOM0; + continue; + } + } + + // Read DLC and data + uint8_t dlc = static_cast(can->sFIFOMailBox[0].RDTR & 0xFU); + uint32_t rdlr = can->sFIFOMailBox[0].RDLR; + uint32_t rdhr = can->sFIFOMailBox[0].RDHR; + + uint8_t data[8]; + data[0] = static_cast(rdlr); + data[1] = static_cast(rdlr >> 8U); + data[2] = static_cast(rdlr >> 16U); + data[3] = static_cast(rdlr >> 24U); + data[4] = static_cast(rdhr); + data[5] = static_cast(rdhr >> 8U); + data[6] = static_cast(rdhr >> 16U); + data[7] = static_cast(rdhr >> 24U); + + // Store in queue + uint8_t idx = (fRxHead + fRxCount) % RX_QUEUE_SIZE; + fRxQueue[idx] = ::can::CANFrame(id, data, dlc); + fRxCount++; + received++; + + // Release FIFO entry + can->RF0R |= CAN_RF0R_RFOM0; + } + + return received; +} + +void BxCanDevice::transmitISR() +{ + // Clear TX request completed flags + fConfig.baseAddress->TSR |= CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2; + + // Disable TMEIE if all mailboxes are now empty (prevents spurious interrupts) + if ((fConfig.baseAddress->TSR & (CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2)) + == (CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2)) + { + fConfig.baseAddress->IER &= ~CAN_IER_TMEIE; + } +} + +bool BxCanDevice::isBusOff() const { return (fConfig.baseAddress->ESR & CAN_ESR_BOFF) != 0U; } + +uint8_t BxCanDevice::getTxErrorCounter() const +{ + return static_cast((fConfig.baseAddress->ESR >> CAN_ESR_TEC_Pos) & 0xFFU); +} + +uint8_t BxCanDevice::getRxErrorCounter() const +{ + return static_cast((fConfig.baseAddress->ESR >> CAN_ESR_REC_Pos) & 0xFFU); +} + +void BxCanDevice::configureAcceptAllFilter() +{ + // Filter bank 0: accept all standard + extended frames into FIFO0 + CAN_TypeDef* can = fConfig.baseAddress; + can->FMR |= CAN_FMR_FINIT; // Enter filter init mode + can->FA1R &= ~(1U << 0U); // Deactivate filter 0 + can->sFilterRegister[0].FR1 = 0U; // ID = 0 (don't care) + can->sFilterRegister[0].FR2 = 0U; // Mask = 0 (accept all) + can->FS1R |= (1U << 0U); // 32-bit scale + can->FM1R &= ~(1U << 0U); // Mask mode (not list) + can->FFA1R &= ~(1U << 0U); // Assign to FIFO0 + can->FA1R |= (1U << 0U); // Activate filter 0 + can->FMR &= ~CAN_FMR_FINIT; // Leave filter init mode +} + +void BxCanDevice::configureFilterList(uint32_t const* idList, uint8_t count) +{ + CAN_TypeDef* can = fConfig.baseAddress; + can->FMR |= CAN_FMR_FINIT; + + // Use filter banks 0..N/2 in 32-bit list mode (2 IDs per bank) + uint8_t bankIdx = 0U; + for (uint8_t i = 0U; i < count && bankIdx < 14U; i += 2U, bankIdx++) + { + can->FA1R &= ~(1U << bankIdx); + can->FS1R |= (1U << bankIdx); // 32-bit scale + can->FM1R |= (1U << bankIdx); // List mode + + // Standard IDs shifted to STID position + can->sFilterRegister[bankIdx].FR1 = (idList[i] << CAN_RI0R_STID_Pos); + if ((i + 1U) < count) + { + can->sFilterRegister[bankIdx].FR2 = (idList[i + 1U] << CAN_RI0R_STID_Pos); + } + else + { + can->sFilterRegister[bankIdx].FR2 = (idList[i] << CAN_RI0R_STID_Pos); + } + + can->FFA1R &= ~(1U << bankIdx); // FIFO0 + can->FA1R |= (1U << bankIdx); // Activate + } + + can->FMR &= ~CAN_FMR_FINIT; +} + +::can::CANFrame const& BxCanDevice::getRxFrame(uint8_t index) const +{ + return fRxQueue[(fRxHead + index) % RX_QUEUE_SIZE]; +} + +uint8_t BxCanDevice::getRxCount() const { return fRxCount; } + +void BxCanDevice::clearRxQueue() +{ + fRxHead = (fRxHead + fRxCount) % RX_QUEUE_SIZE; + fRxCount = 0U; +} + +void BxCanDevice::disableRxInterrupt() { fConfig.baseAddress->IER &= ~CAN_IER_FMPIE0; } + +void BxCanDevice::enableRxInterrupt() { fConfig.baseAddress->IER |= CAN_IER_FMPIE0; } + +} // namespace bios diff --git a/platforms/stm32/bsp/bspCan/src/can/FdCanDevice.cpp b/platforms/stm32/bsp/bspCan/src/can/FdCanDevice.cpp new file mode 100644 index 00000000000..bfb4021b97f --- /dev/null +++ b/platforms/stm32/bsp/bspCan/src/can/FdCanDevice.cpp @@ -0,0 +1,485 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +namespace bios +{ + +// STM32G4 FDCAN message RAM layout (fixed, per instance): +// Each FDCAN instance has 212 words (848 bytes) of message RAM. +// FDCAN1 starts at SRAMCAN_BASE + 0x000 +// FDCAN2 starts at SRAMCAN_BASE + 0x350 +// FDCAN3 starts at SRAMCAN_BASE + 0x6A0 +// +// Layout within each instance (offsets from instance base; RX/TX elements +// are spaced 18 words / 72 bytes apart regardless of configured data size): +// Standard ID filters: 28 elements x 1 word = 28 words (0x000 - 0x06F) +// Extended ID filters: 8 elements x 2 words = 16 words (0x070 - 0x0AF) +// RX FIFO0: 3 elements x 18 words = 54 words (0x0B0 - 0x187) +// RX FIFO1: 3 elements x 18 words = 54 words (0x188 - 0x25F) +// TX Event FIFO: 3 elements x 2 words = 6 words (0x260 - 0x277) +// TX Buffers: 3 elements x 18 words = 54 words (0x278 - 0x34F) + +// uintptr_t so host unit tests with 64-bit fake RAM addresses work; on the +// 32-bit target this is identical to uint32_t. +static uintptr_t getInstanceRamBase(FDCAN_GlobalTypeDef* fdcan) +{ + if (fdcan == FDCAN1) + { + return SRAMCAN_BASE; + } +#if defined(FDCAN2) + if (fdcan == FDCAN2) + { + return SRAMCAN_BASE + 0x350U; + } +#endif +#if defined(FDCAN3) + if (fdcan == FDCAN3) + { + return SRAMCAN_BASE + 0x6A0U; + } +#endif + return SRAMCAN_BASE; +} + +static constexpr uint32_t STD_FILTER_OFFSET = 0x000U; +static constexpr uint32_t RX_FIFO0_OFFSET = 0x0B0U; +// STM32G4 message RAM: TX buffers at 0x278 (not 0x128 as standard M_CAN) +static constexpr uint32_t TX_BUFFER_OFFSET = 0x278U; + +FdCanDevice::FdCanDevice(Config const& config) +: fConfig(config), fRxQueue{}, fRxHead(0U), fRxCount(0U), fInitialized(false), fFrameSentCallback() +{} + +FdCanDevice::FdCanDevice(Config const& config, ::etl::delegate frameSentCallback) +: fConfig(config) +, fRxQueue{} +, fRxHead(0U) +, fRxCount(0U) +, fInitialized(false) +, fFrameSentCallback(frameSentCallback) +{} + +void FdCanDevice::enablePeripheralClock() +{ + // Enable FDCAN clock on APB1 + RCC->APB1ENR1 |= RCC_APB1ENR1_FDCANEN; + uint32_t volatile dummy = RCC->APB1ENR1; + (void)dummy; + + // Select FDCAN kernel clock = PCLK1 (FDCANSEL=10 in CCIPR1[25:24]) + // Default after reset is HSE (00), which may not be enabled. + RCC->CCIPR = (RCC->CCIPR & ~(3U << 24U)) | (2U << 24U); +} + +void FdCanDevice::configureGpio() +{ + GPIO_TypeDef* txPort = fConfig.txGpioPort; + GPIO_TypeDef* rxPort = fConfig.rxGpioPort; + + txPort->MODER &= ~(3U << (fConfig.txPin * 2U)); + txPort->MODER |= (2U << (fConfig.txPin * 2U)); + txPort->OSPEEDR |= (3U << (fConfig.txPin * 2U)); + if (fConfig.txPin < 8U) + { + txPort->AFR[0] &= ~(0xFU << (fConfig.txPin * 4U)); + txPort->AFR[0] |= (static_cast(fConfig.txAf) << (fConfig.txPin * 4U)); + } + else + { + txPort->AFR[1] &= ~(0xFU << ((fConfig.txPin - 8U) * 4U)); + txPort->AFR[1] |= (static_cast(fConfig.txAf) << ((fConfig.txPin - 8U) * 4U)); + } + + rxPort->MODER &= ~(3U << (fConfig.rxPin * 2U)); + rxPort->MODER |= (2U << (fConfig.rxPin * 2U)); + rxPort->PUPDR &= ~(3U << (fConfig.rxPin * 2U)); + rxPort->PUPDR |= (1U << (fConfig.rxPin * 2U)); + if (fConfig.rxPin < 8U) + { + rxPort->AFR[0] &= ~(0xFU << (fConfig.rxPin * 4U)); + rxPort->AFR[0] |= (static_cast(fConfig.rxAf) << (fConfig.rxPin * 4U)); + } + else + { + rxPort->AFR[1] &= ~(0xFU << ((fConfig.rxPin - 8U) * 4U)); + rxPort->AFR[1] |= (static_cast(fConfig.rxAf) << ((fConfig.rxPin - 8U) * 4U)); + } +} + +static constexpr uint32_t INIT_TIMEOUT_CYCLES = 100000U; + +bool FdCanDevice::enterInitMode() +{ + fConfig.baseAddress->CCCR |= FDCAN_CCCR_INIT; + uint32_t timeout = INIT_TIMEOUT_CYCLES; + while ((fConfig.baseAddress->CCCR & FDCAN_CCCR_INIT) == 0U) + { + if (--timeout == 0U) + { + return false; + } + } + // Enable configuration change + fConfig.baseAddress->CCCR |= FDCAN_CCCR_CCE; + return true; +} + +bool FdCanDevice::leaveInitMode() +{ + fConfig.baseAddress->CCCR &= ~FDCAN_CCCR_INIT; + uint32_t timeout = INIT_TIMEOUT_CYCLES; + while ((fConfig.baseAddress->CCCR & FDCAN_CCCR_INIT) != 0U) + { + if (--timeout == 0U) + { + return false; + } + } + return true; +} + +void FdCanDevice::configureBitTiming() +{ + // Nominal bit timing for classic CAN mode + // NBTP: NSJW | NBRP | NTSEG1 | NTSEG2 + fConfig.baseAddress->NBTP = ((fConfig.nsjw) << FDCAN_NBTP_NSJW_Pos) + | ((fConfig.prescaler - 1U) << FDCAN_NBTP_NBRP_Pos) + | ((fConfig.nts1) << FDCAN_NBTP_NTSEG1_Pos) + | ((fConfig.nts2) << FDCAN_NBTP_NTSEG2_Pos); +} + +void FdCanDevice::configureMessageRam() +{ + // STM32G4 has fixed message RAM layout - no configuration registers. + // RXGFC configures global filter behavior and list sizes only. + fConfig.baseAddress->RXGFC = (0U << FDCAN_RXGFC_LSS_Pos) | (0U << FDCAN_RXGFC_LSE_Pos); + + // TX buffer: use FIFO/queue mode + fConfig.baseAddress->TXBC = 0U; // FIFO mode (TFQM=0) +} + +bool FdCanDevice::init() +{ + enablePeripheralClock(); + configureGpio(); + + if (!enterInitMode()) + { + return false; + } + + // Classic CAN mode (no FD), auto retransmission disabled + fConfig.baseAddress->CCCR &= ~FDCAN_CCCR_FDOE; + fConfig.baseAddress->CCCR &= ~FDCAN_CCCR_BRSE; + + configureBitTiming(); + configureMessageRam(); + configureAcceptAllFilter(); + + fInitialized = true; + return true; +} + +bool FdCanDevice::start() +{ + if (!fInitialized) + { + return false; + } + + // Configure HW filter in init mode (before leaveInitMode) + if (fFilterIds != nullptr && fFilterCount > 0U) + { + configureFilterList(fFilterIds, fFilterCount); + } + else + { + configureAcceptAllFilter(); + } + + // Enable RX interrupt only at startup. TCE is managed per-TX by + // transmit(frame, true) and disabled by transmitISR() - matching S32K's + // selective per-buffer interrupt enable/disable pattern. + fConfig.baseAddress->IE = FDCAN_IE_RF0NE; + fConfig.baseAddress->ILS = 0U; + fConfig.baseAddress->ILE = FDCAN_ILE_EINT0; + fConfig.baseAddress->TXBTIE = 0x7U; // Required for TC generation per RM0440 + + if (!leaveInitMode()) + { + return false; + } + + // Write IE again after leaving init mode (persistence workaround). + fConfig.baseAddress->IE = FDCAN_IE_RF0NE; + return true; +} + +void FdCanDevice::stop() +{ + fConfig.baseAddress->IE &= ~(FDCAN_IE_RF0NE | FDCAN_IE_TCE); + enterInitMode(); +} + +bool FdCanDevice::transmit(::can::CANFrame const& frame) +{ + FDCAN_GlobalTypeDef* fdcan = fConfig.baseAddress; + + // Check TX FIFO free level + if ((fdcan->TXFQS & FDCAN_TXFQS_TFFL) == 0U) + { + return false; // TX FIFO full + } + + // Get put index + uint32_t putIdx = (fdcan->TXFQS & FDCAN_TXFQS_TFQPI) >> FDCAN_TXFQS_TFQPI_Pos; + + // Calculate TX buffer element address in message RAM + // STM32G4 FDCAN uses 18-word (72-byte) element spacing in message RAM, + // regardless of configured data field size (TXESC). Section boundaries + // are fixed at max element size. + static constexpr uint32_t TX_ELEMENT_SIZE = 72U; // 18 words x 4 bytes + uintptr_t ramBase = getInstanceRamBase(fdcan); + uint32_t* txBuf + = reinterpret_cast(ramBase + TX_BUFFER_OFFSET + (putIdx * TX_ELEMENT_SIZE)); + + // Word 0: ID + uint32_t id = frame.getId(); + if ((id & 0x80000000U) != 0U) + { + txBuf[0] = ((id & 0x1FFFFFFFU)) | (1U << 30U); // XTD bit + } + else + { + txBuf[0] = ((id & 0x7FFU) << 18U); + } + + // Word 1: DLC, no FD, no BRS + uint8_t dlc = frame.getPayloadLength(); + txBuf[1] = (static_cast(dlc) << 16U); + + // Words 2-3: Data + uint8_t const* data = frame.getPayload(); + txBuf[2] = static_cast(data[0]) | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) | (static_cast(data[3]) << 24U); + txBuf[3] = static_cast(data[4]) | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) | (static_cast(data[7]) << 24U); + + // Request transmission + fdcan->TXBAR = (1U << putIdx); + + return true; +} + +bool FdCanDevice::transmit(::can::CANFrame const& frame, bool txInterruptNeeded) +{ + if (txInterruptNeeded) + { + // Match S32K enableTransmitInterrupt(): clear flag first, then enable mask. + // FlexCANDevice.h:132-136: + // fpDevice->IFLAG1 = fTxInterruptMask0; // clear flag + // fpDevice->IMASK1 |= fTxInterruptMask0; // enable mask + fConfig.baseAddress->IR = FDCAN_IR_TC; // clear stale TC flag + fConfig.baseAddress->IE |= FDCAN_IE_TCE; // enable TC interrupt + } + // S32K: enableTransmitInterrupt() called BEFORE CODE=TRANSMIT. + // Here: TCE enabled BEFORE TXBAR write (inside transmit()). + return transmit(frame); +} + +} // namespace bios + +namespace bios +{ // reopen + +uint8_t FdCanDevice::receiveISR(uint8_t const* filterBitField) +{ + FDCAN_GlobalTypeDef* fdcan = fConfig.baseAddress; + uintptr_t ramBase = getInstanceRamBase(fdcan); + uint8_t received = 0U; + + // NOTE: RF0NE disable moved to CanSystem ISR trampoline (if needed). + // Disabling here is unsafe because receiveTask() may not run if the + // async dispatch is dedup-dropped, permanently blocking all RX. + + // NOTE: RF0L (message lost) is cleared below with RF0N and RF0F. + // Production code could increment an overrun counter here if needed. + + // Clear RF0N BEFORE reading F0FL so late arrivals re-trigger ISR + // (once RF0NE is re-enabled by receiveTask). + fdcan->IR = FDCAN_IR_RF0N | FDCAN_IR_RF0F | FDCAN_IR_RF0L; + + // Snapshot fill level - drain only what's here now. + uint8_t toDrain + = static_cast((fdcan->RXF0S & FDCAN_RXF0S_F0FL) >> FDCAN_RXF0S_F0FL_Pos); + + while (toDrain > 0U) + { + toDrain--; + + if (fRxCount >= RX_QUEUE_SIZE) + { + // Acknowledge without storing + uint32_t getIdx = (fdcan->RXF0S & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos; + fdcan->RXF0A = getIdx; + continue; + } + + uint32_t getIdx = (fdcan->RXF0S & FDCAN_RXF0S_F0GI) >> FDCAN_RXF0S_F0GI_Pos; + + // Read RX FIFO element from message RAM + // STM32G4 FDCAN uses 18-word (72-byte) element spacing in message RAM. + static constexpr uint32_t RX_ELEMENT_SIZE = 72U; // 18 words x 4 bytes + uint32_t const* rxBuf = reinterpret_cast( + ramBase + RX_FIFO0_OFFSET + (getIdx * RX_ELEMENT_SIZE)); + + // Word 0: ID + uint32_t r0 = rxBuf[0]; + uint32_t id; + if ((r0 & (1U << 30U)) != 0U) + { + id = (r0 & 0x1FFFFFFFU) | 0x80000000U; // Extended + } + else + { + id = (r0 >> 18U) & 0x7FFU; // Standard + } + + if (filterBitField != nullptr) + { + uint32_t byteIndex = id / 8U; + uint32_t bitIndex = id % 8U; + if ((filterBitField[byteIndex] & (1U << bitIndex)) == 0U) + { + fdcan->RXF0A = getIdx; + continue; + } + } + + // Word 1: DLC + uint32_t r1 = rxBuf[1]; + uint8_t dlc = static_cast((r1 >> 16U) & 0xFU); + + // Words 2-3: Data + uint32_t d0 = rxBuf[2]; + uint32_t d1 = rxBuf[3]; + uint8_t data[8]; + data[0] = static_cast(d0); + data[1] = static_cast(d0 >> 8U); + data[2] = static_cast(d0 >> 16U); + data[3] = static_cast(d0 >> 24U); + data[4] = static_cast(d1); + data[5] = static_cast(d1 >> 8U); + data[6] = static_cast(d1 >> 16U); + data[7] = static_cast(d1 >> 24U); + + // Store in queue + uint8_t idx = (fRxHead + fRxCount) % RX_QUEUE_SIZE; + fRxQueue[idx] = ::can::CANFrame(id, data, dlc); + fRxCount++; + received++; + + // Acknowledge + fdcan->RXF0A = getIdx; + } + + // RF0N was already cleared at entry - no need to clear again here. + // Any frame arriving during the drain loop will re-set RF0N and + // trigger a new ISR after we return. + return received; +} + +void FdCanDevice::transmitISR() +{ + FDCAN_GlobalTypeDef* fdcan = fConfig.baseAddress; + + // Disable TCE (match S32K disableTransmitInterrupt pattern) + fdcan->IE &= ~FDCAN_IE_TCE; + + // Clear TC flag + fdcan->IR = FDCAN_IR_TC; + + // Invoke callback delegate if set (match FlexCANDevice::transmitISR) + if (fFrameSentCallback.is_valid()) + { + fFrameSentCallback(); + } +} + +bool FdCanDevice::isBusOff() const { return (fConfig.baseAddress->PSR & FDCAN_PSR_BO) != 0U; } + +uint8_t FdCanDevice::getTxErrorCounter() const +{ + return static_cast((fConfig.baseAddress->ECR >> FDCAN_ECR_TEC_Pos) & 0xFFU); +} + +uint8_t FdCanDevice::getRxErrorCounter() const +{ + return static_cast((fConfig.baseAddress->ECR >> FDCAN_ECR_REC_Pos) & 0x7FU); +} + +void FdCanDevice::configureAcceptAllFilter() +{ + // Accept all frames: set global filter to accept non-matching into FIFO0 + fConfig.baseAddress->RXGFC + = (0U << FDCAN_RXGFC_ANFS_Pos) // Accept non-matching std into RX FIFO0 + | (0U << FDCAN_RXGFC_ANFE_Pos); // Accept non-matching ext into RX FIFO0 +} + +void FdCanDevice::configureFilterList(uint32_t const* idList, uint8_t count) +{ + uintptr_t ramBase = getInstanceRamBase(fConfig.baseAddress); + + // Reject non-matching, configure standard ID filter elements + fConfig.baseAddress->RXGFC = (2U << FDCAN_RXGFC_ANFS_Pos) // Reject non-matching std + | (2U << FDCAN_RXGFC_ANFE_Pos) // Reject non-matching ext + | (static_cast(count) << FDCAN_RXGFC_LSS_Pos); + + // Write filter elements to message RAM (standard filter area) + uint32_t* filterRam = reinterpret_cast(ramBase + STD_FILTER_OFFSET); + + for (uint8_t i = 0U; i < count && i < 28U; i++) + { + // Standard filter element: classic filter (ID + mask) for exact match + // SFT = 10 (classic), SFEC = 001 (store in RX FIFO0) + // SFID1 = target ID, SFID2 = 0x7FF (all 11 bits must match) + filterRam[i] = (2U << 30U) // SFT = 10 (classic filter) + | (1U << 27U) // SFEC = store in FIFO0 + | ((idList[i] & 0x7FFU) << 16U) // SFID1 = filter ID + | 0x7FFU; // SFID2 = mask (exact match) + } +} + +::can::CANFrame const& FdCanDevice::getRxFrame(uint8_t index) const +{ + return fRxQueue[(fRxHead + index) % RX_QUEUE_SIZE]; +} + +uint8_t FdCanDevice::getRxCount() const { return fRxCount; } + +void FdCanDevice::clearRxQueue() +{ + fRxHead = (fRxHead + fRxCount) % RX_QUEUE_SIZE; + fRxCount = 0U; +} + +void FdCanDevice::disableRxInterrupt() { fConfig.baseAddress->IE &= ~FDCAN_IE_RF0NE; } + +void FdCanDevice::enableRxInterrupt() { fConfig.baseAddress->IE |= FDCAN_IE_RF0NE; } + +uint32_t FdCanDevice::getHwFifoFillLevel() const +{ + return (fConfig.baseAddress->RXF0S & FDCAN_RXF0S_F0FL) >> FDCAN_RXF0S_F0FL_Pos; +} + +} // namespace bios diff --git a/platforms/stm32/bsp/bspCan/test/CMakeLists.txt b/platforms/stm32/bsp/bspCan/test/CMakeLists.txt new file mode 100644 index 00000000000..08d6151d36e --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(BxCanDeviceTest src/can/BxCanDeviceTest.cpp) + +target_include_directories(BxCanDeviceTest PRIVATE include ../include ../src) + +target_link_libraries(BxCanDeviceTest PRIVATE cpp2can etl gmock gtest_main) + +gtest_discover_tests(BxCanDeviceTest PROPERTIES LABELS "BxCanDeviceTest") + +add_executable(FdCanDeviceTest src/can/FdCanDeviceTest.cpp) + +target_include_directories(FdCanDeviceTest PRIVATE include ../include ../src) + +target_link_libraries(FdCanDeviceTest PRIVATE cpp2can etl gmock gtest_main) + +gtest_discover_tests(FdCanDeviceTest PROPERTIES LABELS "FdCanDeviceTest") diff --git a/platforms/stm32/bsp/bspCan/test/include/mcu/mcu.h b/platforms/stm32/bsp/bspCan/test/include/mcu/mcu.h new file mode 100644 index 00000000000..4b86cd649d5 --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/include/mcu/mcu.h @@ -0,0 +1,12 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Fake mcu.h for unit tests - types defined in test file +#pragma once diff --git a/platforms/stm32/bsp/bspCan/test/src/can/BxCanDeviceTest.cpp b/platforms/stm32/bsp/bspCan/test/src/can/BxCanDeviceTest.cpp new file mode 100644 index 00000000000..0867d082632 --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/src/can/BxCanDeviceTest.cpp @@ -0,0 +1,4110 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test-only hardware fakes - must be defined before any STM32 header inclusion. + +#include +#include +#include +#include +#include + +// Provide __IO as volatile (same as CMSIS) +#ifndef __IO +#define __IO volatile +#endif + +// --- Fake GPIO_TypeDef (matches STM32F4 layout) --- +typedef struct +{ + __IO uint32_t MODER; + __IO uint32_t OTYPER; + __IO uint32_t OSPEEDR; + __IO uint32_t PUPDR; + __IO uint32_t IDR; + __IO uint32_t ODR; + __IO uint32_t BSRR; + __IO uint32_t LCKR; + __IO uint32_t AFR[2]; + __IO uint32_t BRR; +} GPIO_TypeDef; + +// --- Fake RCC_TypeDef (matches STM32F4 layout - need APB1ENR) --- +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t PLLCFGR; + __IO uint32_t CFGR; + __IO uint32_t CIR; + __IO uint32_t AHB1RSTR; + __IO uint32_t AHB2RSTR; + __IO uint32_t AHB3RSTR; + uint32_t RESERVED0; + __IO uint32_t APB1RSTR; + __IO uint32_t APB2RSTR; + uint32_t RESERVED1[2]; + __IO uint32_t AHB1ENR; + __IO uint32_t AHB2ENR; + __IO uint32_t AHB3ENR; + uint32_t RESERVED2; + __IO uint32_t APB1ENR; + __IO uint32_t APB2ENR; + uint32_t RESERVED3[2]; + __IO uint32_t AHB1LPENR; + __IO uint32_t AHB2LPENR; + __IO uint32_t AHB3LPENR; + uint32_t RESERVED4; + __IO uint32_t APB1LPENR; + __IO uint32_t APB2LPENR; + uint32_t RESERVED5[2]; + __IO uint32_t BDCR; + __IO uint32_t CSR; + uint32_t RESERVED6[2]; + __IO uint32_t SSCGR; + __IO uint32_t PLLI2SCFGR; +} RCC_TypeDef; + +// --- Fake CAN TX mailbox --- +typedef struct +{ + __IO uint32_t TIR; + __IO uint32_t TDTR; + __IO uint32_t TDLR; + __IO uint32_t TDHR; +} CAN_TxMailBox_TypeDef; + +// --- Fake CAN FIFO mailbox --- +typedef struct +{ + __IO uint32_t RIR; + __IO uint32_t RDTR; + __IO uint32_t RDLR; + __IO uint32_t RDHR; +} CAN_FIFOMailBox_TypeDef; + +// --- Fake CAN filter register --- +typedef struct +{ + __IO uint32_t FR1; + __IO uint32_t FR2; +} CAN_FilterRegister_TypeDef; + +// --- Fake CAN_TypeDef (matches STM32F4 layout) --- +// MSR.INAK (bit 0) must mirror MCR.INRQ (bit 0) for init mode busy-waits. +// We use a C++ struct with a getter-like mechanism: a helper function +// called before each test ensures MSR tracks MCR. +typedef struct +{ + __IO uint32_t MCR; + __IO uint32_t MSR; + __IO uint32_t TSR; + __IO uint32_t RF0R; + __IO uint32_t RF1R; + __IO uint32_t IER; + __IO uint32_t ESR; + __IO uint32_t BTR; + uint32_t RESERVED0[88]; + CAN_TxMailBox_TypeDef sTxMailBox[3]; + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; + uint32_t RESERVED1[12]; + __IO uint32_t FMR; + __IO uint32_t FM1R; + uint32_t RESERVED2; + __IO uint32_t FS1R; + uint32_t RESERVED3; + __IO uint32_t FFA1R; + uint32_t RESERVED4; + __IO uint32_t FA1R; + uint32_t RESERVED5[8]; + CAN_FilterRegister_TypeDef sFilterRegister[28]; +} CAN_TypeDef; + +// Hook: sync MSR.INAK to match MCR.INRQ after production code writes MCR. +// We patch this into the production code's enterInitMode/leaveInitMode by +// redefining the MSR register read to auto-sync from MCR first. +// CAN_MSR_INAK is bit 0, CAN_MCR_INRQ is bit 0 - same position. +// Override CAN1 macro +#define CAN1 (&fakeCan) + +// --- Static fake peripherals --- +static RCC_TypeDef fakeRcc; +static CAN_TypeDef fakeCan; +static GPIO_TypeDef fakeTxGpio; +static GPIO_TypeDef fakeRxGpio; + +// --- Override hardware macros to point at our fakes --- +#define RCC (&fakeRcc) +#define CAN1 (&fakeCan) + +// --- bxCAN register bit definitions (from stm32f413xx.h / stm32f4xx.h) --- + +// MCR bits +#define CAN_MCR_INRQ_Pos (0U) +#define CAN_MCR_INRQ (0x1UL << CAN_MCR_INRQ_Pos) +#define CAN_MCR_SLEEP_Pos (1U) +#define CAN_MCR_SLEEP (0x1UL << CAN_MCR_SLEEP_Pos) +#define CAN_MCR_TXFP_Pos (2U) +#define CAN_MCR_TXFP (0x1UL << CAN_MCR_TXFP_Pos) +#define CAN_MCR_RFLM_Pos (3U) +#define CAN_MCR_RFLM (0x1UL << CAN_MCR_RFLM_Pos) +#define CAN_MCR_NART_Pos (4U) +#define CAN_MCR_NART (0x1UL << CAN_MCR_NART_Pos) +#define CAN_MCR_AWUM_Pos (5U) +#define CAN_MCR_AWUM (0x1UL << CAN_MCR_AWUM_Pos) +#define CAN_MCR_ABOM_Pos (6U) +#define CAN_MCR_ABOM (0x1UL << CAN_MCR_ABOM_Pos) +#define CAN_MCR_TTCM_Pos (7U) +#define CAN_MCR_TTCM (0x1UL << CAN_MCR_TTCM_Pos) + +// MSR bits +#define CAN_MSR_INAK_Pos (0U) +#define CAN_MSR_INAK (0x1UL << CAN_MSR_INAK_Pos) + +// TSR bits +#define CAN_TSR_RQCP0_Pos (0U) +#define CAN_TSR_RQCP0 (0x1UL << CAN_TSR_RQCP0_Pos) +#define CAN_TSR_RQCP1_Pos (8U) +#define CAN_TSR_RQCP1 (0x1UL << CAN_TSR_RQCP1_Pos) +#define CAN_TSR_RQCP2_Pos (16U) +#define CAN_TSR_RQCP2 (0x1UL << CAN_TSR_RQCP2_Pos) +#define CAN_TSR_TME0_Pos (26U) +#define CAN_TSR_TME0 (0x1UL << CAN_TSR_TME0_Pos) +#define CAN_TSR_TME1_Pos (27U) +#define CAN_TSR_TME1 (0x1UL << CAN_TSR_TME1_Pos) +#define CAN_TSR_TME2_Pos (28U) +#define CAN_TSR_TME2 (0x1UL << CAN_TSR_TME2_Pos) + +// RF0R bits +#define CAN_RF0R_FMP0_Pos (0U) +#define CAN_RF0R_FMP0 (0x3UL << CAN_RF0R_FMP0_Pos) +#define CAN_RF0R_FOVR0_Pos (4U) +#define CAN_RF0R_FOVR0 (0x1UL << CAN_RF0R_FOVR0_Pos) +#define CAN_RF0R_RFOM0_Pos (5U) +#define CAN_RF0R_RFOM0 (0x1UL << CAN_RF0R_RFOM0_Pos) + +// IER bits +#define CAN_IER_TMEIE_Pos (0U) +#define CAN_IER_TMEIE (0x1UL << CAN_IER_TMEIE_Pos) +#define CAN_IER_FMPIE0_Pos (1U) +#define CAN_IER_FMPIE0 (0x1UL << CAN_IER_FMPIE0_Pos) + +// ESR bits +#define CAN_ESR_BOFF_Pos (2U) +#define CAN_ESR_BOFF (0x1UL << CAN_ESR_BOFF_Pos) +#define CAN_ESR_TEC_Pos (16U) +#define CAN_ESR_REC_Pos (24U) + +// BTR bits +#define CAN_BTR_BRP_Pos (0U) +#define CAN_BTR_TS1_Pos (16U) +#define CAN_BTR_TS2_Pos (20U) +#define CAN_BTR_SJW_Pos (24U) +#define CAN_BTR_SILM_Pos (31U) +#define CAN_BTR_SILM (0x1UL << CAN_BTR_SILM_Pos) +#define CAN_BTR_LBKM_Pos (30U) +#define CAN_BTR_LBKM (0x1UL << CAN_BTR_LBKM_Pos) + +// TIR bits (TX mailbox identifier register) +#define CAN_TI0R_TXRQ_Pos (0U) +#define CAN_TI0R_TXRQ (0x1UL << CAN_TI0R_TXRQ_Pos) +#define CAN_TI0R_RTR_Pos (1U) +#define CAN_TI0R_RTR (0x1UL << CAN_TI0R_RTR_Pos) +#define CAN_TI0R_IDE_Pos (2U) +#define CAN_TI0R_IDE (0x1UL << CAN_TI0R_IDE_Pos) +#define CAN_TI0R_EXID_Pos (3U) +#define CAN_TI0R_STID_Pos (21U) + +// RIR bits (RX mailbox identifier register) +#define CAN_RI0R_RTR_Pos (1U) +#define CAN_RI0R_RTR (0x1UL << CAN_RI0R_RTR_Pos) +#define CAN_RI0R_IDE_Pos (2U) +#define CAN_RI0R_IDE (0x1UL << CAN_RI0R_IDE_Pos) +#define CAN_RI0R_EXID_Pos (3U) +#define CAN_RI0R_STID_Pos (21U) + +// FMR bits +#define CAN_FMR_FINIT_Pos (0U) +#define CAN_FMR_FINIT (0x1UL << CAN_FMR_FINIT_Pos) + +// RCC APB1ENR +#define RCC_APB1ENR_CAN1EN_Pos (25U) +#define RCC_APB1ENR_CAN1EN (0x1UL << RCC_APB1ENR_CAN1EN_Pos) + +// Prevent the real mcu.h from being included +#define MCU_MCU_H +#define MCU_TYPEDEFS_H + +// Provide the CANFrame include path directly +#include + +// Now include the driver header - it will see our faked types +#include + +// Include the implementation directly so it compiles with our fakes. +#include + +#include + +class BxCanDeviceTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Zero all fake peripherals + memset(&fakeRcc, 0, sizeof(fakeRcc)); + memset(&fakeCan, 0, sizeof(fakeCan)); + memset(&fakeTxGpio, 0, sizeof(fakeTxGpio)); + memset(&fakeRxGpio, 0, sizeof(fakeRxGpio)); + + // The MSR.INAK bit: after setting MCR.INRQ, the hardware sets INAK. + // In our fake, writing to MCR doesn't auto-set MSR.INAK, so we + // pre-set it so enterInitMode's while-loop terminates immediately. + fakeCan.MSR = CAN_MSR_INAK; + } + + bios::BxCanDevice::Config makeDefaultConfig() + { + bios::BxCanDevice::Config cfg{}; + cfg.baseAddress = &fakeCan; + cfg.prescaler = 4U; // BRP = prescaler - 1 = 3 + cfg.bs1 = 13U; // TS1 + cfg.bs2 = 2U; // TS2 + cfg.sjw = 1U; // SJW + cfg.rxGpioPort = &fakeRxGpio; + cfg.rxPin = 11U; // PA11 (high pin, tests AFR[1] path) + cfg.rxAf = 9U; // AF9 + cfg.txGpioPort = &fakeTxGpio; + cfg.txPin = 5U; // PB5 (low pin, tests AFR[0] path) + cfg.txAf = 9U; // AF9 + return cfg; + } + + // Helper: put device into initialized state + std::unique_ptr makeInitedDevice() + { + auto cfg = makeDefaultConfig(); + auto dev = std::make_unique(cfg); + dev->init(); + return dev; + } + + // Helper: put device into initialized + started state + std::unique_ptr makeStartedDevice() + { + auto dev = makeInitedDevice(); + // Clear INAK so leaveInitMode succeeds + fakeCan.MSR &= ~CAN_MSR_INAK; + // Mark all TX mailboxes empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->start(); + return dev; + } + + // Helper: place a standard frame in RX FIFO0 + void placeRxFrameStd(uint32_t canId, uint8_t dlc, uint8_t const* data) + { + fakeCan.sFIFOMailBox[0].RIR = (canId << CAN_RI0R_STID_Pos); + fakeCan.sFIFOMailBox[0].RDTR = dlc & 0xFU; + if (data != nullptr) + { + fakeCan.sFIFOMailBox[0].RDLR = static_cast(data[0]) + | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) + | (static_cast(data[3]) << 24U); + fakeCan.sFIFOMailBox[0].RDHR = static_cast(data[4]) + | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) + | (static_cast(data[7]) << 24U); + } + } + + // Helper: place an extended frame in RX FIFO0 + void placeRxFrameExt(uint32_t canId, uint8_t dlc, uint8_t const* data) + { + fakeCan.sFIFOMailBox[0].RIR = ((canId & 0x1FFFFFFFU) << CAN_RI0R_EXID_Pos) | CAN_RI0R_IDE; + fakeCan.sFIFOMailBox[0].RDTR = dlc & 0xFU; + if (data != nullptr) + { + fakeCan.sFIFOMailBox[0].RDLR = static_cast(data[0]) + | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) + | (static_cast(data[3]) << 24U); + fakeCan.sFIFOMailBox[0].RDHR = static_cast(data[4]) + | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) + | (static_cast(data[7]) << 24U); + } + } + + // Simulate FIFO with N frames: set FMP0 to N, then on each RFOM write + // decrement. For simplicity, we use a counter pattern: set FMP0 = count, + // and the test hooks RFOM clearing to decrement. Since we can't hook writes + // to volatile, we simulate by setting FMP0 directly and calling receiveISR + // which reads FMP0, processes, sets RFOM (which ORs RF0R). We handle this + // by setting FMP0 = 1 for single-frame tests. + void setFifoCount(uint8_t count) + { + fakeCan.RF0R = (fakeCan.RF0R & ~CAN_RF0R_FMP0) | (count & 0x3U); + } + + // Helper: create a CANFrame with standard ID + ::can::CANFrame makeFrame(uint32_t id, uint8_t const* data, uint8_t dlc) + { + return ::can::CANFrame(id, data, dlc); + } + + // Helper: create a CANFrame with extended ID (sets bit 31) + ::can::CANFrame makeExtFrame(uint32_t rawId, uint8_t const* data, uint8_t dlc) + { + return ::can::CANFrame(rawId | 0x80000000U, data, dlc); + } + + // bxCAN hardware simulation: the driver loops on (RF0R & FMP0) and sets + // RFOM0 to release each frame. In real HW, RFOM0 auto-decrements FMP0. + // Our fake struct can't do this, so we spin a background thread that + // watches for RFOM0 being set and clears FMP0 accordingly. + + // Helper: receive exactly N frames via receiveISR with FIFO simulation. + // Spins a thread that simulates hardware FMP0 decrement on RFOM0 write. + uint8_t receiveFrames( + bios::BxCanDevice& dev, + uint8_t const* filter, + uint8_t const* data, + uint32_t const* ids, + bool const* extended, + uint8_t const* dlcs, + uint8_t totalFrames) + { + // For multi-frame, we'd need complex simulation. + // For simplicity in most tests, we use the single-frame helper. + // This function handles the general case with a background thread. + if (totalFrames == 0U) + { + fakeCan.RF0R = 0U; + return dev.receiveISR(filter); + } + + // We simulate by placing frame 0 and using a thread to decrement FMP0. + // Frame data is loaded from the arrays. + uint8_t frameIdx = 0U; + auto loadFrame = [&](uint8_t idx) + { + if (extended != nullptr && extended[idx]) + { + placeRxFrameExt(ids[idx], dlcs[idx], data + idx * 8U); + } + else + { + placeRxFrameStd(ids[idx], dlcs[idx], data + idx * 8U); + } + }; + + // Load first frame and set FMP0 + loadFrame(0); + fakeCan.RF0R = totalFrames; + + // Launch a thread that watches for RFOM0 and simulates HW behavior + std::atomic done{false}; + std::thread hwSim( + [&]() + { + uint8_t remaining = totalFrames; + while (!done.load(std::memory_order_relaxed)) + { + uint32_t rf0r = fakeCan.RF0R; + if ((rf0r & CAN_RF0R_RFOM0) != 0U) + { + remaining--; + if (remaining > 0U && (frameIdx + 1U) < totalFrames) + { + frameIdx++; + loadFrame(frameIdx); + } + // Clear RFOM0 and set new FMP0 + fakeCan.RF0R = remaining; + if (remaining == 0U) + { + done.store(true, std::memory_order_relaxed); + } + } + } + }); + + uint8_t result = dev.receiveISR(filter); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + return result; + } + + // Simplified single-frame receive helper + uint8_t receiveSingleFrame( + bios::BxCanDevice& dev, + uint32_t id, + bool isExtended, + uint8_t dlc, + uint8_t const* data, + uint8_t const* filter = nullptr) + { + if (isExtended) + { + placeRxFrameExt(id, dlc, data); + } + else + { + placeRxFrameStd(id, dlc, data); + } + fakeCan.RF0R = 1U; // 1 frame pending + + // Thread to simulate HW: when RFOM0 is set, clear FMP0 + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + + uint8_t result = dev.receiveISR(filter); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + return result; + } +}; + +TEST_F(BxCanDeviceTest, initEnablesPeripheralClock) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + + EXPECT_EQ(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, initConfiguresGpioTxAlternateFunctionLowPin) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + // TX pin = 5 (low register AFR[0]), AF9 + uint32_t txModer = (fakeTxGpio.MODER >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); // Alternate function mode + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (cfg.txPin * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); + + // Very high speed + uint32_t txSpeed = (fakeTxGpio.OSPEEDR >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txSpeed, 3U); +} + +TEST_F(BxCanDeviceTest, initConfiguresGpioRxAlternateFunctionHighPin) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + // RX pin = 11 (high register AFR[1]), AF9 + uint32_t rxModer = (fakeRxGpio.MODER >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxModer, 2U); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((cfg.rxPin - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); + + // Pull-up for RX + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); +} + +TEST_F(BxCanDeviceTest, initConfiguresGpioRxLowPin) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 4U; + cfg.rxAf = 7U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[0] >> (cfg.rxPin * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 7U); +} + +TEST_F(BxCanDeviceTest, initConfiguresGpioTxHighPin) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 12U; + cfg.txAf = 9U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[1] >> ((cfg.txPin - 8U) * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); +} + +TEST_F(BxCanDeviceTest, initEntersInitMode) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // After init(), INRQ should be set (we stay in init mode until start()) + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, initClearsSleepMode) +{ + fakeCan.MCR = CAN_MCR_SLEEP; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.MCR & CAN_MCR_SLEEP, 0U); +} + +TEST_F(BxCanDeviceTest, initEnablesAbomAndTxfp) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_ABOM, 0U); + EXPECT_NE(fakeCan.MCR & CAN_MCR_TXFP, 0U); +} + +TEST_F(BxCanDeviceTest, initConfiguresBitTiming) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + uint32_t brp = btr & 0x3FFU; + uint32_t ts1 = (btr >> CAN_BTR_TS1_Pos) & 0xFU; + uint32_t ts2 = (btr >> CAN_BTR_TS2_Pos) & 0x7U; + uint32_t sjw = (btr >> CAN_BTR_SJW_Pos) & 0x3U; + + EXPECT_EQ(brp, cfg.prescaler - 1U); + EXPECT_EQ(ts1, cfg.bs1 - 1U); + EXPECT_EQ(ts2, cfg.bs2 - 1U); + EXPECT_EQ(sjw, cfg.sjw - 1U); +} + +TEST_F(BxCanDeviceTest, initSetsInitializedFlag) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + // start() before init() should be a no-op (not initialized) + fakeCan.MSR &= ~CAN_MSR_INAK; + dev.start(); + // FMPIE0 should NOT be set since start() returns early + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Now init and start should work + fakeCan.MSR |= CAN_MSR_INAK; + dev.init(); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev.start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startLeavesInitMode) +{ + auto dev = makeInitedDevice(); + // leaveInitMode clears INRQ, then busy-waits for INAK=0 + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_EQ(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, startDrainsStaleFifo) +{ + auto dev = makeInitedDevice(); + // Simulate 1 stale frame in FIFO0 + fakeCan.RF0R = 1U; + fakeCan.MSR &= ~CAN_MSR_INAK; + + // start() should drain: while (FMP0 != 0) set RFOM0 + // In our fake, after one iteration RF0R = 1 | RFOM0 = 0x21, FMP0 still 1. + // This would loop forever. For this test, simulate HW clearing. + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + + dev->start(); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + + // After drain, FMPIE0 should be enabled + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startClearsOverrunFlag) +{ + auto dev = makeInitedDevice(); + fakeCan.MSR &= ~CAN_MSR_INAK; + fakeCan.RF0R = 0U; // No stale frames + dev->start(); + // FOVR0 should have been set (to clear it - write-1-to-clear) + EXPECT_NE(fakeCan.RF0R & CAN_RF0R_FOVR0, 0U); +} + +TEST_F(BxCanDeviceTest, startEnablesFmpie0) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopDisablesFmpie0AndTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; // Simulate TMEIE was enabled + fakeCan.MSR |= CAN_MSR_INAK; // So enterInitMode completes + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, stopEntersInitMode) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, transmitFindsEmptyMailbox0) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + + uint8_t data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + ::can::CANFrame frame(0x100, data, 8U); + EXPECT_TRUE(dev->transmit(frame)); + + // Should use mailbox 0 (first empty) + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, transmitStandardIdEncoding) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x7AB, data, 0U); + dev->transmit(frame); + + uint32_t stid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x7ABU); + // IDE bit should NOT be set for standard ID + EXPECT_EQ(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitExtendedIdEncoding) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + // Extended ID: set bit 31 in CANFrame ID per CanId convention + ::can::CANFrame frame(0x80012345U, data, 0U); + dev->transmit(frame); + + // IDE bit should be set + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); + // EXID field + uint32_t exid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_EXID_Pos) & 0x1FFFFFFFU; + EXPECT_EQ(exid, 0x12345U); +} + +TEST_F(BxCanDeviceTest, transmitDlcEncoding) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 5U); + dev->transmit(frame); + + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 5U); +} + +TEST_F(BxCanDeviceTest, transmitPayloadBytes) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22}; + ::can::CANFrame frame(0x100, data, 8U); + dev->transmit(frame); + + // TDLR: bytes 0-3 (LSB first) + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR), 0xAAU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR >> 8U), 0xBBU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR >> 16U), 0xCCU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR >> 24U), 0xDDU); + + // TDHR: bytes 4-7 + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDHR), 0xEEU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDHR >> 8U), 0xFFU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDHR >> 16U), 0x11U); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDHR >> 24U), 0x22U); +} + +TEST_F(BxCanDeviceTest, transmitSetsTxrq) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 1U); + dev->transmit(frame); + + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, transmitEnablesTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + fakeCan.IER &= ~CAN_IER_TMEIE; // Clear TMEIE first + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 1U); + dev->transmit(frame); + + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitAll3FullReturnsFalse) +{ + auto dev = makeStartedDevice(); + // No TME bits set = all mailboxes occupied + fakeCan.TSR = 0U; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 1U); + EXPECT_FALSE(dev->transmit(frame)); +} + +TEST_F(BxCanDeviceTest, transmitMailboxPriority012) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + ::can::CANFrame frame1(0x100, data, 1U); + ::can::CANFrame frame2(0x200, data, 1U); + ::can::CANFrame frame3(0x300, data, 1U); + + // All empty: first TX goes to mailbox 0 + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmit(frame1); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); + + // Only mailbox 1 and 2 empty: next TX goes to mailbox 1 + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmit(frame2); + EXPECT_NE(fakeCan.sTxMailBox[1].TIR & CAN_TI0R_TXRQ, 0U); + + // Only mailbox 2 empty: next TX goes to mailbox 2 + fakeCan.TSR = CAN_TSR_TME2; + dev->transmit(frame3); + EXPECT_NE(fakeCan.sTxMailBox[2].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, transmitZeroPayload) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 0U); +} + +TEST_F(BxCanDeviceTest, transmitMaxPayload8Bytes) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + ::can::CANFrame frame(0x100, data, 8U); + EXPECT_TRUE(dev->transmit(frame)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 8U); +} + +TEST_F(BxCanDeviceTest, transmitUsesMailbox1WhenOnly1Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1; // Only mailbox 1 empty + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x200, data, 1U); + EXPECT_TRUE(dev->transmit(frame)); + uint32_t stid = (fakeCan.sTxMailBox[1].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x200U); +} + +TEST_F(BxCanDeviceTest, receiveIsrNoFramesReturnsZero) +{ + auto dev = makeStartedDevice(); + fakeCan.RF0R = 0U; // No frames pending + uint8_t count = dev->receiveISR(nullptr); + EXPECT_EQ(count, 0U); +} + +TEST_F(BxCanDeviceTest, receiveIsrDrainsFifo0SingleFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80}; + uint8_t count = receiveSingleFrame(*dev, 0x123U, false, 8U, data); + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrStandardIdFromRir) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x456U, false, 1U, data); + + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x456U); +} + +TEST_F(BxCanDeviceTest, receiveIsrExtendedIdFromRir) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x1ABCDEFU, true, 1U, data); + + auto const& frame = dev->getRxFrame(0); + // Extended IDs have bit 31 set in the CANFrame ID + EXPECT_EQ(frame.getId(), 0x1ABCDEFU | 0x80000000U); +} + +TEST_F(BxCanDeviceTest, receiveIsrDlcFromRdtr) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 5U, data); + + EXPECT_EQ(dev->getRxFrame(0).getPayloadLength(), 5U); +} + +TEST_F(BxCanDeviceTest, receiveIsrPayloadByteOrder) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22}; + receiveSingleFrame(*dev, 0x100U, false, 8U, data); + + auto const& frame = dev->getRxFrame(0); + uint8_t const* payload = frame.getPayload(); + EXPECT_EQ(payload[0], 0xAAU); + EXPECT_EQ(payload[1], 0xBBU); + EXPECT_EQ(payload[2], 0xCCU); + EXPECT_EQ(payload[3], 0xDDU); + EXPECT_EQ(payload[4], 0xEEU); + EXPECT_EQ(payload[5], 0xFFU); + EXPECT_EQ(payload[6], 0x11U); + EXPECT_EQ(payload[7], 0x22U); +} + +TEST_F(BxCanDeviceTest, receiveIsrReleasesRfom) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + // After receive, RF0R should have been written with RFOM0 + // (our HW sim cleared it, but the driver wrote it) + // We verify by checking that the frame was received successfully + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrFrameCountReturn) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t count = receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrQueueFullDropsFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + + // Fill the queue to capacity (32 frames) + for (uint8_t i = 0U; i < 32U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + + // 33rd frame: should be dropped but FIFO still released + // Set up frame and FMP0=1 + placeRxFrameStd(0x200U, 1U, data); + fakeCan.RF0R = 1U; + + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + + uint8_t count = dev->receiveISR(nullptr); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + + // Frame was dropped (queue still 32, not 33) + EXPECT_EQ(dev->getRxCount(), 32U); + EXPECT_EQ(count, 0U); // No frames enqueued +} + +TEST_F(BxCanDeviceTest, receiveIsrNullFilterAcceptsAll) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t count = receiveSingleFrame(*dev, 0x7FFU, false, 1U, data, nullptr); + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrBitfieldFilterAccept) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Create filter that accepts ID 0x100 (byte 32, bit 0) + uint8_t filter[256] = {}; + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); // Set bit for ID 0x100 + + uint8_t count = receiveSingleFrame(*dev, 0x100U, false, 1U, data, filter); + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, receiveIsrBitfieldFilterReject) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Create filter that does NOT accept ID 0x100 + uint8_t filter[256] = {}; + // filter[0x100/8] bit for 0x100 is NOT set + + uint8_t count = receiveSingleFrame(*dev, 0x100U, false, 1U, data, filter); + EXPECT_EQ(count, 0U); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, receiveIsrMultipleFramesDrain) +{ + auto dev = makeStartedDevice(); + uint8_t data1[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + uint8_t data2[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x01, 0x02}; + + // Receive first frame + receiveSingleFrame(*dev, 0x100U, false, 8U, data1); + // Receive second frame + receiveSingleFrame(*dev, 0x200U, false, 8U, data2); + + EXPECT_EQ(dev->getRxCount(), 2U); + + auto const& f1 = dev->getRxFrame(0); + EXPECT_EQ(f1.getId(), 0x100U); + EXPECT_EQ(f1.getPayload()[0], 0x11U); + + auto const& f2 = dev->getRxFrame(1); + EXPECT_EQ(f2.getId(), 0x200U); + EXPECT_EQ(f2.getPayload()[0], 0xAAU); +} + +TEST_F(BxCanDeviceTest, receiveIsrStaleFrameDrain) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Receive a frame, then clear, then receive again + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + receiveSingleFrame(*dev, 0x200U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(BxCanDeviceTest, transmitIsrClearsRqcp0) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_RQCP0; + dev->transmitISR(); + // RQCP0 should be set (write-1-to-clear in real HW, but in fake it ORs) + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrClearsRqcp1) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_RQCP1; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrClearsRqcp2) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_RQCP2; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrClearsAllRqcpFlags) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + // All RQCP flags should be written (ORed in) + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrDisablesTmeieWhenAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // All 3 mailboxes empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieWhenNotAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // Only mailbox 0 empty, 1 and 2 still pending + fakeCan.TSR = CAN_TSR_TME0; + dev->transmitISR(); + // After ORing RQCP flags, TME0 is still set but TME1/TME2 are not + // So (TSR & (TME0|TME1|TME2)) != (TME0|TME1|TME2), TMEIE stays enabled + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, rxQueueInitiallyZero) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, rxQueueCorrectFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE}; + receiveSingleFrame(*dev, 0x321U, false, 8U, data); + + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x321U); + EXPECT_EQ(frame.getPayloadLength(), 8U); + EXPECT_EQ(frame.getPayload()[0], 0xDEU); + EXPECT_EQ(frame.getPayload()[7], 0xBEU); +} + +TEST_F(BxCanDeviceTest, rxQueueCountAfterReceive) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + receiveSingleFrame(*dev, 0x101U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 2U); +} + +TEST_F(BxCanDeviceTest, rxQueueClearResets) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + receiveSingleFrame(*dev, 0x101U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 2U); + + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, rxQueueWrapAround) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill queue partially, clear, fill again to cause wrap-around + for (uint8_t i = 0U; i < 30U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + // Now head is at 30. Fill 10 more to wrap around past 32 + for (uint8_t i = 0U; i < 10U; i++) + { + data[0] = i + 1U; + receiveSingleFrame(*dev, 0x200U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 10U); + + // Verify first and last frame + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(9).getId(), 0x209U); +} + +TEST_F(BxCanDeviceTest, rxQueueCapacity32) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0U; i < 32U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + + // Verify all frames are accessible + for (uint8_t i = 0U; i < 32U; i++) + { + EXPECT_EQ(dev->getRxFrame(i).getId(), 0x100U + i); + } +} + +TEST_F(BxCanDeviceTest, disableRxInterruptClearsFmpie0) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); // Enabled after start + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptSetsFmpie0) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, disableRxInterruptPreservesOtherBits) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; // Set another bit + dev->disableRxInterrupt(); + // TMEIE should still be set + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptPreservesOtherBits) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = CAN_IER_TMEIE; // Only TMEIE set, FMPIE0 cleared + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, isBusOffReadsBoffBit) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_FALSE(dev->isBusOff()); + + fakeCan.ESR = CAN_ESR_BOFF; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, isBusOffFalseWhenNotSet) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, getTxErrorCounterReadsEsr) +{ + auto dev = makeStartedDevice(); + // TEC is bits [23:16] of ESR + fakeCan.ESR = (128U << CAN_ESR_TEC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 128U); +} + +TEST_F(BxCanDeviceTest, getRxErrorCounterReadsEsr) +{ + auto dev = makeStartedDevice(); + // REC is bits [31:24] of ESR + fakeCan.ESR = (64U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 64U); +} + +TEST_F(BxCanDeviceTest, errorCounterMaxValue255) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (255U << CAN_ESR_TEC_Pos) | (255U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 255U); + EXPECT_EQ(dev->getRxErrorCounter(), 255U); +} + +TEST_F(BxCanDeviceTest, errorCounterZero) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterEntersFilterInitMode) +{ + auto dev = makeInitedDevice(); + // After init(), configureAcceptAllFilter was called. + // FINIT should be cleared (we left filter init mode) + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterBank0MaskMode) +{ + auto dev = makeInitedDevice(); + // Bank 0 should be in mask mode (FM1R bit 0 = 0) + EXPECT_EQ(fakeCan.FM1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterBank032BitScale) +{ + auto dev = makeInitedDevice(); + // Bank 0 should be 32-bit scale (FS1R bit 0 = 1) + EXPECT_NE(fakeCan.FS1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterBank0AllPass) +{ + auto dev = makeInitedDevice(); + // FR1 = 0 (ID = 0, don't care), FR2 = 0 (mask = 0, accept all) + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterBank0Active) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterAssignedToFifo0) +{ + auto dev = makeInitedDevice(); + // FFA1R bit 0 = 0 means FIFO0 + EXPECT_EQ(fakeCan.FFA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListModeConfigures) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U, 0x400U}; + dev->configureFilterList(ids, 4U); + + // FINIT should be cleared after configuration + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); + + // Banks 0 and 1 should be in list mode (FM1R bits set) + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); + EXPECT_NE(fakeCan.FM1R & (1U << 1U), 0U); + + // Bank 0: FR1 = 0x100 << STID_Pos, FR2 = 0x200 << STID_Pos + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x100U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x200U << CAN_RI0R_STID_Pos); + + // Bank 1: FR1 = 0x300 << STID_Pos, FR2 = 0x400 << STID_Pos + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x300U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x400U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListModeOddCountDuplicatesLast) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U}; + dev->configureFilterList(ids, 3U); + + // Bank 0: FR1 = 0x100, FR2 = 0x200 + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x100U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x200U << CAN_RI0R_STID_Pos); + + // Bank 1: FR1 = 0x300, FR2 = 0x300 (duplicated) + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x300U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x300U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, bitTimingPrescalerEncoding) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 8U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t brp = fakeCan.BTR & 0x3FFU; + EXPECT_EQ(brp, 7U); // prescaler - 1 +} + +TEST_F(BxCanDeviceTest, bitTimingBs1Encoding) +{ + auto cfg = makeDefaultConfig(); + cfg.bs1 = 15U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t ts1 = (fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU; + EXPECT_EQ(ts1, 14U); // bs1 - 1 +} + +TEST_F(BxCanDeviceTest, bitTimingBs2Encoding) +{ + auto cfg = makeDefaultConfig(); + cfg.bs2 = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t ts2 = (fakeCan.BTR >> CAN_BTR_TS2_Pos) & 0x7U; + EXPECT_EQ(ts2, 3U); // bs2 - 1 +} + +TEST_F(BxCanDeviceTest, bitTimingSjwEncoding) +{ + auto cfg = makeDefaultConfig(); + cfg.sjw = 2U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t sjw = (fakeCan.BTR >> CAN_BTR_SJW_Pos) & 0x3U; + EXPECT_EQ(sjw, 1U); // sjw - 1 +} + +TEST_F(BxCanDeviceTest, bitTimingMinValues) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + cfg.bs1 = 1U; + cfg.bs2 = 1U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 0U); // BRP = 0 + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 0U); // TS1 = 0 + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 0U); // TS2 = 0 + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 0U); // SJW = 0 +} + +TEST_F(BxCanDeviceTest, bitTimingCan500kbpsAt42MHz) +{ + // Typical config: 42 MHz APB1, 500 kbps: prescaler=6, BS1=11, BS2=2, SJW=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 6U; + cfg.bs1 = 11U; + cfg.bs2 = 2U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 5U); // BRP = 5 + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 10U); // TS1 = 10 + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 1U); // TS2 = 1 + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 0U); // SJW = 0 +} + +TEST_F(BxCanDeviceTest, doubleInitDoesNotCrash) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // Second init should work without issues + dev.init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, stopRestart) +{ + auto dev = makeStartedDevice(); + + // Stop + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Restart + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitBeforeInitStillTransmits) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + // Don't call init() or start() + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 1U); + // transmit() does not gate on fInitialized - it only checks TME bits + EXPECT_TRUE(dev.transmit(frame)); +} + +TEST_F(BxCanDeviceTest, startBeforeInitIsNoop) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + // start() without init() should return early + fakeCan.MSR &= ~CAN_MSR_INAK; + dev.start(); + // FMPIE0 should NOT be set + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, extendedIdFullRange) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + // Max 29-bit extended ID + uint8_t data[8] = {}; + ::can::CANFrame frame(0x9FFFFFFFU, data, 0U); // 0x80000000 | 0x1FFFFFFF + dev->transmit(frame); + + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); + uint32_t exid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_EXID_Pos) & 0x1FFFFFFFU; + EXPECT_EQ(exid, 0x1FFFFFFFU); +} + +TEST_F(BxCanDeviceTest, extendedIdMinRange) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x80000000U, data, 0U); // Extended ID = 0 + dev->transmit(frame); + + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); + uint32_t exid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_EXID_Pos) & 0x1FFFFFFFU; + EXPECT_EQ(exid, 0U); +} + +TEST_F(BxCanDeviceTest, filterIdBoundary0x000) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t filter[256] = {}; + filter[0] |= 1U; // Accept ID 0 + + uint8_t count = receiveSingleFrame(*dev, 0x000U, false, 1U, data, filter); + EXPECT_EQ(count, 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0U); +} + +TEST_F(BxCanDeviceTest, filterIdBoundary0x7FF) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t filter[256] = {}; + filter[0x7FF / 8U] |= (1U << (0x7FF % 8U)); // Accept ID 0x7FF + + uint8_t count = receiveSingleFrame(*dev, 0x7FFU, false, 1U, data, filter); + EXPECT_EQ(count, 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x7FFU); +} + +TEST_F(BxCanDeviceTest, multipleReceiveCycles) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Cycle 1: receive and clear + for (uint8_t i = 0U; i < 5U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 5U); + dev->clearRxQueue(); + + // Cycle 2: receive and clear + for (uint8_t i = 0U; i < 3U; i++) + { + receiveSingleFrame(*dev, 0x200U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + dev->clearRxQueue(); + + // Cycle 3: verify empty + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, concurrentTxMailboxes) +{ + auto dev = makeStartedDevice(); + uint8_t data1[8] = {0x01}; + uint8_t data2[8] = {0x02}; + uint8_t data3[8] = {0x03}; + + // Fill all 3 mailboxes + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data1, 1U))); + + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x200U, data2, 1U))); + + fakeCan.TSR = CAN_TSR_TME2; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x300U, data3, 1U))); + + // All full now + fakeCan.TSR = 0U; + EXPECT_FALSE(dev->transmit(::can::CANFrame(0x400U, data1, 1U))); + + // Verify each mailbox has correct ID + uint32_t id0 = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + uint32_t id1 = (fakeCan.sTxMailBox[1].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + uint32_t id2 = (fakeCan.sTxMailBox[2].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(id0, 0x100U); + EXPECT_EQ(id1, 0x200U); + EXPECT_EQ(id2, 0x300U); + + // Verify each mailbox has correct data byte 0 + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[0].TDLR), 0x01U); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[1].TDLR), 0x02U); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[2].TDLR), 0x03U); +} + +TEST_F(BxCanDeviceTest, filterListSingleId) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x555U}; + dev->configureFilterList(ids, 1U); + + // Bank 0: FR1 = 0x555 << STID, FR2 = 0x555 << STID (duplicated for odd) + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x555U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x555U << CAN_RI0R_STID_Pos); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); // Activated +} + +TEST_F(BxCanDeviceTest, filterListBanksActivated) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U, 0x400U, 0x500U, 0x600U}; + dev->configureFilterList(ids, 6U); + + // 3 banks should be activated (6 IDs / 2 per bank) + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 1U), 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 2U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListAllAssignedToFifo0) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U, 0x400U}; + dev->configureFilterList(ids, 4U); + + EXPECT_EQ(fakeCan.FFA1R & (1U << 0U), 0U); + EXPECT_EQ(fakeCan.FFA1R & (1U << 1U), 0U); +} + +TEST_F(BxCanDeviceTest, receiveIsrExtendedIdRecovery) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x42}; + + // Receive extended ID + receiveSingleFrame(*dev, 0x12345U, true, 1U, data); + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x12345U | 0x80000000U); + EXPECT_EQ(frame.getPayload()[0], 0x42U); +} + +TEST_F(BxCanDeviceTest, transmitIsrThenTransmitAgain) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Transmit first frame + fakeCan.TSR = CAN_TSR_TME0; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + + // Simulate TX complete: all mailboxes empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); // TMEIE disabled + + // Transmit again: TMEIE should be re-enabled + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x200U, data, 1U))); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, getRxFrameIndexWraps) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill 31 frames, clear, then add 2 more (head at 31) + for (uint8_t i = 0U; i < 31U; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + // Head is now at 31. Add 2 frames -> indices 31 and 0 (wrapped) + receiveSingleFrame(*dev, 0x500U, false, 1U, data); + receiveSingleFrame(*dev, 0x501U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 2U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x500U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x501U); +} + +TEST_F(BxCanDeviceTest, isBusOffWithOtherEsrBitsSet) +{ + auto dev = makeStartedDevice(); + // Set TEC and REC but NOT BOFF + fakeCan.ESR = (100U << CAN_ESR_TEC_Pos) | (50U << CAN_ESR_REC_Pos); + EXPECT_FALSE(dev->isBusOff()); + + // Now set BOFF too + fakeCan.ESR |= CAN_ESR_BOFF; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, transmitStandardIdZero) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x000U, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t stid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0U); + EXPECT_EQ(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitStandardIdMax0x7FF) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x7FFU, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t stid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x7FFU); +} + +TEST_F(BxCanDeviceTest, clockEnableIsIdempotent) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t apb1_1 = fakeRcc.APB1ENR; + dev.init(); + uint32_t apb1_2 = fakeRcc.APB1ENR; + // Should still have CAN1EN set, no extra bits + EXPECT_EQ(apb1_1, apb1_2); +} + +TEST_F(BxCanDeviceTest, receiveIsrZeroDlcFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 0U, data); + + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getPayloadLength(), 0U); +} + +TEST_F(BxCanDeviceTest, receiveIsrMaxDlc8Frame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + receiveSingleFrame(*dev, 0x100U, false, 8U, data); + + EXPECT_EQ(dev->getRxFrame(0).getPayloadLength(), 8U); +} + +TEST_F(BxCanDeviceTest, clearRxQueueAdvancesHead) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Receive 5 frames, clear, then receive 3 more + for (uint8_t i = 0; i < 5; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + for (uint8_t i = 0; i < 3; i++) + { + data[0] = 0xA0U + i; + receiveSingleFrame(*dev, 0x300U + i, false, 1U, data); + } + + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x300U); + EXPECT_EQ(dev->getRxFrame(0).getPayload()[0], 0xA0U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x302U); +} + +TEST_F(BxCanDeviceTest, filterListMaxBanks14) +{ + auto dev = makeInitedDevice(); + // 28 IDs = 14 banks (max for configureFilterList) + uint32_t ids[28]; + for (uint8_t i = 0; i < 28; i++) + { + ids[i] = 0x100U + i; + } + dev->configureFilterList(ids, 28U); + + // All 14 banks should be activated + for (uint8_t b = 0; b < 14; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b << " not active"; + } +} + +TEST_F(BxCanDeviceTest, transmitToMailbox2Only) +{ + auto dev = makeStartedDevice(); + // Only mailbox 2 is empty + fakeCan.TSR = CAN_TSR_TME2; + + uint8_t data[8] = {0xFF}; + ::can::CANFrame frame(0x3FFU, data, 1U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t stid = (fakeCan.sTxMailBox[2].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x3FFU); + EXPECT_EQ(static_cast(fakeCan.sTxMailBox[2].TDLR), 0xFFU); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFinitToggle) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + + // Before init, FMR should be 0 + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); + + dev.init(); + + // After init (which calls configureAcceptAllFilter), FINIT should be cleared + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); +} + +TEST_F(BxCanDeviceTest, disableEnableRxInterruptCycle) +{ + auto dev = makeStartedDevice(); + + // Initially enabled after start + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Disable-enable-disable cycle + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, errorCounterIndependentTecRec) +{ + auto dev = makeStartedDevice(); + // Set TEC=200, REC=50 + fakeCan.ESR = (200U << CAN_ESR_TEC_Pos) | (50U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 200U); + EXPECT_EQ(dev->getRxErrorCounter(), 50U); + + // Change to TEC=10, REC=250 + fakeCan.ESR = (10U << CAN_ESR_TEC_Pos) | (250U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 10U); + EXPECT_EQ(dev->getRxErrorCounter(), 250U); +} + +TEST_F(BxCanDeviceTest, receiveMultipleThenTransmit) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x42}; + + // Receive 3 frames + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + receiveSingleFrame(*dev, 0x101U, false, 1U, data); + receiveSingleFrame(*dev, 0x102U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 3U); + + // Transmit should still work independently + fakeCan.TSR = CAN_TSR_TME0; + ::can::CANFrame txFrame(0x200U, data, 1U); + EXPECT_TRUE(dev->transmit(txFrame)); + + // RX queue unaffected + EXPECT_EQ(dev->getRxCount(), 3U); +} + +TEST_F(BxCanDeviceTest, initDoubleInitPreservesBtrSettings) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 5U; + cfg.bs1 = 10U; + cfg.bs2 = 3U; + cfg.sjw = 2U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t btr1 = fakeCan.BTR; + dev.init(); + uint32_t btr2 = fakeCan.BTR; + EXPECT_EQ(btr1, btr2); +} + +TEST_F(BxCanDeviceTest, initBtrPrescaler1) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 0U); +} + +TEST_F(BxCanDeviceTest, initBtrPrescaler1024) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1024U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 1023U); +} + +TEST_F(BxCanDeviceTest, initBtrBs1Max16) +{ + auto cfg = makeDefaultConfig(); + cfg.bs1 = 16U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts1 = (fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU; + EXPECT_EQ(ts1, 15U); +} + +TEST_F(BxCanDeviceTest, initBtrBs2Max8) +{ + auto cfg = makeDefaultConfig(); + cfg.bs2 = 8U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts2 = (fakeCan.BTR >> CAN_BTR_TS2_Pos) & 0x7U; + EXPECT_EQ(ts2, 7U); +} + +TEST_F(BxCanDeviceTest, initBtrSjwMax4) +{ + auto cfg = makeDefaultConfig(); + cfg.sjw = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t sjw = (fakeCan.BTR >> CAN_BTR_SJW_Pos) & 0x3U; + EXPECT_EQ(sjw, 3U); +} + +TEST_F(BxCanDeviceTest, initGpioTxPin0LowAf) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 0U; + cfg.txAf = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (0U * 4U)) & 0xFU; + EXPECT_EQ(txAf, 4U); + uint32_t txModer = (fakeTxGpio.MODER >> (0U * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); +} + +TEST_F(BxCanDeviceTest, initGpioTxPin7BoundaryLow) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 7U; + cfg.txAf = 11U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (7U * 4U)) & 0xFU; + EXPECT_EQ(txAf, 11U); +} + +TEST_F(BxCanDeviceTest, initGpioRxPin8BoundaryHigh) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 8U; + cfg.rxAf = 5U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((8U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 5U); +} + +TEST_F(BxCanDeviceTest, initGpioRxPin15Max) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 15U; + cfg.rxAf = 9U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((15U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); +} + +TEST_F(BxCanDeviceTest, initClearsSleepModeWhenAlreadyClear) +{ + fakeCan.MCR = 0U; // SLEEP already clear + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.MCR & CAN_MCR_SLEEP, 0U); +} + +TEST_F(BxCanDeviceTest, initAbomFlagSetAfterInit) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_ABOM, 0U); +} + +TEST_F(BxCanDeviceTest, initTxfpFlagSetAfterInit) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_TXFP, 0U); +} + +TEST_F(BxCanDeviceTest, initClockEnableBitPosition25) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 25U), 0U); +} + +TEST_F(BxCanDeviceTest, initClockDoesNotAffectOtherApb1Bits) +{ + fakeRcc.APB1ENR = 0x12345678U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // Other bits should still be set + EXPECT_NE(fakeRcc.APB1ENR & 0x12345678U, 0U); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, initInrqSetBeforeLeaveInit) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // After init we are still in init mode (INRQ set) + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, initGpioTxSpeedVeryHigh) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t txSpeed = (fakeTxGpio.OSPEEDR >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txSpeed, 3U); +} + +TEST_F(BxCanDeviceTest, initGpioRxPullUp) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); +} + +TEST_F(BxCanDeviceTest, initBtrFieldsDoNotOverlap) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 512U; + cfg.bs1 = 16U; + cfg.bs2 = 8U; + cfg.sjw = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + uint32_t brp = btr & 0x3FFU; + uint32_t ts1 = (btr >> CAN_BTR_TS1_Pos) & 0xFU; + uint32_t ts2 = (btr >> CAN_BTR_TS2_Pos) & 0x7U; + uint32_t sjw = (btr >> CAN_BTR_SJW_Pos) & 0x3U; + EXPECT_EQ(brp, 511U); + EXPECT_EQ(ts1, 15U); + EXPECT_EQ(ts2, 7U); + EXPECT_EQ(sjw, 3U); +} + +TEST_F(BxCanDeviceTest, initFilterConfiguredAcceptAll) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // Accept-all filter should be configured after init + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, startWithoutInitDoesNotSetFmpie0) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev.start(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, doubleStartDoesNotCrash) +{ + auto dev = makeStartedDevice(); + // Second start: re-enter init mode briefly, then leave again + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopThenRestartClearsAndReenablesFmpie0) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startClearsInrq) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_EQ(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, stopSetsInrq) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); +} + +TEST_F(BxCanDeviceTest, startClearsFovrFlag) +{ + auto dev = makeInitedDevice(); + fakeCan.MSR &= ~CAN_MSR_INAK; + fakeCan.RF0R = 0U; + dev->start(); + // FOVR0 should have been written (write-1-to-clear) + EXPECT_NE(fakeCan.RF0R & CAN_RF0R_FOVR0, 0U); +} + +TEST_F(BxCanDeviceTest, startNoStaleFifoFrames) +{ + auto dev = makeInitedDevice(); + fakeCan.RF0R = 0U; // No stale frames + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopDisablesTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, stopDisablesFmpie0) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopThenStartTwice) +{ + auto dev = makeStartedDevice(); + + // Stop + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // Start again + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Stop again + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + + // Start once more + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startPreservesAbomFlag) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_ABOM, 0U); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + // ABOM should still be set after leaving init mode + EXPECT_NE(fakeCan.MCR & CAN_MCR_ABOM, 0U); +} + +TEST_F(BxCanDeviceTest, startPreservesTxfpFlag) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_TXFP, 0U); + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_TXFP, 0U); +} + +TEST_F(BxCanDeviceTest, stopThenInitThenStart) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // Re-init + dev->init(); + EXPECT_NE(fakeCan.MCR & CAN_MCR_INRQ, 0U); + + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, startIerOnlyFmpie0Set) +{ + auto dev = makeInitedDevice(); + fakeCan.IER = 0U; + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + // After start, only FMPIE0 should be enabled (not TMEIE) + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, stopClearsIerCompletely) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.IER & (CAN_IER_FMPIE0 | CAN_IER_TMEIE), 0U); +} + +TEST_F(BxCanDeviceTest, startAfterStopRxQueuePreserved) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // RX queue should still have the frame + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + + fakeCan.MSR &= ~CAN_MSR_INAK; + dev->start(); + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(BxCanDeviceTest, startSetsMailboxesEmptyForSubsequentTx) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); +} + +TEST_F(BxCanDeviceTest, stopDoesNotAffectBtrRegister) +{ + auto dev = makeStartedDevice(); + uint32_t btrBefore = fakeCan.BTR; + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + EXPECT_EQ(fakeCan.BTR, btrBefore); +} + +TEST_F(BxCanDeviceTest, startWithFifo2StaleFrames) +{ + auto dev = makeInitedDevice(); + fakeCan.RF0R = 2U; // 2 stale frames + fakeCan.MSR &= ~CAN_MSR_INAK; + + // Background thread to simulate HW draining + std::atomic done{false}; + std::thread hwSim( + [&]() + { + uint8_t remaining = 2U; + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + remaining--; + fakeCan.RF0R = remaining; + if (remaining == 0U) + { + done.store(true, std::memory_order_relaxed); + return; + } + } + } + }); + + dev->start(); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, stopPreservesFilterConfiguration) +{ + auto dev = makeStartedDevice(); + // Check filter is configured after init/start + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); + uint32_t fa1rBefore = fakeCan.FA1R; + + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // Filter config should not be touched by stop + EXPECT_EQ(fakeCan.FA1R, fa1rBefore); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptFromZeroIer) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = 0U; + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, disableRxInterruptIdempotent) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptIdempotent) +{ + auto dev = makeStartedDevice(); + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitEnablesTmeieFromCleanIer) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = CAN_IER_FMPIE0; // Only FMPIE0 set + fakeCan.TSR = CAN_TSR_TME0; + + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + // FMPIE0 should still be set + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrDisablesTmeieAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieMailbox0Busy) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // Mailbox 0 busy, 1 and 2 empty + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieMailbox1Busy) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // Mailbox 1 busy, 0 and 2 empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieMailbox2Busy) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + // Mailbox 2 busy, 0 and 1 empty + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieAllBusy) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = 0U; // All mailboxes busy + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieOnlyMailbox0Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieOnlyMailbox1Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME1; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrKeepsTmeieOnlyMailbox2Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, disableRxInterruptDoesNotAffectTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + dev->disableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, enableRxInterruptDoesNotAffectTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = CAN_IER_TMEIE; // Only TMEIE set + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrDoesNotAffectFmpie0) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + // FMPIE0 should still be set + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, ierBitIsolationFmpie0Only) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = 0U; + dev->enableRxInterrupt(); + // Only FMPIE0 (bit 1) should be set + EXPECT_EQ(fakeCan.IER, CAN_IER_FMPIE0); +} + +TEST_F(BxCanDeviceTest, ierBitIsolationTmeieFromTransmit) +{ + auto dev = makeStartedDevice(); + fakeCan.IER = 0U; + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + // TMEIE (bit 0) should be set + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, multipleTxThenIsrCycleIerState) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // TX -> TMEIE enabled + fakeCan.TSR = CAN_TSR_TME0; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + + // ISR with all empty -> TMEIE disabled + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); + + // TX again -> TMEIE re-enabled + fakeCan.TSR = CAN_TSR_TME1; + dev->transmit(::can::CANFrame(0x200U, data, 1U)); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); + + // ISR with all empty again -> TMEIE disabled + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, disableRxInterruptThenReceiveDoesNotEnqueue) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + // Manually call receiveISR (would normally be called by HW interrupt) + uint8_t data[8] = {}; + // receiveISR can still be called manually, it does not check IER + placeRxFrameStd(0x100U, 1U, data); + fakeCan.RF0R = 1U; + + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + + uint8_t count = dev->receiveISR(nullptr); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + // receiveISR still works even if FMPIE0 disabled (just won't be called by HW) + EXPECT_EQ(count, 1U); +} + +TEST_F(BxCanDeviceTest, enableDisableRxInterruptRapidToggle) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 10; i++) + { + dev->disableRxInterrupt(); + EXPECT_EQ(fakeCan.IER & CAN_IER_FMPIE0, 0U); + dev->enableRxInterrupt(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + } +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFs1rBit0Set) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.FS1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFm1rBit0Clear) +{ + auto dev = makeInitedDevice(); + EXPECT_EQ(fakeCan.FM1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFfa1rBit0Clear) +{ + auto dev = makeInitedDevice(); + EXPECT_EQ(fakeCan.FFA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFa1rBit0Set) +{ + auto dev = makeInitedDevice(); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterFinitClearedAfterConfig) +{ + auto dev = makeInitedDevice(); + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); +} + +TEST_F(BxCanDeviceTest, filterListMode2Ids) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x100U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x200U << CAN_RI0R_STID_Pos); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListMode1IdDuplicatesInBank) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x7FFU}; + dev->configureFilterList(ids, 1U); + + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x7FFU << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x7FFU << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListMode3IdsOdd) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x10U, 0x20U, 0x30U}; + dev->configureFilterList(ids, 3U); + + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x10U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x20U << CAN_RI0R_STID_Pos); + // Bank 1: odd count -> last ID duplicated + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x30U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x30U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListMode5IdsOdd) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x10U, 0x20U, 0x30U, 0x40U, 0x50U}; + dev->configureFilterList(ids, 5U); + + // Bank 0: IDs 0 and 1 + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x10U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x20U << CAN_RI0R_STID_Pos); + // Bank 1: IDs 2 and 3 + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x30U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x40U << CAN_RI0R_STID_Pos); + // Bank 2: ID 4 duplicated + EXPECT_EQ(fakeCan.sFilterRegister[2].FR1, 0x50U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[2].FR2, 0x50U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListMode6Ids3Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U, 0x300U, 0x400U, 0x500U, 0x600U}; + dev->configureFilterList(ids, 6U); + + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 1U), 0U); + EXPECT_NE(fakeCan.FA1R & (1U << 2U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListMode8Ids4Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x10U, 0x20U, 0x30U, 0x40U, 0x50U, 0x60U, 0x70U, 0x80U}; + dev->configureFilterList(ids, 8U); + + for (uint8_t b = 0; b < 4; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + } + EXPECT_EQ(fakeCan.sFilterRegister[3].FR1, 0x70U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[3].FR2, 0x80U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListMode14Ids7Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[14]; + for (uint8_t i = 0; i < 14; i++) + { + ids[i] = 0x100U + i; + } + dev->configureFilterList(ids, 14U); + + for (uint8_t b = 0; b < 7; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + } +} + +TEST_F(BxCanDeviceTest, filterListMode28IdsMax14Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[28]; + for (uint8_t i = 0; i < 28; i++) + { + ids[i] = 0x10U + i; + } + dev->configureFilterList(ids, 28U); + + for (uint8_t b = 0; b < 14; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + EXPECT_NE(fakeCan.FM1R & (1U << b), 0U) << "Bank " << (int)b << " list mode"; + } +} + +TEST_F(BxCanDeviceTest, filterListFinitClearedAfterConfig) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_EQ(fakeCan.FMR & CAN_FMR_FINIT, 0U); +} + +TEST_F(BxCanDeviceTest, filterListFs1r32BitScale) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_NE(fakeCan.FS1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListFm1rListMode) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListFfa1rFifo0Assignment) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_EQ(fakeCan.FFA1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, filterListIdZero) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x000U, 0x000U}; + dev->configureFilterList(ids, 2U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x000U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x000U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListIdMax0x7FF) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x7FFU, 0x7FFU}; + dev->configureFilterList(ids, 2U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x7FFU << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x7FFU << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListOverwritesPreviousAcceptAll) +{ + auto dev = makeInitedDevice(); + // After init, accept-all filter is configured (mask mode) + EXPECT_EQ(fakeCan.FM1R & (1U << 0U), 0U); // Mask mode + + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + // Now should be list mode + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); +} + +TEST_F(BxCanDeviceTest, acceptAllFilterOverwritesPreviousListFilter) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x100U, 0x200U}; + dev->configureFilterList(ids, 2U); + EXPECT_NE(fakeCan.FM1R & (1U << 0U), 0U); + + // Re-configure accept-all + dev->configureAcceptAllFilter(); + EXPECT_EQ(fakeCan.FM1R & (1U << 0U), 0U); // Back to mask mode + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0U); +} + +TEST_F(BxCanDeviceTest, filterListFr1CorrectShift) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x001U, 0x002U}; + dev->configureFilterList(ids, 2U); + // STID_Pos is 21, so 0x001 << 21 = 0x00200000 + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x00200000U); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x00400000U); +} + +TEST_F(BxCanDeviceTest, filterListBank0And1IndependentFr) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x111U, 0x222U, 0x333U, 0x444U}; + dev->configureFilterList(ids, 4U); + + EXPECT_EQ(fakeCan.sFilterRegister[0].FR1, 0x111U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[0].FR2, 0x222U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR1, 0x333U << CAN_RI0R_STID_Pos); + EXPECT_EQ(fakeCan.sFilterRegister[1].FR2, 0x444U << CAN_RI0R_STID_Pos); +} + +TEST_F(BxCanDeviceTest, filterListSequentialIds) +{ + auto dev = makeInitedDevice(); + uint32_t ids[] = {0x400U, 0x401U, 0x402U, 0x403U, 0x404U, 0x405U}; + dev->configureFilterList(ids, 6U); + + for (uint8_t b = 0; b < 3; b++) + { + uint32_t expected1 = (0x400U + b * 2U) << CAN_RI0R_STID_Pos; + uint32_t expected2 = (0x401U + b * 2U) << CAN_RI0R_STID_Pos; + EXPECT_EQ(fakeCan.sFilterRegister[b].FR1, expected1) << "Bank " << (int)b; + EXPECT_EQ(fakeCan.sFilterRegister[b].FR2, expected2) << "Bank " << (int)b; + } +} + +TEST_F(BxCanDeviceTest, filterList10Ids5Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[10]; + for (uint8_t i = 0; i < 10; i++) + { + ids[i] = 0x200U + i; + } + dev->configureFilterList(ids, 10U); + + for (uint8_t b = 0; b < 5; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + } +} + +TEST_F(BxCanDeviceTest, filterList20Ids10Banks) +{ + auto dev = makeInitedDevice(); + uint32_t ids[20]; + for (uint8_t i = 0; i < 20; i++) + { + ids[i] = 0x300U + i; + } + dev->configureFilterList(ids, 20U); + + for (uint8_t b = 0; b < 10; b++) + { + EXPECT_NE(fakeCan.FA1R & (1U << b), 0U) << "Bank " << (int)b; + } +} + +TEST_F(BxCanDeviceTest, filterListReconfigureLeavesOldBanksActive) +{ + auto dev = makeInitedDevice(); + + // First configure 6 IDs (3 banks) + uint32_t ids1[] = {0x100U, 0x200U, 0x300U, 0x400U, 0x500U, 0x600U}; + dev->configureFilterList(ids1, 6U); + EXPECT_NE(fakeCan.FA1R & (1U << 2U), 0U); + + // Now reconfigure with just 2 IDs (1 bank) + uint32_t ids2[] = {0x700U, 0x701U}; + dev->configureFilterList(ids2, 2U); + EXPECT_NE(fakeCan.FA1R & (1U << 0U), 0U); + // configureFilterList does NOT deactivate banks beyond the new list - + // bank 2 from the previous configuration remains active + EXPECT_NE(fakeCan.FA1R & (1U << 2U), 0U); +} + +TEST_F(BxCanDeviceTest, txMailbox0SelectedWhenAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txMailbox1SelectedWhen0Full) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[1].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txMailbox2SelectedWhen01Full) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME2; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[2].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txAllFullReturnsFalse) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + uint8_t data[8] = {}; + EXPECT_FALSE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); +} + +TEST_F(BxCanDeviceTest, txTmeBit000AllFull) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; // TME0=0, TME1=0, TME2=0 + uint8_t data[8] = {}; + EXPECT_FALSE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); +} + +TEST_F(BxCanDeviceTest, txTmeBit100Only0Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + uint32_t stid = (fakeCan.sTxMailBox[0].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x100U); +} + +TEST_F(BxCanDeviceTest, txTmeBit010Only1Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + uint32_t stid = (fakeCan.sTxMailBox[1].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x100U); +} + +TEST_F(BxCanDeviceTest, txTmeBit001Only2Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + uint32_t stid = (fakeCan.sTxMailBox[2].TIR >> CAN_TI0R_STID_Pos) & 0x7FFU; + EXPECT_EQ(stid, 0x100U); +} + +TEST_F(BxCanDeviceTest, txTmeBit110Only01Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + // Should pick mailbox 0 (first empty) + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txTmeBit101Only02Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txTmeBit011Only12Empty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + EXPECT_NE(fakeCan.sTxMailBox[1].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txTmeBit111AllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + uint8_t data[8] = {}; + EXPECT_TRUE(dev->transmit(::can::CANFrame(0x100U, data, 1U))); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txTxrqSetInSelectedMailbox) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME1; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x200U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[1].TIR & CAN_TI0R_TXRQ, 0U); +} + +TEST_F(BxCanDeviceTest, txStandardIdNotIde) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); +} + +TEST_F(BxCanDeviceTest, txExtendedIdSetsIde) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x80000100U, data, 1U)); + EXPECT_NE(fakeCan.sTxMailBox[0].TIR & CAN_TI0R_IDE, 0U); +} + +TEST_F(BxCanDeviceTest, txDlc0) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {}; + dev->transmit(::can::CANFrame(0x100U, data, 0U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 0U); +} + +TEST_F(BxCanDeviceTest, txDlc1) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA}; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 1U); +} + +TEST_F(BxCanDeviceTest, txDlc2) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB}; + dev->transmit(::can::CANFrame(0x100U, data, 2U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 2U); +} + +TEST_F(BxCanDeviceTest, txDlc3) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC}; + dev->transmit(::can::CANFrame(0x100U, data, 3U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 3U); +} + +TEST_F(BxCanDeviceTest, txDlc4) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD}; + dev->transmit(::can::CANFrame(0x100U, data, 4U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 4U); +} + +TEST_F(BxCanDeviceTest, txDlc5) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE}; + dev->transmit(::can::CANFrame(0x100U, data, 5U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 5U); +} + +TEST_F(BxCanDeviceTest, txDlc6) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; + dev->transmit(::can::CANFrame(0x100U, data, 6U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 6U); +} + +TEST_F(BxCanDeviceTest, txDlc7) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11}; + dev->transmit(::can::CANFrame(0x100U, data, 7U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 7U); +} + +TEST_F(BxCanDeviceTest, txDlc8) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0; + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22}; + dev->transmit(::can::CANFrame(0x100U, data, 8U)); + EXPECT_EQ(fakeCan.sTxMailBox[0].TDTR & 0xFU, 8U); +} + +TEST_F(BxCanDeviceTest, transmitIsrWritesRqcp0) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrWritesRqcp1) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrWritesRqcp2) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme000KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = 0U; // All busy + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme001KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme010KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME1; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme011KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme100KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme101KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme110KeepsTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrTme111DisablesTmeie) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrPreservesFmpie0) +{ + auto dev = makeStartedDevice(); + fakeCan.IER |= CAN_IER_TMEIE; + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrNoTmeieWhenNotPreviouslySet) +{ + auto dev = makeStartedDevice(); + fakeCan.IER &= ~CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrCalledMultipleTimesIdempotent) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + fakeCan.IER |= CAN_IER_TMEIE; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrThenTransmitReenablesTmeie) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + fakeCan.IER |= CAN_IER_TMEIE; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); + + fakeCan.TSR = CAN_TSR_TME0; + dev->transmit(::can::CANFrame(0x100U, data, 1U)); + EXPECT_NE(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrRqcpSetEvenWhenAllEmpty) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrRqcpSetEvenWhenAllBusy) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = 0U; + dev->transmitISR(); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP0, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP1, 0U); + EXPECT_NE(fakeCan.TSR & CAN_TSR_RQCP2, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrWithRqcpAlreadySet) +{ + auto dev = makeStartedDevice(); + fakeCan.TSR = CAN_TSR_RQCP0 | CAN_TSR_RQCP1 | CAN_TSR_RQCP2 | CAN_TSR_TME0 | CAN_TSR_TME1 + | CAN_TSR_TME2; + fakeCan.IER |= CAN_IER_TMEIE; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); +} + +TEST_F(BxCanDeviceTest, transmitIsrRapidCallsDoNotCorruptIer) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 10; i++) + { + fakeCan.IER |= CAN_IER_TMEIE; + fakeCan.TSR = CAN_TSR_TME0 | CAN_TSR_TME1 | CAN_TSR_TME2; + dev->transmitISR(); + EXPECT_EQ(fakeCan.IER & CAN_IER_TMEIE, 0U); + EXPECT_NE(fakeCan.IER & CAN_IER_FMPIE0, 0U); + } +} + +TEST_F(BxCanDeviceTest, busOffClearWhenBoffBitClear) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, busOffSetWhenBoffBitSet) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = CAN_ESR_BOFF; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(BxCanDeviceTest, busOffWithTecMaxAndBoff) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = CAN_ESR_BOFF | (255U << CAN_ESR_TEC_Pos); + EXPECT_TRUE(dev->isBusOff()); + EXPECT_EQ(dev->getTxErrorCounter(), 255U); +} + +TEST_F(BxCanDeviceTest, tecZero) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); +} + +TEST_F(BxCanDeviceTest, tec128) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (128U << CAN_ESR_TEC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 128U); +} + +TEST_F(BxCanDeviceTest, tec255) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (255U << CAN_ESR_TEC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 255U); +} + +TEST_F(BxCanDeviceTest, tec1) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (1U << CAN_ESR_TEC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 1U); +} + +TEST_F(BxCanDeviceTest, recZero) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = 0U; + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(BxCanDeviceTest, rec64) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (64U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 64U); +} + +TEST_F(BxCanDeviceTest, rec127) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (127U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 127U); +} + +TEST_F(BxCanDeviceTest, rec255) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (255U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 255U); +} + +TEST_F(BxCanDeviceTest, rec1) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (1U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 1U); +} + +TEST_F(BxCanDeviceTest, tecAndRecSimultaneous) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (100U << CAN_ESR_TEC_Pos) | (200U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 100U); + EXPECT_EQ(dev->getRxErrorCounter(), 200U); +} + +TEST_F(BxCanDeviceTest, busOffDoesNotAffectErrorCounters) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = CAN_ESR_BOFF | (50U << CAN_ESR_TEC_Pos) | (75U << CAN_ESR_REC_Pos); + EXPECT_TRUE(dev->isBusOff()); + EXPECT_EQ(dev->getTxErrorCounter(), 50U); + EXPECT_EQ(dev->getRxErrorCounter(), 75U); +} + +TEST_F(BxCanDeviceTest, errorCountersChangeOverTime) +{ + auto dev = makeStartedDevice(); + fakeCan.ESR = (10U << CAN_ESR_TEC_Pos) | (20U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 10U); + EXPECT_EQ(dev->getRxErrorCounter(), 20U); + + fakeCan.ESR = (150U << CAN_ESR_TEC_Pos) | (200U << CAN_ESR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 150U); + EXPECT_EQ(dev->getRxErrorCounter(), 200U); + + fakeCan.ESR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(BxCanDeviceTest, getRxCountInitiallyZero) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, getRxCountAfterOneFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(BxCanDeviceTest, getRxCountAfter5Frames) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + for (uint8_t i = 0; i < 5; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 5U); +} + +TEST_F(BxCanDeviceTest, getRxCountAfter32Frames) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); +} + +TEST_F(BxCanDeviceTest, clearRxQueueSetsCountToZero) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, clearRxQueueMultipleTimes) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, clearRxQueueOnEmptyQueue) +{ + auto dev = makeStartedDevice(); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(BxCanDeviceTest, rxQueueWrappingWithExactCapacity) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to 30 frames, clear, then fill to 32 (wraps around) + for (uint8_t i = 0; i < 30; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + for (uint8_t i = 0; i < 32; i++) + { + data[0] = i; + receiveSingleFrame(*dev, 0x200U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(31).getId(), 0x21FU); +} + +TEST_F(BxCanDeviceTest, rxQueueHeadAdvancesOnClear) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0; i < 10; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + // Head should now be at 10 + receiveSingleFrame(*dev, 0x500U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x500U); +} + +TEST_F(BxCanDeviceTest, rxQueueMultipleCycleFillAndClear) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (int cycle = 0; cycle < 5; cycle++) + { + for (uint8_t i = 0; i < 8; i++) + { + receiveSingleFrame(*dev, 0x100U * (cycle + 1) + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 8U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + } +} + +TEST_F(BxCanDeviceTest, rxQueueFrameContentAfterWrapping) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill 28 frames, clear, then add 8 more (wraps 28+8=36 > 32) + for (uint8_t i = 0; i < 28; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + for (uint8_t i = 0; i < 8; i++) + { + data[0] = 0xA0U + i; + receiveSingleFrame(*dev, 0x300U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 8U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x300U); + EXPECT_EQ(dev->getRxFrame(0).getPayload()[0], 0xA0U); + EXPECT_EQ(dev->getRxFrame(7).getId(), 0x307U); + EXPECT_EQ(dev->getRxFrame(7).getPayload()[0], 0xA7U); +} + +TEST_F(BxCanDeviceTest, rxQueueFullThen33rdDropped) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + + // 33rd frame should be dropped + placeRxFrameStd(0x999U, 1U, data); + fakeCan.RF0R = 1U; + std::atomic done{false}; + std::thread hwSim( + [&]() + { + while (!done.load(std::memory_order_relaxed)) + { + if ((fakeCan.RF0R & CAN_RF0R_RFOM0) != 0U) + { + fakeCan.RF0R = 0U; + done.store(true, std::memory_order_relaxed); + return; + } + } + }); + uint8_t count = dev->receiveISR(nullptr); + done.store(true, std::memory_order_relaxed); + hwSim.join(); + EXPECT_EQ(count, 0U); + EXPECT_EQ(dev->getRxCount(), 32U); +} + +TEST_F(BxCanDeviceTest, rxQueueClearThenFillToCapacity) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x200U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(31).getId(), 0x21FU); +} + +TEST_F(BxCanDeviceTest, rxQueueGetFrameIndex0) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x42}; + receiveSingleFrame(*dev, 0x123U, false, 1U, data); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x123U); + EXPECT_EQ(dev->getRxFrame(0).getPayload()[0], 0x42U); +} + +TEST_F(BxCanDeviceTest, rxQueueGetFrameLastIndex) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint8_t i = 0; i < 10; i++) + { + data[0] = i; + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxFrame(9).getId(), 0x109U); + EXPECT_EQ(dev->getRxFrame(9).getPayload()[0], 9U); +} + +TEST_F(BxCanDeviceTest, rxQueueStandardAndExtendedMixed) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + receiveSingleFrame(*dev, 0x1ABCDU, true, 1U, data); + receiveSingleFrame(*dev, 0x200U, false, 1U, data); + + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x1ABCDU | 0x80000000U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x200U); +} + +TEST_F(BxCanDeviceTest, rxQueueDifferentDlcValues) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {1, 2, 3, 4, 5, 6, 7, 8}; + + for (uint8_t dlc = 0; dlc <= 8; dlc++) + { + receiveSingleFrame(*dev, 0x100U + dlc, false, dlc, data); + } + EXPECT_EQ(dev->getRxCount(), 9U); + for (uint8_t dlc = 0; dlc <= 8; dlc++) + { + EXPECT_EQ(dev->getRxFrame(dlc).getPayloadLength(), dlc); + } +} + +TEST_F(BxCanDeviceTest, rxQueueReceiveClearReceive) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + receiveSingleFrame(*dev, 0x100U, false, 1U, data); + receiveSingleFrame(*dev, 0x101U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 2U); + + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + receiveSingleFrame(*dev, 0x200U, false, 1U, data); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(BxCanDeviceTest, rxQueueWrappingPreservesDataIntegrity) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill 31 frames and clear + for (uint8_t i = 0; i < 31; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + // Fill 5 more (wraps around position 31 -> 0 -> 1 -> 2 -> 3 -> 4) + for (uint8_t i = 0; i < 5; i++) + { + data[0] = 0xF0U + i; + receiveSingleFrame(*dev, 0x400U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 5U); + for (uint8_t i = 0; i < 5; i++) + { + EXPECT_EQ(dev->getRxFrame(i).getId(), 0x400U + i); + EXPECT_EQ(dev->getRxFrame(i).getPayload()[0], 0xF0U + i); + } +} + +TEST_F(BxCanDeviceTest, rxQueueFullExactly32) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + for (uint8_t i = 0; i < 32; i++) + { + data[0] = i; + receiveSingleFrame(*dev, i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + for (uint8_t i = 0; i < 32; i++) + { + EXPECT_EQ(dev->getRxFrame(i).getId(), static_cast(i)); + EXPECT_EQ(dev->getRxFrame(i).getPayload()[0], i); + } +} + +TEST_F(BxCanDeviceTest, rxQueueClearAfterFullThenRefill) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to capacity + for (uint8_t i = 0; i < 32; i++) + { + receiveSingleFrame(*dev, 0x100U + i, false, 1U, data); + } + dev->clearRxQueue(); + + // Fill again to capacity + for (uint8_t i = 0; i < 32; i++) + { + data[0] = 0x80U + i; + receiveSingleFrame(*dev, 0x300U + i, false, 1U, data); + } + EXPECT_EQ(dev->getRxCount(), 32U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x300U); + EXPECT_EQ(dev->getRxFrame(31).getId(), 0x31FU); +} + +TEST_F(BxCanDeviceTest, rxQueueFilterRejectDoesNotIncrementCount) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t filter[256] = {}; + // Only accept ID 0x100 + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); + + // Try to receive ID 0x200 (rejected) + uint8_t count = receiveSingleFrame(*dev, 0x200U, false, 1U, data, filter); + EXPECT_EQ(count, 0U); + EXPECT_EQ(dev->getRxCount(), 0U); + + // Now receive ID 0x100 (accepted) + count = receiveSingleFrame(*dev, 0x100U, false, 1U, data, filter); + EXPECT_EQ(count, 1U); + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(BxCanDeviceTest, rxQueueFilterAcceptMultipleIds) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + uint8_t filter[256] = {}; + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); + filter[0x200 / 8U] |= (1U << (0x200 % 8U)); + filter[0x300 / 8U] |= (1U << (0x300 % 8U)); + + receiveSingleFrame(*dev, 0x100U, false, 1U, data, filter); + receiveSingleFrame(*dev, 0x150U, false, 1U, data, filter); // Rejected + receiveSingleFrame(*dev, 0x200U, false, 1U, data, filter); + receiveSingleFrame(*dev, 0x250U, false, 1U, data, filter); // Rejected + receiveSingleFrame(*dev, 0x300U, false, 1U, data, filter); + + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x300U); +} + +TEST_F(BxCanDeviceTest, btrBrpField10Bits) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1024U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t brp = fakeCan.BTR & 0x3FFU; + EXPECT_EQ(brp, 1023U); +} + +TEST_F(BxCanDeviceTest, btrTs1Field4Bits) +{ + auto cfg = makeDefaultConfig(); + cfg.bs1 = 16U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts1 = (fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU; + EXPECT_EQ(ts1, 15U); +} + +TEST_F(BxCanDeviceTest, btrTs2Field3Bits) +{ + auto cfg = makeDefaultConfig(); + cfg.bs2 = 8U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts2 = (fakeCan.BTR >> CAN_BTR_TS2_Pos) & 0x7U; + EXPECT_EQ(ts2, 7U); +} + +TEST_F(BxCanDeviceTest, btrSjwField2Bits) +{ + auto cfg = makeDefaultConfig(); + cfg.sjw = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t sjw = (fakeCan.BTR >> CAN_BTR_SJW_Pos) & 0x3U; + EXPECT_EQ(sjw, 3U); +} + +TEST_F(BxCanDeviceTest, btrPrescaler2) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 2U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 1U); +} + +TEST_F(BxCanDeviceTest, btrPrescaler100) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 100U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 99U); +} + +TEST_F(BxCanDeviceTest, btrPrescaler256) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 256U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 255U); +} + +TEST_F(BxCanDeviceTest, btrCan250kbpsAt42MHz) +{ + // 42 MHz APB1, 250 kbps: prescaler=12, BS1=11, BS2=2, SJW=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 12U; + cfg.bs1 = 11U; + cfg.bs2 = 2U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 11U); + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 10U); + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 1U); + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 0U); +} + +TEST_F(BxCanDeviceTest, btrCan1MbpsAt42MHz) +{ + // 42 MHz APB1, 1 Mbps: prescaler=3, BS1=11, BS2=2, SJW=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 3U; + cfg.bs1 = 11U; + cfg.bs2 = 2U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 2U); + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 10U); + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 1U); +} + +TEST_F(BxCanDeviceTest, btrCan125kbpsAt36MHz) +{ + // 36 MHz APB1, 125 kbps: prescaler=18, BS1=13, BS2=2, SJW=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 18U; + cfg.bs1 = 13U; + cfg.bs2 = 2U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 17U); + EXPECT_EQ((fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU, 12U); + EXPECT_EQ((fakeCan.BTR >> CAN_BTR_TS2_Pos) & 0x7U, 1U); +} + +TEST_F(BxCanDeviceTest, btrAllFieldsMaxValues) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1024U; + cfg.bs1 = 16U; + cfg.bs2 = 8U; + cfg.sjw = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 1023U); + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 15U); + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 7U); + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 3U); +} + +TEST_F(BxCanDeviceTest, btrAllFieldsMinValues) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + cfg.bs1 = 1U; + cfg.bs2 = 1U; + cfg.sjw = 1U; + bios::BxCanDevice dev(cfg); + dev.init(); + + uint32_t btr = fakeCan.BTR; + EXPECT_EQ(btr & 0x3FFU, 0U); + EXPECT_EQ((btr >> CAN_BTR_TS1_Pos) & 0xFU, 0U); + EXPECT_EQ((btr >> CAN_BTR_TS2_Pos) & 0x7U, 0U); + EXPECT_EQ((btr >> CAN_BTR_SJW_Pos) & 0x3U, 0U); +} + +TEST_F(BxCanDeviceTest, btrNoSilentOrLoopbackMode) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // SILM and LBKM should not be set for normal operation + EXPECT_EQ(fakeCan.BTR & CAN_BTR_SILM, 0U); + EXPECT_EQ(fakeCan.BTR & CAN_BTR_LBKM, 0U); +} + +TEST_F(BxCanDeviceTest, btrRewrittenOnDoubleInit) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 4U; + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 3U); + + // Corrupt BTR + fakeCan.BTR = 0xFFFFFFFFU; + + // Re-init should restore correct BTR + dev.init(); + EXPECT_EQ(fakeCan.BTR & 0x3FFU, 3U); +} + +TEST_F(BxCanDeviceTest, btrBs1Value8) +{ + auto cfg = makeDefaultConfig(); + cfg.bs1 = 8U; + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t ts1 = (fakeCan.BTR >> CAN_BTR_TS1_Pos) & 0xFU; + EXPECT_EQ(ts1, 7U); +} + +TEST_F(BxCanDeviceTest, clockEnableSetsApb1enrBit25) +{ + fakeRcc.APB1ENR = 0U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 25U), 0U); +} + +TEST_F(BxCanDeviceTest, clockEnablePreservesOtherBitsInApb1enr) +{ + fakeRcc.APB1ENR = 0x00000001U; // Some other peripheral enabled + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & 0x00000001U, 0U); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, clockEnableIdempotentMultipleInits) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + uint32_t v1 = fakeRcc.APB1ENR; + dev.init(); + uint32_t v2 = fakeRcc.APB1ENR; + dev.init(); + uint32_t v3 = fakeRcc.APB1ENR; + EXPECT_EQ(v1, v2); + EXPECT_EQ(v2, v3); +} + +TEST_F(BxCanDeviceTest, clockEnableBeforeGpioConfig) +{ + // After init, both clock and GPIO should be configured + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); + uint32_t txModer = (fakeTxGpio.MODER >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); +} + +TEST_F(BxCanDeviceTest, clockEnableWithAllApb1BitsSet) +{ + fakeRcc.APB1ENR = 0xFFFFFFFFU; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // All bits should still be set + EXPECT_EQ(fakeRcc.APB1ENR, 0xFFFFFFFFU); +} + +TEST_F(BxCanDeviceTest, clockEnableBitExactValue) +{ + fakeRcc.APB1ENR = 0U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, (1U << 25U)); +} + +TEST_F(BxCanDeviceTest, clockEnableDoesNotTouchApb2enr) +{ + fakeRcc.APB2ENR = 0U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeRcc.APB2ENR, 0U); +} + +TEST_F(BxCanDeviceTest, clockEnableDoesNotTouchAhb1enr) +{ + fakeRcc.AHB1ENR = 0U; + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + // Note: GPIO clock enable might set AHB1ENR bits, + // but CAN clock should only touch APB1ENR + // We just verify CAN1EN is in APB1ENR + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, clockEnableWithNeighborBitsPreserved) +{ + // Set bits 24 and 26 (neighbors of bit 25) + fakeRcc.APB1ENR = (1U << 24U) | (1U << 26U); + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 24U), 0U); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 25U), 0U); + EXPECT_NE(fakeRcc.APB1ENR & (1U << 26U), 0U); +} + +TEST_F(BxCanDeviceTest, clockEnableAfterStopAndReInit) +{ + auto dev = makeStartedDevice(); + fakeCan.MSR |= CAN_MSR_INAK; + dev->stop(); + + // Re-init + dev->init(); + EXPECT_NE(fakeRcc.APB1ENR & RCC_APB1ENR_CAN1EN, 0U); +} + +TEST_F(BxCanDeviceTest, gpioRxPullUpConfigured) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + // RX pin should have pull-up (PUPDR = 01) + uint32_t pupdr = (fakeRxGpio.PUPDR >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(pupdr, 1U); +} + +TEST_F(BxCanDeviceTest, gpioTxHighSpeedConfigured) +{ + auto cfg = makeDefaultConfig(); + bios::BxCanDevice dev(cfg); + dev.init(); + + // TX pin should have high speed (OSPEEDR = 11) + uint32_t speed = (fakeTxGpio.OSPEEDR >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(speed, 3U); +} + +TEST_F(BxCanDeviceTest, rxQueueCapacityConstant) +{ + EXPECT_EQ(bios::BxCanDevice::RX_QUEUE_SIZE, 32U); +} diff --git a/platforms/stm32/bsp/bspCan/test/src/can/FakeHwHelpers.h b/platforms/stm32/bsp/bspCan/test/src/can/FakeHwHelpers.h new file mode 100644 index 00000000000..54151b0e51f --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/src/can/FakeHwHelpers.h @@ -0,0 +1,170 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/** + * \file FakeHwHelpers.h + * \brief Smart fake register helpers for STM32 unit tests. + * + * Solves three problems that prevent tests from running on x86_64 host: + * + * 1. HW busy-wait loops: Production code writes a control bit then polls + * a status bit that hardware would auto-set/clear. These helpers hook + * into the fake register structs to simulate the state transitions. + * + * 2. 64-bit pointer truncation: Provides FAKE_ADDR() macro that safely + * creates a testable address from a static array on any platform. + * + * 3. Message RAM access: Provides a fake message RAM region with proper + * addressing that works on both 32-bit and 64-bit hosts. + */ + +#pragma once + +#include +#include + +// ============================================================================ +// Portable address casting - works on both 32-bit ARM and 64-bit host +// ============================================================================ + +/// Cast a pointer to the address type used by the production code (uint32_t). +/// On 64-bit host this truncates, but the production code will cast it back +/// to a pointer via reinterpret_cast(addr) which restores the full address +/// ONLY if the original pointer is in the low 4GB. We use static arrays which +/// on most x86_64 systems are in the low address space. +/// +/// For tests that need message RAM pointer arithmetic, use FakeMessageRam instead. +static inline uint32_t ptrToU32(void* p) +{ + return static_cast(reinterpret_cast(p)); +} + +// ============================================================================ +// BxCAN state simulation +// ============================================================================ + +/// Call after each write to fake CAN->MCR to simulate MSR tracking. +/// When INRQ is set in MCR, hardware sets INAK in MSR. +/// When INRQ is cleared, hardware clears INAK. +struct BxCanStateTracker +{ + /// Bit positions (must match CAN_MCR_INRQ and CAN_MSR_INAK) + static constexpr uint32_t MCR_INRQ = (1U << 0); + static constexpr uint32_t MSR_INAK = (1U << 0); + + /// Call this after writing to fakeCan.MCR + static void syncMsrFromMcr(uint32_t volatile& mcr, uint32_t volatile& msr) + { + if ((mcr & MCR_INRQ) != 0U) + { + msr |= MSR_INAK; // entering init mode + } + else + { + msr &= ~MSR_INAK; // leaving init mode + } + } +}; + +// ============================================================================ +// FDCAN state simulation +// ============================================================================ + +struct FdCanStateTracker +{ + static constexpr uint32_t CCCR_INIT = (1U << 0); + + /// Call this after writing to fakeFdcan.CCCR + static void syncCccrInit(uint32_t volatile& cccr) + { + // On real HW, writing INIT=1 is reflected immediately. + // Writing INIT=0 clears it after the peripheral finishes. + // In the fake, the write already sets/clears the bit directly + // since we're writing to a RAM variable. No extra action needed. + // The production code's while-loop checks the same variable + // and will see the change immediately. + (void)cccr; + } +}; + +// ============================================================================ +// ADC state simulation +// ============================================================================ + +struct AdcStateTracker +{ + /// After writing ADCAL=1 to CR, hardware clears it when calibration done. + /// We clear it immediately since there's no real calibration to do. + static void clearAdcalAfterWrite(uint32_t volatile& cr, uint32_t adcalBit) { cr &= ~adcalBit; } + + /// After writing ADEN=1 to CR, hardware sets ADRDY in ISR. + static void setAdrdyAfterEnable( + uint32_t volatile& cr, uint32_t volatile& isr, uint32_t adenBit, uint32_t adrdyBit) + { + if ((cr & adenBit) != 0U) + { + isr |= adrdyBit; + } + } + + /// After writing ADSTART=1, hardware sets EOC when conversion done. + static void setEocAfterStart( + uint32_t volatile& cr, uint32_t volatile& isr, uint32_t adstartBit, uint32_t eocBit) + { + if ((cr & adstartBit) != 0U) + { + isr |= eocBit; + } + } +}; + +// ============================================================================ +// Flash state simulation +// ============================================================================ + +struct FlashStateTracker +{ + /// Flash BSY bit is always 0 in fake (operation completes instantly). + /// The production waitForFlash() checks FLASH->SR & BSY - as long as + /// we memset SR to 0 in SetUp(), the loop exits immediately. + /// No action needed if SetUp clears SR. + + /// After erasePage sets STRT, clear it (simulates erase complete). + static void clearAfterErase(uint32_t volatile& cr, uint32_t volatile& sr, uint32_t strtBit) + { + cr &= ~strtBit; + sr = 0U; // BSY=0, no errors + } +}; + +// ============================================================================ +// Fake message RAM for FDCAN (solves 64-bit pointer truncation) +// ============================================================================ + +/// Provides a message RAM region that can be addressed via uint32_t offsets. +/// Instead of using absolute addresses (which get truncated on x86_64), +/// the tests should set SRAMCAN_BASE to 0 and use relative offsets into +/// this array. The production code does: `base + offset` then casts to +/// `uint32_t*`. If base is the actual array address (as uintptr_t cast to +/// uint32_t), it gets truncated. The fix: define SRAMCAN_BASE as 0 and +/// override getInstanceRamBase() to return the real pointer. +/// +/// However, since getInstanceRamBase is a static function inside the .cpp, +/// we can't easily override it. The simplest approach: on 64-bit host, +/// accept that message RAM tests will segfault and skip them with a guard. +/// +/// For a complete fix, the production code should use uintptr_t for RAM +/// addresses, which is an API change requiring upstream approval. + +#if UINTPTR_MAX > UINT32_MAX +#define FAKE_MSG_RAM_64BIT_HOST 1 +#else +#define FAKE_MSG_RAM_64BIT_HOST 0 +#endif diff --git a/platforms/stm32/bsp/bspCan/test/src/can/FdCanDeviceTest.cpp b/platforms/stm32/bsp/bspCan/test/src/can/FdCanDeviceTest.cpp new file mode 100644 index 00000000000..5fea5c6b99a --- /dev/null +++ b/platforms/stm32/bsp/bspCan/test/src/can/FdCanDeviceTest.cpp @@ -0,0 +1,3657 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Test-only hardware fakes - must be defined before any STM32 header inclusion. + +#include +#include + +// Provide __IO as volatile (same as CMSIS) +#ifndef __IO +#define __IO volatile +#endif + +// --- Fake GPIO_TypeDef (matches STM32G4 layout) --- +typedef struct +{ + __IO uint32_t MODER; + __IO uint32_t OTYPER; + __IO uint32_t OSPEEDR; + __IO uint32_t PUPDR; + __IO uint32_t IDR; + __IO uint32_t ODR; + __IO uint32_t BSRR; + __IO uint32_t LCKR; + __IO uint32_t AFR[2]; + __IO uint32_t BRR; +} GPIO_TypeDef; + +// --- Fake RCC_TypeDef (matches STM32G4 layout) --- +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t ICSCR; + __IO uint32_t CFGR; + __IO uint32_t PLLCFGR; + uint32_t RESERVED0; + uint32_t RESERVED1; + __IO uint32_t CIER; + __IO uint32_t CIFR; + __IO uint32_t CICR; + uint32_t RESERVED2; + __IO uint32_t AHB1RSTR; + __IO uint32_t AHB2RSTR; + __IO uint32_t AHB3RSTR; + uint32_t RESERVED3; + __IO uint32_t APB1RSTR1; + __IO uint32_t APB1RSTR2; + __IO uint32_t APB2RSTR; + uint32_t RESERVED4; + __IO uint32_t AHB1ENR; + __IO uint32_t AHB2ENR; + __IO uint32_t AHB3ENR; + uint32_t RESERVED5; + __IO uint32_t APB1ENR1; + __IO uint32_t APB1ENR2; + __IO uint32_t APB2ENR; + uint32_t RESERVED6; + __IO uint32_t AHB1SMENR; + __IO uint32_t AHB2SMENR; + __IO uint32_t AHB3SMENR; + uint32_t RESERVED7; + __IO uint32_t APB1SMENR1; + __IO uint32_t APB1SMENR2; + __IO uint32_t APB2SMENR; + uint32_t RESERVED8; + __IO uint32_t CCIPR; + uint32_t RESERVED9; + __IO uint32_t BDCR; + __IO uint32_t CSR; + __IO uint32_t CRRCR; + __IO uint32_t CCIPR2; +} RCC_TypeDef; + +// --- Fake FDCAN_GlobalTypeDef (matches STM32G4 layout) --- +typedef struct +{ + __IO uint32_t CREL; + __IO uint32_t ENDN; + uint32_t RESERVED1; + __IO uint32_t DBTP; + __IO uint32_t TEST; + __IO uint32_t RWD; + __IO uint32_t CCCR; + __IO uint32_t NBTP; + __IO uint32_t TSCC; + __IO uint32_t TSCV; + __IO uint32_t TOCC; + __IO uint32_t TOCV; + uint32_t RESERVED2[4]; + __IO uint32_t ECR; + __IO uint32_t PSR; + __IO uint32_t TDCR; + uint32_t RESERVED3; + __IO uint32_t IR; + __IO uint32_t IE; + __IO uint32_t ILS; + __IO uint32_t ILE; + uint32_t RESERVED4[8]; + __IO uint32_t RXGFC; + __IO uint32_t XIDAM; + __IO uint32_t HPMS; + uint32_t RESERVED5; + __IO uint32_t RXF0S; + __IO uint32_t RXF0A; + __IO uint32_t RXF1S; + __IO uint32_t RXF1A; + uint32_t RESERVED6[8]; + __IO uint32_t TXBC; + __IO uint32_t TXFQS; + __IO uint32_t TXBRP; + __IO uint32_t TXBAR; + __IO uint32_t TXBCR; + __IO uint32_t TXBTO; + __IO uint32_t TXBCF; + __IO uint32_t TXBTIE; + __IO uint32_t TXBCIE; + __IO uint32_t TXEFS; + __IO uint32_t TXEFA; +} FDCAN_GlobalTypeDef; + +// --- Static fake peripherals --- +static RCC_TypeDef fakeRcc; +static FDCAN_GlobalTypeDef fakeFdcan; +static GPIO_TypeDef fakeTxGpio; +static GPIO_TypeDef fakeRxGpio; + +// Fake message RAM (848 bytes = 212 words per FDCAN instance) +static uint32_t fakeMessageRam[212]; + +// --- Override hardware macros to point at our fakes --- +#define RCC (&fakeRcc) +#define FDCAN1 (&fakeFdcan) +#define FDCAN1_BASE (reinterpret_cast(&fakeFdcan)) +#define SRAMCAN_BASE (reinterpret_cast(&fakeMessageRam[0])) +#define PERIPH_BASE 0x40000000UL +#define APB1PERIPH_BASE PERIPH_BASE + +// --- FDCAN register bit definitions (from stm32g474xx.h) --- +#define FDCAN_CCCR_INIT_Pos (0U) +#define FDCAN_CCCR_INIT (0x1UL << FDCAN_CCCR_INIT_Pos) +#define FDCAN_CCCR_CCE_Pos (1U) +#define FDCAN_CCCR_CCE (0x1UL << FDCAN_CCCR_CCE_Pos) +#define FDCAN_CCCR_FDOE_Pos (8U) +#define FDCAN_CCCR_FDOE (0x1UL << FDCAN_CCCR_FDOE_Pos) +#define FDCAN_CCCR_BRSE_Pos (9U) +#define FDCAN_CCCR_BRSE (0x1UL << FDCAN_CCCR_BRSE_Pos) + +#define FDCAN_NBTP_NTSEG2_Pos (0U) +#define FDCAN_NBTP_NTSEG1_Pos (8U) +#define FDCAN_NBTP_NBRP_Pos (16U) +#define FDCAN_NBTP_NSJW_Pos (25U) + +#define FDCAN_ECR_TEC_Pos (0U) +#define FDCAN_ECR_REC_Pos (8U) + +#define FDCAN_PSR_BO_Pos (7U) +#define FDCAN_PSR_BO (0x1UL << FDCAN_PSR_BO_Pos) + +#define FDCAN_IR_RF0N_Pos (0U) +#define FDCAN_IR_RF0N (0x1UL << FDCAN_IR_RF0N_Pos) +#define FDCAN_IR_RF0F_Pos (1U) +#define FDCAN_IR_RF0F (0x1UL << FDCAN_IR_RF0F_Pos) +#define FDCAN_IR_RF0L_Pos (2U) +#define FDCAN_IR_RF0L (0x1UL << FDCAN_IR_RF0L_Pos) +#define FDCAN_IR_TC_Pos (7U) +#define FDCAN_IR_TC (0x1UL << FDCAN_IR_TC_Pos) + +#define FDCAN_IE_RF0NE_Pos (0U) +#define FDCAN_IE_RF0NE (0x1UL << FDCAN_IE_RF0NE_Pos) +#define FDCAN_IE_TCE_Pos (7U) +#define FDCAN_IE_TCE (0x1UL << FDCAN_IE_TCE_Pos) +#define FDCAN_IE_TEFNE_Pos (12U) +#define FDCAN_IE_TEFNE (0x1UL << FDCAN_IE_TEFNE_Pos) + +#define FDCAN_IR_TEFN_Pos (12U) +#define FDCAN_IR_TEFN (0x1UL << FDCAN_IR_TEFN_Pos) + +#define FDCAN_TXEFS_EFFL_Pos (0U) +#define FDCAN_TXEFS_EFFL (0x7UL << FDCAN_TXEFS_EFFL_Pos) +#define FDCAN_TXEFS_EFGI_Pos (8U) +#define FDCAN_TXEFS_EFGI (0x3UL << FDCAN_TXEFS_EFGI_Pos) + +#define FDCAN_DBTP_DSJW_Pos (0U) +#define FDCAN_DBTP_DTSEG2_Pos (4U) +#define FDCAN_DBTP_DTSEG1_Pos (8U) +#define FDCAN_DBTP_DBRP_Pos (16U) +#define FDCAN_DBTP_TDC_Pos (23U) +#define FDCAN_DBTP_TDC (0x1UL << FDCAN_DBTP_TDC_Pos) + +#define FDCAN_TDCR_TDCO_Pos (8U) + +#define FDCAN_ILS_SMSG_Pos (2U) +#define FDCAN_ILS_SMSG (0x1UL << FDCAN_ILS_SMSG_Pos) + +#define FDCAN_ILE_EINT0_Pos (0U) +#define FDCAN_ILE_EINT0 (0x1UL << FDCAN_ILE_EINT0_Pos) +#define FDCAN_ILE_EINT1_Pos (1U) +#define FDCAN_ILE_EINT1 (0x1UL << FDCAN_ILE_EINT1_Pos) + +#define FDCAN_RXGFC_ANFE_Pos (2U) +#define FDCAN_RXGFC_ANFS_Pos (4U) +#define FDCAN_RXGFC_LSS_Pos (16U) +#define FDCAN_RXGFC_LSE_Pos (20U) + +#define FDCAN_RXF0S_F0FL_Pos (0U) +#define FDCAN_RXF0S_F0FL (0xFUL << FDCAN_RXF0S_F0FL_Pos) +#define FDCAN_RXF0S_F0GI_Pos (8U) +#define FDCAN_RXF0S_F0GI (0x3UL << FDCAN_RXF0S_F0GI_Pos) + +#define FDCAN_TXFQS_TFFL_Pos (0U) +#define FDCAN_TXFQS_TFFL (0x7UL << FDCAN_TXFQS_TFFL_Pos) +#define FDCAN_TXFQS_TFQPI_Pos (16U) +#define FDCAN_TXFQS_TFQPI (0x3UL << FDCAN_TXFQS_TFQPI_Pos) + +#define RCC_APB1ENR1_FDCANEN_Pos (25U) +#define RCC_APB1ENR1_FDCANEN (0x1UL << RCC_APB1ENR1_FDCANEN_Pos) + +// Prevent the real mcu.h from being included (it would pull in stm32g474xx.h) +#define MCU_MCU_H +#define MCU_TYPEDEFS_H + +// Provide the CANFrame include path directly +#include + +// Now include the driver header - it will see our faked types +#include + +// Include the implementation directly so it compiles with our fakes. +// The getInstanceRamBase() function compares against FDCAN1 which is now &fakeFdcan. +#include + +#include + +// Message RAM offset constants (must match FdCanDevice.cpp). +static constexpr uint32_t MSG_RAM_STD_FILTER_OFFSET = 0x000U; +static constexpr uint32_t MSG_RAM_RX_FIFO0_OFFSET = 0x0B0U; +static constexpr uint32_t MSG_RAM_TX_BUFFER_OFFSET = 0x278U; // STM32G4 specific + +class FdCanDeviceTest : public ::testing::Test +{ +protected: + void SetUp() override + { + // Zero all fake peripherals and message RAM + memset(&fakeRcc, 0, sizeof(fakeRcc)); + memset(&fakeFdcan, 0, sizeof(fakeFdcan)); + memset(&fakeTxGpio, 0, sizeof(fakeTxGpio)); + memset(&fakeRxGpio, 0, sizeof(fakeRxGpio)); + memset(fakeMessageRam, 0, sizeof(fakeMessageRam)); + + // The CCCR.INIT bit: after setting INIT, the hardware immediately + // reflects it. In our fake we simulate this by pre-setting the bit + // in the write path (the driver busy-waits until INIT is set). + // We handle this by setting INIT in CCCR before construction so + // enterInitMode's while-loop terminates immediately. + } + + bios::FdCanDevice::Config makeDefaultConfig() + { + bios::FdCanDevice::Config cfg{}; + cfg.baseAddress = &fakeFdcan; + cfg.prescaler = 4U; // BRP = prescaler - 1 = 3 + cfg.nts1 = 13U; // NTSEG1 + cfg.nts2 = 2U; // NTSEG2 + cfg.nsjw = 1U; // NSJW + cfg.rxGpioPort = &fakeRxGpio; + cfg.rxPin = 11U; // PA11 (high pin, tests AFR[1] path) + cfg.rxAf = 9U; // AF9 + cfg.txGpioPort = &fakeTxGpio; + cfg.txPin = 5U; // PB5 (low pin, tests AFR[0] path) + cfg.txAf = 9U; // AF9 + return cfg; + } + + // Helper: simulate hardware behavior where INIT bit reflects immediately + void simulateInitBitReflection() + { + // The driver sets INIT, then busy-waits. Since our fake register + // reflects the write immediately (same memory), the loop exits. + // No extra action needed for stack-based volatile registers. + } + + // Helper: put device into initialized + started state + std::unique_ptr makeInitedDevice() + { + auto cfg = makeDefaultConfig(); + auto dev = std::make_unique(cfg); + dev->init(); + return dev; + } + + std::unique_ptr makeStartedDevice() + { + auto dev = makeInitedDevice(); + dev->start(); + return dev; + } + + // Helper: place a frame in RX FIFO0 message RAM at given index + // Element spacing is 72 bytes (18 words) - must match RX_ELEMENT_SIZE + // in FdCanDevice.cpp. + void + placeRxFrame(uint8_t fifoIndex, uint32_t canId, bool extended, uint8_t dlc, uint8_t const* data) + { + uint32_t* rxBuf = reinterpret_cast( + reinterpret_cast(fakeMessageRam) + MSG_RAM_RX_FIFO0_OFFSET + + (fifoIndex * 72U)); + + // Word 0: ID + if (extended) + { + rxBuf[0] = (canId & 0x1FFFFFFFU) | (1U << 30U); // XTD bit + } + else + { + rxBuf[0] = ((canId & 0x7FFU) << 18U); + } + + // Word 1: DLC + rxBuf[1] = (static_cast(dlc) << 16U); + + // Words 2-3: Data + if (data != nullptr) + { + rxBuf[2] = static_cast(data[0]) | (static_cast(data[1]) << 8U) + | (static_cast(data[2]) << 16U) + | (static_cast(data[3]) << 24U); + rxBuf[3] = static_cast(data[4]) | (static_cast(data[5]) << 8U) + | (static_cast(data[6]) << 16U) + | (static_cast(data[7]) << 24U); + } + } + + // Helper: set RX FIFO0 fill level and get index + void setRxFifoStatus(uint8_t fillLevel, uint8_t getIndex) + { + fakeFdcan.RXF0S = (static_cast(fillLevel) << FDCAN_RXF0S_F0FL_Pos) + | (static_cast(getIndex) << FDCAN_RXF0S_F0GI_Pos); + } + + // Helper: set TX FIFO free level and put index + void setTxFifoStatus(uint8_t freeLevel, uint8_t putIndex) + { + fakeFdcan.TXFQS = (static_cast(freeLevel) << FDCAN_TXFQS_TFFL_Pos) + | (static_cast(putIndex) << FDCAN_TXFQS_TFQPI_Pos); + } + + // Helper: read TX buffer element from message RAM + // Element spacing is 72 bytes (18 words) - must match TX_ELEMENT_SIZE + // in FdCanDevice.cpp. + uint32_t const* getTxBuffer(uint8_t index) + { + return reinterpret_cast( + reinterpret_cast(fakeMessageRam) + MSG_RAM_TX_BUFFER_OFFSET + (index * 72U)); + } + + // Helper: read standard filter element from message RAM + uint32_t getStdFilter(uint8_t index) + { + uint32_t const* filterRam = reinterpret_cast( + reinterpret_cast(fakeMessageRam) + MSG_RAM_STD_FILTER_OFFSET); + return filterRam[index]; + } +}; + +TEST_F(FdCanDeviceTest, initEnablesPeripheralClock) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + + EXPECT_EQ(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); +} + +TEST_F(FdCanDeviceTest, initSelectsPclk1KernelClock) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // CCIPR[25:24] should be 10b = PCLK1 + uint32_t fdcanSel = (fakeRcc.CCIPR >> 24U) & 3U; + EXPECT_EQ(fdcanSel, 2U); +} + +TEST_F(FdCanDeviceTest, initConfiguresGpioTxAlternateFunction) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // TX pin = 5 (low register AFR[0]), AF9 + uint32_t txModer = (fakeTxGpio.MODER >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); // Alternate function mode + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (cfg.txPin * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); + + // Very high speed + uint32_t txSpeed = (fakeTxGpio.OSPEEDR >> (cfg.txPin * 2U)) & 3U; + EXPECT_EQ(txSpeed, 3U); +} + +TEST_F(FdCanDeviceTest, initConfiguresGpioRxAlternateFunctionHighPin) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // RX pin = 11 (high register AFR[1]), AF9 + uint32_t rxModer = (fakeRxGpio.MODER >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxModer, 2U); // Alternate function mode + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((cfg.rxPin - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); + + // Pull-up for RX + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (cfg.rxPin * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); +} + +TEST_F(FdCanDeviceTest, initConfiguresGpioRxLowPin) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 4U; // Low pin to test AFR[0] path + cfg.rxAf = 7U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[0] >> (cfg.rxPin * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 7U); +} + +TEST_F(FdCanDeviceTest, initConfiguresGpioTxHighPin) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 12U; + cfg.txAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[1] >> ((cfg.txPin - 8U) * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); +} + +TEST_F(FdCanDeviceTest, initEntersInitMode) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // After init(), INIT bit should still be set (init mode stays until start()) + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + // CCE should be set (configuration change enabled) + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_CCE, 0U); +} + +TEST_F(FdCanDeviceTest, initDisablesFdAndBrs) +{ + auto cfg = makeDefaultConfig(); + // Pre-set FDOE and BRSE to verify init clears them + fakeFdcan.CCCR = FDCAN_CCCR_FDOE | FDCAN_CCCR_BRSE; + bios::FdCanDevice dev(cfg); + dev.init(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_FDOE, 0U); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_BRSE, 0U); +} + +TEST_F(FdCanDeviceTest, initConfiguresBitTiming) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbtp = fakeFdcan.NBTP; + uint32_t nsjw = (nbtp >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + uint32_t nbrp = (nbtp >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + uint32_t nts1 = (nbtp >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + uint32_t nts2 = (nbtp >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + + EXPECT_EQ(nsjw, cfg.nsjw); + EXPECT_EQ(nbrp, cfg.prescaler - 1U); // prescaler is encoded as (prescaler-1) + EXPECT_EQ(nts1, cfg.nts1); + EXPECT_EQ(nts2, cfg.nts2); +} + +TEST_F(FdCanDeviceTest, initConfiguresMessageRam) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // RXGFC should have LSS=0, LSE=0 initially (accept-all replaces it) + // TXBC should be 0 (FIFO mode) + EXPECT_EQ(fakeFdcan.TXBC, 0U); +} + +TEST_F(FdCanDeviceTest, initConfiguresAcceptAllFilter) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // ANFS=0 (accept non-matching std into FIFO0), ANFE=0 (accept non-matching ext into FIFO0) + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 0U); + EXPECT_EQ(anfe, 0U); +} + +TEST_F(FdCanDeviceTest, initSetsInitializedFlag) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + // Before init, start should do nothing (tested in startWithoutInitDoesNothing) + dev.init(); + // After init, start should work - verified indirectly via start() tests +} + +TEST_F(FdCanDeviceTest, doubleInitSafe) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // Second init should not crash + dev.init(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, startEnablesRxFifoInterrupt) +{ + auto dev = makeInitedDevice(); + dev->start(); + // start() enables only RF0NE (RX FIFO 0 new element). TCE is managed + // per-TX by transmit(frame, true) and disabled by transmitISR(). + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); +} + +TEST_F(FdCanDeviceTest, startSetsILS) +{ + auto dev = makeInitedDevice(); + dev->start(); + // All interrupts routed to line 0 (ILS=0) + EXPECT_EQ(fakeFdcan.ILS, 0U); +} + +TEST_F(FdCanDeviceTest, startEnablesILE) +{ + auto dev = makeInitedDevice(); + dev->start(); + // Only EINT0 enabled (all interrupts on line 0) + EXPECT_NE(fakeFdcan.ILE & FDCAN_ILE_EINT0, 0U); +} + +TEST_F(FdCanDeviceTest, startSetsTXBTIE) +{ + auto dev = makeInitedDevice(); + dev->start(); + // All 3 TX buffers enabled + EXPECT_EQ(fakeFdcan.TXBTIE, 0x7U); +} + +TEST_F(FdCanDeviceTest, startLeavesInitMode) +{ + auto dev = makeInitedDevice(); + dev->start(); + // INIT bit should be cleared + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, startWithoutInitDoesNothing) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + // Do NOT call init() + dev.start(); + // IE should remain 0 - start() returned early + EXPECT_EQ(fakeFdcan.IE, 0U); + EXPECT_EQ(fakeFdcan.ILS, 0U); + EXPECT_EQ(fakeFdcan.ILE, 0U); +} + +TEST_F(FdCanDeviceTest, stopDisablesInterrupts) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, stopEntersInitMode) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, transmitReturnsTrueOnSuccess) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); // 3 free slots, put index 0 + + uint8_t data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + ::can::CANFrame frame(0x100, data, 8U); + + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, transmitReturnsFalseWhenFifoFull) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(0U, 0U); // 0 free slots + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 8U); + + EXPECT_FALSE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, transmitSetsCorrectStandardId) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x123, data, 8U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + // Standard ID: shifted left by 18, no XTD bit + EXPECT_EQ(txBuf[0], (0x123U << 18U)); +} + +TEST_F(FdCanDeviceTest, transmitSetsExtendedId) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + // Extended ID has bit 31 set (0x80000000) + uint32_t extId = 0x80000000U | 0x12345U; + ::can::CANFrame frame(extId, data, 8U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + // Extended: raw 29-bit ID in bits [28:0], XTD bit at [30] + EXPECT_EQ(txBuf[0], (0x12345U) | (1U << 30U)); +} + +TEST_F(FdCanDeviceTest, transmitSetsCorrectDlc) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 5U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + uint8_t dlc = static_cast((txBuf[1] >> 16U) & 0xFU); + EXPECT_EQ(dlc, 5U); +} + +TEST_F(FdCanDeviceTest, transmitCopiesPayloadBytes) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + ::can::CANFrame frame(0x100, data, 8U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + // Word 2: bytes 0-3 in little-endian order + EXPECT_EQ(txBuf[2], 0x04030201U); + // Word 3: bytes 4-7 + EXPECT_EQ(txBuf[3], 0x08070605U); +} + +TEST_F(FdCanDeviceTest, transmitSetsRequestBit) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 1U); // put index = 1 + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 8U); + dev->transmit(frame); + + // TXBAR bit 1 should be set + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 1U)); +} + +TEST_F(FdCanDeviceTest, transmitUsesCorrectPutIndex) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(2U, 2U); // put index = 2 + + uint8_t data[8] = {0xAA}; + ::can::CANFrame frame(0x200, data, 1U); + dev->transmit(frame); + + // Data should be written at TX buffer index 2 + uint32_t const* txBuf = getTxBuffer(2); + EXPECT_EQ(txBuf[0], (0x200U << 18U)); + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 2U)); +} + +TEST_F(FdCanDeviceTest, transmitMultipleFramesUseDifferentBuffers) +{ + auto dev = makeStartedDevice(); + + uint8_t data1[8] = {0x11}; + uint8_t data2[8] = {0x22}; + + // First frame at put index 0 + setTxFifoStatus(3U, 0U); + ::can::CANFrame frame1(0x100, data1, 1U); + dev->transmit(frame1); + + // Second frame at put index 1 + setTxFifoStatus(2U, 1U); + ::can::CANFrame frame2(0x200, data2, 1U); + dev->transmit(frame2); + + uint32_t const* buf0 = getTxBuffer(0); + uint32_t const* buf1 = getTxBuffer(1); + EXPECT_EQ(buf0[0], (0x100U << 18U)); + EXPECT_EQ(buf1[0], (0x200U << 18U)); +} + +TEST_F(FdCanDeviceTest, transmitSingleFrame) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(1U, 0U); + + uint8_t data[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE}; + ::can::CANFrame frame(0x7FF, data, 8U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t const* txBuf = getTxBuffer(0); + EXPECT_EQ(txBuf[0], (0x7FFU << 18U)); + EXPECT_EQ(static_cast((txBuf[1] >> 16U) & 0xFU), 8U); + EXPECT_EQ(txBuf[2], 0xEFBEADDEU); + EXPECT_EQ(txBuf[3], 0xBEBAFECAU); +} + +TEST_F(FdCanDeviceTest, transmitWithZeroPayload) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t const* txBuf = getTxBuffer(0); + uint8_t dlc = static_cast((txBuf[1] >> 16U) & 0xFU); + EXPECT_EQ(dlc, 0U); +} + +TEST_F(FdCanDeviceTest, transmitWithMaxPayload) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {0xFF, 0xFE, 0xFD, 0xFC, 0xFB, 0xFA, 0xF9, 0xF8}; + ::can::CANFrame frame(0x100, data, 8U); + EXPECT_TRUE(dev->transmit(frame)); + + uint32_t const* txBuf = getTxBuffer(0); + uint8_t dlc = static_cast((txBuf[1] >> 16U) & 0xFU); + EXPECT_EQ(dlc, 8U); + EXPECT_EQ(txBuf[2], 0xFCFDFEFFU); + EXPECT_EQ(txBuf[3], 0xF8F9FAFBU); +} + +TEST_F(FdCanDeviceTest, receiveISRDrainsFifo) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 1U); +} + +TEST_F(FdCanDeviceTest, receiveISRExtractsStandardId) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x321, false, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x321U); +} + +TEST_F(FdCanDeviceTest, receiveISRExtractsExtendedId) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x1ABCDU, true, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + // Extended IDs have bit 31 set in the CANFrame representation + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x80000000U | 0x1ABCDU); +} + +TEST_F(FdCanDeviceTest, receiveISRExtractsDlc) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 5, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxFrame(0).getPayloadLength(), 5U); +} + +TEST_F(FdCanDeviceTest, receiveISRExtractsPayloadBytes) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + uint8_t const* payload = dev->getRxFrame(0).getPayload(); + EXPECT_EQ(payload[0], 0xAAU); + EXPECT_EQ(payload[1], 0xBBU); + EXPECT_EQ(payload[2], 0xCCU); + EXPECT_EQ(payload[3], 0xDDU); + EXPECT_EQ(payload[4], 0xEEU); + EXPECT_EQ(payload[5], 0xFFU); + EXPECT_EQ(payload[6], 0x11U); + EXPECT_EQ(payload[7], 0x22U); +} + +TEST_F(FdCanDeviceTest, receiveISRAcknowledgesFrame) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + // RXF0A should have been written with the get index (0) + EXPECT_EQ(fakeFdcan.RXF0A, 0U); +} + +TEST_F(FdCanDeviceTest, receiveISRReturnsFrameCount) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + // Place 2 frames (fill level = 2, both at index 0 since fake doesn't auto-advance) + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(2U, 0U); + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 2U); +} + +TEST_F(FdCanDeviceTest, receiveISRDropsWhenQueueFull) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + + // Fill the software RX queue to capacity (32 frames) + for (uint32_t i = 0U; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, static_cast(0x100 + i), false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + + // Now try to receive one more - should be acknowledged but dropped + placeRxFrame(0, 0x999, false, 8, data); + setRxFifoStatus(1U, 0U); + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 0U); + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); +} + +TEST_F(FdCanDeviceTest, receiveISRWithNullFilterAcceptsAll) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x7FF, false, 8, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 1U); +} + +TEST_F(FdCanDeviceTest, receiveISRWithFilterRejectsUnmatched) +{ + auto dev = makeStartedDevice(); + + // Create a filter bit field where only ID 0x100 is accepted + uint8_t filter[256] = {}; + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); + + // Place a frame with ID 0x200 (not in filter) + uint8_t data[8] = {}; + placeRxFrame(0, 0x200, false, 8, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(filter); + EXPECT_EQ(received, 0U); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, receiveISRWithFilterAcceptsMatched) +{ + auto dev = makeStartedDevice(); + + uint8_t filter[256] = {}; + filter[0x100 / 8U] |= (1U << (0x100 % 8U)); + + uint8_t data[8] = {0x42}; + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(filter); + EXPECT_EQ(received, 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); +} + +TEST_F(FdCanDeviceTest, receiveISRSnapshotsFillLevel) +{ + auto dev = makeStartedDevice(); + + // Set fill level to 2 - the ISR should drain exactly 2, not re-read F0FL + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(2U, 0U); + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 2U); +} + +TEST_F(FdCanDeviceTest, receiveISRClearsInterruptFlags) +{ + auto dev = makeStartedDevice(); + + // Pre-set interrupt flags + fakeFdcan.IR = FDCAN_IR_RF0N | FDCAN_IR_RF0F | FDCAN_IR_RF0L; + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + // IR should have RF0N|RF0F|RF0L written (write-1-to-clear) + // In our fake, the last write to IR is the clear value + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_RF0N | FDCAN_IR_RF0F | FDCAN_IR_RF0L); +} + +TEST_F(FdCanDeviceTest, receiveISRWithEmptyFifo) +{ + auto dev = makeStartedDevice(); + setRxFifoStatus(0U, 0U); // F0FL = 0 + + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 0U); +} + +TEST_F(FdCanDeviceTest, getRxCountReturnsZeroInitially) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, getRxFrameReturnsCorrectFrame) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {0xDE, 0xAD}; + placeRxFrame(0, 0x123, false, 2, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x123U); + EXPECT_EQ(frame.getPayloadLength(), 2U); + EXPECT_EQ(frame.getPayload()[0], 0xDEU); + EXPECT_EQ(frame.getPayload()[1], 0xADU); +} + +TEST_F(FdCanDeviceTest, getRxCountAfterReceive) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueResetsCount) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + EXPECT_EQ(dev->getRxCount(), 1U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, rxQueueWrapsAround) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill queue to capacity + for (uint32_t i = 0U; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + + // Clear and receive more - should wrap around + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + // Receive 5 more (these wrap into the beginning of the buffer) + for (uint32_t i = 0U; i < 5U; i++) + { + placeRxFrame(0, 0x500U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 5U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x500U); + EXPECT_EQ(dev->getRxFrame(4).getId(), 0x504U); +} + +TEST_F(FdCanDeviceTest, rxQueueCapacity) { EXPECT_EQ(bios::FdCanDevice::RX_QUEUE_SIZE, 32U); } + +TEST_F(FdCanDeviceTest, disableRxInterruptClearsRF0NE) +{ + auto dev = makeStartedDevice(); + // After start, RF0NE should be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + + dev->disableRxInterrupt(); + // start() only enabled RF0NE, so IE is now empty + EXPECT_EQ(fakeFdcan.IE, 0U); +} + +TEST_F(FdCanDeviceTest, enableRxInterruptSetsRF0NE) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + + dev->enableRxInterrupt(); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, transmitISRClearsTCFlag) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + // Only the TC flag is cleared (write-1-to-clear) + EXPECT_NE(fakeFdcan.IR & FDCAN_IR_TC, 0U); +} + +TEST_F(FdCanDeviceTest, transmitISRWithNoPendingTx) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + // Should not crash + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +// NOTE: transmitISR() does not drain the TX Event FIFO - it only disables TCE +// and clears the TC flag (matching the S32K FlexCAN transmitISR pattern). + +TEST_F(FdCanDeviceTest, transmitISRDoesNotDrainTxEventFifo) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + fakeFdcan.TXEFA = 0xDEADBEEFU; // sentinel - should NOT be written + + dev->transmitISR(); + + // TXEFA should be untouched (no drain) + EXPECT_EQ(fakeFdcan.TXEFA, 0xDEADBEEFU); + // TC flag still cleared + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, isBusOffReturnsTrueWhenBOSet) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = FDCAN_PSR_BO; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, isBusOffReturnsFalseNormally) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = 0U; + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, getTxErrorCounterReadsECR) +{ + auto dev = makeStartedDevice(); + // TEC is in ECR[7:0] + fakeFdcan.ECR = 42U; + EXPECT_EQ(dev->getTxErrorCounter(), 42U); +} + +TEST_F(FdCanDeviceTest, getTxErrorCounterMaxValue) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0xFFU; // max TEC + EXPECT_EQ(dev->getTxErrorCounter(), 255U); +} + +TEST_F(FdCanDeviceTest, getRxErrorCounterReadsECR) +{ + auto dev = makeStartedDevice(); + // REC is in ECR[14:8] + fakeFdcan.ECR = (96U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 96U); +} + +TEST_F(FdCanDeviceTest, getRxErrorCounterMaxValue) +{ + auto dev = makeStartedDevice(); + // REC max is 127 (7 bits) + fakeFdcan.ECR = (0x7FU << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 127U); +} + +TEST_F(FdCanDeviceTest, errorCountersBothReadable) +{ + auto dev = makeStartedDevice(); + // TEC = 100, REC = 50 + fakeFdcan.ECR = 100U | (50U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 100U); + EXPECT_EQ(dev->getRxErrorCounter(), 50U); +} + +TEST_F(FdCanDeviceTest, configureAcceptAllFilterSetsANFS0ANFE0) +{ + auto dev = makeInitedDevice(); + // Set non-zero first to verify it gets overwritten + fakeFdcan.RXGFC = 0xFFFFFFFFU; + dev->configureAcceptAllFilter(); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 0U); + EXPECT_EQ(anfe, 0U); +} + +TEST_F(FdCanDeviceTest, configureFilterListRejectsNonMatching) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x200}; + dev->configureFilterList(idList, 2U); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 2U); // Reject non-matching standard + EXPECT_EQ(anfe, 2U); // Reject non-matching extended +} + +TEST_F(FdCanDeviceTest, configureFilterListSetsLSSCount) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x200, 0x300}; + dev->configureFilterList(idList, 3U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 3U); +} + +TEST_F(FdCanDeviceTest, configureFilterListWritesFilterElements) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x2AB}; + dev->configureFilterList(idList, 2U); + + // Filter element format: SFT(2=classic)<<30 | SFEC(1)<<27 | SFID1<<16 | SFID2(mask) + uint32_t f0 = getStdFilter(0); + EXPECT_EQ(f0, (2U << 30U) | (1U << 27U) | (0x100U << 16U) | 0x7FFU); + + uint32_t f1 = getStdFilter(1); + EXPECT_EQ(f1, (2U << 30U) | (1U << 27U) | (0x2ABU << 16U) | 0x7FFU); +} + +TEST_F(FdCanDeviceTest, configureFilterListCapsAt28) +{ + auto dev = makeInitedDevice(); + + // Create 30 IDs (exceeds 28 max) + uint32_t idList[30]; + for (uint32_t i = 0; i < 30; i++) + { + idList[i] = 0x100U + i; + } + dev->configureFilterList(idList, 30U); + + // Only 28 elements should be written; element 28 and 29 should be zero + uint32_t f27 = getStdFilter(27); + EXPECT_NE(f27, 0U); // element 27 should exist + + // Element at index 28 should NOT have been written (stays 0 from memset) + uint32_t f28 = getStdFilter(28); + EXPECT_EQ(f28, 0U); +} + +TEST_F(FdCanDeviceTest, configureFilterListAcceptsConfiguredIds) +{ + // This is more of an integration test: configure filter, then verify + // the RXGFC is set to reject non-matching but LSS covers our list + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x200, 0x300, 0x400, 0x500}; + dev->configureFilterList(idList, 5U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 5U); + + // Verify each filter element + for (uint8_t i = 0; i < 5; i++) + { + uint32_t f = getStdFilter(i); + uint32_t sfid = (f >> 16U) & 0x7FFU; + EXPECT_EQ(sfid, idList[i]); + } +} + +TEST_F(FdCanDeviceTest, configureBitTimingWritesNBTP) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 8U; + cfg.nts1 = 63U; + cfg.nts2 = 15U; + cfg.nsjw = 7U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbtp = fakeFdcan.NBTP; + EXPECT_NE(nbtp, 0U); +} + +TEST_F(FdCanDeviceTest, prescalerEncodedCorrectly) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 10U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 9U); // prescaler - 1 +} + +TEST_F(FdCanDeviceTest, tseg1Encoded) +{ + auto cfg = makeDefaultConfig(); + cfg.nts1 = 47U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts1 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + EXPECT_EQ(nts1, 47U); +} + +TEST_F(FdCanDeviceTest, tseg2Encoded) +{ + auto cfg = makeDefaultConfig(); + cfg.nts2 = 5U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts2 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + EXPECT_EQ(nts2, 5U); +} + +TEST_F(FdCanDeviceTest, sjwEncoded) +{ + auto cfg = makeDefaultConfig(); + cfg.nsjw = 3U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nsjw = (fakeFdcan.NBTP >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + EXPECT_EQ(nsjw, 3U); +} + +TEST_F(FdCanDeviceTest, bitTimingAllFieldsPackedCorrectly) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 2U; // BRP=1 + cfg.nts1 = 10U; + cfg.nts2 = 3U; + cfg.nsjw = 2U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t expected = (2U << FDCAN_NBTP_NSJW_Pos) | (1U << FDCAN_NBTP_NBRP_Pos) // prescaler - 1 + | (10U << FDCAN_NBTP_NTSEG1_Pos) | (3U << FDCAN_NBTP_NTSEG2_Pos); + EXPECT_EQ(fakeFdcan.NBTP, expected); +} + +TEST_F(FdCanDeviceTest, constructorZerosRxQueue) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, multipleReceiveCycles) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0x01}; + + // Receive 3 frames + for (uint32_t i = 0; i < 3; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 3U); + + // Verify all 3 frames + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x101U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x102U); + + // Clear and receive more + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + placeRxFrame(0, 0x200, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(FdCanDeviceTest, stopThenRestartSequence) +{ + auto dev = makeStartedDevice(); + + // Stop + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + + // Re-start + dev->start(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); +} + +TEST_F(FdCanDeviceTest, transmitAfterStopAndRestart) +{ + auto dev = makeStartedDevice(); + + dev->stop(); + dev->start(); + + setTxFifoStatus(3U, 0U); + uint8_t data[8] = {0x42}; + ::can::CANFrame frame(0x100, data, 1U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, filterBitFieldEdgeCases) +{ + auto dev = makeStartedDevice(); + + // Test ID 0 (first bit of first byte) + uint8_t filter[256] = {}; + filter[0] = 0x01U; // Accept ID 0 + + uint8_t data[8] = {}; + placeRxFrame(0, 0, false, 1, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(filter); + EXPECT_EQ(received, 1U); +} + +TEST_F(FdCanDeviceTest, filterBitFieldHighId) +{ + auto dev = makeStartedDevice(); + + // Test ID 0x7FF (max standard ID) + uint8_t filter[256] = {}; + filter[0x7FF / 8U] |= (1U << (0x7FF % 8U)); + + uint8_t data[8] = {}; + placeRxFrame(0, 0x7FF, false, 1, data); + setRxFifoStatus(1U, 0U); + + uint8_t received = dev->receiveISR(filter); + EXPECT_EQ(received, 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x7FFU); +} + +TEST_F(FdCanDeviceTest, rxQueueFullThenPartialDrain) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to capacity + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + + // Read some frames + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + EXPECT_EQ(dev->getRxFrame(31).getId(), 0x11FU); + + // Clear all and verify empty + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, extendedIdFullRange) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + // Test with maximum extended ID + uint32_t maxExtId = 0x1FFFFFFFU; + placeRxFrame(0, maxExtId, true, 8, data); + setRxFifoStatus(1U, 0U); + + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x80000000U | maxExtId); +} + +TEST_F(FdCanDeviceTest, transmitExtendedIdFullRange) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + uint32_t extId = 0x80000000U | 0x1FFFFFFFU; + ::can::CANFrame frame(extId, data, 0U); + dev->transmit(frame); + + uint32_t const* txBuf = getTxBuffer(0); + EXPECT_EQ(txBuf[0], 0x1FFFFFFFU | (1U << 30U)); +} + +TEST_F(FdCanDeviceTest, disableEnableRxInterruptIdempotent) +{ + auto dev = makeStartedDevice(); + + dev->disableRxInterrupt(); + dev->disableRxInterrupt(); // double disable should be safe + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + + dev->enableRxInterrupt(); + dev->enableRxInterrupt(); // double enable should be safe + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, isBusOffWithOtherBitsSet) +{ + auto dev = makeStartedDevice(); + // Set BO along with other PSR bits - should still detect bus-off + fakeFdcan.PSR = 0xFFFFU; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, errorCountersWithBothFieldsNonZero) +{ + auto dev = makeStartedDevice(); + // TEC=200, REC=100 + fakeFdcan.ECR = 200U | (100U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 200U); + EXPECT_EQ(dev->getRxErrorCounter(), 100U); +} + +TEST_F(FdCanDeviceTest, errorCountersZero) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, receiveISRMultipleFramesDifferentIds) +{ + auto dev = makeStartedDevice(); + + // Simulate 3 frames in FIFO (all at index 0 since our fake doesn't auto-advance) + uint8_t data1[8] = {0x01}; + uint8_t data2[8] = {0x02}; + uint8_t data3[8] = {0x03}; + + // First batch + placeRxFrame(0, 0x100, false, 1, data1); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + placeRxFrame(0, 0x200, false, 1, data2); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + placeRxFrame(0, 0x300, false, 1, data3); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getPayload()[0], 0x01U); + EXPECT_EQ(dev->getRxFrame(1).getPayload()[0], 0x02U); + EXPECT_EQ(dev->getRxFrame(2).getPayload()[0], 0x03U); +} + +TEST_F(FdCanDeviceTest, configureFilterListEmptyList) +{ + auto dev = makeInitedDevice(); + + // Zero-length filter list + dev->configureFilterList(nullptr, 0U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 0U); + + // ANFS and ANFE should both be 2 (reject) + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 2U); + EXPECT_EQ(anfe, 2U); +} + +TEST_F(FdCanDeviceTest, configureFilterListSingleId) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x7FF}; + dev->configureFilterList(idList, 1U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 1U); + + uint32_t f0 = getStdFilter(0); + EXPECT_EQ(f0, (2U << 30U) | (1U << 27U) | (0x7FFU << 16U) | 0x7FFU); +} + +TEST_F(FdCanDeviceTest, getRxFrameIndexWraps) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill half the queue + for (uint32_t i = 0; i < 16; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + // Clear (advances head to 16) + dev->clearRxQueue(); + + // Fill past the end of the array (head=16, fill 20 more -> wraps around) + for (uint32_t i = 0; i < 20; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 20U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(19).getId(), 0x213U); +} + +TEST_F(FdCanDeviceTest, initWithPrescaler1) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; // BRP = 0 + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 0U); +} + +TEST_F(FdCanDeviceTest, initWithPrescaler512) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 512U; // BRP = 511 (max 9-bit field) + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 511U); +} + +TEST_F(FdCanDeviceTest, initWithMaxTseg1) +{ + auto cfg = makeDefaultConfig(); + cfg.nts1 = 255U; // max 8-bit field + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts1 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + EXPECT_EQ(nts1, 255U); +} + +TEST_F(FdCanDeviceTest, initWithMaxTseg2) +{ + auto cfg = makeDefaultConfig(); + cfg.nts2 = 127U; // max 7-bit field + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts2 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + EXPECT_EQ(nts2, 127U); +} + +TEST_F(FdCanDeviceTest, initWithMaxSjw) +{ + auto cfg = makeDefaultConfig(); + cfg.nsjw = 127U; // max 7-bit field + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nsjw = (fakeFdcan.NBTP >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + EXPECT_EQ(nsjw, 127U); +} + +TEST_F(FdCanDeviceTest, initGpioTxPin0) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 0U; + cfg.txAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (0U * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); + uint32_t txModer = (fakeTxGpio.MODER >> (0U * 2U)) & 3U; + EXPECT_EQ(txModer, 2U); +} + +TEST_F(FdCanDeviceTest, initGpioTxPin7) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 7U; + cfg.txAf = 11U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[0] >> (7U * 4U)) & 0xFU; + EXPECT_EQ(txAf, 11U); +} + +TEST_F(FdCanDeviceTest, initGpioTxPin8) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 8U; + cfg.txAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[1] >> ((8U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); +} + +TEST_F(FdCanDeviceTest, initGpioTxPin15) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 15U; + cfg.txAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txAf = (fakeTxGpio.AFR[1] >> ((15U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(txAf, 9U); +} + +TEST_F(FdCanDeviceTest, initGpioRxPin0) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 0U; + cfg.rxAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[0] >> (0U * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (0U * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); +} + +TEST_F(FdCanDeviceTest, initGpioRxPin7) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 7U; + cfg.rxAf = 12U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[0] >> (7U * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 12U); +} + +TEST_F(FdCanDeviceTest, initGpioRxPin8) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 8U; + cfg.rxAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((8U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); +} + +TEST_F(FdCanDeviceTest, initGpioRxPin15) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 15U; + cfg.rxAf = 9U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxAf = (fakeRxGpio.AFR[1] >> ((15U - 8U) * 4U)) & 0xFU; + EXPECT_EQ(rxAf, 9U); +} + +TEST_F(FdCanDeviceTest, initCCESetAfterInit) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // CCE must be set for configuration change + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_CCE, 0U); +} + +TEST_F(FdCanDeviceTest, doubleInitDoesNotBreakBitTiming) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 6U; + cfg.nts1 = 20U; + cfg.nts2 = 4U; + cfg.nsjw = 2U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbtpFirst = fakeFdcan.NBTP; + dev.init(); + EXPECT_EQ(fakeFdcan.NBTP, nbtpFirst); +} + +TEST_F(FdCanDeviceTest, doubleInitPreservesGpioConfig) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txModerFirst = fakeTxGpio.MODER; + uint32_t rxModerFirst = fakeRxGpio.MODER; + dev.init(); + EXPECT_EQ(fakeTxGpio.MODER, txModerFirst); + EXPECT_EQ(fakeRxGpio.MODER, rxModerFirst); +} + +TEST_F(FdCanDeviceTest, initClockEnableBitPreserved) +{ + // Pre-set some other APB1ENR1 bits - init should not clear them + fakeRcc.APB1ENR1 = 0x00000001U; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // FDCANEN should be set AND the pre-existing bit preserved + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); + EXPECT_NE(fakeRcc.APB1ENR1 & 0x00000001U, 0U); +} + +TEST_F(FdCanDeviceTest, initWithAllBitTimingZero) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + cfg.nts1 = 0U; + cfg.nts2 = 0U; + cfg.nsjw = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + // NBTP should be 0 (prescaler-1=0, all others 0) + EXPECT_EQ(fakeFdcan.NBTP, 0U); +} + +TEST_F(FdCanDeviceTest, initTxGpioSpeedIsVeryHigh) +{ + auto cfg = makeDefaultConfig(); + cfg.txPin = 3U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t txSpeed = (fakeTxGpio.OSPEEDR >> (3U * 2U)) & 3U; + EXPECT_EQ(txSpeed, 3U); // Very high speed +} + +TEST_F(FdCanDeviceTest, initRxGpioPullUpEnabled) +{ + auto cfg = makeDefaultConfig(); + cfg.rxPin = 6U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t rxPupdr = (fakeRxGpio.PUPDR >> (6U * 2U)) & 3U; + EXPECT_EQ(rxPupdr, 1U); // Pull-up +} + +TEST_F(FdCanDeviceTest, startWithoutInitLeavesINITUntouched) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + // INIT starts at 0 (not initialized) + fakeFdcan.CCCR = 0U; + dev.start(); + // start() returned early - CCCR unchanged + EXPECT_EQ(fakeFdcan.CCCR, 0U); +} + +TEST_F(FdCanDeviceTest, startWithoutInitLeavesTXBTIEZero) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.start(); + EXPECT_EQ(fakeFdcan.TXBTIE, 0U); +} + +TEST_F(FdCanDeviceTest, doubleStartDoesNotCrash) +{ + auto dev = makeInitedDevice(); + dev->start(); + dev->start(); // second start + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, doubleStartIEPreserved) +{ + auto dev = makeInitedDevice(); + dev->start(); + uint32_t ieFirst = fakeFdcan.IE; + dev->start(); + EXPECT_EQ(fakeFdcan.IE, ieFirst); +} + +TEST_F(FdCanDeviceTest, stopWithoutStartSafe) +{ + auto dev = makeInitedDevice(); + // init was called but not start - stop should not crash + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, stopClearsRF0NE) +{ + auto dev = makeStartedDevice(); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + dev->stop(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, stopClearsTCE) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, stopThenStartIERestored) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + dev->start(); + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); +} + +TEST_F(FdCanDeviceTest, stopThenStartTXBTIERestored) +{ + auto dev = makeStartedDevice(); + dev->stop(); + dev->start(); + EXPECT_EQ(fakeFdcan.TXBTIE, 0x7U); +} + +TEST_F(FdCanDeviceTest, stopThenStartILERestored) +{ + auto dev = makeStartedDevice(); + dev->stop(); + dev->start(); + EXPECT_NE(fakeFdcan.ILE & FDCAN_ILE_EINT0, 0U); +} + +TEST_F(FdCanDeviceTest, multipleStopStartCycles) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 5; i++) + { + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + dev->start(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + } +} + +TEST_F(FdCanDeviceTest, startLeavesINITClearedEvenIfCCCRHasOtherBits) +{ + auto dev = makeInitedDevice(); + // Pre-set some bits in CCCR besides INIT + fakeFdcan.CCCR |= (1U << 4U); // some random bit + dev->start(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, startSetsILSToZero) +{ + auto dev = makeInitedDevice(); + // Pre-set ILS to non-zero + fakeFdcan.ILS = 0xFFFFFFFFU; + dev->start(); + EXPECT_EQ(fakeFdcan.ILS, 0U); +} + +TEST_F(FdCanDeviceTest, startSetsOnlyEINT0) +{ + auto dev = makeInitedDevice(); + dev->start(); + // Only EINT0 should be enabled, not EINT1 + EXPECT_NE(fakeFdcan.ILE & FDCAN_ILE_EINT0, 0U); + // ILE should be exactly EINT0 + EXPECT_EQ(fakeFdcan.ILE, FDCAN_ILE_EINT0); +} + +TEST_F(FdCanDeviceTest, stopDoesNotClearILE) +{ + auto dev = makeStartedDevice(); + dev->stop(); + // stop() only clears specific IE bits and enters init mode + // ILE is not explicitly cleared by stop() + // (verify the actual behavior) + // After stop, INIT should be set + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, stopDoesNotClearTXBTIE) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(fakeFdcan.TXBTIE, 0x7U); + dev->stop(); + // stop() does not explicitly clear TXBTIE + EXPECT_EQ(fakeFdcan.TXBTIE, 0x7U); +} + +TEST_F(FdCanDeviceTest, startAfterStopClearsINIT) +{ + auto dev = makeStartedDevice(); + dev->stop(); + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); + dev->start(); + EXPECT_EQ(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, initDoesNotLeaveInitMode) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // After init, INIT bit should still be set + EXPECT_NE(fakeFdcan.CCCR & FDCAN_CCCR_INIT, 0U); +} + +TEST_F(FdCanDeviceTest, startThenStopThenStartAgainIECorrect) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); + dev->stop(); + // stop() clears RF0NE and TCE + dev->start(); + EXPECT_EQ(fakeFdcan.IE, FDCAN_IE_RF0NE); +} + +TEST_F(FdCanDeviceTest, enableRxInterruptAfterStartPreservesOtherIEBits) +{ + auto dev = makeStartedDevice(); + // Enable TCE manually so there is another IE bit to preserve + fakeFdcan.IE |= FDCAN_IE_TCE; + + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + // TCE should still be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); + + dev->enableRxInterrupt(); + // Both RF0NE and TCE should be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, disableRxInterruptPreservesTCE) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IE |= FDCAN_IE_TCE; + dev->disableRxInterrupt(); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, disableRxInterruptMultipleTimes) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + dev->disableRxInterrupt(); + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, enableRxInterruptMultipleTimes) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + dev->enableRxInterrupt(); + dev->enableRxInterrupt(); + dev->enableRxInterrupt(); + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, IERegisterBitMaskRF0NE) +{ + auto dev = makeStartedDevice(); + // Verify RF0NE bit is at position 0 + EXPECT_EQ(FDCAN_IE_RF0NE, (1U << 0U)); +} + +TEST_F(FdCanDeviceTest, IERegisterBitMaskTCE) +{ + // Verify TCE bit is at position 7 + EXPECT_EQ(FDCAN_IE_TCE, (1U << 7U)); +} + +TEST_F(FdCanDeviceTest, IERegisterBitMaskTEFNE) +{ + // Verify TEFNE bit is at position 12 + EXPECT_EQ(FDCAN_IE_TEFNE, (1U << 12U)); +} + +TEST_F(FdCanDeviceTest, ILSZeroAfterStart) +{ + auto dev = makeStartedDevice(); + // All interrupts routed to line 0 + EXPECT_EQ(fakeFdcan.ILS, 0U); +} + +TEST_F(FdCanDeviceTest, ILSPreSetIsOverwritten) +{ + auto dev = makeInitedDevice(); + fakeFdcan.ILS = 0xFFFFFFFFU; + dev->start(); + EXPECT_EQ(fakeFdcan.ILS, 0U); +} + +TEST_F(FdCanDeviceTest, ILEOnlyEINT0Enabled) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(fakeFdcan.ILE, FDCAN_ILE_EINT0); +} + +TEST_F(FdCanDeviceTest, ILEPreSetIsOverwritten) +{ + auto dev = makeInitedDevice(); + fakeFdcan.ILE = FDCAN_ILE_EINT0 | FDCAN_ILE_EINT1; + dev->start(); + EXPECT_EQ(fakeFdcan.ILE, FDCAN_ILE_EINT0); +} + +TEST_F(FdCanDeviceTest, TXBTIEAllThreeBuffers) +{ + auto dev = makeStartedDevice(); + // Bits 0, 1, 2 should all be set + EXPECT_NE(fakeFdcan.TXBTIE & (1U << 0U), 0U); + EXPECT_NE(fakeFdcan.TXBTIE & (1U << 1U), 0U); + EXPECT_NE(fakeFdcan.TXBTIE & (1U << 2U), 0U); +} + +TEST_F(FdCanDeviceTest, TXBTIENoExtraBitsSet) +{ + auto dev = makeStartedDevice(); + // Only bits 0-2 should be set + EXPECT_EQ(fakeFdcan.TXBTIE & ~0x7U, 0U); +} + +TEST_F(FdCanDeviceTest, disableEnableRxInterruptCyclePreservesIE) +{ + auto dev = makeStartedDevice(); + uint32_t originalIE = fakeFdcan.IE; + + dev->disableRxInterrupt(); + dev->enableRxInterrupt(); + + EXPECT_EQ(fakeFdcan.IE, originalIE); +} + +TEST_F(FdCanDeviceTest, IEAfterStopHasRF0NECleared) +{ + auto dev = makeStartedDevice(); + dev->stop(); + // stop() clears RF0NE (and TCE) + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, ILSBitPositions) +{ + // SMSG is at position 2 + EXPECT_EQ(FDCAN_ILS_SMSG, (1U << 2U)); +} + +TEST_F(FdCanDeviceTest, ILEBitPositions) +{ + EXPECT_EQ(FDCAN_ILE_EINT0, (1U << 0U)); + EXPECT_EQ(FDCAN_ILE_EINT1, (1U << 1U)); +} + +TEST_F(FdCanDeviceTest, enableRxInterruptDoesNotSetTCE) +{ + auto dev = makeStartedDevice(); + dev->disableRxInterrupt(); + dev->enableRxInterrupt(); + // enableRxInterrupt only sets RF0NE, not TCE + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, disableRxInterruptDoesNotAffectILE) +{ + auto dev = makeStartedDevice(); + uint32_t ileBefore = fakeFdcan.ILE; + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.ILE, ileBefore); +} + +TEST_F(FdCanDeviceTest, disableRxInterruptDoesNotAffectILS) +{ + auto dev = makeStartedDevice(); + uint32_t ilsBefore = fakeFdcan.ILS; + dev->disableRxInterrupt(); + EXPECT_EQ(fakeFdcan.ILS, ilsBefore); +} + +TEST_F(FdCanDeviceTest, acceptAllFilterSetsLSSZero) +{ + auto dev = makeInitedDevice(); + dev->configureAcceptAllFilter(); + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 0U); +} + +TEST_F(FdCanDeviceTest, acceptAllFilterSetsLSEZero) +{ + auto dev = makeInitedDevice(); + dev->configureAcceptAllFilter(); + uint32_t lse = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSE_Pos) & 0xFU; + EXPECT_EQ(lse, 0U); +} + +TEST_F(FdCanDeviceTest, acceptAllFilterClearsRejectBits) +{ + auto dev = makeInitedDevice(); + fakeFdcan.RXGFC = 0xFFFFFFFFU; + dev->configureAcceptAllFilter(); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfs, 0U); + EXPECT_EQ(anfe, 0U); +} + +TEST_F(FdCanDeviceTest, filterListWith1IdWritesOneElement) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x555}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + EXPECT_NE(f0, 0U); + // Element 1 should be 0 (only 1 configured) + uint32_t f1 = getStdFilter(1); + EXPECT_EQ(f1, 0U); +} + +TEST_F(FdCanDeviceTest, filterListWith28IdsAllWritten) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[28]; + for (uint32_t i = 0; i < 28; i++) + { + idList[i] = i + 1U; + } + dev->configureFilterList(idList, 28U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 28U); + + // Verify all 28 elements are non-zero + for (uint8_t i = 0; i < 28; i++) + { + EXPECT_NE(getStdFilter(i), 0U); + } +} + +TEST_F(FdCanDeviceTest, filterListWith0IdsLSSIsZero) +{ + auto dev = makeInitedDevice(); + dev->configureFilterList(nullptr, 0U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 0U); +} + +TEST_F(FdCanDeviceTest, filterListANFSRejectBit) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 2U); +} + +TEST_F(FdCanDeviceTest, filterListANFERejectBit) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t anfe = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFE_Pos) & 3U; + EXPECT_EQ(anfe, 2U); +} + +TEST_F(FdCanDeviceTest, filterElementSFTIsClassicFilter) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sft = (f0 >> 30U) & 3U; + EXPECT_EQ(sft, 2U); // Classic filter (ID + mask) +} + +TEST_F(FdCanDeviceTest, filterElementSFECBitIsStoreInFIFO0) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfec = (f0 >> 27U) & 7U; + EXPECT_EQ(sfec, 1U); // Store in RX FIFO 0 +} + +TEST_F(FdCanDeviceTest, filterElementSFID1MatchesId) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x3AB}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x3ABU); +} + +TEST_F(FdCanDeviceTest, filterElementSFID2IsExactMatchMask) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x3AB}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid2 = f0 & 0x7FFU; + EXPECT_EQ(sfid2, 0x7FFU); // SFID2 is the mask (all 11 bits must match) +} + +TEST_F(FdCanDeviceTest, filterListIdMaskedTo11Bits) +{ + auto dev = makeInitedDevice(); + + // Pass an ID with bits above 10 set - should be masked to 11 bits + uint32_t idList[] = {0xFFF}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x7FFU); // Masked to 11 bits +} + +TEST_F(FdCanDeviceTest, filterListMultipleElementsCorrectOrder) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x001, 0x010, 0x100, 0x200, 0x7FF}; + dev->configureFilterList(idList, 5U); + + for (uint8_t i = 0; i < 5; i++) + { + uint32_t f = getStdFilter(i); + uint32_t sfid = (f >> 16U) & 0x7FFU; + EXPECT_EQ(sfid, idList[i]); + } +} + +TEST_F(FdCanDeviceTest, filterListOverwritesPreviousConfig) +{ + auto dev = makeInitedDevice(); + + uint32_t idList1[] = {0x100, 0x200, 0x300}; + dev->configureFilterList(idList1, 3U); + + uint32_t idList2[] = {0x400}; + dev->configureFilterList(idList2, 1U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x400U); +} + +TEST_F(FdCanDeviceTest, acceptAllThenFilterList) +{ + auto dev = makeInitedDevice(); + dev->configureAcceptAllFilter(); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 0U); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 2U); +} + +TEST_F(FdCanDeviceTest, filterListThenAcceptAll) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100}; + dev->configureFilterList(idList, 1U); + + uint32_t anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 2U); + + dev->configureAcceptAllFilter(); + anfs = (fakeFdcan.RXGFC >> FDCAN_RXGFC_ANFS_Pos) & 3U; + EXPECT_EQ(anfs, 0U); +} + +TEST_F(FdCanDeviceTest, filterListWith5IdsHasCorrectLSS) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x001, 0x002, 0x003, 0x004, 0x005}; + dev->configureFilterList(idList, 5U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 5U); +} + +TEST_F(FdCanDeviceTest, filterListWith10IdsHasCorrectLSS) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[10]; + for (uint32_t i = 0; i < 10; i++) + { + idList[i] = 0x100U + i; + } + dev->configureFilterList(idList, 10U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 10U); +} + +TEST_F(FdCanDeviceTest, filterListWith28IdsLSSIs28) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[28]; + for (uint32_t i = 0; i < 28; i++) + { + idList[i] = 0x100U + i; + } + dev->configureFilterList(idList, 28U); + + uint32_t lss = (fakeFdcan.RXGFC >> FDCAN_RXGFC_LSS_Pos) & 0x1FU; + EXPECT_EQ(lss, 28U); +} + +TEST_F(FdCanDeviceTest, filterListIdZero) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x000}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + uint32_t sfid2 = f0 & 0x7FFU; + EXPECT_EQ(sfid1, 0U); + EXPECT_EQ(sfid2, 0x7FFU); // SFID2 is the mask, not the ID +} + +TEST_F(FdCanDeviceTest, filterListIdMaxStandard) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x7FF}; + dev->configureFilterList(idList, 1U); + + uint32_t f0 = getStdFilter(0); + uint32_t sfid1 = (f0 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x7FFU); +} + +TEST_F(FdCanDeviceTest, filterListElement27IsLast) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[28]; + for (uint32_t i = 0; i < 28; i++) + { + idList[i] = 0x100U + i; + } + dev->configureFilterList(idList, 28U); + + uint32_t f27 = getStdFilter(27); + uint32_t sfid1 = (f27 >> 16U) & 0x7FFU; + EXPECT_EQ(sfid1, 0x100U + 27U); +} + +TEST_F(FdCanDeviceTest, filterListRXGFCFullWord) +{ + auto dev = makeInitedDevice(); + + uint32_t idList[] = {0x100, 0x200}; + dev->configureFilterList(idList, 2U); + + uint32_t expected + = (2U << FDCAN_RXGFC_ANFS_Pos) | (2U << FDCAN_RXGFC_ANFE_Pos) | (2U << FDCAN_RXGFC_LSS_Pos); + EXPECT_EQ(fakeFdcan.RXGFC, expected); +} + +TEST_F(FdCanDeviceTest, busOffWithBOBitOnly) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = FDCAN_PSR_BO; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, notBusOffWithZeroPSR) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = 0U; + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, notBusOffWithOtherBitsButNotBO) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = 0x0000007FU; // bits 0-6 set, but not bit 7 (BO) + EXPECT_FALSE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, busOffWithAllPSRBitsSet) +{ + auto dev = makeStartedDevice(); + fakeFdcan.PSR = 0xFFFFFFFFU; + EXPECT_TRUE(dev->isBusOff()); +} + +TEST_F(FdCanDeviceTest, txErrorCounterZero) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0U; + EXPECT_EQ(dev->getTxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, txErrorCounter128) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 128U; + EXPECT_EQ(dev->getTxErrorCounter(), 128U); +} + +TEST_F(FdCanDeviceTest, txErrorCounter255) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 255U; + EXPECT_EQ(dev->getTxErrorCounter(), 255U); +} + +TEST_F(FdCanDeviceTest, rxErrorCounterZero) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0U; + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, rxErrorCounter64) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = (64U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 64U); +} + +TEST_F(FdCanDeviceTest, rxErrorCounter127) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = (127U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 127U); +} + +TEST_F(FdCanDeviceTest, txErrorCounterNotAffectedByREC) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 0U | (127U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, rxErrorCounterNotAffectedByTEC) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 255U | (0U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getRxErrorCounter(), 0U); +} + +TEST_F(FdCanDeviceTest, bothErrorCountersIndependent) +{ + auto dev = makeStartedDevice(); + fakeFdcan.ECR = 42U | (99U << FDCAN_ECR_REC_Pos); + EXPECT_EQ(dev->getTxErrorCounter(), 42U); + EXPECT_EQ(dev->getRxErrorCounter(), 99U); +} + +TEST_F(FdCanDeviceTest, PSRBOPositionCorrect) { EXPECT_EQ(FDCAN_PSR_BO, (1U << 7U)); } + +TEST_F(FdCanDeviceTest, ECRFieldPositionsCorrect) +{ + EXPECT_EQ(FDCAN_ECR_TEC_Pos, 0U); + EXPECT_EQ(FDCAN_ECR_REC_Pos, 8U); +} + +TEST_F(FdCanDeviceTest, getRxCountEmptyAfterConstruction) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + EXPECT_EQ(dev.getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueOnEmpty) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(dev->getRxCount(), 0U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueDoubleClear) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueAfterPartialRead) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Add 5 frames + for (uint32_t i = 0; i < 5; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 5U); + + // Read a frame (does not consume it) + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x102U); + + // Clear all + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, getRxCountAfterMultipleReceives) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint32_t i = 0; i < 10; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 10U); +} + +TEST_F(FdCanDeviceTest, getRxFrameWrappingAtBoundary) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to exactly RX_QUEUE_SIZE + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + + // Clear + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + + // Receive 5 more - these wrap around + for (uint32_t i = 0; i < 5; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 5U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(4).getId(), 0x204U); +} + +TEST_F(FdCanDeviceTest, queueHeadAdvancesOnClear) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Add 10 frames + for (uint32_t i = 0; i < 10; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + dev->clearRxQueue(); + + // Add 5 more - should start from new head position + for (uint32_t i = 0; i < 5; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 5U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(FdCanDeviceTest, multipleClearCycles) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (int cycle = 0; cycle < 10; cycle++) + { + placeRxFrame(0, static_cast(0x100 + cycle), false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), 1U); + EXPECT_EQ(dev->getRxFrame(0).getId(), static_cast(0x100 + cycle)); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + } +} + +TEST_F(FdCanDeviceTest, getRxFrameIndex0) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {0xAA}; + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); +} + +TEST_F(FdCanDeviceTest, getRxFrameLastIndex) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + + EXPECT_EQ( + dev->getRxFrame(bios::FdCanDevice::RX_QUEUE_SIZE - 1).getId(), + 0x100U + bios::FdCanDevice::RX_QUEUE_SIZE - 1); +} + +TEST_F(FdCanDeviceTest, rxQueueDropsWhenFullAndContinuesAcknowledging) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to capacity + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + + // Try one more - should return 0 (dropped) but still ack + placeRxFrame(0, 0x999, false, 1, data); + setRxFifoStatus(1U, 0U); + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 0U); + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); +} + +TEST_F(FdCanDeviceTest, clearThenFillToCapacityAgain) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill to capacity + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + + dev->clearRxQueue(); + + // Fill again + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(FdCanDeviceTest, getRxCountIncrementsByOne) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint32_t i = 0; i < 5; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxCount(), i + 1U); + } +} + +TEST_F(FdCanDeviceTest, clearRxQueueAfterSingleFrame) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); +} + +TEST_F(FdCanDeviceTest, getRxFrameAfterClearAndRefill) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // First fill + placeRxFrame(0, 0x100, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x100U); + + // Clear + dev->clearRxQueue(); + + // Refill + placeRxFrame(0, 0x200, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); +} + +TEST_F(FdCanDeviceTest, queueWrapsCorrectlyAt31To0) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + // Fill 31 frames, clear + for (uint32_t i = 0; i < 31; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + dev->clearRxQueue(); + + // Now head is at 31. Add 3 more - should wrap from 31 to 0, 1 + for (uint32_t i = 0; i < 3; i++) + { + placeRxFrame(0, 0x200U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 3U); + EXPECT_EQ(dev->getRxFrame(0).getId(), 0x200U); + EXPECT_EQ(dev->getRxFrame(1).getId(), 0x201U); + EXPECT_EQ(dev->getRxFrame(2).getId(), 0x202U); +} + +TEST_F(FdCanDeviceTest, rxQueueSizeIs32) { EXPECT_EQ(bios::FdCanDevice::RX_QUEUE_SIZE, 32U); } + +TEST_F(FdCanDeviceTest, getRxCountAfterExactlyFullQueue) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (uint32_t i = 0; i < bios::FdCanDevice::RX_QUEUE_SIZE; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), bios::FdCanDevice::RX_QUEUE_SIZE); +} + +TEST_F(FdCanDeviceTest, receiveAfterClearRxQueueWorks) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + placeRxFrame(0, 0x100, false, 8, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + dev->clearRxQueue(); + + placeRxFrame(0, 0x200, false, 8, data); + setRxFifoStatus(1U, 0U); + uint8_t received = dev->receiveISR(nullptr); + EXPECT_EQ(received, 1U); + EXPECT_EQ(dev->getRxCount(), 1U); +} + +TEST_F(FdCanDeviceTest, clearRxQueueRepeatedlySafe) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 100; i++) + { + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + } +} + +TEST_F(FdCanDeviceTest, fillClearFillClearPattern) +{ + auto dev = makeStartedDevice(); + uint8_t data[8] = {}; + + for (int round = 0; round < 5; round++) + { + for (uint32_t i = 0; i < 8; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + EXPECT_EQ(dev->getRxCount(), 8U); + dev->clearRxQueue(); + EXPECT_EQ(dev->getRxCount(), 0U); + } +} + +TEST_F(FdCanDeviceTest, getRxFramePayloadCorrectAfterWrap) +{ + auto dev = makeStartedDevice(); + + // Fill 30 frames and clear + uint8_t data[8] = {}; + for (uint32_t i = 0; i < 30; i++) + { + placeRxFrame(0, 0x100U + i, false, 1, data); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + } + dev->clearRxQueue(); + + // Now add a frame with specific payload - wraps into beginning + uint8_t payload[8] = {0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE}; + placeRxFrame(0, 0x500, false, 8, payload); + setRxFifoStatus(1U, 0U); + dev->receiveISR(nullptr); + + auto const& frame = dev->getRxFrame(0); + EXPECT_EQ(frame.getId(), 0x500U); + EXPECT_EQ(frame.getPayloadLength(), 8U); + EXPECT_EQ(frame.getPayload()[0], 0xDEU); + EXPECT_EQ(frame.getPayload()[7], 0xBEU); +} + +TEST_F(FdCanDeviceTest, txFifoFreeLevel0ReturnsFalse) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(0U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_FALSE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFifoFreeLevel1ReturnsTrue) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(1U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFifoFreeLevel2ReturnsTrue) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(2U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFifoFreeLevel3ReturnsTrue) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFifoPutIndex0) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 0U)); +} + +TEST_F(FdCanDeviceTest, txFifoPutIndex1) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 1U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 1U)); +} + +TEST_F(FdCanDeviceTest, txFifoPutIndex2) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 2U); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXBAR, (1U << 2U)); +} + +TEST_F(FdCanDeviceTest, txFifoFullThenFreeReturnsTrue) +{ + auto dev = makeStartedDevice(); + + // First: FIFO full + setTxFifoStatus(0U, 0U); + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_FALSE(dev->transmit(frame)); + + // Now: FIFO has space + setTxFifoStatus(1U, 0U); + EXPECT_TRUE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, txFQSFieldPositions) +{ + EXPECT_EQ(FDCAN_TXFQS_TFFL_Pos, 0U); + EXPECT_EQ(FDCAN_TXFQS_TFQPI_Pos, 16U); +} + +TEST_F(FdCanDeviceTest, txFifoChecksOnlyTFFL) +{ + auto dev = makeStartedDevice(); + // TFFL = 0 but other fields non-zero + fakeFdcan.TXFQS = (2U << FDCAN_TXFQS_TFQPI_Pos); + // TFFL is 0, so transmit should fail + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + EXPECT_FALSE(dev->transmit(frame)); +} + +TEST_F(FdCanDeviceTest, transmitDoesNotModifyTXFQS) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + uint32_t txfqsBefore = fakeFdcan.TXFQS; + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXFQS, txfqsBefore); +} + +TEST_F(FdCanDeviceTest, transmitSetsTXBAROnly) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(3U, 0U); + + fakeFdcan.TXBAR = 0U; + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + dev->transmit(frame); + + EXPECT_EQ(fakeFdcan.TXBAR, 1U); +} + +TEST_F(FdCanDeviceTest, transmitTXBCIsZeroForFifoMode) +{ + auto dev = makeStartedDevice(); + EXPECT_EQ(fakeFdcan.TXBC, 0U); +} + +TEST_F(FdCanDeviceTest, txFifoSequentialPutIndices) +{ + auto dev = makeStartedDevice(); + + uint8_t data[8] = {}; + ::can::CANFrame frame(0x100, data, 0U); + + for (uint8_t i = 0; i < 3; i++) + { + setTxFifoStatus(3U - i, i); + dev->transmit(frame); + EXPECT_NE(fakeFdcan.TXBAR & (1U << i), 0U); + } +} + +TEST_F(FdCanDeviceTest, transmitWithFullFifoDoesNotWriteMessageRam) +{ + auto dev = makeStartedDevice(); + setTxFifoStatus(0U, 0U); // Full + + // Zero out TXBAR + fakeFdcan.TXBAR = 0U; + + uint8_t data[8] = {0xFF}; + ::can::CANFrame frame(0x100, data, 1U); + EXPECT_FALSE(dev->transmit(frame)); + + // TXBAR should not be written + EXPECT_EQ(fakeFdcan.TXBAR, 0U); +} + +TEST_F(FdCanDeviceTest, transmitISREmptyFifoDoesNotWriteTXEFA) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + fakeFdcan.TXEFA = 0x12345678U; // Sentinel + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.TXEFA, 0x12345678U); +} + +TEST_F(FdCanDeviceTest, transmitISRMultipleCallsEmptyFifo) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 5; i++) + { + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); + } +} + +TEST_F(FdCanDeviceTest, transmitISRIRWriteIsTC) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectIEWhenTCEClear) +{ + // transmitISR() clears TCE in IE; with TCE already clear, IE is unchanged + auto dev = makeStartedDevice(); + uint32_t ieBefore = fakeFdcan.IE; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IE, ieBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectILE) +{ + auto dev = makeStartedDevice(); + uint32_t ileBefore = fakeFdcan.ILE; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.ILE, ileBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectILS) +{ + auto dev = makeStartedDevice(); + uint32_t ilsBefore = fakeFdcan.ILS; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.ILS, ilsBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectCCCR) +{ + auto dev = makeStartedDevice(); + uint32_t cccrBefore = fakeFdcan.CCCR; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.CCCR, cccrBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRDoesNotAffectTXBTIE) +{ + auto dev = makeStartedDevice(); + uint32_t txbtieBefore = fakeFdcan.TXBTIE; + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.TXBTIE, txbtieBefore); +} + +TEST_F(FdCanDeviceTest, transmitISRPreExistingIRBitsOverwritten) +{ + auto dev = makeStartedDevice(); + // Pre-set IR with some bits + fakeFdcan.IR = 0xFFFFFFFFU; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + // The last write to IR is the clear pattern + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, transmitISRCalledBeforeAnyTransmit) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + // Should be safe even if no transmit was ever done + dev->transmitISR(); + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, transmitISRTEFNBitPosition) { EXPECT_EQ(FDCAN_IR_TEFN, (1U << 12U)); } + +TEST_F(FdCanDeviceTest, transmitISRTCBitPosition) { EXPECT_EQ(FDCAN_IR_TC, (1U << 7U)); } + +TEST_F(FdCanDeviceTest, transmitISRTXEFSFieldPositions) +{ + EXPECT_EQ(FDCAN_TXEFS_EFFL_Pos, 0U); + EXPECT_EQ(FDCAN_TXEFS_EFGI_Pos, 8U); +} + +TEST_F(FdCanDeviceTest, transmitISRSequentialCallsAllClearFlags) +{ + auto dev = makeStartedDevice(); + for (int i = 0; i < 10; i++) + { + fakeFdcan.IR = 0U; + fakeFdcan.TXEFS = 0U; + dev->transmitISR(); + } + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, nbtpPrescaler1FieldValue0) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 0U); +} + +TEST_F(FdCanDeviceTest, nbtpPrescaler256FieldValue255) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 256U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbrp = (fakeFdcan.NBTP >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU; + EXPECT_EQ(nbrp, 255U); +} + +TEST_F(FdCanDeviceTest, nbtpTseg1Is0) +{ + auto cfg = makeDefaultConfig(); + cfg.nts1 = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts1 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + EXPECT_EQ(nts1, 0U); +} + +TEST_F(FdCanDeviceTest, nbtpTseg1Is128) +{ + auto cfg = makeDefaultConfig(); + cfg.nts1 = 128U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts1 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU; + EXPECT_EQ(nts1, 128U); +} + +TEST_F(FdCanDeviceTest, nbtpTseg2Is0) +{ + auto cfg = makeDefaultConfig(); + cfg.nts2 = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts2 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + EXPECT_EQ(nts2, 0U); +} + +TEST_F(FdCanDeviceTest, nbtpTseg2Is64) +{ + auto cfg = makeDefaultConfig(); + cfg.nts2 = 64U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nts2 = (fakeFdcan.NBTP >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU; + EXPECT_EQ(nts2, 64U); +} + +TEST_F(FdCanDeviceTest, nbtpSjwIs0) +{ + auto cfg = makeDefaultConfig(); + cfg.nsjw = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nsjw = (fakeFdcan.NBTP >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + EXPECT_EQ(nsjw, 0U); +} + +TEST_F(FdCanDeviceTest, nbtpSjwIs64) +{ + auto cfg = makeDefaultConfig(); + cfg.nsjw = 64U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nsjw = (fakeFdcan.NBTP >> FDCAN_NBTP_NSJW_Pos) & 0x7FU; + EXPECT_EQ(nsjw, 64U); +} + +TEST_F(FdCanDeviceTest, nbtpFieldPositions) +{ + EXPECT_EQ(FDCAN_NBTP_NTSEG2_Pos, 0U); + EXPECT_EQ(FDCAN_NBTP_NTSEG1_Pos, 8U); + EXPECT_EQ(FDCAN_NBTP_NBRP_Pos, 16U); + EXPECT_EQ(FDCAN_NBTP_NSJW_Pos, 25U); +} + +TEST_F(FdCanDeviceTest, nbtpFieldIsolation500kbps) +{ + // Typical 500kbps: prescaler=4, tseg1=13, tseg2=2, sjw=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 4U; + cfg.nts1 = 13U; + cfg.nts2 = 2U; + cfg.nsjw = 1U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t expected = (1U << FDCAN_NBTP_NSJW_Pos) | (3U << FDCAN_NBTP_NBRP_Pos) + | (13U << FDCAN_NBTP_NTSEG1_Pos) | (2U << FDCAN_NBTP_NTSEG2_Pos); + EXPECT_EQ(fakeFdcan.NBTP, expected); +} + +TEST_F(FdCanDeviceTest, nbtpFieldIsolation250kbps) +{ + // Typical 250kbps: prescaler=8, tseg1=13, tseg2=2, sjw=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 8U; + cfg.nts1 = 13U; + cfg.nts2 = 2U; + cfg.nsjw = 1U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t expected = (1U << FDCAN_NBTP_NSJW_Pos) | (7U << FDCAN_NBTP_NBRP_Pos) + | (13U << FDCAN_NBTP_NTSEG1_Pos) | (2U << FDCAN_NBTP_NTSEG2_Pos); + EXPECT_EQ(fakeFdcan.NBTP, expected); +} + +TEST_F(FdCanDeviceTest, nbtpFieldIsolation1Mbps) +{ + // Typical 1Mbps: prescaler=2, tseg1=13, tseg2=2, sjw=1 + auto cfg = makeDefaultConfig(); + cfg.prescaler = 2U; + cfg.nts1 = 13U; + cfg.nts2 = 2U; + cfg.nsjw = 1U; + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t expected = (1U << FDCAN_NBTP_NSJW_Pos) | (1U << FDCAN_NBTP_NBRP_Pos) + | (13U << FDCAN_NBTP_NTSEG1_Pos) | (2U << FDCAN_NBTP_NTSEG2_Pos); + EXPECT_EQ(fakeFdcan.NBTP, expected); +} + +TEST_F(FdCanDeviceTest, nbtpNoOverlapBetweenFields) +{ + // Set all fields to max and verify no bit overlap + auto cfg = makeDefaultConfig(); + cfg.prescaler = 512U; // BRP = 511 = 0x1FF (9 bits at pos 16) + cfg.nts1 = 255U; // 8 bits at pos 8 + cfg.nts2 = 127U; // 7 bits at pos 0 + cfg.nsjw = 127U; // 7 bits at pos 25 + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t nbtp = fakeFdcan.NBTP; + EXPECT_EQ((nbtp >> FDCAN_NBTP_NTSEG2_Pos) & 0x7FU, 127U); + EXPECT_EQ((nbtp >> FDCAN_NBTP_NTSEG1_Pos) & 0xFFU, 255U); + EXPECT_EQ((nbtp >> FDCAN_NBTP_NBRP_Pos) & 0x1FFU, 511U); + EXPECT_EQ((nbtp >> FDCAN_NBTP_NSJW_Pos) & 0x7FU, 127U); +} + +TEST_F(FdCanDeviceTest, nbtpAllFieldsMinimal) +{ + auto cfg = makeDefaultConfig(); + cfg.prescaler = 1U; + cfg.nts1 = 0U; + cfg.nts2 = 0U; + cfg.nsjw = 0U; + bios::FdCanDevice dev(cfg); + dev.init(); + + EXPECT_EQ(fakeFdcan.NBTP, 0U); +} + +TEST_F(FdCanDeviceTest, clockEnableBitPosition) +{ + EXPECT_EQ(RCC_APB1ENR1_FDCANEN_Pos, 25U); + EXPECT_EQ(RCC_APB1ENR1_FDCANEN, (1U << 25U)); +} + +TEST_F(FdCanDeviceTest, initEnablesFDCANClockInAPB1ENR1) +{ + fakeRcc.APB1ENR1 = 0U; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); +} + +TEST_F(FdCanDeviceTest, initPreservesOtherAPB1ENR1Bits) +{ + fakeRcc.APB1ENR1 = 0x0000FFFFU; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + // Original bits preserved + EXPECT_NE(fakeRcc.APB1ENR1 & 0x0000FFFFU, 0U); + // FDCANEN also set + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); +} + +TEST_F(FdCanDeviceTest, initSetsClockSelectPCLK1) +{ + fakeRcc.CCIPR = 0U; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t fdcanSel = (fakeRcc.CCIPR >> 24U) & 3U; + EXPECT_EQ(fdcanSel, 2U); // PCLK1 +} + +TEST_F(FdCanDeviceTest, initClockSelectClearsOldValue) +{ + // Pre-set clock select to HSE (00) with other bits + fakeRcc.CCIPR = (3U << 24U); // was 11b + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + uint32_t fdcanSel = (fakeRcc.CCIPR >> 24U) & 3U; + EXPECT_EQ(fdcanSel, 2U); // Should be 10b = PCLK1 +} + +TEST_F(FdCanDeviceTest, initClockSelectPreservesOtherCCIPRBits) +{ + fakeRcc.CCIPR = 0x00FFFFFFU; // bits 0-23 set + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // Lower 24 bits should still be set + EXPECT_EQ(fakeRcc.CCIPR & 0x00FFFFFFU, 0x00FFFFFFu); + // Clock select should be PCLK1 + uint32_t fdcanSel = (fakeRcc.CCIPR >> 24U) & 3U; + EXPECT_EQ(fdcanSel, 2U); +} + +TEST_F(FdCanDeviceTest, doubleInitDoesNotDoubleSetClock) +{ + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + uint32_t apb1After1 = fakeRcc.APB1ENR1; + dev.init(); + // OR-ing the same bit twice is idempotent + EXPECT_EQ(fakeRcc.APB1ENR1, apb1After1); +} + +TEST_F(FdCanDeviceTest, initClockSelectBits24And25) +{ + fakeRcc.CCIPR = 0U; + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // Bit 25 should be set, bit 24 should be clear (10b) + EXPECT_NE(fakeRcc.CCIPR & (1U << 25U), 0U); + EXPECT_EQ(fakeRcc.CCIPR & (1U << 24U), 0U); +} + +TEST_F(FdCanDeviceTest, initEnablesClockBeforeGPIO) +{ + // Verify clock is enabled (if clock wasn't enabled, GPIO writes would have no effect + // on real hardware - but we can verify the clock bit is set) + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + EXPECT_NE(fakeRcc.APB1ENR1 & RCC_APB1ENR1_FDCANEN, 0U); +} + +TEST_F(FdCanDeviceTest, initCCIPRUpperBitsUnaffected) +{ + fakeRcc.CCIPR = 0xFC000000U; // bits 26-31 set + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg); + dev.init(); + + // Bits 26-31 should still be set + EXPECT_EQ(fakeRcc.CCIPR & 0xFC000000U, 0xFC000000U); +} + +TEST_F(FdCanDeviceTest, transmitISRInvokesCallbackDelegate) +{ + bool callbackFired = false; + auto callback = ::etl::delegate::create([&callbackFired]() { callbackFired = true; }); + + auto cfg = makeDefaultConfig(); + bios::FdCanDevice dev(cfg, callback); + fakeFdcan.CCCR |= FDCAN_CCCR_INIT; + dev.init(); + dev.start(); + fakeFdcan.TXEFS = 0U; + + dev.transmitISR(); + EXPECT_TRUE(callbackFired); +} + +TEST_F(FdCanDeviceTest, transmitISRWithoutDelegateDoesNotCrash) +{ + // Construct without callback - existing API, should still work + auto dev = makeStartedDevice(); + fakeFdcan.TXEFS = 0U; + + dev->transmitISR(); // Must not crash + EXPECT_EQ(fakeFdcan.IR, FDCAN_IR_TC); +} + +TEST_F(FdCanDeviceTest, transmitWithInterruptEnablesTCE) +{ + auto dev = makeStartedDevice(); + // Clear IE to known state (start() may have set it) + fakeFdcan.IE = FDCAN_IE_RF0NE; // Only RX enabled, no TCE + // TX FIFO has space + fakeFdcan.TXFQS = 0x3U; // TFFL=3 + + ::can::CANFrame frame(0x100U, nullptr, 0U); + bool ok = dev->transmit(frame, true); + EXPECT_TRUE(ok); + // TCE should now be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, transmitWithoutInterruptDoesNotEnableTCE) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IE = FDCAN_IE_RF0NE; // Only RX, no TCE + fakeFdcan.TXFQS = 0x3U; + + ::can::CANFrame frame(0x100U, nullptr, 0U); + bool ok = dev->transmit(frame, false); + EXPECT_TRUE(ok); + // TCE should NOT be set + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} + +TEST_F(FdCanDeviceTest, transmitISRDisablesTCE) +{ + auto dev = makeStartedDevice(); + fakeFdcan.IE = FDCAN_IE_RF0NE | FDCAN_IE_TCE; // Both enabled + fakeFdcan.TXEFS = 0U; + + dev->transmitISR(); + // TCE should be cleared after ISR (match S32K disableTransmitInterrupt) + EXPECT_EQ(fakeFdcan.IE & FDCAN_IE_TCE, 0U); + // RF0NE should still be set + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); +} + +TEST_F(FdCanDeviceTest, receiveISRKeepsRF0NEEnabled) +{ + auto dev = makeStartedDevice(); + // Enable RF0NE + fakeFdcan.IE = FDCAN_IE_RF0NE | FDCAN_IE_TCE; + // Put one frame in RX FIFO + fakeFdcan.RXF0S = (1U << FDCAN_RXF0S_F0FL_Pos); // F0FL=1 + fakeFdcan.IR = FDCAN_IR_RF0N; + + // Place a valid frame in message RAM at RX FIFO0 index 0 + uint8_t data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + placeRxFrame(0, 0x100, false, 8, data); + + dev->receiveISR(nullptr); + + // receiveISR does NOT touch IE - RF0NE disable (if needed) is the + // responsibility of the CanSystem ISR trampoline, because disabling + // here could permanently block RX if the dispatch is dedup-dropped. + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_RF0NE, 0U); + // TCE should be unaffected + EXPECT_NE(fakeFdcan.IE & FDCAN_IE_TCE, 0U); +} diff --git a/platforms/stm32/bsp/bxCanTransceiver/CMakeLists.txt b/platforms/stm32/bsp/bxCanTransceiver/CMakeLists.txt new file mode 100644 index 00000000000..bf2d9028e6d --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/CMakeLists.txt @@ -0,0 +1,21 @@ +add_library(bxCanTransceiver src/can/transceiver/bxcan/BxCanTransceiver.cpp) + +if (BUILD_EXECUTABLE STREQUAL "unitTest") + target_include_directories(bxCanTransceiver PUBLIC include + test/mock/include) + + target_link_libraries( + bxCanTransceiver + PUBLIC asyncMockImpl + bsp + cpp2can + etl + bspIo + lifecycle + gmock) +else () + target_include_directories(bxCanTransceiver PUBLIC include) + + target_link_libraries(bxCanTransceiver PUBLIC async bspCan lifecycle + cpp2can) +endif () diff --git a/platforms/stm32/bsp/bxCanTransceiver/doc/index.rst b/platforms/stm32/bsp/bxCanTransceiver/doc/index.rst new file mode 100644 index 00000000000..11d899d4605 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/doc/index.rst @@ -0,0 +1,45 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +bxCanTransceiver +================ + +Overview +-------- + +The ``bxCanTransceiver`` module implements the OpenBSW +``AbstractCANTransceiver`` interface for the STM32F4 bxCAN peripheral +(classic CAN, 500 kbps). It wraps ``BxCanDevice`` (from ``bspCan``) and adds +lifecycle management, async integration, ISR dispatch and bus-off recovery. + +``init()`` initialises the hardware and moves the transceiver from CLOSED to +INITIALIZED. ``open()`` starts the device (re-initialising it first when +called from CLOSED), schedules the cyclic bus-off poll and moves to OPEN. +``close()`` stops the device, clears the TX queue and returns to CLOSED from +any state; ``shutdown()`` delegates to ``close()``. ``mute()`` and +``unmute()`` toggle between OPEN and MUTED. + +TX: ``write(frame)`` transmits fire-and-forget and notifies registered sent +listeners synchronously. ``write(frame, listener)`` queues the job in a +3-entry TX queue; the listener callback runs in task context (via +``async::execute``) after the TX interrupt, which also sends the next queued +frame. When the hardware mailboxes or the TX queue are full, +``CAN_ERR_TX_HW_QUEUE_FULL`` is returned and an overrun counter increments. + +RX: ``receiveInterrupt()`` is called from the CAN RX ISR and drains the +hardware FIFO into the device's software queue (accept-all; per-listener +filtering happens later). ``receiveTask()`` runs in task context, notifies +the registered frame listeners, clears the software queue and re-enables the +RX interrupt. + +Bus-off recovery: ``cyclicTask()`` polls the bus-off flag every 10 ms, moving +OPEN to MUTED on bus-off and back to OPEN once the bus recovers, unless the +user had explicitly called ``mute()``. diff --git a/platforms/stm32/bsp/bxCanTransceiver/include/can/transceiver/bxcan/BxCanTransceiver.h b/platforms/stm32/bsp/bxCanTransceiver/include/can/transceiver/bxcan/BxCanTransceiver.h new file mode 100644 index 00000000000..6f699030458 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/include/can/transceiver/bxcan/BxCanTransceiver.h @@ -0,0 +1,105 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace bios +{ + +/** + * CAN transceiver for STM32 bxCAN peripheral. + * + * Wraps BxCanDevice to implement the AbstractCANTransceiver interface. + * Operates in classic CAN mode at 500 kbps on STM32F4 family devices. + * + * \see AbstractCANTransceiver + * \see BxCanDevice + */ +class BxCanTransceiver : public ::can::AbstractCANTransceiver +{ +public: + BxCanTransceiver( + ::async::ContextType context, uint8_t busId, BxCanDevice::Config const& devConfig); + + ErrorCode init() override; + ErrorCode open() override; + ErrorCode open(::can::CANFrame const& frame) override; + ErrorCode close() override; + void shutdown() override; + ErrorCode mute() override; + ErrorCode unmute() override; + + /// Fire-and-forget transmit. Calls notifySentListeners() synchronously. + ErrorCode write(::can::CANFrame const& frame) override; + + /// Transmit with deferred callback. Queues {listener, frame} into fTxQueue. + /// canFrameSent() fires from task context after TX ISR, NOT from write(). + ErrorCode write(::can::CANFrame const& frame, ::can::ICANFrameSentListener& listener) override; + + uint32_t getBaudrate() const override; + uint16_t getHwQueueTimeout() const override; + + /// Number of TX frames dropped due to HW queue full or TX listener queue full. + uint32_t getOverrunCount() const { return fOverrunCount; } + + static uint8_t receiveInterrupt(uint8_t transceiverIndex); + static void transmitInterrupt(uint8_t transceiverIndex); + static void disableRxInterrupt(uint8_t transceiverIndex); + static void enableRxInterrupt(uint8_t transceiverIndex); + + void cyclicTask(); + void receiveTask(); + + /// Underlying bxCAN hardware device. Public for test access. + BxCanDevice fDevice; + +private: + /// TX job waiting for its deferred listener callback. + struct TxJobWithCallback + { + TxJobWithCallback(::can::ICANFrameSentListener& listener, ::can::CANFrame const& frame) + : _listener(listener), _frame(frame) + {} + + ::can::ICANFrameSentListener& _listener; + ::can::CANFrame _frame; + }; + + static uint32_t const TX_QUEUE_CAPACITY = 3U; + using TxQueue = ::etl::deque; + + /// Called from TX ISR - defers to task context via async::execute. + void canFrameSentCallback(); + + /// Runs in task context - pops TX queue, calls listener, sends next frame. + void canFrameSentAsyncCallback(); + + void notifyRegisteredSentListener(::can::CANFrame const& frame) { notifySentListeners(frame); } + + static BxCanTransceiver* fpTransceivers[3]; + + ::async::ContextType fContext; + ::async::TimeoutType fCyclicTimeout; + ::async::Function _cyclicTaskRunner; + ::async::Function _canFrameSent; + TxQueue fTxQueue; + uint32_t fOverrunCount; + bool fMuted; +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bxCanTransceiver/module.spec b/platforms/stm32/bsp/bxCanTransceiver/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/bxCanTransceiver/src/can/transceiver/bxcan/BxCanTransceiver.cpp b/platforms/stm32/bsp/bxCanTransceiver/src/can/transceiver/bxcan/BxCanTransceiver.cpp new file mode 100644 index 00000000000..28f390591e8 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/src/can/transceiver/bxcan/BxCanTransceiver.cpp @@ -0,0 +1,310 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +#include + +namespace bios +{ + +BxCanTransceiver* BxCanTransceiver::fpTransceivers[3] = {nullptr, nullptr, nullptr}; + +BxCanTransceiver::BxCanTransceiver( + ::async::ContextType context, uint8_t busId, BxCanDevice::Config const& devConfig) +: AbstractCANTransceiver(busId) +, fDevice(devConfig) +, fContext(context) +, fCyclicTimeout() +, _cyclicTaskRunner( + ::async::Function::CallType::create(*this)) +, _canFrameSent(::async::Function::CallType:: + create(*this)) +, fTxQueue() +, fOverrunCount(0U) +, fMuted(false) +{ + if (busId < 3U) + { + fpTransceivers[busId] = this; + } +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::init() +{ + if (getState() != State::CLOSED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + if (!fDevice.init()) + { + return ErrorCode::CAN_ERR_INIT_FAILED; + } + setState(State::INITIALIZED); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::open() +{ + State const state = getState(); + if (state != State::INITIALIZED && state != State::CLOSED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + if (state == State::CLOSED) + { + if (!fDevice.init()) + { + return ErrorCode::CAN_ERR_INIT_FAILED; + } + } + + if (!fDevice.start()) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + setState(State::OPEN); + fMuted = false; + + // Schedule periodic bus-off polling. + ::async::scheduleAtFixedRate( + fContext, _cyclicTaskRunner, fCyclicTimeout, 10U, ::async::TimeUnit::MILLISECONDS); + + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::open(::can::CANFrame const& /* frame */) +{ + // Wake-up frame not supported on bxCAN + return open(); +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::close() +{ + if (getState() == State::CLOSED) + { + return ErrorCode::CAN_ERR_OK; + } + + fCyclicTimeout.cancel(); + fDevice.stop(); + fTxQueue.clear(); + setState(State::CLOSED); + return ErrorCode::CAN_ERR_OK; +} + +void BxCanTransceiver::shutdown() { close(); } + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::mute() +{ + if (getState() != State::OPEN) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + fMuted = true; + fTxQueue.clear(); + setState(State::MUTED); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::unmute() +{ + if (getState() != State::MUTED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + fMuted = false; + setState(State::OPEN); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode BxCanTransceiver::write(::can::CANFrame const& frame) +{ + if (getState() != State::OPEN || fMuted) + { + return ErrorCode::CAN_ERR_TX_OFFLINE; + } + + if (!fDevice.transmit(frame)) + { + fOverrunCount++; + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + notifySentListeners(frame); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode +BxCanTransceiver::write(::can::CANFrame const& frame, ::can::ICANFrameSentListener& listener) +{ + if (getState() != State::OPEN || fMuted) + { + return ErrorCode::CAN_ERR_TX_OFFLINE; + } + + if (fTxQueue.full()) + { + fOverrunCount++; + notifyRegisteredSentListener(frame); + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + bool const wasEmpty = fTxQueue.empty(); + fTxQueue.emplace_back(listener, frame); + + if (!wasEmpty) + { + // Not the first in queue - will be sent from the TX ISR chain + return ErrorCode::CAN_ERR_OK; + } + + // First in queue - transmit now + if (!fDevice.transmit(frame)) + { + fTxQueue.pop_front(); + fOverrunCount++; + notifyRegisteredSentListener(frame); + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + // Wait for TX ISR -> canFrameSentCallback() -> canFrameSentAsyncCallback() + return ErrorCode::CAN_ERR_OK; +} + +uint32_t BxCanTransceiver::getBaudrate() const { return 500000U; } + +uint16_t BxCanTransceiver::getHwQueueTimeout() const { return 10U; } + +uint8_t BxCanTransceiver::receiveInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + // Accept all frames into the software queue - per-listener filtering + // is done by notifyListeners() in receiveTask(). + return fpTransceivers[transceiverIndex]->fDevice.receiveISR(nullptr); + } + return 0U; +} + +void BxCanTransceiver::transmitInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + BxCanTransceiver* self = fpTransceivers[transceiverIndex]; + self->fDevice.transmitISR(); + + if (!self->fTxQueue.empty()) + { + self->canFrameSentCallback(); + } + } +} + +void BxCanTransceiver::cyclicTask() +{ + // Check bus-off state + if (fDevice.isBusOff()) + { + if (getState() == State::OPEN) + { + setState(State::MUTED); + } + } + else if (getState() == State::MUTED && !fMuted) + { + setState(State::OPEN); + } +} + +void BxCanTransceiver::disableRxInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + fpTransceivers[transceiverIndex]->fDevice.disableRxInterrupt(); + } +} + +void BxCanTransceiver::enableRxInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + fpTransceivers[transceiverIndex]->fDevice.enableRxInterrupt(); + } +} + +void BxCanTransceiver::receiveTask() +{ + uint8_t count = fDevice.getRxCount(); + for (uint8_t i = 0U; i < count; i++) + { + notifyListeners(fDevice.getRxFrame(i)); + } + fDevice.clearRxQueue(); + + // Re-enable RX FIFO interrupt after draining queue + fDevice.enableRxInterrupt(); +} + +void BxCanTransceiver::canFrameSentCallback() { ::async::execute(fContext, _canFrameSent); } + +void BxCanTransceiver::canFrameSentAsyncCallback() +{ + if (!fTxQueue.empty()) + { + // If transceiver is no longer OPEN (bus-off, muted, closed), drop + // all queued TX jobs without calling listeners. + if (getState() != State::OPEN) + { + fTxQueue.clear(); + return; + } + + TxJobWithCallback& job = fTxQueue.front(); + ::can::CANFrame const& frame = job._frame; + ::can::ICANFrameSentListener& listener = job._listener; + fTxQueue.pop_front(); + + bool sendAgain = false; + if (!fTxQueue.empty()) + { + if (getState() == State::OPEN) + { + sendAgain = true; + } + else + { + fTxQueue.clear(); + } + } + + listener.canFrameSent(frame); + notifyRegisteredSentListener(frame); + + if (sendAgain) + { + ::can::CANFrame const& nextFrame = fTxQueue.front()._frame; + if (fDevice.transmit(nextFrame)) + { + // Wait for next TX ISR + return; + } + // HW queue full - no ISR will retrigger, clear remaining + fTxQueue.clear(); + notifyRegisteredSentListener(nextFrame); + } + } +} + +} // namespace bios diff --git a/platforms/stm32/bsp/bxCanTransceiver/test/CMakeLists.txt b/platforms/stm32/bsp/bxCanTransceiver/test/CMakeLists.txt new file mode 100644 index 00000000000..e73c320b832 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/test/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(BxCanTransceiverTest src/can/BxCanTransceiverTest.cpp) + +target_link_libraries( + BxCanTransceiverTest + PRIVATE asyncMockImpl + bsp + bxCanTransceiver + cpp2can + bspIo + lifecycle + bspMock + gmock) + +gtest_discover_tests(BxCanTransceiverTest PROPERTIES LABELS + "BxCanTransceiverTest") diff --git a/platforms/stm32/bsp/bxCanTransceiver/test/mock/include/can/BxCanDevice.h b/platforms/stm32/bsp/bxCanTransceiver/test/mock/include/can/BxCanDevice.h new file mode 100644 index 00000000000..7718745836e --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/test/mock/include/can/BxCanDevice.h @@ -0,0 +1,66 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include + +#include + +namespace bios +{ + +// GMock replacement for the register-level BxCanDevice. +class BxCanDevice +{ +public: + struct Config + { + uint32_t baseAddress; + uint32_t prescaler; + uint32_t bs1; + uint32_t bs2; + uint32_t sjw; + uint32_t rxGpioPort; + uint8_t rxPin; + uint8_t rxAf; + uint32_t txGpioPort; + uint8_t txPin; + uint8_t txAf; + }; + + static constexpr uint32_t RX_QUEUE_SIZE = 32U; + + explicit BxCanDevice(Config const& /* config */) + { + ON_CALL(*this, init()).WillByDefault(::testing::Return(true)); + ON_CALL(*this, start()).WillByDefault(::testing::Return(true)); + } + + MOCK_METHOD(bool, init, ()); + MOCK_METHOD(bool, start, ()); + MOCK_METHOD(void, stop, ()); + MOCK_METHOD(bool, transmit, (::can::CANFrame const& frame)); + MOCK_METHOD(uint8_t, receiveISR, (uint8_t const* filterBitField)); + MOCK_METHOD(void, transmitISR, ()); + MOCK_METHOD(bool, isBusOff, (), (const)); + MOCK_METHOD(uint8_t, getTxErrorCounter, (), (const)); + MOCK_METHOD(uint8_t, getRxErrorCounter, (), (const)); + MOCK_METHOD(void, configureAcceptAllFilter, ()); + MOCK_METHOD(void, configureFilterList, (uint32_t const* idList, uint8_t count)); + MOCK_METHOD(::can::CANFrame const&, getRxFrame, (uint8_t index), (const)); + MOCK_METHOD(uint8_t, getRxCount, (), (const)); + MOCK_METHOD(void, clearRxQueue, ()); + MOCK_METHOD(void, disableRxInterrupt, ()); + MOCK_METHOD(void, enableRxInterrupt, ()); +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/bxCanTransceiver/test/src/can/BxCanTransceiverTest.cpp b/platforms/stm32/bsp/bxCanTransceiver/test/src/can/BxCanTransceiverTest.cpp new file mode 100644 index 00000000000..d1447938d39 --- /dev/null +++ b/platforms/stm32/bsp/bxCanTransceiver/test/src/can/BxCanTransceiverTest.cpp @@ -0,0 +1,943 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "can/transceiver/bxcan/BxCanTransceiver.h" + +#include "async/AsyncMock.h" +#include "bsp/timer/SystemTimerMock.h" +#include "can/canframes/ICANFrameSentListener.h" + +#include +#include + +namespace +{ +using namespace ::can; +using namespace ::testing; +using namespace ::bios; + +class MockCANFrameSentListener : public ::can::ICANFrameSentListener +{ +public: + MOCK_METHOD(void, canFrameSent, (::can::CANFrame const&), (override)); +}; + +class BxCanTransceiverTest : public Test +{ +public: + ::async::AsyncMock fAsyncMock; + ::async::ContextType fAsyncContext = 0; + uint8_t fBusId = 0; + BxCanDevice::Config fDevConfig{}; +}; + +TEST_F(BxCanTransceiverTest, initFromClosed) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.init()); +} + +TEST_F(BxCanTransceiverTest, initTwiceFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.init()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, bxt.init()); +} + +TEST_F(BxCanTransceiverTest, receiveInterruptInvalidIndex) +{ + EXPECT_EQ(0U, BxCanTransceiver::receiveInterrupt(3)); +} + +TEST_F(BxCanTransceiverTest, writeFromClosedFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, bxt.write(frame)); +} + +TEST_F(BxCanTransceiverTest, writeWithListenerFromClosedFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + CANFrame frame; + MockCANFrameSentListener listener; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, bxt.write(frame, listener)); +} + +TEST_F(BxCanTransceiverTest, closeFromClosedReturnsOk) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.close()); +} + +TEST_F(BxCanTransceiverTest, muteFromClosedFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, bxt.mute()); +} + +TEST_F(BxCanTransceiverTest, unmuteFromClosedFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, bxt.unmute()); +} + +TEST_F(BxCanTransceiverTest, openFromClosedWithoutInitFails) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + // CLOSED -> open() should either re-init or fail depending on impl. + // The contract says CLOSED->open() re-inits (see impl). So this tests + // that open() from CLOSED is allowed (re-init path). + EXPECT_CALL(bxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, start()).Times(AnyNumber()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.open()); +} + +class InitedBxCanTransceiverTest : public BxCanTransceiverTest +{ +public: + InitedBxCanTransceiverTest() : fBxt(fAsyncContext, fBusId, fDevConfig) + { + EXPECT_CALL(fBxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(fBxt.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(fBxt.fDevice, stop()).Times(AnyNumber()); + EXPECT_CALL(fAsyncMock, execute(fAsyncContext, _)) + .Times(AnyNumber()) + .WillRepeatedly([](auto, auto& runnable) { runnable.execute(); }); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.init()); + } + + BxCanTransceiver fBxt; +}; + +TEST_F(InitedBxCanTransceiverTest, open) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, openTwiceFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, closeFromOpen) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); +} + +TEST_F(InitedBxCanTransceiverTest, closeFromInitializedReturnsOk) +{ + EXPECT_CALL(fBxt.fDevice, stop()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); +} + +TEST_F(InitedBxCanTransceiverTest, muteFromOpen) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); +} + +TEST_F(InitedBxCanTransceiverTest, muteFromInitializedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.mute()); +} + +TEST_F(InitedBxCanTransceiverTest, unmuteFromMuted) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.unmute()); +} + +TEST_F(InitedBxCanTransceiverTest, unmuteFromOpenFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.unmute()); +} + +TEST_F(InitedBxCanTransceiverTest, writeWhenOpen) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWhenMutedFails) +{ + CANFrame frame; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWhenFifoFull) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, getBaudrate) { EXPECT_EQ(500000U, fBxt.getBaudrate()); } + +TEST_F(InitedBxCanTransceiverTest, getHwQueueTimeout) { EXPECT_EQ(10U, fBxt.getHwQueueTimeout()); } + +TEST_F(InitedBxCanTransceiverTest, receiveTask) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + + fBxt.receiveTask(); +} + +TEST_F(InitedBxCanTransceiverTest, cyclicTaskBusOff) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(true)); + fBxt.cyclicTask(); + + // After bus-off, write should fail because state is MUTED + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, cyclicTaskBusRecovery) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Go to bus-off + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(true)).WillOnce(Return(false)); + + fBxt.cyclicTask(); // bus-off -> MUTED + fBxt.cyclicTask(); // bus-on -> OPEN (auto-recovery, not user mute) + + // Write should work again + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, shutdown) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + fBxt.shutdown(); +} + +TEST_F(InitedBxCanTransceiverTest, receiveInterrupt) +{ + EXPECT_CALL(fBxt.fDevice, receiveISR(_)).WillOnce(Return(0)); + BxCanTransceiver::receiveInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, transmitInterrupt) +{ + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerReturnsOkWhenOpen) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerWhenMutedFails) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerWhenFifoFullReturnsQueueFull) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerCallbackFromTransmitInterrupt) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // The listener should be called when transmitInterrupt fires + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, txQueueFillsToCapacity) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Queue capacity is 3 - fill it up without draining + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // 4th write should fail because queue is full + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, txQueueDrainAndRefill) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(listener, canFrameSent(_)).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Fill queue + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // Drain one via TX ISR callback + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); + + // Now one slot is free + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackSingleFrame) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackTwoFrames) +{ + CANFrame frame1; + CANFrame frame2; + MockCANFrameSentListener listener1; + MockCANFrameSentListener listener2; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame1, listener1)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame2, listener2)); + + // First TX ISR: listener1 fires + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener1, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); + + // Second TX ISR: listener2 fires + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener2, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackThreeFramesFifoOrder) +{ + CANFrame frame; + MockCANFrameSentListener listener1; + MockCANFrameSentListener listener2; + MockCANFrameSentListener listener3; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener1)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener2)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener3)); + + InSequence seq; + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener1, canFrameSent(_)); + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener2, canFrameSent(_)); + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener3, canFrameSent(_)); + + BxCanTransceiver::transmitInterrupt(fBusId); + BxCanTransceiver::transmitInterrupt(fBusId); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackEmptyQueue) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // TX ISR with nothing queued - should not crash + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackAbortedWhenMuted) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // Mute before TX ISR fires + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + + // TX ISR should still call transmitISR but listener is NOT called (muted) + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(0); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackWithFireAndForgetWrite) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); + + // TX ISR fires - no listener was registered, no crash + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackSameListenerTwice) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + EXPECT_CALL(fBxt.fDevice, transmitISR()).Times(2); + EXPECT_CALL(listener, canFrameSent(_)).Times(2); + + BxCanTransceiver::transmitInterrupt(fBusId); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, canFrameSentCallbackMixedWriteStyles) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Fire-and-forget write + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); + // Write with listener + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // TX ISR fires for the listener-based write + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, receiveInterruptPassesNullFilter) +{ + EXPECT_CALL(fBxt.fDevice, receiveISR(nullptr)).WillOnce(Return(1)); + uint8_t count = BxCanTransceiver::receiveInterrupt(fBusId); + EXPECT_EQ(1U, count); +} + +TEST_F(InitedBxCanTransceiverTest, receiveInterruptUnregisteredIndexReturnsZero) +{ + // Index 2 was never registered (only index 0 was) + EXPECT_EQ(0U, BxCanTransceiver::receiveInterrupt(2)); +} + +TEST_F(InitedBxCanTransceiverTest, receiveTaskWithFramesNotifiesListeners) +{ + CANFrame frame1; + CANFrame frame2; + + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(2)); + EXPECT_CALL(fBxt.fDevice, getRxFrame(0)).WillOnce(ReturnRef(frame1)); + EXPECT_CALL(fBxt.fDevice, getRxFrame(1)).WillOnce(ReturnRef(frame2)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + + fBxt.receiveTask(); +} + +TEST_F(InitedBxCanTransceiverTest, receiveTaskReenablesInterrupt) +{ + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + + fBxt.receiveTask(); +} + +TEST_F(InitedBxCanTransceiverTest, writeFireAndForgetNotifiesRegisteredSentListeners) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // notifySentListeners is called synchronously by write(frame) + // No registered sent listeners by default, so it just doesn't crash + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, writeFailureDoesNotNotifySentListeners) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // HW queue full - no notification expected + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, docanWriteThenTxIsr) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // Simulates: DoCAN sets _sendPending after write() returns, + // then TX ISR fires, canFrameSent() clears _sendPending + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, docanConsecutiveMultiFrameSend) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + EXPECT_CALL(fBxt.fDevice, transmitISR()).Times(3); + EXPECT_CALL(listener, canFrameSent(_)).Times(3); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Simulate DoCAN sending 3 consecutive frames + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + BxCanTransceiver::transmitInterrupt(fBusId); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + BxCanTransceiver::transmitInterrupt(fBusId); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, openWithFrameDelegatesToOpen) +{ + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open(frame)); + // Second open should fail, proving state transitioned + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, openFromClosedReinitsDevice) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); + + // Re-open from CLOSED should call init + start + EXPECT_CALL(fBxt.fDevice, init()).Times(1); + EXPECT_CALL(fBxt.fDevice, start()).Times(1); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, closeFromMutedReturnsOk) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); +} + +TEST_F(InitedBxCanTransceiverTest, closeIsIdempotent) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); +} + +TEST_F(InitedBxCanTransceiverTest, muteTwiceFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.mute()); +} + +TEST_F(InitedBxCanTransceiverTest, unmuteFromInitializedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.unmute()); +} + +TEST_F(InitedBxCanTransceiverTest, getBusIdReturnsConfiguredValue) +{ + EXPECT_EQ(fBusId, fBxt.getBusId()); +} + +TEST_F(InitedBxCanTransceiverTest, getStateReflectsLifecycle) +{ + EXPECT_EQ(ICanTransceiver::State::INITIALIZED, fBxt.getState()); + + fBxt.open(); + EXPECT_EQ(ICanTransceiver::State::OPEN, fBxt.getState()); + + fBxt.mute(); + EXPECT_EQ(ICanTransceiver::State::MUTED, fBxt.getState()); + + fBxt.unmute(); + EXPECT_EQ(ICanTransceiver::State::OPEN, fBxt.getState()); + + fBxt.close(); + EXPECT_EQ(ICanTransceiver::State::CLOSED, fBxt.getState()); +} + +TEST_F(InitedBxCanTransceiverTest, disableRxInterruptDelegatesToDevice) +{ + EXPECT_CALL(fBxt.fDevice, disableRxInterrupt()).Times(1); + BxCanTransceiver::disableRxInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, writeFromInitializedReturnsTxOffline) +{ + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, writeWithListenerFromInitializedReturnsTxOffline) +{ + CANFrame frame; + MockCANFrameSentListener listener; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame, listener)); +} + +TEST_F(InitedBxCanTransceiverTest, openFromMutedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.open()); +} + +TEST_F(InitedBxCanTransceiverTest, initFromOpenFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.init()); +} + +TEST_F(InitedBxCanTransceiverTest, initFromMutedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.init()); +} + +TEST_F(InitedBxCanTransceiverTest, writeAfterCloseReturnsTxOffline) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.close()); + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, muteFromMutedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fBxt.mute()); +} + +TEST_F(InitedBxCanTransceiverTest, unmuteRestoresWriteCapability) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.unmute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, shutdownFromInitialized) +{ + fBxt.shutdown(); + EXPECT_EQ(ICanTransceiver::State::CLOSED, fBxt.getState()); +} + +TEST_F(BxCanTransceiverTest, fullLifecycle) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + + EXPECT_CALL(bxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, stop()).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::State::CLOSED, bxt.getState()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.init()); + EXPECT_EQ(ICanTransceiver::State::INITIALIZED, bxt.getState()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.open()); + EXPECT_EQ(ICanTransceiver::State::OPEN, bxt.getState()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.mute()); + EXPECT_EQ(ICanTransceiver::State::MUTED, bxt.getState()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.close()); + EXPECT_EQ(ICanTransceiver::State::CLOSED, bxt.getState()); +} + +TEST_F(BxCanTransceiverTest, reopenAfterShutdown) +{ + BxCanTransceiver bxt(fAsyncContext, fBusId, fDevConfig); + + EXPECT_CALL(bxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(bxt.fDevice, stop()).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.init()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.open()); + bxt.shutdown(); + EXPECT_EQ(ICanTransceiver::State::CLOSED, bxt.getState()); + + // Re-open from CLOSED + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, bxt.open()); + EXPECT_EQ(ICanTransceiver::State::OPEN, bxt.getState()); +} + +TEST_F(InitedBxCanTransceiverTest, busOffDuringPendingTxDropsCallback) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // Bus goes off before TX ISR + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(true)); + fBxt.cyclicTask(); // transitions to MUTED + + // TX ISR fires after bus-off - listener should NOT be called + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(0); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedBxCanTransceiverTest, busOffRecoveryResumesTx) +{ + CANFrame frame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // Bus-off -> MUTED + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(true)).WillOnce(Return(false)); + fBxt.cyclicTask(); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); + + // Bus-on -> OPEN + fBxt.cyclicTask(); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, concurrentRxTxSameCycle) +{ + CANFrame rxFrame; + CANFrame txFrame; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // TX + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(txFrame)); + + // RX in same cycle + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(1)); + EXPECT_CALL(fBxt.fDevice, getRxFrame(0)).WillOnce(ReturnRef(rxFrame)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + + fBxt.receiveTask(); +} + +TEST_F(InitedBxCanTransceiverTest, txIsrAndReceiveTaskInterleave) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // RX task runs + EXPECT_CALL(fBxt.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fBxt.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + fBxt.receiveTask(); + + // TX ISR fires after receive task completed + EXPECT_CALL(fBxt.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + BxCanTransceiver::transmitInterrupt(fBusId); +} + +class ManualAsyncBxCanTransceiverTest : public BxCanTransceiverTest +{ +public: + ManualAsyncBxCanTransceiverTest() : fBxt(fAsyncContext, fBusId, fDevConfig) + { + EXPECT_CALL(fBxt.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(fBxt.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(fBxt.fDevice, stop()).Times(AnyNumber()); + + // Capture the runnable instead of auto-executing + EXPECT_CALL(fAsyncMock, execute(fAsyncContext, _)) + .Times(AnyNumber()) + .WillRepeatedly([this](auto, auto& runnable) { fCapturedRunnable = &runnable; }); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.init()); + } + + void executeDeferred() + { + if (fCapturedRunnable != nullptr) + { + fCapturedRunnable->execute(); + fCapturedRunnable = nullptr; + } + } + + BxCanTransceiver fBxt; + ::async::RunnableType* fCapturedRunnable = nullptr; +}; + +TEST_F(ManualAsyncBxCanTransceiverTest, deferredCallbackNotCalledUntilAsyncFires) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fBxt.fDevice, transmit(_)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.write(frame, listener)); + + // TX ISR fires - defers to async context + EXPECT_CALL(fBxt.fDevice, transmitISR()); + BxCanTransceiver::transmitInterrupt(fBusId); + + // Listener is called when async executes + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + executeDeferred(); +} + +TEST_F(InitedBxCanTransceiverTest, cyclicTaskUserMuteNoAutoRecovery) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.open()); + + // User mutes manually + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fBxt.mute()); + + // Bus is not off, but fMuted is true - cyclicTask should NOT unmute + EXPECT_CALL(fBxt.fDevice, isBusOff()).WillOnce(Return(false)); + fBxt.cyclicTask(); + + // Should still be MUTED because user muted (fMuted = true) + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fBxt.write(frame)); +} + +TEST_F(InitedBxCanTransceiverTest, enableRxInterruptDelegatesToDevice) +{ + EXPECT_CALL(fBxt.fDevice, enableRxInterrupt()).Times(1); + BxCanTransceiver::enableRxInterrupt(fBusId); +} + +TEST_F(BxCanTransceiverTest, disableRxInterruptInvalidIndexSafe) +{ + BxCanTransceiver::disableRxInterrupt(3); +} + +TEST_F(BxCanTransceiverTest, enableRxInterruptInvalidIndexSafe) +{ + BxCanTransceiver::enableRxInterrupt(3); +} + +TEST_F(BxCanTransceiverTest, transmitInterruptInvalidIndexSafe) +{ + BxCanTransceiver::transmitInterrupt(3); +} + +} // namespace From 40c0cc617de8ca23f0348736ce419b5223356b64 Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Tue, 2 Jun 2026 08:19:30 +0200 Subject: [PATCH 05/11] Add STM32 FDCAN transceiver adapter Add the FDCAN transceiver implementation and STM32 test coverage. Wire the FDCAN transceiver into the chip-family CMake selection. --- CMakeLists.txt | 1 + platforms/stm32/bsp/CMakeLists.txt | 3 + .../stm32/bsp/fdCanTransceiver/CMakeLists.txt | 21 + .../stm32/bsp/fdCanTransceiver/doc/index.rst | 50 + .../can/transceiver/fdcan/FdCanTransceiver.h | 105 ++ .../stm32/bsp/fdCanTransceiver/module.spec | 2 + .../transceiver/fdcan/FdCanTransceiver.cpp | 299 ++++++ .../bsp/fdCanTransceiver/test/CMakeLists.txt | 15 + .../test/mock/include/can/FdCanDevice.h | 94 ++ .../test/src/can/FdCanTransceiverTest.cpp | 905 ++++++++++++++++++ 10 files changed, 1495 insertions(+) create mode 100644 platforms/stm32/bsp/fdCanTransceiver/CMakeLists.txt create mode 100644 platforms/stm32/bsp/fdCanTransceiver/doc/index.rst create mode 100644 platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h create mode 100644 platforms/stm32/bsp/fdCanTransceiver/module.spec create mode 100644 platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp create mode 100644 platforms/stm32/bsp/fdCanTransceiver/test/CMakeLists.txt create mode 100644 platforms/stm32/bsp/fdCanTransceiver/test/mock/include/can/FdCanDevice.h create mode 100644 platforms/stm32/bsp/fdCanTransceiver/test/src/can/FdCanTransceiverTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e3aceca614..4cb65495c30 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,6 +193,7 @@ if (BUILD_EXECUTABLE STREQUAL "unitTest") add_subdirectory(platforms/stm32/bsp/bspCan/test) add_subdirectory(platforms/stm32/bsp/bxCanTransceiver/test) + add_subdirectory(platforms/stm32/bsp/fdCanTransceiver/test) elseif (OPENBSW_PLATFORM STREQUAL "s32k1xx") diff --git a/platforms/stm32/bsp/CMakeLists.txt b/platforms/stm32/bsp/CMakeLists.txt index 0fdb650d9bd..47cf2745511 100644 --- a/platforms/stm32/bsp/CMakeLists.txt +++ b/platforms/stm32/bsp/CMakeLists.txt @@ -2,6 +2,7 @@ if (BUILD_EXECUTABLE STREQUAL "unitTest") # Unit test builds: only compile transceivers (with mock devices). Skip # hardware-dependent modules (bspMcu, bspClock, bspCan, etc.). add_subdirectory(bxCanTransceiver) + add_subdirectory(fdCanTransceiver) add_subdirectory(bspUart) else () add_subdirectory(bspMcu) @@ -17,6 +18,8 @@ else () # CAN transceiver - selected by chip family if (CAN_TYPE STREQUAL "BXCAN") add_subdirectory(bxCanTransceiver) + elseif (CAN_TYPE STREQUAL "FDCAN") + add_subdirectory(fdCanTransceiver) endif () # Aggregated BSP target diff --git a/platforms/stm32/bsp/fdCanTransceiver/CMakeLists.txt b/platforms/stm32/bsp/fdCanTransceiver/CMakeLists.txt new file mode 100644 index 00000000000..0f4deeaedab --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/CMakeLists.txt @@ -0,0 +1,21 @@ +add_library(fdCanTransceiver src/can/transceiver/fdcan/FdCanTransceiver.cpp) + +if (BUILD_EXECUTABLE STREQUAL "unitTest") + target_include_directories(fdCanTransceiver PUBLIC include + test/mock/include) + + target_link_libraries( + fdCanTransceiver + PUBLIC asyncMockImpl + bsp + cpp2can + etl + bspIo + lifecycle + gmock) +else () + target_include_directories(fdCanTransceiver PUBLIC include) + + target_link_libraries(fdCanTransceiver PUBLIC async bspCan lifecycle + cpp2can) +endif () diff --git a/platforms/stm32/bsp/fdCanTransceiver/doc/index.rst b/platforms/stm32/bsp/fdCanTransceiver/doc/index.rst new file mode 100644 index 00000000000..a47de0ae6c4 --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/doc/index.rst @@ -0,0 +1,50 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +fdCanTransceiver +================ + +Overview +-------- +Implements the OpenBSW ``AbstractCANTransceiver`` interface for the STM32 +FDCAN peripheral. Wraps the register-level ``FdCanDevice`` (``bspCan`` module) +and adds lifecycle management, async task integration, ISR dispatch and +bus-off recovery. The peripheral runs in classic CAN mode at 500 kbps. + +Lifecycle +--------- +The transceiver follows the ``AbstractCANTransceiver`` state model +(``CLOSED`` -> ``INITIALIZED`` -> ``OPEN`` <-> ``MUTED``). ``init()`` and +``open()`` delegate to ``FdCanDevice::init()`` and ``FdCanDevice::start()``; +``close()`` stops the device from any state. ``mute()`` and ``unmute()`` +suppress and resume transmission while reception stays active. + +TX Path +------- +``write(frame)`` transmits fire-and-forget and notifies sent-listeners +synchronously. ``write(frame, listener)`` queues the job, transmits with the +TX-complete interrupt enabled, and invokes ``listener.canFrameSent()`` from +task context (deferred via ``async::execute``) after the TX ISR; further +queued frames are sent one after another from that callback chain. + +RX Path +------- +``receiveInterrupt()`` runs in the RX ISR and delegates to +``FdCanDevice::receiveISR(nullptr)``, accepting all frames into the software +queue; per-listener filtering happens later in ``notifyListeners()``. +``receiveTask()`` runs in task context, notifies listeners for each queued +frame, clears the queue and re-enables the RX interrupt. + +Bus-Off Recovery +---------------- +``cyclicTask()`` polls ``FdCanDevice::isBusOff()`` periodically. On bus-off +the transceiver transitions from ``OPEN`` to ``MUTED``; once the peripheral +recovers it returns to ``OPEN``, unless the user explicitly called ``mute()``. diff --git a/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h b/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h new file mode 100644 index 00000000000..a0c481a6eec --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h @@ -0,0 +1,105 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace bios +{ + +/** + * CAN transceiver for STM32 FDCAN peripheral. + * + * Wraps FdCanDevice to implement the AbstractCANTransceiver interface. + * Operates in classic CAN mode at 500 kbps on STM32G4 family devices. + * + * \see AbstractCANTransceiver + * \see FdCanDevice + */ +class FdCanTransceiver : public ::can::AbstractCANTransceiver +{ +public: + FdCanTransceiver( + ::async::ContextType context, uint8_t busId, FdCanDevice::Config const& devConfig); + + ErrorCode init() override; + ErrorCode open() override; + ErrorCode open(::can::CANFrame const& frame) override; + ErrorCode close() override; + void shutdown() override; + ErrorCode mute() override; + ErrorCode unmute() override; + + /// Fire-and-forget transmit. Calls notifySentListeners() synchronously. + ErrorCode write(::can::CANFrame const& frame) override; + + /// Transmit with deferred callback. Queues {listener, frame} into fTxQueue. + /// canFrameSent() fires from task context after TX ISR, NOT from write(). + ErrorCode write(::can::CANFrame const& frame, ::can::ICANFrameSentListener& listener) override; + + uint32_t getBaudrate() const override; + uint16_t getHwQueueTimeout() const override; + + /// Number of TX frames dropped due to HW queue full or TX listener queue full. + uint32_t getOverrunCount() const { return fOverrunCount; } + + static uint8_t receiveInterrupt(uint8_t transceiverIndex); + static void transmitInterrupt(uint8_t transceiverIndex); + static void disableRxInterrupt(uint8_t transceiverIndex); + static void enableRxInterrupt(uint8_t transceiverIndex); + + void cyclicTask(); + void receiveTask(); + + /// Underlying FDCAN hardware device. Public for test access. + FdCanDevice fDevice; + +private: + /// TX job queued for async callback. + struct TxJobWithCallback + { + TxJobWithCallback(::can::ICANFrameSentListener& listener, ::can::CANFrame const& frame) + : _listener(listener), _frame(frame) + {} + + ::can::ICANFrameSentListener& _listener; + ::can::CANFrame _frame; + }; + + static uint32_t const TX_QUEUE_CAPACITY = 3U; + using TxQueue = ::etl::deque; + + /// Called from TX ISR - defers to task context via async::execute. + void canFrameSentCallback(); + + /// Runs in task context - pops TX queue, calls listener, sends next frame. + void canFrameSentAsyncCallback(); + + void notifyRegisteredSentListener(::can::CANFrame const& frame) { notifySentListeners(frame); } + + static FdCanTransceiver* fpTransceivers[3]; + + ::async::ContextType fContext; + ::async::TimeoutType fCyclicTimeout; + ::async::Function _cyclicTaskRunner; + ::async::Function _canFrameSent; + TxQueue fTxQueue; + uint32_t fOverrunCount; + bool fMuted; +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/fdCanTransceiver/module.spec b/platforms/stm32/bsp/fdCanTransceiver/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp b/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp new file mode 100644 index 00000000000..e97013703df --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp @@ -0,0 +1,299 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +#include + +namespace bios +{ + +FdCanTransceiver* FdCanTransceiver::fpTransceivers[3] = {nullptr, nullptr, nullptr}; + +FdCanTransceiver::FdCanTransceiver( + ::async::ContextType context, uint8_t busId, FdCanDevice::Config const& devConfig) +: AbstractCANTransceiver(busId) +, fDevice( + devConfig, + ::etl::delegate::create( + *this)) +, fContext(context) +, fCyclicTimeout() +, _cyclicTaskRunner( + ::async::Function::CallType::create(*this)) +, _canFrameSent(::async::Function::CallType:: + create(*this)) +, fTxQueue() +, fOverrunCount(0U) +, fMuted(false) +{ + if (busId < 3U) + { + fpTransceivers[busId] = this; + } +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::init() +{ + if (getState() != State::CLOSED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + if (!fDevice.init()) + { + return ErrorCode::CAN_ERR_INIT_FAILED; + } + setState(State::INITIALIZED); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::open() +{ + State const state = getState(); + if (state != State::INITIALIZED && state != State::CLOSED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + if (state == State::CLOSED) + { + if (!fDevice.init()) + { + return ErrorCode::CAN_ERR_INIT_FAILED; + } + } + + if (!fDevice.start()) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + setState(State::OPEN); + fMuted = false; + + // Auto-schedule bus-off polling. + ::async::scheduleAtFixedRate( + fContext, _cyclicTaskRunner, fCyclicTimeout, 10U, ::async::TimeUnit::MILLISECONDS); + + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::open(::can::CANFrame const& /* frame */) +{ + return open(); +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::close() +{ + if (getState() == State::CLOSED) + { + return ErrorCode::CAN_ERR_OK; + } + + fCyclicTimeout.cancel(); + fDevice.stop(); + setState(State::CLOSED); + return ErrorCode::CAN_ERR_OK; +} + +void FdCanTransceiver::shutdown() { close(); } + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::mute() +{ + if (getState() != State::OPEN) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + fMuted = true; + setState(State::MUTED); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::unmute() +{ + if (getState() != State::MUTED) + { + return ErrorCode::CAN_ERR_ILLEGAL_STATE; + } + + fMuted = false; + setState(State::OPEN); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode FdCanTransceiver::write(::can::CANFrame const& frame) +{ + if (getState() != State::OPEN || fMuted) + { + return ErrorCode::CAN_ERR_TX_OFFLINE; + } + + // Transmit without TX-complete interrupt; no deferred callback is needed. + if (!fDevice.transmit(frame, false)) + { + fOverrunCount++; + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + notifySentListeners(frame); + return ErrorCode::CAN_ERR_OK; +} + +::can::ICanTransceiver::ErrorCode +FdCanTransceiver::write(::can::CANFrame const& frame, ::can::ICANFrameSentListener& listener) +{ + if (getState() != State::OPEN || fMuted) + { + return ErrorCode::CAN_ERR_TX_OFFLINE; + } + + if (fTxQueue.full()) + { + fOverrunCount++; + notifyRegisteredSentListener(frame); + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + bool const wasEmpty = fTxQueue.empty(); + fTxQueue.emplace_back(listener, frame); + + if (!wasEmpty) + { + // Next frame will be sent from TX ISR callback chain. + return ErrorCode::CAN_ERR_OK; + } + + // First sender - transmit with TX-complete interrupt enabled. + // Device enables TCE, ISR will fire on completion and invoke callback delegate. + if (!fDevice.transmit(frame, true)) + { + fTxQueue.pop_front(); + fOverrunCount++; + notifyRegisteredSentListener(frame); + return ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL; + } + + // Wait until TX ISR -> fFrameSentCallback -> canFrameSentCallback(). + return ErrorCode::CAN_ERR_OK; +} + +uint32_t FdCanTransceiver::getBaudrate() const { return 500000U; } + +uint16_t FdCanTransceiver::getHwQueueTimeout() const { return 10U; } + +uint8_t FdCanTransceiver::receiveInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + // Accept all frames at ISR level. Per-listener filtering happens in + // notifyListeners() during receiveTask(). + return fpTransceivers[transceiverIndex]->fDevice.receiveISR(nullptr); + } + return 0U; +} + +void FdCanTransceiver::transmitInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + // Device's transmitISR() disables TCE, clears TC, and invokes the + // callback delegate (bound to canFrameSentCallback). + fpTransceivers[transceiverIndex]->fDevice.transmitISR(); + } +} + +void FdCanTransceiver::canFrameSentCallback() { ::async::execute(fContext, _canFrameSent); } + +void FdCanTransceiver::canFrameSentAsyncCallback() +{ + if (!fTxQueue.empty()) + { + bool sendAgain = false; + { + TxJobWithCallback& job = fTxQueue.front(); + ::can::CANFrame const& frame = job._frame; + ::can::ICANFrameSentListener& listener = job._listener; + fTxQueue.pop_front(); + + if (!fTxQueue.empty()) + { + // Send again only if same precondition as for write() is satisfied. + State const state = getState(); + if ((State::OPEN == state) || (State::INITIALIZED == state)) + { + sendAgain = true; + } + else + { + fTxQueue.clear(); + } + } + + listener.canFrameSent(frame); + notifyRegisteredSentListener(frame); + } + + if (sendAgain) + { + ::can::CANFrame const& frame = fTxQueue.front()._frame; + // Transmit next queued frame with TX-complete interrupt enabled. + if (!fDevice.transmit(frame, true)) + { + fTxQueue.clear(); + } + } + } +} + +void FdCanTransceiver::disableRxInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + fpTransceivers[transceiverIndex]->fDevice.disableRxInterrupt(); + } +} + +void FdCanTransceiver::enableRxInterrupt(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + fpTransceivers[transceiverIndex]->fDevice.enableRxInterrupt(); + } +} + +void FdCanTransceiver::cyclicTask() +{ + if (fDevice.isBusOff()) + { + if (getState() == State::OPEN) + { + setState(State::MUTED); + } + } + else if (getState() == State::MUTED && !fMuted) + { + setState(State::OPEN); + } +} + +void FdCanTransceiver::receiveTask() +{ + uint8_t count = fDevice.getRxCount(); + for (uint8_t i = 0U; i < count; i++) + { + notifyListeners(fDevice.getRxFrame(i)); + } + fDevice.clearRxQueue(); + // Re-enable RX FIFO interrupt after draining software queue + fDevice.enableRxInterrupt(); +} + +} // namespace bios diff --git a/platforms/stm32/bsp/fdCanTransceiver/test/CMakeLists.txt b/platforms/stm32/bsp/fdCanTransceiver/test/CMakeLists.txt new file mode 100644 index 00000000000..dd336a04d98 --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/test/CMakeLists.txt @@ -0,0 +1,15 @@ +add_executable(FdCanTransceiverTest src/can/FdCanTransceiverTest.cpp) + +target_link_libraries( + FdCanTransceiverTest + PRIVATE asyncMockImpl + bsp + fdCanTransceiver + cpp2can + bspIo + lifecycle + bspMock + gmock) + +gtest_discover_tests(FdCanTransceiverTest PROPERTIES LABELS + "FdCanTransceiverTest") diff --git a/platforms/stm32/bsp/fdCanTransceiver/test/mock/include/can/FdCanDevice.h b/platforms/stm32/bsp/fdCanTransceiver/test/mock/include/can/FdCanDevice.h new file mode 100644 index 00000000000..fa7c7ff816a --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/test/mock/include/can/FdCanDevice.h @@ -0,0 +1,94 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include + +#include + +namespace bios +{ + +// GMock replacement for the register-level FdCanDevice. +class FdCanDevice +{ +public: + struct Config + { + uint32_t baseAddress; + uint32_t prescaler; + uint32_t nts1; + uint32_t nts2; + uint32_t nsjw; + uint32_t rxGpioPort; + uint8_t rxPin; + uint8_t rxAf; + uint32_t txGpioPort; + uint8_t txPin; + uint8_t txAf; + }; + + static constexpr uint32_t RX_QUEUE_SIZE = 32U; + + explicit FdCanDevice(Config const& /* config */) + { + ON_CALL(*this, init()).WillByDefault(::testing::Return(true)); + ON_CALL(*this, start()).WillByDefault(::testing::Return(true)); + } + + FdCanDevice(Config const& /* config */, ::etl::delegate callback) + : fFrameSentCallback(callback) + { + ON_CALL(*this, init()).WillByDefault(::testing::Return(true)); + ON_CALL(*this, start()).WillByDefault(::testing::Return(true)); + } + + MOCK_METHOD(bool, init, ()); + MOCK_METHOD(bool, start, ()); + MOCK_METHOD(void, stop, ()); + MOCK_METHOD(bool, transmit, (::can::CANFrame const& frame)); + MOCK_METHOD(bool, transmit, (::can::CANFrame const& frame, bool txInterruptNeeded)); + MOCK_METHOD(uint8_t, receiveISR, (uint8_t const* filterBitField)); + + // Mock transmitISR - tests can set expectations on it. + // Default action invokes the stored callback delegate (matching real FdCanDevice). + MOCK_METHOD(void, transmitISR, ()); + + void setupDefaultTransmitISR() + { + ON_CALL(*this, transmitISR()) + .WillByDefault( + [this]() + { + if (fFrameSentCallback.is_valid()) + { + fFrameSentCallback(); + } + }); + } + + MOCK_METHOD(bool, isBusOff, (), (const)); + MOCK_METHOD(uint8_t, getTxErrorCounter, (), (const)); + MOCK_METHOD(uint8_t, getRxErrorCounter, (), (const)); + MOCK_METHOD(void, configureAcceptAllFilter, ()); + MOCK_METHOD(void, configureFilterList, (uint32_t const* idList, uint8_t count)); + MOCK_METHOD(::can::CANFrame const&, getRxFrame, (uint8_t index), (const)); + MOCK_METHOD(uint8_t, getRxCount, (), (const)); + MOCK_METHOD(void, clearRxQueue, ()); + MOCK_METHOD(void, disableRxInterrupt, ()); + MOCK_METHOD(void, enableRxInterrupt, ()); + + ::etl::delegate fFrameSentCallback; +}; + +} // namespace bios diff --git a/platforms/stm32/bsp/fdCanTransceiver/test/src/can/FdCanTransceiverTest.cpp b/platforms/stm32/bsp/fdCanTransceiver/test/src/can/FdCanTransceiverTest.cpp new file mode 100644 index 00000000000..8da3c37111d --- /dev/null +++ b/platforms/stm32/bsp/fdCanTransceiver/test/src/can/FdCanTransceiverTest.cpp @@ -0,0 +1,905 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "can/transceiver/fdcan/FdCanTransceiver.h" + +#include "async/AsyncMock.h" +#include "can/canframes/ICANFrameSentListener.h" + +#include + +namespace +{ +using namespace ::can; +using namespace ::testing; +using namespace ::bios; + +// Mock for ICANFrameSentListener (write-with-listener / DoCAN callback) +class MockCANFrameSentListener : public ::can::ICANFrameSentListener +{ +public: + MOCK_METHOD(void, canFrameSent, (::can::CANFrame const&), (override)); +}; + +// Fixture: bare transceiver (CLOSED state) +class FdCanTransceiverTest : public Test +{ +public: + ::async::AsyncMock fAsyncMock; + ::async::ContextType fAsyncContext = 0; + uint8_t fBusId = 0; + FdCanDevice::Config fDevConfig{}; +}; + +// Fixture: transceiver in INITIALIZED state (auto-executes async) +class InitedFdCanTransceiverTest : public FdCanTransceiverTest +{ +public: + InitedFdCanTransceiverTest() : fFct(fAsyncContext, fBusId, fDevConfig) + { + fFct.fDevice.setupDefaultTransmitISR(); + EXPECT_CALL(fFct.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, stop()).Times(AnyNumber()); + EXPECT_CALL(fAsyncMock, execute(fAsyncContext, _)) + .Times(AnyNumber()) + .WillRepeatedly([](auto, auto& runnable) { runnable.execute(); }); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.init()); + } + + FdCanTransceiver fFct; +}; + +// Fixture: transceiver in INITIALIZED state with manual async control +// (async::execute is captured but NOT auto-executed) +class ManualAsyncFdCanTransceiverTest : public FdCanTransceiverTest +{ +public: + ManualAsyncFdCanTransceiverTest() : fFct(fAsyncContext, fBusId, fDevConfig) + { + fFct.fDevice.setupDefaultTransmitISR(); + EXPECT_CALL(fFct.fDevice, init()).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, start()).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, stop()).Times(AnyNumber()); + // Capture async::execute calls but do NOT auto-run them + EXPECT_CALL(fAsyncMock, execute(fAsyncContext, _)) + .Times(AnyNumber()) + .WillRepeatedly([this](auto, auto& runnable) { fCapturedRunnable = &runnable; }); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.init()); + } + + /// Manually execute the last captured async runnable (simulates task context) + void executeAsyncCallback() + { + ASSERT_NE(nullptr, fCapturedRunnable); + fCapturedRunnable->execute(); + fCapturedRunnable = nullptr; + } + + FdCanTransceiver fFct; + ::async::RunnableType* fCapturedRunnable = nullptr; +}; + +TEST_F(FdCanTransceiverTest, initFromClosed) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fct.init()); +} + +TEST_F(FdCanTransceiverTest, initTwiceFails) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fct.init()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fct.init()); +} + +TEST_F(InitedFdCanTransceiverTest, open) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, openTwiceFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, openFromClosed) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, closeFromOpen) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); +} + +TEST_F(InitedFdCanTransceiverTest, closeFromClosedReturnsOk) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); +} + +TEST_F(InitedFdCanTransceiverTest, muteFromOpen) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); +} + +TEST_F(InitedFdCanTransceiverTest, muteFromInitializedFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.mute()); +} + +TEST_F(InitedFdCanTransceiverTest, unmuteFromMuted) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, unmuteFromOpenFails) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, writeWhenOpen) +{ + CANFrame frame; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, writeWhenMutedFails) +{ + CANFrame frame; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, writeWhenFifoFull) +{ + CANFrame frame; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, getBaudrate) { EXPECT_EQ(500000U, fFct.getBaudrate()); } + +TEST_F(InitedFdCanTransceiverTest, getHwQueueTimeout) { EXPECT_EQ(10U, fFct.getHwQueueTimeout()); } + +TEST_F(InitedFdCanTransceiverTest, receiveTask) +{ + CANFrame frame; + + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + + fFct.receiveTask(); +} + +TEST_F(InitedFdCanTransceiverTest, cyclicTaskBusOff) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, isBusOff()).WillOnce(Return(true)); + fFct.cyclicTask(); + + // After bus-off, write should fail + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, cyclicTaskBusRecovery) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, isBusOff()).WillOnce(Return(true)).WillOnce(Return(false)); + + fFct.cyclicTask(); // bus-off -> MUTED + fFct.cyclicTask(); // bus-on -> OPEN + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + CANFrame frame; + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, shutdown) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + fFct.shutdown(); +} + +TEST_F(InitedFdCanTransceiverTest, receiveInterrupt) +{ + EXPECT_CALL(fFct.fDevice, receiveISR(_)).WillOnce(Return(0)); + FdCanTransceiver::receiveInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, transmitInterrupt) +{ + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(FdCanTransceiverTest, receiveInterruptInvalidIndex) +{ + EXPECT_EQ(0U, FdCanTransceiver::receiveInterrupt(3)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rOk) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rFail) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rWhenMuted) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rDoesNotCallListenerSynchronously) +{ + CANFrame frame; + StrictMock listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + // StrictMock: any call to canFrameSent during write() will FAIL + EXPECT_CALL(listener, canFrameSent(_)).Times(0); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Verify: listener was NOT called during write() + Mock::VerifyAndClearExpectations(&listener); + + // Now allow the callback to happen (TX ISR triggers it) + EXPECT_CALL(listener, canFrameSent(_)).Times(AnyNumber()); + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, writeOverFillQueue) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillRepeatedly(Return(true)); + + // Queue capacity is 3; items are popped only in canFrameSentAsyncCallback, + // which we never trigger here to simulate queue-full. + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fFct.write(frame, listener)); +} + +TEST_F(ManualAsyncFdCanTransceiverTest, writeSecondFrameNotTransmittedImmediately) +{ + CANFrame frame1; + CANFrame frame2; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + // Only ONE transmit call expected from the first write() + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + // At this point, only frame1 was transmitted to HW; frame2 waits in queue. + Mock::VerifyAndClearExpectations(&fFct.fDevice); + + // Now simulate TX ISR for frame1 - this should transmit frame2 + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + + FdCanTransceiver::transmitInterrupt(fBusId); + // Execute the deferred async callback + executeAsyncCallback(); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithOneFrame) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // TX ISR fires -> canFrameSentCallback -> async -> canFrameSentAsyncCallback + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +/// Note: S32K allows write in INITIALIZED, our port requires OPEN. Test uses OPEN. +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesChained) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + // TX ISR for frame1 - pops frame1, notifies listener, transmits frame2 + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesInStateOpen) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillRepeatedly(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesIsAbortedWhenMuted) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + fFct.mute(); + + // TX ISR fires - first frame was already submitted. Callback should pop + // frame1 and notify listener, but NOT submit frame2 (muted). + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesFailure) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(false)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + // First write fails at HW level + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_HW_QUEUE_FULL, fFct.write(frame1, listener)); + // Second write succeeds + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, canFrameSentCallbackWithTwoFramesIsAbortedWhenNextSendFails) +{ + MockCANFrameSentListener listener; + CANFrame frame1; + CANFrame frame2; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)).WillOnce(Return(false)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener)); + + // TX ISR for frame1 - callback pops frame1, tries to send frame2 but + // transmit returns false => aborted, queue cleared + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(ManualAsyncFdCanTransceiverTest, canFrameSentCallbackUsesAsyncExecute) +{ + CANFrame frame; + StrictMock listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // TX ISR fires - should call async::execute, NOT call listener directly + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + // Verify async::execute was called (runnable was captured) + EXPECT_NE(nullptr, fCapturedRunnable); + + // Listener not called yet (still in ISR context) + Mock::VerifyAndClearExpectations(&listener); + + // Now execute in task context + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + executeAsyncCallback(); +} + +TEST_F(ManualAsyncFdCanTransceiverTest, canFrameSentCallbackRunsInTaskContext) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // TX ISR fires + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + // At this point, listener should NOT have been called + EXPECT_CALL(listener, canFrameSent(_)).Times(0); + Mock::VerifyAndClearExpectations(&listener); + + // Execute the deferred callback (task context) + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + executeAsyncCallback(); +} + +TEST_F(InitedFdCanTransceiverTest, receiveInterruptPassesFilterToDevice) +{ + EXPECT_CALL(fFct.fDevice, receiveISR(_)).WillOnce(Return(1)); + FdCanTransceiver::receiveInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, receiveInterruptAcceptsAllFrames) +{ + EXPECT_CALL(fFct.fDevice, receiveISR(_)).WillOnce(Return(1)).WillOnce(Return(3)); + + EXPECT_EQ(1U, FdCanTransceiver::receiveInterrupt(fBusId)); + EXPECT_EQ(3U, FdCanTransceiver::receiveInterrupt(fBusId)); +} + +TEST_F(InitedFdCanTransceiverTest, receiveTaskNotifiesListenersForEachFrame) +{ + CANFrame frame0; + CANFrame frame1; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(2)); + EXPECT_CALL(fFct.fDevice, getRxFrame(0)).WillOnce(ReturnRef(frame0)); + EXPECT_CALL(fFct.fDevice, getRxFrame(1)).WillOnce(ReturnRef(frame1)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + + fFct.receiveTask(); +} + +TEST_F(InitedFdCanTransceiverTest, receiveTaskReEnablesInterrupt) +{ + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + + fFct.receiveTask(); +} + +/// Note: upstream AbstractCANTransceiver only calls SystemTimer when +/// IFilteredCANFrameSentListener is registered. This test verifies the +/// write succeeds and the transceiver doesn't crash without registered listeners. +TEST_F(InitedFdCanTransceiverTest, notifyRegisteredSentListenerMatch) +{ + CANFrame frame; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, notifyRegisteredSentListenerFromCallback) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // TX ISR -> async callback -> pops queue -> calls listener.canFrameSent + // Also calls notifyRegisteredSentListener (notifySentListeners) + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F( + ManualAsyncFdCanTransceiverTest, writeWithListenerThenTransmitInterruptTriggersDeferredCallback) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Step 1: TX ISR fires - defers to task context + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + ASSERT_NE(nullptr, fCapturedRunnable); + + // Step 2: Task context executes - listener gets called + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + executeAsyncCallback(); +} + +TEST_F(ManualAsyncFdCanTransceiverTest, multipleWriteWithListenerProcessedSerially) +{ + CANFrame frame1; + CANFrame frame2; + MockCANFrameSentListener listener1; + MockCANFrameSentListener listener2; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)) + .WillOnce(Return(true)) // frame1 + .WillOnce(Return(true)); // frame2 (from callback chain) + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame1, listener1)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame2, listener2)); + + // TX ISR for frame1 + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + // Execute deferred callback for frame1 - should notify listener1, submit frame2 + EXPECT_CALL(listener1, canFrameSent(_)).Times(1); + EXPECT_CALL(listener2, canFrameSent(_)).Times(0); + executeAsyncCallback(); + + Mock::VerifyAndClearExpectations(&listener1); + Mock::VerifyAndClearExpectations(&listener2); + + // TX ISR for frame2 + fCapturedRunnable = nullptr; + EXPECT_CALL(fFct.fDevice, transmitISR()); + FdCanTransceiver::transmitInterrupt(fBusId); + + // Execute deferred callback for frame2 - should notify listener2 + EXPECT_CALL(listener2, canFrameSent(_)).Times(1); + executeAsyncCallback(); +} + +TEST_F(InitedFdCanTransceiverTest, transmitInterruptWithNoPendingListener) +{ + EXPECT_CALL(fFct.fDevice, transmitISR()); + + // No write(frame, listener) was called - TX ISR should just call transmitISR + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(FdCanTransceiverTest, transmitInterruptInvalidIndex) +{ + // Should not crash - just silently ignored + FdCanTransceiver::transmitInterrupt(3); + FdCanTransceiver::transmitInterrupt(255); +} + +TEST_F(InitedFdCanTransceiverTest, writeWithListenerWhenNotOpen) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + // State is INITIALIZED (not OPEN) - write should fail + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, closeWithPendingTxQueue) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Close with a pending TX job - should not crash + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); + + // TX ISR after close - should not crash or call listener + EXPECT_CALL(listener, canFrameSent(_)).Times(0); +} + +TEST_F(InitedFdCanTransceiverTest, muteWithPendingTxQueue) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Mute with a pending TX job - should not crash + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); +} + +TEST_F(InitedFdCanTransceiverTest, disableRxInterruptDelegates) +{ + EXPECT_CALL(fFct.fDevice, disableRxInterrupt()).Times(1); + FdCanTransceiver::disableRxInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, enableRxInterruptDelegates) +{ + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + FdCanTransceiver::enableRxInterrupt(fBusId); +} + +TEST_F(FdCanTransceiverTest, disableRxInterruptInvalidIndex) +{ + // Should not crash + FdCanTransceiver::disableRxInterrupt(3); + FdCanTransceiver::disableRxInterrupt(255); +} + +TEST_F(FdCanTransceiverTest, enableRxInterruptInvalidIndex) +{ + // Should not crash + FdCanTransceiver::enableRxInterrupt(3); + FdCanTransceiver::enableRxInterrupt(255); +} + +TEST_F(FdCanTransceiverTest, writeInClosedState) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + CANFrame frame; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fct.write(frame)); +} + +TEST_F(InitedFdCanTransceiverTest, writeInInitializedState) +{ + CANFrame frame; + // State is INITIALIZED - write should fail + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame)); +} + +TEST_F(FdCanTransceiverTest, write2rInClosedState) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, write2rInInitializedState) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame, listener)); +} + +TEST_F(FdCanTransceiverTest, muteInClosedState) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + EXPECT_CALL(fct.fDevice, init()).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fct.mute()); +} + +TEST_F(InitedFdCanTransceiverTest, muteInMutedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.mute()); +} + +TEST_F(FdCanTransceiverTest, unmuteInClosedState) +{ + FdCanTransceiver fct(fAsyncContext, fBusId, fDevConfig); + EXPECT_CALL(fct.fDevice, init()).Times(AnyNumber()); + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, unmuteInInitializedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, unmuteInOpenState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.unmute()); +} + +TEST_F(InitedFdCanTransceiverTest, openInOpenState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, openInMutedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_ILLEGAL_STATE, fFct.open()); +} + +TEST_F(InitedFdCanTransceiverTest, closeInInitializedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); +} + +TEST_F(InitedFdCanTransceiverTest, closeInMutedState) +{ + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.mute()); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.close()); +} + +TEST_F(InitedFdCanTransceiverTest, busOffDuringPendingTxClearsQueue) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Bus-off detected by cyclicTask + EXPECT_CALL(fFct.fDevice, isBusOff()).WillOnce(Return(true)); + fFct.cyclicTask(); + + // After bus-off, write with listener should fail + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_TX_OFFLINE, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, busRecoveryAfterQueueClear) +{ + CANFrame frame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillRepeatedly(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); + + // Bus-off + EXPECT_CALL(fFct.fDevice, isBusOff()).WillOnce(Return(true)).WillOnce(Return(false)); + fFct.cyclicTask(); // OPEN -> MUTED + + // Bus recovery + fFct.cyclicTask(); // MUTED -> OPEN + + // New write should succeed + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(frame, listener)); +} + +TEST_F(InitedFdCanTransceiverTest, receiveTaskWhileTxPending) +{ + CANFrame txFrame; + CANFrame rxFrame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(txFrame, listener)); + + // RX task runs while TX is pending - should work independently + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(1)); + EXPECT_CALL(fFct.fDevice, getRxFrame(0)).WillOnce(ReturnRef(rxFrame)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + fFct.receiveTask(); + + // TX ISR still works after RX task + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); +} + +TEST_F(InitedFdCanTransceiverTest, transmitInterruptDuringReceiveTask) +{ + CANFrame txFrame; + MockCANFrameSentListener listener; + + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.open()); + + EXPECT_CALL(fFct.fDevice, transmit(_, _)).WillOnce(Return(true)); + EXPECT_EQ(ICanTransceiver::ErrorCode::CAN_ERR_OK, fFct.write(txFrame, listener)); + + // TX ISR fires + EXPECT_CALL(fFct.fDevice, transmitISR()); + EXPECT_CALL(listener, canFrameSent(_)).Times(1); + FdCanTransceiver::transmitInterrupt(fBusId); + + // RX task runs after TX ISR - should still work normally + EXPECT_CALL(fFct.fDevice, getRxCount()).WillOnce(Return(0)); + EXPECT_CALL(fFct.fDevice, clearRxQueue()).Times(1); + EXPECT_CALL(fFct.fDevice, enableRxInterrupt()).Times(1); + fFct.receiveTask(); +} + +} // namespace From 6ffb562889e28a71713ca65700ad15793597c492 Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Tue, 2 Jun 2026 08:22:22 +0200 Subject: [PATCH 06/11] Add STM32 fault handler and watchdog safety Add the STM32 hard fault handler with RAM dump region and the IWDG watchdog driver. Add the G474RE safety manager sources and watchdog unit tests. --- CMakeLists.txt | 1 + .../nucleo_g474re/safety/CMakeLists.txt | 1 + .../safety/safeLifecycle/CMakeLists.txt | 6 + .../include/safeLifecycle/SafetyManager.h | 32 + .../safeLifecycle/src/SafetyManager.cpp | 62 ++ platforms/stm32/CMakeLists.txt | 6 + .../stm32/hardFaultHandler/CMakeLists.txt | 13 + .../stm32/hardFaultHandler/doc/index.rst | 37 + platforms/stm32/hardFaultHandler/module.spec | 2 + .../hardFaultHandler/src/hardFaultHandler.s | 144 +++ platforms/stm32/safety/CMakeLists.txt | 1 + .../safety/safeBspMcuWatchdog/CMakeLists.txt | 6 + .../include/watchdog/Watchdog.h | 90 ++ .../safety/safeBspMcuWatchdog/module.spec | 2 + .../src/watchdog/Watchdog.cpp | 109 ++ .../safeBspMcuWatchdog/test/CMakeLists.txt | 7 + .../safeBspMcuWatchdog/test/include/mcu/mcu.h | 11 + .../test/src/WatchdogTest.cpp | 944 ++++++++++++++++++ 18 files changed, 1474 insertions(+) create mode 100644 executables/referenceApp/platforms/nucleo_g474re/safety/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h create mode 100644 executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/src/SafetyManager.cpp create mode 100644 platforms/stm32/hardFaultHandler/CMakeLists.txt create mode 100644 platforms/stm32/hardFaultHandler/doc/index.rst create mode 100644 platforms/stm32/hardFaultHandler/module.spec create mode 100644 platforms/stm32/hardFaultHandler/src/hardFaultHandler.s create mode 100644 platforms/stm32/safety/CMakeLists.txt create mode 100644 platforms/stm32/safety/safeBspMcuWatchdog/CMakeLists.txt create mode 100644 platforms/stm32/safety/safeBspMcuWatchdog/include/watchdog/Watchdog.h create mode 100644 platforms/stm32/safety/safeBspMcuWatchdog/module.spec create mode 100644 platforms/stm32/safety/safeBspMcuWatchdog/src/watchdog/Watchdog.cpp create mode 100644 platforms/stm32/safety/safeBspMcuWatchdog/test/CMakeLists.txt create mode 100644 platforms/stm32/safety/safeBspMcuWatchdog/test/include/mcu/mcu.h create mode 100644 platforms/stm32/safety/safeBspMcuWatchdog/test/src/WatchdogTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4cb65495c30..baf4c25ba94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,6 +194,7 @@ if (BUILD_EXECUTABLE STREQUAL "unitTest") add_subdirectory(platforms/stm32/bsp/bspCan/test) add_subdirectory(platforms/stm32/bsp/bxCanTransceiver/test) add_subdirectory(platforms/stm32/bsp/fdCanTransceiver/test) + add_subdirectory(platforms/stm32/safety/safeBspMcuWatchdog/test) elseif (OPENBSW_PLATFORM STREQUAL "s32k1xx") diff --git a/executables/referenceApp/platforms/nucleo_g474re/safety/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/safety/CMakeLists.txt new file mode 100644 index 00000000000..89a1188107d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/safety/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(safeLifecycle) diff --git a/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/CMakeLists.txt new file mode 100644 index 00000000000..75799a0bb8c --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(safeLifecycle src/SafetyManager.cpp) + +target_include_directories(safeLifecycle PUBLIC include) + +target_link_libraries(safeLifecycle PRIVATE safeSupervisor safeUtils + safeBspMcuWatchdog) diff --git a/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h new file mode 100644 index 00000000000..1ffe5faba23 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace safety +{ + +class SafetyManager +{ +public: + SafetyManager(); + void init(); + void run(); + void shutdown(); + void cyclic(); + +private: + uint16_t _counter; + static constexpr uint8_t WATCHDOG_CYCLIC_COUNTER = 8U; ///< Kick every 8 cycles (80ms @ 10ms) +}; + +} // namespace safety diff --git a/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/src/SafetyManager.cpp b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/src/SafetyManager.cpp new file mode 100644 index 00000000000..4c3af0c4787 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/safety/safeLifecycle/src/SafetyManager.cpp @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "safeLifecycle/SafetyManager.h" + +#include +#include +#include + +namespace safety +{ + +using ::util::logger::Logger; +using ::util::logger::SAFETY; + +SafetyManager::SafetyManager() : _counter(0U) {} + +void SafetyManager::init() +{ + Logger::debug(SAFETY, "SafetyManager initialized"); + + if (bsp::Watchdog::isResetFromWatchdog()) + { + Logger::warn(SAFETY, "Previous reset was caused by IWDG watchdog"); + bsp::Watchdog::clearResetFlag(); + } +} + +void SafetyManager::run() +{ + // Enable IWDG here (not in init) - the cyclic scheduler is already running + // at this point, so the 250ms timeout won't fire before the first kick. + bsp::Watchdog::enableWatchdog(bsp::Watchdog::DEFAULT_TIMEOUT); + Logger::debug(SAFETY, "IWDG watchdog enabled (250ms timeout)"); +} + +void SafetyManager::shutdown() {} + +void SafetyManager::cyclic() +{ + auto& supervisor = SafeSupervisor::getInstance(); + supervisor.safetyManagerSequenceMonitor.hit( + SafeSupervisor::SafetyManagerSequence::SAFETY_MANAGER_ENTER); + + _counter++; + if (_counter >= WATCHDOG_CYCLIC_COUNTER) + { + _counter = 0U; + bsp::Watchdog::serviceWatchdog(); + } + + supervisor.safetyManagerSequenceMonitor.hit( + SafeSupervisor::SafetyManagerSequence::SAFETY_MANAGER_LEAVE); +} +} // namespace safety diff --git a/platforms/stm32/CMakeLists.txt b/platforms/stm32/CMakeLists.txt index 7315f7f7653..ce465ec49c2 100644 --- a/platforms/stm32/CMakeLists.txt +++ b/platforms/stm32/CMakeLists.txt @@ -20,6 +20,12 @@ if (NOT BUILD_EXECUTABLE STREQUAL "unitTest") # ETL implementation add_subdirectory(etlImpl) + + # Safety modules + add_subdirectory(safety) + + # Other modules + add_subdirectory(hardFaultHandler) else () # Unit-test builds compile the transceivers against mock devices. add_subdirectory(bsp) diff --git a/platforms/stm32/hardFaultHandler/CMakeLists.txt b/platforms/stm32/hardFaultHandler/CMakeLists.txt new file mode 100644 index 00000000000..2c7881695f5 --- /dev/null +++ b/platforms/stm32/hardFaultHandler/CMakeLists.txt @@ -0,0 +1,13 @@ +add_library(hardFaultHandler src/hardFaultHandler.s) + +# The .s file uses preprocessor conditionals for the chip family. +set_source_files_properties(src/hardFaultHandler.s + PROPERTIES COMPILE_OPTIONS "-x;assembler-with-cpp") + +set_target_properties(hardFaultHandler PROPERTIES COMPILE_WARNING_AS_ERROR OFF) + +target_compile_options( + hardFaultHandler + PRIVATE "$<$:-Wno-unused-command-line-argument>") + +target_link_libraries(hardFaultHandler PRIVATE bspMcu) diff --git a/platforms/stm32/hardFaultHandler/doc/index.rst b/platforms/stm32/hardFaultHandler/doc/index.rst new file mode 100644 index 00000000000..8a72ce4d537 --- /dev/null +++ b/platforms/stm32/hardFaultHandler/doc/index.rst @@ -0,0 +1,37 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +hardFaultHandler +================ + +Overview +-------- + +The ``hardFaultHandler`` module provides a Cortex-M4 hard fault handler that saves a register +dump to a no-init RAM region. The dump survives a software reset, allowing post-mortem +analysis of the fault cause after reboot. + +The following data is saved to the dump: + +- CPU registers of the faulting context (R0-R12, SP, LR, PC, xPSR) +- LR and PSR at the entry of the exception handler +- stack contents (as much as possible) +- checksum of the dump + +The handler is written in assembly to guarantee correct stack frame extraction from either +MSP or PSP, depending on which stack was active at the time of the fault. + +Integration +----------- + +The board integration must branch the HardFault vector to ``customHardFaultHandler`` and +provide ``HardFault_Handler_Final``. The dump region must be reserved by the board linker +script. diff --git a/platforms/stm32/hardFaultHandler/module.spec b/platforms/stm32/hardFaultHandler/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/hardFaultHandler/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/hardFaultHandler/src/hardFaultHandler.s b/platforms/stm32/hardFaultHandler/src/hardFaultHandler.s new file mode 100644 index 00000000000..f8bdbcc340c --- /dev/null +++ b/platforms/stm32/hardFaultHandler/src/hardFaultHandler.s @@ -0,0 +1,144 @@ +// Copyright (c) 2024 Accenture +// Copyright (c) 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +.syntax unified +.arch armv7-m + +//------------------------------------------------------------------------------ +// Hard fault register dump for STM32 (Cortex-M4) +// +// Dumps all registers + stack snapshot to a fixed NO_INIT RAM location. +// RAM addresses are chip-specific - configured via defines. +//------------------------------------------------------------------------------ + +// Dump location - use top of SRAM for both F4 (320KB) and G4 (128KB). +// The dump region must be reserved by the board linker script (see the board +// integration). +// Default: use conservative address that works for G4 (smallest SRAM). +// G4: SRAM ends at 0x20020000, F4: SRAM ends at 0x20050000 +#if defined(STM32_FAMILY_F4) +.equ NO_INIT_RAM_START, 0x2004FC00 +#else +.equ NO_INIT_RAM_START, 0x2001FC00 +#endif +.equ NO_INIT_RAM_SIZE, 0x400 +.equ DUMP_SIZE, 0x140 +.equ DUMP_START, NO_INIT_RAM_START + NO_INIT_RAM_SIZE - DUMP_SIZE + +// Dump fields offsets +.equ DUMP_HANDLER_PSR, 0x000 +.equ DUMP_HANDLER_LR, 0x004 +.equ DUMP_ORIGIN_R0, 0x008 +.equ DUMP_ORIGIN_R1, 0x00C +.equ DUMP_ORIGIN_R2, 0x010 +.equ DUMP_ORIGIN_R3, 0x014 +.equ DUMP_ORIGIN_R4, 0x018 +.equ DUMP_ORIGIN_R5, 0x01C +.equ DUMP_ORIGIN_R6, 0x020 +.equ DUMP_ORIGIN_R7, 0x024 +.equ DUMP_ORIGIN_R8, 0x028 +.equ DUMP_ORIGIN_R9, 0x02C +.equ DUMP_ORIGIN_R10, 0x030 +.equ DUMP_ORIGIN_R11, 0x034 +.equ DUMP_ORIGIN_R12, 0x038 +.equ DUMP_ORIGIN_SP, 0x03C +.equ DUMP_ORIGIN_LR, 0x040 +.equ DUMP_ORIGIN_PC, 0x044 +.equ DUMP_ORIGIN_PSR, 0x048 +.equ DUMP_STACK_START, 0x04C +.equ DUMP_STACK_END, 0x13C +.equ DUMP_CHECKSUM, 0x13C + +// Stack frame offsets +.equ FRAME_R0, 0x00 +.equ FRAME_R1, 0x04 +.equ FRAME_R2, 0x08 +.equ FRAME_R3, 0x0C +.equ FRAME_R12, 0x10 +.equ FRAME_LR, 0x14 +.equ FRAME_PC, 0x18 +.equ FRAME_PSR, 0x1C +.equ FRAME_SIZE, 0x20 + +//------------------------------------------------------------------------------ +// WARNING: +// R4-R11 must not be used before they are written to the dump, since they are +// not saved in stack. Stack must not be used. +//------------------------------------------------------------------------------ + +.text +.globl customHardFaultHandler +.type customHardFaultHandler, %function + +customHardFaultHandler: + + // Save flags for dump in advance + mrs R12, XPSR + + // Save PSR and LR + ldr R2, = DUMP_START + str R12, [R2, #DUMP_HANDLER_PSR] + str LR, [R2, #DUMP_HANDLER_LR] + + // Save values of registers which are not framed + str R4, [R2, #DUMP_ORIGIN_R4] + str R5, [R2, #DUMP_ORIGIN_R5] + str R6, [R2, #DUMP_ORIGIN_R6] + str R7, [R2, #DUMP_ORIGIN_R7] + str R8, [R2, #DUMP_ORIGIN_R8] + str R9, [R2, #DUMP_ORIGIN_R9] + str R10, [R2, #DUMP_ORIGIN_R10] + str R11, [R2, #DUMP_ORIGIN_R11] + + // Analyze exception return code, calculate the value of SP which was + // active at the moment of exception, and save it + tst LR, 4 + ite eq + mrseq R1, MSP + mrsne R1, PSP + add R0, R1, FRAME_SIZE + str R0, [R2, #DUMP_ORIGIN_SP] + + // Save value of registers which are framed + ldr R0, [R1, #FRAME_R0] + str R0, [R2, #DUMP_ORIGIN_R0] + ldr R0, [R1, #FRAME_R1] + str R0, [R2, #DUMP_ORIGIN_R1] + ldr R0, [R1, #FRAME_R2] + str R0, [R2, #DUMP_ORIGIN_R2] + ldr R0, [R1, #FRAME_R3] + str R0, [R2, #DUMP_ORIGIN_R3] + ldr R0, [R1, #FRAME_R12] + str R0, [R2, #DUMP_ORIGIN_R12] + ldr R0, [R1, #FRAME_LR] + str R0, [R2, #DUMP_ORIGIN_LR] + ldr R0, [R1, #FRAME_PC] + str R0, [R2, #DUMP_ORIGIN_PC] + ldr R0, [R1, #FRAME_PSR] + str R0, [R2, #DUMP_ORIGIN_PSR] + + // Add some stack contents to dump + add R1, FRAME_SIZE + add R2, DUMP_STACK_START + add R3, R2, DUMP_STACK_END - DUMP_STACK_START + 1: + ldr R0, [R1], 4 + str R0, [R2], 4 + cmp R2, R3 + bne 1b + + // Calculate and save checksum + mov R1, 0 + ldr R2, = DUMP_START + ldr R3, = DUMP_START + DUMP_CHECKSUM + 1: + ldr R0, [R2], 4 + add R1, R0 + cmp R2, R3 + bne 1b + str R1, [R3] + + // Complete HardFault handling + b HardFault_Handler_Final diff --git a/platforms/stm32/safety/CMakeLists.txt b/platforms/stm32/safety/CMakeLists.txt new file mode 100644 index 00000000000..b13c0a3f24a --- /dev/null +++ b/platforms/stm32/safety/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(safeBspMcuWatchdog) diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/CMakeLists.txt b/platforms/stm32/safety/safeBspMcuWatchdog/CMakeLists.txt new file mode 100644 index 00000000000..810161f1914 --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(safeBspMcuWatchdog src/watchdog/Watchdog.cpp) +target_include_directories(safeBspMcuWatchdog PUBLIC include) +target_link_libraries( + safeBspMcuWatchdog + PUBLIC bspMcu + PRIVATE platform) diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/include/watchdog/Watchdog.h b/platforms/stm32/safety/safeBspMcuWatchdog/include/watchdog/Watchdog.h new file mode 100644 index 00000000000..4f5b3d63c7a --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/include/watchdog/Watchdog.h @@ -0,0 +1,90 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace safety +{ +namespace bsp +{ + +// IWDG driver for STM32; the G474RE board's SafetyManager drives it directly. +// +// STM32 IWDG specifics: +// - LSI clock ~32 kHz (not trimmed - 17..47 kHz depending on device) +// - 12-bit reload register (0..4095) +// - Once started, cannot be disabled (hardware limitation) +// - Kick sequence: write 0xAAAA to KR +// - Unlock sequence: write 0x5555 to KR, then configure PR + RLR +// - Start sequence: write 0xCCCC to KR +class Watchdog +{ +public: + Watchdog() = default; + + explicit Watchdog(uint32_t const timeout, uint32_t const clockSpeed = DEFAULT_CLOCK_SPEED) + { + enableWatchdog(timeout, false, clockSpeed); + } + + /** + * \brief Enable the IWDG with the given timeout. + * \param timeout Timeout in milliseconds (max ~32 s at 32 kHz LSI). + * \param interruptActive Ignored on STM32 (IWDG has no interrupt mode). + * \param clockSpeed LSI clock speed in Hz (default 32000). + */ + static void enableWatchdog( + uint32_t timeout, bool interruptActive = false, uint32_t clockSpeed = DEFAULT_CLOCK_SPEED); + + /** + * \brief Cannot disable the STM32 IWDG once started. This is a no-op. + */ + static void disableWatchdog(); + + /** + * \brief Kick (service) the IWDG by writing 0xAAAA to KR. + */ + static void serviceWatchdog(); + + /** + * \brief Check that the IWDG prescaler and reload match the expected timeout. + * \return true if the configuration is consistent. + */ + static bool + checkWatchdogConfiguration(uint32_t timeout, uint32_t clockSpeed = DEFAULT_CLOCK_SPEED); + + /** + * \brief Check if the last reset was caused by the IWDG. + * \return true if RCC_CSR_IWDGRSTF is set. + */ + static bool isResetFromWatchdog(); + + /** + * \brief Clear the IWDG reset flag in RCC_CSR. + */ + static void clearResetFlag(); + + static uint32_t getWatchdogServiceCounter(); + + static constexpr uint32_t DEFAULT_TIMEOUT = 250U; ///< 250 ms + static constexpr uint32_t DEFAULT_CLOCK_SPEED = 32000U; ///< LSI ~32 kHz + +private: + static uint32_t watchdogServiceCounter; + + /// Compute prescaler divider and reload value for a given timeout. + static void computePrescalerAndReload( + uint32_t timeoutMs, uint32_t clockHz, uint8_t& prescalerCode, uint16_t& reload); +}; + +} // namespace bsp +} // namespace safety diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/module.spec b/platforms/stm32/safety/safeBspMcuWatchdog/module.spec new file mode 100644 index 00000000000..50ed87e068b --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/module.spec @@ -0,0 +1,2 @@ +maturity: raw +oss: true diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/src/watchdog/Watchdog.cpp b/platforms/stm32/safety/safeBspMcuWatchdog/src/watchdog/Watchdog.cpp new file mode 100644 index 00000000000..fd9b150e1cc --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/src/watchdog/Watchdog.cpp @@ -0,0 +1,109 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include + +#include + +namespace safety +{ +namespace bsp +{ + +uint32_t Watchdog::watchdogServiceCounter = 0U; + +void Watchdog::computePrescalerAndReload( + uint32_t timeoutMs, uint32_t clockHz, uint8_t& prescalerCode, uint16_t& reload) +{ + // IWDG prescaler dividers: /4, /8, /16, /32, /64, /128, /256 + // prescalerCode 0..6 -> divider = 4 << code + // + // timeout_ms = (reload + 1) * divider * 1000 / clockHz + // reload = (timeout_ms * clockHz) / (divider * 1000) - 1 + + static uint16_t const dividers[] = {4, 8, 16, 32, 64, 128, 256}; + + for (uint8_t code = 0U; code < 7U; code++) + { + uint32_t ticks = (timeoutMs * (clockHz / 1000U)) / dividers[code]; + if (ticks == 0U) + { + ticks = 1U; + } + if (ticks <= 4096U) + { + prescalerCode = code; + reload = static_cast(ticks - 1U); + return; + } + } + + // Largest possible: prescaler /256, reload 4095 + prescalerCode = 6U; + reload = 4095U; +} + +void Watchdog::enableWatchdog(uint32_t timeout, bool /*interruptActive*/, uint32_t clockSpeed) +{ + uint8_t prescalerCode; + uint16_t reload; + computePrescalerAndReload(timeout, clockSpeed, prescalerCode, reload); + + // Unlock IWDG registers + IWDG->KR = 0x5555U; + + IWDG->PR = prescalerCode; + IWDG->RLR = reload; + + // Wait for registers to be updated + while ((IWDG->SR & (IWDG_SR_PVU | IWDG_SR_RVU)) != 0U) {} + + // Start the watchdog (irreversible) + IWDG->KR = 0xCCCCU; + + serviceWatchdog(); +} + +void Watchdog::disableWatchdog() +{ + // STM32 IWDG cannot be disabled once started. + // This is intentionally a no-op. +} + +void Watchdog::serviceWatchdog() +{ + IWDG->KR = 0xAAAAU; + watchdogServiceCounter++; +} + +bool Watchdog::checkWatchdogConfiguration(uint32_t timeout, uint32_t clockSpeed) +{ + uint8_t expectedPrescaler; + uint16_t expectedReload; + computePrescalerAndReload(timeout, clockSpeed, expectedPrescaler, expectedReload); + + uint32_t actualPrescaler = IWDG->PR & 0x7U; + uint32_t actualReload = IWDG->RLR & 0xFFFU; + + return (actualPrescaler == expectedPrescaler) && (actualReload == expectedReload); +} + +bool Watchdog::isResetFromWatchdog() { return (RCC->CSR & RCC_CSR_IWDGRSTF) != 0U; } + +void Watchdog::clearResetFlag() +{ + // Writing RMVF bit clears all reset flags + RCC->CSR |= RCC_CSR_RMVF; +} + +uint32_t Watchdog::getWatchdogServiceCounter() { return watchdogServiceCounter; } + +} // namespace bsp +} // namespace safety diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/test/CMakeLists.txt b/platforms/stm32/safety/safeBspMcuWatchdog/test/CMakeLists.txt new file mode 100644 index 00000000000..12cf8635773 --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/test/CMakeLists.txt @@ -0,0 +1,7 @@ +add_executable(WatchdogTest src/WatchdogTest.cpp) + +target_include_directories(WatchdogTest PRIVATE include ../include ../src) + +target_link_libraries(WatchdogTest PRIVATE platform gmock gtest_main) + +gtest_discover_tests(WatchdogTest PROPERTIES LABELS "WatchdogTest") diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/test/include/mcu/mcu.h b/platforms/stm32/safety/safeBspMcuWatchdog/test/include/mcu/mcu.h new file mode 100644 index 00000000000..30abb173e91 --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/test/include/mcu/mcu.h @@ -0,0 +1,11 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once diff --git a/platforms/stm32/safety/safeBspMcuWatchdog/test/src/WatchdogTest.cpp b/platforms/stm32/safety/safeBspMcuWatchdog/test/src/WatchdogTest.cpp new file mode 100644 index 00000000000..380b4be59cf --- /dev/null +++ b/platforms/stm32/safety/safeBspMcuWatchdog/test/src/WatchdogTest.cpp @@ -0,0 +1,944 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// Uses fake IWDG_TypeDef and RCC_TypeDef structs so register writes go +// to testable memory instead of real hardware. + +#include +#include + +#ifndef __IO +#define __IO volatile +#endif + +// Select G4 code path +#define STM32G474xx + +// --- Fake IWDG_TypeDef --- +typedef struct +{ + __IO uint32_t KR; + __IO uint32_t PR; + __IO uint32_t RLR; + __IO uint32_t SR; + __IO uint32_t WINR; +} IWDG_TypeDef; + +// --- Fake RCC_TypeDef (STM32G4 layout) --- +typedef struct +{ + __IO uint32_t CR; + __IO uint32_t ICSCR; + __IO uint32_t CFGR; + __IO uint32_t PLLCFGR; + uint32_t RESERVED0; + uint32_t RESERVED1; + __IO uint32_t CIER; + __IO uint32_t CIFR; + __IO uint32_t CICR; + uint32_t RESERVED2; + __IO uint32_t AHB1RSTR; + __IO uint32_t AHB2RSTR; + __IO uint32_t AHB3RSTR; + uint32_t RESERVED3; + __IO uint32_t APB1RSTR1; + __IO uint32_t APB1RSTR2; + __IO uint32_t APB2RSTR; + uint32_t RESERVED4; + __IO uint32_t AHB1ENR; + __IO uint32_t AHB2ENR; + __IO uint32_t AHB3ENR; + uint32_t RESERVED5; + __IO uint32_t APB1ENR1; + __IO uint32_t APB1ENR2; + __IO uint32_t APB2ENR; + uint32_t RESERVED6; + __IO uint32_t AHB1SMENR; + __IO uint32_t AHB2SMENR; + __IO uint32_t AHB3SMENR; + uint32_t RESERVED7; + __IO uint32_t APB1SMENR1; + __IO uint32_t APB1SMENR2; + __IO uint32_t APB2SMENR; + uint32_t RESERVED8; + __IO uint32_t CCIPR; + uint32_t RESERVED9; + __IO uint32_t BDCR; + __IO uint32_t CSR; + __IO uint32_t CRRCR; + __IO uint32_t CCIPR2; +} RCC_TypeDef; + +// --- Static fake peripherals --- +static IWDG_TypeDef fakeIwdg; +static RCC_TypeDef fakeRcc; + +// --- Override hardware macros --- +#define IWDG (&fakeIwdg) +#define RCC (&fakeRcc) + +// IWDG SR bit definitions +#define IWDG_SR_PVU (1U << 0) +#define IWDG_SR_RVU (1U << 1) + +// RCC CSR bit definitions +#define RCC_CSR_IWDGRSTF (1U << 29) +#define RCC_CSR_RMVF (1U << 23) + +// Provide platform/estdint.h types +#ifndef PLATFORM_ESTDINT_H +#define PLATFORM_ESTDINT_H +#include +#endif + +// Include production code (header + implementation directly) +#include +#include + +#include + +using namespace safety::bsp; + +class WatchdogTest : public ::testing::Test +{ +protected: + void SetUp() override + { + std::memset(&fakeIwdg, 0, sizeof(fakeIwdg)); + std::memset(&fakeRcc, 0, sizeof(fakeRcc)); + // Reset the static service counter by calling enable + reading + // We will use a helper to reset state between tests + } +}; + +// computePrescalerAndReload tests (verified through enableWatchdog PR/RLR) + +TEST_F(WatchdogTest, enableWatchdog_1ms_32000Hz) +{ + // timeout=1ms, clock=32000 => ticks = (1 * 32) / 4 = 8, prescaler=0, reload=7 + Watchdog::enableWatchdog(1U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 7U); +} + +TEST_F(WatchdogTest, enableWatchdog_100ms_32000Hz) +{ + // timeout=100ms, clock=32000 => ticks = (100 * 32) / 4 = 800, prescaler=0, reload=799 + Watchdog::enableWatchdog(100U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 799U); +} + +TEST_F(WatchdogTest, enableWatchdog_250ms_32000Hz) +{ + // timeout=250ms, clock=32000 => ticks = (250 * 32) / 4 = 2000, prescaler=0, reload=1999 + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +TEST_F(WatchdogTest, enableWatchdog_500ms_32000Hz) +{ + // timeout=500ms, clock=32000 => ticks = (500 * 32) / 4 = 4000, prescaler=0, reload=3999 + Watchdog::enableWatchdog(500U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_1000ms_32000Hz) +{ + // timeout=1000ms, clock=32000 => ticks = (1000 * 32) / 4 = 8000 > 4096 + // Try prescaler 1 (/8): ticks = (1000 * 32) / 8 = 4000, prescaler=1, reload=3999 + Watchdog::enableWatchdog(1000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_2000ms_32000Hz) +{ + // ticks@/4 = 16000; /8 = 8000; /16 = 4000 => prescaler=2, reload=3999 + Watchdog::enableWatchdog(2000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 2U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_4000ms_32000Hz) +{ + // ticks@/4=32000; /8=16000; /16=8000; /32=4000 => prescaler=3, reload=3999 + Watchdog::enableWatchdog(4000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_8000ms_32000Hz) +{ + // /64 = 4000 => prescaler=4, reload=3999 + Watchdog::enableWatchdog(8000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 4U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_16000ms_32000Hz) +{ + // /128 = 4000 => prescaler=5, reload=3999 + Watchdog::enableWatchdog(16000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 5U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_32000ms_32000Hz) +{ + // /256 = 4000 => prescaler=6, reload=3999 + Watchdog::enableWatchdog(32000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_MaxTimeout_Clamps) +{ + // Extremely large timeout, should clamp to prescaler=6, reload=4095 + Watchdog::enableWatchdog(100000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 4095U); +} + +// --- Different clock speeds --- + +TEST_F(WatchdogTest, enableWatchdog_250ms_40000Hz) +{ + // ticks = (250 * 40) / 4 = 2500, prescaler=0, reload=2499 + Watchdog::enableWatchdog(250U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, enableWatchdog_1000ms_40000Hz) +{ + // ticks@/4 = 10000 > 4096; /8 = 5000 > 4096; /16 = 2500 => prescaler=2, reload=2499 + Watchdog::enableWatchdog(1000U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 2U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, enableWatchdog_100ms_128000Hz) +{ + // ticks = (100 * 128) / 4 = 3200, prescaler=0, reload=3199 + Watchdog::enableWatchdog(100U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 3199U); +} + +TEST_F(WatchdogTest, enableWatchdog_250ms_128000Hz) +{ + // ticks@/4 = 8000; /8 = 4000 => prescaler=1, reload=3999 + Watchdog::enableWatchdog(250U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_1000ms_128000Hz) +{ + // ticks@/4=32000; /8=16000; /16=8000; /32=4000 => prescaler=3, reload=3999 + Watchdog::enableWatchdog(1000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_500ms_128000Hz) +{ + // ticks@/4=16000; /8=8000; /16=4000 => prescaler=2, reload=3999 + Watchdog::enableWatchdog(500U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 2U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +// --- Prescaler code boundary: each prescaler code (0-6) --- + +TEST_F(WatchdogTest, prescalerCode0_SmallTimeout) +{ + // 10ms at 32kHz: ticks = (10*32)/4 = 80 => code 0, reload=79 + Watchdog::enableWatchdog(10U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 79U); +} + +TEST_F(WatchdogTest, prescalerCode0_AtLimit) +{ + // 512ms at 32kHz: ticks = (512*32)/4 = 4096 => code 0, reload=4095 + Watchdog::enableWatchdog(512U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 4095U); +} + +TEST_F(WatchdogTest, prescalerCode1_JustOverCode0) +{ + // 513ms at 32kHz: ticks@/4 = 4104 > 4096; /8 = 2052 => code 1, reload=2051 + Watchdog::enableWatchdog(513U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 2051U); +} + +TEST_F(WatchdogTest, prescalerCode2_Boundary) +{ + // 1025ms at 32kHz: ticks@/4=8200; /8=4100>4096; /16=2050 => code 2, reload=2049 + Watchdog::enableWatchdog(1025U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 2U); + EXPECT_EQ(fakeIwdg.RLR, 2049U); +} + +TEST_F(WatchdogTest, prescalerCode3_Boundary) +{ + // 2049ms at 32kHz: ticks = (2049*32)/32 = 2049, reload = 2049-1 = 2048 + Watchdog::enableWatchdog(2049U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 2048U); +} + +TEST_F(WatchdogTest, prescalerCode4_Boundary) +{ + // 4097ms at 32kHz: ticks=(4097*32)/64=2048, reload=2048-1=2047 + Watchdog::enableWatchdog(4097U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 4U); + EXPECT_EQ(fakeIwdg.RLR, 2047U); +} + +TEST_F(WatchdogTest, prescalerCode5_Boundary) +{ + // 8193ms at 32kHz: (8193*32)/64=4096 fits code 4; need >4096 to force code 5 + // Use 8194ms: (8194*32)/64=4097>4096 -> code 5, ticks=(8194*32)/128=2048, rlr=2047 + Watchdog::enableWatchdog(8194U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 5U); + EXPECT_EQ(fakeIwdg.RLR, 2047U); +} + +TEST_F(WatchdogTest, prescalerCode6_Boundary) +{ + // 16385ms: (16385*32)/128=4096 fits code 5; need >4096 to force code 6 + // 16386*32/128=4096 still fits code 5. Use 16389: (16389*32)/128=4100>4096 -> code 6 + // ticks=(16389*32)/256=2049, rlr=2048 + Watchdog::enableWatchdog(16389U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 2047U); +} + +// enableWatchdog: KR unlock/start sequence + +TEST_F(WatchdogTest, enableWatchdog_WritesUnlockKey) +{ + // We check that KR received the start key last (0xCCCC written, then 0xAAAA from + // serviceWatchdog) + Watchdog::enableWatchdog(250U, false, 32000U); + // After enableWatchdog, the last KR write was serviceWatchdog => 0xAAAA + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, enableWatchdog_SetsPrescaler) +{ + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); +} + +TEST_F(WatchdogTest, enableWatchdog_SetsReload) +{ + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +TEST_F(WatchdogTest, enableWatchdog_DefaultClockSpeed) +{ + Watchdog::enableWatchdog(250U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +TEST_F(WatchdogTest, enableWatchdog_IgnoresInterruptFlag) +{ + Watchdog::enableWatchdog(250U, true, 32000U); + // Same result regardless of interruptActive + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +// serviceWatchdog tests + +TEST_F(WatchdogTest, serviceWatchdog_WritesKickKey) +{ + Watchdog::serviceWatchdog(); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, serviceWatchdog_IncrementsCounter) +{ + uint32_t before = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), before + 1U); +} + +TEST_F(WatchdogTest, serviceWatchdog_MultipleKicks) +{ + uint32_t before = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + Watchdog::serviceWatchdog(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), before + 3U); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, serviceWatchdog_KRAlwaysKickValue) +{ + fakeIwdg.KR = 0x1234U; + Watchdog::serviceWatchdog(); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, serviceWatchdog_TenKicks) +{ + uint32_t before = Watchdog::getWatchdogServiceCounter(); + for (int i = 0; i < 10; i++) + { + Watchdog::serviceWatchdog(); + } + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), before + 10U); +} + +// checkWatchdogConfiguration tests + +TEST_F(WatchdogTest, checkConfig_MatchingConfig_ReturnsTrue) +{ + // Set up fake IWDG to match 250ms at 32kHz: PR=0, RLR=1999 + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 1999U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_MismatchedPR_ReturnsFalse) +{ + fakeIwdg.PR = 1U; // Wrong prescaler + fakeIwdg.RLR = 1999U; + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_MismatchedRLR_ReturnsFalse) +{ + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 2000U; // Wrong reload + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_BothMismatched_ReturnsFalse) +{ + fakeIwdg.PR = 3U; + fakeIwdg.RLR = 500U; + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_1000ms_32kHz_Match) +{ + fakeIwdg.PR = 1U; + fakeIwdg.RLR = 3999U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(1000U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_100ms_128kHz_Match) +{ + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 3199U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(100U, 128000U)); +} + +TEST_F(WatchdogTest, checkConfig_DefaultClockSpeed) +{ + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 1999U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U)); +} + +TEST_F(WatchdogTest, checkConfig_ExtraBitsInPR_Masked) +{ + // PR has bits above bit 2 set - should be masked to 3 bits + fakeIwdg.PR = 0U | (0xF0U); + fakeIwdg.RLR = 1999U; + // checkWatchdogConfiguration masks PR with 0x7 + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_ExtraBitsInRLR_Masked) +{ + // RLR has bits above bit 11 set - should be masked to 12 bits + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 1999U | (0xF000U); + // checkWatchdogConfiguration masks RLR with 0xFFF + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_500ms_40kHz_Match) +{ + // 500ms@40kHz: /4 => ticks=5000>4096; /8=>2500 => PR=1, RLR=2499 + fakeIwdg.PR = 1U; + fakeIwdg.RLR = 2499U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(500U, 40000U)); +} + +TEST_F(WatchdogTest, checkConfig_AfterEnable_Consistent) +{ + Watchdog::enableWatchdog(500U, false, 32000U); + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(500U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_AfterEnable_DifferentTimeout_False) +{ + Watchdog::enableWatchdog(500U, false, 32000U); + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); +} + +// isResetFromWatchdog tests + +TEST_F(WatchdogTest, isResetFromWatchdog_FlagSet) +{ + fakeRcc.CSR = RCC_CSR_IWDGRSTF; + EXPECT_TRUE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_FlagCleared) +{ + fakeRcc.CSR = 0U; + EXPECT_FALSE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_OtherBitsSet) +{ + fakeRcc.CSR = 0xFFFFFFFFU & ~RCC_CSR_IWDGRSTF; + EXPECT_FALSE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_AllBitsSet) +{ + fakeRcc.CSR = 0xFFFFFFFFU; + EXPECT_TRUE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_OnlyIwdgBit) +{ + fakeRcc.CSR = RCC_CSR_IWDGRSTF; + EXPECT_TRUE(Watchdog::isResetFromWatchdog()); +} + +// clearResetFlag tests + +TEST_F(WatchdogTest, clearResetFlag_SetsRMVF) +{ + fakeRcc.CSR = 0U; + Watchdog::clearResetFlag(); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_RMVF, 0U); +} + +TEST_F(WatchdogTest, clearResetFlag_PreservesOtherBits) +{ + fakeRcc.CSR = RCC_CSR_IWDGRSTF; + Watchdog::clearResetFlag(); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_RMVF, 0U); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_IWDGRSTF, 0U); +} + +TEST_F(WatchdogTest, clearResetFlag_MultipleCalls) +{ + fakeRcc.CSR = 0U; + Watchdog::clearResetFlag(); + Watchdog::clearResetFlag(); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_RMVF, 0U); +} + +// getWatchdogServiceCounter tests + +TEST_F(WatchdogTest, getWatchdogServiceCounter_IncrementsOnService) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); +} + +TEST_F(WatchdogTest, getWatchdogServiceCounter_EnableAlsoKicks) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::enableWatchdog(250U, false, 32000U); + // enableWatchdog calls serviceWatchdog once at the end + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); +} + +TEST_F(WatchdogTest, getWatchdogServiceCounter_ConsistentAfterMultipleCalls) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + for (uint32_t i = 0; i < 20; i++) + { + Watchdog::serviceWatchdog(); + } + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 20U); +} + +// disableWatchdog tests (no-op verification) + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeKR) +{ + fakeIwdg.KR = 0x1234U; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.KR, 0x1234U); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangePR) +{ + fakeIwdg.PR = 3U; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.PR, 3U); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeRLR) +{ + fakeIwdg.RLR = 2000U; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.RLR, 2000U); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeSR) +{ + fakeIwdg.SR = 0x3U; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.SR, 0x3U); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeServiceCounter) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::disableWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev); +} + +// Edge case: timeout 0 + +TEST_F(WatchdogTest, enableWatchdog_Timeout0) +{ + // timeout=0ms, clock=32000 => ticks = 0, forced to 1, prescaler=0, reload=0 + Watchdog::enableWatchdog(0U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 0U); +} + +TEST_F(WatchdogTest, enableWatchdog_Timeout0_128kHz) +{ + Watchdog::enableWatchdog(0U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 0U); +} + +// Edge case: very large timeout overflows to max + +TEST_F(WatchdogTest, enableWatchdog_Overflow_50000ms) +{ + Watchdog::enableWatchdog(50000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 4095U); +} + +TEST_F(WatchdogTest, enableWatchdog_Overflow_128kHz_50000ms) +{ + Watchdog::enableWatchdog(50000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 4095U); +} + +// Constructor test + +TEST_F(WatchdogTest, constructor_CallsEnableWatchdog) +{ + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog wdt(250U, 32000U); + // Constructor calls enableWatchdog which calls serviceWatchdog + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); +} + +TEST_F(WatchdogTest, constructor_DefaultClock) +{ + Watchdog wdt(100U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 799U); +} + +// Full sequence tests + +TEST_F(WatchdogTest, fullSequence_EnableCheckService) +{ + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(250U, 32000U)); + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, fullSequence_EnableServiceDisableService) +{ + Watchdog::enableWatchdog(500U, false, 32000U); + Watchdog::serviceWatchdog(); + Watchdog::disableWatchdog(); // no-op + // Registers should still be from enableWatchdog + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(500U, 32000U)); + Watchdog::serviceWatchdog(); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, fullSequence_CheckResetFlagFlow) +{ + fakeRcc.CSR = RCC_CSR_IWDGRSTF; + EXPECT_TRUE(Watchdog::isResetFromWatchdog()); + Watchdog::clearResetFlag(); + EXPECT_NE(fakeRcc.CSR & RCC_CSR_RMVF, 0U); +} + +// Additional prescaler/reload combination tests + +TEST_F(WatchdogTest, prescaler_5ms_32kHz) +{ + // ticks = (5*32)/4 = 40 => code 0, reload=39 + Watchdog::enableWatchdog(5U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 39U); +} + +TEST_F(WatchdogTest, prescaler_50ms_32kHz) +{ + // ticks = (50*32)/4 = 400 => code 0, reload=399 + Watchdog::enableWatchdog(50U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 399U); +} + +TEST_F(WatchdogTest, prescaler_200ms_32kHz) +{ + // ticks = (200*32)/4 = 1600 => code 0, reload=1599 + Watchdog::enableWatchdog(200U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1599U); +} + +TEST_F(WatchdogTest, prescaler_3000ms_32kHz) +{ + // ticks@/4=24000; /8=12000; /16=6000>4096; /32=3000 => code 3, reload=2999 + Watchdog::enableWatchdog(3000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 2999U); +} + +TEST_F(WatchdogTest, prescaler_10000ms_32kHz) +{ + // /64=5000>4096; /128=2500 => code 5, reload=2499 + Watchdog::enableWatchdog(10000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 5U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, prescaler_20000ms_32kHz) +{ + // /128=5000>4096; /256=2500 => code 6, reload=2499 + Watchdog::enableWatchdog(20000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, prescaler_2ms_40kHz) +{ + // ticks = (2*40)/4 = 20 => code 0, reload=19 + Watchdog::enableWatchdog(2U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 19U); +} + +TEST_F(WatchdogTest, prescaler_10ms_128kHz) +{ + // ticks = (10*128)/4 = 320 => code 0, reload=319 + Watchdog::enableWatchdog(10U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 319U); +} + +TEST_F(WatchdogTest, prescaler_2000ms_128kHz) +{ + // /4=64000; /8=32000; /16=16000; /32=8000; /64=4000 => code 4, reload=3999 + Watchdog::enableWatchdog(2000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 4U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, prescaler_4000ms_128kHz) +{ + // /64=8000; /128=4000 => code 5, reload=3999 + Watchdog::enableWatchdog(4000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 5U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, prescaler_8000ms_128kHz) +{ + // /128=8000; /256=4000 => code 6, reload=3999 + Watchdog::enableWatchdog(8000U, false, 128000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +// Re-enable with different timeouts + +TEST_F(WatchdogTest, reEnable_DifferentTimeout) +{ + Watchdog::enableWatchdog(250U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1999U); + + // Re-enable with larger timeout + Watchdog::enableWatchdog(1000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, reEnable_SmallToLarge) +{ + Watchdog::enableWatchdog(1U, false, 32000U); + EXPECT_EQ(fakeIwdg.RLR, 7U); + + Watchdog::enableWatchdog(32000U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 6U); + EXPECT_EQ(fakeIwdg.RLR, 3999U); +} + +TEST_F(WatchdogTest, enableWatchdog_300ms_32kHz) +{ + // ticks = (300*32)/4 = 2400 => code 0, reload=2399 + Watchdog::enableWatchdog(300U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 2399U); +} + +TEST_F(WatchdogTest, enableWatchdog_400ms_32kHz) +{ + // ticks = (400*32)/4 = 3200 => code 0, reload=3199 + Watchdog::enableWatchdog(400U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 3199U); +} + +TEST_F(WatchdogTest, enableWatchdog_150ms_40kHz) +{ + // ticks = (150*40)/4 = 1500 => code 0, reload=1499 + Watchdog::enableWatchdog(150U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 1499U); +} + +TEST_F(WatchdogTest, enableWatchdog_50ms_40kHz) +{ + // ticks = (50*40)/4 = 500 => code 0, reload=499 + Watchdog::enableWatchdog(50U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 499U); +} + +TEST_F(WatchdogTest, enableWatchdog_2000ms_40kHz) +{ + // /4=20000; /8=10000; /16=5000>4096; /32=2500 => code 3, reload=2499 + Watchdog::enableWatchdog(2000U, false, 40000U); + EXPECT_EQ(fakeIwdg.PR, 3U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} + +TEST_F(WatchdogTest, checkConfig_2000ms_40kHz_Match) +{ + fakeIwdg.PR = 3U; + fakeIwdg.RLR = 2499U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(2000U, 40000U)); +} + +TEST_F(WatchdogTest, checkConfig_2000ms_40kHz_WrongPR) +{ + fakeIwdg.PR = 2U; + fakeIwdg.RLR = 2499U; + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(2000U, 40000U)); +} + +TEST_F(WatchdogTest, checkConfig_2000ms_40kHz_WrongRLR) +{ + fakeIwdg.PR = 3U; + fakeIwdg.RLR = 2498U; + EXPECT_FALSE(Watchdog::checkWatchdogConfiguration(2000U, 40000U)); +} + +TEST_F(WatchdogTest, checkConfig_32000ms_32kHz_Match) +{ + fakeIwdg.PR = 6U; + fakeIwdg.RLR = 3999U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(32000U, 32000U)); +} + +TEST_F(WatchdogTest, checkConfig_1ms_32kHz_Match) +{ + fakeIwdg.PR = 0U; + fakeIwdg.RLR = 7U; + EXPECT_TRUE(Watchdog::checkWatchdogConfiguration(1U, 32000U)); +} + +TEST_F(WatchdogTest, serviceWatchdog_AfterDisable_StillWorks) +{ + Watchdog::disableWatchdog(); + uint32_t prev = Watchdog::getWatchdogServiceCounter(); + Watchdog::serviceWatchdog(); + EXPECT_EQ(Watchdog::getWatchdogServiceCounter(), prev + 1U); + EXPECT_EQ(fakeIwdg.KR, 0xAAAAU); +} + +TEST_F(WatchdogTest, disableWatchdog_DoesNotChangeWINR) +{ + fakeIwdg.WINR = 0xABCDU; + Watchdog::disableWatchdog(); + EXPECT_EQ(fakeIwdg.WINR, 0xABCDU); +} + +TEST_F(WatchdogTest, isResetFromWatchdog_ZeroCSR) +{ + fakeRcc.CSR = 0U; + EXPECT_FALSE(Watchdog::isResetFromWatchdog()); +} + +TEST_F(WatchdogTest, clearResetFlag_InitiallyZero) +{ + fakeRcc.CSR = 0U; + Watchdog::clearResetFlag(); + EXPECT_EQ(fakeRcc.CSR, RCC_CSR_RMVF); +} + +TEST_F(WatchdogTest, enableWatchdog_25ms_32kHz) +{ + // ticks = (25*32)/4 = 200 => code 0, reload=199 + Watchdog::enableWatchdog(25U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 199U); +} + +TEST_F(WatchdogTest, enableWatchdog_75ms_32kHz) +{ + // ticks = (75*32)/4 = 600 => code 0, reload=599 + Watchdog::enableWatchdog(75U, false, 32000U); + EXPECT_EQ(fakeIwdg.PR, 0U); + EXPECT_EQ(fakeIwdg.RLR, 599U); +} + +TEST_F(WatchdogTest, constructor_500ms_40kHz) +{ + Watchdog wdt(500U, 40000U); + EXPECT_EQ(fakeIwdg.PR, 1U); + EXPECT_EQ(fakeIwdg.RLR, 2499U); +} From aeb87bd01459d0dd7bc987dbb461bd568b0e3393 Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Tue, 2 Jun 2026 08:36:48 +0200 Subject: [PATCH 07/11] Add STM32 G474RE FreeRTOS board config --- .gitattributes | 1 + CMakePresets.json | 26 + NOTICE.md | 1 + doc/dev/modules/executables.rst | 9 + .../platforms/nucleo_g474re/CMakeLists.txt | 11 + .../platforms/nucleo_g474re/Options.cmake | 46 + .../bspConfiguration/CMakeLists.txt | 9 + .../bspConfiguration/doc/index.rst | 57 ++ .../bspConfiguration/doc/user/bspStdIo.rst | 34 + .../bspConfiguration/doc/user/bspUart.rst | 50 + .../bspConfiguration/doc/user/index.rst | 32 + .../include/bsp/uart/UartConfig.h | 28 + .../bspConfiguration/module.spec | 1 + .../bspConfiguration/src/bsp/stdIo/stdIo.cpp | 29 + .../src/bsp/uart/UartConfig.cpp | 50 + .../freeRtosCoreConfiguration/CMakeLists.txt | 3 + .../include/os/FreeRtosPlatformConfig.h | 118 +++ .../freeRtosCoreConfiguration/module.spec | 1 + .../nucleo_g474re/main/CMakeLists.txt | 63 ++ .../nucleo_g474re/main/doc/index.rst | 40 + .../nucleo_g474re/main/doc/user/StaticBsp.rst | 24 + .../nucleo_g474re/main/doc/user/index.rst | 32 + .../nucleo_g474re/main/doc/user/main.rst | 35 + .../nucleo_g474re/main/doc/user/startup.rst | 50 + .../main/doc/user/systems/CanSystem.rst | 79 ++ .../main/include/lifecycle/StaticBsp.h | 24 + .../main/include/systems/CanSystem.h | 83 ++ .../main/linkerscript/application.ld | 129 +++ .../platforms/nucleo_g474re/main/module.spec | 1 + .../main/src/lifecycle/StaticBsp.cpp | 25 + .../platforms/nucleo_g474re/main/src/main.cpp | 87 ++ .../nucleo_g474re/main/src/os/isr/isr_can.cpp | 19 + .../nucleo_g474re/main/src/os/isr/isr_sys.cpp | 29 + .../main/src/osHooks/freertos/osHooks.cpp | 19 + .../main/src/systems/CanSystem.cpp | 169 ++++ .../3rdparty/freertos_cm4_sysTick/.riminfo | 15 + .../freertos_cm4_sysTick/CMakeLists.txt | 14 + .../3rdparty/freertos_cm4_sysTick/LICENSE.md | 19 + .../3rdparty/freertos_cm4_sysTick/port.c | 857 ++++++++++++++++++ .../3rdparty/freertos_cm4_sysTick/portmacro.h | 254 ++++++ platforms/stm32/CMakeLists.txt | 5 + platforms/stm32/bsp/bspUart/CMakeLists.txt | 2 +- .../stm32/bsp/bspUart/include/bsp/Uart.h | 2 + .../can/transceiver/fdcan/FdCanTransceiver.h | 1 + .../transceiver/fdcan/FdCanTransceiver.cpp | 12 + 45 files changed, 2594 insertions(+), 1 deletion(-) create mode 100644 .gitattributes create mode 100644 executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_g474re/Options.cmake create mode 100644 executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/index.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspStdIo.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspUart.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/index.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/include/bsp/uart/UartConfig.h create mode 100644 executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec create mode 100644 executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/stdIo/stdIo.cpp create mode 100644 executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/uart/UartConfig.cpp create mode 100644 executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h create mode 100644 executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/module.spec create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/doc/index.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/doc/user/StaticBsp.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/doc/user/index.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/doc/user/main.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/doc/user/startup.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/doc/user/systems/CanSystem.rst create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/include/lifecycle/StaticBsp.h create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/include/systems/CanSystem.h create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/linkerscript/application.ld create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/module.spec create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/src/lifecycle/StaticBsp.cpp create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/src/main.cpp create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_can.cpp create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_sys.cpp create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/freertos/osHooks.cpp create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/src/systems/CanSystem.cpp create mode 100644 platforms/stm32/3rdparty/freertos_cm4_sysTick/.riminfo create mode 100644 platforms/stm32/3rdparty/freertos_cm4_sysTick/CMakeLists.txt create mode 100644 platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md create mode 100644 platforms/stm32/3rdparty/freertos_cm4_sysTick/port.c create mode 100644 platforms/stm32/3rdparty/freertos_cm4_sysTick/portmacro.h diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000000..43b4612516d --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.riminfo -whitespace diff --git a/CMakePresets.json b/CMakePresets.json index 19f96ee79a4..782ca1083ce 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -87,6 +87,26 @@ "OPENBSW_PLATFORM": "stm32" } }, + { + "name": "nucleo-g474re-freertos-gcc", + "generator": "Ninja Multi-Config", + "displayName": "NUCLEO-G474RE FreeRTOS configuration (GCC)", + "description": "Configure for NUCLEO-G474RE platform using ARM GCC toolchain", + "inherits": "_config-base", + "toolchainFile": "${sourceDir}/cmake/toolchains/ArmNoneEabi-gcc.cmake", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "BUILD_EXECUTABLE": "referenceApp", + "BUILD_TARGET_PLATFORM": "NUCLEO_G474RE", + "BUILD_TARGET_RTOS": "FREERTOS", + "STM32_CHIP": "STM32G474RE", + "CMAKE_ASM_FLAGS_RELWITHDEBINFO": "-g3", + "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo;Debug;Release", + "CMAKE_C_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" + } + }, { "name": "posix-freertos", "displayName": "POSIX-FREERTOS compliant configuration", @@ -268,6 +288,12 @@ "configurePreset": "tests-stm32-release", "configuration": "Release" }, + { + "name": "nucleo-g474re-freertos-gcc", + "displayName": "NUCLEO-G474RE FreeRTOS build (GCC)", + "description": "Build reference application for NUCLEO-G474RE platform with GCC", + "configurePreset": "nucleo-g474re-freertos-gcc" + }, { "name": "posix-freertos", "displayName": "build POSIX-FREERTOS", diff --git a/NOTICE.md b/NOTICE.md index fcd5577cd22..321ae205ec1 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -42,6 +42,7 @@ We recommend to read their licenses, as their terms may differ from the terms de | FreeRTOS Real Time Kernel | 10.6.2 | MIT | ``platforms/posix/3rdparty/freeRtosPosix/LICENSE.md`` | | FreeRTOS Real Time Kernel | 10.6.2 | MIT | ``platforms/s32k1xx/3rdparty/freertos_cm4_sysTick/LICENSE.md`` | | FreeRTOS Real Time Kernel | 10.6.2 | MIT | ``libs/3rdparty/freeRtos/LICENSE.md`` | +| FreeRTOS Real Time Kernel | 10.6.2 | MIT | ``platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md`` | | ThreadX Kernel | 6.4.3 | MIT | ``libs/3rdparty/threadx/LICENSE.md`` | | ThreadX Cortex M4 Port | 6.4.3 | MIT | ``platforms/s32k1xx/3rdparty/threadx/LICENSE.md`` | | ThreadX Linux Port | 6.4.3 | MIT | ``platforms/posix/3rdparty/threadx/LICENSE.md`` | diff --git a/doc/dev/modules/executables.rst b/doc/dev/modules/executables.rst index 255b965ec3f..c3a8490012d 100644 --- a/doc/dev/modules/executables.rst +++ b/doc/dev/modules/executables.rst @@ -47,6 +47,15 @@ S32K148EVB ../../../executables/referenceApp/platforms/s32k148evb/**/doc/index +STM32 Nucleo +++++++++++++ + +.. toctree:: + :maxdepth: 1 + :glob: + + ../../../executables/referenceApp/platforms/nucleo_*/**/doc/index + Safety ++++++ diff --git a/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt new file mode 100644 index 00000000000..1d7adfc3e7f --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt @@ -0,0 +1,11 @@ +# NUCLEO-G474RE platform for referenceApp + +add_subdirectory(bspConfiguration) +add_subdirectory(safety) +add_subdirectory(main) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_subdirectory(freeRtosCoreConfiguration) + add_library(freeRtosPort ALIAS freeRtosCm4SysTickPort) + add_library(freeRtosPortImpl ALIAS freeRtosCm4SysTick) +endif () diff --git a/executables/referenceApp/platforms/nucleo_g474re/Options.cmake b/executables/referenceApp/platforms/nucleo_g474re/Options.cmake new file mode 100644 index 00000000000..93498875ca1 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/Options.cmake @@ -0,0 +1,46 @@ +# ******************************************************************************* +# Copyright (c) 2026 An Dao +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +set(OPENBSW_PLATFORM + "stm32" + CACHE STRING "" FORCE) +set(STM32_CHIP + "STM32G474RE" + CACHE STRING "" FORCE) +set(BUILD_TARGET_RTOS + "FREERTOS" + CACHE STRING "") +set(PLATFORM_SUPPORT_CAN + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_IO + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_ETHERNET + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_TRANSPORT + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_UDS + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_WATCHDOG + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_MPU + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_STORAGE + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_ROM_CHECK + OFF + CACHE BOOL "" FORCE) diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..5cb06edc3e1 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(bspConfiguration src/bsp/stdIo/stdIo.cpp + src/bsp/uart/UartConfig.cpp) + +target_include_directories(bspConfiguration PUBLIC include) + +target_link_libraries( + bspConfiguration + PUBLIC bspUart + PRIVATE bspMcu platform) diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/index.rst b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/index.rst new file mode 100644 index 00000000000..4f9aaab32ef --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/index.rst @@ -0,0 +1,57 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspconfig_nucleo_g474re: + +bspConfiguration - NUCLEO-G474RE +================================ + +Overview +-------- + +The ``bspConfiguration`` module contains hardware-specific configuration for the +NUCLEO-G474RE evaluation board. Driver logic is separated from its configuration +so that users can customise a project for different boards or pin assignments +without modifying driver source code. + +The STM32G474RE platform exposes a minimal set of BSP peripherals required for +the CAN reference application: + +- **bspUart** - USART2 configuration (ST-LINK Virtual COM Port on PA2/PA3). +- **bspStdIo** - Standard I/O bridge that routes ``putByteToStdout`` / + ``getByteFromStdin`` through the configured UART instance. + +.. note:: + + Unlike the S32K148EVB reference application, this platform does not include + ADC, PWM, or digital I/O configuration modules because the CAN reference + application does not require analogue inputs or GPIO-driven outputs. + These modules can be added following the same configuration pattern if + a future application requires them. + +Hardware Summary +++++++++++++++++ + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + "MCU", "STM32G474RE (Arm Cortex-M4F, 170 MHz, single-precision FPU)" + "UART peripheral", "USART2 - routed to ST-LINK/V3 Virtual COM Port" + "UART TX pin", "PA2 (AF7)" + "UART RX pin", "PA3 (AF7)" + "UART baud rate", "115 200 baud (BRR = 1476 at 170 MHz APB1)" + "CAN peripheral", "FDCAN1 (configured in CanSystem, not bspConfiguration)" + +.. toctree:: + :hidden: + + user/index diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspStdIo.rst b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspStdIo.rst new file mode 100644 index 00000000000..c8aa2d42b46 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspStdIo.rst @@ -0,0 +1,34 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspConfig_StdIo_G474RE: + +bspStdIo +======== + +Overview +-------- + +The standard I/O bridge implements the ``putByteToStdout`` and +``getByteFromStdin`` C-linkage functions required by the OpenBSW logging +framework, routing character-level I/O through the UART ``TERMINAL`` instance +configured in :ref:`bspConfig_Uart_G474RE`. + +``putByteToStdout(uint8_t byte)`` transmits one byte over USART2 TX (PA2), +blocking by polling until the UART TX register is free. + +``getByteFromStdin()`` attempts to read one byte from USART2 RX (PA3) and is +non-blocking: if ``Uart::read()`` reports that no byte was received, it returns +``-1`` (POSIX ``getchar()`` convention); otherwise it returns the byte value +(0-255). + +Both functions resolve the UART instance once into a ``static`` local +reference, avoiding a global constructor ordering dependency. diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspUart.rst b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspUart.rst new file mode 100644 index 00000000000..aa8cc595e93 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/bspUart.rst @@ -0,0 +1,50 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspConfig_Uart_G474RE: + +bspUart Configuration +===================== + +Overview +-------- + +The UART configuration defines the hardware parameters for the on-board +ST-LINK Virtual COM Port of the NUCLEO-G474RE. A single UART instance +(``TERMINAL``) is configured for debug logging and interactive console use. + +``UartConfig.h`` declares the ``Uart::Id`` enumeration of available UART +instances, and ``UartConfig.cpp`` maps each ``Uart::Id`` to its hardware +parameters and provides the singleton accessor +``bsp::Uart::getInstance(Uart::Id)``. + +Configuration +------------- + +.. csv-table:: + :header: "Parameter", "Value" + :widths: 30, 70 + :width: 100% + + "Peripheral", "USART2 (APB1)" + "TX pin", "PA2, alternate function AF7" + "RX pin", "PA3, alternate function AF7" + "Baud rate", "115 200 baud" + "BRR", "170 000 000 / 115 200 = 1475.69 -> 1476" + +The resulting actual baud rate is 170 MHz / 1476 = 115 176 baud (0.021 % error), +well within the UART tolerance. + +.. note:: + + The UART driver uses polling mode (no DMA, no interrupts). For high- + throughput or latency-sensitive applications, consider extending the + ``bspUart`` BSP module with an interrupt-driven or DMA-backed backend. diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/index.rst b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/index.rst new file mode 100644 index 00000000000..5010a644f16 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/doc/user/index.rst @@ -0,0 +1,32 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +User Documentation +================== + +Detailed documentation for each BSP configuration module on the NUCLEO-G474RE +platform. + +Modules +------- + +.. toctree:: + :hidden: + + bspUart + bspStdIo + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + :ref:`bspConfig_Uart_G474RE`, "USART2 pin mapping, baud-rate register value, and clock-enable bits" + :ref:`bspConfig_StdIo_G474RE`, "Standard I/O bridge routing printf / scanf to the UART terminal" diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/include/bsp/uart/UartConfig.h b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/include/bsp/uart/UartConfig.h new file mode 100644 index 00000000000..b1f23bee3c4 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/include/bsp/uart/UartConfig.h @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "bsp/Uart.h" + +#include + +namespace bsp +{ + +enum class Uart::Id : size_t +{ + TERMINAL, + INVALID, +}; + +static constexpr size_t NUMBER_OF_UARTS = static_cast(Uart::Id::INVALID); + +} // namespace bsp diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/stdIo/stdIo.cpp b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/stdIo/stdIo.cpp new file mode 100644 index 00000000000..d6b4b5f14fb --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/stdIo/stdIo.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "bsp/uart/UartConfig.h" +#include "platform/estdint.h" + +extern "C" void putByteToStdout(uint8_t const byte) +{ + static bsp::Uart& uart = bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL); + uart.write(etl::span(&byte, 1U)); +} + +extern "C" int32_t getByteFromStdin() +{ + static bsp::Uart& uart = bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL); + uint8_t dataByte = 0; + if (uart.read(etl::span(&dataByte, 1U)) == 0U) + { + return -1; + } + return dataByte; +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/uart/UartConfig.cpp b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/uart/UartConfig.cpp new file mode 100644 index 00000000000..5c72b5d6cdc --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/src/bsp/uart/UartConfig.cpp @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// NUCLEO-G474RE: USART2 on PA2 (TX) / PA3 (RX), AF7 +// ST-LINK VCP, 115200 baud @ 170 MHz APB1 + +#include +#include +#include + +namespace bsp +{ + +Uart::UartConfig const Uart::_uartConfigs[] = { + { + USART2, // usart + GPIOA, // gpioPort + 2U, // txPin (PA2) + 3U, // rxPin (PA3) + 7U, // af (AF7) + 1476U, // brr (170000000 / 115200 = 1475.69 -> 1476) + RCC_AHB2ENR_GPIOAEN, // rccGpioEnBit + RCC_APB1ENR1_USART2EN, // rccUsartEnBit + &RCC->AHB2ENR, // rccGpioEnReg + &RCC->APB1ENR1, // rccUsartEnReg + }, +}; + +static Uart instances[] = { + Uart(Uart::Id::TERMINAL), +}; + +Uart& Uart::getInstance(Id id) +{ + ETL_ASSERT( + id < Id::INVALID, ETL_ERROR_GENERIC("UartId::INVALID is not a valid Uart identifier")); + static_assert( + NUMBER_OF_UARTS == static_cast(etl::size(instances)), + "Not enough Uart instances defined"); + return instances[static_cast(id)]; +} + +} // namespace bsp diff --git a/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..1e859851fac --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(freeRtosCoreConfiguration INTERFACE) +target_include_directories(freeRtosCoreConfiguration INTERFACE include) +target_link_libraries(freeRtosCoreConfiguration INTERFACE bsp bspMcu) diff --git a/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h new file mode 100644 index 00000000000..c55b65c60c8 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h @@ -0,0 +1,118 @@ +// Copyright 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +// FreeRTOS device-specific configuration for NUCLEO-G474RE. +// Included by asyncFreeRtos/freeRtosConfiguration/FreeRTOSConfig.h. + +#pragma once + +#include "bsp/SystemTime.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define configCPU_CLOCK_HZ (170000000UL) + +#undef configMINIMAL_STACK_SIZE +#define configMINIMAL_STACK_SIZE ((unsigned short)512) +#define configMAX_TASK_NAME_LEN (10) +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 30 +#undef configCHECK_FOR_STACK_OVERFLOW +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configRECORD_STACK_HIGH_ADDRESS 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_FPU 0 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 + +/* Memory allocation */ +#define configTOTAL_HEAP_SIZE 512 +#define configAPPLICATION_ALLOCATED_HEAP 1 + +/* Co-routine definitions */ +#define configMAX_CO_ROUTINE_PRIORITIES (0) + +/* Software timer definitions */ +#undef configTIMER_QUEUE_LENGTH +#define configTIMER_QUEUE_LENGTH 30 +#undef configTIMER_TASK_STACK_DEPTH +#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE) + +/* API function includes */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vResumeFromISR 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_eTaskGetState 1 +#undef INCLUDE_uxTaskGetStackHighWaterMark +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xQueueGetMutexHolder 1 +#undef INCLUDE_xTaskGetCurrentTaskHandle +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#undef INCLUDE_xTaskGetIdleTaskHandle +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_pcTaskGetTaskName 1 +#define INCLUDE_xEventGroupSetBitFromISR 1 +#undef INCLUDE_xTimerPendFunctionCall +#define INCLUDE_xTimerPendFunctionCall 1 + +/* Cortex-M specific definitions */ +#ifdef __NVIC_PRIO_BITS +#define configPRIO_BITS __NVIC_PRIO_BITS +#else +#define configPRIO_BITS 4 +#endif + +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0F +// ISRs that call FreeRTOS FromISR APIs must run at numeric priority >= this value. +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x06 + +#ifndef configKERNEL_INTERRUPT_PRIORITY +#define configKERNEL_INTERRUPT_PRIORITY \ + (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#endif + +#ifndef configMAX_SYSCALL_INTERRUPT_PRIORITY +#define configMAX_SYSCALL_INTERRUPT_PRIORITY \ + (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#endif + +/* Assert */ +#define DEV_ASSERT(x) +#if defined(__GNUC__) && (!defined(__ASSEMBLER__)) +#include "mcu/mcu.h" +#endif +#define configASSERT(x) DEV_ASSERT(x) + +/* Tickless idle */ +#define configUSE_TICKLESS_IDLE 0 +#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 +#define configUSE_TICKLESS_IDLE_DECISION_HOOK 0 + +/* Map FreeRTOS port handlers to CMSIS standard names */ +#define vPortSVCHandler SVC_Handler +#define xPortPendSVHandler PendSV_Handler +#define xPortSysTickHandler SysTick_Handler + +/* Run-time stats */ +#define configGENERATE_RUN_TIME_STATS (1) +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() getSystemTimeUs32Bit() + +#define configTIMER_SERVICE_TASK_NAME "TIMER_OS" + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/freeRtosCoreConfiguration/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt new file mode 100644 index 00000000000..8d236b3ca1e --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt @@ -0,0 +1,63 @@ +# startUp is an INTERFACE library - it carries no compiled sources, only +# propagates link dependencies (bspMcu, common, hardFaultHandler) and the linker +# script to consumers via INTERFACE properties. +add_library(startUp INTERFACE) + +target_link_libraries(startUp INTERFACE bspMcu common hardFaultHandler) + +add_library( + main + src/os/isr/isr_can.cpp + src/os/isr/isr_sys.cpp + src/systems/CanSystem.cpp + src/lifecycle/StaticBsp.cpp + src/main.cpp) + +target_include_directories(main PUBLIC include) + +target_link_libraries( + main + PUBLIC bspConfiguration + PRIVATE bspClock + bspTimer + bspUart + fdCanTransceiver + configuration + etl + lifecycle + safeSupervisor + startUp) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + target_link_libraries(main PRIVATE freeRtosCm4SysTick) +endif () + +# --whole-archive forces the linker to include ALL object files from the main +# library, even if no other translation unit references them. This is required +# because isr_can.cpp provides strong ISR definitions (FDCAN1_IT0_IRQHandler, +# FDCAN1_IT1_IRQHandler) that override the weak default handlers in the startup +# assembly. Without --whole-archive the linker would discard isr_can.o as +# "unreferenced" and the weak stubs would remain, silently swallowing CAN IRQs. +set_target_properties(main PROPERTIES INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE + "") +target_link_options( + main + INTERFACE + "LINKER:--whole-archive" + "$" + "LINKER:--no-whole-archive") + +set_target_properties( + startUp + PROPERTIES PROP_LINKER_SCRIPT + "${CMAKE_CURRENT_SOURCE_DIR}/linkerscript/application.ld") +set_target_properties( + startUp + PROPERTIES LINK_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/linkerscript/application.ld") + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_library(osHooks src/osHooks/freertos/osHooks.cpp) + target_link_libraries(osHooks PRIVATE common freeRtosCm4SysTick) +endif () +target_include_directories(osHooks PRIVATE include) diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/index.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/index.rst new file mode 100644 index 00000000000..9fa4523d3e1 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/index.rst @@ -0,0 +1,40 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_main: + +main - NUCLEO-G474RE +==================== + +Overview +-------- + +The ``main`` module contains the platform-specific entry point, startup code, linker +script, and lifecycle systems for the NUCLEO-G474RE evaluation board: + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + "MCU", "STM32G474RE - Arm Cortex-M4F, single-precision FPU" + "Core clock", "170 MHz (HSI16 with PLL, boost mode enabled)" + "Flash", "512 KB" + "SRAM", "128 KB (SRAM1 80 KB + SRAM2 16 KB + CCM SRAM 32 KB)" + "CAN", "FDCAN1 - PA11 (RX) / PA12 (TX), AF9, 500 kbit/s" + "Debug UART", "USART2 - PA2 TX (AF7), 115200 baud, routed to ST-LINK VCP" + "User LED", "LD2 on PA5 (active-high)" + "Debug interface", "On-board ST-LINK/V3 (SWD)" + +See :ref:`bspconfig_nucleo_g474re` for the UART and standard I/O configuration, and the +user documentation for the sub-modules: + +.. toctree:: + user/index diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/StaticBsp.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/StaticBsp.rst new file mode 100644 index 00000000000..6bddfae3da2 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/StaticBsp.rst @@ -0,0 +1,24 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_StaticBsp: + +Static BSP +========== + +Overview +-------- + +The ``StaticBsp`` class aggregates the BSP peripherals that must be operational before +the lifecycle starts ("static"): the system timer (DWT cycle counter based) and the +UART terminal (USART2, see :ref:`bspConfig_Uart_G474RE`). It is instantiated in +``main.cpp`` and initialized from ``main()`` before ``app_main()``, so timing services +and serial output are available during the early lifecycle phases. diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/index.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/index.rst new file mode 100644 index 00000000000..e089d3fb166 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/index.rst @@ -0,0 +1,32 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +User Documentation +================== + +Find the detailed information about each module below: + +.. toctree:: + :hidden: + + startup + main + StaticBsp + systems/CanSystem + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + :ref:`nucleo_g474re_startup`, "Startup Code and Reset Handler" + :ref:`nucleo_g474re_main_entry`, "Main File and boot sequence" + :ref:`nucleo_g474re_StaticBsp`, "Static BSP - pre-lifecycle peripheral init" + :ref:`nucleo_g474re_CanSystem`, "CAN System (FDCAN1, 500 kbit/s)" diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/main.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/main.rst new file mode 100644 index 00000000000..a41942fd4ca --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/main.rst @@ -0,0 +1,35 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_main_entry: + +main File +========= + +The ``main.cpp`` module is the entry point of the NUCLEO-G474RE reference application. +``SystemInit()``, called from ``Reset_Handler``, configures the PLL to 170 MHz, switches +on the LD2 LED (PA5), and sets up early USART2 TX output for use before the BSP UART +driver is initialized. + +``main()`` constructs the safety supervisor, initializes the static BSP (system timer +and UART terminal, see :ref:`nucleo_g474re_StaticBsp`) and calls ``app_main()``, which +starts the lifecycle manager and the FreeRTOS scheduler. The task contexts are defined +in ``async/Config.h`` of ``referenceApp/asyncCoreConfiguration``: ``TASK_BACKGROUND``, +``TASK_BSP``, ``TASK_UDS``, ``TASK_DEMO``, ``TASK_ETHERNET``, ``TASK_CAN``, +``TASK_SYSADMIN`` and ``TASK_SAFETY``. + +``platformLifecycleAdd()`` registers the platform's ``CanSystem`` at runlevel 2 in the +``TASK_CAN`` context; the remaining components - including ``RuntimeSystem`` and +``SafetySystem`` at runlevel 1 - are registered by the shared application code. + +``HardFault_Handler`` branches to ``customHardFaultHandler`` from the +``hardFaultHandler`` module, which dumps the registers to RAM and ends in +``HardFault_Handler_Final``, which performs a system reset. diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/startup.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/startup.rst new file mode 100644 index 00000000000..fc83573298b --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/startup.rst @@ -0,0 +1,50 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_startup: + +Startup Code +============ + +The startup code (``platforms/stm32/bsp/bspMcu/startup/startup_stm32g474xx.s``) provides +the vector table and the ``Reset_Handler`` for the STM32G474xx. + +Vector Table +------------ + +The vector table is placed at the base of flash (``0x0800_0000``) in the +``.isr_vector`` section. It contains the initial stack pointer, the Cortex-M4 exception +vectors, and the 102 STM32G474xx interrupt vectors. Every handler is a weak alias of +``Default_Handler`` (an infinite loop) and is overridden by a strong definition where +the application implements one (e.g. ``HardFault_Handler``, ``FDCAN1_IT0_IRQHandler``). + +Reset Handler +------------- + +``Reset_Handler`` executes the following sequence: + +1. Load the initial stack pointer (``_estack``). +2. Copy the ``.data`` section from flash to SRAM. +3. Zero-fill the ``.bss`` section. +4. Enable the FPU (CP10/CP11 full access in ``CPACR``, followed by ``dsb``/``isb``). +5. Call ``SystemInit()`` (PLL and early peripheral setup, defined in ``main.cpp``). +6. Call ``__libc_init_array`` (C++ global/static constructors). +7. Call ``main()``. + +The startup code does not mask or unmask interrupts; interrupt control is left to the +FreeRTOS port once the scheduler starts. + +Memory Layout +------------- + +The linker script places code in 512 KB flash at ``0x0800_0000`` and data in 128 KB of +contiguous SRAM at ``0x2000_0000`` (SRAM1 80 KB + SRAM2 16 KB + CCM SRAM 32 KB, which +is aliased at ``0x2001_8000``). The stack grows down from the top of SRAM. diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/systems/CanSystem.rst b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/systems/CanSystem.rst new file mode 100644 index 00000000000..3d443708a9b --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/doc/user/systems/CanSystem.rst @@ -0,0 +1,79 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_g474re_CanSystem: + +CAN System +========== + +Overview +-------- + +The ``CanSystem`` implements the ``ICanSystem`` interface for the NUCLEO-G474RE using +the STM32G474RE's FDCAN1 peripheral. It manages a single ``FdCanTransceiver`` +(``CAN_0``), participates in the lifecycle as a ``SingleContextLifecycleComponent`` +running in the ``TASK_CAN`` context, and provides the ``extern "C"`` ISR trampolines. +It is registered as an ``etl::singleton`` so the ISR handlers can reach the instance +without global variables. + +Hardware Configuration +---------------------- + +FDCAN1 uses PA11 (RX) and PA12 (TX), alternate function AF9. An external CAN +transceiver is required to drive the bus. Bit timing at 170 MHz kernel clock: +prescaler 20 (8.5 MHz time quantum), 17 TQ per bit (1 sync + 14 seg1 + 2 seg2), +giving 500 kbit/s with a sample point of 88.2 %. + +Lifecycle +--------- + +``init()`` only signals ``transitionDone()`` - the peripheral is untouched until +``run()``, which: + +1. Enables the ``CanRxRunnable`` and sets the accept-all frame filter. +2. Initializes and opens the ``FdCanTransceiver`` (FDCAN1 register setup, normal mode). +3. Enables the RX FIFO 0 interrupt and configures the NVIC: + + .. code-block:: c++ + + FDCAN1->IE = FDCAN_IE_RF0NE; + FDCAN1->ILS = 0U; + FDCAN1->ILE = FDCAN_ILE_EINT0; + + NVIC_SetPriority(FDCAN1_IT0_IRQn, 6); + NVIC_SetPriority(FDCAN1_IT1_IRQn, 6); + NVIC_ClearPendingIRQ(FDCAN1_IT0_IRQn); + NVIC_ClearPendingIRQ(FDCAN1_IT1_IRQn); + NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + NVIC_EnableIRQ(FDCAN1_IT1_IRQn); + +4. Schedules a periodic 50 ms timer that runs the ``CanTxRunnable``. + +``shutdown()`` closes and shuts down the transceiver and disables the RX runnable. + +Interrupt Handling +------------------ + +With ``ILS = 0`` all FDCAN1 interrupt sources are routed to interrupt line 0 (only +``EINT0`` is enabled), so ``FDCAN1_IT0_IRQHandler`` handles both RX and TX events while +``FDCAN1_IT1_IRQHandler`` / ``call_can_isr_TX()`` are empty. ``call_can_isr_RX()`` +reads ``FDCAN1->IR`` once and: + +- on ``RF0N``, reads pending frames from the hardware FIFO into the software queue + under lock (``FdCanTransceiver::receiveInterrupt``) and, if frames were received, + dispatches the ``CanRxRunnable`` into the ``TASK_CAN`` context; +- on ``TC``, forwards the TX-complete event to ``FdCanTransceiver::transmitInterrupt``. + +The ``CanRxRunnable`` calls ``receiveTask()``, which drains the software queue and +notifies the registered frame listeners. The ``CanTxRunnable`` calls +``FdCanTransceiver::pollTxCallback(CAN_0)``, which invokes the sent listener of +completed transmissions; the 50 ms timer guarantees this poll runs even when a +dispatch from the ISR is dropped by the async dedup logic. diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/include/lifecycle/StaticBsp.h b/executables/referenceApp/platforms/nucleo_g474re/main/include/lifecycle/StaticBsp.h new file mode 100644 index 00000000000..d81d46d8d9e --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/include/lifecycle/StaticBsp.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace platform +{ + +class StaticBsp +{ +public: + void init(); +}; + +} // namespace platform diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/include/systems/CanSystem.h b/executables/referenceApp/platforms/nucleo_g474re/main/include/systems/CanSystem.h new file mode 100644 index 00000000000..d8f777c6f12 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/include/systems/CanSystem.h @@ -0,0 +1,83 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace systems +{ + +/** + * CAN system for the NUCLEO-G474RE: manages a single FdCanTransceiver on FDCAN1 + * (PA11 RX / PA12 TX, 500 kbit/s). Registered as an etl::singleton so the ISR + * trampolines can reach the instance without global variables. + */ +class CanSystem +: public ::lifecycle::SingleContextLifecycleComponent +, public ::can::ICanSystem +, public ::etl::singleton_base +{ +public: + CanSystem(::async::ContextType context); + + void init() override; + void run() override; + void shutdown() override; + + ::can::ICanTransceiver* getCanTransceiver(uint8_t busId) override; + + /** + * Dispatches CAN RX processing into the async context. + * \note Called from ISR context (call_can_isr_RX). Must be safe for ISR invocation. + */ + void dispatchRxTask(); + + /** Dispatches CAN TX confirmation processing into the async context. */ + void dispatchTxTask(); + +private: + class CanRxRunnable : public ::async::RunnableType + { + public: + explicit CanRxRunnable(CanSystem& parent); + + void execute() override; + + void setEnabled(bool enabled) { _enabled = enabled; } + + private: + CanSystem& _parent; + bool _enabled; + }; + + class CanTxRunnable : public ::async::RunnableType + { + public: + explicit CanTxRunnable(CanSystem& parent) : _parent(parent) {} + + void execute() override; + + private: + CanSystem& _parent; + }; + + ::async::ContextType _context; + ::bios::FdCanTransceiver _transceiver0; + CanRxRunnable _canRxRunnable; + CanTxRunnable _canTxRunnable; + ::async::TimeoutType _canTxTimeout; +}; + +} // namespace systems diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/linkerscript/application.ld b/executables/referenceApp/platforms/nucleo_g474re/main/linkerscript/application.ld new file mode 100644 index 00000000000..949c035248e --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/linkerscript/application.ld @@ -0,0 +1,129 @@ +/* Linker script for STM32G474RE (NUCLEO-G474RE) + * + * SPDX-License-Identifier: Apache-2.0 + * + * Flash: 512 KB at 0x08000000 + * SRAM: 128 KB at 0x20000000 + */ + +ENTRY(Reset_Handler) + +_Min_Heap_Size = 0x200; +_Min_Stack_Size = 0x400; + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 512K + /* Top 1 KB (0x2001FC00-0x20020000) is reserved for the hard fault + * handler dump region and survives reset (never initialized here). */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 127K +} + +SECTIONS +{ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) + . = ALIGN(4); + } > FLASH + + .text : + { + . = ALIGN(4); + *(.text) + *(.text*) + *(.glue_7) + *(.glue_7t) + *(.eh_frame) + + KEEP(*(.init)) + KEEP(*(.fini)) + + . = ALIGN(4); + _etext = .; + } > FLASH + + .rodata : + { + . = ALIGN(4); + *(.rodata) + *(.rodata*) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + .ARM : + { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > FLASH + + .preinit_array : + { + PROVIDE_HIDDEN(__preinit_array_start = .); + KEEP(*(.preinit_array*)) + PROVIDE_HIDDEN(__preinit_array_end = .); + } > FLASH + + .init_array : + { + PROVIDE_HIDDEN(__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array*)) + PROVIDE_HIDDEN(__init_array_end = .); + } > FLASH + + .fini_array : + { + PROVIDE_HIDDEN(__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array*)) + PROVIDE_HIDDEN(__fini_array_end = .); + } > FLASH + + _sidata = LOADADDR(.data); + + .data : + { + . = ALIGN(4); + _sdata = .; + *(.data) + *(.data*) + . = ALIGN(4); + _edata = .; + } > RAM AT> FLASH + + .bss : + { + . = ALIGN(4); + _sbss = .; + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + __bss_end__ = _ebss; + } > RAM + + ._user_heap_stack : + { + . = ALIGN(8); + __end__ = .; + _end = .; + PROVIDE(end = .); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } > RAM + + _estack = ORIGIN(RAM) + LENGTH(RAM); + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/module.spec b/executables/referenceApp/platforms/nucleo_g474re/main/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/lifecycle/StaticBsp.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/lifecycle/StaticBsp.cpp new file mode 100644 index 00000000000..40d1147666f --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/lifecycle/StaticBsp.cpp @@ -0,0 +1,25 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "lifecycle/StaticBsp.h" + +#include "bsp/timer/SystemTimer.h" +#include "bsp/uart/UartConfig.h" + +namespace platform +{ + +void StaticBsp::init() +{ + initSystemTimer(); + bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL).init(); +} + +} // namespace platform diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/main.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/main.cpp new file mode 100644 index 00000000000..80b656ce993 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/main.cpp @@ -0,0 +1,87 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "async/Config.h" +#include "clock/clockConfig.h" +#include "lifecycle/StaticBsp.h" +#include "mcu/mcu.h" +#include "systems/CanSystem.h" + +#include +#include +#include + +extern void app_main(); + +extern "C" +{ +void SystemInit() +{ + configurePll(); + + // LED ON: PA5 = LD2 (output push-pull) + static constexpr uint8_t LED_PIN = 5U; + RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; + GPIOA->MODER &= ~(3U << (LED_PIN * 2U)); + GPIOA->MODER |= (1U << (LED_PIN * 2U)); // GPIO output mode + GPIOA->BSRR = (1U << LED_PIN); + + // Early USART2 TX (PA2 AF7, 115200 @ 170 MHz), usable before the BSP + // UART driver is initialized. + // BRR = 170 000 000 / 115 200 = 1475.69 -> 1476 + static constexpr uint8_t USART_TX_PIN = 2U; + static constexpr uint8_t USART_TX_AF = 7U; // AF7 = USART2_TX on PA2 + static constexpr uint32_t USART_BRR_115200_170MHZ = 1476U; + + RCC->APB1ENR1 |= RCC_APB1ENR1_USART2EN; + (void)RCC->APB1ENR1; // Read-back for clock propagation + (void)RCC->APB1ENR1; + GPIOA->MODER &= ~(3U << (USART_TX_PIN * 2U)); + GPIOA->MODER |= (2U << (USART_TX_PIN * 2U)); // Alternate function mode + GPIOA->AFR[0] &= ~(0xFU << (USART_TX_PIN * 4U)); + GPIOA->AFR[0] |= (static_cast(USART_TX_AF) << (USART_TX_PIN * 4U)); + USART2->CR1 = 0; + USART2->CR2 = 0; + USART2->CR3 = 0; + USART2->BRR = USART_BRR_115200_170MHZ; + USART2->CR1 = USART_CR1_TE | USART_CR1_UE; + while ((USART2->ISR & USART_ISR_TEACK) == 0) {} // Wait for TX enable ack +} +} // extern "C" + +namespace platform +{ +StaticBsp staticBsp; + +StaticBsp& getStaticBsp() { return staticBsp; } + +::etl::typed_storage<::systems::CanSystem> canSystem; + +void platformLifecycleAdd(::lifecycle::LifecycleManager& lifecycleManager, uint8_t const level) +{ + if (level == 2U) + { + lifecycleManager.addComponent("can", canSystem.create(TASK_CAN), level); + } +} +} // namespace platform + +namespace systems +{ +::can::ICanSystem& getCanSystem() { return *::platform::canSystem; } +} // namespace systems + +int main() +{ + ::safety::safeSupervisorConstructor.construct(); + ::platform::staticBsp.init(); + app_main(); + return 1; +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_can.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_can.cpp new file mode 100644 index 00000000000..e2f4dc34f81 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_can.cpp @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +extern "C" +{ +extern void call_can_isr_RX(); +extern void call_can_isr_TX(); + +void FDCAN1_IT0_IRQHandler(void) { call_can_isr_RX(); } + +void FDCAN1_IT1_IRQHandler(void) { call_can_isr_TX(); } +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_sys.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_sys.cpp new file mode 100644 index 00000000000..58b8505a04d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/os/isr/isr_sys.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "reset/softwareSystemReset.h" + +extern "C" +{ +void HardFault_Handler() +{ + // WARNING: + // Do NOT modify this function if possible. Use HardFault_Handler_Final instead. + // Refer to hardFaultHandler documentation for details. +#ifndef UNIT_TEST + asm volatile("b customHardFaultHandler"); +#else + while (true) {} +#endif +} + +void HardFault_Handler_Final() { softwareSystemReset(); } + +} // extern "C" diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/freertos/osHooks.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/freertos/osHooks.cpp new file mode 100644 index 00000000000..0e19b52cc59 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/freertos/osHooks.cpp @@ -0,0 +1,19 @@ +// Copyright 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FreeRTOS.h" +#include "task.h" + +extern "C" +{ +void vApplicationStackOverflowHook(TaskHandle_t /* xTask */, char* /* pcTaskName */) +{ + while (true) {} +} + +void vApplicationMallocFailedHook(void) +{ + while (true) {} +} +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/systems/CanSystem.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/systems/CanSystem.cpp new file mode 100644 index 00000000000..b1f72b9aabd --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/systems/CanSystem.cpp @@ -0,0 +1,169 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "systems/CanSystem.h" + +#include "async/Config.h" +#include "async/Hook.h" +#include "busid/BusId.h" +#include "mcu/mcu.h" + +namespace +{ +// FDCAN1 configuration for STM32G474RE +// FDCAN1: PA11 (RX, AF9), PA12 (TX, AF9), 500 kbit/s @ 170 MHz +// NBTP: NSJW=1TQ, NBS1=14TQ, NBS2=2TQ, NBRP=20 -> 500 kbit/s +::bios::FdCanDevice::Config const fdcan1Config = { + FDCAN1, // peripheral + 20U, // prescaler (170 MHz / 20 = 8.5 MHz -> 17 TQ @ 500k) + 13U, // tseg1 (14 TQ - 1) + 1U, // tseg2 (2 TQ - 1) + 0U, // sjw (1 TQ - 1) + GPIOA, // rxPort + 11U, // rxPin + 9U, // rxAf (AF9 = FDCAN1) + GPIOA, // txPort + 12U, // txPin + 9U, // txAf (AF9 = FDCAN1) +}; +} // namespace + +namespace systems +{ + +CanSystem::CanSystem(::async::ContextType context) +: ::lifecycle::SingleContextLifecycleComponent(context) +, ::etl::singleton_base(*this) +, _context(context) +, _transceiver0(context, ::busid::CAN_0, fdcan1Config) +, _canRxRunnable(*this) +, _canTxRunnable(*this) +{} + +void CanSystem::init() { transitionDone(); } + +void CanSystem::run() +{ + _canRxRunnable.setEnabled(true); + + // Accept all CAN frames (no HW filter). fFilterIds/fFilterCount are + // uninitialized in FdCanDevice - must explicitly set to accept-all. + _transceiver0.fDevice.fFilterIds = nullptr; + _transceiver0.fDevice.fFilterCount = 0U; + + (void)_transceiver0.init(); + (void)_transceiver0.open(); + + // Enable FDCAN IRQs only after the transceiver is open, so the async + // framework is ready before ISRs fire. + // Enable RX interrupt only. TCE is managed by FdCanDevice per-TX + // (enabled before listener TX, disabled by transmitISR). + FDCAN1->IE = FDCAN_IE_RF0NE; + FDCAN1->ILS = 0U; + FDCAN1->ILE = FDCAN_ILE_EINT0; + + // ISR priority must be numerically >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + // to safely use FreeRTOS FromISR APIs. + NVIC_SetPriority(FDCAN1_IT0_IRQn, 6); + NVIC_SetPriority(FDCAN1_IT1_IRQn, 6); + NVIC_ClearPendingIRQ(FDCAN1_IT0_IRQn); + NVIC_ClearPendingIRQ(FDCAN1_IT1_IRQn); + NVIC_EnableIRQ(FDCAN1_IT0_IRQn); + NVIC_EnableIRQ(FDCAN1_IT1_IRQn); + + // Schedule periodic TX callback poll. The ISR dispatches canTxRunnable + // via async::execute, but dedup drops repeated dispatches when the + // runnable is already queued. The periodic timer ensures pollTxCallback + // runs within 50ms regardless of dedup. + ::async::scheduleAtFixedRate( + _context, _canTxRunnable, _canTxTimeout, 50U, ::async::TimeUnit::MILLISECONDS); + + transitionDone(); +} + +void CanSystem::shutdown() +{ + (void)_transceiver0.close(); + _transceiver0.shutdown(); + + _canRxRunnable.setEnabled(false); + + transitionDone(); +} + +::can::ICanTransceiver* CanSystem::getCanTransceiver(uint8_t busId) +{ + if (busId == ::busid::CAN_0) + { + return &_transceiver0; + } + return nullptr; +} + +void CanSystem::dispatchRxTask() { ::async::execute(_context, _canRxRunnable); } + +void CanSystem::dispatchTxTask() { ::async::execute(_context, _canTxRunnable); } + +void CanSystem::CanTxRunnable::execute() +{ + // Process TX completion in task context. The ISR dispatches here via + // async::execute after TC fires. pollTxCallback checks the TX queue and + // invokes the sent listener of each completed frame. + ::bios::FdCanTransceiver::pollTxCallback(::busid::CAN_0); +} + +CanSystem::CanRxRunnable::CanRxRunnable(CanSystem& parent) : _parent(parent), _enabled(false) {} + +void CanSystem::CanRxRunnable::execute() +{ + if (_enabled) + { + _parent._transceiver0.receiveTask(); + } +} + +} // namespace systems + +extern "C" +{ +/** + * Combined RX + TX ISR - all FDCAN1 interrupts routed to IT0. + * Checks IR register to determine if RX, TX, or both occurred. + */ +void call_can_isr_RX() +{ + ::asyncEnterIsrGroup(ISR_GROUP_CAN); + + uint32_t const ir = FDCAN1->IR; + + if ((ir & FDCAN_IR_RF0N) != 0U) + { + ::async::LockType const lock; + uint8_t framesReceived = ::bios::FdCanTransceiver::receiveInterrupt(::busid::CAN_0); + + if (framesReceived > 0) + { + ::systems::CanSystem::instance().dispatchRxTask(); + } + } + + if ((ir & FDCAN_IR_TC) != 0U) + { + ::bios::FdCanTransceiver::transmitInterrupt(::busid::CAN_0); + } + + ::asyncLeaveIsrGroup(ISR_GROUP_CAN); +} + +void call_can_isr_TX() +{ + // Not used - all interrupts routed to IT0 +} +} diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/.riminfo b/platforms/stm32/3rdparty/freertos_cm4_sysTick/.riminfo new file mode 100644 index 00000000000..089fb228cb7 --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/.riminfo @@ -0,0 +1,15 @@ +f56abc63a845842f780631834d300cda2953f026 + +RIM Info file. You're welcome to read but don't write it. +Instead, use RIM commands to do the things you want to do. +BEWARE: Any manual modification will invalidate the file! + +remote_url : https://github.com/FreeRTOS/FreeRTOS-Kernel.git +revision_sha1 : 07055b94bac48f5808f005f22099d9a4337936f3 +target_revision: V10.6.2 +ignores : CMakeLists.txt,LICENSE.md +checksum : 4c1fabc997d04aaca896c03b138e2cb38d71e1d9 +subdir : portable/GCC/ARM_CM4F + +committer_date : 2023-11-29 14:13:55 +0000 + diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/CMakeLists.txt b/platforms/stm32/3rdparty/freertos_cm4_sysTick/CMakeLists.txt new file mode 100644 index 00000000000..c932d94c7f5 --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/CMakeLists.txt @@ -0,0 +1,14 @@ +add_library(freeRtosCm4SysTickPort INTERFACE) + +target_include_directories(freeRtosCm4SysTickPort INTERFACE .) + +add_library(freeRtosCm4SysTick port.c) + +target_include_directories(freeRtosCm4SysTick PUBLIC .) + +target_link_libraries( + freeRtosCm4SysTick + PUBLIC freeRtos + PRIVATE bspInterrupts) + +target_compile_options(freeRtosCm4SysTick PRIVATE -Wno-unused-but-set-variable) diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md b/platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md new file mode 100644 index 00000000000..9cf106272ac --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/port.c b/platforms/stm32/3rdparty/freertos_cm4_sysTick/port.c new file mode 100644 index 00000000000..73430b5bfd3 --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/port.c @@ -0,0 +1,857 @@ +/* + * FreeRTOS Kernel V10.6.2 + * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + +/*----------------------------------------------------------- +* Implementation of functions defined in portable.h for the ARM CM4F port. +*----------------------------------------------------------*/ + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#ifndef __VFP_FP__ + #error This port can only be used when the project options are configured to enable hardware floating point support. +#endif + +/* Constants required to manipulate the core. Registers first... */ +#define portNVIC_SYSTICK_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000e010 ) ) +#define portNVIC_SYSTICK_LOAD_REG ( *( ( volatile uint32_t * ) 0xe000e014 ) ) +#define portNVIC_SYSTICK_CURRENT_VALUE_REG ( *( ( volatile uint32_t * ) 0xe000e018 ) ) +#define portNVIC_SHPR3_REG ( *( ( volatile uint32_t * ) 0xe000ed20 ) ) +/* ...then bits in the registers. */ +#define portNVIC_SYSTICK_CLK_BIT ( 1UL << 2UL ) +#define portNVIC_SYSTICK_INT_BIT ( 1UL << 1UL ) +#define portNVIC_SYSTICK_ENABLE_BIT ( 1UL << 0UL ) +#define portNVIC_SYSTICK_COUNT_FLAG_BIT ( 1UL << 16UL ) +#define portNVIC_PENDSVCLEAR_BIT ( 1UL << 27UL ) +#define portNVIC_PEND_SYSTICK_SET_BIT ( 1UL << 26UL ) +#define portNVIC_PEND_SYSTICK_CLEAR_BIT ( 1UL << 25UL ) + +/* Constants used to detect a Cortex-M7 r0p1 core, which should use the ARM_CM7 + * r0p1 port. */ +#define portCPUID ( *( ( volatile uint32_t * ) 0xE000ed00 ) ) +#define portCORTEX_M7_r0p1_ID ( 0x410FC271UL ) +#define portCORTEX_M7_r0p0_ID ( 0x410FC270UL ) + +#define portMIN_INTERRUPT_PRIORITY ( 255UL ) +#define portNVIC_PENDSV_PRI ( ( ( uint32_t ) portMIN_INTERRUPT_PRIORITY ) << 16UL ) +#define portNVIC_SYSTICK_PRI ( ( ( uint32_t ) portMIN_INTERRUPT_PRIORITY ) << 24UL ) + +/* Constants required to check the validity of an interrupt priority. */ +#define portFIRST_USER_INTERRUPT_NUMBER ( 16 ) +#define portNVIC_IP_REGISTERS_OFFSET_16 ( 0xE000E3F0 ) +#define portAIRCR_REG ( *( ( volatile uint32_t * ) 0xE000ED0C ) ) +#define portMAX_8_BIT_VALUE ( ( uint8_t ) 0xff ) +#define portTOP_BIT_OF_BYTE ( ( uint8_t ) 0x80 ) +#define portMAX_PRIGROUP_BITS ( ( uint8_t ) 7 ) +#define portPRIORITY_GROUP_MASK ( 0x07UL << 8UL ) +#define portPRIGROUP_SHIFT ( 8UL ) + +/* Masks off all bits but the VECTACTIVE bits in the ICSR register. */ +#define portVECTACTIVE_MASK ( 0xFFUL ) + +/* Constants required to manipulate the VFP. */ +#define portFPCCR ( ( volatile uint32_t * ) 0xe000ef34 ) /* Floating point context control register. */ +#define portASPEN_AND_LSPEN_BITS ( 0x3UL << 30UL ) + +/* Constants required to set up the initial stack. */ +#define portINITIAL_XPSR ( 0x01000000 ) +#define portINITIAL_EXC_RETURN ( 0xfffffffd ) + +/* The systick is a 24-bit counter. */ +#define portMAX_24_BIT_NUMBER ( 0xffffffUL ) + +/* For strict compliance with the Cortex-M spec the task start address should + * have bit-0 clear, as it is loaded into the PC on exit from an ISR. */ +#define portSTART_ADDRESS_MASK ( ( StackType_t ) 0xfffffffeUL ) + +/* A fiddle factor to estimate the number of SysTick counts that would have + * occurred while the SysTick counter is stopped during tickless idle + * calculations. */ +#define portMISSED_COUNTS_FACTOR ( 94UL ) + +/* Let the user override the default SysTick clock rate. If defined by the + * user, this symbol must equal the SysTick clock rate when the CLK bit is 0 in the + * configuration register. */ +#ifndef configSYSTICK_CLOCK_HZ + #define configSYSTICK_CLOCK_HZ ( configCPU_CLOCK_HZ ) + /* Ensure the SysTick is clocked at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( portNVIC_SYSTICK_CLK_BIT ) +#else + /* Select the option to clock SysTick not at the same frequency as the core. */ + #define portNVIC_SYSTICK_CLK_BIT_CONFIG ( 0 ) +#endif + +/* Let the user override the pre-loading of the initial LR with the address of + * prvTaskExitError() in case it messes up unwinding of the stack in the + * debugger. */ +#ifdef configTASK_RETURN_ADDRESS + #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS +#else + #define portTASK_RETURN_ADDRESS prvTaskExitError +#endif + +/* + * Setup the timer to generate the tick interrupts. The implementation in this + * file is weak to allow application writers to change the timer used to + * generate the tick interrupt. + */ +void vPortSetupTimerInterrupt( void ); + +/* + * Exception handlers. + */ +void xPortPendSVHandler( void ) __attribute__( ( naked ) ); +void xPortSysTickHandler( void ); +void vPortSVCHandler( void ) __attribute__( ( naked ) ); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +static void prvPortStartFirstTask( void ) __attribute__( ( naked ) ); + +/* + * Function to enable the VFP. + */ +static void vPortEnableVFP( void ) __attribute__( ( naked ) ); + +/* + * Used to catch tasks that attempt to return from their implementing function. + */ +static void prvTaskExitError( void ); + +/*-----------------------------------------------------------*/ + +/* Each task maintains its own interrupt status in the critical nesting + * variable. */ +static UBaseType_t uxCriticalNesting = 0xaaaaaaaa; + +/* + * The number of SysTick increments that make up one tick period. + */ +#if ( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t ulTimerCountsForOneTick = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * The maximum number of tick periods that can be suppressed is limited by the + * 24 bit resolution of the SysTick timer. + */ +#if ( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t xMaximumPossibleSuppressedTicks = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Compensate for the CPU cycles that pass while the SysTick is stopped (low + * power functionality only. + */ +#if ( configUSE_TICKLESS_IDLE == 1 ) + static uint32_t ulStoppedTimerCompensation = 0; +#endif /* configUSE_TICKLESS_IDLE */ + +/* + * Used by the portASSERT_IF_INTERRUPT_PRIORITY_INVALID() macro to ensure + * FreeRTOS API functions are not called from interrupts that have been assigned + * a priority above configMAX_SYSCALL_INTERRUPT_PRIORITY. + */ +#if ( configASSERT_DEFINED == 1 ) + static uint8_t ucMaxSysCallPriority = 0; + static uint32_t ulMaxPRIGROUPValue = 0; + static const volatile uint8_t * const pcInterruptPriorityRegisters = ( const volatile uint8_t * const ) portNVIC_IP_REGISTERS_OFFSET_16; +#endif /* configASSERT_DEFINED */ + +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack, + TaskFunction_t pxCode, + void * pvParameters ) +{ + /* Simulate the stack frame as it would be created by a context switch + * interrupt. */ + + /* Offset added to account for the way the MCU uses the stack on entry/exit + * of interrupts, and to ensure alignment. */ + pxTopOfStack--; + + *pxTopOfStack = portINITIAL_XPSR; /* xPSR */ + pxTopOfStack--; + *pxTopOfStack = ( ( StackType_t ) pxCode ) & portSTART_ADDRESS_MASK; /* PC */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) portTASK_RETURN_ADDRESS; /* LR */ + + /* Save code space by skipping register initialisation. */ + pxTopOfStack -= 5; /* R12, R3, R2 and R1. */ + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + + /* A save method is being used that requires each task to maintain its + * own exec return value. */ + pxTopOfStack--; + *pxTopOfStack = portINITIAL_EXC_RETURN; + + pxTopOfStack -= 8; /* R11, R10, R9, R8, R7, R6, R5 and R4. */ + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +static void prvTaskExitError( void ) +{ + volatile uint32_t ulDummy = 0; + + /* A function that implements a task must not exit or attempt to return to + * its caller as there is nothing to return to. If a task wants to exit it + * should instead call vTaskDelete( NULL ). + * + * Artificially force an assert() to be triggered if configASSERT() is + * defined, then stop here so application writers can catch the error. */ + configASSERT( uxCriticalNesting == ~0UL ); + portDISABLE_INTERRUPTS(); + + while( ulDummy == 0 ) + { + /* This file calls prvTaskExitError() after the scheduler has been + * started to remove a compiler warning about the function being defined + * but never called. ulDummy is used purely to quieten other warnings + * about code appearing after this function is called - making ulDummy + * volatile makes the compiler think the function could return and + * therefore not output an 'unreachable code' warning for code that appears + * after it. */ + } +} +/*-----------------------------------------------------------*/ + +void vPortSVCHandler( void ) +{ + __asm volatile ( + " ldr r3, pxCurrentTCBConst2 \n" /* Restore the context. */ + " ldr r1, [r3] \n" /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */ + " ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldmia r0!, {r4-r11, r14} \n" /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */ + " msr psp, r0 \n" /* Restore the task stack pointer. */ + " isb \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " bx r14 \n" + " \n" + " .align 4 \n" + "pxCurrentTCBConst2: .word pxCurrentTCB \n" + ); +} +/*-----------------------------------------------------------*/ + +static void prvPortStartFirstTask( void ) +{ + /* Start the first task. This also clears the bit that indicates the FPU is + * in use in case the FPU was used before the scheduler was started - which + * would otherwise result in the unnecessary leaving of space in the SVC stack + * for lazy saving of FPU registers. */ + __asm volatile ( + " ldr r0, =0xE000ED08 \n" /* Use the NVIC offset register to locate the stack. */ + " ldr r0, [r0] \n" + " ldr r0, [r0] \n" + " msr msp, r0 \n" /* Set the msp back to the start of the stack. */ + " mov r0, #0 \n" /* Clear the bit that indicates the FPU is in use, see comment above. */ + " msr control, r0 \n" + " cpsie i \n" /* Globally enable interrupts. */ + " cpsie f \n" + " dsb \n" + " isb \n" + " svc 0 \n" /* System call to start first task. */ + " nop \n" + " .ltorg \n" + ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +BaseType_t xPortStartScheduler( void ) +{ + /* This port can be used on all revisions of the Cortex-M7 core other than + * the r0p1 parts. r0p1 parts should use the port from the + * /source/portable/GCC/ARM_CM7/r0p1 directory. */ + configASSERT( portCPUID != portCORTEX_M7_r0p1_ID ); + configASSERT( portCPUID != portCORTEX_M7_r0p0_ID ); + + #if ( configASSERT_DEFINED == 1 ) + { + volatile uint8_t ucOriginalPriority; + volatile uint32_t ulImplementedPrioBits = 0; + volatile uint8_t * const pucFirstUserPriorityRegister = ( volatile uint8_t * const ) ( portNVIC_IP_REGISTERS_OFFSET_16 + portFIRST_USER_INTERRUPT_NUMBER ); + volatile uint8_t ucMaxPriorityValue; + + /* Determine the maximum priority from which ISR safe FreeRTOS API + * functions can be called. ISR safe functions are those that end in + * "FromISR". FreeRTOS maintains separate thread and ISR API functions to + * ensure interrupt entry is as fast and simple as possible. + * + * Save the interrupt priority value that is about to be clobbered. */ + ucOriginalPriority = *pucFirstUserPriorityRegister; + + /* Determine the number of priority bits available. First write to all + * possible bits. */ + *pucFirstUserPriorityRegister = portMAX_8_BIT_VALUE; + + /* Read the value back to see how many bits stuck. */ + ucMaxPriorityValue = *pucFirstUserPriorityRegister; + + /* Use the same mask on the maximum system call priority. */ + ucMaxSysCallPriority = configMAX_SYSCALL_INTERRUPT_PRIORITY & ucMaxPriorityValue; + + /* Check that the maximum system call priority is nonzero after + * accounting for the number of priority bits supported by the + * hardware. A priority of 0 is invalid because setting the BASEPRI + * register to 0 unmasks all interrupts, and interrupts with priority 0 + * cannot be masked using BASEPRI. + * See https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html */ + configASSERT( ucMaxSysCallPriority ); + + /* Check that the bits not implemented in hardware are zero in + * configMAX_SYSCALL_INTERRUPT_PRIORITY. */ + configASSERT( ( configMAX_SYSCALL_INTERRUPT_PRIORITY & ( ~ucMaxPriorityValue ) ) == 0U ); + + /* Calculate the maximum acceptable priority group value for the number + * of bits read back. */ + + while( ( ucMaxPriorityValue & portTOP_BIT_OF_BYTE ) == portTOP_BIT_OF_BYTE ) + { + ulImplementedPrioBits++; + ucMaxPriorityValue <<= ( uint8_t ) 0x01; + } + + if( ulImplementedPrioBits == 8 ) + { + /* When the hardware implements 8 priority bits, there is no way for + * the software to configure PRIGROUP to not have sub-priorities. As + * a result, the least significant bit is always used for sub-priority + * and there are 128 preemption priorities and 2 sub-priorities. + * + * This may cause some confusion in some cases - for example, if + * configMAX_SYSCALL_INTERRUPT_PRIORITY is set to 5, both 5 and 4 + * priority interrupts will be masked in Critical Sections as those + * are at the same preemption priority. This may appear confusing as + * 4 is higher (numerically lower) priority than + * configMAX_SYSCALL_INTERRUPT_PRIORITY and therefore, should not + * have been masked. Instead, if we set configMAX_SYSCALL_INTERRUPT_PRIORITY + * to 4, this confusion does not happen and the behaviour remains the same. + * + * The following assert ensures that the sub-priority bit in the + * configMAX_SYSCALL_INTERRUPT_PRIORITY is clear to avoid the above mentioned + * confusion. */ + configASSERT( ( configMAX_SYSCALL_INTERRUPT_PRIORITY & 0x1U ) == 0U ); + ulMaxPRIGROUPValue = 0; + } + else + { + ulMaxPRIGROUPValue = portMAX_PRIGROUP_BITS - ulImplementedPrioBits; + } + + /* Shift the priority group value back to its position within the AIRCR + * register. */ + ulMaxPRIGROUPValue <<= portPRIGROUP_SHIFT; + ulMaxPRIGROUPValue &= portPRIORITY_GROUP_MASK; + + /* Restore the clobbered interrupt priority register to its original + * value. */ + *pucFirstUserPriorityRegister = ucOriginalPriority; + } + #endif /* configASSERT_DEFINED */ + + /* Make PendSV and SysTick the lowest priority interrupts. */ + portNVIC_SHPR3_REG |= portNVIC_PENDSV_PRI; + portNVIC_SHPR3_REG |= portNVIC_SYSTICK_PRI; + + /* Start the timer that generates the tick ISR. Interrupts are disabled + * here already. */ + vPortSetupTimerInterrupt(); + + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Ensure the VFP is enabled - it should be anyway. */ + vPortEnableVFP(); + + /* Lazy save always. */ + *( portFPCCR ) |= portASPEN_AND_LSPEN_BITS; + + /* Start the first task. */ + prvPortStartFirstTask(); + + /* Should never get here as the tasks will now be executing! Call the task + * exit error function to prevent compiler warnings about a static function + * not being called in the case that the application writer overrides this + * functionality by defining configTASK_RETURN_ADDRESS. Call + * vTaskSwitchContext() so link time optimisation does not remove the + * symbol. */ + vTaskSwitchContext(); + prvTaskExitError(); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + * Artificially force an assert. */ + configASSERT( uxCriticalNesting == 1000UL ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + uxCriticalNesting++; + + /* This is not the interrupt safe version of the enter critical function so + * assert() if it is being called from an interrupt context. Only API + * functions that end in "FromISR" can be used in an interrupt. Only assert if + * the critical nesting count is 1 to protect against recursive calls if the + * assert function also uses a critical section. */ + if( uxCriticalNesting == 1 ) + { + configASSERT( ( portNVIC_INT_CTRL_REG & portVECTACTIVE_MASK ) == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + configASSERT( uxCriticalNesting ); + uxCriticalNesting--; + + if( uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } +} +/*-----------------------------------------------------------*/ + +void xPortPendSVHandler( void ) +{ + /* This is a naked function. */ + + __asm volatile + ( + " mrs r0, psp \n" + " isb \n" + " \n" + " ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */ + " ldr r2, [r3] \n" + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, push high vfp registers. */ + " it eq \n" + " vstmdbeq r0!, {s16-s31} \n" + " \n" + " stmdb r0!, {r4-r11, r14} \n" /* Save the core registers. */ + " str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */ + " \n" + " stmdb sp!, {r0, r3} \n" + " mov r0, %0 \n" + " msr basepri, r0 \n" + " dsb \n" + " isb \n" + " bl vTaskSwitchContext \n" + " mov r0, #0 \n" + " msr basepri, r0 \n" + " ldmia sp!, {r0, r3} \n" + " \n" + " ldr r1, [r3] \n" /* The first item in pxCurrentTCB is the task top of stack. */ + " ldr r0, [r1] \n" + " \n" + " ldmia r0!, {r4-r11, r14} \n" /* Pop the core registers. */ + " \n" + " tst r14, #0x10 \n" /* Is the task using the FPU context? If so, pop the high vfp registers too. */ + " it eq \n" + " vldmiaeq r0!, {s16-s31} \n" + " \n" + " msr psp, r0 \n" + " isb \n" + " \n" + #ifdef WORKAROUND_PMU_CM001 /* XMC4000 specific errata workaround. */ + #if WORKAROUND_PMU_CM001 == 1 + " push { r14 } \n" + " pop { pc } \n" + #endif + #endif + " \n" + " bx r14 \n" + " \n" + " .align 4 \n" + "pxCurrentTCBConst: .word pxCurrentTCB \n" + ::"i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) + ); +} +/*-----------------------------------------------------------*/ + +void xPortSysTickHandler( void ) +{ + /* The SysTick runs at the lowest interrupt priority, so when this interrupt + * executes all interrupts must be unmasked. There is therefore no need to + * save and then restore the interrupt mask value as its value is already + * known. */ + portDISABLE_INTERRUPTS(); + { + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + /* A context switch is required. Context switching is performed in + * the PendSV interrupt. Pend the PendSV interrupt. */ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; + } + } + portENABLE_INTERRUPTS(); +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TICKLESS_IDLE == 1 ) + + __attribute__( ( weak ) ) void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ) + { + uint32_t ulReloadValue, ulCompleteTickPeriods, ulCompletedSysTickDecrements, ulSysTickDecrementsLeft; + TickType_t xModifiableIdleTime; + + /* Make sure the SysTick reload value does not overflow the counter. */ + if( xExpectedIdleTime > xMaximumPossibleSuppressedTicks ) + { + xExpectedIdleTime = xMaximumPossibleSuppressedTicks; + } + + /* Enter a critical section but don't use the taskENTER_CRITICAL() + * method as that will mask interrupts that should exit sleep mode. */ + __asm volatile ( "cpsid i" ::: "memory" ); + __asm volatile ( "dsb" ); + __asm volatile ( "isb" ); + + /* If a context switch is pending or a task is waiting for the scheduler + * to be unsuspended then abandon the low power entry. */ + if( eTaskConfirmSleepModeStatus() == eAbortSleep ) + { + /* Re-enable interrupts - see comments above the cpsid instruction + * above. */ + __asm volatile ( "cpsie i" ::: "memory" ); + } + else + { + /* Stop the SysTick momentarily. The time the SysTick is stopped for + * is accounted for as best it can be, but using the tickless mode will + * inevitably result in some tiny drift of the time maintained by the + * kernel with respect to calendar time. */ + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Use the SysTick current-value register to determine the number of + * SysTick decrements remaining until the next tick interrupt. If the + * current-value register is zero, then there are actually + * ulTimerCountsForOneTick decrements remaining, not zero, because the + * SysTick requests the interrupt when decrementing from 1 to 0. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulTimerCountsForOneTick; + } + + /* Calculate the reload value required to wait xExpectedIdleTime + * tick periods. -1 is used because this code normally executes part + * way through the first tick period. But if the SysTick IRQ is now + * pending, then clear the IRQ, suppressing the first tick, and correct + * the reload value to reflect that the second tick period is already + * underway. The expected idle time is always at least two ticks. */ + ulReloadValue = ulSysTickDecrementsLeft + ( ulTimerCountsForOneTick * ( xExpectedIdleTime - 1UL ) ); + + if( ( portNVIC_INT_CTRL_REG & portNVIC_PEND_SYSTICK_SET_BIT ) != 0 ) + { + portNVIC_INT_CTRL_REG = portNVIC_PEND_SYSTICK_CLEAR_BIT; + ulReloadValue -= ulTimerCountsForOneTick; + } + + if( ulReloadValue > ulStoppedTimerCompensation ) + { + ulReloadValue -= ulStoppedTimerCompensation; + } + + /* Set the new reload value. */ + portNVIC_SYSTICK_LOAD_REG = ulReloadValue; + + /* Clear the SysTick count flag and set the count value back to + * zero. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Restart SysTick. */ + portNVIC_SYSTICK_CTRL_REG |= portNVIC_SYSTICK_ENABLE_BIT; + + /* Sleep until something happens. configPRE_SLEEP_PROCESSING() can + * set its parameter to 0 to indicate that its implementation contains + * its own wait for interrupt or wait for event instruction, and so wfi + * should not be executed again. However, the original expected idle + * time variable must remain unmodified, so a copy is taken. */ + xModifiableIdleTime = xExpectedIdleTime; + configPRE_SLEEP_PROCESSING( xModifiableIdleTime ); + + if( xModifiableIdleTime > 0 ) + { + __asm volatile ( "dsb" ::: "memory" ); + __asm volatile ( "wfi" ); + __asm volatile ( "isb" ); + } + + configPOST_SLEEP_PROCESSING( xExpectedIdleTime ); + + /* Re-enable interrupts to allow the interrupt that brought the MCU + * out of sleep mode to execute immediately. See comments above + * the cpsid instruction above. */ + __asm volatile ( "cpsie i" ::: "memory" ); + __asm volatile ( "dsb" ); + __asm volatile ( "isb" ); + + /* Disable interrupts again because the clock is about to be stopped + * and interrupts that execute while the clock is stopped will increase + * any slippage between the time maintained by the RTOS and calendar + * time. */ + __asm volatile ( "cpsid i" ::: "memory" ); + __asm volatile ( "dsb" ); + __asm volatile ( "isb" ); + + /* Disable the SysTick clock without reading the + * portNVIC_SYSTICK_CTRL_REG register to ensure the + * portNVIC_SYSTICK_COUNT_FLAG_BIT is not cleared if it is set. Again, + * the time the SysTick is stopped for is accounted for as best it can + * be, but using the tickless mode will inevitably result in some tiny + * drift of the time maintained by the kernel with respect to calendar + * time*/ + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT ); + + /* Determine whether the SysTick has already counted to zero. */ + if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + uint32_t ulCalculatedLoadValue; + + /* The tick interrupt ended the sleep (or is now pending), and + * a new tick period has started. Reset portNVIC_SYSTICK_LOAD_REG + * with whatever remains of the new tick period. */ + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ) - ( ulReloadValue - portNVIC_SYSTICK_CURRENT_VALUE_REG ); + + /* Don't allow a tiny value, or values that have somehow + * underflowed because the post sleep hook did something + * that took too long or because the SysTick current-value register + * is zero. */ + if( ( ulCalculatedLoadValue <= ulStoppedTimerCompensation ) || ( ulCalculatedLoadValue > ulTimerCountsForOneTick ) ) + { + ulCalculatedLoadValue = ( ulTimerCountsForOneTick - 1UL ); + } + + portNVIC_SYSTICK_LOAD_REG = ulCalculatedLoadValue; + + /* As the pending tick will be processed as soon as this + * function exits, the tick value maintained by the tick is stepped + * forward by one less than the time spent waiting. */ + ulCompleteTickPeriods = xExpectedIdleTime - 1UL; + } + else + { + /* Something other than the tick interrupt ended the sleep. */ + + /* Use the SysTick current-value register to determine the + * number of SysTick decrements remaining until the expected idle + * time would have ended. */ + ulSysTickDecrementsLeft = portNVIC_SYSTICK_CURRENT_VALUE_REG; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG != portNVIC_SYSTICK_CLK_BIT ) + { + /* If the SysTick is not using the core clock, the current- + * value register might still be zero here. In that case, the + * SysTick didn't load from the reload register, and there are + * ulReloadValue decrements remaining in the expected idle + * time, not zero. */ + if( ulSysTickDecrementsLeft == 0 ) + { + ulSysTickDecrementsLeft = ulReloadValue; + } + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Work out how long the sleep lasted rounded to complete tick + * periods (not the ulReload value which accounted for part + * ticks). */ + ulCompletedSysTickDecrements = ( xExpectedIdleTime * ulTimerCountsForOneTick ) - ulSysTickDecrementsLeft; + + /* How many complete tick periods passed while the processor + * was waiting? */ + ulCompleteTickPeriods = ulCompletedSysTickDecrements / ulTimerCountsForOneTick; + + /* The reload value is set to whatever fraction of a single tick + * period remains. */ + portNVIC_SYSTICK_LOAD_REG = ( ( ulCompleteTickPeriods + 1UL ) * ulTimerCountsForOneTick ) - ulCompletedSysTickDecrements; + } + + /* Restart SysTick so it runs from portNVIC_SYSTICK_LOAD_REG again, + * then set portNVIC_SYSTICK_LOAD_REG back to its standard value. If + * the SysTick is not using the core clock, temporarily configure it to + * use the core clock. This configuration forces the SysTick to load + * from portNVIC_SYSTICK_LOAD_REG immediately instead of at the next + * cycle of the other clock. Then portNVIC_SYSTICK_LOAD_REG is ready + * to receive the standard value immediately. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + #if ( portNVIC_SYSTICK_CLK_BIT_CONFIG == portNVIC_SYSTICK_CLK_BIT ) + { + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + } + #else + { + /* The temporary usage of the core clock has served its purpose, + * as described above. Resume usage of the other clock. */ + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT | portNVIC_SYSTICK_INT_BIT; + + if( ( portNVIC_SYSTICK_CTRL_REG & portNVIC_SYSTICK_COUNT_FLAG_BIT ) != 0 ) + { + /* The partial tick period already ended. Be sure the SysTick + * counts it only once. */ + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0; + } + + portNVIC_SYSTICK_LOAD_REG = ulTimerCountsForOneTick - 1UL; + portNVIC_SYSTICK_CTRL_REG = portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT; + } + #endif /* portNVIC_SYSTICK_CLK_BIT_CONFIG */ + + /* Step the tick to account for any tick periods that elapsed. */ + vTaskStepTick( ulCompleteTickPeriods ); + + /* Exit with interrupts enabled. */ + __asm volatile ( "cpsie i" ::: "memory" ); + } + } + +#endif /* #if configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +__attribute__( ( weak ) ) void vPortSetupTimerInterrupt( void ) +{ + /* Calculate the constants required to configure the tick interrupt. */ + #if ( configUSE_TICKLESS_IDLE == 1 ) + { + ulTimerCountsForOneTick = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ); + xMaximumPossibleSuppressedTicks = portMAX_24_BIT_NUMBER / ulTimerCountsForOneTick; + ulStoppedTimerCompensation = portMISSED_COUNTS_FACTOR / ( configCPU_CLOCK_HZ / configSYSTICK_CLOCK_HZ ); + } + #endif /* configUSE_TICKLESS_IDLE */ + + /* Stop and clear the SysTick. */ + portNVIC_SYSTICK_CTRL_REG = 0UL; + portNVIC_SYSTICK_CURRENT_VALUE_REG = 0UL; + + /* Configure SysTick to interrupt at the requested rate. */ + portNVIC_SYSTICK_LOAD_REG = ( configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ ) - 1UL; + portNVIC_SYSTICK_CTRL_REG = ( portNVIC_SYSTICK_CLK_BIT_CONFIG | portNVIC_SYSTICK_INT_BIT | portNVIC_SYSTICK_ENABLE_BIT ); +} +/*-----------------------------------------------------------*/ + +/* This is a naked function. */ +static void vPortEnableVFP( void ) +{ + __asm volatile + ( + " ldr.w r0, =0xE000ED88 \n" /* The FPU enable bits are in the CPACR. */ + " ldr r1, [r0] \n" + " \n" + " orr r1, r1, #( 0xf << 20 ) \n" /* Enable CP10 and CP11 coprocessors, then save back. */ + " str r1, [r0] \n" + " bx r14 \n" + " .ltorg \n" + ); +} +/*-----------------------------------------------------------*/ + +#if ( configASSERT_DEFINED == 1 ) + + void vPortValidateInterruptPriority( void ) + { + uint32_t ulCurrentInterrupt; + uint8_t ucCurrentPriority; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile ( "mrs %0, ipsr" : "=r" ( ulCurrentInterrupt )::"memory" ); + + /* Is the interrupt number a user defined interrupt? */ + if( ulCurrentInterrupt >= portFIRST_USER_INTERRUPT_NUMBER ) + { + /* Look up the interrupt's priority. */ + ucCurrentPriority = pcInterruptPriorityRegisters[ ulCurrentInterrupt ]; + + /* The following assertion will fail if a service routine (ISR) for + * an interrupt that has been assigned a priority above + * configMAX_SYSCALL_INTERRUPT_PRIORITY calls an ISR safe FreeRTOS API + * function. ISR safe FreeRTOS API functions must *only* be called + * from interrupts that have been assigned a priority at or below + * configMAX_SYSCALL_INTERRUPT_PRIORITY. + * + * Numerically low interrupt priority numbers represent logically high + * interrupt priorities, therefore the priority of the interrupt must + * be set to a value equal to or numerically *higher* than + * configMAX_SYSCALL_INTERRUPT_PRIORITY. + * + * Interrupts that use the FreeRTOS API must not be left at their + * default priority of zero as that is the highest possible priority, + * which is guaranteed to be above configMAX_SYSCALL_INTERRUPT_PRIORITY, + * and therefore also guaranteed to be invalid. + * + * FreeRTOS maintains separate thread and ISR API functions to ensure + * interrupt entry is as fast and simple as possible. + * + * The following links provide detailed information: + * https://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html + * https://www.FreeRTOS.org/FAQHelp.html */ + configASSERT( ucCurrentPriority >= ucMaxSysCallPriority ); + } + + /* Priority grouping: The interrupt controller (NVIC) allows the bits + * that define each interrupt's priority to be split between bits that + * define the interrupt's pre-emption priority bits and bits that define + * the interrupt's sub-priority. For simplicity all bits must be defined + * to be pre-emption priority bits. The following assertion will fail if + * this is not the case (if some bits represent a sub-priority). + * + * If the application only uses CMSIS libraries for interrupt + * configuration then the correct setting can be achieved on all Cortex-M + * devices by calling NVIC_SetPriorityGrouping( 0 ); before starting the + * scheduler. Note however that some vendor specific peripheral libraries + * assume a non-zero priority group setting, in which cases using a value + * of zero will result in unpredictable behaviour. */ + configASSERT( ( portAIRCR_REG & portPRIORITY_GROUP_MASK ) <= ulMaxPRIGROUPValue ); + } + +#endif /* configASSERT_DEFINED */ diff --git a/platforms/stm32/3rdparty/freertos_cm4_sysTick/portmacro.h b/platforms/stm32/3rdparty/freertos_cm4_sysTick/portmacro.h new file mode 100644 index 00000000000..ec9cfc99fcb --- /dev/null +++ b/platforms/stm32/3rdparty/freertos_cm4_sysTick/portmacro.h @@ -0,0 +1,254 @@ +/* + * FreeRTOS Kernel V10.6.2 + * Copyright (C) 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * SPDX-License-Identifier: MIT + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * https://www.FreeRTOS.org + * https://github.com/FreeRTOS + * + */ + + +#ifndef PORTMACRO_H + #define PORTMACRO_H + +/* *INDENT-OFF* */ +#ifdef __cplusplus + extern "C" { +#endif +/* *INDENT-ON* */ + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ + #define portCHAR char + #define portFLOAT float + #define portDOUBLE double + #define portLONG long + #define portSHORT short + #define portSTACK_TYPE uint32_t + #define portBASE_TYPE long + + typedef portSTACK_TYPE StackType_t; + typedef long BaseType_t; + typedef unsigned long UBaseType_t; + + #if ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_16_BITS ) + typedef uint16_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffff + #elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_32_BITS ) + typedef uint32_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffUL + +/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do + * not need to be guarded with a critical section. */ + #define portTICK_TYPE_IS_ATOMIC 1 + #elif ( configTICK_TYPE_WIDTH_IN_BITS == TICK_TYPE_WIDTH_64_BITS ) + typedef uint64_t TickType_t; + #define portMAX_DELAY ( TickType_t ) 0xffffffffffffffffULL + #else + #error configTICK_TYPE_WIDTH_IN_BITS set to unsupported tick type width. + #endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ + #define portSTACK_GROWTH ( -1 ) + #define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) + #define portBYTE_ALIGNMENT 8 + #define portDONT_DISCARD __attribute__( ( used ) ) +/*-----------------------------------------------------------*/ + +/* Scheduler utilities. */ + #define portYIELD() \ + { \ + /* Set a PendSV to request a context switch. */ \ + portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \ + \ + /* Barriers are normally not required but do ensure the code is completely \ + * within the specified behaviour for the architecture. */ \ + __asm volatile ( "dsb" ::: "memory" ); \ + __asm volatile ( "isb" ); \ + } + + #define portNVIC_INT_CTRL_REG ( *( ( volatile uint32_t * ) 0xe000ed04 ) ) + #define portNVIC_PENDSVSET_BIT ( 1UL << 28UL ) + #define portEND_SWITCHING_ISR( xSwitchRequired ) do { if( xSwitchRequired != pdFALSE ) portYIELD(); } while( 0 ) + #define portYIELD_FROM_ISR( x ) portEND_SWITCHING_ISR( x ) +/*-----------------------------------------------------------*/ + +/* Critical section management. */ + extern void vPortEnterCritical( void ); + extern void vPortExitCritical( void ); + #define portSET_INTERRUPT_MASK_FROM_ISR() ulPortRaiseBASEPRI() + #define portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) vPortSetBASEPRI( x ) + #define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI() + #define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 ) + #define portENTER_CRITICAL() vPortEnterCritical() + #define portEXIT_CRITICAL() vPortExitCritical() + +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are + * not necessary for to use this port. They are defined so the common demo files + * (which build with all the ports) will build. */ + #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void * pvParameters ) + #define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void * pvParameters ) +/*-----------------------------------------------------------*/ + +/* Tickless idle/low power functionality. */ + #ifndef portSUPPRESS_TICKS_AND_SLEEP + extern void vPortSuppressTicksAndSleep( TickType_t xExpectedIdleTime ); + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) vPortSuppressTicksAndSleep( xExpectedIdleTime ) + #endif +/*-----------------------------------------------------------*/ + +/* Architecture specific optimisations. */ + #ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 + #endif + + #if configUSE_PORT_OPTIMISED_TASK_SELECTION == 1 + +/* Generic helper function. */ + __attribute__( ( always_inline ) ) static inline uint8_t ucPortCountLeadingZeros( uint32_t ulBitmap ) + { + uint8_t ucReturn; + + __asm volatile ( "clz %0, %1" : "=r" ( ucReturn ) : "r" ( ulBitmap ) : "memory" ); + + return ucReturn; + } + +/* Check the configuration. */ + #if ( configMAX_PRIORITIES > 32 ) + #error configUSE_PORT_OPTIMISED_TASK_SELECTION can only be set to 1 when configMAX_PRIORITIES is less than or equal to 32. It is very rare that a system requires more than 10 to 15 difference priorities as tasks that share a priority will time slice. + #endif + +/* Store/clear the ready priorities in a bit map. */ + #define portRECORD_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) |= ( 1UL << ( uxPriority ) ) + #define portRESET_READY_PRIORITY( uxPriority, uxReadyPriorities ) ( uxReadyPriorities ) &= ~( 1UL << ( uxPriority ) ) + +/*-----------------------------------------------------------*/ + + #define portGET_HIGHEST_PRIORITY( uxTopPriority, uxReadyPriorities ) uxTopPriority = ( 31UL - ( uint32_t ) ucPortCountLeadingZeros( ( uxReadyPriorities ) ) ) + + #endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + + #ifdef configASSERT + void vPortValidateInterruptPriority( void ); + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() vPortValidateInterruptPriority() + #endif + +/* portNOP() is not required by this port. */ + #define portNOP() + + #define portINLINE __inline + + #ifndef portFORCE_INLINE + #define portFORCE_INLINE inline __attribute__( ( always_inline ) ) + #endif + + portFORCE_INLINE static BaseType_t xPortIsInsideInterrupt( void ) + { + uint32_t ulCurrentInterrupt; + BaseType_t xReturn; + + /* Obtain the number of the currently executing interrupt. */ + __asm volatile ( "mrs %0, ipsr" : "=r" ( ulCurrentInterrupt )::"memory" ); + + if( ulCurrentInterrupt == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + + return xReturn; + } + +/*-----------------------------------------------------------*/ + + portFORCE_INLINE static void vPortRaiseBASEPRI( void ) + { + uint32_t ulNewBASEPRI; + + __asm volatile + ( + " mov %0, %1 \n"\ + " msr basepri, %0 \n"\ + " isb \n"\ + " dsb \n"\ + : "=r" ( ulNewBASEPRI ) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" + ); + } + +/*-----------------------------------------------------------*/ + + portFORCE_INLINE static uint32_t ulPortRaiseBASEPRI( void ) + { + uint32_t ulOriginalBASEPRI, ulNewBASEPRI; + + __asm volatile + ( + " mrs %0, basepri \n"\ + " mov %1, %2 \n"\ + " msr basepri, %1 \n"\ + " isb \n"\ + " dsb \n"\ + : "=r" ( ulOriginalBASEPRI ), "=r" ( ulNewBASEPRI ) : "i" ( configMAX_SYSCALL_INTERRUPT_PRIORITY ) : "memory" + ); + + /* This return will not be reached but is necessary to prevent compiler + * warnings. */ + return ulOriginalBASEPRI; + } +/*-----------------------------------------------------------*/ + + portFORCE_INLINE static void vPortSetBASEPRI( uint32_t ulNewMaskValue ) + { + __asm volatile + ( + " msr basepri, %0 "::"r" ( ulNewMaskValue ) : "memory" + ); + } +/*-----------------------------------------------------------*/ + + #define portMEMORY_BARRIER() __asm volatile ( "" ::: "memory" ) + +/* *INDENT-OFF* */ +#ifdef __cplusplus + } +#endif +/* *INDENT-ON* */ + +#endif /* PORTMACRO_H */ diff --git a/platforms/stm32/CMakeLists.txt b/platforms/stm32/CMakeLists.txt index ce465ec49c2..b3bc142a269 100644 --- a/platforms/stm32/CMakeLists.txt +++ b/platforms/stm32/CMakeLists.txt @@ -16,6 +16,11 @@ if (NOT BUILD_EXECUTABLE STREQUAL "unitTest") ) endif () + # 3rdparty modules - RTOS + if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_subdirectory(3rdparty/freertos_cm4_sysTick) + endif () + add_subdirectory(bsp) # ETL implementation diff --git a/platforms/stm32/bsp/bspUart/CMakeLists.txt b/platforms/stm32/bsp/bspUart/CMakeLists.txt index 2498a80301b..104912c30a4 100644 --- a/platforms/stm32/bsp/bspUart/CMakeLists.txt +++ b/platforms/stm32/bsp/bspUart/CMakeLists.txt @@ -9,5 +9,5 @@ else () target_link_libraries( bspUart PUBLIC bsp - PRIVATE bspMcu) + PRIVATE bspConfiguration bspMcu) endif () diff --git a/platforms/stm32/bsp/bspUart/include/bsp/Uart.h b/platforms/stm32/bsp/bspUart/include/bsp/Uart.h index 6923323216a..c780bde3374 100644 --- a/platforms/stm32/bsp/bspUart/include/bsp/Uart.h +++ b/platforms/stm32/bsp/bspUart/include/bsp/Uart.h @@ -41,3 +41,5 @@ class Uart BSP_UART_CONCEPT_CHECKER(Uart) } // namespace bsp + +#include diff --git a/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h b/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h index a0c481a6eec..1edbe5e39c1 100644 --- a/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h +++ b/platforms/stm32/bsp/fdCanTransceiver/include/can/transceiver/fdcan/FdCanTransceiver.h @@ -61,6 +61,7 @@ class FdCanTransceiver : public ::can::AbstractCANTransceiver static void transmitInterrupt(uint8_t transceiverIndex); static void disableRxInterrupt(uint8_t transceiverIndex); static void enableRxInterrupt(uint8_t transceiverIndex); + static void pollTxCallback(uint8_t transceiverIndex); void cyclicTask(); void receiveTask(); diff --git a/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp b/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp index e97013703df..4834077949c 100644 --- a/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp +++ b/platforms/stm32/bsp/fdCanTransceiver/src/can/transceiver/fdcan/FdCanTransceiver.cpp @@ -284,6 +284,18 @@ void FdCanTransceiver::cyclicTask() } } +void FdCanTransceiver::pollTxCallback(uint8_t transceiverIndex) +{ + if (transceiverIndex < 3U && fpTransceivers[transceiverIndex] != nullptr) + { + FdCanTransceiver* self = fpTransceivers[transceiverIndex]; + if (!self->fTxQueue.empty()) + { + self->canFrameSentAsyncCallback(); + } + } +} + void FdCanTransceiver::receiveTask() { uint8_t count = fDevice.getRxCount(); From 3c42433996daff094646333805e30229dbf6507e Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Tue, 2 Jun 2026 08:43:24 +0200 Subject: [PATCH 08/11] Add STM32 ThreadX Cortex-M4 support --- .gitattributes | 1 + CMakePresets.json | 26 + NOTICE.md | 1 + .../platforms/nucleo_g474re/CMakeLists.txt | 4 + .../nucleo_g474re/main/CMakeLists.txt | 8 + .../src/bsp/threadx/tx_execution_initialize.c | 17 + .../src/bsp/threadx/tx_initialize_low_level.S | 96 +++ .../main/src/osHooks/threadx/osHooks.cpp | 66 ++ .../threadXCoreConfiguration/CMakeLists.txt | 3 + .../include/tx_user.h | 50 ++ .../threadXCoreConfiguration/module.spec | 1 + .../stm32/3rdparty/threadx/CMakeLists.txt | 19 + platforms/stm32/3rdparty/threadx/LICENSE.md | 21 + .../threadx/ports/cortex_m4/gnu/.riminfo | 15 + .../ports/cortex_m4/gnu/CMakeLists.txt | 18 + .../gnu/example_build/build_threadx.bat | 228 ++++++ .../example_build/build_threadx_sample.bat | 5 + .../gnu/example_build/cortexm4_crt0.S | 119 +++ .../gnu/example_build/cortexm4_vectors.S | 77 ++ .../gnu/example_build/sample_threadx.c | 370 +++++++++ .../gnu/example_build/sample_threadx.ld | 125 +++ .../example_build/tx_initialize_low_level.S | 232 ++++++ .../threadx/ports/cortex_m4/gnu/inc/tx_port.h | 728 ++++++++++++++++++ .../ports/cortex_m4/gnu/readme_threadx.txt | 209 +++++ .../ports/cortex_m4/gnu/src/tx_misra.S | 722 +++++++++++++++++ .../gnu/src/tx_thread_context_restore.S | 87 +++ .../gnu/src/tx_thread_context_save.S | 85 ++ .../gnu/src/tx_thread_interrupt_control.S | 84 ++ .../gnu/src/tx_thread_interrupt_disable.S | 84 ++ .../gnu/src/tx_thread_interrupt_restore.S | 81 ++ .../cortex_m4/gnu/src/tx_thread_schedule.S | 330 ++++++++ .../cortex_m4/gnu/src/tx_thread_stack_build.S | 138 ++++ .../gnu/src/tx_thread_system_return.S | 98 +++ .../cortex_m4/gnu/src/tx_timer_interrupt.S | 260 +++++++ platforms/stm32/CMakeLists.txt | 2 + 35 files changed, 4410 insertions(+) create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_execution_initialize.c create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_initialize_low_level.S create mode 100644 executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/threadx/osHooks.cpp create mode 100644 executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/include/tx_user.h create mode 100644 executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/module.spec create mode 100644 platforms/stm32/3rdparty/threadx/CMakeLists.txt create mode 100644 platforms/stm32/3rdparty/threadx/LICENSE.md create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/.riminfo create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/CMakeLists.txt create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx.bat create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx_sample.bat create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_crt0.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_vectors.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.c create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.ld create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/tx_initialize_low_level.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/inc/tx_port.h create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/readme_threadx.txt create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_misra.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_restore.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_save.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_control.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_disable.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_restore.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_schedule.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_stack_build.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_system_return.S create mode 100644 platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_timer_interrupt.S diff --git a/.gitattributes b/.gitattributes index 43b4612516d..1847af861ed 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.riminfo -whitespace +platforms/*/3rdparty/threadx/ports/** -whitespace diff --git a/CMakePresets.json b/CMakePresets.json index 782ca1083ce..a3a1fcf6cb2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -107,6 +107,26 @@ "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" } }, + { + "name": "nucleo-g474re-threadx-gcc", + "generator": "Ninja Multi-Config", + "displayName": "NUCLEO-G474RE ThreadX configuration (GCC)", + "description": "Configure for NUCLEO-G474RE platform using ARM GCC toolchain", + "inherits": "_config-base", + "toolchainFile": "${sourceDir}/cmake/toolchains/ArmNoneEabi-gcc.cmake", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "BUILD_EXECUTABLE": "referenceApp", + "BUILD_TARGET_PLATFORM": "NUCLEO_G474RE", + "BUILD_TARGET_RTOS": "THREADX", + "STM32_CHIP": "STM32G474RE", + "CMAKE_ASM_FLAGS_RELWITHDEBINFO": "-g3", + "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo;Debug;Release", + "CMAKE_C_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" + } + }, { "name": "posix-freertos", "displayName": "POSIX-FREERTOS compliant configuration", @@ -294,6 +314,12 @@ "description": "Build reference application for NUCLEO-G474RE platform with GCC", "configurePreset": "nucleo-g474re-freertos-gcc" }, + { + "name": "nucleo-g474re-threadx-gcc", + "displayName": "NUCLEO-G474RE ThreadX build (GCC)", + "description": "Build reference application for NUCLEO-G474RE platform with GCC", + "configurePreset": "nucleo-g474re-threadx-gcc" + }, { "name": "posix-freertos", "displayName": "build POSIX-FREERTOS", diff --git a/NOTICE.md b/NOTICE.md index 321ae205ec1..f7a53d1b849 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -45,6 +45,7 @@ We recommend to read their licenses, as their terms may differ from the terms de | FreeRTOS Real Time Kernel | 10.6.2 | MIT | ``platforms/stm32/3rdparty/freertos_cm4_sysTick/LICENSE.md`` | | ThreadX Kernel | 6.4.3 | MIT | ``libs/3rdparty/threadx/LICENSE.md`` | | ThreadX Cortex M4 Port | 6.4.3 | MIT | ``platforms/s32k1xx/3rdparty/threadx/LICENSE.md`` | +| ThreadX Cortex M4 Port | 6.4.3 | MIT | ``platforms/stm32/3rdparty/threadx/LICENSE.md`` | | ThreadX Linux Port | 6.4.3 | MIT | ``platforms/posix/3rdparty/threadx/LICENSE.md`` | | CMSIS | 6.1.0 | Apache v2 | ``libs/3rdparty/cmsis/LICENSE`` | | NXP S32K148 Headers | 1.1a | BSD-3 | ``platforms/s32k1xx/bsp/bspMcu/include/3rdparty/nxp/*.h`` | diff --git a/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt index 1d7adfc3e7f..04843bb3b84 100644 --- a/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt +++ b/executables/referenceApp/platforms/nucleo_g474re/CMakeLists.txt @@ -8,4 +8,8 @@ if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") add_subdirectory(freeRtosCoreConfiguration) add_library(freeRtosPort ALIAS freeRtosCm4SysTickPort) add_library(freeRtosPortImpl ALIAS freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_subdirectory(threadXCoreConfiguration) + add_library(threadXPort ALIAS threadXCortexM4Port) + add_library(threadXPortImpl ALIAS threadXCortexM4) endif () diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt index 8d236b3ca1e..c6350831ed0 100644 --- a/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt +++ b/executables/referenceApp/platforms/nucleo_g474re/main/CMakeLists.txt @@ -30,6 +30,10 @@ target_link_libraries( if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") target_link_libraries(main PRIVATE freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + target_sources(startUp INTERFACE src/bsp/threadx/tx_initialize_low_level.S + src/bsp/threadx/tx_execution_initialize.c) + target_link_libraries(main PRIVATE threadXCortexM4) endif () # --whole-archive forces the linker to include ALL object files from the main @@ -59,5 +63,9 @@ set_target_properties( if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") add_library(osHooks src/osHooks/freertos/osHooks.cpp) target_link_libraries(osHooks PRIVATE common freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_library(osHooks src/osHooks/threadx/osHooks.cpp) + target_link_libraries(osHooks PRIVATE common threadXCortexM4 asyncThreadX + threadXConfiguration) endif () target_include_directories(osHooks PRIVATE include) diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_execution_initialize.c b/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_execution_initialize.c new file mode 100644 index 00000000000..612501e3939 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_execution_initialize.c @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/* + * Custom profiling initialization function + * + * Do not remove! This is called by ThreadX core just before its scheduler runs + * if TX_ENABLE_EXECUTION_CHANGE_NOTIFY is defined. + */ +void _tx_execution_initialize() { return; } diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_initialize_low_level.S b/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_initialize_low_level.S new file mode 100644 index 00000000000..92d10f6aaa4 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/bsp/threadx/tx_initialize_low_level.S @@ -0,0 +1,96 @@ +@/*************************************************************************** +@ * Copyright (c) 2024 Microsoft Corporation +@ * +@ * This program and the accompanying materials are made available under the +@ * terms of the MIT License which is available at +@ * https://opensource.org/licenses/MIT. +@ * +@ * SPDX-License-Identifier: MIT +@ **************************************************************************/ +@ +@ ThreadX low-level initialization for NUCLEO-G474RE (Cortex-M4F, 170 MHz) +@ + +#include "async/Config.h" + + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global _tx_timer_interrupt + .global __main + .global g_pfnVectors + .global __tx_SysTickHandler + .extern setupApplicationsIsr + +SYSTEM_CLOCK = 170000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / ASYNC_CONFIG_TICK_IN_US) -1) + + .text 32 + .align 4 + .syntax unified + +@ VOID _tx_initialize_low_level(VOID) + .global _tx_initialize_low_level + .thumb_func +_tx_initialize_low_level: + + CPSID i + + @ Setup Vector Table Offset Register + MOV r0, #0xE000E000 + LDR r1, =g_pfnVectors + STR r1, [r0, #0xD08] + + @ Set system stack pointer from vector value + LDR r0, =_tx_thread_system_stack_ptr + LDR r1, =g_pfnVectors + LDR r1, [r1] + STR r1, [r0] + + @ Enable the cycle count register (DWT) + LDR r0, =0xE0001000 + LDR r1, [r0] + ORR r1, r1, #1 + STR r1, [r0] + + @ Configure SysTick + MOV r0, #0xE000E000 + LDR r1, =SYSTICK_CYCLES + STR r1, [r0, #0x14] @ SysTick Reload Value + MOV r1, #0x7 @ Enable, interrupt, processor clock + STR r1, [r0, #0x10] @ SysTick Control + + @ Configure handler priorities + LDR r1, =0x00000000 @ UsgF, BusF, MemM = 0 + STR r1, [r0, #0xD18] + + LDR r1, =0xFF000000 @ SVCall = 0xFF (lowest) + STR r1, [r0, #0xD1C] + + LDR r1, =0x40FF0000 @ SysTick = 0x40, PendSV = 0xFF + STR r1, [r0, #0xD20] + + @ Configure platform-specific ISRs + PUSH {lr} + BL setupApplicationsIsr + POP {lr} + + BX lr + +@ SysTick handler - redirects to ThreadX timer interrupt + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: + PUSH {r0, lr} + BL _tx_timer_interrupt + POP {r0, lr} + BX LR + + .section .text.SVC_Handler + .global SVC_Handler + .thumb_func +SVC_Handler: + ldr r0, =Default_Handler + bx r0 diff --git a/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/threadx/osHooks.cpp b/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/threadx/osHooks.cpp new file mode 100644 index 00000000000..252a436348c --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/main/src/osHooks/threadx/osHooks.cpp @@ -0,0 +1,66 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "async/Config.h" +#include "async/Hook.h" +#include "mcu/mcu.h" +#include "tx_api.h" + +#include + +extern "C" +{ +extern TX_THREAD _tx_timer_thread; + +// Called from tx_initialize_low_level.S. NVIC priorities for peripheral +// interrupts are configured by the owning systems during board bring-up. +void setupApplicationsIsr() {} + +void _tx_execution_thread_enter() +{ + TX_THREAD* thread = tx_thread_identify(); + if (thread->tx_thread_priority == _tx_timer_thread.tx_thread_priority) + { + asyncEnterTask(static_cast(ASYNC_CONFIG_TASK_COUNT)); + } + else + { + asyncEnterTask(static_cast(thread->tx_thread_entry_parameter)); + } +} + +void _tx_execution_thread_exit() +{ + TX_THREAD* thread = tx_thread_identify(); + if (thread->tx_thread_priority == _tx_timer_thread.tx_thread_priority) + { + asyncLeaveTask(static_cast(ASYNC_CONFIG_TASK_COUNT)); + } + else + { + asyncLeaveTask(static_cast(thread->tx_thread_entry_parameter)); + } +} + +void tx_low_power_enter() +{ + // Feed IWDG before entering low-power mode to prevent watchdog reset. + IWDG->KR = 0xAAAAU; +} + +void tx_low_power_exit() {} + +void vIllegalISR() +{ + printf("vIllegalISR\r\n"); + for (;;) + ; +} +} diff --git a/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..f0d6bbc1f68 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(threadXCoreConfiguration INTERFACE) + +target_include_directories(threadXCoreConfiguration INTERFACE include) diff --git a/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/include/tx_user.h b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/include/tx_user.h new file mode 100644 index 00000000000..53f6b4692fd --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/include/tx_user.h @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +/* ThreadX user configuration for NUCLEO-G474RE (Cortex-M4F, 170 MHz). */ + +#include "async/Config.h" + +#define ROUND_UP_32(x) (((x) + 31U) & ~31U) +#define TX_MAX_PRIORITIES_LIMIT 1024 +#define TX_MIN_PRIORITIES 32 + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES \ + ((ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U)) > TX_MAX_PRIORITIES_LIMIT \ + ? TX_MAX_PRIORITIES_LIMIT \ + : ((ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U)) < TX_MIN_PRIORITIES \ + ? TX_MIN_PRIORITIES \ + : ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U))) +#endif // TX_MAX_PRIORITIES + +#if (TX_MAX_PRIORITIES < 32) || (TX_MAX_PRIORITIES > 1024) || ((TX_MAX_PRIORITIES % 32) != 0) +#error "TX_MAX_PRIORITIES must be between 32 and 1024 and evenly divisible by 32" +#endif + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK (1024U) +#endif + +#define TX_ENABLE_STACK_CHECKING + +#define TX_NO_FILEX_POINTER + +/* Timer thread stack - the default 1024 is too small when the timer + * callback dispatches multiple runnables. */ +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE (8192U) +#endif + +#define TX_TIMER_TICKS_PER_SECOND (1000000U / ASYNC_CONFIG_TICK_IN_US) + +#define TX_ENABLE_EXECUTION_CHANGE_NOTIFY diff --git a/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_g474re/threadXCoreConfiguration/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/platforms/stm32/3rdparty/threadx/CMakeLists.txt b/platforms/stm32/3rdparty/threadx/CMakeLists.txt new file mode 100644 index 00000000000..bb368930244 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) + +project(threadXCortexM4 + LANGUAGES C ASM +) + +add_library(threadXCortexM4) + +add_subdirectory(ports/cortex_m4/gnu) + +add_library(threadXCortexM4Port INTERFACE) + +target_include_directories(threadXCortexM4Port INTERFACE ports/cortex_m4/gnu/inc) + +target_link_libraries(threadXCortexM4 PUBLIC bspInterrupts bspMcu threadX threadXCoreConfiguration) +target_link_libraries(threadXCortexM4Port INTERFACE threadXCoreConfiguration) + +target_compile_definitions(threadXCortexM4Port INTERFACE TX_INCLUDE_USER_DEFINE_FILE) +target_compile_definitions(threadXCortexM4 PRIVATE TX_INCLUDE_USER_DEFINE_FILE) diff --git a/platforms/stm32/3rdparty/threadx/LICENSE.md b/platforms/stm32/3rdparty/threadx/LICENSE.md new file mode 100644 index 00000000000..9b2b9468170 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 - present Microsoft Corporation + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/.riminfo b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/.riminfo new file mode 100644 index 00000000000..091ff1a77b0 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/.riminfo @@ -0,0 +1,15 @@ +e90467f30bb69d7ed878b3a0fe9ee24e761693ec + +RIM Info file. You're welcome to read but don't write it. +Instead, use RIM commands to do the things you want to do. +BEWARE: Any manual modification will invalidate the file! + +remote_url : https://github.com/eclipse-threadx/threadx.git +revision_sha1 : c4ad279b85fcf836c0b42d3e140fc32f0554e7b5 +target_revision: v6.4.3.202503_rel +ignores : +checksum : 7060c2b72cae400a4e0c5bf964fac9bcd9c73b78 +subdir : ports/cortex_m4/gnu + +committer_date : 2025-09-28 23:22:13 +0100 + diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/CMakeLists.txt b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/CMakeLists.txt new file mode 100644 index 00000000000..71e2d4f2e21 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/CMakeLists.txt @@ -0,0 +1,18 @@ + +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_schedule.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S + ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S + # {{END_TARGET_SOURCES}} +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc +) diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx.bat b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx.bat new file mode 100644 index 00000000000..c8e909cc4b9 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx.bat @@ -0,0 +1,228 @@ +del tx.a +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_stack_build.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_schedule.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_system_return.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_context_save.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_context_restore.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_thread_interrupt_control.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb ../src/tx_timer_interrupt.S + +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_allocate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_pool_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_block_release.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_allocate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_pool_search.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_byte_release.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_set.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_event_flags_set_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_initialize_high_level.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_initialize_kernel_enter.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_initialize_kernel_setup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_priority_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_mutex_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_flush.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_front_send.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_receive.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_send.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_queue_send_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_ceiling_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_cleanup.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_semaphore_put_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_entry_exit_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_identify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_preemption_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_priority_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_relinquish.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_reset.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_resume.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_shell_entry.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_sleep.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_stack_analyze.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_stack_error_handler.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_stack_error_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_suspend.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_system_preempt_check.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_system_resume.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_system_suspend.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_terminate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_time_slice.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_time_slice_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_timeout.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_thread_wait_abort.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_time_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_time_set.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_activate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_deactivate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_expiration_process.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_performance_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_performance_system_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_system_activate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_system_deactivate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_timer_thread_entry.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_buffer_full_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_enable.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_event_filter.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_event_unfilter.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_disable.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_initialize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_interrupt_control.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_isr_enter_insert.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_isr_exit_insert.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_object_register.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_object_unregister.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/tx_trace_user_event_insert.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_allocate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_pool_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_pool_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_pool_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_pool_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_block_release.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_allocate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_pool_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_pool_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_pool_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_pool_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_byte_release.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_set.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_event_flags_set_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_mutex_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_flush.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_front_send.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_receive.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_send.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_queue_send_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_ceiling_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_prioritize.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_put.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_semaphore_put_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_entry_exit_notify.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_info_get.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_preemption_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_priority_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_relinquish.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_reset.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_resume.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_suspend.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_terminate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_time_slice_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_thread_wait_abort.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_activate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_change.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_create.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_deactivate.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_delete.c +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc ../../../../common/src/txe_timer_info_get.c + +arm-none-eabi-ar -r tx.a tx_thread_stack_build.o tx_thread_schedule.o tx_thread_system_return.o tx_thread_context_save.o tx_thread_context_restore.o tx_timer_interrupt.o tx_thread_interrupt_control.o +arm-none-eabi-ar -r tx.a tx_block_allocate.o tx_block_pool_cleanup.o tx_block_pool_create.o tx_block_pool_delete.o tx_block_pool_info_get.o +arm-none-eabi-ar -r tx.a tx_block_pool_initialize.o tx_block_pool_performance_info_get.o tx_block_pool_performance_system_info_get.o tx_block_pool_prioritize.o +arm-none-eabi-ar -r tx.a tx_block_release.o tx_byte_allocate.o tx_byte_pool_cleanup.o tx_byte_pool_create.o tx_byte_pool_delete.o tx_byte_pool_info_get.o +arm-none-eabi-ar -r tx.a tx_byte_pool_initialize.o tx_byte_pool_performance_info_get.o tx_byte_pool_performance_system_info_get.o tx_byte_pool_prioritize.o +arm-none-eabi-ar -r tx.a tx_byte_pool_search.o tx_byte_release.o tx_event_flags_cleanup.o tx_event_flags_create.o tx_event_flags_delete.o tx_event_flags_get.o +arm-none-eabi-ar -r tx.a tx_event_flags_info_get.o tx_event_flags_initialize.o tx_event_flags_performance_info_get.o tx_event_flags_performance_system_info_get.o +arm-none-eabi-ar -r tx.a tx_event_flags_set.o tx_event_flags_set_notify.o tx_initialize_high_level.o tx_initialize_kernel_enter.o tx_initialize_kernel_setup.o +arm-none-eabi-ar -r tx.a tx_mutex_cleanup.o tx_mutex_create.o tx_mutex_delete.o tx_mutex_get.o tx_mutex_info_get.o tx_mutex_initialize.o tx_mutex_performance_info_get.o +arm-none-eabi-ar -r tx.a tx_mutex_performance_system_info_get.o tx_mutex_prioritize.o tx_mutex_priority_change.o tx_mutex_put.o tx_queue_cleanup.o tx_queue_create.o +arm-none-eabi-ar -r tx.a tx_queue_delete.o tx_queue_flush.o tx_queue_front_send.o tx_queue_info_get.o tx_queue_initialize.o tx_queue_performance_info_get.o +arm-none-eabi-ar -r tx.a tx_queue_performance_system_info_get.o tx_queue_prioritize.o tx_queue_receive.o tx_queue_send.o tx_queue_send_notify.o tx_semaphore_ceiling_put.o +arm-none-eabi-ar -r tx.a tx_semaphore_cleanup.o tx_semaphore_create.o tx_semaphore_delete.o tx_semaphore_get.o tx_semaphore_info_get.o tx_semaphore_initialize.o +arm-none-eabi-ar -r tx.a tx_semaphore_performance_info_get.o tx_semaphore_performance_system_info_get.o tx_semaphore_prioritize.o tx_semaphore_put.o tx_semaphore_put_notify.o +arm-none-eabi-ar -r tx.a tx_thread_create.o tx_thread_delete.o tx_thread_entry_exit_notify.o tx_thread_identify.o tx_thread_info_get.o tx_thread_initialize.o +arm-none-eabi-ar -r tx.a tx_thread_performance_info_get.o tx_thread_performance_system_info_get.o tx_thread_preemption_change.o tx_thread_priority_change.o tx_thread_relinquish.o +arm-none-eabi-ar -r tx.a tx_thread_reset.o tx_thread_resume.o tx_thread_shell_entry.o tx_thread_sleep.o tx_thread_stack_analyze.o tx_thread_stack_error_handler.o +arm-none-eabi-ar -r tx.a tx_thread_stack_error_notify.o tx_thread_suspend.o tx_thread_system_preempt_check.o tx_thread_system_resume.o tx_thread_system_suspend.o +arm-none-eabi-ar -r tx.a tx_thread_terminate.o tx_thread_time_slice.o tx_thread_time_slice_change.o tx_thread_timeout.o tx_thread_wait_abort.o tx_time_get.o +arm-none-eabi-ar -r tx.a tx_time_set.o tx_timer_activate.o tx_timer_change.o tx_timer_create.o tx_timer_deactivate.o tx_timer_delete.o tx_timer_expiration_process.o +arm-none-eabi-ar -r tx.a tx_timer_info_get.o tx_timer_initialize.o tx_timer_performance_info_get.o tx_timer_performance_system_info_get.o tx_timer_system_activate.o +arm-none-eabi-ar -r tx.a tx_timer_system_deactivate.o tx_timer_thread_entry.o tx_trace_enable.o tx_trace_disable.o tx_trace_initialize.o tx_trace_interrupt_control.o +arm-none-eabi-ar -r tx.a tx_trace_isr_enter_insert.o tx_trace_isr_exit_insert.o tx_trace_object_register.o tx_trace_object_unregister.o tx_trace_user_event_insert.o +arm-none-eabi-ar -r tx.a tx_trace_buffer_full_notify.o tx_trace_event_filter.o tx_trace_event_unfilter.o +arm-none-eabi-ar -r tx.a txe_block_allocate.o txe_block_pool_create.o txe_block_pool_delete.o txe_block_pool_info_get.o txe_block_pool_prioritize.o txe_block_release.o +arm-none-eabi-ar -r tx.a txe_byte_allocate.o txe_byte_pool_create.o txe_byte_pool_delete.o txe_byte_pool_info_get.o txe_byte_pool_prioritize.o txe_byte_release.o +arm-none-eabi-ar -r tx.a txe_event_flags_create.o txe_event_flags_delete.o txe_event_flags_get.o txe_event_flags_info_get.o txe_event_flags_set.o +arm-none-eabi-ar -r tx.a txe_event_flags_set_notify.o txe_mutex_create.o txe_mutex_delete.o txe_mutex_get.o txe_mutex_info_get.o txe_mutex_prioritize.o +arm-none-eabi-ar -r tx.a txe_mutex_put.o txe_queue_create.o txe_queue_delete.o txe_queue_flush.o txe_queue_front_send.o txe_queue_info_get.o txe_queue_prioritize.o +arm-none-eabi-ar -r tx.a txe_queue_receive.o txe_queue_send.o txe_queue_send_notify.o txe_semaphore_ceiling_put.o txe_semaphore_create.o txe_semaphore_delete.o +arm-none-eabi-ar -r tx.a txe_semaphore_get.o txe_semaphore_info_get.o txe_semaphore_prioritize.o txe_semaphore_put.o txe_semaphore_put_notify.o txe_thread_create.o +arm-none-eabi-ar -r tx.a txe_thread_delete.o txe_thread_entry_exit_notify.o txe_thread_info_get.o txe_thread_preemption_change.o txe_thread_priority_change.o +arm-none-eabi-ar -r tx.a txe_thread_relinquish.o txe_thread_reset.o txe_thread_resume.o txe_thread_suspend.o txe_thread_terminate.o txe_thread_time_slice_change.o +arm-none-eabi-ar -r tx.a txe_thread_wait_abort.o txe_timer_activate.o txe_timer_change.o txe_timer_create.o txe_timer_deactivate.o txe_timer_delete.o txe_timer_info_get.o diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx_sample.bat b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx_sample.bat new file mode 100644 index 00000000000..2be15702aaf --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/build_threadx_sample.bat @@ -0,0 +1,5 @@ +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb cortexm4_vectors.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb cortexm4_crt0.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb tx_initialize_low_level.S +arm-none-eabi-gcc -c -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -I../../../../common/inc -I../inc sample_threadx.c +arm-none-eabi-gcc -g -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=vfpv4 -mthumb -T sample_threadx.ld -ereset_handler -nostartfiles -o sample_threadx.out -Wl,-Map=sample_threadx.map cortexm4_vectors.o cortexm4_crt0.o tx_initialize_low_level.o sample_threadx.o tx.a diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_crt0.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_crt0.S new file mode 100644 index 00000000000..bb530ac5a0c --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_crt0.S @@ -0,0 +1,119 @@ + .global _start + .extern main + + + .section .init, "ax" + .code 16 + .align 2 + .thumb_func + + +_start: + CPSID i + ldr r1, =__stack_end__ + mov sp, r1 + + /* Copy initialised sections into RAM if required. */ + ldr r0, =__data_load_start__ + ldr r1, =__data_start__ + ldr r2, =__data_end__ + bl crt0_memory_copy + ldr r0, =__text_load_start__ + ldr r1, =__text_start__ + ldr r2, =__text_end__ + bl crt0_memory_copy + ldr r0, =__fast_load_start__ + ldr r1, =__fast_start__ + ldr r2, =__fast_end__ + bl crt0_memory_copy + ldr r0, =__ctors_load_start__ + ldr r1, =__ctors_start__ + ldr r2, =__ctors_end__ + bl crt0_memory_copy + ldr r0, =__dtors_load_start__ + ldr r1, =__dtors_start__ + ldr r2, =__dtors_end__ + bl crt0_memory_copy + ldr r0, =__rodata_load_start__ + ldr r1, =__rodata_start__ + ldr r2, =__rodata_end__ + bl crt0_memory_copy + + + /* Zero bss. */ + ldr r0, =__bss_start__ + ldr r1, =__bss_end__ + mov r2, #0 + bl crt0_memory_set + + /* Setup heap - not recommended for Threadx but here for compatibility reasons */ + ldr r0, = __heap_start__ + ldr r1, = __heap_end__ + sub r1, r1, r0 + mov r2, #0 + str r2, [r0] + add r0, r0, #4 + str r1, [r0] + + /* constructors in case of using C++ */ + ldr r0, =__ctors_start__ + ldr r1, =__ctors_end__ +crt0_ctor_loop: + cmp r0, r1 + beq crt0_ctor_end + ldr r2, [r0] + add r0, #4 + push {r0-r1} + blx r2 + pop {r0-r1} + b crt0_ctor_loop +crt0_ctor_end: + + /* Setup call frame for main() */ + mov r0, #0 + mov lr, r0 + mov r12, sp + +start: + /* Jump to main() */ + mov r0, #0 + mov r1, #0 + ldr r2, =main + blx r2 + /* when main returns, loop forever. */ +crt0_exit_loop: + b crt0_exit_loop + + + + /* Startup helper functions. */ + +crt0_memory_copy: + cmp r0, r1 + beq memory_copy_done + sub r2, r2, r1 + beq memory_copy_done +memory_copy_loop: + ldrb r3, [r0] + add r0, r0, #1 + strb r3, [r1] + add r1, r1, #1 + sub r2, r2, #1 + bne memory_copy_loop +memory_copy_done: + bx lr + +crt0_memory_set: + cmp r0, r1 + beq memory_set_done + strb r2, [r0] + add r0, r0, #1 + b crt0_memory_set +memory_set_done: + bx lr + + /* Setup attibutes of stack and heap sections so they don't take up room in the elf file */ + .section .stack, "wa", %nobits + .section .stack_process, "wa", %nobits + .section .heap, "wa", %nobits + \ No newline at end of file diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_vectors.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_vectors.S new file mode 100644 index 00000000000..6ae558e4dbb --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/cortexm4_vectors.S @@ -0,0 +1,77 @@ + .global reset_handler + + .global __tx_NMIHandler + .global __tx_BadHandler + .global __tx_SVCallHandler + .global __tx_DBGHandler + .global __tx_PendSVHandler + .global __tx_SysTickHandler + .global __tx_BadHandler + + .syntax unified + .section .vectors, "ax" + .code 16 + .align 0 + .global _vectors + +_vectors: + .word __stack_end__ + .word reset_handler + .word __tx_NMIHandler + .word __tx_HardfaultHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word 0 // Reserved + .word 0 // Reserved + .word 0 // Reserved + .word 0 // Reserved + .word __tx_SVCallHandler //_SVC_Handler - used by Threadx scheduler // + .word __tx_DBGHandler + .word 0 // Reserved + .word __tx_PendSVHandler + .word __tx_SysTickHandler // Used by Threadx timer functionality + .word __tx_BadHandler // Populate with user Interrupt handler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + .word __tx_BadHandler + + .section .init, "ax" + .thumb_func +reset_handler: + // low level hardware config, such as PLL setup goes here + b _start + + + diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.c b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.c new file mode 100644 index 00000000000..597f373ca09 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.c @@ -0,0 +1,370 @@ +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; +UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) + break; + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)) + break; + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.ld b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.ld new file mode 100644 index 00000000000..c65a134645a --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/sample_threadx.ld @@ -0,0 +1,125 @@ +MEMORY +{ + RAM (wx) : ORIGIN = 0x20000000, LENGTH = 0x00800000 + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00400000 +} + +__STACKSIZE__ = 1024; +__STACKSIZE_PROCESS__ = 0; +__HEAPSIZE__ = 128; + +SECTIONS +{ + .vectors : + { + KEEP(*(.vectors .vectors.*)) + } > FLASH + + .text : + { + *(.text*) + + KEEP(*(.init)) + KEEP(*(.fini)) + + /* .ctors */ + *crtbegin.o(.ctors) + *crtbegin?.o(.ctors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) + __ctors_start__ = ALIGN(4); + *(SORT(.ctors.*)) + *(.ctors) + __ctors_end__ = ALIGN(4); + + /* .dtors */ + *crtbegin.o(.dtors) + *crtbegin?.o(.dtors) + *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) + __dtors_start__ = ALIGN(4); + *(SORT(.dtors.*)) + *(.dtors) + __dtors_end__ = ALIGN(4); + + *(.rodata*) + + KEEP(*(.eh_frame*)) + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + __exidx_start = .; + .ARM.exidx : + { + *(.ARM.exidx* .gnu.linkonce.armexidx.*) + } > FLASH + __exidx_end = .; + + __data_load_start__ = ALIGN (4); + + .data : AT (__data_load_start__) + { + __data_start__ = .; + + *(vtable) + *(.data*) + + . = ALIGN(4); + /* preinit data */ + PROVIDE_HIDDEN (__preinit_array_start = .); + KEEP(*(.preinit_array)) + PROVIDE_HIDDEN (__preinit_array_end = .); + + . = ALIGN(4); + /* init data */ + PROVIDE_HIDDEN (__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE_HIDDEN (__init_array_end = .); + + . = ALIGN(4); + /* finit data */ + PROVIDE_HIDDEN (__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE_HIDDEN (__fini_array_end = .); + + KEEP(*(.jcr*)) + . = ALIGN(4); + /* All data end */ + + __data_end__ = .; + + } > RAM + + .bss : + { + . = ALIGN(4); + __bss_start__ = .; + *(.bss*) + *(COMMON) + . = ALIGN(4); + __bss_end__ = .; + + } > RAM + + .heap (COPY): + { + __heap_start__ = ALIGN(4); + *(.heap) + . = ALIGN(. + __HEAPSIZE__, 4); + __heap_end__ = ALIGN(4); + } > RAM + + .stack ALIGN(4) (NOLOAD) : + { + __stack_start__ = ALIGN(4); + *(.stack) + . = ALIGN(. + __STACKSIZE__, 4); + __stack_end__ = ALIGN(4); + } > RAM + + __RAM_segment_used_end__ = .; +} diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/tx_initialize_low_level.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/tx_initialize_low_level.S new file mode 100644 index 00000000000..d7e11c06bc2 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/example_build/tx_initialize_low_level.S @@ -0,0 +1,232 @@ +@/*************************************************************************** +@ * Copyright (c) 2024 Microsoft Corporation +@ * +@ * This program and the accompanying materials are made available under the +@ * terms of the MIT License which is available at +@ * https://opensource.org/licenses/MIT. +@ * +@ * SPDX-License-Identifier: MIT +@ **************************************************************************/ +@ +@ +@/**************************************************************************/ +@/**************************************************************************/ +@/** */ +@/** ThreadX Component */ +@/** */ +@/** Initialize */ +@/** */ +@/**************************************************************************/ +@/**************************************************************************/ +@ +@ + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global __RAM_segment_used_end__ + .global _tx_timer_interrupt + .global __main + .global __tx_SVCallHandler + .global __tx_PendSVHandler + .global _vectors + .global __tx_NMIHandler @ NMI + .global __tx_BadHandler @ HardFault + .global __tx_SVCallHandler @ SVCall + .global __tx_DBGHandler @ Monitor + .global __tx_PendSVHandler @ PendSV + .global __tx_SysTickHandler @ SysTick + .global __tx_IntHandler @ Int 0 +@ +@ +SYSTEM_CLOCK = 6000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) + + .text 32 + .align 4 + .syntax unified +@/**************************************************************************/ +@/* */ +@/* FUNCTION RELEASE */ +@/* */ +@/* _tx_initialize_low_level Cortex-M4/GNU */ +@/* 6.1 */ +@/* AUTHOR */ +@/* */ +@/* William E. Lamie, Microsoft Corporation */ +@/* */ +@/* DESCRIPTION */ +@/* */ +@/* This function is responsible for any low-level processor */ +@/* initialization, including setting up interrupt vectors, setting */ +@/* up a periodic timer interrupt source, saving the system stack */ +@/* pointer for use in ISR processing later, and finding the first */ +@/* available RAM memory address for tx_application_define. */ +@/* */ +@/* INPUT */ +@/* */ +@/* None */ +@/* */ +@/* OUTPUT */ +@/* */ +@/* None */ +@/* */ +@/* CALLS */ +@/* */ +@/* None */ +@/* */ +@/* CALLED BY */ +@/* */ +@/* _tx_initialize_kernel_enter ThreadX entry function */ +@/* */ +@/* RELEASE HISTORY */ +@/* */ +@/* DATE NAME DESCRIPTION */ +@/* */ +@/* 05-19-2020 William E. Lamie Initial Version 6.0 */ +@/* 09-30-2020 William E. Lamie Modified Comment(s), fixed */ +@/* GNU assembly comment, */ +@/* cleaned up whitespace, */ +@/* resulting in version 6.1 */ +@/* */ +@/**************************************************************************/ +@VOID _tx_initialize_low_level(VOID) +@{ + .global _tx_initialize_low_level + .thumb_func +_tx_initialize_low_level: +@ +@ /* Disable interrupts during ThreadX initialization. */ +@ + CPSID i +@ +@ /* Set base of available memory to end of non-initialised RAM area. */ +@ + LDR r0, =_tx_initialize_unused_memory @ Build address of unused memory pointer + LDR r1, =__RAM_segment_used_end__ @ Build first free address + ADD r1, r1, #4 @ + STR r1, [r0] @ Setup first unused memory pointer +@ +@ /* Setup Vector Table Offset Register. */ +@ + MOV r0, #0xE000E000 @ Build address of NVIC registers + LDR r1, =_vectors @ Pickup address of vector table + STR r1, [r0, #0xD08] @ Set vector table address +@ +@ /* Set system stack pointer from vector value. */ +@ + LDR r0, =_tx_thread_system_stack_ptr @ Build address of system stack pointer + LDR r1, =_vectors @ Pickup address of vector table + LDR r1, [r1] @ Pickup reset stack pointer + STR r1, [r0] @ Save system stack pointer +@ +@ /* Enable the cycle count register. */ +@ + LDR r0, =0xE0001000 @ Build address of DWT register + LDR r1, [r0] @ Pickup the current value + ORR r1, r1, #1 @ Set the CYCCNTENA bit + STR r1, [r0] @ Enable the cycle count register +@ +@ /* Configure SysTick for 100Hz clock, or 16384 cycles if no reference. */ +@ + MOV r0, #0xE000E000 @ Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + STR r1, [r0, #0x14] @ Setup SysTick Reload Value + MOV r1, #0x7 @ Build SysTick Control Enable Value + STR r1, [r0, #0x10] @ Setup SysTick Control +@ +@ /* Configure handler priorities. */ +@ + LDR r1, =0x00000000 @ Rsrv, UsgF, BusF, MemM + STR r1, [r0, #0xD18] @ Setup System Handlers 4-7 Priority Registers + + LDR r1, =0xFF000000 @ SVCl, Rsrv, Rsrv, Rsrv + STR r1, [r0, #0xD1C] @ Setup System Handlers 8-11 Priority Registers + @ Note: SVC must be lowest priority, which is 0xFF + + LDR r1, =0x40FF0000 @ SysT, PnSV, Rsrv, DbgM + STR r1, [r0, #0xD20] @ Setup System Handlers 12-15 Priority Registers + @ Note: PnSV must be lowest priority, which is 0xFF + +@ +@ /* Return to caller. */ +@ + BX lr +@} +@ + +@/* Define shells for each of the unused vectors. */ +@ + .global __tx_BadHandler + .thumb_func +__tx_BadHandler: + B __tx_BadHandler + +@ /* added to catch the hardfault */ + + .global __tx_HardfaultHandler + .thumb_func +__tx_HardfaultHandler: + B __tx_HardfaultHandler + + +@ /* added to catch the SVC */ + + .global __tx_SVCallHandler + .thumb_func +__tx_SVCallHandler: + B __tx_SVCallHandler + + +@ /* Generic interrupt handler template */ + .global __tx_IntHandler + .thumb_func +__tx_IntHandler: +@ VOID InterruptHandler (VOID) +@ { + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter @ Call the ISR enter function +#endif + +@ /* Do interrupt handler work here */ +@ /* BL .... */ + +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit @ Call the ISR exit function +#endif + POP {r0, lr} + BX LR +@ } + +@ /* System Tick timer interrupt handler */ + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: +@ VOID TimerInterruptHandler (VOID) +@ { +@ + PUSH {r0, lr} +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_enter @ Call the ISR enter function +#endif + BL _tx_timer_interrupt +#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY + BL _tx_execution_isr_exit @ Call the ISR exit function +#endif + POP {r0, lr} + BX LR +@ } + + +@ /* NMI, DBG handlers */ + .global __tx_NMIHandler + .thumb_func +__tx_NMIHandler: + B __tx_NMIHandler + + .global __tx_DBGHandler + .thumb_func +__tx_DBGHandler: + B __tx_DBGHandler diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/inc/tx_port.h b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/inc/tx_port.h new file mode 100644 index 00000000000..3eb8dbffa37 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/inc/tx_port.h @@ -0,0 +1,728 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Port Specific */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + +/**************************************************************************/ +/* */ +/* PORT SPECIFIC C INFORMATION RELEASE */ +/* */ +/* tx_port.h Cortex-M4/GNU */ +/* 6.1.12 */ +/* */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This file contains data type definitions that make the ThreadX */ +/* real-time kernel function identically on a variety of different */ +/* processor architectures. For example, the size or number of bits */ +/* in an "int" data type vary between microprocessor architectures and */ +/* even C compilers for the same microprocessor. ThreadX does not */ +/* directly use native C data types. Instead, ThreadX creates its */ +/* own special types that can be mapped to actual data types by this */ +/* file to guarantee consistency in the interface and functionality. */ +/* */ +/* This file replaces the previous Cortex-M3/M4/M7 files. It unifies */ +/* the ARMv7-M architecture and compilers into one common file. */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comments, updated */ +/* typedef to fix misra */ +/* violation, */ +/* fixed predefined macro, */ +/* resulting in version 6.1.10 */ +/* 04-25-2022 Scott Larson Modified comments and added */ +/* volatile to registers, */ +/* resulting in version 6.1.11 */ +/* 07-29-2022 Scott Larson Modified comments and */ +/* described BASEPRI usage, */ +/* resulting in version 6.1.12 */ +/* */ +/**************************************************************************/ + +#ifndef TX_PORT_H +#define TX_PORT_H + + +/* Determine if the optional ThreadX user define file should be used. */ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE + +/* Yes, include the user defines in tx_user.h. The defines in this file may + alternately be defined on the command line. */ + +#include "tx_user.h" +#endif + + +/* Define compiler library include files. */ + +#include +#include + +#ifdef __ICCARM__ +#include /* IAR Intrinsics */ +#define __asm__ __asm /* Define to make all inline asm look similar */ +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#include +#endif +#endif /* __ICCARM__ */ + +#ifdef __ghs__ +#include +#include "tx_ghs.h" +#endif /* __ghs__ */ + + +#if !defined(__GNUC__) && !defined(__CC_ARM) +#define __get_control_value __get_CONTROL +#define __set_control_value __set_CONTROL +#endif + +#ifndef __GNUC__ +#define __get_ipsr_value __get_IPSR +#endif + +/* Define ThreadX basic types for this port. */ + +#define VOID void +typedef char CHAR; +typedef unsigned char UCHAR; +typedef int INT; +typedef unsigned int UINT; +typedef long LONG; +typedef unsigned long ULONG; +typedef unsigned long long ULONG64; +typedef short SHORT; +typedef unsigned short USHORT; +#define ULONG64_DEFINED + +/* Define the priority levels for ThreadX. Legal values range + from 32 to 1024 and MUST be evenly divisible by 32. */ + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES 32 +#endif + + +/* Define the minimum stack for a ThreadX thread on this processor. If the size supplied during + thread creation is less than this value, the thread create call will return an error. */ + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK 200 /* Minimum stack size for this port */ +#endif + + +/* Define the system timer thread's default stack size and priority. These are only applicable + if TX_TIMER_PROCESS_IN_ISR is not defined. */ + +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE 1024 /* Default timer thread stack size */ +#endif + +#ifndef TX_TIMER_THREAD_PRIORITY +#define TX_TIMER_THREAD_PRIORITY 0 /* Default timer thread priority */ +#endif + +/* By default, ThreadX for Cortex-M uses the PRIMASK register to enable/disable interrupts. +If using BASEPRI is desired, define the following two symbols for both c and assembly files: +TX_PORT_USE_BASEPRI - This tells ThreadX to use BASEPRI instead of PRIMASK. +TX_PORT_BASEPRI = (priority_mask << (8 - number_priority_bits)) - this defines the maximum priority level to mask. +Any interrupt with a higher priority than priority_mask will not be masked, thus the interrupt will run. +*/ + +/* Define various constants for the ThreadX Cortex-M port. */ + +#define TX_INT_DISABLE 1 /* Disable interrupts */ +#define TX_INT_ENABLE 0 /* Enable interrupts */ + + +/* Define the clock source for trace event entry time stamp. The following two item are port specific. + For example, if the time source is at the address 0x0a800024 and is 16-bits in size, the clock + source constants would be: + +#define TX_TRACE_TIME_SOURCE *((volatile ULONG *) 0x0a800024) +#define TX_TRACE_TIME_MASK 0x0000FFFFUL + +*/ + +#ifndef TX_MISRA_ENABLE +#ifndef TX_TRACE_TIME_SOURCE +#define TX_TRACE_TIME_SOURCE *((volatile ULONG *) 0xE0001004) +#endif +#else +ULONG _tx_misra_time_stamp_get(VOID); +#define TX_TRACE_TIME_SOURCE _tx_misra_time_stamp_get() +#endif + +#ifndef TX_TRACE_TIME_MASK +#define TX_TRACE_TIME_MASK 0xFFFFFFFFUL +#endif + +#ifdef __ghs__ +/* Define constants for Green Hills EventAnalyzer. */ + +/* Define the number of ticks per second. This informs the EventAnalyzer what the timestamps + represent. By default, this is set to 1,000,000 i.e., one tick every microsecond. */ + +#define TX_EL_TICKS_PER_SECOND 1000000 + +/* Define the method of how to get the upper and lower 32-bits of the time stamp. By default, simply + simulate the time-stamp source with a counter. */ + +#define read_tbu() _tx_el_time_base_upper +#define read_tbl() ++_tx_el_time_base_lower +#endif /* __ghs__ */ + +/* Define the port specific options for the _tx_build_options variable. This variable indicates + how the ThreadX library was built. */ + +#define TX_PORT_SPECIFIC_BUILD_OPTIONS (0) + + +/* Define the in-line initialization constant so that modules with in-line + initialization capabilities can prevent their initialization from being + a function call. */ + +#ifdef TX_MISRA_ENABLE +#define TX_DISABLE_INLINE +#else +#define TX_INLINE_INITIALIZATION +#endif + + +/* Determine whether or not stack checking is enabled. By default, ThreadX stack checking is + disabled. When the following is defined, ThreadX thread stack checking is enabled. If stack + checking is enabled (TX_ENABLE_STACK_CHECKING is defined), the TX_DISABLE_STACK_FILLING + define is negated, thereby forcing the stack fill which is necessary for the stack checking + logic. */ + +#ifndef TX_MISRA_ENABLE +#ifdef TX_ENABLE_STACK_CHECKING +#undef TX_DISABLE_STACK_FILLING +#endif +#endif + + +/* Define the TX_THREAD control block extensions for this port. The main reason + for the multiple macros is so that backward compatibility can be maintained with + existing ThreadX kernel awareness modules. */ + +#define TX_THREAD_EXTENSION_0 +#define TX_THREAD_EXTENSION_1 +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#define TX_THREAD_EXTENSION_2 VOID *tx_thread_iar_tls_pointer; +#elif defined(__ghs__) +#define TX_THREAD_EXTENSION_2 VOID * tx_thread_eh_globals; \ + int Errno; /* errno. */ \ + char * strtok_saved_pos; /* strtok() position. */ +#else +#define TX_THREAD_EXTENSION_2 +#endif + + +#define TX_THREAD_EXTENSION_3 + + + +/* Define the port extensions of the remaining ThreadX objects. */ + +#define TX_BLOCK_POOL_EXTENSION +#define TX_BYTE_POOL_EXTENSION +#define TX_EVENT_FLAGS_GROUP_EXTENSION +#define TX_MUTEX_EXTENSION +#define TX_QUEUE_EXTENSION +#define TX_SEMAPHORE_EXTENSION +#define TX_TIMER_EXTENSION + + +/* Define the user extension field of the thread control block. Nothing + additional is needed for this port so it is defined as white space. */ + +#ifndef TX_THREAD_USER_EXTENSION +#define TX_THREAD_USER_EXTENSION +#endif + + +/* Define the macros for processing extensions in tx_thread_create, tx_thread_delete, + tx_thread_shell_entry, and tx_thread_terminate. */ + + +#ifdef TX_ENABLE_IAR_LIBRARY_SUPPORT +#if (__VER__ < 8000000) +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = __iar_dlib_perthread_allocate(); +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) __iar_dlib_perthread_deallocate(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION __iar_dlib_perthread_access(0); +#else +void *_tx_iar_create_per_thread_tls_area(void); +void _tx_iar_destroy_per_thread_tls_area(void *tls_ptr); +void __iar_Initlocks(void); + +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) thread_ptr -> tx_thread_iar_tls_pointer = _tx_iar_create_per_thread_tls_area(); +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) do {_tx_iar_destroy_per_thread_tls_area(thread_ptr -> tx_thread_iar_tls_pointer); \ + thread_ptr -> tx_thread_iar_tls_pointer = TX_NULL; } while(0); +#define TX_PORT_SPECIFIC_PRE_SCHEDULER_INITIALIZATION do {__iar_Initlocks();} while(0); +#endif +#else +#define TX_THREAD_CREATE_EXTENSION(thread_ptr) +#define TX_THREAD_DELETE_EXTENSION(thread_ptr) +#endif + +#if defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) + +#ifdef TX_MISRA_ENABLE + +ULONG _tx_misra_control_get(void); +void _tx_misra_control_set(ULONG value); +ULONG _tx_misra_fpccr_get(void); +void _tx_misra_vfp_touch(void); + +#else /* TX_MISRA_ENABLE not defined */ + +/* Define some helper functions (these are intrinsics in some compilers). */ +#ifdef __GNUC__ /* GCC and ARM Compiler 6 */ + +__attribute__( ( always_inline ) ) static inline ULONG __get_control_value(void) +{ +ULONG control_value; + + __asm__ volatile (" MRS %0,CONTROL ": "=r" (control_value) ); + return(control_value); +} + +__attribute__( ( always_inline ) ) static inline void __set_control_value(ULONG control_value) +{ + __asm__ volatile (" MSR CONTROL,%0": : "r" (control_value): "memory" ); +} + +#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); + +#elif defined(__CC_ARM) /* ARM Compiler 5 */ + +__attribute__( ( always_inline ) ) ULONG __get_control_value(void) +{ +ULONG control_value; + + __asm volatile ("MRS control_value,CONTROL"); + return(control_value); +} + +__attribute__( ( always_inline ) ) void __set_control_value(ULONG control_value) +{ + __asm__ volatile ("MSR CONTROL,control_value"); +} +/* Can't access VFP registers with inline asm, so define this in tx_thread_schedule. */ +void _tx_vfp_access(void); +#define TX_VFP_TOUCH() _tx_vfp_access(); + +#elif defined(__ICCARM__) /* IAR */ +#define TX_VFP_TOUCH() __asm__ volatile ("VMOV.F32 s0, s0"); +#endif /* Helper functions for different compilers */ + +#endif /* TX_MISRA_ENABLE */ + + +/* A completed thread falls into _thread_shell_entry and we can simply deactivate the FPU via CONTROL.FPCA + in order to ensure no lazy stacking will occur. */ + +#ifndef TX_MISRA_ENABLE + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control_value(_tx_vfp_state); \ + } +#else + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } + +#endif + +/* A thread can be terminated by another thread, so we first check if it's self-terminating and not in an ISR. + If so, deactivate the FPU via CONTROL.FPCA. Otherwise we are in an interrupt or another thread is terminating + this one, so if the FPCCR.LSPACT bit is set, we need to save the CONTROL.FPCA state, touch the FPU to flush + the lazy FPU save, then restore the CONTROL.FPCA state. */ + +#ifndef TX_MISRA_ENABLE + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control_value(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = *((volatile ULONG *) 0xE000EF34); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + TX_VFP_TOUCH(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = __get_control_value(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + __set_control_value(_tx_vfp_state); \ + } \ + } \ + } \ + } +#else + +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) { \ + ULONG _tx_system_state; \ + _tx_system_state = TX_THREAD_GET_SYSTEM_STATE(); \ + if ((_tx_system_state == ((ULONG) 0)) && ((thread_ptr) == _tx_thread_current_ptr)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + else \ + { \ + ULONG _tx_fpccr; \ + _tx_fpccr = _tx_misra_fpccr_get(); \ + _tx_fpccr = _tx_fpccr & ((ULONG) 0x01); \ + if (_tx_fpccr == ((ULONG) 0x01)) \ + { \ + ULONG _tx_vfp_state; \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ((ULONG) 0x4); \ + _tx_misra_vfp_touch(); \ + if (_tx_vfp_state == ((ULONG) 0)) \ + { \ + _tx_vfp_state = _tx_misra_control_get(); \ + _tx_vfp_state = _tx_vfp_state & ~((ULONG) 0x4); \ + _tx_misra_control_set(_tx_vfp_state); \ + } \ + } \ + } \ + } +#endif + +#else /* No VFP in use */ + +#define TX_THREAD_COMPLETED_EXTENSION(thread_ptr) +#define TX_THREAD_TERMINATED_EXTENSION(thread_ptr) + +#endif /* defined(__ARMVFP__) || defined(__ARM_PCS_VFP) || defined(__ARM_FP) || defined(__TARGET_FPU_VFP) || defined(__VFP__) */ + + +/* Define the ThreadX object creation extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_CREATE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_CREATE_EXTENSION(group_ptr) +#define TX_MUTEX_CREATE_EXTENSION(mutex_ptr) +#define TX_QUEUE_CREATE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_CREATE_EXTENSION(semaphore_ptr) +#define TX_TIMER_CREATE_EXTENSION(timer_ptr) + + +/* Define the ThreadX object deletion extensions for the remaining objects. */ + +#define TX_BLOCK_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_BYTE_POOL_DELETE_EXTENSION(pool_ptr) +#define TX_EVENT_FLAGS_GROUP_DELETE_EXTENSION(group_ptr) +#define TX_MUTEX_DELETE_EXTENSION(mutex_ptr) +#define TX_QUEUE_DELETE_EXTENSION(queue_ptr) +#define TX_SEMAPHORE_DELETE_EXTENSION(semaphore_ptr) +#define TX_TIMER_DELETE_EXTENSION(timer_ptr) + + +/* Define the get system state macro. */ + +#ifndef TX_THREAD_GET_SYSTEM_STATE +#ifndef TX_MISRA_ENABLE + +#ifdef __CC_ARM /* ARM Compiler 5 */ + +register unsigned int _ipsr __asm("ipsr"); +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _ipsr) + +#elif defined(__GNUC__) /* GCC and ARM Compiler 6 */ + +__attribute__( ( always_inline ) ) static inline unsigned int __get_ipsr_value(void) +{ +unsigned int ipsr_value; + __asm__ volatile (" MRS %0,IPSR ": "=r" (ipsr_value) ); + return(ipsr_value); +} + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_ipsr_value()) + +#elif defined(__ICCARM__) /* IAR */ + +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | __get_IPSR()) + +#endif /* TX_THREAD_GET_SYSTEM_STATE for different compilers */ + +#else /* TX_MISRA_ENABLE is defined, use MISRA function. */ +ULONG _tx_misra_ipsr_get(VOID); +#define TX_THREAD_GET_SYSTEM_STATE() (_tx_thread_system_state | _tx_misra_ipsr_get()) +#endif /* TX_MISRA_ENABLE */ +#endif /* TX_THREAD_GET_SYSTEM_STATE */ + + +/* Define the check for whether or not to call the _tx_thread_system_return function. A non-zero value + indicates that _tx_thread_system_return should not be called. This overrides the definition in tx_thread.h + for Cortex-M since so we don't waste time checking the _tx_thread_system_state variable that is always + zero after initialization for Cortex-M ports. */ + +#ifndef TX_THREAD_SYSTEM_RETURN_CHECK +#define TX_THREAD_SYSTEM_RETURN_CHECK(c) (c) = ((ULONG) _tx_thread_preempt_disable); +#endif + +/* Define the macro to ensure _tx_thread_preempt_disable is set early in initialization in order to + prevent early scheduling on Cortex-M parts. */ + +#define TX_PORT_SPECIFIC_POST_INITIALIZATION _tx_thread_preempt_disable++; + + + + +#ifndef TX_DISABLE_INLINE + +/* Define the TX_LOWEST_SET_BIT_CALCULATE macro for each compiler. */ +#ifdef __ICCARM__ /* IAR Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __CLZ(__RBIT((m))); +#elif defined(__CC_ARM) /* AC5 Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) (b) = (UINT) __clz(__rbit((m))); +#elif defined(__GNUC__) /* GCC and AC6 Compiler */ +#define TX_LOWEST_SET_BIT_CALCULATE(m, b) __asm__ volatile (" RBIT %0,%1 ": "=r" (m) : "r" (m) ); \ + __asm__ volatile (" CLZ %0,%1 ": "=r" (b) : "r" (m) ); +#endif + + + +/* Define the interrupt disable/restore macros for each compiler. */ + +#if defined(__GNUC__) || defined(__ICCARM__) + +/*** GCC/AC6 and IAR ***/ + +__attribute__( ( always_inline ) ) static inline unsigned int __get_interrupt_posture(void) +{ +unsigned int posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS %0, BASEPRI ": "=r" (posture)); +#else + __asm__ volatile ("MRS %0, PRIMASK ": "=r" (posture)); +#endif + return(posture); +} + +#ifdef TX_PORT_USE_BASEPRI +__attribute__( ( always_inline ) ) static inline void __set_basepri_value(unsigned int basepri_value) +{ + __asm__ volatile ("MSR BASEPRI,%0 ": : "r" (basepri_value)); +} +#else +__attribute__( ( always_inline ) ) static inline void __enable_interrupts(void) +{ + __asm__ volatile ("CPSIE i": : : "memory"); +} +#endif + +__attribute__( ( always_inline ) ) static inline void __restore_interrupt(unsigned int int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); + //__asm__ volatile ("MSR BASEPRI,%0": : "r" (int_posture): "memory"); +#else + __asm__ volatile ("MSR PRIMASK,%0": : "r" (int_posture): "memory"); +#endif +} + +__attribute__( ( always_inline ) ) static inline unsigned int __disable_interrupts(void) +{ +unsigned int int_posture; + + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i" : : : "memory"); +#endif + return(int_posture); +} + +__attribute__( ( always_inline ) ) static inline void _tx_thread_system_return_inline(void) +{ +unsigned int interrupt_save; + + /* Set PendSV to invoke ThreadX scheduler. */ + *((volatile ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (__get_ipsr_value() == 0) + { + interrupt_save = __get_interrupt_posture(); +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(0); +#else + __enable_interrupts(); +#endif + __restore_interrupt(interrupt_save); + } +} + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupt(interrupt_save); + +/*** End GCC/AC6 and IAR ***/ + +#elif defined(__CC_ARM) + +/*** AC5 ***/ + +static __inline unsigned int __get_interrupt_posture(void) +{ +unsigned int posture; +#ifdef TX_PORT_USE_BASEPRI + __asm__ volatile ("MRS #posture, BASEPRI"); +#else + __asm__ volatile ("MRS #posture, PRIMASK"); +#endif + return(posture); +} + +#ifdef TX_PORT_USE_BASEPRI +static __inline void __set_basepri_value(unsigned int basepri_value) +{ + __asm__ volatile ("MSR BASEPRI, #basepri_value"); +} +#endif + +static __inline unsigned int __disable_interrupts(void) +{ +unsigned int int_posture; + + int_posture = __get_interrupt_posture(); + +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(TX_PORT_BASEPRI); +#else + __asm__ volatile ("CPSID i"); +#endif + return(int_posture); +} + +static __inline void __restore_interrupt(unsigned int int_posture) +{ +#ifdef TX_PORT_USE_BASEPRI + __set_basepri_value(int_posture); +#else + __asm__ volatile ("MSR PRIMASK, #int_posture"); +#endif +} + +static void _tx_thread_system_return_inline(void) +{ +unsigned int interrupt_save; + + /* Set PendSV to invoke ThreadX scheduler. */ + *((volatile ULONG *) 0xE000ED04) = ((ULONG) 0x10000000); + if (_ipsr == 0) + { +#ifdef TX_PORT_USE_BASEPRI + interrupt_save = __get_interrupt_posture(); + __set_basepri_value(0); + __set_basepri_value(interrupt_save); +#else + interrupt_save = __disable_irq(); + __enable_irq(); + if (interrupt_save != 0) + __disable_irq(); +#endif + } +} + + +#define TX_INTERRUPT_SAVE_AREA UINT interrupt_save; +#define TX_DISABLE interrupt_save = __disable_interrupts(); +#define TX_RESTORE __restore_interrupt(interrupt_save); + +/*** End AC5 ***/ + +#endif /* Interrupt disable/restore macros for each compiler. */ + +/* Redefine _tx_thread_system_return for improved performance. */ + +#define _tx_thread_system_return _tx_thread_system_return_inline + + +#else /* TX_DISABLE_INLINE is defined */ + +UINT _tx_thread_interrupt_disable(VOID); +VOID _tx_thread_interrupt_restore(UINT previous_posture); + +#define TX_INTERRUPT_SAVE_AREA register UINT interrupt_save; + +#define TX_DISABLE interrupt_save = _tx_thread_interrupt_disable(); +#define TX_RESTORE _tx_thread_interrupt_restore(interrupt_save); +#endif /* TX_DISABLE_INLINE */ + + +/* Define FPU extension for the Cortex-M. Each is assumed to be called in the context of the executing + thread. These are no longer needed, but are preserved for backward compatibility only. */ + +void tx_thread_fpu_enable(void); +void tx_thread_fpu_disable(void); + + +/* Define the version ID of ThreadX. This may be utilized by the application. */ + +#ifdef TX_THREAD_INIT +CHAR _tx_version_id[] = + "Copyright (c) 2024 Microsoft Corporation. * ThreadX Cortex-M4/GNU Version 6.4.2 *"; +#else +#ifdef TX_MISRA_ENABLE +extern CHAR _tx_version_id[100]; +#else +extern CHAR _tx_version_id[]; +#endif +#endif + + +#endif diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/readme_threadx.txt b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/readme_threadx.txt new file mode 100644 index 00000000000..d9063d65cc5 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/readme_threadx.txt @@ -0,0 +1,209 @@ + Microsoft's Azure RTOS ThreadX for ARMv7-M + (Cortex-M3, Cortex-M4, Cortex-M7) + Using the GNU Tools + + +1. Building the ThreadX run-time Library + +Navigate to the "example_build" directory. Ensure that +you have setup your path and other environment variables necessary for the ARM +GNU compiler. At this point you may run the build_threadx.bat batch file. +This will build the ThreadX run-time environment in the "example_build" +directory. + +You should observe assembly and compilation of a series of ThreadX source +files. At the end of the batch file, they are all combined into the +run-time library file: tx.a. This file must be linked with your +application in order to use ThreadX. + + +2. Demonstration System + +The ThreadX demonstration is designed to execute on Cortex-M evaluation boards +or on a dedicated simulator. + +Building the demonstration is easy, simply execute the build_threadx_sample.bat +batch file while inside the "example_build" directory. + +You should observe the compilation of sample_threadx.c (which is the demonstration +application) and linking with tx.a. The resulting file sample_threadx.out is a binary +file that can be downloaded and executed on the a simulator, or downloaded to a board. + + +3. System Initialization + +The entry point in ThreadX for the Cortex-M using gnu tools uses the standard GNU +Cortex-M reset sequence. From the reset vector the C runtime will be initialized. + +The ThreadX tx_initialize_low_level.S file is responsible for setting up +various system data structures, the vector area, and a periodic timer interrupt +source. + +In addition, _tx_initialize_low_level determines the first available +address for use by the application, which is supplied as the sole input +parameter to your application definition function, tx_application_define. + + +4. Register Usage and Stack Frames + +The following defines the saved context stack frames for context switches +that occur as a result of interrupt handling or from thread-level API calls. +All suspended threads have the same stack frame in the Cortex-M version of +ThreadX. The top of the suspended thread's stack is pointed to by +tx_thread_stack_ptr in the associated thread control block TX_THREAD. + +Non-FPU Stack Frame: + + Stack Offset Stack Contents + + 0x00 lr Interrupted lr (lr at time of PENDSV) + 0x04 r4 Software stacked GP registers + 0x08 r5 + 0x0C r6 + 0x10 r7 + 0x14 r8 + 0x18 r9 + 0x1C r10 + 0x20 r11 + 0x24 r0 Hardware stacked registers + 0x28 r1 + 0x2C r2 + 0x30 r3 + 0x34 r12 + 0x38 lr + 0x3C pc + 0x40 xPSR + +FPU Stack Frame (only interrupted thread with FPU enabled): + + Stack Offset Stack Contents + + 0x00 lr Interrupted lr (lr at time of PENDSV) + 0x04 s16 Software stacked FPU registers + 0x08 s17 + 0x0C s18 + 0x10 s19 + 0x14 s20 + 0x18 s21 + 0x1C s22 + 0x20 s23 + 0x24 s24 + 0x28 s25 + 0x2C s26 + 0x30 s27 + 0x34 s28 + 0x38 s29 + 0x3C s30 + 0x40 s31 + 0x44 r4 Software stacked registers + 0x48 r5 + 0x4C r6 + 0x50 r7 + 0x54 r8 + 0x58 r9 + 0x5C r10 + 0x60 r11 + 0x64 r0 Hardware stacked registers + 0x68 r1 + 0x6C r2 + 0x70 r3 + 0x74 r12 + 0x78 lr + 0x7C pc + 0x80 xPSR + 0x84 s0 Hardware stacked FPU registers + 0x88 s1 + 0x8C s2 + 0x90 s3 + 0x94 s4 + 0x98 s5 + 0x9C s6 + 0xA0 s7 + 0xA4 s8 + 0xA8 s9 + 0xAC s10 + 0xB0 s11 + 0xB4 s12 + 0xB8 s13 + 0xBC s14 + 0xC0 s15 + 0xC4 fpscr + + +5. Improving Performance + +The distribution version of ThreadX is built without any compiler optimizations. +This makes it easy to debug because you can trace or set breakpoints inside of +ThreadX itself. Of course, this costs some performance. To make it run faster, +you can change the build_threadx.bat file to remove the -g option and enable +all compiler optimizations. + +In addition, you can eliminate the ThreadX basic API error checking by +compiling your application code with the symbol TX_DISABLE_ERROR_CHECKING +defined. + + +6. Interrupt Handling + +ThreadX provides complete and high-performance interrupt handling for Cortex-M +targets. There are a certain set of requirements that are defined in the +following sub-sections: + + +6.1 Vector Area + +The Cortex-M vectors start at the label __tx_vectors or similar. The application may modify +the vector area according to its needs. There is code in tx_initialize_low_level() that will +configure the vector base register. + + +6.2 Managed Interrupts + +A ThreadX managed interrupt is defined below. By following these conventions, the +application ISR is then allowed access to various ThreadX services from the ISR. +Here is the standard template for managed ISRs in ThreadX: + + + .global __tx_IntHandler + .thumb_func +__tx_IntHandler: +; VOID InterruptHandler (VOID) +; { + PUSH {r0, lr} + +; /* Do interrupt handler work here */ +; /* BL */ + + POP {r0, lr} + BX lr +; } + + +Note: the Cortex-M requires exception handlers to be thumb labels, this implies bit 0 set. +To accomplish this, the declaration of the label has to be preceded by the assembler directive +.thumb_func to instruct the linker to create thumb labels. The label __tx_IntHandler needs to +be inserted in the correct location in the interrupt vector table. This table is typically +located in either your runtime startup file or in the tx_initialize_low_level.S file. + + +7. FPU Support + +ThreadX for Cortex-M supports automatic ("lazy") VFP support, which means that applications threads +can simply use the VFP and ThreadX automatically maintains the VFP registers as part of the thread +context - no additional setup by the application. + + +8. Revision History + +For generic code revision information, please refer to the readme_threadx_generic.txt +file, which is included in your distribution. The following details the revision +information associated with this specific port of ThreadX: + +06-02-2021 Initial ThreadX version 6.1.7 for Cortex-M using GNU tools. + + +Copyright(c) 1996-2021 Microsoft Corporation + + +https://azure.com/rtos + diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_misra.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_misra.S new file mode 100644 index 00000000000..8ac0c629f1a --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_misra.S @@ -0,0 +1,722 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** ThreadX MISRA Compliance */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + #define SHT_PROGBITS 0x1 + + .global __aeabi_memset + .global _tx_thread_current_ptr + .global _tx_thread_interrupt_disable + .global _tx_thread_interrupt_restore + .global _tx_thread_stack_analyze + .global _tx_thread_stack_error_handler + .global _tx_thread_system_state +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_trace_buffer_current_ptr + .global _tx_trace_buffer_end_ptr + .global _tx_trace_buffer_start_ptr + .global _tx_trace_event_enable_bits + .global _tx_trace_full_notify_function + .global _tx_trace_header_ptr +#endif + + .global _tx_misra_always_true + .global _tx_misra_block_pool_to_uchar_pointer_convert + .global _tx_misra_byte_pool_to_uchar_pointer_convert + .global _tx_misra_char_to_uchar_pointer_convert + .global _tx_misra_const_char_to_char_pointer_convert +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_entry_to_uchar_pointer_convert +#endif + .global _tx_misra_indirect_void_to_uchar_pointer_convert + .global _tx_misra_memset + .global _tx_misra_message_copy +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_object_to_uchar_pointer_convert +#endif + .global _tx_misra_pointer_to_ulong_convert + .global _tx_misra_status_get + .global _tx_misra_thread_stack_check +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_time_stamp_get +#endif + .global _tx_misra_timer_indirect_to_void_pointer_convert + .global _tx_misra_timer_pointer_add + .global _tx_misra_timer_pointer_dif +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_trace_event_insert +#endif + .global _tx_misra_uchar_pointer_add + .global _tx_misra_uchar_pointer_dif + .global _tx_misra_uchar_pointer_sub + .global _tx_misra_uchar_to_align_type_pointer_convert + .global _tx_misra_uchar_to_block_pool_pointer_convert +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_uchar_to_entry_pointer_convert + .global _tx_misra_uchar_to_header_pointer_convert +#endif + .global _tx_misra_uchar_to_indirect_byte_pool_pointer_convert + .global _tx_misra_uchar_to_indirect_uchar_pointer_convert +#ifdef TX_ENABLE_EVENT_TRACE + .global _tx_misra_uchar_to_object_pointer_convert +#endif + .global _tx_misra_uchar_to_void_pointer_convert + .global _tx_misra_ulong_pointer_add + .global _tx_misra_ulong_pointer_dif + .global _tx_misra_ulong_pointer_sub + .global _tx_misra_ulong_to_pointer_convert + .global _tx_misra_ulong_to_thread_pointer_convert + .global _tx_misra_user_timer_pointer_get + .global _tx_misra_void_to_block_pool_pointer_convert + .global _tx_misra_void_to_byte_pool_pointer_convert + .global _tx_misra_void_to_event_flags_pointer_convert + .global _tx_misra_void_to_indirect_uchar_pointer_convert + .global _tx_misra_void_to_mutex_pointer_convert + .global _tx_misra_void_to_queue_pointer_convert + .global _tx_misra_void_to_semaphore_pointer_convert + .global _tx_misra_void_to_thread_pointer_convert + .global _tx_misra_void_to_uchar_pointer_convert + .global _tx_misra_void_to_ulong_pointer_convert + .global _tx_misra_ipsr_get + .global _tx_misra_control_get + .global _tx_misra_control_set +#ifdef __ARM_FP + .global _tx_misra_fpccr_get + .global _tx_misra_vfp_touch +#endif + + .global _tx_misra_event_flags_group_not_used + .global _tx_misra_event_flags_set_notify_not_used + .global _tx_misra_queue_not_used + .global _tx_misra_queue_send_notify_not_used + .global _tx_misra_semaphore_not_used + .global _tx_misra_semaphore_put_notify_not_used + .global _tx_misra_thread_entry_exit_notify_not_used + .global _tx_misra_thread_not_used + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_memset(VOID *ptr, UINT value, UINT size); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .align 4 + .syntax unified + .thumb_func +_tx_misra_memset: + PUSH {R4,LR} + MOVS R4,R0 + MOVS R0,R2 + MOVS R2,R1 + MOVS R1,R0 + MOVS R0,R4 + BL __aeabi_memset + POP {R4,PC} // return + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UCHAR *_tx_misra_uchar_pointer_add(UCHAR *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_uchar_pointer_add: + ADD R0,R0,R1 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UCHAR *_tx_misra_uchar_pointer_sub(UCHAR *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_uchar_pointer_sub: + RSBS R1,R1,#+0 + ADD R0,R0,R1 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_uchar_pointer_dif(UCHAR *ptr1, UCHAR *ptr2); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_uchar_pointer_dif: + SUBS R0,R0,R1 + BX LR // return + + +/************************************************************************************************************************************/ +/************************************************************************************************************************************/ +/** */ +/** This single function serves all of the below prototypes. */ +/** */ +/** ULONG _tx_misra_pointer_to_ulong_convert(VOID *ptr); */ +/** VOID *_tx_misra_ulong_to_pointer_convert(ULONG input); */ +/** UCHAR **_tx_misra_indirect_void_to_uchar_pointer_convert(VOID **return_ptr); */ +/** UCHAR **_tx_misra_uchar_to_indirect_uchar_pointer_convert(UCHAR *pointer); */ +/** UCHAR *_tx_misra_block_pool_to_uchar_pointer_convert(TX_BLOCK_POOL *pool); */ +/** TX_BLOCK_POOL *_tx_misra_void_to_block_pool_pointer_convert(VOID *pointer); */ +/** UCHAR *_tx_misra_void_to_uchar_pointer_convert(VOID *pointer); */ +/** TX_BLOCK_POOL *_tx_misra_uchar_to_block_pool_pointer_convert(UCHAR *pointer); */ +/** UCHAR **_tx_misra_void_to_indirect_uchar_pointer_convert(VOID *pointer); */ +/** TX_BYTE_POOL *_tx_misra_void_to_byte_pool_pointer_convert(VOID *pointer); */ +/** UCHAR *_tx_misra_byte_pool_to_uchar_pointer_convert(TX_BYTE_POOL *pool); */ +/** ALIGN_TYPE *_tx_misra_uchar_to_align_type_pointer_convert(UCHAR *pointer); */ +/** TX_BYTE_POOL **_tx_misra_uchar_to_indirect_byte_pool_pointer_convert(UCHAR *pointer); */ +/** TX_EVENT_FLAGS_GROUP *_tx_misra_void_to_event_flags_pointer_convert(VOID *pointer); */ +/** ULONG *_tx_misra_void_to_ulong_pointer_convert(VOID *pointer); */ +/** TX_MUTEX *_tx_misra_void_to_mutex_pointer_convert(VOID *pointer); */ +/** TX_QUEUE *_tx_misra_void_to_queue_pointer_convert(VOID *pointer); */ +/** TX_SEMAPHORE *_tx_misra_void_to_semaphore_pointer_convert(VOID *pointer); */ +/** VOID *_tx_misra_uchar_to_void_pointer_convert(UCHAR *pointer); */ +/** TX_THREAD *_tx_misra_ulong_to_thread_pointer_convert(ULONG value); */ +/** VOID *_tx_misra_timer_indirect_to_void_pointer_convert(TX_TIMER_INTERNAL **pointer); */ +/** CHAR *_tx_misra_const_char_to_char_pointer_convert(const char *pointer); */ +/** TX_THREAD *_tx_misra_void_to_thread_pointer_convert(void *pointer); */ +/** UCHAR *_tx_misra_object_to_uchar_pointer_convert(TX_TRACE_OBJECT_ENTRY *pointer); */ +/** TX_TRACE_OBJECT_ENTRY *_tx_misra_uchar_to_object_pointer_convert(UCHAR *pointer); */ +/** TX_TRACE_HEADER *_tx_misra_uchar_to_header_pointer_convert(UCHAR *pointer); */ +/** TX_TRACE_BUFFER_ENTRY *_tx_misra_uchar_to_entry_pointer_convert(UCHAR *pointer); */ +/** UCHAR *_tx_misra_entry_to_uchar_pointer_convert(TX_TRACE_BUFFER_ENTRY *pointer); */ +/** UCHAR *_tx_misra_char_to_uchar_pointer_convert(CHAR *pointer); */ +/** VOID _tx_misra_event_flags_group_not_used(TX_EVENT_FLAGS_GROUP *group_ptr); */ +/** VOID _tx_misra_event_flags_set_notify_not_used(VOID (*events_set_notify)(TX_EVENT_FLAGS_GROUP *notify_group_ptr)); */ +/** VOID _tx_misra_queue_not_used(TX_QUEUE *queue_ptr); */ +/** VOID _tx_misra_queue_send_notify_not_used(VOID (*queue_send_notify)(TX_QUEUE *notify_queue_ptr)); */ +/** VOID _tx_misra_semaphore_not_used(TX_SEMAPHORE *semaphore_ptr); */ +/** VOID _tx_misra_semaphore_put_notify_not_used(VOID (*semaphore_put_notify)(TX_SEMAPHORE *notify_semaphore_ptr)); */ +/** VOID _tx_misra_thread_not_used(TX_THREAD *thread_ptr); */ +/** VOID _tx_misra_thread_entry_exit_notify_not_used(VOID (*thread_entry_exit_notify)(TX_THREAD *notify_thread_ptr, UINT id)); */ +/** */ +/************************************************************************************************************************************/ +/************************************************************************************************************************************/ + .text + .thumb_func +_tx_misra_pointer_to_ulong_convert: +_tx_misra_ulong_to_pointer_convert: +_tx_misra_indirect_void_to_uchar_pointer_convert: +_tx_misra_uchar_to_indirect_uchar_pointer_convert: +_tx_misra_block_pool_to_uchar_pointer_convert: +_tx_misra_void_to_block_pool_pointer_convert: +_tx_misra_void_to_uchar_pointer_convert: +_tx_misra_uchar_to_block_pool_pointer_convert: +_tx_misra_void_to_indirect_uchar_pointer_convert: +_tx_misra_void_to_byte_pool_pointer_convert: +_tx_misra_byte_pool_to_uchar_pointer_convert: +_tx_misra_uchar_to_align_type_pointer_convert: +_tx_misra_uchar_to_indirect_byte_pool_pointer_convert: +_tx_misra_void_to_event_flags_pointer_convert: +_tx_misra_void_to_ulong_pointer_convert: +_tx_misra_void_to_mutex_pointer_convert: +_tx_misra_void_to_queue_pointer_convert: +_tx_misra_void_to_semaphore_pointer_convert: +_tx_misra_uchar_to_void_pointer_convert: +_tx_misra_ulong_to_thread_pointer_convert: +_tx_misra_timer_indirect_to_void_pointer_convert: +_tx_misra_const_char_to_char_pointer_convert: +_tx_misra_void_to_thread_pointer_convert: +#ifdef TX_ENABLE_EVENT_TRACE +_tx_misra_object_to_uchar_pointer_convert: +_tx_misra_uchar_to_object_pointer_convert: +_tx_misra_uchar_to_header_pointer_convert: +_tx_misra_uchar_to_entry_pointer_convert: +_tx_misra_entry_to_uchar_pointer_convert: +#endif +_tx_misra_char_to_uchar_pointer_convert: +_tx_misra_event_flags_group_not_used: +_tx_misra_event_flags_set_notify_not_used: +_tx_misra_queue_not_used: +_tx_misra_queue_send_notify_not_used: +_tx_misra_semaphore_not_used: +_tx_misra_semaphore_put_notify_not_used: +_tx_misra_thread_entry_exit_notify_not_used: +_tx_misra_thread_not_used: + + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG *_tx_misra_ulong_pointer_add(ULONG *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_ulong_pointer_add: + ADD R0,R0,R1, LSL #+2 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG *_tx_misra_ulong_pointer_sub(ULONG *ptr, ULONG amount); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_ulong_pointer_sub: + MVNS R2,#+3 + MULS R1,R2,R1 + ADD R0,R0,R1 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_ulong_pointer_dif(ULONG *ptr1, ULONG *ptr2); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_ulong_pointer_dif: + SUBS R0,R0,R1 + ASRS R0,R0,#+2 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_message_copy(ULONG **source, ULONG **destination, */ +/** UINT size); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_message_copy: + PUSH {R4,R5} + LDR R3,[R0, #+0] + LDR R4,[R1, #+0] + LDR R5,[R3, #+0] + STR R5,[R4, #+0] + ADDS R4,R4,#+4 + ADDS R3,R3,#+4 + CMP R2,#+2 + BCC.N _tx_misra_message_copy_0 + SUBS R2,R2,#+1 + B.N _tx_misra_message_copy_1 +_tx_misra_message_copy_2: + LDR R5,[R3, #+0] + STR R5,[R4, #+0] + ADDS R4,R4,#+4 + ADDS R3,R3,#+4 + SUBS R2,R2,#+1 +_tx_misra_message_copy_1: + CMP R2,#+0 + BNE.N _tx_misra_message_copy_2 +_tx_misra_message_copy_0: + STR R3,[R0, #+0] + STR R4,[R1, #+0] + POP {R4,R5} + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_timer_pointer_dif(TX_TIMER_INTERNAL **ptr1, */ +/** TX_TIMER_INTERNAL **ptr2); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_timer_pointer_dif: + SUBS R0,R0,R1 + ASRS R0,R0,#+2 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** TX_TIMER_INTERNAL **_tx_misra_timer_pointer_add(TX_TIMER_INTERNAL */ +/** **ptr1, ULONG size); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_timer_pointer_add: + ADD R0,R0,R1, LSL #+2 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_user_timer_pointer_get(TX_TIMER_INTERNAL */ +/** *internal_timer, TX_TIMER **user_timer); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_user_timer_pointer_get: + SUBS R0,#8 + STR R0,[R1, #+0] + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_thread_stack_check(TX_THREAD *thread_ptr, */ +/** VOID **highest_stack); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_thread_stack_check: + PUSH {R3-R5,LR} + MOVS R4,R0 + MOVS R5,R1 + BL _tx_thread_interrupt_disable + CMP R4,#+0 + BEQ.N _tx_misra_thread_stack_check_0 + LDR R1,[R4, #+0] + LDR R2,=0x54485244 + CMP R1,R2 + BNE.N _tx_misra_thread_stack_check_0 + LDR R1,[R4, #+8] + LDR R2,[R5, #+0] + CMP R1,R2 + BCS.N _tx_misra_thread_stack_check_1 + LDR R1,[R4, #+8] + STR R1,[R5, #+0] +_tx_misra_thread_stack_check_1: + LDR R1,[R4, #+12] + LDR R1,[R1, #+0] + CMP R1,#-269488145 + BNE.N _tx_misra_thread_stack_check_2 + LDR R1,[R4, #+16] + LDR R1,[R1, #+1] + CMP R1,#-269488145 + BNE.N _tx_misra_thread_stack_check_2 + LDR R1,[R5, #+0] + LDR R2,[R4, #+12] + CMP R1,R2 + BCS.N _tx_misra_thread_stack_check_3 +_tx_misra_thread_stack_check_2: + BL _tx_thread_interrupt_restore + MOVS R0,R4 + BL _tx_thread_stack_error_handler + BL _tx_thread_interrupt_disable +_tx_misra_thread_stack_check_3: + LDR R1,[R5, #+0] + LDR R1,[R1, #-4] + CMP R1,#-269488145 + BEQ.N _tx_misra_thread_stack_check_0 + BL _tx_thread_interrupt_restore + MOVS R0,R4 + BL _tx_thread_stack_analyze + BL _tx_thread_interrupt_disable +_tx_misra_thread_stack_check_0: + BL _tx_thread_interrupt_restore + POP {R0,R4,R5,PC} // return + +#ifdef TX_ENABLE_EVENT_TRACE + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** VOID _tx_misra_trace_event_insert(ULONG event_id, */ +/** VOID *info_field_1, ULONG info_field_2, ULONG info_field_3, */ +/** ULONG info_field_4, ULONG filter, ULONG time_stamp); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_trace_event_insert: + PUSH {R3-R7,LR} + LDR.N R4,DataTable2_1 + LDR R4,[R4, #+0] + CMP R4,#+0 + BEQ.N _tx_misra_trace_event_insert_0 + LDR.N R5,DataTable2_2 + LDR R5,[R5, #+0] + LDR R6,[SP, #+28] + TST R5,R6 + BEQ.N _tx_misra_trace_event_insert_0 + LDR.N R5,DataTable2_3 + LDR R5,[R5, #+0] + LDR.N R6,DataTable2_4 + LDR R6,[R6, #+0] + CMP R5,#+0 + BNE.N _tx_misra_trace_event_insert_1 + LDR R5,[R6, #+44] + LDR R7,[R6, #+60] + LSLS R7,R7,#+16 + ORRS R7,R7,#0x80000000 + ORRS R5,R7,R5 + B.N _tx_misra_trace_event_insert_2 +_tx_misra_trace_event_insert_1: + CMP R5,#-252645136 + BCS.N _tx_misra_trace_event_insert_3 + MOVS R5,R6 + MOVS R6,#-1 + B.N _tx_misra_trace_event_insert_2 +_tx_misra_trace_event_insert_3: + MOVS R6,#-252645136 + MOVS R5,#+0 +_tx_misra_trace_event_insert_2: + STR R6,[R4, #+0] + STR R5,[R4, #+4] + STR R0,[R4, #+8] + LDR R0,[SP, #+32] + STR R0,[R4, #+12] + STR R1,[R4, #+16] + STR R2,[R4, #+20] + STR R3,[R4, #+24] + LDR R0,[SP, #+24] + STR R0,[R4, #+28] + ADDS R4,R4,#+32 + LDR.N R0,DataTable2_5 + LDR R0,[R0, #+0] + CMP R4,R0 + BCC.N _tx_misra_trace_event_insert_4 + LDR.N R0,DataTable2_6 + LDR R4,[R0, #+0] + LDR.N R0,DataTable2_1 + STR R4,[R0, #+0] + LDR.N R0,DataTable2_7 + LDR R0,[R0, #+0] + STR R4,[R0, #+32] + LDR.N R0,DataTable2_8 + LDR R0,[R0, #+0] + CMP R0,#+0 + BEQ.N _tx_misra_trace_event_insert_0 + LDR.N R0,DataTable2_7 + LDR R0,[R0, #+0] + LDR.N R1,DataTable2_8 + LDR R1,[R1, #+0] + BLX R1 + B.N _tx_misra_trace_event_insert_0 +_tx_misra_trace_event_insert_4: + LDR.N R0,DataTable2_1 + STR R4,[R0, #+0] + LDR.N R0,DataTable2_7 + LDR R0,[R0, #+0] + STR R4,[R0, #+32] +_tx_misra_trace_event_insert_0: + POP {R0,R4-R7,PC} // return + + + .data +DataTable2_1: + .word _tx_trace_buffer_current_ptr + + .data +DataTable2_2: + .word _tx_trace_event_enable_bits + + .data +DataTable2_5: + .word _tx_trace_buffer_end_ptr + + .data +DataTable2_6: + .word _tx_trace_buffer_start_ptr + + .data +DataTable2_7: + .word _tx_trace_header_ptr + + .data +DataTable2_8: + .word _tx_trace_full_notify_function + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ULONG _tx_misra_time_stamp_get(VOID); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_time_stamp_get: + MOVS R0,#+0 + BX LR // return + +#endif + + .data +DataTable2_3: + .word _tx_thread_system_state + + .data +DataTable2_4: + .word _tx_thread_current_ptr + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UINT _tx_misra_always_true(void); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_always_true: + MOVS R0,#+1 + BX LR // return + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** UINT _tx_misra_status_get(UINT status); */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + + .text + .thumb_func +_tx_misra_status_get: + MOVS R0,#+0 + BX LR // return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** ULONG _tx_misra_ipsr_get(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_ipsr_get: + MRS R0, IPSR + BX LR // return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** ULONG _tx_misra_control_get(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_control_get: + MRS R0, CONTROL + BX LR // return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** void _tx_misra_control_set(ULONG value); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_control_set: + MSR CONTROL, R0 + BX LR // return + + +#ifdef __ARM_FP + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** ULONG _tx_misra_fpccr_get(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_fpccr_get: + LDR r0, =0xE000EF34 // Build FPCCR address + LDR r0, [r0] // Load FPCCR value + BX LR // return + + +/***********************************************************************************************/ +/***********************************************************************************************/ +/** */ +/** void _tx_misra_vfp_touch(void); */ +/** */ +/***********************************************************************************************/ +/***********************************************************************************************/ + + .text + .thumb_func +_tx_misra_vfp_touch: + vmov.f32 s0, s0 + BX LR // return + +#endif + + + .data + .word 0 diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_restore.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_restore.S new file mode 100644 index 00000000000..af374956516 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_restore.S @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + .global _tx_execution_isr_exit +#endif + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_restore Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_exit] Execution profiling ISR exit */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs Interrupt Service Routines */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_restore(VOID) +// { + .global _tx_thread_context_restore + .thumb_func +_tx_thread_context_restore: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR exit function to indicate an ISR is complete. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_exit // Call the ISR exit function + POP {r0, lr} // Recover return address +#endif + + BX lr +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_save.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_save.S new file mode 100644 index 00000000000..0728d86e910 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_context_save.S @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_context_save Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is only needed for legacy applications and it should */ +/* not be called in any new development on a Cortex-M. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* [_tx_execution_isr_enter] Execution profiling ISR enter */ +/* */ +/* CALLED BY */ +/* */ +/* ISRs */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_context_save(VOID) +// { + .global _tx_thread_context_save + .thumb_func +_tx_thread_context_save: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the ISR enter function to indicate an ISR is starting. */ + PUSH {r0, lr} // Save return address + BL _tx_execution_isr_enter // Call the ISR enter function + POP {r0, lr} // Recover return address +#endif + + /* Context is already saved - just return. */ + + BX lr +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_control.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_control.S new file mode 100644 index 00000000000..38790a855dc --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_control.S @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_control Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for changing the interrupt lockout */ +/* posture of the system. */ +/* */ +/* INPUT */ +/* */ +/* new_posture New interrupt lockout posture */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_control(UINT new_posture) +// { + .global _tx_thread_interrupt_control + .thumb_func +_tx_thread_interrupt_control: +#ifdef TX_PORT_USE_BASEPRI + MRS r1, BASEPRI // Pickup current interrupt posture + MSR BASEPRI, r0 // Apply the new interrupt posture + MOV r0, r1 // Transfer old to return register +#else + MRS r1, PRIMASK // Pickup current interrupt lockout + MSR PRIMASK, r0 // Apply the new interrupt lockout + MOV r0, r1 // Transfer old to return register +#endif + BX lr // Return to caller +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_disable.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_disable.S new file mode 100644 index 00000000000..e0ae359abd8 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_disable.S @@ -0,0 +1,84 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_disable Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for disabling interrupts and returning */ +/* the previous interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* old_posture Old interrupt lockout posture */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// UINT _tx_thread_interrupt_disable(VOID) +// { + .global _tx_thread_interrupt_disable + .thumb_func +_tx_thread_interrupt_disable: + /* Return current interrupt lockout posture. */ +#ifdef TX_PORT_USE_BASEPRI + MRS r0, BASEPRI + LDR r1, =TX_PORT_BASEPRI + MSR BASEPRI, r1 +#else + MRS r0, PRIMASK + CPSID i +#endif + BX lr +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_restore.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_restore.S new file mode 100644 index 00000000000..32839c40511 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_interrupt_restore.S @@ -0,0 +1,81 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_interrupt_restore Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for restoring the previous */ +/* interrupt lockout posture. */ +/* */ +/* INPUT */ +/* */ +/* previous_posture Previous interrupt posture */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* Application Code */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_interrupt_restore(UINT previous_posture) +// { + .global _tx_thread_interrupt_restore + .thumb_func +_tx_thread_interrupt_restore: + /* Restore previous interrupt lockout posture. */ +#ifdef TX_PORT_USE_BASEPRI + MSR BASEPRI, r0 +#else + MSR PRIMASK, r0 +#endif + BX lr +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_schedule.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_schedule.S new file mode 100644 index 00000000000..8b283009cb9 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_schedule.S @@ -0,0 +1,330 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .global _tx_thread_current_ptr + .global _tx_thread_execute_ptr + .global _tx_timer_time_slice + .global _tx_execution_thread_enter + .global _tx_execution_thread_exit +#ifdef TX_LOW_POWER + .global tx_low_power_enter + .global tx_low_power_exit +#endif + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_schedule Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function waits for a thread control block pointer to appear in */ +/* the _tx_thread_execute_ptr variable. Once a thread pointer appears */ +/* in the variable, the corresponding thread is resumed. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* _tx_thread_system_return Return to system from thread */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Fixed predefined macro name, */ +/* resulting in version 6.1.10 */ +/* 04-25-2022 Scott Larson Added BASEPRI support, */ +/* resulting in version 6.1.11 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_schedule(VOID) +// { + .global _tx_thread_schedule + .thumb_func +_tx_thread_schedule: + + /* This function should only ever be called on Cortex-M + from the first schedule request. Subsequent scheduling occurs + from the PendSV handling routine below. */ + + /* Clear the preempt-disable flag to enable rescheduling after initialization on Cortex-M targets. */ + + MOV r0, #0 // Build value for TX_FALSE + LDR r2, =_tx_thread_preempt_disable // Build address of preempt disable flag + STR r0, [r2, #0] // Clear preempt disable flag + + /* Clear CONTROL.FPCA bit so VFP registers aren't unnecessarily stacked. */ + +#ifdef __ARM_FP + MRS r0, CONTROL // Pickup current CONTROL register + BIC r0, r0, #4 // Clear the FPCA bit + MSR CONTROL, r0 // Setup new CONTROL register +#endif + + /* Enable interrupts */ + CPSIE i + + /* Enter the scheduler for the first time. */ + + MOV r0, #0x10000000 // Load PENDSVSET bit + MOV r1, #0xE000E000 // Load NVIC base + STR r0, [r1, #0xD04] // Set PENDSVBIT in ICSR + DSB // Complete all memory accesses + ISB // Flush pipeline + + /* Wait here for the PendSV to take place. */ + +__tx_wait_here: + B __tx_wait_here // Wait for the PendSV to happen +// } + + /* Generic context switching PendSV handler. */ + + .global PendSV_Handler + .global __tx_PendSVHandler + .syntax unified + .thumb_func +PendSV_Handler: + .thumb_func +__tx_PendSVHandler: + + /* Get current thread value and new thread pointer. */ + +__tx_ts_handler: + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the thread exit function to indicate the thread is no longer executing. */ +#ifdef TX_PORT_USE_BASEPRI + LDR r1, =TX_PORT_BASEPRI // Mask interrupt priorities =< TX_PORT_BASEPRI + MSR BASEPRI, r1 +#else + CPSID i // Disable interrupts +#endif /* TX_PORT_USE_BASEPRI */ + PUSH {r0, lr} // Save LR (and r0 just for alignment) + BL _tx_execution_thread_exit // Call the thread exit function + POP {r0, lr} // Recover LR +#ifdef TX_PORT_USE_BASEPRI + MOV r0, 0 // Disable BASEPRI masking (enable interrupts) + MSR BASEPRI, r0 +#else + CPSIE i // Enable interrupts +#endif /* TX_PORT_USE_BASEPRI */ +#endif /* EXECUTION PROFILE */ + + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + MOV r3, #0 // Build NULL value + LDR r1, [r0] // Pickup current thread pointer + + /* Determine if there is a current thread to finish preserving. */ + + CBZ r1, __tx_ts_new // If NULL, skip preservation + + /* Recover PSP and preserve current thread context. */ + + STR r3, [r0] // Set _tx_thread_current_ptr to NULL + MRS r12, PSP // Pickup PSP pointer (thread's stack pointer) + STMDB r12!, {r4-r11} // Save its remaining registers +#ifdef __ARM_FP + TST LR, #0x10 // Determine if the VFP extended frame is present + BNE _skip_vfp_save + VSTMDB r12!,{s16-s31} // Yes, save additional VFP registers +_skip_vfp_save: +#endif + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + STMDB r12!, {LR} // Save LR on the stack + + /* Determine if time-slice is active. If it isn't, skip time handling processing. */ + + LDR r5, [r4] // Pickup current time-slice + STR r12, [r1, #8] // Save the thread stack pointer + CBZ r5, __tx_ts_new // If not active, skip processing + + /* Time-slice is active, save the current thread's time-slice and clear the global time-slice variable. */ + + STR r5, [r1, #24] // Save current time-slice + + /* Clear the global time-slice. */ + + STR r3, [r4] // Clear time-slice + + /* Executing thread is now completely preserved!!! */ + +__tx_ts_new: + + /* Now we are looking for a new thread to execute! */ + +#ifdef TX_PORT_USE_BASEPRI + LDR r1, =TX_PORT_BASEPRI // Mask interrupt priorities =< TX_PORT_BASEPRI + MSR BASEPRI, r1 +#else + CPSID i // Disable interrupts +#endif + LDR r1, [r2] // Is there another thread ready to execute? + CBZ r1, __tx_ts_wait // No, skip to the wait processing + + /* Yes, another thread is ready for else, make the current thread the new thread. */ + + STR r1, [r0] // Setup the current thread pointer to the new thread +#ifdef TX_PORT_USE_BASEPRI + MOV r4, #0 // Disable BASEPRI masking (enable interrupts) + MSR BASEPRI, r4 +#else + CPSIE i // Enable interrupts +#endif + + /* Increment the thread run count. */ + +__tx_ts_restore: + LDR r7, [r1, #4] // Pickup the current thread run count + LDR r4, =_tx_timer_time_slice // Build address of time-slice variable + LDR r5, [r1, #24] // Pickup thread's current time-slice + ADD r7, r7, #1 // Increment the thread run count + STR r7, [r1, #4] // Store the new run count + + /* Setup global time-slice with thread's current time-slice. */ + + STR r5, [r4] // Setup global time-slice + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + /* Call the thread entry function to indicate the thread is executing. */ + PUSH {r0, r1} // Save r0 and r1 + BL _tx_execution_thread_enter // Call the thread execution enter function + POP {r0, r1} // Recover r0 and r1 +#endif + + /* Restore the thread context and PSP. */ + + LDR r12, [r1, #8] // Pickup thread's stack pointer + LDMIA r12!, {LR} // Pickup LR +#ifdef __ARM_FP + TST LR, #0x10 // Determine if the VFP extended frame is present + BNE _skip_vfp_restore // If not, skip VFP restore + VLDMIA r12!, {s16-s31} // Yes, restore additional VFP registers +_skip_vfp_restore: +#endif + LDMIA r12!, {r4-r11} // Recover thread's registers + MSR PSP, r12 // Setup the thread's stack pointer + + /* Return to thread. */ + + BX lr // Return to thread! + + /* The following is the idle wait processing... in this case, no threads are ready for execution and the + system will simply be idle until an interrupt occurs that makes a thread ready. Note that interrupts + are disabled to allow use of WFI for waiting for a thread to arrive. */ + +__tx_ts_wait: +#ifdef TX_PORT_USE_BASEPRI + LDR r1, =TX_PORT_BASEPRI // Mask interrupt priorities =< TX_PORT_BASEPRI + MSR BASEPRI, r1 +#else + CPSID i // Disable interrupts +#endif + LDR r1, [r2] // Pickup the next thread to execute pointer + STR r1, [r0] // Store it in the current pointer + CBNZ r1, __tx_ts_ready // If non-NULL, a new thread is ready! + +#ifdef TX_LOW_POWER + PUSH {r0-r3} + BL tx_low_power_enter // Possibly enter low power mode + POP {r0-r3} +#endif + +#ifdef TX_ENABLE_WFI + DSB // Ensure no outstanding memory transactions + WFI // Wait for interrupt + ISB // Ensure pipeline is flushed +#endif + +#ifdef TX_LOW_POWER + PUSH {r0-r3} + BL tx_low_power_exit // Exit low power mode + POP {r0-r3} +#endif + +#ifdef TX_PORT_USE_BASEPRI + MOV r4, #0 // Disable BASEPRI masking (enable interrupts) + MSR BASEPRI, r4 +#else + CPSIE i // Enable interrupts +#endif + B __tx_ts_wait // Loop to continue waiting + + /* At this point, we have a new thread ready to go. Clear any newly pended PendSV - since we are + already in the handler! */ + +__tx_ts_ready: + MOV r7, #0x08000000 // Build clear PendSV value + MOV r8, #0xE000E000 // Build base NVIC address + STR r7, [r8, #0xD04] // Clear any PendSV + + /* Re-enable interrupts and restore new thread. */ +#ifdef TX_PORT_USE_BASEPRI + MOV r4, #0 // Disable BASEPRI masking (enable interrupts) + MSR BASEPRI, r4 +#else + CPSIE i // Enable interrupts +#endif + B __tx_ts_restore // Restore the thread +// } + +#ifdef __ARM_FP + + .global tx_thread_fpu_enable + .thumb_func +tx_thread_fpu_enable: + .global tx_thread_fpu_disable + .thumb_func +tx_thread_fpu_disable: + + /* Automatic VPF logic is supported, this function is present only for + backward compatibility purposes and therefore simply returns. */ + + BX LR // Return to caller + +#endif diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_stack_build.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_stack_build.S new file mode 100644 index 00000000000..c62ccf30584 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_stack_build.S @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_stack_build Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function builds a stack frame on the supplied thread's stack. */ +/* The stack frame results in a fake interrupt return to the supplied */ +/* function pointer. */ +/* */ +/* INPUT */ +/* */ +/* thread_ptr Pointer to thread control blk */ +/* function_ptr Pointer to return function */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_thread_create Create thread service */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID)) +// { + .global _tx_thread_stack_build + .thumb_func +_tx_thread_stack_build: + + /* Build a fake interrupt frame. The form of the fake interrupt stack + on the Cortex-M should look like the following after it is built: + + Stack Top: + LR Interrupted LR (LR at time of PENDSV) + r4 Initial value for r4 + r5 Initial value for r5 + r6 Initial value for r6 + r7 Initial value for r7 + r8 Initial value for r8 + r9 Initial value for r9 + r10 Initial value for r10 + r11 Initial value for r11 + r0 Initial value for r0 (Hardware stack starts here!!) + r1 Initial value for r1 + r2 Initial value for r2 + r3 Initial value for r3 + r12 Initial value for r12 + lr Initial value for lr + pc Initial value for pc + xPSR Initial value for xPSR + + Stack Bottom: (higher memory address) */ + + LDR r2, [r0, #16] // Pickup end of stack area + BIC r2, r2, #0x7 // Align frame for 8-byte alignment + SUB r2, r2, #68 // Subtract frame size + LDR r3, =0xFFFFFFFD // Build initial LR value + STR r3, [r2, #0] // Save on the stack + + /* Actually build the stack frame. */ + + MOV r3, #0 // Build initial register value + STR r3, [r2, #4] // Store initial r4 + STR r3, [r2, #8] // Store initial r5 + STR r3, [r2, #12] // Store initial r6 + STR r3, [r2, #16] // Store initial r7 + STR r3, [r2, #20] // Store initial r8 + STR r3, [r2, #24] // Store initial r9 + STR r3, [r2, #28] // Store initial r10 + STR r3, [r2, #32] // Store initial r11 + + /* Hardware stack follows. */ + + STR r3, [r2, #36] // Store initial r0 + STR r3, [r2, #40] // Store initial r1 + STR r3, [r2, #44] // Store initial r2 + STR r3, [r2, #48] // Store initial r3 + STR r3, [r2, #52] // Store initial r12 + MOV r3, #0xFFFFFFFF // Poison EXC_RETURN value + STR r3, [r2, #56] // Store initial lr + STR r1, [r2, #60] // Store initial pc + MOV r3, #0x01000000 // Only T-bit need be set + STR r3, [r2, #64] // Store initial xPSR + + /* Setup stack pointer. */ + // thread_ptr -> tx_thread_stack_ptr = r2; + + STR r2, [r0, #8] // Save stack pointer in thread's + // control block + BX lr // Return to caller +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_system_return.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_system_return.S new file mode 100644 index 00000000000..234a2121fb9 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_thread_system_return.S @@ -0,0 +1,98 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Thread */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .text 32 + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_thread_system_return Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is target processor specific. It is used to transfer */ +/* control from a thread back to the ThreadX system. Only a */ +/* minimal context is saved since the compiler assumes temp registers */ +/* are going to get slicked by a function call anyway. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_thread_schedule Thread scheduling loop */ +/* */ +/* CALLED BY */ +/* */ +/* ThreadX components */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_thread_system_return(VOID) +// { + .thumb_func + .global _tx_thread_system_return +_tx_thread_system_return: + + /* Return to real scheduler via PendSV. Note that this routine is often + replaced with in-line assembly in tx_port.h to improved performance. */ + + MOV r0, #0x10000000 // Load PENDSVSET bit + MOV r1, #0xE000E000 // Load NVIC base + STR r0, [r1, #0xD04] // Set PENDSVBIT in ICSR + MRS r0, IPSR // Pickup IPSR + CMP r0, #0 // Is it a thread returning? + BNE _isr_context // If ISR, skip interrupt enable +#ifdef TX_PORT_USE_BASEPRI + MRS r1, BASEPRI // Thread context returning, pickup BASEPRI + MOV r0, #0 + MSR BASEPRI, r0 // Enable interrupts + MSR BASEPRI, r1 // Restore original interrupt posture +#else + MRS r1, PRIMASK // Thread context returning, pickup PRIMASK + CPSIE i // Enable interrupts + MSR PRIMASK, r1 // Restore original interrupt posture +#endif +_isr_context: + BX lr // Return to caller +// } diff --git a/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_timer_interrupt.S b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_timer_interrupt.S new file mode 100644 index 00000000000..4d0c8003e11 --- /dev/null +++ b/platforms/stm32/3rdparty/threadx/ports/cortex_m4/gnu/src/tx_timer_interrupt.S @@ -0,0 +1,260 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Timer */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + + .global _tx_timer_time_slice + .global _tx_timer_system_clock + .global _tx_timer_current_ptr + .global _tx_timer_list_start + .global _tx_timer_list_end + .global _tx_timer_expired_time_slice + .global _tx_timer_expired + .global _tx_thread_time_slice + .global _tx_timer_expiration_process + + .text + .align 4 + .syntax unified +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_timer_interrupt Cortex-M4/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function processes the hardware timer interrupt. This */ +/* processing includes incrementing the system clock and checking for */ +/* time slice and/or timer expiration. If either is found, the */ +/* expiration functions are called. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* _tx_timer_expiration_process Timer expiration processing */ +/* _tx_thread_time_slice Time slice interrupted thread */ +/* */ +/* CALLED BY */ +/* */ +/* interrupt vector */ +/* */ +/* RELEASE HISTORY */ +/* */ +/* DATE NAME DESCRIPTION */ +/* */ +/* 06-02-2021 Scott Larson Initial Version 6.1.7 */ +/* 01-31-2022 Scott Larson Modified comment(s), added */ +/* TX_NO_TIMER support, */ +/* resulting in version 6.1.10 */ +/* 03-08-2023 Scott Larson Include tx_user.h, */ +/* resulting in version 6.2.1 */ +/* */ +/**************************************************************************/ +// VOID _tx_timer_interrupt(VOID) +// { +#ifndef TX_NO_TIMER + .global _tx_timer_interrupt + .thumb_func +_tx_timer_interrupt: + + /* Upon entry to this routine, it is assumed that the compiler scratch registers are available + for use. */ + + /* Increment the system clock. */ + // _tx_timer_system_clock++; + + LDR r1, =_tx_timer_system_clock // Pickup address of system clock + LDR r0, [r1, #0] // Pickup system clock + ADD r0, r0, #1 // Increment system clock + STR r0, [r1, #0] // Store new system clock + + /* Test for time-slice expiration. */ + // if (_tx_timer_time_slice) + // { + + LDR r3, =_tx_timer_time_slice // Pickup address of time-slice + LDR r2, [r3, #0] // Pickup time-slice + CBZ r2, __tx_timer_no_time_slice // Is it non-active? + // Yes, skip time-slice processing + + /* Decrement the time_slice. */ + // _tx_timer_time_slice--; + + SUB r2, r2, #1 // Decrement the time-slice + STR r2, [r3, #0] // Store new time-slice value + + /* Check for expiration. */ + // if (__tx_timer_time_slice == 0) + + CBNZ r2, __tx_timer_no_time_slice // Has it expired? + // No, skip expiration processing + + /* Set the time-slice expired flag. */ + // _tx_timer_expired_time_slice = TX_TRUE; + + LDR r3, =_tx_timer_expired_time_slice // Pickup address of expired flag + MOV r0, #1 // Build expired value + STR r0, [r3, #0] // Set time-slice expiration flag + + // } + +__tx_timer_no_time_slice: + + /* Test for timer expiration. */ + // if (*_tx_timer_current_ptr) + // { + + LDR r1, =_tx_timer_current_ptr // Pickup current timer pointer address + LDR r0, [r1, #0] // Pickup current timer + LDR r2, [r0, #0] // Pickup timer list entry + CBZ r2, __tx_timer_no_timer // Is there anything in the list? + // No, just increment the timer + + /* Set expiration flag. */ + // _tx_timer_expired = TX_TRUE; + + LDR r3, =_tx_timer_expired // Pickup expiration flag address + MOV r2, #1 // Build expired value + STR r2, [r3, #0] // Set expired flag + B __tx_timer_done // Finished timer processing + + // } + // else + // { +__tx_timer_no_timer: + + /* No timer expired, increment the timer pointer. */ + // _tx_timer_current_ptr++; + + ADD r0, r0, #4 // Move to next timer + + /* Check for wrap-around. */ + // if (_tx_timer_current_ptr == _tx_timer_list_end) + + LDR r3, =_tx_timer_list_end // Pickup addr of timer list end + LDR r2, [r3, #0] // Pickup list end + CMP r0, r2 // Are we at list end? + BNE __tx_timer_skip_wrap // No, skip wrap-around logic + + /* Wrap to beginning of list. */ + // _tx_timer_current_ptr = _tx_timer_list_start; + + LDR r3, =_tx_timer_list_start // Pickup addr of timer list start + LDR r0, [r3, #0] // Set current pointer to list start + +__tx_timer_skip_wrap: + + STR r0, [r1, #0] // Store new current timer pointer + // } + +__tx_timer_done: + + /* See if anything has expired. */ + // if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of expired flag + LDR r2, [r3, #0] // Pickup time-slice expired flag + CBNZ r2, __tx_something_expired // Did a time-slice expire? + // If non-zero, time-slice expired + LDR r1, =_tx_timer_expired // Pickup addr of other expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CBZ r0, __tx_timer_nothing_expired // Did a timer expire? + // No, nothing expired + +__tx_something_expired: + + STMDB sp!, {r0, lr} // Save the lr register on the stack + // and save r0 just to keep 8-byte alignment + + /* Did a timer expire? */ + // if (_tx_timer_expired) + // { + + LDR r1, =_tx_timer_expired // Pickup addr of expired flag + LDR r0, [r1, #0] // Pickup timer expired flag + CBZ r0, __tx_timer_dont_activate // Check for timer expiration + // If not set, skip timer activation + + /* Process timer expiration. */ + // _tx_timer_expiration_process(); + + BL _tx_timer_expiration_process // Call the timer expiration handling routine + + // } +__tx_timer_dont_activate: + + /* Did time slice expire? */ + // if (_tx_timer_expired_time_slice) + // { + + LDR r3, =_tx_timer_expired_time_slice // Pickup addr of time-slice expired + LDR r2, [r3, #0] // Pickup the actual flag + CBZ r2, __tx_timer_not_ts_expiration // See if the flag is set + // No, skip time-slice processing + + /* Time slice interrupted thread. */ + // _tx_thread_time_slice(); + + BL _tx_thread_time_slice // Call time-slice processing + LDR r0, =_tx_thread_preempt_disable // Build address of preempt disable flag + LDR r1, [r0] // Is the preempt disable flag set? + CBNZ r1, __tx_timer_skip_time_slice // Yes, skip the PendSV logic + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r1, [r0] // Pickup the current thread pointer + LDR r2, =_tx_thread_execute_ptr // Build execute thread pointer address + LDR r3, [r2] // Pickup the execute thread pointer + LDR r0, =0xE000ED04 // Build address of control register + LDR r2, =0x10000000 // Build value for PendSV bit + CMP r1, r3 // Are they the same? + BEQ __tx_timer_skip_time_slice // If the same, there was no time-slice performed + STR r2, [r0] // Not the same, issue the PendSV for preemption +__tx_timer_skip_time_slice: + + // } + +__tx_timer_not_ts_expiration: + + LDMIA sp!, {r0, lr} // Recover lr register (r0 is just there for + // the 8-byte stack alignment + + // } + +__tx_timer_nothing_expired: + + DSB // Complete all memory access + BX lr // Return to caller +// } +#endif diff --git a/platforms/stm32/CMakeLists.txt b/platforms/stm32/CMakeLists.txt index b3bc142a269..b598ec87b14 100644 --- a/platforms/stm32/CMakeLists.txt +++ b/platforms/stm32/CMakeLists.txt @@ -19,6 +19,8 @@ if (NOT BUILD_EXECUTABLE STREQUAL "unitTest") # 3rdparty modules - RTOS if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") add_subdirectory(3rdparty/freertos_cm4_sysTick) + elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_subdirectory(3rdparty/threadx) endif () add_subdirectory(bsp) From 0b811b11b4ea4a72d8873fda4c4a6926e91bf627 Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Tue, 2 Jun 2026 08:47:34 +0200 Subject: [PATCH 09/11] Add STM32 F413ZH bxCAN board config Add the NUCLEO-F413ZH reference application board: BSP configuration, board main with FreeRTOS and ThreadX variants, bxCAN CAN system, and the board presets. --- CMakePresets.json | 52 +++++++ .../platforms/nucleo_f413zh/CMakeLists.txt | 15 ++ .../platforms/nucleo_f413zh/Options.cmake | 49 ++++++ .../bspConfiguration/CMakeLists.txt | 9 ++ .../bspConfiguration/doc/index.rst | 57 +++++++ .../bspConfiguration/doc/user/bspStdIo.rst | 34 +++++ .../bspConfiguration/doc/user/bspUart.rst | 50 ++++++ .../bspConfiguration/doc/user/index.rst | 32 ++++ .../include/bsp/uart/UartConfig.h | 28 ++++ .../bspConfiguration/module.spec | 2 + .../bspConfiguration/src/bsp/stdIo/stdIo.cpp | 29 ++++ .../src/bsp/uart/UartConfig.cpp | 50 ++++++ .../freeRtosCoreConfiguration/CMakeLists.txt | 3 + .../include/os/FreeRtosPlatformConfig.h | 122 +++++++++++++++ .../freeRtosCoreConfiguration/module.spec | 1 + .../nucleo_f413zh/main/CMakeLists.txt | 71 +++++++++ .../nucleo_f413zh/main/doc/index.rst | 40 +++++ .../nucleo_f413zh/main/doc/user/StaticBsp.rst | 24 +++ .../nucleo_f413zh/main/doc/user/index.rst | 32 ++++ .../nucleo_f413zh/main/doc/user/main.rst | 40 +++++ .../nucleo_f413zh/main/doc/user/startup.rst | 54 +++++++ .../main/doc/user/systems/CanSystem.rst | 77 ++++++++++ .../main/include/lifecycle/StaticBsp.h | 24 +++ .../main/include/systems/CanSystem.h | 67 +++++++++ .../main/linkerscript/application.ld | 129 ++++++++++++++++ .../platforms/nucleo_f413zh/main/module.spec | 1 + .../src/bsp/threadx/tx_execution_initialize.c | 17 +++ .../src/bsp/threadx/tx_initialize_low_level.S | 96 ++++++++++++ .../main/src/lifecycle/StaticBsp.cpp | 25 +++ .../platforms/nucleo_f413zh/main/src/main.cpp | 112 ++++++++++++++ .../nucleo_f413zh/main/src/os/isr/isr_can.cpp | 19 +++ .../nucleo_f413zh/main/src/os/isr/isr_sys.cpp | 29 ++++ .../main/src/osHooks/freertos/osHooks.cpp | 25 +++ .../main/src/osHooks/threadx/osHooks.cpp | 62 ++++++++ .../main/src/systems/CanSystem.cpp | 142 ++++++++++++++++++ .../nucleo_f413zh/safety/CMakeLists.txt | 1 + .../nucleo_f413zh/safety/module.spec | 1 + .../safety/safeLifecycle/CMakeLists.txt | 6 + .../include/safeLifecycle/SafetyManager.h | 32 ++++ .../safeLifecycle/src/SafetyManager.cpp | 63 ++++++++ .../threadXCoreConfiguration/CMakeLists.txt | 3 + .../include/tx_user.h | 50 ++++++ .../threadXCoreConfiguration/module.spec | 1 + .../bspConfiguration/module.spec | 1 + 44 files changed, 1777 insertions(+) create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/Options.cmake create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/index.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspStdIo.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspUart.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/index.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/include/bsp/uart/UartConfig.h create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/module.spec create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/stdIo/stdIo.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/uart/UartConfig.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/module.spec create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/doc/index.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/StaticBsp.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/index.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/main.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/startup.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/systems/CanSystem.rst create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/include/lifecycle/StaticBsp.h create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/include/systems/CanSystem.h create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/linkerscript/application.ld create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/module.spec create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_execution_initialize.c create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_initialize_low_level.S create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/src/lifecycle/StaticBsp.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/src/main.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_can.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_sys.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/freertos/osHooks.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/threadx/osHooks.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/main/src/systems/CanSystem.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/safety/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/safety/module.spec create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/src/SafetyManager.cpp create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/CMakeLists.txt create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/include/tx_user.h create mode 100644 executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/module.spec diff --git a/CMakePresets.json b/CMakePresets.json index a3a1fcf6cb2..e94879caf9a 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -127,6 +127,46 @@ "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" } }, + { + "name": "nucleo-f413zh-freertos-gcc", + "generator": "Ninja Multi-Config", + "displayName": "NUCLEO-F413ZH FreeRTOS configuration (GCC)", + "description": "Configure for NUCLEO-F413ZH platform using ARM GCC toolchain", + "inherits": "_config-base", + "toolchainFile": "${sourceDir}/cmake/toolchains/ArmNoneEabi-gcc.cmake", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "BUILD_EXECUTABLE": "referenceApp", + "BUILD_TARGET_PLATFORM": "NUCLEO_F413ZH", + "BUILD_TARGET_RTOS": "FREERTOS", + "STM32_CHIP": "STM32F413ZH", + "CMAKE_ASM_FLAGS_RELWITHDEBINFO": "-g3", + "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo;Debug;Release", + "CMAKE_C_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "nucleo-f413zh-threadx-gcc", + "generator": "Ninja Multi-Config", + "displayName": "NUCLEO-F413ZH ThreadX configuration (GCC)", + "description": "Configure for NUCLEO-F413ZH platform using ARM GCC toolchain", + "inherits": "_config-base", + "toolchainFile": "${sourceDir}/cmake/toolchains/ArmNoneEabi-gcc.cmake", + "binaryDir": "${sourceDir}/build/${presetName}", + "cacheVariables": { + "BUILD_EXECUTABLE": "referenceApp", + "BUILD_TARGET_PLATFORM": "NUCLEO_F413ZH", + "BUILD_TARGET_RTOS": "THREADX", + "STM32_CHIP": "STM32F413ZH", + "CMAKE_ASM_FLAGS_RELWITHDEBINFO": "-g3", + "CMAKE_CONFIGURATION_TYPES": "RelWithDebInfo;Debug;Release", + "CMAKE_C_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_CXX_FLAGS_RELWITHDEBINFO": "-g3 -O2 -DNDEBUG", + "CMAKE_DEFAULT_BUILD_TYPE": "RelWithDebInfo" + } + }, { "name": "posix-freertos", "displayName": "POSIX-FREERTOS compliant configuration", @@ -320,6 +360,18 @@ "description": "Build reference application for NUCLEO-G474RE platform with GCC", "configurePreset": "nucleo-g474re-threadx-gcc" }, + { + "name": "nucleo-f413zh-freertos-gcc", + "displayName": "NUCLEO-F413ZH FreeRTOS build (GCC)", + "description": "Build reference application for NUCLEO-F413ZH platform with GCC", + "configurePreset": "nucleo-f413zh-freertos-gcc" + }, + { + "name": "nucleo-f413zh-threadx-gcc", + "displayName": "NUCLEO-F413ZH ThreadX build (GCC)", + "description": "Build reference application for NUCLEO-F413ZH platform with GCC", + "configurePreset": "nucleo-f413zh-threadx-gcc" + }, { "name": "posix-freertos", "displayName": "build POSIX-FREERTOS", diff --git a/executables/referenceApp/platforms/nucleo_f413zh/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/CMakeLists.txt new file mode 100644 index 00000000000..6976e4fbde5 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/CMakeLists.txt @@ -0,0 +1,15 @@ +# NUCLEO-F413ZH platform for referenceApp + +add_subdirectory(bspConfiguration) +add_subdirectory(safety) +add_subdirectory(main) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_subdirectory(freeRtosCoreConfiguration) + add_library(freeRtosPort ALIAS freeRtosCm4SysTickPort) + add_library(freeRtosPortImpl ALIAS freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_subdirectory(threadXCoreConfiguration) + add_library(threadXPort ALIAS threadXCortexM4Port) + add_library(threadXPortImpl ALIAS threadXCortexM4) +endif () diff --git a/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake b/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake new file mode 100644 index 00000000000..0046fbb85ca --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake @@ -0,0 +1,49 @@ +# ******************************************************************************* +# Copyright (c) 2026 An Dao +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +# NUCLEO-F413ZH platform options + +set(OPENBSW_PLATFORM + "stm32" + CACHE STRING "" FORCE) +set(STM32_CHIP + "STM32F413ZH" + CACHE STRING "" FORCE) +set(BUILD_TARGET_RTOS + "FREERTOS" + CACHE STRING "Target RTOS: FREERTOS or THREADX") + +set(PLATFORM_SUPPORT_CAN + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_IO + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_ETHERNET + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_TRANSPORT + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_UDS + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_WATCHDOG + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_MPU + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_STORAGE + OFF + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_ROM_CHECK + OFF + CACHE BOOL "" FORCE) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..5cb06edc3e1 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/CMakeLists.txt @@ -0,0 +1,9 @@ +add_library(bspConfiguration src/bsp/stdIo/stdIo.cpp + src/bsp/uart/UartConfig.cpp) + +target_include_directories(bspConfiguration PUBLIC include) + +target_link_libraries( + bspConfiguration + PUBLIC bspUart + PRIVATE bspMcu platform) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/index.rst b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/index.rst new file mode 100644 index 00000000000..d51c3c4a000 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/index.rst @@ -0,0 +1,57 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspconfig_nucleo_f413zh: + +bspConfiguration - NUCLEO-F413ZH +================================ + +Overview +-------- + +The ``bspConfiguration`` module contains hardware-specific configuration for the +NUCLEO-F413ZH evaluation board. Driver logic is separated from its configuration +so that users can customise a project for different boards or pin assignments +without modifying driver source code. + +The STM32F413ZH platform exposes a minimal set of BSP peripherals required for +the CAN reference application: + +- **bspUart** - USART3 configuration (ST-LINK Virtual COM Port on PD8/PD9). +- **bspStdIo** - Standard I/O bridge that routes ``putByteToStdout`` / + ``getByteFromStdin`` through the configured UART instance. + +.. note:: + + Unlike the S32K148EVB reference application, this platform does not include + ADC, PWM, or digital I/O configuration modules because the CAN reference + application does not require analogue inputs or GPIO-driven outputs. + These modules can be added following the same configuration pattern if + a future application requires them. + +Hardware Summary +++++++++++++++++ + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + "MCU", "STM32F413ZH (Arm Cortex-M4F, 96 MHz, single-precision FPU)" + "UART peripheral", "USART3 - routed to ST-LINK/V2-1 Virtual COM Port" + "UART TX pin", "PD8 (AF7)" + "UART RX pin", "PD9 (AF7)" + "UART baud rate", "115 200 baud (BRR = 417 at 48 MHz APB1)" + "CAN peripheral", "CAN1 (bxCAN, configured in CanSystem, not bspConfiguration)" + +.. toctree:: + :hidden: + + user/index diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspStdIo.rst b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspStdIo.rst new file mode 100644 index 00000000000..3805fb8f22a --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspStdIo.rst @@ -0,0 +1,34 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspConfig_StdIo_F413ZH: + +bspStdIo +======== + +Overview +-------- + +The standard I/O bridge implements the ``putByteToStdout`` and +``getByteFromStdin`` C-linkage functions required by the OpenBSW logging +framework, routing character-level I/O through the UART ``TERMINAL`` instance +configured in :ref:`bspConfig_Uart_F413ZH`. + +``putByteToStdout(uint8_t byte)`` transmits one byte over USART3 TX (PD8), +blocking by polling until the UART TX register is free. + +``getByteFromStdin()`` attempts to read one byte from USART3 RX (PD9) and is +non-blocking: if ``Uart::read()`` reports that no byte was received, it returns +``-1`` (POSIX ``getchar()`` convention); otherwise it returns the byte value +(0-255). + +Both functions resolve the UART instance once into a ``static`` local +reference, avoiding a global constructor ordering dependency. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspUart.rst b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspUart.rst new file mode 100644 index 00000000000..9841cd00aed --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/bspUart.rst @@ -0,0 +1,50 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _bspConfig_Uart_F413ZH: + +bspUart Configuration +===================== + +Overview +-------- + +The UART configuration defines the hardware parameters for the on-board +ST-LINK Virtual COM Port of the NUCLEO-F413ZH. A single UART instance +(``TERMINAL``) is configured for debug logging and interactive console use. + +``UartConfig.h`` declares the ``Uart::Id`` enumeration of available UART +instances, and ``UartConfig.cpp`` maps each ``Uart::Id`` to its hardware +parameters and provides the singleton accessor +``bsp::Uart::getInstance(Uart::Id)``. + +Configuration +------------- + +.. csv-table:: + :header: "Parameter", "Value" + :widths: 30, 70 + :width: 100% + + "Peripheral", "USART3 (APB1, 48 MHz = 96 MHz SYSCLK / 2)" + "TX pin", "PD8, alternate function AF7" + "RX pin", "PD9, alternate function AF7" + "Baud rate", "115 200 baud" + "BRR", "48 000 000 / 115 200 = 416.67 -> 417" + +The resulting actual baud rate is 48 MHz / 417 = 115 108 baud (0.080 % error), +well within the UART tolerance. + +.. note:: + + The UART driver uses polling mode (no DMA, no interrupts). For high- + throughput or latency-sensitive applications, consider extending the + ``bspUart`` BSP module with an interrupt-driven or DMA-backed backend. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/index.rst b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/index.rst new file mode 100644 index 00000000000..e2d54e3e7f5 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/doc/user/index.rst @@ -0,0 +1,32 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +User Documentation +================== + +Detailed documentation for each BSP configuration module on the NUCLEO-F413ZH +platform. + +Modules +------- + +.. toctree:: + :hidden: + + bspUart + bspStdIo + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + :ref:`bspConfig_Uart_F413ZH`, "USART3 pin mapping, baud-rate register value, and clock-enable bits" + :ref:`bspConfig_StdIo_F413ZH`, "Standard I/O bridge routing printf / scanf to the UART terminal" diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/include/bsp/uart/UartConfig.h b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/include/bsp/uart/UartConfig.h new file mode 100644 index 00000000000..b1f23bee3c4 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/include/bsp/uart/UartConfig.h @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "bsp/Uart.h" + +#include + +namespace bsp +{ + +enum class Uart::Id : size_t +{ + TERMINAL, + INVALID, +}; + +static constexpr size_t NUMBER_OF_UARTS = static_cast(Uart::Id::INVALID); + +} // namespace bsp diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/module.spec new file mode 100644 index 00000000000..c918431ceaa --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/module.spec @@ -0,0 +1,2 @@ +unit_test: false +oss: true diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/stdIo/stdIo.cpp b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/stdIo/stdIo.cpp new file mode 100644 index 00000000000..d6b4b5f14fb --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/stdIo/stdIo.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "bsp/uart/UartConfig.h" +#include "platform/estdint.h" + +extern "C" void putByteToStdout(uint8_t const byte) +{ + static bsp::Uart& uart = bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL); + uart.write(etl::span(&byte, 1U)); +} + +extern "C" int32_t getByteFromStdin() +{ + static bsp::Uart& uart = bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL); + uint8_t dataByte = 0; + if (uart.read(etl::span(&dataByte, 1U)) == 0U) + { + return -1; + } + return dataByte; +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/uart/UartConfig.cpp b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/uart/UartConfig.cpp new file mode 100644 index 00000000000..fd9acabe943 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/bspConfiguration/src/bsp/uart/UartConfig.cpp @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +// NUCLEO-F413ZH: USART3 on PD8 (TX) / PD9 (RX), AF7 +// ST-LINK VCP, 115200 baud @ 48 MHz APB1 + +#include +#include +#include + +namespace bsp +{ + +Uart::UartConfig const Uart::_uartConfigs[] = { + { + USART3, // usart + GPIOD, // gpioPort + 8U, // txPin (PD8) + 9U, // rxPin (PD9) + 7U, // af (AF7) + 417U, // brr (48000000 / 115200 = 416.67 -> 417) + RCC_AHB1ENR_GPIODEN, // rccGpioEnBit + RCC_APB1ENR_USART3EN, // rccUsartEnBit + &RCC->AHB1ENR, // rccGpioEnReg + &RCC->APB1ENR, // rccUsartEnReg + }, +}; + +static Uart instances[] = { + Uart(Uart::Id::TERMINAL), +}; + +Uart& Uart::getInstance(Id id) +{ + ETL_ASSERT( + id < Id::INVALID, ETL_ERROR_GENERIC("UartId::INVALID is not a valid Uart identifier")); + static_assert( + NUMBER_OF_UARTS == static_cast(etl::size(instances)), + "Not enough Uart instances defined"); + return instances[static_cast(id)]; +} + +} // namespace bsp diff --git a/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..1e859851fac --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(freeRtosCoreConfiguration INTERFACE) +target_include_directories(freeRtosCoreConfiguration INTERFACE include) +target_link_libraries(freeRtosCoreConfiguration INTERFACE bsp bspMcu) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h new file mode 100644 index 00000000000..6f2786eb840 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/include/os/FreeRtosPlatformConfig.h @@ -0,0 +1,122 @@ +// Copyright 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +// FreeRTOS device-specific configuration for NUCLEO-F413ZH. +// Included by asyncFreeRtos/freeRtosConfiguration/FreeRTOSConfig.h. + +#pragma once + +#include "bsp/SystemTime.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define configCPU_CLOCK_HZ (96000000UL) + +#undef configMINIMAL_STACK_SIZE +#define configMINIMAL_STACK_SIZE ((unsigned short)512) +#define configMAX_TASK_NAME_LEN (10) +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 30 +#undef configCHECK_FOR_STACK_OVERFLOW +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configRECORD_STACK_HIGH_ADDRESS 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_MALLOC_FAILED_HOOK 0 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_FPU 0 +#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 + +/* Memory allocation */ +#define configTOTAL_HEAP_SIZE 512 +#define configAPPLICATION_ALLOCATED_HEAP 1 + +/* Co-routine definitions */ +#define configMAX_CO_ROUTINE_PRIORITIES (0) + +/* Software timer definitions */ +#undef configTIMER_QUEUE_LENGTH +#define configTIMER_QUEUE_LENGTH 30 +#undef configTIMER_TASK_STACK_DEPTH +#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE) + +/* API function includes */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vResumeFromISR 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_eTaskGetState 1 +#undef INCLUDE_uxTaskGetStackHighWaterMark +#define INCLUDE_uxTaskGetStackHighWaterMark 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xQueueGetMutexHolder 1 +#undef INCLUDE_xTaskGetCurrentTaskHandle +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#undef INCLUDE_xTaskGetIdleTaskHandle +#define INCLUDE_xTaskGetIdleTaskHandle 1 +#define INCLUDE_pcTaskGetTaskName 1 +#define INCLUDE_xEventGroupSetBitFromISR 1 +#undef INCLUDE_xTimerPendFunctionCall +#define INCLUDE_xTimerPendFunctionCall 1 + +/* Cortex-M specific definitions */ +#ifdef __NVIC_PRIO_BITS +#define configPRIO_BITS __NVIC_PRIO_BITS +#else +#define configPRIO_BITS 4 +#endif + +#define configLIBRARY_LOWEST_INTERRUPT_PRIORITY 0x0F +// ISRs that call FreeRTOS FromISR APIs must run at numeric priority >= this value. +#define configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY 0x05 + +#ifndef configKERNEL_INTERRUPT_PRIORITY +#define configKERNEL_INTERRUPT_PRIORITY \ + (configLIBRARY_LOWEST_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#endif + +#ifndef configMAX_SYSCALL_INTERRUPT_PRIORITY +#define configMAX_SYSCALL_INTERRUPT_PRIORITY \ + (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS)) +#endif + +/* Assert */ +#define DEV_ASSERT(x) +#if defined(__GNUC__) && (!defined(__ASSEMBLER__)) +#include "mcu/mcu.h" +#endif +#define configASSERT(x) DEV_ASSERT(x) + +/* Override tick hook to feed IWDG (may be enabled from previous firmware) */ +#undef configUSE_TICK_HOOK +#define configUSE_TICK_HOOK 1 + +/* Tickless idle */ +#define configUSE_TICKLESS_IDLE 0 +#define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 +#define configUSE_TICKLESS_IDLE_DECISION_HOOK 0 + +/* Map FreeRTOS port handlers to CMSIS standard names */ +#define vPortSVCHandler SVC_Handler +#define xPortPendSVHandler PendSV_Handler +#define xPortSysTickHandler SysTick_Handler + +/* Run-time stats */ +#define configGENERATE_RUN_TIME_STATS (1) +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() getSystemTimeUs32Bit() + +#define configTIMER_SERVICE_TASK_NAME "TIMER_OS" + +#ifdef __cplusplus +} /* extern "C" */ +#endif diff --git a/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/freeRtosCoreConfiguration/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/main/CMakeLists.txt new file mode 100644 index 00000000000..472f82ef313 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/CMakeLists.txt @@ -0,0 +1,71 @@ +# startUp is an INTERFACE library - it carries no compiled sources, only +# propagates link dependencies (bspMcu, common, hardFaultHandler) and the linker +# script to consumers via INTERFACE properties. +add_library(startUp INTERFACE) + +target_link_libraries(startUp INTERFACE bspMcu common hardFaultHandler) + +add_library( + main + src/os/isr/isr_can.cpp + src/os/isr/isr_sys.cpp + src/systems/CanSystem.cpp + src/lifecycle/StaticBsp.cpp + src/main.cpp) + +target_include_directories(main PUBLIC include) + +target_link_libraries( + main + PUBLIC bspConfiguration + PRIVATE bspClock + bspTimer + bspUart + bxCanTransceiver + configuration + etl + lifecycle + safeSupervisor + startUp) + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + target_link_libraries(main PRIVATE freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + target_sources(startUp INTERFACE src/bsp/threadx/tx_initialize_low_level.S + src/bsp/threadx/tx_execution_initialize.c) + target_link_libraries(main PRIVATE threadXCortexM4) +endif () + +# --whole-archive forces the linker to include ALL object files from the main +# library, even if no other translation unit references them. This is required +# because isr_can.cpp provides strong ISR definitions (CAN1_RX0_IRQHandler, +# CAN1_TX_IRQHandler) that override the weak default handlers in the startup +# assembly. Without --whole-archive the linker would discard isr_can.o as +# "unreferenced" and the weak stubs would remain, silently swallowing CAN IRQs. +set_target_properties(main PROPERTIES INTERFACE_LINK_LIBRARIES_DIRECT_EXCLUDE + "") +target_link_options( + main + INTERFACE + "LINKER:--whole-archive" + "$" + "LINKER:--no-whole-archive") + +set_target_properties( + startUp + PROPERTIES PROP_LINKER_SCRIPT + "${CMAKE_CURRENT_SOURCE_DIR}/linkerscript/application.ld") +set_target_properties( + startUp + PROPERTIES LINK_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/linkerscript/application.ld") + +if (BUILD_TARGET_RTOS STREQUAL "FREERTOS") + add_library(osHooks src/osHooks/freertos/osHooks.cpp) + target_link_libraries(osHooks PRIVATE common freeRtosCm4SysTick) +elseif (BUILD_TARGET_RTOS STREQUAL "THREADX") + add_library(osHooks src/osHooks/threadx/osHooks.cpp) + target_link_libraries(osHooks PRIVATE common threadXCortexM4 asyncThreadX + threadXConfiguration) +endif () +target_include_directories(osHooks PRIVATE include) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/index.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/index.rst new file mode 100644 index 00000000000..00ec97bb42d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/index.rst @@ -0,0 +1,40 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_main: + +main - NUCLEO-F413ZH +==================== + +Overview +-------- + +The ``main`` module contains the platform-specific entry point, startup code, linker +script, and lifecycle systems for the NUCLEO-F413ZH evaluation board: + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + "MCU", "STM32F413ZH - Arm Cortex-M4, single-precision FPU" + "Core clock", "96 MHz (HSI with PLL)" + "Flash", "1.5 MB" + "SRAM", "320 KB (SRAM1 256 KB + SRAM2 64 KB)" + "CAN", "bxCAN CAN1 - PD0 (RX) / PD1 (TX), AF9, 500 kbit/s" + "Debug UART", "USART3 - PD8 TX (AF7), 115200 baud, routed to ST-LINK VCP" + "User LED", "LD1 (green) on PB0 (active-high)" + "Debug interface", "On-board ST-LINK/V2-1 (SWD)" + +See :ref:`bspconfig_nucleo_f413zh` for the UART and standard I/O configuration, and the +user documentation for the sub-modules: + +.. toctree:: + user/index diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/StaticBsp.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/StaticBsp.rst new file mode 100644 index 00000000000..9ca2bc298c4 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/StaticBsp.rst @@ -0,0 +1,24 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_StaticBsp: + +Static BSP +========== + +Overview +-------- + +The ``StaticBsp`` class aggregates the BSP peripherals that must be operational before +the lifecycle starts ("static"): the system timer (DWT cycle counter based) and the +UART terminal (USART3, see :ref:`bspConfig_Uart_F413ZH`). It is instantiated in +``main.cpp`` and initialized from ``main()`` before ``app_main()``, so timing services +and serial output are available during the early lifecycle phases. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/index.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/index.rst new file mode 100644 index 00000000000..03faa96dded --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/index.rst @@ -0,0 +1,32 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +User Documentation +================== + +Find the detailed information about each module below: + +.. toctree:: + :hidden: + + startup + main + StaticBsp + systems/CanSystem + +.. csv-table:: + :widths: 30, 70 + :width: 100% + + :ref:`nucleo_f413zh_startup`, "Startup Code and Reset Handler" + :ref:`nucleo_f413zh_main_entry`, "Main File and boot sequence" + :ref:`nucleo_f413zh_StaticBsp`, "Static BSP - pre-lifecycle peripheral init" + :ref:`nucleo_f413zh_CanSystem`, "CAN System (bxCAN1, 500 kbit/s)" diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/main.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/main.rst new file mode 100644 index 00000000000..663366dc4d7 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/main.rst @@ -0,0 +1,40 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_main_entry: + +main File +========= + +The ``main.cpp`` module is the entry point of the NUCLEO-F413ZH reference application. +``SystemInit()``, called from ``Reset_Handler``, configures the PLL to 96 MHz, switches +on the LD1 LED (PB0), and sets up early USART3 TX output for use before the BSP UART +driver is initialized. + +``main()`` constructs the safety supervisor, initializes the static BSP (system timer +and UART terminal, see :ref:`nucleo_f413zh_StaticBsp`) and calls ``app_main()``, which +starts the lifecycle manager and the RTOS scheduler. The IWDG refresh writes in +``main()`` are no-ops until the SafetyManager enables the watchdog at runlevel 1. The +task contexts are defined in ``async/Config.h`` of ``referenceApp/asyncCoreConfiguration``: +``TASK_BACKGROUND``, ``TASK_BSP``, ``TASK_UDS``, ``TASK_DEMO``, ``TASK_ETHERNET``, +``TASK_CAN``, ``TASK_SYSADMIN`` and ``TASK_SAFETY``. + +``platformLifecycleAdd()`` registers the platform's ``CanSystem`` at runlevel 2 in the +``TASK_CAN`` context; the remaining components - including ``RuntimeSystem`` and +``SafetySystem`` at runlevel 1 and, since the platform enables +``PLATFORM_SUPPORT_TRANSPORT`` and ``PLATFORM_SUPPORT_UDS``, the transport, DoCAN and +UDS systems - are registered by the shared application code. + +``setupApplicationsIsr()`` is a no-op; CAN NVIC setup happens in ``CanSystem::run()``. + +``HardFault_Handler`` branches to ``customHardFaultHandler`` from the +``hardFaultHandler`` module, which dumps the registers to RAM and ends in +``HardFault_Handler_Final``, which performs a system reset. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/startup.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/startup.rst new file mode 100644 index 00000000000..b286cc96fea --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/startup.rst @@ -0,0 +1,54 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_startup: + +Startup Code +============ + +The startup code (``platforms/stm32/bsp/bspMcu/startup/startup_stm32f413xx.s``) provides +the vector table and the ``Reset_Handler`` for the STM32F413xx. + +Vector Table +------------ + +The vector table is placed at the base of flash (``0x0800_0000``) in the +``.isr_vector`` section. It contains the initial stack pointer, the Cortex-M4 exception +vectors, and the 102 STM32F413xx interrupt vectors (RM0430). Every handler is a weak +alias of ``Default_Handler`` (an infinite loop) and is overridden by a strong definition +where the application implements one (e.g. ``HardFault_Handler``, +``CAN1_RX0_IRQHandler``). + +Reset Handler +------------- + +``Reset_Handler`` executes the following sequence: + +1. Load the initial stack pointer (``_estack``). +2. Copy the ``.data`` section from flash to SRAM. +3. Zero-fill the ``.bss`` section. +4. Enable the FPU (CP10/CP11 full access in ``CPACR``, followed by ``dsb``/``isb``). + This is required for ThreadX; the FreeRTOS port enables the FPU itself. +5. Call ``SystemInit()`` (PLL and early peripheral setup, defined in ``main.cpp``). +6. Call ``__libc_init_array`` (C++ global/static constructors). +7. Call ``main()``. + +The startup code does not mask or unmask interrupts; interrupt control is left to the +RTOS port once the scheduler starts. + +Memory Layout +------------- + +The linker script places code in 1536 KB flash at ``0x0800_0000`` and data in the +320 KB of contiguous SRAM at ``0x2000_0000`` (SRAM1 256 KB + SRAM2 64 KB). The top +1 KB of SRAM (``0x2004_FC00``-``0x2005_0000``) is excluded from the ``RAM`` region and +reserved for the hard fault handler dump, so it survives reset. The stack grows down +from ``_estack`` at ``0x2004_FC00``. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/systems/CanSystem.rst b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/systems/CanSystem.rst new file mode 100644 index 00000000000..6def2fc69fd --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/doc/user/systems/CanSystem.rst @@ -0,0 +1,77 @@ +.. + ******************************************************************************* + Copyright (c) 2026 An Dao + + This program and the accompanying materials are made available under the + terms of the Apache License Version 2.0 which is available at + https://www.apache.org/licenses/LICENSE-2.0 + + SPDX-License-Identifier: Apache-2.0 + ******************************************************************************* + +.. _nucleo_f413zh_CanSystem: + +CAN System +========== + +Overview +-------- + +The ``CanSystem`` implements the ``ICanSystem`` interface for the NUCLEO-F413ZH using +the STM32F413ZH's bxCAN peripheral on CAN1. It manages a single ``BxCanTransceiver`` +(``CAN_0``), participates in the lifecycle as a ``SingleContextLifecycleComponent`` +running in the ``TASK_CAN`` context, and provides the ``extern "C"`` ISR trampolines. +It is registered as an ``etl::singleton`` so the ISR handlers can reach the instance +without global variables. + +Hardware Configuration +---------------------- + +CAN1 uses PD0 (RX) and PD1 (TX), alternate function AF9. An external CAN transceiver +is required to drive the bus. Bit timing at 48 MHz APB1 clock: prescaler 6 (8 MHz time +quantum), 16 TQ per bit (1 sync + 13 BS1 + 2 BS2), giving 500 kbit/s with a sample +point of 87.5 %. + +Lifecycle +--------- + +``init()`` only signals ``transitionDone()`` - the peripheral is untouched until +``run()``, which: + +1. Enables the ``CanRxRunnable``. +2. Initializes and opens the ``BxCanTransceiver`` (CAN1 register setup, normal mode). +3. Configures the NVIC and enables the CAN1 IRQ lines: + + .. code-block:: c++ + + NVIC_SetPriority(CAN1_RX0_IRQn, 6); + NVIC_SetPriority(CAN1_TX_IRQn, 6); + NVIC_ClearPendingIRQ(CAN1_RX0_IRQn); + NVIC_ClearPendingIRQ(CAN1_TX_IRQn); + NVIC_EnableIRQ(CAN1_RX0_IRQn); + NVIC_EnableIRQ(CAN1_TX_IRQn); + + The NVIC is configured in ``run()``, not in ``setupApplicationsIsr()``, so the async + framework is fully initialized before any CAN ISR can fire. + +``shutdown()`` closes and shuts down the transceiver and disables the RX runnable. + +Interrupt Handling +------------------ + +bxCAN uses dedicated NVIC lines: ``CAN1_RX0_IRQHandler`` (FIFO 0 message pending, +FMPIE0) routes to ``call_can_isr_RX()`` and ``CAN1_TX_IRQHandler`` (TX mailbox empty) +routes to ``call_can_isr_TX()``. + +``call_can_isr_RX()`` first disables the FMPIE0 interrupt: the bxCAN RX FIFO is only +3 messages deep, and on a heavily loaded bus it can refill before the ISR returns, +causing endless ISR re-entry. It then reads pending frames from the hardware FIFO into +the software queue under lock (``BxCanTransceiver::receiveInterrupt``) and, if frames +were received, dispatches the ``CanRxRunnable`` into the ``TASK_CAN`` context; FMPIE0 +stays disabled until ``receiveTask()`` has drained the software queue. If no frames +were pending, FMPIE0 is re-enabled immediately. + +``call_can_isr_TX()`` forwards the TX-complete event to +``BxCanTransceiver::transmitInterrupt``, which releases the TX mailbox and sends the +next queued frame. The ``CanRxRunnable`` calls ``receiveTask()``, which drains the +software queue and notifies the registered frame listeners. diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/include/lifecycle/StaticBsp.h b/executables/referenceApp/platforms/nucleo_f413zh/main/include/lifecycle/StaticBsp.h new file mode 100644 index 00000000000..d81d46d8d9e --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/include/lifecycle/StaticBsp.h @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace platform +{ + +class StaticBsp +{ +public: + void init(); +}; + +} // namespace platform diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/include/systems/CanSystem.h b/executables/referenceApp/platforms/nucleo_f413zh/main/include/systems/CanSystem.h new file mode 100644 index 00000000000..ff70511ddc2 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/include/systems/CanSystem.h @@ -0,0 +1,67 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include +#include +#include +#include + +namespace systems +{ + +/** + * CAN system for the NUCLEO-F413ZH: manages a single BxCanTransceiver on CAN1 + * (PD0 RX / PD1 TX, 500 kbit/s). Registered as an etl::singleton so the ISR + * trampolines can reach the instance without global variables. + */ +class CanSystem +: public ::lifecycle::SingleContextLifecycleComponent +, public ::can::ICanSystem +, public ::etl::singleton_base +{ +public: + CanSystem(::async::ContextType context); + + void init() override; + void run() override; + void shutdown() override; + + ::can::ICanTransceiver* getCanTransceiver(uint8_t busId) override; + + /** + * Dispatches CAN RX processing into the async context. + * \note Called from ISR context (call_can_isr_RX). Must be safe for ISR invocation. + */ + void dispatchRxTask(); + +private: + class CanRxRunnable : public ::async::RunnableType + { + public: + explicit CanRxRunnable(CanSystem& parent); + + void execute() override; + + void setEnabled(bool enabled) { _enabled = enabled; } + + private: + CanSystem& _parent; + bool _enabled; + }; + + ::async::ContextType _context; + ::bios::BxCanTransceiver _transceiver0; + CanRxRunnable _canRxRunnable; +}; + +} // namespace systems diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/linkerscript/application.ld b/executables/referenceApp/platforms/nucleo_f413zh/main/linkerscript/application.ld new file mode 100644 index 00000000000..7192c02f28d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/linkerscript/application.ld @@ -0,0 +1,129 @@ +/* Linker script for STM32F413ZH (NUCLEO-F413ZH) + * + * SPDX-License-Identifier: Apache-2.0 + * + * Flash: 1536 KB at 0x08000000 + * SRAM: 320 KB at 0x20000000 + */ + +ENTRY(Reset_Handler) + +_Min_Heap_Size = 0x200; +_Min_Stack_Size = 0x400; + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 1536K + /* Top 1 KB (0x2004FC00-0x20050000) is reserved for the hard fault + * handler dump region and survives reset (never initialized here). */ + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 319K +} + +SECTIONS +{ + .isr_vector : + { + . = ALIGN(4); + KEEP(*(.isr_vector)) + . = ALIGN(4); + } > FLASH + + .text : + { + . = ALIGN(4); + *(.text) + *(.text*) + *(.glue_7) + *(.glue_7t) + *(.eh_frame) + + KEEP(*(.init)) + KEEP(*(.fini)) + + . = ALIGN(4); + _etext = .; + } > FLASH + + .rodata : + { + . = ALIGN(4); + *(.rodata) + *(.rodata*) + . = ALIGN(4); + } > FLASH + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } > FLASH + + .ARM : + { + __exidx_start = .; + *(.ARM.exidx*) + __exidx_end = .; + } > FLASH + + .preinit_array : + { + PROVIDE_HIDDEN(__preinit_array_start = .); + KEEP(*(.preinit_array*)) + PROVIDE_HIDDEN(__preinit_array_end = .); + } > FLASH + + .init_array : + { + PROVIDE_HIDDEN(__init_array_start = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array*)) + PROVIDE_HIDDEN(__init_array_end = .); + } > FLASH + + .fini_array : + { + PROVIDE_HIDDEN(__fini_array_start = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array*)) + PROVIDE_HIDDEN(__fini_array_end = .); + } > FLASH + + _sidata = LOADADDR(.data); + + .data : + { + . = ALIGN(4); + _sdata = .; + *(.data) + *(.data*) + . = ALIGN(4); + _edata = .; + } > RAM AT> FLASH + + .bss : + { + . = ALIGN(4); + _sbss = .; + __bss_start__ = _sbss; + *(.bss) + *(.bss*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + __bss_end__ = _ebss; + } > RAM + + ._user_heap_stack : + { + . = ALIGN(8); + __end__ = .; + _end = .; + PROVIDE(end = .); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + } > RAM + + _estack = ORIGIN(RAM) + LENGTH(RAM); + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/main/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_execution_initialize.c b/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_execution_initialize.c new file mode 100644 index 00000000000..612501e3939 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_execution_initialize.c @@ -0,0 +1,17 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/* + * Custom profiling initialization function + * + * Do not remove! This is called by ThreadX core just before its scheduler runs + * if TX_ENABLE_EXECUTION_CHANGE_NOTIFY is defined. + */ +void _tx_execution_initialize() { return; } diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_initialize_low_level.S b/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_initialize_low_level.S new file mode 100644 index 00000000000..0d9c1203b4b --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/bsp/threadx/tx_initialize_low_level.S @@ -0,0 +1,96 @@ +@/*************************************************************************** +@ * Copyright (c) 2024 Microsoft Corporation +@ * +@ * This program and the accompanying materials are made available under the +@ * terms of the MIT License which is available at +@ * https://opensource.org/licenses/MIT. +@ * +@ * SPDX-License-Identifier: MIT +@ **************************************************************************/ +@ +@ ThreadX low-level initialization for NUCLEO-F413ZH (Cortex-M4, 96 MHz) +@ + +#include "async/Config.h" + + .global _tx_thread_system_stack_ptr + .global _tx_initialize_unused_memory + .global _tx_timer_interrupt + .global __main + .global g_pfnVectors + .global __tx_SysTickHandler + .extern setupApplicationsIsr + +SYSTEM_CLOCK = 96000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / ASYNC_CONFIG_TICK_IN_US) -1) + + .text 32 + .align 4 + .syntax unified + +@ VOID _tx_initialize_low_level(VOID) + .global _tx_initialize_low_level + .thumb_func +_tx_initialize_low_level: + + CPSID i + + @ Setup Vector Table Offset Register + MOV r0, #0xE000E000 + LDR r1, =g_pfnVectors + STR r1, [r0, #0xD08] + + @ Set system stack pointer from vector value + LDR r0, =_tx_thread_system_stack_ptr + LDR r1, =g_pfnVectors + LDR r1, [r1] + STR r1, [r0] + + @ Enable the cycle count register (DWT) + LDR r0, =0xE0001000 + LDR r1, [r0] + ORR r1, r1, #1 + STR r1, [r0] + + @ Configure SysTick + MOV r0, #0xE000E000 + LDR r1, =SYSTICK_CYCLES + STR r1, [r0, #0x14] @ SysTick Reload Value + MOV r1, #0x7 @ Enable, interrupt, processor clock + STR r1, [r0, #0x10] @ SysTick Control + + @ Configure handler priorities + LDR r1, =0x00000000 @ UsgF, BusF, MemM = 0 + STR r1, [r0, #0xD18] + + LDR r1, =0xFF000000 @ SVCall = 0xFF (lowest) + STR r1, [r0, #0xD1C] + + LDR r1, =0x40FF0000 @ SysTick = 0x40, PendSV = 0xFF + STR r1, [r0, #0xD20] + + @ Configure platform-specific ISRs + PUSH {lr} + BL setupApplicationsIsr + POP {lr} + + BX lr + +@ SysTick handler - redirects to ThreadX timer interrupt + .global __tx_SysTickHandler + .global SysTick_Handler + .thumb_func +__tx_SysTickHandler: + .thumb_func +SysTick_Handler: + PUSH {r0, lr} + BL _tx_timer_interrupt + POP {r0, lr} + BX LR + + .section .text.SVC_Handler + .global SVC_Handler + .thumb_func +SVC_Handler: + ldr r0, =Default_Handler + bx r0 diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/lifecycle/StaticBsp.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/lifecycle/StaticBsp.cpp new file mode 100644 index 00000000000..40d1147666f --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/lifecycle/StaticBsp.cpp @@ -0,0 +1,25 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "lifecycle/StaticBsp.h" + +#include "bsp/timer/SystemTimer.h" +#include "bsp/uart/UartConfig.h" + +namespace platform +{ + +void StaticBsp::init() +{ + initSystemTimer(); + bsp::Uart::getInstance(bsp::Uart::Id::TERMINAL).init(); +} + +} // namespace platform diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/main.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/main.cpp new file mode 100644 index 00000000000..b63095f1f12 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/main.cpp @@ -0,0 +1,112 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "async/Config.h" +#include "clock/clockConfig.h" +#include "lifecycle/StaticBsp.h" +#include "mcu/mcu.h" +#ifdef PLATFORM_SUPPORT_CAN +#include "systems/CanSystem.h" +#endif + +#include +#include +#include + +extern void app_main(); + +extern "C" +{ +void SystemInit() +{ + configurePll(); + + // LED ON: PB0 = LD1 (green) on NUCLEO-F413ZH + static constexpr uint8_t LED_PIN = 0U; + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; + (void)RCC->AHB1ENR; // Read-back for clock propagation + GPIOB->MODER &= ~(3U << (LED_PIN * 2U)); + GPIOB->MODER |= (1U << (LED_PIN * 2U)); // GPIO output mode + GPIOB->BSRR = (1U << LED_PIN); + + // Early USART3 TX (PD8 AF7, 115200 @ 48 MHz APB1), usable before the BSP + // UART driver is initialized. + // BRR = 48 000 000 / 115 200 = 416.67 -> 417 + static constexpr uint8_t USART_TX_PIN = 8U; + static constexpr uint8_t USART_TX_AF = 7U; // AF7 = USART3_TX on PD8 + static constexpr uint32_t USART_BRR_115200_48MHZ = 417U; + + RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; + (void)RCC->AHB1ENR; + RCC->APB1ENR |= RCC_APB1ENR_USART3EN; + (void)RCC->APB1ENR; + + // PD8 = AF7 (USART3_TX) - pin 8 uses AFR[1], index = pin - 8 = 0 + GPIOD->MODER &= ~(3U << (USART_TX_PIN * 2U)); + GPIOD->MODER |= (2U << (USART_TX_PIN * 2U)); // Alternate function mode + GPIOD->AFR[1] &= ~(0xFU << ((USART_TX_PIN - 8U) * 4U)); + GPIOD->AFR[1] |= (static_cast(USART_TX_AF) << ((USART_TX_PIN - 8U) * 4U)); + + USART3->CR1 = 0; + USART3->CR2 = 0; + USART3->CR3 = 0; + USART3->BRR = USART_BRR_115200_48MHZ; + USART3->CR1 = USART_CR1_TE | USART_CR1_UE; + while ((USART3->SR & USART_SR_TC) == 0) {} // Wait for TX complete +} + +// NVIC priorities for peripheral interrupts are configured by the owning +// systems during board bring-up. +void setupApplicationsIsr(void) {} +} // extern "C" + +namespace platform +{ +StaticBsp staticBsp; + +StaticBsp& getStaticBsp() { return staticBsp; } + +#ifdef PLATFORM_SUPPORT_CAN +::etl::typed_storage<::systems::CanSystem> canSystem; +#endif + +void platformLifecycleAdd(::lifecycle::LifecycleManager& lifecycleManager, uint8_t const level) +{ + (void)lifecycleManager; +#ifdef PLATFORM_SUPPORT_CAN + if (level == 2U) + { + lifecycleManager.addComponent("can", canSystem.create(TASK_CAN), level); + } +#endif + (void)level; +} +} // namespace platform + +#ifdef PLATFORM_SUPPORT_CAN +namespace systems +{ +::can::ICanSystem& getCanSystem() { return *::platform::canSystem; } +} // namespace systems +#endif + +int main() +{ + // IWDG refresh - a no-op unless the watchdog is already running (it is + // enabled later by the SafetyManager). + IWDG->KR = 0xAAAAU; + + ::safety::safeSupervisorConstructor.construct(); + ::platform::staticBsp.init(); + IWDG->KR = 0xAAAAU; + + app_main(); + return 1; +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_can.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_can.cpp new file mode 100644 index 00000000000..c3d5bf28539 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_can.cpp @@ -0,0 +1,19 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +extern "C" +{ +extern void call_can_isr_RX(); +extern void call_can_isr_TX(); + +void CAN1_RX0_IRQHandler(void) { call_can_isr_RX(); } + +void CAN1_TX_IRQHandler(void) { call_can_isr_TX(); } +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_sys.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_sys.cpp new file mode 100644 index 00000000000..58b8505a04d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/os/isr/isr_sys.cpp @@ -0,0 +1,29 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "reset/softwareSystemReset.h" + +extern "C" +{ +void HardFault_Handler() +{ + // WARNING: + // Do NOT modify this function if possible. Use HardFault_Handler_Final instead. + // Refer to hardFaultHandler documentation for details. +#ifndef UNIT_TEST + asm volatile("b customHardFaultHandler"); +#else + while (true) {} +#endif +} + +void HardFault_Handler_Final() { softwareSystemReset(); } + +} // extern "C" diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/freertos/osHooks.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/freertos/osHooks.cpp new file mode 100644 index 00000000000..232043f997c --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/freertos/osHooks.cpp @@ -0,0 +1,25 @@ +// Copyright 2026 An Dao +// +// SPDX-License-Identifier: Apache-2.0 + +#include "FreeRTOS.h" +#include "mcu/mcu.h" +#include "task.h" + +extern "C" +{ +void vApplicationStackOverflowHook(TaskHandle_t /* xTask */, char* /* pcTaskName */) +{ + while (true) {} +} + +void vApplicationMallocFailedHook(void) +{ + while (true) {} +} + +// Feeds the IWDG on every tick. The IWDG may already be running from a +// previous firmware image (it cannot be disabled, only fed). This feed runs +// in addition to the SafetyManager's cyclic watchdog service. +void vApplicationTickHook(void) { IWDG->KR = 0xAAAAU; } +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/threadx/osHooks.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/threadx/osHooks.cpp new file mode 100644 index 00000000000..599f7a09365 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/osHooks/threadx/osHooks.cpp @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "async/Config.h" +#include "async/Hook.h" +#include "mcu/mcu.h" +#include "tx_api.h" + +#include + +extern "C" +{ +extern TX_THREAD _tx_timer_thread; + +void _tx_execution_thread_enter() +{ + TX_THREAD* thread = tx_thread_identify(); + if (thread->tx_thread_priority == _tx_timer_thread.tx_thread_priority) + { + asyncEnterTask(static_cast(ASYNC_CONFIG_TASK_COUNT)); + } + else + { + asyncEnterTask(static_cast(thread->tx_thread_entry_parameter)); + } +} + +void _tx_execution_thread_exit() +{ + TX_THREAD* thread = tx_thread_identify(); + if (thread->tx_thread_priority == _tx_timer_thread.tx_thread_priority) + { + asyncLeaveTask(static_cast(ASYNC_CONFIG_TASK_COUNT)); + } + else + { + asyncLeaveTask(static_cast(thread->tx_thread_entry_parameter)); + } +} + +void tx_low_power_enter() +{ + // Feed IWDG before entering low-power mode to prevent watchdog reset. + IWDG->KR = 0xAAAAU; +} + +void tx_low_power_exit() {} + +void vIllegalISR() +{ + printf("vIllegalISR\r\n"); + for (;;) + ; +} +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/main/src/systems/CanSystem.cpp b/executables/referenceApp/platforms/nucleo_f413zh/main/src/systems/CanSystem.cpp new file mode 100644 index 00000000000..68264bb051b --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/main/src/systems/CanSystem.cpp @@ -0,0 +1,142 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "systems/CanSystem.h" + +#include "async/Config.h" +#include "async/Hook.h" +#include "busid/BusId.h" +#include "mcu/mcu.h" + +namespace +{ +// bxCAN1 configuration for STM32F413ZH +// CAN1: PD0 (RX, AF9), PD1 (TX, AF9), 500 kbit/s @ 48 MHz APB1 +// BTR: SJW=1TQ, BS1=13TQ, BS2=2TQ, prescaler=6 -> 500 kbit/s +::bios::BxCanDevice::Config const can1Config = { + CAN1, // peripheral + 6U, // prescaler (48 MHz / 6 = 8 MHz -> 16 TQ @ 500k) + 13U, // bs1 (13 TQ) + 2U, // bs2 (2 TQ) + 1U, // sjw (1 TQ) + GPIOD, // rxPort + 0U, // rxPin (PD0) + 9U, // rxAf (AF9 = CAN1) + GPIOD, // txPort + 1U, // txPin (PD1) + 9U, // txAf (AF9 = CAN1) +}; +} // namespace + +namespace systems +{ + +CanSystem::CanSystem(::async::ContextType context) +: ::lifecycle::SingleContextLifecycleComponent(context) +, ::etl::singleton_base(*this) +, _context(context) +, _transceiver0(context, ::busid::CAN_0, can1Config) +, _canRxRunnable(*this) +{} + +void CanSystem::init() { transitionDone(); } + +void CanSystem::run() +{ + _canRxRunnable.setEnabled(true); + + (void)_transceiver0.init(); + (void)_transceiver0.open(); + + // Enable CAN IRQs only after the transceiver is open, so the async + // framework is ready before ISRs fire. + // ISR priority must be numerically >= configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY + // to safely use FreeRTOS FromISR APIs. + NVIC_SetPriority(CAN1_RX0_IRQn, 6); + NVIC_SetPriority(CAN1_TX_IRQn, 6); + NVIC_ClearPendingIRQ(CAN1_RX0_IRQn); + NVIC_ClearPendingIRQ(CAN1_TX_IRQn); + NVIC_EnableIRQ(CAN1_RX0_IRQn); + NVIC_EnableIRQ(CAN1_TX_IRQn); + + transitionDone(); +} + +void CanSystem::shutdown() +{ + (void)_transceiver0.close(); + _transceiver0.shutdown(); + + _canRxRunnable.setEnabled(false); + + transitionDone(); +} + +::can::ICanTransceiver* CanSystem::getCanTransceiver(uint8_t busId) +{ + if (busId == ::busid::CAN_0) + { + return &_transceiver0; + } + return nullptr; +} + +void CanSystem::dispatchRxTask() { ::async::execute(_context, _canRxRunnable); } + +CanSystem::CanRxRunnable::CanRxRunnable(CanSystem& parent) : _parent(parent), _enabled(false) {} + +void CanSystem::CanRxRunnable::execute() +{ + if (_enabled) + { + _parent._transceiver0.receiveTask(); + } +} + +} // namespace systems + +extern "C" +{ +/** + * RX ISR for bxCAN1. Disables the RX FIFO interrupt (FMPIE0) to prevent + * re-entry while the 3-deep hardware FIFO refills, reads pending frames under + * lock, and dispatches the CanRxRunnable. FMPIE0 is re-enabled either here + * (no frames) or in receiveTask() after the software queue is drained. + */ +void call_can_isr_RX() +{ + ::bios::BxCanTransceiver::disableRxInterrupt(::busid::CAN_0); + ::asyncEnterIsrGroup(ISR_GROUP_CAN); + + uint8_t framesReceived; + { + ::async::LockType const lock; + framesReceived = ::bios::BxCanTransceiver::receiveInterrupt(::busid::CAN_0); + } + + if (framesReceived > 0) + { + ::systems::CanSystem::instance().dispatchRxTask(); + } + else + { + ::bios::BxCanTransceiver::enableRxInterrupt(::busid::CAN_0); + } + + ::asyncLeaveIsrGroup(ISR_GROUP_CAN); +} + +void call_can_isr_TX() +{ + ::asyncEnterIsrGroup(ISR_GROUP_CAN); + ::bios::BxCanTransceiver::transmitInterrupt(::busid::CAN_0); + ::asyncLeaveIsrGroup(ISR_GROUP_CAN); +} +} diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/safety/CMakeLists.txt new file mode 100644 index 00000000000..89a1188107d --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(safeLifecycle) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/safety/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/CMakeLists.txt new file mode 100644 index 00000000000..75799a0bb8c --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(safeLifecycle src/SafetyManager.cpp) + +target_include_directories(safeLifecycle PUBLIC include) + +target_link_libraries(safeLifecycle PRIVATE safeSupervisor safeUtils + safeBspMcuWatchdog) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h new file mode 100644 index 00000000000..1ffe5faba23 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/include/safeLifecycle/SafetyManager.h @@ -0,0 +1,32 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include + +namespace safety +{ + +class SafetyManager +{ +public: + SafetyManager(); + void init(); + void run(); + void shutdown(); + void cyclic(); + +private: + uint16_t _counter; + static constexpr uint8_t WATCHDOG_CYCLIC_COUNTER = 8U; ///< Kick every 8 cycles (80ms @ 10ms) +}; + +} // namespace safety diff --git a/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/src/SafetyManager.cpp b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/src/SafetyManager.cpp new file mode 100644 index 00000000000..70c07278ca1 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/safety/safeLifecycle/src/SafetyManager.cpp @@ -0,0 +1,63 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "safeLifecycle/SafetyManager.h" + +#include +#include +#include + +namespace safety +{ + +using ::util::logger::Logger; +using ::util::logger::SAFETY; + +SafetyManager::SafetyManager() : _counter(0U) {} + +void SafetyManager::init() +{ + Logger::debug(SAFETY, "SafetyManager initialized"); + + if (bsp::Watchdog::isResetFromWatchdog()) + { + Logger::warn(SAFETY, "Previous reset was caused by IWDG watchdog"); + bsp::Watchdog::clearResetFlag(); + } +} + +void SafetyManager::run() +{ + // Enable IWDG here (not in init) - the cyclic scheduler is already running + // at this point, so the 250ms timeout won't fire before the first kick. + bsp::Watchdog::enableWatchdog(bsp::Watchdog::DEFAULT_TIMEOUT); + Logger::debug(SAFETY, "IWDG watchdog enabled (250ms timeout)"); +} + +void SafetyManager::shutdown() {} + +void SafetyManager::cyclic() +{ + auto& supervisor = SafeSupervisor::getInstance(); + supervisor.safetyManagerSequenceMonitor.hit( + SafeSupervisor::SafetyManagerSequence::SAFETY_MANAGER_ENTER); + + // Kick IWDG every WATCHDOG_CYCLIC_COUNTER cycles (80ms @ 10ms cycle) + _counter++; + if (_counter >= WATCHDOG_CYCLIC_COUNTER) + { + _counter = 0U; + bsp::Watchdog::serviceWatchdog(); + } + + supervisor.safetyManagerSequenceMonitor.hit( + SafeSupervisor::SafetyManagerSequence::SAFETY_MANAGER_LEAVE); +} +} // namespace safety diff --git a/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/CMakeLists.txt b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/CMakeLists.txt new file mode 100644 index 00000000000..f0d6bbc1f68 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(threadXCoreConfiguration INTERFACE) + +target_include_directories(threadXCoreConfiguration INTERFACE include) diff --git a/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/include/tx_user.h b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/include/tx_user.h new file mode 100644 index 00000000000..d4c03c743be --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/include/tx_user.h @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +/* ThreadX user configuration for NUCLEO-F413ZH (Cortex-M4, 96 MHz). */ + +#include "async/Config.h" + +#define ROUND_UP_32(x) (((x) + 31U) & ~31U) +#define TX_MAX_PRIORITIES_LIMIT 1024 +#define TX_MIN_PRIORITIES 32 + +#ifndef TX_MAX_PRIORITIES +#define TX_MAX_PRIORITIES \ + ((ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U)) > TX_MAX_PRIORITIES_LIMIT \ + ? TX_MAX_PRIORITIES_LIMIT \ + : ((ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U)) < TX_MIN_PRIORITIES \ + ? TX_MIN_PRIORITIES \ + : ROUND_UP_32(ASYNC_CONFIG_TASK_COUNT + 1U))) +#endif // TX_MAX_PRIORITIES + +#if (TX_MAX_PRIORITIES < 32) || (TX_MAX_PRIORITIES > 1024) || ((TX_MAX_PRIORITIES % 32) != 0) +#error "TX_MAX_PRIORITIES must be between 32 and 1024 and evenly divisible by 32" +#endif + +#ifndef TX_MINIMUM_STACK +#define TX_MINIMUM_STACK (1024U) +#endif + +#define TX_ENABLE_STACK_CHECKING + +#define TX_NO_FILEX_POINTER + +/* Timer thread stack - the default 1024 is too small when the timer + * callback dispatches multiple runnables. */ +#ifndef TX_TIMER_THREAD_STACK_SIZE +#define TX_TIMER_THREAD_STACK_SIZE (8192U) +#endif + +#define TX_TIMER_TICKS_PER_SECOND (1000000U / ASYNC_CONFIG_TICK_IN_US) + +#define TX_ENABLE_EXECUTION_CHANGE_NOTIFY diff --git a/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/module.spec new file mode 100644 index 00000000000..87faef85b71 --- /dev/null +++ b/executables/referenceApp/platforms/nucleo_f413zh/threadXCoreConfiguration/module.spec @@ -0,0 +1 @@ +oss: true diff --git a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec index 87faef85b71..c918431ceaa 100644 --- a/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec +++ b/executables/referenceApp/platforms/nucleo_g474re/bspConfiguration/module.spec @@ -1 +1,2 @@ +unit_test: false oss: true From 6d8087a27ef7729ae4a5da0a938259b480b37802 Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Tue, 2 Jun 2026 08:51:06 +0200 Subject: [PATCH 10/11] Configure STM32 DoCAN addressing and UDS sessions Introduce two feature flags set by the STM32 board options: PLATFORM_SUPPORT_OBD_UDS_ADDRESSING selects 0x7E0/0x7E8 tester CAN IDs and logical address 0x0600, and PLATFORM_SUPPORT_PROGRAMMING_SESSION adds an application-level programming session. The BSW ProgrammingSession disables the UDS dispatcher on entry (bootloader handover), so the application session keeps the dispatcher alive and uses session index 0x02 to avoid matching PROGRAMMING_SESSION() in switchSession(). --- .../application/src/systems/DoCanSystem.cpp | 6 +- .../configuration/include/app/appConfig.h | 4 ++ .../platforms/nucleo_f413zh/Options.cmake | 6 ++ .../platforms/nucleo_g474re/Options.cmake | 6 ++ .../src/uds/session/DiagSession.cpp | 70 +++++++++++++++++++ 5 files changed, 91 insertions(+), 1 deletion(-) diff --git a/executables/referenceApp/application/src/systems/DoCanSystem.cpp b/executables/referenceApp/application/src/systems/DoCanSystem.cpp index 82a08a890e5..ebd78bc84da 100644 --- a/executables/referenceApp/application/src/systems/DoCanSystem.cpp +++ b/executables/referenceApp/application/src/systems/DoCanSystem.cpp @@ -41,7 +41,11 @@ namespace docan { DoCanSystem::AddressingFilterType::AddressEntryType DoCanSystem::_addresses[] - = {{0x02A, 0x0F0U, 0x0F0U, LOGICAL_ADDRESS, 0, 0}}; +#ifdef PLATFORM_SUPPORT_OBD_UDS_ADDRESSING + = {{0x7E0U, 0x7E8U, 0x7E8U, LOGICAL_ADDRESS, 0, 0}}; +#else + = {{0x02AU, 0x0F0U, 0x0F0U, LOGICAL_ADDRESS, 0, 0}}; +#endif DoCanSystem::DoCanSystem( ::transport::ITransportSystem& transportSystem, diff --git a/executables/referenceApp/configuration/include/app/appConfig.h b/executables/referenceApp/configuration/include/app/appConfig.h index 79214e3c9be..93b88a9e13c 100644 --- a/executables/referenceApp/configuration/include/app/appConfig.h +++ b/executables/referenceApp/configuration/include/app/appConfig.h @@ -13,7 +13,11 @@ #include #ifdef PLATFORM_SUPPORT_TRANSPORT +#ifdef PLATFORM_SUPPORT_OBD_UDS_ADDRESSING +static constexpr uint16_t LOGICAL_ADDRESS = 0x0600U; +#else static constexpr uint16_t LOGICAL_ADDRESS = 0x002AU; +#endif #endif // PLATFORM_SUPPORT_TRANSPORT #define safety_task_stackSize 2048U diff --git a/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake b/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake index 0046fbb85ca..30532eab3bc 100644 --- a/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake +++ b/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake @@ -35,6 +35,12 @@ set(PLATFORM_SUPPORT_TRANSPORT set(PLATFORM_SUPPORT_UDS ON CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_OBD_UDS_ADDRESSING + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_PROGRAMMING_SESSION + ON + CACHE BOOL "" FORCE) set(PLATFORM_SUPPORT_WATCHDOG OFF CACHE BOOL "" FORCE) diff --git a/executables/referenceApp/platforms/nucleo_g474re/Options.cmake b/executables/referenceApp/platforms/nucleo_g474re/Options.cmake index 93498875ca1..50ca7022af8 100644 --- a/executables/referenceApp/platforms/nucleo_g474re/Options.cmake +++ b/executables/referenceApp/platforms/nucleo_g474re/Options.cmake @@ -32,6 +32,12 @@ set(PLATFORM_SUPPORT_TRANSPORT set(PLATFORM_SUPPORT_UDS ON CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_OBD_UDS_ADDRESSING + ON + CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_PROGRAMMING_SESSION + ON + CACHE BOOL "" FORCE) set(PLATFORM_SUPPORT_WATCHDOG OFF CACHE BOOL "" FORCE) diff --git a/executables/referenceApp/udsConfiguration/src/uds/session/DiagSession.cpp b/executables/referenceApp/udsConfiguration/src/uds/session/DiagSession.cpp index 9bc0f049da7..106bdde450e 100644 --- a/executables/referenceApp/udsConfiguration/src/uds/session/DiagSession.cpp +++ b/executables/referenceApp/udsConfiguration/src/uds/session/DiagSession.cpp @@ -1,5 +1,6 @@ /******************************************************************************** * Copyright (c) 2024 Accenture + * Copyright (c) 2026 An Dao * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at @@ -16,6 +17,38 @@ namespace uds { + +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION +// Application-level programming session. Uses SessionType PROGRAMMING (0x02) +// for the correct response byte, but session index 0x02 instead of +// ProgrammingSession's 0x04 so switchSession()'s equality check against +// PROGRAMMING_SESSION() does not match. +class AppProgrammingSession : public DiagSession +{ +public: + AppProgrammingSession() : DiagSession(PROGRAMMING, 0x02U) {} + + DiagReturnCode::Type isTransitionPossible(SessionType const target) override + { + switch (target) + { + case DEFAULT: + case EXTENDED: + case PROGRAMMING: return DiagReturnCode::OK; + default: return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; + } + } + + DiagSession& getTransitionResult(SessionType target) override; +}; + +static AppProgrammingSession& appProgrammingSession() +{ + static AppProgrammingSession s; + return s; +} +#endif + ApplicationDefaultSession& DiagSession::APPLICATION_DEFAULT_SESSION() { static ApplicationDefaultSession applicationDefaultSession; @@ -62,13 +95,18 @@ ApplicationDefaultSession::isTransitionPossible(DiagSession::SessionType const t { case DiagSession::DEFAULT: case DiagSession::EXTENDED: +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION + case DiagSession::PROGRAMMING: +#endif { return DiagReturnCode::OK; } +#ifndef PLATFORM_SUPPORT_PROGRAMMING_SESSION case DiagSession::PROGRAMMING: { return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED_IN_ACTIVE_SESSION; } +#endif default: { return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; @@ -85,6 +123,12 @@ ApplicationDefaultSession::getTransitionResult(DiagSession::SessionType const ta { return DiagSession::APPLICATION_EXTENDED_SESSION(); } +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION + case DiagSession::PROGRAMMING: + { + return appProgrammingSession(); + } +#endif default: { return *this; @@ -123,7 +167,11 @@ ApplicationExtendedSession::getTransitionResult(DiagSession::SessionType const t } case DiagSession::PROGRAMMING: { +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION + return appProgrammingSession(); +#else return DiagSession::PROGRAMMING_SESSION(); +#endif } default: { @@ -166,4 +214,26 @@ DiagSession& ProgrammingSession::getTransitionResult(DiagSession::SessionType co } } +#ifdef PLATFORM_SUPPORT_PROGRAMMING_SESSION +DiagSession& +AppProgrammingSession::getTransitionResult(DiagSession::SessionType const targetSession) +{ + switch (targetSession) + { + case DiagSession::DEFAULT: + { + return DiagSession::APPLICATION_DEFAULT_SESSION(); + } + case DiagSession::EXTENDED: + { + return DiagSession::APPLICATION_EXTENDED_SESSION(); + } + default: + { + return *this; + } + } +} +#endif + } // namespace uds From 37a3be1ec1f5c8ba92c892a34e963cb4b8ff0d0c Mon Sep 17 00:00:00 2001 From: nhuvaoanh123 Date: Tue, 2 Jun 2026 09:00:07 +0200 Subject: [PATCH 11/11] Add STM32 UDS diagnostic services Add demo UDS services for the STM32 boards behind the PLATFORM_SUPPORT_UDS_DEMO_SERVICES board option: ECU reset, an in-memory DTC store with read/clear/control services, security access, VIN write, and demo routine control. The UDS dispatcher accepts functional requests when the demo services are enabled. --- .../referenceApp/application/CMakeLists.txt | 11 ++ .../application/include/systems/UdsSystem.h | 35 ++++ .../application/include/uds/Stm32ClearDtc.h | 36 ++++ .../include/uds/Stm32ControlDtcSetting.h | 37 +++++ .../include/uds/Stm32DemoRoutine.h | 50 ++++++ .../application/include/uds/Stm32DtcManager.h | 62 +++++++ .../include/uds/Stm32ReadDtcInfo.h | 48 ++++++ .../include/uds/Stm32SecurityAccess.h | 57 +++++++ .../application/include/uds/Stm32WriteVin.h | 42 +++++ .../application/src/systems/UdsSystem.cpp | 113 +++++++++++++ .../application/src/uds/Stm32ClearDtc.cpp | 44 +++++ .../src/uds/Stm32ControlDtcSetting.cpp | 56 +++++++ .../application/src/uds/Stm32DemoRoutine.cpp | 85 ++++++++++ .../application/src/uds/Stm32DtcManager.cpp | 156 ++++++++++++++++++ .../application/src/uds/Stm32ReadDtcInfo.cpp | 125 ++++++++++++++ .../src/uds/Stm32SecurityAccess.cpp | 136 +++++++++++++++ .../application/src/uds/Stm32WriteVin.cpp | 69 ++++++++ .../platforms/nucleo_f413zh/Options.cmake | 3 + .../platforms/nucleo_g474re/Options.cmake | 3 + 19 files changed, 1168 insertions(+) create mode 100644 executables/referenceApp/application/include/uds/Stm32ClearDtc.h create mode 100644 executables/referenceApp/application/include/uds/Stm32ControlDtcSetting.h create mode 100644 executables/referenceApp/application/include/uds/Stm32DemoRoutine.h create mode 100644 executables/referenceApp/application/include/uds/Stm32DtcManager.h create mode 100644 executables/referenceApp/application/include/uds/Stm32ReadDtcInfo.h create mode 100644 executables/referenceApp/application/include/uds/Stm32SecurityAccess.h create mode 100644 executables/referenceApp/application/include/uds/Stm32WriteVin.h create mode 100644 executables/referenceApp/application/src/uds/Stm32ClearDtc.cpp create mode 100644 executables/referenceApp/application/src/uds/Stm32ControlDtcSetting.cpp create mode 100644 executables/referenceApp/application/src/uds/Stm32DemoRoutine.cpp create mode 100644 executables/referenceApp/application/src/uds/Stm32DtcManager.cpp create mode 100644 executables/referenceApp/application/src/uds/Stm32ReadDtcInfo.cpp create mode 100644 executables/referenceApp/application/src/uds/Stm32SecurityAccess.cpp create mode 100644 executables/referenceApp/application/src/uds/Stm32WriteVin.cpp diff --git a/executables/referenceApp/application/CMakeLists.txt b/executables/referenceApp/application/CMakeLists.txt index dcee58d8b46..9538bb4f81f 100644 --- a/executables/referenceApp/application/CMakeLists.txt +++ b/executables/referenceApp/application/CMakeLists.txt @@ -14,6 +14,17 @@ if (PLATFORM_SUPPORT_TRANSPORT) if (PLATFORM_SUPPORT_UDS) target_sources(app.referenceApp PRIVATE src/systems/UdsSystem.cpp src/uds/ReadIdentifierPot.cpp) + if (PLATFORM_SUPPORT_UDS_DEMO_SERVICES) + target_sources( + app.referenceApp + PRIVATE src/uds/Stm32ClearDtc.cpp + src/uds/Stm32ControlDtcSetting.cpp + src/uds/Stm32DemoRoutine.cpp + src/uds/Stm32DtcManager.cpp + src/uds/Stm32ReadDtcInfo.cpp + src/uds/Stm32SecurityAccess.cpp + src/uds/Stm32WriteVin.cpp) + endif () endif () endif () diff --git a/executables/referenceApp/application/include/systems/UdsSystem.h b/executables/referenceApp/application/include/systems/UdsSystem.h index 4d2a8a8a1cc..bdf5e6e5c1d 100644 --- a/executables/referenceApp/application/include/systems/UdsSystem.h +++ b/executables/referenceApp/application/include/systems/UdsSystem.h @@ -1,5 +1,6 @@ /******************************************************************************** * Copyright (c) 2024 Accenture + * Copyright (c) 2026 An Dao * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at @@ -22,6 +23,19 @@ #include #include #include + +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + #include #include #include @@ -101,7 +115,28 @@ class UdsSystem ReadIdentifierFromMemory _read22Cf01; ReadIdentifierPot _read22Cf02; WriteIdentifierToMemory _write2eCf03; +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + ReadIdentifierFromMemory _readF190; + Stm32WriteVin _writeF190; + ReadIdentifierFromMemory _readF195; + ReadIdentifierFromMemory _readF18C; + ReadIdentifierFromMemory _readF193; + ReadIdentifierFromMemory _readF18A; + ReadIdentifierFromMemory _readF180; +#endif TesterPresent _testerPresent; +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + ECUReset _ecuReset; + HardReset _hardReset; + Stm32SecurityAccess _securityAccess; + Stm32DtcManager _dtcManager; + Stm32ClearDtc _clearDtc; + Stm32ReadDtcInfo _readDtcInfo; + Stm32ControlDtcSetting _controlDtcSetting; + Stm32DemoRoutine _routineFF00; + Stm32DemoRoutine _routineFF01; + Stm32DemoRoutine _routineFF02; +#endif ::async::ContextType _context; ::async::TimeoutType _timeout; diff --git a/executables/referenceApp/application/include/uds/Stm32ClearDtc.h b/executables/referenceApp/application/include/uds/Stm32ClearDtc.h new file mode 100644 index 00000000000..8abd2b64df4 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32ClearDtc.h @@ -0,0 +1,36 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/Stm32DtcManager.h" +#include "uds/base/Service.h" + +namespace uds +{ +/** + * ClearDiagnosticInformation (SID 0x14). + * Accepts 3-byte group-of-DTC. 0xFFFFFF = clear all. + */ +class Stm32ClearDtc : public Service +{ +public: + explicit Stm32ClearDtc(Stm32DtcManager& dtcManager); + +private: + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const request[], + uint16_t requestLength) override; + + Stm32DtcManager& _dtcManager; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32ControlDtcSetting.h b/executables/referenceApp/application/include/uds/Stm32ControlDtcSetting.h new file mode 100644 index 00000000000..d4533889624 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32ControlDtcSetting.h @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/Stm32DtcManager.h" +#include "uds/base/Service.h" + +namespace uds +{ +/** + * ControlDTCSetting (SID 0x85). + * Subfunction 0x01 = ON (enable DTC recording) + * Subfunction 0x02 = OFF (disable DTC recording) + */ +class Stm32ControlDtcSetting : public Service +{ +public: + explicit Stm32ControlDtcSetting(Stm32DtcManager& dtcManager); + +private: + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const request[], + uint16_t requestLength) override; + + Stm32DtcManager& _dtcManager; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32DemoRoutine.h b/executables/referenceApp/application/include/uds/Stm32DemoRoutine.h new file mode 100644 index 00000000000..bfd1e0b4777 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32DemoRoutine.h @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/jobs/RoutineControlJob.h" + +namespace uds +{ +/** + * Demo routine for the STM32 diagnostic demo. + * Handles start/stop/requestResults for a single routine ID. + * Returns positive response with no data for all subfunctions. + */ +class Stm32DemoRoutine : public RoutineControlJob +{ +public: + explicit Stm32DemoRoutine(uint16_t routineId); + + DiagReturnCode::Type start( + IncomingDiagConnection& connection, + uint8_t const* request, + uint16_t requestLength) override; + + DiagReturnCode::Type stop( + IncomingDiagConnection& connection, + uint8_t const* request, + uint16_t requestLength) override; + + DiagReturnCode::Type requestResults( + IncomingDiagConnection& connection, + uint8_t const* request, + uint16_t requestLength) override; + +private: + uint8_t _implRequest[4]; // SID + subFn + routineId[2] + RoutineControlJobNode _stopNode; + RoutineControlJobNode _resultsNode; + uint8_t _stopImplRequest[4]; + uint8_t _resultsImplRequest[4]; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32DtcManager.h b/executables/referenceApp/application/include/uds/Stm32DtcManager.h new file mode 100644 index 00000000000..9b7a980a1b6 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32DtcManager.h @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "platform/estdint.h" + +#include + +namespace uds +{ +/** + * Minimal DTC manager for the STM32 diagnostic demo. + * In-memory only (no NvM persistence). + */ +class Stm32DtcManager +{ +public: + static uint8_t const MAX_DTCS = 16U; + + // ISO 14229-1 DTC status byte bits + static uint8_t const STATUS_TEST_FAILED = 0x01U; + static uint8_t const STATUS_CONFIRMED = 0x08U; + static uint8_t const STATUS_TEST_NOT_COMPLETE = 0x10U; + + struct DtcEntry + { + uint32_t dtcNumber; // 3-byte DTC (e.g. 0x123456) + uint8_t statusByte; + bool active; + }; + + Stm32DtcManager(); + + void reportFault(uint32_t dtcNumber); + void clearAll(); + void clearByGroup(uint32_t groupOfDtc); + + uint16_t getCountByStatusMask(uint8_t statusMask) const; + uint8_t getDtcsByStatusMask(uint8_t statusMask, uint8_t* buffer, uint16_t bufferSize) const; + uint8_t getSupportedDtcs(uint8_t* buffer, uint16_t bufferSize) const; + + void setDtcSettingEnabled(bool enabled) { _dtcSettingEnabled = enabled; } + + bool isDtcSettingEnabled() const { return _dtcSettingEnabled; } + +private: + DtcEntry* findOrCreate(uint32_t dtcNumber); + + etl::array _dtcs; + uint8_t _dtcCount; + bool _dtcSettingEnabled; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32ReadDtcInfo.h b/executables/referenceApp/application/include/uds/Stm32ReadDtcInfo.h new file mode 100644 index 00000000000..7ec64e4a27d --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32ReadDtcInfo.h @@ -0,0 +1,48 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/Stm32DtcManager.h" +#include "uds/base/Service.h" + +namespace uds +{ +/** + * ReadDTCInformation (SID 0x19). + * + * Subfunctions: + * 0x01 = reportNumberOfDTCByStatusMask + * 0x02 = reportDTCByStatusMask + * 0x0A = reportSupportedDTC + */ +class Stm32ReadDtcInfo : public Service +{ +public: + explicit Stm32ReadDtcInfo(Stm32DtcManager const& dtcManager); + +private: + static uint16_t const DTC_BUFFER_SIZE = 128U; + + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const request[], + uint16_t requestLength) override; + + DiagReturnCode::Type + handleReportNumberByStatusMask(IncomingDiagConnection& connection, uint8_t statusMask); + DiagReturnCode::Type + handleReportByStatusMask(IncomingDiagConnection& connection, uint8_t statusMask); + DiagReturnCode::Type handleReportSupportedDtc(IncomingDiagConnection& connection); + + Stm32DtcManager const& _dtcManager; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32SecurityAccess.h b/executables/referenceApp/application/include/uds/Stm32SecurityAccess.h new file mode 100644 index 00000000000..81972910d4f --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32SecurityAccess.h @@ -0,0 +1,57 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include "uds/base/Service.h" + +namespace uds +{ +/** + * SecurityAccess (SID 0x27) implementation for the STM32 diagnostic demo. + * + * Algorithm: + * Seed: LCG (a=1664525, c=1013904223) | 1, returned as four big-endian bytes + * Key: (seed ^ 0xDEADBEEF) truncated to uint16_t + * + * Subfunctions: + * 0x01 = requestSeed -> returns 4-byte seed (or zero seed if already unlocked) + * 0x02 = sendKey -> validates 2-byte key + */ +class Stm32SecurityAccess : public Service +{ +public: + Stm32SecurityAccess(); + +private: + static uint8_t const MAX_FAILED_ATTEMPTS = 3U; + static uint32_t const XOR_SECRET = 0xDEADBEEFU; + static uint32_t const LCG_A = 1664525U; + static uint32_t const LCG_C = 1013904223U; + + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const request[], + uint16_t requestLength) override; + + DiagReturnCode::Type handleRequestSeed(IncomingDiagConnection& connection); + DiagReturnCode::Type handleSendKey( + IncomingDiagConnection& connection, uint8_t const request[], uint16_t requestLength); + + uint32_t nextSeed(); + + uint32_t _seed; + bool _seedIssued; + bool _unlocked; + uint8_t _failedAttempts; + bool _lockedUntilReset; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/include/uds/Stm32WriteVin.h b/executables/referenceApp/application/include/uds/Stm32WriteVin.h new file mode 100644 index 00000000000..30098c0b3b4 --- /dev/null +++ b/executables/referenceApp/application/include/uds/Stm32WriteVin.h @@ -0,0 +1,42 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#pragma once + +#include +#include + +namespace uds +{ + +/** + * WriteDID handler for VIN (F190) that accepts variable-length payloads. + * + * Unlike WriteIdentifierToMemory (which enforces exact length), this handler + * accepts any payload from 1 to memory.size() bytes. + */ +class Stm32WriteVin : public DataIdentifierJob +{ +public: + Stm32WriteVin(uint16_t identifier, ::etl::span const& memory); + + DiagReturnCode::Type verify(uint8_t const* request, uint16_t requestLength) override; + + DiagReturnCode::Type process( + IncomingDiagConnection& connection, + uint8_t const* request, + uint16_t requestLength) override; + +private: + uint8_t _implementedRequest[3]; + ::etl::span _memory; +}; + +} // namespace uds diff --git a/executables/referenceApp/application/src/systems/UdsSystem.cpp b/executables/referenceApp/application/src/systems/UdsSystem.cpp index 1404acf3f70..8e743789a8a 100644 --- a/executables/referenceApp/application/src/systems/UdsSystem.cpp +++ b/executables/referenceApp/application/src/systems/UdsSystem.cpp @@ -1,5 +1,6 @@ /******************************************************************************** * Copyright (c) 2024 Accenture + * Copyright (c) 2026 An Dao * * This program and the accompanying materials are made available under the * terms of the Apache License Version 2.0 which is available at @@ -24,6 +25,28 @@ uint8_t const responseData22Cf01[] etl::array storedData2eCf03 = {0}; +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES +// VIN (DID F190) - 17-byte writable scratchpad. +etl::array vinData + = {'O', 'B', 'S', 'W', 'S', 'T', 'M', '3', '2', '0', '0', '0', '0', '0', '0', '0', '1'}; + +// SW version (DID F195) - read-only. +uint8_t const swVersionData[] = {'1', '.', '0', '.', '0', '-', 's', 't', 'm', '3', '2'}; + +// ECU serial number (DID F18C) +uint8_t const ecuSerialData[] + = {'O', 'B', 'S', 'W', '-', 'S', 'T', 'M', '3', '2', '-', '0', '0', '1'}; + +// HW version (DID F193) +uint8_t const hwVersionData[] = {'H', 'W', '-', '1', '.', '0'}; + +// Supplier ID (DID F18A) +uint8_t const supplierIdData[] = {'E', 'c', 'l', 'i', 'p', 's', 'e', '-', 'O', 'B', 'S', 'W'}; + +// Boot SW version (DID F180) +uint8_t const bootSwVersionData[] = {'B', 'O', 'O', 'T', '-', '1', '.', '0'}; +#endif + UdsSystem::UdsSystem( lifecycle::LifecycleManager& lManager, transport::ITransportSystem& transportSystem, @@ -42,7 +65,11 @@ UdsSystem::UdsSystem( transport::TransportConfiguration::DIAG_PAYLOAD_SIZE, ::busid::SELFDIAG, true, /* activate outgoing pending */ +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + true, /* accept all requests */ +#else false, /* accept all requests */ +#endif true, /* copy functional requests */ context} , _udsDispatcher( @@ -57,7 +84,28 @@ UdsSystem::UdsSystem( , _read22Cf01(0xCF01, responseData22Cf01) , _read22Cf02() , _write2eCf03(0xCF03, storedData2eCf03) +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES +, _readF190(0xF190, ::etl::span(vinData.data(), vinData.size())) +, _writeF190(0xF190, ::etl::span(vinData.data(), vinData.size())) +, _readF195(0xF195, swVersionData, sizeof(swVersionData)) +, _readF18C(0xF18C, ecuSerialData, sizeof(ecuSerialData)) +, _readF193(0xF193, hwVersionData, sizeof(hwVersionData)) +, _readF18A(0xF18A, supplierIdData, sizeof(supplierIdData)) +, _readF180(0xF180, bootSwVersionData, sizeof(bootSwVersionData)) +#endif , _testerPresent() +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES +, _ecuReset() +, _hardReset(_udsLifecycleConnector, _udsDispatcher) +, _securityAccess() +, _dtcManager() +, _clearDtc(_dtcManager) +, _readDtcInfo(_dtcManager) +, _controlDtcSetting(_dtcManager) +, _routineFF00(0xFF00U) +, _routineFF01(0xFF01U) +, _routineFF02(0xFF02U) +#endif , _context(context) , _timeout() { @@ -133,6 +181,46 @@ void UdsSystem::addDiagJobs() (void)_jobRoot.addAbstractDiagJob(_testerPresent); (void)_jobRoot.addAbstractDiagJob(_diagnosticSessionControl); (void)_jobRoot.addAbstractDiagJob(_communicationControl); + +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + // 11 - ECUReset + (void)_jobRoot.addAbstractDiagJob(_ecuReset); + (void)_jobRoot.addAbstractDiagJob(_hardReset); + + // 14 - ClearDiagnosticInformation + (void)_jobRoot.addAbstractDiagJob(_clearDtc); + + // 19 - ReadDTCInformation + (void)_jobRoot.addAbstractDiagJob(_readDtcInfo); + + // 22 - ReadDataByIdentifier (demo DIDs) + (void)_jobRoot.addAbstractDiagJob(_readF190); + (void)_jobRoot.addAbstractDiagJob(_readF195); + (void)_jobRoot.addAbstractDiagJob(_readF18C); + (void)_jobRoot.addAbstractDiagJob(_readF193); + (void)_jobRoot.addAbstractDiagJob(_readF18A); + (void)_jobRoot.addAbstractDiagJob(_readF180); + + // 27 - SecurityAccess + (void)_jobRoot.addAbstractDiagJob(_securityAccess); + + // 2E - WriteDataByIdentifier (demo DIDs) + (void)_jobRoot.addAbstractDiagJob(_writeF190); + + // 31 - Routine Control (demo routines) + (void)_jobRoot.addAbstractDiagJob(_routineFF00.getStartRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF00.getStopRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF00.getRequestRoutineResults()); + (void)_jobRoot.addAbstractDiagJob(_routineFF01.getStartRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF01.getStopRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF01.getRequestRoutineResults()); + (void)_jobRoot.addAbstractDiagJob(_routineFF02.getStartRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF02.getStopRoutine()); + (void)_jobRoot.addAbstractDiagJob(_routineFF02.getRequestRoutineResults()); + + // 85 - ControlDTCSetting + (void)_jobRoot.addAbstractDiagJob(_controlDtcSetting); +#endif } void UdsSystem::removeDiagJobs() @@ -156,6 +244,31 @@ void UdsSystem::removeDiagJobs() _jobRoot.removeAbstractDiagJob(_testerPresent); _jobRoot.removeAbstractDiagJob(_diagnosticSessionControl); _jobRoot.removeAbstractDiagJob(_communicationControl); + +#ifdef PLATFORM_SUPPORT_UDS_DEMO_SERVICES + _jobRoot.removeAbstractDiagJob(_ecuReset); + _jobRoot.removeAbstractDiagJob(_hardReset); + _jobRoot.removeAbstractDiagJob(_clearDtc); + _jobRoot.removeAbstractDiagJob(_readDtcInfo); + _jobRoot.removeAbstractDiagJob(_readF190); + _jobRoot.removeAbstractDiagJob(_readF195); + _jobRoot.removeAbstractDiagJob(_readF18C); + _jobRoot.removeAbstractDiagJob(_readF193); + _jobRoot.removeAbstractDiagJob(_readF18A); + _jobRoot.removeAbstractDiagJob(_readF180); + _jobRoot.removeAbstractDiagJob(_securityAccess); + _jobRoot.removeAbstractDiagJob(_writeF190); + _jobRoot.removeAbstractDiagJob(_routineFF00.getStartRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF00.getStopRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF00.getRequestRoutineResults()); + _jobRoot.removeAbstractDiagJob(_routineFF01.getStartRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF01.getStopRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF01.getRequestRoutineResults()); + _jobRoot.removeAbstractDiagJob(_routineFF02.getStartRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF02.getStopRoutine()); + _jobRoot.removeAbstractDiagJob(_routineFF02.getRequestRoutineResults()); + _jobRoot.removeAbstractDiagJob(_controlDtcSetting); +#endif } void UdsSystem::execute() {} diff --git a/executables/referenceApp/application/src/uds/Stm32ClearDtc.cpp b/executables/referenceApp/application/src/uds/Stm32ClearDtc.cpp new file mode 100644 index 00000000000..a6675e7eae9 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32ClearDtc.cpp @@ -0,0 +1,44 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32ClearDtc.h" + +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +static uint8_t const SID_CLEAR_DTC = 0x14U; + +Stm32ClearDtc::Stm32ClearDtc(Stm32DtcManager& dtcManager) +: Service(SID_CLEAR_DTC, 3U, 0U, DiagSession::ALL_SESSIONS()), _dtcManager(dtcManager) +{} + +DiagReturnCode::Type Stm32ClearDtc::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (requestLength < 3U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint32_t const groupOfDtc = (static_cast(request[0]) << 16U) + | (static_cast(request[1]) << 8U) + | static_cast(request[2]); + + _dtcManager.clearByGroup(groupOfDtc); + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32ControlDtcSetting.cpp b/executables/referenceApp/application/src/uds/Stm32ControlDtcSetting.cpp new file mode 100644 index 00000000000..d40fb105201 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32ControlDtcSetting.cpp @@ -0,0 +1,56 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32ControlDtcSetting.h" + +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +static uint8_t const SID_CONTROL_DTC_SETTING = 0x85U; +static uint8_t const SF_ON = 0x01U; +static uint8_t const SF_OFF = 0x02U; + +Stm32ControlDtcSetting::Stm32ControlDtcSetting(Stm32DtcManager& dtcManager) +: Service(SID_CONTROL_DTC_SETTING, 1U, 1U, DiagSession::ALL_SESSIONS()), _dtcManager(dtcManager) +{} + +DiagReturnCode::Type Stm32ControlDtcSetting::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (requestLength < 1U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint8_t const subFunction = request[0]; + + if (subFunction == SF_ON) + { + _dtcManager.setDtcSettingEnabled(true); + } + else if (subFunction == SF_OFF) + { + _dtcManager.setDtcSettingEnabled(false); + } + else + { + return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; + } + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(subFunction); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32DemoRoutine.cpp b/executables/referenceApp/application/src/uds/Stm32DemoRoutine.cpp new file mode 100644 index 00000000000..42d5b2b83a4 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32DemoRoutine.cpp @@ -0,0 +1,85 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32DemoRoutine.h" + +#include "uds/UdsConstants.h" +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +Stm32DemoRoutine::Stm32DemoRoutine(uint16_t const routineId) +: RoutineControlJob(_implRequest, sizeof(_implRequest), &_stopNode, &_resultsNode) +, _stopNode(_stopImplRequest, *this) +, _resultsNode(_resultsImplRequest, *this) +{ + // Start routine: [0x31, 0x01, routineId_hi, routineId_lo] + _implRequest[0] = ServiceId::ROUTINE_CONTROL; + _implRequest[1] = 0x01U; + _implRequest[2] = static_cast((routineId >> 8U) & 0xFFU); + _implRequest[3] = static_cast(routineId & 0xFFU); + + // Stop routine: [0x31, 0x02, routineId_hi, routineId_lo] + _stopImplRequest[0] = ServiceId::ROUTINE_CONTROL; + _stopImplRequest[1] = 0x02U; + _stopImplRequest[2] = _implRequest[2]; + _stopImplRequest[3] = _implRequest[3]; + + // Request results: [0x31, 0x03, routineId_hi, routineId_lo] + _resultsImplRequest[0] = ServiceId::ROUTINE_CONTROL; + _resultsImplRequest[1] = 0x03U; + _resultsImplRequest[2] = _implRequest[2]; + _resultsImplRequest[3] = _implRequest[3]; + + disableSequenceCheck(); +} + +DiagReturnCode::Type Stm32DemoRoutine::start( + IncomingDiagConnection& connection, + uint8_t const* const /* request */, + uint16_t const /* requestLength */) +{ + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x01U); + (void)response.appendUint8(_implRequest[2]); + (void)response.appendUint8(_implRequest[3]); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32DemoRoutine::stop( + IncomingDiagConnection& connection, + uint8_t const* const /* request */, + uint16_t const /* requestLength */) +{ + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x02U); + (void)response.appendUint8(_implRequest[2]); + (void)response.appendUint8(_implRequest[3]); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32DemoRoutine::requestResults( + IncomingDiagConnection& connection, + uint8_t const* const /* request */, + uint16_t const /* requestLength */) +{ + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x03U); + (void)response.appendUint8(_implRequest[2]); + (void)response.appendUint8(_implRequest[3]); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32DtcManager.cpp b/executables/referenceApp/application/src/uds/Stm32DtcManager.cpp new file mode 100644 index 00000000000..588288579f6 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32DtcManager.cpp @@ -0,0 +1,156 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32DtcManager.h" + +namespace uds +{ + +Stm32DtcManager::Stm32DtcManager() : _dtcs{}, _dtcCount(0U), _dtcSettingEnabled(true) {} + +void Stm32DtcManager::reportFault(uint32_t const dtcNumber) +{ + if (!_dtcSettingEnabled) + { + return; + } + + DtcEntry* entry = findOrCreate(dtcNumber); + if (entry != nullptr) + { + entry->statusByte |= STATUS_TEST_FAILED | STATUS_CONFIRMED; + entry->statusByte &= static_cast(~STATUS_TEST_NOT_COMPLETE); + } +} + +void Stm32DtcManager::clearAll() +{ + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + _dtcs[i].statusByte = 0U; + _dtcs[i].active = false; + } + _dtcCount = 0U; +} + +void Stm32DtcManager::clearByGroup(uint32_t const groupOfDtc) +{ + if (groupOfDtc == 0xFFFFFFU) + { + clearAll(); + return; + } + + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active && (_dtcs[i].dtcNumber == groupOfDtc)) + { + _dtcs[i].statusByte = 0U; + _dtcs[i].active = false; + } + } +} + +uint16_t Stm32DtcManager::getCountByStatusMask(uint8_t const statusMask) const +{ + uint16_t count = 0U; + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active && ((_dtcs[i].statusByte & statusMask) != 0U)) + { + ++count; + } + } + return count; +} + +uint8_t Stm32DtcManager::getDtcsByStatusMask( + uint8_t const statusMask, uint8_t* const buffer, uint16_t const bufferSize) const +{ + uint8_t count = 0U; + uint16_t offset = 0U; + + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active && ((_dtcs[i].statusByte & statusMask) != 0U)) + { + if ((offset + 4U) > bufferSize) + { + break; + } + // DTC high, mid, low bytes + status byte + buffer[offset] = static_cast((_dtcs[i].dtcNumber >> 16U) & 0xFFU); + buffer[offset + 1] = static_cast((_dtcs[i].dtcNumber >> 8U) & 0xFFU); + buffer[offset + 2] = static_cast(_dtcs[i].dtcNumber & 0xFFU); + buffer[offset + 3] = _dtcs[i].statusByte; + offset += 4U; + ++count; + } + } + return count; +} + +uint8_t Stm32DtcManager::getSupportedDtcs(uint8_t* const buffer, uint16_t const bufferSize) const +{ + uint8_t count = 0U; + uint16_t offset = 0U; + + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active) + { + if ((offset + 4U) > bufferSize) + { + break; + } + buffer[offset] = static_cast((_dtcs[i].dtcNumber >> 16U) & 0xFFU); + buffer[offset + 1] = static_cast((_dtcs[i].dtcNumber >> 8U) & 0xFFU); + buffer[offset + 2] = static_cast(_dtcs[i].dtcNumber & 0xFFU); + buffer[offset + 3] = _dtcs[i].statusByte; + offset += 4U; + ++count; + } + } + return count; +} + +Stm32DtcManager::DtcEntry* Stm32DtcManager::findOrCreate(uint32_t const dtcNumber) +{ + DtcEntry* freeSlot = nullptr; + for (uint8_t i = 0U; i < _dtcCount; ++i) + { + if (_dtcs[i].active && (_dtcs[i].dtcNumber == dtcNumber)) + { + return &_dtcs[i]; + } + // Reuse cleared (inactive) slots so fault/clear cycles don't exhaust the array. + if ((!_dtcs[i].active) && (freeSlot == nullptr)) + { + freeSlot = &_dtcs[i]; + } + } + + if ((freeSlot == nullptr) && (_dtcCount < MAX_DTCS)) + { + freeSlot = &_dtcs[_dtcCount]; + ++_dtcCount; + } + + if (freeSlot != nullptr) + { + freeSlot->dtcNumber = dtcNumber; + freeSlot->statusByte = 0U; + freeSlot->active = true; + } + + return freeSlot; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32ReadDtcInfo.cpp b/executables/referenceApp/application/src/uds/Stm32ReadDtcInfo.cpp new file mode 100644 index 00000000000..a5c73e2c59b --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32ReadDtcInfo.cpp @@ -0,0 +1,125 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32ReadDtcInfo.h" + +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +static uint8_t const SID_READ_DTC_INFO = 0x19U; +static uint8_t const SF_REPORT_NUM_BY_MASK = 0x01U; +static uint8_t const SF_REPORT_BY_MASK = 0x02U; +static uint8_t const SF_REPORT_SUPPORTED = 0x0AU; + +// ISO 14229: DTC status availability mask (all bits supported) +static uint8_t const STATUS_AVAILABILITY_MASK = 0xFFU; + +Stm32ReadDtcInfo::Stm32ReadDtcInfo(Stm32DtcManager const& dtcManager) +: Service(SID_READ_DTC_INFO, DiagSession::ALL_SESSIONS()), _dtcManager(dtcManager) +{} + +DiagReturnCode::Type Stm32ReadDtcInfo::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (requestLength < 1U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint8_t const subFunction = request[0]; + + switch (subFunction) + { + case SF_REPORT_NUM_BY_MASK: + { + if (requestLength < 2U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + return handleReportNumberByStatusMask(connection, request[1]); + } + case SF_REPORT_BY_MASK: + { + if (requestLength < 2U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + return handleReportByStatusMask(connection, request[1]); + } + case SF_REPORT_SUPPORTED: + { + return handleReportSupportedDtc(connection); + } + default: + { + return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; + } + } +} + +DiagReturnCode::Type Stm32ReadDtcInfo::handleReportNumberByStatusMask( + IncomingDiagConnection& connection, uint8_t const statusMask) +{ + uint16_t const count = _dtcManager.getCountByStatusMask(statusMask); + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(SF_REPORT_NUM_BY_MASK); + (void)response.appendUint8(STATUS_AVAILABILITY_MASK); + // DTC format identifier (ISO 14229-1 format) + (void)response.appendUint8(0x01U); + // DTC count high/low + (void)response.appendUint8(static_cast((count >> 8U) & 0xFFU)); + (void)response.appendUint8(static_cast(count & 0xFFU)); + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32ReadDtcInfo::handleReportByStatusMask( + IncomingDiagConnection& connection, uint8_t const statusMask) +{ + uint8_t dtcBuffer[DTC_BUFFER_SIZE]; + uint8_t const dtcCount + = _dtcManager.getDtcsByStatusMask(statusMask, dtcBuffer, DTC_BUFFER_SIZE); + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(SF_REPORT_BY_MASK); + (void)response.appendUint8(STATUS_AVAILABILITY_MASK); + + for (uint16_t i = 0U; i < (static_cast(dtcCount) * 4U); ++i) + { + (void)response.appendUint8(dtcBuffer[i]); + } + + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32ReadDtcInfo::handleReportSupportedDtc(IncomingDiagConnection& connection) +{ + uint8_t dtcBuffer[DTC_BUFFER_SIZE]; + uint8_t const dtcCount = _dtcManager.getSupportedDtcs(dtcBuffer, DTC_BUFFER_SIZE); + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(SF_REPORT_SUPPORTED); + (void)response.appendUint8(STATUS_AVAILABILITY_MASK); + + for (uint16_t i = 0U; i < (static_cast(dtcCount) * 4U); ++i) + { + (void)response.appendUint8(dtcBuffer[i]); + } + + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32SecurityAccess.cpp b/executables/referenceApp/application/src/uds/Stm32SecurityAccess.cpp new file mode 100644 index 00000000000..9f573d8f682 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32SecurityAccess.cpp @@ -0,0 +1,136 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32SecurityAccess.h" + +#include "uds/UdsConstants.h" +#include "uds/connection/IncomingDiagConnection.h" +#include "uds/session/DiagSession.h" + +namespace uds +{ + +Stm32SecurityAccess::Stm32SecurityAccess() +: Service(ServiceId::SECURITY_ACCESS, DiagSession::ALL_SESSIONS()) +, _seed(0x12345678U) +, _seedIssued(false) +, _unlocked(false) +, _failedAttempts(0U) +, _lockedUntilReset(false) +{} + +uint32_t Stm32SecurityAccess::nextSeed() +{ + _seed = (_seed * LCG_A + LCG_C) | 0x00000001U; + return _seed; +} + +DiagReturnCode::Type Stm32SecurityAccess::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (requestLength < 1U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint8_t const subFunction = request[0]; + + if (subFunction == 0x01U) + { + return handleRequestSeed(connection); + } + else if (subFunction == 0x02U) + { + return handleSendKey(connection, request, requestLength); + } + + return DiagReturnCode::ISO_SUBFUNCTION_NOT_SUPPORTED; +} + +DiagReturnCode::Type Stm32SecurityAccess::handleRequestSeed(IncomingDiagConnection& connection) +{ + if (_lockedUntilReset) + { + return DiagReturnCode::ISO_EXCEEDED_NUMS_OF_ATTEMPTS; + } + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x01U); // echo subfunction + + if (_unlocked) + { + // Already unlocked - return 4-byte zero seed per ISO 14229. + (void)response.appendUint8(0x00U); + (void)response.appendUint8(0x00U); + (void)response.appendUint8(0x00U); + (void)response.appendUint8(0x00U); + } + else + { + uint32_t const seed = nextSeed(); + _seedIssued = true; + // 4-byte big-endian seed. + (void)response.appendUint8(static_cast((seed >> 24U) & 0xFFU)); + (void)response.appendUint8(static_cast((seed >> 16U) & 0xFFU)); + (void)response.appendUint8(static_cast((seed >> 8U) & 0xFFU)); + (void)response.appendUint8(static_cast(seed & 0xFFU)); + } + + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32SecurityAccess::handleSendKey( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + if (_lockedUntilReset) + { + return DiagReturnCode::ISO_EXCEEDED_NUMS_OF_ATTEMPTS; + } + + if (!_seedIssued) + { + return DiagReturnCode::ISO_REQUEST_SEQUENCE_ERROR; + } + + if (requestLength < 3U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + uint16_t const receivedKey + = (static_cast(request[1]) << 8U) | static_cast(request[2]); + uint16_t const expectedKey = static_cast(_seed ^ XOR_SECRET); + + if (receivedKey == expectedKey) + { + _unlocked = true; + _seedIssued = false; + _failedAttempts = 0U; + + PositiveResponse& response = connection.releaseRequestGetResponse(); + (void)response.appendUint8(0x02U); // echo subfunction + (void)connection.sendPositiveResponseInternal(response.getLength(), *this); + return DiagReturnCode::OK; + } + + _failedAttempts++; + _seedIssued = false; + + if (_failedAttempts >= MAX_FAILED_ATTEMPTS) + { + _lockedUntilReset = true; + return DiagReturnCode::ISO_EXCEEDED_NUMS_OF_ATTEMPTS; + } + + return DiagReturnCode::ISO_INVALID_KEY; +} + +} // namespace uds diff --git a/executables/referenceApp/application/src/uds/Stm32WriteVin.cpp b/executables/referenceApp/application/src/uds/Stm32WriteVin.cpp new file mode 100644 index 00000000000..ad6c9b90911 --- /dev/null +++ b/executables/referenceApp/application/src/uds/Stm32WriteVin.cpp @@ -0,0 +1,69 @@ +/******************************************************************************** + * Copyright (c) 2026 An Dao + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "uds/Stm32WriteVin.h" + +#include "uds/connection/IncomingDiagConnection.h" + +#include + +namespace uds +{ + +Stm32WriteVin::Stm32WriteVin(uint16_t const identifier, ::etl::span const& memory) +: DataIdentifierJob(_implementedRequest), _memory(memory) +{ + _implementedRequest[0] = 0x2EU; + _implementedRequest[1] = static_cast((identifier >> 8U) & 0xFFU); + _implementedRequest[2] = static_cast(identifier & 0xFFU); +} + +DiagReturnCode::Type +Stm32WriteVin::verify(uint8_t const* const request, uint16_t const requestLength) +{ + // request[0..1] = DID high/low (SID already stripped by parent) + if (!compare(request, getImplementedRequest() + 1U, 2U)) + { + return DiagReturnCode::NOT_RESPONSIBLE; + } + + // Must have at least 2 DID bytes + 1 data byte + if (requestLength < 3U) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + // Data portion must fit in the memory buffer + uint16_t const dataLen = requestLength - 2U; + if (dataLen > _memory.size()) + { + return DiagReturnCode::ISO_INVALID_FORMAT; + } + + return DiagReturnCode::OK; +} + +DiagReturnCode::Type Stm32WriteVin::process( + IncomingDiagConnection& connection, uint8_t const* const request, uint16_t const requestLength) +{ + // Framework already stripped DID bytes; request is data-only. + for (uint16_t i = 0U; i < requestLength; ++i) + { + _memory[i] = request[i]; + } + // Zero-fill the tail so a short write leaves no stale bytes behind. + std::fill(_memory.begin() + requestLength, _memory.end(), static_cast(0U)); + + (void)connection.releaseRequestGetResponse(); + (void)connection.sendPositiveResponse(*this); + return DiagReturnCode::OK; +} + +} // namespace uds diff --git a/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake b/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake index 30532eab3bc..aacdaa15a62 100644 --- a/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake +++ b/executables/referenceApp/platforms/nucleo_f413zh/Options.cmake @@ -41,6 +41,9 @@ set(PLATFORM_SUPPORT_OBD_UDS_ADDRESSING set(PLATFORM_SUPPORT_PROGRAMMING_SESSION ON CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_UDS_DEMO_SERVICES + ON + CACHE BOOL "" FORCE) set(PLATFORM_SUPPORT_WATCHDOG OFF CACHE BOOL "" FORCE) diff --git a/executables/referenceApp/platforms/nucleo_g474re/Options.cmake b/executables/referenceApp/platforms/nucleo_g474re/Options.cmake index 50ca7022af8..0fcbecc817d 100644 --- a/executables/referenceApp/platforms/nucleo_g474re/Options.cmake +++ b/executables/referenceApp/platforms/nucleo_g474re/Options.cmake @@ -38,6 +38,9 @@ set(PLATFORM_SUPPORT_OBD_UDS_ADDRESSING set(PLATFORM_SUPPORT_PROGRAMMING_SESSION ON CACHE BOOL "" FORCE) +set(PLATFORM_SUPPORT_UDS_DEMO_SERVICES + ON + CACHE BOOL "" FORCE) set(PLATFORM_SUPPORT_WATCHDOG OFF CACHE BOOL "" FORCE)