EMLIB to Peripheral HAL Migration Guide: Migration Patterns#
Purpose: This section documents common transformation patterns that occur repeatedly across the migration. Understanding these patterns enables systematic code transformation and reduces errors.
Pattern Overview#
EMLIB Pattern | Applies To | Complexity | Key Change in Peripheral HAL |
|---|---|---|---|
[A] Init with Peripheral Enable | All peripherals that has an enable (EN) register | Low | Split |
[B] Init with Conditional Operation Start | TIMER, LETIMER | Medium | Handle |
[C] Init with Feature Enable | EUSART, USART | Medium | Three-step enable sequence |
[D] Boolean Parameter Split | Runtime control functions with boolean parameters | Low | Boolean → separate functions |
[E] Parameter Consolidation | GPIO pin functions | Medium | Separate params → structure |
Pattern A: Init with Peripheral Enable#
EMLIB Pattern:
LDMA_Init_TypeDef init = LDMA_INIT_DEFAULT;
// Single function call: configure + enable peripheral
LDMA_Init(LDMA0, &init);Peripheral HAL Pattern:
sl_hal_ldma_init_t init = SL_HAL_LDMA_INIT_DEFAULT;
// Split into two explicit calls
sl_hal_ldma_init(LDMA0, &init); // Step 1: Configure
sl_hal_ldma_enable(LDMA0); // Step 2: EnableTransformation Rules:
Replace
<PERIPHERAL>_Init()withsl_hal_<peripheral>_init()Add
sl_hal_<peripheral>_enable()call afterinit()More might be needed depending on the peripheral (see the next migration patterns)
Rationale:
Peripheral HAL separates configuration from enablement
This allows configuration changes in WSTATIC registers without disabling the peripheral
Explicit control over when peripheral becomes enabled
Pattern B: Init with Conditional Operation Start#
When this pattern applies:
Peripherals with ongoing operations (TIMER, LETIMER)
EMLIB
init.enablefield controls operation start, not peripheral enableNeed to distinguish between peripheral enable and operation start
EMLIB Pattern:
// init.enable controls whether counting starts
TIMER_Init_TypeDef init = TIMER_INIT_DEFAULT;
init.enable = true; // Start counting after init
TIMER_Init(TIMER0, &init);
// ↑ After this: peripheral enabled AND counting startedPeripheral HAL Pattern:
// Three separate operations: config, enable peripheral, start operation
sl_hal_timer_init_t init = SL_HAL_TIMER_INIT_DEFAULT;
sl_hal_timer_init(TIMER0, &init); // Step 1: Configure
sl_hal_timer_enable(TIMER0); // Step 2: Enable peripheral
sl_hal_timer_start(TIMER0); // Step 3: Start countingTransformation Rules:
Replace
TIMER_Init()withsl_hal_timer_init()Always add
sl_hal_timer_enable()afterinit()(peripheral must be enabled)Check if original
init.enablewastrueIf yes, add
sl_hal_timer_start()afterenable()If no, omit the
start()call (peripheral enabled but not counting)
Rationale:
Series 2/3 hardware has separate enable and start/stop mechanisms
Peripheral enable must be done first
Operation start/stop can be done while enabled
This pattern gives flexibility: enable peripheral once, start/stop multiple times
Pattern C: Init with Feature Enable#
When this pattern applies:
Communication peripherals (EUSART, USART)
EMLIB
init.enablefield controls transmitter/receiver enable, not peripheral enableThree-level enable hierarchy: bus clock → peripheral → TX/RX
EMLIB Pattern:
// init.enable controls TX/RX, peripheral enable is a side effect
EUSART_UartInitHf_TypeDef init = EUSART_UART_INIT_DEFAULT_HF;
init.enable = eusartEnable; // Enable both TX and RX
EUSART_UartInitHf(EUSART0, &init);
// ↑ After this: peripheral enabled AND TX/RX enabledPeripheral HAL Pattern:
// Three explicit steps: config, enable peripheral, enable TX/RX
sl_hal_eusart_config_uart_t config = SL_HAL_EUSART_CONFIG_UART_DEFAULT;
sl_hal_eusart_init_uart(EUSART0, &config); // Step 1: Configure
sl_hal_eusart_enable(EUSART0); // Step 2: Enable peripheral
sl_hal_eusart_enable_tx(EUSART0); // Step 3a: Enable transmitter
sl_hal_eusart_enable_rx(EUSART0); // Step 3b: Enable receiverTransformation Rules:
Replace
EUSART_UartInitHf()withsl_hal_eusart_init_uart()Always add
sl_hal_eusart_enable()afterinit()Map
init.enablevalue to appropriate enable functions:eusartEnable→ call bothsl_hal_eusart_enable_tx()andsl_hal_eusart_enable_rx()eusartEnableTx→ call onlysl_hal_eusart_enable_tx()eusartEnableRx→ call onlysl_hal_eusart_enable_rx()eusartDisable→ omit TX/RX enable calls
Rationale:
Separates peripheral-level enable from feature-level enable
Allows reconfiguration of TX/RX without full reinitialization
Matches hardware's hierarchical enable structure
Pattern D: Boolean Parameter Split#
When this pattern applies:
Runtime control functions (not
init()functions) with boolean parametersEMLIB uses single function with boolean to control binary operations (enable/disable, start/stop, etc.)
HAL provides separate, semantically clear functions
EMLIB Pattern:
// Single function with boolean parameter
TIMER_Enable(TIMER0, true); // Start counting
TIMER_Enable(TIMER0, false); // Stop counting
TIMER_EnableDTI(TIMER0, true); // Enable DTI
TIMER_EnableDTI(TIMER0, false); // Disable DTI
CMU_ClockEnable(cmuClock_GPIO, true); // Enable clock
CMU_ClockEnable(cmuClock_GPIO, false); // Disable clockPeripheral HAL Pattern:
// Separate, semantically clear functions
sl_hal_timer_start(TIMER0);
sl_hal_timer_stop(TIMER0);
sl_hal_timer_dti_enable(TIMER0);
sl_hal_timer_dti_disable(TIMER0);
sl_clock_manager_enable_bus_clock(SL_BUS_CLOCK_GPIO);
sl_clock_manager_disable_bus_clock(SL_BUS_CLOCK_GPIO);Transformation Rules:
Identify function calls with boolean parameter controlling binary operations
Understand what the function actually does (not just what the name suggests)
Replace with appropriate HAL function pair:
true→sl_hal_*_enable(),sl_hal_*_start(), orsl_clock_manager_enable_bus_clock()false→sl_hal_*_disable(),sl_hal_*_stop(), orsl_clock_manager_disable_bus_clock()
Remove the boolean parameter
Rationale:
Separate functions are more explicit and self-documenting
Eliminates conditional logic in favor of direct function calls
HAL uses accurate, descriptive function names that reflect actual behavior
Clearer intent at call sites
Pattern E: Parameter Consolidation#
When this pattern applies:
GPIO functions that take separate
portandpinparametersEMLIB uses two separate parameters for port and pin identification
HAL consolidates these into a single
sl_gpio_t{}structure
EMLIB Pattern:
// Separate port and pin parameters
GPIO_PinModeSet(gpioPortA, 5, gpioModePushPull, 1);
GPIO_PinOutClear(gpioPortA, 5);
GPIO_PinOutSet(gpioPortA, 5);Peripheral HAL Pattern:
// Consolidated into sl_gpio_t{} structure
sl_gpio_t gpio = {.port = SL_GPIO_PORT_A, .pin = 5};
sl_hal_gpio_set_pin_mode(&gpio, SL_GPIO_MODE_PUSH_PULL, true);
sl_hal_gpio_clear_pin(&gpio);
sl_hal_gpio_set_pin(&gpio);Transformation Rules:
Create
sl_gpio_t{}structure withportandpinfieldsConvert port constant (
gpioPortX→SL_GPIO_PORT_X)Pass structure by pointer to HAL functions
Convert mode constants (
gpioModeX→SL_GPIO_MODE_X)Convert output value from
unsigned inttobool
Rationale:
Structure provides better type safety
Easier to pass GPIO pin information as single parameter
Reduces parameter count in function signatures
Aligns with modern C practices
Pattern Summary#
When migrating code:
Identify the EMLIB pattern by checking the peripheral and function type
Follow the transformation rules specific to that pattern
Test for behavioral equivalence (see Common Pitfalls and Solutions)
Advice: Achieve behavioral equivalence at first - the sequence of register access should the same state after the migration. You can rework the code afterwards to better optimize using the simpler APIs of HAL peripherals.
Quick Reference:
EMLIB Code | Pattern | HAL Equivalent |
|---|---|---|
| A |
|
| A |
|
| B |
|
| B |
|
| C |
|
| C |
|
| D |
|
| D |
|
| E | Create |
| E | Create |