diff --git a/.cproject b/.cproject
new file mode 100644
index 0000000000..84a27985aa
--- /dev/null
+++ b/.cproject
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.project b/.project
new file mode 100644
index 0000000000..704295e0c7
--- /dev/null
+++ b/.project
@@ -0,0 +1,27 @@
+
+
+ chibios-contrib
+
+
+
+
+
+ org.eclipse.cdt.managedbuilder.core.genmakebuilder
+ clean,full,incremental,
+
+
+
+
+ org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder
+ full,incremental,
+
+
+
+
+
+ org.eclipse.cdt.core.cnature
+ org.eclipse.cdt.core.ccnature
+ org.eclipse.cdt.managedbuilder.core.managedBuildNature
+ org.eclipse.cdt.managedbuilder.core.ScannerConfigNature
+
+
diff --git a/demos/SK32/GENERIC_SK32_F077/Makefile b/demos/SK32/GENERIC_SK32_F077/Makefile
new file mode 100644
index 0000000000..19851b88f5
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = ch
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/demos/SK32/GENERIC_SK32_F077/_dry.txt b/demos/SK32/GENERIC_SK32_F077/_dry.txt
new file mode 100644
index 0000000000..4e571d45fa
Binary files /dev/null and b/demos/SK32/GENERIC_SK32_F077/_dry.txt differ
diff --git a/demos/SK32/GENERIC_SK32_F077/build_log.txt b/demos/SK32/GENERIC_SK32_F077/build_log.txt
new file mode 100644
index 0000000000..4260ae628d
Binary files /dev/null and b/demos/SK32/GENERIC_SK32_F077/build_log.txt differ
diff --git a/demos/SK32/GENERIC_SK32_F077/build_out.txt b/demos/SK32/GENERIC_SK32_F077/build_out.txt
new file mode 100644
index 0000000000..535475390b
Binary files /dev/null and b/demos/SK32/GENERIC_SK32_F077/build_out.txt differ
diff --git a/demos/SK32/GENERIC_SK32_F077/build_out2.txt b/demos/SK32/GENERIC_SK32_F077/build_out2.txt
new file mode 100644
index 0000000000..47997c6a79
Binary files /dev/null and b/demos/SK32/GENERIC_SK32_F077/build_out2.txt differ
diff --git a/demos/SK32/GENERIC_SK32_F077/build_out3.txt b/demos/SK32/GENERIC_SK32_F077/build_out3.txt
new file mode 100644
index 0000000000..19ff58f44a
Binary files /dev/null and b/demos/SK32/GENERIC_SK32_F077/build_out3.txt differ
diff --git a/demos/SK32/GENERIC_SK32_F077/cfg/chconf.h b/demos/SK32/GENERIC_SK32_F077/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/demos/SK32/GENERIC_SK32_F077/cfg/halconf.h b/demos/SK32/GENERIC_SK32_F077/cfg/halconf.h
new file mode 100644
index 0000000000..cb428219fc
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077/cfg/halconf.h
@@ -0,0 +1,568 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL TRUE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB TRUE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/demos/SK32/GENERIC_SK32_F077/cfg/mcuconf.h b/demos/SK32/GENERIC_SK32_F077/cfg/mcuconf.h
new file mode 100644
index 0000000000..294d8d6908
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077/cfg/mcuconf.h
@@ -0,0 +1,270 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for
+ * the USB IP. The SK32 port provides native drivers for the core platform
+ * (clocks, SysTick-based ST, ISR aggregation, GPIO/PAL, USB) and reuses the
+ * shared ChibiOS STM32 low level drivers (TIMv1, USARTv2, ADCv1, SPIv2,
+ * I2Cv2, ...) for the remaining peripherals. This mcuconf.h must therefore
+ * satisfy two naming schemes:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST settings (used by the SK32 HAL
+ * low level drivers), and
+ * - STM32F0xx_MCUCONF + the STM32_* settings consumed by the shared
+ * ChibiOS STM32 low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define STM32F0xx_MCUCONF
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * STM32 HAL driver system settings (shared LLD requirements).
+ */
+#define STM32_NO_INIT FALSE
+#define STM32_PVD_ENABLE FALSE
+#define STM32_PLS STM32_PLS_LEV0
+#define STM32_HSI_ENABLED TRUE
+#define STM32_HSI14_ENABLED TRUE
+#define STM32_HSI48_ENABLED FALSE
+#define STM32_LSI_ENABLED TRUE
+#define STM32_HSE_ENABLED FALSE
+#define STM32_LSE_ENABLED FALSE
+#define STM32_SW STM32_SW_PLL
+#define STM32_PLLSRC STM32_PLLSRC_HSI_DIV2
+#define STM32_PREDIV_VALUE 1
+#define STM32_PLLMUL_VALUE 12
+#define STM32_HPRE STM32_HPRE_DIV1
+#define STM32_PPRE STM32_PPRE_DIV1
+#define STM32_MCOSEL STM32_MCOSEL_NOCLOCK
+#define STM32_MCOPRE STM32_MCOPRE_DIV1
+#define STM32_PLLNODIV STM32_PLLNODIV_DIV2
+#define STM32_USBSW STM32_USBSW_HSI48
+#define STM32_CECSW STM32_CECSW_HSI
+#define STM32_I2C1SW STM32_I2C1SW_HSI
+#define STM32_USART1SW STM32_USART1SW_PCLK
+#define STM32_RTCSEL STM32_RTCSEL_LSI
+
+/*
+ * IRQ system settings.
+ * The EXTI line IRQ priorities are configured through the native
+ * SK32_IRQ_EXTI*_PRIORITY defaults in sk32_isr.h (priority 3), the shared
+ * STM32 EXTIv1/GPIOv2 layers are not used by this port.
+ */
+#define STM32_IRQ_USART1_PRIORITY 3
+#define STM32_IRQ_USART2_PRIORITY 3
+#define STM32_IRQ_USART3_8_PRIORITY 3
+
+/*
+ * ADC driver system settings.
+ */
+#define STM32_ADC_USE_ADC1 FALSE
+#define STM32_ADC_ADC1_CFGR2 ADC_CFGR2_CKMODE_ADCCLK
+#define STM32_ADC_ADC1_DMA_PRIORITY 2
+#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 2
+#define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(1, 1)
+
+/*
+ * CAN driver system settings.
+ */
+#define STM32_CAN_USE_CAN1 FALSE
+#define STM32_CAN_CAN1_IRQ_PRIORITY 3
+
+/*
+ * DAC driver system settings.
+ */
+#define STM32_DAC_DUAL_MODE FALSE
+#define STM32_DAC_USE_DAC1_CH1 FALSE
+#define STM32_DAC_USE_DAC1_CH2 FALSE
+#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY 2
+#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY 2
+#define STM32_DAC_DAC1_CH1_DMA_PRIORITY 2
+#define STM32_DAC_DAC1_CH2_DMA_PRIORITY 2
+#define STM32_DAC_DAC1_CH1_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_DAC_DAC1_CH2_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+
+/*
+ * GPT driver system settings.
+ */
+#define STM32_GPT_USE_TIM1 FALSE
+#define STM32_GPT_USE_TIM2 FALSE
+#define STM32_GPT_USE_TIM3 FALSE
+#define STM32_GPT_USE_TIM6 FALSE
+#define STM32_GPT_USE_TIM14 FALSE
+#define STM32_GPT_TIM1_IRQ_PRIORITY 2
+#define STM32_GPT_TIM2_IRQ_PRIORITY 2
+#define STM32_GPT_TIM3_IRQ_PRIORITY 2
+#define STM32_GPT_TIM6_IRQ_PRIORITY 2
+#define STM32_GPT_TIM14_IRQ_PRIORITY 2
+
+/*
+ * I2C driver system settings.
+ */
+#define STM32_I2C_USE_I2C1 FALSE
+#define STM32_I2C_USE_I2C2 FALSE
+#define STM32_I2C_BUSY_TIMEOUT 50
+#define STM32_I2C_I2C1_IRQ_PRIORITY 3
+#define STM32_I2C_I2C2_IRQ_PRIORITY 3
+#define STM32_I2C_USE_DMA TRUE
+#define STM32_I2C_I2C1_DMA_PRIORITY 1
+#define STM32_I2C_I2C2_DMA_PRIORITY 1
+#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure")
+
+/*
+ * I2S driver system settings.
+ */
+#define STM32_I2S_USE_SPI1 FALSE
+#define STM32_I2S_USE_SPI2 FALSE
+#define STM32_I2S_SPI1_MODE (STM32_I2S_MODE_MASTER | \
+ STM32_I2S_MODE_RX)
+#define STM32_I2S_SPI2_MODE (STM32_I2S_MODE_MASTER | \
+ STM32_I2S_MODE_RX)
+#define STM32_I2S_SPI1_IRQ_PRIORITY 2
+#define STM32_I2S_SPI2_IRQ_PRIORITY 2
+#define STM32_I2S_SPI1_DMA_PRIORITY 1
+#define STM32_I2S_SPI2_DMA_PRIORITY 1
+#define STM32_I2S_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_I2S_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_I2S_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_I2S_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure")
+
+/*
+ * ICU driver system settings.
+ */
+#define STM32_ICU_USE_TIM1 FALSE
+#define STM32_ICU_USE_TIM2 FALSE
+#define STM32_ICU_USE_TIM3 FALSE
+#define STM32_ICU_TIM1_IRQ_PRIORITY 3
+#define STM32_ICU_TIM2_IRQ_PRIORITY 3
+#define STM32_ICU_TIM3_IRQ_PRIORITY 3
+
+/*
+ * PWM driver system settings.
+ */
+#define STM32_PWM_USE_ADVANCED FALSE
+#define STM32_PWM_USE_TIM1 FALSE
+#define STM32_PWM_USE_TIM2 FALSE
+#define STM32_PWM_USE_TIM3 FALSE
+#define STM32_PWM_TIM1_IRQ_PRIORITY 3
+#define STM32_PWM_TIM2_IRQ_PRIORITY 3
+#define STM32_PWM_TIM3_IRQ_PRIORITY 3
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches, USART2 is mapped on PA2/PA3 (alternate 1)
+ * as documented in board.h.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 TRUE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ */
+#define STM32_SPI_USE_SPI1 FALSE
+#define STM32_SPI_USE_SPI2 FALSE
+#define STM32_SPI_SPI1_DMA_PRIORITY 1
+#define STM32_SPI_SPI2_DMA_PRIORITY 1
+#define STM32_SPI_SPI1_IRQ_PRIORITY 2
+#define STM32_SPI_SPI2_IRQ_PRIORITY 2
+#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure")
+
+/*
+ * ST driver system settings.
+ */
+#define STM32_ST_IRQ_PRIORITY 2
+#define STM32_ST_USE_TIMER 2
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode, the STM32_ST_* settings above are only kept for the shared
+ * STM32 low level drivers.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+/*
+ * UART driver system settings.
+ */
+#define STM32_UART_USE_USART1 FALSE
+#define STM32_UART_USE_USART2 FALSE
+#define STM32_UART_USE_USART3 FALSE
+#define STM32_UART_USE_UART4 FALSE
+#define STM32_UART_USART1_DMA_PRIORITY 0
+#define STM32_UART_USART2_DMA_PRIORITY 0
+#define STM32_UART_USART3_DMA_PRIORITY 0
+#define STM32_UART_UART4_DMA_PRIORITY 0
+#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_UART_UART4_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
+#define STM32_UART_UART4_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
+#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure")
+
+/*
+ * USB driver system settings.
+ * The SK32 musbfsfc USB LLD (not the STM32 USBv1 LLD) is used by this port.
+ */
+#define STM32_USB_USE_USB1 TRUE
+#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE
+#define STM32_USB_USB1_LP_IRQ_PRIORITY 3
+
+/*
+ * WDG driver system settings.
+ */
+#define STM32_WDG_USE_IWDG FALSE
+
+#endif /* MCUCONF_H */
diff --git a/demos/SK32/GENERIC_SK32_F077/main.c b/demos/SK32/GENERIC_SK32_F077/main.c
new file mode 100644
index 0000000000..f70d722129
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077/main.c
@@ -0,0 +1,67 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+#include "ch.h"
+#include "hal.h"
+
+/*===========================================================================*/
+/* Generic code. */
+/*===========================================================================*/
+
+/*
+ * LED blinker thread, times are in milliseconds.
+ */
+static THD_WORKING_AREA(waThread1, 256);
+static THD_FUNCTION(Thread1, arg) {
+ (void)arg;
+ chRegSetThreadName("blinker");
+ while (true) {
+ palToggleLine(LINE_LED_GREEN);
+ chThdSleepMilliseconds(500);
+ }
+}
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ palSetLineMode(LINE_LED_GREEN, PAL_MODE_OUTPUT_PUSHPULL);
+
+ /*
+ * Creates the blinker thread.
+ */
+ chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);
+
+ /*
+ * Normal main() thread activity.
+ */
+ while (true) {
+ palToggleLine(LINE_LED_GREEN);
+ chThdSleepMilliseconds(500);
+ }
+}
diff --git a/demos/SK32/GENERIC_SK32_F077/make_check.txt b/demos/SK32/GENERIC_SK32_F077/make_check.txt
new file mode 100644
index 0000000000..0945847dab
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077/make_check.txt
@@ -0,0 +1,31 @@
+Compiling hal_safety.c
+Compiling hal.c
+Compiling hal_st.c
+Compiling hal_buffered_serial.c
+Compiling hal_buffers.c
+Compiling hal_queues.c
+Compiling hal_mmcsd.c
+Compiling hal_pal.c
+Compiling hal_usb.c
+Compiling hal_flash.c
+Compiling nvic.c
+Compiling stm32_isr.c
+Compiling hal_lld.c
+Compiling hal_efl_lld.c
+Compiling stm32_dma.c
+Compiling stm32_exti.c
+Compiling hal_pal_lld.c
+Compiling hal_st_lld.c
+Compiling hal_usb_lld.c
+Compiling board.c
+Compiling main.c
+Linking build/ch.elf
+Creating build/ch.hex
+Creating build/ch.bin
+Creating build/ch.dmp
+Creating build/ch.list
+
+
+Done
+ text data bss dec hex filename
+ 8132 4 8184 16320 3fc0 build/ch.elf
diff --git a/demos/SK32/GENERIC_SK32_F077_PROBE/Makefile b/demos/SK32/GENERIC_SK32_F077_PROBE/Makefile
new file mode 100644
index 0000000000..19851b88f5
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_PROBE/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = ch
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/demos/SK32/GENERIC_SK32_F077_PROBE/build_probe1.txt b/demos/SK32/GENERIC_SK32_F077_PROBE/build_probe1.txt
new file mode 100644
index 0000000000..12e75cc73f
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_PROBE/build_probe1.txt
@@ -0,0 +1,157 @@
+Compiler Options
+arm-none-eabi-gcc -c -mcpu=cortex-m0 -mthumb -O2 -ggdb -fomit-frame-pointer -falign-functions=16 -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wundef -Wstrict-prototypes -Wa,-alms=./build/lst/ -DCORTEX_USE_FPU=FALSE -DSK32F077xB -MD -MP -MF ./.dep/build.d -I. -I../../../../chibios-contrib/os/common/ext/CMSIS/SK32/SK32F0xx -I../../../../chibios-contrib/os/common/startup/ARMCMx/devices/SK32F0xx -I../../../../chibios-contrib/os/hal/boards/GENERIC_SK32_F077 -I../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx -I../../../../chibios/os/common/ext/ARM/CMSIS/Core/Include -I../../../../chibios/os/common/oop/include -I../../../../chibios/os/common/portability/GCC -I../../../../chibios/os/common/ports/ARM-common -I../../../../chibios/os/common/ports/ARMv6-M -I../../../../chibios/os/common/startup/ARMCMx/compilers/GCC -I../../../../chibios/os/common/startup/ARMCMx/compilers/GCC/ld -I../../../../chibios/os/hal/include -I../../../../chibios/os/hal/osal/rt-nil -I../../../../chibios/os/hal/ports/STM32/LLD/ADCv1 -I../../../../chibios/os/hal/ports/STM32/LLD/CANv1 -I../../../../chibios/os/hal/ports/STM32/LLD/DACv1 -I../../../../chibios/os/hal/ports/STM32/LLD/DMAv1 -I../../../../chibios/os/hal/ports/STM32/LLD/I2Cv2 -I../../../../chibios/os/hal/ports/STM32/LLD/RTCv2 -I../../../../chibios/os/hal/ports/STM32/LLD/SPIv2 -I../../../../chibios/os/hal/ports/STM32/LLD/TIMv1 -I../../../../chibios/os/hal/ports/STM32/LLD/xWDGv1 -I../../../../chibios/os/hal/ports/common/ARMCMx -I../../../../chibios/os/license -I../../../../chibios/os/oslib/include -I../../../../chibios/os/rt/include -I./cfg main.c -o main.o
+
+Compiling crt0_v6m.S
+Compiling vectors.S
+Compiling chcoreasm.S
+Compiling crt1.c
+Compiling oop_base_object.c
+Compiling hal.c
+make.exe : In file included from ../../../../chibios/os/hal/include/hal_adc.h:101,
+At line:1 char:68
++ ... \msys64\usr\bin;C:\msys64\ucrt64\bin;"+$env:PATH; & make 2>&1 | Out-F ...
++ ~~~~~~~~~~~
+ + CategoryInfo : NotSpecified: (In file include.../hal_adc.h:101,:String) [], RemoteException
+ + FullyQualifiedErrorId : NativeCommandError
+
+ from ../../../../chibios/os/hal/include/hal.h:324,
+ from ../../../../chibios/os/hal/src/hal.c:25:
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:97:6: warning: 'STM32_ADC_SUPPORTS_OVERSAMPLING' is not
+defined, evaluates to '0' [-Wundef]
+ 97 | #if (STM32_ADC_SUPPORTS_OVERSAMPLING == TRUE) || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:166:6: warning: 'STM32_ADC_SUPPORTS_PRESCALER' is not de
+fined, evaluates to '0' [-Wundef]
+ 166 | #if (STM32_ADC_SUPPORTS_PRESCALER == TRUE) || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:185:2: error: #error "ADCv1 only supports F0 and L0 STM3
+2 devices"
+ 185 | #error "ADCv1 only supports F0 and L0 STM32 devices"
+ | ^~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:196:2: error: #error "STM32_HAS_ADC1 not defined in regi
+stry"
+ 196 | #error "STM32_HAS_ADC1 not defined in registry"
+ | ^~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:200:2: error: #error "STM32_ADC_SUPPORTS_PRESCALER not d
+efined in registry"
+ 200 | #error "STM32_ADC_SUPPORTS_PRESCALER not defined in registry"
+ | ^~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:222:2: error: #error "ADC driver activated but no ADC pe
+ripheral assigned"
+ 222 | #error "ADC driver activated but no ADC peripheral assigned"
+ | ^~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:239:33: error: missing binary operator before token '('
+ 239 | !STM32_DMA_IS_VALID_PRIORITY(STM32_ADC_ADC1_DMA_PRIORITY)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:247:5: warning: 'STM32_DMA_SUPPORTS_DMAMUX' is not defin
+ed, evaluates to '0' [-Wundef]
+ 247 | #if STM32_DMA_SUPPORTS_DMAMUX
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:253:27: error: missing binary operator before token '('
+ 253 | !STM32_DMA_IS_VALID_ID(STM32_ADC_ADC1_DMA_STREAM, STM32_ADC1_DMA_MSK)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:260:5: warning: 'STM32_ADC_SUPPORTS_PRESCALER' is not de
+fined, evaluates to '0' [-Wundef]
+ 260 | #if STM32_ADC_SUPPORTS_PRESCALER == TRUE
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/ADCv1/hal_adc_lld.h:324:9: error: unknown type name 'stm32_dma_stream_t'
+ 324 | const stm32_dma_stream_t *dmastp; \
+ | ^~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/include/hal_adc.h:205:3: note: in expansion of macro 'adc_lld_driver_fields'
+ 205 | adc_lld_driver_fields
+ | ^~~~~~~~~~~~~~~~~~~~~
+In file included from ../../../../chibios/os/hal/include/hal_spi_v1.h:136,
+ from ../../../../chibios/os/hal/include/hal_spi.h:35,
+ from ../../../../chibios/os/hal/include/hal.h:339:
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:215:28: warning: 'STM32_HAS_SPI1' is not defined, evalua
+tes to '0' [-Wundef]
+ 215 | #if STM32_SPI_USE_SPI1 && !STM32_HAS_SPI1
+ | ^~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:216:2: error: #error "SPI1 not present in the selected d
+evice"
+ 216 | #error "SPI1 not present in the selected device"
+ | ^~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:275:6: warning: 'STM32_DMA_IS_VALID_PRIORITY' is not def
+ined, evaluates to '0' [-Wundef]
+ 275 | !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI1_DMA_PRIORITY)
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:275:33: error: missing binary operator before token '('
+ 275 | !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI1_DMA_PRIORITY)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:280:33: error: missing binary operator before token '('
+ 280 | !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI2_DMA_PRIORITY)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:285:33: error: missing binary operator before token '('
+ 285 | !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI3_DMA_PRIORITY)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:290:33: error: missing binary operator before token '('
+ 290 | !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI4_DMA_PRIORITY)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:295:33: error: missing binary operator before token '('
+ 295 | !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI5_DMA_PRIORITY)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:300:33: error: missing binary operator before token '('
+ 300 | !STM32_DMA_IS_VALID_PRIORITY(STM32_SPI_SPI6_DMA_PRIORITY)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:337:6: warning: 'STM32_DMA_IS_VALID_STREAM' is not defin
+ed, evaluates to '0' [-Wundef]
+ 337 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI1_RX_DMA_STREAM)
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:337:31: error: missing binary operator before token '('
+ 337 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI1_RX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:342:6: warning: 'STM32_DMA_IS_VALID_STREAM' is not defin
+ed, evaluates to '0' [-Wundef]
+ 342 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI1_TX_DMA_STREAM)
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:342:31: error: missing binary operator before token '('
+ 342 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI1_TX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:347:31: error: missing binary operator before token '('
+ 347 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI2_RX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:352:31: error: missing binary operator before token '('
+ 352 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI2_TX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:357:31: error: missing binary operator before token '('
+ 357 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI3_RX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:362:31: error: missing binary operator before token '('
+ 362 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI3_TX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:367:31: error: missing binary operator before token '('
+ 367 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI4_RX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:372:31: error: missing binary operator before token '('
+ 372 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI4_TX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:377:31: error: missing binary operator before token '('
+ 377 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI5_RX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:382:31: error: missing binary operator before token '('
+ 382 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI5_TX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:387:31: error: missing binary operator before token '('
+ 387 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI6_RX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:392:31: error: missing binary operator before token '('
+ 392 | !STM32_DMA_IS_VALID_STREAM(STM32_SPI_SPI6_TX_DMA_STREAM)
+ | ^
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:397:5: warning: 'STM32_ADVANCED_DMA' is not defined, eva
+luates to '0' [-Wundef]
+ 397 | #if STM32_ADVANCED_DMA && !STM32_DMA_SUPPORTS_DMAMUX
+ | ^~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:485:9: error: unknown type name 'stm32_dma_stream_t'
+ 485 | const stm32_dma_stream_t *dmarx; \
+ | ^~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/include/hal_spi_v1.h:217:3: note: in expansion of macro 'spi_lld_driver_fields'
+ 217 | spi_lld_driver_fields
+ | ^~~~~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/ports/STM32/LLD/SPIv2/hal_spi_lld.h:487:9: error: unknown type name 'stm32_dma_stream_t'
+ 487 | const stm32_dma_stream_t *dmatx; \
+ | ^~~~~~~~~~~~~~~~~~
+../../../../chibios/os/hal/include/hal_spi_v1.h:217:3: note: in expansion of macro 'spi_lld_driver_fields'
+ 217 | spi_lld_driver_fields
+ | ^~~~~~~~~~~~~~~~~~~~~
+make: *** [../../../../chibios/os/common/startup/ARMCMx/compilers/GCC/mk/rules.mk:195: build/obj/hal.o] Error 1
+EXIT=2
diff --git a/demos/SK32/GENERIC_SK32_F077_PROBE/build_probe2.txt b/demos/SK32/GENERIC_SK32_F077_PROBE/build_probe2.txt
new file mode 100644
index 0000000000..ce48675c7a
Binary files /dev/null and b/demos/SK32/GENERIC_SK32_F077_PROBE/build_probe2.txt differ
diff --git a/demos/SK32/GENERIC_SK32_F077_PROBE/cfg/chconf.h b/demos/SK32/GENERIC_SK32_F077_PROBE/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_PROBE/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/demos/SK32/GENERIC_SK32_F077_PROBE/cfg/halconf.h b/demos/SK32/GENERIC_SK32_F077_PROBE/cfg/halconf.h
new file mode 100644
index 0000000000..cb428219fc
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_PROBE/cfg/halconf.h
@@ -0,0 +1,568 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL TRUE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB TRUE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/demos/SK32/GENERIC_SK32_F077_PROBE/cfg/mcuconf.h b/demos/SK32/GENERIC_SK32_F077_PROBE/cfg/mcuconf.h
new file mode 100644
index 0000000000..294d8d6908
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_PROBE/cfg/mcuconf.h
@@ -0,0 +1,270 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for
+ * the USB IP. The SK32 port provides native drivers for the core platform
+ * (clocks, SysTick-based ST, ISR aggregation, GPIO/PAL, USB) and reuses the
+ * shared ChibiOS STM32 low level drivers (TIMv1, USARTv2, ADCv1, SPIv2,
+ * I2Cv2, ...) for the remaining peripherals. This mcuconf.h must therefore
+ * satisfy two naming schemes:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST settings (used by the SK32 HAL
+ * low level drivers), and
+ * - STM32F0xx_MCUCONF + the STM32_* settings consumed by the shared
+ * ChibiOS STM32 low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define STM32F0xx_MCUCONF
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * STM32 HAL driver system settings (shared LLD requirements).
+ */
+#define STM32_NO_INIT FALSE
+#define STM32_PVD_ENABLE FALSE
+#define STM32_PLS STM32_PLS_LEV0
+#define STM32_HSI_ENABLED TRUE
+#define STM32_HSI14_ENABLED TRUE
+#define STM32_HSI48_ENABLED FALSE
+#define STM32_LSI_ENABLED TRUE
+#define STM32_HSE_ENABLED FALSE
+#define STM32_LSE_ENABLED FALSE
+#define STM32_SW STM32_SW_PLL
+#define STM32_PLLSRC STM32_PLLSRC_HSI_DIV2
+#define STM32_PREDIV_VALUE 1
+#define STM32_PLLMUL_VALUE 12
+#define STM32_HPRE STM32_HPRE_DIV1
+#define STM32_PPRE STM32_PPRE_DIV1
+#define STM32_MCOSEL STM32_MCOSEL_NOCLOCK
+#define STM32_MCOPRE STM32_MCOPRE_DIV1
+#define STM32_PLLNODIV STM32_PLLNODIV_DIV2
+#define STM32_USBSW STM32_USBSW_HSI48
+#define STM32_CECSW STM32_CECSW_HSI
+#define STM32_I2C1SW STM32_I2C1SW_HSI
+#define STM32_USART1SW STM32_USART1SW_PCLK
+#define STM32_RTCSEL STM32_RTCSEL_LSI
+
+/*
+ * IRQ system settings.
+ * The EXTI line IRQ priorities are configured through the native
+ * SK32_IRQ_EXTI*_PRIORITY defaults in sk32_isr.h (priority 3), the shared
+ * STM32 EXTIv1/GPIOv2 layers are not used by this port.
+ */
+#define STM32_IRQ_USART1_PRIORITY 3
+#define STM32_IRQ_USART2_PRIORITY 3
+#define STM32_IRQ_USART3_8_PRIORITY 3
+
+/*
+ * ADC driver system settings.
+ */
+#define STM32_ADC_USE_ADC1 FALSE
+#define STM32_ADC_ADC1_CFGR2 ADC_CFGR2_CKMODE_ADCCLK
+#define STM32_ADC_ADC1_DMA_PRIORITY 2
+#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 2
+#define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(1, 1)
+
+/*
+ * CAN driver system settings.
+ */
+#define STM32_CAN_USE_CAN1 FALSE
+#define STM32_CAN_CAN1_IRQ_PRIORITY 3
+
+/*
+ * DAC driver system settings.
+ */
+#define STM32_DAC_DUAL_MODE FALSE
+#define STM32_DAC_USE_DAC1_CH1 FALSE
+#define STM32_DAC_USE_DAC1_CH2 FALSE
+#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY 2
+#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY 2
+#define STM32_DAC_DAC1_CH1_DMA_PRIORITY 2
+#define STM32_DAC_DAC1_CH2_DMA_PRIORITY 2
+#define STM32_DAC_DAC1_CH1_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_DAC_DAC1_CH2_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+
+/*
+ * GPT driver system settings.
+ */
+#define STM32_GPT_USE_TIM1 FALSE
+#define STM32_GPT_USE_TIM2 FALSE
+#define STM32_GPT_USE_TIM3 FALSE
+#define STM32_GPT_USE_TIM6 FALSE
+#define STM32_GPT_USE_TIM14 FALSE
+#define STM32_GPT_TIM1_IRQ_PRIORITY 2
+#define STM32_GPT_TIM2_IRQ_PRIORITY 2
+#define STM32_GPT_TIM3_IRQ_PRIORITY 2
+#define STM32_GPT_TIM6_IRQ_PRIORITY 2
+#define STM32_GPT_TIM14_IRQ_PRIORITY 2
+
+/*
+ * I2C driver system settings.
+ */
+#define STM32_I2C_USE_I2C1 FALSE
+#define STM32_I2C_USE_I2C2 FALSE
+#define STM32_I2C_BUSY_TIMEOUT 50
+#define STM32_I2C_I2C1_IRQ_PRIORITY 3
+#define STM32_I2C_I2C2_IRQ_PRIORITY 3
+#define STM32_I2C_USE_DMA TRUE
+#define STM32_I2C_I2C1_DMA_PRIORITY 1
+#define STM32_I2C_I2C2_DMA_PRIORITY 1
+#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure")
+
+/*
+ * I2S driver system settings.
+ */
+#define STM32_I2S_USE_SPI1 FALSE
+#define STM32_I2S_USE_SPI2 FALSE
+#define STM32_I2S_SPI1_MODE (STM32_I2S_MODE_MASTER | \
+ STM32_I2S_MODE_RX)
+#define STM32_I2S_SPI2_MODE (STM32_I2S_MODE_MASTER | \
+ STM32_I2S_MODE_RX)
+#define STM32_I2S_SPI1_IRQ_PRIORITY 2
+#define STM32_I2S_SPI2_IRQ_PRIORITY 2
+#define STM32_I2S_SPI1_DMA_PRIORITY 1
+#define STM32_I2S_SPI2_DMA_PRIORITY 1
+#define STM32_I2S_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_I2S_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_I2S_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_I2S_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure")
+
+/*
+ * ICU driver system settings.
+ */
+#define STM32_ICU_USE_TIM1 FALSE
+#define STM32_ICU_USE_TIM2 FALSE
+#define STM32_ICU_USE_TIM3 FALSE
+#define STM32_ICU_TIM1_IRQ_PRIORITY 3
+#define STM32_ICU_TIM2_IRQ_PRIORITY 3
+#define STM32_ICU_TIM3_IRQ_PRIORITY 3
+
+/*
+ * PWM driver system settings.
+ */
+#define STM32_PWM_USE_ADVANCED FALSE
+#define STM32_PWM_USE_TIM1 FALSE
+#define STM32_PWM_USE_TIM2 FALSE
+#define STM32_PWM_USE_TIM3 FALSE
+#define STM32_PWM_TIM1_IRQ_PRIORITY 3
+#define STM32_PWM_TIM2_IRQ_PRIORITY 3
+#define STM32_PWM_TIM3_IRQ_PRIORITY 3
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches, USART2 is mapped on PA2/PA3 (alternate 1)
+ * as documented in board.h.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 TRUE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ */
+#define STM32_SPI_USE_SPI1 FALSE
+#define STM32_SPI_USE_SPI2 FALSE
+#define STM32_SPI_SPI1_DMA_PRIORITY 1
+#define STM32_SPI_SPI2_DMA_PRIORITY 1
+#define STM32_SPI_SPI1_IRQ_PRIORITY 2
+#define STM32_SPI_SPI2_IRQ_PRIORITY 2
+#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure")
+
+/*
+ * ST driver system settings.
+ */
+#define STM32_ST_IRQ_PRIORITY 2
+#define STM32_ST_USE_TIMER 2
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode, the STM32_ST_* settings above are only kept for the shared
+ * STM32 low level drivers.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+/*
+ * UART driver system settings.
+ */
+#define STM32_UART_USE_USART1 FALSE
+#define STM32_UART_USE_USART2 FALSE
+#define STM32_UART_USE_USART3 FALSE
+#define STM32_UART_USE_UART4 FALSE
+#define STM32_UART_USART1_DMA_PRIORITY 0
+#define STM32_UART_USART2_DMA_PRIORITY 0
+#define STM32_UART_USART3_DMA_PRIORITY 0
+#define STM32_UART_UART4_DMA_PRIORITY 0
+#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_UART_UART4_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
+#define STM32_UART_UART4_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
+#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure")
+
+/*
+ * USB driver system settings.
+ * The SK32 musbfsfc USB LLD (not the STM32 USBv1 LLD) is used by this port.
+ */
+#define STM32_USB_USE_USB1 TRUE
+#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE
+#define STM32_USB_USB1_LP_IRQ_PRIORITY 3
+
+/*
+ * WDG driver system settings.
+ */
+#define STM32_WDG_USE_IWDG FALSE
+
+#endif /* MCUCONF_H */
diff --git a/demos/SK32/GENERIC_SK32_F077_PROBE/main.c b/demos/SK32/GENERIC_SK32_F077_PROBE/main.c
new file mode 100644
index 0000000000..68586f303f
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_PROBE/main.c
@@ -0,0 +1,92 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/*
+ * SK32F077 native serial smoke test.
+ *
+ * USART2 is mapped on PA2 (TX) / PA3 (RX), alternate function 1, as
+ * configured by the board early initialization code (board.h). The driver
+ * is the native SK32F0xx SR/DR class serial LLD (hal_serial_lld.c).
+ *
+ * The demo blinks the green LED and echoes back over SD2 every byte
+ * received from the UART at the default bit rate (38400 baud, 8N1).
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+/*===========================================================================*/
+/* Generic code. */
+/*===========================================================================*/
+
+/*
+ * LED blinker thread, times are in milliseconds.
+ */
+static THD_WORKING_AREA(waThread1, 256);
+static THD_FUNCTION(Thread1, arg) {
+ (void)arg;
+ chRegSetThreadName("blinker");
+ while (true) {
+ palToggleLine(LINE_LED_GREEN);
+ chThdSleepMilliseconds(250);
+ }
+}
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ /*
+ * Starting the serial driver SD2 (USART2) with the default configuration,
+ * the driver enables the USART2 clock and NVIC vector when started.
+ */
+ sdStart(&SD2, NULL);
+
+ /*
+ * Starting the LED blinker thread.
+ */
+ chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);
+
+ /*
+ * Banner and then byte-by-byte echo loop.
+ */
+ {
+ static const char banner[] = "SK32F077 native serial demo ready\r\n";
+
+ sdWrite(&SD2, (const uint8_t *)banner, sizeof(banner) - 1U);
+ }
+
+ while (true) {
+ msg_t b;
+
+ b = sdGetTimeout(&SD2, TIME_INFINITE);
+ if (b >= MSG_OK) {
+ sdPut(&SD2, b);
+ }
+ }
+}
diff --git a/demos/SK32/GENERIC_SK32_F077_SERIAL/Makefile b/demos/SK32/GENERIC_SK32_F077_SERIAL/Makefile
new file mode 100644
index 0000000000..19851b88f5
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_SERIAL/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = ch
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/demos/SK32/GENERIC_SK32_F077_SERIAL/build_out.txt b/demos/SK32/GENERIC_SK32_F077_SERIAL/build_out.txt
new file mode 100644
index 0000000000..75024befb0
Binary files /dev/null and b/demos/SK32/GENERIC_SK32_F077_SERIAL/build_out.txt differ
diff --git a/demos/SK32/GENERIC_SK32_F077_SERIAL/cfg/chconf.h b/demos/SK32/GENERIC_SK32_F077_SERIAL/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_SERIAL/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/demos/SK32/GENERIC_SK32_F077_SERIAL/cfg/halconf.h b/demos/SK32/GENERIC_SK32_F077_SERIAL/cfg/halconf.h
new file mode 100644
index 0000000000..cb428219fc
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_SERIAL/cfg/halconf.h
@@ -0,0 +1,568 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL TRUE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB TRUE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/demos/SK32/GENERIC_SK32_F077_SERIAL/cfg/mcuconf.h b/demos/SK32/GENERIC_SK32_F077_SERIAL/cfg/mcuconf.h
new file mode 100644
index 0000000000..294d8d6908
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_SERIAL/cfg/mcuconf.h
@@ -0,0 +1,270 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for
+ * the USB IP. The SK32 port provides native drivers for the core platform
+ * (clocks, SysTick-based ST, ISR aggregation, GPIO/PAL, USB) and reuses the
+ * shared ChibiOS STM32 low level drivers (TIMv1, USARTv2, ADCv1, SPIv2,
+ * I2Cv2, ...) for the remaining peripherals. This mcuconf.h must therefore
+ * satisfy two naming schemes:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST settings (used by the SK32 HAL
+ * low level drivers), and
+ * - STM32F0xx_MCUCONF + the STM32_* settings consumed by the shared
+ * ChibiOS STM32 low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define STM32F0xx_MCUCONF
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * STM32 HAL driver system settings (shared LLD requirements).
+ */
+#define STM32_NO_INIT FALSE
+#define STM32_PVD_ENABLE FALSE
+#define STM32_PLS STM32_PLS_LEV0
+#define STM32_HSI_ENABLED TRUE
+#define STM32_HSI14_ENABLED TRUE
+#define STM32_HSI48_ENABLED FALSE
+#define STM32_LSI_ENABLED TRUE
+#define STM32_HSE_ENABLED FALSE
+#define STM32_LSE_ENABLED FALSE
+#define STM32_SW STM32_SW_PLL
+#define STM32_PLLSRC STM32_PLLSRC_HSI_DIV2
+#define STM32_PREDIV_VALUE 1
+#define STM32_PLLMUL_VALUE 12
+#define STM32_HPRE STM32_HPRE_DIV1
+#define STM32_PPRE STM32_PPRE_DIV1
+#define STM32_MCOSEL STM32_MCOSEL_NOCLOCK
+#define STM32_MCOPRE STM32_MCOPRE_DIV1
+#define STM32_PLLNODIV STM32_PLLNODIV_DIV2
+#define STM32_USBSW STM32_USBSW_HSI48
+#define STM32_CECSW STM32_CECSW_HSI
+#define STM32_I2C1SW STM32_I2C1SW_HSI
+#define STM32_USART1SW STM32_USART1SW_PCLK
+#define STM32_RTCSEL STM32_RTCSEL_LSI
+
+/*
+ * IRQ system settings.
+ * The EXTI line IRQ priorities are configured through the native
+ * SK32_IRQ_EXTI*_PRIORITY defaults in sk32_isr.h (priority 3), the shared
+ * STM32 EXTIv1/GPIOv2 layers are not used by this port.
+ */
+#define STM32_IRQ_USART1_PRIORITY 3
+#define STM32_IRQ_USART2_PRIORITY 3
+#define STM32_IRQ_USART3_8_PRIORITY 3
+
+/*
+ * ADC driver system settings.
+ */
+#define STM32_ADC_USE_ADC1 FALSE
+#define STM32_ADC_ADC1_CFGR2 ADC_CFGR2_CKMODE_ADCCLK
+#define STM32_ADC_ADC1_DMA_PRIORITY 2
+#define STM32_ADC_ADC1_DMA_IRQ_PRIORITY 2
+#define STM32_ADC_ADC1_DMA_STREAM STM32_DMA_STREAM_ID(1, 1)
+
+/*
+ * CAN driver system settings.
+ */
+#define STM32_CAN_USE_CAN1 FALSE
+#define STM32_CAN_CAN1_IRQ_PRIORITY 3
+
+/*
+ * DAC driver system settings.
+ */
+#define STM32_DAC_DUAL_MODE FALSE
+#define STM32_DAC_USE_DAC1_CH1 FALSE
+#define STM32_DAC_USE_DAC1_CH2 FALSE
+#define STM32_DAC_DAC1_CH1_IRQ_PRIORITY 2
+#define STM32_DAC_DAC1_CH2_IRQ_PRIORITY 2
+#define STM32_DAC_DAC1_CH1_DMA_PRIORITY 2
+#define STM32_DAC_DAC1_CH2_DMA_PRIORITY 2
+#define STM32_DAC_DAC1_CH1_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_DAC_DAC1_CH2_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+
+/*
+ * GPT driver system settings.
+ */
+#define STM32_GPT_USE_TIM1 FALSE
+#define STM32_GPT_USE_TIM2 FALSE
+#define STM32_GPT_USE_TIM3 FALSE
+#define STM32_GPT_USE_TIM6 FALSE
+#define STM32_GPT_USE_TIM14 FALSE
+#define STM32_GPT_TIM1_IRQ_PRIORITY 2
+#define STM32_GPT_TIM2_IRQ_PRIORITY 2
+#define STM32_GPT_TIM3_IRQ_PRIORITY 2
+#define STM32_GPT_TIM6_IRQ_PRIORITY 2
+#define STM32_GPT_TIM14_IRQ_PRIORITY 2
+
+/*
+ * I2C driver system settings.
+ */
+#define STM32_I2C_USE_I2C1 FALSE
+#define STM32_I2C_USE_I2C2 FALSE
+#define STM32_I2C_BUSY_TIMEOUT 50
+#define STM32_I2C_I2C1_IRQ_PRIORITY 3
+#define STM32_I2C_I2C2_IRQ_PRIORITY 3
+#define STM32_I2C_USE_DMA TRUE
+#define STM32_I2C_I2C1_DMA_PRIORITY 1
+#define STM32_I2C_I2C2_DMA_PRIORITY 1
+#define STM32_I2C_I2C1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_I2C_I2C1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_I2C_I2C2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_I2C_I2C2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_I2C_DMA_ERROR_HOOK(i2cp) osalSysHalt("DMA failure")
+
+/*
+ * I2S driver system settings.
+ */
+#define STM32_I2S_USE_SPI1 FALSE
+#define STM32_I2S_USE_SPI2 FALSE
+#define STM32_I2S_SPI1_MODE (STM32_I2S_MODE_MASTER | \
+ STM32_I2S_MODE_RX)
+#define STM32_I2S_SPI2_MODE (STM32_I2S_MODE_MASTER | \
+ STM32_I2S_MODE_RX)
+#define STM32_I2S_SPI1_IRQ_PRIORITY 2
+#define STM32_I2S_SPI2_IRQ_PRIORITY 2
+#define STM32_I2S_SPI1_DMA_PRIORITY 1
+#define STM32_I2S_SPI2_DMA_PRIORITY 1
+#define STM32_I2S_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_I2S_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_I2S_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_I2S_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_I2S_DMA_ERROR_HOOK(i2sp) osalSysHalt("DMA failure")
+
+/*
+ * ICU driver system settings.
+ */
+#define STM32_ICU_USE_TIM1 FALSE
+#define STM32_ICU_USE_TIM2 FALSE
+#define STM32_ICU_USE_TIM3 FALSE
+#define STM32_ICU_TIM1_IRQ_PRIORITY 3
+#define STM32_ICU_TIM2_IRQ_PRIORITY 3
+#define STM32_ICU_TIM3_IRQ_PRIORITY 3
+
+/*
+ * PWM driver system settings.
+ */
+#define STM32_PWM_USE_ADVANCED FALSE
+#define STM32_PWM_USE_TIM1 FALSE
+#define STM32_PWM_USE_TIM2 FALSE
+#define STM32_PWM_USE_TIM3 FALSE
+#define STM32_PWM_TIM1_IRQ_PRIORITY 3
+#define STM32_PWM_TIM2_IRQ_PRIORITY 3
+#define STM32_PWM_TIM3_IRQ_PRIORITY 3
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches, USART2 is mapped on PA2/PA3 (alternate 1)
+ * as documented in board.h.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 TRUE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ */
+#define STM32_SPI_USE_SPI1 FALSE
+#define STM32_SPI_USE_SPI2 FALSE
+#define STM32_SPI_SPI1_DMA_PRIORITY 1
+#define STM32_SPI_SPI2_DMA_PRIORITY 1
+#define STM32_SPI_SPI1_IRQ_PRIORITY 2
+#define STM32_SPI_SPI2_IRQ_PRIORITY 2
+#define STM32_SPI_SPI1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_SPI_SPI1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_SPI_SPI2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_SPI_SPI2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_SPI_DMA_ERROR_HOOK(spip) osalSysHalt("DMA failure")
+
+/*
+ * ST driver system settings.
+ */
+#define STM32_ST_IRQ_PRIORITY 2
+#define STM32_ST_USE_TIMER 2
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode, the STM32_ST_* settings above are only kept for the shared
+ * STM32 low level drivers.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+/*
+ * UART driver system settings.
+ */
+#define STM32_UART_USE_USART1 FALSE
+#define STM32_UART_USE_USART2 FALSE
+#define STM32_UART_USE_USART3 FALSE
+#define STM32_UART_USE_UART4 FALSE
+#define STM32_UART_USART1_DMA_PRIORITY 0
+#define STM32_UART_USART2_DMA_PRIORITY 0
+#define STM32_UART_USART3_DMA_PRIORITY 0
+#define STM32_UART_UART4_DMA_PRIORITY 0
+#define STM32_UART_USART1_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_UART_USART1_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_UART_USART2_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 5)
+#define STM32_UART_USART2_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 4)
+#define STM32_UART_USART3_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 3)
+#define STM32_UART_USART3_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 2)
+#define STM32_UART_UART4_RX_DMA_STREAM STM32_DMA_STREAM_ID(1, 6)
+#define STM32_UART_UART4_TX_DMA_STREAM STM32_DMA_STREAM_ID(1, 7)
+#define STM32_UART_DMA_ERROR_HOOK(uartp) osalSysHalt("DMA failure")
+
+/*
+ * USB driver system settings.
+ * The SK32 musbfsfc USB LLD (not the STM32 USBv1 LLD) is used by this port.
+ */
+#define STM32_USB_USE_USB1 TRUE
+#define STM32_USB_LOW_POWER_ON_SUSPEND FALSE
+#define STM32_USB_USB1_LP_IRQ_PRIORITY 3
+
+/*
+ * WDG driver system settings.
+ */
+#define STM32_WDG_USE_IWDG FALSE
+
+#endif /* MCUCONF_H */
diff --git a/demos/SK32/GENERIC_SK32_F077_SERIAL/main.c b/demos/SK32/GENERIC_SK32_F077_SERIAL/main.c
new file mode 100644
index 0000000000..68586f303f
--- /dev/null
+++ b/demos/SK32/GENERIC_SK32_F077_SERIAL/main.c
@@ -0,0 +1,92 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/*
+ * SK32F077 native serial smoke test.
+ *
+ * USART2 is mapped on PA2 (TX) / PA3 (RX), alternate function 1, as
+ * configured by the board early initialization code (board.h). The driver
+ * is the native SK32F0xx SR/DR class serial LLD (hal_serial_lld.c).
+ *
+ * The demo blinks the green LED and echoes back over SD2 every byte
+ * received from the UART at the default bit rate (38400 baud, 8N1).
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+/*===========================================================================*/
+/* Generic code. */
+/*===========================================================================*/
+
+/*
+ * LED blinker thread, times are in milliseconds.
+ */
+static THD_WORKING_AREA(waThread1, 256);
+static THD_FUNCTION(Thread1, arg) {
+ (void)arg;
+ chRegSetThreadName("blinker");
+ while (true) {
+ palToggleLine(LINE_LED_GREEN);
+ chThdSleepMilliseconds(250);
+ }
+}
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ /*
+ * Starting the serial driver SD2 (USART2) with the default configuration,
+ * the driver enables the USART2 clock and NVIC vector when started.
+ */
+ sdStart(&SD2, NULL);
+
+ /*
+ * Starting the LED blinker thread.
+ */
+ chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);
+
+ /*
+ * Banner and then byte-by-byte echo loop.
+ */
+ {
+ static const char banner[] = "SK32F077 native serial demo ready\r\n";
+
+ sdWrite(&SD2, (const uint8_t *)banner, sizeof(banner) - 1U);
+ }
+
+ while (true) {
+ msg_t b;
+
+ b = sdGetTimeout(&SD2, TIME_INFINITE);
+ if (b >= MSG_OK) {
+ sdPut(&SD2, b);
+ }
+ }
+}
diff --git a/os/common/ext/CMSIS/SK32/SK32F0xx/sk32f0xx.h b/os/common/ext/CMSIS/SK32/SK32F0xx/sk32f0xx.h
new file mode 100644
index 0000000000..c76b8cdb74
--- /dev/null
+++ b/os/common/ext/CMSIS/SK32/SK32F0xx/sk32f0xx.h
@@ -0,0 +1,3448 @@
+/**
+ ******************************************************************************
+ * @file sk32f0xx.h
+ * @author 3Think FD Department
+ * @version V1.0.0
+ * @date 2-September-2025
+ * @brief CMSIS Cortex-M0 Device Peripheral Access Layer Header File.
+ * This file contains all the peripheral register's definitions, bits
+ * definitions and memory mapping for SK32F0xx devices.
+ *
+ * The file is the unique include file that the application programmer
+ * is using in the C source code, usually in main.c. This file contains:
+ * - Configuration section that allows to select:
+ * - The device used in the target application
+ * - To use or not the peripheral’s drivers in application code(i.e.
+ * code will be based on direct access to peripheral’s registers
+ * rather than drivers API), this option is controlled by
+ * "#define USE_STDPERIPH_DRIVER"
+ * - To change few application-specific parameters such as the HSE
+ * crystal frequency
+ * - Data structures and the address mapping for all peripherals
+ * - Peripheral's registers declarations and bits definition
+ * - Macros to access peripheral’s registers hardware
+ *
+ ******************************************************************************
+ * @attention
+ *
+ * Copyright (c) 2025 3Think.
+ * 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
+ * @{
+ */
+
+/** @addtogroup sk32f0xx
+ * @{
+ */
+
+#ifndef __SK32F0XX_H
+#define __SK32F0XX_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @addtogroup Library_configuration_section
+ * @{
+ */
+
+
+/**
+ * @brief In the following line adjust the value of External High Speed oscillator (HSE)
+ used in your application
+
+ Tip: To avoid modifying this file each time you need to use different HSE, you
+ can define the HSE value in your toolchain compiler preprocessor.
+ */
+#if !defined (HSE_VALUE)
+#define HSE_VALUE ((uint32_t)8000000) /*!< Value of the External oscillator in Hz*/
+#endif /* HSE_VALUE */
+
+/**
+ * @brief In the following line adjust the External High Speed oscillator (HSE) Startup
+ Timeout value
+ */
+#if !defined (HSE_STARTUP_TIMEOUT)
+#define HSE_STARTUP_TIMEOUT ((uint16_t)0x5000) /*!< Time out for HSE start up */
+#endif /* HSE_STARTUP_TIMEOUT */
+
+/**
+ * @brief In the following line adjust the Internal High Speed oscillator (HSI) Startup
+ Timeout value
+ */
+#if !defined (HSI_STARTUP_TIMEOUT)
+#define HSI_STARTUP_TIMEOUT ((uint16_t)0x5000) /*!< Time out for HSI start up */
+#endif /* HSI_STARTUP_TIMEOUT */
+
+#if !defined (HSI_VALUE)
+#define HSI_VALUE ((uint32_t)8000000) /*!< Value of the Internal High Speed oscillator in Hz.
+ The real value may vary depending on the variations
+ in voltage and temperature. */
+#endif /* HSI_VALUE */
+
+#if !defined (PLL48_VALUE)
+#define PLL48_VALUE ((uint32_t)48000000) /*!< Value of the Internal High Speed oscillator for USB in Hz.
+ The real value may vary depending on the variations
+ in voltage and temperature. */
+#endif /* PLL48_VALUE */
+
+#if !defined (LSI_VALUE)
+#define LSI_VALUE ((uint32_t)40000) /*!< Value of the Internal Low Speed oscillator in Hz
+ The real value may vary depending on the variations
+ in voltage and temperature. */
+#endif /* LSI_VALUE */
+
+#if !defined (LSE_VALUE)
+#define LSE_VALUE ((uint32_t)32768) /*!< Value of the External Low Speed oscillator in Hz */
+#endif /* LSE_VALUE */
+
+/**
+ * @brief SK32F0xx Standard Peripheral Library version number V1.0.0
+ */
+#define __SK32F0XX_STDPERIPH_VERSION_MAIN (0x01) /*!< [31:24] main version */
+#define __SK32F0XX_STDPERIPH_VERSION_SUB1 (0x00) /*!< [23:16] sub1 version */
+#define __SK32F0XX_STDPERIPH_VERSION_SUB2 (0x00) /*!< [15:8] sub2 version */
+#define __SK32F0XX_STDPERIPH_VERSION_RC (0x00) /*!< [7:0] release candidate */
+#define __SK32F0XX_STDPERIPH_VERSION ((__SK32F0XX_STDPERIPH_VERSION_MAIN << 24)\
+ |(__SK32F0XX_STDPERIPH_VERSION_SUB1 << 16)\
+ |(__SK32F0XX_STDPERIPH_VERSION_SUB2 << 8)\
+ |(__SK32F0XX_STDPERIPH_VERSION_RC))
+
+/**
+ * @}
+ */
+
+/** @addtogroup Configuration_section_for_CMSIS
+ * @{
+ */
+
+/**
+ * @brief SK32F0xx Interrupt Number Definition, according to the selected device
+ * in @ref Library_configuration_section
+ */
+#define __CM0_REV 0 /*!< Core Revision r0p0 */
+#define __MPU_PRESENT 0 /*!< SK32F0xx do not provide MPU */
+#define __NVIC_PRIO_BITS 2 /*!< SK32F0xx uses 2 Bits for the Priority Levels */
+#define __Vendor_SysTickConfig 0 /*!< Set to 1 if different SysTick Config is used */
+
+/*!< Interrupt Number Definition */
+typedef enum IRQn {
+ /****** Cortex-M0 Processor Exceptions Numbers ******************************************************/
+ NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */
+ HardFault_IRQn = -13, /*!< 3 Cortex-M0 Hard Fault Interrupt */
+ SVC_IRQn = -5, /*!< 11 Cortex-M0 SV Call Interrupt */
+ PendSV_IRQn = -2, /*!< 14 Cortex-M0 Pend SV Interrupt */
+ SysTick_IRQn = -1, /*!< 15 Cortex-M0 System Tick Interrupt */
+
+ WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */
+ PVD_VDDIO2_IRQn = 1, /*!< PVD and VDDIO2 supply comparator through EXTI Line detect Interrupt */
+ RTC_IRQn = 2, /*!< RTC through EXTI Line Interrupt */
+ FLASH_IRQn = 3, /*!< FLASH Interrupt */
+ RCC_CRS_IRQn = 4, /*!< RCC and CRS Interrupts */
+ EXTI0_1_IRQn = 5, /*!< EXTI Line 0 and 1 Interrupts */
+ EXTI2_3_IRQn = 6, /*!< EXTI Line 2 and 3 Interrupts */
+ EXTI4_15_IRQn = 7, /*!< EXTI Line 4 to 15 Interrupts */
+ TSC_IRQn = 8, /*!< TSC Interrupt */
+ DMA1_Channel1_IRQn = 9, /*!< DMA1 Channel 1 Interrupt */
+ DMA1_Channel2_3_IRQn = 10, /*!< DMA1 Channel 2 and Channel 3 Interrupts */
+ DMA1_Channel4_5_6_IRQn = 11, /*!< DMA1 Channel 4, Channel 5, Channel 6 Interrupts */
+ ADC_COMP_IRQn = 12, /*!< ADC1, COMP1 and COMP2 Interrupts */
+ TIM1_BRK_UP_TRG_COM_IRQn = 13, /*!< TIM1 Break, Update, Trigger and Commutation Interrupts */
+ TIM1_CC_IRQn = 14, /*!< TIM1 Capture Compare Interrupt */
+ TIM2_IRQn = 15, /*!< TIM2 Interrupt */
+ TIM3_IRQn = 16, /*!< TIM3 Interrupt */
+ TIM6_DAC_IRQn = 17, /*!< TIM6 and DAC Interrupts */
+ TIM16_IRQn = 21, /*!< TIM16 Interrupt */
+ TIM17_IRQn = 22, /*!< TIM17 Interrupt */
+ I2C1_IRQn = 23, /*!< I2C1 Interrupt */
+ I2C2_IRQn = 24, /*!< I2C2 Interrupt */
+ SPI1_IRQn = 25, /*!< SPI1 Interrupt */
+ SPI2_IRQn = 26, /*!< SPI2 Interrupt */
+ USART1_IRQn = 27, /*!< USART1 Interrupt */
+ USART2_IRQn = 28, /*!< USART2 Interrupt */
+ SLED_IRQn = 29, /*!< SLED Interrupts */
+ KBCU_IRQn = 30, /*!< KBCU Interrupts */
+ USB_IRQn = 31 /*!< USB Low Priority global Interrupt */
+} IRQn_Type;
+
+/**
+ * @}
+ */
+
+#include "core_cm0.h"
+#include "system_sk32f0xx.h"
+#include
+
+/** @addtogroup Exported_macros
+ * @{
+ */
+#define UNUSED(X) (void)X
+
+/**
+ * @}
+ */
+
+/** @addtogroup Exported_types
+ * @{
+ */
+
+typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
+
+typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
+#define IS_FUNCTIONAL_STATE(STATE) (((STATE) == DISABLE) || ((STATE) == ENABLE))
+
+typedef enum {ERROR = 0, SUCCESS = !ERROR} ErrorStatus;
+
+/** @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 CFGR1; /*!< ADC Configuration register 1, Address offset:0x0C */
+ __IO uint32_t CFGR2; /*!< ADC Configuration register 2, Address offset:0x10 */
+ __IO uint32_t SMPR; /*!< ADC Sampling time register, Address offset:0x14 */
+ uint32_t RESERVED1; /*!< Reserved, 0x18 */
+ uint32_t RESERVED2; /*!< Reserved, 0x1C */
+ __IO uint32_t TR; /*!< ADC watchdog threshold register, Address offset:0x20 */
+ uint32_t RESERVED3; /*!< Reserved, 0x24 */
+ __IO uint32_t CHSELR; /*!< ADC channel selection register, Address offset:0x28 */
+ uint32_t RESERVED4[5]; /*!< Reserved, 0x2C */
+ __IO uint32_t DR; /*!< ADC data register, Address offset:0x40 */
+ __IO uint32_t VREFSR; /*!< ADC Vref register, Address offset:0x44 */
+} ADC_TypeDef;
+
+typedef struct {
+ __IO uint32_t CCR;
+} ADC_Common_TypeDef;
+
+
+/**
+ * @brief Comparator
+ */
+
+typedef struct {
+ __IO uint32_t CSR; /*!< COMP comparator control and status register, Address offset: 0x1C */
+} COMP_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 */
+ uint32_t RESERVED2; /*!< Reserved, 0x0C */
+ __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */
+} 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 */
+} 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 CCR; /*!< DMA channel x configuration register Address offset: 0x00 */
+ __IO uint32_t CNDTR; /*!< DMA channel x number of data register Address offset: 0x04 */
+ __IO uint32_t CPAR; /*!< DMA channel x peripheral address register Address offset: 0x08 */
+ __IO uint32_t CMAR; /*!< DMA channel x memory address register Address offset: 0x0C */
+} 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 */
+ uint32_t RESERVED0[40]; /*!< Reserved as declared by channel typedef 0x08 - 0xA4 */
+ __IO uint32_t RMPCR; /*!< Remap control register, Address offset: 0xA8 */
+} DMA_TypeDef;
+
+/**
+ * @brief External Interrupt/Event Controller
+ */
+
+typedef struct {
+ __IO uint32_t IMR; /*!CR |= (uint32_t)0x00000001;
+
+ /* Reset SW[1:0], HPRE[3:0], PPRE[2:0] and MCO[3:0] bits */
+ RCC->CFGR &= (uint32_t)0xF0FFF80C;
+
+ /* Reset HSEON, CSSON and PLLON bits */
+ RCC->CR &= (uint32_t)0xFEF6FFFF;
+
+ /* Reset HSEBYP bit */
+ RCC->CR &= (uint32_t)0xFFFBFFFF;
+
+ /* Reset PLLSRC, PLLXTPRE and PLLMUL[3:0] bits */
+ RCC->CFGR &= (uint32_t)0xFFC0FFFF;
+
+ /* Reset PREDIV1[3:0] bits */
+ RCC->CFGR2 &= (uint32_t)0xFFFFFFF0;
+
+ /* Reset USBSW, TIM6SW[1:0], TSCSW and LCDSW bits */
+ RCC->CFGR3 &= (uint32_t)0xF0FFFF7F;
+
+ /* Reset PLL48ON bit */
+ RCC->CR2 &= (uint32_t)0xFFFEFFFF;
+
+ /* Disable all interrupts */
+ RCC->CIR = 0x00000000;
+
+ /* Configure the System clock frequency, AHB/APBx prescalers and Flash settings */
+ SetSysClock();
+}
+
+/**
+ * @brief Update SystemCoreClock according to Clock Register Values
+ * The SystemCoreClock variable contains the core clock (HCLK), it can
+ * be used by the user application to setup the SysTick timer or configure
+ * other parameters.
+ *
+ * @note Each time the core clock (HCLK) changes, this function must be called
+ * to update SystemCoreClock variable value. Otherwise, any configuration
+ * based on this variable will be incorrect.
+ *
+ * @note - The system frequency computed by this function is not the real
+ * frequency in the chip. It is calculated based on the predefined
+ * constant and the selected clock source:
+ *
+ * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
+ *
+ * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
+ *
+ * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
+ * or HSI_VALUE(*) multiplied/divided by the PLL factors.
+ *
+ * (*) HSI_VALUE is a constant defined in sk32f0xx.h file (default value
+ * 8 MHz) but the real value may vary depending on the variations
+ * in voltage and temperature.
+ *
+ * (**) HSE_VALUE is a constant defined in sk32f0xx.h file (default value
+ * 8 MHz), user has to ensure that HSE_VALUE is same as the real
+ * frequency of the crystal used. Otherwise, this function may
+ * have wrong result.
+ *
+ * - The result of this function could be not correct when using fractional
+ * value for HSE crystal.
+ * @param None
+ * @retval None
+ */
+void SystemCoreClockUpdate(void)
+{
+ uint32_t tmp = 0, pllmull = 0, pllsource = 0, prediv1factor = 0;
+
+ /* Get SYSCLK source -------------------------------------------------------*/
+ tmp = RCC->CFGR & RCC_CFGR_SWS;
+
+ switch (tmp)
+ {
+ case 0x00: /* HSI used as system clock */
+ SystemCoreClock = HSI_VALUE;
+ break;
+ case 0x04: /* HSE used as system clock */
+ SystemCoreClock = HSE_VALUE;
+ break;
+ case 0x08: /* PLL used as system clock */
+ /* Get PLL clock source and multiplication factor ----------------------*/
+ pllmull = RCC->CFGR & RCC_CFGR_PLLMUL;
+ pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;
+ pllmull = (pllmull >> 18) + 2;
+
+ if (pllsource == 0x00)
+ {
+ /* HSI oscillator clock divided by 2 selected as PLL clock entry */
+ SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
+ }
+ else
+ {
+ prediv1factor = (RCC->CFGR2 & RCC_CFGR2_PREDIV1) + 1;
+ /* HSE oscillator clock selected as PREDIV1 clock entry */
+ SystemCoreClock = (HSE_VALUE / prediv1factor) * pllmull;
+ }
+ break;
+ default: /* HSI used as system clock */
+ SystemCoreClock = HSI_VALUE;
+ break;
+ }
+ /* Compute HCLK clock frequency ----------------*/
+ /* Get HCLK prescaler */
+ tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
+ /* HCLK clock frequency */
+ SystemCoreClock >>= tmp;
+}
+
+/**
+ * @brief Configures the System clock frequency, AHB/APBx prescalers and Flash
+ * settings.
+ * @note This function should be called only once the RCC clock configuration
+ * is reset to the default reset state (done in SystemInit() function).
+ * @param None
+ * @retval None
+ */
+void SetSysClock(void)
+{
+ __IO uint32_t StartUpCounter = 0, HSEStatus = 0;
+
+#ifdef SYSCLK_HSI72M
+
+ /* Enable Prefetch Buffer and set Flash 2 wait Latency */
+ FLASH->ACR = FLASH_ACR_PRFTBE | 0X02;
+
+ /* PLL configuration = HSI * 9 = 72 MHz */
+ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMUL));
+ RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSI_PREDIV | RCC_CFGR_PLLMUL9);
+
+ /* Enable PLL */
+ RCC->CR |= RCC_CR_PLLON;
+
+ /* Wait till PLL is ready */
+ while ((RCC->CR & RCC_CR_PLLRDY) == 0)
+ {
+ }
+
+ /* Select PLL as system clock source */
+ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
+ RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
+
+ /* Wait till PLL is used as system clock source */
+ while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL)
+ {
+ }
+
+#elif defined SYSCLK_HSI48M
+
+ /* Enable Prefetch Buffer and set Flash 2 wait Latency */
+ FLASH->ACR = FLASH_ACR_PRFTBE | 0X02;
+
+ /* PLL configuration = HSI * 6 = 48 MHz */
+ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMUL));
+ RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSI_PREDIV | RCC_CFGR_PLLMUL6);
+
+ /* Enable PLL */
+ RCC->CR |= RCC_CR_PLLON;
+
+ /* Wait till PLL is ready */
+ while ((RCC->CR & RCC_CR_PLLRDY) == 0)
+ {
+ }
+
+ /* Select PLL as system clock source */
+ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
+ RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;
+
+ /* Wait till PLL is used as system clock source */
+ while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)RCC_CFGR_SWS_PLL)
+ {
+ }
+
+#elif defined SYSCLK_HSE72M
+
+ /* Enable Prefetch Buffer and set Flash 2 wait Latency */
+ FLASH->ACR = FLASH_ACR_PRFTBE | 0X02;
+
+ /* Enable HSE */
+ RCC->CR |= ((uint32_t)RCC_CR_HSEON);
+
+ /* Wait till HSE is ready and if Time out is reached exit */
+ do
+ {
+ HSEStatus = RCC->CR & RCC_CR_HSERDY;
+ StartUpCounter++;
+ }
+ while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+ if ((RCC->CR & RCC_CR_HSERDY) != RESET)
+ {
+ HSEStatus = (uint32_t)0x01;
+ }
+ else
+ {
+ HSEStatus = (uint32_t)0x00;
+ }
+ if (HSEStatus == (uint32_t)0x01)
+ {
+ /* PLL configuration = HSE * 9 = 72 MHz */
+ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMUL));
+ RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE_PREDIV | RCC_CFGR_PLLMUL9);
+
+ /* Enable PLL */
+ RCC_PLLCmd(ENABLE);
+
+ /* Select PLL as system clock source */
+ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
+
+ /* Wait till PLL is used as system clock source */
+ while (RCC_GetSYSCLKSource() != 0x08)
+ {
+ }
+ }
+ else
+ {
+ /* If HSE fails to start-up, the application will have wrong clock
+ configuration. User can add here some code to deal with this error */
+ }
+
+
+#elif defined SYSCLK_HSE48M
+
+ /* Enable Prefetch Buffer and set Flash 2 wait Latency */
+ FLASH->ACR = FLASH_ACR_PRFTBE | 0X02;
+
+ /* Enable HSE */
+ RCC->CR |= ((uint32_t)RCC_CR_HSEON);
+
+ /* Wait till HSE is ready and if Time out is reached exit */
+ do
+ {
+ HSEStatus = RCC->CR & RCC_CR_HSERDY;
+ StartUpCounter++;
+ }
+ while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+ if ((RCC->CR & RCC_CR_HSERDY) != RESET)
+ {
+ HSEStatus = (uint32_t)0x01;
+ }
+ else
+ {
+ HSEStatus = (uint32_t)0x00;
+ }
+ if (HSEStatus == (uint32_t)0x01)
+ {
+ /* PLL configuration = HSE * 6 = 48 MHz */
+ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLMUL));
+ RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE_PREDIV | RCC_CFGR_PLLMUL6);
+
+ /* Enable PLL */
+ RCC_PLLCmd(ENABLE);
+
+ /* Select PLL as system clock source */
+ RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
+
+ /* Wait till PLL is used as system clock source */
+ while (RCC_GetSYSCLKSource() != 0x08)
+ {
+ }
+ }
+ else
+ { /* If HSE fails to start-up, the application will have wrong clock
+ configuration. User can add here some code to deal with this error */
+ }
+
+#elif defined SYSCLK_HSE
+
+ /* Enable HSE */
+ RCC->CR |= ((uint32_t)RCC_CR_HSEON);
+
+ /* Wait till HSE is ready and if Time out is reached exit */
+ do
+ {
+ HSEStatus = RCC->CR & RCC_CR_HSERDY;
+ StartUpCounter++;
+ }
+ while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));
+
+ if ((RCC->CR & RCC_CR_HSERDY) != RESET)
+ {
+ HSEStatus = (uint32_t)0x01;
+ }
+ else
+ {
+ HSEStatus = (uint32_t)0x00;
+ }
+ if (HSEStatus == (uint32_t)0x01)
+ {
+ /* Select HSE as system clock source */
+ RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
+ RCC->CFGR |= (uint32_t)RCC_CFGR_SW_HSE;
+ }
+ else
+ { /* If HSE fails to start-up, the application will have wrong clock
+ configuration. User can add here some code to deal with this error */
+ }
+
+#endif
+}
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
diff --git a/os/common/ext/CMSIS/SK32/SK32F0xx/system_sk32f0xx.h b/os/common/ext/CMSIS/SK32/SK32F0xx/system_sk32f0xx.h
new file mode 100644
index 0000000000..591b2e20d7
--- /dev/null
+++ b/os/common/ext/CMSIS/SK32/SK32F0xx/system_sk32f0xx.h
@@ -0,0 +1,96 @@
+/**
+ ******************************************************************************
+ * @file system_sk32f0xx.h
+ * @author 3Think FD Department
+ * @version V1.0.0
+ * @date 2-September-2025
+ * @brief CMSIS Cortex-M0 Device Peripheral Access Layer System Header File.
+ ******************************************************************************
+ * @attention
+ *
+ * Copyright (c) 2025 3Tink.
+ * 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
+ * @{
+ */
+
+/** @addtogroup sk32f0xx_system
+ * @{
+ */
+
+/**
+ * @brief Define to prevent recursive inclusion
+ */
+#ifndef __SYSTEM_SK32F0XX_H
+#define __SYSTEM_SK32F0XX_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/** @addtogroup SK32F0xx_System_Includes
+ * @{
+ */
+
+/**
+ * @}
+ */
+
+
+/** @addtogroup SK32F0xx_System_Exported_types
+ * @{
+ */
+
+extern uint32_t SystemCoreClock; /*!< System Clock Frequency (Core Clock) */
+
+/**
+ * @}
+ */
+
+/** @addtogroup SK32F0xx_System_Exported_Constants
+ * @{
+ */
+
+/**
+ * @}
+ */
+
+/** @addtogroup SK32F0xx_System_Exported_Macros
+ * @{
+ */
+
+/**
+ * @}
+ */
+
+/** @addtogroup SK32F0xx_System_Exported_Functions
+ * @{
+ */
+
+extern void SystemInit(void);
+extern void SystemCoreClockUpdate(void);
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__SYSTEM_SK32F0XX_H */
+
+/**
+ * @}
+ */
+
+/**
+ * @}
+ */
diff --git a/os/common/startup/ARMCMx/compilers/GCC/ld/SK32F077xB.ld b/os/common/startup/ARMCMx/compilers/GCC/ld/SK32F077xB.ld
new file mode 100644
index 0000000000..753683cb9e
--- /dev/null
+++ b/os/common/startup/ARMCMx/compilers/GCC/ld/SK32F077xB.ld
@@ -0,0 +1,516 @@
+/*
+ ChibiOS - Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/*
+ * 3Think SK32F077xB memory setup (128 KB flash, 8 KB SRAM).
+ */
+MEMORY
+{
+ flash0 (rx) : org = 0x08000000, len = 128k
+ flash1 (rx) : org = 0x00000000, len = 0
+ flash2 (rx) : org = 0x00000000, len = 0
+ flash3 (rx) : org = 0x00000000, len = 0
+ flash4 (rx) : org = 0x00000000, len = 0
+ flash5 (rx) : org = 0x00000000, len = 0
+ flash6 (rx) : org = 0x00000000, len = 0
+ flash7 (rx) : org = 0x00000000, len = 0
+ ram0 (wx) : org = 0x20000000, len = 8k
+ ram1 (wx) : org = 0x00000000, len = 0
+ ram2 (wx) : org = 0x00000000, len = 0
+ ram3 (wx) : org = 0x00000000, len = 0
+ ram4 (wx) : org = 0x00000000, len = 0
+ ram5 (wx) : org = 0x00000000, len = 0
+ ram6 (wx) : org = 0x00000000, len = 0
+ ram7 (wx) : org = 0x00000000, len = 0
+}
+
+/* For each data/text section two region are defined, a virtual region
+ and a load region (_LMA suffix).*/
+
+/* Flash region to be used for exception vectors.*/
+REGION_ALIAS("VECTORS_FLASH", flash0);
+REGION_ALIAS("VECTORS_FLASH_LMA", flash0);
+
+/* Flash region to be used for constructors and destructors.*/
+REGION_ALIAS("XTORS_FLASH", flash0);
+REGION_ALIAS("XTORS_FLASH_LMA", flash0);
+
+/* Flash region to be used for code text.*/
+REGION_ALIAS("TEXT_FLASH", flash0);
+REGION_ALIAS("TEXT_FLASH_LMA", flash0);
+
+/* Flash region to be used for read only data.*/
+REGION_ALIAS("RODATA_FLASH", flash0);
+REGION_ALIAS("RODATA_FLASH_LMA", flash0);
+
+/* Flash region to be used for various.*/
+REGION_ALIAS("VARIOUS_FLASH", flash0);
+REGION_ALIAS("VARIOUS_FLASH_LMA", flash0);
+
+/* Flash region to be used for RAM(n) initialization data.*/
+REGION_ALIAS("RAM_INIT_FLASH_LMA", flash0);
+
+/* RAM region to be used for Main stack. This stack accommodates the processing
+ of all exceptions and interrupts.*/
+REGION_ALIAS("MAIN_STACK_RAM", ram0);
+
+/* RAM region to be used for the process stack. This is the stack used by
+ the main() function.*/
+REGION_ALIAS("PROCESS_STACK_RAM", ram0);
+
+/* RAM region to be used for data segment.*/
+REGION_ALIAS("DATA_RAM", ram0);
+REGION_ALIAS("DATA_RAM_LMA", flash0);
+
+/* RAM region to be used for BSS segment.*/
+REGION_ALIAS("BSS_RAM", ram0);
+
+/* RAM region to be used for the default heap.*/
+REGION_ALIAS("HEAP_RAM", ram0);
+
+/*
+ * Generic rules, inlined below (rules_stacks/code/data/memory) so this
+ * script stays self-contained and does not depend on INCLUDE resolution.
+ */
+
+/* Stack rules inclusion (rules_stacks.ld).*/
+SECTIONS
+{
+ /* Special section for exceptions stack.*/
+ .mstack (NOLOAD) :
+ {
+ . = ALIGN(8);
+ __main_stack_base__ = .;
+ . += __main_stack_size__;
+ . = ALIGN(8);
+ __main_stack_end__ = .;
+ } > MAIN_STACK_RAM
+
+ /* Special section for process stack.*/
+ .pstack (NOLOAD) :
+ {
+ . = ALIGN(8);
+ __process_stack_base__ = .;
+ __main_thread_stack_base__ = .;
+ . += __process_stack_size__;
+ . = ALIGN(8);
+ __process_stack_end__ = .;
+ __main_thread_stack_end__ = .;
+ } > PROCESS_STACK_RAM
+}
+
+/* Code rules inclusion (rules_code.ld).*/
+ENTRY(Reset_Handler)
+
+__vectors_align__ = DEFINED(__vectors_align__) ? __vectors_align__ : 1024;
+
+SECTIONS
+{
+ .vectors : ALIGN(__vectors_align__)
+ {
+ __textvectors_base__ = LOADADDR(.vectors);
+ __vectors_base__ = .;
+ KEEP(*(.vectors))
+ __vectors_end__ = .;
+ } > VECTORS_FLASH AT > VECTORS_FLASH_LMA
+
+ .xtors : ALIGN(4)
+ {
+ __init_array_base__ = .;
+ KEEP(*(SORT(.init_array.*)))
+ KEEP(*(.init_array))
+ __init_array_end__ = .;
+ __fini_array_base__ = .;
+ KEEP(*(.fini_array))
+ KEEP(*(SORT(.fini_array.*)))
+ __fini_array_end__ = .;
+ } > XTORS_FLASH AT > XTORS_FLASH_LMA
+
+ .text : ALIGN_WITH_INPUT
+ {
+ __text_base__ = .;
+ *(.text)
+ *(.text.*)
+ *(.glue_7t)
+ *(.glue_7)
+ *(.gcc*)
+ __text_end__ = .;
+ } > TEXT_FLASH AT > TEXT_FLASH_LMA
+
+ .rodata : ALIGN(4)
+ {
+ __rodata_base__ = .;
+ *(.rodata)
+ *(.rodata.*)
+ . = ALIGN(4);
+ __rodata_end__ = .;
+ } > RODATA_FLASH AT > RODATA_FLASH_LMA
+
+ .ARM.extab :
+ {
+ *(.ARM.extab* .gnu.linkonce.armextab.*)
+ } > VARIOUS_FLASH AT > VARIOUS_FLASH_LMA
+
+ .ARM.exidx : {
+ __exidx_base__ = .;
+ __exidx_start = .;
+ *(.ARM.exidx* .gnu.linkonce.armexidx.*)
+ __exidx_end__ = .;
+ __exidx_end = .;
+ } > VARIOUS_FLASH AT > VARIOUS_FLASH_LMA
+
+ .eh_frame_hdr :
+ {
+ *(.eh_frame_hdr)
+ } > VARIOUS_FLASH AT > VARIOUS_FLASH_LMA
+
+ .eh_frame : ONLY_IF_RO
+ {
+ *(.eh_frame)
+ } > VARIOUS_FLASH AT > VARIOUS_FLASH_LMA
+}
+
+/* Data rules inclusion (rules_data.ld).*/
+SECTIONS
+{
+ .data : ALIGN(4)
+ {
+ PROVIDE(_textdata = LOADADDR(.data));
+ PROVIDE(_data = .);
+ __textdata_base__ = LOADADDR(.data);
+ __data_base__ = .;
+ *(.data)
+ *(.data.*)
+ *(.ramtext)
+ . = ALIGN(4);
+ PROVIDE(_edata = .);
+ __data_end__ = .;
+ } > DATA_RAM AT > DATA_RAM_LMA
+
+ .bss (NOLOAD) : ALIGN(4)
+ {
+ __bss_base__ = .;
+ *(.bss)
+ *(.bss.*)
+ *(COMMON)
+ . = ALIGN(4);
+ __bss_end__ = .;
+ PROVIDE(end = .);
+ } > BSS_RAM
+}
+
+/* Memory rules inclusion (rules_memory.ld).*/
+__ram0_base__ = ORIGIN(ram0);
+__ram0_size__ = LENGTH(ram0);
+__ram0_end__ = __ram0_base__ + __ram0_size__;
+__ram1_base__ = ORIGIN(ram1);
+__ram1_size__ = LENGTH(ram1);
+__ram1_end__ = __ram1_base__ + __ram1_size__;
+__ram2_base__ = ORIGIN(ram2);
+__ram2_size__ = LENGTH(ram2);
+__ram2_end__ = __ram2_base__ + __ram2_size__;
+__ram3_base__ = ORIGIN(ram3);
+__ram3_size__ = LENGTH(ram3);
+__ram3_end__ = __ram3_base__ + __ram3_size__;
+__ram4_base__ = ORIGIN(ram4);
+__ram4_size__ = LENGTH(ram4);
+__ram4_end__ = __ram4_base__ + __ram4_size__;
+__ram5_base__ = ORIGIN(ram5);
+__ram5_size__ = LENGTH(ram5);
+__ram5_end__ = __ram5_base__ + __ram5_size__;
+__ram6_base__ = ORIGIN(ram6);
+__ram6_size__ = LENGTH(ram6);
+__ram6_end__ = __ram6_base__ + __ram6_size__;
+__ram7_base__ = ORIGIN(ram7);
+__ram7_size__ = LENGTH(ram7);
+__ram7_end__ = __ram7_base__ + __ram7_size__;
+
+__flash0_base__ = ORIGIN(flash0);
+__flash0_size__ = LENGTH(flash0);
+__flash0_end__ = __flash0_base__ + __flash0_size__;
+__flash1_base__ = ORIGIN(flash1);
+__flash1_size__ = LENGTH(flash1);
+__flash1_end__ = __flash1_base__ + __flash1_size__;
+__flash2_base__ = ORIGIN(flash2);
+__flash2_size__ = LENGTH(flash2);
+__flash2_end__ = __flash2_base__ + __flash2_size__;
+__flash3_base__ = ORIGIN(flash3);
+__flash3_size__ = LENGTH(flash3);
+__flash3_end__ = __flash3_base__ + __flash3_size__;
+__flash4_base__ = ORIGIN(flash4);
+__flash4_size__ = LENGTH(flash4);
+__flash4_end__ = __flash4_base__ + __flash4_size__;
+__flash5_base__ = ORIGIN(flash5);
+__flash5_size__ = LENGTH(flash5);
+__flash5_end__ = __flash5_base__ + __flash5_size__;
+__flash6_base__ = ORIGIN(flash6);
+__flash6_size__ = LENGTH(flash6);
+__flash6_end__ = __flash6_base__ + __flash6_size__;
+__flash7_base__ = ORIGIN(flash7);
+__flash7_size__ = LENGTH(flash7);
+__flash7_end__ = __flash7_base__ + __flash7_size__;
+
+SECTIONS
+{
+ .ram0_init : ALIGN(4)
+ {
+ __ram0_init_text__ = LOADADDR(.ram0_init);
+ __ram0_init__ = .;
+ KEEP(*(.ram0_init))
+ KEEP(*(.ram0_init.*))
+ . = ALIGN(4);
+ } > ram0 AT > RAM_INIT_FLASH_LMA
+
+ .ram0 (NOLOAD) : ALIGN(4)
+ {
+ __ram0_clear__ = .;
+ *(.ram0_clear)
+ *(.ram0_clear.*)
+ . = ALIGN(4);
+ __ram0_noinit__ = .;
+ *(.ram0)
+ *(.ram0.*)
+ . = ALIGN(4);
+ __ram0_free__ = .;
+ } > ram0
+
+ .ram1_init : ALIGN(4)
+ {
+ __ram1_init_text__ = LOADADDR(.ram1_init);
+ __ram1_init__ = .;
+ KEEP(*(.ram1_init))
+ KEEP(*(.ram1_init.*))
+ . = ALIGN(4);
+ } > ram1 AT > RAM_INIT_FLASH_LMA
+
+ .ram1 (NOLOAD) : ALIGN(4)
+ {
+ __ram1_clear__ = .;
+ *(.ram1_clear)
+ *(.ram1_clear.*)
+ . = ALIGN(4);
+ __ram1_noinit__ = .;
+ *(.ram1)
+ *(.ram1.*)
+ . = ALIGN(4);
+ __ram1_free__ = .;
+ } > ram1
+
+ .ram2_init : ALIGN(4)
+ {
+ __ram2_init_text__ = LOADADDR(.ram2_init);
+ __ram2_init__ = .;
+ KEEP(*(.ram2_init))
+ KEEP(*(.ram2_init.*))
+ . = ALIGN(4);
+ } > ram2 AT > RAM_INIT_FLASH_LMA
+
+ .ram2 (NOLOAD) : ALIGN(4)
+ {
+ __ram2_clear__ = .;
+ *(.ram2_clear)
+ *(.ram2_clear.*)
+ . = ALIGN(4);
+ __ram2_noinit__ = .;
+ *(.ram2)
+ *(.ram2.*)
+ . = ALIGN(4);
+ __ram2_free__ = .;
+ } > ram2
+
+ .ram3_init : ALIGN(4)
+ {
+ __ram3_init_text__ = LOADADDR(.ram3_init);
+ __ram3_init__ = .;
+ KEEP(*(.ram3_init))
+ KEEP(*(.ram3_init.*))
+ . = ALIGN(4);
+ } > ram3 AT > RAM_INIT_FLASH_LMA
+
+ .ram3 (NOLOAD) : ALIGN(4)
+ {
+ __ram3_clear__ = .;
+ *(.ram3_clear)
+ *(.ram3_clear.*)
+ . = ALIGN(4);
+ __ram3_noinit__ = .;
+ *(.ram3)
+ *(.ram3.*)
+ . = ALIGN(4);
+ __ram3_free__ = .;
+ } > ram3
+
+ .ram4_init : ALIGN(4)
+ {
+ __ram4_init_text__ = LOADADDR(.ram4_init);
+ __ram4_init__ = .;
+ KEEP(*(.ram4_init))
+ KEEP(*(.ram4_init.*))
+ . = ALIGN(4);
+ } > ram4 AT > RAM_INIT_FLASH_LMA
+
+ .ram4 (NOLOAD) : ALIGN(4)
+ {
+ __ram4_clear__ = .;
+ *(.ram4_clear)
+ *(.ram4_clear.*)
+ . = ALIGN(4);
+ __ram4_noinit__ = .;
+ *(.ram4)
+ *(.ram4.*)
+ . = ALIGN(4);
+ __ram4_free__ = .;
+ } > ram4
+
+ .ram5_init : ALIGN(4)
+ {
+ __ram5_init_text__ = LOADADDR(.ram5_init);
+ __ram5_init__ = .;
+ KEEP(*(.ram5_init))
+ KEEP(*(.ram5_init.*))
+ . = ALIGN(4);
+ } > ram5 AT > RAM_INIT_FLASH_LMA
+
+ .ram5 (NOLOAD) : ALIGN(4)
+ {
+ __ram5_clear__ = .;
+ *(.ram5_clear)
+ *(.ram5_clear.*)
+ . = ALIGN(4);
+ __ram5_noinit__ = .;
+ *(.ram5)
+ *(.ram5.*)
+ . = ALIGN(4);
+ __ram5_free__ = .;
+ } > ram5
+
+ .ram6_init : ALIGN(4)
+ {
+ __ram6_init_text__ = LOADADDR(.ram6_init);
+ __ram6_init__ = .;
+ KEEP(*(.ram6_init))
+ KEEP(*(.ram6_init.*))
+ . = ALIGN(4);
+ } > ram6 AT > RAM_INIT_FLASH_LMA
+
+ .ram6 (NOLOAD) : ALIGN(4)
+ {
+ __ram6_clear__ = .;
+ *(.ram6_clear)
+ *(.ram6_clear.*)
+ . = ALIGN(4);
+ __ram6_noinit__ = .;
+ *(.ram6)
+ *(.ram6.*)
+ . = ALIGN(4);
+ __ram6_free__ = .;
+ } > ram6
+
+ .ram7_init : ALIGN(4)
+ {
+ __ram7_init_text__ = LOADADDR(.ram7_init);
+ __ram7_init__ = .;
+ KEEP(*(.ram7_init))
+ KEEP(*(.ram7_init.*))
+ . = ALIGN(4);
+ } > ram7 AT > RAM_INIT_FLASH_LMA
+
+ .ram7 (NOLOAD) : ALIGN(4)
+ {
+ __ram7_clear__ = .;
+ *(.ram7_clear)
+ *(.ram7_clear.*)
+ . = ALIGN(4);
+ __ram7_noinit__ = .;
+ *(.ram7)
+ *(.ram7.*)
+ . = ALIGN(4);
+ __ram7_free__ = .;
+ } > ram7
+
+ .flash0 : ALIGN(4)
+ {
+ __flash0_init__ = .;
+ KEEP(*(.flash0_init))
+ KEEP(*(.flash0_init.*))
+ __flash0_free__ = .;
+ } > flash0
+
+ .flash1 : ALIGN(4)
+ {
+ __flash1_init__ = .;
+ KEEP(*(.flash1_init))
+ KEEP(*(.flash1_init.*))
+ __flash1_free__ = .;
+ } > flash1
+
+ .flash2 : ALIGN(4)
+ {
+ __flash2_init__ = .;
+ KEEP(*(.flash2_init))
+ KEEP(*(.flash2_init.*))
+ __flash2_free__ = .;
+ } > flash2
+
+ .flash3 : ALIGN(4)
+ {
+ __flash3_init__ = .;
+ KEEP(*(.flash3_init))
+ KEEP(*(.flash3_init.*))
+ __flash3_free__ = .;
+ } > flash3
+
+ .flash4 : ALIGN(4)
+ {
+ __flash4_init__ = .;
+ KEEP(*(.flash4_init))
+ KEEP(*(.flash4_init.*))
+ __flash4_free__ = .;
+ } > flash4
+
+ .flash5 : ALIGN(4)
+ {
+ __flash5_init__ = .;
+ KEEP(*(.flash5_init))
+ KEEP(*(.flash5_init.*))
+ __flash5_free__ = .;
+ } > flash5
+
+ .flash6 : ALIGN(4)
+ {
+ __flash6_init__ = .;
+ KEEP(*(.flash6_init))
+ KEEP(*(.flash6_init.*))
+ __flash6_free__ = .;
+ } > flash6
+
+ .flash7 : ALIGN(4)
+ {
+ __flash7_init__ = .;
+ KEEP(*(.flash7_init))
+ KEEP(*(.flash7_init.*))
+ __flash7_free__ = .;
+ } > flash7
+
+ /* The default heap uses the (statically) unused part of a RAM section.*/
+ .heap (NOLOAD) :
+ {
+ . = ALIGN(8);
+ __heap_base__ = .;
+ . = ORIGIN(HEAP_RAM) + LENGTH(HEAP_RAM);
+ __heap_end__ = .;
+ } > HEAP_RAM
+}
diff --git a/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk b/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
new file mode 100644
index 0000000000..2af55a8e96
--- /dev/null
+++ b/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
@@ -0,0 +1,20 @@
+# List of the ChibiOS generic SK32F0xx startup and CMSIS files.
+STARTUPSRC = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/crt1.c
+
+STARTUPASM = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/crt0_v6m.S \
+ $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/vectors.S
+
+STARTUPINC = $(CHIBIOS)/os/common/portability/GCC \
+ $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC \
+ $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/ld \
+ $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/devices/SK32F0xx \
+ $(CHIBIOS)/os/common/ext/ARM/CMSIS/Core/Include \
+ $(CHIBIOS_CONTRIB)/os/common/ext/CMSIS/SK32/SK32F0xx
+
+STARTUPLD = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/ld
+STARTUPLD_CONTRIB = $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/ld
+
+# Shared variables
+ALLXASMSRC += $(STARTUPASM)
+ALLCSRC += $(STARTUPSRC)
+ALLINC += $(STARTUPINC)
diff --git a/os/common/startup/ARMCMx/devices/SK32F0xx/cmparams.h b/os/common/startup/ARMCMx/devices/SK32F0xx/cmparams.h
new file mode 100644
index 0000000000..ca1092f997
--- /dev/null
+++ b/os/common/startup/ARMCMx/devices/SK32F0xx/cmparams.h
@@ -0,0 +1,92 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/cmparams.h
+ * @brief ARM Cortex-M0 parameters for the 3Think SK32F0xx.
+ *
+ * @defgroup ARMCMx_SK32F0xx SK32F0xx Specific Parameters
+ * @ingroup ARMCMx_SPECIFIC
+ * @details This file contains the Cortex-M0 specific parameters for the
+ * SK32F0xx platform.
+ * @{
+ */
+
+#ifndef CMPARAMS_H
+#define CMPARAMS_H
+
+/**
+ * @brief Cortex core model.
+ */
+#define CORTEX_MODEL 0
+
+/**
+ * @brief Floating Point unit presence.
+ */
+#define CORTEX_HAS_FPU 0
+
+/**
+ * @brief Number of bits in priority masks.
+ */
+#define CORTEX_PRIORITY_BITS 2U
+
+/* If the device type is not externally defined, for example from the Makefile,
+ then a file named board.h is included. This file must contain a device
+ definition compatible with the vendor include file.*/
+#if !defined (SK32F072xB) && !defined (SK32F077xB)
+#include "board.h"
+#endif
+
+/**
+ * @brief Number of interrupt vectors.
+ * @note This number does not include the 16 system vectors and must be
+ * rounded to a multiple of 8.
+ * @note SK32F077 devices expose only 32 IRQ lines (0..31), some vector
+ * slots (8, 13-15, 24, 26) are Reserved on the SK32F077 variant.
+ */
+#define CORTEX_NUM_VECTORS 32
+
+/**
+ * @brief Number of MPU regions.
+ */
+#define CORTEX_MPU_REGIONS 0
+
+/* The following code is not processed when the file is included from an
+ asm module.*/
+#if !defined(_FROM_ASM_)
+
+/* Including the device CMSIS header. Note, we are not using the definitions
+ from this header because we need this file to be usable also from
+ assembler source files. We verify that the info matches instead.*/
+#include "sk32f0xx.h"
+
+/*lint -save -e9029 [10.4] Signedness comes from external files, it is
+ unpredictable but gives no problems.*/
+#if CORTEX_MODEL != __CORTEX_M
+#error "CMSIS __CORTEX_M mismatch"
+#endif
+
+#if CORTEX_PRIORITY_BITS != __NVIC_PRIO_BITS
+#error "CMSIS __NVIC_PRIO_BITS mismatch"
+#endif
+/*lint -restore*/
+
+#endif /* !defined(_FROM_ASM_) */
+
+#endif /* CMPARAMS_H */
+
+/** @} */
diff --git a/os/hal/boards/GENERIC_SK32_F077/board.c b/os/hal/boards/GENERIC_SK32_F077/board.c
new file mode 100644
index 0000000000..310a767d79
--- /dev/null
+++ b/os/hal/boards/GENERIC_SK32_F077/board.c
@@ -0,0 +1,218 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#include "hal.h"
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/* GPIO enable bits are located in bits 17..22 of the AHB clock/reset
+ registers (the vendor device header contains wrong values for the GPIOD,
+ GPIOE, GPIOF reset macros, the enable macros are correct and are used
+ here).*/
+#define SK32_GPIO_EN_MASK (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | \
+ RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN | \
+ RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOFEN)
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type of SK32F0xx GPIO port setup.
+ */
+typedef struct {
+ uint32_t moder;
+ uint32_t otyper;
+ uint32_t ospeedr;
+ uint32_t pupdr;
+ uint32_t odr;
+ uint32_t afrl;
+ uint32_t afrh;
+} gpio_setup_t;
+
+/**
+ * @brief Type of SK32F0xx GPIO initialization data.
+ */
+typedef struct {
+#if SK32_HAS_GPIOA || defined(__DOXYGEN__)
+ gpio_setup_t PAData;
+#endif
+#if SK32_HAS_GPIOB || defined(__DOXYGEN__)
+ gpio_setup_t PBData;
+#endif
+#if SK32_HAS_GPIOC || defined(__DOXYGEN__)
+ gpio_setup_t PCData;
+#endif
+#if SK32_HAS_GPIOD || defined(__DOXYGEN__)
+ gpio_setup_t PDData;
+#endif
+#if SK32_HAS_GPIOE || defined(__DOXYGEN__)
+ gpio_setup_t PEData;
+#endif
+#if SK32_HAS_GPIOF || defined(__DOXYGEN__)
+ gpio_setup_t PFData;
+#endif
+} gpio_config_t;
+
+/**
+ * @brief SK32F0xx GPIO static initialization data.
+ */
+static const gpio_config_t gpio_default_config = {
+#if SK32_HAS_GPIOA
+ {VAL_GPIOA_MODER, VAL_GPIOA_OTYPER, VAL_GPIOA_OSPEEDR, VAL_GPIOA_PUPDR,
+ VAL_GPIOA_ODR, VAL_GPIOA_AFRL, VAL_GPIOA_AFRH},
+#endif
+#if SK32_HAS_GPIOB
+ {VAL_GPIOB_MODER, VAL_GPIOB_OTYPER, VAL_GPIOB_OSPEEDR, VAL_GPIOB_PUPDR,
+ VAL_GPIOB_ODR, VAL_GPIOB_AFRL, VAL_GPIOB_AFRH},
+#endif
+#if SK32_HAS_GPIOC
+ {VAL_GPIOC_MODER, VAL_GPIOC_OTYPER, VAL_GPIOC_OSPEEDR, VAL_GPIOC_PUPDR,
+ VAL_GPIOC_ODR, VAL_GPIOC_AFRL, VAL_GPIOC_AFRH},
+#endif
+#if SK32_HAS_GPIOD
+ {VAL_GPIOD_MODER, VAL_GPIOD_OTYPER, VAL_GPIOD_OSPEEDR, VAL_GPIOD_PUPDR,
+ VAL_GPIOD_ODR, VAL_GPIOD_AFRL, VAL_GPIOD_AFRH},
+#endif
+#if SK32_HAS_GPIOE
+ {VAL_GPIOE_MODER, VAL_GPIOE_OTYPER, VAL_GPIOE_OSPEEDR, VAL_GPIOE_PUPDR,
+ VAL_GPIOE_ODR, VAL_GPIOE_AFRL, VAL_GPIOE_AFRH},
+#endif
+#if SK32_HAS_GPIOF
+ {VAL_GPIOF_MODER, VAL_GPIOF_OTYPER, VAL_GPIOF_OSPEEDR, VAL_GPIOF_PUPDR,
+ VAL_GPIOF_ODR, VAL_GPIOF_AFRL, VAL_GPIOF_AFRH}
+#endif
+};
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static void gpio_init(GPIO_TypeDef *gpiop, const gpio_setup_t *config) {
+
+ gpiop->OTYPER = (uint16_t)config->otyper;
+ gpiop->OSPEEDR = config->ospeedr;
+ gpiop->PUPDR = config->pupdr;
+ gpiop->ODR = (uint16_t)config->odr;
+ gpiop->AFR[0] = config->afrl;
+ gpiop->AFR[1] = config->afrh;
+ gpiop->MODER = config->moder;
+}
+
+static void sk32_gpio_init(void) {
+
+ /* Enabling GPIO-related clocks, the mask comes from the local
+ definitions above.*/
+ rccResetAHB(SK32_GPIO_EN_MASK);
+ rccEnableAHB(SK32_GPIO_EN_MASK, true);
+
+ /* Initializing all the defined GPIO ports.*/
+#if SK32_HAS_GPIOA
+ gpio_init(GPIOA, &gpio_default_config.PAData);
+#endif
+#if SK32_HAS_GPIOB
+ gpio_init(GPIOB, &gpio_default_config.PBData);
+#endif
+#if SK32_HAS_GPIOC
+ gpio_init(GPIOC, &gpio_default_config.PCData);
+#endif
+#if SK32_HAS_GPIOD
+ gpio_init(GPIOD, &gpio_default_config.PDData);
+#endif
+#if SK32_HAS_GPIOE
+ gpio_init(GPIOE, &gpio_default_config.PEData);
+#endif
+#if SK32_HAS_GPIOF
+ gpio_init(GPIOF, &gpio_default_config.PFData);
+#endif
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Early initialization code.
+ * @details GPIO ports and system clocks are initialized before everything
+ * else.
+ */
+void __early_init(void) {
+
+ sk32_gpio_init();
+ sk32_clock_init();
+}
+
+#if HAL_USE_SDC || defined(__DOXYGEN__)
+/**
+ * @brief SDC card detection.
+ */
+bool sdc_lld_is_card_inserted(SDCDriver *sdcp) {
+
+ (void)sdcp;
+ /* CHTODO: Fill the implementation.*/
+ return true;
+}
+
+/**
+ * @brief SDC card write protection detection.
+ */
+bool sdc_lld_is_write_protected(SDCDriver *sdcp) {
+
+ (void)sdcp;
+ /* CHTODO: Fill the implementation.*/
+ return false;
+}
+#endif /* HAL_USE_SDC */
+
+#if HAL_USE_MMC_SPI || defined(__DOXYGEN__)
+/**
+ * @brief MMC_SPI card detection.
+ */
+bool mmc_lld_is_card_inserted(MMCDriver *mmcp) {
+
+ (void)mmcp;
+ /* CHTODO: Fill the implementation.*/
+ return true;
+}
+
+/**
+ * @brief MMC_SPI card write protection detection.
+ */
+bool mmc_lld_is_write_protected(MMCDriver *mmcp) {
+
+ (void)mmcp;
+ /* CHTODO: Fill the implementation.*/
+ return false;
+}
+#endif
+
+/**
+ * @brief Board-specific initialization code.
+ * @note You can add your board-specific code here.
+ */
+void boardInit(void) {
+
+}
diff --git a/os/hal/boards/GENERIC_SK32_F077/board.h b/os/hal/boards/GENERIC_SK32_F077/board.h
new file mode 100644
index 0000000000..e8ed3b4812
--- /dev/null
+++ b/os/hal/boards/GENERIC_SK32_F077/board.h
@@ -0,0 +1,973 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/*
+ * This file has been automatically generated using ChibiStudio board
+ * generator plugin. Do not edit manually.
+ */
+
+#ifndef BOARD_H
+#define BOARD_H
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*
+ * Setup for the generic SK32F077 board.
+ * The SK32F0xx family is register-compatible with the STM32F072 except for
+ * the USB IP (SK32F077: Mentor-MUSB-class musbfsfc controller).
+ */
+
+/*
+ * Board identifier.
+ */
+#define BOARD_GENERIC_SK32_F077
+#define BOARD_NAME "3Think SK32F077 generic board"
+
+/*
+ * Board oscillators-related settings.
+ * NOTE: LSE not fitted, the SK32 system clock is generated from the internal
+ * HSI oscillator through the main PLL by the SK32 clock driver (mcuconf.h).
+ */
+#if !defined(SK32_LSECLK)
+#define SK32_LSECLK 0U
+#endif
+
+#define SK32_LSEDRV (3U << 3U)
+
+#if !defined(SK32_HSECLK)
+#define SK32_HSECLK 8000000U
+#endif
+
+#define SK32_HSE_BYPASS
+
+/*
+ * MCU device variant, selected by the project Makefile through the
+ * -DSK32F077xB or -DSK32F072xB define (see sk32_registry.h).
+ */
+
+/*
+ * IO pins assignments.
+ */
+#define GPIOA_ARD_A0 0U
+#define GPIOA_ADC1_IN0 0U
+#define GPIOA_ARD_A1 1U
+#define GPIOA_ADC1_IN1 1U
+#define GPIOA_ARD_D1 2U
+#define GPIOA_USART2_TX 2U
+#define GPIOA_ARD_D0 3U
+#define GPIOA_USART2_RX 3U
+#define GPIOA_ARD_A2 4U
+#define GPIOA_ADC1_IN4 4U
+#define GPIOA_LED_GREEN 5U
+#define GPIOA_ARD_D12 6U
+#define GPIOA_ARD_D11 7U
+#define GPIOA_ARD_D7 8U
+#define GPIOA_ARD_D8 9U
+#define GPIOA_ARD_D2 10U
+#define GPIOA_PIN11 11U
+#define GPIOA_PIN12 12U
+#define GPIOA_SWDIO 13U
+#define GPIOA_SWCLK 14U
+#define GPIOA_PIN15 15U
+
+#define GPIOB_ARD_A3 0U
+#define GPIOB_ADC1_IN8 0U
+#define GPIOB_PIN1 1U
+#define GPIOB_PIN2 2U
+#define GPIOB_SWO 3U
+#define GPIOB_ARD_D3 3U
+#define GPIOB_ARD_D5 4U
+#define GPIOB_ARD_D4 5U
+#define GPIOB_ARD_D10 6U
+#define GPIOB_PIN7 7U
+#define GPIOB_ARD_D15 8U
+#define GPIOB_ARD_D14 9U
+#define GPIOB_ARD_D6 10U
+#define GPIOB_PIN11 11U
+#define GPIOB_PIN12 12U
+#define GPIOB_PIN13 13U
+#define GPIOB_PIN14 14U
+#define GPIOB_PIN15 15U
+
+#define GPIOC_ARD_A5 0U
+#define GPIOC_ADC1_IN11 0U
+#define GPIOC_ARD_A4 1U
+#define GPIOC_ADC1_IN10 1U
+#define GPIOC_PIN2 2U
+#define GPIOC_PIN3 3U
+#define GPIOC_PIN4 4U
+#define GPIOC_PIN5 5U
+#define GPIOC_PIN6 6U
+#define GPIOC_ARD_D9 7U
+#define GPIOC_PIN8 8U
+#define GPIOC_PIN9 9U
+#define GPIOC_PIN10 10U
+#define GPIOC_PIN11 11U
+#define GPIOC_PIN12 12U
+#define GPIOC_BUTTON 13U
+#define GPIOC_OSC32_IN 14U
+#define GPIOC_OSC32_OUT 15U
+
+#define GPIOD_PIN0 0U
+#define GPIOD_PIN1 1U
+#define GPIOD_PIN2 2U
+#define GPIOD_PIN3 3U
+#define GPIOD_PIN4 4U
+#define GPIOD_PIN5 5U
+#define GPIOD_PIN6 6U
+#define GPIOD_PIN7 7U
+#define GPIOD_PIN8 8U
+#define GPIOD_PIN9 9U
+#define GPIOD_PIN10 10U
+#define GPIOD_PIN11 11U
+#define GPIOD_PIN12 12U
+#define GPIOD_PIN13 13U
+#define GPIOD_PIN14 14U
+#define GPIOD_PIN15 15U
+
+#define GPIOE_PIN0 0U
+#define GPIOE_PIN1 1U
+#define GPIOE_PIN2 2U
+#define GPIOE_PIN3 3U
+#define GPIOE_PIN4 4U
+#define GPIOE_PIN5 5U
+#define GPIOE_PIN6 6U
+#define GPIOE_PIN7 7U
+#define GPIOE_PIN8 8U
+#define GPIOE_PIN9 9U
+#define GPIOE_PIN10 10U
+#define GPIOE_PIN11 11U
+#define GPIOE_PIN12 12U
+#define GPIOE_PIN13 13U
+#define GPIOE_PIN14 14U
+#define GPIOE_PIN15 15U
+
+#define GPIOF_OSC_IN 0U
+#define GPIOF_OSC_OUT 1U
+#define GPIOF_PIN2 2U
+#define GPIOF_PIN3 3U
+#define GPIOF_PIN4 4U
+#define GPIOF_PIN5 5U
+#define GPIOF_PIN6 6U
+#define GPIOF_PIN7 7U
+#define GPIOF_PIN8 8U
+#define GPIOF_PIN9 9U
+#define GPIOF_PIN10 10U
+#define GPIOF_PIN11 11U
+#define GPIOF_PIN12 12U
+#define GPIOF_PIN13 13U
+#define GPIOF_PIN14 14U
+#define GPIOF_PIN15 15U
+
+/*
+ * IO lines assignments.
+ */
+#define LINE_ARD_A0 PAL_LINE(GPIOA, 0U)
+#define LINE_ADC1_IN0 PAL_LINE(GPIOA, 0U)
+#define LINE_ARD_A1 PAL_LINE(GPIOA, 1U)
+#define LINE_ADC1_IN1 PAL_LINE(GPIOA, 1U)
+#define LINE_ARD_D1 PAL_LINE(GPIOA, 2U)
+#define LINE_USART2_TX PAL_LINE(GPIOA, 2U)
+#define LINE_ARD_D0 PAL_LINE(GPIOA, 3U)
+#define LINE_USART2_RX PAL_LINE(GPIOA, 3U)
+#define LINE_ARD_A2 PAL_LINE(GPIOA, 4U)
+#define LINE_ADC1_IN4 PAL_LINE(GPIOA, 4U)
+#define LINE_LED_GREEN PAL_LINE(GPIOA, 5U)
+#define LINE_ARD_D12 PAL_LINE(GPIOA, 6U)
+#define LINE_ARD_D11 PAL_LINE(GPIOA, 7U)
+#define LINE_ARD_D7 PAL_LINE(GPIOA, 8U)
+#define LINE_ARD_D8 PAL_LINE(GPIOA, 9U)
+#define LINE_ARD_D2 PAL_LINE(GPIOA, 10U)
+#define LINE_SWDIO PAL_LINE(GPIOA, 13U)
+#define LINE_SWCLK PAL_LINE(GPIOA, 14U)
+#define LINE_ARD_A3 PAL_LINE(GPIOB, 0U)
+#define LINE_ADC1_IN8 PAL_LINE(GPIOB, 0U)
+#define LINE_SWO PAL_LINE(GPIOB, 3U)
+#define LINE_ARD_D3 PAL_LINE(GPIOB, 3U)
+#define LINE_ARD_D5 PAL_LINE(GPIOB, 4U)
+#define LINE_ARD_D4 PAL_LINE(GPIOB, 5U)
+#define LINE_ARD_D10 PAL_LINE(GPIOB, 6U)
+#define LINE_ARD_D15 PAL_LINE(GPIOB, 8U)
+#define LINE_ARD_D14 PAL_LINE(GPIOB, 9U)
+#define LINE_ARD_D6 PAL_LINE(GPIOB, 10U)
+#define LINE_ARD_A5 PAL_LINE(GPIOC, 0U)
+#define LINE_ADC1_IN11 PAL_LINE(GPIOC, 0U)
+#define LINE_ARD_A4 PAL_LINE(GPIOC, 1U)
+#define LINE_ADC1_IN10 PAL_LINE(GPIOC, 1U)
+#define LINE_ARD_D9 PAL_LINE(GPIOC, 7U)
+#define LINE_BUTTON PAL_LINE(GPIOC, 13U)
+#define LINE_OSC32_IN PAL_LINE(GPIOC, 14U)
+#define LINE_OSC32_OUT PAL_LINE(GPIOC, 15U)
+#define LINE_OSC_IN PAL_LINE(GPIOF, 0U)
+#define LINE_OSC_OUT PAL_LINE(GPIOF, 1U)
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*
+ * I/O ports initial setup, this configuration is established soon after reset
+ * in the initialization code.
+ * Please refer to the STM32 Reference Manual for details.
+ */
+#define PIN_MODE_INPUT(n) (0U << ((n) * 2U))
+#define PIN_MODE_OUTPUT(n) (1U << ((n) * 2U))
+#define PIN_MODE_ALTERNATE(n) (2U << ((n) * 2U))
+#define PIN_MODE_ANALOG(n) (3U << ((n) * 2U))
+#define PIN_ODR_LOW(n) (0U << (n))
+#define PIN_ODR_HIGH(n) (1U << (n))
+#define PIN_OTYPE_PUSHPULL(n) (0U << (n))
+#define PIN_OTYPE_OPENDRAIN(n) (1U << (n))
+#define PIN_OSPEED_VERYLOW(n) (0U << ((n) * 2U))
+#define PIN_OSPEED_LOW(n) (1U << ((n) * 2U))
+#define PIN_OSPEED_MEDIUM(n) (2U << ((n) * 2U))
+#define PIN_OSPEED_HIGH(n) (3U << ((n) * 2U))
+#define PIN_PUPDR_FLOATING(n) (0U << ((n) * 2U))
+#define PIN_PUPDR_PULLUP(n) (1U << ((n) * 2U))
+#define PIN_PUPDR_PULLDOWN(n) (2U << ((n) * 2U))
+#define PIN_AFIO_AF(n, v) ((v) << (((n) % 8U) * 4U))
+
+/*
+ * GPIOA setup:
+ *
+ * PA0 - ARD_A0 ADC1_IN0 (input pullup).
+ * PA1 - ARD_A1 ADC1_IN1 (input pullup).
+ * PA2 - ARD_D1 USART2_TX (alternate 1).
+ * PA3 - ARD_D0 USART2_RX (alternate 1).
+ * PA4 - ARD_A2 ADC1_IN4 (input pullup).
+ * PA5 - LED_GREEN (output pushpull high).
+ * PA6 - ARD_D12 (input pullup).
+ * PA7 - ARD_D11 (input pullup).
+ * PA8 - ARD_D7 (input pullup).
+ * PA9 - ARD_D8 (input pullup).
+ * PA10 - ARD_D2 (input pullup).
+ * PA11 - PIN11 (input pullup).
+ * PA12 - PIN12 (input pullup).
+ * PA13 - SWDIO (alternate 0).
+ * PA14 - SWCLK (alternate 0).
+ * PA15 - PIN15 (input pullup).
+ */
+#define VAL_GPIOA_MODER (PIN_MODE_INPUT(GPIOA_ARD_A0) | \
+ PIN_MODE_INPUT(GPIOA_ARD_A1) | \
+ PIN_MODE_ALTERNATE(GPIOA_ARD_D1) | \
+ PIN_MODE_ALTERNATE(GPIOA_ARD_D0) | \
+ PIN_MODE_INPUT(GPIOA_ARD_A2) | \
+ PIN_MODE_OUTPUT(GPIOA_LED_GREEN) | \
+ PIN_MODE_INPUT(GPIOA_ARD_D12) | \
+ PIN_MODE_INPUT(GPIOA_ARD_D11) | \
+ PIN_MODE_INPUT(GPIOA_ARD_D7) | \
+ PIN_MODE_INPUT(GPIOA_ARD_D8) | \
+ PIN_MODE_INPUT(GPIOA_ARD_D2) | \
+ PIN_MODE_INPUT(GPIOA_PIN11) | \
+ PIN_MODE_INPUT(GPIOA_PIN12) | \
+ PIN_MODE_ALTERNATE(GPIOA_SWDIO) | \
+ PIN_MODE_ALTERNATE(GPIOA_SWCLK) | \
+ PIN_MODE_INPUT(GPIOA_PIN15))
+#define VAL_GPIOA_OTYPER (PIN_OTYPE_PUSHPULL(GPIOA_ARD_A0) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_ARD_A1) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_ARD_D1) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_ARD_D0) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_ARD_A2) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_LED_GREEN) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_ARD_D12) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_ARD_D11) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_ARD_D7) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_ARD_D8) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_ARD_D2) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_PIN11) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_PIN12) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_SWDIO) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_SWCLK) | \
+ PIN_OTYPE_PUSHPULL(GPIOA_PIN15))
+#define VAL_GPIOA_OSPEEDR (PIN_OSPEED_HIGH(GPIOA_ARD_A0) | \
+ PIN_OSPEED_HIGH(GPIOA_ARD_A1) | \
+ PIN_OSPEED_LOW(GPIOA_ARD_D1) | \
+ PIN_OSPEED_LOW(GPIOA_ARD_D0) | \
+ PIN_OSPEED_HIGH(GPIOA_ARD_A2) | \
+ PIN_OSPEED_LOW(GPIOA_LED_GREEN) | \
+ PIN_OSPEED_HIGH(GPIOA_ARD_D12) | \
+ PIN_OSPEED_HIGH(GPIOA_ARD_D11) | \
+ PIN_OSPEED_HIGH(GPIOA_ARD_D7) | \
+ PIN_OSPEED_HIGH(GPIOA_ARD_D8) | \
+ PIN_OSPEED_HIGH(GPIOA_ARD_D2) | \
+ PIN_OSPEED_HIGH(GPIOA_PIN11) | \
+ PIN_OSPEED_HIGH(GPIOA_PIN12) | \
+ PIN_OSPEED_HIGH(GPIOA_SWDIO) | \
+ PIN_OSPEED_HIGH(GPIOA_SWCLK) | \
+ PIN_OSPEED_HIGH(GPIOA_PIN15))
+#define VAL_GPIOA_PUPDR (PIN_PUPDR_PULLUP(GPIOA_ARD_A0) | \
+ PIN_PUPDR_PULLUP(GPIOA_ARD_A1) | \
+ PIN_PUPDR_FLOATING(GPIOA_ARD_D1) | \
+ PIN_PUPDR_FLOATING(GPIOA_ARD_D0) | \
+ PIN_PUPDR_PULLUP(GPIOA_ARD_A2) | \
+ PIN_PUPDR_FLOATING(GPIOA_LED_GREEN) | \
+ PIN_PUPDR_PULLUP(GPIOA_ARD_D12) | \
+ PIN_PUPDR_PULLUP(GPIOA_ARD_D11) | \
+ PIN_PUPDR_PULLUP(GPIOA_ARD_D7) | \
+ PIN_PUPDR_PULLUP(GPIOA_ARD_D8) | \
+ PIN_PUPDR_PULLUP(GPIOA_ARD_D2) | \
+ PIN_PUPDR_PULLUP(GPIOA_PIN11) | \
+ PIN_PUPDR_PULLUP(GPIOA_PIN12) | \
+ PIN_PUPDR_PULLUP(GPIOA_SWDIO) | \
+ PIN_PUPDR_PULLDOWN(GPIOA_SWCLK) | \
+ PIN_PUPDR_PULLUP(GPIOA_PIN15))
+#define VAL_GPIOA_ODR (PIN_ODR_HIGH(GPIOA_ARD_A0) | \
+ PIN_ODR_HIGH(GPIOA_ARD_A1) | \
+ PIN_ODR_HIGH(GPIOA_ARD_D1) | \
+ PIN_ODR_HIGH(GPIOA_ARD_D0) | \
+ PIN_ODR_HIGH(GPIOA_ARD_A2) | \
+ PIN_ODR_LOW(GPIOA_LED_GREEN) | \
+ PIN_ODR_HIGH(GPIOA_ARD_D12) | \
+ PIN_ODR_HIGH(GPIOA_ARD_D11) | \
+ PIN_ODR_HIGH(GPIOA_ARD_D7) | \
+ PIN_ODR_HIGH(GPIOA_ARD_D8) | \
+ PIN_ODR_HIGH(GPIOA_ARD_D2) | \
+ PIN_ODR_HIGH(GPIOA_PIN11) | \
+ PIN_ODR_HIGH(GPIOA_PIN12) | \
+ PIN_ODR_HIGH(GPIOA_SWDIO) | \
+ PIN_ODR_HIGH(GPIOA_SWCLK) | \
+ PIN_ODR_HIGH(GPIOA_PIN15))
+#define VAL_GPIOA_AFRL (PIN_AFIO_AF(GPIOA_ARD_A0, 0U) | \
+ PIN_AFIO_AF(GPIOA_ARD_A1, 0U) | \
+ PIN_AFIO_AF(GPIOA_ARD_D1, 1U) | \
+ PIN_AFIO_AF(GPIOA_ARD_D0, 1U) | \
+ PIN_AFIO_AF(GPIOA_ARD_A2, 0U) | \
+ PIN_AFIO_AF(GPIOA_LED_GREEN, 0U) | \
+ PIN_AFIO_AF(GPIOA_ARD_D12, 0U) | \
+ PIN_AFIO_AF(GPIOA_ARD_D11, 0U))
+#define VAL_GPIOA_AFRH (PIN_AFIO_AF(GPIOA_ARD_D7, 0U) | \
+ PIN_AFIO_AF(GPIOA_ARD_D8, 0U) | \
+ PIN_AFIO_AF(GPIOA_ARD_D2, 0U) | \
+ PIN_AFIO_AF(GPIOA_PIN11, 0U) | \
+ PIN_AFIO_AF(GPIOA_PIN12, 0U) | \
+ PIN_AFIO_AF(GPIOA_SWDIO, 0U) | \
+ PIN_AFIO_AF(GPIOA_SWCLK, 0U) | \
+ PIN_AFIO_AF(GPIOA_PIN15, 0U))
+
+/*
+ * GPIOB setup:
+ *
+ * PB0 - ARD_A3 ADC1_IN8 (input pullup).
+ * PB1 - PIN1 (input pullup).
+ * PB2 - PIN2 (input pullup).
+ * PB3 - SWO ARD_D3 (alternate 0).
+ * PB4 - ARD_D5 (input pullup).
+ * PB5 - ARD_D4 (input pullup).
+ * PB6 - ARD_D10 (input pullup).
+ * PB7 - PIN7 (input pullup).
+ * PB8 - ARD_D15 (input pullup).
+ * PB9 - ARD_D14 (input pullup).
+ * PB10 - ARD_D6 (input pullup).
+ * PB11 - PIN11 (input pullup).
+ * PB12 - PIN12 (input pullup).
+ * PB13 - PIN13 (input pullup).
+ * PB14 - PIN14 (input pullup).
+ * PB15 - PIN15 (input pullup).
+ */
+#define VAL_GPIOB_MODER (PIN_MODE_INPUT(GPIOB_ARD_A3) | \
+ PIN_MODE_INPUT(GPIOB_PIN1) | \
+ PIN_MODE_INPUT(GPIOB_PIN2) | \
+ PIN_MODE_ALTERNATE(GPIOB_SWO) | \
+ PIN_MODE_INPUT(GPIOB_ARD_D5) | \
+ PIN_MODE_INPUT(GPIOB_ARD_D4) | \
+ PIN_MODE_INPUT(GPIOB_ARD_D10) | \
+ PIN_MODE_INPUT(GPIOB_PIN7) | \
+ PIN_MODE_INPUT(GPIOB_ARD_D15) | \
+ PIN_MODE_INPUT(GPIOB_ARD_D14) | \
+ PIN_MODE_INPUT(GPIOB_ARD_D6) | \
+ PIN_MODE_INPUT(GPIOB_PIN11) | \
+ PIN_MODE_INPUT(GPIOB_PIN12) | \
+ PIN_MODE_INPUT(GPIOB_PIN13) | \
+ PIN_MODE_INPUT(GPIOB_PIN14) | \
+ PIN_MODE_INPUT(GPIOB_PIN15))
+#define VAL_GPIOB_OTYPER (PIN_OTYPE_PUSHPULL(GPIOB_ARD_A3) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_PIN1) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_PIN2) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_SWO) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_ARD_D5) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_ARD_D4) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_ARD_D10) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_PIN7) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_ARD_D15) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_ARD_D14) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_ARD_D6) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_PIN11) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_PIN12) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_PIN13) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_PIN14) | \
+ PIN_OTYPE_PUSHPULL(GPIOB_PIN15))
+#define VAL_GPIOB_OSPEEDR (PIN_OSPEED_HIGH(GPIOB_ARD_A3) | \
+ PIN_OSPEED_HIGH(GPIOB_PIN1) | \
+ PIN_OSPEED_HIGH(GPIOB_PIN2) | \
+ PIN_OSPEED_HIGH(GPIOB_SWO) | \
+ PIN_OSPEED_HIGH(GPIOB_ARD_D5) | \
+ PIN_OSPEED_HIGH(GPIOB_ARD_D4) | \
+ PIN_OSPEED_HIGH(GPIOB_ARD_D10) | \
+ PIN_OSPEED_HIGH(GPIOB_PIN7) | \
+ PIN_OSPEED_HIGH(GPIOB_ARD_D15) | \
+ PIN_OSPEED_HIGH(GPIOB_ARD_D14) | \
+ PIN_OSPEED_HIGH(GPIOB_ARD_D6) | \
+ PIN_OSPEED_HIGH(GPIOB_PIN11) | \
+ PIN_OSPEED_HIGH(GPIOB_PIN12) | \
+ PIN_OSPEED_HIGH(GPIOB_PIN13) | \
+ PIN_OSPEED_HIGH(GPIOB_PIN14) | \
+ PIN_OSPEED_HIGH(GPIOB_PIN15))
+#define VAL_GPIOB_PUPDR (PIN_PUPDR_PULLUP(GPIOB_ARD_A3) | \
+ PIN_PUPDR_PULLUP(GPIOB_PIN1) | \
+ PIN_PUPDR_PULLUP(GPIOB_PIN2) | \
+ PIN_PUPDR_PULLUP(GPIOB_SWO) | \
+ PIN_PUPDR_PULLUP(GPIOB_ARD_D5) | \
+ PIN_PUPDR_PULLUP(GPIOB_ARD_D4) | \
+ PIN_PUPDR_PULLUP(GPIOB_ARD_D10) | \
+ PIN_PUPDR_PULLUP(GPIOB_PIN7) | \
+ PIN_PUPDR_PULLUP(GPIOB_ARD_D15) | \
+ PIN_PUPDR_PULLUP(GPIOB_ARD_D14) | \
+ PIN_PUPDR_PULLUP(GPIOB_ARD_D6) | \
+ PIN_PUPDR_PULLUP(GPIOB_PIN11) | \
+ PIN_PUPDR_PULLUP(GPIOB_PIN12) | \
+ PIN_PUPDR_PULLUP(GPIOB_PIN13) | \
+ PIN_PUPDR_PULLUP(GPIOB_PIN14) | \
+ PIN_PUPDR_PULLUP(GPIOB_PIN15))
+#define VAL_GPIOB_ODR (PIN_ODR_HIGH(GPIOB_ARD_A3) | \
+ PIN_ODR_HIGH(GPIOB_PIN1) | \
+ PIN_ODR_HIGH(GPIOB_PIN2) | \
+ PIN_ODR_HIGH(GPIOB_SWO) | \
+ PIN_ODR_HIGH(GPIOB_ARD_D5) | \
+ PIN_ODR_HIGH(GPIOB_ARD_D4) | \
+ PIN_ODR_HIGH(GPIOB_ARD_D10) | \
+ PIN_ODR_HIGH(GPIOB_PIN7) | \
+ PIN_ODR_HIGH(GPIOB_ARD_D15) | \
+ PIN_ODR_HIGH(GPIOB_ARD_D14) | \
+ PIN_ODR_HIGH(GPIOB_ARD_D6) | \
+ PIN_ODR_HIGH(GPIOB_PIN11) | \
+ PIN_ODR_HIGH(GPIOB_PIN12) | \
+ PIN_ODR_HIGH(GPIOB_PIN13) | \
+ PIN_ODR_HIGH(GPIOB_PIN14) | \
+ PIN_ODR_HIGH(GPIOB_PIN15))
+#define VAL_GPIOB_AFRL (PIN_AFIO_AF(GPIOB_ARD_A3, 0U) | \
+ PIN_AFIO_AF(GPIOB_PIN1, 0U) | \
+ PIN_AFIO_AF(GPIOB_PIN2, 0U) | \
+ PIN_AFIO_AF(GPIOB_SWO, 0U) | \
+ PIN_AFIO_AF(GPIOB_ARD_D5, 0U) | \
+ PIN_AFIO_AF(GPIOB_ARD_D4, 0U) | \
+ PIN_AFIO_AF(GPIOB_ARD_D10, 0U) | \
+ PIN_AFIO_AF(GPIOB_PIN7, 0U))
+#define VAL_GPIOB_AFRH (PIN_AFIO_AF(GPIOB_ARD_D15, 0U) | \
+ PIN_AFIO_AF(GPIOB_ARD_D14, 0U) | \
+ PIN_AFIO_AF(GPIOB_ARD_D6, 0U) | \
+ PIN_AFIO_AF(GPIOB_PIN11, 0U) | \
+ PIN_AFIO_AF(GPIOB_PIN12, 0U) | \
+ PIN_AFIO_AF(GPIOB_PIN13, 0U) | \
+ PIN_AFIO_AF(GPIOB_PIN14, 0U) | \
+ PIN_AFIO_AF(GPIOB_PIN15, 0U))
+
+/*
+ * GPIOC setup:
+ *
+ * PC0 - ARD_A5 ADC1_IN11 (input pullup).
+ * PC1 - ARD_A4 ADC1_IN10 (input pullup).
+ * PC2 - PIN2 (input pullup).
+ * PC3 - PIN3 (input pullup).
+ * PC4 - PIN4 (input pullup).
+ * PC5 - PIN5 (input pullup).
+ * PC6 - PIN6 (input pullup).
+ * PC7 - ARD_D9 (input pullup).
+ * PC8 - PIN8 (input pullup).
+ * PC9 - PIN9 (input pullup).
+ * PC10 - PIN10 (input pullup).
+ * PC11 - PIN11 (input pullup).
+ * PC12 - PIN12 (input pullup).
+ * PC13 - BUTTON (input floating).
+ * PC14 - OSC32_IN (input floating).
+ * PC15 - OSC32_OUT (input floating).
+ */
+#define VAL_GPIOC_MODER (PIN_MODE_INPUT(GPIOC_ARD_A5) | \
+ PIN_MODE_INPUT(GPIOC_ARD_A4) | \
+ PIN_MODE_INPUT(GPIOC_PIN2) | \
+ PIN_MODE_INPUT(GPIOC_PIN3) | \
+ PIN_MODE_INPUT(GPIOC_PIN4) | \
+ PIN_MODE_INPUT(GPIOC_PIN5) | \
+ PIN_MODE_INPUT(GPIOC_PIN6) | \
+ PIN_MODE_INPUT(GPIOC_ARD_D9) | \
+ PIN_MODE_INPUT(GPIOC_PIN8) | \
+ PIN_MODE_INPUT(GPIOC_PIN9) | \
+ PIN_MODE_INPUT(GPIOC_PIN10) | \
+ PIN_MODE_INPUT(GPIOC_PIN11) | \
+ PIN_MODE_INPUT(GPIOC_PIN12) | \
+ PIN_MODE_INPUT(GPIOC_BUTTON) | \
+ PIN_MODE_INPUT(GPIOC_OSC32_IN) | \
+ PIN_MODE_INPUT(GPIOC_OSC32_OUT))
+#define VAL_GPIOC_OTYPER (PIN_OTYPE_PUSHPULL(GPIOC_ARD_A5) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_ARD_A4) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN2) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN3) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN4) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN5) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN6) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_ARD_D9) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN8) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN9) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN10) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN11) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_PIN12) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_BUTTON) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_OSC32_IN) | \
+ PIN_OTYPE_PUSHPULL(GPIOC_OSC32_OUT))
+#define VAL_GPIOC_OSPEEDR (PIN_OSPEED_HIGH(GPIOC_ARD_A5) | \
+ PIN_OSPEED_HIGH(GPIOC_ARD_A4) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN2) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN3) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN4) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN5) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN6) | \
+ PIN_OSPEED_HIGH(GPIOC_ARD_D9) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN8) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN9) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN10) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN11) | \
+ PIN_OSPEED_HIGH(GPIOC_PIN12) | \
+ PIN_OSPEED_HIGH(GPIOC_BUTTON) | \
+ PIN_OSPEED_HIGH(GPIOC_OSC32_IN) | \
+ PIN_OSPEED_HIGH(GPIOC_OSC32_OUT))
+#define VAL_GPIOC_PUPDR (PIN_PUPDR_PULLUP(GPIOC_ARD_A5) | \
+ PIN_PUPDR_PULLUP(GPIOC_ARD_A4) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN2) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN3) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN4) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN5) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN6) | \
+ PIN_PUPDR_PULLUP(GPIOC_ARD_D9) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN8) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN9) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN10) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN11) | \
+ PIN_PUPDR_PULLUP(GPIOC_PIN12) | \
+ PIN_PUPDR_FLOATING(GPIOC_BUTTON) | \
+ PIN_PUPDR_FLOATING(GPIOC_OSC32_IN) | \
+ PIN_PUPDR_FLOATING(GPIOC_OSC32_OUT))
+#define VAL_GPIOC_ODR (PIN_ODR_HIGH(GPIOC_ARD_A5) | \
+ PIN_ODR_HIGH(GPIOC_ARD_A4) | \
+ PIN_ODR_HIGH(GPIOC_PIN2) | \
+ PIN_ODR_HIGH(GPIOC_PIN3) | \
+ PIN_ODR_HIGH(GPIOC_PIN4) | \
+ PIN_ODR_HIGH(GPIOC_PIN5) | \
+ PIN_ODR_HIGH(GPIOC_PIN6) | \
+ PIN_ODR_HIGH(GPIOC_ARD_D9) | \
+ PIN_ODR_HIGH(GPIOC_PIN8) | \
+ PIN_ODR_HIGH(GPIOC_PIN9) | \
+ PIN_ODR_HIGH(GPIOC_PIN10) | \
+ PIN_ODR_HIGH(GPIOC_PIN11) | \
+ PIN_ODR_HIGH(GPIOC_PIN12) | \
+ PIN_ODR_HIGH(GPIOC_BUTTON) | \
+ PIN_ODR_HIGH(GPIOC_OSC32_IN) | \
+ PIN_ODR_HIGH(GPIOC_OSC32_OUT))
+#define VAL_GPIOC_AFRL (PIN_AFIO_AF(GPIOC_ARD_A5, 0U) | \
+ PIN_AFIO_AF(GPIOC_ARD_A4, 0U) | \
+ PIN_AFIO_AF(GPIOC_PIN2, 0U) | \
+ PIN_AFIO_AF(GPIOC_PIN3, 0U) | \
+ PIN_AFIO_AF(GPIOC_PIN4, 0U) | \
+ PIN_AFIO_AF(GPIOC_PIN5, 0U) | \
+ PIN_AFIO_AF(GPIOC_PIN6, 0U) | \
+ PIN_AFIO_AF(GPIOC_ARD_D9, 0U))
+#define VAL_GPIOC_AFRH (PIN_AFIO_AF(GPIOC_PIN8, 0U) | \
+ PIN_AFIO_AF(GPIOC_PIN9, 0U) | \
+ PIN_AFIO_AF(GPIOC_PIN10, 0U) | \
+ PIN_AFIO_AF(GPIOC_PIN11, 0U) | \
+ PIN_AFIO_AF(GPIOC_PIN12, 0U) | \
+ PIN_AFIO_AF(GPIOC_BUTTON, 0U) | \
+ PIN_AFIO_AF(GPIOC_OSC32_IN, 0U) | \
+ PIN_AFIO_AF(GPIOC_OSC32_OUT, 0U))
+
+/*
+ * GPIOD setup:
+ *
+ * PD0 - PIN0 (input pullup).
+ * PD1 - PIN1 (input pullup).
+ * PD2 - PIN2 (input pullup).
+ * PD3 - PIN3 (input pullup).
+ * PD4 - PIN4 (input pullup).
+ * PD5 - PIN5 (input pullup).
+ * PD6 - PIN6 (input pullup).
+ * PD7 - PIN7 (input pullup).
+ * PD8 - PIN8 (input pullup).
+ * PD9 - PIN9 (input pullup).
+ * PD10 - PIN10 (input pullup).
+ * PD11 - PIN11 (input pullup).
+ * PD12 - PIN12 (input pullup).
+ * PD13 - PIN13 (input pullup).
+ * PD14 - PIN14 (input pullup).
+ * PD15 - PIN15 (input pullup).
+ */
+#define VAL_GPIOD_MODER (PIN_MODE_INPUT(GPIOD_PIN0) | \
+ PIN_MODE_INPUT(GPIOD_PIN1) | \
+ PIN_MODE_INPUT(GPIOD_PIN2) | \
+ PIN_MODE_INPUT(GPIOD_PIN3) | \
+ PIN_MODE_INPUT(GPIOD_PIN4) | \
+ PIN_MODE_INPUT(GPIOD_PIN5) | \
+ PIN_MODE_INPUT(GPIOD_PIN6) | \
+ PIN_MODE_INPUT(GPIOD_PIN7) | \
+ PIN_MODE_INPUT(GPIOD_PIN8) | \
+ PIN_MODE_INPUT(GPIOD_PIN9) | \
+ PIN_MODE_INPUT(GPIOD_PIN10) | \
+ PIN_MODE_INPUT(GPIOD_PIN11) | \
+ PIN_MODE_INPUT(GPIOD_PIN12) | \
+ PIN_MODE_INPUT(GPIOD_PIN13) | \
+ PIN_MODE_INPUT(GPIOD_PIN14) | \
+ PIN_MODE_INPUT(GPIOD_PIN15))
+#define VAL_GPIOD_OTYPER (PIN_OTYPE_PUSHPULL(GPIOD_PIN0) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN1) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN2) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN3) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN4) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN5) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN6) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN7) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN8) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN9) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN10) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN11) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN12) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN13) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN14) | \
+ PIN_OTYPE_PUSHPULL(GPIOD_PIN15))
+#define VAL_GPIOD_OSPEEDR (PIN_OSPEED_HIGH(GPIOD_PIN0) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN1) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN2) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN3) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN4) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN5) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN6) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN7) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN8) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN9) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN10) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN11) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN12) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN13) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN14) | \
+ PIN_OSPEED_HIGH(GPIOD_PIN15))
+#define VAL_GPIOD_PUPDR (PIN_PUPDR_PULLUP(GPIOD_PIN0) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN1) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN2) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN3) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN4) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN5) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN6) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN7) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN8) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN9) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN10) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN11) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN12) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN13) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN14) | \
+ PIN_PUPDR_PULLUP(GPIOD_PIN15))
+#define VAL_GPIOD_ODR (PIN_ODR_HIGH(GPIOD_PIN0) | \
+ PIN_ODR_HIGH(GPIOD_PIN1) | \
+ PIN_ODR_HIGH(GPIOD_PIN2) | \
+ PIN_ODR_HIGH(GPIOD_PIN3) | \
+ PIN_ODR_HIGH(GPIOD_PIN4) | \
+ PIN_ODR_HIGH(GPIOD_PIN5) | \
+ PIN_ODR_HIGH(GPIOD_PIN6) | \
+ PIN_ODR_HIGH(GPIOD_PIN7) | \
+ PIN_ODR_HIGH(GPIOD_PIN8) | \
+ PIN_ODR_HIGH(GPIOD_PIN9) | \
+ PIN_ODR_HIGH(GPIOD_PIN10) | \
+ PIN_ODR_HIGH(GPIOD_PIN11) | \
+ PIN_ODR_HIGH(GPIOD_PIN12) | \
+ PIN_ODR_HIGH(GPIOD_PIN13) | \
+ PIN_ODR_HIGH(GPIOD_PIN14) | \
+ PIN_ODR_HIGH(GPIOD_PIN15))
+#define VAL_GPIOD_AFRL (PIN_AFIO_AF(GPIOD_PIN0, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN1, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN2, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN3, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN4, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN5, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN6, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN7, 0U))
+#define VAL_GPIOD_AFRH (PIN_AFIO_AF(GPIOD_PIN8, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN9, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN10, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN11, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN12, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN13, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN14, 0U) | \
+ PIN_AFIO_AF(GPIOD_PIN15, 0U))
+
+/*
+ * GPIOE setup:
+ *
+ * PE0 - PIN0 (input pullup).
+ * PE1 - PIN1 (input pullup).
+ * PE2 - PIN2 (input pullup).
+ * PE3 - PIN3 (input pullup).
+ * PE4 - PIN4 (input pullup).
+ * PE5 - PIN5 (input pullup).
+ * PE6 - PIN6 (input pullup).
+ * PE7 - PIN7 (input pullup).
+ * PE8 - PIN8 (input pullup).
+ * PE9 - PIN9 (input pullup).
+ * PE10 - PIN10 (input pullup).
+ * PE11 - PIN11 (input pullup).
+ * PE12 - PIN12 (input pullup).
+ * PE13 - PIN13 (input pullup).
+ * PE14 - PIN14 (input pullup).
+ * PE15 - PIN15 (input pullup).
+ */
+#define VAL_GPIOE_MODER (PIN_MODE_INPUT(GPIOE_PIN0) | \
+ PIN_MODE_INPUT(GPIOE_PIN1) | \
+ PIN_MODE_INPUT(GPIOE_PIN2) | \
+ PIN_MODE_INPUT(GPIOE_PIN3) | \
+ PIN_MODE_INPUT(GPIOE_PIN4) | \
+ PIN_MODE_INPUT(GPIOE_PIN5) | \
+ PIN_MODE_INPUT(GPIOE_PIN6) | \
+ PIN_MODE_INPUT(GPIOE_PIN7) | \
+ PIN_MODE_INPUT(GPIOE_PIN8) | \
+ PIN_MODE_INPUT(GPIOE_PIN9) | \
+ PIN_MODE_INPUT(GPIOE_PIN10) | \
+ PIN_MODE_INPUT(GPIOE_PIN11) | \
+ PIN_MODE_INPUT(GPIOE_PIN12) | \
+ PIN_MODE_INPUT(GPIOE_PIN13) | \
+ PIN_MODE_INPUT(GPIOE_PIN14) | \
+ PIN_MODE_INPUT(GPIOE_PIN15))
+#define VAL_GPIOE_OTYPER (PIN_OTYPE_PUSHPULL(GPIOE_PIN0) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN1) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN2) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN3) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN4) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN5) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN6) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN7) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN8) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN9) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN10) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN11) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN12) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN13) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN14) | \
+ PIN_OTYPE_PUSHPULL(GPIOE_PIN15))
+#define VAL_GPIOE_OSPEEDR (PIN_OSPEED_HIGH(GPIOE_PIN0) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN1) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN2) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN3) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN4) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN5) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN6) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN7) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN8) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN9) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN10) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN11) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN12) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN13) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN14) | \
+ PIN_OSPEED_HIGH(GPIOE_PIN15))
+#define VAL_GPIOE_PUPDR (PIN_PUPDR_PULLUP(GPIOE_PIN0) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN1) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN2) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN3) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN4) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN5) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN6) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN7) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN8) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN9) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN10) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN11) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN12) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN13) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN14) | \
+ PIN_PUPDR_PULLUP(GPIOE_PIN15))
+#define VAL_GPIOE_ODR (PIN_ODR_HIGH(GPIOE_PIN0) | \
+ PIN_ODR_HIGH(GPIOE_PIN1) | \
+ PIN_ODR_HIGH(GPIOE_PIN2) | \
+ PIN_ODR_HIGH(GPIOE_PIN3) | \
+ PIN_ODR_HIGH(GPIOE_PIN4) | \
+ PIN_ODR_HIGH(GPIOE_PIN5) | \
+ PIN_ODR_HIGH(GPIOE_PIN6) | \
+ PIN_ODR_HIGH(GPIOE_PIN7) | \
+ PIN_ODR_HIGH(GPIOE_PIN8) | \
+ PIN_ODR_HIGH(GPIOE_PIN9) | \
+ PIN_ODR_HIGH(GPIOE_PIN10) | \
+ PIN_ODR_HIGH(GPIOE_PIN11) | \
+ PIN_ODR_HIGH(GPIOE_PIN12) | \
+ PIN_ODR_HIGH(GPIOE_PIN13) | \
+ PIN_ODR_HIGH(GPIOE_PIN14) | \
+ PIN_ODR_HIGH(GPIOE_PIN15))
+#define VAL_GPIOE_AFRL (PIN_AFIO_AF(GPIOE_PIN0, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN1, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN2, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN3, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN4, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN5, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN6, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN7, 0U))
+#define VAL_GPIOE_AFRH (PIN_AFIO_AF(GPIOE_PIN8, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN9, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN10, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN11, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN12, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN13, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN14, 0U) | \
+ PIN_AFIO_AF(GPIOE_PIN15, 0U))
+
+/*
+ * GPIOF setup:
+ *
+ * PF0 - OSC_IN (input floating).
+ * PF1 - OSC_OUT (input floating).
+ * PF2 - PIN2 (input pullup).
+ * PF3 - PIN3 (input pullup).
+ * PF4 - PIN4 (input pullup).
+ * PF5 - PIN5 (input pullup).
+ * PF6 - PIN6 (input pullup).
+ * PF7 - PIN7 (input pullup).
+ * PF8 - PIN8 (input pullup).
+ * PF9 - PIN9 (input pullup).
+ * PF10 - PIN10 (input pullup).
+ * PF11 - PIN11 (input pullup).
+ * PF12 - PIN12 (input pullup).
+ * PF13 - PIN13 (input pullup).
+ * PF14 - PIN14 (input pullup).
+ * PF15 - PIN15 (input pullup).
+ */
+#define VAL_GPIOF_MODER (PIN_MODE_INPUT(GPIOF_OSC_IN) | \
+ PIN_MODE_INPUT(GPIOF_OSC_OUT) | \
+ PIN_MODE_INPUT(GPIOF_PIN2) | \
+ PIN_MODE_INPUT(GPIOF_PIN3) | \
+ PIN_MODE_INPUT(GPIOF_PIN4) | \
+ PIN_MODE_INPUT(GPIOF_PIN5) | \
+ PIN_MODE_INPUT(GPIOF_PIN6) | \
+ PIN_MODE_INPUT(GPIOF_PIN7) | \
+ PIN_MODE_INPUT(GPIOF_PIN8) | \
+ PIN_MODE_INPUT(GPIOF_PIN9) | \
+ PIN_MODE_INPUT(GPIOF_PIN10) | \
+ PIN_MODE_INPUT(GPIOF_PIN11) | \
+ PIN_MODE_INPUT(GPIOF_PIN12) | \
+ PIN_MODE_INPUT(GPIOF_PIN13) | \
+ PIN_MODE_INPUT(GPIOF_PIN14) | \
+ PIN_MODE_INPUT(GPIOF_PIN15))
+#define VAL_GPIOF_OTYPER (PIN_OTYPE_PUSHPULL(GPIOF_OSC_IN) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_OSC_OUT) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN2) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN3) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN4) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN5) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN6) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN7) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN8) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN9) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN10) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN11) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN12) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN13) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN14) | \
+ PIN_OTYPE_PUSHPULL(GPIOF_PIN15))
+#define VAL_GPIOF_OSPEEDR (PIN_OSPEED_HIGH(GPIOF_OSC_IN) | \
+ PIN_OSPEED_HIGH(GPIOF_OSC_OUT) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN2) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN3) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN4) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN5) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN6) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN7) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN8) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN9) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN10) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN11) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN12) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN13) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN14) | \
+ PIN_OSPEED_HIGH(GPIOF_PIN15))
+#define VAL_GPIOF_PUPDR (PIN_PUPDR_FLOATING(GPIOF_OSC_IN) | \
+ PIN_PUPDR_FLOATING(GPIOF_OSC_OUT) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN2) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN3) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN4) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN5) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN6) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN7) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN8) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN9) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN10) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN11) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN12) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN13) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN14) | \
+ PIN_PUPDR_PULLUP(GPIOF_PIN15))
+#define VAL_GPIOF_ODR (PIN_ODR_HIGH(GPIOF_OSC_IN) | \
+ PIN_ODR_HIGH(GPIOF_OSC_OUT) | \
+ PIN_ODR_HIGH(GPIOF_PIN2) | \
+ PIN_ODR_HIGH(GPIOF_PIN3) | \
+ PIN_ODR_HIGH(GPIOF_PIN4) | \
+ PIN_ODR_HIGH(GPIOF_PIN5) | \
+ PIN_ODR_HIGH(GPIOF_PIN6) | \
+ PIN_ODR_HIGH(GPIOF_PIN7) | \
+ PIN_ODR_HIGH(GPIOF_PIN8) | \
+ PIN_ODR_HIGH(GPIOF_PIN9) | \
+ PIN_ODR_HIGH(GPIOF_PIN10) | \
+ PIN_ODR_HIGH(GPIOF_PIN11) | \
+ PIN_ODR_HIGH(GPIOF_PIN12) | \
+ PIN_ODR_HIGH(GPIOF_PIN13) | \
+ PIN_ODR_HIGH(GPIOF_PIN14) | \
+ PIN_ODR_HIGH(GPIOF_PIN15))
+#define VAL_GPIOF_AFRL (PIN_AFIO_AF(GPIOF_OSC_IN, 0U) | \
+ PIN_AFIO_AF(GPIOF_OSC_OUT, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN2, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN3, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN4, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN5, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN6, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN7, 0U))
+#define VAL_GPIOF_AFRH (PIN_AFIO_AF(GPIOF_PIN8, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN9, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN10, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN11, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN12, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN13, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN14, 0U) | \
+ PIN_AFIO_AF(GPIOF_PIN15, 0U))
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if !defined(_FROM_ASM_)
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void boardInit(void);
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FROM_ASM_ */
+
+#endif /* BOARD_H */
diff --git a/os/hal/boards/GENERIC_SK32_F077/board.mk b/os/hal/boards/GENERIC_SK32_F077/board.mk
new file mode 100644
index 0000000000..7332a0beff
--- /dev/null
+++ b/os/hal/boards/GENERIC_SK32_F077/board.mk
@@ -0,0 +1,9 @@
+# List of all the board related files.
+BOARDSRC = $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077/board.c
+
+# Required include directories
+BOARDINC = $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Shared variables
+ALLCSRC += $(BOARDSRC)
+ALLINC += $(BOARDINC)
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_adc_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_adc_lld.c
new file mode 100644
index 0000000000..068289677a
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_adc_lld.c
@@ -0,0 +1,376 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_adc_lld.c
+ * @brief SK32F0xx ADC subsystem low level driver source.
+ *
+ * @addtogroup ADC
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_ADC || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/**
+ * @brief Write-one-to-clear of the whole ADC ISR register.
+ */
+#define adc_lld_clear_isr(adcp) ((adcp)->adc->ISR = (adcp)->adc->ISR)
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief ADC1 driver identifier.
+ * @note The ADC1 interrupt vector is shared with the two comparators
+ * (COMP1/COMP2), the comparators are not used by this driver.
+ */
+#if SK32_ADC_USE_ADC1 || defined(__DOXYGEN__)
+ADCDriver ADCD1;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Stops an ongoing conversion, if any, and disables the ADC.
+ * @details This driver samples the data register in the interrupt handler,
+ * there is no DMA channel to stop: the conversion is aborted by
+ * writing ADSTP and the ADC is then fully disabled with ADDIS.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+static void adc_lld_stop_adc(ADCDriver *adcp) {
+
+ /* Disabling all the ADC interrupts first so no new event can be raised
+ while the peripheral is being stopped.*/
+ adcp->adc->IER = 0U;
+
+ /* Aborting a possible ongoing conversion. ADSTP is only accepted when a
+ conversion is in progress (ADSTART set) and is cleared by the hardware
+ when the conversion is effectively stopped.*/
+ if ((adcp->adc->CR & ADC_CR_ADSTART) != 0U) {
+ adcp->adc->CR |= ADC_CR_ADSTP;
+ while ((adcp->adc->CR & ADC_CR_ADSTP) != 0U)
+ ;
+ }
+
+ /* Disabling the ADC.*/
+ adcp->adc->CR |= ADC_CR_ADDIS;
+ while ((adcp->adc->CR & ADC_CR_ADEN) != 0U) {
+ /* Waiting for ADC to be disabled.*/
+ }
+
+ adc_lld_clear_isr(adcp);
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if SK32_ADC_USE_ADC1 || defined(__DOXYGEN__)
+#if !defined(SK32_ADC1_SUPPRESS_ISR)
+#if !defined(SK32_ADC_COMP_HANDLER)
+#error "SK32_ADC_COMP_HANDLER not defined"
+#endif
+/**
+ * @brief ADC interrupt handler.
+ * @note The vector is shared with the comparators, only the ADC interrupt
+ * and status register is serviced here.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_ADC_COMP_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ adc_lld_serve_interrupt(&ADCD1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* !defined(SK32_ADC1_SUPPRESS_ISR) */
+#endif /* SK32_ADC_USE_ADC1 */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level ADC driver initialization.
+ *
+ * @notapi
+ */
+void adc_lld_init(void) {
+
+#if SK32_ADC_USE_ADC1
+ /* Driver initialization.*/
+ adcObjectInit(&ADCD1);
+ ADCD1.adc = ADC1;
+ ADCD1.isr_count = 0U;
+ ADCD1.isr_total = 0U;
+
+ /* The vector is initialized on driver initialization and never
+ disabled. A Cortex-M0 device has no way to read back the priority of
+ an enabled vector, so this must be the only place setting it.*/
+ nvicEnableVector(SK32_ADC_COMP_NUMBER, SK32_ADC_ADC1_IRQ_PRIORITY);
+#endif
+}
+
+/**
+ * @brief Configures and activates the ADC peripheral.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adc_lld_start(ADCDriver *adcp) {
+
+ /* If in stopped state then enables the ADC and configures its clock.*/
+ if (adcp->state == ADC_STOP) {
+ rccResetADC1();
+ rccEnableADC1(true);
+
+ /* The SK32F0xx has no ADC prescaler bits in the CCR register (only the
+ VREFINT/temperature sensor enables), the ADC kernel clock is selected
+ by the CFGR2 CKMODE bits. The internal source enables in CCR are
+ managed by the application through adcSK32SetCCR()/adcSK32EnableVREF()
+ and friends, so they are left untouched here.*/
+ adcp->adc->CFGR2 = SK32_ADC_ADC1_CFGR2;
+ }
+}
+
+/**
+ * @brief Deactivates the ADC peripheral.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adc_lld_stop(ADCDriver *adcp) {
+
+ /* If in ready state then disables the ADC peripheral and clock.*/
+ if (adcp->state == ADC_READY) {
+ adc_lld_stop_adc(adcp);
+ rccDisableADC1();
+ }
+}
+
+/**
+ * @brief Starts an ADC conversion.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adc_lld_start_conversion(ADCDriver *adcp) {
+ uint32_t ier;
+ const ADCConversionGroup *grpp = adcp->grpp;
+
+ /* Write back ISR bits to clear the register.*/
+ adc_lld_clear_isr(adcp);
+
+ /* Apply the ADC configuration. CFGR1 must be written while the ADC is
+ disabled: modifying the RES bits with ADEN set would be ignored (see
+ the device errata).*/
+ adcp->adc->CFGR1 = grpp->cfgr1;
+ adcp->adc->CHSELR = grpp->chselr;
+ adcp->adc->SMPR = grpp->smpr;
+
+ /* Enable the ADC interrupts. EOCIE is always required because every
+ sample is read from the DR register in the interrupt handler. The
+ overrun and watchdog interrupts (and the TR register) are only enabled
+ when an error callback is provided.*/
+ ier = ADC_IER_EOCIE;
+ if (grpp->error_cb != NULL) {
+ adcp->adc->TR = grpp->tr;
+ ier |= ADC_IER_OVRIE | ADC_IER_AWDIE;
+ }
+ adcp->adc->IER = ier;
+
+ /* Sample counter initialization.*/
+ adcp->isr_count = 0U;
+ adcp->isr_total = (uint32_t)grpp->num_channels * (uint32_t)adcp->depth;
+
+ /* Enable the ADC and wait for it to become ready.*/
+ adcp->adc->CR |= ADC_CR_ADEN;
+ while ((adcp->adc->ISR & ADC_ISR_ADRDY) == 0U) {
+ /* Waiting for the ADC to be ready.*/
+ }
+
+ /* ADC conversion start.*/
+ adcp->adc->CR |= ADC_CR_ADSTART;
+}
+
+/**
+ * @brief Stops an ongoing conversion.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adc_lld_stop_conversion(ADCDriver *adcp) {
+
+ adc_lld_stop_adc(adcp);
+}
+
+/**
+ * @brief ISR code.
+ * @details On each EOC event the converted sample is read from the DR
+ * register into the samples buffer; when the whole buffer has been
+ * filled the standard full-buffer ISR code is invoked. The
+ * hardware end of sequence flag is not used: with N channels in
+ * the group and a buffer depth D the driver counts exactly
+ * N * D end-of-conversion events.
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adc_lld_serve_interrupt(ADCDriver *adcp) {
+ uint32_t isr;
+
+ isr = adcp->adc->ISR;
+ adc_lld_clear_isr(adcp);
+
+ /* It could be a spurious interrupt caused by events raised after the
+ conversion was stopped, just ignore it in this case.*/
+ if (adcp->grpp == NULL) {
+ return;
+ }
+
+ if ((isr & (ADC_ISR_OVR | ADC_ISR_AWD)) != 0U) {
+ /* Overflow or analog watchdog condition.*/
+ adcerror_t emask = 0U;
+
+ if ((isr & ADC_ISR_OVR) != 0U) {
+ /* ADC overflow condition, this could happen only if the interrupt
+ handler is unable to read the DR register fast enough.*/
+ emask |= ADC_ERR_OVERFLOW;
+ }
+ if ((isr & ADC_ISR_AWD) != 0U) {
+ /* Analog watchdog error.*/
+ emask |= ADC_ERR_AWD1;
+ }
+ _adc_isr_error_code(adcp, emask);
+ return;
+ }
+
+ if ((isr & ADC_ISR_EOC) != 0U) {
+ /* Store the sample and advance the counter. Extra samples (possible
+ only in circular mode right after a buffer wrap) are discarded.*/
+ if (adcp->isr_count < adcp->isr_total) {
+ adcp->samples[adcp->isr_count] = (adcsample_t)(adcp->adc->DR & 0xFFFFU);
+ }
+ else {
+ (void)(adcp->adc->DR & 0xFFFFU);
+ }
+ adcp->isr_count++;
+
+ if (adcp->isr_count >= adcp->isr_total) {
+ /* The whole buffer has been filled. In circular mode the counter is
+ reset before the full-buffer code so the next buffer cycle keeps
+ overwriting the samples buffer from the start.*/
+ adcp->isr_count = 0U;
+ _adc_isr_full_code(adcp);
+ }
+ }
+}
+
+/**
+ * @brief Enables the VREFEN bit.
+ * @details The VREFEN bit is required in order to sample the VREFINT
+ * internal reference voltage (channel 17).
+ * @note This is a SK32-only functionality.
+ * @note This function is meant to be called after @p adcStart().
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adcSK32EnableVREF(ADCDriver *adcp) {
+
+ (void)adcp;
+
+ ADC->CCR |= ADC_CCR_VREFEN;
+}
+
+/**
+ * @brief Disables the VREFEN bit.
+ * @note This is a SK32-only functionality.
+ * @note This function is meant to be called after @p adcStart().
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adcSK32DisableVREF(ADCDriver *adcp) {
+
+ (void)adcp;
+
+ ADC->CCR &= ~ADC_CCR_VREFEN;
+}
+
+/**
+ * @brief Enables the TSEN bit.
+ * @details The TSEN bit is required in order to sample the internal
+ * temperature sensor (channel 16).
+ * @note This is a SK32-only functionality.
+ * @note This function is meant to be called after @p adcStart().
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adcSK32EnableTS(ADCDriver *adcp) {
+
+ (void)adcp;
+
+ ADC->CCR |= ADC_CCR_TSEN;
+}
+
+/**
+ * @brief Disables the TSEN bit.
+ * @note This is a SK32-only functionality.
+ * @note This function is meant to be called after @p adcStart().
+ *
+ * @param[in] adcp pointer to the @p ADCDriver object
+ *
+ * @notapi
+ */
+void adcSK32DisableTS(ADCDriver *adcp) {
+
+ (void)adcp;
+
+ ADC->CCR &= ~ADC_CCR_TSEN;
+}
+
+#endif /* HAL_USE_ADC */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_adc_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_adc_lld.h
new file mode 100644
index 0000000000..ea2af6d114
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_adc_lld.h
@@ -0,0 +1,272 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_adc_lld.h
+ * @brief SK32F0xx ADC subsystem low level driver header.
+ * @details The SK32F0xx embeds a single ADC1 unit with the classic STM32F0
+ * register set (ISR/IER/CR/CFGR1/CFGR2/SMPR/TR/CHSELR/DR plus the
+ * shared ADC_Common_TypeDef CCR register and the VREFSR register).
+ * Unlike the shared STM32 ADCv1 LLD this driver does not use a DMA
+ * stream: every EOC event is serviced in the ADC interrupt handler
+ * and the converted sample is read from the DR register, so no
+ * STM32 DMA support is required. This driver is written directly
+ * against the vendor CMSIS header (@p sk32f0xx.h).
+ *
+ * @addtogroup ADC
+ * @{
+ */
+
+#ifndef HAL_ADC_LLD_H
+#define HAL_ADC_LLD_H
+
+#if HAL_USE_ADC || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @name Possible ADC errors mask bits
+ * @{
+ */
+#define ADC_ERR_DMAFAILURE 1U /**< DMA operations failure. */
+#define ADC_ERR_OVERFLOW 2U /**< ADC overflow condition. */
+#define ADC_ERR_AWD1 4U /**< Watchdog triggered. */
+/** @} */
+
+/**
+ * @name Sampling rates
+ * @note On the SK32F0xx a single SMPR register field (SMP[2:0]) applies
+ * to every enabled channel, unlike the per-channel SMPRx registers
+ * of the bigger STM32 families.
+ * @{
+ */
+#define ADC_SMPR_SMP_1P5 0U /**< @brief 14 cycles conversion time. */
+#define ADC_SMPR_SMP_7P5 1U /**< @brief 21 cycles conversion time. */
+#define ADC_SMPR_SMP_13P5 2U /**< @brief 28 cycles conversion time. */
+#define ADC_SMPR_SMP_28P5 3U /**< @brief 41 cycles conversion time. */
+#define ADC_SMPR_SMP_41P5 4U /**< @brief 54 cycles conversion time. */
+#define ADC_SMPR_SMP_55P5 5U /**< @brief 68 cycles conversion time. */
+#define ADC_SMPR_SMP_71P5 6U /**< @brief 84 cycles conversion time. */
+#define ADC_SMPR_SMP_239P5 7U /**< @brief 252 cycles conversion time. */
+/** @} */
+
+/**
+ * @name CFGR1 register configuration helpers
+ * @note The vendor CMSIS header only spells the raw RES_0/RES_1 bits,
+ * the resolution level helpers used in the conversion groups are
+ * defined here.
+ * @{
+ */
+#define ADC_CFGR1_RES_12BIT (0U << 3U)
+#define ADC_CFGR1_RES_10BIT (1U << 3U)
+#define ADC_CFGR1_RES_8BIT (2U << 3U)
+#define ADC_CFGR1_RES_6BIT (3U << 3U)
+
+#define ADC_CFGR1_EXTSEL_MASK (15U << 6U)
+#define ADC_CFGR1_EXTSEL_SRC(n) ((n) << 6U)
+
+#define ADC_CFGR1_EXTEN_MASK (3U << 10U)
+#define ADC_CFGR1_EXTEN_DISABLED (0U << 10U)
+#define ADC_CFGR1_EXTEN_RISING (1U << 10U)
+#define ADC_CFGR1_EXTEN_FALLING (2U << 10U)
+#define ADC_CFGR1_EXTEN_BOTH (3U << 10U)
+/** @} */
+
+/**
+ * @name CFGR2 register configuration helpers
+ * @note The SK32F0xx has no ADC prescaler in the CCR register: the ADC
+ * kernel clock is derived from the APB (PCLK) clock through the
+ * CFGR2 CKMODE[1:0] bits (the CMSIS comments name the single bits
+ * "PCLK div2" for bit 30 and "PCLK div4" for bit 31).
+ * @{
+ */
+#define ADC_CFGR2_CKMODE_MASK (3U << 30U)
+#define ADC_CFGR2_CKMODE_ADCCLK (0U << 30U)
+#define ADC_CFGR2_CKMODE_PCLK_DIV2 (1U << 30U)
+#define ADC_CFGR2_CKMODE_PCLK_DIV4 (2U << 30U)
+/** @} */
+
+/**
+ * @name Threshold register initializer
+ * @{
+ */
+#define ADC_TR(low, high) (((uint32_t)(high) << 16U) | \
+ (uint32_t)(low))
+/** @} */
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief ADC1 driver enable switch.
+ * @details If set to @p TRUE the support for ADC1 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SK32_ADC_USE_ADC1) || defined(__DOXYGEN__)
+#define SK32_ADC_USE_ADC1 FALSE
+#endif
+
+/**
+ * @brief ADC1 CFGR2 initialization.
+ * @note The ADC kernel clock is the APB clock divided as specified by
+ * the CKMODE bits. The default (PCLK/4 -> 18 MHz with the 72 MHz
+ * PCLK used by this platform) keeps the ADC inside its maximum
+ * conversion clock; it can be overridden in mcuconf.h.
+ */
+#if !defined(SK32_ADC_ADC1_CFGR2) || defined(__DOXYGEN__)
+#define SK32_ADC_ADC1_CFGR2 ADC_CFGR2_CKMODE_PCLK_DIV4
+#endif
+
+/**
+ * @brief ADC1 interrupt priority level setting.
+ * @note ARMv6-M (Cortex-M0) implements only 2 priority bits, the valid
+ * priority range is 0 (highest) .. 3 (lowest).
+ */
+#if !defined(SK32_ADC_ADC1_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_ADC_ADC1_IRQ_PRIORITY 2
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if !defined(SK32_HAS_ADC1)
+#define SK32_HAS_ADC1 FALSE
+#endif
+
+#if SK32_ADC_USE_ADC1 && !SK32_HAS_ADC1
+#error "ADC1 not present in the selected device"
+#endif
+
+#if !SK32_ADC_USE_ADC1
+#error "ADC driver activated but no ADC peripheral assigned"
+#endif
+
+/* ADC1 IRQ priority tests.*/
+#if SK32_ADC_USE_ADC1 && !defined(SK32_ADC1_SUPPRESS_ISR) && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(SK32_ADC_ADC1_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to ADC1"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief ADC sample data type.
+ */
+typedef uint16_t adcsample_t;
+
+/**
+ * @brief Channels number in a conversion group.
+ */
+typedef uint16_t adc_channels_num_t;
+
+/**
+ * @brief Type of an ADC error mask.
+ */
+typedef uint32_t adcerror_t;
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level fields of the ADC driver structure.
+ * @note The two @p isr_* fields implement the software sample counter
+ * used by the interrupt driven (DMA-less) conversion path.
+ */
+#define adc_lld_driver_fields \
+ /* Pointer to the ADCx registers block.*/ \
+ ADC_TypeDef *adc; \
+ /* Samples collected so far in the current conversion.*/ \
+ uint32_t isr_count; \
+ /* Total samples to collect (num_channels * depth).*/ \
+ uint32_t isr_total;
+
+/**
+ * @brief Low level fields of the ADC driver configuration structure.
+ */
+#define adc_lld_config_fields \
+ /* Dummy configuration, it is not needed.*/ \
+ uint32_t dummy;
+
+/**
+ * @brief Low level fields of the ADC conversion group configuration
+ * structure.
+ */
+#define adc_lld_configuration_group_fields \
+ /* ADC CFGR1 register initialization data. \
+ NOTE: The bits @p ADC_CFGR1_CONT or @p ADC_CFGR1_DISCEN must be \
+ specified in continuous mode or if the buffer depth is \
+ greater than one.*/ \
+ uint32_t cfgr1; \
+ /* ADC TR register initialization data.*/ \
+ uint32_t tr; \
+ /* ADC SMPR register initialization data.*/ \
+ uint32_t smpr; \
+ /* ADC CHSELR register initialization data. \
+ NOTE: The number of bits at logic level one in this register must \
+ be equal to the number in the @p num_channels field.*/ \
+ uint32_t chselr;
+
+/**
+ * @brief Changes the value of the ADC CCR register.
+ * @details Use this function in order to enable or disable the internal
+ * analog sources (VREFINT, temperature sensor). See the
+ * documentation in the device reference manual.
+ */
+#define adcSK32SetCCR(ccr) (ADC->CCR = (ccr))
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if SK32_ADC_USE_ADC1 && !defined(__DOXYGEN__)
+extern ADCDriver ADCD1;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void adc_lld_init(void);
+ void adc_lld_start(ADCDriver *adcp);
+ void adc_lld_stop(ADCDriver *adcp);
+ void adc_lld_start_conversion(ADCDriver *adcp);
+ void adc_lld_stop_conversion(ADCDriver *adcp);
+ void adc_lld_serve_interrupt(ADCDriver *adcp);
+ void adcSK32EnableVREF(ADCDriver *adcp);
+ void adcSK32DisableVREF(ADCDriver *adcp);
+ void adcSK32EnableTS(ADCDriver *adcp);
+ void adcSK32DisableTS(ADCDriver *adcp);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_ADC */
+
+#endif /* HAL_ADC_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_can_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_can_lld.h
new file mode 100644
index 0000000000..035490cf8e
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_can_lld.h
@@ -0,0 +1,38 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_can_lld.h
+ * @brief sk320077 CAN subsystem low level driver stub.
+ * @details The SK32F077 does not integrate a CAN controller. This header only
+ * exists to satisfy the unconditional include in the generic hal_can.h
+ * so that this platform never needs to reference the shared STM32
+ * CANv1 LLD header tree.
+ *
+ * @addtogroup SK32F0xx_CAN
+ * @{
+ */
+
+#ifndef HAL_CAN_LLD_H
+#define HAL_CAN_LLD_H
+
+#if HAL_USE_CAN || defined(__DOXYGEN__)
+
+#endif /* HAL_USE_CAN */
+
+#endif /* HAL_CAN_LLD_H */
+
+/** @} */
\ No newline at end of file
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_dac_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_dac_lld.h
new file mode 100644
index 0000000000..0bf3c777b1
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_dac_lld.h
@@ -0,0 +1,38 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_dac_lld.h
+ * @brief SK32F077 DAC subsystem low level driver stub.
+ * @details The SK32F077 does not integrate a DAC controller. This header only
+ * exists to satisfy the unconditional include in the generic hal_dac.h
+ * so that this platform never needs to reference the shared STM32
+ * DACv1 LLD header tree.
+ *
+ * @addtogroup SK32F0xx_DAC
+ * @{
+ */
+
+#ifndef HAL_DAC_LLD_H
+#define HAL_DAC_LLD_H
+
+#if HAL_USE_DAC || defined(__DOXYGEN__)
+
+#endif /* HAL_USE_DAC */
+
+#endif /* HAL_DAC_LLD_H */
+
+/** @} */
\ No newline at end of file
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_efl_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_efl_lld.c
new file mode 100644
index 0000000000..1f52923858
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_efl_lld.c
@@ -0,0 +1,477 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file hal_efl_lld.c
+ * @brief STM32F0xx Embedded Flash subsystem low level driver source.
+ *
+ * @addtogroup HAL_EFL
+ * @{
+ */
+
+#include
+
+#include "hal.h"
+
+#if (HAL_USE_EFL == TRUE) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+#define STM32_FLASH_LINE_MASK (STM32_FLASH_LINE_SIZE - 1U)
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief EFL1 driver identifier.
+ */
+EFlashDriver EFLD1;
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+static const flash_descriptor_t efl_lld_descriptor = {
+ .attributes = FLASH_ATTR_ERASED_IS_ONE |
+ FLASH_ATTR_MEMORY_MAPPED,
+ .page_size = STM32_FLASH_LINE_SIZE,
+ .sectors_count = STM32_FLASH_NUMBER_OF_BANKS *
+ STM32_FLASH_SECTORS_PER_BANK,
+ .sectors = NULL,
+ .sectors_size = STM32_FLASH_SECTOR_SIZE,
+ .address = (uint8_t *)FLASH_BASE,
+ .size = STM32_FLASH_NUMBER_OF_BANKS *
+ STM32_FLASH_SECTORS_PER_BANK *
+ STM32_FLASH_SECTOR_SIZE
+};
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+static inline void stm32_flash_lock(EFlashDriver *eflp) {
+
+ eflp->flash->CR |= FLASH_CR_LOCK;
+}
+
+static inline void stm32_flash_unlock(EFlashDriver *eflp) {
+
+ eflp->flash->KEYR |= FLASH_FKEY1;
+ eflp->flash->KEYR |= FLASH_FKEY2;
+}
+
+static inline void stm32_flash_enable_pgm(EFlashDriver *eflp) {
+
+ eflp->flash->CR |= FLASH_CR_PG;
+}
+
+static inline void stm32_flash_disable_pgm(EFlashDriver *eflp) {
+
+ eflp->flash->CR &= ~FLASH_CR_PG;
+}
+
+static inline void stm32_flash_clear_status(EFlashDriver *eflp) {
+
+ eflp->flash->SR = 0x0000FFFFU;
+}
+
+static inline void stm32_flash_wait_busy(EFlashDriver *eflp) {
+
+ /* Wait for busy bit clear.*/
+ while ((eflp->flash->SR & FLASH_SR_BSY) != 0U) {
+ }
+}
+
+static inline flash_error_t stm32_flash_check_errors(EFlashDriver *eflp) {
+ uint32_t sr = eflp->flash->SR;
+
+ /* Clearing error conditions.*/
+ eflp->flash->SR = sr & 0x0000FFFFU;
+
+ /* Decoding relevant errors.*/
+ if ((sr & FLASH_SR_WRPERR) != 0U) {
+ return FLASH_ERROR_HW_FAILURE;
+ }
+
+ if ((sr & FLASH_SR_PGERR) != 0U) {
+ return eflp->state == FLASH_PGM ? FLASH_ERROR_PROGRAM : FLASH_ERROR_ERASE;
+ }
+
+ return FLASH_NO_ERROR;
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level Embedded Flash driver initialization.
+ *
+ * @notapi
+ */
+void efl_lld_init(void) {
+
+ /* Driver initialization.*/
+ eflObjectInit(&EFLD1);
+ EFLD1.flash = FLASH;
+}
+
+/**
+ * @brief Configures and activates the Embedded Flash peripheral.
+ *
+ * @param[in] eflp pointer to a @p EFlashDriver structure
+ *
+ * @notapi
+ */
+void efl_lld_start(EFlashDriver *eflp) {
+ stm32_flash_unlock(eflp);
+ FLASH->CR = 0x00000000U;
+}
+
+/**
+ * @brief Deactivates the Embedded Flash peripheral.
+ *
+ * @param[in] eflp pointer to a @p EFlashDriver structure
+ *
+ * @notapi
+ */
+void efl_lld_stop(EFlashDriver *eflp) {
+
+ stm32_flash_lock(eflp);
+}
+
+/**
+ * @brief Gets the flash descriptor structure.
+ *
+ * @param[in] ip pointer to a @p EFlashDriver instance
+ * @return A flash device descriptor.
+ * @retval Pointer to single bank if DBM not enabled.
+ * @retval Pointer to bank1 if DBM enabled.
+ *
+ * @notapi
+ */
+const flash_descriptor_t *efl_lld_get_descriptor(void *instance) {
+
+ (void)instance;
+
+ return &efl_lld_descriptor;
+}
+
+/**
+ * @brief Read operation.
+ *
+ * @param[in] ip pointer to a @p EFlashDriver instance
+ * @param[in] offset offset within full flash address space
+ * @param[in] n number of bytes to be read
+ * @param[out] rp pointer to the data buffer
+ * @return An error code.
+ * @retval FLASH_NO_ERROR if there is no erase operation in progress.
+ * @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
+ * @retval FLASH_ERROR_READ if the read operation failed.
+ * @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
+ *
+ * @notapi
+ */
+flash_error_t efl_lld_read(void *instance, flash_offset_t offset,
+ size_t n, uint8_t *rp) {
+ EFlashDriver *devp = (EFlashDriver *)instance;
+ flash_error_t err = FLASH_NO_ERROR;
+
+ osalDbgCheck((instance != NULL) && (rp != NULL) && (n > 0U));
+ osalDbgCheck((size_t)offset + n <= (size_t)efl_lld_descriptor.size);
+ osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
+ "invalid state");
+
+ /* No reading while erasing.*/
+ if (devp->state == FLASH_ERASE) {
+ return FLASH_BUSY_ERASING;
+ }
+
+ /* FLASH_READ state while the operation is performed.*/
+ devp->state = FLASH_READ;
+
+ /* Clearing error status bits.*/
+ stm32_flash_clear_status(devp);
+
+ /* Actual read implementation.*/
+ memcpy((void *)rp, (const void *)efl_lld_descriptor.address + offset, n);
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ return err;
+}
+
+/**
+ * @brief Program operation.
+ * @note The device supports ECC, it is only possible to write erased
+ * pages once except when writing all zeroes.
+ *
+ * @param[in] ip pointer to a @p EFlashDriver instance
+ * @param[in] offset offset within full flash address space
+ * @param[in] n number of bytes to be programmed
+ * @param[in] pp pointer to the data buffer
+ * @return An error code.
+ * @retval FLASH_NO_ERROR if there is no erase operation in progress.
+ * @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
+ * @retval FLASH_ERROR_PROGRAM if the program operation failed.
+ * @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
+ *
+ * @notapi
+ */
+flash_error_t efl_lld_program(void *instance, flash_offset_t offset,
+ size_t n, const uint8_t *pp) {
+ EFlashDriver *devp = (EFlashDriver *)instance;
+ flash_error_t err = FLASH_NO_ERROR;
+
+ osalDbgCheck((instance != NULL) && (pp != NULL) && (n > 0U));
+ osalDbgCheck((size_t)offset + n <= (size_t)efl_lld_descriptor.size);
+ osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
+ "invalid state");
+
+ /* No programming while erasing.*/
+ if (devp->state == FLASH_ERASE) {
+ return FLASH_BUSY_ERASING;
+ }
+
+ /* FLASH_PGM state while the operation is performed.*/
+ devp->state = FLASH_PGM;
+
+ /* Clearing error status bits.*/
+ stm32_flash_clear_status(devp);
+
+ /* Enabling PGM mode in the controller.*/
+ stm32_flash_enable_pgm(devp);
+
+ /* Actual program implementation.*/
+ while (n > 0U) {
+ volatile uint16_t *address;
+
+ union {
+ uint16_t hw[STM32_FLASH_LINE_SIZE / sizeof (uint16_t)];
+ uint8_t b[STM32_FLASH_LINE_SIZE / sizeof (uint8_t)];
+ } line;
+
+ /* Unwritten bytes are initialized to all ones.*/
+ line.hw[0] = 0xFFFFU;
+
+ /* Programming address aligned to flash lines.*/
+ address = (volatile uint16_t *)(efl_lld_descriptor.address +
+ (offset & ~STM32_FLASH_LINE_MASK));
+
+ /* Copying data inside the prepared line.*/
+ do {
+ line.b[offset & STM32_FLASH_LINE_MASK] = *pp;
+ offset++;
+ n--;
+ pp++;
+ }
+ while ((n > 0U) & ((offset & STM32_FLASH_LINE_MASK) != 0U));
+
+ /* Programming line.*/
+ address[0] = line.hw[0];
+ stm32_flash_wait_busy(devp);
+
+ err = stm32_flash_check_errors(devp);
+ if (err != FLASH_NO_ERROR) {
+ break;
+ }
+ }
+
+ /* Disabling PGM mode in the controller.*/
+ stm32_flash_disable_pgm(devp);
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ return err;
+}
+
+/**
+ * @brief Starts a whole-device erase operation.
+ * @note This function only erases bank 2 if it is present. Bank 1 is not
+ * allowed since it is normally where the primary program is located.
+ * Pages on bank 1 can be individually erased.
+ *
+ * @param[in] ip pointer to a @p EFlashDriver instance
+ * @return An error code.
+ * @retval FLASH_NO_ERROR if there is no erase operation in progress.
+ * @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
+ * @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
+ *
+ * @notapi
+ */
+flash_error_t efl_lld_start_erase_all(void *instance) {
+ (void) instance;
+
+ return FLASH_ERROR_UNIMPLEMENTED;
+}
+
+/**
+ * @brief Starts an sector erase operation.
+ *
+ * @param[in] ip pointer to a @p EFlashDriver instance
+ * @param[in] sector sector to be erased
+ * this is an index within the total sectors
+ * in a flash bank
+ * @return An error code.
+ * @retval FLASH_NO_ERROR if there is no erase operation in progress.
+ * @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
+ * @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
+ *
+ * @notapi
+ */
+flash_error_t efl_lld_start_erase_sector(void *instance,
+ flash_sector_t sector) {
+ EFlashDriver *devp = (EFlashDriver *)instance;
+
+ osalDbgCheck(instance != NULL);
+ osalDbgCheck(sector < efl_lld_descriptor.sectors_count);
+ osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
+ "invalid state");
+
+ /* No erasing while erasing.*/
+ if (devp->state == FLASH_ERASE) {
+ return FLASH_BUSY_ERASING;
+ }
+
+ /* FLASH_PGM state while the operation is performed.*/
+ devp->state = FLASH_ERASE;
+
+ /* Clearing error status bits.*/
+ stm32_flash_clear_status(devp);
+
+ /* Enable page erase.*/
+ devp->flash->CR |= FLASH_CR_PER;
+
+ /* Set the page.*/
+ devp->flash->AR = (uint32_t)(efl_lld_descriptor.address +
+ flashGetSectorOffset(getBaseFlash(devp), sector));
+
+ /* Start the erase.*/
+ devp->flash->CR |= FLASH_CR_STRT;
+
+ return FLASH_NO_ERROR;
+}
+
+/**
+ * @brief Queries the driver for erase operation progress.
+ *
+ * @param[in] ip pointer to a @p EFlashDriver instance
+ * @param[out] msec recommended time, in milliseconds, that
+ * should be spent before calling this
+ * function again, can be @p NULL
+ * @return An error code.
+ * @retval FLASH_NO_ERROR if there is no erase operation in progress.
+ * @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
+ * @retval FLASH_ERROR_ERASE if the erase operation failed.
+ * @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
+ *
+ * @api
+ */
+flash_error_t efl_lld_query_erase(void *instance, uint32_t *msec) {
+ EFlashDriver *devp = (EFlashDriver *)instance;
+ flash_error_t err = FLASH_NO_ERROR;
+
+ /* If there is an erase in progress then the device must be checked.*/
+ if (devp->state == FLASH_ERASE) {
+
+ /* Checking for operation in progress.*/
+ if ((devp->flash->SR & FLASH_SR_BSY) == 0U) {
+
+ /* Disabling the various erase control bits.*/
+ devp->flash->CR &= ~(FLASH_CR_OPTER | FLASH_CR_OPTPG |
+ FLASH_CR_MER | FLASH_CR_PER);
+
+ /* Back to ready state.*/
+ devp->state = FLASH_READY;
+ }
+ else {
+ /* Recommended time before polling again. This is a simplified
+ implementation.*/
+ if (msec != NULL) {
+ *msec = (uint32_t)STM32_FLASH_WAIT_TIME_MS;
+ }
+
+ err = FLASH_BUSY_ERASING;
+ }
+ }
+
+ return err;
+}
+
+/**
+ * @brief Returns the erase state of a sector.
+ *
+ * @param[in] ip pointer to a @p EFlashDriver instance
+ * @param[in] sector sector to be verified
+ * @return An error code.
+ * @retval FLASH_NO_ERROR if the sector is erased.
+ * @retval FLASH_BUSY_ERASING if there is an erase operation in progress.
+ * @retval FLASH_ERROR_VERIFY if the verify operation failed.
+ * @retval FLASH_ERROR_HW_FAILURE if access to the memory failed.
+ *
+ * @notapi
+ */
+flash_error_t efl_lld_verify_erase(void *instance, flash_sector_t sector) {
+ EFlashDriver *devp = (EFlashDriver *)instance;
+ uint32_t *address;
+ flash_error_t err = FLASH_NO_ERROR;
+ unsigned i;
+
+ osalDbgCheck(instance != NULL);
+ osalDbgCheck(sector < efl_lld_descriptor.sectors_count);
+ osalDbgAssert((devp->state == FLASH_READY) || (devp->state == FLASH_ERASE),
+ "invalid state");
+
+ /* No verifying while erasing.*/
+ if (devp->state == FLASH_ERASE) {
+ return FLASH_BUSY_ERASING;
+ }
+
+ /* Address of the sector.*/
+ address = (uint32_t *)(efl_lld_descriptor.address +
+ flashGetSectorOffset(getBaseFlash(devp), sector));
+
+ /* FLASH_READ state while the operation is performed.*/
+ devp->state = FLASH_READ;
+
+ /* Scanning the sector space.*/
+ uint32_t sector_size = flashGetSectorSize(getBaseFlash(devp), sector);
+ for (i = 0U; i < sector_size / sizeof(uint32_t); i++) {
+ if (*address != 0xFFFFFFFFU) {
+ err = FLASH_ERROR_VERIFY;
+ break;
+ }
+ address++;
+ }
+
+ /* Ready state again.*/
+ devp->state = FLASH_READY;
+
+ return err;
+}
+
+#endif /* HAL_USE_EFL == TRUE */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_efl_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_efl_lld.h
new file mode 100644
index 0000000000..1a071db451
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_efl_lld.h
@@ -0,0 +1,120 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file hal_efl_lld.h
+ * @brief STM32F1xx Embedded Flash subsystem low level driver header.
+ *
+ * @addtogroup HAL_EFL
+ * @{
+ */
+
+#ifndef HAL_EFL_LLD_H
+#define HAL_EFL_LLD_H
+
+#if (HAL_USE_EFL == TRUE) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name STM32F1xx configuration options
+ * @{
+ */
+/**
+ * @brief Suggested wait time during erase operations polling.
+ */
+#if !defined(STM32_FLASH_WAIT_TIME_MS) || defined(__DOXYGEN__)
+#define STM32_FLASH_WAIT_TIME_MS 1
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if !defined(STM32_FLASH_SECTOR_SIZE)
+#error "STM32_FLASH_SECTOR_SIZE not defined in registry"
+#endif
+
+#if !defined(STM32_FLASH_NUMBER_OF_BANKS)
+#error "STM32_FLASH_NUMBER_OF_BANKS not defined in registry"
+#endif
+
+#if !defined(STM32_FLASH_SECTORS_PER_BANK)
+#error "STM32_FLASH_SECTORS_PER_BANK not defined in registry"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level fields of the embedded flash driver structure.
+ */
+#define efl_lld_driver_fields \
+ /* Flash registers.*/ \
+ FLASH_TypeDef *flash;
+
+/**
+ * @brief Low level fields of the embedded flash configuration structure.
+ */
+#define efl_lld_config_fields \
+ /* Dummy configuration, it is not needed.*/ \
+ uint32_t dummy;
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if !defined(__DOXYGEN__)
+extern EFlashDriver EFLD1;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void efl_lld_init(void);
+ void efl_lld_start(EFlashDriver *eflp);
+ void efl_lld_stop(EFlashDriver *eflp);
+ const flash_descriptor_t *efl_lld_get_descriptor(void *instance);
+ flash_error_t efl_lld_read(void *instance, flash_offset_t offset,
+ size_t n, uint8_t *rp);
+ flash_error_t efl_lld_program(void *instance, flash_offset_t offset,
+ size_t n, const uint8_t *pp);
+ flash_error_t efl_lld_start_erase_all(void *instance);
+ flash_error_t efl_lld_start_erase_sector(void *instance,
+ flash_sector_t sector);
+ flash_error_t efl_lld_query_erase(void *instance, uint32_t *wait_time);
+ flash_error_t efl_lld_verify_erase(void *instance, flash_sector_t sector);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_EFL == TRUE */
+
+#endif /* HAL_EFL_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_gpt_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_gpt_lld.c
new file mode 100644
index 0000000000..c356804750
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_gpt_lld.c
@@ -0,0 +1,361 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_gpt_lld.c
+ * @brief SK32F0xx GPT subsystem low level driver source.
+ *
+ * @addtogroup GPT
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_GPT || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief GPTD3 driver identifier.
+ * @note The driver GPTD3 allocates the general purpose timer TIM3
+ * (APB1 clock domain) when enabled.
+ */
+#if SK32_GPT_USE_TIM3 || defined(__DOXYGEN__)
+GPTDriver GPTD3;
+#endif
+
+/**
+ * @brief GPTD16 driver identifier.
+ * @note The driver GPTD16 allocates the general purpose timer TIM16
+ * (APB2 clock domain) when enabled.
+ */
+#if SK32_GPT_USE_TIM16 || defined(__DOXYGEN__)
+GPTDriver GPTD16;
+#endif
+
+/**
+ * @brief GPTD17 driver identifier.
+ * @note The driver GPTD17 allocates the general purpose timer TIM17
+ * (APB2 clock domain) when enabled.
+ */
+#if SK32_GPT_USE_TIM17 || defined(__DOXYGEN__)
+GPTDriver GPTD17;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if SK32_GPT_USE_TIM3 || defined(__DOXYGEN__)
+#if !defined(SK32_TIM3_SUPPRESS_ISR)
+#if !defined(SK32_TIM3_HANDLER)
+#error "SK32_TIM3_HANDLER not defined"
+#endif
+/**
+ * @brief TIM3 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_TIM3_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ gpt_lld_serve_interrupt(&GPTD3);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* !defined(SK32_TIM3_SUPPRESS_ISR) */
+#endif /* SK32_GPT_USE_TIM3 */
+
+#if SK32_GPT_USE_TIM16 || defined(__DOXYGEN__)
+#if !defined(SK32_TIM16_SUPPRESS_ISR)
+#if !defined(SK32_TIM16_HANDLER)
+#error "SK32_TIM16_HANDLER not defined"
+#endif
+/**
+ * @brief TIM16 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_TIM16_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ gpt_lld_serve_interrupt(&GPTD16);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* !defined(SK32_TIM16_SUPPRESS_ISR) */
+#endif /* SK32_GPT_USE_TIM16 */
+
+#if SK32_GPT_USE_TIM17 || defined(__DOXYGEN__)
+#if !defined(SK32_TIM17_SUPPRESS_ISR)
+#if !defined(SK32_TIM17_HANDLER)
+#error "SK32_TIM17_HANDLER not defined"
+#endif
+/**
+ * @brief TIM17 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_TIM17_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ gpt_lld_serve_interrupt(&GPTD17);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* !defined(SK32_TIM17_SUPPRESS_ISR) */
+#endif /* SK32_GPT_USE_TIM17 */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level GPT driver initialization.
+ *
+ * @notapi
+ */
+void gpt_lld_init(void) {
+
+#if SK32_GPT_USE_TIM3
+ /* Driver initialization.*/
+ GPTD3.tim = TIM3;
+ gptObjectInit(&GPTD3);
+#endif
+
+#if SK32_GPT_USE_TIM16
+ /* Driver initialization.*/
+ GPTD16.tim = TIM16;
+ gptObjectInit(&GPTD16);
+#endif
+
+#if SK32_GPT_USE_TIM17
+ /* Driver initialization.*/
+ GPTD17.tim = TIM17;
+ gptObjectInit(&GPTD17);
+#endif
+}
+
+/**
+ * @brief Configures and activates the GPT peripheral.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ *
+ * @notapi
+ */
+void gpt_lld_start(GPTDriver *gptp) {
+ uint16_t psc;
+
+ if (gptp->state == GPT_STOP) {
+ /* Clock activation.*/
+#if SK32_GPT_USE_TIM3
+ if (&GPTD3 == gptp) {
+ rccEnableTIM3(true);
+ rccResetTIM3();
+#if !defined(SK32_TIM3_SUPPRESS_ISR)
+ nvicEnableVector(SK32_TIM3_NUMBER, SK32_GPT_TIM3_IRQ_PRIORITY);
+#endif
+ gptp->clock = SK32_TIMCLK1;
+ }
+#endif
+
+#if SK32_GPT_USE_TIM16
+ if (&GPTD16 == gptp) {
+ rccEnableTIM16(true);
+ rccResetTIM16();
+#if !defined(SK32_TIM16_SUPPRESS_ISR)
+ nvicEnableVector(SK32_TIM16_NUMBER, SK32_GPT_TIM16_IRQ_PRIORITY);
+#endif
+ gptp->clock = SK32_TIMCLK2;
+ }
+#endif
+
+#if SK32_GPT_USE_TIM17
+ if (&GPTD17 == gptp) {
+ rccEnableTIM17(true);
+ rccResetTIM17();
+#if !defined(SK32_TIM17_SUPPRESS_ISR)
+ nvicEnableVector(SK32_TIM17_NUMBER, SK32_GPT_TIM17_IRQ_PRIORITY);
+#endif
+ gptp->clock = SK32_TIMCLK2;
+ }
+#endif
+ }
+
+ /* Prescaler value calculation.*/
+ psc = (uint16_t)((gptp->clock / gptp->config->frequency) - 1U);
+ osalDbgAssert(((uint32_t)(psc + 1U) * gptp->config->frequency) ==
+ gptp->clock, "invalid frequency");
+
+ /* Timer configuration.*/
+ gptp->tim->CR1 = 0U; /* Initially stopped. */
+ gptp->tim->CR2 = gptp->config->cr2;
+ gptp->tim->PSC = psc; /* Prescaler value. */
+ gptp->tim->SR = 0U; /* Clear pending IRQs. */
+ gptp->tim->DIER = gptp->config->dier & /* DMA-related DIER bits. */
+ ~SK32_TIM_DIER_IRQ_MASK;
+}
+
+/**
+ * @brief Deactivates the GPT peripheral.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ *
+ * @notapi
+ */
+void gpt_lld_stop(GPTDriver *gptp) {
+
+ if (gptp->state == GPT_READY) {
+ gptp->tim->CR1 = 0U; /* Timer disabled. */
+ gptp->tim->DIER = 0U; /* All IRQs disabled. */
+ gptp->tim->SR = 0U; /* Clear pending IRQs. */
+
+#if SK32_GPT_USE_TIM3
+ if (&GPTD3 == gptp) {
+#if !defined(SK32_TIM3_SUPPRESS_ISR)
+ nvicDisableVector(SK32_TIM3_NUMBER);
+#endif
+ rccDisableTIM3();
+ }
+#endif
+
+#if SK32_GPT_USE_TIM16
+ if (&GPTD16 == gptp) {
+#if !defined(SK32_TIM16_SUPPRESS_ISR)
+ nvicDisableVector(SK32_TIM16_NUMBER);
+#endif
+ rccDisableTIM16();
+ }
+#endif
+
+#if SK32_GPT_USE_TIM17
+ if (&GPTD17 == gptp) {
+#if !defined(SK32_TIM17_SUPPRESS_ISR)
+ nvicDisableVector(SK32_TIM17_NUMBER);
+#endif
+ rccDisableTIM17();
+ }
+#endif
+ }
+}
+
+/**
+ * @brief Starts the timer in continuous mode.
+ * @note Interval values 0 and 1 are invalid on this architecture.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ * @param[in] interval period in ticks
+ *
+ * @notapi
+ */
+void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t interval) {
+
+ osalDbgAssert(interval > (gptcnt_t)0, "invalid interval");
+
+ gptp->tim->ARR = (uint32_t)(interval - 1U); /* Time constant. */
+ gptp->tim->EGR = TIM_EGR_UG; /* Update event. */
+ gptp->tim->CNT = 0U; /* Reset counter. */
+
+ /* NOTE: After generating the UG event it takes several clock cycles before
+ SR bit 0 goes to 1. This is why the clearing of CNT has been inserted
+ before the clearing of SR, to give it some time.*/
+ gptp->tim->SR = 0U; /* Clear pending IRQs. */
+ if (NULL != gptp->config->callback)
+ gptp->tim->DIER |= TIM_DIER_UIE; /* Update Event IRQ enabled.*/
+ gptp->tim->CR1 = TIM_CR1_ARPE | TIM_CR1_URS | TIM_CR1_CEN;
+}
+
+/**
+ * @brief Stops the timer.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ *
+ * @notapi
+ */
+void gpt_lld_stop_timer(GPTDriver *gptp) {
+
+ gptp->tim->CR1 = 0U; /* Initially stopped. */
+ gptp->tim->SR = 0U; /* Clear pending IRQs. */
+
+ /* All interrupts disabled.*/
+ gptp->tim->DIER &= ~SK32_TIM_DIER_IRQ_MASK;
+}
+
+/**
+ * @brief Starts the timer in one shot mode and waits for completion.
+ * @details This function specifically polls the timer waiting for completion
+ * in order to not have extra delays caused by interrupt servicing,
+ * this function is only recommended for short delays.
+ * @note Interval values 0 and 1 are invalid on this architecture.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ * @param[in] interval time interval in ticks
+ *
+ * @notapi
+ */
+void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval) {
+
+ osalDbgAssert(interval > (gptcnt_t)0, "invalid interval");
+
+ gptp->tim->CR1 = TIM_CR1_UDIS; /* Immediate update. */
+ gptp->tim->ARR = (uint32_t)(interval - 1U); /* Time constant. */
+ gptp->tim->EGR = TIM_EGR_UG; /* Update event. */
+ gptp->tim->SR = 0U; /* Clear pending IRQs. */
+ gptp->tim->CR1 = TIM_CR1_OPM | TIM_CR1_URS | TIM_CR1_CEN;
+ while (!(gptp->tim->SR & TIM_SR_UIF))
+ ;
+ gptp->tim->SR = 0U; /* Clear pending IRQs. */
+}
+
+/**
+ * @brief Shared IRQ handler.
+ *
+ * @param[in] gptp pointer to a @p GPTDriver object
+ *
+ * @notapi
+ */
+void gpt_lld_serve_interrupt(GPTDriver *gptp) {
+ uint32_t sr;
+
+ sr = gptp->tim->SR;
+ sr &= gptp->tim->DIER & SK32_TIM_DIER_IRQ_MASK;
+ gptp->tim->SR = ~sr;
+ if ((sr & TIM_SR_UIF) != 0) {
+ _gpt_isr_invoke_cb(gptp);
+ }
+}
+
+#endif /* HAL_USE_GPT */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_gpt_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_gpt_lld.h
new file mode 100644
index 0000000000..1844696bc3
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_gpt_lld.h
@@ -0,0 +1,344 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_gpt_lld.h
+ * @brief SK32F0xx GPT subsystem low level driver header.
+ * @details The SK32F0xx general purpose timers (TIM3 on the APB1 bus and
+ * the basic/general purpose TIM16/TIM17 on the APB2 bus) use the
+ * classic 16-bit CR1/CR2/SMCR/DIER/SR/EGR + 32-bit CNT/ARR/CCRx
+ * register layout (TIMv1 class). This driver is written directly
+ * against the vendor CMSIS header (@p sk32f0xx.h), no STM32 port
+ * code is involved.
+ *
+ * @addtogroup GPT
+ * @{
+ */
+
+#ifndef HAL_GPT_LLD_H
+#define HAL_GPT_LLD_H
+
+#if HAL_USE_GPT || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @name TIM DIER register masks
+ * @{
+ */
+#define SK32_TIM_DIER_IRQ_MASK (TIM_DIER_UIE | \
+ TIM_DIER_CC1IE | \
+ TIM_DIER_CC2IE | \
+ TIM_DIER_CC3IE | \
+ TIM_DIER_CC4IE | \
+ TIM_DIER_COMIE | \
+ TIM_DIER_TIE | \
+ TIM_DIER_BIE)
+/** @} */
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief GPTD3 driver enable switch.
+ * @details If set to @p TRUE the support for GPTD3 (TIM3) is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SK32_GPT_USE_TIM3) || defined(__DOXYGEN__)
+#define SK32_GPT_USE_TIM3 FALSE
+#endif
+
+/**
+ * @brief GPTD16 driver enable switch.
+ * @details If set to @p TRUE the support for GPTD16 (TIM16) is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SK32_GPT_USE_TIM16) || defined(__DOXYGEN__)
+#define SK32_GPT_USE_TIM16 FALSE
+#endif
+
+/**
+ * @brief GPTD17 driver enable switch.
+ * @details If set to @p TRUE the support for GPTD17 (TIM17) is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SK32_GPT_USE_TIM17) || defined(__DOXYGEN__)
+#define SK32_GPT_USE_TIM17 FALSE
+#endif
+
+/**
+ * @brief GPTD3 interrupt priority level setting.
+ * @note ARMv6-M (Cortex-M0) implements only 2 priority bits, the valid
+ * priority range is 0 (highest) .. 3 (lowest).
+ */
+#if !defined(SK32_GPT_TIM3_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_GPT_TIM3_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief GPTD16 interrupt priority level setting.
+ */
+#if !defined(SK32_GPT_TIM16_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_GPT_TIM16_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief GPTD17 interrupt priority level setting.
+ */
+#if !defined(SK32_GPT_TIM17_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_GPT_TIM17_IRQ_PRIORITY 3
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if !defined(SK32_HAS_TIM3)
+#define SK32_HAS_TIM3 FALSE
+#endif
+#if !defined(SK32_HAS_TIM16)
+#define SK32_HAS_TIM16 FALSE
+#endif
+#if !defined(SK32_HAS_TIM17)
+#define SK32_HAS_TIM17 FALSE
+#endif
+
+#if SK32_GPT_USE_TIM3 && !SK32_HAS_TIM3
+#error "TIM3 not present in the selected device"
+#endif
+
+#if SK32_GPT_USE_TIM16 && !SK32_HAS_TIM16
+#error "TIM16 not present in the selected device"
+#endif
+
+#if SK32_GPT_USE_TIM17 && !SK32_HAS_TIM17
+#error "TIM17 not present in the selected device"
+#endif
+
+#if !SK32_GPT_USE_TIM3 && !SK32_GPT_USE_TIM16 && !SK32_GPT_USE_TIM17
+#error "GPT driver activated but no TIM peripheral assigned"
+#endif
+
+/* Checks on allocation of TIMx units.*/
+#if SK32_GPT_USE_TIM3
+#if defined(SK32_TIM3_IS_USED)
+#error "GPTD3 requires TIM3 but the timer is already used"
+#else
+#define SK32_TIM3_IS_USED
+#endif
+#endif
+
+#if SK32_GPT_USE_TIM16
+#if defined(SK32_TIM16_IS_USED)
+#error "GPTD16 requires TIM16 but the timer is already used"
+#else
+#define SK32_TIM16_IS_USED
+#endif
+#endif
+
+#if SK32_GPT_USE_TIM17
+#if defined(SK32_TIM17_IS_USED)
+#error "GPTD17 requires TIM17 but the timer is already used"
+#else
+#define SK32_TIM17_IS_USED
+#endif
+#endif
+
+/* IRQ priority checks.*/
+#if SK32_GPT_USE_TIM3 && !defined(SK32_TIM3_SUPPRESS_ISR) && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(SK32_GPT_TIM3_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIM3"
+#endif
+
+#if SK32_GPT_USE_TIM16 && !defined(SK32_TIM16_SUPPRESS_ISR) && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(SK32_GPT_TIM16_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIM16"
+#endif
+
+#if SK32_GPT_USE_TIM17 && !defined(SK32_TIM17_SUPPRESS_ISR) && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(SK32_GPT_TIM17_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to TIM17"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief GPT frequency type.
+ */
+typedef uint32_t gptfreq_t;
+
+/**
+ * @brief GPT counter type.
+ */
+typedef uint32_t gptcnt_t;
+
+/**
+ * @brief Driver configuration structure.
+ * @note It could be empty on some architectures.
+ */
+typedef struct {
+ /**
+ * @brief Timer clock in Hz.
+ * @note The low level can use assertions in order to catch invalid
+ * frequency specifications.
+ */
+ gptfreq_t frequency;
+ /**
+ * @brief Timer callback pointer.
+ * @note This callback is invoked on GPT counter events.
+ * @note This callback can be set to @p NULL but in that case the
+ * one-shot mode cannot be used.
+ */
+ gptcallback_t callback;
+ /* End of the mandatory fields.*/
+ /**
+ * @brief TIM CR2 register initialization data.
+ * @note The value of this field should normally be equal to zero.
+ */
+ uint16_t cr2;
+ /**
+ * @brief TIM DIER register initialization data.
+ * @note The value of this field should normally be equal to zero.
+ * @note Only the DMA-related bits can be specified in this field.
+ */
+ uint16_t dier;
+} GPTConfig;
+
+/**
+ * @brief Structure representing a GPT driver.
+ */
+struct GPTDriver {
+ /**
+ * @brief Driver state.
+ */
+ gptstate_t state;
+ /**
+ * @brief Current configuration data.
+ */
+ const GPTConfig *config;
+#if defined(GPT_DRIVER_EXT_FIELDS)
+ GPT_DRIVER_EXT_FIELDS
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Timer base clock.
+ */
+ uint32_t clock;
+ /**
+ * @brief Pointer to the TIMx registers block.
+ */
+ TIM_TypeDef *tim;
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Changes the interval of GPT peripheral.
+ * @details This function changes the interval of a running GPT unit.
+ * @pre The GPT unit must be running in continuous mode.
+ * @post The GPT unit interval is changed to the new value.
+ * @note The function has effect at the next cycle start.
+ *
+ * @param[in] gptp pointer to a @p GPTDriver object
+ * @param[in] interval new cycle time in timer ticks
+ *
+ * @notapi
+ */
+#define gpt_lld_change_interval(gptp, interval) \
+ ((gptp)->tim->ARR = (uint32_t)((interval) - 1U))
+
+/**
+ * @brief Returns the interval of GPT peripheral.
+ * @pre The GPT unit must be running in continuous mode.
+ *
+ * @param[in] gptp pointer to a @p GPTDriver object
+ * @return The current interval.
+ *
+ * @notapi
+ */
+#define gpt_lld_get_interval(gptp) ((gptcnt_t)((gptp)->tim->ARR + 1U))
+
+/**
+ * @brief Returns the counter value of GPT peripheral.
+ * @pre The GPT unit must be running in continuous mode.
+ * @note The nature of the counter is not defined, it may count upward
+ * or downward, it could be continuously running or not.
+ *
+ * @param[in] gptp pointer to a @p GPTDriver object
+ * @return The current counter value.
+ *
+ * @notapi
+ */
+#define gpt_lld_get_counter(gptp) ((gptcnt_t)(gptp)->tim->CNT)
+
+/**
+ * @brief Returns the TIM associated with a GPT.
+ *
+ * @param[in] gptp pointer to the @p GPTDriver object
+ * @return The TIM reference.
+ *
+ * @notapi
+ */
+#define gpt_lld_get_timer(gptp) ((gptp)->tim)
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if SK32_GPT_USE_TIM3 && !defined(__DOXYGEN__)
+extern GPTDriver GPTD3;
+#endif
+
+#if SK32_GPT_USE_TIM16 && !defined(__DOXYGEN__)
+extern GPTDriver GPTD16;
+#endif
+
+#if SK32_GPT_USE_TIM17 && !defined(__DOXYGEN__)
+extern GPTDriver GPTD17;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void gpt_lld_init(void);
+ void gpt_lld_start(GPTDriver *gptp);
+ void gpt_lld_stop(GPTDriver *gptp);
+ void gpt_lld_start_timer(GPTDriver *gptp, gptcnt_t period);
+ void gpt_lld_stop_timer(GPTDriver *gptp);
+ void gpt_lld_polled_delay(GPTDriver *gptp, gptcnt_t interval);
+ void gpt_lld_serve_interrupt(GPTDriver *gptp);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_GPT */
+
+#endif /* HAL_GPT_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_i2c_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_i2c_lld.c
new file mode 100644
index 0000000000..62d8563c41
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_i2c_lld.c
@@ -0,0 +1,632 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_i2c_lld.c
+ * @brief SK32F0xx low level I2C driver code.
+ * @details The SK32F0xx I2C is a legacy CR1/CR2/SR1/SR2/DR/CCR unit (the
+ * register set of the STM32F1 I2C minus the TRISE register) served
+ * by a single interrupt vector. Transfers are driven byte by byte
+ * by the event (SB/ADDR/BTF) and buffer (TXE/RXNE) interrupts, the
+ * STM32 EV/ER IRQ split and the DMA are not used.
+ *
+ * The I2C1 input clock is not software selectable (the SK32 CFGR3
+ * has no I2C1SW bits) and is analyzed as the 8MHz HSI; the CCR/FREQ
+ * programming is based on this frequency and must be verified on
+ * the actual silicon.
+ *
+ * @addtogroup I2C
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_I2C || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/**
+ * @brief Interrupt enable bits of CR2, owned by this driver.
+ */
+#define SK32_I2C_CR2_IE_MASK ((uint16_t)(I2C_CR2_ITERREN | \
+ I2C_CR2_ITEVTEN | \
+ I2C_CR2_ITBUFEN))
+
+/**
+ * @brief Error condition flags of SR1 monitored by this driver.
+ */
+#define SK32_I2C_SR1_ERRORS ((uint16_t)(I2C_SR1_BERR | \
+ I2C_SR1_ARLO | \
+ I2C_SR1_AF | \
+ I2C_SR1_OVR))
+
+/**
+ * @brief I2C peripheral input clock (analyzed as the 8MHz HSI).
+ */
+#define SK32_I2C_CLK ((uint32_t)SK32_HSICLK)
+
+/**
+ * @brief I2C peripheral clock expressed in MHz (CR2 FREQ field).
+ */
+#define SK32_I2C_FREQ ((uint16_t)(SK32_HSICLK / 1000000U))
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/** @brief I2C1 driver identifier.*/
+#if SK32_I2C_USE_I2C1 || defined(__DOXYGEN__)
+I2CDriver I2CD1;
+#endif
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Resets the I2C peripheral to its disabled state.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+static void i2c_lld_abort_operation(I2CDriver *i2cp) {
+ I2C_TypeDef *dp = i2cp->i2c;
+
+ /* Stops the I2C peripheral and clears all the flags.*/
+ dp->CR1 = I2C_CR1_SWRST;
+ dp->CR1 = 0U;
+ dp->CR2 = 0U;
+ dp->SR1 = 0U;
+}
+
+/**
+ * @brief Programs the CCR register and the CR2 FREQ field.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+static void i2c_lld_set_clock(I2CDriver *i2cp) {
+ I2C_TypeDef *dp = i2cp->i2c;
+ uint32_t clock_speed = i2cp->config->clock_speed;
+ i2cdutycycle_t duty = i2cp->config->duty_cycle;
+ uint32_t ccr;
+
+ osalDbgCheck((i2cp != NULL) &&
+ (clock_speed > 0U) &&
+ (clock_speed <= 400000U));
+
+ /* CR2 FREQ[5:0] = peripheral input clock in MHz.*/
+ dp->CR2 &= (uint16_t)~I2C_CR2_FREQ;
+ dp->CR2 |= SK32_I2C_FREQ;
+
+ /* CCR programming, standard mode uses a 1/1 duty cycle while the fast
+ mode uses the 2/1 or the 16/9 duty cycle selected in the config.*/
+ if (clock_speed <= 100000U) {
+ osalDbgAssert(duty == STD_DUTY_CYCLE, "invalid standard mode duty cycle");
+
+ /* Standard mode CCR: Tlow/Thigh = 1/1.*/
+ ccr = SK32_I2C_CLK / (clock_speed * 2U);
+ osalDbgAssert(ccr >= 0x04U,
+ "clock divider less than 0x04 not allowed");
+ dp->CCR = (uint16_t)ccr;
+ }
+ else {
+ osalDbgAssert((duty == FAST_DUTY_CYCLE_2) ||
+ (duty == FAST_DUTY_CYCLE_16_9),
+ "invalid fast mode duty cycle");
+
+ if (duty == FAST_DUTY_CYCLE_16_9) {
+ /* Fast mode CCR with a 16/9 duty cycle.*/
+ ccr = SK32_I2C_CLK / (clock_speed * 25U);
+ }
+ else {
+ /* Fast mode CCR with a 2/1 duty cycle.*/
+ ccr = SK32_I2C_CLK / (clock_speed * 3U);
+ }
+
+ osalDbgAssert(ccr >= 0x01U, "clock divider less than 0x01 not allowed");
+ osalDbgAssert(ccr <= (uint32_t)I2C_CCR_CCR, "the selected clock is too low");
+
+ if (duty == FAST_DUTY_CYCLE_16_9) {
+ dp->CCR = (uint16_t)(I2C_CCR_FS | I2C_CCR_DUTY | (ccr & I2C_CCR_CCR));
+ }
+ else {
+ dp->CCR = (uint16_t)(I2C_CCR_FS | (ccr & I2C_CCR_CCR));
+ }
+ }
+}
+
+/**
+ * @brief Programs the operation mode bits of CR1.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+static void i2c_lld_set_opmode(I2CDriver *i2cp) {
+ I2C_TypeDef *dp = i2cp->i2c;
+ uint16_t regCR1 = dp->CR1;
+
+ switch (i2cp->config->op_mode) {
+ case OPMODE_SMBUS_DEVICE:
+ regCR1 |= I2C_CR1_SMBUS;
+ regCR1 &= (uint16_t)~I2C_CR1_SMBTYPE;
+ break;
+ case OPMODE_SMBUS_HOST:
+ regCR1 |= (uint16_t)(I2C_CR1_SMBUS | I2C_CR1_SMBTYPE);
+ break;
+ case OPMODE_I2C:
+ default:
+ regCR1 &= (uint16_t)~(I2C_CR1_SMBUS | I2C_CR1_SMBTYPE);
+ break;
+ }
+ dp->CR1 = regCR1;
+}
+
+/**
+ * @brief Common error interrupt handling.
+ * @note Called with the transfer context active, the @p errors mask is
+ * filled and the waiting thread is resumed with @p MSG_RESET.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ * @param[in] sr1 content of the SR1 register (with the error bits)
+ *
+ * @notapi
+ */
+static void i2c_lld_serve_error_interrupt(I2CDriver *i2cp, uint16_t sr1) {
+ I2C_TypeDef *dp = i2cp->i2c;
+
+ i2cp->errors = I2C_NO_ERROR;
+
+ if ((sr1 & I2C_SR1_BERR) != 0U) {
+ i2cp->errors |= I2C_BUS_ERROR;
+ }
+ if ((sr1 & I2C_SR1_ARLO) != 0U) {
+ i2cp->errors |= I2C_ARBITRATION_LOST;
+ }
+ if ((sr1 & I2C_SR1_AF) != 0U) {
+ i2cp->errors |= I2C_ACK_FAILURE;
+ }
+ if ((sr1 & I2C_SR1_OVR) != 0U) {
+ i2cp->errors |= I2C_OVERRUN;
+ }
+
+ /* Disables the interrupt sources, the transfer is over.*/
+ dp->CR2 &= (uint16_t)~SK32_I2C_CR2_IE_MASK;
+
+ /* An acknowledge failure leaves the lines in an undefined state while the
+ master still owns the bus, a STOP releases them. In case of a bus
+ error or of an arbitration loss the bus is (or becomes) owned by some
+ other master, generating a STOP would disturb it, so nothing is done.*/
+ if ((sr1 & (I2C_SR1_AF | I2C_SR1_BERR)) != 0U) {
+ dp->CR1 |= I2C_CR1_STOP;
+ }
+
+ /* Clears the error flags, they are cleared by writing zeros to them.*/
+ dp->SR1 = (uint16_t)0U;
+
+ SK32_I2C_ERROR_HOOK(i2cp);
+ _i2c_wakeup_error_isr(i2cp);
+}
+
+/**
+ * @brief Common I2C interrupt handler.
+ * @note One event is served per invocation; the peripheral keeps the
+ * interrupt line asserted while a served condition (or a new one)
+ * remains pending, so the leftover flags generate a new interrupt
+ * as soon as this handler returns. Reading SR1 followed by the
+ * appropriate register access is what clears each flag.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+static void i2c_lld_serve_interrupt(I2CDriver *i2cp) {
+ I2C_TypeDef *dp = i2cp->i2c;
+ uint16_t sr1 = dp->SR1;
+ uint16_t sr2;
+
+ /* Error conditions detection, the transfer is aborted.*/
+ if ((sr1 & SK32_I2C_SR1_ERRORS) != 0U) {
+ i2c_lld_serve_error_interrupt(i2cp, sr1);
+ return;
+ }
+
+ /* EV5, start condition generated. The address byte (with the R/W bit)
+ is written to the data register, this also clears the SB flag.*/
+ if ((sr1 & I2C_SR1_SB) != 0U) {
+ dp->DR = i2cp->addr;
+ return;
+ }
+
+ /* EV6, address sent. Reading SR2 clears the ADDR flag.*/
+ if ((sr1 & I2C_SR1_ADDR) != 0U) {
+ sr2 = dp->SR2;
+
+ if ((sr2 & I2C_SR2_TRA) != 0U) {
+ /* Master transmitter: the first data byte is written here, right
+ after the ADDR flag has been cleared. A data byte written while
+ the ADDR flag is still pending is ignored by the peripheral, so
+ the first byte is never written on the TXE event.*/
+ if ((i2cp->txbytes > 0U) && (i2cp->txidx < i2cp->txbytes)) {
+ dp->DR = i2cp->txbuf[i2cp->txidx];
+ i2cp->txidx++;
+ }
+ }
+ else {
+ /* Master receiver: the single data byte of a 1 byte read must be
+ NACKed and followed by a STOP, both are programmed here.*/
+ if (i2cp->rxbytes == 1U) {
+ dp->CR1 &= (uint16_t)~I2C_CR1_ACK;
+ dp->CR1 |= I2C_CR1_STOP;
+ }
+ }
+ return;
+ }
+
+ /* EV7, data received.*/
+ if (((sr1 & I2C_SR1_RXNE) != 0U) && (i2cp->rxphase != 0U)) {
+ uint8_t b = (uint8_t)dp->DR;
+
+ if (i2cp->rxidx < i2cp->rxbytes) {
+ i2cp->rxbuf[i2cp->rxidx] = b;
+ }
+ i2cp->rxidx++;
+
+ if (i2cp->rxidx >= i2cp->rxbytes) {
+ /* Last data byte received, the STOP has already been programmed
+ before its arrival (see below).*/
+ dp->CR2 &= (uint16_t)~SK32_I2C_CR2_IE_MASK;
+ _i2c_wakeup_isr(i2cp);
+ }
+ else if (i2cp->rxidx == (i2cp->rxbytes - 1U)) {
+ /* The byte just read is the second to last: the last one must be
+ NACKed and followed by a STOP. The peripheral stretches SCL when
+ it cannot proceed, so the programming below is in time for the
+ 9th clock of the next byte.*/
+ dp->CR1 &= (uint16_t)~I2C_CR1_ACK;
+ dp->CR1 |= I2C_CR1_STOP;
+ }
+ return;
+ }
+
+ /* EV8, data to be transmitted.*/
+ if (((sr1 & I2C_SR1_TXE) != 0U) && (i2cp->rxphase == 0U) &&
+ (i2cp->txidx < i2cp->txbytes)) {
+ dp->DR = i2cp->txbuf[i2cp->txidx];
+ i2cp->txidx++;
+ return;
+ }
+
+ /* EV8_2, byte transfer finished (BTF). In master transmitter mode it
+ means that the last queued byte has been shifted out.*/
+ if ((sr1 & I2C_SR1_BTF) != 0U) {
+ if (i2cp->rxphase == 0U) {
+ if (i2cp->txidx < i2cp->txbytes) {
+ /* The peripheral is stretching the clock waiting for the next byte
+ (the software did not keep the data register filled).*/
+ dp->DR = i2cp->txbuf[i2cp->txidx];
+ i2cp->txidx++;
+ return;
+ }
+
+ if (i2cp->rxbytes > 0U) {
+ /* Combined write-then-read transfer: a repeated START is generated
+ and the address is sent again with the R/W bit set.*/
+ i2cp->rxphase = true;
+ i2cp->addr = (i2caddr_t)(i2cp->addr | 1U);
+ dp->CR1 |= (uint16_t)(I2C_CR1_ACK | I2C_CR1_START);
+ return;
+ }
+
+ /* Pure transmit: end of operation, a STOP is generated.*/
+ dp->CR1 |= I2C_CR1_STOP;
+ dp->CR2 &= (uint16_t)~SK32_I2C_CR2_IE_MASK;
+ _i2c_wakeup_isr(i2cp);
+ }
+ return;
+ }
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if SK32_I2C_USE_I2C1 || defined(__DOXYGEN__)
+#if !defined(SK32_I2C1_SUPPRESS_ISR)
+#if !defined(SK32_I2C1_HANDLER)
+#error "SK32_I2C1_HANDLER not defined"
+#endif
+/**
+ * @brief I2C1 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_I2C1_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ i2c_lld_serve_interrupt(&I2CD1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+#endif
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level I2C driver initialization.
+ *
+ * @notapi
+ */
+void i2c_lld_init(void) {
+
+#if SK32_I2C_USE_I2C1
+ i2cObjectInit(&I2CD1);
+ I2CD1.i2c = I2C1;
+ I2CD1.thread = NULL;
+#endif
+}
+
+/**
+ * @brief Configures and activates the I2C peripheral.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+void i2c_lld_start(I2CDriver *i2cp) {
+ I2C_TypeDef *dp = i2cp->i2c;
+
+ /* If in stopped state then enables the I2C clock and the interrupt
+ vector.*/
+ if (i2cp->state == I2C_STOP) {
+#if SK32_I2C_USE_I2C1
+ if (&I2CD1 == i2cp) {
+ rccResetI2C1();
+ rccEnableI2C1(true);
+ nvicEnableVector(SK32_I2C1_NUMBER, SK32_I2C_I2C1_PRIORITY);
+ }
+#endif
+ }
+
+ /* Reset the peripheral to a known state.*/
+ i2c_lld_abort_operation(i2cp);
+
+ /* Setup the peripheral parameters and enable it, ACK is on by default
+ so that multi-byte receptions are acknowledged until the last byte.*/
+ i2c_lld_set_clock(i2cp);
+ i2c_lld_set_opmode(i2cp);
+ dp->CR1 = (uint16_t)(I2C_CR1_PE | I2C_CR1_ACK);
+}
+
+/**
+ * @brief Deactivates the I2C peripheral.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+void i2c_lld_stop(I2CDriver *i2cp) {
+
+ /* If not in stopped state then disables the I2C clock.*/
+ if (i2cp->state != I2C_STOP) {
+ i2c_lld_abort_operation(i2cp);
+
+#if SK32_I2C_USE_I2C1
+ if (&I2CD1 == i2cp) {
+ nvicDisableVector(SK32_I2C1_NUMBER);
+ rccDisableI2C1();
+ }
+#endif
+ }
+}
+
+/**
+ * @brief Receives data via the I2C bus as master.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ * @param[in] addr slave device address (7 bits, no R/W bit)
+ * @param[out] rxbuf pointer to the receive buffer
+ * @param[in] rxbytes number of bytes to be received
+ * @param[in] timeout the number of ticks before the operation timeouts,
+ * the following special values are allowed:
+ * - @a TIME_INFINITE no timeout.
+ * .
+ * @return The operation status.
+ * @retval MSG_OK if the function succeeded.
+ * @retval MSG_RESET if one or more I2C errors occurred, the errors can
+ * be retrieved using @p i2cGetErrors().
+ * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a
+ * timeout the driver must be stopped and restarted
+ * because the bus is in an uncertain state.
+ *
+ * @notapi
+ */
+msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ uint8_t *rxbuf, size_t rxbytes,
+ sysinterval_t timeout) {
+ I2C_TypeDef *dp = i2cp->i2c;
+ systime_t start, end;
+ msg_t msg;
+
+ osalDbgCheck((i2cp != NULL) && (rxbuf != NULL) && (rxbytes > 0U));
+
+ /* Initializes the driver fields, the LSB of the address selects the read
+ direction.*/
+ i2cp->errors = I2C_NO_ERROR;
+ i2cp->addr = (i2caddr_t)((addr << 1) | 1U);
+ i2cp->txbuf = NULL;
+ i2cp->txbytes = 0U;
+ i2cp->txidx = 0U;
+ i2cp->rxbuf = rxbuf;
+ i2cp->rxbytes = rxbytes;
+ i2cp->rxidx = 0U;
+ i2cp->rxphase = true;
+
+ /* Releases the lock from the high level driver.*/
+ osalSysUnlock();
+
+ /* Calculating the time window for the timeout on the busy bus
+ condition.*/
+ start = osalOsGetSystemTimeX();
+ end = osalTimeAddX(start, OSAL_MS2I(SK32_I2C_BUSY_TIMEOUT));
+
+ /* Waits until the BUSY flag is reset and no STOP is pending or,
+ alternatively, for a timeout condition.*/
+ while (true) {
+ osalSysLock();
+
+ /* If the bus is not busy then the operation can continue, note, the
+ loop is exited in the locked state.*/
+ if (((dp->SR2 & I2C_SR2_BUSY) == 0U) && ((dp->CR1 & I2C_CR1_STOP) == 0U)) {
+ break;
+ }
+
+ /* If the system time went outside the allowed window then a timeout
+ condition is returned.*/
+ if (!osalTimeIsInRangeX(osalOsGetSystemTimeX(), start, end)) {
+ return MSG_TIMEOUT;
+ }
+
+ osalSysUnlock();
+ }
+
+ /* Starts the operation.*/
+ dp->CR2 |= SK32_I2C_CR2_IE_MASK;
+ dp->CR1 |= (uint16_t)(I2C_CR1_ACK | I2C_CR1_START);
+
+ /* Waits for the operation completion or a timeout.*/
+ msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout);
+
+ /* In case of a software timeout the interrupt sources are disabled and a
+ STOP is sent as an extreme attempt to release the bus.*/
+ if (msg == MSG_TIMEOUT) {
+ dp->CR2 &= (uint16_t)~SK32_I2C_CR2_IE_MASK;
+ dp->CR1 |= I2C_CR1_STOP;
+ }
+
+ return msg;
+}
+
+/**
+ * @brief Transmits data via the I2C bus as master.
+ * @details This function is also used for the "read-through-write" paradigm:
+ * if @p rxbytes is not zero a repeated START is generated after the
+ * last transmitted byte and @p rxbytes bytes are received from the
+ * same slave.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ * @param[in] addr slave device address (7 bits, no R/W bit)
+ * @param[in] txbuf pointer to the transmit buffer
+ * @param[in] txbytes number of bytes to be transmitted
+ * @param[out] rxbuf pointer to the receive buffer
+ * @param[in] rxbytes number of bytes to be received, set it to 0 if you
+ * want a transmit only operation
+ * @param[in] timeout the number of ticks before the operation timeouts,
+ * the following special values are allowed:
+ * - @a TIME_INFINITE no timeout.
+ * .
+ * @return The operation status.
+ * @retval MSG_OK if the function succeeded.
+ * @retval MSG_RESET if one or more I2C errors occurred, the errors can
+ * be retrieved using @p i2cGetErrors().
+ * @retval MSG_TIMEOUT if a timeout occurred before operation end. After a
+ * timeout the driver must be stopped and restarted
+ * because the bus is in an uncertain state.
+ *
+ * @notapi
+ */
+msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ const uint8_t *txbuf, size_t txbytes,
+ uint8_t *rxbuf, size_t rxbytes,
+ sysinterval_t timeout) {
+ I2C_TypeDef *dp = i2cp->i2c;
+ systime_t start, end;
+ msg_t msg;
+
+ osalDbgCheck((i2cp != NULL) && (txbuf != NULL) && (txbytes > 0U) &&
+ ((rxbytes == 0U) || ((rxbuf != NULL) && (rxbytes > 0U))));
+
+ /* Initializes the driver fields, the LSB of the address selects the write
+ direction, it is flipped by the interrupt handler before the repeated
+ START of a combined transfer.*/
+ i2cp->errors = I2C_NO_ERROR;
+ i2cp->addr = (i2caddr_t)(addr << 1);
+ i2cp->txbuf = txbuf;
+ i2cp->txbytes = txbytes;
+ i2cp->txidx = 0U;
+ i2cp->rxbuf = rxbuf;
+ i2cp->rxbytes = rxbytes;
+ i2cp->rxidx = 0U;
+ i2cp->rxphase = false;
+
+ /* Releases the lock from the high level driver.*/
+ osalSysUnlock();
+
+ /* Calculating the time window for the timeout on the busy bus
+ condition.*/
+ start = osalOsGetSystemTimeX();
+ end = osalTimeAddX(start, OSAL_MS2I(SK32_I2C_BUSY_TIMEOUT));
+
+ /* Waits until the BUSY flag is reset and no STOP is pending or,
+ alternatively, for a timeout condition.*/
+ while (true) {
+ osalSysLock();
+
+ /* If the bus is not busy then the operation can continue, note, the
+ loop is exited in the locked state.*/
+ if (((dp->SR2 & I2C_SR2_BUSY) == 0U) && ((dp->CR1 & I2C_CR1_STOP) == 0U)) {
+ break;
+ }
+
+ /* If the system time went outside the allowed window then a timeout
+ condition is returned.*/
+ if (!osalTimeIsInRangeX(osalOsGetSystemTimeX(), start, end)) {
+ return MSG_TIMEOUT;
+ }
+
+ osalSysUnlock();
+ }
+
+ /* Starts the operation.*/
+ dp->CR2 |= SK32_I2C_CR2_IE_MASK;
+ dp->CR1 |= (uint16_t)(I2C_CR1_ACK | I2C_CR1_START);
+
+ /* Waits for the operation completion or a timeout.*/
+ msg = osalThreadSuspendTimeoutS(&i2cp->thread, timeout);
+
+ /* In case of a software timeout the interrupt sources are disabled and a
+ STOP is sent as an extreme attempt to release the bus.*/
+ if (msg == MSG_TIMEOUT) {
+ dp->CR2 &= (uint16_t)~SK32_I2C_CR2_IE_MASK;
+ dp->CR1 |= I2C_CR1_STOP;
+ }
+
+ return msg;
+}
+
+#endif /* HAL_USE_I2C */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_i2c_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_i2c_lld.h
new file mode 100644
index 0000000000..a680d3777f
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_i2c_lld.h
@@ -0,0 +1,290 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_i2c_lld.h
+ * @brief SK32F0xx low level I2C driver header.
+ * @details The SK32F0xx I2C is a legacy CR1/CR2/SR1/SR2/DR/CCR unit (the
+ * register set used by the STM32F1 family with the rise-time
+ * register removed) and is served by a single interrupt vector
+ * (no EV/ER IRQ split). This driver is interrupt-driven, is
+ * written directly against the vendor CMSIS header (@p
+ * sk32f0xx.h) and does not use DMA.
+ * @note Only the master mode is supported, the I2C_ENABLE_SLAVE_MODE
+ * switch must stay at its default @p FALSE value.
+ *
+ * @addtogroup I2C
+ * @{
+ */
+
+#ifndef HAL_I2C_LLD_H
+#define HAL_I2C_LLD_H
+
+#if HAL_USE_I2C || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief This I2C LLD does not support the slave mode.
+ */
+#define I2C_SUPPORTS_SLAVE_MODE FALSE
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief I2C1 driver enable switch.
+ * @details If set to @p TRUE the support for I2C1 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SK32_I2C_USE_I2C1) || defined(__DOXYGEN__)
+#define SK32_I2C_USE_I2C1 FALSE
+#endif
+
+/**
+ * @brief I2C1 interrupt priority level setting.
+ * @note ARMv6-M (Cortex-M0) implements only 2 priority bits, the valid
+ * priority range is 0 (highest) .. 3 (lowest).
+ */
+#if !defined(SK32_I2C_I2C1_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_I2C_I2C1_PRIORITY 3
+#endif
+
+/**
+ * @brief I2C timeout on busy condition in milliseconds.
+ */
+#if !defined(SK32_I2C_BUSY_TIMEOUT) || defined(__DOXYGEN__)
+#define SK32_I2C_BUSY_TIMEOUT 50
+#endif
+
+/**
+ * @brief I2C error hook.
+ * @details This hook is invoked from the ISR context when a bus condition
+ * (NACK, bus error, arbitration loss, overrun) aborts an ongoing
+ * transfer.
+ */
+#if !defined(SK32_I2C_ERROR_HOOK) || defined(__DOXYGEN__)
+#define SK32_I2C_ERROR_HOOK(i2cp) osalSysHalt("I2C failure")
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if SK32_I2C_USE_I2C1 && !SK32_HAS_I2C1
+#error "I2C1 not present in the selected device"
+#endif
+
+#if !SK32_I2C_USE_I2C1
+#error "I2C driver activated but no I2C peripheral assigned"
+#endif
+
+#if SK32_I2C_USE_I2C1 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(SK32_I2C_I2C1_PRIORITY)
+#error "Invalid IRQ priority assigned to I2C1"
+#endif
+
+/* Checks on allocation of I2C units.*/
+#if SK32_I2C_USE_I2C1
+#if defined(SK32_I2C1_IS_USED)
+#error "I2CD1 requires I2C1 but it is already used"
+#else
+#define SK32_I2C1_IS_USED
+#endif
+#endif
+
+#if I2C_ENABLE_SLAVE_MODE
+#error "I2C slave mode not supported by this driver"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type representing an I2C address.
+ */
+typedef uint16_t i2caddr_t;
+
+/**
+ * @brief Type of I2C driver condition flags.
+ */
+typedef uint32_t i2cflags_t;
+
+/**
+ * @brief Supported modes for the I2C bus.
+ */
+typedef enum {
+ OPMODE_I2C = 1,
+ OPMODE_SMBUS_DEVICE = 2,
+ OPMODE_SMBUS_HOST = 3,
+} i2copmode_t;
+
+/**
+ * @brief Supported duty cycle modes for the I2C bus.
+ */
+typedef enum {
+ STD_DUTY_CYCLE = 1,
+ FAST_DUTY_CYCLE_2 = 2,
+ FAST_DUTY_CYCLE_16_9 = 3,
+} i2cdutycycle_t;
+
+/**
+ * @brief I2C driver configuration structure.
+ */
+struct hal_i2c_config {
+ /* End of the mandatory fields.*/
+ i2copmode_t op_mode; /**< @brief Specifies the I2C mode. */
+ uint32_t clock_speed; /**< @brief Specifies the clock frequency.
+ @note Must be set to a value lower
+ than 400kHz. */
+ i2cdutycycle_t duty_cycle; /**< @brief Specifies the I2C fast mode
+ duty cycle. */
+};
+
+/**
+ * @brief Type of a structure representing an I2C configuration.
+ */
+typedef struct hal_i2c_config I2CConfig;
+
+/**
+ * @brief Type of a structure representing an I2C driver.
+ */
+typedef struct hal_i2c_driver I2CDriver;
+
+/**
+ * @brief Structure representing an I2C driver.
+ */
+struct hal_i2c_driver {
+ /**
+ * @brief Driver state.
+ */
+ i2cstate_t state;
+ /**
+ * @brief Current configuration data.
+ */
+ const I2CConfig *config;
+ /**
+ * @brief Error flags.
+ */
+ i2cflags_t errors;
+#if I2C_USE_MUTUAL_EXCLUSION || defined(__DOXYGEN__)
+ /**
+ * @brief Mutex protecting the bus.
+ */
+ mutex_t mutex;
+#endif /* I2C_USE_MUTUAL_EXCLUSION */
+#if defined(I2C_DRIVER_EXT_FIELDS)
+ I2C_DRIVER_EXT_FIELDS
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Thread waiting for I/O completion.
+ */
+ thread_reference_t thread;
+ /**
+ * @brief Pointer to the I2Cx registers block.
+ */
+ I2C_TypeDef *i2c;
+ /**
+ * @brief Address byte (slave address shifted left plus the R/W bit).
+ */
+ i2caddr_t addr;
+ /**
+ * @brief Pointer to the transmit buffer.
+ */
+ const uint8_t *txbuf;
+ /**
+ * @brief Number of data bytes to be transmitted.
+ */
+ size_t txbytes;
+ /**
+ * @brief Number of data bytes written to the data register.
+ */
+ size_t txidx;
+ /**
+ * @brief Pointer to the receive buffer.
+ */
+ uint8_t *rxbuf;
+ /**
+ * @brief Number of data bytes to be received.
+ */
+ size_t rxbytes;
+ /**
+ * @brief Number of data bytes read from the data register.
+ */
+ size_t rxidx;
+ /**
+ * @brief Read phase flag, set when the read (repeated start) phase of
+ * a combined transfer is in progress.
+ */
+ bool rxphase;
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Get errors from I2C driver.
+ *
+ * @param[in] i2cp pointer to the @p I2CDriver object
+ *
+ * @notapi
+ */
+#define i2c_lld_get_errors(i2cp) ((i2cp)->errors)
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if !defined(__DOXYGEN__)
+#if SK32_I2C_USE_I2C1
+extern I2CDriver I2CD1;
+#endif
+#endif /* !defined(__DOXYGEN__) */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void i2c_lld_init(void);
+ void i2c_lld_start(I2CDriver *i2cp);
+ void i2c_lld_stop(I2CDriver *i2cp);
+ msg_t i2c_lld_master_transmit_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ const uint8_t *txbuf, size_t txbytes,
+ uint8_t *rxbuf, size_t rxbytes,
+ sysinterval_t timeout);
+ msg_t i2c_lld_master_receive_timeout(I2CDriver *i2cp, i2caddr_t addr,
+ uint8_t *rxbuf, size_t rxbytes,
+ sysinterval_t timeout);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_I2C */
+
+#endif /* HAL_I2C_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_kbcu_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_kbcu_lld.c
new file mode 100644
index 0000000000..f89427d6ab
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_kbcu_lld.c
@@ -0,0 +1,411 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_kbcu_lld.c
+ * @brief SK32F0xx KBCU subsystem low level driver source.
+ * @details Implements the KBCU (Keyboard Control Unit) peripheral driver in
+ * independent scan mode. The key matrix is scanned by the hardware
+ * and the statuses are read back through the KSR registers, an
+ * optional interrupt aborts the current cycle and lets the driver
+ * snapshot the key statuses and fire a user callback. The RGB
+ * (LED) data is streamed through a DMA1 channel in memory to
+ * peripheral mode targeting the @p KBCU->DMAR register.
+ *
+ * @addtogroup KBCU
+ * @{
+ */
+
+#include "hal.h"
+#include "hal_kbcu_lld.h"
+
+#if HAL_USE_KBCU || defined(__DOXYGEN__)
+
+/* The KBCU driver allocates a DMA1 channel for the RGB (LED) burst, so the
+ DMA helper code of sk32_dma.c (and dmaInit()) must be part of the build.
+ The platform header (hal_lld.h) defines SK32_DMA_REQUIRED automatically
+ whenever HAL_USE_KBCU is TRUE, this check only protects direct/standalone
+ uses.*/
+#if !defined(SK32_DMA_REQUIRED)
+#error "the KBCU low level driver requires SK32_DMA_REQUIRED to be defined"
+#endif
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/**
+ * @brief Maximum number of LED row registers.
+ */
+#define SK32_KBCU_LED_ROWS 8U
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/**
+ * @brief @p TRUE when the driver is started.
+ */
+static bool kbcu_started;
+
+/**
+ * @brief DMA1 channel used to stream the RGB frame to the KBCU.
+ */
+static const sk32_dma_stream_t *kbcu_stream;
+
+/**
+ * @brief Key status callback, invoked from the KBCU ISR.
+ */
+static kbcu_key_cb kbcu_cb;
+
+/**
+ * @brief Snapshot of the 6 KSR registers taken inside the ISR.
+ */
+static uint32_t kbcu_ksr[SK32_KBCU_KSRS];
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Routes the DMA1 channel 4 request line to the KBCU.
+ * @note The remapping code is written into the 6 request bits of the
+ * channel slot of the SYSCFG CFGR3 register, following the vendor
+ * SYSCFG_DMAChannelConfig() register layout (channel 4 uses the
+ * slot at bits 24..29).
+ *
+ * @param[in] code the 6 bit request selection code
+ */
+static void kbcu_remap(uint32_t code) {
+ SYSCFG->CFGR3 = (SYSCFG->CFGR3 & ~(SK32_KBCU_DMA_REQ_MASK <<
+ SK32_KBCU_DMA_SLOT_SHIFT)) |
+ (code << SK32_KBCU_DMA_SLOT_SHIFT);
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if !defined(SK32_KBCU_HANDLER)
+#error "SK32_KBCU_HANDLER not defined"
+#endif
+/**
+ * @brief KBCU interrupt handler.
+ * @details Clears the frame/key flags, snapshots the key statuses and fires
+ * the user callback when a key status change was detected.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_KBCU_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ if (KBCU->SR & KBCU_SR_FRAMEF) {
+ KBCU->SR &= ~KBCU_SR_FRAMEF;
+ }
+
+ if (KBCU->SR & KBCU_SR_KEYF) {
+ KBCU->SR &= ~KBCU_SR_KEYF;
+
+ for (uint8_t i = 0U; i < SK32_KBCU_KSRS; i++) {
+ kbcu_ksr[i] = KBCU->KSR[i];
+ }
+
+ if (kbcu_cb != NULL) {
+ kbcu_cb(kbcu_ksr);
+ }
+ }
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Initializes the KBCU driver.
+ * @note The function is idempotent and can be called multiple times
+ * without issues.
+ *
+ * @init
+ */
+void kbcu_lld_init(void) {
+
+ kbcu_started = false;
+ kbcu_stream = NULL;
+ kbcu_cb = NULL;
+}
+
+/**
+ * @brief Starts the KBCU peripheral in independent scan mode.
+ * @details Enables the KBCU APB1 clock, programs the CR0/CR1/CR2 fields for
+ * independent mode (the multiplexing field is kept cleared), enables
+ * the KBCU NVIC interrupt and finally sets the @p EN bit.
+ * @note The column/KEY pads must be configured in alternate function mode
+ * by the application/board code, this driver does not touch the GPIO.
+ * @note The interrupt configuration (which sources are enabled) is left to
+ * @p kbcu_lld_itconfig() so that @p kbcu_lld_start() stays minimal.
+ *
+ * @param[in] config pointer to the @p KBCUConfig structure
+ *
+ * @init
+ */
+void kbcu_lld_start(const KBCUConfig *config) {
+ uint32_t cr0;
+
+ osalDbgCheck(config != NULL);
+
+ if (kbcu_started) {
+ return;
+ }
+
+ /* Enabling the KBCU APB1 clock and resetting the peripheral.*/
+ rccEnableKBCU(true);
+ rccResetKBCU();
+
+ /* CR0: columns, key/columns effective levels, column mask and cycle
+ value. Independent mode, the managed bits are cleared first.*/
+ cr0 = (config->key_effective_level == KBCU_KEY_LEVEL_HIGH) ?
+ KBCU_CR0_KIP : 0U;
+ cr0 |= (config->columns_effective_level == KBCU_COLUMN_LEVEL_HIGH) ?
+ KBCU_CR0_COP : 0U;
+ cr0 |= ((uint32_t)((config->columns_num - 15U) << 2)) & KBCU_CR0_CS;
+ cr0 |= ((uint32_t)config->column_mask << 16) & KBCU_CR0_CMASK;
+ cr0 |= ((uint32_t)config->columns_cycle_value << 24) & KBCU_CR0_CCR;
+ KBCU->CR0 = (KBCU->CR0 & ~SK32_KBCU_CR0_OWNED_MASK) | cr0;
+
+ /* CR1: cycle count, clock division, flicker length and dead time.*/
+ KBCU->CR1 = (KBCU->CR1 & ~SK32_KBCU_CR1_OWNED_MASK) |
+ (uint32_t)config->columns_cycle_count |
+ ((uint32_t)config->clock_division << 8) |
+ ((uint32_t)config->flicker_length << 16) |
+ ((uint32_t)config->dead_time << 24);
+
+ /* Independent scan mode (the multiplexing field stays cleared).*/
+ KBCU->CR2 &= ~KBCU_CR2_SM;
+
+ /* NVIC vector; the actual IER sources are programmed by itconfig().*/
+ nvicEnableVector(SK32_KBCU_NUMBER, SK32_IRQ_KBCU_PRIORITY);
+
+ /* Peripheral enable.*/
+ KBCU->CR0 |= KBCU_CR0_EN;
+
+ kbcu_started = true;
+}
+
+/**
+ * @brief Stops the KBCU peripheral.
+ * @details Disables any pending RGB DMA, clears all the managed CR0 bits,
+ * disables the NVIC vector and the peripheral clock.
+ *
+ * @init
+ */
+void kbcu_lld_stop(void) {
+
+ if (!kbcu_started) {
+ return;
+ }
+
+ kbcu_lld_led_dma_stop();
+
+ KBCU->CR0 &= ~SK32_KBCU_CR0_OWNED_MASK;
+ nvicDisableVector(SK32_KBCU_NUMBER);
+ rccDisableKBCU();
+
+ kbcu_started = false;
+}
+
+/**
+ * @brief Enables the KBCU peripheral (sets the EN bit).
+ */
+void kbcu_lld_enable(void) {
+ KBCU->CR0 |= KBCU_CR0_EN;
+}
+
+/**
+ * @brief Disables the KBCU peripheral (clears the EN bit).
+ */
+void kbcu_lld_disable(void) {
+ KBCU->CR0 &= ~KBCU_CR0_EN;
+}
+
+/**
+ * @brief Configures the KBCU interrupt sources and frame count.
+ *
+ * @param[in] it OR of the enabled sources (@p kbcu_it_t)
+ * @param[in] frame_count frame count value (0x00..0xFF)
+ */
+void kbcu_lld_itconfig(kbcu_it_t it, uint8_t frame_count) {
+ uint32_t ier = (KBCU->IER & ~SK32_KBCU_IER_OWNED_MASK);
+
+ ier |= (uint32_t)it & (KBCU_IER_FRAMEIE | KBCU_IER_COLIE | KBCU_IER_KEYIE);
+ ier |= (uint32_t)frame_count << 16;
+
+ KBCU->IER = ier;
+}
+
+/**
+ * @brief Installs a key status callback.
+ * @note The callback is invoked from the KBCU ISR context.
+ *
+ * @param[in] cb callback function or @p NULL to remove it
+ */
+void kbcu_lld_set_callback(kbcu_key_cb cb) {
+ kbcu_cb = cb;
+}
+
+/**
+ * @brief Returns the 8 row statuses of a column.
+ *
+ * @param[in] column column index (0..23)
+ * @return The 8 row status bits
+ */
+uint8_t kbcu_lld_get_column(uint8_t column) {
+ return (uint8_t)((KBCU->KSR[column / 4U] >> ((column % 4U) * 8U)) & 0xFFU);
+}
+
+/**
+ * @brief Returns the status of a single key.
+ *
+ * @param[in] column column index (0..23)
+ * @param[in] row row index (0..7)
+ * @retval @p true if the key is pressed
+ * @retval @p false otherwise
+ */
+bool kbcu_lld_get_key(uint8_t column, uint8_t row) {
+ return (KBCU->KSR[column / 4U] >> (((column % 4U) * 8U) + row)) & 1U;
+}
+
+/**
+ * @brief Reads the six KSR registers.
+ *
+ * @param[out] ksr pointer to an array of at least 6 elements
+ */
+void kbcu_lld_read_ksr(uint32_t ksr[SK32_KBCU_KSRS]) {
+ for (uint8_t i = 0U; i < SK32_KBCU_KSRS; i++) {
+ ksr[i] = KBCU->KSR[i];
+ }
+}
+
+/**
+ * @brief Returns the key flag register content.
+ *
+ * @return The @p SR key flags (KBCU_SR_KEYF bits)
+ */
+uint32_t kbcu_lld_get_key_flag(void) {
+ return KBCU->SR & KBCU_SR_KEYF;
+}
+
+/**
+ * @brief Programs a single LED row register.
+ * @note This is the CPU driven path, useful while the DMA channel is not
+ * allocated yet or for small/static outputs.
+ *
+ * @param[in] row LED register index (0..7)
+ * @param[in] r red duty
+ * @param[in] g green duty
+ * @param[in] b blue duty
+ * @param[in] mask brightness mask field
+ */
+void kbcu_lld_led_set(uint8_t row, uint8_t r, uint8_t g, uint8_t b,
+ uint8_t mask) {
+ if (row < SK32_KBCU_LED_ROWS) {
+ KBCU->LEDR[row] = (uint32_t)r | ((uint32_t)g << 8) |
+ ((uint32_t)b << 16) | ((uint32_t)mask << 24);
+ }
+}
+
+/**
+ * @brief Configures the KBCU DMA burst base and length.
+ * @note Mirrors the vendor KBCU_DMAConfig() : the base is a word index
+ * into the register window (see @p kbcu_dmabase_t), the burst
+ * length must be between 0x00 and 0x1F.
+ *
+ * @param[in] base DMA base address (@p kbcu_dmabase_t)
+ * @param[in] burst_length DMA burst length (0x00..0x1F)
+ */
+void kbcu_lld_dma_config(kbcu_dmabase_t base, uint16_t burst_length) {
+ KBCU->DCR = (uint32_t)base | ((uint32_t)burst_length << 8);
+}
+
+/**
+ * @brief Starts a memory to peripheral DMA transfer of an RGB frame into
+ * the KBCU (LED) registers.
+ * @details On the first call the DMA1 channel is allocated, the SYSCFG
+ * request line is routed to the KBCU and the peripheral clock of the
+ * output path is enabled. A burst is then programmed by writing the
+ * DCR register (base + burst length) and the channel is armed. The
+ * application updates the framebuffer in RAM and calls this function
+ * again to reload the frame; the DMA runs in single-shot mode.
+ *
+ * @param[in] base DMA base address (@p kbcu_dmabase_t)
+ * @param[in] burst_length DMA burst length (0x00..0x1F)
+ * @param[in] framebuf pointer to the RGB frame buffer in RAM
+ * @param[in] size number of bytes to transfer
+ */
+void kbcu_lld_led_dma_start(kbcu_dmabase_t base, uint16_t burst_length,
+ const uint8_t *framebuf, size_t size) {
+ osalDbgCheck(framebuf != NULL);
+ osalDbgCheck(size > 0U);
+
+ if (kbcu_stream == NULL) {
+ /* Enabling the SYSCFG clock (needed for the request remapping) and the
+ KBCU APB1 clock.*/
+ rccEnableAPB2(RCC_APB2ENR_SYSCFGEN, true);
+ if (!kbcu_started) {
+ rccEnableKBCU(true);
+ }
+
+ kbcu_remap(SK32_KBCU_DMA_REQ_CODE);
+
+ kbcu_stream = dmaStreamAlloc(SK32_KBCU_DMA_STREAM,
+ SK32_KBCU_DMA_PRIORITY, NULL, NULL);
+ osalDbgAssert(kbcu_stream != NULL, "KBCU DMA channel busy");
+ }
+
+ kbcu_lld_dma_config(base, burst_length);
+
+ dmaStreamSetPeripheral(kbcu_stream, &KBCU->DMAR);
+ dmaStreamSetMemory0(kbcu_stream, framebuf);
+ dmaStreamSetTransactionSize(kbcu_stream, (uint32_t)size);
+ dmaStreamSetMode(kbcu_stream, SK32_KBCU_DMA_MODE);
+
+ /* Enabling the KBCU DMA burst (BEN) then arming the channel.*/
+ KBCU->CR0 |= KBCU_CR0_BEN;
+ dmaStreamEnable(kbcu_stream);
+}
+
+/**
+ * @brief Stops and releases the KBCU RGB DMA channel.
+ */
+void kbcu_lld_led_dma_stop(void) {
+ if (kbcu_stream != NULL) {
+ dmaStreamDisable(kbcu_stream);
+ dmaStreamFree(kbcu_stream);
+ kbcu_stream = NULL;
+ }
+ KBCU->CR0 &= ~KBCU_CR0_BEN;
+}
+
+#endif /* HAL_USE_KBCU */
+
+/** @} */
\ No newline at end of file
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_kbcu_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_kbcu_lld.h
new file mode 100644
index 0000000000..1531340495
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_kbcu_lld.h
@@ -0,0 +1,293 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_kbcu_lld.h
+ * @brief SK32F0xx KBCU subsystem low level driver header.
+ * @details The KBCU (Keyboard Control Unit) is a 3Think peripheral used for
+ * scanning keyboard matrixes and for driving RGB LED columns. This
+ * driver supports the independent scan mode where the RGB scanning
+ * and the KEY scanning run at the same level and the key statuses
+ * are read on dedicated KEY inputs through the shared KSR registers.
+ * @note The RGB (LED) stream is pushed through a DMA1 channel in memory to
+ * peripheral mode targeting the @p KBCU->DMAR register, so this
+ * driver allocates DMA channels and requires @p SK32_DMA_REQUIRED
+ * (defined in hal_lld.h whenever @p HAL_USE_KBCU is enabled).
+ *
+ * @addtogroup KBCU
+ * @{
+ */
+
+#ifndef HAL_KBCU_LLD_H
+#define HAL_KBCU_LLD_H
+
+#if (HAL_USE_KBCU == TRUE) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief Mask of the CR0 bits owned by this driver.
+ * @details The @p EN, @p BEN, column count, key/column effective levels,
+ * column mask and column cycle value fields are managed by the
+ * driver so that unrelated bits are never overwritten.
+ */
+#define SK32_KBCU_CR0_OWNED_MASK (KBCU_CR0_EN | \
+ KBCU_CR0_BEN | \
+ KBCU_CR0_CS | \
+ KBCU_CR0_KIP | \
+ KBCU_CR0_COP | \
+ KBCU_CR0_CMASK| \
+ KBCU_CR0_CCR)
+
+/**
+ * @brief Mask used to clear the managed CR1 fields before programming.
+ * @note Equal to the vendor CR1_CLEAR_Mask.
+ */
+#define SK32_KBCU_CR1_OWNED_MASK 0x07FFFFFFU
+
+/**
+ * @brief Mask used to clear the managed IER fields before programming.
+ * @note Equal to the vendor IER_CLEAR_Mask.
+ */
+#define SK32_KBCU_IER_OWNED_MASK KBCU_IER_FRAME | KBCU_IER_KEYIE | \
+ KBCU_IER_COLIE | KBCU_IER_FRAMEIE
+
+/**
+ * @brief Number of KSR (key status) registers.
+ * @details The 6 KSR registers cover 4 columns each (8 row bits per column),
+ * i.e. up to 24 columns.
+ */
+#define SK32_KBCU_KSRS 6U
+
+/**
+ * @brief DMA1 channel identifier used by the KBCU RGB (LED) burst.
+ */
+#define SK32_KBCU_DMA_STREAM SK32_DMA_STREAM_ID(1, 4)
+
+/**
+ * @brief SYSCFG CFGR3 DMA request code of the KBCU.
+ * @note The request code is a 6 bit value placed in the low 6 bits of the
+ * per-channel slot of CFGR3, see the vendor
+ * SYSCFG_DMAChannelConfig() (SYSCFG_DMARemap_KBCU = 0x2E).
+ */
+#define SK32_KBCU_DMA_REQ_CODE 0x2EU
+
+/**
+ * @brief Mask of the request code bits inside a CFGR3 channel slot.
+ */
+#define SK32_KBCU_DMA_REQ_MASK 0x3FU
+
+/**
+ * @brief Position of the DMA1 channel 4 request slot in CFGR3.
+ * @note The request code of a channel occupies the low 6 bits of an 8 bits
+ * wide slot located at ((channel - 1) * 8), channel 4 uses bits
+ * 24..29 (the same layout used by the SLED driver).
+ */
+#define SK32_KBCU_DMA_SLOT_SHIFT (((4) - 1U) * 8U)
+
+/**
+ * @brief DMA priority of the KBCU channel.
+ */
+#define SK32_KBCU_DMA_PRIORITY 3U
+
+/**
+ * @brief MCCR mode of the KBCU DMA transfers (memory to peripheral,
+ * memory pointer increment, no peripheral increment, priority).
+ */
+#define SK32_KBCU_DMA_MODE (SK32_DMA_CR_DIR_M2P | \
+ SK32_DMA_CR_MINC | \
+ SK32_DMA_CR_PSIZE_BYTE| \
+ SK32_DMA_CR_MSIZE_BYTE| \
+ SK32_DMA_CR_PL(SK32_KBCU_DMA_PRIORITY))
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @brief KBCU interrupt priority level.
+ * @note May be overridden in mcuconf.h.
+ */
+#if !defined(SK32_IRQ_KBCU_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_IRQ_KBCU_PRIORITY 3
+#endif
+
+#if !OSAL_IRQ_IS_VALID_PRIORITY(SK32_IRQ_KBCU_PRIORITY) && !defined(__DOXYGEN__)
+#error "invalid priority level for the SK32 KBCU interrupt"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief KBCU key effective level.
+ */
+typedef enum {
+ KBCU_KEY_LEVEL_LOW = 0x00, /**< @brief Key active on low. */
+ KBCU_KEY_LEVEL_HIGH = 0x01 /**< @brief Key active on high. */
+} kbcu_key_level_t;
+
+/**
+ * @brief KBCU columns effective level.
+ */
+typedef enum {
+ KBCU_COLUMN_LEVEL_LOW = 0x00, /**< @brief Columns active on low. */
+ KBCU_COLUMN_LEVEL_HIGH = 0x01 /**< @brief Columns active on high. */
+} kbcu_columns_level_t;
+
+/**
+ * @brief KBCU dead time (debounce) setting.
+ */
+typedef enum {
+ KBCU_DEADTIME_0 = 0x00, /**< @brief No dead time. */
+ KBCU_DEADTIME_1 = 0x01, /**< @brief Dead time 1. */
+ KBCU_DEADTIME_2 = 0x02, /**< @brief Dead time 2. */
+ KBCU_DEADTIME_3 = 0x03, /**< @brief Dead time 3. */
+ KBCU_DEADTIME_4 = 0x04, /**< @brief Dead time 4. */
+ KBCU_DEADTIME_5 = 0x05, /**< @brief Dead time 5. */
+ KBCU_DEADTIME_6 = 0x06, /**< @brief Dead time 6. */
+ KBCU_DEADTIME_7 = 0x07 /**< @brief Dead time 7. */
+} kbcu_deadtime_t;
+
+/**
+ * @brief KBCU interrupt sources.
+ */
+typedef enum {
+ KBCU_IT_FRAME = KBCU_IER_FRAMEIE, /**< @brief Frame interrupt enable. */
+ KBCU_IT_COLUMN = KBCU_IER_COLIE, /**< @brief Column interrupt enable. */
+ KBCU_IT_KEY = KBCU_IER_KEYIE /**< @brief Key interrupt enable. */
+} kbcu_it_t;
+
+/**
+ * @brief KBCU DMA base address (word index into the register window).
+ * @note The base selects the register where a DMA burst starts to write,
+ * the value is the word offset of the register (LEDR0 = 0x0B -> byte
+ * 0x2C). These match the vendor KBCU_DMABase_* constants (the LEDR
+ * registers are the only meaningful bases for RGB output).
+ */
+typedef enum {
+ KBCU_DMBASE_LEDR0 = 0x0BU, /**< @brief Base: LED register 0. */
+ KBCU_DMBASE_LEDR1 = 0x0CU, /**< @brief Base: LED register 1. */
+ KBCU_DMBASE_LEDR2 = 0x0DU, /**< @brief Base: LED register 2. */
+ KBCU_DMBASE_LEDR3 = 0x0EU, /**< @brief Base: LED register 3. */
+ KBCU_DMBASE_LEDR4 = 0x0FU, /**< @brief Base: LED register 4. */
+ KBCU_DMBASE_LEDR5 = 0x10U, /**< @brief Base: LED register 5. */
+ KBCU_DMBASE_LEDR6 = 0x11U, /**< @brief Base: LED register 6. */
+ KBCU_DMBASE_LEDR7 = 0x12U /**< @brief Base: LED register 7. */
+} kbcu_dmabase_t;
+
+/**
+ * @brief KBCU key status callback.
+ * @details Invoked from the KBCU ISR whenever a key status change is
+ * detected (@p KBCU_SR_KEYF set). The pointer references the
+ * driver internal KSR snapshot which stays valid until the next
+ * interrupt.
+ *
+ * @param[in] ksr pointer to the 6 KSR values snapshot
+ */
+typedef void (*kbcu_key_cb)(const uint32_t *ksr);
+
+/**
+ * @brief KBCU driver configuration.
+ * @details Independent scan mode only (the multiplexing S/M field is kept
+ * cleared). The numeric fields carry logical values: the column
+ * count (15..24) and effective level/dead time selects are encoded
+ * into the CR0/CR1 fields by the driver.
+ */
+typedef struct {
+ /**
+ * @brief Number of columns (15..24).
+ */
+ uint8_t columns_num;
+ /**
+ * @brief Columns cycle value (CCR), must be between 0x00 and 0x7F.
+ */
+ uint8_t columns_cycle_value;
+ /**
+ * @brief Columns cycle count (ARR), must be between 0x01 and 0xFF.
+ */
+ uint8_t columns_cycle_count;
+ /**
+ * @brief Clock division factor (PSC), must be between 0x00 and 0xFF.
+ */
+ uint8_t clock_division;
+ /**
+ * @brief Key effective level.
+ */
+ kbcu_key_level_t key_effective_level;
+ /**
+ * @brief Columns effective level.
+ */
+ kbcu_columns_level_t columns_effective_level;
+ /**
+ * @brief Dead time (debounce).
+ */
+ kbcu_deadtime_t dead_time;
+ /**
+ * @brief Flicker length (FC), must be between 0x00 and 0xFF.
+ */
+ uint8_t flicker_length;
+ /**
+ * @brief Column mask (CMASK), must be between 0x00 and 0x7F.
+ */
+ uint8_t column_mask;
+} KBCUConfig;
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void kbcu_lld_init(void);
+ void kbcu_lld_start(const KBCUConfig *config);
+ void kbcu_lld_stop(void);
+
+ void kbcu_lld_enable(void);
+ void kbcu_lld_disable(void);
+
+ void kbcu_lld_itconfig(kbcu_it_t it, uint8_t frame_count);
+ void kbcu_lld_set_callback(kbcu_key_cb cb);
+
+ uint8_t kbcu_lld_get_column(uint8_t column);
+ bool kbcu_lld_get_key(uint8_t column, uint8_t row);
+ void kbcu_lld_read_ksr(uint32_t ksr[SK32_KBCU_KSRS]);
+ uint32_t kbcu_lld_get_key_flag(void);
+
+ void kbcu_lld_led_set(uint8_t row, uint8_t r, uint8_t g, uint8_t b,
+ uint8_t mask);
+ void kbcu_lld_dma_config(kbcu_dmabase_t base, uint16_t burst_length);
+ void kbcu_lld_led_dma_start(kbcu_dmabase_t base, uint16_t burst_length,
+ const uint8_t *framebuf, size_t size);
+ void kbcu_lld_led_dma_stop(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_KBCU */
+
+#endif /* HAL_KBCU_LLD_H */
+
+/** @} */
\ No newline at end of file
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_lld.c
new file mode 100644
index 0000000000..4b1a4ce74a
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_lld.c
@@ -0,0 +1,156 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_lld.c
+ * @brief SK32F0xx HAL subsystem low level driver source.
+ *
+ * @addtogroup HAL
+ * @{
+ */
+
+#include "hal.h"
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/* GPIO enable bits are located in bits 17..22 of the AHB clock/reset
+ registers (the vendor device header contains wrong values for the GPIOD,
+ GPIOE, GPIOF reset macros, the enable macros are correct and are used
+ here).*/
+#define SK32_GPIO_CLK_MASK (RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOBEN | \
+ RCC_AHBENR_GPIOCEN | RCC_AHBENR_GPIODEN | \
+ RCC_AHBENR_GPIOEEN | RCC_AHBENR_GPIOFEN)
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief CMSIS system core clock variable.
+ */
+uint32_t SystemCoreClock = SK32_HCLK;
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level HAL driver initialization.
+ *
+ * @notapi
+ */
+void hal_lld_init(void) {
+
+ /* Reset of all the peripherals. GPIOs are not reset because the board
+ early initialization code may have already configured them. SYSCFG
+ is not reset either: Cortex-M0 has no VTOR, so when the application
+ is launched behind the sk32duino bootloader its exception vectors are
+ fetched from the SRAM copy selected by SYSCFG CFGR1 MEM_MODE = 0b11.
+ Resetting SYSCFG here would silently clear that remap and the first
+ interrupt (the SysTick started by chSysInit()) would vector into the
+ bootloader handlers again. The device header spells this bit
+ RCC_APB2ENR_SYSCFGRST although it lives in the APB2RSTR register.*/
+ RCC->AHBRSTR = (uint32_t)~SK32_GPIO_CLK_MASK;
+ RCC->AHBRSTR = (uint32_t)0;
+ RCC->APB2RSTR = (uint32_t)~RCC_APB2ENR_SYSCFGRST;
+ RCC->APB2RSTR = (uint32_t)0;
+ RCC->APB1RSTR = (uint32_t)0xFFFFFFFF;
+ RCC->APB1RSTR = (uint32_t)0;
+
+ /* DMA subsystem initialization.*/
+#if defined(SK32_DMA_REQUIRED)
+ dmaInit();
+#endif
+}
+
+/**
+ * @brief SK32F0xx clocks and PLL initialization.
+ * @note All the involved constants come from the file @p mcuconf.h.
+ * @note This function should be invoked just after the system reset from
+ * the board early initialization code (__early_init).
+ *
+ * @special
+ */
+void sk32_clock_init(void) {
+
+#if !SK32_NO_INIT
+ /* HSI setup, it enforces the reset situation in order to handle possible
+ problems with JTAG probes and re-initializations.*/
+ RCC->CR |= RCC_CR_HSION; /* Make sure HSI is ON. */
+ while ((RCC->CR & RCC_CR_HSIRDY) == 0)
+ ; /* Wait until HSI is stable. */
+
+ /* HSI is selected as new source without touching the other fields in
+ CFGR. Clearing the register has to be postponed after HSI is the
+ new source.*/
+ RCC->CFGR &= ~RCC_CFGR_SW; /* Reset SW, selecting HSI. */
+ while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_HSI)
+ ; /* Wait until HSI is selected. */
+
+ /* Registers finally cleared to reset values.*/
+ RCC->CR &= RCC_CR_HSITRIM | RCC_CR_HSION; /* CR reset value. */
+ RCC->CFGR = 0; /* CFGR reset value. */
+ RCC->CFGR2 = 0; /* CFGR2 reset value. */
+ RCC->CFGR3 = 0; /* CFGR3 reset value. */
+ RCC->CR2 = 0; /* CR2 reset value. */
+
+#if SK32_ACTIVATE_PLL
+ /* PLL configuration, the clock source is the HSI divided by PREDIV1,
+ the multiplication factor is SK32_PLLMUL_VALUE. The AHB/APB prescalers
+ are left at their DIV1 reset value.*/
+ RCC->CFGR2 = SK32_PREDIV1; /* PREDIV1 value. */
+ RCC->CFGR = SK32_PLLSRC_HSI_PREDIV | SK32_PLLMUL;
+
+ /* PLL activation.*/
+ RCC->CR |= RCC_CR_PLLON;
+ while ((RCC->CR & RCC_CR_PLLRDY) == 0)
+ ; /* Wait until PLL is stable. */
+#endif /* SK32_ACTIVATE_PLL */
+
+ /* Flash setup and final clock selection. 2 wait states are required at
+ 72MHz (the vendor keeps 2 wait states also at 48MHz).*/
+ FLASH->ACR = SK32_FLASHBITS;
+
+ /* Switching to the configured clock source if it is different from HSI.*/
+#if (SK32_SW != SK32_SW_HSI)
+ RCC->CFGR |= SK32_SW;
+ while ((RCC->CFGR & RCC_CFGR_SWS) != (SK32_SW << 2))
+ ; /* Wait selection complete. */
+#endif /* (SK32_SW != SK32_SW_HSI) */
+
+ /* SystemCoreClock is updated with the real HCLK frequency. It runs before
+ the initialization of the .data segment, the same value is assigned by
+ the variable initializer.*/
+ SystemCoreClock = SK32_HCLK;
+#endif /* !SK32_NO_INIT */
+}
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_lld.h
new file mode 100644
index 0000000000..65e7e9950d
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_lld.h
@@ -0,0 +1,332 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_lld.h
+ * @brief SK32F0xx HAL subsystem low level driver header.
+ * @details This file is the platform specific part of the HAL for the
+ * 3Think SK32F0xx family (SK32F072 / SK32F077).
+ *
+ * @addtogroup HAL
+ * @{
+ */
+
+#ifndef HAL_LLD_H
+#define HAL_LLD_H
+
+/*
+ * The vendor device header provides all the register definitions used by
+ * this port. It is self contained (it does not require the SPL peripheral
+ * headers) and is shared by the whole HAL.
+ */
+#include "sk32f0xx.h"
+
+/*
+ * Registry definitions.
+ */
+#include "sk32_registry.h"
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @name Platform identification macros
+ * @{
+ */
+#if defined(SK32F072xB) || defined(__DOXYGEN__)
+#define PLATFORM_NAME "SK32F072xB devices"
+#elif defined(SK32F077xB)
+#define PLATFORM_NAME "SK32F077xB devices"
+#else
+#error "SK32F0xx device unsupported or not specified"
+#endif
+/** @} */
+
+/**
+ * @name Internal clock sources
+ * @{
+ */
+#define SK32_HSI_CLK SK32_HSICLK /**< High speed internal clock.*/
+#define SK32_PLL48_CLK SK32_PLL48CLK /**< USB dedicated 48MHz clock.*/
+/** @} */
+
+/**
+ * @name RCC_CFGR register bits definitions
+ * @{
+ */
+#define SK32_SW_HSI (0 << 0) /**< SYSCLK source is HSI. */
+#define SK32_SW_PLL (2 << 0) /**< SYSCLK source is PLL. */
+
+#define SK32_HPRE_DIV1 (0 << 4) /**< SYSCLK divided by 1. */
+
+#define SK32_PPRE_DIV1 (0 << 8) /**< HCLK divided by 1. */
+
+#define SK32_PLLSRC_HSI_PREDIV (1 << 15) /**< PLL clock source is
+ HSI/PREDIV1. */
+/** @} */
+
+/**
+ * @name RCC_CFGR2 register bits definitions
+ * @{
+ */
+#define SK32_PREDIV1_DIV1 (0 << 0) /**< PLL input divided by 1. */
+/** @} */
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief Disables the RCC initialization in the HAL.
+ */
+#if !defined(SK32_NO_INIT) || defined(__DOXYGEN__)
+#define SK32_NO_INIT FALSE
+#endif
+
+/**
+ * @brief Main clock source selection.
+ * @note The default is a 72MHz system clock from the 8MHz HSI using the
+ * main PLL (HSI / PREDIV1(=1) * PLLMUL(=9)), matching the vendor
+ * clock configuration tool output.
+ */
+#if !defined(SK32_SW) || defined(__DOXYGEN__)
+#define SK32_SW SK32_SW_PLL
+#endif
+
+/**
+ * @brief PREDIV1 divider value (1..16) applied to the HSI before the PLL.
+ * @note The default is 1 (8MHz into the PLL).
+ */
+#if !defined(SK32_PREDIV_VALUE) || defined(__DOXYGEN__)
+#define SK32_PREDIV_VALUE 1
+#endif
+
+/**
+ * @brief PLL multiplier value (2..16).
+ * @note The default is 9 (72MHz output).
+ */
+#if !defined(SK32_PLLMUL_VALUE) || defined(__DOXYGEN__)
+#define SK32_PLLMUL_VALUE 9
+#endif
+
+/**
+ * @brief AHB prescaler value.
+ * @note Only DIV1 is currently supported by this port.
+ */
+#if !defined(SK32_HPRE) || defined(__DOXYGEN__)
+#define SK32_HPRE SK32_HPRE_DIV1
+#endif
+
+/**
+ * @brief APB prescaler value.
+ * @note Only DIV1 is currently supported by this port.
+ */
+#if !defined(SK32_PPRE) || defined(__DOXYGEN__)
+#define SK32_PPRE SK32_PPRE_DIV1
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/*
+ * Configuration-related checks.
+ */
+#if !defined(SK32F0xx_MCUCONF)
+#error "Using a wrong mcuconf.h file, SK32F0xx_MCUCONF not defined"
+#endif
+
+/*
+ * Clock source checks.
+ */
+#if (SK32_SW != SK32_SW_HSI) && (SK32_SW != SK32_SW_PLL)
+#error "invalid SK32_SW value specified"
+#endif
+
+/*
+ * PLL multiplier checks.
+ */
+#if ((SK32_PLLMUL_VALUE >= 2) && (SK32_PLLMUL_VALUE <= 16)) || \
+ defined(__DOXYGEN__)
+#define SK32_PLLMUL ((SK32_PLLMUL_VALUE - 2) << 18)
+#else
+#error "invalid SK32_PLLMUL_VALUE value specified (2..16)"
+#endif
+
+/*
+ * PREDIV1 checks.
+ */
+#if ((SK32_PREDIV_VALUE >= 1) && (SK32_PREDIV_VALUE <= 16)) || \
+ defined(__DOXYGEN__)
+#define SK32_PREDIV1 (SK32_PREDIV_VALUE - 1)
+#else
+#error "invalid SK32_PREDIV_VALUE value specified (1..16)"
+#endif
+
+/*
+ * Prescaler checks (DIV1 only for now).
+ */
+#if (SK32_HPRE != SK32_HPRE_DIV1) && !defined(__DOXYGEN__)
+#error "invalid SK32_HPRE value specified, only SK32_HPRE_DIV1 is supported"
+#endif
+
+#if (SK32_PPRE != SK32_PPRE_DIV1) && !defined(__DOXYGEN__)
+#error "invalid SK32_PPRE value specified, only SK32_PPRE_DIV1 is supported"
+#endif
+
+/**
+ * @brief PLL activation flag.
+ */
+#if (SK32_SW == SK32_SW_PLL) || defined(__DOXYGEN__)
+#define SK32_ACTIVATE_PLL TRUE
+#else
+#define SK32_ACTIVATE_PLL FALSE
+#endif
+
+/**
+ * @brief PLL input clock frequency (HSI divided by PREDIV1).
+ */
+#define SK32_PLLCLKIN (SK32_HSICLK / SK32_PREDIV_VALUE)
+
+/**
+ * @brief PLL output clock frequency.
+ */
+#define SK32_PLLCLKOUT (SK32_PLLCLKIN * SK32_PLLMUL_VALUE)
+
+/* PLL output frequency range check.*/
+#if (SK32_PLLCLKOUT < SK32_PLLCLKIN) || (SK32_PLLCLKOUT > SK32_SYSCLK_MAX)
+#error "SK32_PLLCLKOUT outside acceptable range (up to SK32_SYSCLK_MAX)"
+#endif
+
+/**
+ * @brief System clock source frequency.
+ */
+#if (SK32_SW == SK32_SW_PLL) || defined(__DOXYGEN__)
+#define SK32_SYSCLK SK32_PLLCLKOUT
+#elif SK32_SW == SK32_SW_HSI
+#define SK32_SYSCLK SK32_HSICLK
+#else
+#error "invalid SK32_SW value specified"
+#endif
+
+/**
+ * @brief AHB frequency.
+ */
+#if (SK32_HPRE == SK32_HPRE_DIV1) || defined(__DOXYGEN__)
+#define SK32_HCLK (SK32_SYSCLK / 1)
+#else
+#error "invalid SK32_HPRE value specified"
+#endif
+
+/**
+ * @brief APB frequency.
+ */
+#if (SK32_PPRE == SK32_PPRE_DIV1) || defined(__DOXYGEN__)
+#define SK32_PCLK1 (SK32_HCLK / 1)
+#define SK32_PCLK2 (SK32_HCLK / 1)
+#else
+#error "invalid SK32_PPRE value specified"
+#endif
+
+/**
+ * @brief Timers clock.
+ */
+#define SK32_TIMCLK1 SK32_PCLK1
+#define SK32_TIMCLK2 SK32_PCLK2
+
+/**
+ * @brief Flash settings.
+ * @note 2 wait states plus prefetch buffer enable, as programmed by the
+ * vendor clock configuration.
+ */
+#define SK32_FLASHBITS (FLASH_ACR_PRFTBE | \
+ SK32_FLASH_WAIT_STATES)
+
+/**
+ * @brief USB frequency.
+ * @note The USB controller is clocked by the dedicated PLL48 (enabled by
+ * the USB low level driver), not by the system clock.
+ */
+#define SK32_USBCLK SK32_PLL48CLK
+
+/*
+ * DMA services activation.
+ *
+ * The SK32 DMA helper code (sk32_dma.c) and the DMA1 initialization in
+ * hal_lld.c are self-gated on SK32_DMA_REQUIRED: any low level driver of
+ * this platform that allocates DMA1 channels must cause the macro to be
+ * defined. The SLED (hal_sled_lld.c) and KBCU (hal_kbcu_lld.c) drivers are
+ * the native DMA users, so the macro is tied to the HAL_USE_SLED and
+ * HAL_USE_KBCU switches.
+ */
+#if (defined(HAL_USE_SLED) && (HAL_USE_SLED == TRUE)) || \
+ (defined(HAL_USE_KBCU) && (HAL_USE_KBCU == TRUE))
+#define SK32_DMA_REQUIRED
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Returns the frequency of a clock point in Hz.
+ * @note Static implementation.
+ *
+ * @param[in] clkpt clock point to be returned
+ * @return The clock point frequency in Hz or zero if the
+ * frequency is unknown.
+ *
+ * @notapi
+ */
+#define hal_lld_get_clock_point(clkpt) 0U
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+/* Various helpers.*/
+#include "nvic.h"
+
+/*
+ * Native SK32F0xx helper headers.
+ */
+#include "sk32_isr.h"
+#include "sk32_rcc.h"
+#include "sk32_dma.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void hal_lld_init(void);
+ void sk32_clock_init(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_pal_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_pal_lld.c
new file mode 100644
index 0000000000..f7f427e70f
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_pal_lld.c
@@ -0,0 +1,250 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_pal_lld.c
+ * @brief SK32F0xx PAL low level driver code.
+ * @details This driver is written directly against the vendor CMSIS header
+ * (@p sk32f0xx.h). The GPIO registers are the STM32F0-style
+ * MODER/OTYPER/OSPEEDR/PUPDR/IDR/ODR/BSRR/AFR set (the BSRR
+ * register is written with a single 32 bits access, the alternate
+ * function registers are an AFR[2] array) and the EXTI line
+ * multiplexing is programmed through the SYSCFG EXTICR registers
+ * (4 bits per line).
+ *
+ * @addtogroup PAL
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_PAL || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE) || defined(__DOXYGEN__)
+/**
+ * @brief Event records for the 16 GPIO EXTI channels.
+ */
+palevent_t _pal_events[16];
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief PAL driver initialization.
+ *
+ * @notapi
+ */
+void _pal_lld_init(void) {
+
+#if PAL_USE_CALLBACKS || PAL_USE_WAIT || defined(__DOXYGEN__)
+ unsigned i;
+
+ for (i = 0; i < 16; i++) {
+ _pal_init_event(i);
+ }
+#endif
+}
+
+/**
+ * @brief Pads mode setup.
+ * @details This function programs a pads group belonging to the same port
+ * with the specified mode.
+ * @note @p PAL_MODE_UNCONNECTED is implemented as push pull at minimum
+ * speed.
+ *
+ * @param[in] port the port identifier
+ * @param[in] mask the group mask
+ * @param[in] mode the mode
+ *
+ * @notapi
+ */
+void _pal_lld_setgroupmode(ioportid_t port,
+ ioportmask_t mask,
+ iomode_t mode) {
+
+ uint32_t moder = (mode & PAL_SK32_MODE_MASK) >> 0;
+ uint32_t otyper = (mode & PAL_SK32_OTYPE_MASK) >> 2;
+ uint32_t ospeedr = (mode & PAL_SK32_OSPEED_MASK) >> 3;
+ uint32_t pupdr = (mode & PAL_SK32_PUPDR_MASK) >> 5;
+ uint32_t altr = (mode & PAL_SK32_ALTERNATE_MASK) >> 7;
+ uint32_t bit = 0;
+ while (true) {
+ if ((mask & 1) != 0) {
+ uint32_t altrmask, m1, m2, m4;
+
+ altrmask = altr << ((bit & 7) * 4);
+ m1 = 1 << bit;
+ m2 = 3 << (bit * 2);
+ m4 = 15 << ((bit & 7) * 4);
+ port->OTYPER = (port->OTYPER & ~m1) | otyper;
+ port->OSPEEDR = (port->OSPEEDR & ~m2) | ospeedr;
+ port->PUPDR = (port->PUPDR & ~m2) | pupdr;
+ if ((mode & PAL_SK32_MODE_MASK) == PAL_SK32_MODE_ALTERNATE) {
+ /* If going in alternate mode then the alternate number is set
+ before switching mode in order to avoid glitches.*/
+ if (bit < 8)
+ port->AFR[0] = (port->AFR[0] & ~m4) | altrmask;
+ else
+ port->AFR[1] = (port->AFR[1] & ~m4) | altrmask;
+ port->MODER = (port->MODER & ~m2) | moder;
+ }
+ else {
+ /* If going into a non-alternate mode then the mode is switched
+ before setting the alternate mode in order to avoid glitches.*/
+ port->MODER = (port->MODER & ~m2) | moder;
+ if (bit < 8)
+ port->AFR[0] = (port->AFR[0] & ~m4) | altrmask;
+ else
+ port->AFR[1] = (port->AFR[1] & ~m4) | altrmask;
+ }
+ }
+ mask >>= 1;
+ if (!mask)
+ return;
+ otyper <<= 1;
+ ospeedr <<= 2;
+ pupdr <<= 2;
+ moder <<= 2;
+ bit++;
+ }
+}
+
+#if PAL_USE_CALLBACKS || PAL_USE_WAIT || defined(__DOXYGEN__)
+/**
+ * @brief Pad event enable.
+ * @note Programming an unknown or unsupported mode is silently ignored.
+ *
+ * @param[in] port port identifier
+ * @param[in] pad pad number within the port
+ * @param[in] mode pad event mode
+ *
+ * @notapi
+ */
+void _pal_lld_enablepadevent(ioportid_t port,
+ iopadid_t pad,
+ ioeventmode_t mode) {
+
+ uint32_t padmask, cridx, croff, crmask, portidx;
+
+ /* Mask of the pad.*/
+ padmask = 1U << (uint32_t)pad;
+
+ /* Multiple channel setting of the same channel not allowed, first disable
+ it. This is done because on SK32 the same channel cannot be mapped on
+ multiple ports.*/
+ osalDbgAssert(((EXTI->RTSR & padmask) == 0U) &&
+ ((EXTI->FTSR & padmask) == 0U), "channel already in use");
+
+ /* Port index is obtained assuming that GPIO ports are placed at regular
+ 0x400 intervals in memory space. So far this is true for all devices.*/
+ portidx = (((uint32_t)port - (uint32_t)GPIOA) >> 10U) & 0xFU;
+
+ /* Index and mask of the EXTICR register to be used, on this family the
+ line to port mapping is done in the SYSCFG block, 4 bits per line.*/
+ cridx = (uint32_t)pad >> 2U;
+ croff = ((uint32_t)pad & 3U) * 4U;
+ crmask = ~(0xFU << croff);
+ SYSCFG->EXTICR[cridx] = (SYSCFG->EXTICR[cridx] & crmask) | (portidx << croff);
+
+ /* Programming edge registers.*/
+ if (mode & PAL_EVENT_MODE_RISING_EDGE)
+ EXTI->RTSR |= padmask;
+ else
+ EXTI->RTSR &= ~padmask;
+ if (mode & PAL_EVENT_MODE_FALLING_EDGE)
+ EXTI->FTSR |= padmask;
+ else
+ EXTI->FTSR &= ~padmask;
+
+ /* Programming interrupt and event registers.*/
+ EXTI->IMR |= padmask;
+ EXTI->EMR &= ~padmask;
+}
+
+/**
+ * @brief Pad event disable.
+ * @details This function disables previously programmed event callbacks.
+ *
+ * @param[in] port port identifier
+ * @param[in] pad pad number within the port
+ *
+ * @notapi
+ */
+void _pal_lld_disablepadevent(ioportid_t port, iopadid_t pad) {
+ uint32_t padmask, rtsr, ftsr;
+
+ rtsr = EXTI->RTSR;
+ ftsr = EXTI->FTSR;
+
+ /* Mask of the pad.*/
+ padmask = 1U << (uint32_t)pad;
+
+ /* If either RTSR or FTSR is enabled then the channel is in use.*/
+ if (((rtsr | ftsr) & padmask) != 0U) {
+ uint32_t cridx, croff, crport, portidx;
+
+ /* Port index is obtained assuming that GPIO ports are placed at regular
+ 0x400 intervals in memory space. So far this is true for all devices.*/
+ portidx = (((uint32_t)port - (uint32_t)GPIOA) >> 10U) & 0xFU;
+
+ /* Index and mask of the EXTICR register to be used.*/
+ cridx = (uint32_t)pad >> 2U;
+ croff = ((uint32_t)pad & 3U) * 4U;
+ crport = (SYSCFG->EXTICR[cridx] >> croff) & 0xFU;
+
+ osalDbgAssert(crport == portidx, "channel mapped on different port");
+
+ /* Disabling channel.*/
+ EXTI->IMR &= ~padmask;
+ EXTI->EMR &= ~padmask;
+ EXTI->RTSR = rtsr & ~padmask;
+ EXTI->FTSR = ftsr & ~padmask;
+ EXTI->PR = padmask;
+
+#if PAL_USE_CALLBACKS || PAL_USE_WAIT
+ /* Callback cleared and/or thread reset.*/
+ _pal_clear_event(pad);
+#endif
+ }
+}
+#endif /* PAL_USE_CALLBACKS || PAL_USE_WAIT */
+
+#endif /* HAL_USE_PAL */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_pal_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_pal_lld.h
new file mode 100644
index 0000000000..8ec829c741
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_pal_lld.h
@@ -0,0 +1,478 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_pal_lld.h
+ * @brief SK32F0xx PAL low level driver header.
+ * @details The SK32F0xx GPIO units are register-compatible with the STM32F0
+ * family (MODER/OTYPER/OSPEEDR/PUPDR/IDR/ODR/BSRR/AFR register set,
+ * the EXTI line multiplexing is done in the SYSCFG EXTICR registers).
+ * The driver programs directly the registers declared in the vendor
+ * CMSIS header (@p sk32f0xx.h, @p GPIO_TypeDef), no STM32 port code
+ * is involved.
+ *
+ * @addtogroup PAL
+ * @{
+ */
+
+#ifndef HAL_PAL_LLD_H
+#define HAL_PAL_LLD_H
+
+#if HAL_USE_PAL || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Unsupported modes and specific modes */
+/*===========================================================================*/
+
+/* Specifies palInit() without parameter, required until all platforms will
+ be updated to the new style.*/
+#define PAL_NEW_INIT
+
+#undef PAL_MODE_RESET
+#undef PAL_MODE_UNCONNECTED
+#undef PAL_MODE_INPUT
+#undef PAL_MODE_INPUT_PULLUP
+#undef PAL_MODE_INPUT_PULLDOWN
+#undef PAL_MODE_INPUT_ANALOG
+#undef PAL_MODE_OUTPUT_PUSHPULL
+#undef PAL_MODE_OUTPUT_OPENDRAIN
+
+/**
+ * @name SK32-specific I/O mode flags
+ * @{
+ */
+#define PAL_SK32_MODE_MASK (3U << 0U)
+#define PAL_SK32_MODE_INPUT (0U << 0U)
+#define PAL_SK32_MODE_OUTPUT (1U << 0U)
+#define PAL_SK32_MODE_ALTERNATE (2U << 0U)
+#define PAL_SK32_MODE_ANALOG (3U << 0U)
+
+#define PAL_SK32_OTYPE_MASK (1U << 2U)
+#define PAL_SK32_OTYPE_PUSHPULL (0U << 2U)
+#define PAL_SK32_OTYPE_OPENDRAIN (1U << 2U)
+
+#define PAL_SK32_OSPEED_MASK (3U << 3U)
+#define PAL_SK32_OSPEED_LOWEST (0U << 3U)
+#define PAL_SK32_OSPEED_MID (1U << 3U)
+#define PAL_SK32_OSPEED_HIGHEST (3U << 3U)
+
+#define PAL_SK32_PUPDR_MASK (3U << 5U)
+#define PAL_SK32_PUPDR_FLOATING (0U << 5U)
+#define PAL_SK32_PUPDR_PULLUP (1U << 5U)
+#define PAL_SK32_PUPDR_PULLDOWN (2U << 5U)
+
+#define PAL_SK32_ALTERNATE_MASK (15U << 7U)
+#define PAL_SK32_ALTERNATE(n) ((n) << 7U)
+
+/**
+ * @brief Alternate function.
+ *
+ * @param[in] n alternate function selector
+ */
+#define PAL_MODE_ALTERNATE(n) (PAL_SK32_MODE_ALTERNATE | \
+ PAL_SK32_ALTERNATE(n))
+/** @} */
+
+/**
+ * @name Standard I/O mode flags
+ * @{
+ */
+/**
+ * @brief Implemented as input.
+ */
+#define PAL_MODE_RESET PAL_SK32_MODE_INPUT
+
+/**
+ * @brief Implemented as input with pull-up.
+ */
+#define PAL_MODE_UNCONNECTED PAL_MODE_INPUT_PULLUP
+
+/**
+ * @brief Regular input high-Z pad.
+ */
+#define PAL_MODE_INPUT PAL_SK32_MODE_INPUT
+
+/**
+ * @brief Input pad with weak pull up resistor.
+ */
+#define PAL_MODE_INPUT_PULLUP (PAL_SK32_MODE_INPUT | \
+ PAL_SK32_PUPDR_PULLUP)
+
+/**
+ * @brief Input pad with weak pull down resistor.
+ */
+#define PAL_MODE_INPUT_PULLDOWN (PAL_SK32_MODE_INPUT | \
+ PAL_SK32_PUPDR_PULLDOWN)
+
+/**
+ * @brief Analog input mode.
+ */
+#define PAL_MODE_INPUT_ANALOG PAL_SK32_MODE_ANALOG
+
+/**
+ * @brief Push-pull output pad.
+ */
+#define PAL_MODE_OUTPUT_PUSHPULL (PAL_SK32_MODE_OUTPUT | \
+ PAL_SK32_OTYPE_PUSHPULL)
+
+/**
+ * @brief Open-drain output pad.
+ */
+#define PAL_MODE_OUTPUT_OPENDRAIN (PAL_SK32_MODE_OUTPUT | \
+ PAL_SK32_OTYPE_OPENDRAIN)
+/** @} */
+
+/*===========================================================================*/
+/* I/O Ports Types and constants. */
+/*===========================================================================*/
+
+/**
+ * @name Port related definitions
+ * @{
+ */
+/**
+ * @brief Width, in bits, of an I/O port.
+ */
+#define PAL_IOPORTS_WIDTH 16
+
+/**
+ * @brief Whole port mask.
+ * @details This macro specifies all the valid bits into a port.
+ */
+#define PAL_WHOLE_PORT ((ioportmask_t)0xFFFF)
+/** @} */
+
+/**
+ * @name Line handling macros
+ * @{
+ */
+/**
+ * @brief Forms a line identifier.
+ * @details A port/pad pair are encoded into an @p ioline_t type. The encoding
+ * of this type is platform-dependent.
+ * @note In this driver the pad number is encoded in the lower 4 bits of
+ * the GPIO address which are guaranteed to be zero.
+ */
+#define PAL_LINE(port, pad) \
+ ((ioline_t)((uint32_t)(port)) | ((uint32_t)(pad)))
+
+/**
+ * @brief Decodes a port identifier from a line identifier.
+ */
+#define PAL_PORT(line) \
+ ((GPIO_TypeDef *)(((uint32_t)(line)) & 0xFFFFFFF0U))
+
+/**
+ * @brief Decodes a pad identifier from a line identifier.
+ */
+#define PAL_PAD(line) \
+ ((uint32_t)((uint32_t)(line) & 0x0000000FU))
+
+/**
+ * @brief Value identifying an invalid line.
+ */
+#define PAL_NOLINE 0U
+/** @} */
+
+/**
+ * @brief Type of digital I/O port sized unsigned integer.
+ */
+typedef uint32_t ioportmask_t;
+
+/**
+ * @brief Type of digital I/O modes.
+ */
+typedef uint32_t iomode_t;
+
+/**
+ * @brief Type of an I/O line.
+ */
+typedef uint32_t ioline_t;
+
+/**
+ * @brief Type of an event mode.
+ */
+typedef uint32_t ioeventmode_t;
+
+/**
+ * @brief Type of a port Identifier.
+ * @details This type can be a scalar or some kind of pointer, do not make
+ * any assumption about it, use the provided macros when populating
+ * variables of this type.
+ */
+typedef GPIO_TypeDef * ioportid_t;
+
+/**
+ * @brief Type of an pad identifier.
+ */
+typedef uint32_t iopadid_t;
+
+/*===========================================================================*/
+/* I/O Ports Identifiers. */
+/* The ports presence depends on the selected device variant (SK32F072 has */
+/* GPIOE, SK32F077 has GPIOF). */
+/*===========================================================================*/
+
+/**
+ * @brief GPIO port A identifier.
+ */
+#if SK32_HAS_GPIOA || defined(__DOXYGEN__)
+#define IOPORT1 GPIOA
+#endif
+
+/**
+ * @brief GPIO port B identifier.
+ */
+#if SK32_HAS_GPIOB || defined(__DOXYGEN__)
+#define IOPORT2 GPIOB
+#endif
+
+/**
+ * @brief GPIO port C identifier.
+ */
+#if SK32_HAS_GPIOC || defined(__DOXYGEN__)
+#define IOPORT3 GPIOC
+#endif
+
+/**
+ * @brief GPIO port D identifier.
+ */
+#if SK32_HAS_GPIOD || defined(__DOXYGEN__)
+#define IOPORT4 GPIOD
+#endif
+
+/**
+ * @brief GPIO port E identifier.
+ */
+#if SK32_HAS_GPIOE || defined(__DOXYGEN__)
+#define IOPORT5 GPIOE
+#endif
+
+/**
+ * @brief GPIO port F identifier.
+ */
+#if SK32_HAS_GPIOF || defined(__DOXYGEN__)
+#define IOPORT6 GPIOF
+#endif
+
+/*===========================================================================*/
+/* Implementation, some of the following macros could be implemented as */
+/* functions, if so please put them in pal_lld.c. */
+/*===========================================================================*/
+
+/**
+ * @brief GPIO ports subsystem initialization.
+ *
+ * @notapi
+ */
+#define pal_lld_init() _pal_lld_init()
+
+/**
+ * @brief Reads an I/O port.
+ * @details This function is implemented by reading the GPIO IDR register, the
+ * implementation has no side effects.
+ * @note This function is not meant to be invoked directly by the application
+ * code.
+ *
+ * @param[in] port port identifier
+ * @return The port bits.
+ *
+ * @notapi
+ */
+#define pal_lld_readport(port) ((ioportmask_t)((port)->IDR))
+
+/**
+ * @brief Reads the output latch.
+ * @details This function is implemented by reading the GPIO ODR register, the
+ * implementation has no side effects.
+ * @note This function is not meant to be invoked directly by the application
+ * code.
+ *
+ * @param[in] port port identifier
+ * @return The latched logical states.
+ *
+ * @notapi
+ */
+#define pal_lld_readlatch(port) ((ioportmask_t)((port)->ODR))
+
+/**
+ * @brief Writes on a I/O port.
+ * @details This function is implemented by writing the GPIO ODR register, the
+ * implementation has no side effects.
+ *
+ * @param[in] port port identifier
+ * @param[in] bits bits to be written on the specified port
+ *
+ * @notapi
+ */
+#define pal_lld_writeport(port, bits) ((port)->ODR = (uint16_t)(bits))
+
+/**
+ * @brief Sets a bits mask on a I/O port.
+ * @details This function is implemented by writing the GPIO BSRR register, the
+ * implementation has no side effects.
+ *
+ * @param[in] port port identifier
+ * @param[in] bits bits to be ORed on the specified port
+ *
+ * @notapi
+ */
+#define pal_lld_setport(port, bits) ((port)->BSRR = (uint32_t)(bits))
+
+/**
+ * @brief Clears a bits mask on a I/O port.
+ * @details This function is implemented by writing the GPIO BSRR register, the
+ * implementation has no side effects.
+ *
+ * @param[in] port port identifier
+ * @param[in] bits bits to be cleared on the specified port
+ *
+ * @notapi
+ */
+#define pal_lld_clearport(port, bits) ((port)->BSRR = ((uint32_t)(bits) << 16U))
+
+/**
+ * @brief Writes a group of bits.
+ * @details This function is implemented by writing the GPIO BSRR register, the
+ * implementation has no side effects.
+ *
+ * @param[in] port port identifier
+ * @param[in] mask group mask
+ * @param[in] offset the group bit offset within the port
+ * @param[in] bits bits to be written. Values exceeding the group
+ * width are masked.
+ *
+ * @notapi
+ */
+#define pal_lld_writegroup(port, mask, offset, bits) { \
+ (port)->BSRR = (((~(uint32_t)(bits)) & (uint32_t)(mask)) << (16U + (offset))) | \
+ (((uint32_t)(bits) & (uint32_t)(mask)) << (offset)); \
+}
+
+/**
+ * @brief Pads group mode setup.
+ * @details This function programs a pads group belonging to the same port
+ * with the specified mode.
+ *
+ * @param[in] port port identifier
+ * @param[in] mask group mask
+ * @param[in] offset group bit offset within the port
+ * @param[in] mode group mode
+ *
+ * @notapi
+ */
+#define pal_lld_setgroupmode(port, mask, offset, mode) \
+ _pal_lld_setgroupmode(port, mask << offset, mode)
+
+/**
+ * @brief Writes a logical state on an output pad.
+ *
+ * @param[in] port port identifier
+ * @param[in] pad pad number within the port
+ * @param[in] bit logical value, the value must be @p PAL_LOW or
+ * @p PAL_HIGH
+ *
+ * @notapi
+ */
+#define pal_lld_writepad(port, pad, bit) pal_lld_writegroup(port, 1, pad, bit)
+
+/**
+ * @brief Pad event enable.
+ * @note Programming an unknown or unsupported mode is silently ignored.
+ *
+ * @param[in] port port identifier
+ * @param[in] pad pad number within the port
+ * @param[in] mode pad event mode
+ *
+ * @notapi
+ */
+#define pal_lld_enablepadevent(port, pad, mode) \
+ _pal_lld_enablepadevent(port, pad, mode)
+
+/**
+ * @brief Pad event disable.
+ * @details This function disables previously programmed event callbacks.
+ *
+ * @param[in] port port identifier
+ * @param[in] pad pad number within the port
+ *
+ * @notapi
+ */
+#define pal_lld_disablepadevent(port, pad) \
+ _pal_lld_disablepadevent(port, pad)
+
+/**
+ * @brief Returns a PAL event structure associated to a pad.
+ *
+ * @param[in] port port identifier
+ * @param[in] pad pad number within the port
+ *
+ * @notapi
+ */
+#define pal_lld_get_pad_event(port, pad) \
+ &_pal_events[pad]; (void)(port)
+
+/**
+ * @brief Returns a PAL event structure associated to a line.
+ *
+ * @param[in] line line identifier
+ *
+ * @notapi
+ */
+#define pal_lld_get_line_event(line) \
+ &_pal_events[PAL_PAD(line)]
+
+/**
+ * @brief Pad event enable check.
+ *
+ * @param[in] port port identifier
+ * @param[in] pad pad number within the port
+ * @return Pad event status.
+ * @retval false if the pad event is disabled.
+ * @retval true if the pad event is enabled.
+ *
+ * @notapi
+ */
+#define pal_lld_ispadeventenabled(port, pad) \
+ (bool)((EXTI->IMR & (1U << (uint32_t)pad)) != 0U)
+
+#if !defined(__DOXYGEN__)
+#if (PAL_USE_WAIT == TRUE) || (PAL_USE_CALLBACKS == TRUE)
+extern palevent_t _pal_events[16];
+#endif
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void _pal_lld_init(void);
+ void _pal_lld_setgroupmode(ioportid_t port,
+ ioportmask_t mask,
+ iomode_t mode);
+ void _pal_lld_enablepadevent(ioportid_t port,
+ iopadid_t pad,
+ ioeventmode_t mode);
+ void _pal_lld_disablepadevent(ioportid_t port, iopadid_t pad);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_PAL */
+
+#endif /* HAL_PAL_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_rtc_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_rtc_lld.h
new file mode 100644
index 0000000000..072773d143
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_rtc_lld.h
@@ -0,0 +1,39 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_rtc_lld.h
+ * @brief SK32F077 RTC subsystem low level driver stub.
+ * @details The SK32F077 does not integrate a real-time clock with the register
+ * set expected by the generic RTC driver. This header only exists to
+ * satisfy the unconditional include in the generic hal_rtc.h so that
+ * this platform never needs to reference the shared STM32 RTCv2 LLD
+ * header tree.
+ *
+ * @addtogroup SK32F0xx_RTC
+ * @{
+ */
+
+#ifndef HAL_RTC_LLD_H
+#define HAL_RTC_LLD_H
+
+#if HAL_USE_RTC || defined(__DOXYGEN__)
+
+#endif /* HAL_USE_RTC */
+
+#endif /* HAL_RTC_LLD_H */
+
+/** @} */
\ No newline at end of file
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_serial_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_serial_lld.c
new file mode 100644
index 0000000000..ce9d4d4121
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_serial_lld.c
@@ -0,0 +1,360 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_serial_lld.c
+ * @brief SK32F0xx low level serial driver code.
+ * @details The SK32F0xx USART is a legacy SR/DR style unit (USARTv1 class).
+ *
+ * @addtogroup SERIAL
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_SERIAL || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/** @brief USART1 serial driver identifier.*/
+#if SK32_SERIAL_USE_USART1 || defined(__DOXYGEN__)
+SerialDriver SD1;
+#endif
+
+/** @brief USART2 serial driver identifier.*/
+#if SK32_SERIAL_USE_USART2 || defined(__DOXYGEN__)
+SerialDriver SD2;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/** @brief Driver default configuration.*/
+static const SerialConfig default_config =
+{
+ SERIAL_DEFAULT_BITRATE,
+ 0,
+ USART_CR2_STOP1_BITS,
+ 0
+};
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief USART initialization.
+ * @details This function must be invoked with interrupts disabled.
+ *
+ * @param[in] sdp pointer to a @p SerialDriver object
+ * @param[in] config the architecture-dependent serial driver configuration
+ */
+static void usart_init(SerialDriver *sdp, const SerialConfig *config) {
+ uint32_t brr;
+ USART_TypeDef *u = sdp->usart;
+
+ brr = (uint32_t)((sdp->clock + config->speed/2) / config->speed);
+
+#if defined(USART_CR1_OVER8)
+ /* Correcting BRR value when oversampling by 8 instead of 16.
+ Fraction is still 4 bits wide, but only lower 3 bits used.
+ Mantissa is doubled, but Fraction is left the same.*/
+ if (config->cr1 & USART_CR1_OVER8)
+ brr = ((brr & ~7) * 2) | (brr & 7);
+#endif
+
+ osalDbgAssert(brr < 0x10000, "invalid BRR value");
+
+ u->BRR = brr;
+
+ /* Note that some bits are enforced.*/
+ u->CR2 = config->cr2 | USART_CR2_LBDIE;
+ u->CR3 = config->cr3 | USART_CR3_EIE;
+ u->CR1 = config->cr1 | USART_CR1_UE | USART_CR1_PEIE |
+ USART_CR1_RXNEIE | USART_CR1_TE |
+ USART_CR1_RE;
+ u->SR = 0;
+ (void)u->SR; /* SR reset step 1.*/
+ (void)u->DR; /* SR reset step 2.*/
+
+ /* Deciding mask to be applied on the data register on receive, this is
+ required in order to mask out the parity bit.*/
+ if ((config->cr1 & (USART_CR1_M | USART_CR1_PCE)) == USART_CR1_PCE) {
+ sdp->rxmask = 0x7F;
+ }
+ else {
+ sdp->rxmask = 0xFF;
+ }
+}
+
+/**
+ * @brief USART de-initialization.
+ * @details This function must be invoked with interrupts disabled.
+ *
+ * @param[in] u pointer to an USART I/O block
+ */
+static void usart_deinit(USART_TypeDef *u) {
+
+ u->CR1 = 0;
+ u->CR2 = 0;
+ u->CR3 = 0;
+}
+
+/**
+ * @brief Error handling routine.
+ *
+ * @param[in] sdp pointer to a @p SerialDriver object
+ * @param[in] sr USART SR register value
+ */
+static void set_error(SerialDriver *sdp, uint16_t sr) {
+ eventflags_t sts = 0;
+
+ if (sr & USART_SR_ORE)
+ sts |= SD_OVERRUN_ERROR;
+ if (sr & USART_SR_PE)
+ sts |= SD_PARITY_ERROR;
+ if (sr & USART_SR_FE)
+ sts |= SD_FRAMING_ERROR;
+ if (sr & USART_SR_NE)
+ sts |= SD_NOISE_ERROR;
+ chnAddFlagsI(sdp, sts);
+}
+
+#if SK32_SERIAL_USE_USART1 || defined(__DOXYGEN__)
+static void notify1(io_queue_t *qp) {
+
+ (void)qp;
+ USART1->CR1 |= USART_CR1_TXEIE | USART_CR1_TCIE;
+}
+#endif
+
+#if SK32_SERIAL_USE_USART2 || defined(__DOXYGEN__)
+static void notify2(io_queue_t *qp) {
+
+ (void)qp;
+ USART2->CR1 |= USART_CR1_TXEIE | USART_CR1_TCIE;
+}
+#endif
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if SK32_SERIAL_USE_USART1 || defined(__DOXYGEN__)
+#if !defined(SK32_USART1_SUPPRESS_ISR)
+#if !defined(SK32_USART1_HANDLER)
+#error "SK32_USART1_HANDLER not defined"
+#endif
+/**
+ * @brief USART1 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_USART1_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ sd_lld_serve_interrupt(&SD1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+#endif
+
+#if SK32_SERIAL_USE_USART2 || defined(__DOXYGEN__)
+#if !defined(SK32_USART2_SUPPRESS_ISR)
+#if !defined(SK32_USART2_HANDLER)
+#error "SK32_USART2_HANDLER not defined"
+#endif
+/**
+ * @brief USART2 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_USART2_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ sd_lld_serve_interrupt(&SD2);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+#endif
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level serial driver initialization.
+ *
+ * @notapi
+ */
+void sd_lld_init(void) {
+
+#if SK32_SERIAL_USE_USART1
+ sdObjectInit(&SD1, NULL, notify1);
+ SD1.usart = USART1;
+ SD1.clock = SK32_PCLK2;
+#if !defined(SK32_USART1_SUPPRESS_ISR) && defined(SK32_USART1_NUMBER)
+ nvicEnableVector(SK32_USART1_NUMBER, SK32_SERIAL_USART1_PRIORITY);
+#endif
+#endif
+
+#if SK32_SERIAL_USE_USART2
+ sdObjectInit(&SD2, NULL, notify2);
+ SD2.usart = USART2;
+ SD2.clock = SK32_PCLK1;
+#if !defined(SK32_USART2_SUPPRESS_ISR) && defined(SK32_USART2_NUMBER)
+ nvicEnableVector(SK32_USART2_NUMBER, SK32_SERIAL_USART2_PRIORITY);
+#endif
+#endif
+}
+
+/**
+ * @brief Low level serial driver configuration and (re)start.
+ *
+ * @param[in] sdp pointer to a @p SerialDriver object
+ * @param[in] config the architecture-dependent serial driver configuration.
+ * If this parameter is set to @p NULL then a default
+ * configuration is used.
+ *
+ * @notapi
+ */
+void sd_lld_start(SerialDriver *sdp, const SerialConfig *config) {
+
+ if (config == NULL)
+ config = &default_config;
+
+ if (sdp->state == SD_STOP) {
+#if SK32_SERIAL_USE_USART1
+ if (&SD1 == sdp) {
+ rccEnableUSART1(true);
+ }
+#endif
+#if SK32_SERIAL_USE_USART2
+ if (&SD2 == sdp) {
+ rccEnableUSART2(true);
+ }
+#endif
+ }
+ usart_init(sdp, config);
+}
+
+/**
+ * @brief Low level serial driver stop.
+ * @details De-initializes the USART, stops the associated clock.
+ *
+ * @param[in] sdp pointer to a @p SerialDriver object
+ *
+ * @notapi
+ */
+void sd_lld_stop(SerialDriver *sdp) {
+
+ if (sdp->state == SD_READY) {
+ usart_deinit(sdp->usart);
+#if SK32_SERIAL_USE_USART1
+ if (&SD1 == sdp) {
+ rccDisableUSART1();
+ return;
+ }
+#endif
+#if SK32_SERIAL_USE_USART2
+ if (&SD2 == sdp) {
+ rccDisableUSART2();
+ return;
+ }
+#endif
+ }
+}
+
+/**
+ * @brief Common IRQ handler.
+ *
+ * @param[in] sdp communication channel associated to the USART
+ */
+void sd_lld_serve_interrupt(SerialDriver *sdp) {
+ USART_TypeDef *u = sdp->usart;
+ uint16_t cr1;
+ uint16_t sr = u->SR;
+
+ /* Special case, LIN break detection.*/
+ if (sr & USART_SR_LBD) {
+ osalSysLockFromISR();
+ chnAddFlagsI(sdp, SD_BREAK_DETECTED);
+ u->SR = ~USART_SR_LBD;
+ osalSysUnlockFromISR();
+ }
+
+ /* Data available.*/
+ osalSysLockFromISR();
+ while (sr & (USART_SR_RXNE | USART_SR_ORE | USART_SR_NE | USART_SR_FE |
+ USART_SR_PE)) {
+ uint8_t b;
+
+ /* Error condition detection.*/
+ if (sr & (USART_SR_ORE | USART_SR_NE | USART_SR_FE | USART_SR_PE))
+ set_error(sdp, sr);
+ b = (uint8_t)u->DR & sdp->rxmask;
+ if (sr & USART_SR_RXNE)
+ sdIncomingDataI(sdp, b);
+ sr = u->SR;
+ }
+ osalSysUnlockFromISR();
+
+ /* Caching CR1.*/
+ cr1 = u->CR1;
+
+ /* Transmission buffer empty.*/
+ if ((cr1 & USART_CR1_TXEIE) && (sr & USART_SR_TXE)) {
+ msg_t b;
+ osalSysLockFromISR();
+ b = oqGetI(&sdp->oqueue);
+ if (b < MSG_OK) {
+ chnAddFlagsI(sdp, CHN_OUTPUT_EMPTY);
+ cr1 &= ~USART_CR1_TXEIE;
+ }
+ else
+ u->DR = b;
+ osalSysUnlockFromISR();
+ }
+
+ /* Physical transmission end.*/
+ if ((cr1 & USART_CR1_TCIE) && (sr & USART_SR_TC)) {
+ osalSysLockFromISR();
+ if (oqIsEmptyI(&sdp->oqueue)) {
+ chnAddFlagsI(sdp, CHN_TRANSMISSION_END);
+ cr1 &= ~USART_CR1_TCIE;
+ }
+ osalSysUnlockFromISR();
+ }
+
+ /* Writing CR1 once.*/
+ u->CR1 = cr1;
+}
+
+#endif /* HAL_USE_SERIAL */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_serial_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_serial_lld.h
new file mode 100644
index 0000000000..3010dbe339
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_serial_lld.h
@@ -0,0 +1,227 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_serial_lld.h
+ * @brief SK32F0xx low level serial driver header.
+ * @details The SK32F0xx USART is a legacy SR/DR style unit (USARTv1 class,
+ * the same register set used by the STM32F1 family). This driver
+ * is written directly against the vendor CMSIS header
+ * (@p sk32f0xx.h), no STM32 port code is involved.
+ *
+ * @addtogroup SERIAL
+ * @{
+ */
+
+#ifndef HAL_SERIAL_LLD_H
+#define HAL_SERIAL_LLD_H
+
+#if HAL_USE_SERIAL || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief USART1 driver enable switch.
+ * @details If set to @p TRUE the support for USART1 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SK32_SERIAL_USE_USART1) || defined(__DOXYGEN__)
+#define SK32_SERIAL_USE_USART1 FALSE
+#endif
+
+/**
+ * @brief USART2 driver enable switch.
+ * @details If set to @p TRUE the support for USART2 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SK32_SERIAL_USE_USART2) || defined(__DOXYGEN__)
+#define SK32_SERIAL_USE_USART2 FALSE
+#endif
+
+/**
+ * @brief USART1 interrupt priority level setting.
+ * @note ARMv6-M (Cortex-M0) implements only 2 priority bits, the valid
+ * priority range is 0 (highest) .. 3 (lowest).
+ */
+#if !defined(SK32_SERIAL_USART1_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_SERIAL_USART1_PRIORITY 3
+#endif
+
+/**
+ * @brief USART2 interrupt priority level setting.
+ */
+#if !defined(SK32_SERIAL_USART2_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_SERIAL_USART2_PRIORITY 3
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if SK32_SERIAL_USE_USART1 && !SK32_HAS_USART1
+#error "USART1 not present in the selected device"
+#endif
+
+#if SK32_SERIAL_USE_USART2 && !SK32_HAS_USART2
+#error "USART2 not present in the selected device"
+#endif
+
+#if !SK32_SERIAL_USE_USART1 && !SK32_SERIAL_USE_USART2
+#error "SERIAL driver activated but no USART peripheral assigned"
+#endif
+
+#if SK32_SERIAL_USE_USART1 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(SK32_SERIAL_USART1_PRIORITY)
+#error "Invalid IRQ priority assigned to USART1"
+#endif
+
+#if SK32_SERIAL_USE_USART2 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(SK32_SERIAL_USART2_PRIORITY)
+#error "Invalid IRQ priority assigned to USART2"
+#endif
+
+/* Checks on allocation of USART units.*/
+#if SK32_SERIAL_USE_USART1
+#if defined(SK32_USART1_IS_USED)
+#error "SD1 requires USART1 but it is already used"
+#else
+#define SK32_USART1_IS_USED
+#endif
+#endif
+
+#if SK32_SERIAL_USE_USART2
+#if defined(SK32_USART2_IS_USED)
+#error "SD2 requires USART2 but it is already used"
+#else
+#define SK32_USART2_IS_USED
+#endif
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief SK32 Serial Driver configuration structure.
+ * @details An instance of this structure must be passed to @p sdStart()
+ * in order to configure and start a serial driver operations.
+ * @note This structure content is architecture dependent, each driver
+ * implementation defines its own version and the custom static
+ * initializers.
+ */
+typedef struct hal_serial_config {
+ /**
+ * @brief Bit rate.
+ */
+ uint32_t speed;
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Initialization value for the CR1 register.
+ */
+ uint16_t cr1;
+ /**
+ * @brief Initialization value for the CR2 register.
+ */
+ uint16_t cr2;
+ /**
+ * @brief Initialization value for the CR3 register.
+ */
+ uint16_t cr3;
+} SerialConfig;
+
+/**
+ * @brief @p SerialDriver specific data.
+ */
+#define _serial_driver_data \
+ _base_asynchronous_channel_data \
+ /* Driver state.*/ \
+ sdstate_t state; \
+ /* Input queue.*/ \
+ input_queue_t iqueue; \
+ /* Output queue.*/ \
+ output_queue_t oqueue; \
+ /* Input circular buffer.*/ \
+ uint8_t ib[SERIAL_BUFFERS_SIZE]; \
+ /* Output circular buffer.*/ \
+ uint8_t ob[SERIAL_BUFFERS_SIZE]; \
+ /* End of the mandatory fields.*/ \
+ /* Pointer to the USART registers block.*/ \
+ USART_TypeDef *usart; \
+ /* Clock frequency for the associated USART.*/ \
+ uint32_t clock; \
+ /* Mask to be applied on received frames.*/ \
+ uint8_t rxmask;
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*
+ * Stop bit encoding is not present in the vendor device header under this
+ * symbolic form, it is defined here (USARTv1 convention).
+ */
+#ifndef USART_CR2_STOP1_BITS
+#define USART_CR2_STOP1_BITS (0 << 12) /**< @brief CR2 1 stop bit value.*/
+#endif
+#ifndef USART_CR2_STOP0P5_BITS
+#define USART_CR2_STOP0P5_BITS (1 << 12) /**< @brief CR2 0.5 stop bit value.*/
+#endif
+#ifndef USART_CR2_STOP2_BITS
+#define USART_CR2_STOP2_BITS (2 << 12) /**< @brief CR2 2 stop bit value.*/
+#endif
+#ifndef USART_CR2_STOP1P5_BITS
+#define USART_CR2_STOP1P5_BITS (3 << 12) /**< @brief CR2 1.5 stop bit value.*/
+#endif
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if SK32_SERIAL_USE_USART1 && !defined(__DOXYGEN__)
+extern SerialDriver SD1;
+#endif
+#if SK32_SERIAL_USE_USART2 && !defined(__DOXYGEN__)
+extern SerialDriver SD2;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void sd_lld_init(void);
+ void sd_lld_start(SerialDriver *sdp, const SerialConfig *config);
+ void sd_lld_stop(SerialDriver *sdp);
+ void sd_lld_serve_interrupt(SerialDriver *sdp);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_SERIAL */
+
+#endif /* HAL_SERIAL_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c
new file mode 100644
index 0000000000..74dbf513eb
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c
@@ -0,0 +1,514 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_sled_lld.c
+ * @brief SK32F0xx serial LED (SLED) subsystem low level driver code.
+ * @details This driver implements the transfer side of the 3Think SLED
+ * controller: the register programming (CR/SR/CDR/TCR/FCR) mirrors
+ * the SK32F0xx_StdPeriph_Driver (sk32f0xx_sled.c) while the DMA
+ * side is built on the native SK32 DMA helper (sk32_dma.c). Each
+ * of the two SLED groups owns one DMA1 channel (channel 1 for SLED1
+ * and channel 2 for SLED2) that must be routed to the SLED request
+ * line through the SYSCFG CFGR3 remapping, exactly like the vendor
+ * SYSCFG_DMAChannelConfig() API does.
+ * @note The transfers are memory-to-peripheral operations that push a raw
+ * color byte stream into the group data register (SLED->DR[x]);
+ * the write width is selected by the caller and controls how the
+ * hardware fills each 32 bits wide FIFO entry, which determines how
+ * the four output channels of a group are driven:
+ * - @p SLED_WIDTH_8BIT: every byte is replicated 4 times into the
+ * FIFO entry, so all four channels carry the same byte stream (one
+ * light effect, smallest RAM footprint);
+ * - @p SLED_WIDTH_16BIT: every half word is replicated twice, so
+ * CH0 == CH2 and CH1 == CH3 each carry their own stream (two
+ * effects);
+ * - @p SLED_WIDTH_32BIT: every word is stored directly, so each of
+ * the four channels carries its own byte (four effects).
+ * The byte-to-channel mapping is little endian (byte_i of a FIFO
+ * entry is serialized on channel i).
+ * @note The transfer is synchronous: the function waits for the previous
+ * frame (including its reset pulse) to be completely shifted out,
+ * reloads the DMA channel with the requested width, arms the reset
+ * code (CR RSTSTRx) and waits until the hardware reports the reset
+ * completed, so every call produces one complete, self contained
+ * WS2812 frame. No SLED interrupt is used, the completion is
+ * detected by polling the DMA channel counter and the SLED reset
+ * flag.
+ *
+ * @addtogroup SK32F0xx_SLED
+ * @{
+ */
+
+#include "hal.h"
+#include "hal_sled_lld.h"
+
+#if HAL_USE_SLED || defined(__DOXYGEN__)
+
+/* The SLED driver allocates two DMA1 channels, so the DMA helper code of
+ sk32_dma.c (and dmaInit()) must be part of the build. The platform
+ header (hal_lld.h) defines SK32_DMA_REQUIRED automatically whenever
+ HAL_USE_SLED is TRUE, this check only protects direct/standalone uses.*/
+#if !defined(SK32_DMA_REQUIRED)
+#error "the SLED low level driver requires SK32_DMA_REQUIRED to be defined"
+#endif
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/**
+ * @brief DMA channel identifier used by SLED1 (DMA1 channel 1).
+ */
+#define SK32_SLED1_DMA_STREAM SK32_DMA_STREAM_ID(1, 1)
+
+/**
+ * @brief DMA channel identifier used by SLED2 (DMA1 channel 2).
+ */
+#define SK32_SLED2_DMA_STREAM SK32_DMA_STREAM_ID(1, 2)
+
+/**
+ * @brief SYSCFG CFGR3 DMA request code of the SLED1 group (group "G1").
+ * @note The request codes are 6 bit values placed in the low 6 bits of
+ * the 8 bits wide per-channel slot of CFGR3, see the vendor
+ * SYSCFG_DMAChannelConfig() (SYSCFG_DMARemap_SLED_G1).
+ */
+#define SK32_SLED1_DMA_REQ_CODE 0x2FU
+
+/**
+ * @brief SYSCFG CFGR3 DMA request code of the SLED2 group (group "G2").
+ */
+#define SK32_SLED2_DMA_REQ_CODE 0x30U
+
+/**
+ * @brief Mask of the request code bits inside a CFGR3 channel slot.
+ */
+#define SK32_SLED_DMA_REQ_MASK 0x3FU
+
+/**
+ * @brief Position of the DMA1 channel request slot in CFGR3.
+ * @note The request code of a channel occupies the low 6 bits of an 8
+ * bits wide slot whose base address is ((channel - 1) * 8):
+ * channel 1 uses bits 0..5, channel 2 bits 8..13 and so on, the
+ * same layout used by the vendor SYSCFG_DMAChannelConfig() and by
+ * the SYSCFG_CFGR3_DMA_CHxREQ field masks of the device header.
+ *
+ * @param[in] channel the physical DMA1 channel number (1..4 for CFGR3)
+ */
+#define SK32_SLED_DMA_SLOT_SHIFT(channel) (((channel) - 1U) * 8U)
+
+/**
+ * @brief Maximum time in milliseconds waited for the SLED to complete a
+ * frame (shift out plus reset pulse) before the operation is
+ * aborted with @p MSG_TIMEOUT.
+ */
+#if !defined(SK32_SLED_BUSY_TIMEOUT) || defined(__DOXYGEN__)
+#define SK32_SLED_BUSY_TIMEOUT 100
+#endif
+
+/**
+ * @brief CCR mode of the SLED DMA transfers base (memory to peripheral,
+ * memory pointer increment, no peripheral increment, priority).
+ * @note The access width bits (PSIZE/MSIZE) are added per transfer from
+ * the selected write width in @p sled_lld_send().
+ */
+#define SK32_SLED_DMA_MODE (SK32_DMA_CR_DIR_M2P | \
+ SK32_DMA_CR_MINC | \
+ SK32_DMA_CR_PL(SK32_SLED_DMA_PRIORITY))
+
+/**
+ * @brief Builds the CCR PSIZE/MSIZE access width bits for a SLED write
+ * width. The memory and peripheral access sizes are kept in sync
+ * so that every DMA beat pushes one FIFO entry of the selected
+ * width.
+ *
+ * @param[in] width the SLED write width (@p sled_width_t)
+ * @return The PSIZE | MSIZE bit pattern to OR into the CCR mode.
+ */
+#define SK32_SLED_DMA_SIZE_MODE(width) \
+ ((width) == SLED_WIDTH_16BIT ? (SK32_DMA_CR_PSIZE_HWORD | \
+ SK32_DMA_CR_MSIZE_HWORD) : \
+ (width) == SLED_WIDTH_32BIT ? (SK32_DMA_CR_PSIZE_WORD | \
+ SK32_DMA_CR_MSIZE_WORD) : \
+ (SK32_DMA_CR_PSIZE_BYTE | \
+ SK32_DMA_CR_MSIZE_BYTE))
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables. */
+/*===========================================================================*/
+
+/**
+ * @brief SLED1/SLED2 DMA stream descriptors, one per group.
+ */
+static const sk32_dma_stream_t *sled_streams[2];
+
+/**
+ * @brief @p TRUE when the driver is started (SLED enabled and DMA
+ * channels allocated).
+ */
+static bool sled_started;
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Waits for the SLED to terminate the reset pulse of a group.
+ * @note The RSTSTRx bit is set by software to arm the reset code that
+ * the hardware emits once the FIFO and the shift registers have
+ * been drained; the bit is cleared by the hardware when the reset
+ * pulse has been completed.
+ *
+ * @param[in] group the SLED group index
+ * @retval The operation result.
+ * @retval MSG_OK the SLED is ready.
+ * @retval MSG_TIMEOUT the reset did not complete in time.
+ */
+static msg_t sled_lld_wait_reset_done(sled_group_t group) {
+ uint32_t mask = sled_lld_rststr_mask(group);
+ int tmo = SK32_SLED_BUSY_TIMEOUT;
+
+ while ((SLED->CR & mask) != 0U) {
+ if (tmo-- <= 0) {
+ return MSG_TIMEOUT;
+ }
+ chThdSleepMilliseconds(1);
+ }
+
+ return MSG_OK;
+}
+
+/**
+ * @brief Routes a DMA1 channel to the SLED group request line.
+ * @note The remapping code is written into the 6 request bits of the
+ * channel slot of the SYSCFG CFGR3 register, following the vendor
+ * SYSCFG_DMAChannelConfig() register layout (channel 1 uses the
+ * slot at bits 0..5, channel 2 the slot at bits 8..13, ...).
+ *
+ * @param[in] channel the physical DMA1 channel number (1..4)
+ * @param[in] code the 6 bit request selection code
+ */
+static void sled_lld_remap(uint32_t channel, uint32_t code) {
+ uint32_t shift = SK32_SLED_DMA_SLOT_SHIFT(channel);
+
+ SYSCFG->CFGR3 = (SYSCFG->CFGR3 & ~(SK32_SLED_DMA_REQ_MASK << shift)) |
+ (code << shift);
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Initializes the SLED driver.
+ * @note The function is idempotent and can be called multiple times
+ * without issues.
+ *
+ * @init
+ */
+void sled_lld_init(void) {
+
+ sled_streams[0] = NULL;
+ sled_streams[1] = NULL;
+ sled_started = false;
+}
+
+/**
+ * @brief Starts the SLED peripheral.
+ * @details The SLED APB2 clock and the SYSCFG clock are enabled, the
+ * time-code/baud/polarity registers are programmed, the DMA
+ * channels of the enabled groups are allocated and routed to the
+ * SLED request lines and finally the peripheral is enabled.
+ * @note The output pads must be configured in alternate function 14 by
+ * the application/board code, this driver does not touch the GPIO.
+ * @note The values programmed through @p config match the vendor 3Think
+ * RGBKeyboardSTK reference implementation (T0H = 4, T1H = 15,
+ * TRST = 80, clock divided by 4, baud divided by 32).
+ *
+ * @param[in] config pointer to the @p SLEDConfig structure
+ *
+ * @init
+ */
+void sled_lld_start(const SLEDConfig *config) {
+ uint32_t cr;
+
+ osalDbgCheck(config != NULL);
+
+ if (sled_started) {
+ return;
+ }
+
+ /* Enabling the SLED APB2 clock and the SYSCFG APB2 clock (the latter is
+ needed for the DMA request remapping).*/
+ rccEnableAPB2(RCC_APB2ENR_SLEDEN | RCC_APB2ENR_SYSCFGEN, true);
+ rccResetAPB2(RCC_APB2ENR_SLEDRST);
+
+ /* FIFO reset.*/
+ SLED->FCR = SLED_FCR_FRST;
+ SLED->FCR = 0U;
+
+ /* Time codes and clock dividers (they must be programmed before the EN
+ bit is set, the same order used by the vendor demonstration code).*/
+ SLED->TCR = (uint32_t)((uint32_t)(config->trst_cycles << 16) | \
+ (uint32_t)(config->t1h_cycles << 8) | \
+ (uint32_t)(config->t0h_cycles << 0));
+ SLED->CDR = (uint32_t)((uint32_t)(config->baud_prescaler << 8) | \
+ (uint32_t)(config->prescaler << 0));
+
+ /* Idle and reset polarities, no sync, no interrupts. The DMA request
+ lines of the enabled groups are switched on (DMAEN1/DMAEN2).*/
+ cr = SLED->CR;
+ cr &= ~(uint32_t)(SLED_CR_EN | SLED_CR_SYNCEN | SLED_CR_RSTSTS | \
+ SLED_CR_IDLESTS | SLED_CR_SYNCSTS | \
+ SLED_CR_DMAEN1 | SLED_CR_INTEN1 | \
+ SLED_CR_DMAEN2 | SLED_CR_INTEN2);
+ cr |= (config->reset_polarity != SLED_POLARITY_LOW) ? SLED_CR_RSTSTS : 0U;
+ cr |= (config->idle_polarity != SLED_POLARITY_LOW) ? SLED_CR_IDLESTS : 0U;
+#if SK32_SLED_USE_SLED1
+ cr |= SLED_CR_DMAEN1;
+#endif
+#if SK32_SLED_USE_SLED2
+ cr |= SLED_CR_DMAEN2;
+#endif
+ SLED->CR = cr;
+
+ /* DMA channels allocation and SYSCFG request remapping.*/
+#if SK32_SLED_USE_SLED1
+ sled_streams[SLED1] = dmaStreamAlloc(SK32_SLED1_DMA_STREAM,
+ SK32_SLED_DMA_PRIORITY, NULL, NULL);
+ osalDbgAssert(sled_streams[SLED1] != NULL, "SLED1 DMA channel busy");
+ sled_lld_remap(1, SK32_SLED1_DMA_REQ_CODE);
+#endif
+
+#if SK32_SLED_USE_SLED2
+ sled_streams[SLED2] = dmaStreamAlloc(SK32_SLED2_DMA_STREAM,
+ SK32_SLED_DMA_PRIORITY, NULL, NULL);
+ osalDbgAssert(sled_streams[SLED2] != NULL, "SLED2 DMA channel busy");
+ sled_lld_remap(2, SK32_SLED2_DMA_REQ_CODE);
+#endif
+
+ /* DMA channel base configuration (peripheral address and mode).*/
+ if (sled_streams[SLED1] != NULL) {
+ dmaStreamSetPeripheral(sled_streams[SLED1], &SLED->DR[0]);
+ dmaStreamSetMode(sled_streams[SLED1], SK32_SLED_DMA_MODE);
+ }
+ if (sled_streams[SLED2] != NULL) {
+ dmaStreamSetPeripheral(sled_streams[SLED2], &SLED->DR[1]);
+ dmaStreamSetMode(sled_streams[SLED2], SK32_SLED_DMA_MODE);
+ }
+
+ /* SLED enable, data written to the FIFO after this point is serialized
+ on the configured outputs.*/
+ SLED->CR |= SLED_CR_EN;
+
+ sled_started = true;
+}
+
+/**
+ * @brief Stops the SLED peripheral.
+ * @details The peripheral is disabled, the DMA channels are freed and the
+ * SLED clock is gated off.
+ *
+ * @init
+ */
+void sled_lld_stop(void) {
+
+ if (!sled_started) {
+ return;
+ }
+
+ /* Peripheral disabled and DMA requests gated off.*/
+ SLED->CR &= ~(uint32_t)(SLED_CR_EN | SLED_CR_DMAEN1 | SLED_CR_DMAEN2);
+
+ /* DMA channels released.*/
+#if SK32_SLED_USE_SLED1
+ if (sled_streams[SLED1] != NULL) {
+ dmaStreamFree(sled_streams[SLED1]);
+ sled_streams[SLED1] = NULL;
+ }
+#endif
+#if SK32_SLED_USE_SLED2
+ if (sled_streams[SLED2] != NULL) {
+ dmaStreamFree(sled_streams[SLED2]);
+ sled_streams[SLED2] = NULL;
+ }
+#endif
+
+ rccDisableAPB2(RCC_APB2ENR_SLEDEN);
+
+ sled_started = false;
+}
+
+/**
+ * @brief Sends one complete frame on a SLED group.
+ * @details The function is synchronous: it waits for any previous frame
+ * (data plus reset pulse) to be completely shifted out, reloads
+ * the DMA channel with the new stream using the selected write
+ * width, arms the trailing reset code and waits until the hardware
+ * reports that the whole frame has been transmitted, so every call
+ * produces one complete, self contained WS2812 frame.
+ * @note The write width selects how the hardware fills each FIFO entry
+ * and therefore which output channels are driven:
+ * - @p SLED_WIDTH_8BIT: every byte is replicated on all four
+ * channels (same effect, GRB/GRB/... programs one LED per 3
+ * bytes) and @p size is a byte count;
+ * - @p SLED_WIDTH_16BIT: the low byte of each half word drives
+ * CH0/CH2 and the high byte CH1/CH3, so @p data must hold
+ * @p size/2 half words (GRB stream of two effects interleaved);
+ * - @p SLED_WIDTH_32BIT: byte_i of each word drives CH_i, so
+ * @p data must hold @p size/4 words (GRB streams of four effects
+ * interleaved).
+ * In every case the byte to channel mapping is little endian
+ * (byte_i -> CH_i of the FIFO entry).
+ * @note The transfer size is limited to
+ * @p SK32_SLED_MAX_TRANSFER_BYTES bytes by the 16 bit DMA counter
+ * and must be an integer number of the selected width.
+ * @note The buffer is not modified by the driver and only needs to stay
+ * valid until the function returns (the transfer is synchronous).
+ *
+ * @param[in] group the SLED group identifier (@p SLED1 or @p SLED2)
+ * @param[in] width the data register write width (@p sled_width_t)
+ * @param[in] data pointer to the byte stream to be transmitted
+ * @param[in] size number of bytes of @p data
+ * @return The operation result.
+ * @retval MSG_OK the frame has been transmitted.
+ * @retval MSG_RESET the driver or the group is not started, or the size
+ * is not a multiple of the selected width.
+ * @retval MSG_TIMEOUT the SLED did not become ready in time.
+ *
+ * @api
+ */
+msg_t sled_lld_send(sled_group_t group, sled_width_t width,
+ const uint8_t *data, size_t size) {
+ const sk32_dma_stream_t *dmastp;
+ uint32_t mode;
+ msg_t ret;
+ int tmo;
+ size_t beats;
+
+ osalDbgCheck(SK32_SLED_IS_VALID_GROUP(group));
+ osalDbgCheck((width == SLED_WIDTH_8BIT) ||
+ (width == SLED_WIDTH_16BIT) ||
+ (width == SLED_WIDTH_32BIT));
+ osalDbgCheck((data != NULL) || (size == 0U));
+
+ if (!sled_started) {
+ return MSG_RESET;
+ }
+
+ dmastp = sled_streams[group];
+ if ((dmastp == NULL) || (data == NULL)) {
+ return MSG_RESET;
+ }
+
+ if (size == 0U) {
+ return MSG_OK;
+ }
+ if (size > SK32_SLED_MAX_TRANSFER_BYTES) {
+ return MSG_RESET;
+ }
+
+ /* The byte stream must be an integer number of the selected data width
+ (1, 2 or 4 bytes per FIFO entry).*/
+ beats = size / (1U << ((unsigned)width));
+ if (beats * (1U << ((unsigned)width)) != size) {
+ return MSG_RESET;
+ }
+
+ /* Waiting for the previous frame (data plus its trailing reset pulse) to
+ be completed: the RSTSTRx bit is cleared by the hardware when the reset
+ code has been emitted. This is the same synchronization used by the
+ vendor SLED_updata() (while (SLED->CR & RSTSTRx) ;). Note that the BUSY
+ flag is not suited for this wait: per the user guide the block keeps
+ outputting the IDLE code with BUSY stuck high when the FIFO and the
+ shift register are empty and RSTSTR is 0.*/
+ ret = sled_lld_wait_reset_done(group);
+ if (ret != MSG_OK) {
+ return ret;
+ }
+
+ /* DMA channel reload with the access size matching the write width.*/
+ mode = SK32_SLED_DMA_MODE | SK32_SLED_DMA_SIZE_MODE(width);
+ dmaStreamDisable(dmastp);
+ dmaStreamSetTransactionSize(dmastp, beats);
+ dmaStreamSetMemory0(dmastp, data);
+ dmaStreamSetMode(dmastp, mode);
+ dmaStreamEnable(dmastp);
+
+ /* Arming the reset code that terminates the frame: the hardware starts
+ the reset pulse only after the FIFO and the shift registers have been
+ drained (the same sequence used by the vendor demonstration code).*/
+ SLED->CR |= sled_lld_rststr_mask(group);
+
+ /* Waiting for the DMA to push every FIFO entry into the SLED FIFO.*/
+ tmo = SK32_SLED_BUSY_TIMEOUT;
+ while (dmaStreamGetTransactionSize(dmastp) != 0U) {
+ if (tmo-- <= 0) {
+ dmaStreamDisable(dmastp);
+ return MSG_TIMEOUT;
+ }
+ chThdSleepMilliseconds(1);
+ }
+ dmaStreamDisable(dmastp);
+
+ /* Waiting for the reset pulse to complete: when RSTSTRx is cleared by the
+ hardware the whole frame (data plus reset) has been transmitted.*/
+ ret = sled_lld_wait_reset_done(group);
+ if (ret != MSG_OK) {
+ return ret;
+ }
+
+ /* Clearing any stale interrupt flag of the group (write-1-to-clear).*/
+ SLED->SR = (group == SLED1) ? SLED_SR_INT1 : SLED_SR_INT2;
+
+ return MSG_OK;
+}
+
+/**
+ * @brief Sends one complete frame on a SLED group using byte wide (8 bit)
+ * writes.
+ * @details Equivalent to @p sled_lld_send(group, SLED_WIDTH_8BIT, data,
+ * size): every byte is replicated on all four output channels of
+ * the group, so the four channels carry the same light effect.
+ *
+ * @param[in] group the SLED group identifier (@p SLED1 or @p SLED2)
+ * @param[in] data pointer to the byte stream to be transmitted
+ * @param[in] size number of bytes to be transmitted
+ * @return The operation result.
+ * @retval MSG_OK the frame has been transmitted.
+ * @retval MSG_RESET the driver or the group is not started.
+ * @retval MSG_TIMEOUT the SLED did not become ready in time.
+ *
+ * @api
+ */
+msg_t sled_lld_send_bytes(sled_group_t group, const uint8_t *data,
+ size_t size) {
+
+ return sled_lld_send(group, SLED_WIDTH_8BIT, data, size);
+}
+
+#endif /* HAL_USE_SLED == TRUE */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.h
new file mode 100644
index 0000000000..4bca91726d
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.h
@@ -0,0 +1,310 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_sled_lld.h
+ * @brief SK32F0xx serial LED (SLED) subsystem low level driver header.
+ * @details The SK32F077 embeds a 3Think "serial LED" controller (SLED), a
+ * peripheral that is not present on the STM32F0xx devices. It is
+ * designed to drive WS2812/SK6812 style addressable LEDs without
+ * CPU bit-banging: the T0H/T1H/TRST time-code fields of the TCR
+ * register program the per-bit timing and the CDR register programs
+ * the serial bit clock. The unit provides two groups (SLED1/SLED2,
+ * DR[0]/DR[1]); each group is served by one DMA1 channel (channel 1
+ * for SLED1 and channel 2 for SLED2) routed to the SLED request
+ * line through the SYSCFG CFGR3 channel remapping, the same request
+ * routing used by the vendor SYSCFG_DMAChannelConfig() API.
+ * @note The data path follows the vendor reference implementation
+ * (SK32F077x_RGBKeyboardSTK User/Src/sled.c): the DMA performs
+ * byte wide transfers from a raw color byte stream into the @p DR
+ * register of a group, exactly like the vendor code does with
+ * @p KBCU_RGBBuff and a channel configured for byte sized
+ * memory/peripheral accesses. Each 8-bit write pushes one color
+ * byte into the group FIFO and the SLED block serializes the byte
+ * stream on the group output pads, so a plain GRB/GRB/... byte
+ * stream drives one WS2812 string without any CPU bit packing.
+ * @note The vendor "how to use" sequence is:
+ * - enable the SLED APB2 clock,
+ * - configure the output pads in alternate function 14 (the
+ * mechanical RGB keyboard reference uses PB8 for the SLED1
+ * output, GPIO_AF_14),
+ * - program CDR (PSC/BRD), TCR (T0H/T1H/TRST) and the CR polarity
+ * bits before enabling the peripheral,
+ * - enable the DMA request line of the group (DMAEN1/DMAEN2),
+ * - enable the SLED (CR EN),
+ * - for every frame: wait for the previous reset pulse to finish,
+ * reload the DMA channel with the new byte stream, enable it and
+ * write the RSTSTRx bit: the block starts the reset code when
+ * the FIFO and the shift register have been drained.
+ * @note This low level driver does not handle the GPIO alternate function
+ * configuration: the pads (e.g. PB8 = SLED1, alternate function 14)
+ * must be routed by the application/board code before the data
+ * transfers are started.
+ *
+ * @addtogroup SK32F0xx_SLED
+ * @{
+ */
+
+#ifndef HAL_SLED_LLD_H
+#define HAL_SLED_LLD_H
+
+#if (HAL_USE_SLED == TRUE) || defined(__DOXYGEN__)
+
+#include "sk32_dma.h"
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief Mask of the CR bits owned by this driver.
+ */
+#define SK32_SLED_CR_OWNED_MASK (SLED_CR_EN | \
+ SLED_CR_SYNCEN | \
+ SLED_CR_RSTSTS | \
+ SLED_CR_IDLESTS | \
+ SLED_CR_SYNCSTS | \
+ SLED_CR_DMAEN1 | \
+ SLED_CR_INTEN1 | \
+ SLED_CR_DMAEN2 | \
+ SLED_CR_INTEN2)
+
+/**
+ * @brief Bits identifying the SLED1 group (group index 0).
+ */
+#define SK32_SLED_GROUP1_MASK SLED_CR_DMAEN1
+
+/**
+ * @brief Bits identifying the SLED2 group (group index 1).
+ */
+#define SK32_SLED_GROUP2_MASK SLED_CR_DMAEN2
+
+/**
+ * @brief Maximum number of bytes that can be queued in one group transfer.
+ * @note The DMA CNDTR register is 16 bits wide.
+ */
+#define SK32_SLED_MAX_TRANSFER_BYTES 65535U
+
+/**
+ * @brief Checks if a SLED group index is in the valid range.
+ *
+ * @param[in] group the SLED group index
+ * @return The check result.
+ * @retval false invalid group.
+ * @retval true correct group.
+ */
+#define SK32_SLED_IS_VALID_GROUP(group) (((group) >= 0) && ((group) <= 1))
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name SK32F0xx SLED configuration options
+ * @{
+ */
+/**
+ * @brief DMA priority used when allocating the DMA1 channels assigned to
+ * the SLED groups.
+ */
+#if !defined(SK32_SLED_DMA_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_SLED_DMA_PRIORITY 2
+#endif
+
+/**
+ * @brief Enable the SLED1 group (DR[0], DMA1 channel 1).
+ */
+#if !defined(SK32_SLED_USE_SLED1) || defined(__DOXYGEN__)
+#define SK32_SLED_USE_SLED1 TRUE
+#endif
+
+/**
+ * @brief Enable the SLED2 group (DR[1], DMA1 channel 2).
+ */
+#if !defined(SK32_SLED_USE_SLED2) || defined(__DOXYGEN__)
+#define SK32_SLED_USE_SLED2 FALSE
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type of a SLED output polarity setting.
+ */
+typedef enum {
+ SLED_POLARITY_LOW = 0, /**< @brief Active/idle low level. */
+ SLED_POLARITY_HIGH = 1 /**< @brief Active/idle high level. */
+} sled_polarity_t;
+
+/**
+ * @brief Type of a SLED group identifier.
+ * @note The numeric values are the indices into the DR[] register array
+ * and into the internal group configuration table.
+ */
+typedef enum {
+ SLED1 = 0, /**< @brief SLED1 group. */
+ SLED2 = 1 /**< @brief SLED2 group. */
+} sled_group_t;
+
+/**
+ * @brief Type of a SLED data register write width.
+ * @details The write width selects how the hardware fills the 32 bits wide
+ * FIFO entry when data is written to the group data register, which
+ * determines how the four output channels of a group are driven:
+ * - @p SLED_WIDTH_8BIT: one byte is replicated 4 times into the
+ * FIFO entry, so all four channels carry the same data stream
+ * (the same light effect on CH0..CH3) using the least amount of
+ * RAM;
+ * - @p SLED_WIDTH_16BIT: a half word is replicated twice into the
+ * FIFO entry, so CH0 == CH2 carry one stream and CH1 == CH3 the
+ * other (two simultaneous effects);
+ * - @p SLED_WIDTH_32BIT: a whole word is stored directly into the
+ * FIFO entry, so each of the four channels carries its own byte
+ * (four simultaneous effects).
+ * @note The byte to channel mapping is little endian: byte_i of a FIFO
+ * entry is serialized on channel i (CH0 is the least significant
+ * byte).
+ */
+typedef enum {
+ SLED_WIDTH_8BIT = 0, /**< @brief 8 bit writes (1 byte). */
+ SLED_WIDTH_16BIT = 1, /**< @brief 16 bit writes (2 bytes).*/
+ SLED_WIDTH_32BIT = 2 /**< @brief 32 bit writes (4 bytes).*/
+} sled_width_t;
+
+/**
+ * @brief SLED configuration structure.
+ * @note The time-code and divider values are expressed in SLED clock
+ * cycles, the meaning of each field matches the vendor
+ * SK32F0xx_StdPeriph_Driver SLED_InitTypeDef so that the register
+ * programming performed by the vendor demonstration code can be
+ * ported unchanged:
+ * @code
+ * SLED->TCR = (80 << 16) | (15 << 8) | (4 << 0); // TRST/T1H/T0H
+ * SLED->CDR = (3 << 0) | (31 << 8); // PSC / BRD
+ * @endcode
+ * is written as @p { .t0h_cycles = 4, .t1h_cycles = 15,
+ * .trst_cycles = 80, .prescaler = 3, .baud_prescaler = 31 }.
+ */
+typedef struct {
+ /**
+ * @brief T0H time code (5 bits, bits [4:0] of TCR).
+ */
+ uint8_t t0h_cycles;
+ /**
+ * @brief T1H time code (5 bits, bits [12:8] of TCR).
+ */
+ uint8_t t1h_cycles;
+ /**
+ * @brief Reset code duration (8 bits, bits [23:16] of TCR).
+ */
+ uint8_t trst_cycles;
+ /**
+ * @brief SLED clock prescaler (2 bits, bits [1:0] of CDR).
+ * @note The value selects the SCLK divider: 0..3 = /1../4.
+ */
+ uint8_t prescaler;
+ /**
+ * @brief Baud rate divider (5 bits, bits [12:8] of CDR).
+ * @note The serial bit clock is fSCLK / (baud_prescaler + 1).
+ */
+ uint8_t baud_prescaler;
+ /**
+ * @brief Idle level polarity (CR bit IDLESTS).
+ * @note WS2812 compatible idle level is @p SLED_POLARITY_LOW.
+ */
+ sled_polarity_t idle_polarity;
+ /**
+ * @brief Reset level polarity (CR bit RSTSTS).
+ * @note WS2812 compatible reset level is @p SLED_POLARITY_LOW.
+ */
+ sled_polarity_t reset_polarity;
+} SLEDConfig;
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Returns the CR "reset start" bit of a group.
+ *
+ * @param[in] group the SLED group index
+ * @return The CR RSTSTRx mask.
+ */
+#define sled_lld_rststr_mask(group) \
+ ((group) == SLED1 ? SLED_CR_RSTSTR1 : SLED_CR_RSTSTR2)
+
+/**
+ * @brief Returns the CR "DMA enable" bit of a group.
+ *
+ * @param[in] group the SLED group index
+ * @return The CR DMAENx mask.
+ */
+#define sled_lld_dmaen_mask(group) \
+ ((group) == SLED1 ? SLED_CR_DMAEN1 : SLED_CR_DMAEN2)
+
+/**
+ * @brief Returns the SR "busy" flag of a group.
+ *
+ * @param[in] group the SLED group index
+ * @return The SR BUSYx mask.
+ */
+#define sled_lld_busy_mask(group) \
+ ((group) == SLED1 ? SLED_SR_BUSY1 : SLED_SR_BUSY2)
+
+/**
+ * @brief Returns the FCR FIFO count field of a group.
+ *
+ * @param[in] group the SLED group index
+ * @return The FCR FCNT field mask.
+ */
+#define sled_lld_fcnt_mask(group) \
+ ((group) == SLED1 ? SLED_FCR_FCNT1 : SLED_FCR_FCNT2)
+
+/**
+ * @brief Returns the DR register of a group.
+ *
+ * @param[in] group the SLED group index
+ * @return The DR register index in the DR[] array.
+ */
+#define sled_lld_dr_index(group) \
+ ((group) == SLED1 ? 0U : 1U)
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void sled_lld_init(void);
+ void sled_lld_start(const SLEDConfig *config);
+ void sled_lld_stop(void);
+ msg_t sled_lld_send(sled_group_t group, sled_width_t width,
+ const uint8_t *data, size_t size);
+ msg_t sled_lld_send_bytes(sled_group_t group, const uint8_t *data,
+ size_t size);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_SLED == TRUE */
+
+#endif /* HAL_SLED_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_spi_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_spi_lld.c
new file mode 100644
index 0000000000..36a2138876
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_spi_lld.c
@@ -0,0 +1,397 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_spi_lld.c
+ * @brief SK32F0xx low level SPI driver code.
+ * @details The SK32F0xx SPI is a legacy CR1/CR2/SR/DR unit (SPIv1 class,
+ * no FIFO). Transfers are driven by the RXNE/TXE interrupts, the
+ * end of a transfer is detected on the reception of the last
+ * frame, this is more reliable than watching the TXE flag because
+ * the data register goes empty before the last frame is shifted
+ * out.
+ *
+ * @addtogroup SPI
+ * @{
+ */
+
+#include "hal.h"
+
+#if HAL_USE_SPI || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/**
+ * @brief CR2 bits owned by this driver (interrupt enables).
+ */
+#define SK32_SPI_CR2_IE_MASK ((uint16_t)(SPI_CR2_RXNEIE | \
+ SPI_CR2_TXEIE | \
+ SPI_CR2_ERRIE))
+
+/**
+ * @brief CR2 bits owned by this driver including the DMA request bits
+ * that the interrupt-driven driver never uses.
+ */
+#define SK32_SPI_CR2_OWNED_MASK ((uint16_t)(SK32_SPI_CR2_IE_MASK | \
+ SPI_CR2_RXDMAEN | \
+ SPI_CR2_TXDMAEN))
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/** @brief SPI1 driver identifier.*/
+#if SK32_SPI_USE_SPI1 || defined(__DOXYGEN__)
+SPIDriver SPID1;
+#endif
+
+/** @brief SPI2 driver identifier.*/
+#if SK32_SPI_USE_SPI2 || defined(__DOXYGEN__)
+SPIDriver SPID2;
+#endif
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Starts a transfer.
+ * @note This function is meant to be called with interrupts disabled.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of frames to be transferred
+ * @param[in] txbuf the pointer to the transmit buffer or @p NULL
+ * @param[out] rxbuf the pointer to the receive buffer or @p NULL
+ */
+static void spi_lld_transfer_start(SPIDriver *spip, size_t n,
+ const void *txbuf, void *rxbuf) {
+
+ spip->txbuf = txbuf;
+ spip->rxbuf = rxbuf;
+ spip->txidx = 0U;
+ spip->rxidx = 0U;
+ spip->count = n;
+
+ /* Enabling the sources, the first TXE interrupt writes the first frame
+ into the data register starting the transfer.*/
+ spip->spi->CR2 |= SK32_SPI_CR2_IE_MASK;
+}
+
+/**
+ * @brief Common IRQ handler.
+ * @note The data register must always be read on RXNE even when there is
+ * no receive buffer, leaving received frames unread would raise an
+ * overrun condition.
+ *
+ * @param[in] spip communication channel associated to the SPI
+ */
+static void spi_lld_serve_interrupt(SPIDriver *spip) {
+ SPI_TypeDef *spi = spip->spi;
+ uint16_t cr2 = spi->CR2;
+ uint16_t sr = spi->SR;
+
+ /* Error conditions detection, the transfer is aborted. Both the overrun
+ and the mode fault conditions are cleared by the SR read above followed
+ by a data register read (OVR) or a CR1 write (MODF).*/
+ if (((cr2 & SPI_CR2_ERRIE) != 0U) &&
+ ((sr & (SPI_SR_OVR | SPI_SR_MODF)) != 0U)) {
+ if ((sr & SPI_SR_OVR) != 0U) {
+ (void)spi->DR;
+ }
+ if ((sr & SPI_SR_MODF) != 0U) {
+ spi->CR1 = spi->CR1;
+ }
+ spi->CR2 &= (uint16_t)~SK32_SPI_CR2_IE_MASK;
+ SK32_SPI_ERROR_HOOK(spip);
+ return;
+ }
+
+ /* Data received. The transfer is complete when the last frame has been
+ received, the RXNE counter is the reliable completion indicator on
+ units without a FIFO.*/
+ if (((cr2 & SPI_CR2_RXNEIE) != 0U) && ((sr & SPI_SR_RXNE) != 0U)) {
+ uint16_t dr = spi->DR;
+
+ if (spip->rxbuf != NULL) {
+ if ((spi->CR1 & SPI_CR1_DFF) != 0U) {
+ ((uint16_t *)spip->rxbuf)[spip->rxidx] = dr;
+ }
+ else {
+ ((uint8_t *)spip->rxbuf)[spip->rxidx] = (uint8_t)dr;
+ }
+ }
+ spip->rxidx++;
+ if (spip->rxidx >= spip->count) {
+ spi->CR2 &= (uint16_t)~SK32_SPI_CR2_IE_MASK;
+ _spi_isr_code(spip);
+ return;
+ }
+ }
+
+ /* Data to be transmitted. When there is no transmit buffer a dummy
+ all-ones frame is sent (receive-only and ignore operations).*/
+ if (((cr2 & SPI_CR2_TXEIE) != 0U) && ((sr & SPI_SR_TXE) != 0U)) {
+ if (spip->txidx < spip->count) {
+ uint16_t frame = 0xFFFFU;
+
+ if (spip->txbuf != NULL) {
+ if ((spi->CR1 & SPI_CR1_DFF) != 0U) {
+ frame = ((const uint16_t *)spip->txbuf)[spip->txidx];
+ }
+ else {
+ frame = ((const uint8_t *)spip->txbuf)[spip->txidx];
+ }
+ }
+ spi->DR = frame;
+ spip->txidx++;
+ }
+ else {
+ /* All the frames have been queued, waiting for the last reception
+ to complete the operation.*/
+ spi->CR2 &= (uint16_t)~(uint16_t)SPI_CR2_TXEIE;
+ }
+ }
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if SK32_SPI_USE_SPI1 || defined(__DOXYGEN__)
+#if !defined(SK32_SPI1_SUPPRESS_ISR)
+#if !defined(SK32_SPI1_HANDLER)
+#error "SK32_SPI1_HANDLER not defined"
+#endif
+/**
+ * @brief SPI1 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_SPI1_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_lld_serve_interrupt(&SPID1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+#endif
+
+#if SK32_SPI_USE_SPI2 || defined(__DOXYGEN__)
+#if !defined(SK32_SPI2_SUPPRESS_ISR)
+#if !defined(SK32_SPI2_HANDLER)
+#error "SK32_SPI2_HANDLER not defined"
+#endif
+/**
+ * @brief SPI2 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_SPI2_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ spi_lld_serve_interrupt(&SPID2);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+#endif
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level SPI driver initialization.
+ *
+ * @notapi
+ */
+void spi_lld_init(void) {
+
+#if SK32_SPI_USE_SPI1
+ spiObjectInit(&SPID1);
+ SPID1.spi = SPI1;
+#if !defined(SK32_SPI1_SUPPRESS_ISR) && defined(SK32_SPI1_NUMBER)
+ nvicEnableVector(SK32_SPI1_NUMBER, SK32_SPI_SPI1_PRIORITY);
+#endif
+#endif
+
+#if SK32_SPI_USE_SPI2
+ spiObjectInit(&SPID2);
+ SPID2.spi = SPI2;
+#if !defined(SK32_SPI2_SUPPRESS_ISR) && defined(SK32_SPI2_NUMBER)
+ nvicEnableVector(SK32_SPI2_NUMBER, SK32_SPI_SPI2_PRIORITY);
+#endif
+#endif
+}
+
+/**
+ * @brief Configures and activates the SPI peripheral.
+ * @note Master mode with software slave management is forced on, the
+ * NSS pin is therefore available as a generic I/O pin and the
+ * chip select is handled by the upper driver (if configured).
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_start(SPIDriver *spip) {
+
+ /* If in stopped state then enables the SPI clock.*/
+ if (spip->state == SPI_STOP) {
+#if SK32_SPI_USE_SPI1
+ if (&SPID1 == spip) {
+ rccEnableSPI1(true);
+ }
+#endif
+#if SK32_SPI_USE_SPI2
+ if (&SPID2 == spip) {
+ rccEnableSPI2(true);
+ }
+#endif
+ }
+
+ /* SPI setup and enable.*/
+ spip->spi->CR1 &= (uint16_t)~SPI_CR1_SPE;
+ spip->spi->CR1 = spip->config->cr1 | SPI_CR1_MSTR | SPI_CR1_SSM |
+ SPI_CR1_SSI;
+ spip->spi->CR2 = spip->config->cr2 & (uint16_t)~SK32_SPI_CR2_OWNED_MASK;
+ spip->spi->CR1 |= SPI_CR1_SPE;
+
+ /* Making sure that no stale received frame can fool the next transfer
+ into reading a spurious byte as its first frame.*/
+ (void)spip->spi->SR;
+ (void)spip->spi->DR;
+}
+
+/**
+ * @brief Deactivates the SPI peripheral.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ *
+ * @notapi
+ */
+void spi_lld_stop(SPIDriver *spip) {
+
+ /* If in ready state then disables the SPI clock.*/
+ if (spip->state == SPI_READY) {
+ spip->spi->CR1 &= (uint16_t)~SPI_CR1_SPE;
+ spip->spi->CR1 = 0U;
+ spip->spi->CR2 = 0U;
+
+#if SK32_SPI_USE_SPI1
+ if (&SPID1 == spip) {
+ rccDisableSPI1();
+ }
+#endif
+#if SK32_SPI_USE_SPI2
+ if (&SPID2 == spip) {
+ rccDisableSPI2();
+ }
+#endif
+ }
+}
+
+/**
+ * @brief Ignores data on the SPI bus.
+ * @details This asynchronous function starts an receive process and ignores
+ * the received data.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of frames to send and discard
+ *
+ * @notapi
+ */
+void spi_lld_ignore(SPIDriver *spip, size_t n) {
+
+ spi_lld_transfer_start(spip, n, NULL, NULL);
+}
+
+/**
+ * @brief Exchanges data on the SPI bus.
+ * @details This asynchronous function starts a simultaneous transmit/receive
+ * operation.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of frames to send and receive
+ * @param[in] txbuf the pointer to the transmit buffer
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @notapi
+ */
+void spi_lld_exchange(SPIDriver *spip, size_t n,
+ const void *txbuf, void *rxbuf) {
+
+ spi_lld_transfer_start(spip, n, txbuf, rxbuf);
+}
+
+/**
+ * @brief Sends data over the SPI bus.
+ * @details This asynchronous function starts a transmit operation.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of frames to send
+ * @param[in] txbuf the pointer to the transmit buffer
+ *
+ * @notapi
+ */
+void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf) {
+
+ spi_lld_transfer_start(spip, n, txbuf, NULL);
+}
+
+/**
+ * @brief Receives data from the SPI bus.
+ * @details This asynchronous function starts a receive operation.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] n number of frames to receive
+ * @param[out] rxbuf the pointer to the receive buffer
+ *
+ * @notapi
+ */
+void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf) {
+
+ spi_lld_transfer_start(spip, n, NULL, rxbuf);
+}
+
+/**
+ * @brief Exchanges one frame using a polled wait.
+ * @details This synchronous function exchanges one frame using a polled
+ * synchronization method.
+ *
+ * @param[in] spip pointer to the @p SPIDriver object
+ * @param[in] frame the data frame to send over the SPI bus
+ * @return The received data frame from the SPI bus.
+ *
+ * @notapi
+ */
+uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame) {
+
+ spip->spi->DR = frame;
+ while ((spip->spi->SR & SPI_SR_RXNE) == 0U)
+ ;
+ return spip->spi->DR;
+}
+
+#endif /* HAL_USE_SPI */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_spi_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_spi_lld.h
new file mode 100644
index 0000000000..e9127dc24e
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_spi_lld.h
@@ -0,0 +1,268 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_spi_lld.h
+ * @brief SK32F0xx low level SPI driver header.
+ * @details The SK32F0xx SPI is a legacy CR1/CR2/SR/DR unit (SPIv1 class, the
+ * same register set used by the STM32F1 family, no FIFO). This
+ * driver is interrupt-driven (RXNE/TXE/ERRIE) and is written
+ * directly against the vendor CMSIS header (@p sk32f0xx.h), no
+ * STM32 port code and no DMA are involved.
+ *
+ * @addtogroup SPI
+ * @{
+ */
+
+#ifndef HAL_SPI_LLD_H
+#define HAL_SPI_LLD_H
+
+#if HAL_USE_SPI || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief Circular mode support flag.
+ */
+#define SPI_SUPPORTS_CIRCULAR FALSE
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief SPI1 driver enable switch.
+ * @details If set to @p TRUE the support for SPI1 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SK32_SPI_USE_SPI1) || defined(__DOXYGEN__)
+#define SK32_SPI_USE_SPI1 FALSE
+#endif
+
+/**
+ * @brief SPI2 driver enable switch.
+ * @details If set to @p TRUE the support for SPI2 is included.
+ * @note The default is @p FALSE.
+ */
+#if !defined(SK32_SPI_USE_SPI2) || defined(__DOXYGEN__)
+#define SK32_SPI_USE_SPI2 FALSE
+#endif
+
+/**
+ * @brief SPI1 interrupt priority level setting.
+ * @note ARMv6-M (Cortex-M0) implements only 2 priority bits, the valid
+ * priority range is 0 (highest) .. 3 (lowest).
+ */
+#if !defined(SK32_SPI_SPI1_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_SPI_SPI1_PRIORITY 3
+#endif
+
+/**
+ * @brief SPI2 interrupt priority level setting.
+ */
+#if !defined(SK32_SPI_SPI2_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_SPI_SPI2_PRIORITY 3
+#endif
+
+/**
+ * @brief SPI error hook.
+ * @details This hook is invoked from the ISR context when an overrun or a
+ * mode fault aborts an ongoing transfer.
+ */
+#if !defined(SK32_SPI_ERROR_HOOK) || defined(__DOXYGEN__)
+#define SK32_SPI_ERROR_HOOK(spip) osalSysHalt("SPI failure")
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if SK32_SPI_USE_SPI1 && !SK32_HAS_SPI1
+#error "SPI1 not present in the selected device"
+#endif
+
+#if SK32_SPI_USE_SPI2 && !SK32_HAS_SPI2
+#error "SPI2 not present in the selected device"
+#endif
+
+#if !SK32_SPI_USE_SPI1 && !SK32_SPI_USE_SPI2
+#error "SPI driver activated but no SPI peripheral assigned"
+#endif
+
+#if SK32_SPI_USE_SPI1 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(SK32_SPI_SPI1_PRIORITY)
+#error "Invalid IRQ priority assigned to SPI1"
+#endif
+
+#if SK32_SPI_USE_SPI2 && \
+ !OSAL_IRQ_IS_VALID_PRIORITY(SK32_SPI_SPI2_PRIORITY)
+#error "Invalid IRQ priority assigned to SPI2"
+#endif
+
+/* Checks on allocation of SPI units.*/
+#if SK32_SPI_USE_SPI1
+#if defined(SK32_SPI1_IS_USED)
+#error "SPID1 requires SPI1 but it is already used"
+#else
+#define SK32_SPI1_IS_USED
+#endif
+#endif
+
+#if SK32_SPI_USE_SPI2
+#if defined(SK32_SPI2_IS_USED)
+#error "SPID2 requires SPI2 but it is already used"
+#else
+#define SK32_SPI2_IS_USED
+#endif
+#endif
+
+#if SPI_SELECT_MODE == SPI_SELECT_MODE_LLD
+#error "SPI_SELECT_MODE_LLD not supported by this driver"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @name CR1 helpers
+ * @{
+ */
+/**
+ * @brief Data frame format bit.
+ * @details The vendor CMSIS header names the CR1 bit 11 "CRCL" (the STM32F0
+ * SPI convention) but the vendor StdPeriph driver uses it as the
+ * 8/16 bit data frame format selector (@p SPI_DataSize_16b is
+ * defined to 0x0800, the STM32F1 @p DFF convention). It is
+ * aliased here with the name used by the ChibiOS SPIv1 drivers.
+ */
+#if !defined(SPI_CR1_DFF) || defined(__DOXYGEN__)
+#define SPI_CR1_DFF ((uint16_t)0x0800)
+#endif
+
+/**
+ * @brief Baud rate selection: PCLK2 divided by 2.
+ */
+#define SK32_SPI_BR_PCLK_DIV2 (0U << 3)
+/**
+ * @brief Baud rate selection: PCLK2 divided by 4.
+ */
+#define SK32_SPI_BR_PCLK_DIV4 (1U << 3)
+/**
+ * @brief Baud rate selection: PCLK2 divided by 8.
+ */
+#define SK32_SPI_BR_PCLK_DIV8 (2U << 3)
+/**
+ * @brief Baud rate selection: PCLK2 divided by 16.
+ */
+#define SK32_SPI_BR_PCLK_DIV16 (3U << 3)
+/**
+ * @brief Baud rate selection: PCLK2 divided by 32.
+ */
+#define SK32_SPI_BR_PCLK_DIV32 (4U << 3)
+/**
+ * @brief Baud rate selection: PCLK2 divided by 64.
+ */
+#define SK32_SPI_BR_PCLK_DIV64 (5U << 3)
+/**
+ * @brief Baud rate selection: PCLK2 divided by 128.
+ */
+#define SK32_SPI_BR_PCLK_DIV128 (6U << 3)
+/**
+ * @brief Baud rate selection: PCLK2 divided by 256.
+ */
+#define SK32_SPI_BR_PCLK_DIV256 (7U << 3)
+/** @} */
+
+/**
+ * @brief Low level fields of the SPI driver structure.
+ * @note The transfer state is owned by the driver ISR, @p txidx counts
+ * the frames written to the data register while @p rxidx counts
+ * the frames read back; the transfer is complete when the last
+ * frame has been received.
+ */
+#define spi_lld_driver_fields \
+ /* Pointer to the SPIx registers block.*/ \
+ SPI_TypeDef *spi; \
+ /* Pointer to the transmit buffer or NULL.*/ \
+ const void *txbuf; \
+ /* Pointer to the receive buffer or NULL.*/ \
+ void *rxbuf; \
+ /* Number of frames written to the data register.*/ \
+ size_t txidx; \
+ /* Number of frames read from the data register.*/ \
+ size_t rxidx; \
+ /* Total number of frames of the current transfer.*/ \
+ size_t count;
+
+/**
+ * @brief Low level fields of the SPI configuration structure.
+ * @details The CR1 content is applied to the peripheral with the master
+ * mode and the software slave management bits forced on. The
+ * CR2 interrupt enable bits (RXNEIE, TXEIE, ERRIE) and the DMA
+ * request bits are owned by this driver and masked out of the
+ * configured value.
+ */
+#define spi_lld_config_fields \
+ /* SPI CR1 register initialization data.*/ \
+ uint16_t cr1; \
+ /* SPI CR2 register initialization data.*/ \
+ uint16_t cr2;
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if SK32_SPI_USE_SPI1 && !defined(__DOXYGEN__)
+extern SPIDriver SPID1;
+#endif
+#if SK32_SPI_USE_SPI2 && !defined(__DOXYGEN__)
+extern SPIDriver SPID2;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void spi_lld_init(void);
+ void spi_lld_start(SPIDriver *spip);
+ void spi_lld_stop(SPIDriver *spip);
+ void spi_lld_ignore(SPIDriver *spip, size_t n);
+ void spi_lld_exchange(SPIDriver *spip, size_t n,
+ const void *txbuf, void *rxbuf);
+ void spi_lld_send(SPIDriver *spip, size_t n, const void *txbuf);
+ void spi_lld_receive(SPIDriver *spip, size_t n, void *rxbuf);
+ uint16_t spi_lld_polled_exchange(SPIDriver *spip, uint16_t frame);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_SPI */
+
+#endif /* HAL_SPI_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_st_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_st_lld.c
new file mode 100644
index 0000000000..9a6325a349
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_st_lld.c
@@ -0,0 +1,124 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_st_lld.c
+ * @brief SK32 ST Driver subsystem low level driver code.
+ * @details This driver implements the periodic tick using the Cortex-M0
+ * SysTick counter. The SK32F0xx family is register-compatible with
+ * the STM32F0 family, the configuration macro set is the same one
+ * used by the other SK32 low level drivers (@p SK32_* naming).
+ *
+ * @addtogroup ST
+ * @{
+ */
+
+#include "hal.h"
+
+#if (OSAL_ST_MODE != OSAL_ST_MODE_NONE) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+#if OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC
+
+#define ST_HANDLER SysTick_Handler
+#define SYSTICK_CK SK32_HCLK
+
+#if SYSTICK_CK % OSAL_ST_FREQUENCY != 0
+#error "the selected ST frequency is not obtainable because integer rounding"
+#endif
+
+#if (SYSTICK_CK / OSAL_ST_FREQUENCY) - 1 > 0xFFFFFF
+#error "the selected ST frequency is not obtainable because SysTick timer counter limits"
+#endif
+
+#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/**
+ * @brief Interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(ST_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ st_lld_serve_interrupt();
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level ST driver initialization.
+ *
+ * @notapi
+ */
+void st_lld_init(void) {
+
+#if OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC
+ /* Periodic systick mode, the Cortex-M0 internal systick timer is used
+ in this mode. The SysTick clock source is HCLK (SK32_HCLK).*/
+ SysTick->LOAD = (SYSTICK_CK / OSAL_ST_FREQUENCY) - 1;
+ SysTick->VAL = 0;
+ SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
+ SysTick_CTRL_ENABLE_Msk |
+ SysTick_CTRL_TICKINT_Msk;
+
+ /* IRQ enabled.*/
+ nvicSetSystemHandlerPriority(HANDLER_SYSTICK, SK32_ST_IRQ_PRIORITY);
+#endif /* OSAL_ST_MODE == OSAL_ST_MODE_PERIODIC */
+}
+
+/**
+ * @brief IRQ handling code.
+ */
+void st_lld_serve_interrupt(void) {
+
+ osalSysLockFromISR();
+ osalOsTimerHandlerI();
+ osalSysUnlockFromISR();
+}
+
+#endif /* OSAL_ST_MODE != OSAL_ST_MODE_NONE */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_st_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_st_lld.h
new file mode 100644
index 0000000000..09b75f97c4
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_st_lld.h
@@ -0,0 +1,85 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_st_lld.h
+ * @brief SK32 ST Driver subsystem low level driver header.
+ * @details The SK32F0xx ST low level driver uses the Cortex-M0 SysTick
+ * counter in periodic mode (@p OSAL_ST_MODE_PERIODIC), free running
+ * mode is not supported because it would require a general purpose
+ * timer which is not guaranteed to be present on every SK32F0xx
+ * device.
+ *
+ * @addtogroup ST
+ * @{
+ */
+
+#ifndef HAL_ST_LLD_H
+#define HAL_ST_LLD_H
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief SysTick exception priority.
+ */
+#if !defined(SK32_ST_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_ST_IRQ_PRIORITY 8
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if OSAL_ST_MODE == OSAL_ST_MODE_FREERUNNING
+#error "the SK32F0xx ST LLD only supports periodic mode (set CH_CFG_ST_TIMEDELTA to zero)"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void st_lld_init(void);
+ void st_lld_serve_interrupt(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_ST_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_usb_lld.c b/os/hal/ports/SK32/SK32F0xx/hal_usb_lld.c
new file mode 100644
index 0000000000..5fe291b2bc
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_usb_lld.c
@@ -0,0 +1,1380 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ SK32F0xx USB LLD - 3Think SK32F077 (Mentor/MUSB musbfsfc class USB FS controller)
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_usb_lld.c
+ * @brief SK32 USB subsystem low level driver source.
+ * @details The SK32F077 embeds a Mentor/MUSB-class (musbfsfc) full speed
+ * USB device controller. Its register file (all 8 bit registers,
+ * endpoint register windows selected through the INDEX register and
+ * direct 8 bit FIFO windows) is completely different from the
+ * STM32F0 USB-PMA peripheral used on the STM32F072 the chip is
+ * otherwise compatible with. This driver implements the standard
+ * ChibiOS USB low level driver API on top of that controller.
+ *
+ * The register level programming sequences mirror the 3Think
+ * CherryUSB port (usb_dc_musbfsfc.c) shipped in the
+ * "SK32F0xx_Firmware Package": the controller is used in
+ * "manual" mode (no AUTOSET/AUTOCLR), each IN/OUT transaction is
+ * serviced in the single USB interrupt (vector 31).
+ *
+ * Control (EP0) transfers are entirely driven by the high level
+ * state machine in hal_usb.c through the regular
+ * usbStartTransmitI()/usbStartReceiveI() calls (software status
+ * stage, USB_EP0_STATUS_STAGE_SW); this driver only translates the
+ * controller events into the expected *_cb invocations.
+ *
+ * @addtogroup USB
+ * @{
+ */
+
+#include
+
+#include "hal.h"
+
+#if HAL_USE_USB || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/**
+ * @brief INTRIN1/INTRIN1E bit of EP0.
+ */
+#define SK32_EP0_IRQ_BIT 0x01U
+
+/**
+ * @brief PWR->CR bits controlling the internal USB PHY LDO.
+ * @note These bits are a SK32 extension and are not defined in the
+ * standard STM32F072 CMSIS header.
+ */
+#define SK32_PWR_CR_LDO_VS_MASK (3U << 18U) /**< PHY voltage range. */
+#define SK32_PWR_CR_LDO_SEL (1U << 17U) /**< 0=VDD, 1=LDO. */
+#define SK32_PWR_CR_LDO_EN (1U << 16U) /**< LDO enable. */
+/**< 3.3V range, supply from the internal LDO, LDO enabled. */
+#define SK32_PWR_CR_PHY_3V3_LDO (SK32_PWR_CR_LDO_EN | SK32_PWR_CR_LDO_SEL)
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/** @brief USB1 driver identifier.*/
+#if SK32_USB_USE_USB1 || defined(__DOXYGEN__)
+USBDriver USBD1;
+#endif
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/**
+ * @brief EP0 state.
+ * @note It is an union because IN and OUT endpoints are never used at the
+ * same time for EP0.
+ */
+static union {
+ /**
+ * @brief IN EP0 state.
+ */
+ USBInEndpointState in;
+ /**
+ * @brief OUT EP0 state.
+ */
+ USBOutEndpointState out;
+} ep0_state;
+
+/**
+ * @brief Buffer for the EP0 setup packets.
+ */
+static uint8_t ep0setup_buffer[8];
+
+/**
+ * @brief EP0 initialization structure.
+ */
+static const USBEndpointConfig ep0config = {
+ .ep_mode = USB_EP_MODE_TYPE_CTRL,
+ .setup_cb = _usb_ep0setup,
+ .in_cb = _usb_ep0in,
+ .out_cb = _usb_ep0out,
+ .in_maxsize = 0x40U,
+ .out_maxsize = 0x40U,
+ .in_state = &ep0_state.in,
+ .out_state = &ep0_state.out,
+ .ep_buffers = 1U,
+ .setup_buf = ep0setup_buffer
+};
+
+/**
+ * @brief Set when an EP0 SETUP packet is being served by the ISR and the
+ * setup data has still to be popped from the FIFO by the high level
+ * layer (usb_lld_read_setup()). The OPKTRD service must be performed
+ * after the FIFO pop, exactly like the vendor driver does.
+ */
+static bool ep0_setup_pending = false;
+
+/**
+ * @brief Set when the last packet of an EP0 OUT data phase has been
+ * serviced with DATEND.
+ * @details On this controller DATEND tells the hardware that the data phase
+ * of the control transfer is over, the status stage is then
+ * completed by the hardware without generating any further EP0
+ * event. For a control write (OUT data phase) the status stage is
+ * an IN zero length packet that the controller sends by itself,
+ * so usb_lld_start_in() must not arm a second one when the high
+ * level layer asks for the status packet: the transfer completion
+ * is synthesized instead.
+ */
+static bool ep0_out_status_pending = false;
+
+#if defined(SK32_USB_TRACE)
+/*===========================================================================*/
+/* USB bring-up trace. */
+/* An ISR-safe byte ring records raw USB events (EP0 CSR0 snapshots, state */
+/* machine transitions, transfers arms). The board firmware drains the ring */
+/* from a thread and prints it on the debug UART. This block is only compiled */
+/* when SK32_USB_TRACE is defined (bring-up aid, normally disabled). */
+/*===========================================================================*/
+#define USB_TRACE_RING_SIZE 2048U
+
+static volatile uint8_t usb_trace_ring[USB_TRACE_RING_SIZE];
+static volatile uint16_t usb_trace_wr;
+static volatile uint16_t usb_trace_rd;
+
+static void usb_trace_byte(uint8_t b) {
+ uint16_t wr;
+ uint16_t next;
+
+ wr = usb_trace_wr;
+ next = (uint16_t)(wr + 1U);
+ if (next >= USB_TRACE_RING_SIZE) {
+ next = 0U;
+ }
+ if (next != usb_trace_rd) {
+ usb_trace_ring[wr] = b;
+ usb_trace_wr = next;
+ }
+}
+
+static void usb_trace_event(uint8_t tag, uint8_t a, uint8_t b, uint8_t c) {
+ usb_trace_byte(tag);
+ usb_trace_byte(a);
+ usb_trace_byte(b);
+ usb_trace_byte(c);
+}
+
+static uint8_t usb_trace_frame(void) {
+ return SK32_USB->FRAME1;
+}
+
+static void usb_trace_bytes(uint8_t tag, const uint8_t *buf, size_t n) {
+ size_t i;
+ usb_trace_byte(tag);
+ for (i = 0U; i < n; i++) {
+ usb_trace_byte(buf[i]);
+ }
+}
+
+size_t sk32_usb_trace_drain(uint8_t *bp, size_t max) {
+ size_t i;
+
+ i = 0U;
+ while ((i < max) && (usb_trace_rd != usb_trace_wr)) {
+ bp[i] = usb_trace_ring[usb_trace_rd];
+ i++;
+ usb_trace_rd = (uint16_t)(usb_trace_rd + 1U);
+ if (usb_trace_rd >= USB_TRACE_RING_SIZE) {
+ usb_trace_rd = 0U;
+ }
+ }
+ return i;
+}
+
+/* Export a trace emission entry point so board level code (QMK hooks such
+ as matrix_scan_user/process_record_user) can tag application events into
+ the same ring buffer. Only used as a bring-up aid.*/
+void sk32_usb_trace_emit(uint8_t tag, uint8_t a, uint8_t b, uint8_t c) {
+ usb_trace_event(tag, a, b, c);
+}
+#endif /* SK32_USB_TRACE */
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Selects an endpoint register window through the INDEX register.
+ *
+ * @param[in] ep endpoint number (0..7)
+ * @return The previous INDEX register value.
+ *
+ * @notapi
+ */
+static uint8_t usb_ep_select(uint8_t ep) {
+ uint8_t old_index;
+
+ old_index = SK32_USB->INDEX;
+ SK32_USB->INDEX = ep;
+ return old_index;
+}
+
+/**
+ * @brief Writes a packet payload into an endpoint FIFO.
+ *
+ * @param[in] ep endpoint number
+ * @param[in] buf buffer where to fetch the packet data
+ * @param[in] n number of bytes to write
+ *
+ * @notapi
+ */
+static void usb_fifo_write(uint8_t ep, const uint8_t *buf, size_t n) {
+
+ /* The FIFO windows are directly addressed, no INDEX selection needed.*/
+ while (n > 0U) {
+ SK32_USB->FIFO[ep] = *buf++;
+ n--;
+ }
+}
+
+/**
+ * @brief Reads a received packet payload from an endpoint FIFO.
+ *
+ * @param[in] ep endpoint number
+ * @param[in] buf buffer where to copy the packet data
+ * @param[in] n number of bytes to read
+ *
+ * @notapi
+ */
+static void usb_fifo_read(uint8_t ep, uint8_t *buf, size_t n) {
+
+ /* The FIFO windows are directly addressed, no INDEX selection needed.*/
+ while (n > 0U) {
+ *buf++ = SK32_USB->FIFO[ep];
+ n--;
+ }
+}
+
+/**
+ * @brief Reads the received packet byte count of the selected endpoint.
+ *
+ * @return The received byte count.
+ *
+ * @notapi
+ */
+static size_t usb_get_rx_count(void) {
+
+ return (size_t)SK32_USB->COUNT |
+ ((size_t)SK32_USB->OUTCOUNT2 << 8U);
+}
+
+/**
+ * @brief Decides whether DATEND must be set together with IPKTRD when
+ * arming an EP0 IN data packet.
+ * @details On this controller the data stage of a control read is closed by
+ * setting DATEND on the very packet that carries the last data of
+ * the stage. Until DATEND is set the controller keeps NAKing the
+ * host's status OUT packet, so the host never gets its
+ * acknowledgement and eventually resets the bus. DATEND must be set
+ * when the packet being loaded empties the whole data stage:
+ * - a short packet (n < MPS, including a terminating zero length
+ * data packet) always ends the data stage;
+ * - a full size packet ends the data stage only when it delivers the
+ * whole requested wLength (no trailing zero length packet will be
+ * requested by the high level layer afterwards).
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] txsize total size of the current high level IN transfer
+ * @param[in] left bytes still to transfer after the packet being armed
+ * @param[in] n size of the packet being armed
+ * @return true if DATEND must be set together with IPKTRD.
+ */
+static bool usb_ep0_in_datend(const USBDriver *usbp, size_t txsize,
+ size_t left, size_t n) {
+ size_t wl;
+
+ if (left > 0U) {
+ return false;
+ }
+ if (n < (size_t)usbp->epc[0]->in_maxsize) {
+ return true;
+ }
+ wl = (size_t)usbp->setup[6] | ((size_t)usbp->setup[7] << 8U);
+ return txsize >= wl;
+}
+
+/**
+ * @brief Common ISR code, IN endpoint transmission.
+ * @details Invoked when an IN packet previously loaded with IPKTRD has been
+ * transmitted on the bus. If the transfer has not been completed
+ * the next packet is loaded, otherwise the IN callback is invoked.
+ * @pre The INDEX register must select the endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ *
+ * @notapi
+ */
+static void usb_serve_in(USBDriver *usbp, usbep_t ep) {
+ size_t n;
+ const USBEndpointConfig *epcp = usbp->epc[ep];
+ USBInEndpointState *isp = epcp->in_state;
+
+ /* A transmit underrun condition is acknowledged (IN endpoints only, the
+ CSR register of EP0 is the CSR0 register with a different bit map).*/
+ if (ep != 0U) {
+ if ((SK32_USB->CSR & SK32_INCSR1_UNDERRUN) != 0U) {
+ SK32_USB->CSR &= (uint8_t)~SK32_INCSR1_UNDERRUN;
+ }
+ }
+
+ /* Only meaningful if the high level layer has a transmit pending.*/
+ if ((usbp->transmitting & (uint16_t)((unsigned)1U << (unsigned)ep)) == 0U) {
+ return;
+ }
+
+ /* Accumulating the size of the packet whose transmission has just been
+ acknowledged by the controller.*/
+ isp->txcnt += isp->txlast;
+ n = isp->txsize - isp->txcnt;
+ if (n > 0U) {
+ /* Transfer not completed, loading the next packet.*/
+ if (n > (size_t)epcp->in_maxsize) {
+ n = (size_t)epcp->in_maxsize;
+ }
+ isp->txbuf += isp->txlast;
+ isp->txlast = n;
+ usb_fifo_write(ep, isp->txbuf, n);
+
+ if (ep == 0U) {
+ /* On this controller the data stage of a control read is closed by
+ DATEND set together with the last data packet (a short packet, a
+ full size packet delivering the whole requested wLength or the
+ terminating zero length packet). Without DATEND the controller
+ would keep NAKing the host's status OUT packet and the control
+ transfer would never complete, forcing the host to reset the bus.*/
+ if (usb_ep0_in_datend(usbp, isp->txsize,
+ (isp->txsize - isp->txcnt) - n, n)) {
+ SK32_USB->CSR |= SK32_CSR0_DATEND | SK32_CSR0_IPKTRD;
+ }
+ else {
+ SK32_USB->CSR |= SK32_CSR0_IPKTRD;
+ }
+ }
+ else {
+ SK32_USB->CSR |= SK32_INCSR1_IPKTRD;
+ }
+ }
+ else {
+ /* Transfer completed, invoking the callback.*/
+#if defined(SK32_USB_TRACE)
+ usb_trace_event('N', (uint8_t)ep, (uint8_t)isp->txlast, 0U);
+#endif
+ _usb_isr_invoke_in_cb(usbp, ep);
+ }
+}
+
+/**
+ * @brief Common ISR code, OUT endpoint reception.
+ * @details Invoked when an OUT packet has been received (OPKTRD set). The
+ * packet is read from the FIFO and the receive operation is
+ * re-armed; the OUT callback is invoked when the transfer is
+ * complete (short packet received or expected packet count reached).
+ * @pre The INDEX register must select the endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ *
+ * @notapi
+ */
+static void usb_serve_out(USBDriver *usbp, usbep_t ep) {
+ size_t n;
+ const USBEndpointConfig *epcp = usbp->epc[ep];
+ USBOutEndpointState *osp = epcp->out_state;
+
+ /* Endpoint 0 uses the CSR0 register, the other endpoints the OUTCSR1
+ register, but the OPKTRD flag is bit 0 in both cases.*/
+ if (ep == 0U) {
+ if ((SK32_USB->CSR & SK32_CSR0_OPKTRD) == 0U) {
+ return;
+ }
+ }
+ else {
+ if ((SK32_USB->OUTCSR1 & SK32_OUTCSR1_OPKTRD) == 0U) {
+ return;
+ }
+ }
+
+ /* Unsolicited packet, no high level receive in progress: the packet is
+ discarded and the endpoint re-armed to avoid locking out the host.*/
+ if ((usbp->receiving & (uint16_t)((unsigned)1U << (unsigned)ep)) == 0U) {
+ if (ep == 0U) {
+ SK32_USB->CSR |= SK32_CSR0_SEROPKTRD;
+ }
+ else {
+ SK32_USB->OUTCSR1 &= (uint8_t)~SK32_OUTCSR1_OPKTRD;
+ }
+ return;
+ }
+
+ /* Reading the received packet from the FIFO.*/
+ n = usb_get_rx_count();
+ if ((ep == 0U) && (osp->rxbuf == NULL) && (osp->rxsize == 0U)) {
+ /* Zero sized status stage reception armed by the high level layer: any
+ received byte count is forced to zero (status packets are zero
+ length by definition) so that the counters below cannot underflow
+ and the FIFO is not popped into a NULL buffer.*/
+ n = 0U;
+ }
+ if ((n > 0U) && (osp->rxbuf != NULL)) {
+ usb_fifo_read(ep, osp->rxbuf, n);
+ }
+
+ /* Servicing OPKTRD, this re-enables the reception of the next packet.
+ On EP0 the service of the last packet of the transfer also sets
+ DATEND: the controller then closes the control transfer and handles
+ the status stage by itself without generating any further EP0 event
+ (exactly like the vendor driver does on the last OUT data packet).*/
+ if (ep == 0U) {
+ if ((osp->rxbuf == NULL) && (osp->rxsize == 0U)) {
+ /* Host's zero length status packet of a control read transfer: the
+ control transfer is completed on the controller side by DATEND
+ (the packet has already been acknowledged on the bus, this only
+ closes the controller state machine).*/
+ SK32_USB->CSR |= SK32_CSR0_SEROPKTRD | SK32_CSR0_DATEND;
+ }
+ else {
+ if ((n < (size_t)epcp->out_maxsize) || (osp->rxpkts == 1U)) {
+ /* Last packet of the OUT data phase of a control write transfer:
+ DATEND makes the controller send the status IN zero length
+ packet by itself, usb_lld_start_in() will synthesize the
+ transfer completion instead of arming a second packet.*/
+ SK32_USB->CSR |= SK32_CSR0_SEROPKTRD | SK32_CSR0_DATEND;
+ ep0_out_status_pending = true;
+ }
+ else {
+ SK32_USB->CSR |= SK32_CSR0_SEROPKTRD;
+ }
+ }
+ }
+ else {
+ SK32_USB->OUTCSR1 &= (uint8_t)~SK32_OUTCSR1_OPKTRD;
+ }
+
+ /* Transaction data updated.*/
+ if (osp->rxbuf != NULL) {
+ osp->rxbuf += n;
+ }
+ osp->rxcnt += n;
+ osp->rxsize -= n;
+ osp->rxpkts -= 1U;
+
+ /* The transaction is completed if the expected number of packets has been
+ received or the current packet is a short packet.*/
+ if ((n < (size_t)epcp->out_maxsize) || (osp->rxpkts == 0U)) {
+ /* Transfer complete, invoking the callback.*/
+ _usb_isr_invoke_out_cb(usbp, ep);
+ }
+}
+
+/**
+ * @brief Handles an EP0 SETUP packet.
+ * @details The FIFO contains the 8 bytes setup packet. The high level setup
+ * callback is invoked; the callback pops the setup data through
+ * usbReadSetup()/usb_lld_read_setup() which also performs the
+ * OPKTRD service (the packet must be read before being serviced,
+ * exactly as done by the vendor driver).
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+static void usb_serve_ep0_setup(USBDriver *usbp) {
+
+ ep0_setup_pending = true;
+ _usb_isr_invoke_setup_cb(usbp, 0);
+
+ /* The setup callback did not consume the packet, discarding it.*/
+ if (ep0_setup_pending) {
+ SK32_USB->CSR |= SK32_CSR0_SEROPKTRD;
+ ep0_setup_pending = false;
+ }
+#if defined(SK32_USB_TRACE)
+ usb_trace_bytes('S', usbp->setup, 8U);
+#endif
+}
+
+/**
+ * @brief Common ISR code for EP0.
+ * @details EP0 events (SETUP packets, control data packets and IN packet
+ * transmission completions) are all reported on the INTRIN1 bit 0.
+ * The CSR0 flags and the high level EP0 state machine are used to
+ * tell the event kinds apart.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+static void usb_serve_ep0(USBDriver *usbp) {
+ uint8_t csr;
+ uint8_t old_index;
+ bool setup_aborted;
+
+ old_index = usb_ep_select(0);
+ csr = SK32_USB->CSR;
+#if defined(SK32_USB_TRACE)
+ /* The event carries the received byte count (EP0 packets are at most 64
+ bytes so the low byte is enough) instead of the frame number; the
+ paired 'X' event still records the frame number. The count allows the
+ trace parser to tell a genuine zero length OUT status packet (count 0)
+ from the 8 bytes SETUP of the next control transfer (count 8).*/
+ usb_trace_event('E', csr, (uint8_t)usbp->ep0state,
+ (uint8_t)usb_get_rx_count());
+#endif
+
+ /* SETUPEND is set when a new SETUP packet has aborted an ongoing control
+ transfer before DATAEND. It must be serviced and it also tells us that
+ the pending packet is a SETUP packet even if the high level state
+ machine is still inside the previous (aborted) transfer.*/
+ setup_aborted = (csr & SK32_CSR0_SETUPEND) != 0U;
+ if (setup_aborted) {
+ SK32_USB->CSR |= SK32_CSR0_SERSETUPEND;
+ }
+
+ /* A STALL handshake has been sent, clearing the status flag.*/
+ if ((csr & SK32_CSR0_SENTST) != 0U) {
+ SK32_USB->CSR &= (uint8_t)~SK32_CSR0_SENTST;
+ }
+
+ if ((csr & SK32_CSR0_OPKTRD) != 0U) {
+ /* A packet has been received on EP0: it is a SETUP packet when the high
+ level state machine is waiting for one, when it is in the error state
+ (a STALL has been sent and a new transfer is starting) or when the
+ previous transfer has been aborted. Otherwise it is a data/status
+ packet of a control write.*/
+ if (setup_aborted ||
+ (usbp->ep0state == USB_EP0_STP_WAITING) ||
+ (usbp->ep0state == USB_EP0_ERROR)) {
+ usb_serve_ep0_setup(usbp);
+ }
+ else {
+ const USBOutEndpointState *osp0 = usbp->epc[0]->out_state;
+
+ if ((usb_get_rx_count() == 8U) &&
+ (osp0->rxbuf == NULL) && (osp0->rxsize == 0U)) {
+ /* The high level layer armed EP0 to receive the zero length OUT
+ status packet of a control read. When the last IN data packet was
+ armed with DATAEND the musbfsfc core swallows the host's status
+ ZLP without raising OPKTRD, so the 8 bytes packet received here
+ is actually the SETUP packet of the next control transfer.
+ Dispatch it to the setup handler instead of discarding it as a
+ spurious data packet: the generic state machine aborts the
+ previous (already completed on the controller side) transfer.*/
+ usb_serve_ep0_setup(usbp);
+ }
+ else {
+ usb_serve_out(usbp, 0);
+ }
+ }
+ }
+ else {
+ /* No packet pending: the event must be the transmission completion of
+ an IN packet previously loaded with IPKTRD.*/
+ usb_serve_in(usbp, 0);
+ }
+
+#if defined(SK32_USB_TRACE)
+ {
+ uint8_t csrx;
+ uint8_t idx;
+
+ /* CSR0 is valid only while the INDEX register selects EP0.*/
+ idx = SK32_USB->INDEX;
+ SK32_USB->INDEX = 0U;
+ csrx = SK32_USB->CSR;
+ SK32_USB->INDEX = idx;
+ usb_trace_event('X', csrx, (uint8_t)usbp->ep0state, usb_trace_frame());
+ }
+#endif
+ SK32_USB->INDEX = old_index;
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if SK32_USB_USE_USB1 || defined(__DOXYGEN__)
+/**
+ * @brief USB interrupt handler.
+ * @note The SK32F077 has a single USB IRQ line (vector 31); the HP and LP
+ * STM32F0 interrupt naming conventions both map onto this vector.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_USB_HANDLER) {
+ uint8_t is;
+ uint8_t txis;
+ uint8_t rxis;
+ uint8_t old_index;
+ uint8_t ep;
+ USBDriver *usbp = &USBD1;
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Reading the common USB interrupt flags.*/
+ is = SK32_USB->INTRUSB;
+
+ /* USB bus reset handling: the controller is brought back to a known state
+ and the high level driver is reset (this re-initializes EP0).*/
+ if ((is & SK32_INTRUSB_RESET) != 0U) {
+ old_index = SK32_USB->INDEX;
+#if defined(SK32_USB_TRACE)
+ usb_trace_event('R', is, (uint8_t)usbp->ep0state, usb_trace_frame());
+#endif
+
+ /* Only EP0 remains enabled, all the other endpoint interrupt sources
+ are disabled.*/
+ SK32_USB->INTRIN1E = SK32_EP0_IRQ_BIT;
+ SK32_USB->INTRIN2E = 0x00U;
+ SK32_USB->INTROUT1E = 0x00U;
+ SK32_USB->INTROUT2E = 0x00U;
+
+ /* Deconfiguring the endpoints and clearing their residual state.*/
+ for (ep = 1U; ep < (uint8_t)USB_MAX_ENDPOINTS; ep++) {
+ SK32_USB->INDEX = ep;
+ SK32_USB->INMAXP = 0U;
+ SK32_USB->OUTMAXP = 0U;
+ SK32_USB->INCSR2 = 0U;
+ SK32_USB->OUTCSR2 = 0U;
+ SK32_USB->CSR = 0U;
+ SK32_USB->OUTCSR1 = 0U;
+ }
+
+ SK32_USB->INDEX = old_index;
+
+ /* High level reset: clears the driver state and calls usb_lld_reset().*/
+ _usb_reset(usbp);
+
+ OSAL_IRQ_EPILOGUE();
+ return;
+ }
+
+ /* USB bus SUSPEND condition handling.*/
+ if ((is & SK32_INTRUSB_SUS) != 0U) {
+#if defined(SK32_USB_TRACE)
+ usb_trace_event('Z', is, (uint8_t)usbp->ep0state, 0U);
+#endif
+ _usb_suspend(usbp);
+ }
+
+ /* USB bus RESUME condition handling.*/
+ if ((is & SK32_INTRUSB_RESUME) != 0U) {
+#if defined(SK32_USB_TRACE)
+ usb_trace_event('W', is, (uint8_t)usbp->ep0state, 0U);
+#endif
+ _usb_wakeup(usbp);
+ }
+
+ /* SOF handling.*/
+ if ((is & SK32_INTRUSB_SOF) != 0U) {
+ _usb_isr_invoke_sof_cb(usbp);
+ }
+
+ /* Reading the endpoint interrupt flags and masking them with the enables.
+ EP0 and the IN endpoints are reported by INTRIN1 while the OUT endpoints
+ (EP1..7) are reported by INTROUT1. Note that EP0 events (both
+ directions) are reported on INTRIN1 bit 0 and are serviced through the
+ CSR0 register.*/
+ txis = (uint8_t)(SK32_USB->INTRIN1 & SK32_USB->INTRIN1E);
+ rxis = (uint8_t)(SK32_USB->INTROUT1 & SK32_USB->INTROUT1E);
+
+ old_index = SK32_USB->INDEX;
+
+ /* EP0 events.*/
+ if ((txis & SK32_EP0_IRQ_BIT) != 0U) {
+ usb_serve_ep0(usbp);
+ }
+
+ /* IN endpoints events.*/
+ for (ep = 1U; ep < (uint8_t)USB_MAX_ENDPOINTS; ep++) {
+ if ((txis & ((uint8_t)1U << ep)) != 0U) {
+ if (usbp->epc[ep] != NULL) {
+ SK32_USB->INDEX = ep;
+ usb_serve_in(usbp, ep);
+ }
+ }
+ }
+
+ /* OUT endpoints events.*/
+ for (ep = 1U; ep < (uint8_t)USB_MAX_ENDPOINTS; ep++) {
+ if ((rxis & ((uint8_t)1U << ep)) != 0U) {
+ if (usbp->epc[ep] != NULL) {
+ SK32_USB->INDEX = ep;
+ usb_serve_out(usbp, ep);
+ }
+ }
+ }
+
+ SK32_USB->INDEX = old_index;
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif /* SK32_USB_USE_USB1 */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Low level USB driver initialization.
+ *
+ * @notapi
+ */
+void usb_lld_init(void) {
+
+ /* Driver initialization.*/
+ ep0_setup_pending = false;
+ usbObjectInit(&USBD1);
+}
+
+/**
+ * @brief Configures and activates the USB peripheral.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_start(USBDriver *usbp) {
+
+ if (usbp->state == USB_STOP) {
+#if SK32_USB_USE_USB1
+ if (&USBD1 == usbp) {
+ uint32_t pwrcr;
+
+ /* The musbfsfc controller requires a 48MHz reference clock. On the
+ SK32F077 this clock is generated by the internal PLL48 which is
+ routed to the USB peripheral by clearing the USBSW bit of CFGR3
+ (see sk32f0xx.h).*/
+ RCC->CR2 |= RCC_CR2_PLL48ON;
+ while ((RCC->CR2 & RCC_CR2_PLL48RDY) == 0U) {
+ }
+ RCC->CFGR3 &= ~RCC_CFGR3_USBSW;
+
+ /* Clocking the power interface to program the internal USB PHY.*/
+ rccEnablePWRInterface(true);
+
+ /* Programming the internal USB PHY supply: 3.3V range, powered by the
+ internal LDO. These PWR->CR bits are a SK32 extension.*/
+ pwrcr = PWR->CR & ~((uint32_t)SK32_PWR_CR_LDO_VS_MASK |
+ SK32_PWR_CR_LDO_EN);
+ PWR->CR = pwrcr | (uint32_t)SK32_PWR_CR_PHY_3V3_LDO;
+
+ /* Configuring PA11 (D-) and PA12 (D+) as push-pull outputs at 50MHz,
+ as done by the vendor BSP. The USB PHY takes the pins over once the
+ controller is enabled.*/
+ rccEnableAHB(RCC_AHBENR_GPIOAEN, true);
+ GPIOA->MODER = (GPIOA->MODER & ~((uint32_t)0x3U << 22U) &
+ ~((uint32_t)0x3U << 24U)) |
+ ((uint32_t)0x1U << 22U) | ((uint32_t)0x1U << 24U);
+ GPIOA->OTYPER &= ~((uint32_t)0x3U << 11U);
+ GPIOA->OSPEEDR = (GPIOA->OSPEEDR & ~((uint32_t)0x3U << 22U) &
+ ~((uint32_t)0x3U << 24U)) |
+ ((uint32_t)0x3U << 22U) | ((uint32_t)0x3U << 24U);
+ GPIOA->PUPDR &= ~((uint32_t)0x3U << 22U) &
+ ~((uint32_t)0x3U << 24U);
+ /* Strong drive current (level 7) for the PA11/PA12 pads. The SK32
+ specific DCR (drive current configuration) register selects the pad
+ output current, OSPEEDR only selects the edge speed. The vendor BSP
+ programs the maximum level on the USB pads; leaving the default weak
+ level degrades the full speed signaling and the host fails to receive
+ the device responses during enumeration.*/
+ GPIOA->DCR[1] = (GPIOA->DCR[1] & ~((uint32_t)0x7U << 12U) &
+ ~((uint32_t)0x7U << 16U)) |
+ ((uint32_t)0x7U << 12U) | ((uint32_t)0x7U << 16U);
+
+ /* USB peripheral clock activation and reset.*/
+ rccEnableUSB(true);
+ rccResetUSB();
+
+ /* Enabling the shared USB IRQ vector (HP and LP naming conventions
+ map onto the same vector 31 on this device).*/
+ nvicEnableVector(SK32_USB_NUMBER,
+ SK32_USB_USB1_LP_IRQ_PRIORITY);
+
+ /* Basic controller setup; the bus is not connected yet (the D+ pull-up
+ is applied by usb_lld_connect_bus()).*/
+ SK32_USB->FADDR = 0U;
+ SK32_USB->PULL = 0U;
+ SK32_USB->POWER = SK32_POWER_ENSUS;
+ }
+#endif /* SK32_USB_USE_USB1 */
+
+ /* Reset procedure enforced on driver start (initializes EP0).*/
+ usb_lld_reset(usbp);
+#if defined(SK32_USB_TRACE)
+ usb_trace_event('C', (uint8_t)usbp->state, 0U, 0U);
+#endif
+ }
+}
+
+/**
+ * @brief Deactivates the USB peripheral.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_stop(USBDriver *usbp) {
+
+ /* If in ready state then disables the USB clock.*/
+ if (usbp->state != USB_STOP) {
+#if SK32_USB_USE_USB1
+ if (&USBD1 == usbp) {
+ nvicDisableVector(SK32_USB_NUMBER);
+
+ /* Disconnecting the bus and disabling all the interrupt sources.*/
+ SK32_USB->PULL = 0U;
+ SK32_USB->POWER = 0U;
+ SK32_USB->INTRUSBE = 0U;
+ SK32_USB->INTRIN1E = 0U;
+ SK32_USB->INTRIN2E = 0U;
+ SK32_USB->INTROUT1E = 0U;
+ SK32_USB->INTROUT2E = 0U;
+
+ rccDisableUSB();
+ }
+#endif /* SK32_USB_USE_USB1 */
+ }
+}
+
+/**
+ * @brief USB low level reset routine.
+ * @details Called after a USB bus reset (from the ISR through _usb_reset())
+ * or on driver start.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_reset(USBDriver *usbp) {
+ uint8_t csr;
+ uint8_t irq;
+ uint8_t old_index;
+
+ /* Post reset initialization.*/
+ SK32_USB->FADDR = 0U;
+
+ /* A bus reset aborts any ongoing control transfer: the EP0 software state
+ flags are cleared so that a stale flag cannot corrupt the next control
+ transfer serviced after the reset.*/
+ ep0_setup_pending = false;
+ ep0_out_status_pending = false;
+
+ /* Suspend detection enabled (the suspend mode entry is reported through
+ INTRUSB.SUS).*/
+ SK32_USB->POWER |= SK32_POWER_ENSUS;
+
+ /* Only EP0 is enabled; the other endpoints are enabled on demand by
+ usb_lld_init_endpoint().*/
+ SK32_USB->INTRIN1E = SK32_EP0_IRQ_BIT;
+ SK32_USB->INTRIN2E = 0x00U;
+ SK32_USB->INTROUT1E = 0x00U;
+ SK32_USB->INTROUT2E = 0x00U;
+
+ /* Common USB interrupts: SUSPEND, RESUME and RESET are always enabled.
+ The SOF interrupt is only enabled if a callback is defined because it
+ is a high rate source.*/
+ irq = SK32_INTRUSBE_SUS | SK32_INTRUSBE_RESUME | SK32_INTRUSBE_RESET;
+ if ((usbp->config != NULL) && (usbp->config->sof_cb != NULL)) {
+ irq |= SK32_INTRUSBE_SOF;
+ }
+ SK32_USB->INTRUSBE = irq;
+
+ /* Servicing any stale EP0 condition left by the bus reset.*/
+ old_index = usb_ep_select(0);
+ csr = SK32_USB->CSR;
+ if ((csr & SK32_CSR0_OPKTRD) != 0U) {
+ SK32_USB->CSR |= SK32_CSR0_SEROPKTRD;
+ }
+ if ((csr & SK32_CSR0_SETUPEND) != 0U) {
+ SK32_USB->CSR |= SK32_CSR0_SERSETUPEND;
+ }
+ SK32_USB->INDEX = old_index;
+
+ /* EP0 initialization.*/
+ usbp->epc[0] = &ep0config;
+ usb_lld_init_endpoint(usbp, 0U);
+}
+
+/**
+ * @brief Sets the USB address.
+ * @details With USB_LATE_SET_ADDRESS the high level layer invokes this
+ * function once the SET_ADDRESS status stage has been completed,
+ * so the controller can safely latch the new function address.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_set_address(USBDriver *usbp) {
+
+ SK32_USB->FADDR = (uint8_t)(usbp->address & SK32_FADDR_FUNC_ADDR);
+}
+
+/**
+ * @brief Enables an endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ *
+ * @notapi
+ */
+void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep) {
+ const USBEndpointConfig *epcp = usbp->epc[ep];
+ uint8_t old_index;
+
+ old_index = usb_ep_select(ep);
+
+ if (ep == 0U) {
+ /* EP0 is a bidirectional control endpoint with a fixed 64 bytes packet
+ size; only the interrupt source needs to be enabled.*/
+ SK32_USB->INTRIN1E |= SK32_EP0_IRQ_BIT;
+ }
+ else {
+ bool is_in = epcp->in_state != NULL;
+ bool is_out = epcp->out_state != NULL;
+
+ /* The musbfsfc endpoints are unidirectional: an endpoint is configured
+ as IN by setting INCSR2.MODE, otherwise it is an OUT endpoint. The
+ same FIFO/register window cannot serve both directions (EP0 is the
+ only bidirectional endpoint).*/
+ osalDbgAssert(!(is_in && is_out),
+ "SK32 endpoint cannot be both IN and OUT");
+
+ if (is_in) {
+ /* IN endpoint configuration: max packet size expressed in words,
+ direction set to IN, interrupt source enabled.*/
+ SK32_USB->INMAXP = (uint8_t)((epcp->in_maxsize + 7U) >> 3U);
+ SK32_USB->INCSR2 |= SK32_INCSR2_MODE;
+ if ((epcp->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_ISOC) {
+ SK32_USB->INCSR2 |= SK32_INCSR2_ISO;
+ }
+ SK32_USB->INTRIN1E |= (uint8_t)(1U << ep);
+ }
+ if (is_out) {
+ /* OUT endpoint configuration.*/
+ SK32_USB->OUTMAXP = (uint8_t)((epcp->out_maxsize + 7U) >> 3U);
+ SK32_USB->INCSR2 &= (uint8_t)~SK32_INCSR2_MODE;
+ if ((epcp->ep_mode & USB_EP_MODE_TYPE) == USB_EP_MODE_TYPE_ISOC) {
+ SK32_USB->OUTCSR2 |= SK32_OUTCSR2_ISO;
+ }
+ SK32_USB->INTROUT1E |= (uint8_t)(1U << ep);
+ }
+ }
+
+ SK32_USB->INDEX = old_index;
+}
+
+/**
+ * @brief Disables all the active endpoints except the endpoint zero.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ *
+ * @notapi
+ */
+void usb_lld_disable_endpoints(USBDriver *usbp) {
+ uint8_t ep;
+ uint8_t old_index;
+
+ (void)usbp;
+
+ old_index = usb_ep_select(0);
+
+ /* Disabling all the endpoints except EP0.*/
+ for (ep = 1U; ep < (uint8_t)USB_MAX_ENDPOINTS; ep++) {
+ SK32_USB->INDEX = ep;
+ SK32_USB->INTRIN1E &= (uint8_t)~(uint8_t)(1U << ep);
+ SK32_USB->INTROUT1E &= (uint8_t)~(uint8_t)(1U << ep);
+ SK32_USB->INMAXP = 0U;
+ SK32_USB->OUTMAXP = 0U;
+ SK32_USB->INCSR2 = 0U;
+ SK32_USB->OUTCSR2 = 0U;
+ SK32_USB->CSR = 0U;
+ SK32_USB->OUTCSR1 = 0U;
+ }
+
+ SK32_USB->INDEX = old_index;
+}
+
+/**
+ * @brief Returns the status of an OUT endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ * @return The endpoint status.
+ * @retval EP_STATUS_DISABLED The endpoint is not active.
+ * @retval EP_STATUS_STALLED The endpoint is stalled.
+ * @retval EP_STATUS_ACTIVE The endpoint is active.
+ *
+ * @notapi
+ */
+usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep) {
+ uint8_t old_index;
+ uint8_t csr;
+ usbepstatus_t status;
+
+ if ((usbp->epc[ep] == NULL) || (usbp->epc[ep]->out_state == NULL)) {
+ return EP_STATUS_DISABLED;
+ }
+
+ old_index = usb_ep_select(ep);
+ if (ep == 0U) {
+ csr = SK32_USB->CSR;
+ status = ((csr & SK32_CSR0_SENDST) != 0U) ? EP_STATUS_STALLED :
+ EP_STATUS_ACTIVE;
+ }
+ else {
+ csr = SK32_USB->OUTCSR1;
+ status = ((csr & SK32_OUTCSR1_SENDST) != 0U) ? EP_STATUS_STALLED :
+ EP_STATUS_ACTIVE;
+ }
+ SK32_USB->INDEX = old_index;
+
+ return status;
+}
+
+/**
+ * @brief Returns the status of an IN endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ * @return The endpoint status.
+ * @retval EP_STATUS_DISABLED The endpoint is not active.
+ * @retval EP_STATUS_STALLED The endpoint is stalled.
+ * @retval EP_STATUS_ACTIVE The endpoint is active.
+ *
+ * @notapi
+ */
+usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep) {
+ uint8_t old_index;
+ uint8_t csr;
+ usbepstatus_t status;
+
+ if ((usbp->epc[ep] == NULL) || (usbp->epc[ep]->in_state == NULL)) {
+ return EP_STATUS_DISABLED;
+ }
+
+ old_index = usb_ep_select(ep);
+ if (ep == 0U) {
+ csr = SK32_USB->CSR;
+ status = ((csr & SK32_CSR0_SENDST) != 0U) ? EP_STATUS_STALLED :
+ EP_STATUS_ACTIVE;
+ }
+ else {
+ csr = SK32_USB->CSR;
+ status = ((csr & SK32_INCSR1_SENDST) != 0U) ? EP_STATUS_STALLED :
+ EP_STATUS_ACTIVE;
+ }
+ SK32_USB->INDEX = old_index;
+
+ return status;
+}
+
+/**
+ * @brief Reads a setup packet from the endpoint zero FIFO.
+ * @details This function must be invoked in the context of the @p setup_cb
+ * callback in order to read the received setup packet. When invoked
+ * while an EP0 SETUP packet is being served by the ISR the OPKTRD
+ * service is performed right after the FIFO pop, mirroring the
+ * vendor driver sequence.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ * @param[out] buf buffer where to copy the packet data
+ *
+ * @notapi
+ */
+void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf) {
+ uint8_t old_index;
+
+ (void)usbp;
+ (void)ep;
+
+ /* Reading the 8 setup bytes from the EP0 FIFO window (direct access).*/
+ usb_fifo_read(0U, buf, 8U);
+
+ /* If a SETUP packet is currently being served by the ISR the OPKTRD flag
+ must be serviced after the data has been popped from the FIFO.*/
+ if (ep0_setup_pending) {
+ old_index = usb_ep_select(0);
+ SK32_USB->CSR |= SK32_CSR0_SEROPKTRD;
+#if defined(SK32_USB_TRACE)
+ {
+ uint8_t csrv = SK32_USB->CSR;
+ usb_trace_event('K', csrv, (uint8_t)usbp->ep0state, 0U);
+ }
+#endif
+ SK32_USB->INDEX = old_index;
+ ep0_setup_pending = false;
+ }
+}
+
+/**
+ * @brief Starts a receive operation on an OUT endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ *
+ * @notapi
+ */
+void usb_lld_start_out(USBDriver *usbp, usbep_t ep) {
+ USBOutEndpointState *osp = usbp->epc[ep]->out_state;
+ uint8_t old_index;
+
+ /* Transfer initialization: the expected number of packets is computed so
+ that the ISR can detect the end of the transaction also when the last
+ packet is not a short packet.*/
+ if (osp->rxsize == 0U) {
+ /* Special case for zero sized packets (e.g. the EP0 status ZLP).*/
+ osp->rxpkts = 1U;
+ }
+ else {
+ osp->rxpkts = (uint16_t)((osp->rxsize +
+ usbp->epc[ep]->out_maxsize - 1U) /
+ usbp->epc[ep]->out_maxsize);
+ }
+
+ if (ep > 0U) {
+ /* Enabling the OUT interrupt source. The endpoint itself is always
+ ready to receive an OUT token once it has been initialized; unserviced
+ packets are automatically NAKed by the controller when the FIFO is
+ full, so no explicit VALID/NAK arm is required.*/
+ old_index = usb_ep_select(ep);
+ SK32_USB->INTROUT1E |= (uint8_t)(1U << ep);
+ SK32_USB->INDEX = old_index;
+ }
+#if defined(SK32_USB_TRACE)
+ if (ep == 0U) {
+ uint8_t csra;
+ uint8_t idxa;
+
+ idxa = SK32_USB->INDEX;
+ SK32_USB->INDEX = 0U;
+ csra = SK32_USB->CSR;
+ SK32_USB->INDEX = idxa;
+ usb_trace_event('O', (uint8_t)osp->rxsize, (uint8_t)usbp->ep0state,
+ (uint8_t)osp->rxpkts);
+ if (osp->rxsize == 0U) {
+ /* Snapshot of CSR0 right after arming the EP0 status ZLP receive. */
+ usb_trace_event('A', csra, (uint8_t)usbp->ep0state, usb_trace_frame());
+ }
+ }
+#endif
+}
+
+/**
+ * @brief Starts a transmit operation on an IN endpoint.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ *
+ * @notapi
+ */
+void usb_lld_start_in(USBDriver *usbp, usbep_t ep) {
+ size_t n;
+ USBInEndpointState *isp = usbp->epc[ep]->in_state;
+ uint8_t old_index;
+
+ /* Transfer initialization: the first packet is written into the FIFO and
+ the transmission is started with IPKTRD.*/
+ n = isp->txsize;
+ if (n > (size_t)usbp->epc[ep]->in_maxsize) {
+ n = (size_t)usbp->epc[ep]->in_maxsize;
+ }
+ isp->txlast = n;
+
+ if (n > 0U) {
+ usb_fifo_write(ep, isp->txbuf, n);
+ }
+
+ old_index = usb_ep_select(ep);
+ if (ep == 0U) {
+ if (ep0_out_status_pending) {
+ /* Control write transfer with an OUT data phase: DATEND was already
+ set when the last OUT data packet was serviced in usb_serve_out(),
+ the status IN zero length packet is sent by the hardware on its
+ own. The transfer completion is synthesized here instead of arming
+ a duplicate status packet (the EP0 event eventually generated by
+ the hardware when the status packet has been sent is ignored
+ because there is no transmit pending anymore).*/
+ ep0_out_status_pending = false;
+#if defined(SK32_USB_TRACE)
+ usb_trace_event('Y', (uint8_t)usbp->ep0state, 0U, 0U);
+#endif
+ SK32_USB->INDEX = old_index;
+ _usb_isr_invoke_in_cb(usbp, ep);
+ return;
+ }
+ if ((n == 0U) && (usbp->ep0state == USB_EP0_IN_SENDING_STS)) {
+ /* Zero length status packet of a control write transfer: DATEND makes
+ the hardware send the packet and close the control transfer, the
+ completion is then reported through the regular EP0 IN event.*/
+ SK32_USB->CSR |= SK32_CSR0_DATEND | SK32_CSR0_IPKTRD;
+ }
+ else {
+ /* Data stage packet (full size, short packet or zero length packet
+ terminating the data phase of a control read): when this packet
+ closes the whole data stage DATEND is set together with IPKTRD so
+ that the controller stops NAKing the host's status OUT packet and
+ completes the control transfer (see usb_ep0_in_datend()).*/
+ if (usb_ep0_in_datend(usbp, isp->txsize,
+ isp->txsize - isp->txcnt - isp->txlast, n)) {
+ SK32_USB->CSR |= SK32_CSR0_DATEND | SK32_CSR0_IPKTRD;
+ }
+ else {
+ SK32_USB->CSR |= SK32_CSR0_IPKTRD;
+ }
+ }
+ }
+ else {
+ /* Regular IN endpoint: an empty FIFO with IPKTRD set transmits a zero
+ length packet.*/
+ SK32_USB->CSR |= SK32_INCSR1_IPKTRD;
+ }
+#if defined(SK32_USB_TRACE)
+ if (ep == 0U) {
+ uint8_t csrv = SK32_USB->CSR; /* CSR0, INDEX still selects EP0. */
+ usb_trace_event('I', (uint8_t)n, (uint8_t)usbp->ep0state, csrv);
+ }
+#endif
+ SK32_USB->INDEX = old_index;
+}
+
+/**
+ * @brief Brings an OUT endpoint in the stalled state.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ *
+ * @notapi
+ */
+void usb_lld_stall_out(USBDriver *usbp, usbep_t ep) {
+ uint8_t csr;
+ uint8_t old_index;
+
+ (void)usbp;
+
+ old_index = usb_ep_select(ep);
+ if (ep == 0U) {
+ /* Stalling EP0: if a packet is pending it must be dropped at the same
+ time, otherwise the STALL handshake would be overridden by the packet
+ acknowledgment.*/
+ csr = SK32_USB->CSR;
+ if ((csr & SK32_CSR0_OPKTRD) != 0U) {
+ SK32_USB->CSR |= SK32_CSR0_SENDST | SK32_CSR0_SEROPKTRD;
+ }
+ else {
+ SK32_USB->CSR |= SK32_CSR0_SENDST;
+ }
+ }
+ else {
+ SK32_USB->OUTCSR1 |= SK32_OUTCSR1_SENDST;
+ }
+ SK32_USB->INDEX = old_index;
+}
+
+/**
+ * @brief Brings an IN endpoint in the stalled state.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ *
+ * @notapi
+ */
+void usb_lld_stall_in(USBDriver *usbp, usbep_t ep) {
+ uint8_t old_index;
+
+ (void)usbp;
+
+ old_index = usb_ep_select(ep);
+ if (ep == 0U) {
+ SK32_USB->CSR |= SK32_CSR0_SENDST;
+ }
+ else {
+ SK32_USB->CSR |= SK32_INCSR1_SENDST;
+ }
+ SK32_USB->INDEX = old_index;
+}
+
+/**
+ * @brief Brings an OUT endpoint in the active state.
+ * @details Removes a possible STALL condition and resets the data toggle.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ *
+ * @notapi
+ */
+void usb_lld_clear_out(USBDriver *usbp, usbep_t ep) {
+ uint8_t old_index;
+
+ (void)usbp;
+
+ old_index = usb_ep_select(ep);
+ if (ep == 0U) {
+ /* EP0: removing the stall flags, the toggle is managed by the hardware
+ and is reset by every SETUP packet.*/
+ SK32_USB->CSR &= (uint8_t)~(SK32_CSR0_SENDST | SK32_CSR0_SENTST);
+ }
+ else {
+ /* Clear the stall on the OUT endpoint and reset the data toggle.*/
+ SK32_USB->OUTCSR1 &= (uint8_t)~(SK32_OUTCSR1_SENDST |
+ SK32_OUTCSR1_SENTST);
+ SK32_USB->OUTCSR1 |= SK32_OUTCSR1_CLRDT;
+ }
+ SK32_USB->INDEX = old_index;
+}
+
+/**
+ * @brief Brings an IN endpoint in the active state.
+ * @details Removes a possible STALL condition and resets the data toggle.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ *
+ * @notapi
+ */
+void usb_lld_clear_in(USBDriver *usbp, usbep_t ep) {
+ uint8_t old_index;
+
+ (void)usbp;
+
+ old_index = usb_ep_select(ep);
+ if (ep == 0U) {
+ /* EP0: removing the stall flags, the toggle is managed by the hardware
+ and is reset by every SETUP packet.*/
+ SK32_USB->CSR &= (uint8_t)~(SK32_CSR0_SENDST | SK32_CSR0_SENTST);
+ }
+ else {
+ /* Clear the stall on the IN endpoint and reset the data toggle.*/
+ SK32_USB->CSR &= (uint8_t)~(SK32_INCSR1_SENDST | SK32_INCSR1_SENTST);
+ SK32_USB->CSR |= SK32_INCSR1_CLRDT;
+ }
+ SK32_USB->INDEX = old_index;
+}
+
+#endif /* HAL_USE_USB */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_usb_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_usb_lld.h
new file mode 100644
index 0000000000..ddb2ead4c4
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_usb_lld.h
@@ -0,0 +1,550 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ SK32F0xx USB LLD - 3Think SK32F077 (Mentor/MUSB musbfsfc class USB FS controller)
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_usb_lld.h
+ * @brief SK32 USB subsystem low level driver header.
+ * @details The SK32F077 embeds a Mentor/MUSB (musbfsfc) full-speed USB device
+ * controller whose register map is completely different from the
+ * STM32F0 USB (PMA) peripheral, although the rest of the chip is
+ * STM32F072 register compatible. This driver is modeled on the
+ * 3Think CherryUSB port (usb_dc_musbfsfc) shipped inside the
+ * "SK32F0xx_Firmware Package".
+ *
+ * @addtogroup USB
+ * @{
+ */
+
+#ifndef HAL_USB_LLD_H
+#define HAL_USB_LLD_H
+
+#if HAL_USE_USB || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief Maximum endpoint number supported by the controller.
+ * @note The musbfsfc core has 16 endpoint FIFOs but the INTR1/OUT1
+ * registers used by this driver cover endpoints 0..7. QMK class
+ * drivers only need a handful of endpoints.
+ */
+#define USB_MAX_ENDPOINTS 8U
+
+/**
+ * @brief Status stage handling method (software driven, same as the
+ * STM32 USBv1 LLD: the control-transfer status ZLP is exchanged
+ * through usbStartTransmitI()/usbStartReceiveI()).
+ */
+#define USB_EP0_STATUS_STAGE USB_EP0_STATUS_STAGE_SW
+
+/**
+ * @brief This device requires the address change after the status packet.
+ * @note The musbfsfc core latches FADDR only when the firmware writes it;
+ * a SET_ADDRESS control transfer is completed with the status ZLP
+ * still sent at address zero, so the address is programmed by
+ * usb_lld_set_address() which the high level layer invokes once the
+ * status stage has completed.
+ */
+#define USB_SET_ADDRESS_MODE USB_LATE_SET_ADDRESS
+
+/**
+ * @brief Method for set address acknowledge.
+ */
+#define USB_SET_ADDRESS_ACK_HANDLING USB_SET_ADDRESS_ACK_SW
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @brief USB driver enable switch.
+ * @details If set to @p TRUE the support for USB is included.
+ * @note The default is @p TRUE.
+ */
+#if !defined(SK32_USB_USE_USB1) || defined(__DOXYGEN__)
+#define SK32_USB_USE_USB1 TRUE
+#endif
+
+/**
+ * @brief Enables the USB device low power mode on suspend.
+ */
+#if !defined(SK32_USB_LOW_POWER_ON_SUSPEND) || defined(__DOXYGEN__)
+#define SK32_USB_LOW_POWER_ON_SUSPEND FALSE
+#endif
+
+/**
+ * @brief USB interrupt priority level setting.
+ * @note The SK32F077 has a single USB IRQ line (vector 31) shared by the
+ * "HP" and "LP" STM32F0 naming conventions.
+ */
+#if !defined(SK32_USB_USB1_HP_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_USB_USB1_HP_IRQ_PRIORITY 3
+#endif
+
+/**
+ * @brief USB interrupt priority level setting.
+ */
+#if !defined(SK32_USB_USB1_LP_IRQ_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_USB_USB1_LP_IRQ_PRIORITY 3
+#endif
+
+/*===========================================================================*/
+/* SK32 USB controller register definition. */
+/*===========================================================================*/
+
+/**
+ * @brief SK32 musbfsfc USB controller base address.
+ */
+#define SK32_USB_BASE 0x40005C00U
+
+/**
+ * @brief Type of the SK32 musbfsfc USB register file.
+ * @note All registers are 8 bit wide and the endpoint status/control
+ * registers are selected through the INDEX register.
+ */
+typedef struct {
+ volatile uint8_t FADDR; /**< @brief Function address. */
+ volatile uint8_t POWER; /**< @brief Power management. */
+ volatile uint8_t INTRIN1; /**< @brief EP0 + IN EP1..7 interrupts. */
+ volatile uint8_t INTRIN2; /**< @brief IN EP8..15 interrupts. */
+ volatile uint8_t INTROUT1; /**< @brief OUT EP1..7 interrupts. */
+ volatile uint8_t INTROUT2; /**< @brief OUT EP8..15 interrupts. */
+ volatile uint8_t INTRUSB; /**< @brief Common USB interrupts. */
+ volatile uint8_t INTRIN1E; /**< @brief INTRIN1 enable. */
+ volatile uint8_t INTRIN2E; /**< @brief INTRIN2 enable. */
+ volatile uint8_t INTROUT1E; /**< @brief INTROUT1 enable. */
+ volatile uint8_t INTROUT2E; /**< @brief INTROUT2 enable. */
+ volatile uint8_t INTRUSBE; /**< @brief INTRUSB enable. */
+ volatile uint8_t FRAME1; /**< @brief Frame number bits 0..7. */
+ volatile uint8_t FRAME2; /**< @brief Frame number bits 8..10. */
+ volatile uint8_t INDEX; /**< @brief Endpoint index selection. */
+ volatile uint8_t RESERVED0;
+ volatile uint8_t INMAXP; /**< @brief IN endpoint max packet size. */
+ volatile uint8_t CSR; /**< @brief EP0 CSR / IN endpoint CSR1. */
+ volatile uint8_t INCSR2; /**< @brief IN endpoint CSR2. */
+ volatile uint8_t OUTMAXP; /**< @brief OUT endpoint max packet size. */
+ volatile uint8_t OUTCSR1; /**< @brief OUT endpoint CSR1. */
+ volatile uint8_t OUTCSR2; /**< @brief OUT endpoint CSR2. */
+ volatile uint8_t COUNT; /**< @brief Received bytes (low byte). */
+ volatile uint8_t OUTCOUNT2; /**< @brief Received bytes (high byte). */
+ volatile uint8_t RESERVED1[8];
+ volatile uint8_t FIFO[16]; /**< @brief Endpoint FIFOs (8 bit). */
+ volatile uint8_t RESERVED2[8];
+ volatile uint8_t PULL; /**< @brief D+/D- pull-up control. */
+} SK32_USB_TypeDef;
+
+/**
+ * @brief SK32 musbfsfc controller instance.
+ * @note Named SK32_USB on purpose: the STM32F072 CMSIS header (which this
+ * port reuses for the rest of the chip) already defines a different
+ * `USB` peripheral for the PMA-based ST USB IP.
+ */
+#define SK32_USB ((SK32_USB_TypeDef *)SK32_USB_BASE)
+
+/* FADDR register. */
+#define SK32_FADDR_FUNC_ADDR 0x7FU /**< Function address. */
+#define SK32_FADDR_UPDATE 0x80U /**< Address update pending. */
+
+/* POWER register. */
+#define SK32_POWER_ENSUS 0x01U /**< Enable suspend mode. */
+#define SK32_POWER_SUSMOD 0x02U /**< Suspend mode enable. */
+#define SK32_POWER_RESUME 0x04U /**< Resume signaling. */
+#define SK32_POWER_RESET 0x08U /**< Bus reset in progress. */
+#define SK32_POWER_ISOUP 0x80U /**< Isochronous ups. */
+
+/* INTRUSB register. */
+#define SK32_INTRUSB_SUS 0x01U /**< Suspend interrupt. */
+#define SK32_INTRUSB_RESUME 0x02U /**< Resume interrupt. */
+#define SK32_INTRUSB_RESET 0x04U /**< Reset interrupt. */
+#define SK32_INTRUSB_SOF 0x08U /**< SOF interrupt. */
+
+/* INTRUSBE register. */
+#define SK32_INTRUSBE_SUS 0x01U
+#define SK32_INTRUSBE_RESUME 0x02U
+#define SK32_INTRUSBE_RESET 0x04U
+#define SK32_INTRUSBE_SOF 0x08U
+
+/* EP0 CSR (index 0) register. */
+#define SK32_CSR0_OPKTRD 0x01U /**< OUT/setup packet ready. */
+#define SK32_CSR0_IPKTRD 0x02U /**< IN packet ready (TX). */
+#define SK32_CSR0_SENTST 0x04U /**< STALL handshake sent. */
+#define SK32_CSR0_DATEND 0x08U /**< End of data stage. */
+#define SK32_CSR0_SETUPEND 0x10U /**< Setup ended before DATAEND.*/
+#define SK32_CSR0_SENDST 0x20U /**< Send STALL handshake. */
+#define SK32_CSR0_SEROPKTRD 0x40U /**< Service RX packet ready. */
+#define SK32_CSR0_SERSETUPEND 0x80U /**< Service SETUPEND. */
+
+/* IN CSR1 register (index != 0, aliased with the CSR register). */
+#define SK32_INCSR1_IPKTRD 0x01U /**< IN packet ready (TX). */
+#define SK32_INCSR1_FIFONE 0x02U /**< TX FIFO not empty. */
+#define SK32_INCSR1_UNDERRUN 0x04U /**< TX underrun. */
+#define SK32_INCSR1_FLFIFO 0x08U /**< Flush TX FIFO. */
+#define SK32_INCSR1_SENDST 0x10U /**< Send STALL handshake. */
+#define SK32_INCSR1_SENTST 0x20U /**< STALL handshake sent. */
+#define SK32_INCSR1_CLRDT 0x40U /**< Clear data toggle. */
+
+/* IN CSR2 register. */
+#define SK32_INCSR2_FRCDT 0x08U /**< Force data toggle. */
+#define SK32_INCSR2_DMAEN 0x10U /**< DMA request enable. */
+#define SK32_INCSR2_MODE 0x20U /**< Endpoint direction IN. */
+#define SK32_INCSR2_ISO 0x40U /**< Isochronous endpoint. */
+#define SK32_INCSR2_AUTOSET 0x80U /**< Auto set on FIFO write. */
+
+/* OUT CSR1 register. */
+#define SK32_OUTCSR1_OPKTRD 0x01U /**< OUT packet ready. */
+#define SK32_OUTCSR1_FIFOF 0x02U /**< RX FIFO full. */
+#define SK32_OUTCSR1_OVERRUN 0x04U /**< RX overrun. */
+#define SK32_OUTCSR1_DATERR 0x08U /**< RX data error. */
+#define SK32_OUTCSR1_FLFIFO 0x10U /**< Flush RX FIFO. */
+#define SK32_OUTCSR1_SENDST 0x20U /**< Send STALL handshake. */
+#define SK32_OUTCSR1_SENTST 0x40U /**< STALL handshake sent. */
+#define SK32_OUTCSR1_CLRDT 0x80U /**< Clear data toggle. */
+
+/* OUT CSR2 register. */
+#define SK32_OUTCSR2_DMAMODE 0x10U /**< DMA burst mode. */
+#define SK32_OUTCSR2_DMAEN 0x20U /**< DMA request enable. */
+#define SK32_OUTCSR2_ISO 0x40U /**< Isochronous endpoint. */
+#define SK32_OUTCSR2_AUTOCLR 0x80U /**< Auto clear on FIFO read. */
+
+/* PULL register. */
+#define SK32_PULL_DP 0x01U /**< D+ pull-up (full speed). */
+#define SK32_PULL_DM 0x02U /**< D- pull-up (low speed). */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+#if !defined(SK32_USB_HANDLER) || !defined(SK32_USB_NUMBER)
+#error "SK32_USB_HANDLER/NUMBER not defined in registry"
+#endif
+
+#if SK32_USB_USE_USB1 && !OSAL_IRQ_IS_VALID_PRIORITY(SK32_USB_USB1_LP_IRQ_PRIORITY)
+#error "Invalid IRQ priority assigned to USB LP"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type of an IN endpoint state structure.
+ */
+typedef struct {
+ /**
+ * @brief Requested transmit transfer size.
+ */
+ size_t txsize;
+ /**
+ * @brief Transmitted bytes so far.
+ */
+ size_t txcnt;
+ /**
+ * @brief Pointer to the transmission linear buffer.
+ */
+ const uint8_t *txbuf;
+#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__)
+ /**
+ * @brief Waiting thread.
+ */
+ thread_reference_t thread;
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Size of the last transmitted packet.
+ */
+ size_t txlast;
+} USBInEndpointState;
+
+/**
+ * @brief Type of an OUT endpoint state structure.
+ */
+typedef struct {
+ /**
+ * @brief Requested receive transfer size.
+ */
+ size_t rxsize;
+ /**
+ * @brief Received bytes so far.
+ */
+ size_t rxcnt;
+ /**
+ * @brief Pointer to the receive linear buffer.
+ */
+ uint8_t *rxbuf;
+#if (USB_USE_WAIT == TRUE) || defined(__DOXYGEN__)
+ /**
+ * @brief Waiting thread.
+ */
+ thread_reference_t thread;
+#endif
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Number of packets to receive.
+ */
+ uint16_t rxpkts;
+} USBOutEndpointState;
+
+/**
+ * @brief Type of an USB endpoint configuration structure.
+ * @note Platform specific restrictions may apply to endpoints.
+ */
+typedef struct {
+ /**
+ * @brief Type and mode of the endpoint.
+ */
+ uint32_t ep_mode;
+ /**
+ * @brief Setup packet notification callback.
+ * @post The application must immediately call @p usbReadPacket() in
+ * order to access the received packet.
+ */
+ usbepcallback_t setup_cb;
+ /**
+ * @brief IN endpoint notification callback.
+ */
+ usbepcallback_t in_cb;
+ /**
+ * @brief OUT endpoint notification callback.
+ */
+ usbepcallback_t out_cb;
+ /**
+ * @brief IN endpoint maximum packet size.
+ */
+ uint16_t in_maxsize;
+ /**
+ * @brief OUT endpoint maximum packet size.
+ */
+ uint16_t out_maxsize;
+ /**
+ * @brief @p USBEndpointState associated to the IN endpoint.
+ */
+ USBInEndpointState *in_state;
+ /**
+ * @brief @p USBEndpointState associated to the OUT endpoint.
+ */
+ USBOutEndpointState *out_state;
+ /* End of the mandatory fields.*/
+ /**
+ * @brief Reserved field, not currently used.
+ */
+ uint16_t ep_buffers;
+ /**
+ * @brief Pointer to a buffer for setup packets.
+ */
+ uint8_t *setup_buf;
+} USBEndpointConfig;
+
+/**
+ * @brief Type of an USB driver configuration structure.
+ */
+typedef struct {
+ /**
+ * @brief USB events callback.
+ */
+ usbeventcb_t event_cb;
+ /**
+ * @brief Device GET_DESCRIPTOR request callback.
+ */
+ usbgetdescriptor_t get_descriptor_cb;
+ /**
+ * @brief Requests hook callback.
+ */
+ usbreqhandler_t requests_hook_cb;
+ /**
+ * @brief Start Of Frame callback.
+ */
+ usbcallback_t sof_cb;
+ /* End of the mandatory fields.*/
+} USBConfig;
+
+/**
+ * @brief Structure representing an USB driver.
+ */
+struct USBDriver {
+ /**
+ * @brief Driver state.
+ */
+ usbstate_t state;
+ /**
+ * @brief Current configuration data.
+ */
+ const USBConfig *config;
+ /**
+ * @brief Bit map of the transmitting IN endpoints.
+ */
+ uint16_t transmitting;
+ /**
+ * @brief Bit map of the receiving OUT endpoints.
+ */
+ uint16_t receiving;
+ /**
+ * @brief Active endpoints configurations.
+ */
+ const USBEndpointConfig *epc[USB_MAX_ENDPOINTS + 1];
+ /**
+ * @brief Fields available to user, it can be used to associate an
+ * application-defined handler to an IN endpoint.
+ */
+ void *in_params[USB_MAX_ENDPOINTS];
+ /**
+ * @brief Fields available to user, it can be used to associate an
+ * application-defined handler to an OUT endpoint.
+ */
+ void *out_params[USB_MAX_ENDPOINTS];
+ /**
+ * @brief Endpoint 0 state.
+ */
+ usbep0state_t ep0state;
+ /**
+ * @brief Next position in the buffer to be transferred through endpoint 0.
+ */
+ uint8_t *ep0next;
+ /**
+ * @brief Number of bytes yet to be transferred through endpoint 0.
+ */
+ size_t ep0n;
+ /**
+ * @brief Endpoint 0 end transaction callback.
+ */
+ usbcallback_t ep0endcb;
+ /**
+ * @brief Setup packet buffer.
+ */
+ uint8_t setup[8];
+ /**
+ * @brief Current USB device status.
+ */
+ uint16_t status;
+ /**
+ * @brief Assigned USB address.
+ */
+ uint8_t address;
+ /**
+ * @brief Current USB device configuration.
+ */
+ uint8_t configuration;
+ /**
+ * @brief State of the driver when a suspend happened.
+ */
+ usbstate_t saved_state;
+#if defined(USB_DRIVER_EXT_FIELDS)
+ USB_DRIVER_EXT_FIELDS
+#endif
+ /* End of the mandatory fields.*/
+};
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @brief Returns the current frame number.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @return The current frame number.
+ *
+ * @notapi
+ */
+#define usb_lld_get_frame_number(usbp) \
+ ((uint16_t)(((uint16_t)SK32_USB->FRAME1) | \
+ ((uint16_t)(SK32_USB->FRAME2 & 0x07U) << 8U)))
+
+/**
+ * @brief Returns the exact size of a receive transaction.
+ *
+ * @param[in] usbp pointer to the @p USBDriver object
+ * @param[in] ep endpoint number
+ * @return Received data size.
+ *
+ * @notapi
+ */
+#define usb_lld_get_transaction_size(usbp, ep) \
+ ((usbp)->epc[ep]->out_state->rxcnt)
+
+/**
+ * @brief Connects the USB device by enabling the D+ pull-up.
+ *
+ * @notapi
+ */
+#if !defined(usb_lld_connect_bus)
+#define usb_lld_connect_bus(usbp) \
+ ((void)(usbp), SK32_USB->PULL |= SK32_PULL_DP)
+#endif
+
+/**
+ * @brief Disconnect the USB device by disabling the D+ pull-up.
+ *
+ * @notapi
+ */
+#if !defined(usb_lld_disconnect_bus)
+#define usb_lld_disconnect_bus(usbp) \
+ ((void)(usbp), SK32_USB->PULL = 0U)
+#endif
+
+/**
+ * @brief Start of host wake-up procedure (remote wakeup).
+ * @details The resume signaling is generated by toggling the RESUME bit of
+ * the POWER register for about 10ms as done by the vendor driver.
+ *
+ * @notapi
+ */
+#define usb_lld_wakeup_host(usbp) \
+ do { \
+ (void)(usbp); \
+ SK32_USB->POWER |= SK32_POWER_RESUME; \
+ osalThreadSleepMilliseconds(10); \
+ SK32_USB->POWER &= (uint8_t)~SK32_POWER_RESUME; \
+ } while (false)
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if SK32_USB_USE_USB1 && !defined(__DOXYGEN__)
+extern USBDriver USBD1;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void usb_lld_init(void);
+ void usb_lld_start(USBDriver *usbp);
+ void usb_lld_stop(USBDriver *usbp);
+ void usb_lld_reset(USBDriver *usbp);
+ void usb_lld_set_address(USBDriver *usbp);
+ void usb_lld_init_endpoint(USBDriver *usbp, usbep_t ep);
+ void usb_lld_disable_endpoints(USBDriver *usbp);
+ usbepstatus_t usb_lld_get_status_in(USBDriver *usbp, usbep_t ep);
+ usbepstatus_t usb_lld_get_status_out(USBDriver *usbp, usbep_t ep);
+ void usb_lld_read_setup(USBDriver *usbp, usbep_t ep, uint8_t *buf);
+ void usb_lld_start_out(USBDriver *usbp, usbep_t ep);
+ void usb_lld_start_in(USBDriver *usbp, usbep_t ep);
+ void usb_lld_stall_out(USBDriver *usbp, usbep_t ep);
+ void usb_lld_stall_in(USBDriver *usbp, usbep_t ep);
+ void usb_lld_clear_out(USBDriver *usbp, usbep_t ep);
+ void usb_lld_clear_in(USBDriver *usbp, usbep_t ep);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* HAL_USE_USB */
+
+#endif /* HAL_USB_LLD_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/hal_wdg_lld.h b/os/hal/ports/SK32/SK32F0xx/hal_wdg_lld.h
new file mode 100644
index 0000000000..0a7a7a2f31
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/hal_wdg_lld.h
@@ -0,0 +1,38 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/hal_wdg_lld.h
+ * @brief SK32F077 WDG subsystem low level driver stub.
+ * @details The SK32F077 does not integrate a watchdog controller compatible
+ * with the generic WDG driver. This header only exists to satisfy the
+ * unconditional include in the generic hal_wdg.h so that this platform
+ * never needs to reference the shared STM32 xWDGv1 LLD header tree.
+ *
+ * @addtogroup SK32F0xx_WDG
+ * @{
+ */
+
+#ifndef HAL_WDG_LLD_H
+#define HAL_WDG_LLD_H
+
+#if HAL_USE_WDG || defined(__DOXYGEN__)
+
+#endif /* HAL_USE_WDG */
+
+#endif /* HAL_WDG_LLD_H */
+
+/** @} */
\ No newline at end of file
diff --git a/os/hal/ports/SK32/SK32F0xx/platform.mk b/os/hal/ports/SK32/SK32F0xx/platform.mk
new file mode 100644
index 0000000000..2df2214117
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/platform.mk
@@ -0,0 +1,150 @@
+# Required platform files.
+# SK32F0xx is register-compatible with STM32F072 except for the USB IP.
+PLATFORMSRC := $(CHIBIOS)/os/hal/ports/common/ARMCMx/nvic.c \
+ $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/sk32_isr.c \
+ $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_lld.c \
+ $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_efl_lld.c \
+ $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_st_lld.c \
+
+# Required include directories.
+PLATFORMINC := $(CHIBIOS)/os/hal/ports/common/ARMCMx \
+ $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx
+
+# Optional platform files.
+ifeq ($(USE_SMART_BUILD),yes)
+
+# Configuration files directory
+ifeq ($(HALCONFDIR),)
+ ifeq ($(CONFDIR),)
+ HALCONFDIR = .
+ else
+ HALCONFDIR := $(CONFDIR)
+ endif
+endif
+
+HALCONF := $(strip $(shell cat $(HALCONFDIR)/halconf.h | egrep -e "\#define"))
+
+else
+endif
+
+# The SK32 PAL (GPIO) driver is native and programs the vendor CMSIS
+# registers directly, the shared STM32 GPIOv2 LLD is not used.
+ifeq ($(USE_SMART_BUILD),yes)
+ifneq ($(findstring HAL_USE_PAL TRUE,$(HALCONF)),)
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_pal_lld.c
+endif
+else
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_pal_lld.c
+endif
+
+# The SK32 USART is a legacy SR/DR class unit, its native serial driver is
+# used instead of the shared STM32 USARTv2 LLD (ISR/ICR register set).
+ifeq ($(USE_SMART_BUILD),yes)
+ifneq ($(findstring HAL_USE_SERIAL TRUE,$(HALCONF)),)
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_serial_lld.c
+endif
+else
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_serial_lld.c
+endif
+
+# The SK32 DMA helper is a native driver managing the shared DMA1 channels
+# (CCR/CNDTR/CPAR/CMAR register set of the vendor library). Its code is
+# self-gated on SK32_DMA_REQUIRED, a macro defined by any driver of this
+# platform that needs DMA services, so it can be always added to the build
+# list like the shared STM32 DMAv1 driver.mk does.
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/sk32_dma.c
+
+# The SK32 SPI is a legacy CR1/CR2/SR/DR class unit (SPIv1 class), its
+# native interrupt-driven driver is used instead of the shared STM32 SPIv2
+# LLD (the latter would require the STM32 registry macros, e.g.
+# STM32_HAS_SPI1, which the SK32 platform does not define).
+ifeq ($(USE_SMART_BUILD),yes)
+ifneq ($(findstring HAL_USE_SPI TRUE,$(HALCONF)),)
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_spi_lld.c
+endif
+else
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_spi_lld.c
+endif
+
+# The SK32 I2C is a legacy CR1/CR2/SR1/SR2/DR/CCR class unit (no TRISE
+# register, single I2C1 IRQ vector), its native interrupt-driven master
+# driver is used instead of the shared STM32 I2Cv1/I2Cv2 LLDs (the latter
+# target the ISR/ICR/TIMINGR register set while I2Cv1 requires TRISE, DMA
+# streams and the EV/ER IRQ split, none of which applies to this platform).
+ifeq ($(USE_SMART_BUILD),yes)
+ifneq ($(findstring HAL_USE_I2C TRUE,$(HALCONF)),)
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_i2c_lld.c
+endif
+else
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_i2c_lld.c
+endif
+
+# The SK32 SLED (serial LED) is a 3Think peripheral present on the SK32F077
+# variant only. Its low level driver is native and drives the two WS2812
+# capable groups through byte wide DMA1 transfers; it allocates DMA1 channel
+# 1 (SLED1) and channel 2 (SLED2) so it also pulls in the DMA helper and the
+# DMA1 initialization through the SK32_DMA_REQUIRED macro (see hal_lld.h).
+# Like sk32_dma.c the file is added unconditionally: its body is self-gated
+# on HAL_USE_SLED (no code is generated unless the macro is TRUE), so the
+# smart-build text scan of the keyboard halconf overrides is not needed.
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c
+
+# The SK32 KBCU (Keyboard Control Unit) is a 3Think peripheral used for
+# keyboard matrix scanning (independent mode) and for driving the RGB LED
+# columns. Its low level driver is native; the LED stream is pushed through
+# DMA1 channel 4 in memory to peripheral mode so the driver also pulls in the
+# DMA helper and the DMA1 initialization through the SK32_DMA_REQUIRED macro
+# (see hal_lld.h). Like sk32_dma.c the file is added unconditionally: its
+# body is self-gated on HAL_USE_KBCU (no code is generated unless the macro is
+# TRUE), so the smart-build text scan of the keyboard halconf overrides is not
+# needed.
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_kbcu_lld.c
+
+# The SK32 general purpose timers (TIM3 on the APB1 bus and the basic
+# TIM16/TIM17 on the APB2 bus) are served by the native GPT driver added
+# below, the shared STM32 TIMv1 LLD is not used (it requires the STM32
+# registry macros and mcuconf naming scheme). The same applies to the ADC1
+# unit: the shared STM32 ADCv1 LLD relies on an STM32 DMA stream while this
+# platform captures the samples in the ADC interrupt handler, so the native
+# hal_adc_lld.c is used instead.
+ifeq ($(USE_SMART_BUILD),yes)
+ifneq ($(findstring HAL_USE_GPT TRUE,$(HALCONF)),)
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_gpt_lld.c
+endif
+ifneq ($(findstring HAL_USE_ADC TRUE,$(HALCONF)),)
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_adc_lld.c
+endif
+else
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_gpt_lld.c
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_adc_lld.c
+endif
+
+# This platform no longer pulls in any shared STM32 LLD. Historically the
+# CANv1/DACv1/DMAv1/RTCv2/xWDGv1 driver.mk files were included here but every
+# one of them compiled to a stub because the associated generic HAL has its
+# corresponding HAL_USE_* macro disabled on this platform, and their PLATFORMINC
+# header dir leaked STM32 headers into the include path. The only genuine
+# consumer was DMAv1, which is now served by the native sk32_dma helper added
+# above with the same full interface.
+# The SK32 system tick (ST) driver is native and uses the Cortex-M0 SysTick
+# counter in periodic mode, so the shared STM32 SYSTICKv1 LLD is not used.
+# The shared STM32 SPIv2, I2Cv1/I2Cv2, ADCv1 and TIMv1 LLDs are not used
+# either: the SPI, I2C, ADC and GPT units of this platform are served by
+# the native drivers added above.
+# The shared STM32 USARTv2 LLD targets the ISR/ICR register set (STM32F0/F3/L4
+# class), the SK32 USART is instead a legacy SR/DR class unit handled by the
+# native serial driver added above.
+# Native stub headers (hal_can_lld.h/hal_dac_lld.h/hal_rtc_lld.h/hal_wdg_lld.h)
+# are provided in this port directory so the generic hal.h can resolve the
+# unconditional includes in hal_can.h/hal_dac.h/hal_rtc.h/hal_wdg.h without
+# referencing the shared STM32 header trees.
+
+# SK32F077 uses a Mentor-MUSB-class USB FS controller (musbfsfc), which is NOT
+# compatible with the STM32 USBv1 LLD, so USBv1/driver.mk is not included here.
+# The SK32 USB low level driver lives in this port directory instead.
+PLATFORMSRC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/hal_usb_lld.c
+PLATFORMINC += $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx
+
+# Shared variables
+ALLCSRC += $(PLATFORMSRC)
+ALLINC += $(PLATFORMINC)
diff --git a/os/hal/ports/SK32/SK32F0xx/sk32_dma.c b/os/hal/ports/SK32/SK32F0xx/sk32_dma.c
new file mode 100644
index 0000000000..2c06b2960f
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/sk32_dma.c
@@ -0,0 +1,391 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/sk32_dma.c
+ * @brief DMA helper driver code.
+ *
+ * @addtogroup SK32F0xx_DMA
+ * @details DMA sharing helper driver. In the SK32 the DMA1 channels are a
+ * shared resource, this driver allows to allocate and free the DMA
+ * channels at runtime in order to allow all the other device
+ * drivers to coordinate the access to the resource.
+ * @note The DMA ISR handlers are all declared into this module because
+ * sharing, the various device drivers can associate a callback to
+ * ISRs when allocating streams.
+ * @note The SK32 DMA1 interrupts are aggregated on three NVIC vectors
+ * (channel 1 alone, channels 2-3, channels 4-6), the shared
+ * vectors are declared here and serve one channel at a time, the
+ * same approach used by the STM32F0xx port for its non-standard
+ * DMA shared IRQ handlers.
+ * @note This driver is compiled only if some driver requiring DMA
+ * services has been enabled, see @p SK32_DMA_REQUIRED.
+ * @{
+ */
+
+#include "hal.h"
+
+/* The following macro is only defined if some driver requiring DMA services
+ has been enabled.*/
+#if defined(SK32_DMA_REQUIRED) || defined(__DOXYGEN__)
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/**
+ * @brief Mask of the DMA1 streams in @p dma.allocated_mask.
+ */
+#define SK32_DMA1_STREAMS_MASK ((1U << SK32_DMA1_NUM_CHANNELS) - 1U)
+
+/*
+ * Default ISR collision masks, only used if not overridden by the registry.
+ */
+#if !defined(SK32_DMA1_CH1_CMASK)
+#define SK32_DMA1_CH1_CMASK (1U << 0U)
+#endif
+#if !defined(SK32_DMA1_CH2_CMASK)
+#define SK32_DMA1_CH2_CMASK (1U << 1U)
+#endif
+#if !defined(SK32_DMA1_CH3_CMASK)
+#define SK32_DMA1_CH3_CMASK (1U << 2U)
+#endif
+#if !defined(SK32_DMA1_CH4_CMASK)
+#define SK32_DMA1_CH4_CMASK (1U << 3U)
+#endif
+#if !defined(SK32_DMA1_CH5_CMASK)
+#define SK32_DMA1_CH5_CMASK (1U << 4U)
+#endif
+#if !defined(SK32_DMA1_CH6_CMASK)
+#define SK32_DMA1_CH6_CMASK (1U << 5U)
+#endif
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/**
+ * @brief DMA streams descriptors.
+ * @details This table keeps the association between an unique stream
+ * identifier and the involved physical registers.
+ * @note Don't use this array directly, use the appropriate wrapper macros
+ * instead: @p SK32_DMA1_STREAM1, @p SK32_DMA1_STREAM2 etc.
+ */
+const sk32_dma_stream_t _sk32_dma_streams[SK32_DMA_STREAMS] = {
+#if SK32_DMA1_NUM_CHANNELS > 0
+ {DMA1, DMA1_Channel1, SK32_DMA1_CH1_CMASK, 0, 0, 0, SK32_DMA1_CH1_NUMBER},
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 1
+ {DMA1, DMA1_Channel2, SK32_DMA1_CH2_CMASK, 0, 4, 1, SK32_DMA1_CH2_NUMBER},
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 2
+ {DMA1, DMA1_Channel3, SK32_DMA1_CH3_CMASK, 0, 8, 2, SK32_DMA1_CH3_NUMBER},
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 3
+ {DMA1, DMA1_Channel4, SK32_DMA1_CH4_CMASK, 0, 12, 3, SK32_DMA1_CH4_NUMBER},
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 4
+ {DMA1, DMA1_Channel5, SK32_DMA1_CH5_CMASK, 0, 16, 4, SK32_DMA1_CH5_NUMBER},
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 5
+ {DMA1, DMA1_Channel6, SK32_DMA1_CH6_CMASK, 0, 20, 5, SK32_DMA1_CH6_NUMBER},
+#endif
+};
+
+/*===========================================================================*/
+/* Driver local variables and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Global DMA-related data structures.
+ */
+static struct {
+ /**
+ * @brief Mask of the allocated streams.
+ */
+ uint32_t allocated_mask;
+ /**
+ * @brief Mask of the enabled streams ISRs.
+ */
+ uint32_t isr_mask;
+ /**
+ * @brief DMA IRQ redirectors.
+ */
+ struct {
+ /**
+ * @brief DMA callback function.
+ */
+ sk32_dmaisr_t func;
+ /**
+ * @brief DMA callback parameter.
+ */
+ void *param;
+ } streams[SK32_DMA_STREAMS];
+} dma;
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+/**
+ * @brief DMA1 channel 1 interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_DMA1_CH1_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ dmaServeInterrupt(SK32_DMA1_STREAM1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+/**
+ * @brief DMA1 channels 2 and 3 shared interrupt handler.
+ * @note It is declared here because this device has a non-standard
+ * DMA shared IRQ handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_DMA1_CH23_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Check on channel 2.*/
+ dmaServeInterrupt(SK32_DMA1_STREAM2);
+
+ /* Check on channel 3.*/
+ dmaServeInterrupt(SK32_DMA1_STREAM3);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+/**
+ * @brief DMA1 channels 4, 5 and 6 shared interrupt handler.
+ * @note It is declared here because this device has a non-standard
+ * DMA shared IRQ handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_DMA1_CH456_HANDLER) {
+
+ OSAL_IRQ_PROLOGUE();
+
+ /* Check on channel 4.*/
+ dmaServeInterrupt(SK32_DMA1_STREAM4);
+
+ /* Check on channel 5.*/
+ dmaServeInterrupt(SK32_DMA1_STREAM5);
+
+ /* Check on channel 6.*/
+ dmaServeInterrupt(SK32_DMA1_STREAM6);
+
+ OSAL_IRQ_EPILOGUE();
+}
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief SK32 DMA helper initialization.
+ *
+ * @init
+ */
+void dmaInit(void) {
+ int i;
+
+ dma.allocated_mask = 0U;
+ dma.isr_mask = 0U;
+ for (i = 0; i < SK32_DMA_STREAMS; i++) {
+ _sk32_dma_streams[i].channel->CCR = SK32_DMA_CCR_RESET_VALUE;
+ dma.streams[i].func = NULL;
+ }
+ DMA1->IFCR = 0xFFFFFFFFU;
+}
+
+/**
+ * @brief Allocates a DMA stream.
+ * @details The stream is allocated and, if required, the DMA clock enabled.
+ * The function also enables the IRQ vector associated to the stream
+ * and initializes its priority.
+ *
+ * @param[in] id numeric identifier of a specific stream
+ * @param[in] priority IRQ priority for the DMA stream
+ * @param[in] func handling function pointer, can be @p NULL
+ * @param[in] param a parameter to be passed to the handling function
+ * @return Pointer to the allocated @p sk32_dma_stream_t
+ * structure.
+ * @retval NULL if the stream is not available.
+ *
+ * @iclass
+ */
+const sk32_dma_stream_t *dmaStreamAllocI(uint32_t id,
+ uint32_t priority,
+ sk32_dmaisr_t func,
+ void *param) {
+ uint32_t mask;
+
+ osalDbgCheckClassI();
+ osalDbgCheck(SK32_DMA_IS_VALID_STREAM(id));
+ osalDbgCheck(SK32_DMA_IS_VALID_PRIORITY(priority));
+
+ mask = (1U << id);
+ if ((dma.allocated_mask & mask) == 0U) {
+ const sk32_dma_stream_t *dmastp = SK32_DMA_STREAM(id);
+
+ /* Installs the DMA handler.*/
+ dma.streams[id].func = func;
+ dma.streams[id].param = param;
+ dma.allocated_mask |= mask;
+
+ /* Enabling DMA1 clock.*/
+ if ((SK32_DMA1_STREAMS_MASK & mask) != 0U) {
+ rccEnableDMA1(true);
+ }
+
+ /* Enables the associated IRQ vector if not already enabled and if a
+ callback is defined.*/
+ if (func != NULL) {
+ if ((dma.isr_mask & dmastp->cmask) == 0U) {
+ nvicEnableVector(dmastp->vector, priority);
+ }
+ dma.isr_mask |= mask;
+ }
+
+ /* Putting the stream in a known state.*/
+ dmaStreamDisable(dmastp);
+ dmastp->channel->CCR = SK32_DMA_CCR_RESET_VALUE;
+
+ return dmastp;
+ }
+
+ return NULL;
+}
+
+/**
+ * @brief Allocates a DMA stream.
+ * @details The stream is allocated and, if required, the DMA clock enabled.
+ * The function also enables the IRQ vector associated to the stream
+ * and initializes its priority.
+ *
+ * @param[in] id numeric identifier of a specific stream
+ * @param[in] priority IRQ priority for the DMA stream
+ * @param[in] func handling function pointer, can be @p NULL
+ * @param[in] param a parameter to be passed to the handling function
+ * @return Pointer to the allocated @p sk32_dma_stream_t
+ * structure.
+ * @retval NULL if the stream is not available.
+ *
+ * @api
+ */
+const sk32_dma_stream_t *dmaStreamAlloc(uint32_t id,
+ uint32_t priority,
+ sk32_dmaisr_t func,
+ void *param) {
+ const sk32_dma_stream_t *dmastp;
+
+ osalSysLock();
+ dmastp = dmaStreamAllocI(id, priority, func, param);
+ osalSysUnlock();
+
+ return dmastp;
+}
+
+/**
+ * @brief Releases a DMA stream.
+ * @details The stream is freed and, if required, the DMA clock disabled.
+ * Trying to release a unallocated stream is an illegal operation
+ * and is trapped if assertions are enabled.
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ *
+ * @iclass
+ */
+void dmaStreamFreeI(const sk32_dma_stream_t *dmastp) {
+ uint32_t selfindex = (uint32_t)dmastp->selfindex;
+
+ osalDbgCheck(dmastp != NULL);
+
+ /* Check if the stream is not taken.*/
+ osalDbgAssert((dma.allocated_mask & (1U << selfindex)) != 0U,
+ "not allocated");
+
+ /* Marks the stream as not allocated.*/
+ dma.allocated_mask &= ~(1U << selfindex);
+ dma.isr_mask &= ~(1U << selfindex);
+
+ /* Disables the associated IRQ vector if it is no more in use.*/
+ if ((dma.isr_mask & dmastp->cmask) == 0U) {
+ nvicDisableVector(dmastp->vector);
+ }
+
+ /* Removes the DMA handler.*/
+ dma.streams[selfindex].func = NULL;
+ dma.streams[selfindex].param = NULL;
+
+ /* Shutting down the clock if it is no more required.*/
+ if ((dma.allocated_mask & SK32_DMA1_STREAMS_MASK) == 0U) {
+ rccDisableDMA1();
+ }
+}
+
+/**
+ * @brief Releases a DMA stream.
+ * @details The stream is freed and, if required, the DMA clock disabled.
+ * Trying to release a unallocated stream is an illegal operation
+ * and is trapped if assertions are enabled.
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ *
+ * @api
+ */
+void dmaStreamFree(const sk32_dma_stream_t *dmastp) {
+
+ osalSysLock();
+ dmaStreamFreeI(dmastp);
+ osalSysUnlock();
+}
+
+/**
+ * @brief Serves a DMA IRQ.
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ *
+ * @special
+ */
+void dmaServeInterrupt(const sk32_dma_stream_t *dmastp) {
+ uint32_t flags;
+ uint32_t selfindex = (uint32_t)dmastp->selfindex;
+
+ flags = (dmastp->dma->ISR >> dmastp->shift) & SK32_DMA_ISR_MASK;
+ if (flags & dmastp->channel->CCR) {
+ dmastp->dma->IFCR = flags << dmastp->shift;
+ if (dma.streams[selfindex].func) {
+ dma.streams[selfindex].func(dma.streams[selfindex].param, flags);
+ }
+ }
+}
+
+#endif /* SK32_DMA_REQUIRED */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/sk32_dma.h b/os/hal/ports/SK32/SK32F0xx/sk32_dma.h
new file mode 100644
index 0000000000..5a9c99fc70
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/sk32_dma.h
@@ -0,0 +1,414 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/sk32_dma.h
+ * @brief DMA helper driver header.
+ * @details This driver allows to allocate and free the DMA1 channels at
+ * runtime in order to allow all the other device drivers to
+ * coordinate the access to the shared resource. In the rest of
+ * this document the DMA channels are referred to as "streams",
+ * the same naming convention used by the STM32 and AT32 DMA helper
+ * drivers, so the exported functions can be used unchanged.
+ * @note The SK32 DMA is a classic DMA1 unit with 6 channels whose
+ * per-channel registers are CCR, CNDTR, CPAR and CMAR, exactly the
+ * register set managed by the vendor SK32F0xx_StdPeriph_Driver DMA
+ * driver; the configuration register bits used by this driver are
+ * the DMA_CCR_* constants from the SK32 device header.
+ *
+ * @addtogroup SK32F0xx_DMA
+ * @{
+ */
+
+#ifndef SK32_DMA_H
+#define SK32_DMA_H
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/**
+ * @brief DMA capability.
+ * @details if @p TRUE then the DMA is able of burst transfers, FIFOs,
+ * scatter gather and other advanced features.
+ */
+#define SK32_DMA_ADVANCED FALSE
+
+/**
+ * @brief Maximum number of transfers in a single operation.
+ */
+#define SK32_DMA_MAX_TRANSFER 65535
+
+/**
+ * @brief Total number of DMA streams.
+ * @details This is the total number of streams among all the DMA units.
+ */
+#define SK32_DMA_STREAMS (SK32_DMA1_NUM_CHANNELS + \
+ SK32_DMA2_NUM_CHANNELS)
+
+/**
+ * @brief Mask of the ISR bits passed to the DMA callback functions.
+ */
+#define SK32_DMA_ISR_MASK 0x0E
+
+/**
+ * @brief Checks if a DMA priority is within the valid range.
+ * @param[in] prio DMA priority
+ *
+ * @retval The check result.
+ * @retval false invalid DMA priority.
+ * @retval true correct DMA priority.
+ */
+#define SK32_DMA_IS_VALID_PRIORITY(prio) (((prio) >= 0U) && ((prio) <= 3U))
+
+/**
+ * @brief Checks if a DMA stream id is within the valid range.
+ *
+ * @param[in] id DMA stream id
+ * @retval The check result.
+ * @retval false invalid DMA channel.
+ * @retval true correct DMA channel.
+ */
+#define SK32_DMA_IS_VALID_STREAM(id) (((id) >= 0U) && \
+ ((id) < SK32_DMA_STREAMS))
+
+/**
+ * @brief Returns an unique numeric identifier for a DMA stream.
+ *
+ * @param[in] dma the DMA unit number
+ * @param[in] stream the stream number
+ * @return An unique numeric stream identifier.
+ */
+#define SK32_DMA_STREAM_ID(dma, stream) \
+ ((((dma) - 1) * SK32_DMA1_NUM_CHANNELS) + ((stream) - 1))
+
+/**
+ * @brief Returns a DMA stream identifier mask.
+ *
+ * @param[in] dma the DMA unit number
+ * @param[in] stream the stream number
+ * @return A DMA stream identifier mask.
+ */
+#define SK32_DMA_STREAM_ID_MSK(dma, stream) \
+ (1U << SK32_DMA_STREAM_ID(dma, stream))
+
+/**
+ * @name DMA streams identifiers
+ * @{
+ */
+/**
+ * @brief Returns a pointer to a sk32_dma_stream_t structure.
+ *
+ * @param[in] id the stream numeric identifier
+ * @return A pointer to the sk32_dma_stream_t constant structure
+ * associated to the DMA stream.
+ */
+#define SK32_DMA_STREAM(id) (&_sk32_dma_streams[id])
+
+#if SK32_DMA1_NUM_CHANNELS > 0
+#define SK32_DMA1_STREAM1 SK32_DMA_STREAM(0)
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 1
+#define SK32_DMA1_STREAM2 SK32_DMA_STREAM(1)
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 2
+#define SK32_DMA1_STREAM3 SK32_DMA_STREAM(2)
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 3
+#define SK32_DMA1_STREAM4 SK32_DMA_STREAM(3)
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 4
+#define SK32_DMA1_STREAM5 SK32_DMA_STREAM(4)
+#endif
+#if SK32_DMA1_NUM_CHANNELS > 5
+#define SK32_DMA1_STREAM6 SK32_DMA_STREAM(5)
+#endif
+/** @} */
+
+/**
+ * @name CCR register constants
+ * @note The bit values are the DMA_CCR_* constants from the vendor CMSIS
+ * device header (sk32f0xx.h), the same used by the vendor
+ * StdPeriph DMA driver (DMA_Init, DMA_Cmd, DMA_ITConfig, ...).
+ * @{
+ */
+#define SK32_DMA_CCR_RESET_VALUE 0x00000000U
+#define SK32_DMA_CR_EN DMA_CCR_EN
+#define SK32_DMA_CR_TEIE DMA_CCR_TEIE
+#define SK32_DMA_CR_HTIE DMA_CCR_HTIE
+#define SK32_DMA_CR_TCIE DMA_CCR_TCIE
+#define SK32_DMA_CR_DIR_MASK (DMA_CCR_DIR | DMA_CCR_MEM2MEM)
+#define SK32_DMA_CR_DIR_P2M 0U
+#define SK32_DMA_CR_DIR_M2P DMA_CCR_DIR
+#define SK32_DMA_CR_DIR_M2M DMA_CCR_MEM2MEM
+#define SK32_DMA_CR_CIRC DMA_CCR_CIRC
+#define SK32_DMA_CR_PINC DMA_CCR_PINC
+#define SK32_DMA_CR_MINC DMA_CCR_MINC
+#define SK32_DMA_CR_PSIZE_MASK DMA_CCR_PSIZE
+#define SK32_DMA_CR_PSIZE_BYTE 0U
+#define SK32_DMA_CR_PSIZE_HWORD DMA_CCR_PSIZE_0
+#define SK32_DMA_CR_PSIZE_WORD DMA_CCR_PSIZE_1
+#define SK32_DMA_CR_MSIZE_MASK DMA_CCR_MSIZE
+#define SK32_DMA_CR_MSIZE_BYTE 0U
+#define SK32_DMA_CR_MSIZE_HWORD DMA_CCR_MSIZE_0
+#define SK32_DMA_CR_MSIZE_WORD DMA_CCR_MSIZE_1
+#define SK32_DMA_CR_SIZE_MASK (SK32_DMA_CR_PSIZE_MASK | \
+ SK32_DMA_CR_MSIZE_MASK)
+#define SK32_DMA_CR_PL_MASK DMA_CCR_PL
+#define SK32_DMA_CR_PL(n) ((n) << 12U)
+/** @} */
+
+/**
+ * @name Status flags passed to the ISR callbacks
+ * @details The flags are pre-shifted and aligned to bit zero, the same
+ * convention used by the STM32 DMA helper.
+ * @{
+ */
+#define SK32_DMA_ISR_TEIF DMA_ISR_TEIF1
+#define SK32_DMA_ISR_HTIF DMA_ISR_HTIF1
+#define SK32_DMA_ISR_TCIF DMA_ISR_TCIF1
+/** @} */
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/**
+ * @brief Type of a DMA callback.
+ *
+ * @param[in] p parameter for the registered function
+ * @param[in] flags pre-shifted content of the ISR register, the bits
+ * are aligned to bit zero
+ */
+typedef void (*sk32_dmaisr_t)(void *p, uint32_t flags);
+
+/**
+ * @brief SK32 DMA stream descriptor structure.
+ */
+typedef struct {
+ DMA_TypeDef *dma; /**< @brief Associated DMA. */
+ DMA_Channel_TypeDef *channel; /**< @brief Associated DMA channel. */
+ uint32_t cmask; /**< @brief Mask of streams sharing
+ the same ISR. */
+ uint8_t dummy; /**< @brief Filler. */
+ uint8_t shift; /**< @brief Bit offset in ISR and
+ IFCR registers. */
+ uint8_t selfindex; /**< @brief Index to self in array. */
+ uint8_t vector; /**< @brief Associated IRQ vector. */
+} sk32_dma_stream_t;
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @name Macro Functions
+ * @{
+ */
+/**
+ * @brief Associates a peripheral data register to a DMA stream.
+ * @note This function can be invoked in both ISR or thread context.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ * @param[in] addr value to be written in the CPAR register
+ *
+ * @special
+ */
+#define dmaStreamSetPeripheral(dmastp, addr) { \
+ (dmastp)->channel->CPAR = (uint32_t)(addr); \
+}
+
+/**
+ * @brief Associates a memory destination to a DMA stream.
+ * @note This function can be invoked in both ISR or thread context.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ * @param[in] addr value to be written in the CMAR register
+ *
+ * @special
+ */
+#define dmaStreamSetMemory0(dmastp, addr) { \
+ (dmastp)->channel->CMAR = (uint32_t)(addr); \
+}
+
+/**
+ * @brief Sets the number of transfers to be performed.
+ * @note This function can be invoked in both ISR or thread context.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ * @param[in] size value to be written in the CNDTR register
+ *
+ * @special
+ */
+#define dmaStreamSetTransactionSize(dmastp, size) { \
+ (dmastp)->channel->CNDTR = (uint32_t)(size); \
+}
+
+/**
+ * @brief Returns the number of transfers to be performed.
+ * @note This function can be invoked in both ISR or thread context.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ * @return The number of transfers to be performed.
+ *
+ * @special
+ */
+#define dmaStreamGetTransactionSize(dmastp) ((size_t)((dmastp)->channel->CNDTR))
+
+/**
+ * @brief Programs the stream mode settings.
+ * @note This function can be invoked in both ISR or thread context.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ * @param[in] mode value to be written in the CCR register
+ *
+ * @special
+ */
+#define dmaStreamSetMode(dmastp, mode) { \
+ (dmastp)->channel->CCR = (uint32_t)(mode); \
+}
+
+/**
+ * @brief DMA stream enable.
+ * @note This function can be invoked in both ISR or thread context.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ *
+ * @special
+ */
+#define dmaStreamEnable(dmastp) { \
+ (dmastp)->channel->CCR |= SK32_DMA_CR_EN; \
+}
+
+/**
+ * @brief DMA stream disable.
+ * @details The function disables the specified stream and then clears any
+ * pending interrupt.
+ * @note This function can be invoked in both ISR or thread context.
+ * @note Interrupts enabling flags are set to zero after this call, see
+ * bug 3607518.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ *
+ * @special
+ */
+#define dmaStreamDisable(dmastp) { \
+ (dmastp)->channel->CCR &= ~(SK32_DMA_CR_TCIE | SK32_DMA_CR_HTIE | \
+ SK32_DMA_CR_TEIE | SK32_DMA_CR_EN); \
+ dmaStreamClearInterrupt(dmastp); \
+}
+
+/**
+ * @brief DMA stream interrupt sources clear.
+ * @note This function can be invoked in both ISR or thread context.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ *
+ * @special
+ */
+#define dmaStreamClearInterrupt(dmastp) { \
+ (dmastp)->dma->IFCR = SK32_DMA_ISR_MASK << (dmastp)->shift; \
+}
+
+/**
+ * @brief Starts a memory to memory operation using the specified stream.
+ * @note The default transfer data mode is "byte to byte" but it can be
+ * changed by specifying extra options in the @p mode parameter.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ * @param[in] mode value to be written in the CCR register, this value
+ * is implicitly ORed with:
+ * - @p SK32_DMA_CR_MINC
+ * - @p SK32_DMA_CR_PINC
+ * - @p SK32_DMA_CR_DIR_M2M
+ * - @p SK32_DMA_CR_EN
+ * .
+ * @param[in] src source address
+ * @param[in] dst destination address
+ * @param[in] n number of data units to copy
+ */
+#define dmaStartMemCopy(dmastp, mode, src, dst, n) { \
+ dmaStreamSetPeripheral(dmastp, src); \
+ dmaStreamSetMemory0(dmastp, dst); \
+ dmaStreamSetTransactionSize(dmastp, n); \
+ dmaStreamSetMode(dmastp, (mode) | \
+ SK32_DMA_CR_MINC | SK32_DMA_CR_PINC | \
+ SK32_DMA_CR_DIR_M2M | SK32_DMA_CR_EN); \
+}
+
+/**
+ * @brief Polled wait for DMA transfer end.
+ * @pre The stream must have been allocated using @p dmaStreamAlloc().
+ * @post After use the stream can be released using @p dmaStreamRelease().
+ *
+ * @param[in] dmastp pointer to a sk32_dma_stream_t structure
+ */
+#define dmaWaitCompletion(dmastp) { \
+ while ((dmastp)->channel->CNDTR > 0U) \
+ ; \
+ dmaStreamDisable(dmastp); \
+}
+/** @} */
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#if !defined(__DOXYGEN__)
+extern const sk32_dma_stream_t _sk32_dma_streams[SK32_DMA_STREAMS];
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void dmaInit(void);
+ const sk32_dma_stream_t *dmaStreamAllocI(uint32_t id,
+ uint32_t priority,
+ sk32_dmaisr_t func,
+ void *param);
+ const sk32_dma_stream_t *dmaStreamAlloc(uint32_t id,
+ uint32_t priority,
+ sk32_dmaisr_t func,
+ void *param);
+ void dmaStreamFreeI(const sk32_dma_stream_t *dmastp);
+ void dmaStreamFree(const sk32_dma_stream_t *dmastp);
+ void dmaServeInterrupt(const sk32_dma_stream_t *dmastp);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SK32_DMA_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/sk32_isr.c b/os/hal/ports/SK32/SK32F0xx/sk32_isr.c
new file mode 100644
index 0000000000..8705a4f9fb
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/sk32_isr.c
@@ -0,0 +1,175 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/sk32_isr.c
+ * @brief SK32F0xx ISR handler code.
+ * @details Only the EXTI (PAL) interrupt handlers are aggregated here, the
+ * interrupt handlers of the peripheral specific drivers are
+ * implemented inside the respective low level drivers.
+ *
+ * @addtogroup SK32F0xx_ISR
+ * @{
+ */
+
+#include "hal.h"
+
+/*===========================================================================*/
+/* Driver local definitions. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver exported variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local variables. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver local functions. */
+/*===========================================================================*/
+
+#define exti_serve_irq(pr, channel) { \
+ \
+ if ((pr) & (1U << (channel))) { \
+ _pal_isr_code(channel); \
+ } \
+}
+
+/*===========================================================================*/
+/* Driver interrupt handlers. */
+/*===========================================================================*/
+
+#if (HAL_USE_PAL && (PAL_USE_WAIT || PAL_USE_CALLBACKS)) || defined(__DOXYGEN__)
+#if !defined(SK32_DISABLE_EXTI0_1_HANDLER)
+/**
+ * @brief EXTI[0]...EXTI[1] interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_EXTI0_1_HANDLER) {
+ uint32_t pr;
+
+ OSAL_IRQ_PROLOGUE();
+
+ pr = EXTI->PR;
+ pr &= ((1U << 0) | (1U << 1));
+ EXTI->PR = pr;
+
+ exti_serve_irq(pr, 0);
+ exti_serve_irq(pr, 1);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+
+#if !defined(SK32_DISABLE_EXTI2_3_HANDLER)
+/**
+ * @brief EXTI[2]...EXTI[3] interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_EXTI2_3_HANDLER) {
+ uint32_t pr;
+
+ OSAL_IRQ_PROLOGUE();
+
+ pr = EXTI->PR;
+ pr &= ((1U << 2) | (1U << 3));
+ EXTI->PR = pr;
+
+ exti_serve_irq(pr, 2);
+ exti_serve_irq(pr, 3);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+
+#if !defined(SK32_DISABLE_EXTI4_15_HANDLER)
+/**
+ * @brief EXTI[4]...EXTI[15] interrupt handler.
+ *
+ * @isr
+ */
+OSAL_IRQ_HANDLER(SK32_EXTI4_15_HANDLER) {
+ uint32_t pr;
+
+ OSAL_IRQ_PROLOGUE();
+
+ pr = EXTI->PR;
+ pr &= ((1U << 4) | (1U << 5) | (1U << 6) | (1U << 7) | (1U << 8) |
+ (1U << 9) | (1U << 10) | (1U << 11) | (1U << 12) | (1U << 13) |
+ (1U << 14) | (1U << 15));
+ EXTI->PR = pr;
+
+ exti_serve_irq(pr, 4);
+ exti_serve_irq(pr, 5);
+ exti_serve_irq(pr, 6);
+ exti_serve_irq(pr, 7);
+ exti_serve_irq(pr, 8);
+ exti_serve_irq(pr, 9);
+ exti_serve_irq(pr, 10);
+ exti_serve_irq(pr, 11);
+ exti_serve_irq(pr, 12);
+ exti_serve_irq(pr, 13);
+ exti_serve_irq(pr, 14);
+ exti_serve_irq(pr, 15);
+
+ OSAL_IRQ_EPILOGUE();
+}
+#endif
+
+#endif /* HAL_USE_PAL && (PAL_USE_WAIT || PAL_USE_CALLBACKS) */
+
+/*===========================================================================*/
+/* Driver exported functions. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables IRQ sources.
+ * @details The NVIC vectors of the EXTI lines are enabled here because the
+ * shared EXTI handlers are implemented in this file. Every other
+ * interrupt vector of the port is enabled by the low level driver
+ * owning the peripheral.
+ *
+ * @notapi
+ */
+void irqInit(void) {
+
+#if HAL_USE_PAL
+ nvicEnableVector(SK32_EXTI0_1_NUMBER, SK32_IRQ_EXTI0_1_PRIORITY);
+ nvicEnableVector(SK32_EXTI2_3_NUMBER, SK32_IRQ_EXTI2_3_PRIORITY);
+ nvicEnableVector(SK32_EXTI4_15_NUMBER, SK32_IRQ_EXTI4_15_PRIORITY);
+#endif
+}
+
+/**
+ * @brief Disables IRQ sources.
+ *
+ * @notapi
+ */
+void irqDeinit(void) {
+
+#if HAL_USE_PAL
+ nvicDisableVector(SK32_EXTI0_1_NUMBER);
+ nvicDisableVector(SK32_EXTI2_3_NUMBER);
+ nvicDisableVector(SK32_EXTI4_15_NUMBER);
+#endif
+}
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/sk32_isr.h b/os/hal/ports/SK32/SK32F0xx/sk32_isr.h
new file mode 100644
index 0000000000..ab196f5abd
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/sk32_isr.h
@@ -0,0 +1,108 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/sk32_isr.h
+ * @brief SK32F0xx ISR handler header.
+ * @details The peripheral specific interrupt handlers of this port are
+ * implemented inside the low level drivers that own the unit
+ * (USART handlers live in the serial driver, the USB handler in
+ * the USB driver and so on), only the EXTI handlers shared by the
+ * PAL subsystem are aggregated in the file @p sk32_isr.c.
+ *
+ * @addtogroup SK32F0xx_ISR
+ * @{
+ */
+
+#ifndef SK32_ISR_H
+#define SK32_ISR_H
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/**
+ * @name Configuration options
+ * @{
+ */
+/**
+ * @brief EXTI0..1 interrupt priority level setting.
+ */
+#if !defined(SK32_IRQ_EXTI0_1_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_IRQ_EXTI0_1_PRIORITY 3
+#endif
+
+/**
+ * @brief EXTI2..3 interrupt priority level setting.
+ */
+#if !defined(SK32_IRQ_EXTI2_3_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_IRQ_EXTI2_3_PRIORITY 3
+#endif
+
+/**
+ * @brief EXTI4..15 interrupt priority level setting.
+ */
+#if !defined(SK32_IRQ_EXTI4_15_PRIORITY) || defined(__DOXYGEN__)
+#define SK32_IRQ_EXTI4_15_PRIORITY 3
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/* IRQ priority checks.*/
+#if !OSAL_IRQ_IS_VALID_PRIORITY(SK32_IRQ_EXTI0_1_PRIORITY)
+#error "Invalid IRQ priority assigned to SK32_IRQ_EXTI0_1_PRIORITY"
+#endif
+
+#if !OSAL_IRQ_IS_VALID_PRIORITY(SK32_IRQ_EXTI2_3_PRIORITY)
+#error "Invalid IRQ priority assigned to SK32_IRQ_EXTI2_3_PRIORITY"
+#endif
+
+#if !OSAL_IRQ_IS_VALID_PRIORITY(SK32_IRQ_EXTI4_15_PRIORITY)
+#error "Invalid IRQ priority assigned to SK32_IRQ_EXTI4_15_PRIORITY"
+#endif
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+ void irqInit(void);
+ void irqDeinit(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SK32_ISR_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/sk32_rcc.h b/os/hal/ports/SK32/SK32F0xx/sk32_rcc.h
new file mode 100644
index 0000000000..9e021ed4c5
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/sk32_rcc.h
@@ -0,0 +1,1035 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file SK32F0xx/sk32_rcc.h
+ * @brief RCC helper driver header.
+ * @note This file requires definitions from the SK32 CMSIS header file
+ * @p sk32f0xx.h.
+ *
+ * @addtogroup SK32F0xx_RCC
+ * @{
+ */
+
+#ifndef SK32_RCC_H
+#define SK32_RCC_H
+
+/*===========================================================================*/
+/* Driver constants. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver pre-compile time settings. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Derived constants and error checks. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver data structures and types. */
+/*===========================================================================*/
+
+/*===========================================================================*/
+/* Driver macros. */
+/*===========================================================================*/
+
+/**
+ * @name Generic RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the clock of one or more peripheral on the APB1 bus.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] mask APB1 peripherals mask
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableAPB1(mask, lp) { \
+ RCC->APB1ENR |= (mask); \
+ (void)RCC->APB1ENR; \
+}
+
+/**
+ * @brief Disables the clock of one or more peripheral on the APB1 bus.
+ *
+ * @param[in] mask APB1 peripherals mask
+ *
+ * @api
+ */
+#define rccDisableAPB1(mask) { \
+ RCC->APB1ENR &= ~(mask); \
+ (void)RCC->APB1ENR; \
+}
+
+/**
+ * @brief Resets one or more peripheral on the APB1 bus.
+ *
+ * @param[in] mask APB1 peripherals mask
+ *
+ * @api
+ */
+#define rccResetAPB1(mask) { \
+ RCC->APB1RSTR |= (mask); \
+ RCC->APB1RSTR &= ~(mask); \
+ (void)RCC->APB1RSTR; \
+}
+
+/**
+ * @brief Enables the clock of one or more peripheral on the APB2 bus.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] mask APB2 peripherals mask
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableAPB2(mask, lp) { \
+ RCC->APB2ENR |= (mask); \
+ (void)RCC->APB2ENR; \
+}
+
+/**
+ * @brief Disables the clock of one or more peripheral on the APB2 bus.
+ *
+ * @param[in] mask APB2 peripherals mask
+ *
+ * @api
+ */
+#define rccDisableAPB2(mask) { \
+ RCC->APB2ENR &= ~(mask); \
+ (void)RCC->APB2ENR; \
+}
+
+/**
+ * @brief Resets one or more peripheral on the APB2 bus.
+ *
+ * @param[in] mask APB2 peripherals mask
+ *
+ * @api
+ */
+#define rccResetAPB2(mask) { \
+ RCC->APB2RSTR |= (mask); \
+ RCC->APB2RSTR &= ~(mask); \
+ (void)RCC->APB2RSTR; \
+}
+
+/**
+ * @brief Enables the clock of one or more peripheral on the AHB bus.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] mask AHB peripherals mask
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableAHB(mask, lp) { \
+ RCC->AHBENR |= (mask); \
+ (void)RCC->AHBENR; \
+}
+
+/**
+ * @brief Disables the clock of one or more peripheral on the AHB bus.
+ *
+ * @param[in] mask AHB peripherals mask
+ *
+ * @api
+ */
+#define rccDisableAHB(mask) { \
+ RCC->AHBENR &= ~(mask); \
+ (void)RCC->AHBENR; \
+}
+
+/**
+ * @brief Resets one or more peripheral on the AHB bus.
+ *
+ * @param[in] mask AHB peripherals mask
+ *
+ * @api
+ */
+#define rccResetAHB(mask) { \
+ RCC->AHBRSTR |= (mask); \
+ RCC->AHBRSTR &= ~(mask); \
+ (void)RCC->AHBRSTR; \
+}
+/** @} */
+
+/**
+ * @name ADC peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the ADC1 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableADC1(lp) rccEnableAPB2(RCC_APB2ENR_ADC1EN, lp)
+
+/**
+ * @brief Disables the ADC1 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableADC1() rccDisableAPB2(RCC_APB2ENR_ADC1EN)
+
+/**
+ * @brief Resets the ADC1 peripheral.
+ * @note The SK32 CMSIS header does not define RCC_APB2RSTR_ADC1RST, the
+ * reset bit is aliased to the (bit-identical) enable bit value.
+ *
+ * @api
+ */
+#if !defined(RCC_APB2RSTR_ADC1RST)
+#define RCC_APB2RSTR_ADC1RST RCC_APB2ENR_ADC1RST
+#endif
+#define rccResetADC1() rccResetAPB2(RCC_APB2RSTR_ADC1RST)
+/** @} */
+
+/**
+ * @name CAN peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the CAN1 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableCAN1(lp) rccEnableAPB1(RCC_APB1ENR_CANEN, lp)
+
+/**
+ * @brief Disables the CAN1 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableCAN1() rccDisableAPB1(RCC_APB1ENR_CANEN)
+
+/**
+ * @brief Resets the CAN1 peripheral.
+ *
+ * @api
+ */
+#define rccResetCAN1() rccResetAPB1(RCC_APB1RSTR_CANRST)
+/** @} */
+
+/**
+ * @name DAC peripheral specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the DAC1 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableDAC1(lp) rccEnableAPB1(RCC_APB1ENR_DACEN, lp)
+
+/**
+ * @brief Disables the DAC1 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableDAC1() rccDisableAPB1(RCC_APB1ENR_DACEN)
+
+/**
+ * @brief Resets the DAC1 peripheral.
+ *
+ * @api
+ */
+#define rccResetDAC1() rccResetAPB1(RCC_APB1RSTR_DACRST)
+/** @} */
+
+/**
+ * @name PWR interface specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the PWR interface clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnablePWRInterface(lp) rccEnableAPB1(RCC_APB1ENR_PWREN, lp)
+
+/**
+ * @brief Disables PWR interface clock.
+ *
+ * @api
+ */
+#define rccDisablePWRInterface() rccDisableAPB1(RCC_APB1ENR_PWREN)
+
+/**
+ * @brief Resets the PWR interface.
+ *
+ * @api
+ */
+#define rccResetPWRInterface() rccResetAPB1(RCC_APB1RSTR_PWRRST)
+/** @} */
+
+/**
+ * @name DMA peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the DMA1 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableDMA1(lp) rccEnableAHB(RCC_AHBENR_DMAEN, lp)
+
+/**
+ * @brief Disables the DMA1 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableDMA1() rccDisableAHB(RCC_AHBENR_DMAEN)
+
+/**
+ * @brief Resets the DMA1 peripheral.
+ * @note Not supported in this family, does nothing.
+ *
+ * @api
+ */
+#define rccResetDMA1()
+
+/**
+ * @brief Enables the DMA2 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableDMA2(lp) rccEnableAHB(RCC_AHBENR_DMA2EN, lp)
+
+/**
+ * @brief Disables the DMA2 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableDMA2() rccDisableAHB(RCC_AHBENR_DMA2EN)
+
+/**
+ * @brief Resets the DMA2 peripheral.
+ * @note Not supported in this family, does nothing.
+ *
+ * @api
+ */
+#define rccResetDMA2()
+/** @} */
+
+/**
+ * @name I2C peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the I2C1 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableI2C1(lp) rccEnableAPB1(RCC_APB1ENR_I2C1EN, lp)
+
+/**
+ * @brief Disables the I2C1 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableI2C1() rccDisableAPB1(RCC_APB1ENR_I2C1EN)
+
+/**
+ * @brief Resets the I2C1 peripheral.
+ * @note The SK32 CMSIS header does not define RCC_APB1RSTR_I2C1RST, the
+ * reset bit is aliased to the (bit-identical) enable bit value.
+ *
+ * @api
+ */
+#if !defined(RCC_APB1RSTR_I2C1RST)
+#define RCC_APB1RSTR_I2C1RST RCC_APB1ENR_I2C1RST
+#endif
+#define rccResetI2C1() rccResetAPB1(RCC_APB1RSTR_I2C1RST)
+
+/**
+ * @brief Enables the I2C2 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableI2C2(lp) rccEnableAPB1(RCC_APB1ENR_I2C2EN, lp)
+
+/**
+ * @brief Disables the I2C2 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableI2C2() rccDisableAPB1(RCC_APB1ENR_I2C2EN)
+
+/**
+ * @brief Resets the I2C2 peripheral.
+ *
+ * @api
+ */
+#define rccResetI2C2() rccResetAPB1(RCC_APB1RSTR_I2C2RST)
+/** @} */
+
+/**
+ * @name SPI peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the SPI1 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableSPI1(lp) rccEnableAPB2(RCC_APB2ENR_SPI1EN, lp)
+
+/**
+ * @brief Disables the SPI1 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableSPI1() rccDisableAPB2(RCC_APB2ENR_SPI1EN)
+
+/**
+ * @brief Resets the SPI1 peripheral.
+ *
+ * @api
+ */
+#define rccResetSPI1() rccResetAPB2(RCC_APB2RSTR_SPI1RST)
+
+/**
+ * @brief Enables the SPI2 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableSPI2(lp) rccEnableAPB1(RCC_APB1ENR_SPI2EN, lp)
+
+/**
+ * @brief Disables the SPI2 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableSPI2() rccDisableAPB1(RCC_APB1ENR_SPI2EN)
+
+/**
+ * @brief Resets the SPI2 peripheral.
+ *
+ * @api
+ */
+#define rccResetSPI2() rccResetAPB1(RCC_APB1RSTR_SPI2RST)
+/** @} */
+
+/**
+ * @name TIM peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the TIM1 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableTIM1(lp) rccEnableAPB2(RCC_APB2ENR_TIM1EN, lp)
+
+/**
+ * @brief Disables the TIM1 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableTIM1() rccDisableAPB2(RCC_APB2ENR_TIM1EN)
+
+/**
+ * @brief Resets the TIM1 peripheral.
+ *
+ * @api
+ */
+#define rccResetTIM1() rccResetAPB2(RCC_APB2RSTR_TIM1RST)
+
+/**
+ * @brief Enables the TIM2 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableTIM2(lp) rccEnableAPB1(RCC_APB1ENR_TIM2EN, lp)
+
+/**
+ * @brief Disables the TIM2 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableTIM2() rccDisableAPB1(RCC_APB1ENR_TIM2EN)
+
+/**
+ * @brief Resets the TIM2 peripheral.
+ *
+ * @api
+ */
+#define rccResetTIM2() rccResetAPB1(RCC_APB1RSTR_TIM2RST)
+
+/**
+ * @brief Enables the TIM3 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableTIM3(lp) rccEnableAPB1(RCC_APB1ENR_TIM3EN, lp)
+
+/**
+ * @brief Disables the TIM3 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableTIM3() rccDisableAPB1(RCC_APB1ENR_TIM3EN)
+
+/**
+ * @brief Resets the TIM3 peripheral.
+ * @note The SK32 CMSIS header does not define RCC_APB1RSTR_TIM3RST, the
+ * reset bit is aliased to the (bit-identical) enable bit value.
+ *
+ * @api
+ */
+#if !defined(RCC_APB1RSTR_TIM3RST)
+#define RCC_APB1RSTR_TIM3RST RCC_APB1ENR_TIM3RST
+#endif
+#define rccResetTIM3() rccResetAPB1(RCC_APB1RSTR_TIM3RST)
+
+/**
+ * @brief Enables the TIM6 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableTIM6(lp) rccEnableAPB1(RCC_APB1ENR_TIM6EN, lp)
+
+/**
+ * @brief Disables the TIM6 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableTIM6() rccDisableAPB1(RCC_APB1ENR_TIM6EN)
+
+/**
+ * @brief Resets the TIM6 peripheral.
+ *
+ * @api
+ */
+#define rccResetTIM6() rccResetAPB1(RCC_APB1RSTR_TIM6RST)
+
+/**
+ * @brief Enables the TIM7 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableTIM7(lp) rccEnableAPB1(RCC_APB1ENR_TIM7EN, lp)
+
+/**
+ * @brief Disables the TIM7 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableTIM7() rccDisableAPB1(RCC_APB1ENR_TIM7EN)
+
+/**
+ * @brief Resets the TIM7 peripheral.
+ *
+ * @api
+ */
+#define rccResetTIM7() rccResetAPB1(RCC_APB1RSTR_TIM7RST)
+
+/**
+ * @brief Enables the TIM14 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableTIM14(lp) rccEnableAPB1(RCC_APB1ENR_TIM14EN, lp)
+
+/**
+ * @brief Disables the TIM14 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableTIM14() rccDisableAPB1(RCC_APB1ENR_TIM14EN)
+
+/**
+ * @brief Resets the TIM14 peripheral.
+ *
+ * @api
+ */
+#define rccResetTIM14() rccResetAPB1(RCC_APB1RSTR_TIM14RST)
+
+/**
+ * @brief Enables the TIM15 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableTIM15(lp) rccEnableAPB2(RCC_APB2ENR_TIM15EN, lp)
+
+/**
+ * @brief Disables the TIM15 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableTIM15() rccDisableAPB2(RCC_APB2ENR_TIM15EN)
+
+/**
+ * @brief Resets the TIM15 peripheral.
+ *
+ * @api
+ */
+#define rccResetTIM15() rccResetAPB2(RCC_APB2RSTR_TIM15RST)
+
+/**
+ * @brief Enables the TIM16 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableTIM16(lp) rccEnableAPB2(RCC_APB2ENR_TIM16EN, lp)
+
+/**
+ * @brief Disables the TIM16 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableTIM16() rccDisableAPB2(RCC_APB2ENR_TIM16EN)
+
+/**
+ * @brief Resets the TIM16 peripheral.
+ * @note The SK32 CMSIS header does not define RCC_APB2RSTR_TIM16RST, the
+ * reset bit is aliased to the (bit-identical) enable bit value.
+ *
+ * @api
+ */
+#if !defined(RCC_APB2RSTR_TIM16RST)
+#define RCC_APB2RSTR_TIM16RST RCC_APB2ENR_TIM16RST
+#endif
+#define rccResetTIM16() rccResetAPB2(RCC_APB2RSTR_TIM16RST)
+
+/**
+ * @brief Enables the TIM17 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableTIM17(lp) rccEnableAPB2(RCC_APB2ENR_TIM17EN, lp)
+
+/**
+ * @brief Disables the TIM17 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableTIM17() rccDisableAPB2(RCC_APB2ENR_TIM17EN)
+
+/**
+ * @brief Resets the TIM17 peripheral.
+ * @note The SK32 CMSIS header does not define RCC_APB2RSTR_TIM17RST, the
+ * reset bit is aliased to the (bit-identical) enable bit value.
+ *
+ * @api
+ */
+#if !defined(RCC_APB2RSTR_TIM17RST)
+#define RCC_APB2RSTR_TIM17RST RCC_APB2ENR_TIM17RST
+#endif
+#define rccResetTIM17() rccResetAPB2(RCC_APB2RSTR_TIM17RST)
+/** @} */
+
+/**
+ * @name USART/UART peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the USART1 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableUSART1(lp) rccEnableAPB2(RCC_APB2ENR_USART1EN, lp)
+
+/**
+ * @brief Disables the USART1 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableUSART1() rccDisableAPB2(RCC_APB2ENR_USART1EN)
+
+/**
+ * @brief Resets the USART1 peripheral.
+ *
+ * @api
+ */
+#define rccResetUSART1() rccResetAPB2(RCC_APB2RSTR_USART1RST)
+
+/**
+ * @brief Enables the USART2 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableUSART2(lp) rccEnableAPB1(RCC_APB1ENR_USART2EN, lp)
+
+/**
+ * @brief Disables the USART2 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableUSART2() rccDisableAPB1(RCC_APB1ENR_USART2EN)
+
+/**
+ * @brief Resets the USART2 peripheral.
+ *
+ * @api
+ */
+#define rccResetUSART2() rccResetAPB1(RCC_APB1RSTR_USART2RST)
+
+/**
+ * @brief Enables the USART3 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableUSART3(lp) rccEnableAPB1(RCC_APB1ENR_USART3EN, lp)
+
+/**
+ * @brief Disables the USART3 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableUSART3() rccDisableAPB1(RCC_APB1ENR_USART3EN)
+
+/**
+ * @brief Resets the USART3 peripheral.
+ *
+ * @api
+ */
+#define rccResetUSART3() rccResetAPB1(RCC_APB1RSTR_USART3RST)
+
+/**
+ * @brief Enables the USART4 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableUART4(lp) rccEnableAPB1(RCC_APB1ENR_USART4EN, lp)
+
+/**
+ * @brief Disables the USART4 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableUART4() rccDisableAPB1(RCC_APB1ENR_USART4EN)
+
+/**
+ * @brief Resets the USART4 peripheral.
+ *
+ * @api
+ */
+#define rccResetUART4() rccResetAPB1(RCC_APB1RSTR_USART4RST)
+
+/**
+ * @brief Enables the USART5 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableUART5(lp) rccEnableAPB1(RCC_APB1ENR_USART5EN, lp)
+
+/**
+ * @brief Disables the USART5 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableUART5() rccDisableAPB1(RCC_APB1ENR_USART5EN)
+
+/**
+ * @brief Resets the USART5 peripheral.
+ *
+ * @api
+ */
+#define rccResetUART5() rccResetAPB1(RCC_APB1RSTR_USART5RST)
+
+/**
+ * @brief Enables the USART6 peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableUSART6(lp) rccEnableAPB2(RCC_APB2ENR_USART6EN, lp)
+
+/**
+ * @brief Disables the USART6 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableUSART6() rccDisableAPB2(RCC_APB2ENR_USART6EN)
+
+/**
+ * @brief Resets the USART6 peripheral.
+ *
+ * @api
+ */
+#define rccResetUSART6() rccResetAPB2(RCC_APB2RSTR_USART6RST)
+
+/**
+ * @brief Enables the UART7 peripheral clock.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableUART7(lp) rccEnableAPB2(RCC_APB2ENR_USART7EN, lp)
+
+/**
+ * @brief Disables the UART7 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableUART7() rccDisableAPB2(RCC_APB2ENR_USART7EN)
+
+/**
+ * @brief Resets the UART7 peripheral.
+ *
+ * @api
+ */
+#define rccResetUART7() rccResetAPB2(RCC_APB2RSTR_USART7RST)
+
+/**
+ * @brief Enables the UART8 peripheral clock.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableUART8(lp) rccEnableAPB2(RCC_APB2ENR_USART8EN, lp)
+
+/**
+ * @brief Disables the UART8 peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableUART8() rccDisableAPB2(RCC_APB2ENR_USART8EN)
+
+/**
+ * @brief Resets the UART8 peripheral.
+ *
+ * @api
+ */
+#define rccResetUART8() rccResetAPB2(RCC_APB2RSTR_USART8RST)
+/** @} */
+
+/**
+ * @name USB peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the USB peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ * @note The SK32 CMSIS header names the APB1ENR USB clock gate bit
+ * USBRST (same convention as the STM32F0x2 devices), so it is used
+ * here in place of the STM32-generic USBEN name.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableUSB(lp) rccEnableAPB1(RCC_APB1ENR_USBRST, lp)
+
+/**
+ * @brief Disables the USB peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableUSB() rccDisableAPB1(RCC_APB1ENR_USBRST)
+
+/**
+ * @brief Resets the USB peripheral.
+ * @note The SK32 CMSIS header does not define RCC_APB1RSTR_USBRST, the
+ * reset bit is aliased to the (bit-identical) enable bit value.
+ *
+ * @api
+ */
+#if !defined(RCC_APB1RSTR_USBRST)
+#define RCC_APB1RSTR_USBRST RCC_APB1ENR_USBRST
+#endif
+#define rccResetUSB() rccResetAPB1(RCC_APB1RSTR_USBRST)
+/** @} */
+
+/**
+ * @name CRC peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the CRC peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableCRC(lp) rccEnableAHB(RCC_AHBENR_CRCEN, lp)
+
+/**
+ * @brief Disables the CRC peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableCRC() rccDisableAHB(RCC_AHBENR_CRCEN)
+
+/**
+ * @brief Resets the CRC peripheral.
+ *
+ * @api
+ */
+#define rccResetCRC() rccResetAHB(RCC_AHBRSTR_CRCRST)
+/** @} */
+
+/**
+ * @name KBCU peripheral specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the KBCU peripheral clock.
+ * @note The KBCU (Keyboard Control Unit) is a 3Think peripheral on the
+ * APB1 bus, present on both the SK32F072 and SK32F077 variants.
+ * The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableKBCU(lp) rccEnableAPB1(RCC_APB1ENR_KBCUEN, lp)
+
+/**
+ * @brief Disables the KBCU peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableKBCU() rccDisableAPB1(RCC_APB1ENR_KBCUEN)
+
+/**
+ * @brief Resets the KBCU peripheral.
+ * @note The SK32 CMSIS header defines RCC_APB1RSTR_KBCURST with the same
+ * value as the (bit-identical) enable bit, kept for clarity.
+ *
+ * @api
+ */
+#if !defined(RCC_APB1RSTR_KBCURST)
+#define RCC_APB1RSTR_KBCURST RCC_APB1ENR_KBCUEN
+#endif
+#define rccResetKBCU() rccResetAPB1(RCC_APB1RSTR_KBCURST)
+/** @} */
+
+/**
+ * @name WWDG peripherals specific RCC operations
+ * @{
+ */
+/**
+ * @brief Enables the WWDG peripheral clock.
+ * @note The @p lp parameter is ignored in this family.
+ *
+ * @param[in] lp low power enable flag
+ *
+ * @api
+ */
+#define rccEnableWWDG(lp) rccEnableAPB1(RCC_APB1ENR_WWDGEN, lp)
+
+/**
+ * @brief Disables the WWDG peripheral clock.
+ *
+ * @api
+ */
+#define rccDisableWWDG() rccDisableAPB1(RCC_APB1ENR_WWDGEN)
+
+/**
+ * @brief Resets the WWDG peripheral.
+ *
+ * @api
+ */
+#define rccResetWWDG() rccResetAPB1(RCC_APB1RSTR_WWDGRST)
+/** @} */
+
+/*===========================================================================*/
+/* External declarations. */
+/*===========================================================================*/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SK32_RCC_H */
+
+/** @} */
diff --git a/os/hal/ports/SK32/SK32F0xx/sk32_registry.h b/os/hal/ports/SK32/SK32F0xx/sk32_registry.h
new file mode 100644
index 0000000000..d8141f6326
--- /dev/null
+++ b/os/hal/ports/SK32/SK32F0xx/sk32_registry.h
@@ -0,0 +1,251 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+/**
+ * @file sk32_registry.h
+ * @brief SK32F0xx capabilities registry.
+ *
+ * @addtogroup HAL
+ * @{
+ */
+
+#ifndef SK32_REGISTRY_H
+#define SK32_REGISTRY_H
+
+/*===========================================================================*/
+/* Platform capabilities. */
+/*===========================================================================*/
+
+/**
+ * @brief Maximum system and core clock (SYSCLK) frequency.
+ */
+#define SK32_SYSCLK_MAX 72000000UL
+
+/**
+ * @brief HSI (internal high speed oscillator) frequency.
+ */
+#define SK32_HSICLK 8000000UL
+
+/**
+ * @brief PLL48 (internal oscillator used for the USB) frequency.
+ * @note Enabled through the RCC->CR2.PLL48ON bit, it is independent from
+ * the main PLL used for the system clock.
+ */
+#define SK32_PLL48CLK 48000000UL
+
+/**
+ * @name GPIO attributes
+ * @{
+ */
+/* The AHB clock/reset registers expose enable bits for ports A..F on the
+ whole SK32F0xx family. The GPIOF port is present on the SK32F077 while the
+ GPIOE port is present on the SK32F072.*/
+#define SK32_NUM_GPIO 6
+#define SK32_HAS_GPIOA TRUE
+#define SK32_HAS_GPIOB TRUE
+#define SK32_HAS_GPIOC TRUE
+#define SK32_HAS_GPIOD TRUE
+#if defined(SK32F077xB)
+#define SK32_HAS_GPIOE FALSE
+#define SK32_HAS_GPIOF TRUE
+#else
+#define SK32_HAS_GPIOE TRUE
+#define SK32_HAS_GPIOF FALSE
+#endif
+/** @} */
+
+/**
+ * @name Peripheral presence
+ * @note Whole-family peripherals (declared regardless of the variant):
+ * GPIO, USART1/2, SPI1, I2C1, ADC1, DMA1, USB (musbfsfc).
+ * @note On the SK32F077 variant the I2C2 and SPI2 units and the TIM1,
+ * TIM2 timers are not present (their interrupt slots are reserved
+ * in the vector table).
+ * @{
+ */
+#define SK32_HAS_USART1 TRUE
+#define SK32_HAS_USART2 TRUE
+#define SK32_HAS_SPI1 TRUE
+#define SK32_HAS_I2C1 TRUE
+#define SK32_HAS_ADC1 TRUE
+#define SK32_HAS_DMA1 TRUE
+#define SK32_HAS_USB TRUE
+#define SK32_HAS_TIM16 TRUE
+#define SK32_HAS_TIM17 TRUE
+
+#if defined(SK32F077xB)
+#define SK32_HAS_SPI2 FALSE
+#define SK32_HAS_I2C2 FALSE
+#define SK32_HAS_TIM1 FALSE
+#define SK32_HAS_TIM2 FALSE
+#define SK32_HAS_TIM3 TRUE
+#else
+#define SK32_HAS_SPI2 TRUE
+#define SK32_HAS_I2C2 TRUE
+#define SK32_HAS_TIM1 TRUE
+#define SK32_HAS_TIM2 TRUE
+#define SK32_HAS_TIM3 TRUE
+#endif
+/** @} */
+
+/**
+ * @name Interrupt vectors.
+ * @details A Cortex-M0 vector slot is identified by the byte offset (in hex)
+ * of its entry inside the vector table, IRQn maps to
+ * Vector(0x40 + 4 * IRQn).
+ * @note The IRQn values are the ones declared in the device CMSIS header
+ * (sk32f0xx.h, IRQn_Type).
+ * @note On the SK32F077 variant the slots 8 (TSC), 13-15 (TIM1, TIM2),
+ * 24 (I2C2) and 26 (SPI2) are Reserved; they are still declared in
+ * the family-wide device header.
+ * @{
+ */
+#define SK32_WWDG_NUMBER 0
+#define SK32_PVD_VDDIO2_NUMBER 1
+#define SK32_RTC_NUMBER 2
+#define SK32_FLASH_NUMBER 3
+#define SK32_RCC_CRS_NUMBER 4
+#define SK32_EXTI0_1_NUMBER 5
+#define SK32_EXTI2_3_NUMBER 6
+#define SK32_EXTI4_15_NUMBER 7
+#define SK32_TSC_NUMBER 8
+#define SK32_DMA1_CH1_NUMBER 9
+#define SK32_DMA1_CH23_NUMBER 10
+#define SK32_DMA1_CH456_NUMBER 11
+#define SK32_ADC_COMP_NUMBER 12
+#define SK32_TIM1_BRK_UP_TRG_COM_NUMBER 13
+#define SK32_TIM1_CC_NUMBER 14
+#define SK32_TIM2_NUMBER 15
+#define SK32_TIM3_NUMBER 16
+#define SK32_TIM6_DAC_NUMBER 17
+#define SK32_TIM16_NUMBER 21
+#define SK32_TIM17_NUMBER 22
+#define SK32_I2C1_NUMBER 23
+#define SK32_I2C2_NUMBER 24
+#define SK32_SPI1_NUMBER 25
+#define SK32_SPI2_NUMBER 26
+#define SK32_USART1_NUMBER 27
+#define SK32_USART2_NUMBER 28
+#define SK32_USB_NUMBER 31
+
+#if defined(SK32F077xB) || defined(__DOXYGEN__)
+#define SK32_SLED_NUMBER 29
+#define SK32_KBCU_NUMBER 30
+#endif
+
+#define SK32_WWDG_HANDLER Vector40
+#define SK32_PVD_VDDIO2_HANDLER Vector44
+#define SK32_RTC_HANDLER Vector48
+#define SK32_FLASH_HANDLER Vector4C
+#define SK32_RCC_CRS_HANDLER Vector50
+#define SK32_EXTI0_1_HANDLER Vector54
+#define SK32_EXTI2_3_HANDLER Vector58
+#define SK32_EXTI4_15_HANDLER Vector5C
+#define SK32_TSC_HANDLER Vector60
+#define SK32_DMA1_CH1_HANDLER Vector64
+#define SK32_DMA1_CH23_HANDLER Vector68
+#define SK32_DMA1_CH456_HANDLER Vector6C
+#define SK32_ADC_COMP_HANDLER Vector70
+#define SK32_TIM1_BRK_UP_TRG_COM_HANDLER Vector74
+#define SK32_TIM1_CC_HANDLER Vector78
+#define SK32_TIM2_HANDLER Vector7C
+#define SK32_TIM3_HANDLER Vector80
+#define SK32_TIM6_DAC_HANDLER Vector84
+#define SK32_TIM16_HANDLER Vector94
+#define SK32_TIM17_HANDLER Vector98
+#define SK32_I2C1_HANDLER Vector9C
+#define SK32_I2C2_HANDLER VectorA0
+#define SK32_SPI1_HANDLER VectorA4
+#define SK32_SPI2_HANDLER VectorA8
+#define SK32_USART1_HANDLER VectorAC
+#define SK32_USART2_HANDLER VectorB0
+#define SK32_USB_HANDLER VectorBC
+
+#if defined(SK32F077xB) || defined(__DOXYGEN__)
+#define SK32_SLED_HANDLER VectorB4
+#define SK32_KBCU_HANDLER VectorB8
+#endif
+/** @} */
+
+/*===========================================================================*/
+/* DMA attributes. */
+/*===========================================================================*/
+
+/**
+ * @name DMA1 channels mapping
+ * @details The DMA1 channels are aggregated on three NVIC vectors: channel 1
+ * owns a dedicated vector while channels 2-3 and channels 4-6 share
+ * one vector each. Every channel is mapped on the vector of its
+ * group and a collision mask (CMASK) groups the channels that are
+ * served by the same ISR entry; the collision masks are used by the
+ * DMA driver to enable/disable a shared vector only on the first
+ * allocation and on the last release inside the group.
+ * @{
+ */
+#define SK32_DMA1_NUM_CHANNELS 6
+#define SK32_DMA2_NUM_CHANNELS 0
+
+#define SK32_DMA1_CH2_NUMBER SK32_DMA1_CH23_NUMBER
+#define SK32_DMA1_CH3_NUMBER SK32_DMA1_CH23_NUMBER
+#define SK32_DMA1_CH4_NUMBER SK32_DMA1_CH456_NUMBER
+#define SK32_DMA1_CH5_NUMBER SK32_DMA1_CH456_NUMBER
+#define SK32_DMA1_CH6_NUMBER SK32_DMA1_CH456_NUMBER
+
+#define SK32_DMA1_CH1_CMASK 0x00000001U
+#define SK32_DMA1_CH2_CMASK 0x00000006U
+#define SK32_DMA1_CH3_CMASK 0x00000006U
+#define SK32_DMA1_CH4_CMASK 0x00000078U
+#define SK32_DMA1_CH5_CMASK 0x00000078U
+#define SK32_DMA1_CH6_CMASK 0x00000078U
+/** @} */
+
+/*===========================================================================*/
+/* Flash attributes. */
+/*===========================================================================*/
+
+/**
+ * @brief Flash wait states required above the HSI frequency.
+ * @note The vendor clock configuration applies 2 wait states at 72MHz
+ * (and keeps them at 48MHz as well).
+ */
+#define SK32_FLASH_WAIT_STATES 2
+
+/**
+ * @name Embedded Flash geometry.
+ * @details The SK32F077 internal flash is single-bank, 128 KByte, organised in
+ * 2 KByte pages (unlike the STM32F072 1 KByte pages). The write line
+ * is a half-word (2 bytes) exactly like the STM32F0 family. These
+ * macros are consumed by the EFL low level driver (hal_efl_lld.c)
+ * which derives its sector/count/size and the programming alignment
+ * from them; they must not be redefined elsewhere.
+ * @{
+ */
+#if !defined(STM32_FLASH_LINE_SIZE)
+#define STM32_FLASH_LINE_SIZE 2
+#endif
+#if !defined(STM32_FLASH_SECTOR_SIZE)
+#define STM32_FLASH_SECTOR_SIZE 2048U
+#endif
+#if !defined(STM32_FLASH_NUMBER_OF_BANKS)
+#define STM32_FLASH_NUMBER_OF_BANKS 1
+#endif
+#if !defined(STM32_FLASH_SECTORS_PER_BANK)
+#define STM32_FLASH_SECTORS_PER_BANK 64U
+#endif
+/** @} */
+
+#endif /* SK32_REGISTRY_H */
diff --git a/testhal/SK32/ADC/Makefile b/testhal/SK32/ADC/Makefile
new file mode 100644
index 0000000000..1016e8ce58
--- /dev/null
+++ b/testhal/SK32/ADC/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = adc
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/testhal/SK32/ADC/adc.hex b/testhal/SK32/ADC/adc.hex
new file mode 100644
index 0000000000..8eb0b016f6
--- /dev/null
+++ b/testhal/SK32/ADC/adc.hex
@@ -0,0 +1,298 @@
+:020000040800F2
+:100000000004002091010008710F0008930100080E
+:100010009301000893010008930100089301000870
+:100020009301000893010008930100089301000860
+:1000300093010008930100089301000801050008DE
+:100040009301000893010008930100089301000840
+:100050009301000893010008930100089301000830
+:100060009301000893010008930100089301000820
+:10007000410800089301000893010008930100085B
+:100080009301000893010008930100089301000800
+:1000900093010008930100089301000893010008F0
+:1000A00093010008930100089301000893010008E0
+:1000B00093010008930100089301000893010008D0
+:1000C00072B6244880F30888234880F30988022008
+:1000D00080F31488BFF36F8F00F0DAF800F0D0FBE4
+:1000E0001E481F491B4A914202DA08600431FAE7B0
+:1000F0001C49194A914202DA08600431FAE71A49A8
+:100100001A4A1B4B9A4204DA086810600431043220
+:10011000F8E700201749184A914202DA08600431D2
+:10012000FAE700F0CDF800F0BBF8144C144DAC42E7
+:1001300003DA216888470434F9E700F051FF114CD5
+:10014000114DAC4203DA216888470434F9E70F49BE
+:1001500008470000000400200008002055555555B0
+:1001600000000020000400204812000800080020C1
+:100170000408002008080020240A0020C00000080D
+:10018000C0000008C0000008C0000008B10200085C
+:1001900096E700F000F8FEE7F0B544464D465646B7
+:1001A0005F46F0B46B46CB60C3689D46F0BCA0468A
+:1001B000A946B246BB46F0BD62B6281CA047002047
+:1001C00000F0EEFDFEE700F01BFD024A024B13605B
+:1001D000FEE7C04604ED00E000000080F0B5050039
+:1001E000830749D0541E002A45D003000326CAB213
+:1001F00002E00135013C3ED301332A703342F8D18D
+:10020000032C30D9FF220A401502AD182A04AD187C
+:100210000F2C34D92700103F3F093E01B4461E0081
+:100220001A0010366644156055609560D56010322E
+:100230009642F8D101373F01DB190F270C222740E6
+:1002400022403C00002A0ED03E1FB608B400A4464F
+:100250001A001C1D644420C2A242FCD103240136B2
+:10026000B6009B193C40002C05D0C9B21C1919706E
+:1002700001339C42FBD1F0BD03001400C0E727000E
+:10028000E2E7C0460000000000000000000000009F
+:100290007047C046000000000000000000000000A1
+:1002A0007047C04600000000000000000000000091
+:1002B000FEE7C04600000000000000000000000053
+:1002C00070B5114C250080356068A26821689042A5
+:1002D0000AD2030040C940C39342FBD3013A131A28
+:1002E0009B0801339B00C018E368834207D9013B98
+:1002F0001A1A9208013200219200FFF76FFF1034A2
+:10030000AC42E1D170BDC0460411000800000000FD
+:1003100010B500F095F800F01BF900F011F800F0AE
+:1003200017FB00F005F810BD000000000000000001
+:1003300010B500F0F5F810BD00000000000000004E
+:1003400010B500F0ADF910BD000000000000000085
+:10035000012310B50370002343608360C360036111
+:100360004361183000F06CFD10BDC0460000000075
+:1003700010B5040072B6416000F0AAF902232370A0
+:1003800062B6002010BDC046000000000000000062
+:1003900010B5040072B6C36003238260037001616C
+:1003A00000F0B6F9200001211430494200F028FD88
+:1003B00062B610BD00000000000000000000000058
+:1003C0000F4A70B594460322FF25C0260240D20092
+:1003D00095408901914083089B006344B6009C5975
+:1003E0001F22AC430C439C51012302409340C02286
+:1003F00040098000604452008350036070BDC046D5
+:1004000000E100E00000000000000000000000002B
+:100410000F2310B50340040003202040FF24074AA7
+:10042000C0009446844089018140083B9B089B00A2
+:100430006344DA69A2430A43DA6110BD00ED00E0CB
+:10044000064B074A02219A62002249429A62D96009
+:100450000131DA6019611A617047C046001002402C
+:10046000FFFF81FF0000000000000000000000000E
+:1004700001211D4A13680B431360013113681942AF
+:10048000FCD0032153688B435360174A093108009D
+:10049000536818401942FAD1FD2311680B401360CC
+:1004A000E4235B035060D06210635063D0625360FA
+:1004B000802311685B040B43802113600A4A89047E
+:1004C00013680B42FCD01221084B19605368103995
+:1004D0000B4353600C2204494B681340082BFBD19B
+:1004E000034B044A1A60704700100240002002408B
+:1004F0000008002000A24A040000000000000000E4
+:1005000010B5744672B600F05BFA62B6200000F0D7
+:1005100047FD10BD000000000000000000000000CA
+:10052000064B074A10B55A6000229A600732022132
+:100530000B201A60FFF76CFF10BDC04610E000E012
+:100540001F1C000000000000000000000000000070
+:100550007047C046000000000000000000000000DE
+:10056000F0B54E46032457464546DE46E0B5270023
+:10057000530917405607D508520525401C40130F54
+:10058000012285B0039391460023A4460297F60FFB
+:10059000009717E05C4607682740009C27430760E8
+:1005A000072B52D85446076A3A4022430262490850
+:1005B00044D0624692009446009A76009200AD00C4
+:1005C000009201334A460A42F1D007274C46039A6B
+:1005D0001F40BF00BA409C4092468288A2433243EB
+:1005E00092B28280846801945C00A0460324424653
+:1005F0009440E24393462200A046019C944322008B
+:1006000044462A438260C268A24364462243C260D1
+:100610000F22BA40029CD243022CBBD1072B1AD81E
+:10062000076A17405246174307625C46026849084A
+:100630002240009C224302600029BAD105B0F0BCE0
+:10064000BB46B246A946A046F0BD5446476A3A406A
+:1006500022434262ABE7476A17405246174347625C
+:10066000E3E7C046000000000000000000000000BA
+:100670000023436083685B0706D510228368134319
+:10068000836083681A42FCD102228368134383602B
+:10069000013A83681A42FCD1036803607047C04680
+:1006A00010B5074C2000FFF753FE064B0321A36251
+:1006B00000230C20E3622363FFF782FE10BDC046D7
+:1006C0000808002000240140000000000000000095
+:1006D000037810B5012B12D18022094B9200D96802
+:1006E000084C1143D960D9682140D960D96899690B
+:1006F0000A439A6180229B69836A12061A6110BDBF
+:1007000000100240FFFDFFFF00000000000000009D
+:10071000836A026919681960D168D9609169996220
+:10072000516959619168002915D0116919629421A4
+:1007300059600021C1625188C2684A4301210263A5
+:100740009A680A439A601A681142FCD004219A6898
+:100750000A439A6070470421EAE7C046000000009F
+:10076000F8B50400806A2569036802680260002DFC
+:100770000DD090221A420BD15B0708D5E36A226B99
+:10078000934222D30133016C9A4225D9E362F8BD2A
+:10079000DE06F60F76001B0601D504231E43FFF785
+:1007A00067FFAB68002B29D00522310020002270A2
+:1007B00098472378052B21D072B6012120004942A9
+:1007C000143000F02DFB62B6E1E7076CA1685E0013
+:1007D000013377529A42D9D800232E78E362002E53
+:1007E00011D06B68002BD2D00422200022709847D1
+:1007F0002378042BCBD1013B2370C8E7022323705D
+:1008000000232361D8E7FFF733FF02226B682270D1
+:10081000002B0CD0200098472378022B09D072B609
+:1008200020000021143000F0FBFA62B6AFE723612C
+:10083000F5E723699D42F2D12661F0E70000000050
+:1008400010B574460348FFF78BFF200000F0A8FBAB
+:1008500010BDC04608080020000000000000000095
+:100860008023034ADB0311680B4313607047C046C3
+:100870000827014000000000000000000000000008
+:10088000FC22244BD203996A10B511439962996AEC
+:100890002148802401409962996A59691F480A4396
+:1008A0005A6190225B6900231D49D20593809160B3
+:1008B0001C49D1602121494291828821490111625C
+:1008C0001949536211600122184952428B808A6093
+:1008D000C8608A820B624B620C601549154C8B8094
+:1008E0008A60CC608A820B624B620B6012498B80FB
+:1008F0008A60C8608A8211480B624B620B601049A3
+:100900008B808A60C8608A820B624B620B60FFF743
+:10091000AFFD10BD00100240FFFF81FF555555553A
+:100920005FF7FFFF05515565A0040028000400484B
+:100930000008004855555501000C004850555555C4
+:10094000001400480000000000000000000000004B
+:100950007047C046000000000000000000000000DA
+:10096000012310B5084C23700023636000F070FA77
+:1009700000F0AEFA00F0CCFA0449054800F068F93E
+:100980000223237062B610BDB0090020B811000820
+:10099000200900200000000000000000000000000E
+:1009A00072B68822024B9850024B853A1A70FEE7C5
+:1009B00020090020B0090020000000000000000015
+:1009C00010B500F03DF810BD000000000000000070
+:1009D00010B5036100230C4C43612369C2609A681F
+:1009E000914204D91B68891A9A688A42FAD35A68D4
+:1009F00081600360426010609A685860521A9A6081
+:100A000001235B42A36110BD20090020000000000B
+:100A1000036881689A6852189A60426813605A6045
+:100A2000002301220360024B52429A617047C04684
+:100A3000200900200000000000000000000000006D
+:100A400070B51B4DEB692C690133EB612B00103342
+:100A50009C4215D0A368013BA3600FE02268616847
+:100A60000A605160236062B620002169E3689847FC
+:100A700072B66369002B04D12C69A368002BEDD0FA
+:100A800070BD2A6991688B4204D912685B1A91681B
+:100A90009942FAD35168A360616022600C6091684A
+:100AA0005460CB1A936001235B42AB61E4E7C0461C
+:100AB00020090020000000000000000000000000ED
+:100AC00072B62423CB5C0C2B0BD8114A9B00D35855
+:100AD0009F468A6A9368013393604A680B68136083
+:100AE0005A60012300225B428B622533CA548B6912
+:100AF00088681B689A688242FBD25A680B604A6019
+:100B00001160596062B6704700228B6A1A60E8E78C
+:100B1000CC110008000000000000000000000000F0
+:100B200024230022C254836981681B689A688A4220
+:100B3000FBD25A6803604260106058607047C0463C
+:100B40002422074B10B5D96888541868046863607C
+:100B50001C6001248454D860FFF71EFB10BDC04602
+:100B6000200900200000000000000000000000003C
+:100B700070B5164C0500E66886B04B1C19D03300E2
+:100B80006846134AFFF724FF2423E1682068CD5408
+:100B90000268546022600122C254E060FFF7FCFA50
+:100BA000009B002B02D06846FFF732FFB06A06B008
+:100BB00070BD2423F0542068310002685460226024
+:100BC0000122C254E060FFF7E7FAEFE720090020B6
+:100BD000C10A000800000000000000000000000042
+:100BE000044BDA68196890688B68984280414042EB
+:100BF0007047C046200900200000000000000000EF
+:100C00000D4B10B51868D9680268012453601A604A
+:100C1000D8602422002384548B548B698C681B6811
+:100C20009A68A242FBD85A680B604A60116059600A
+:100C3000FFF7B2FA10BDC0462009002000000000F6
+:100C4000FEE7C046000000000000000000000000B9
+:100C5000F0B5002604001D4B87B0066381630D00CC
+:100C6000586000F08DF923002833A362E362183B3B
+:100C70002361636101235B42A361154BE661DB697C
+:100C80000027A660666321001E0088232662676233
+:100C9000246064603C31E75020000E4A083B00F0BD
+:100CA0001FF86B68E06024220362012383540A4A20
+:100CB00003930092EA68094B01922A6968460292FE
+:100CC0000493059700F064F807B0F0BDB009002068
+:100CD000200900200012000888110008410C0008BB
+:100CE00010B5040008008B60436402210023818456
+:100CF0000364C363013926338461C154C261220095
+:100D0000030028320261E26A103342611360E36239
+:100D10001C33C36204330363436310BD000000004F
+:100D20008368020018005168503830B5016219001C
+:100D300014697439C1600C61546902254C61114C0D
+:100D40000C621468D1681A002C3A816015804164E5
+:100D500000210D4A0164C163013D26318261455481
+:100D6000C46119001400403928340C60D46A4C6006
+:100D70002160D1621A00203B243A12601B605B6044
+:100D800030BDC046B9010008200900200000000065
+:100D900010B5FFF7C5FFFFF7C3FE10BD0000000050
+:100DA00070B572B6114BDC682500A062E06A2C3584
+:100DB000854207D12623E35C002B0ED00F20FFF7DE
+:100DC000BFFE70BD0368E362FFF7AAFEE06AA842B7
+:100DD000F8D12623E35C002BF0D12533E35C9B079D
+:100DE000ECD16269236913605A60E7E720090020AB
+:100DF00010B5010072B60820FFF7BAFE62B610BD4A
+:100E0000064B10B5DB68002905D00360986203200B
+:100E1000FFF7AEFE10BD01204042FBE72009002095
+:100E20000300006810B5002804D000221A60816217
+:100E3000FFF776FE10BDC046000000000000000075
+:100E400000230060406083607047C04600000000DF
+:100E5000024B034A1A60034A5A607047B8090020DF
+:100E6000280A0020002000200000000000000000F0
+:100E700070B5094C26686568AB1B984209D82B1AD7
+:100E800048421840821A964203D8954201D36260C4
+:100E900070BD0020FCE7C046B8090020000000003B
+:100EA00070B572B6094C26686568AB1B98420AD8C3
+:100EB0002B1A48421840821A964204D8954202D30F
+:100EC000626062B670BD0020FBE7C046B809002032
+:100ED0000548064B10B503600023436083600C3067
+:100EE000FFF7AEFF10BDC046C0090020A10E0008EC
+:100EF00010B50024046041608260C36010BDC0462C
+:100F000010B50022FFF7B4FF10BDC046000000007E
+:100F100070B5134C2000FFF793FF23002000114D04
+:100F20001033236104222B0014211430FFF7E0FF5B
+:100F3000230024336362230020002833A3620422A9
+:100F40002B001C212C30FFF7D3FF23003C33E3633D
+:100F500023004033236423004433636470BDC046E0
+:100F6000DC090020010F0008000000000000000064
+:100F7000EFF30983203383F3098862B67047C046D4
+:100F8000024A0349136A0B401362704700ED00E008
+:100F9000FFFF00FF00000000000000000000000054
+:100FA00010B50F3000D110BD72B6EFF30984203CAC
+:100FB00084F3098880235B04E361FFF711FE0028B6
+:100FC00002D0034BA361EEE7024BA361EBE7C046FF
+:100FD000C7010008CA01000800000000000000006E
+:100FE000F0B5DE46454657464E46E0B587B0FFF7BA
+:100FF0008FF9FFF7B5FC80213A4D01222800C90185
+:10100000FFF7AEFA8021012228000902FFF7A8FAB3
+:101010008023DB05AB618023334F1B06AB610021CE
+:101020003800FFF7A5F93800FFF71AFC6420FFF736
+:10103000DFFE2E4B98462E4B9B462E4B0193802372
+:10104000DB059A46802302AA1B021400994606AECD
+:10105000009202E00234B44211D001232200414642
+:101060003800FFF795F90028F4D05346AB614B46A2
+:101070005846AB610234FFF793FCB442EDD102ABAA
+:1010800019880E269C460800009A664453881C1C4A
+:101090008B4200D90C1CA1B21C1C834200D2041C40
+:1010A0000232A0B2B242F1D1134B994205D9134B8F
+:1010B000984202D8401A642808D98023DB05AB6126
+:1010C00080231B020198AB61FFF76AFC802380221A
+:1010D000DB01AB61AB8A120253400948AB82FFF7D8
+:1010E00087FEACE700040048080800202C12000826
+:1010F00090110008A41100084B040000540B0000DC
+:041100008813000050
+:101104004C120008240A0020240A0020240A00208B
+:101114004C12000800000000000000000000000065
+:101124004C12000800000000000000000000000055
+:101134004C12000800000000000000000000000045
+:101144004C12000800000000000000000000000035
+:101154004C12000800000000000000000000000025
+:101164004C12000800000000000000000000000015
+:101174004C12000800000000000000000000000005
+:101184006330000069646C65000000004144432042
+:10119400636F6E76657273696F6E204641494C00C9
+:1011A4004144432056524546494E54204641494CF9
+:1011B400000000008411000800000000000000008E
+:1011C4004008002020090020040B0008E20A00085F
+:1011D400E20A0008080B0008DA0A0008D20A00082C
+:1011E400E20A0008DA0A0008E20A0008E20A000833
+:1011F400E20A0008E20A0008DA0A00086D61696E72
+:10120400002C063804044C080C10141C2024252639
+:10121400000000000000240401000400000000009D
+:10122400000C001028303400000001000000000011
+:1012340000000000000000000000000007000000A3
+:0412440000000200A4
+:0412480000A24A04B2
+:04000005080001915D
+:00000001FF
diff --git a/testhal/SK32/ADC/build.log b/testhal/SK32/ADC/build.log
new file mode 100644
index 0000000000..282d1b5fb9
--- /dev/null
+++ b/testhal/SK32/ADC/build.log
@@ -0,0 +1 @@
+make: Nothing to be done for 'all'.
diff --git a/testhal/SK32/ADC/build_adc.log b/testhal/SK32/ADC/build_adc.log
new file mode 100644
index 0000000000..4aa567f9ce
--- /dev/null
+++ b/testhal/SK32/ADC/build_adc.log
@@ -0,0 +1,73 @@
+Compiler Options
+arm-none-eabi-gcc -c -mcpu=cortex-m0 -mthumb -O2 -ggdb -fomit-frame-pointer -falign-functions=16 -ffunction-sections -fdata-sections -fno-common -Wall -Wextra -Wundef -Wstrict-prototypes -Wa,-alms=./build/lst/ -DCORTEX_USE_FPU=FALSE -DSK32F077xB -MD -MP -MF ./.dep/build.d -I. -I../../../../chibios-contrib/os/common/ext/CMSIS/SK32/SK32F0xx -I../../../../chibios-contrib/os/common/startup/ARMCMx/devices/SK32F0xx -I../../../../chibios-contrib/os/hal/boards/GENERIC_SK32_F077 -I../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx -I../../../../chibios/os/common/ext/ARM/CMSIS/Core/Include -I../../../../chibios/os/common/oop/include -I../../../../chibios/os/common/portability/GCC -I../../../../chibios/os/common/ports/ARM-common -I../../../../chibios/os/common/ports/ARMv6-M -I../../../../chibios/os/common/startup/ARMCMx/compilers/GCC -I../../../../chibios/os/common/startup/ARMCMx/compilers/GCC/ld -I../../../../chibios/os/hal/include -I../../../../chibios/os/hal/osal/rt-nil -I../../../../chibios/os/hal/ports/STM32/LLD/CANv1 -I../../../../chibios/os/hal/ports/STM32/LLD/DACv1 -I../../../../chibios/os/hal/ports/STM32/LLD/DMAv1 -I../../../../chibios/os/hal/ports/STM32/LLD/RTCv2 -I../../../../chibios/os/hal/ports/STM32/LLD/xWDGv1 -I../../../../chibios/os/hal/ports/common/ARMCMx -I../../../../chibios/os/license -I../../../../chibios/os/oslib/include -I../../../../chibios/os/rt/include -I./cfg main.c -o main.o
+
+Compiling crt0_v6m.S
+Compiling vectors.S
+Compiling chcoreasm.S
+Compiling crt1.c
+Compiling oop_base_object.c
+Compiling hal.c
+Compiling hal_safety.c
+Compiling hal_st.c
+Compiling hal_buffered_serial.c
+Compiling hal_buffers.c
+Compiling hal_queues.c
+Compiling hal_mmcsd.c
+Compiling hal_adc.c
+Compiling hal_pal.c
+Compiling hal_flash.c
+Compiling nvic.c
+Compiling sk32_isr.c
+Compiling hal_lld.c
+Compiling hal_efl_lld.c
+Compiling hal_st_lld.c
+Compiling hal_pal_lld.c
+Compiling sk32_dma.c
+Compiling hal_sled_lld.c
+In file included from ../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:49:
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.h:65:6: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 65 | #if (HAL_USE_SLED == TRUE) || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:51:5: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 51 | #if HAL_USE_SLED || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+Compiling hal_adc_lld.c
+Compiling stm32_dma.c
+Compiling hal_usb_lld.c
+Compiling board.c
+Compiling osal.c
+Compiling chsys.c
+Compiling chrfcu.c
+Compiling chdebug.c
+Compiling chtrace.c
+Compiling chvt.c
+Compiling chschd.c
+Compiling chinstances.c
+Compiling chthreads.c
+Compiling chregistry.c
+Compiling chsem.c
+Compiling chmtx.c
+Compiling chcond.c
+Compiling chevents.c
+Compiling chmsg.c
+Compiling chdynamic.c
+Compiling chmboxes.c
+Compiling chmemcore.c
+Compiling chmemheaps.c
+Compiling chmempools.c
+Compiling chpipes.c
+Compiling chobjcaches.c
+Compiling chdelegates.c
+Compiling chfactory.c
+Compiling chcore.c
+Compiling main.c
+Linking build/adc.elf
+Creating build/adc.hex
+Creating build/adc.bin
+Creating build/adc.dmp
+
+ text data bss dec hex filename
+ 4680 4 8184 12868 3244 build/adc.elf
+Creating build/adc.list
+
+Done
diff --git a/testhal/SK32/ADC/cfg/chconf.h b/testhal/SK32/ADC/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/testhal/SK32/ADC/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/ADC/cfg/halconf.h b/testhal/SK32/ADC/cfg/halconf.h
new file mode 100644
index 0000000000..0bd3042140
--- /dev/null
+++ b/testhal/SK32/ADC/cfg/halconf.h
@@ -0,0 +1,568 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC TRUE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB FALSE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/ADC/cfg/mcuconf.h b/testhal/SK32/ADC/cfg/mcuconf.h
new file mode 100644
index 0000000000..c51a5cc8a0
--- /dev/null
+++ b/testhal/SK32/ADC/cfg/mcuconf.h
@@ -0,0 +1,115 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for the
+ * USB IP, but the SK32 port provides native drivers for the entire core
+ * platform and for every peripheral, so no shared STM32 low level driver
+ * (TIMv1, USARTv2, ADCv1, SPIv2, I2Cv2, ..., nor the CANv1/DACv1/DMAv1/
+ * RTCv2/xWDGv1 stubs) is linked in. This mcuconf.h only needs to satisfy
+ * the single SK32 naming scheme:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST/peripheral settings consumed by
+ * the native SK32 HAL low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * ADC driver system settings.
+ * The SK32 native interrupt-driven ADC1 driver (every sample is read from
+ * the DR register in the ADC ISR, no DMA stream is involved) is selected
+ * through the SK32_ADC_USE_ADC1 switch. This testhal samples the VREFINT
+ * internal reference voltage (channel 17, enabled through the CCR VREFEN bit).
+ * The ADC kernel clock comes from the 72 MHz PCLK through the CFGR2 CKMODE
+ * bits, PCLK/4 (18 MHz) is used here.
+ */
+#define SK32_ADC_USE_ADC1 TRUE
+#define SK32_ADC_ADC1_IRQ_PRIORITY 3
+#define SK32_ADC_ADC1_CFGR2 ADC_CFGR2_CKMODE_PCLK_DIV4
+
+/*
+ * GPT driver system settings.
+ * The SK32 native GPT driver (TIM3 on the APB1 bus, TIM16/TIM17 on the APB2
+ * bus) is selected through the SK32_GPT_USE_TIMx switches. All the timers are
+ * left disabled in this ADC testhal.
+ */
+#define SK32_GPT_USE_TIM3 FALSE
+#define SK32_GPT_USE_TIM16 FALSE
+#define SK32_GPT_USE_TIM17 FALSE
+#define SK32_GPT_TIM3_IRQ_PRIORITY 3
+#define SK32_GPT_TIM16_IRQ_PRIORITY 3
+#define SK32_GPT_TIM17_IRQ_PRIORITY 3
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches, USART2 is mapped on PA2/PA3 (alternate 1)
+ * as documented in board.h.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 TRUE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ * The SK32 native interrupt-driven SPI driver is selected through the
+ * SK32_SPI_USE_SPIx switches.
+ *
+ * SPI1 self-loopback test:
+ * - SPI1 master on PA7 (SPI1_MOSI, alternate 12) and PA6 (SPI1_MISO,
+ * alternate 12), the two pads are shorted on the board.
+ */
+#define SK32_SPI_USE_SPI1 TRUE
+#define SK32_SPI_USE_SPI2 FALSE
+#define SK32_SPI_SPI1_PRIORITY 3
+#define SK32_SPI_SPI2_PRIORITY 3
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+#endif /* MCUCONF_H */
\ No newline at end of file
diff --git a/testhal/SK32/ADC/main.c b/testhal/SK32/ADC/main.c
new file mode 100644
index 0000000000..99a6a51d11
--- /dev/null
+++ b/testhal/SK32/ADC/main.c
@@ -0,0 +1,148 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+#include "ch.h"
+#include "hal.h"
+
+/*===========================================================================*/
+/* SK32F0xx ADC1 VREFINT sampling test. */
+/*===========================================================================*/
+
+/*
+ * Test resources used by this test:
+ *
+ * - ADC1 with the internal reference voltage VREFINT (channel 17) as the
+ * only sampled source. The VREFEN bit of the shared ADC CCR register is
+ * set right after adcStart() and left enabled for the whole test.
+ * - The native SK32F0xx ADC LLD samples the DR register inside the ADC
+ * interrupt handler (EOC events), no DMA stream is used.
+ * - PB14 / PB15: two board LEDs wired to push-pull outputs, the PAL
+ * abstraction drives them high (PAL_HIGH) to switch the LEDs on.
+ *
+ * Test procedure:
+ * - The main thread starts ADCD1 (the ADC kernel clock is PCLK/4, i.e.
+ * 18 MHz with the 72 MHz PCLK of this platform), enables VREFINT and
+ * performs a sequence of blocking single conversions (12 bit, right
+ * aligned, 239.5 cycle sampling time).
+ * - Each conversion returns a raw sample of VREFINT / VDDA * 4096: with
+ * the ~1.2 V reference and a supply in the 1.8 .. 3.6 V range the value
+ * must fall in the 1100 .. 2900 window. Eight consecutive samples must
+ * additionally be stable (max - min <= 100) because an internal
+ * reference does not move.
+ * - On success PB14 is switched on solid and PB15 blinks at 1 Hz as a
+ * heartbeat. On the first out-of-range or unstable sample PB14 is
+ * switched off, PB15 is switched on solid and the system halts.
+ */
+
+#define ADC_NUM_SAMPLES 8U
+
+/* Acceptable raw window for the 12 bit VREFINT conversion (see above). */
+#define ADC_VREFINT_RAW_MIN 1100U
+#define ADC_VREFINT_RAW_MAX 2900U
+/* Maximum allowed spread across the samples of one measurement. */
+#define ADC_VREFINT_RAW_SPREAD 100U
+
+/*
+ * ADC conversion group: a single conversion of channel 17 (VREFINT), 12 bit
+ * resolution (RES bits = 0), right aligned (ALIGN = 0), software triggered
+ * (EXTEN = 0), one shot. The 239.5 cycle sampling time is the longest one
+ * available and is the recommended setting for the internal references.
+ */
+static const ADCConversionGroup vrefgrp = {
+ .circular = FALSE,
+ .num_channels = 1U,
+ .end_cb = NULL,
+ .error_cb = NULL,
+ .cfgr1 = 0U,
+ .tr = 0U,
+ .smpr = ADC_SMPR_SMP_239P5,
+ .chselr = ADC_CHSELR_CHSEL17
+};
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+ adcsample_t sample[ADC_NUM_SAMPLES];
+ adcsample_t mn;
+ adcsample_t mx;
+ unsigned i;
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ /* Configuring the PB14/PB15 LED lines as push-pull outputs, initially off. */
+ palSetPadMode(GPIOB, GPIOB_PIN14, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, GPIOB_PIN15, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palClearPad(GPIOB, GPIOB_PIN15);
+
+ /*
+ * Starting the ADC and enabling the VREFINT analog source. A short delay
+ * lets the internal reference settle before the first conversion.
+ */
+ adcStart(&ADCD1, NULL);
+ adcSK32EnableVREF(&ADCD1);
+ chThdSleepMilliseconds(10);
+
+ while (true) {
+ /* Sampling one measurement: ADC_NUM_SAMPLES blocking conversions.*/
+ for (i = 0U; i < ADC_NUM_SAMPLES; i++) {
+ msg_t res;
+
+ res = adcConvert(&ADCD1, &vrefgrp, &sample[i], 1U);
+ if (res != MSG_OK) {
+ /* Conversion aborted by an ADC error event.*/
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palSetPad(GPIOB, GPIOB_PIN15);
+ chSysHalt("ADC conversion FAIL");
+ }
+ }
+
+ /* Range check.*/
+ mn = sample[0];
+ mx = sample[0];
+ for (i = 1U; i < ADC_NUM_SAMPLES; i++) {
+ if (sample[i] < mn) {
+ mn = sample[i];
+ }
+ if (sample[i] > mx) {
+ mx = sample[i];
+ }
+ }
+
+ if ((mn < ADC_VREFINT_RAW_MIN) || (mx > ADC_VREFINT_RAW_MAX) ||
+ ((uint32_t)(mx - mn) > (uint32_t)ADC_VREFINT_RAW_SPREAD)) {
+ /* Failure: PB14 off, PB15 on solid, halt.*/
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palSetPad(GPIOB, GPIOB_PIN15);
+ chSysHalt("ADC VREFINT FAIL");
+ }
+
+ /* Success: PB14 on solid, PB15 blinks at 1 Hz as a heartbeat.*/
+ palSetPad(GPIOB, GPIOB_PIN14);
+ palTogglePad(GPIOB, GPIOB_PIN15);
+ chThdSleepMilliseconds(500);
+ }
+}
diff --git a/testhal/SK32/EEPROM/Makefile b/testhal/SK32/EEPROM/Makefile
new file mode 100644
index 0000000000..2627b30156
--- /dev/null
+++ b/testhal/SK32/EEPROM/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = eeprom
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
\ No newline at end of file
diff --git a/testhal/SK32/EEPROM/cfg/chconf.h b/testhal/SK32/EEPROM/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/testhal/SK32/EEPROM/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/EEPROM/cfg/halconf.h b/testhal/SK32/EEPROM/cfg/halconf.h
new file mode 100644
index 0000000000..33e2892463
--- /dev/null
+++ b/testhal/SK32/EEPROM/cfg/halconf.h
@@ -0,0 +1,537 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ * @note ENABLED for this test: it is the backing store used by the QMK
+ * wear-leveling EEPROM on the SK32F077. The test proves that a
+ * value written at the tail of the internal flash is still readable
+ * after a reset (cross-reset persistence).
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL TRUE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB FALSE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
\ No newline at end of file
diff --git a/testhal/SK32/EEPROM/cfg/mcuconf.h b/testhal/SK32/EEPROM/cfg/mcuconf.h
new file mode 100644
index 0000000000..8501ea2ce3
--- /dev/null
+++ b/testhal/SK32/EEPROM/cfg/mcuconf.h
@@ -0,0 +1,86 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for the
+ * USB IP, but the SK32 port provides native drivers for the entire core
+ * platform and for every peripheral, so no shared STM32 low level driver
+ * (TIMv1, USARTv2, ADCv1, SPIv2, I2Cv2, ..., nor the CANv1/DACv1/DMAv1/
+ * RTCv2/xWDGv1 stubs) is linked in. This mcuconf.h only needs to satisfy
+ * the single SK32 naming scheme:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST/peripheral settings consumed by
+ * the native SK32 HAL low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches. Not used by this test: HAL_USE_SERIAL
+ * is disabled in halconf.h, the switches are kept at FALSE.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 FALSE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ * The SK32 native interrupt-driven SPI driver is selected through the
+ * SK32_SPI_USE_SPIx switches. Not used by this test: HAL_USE_SPI is
+ * disabled in halconf.h, the switches are kept at FALSE.
+ */
+#define SK32_SPI_USE_SPI1 FALSE
+#define SK32_SPI_USE_SPI2 FALSE
+#define SK32_SPI_SPI1_PRIORITY 3
+#define SK32_SPI_SPI2_PRIORITY 3
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+#endif /* MCUCONF_H */
\ No newline at end of file
diff --git a/testhal/SK32/EEPROM/main.c b/testhal/SK32/EEPROM/main.c
new file mode 100644
index 0000000000..3c18286394
--- /dev/null
+++ b/testhal/SK32/EEPROM/main.c
@@ -0,0 +1,128 @@
+/*
+ * EEPROM / flash persistence verification for the SK32F077 testhal.
+ *
+ * Copyright (C) 2026 QMK - Apache License 2.0.
+ *
+ * This test proves **cross-reset (power-cycle) persistence** of the internal
+ * flash, which is the backing store used by the QMK wear-leveling EEPROM.
+ *
+ * On every start it:
+ * 1. reads a 16-bit counter from the LAST 2 KB flash sector,
+ * 2. increments it,
+ * 3. erases that sector and programs the new value back,
+ * 4. reads it back and checks it,
+ * 5. blinks PB14 the read-back value mod 40 times (easy to eyeball), and
+ * drives PB15 high only when the whole round (erase+program+readback)
+ * succeeded.
+ *
+ * Because the value is committed to flash (not RAM), issuing a chip reset
+ * (the ST-Link "virtual unplug" reset — no USB disconnect needed) must NOT
+ * clear it: each reset makes the count tick up. That is exactly the property
+ * the wear-leveling EEPROM relies on.
+ *
+ * PB14 / PB15 are the same "user" LEDs used by the other SK32 testhals.
+ */
+
+#include "ch.h"
+#include "hal.h"
+#include "hal_efl.h"
+
+/*===========================================================================*/
+/* Application entry point. */
+/*===========================================================================*/
+int main(void) {
+
+ /* The last flash sector is dedicated to the counter. It sits at the very
+ end of the 128 KB flash (0x0801F800 on the SK32F077xB), far from the
+ bootloader (0x08004000 limit) and far from the application code. */
+ enum {
+ BLINK_SPAN = 40U, /* blink count is shown modulo this */
+ BLINK_ON_MS = 150U, /* LED on time per blink */
+ BLINK_OFF_MS = 150U, /* LED off time per blink */
+ IDLE_MS = 60U /* pause before showing the next counter */
+ };
+
+ BaseFlash *fbp;
+ const flash_descriptor_t *desc;
+ flash_sector_t sector;
+ flash_offset_t offset;
+ volatile uint16_t *slot;
+ uint16_t current, next;
+ flash_error_t err;
+ unsigned int blink, i;
+
+ /*
+ * System initializations.
+ */
+ halInit();
+ chSysInit();
+
+ /* PB14/PB15 as push-pull outputs, off by default. */
+ palSetPadMode(GPIOB, 14U, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, 15U, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, 14U);
+ palClearPad(GPIOB, 15U);
+
+ /* Start the embedded-flash driver. No vendor configuration structure is
+ used by the SK32 EFL LLD, so NULL is passed. */
+ eflStart(&EFLD1, NULL);
+ fbp = getBaseFlash(&EFLD1);
+ desc = flashGetDescriptor(fbp);
+
+ /* Pick the last sector and compute its offset (from flash base 0) and its
+ absolute memory address. */
+ sector = (flash_sector_t)(desc->sectors_count - 1U);
+ offset = (flash_offset_t)sector * (flash_offset_t)desc->sectors_size;
+ slot = (volatile uint16_t *)(void *)(desc->address + (unsigned int)offset);
+
+ current = *slot;
+ if (current == 0xFFFFU) {
+ /* Fresh erased sector: first run. */
+ current = 0U;
+ }
+ next = (uint16_t)(current + 1U);
+
+ /* Erase the counter sector. */
+ err = flashStartEraseSector(fbp, sector);
+ if (err == FLASH_NO_ERROR) {
+ err = flashWaitErase(fbp);
+ }
+
+ /* Program the new value (2 bytes = one flash line on the SK32F077). */
+ if (err == FLASH_NO_ERROR) {
+ err = flashProgram(fbp, offset, (uint32_t)sizeof(next),
+ (const uint8_t *)&next);
+ }
+
+ /* Commit: verify the slot really reads back what we programmed. */
+ if ((err == FLASH_NO_ERROR) && (*slot == next)) {
+ /* Persistence round-trip OK: light PB15 solid. */
+ palSetPad(GPIOB, 15U);
+ }
+ else {
+ /* Erase/program/readback error: PB15 stays off; PB14 does one slow
+ blink below as the failure indicator. */
+ next = 0xFFFFU;
+ }
+
+ /* Show the programmed counter value on PB14 (blink count % BLINK_SPAN). */
+ blink = (unsigned int)(next % BLINK_SPAN);
+ if (next == 0xFFFFU) {
+ blink = 1U; /* a single slow blink signals the error path */
+ }
+
+ for (i = 0U; i < blink; i++) {
+ palSetPad(GPIOB, 14U);
+ chThdSleepMilliseconds(BLINK_ON_MS);
+ palClearPad(GPIOB, 14U);
+ chThdSleepMilliseconds(BLINK_OFF_MS);
+ }
+
+ /* Idle; PB14 stays off, PB15 indicates last round success. */
+ chThdSleepMilliseconds(IDLE_MS);
+
+ while (true) {
+ palTogglePad(GPIOB, 14U);
+ chThdSleepMilliseconds(500U);
+ }
+}
\ No newline at end of file
diff --git a/testhal/SK32/GPIO/Makefile b/testhal/SK32/GPIO/Makefile
new file mode 100644
index 0000000000..76b996d47f
--- /dev/null
+++ b/testhal/SK32/GPIO/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = gpio
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/testhal/SK32/GPIO/build.log b/testhal/SK32/GPIO/build.log
new file mode 100644
index 0000000000..21582cdd77
--- /dev/null
+++ b/testhal/SK32/GPIO/build.log
@@ -0,0 +1,71 @@
+Compiling crt0_v6m.S
+Compiling vectors.S
+Compiling chcoreasm.S
+Compiling crt1.c
+Compiling oop_base_object.c
+Compiling hal.c
+Compiling hal_safety.c
+Compiling hal_st.c
+Compiling hal_buffered_serial.c
+Compiling hal_buffers.c
+Compiling hal_queues.c
+Compiling hal_mmcsd.c
+Compiling hal_pal.c
+Compiling hal_serial.c
+Compiling hal_usb.c
+Compiling hal_flash.c
+Compiling nvic.c
+Compiling sk32_isr.c
+Compiling hal_lld.c
+Compiling hal_efl_lld.c
+Compiling hal_st_lld.c
+Compiling hal_pal_lld.c
+Compiling hal_serial_lld.c
+Compiling sk32_dma.c
+Compiling hal_sled_lld.c
+In file included from ../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:49:
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.h:65:6: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 65 | #if (HAL_USE_SLED == TRUE) || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:51:5: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 51 | #if HAL_USE_SLED || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+Compiling stm32_dma.c
+Compiling hal_usb_lld.c
+Compiling board.c
+Compiling osal.c
+Compiling chsys.c
+Compiling chrfcu.c
+Compiling chdebug.c
+Compiling chtrace.c
+Compiling chvt.c
+Compiling chschd.c
+Compiling chinstances.c
+Compiling chthreads.c
+Compiling chregistry.c
+Compiling chsem.c
+Compiling chmtx.c
+Compiling chcond.c
+Compiling chevents.c
+Compiling chmsg.c
+Compiling chdynamic.c
+Compiling chmboxes.c
+Compiling chmemcore.c
+Compiling chmemheaps.c
+Compiling chmempools.c
+Compiling chpipes.c
+Compiling chobjcaches.c
+Compiling chdelegates.c
+Compiling chfactory.c
+Compiling chcore.c
+Compiling main.c
+Linking build/gpio.elf
+Creating build/gpio.hex
+Creating build/gpio.bin
+Creating build/gpio.dmp
+
+ text data bss dec hex filename
+ 9752 4 8184 17940 4614 build/gpio.elf
+Creating build/gpio.list
+
+Done
diff --git a/testhal/SK32/GPIO/cfg/chconf.h b/testhal/SK32/GPIO/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/testhal/SK32/GPIO/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/GPIO/cfg/halconf.h b/testhal/SK32/GPIO/cfg/halconf.h
new file mode 100644
index 0000000000..cb428219fc
--- /dev/null
+++ b/testhal/SK32/GPIO/cfg/halconf.h
@@ -0,0 +1,568 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL TRUE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB TRUE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/GPIO/cfg/mcuconf.h b/testhal/SK32/GPIO/cfg/mcuconf.h
new file mode 100644
index 0000000000..4840860fda
--- /dev/null
+++ b/testhal/SK32/GPIO/cfg/mcuconf.h
@@ -0,0 +1,75 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for the
+ * USB IP, but the SK32 port provides native drivers for the entire core
+ * platform and for every peripheral, so no shared STM32 low level driver
+ * (TIMv1, USARTv2, ADCv1, SPIv2, I2Cv2, ..., nor the CANv1/DACv1/DMAv1/
+ * RTCv2/xWDGv1 stubs) is linked in. This mcuconf.h only needs to satisfy
+ * the single SK32 naming scheme:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST/peripheral settings consumed by
+ * the native SK32 HAL low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches, USART2 is mapped on PA2/PA3 (alternate 1)
+ * as documented in board.h.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 TRUE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+#endif /* MCUCONF_H */
\ No newline at end of file
diff --git a/testhal/SK32/GPIO/gpio.hex b/testhal/SK32/GPIO/gpio.hex
new file mode 100644
index 0000000000..59dee9f411
--- /dev/null
+++ b/testhal/SK32/GPIO/gpio.hex
@@ -0,0 +1,614 @@
+:020000040800F2
+:100000000004002091010008E1220008930100088B
+:100010009301000893010008930100089301000870
+:100020009301000893010008930100089301000860
+:10003000930100089301000893010008610F000874
+:100040009301000893010008930100089301000840
+:100050009301000893010008930100089301000830
+:100060009301000893010008930100089301000820
+:100070009301000893010008930100089301000810
+:100080009301000893010008930100089301000800
+:1000900093010008930100089301000893010008F0
+:1000A00093010008930100089301000893010008E0
+:1000B000211200089301000893010008A114000810
+:1000C00072B6244880F30888234880F30988022008
+:1000D00080F31488BFF36F8F00F06AF901F0E0FC41
+:1000E0001E481F491B4A914202DA08600431FAE7B0
+:1000F0001C49194A914202DA08600431FAE71A49A8
+:100100001A4A1B4B9A4204DA086810600431043220
+:10011000F8E700201749184A914202DA08600431D2
+:10012000FAE700F05DF900F04BF9144C144DAC42C5
+:1001300003DA216888470434F9E702F031F9114CF9
+:10014000114DAC4203DA216888470434F9E70F49BE
+:1001500008470000000400200008002055555555B0
+:1001600000000020000400201826000800080020DD
+:100170000408002008080020700C0020C0000008BF
+:10018000C0000008C0000008C0000008D10300083B
+:1001900096E700F000F8FEE7F0B544464D465646B7
+:1001A0005F46F0B46B46CB60C3689D46F0BCA0468A
+:1001B000A946B246BB46F0BD62B6281CA047002047
+:1001C00001F056FFFEE701F04BFE024A024B1360BE
+:1001D000FEE7C04604ED00E000000080002934D0B6
+:1001E0000123002210B488422CD301242407A14209
+:1001F00004D2814202D209011B01F8E7E400A142C6
+:1002000004D2814202D249005B00F8E7884201D360
+:10021000401A1A434C08A04202D3001B5C08224338
+:100220008C08A04202D3001B9C082243CC08A042A9
+:1002300002D3001BDC082243002803D01B0901D095
+:100240000909E3E7100010BC704701B5002000F079
+:100250000BF802BD0029F8D003B5FFF7C1FF0EBCB3
+:100260004243891A1847C0467047C046F0B505009A
+:10027000830749D0541E002A45D003000326CAB282
+:1002800002E00135013C3ED301332A703342F8D1FC
+:10029000032C30D9FF220A401502AD182A04AD18EC
+:1002A0000F2C34D92700103F3F093E01B4461E00F1
+:1002B0001A0010366644156055609560D56010329E
+:1002C0009642F8D101373F01DB190F270C22274056
+:1002D00022403C00002A0ED03E1FB608B400A446BF
+:1002E0001A001C1D644420C2A242FCD10324013622
+:1002F000B6009B193C40002C05D0C9B21C191970DE
+:1003000001339C42FBD1F0BD03001400C0E727007D
+:10031000E2E7C046F0B505000F2A33D903240300F5
+:1003200026000B431E401C4237D11500103D2D09FD
+:1003300003000C00AC462F013F1865685D60A5689E
+:100340009D60E568DD60256810341D601D00103378
+:10035000BD42F2D165460F230C2401352D01491908
+:100360001340451914421AD08C59AC5104369C1BC9
+:10037000032CF9D8043B9B0801339B00ED18C918E6
+:1003800003231A40561E002A06D00023CC5C1A0014
+:10039000EC5401339642F9D1F0BD561EF5E71A0030
+:1003A000F0E7C04600000000000000000000000070
+:1003B0007047C04600000000000000000000000080
+:1003C0007047C04600000000000000000000000070
+:1003D000FEE7C04600000000000000000000000032
+:1003E00070B5114C250080356068A2682168904284
+:1003F0000AD2030040C940C39342FBD3013A131A07
+:100400009B0801339B00C018E368834207D9013B76
+:100410001A1A9208013200219200FFF727FF1034C8
+:10042000AC42E1D170BDC046FC23000800000000D2
+:1004300010B500F035FD00F0BBFD00F0D9F900F07B
+:1004400057FA01F095FB00F003F810BD0000000022
+:1004500010B500F095FD10BD000000000000000088
+:10046000F0B5C646836804000F00150000B593423E
+:1004700000D28568A1692669761AB5421BD30CD8CB
+:100480002A003800FFF746FFE268A36828005B1BDC
+:10049000A261A36080BCB846F0BD32003800FFF70F
+:1004A00039FFAB1B98461A00E168B819FFF732FF15
+:1004B000E2684244E9E72A003800FFF72BFFA2690F
+:1004C0005219E2E7000000000000000000000000F8
+:1004D000F0B5C646836804000F00150000B59342CE
+:1004E00000D2856860692669361AB5421BD30CD8DC
+:1004F0002A003900FFF70EFFE268A36828005B1BA3
+:100500006261A36080BCB846F0BD32003900FFF7DD
+:1005100001FFAB1B98461A00E068B919FFF7FAFE15
+:10052000E2684244E9E72A003900FFF7F3FE626916
+:100530005219E2E700000000000000000000000087
+:1005400010B50024C361029BC160816141618918BB
+:100550000060406084600161036210BD0000000023
+:100560004369826910B5934211D08268013282607A
+:100570005A1C42611970426903699A4204D20021EF
+:1005800001F0BEFD002010BDC3684361F7E782683B
+:10059000002AEAD001204042F5E7C04600000000F2
+:1005A00070B505000C0072B605E02100280001F0CE
+:1005B0008FFD002816DBAB68002BF6D0AB68AA696C
+:1005C000013BAB60531CAB6114782A69934201D3A1
+:1005D000EB68AB61EB69002B01D02800984762B64D
+:1005E000200070BD62B6FCE70000000000000000C3
+:1005F000F0B5D6464F4646469A46C369C0B59146C1
+:1006000007000E00984672B6151E11D02A00310060
+:100610003800FFF725FF041E11D04346002B01D000
+:100620003800C04762B62D1B361972B6002DEDD1C9
+:10063000484662B6E0BCBA46B146A846F0BD51464F
+:10064000380001F045FD0028E0D04B46581BF0E78C
+:10065000C361009BC1608161416189180060406095
+:10066000826001610362704700000000000000002A
+:1006700070B504000E00150072B605E029002000D8
+:1006800001F026FD002817DBA368002BF6D0A36835
+:10069000013BA36063695A1C62611E706269236931
+:1006A0009A4201D3E3686361E369002B01D0200023
+:1006B000984762B6002070BD62B6FCE700000000FB
+:1006C0008369426910B59A4210D082680132826013
+:1006D0005A1C82611C7803699A4204D2002101F0FD
+:1006E0000FFD200010BDC3688361F7E78268002A10
+:1006F000EBD001204042F5E70000000000000000C0
+:10070000F0B5D6464F4646469A46C369C0B59146AF
+:1007100007000E00984672B6151E11D02A0031004F
+:100720003800FFF7D5FE041E11D04346002B01D040
+:100730003800C04762B62D1B361972B6002DEDD1B8
+:10074000484662B6E0BCBA46B146A846F0BD51463E
+:10075000380001F0BDFC0028E0D04B46581BF0E704
+:1007600010B50C30FFF744FF10BDC046000000007C
+:10077000012310B50C305B42FFF73AFF10BDC046B5
+:1007800010B53030FFF7BCFF10BDC04600000000C0
+:10079000012310B530305B42FFF7B2FF10BDC046F9
+:1007A00010B50C30FFF7FCFE10BDC0460000000085
+:1007B000012110B50C304942FFF7F2FE10BDC046D2
+:1007C00010B53030FFF754FF10BDC04600000000E8
+:1007D000012210B530305242FFF74AFF10BDC0462B
+:1007E00001394B425941142049420840143870479E
+:1007F00010B500F075FC10BD000000000000000006
+:1008000030B5150002000B000C4983B002C2040091
+:10081000426001000122543102720C3000940F3208
+:10082000FFF78EFE210020002B00102264313030B3
+:100830000094FFF70DFF03B030BDC0467C240008D4
+:10084000436970B504000D00002B07D0200029007B
+:100850000C30FFF785FE00280BDB70BD042104304F
+:1008600001F066FC200029000C30FFF779FE00281B
+:10087000F3DA8021201D090101F05AFCEDE7C046A2
+:1008800010B50400002101F0C3F80021200001F0A0
+:100890009FF863681B68002B02D006212000984750
+:1008A000A368044A1340A36000230622A3677033A1
+:1008B000E25410BDFEFFFEFF00000000000000003B
+:1008C000822310B50400C25C0833C25400F038FF24
+:1008D00063681B68002B02D0012120009847032386
+:1008E000237010BD000000000000000000000000A8
+:1008F00010B500F0CDFE10BD0000000000000000AB
+:1009000070B50123002504004560037040220021DA
+:100910003030FFF7ABFCA56070BDC04600000000A2
+:10092000022370B5002503708633C55004002422CD
+:100930000021856785600C30FFF798FC702320004C
+:10094000E55400F0B5FE63681B68002B02D000215F
+:100950002000984770BDC046000000000000000065
+:10096000037810B50400052B0BD08C22835405238B
+:10097000037043681B68002B01D0042198470023B3
+:10098000A36010BD00000000000000000000000097
+:10099000037810B5052B00D010BD8733C35C0370FE
+:1009A00043681B68002BF7D005219847F4E7C04641
+:1009B000702370B5C25C0400002A06D08268C14969
+:1009C0000A40826000228267C25422000021200077
+:1009D000803200F035FF63689B68002B2DD020002B
+:1009E0009847002829D0A16F8623E25C2300863334
+:1009F0005B781B0213438B4200D3A1E08022A367E4
+:100A0000A25C52B2002A4FDB002B00D18AE0702397
+:100A10001522E25472B601206389A26F03436381F9
+:100A2000E368616F9B6920001A60002299605A6038
+:100A3000002100F025FF62B635E060228025160017
+:100A4000605D064002422BD181237F22E35C02409D
+:100A50001B0213439C4A934200D1F6E03CD880220B
+:100A60005200934200D1A0E000D96DE0012B00D1EB
+:100A7000E9E0022B00D1A4E022008832002B0FD144
+:100A80000223A367002302216267E367ACE7902299
+:100A90001201934200D1A5E08C4A934200D1D2E0EA
+:100AA0002000FFF7EDFE70BD002B27D07023092238
+:100AB000E25472B601202389A26F03432381E368C5
+:100AC000616F5B691A6000229960002120005A6002
+:100AD00000F0FEFE62B6E6E780221201934200D1EA
+:100AE000C7E0D4D8A022D200934240D1784A002354
+:100AF000E2676367A36740B2002813DA7023142209
+:100B0000E25472B6012263890021134300226381FB
+:100B1000E36820009B699A601A605A6000F0B0FE9A
+:100B200062B6C0E770230B22E25472B60122238919
+:100B3000134300222381E3685B699A601A60C4E76B
+:100B40008023E25C0B005CE781225200934262D07A
+:100B5000FF32FF329342A3D18223E35C012B9FD16A
+:100B600088220221A35A0B43A3520022BFE7594A0D
+:100B700094466344012B00D992E78423E25C23006E
+:100B800084335B7883211B02134382226068A25C5A
+:100B90004568615C2000A847002800D180E74368D1
+:100BA00001686367A167E6671EE78223E35C012BA8
+:100BB00000D075E788220221A35A8B43A35200225A
+:100BC00095E784230F21E25CE35620001140002BBF
+:100BD00057DB00F0E5FD012857D0022800D05FE781
+:100BE0003D4A4DE72378042B51D08223E35C002B50
+:100BF0000AD08B22A3540423237063681B68002B44
+:100C000002D002212000984700236367A367E367AF
+:100C10008033E05C6FE78223E35C002B00D03FE78A
+:100C20000F2211008433E35C19401A4206D05BB2F4
+:100C30002000002B5CDB00F003FF605D0023636796
+:100C4000A367E36757E7254A1AE78223E35C002B93
+:100C500000D025E70F2211008433E35C19401A42CB
+:100C6000ECD05BB22000002B3EDB00F0B1FE605DFB
+:100C7000E4E723008B33636701230121A367E66761
+:100C8000B2E600F0B5FD0128A7D1154AF8E672B624
+:100C90002000A368134A00211340A36020221030D3
+:100CA000FFF7E4FA200000F05BFD62B68B23E65408
+:100CB000883B237063681B68002B96D003212000BB
+:100CC000984792E7FEFFFEFF02030000020C0000BF
+:100CD000C108000800FAFFFF582500085C2500083D
+:100CE000542500080100010000F092FE605DA5E7B8
+:100CF00000F0BEFE605DA1E7000000000000000003
+:100D0000702310B5C35C0400152B20D8244A9B0027
+:100D1000D3589F468623E25C230086335B78806F3E
+:100D20001B021343984223D370231422E25472B659
+:100D3000012263890021134300226381E3682000BC
+:100D40009B699A601A605A6000F09AFD62B610BD05
+:100D5000FFF796FDFBE7C36F002B00D09847A36811
+:100D6000104A1340A36070220023A367A354EEE748
+:100D7000E368198AFFF76EFA0029D5D172B601220D
+:100D80002389200013432381E3685B69996019601C
+:100D9000596000F09DFD62B670230A22E254D6E746
+:100DA000A4240008FEFFFEFF000000000000000079
+:100DB0007023C35C10B50400152B05D8154A9B00A1
+:100DC000D3589F46FFF75CFD10BD70230B22C25421
+:100DD00072B6012203890021134300220381C368F4
+:100DE0005B699A601A605A6000F072FD62B6EBE7C8
+:100DF000C3689B695B68002BE6D1C36F002B00D0F2
+:100E00009847A368044A1340A36070220023A36795
+:100E1000A354D9E7FC240008FEFFFEFF00000000F9
+:100E20000F4A70B594460322FF25C0260240D20027
+:100E300095408901914083089B006344B6009C590A
+:100E40001F22AC430C439C51012302409340C0221B
+:100E500040098000604452008350036070BDC0466A
+:100E600000E100E0000000000000000000000000C1
+:100E70000F2310B50340040003202040FF24074A3D
+:100E8000C0009446844089018140083B9B089B0038
+:100E90006344DA69A2430A43DA6110BD00ED00E061
+:100EA000064B074A02219A62002249429A62D9609F
+:100EB0000131DA6019611A617047C04600100240C2
+:100EC000FFFF81FF000000000000000000000000A4
+:100ED00001211D4A13680B43136001311368194245
+:100EE000FCD0032153688B435360174A0931080033
+:100EF000536818401942FAD1FD2311680B40136062
+:100F0000E4235B035060D06210635063D06253608F
+:100F1000802311685B040B43802113600A4A890413
+:100F200013680B42FCD01221084B1960536810392A
+:100F30000B4353600C2204494B681340082BFBD130
+:100F4000034B044A1A607047001002400020024020
+:100F50000008002000A24A04000000000000000079
+:100F600010B5744672B600F02BFE62B6200001F098
+:100F7000CFF910BD000000000000000000000000DC
+:100F8000064B074A10B55A6000229A6007320221C8
+:100F90000B201A60FFF76CFF10BDC04610E000E0A8
+:100FA0001F1C000000000000000000000000000006
+:100FB0007047C04600000000000000000000000074
+:100FC000F0B54E46032457464546DE46E0B52700B9
+:100FD000530917405607D508520525401C40130FEA
+:100FE000012285B0039391460023A4460297F60F91
+:100FF000009717E05C4607682740009C274307607E
+:10100000072B52D85446076A3A40224302624908E5
+:1010100044D0624692009446009A76009200AD0059
+:10102000009201334A460A42F1D007274C46039A00
+:101030001F40BF00BA409C4092468288A243324380
+:1010400092B28280846801945C00A04603244246E8
+:101050009440E24393462200A046019C9443220020
+:1010600044462A438260C268A24364462243C26067
+:101070000F22BA40029CD243022CBBD1072B1AD8B4
+:10108000076A17405246174307625C4602684908E0
+:101090002240009C224302600029BAD105B0F0BC76
+:1010A000BB46B246A946A046F0BD5446476A3A4000
+:1010B00022434262ABE7476A1740524617434762F2
+:1010C000E3E7C04600000000000000000000000050
+:1010D000C021024A93890B4393817047004400402A
+:1010E00010B5074C074A20000021FFF789FB064B8B
+:1010F00003216367054B1C20A367FFF791FE10BD1A
+:1011000008080020D11000080044004000A24A0452
+:10111000F8B5CE464746456F06002B8880B59CB291
+:10112000DB0552D472B62F23234234D0203B98469D
+:101130000E3B9946202708E0A98889B227421FD193
+:101140002F222B889CB21A4225D043462342F3D04B
+:1011500049464A462140E3084942134039401B02B0
+:101160001943A30701D540231943630701D5802301
+:101170001943301D00F0DCFFA98889B22742DFD077
+:101180007C23F35C30001940FFF75AFB2F222B8899
+:101190009CB21A42D9D162B68022AB8990469FB2E6
+:1011A0001A4201D022421AD1402398463B4207D02E
+:1011B000234205D072B6726CB36C9A421AD062B6F2
+:1011C000AF81C0BCB946B046F8BD72B680210430CC
+:1011D000C90000F0ADFF104B2B8062B6A2E772B6DB
+:1011E00030003030FFF76CFA00280DDB80B2A880A9
+:1011F00062B6D9E7B36B002BE1D01021301D00F0AF
+:1012000097FF43469F43DAE70821301D00F090FF27
+:1012100043469F43ECE7C046FFFEFFFF000000008F
+:1012200010B574460348FFF773FF200001F070F813
+:1012300010BDC046080800200000000000000000AB
+:101240000100F0B5D64646464F468B1C57489A4695
+:101250009B00C318C0B55E68B269002956D101244D
+:10126000534B5F7C3C424CD047893C4200D18BE0E1
+:101270009F7DDC7D936824023C43002B00D168E015
+:10128000002C00D18AE00125AC461D19A946494D24
+:10129000A846884447463F781F7001339945F9D1E5
+:1012A000936800294CD1002B00D17DE0738A404F18
+:1012B000A34260D89389012B5DD040257B7C2B43D2
+:1012C0007B749368002B01D01B19936017685368D7
+:1012D0003F1B1B19536093891760013B9BB293819D
+:1012E000728AA24201D8002B0BD1624643899343F4
+:1012F000438153469D0045196B68DB68002B00D085
+:101300009847E0BCBA46B146A846F0BD0124284F34
+:10131000A0463B7D1C42F4D023008B409C46644693
+:1013200043891C4235D0BB7DFC7D24021C4393685D
+:1013300006D0002BA9D145463B7DAB433B75C5E7A5
+:101340001B4F3D7DB8460127BD432F0045462F75F5
+:10135000B8E713681F1E24D0002C1FD00125AC460F
+:101360002700002BA2D14825114C637C2B436374CA
+:101370003C000027ABE748257B7C2B437B7401278F
+:101380000D4B1F7093689DE740215A7C0A435A74A5
+:10139000B7E742463B7D93433B75B2E701239C464A
+:1013A00084E701239C46DEE71368D9E7A408002000
+:1013B000005C0040205C0040880800200000000025
+:1013C0000100F8B5CE46474630488B1C9B00C31839
+:1013D0005B6880B59C465A69002904D004242C4BD4
+:1013E0005D7C2C423FD101238B4005892B420FD0DD
+:1013F000D4681668A14654684C445460371BA64212
+:101400000AD19D4363469B680581002B00D0984715
+:10141000C0BCB946B046F8BD63461B8ABC46984678
+:101420009F422AD864469368D4604B449360002C52
+:1014300007D0184A1D198C181A78013322709D4262
+:10144000FAD1002913D1474519D8C3681B8A9C4596
+:1014500003D38623C35A9E4211D30A210C4A537CDC
+:101460000B435374D4E75D7CA5435D74BBE7012156
+:10147000074A537C0B435374CAE79C46D2E70221C8
+:10148000034A537C0B435374C2E7C046A4080020B0
+:10149000005C0040205C00400000000000000000F4
+:1014A000F0B5564644465F464D46F0B4744BF2469E
+:1014B0009A7983B0D4B2520700D583E0714DE30727
+:1014C00053D4A3074BD4240705D56B68DB68002BE6
+:1014D00001D0280098476A4B9A78DF7917401A792B
+:1014E000597A11409A7B8846D2B2019201223A423F
+:1014F00041D101239946624B2E0001249B463B00BB
+:101500004A4623411A4207D03369002B04D05B4678
+:10151000E0B29873FFF754FF01340436082CEED183
+:101520000126574F073C434623411E4206D02B69F4
+:10153000002B03D0E0B2B873FFF782FE013404350C
+:10154000082CF0D14E4B019A50469A7300F0E0FE01
+:1015500003B0F0BCBB46B246A946A046F0BD280089
+:10156000FFF716FA2407B6D5AFE72800FFF7F8F91A
+:10157000A307A8D5F3E700219C7B9973597CE4B2BB
+:10158000C8B2C9063AD404210240014202D0587CB4
+:1015900088435874002A55D07023EB5CEA68002B0E
+:1015A00036D0062B34D0364E9069B17DF37D1B02C8
+:1015B0000B43082B02D18368002B47D00020FFF794
+:1015C0003FFE37E00122987BDA71002201211A7276
+:1015D000C0B25A729A7299730131C9B21A74DA742C
+:1015E0009A745A755A741A750829F4D19873254853
+:1015F000FFF796F9504600F08BFEA9E78026597C4C
+:101600000240314359740421014216D1002A19D0F5
+:1016100001231D4E00213370EB6828005B6898475A
+:101620003378002B06D04021154A537C0B4353746A
+:1016300000233370124B9C735BE7587C88435874CB
+:10164000002AE5D10020FFF7BBFEF3E703689B46C5
+:10165000002BB3D10C4B28009946012349460B704F
+:101660005368002198474B461B78002BE2D040235B
+:10167000727C134373745A464B461A70DAE7C046BD
+:10168000005C0040A4080020890800200000000041
+:10169000002210B5024B03481A70FFF731F910BD54
+:1016A00089080020A40800200000000000000000BD
+:1016B000002230B501241D4B1D491A700A701D49C6
+:1016C0000A705978214359700121D9711A725A72DE
+:1016D0009A724368002B27D0DB685A1E9341DB00C7
+:1016E0000733124AD3720023917B9373537CC9B2A0
+:1016F000DCB2DB0703D54025537C2B435374E4064F
+:1017000004D58024094A537C23435374074B0A4A67
+:1017100099730021C26001209A7B9973D979D2B262
+:101720000143D9719A7330BD0723DAE7005C0040AA
+:1017300089080020880800206025000800000000BB
+:101740008A23C25C0B3B1340014A13707047C046AA
+:10175000005C0040000000000000000000000000ED
+:10176000002270B5012101250C4B9E7B9A73F6B2C5
+:101770002C008C40C8B29873D8790131A043D8713D
+:10178000587AA04358721A74DA749A745A755A7453
+:101790001A750829ECD19E7370BDC046005C0040EC
+:1017A0008B1C9B00C0184368002B18D09B69002B32
+:1017B00015D00C4A937B9173DBB2002908D020200E
+:1017C000127D1040424250410130064A93737047E7
+:1017D0002020527C1040424250410130F5E7002069
+:1017E000F5E7C046005C004000000000000000007B
+:1017F0008B1C9B00C0184368002B17D05B69002B23
+:1018000014D00B4A937B9173527CDBB2002907D032
+:1018100010201040424250410130054A93737047F6
+:1018200020201040424250410130F6E70020F6E708
+:10183000005C00400000000000000000000000000C
+:1018400030B5140020200B4908340B5C13700132B2
+:10185000A242FAD1084B1A78002A09D00024402568
+:101860008A7B8C73487CD2B2284348748A731C707C
+:1018700030BDC046005C0040890800200000000028
+:101880008B1C9B00C018426870B595690C002868D5
+:101890000123002805D0518A40180138FEF79EFC2C
+:1018A00083B2AB81002C0AD00120A040044B997B6D
+:1018B0009C735A7AC9B20243D2B25A72997370BDFC
+:1018C000005C00400000000000000000000000007C
+:1018D000F8B58B1C4746CE469B00C3185B6880B5A5
+:1018E0009C465F693B68984663461C8A444500D91C
+:1018F0004446FC60002C10D12F4A937B9173DBB2DD
+:10190000002937D001202C494A7C02434A742A4AD4
+:101910009373C0BCB946B046F8BD274ABB68914630
+:10192000264AE6188D181A7801332A709E42FAD199
+:101930004B464A469B7B9173DBB20029E2D120499A
+:101940000A78002A29D141467A688A1A94421ED11F
+:10195000C268128A944207D38622815C863042781C
+:1019600012020A43904512D30A2013494A7C0243CB
+:101970004A74CCE712490D78002D0ED17021415CDC
+:101980000B29E0D10A20517C01435174BFE70220AA
+:1019900009494A7C02434A74B9E700220A70064AA0
+:1019A0009373012203899343038163469B68002B51
+:1019B000AFD000219847ACE7005C0040205C0040BD
+:1019C0008808002000000000000000000000000067
+:1019D0000C4B9A7B9973D2B2002907D1597CC90765
+:1019E0000BD56020597C0143597403E02020197DF8
+:1019F00001431975034B9A7370472020597C0143AA
+:101A00005974F7E7005C004000000000000000008F
+:101A1000084B9A7B9973D2B2002906D12020597CB9
+:101A200001435974034B9A7370471020597C01434A
+:101A30005974F7E7005C004000000000000000005F
+:101A40000A4B9A7B9973D2B2002906D12420597C83
+:101A500081435974054B9A7370476020197D814307
+:101A60001975197D203001431975F3E7005C0040BA
+:101A70000A4B9A7B9973D2B2002906D12420597C53
+:101A800081435974054B9A7370473020597C8143C8
+:101A90005974597C103001435974F3E7005C0040DD
+:101AA000FC22244BD203996A10B511439962996ABA
+:101AB0002148802401409962996A59691F480A4364
+:101AC0005A6190225B6900231D49D2059380916081
+:101AD0001C49D1602121494291828821490111622A
+:101AE0001949536211600122184952428B808A6061
+:101AF000C8608A820B624B620C601549154C8B8062
+:101B00008A60CC608A820B624B620B6012498B80C8
+:101B10008A60C8608A8211480B624B620B60104970
+:101B20008B808A60C8608A820B624B620B60FFF711
+:101B3000CFF910BD00100240FFFF81FF55555555EC
+:101B40005FF7FFFF05515565A00400280004004819
+:101B50000008004855555501000C00485055555592
+:101B60000014004800000000000000000000000019
+:101B70007047C046000000000000000000000000A8
+:101B8000012310B5084C23700023636000F018FB9C
+:101B900000F056FB00F074FB0449054800F088F99A
+:101BA0000223237062B610BDA80A0020A4250008F5
+:101BB000180A0020000000000000000000000000E3
+:101BC00010B500F03DF810BD00000000000000005E
+:101BD00010B5036100230C4C43612369C2609A680D
+:101BE000914204D91B68891A9A688A42FAD35A68C2
+:101BF00081600360426010609A685860521A9A606F
+:101C000001235B42A36110BD180A00200000000000
+:101C1000036881689A6852189A60426813605A6033
+:101C2000002301220360024B52429A617047C04672
+:101C3000180A002000000000000000000000000062
+:101C400070B51B4DEB692C690133EB612B00103330
+:101C50009C4215D0A368013BA3600FE02268616835
+:101C60000A605160236062B620002169E3689847EA
+:101C700072B66369002B04D12C69A368002BEDD0E8
+:101C800070BD2A6991688B4204D912685B1A916809
+:101C90009942FAD35168A360616022600C60916838
+:101CA0005460CB1A936001235B42AB61E4E7C0460A
+:101CB000180A0020000000000000000000000000E2
+:101CC00072B62423CB5C0C2B0BD8114A9B00D35843
+:101CD0009F468A6A9368013393604A680B68136071
+:101CE0005A60012300225B428B622533CA548B6900
+:101CF00088681B689A688242FBD25A680B604A6007
+:101D00001160596062B6704700228B6A1A60E8E77A
+:101D1000B8250008000000000000000000000000DE
+:101D200024230022C254836981681B689A688A420E
+:101D3000FBD25A6803604260106058607047C0462A
+:101D40002422074B10B5D96888541868046863606A
+:101D50001C6001248454D860FEF71EFA10BDC046F2
+:101D6000180A002000000000000000000000000031
+:101D700070B5164C0500E66886B04B1C19D03300D0
+:101D80006846134AFFF724FF2423E1682068CD54F6
+:101D90000268546022600122C254E060FEF7FCF940
+:101DA000009B002B02D06846FFF732FFB06A06B0F6
+:101DB00070BD2423F0542068310002685460226012
+:101DC0000122C254E060FEF7E7F9EFE7180A0020AD
+:101DD000C11C00080000000000000000000000001E
+:101DE00070B5154C8162E368826899688A4214D99B
+:101DF000242200259D549A6912689568A942FBD354
+:101E000051681A6059600B6053600121242281548B
+:101E10001900E060FEF7C0F970BD24230021C15411
+:101E200083691B6899688A42FBD95A6803604260DB
+:101E300010605860F0E7C046180A0020000000005B
+:101E4000044BDA68196890688B6898428041404278
+:101E50007047C046180A0020000000000000000083
+:101E60000D4B10B51868D9680268012453601A60D8
+:101E7000D8602422002384548B548B698C681B689F
+:101E80009A68A242FBD85A680B604A601160596098
+:101E9000FEF782F910BDC046180A002000000000BD
+:101EA000FEE7C04600000000000000000000000047
+:101EB000F0B5002604001D4B87B0066381630D005A
+:101EC000586000F015FA23002833A362E362183B40
+:101ED0002361636101235B42A361154BE661DB690A
+:101EE0000027A660666321001E00882326626762C1
+:101EF000246064603C31E75020000E4A083B00F04B
+:101F00001FF86B68E06024220362012383540A4AAD
+:101F100003930092EA68094B01922A69684602928B
+:101F20000493059700F064F807B0F0BDA80A0020FC
+:101F3000180A0020EC25000888250008A11E0008CA
+:101F400010B5040008008B604364022100238184E3
+:101F50000364C363013926338461C154C261220022
+:101F6000030028320261E26A103342611360E362C7
+:101F70001C33C36204330363436310BD00000000DD
+:101F80008368020018005168503830B501621900AA
+:101F900014697439C1600C61546902254C61114C9B
+:101FA0000C621468D1681A002C3A81601580416473
+:101FB00000210D4A0164C163013D2631826145540F
+:101FC000C46119001400403928340C60D46A4C6094
+:101FD0002160D1621A00203B243A12601B605B60D2
+:101FE00030BDC046B9010008180A002000000000FA
+:101FF00010B5FFF7C5FFFFF793FE10BD000000000E
+:1020000010B572B650394418210024392062E160BD
+:102010000B61029B20004B61124B0B620223A260FA
+:10202000626400220121A3840F4B2264E263263202
+:10203000A361A1540D4A1900E261220028312161F7
+:10204000D96A103261610A60DA6223002C33E362DC
+:102050000433002123636363FFF7C2FE62B62000EE
+:1020600010BDC046B9010008180A002090250008DC
+:1020700070B572B6114BDC682500A062E06A2C35A1
+:10208000854207D12623E35C002B0ED00F20FFF7FB
+:1020900057FE70BD0368E362FFF742FEE06AA842A4
+:1020A000F8D12623E35C002BF0D12533E35C9B07BA
+:1020B000ECD16269236913605A60E7E7180A0020CF
+:1020C00010B5010072B60820FFF752FE62B610BDCF
+:1020D000084B10B5DB68002908D0426818605A60C8
+:1020E000136043600420FFF743FE10BD012040420F
+:1020F000FBE7C046180A00200000000000000000B6
+:102100000300006810B5834205D002681A6053606E
+:102110008162FFF705FE10BD000000000000000016
+:1021200000230060406083607047C04600000000EC
+:10213000F0B5C646046806000D0000B5A04233D0D5
+:102140000023242798460CE06068A368C26B134301
+:10215000C363C25D0A2A1DD00B2A28D02468A64278
+:1021600022D0E3682B43E360002DEDD023692B429E
+:10217000EAD12468B44217D0E3682B43E3602369B3
+:102180002B42F6D06068A368C26B1343C363C25D81
+:102190000A2AE1D1826A1A42E0D043468362FFF7FD
+:1021A000BFFD2468A642DCD180BCB846F0BD826A7F
+:1021B00013409A42D2D1F0E7000000000000000076
+:1021C000024B034A1A60034A5A607047B00A002063
+:1021D000700C002000200020000000000000000023
+:1021E00070B5094C26686568AB1B984209D82B1A54
+:1021F00048421840821A964203D8954201D3626041
+:1022000070BD0020FCE7C046B00A002000000000BE
+:1022100070B572B6094C26686568AB1B98420AD83F
+:102220002B1A48421840821A964204D8954202D38B
+:10223000626062B670BD0020FBE7C046B00A0020B5
+:102240000548064B10B503600023436083600C30E3
+:10225000FFF766FF10BDC046B80A00201122000833
+:1022600010B50024046041608260C36010BDC046A8
+:1022700010B50022FFF7B4FF10BDC04600000000FB
+:1022800070B5134C2000FFF74BFF23002000114DC9
+:102290001033236104222B0014211430FFF7E0FFD8
+:1022A000230024336362230020002833A362042226
+:1022B0002B001C212C30FFF7D3FF23003C33E363BA
+:1022C00023004033236423004433636470BDC0465D
+:1022D000D40A002071220008000000000000000065
+:1022E000EFF30983203383F3098862B67047C04651
+:1022F000024A0349136A0B401362704700ED00E085
+:10230000FFFF00FF000000000000000000000000D0
+:1023100010B50F3000D110BD72B6EFF30984203C28
+:1023200084F3098880235B04E361FFF789FD0028BB
+:1023300002D0034BA361EEE7024BA361EBE7C0467B
+:10234000C7010008CA0100080000000000000000EA
+:1023500070B580250B4B0C4ADB680C4CDA61ED0143
+:1023600080231B02A5610A48A361FFF7A9FE802311
+:10237000DB05A36180231B060548A361FFF7A0FED0
+:10238000EEE7C046180A002098250008000400481F
+:1023900088130000000000000000000000000000A2
+:1023A00010B582B0FEF744F8FFF7EAFB80210F4C2E
+:1023B00001222000C901FEF703FE80210122200036
+:1023C0000902FEF7FDFD8023DB05A36180230022C7
+:1023D000A8211B06A36106480092064B80324900E3
+:1023E000FFF70EFE0448FFF76BFEFBE70004004812
+:0C23F000200B002051230008881300007F
+:1023FC001C260008700C0020700C0020700C0020B3
+:10240C001C26000800000000000000000000000076
+:10241C001C26000800000000000000000000000066
+:10242C001C26000800000000000000000000000056
+:10243C001C26000800000000000000000000000046
+:10244C001C26000800000000000000000000000036
+:10245C001C26000800000000000000000000000026
+:10246C001C26000800000000000000000000000016
+:10247C00000000009107000871070008D107000850
+:10248C00B1070008C1070008A10700088107000870
+:10249C0061070008E1070008500D00084E0D000808
+:1024AC004E0D00084E0D00084E0D00084E0D000894
+:1024BC00500D00084E0D00084E0D0008140D0008BC
+:1024CC00280D0008560D00084E0D00084E0D000892
+:1024DC004E0D00084E0D00084E0D00084E0D000864
+:1024EC004E0D00084E0D00084E0D0008500D000852
+:1024FC00C40D0008C80D0008C80D0008C80D000860
+:10250C00C80D0008C80D0008C40D0008C80D00084F
+:10251C00C80D0008C80D0008C40D0008C40D000843
+:10252C00C80D0008C80D0008C80D0008C80D00082B
+:10253C00C80D0008C80D0008C80D0008C80D00081B
+:10254C00F00D0008CA0D000801000000000000009A
+:10255C000000000000000000B1090008010D000897
+:10256C00B10D0008400040009408002094080020A1
+:10257C00010000008C0800206330000069646C6569
+:10258C00000000006E6F6E616D6500006C65642D5F
+:10259C00626C696E6B00000084250008000000006E
+:1025AC000000000038090020180A0020041D000853
+:1025BC00E21C0008E21C0008081D0008DA1C0008D8
+:1025CC00D21C0008E21C0008DA1C0008E21C0008FF
+:1025DC00E21C0008E21C0008E21C0008DA1C0008DF
+:1025EC006D61696E002C063804044C080C10141C28
+:1025FC002024252600000000000024040100040013
+:0C260C0000000000000C0010283034001A
+:0426180000A24A04CE
+:04000005080001915D
+:00000001FF
diff --git a/testhal/SK32/GPIO/main.c b/testhal/SK32/GPIO/main.c
new file mode 100644
index 0000000000..fbe0deee47
--- /dev/null
+++ b/testhal/SK32/GPIO/main.c
@@ -0,0 +1,85 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+#include "ch.h"
+#include "hal.h"
+
+/*===========================================================================*/
+/* GPIO test use case on the GENERIC_SK32_F077 board. */
+/*===========================================================================*/
+
+/*
+ * Board resources used by this test:
+ *
+ * - PB14 / PB15: two board LEDs wired to push-pull outputs, the PAL
+ * abstraction drives them high (PAL_HIGH) to switch the LEDs on.
+ *
+ * Both the pad based APIs (palSetPadMode(), palSetPad(), ...) are exercised
+ * on this port because they operate on the underlying GPIO register block.
+ */
+
+/* Blinker thread, times are in milliseconds. Both LEDs are switched on for
+ 500 ms and off for 500 ms, forever (1 s full blink period). */
+static THD_WORKING_AREA(waBlinkThread, 128);
+static THD_FUNCTION(BlinkThread, arg) {
+ (void)arg;
+ chRegSetThreadName("led-blink");
+ while (true) {
+ palSetPad(GPIOB, GPIOB_PIN14);
+ palSetPad(GPIOB, GPIOB_PIN15);
+ chThdSleepMilliseconds(500);
+
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palClearPad(GPIOB, GPIOB_PIN15);
+ chThdSleepMilliseconds(500);
+ }
+}
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ /* Configuring the PB14/PB15 LED lines as push-pull outputs, initially off. */
+ palSetPadMode(GPIOB, GPIOB_PIN14, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, GPIOB_PIN15, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palClearPad(GPIOB, GPIOB_PIN15);
+
+ /*
+ * Creating the LED blinker thread.
+ */
+ chThdCreateStatic(waBlinkThread, sizeof(waBlinkThread), NORMALPRIO,
+ BlinkThread, NULL);
+
+ /*
+ * Normal main() thread activity.
+ */
+ while (true) {
+ chThdSleepMilliseconds(500);
+ }
+}
diff --git a/testhal/SK32/IIC/Makefile b/testhal/SK32/IIC/Makefile
new file mode 100644
index 0000000000..51a33bb77a
--- /dev/null
+++ b/testhal/SK32/IIC/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = iic
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/testhal/SK32/IIC/build.log b/testhal/SK32/IIC/build.log
new file mode 100644
index 0000000000..7b55f64e08
--- /dev/null
+++ b/testhal/SK32/IIC/build.log
@@ -0,0 +1,70 @@
+Compiling crt0_v6m.S
+Compiling vectors.S
+Compiling chcoreasm.S
+Compiling crt1.c
+Compiling oop_base_object.c
+Compiling hal.c
+Compiling hal_safety.c
+Compiling hal_st.c
+Compiling hal_buffered_serial.c
+Compiling hal_buffers.c
+Compiling hal_queues.c
+Compiling hal_mmcsd.c
+Compiling hal_i2c.c
+Compiling hal_pal.c
+Compiling hal_flash.c
+Compiling nvic.c
+Compiling sk32_isr.c
+Compiling hal_lld.c
+Compiling hal_efl_lld.c
+Compiling hal_st_lld.c
+Compiling hal_pal_lld.c
+Compiling sk32_dma.c
+Compiling hal_i2c_lld.c
+Compiling hal_sled_lld.c
+In file included from ../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:49:
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.h:65:6: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 65 | #if (HAL_USE_SLED == TRUE) || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:51:5: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 51 | #if HAL_USE_SLED || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+Compiling stm32_dma.c
+Compiling hal_usb_lld.c
+Compiling board.c
+Compiling osal.c
+Compiling chsys.c
+Compiling chrfcu.c
+Compiling chdebug.c
+Compiling chtrace.c
+Compiling chvt.c
+Compiling chschd.c
+Compiling chinstances.c
+Compiling chthreads.c
+Compiling chregistry.c
+Compiling chsem.c
+Compiling chmtx.c
+Compiling chcond.c
+Compiling chevents.c
+Compiling chmsg.c
+Compiling chdynamic.c
+Compiling chmboxes.c
+Compiling chmemcore.c
+Compiling chmemheaps.c
+Compiling chmempools.c
+Compiling chpipes.c
+Compiling chobjcaches.c
+Compiling chdelegates.c
+Compiling chfactory.c
+Compiling chcore.c
+Compiling main.c
+Linking build/iic.elf
+Creating build/iic.hex
+Creating build/iic.bin
+Creating build/iic.dmp
+
+ text data bss dec hex filename
+ 5528 4 8184 13716 3594 build/iic.elf
+Creating build/iic.list
+
+Done
diff --git a/testhal/SK32/IIC/cfg/chconf.h b/testhal/SK32/IIC/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/testhal/SK32/IIC/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/IIC/cfg/halconf.h b/testhal/SK32/IIC/cfg/halconf.h
new file mode 100644
index 0000000000..ce66c4a773
--- /dev/null
+++ b/testhal/SK32/IIC/cfg/halconf.h
@@ -0,0 +1,568 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C TRUE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB FALSE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/IIC/cfg/mcuconf.h b/testhal/SK32/IIC/cfg/mcuconf.h
new file mode 100644
index 0000000000..7f17fe8bd3
--- /dev/null
+++ b/testhal/SK32/IIC/cfg/mcuconf.h
@@ -0,0 +1,98 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for the
+ * USB IP, but the SK32 port provides native drivers for the entire core
+ * platform and for every peripheral, so no shared STM32 low level driver
+ * (TIMv1, USARTv2, ADCv1, SPIv2, I2Cv2, ..., nor the CANv1/DACv1/DMAv1/
+ * RTCv2/xWDGv1 stubs) is linked in. This mcuconf.h only needs to satisfy
+ * the single SK32 naming scheme:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST/peripheral settings consumed by
+ * the native SK32 HAL low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * I2C driver system settings.
+ * The SK32 native interrupt-driven master I2C driver is selected through
+ * the SK32_I2C_USE_I2C1 switch.
+ *
+ * I2C1 EEPROM test:
+ * - I2C1 master at 100kHz (standard mode) on PB6 (I2C1_SCL, alternate 13)
+ * and PB7 (I2C1_SDA, alternate 13), open drain outputs.
+ */
+#define SK32_I2C_USE_I2C1 TRUE
+#define SK32_I2C_I2C1_PRIORITY 3
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches. Not used by this test: HAL_USE_SERIAL
+ * is disabled in halconf.h, the switches are kept at FALSE.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 FALSE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ * The SK32 native interrupt-driven SPI driver is selected through the
+ * SK32_SPI_USE_SPIx switches. Not used by this test: HAL_USE_SPI is
+ * disabled in halconf.h, the switches are kept at FALSE.
+ */
+#define SK32_SPI_USE_SPI1 FALSE
+#define SK32_SPI_USE_SPI2 FALSE
+#define SK32_SPI_SPI1_PRIORITY 3
+#define SK32_SPI_SPI2_PRIORITY 3
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+#endif /* MCUCONF_H */
\ No newline at end of file
diff --git a/testhal/SK32/IIC/iic.hex b/testhal/SK32/IIC/iic.hex
new file mode 100644
index 0000000000..568852bed1
--- /dev/null
+++ b/testhal/SK32/IIC/iic.hex
@@ -0,0 +1,350 @@
+:020000040800F2
+:10000000000400209101000811120008930100086B
+:100010009301000893010008930100089301000870
+:100020009301000893010008930100089301000860
+:10003000930100089301000893010008A10500083E
+:100040009301000893010008930100089301000840
+:100050009301000893010008930100089301000830
+:100060009301000893010008930100089301000820
+:100070009301000893010008930100089301000810
+:100080009301000893010008930100089301000800
+:10009000930100089301000893010008110700086C
+:1000A00093010008930100089301000893010008E0
+:1000B00093010008930100089301000893010008D0
+:1000C00072B6244880F30888234880F30988022008
+:1000D00080F31488BFF36F8F00F022F900F0B8FCB2
+:1000E0001E481F491B4A914202DA08600431FAE7B0
+:1000F0001C49194A914202DA08600431FAE71A49A8
+:100100001A4A1B4B9A4204DA086810600431043220
+:10011000F8E700201749184A914202DA08600431D2
+:10012000FAE700F015F900F003F9144C144DAC4255
+:1001300003DA216888470434F9E701F0C1F8114C6B
+:10014000114DAC4203DA216888470434F9E70F49BE
+:1001500008470000000400200008002055555555B0
+:10016000000000200004002098150008000800206E
+:100170000408002008080020880B0020C0000008A8
+:10018000C0000008C0000008C000000841030008CB
+:1001900096E700F000F8FEE7F0B544464D465646B7
+:1001A0005F46F0B46B46CB60C3689D46F0BCA0468A
+:1001B000A946B246BB46F0BD62B6281CA047002047
+:1001C00000F03EFFFEE700F033FE024A024B1360F0
+:1001D000FEE7C04604ED00E000000080002934D0B6
+:1001E0000123002210B488422CD301242407A14209
+:1001F00004D2814202D209011B01F8E7E400A142C6
+:1002000004D2814202D249005B00F8E7884201D360
+:10021000401A1A434C08A04202D3001B5C08224338
+:100220008C08A04202D3001B9C082243CC08A042A9
+:1002300002D3001BDC082243002803D01B0901D095
+:100240000909E3E7100010BC704701B5002000F079
+:100250000BF802BD0029F8D003B5FFF7C1FF0EBCB3
+:100260004243891A1847C0467047C046F0B505009A
+:10027000830749D0541E002A45D003000326CAB282
+:1002800002E00135013C3ED301332A703342F8D1FC
+:10029000032C30D9FF220A401502AD182A04AD18EC
+:1002A0000F2C34D92700103F3F093E01B4461E00F1
+:1002B0001A0010366644156055609560D56010329E
+:1002C0009642F8D101373F01DB190F270C22274056
+:1002D00022403C00002A0ED03E1FB608B400A446BF
+:1002E0001A001C1D644420C2A242FCD10324013622
+:1002F000B6009B193C40002C05D0C9B21C191970DE
+:1003000001339C42FBD1F0BD03001400C0E727007D
+:10031000E2E7C0460000000000000000000000000E
+:100320007047C04600000000000000000000000010
+:100330007047C04600000000000000000000000000
+:10034000FEE7C046000000000000000000000000C2
+:1003500070B5114C250080356068A2682168904214
+:100360000AD2030040C940C39342FBD3013A131A97
+:100370009B0801339B00C018E368834207D9013B07
+:100380001A1A9208013200219200FFF76FFF103411
+:10039000AC42E1D170BDC04620140008000000004E
+:1003A00010B500F09DF800F023F900F011F800F00E
+:1003B000B7FB00F005F810BD0000000000000000D1
+:1003C00010B500F0FDF810BD0000000000000000B6
+:1003D00010B500F065FA10BD00000000000000003C
+:1003E000012310B50370002343600C3000F078FE49
+:1003F00010BDC0460000000000000000000000002A
+:1004000010B5040072B6416000F05AFA022323705E
+:1004100062B6002010BDC0460000000000000000D1
+:1004200010B5040084B072B60020A06003302070C4
+:10043000089802900798019006980090200000F01C
+:10044000BFFA431C5A1E934102225B429343053379
+:10045000237062B604B010BD000000000000000070
+:100460000F4A70B594460322FF25C0260240D200F1
+:1004700095408901914083089B006344B6009C59D4
+:100480001F22AC430C439C51012302409340C022E5
+:1004900040098000604452008350036070BDC04634
+:1004A00000E100E00000000000000000000000008B
+:1004B0000F2310B50340040003202040FF24074A07
+:1004C000C0009446844089018140083B9B089B0002
+:1004D0006344DA69A2430A43DA6110BD00ED00E02B
+:1004E000064B074A02219A62002249429A62D96069
+:1004F0000131DA6019611A617047C046001002408C
+:10050000FFFF81FF0000000000000000000000006D
+:1005100001211D4A13680B4313600131136819420E
+:10052000FCD0032153688B435360174A09310800FC
+:10053000536818401942FAD1FD2311680B4013602B
+:10054000E4235B035060D06210635063D062536059
+:10055000802311685B040B43802113600A4A8904DD
+:1005600013680B42FCD01221084B196053681039F4
+:100570000B4353600C2204494B681340082BFBD1FA
+:10058000034B044A1A6070470010024000200240EA
+:100590000008002000A24A04000000000000000043
+:1005A00010B5744672B600F0F3FA62B6200000F09F
+:1005B00047FE10BD00000000000000000000000029
+:1005C000064B074A10B55A6000229A600732022192
+:1005D0000B201A60FFF76CFF10BDC04610E000E072
+:1005E0001F1C0000000000000000000000000000D0
+:1005F0007047C0460000000000000000000000003E
+:10060000F0B54E46032457464546DE46E0B5270082
+:10061000530917405607D508520525401C40130FB3
+:10062000012285B0039391460023A4460297F60F5A
+:10063000009717E05C4607682740009C2743076047
+:10064000072B52D85446076A3A40224302624908AF
+:1006500044D0624692009446009A76009200AD0023
+:10066000009201334A460A42F1D007274C46039ACA
+:100670001F40BF00BA409C4092468288A24332434A
+:1006800092B28280846801945C00A04603244246B2
+:100690009440E24393462200A046019C94432200EA
+:1006A00044462A438260C268A24364462243C26031
+:1006B0000F22BA40029CD243022CBBD1072B1AD87E
+:1006C000076A17405246174307625C4602684908AA
+:1006D0002240009C224302600029BAD105B0F0BC40
+:1006E000BB46B246A946A046F0BD5446476A3A40CA
+:1006F00022434262ABE7476A1740524617434762BC
+:10070000E3E7C04600000000000000000000000019
+:1007100070B5F0255B482D01026A7446918A8BB250
+:10072000294229D1D90721D4990752D4402119420D
+:100730005ED0415C002900D071E019065DD45B07F2
+:1007400016D5036BC16A994200D984E0836B002BF4
+:1007500000D186E001214023C154838C0B43838464
+:10076000A0231188DB000B43138001E0838C1382EC
+:10077000200000F065FD70BDD905C90F9D0501D5AC
+:10078000022529438160590503D504258168294341
+:100790008160190503D50825816829438160918806
+:1007A000394801409180A021C9000B4204D0802328
+:1007B00011889B000B43138000233448938200F080
+:1007C000D7F972B602213248494200F079FC62B68C
+:1007D000CEE7138B5B0717D5C36A002BC8D0016B1C
+:1007E0008B42C5D9836A5B5C013113820163BFE729
+:1007F0001D060DD5415C0029BAD1016BC56AA9421D
+:10080000F0D35B07A2D4B3E7836B012BB0D118E020
+:100810005B07ADD5435C002B00D192E7A8E7158AB2
+:10082000C36B816BADB28B4203D2416BCD54C36BB2
+:10083000816B0133C3638B4218D201398B4200D0E4
+:1008400096E7138813490B401380802311889B007F
+:100850000B4313808CE7816AC95C01331182036307
+:1008600086E7802311889B000B43138093880649F9
+:100870000B40938072B600211C3000F021FC62B660
+:1008800076E7C04608080020FFF8FFFFA014000824
+:1008900024080020FFFBFFFF000000000000000014
+:1008A00010B5044C2000FFF79BFD034B236200238F
+:1008B000E36110BD08080020005400400000000063
+:1008C000F8B5066A037805003400012B3FD00023F9
+:1008D0003F22324F6D6827802380A380A382A388A4
+:1008E00069689343287AA380A388373A1343A38087
+:1008F0002B4B994212D82B484900FFF76FFC80B26E
+:10090000A08323882A789BB2022A18D0032A1BD1FD
+:100910000A2213432380244B3380F8BD03282ED0B2
+:100920004B0059181F48FFF759FC0005000D0743FD
+:10093000BFB2A78323882A789BB2022AE6D1082275
+:100940009343063A1343E5E70A229343E2E7174B42
+:100950009842BCD18022164B920319691548114365
+:1009600019611969014019611969D96917200A4388
+:10097000DA610321DB69FFF773FD2C6AA7E78B00BF
+:1009800059188B00C9180748FFF728FCC023000539
+:10099000000D1B021843A083B3E7C0460080FFFF91
+:1009A000A086010000127A0001040000080800205F
+:1009B00000100240FFFFDFFF000000000000000009
+:1009C000F8B5C362069B00254363079B4900836318
+:1009D0004023046A8560818482620563C563C554CF
+:1009E00062B68025FA26164A0221D769AD00760044
+:1009F00000E062B672B6238B194202D123882B42E3
+:100A000006D0D369DB1BB342F3D301204042F8BDCB
+:100A1000E023A288DB001343A380A0232288DB000D
+:100A20001343238008991C3000F03AFB431CEED19D
+:100A3000A388044A1340A38023881D432580E6E74A
+:100A400030090020FFF8FFFF000000000000000058
+:100A5000FC22244BD203996A10B511439962996A1A
+:100A60002148802401409962996A59691F480A43C4
+:100A70005A6190225B6900231D49D20593809160E1
+:100A80001C49D1602121494291828821490111628A
+:100A90001949536211600122184952428B808A60C1
+:100AA000C8608A820B624B620C601549154C8B80C2
+:100AB0008A60CC608A820B624B620B6012498B8029
+:100AC0008A60C8608A8211480B624B620B601049D1
+:100AD0008B808A60C8608A820B624B620B60FFF772
+:100AE00017FD10BD00100240FFFF81FF5555555501
+:100AF0005FF7FFFF05515565A0040028000400487A
+:100B00000008004855555501000C004850555555F2
+:100B10000014004800000000000000000000000079
+:100B20007047C04600000000000000000000000008
+:100B3000012310B5084C23700023636000F0D8FA3D
+:100B400000F016FB00F034FB0449054800F098F96A
+:100B50000223237062B610BDC009002018150008DA
+:100B6000300900200000000000000000000000002C
+:100B700072B68822024B9850024B853A1A70FEE7F3
+:100B800030090020C0090020000000000000000023
+:100B900010B500F03DF810BD00000000000000009E
+:100BA00010B5036100230C4C43612369C2609A684D
+:100BB000914204D91B68891A9A688A42FAD35A6802
+:100BC00081600360426010609A685860521A9A60AF
+:100BD00001235B42A36110BD30090020000000002A
+:100BE000036881689A6852189A60426813605A6074
+:100BF000002301220360024B52429A617047C046B3
+:100C0000300900200000000000000000000000008B
+:100C100070B51B4DEB692C690133EB612B00103370
+:100C20009C4215D0A368013BA3600FE02268616875
+:100C30000A605160236062B620002169E36898472A
+:100C400072B66369002B04D12C69A368002BEDD028
+:100C500070BD2A6991688B4204D912685B1A916849
+:100C60009942FAD35168A360616022600C60916878
+:100C70005460CB1A936001235B42AB61E4E7C0464A
+:100C8000300900200000000000000000000000000B
+:100C900072B62423CB5C0C2B0BD8114A9B00D35883
+:100CA0009F468A6A9368013393604A680B681360B1
+:100CB0005A60012300225B428B622533CA548B6940
+:100CC00088681B689A688242FBD25A680B604A6047
+:100CD0001160596062B6704700228B6A1A60E8E7BB
+:100CE0002C150008000000000000000000000000BB
+:100CF00024230022C254836981681B689A688A424F
+:100D0000FBD25A6803604260106058607047C0466A
+:100D10002422074B10B5D9688854186804686360AA
+:100D20001C6001248454D860FFF736FA10BDC04619
+:100D3000300900200000000000000000000000005A
+:100D400070B5164C0500E66886B04B1C19D0330010
+:100D50006846134AFFF724FF2423E1682068CD5436
+:100D60000268546022600122C254E060FFF714FA66
+:100D7000009B002B02D06846FFF732FFB06A06B036
+:100D800070BD2423F0542068310002685460226052
+:100D90000122C254E060FFF7FFF9EFE730090020BD
+:100DA000910C00080000000000000000000000009E
+:100DB00070B5154C8162E368826899688A4214D9DB
+:100DC000242200259D549A6912689568A942FBD394
+:100DD00051681A6059600B605360012124228154CC
+:100DE0001900E060FFF7D8F970BD24230021C15439
+:100DF00083691B6899688A42FBD95A68036042601C
+:100E000010605860F0E7C046300900200000000084
+:100E1000044BDA68196890688B68984280414042B8
+:100E20007047C046300900200000000000000000AC
+:100E30000D4B10B51868D9680268012453601A6018
+:100E4000D8602422002384548B548B698C681B68DF
+:100E50009A68A242FBD85A680B604A6011605960D8
+:100E6000FFF79AF910BDC0463009002000000000CD
+:100E7000FEE7C04600000000000000000000000087
+:100E8000F0B5002604001D4B87B0066381630D009A
+:100E9000586000F0C5F923002833A362E362183BD1
+:100EA0002361636101235B42A361154BE661DB694A
+:100EB0000027A660666321001E0088232662676201
+:100EC000246064603C31E75020000E4A083B00F08B
+:100ED0001FF86B68E06024220362012383540A4AEE
+:100EE00003930092EA68094B01922A6968460292CC
+:100EF0000493059700F064F807B0F0BDC009002026
+:100F00003009002060150008B0140008710E0008B8
+:100F100010B5040008008B60436402210023818423
+:100F20000364C363013926338461C154C261220062
+:100F3000030028320261E26A103342611360E36207
+:100F40001C33C36204330363436310BD000000001D
+:100F50008368020018005168503830B501621900EA
+:100F600014697439C1600C61546902254C61114CDB
+:100F70000C621468D1681A002C3A816015804164B3
+:100F800000210D4A0164C163013D2631826145544F
+:100F9000C46119001400403928340C60D46A4C60D4
+:100FA0002160D1621A00203B243A12601B605B6012
+:100FB00030BDC046B9010008300900200000000023
+:100FC00010B5FFF7C5FFFFF793FE10BD000000004E
+:100FD00010B572B650394418210024392062E160FE
+:100FE0000B61029B20004B61124B0B620223A2603B
+:100FF000626400220121A3840F4B2264E263263243
+:10100000A361A1540D4A1900E26122002831216137
+:10101000D96A103261610A60DA6223002C33E3621C
+:101020000433002123636363FFF7C2FE62B620002E
+:1010300010BDC046B901000830090020B8140008EE
+:1010400070B572B6114BDC682500A062E06A2C35E1
+:10105000854207D12623E35C002B0ED00F20FFF73B
+:1010600057FE70BD0368E362FFF742FEE06AA842E4
+:10107000F8D12623E35C002BF0D12533E35C9B07FA
+:10108000ECD16269236913605A60E7E730090020F8
+:1010900010B5010072B60820FFF752FE62B610BD0F
+:1010A000064B10B5DB68002905D003609862032069
+:1010B000FFF746FE10BD01204042FBE7300900204B
+:1010C0000300006810B5002804D000221A60816275
+:1010D000FFF70EFE10BDC04600000000000000003B
+:1010E00000230060406083607047C046000000003D
+:1010F000024B034A1A60034A5A607047C80900202D
+:10110000880B0020002000200000000000000000EC
+:1011100070B5094C26686568AB1B984209D82B1A34
+:1011200048421840821A964203D8954201D3626021
+:1011300070BD0020FCE7C046C80900200000000088
+:1011400070B572B6094C26686568AB1B98420AD820
+:101150002B1A48421840821A964204D8954202D36C
+:10116000626062B670BD0020FBE7C046C80900207F
+:101170000548064B10B503600023436083600C30C4
+:10118000FFF7AEFF10BDC046D00900204111000896
+:1011900010B50024046041608260C36010BDC04689
+:1011A00010B50022FFF7B4FF10BDC04600000000DC
+:1011B00070B5134C2000FFF793FF23002000114D62
+:1011C0001033236104222B0014211430FFF7E0FFB9
+:1011D000230024336362230020002833A362042207
+:1011E0002B001C212C30FFF7D3FF23003C33E3639B
+:1011F00023004033236423004433636470BDC0463E
+:10120000EC090020A111000800000000000000000F
+:10121000EFF30983203383F3098862B67047C04631
+:10122000024A0349136A0B401362704700ED00E065
+:10123000FFFF00FF000000000000000000000000B1
+:1012400010B50F3000D110BD72B6EFF30984203C09
+:1012500084F3098880235B04E361FFF7D9FD00284C
+:1012600002D0034BA361EEE7024BA361EBE7C0465C
+:10127000C7010008CA0100080000000000000000CB
+:1012800070B58025084B094ADB68094CDA61ED012D
+:101290000848A561FFF7FCFE8023DB050548A36134
+:1012A000FFF7F6FEF4E7C04630090020C01400083E
+:1012B0000004004888130000000000000000000047
+:1012C000F0B5DE4657464E464546E0B589B0FFF7D5
+:1012D00067F8FFF72DFC8021484C01222000C9014E
+:1012E000FFF78EF98021200001220902FFF788F91B
+:1012F0008023DB05A36180230022A8211B06A361B4
+:1013000049003F4B00923F488032FFF761FE2000CA
+:1013100040213D4AFFF774F9200080213A4AFFF747
+:101320006FF93A4B3A4918009846FFF769F80023DD
+:101330009A46384B00240493374B00260593374BCD
+:1013400007AD9B46FA235B00994606AF0BE080226F
+:101350002A4B12069A6100239A46FA200134800033
+:10136000E4B2FFF795FE4B462E80AC702A00029344
+:1013700050210323404601960096FFF751F80028BC
+:1013800031D16420FFF784FE4B46029301232A00EB
+:1013900001935021404600970133FFF741F80028A0
+:1013A00016D13B78A34209D08022144BD2019A6116
+:1013B0008022120258469A61FFF7DAFB5346002B4F
+:1013C000C5D180220D4B12029A6101239A46C4E7CF
+:1013D00080220A4BD2019A618022120205989A61FA
+:1013E000FFF7C6FBDDE78022044BD2019A61802221
+:1013F000120204989A61FFF7BBFBC2E700040048A1
+:1014000081120008380A00208E060000080800201B
+:101410008C150008CC140008E4140008FC14000823
+:101420009C150008880B0020880B0020880B0020EA
+:101430009C150008000000000000000000000000F3
+:101440009C150008000000000000000000000000E3
+:101450009C150008000000000000000000000000D3
+:101460009C150008000000000000000000000000C3
+:101470009C150008000000000000000000000000B3
+:101480009C150008000000000000000000000000A3
+:101490009C15000800000000000000000000000093
+:1014A000493243206661696C7572650063300000E3
+:1014B00069646C65000000006E6F6E616D65000010
+:1014C0006C65642D626C696E6B00000049324331BB
+:1014D00020454550524F4D20777269746520464132
+:1014E000494C00004932433120454550524F4D2070
+:1014F00072656164204641494C0000004932433125
+:1015000020454550524F4D2064617461206D6973D0
+:101510006D61746368000000AC14000800000000F6
+:10152000000000005008002030090020D40C000802
+:10153000B20C0008B20C0008D80C0008AA0C000875
+:10154000A20C0008B20C0008AA0C0008B20C00089B
+:10155000B20C0008B20C0008B20C0008AA0C00087B
+:101560006D61696E002C063804044C080C10141CC4
+:1015700020242526000000000000240401000400AF
+:1015800000000000000C00102830340001000000B2
+:08159000A0860100010000002B
+:0415980000A24A045F
+:04000005080001915D
+:00000001FF
diff --git a/testhal/SK32/IIC/main.c b/testhal/SK32/IIC/main.c
new file mode 100644
index 0000000000..ffa2323f8f
--- /dev/null
+++ b/testhal/SK32/IIC/main.c
@@ -0,0 +1,174 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+#include "ch.h"
+#include "hal.h"
+
+/*===========================================================================*/
+/* I2C1 EEPROM test on the GENERIC_SK32_F077 board. */
+/*===========================================================================*/
+
+/*
+ * Test resources used by this test:
+ *
+ * - PB6 (I2C1_SCL) and PB7 (I2C1_SDA), both on alternate function 13 in
+ * open drain mode. A 24Cxx/M24C64 style I2C EEPROM (hardware address
+ * 0xA0, A0/A1/A2 strapped low) with 4.7k pull-up resistors to VCC must
+ * be wired to the two pads, the same connection used by the vendor
+ * I2C_EEPROM example.
+ * - PB14 / PB15: two board LEDs wired to push-pull outputs, the PAL
+ * abstraction drives them high (PAL_HIGH) to switch the LEDs on.
+ *
+ * Test procedure:
+ * - The I2C1 master is started at 100kHz standard mode (the SK32 I2C
+ * input clock is analyzed as the 8MHz HSI by the low level driver).
+ * - Every round a byte (a rotating pattern) is written to the EEPROM
+ * address 0x0000 with a byte write, the internal write cycle is awaited
+ * and the byte is read back with a combined random read
+ * (write the two address bytes, repeated START, read one byte).
+ * - PB15 is toggled after every round whose readback matches (visible
+ * blinking) while PB14 provides a 1 Hz heartbeat.
+ * - On the first mismatch or transfer error (e.g. no EEPROM wired -> ACK
+ * failure) both LEDs are switched on solid and the system halts.
+ */
+
+/* EEPROM slave address, 7 bits (0xA0 with the R/W bit => 0x50). */
+#define EEPROM_ADDR 0x50U
+
+/* Blinker thread, times are in milliseconds. */
+static THD_WORKING_AREA(waBlinkThread, 128);
+static THD_FUNCTION(BlinkThread, arg) {
+ (void)arg;
+ chRegSetThreadName("led-blink");
+ while (true) {
+ palSetPad(GPIOB, GPIOB_PIN14);
+ chThdSleepMilliseconds(500);
+
+ palClearPad(GPIOB, GPIOB_PIN14);
+ chThdSleepMilliseconds(500);
+ }
+}
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+ static const I2CConfig i2c1_cfg = {
+ .op_mode = OPMODE_I2C,
+ .clock_speed = 100000U,
+ .duty_cycle = STD_DUTY_CYCLE
+ };
+ uint8_t txbuf[3]; /* Address high, address low, data byte. */
+ uint8_t rxbuf[1];
+ uint8_t pattern = 0x00U;
+ bool led = false;
+ msg_t msg;
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ /* Configuring the PB14/PB15 LED lines as push-pull outputs, initially off. */
+ palSetPadMode(GPIOB, GPIOB_PIN14, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, GPIOB_PIN15, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palClearPad(GPIOB, GPIOB_PIN15);
+
+ /*
+ * Creating the LED blinker thread.
+ */
+ chThdCreateStatic(waBlinkThread, sizeof(waBlinkThread), NORMALPRIO,
+ BlinkThread, NULL);
+
+ /*
+ * I2C1 pads setup on alternate function 13 (open drain, floating, the
+ * pull-ups are external 4.7k resistors on the EEPROM module):
+ * - PB6 = I2C1_SCL.
+ * - PB7 = I2C1_SDA.
+ */
+ palSetPadMode(GPIOB, 6U, PAL_SK32_MODE_ALTERNATE |
+ PAL_SK32_ALTERNATE(13) |
+ PAL_SK32_OTYPE_OPENDRAIN |
+ PAL_SK32_OSPEED_MID |
+ PAL_SK32_PUPDR_FLOATING);
+ palSetPadMode(GPIOB, 7U, PAL_SK32_MODE_ALTERNATE |
+ PAL_SK32_ALTERNATE(13) |
+ PAL_SK32_OTYPE_OPENDRAIN |
+ PAL_SK32_OSPEED_MID |
+ PAL_SK32_PUPDR_FLOATING);
+
+ /* Starting the I2C driver (enables the I2C1 clock and the NVIC vector,
+ then programs CR2/CCR/CR1 in master mode).*/
+ i2cStart(&I2CD1, &i2c1_cfg);
+
+ /*
+ * Running the EEPROM write/read test forever.
+ */
+ while (true) {
+ /* Byte write: [addr high][addr low][data] at address 0x0000.*/
+ txbuf[0] = 0x00U;
+ txbuf[1] = 0x00U;
+ txbuf[2] = pattern;
+ msg = i2cMasterTransmitTimeout(&I2CD1, EEPROM_ADDR, txbuf,
+ sizeof(txbuf), NULL, 0U,
+ TIME_MS2I(50));
+ if (msg != MSG_OK) {
+ palSetPad(GPIOB, GPIOB_PIN14);
+ palSetPad(GPIOB, GPIOB_PIN15);
+ chSysHalt("I2C1 EEPROM write FAIL");
+ }
+
+ /* Waiting for the internal write cycle (tWR <= 5ms typical).*/
+ chThdSleepMilliseconds(10);
+
+ /* Random read: write the two address bytes then read one byte back.*/
+ msg = i2cMasterTransmitTimeout(&I2CD1, EEPROM_ADDR, txbuf, 2U,
+ rxbuf, sizeof(rxbuf),
+ TIME_MS2I(50));
+ if (msg != MSG_OK) {
+ palSetPad(GPIOB, GPIOB_PIN14);
+ palSetPad(GPIOB, GPIOB_PIN15);
+ chSysHalt("I2C1 EEPROM read FAIL");
+ }
+
+ /* Verifying the readback byte.*/
+ if (rxbuf[0] != pattern) {
+ palSetPad(GPIOB, GPIOB_PIN14);
+ palSetPad(GPIOB, GPIOB_PIN15);
+ chSysHalt("I2C1 EEPROM data mismatch");
+ }
+
+ /* Signaling a successful round with a slow PB15 blink.*/
+ if (led) {
+ palClearPad(GPIOB, GPIOB_PIN15);
+ led = false;
+ }
+ else {
+ palSetPad(GPIOB, GPIOB_PIN15);
+ led = true;
+ }
+
+ pattern++;
+ chThdSleepMilliseconds(100);
+ }
+}
diff --git a/testhal/SK32/KBCU/Makefile b/testhal/SK32/KBCU/Makefile
new file mode 100644
index 0000000000..5d52c59bde
--- /dev/null
+++ b/testhal/SK32/KBCU/Makefile
@@ -0,0 +1,193 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = kbcu
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# The KBCU driver uses a single NVIC vector whose priority is selected through
+# the SK32_IRQ_KBCU_PRIORITY constant (default in hal_kbcu_lld.h = 3); it is
+# repeated here for clarity.
+UDEFS += -DSK32_IRQ_KBCU_PRIORITY=3
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
\ No newline at end of file
diff --git a/testhal/SK32/KBCU/cfg/chconf.h b/testhal/SK32/KBCU/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/testhal/SK32/KBCU/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/KBCU/cfg/halconf.h b/testhal/SK32/KBCU/cfg/halconf.h
new file mode 100644
index 0000000000..366349cbdf
--- /dev/null
+++ b/testhal/SK32/KBCU/cfg/halconf.h
@@ -0,0 +1,593 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB FALSE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SK32 KBCU driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SK32 KBCU subsystem.
+ * @note This switch is not part of the generic ChibiOS halconf template:
+ * the KBCU (Keyboard Control Unit) is a 3Think peripheral used for
+ * keyboard matrix scanning (independent mode) and for driving the
+ * RGB LED columns. The platform header (hal_lld.h) derives the
+ * SK32_DMA_REQUIRED macro from it so that the DMA1 helper code and
+ * the DMA1 initialization are included in the build (the LED stream
+ * is pushed through DMA1 channel 4).
+ */
+#if !defined(HAL_USE_KBCU) || defined(__DOXYGEN__)
+#define HAL_USE_KBCU TRUE
+#endif
+
+/*
+ * The SLED peripheral is not used by this test.
+ */
+#if !defined(HAL_USE_SLED) || defined(__DOXYGEN__)
+#define HAL_USE_SLED FALSE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/KBCU/cfg/mcuconf.h b/testhal/SK32/KBCU/cfg/mcuconf.h
new file mode 100644
index 0000000000..8501ea2ce3
--- /dev/null
+++ b/testhal/SK32/KBCU/cfg/mcuconf.h
@@ -0,0 +1,86 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for the
+ * USB IP, but the SK32 port provides native drivers for the entire core
+ * platform and for every peripheral, so no shared STM32 low level driver
+ * (TIMv1, USARTv2, ADCv1, SPIv2, I2Cv2, ..., nor the CANv1/DACv1/DMAv1/
+ * RTCv2/xWDGv1 stubs) is linked in. This mcuconf.h only needs to satisfy
+ * the single SK32 naming scheme:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST/peripheral settings consumed by
+ * the native SK32 HAL low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches. Not used by this test: HAL_USE_SERIAL
+ * is disabled in halconf.h, the switches are kept at FALSE.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 FALSE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ * The SK32 native interrupt-driven SPI driver is selected through the
+ * SK32_SPI_USE_SPIx switches. Not used by this test: HAL_USE_SPI is
+ * disabled in halconf.h, the switches are kept at FALSE.
+ */
+#define SK32_SPI_USE_SPI1 FALSE
+#define SK32_SPI_USE_SPI2 FALSE
+#define SK32_SPI_SPI1_PRIORITY 3
+#define SK32_SPI_SPI2_PRIORITY 3
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+#endif /* MCUCONF_H */
\ No newline at end of file
diff --git a/testhal/SK32/KBCU/main.c b/testhal/SK32/KBCU/main.c
new file mode 100644
index 0000000000..17b0bf290c
--- /dev/null
+++ b/testhal/SK32/KBCU/main.c
@@ -0,0 +1,165 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+#include "ch.h"
+#include "hal.h"
+#include "hal_kbcu_lld.h"
+
+/*===========================================================================*/
+/* KBCU independent-mode bring-up test on the GENERIC_SK32_F077 board. */
+/*===========================================================================*/
+
+/*
+ * Hardware description.
+ *
+ * The KBCU peripheral is driven in independent scan mode: the RGB (LED)
+ * scanning and the KEY scanning run at the same level and the key statuses
+ * are read back on dedicated KEY inputs through the shared KSR registers.
+ * The RGB frame is streamed into the LED registers by DMA1 channel 4
+ * (memory to peripheral, targeting KBCU->DMAR).
+ *
+ * Columns (COL) are the scan outputs, Keys (KEY) are the row inputs:
+ * COL0 = PC7 (alternate function 7), COL1 = PC8 (AF7),
+ * COL4 = PC4 (AF8), COL5 = PC5 (AF8) (not used here,
+ * kept for reference)
+ * KEY0 = PD3 (AF9), KEY1 = PB1 (AF8),
+ * KEY2 = PB2 (AF8)
+ *
+ * Status LEDs on the board:
+ * PB14 stays SOLID HIGH if the KBCU start sequence fails to complete,
+ * PB15 toggles once per detected key press (jumper test described below).
+ *
+ * How to exercise it:
+ * - RGB: connect a scope to the COL pins (e.g. PC7) and observe the scan
+ * burst; wire RGB LEDs on the columns to see the streamed frame colour.
+ * - Key: short COL0 (PC7) to KEY0 (PD3) with a jumper to simulate a pressed
+ * key at (column 0, row 0). The KBCU key interrupt fires, the driver
+ * snapshot callback runs and PB15 toggles.
+ */
+
+/*===========================================================================*/
+/* Application entry point. */
+/*===========================================================================*/
+
+/**
+ * @brief Key status callback, invoked from the KBCU ISR on a key change.
+ * @note Only the first KSR word is inspected and asserted: the test wires
+ * a single key at (column 0, row 0).
+ */
+static void key_isr(const uint32_t *ksr) {
+ (void)ksr;
+}
+
+int main(void) {
+
+ /* Independent scan mode configuration. The field values mirror the vendor
+ RGBKeyboardSTK demo: key/columns effective level LOW, clock divided by
+ "1" (PSC = 1), no dead time, no flicker, no column mask. columns_cycle_
+ value (CCR) drives the LED brightness duty, columns_cycle_count (ARR) the
+ scan frequency.*/
+ static const KBCUConfig kbcu_cfg = {
+ .columns_num = 16U, /* 16 columns */
+ .columns_cycle_value = 0x3FU, /* CCR 0x00..0x7F duty */
+ .columns_cycle_count = 0xFFU, /* ARR 0x01..0xFF */
+ .clock_division = 1U, /* PSC */
+ .key_effective_level = KBCU_KEY_LEVEL_LOW,
+ .columns_effective_level = KBCU_COLUMN_LEVEL_LOW,
+ .dead_time = KBCU_DEADTIME_0,
+ .flicker_length = 0U,
+ .column_mask = 0U
+ };
+
+ /* RGB frame: 8 pixels x 3 bytes (R,G,B), pushed via DMA1 channel 4. The
+ burst length (DCR.DBL) must stay within 0x00..0x1F so it is capped at
+ the frame size here (24 <= 31).*/
+ static uint8_t frame[8U * 3U];
+ enum {
+ FRAME_BYTES = sizeof(frame),
+ BURST_LEN = FRAME_BYTES
+ };
+
+ volatile uint32_t key_events = 0U;
+ uint8_t phase = 0U;
+ unsigned i;
+
+ halInit();
+ chSysInit();
+
+ /* PB14/PB15 status LEDs as push-pull outputs, initially off.*/
+ palSetPadMode(GPIOB, 14U, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, 15U, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, 14U);
+ palClearPad(GPIOB, 15U);
+
+ /*
+ * Column/KEY pads in alternate function (the KBCU network is wired by the
+ * hardware scan engine, the driver does not touch these pins).
+ */
+ palSetPadMode(GPIOB, 1U, PAL_MODE_ALTERNATE(8) | PAL_SK32_OSPEED_HIGHEST); /* KEY1 */
+ palSetPadMode(GPIOB, 2U, PAL_MODE_ALTERNATE(8) | PAL_SK32_OSPEED_HIGHEST); /* KEY2 */
+ palSetPadMode(GPIOD, 3U, PAL_MODE_ALTERNATE(9) | PAL_SK32_OSPEED_HIGHEST); /* KEY0 */
+ palSetPadMode(GPIOC, 7U, PAL_MODE_ALTERNATE(7) | PAL_SK32_OSPEED_HIGHEST); /* COL0 */
+ palSetPadMode(GPIOC, 8U, PAL_MODE_ALTERNATE(7) | PAL_SK32_OSPEED_HIGHEST); /* COL1 */
+ palSetPadMode(GPIOC, 4U, PAL_MODE_ALTERNATE(8) | PAL_SK32_OSPEED_HIGHEST); /* COL4 */
+ palSetPadMode(GPIOC, 5U, PAL_MODE_ALTERNATE(8) | PAL_SK32_OSPEED_HIGHEST); /* COL5 */
+
+ /* Starting the KBCU peripheral in independent scan mode.*/
+ kbcu_lld_init();
+ kbcu_lld_start(&kbcu_cfg);
+
+ /* Key interrupt only (no frame/column interrupt), frame count 1. Install
+ the snapshot callback.*/
+ kbcu_lld_itconfig(KBCU_IT_KEY, 1U);
+ kbcu_lld_set_callback(key_isr);
+
+ /*
+ * RGB/DMA loop: render a running rainbow into the frame buffer and push it
+ * to the KBCU LED registers through DMA1 channel 4 every 30 ms.
+ * - PB14 toggles each iteration: heartbeat proving the loop is alive.
+ * - PB15 reflects the KBCU key activity: when the tester shorts COL0 (PC7)
+ * to KEY0 (PD3) the key interrupt fires and PB15 latches HIGH.
+ */
+ while (true) {
+ /* Heartbeat: the loop is responsive.*/
+ palTogglePad(GPIOB, 14U);
+
+ /* Render a rainbow gradient, rotated by the phase counter.*/
+ for (i = 0U; i < 8U; i++) {
+ uint8_t *px = &frame[i * 3U];
+ px[0] = (uint8_t)((i * 32U + phase) & 0xFFU); /* R */
+ px[1] = (uint8_t)((i * 32U + phase * 2U) & 0xFFU); /* G */
+ px[2] = (uint8_t)((i * 32U + phase * 3U) & 0xFFU); /* B */
+ }
+
+ /* Push the frame to the LED registers by DMA (single-shot burst).*/
+ kbcu_lld_led_dma_start(KBCU_DMBASE_LEDR0, BURST_LEN, frame, FRAME_BYTES);
+
+ /* If the KBCU reported a key status change at least once, the key input
+ (KEY0 = PD3) has been toggled; latch PB15. The flag is a high-to-low
+ interrupt latch so it is cleared here after sampling.*/
+ if (kbcu_lld_get_key_flag() != 0U) {
+ key_events++;
+ palSetPad(GPIOB, 15U);
+ KBCU->SR = (KBCU->SR & ~KBCU_SR_KEYF);
+ }
+
+ /* Advance the phase and wait for the next frame slot.*/
+ phase = (uint8_t)(phase + 1U);
+
+ chThdSleepMilliseconds(30);
+ }
+}
\ No newline at end of file
diff --git a/testhal/SK32/SERIAL/Makefile b/testhal/SK32/SERIAL/Makefile
new file mode 100644
index 0000000000..6bc8ced4ad
--- /dev/null
+++ b/testhal/SK32/SERIAL/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = serial
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/testhal/SK32/SERIAL/build.log b/testhal/SK32/SERIAL/build.log
new file mode 100644
index 0000000000..b46712a1b2
--- /dev/null
+++ b/testhal/SK32/SERIAL/build.log
@@ -0,0 +1,71 @@
+Compiling crt0_v6m.S
+Compiling vectors.S
+Compiling chcoreasm.S
+Compiling crt1.c
+Compiling oop_base_object.c
+Compiling hal.c
+Compiling hal_safety.c
+Compiling hal_st.c
+Compiling hal_buffered_serial.c
+Compiling hal_buffers.c
+Compiling hal_queues.c
+Compiling hal_mmcsd.c
+Compiling hal_pal.c
+Compiling hal_serial.c
+Compiling hal_usb.c
+Compiling hal_flash.c
+Compiling nvic.c
+Compiling sk32_isr.c
+Compiling hal_lld.c
+Compiling hal_efl_lld.c
+Compiling hal_st_lld.c
+Compiling hal_pal_lld.c
+Compiling hal_serial_lld.c
+Compiling sk32_dma.c
+Compiling hal_sled_lld.c
+In file included from ../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:49:
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.h:65:6: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 65 | #if (HAL_USE_SLED == TRUE) || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:51:5: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 51 | #if HAL_USE_SLED || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+Compiling stm32_dma.c
+Compiling hal_usb_lld.c
+Compiling board.c
+Compiling osal.c
+Compiling chsys.c
+Compiling chrfcu.c
+Compiling chdebug.c
+Compiling chtrace.c
+Compiling chvt.c
+Compiling chschd.c
+Compiling chinstances.c
+Compiling chthreads.c
+Compiling chregistry.c
+Compiling chsem.c
+Compiling chmtx.c
+Compiling chcond.c
+Compiling chevents.c
+Compiling chmsg.c
+Compiling chdynamic.c
+Compiling chmboxes.c
+Compiling chmemcore.c
+Compiling chmemheaps.c
+Compiling chmempools.c
+Compiling chpipes.c
+Compiling chobjcaches.c
+Compiling chdelegates.c
+Compiling chfactory.c
+Compiling chcore.c
+Compiling main.c
+Linking build/serial.elf
+Creating build/serial.hex
+Creating build/serial.bin
+Creating build/serial.dmp
+
+ text data bss dec hex filename
+ 10228 4 8184 18416 47f0 build/serial.elf
+Creating build/serial.list
+
+Done
diff --git a/testhal/SK32/SERIAL/cfg/chconf.h b/testhal/SK32/SERIAL/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/testhal/SK32/SERIAL/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/SERIAL/cfg/halconf.h b/testhal/SK32/SERIAL/cfg/halconf.h
new file mode 100644
index 0000000000..cb428219fc
--- /dev/null
+++ b/testhal/SK32/SERIAL/cfg/halconf.h
@@ -0,0 +1,568 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL TRUE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB TRUE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/SERIAL/cfg/mcuconf.h b/testhal/SK32/SERIAL/cfg/mcuconf.h
new file mode 100644
index 0000000000..c3f0d63ee3
--- /dev/null
+++ b/testhal/SK32/SERIAL/cfg/mcuconf.h
@@ -0,0 +1,78 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for the
+ * USB IP, but the SK32 port provides native drivers for the entire core
+ * platform and for every peripheral, so no shared STM32 low level driver
+ * (TIMv1, USARTv2, ADCv1, SPIv2, I2Cv2, ..., nor the CANv1/DACv1/DMAv1/
+ * RTCv2/xWDGv1 stubs) is linked in. This mcuconf.h only needs to satisfy
+ * the single SK32 naming scheme:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST/peripheral settings consumed by
+ * the native SK32 HAL low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches.
+ *
+ * SERIAL self-loopback test:
+ * - USART1 is used on PB5 (USART1_TX, alternate 10) and PB6 (USART1_RX,
+ * alternate 10), the two pads are shorted on the board.
+ */
+#define SK32_SERIAL_USE_USART1 TRUE
+#define SK32_SERIAL_USE_USART2 FALSE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+#endif /* MCUCONF_H */
\ No newline at end of file
diff --git a/testhal/SK32/SERIAL/main.c b/testhal/SK32/SERIAL/main.c
new file mode 100644
index 0000000000..c62b48840e
--- /dev/null
+++ b/testhal/SK32/SERIAL/main.c
@@ -0,0 +1,153 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+#include "ch.h"
+#include "hal.h"
+
+/*===========================================================================*/
+/* USART1 self-loopback test on the GENERIC_SK32_F077 board. */
+/*===========================================================================*/
+
+/*
+ * Test resources used by this test:
+ *
+ * - PB5 (USART1_TX) and PB6 (USART1_RX), both on alternate function 10.
+ * The two pads are shorted on the board so every transmitted byte is
+ * received back on the RX line (self-loopback).
+ * - PB14 / PB15: two board LEDs wired to push-pull outputs, the PAL
+ * abstraction drives them high (PAL_HIGH) to switch the LEDs on.
+ *
+ * Test procedure:
+ * - 256 bytes (0x00 .. 0xFF) are sent one at a time over SD1 (USART1,
+ * 115200 8N1) and each one is waited for on the input queue with a
+ * timeout; the received byte must equal the transmitted one.
+ * - PB14 blinks at 1 Hz while the test is running and keeps blinking
+ * forever after a full pass.
+ * - On the first failure both LEDs are switched on solid and the system
+ * halts (the failed byte number is reported through the debug channel
+ * when a debugger is attached).
+ */
+
+/* Blinker thread, times are in milliseconds. */
+static THD_WORKING_AREA(waBlinkThread, 128);
+static THD_FUNCTION(BlinkThread, arg) {
+ (void)arg;
+ chRegSetThreadName("led-blink");
+ while (true) {
+ palSetPad(GPIOB, GPIOB_PIN14);
+ chThdSleepMilliseconds(500);
+
+ palClearPad(GPIOB, GPIOB_PIN14);
+ chThdSleepMilliseconds(500);
+ }
+}
+
+/* Marks a failed round trip and halts the system. */
+static void fail(uint16_t round, uint8_t tx, uint8_t rx) {
+ palSetPad(GPIOB, GPIOB_PIN14);
+ palSetPad(GPIOB, GPIOB_PIN15);
+ chSysHalt("USART1 loopback FAIL");
+ (void)round;
+ (void)tx;
+ (void)rx;
+}
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+
+ static const SerialConfig usart1_cfg = {
+ .speed = 115200,
+ .cr1 = 0, /* 8 data bits, no parity, no OVER8. */
+ .cr2 = USART_CR2_STOP1_BITS, /* One stop bit. */
+ .cr3 = 0
+ };
+ unsigned round;
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ /* Configuring the PB14/PB15 LED lines as push-pull outputs, initially off. */
+ palSetPadMode(GPIOB, GPIOB_PIN14, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, GPIOB_PIN15, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palClearPad(GPIOB, GPIOB_PIN15);
+
+ /*
+ * Creating the LED blinker thread.
+ */
+ chThdCreateStatic(waBlinkThread, sizeof(waBlinkThread), NORMALPRIO,
+ BlinkThread, NULL);
+
+ /*
+ * USART1 pads setup on alternate function 10:
+ * - PB5 = USART1_TX, push-pull output.
+ * - PB6 = USART1_RX, input line connected to the peripheral through the
+ * alternate function (floating, the line is driven by PB5 through the
+ * loopback wire).
+ */
+ palSetPadMode(GPIOB, 5U, PAL_MODE_ALTERNATE(10) |
+ PAL_SK32_OSPEED_MID);
+ palSetPadMode(GPIOB, 6U, PAL_MODE_ALTERNATE(10) |
+ PAL_SK32_OSPEED_MID);
+
+ /* Starting the serial driver (enables the USART1 clock and the NVIC
+ vector, then programs BRR/CR1/CR2/CR3). */
+ sdStart(&SD1, &usart1_cfg);
+
+ /*
+ * Running the loopback test.
+ */
+ for (round = 0; round < 256; round++) {
+ uint8_t tx = (uint8_t)round;
+ uint8_t rx = 0;
+
+ /* Sending the byte, blocking with a timeout.*/
+ if (sdWriteTimeout(&SD1, &tx, 1U, TIME_MS2I(100)) != 1U) {
+ fail(round, tx, rx);
+ }
+
+ /* Waiting for the echoed byte, the RXNE interrupt pushes it into the
+ input queue. The timeout catches wiring/driver faults.*/
+ if (sdReadTimeout(&SD1, &rx, 1U, TIME_MS2I(500)) != 1U) {
+ fail(round, tx, rx);
+ }
+
+ /* The received byte must match the transmitted one.*/
+ if (rx != tx) {
+ fail(round, tx, rx);
+ }
+ }
+
+ /* Full pass: signal it by switching PB15 on (PB14 keeps blinking).*/
+ palSetPad(GPIOB, GPIOB_PIN15);
+
+ /*
+ * Normal main() thread activity.
+ */
+ while (true) {
+ chThdSleepMilliseconds(1000);
+ }
+}
diff --git a/testhal/SK32/SERIAL/serial.hex b/testhal/SK32/SERIAL/serial.hex
new file mode 100644
index 0000000000..003160cfc1
--- /dev/null
+++ b/testhal/SK32/SERIAL/serial.hex
@@ -0,0 +1,644 @@
+:020000040800F2
+:100000000004002091010008D1230008930100089A
+:100010009301000893010008930100089301000870
+:100020009301000893010008930100089301000860
+:10003000930100089301000893010008810F000854
+:100040009301000893010008930100089301000840
+:100050009301000893010008930100089301000830
+:100060009301000893010008930100089301000820
+:100070009301000893010008930100089301000810
+:100080009301000893010008930100089301000800
+:1000900093010008930100089301000893010008F0
+:1000A000930100089301000893010008F112000871
+:1000B00093010008930100089301000871150008DE
+:1000C00072B6244880F30888234880F30988022008
+:1000D00080F31488BFF36F8F00F06AF901F048FDD8
+:1000E0001E481F491B4A914202DA08600431FAE7B0
+:1000F0001C49194A914202DA08600431FAE71A49A8
+:100100001A4A1B4B9A4204DA086810600431043220
+:10011000F8E700201749184A914202DA08600431D2
+:10012000FAE700F05DF900F04BF9144C144DAC42C5
+:1001300003DA216888470434F9E702F0A1F9114C89
+:10014000114DAC4203DA216888470434F9E70F49BE
+:1001500008470000000400200008002055555555B0
+:100160000000002000040020F42700080008002000
+:100170000408002008080020700C0020C0000008BF
+:10018000C0000008C0000008C0000008D10300083B
+:1001900096E700F000F8FEE7F0B544464D465646B7
+:1001A0005F46F0B46B46CB60C3689D46F0BCA0468A
+:1001B000A946B246BB46F0BD62B6281CA047002047
+:1001C00001F0CEFFFEE701F0C3FE024A024B1360CE
+:1001D000FEE7C04604ED00E000000080002934D0B6
+:1001E0000123002210B488422CD301242407A14209
+:1001F00004D2814202D209011B01F8E7E400A142C6
+:1002000004D2814202D249005B00F8E7884201D360
+:10021000401A1A434C08A04202D3001B5C08224338
+:100220008C08A04202D3001B9C082243CC08A042A9
+:1002300002D3001BDC082243002803D01B0901D095
+:100240000909E3E7100010BC704701B5002000F079
+:100250000BF802BD0029F8D003B5FFF7C1FF0EBCB3
+:100260004243891A1847C0467047C046F0B505009A
+:10027000830749D0541E002A45D003000326CAB282
+:1002800002E00135013C3ED301332A703342F8D1FC
+:10029000032C30D9FF220A401502AD182A04AD18EC
+:1002A0000F2C34D92700103F3F093E01B4461E00F1
+:1002B0001A0010366644156055609560D56010329E
+:1002C0009642F8D101373F01DB190F270C22274056
+:1002D00022403C00002A0ED03E1FB608B400A446BF
+:1002E0001A001C1D644420C2A242FCD10324013622
+:1002F000B6009B193C40002C05D0C9B21C191970DE
+:1003000001339C42FBD1F0BD03001400C0E727007D
+:10031000E2E7C046F0B505000F2A33D903240300F5
+:1003200026000B431E401C4237D11500103D2D09FD
+:1003300003000C00AC462F013F1865685D60A5689E
+:100340009D60E568DD60256810341D601D00103378
+:10035000BD42F2D165460F230C2401352D01491908
+:100360001340451914421AD08C59AC5104369C1BC9
+:10037000032CF9D8043B9B0801339B00ED18C918E6
+:1003800003231A40561E002A06D00023CC5C1A0014
+:10039000EC5401339642F9D1F0BD561EF5E71A0030
+:1003A000F0E7C04600000000000000000000000070
+:1003B0007047C04600000000000000000000000080
+:1003C0007047C04600000000000000000000000070
+:1003D000FEE7C04600000000000000000000000032
+:1003E00070B5114C250080356068A2682168904284
+:1003F0000AD2030040C940C39342FBD3013A131A07
+:100400009B0801339B00C018E368834207D9013B76
+:100410001A1A9208013200219200FFF727FF1034C8
+:10042000AC42E1D170BDC046B42500080000000018
+:1004300010B500F045FD00F0CBFD00F0D9F900F05B
+:1004400067FA01F0FDFB00F003F810BD00000000AA
+:1004500010B500F0A5FD10BD000000000000000078
+:10046000F0B5C646836804000F00150000B593423E
+:1004700000D28568A1692669761AB5421BD30CD8CB
+:100480002A003800FFF746FFE268A36828005B1BDC
+:10049000A261A36080BCB846F0BD32003800FFF70F
+:1004A00039FFAB1B98461A00E168B819FFF732FF15
+:1004B000E2684244E9E72A003800FFF72BFFA2690F
+:1004C0005219E2E7000000000000000000000000F8
+:1004D000F0B5C646836804000F00150000B59342CE
+:1004E00000D2856860692669361AB5421BD30CD8DC
+:1004F0002A003900FFF70EFFE268A36828005B1BA3
+:100500006261A36080BCB846F0BD32003900FFF7DD
+:1005100001FFAB1B98461A00E068B919FFF7FAFE15
+:10052000E2684244E9E72A003900FFF7F3FE626916
+:100530005219E2E700000000000000000000000087
+:1005400010B50024C361029BC160816141618918BB
+:100550000060406084600161036210BD0000000023
+:100560004369826910B5934211D08268013282607A
+:100570005A1C42611970426903699A4204D20021EF
+:1005800001F036FE002010BDC3684361F7E78268C2
+:10059000002AEAD001204042F5E7C04600000000F2
+:1005A00070B505000C0072B605E02100280001F0CE
+:1005B00007FE002816DBAB68002BF6D0AB68AA69F3
+:1005C000013BAB60531CAB6114782A69934201D3A1
+:1005D000EB68AB61EB69002B01D02800984762B64D
+:1005E000200070BD62B6FCE70000000000000000C3
+:1005F000F0B5D6464F4646469A46C369C0B59146C1
+:1006000007000E00984672B6151E11D02A00310060
+:100610003800FFF725FF041E11D04346002B01D000
+:100620003800C04762B62D1B361972B6002DEDD1C9
+:10063000484662B6E0BCBA46B146A846F0BD51464F
+:10064000380001F0BDFD0028E0D04B46581BF0E714
+:10065000C361009BC1608161416189180060406095
+:10066000826001610362704700000000000000002A
+:1006700070B504000E00150072B605E029002000D8
+:1006800001F09EFD002817DBA368002BF6D0A368BD
+:10069000013BA36063695A1C62611E706269236931
+:1006A0009A4201D3E3686361E369002B01D0200023
+:1006B000984762B6002070BD62B6FCE700000000FB
+:1006C0008369426910B59A4210D082680132826013
+:1006D0005A1C82611C7803699A4204D2002101F0FD
+:1006E00087FD200010BDC3688361F7E78268002A98
+:1006F000EBD001204042F5E70000000000000000C0
+:10070000F0B5D6464F4646469A46C369C0B59146AF
+:1007100007000E00984672B6151E11D02A0031004F
+:100720003800FFF7D5FE041E11D04346002B01D040
+:100730003800C04762B62D1B361972B6002DEDD1B8
+:10074000484662B6E0BCBA46B146A846F0BD51463E
+:10075000380001F035FD0028E0D04B46581BF0E78B
+:1007600010B50C30FFF744FF10BDC046000000007C
+:10077000012310B50C305B42FFF73AFF10BDC046B5
+:1007800010B53030FFF7BCFF10BDC04600000000C0
+:10079000012310B530305B42FFF7B2FF10BDC046F9
+:1007A00010B50C30FFF7FCFE10BDC0460000000085
+:1007B000012110B50C304942FFF7F2FE10BDC046D2
+:1007C00010B53030FFF754FF10BDC04600000000E8
+:1007D000012210B530305242FFF74AFF10BDC0462B
+:1007E00001394B425941142049420840143870479E
+:1007F00010B500F085FC10BD0000000000000000F6
+:1008000030B5150002000B000C4983B002C2040091
+:10081000426001000122543102720C3000940F3208
+:10082000FFF78EFE210020002B00102264313030B3
+:100830000094FFF70DFF03B030BDC046342600081A
+:1008400010B5040072B600F073FC0223237262B686
+:10085000002010BD000000000000000000000000AB
+:10086000436970B504000D00002B07D0200029005B
+:100870000C30FFF775FE00280BDB70BD042104303F
+:1008800001F0CEFC200029000C30FFF769FE0028A3
+:10089000F3DA8021201D090101F0C2FCEDE7C0461A
+:1008A00010B50400002101F01BF90021200001F027
+:1008B000F7F863681B68002B02D0062120009847D8
+:1008C000A368044A1340A36000230622A367703381
+:1008D000E25410BDFEFFFEFF00000000000000001B
+:1008E000822310B50400C25C0833C25400F090FFAC
+:1008F00063681B68002B02D0012120009847032366
+:10090000237010BD00000000000000000000000087
+:1009100010B500F025FF10BD000000000000000031
+:1009200070B50123002504004560037040220021BA
+:100930003030FFF79BFCA56070BDC0460000000092
+:10094000022370B5002503708633C55004002422AD
+:100950000021856785600C30FFF788FC702320003C
+:10096000E55400F00DFF63681B68002B02D00021E6
+:100970002000984770BDC046000000000000000045
+:10098000037810B50400052B0BD08C22835405236B
+:10099000037043681B68002B01D004219847002393
+:1009A000A36010BD00000000000000000000000077
+:1009B000037810B5052B00D010BD8733C35C0370DE
+:1009C00043681B68002BF7D005219847F4E7C04621
+:1009D000702370B5C25C0400002A06D08268C14949
+:1009E0000A40826000228267C25422000021200057
+:1009F000803200F08DFF63689B68002B2DD02000B3
+:100A00009847002829D0A16F8623E25C2300863313
+:100A10005B781B0213438B4200D3A1E08022A367C3
+:100A2000A25C52B2002A4FDB002B00D18AE0702377
+:100A30001522E25472B601206389A26F03436381D9
+:100A4000E368616F9B6920001A60002299605A6018
+:100A5000002100F07DFF62B635E06022802516009F
+:100A6000605D064002422BD181237F22E35C02407D
+:100A70001B0213439C4A934200D1F6E03CD88022EB
+:100A80005200934200D1A0E000D96DE0012B00D1CB
+:100A9000E9E0022B00D1A4E022008832002B0FD124
+:100AA0000223A367002302216267E367ACE7902279
+:100AB0001201934200D1A5E08C4A934200D1D2E0CA
+:100AC0002000FFF7EDFE70BD002B27D07023092218
+:100AD000E25472B601202389A26F03432381E368A5
+:100AE000616F5B691A6000229960002120005A60E2
+:100AF00000F056FF62B6E6E780221201934200D171
+:100B0000C7E0D4D8A022D200934240D1784A002333
+:100B1000E2676367A36740B2002813DA70231422E8
+:100B2000E25472B6012263890021134300226381DB
+:100B3000E36820009B699A601A605A6000F008FF21
+:100B400062B6C0E770230B22E25472B601222389F9
+:100B5000134300222381E3685B699A601A60C4E74B
+:100B60008023E25C0B005CE781225200934262D05A
+:100B7000FF32FF329342A3D18223E35C012B9FD14A
+:100B800088220221A35A0B43A3520022BFE7594AED
+:100B900094466344012B00D992E78423E25C23004E
+:100BA00084335B7883211B02134382226068A25C3A
+:100BB0004568615C2000A847002800D180E74368B1
+:100BC00001686367A167E6671EE78223E35C012B88
+:100BD00000D075E788220221A35A8B43A35200223A
+:100BE00095E784230F21E25CE35620001140002B9F
+:100BF00057DB00F03DFE012857D0022800D05FE708
+:100C00003D4A4DE72378042B51D08223E35C002B2F
+:100C10000AD08B22A3540423237063681B68002B23
+:100C200002D002212000984700236367A367E3678F
+:100C30008033E05C6FE78223E35C002B00D03FE76A
+:100C40000F2211008433E35C19401A4206D05BB2D4
+:100C50002000002B5CDB00F05BFF605D002363671E
+:100C6000A367E36757E7254A1AE78223E35C002B73
+:100C700000D025E70F2211008433E35C19401A42AB
+:100C8000ECD05BB22000002B3EDB00F009FF605D82
+:100C9000E4E723008B33636701230121A367E66741
+:100CA000B2E600F00DFE0128A7D1154AF8E672B6AB
+:100CB0002000A368134A00211340A36020221030B3
+:100CC000FFF7D4FA200000F0B3FD62B68B23E654A0
+:100CD000883B237063681B68002B96D0032120009B
+:100CE000984792E7FEFFFEFF02030000020C00009F
+:100CF000E108000800FAFFFF102700081427000889
+:100D00000C2700080100010000F0EAFE605DA5E785
+:100D100000F016FF605DA1E7000000000000000089
+:100D2000702310B5C35C0400152B20D8244A9B0007
+:100D3000D3589F468623E25C230086335B78806F1E
+:100D40001B021343984223D370231422E25472B639
+:100D5000012263890021134300226381E36820009C
+:100D60009B699A601A605A6000F0F2FD62B610BD8D
+:100D7000FFF796FDFBE7C36F002B00D09847A368F1
+:100D8000104A1340A36070220023A367A354EEE728
+:100D9000E368198AFFF75EFA0029D5D172B60122FD
+:100DA0002389200013432381E3685B6999601960FC
+:100DB000596000F0F5FD62B670230A22E254D6E7CE
+:100DC0005C260008FEFFFEFF00000000000000009F
+:100DD0007023C35C10B50400152B05D8154A9B0081
+:100DE000D3589F46FFF75CFD10BD70230B22C25401
+:100DF00072B6012203890021134300220381C368D4
+:100E00005B699A601A605A6000F0CAFD62B6EBE74F
+:100E1000C3689B695B68002BE6D1C36F002B00D0D1
+:100E20009847A368044A1340A36070220023A36775
+:100E3000A354D9E7B4260008FEFFFEFF000000001F
+:100E40000F4A70B594460322FF25C0260240D20007
+:100E500095408901914083089B006344B6009C59EA
+:100E60001F22AC430C439C51012302409340C022FB
+:100E700040098000604452008350036070BDC0464A
+:100E800000E100E0000000000000000000000000A1
+:100E90000F2310B50340040003202040FF24074A1D
+:100EA000C0009446844089018140083B9B089B0018
+:100EB0006344DA69A2430A43DA6110BD00ED00E041
+:100EC000064B074A02219A62002249429A62D9607F
+:100ED0000131DA6019611A617047C04600100240A2
+:100EE000FFFF81FF00000000000000000000000084
+:100EF00001211D4A13680B43136001311368194225
+:100F0000FCD0032153688B435360174A0931080012
+:100F1000536818401942FAD1FD2311680B40136041
+:100F2000E4235B035060D06210635063D06253606F
+:100F3000802311685B040B43802113600A4A8904F3
+:100F400013680B42FCD01221084B1960536810390A
+:100F50000B4353600C2204494B681340082BFBD110
+:100F6000034B044A1A607047001002400020024000
+:100F70000008002000A24A04000000000000000059
+:100F800010B5744672B600F093FE62B6200001F010
+:100F900037FA10BD00000000000000000000000053
+:100FA000064B074A10B55A6000229A6007320221A8
+:100FB0000B201A60FFF76CFF10BDC04610E000E088
+:100FC0001F1C0000000000000000000000000000E6
+:100FD0007047C04600000000000000000000000054
+:100FE000F0B54E46032457464546DE46E0B5270099
+:100FF000530917405607D508520525401C40130FCA
+:10100000012285B0039391460023A4460297F60F70
+:10101000009717E05C4607682740009C274307605D
+:10102000072B52D85446076A3A40224302624908C5
+:1010300044D0624692009446009A76009200AD0039
+:10104000009201334A460A42F1D007274C46039AE0
+:101050001F40BF00BA409C4092468288A243324360
+:1010600092B28280846801945C00A04603244246C8
+:101070009440E24393462200A046019C9443220000
+:1010800044462A438260C268A24364462243C26047
+:101090000F22BA40029CD243022CBBD1072B1AD894
+:1010A000076A17405246174307625C4602684908C0
+:1010B0002240009C224302600029BAD105B0F0BC56
+:1010C000BB46B246A946A046F0BD5446476A3A40E0
+:1010D00022434262ABE7476A1740524617434762D2
+:1010E000E3E7C04600000000000000000000000030
+:1010F000C021024A93890B43938170470038014015
+:1011000010B5074C074A20000021FFF779FB064B7A
+:1011100003216367054B1B20A367FFF791FE10BDFA
+:1011200008080020F11000080038014000A24A041D
+:10113000F8B5CE46474606000B0080B500293AD0E8
+:101140004022DC881F891443A0469C881F4B0968F5
+:1011500023439946A0233F3A5B01480817431C40AC
+:10116000337A012B1CD0B36F756FC018FFF736F8B8
+:10117000434680B228812B824B46AF82AB8100234D
+:101180002B802B88AB88124B7C229C466444631EC8
+:101190009C41E4017F34B454C0BCB946B046F8BDAC
+:1011A0000C4B9E42DFD180220B4BD2019D692A431A
+:1011B0009A619B69D7E7054B962099469621402373
+:1011C000002401279846C0010902C9E72C2100002C
+:1011D00000FCFFFF08080020001002400000000093
+:1011E000F8B5CE464746456F06002B8880B59CB2C1
+:1011F000DB0552D472B62F23234234D0203B9846CD
+:101200000E3B9946202708E0A98889B227421FD1C2
+:101210002F222B889CB21A4225D043462342F3D07A
+:1012200049464A462140E3084942134039401B02DF
+:101230001943A30701D540231943630701D5802330
+:101240001943301D00F0ECFFA98889B22742DFD096
+:101250007C23F35C30001940FFF702FB2F222B8820
+:101260009CB21A42D9D162B68022AB8990469FB215
+:101270001A4201D022421AD1402398463B4207D05D
+:10128000234205D072B6726CB36C9A421AD062B621
+:10129000AF81C0BCB946B046F8BD72B680210430FB
+:1012A000C90000F0BDFF104B2B8062B6A2E772B6FA
+:1012B00030003030FFF704FA00280DDB80B2A88040
+:1012C00062B6D9E7B36B002BE1D01021301D00F0DE
+:1012D000A7FF43469F43DAE70821301D00F0A0FF37
+:1012E00043469F43ECE7C046FFFEFFFF00000000BF
+:1012F00010B574460348FFF773FF200001F080F833
+:1013000010BDC046080800200000000000000000DA
+:101310000100F0B5D64646464F468B1C57489A46C4
+:101320009B00C318C0B55E68B269002956D101247C
+:10133000534B5F7C3C424CD047893C4200D18BE010
+:101340009F7DDC7D936824023C43002B00D168E044
+:10135000002C00D18AE00125AC461D19A946494D53
+:10136000A846884447463F781F7001339945F9D114
+:10137000936800294CD1002B00D17DE0738A404F47
+:10138000A34260D89389012B5DD040257B7C2B4301
+:101390007B749368002B01D01B1993601768536806
+:1013A0003F1B1B19536093891760013B9BB29381CC
+:1013B000728AA24201D8002B0BD162464389934323
+:1013C000438153469D0045196B68DB68002B00D0B4
+:1013D0009847E0BCBA46B146A846F0BD0124284F64
+:1013E000A0463B7D1C42F4D023008B409C466446C3
+:1013F00043891C4235D0BB7DFC7D24021C4393688D
+:1014000006D0002BA9D145463B7DAB433B75C5E7D4
+:101410001B4F3D7DB8460127BD432F0045462F7524
+:10142000B8E713681F1E24D0002C1FD00125AC463E
+:101430002700002BA2D14825114C637C2B436374F9
+:101440003C000027ABE748257B7C2B437B740127BE
+:101450000D4B1F7093689DE740215A7C0A435A74D4
+:10146000B7E742463B7D93433B75B2E701239C4679
+:1014700084E701239C46DEE71368D9E7A40800202F
+:10148000005C0040205C0040880800200000000054
+:101490000100F8B5CE46474630488B1C9B00C31868
+:1014A0005B6880B59C465A69002904D004242C4B03
+:1014B0005D7C2C423FD101238B4005892B420FD00C
+:1014C000D4681668A14654684C445460371BA64241
+:1014D0000AD19D4363469B680581002B00D0984745
+:1014E000C0BCB946B046F8BD63461B8ABC469846A8
+:1014F0009F422AD864469368D4604B449360002C82
+:1015000007D0184A1D198C181A78013322709D4291
+:10151000FAD1002913D1474519D8C3681B8A9C45C5
+:1015200003D38623C35A9E4211D30A210C4A537C0B
+:101530000B435374D4E75D7CA5435D74BBE7012185
+:10154000074A537C0B435374CAE79C46D2E70221F7
+:10155000034A537C0B435374C2E7C046A4080020DF
+:10156000005C0040205C0040000000000000000023
+:10157000F0B5564644465F464D46F0B4744BF246CD
+:101580009A7983B0D4B2520700D583E0714DE30756
+:1015900053D4A3074BD4240705D56B68DB68002B15
+:1015A00001D0280098476A4B9A78DF7917401A795A
+:1015B000597A11409A7B8846D2B2019201223A426E
+:1015C00041D101239946624B2E0001249B463B00EA
+:1015D0004A4623411A4207D03369002B04D05B46A8
+:1015E000E0B29873FFF754FF01340436082CEED1B3
+:1015F0000126574F073C434623411E4206D02B6924
+:10160000002B03D0E0B2B873FFF782FE013404353B
+:10161000082CF0D14E4B019A50469A7300F0F0FE20
+:1016200003B0F0BCBB46B246A946A046F0BD2800B8
+:10163000FFF7BEF92407B6D5AFE72800FFF7A0F9FA
+:10164000A307A8D5F3E700219C7B9973597CE4B2EA
+:10165000C8B2C9063AD404210240014202D0587CE3
+:1016600088435874002A55D07023EB5CEA68002B3D
+:1016700036D0062B34D0364E9069B17DF37D1B02F7
+:101680000B43082B02D18368002B47D00020FFF7C3
+:101690003FFE37E00122987BDA71002201211A72A5
+:1016A000C0B25A729A7299730131C9B21A74DA745B
+:1016B0009A745A755A741A750829F4D19873254882
+:1016C000FFF73EF9504600F09BFEA9E78026597CC3
+:1016D0000240314359740421014216D1002A19D025
+:1016E00001231D4E00213370EB6828005B6898478A
+:1016F0003378002B06D04021154A537C0B4353749A
+:1017000000233370124B9C735BE7587C88435874FA
+:10171000002AE5D10020FFF7BBFEF3E703689B46F4
+:10172000002BB3D10C4B28009946012349460B707E
+:101730005368002198474B461B78002BE2D040238A
+:10174000727C134373745A464B461A70DAE7C046EC
+:10175000005C0040A4080020890800200000000070
+:10176000002210B5024B03481A70FFF7D9F810BDDC
+:1017700089080020A40800200000000000000000EC
+:10178000002230B501241D4B1D491A700A701D49F5
+:101790000A705978214359700121D9711A725A720D
+:1017A0009A724368002B27D0DB685A1E9341DB00F6
+:1017B0000733124AD3720023917B9373537CC9B2CF
+:1017C000DCB2DB0703D54025537C2B435374E4067E
+:1017D00004D58024094A537C23435374074B0A4A97
+:1017E00099730021C26001209A7B9973D979D2B292
+:1017F0000143D9719A7330BD0723DAE7005C0040DA
+:101800008908002088080020182700080000000030
+:101810008A23C25C0B3B1340014A13707047C046D9
+:10182000005C00400000000000000000000000001C
+:10183000002270B5012101250C4B9E7B9A73F6B2F4
+:101840002C008C40C8B29873D8790131A043D8716C
+:10185000587AA04358721A74DA749A745A755A7482
+:101860001A750829ECD19E7370BDC046005C00401B
+:101870008B1C9B00C0184368002B18D09B69002B61
+:1018800015D00C4A937B9173DBB2002908D020203D
+:10189000127D1040424250410130064A9373704716
+:1018A0002020527C1040424250410130F5E7002098
+:1018B000F5E7C046005C00400000000000000000AA
+:1018C0008B1C9B00C0184368002B17D05B69002B52
+:1018D00014D00B4A937B9173527CDBB2002907D062
+:1018E00010201040424250410130054A9373704726
+:1018F00020201040424250410130F6E70020F6E738
+:10190000005C00400000000000000000000000003B
+:1019100030B5140020200B4908340B5C13700132E1
+:10192000A242FAD1084B1A78002A09D00024402597
+:101930008A7B8C73487CD2B2284348748A731C70AB
+:1019400030BDC046005C0040890800200000000057
+:101950008B1C9B00C018426870B595690C00286804
+:101960000123002805D0518A40180138FEF736FCC3
+:1019700083B2AB81002C0AD00120A040044B997B9C
+:101980009C735A7AC9B20243D2B25A72997370BD2B
+:10199000005C0040000000000000000000000000AB
+:1019A000F8B58B1C4746CE469B00C3185B6880B5D4
+:1019B0009C465F693B68984663461C8A444500D94B
+:1019C0004446FC60002C10D12F4A937B9173DBB20C
+:1019D000002937D001202C494A7C02434A742A4A04
+:1019E0009373C0BCB946B046F8BD274ABB68914660
+:1019F000264AE6188D181A7801332A709E42FAD1C9
+:101A00004B464A469B7B9173DBB20029E2D12049C9
+:101A10000A78002A29D141467A688A1A94421ED14E
+:101A2000C268128A944207D38622815C863042784B
+:101A300012020A43904512D30A2013494A7C0243FA
+:101A40004A74CCE712490D78002D0ED17021415C0B
+:101A50000B29E0D10A20517C01435174BFE70220D9
+:101A600009494A7C02434A74B9E700220A70064ACF
+:101A70009373012203899343038163469B68002B80
+:101A8000AFD000219847ACE7005C0040205C0040EC
+:101A90008808002000000000000000000000000096
+:101AA0000C4B9A7B9973D2B2002907D1597CC90794
+:101AB0000BD56020597C0143597403E02020197D27
+:101AC00001431975034B9A7370472020597C0143D9
+:101AD0005974F7E7005C00400000000000000000BF
+:101AE000084B9A7B9973D2B2002906D12020597CE9
+:101AF00001435974034B9A7370471020597C01437A
+:101B00005974F7E7005C004000000000000000008E
+:101B10000A4B9A7B9973D2B2002906D12420597CB2
+:101B200081435974054B9A7370476020197D814336
+:101B30001975197D203001431975F3E7005C0040E9
+:101B40000A4B9A7B9973D2B2002906D12420597C82
+:101B500081435974054B9A7370473020597C8143F7
+:101B60005974597C103001435974F3E7005C00400C
+:101B7000FC22244BD203996A10B511439962996AE9
+:101B80002148802401409962996A59691F480A4393
+:101B90005A6190225B6900231D49D20593809160B0
+:101BA0001C49D16021214942918288214901116259
+:101BB0001949536211600122184952428B808A6090
+:101BC000C8608A820B624B620C601549154C8B8091
+:101BD0008A60CC608A820B624B620B6012498B80F8
+:101BE0008A60C8608A8211480B624B620B601049A0
+:101BF0008B808A60C8608A820B624B620B60FFF741
+:101C000077F910BD00100240FFFF81FF5555555573
+:101C10005FF7FFFF05515565A00400280004004848
+:101C20000008004855555501000C004850555555C1
+:101C30000014004800000000000000000000000048
+:101C40007047C046000000000000000000000000D7
+:101C5000012310B5084C23700023636000F028FBBB
+:101C600000F066FB00F084FB0449054800F098F999
+:101C70000223237062B610BDA80A00207427000852
+:101C8000180A002000000000000000000000000012
+:101C900072B68822024B9850024B853A1A70FEE7C2
+:101CA000180A0020A80A0020000000000000000020
+:101CB00010B500F03DF810BD00000000000000006D
+:101CC00010B5036100230C4C43612369C2609A681C
+:101CD000914204D91B68891A9A688A42FAD35A68D1
+:101CE00081600360426010609A685860521A9A607E
+:101CF00001235B42A36110BD180A00200000000010
+:101D0000036881689A6852189A60426813605A6042
+:101D1000002301220360024B52429A617047C04681
+:101D2000180A002000000000000000000000000071
+:101D300070B51B4DEB692C690133EB612B0010333F
+:101D40009C4215D0A368013BA3600FE02268616844
+:101D50000A605160236062B620002169E3689847F9
+:101D600072B66369002B04D12C69A368002BEDD0F7
+:101D700070BD2A6991688B4204D912685B1A916818
+:101D80009942FAD35168A360616022600C60916847
+:101D90005460CB1A936001235B42AB61E4E7C04619
+:101DA000180A0020000000000000000000000000F1
+:101DB00072B62423CB5C0C2B0BD8114A9B00D35852
+:101DC0009F468A6A9368013393604A680B68136080
+:101DD0005A60012300225B428B622533CA548B690F
+:101DE00088681B689A688242FBD25A680B604A6016
+:101DF0001160596062B6704700228B6A1A60E8E78A
+:101E0000882700080000000000000000000000001B
+:101E100024230022C254836981681B689A688A421D
+:101E2000FBD25A6803604260106058607047C04639
+:101E30002422074B10B5D968885418680468636079
+:101E40001C6001248454D860FEF7A6F910BDC0467A
+:101E5000180A002000000000000000000000000040
+:101E600070B5164C0500E66886B04B1C19D03300DF
+:101E70006846134AFFF724FF2423E1682068CD5405
+:101E80000268546022600122C254E060FEF784F9C7
+:101E9000009B002B02D06846FFF732FFB06A06B005
+:101EA00070BD2423F0542068310002685460226021
+:101EB0000122C254E060FEF76FF9EFE7180A002034
+:101EC000B11D00080000000000000000000000003C
+:101ED00070B5154C8162E368826899688A4214D9AA
+:101EE000242200259D549A6912689568A942FBD363
+:101EF00051681A6059600B6053600121242281549B
+:101F00001900E060FEF748F970BD24230021C15498
+:101F100083691B6899688A42FBD95A6803604260EA
+:101F200010605860F0E7C046180A0020000000006A
+:101F3000044BDA68196890688B6898428041404287
+:101F40007047C046180A0020000000000000000092
+:101F50000D4B10B51868D9680268012453601A60E7
+:101F6000D8602422002384548B548B698C681B68AE
+:101F70009A68A242FBD85A680B604A6011605960A7
+:101F8000FEF70AF910BDC046180A00200000000044
+:101F9000FEE7C04600000000000000000000000056
+:101FA000F0B5002604001D4B87B0066381630D0069
+:101FB000586000F015FA23002833A362E362183B4F
+:101FC0002361636101235B42A361154BE661DB6919
+:101FD0000027A660666321001E00882326626762D0
+:101FE000246064603C31E75020000E4A083B00F05A
+:101FF0001FF86B68E06024220362012383540A4ABD
+:1020000003930092EA68094B01922A69684602929A
+:102010000493059700F064F807B0F0BDA80A00200B
+:10202000180A0020BC27000840270008911F00085C
+:1020300010B5040008008B604364022100238184F2
+:102040000364C363013926338461C154C261220031
+:10205000030028320261E26A103342611360E362D6
+:102060001C33C36204330363436310BD00000000EC
+:102070008368020018005168503830B501621900B9
+:1020800014697439C1600C61546902254C61114CAA
+:102090000C621468D1681A002C3A81601580416482
+:1020A00000210D4A0164C163013D2631826145541E
+:1020B000C46119001400403928340C60D46A4C60A3
+:1020C0002160D1621A00203B243A12601B605B60E1
+:1020D00030BDC046B9010008180A00200000000009
+:1020E00010B5FFF7C5FFFFF793FE10BD000000001D
+:1020F00010B572B650394418210024392062E160CD
+:102100000B61029B20004B61124B0B620223A26009
+:10211000626400220121A3840F4B2264E263263211
+:10212000A361A1540D4A1900E26122002831216106
+:10213000D96A103261610A60DA6223002C33E362EB
+:102140000433002123636363FFF7C2FE62B62000FD
+:1021500010BDC046B9010008180A00204827000831
+:1021600070B572B6114BDC682500A062E06A2C35B0
+:10217000854207D12623E35C002B0ED00F20FFF70A
+:1021800057FE70BD0368E362FFF742FEE06AA842B3
+:10219000F8D12623E35C002BF0D12533E35C9B07C9
+:1021A000ECD16269236913605A60E7E7180A0020DE
+:1021B00010B5010072B60820FFF752FE62B610BDDE
+:1021C000084B10B5DB68002908D0426818605A60D7
+:1021D000136043600420FFF743FE10BD012040421E
+:1021E000FBE7C046180A00200000000000000000C5
+:1021F0000300006810B5834205D002681A6053607E
+:102200008162FFF705FE10BD000000000000000025
+:1022100000230060406083607047C04600000000FB
+:10222000F0B5C646046806000D0000B5A04233D0E4
+:102230000023242798460CE06068A368C26B134310
+:10224000C363C25D0A2A1DD00B2A28D02468A64287
+:1022500022D0E3682B43E360002DEDD023692B42AD
+:10226000EAD12468B44217D0E3682B43E3602369C2
+:102270002B42F6D06068A368C26B1343C363C25D90
+:102280000A2AE1D1826A1A42E0D043468362FFF70C
+:10229000BFFD2468A642DCD180BCB846F0BD826A8E
+:1022A00013409A42D2D1F0E7000000000000000085
+:1022B000024B034A1A60034A5A607047B00A002072
+:1022C000700C002000200020000000000000000032
+:1022D00070B5094C26686568AB1B984209D82B1A63
+:1022E00048421840821A964203D8954201D3626050
+:1022F00070BD0020FCE7C046B00A002000000000CE
+:1023000070B572B6094C26686568AB1B98420AD84E
+:102310002B1A48421840821A964204D8954202D39A
+:10232000626062B670BD0020FBE7C046B00A0020C4
+:102330000548064B10B503600023436083600C30F2
+:10234000FFF766FF10BDC046B80A00200123000851
+:1023500010B50024046041608260C36010BDC046B7
+:1023600010B50022FFF7B4FF10BDC046000000000A
+:1023700070B5134C2000FFF74BFF23002000114DD8
+:102380001033236104222B0014211430FFF7E0FFE7
+:10239000230024336362230020002833A362042235
+:1023A0002B001C212C30FFF7D3FF23003C33E363C9
+:1023B00023004033236423004433636470BDC0466C
+:1023C000D40A002061230008000000000000000083
+:1023D000EFF30983203383F3098862B67047C04660
+:1023E000024A0349136A0B401362704700ED00E094
+:1023F000FFFF00FF000000000000000000000000E0
+:1024000010B50F3000D110BD72B6EFF30984203C37
+:1024100084F3098880235B04E361FFF789FD0028CA
+:1024200002D0034BA361EEE7024BA361EBE7C0468A
+:10243000C7010008CA0100080000000000000000F9
+:1024400070B58025084B094ADB68094CDA61ED015B
+:102450000848A561FFF7ACFE8023DB050548A361B2
+:10246000FFF7A6FEF4E7C046180A00205027000830
+:102470000004004888130000000000000000000075
+:10248000F0B5D6464F464646C0B584B0FDF7D0FFFE
+:10249000FFF7DEFB80213E4C01222000C901FEF740
+:1024A0009FFD8021200001220902FEF799FD802373
+:1024B000DB05A36180230022A8211B06A36149003C
+:1024C000344B009234488032FFF712FE2000202166
+:1024D000324AFEF785FD20004021304AFEF780FD9C
+:1024E0002F4B304918009846FEF7AAF93023434491
+:1024F0009A460C239C4600230E260F25002499465D
+:10250000294F6E446D44E0444B462B70FA23012260
+:10251000310050469B003470FEF7F2F8012809D0D4
+:1025200080221B4BD2019A618022120238009A61EC
+:10253000FFF7AEFB0122290040461C4BFEF758F87E
+:10254000012809D08022124BD2019A618022120206
+:1025500038009A61FFF79CFB2A7833789A4209D0B9
+:1025600080220B4BD2019A618022120238009A61BC
+:10257000FFF78EFB802301345B009C42C4D1802294
+:10258000034B12029A610A48FFF712FEFBE7C046AE
+:102590000004004841240008200B00200A05000028
+:1025A00008080020E82700085C27000888130000BE
+:0425B00010270000F0
+:1025B400F8270008700C0020700C0020700C00201C
+:1025C400F8270008000000000000000000000000E0
+:1025D400F8270008000000000000000000000000D0
+:1025E400F8270008000000000000000000000000C0
+:1025F400F8270008000000000000000000000000B0
+:10260400F82700080000000000000000000000009F
+:10261400F82700080000000000000000000000008F
+:10262400F82700080000000000000000000000007F
+:10263400000000009107000871070008D107000896
+:10264400B1070008C1070008A107000881070008B6
+:1026540061070008E1070008700D00086E0D00080E
+:102664006E0D00086E0D00086E0D00086E0D00085A
+:10267400700D00086E0D00086E0D0008340D000882
+:10268400480D0008760D00086E0D00086E0D000858
+:102694006E0D00086E0D00086E0D00086E0D00082A
+:1026A4006E0D00086E0D00086E0D0008700D000818
+:1026B400E40D0008E80D0008E80D0008E80D000826
+:1026C400E80D0008E80D0008E40D0008E80D000816
+:1026D400E80D0008E80D0008E40D0008E40D00080A
+:1026E400E80D0008E80D0008E80D0008E80D0008F2
+:1026F400E80D0008E80D0008E80D0008E80D0008E2
+:10270400100E0008EA0D000801000000000000009F
+:102714000000000000000000D1090008210D00089D
+:10272400D10D0008400040009408002094080020C7
+:10273400010000008C0800206330000069646C65AF
+:10274400000000006E6F6E616D6500006C65642DA5
+:10275400626C696E6B000000555341525431206C19
+:102764006F6F706261636B204641494C000000004A
+:102774003C27000800000000000000003809002089
+:10278400180A0020F41D0008D21D0008D21D0008FC
+:10279400F81D0008CA1D0008C21D0008D21D00084B
+:1027A400CA1D0008D21D0008D21D0008D21D000851
+:1027B400D21D0008CA1D00086D61696E002C063820
+:1027C40004044C080C10141C2024252600000000CE
+:1027D400000024040100040000000000000C0010AC
+:1027E4002830340000C20100000000000000000096
+:0427F40000A24A04F1
+:04000005080001915D
+:00000001FF
diff --git a/testhal/SK32/SLED/Makefile b/testhal/SK32/SLED/Makefile
new file mode 100644
index 0000000000..416951e4e3
--- /dev/null
+++ b/testhal/SK32/SLED/Makefile
@@ -0,0 +1,194 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = sled
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# The SLED low level driver compiles the SLED1 group by default
+# (SK32_SLED_USE_SLED1=TRUE). This test drives the PC0 pad, which is the
+# datasheet SLED1_CH0 output served by the SLED2 channel registers
+# (DR[1]/DMAEN2/RSTSTR2), so the SLED2 group must be compiled in as well.
+UDEFS += -DSK32_SLED_USE_SLED2=TRUE
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/testhal/SK32/SLED/build.log b/testhal/SK32/SLED/build.log
new file mode 100644
index 0000000000..7f17f24658
--- /dev/null
+++ b/testhal/SK32/SLED/build.log
@@ -0,0 +1,130 @@
+Compiling crt0_v6m.S
+Compiling vectors.S
+Compiling chcoreasm.S
+Compiling crt1.c
+Compiling oop_base_object.c
+Compiling hal.c
+Compiling hal_safety.c
+Compiling hal_st.c
+Compiling hal_buffered_serial.c
+Compiling hal_buffers.c
+Compiling hal_queues.c
+Compiling hal_mmcsd.c
+Compiling hal_pal.c
+Compiling hal_flash.c
+Compiling nvic.c
+Compiling sk32_isr.c
+Compiling hal_lld.c
+Compiling hal_efl_lld.c
+Compiling hal_st_lld.c
+Compiling hal_pal_lld.c
+Compiling sk32_dma.c
+In file included from ../../../../chibios/os/hal/include/hal.h:28,
+ from ../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/sk32_dma.c:40:
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/sk32_dma.c: In function 'dmaStreamAllocI':
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/sk32_dma.h:86:45: warning: comparison of unsigned expression in '>= 0' is always true [-Wtype-limits]
+ 86 | #define SK32_DMA_IS_VALID_STREAM(id) (((id) >= 0U) && \
+ | ^~
+../../../../chibios/os/common/portability/GCC/ccportab.h:136:49: note: in definition of macro 'CC_UNLIKELY'
+ 136 | #define CC_UNLIKELY(x) __builtin_expect(!!(x), 0)
+ | ^
+../../../../chibios/os/rt/include/chearly.h:191:25: note: in expansion of macro 'PORT_UNLIKELY'
+ 191 | #define unlikely(x) PORT_UNLIKELY(x)
+ | ^~~~~~~~~~~~~
+../../../../chibios/os/rt/include/chdebug.h:120:9: note: in expansion of macro 'unlikely'
+ 120 | if (unlikely(!(c))) { \
+ | ^~~~~~~~
+../../../../chibios/os/hal/osal/rt-nil/osal.h:281:25: note: in expansion of macro 'chDbgCheck'
+ 281 | #define osalDbgCheck(c) chDbgCheck(c)
+ | ^~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/sk32_dma.c:249:3: note: in expansion of macro 'osalDbgCheck'
+ 249 | osalDbgCheck(SK32_DMA_IS_VALID_STREAM(id));
+ | ^~~~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/sk32_dma.c:249:16: note: in expansion of macro 'SK32_DMA_IS_VALID_STREAM'
+ 249 | osalDbgCheck(SK32_DMA_IS_VALID_STREAM(id));
+ | ^~~~~~~~~~~~~~~~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/sk32_dma.h:76:51: warning: comparison of unsigned expression in '>= 0' is always true [-Wtype-limits]
+ 76 | #define SK32_DMA_IS_VALID_PRIORITY(prio) (((prio) >= 0U) && ((prio) <= 3U))
+ | ^~
+../../../../chibios/os/common/portability/GCC/ccportab.h:136:49: note: in definition of macro 'CC_UNLIKELY'
+ 136 | #define CC_UNLIKELY(x) __builtin_expect(!!(x), 0)
+ | ^
+../../../../chibios/os/rt/include/chearly.h:191:25: note: in expansion of macro 'PORT_UNLIKELY'
+ 191 | #define unlikely(x) PORT_UNLIKELY(x)
+ | ^~~~~~~~~~~~~
+../../../../chibios/os/rt/include/chdebug.h:120:9: note: in expansion of macro 'unlikely'
+ 120 | if (unlikely(!(c))) { \
+ | ^~~~~~~~
+../../../../chibios/os/hal/osal/rt-nil/osal.h:281:25: note: in expansion of macro 'chDbgCheck'
+ 281 | #define osalDbgCheck(c) chDbgCheck(c)
+ | ^~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/sk32_dma.c:250:3: note: in expansion of macro 'osalDbgCheck'
+ 250 | osalDbgCheck(SK32_DMA_IS_VALID_PRIORITY(priority));
+ | ^~~~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/sk32_dma.c:250:16: note: in expansion of macro 'SK32_DMA_IS_VALID_PRIORITY'
+ 250 | osalDbgCheck(SK32_DMA_IS_VALID_PRIORITY(priority));
+ | ^~~~~~~~~~~~~~~~~~~~~~~~~~
+Compiling hal_sled_lld.c
+In file included from ../../../../chibios/os/hal/include/hal.h:28,
+ from ../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:48:
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c: In function 'sled_lld_send_bytes':
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.h:110:51: warning: comparison is always true due to limited range of data type [-Wtype-limits]
+ 110 | #define SK32_SLED_IS_VALID_GROUP(group) (((group) >= 0) && ((group) <= 1))
+ | ^~
+../../../../chibios/os/common/portability/GCC/ccportab.h:136:49: note: in definition of macro 'CC_UNLIKELY'
+ 136 | #define CC_UNLIKELY(x) __builtin_expect(!!(x), 0)
+ | ^
+../../../../chibios/os/rt/include/chearly.h:191:25: note: in expansion of macro 'PORT_UNLIKELY'
+ 191 | #define unlikely(x) PORT_UNLIKELY(x)
+ | ^~~~~~~~~~~~~
+../../../../chibios/os/rt/include/chdebug.h:120:9: note: in expansion of macro 'unlikely'
+ 120 | if (unlikely(!(c))) { \
+ | ^~~~~~~~
+../../../../chibios/os/hal/osal/rt-nil/osal.h:281:25: note: in expansion of macro 'chDbgCheck'
+ 281 | #define osalDbgCheck(c) chDbgCheck(c)
+ | ^~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:394:3: note: in expansion of macro 'osalDbgCheck'
+ 394 | osalDbgCheck(SK32_SLED_IS_VALID_GROUP(group));
+ | ^~~~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:394:16: note: in expansion of macro 'SK32_SLED_IS_VALID_GROUP'
+ 394 | osalDbgCheck(SK32_SLED_IS_VALID_GROUP(group));
+ | ^~~~~~~~~~~~~~~~~~~~~~~~
+Compiling stm32_dma.c
+Compiling hal_usb_lld.c
+Compiling board.c
+Compiling osal.c
+Compiling chsys.c
+Compiling chrfcu.c
+Compiling chdebug.c
+Compiling chtrace.c
+Compiling chvt.c
+Compiling chschd.c
+Compiling chinstances.c
+Compiling chthreads.c
+Compiling chregistry.c
+Compiling chsem.c
+Compiling chmtx.c
+Compiling chcond.c
+Compiling chevents.c
+Compiling chmsg.c
+Compiling chdynamic.c
+Compiling chmboxes.c
+Compiling chmemcore.c
+Compiling chmemheaps.c
+Compiling chmempools.c
+Compiling chpipes.c
+Compiling chobjcaches.c
+Compiling chdelegates.c
+Compiling chfactory.c
+Compiling chcore.c
+Compiling main.c
+Linking build/sled.elf
+Creating build/sled.hex
+Creating build/sled.bin
+Creating build/sled.dmp
+
+ text data bss dec hex filename
+ 5644 4 8184 13832 3608 build/sled.elf
+Creating build/sled.list
+
+Done
diff --git a/testhal/SK32/SLED/cfg/chconf.h b/testhal/SK32/SLED/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/testhal/SK32/SLED/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/SLED/cfg/halconf.h b/testhal/SK32/SLED/cfg/halconf.h
new file mode 100644
index 0000000000..a9d587de17
--- /dev/null
+++ b/testhal/SK32/SLED/cfg/halconf.h
@@ -0,0 +1,584 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB FALSE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SK32 SLED driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SK32 SLED subsystem.
+ * @note This switch is not part of the generic ChibiOS halconf template:
+ * the SLED controller is a 3Think peripheral found on the SK32F077
+ * only. The platform header (hal_lld.h) derives the
+ * SK32_DMA_REQUIRED macro from it so that the DMA1 helper code and
+ * the DMA1 initialization are included in the build.
+ */
+#if !defined(HAL_USE_SLED) || defined(__DOXYGEN__)
+#define HAL_USE_SLED TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/SLED/cfg/mcuconf.h b/testhal/SK32/SLED/cfg/mcuconf.h
new file mode 100644
index 0000000000..af1332d1ca
--- /dev/null
+++ b/testhal/SK32/SLED/cfg/mcuconf.h
@@ -0,0 +1,98 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for the
+ * USB IP, but the SK32 port provides native drivers for the entire core
+ * platform and for every peripheral, so no shared STM32 low level driver
+ * (TIMv1, USARTv2, ADCv1, SPIv2, I2Cv2, ..., nor the CANv1/DACv1/DMAv1/
+ * RTCv2/xWDGv1 stubs) is linked in. This mcuconf.h only needs to satisfy
+ * the single SK32 naming scheme:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST/peripheral settings consumed by
+ * the native SK32 HAL low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches. Not used by this test: HAL_USE_SERIAL
+ * is disabled in halconf.h, the switches are kept at FALSE.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 FALSE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ * The SK32 native interrupt-driven SPI driver is selected through the
+ * SK32_SPI_USE_SPIx switches. Not used by this test: HAL_USE_SPI is
+ * disabled in halconf.h, the switches are kept at FALSE.
+ */
+#define SK32_SPI_USE_SPI1 FALSE
+#define SK32_SPI_USE_SPI2 FALSE
+#define SK32_SPI_SPI1_PRIORITY 3
+#define SK32_SPI_SPI2_PRIORITY 3
+
+/*
+ * SLED driver system settings.
+ * The SK32 SLED driver (hal_sled_lld.c) is enabled through the HAL_USE_SLED
+ * switch in halconf.h. It allocates one DMA1 channel per compiled group
+ * (channel 1 for the SLED1 registers, channel 2 for the SLED2 registers)
+ * and routes each channel to its SLED request line through the SYSCFG CFGR3
+ * remapping. This test drives the PC0 pad = datasheet SLED1_CH0 output,
+ * which is served by the SLED2 channel registers (DR[1]/DMAEN2/RSTSTR2);
+ * the Makefile therefore adds -DSK32_SLED_USE_SLED2=TRUE. The pad (PC0,
+ * alternate function 14) is configured by main.c.
+ */
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+#endif /* MCUCONF_H */
\ No newline at end of file
diff --git a/testhal/SK32/SLED/main.c b/testhal/SK32/SLED/main.c
new file mode 100644
index 0000000000..d3acf245b3
--- /dev/null
+++ b/testhal/SK32/SLED/main.c
@@ -0,0 +1,150 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+#include "ch.h"
+#include "hal.h"
+#include "hal_sled_lld.h"
+
+/*===========================================================================*/
+/* SLED2 (PC0 / SLED1_CH0) 64xRGB chaser on the GENERIC_SK32_F077 board. */
+/*===========================================================================*/
+
+/*
+ * LED string: 64 addressable WS2812/SK6812 RGB LEDs driven by the SK32F077
+ * SLED peripheral. Only the PC0 pad is used:
+ *
+ * - PC0 = SLED1_CH0 output pad (datasheet naming), served by the SLED2
+ * group registers (DR[1]/DMAEN2/RSTSTR2, DMA1 channel 2, request SLED_G2);
+ * alternate function 14.
+ *
+ * - The data is written with 8-bit wide accesses (@p SLED_WIDTH_8BIT): every
+ * byte is replicated on the four channels of the group, but only CH0 is
+ * wired to the string on PC0 so the byte stream drives the strip.
+ *
+ * - Wire order is GRB and the SLED block serializes each byte verbatim (no
+ * bit packing): 64 LEDs * 3 bytes = 192 bytes per frame.
+ *
+ * A "running water" (chaser) effect is rendered: a bright comet with a short
+ * fading tail sweeps along the 64 LEDs and wraps around.
+ *
+ * PB14 / PB15: PB15 toggles after every acknowledged frame; PB14 is driven
+ * solid HIGH when a frame times out (the test never halts).
+ */
+
+/*===========================================================================*/
+/* Application entry point. */
+/*===========================================================================*/
+int main(void) {
+
+ /* Vendor RGBKeyboardSTK time codes: T0H=4, T1H=15, TRST=80 cycles, clock
+ /4, baud /32.*/
+ static const SLEDConfig sled_cfg = {
+ .t0h_cycles = 4U,
+ .t1h_cycles = 15U,
+ .trst_cycles = 80U,
+ .prescaler = 3U,
+ .baud_prescaler = 31U,
+ .idle_polarity = SLED_POLARITY_LOW,
+ .reset_polarity = SLED_POLARITY_LOW
+ };
+
+ /* Number of RGB LEDs and its byte frame size (3 bytes GRB each).*/
+ enum {
+ LED_COUNT = 64,
+ FRAME_BYTES = LED_COUNT * 3
+ };
+
+ static uint8_t frame[FRAME_BYTES];
+ bool led_success = false;
+ uint32_t head = 0U;
+ unsigned led;
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ /* Configuring the PB14/PB15 LED lines as push-pull outputs, initially off. */
+ palSetPadMode(GPIOB, GPIOB_PIN14, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, GPIOB_PIN15, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palClearPad(GPIOB, GPIOB_PIN15);
+
+ /*
+ * Output pad setup on alternate function 14: PC0 = SLED1_CH0, served by
+ * the SLED2 group registers (DR[1]). The SLED low level driver does not
+ * configure the pads, so it is routed here before the subsystem is
+ * started.
+ */
+ palSetPadMode(GPIOC, 0U, PAL_MODE_ALTERNATE(14) | PAL_SK32_OSPEED_HIGHEST);
+
+ /*
+ * Starting the SLED subsystem with the SLED2 group enabled (SK32_SLED_USE_
+ * SLED2=TRUE is set in the Makefile).
+ */
+ sled_lld_init();
+ sled_lld_start(&sled_cfg);
+
+ /*
+ * Frame loop: render one chaser frame every 30 ms and stream it out.
+ * PB15 toggles on every acknowledged frame, PB14 stays solid HIGH after a
+ * timeout.
+ */
+ while (true) {
+
+ /* Render the chaser: a bright white head with a red/blue fading tail. */
+ for (led = 0U; led < LED_COUNT; led++) {
+ uint8_t *px = &frame[led * 3U];
+ int32_t d = (int32_t)((head + LED_COUNT - (uint32_t)led) % LED_COUNT);
+ uint8_t a;
+
+ if (d == 0) {
+ /* Comet head: bright white. */
+ px[0] = 0xFFU; /* G */
+ px[1] = 0xFFU; /* R */
+ px[2] = 0xFFU; /* B */
+ }
+ else {
+ /* Fading tail, intensity falls off with distance from the head. */
+ a = (d >= 12U) ? 0U : (uint8_t)(32U - ((uint32_t)d * 32U) / 12U);
+ px[0] = (uint8_t)(a * 8U / 32U); /* G (small) */
+ px[1] = (uint8_t)(a * 24U / 32U); /* R (medium) */
+ px[2] = (uint8_t)(a * 16U / 32U); /* B (large) */
+ }
+ }
+
+ /* 8-bit write on the SLED2 group: every byte goes out on CH0 = PC0. */
+ if (sled_lld_send_bytes(SLED2, frame, sizeof(frame)) == MSG_OK) {
+ led_success = !led_success;
+ (led_success) ? palSetPad(GPIOB, GPIOB_PIN15) : palClearPad(GPIOB, GPIOB_PIN15);
+ }
+ else {
+ /* Frame not acknowledged: report it on PB14 and keep going. */
+ palSetPad(GPIOB, GPIOB_PIN14);
+ }
+
+ /* Advance the chaser head (wraps around).*/
+ head = (head + 1U) % LED_COUNT;
+
+ chThdSleepMilliseconds(30);
+ }
+}
\ No newline at end of file
diff --git a/testhal/SK32/SLED/sled.hex b/testhal/SK32/SLED/sled.hex
new file mode 100644
index 0000000000..d30ac71c42
--- /dev/null
+++ b/testhal/SK32/SLED/sled.hex
@@ -0,0 +1,335 @@
+:020000040800F2
+:100000000004002091010008A111000893010008DC
+:100010009301000893010008930100089301000870
+:100020009301000893010008930100089301000860
+:1000300093010008930100089301000811050008CE
+:100040009301000893010008930100089301000840
+:100050009301000893010008930100089301000830
+:1000600093010008810600081108000861080008D3
+:100070009301000893010008930100089301000810
+:100080009301000893010008930100089301000800
+:1000900093010008930100089301000893010008F0
+:1000A00093010008930100089301000893010008E0
+:1000B00093010008930100089301000893010008D0
+:1000C00072B6244880F30888234880F30988022008
+:1000D00080F31488BFF36F8F00F022F900F018FD51
+:1000E0001E481F491B4A914202DA08600431FAE7B0
+:1000F0001C49194A914202DA08600431FAE71A49A8
+:100100001A4A1B4B9A4204DA086810600431043220
+:10011000F8E700201749184A914202DA08600431D2
+:10012000FAE700F015F900F003F9144C144DAC4255
+:1001300003DA216888470434F9E701F069F8114CC3
+:10014000114DAC4203DA216888470434F9E70F49BE
+:1001500008470000000400200008002055555555B0
+:100160000000002000040020A01400080008002067
+:100170000408002008080020340A0020C0000008FD
+:10018000C0000008C0000008C000000841030008CB
+:1001900096E700F000F8FEE7F0B544464D465646B7
+:1001A0005F46F0B46B46CB60C3689D46F0BCA0468A
+:1001B000A946B246BB46F0BD62B6281CA047002047
+:1001C00000F026FFFEE700F053FE024A024B1360E8
+:1001D000FEE7C04604ED00E000000080002934D0B6
+:1001E0000123002210B488422CD301242407A14209
+:1001F00004D2814202D209011B01F8E7E400A142C6
+:1002000004D2814202D249005B00F8E7884201D360
+:10021000401A1A434C08A04202D3001B5C08224338
+:100220008C08A04202D3001B9C082243CC08A042A9
+:1002300002D3001BDC082243002803D01B0901D095
+:100240000909E3E7100010BC704701B5002000F079
+:100250000BF802BD0029F8D003B5FFF7C1FF0EBCB3
+:100260004243891A1847C0467047C046F0B505009A
+:10027000830749D0541E002A45D003000326CAB282
+:1002800002E00135013C3ED301332A703342F8D1FC
+:10029000032C30D9FF220A401502AD182A04AD18EC
+:1002A0000F2C34D92700103F3F093E01B4461E00F1
+:1002B0001A0010366644156055609560D56010329E
+:1002C0009642F8D101373F01DB190F270C22274056
+:1002D00022403C00002A0ED03E1FB608B400A446BF
+:1002E0001A001C1D644420C2A242FCD10324013622
+:1002F000B6009B193C40002C05D0C9B21C191970DE
+:1003000001339C42FBD1F0BD03001400C0E727007D
+:10031000E2E7C0460000000000000000000000000E
+:100320007047C04600000000000000000000000010
+:100330007047C04600000000000000000000000000
+:10034000FEE7C046000000000000000000000000C2
+:1003500070B5114C250080356068A2682168904214
+:100360000AD2030040C940C39342FBD3013A131A97
+:100370009B0801339B00C018E368834207D9013B07
+:100380001A1A9208013200219200FFF76FFF103411
+:10039000AC42E1D170BDC0462C1300080000000043
+:1003A00010B500F055F800F0DBF800F019FC00F093
+:1003B00007F810BD00000000000000000000000071
+:1003C00010B500F0B5F810BD0000000000000000FE
+:1003D0000F4A70B594460322FF25C0260240D20082
+:1003E00095408901914083089B006344B6009C5965
+:1003F0001F22AC430C439C51012302409340C02276
+:1004000040098000604452008350036070BDC046C4
+:1004100000E100E00000000000000000000000001B
+:100420000F2310B50340040003202040FF24074A97
+:10043000C0009446844089018140083B9B089B0092
+:100440006344DA69A2430A43DA6110BD00ED00E0BB
+:10045000074B084A022110B59A62002249429A626B
+:10046000D9600131DA6019611A6100F029F910BD13
+:1004700000100240FFFF81FF0000000000000000AC
+:1004800001211D4A13680B4313600131136819429F
+:10049000FCD0032153688B435360174A093108008D
+:1004A000536818401942FAD1FD2311680B401360BC
+:1004B000E4235B035060D06210635063D0625360EA
+:1004C000802311685B040B43802113600A4A89046E
+:1004D00013680B42FCD01221084B19605368103985
+:1004E0000B4353600C2204494B681340082BFBD18B
+:1004F000034B044A1A60704700100240002002407B
+:100500000008002000A24A040000000000000000D3
+:1005100010B5744672B600F08BFB62B6200000F096
+:1005200057FE10BD000000000000000000000000A9
+:10053000064B074A10B55A6000229A600732022122
+:100540000B201A60FFF76CFF10BDC04610E000E002
+:100550001F1C000000000000000000000000000060
+:100560007047C046000000000000000000000000CE
+:10057000F0B54E46032457464546DE46E0B5270013
+:10058000530917405607D508520525401C40130F44
+:10059000012285B0039391460023A4460297F60FEB
+:1005A000009717E05C4607682740009C27430760D8
+:1005B000072B52D85446076A3A4022430262490840
+:1005C00044D0624692009446009A76009200AD00B4
+:1005D000009201334A460A42F1D007274C46039A5B
+:1005E0001F40BF00BA409C4092468288A2433243DB
+:1005F00092B28280846801945C00A0460324424643
+:100600009440E24393462200A046019C944322007A
+:1006100044462A438260C268A24364462243C260C1
+:100620000F22BA40029CD243022CBBD1072B1AD80E
+:10063000076A17405246174307625C46026849083A
+:100640002240009C224302600029BAD105B0F0BCD0
+:10065000BB46B246A946A046F0BD5446476A3A405A
+:1006600022434262ABE7476A17405246174347624C
+:10067000E3E7C046000000000000000000000000AA
+:100680000E210A4B10B51A6874461140084A1268C8
+:100690000A4206D0074A59609368002B01D0D068FF
+:1006A0009847200000F094FD10BDC04600000240B5
+:1006B00008000240080800200000000000000000C0
+:1006C0000020002100230D4A106051600C490B608E
+:1006D0000C4993600B600C4913610B600B499361EB
+:1006E0000B600B4913620B600A4993620B60136342
+:1006F0000122094B52425A607047C0460808002048
+:10070000080002401C000240300002404400024049
+:10071000580002406C00024000000240000000004F
+:100720009C460123F0B54646D6464F461D00C0B54F
+:10073000854000261E4C994623688A46B0461D42D5
+:1007400029D11C4F0601B919884661460130C00005
+:1007500022502B43201841602360AB0605D0494648
+:10076000154B5869084358615B69002A06D0BA19CD
+:1007700063689068184214D01D4365600F20B91952
+:100780004B68497B1A6882431A600E228A40B85926
+:10079000426000221A604046E0BCBA46B146A84614
+:1007A000F0BD5146D07BFFF713FE6368E4E7C04617
+:1007B00008080020AC1300080010024000000000F0
+:1007C00010B572B6FFF7ACFF62B610BD00000000B6
+:1007D00070B50468457B23680E21EB4019404368DF
+:1007E000827B1B6819420BD00B00AB400132636067
+:1007F000044BD200D458002C02D09B185868A04754
+:1008000070BDC04608080020000000000000000085
+:100810000E210C4A10B5136874461B0919400A4B87
+:100820001B680B4207D00B015360084A1369002B69
+:1008300001D0506998470648FFF7CAFF200000F032
+:10084000C7FC10BD000002401C0002400808002048
+:10085000CC130008000000000000000000000000B1
+:1008600070B5084C75462000FFF7B2FF200010302D
+:10087000FFF7AEFF20002030FFF7AAFF280000F0AE
+:10088000A7FC70BDDC1300080000000000000000A1
+:10089000002000210022034B18605960024B1A709F
+:1008A0007047C046480800204008002000000000B3
+:1008B00070B5344C2178002962D1334B334A9D699D
+:1008C0002A439A619A698022DD68D2042A43DA6059
+:1008D000DA682F4D2A40DA60DB6801232D4A9361E4
+:1008E0009161837841781B0409020B4301780B4323
+:1008F000D3600379C1781B020B4393601568264BC4
+:100900001D404379591E8B4181795B02481E81410C
+:1009100009020B4321492B430B431360022100239F
+:1009200000220020FFF74CFF3F221D4D1D4E2B6B78
+:1009300030609343103A13432B63002200230221BB
+:100940000120FFF73DFF2A6B174B70601A40C02350
+:100950009B0113432B633368002B04D05B68134A5D
+:100960009A60134A1A60002804D04368114A9A60BA
+:100970000F4A1A60012207490B6813430B6022706B
+:1009800070BDC04640080020001002400100000475
+:10099000FFFFFFFB00680140F6F8FCFC00000101CE
+:1009A0000000014048080020FFC0FFFF1068014020
+:1009B00090200000146801400000000000000000CA
+:1009C000F0B5D6464F4646464A4B82461B78894686
+:1009D0009046C0B5002B00D188E0474B8200D75825
+:1009E000002F00D182E0002900D17FE04346002B98
+:1009F0006AD080235B02984500D377E005006B1E28
+:100A00009D41FF236D421B051D4080235B039C46D7
+:100A10003A4E6544336864242B4203D108E0013C1C
+:100A2000002C60D00A2000F01BFB33681D42F6D179
+:100A30000F217B6838681A6801248A431A600E22E5
+:100A4000797B8A404146426059604946D9602C49C9
+:100A5000196019682143294C1960216829432160D4
+:100A600059686424002903D10DE0013C002C31D0E9
+:100A70000A2000F0F5FA7B685A68002AF5D1797BE4
+:100A80000E328A4038680F2419681C4FA143196040
+:100A900042603B6855341D4203D108E0013C002C04
+:100AA00021D00A2000F0DCFA3B682B42F6D15646F2
+:100AB000731E9E41FF2376425B051E4080239B03ED
+:100AC0009C460E4B66445E600020E0BCBA46B146D0
+:100AD000A846F0BD0F211A688A431A600E23797B5D
+:100AE0003A688B40536001204042EEE702204042CA
+:100AF000EBE7C0464008002048080020006801409D
+:100B00009020000000000000000000000000000035
+:100B1000FC22244BD203996A10B511439962996A59
+:100B20002148802401409962996A59691F480A4303
+:100B30005A6190225B6900231D49D2059380916020
+:100B40001C49D160212149429182882149011162C9
+:100B50001949536211600122184952428B808A6000
+:100B6000C8608A820B624B620C601549154C8B8001
+:100B70008A60CC608A820B624B620B6012498B8068
+:100B80008A60C8608A8211480B624B620B60104910
+:100B90008B808A60C8608A820B624B620B60FFF7B1
+:100BA0006FFC10BD00100240FFFF81FF55555555E9
+:100BB0005FF7FFFF05515565A004002800040048B9
+:100BC0000008004855555501000C00485055555532
+:100BD00000140048000000000000000000000000B9
+:100BE0007047C04600000000000000000000000048
+:100BF000012310B5084C23700023636000F040FA15
+:100C000000F07EFA00F09CFA0449054800F058F91B
+:100C10000223237062B610BDC0090020181400081A
+:100C2000300900200000000000000000000000006B
+:100C300010B500F03DF810BD0000000000000000FD
+:100C400010B5036100230C4C43612369C2609A68AC
+:100C5000914204D91B68891A9A688A42FAD35A6861
+:100C600081600360426010609A685860521A9A600E
+:100C700001235B42A36110BD300900200000000089
+:100C8000036881689A6852189A60426813605A60D3
+:100C9000002301220360024B52429A617047C04612
+:100CA00030090020000000000000000000000000EB
+:100CB00070B51B4DEB692C690133EB612B001033D0
+:100CC0009C4215D0A368013BA3600FE022686168D5
+:100CD0000A605160236062B620002169E36898478A
+:100CE00072B66369002B04D12C69A368002BEDD088
+:100CF00070BD2A6991688B4204D912685B1A9168A9
+:100D00009942FAD35168A360616022600C609168D7
+:100D10005460CB1A936001235B42AB61E4E7C046A9
+:100D2000300900200000000000000000000000006A
+:100D300072B62423CB5C0C2B0BD8114A9B00D358E2
+:100D40009F468A6A9368013393604A680B68136010
+:100D50005A60012300225B428B622533CA548B699F
+:100D600088681B689A688242FBD25A680B604A60A6
+:100D70001160596062B6704700228B6A1A60E8E71A
+:100D80002C1400080000000000000000000000001B
+:100D900024230022C254836981681B689A688A42AE
+:100DA000FBD25A6803604260106058607047C046CA
+:100DB0002422074B10B5D96888541868046863600A
+:100DC0001C6001248454D860FFF7E6F910BDC046CA
+:100DD00030090020000000000000000000000000BA
+:100DE00070B5164C0500E66886B04B1C19D0330070
+:100DF0006846134AFFF724FF2423E1682068CD5496
+:100E00000268546022600122C254E060FFF7C4F916
+:100E1000009B002B02D06846FFF732FFB06A06B095
+:100E200070BD2423F05420683100026854602260B1
+:100E30000122C254E060FFF7AFF9EFE7300900206C
+:100E4000310D00080000000000000000000000005C
+:100E5000044BDA68196890688B6898428041404278
+:100E60007047C0463009002000000000000000006C
+:100E70000D4B10B51868D9680268012453601A60D8
+:100E8000D8602422002384548B548B698C681B689F
+:100E90009A68A242FBD85A680B604A601160596098
+:100EA000FFF77AF910BDC0463009002000000000AD
+:100EB000FEE7C04600000000000000000000000047
+:100EC000F0B5002604001D4B87B0066381630D005A
+:100ED000586000F06DF923002833A362E362183BE9
+:100EE0002361636101235B42A361154BE661DB690A
+:100EF0000027A660666321001E00882326626762C1
+:100F0000246064603C31E75020000E4A083B00F04A
+:100F10001FF86B68E06024220362012383540A4AAD
+:100F200003930092EA68094B01922A69684602928B
+:100F30000493059700F064F807B0F0BDC0090020E5
+:100F4000300900206014000810140008B10E0008D9
+:100F500010B5040008008B604364022100238184E3
+:100F60000364C363013926338461C154C261220022
+:100F7000030028320261E26A103342611360E362C7
+:100F80001C33C36204330363436310BD00000000DD
+:100F90008368020018005168503830B501621900AA
+:100FA00014697439C1600C61546902254C61114C9B
+:100FB0000C621468D1681A002C3A81601580416473
+:100FC00000210D4A0164C163013D2631826145540F
+:100FD000C46119001400403928340C60D46A4C6094
+:100FE0002160D1621A00203B243A12601B605B60D2
+:100FF00030BDC046B90100083009002000000000E3
+:1010000010B5FFF7C5FFFFF7C3FE10BD00000000DD
+:1010100070B572B6114BDC682500A062E06A2C3511
+:10102000854207D12623E35C002B0ED00F20FFF76B
+:10103000BFFE70BD0368E362FFF7AAFEE06AA84244
+:10104000F8D12623E35C002BF0D12533E35C9B072A
+:10105000ECD16269236913605A60E7E73009002028
+:1010600010B5010072B60820FFF7BAFE62B610BDD7
+:1010700000230060406083607047C04600000000AD
+:10108000024B034A1A60034A5A607047C80900209D
+:10109000380A0020002000200000000000000000AE
+:1010A00070B5094C26686568AB1B984209D82B1AA5
+:1010B00048421840821A964203D8954201D3626092
+:1010C00070BD0020FCE7C046C809002000000000F9
+:1010D00070B572B6094C26686568AB1B98420AD891
+:1010E0002B1A48421840821A964204D8954202D3DD
+:1010F000626062B670BD0020FBE7C046C8090020F0
+:101100000548064B10B503600023436083600C3034
+:10111000FFF7AEFF10BDC046D0090020D110000877
+:1011200010B50024046041608260C36010BDC046F9
+:1011300010B50022FFF7B4FF10BDC046000000004C
+:1011400070B5134C2000FFF793FF23002000114DD2
+:101150001033236104222B0014211430FFF7E0FF29
+:10116000230024336362230020002833A362042277
+:101170002B001C212C30FFF7D3FF23003C33E3630B
+:1011800023004033236423004433636470BDC046AE
+:10119000EC090020311100080000000000000000F0
+:1011A000EFF30983203383F3098862B67047C046A2
+:1011B000024A0349136A0B401362704700ED00E0D6
+:1011C000FFFF00FF00000000000000000000000022
+:1011D00010B50F3000D110BD72B6EFF30984203C7A
+:1011E00084F3098880235B04E361FFF731FE002864
+:1011F00002D0034BA361EEE7024BA361EBE7C046CD
+:10120000C7010008CA01000800000000000000003B
+:10121000F0B5DE4657464E464546E0B585B0FFF789
+:10122000BFF8FFF7E5FC80213B4B01221800C90104
+:101230009946FFF79DF98021012248460902FFF7F0
+:1012400097F980234A46DB059361802380211B06A2
+:1012500093614846314A4900FFF78AF901212F4A34
+:101260002F48FFF785F9FFF713FB2E48FFF720FB08
+:10127000002398469B4601932B4B0027FF26002412
+:101280009A4603AD0CE0019B002B37D080234A46E1
+:10129000DB05936100230193FA20C000FFF7E0FE15
+:1012A00043460322290001202E706F70AB70FFF7B8
+:1012B00087FB002829D15B46002B26D080234A4695
+:1012C0001B06936100239B46601C0321FEF7C2FFAF
+:1012D00052464B005B18D65C53445F789B7802AA59
+:1012E000984616701300577042460C009A70190009
+:1012F00003220020FFF764FB0028C4D080234A4665
+:10130000DB01936101230193C6E780234A461B0258
+:10131000936101239B46D7E7000400481A070000A9
+:0C13200000080048981400088C14000815
+:10132C00A4140008340A0020340A0020340A0020D7
+:10133C00A4140008000000000000000000000000E1
+:10134C00A4140008000000000000000000000000D1
+:10135C00A4140008000000000000000000000000C1
+:10136C00A4140008000000000000000000000000B1
+:10137C00A4140008000000000000000000000000A1
+:10138C00A414000800000000000000000000000091
+:10139C00A414000800000000000000000000000081
+:1013AC00000002400800024001000000000000099B
+:1013BC00000002401C000240060000000004010A6C
+:1013CC000000024030000240060000000008020A43
+:1013DC00000002404400024078000000000C030BA7
+:1013EC000000024058000240780000000010040B7E
+:1013FC00000002406C000240780000000014050B55
+:10140C006330000069646C65000000000C14000877
+:10141C0000000000000000005008002030090020EF
+:10142C00740D0008520D0008520D0008780D0008CC
+:10143C004A0D0008420D0008520D00084A0D000824
+:10144C00520D0008520D0008520D0008520D0008F4
+:10145C004A0D00086D61696E002C063804044C08B6
+:10146C000C10141C2024252600000000000024046D
+:10147C000100040000000000000C001028303400B3
+:10148C00FF000000FF000000FF000000040F5003ED
+:04149C001F0000002D
+:0414A00000A24A0458
+:04000005080001915D
+:00000001FF
diff --git a/testhal/SK32/SPI/Makefile b/testhal/SK32/SPI/Makefile
new file mode 100644
index 0000000000..067a7cb9f4
--- /dev/null
+++ b/testhal/SK32/SPI/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = spi
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/testhal/SK32/SPI/build.log b/testhal/SK32/SPI/build.log
new file mode 100644
index 0000000000..84bded5160
--- /dev/null
+++ b/testhal/SK32/SPI/build.log
@@ -0,0 +1,73 @@
+Compiling crt0_v6m.S
+Compiling vectors.S
+Compiling chcoreasm.S
+Compiling crt1.c
+Compiling oop_base_object.c
+Compiling hal.c
+Compiling hal_safety.c
+Compiling hal_st.c
+Compiling hal_buffered_serial.c
+Compiling hal_buffers.c
+Compiling hal_queues.c
+Compiling hal_mmcsd.c
+Compiling hal_pal.c
+Compiling hal_serial.c
+Compiling hal_spi.c
+Compiling hal_usb.c
+Compiling hal_flash.c
+Compiling nvic.c
+Compiling sk32_isr.c
+Compiling hal_lld.c
+Compiling hal_efl_lld.c
+Compiling hal_st_lld.c
+Compiling hal_pal_lld.c
+Compiling hal_serial_lld.c
+Compiling sk32_dma.c
+Compiling hal_spi_lld.c
+Compiling hal_sled_lld.c
+In file included from ../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:49:
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.h:65:6: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 65 | #if (HAL_USE_SLED == TRUE) || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+../../../../chibios-contrib/os/hal/ports/SK32/SK32F0xx/hal_sled_lld.c:51:5: warning: "HAL_USE_SLED" is not defined, evaluates to 0 [-Wundef]
+ 51 | #if HAL_USE_SLED || defined(__DOXYGEN__)
+ | ^~~~~~~~~~~~
+Compiling stm32_dma.c
+Compiling hal_usb_lld.c
+Compiling board.c
+Compiling osal.c
+Compiling chsys.c
+Compiling chrfcu.c
+Compiling chdebug.c
+Compiling chtrace.c
+Compiling chvt.c
+Compiling chschd.c
+Compiling chinstances.c
+Compiling chthreads.c
+Compiling chregistry.c
+Compiling chsem.c
+Compiling chmtx.c
+Compiling chcond.c
+Compiling chevents.c
+Compiling chmsg.c
+Compiling chdynamic.c
+Compiling chmboxes.c
+Compiling chmemcore.c
+Compiling chmemheaps.c
+Compiling chmempools.c
+Compiling chpipes.c
+Compiling chobjcaches.c
+Compiling chdelegates.c
+Compiling chfactory.c
+Compiling chcore.c
+Compiling main.c
+Linking build/spi.elf
+Creating build/spi.hex
+Creating build/spi.bin
+Creating build/spi.dmp
+
+ text data bss dec hex filename
+ 10588 4 8184 18776 4958 build/spi.elf
+Creating build/spi.list
+
+Done
diff --git a/testhal/SK32/SPI/cfg/chconf.h b/testhal/SK32/SPI/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/testhal/SK32/SPI/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/SPI/cfg/halconf.h b/testhal/SK32/SPI/cfg/halconf.h
new file mode 100644
index 0000000000..5869632a65
--- /dev/null
+++ b/testhal/SK32/SPI/cfg/halconf.h
@@ -0,0 +1,568 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT FALSE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL TRUE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI TRUE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB TRUE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/SPI/cfg/mcuconf.h b/testhal/SK32/SPI/cfg/mcuconf.h
new file mode 100644
index 0000000000..74148e7034
--- /dev/null
+++ b/testhal/SK32/SPI/cfg/mcuconf.h
@@ -0,0 +1,89 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for the
+ * USB IP, but the SK32 port provides native drivers for the entire core
+ * platform and for every peripheral, so no shared STM32 low level driver
+ * (TIMv1, USARTv2, ADCv1, SPIv2, I2Cv2, ..., nor the CANv1/DACv1/DMAv1/
+ * RTCv2/xWDGv1 stubs) is linked in. This mcuconf.h only needs to satisfy
+ * the single SK32 naming scheme:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST/peripheral settings consumed by
+ * the native SK32 HAL low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches, USART2 is mapped on PA2/PA3 (alternate 1)
+ * as documented in board.h.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 TRUE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ * The SK32 native interrupt-driven SPI driver is selected through the
+ * SK32_SPI_USE_SPIx switches.
+ *
+ * SPI1 self-loopback test:
+ * - SPI1 master on PA7 (SPI1_MOSI, alternate 12) and PA6 (SPI1_MISO,
+ * alternate 12), the two pads are shorted on the board.
+ */
+#define SK32_SPI_USE_SPI1 TRUE
+#define SK32_SPI_USE_SPI2 FALSE
+#define SK32_SPI_SPI1_PRIORITY 3
+#define SK32_SPI_SPI2_PRIORITY 3
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+#endif /* MCUCONF_H */
\ No newline at end of file
diff --git a/testhal/SK32/SPI/main.c b/testhal/SK32/SPI/main.c
new file mode 100644
index 0000000000..d56512c4c1
--- /dev/null
+++ b/testhal/SK32/SPI/main.c
@@ -0,0 +1,150 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+#include "ch.h"
+#include "hal.h"
+
+/*===========================================================================*/
+/* SPI1 self-loopback test on the GENERIC_SK32_F077 board. */
+/*===========================================================================*/
+
+/*
+ * Test resources used by this test:
+ *
+ * - PA7 (SPI1_MOSI) and PA6 (SPI1_MISO), both on alternate function 12.
+ * The two pads are shorted on the board so the master receives back on
+ * MISO the very byte it shifts out on MOSI (self-loopback, the SCK clock
+ * is generated internally by the SPI1 master).
+ * - PB14 / PB15: two board LEDs wired to push-pull outputs, the PAL
+ * abstraction drives them high (PAL_HIGH) to switch the LEDs on.
+ *
+ * Test procedure:
+ * - 256 bytes (0x00 .. 0xFF) are exchanged over SPID1 (SPI1 master, CPOL=0
+ * CPHA=0, 8 bits, 72MHz/32 = 2.25MHz) in one synchronous
+ * @p spiExchange() and the received buffer must equal the transmitted
+ * one.
+ * - The test round is repeated forever; PB15 is toggled after every
+ * successful round (visible blinking) while PB14 provides a 1 Hz
+ * heartbeat.
+ * - On the first mismatch both LEDs are switched on solid and the system
+ * halts.
+ */
+
+/* Blinker thread, times are in milliseconds. */
+static THD_WORKING_AREA(waBlinkThread, 128);
+static THD_FUNCTION(BlinkThread, arg) {
+ (void)arg;
+ chRegSetThreadName("led-blink");
+ while (true) {
+ palSetPad(GPIOB, GPIOB_PIN14);
+ chThdSleepMilliseconds(500);
+
+ palClearPad(GPIOB, GPIOB_PIN14);
+ chThdSleepMilliseconds(500);
+ }
+}
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+
+ static const SPIConfig spi1_cfg = {
+ .end_cb = NULL,
+ .ssport = GPIOA, /* Dummy, no slave select used. */
+ .sspad = 0U,
+ .cr1 = SK32_SPI_BR_PCLK_DIV32,
+ .cr2 = 0U
+ };
+ uint8_t txbuf[256];
+ uint8_t rxbuf[256];
+ bool led = false;
+ unsigned i;
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ /* Configuring the PB14/PB15 LED lines as push-pull outputs, initially off. */
+ palSetPadMode(GPIOB, GPIOB_PIN14, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, GPIOB_PIN15, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palClearPad(GPIOB, GPIOB_PIN15);
+
+ /*
+ * Creating the LED blinker thread.
+ */
+ chThdCreateStatic(waBlinkThread, sizeof(waBlinkThread), NORMALPRIO,
+ BlinkThread, NULL);
+
+ /*
+ * SPI1 pads setup on alternate function 12:
+ * - PA7 = SPI1_MOSI (master output).
+ * - PA6 = SPI1_MISO (master input, connected to MOSI through the
+ * loopback wire).
+ * The SCK pin is left unconfigured, the serial clock is generated
+ * internally by the master and both data pads are driven/sampled
+ * synchronously with it.
+ */
+ palSetPadMode(GPIOA, 7U, PAL_MODE_ALTERNATE(12) |
+ PAL_SK32_OSPEED_MID);
+ palSetPadMode(GPIOA, 6U, PAL_MODE_ALTERNATE(12) |
+ PAL_SK32_OSPEED_MID);
+
+ /* Filling the transmit buffer with the 0x00..0xFF pattern.*/
+ for (i = 0U; i < sizeof(txbuf); i++) {
+ txbuf[i] = (uint8_t)i;
+ }
+
+ /* Starting the SPI driver (enables the SPI1 clock and the NVIC vector,
+ then programs CR1/CR2 in master mode with software slave management).*/
+ spiStart(&SPID1, &spi1_cfg);
+
+ /*
+ * Running the loopback test forever.
+ */
+ while (true) {
+ /* Synchronous full-duplex exchange, RXNE/TXE interrupt driven.*/
+ spiExchange(&SPID1, sizeof(txbuf), txbuf, rxbuf);
+
+ /* Verifying the received buffer.*/
+ for (i = 0U; i < sizeof(txbuf); i++) {
+ if (rxbuf[i] != txbuf[i]) {
+ palSetPad(GPIOB, GPIOB_PIN14);
+ palSetPad(GPIOB, GPIOB_PIN15);
+ chSysHalt("SPI1 loopback FAIL");
+ }
+ }
+
+ /* Signaling a successful round with a slow PB15 blink.*/
+ if (led) {
+ palClearPad(GPIOB, GPIOB_PIN15);
+ led = false;
+ }
+ else {
+ palSetPad(GPIOB, GPIOB_PIN15);
+ led = true;
+ }
+ chThdSleepMilliseconds(250);
+ }
+}
diff --git a/testhal/SK32/SPI/spi.hex b/testhal/SK32/SPI/spi.hex
new file mode 100644
index 0000000000..13c92577cd
--- /dev/null
+++ b/testhal/SK32/SPI/spi.hex
@@ -0,0 +1,666 @@
+:020000040800F2
+:100000000004002091010008412500089301000828
+:100010009301000893010008930100089301000870
+:100020009301000893010008930100089301000860
+:10003000930100089301000893010008D10F000804
+:100040009301000893010008930100089301000840
+:100050009301000893010008930100089301000830
+:100060009301000893010008930100089301000820
+:100070009301000893010008930100089301000810
+:100080009301000893010008930100089301000800
+:1000900093010008930100089301000893010008F0
+:1000A00093010008B11200089301000893010008B1
+:1000B000911200089301000893010008A11600089E
+:1000C00072B6244880F30888234880F30988022008
+:1000D00080F31488BFF36F8F00F06AF901F0E0FD40
+:1000E0001E481F491B4A914202DA08600431FAE7B0
+:1000F0001C49194A914202DA08600431FAE71A49A8
+:100100001A4A1B4B9A4204DA086810600431043220
+:10011000F8E700201749184A914202DA08600431D2
+:10012000FAE700F05DF900F04BF9144C144DAC42C5
+:1001300003DA216888470434F9E702F059FA114CD0
+:10014000114DAC4203DA216888470434F9E70F49BE
+:1001500008470000000400200008002055555555B0
+:1001600000000020000400205C2900080008002096
+:100170000408002008080020A00C0020C00000088F
+:10018000C0000008C0000008C0000008D10300083B
+:1001900096E700F000F8FEE7F0B544464D465646B7
+:1001A0005F46F0B46B46CB60C3689D46F0BCA0468A
+:1001B000A946B246BB46F0BD62B6281CA047002047
+:1001C00002F066F8FEE701F05BFF024A024B1360A3
+:1001D000FEE7C04604ED00E000000080002934D0B6
+:1001E0000123002210B488422CD301242407A14209
+:1001F00004D2814202D209011B01F8E7E400A142C6
+:1002000004D2814202D249005B00F8E7884201D360
+:10021000401A1A434C08A04202D3001B5C08224338
+:100220008C08A04202D3001B9C082243CC08A042A9
+:1002300002D3001BDC082243002803D01B0901D095
+:100240000909E3E7100010BC704701B5002000F079
+:100250000BF802BD0029F8D003B5FFF7C1FF0EBCB3
+:100260004243891A1847C0467047C046F0B505009A
+:10027000830749D0541E002A45D003000326CAB282
+:1002800002E00135013C3ED301332A703342F8D1FC
+:10029000032C30D9FF220A401502AD182A04AD18EC
+:1002A0000F2C34D92700103F3F093E01B4461E00F1
+:1002B0001A0010366644156055609560D56010329E
+:1002C0009642F8D101373F01DB190F270C22274056
+:1002D00022403C00002A0ED03E1FB608B400A446BF
+:1002E0001A001C1D644420C2A242FCD10324013622
+:1002F000B6009B193C40002C05D0C9B21C191970DE
+:1003000001339C42FBD1F0BD03001400C0E727007D
+:10031000E2E7C046F0B505000F2A33D903240300F5
+:1003200026000B431E401C4237D11500103D2D09FD
+:1003300003000C00AC462F013F1865685D60A5689E
+:100340009D60E568DD60256810341D601D00103378
+:10035000BD42F2D165460F230C2401352D01491908
+:100360001340451914421AD08C59AC5104369C1BC9
+:10037000032CF9D8043B9B0801339B00ED18C918E6
+:1003800003231A40561E002A06D00023CC5C1A0014
+:10039000EC5401339642F9D1F0BD561EF5E71A0030
+:1003A000F0E7C04600000000000000000000000070
+:1003B0007047C04600000000000000000000000080
+:1003C0007047C04600000000000000000000000070
+:1003D000FEE7C04600000000000000000000000032
+:1003E00070B5114C250080356068A2682168904284
+:1003F0000AD2030040C940C39342FBD3013A131A07
+:100400009B0801339B00C018E368834207D9013B76
+:100410001A1A9208013200219200FFF727FF1034C8
+:10042000AC42E1D170BDC0461027000800000000BA
+:1004300010B500F06DFD00F0F3FD00F0D9F900F00B
+:100440001FFA00F08DFA01F093FC00F001F810BDE6
+:1004500010B500F0CDFD10BD000000000000000050
+:10046000F0B5C646836804000F00150000B593423E
+:1004700000D28568A1692669761AB5421BD30CD8CB
+:100480002A003800FFF746FFE268A36828005B1BDC
+:10049000A261A36080BCB846F0BD32003800FFF70F
+:1004A00039FFAB1B98461A00E168B819FFF732FF15
+:1004B000E2684244E9E72A003800FFF72BFFA2690F
+:1004C0005219E2E7000000000000000000000000F8
+:1004D000F0B5C646836804000F00150000B59342CE
+:1004E00000D2856860692669361AB5421BD30CD8DC
+:1004F0002A003900FFF70EFFE268A36828005B1BA3
+:100500006261A36080BCB846F0BD32003900FFF7DD
+:1005100001FFAB1B98461A00E068B919FFF7FAFE15
+:10052000E2684244E9E72A003900FFF7F3FE626916
+:100530005219E2E700000000000000000000000087
+:1005400010B50024C361029BC160816141618918BB
+:100550000060406084600161036210BD0000000023
+:100560004369826910B5934211D08268013282607A
+:100570005A1C42611970426903699A4204D20021EF
+:1005800001F0EEFE002010BDC3684361F7E782680A
+:10059000002AEAD001204042F5E7C04600000000F2
+:1005A00070B505000C0072B605E02100280001F0CE
+:1005B000BFFE002816DBAB68002BF6D0AB68AA693B
+:1005C000013BAB60531CAB6114782A69934201D3A1
+:1005D000EB68AB61EB69002B01D02800984762B64D
+:1005E000200070BD62B6FCE70000000000000000C3
+:1005F000F0B5D6464F4646469A46C369C0B59146C1
+:1006000007000E00984672B6151E11D02A00310060
+:100610003800FFF725FF041E11D04346002B01D000
+:100620003800C04762B62D1B361972B6002DEDD1C9
+:10063000484662B6E0BCBA46B146A846F0BD51464F
+:10064000380001F075FE0028E0D04B46581BF0E75B
+:10065000C361009BC1608161416189180060406095
+:10066000826001610362704700000000000000002A
+:1006700070B504000E00150072B605E029002000D8
+:1006800001F056FE002817DBA368002BF6D0A36804
+:10069000013BA36063695A1C62611E706269236931
+:1006A0009A4201D3E3686361E369002B01D0200023
+:1006B000984762B6002070BD62B6FCE700000000FB
+:1006C0008369426910B59A4210D082680132826013
+:1006D0005A1C82611C7803699A4204D2002101F0FD
+:1006E0003FFE200010BDC3688361F7E78268002ADF
+:1006F000EBD001204042F5E70000000000000000C0
+:10070000F0B5D6464F4646469A46C369C0B59146AF
+:1007100007000E00984672B6151E11D02A0031004F
+:100720003800FFF7D5FE041E11D04346002B01D040
+:100730003800C04762B62D1B361972B6002DEDD1B8
+:10074000484662B6E0BCBA46B146A846F0BD51463E
+:10075000380001F0EDFD0028E0D04B46581BF0E7D3
+:1007600010B50C30FFF744FF10BDC046000000007C
+:10077000012310B50C305B42FFF73AFF10BDC046B5
+:1007800010B53030FFF7BCFF10BDC04600000000C0
+:10079000012310B530305B42FFF7B2FF10BDC046F9
+:1007A00010B50C30FFF7FCFE10BDC0460000000085
+:1007B000012110B50C304942FFF7F2FE10BDC046D2
+:1007C00010B53030FFF754FF10BDC04600000000E8
+:1007D000012210B530305242FFF74AFF10BDC0462B
+:1007E00001394B425941142049420840143870479E
+:1007F00010B500F0ADFC10BD0000000000000000CE
+:1008000030B5150002000B000C4983B002C2040091
+:10081000426001000122543102720C3000940F3208
+:10082000FFF78EFE210020002B00102264313030B3
+:100830000094FFF70DFF03B030BDC04690270008BD
+:10084000436970B504000D00002B07D0200029007B
+:100850000C30FFF785FE00280BDB70BD042104304F
+:1008600001F096FD200029000C30FFF779FE0028EA
+:10087000F3DA8021201D090101F08AFDEDE7C04671
+:1008800010B500F095FD10BD000000000000000054
+:10089000012310B503700023436083600C3001F026
+:1008A0006FFD10BD0000000000000000000000000F
+:1008B00010B5040072B6416000F08AFD0223237077
+:1008C00062B6002010BDC04600000000000000001D
+:1008D00010B5040072B603202070200000F0A0FDC7
+:1008E000200001210830494201F002FD62B610BD2E
+:1008F00010B50400002101F08BF90021200001F067
+:1009000067F963681B68002B02D006212000984716
+:10091000A368044A1340A36000230622A367703330
+:10092000E25410BDFEFFFEFF0000000000000000CA
+:10093000822310B50400C25C0833C25401F000F8F1
+:1009400063681B68002B02D0012120009847032315
+:10095000237010BD00000000000000000000000037
+:1009600010B500F095FF10BD000000000000000071
+:1009700070B501230025040045600370402200216A
+:100980003030FFF773FCA56070BDC046000000006A
+:10099000022370B5002503708633C550040024225D
+:1009A0000021856785600C30FFF760FC7023200014
+:1009B000E55400F07DFF63681B68002B02D0002126
+:1009C0002000984770BDC0460000000000000000F5
+:1009D000037810B50400052B0BD08C22835405231B
+:1009E000037043681B68002B01D004219847002343
+:1009F000A36010BD00000000000000000000000027
+:100A0000037810B5052B00D010BD8733C35C03708D
+:100A100043681B68002BF7D005219847F4E7C046D0
+:100A2000702370B5C25C0400002A06D08268C149F8
+:100A30000A40826000228267C25422000021200006
+:100A4000803200F0FDFF63689B68002B2DD02000F2
+:100A50009847002829D0A16F8623E25C23008633C3
+:100A60005B781B0213438B4200D3A1E08022A36773
+:100A7000A25C52B2002A4FDB002B00D18AE0702327
+:100A80001522E25472B601206389A26F0343638189
+:100A9000E368616F9B6920001A60002299605A60C8
+:100AA000002100F0EDFF62B635E0602280251600DF
+:100AB000605D064002422BD181237F22E35C02402D
+:100AC0001B0213439C4A934200D1F6E03CD880229B
+:100AD0005200934200D1A0E000D96DE0012B00D17B
+:100AE000E9E0022B00D1A4E022008832002B0FD1D4
+:100AF0000223A367002302216267E367ACE7902229
+:100B00001201934200D1A5E08C4A934200D1D2E079
+:100B10002000FFF7EDFE70BD002B27D070230922C7
+:100B2000E25472B601202389A26F03432381E36854
+:100B3000616F5B691A6000229960002120005A6091
+:100B400000F0C6FF62B6E6E780221201934200D1B0
+:100B5000C7E0D4D8A022D200934240D1784A0023E3
+:100B6000E2676367A36740B2002813DA7023142298
+:100B7000E25472B60122638900211343002263818B
+:100B8000E36820009B699A601A605A6000F078FF61
+:100B900062B6C0E770230B22E25472B601222389A9
+:100BA000134300222381E3685B699A601A60C4E7FB
+:100BB0008023E25C0B005CE781225200934262D00A
+:100BC000FF32FF329342A3D18223E35C012B9FD1FA
+:100BD00088220221A35A0B43A3520022BFE7594A9D
+:100BE00094466344012B00D992E78423E25C2300FE
+:100BF00084335B7883211B02134382226068A25CEA
+:100C00004568615C2000A847002800D180E7436860
+:100C100001686367A167E6671EE78223E35C012B37
+:100C200000D075E788220221A35A8B43A3520022E9
+:100C300095E784230F21E25CE35620001140002B4E
+:100C400057DB00F0ADFE012857D0022800D05FE747
+:100C50003D4A4DE72378042B51D08223E35C002BDF
+:100C60000AD08B22A3540423237063681B68002BD3
+:100C700002D002212000984700236367A367E3673F
+:100C80008033E05C6FE78223E35C002B00D03FE71A
+:100C90000F2211008433E35C19401A4206D05BB284
+:100CA0002000002B5CDB00F0CBFF605D002363675E
+:100CB000A367E36757E7254A1AE78223E35C002B23
+:100CC00000D025E70F2211008433E35C19401A425B
+:100CD000ECD05BB22000002B3EDB00F079FF605DC2
+:100CE000E4E723008B33636701230121A367E667F1
+:100CF000B2E600F07DFE0128A7D1154AF8E672B6EB
+:100D00002000A368134A00211340A3602022103062
+:100D1000FFF7ACFA200000F023FE62B68B23E65406
+:100D2000883B237063681B68002B96D0032120004A
+:100D3000984792E7FEFFFEFF02030000020C00004E
+:100D40003109000800FAFFFF6C280008702800082D
+:100D5000682800080100010000F05AFF605DA5E767
+:100D600000F086FF605DA1E70000000000000000C9
+:100D7000702310B5C35C0400152B20D8244A9B00B7
+:100D8000D3589F468623E25C230086335B78806FCE
+:100D90001B021343984223D370231422E25472B6E9
+:100DA000012263890021134300226381E36820004C
+:100DB0009B699A601A605A6000F062FE62B610BDCC
+:100DC000FFF796FDFBE7C36F002B00D09847A368A1
+:100DD000104A1340A36070220023A367A354EEE7D8
+:100DE000E368198AFFF736FA0029D5D172B60122D5
+:100DF0002389200013432381E3685B6999601960AC
+:100E0000596000F065FE62B670230A22E254D6E70C
+:100E1000B8270008FEFFFEFF0000000000000000F1
+:100E20007023C35C10B50400152B05D8154A9B0030
+:100E3000D3589F46FFF75CFD10BD70230B22C254B0
+:100E400072B6012203890021134300220381C36883
+:100E50005B699A601A605A6000F03AFE62B6EBE78E
+:100E6000C3689B695B68002BE6D1C36F002B00D081
+:100E70009847A368044A1340A36070220023A36725
+:100E8000A354D9E710280008FEFFFEFF0000000071
+:100E90000F4A70B594460322FF25C0260240D200B7
+:100EA00095408901914083089B006344B6009C599A
+:100EB0001F22AC430C439C51012302409340C022AB
+:100EC00040098000604452008350036070BDC046FA
+:100ED00000E100E000000000000000000000000051
+:100EE0000F2310B50340040003202040FF24074ACD
+:100EF000C0009446844089018140083B9B089B00C8
+:100F00006344DA69A2430A43DA6110BD00ED00E0F0
+:100F1000064B074A02219A62002249429A62D9602E
+:100F20000131DA6019611A617047C0460010024051
+:100F3000FFFF81FF00000000000000000000000033
+:100F400001211D4A13680B431360013113681942D4
+:100F5000FCD0032153688B435360174A09310800C2
+:100F6000536818401942FAD1FD2311680B401360F1
+:100F7000E4235B035060D06210635063D06253601F
+:100F8000802311685B040B43802113600A4A8904A3
+:100F900013680B42FCD01221084B196053681039BA
+:100FA0000B4353600C2204494B681340082BFBD1C0
+:100FB000034B044A1A6070470010024000200240B0
+:100FC0000008002000A24A04000000000000000009
+:100FD00010B5744672B600F003FF62B6200001F04F
+:100FE000C7FA10BD00000000000000000000000073
+:100FF000064B074A10B55A6000229A600732022158
+:101000000B201A60FFF76CFF10BDC04610E000E037
+:101010001F1C000000000000000000000000000095
+:101020007047C04600000000000000000000000003
+:10103000F0B54E46032457464546DE46E0B5270048
+:10104000530917405607D508520525401C40130F79
+:10105000012285B0039391460023A4460297F60F20
+:10106000009717E05C4607682740009C274307600D
+:10107000072B52D85446076A3A4022430262490875
+:1010800044D0624692009446009A76009200AD00E9
+:10109000009201334A460A42F1D007274C46039A90
+:1010A0001F40BF00BA409C4092468288A243324310
+:1010B00092B28280846801945C00A0460324424678
+:1010C0009440E24393462200A046019C94432200B0
+:1010D00044462A438260C268A24364462243C260F7
+:1010E0000F22BA40029CD243022CBBD1072B1AD844
+:1010F000076A17405246174307625C460268490870
+:101100002240009C224302600029BAD105B0F0BC05
+:10111000BB46B246A946A046F0BD5446476A3A408F
+:1011200022434262ABE7476A174052461743476281
+:10113000E3E7C046000000000000000000000000DF
+:10114000C021024A93890B439381704700440040B9
+:1011500010B5074C074A20000021FFF751FB064B52
+:1011600003216367054B1C20A367FFF791FE10BDA9
+:1011700008080020411100080044004000A24A0471
+:10118000F8B5CE464746456F06002B8880B59CB221
+:10119000DB0552D472B62F23234234D0203B98462D
+:1011A0000E3B9946202708E0A98889B227421FD123
+:1011B0002F222B889CB21A4225D043462342F3D0DB
+:1011C00049464A462140E3084942134039401B0240
+:1011D0001943A30701D540231943630701D5802391
+:1011E0001943301D01F0D4F8A98889B22742DFD015
+:1011F0007C23F35C30001940FFF722FB2F222B8861
+:101200009CB21A42D9D162B68022AB8990469FB275
+:101210001A4201D022421AD1402398463B4207D0BD
+:10122000234205D072B6726CB36C9A421AD062B681
+:10123000AF81C0BCB946B046F8BD72B6802104305B
+:10124000C90001F0A5F8104B2B8062B6A2E772B678
+:1012500030003030FFF734FA00280DDB80B2A88070
+:1012600062B6D9E7B36B002BE1D01021301D01F03D
+:101270008FF843469F43DAE70821301D01F088F8D4
+:1012800043469F43ECE7C046FFFEFFFF000000001F
+:1012900010B574460348FFF773FF200001F068F9AA
+:1012A00010BDC0460808002000000000000000003B
+:1012B000F8B54F464646C0B436487546C1698A8877
+:1012C0000B8994B29BB2920602D560221A4231D1A8
+:1012D000620615D5DA0713D58E89476AB2B29046F1
+:1012E000C26A9146002F05D00E88360548D55600B3
+:1012F0004246F2534A46066B0132C262B24229D2DA
+:10130000802222420FD09B070DD5836A046BA34233
+:1013100032D2026A002A3BD00C88240535D4D25C34
+:1013200001338A818362280001F022F9C0BCB946EA
+:10133000B046F8BD5A0600D58A899B0602D50B88AF
+:101340009BB20B80E0228B88134893438B8000F084
+:1013500037FDE8E7E0228B8893438B8002230370FC
+:1013600043681B68002B00D0984772B600210B48D9
+:1013700000F0CEFF62B6D6E78B8893438B80D2E72E
+:101380004646BE54C26A9146B4E75C00A25AC7E71B
+:10139000034AC5E788080020742800089008002048
+:1013A000FFFF00000000000000000000000000003F
+:1013B00010B5054C2000FFF76BFA044B03211920F0
+:1013C000E361FFF765FD10BD880800200030014093
+:1013D0000378012B15D04021C3691A888A431A80EB
+:1013E000C12241689200888902431A80CA89E32198
+:1013F0008A439A801A88A3390A431A801A899B89DA
+:101400007047054B9842E6D18022044B52019969FE
+:101410000A439A619B69DEE78808002000100240B9
+:10142000436200230163E0210262C2698362C362F6
+:1014300093880B4393807047000000000000000079
+:101440000100F0B5D64646464F468B1C57489A4693
+:101450009B00C318C0B55E68B269002956D101244B
+:10146000534B5F7C3C424CD047893C4200D18BE0DF
+:101470009F7DDC7D936824023C43002B00D168E013
+:10148000002C00D18AE00125AC461D19A946494D22
+:10149000A846884447463F781F7001339945F9D1E3
+:1014A000936800294CD1002B00D17DE0738A404F16
+:1014B000A34260D89389012B5DD040257B7C2B43D0
+:1014C0007B749368002B01D01B19936017685368D5
+:1014D0003F1B1B19536093891760013B9BB293819B
+:1014E000728AA24201D8002B0BD1624643899343F2
+:1014F000438153469D0045196B68DB68002B00D083
+:101500009847E0BCBA46B146A846F0BD0124284F32
+:10151000A0463B7D1C42F4D023008B409C46644691
+:1015200043891C4235D0BB7DFC7D24021C4393685B
+:1015300006D0002BA9D145463B7DAB433B75C5E7A3
+:101540001B4F3D7DB8460127BD432F0045462F75F3
+:10155000B8E713681F1E24D0002C1FD00125AC460D
+:101560002700002BA2D14825114C637C2B436374C8
+:101570003C000027ABE748257B7C2B437B7401278D
+:101580000D4B1F7093689DE740215A7C0A435A74A3
+:10159000B7E742463B7D93433B75B2E701239C4648
+:1015A00084E701239C46DEE71368D9E7D8080020CA
+:1015B000005C0040205C0040BC08002000000000EF
+:1015C0000100F8B5CE46474630488B1C9B00C31837
+:1015D0005B6880B59C465A69002904D004242C4BD2
+:1015E0005D7C2C423FD101238B4005892B420FD0DB
+:1015F000D4681668A14654684C445460371BA64210
+:101600000AD19D4363469B680581002B00D0984713
+:10161000C0BCB946B046F8BD63461B8ABC46984676
+:101620009F422AD864469368D4604B449360002C50
+:1016300007D0184A1D198C181A78013322709D4260
+:10164000FAD1002913D1474519D8C3681B8A9C4594
+:1016500003D38623C35A9E4211D30A210C4A537CDA
+:101660000B435374D4E75D7CA5435D74BBE7012154
+:10167000074A537C0B435374CAE79C46D2E70221C6
+:10168000034A537C0B435374C2E7C046D80800207A
+:10169000005C0040205C00400000000000000000F2
+:1016A000F0B5564644465F464D46F0B4744BF2469C
+:1016B0009A7983B0D4B2520700D583E0714DE30725
+:1016C00053D4A3074BD4240705D56B68DB68002BE4
+:1016D00001D0280098476A4B9A78DF7917401A7929
+:1016E000597A11409A7B8846D2B2019201223A423D
+:1016F00041D101239946624B2E0001249B463B00B9
+:101700004A4623411A4207D03369002B04D05B4676
+:10171000E0B29873FFF754FF01340436082CEED181
+:101720000126574F073C434623411E4206D02B69F2
+:10173000002B03D0E0B2B873FFF782FE013404350A
+:10174000082CF0D14E4B019A50469A7300F010FFCE
+:1017500003B0F0BCBB46B246A946A046F0BD280087
+:10176000FFF74EF92407B6D5AFE72800FFF730F9A9
+:10177000A307A8D5F3E700219C7B9973597CE4B2B9
+:10178000C8B2C9063AD404210240014202D0587CB2
+:1017900088435874002A55D07023EB5CEA68002B0C
+:1017A00036D0062B34D0364E9069B17DF37D1B02C6
+:1017B0000B43082B02D18368002B47D00020FFF792
+:1017C0003FFE37E00122987BDA71002201211A7274
+:1017D000C0B25A729A7299730131C9B21A74DA742A
+:1017E0009A745A755A741A750829F4D19873254851
+:1017F000FFF7CEF8504600F0BBFEA9E78026597CE3
+:101800000240314359740421014216D1002A19D0F3
+:1018100001231D4E00213370EB6828005B68984758
+:101820003378002B06D04021154A537C0B43537468
+:1018300000233370124B9C735BE7587C88435874C9
+:10184000002AE5D10020FFF7BBFEF3E703689B46C3
+:10185000002BB3D10C4B28009946012349460B704D
+:101860005368002198474B461B78002BE2D0402359
+:10187000727C134373745A464B461A70DAE7C046BB
+:10188000005C0040D8080020BD08002000000000D7
+:10189000002210B5024B03481A70FFF769F810BD1B
+:1018A000BD080020D8080020000000000000000053
+:1018B000002230B501241D4B1D491A700A701D49C4
+:1018C0000A705978214359700121D9711A725A72DC
+:1018D0009A724368002B27D0DB685A1E9341DB00C5
+:1018E0000733124AD3720023917B9373537CC9B29E
+:1018F000DCB2DB0703D54025537C2B435374E4064D
+:1019000004D58024094A537C23435374074B0A4A65
+:1019100099730021C26001209A7B9973D979D2B260
+:101920000143D9719A7330BD0723DAE7005C0040A8
+:10193000BD080020BC080020B428000800000000FA
+:101940008A23C25C0B3B1340014A13707047C046A8
+:10195000005C0040000000000000000000000000EB
+:10196000002270B5012101250C4B9E7B9A73F6B2C3
+:101970002C008C40C8B29873D8790131A043D8713B
+:10198000587AA04358721A74DA749A745A755A7451
+:101990001A750829ECD19E7370BDC046005C0040EA
+:1019A0008B1C9B00C0184368002B18D09B69002B30
+:1019B00015D00C4A937B9173DBB2002908D020200C
+:1019C000127D1040424250410130064A93737047E5
+:1019D0002020527C1040424250410130F5E7002067
+:1019E000F5E7C046005C0040000000000000000079
+:1019F0008B1C9B00C0184368002B17D05B69002B21
+:101A000014D00B4A937B9173527CDBB2002907D030
+:101A100010201040424250410130054A93737047F4
+:101A200020201040424250410130F6E70020F6E706
+:101A3000005C00400000000000000000000000000A
+:101A400030B5140020200B4908340B5C13700132B0
+:101A5000A242FAD1084B1A78002A09D00024402566
+:101A60008A7B8C73487CD2B2284348748A731C707A
+:101A700030BDC046005C0040BD08002000000000F2
+:101A80008B1C9B00C018426870B595690C002868D3
+:101A90000123002805D0518A40180138FEF79EFB2B
+:101AA00083B2AB81002C0AD00120A040044B997B6B
+:101AB0009C735A7AC9B20243D2B25A72997370BDFA
+:101AC000005C00400000000000000000000000007A
+:101AD000F8B58B1C4746CE469B00C3185B6880B5A3
+:101AE0009C465F693B68984663461C8A444500D91A
+:101AF0004446FC60002C10D12F4A937B9173DBB2DB
+:101B0000002937D001202C494A7C02434A742A4AD2
+:101B10009373C0BCB946B046F8BD274ABB6891462E
+:101B2000264AE6188D181A7801332A709E42FAD197
+:101B30004B464A469B7B9173DBB20029E2D1204998
+:101B40000A78002A29D141467A688A1A94421ED11D
+:101B5000C268128A944207D38622815C863042781A
+:101B600012020A43904512D30A2013494A7C0243C9
+:101B70004A74CCE712490D78002D0ED17021415CDA
+:101B80000B29E0D10A20517C01435174BFE70220A8
+:101B900009494A7C02434A74B9E700220A70064A9E
+:101BA0009373012203899343038163469B68002B4F
+:101BB000AFD000219847ACE7005C0040205C0040BB
+:101BC000BC08002000000000000000000000000031
+:101BD0000C4B9A7B9973D2B2002907D1597CC90763
+:101BE0000BD56020597C0143597403E02020197DF6
+:101BF00001431975034B9A7370472020597C0143A8
+:101C00005974F7E7005C004000000000000000008D
+:101C1000084B9A7B9973D2B2002906D12020597CB7
+:101C200001435974034B9A7370471020597C014348
+:101C30005974F7E7005C004000000000000000005D
+:101C40000A4B9A7B9973D2B2002906D12420597C81
+:101C500081435974054B9A7370476020197D814305
+:101C60001975197D203001431975F3E7005C0040B8
+:101C70000A4B9A7B9973D2B2002906D12420597C51
+:101C800081435974054B9A7370473020597C8143C6
+:101C90005974597C103001435974F3E7005C0040DB
+:101CA000FC22244BD203996A10B511439962996AB8
+:101CB0002148802401409962996A59691F480A4362
+:101CC0005A6190225B6900231D49D205938091607F
+:101CD0001C49D16021214942918288214901116228
+:101CE0001949536211600122184952428B808A605F
+:101CF000C8608A820B624B620C601549154C8B8060
+:101D00008A60CC608A820B624B620B6012498B80C6
+:101D10008A60C8608A8211480B624B620B6010496E
+:101D20008B808A60C8608A820B624B620B60FFF70F
+:101D300007F910BD00100240FFFF81FF55555555B2
+:101D40005FF7FFFF05515565A00400280004004817
+:101D50000008004855555501000C00485055555590
+:101D60000014004800000000000000000000000017
+:101D70007047C046000000000000000000000000A6
+:101D8000012310B5084C23700023636000F048FB6A
+:101D900000F086FB00F0A4FB0449054800F098F928
+:101DA0000223237062B610BDD80A0020D82800088C
+:101DB000480A0020000000000000000000000000B1
+:101DC00072B68822024B9850024B853A1A70FEE791
+:101DD000480A0020D80A002000000000000000008F
+:101DE00010B500F03DF810BD00000000000000003C
+:101DF00010B5036100230C4C43612369C2609A68EB
+:101E0000914204D91B68891A9A688A42FAD35A689F
+:101E100081600360426010609A685860521A9A604C
+:101E200001235B42A36110BD480A002000000000AE
+:101E3000036881689A6852189A60426813605A6011
+:101E4000002301220360024B52429A617047C04650
+:101E5000480A002000000000000000000000000010
+:101E600070B51B4DEB692C690133EB612B0010330E
+:101E70009C4215D0A368013BA3600FE02268616813
+:101E80000A605160236062B620002169E3689847C8
+:101E900072B66369002B04D12C69A368002BEDD0C6
+:101EA00070BD2A6991688B4204D912685B1A9168E7
+:101EB0009942FAD35168A360616022600C60916816
+:101EC0005460CB1A936001235B42AB61E4E7C046E8
+:101ED000480A002000000000000000000000000090
+:101EE00072B62423CB5C0C2B0BD8114A9B00D35821
+:101EF0009F468A6A9368013393604A680B6813604F
+:101F00005A60012300225B428B622533CA548B69DD
+:101F100088681B689A688242FBD25A680B604A60E4
+:101F20001160596062B6704700228B6A1A60E8E758
+:101F3000EC28000800000000000000000000000085
+:101F400024230022C254836981681B689A688A42EC
+:101F5000FBD25A6803604260106058607047C04608
+:101F60002422074B10B5D968885418680468636048
+:101F70001C6001248454D860FEF70EF910BDC046E1
+:101F8000480A0020000000000000000000000000DF
+:101F900070B5164C0500E66886B04B1C19D03300AE
+:101FA0006846134AFFF724FF2423E1682068CD54D4
+:101FB0000268546022600122C254E060FEF7ECF82F
+:101FC000009B002B02D06846FFF732FFB06A06B0D4
+:101FD00070BD2423F05420683100026854602260F0
+:101FE0000122C254E060FEF7D7F8EFE7480A00206C
+:101FF000E11E0008000000000000000000000000DA
+:1020000070B5154C8162E368826899688A4214D978
+:10201000242200259D549A6912689568A942FBD331
+:1020200051681A6059600B60536001212422815469
+:102030001900E060FEF7B0F870BD24230021C15400
+:1020400083691B6899688A42FBD95A6803604260B9
+:1020500010605860F0E7C046480A00200000000009
+:10206000044BDA68196890688B6898428041404256
+:102070007047C046480A0020000000000000000031
+:102080000D4B10B51868D9680268012453601A60B6
+:10209000D8602422002384548B548B698C681B687D
+:1020A0009A68A242FBD85A680B604A601160596076
+:1020B000FEF772F810BDC046480A0020000000007C
+:1020C000FEE7C04600000000000000000000000025
+:1020D000F0B5002604001D4B87B0066381630D0038
+:1020E000586000F035FA23002833A362E362183BFE
+:1020F0002361636101235B42A361154BE661DB69E8
+:102100000027A660666321001E008823266267629E
+:10211000246064603C31E75020000E4A083B00F028
+:102120001FF86B68E06024220362012383540A4A8B
+:1021300003930092EA68094B01922A696846029269
+:102140000493059700F064F807B0F0BDD80A0020AA
+:10215000480A00202029000884280008C12000081F
+:1021600010B5040008008B604364022100238184C1
+:102170000364C363013926338461C154C261220000
+:10218000030028320261E26A103342611360E362A5
+:102190001C33C36204330363436310BD00000000BB
+:1021A0008368020018005168503830B50162190088
+:1021B00014697439C1600C61546902254C61114C79
+:1021C0000C621468D1681A002C3A81601580416451
+:1021D00000210D4A0164C163013D263182614554ED
+:1021E000C46119001400403928340C60D46A4C6072
+:1021F0002160D1621A00203B243A12601B605B60B0
+:1022000030BDC046B9010008480A002000000000A7
+:1022100010B5FFF7C5FFFFF793FE10BD00000000EB
+:1022200010B572B650394418210024392062E1609B
+:102230000B61029B20004B61124B0B620223A260D8
+:10224000626400220121A3840F4B2264E2632632E0
+:10225000A361A1540D4A1900E261220028312161D5
+:10226000D96A103261610A60DA6223002C33E362BA
+:102270000433002123636363FFF7C2FE62B62000CC
+:1022800010BDC046B9010008480A00208C2800088B
+:1022900070B572B6114BDC682500A062E06A2C357F
+:1022A000854207D12623E35C002B0ED00F20FFF7D9
+:1022B00057FE70BD0368E362FFF742FEE06AA84282
+:1022C000F8D12623E35C002BF0D12533E35C9B0798
+:1022D000ECD16269236913605A60E7E7480A00207D
+:1022E00010B5010072B60820FFF752FE62B610BDAD
+:1022F000064B10B5DB68002905D003609862032007
+:10230000FFF746FE10BD01204042FBE7480A0020CF
+:102310000300006810B5002804D000221A60816212
+:10232000FFF70EFE10BDC0460000000000000000D8
+:10233000084B10B5DB68002908D0426818605A6065
+:10234000136043600420FFF723FE10BD01204042CC
+:10235000FBE7C046480A0020000000000000000023
+:102360000300006810B5834205D002681A6053600C
+:102370008162FFF7E5FD10BD0000000000000000D5
+:1023800000230060406083607047C046000000008A
+:10239000F0B5C646046806000D0000B5A04233D073
+:1023A0000023242798460CE06068A368C26B13439F
+:1023B000C363C25D0A2A1DD00B2A28D02468A64216
+:1023C00022D0E3682B43E360002DEDD023692B423C
+:1023D000EAD12468B44217D0E3682B43E360236951
+:1023E0002B42F6D06068A368C26B1343C363C25D1F
+:1023F0000A2AE1D1826A1A42E0D043468362FFF79B
+:102400009FFD2468A642DCD180BCB846F0BD826A3C
+:1024100013409A42D2D1F0E7000000000000000013
+:10242000024B034A1A60034A5A607047E00A0020D0
+:10243000A00C002000200020000000000000000090
+:1024400070B5094C26686568AB1B984209D82B1AF1
+:1024500048421840821A964203D8954201D36260DE
+:1024600070BD0020FCE7C046E00A0020000000002C
+:1024700070B572B6094C26686568AB1B98420AD8DD
+:102480002B1A48421840821A964204D8954202D329
+:10249000626062B670BD0020FBE7C046E00A002023
+:1024A0000548064B10B503600023436083600C3081
+:1024B000FFF766FF10BDC046E80A0020712400083F
+:1024C00010B50024046041608260C36010BDC04646
+:1024D00010B50022FFF7B4FF10BDC0460000000099
+:1024E00070B5134C2000FFF74BFF23002000114D67
+:1024F0001033236104222B0014211430FFF7E0FF76
+:10250000230024336362230020002833A3620422C3
+:102510002B001C212C30FFF7D3FF23003C33E36357
+:1025200023004033236423004433636470BDC046FA
+:10253000040B0020D124000800000000000000006F
+:10254000EFF30983203383F3098862B67047C046EE
+:10255000024A0349136A0B401362704700ED00E022
+:10256000FFFF00FF0000000000000000000000006E
+:1025700010B50F3000D110BD72B6EFF30984203CC6
+:1025800084F3098880235B04E361FFF769FD002879
+:1025900002D0034BA361EEE7024BA361EBE7C04619
+:1025A000C7010008CA010008000000000000000088
+:1025B00070B58025084B094ADB68094CDA61ED01EA
+:1025C0000848A561FFF78CFE8023DB050548A36161
+:1025D000FFF786FEF4E7C046480A0020942800086A
+:1025E0000004004888130000000000000000000004
+:1025F000F0B5DE4657464E464546E0B53B4CA54451
+:10260000FDF716FFFFF7BCFB8021394C01222000AB
+:10261000C901FEF70DFD8021012220000902FEF70D
+:1026200007FD8023DB05A36180230022A8211B0670
+:10263000A36149002F4B00922F488032FFF7F0FD35
+:10264000902080212D4AC005FEF7F2FC9020402109
+:102650002A4AC005FEF7ECFC8021002304AA4900A9
+:102660001370013301328B42FAD12548254903907A
+:10267000FEF71EF900239B4602AB9C460826DD1D93
+:10268000214BFF369A461A4F6644802144AB03988B
+:1026900004AA4900FEF71CF98023DB019946802338
+:1026A0001B0201249846325D2B5D9A4206D04B46B0
+:1026B000BB6143465046BB61FFF782FB02230134F6
+:1026C000FF339C42EFD15B46002B08D080231B06D2
+:1026D000BB6100239B460D48FFF702FED5E7802330
+:1026E0001B02BB6101239B46F5E7C046ECFDFFFFE3
+:1026F00000040048B1250008500B00200A06000025
+:10270000880800204C290008A0280008C4090000FF
+:1027100060290008A00C0020A00C0020A00C0020C4
+:102720006029000800000000000000000000000018
+:102730006029000800000000000000000000000008
+:1027400060290008000000000000000000000000F8
+:1027500060290008000000000000000000000000E8
+:1027600060290008000000000000000000000000D8
+:1027700060290008000000000000000000000000C8
+:1027800060290008000000000000000000000000B8
+:10279000000000009107000871070008D107000839
+:1027A000B1070008C1070008A10700088107000859
+:1027B00061070008E1070008C00D0008BE0D000811
+:1027C000BE0D0008BE0D0008BE0D0008BE0D0008BD
+:1027D000C00D0008BE0D0008BE0D0008840D0008E5
+:1027E000980D0008C60D0008BE0D0008BE0D0008BB
+:1027F000BE0D0008BE0D0008BE0D0008BE0D00088D
+:10280000BE0D0008BE0D0008BE0D0008C00D00087A
+:10281000340E0008380E0008380E0008380E000884
+:10282000380E0008380E0008340E0008380E000874
+:10283000380E0008380E0008340E0008340E000868
+:10284000380E0008380E0008380E0008380E000850
+:10285000380E0008380E0008380E0008380E000840
+:10286000600E00083A0E00080100000000000000A1
+:1028700000000000535049206661696C7572650064
+:102880006330000069646C65000000006E6F6E616B
+:102890006D6500006C65642D626C696E6B000000F4
+:1028A00053504931206C6F6F706261636B204641F9
+:1028B000494C000000000000210A0008710D0008CA
+:1028C000210E000840004000C8080020C808002071
+:1028D00001000000C008002080280008000000005F
+:1028E0000000000068090020480A0020241F00089A
+:1028F000021F0008021F0008281F0008FA1E000817
+:10290000F21E0008021F0008FA1E0008021F00083D
+:10291000021F0008021F0008021F0008FA1E00081C
+:102920006D61696E002C063804044C080C10141CF0
+:1029300020242526000000000000240401000400DB
+:1029400000000000000C00102830340000000000DF
+:0C29500000000048000000002000000013
+:04295C0000A24A0487
+:04000005080001915D
+:00000001FF
diff --git a/testhal/SK32/TIM/Makefile b/testhal/SK32/TIM/Makefile
new file mode 100644
index 0000000000..59b21e7954
--- /dev/null
+++ b/testhal/SK32/TIM/Makefile
@@ -0,0 +1,188 @@
+##############################################################################
+# Build global options
+# NOTE: Can be overridden externally.
+#
+
+# Compiler options here.
+ifeq ($(USE_OPT),)
+ USE_OPT = -O2 -ggdb -fomit-frame-pointer -falign-functions=16
+endif
+
+# C specific options here (added to USE_OPT).
+ifeq ($(USE_COPT),)
+ USE_COPT =
+endif
+
+# C++ specific options here (added to USE_OPT).
+ifeq ($(USE_CPPOPT),)
+ USE_CPPOPT = -fno-rtti
+endif
+
+# Enable this if you want the linker to remove unused code and data.
+ifeq ($(USE_LINK_GC),)
+ USE_LINK_GC = yes
+endif
+
+# Linker extra options here.
+ifeq ($(USE_LDOPT),)
+ USE_LDOPT =
+endif
+
+# Enable this if you want link time optimizations (LTO).
+# NOTE: left at "no" because -flto + a toolchain path containing spaces
+# breaks the link on Windows (lto-wrapper -> make: Permission denied).
+ifeq ($(USE_LTO),)
+ USE_LTO = no
+endif
+
+# Enable this if you want to see the full log while compiling.
+ifeq ($(USE_VERBOSE_COMPILE),)
+ USE_VERBOSE_COMPILE = no
+endif
+
+# If enabled, this option makes the build process faster by not compiling
+# modules not used in the current configuration.
+ifeq ($(USE_SMART_BUILD),)
+ USE_SMART_BUILD = yes
+endif
+
+#
+# Build global options
+##############################################################################
+
+##############################################################################
+# Architecture or project specific options
+#
+
+# Stack size to be allocated to the Cortex-M process stack. This stack is
+# the stack used by the main() thread.
+ifeq ($(USE_PROCESS_STACKSIZE),)
+ USE_PROCESS_STACKSIZE = 0x400
+endif
+
+# Stack size to the allocated to the Cortex-M main/exceptions stack. This
+# stack is used for processing interrupts and exceptions.
+ifeq ($(USE_EXCEPTIONS_STACKSIZE),)
+ USE_EXCEPTIONS_STACKSIZE = 0x400
+endif
+
+# Enables the use of FPU (no, softfp, hard).
+ifeq ($(USE_FPU),)
+ USE_FPU = no
+endif
+
+# FPU-related options.
+ifeq ($(USE_FPU_OPT),)
+ USE_FPU_OPT =
+endif
+
+#
+# Architecture or project specific options
+##############################################################################
+
+##############################################################################
+# Project, target, sources and paths
+#
+
+# Define project name here
+PROJECT = tim
+
+# Target settings.
+MCU = cortex-m0
+
+# Imported source files and paths.
+CHIBIOS := ../../../../chibios
+CHIBIOS_CONTRIB := ../../../../chibios-contrib
+CONFDIR := ./cfg
+BUILDDIR := ./build
+DEPDIR := ./.dep
+BOARDDIR := $(CHIBIOS_CONTRIB)/os/hal/boards/GENERIC_SK32_F077
+
+# Licensing files.
+include $(CHIBIOS)/os/license/license.mk
+# Startup files.
+include $(CHIBIOS_CONTRIB)/os/common/startup/ARMCMx/compilers/GCC/mk/startup_SK32F0xx.mk
+# HAL-OSAL files (optional).
+include $(CHIBIOS)/os/hal/hal.mk
+include $(CHIBIOS_CONTRIB)/os/hal/ports/SK32/SK32F0xx/platform.mk
+include $(BOARDDIR)/board.mk
+include $(CHIBIOS)/os/hal/osal/rt-nil/osal.mk
+# RTOS files (optional).
+include $(CHIBIOS)/os/rt/rt.mk
+include $(CHIBIOS)/os/common/ports/ARMv6-M/compilers/GCC/mk/port.mk
+# Auto-build files in ./source recursively.
+include $(CHIBIOS)/tools/mk/autobuild.mk
+
+# Define linker script file here
+LDSCRIPT= $(STARTUPLD_CONTRIB)/SK32F077xB.ld
+
+# C sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CSRC = $(ALLCSRC) \
+ main.c
+
+# C++ sources that can be compiled in ARM or THUMB mode depending on the global
+# setting.
+CPPSRC = $(ALLCPPSRC)
+
+# List ASM source files here.
+ASMSRC = $(ALLASMSRC)
+
+# List ASM with preprocessor source files here.
+ASMXSRC = $(ALLXASMSRC)
+
+# Inclusion directories.
+INCDIR = $(CONFDIR) $(ALLINC)
+
+# Define C warning options here.
+CWARN = -Wall -Wextra -Wundef -Wstrict-prototypes
+
+# Define C++ warning options here.
+CPPWARN = -Wall -Wextra -Wundef
+
+#
+# Project, target, sources and paths
+##############################################################################
+
+##############################################################################
+# Start of user section
+#
+
+# List all user C define here, like -D_DEBUG=1
+UDEFS = -DSK32F077xB
+
+# Define ASM defines here
+UADEFS = -DSK32F077xB
+
+# List all user directories here
+UINCDIR =
+
+# List the user directory to look for the libraries here
+ULIBDIR =
+
+# List all user libraries here
+ULIBS =
+
+#
+# End of user section
+##############################################################################
+
+##############################################################################
+# Common rules
+#
+
+RULESPATH = $(CHIBIOS)/os/common/startup/ARMCMx/compilers/GCC/mk
+include $(RULESPATH)/arm-none-eabi.mk
+include $(RULESPATH)/rules.mk
+
+#
+# Common rules
+##############################################################################
+
+##############################################################################
+# Custom rules
+#
+
+#
+# Custom rules
+##############################################################################
diff --git a/testhal/SK32/TIM/build.log b/testhal/SK32/TIM/build.log
new file mode 100644
index 0000000000..282d1b5fb9
--- /dev/null
+++ b/testhal/SK32/TIM/build.log
@@ -0,0 +1 @@
+make: Nothing to be done for 'all'.
diff --git a/testhal/SK32/TIM/cfg/chconf.h b/testhal/SK32/TIM/cfg/chconf.h
new file mode 100644
index 0000000000..021df6818f
--- /dev/null
+++ b/testhal/SK32/TIM/cfg/chconf.h
@@ -0,0 +1,819 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file rt/templates/chconf.h
+ * @brief Configuration file template.
+ * @details A copy of this file must be placed in each project directory, it
+ * contains the application specific kernel settings.
+ *
+ * @addtogroup config
+ * @details Kernel related settings and hooks.
+ * @{
+ */
+
+#ifndef CHCONF_H
+#define CHCONF_H
+
+#define _CHIBIOS_RT_CONF_
+#define _CHIBIOS_RT_CONF_VER_7_0_
+
+/*===========================================================================*/
+/**
+ * @name System settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Handling of instances.
+ * @note If enabled then threads assigned to various instances can
+ * interact each other using the same synchronization objects.
+ * If disabled then each OS instance is a separate world, no
+ * direct interactions are handled by the OS.
+ */
+#if !defined(CH_CFG_SMP_MODE)
+#define CH_CFG_SMP_MODE FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name System timers settings
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System time counter resolution.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_ST_RESOLUTION)
+#define CH_CFG_ST_RESOLUTION 32
+#endif
+
+/**
+ * @brief System tick frequency.
+ * @details Frequency of the system timer that drives the system ticks. This
+ * setting also defines the system tick time unit.
+ */
+#if !defined(CH_CFG_ST_FREQUENCY)
+#define CH_CFG_ST_FREQUENCY 10000
+#endif
+
+/**
+ * @brief Time intervals data size.
+ * @note Allowed values are 16, 32 or 64 bits.
+ */
+#if !defined(CH_CFG_INTERVALS_SIZE)
+#define CH_CFG_INTERVALS_SIZE 32
+#endif
+
+/**
+ * @brief Time types data size.
+ * @note Allowed values are 16 or 32 bits.
+ */
+#if !defined(CH_CFG_TIME_TYPES_SIZE)
+#define CH_CFG_TIME_TYPES_SIZE 32
+#endif
+
+/**
+ * @brief Time delta constant for the tick-less mode.
+ * @note If this value is zero then the system uses the classic
+ * periodic tick. This value represents the minimum number
+ * of ticks that is safe to specify in a timeout directive.
+ * The value one is not valid, timeouts are rounded up to
+ * this value.
+ * @note Set to zero here because the SK32 port uses its native
+ * SysTick-based ST driver which operates in periodic mode.
+ */
+#if !defined(CH_CFG_ST_TIMEDELTA)
+#define CH_CFG_ST_TIMEDELTA 0
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel parameters and options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Round robin interval.
+ * @details This constant is the number of system ticks allowed for the
+ * threads before preemption occurs. Setting this value to zero
+ * disables the preemption for threads with equal priority and the
+ * round robin becomes cooperative. Note that higher priority
+ * threads can still preempt, the kernel is always preemptive.
+ * @note Disabling the round robin preemption makes the kernel more compact
+ * and generally faster.
+ * @note The round robin preemption is not supported in tickless mode and
+ * must be set to zero in that case.
+ */
+#if !defined(CH_CFG_TIME_QUANTUM)
+#define CH_CFG_TIME_QUANTUM 0
+#endif
+
+/**
+ * @brief Idle thread automatic spawn suppression.
+ * @details When this option is activated the function @p chSysInit()
+ * does not spawn the idle thread. The application @p main()
+ * function becomes the idle thread and must implement an
+ * infinite loop.
+ */
+#if !defined(CH_CFG_NO_IDLE_THREAD)
+#define CH_CFG_NO_IDLE_THREAD FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Performance options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief OS optimization.
+ * @details If enabled then time efficient rather than space efficient code
+ * is used when two possible implementations exist.
+ *
+ * @note This is not related to the compiler optimization options.
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_OPTIMIZE_SPEED)
+#define CH_CFG_OPTIMIZE_SPEED TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Subsystem options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Time Measurement APIs.
+ * @details If enabled then the time measurement APIs are included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TM)
+#define CH_CFG_USE_TM FALSE
+#endif
+
+/**
+ * @brief Time Stamps APIs.
+ * @details If enabled then the time stamps APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_TIMESTAMP)
+#define CH_CFG_USE_TIMESTAMP TRUE
+#endif
+
+/**
+ * @brief Threads registry APIs.
+ * @details If enabled then the registry APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_REGISTRY)
+#define CH_CFG_USE_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Threads synchronization APIs.
+ * @details If enabled then the @p chThdWait() function is included in
+ * the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_WAITEXIT)
+#define CH_CFG_USE_WAITEXIT TRUE
+#endif
+
+/**
+ * @brief Semaphores APIs.
+ * @details If enabled then the Semaphores APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES)
+#define CH_CFG_USE_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Semaphores queuing mode.
+ * @details If enabled then the threads are enqueued on semaphores by
+ * priority rather than in FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_SEMAPHORES_PRIORITY)
+#define CH_CFG_USE_SEMAPHORES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Mutexes APIs.
+ * @details If enabled then the mutexes APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MUTEXES)
+#define CH_CFG_USE_MUTEXES TRUE
+#endif
+
+/**
+ * @brief Enables recursive behavior on mutexes.
+ * @note Recursive mutexes are heavier and have an increased
+ * memory footprint.
+ *
+ * @note The default is @p FALSE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_MUTEXES_RECURSIVE)
+#define CH_CFG_USE_MUTEXES_RECURSIVE FALSE
+#endif
+
+/**
+ * @brief Conditional Variables APIs.
+ * @details If enabled then the conditional variables APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MUTEXES.
+ */
+#if !defined(CH_CFG_USE_CONDVARS)
+#define CH_CFG_USE_CONDVARS TRUE
+#endif
+
+/**
+ * @brief Conditional Variables APIs with timeout.
+ * @details If enabled then the conditional variables APIs with timeout
+ * specification are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_CONDVARS.
+ */
+#if !defined(CH_CFG_USE_CONDVARS_TIMEOUT)
+#define CH_CFG_USE_CONDVARS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs.
+ * @details If enabled then the event flags APIs are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_EVENTS)
+#define CH_CFG_USE_EVENTS TRUE
+#endif
+
+/**
+ * @brief Events Flags APIs with timeout.
+ * @details If enabled then the events APIs with timeout specification
+ * are included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_EVENTS.
+ */
+#if !defined(CH_CFG_USE_EVENTS_TIMEOUT)
+#define CH_CFG_USE_EVENTS_TIMEOUT TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages APIs.
+ * @details If enabled then the synchronous messages APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MESSAGES)
+#define CH_CFG_USE_MESSAGES TRUE
+#endif
+
+/**
+ * @brief Synchronous Messages queuing mode.
+ * @details If enabled then messages are served by priority rather than in
+ * FIFO order.
+ *
+ * @note The default is @p FALSE. Enable this if you have special
+ * requirements.
+ * @note Requires @p CH_CFG_USE_MESSAGES.
+ */
+#if !defined(CH_CFG_USE_MESSAGES_PRIORITY)
+#define CH_CFG_USE_MESSAGES_PRIORITY FALSE
+#endif
+
+/**
+ * @brief Dynamic Threads APIs.
+ * @details If enabled then the dynamic threads creation APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_WAITEXIT.
+ * @note Requires @p CH_CFG_USE_HEAP and/or @p CH_CFG_USE_MEMPOOLS.
+ */
+#if !defined(CH_CFG_USE_DYNAMIC)
+#define CH_CFG_USE_DYNAMIC TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name OSLIB options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Mailboxes APIs.
+ * @details If enabled then the asynchronous messages (mailboxes) APIs are
+ * included in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_SEMAPHORES.
+ */
+#if !defined(CH_CFG_USE_MAILBOXES)
+#define CH_CFG_USE_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Core Memory Manager APIs.
+ * @details If enabled then the core memory manager APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMCORE)
+#define CH_CFG_USE_MEMCORE TRUE
+#endif
+
+/**
+ * @brief Managed RAM size.
+ * @details Size of the RAM area to be managed by the OS. If set to zero
+ * then the whole available RAM is used. The core memory is made
+ * available to the heap allocator and/or can be used directly through
+ * the simplified core memory allocator.
+ *
+ * @note In order to let the OS manage the whole RAM the linker script must
+ * provide the @p __heap_base__ and @p __heap_end__ symbols.
+ * @note Requires @p CH_CFG_USE_MEMCORE.
+ */
+#if !defined(CH_CFG_MEMCORE_SIZE)
+#define CH_CFG_MEMCORE_SIZE 0
+#endif
+
+/**
+ * @brief Heap Allocator APIs.
+ * @details If enabled then the memory heap allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ * @note Requires @p CH_CFG_USE_MEMCORE and either @p CH_CFG_USE_MUTEXES or
+ * @p CH_CFG_USE_SEMAPHORES.
+ * @note Mutexes are recommended.
+ */
+#if !defined(CH_CFG_USE_HEAP)
+#define CH_CFG_USE_HEAP TRUE
+#endif
+
+/**
+ * @brief Memory Pools Allocator APIs.
+ * @details If enabled then the memory pools allocator APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_MEMPOOLS)
+#define CH_CFG_USE_MEMPOOLS TRUE
+#endif
+
+/**
+ * @brief Objects FIFOs APIs.
+ * @details If enabled then the objects FIFOs APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_FIFOS)
+#define CH_CFG_USE_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Pipes APIs.
+ * @details If enabled then the pipes APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_PIPES)
+#define CH_CFG_USE_PIPES TRUE
+#endif
+
+/**
+ * @brief Objects Caches APIs.
+ * @details If enabled then the objects caches APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_OBJ_CACHES)
+#define CH_CFG_USE_OBJ_CACHES TRUE
+#endif
+
+/**
+ * @brief Delegate threads APIs.
+ * @details If enabled then the delegate threads APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_DELEGATES)
+#define CH_CFG_USE_DELEGATES TRUE
+#endif
+
+/**
+ * @brief Jobs Queues APIs.
+ * @details If enabled then the jobs queues APIs are included
+ * in the kernel.
+ *
+ * @note The default is @p TRUE.
+ */
+#if !defined(CH_CFG_USE_JOBS)
+#define CH_CFG_USE_JOBS TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Objects factory options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Objects Factory APIs.
+ * @details If enabled then the objects factory APIs are included in the
+ * kernel.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_CFG_USE_FACTORY)
+#define CH_CFG_USE_FACTORY TRUE
+#endif
+
+/**
+ * @brief Maximum length for object names.
+ * @details If the specified length is zero then the name is stored by
+ * pointer but this could have unintended side effects.
+ */
+#if !defined(CH_CFG_FACTORY_MAX_NAMES_LENGTH)
+#define CH_CFG_FACTORY_MAX_NAMES_LENGTH 8
+#endif
+
+/**
+ * @brief Enables the registry of generic objects.
+ */
+#if !defined(CH_CFG_FACTORY_OBJECTS_REGISTRY)
+#define CH_CFG_FACTORY_OBJECTS_REGISTRY TRUE
+#endif
+
+/**
+ * @brief Enables factory for generic buffers.
+ */
+#if !defined(CH_CFG_FACTORY_GENERIC_BUFFERS)
+#define CH_CFG_FACTORY_GENERIC_BUFFERS TRUE
+#endif
+
+/**
+ * @brief Enables factory for semaphores.
+ */
+#if !defined(CH_CFG_FACTORY_SEMAPHORES)
+#define CH_CFG_FACTORY_SEMAPHORES TRUE
+#endif
+
+/**
+ * @brief Enables factory for mailboxes.
+ */
+#if !defined(CH_CFG_FACTORY_MAILBOXES)
+#define CH_CFG_FACTORY_MAILBOXES TRUE
+#endif
+
+/**
+ * @brief Enables factory for objects FIFOs.
+ */
+#if !defined(CH_CFG_FACTORY_OBJ_FIFOS)
+#define CH_CFG_FACTORY_OBJ_FIFOS TRUE
+#endif
+
+/**
+ * @brief Enables factory for Pipes.
+ */
+#if !defined(CH_CFG_FACTORY_PIPES) || defined(__DOXYGEN__)
+#define CH_CFG_FACTORY_PIPES TRUE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Debug options
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief Debug option, kernel statistics.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_STATISTICS)
+#define CH_DBG_STATISTICS FALSE
+#endif
+
+/**
+ * @brief Debug option, system state check.
+ * @details If enabled the correct call protocol for system APIs is checked
+ * at runtime.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_SYSTEM_STATE_CHECK)
+#define CH_DBG_SYSTEM_STATE_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, parameters checks.
+ * @details If enabled then the checks on the API functions input
+ * parameters are activated.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_CHECKS)
+#define CH_DBG_ENABLE_CHECKS FALSE
+#endif
+
+/**
+ * @brief Debug option, consistency checks.
+ * @details If enabled then all the assertions in the kernel code are
+ * activated. This includes consistency checks inside the kernel,
+ * runtime anomalies and port-defined checks.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_ENABLE_ASSERTS)
+#define CH_DBG_ENABLE_ASSERTS FALSE
+#endif
+
+/**
+ * @brief Debug option, trace buffer.
+ * @details If enabled then the trace buffer is activated.
+ *
+ * @note The default is @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_MASK)
+#define CH_DBG_TRACE_MASK CH_DBG_TRACE_MASK_DISABLED
+#endif
+
+/**
+ * @brief Trace buffer entries.
+ * @note The trace buffer is only allocated if @p CH_DBG_TRACE_MASK is
+ * different from @p CH_DBG_TRACE_MASK_DISABLED.
+ */
+#if !defined(CH_DBG_TRACE_BUFFER_SIZE)
+#define CH_DBG_TRACE_BUFFER_SIZE 128
+#endif
+
+/**
+ * @brief Debug option, stack checks.
+ * @details If enabled then a runtime stack check is performed.
+ *
+ * @note The default is @p FALSE.
+ * @note The stack check is performed in a architecture/port dependent way.
+ * It may not be implemented or some ports.
+ * @note The default failure mode is to halt the system with the global
+ * @p panic_msg variable set to @p NULL.
+ */
+#if !defined(CH_DBG_ENABLE_STACK_CHECK)
+#define CH_DBG_ENABLE_STACK_CHECK FALSE
+#endif
+
+/**
+ * @brief Debug option, stacks initialization.
+ * @details If enabled then the threads working area is filled with a byte
+ * value when a thread is created. This can be useful for the
+ * runtime measurement of the used stack.
+ *
+ * @note The default is @p FALSE.
+ */
+#if !defined(CH_DBG_FILL_THREADS)
+#define CH_DBG_FILL_THREADS FALSE
+#endif
+
+/**
+ * @brief Debug option, threads profiling.
+ * @details If enabled then a field is added to the @p thread_t structure that
+ * counts the system ticks occurred while executing the thread.
+ *
+ * @note The default is @p FALSE.
+ * @note This debug option is not currently compatible with the
+ * tickless mode.
+ */
+#if !defined(CH_DBG_THREADS_PROFILING)
+#define CH_DBG_THREADS_PROFILING FALSE
+#endif
+
+/** @} */
+
+/*===========================================================================*/
+/**
+ * @name Kernel hooks
+ * @{
+ */
+/*===========================================================================*/
+
+/**
+ * @brief System structure extension.
+ * @details User fields added to the end of the @p ch_system_t structure.
+ */
+#define CH_CFG_SYSTEM_EXTRA_FIELDS \
+ /* Add system custom fields here.*/
+
+/**
+ * @brief System initialization hook.
+ * @details User initialization code added to the @p chSysInit() function
+ * just before interrupts are enabled globally.
+ */
+#define CH_CFG_SYSTEM_INIT_HOOK() { \
+ /* Add system initialization code here.*/ \
+}
+
+/**
+ * @brief OS instance structure extension.
+ * @details User fields added to the end of the @p os_instance_t structure.
+ */
+#define CH_CFG_OS_INSTANCE_EXTRA_FIELDS \
+ /* Add OS instance custom fields here.*/
+
+/**
+ * @brief OS instance initialization hook.
+ *
+ * @param[in] oip pointer to the @p os_instance_t structure
+ */
+#define CH_CFG_OS_INSTANCE_INIT_HOOK(oip) { \
+ /* Add OS instance initialization code here.*/ \
+}
+
+/**
+ * @brief Threads descriptor structure extension.
+ * @details User fields added to the end of the @p thread_t structure.
+ */
+#define CH_CFG_THREAD_EXTRA_FIELDS \
+ /* Add threads custom fields here.*/
+
+/**
+ * @brief Threads initialization hook.
+ * @details User initialization code added to the @p _thread_init() function.
+ *
+ * @note It is invoked from within @p _thread_init() and implicitly from all
+ * the threads creation APIs.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_INIT_HOOK(tp) { \
+ /* Add threads initialization code here.*/ \
+}
+
+/**
+ * @brief Threads finalization hook.
+ * @details User finalization code added to the @p chThdExit() API.
+ *
+ * @param[in] tp pointer to the @p thread_t structure
+ */
+#define CH_CFG_THREAD_EXIT_HOOK(tp) { \
+ /* Add threads finalization code here.*/ \
+}
+
+/**
+ * @brief Context switch hook.
+ * @details This hook is invoked just before switching between threads.
+ *
+ * @param[in] ntp thread being switched in
+ * @param[in] otp thread being switched out
+ */
+#define CH_CFG_CONTEXT_SWITCH_HOOK(ntp, otp) { \
+ /* Context switch code here.*/ \
+}
+
+/**
+ * @brief ISR enter hook.
+ */
+#define CH_CFG_IRQ_PROLOGUE_HOOK() { \
+ /* IRQ prologue code here.*/ \
+}
+
+/**
+ * @brief ISR exit hook.
+ */
+#define CH_CFG_IRQ_EPILOGUE_HOOK() { \
+ /* IRQ epilogue code here.*/ \
+}
+
+/**
+ * @brief Idle thread enter hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to activate a power saving mode.
+ */
+#define CH_CFG_IDLE_ENTER_HOOK() { \
+ /* Idle-enter code here.*/ \
+}
+
+/**
+ * @brief Idle thread leave hook.
+ * @note This hook is invoked within a critical zone, no OS functions
+ * should be invoked from here.
+ * @note This macro can be used to deactivate a power saving mode.
+ */
+#define CH_CFG_IDLE_LEAVE_HOOK() { \
+ /* Idle-leave code here.*/ \
+}
+
+/**
+ * @brief Idle Loop hook.
+ * @details This hook is continuously invoked by the idle thread loop.
+ */
+#define CH_CFG_IDLE_LOOP_HOOK() { \
+ /* Idle loop code here.*/ \
+}
+
+/**
+ * @brief System tick event hook.
+ * @details This hook is invoked in the system tick handler immediately
+ * after processing the virtual timers queue.
+ */
+#define CH_CFG_SYSTEM_TICK_HOOK() { \
+ /* System tick event code here.*/ \
+}
+
+/**
+ * @brief System halt hook.
+ * @details This hook is invoked in case to a system halting error before
+ * the system is halted.
+ */
+#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
+ /* System halt code here.*/ \
+}
+
+/**
+ * @brief Trace hook.
+ * @details This hook is invoked each time a new record is written in the
+ * trace buffer.
+ */
+#define CH_CFG_TRACE_HOOK(tep) { \
+ /* Trace code here.*/ \
+}
+
+/**
+ * @brief Runtime Faults Collection Unit hook.
+ * @details This hook is invoked each time new faults are collected and stored.
+ */
+#define CH_CFG_RUNTIME_FAULTS_HOOK(mask) { \
+ /* Faults handling code here.*/ \
+}
+
+/** @} */
+
+/*===========================================================================*/
+/* Port-specific settings (override port settings defaulted in chcore.h). */
+/*===========================================================================*/
+
+#endif /* CHCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/TIM/cfg/halconf.h b/testhal/SK32/TIM/cfg/halconf.h
new file mode 100644
index 0000000000..5080660028
--- /dev/null
+++ b/testhal/SK32/TIM/cfg/halconf.h
@@ -0,0 +1,568 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+/**
+ * @file templates/halconf.h
+ * @brief HAL configuration header.
+ * @details HAL configuration file, this file allows to enable or disable the
+ * various device drivers from your application. You may also use
+ * this file in order to override the device drivers default settings.
+ *
+ * @addtogroup HAL_CONF
+ * @{
+ */
+
+#ifndef HALCONF_H
+#define HALCONF_H
+
+#define _CHIBIOS_HAL_CONF_
+#define _CHIBIOS_HAL_CONF_VER_9_1_
+
+#include "mcuconf.h"
+
+/**
+ * @brief Enables the HAL safety subsystem.
+ */
+#if !defined(HAL_USE_SAFETY) || defined(__DOXYGEN__)
+#define HAL_USE_SAFETY FALSE
+#endif
+
+/**
+ * @brief Enables the PAL subsystem.
+ */
+#if !defined(HAL_USE_PAL) || defined(__DOXYGEN__)
+#define HAL_USE_PAL TRUE
+#endif
+
+/**
+ * @brief Enables the ADC subsystem.
+ */
+#if !defined(HAL_USE_ADC) || defined(__DOXYGEN__)
+#define HAL_USE_ADC FALSE
+#endif
+
+/**
+ * @brief Enables the CAN subsystem.
+ */
+#if !defined(HAL_USE_CAN) || defined(__DOXYGEN__)
+#define HAL_USE_CAN FALSE
+#endif
+
+/**
+ * @brief Enables the cryptographic subsystem.
+ */
+#if !defined(HAL_USE_CRY) || defined(__DOXYGEN__)
+#define HAL_USE_CRY FALSE
+#endif
+
+/**
+ * @brief Enables the DAC subsystem.
+ */
+#if !defined(HAL_USE_DAC) || defined(__DOXYGEN__)
+#define HAL_USE_DAC FALSE
+#endif
+
+/**
+ * @brief Enables the EFlash subsystem.
+ */
+#if !defined(HAL_USE_EFL) || defined(__DOXYGEN__)
+#define HAL_USE_EFL FALSE
+#endif
+
+/**
+ * @brief Enables the GPT subsystem.
+ */
+#if !defined(HAL_USE_GPT) || defined(__DOXYGEN__)
+#define HAL_USE_GPT TRUE
+#endif
+
+/**
+ * @brief Enables the I2C subsystem.
+ */
+#if !defined(HAL_USE_I2C) || defined(__DOXYGEN__)
+#define HAL_USE_I2C FALSE
+#endif
+
+/**
+ * @brief Enables the I2S subsystem.
+ */
+#if !defined(HAL_USE_I2S) || defined(__DOXYGEN__)
+#define HAL_USE_I2S FALSE
+#endif
+
+/**
+ * @brief Enables the ICU subsystem.
+ */
+#if !defined(HAL_USE_ICU) || defined(__DOXYGEN__)
+#define HAL_USE_ICU FALSE
+#endif
+
+/**
+ * @brief Enables the MAC subsystem.
+ */
+#if !defined(HAL_USE_MAC) || defined(__DOXYGEN__)
+#define HAL_USE_MAC FALSE
+#endif
+
+/**
+ * @brief Enables the MMC_SPI subsystem.
+ */
+#if !defined(HAL_USE_MMC_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_MMC_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the PWM subsystem.
+ */
+#if !defined(HAL_USE_PWM) || defined(__DOXYGEN__)
+#define HAL_USE_PWM FALSE
+#endif
+
+/**
+ * @brief Enables the RTC subsystem.
+ */
+#if !defined(HAL_USE_RTC) || defined(__DOXYGEN__)
+#define HAL_USE_RTC FALSE
+#endif
+
+/**
+ * @brief Enables the SDC subsystem.
+ */
+#if !defined(HAL_USE_SDC) || defined(__DOXYGEN__)
+#define HAL_USE_SDC FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL subsystem.
+ */
+#if !defined(HAL_USE_SERIAL) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL FALSE
+#endif
+
+/**
+ * @brief Enables the SERIAL over USB subsystem.
+ */
+#if !defined(HAL_USE_SERIAL_USB) || defined(__DOXYGEN__)
+#define HAL_USE_SERIAL_USB FALSE
+#endif
+
+/**
+ * @brief Enables the SIO subsystem.
+ */
+#if !defined(HAL_USE_SIO) || defined(__DOXYGEN__)
+#define HAL_USE_SIO FALSE
+#endif
+
+/**
+ * @brief Enables the SPI subsystem.
+ */
+#if !defined(HAL_USE_SPI) || defined(__DOXYGEN__)
+#define HAL_USE_SPI FALSE
+#endif
+
+/**
+ * @brief Enables the TRNG subsystem.
+ */
+#if !defined(HAL_USE_TRNG) || defined(__DOXYGEN__)
+#define HAL_USE_TRNG FALSE
+#endif
+
+/**
+ * @brief Enables the UART subsystem.
+ */
+#if !defined(HAL_USE_UART) || defined(__DOXYGEN__)
+#define HAL_USE_UART FALSE
+#endif
+
+/**
+ * @brief Enables the USB subsystem.
+ */
+#if !defined(HAL_USE_USB) || defined(__DOXYGEN__)
+#define HAL_USE_USB FALSE
+#endif
+
+/**
+ * @brief Enables the WDG subsystem.
+ */
+#if !defined(HAL_USE_WDG) || defined(__DOXYGEN__)
+#define HAL_USE_WDG FALSE
+#endif
+
+/**
+ * @brief Enables the WSPI subsystem.
+ */
+#if !defined(HAL_USE_WSPI) || defined(__DOXYGEN__)
+#define HAL_USE_WSPI FALSE
+#endif
+
+/*===========================================================================*/
+/* PAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define PAL_USE_CALLBACKS FALSE
+#endif
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(PAL_USE_WAIT) || defined(__DOXYGEN__)
+#define PAL_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* ADC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_WAIT) || defined(__DOXYGEN__)
+#define ADC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p adcAcquireBus() and @p adcReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(ADC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define ADC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* CAN driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Sleep mode related APIs inclusion switch.
+ */
+#if !defined(CAN_USE_SLEEP_MODE) || defined(__DOXYGEN__)
+#define CAN_USE_SLEEP_MODE TRUE
+#endif
+
+/**
+ * @brief Enforces the driver to use direct callbacks rather than OSAL events.
+ */
+#if !defined(CAN_ENFORCE_USE_CALLBACKS) || defined(__DOXYGEN__)
+#define CAN_ENFORCE_USE_CALLBACKS FALSE
+#endif
+
+/*===========================================================================*/
+/* CRY driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the SW fall-back of the cryptographic driver.
+ * @details When enabled, this option, activates a fall-back software
+ * implementation for algorithms not supported by the underlying
+ * hardware.
+ * @note Fall-back implementations may not be present for all algorithms.
+ */
+#if !defined(HAL_CRY_USE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_USE_FALLBACK FALSE
+#endif
+
+/**
+ * @brief Makes the driver forcibly use the fall-back implementations.
+ */
+#if !defined(HAL_CRY_ENFORCE_FALLBACK) || defined(__DOXYGEN__)
+#define HAL_CRY_ENFORCE_FALLBACK FALSE
+#endif
+
+/*===========================================================================*/
+/* DAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_WAIT) || defined(__DOXYGEN__)
+#define DAC_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p dacAcquireBus() and @p dacReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(DAC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define DAC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* I2C driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Slave mode API enable switch.
+ * @note The low level driver must support this capability.
+ */
+#if !defined(I2C_ENABLE_SLAVE_MODE)
+#define I2C_ENABLE_SLAVE_MODE FALSE
+#endif
+
+/**
+ * @brief Enables the mutual exclusion APIs on the I2C bus.
+ */
+#if !defined(I2C_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define I2C_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* MAC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables the zero-copy API.
+ */
+#if !defined(MAC_USE_ZERO_COPY) || defined(__DOXYGEN__)
+#define MAC_USE_ZERO_COPY FALSE
+#endif
+
+/**
+ * @brief Enables an event sources for incoming packets.
+ */
+#if !defined(MAC_USE_EVENTS) || defined(__DOXYGEN__)
+#define MAC_USE_EVENTS TRUE
+#endif
+
+/*===========================================================================*/
+/* MMC_SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Timeout before assuming a failure while waiting for card idle.
+ * @note Time is in milliseconds.
+ */
+#if !defined(MMC_IDLE_TIMEOUT_MS) || defined(__DOXYGEN__)
+#define MMC_IDLE_TIMEOUT_MS 1000
+#endif
+
+/**
+ * @brief Mutual exclusion on the SPI bus.
+ */
+#if !defined(MMC_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define MMC_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/*===========================================================================*/
+/* SDC driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Number of initialization attempts before rejecting the card.
+ * @note Attempts are performed at 10mS intervals.
+ */
+#if !defined(SDC_INIT_RETRY) || defined(__DOXYGEN__)
+#define SDC_INIT_RETRY 100
+#endif
+
+/**
+ * @brief Include support for MMC cards.
+ * @note MMC support is not yet implemented so this option must be kept
+ * at @p FALSE.
+ */
+#if !defined(SDC_MMC_SUPPORT) || defined(__DOXYGEN__)
+#define SDC_MMC_SUPPORT FALSE
+#endif
+
+/**
+ * @brief Delays insertions.
+ * @details If enabled this options inserts delays into the MMC waiting
+ * routines releasing some extra CPU time for the threads with
+ * lower priority, this may slow down the driver a bit however.
+ */
+#if !defined(SDC_NICE_WAITING) || defined(__DOXYGEN__)
+#define SDC_NICE_WAITING TRUE
+#endif
+
+/**
+ * @brief OCR initialization constant for V20 cards.
+ */
+#if !defined(SDC_INIT_OCR_V20) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR_V20 0x50FF8000U
+#endif
+
+/**
+ * @brief OCR initialization constant for non-V20 cards.
+ */
+#if !defined(SDC_INIT_OCR) || defined(__DOXYGEN__)
+#define SDC_INIT_OCR 0x80100000U
+#endif
+
+/*===========================================================================*/
+/* SERIAL driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SERIAL_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SERIAL_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Serial buffers size.
+ * @details Configuration parameter, you can change the depth of the queue
+ * buffers depending on the requirements of your application.
+ * @note The default is 16 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_BUFFERS_SIZE 16
+#endif
+
+/*===========================================================================*/
+/* SIO driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Default bit rate.
+ * @details Configuration parameter, this is the baud rate selected for the
+ * default configuration.
+ */
+#if !defined(SIO_DEFAULT_BITRATE) || defined(__DOXYGEN__)
+#define SIO_DEFAULT_BITRATE 38400
+#endif
+
+/**
+ * @brief Support for thread synchronization API.
+ */
+#if !defined(SIO_USE_SYNCHRONIZATION) || defined(__DOXYGEN__)
+#define SIO_USE_SYNCHRONIZATION TRUE
+#endif
+
+/*===========================================================================*/
+/* SERIAL_USB driver related setting. */
+/*===========================================================================*/
+
+/**
+ * @brief Serial over USB buffers size.
+ * @details Configuration parameter, the buffer size must be a multiple of
+ * the USB data endpoint maximum packet size.
+ * @note The default is 256 bytes for both the transmission and receive
+ * buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_SIZE) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_SIZE 256
+#endif
+
+/**
+ * @brief Serial over USB number of buffers.
+ * @note The default is 2 buffers.
+ */
+#if !defined(SERIAL_USB_BUFFERS_NUMBER) || defined(__DOXYGEN__)
+#define SERIAL_USB_BUFFERS_NUMBER 2
+#endif
+
+/*===========================================================================*/
+/* SPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_WAIT) || defined(__DOXYGEN__)
+#define SPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Inserts an assertion on function errors before returning.
+ */
+#if !defined(SPI_USE_ASSERT_ON_ERROR) || defined(__DOXYGEN__)
+#define SPI_USE_ASSERT_ON_ERROR TRUE
+#endif
+
+/**
+ * @brief Enables the @p spiAcquireBus() and @p spiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define SPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+/**
+ * @brief Handling method for SPI CS line.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(SPI_SELECT_MODE) || defined(__DOXYGEN__)
+#define SPI_SELECT_MODE SPI_SELECT_MODE_PAD
+#endif
+
+/*===========================================================================*/
+/* UART driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_WAIT) || defined(__DOXYGEN__)
+#define UART_USE_WAIT FALSE
+#endif
+
+/**
+ * @brief Enables the @p uartAcquireBus() and @p uartReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(UART_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define UART_USE_MUTUAL_EXCLUSION FALSE
+#endif
+
+/*===========================================================================*/
+/* USB driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(USB_USE_WAIT) || defined(__DOXYGEN__)
+#define USB_USE_WAIT FALSE
+#endif
+
+/*===========================================================================*/
+/* WSPI driver related settings. */
+/*===========================================================================*/
+
+/**
+ * @brief Enables synchronous APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_WAIT) || defined(__DOXYGEN__)
+#define WSPI_USE_WAIT TRUE
+#endif
+
+/**
+ * @brief Enables the @p wspiAcquireBus() and @p wspiReleaseBus() APIs.
+ * @note Disabling this option saves both code and data space.
+ */
+#if !defined(WSPI_USE_MUTUAL_EXCLUSION) || defined(__DOXYGEN__)
+#define WSPI_USE_MUTUAL_EXCLUSION TRUE
+#endif
+
+#endif /* HALCONF_H */
+
+/** @} */
diff --git a/testhal/SK32/TIM/cfg/mcuconf.h b/testhal/SK32/TIM/cfg/mcuconf.h
new file mode 100644
index 0000000000..c8954f4a84
--- /dev/null
+++ b/testhal/SK32/TIM/cfg/mcuconf.h
@@ -0,0 +1,102 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+
+ 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.
+*/
+
+#ifndef MCUCONF_H
+#define MCUCONF_H
+
+/*
+ * SK32F0xx drivers configuration.
+ * The following settings override the default settings present in
+ * the various device driver implementation headers.
+ * Note that the settings for each driver only have effect if the whole
+ * driver is enabled in halconf.h.
+ *
+ * The SK32F0xx family is register-compatible with STM32F072 except for the
+ * USB IP, but the SK32 port provides native drivers for the entire core
+ * platform and for every peripheral, so no shared STM32 low level driver
+ * (TIMv1, USARTv2, ADCv1, SPIv2, I2Cv2, ..., nor the CANv1/DACv1/DMAv1/
+ * RTCv2/xWDGv1 stubs) is linked in. This mcuconf.h only needs to satisfy
+ * the single SK32 naming scheme:
+ * - SK32F0xx_MCUCONF + the SK32_* clock/ST/peripheral settings consumed by
+ * the native SK32 HAL low level drivers.
+ *
+ * IRQ priorities:
+ * 3...0 Lowest...Highest.
+ *
+ * DMA priorities:
+ * 0...3 Lowest...Highest.
+ */
+
+#define SK32F0xx_MCUCONF
+
+/*
+ * SK32 HAL system clock settings.
+ * SK32_* defaults (72MHz system clock from the 8MHz HSI through the main
+ * PLL, HSI/PREDIV1(=1) * PLLMUL(=9)) are applied by hal_lld.h when not
+ * defined here; they are repeated below for clarity.
+ */
+#define SK32_SW SK32_SW_PLL
+#define SK32_PREDIV_VALUE 1
+#define SK32_PLLMUL_VALUE 9
+#define SK32_HPRE SK32_HPRE_DIV1
+#define SK32_PPRE SK32_PPRE_DIV1
+
+/*
+ * GPT driver system settings.
+ * The SK32 native GPT driver (TIM3 on the APB1 bus, TIM16/TIM17 on the APB2
+ * bus) is selected through the SK32_GPT_USE_TIMx switches. All three timers
+ * are used by the TIM testhal to verify interrupt driven counting.
+ */
+#define SK32_GPT_USE_TIM3 TRUE
+#define SK32_GPT_USE_TIM16 TRUE
+#define SK32_GPT_USE_TIM17 TRUE
+#define SK32_GPT_TIM3_IRQ_PRIORITY 3
+#define SK32_GPT_TIM16_IRQ_PRIORITY 3
+#define SK32_GPT_TIM17_IRQ_PRIORITY 3
+
+/*
+ * SERIAL driver system settings.
+ * The SK32 native serial driver (SR/DR USART) is selected through the
+ * SK32_SERIAL_USE_USARTx switches, USART2 is mapped on PA2/PA3 (alternate 1)
+ * as documented in board.h.
+ */
+#define SK32_SERIAL_USE_USART1 FALSE
+#define SK32_SERIAL_USE_USART2 TRUE
+#define SK32_SERIAL_USART1_PRIORITY 3
+#define SK32_SERIAL_USART2_PRIORITY 3
+
+/*
+ * SPI driver system settings.
+ * The SK32 native interrupt-driven SPI driver is selected through the
+ * SK32_SPI_USE_SPIx switches.
+ *
+ * SPI1 self-loopback test:
+ * - SPI1 master on PA7 (SPI1_MOSI, alternate 12) and PA6 (SPI1_MISO,
+ * alternate 12), the two pads are shorted on the board.
+ */
+#define SK32_SPI_USE_SPI1 TRUE
+#define SK32_SPI_USE_SPI2 FALSE
+#define SK32_SPI_SPI1_PRIORITY 3
+#define SK32_SPI_SPI2_PRIORITY 3
+
+/*
+ * SK32 ST driver system settings.
+ * The SK32 port uses its native SysTick-based ST driver which requires
+ * periodic mode.
+ */
+#define SK32_ST_IRQ_PRIORITY 2
+
+#endif /* MCUCONF_H */
\ No newline at end of file
diff --git a/testhal/SK32/TIM/main.c b/testhal/SK32/TIM/main.c
new file mode 100644
index 0000000000..28506f93cd
--- /dev/null
+++ b/testhal/SK32/TIM/main.c
@@ -0,0 +1,177 @@
+/*
+ ChibiOS - Copyright (C) 2006-2026 Giovanni Di Sirio.
+ Copyright (C) 2026 QMK
+
+ 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.
+*/
+
+#include "ch.h"
+#include "hal.h"
+
+/*===========================================================================*/
+/* SK32F0xx TIM3/TIM16/TIM17 GPT interrupt counting test. */
+/*===========================================================================*/
+
+/*
+ * Test resources used by this test:
+ *
+ * - GPTD3 (TIM3, APB1 clock domain) with a 10 kHz tick and a 10000-tick
+ * period: its update interrupt fires once per second.
+ * - GPTD16 (TIM16, APB2 clock domain) with a 10 kHz tick and a 2000-tick
+ * period: its update interrupt fires five times per second.
+ * - GPTD17 (TIM17, APB2 clock domain) with a 2 kHz tick and a 2000-tick
+ * period: its update interrupt fires once per second.
+ * - PB14 / PB15: two board LEDs wired to push-pull outputs, the PAL
+ * abstraction drives them high (PAL_HIGH) to switch the LEDs on.
+ *
+ * Test procedure:
+ * - Each update interrupt increments its own counter in the GPT callback
+ * (interrupt context) and GPTD3/GPTD16 additionally toggle PB14/PB15,
+ * producing two clearly distinguishable blink rates (1 s period on PB14,
+ * 200 ms period on PB15).
+ * - The main thread samples the three counters every 10 s of wall time and
+ * verifies that GPTD3 and GPTD17 have ticked about ten times and GPTD16
+ * about fifty times: this proves the timers are actually counting and
+ * that the update interrupts are being serviced.
+ * - On the first out-of-range delta both LEDs are switched on solid and the
+ * system halts.
+ */
+
+/* Interrupt context counters, incremented by the GPT callbacks. */
+static volatile uint32_t cnt3;
+static volatile uint32_t cnt16;
+static volatile uint32_t cnt17;
+
+/*
+ * GPT update callbacks. GPTD3 toggles PB14 (once per second) and GPTD16
+ * toggles PB15 (five times per second), GPTD17 is only used for counting.
+ */
+static void gpt3cb(GPTDriver *gptp) {
+
+ (void)gptp;
+ cnt3++;
+ palTogglePad(GPIOB, GPIOB_PIN14);
+}
+
+static void gpt16cb(GPTDriver *gptp) {
+
+ (void)gptp;
+ cnt16++;
+ palTogglePad(GPIOB, GPIOB_PIN15);
+}
+
+static void gpt17cb(GPTDriver *gptp) {
+
+ (void)gptp;
+ cnt17++;
+}
+
+/*
+ * GPT configurations.
+ * The 72 MHz peripheral clock is divided down to the tick frequency, then
+ * each period is expressed in ticks of that timer tick clock.
+ */
+static const GPTConfig gpt3cfg = {
+ .frequency = 10000U, /* 72MHz / 7200 -> 10 kHz tick clock. */
+ .callback = gpt3cb,
+ .cr2 = 0U,
+ .dier = 0U
+};
+
+static const GPTConfig gpt16cfg = {
+ .frequency = 10000U, /* 72MHz / 7200 -> 10 kHz tick clock. */
+ .callback = gpt16cb,
+ .cr2 = 0U,
+ .dier = 0U
+};
+
+static const GPTConfig gpt17cfg = {
+ .frequency = 2000U, /* 72MHz / 36000 -> 2 kHz tick clock. */
+ .callback = gpt17cb,
+ .cr2 = 0U,
+ .dier = 0U
+};
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+ uint32_t base3;
+ uint32_t base16;
+ uint32_t base17;
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+ /* Configuring the PB14/PB15 LED lines as push-pull outputs, initially off. */
+ palSetPadMode(GPIOB, GPIOB_PIN14, PAL_MODE_OUTPUT_PUSHPULL);
+ palSetPadMode(GPIOB, GPIOB_PIN15, PAL_MODE_OUTPUT_PUSHPULL);
+ palClearPad(GPIOB, GPIOB_PIN14);
+ palClearPad(GPIOB, GPIOB_PIN15);
+
+ /*
+ * GPTD3: 1 Hz period (10000 ticks @ 10 kHz).
+ * GPTD16: 200 ms period (2000 ticks @ 10 kHz).
+ * GPTD17: 1 s period (2000 ticks @ 2 kHz).
+ */
+ gptStart(&GPTD3, &gpt3cfg);
+ gptStart(&GPTD16, &gpt16cfg);
+ gptStart(&GPTD17, &gpt17cfg);
+ gptStartContinuous(&GPTD3, 10000U);
+ gptStartContinuous(&GPTD16, 2000U);
+ gptStartContinuous(&GPTD17, 2000U);
+
+ /*
+ * Counting verification loop.
+ */
+ base3 = cnt3;
+ base16 = cnt16;
+ base17 = cnt17;
+ while (true) {
+ uint32_t delta3;
+ uint32_t delta16;
+ uint32_t delta17;
+
+ /* Sampling window of 10 s of wall time.*/
+ chThdSleepMilliseconds(10000);
+
+ delta3 = cnt3 - base3;
+ delta16 = cnt16 - base16;
+ delta17 = cnt17 - base17;
+
+ /*
+ * Expected values: about 10 interrupts for GPTD3 and GPTD17 (1 Hz) and
+ * about 50 interrupts for GPTD16 (5 Hz). A small tolerance absorbs the
+ * phase difference between the sampling points and the timer periods.
+ */
+ if ((delta3 < 8U) || (delta3 > 12U) ||
+ (delta16 < 45U) || (delta16 > 55U) ||
+ (delta17 < 8U) || (delta17 > 12U)) {
+ /* Failure: both LEDs on solid, halt.*/
+ palSetPad(GPIOB, GPIOB_PIN14);
+ palSetPad(GPIOB, GPIOB_PIN15);
+ chSysHalt("TIM interrupt counting FAIL");
+ }
+
+ base3 = cnt3;
+ base16 = cnt16;
+ base17 = cnt17;
+ }
+}
diff --git a/testhal/SK32/TIM/tim.hex b/testhal/SK32/TIM/tim.hex
new file mode 100644
index 0000000000..eb3e7e6067
--- /dev/null
+++ b/testhal/SK32/TIM/tim.hex
@@ -0,0 +1,308 @@
+:020000040800F2
+:100000000004002091010008F10F0008930100088E
+:100010009301000893010008930100089301000870
+:100020009301000893010008930100089301000860
+:10003000930100089301000893010008610500087E
+:100040009301000893010008930100089301000840
+:100050009301000893010008930100089301000830
+:100060009301000893010008930100089301000820
+:100070009301000893010008930100089301000810
+:10008000D1060008930100089301000893010008BD
+:100090009301000831070008910700089301000848
+:1000A00093010008930100089301000893010008E0
+:1000B00093010008930100089301000893010008D0
+:1000C00072B6244880F30888234880F30988022008
+:1000D00080F31488BFF36F8F00F022F900F030FC3A
+:1000E0001E481F491B4A914202DA08600431FAE7B0
+:1000F0001C49194A914202DA08600431FAE71A49A8
+:100100001A4A1B4B9A4204DA086810600431043220
+:10011000F8E700201749184A914202DA08600431D2
+:10012000FAE700F015F900F003F9144C144DAC4255
+:1001300003DA216888470434F9E700F0B9FF114C6D
+:10014000114DAC4203DA216888470434F9E70F49BE
+:1001500008470000000400200008002055555555B0
+:100160000000002000040020001300080008002008
+:100170000408002008080020280A0020C000000809
+:10018000C0000008C0000008C000000841030008CB
+:1001900096E700F000F8FEE7F0B544464D465646B7
+:1001A0005F46F0B46B46CB60C3689D46F0BCA0468A
+:1001B000A946B246BB46F0BD62B6281CA047002047
+:1001C00000F04EFEFEE700F07BFD024A024B13609A
+:1001D000FEE7C04604ED00E000000080002934D0B6
+:1001E0000123002210B488422CD301242407A14209
+:1001F00004D2814202D209011B01F8E7E400A142C6
+:1002000004D2814202D249005B00F8E7884201D360
+:10021000401A1A434C08A04202D3001B5C08224338
+:100220008C08A04202D3001B9C082243CC08A042A9
+:1002300002D3001BDC082243002803D01B0901D095
+:100240000909E3E7100010BC704701B5002000F079
+:100250000BF802BD0029F8D003B5FFF7C1FF0EBCB3
+:100260004243891A1847C0467047C046F0B505009A
+:10027000830749D0541E002A45D003000326CAB282
+:1002800002E00135013C3ED301332A703342F8D1FC
+:10029000032C30D9FF220A401502AD182A04AD18EC
+:1002A0000F2C34D92700103F3F093E01B4461E00F1
+:1002B0001A0010366644156055609560D56010329E
+:1002C0009642F8D101373F01DB190F270C22274056
+:1002D00022403C00002A0ED03E1FB608B400A446BF
+:1002E0001A001C1D644420C2A242FCD10324013622
+:1002F000B6009B193C40002C05D0C9B21C191970DE
+:1003000001339C42FBD1F0BD03001400C0E727007D
+:10031000E2E7C0460000000000000000000000000E
+:100320007047C04600000000000000000000000010
+:100330007047C04600000000000000000000000000
+:10034000FEE7C046000000000000000000000000C2
+:1003500070B5114C250080356068A2682168904214
+:100360000AD2030040C940C39342FBD3013A131A97
+:100370009B0801339B00C018E368834207D9013B07
+:100380001A1A9208013200219200FFF76FFF103411
+:10039000AC42E1D170BDC046C011000800000000B1
+:1003A00010B500F07DF800F003F900F011F800F04E
+:1003B0002FFB00F005F810BD000000000000000059
+:1003C00010B500F0DDF810BD0000000000000000D6
+:1003D00010B500F00DFA10BD000000000000000094
+:1003E00001230370002343607047C04600000000F3
+:1003F00010B5040072B6416000F01AFA02232370AF
+:1004000062B6002010BDC0460000000000000000E1
+:1004100010B572B60323037000F07AFA62B610BD0D
+:100420000F4A70B594460322FF25C0260240D20031
+:1004300095408901914083089B006344B6009C5914
+:100440001F22AC430C439C51012302409340C02225
+:1004500040098000604452008350036070BDC04674
+:1004600000E100E0000000000000000000000000CB
+:100470000F2310B50340040003202040FF24074A47
+:10048000C0009446844089018140083B9B089B0042
+:100490006344DA69A2430A43DA6110BD00ED00E06B
+:1004A000064B074A02219A62002249429A62D960A9
+:1004B0000131DA6019611A617047C04600100240CC
+:1004C000FFFF81FF000000000000000000000000AE
+:1004D00001211D4A13680B4313600131136819424F
+:1004E000FCD0032153688B435360174A093108003D
+:1004F000536818401942FAD1FD2311680B4013606C
+:10050000E4235B035060D06210635063D062536099
+:10051000802311685B040B43802113600A4A89041D
+:1005200013680B42FCD01221084B19605368103934
+:100530000B4353600C2204494B681340082BFBD13A
+:10054000034B044A1A60704700100240002002402A
+:100550000008002000A24A04000000000000000083
+:1005600010B5744672B600F08BFA62B6200000F047
+:1005700057FD10BD0000000000000000000000005A
+:10058000064B074A10B55A6000229A6007320221D2
+:100590000B201A60FFF76CFF10BDC04610E000E0B2
+:1005A0001F1C000000000000000000000000000010
+:1005B0007047C0460000000000000000000000007E
+:1005C000F0B54E46032457464546DE46E0B52700C3
+:1005D000530917405607D508520525401C40130FF4
+:1005E000012285B0039391460023A4460297F60F9B
+:1005F000009717E05C4607682740009C2743076088
+:10060000072B52D85446076A3A40224302624908EF
+:1006100044D0624692009446009A76009200AD0063
+:10062000009201334A460A42F1D007274C46039A0A
+:100630001F40BF00BA409C4092468288A24332438A
+:1006400092B28280846801945C00A04603244246F2
+:100650009440E24393462200A046019C944322002A
+:1006600044462A438260C268A24364462243C26071
+:100670000F22BA40029CD243022CBBD1072B1AD8BE
+:10068000076A17405246174307625C4602684908EA
+:100690002240009C224302600029BAD105B0F0BC80
+:1006A000BB46B246A946A046F0BD5446476A3A400A
+:1006B00022434262ABE7476A1740524617434762FC
+:1006C000E3E7C0460000000000000000000000005A
+:1006D00070B5FF2512487446C1680A8A8B89134099
+:1006E0002A001A40D24392B20A82DB0707D5037868
+:1006F000042B08D043685B68002B00D0984720008B
+:1007000000F08EFC70BD023B037000230B800B8257
+:100710008B89AB438B8143685B68002BEED1EEE79E
+:100720002808002000000000000000000000000079
+:1007300070B5FF2512487446C1680A8A8B89134038
+:100740002A001A40D24392B20A82DB0707D5037807
+:10075000042B08D043685B68002B00D0984720002A
+:1007600000F05EFC70BD023B037000230B800B8227
+:100770008B89AB438B8143685B68002BEED1EEE73E
+:100780001808002000000000000000000000000029
+:1007900070B5FF2512487446C1680A8A8B891340D8
+:1007A0002A001A40D24392B20A82DB0707D50378A7
+:1007B000042B08D043685B68002B00D098472000CA
+:1007C00000F02EFC70BD023B037000230B800B82F7
+:1007D0008B89AB438B8143685B68002BEED1EEE7DE
+:1007E00008080020000000000000000000000000D9
+:1007F0000848094B10B5C360FFF7F2FD0748084BE6
+:10080000C360FFF7EDFD0748074BC360FFF7E8FD46
+:1008100010BDC04628080020000400401808002031
+:10082000004401400808002000480140000000008A
+:10083000037870B50400012B12D06568A0682968A0
+:10084000FFF7CCFC0022E36801381A80298980B2C6
+:100850009980FF2118851A826A898A439A8170BD1E
+:10086000244B984231D0244B984218D0234B9842C5
+:10087000E3D18022224BD20299691620114399615B
+:100880009969D9680A43DA60DA681E490A40DA6071
+:100890000321DB68FFF7C4FD1B4BA360CDE780227B
+:1008A000174B920299691520114399619969D9688A
+:1008B0000A43DA60DA6815490A40DA600321DB6826
+:1008C000FFF7AEFD104BA360B7E702210C4B1020E1
+:1008D000DA690A43DA61DA691A690A431A611A693C
+:1008E0008A431A6101311B69FFF79AFD064BA36029
+:1008F000A3E7C046280800201808002008080020A8
+:1009000000100240FFFFFBFF00A24A04FFFFFDFFB3
+:100910000122C3680139D9629A8200225A621A827E
+:1009200042685268002A04D001219A890A4392B28F
+:100930009A8185221A8070470000000000000000A4
+:10094000FC22244BD203996A10B511439962996A2B
+:100950002148802401409962996A59691F480A43D5
+:100960005A6190225B6900231D49D20593809160F2
+:100970001C49D1602121494291828821490111629B
+:100980001949536211600122184952428B808A60D2
+:10099000C8608A820B624B620C601549154C8B80D3
+:1009A0008A60CC608A820B624B620B6012498B803A
+:1009B0008A60C8608A8211480B624B620B601049E2
+:1009C0008B808A60C8608A820B624B620B60FFF783
+:1009D0007FFD10BD00100240FFFF81FF55555555AA
+:1009E0005FF7FFFF05515565A0040028000400488B
+:1009F0000008004855555501000C00485055555504
+:100A0000001400480000000000000000000000008A
+:100A10007047C04600000000000000000000000019
+:100A2000012310B5084C23700023636000F050FAD6
+:100A300000F08EFA00F0ACFA0449054800F068F9BD
+:100A40000223237062B610BDA809002068120008B6
+:100A50001809002000000000000000000000000055
+:100A600072B68822024B9850024B853A1A70FEE704
+:100A700018090020A8090020000000000000000064
+:100A800010B500F03DF810BD0000000000000000AF
+:100A900010B5036100230C4C43612369C2609A685E
+:100AA000914204D91B68891A9A688A42FAD35A6813
+:100AB00081600360426010609A685860521A9A60C0
+:100AC00001235B42A36110BD180900200000000053
+:100AD000036881689A6852189A60426813605A6085
+:100AE000002301220360024B52429A617047C046C4
+:100AF00018090020000000000000000000000000B5
+:100B000070B51B4DEB692C690133EB612B00103381
+:100B10009C4215D0A368013BA3600FE02268616886
+:100B20000A605160236062B620002169E36898473B
+:100B300072B66369002B04D12C69A368002BEDD039
+:100B400070BD2A6991688B4204D912685B1A91685A
+:100B50009942FAD35168A360616022600C60916889
+:100B60005460CB1A936001235B42AB61E4E7C0465B
+:100B70001809002000000000000000000000000034
+:100B800072B62423CB5C0C2B0BD8114A9B00D35894
+:100B90009F468A6A9368013393604A680B681360C2
+:100BA0005A60012300225B428B622533CA548B6951
+:100BB00088681B689A688242FBD25A680B604A6058
+:100BC0001160596062B6704700228B6A1A60E8E7CC
+:100BD0007C1200080000000000000000000000007F
+:100BE00024230022C254836981681B689A688A4260
+:100BF000FBD25A6803604260106058607047C0467C
+:100C00002422074B10B5D9688854186804686360BB
+:100C10001C6001248454D860FFF7BEFA10BDC046A2
+:100C20001809002000000000000000000000000083
+:100C300070B5164C0500E66886B04B1C19D0330021
+:100C40006846134AFFF724FF2423E1682068CD5447
+:100C50000268546022600122C254E060FFF79CFAEF
+:100C6000009B002B02D06846FFF732FFB06A06B047
+:100C700070BD2423F0542068310002685460226063
+:100C80000122C254E060FFF787FAEFE7180900205D
+:100C9000810B0008000000000000000000000000C0
+:100CA000044BDA68196890688B689842804140422A
+:100CB0007047C04618090020000000000000000036
+:100CC0000D4B10B51868D9680268012453601A608A
+:100CD000D8602422002384548B548B698C681B6851
+:100CE0009A68A242FBD85A680B604A60116059604A
+:100CF000FFF752FA10BDC04618090020000000009E
+:100D0000FEE7C046000000000000000000000000F8
+:100D1000F0B5002604001D4B87B0066381630D000B
+:100D2000586000F06DF923002833A362E362183B9A
+:100D30002361636101235B42A361154BE661DB69BB
+:100D40000027A660666321001E0088232662676272
+:100D5000246064603C31E75020000E4A083B00F0FC
+:100D60001FF86B68E06024220362012383540A4A5F
+:100D700003930092EA68094B01922A69684602923D
+:100D80000493059700F064F807B0F0BDA8090020AF
+:100D900018090020B012000844120008010D0008D4
+:100DA00010B5040008008B60436402210023818495
+:100DB0000364C363013926338461C154C2612200D4
+:100DC000030028320261E26A103342611360E36279
+:100DD0001C33C36204330363436310BD000000008F
+:100DE0008368020018005168503830B5016219005C
+:100DF00014697439C1600C61546902254C61114C4D
+:100E00000C621468D1681A002C3A81601580416424
+:100E100000210D4A0164C163013D263182614554C0
+:100E2000C46119001400403928340C60D46A4C6045
+:100E30002160D1621A00203B243A12601B605B6083
+:100E400030BDC046B90100081809002000000000AC
+:100E500010B5FFF7C5FFFFF7C3FE10BD000000008F
+:100E600070B572B6114BDC682500A062E06A2C35C3
+:100E7000854207D12623E35C002B0ED00F20FFF71D
+:100E8000BFFE70BD0368E362FFF7AAFEE06AA842F6
+:100E9000F8D12623E35C002BF0D12533E35C9B07DC
+:100EA000ECD16269236913605A60E7E718090020F2
+:100EB00010B5010072B60820FFF7BAFE62B610BD89
+:100EC00000230060406083607047C046000000005F
+:100ED000024B034A1A60034A5A607047B009002067
+:100EE000280A002000200020000000000000000070
+:100EF00070B5094C26686568AB1B984209D82B1A57
+:100F000048421840821A964203D8954201D3626043
+:100F100070BD0020FCE7C046B009002000000000C2
+:100F200070B572B6094C26686568AB1B98420AD842
+:100F30002B1A48421840821A964204D8954202D38E
+:100F4000626062B670BD0020FBE7C046B0090020B9
+:100F50000548064B10B503600023436083600C30E6
+:100F6000FFF7AEFF10BDC046B8090020210F0008F2
+:100F700010B50024046041608260C36010BDC046AB
+:100F800010B50022FFF7B4FF10BDC04600000000FE
+:100F900070B5134C2000FFF793FF23002000114D84
+:100FA0001033236104222B0014211430FFF7E0FFDB
+:100FB000230024336362230020002833A362042229
+:100FC0002B001C212C30FFF7D3FF23003C33E363BD
+:100FD00023004033236423004433636470BDC04660
+:100FE000D4090020810F000800000000000000006C
+:100FF000EFF30983203383F3098862B67047C04654
+:10100000024A0349136A0B401362704700ED00E087
+:10101000FFFF00FF000000000000000000000000D3
+:1010200010B50F3000D110BD72B6EFF30984203C2B
+:1010300084F3098880235B04E361FFF731FE002815
+:1010400002D0034BA361EEE7024BA361EBE7C0467E
+:10105000C7010008CA0100080000000000000000ED
+:10106000054A1368013313608023044ADB01918A27
+:101070004B4093827047C046240A00200004004879
+:101080008021054A0902136801331360034A938AD9
+:101090004B4093827047C046200A0020000400485D
+:1010A000024A1368013313607047C0461C0A0020CF
+:1010B000F0B5DE4657464E464546E0B583B0FFF7ED
+:1010C0006FF9FFF7ADFC8021304B01221800C901F8
+:1010D0009846FFF775FA8021012240460902FFF782
+:1010E0006FFA80234246DB0593618023284E1B065E
+:1010F000936130002749FFF77BF9274D27492800E6
+:10110000FFF776F9264C27492000FFF771F93000E8
+:101110002549FFF77DF9FA212800C900FFF778F982
+:10112000FA212000C900FFF773F9204E204D3768DF
+:10113000204C2B68994623689A461F4B01938023C5
+:10114000DB019B461D48FFF7B3FE33682A68083B66
+:101150002168DB1B042B09D84B462D3AD21A0A2AE8
+:1011600004D853460839C91A042909D943465A46AE
+:101170009A61802342461B0201989361FFF770FC3D
+:1011800037682B68994623689A46DBE700040048D5
+:1011900028080020F412000818080020E8120008AF
+:1011A00008080020DC12000810270000240A002094
+:1011B000200A00201C0A00204C120008A086010012
+:1011C00004130008280A0020280A0020280A00200A
+:1011D00004130008000000000000000000000000F0
+:1011E00004130008000000000000000000000000E0
+:1011F00004130008000000000000000000000000D0
+:1012000004130008000000000000000000000000BF
+:1012100004130008000000000000000000000000AF
+:10122000041300080000000000000000000000009F
+:10123000041300080000000000000000000000008F
+:101240006330000069646C650000000054494D2063
+:10125000696E7465727275707420636F756E7469EF
+:101260006E67204641494C00401200080000000013
+:10127000000000003808002018090020C40B0008F6
+:10128000A20B0008A20B0008C80B00089A0B00086C
+:10129000920B0008A20B00089A0B0008A20B000892
+:1012A000A20B0008A20B0008A20B00089A0B000872
+:1012B0006D61696E002C063804044C080C10141C77
+:1012C0002024252600000000000024040100040062
+:1012D00000000000000C001028303400D00700008F
+:1012E000A110000800000000102700008110000875
+:1012F000000000001027000061100008000000003E
+:0413000000A24A04F9
+:04000005080001915D
+:00000001FF