EMLIB to Peripheral HAL Migration Guide: Appendix#

Glossary#

Term

Definition

EMLIB

Legacy peripheral library for Series 0/1/2 devices (em_*.h)

Peripheral HAL

Modern peripheral library for Series 2/3 devices (sl_hal_*.h)

Behavioral Equivalence

Migrated code produces identical peripheral state as original code

Clock Manager

Service (sl_clock_manager) that manages peripheral bus clocks

Power Manager

Service (sl_power_manager) that coordinates sleep modes across components

Asynchronous API

API design where operations are explicitly decoupled (Peripheral HAL style)

Synchronous API

API design where operations are combined atomically (EMLIB style)

Init Structure

Configuration structure passed to init functions (e.g., TIMER_Init_TypeDef)

Enable Field

Field in init structure (semantics vary by peripheral)

Peripheral Enable

Powering on the peripheral hardware (allows register access)

Operation Start

Beginning the peripheral's function (e.g., start counting, start transmitting)

Feature Enable

Enabling specific peripheral features (e.g., TX/RX, interrupts)

Bus Clock

Clock that provides timing to peripheral bus interface

EM (Energy Mode)

Power state (EM0 = active, EM1 = sleep, EM2 = deep sleep, EM3 = stop, EM4 = shut down)

WSTATIC

A register type that can only be written while the register is disabled, otherwise a bus fault will occur

Related Documentation#

FAQ#

General Migration#

Q: Do I have to migrate all peripherals at once?

A: Not necessarily for Series 2. You can migrate incrementally, peripheral by peripheral. EMLIB and Peripheral HAL can coexist in the same project, allowing you to migrate one peripheral at a time.

Q: Will my EMLIB code stop working on Series 2 devices?

A: EMLIB is still supported on Series 2 devices but is in maintenance mode; no new features, only critical bug fixes. For Series 3, EMLIB is not supported at all.

Q: How much will my code size change?

A: Code size typically decreases with Peripheral HAL because it avoids static inlining used in EMLIB, resulting in smaller overall binary size. However, the exact change depends on your application's peripheral usage patterns.

Q: Can I use EMLIB and Peripheral HAL in the same project?

A: Yes. EMLIB and Peripheral HAL can coexist in the same project. You can use them for different peripherals, or even for the same peripheral instance during migration.

Technical Details#

Q: Why doesn't sl_hal_cmu exist?

A: Clock management has been redesigned as a service (sl_clock_manager) rather than a HAL driver. This provides better abstraction, automatic dependency management, and integration with Power Manager. See Pitfall 5: Using CMU Functions for Clock Management for details.

Q: Why aren't there energy mode entry functions in sl_hal_emu?

A: Sleep mode entry has been redesigned as a service (sl_power_manager) that coordinates power requirements across all application components. This prevents conflicts and automatically selects the deepest safe sleep mode. Direct sleep entry would bypass this coordination. See Pitfall 6: Using EMU Functions for Sleep Modes for details.

Q: What does the enable field in EMLIB init structures control?

A: It varies by peripheral. This is a major source of confusion:

  • TIMER/LETIMER: Controls whether operation starts (true = start counting)

  • EUSART/USART: Controls TX/RX enable (e.g., eusartEnable = enable both TX and RX)

  • Other peripherals: May control peripheral enable

Always check the specific peripheral's pattern in Migration Patterns to understand what enable means.

Q: Do I need to call sl_hal_<peripheral>_enable() for every peripheral?

A: Yes, almost always. In Peripheral HAL, peripheral enable is separate from configuration. After calling sl_hal_<peripheral>_init(), you must call sl_<hal_peripheral>_enable() to activate the peripheral. The only exception is if you're intentionally keeping the peripheral disabled.

Q: How do I know which SL_BUS_CLOCK_* to use?

A: Use the same peripheral name: cmuClock_TIMER0 becomes SL_BUS_CLOCK_TIMER0, cmuClock_GPIO becomes SL_BUS_CLOCK_GPIO, etc. The mapping is straightforward.

Q: Why does it seem like there's no equivalent of an EMLIB function in sl_hal?

A: This often occurs because the EMLIB function was doing too much for the low-level driver philosophy of Peripheral HAL. EMLIB sometimes combined multiple hardware operations into a single high-level convenience function with state management, while Peripheral HAL keeps operations simple and explicit.

For example, I2C_TransferInit() and I2C_Transfer() have no direct HAL equivalents because they combine start/stop commands, address transmission, data transfer, state machine management, and error handling into one function. For I2C, you have two options: use a higher-level driver like sl_i2cspm (recommended), or manually implement the transfer using a sequence of HAL primitives (sl_hal_i2c_start(), sl_hal_i2c_write_address(), sl_hal_i2c_write_data(), etc.) with custom state management.

Q: Are there any peripherals that don't follow the standard patterns?

A: Most common peripherals follow patterns A, B, C, or D from Migration Patterns. Some specialized peripherals may have unique requirements; always consult the peripheral-specific HAL documentation.

Q: What's the difference between sl_hal_timer_enable() and sl_hal_timer_start()?

A: You must enable before you can start. Enable is usually done once; start/stop can be called many times.

  • sl_hal_timer_enable(): Enables the peripheral (powers it on, allows register writes)

  • sl_hal_timer_start(): Starts the counting operation (timer begins incrementing)

Troubleshooting#

Q: My peripheral doesn't work after migration, but there are no compile errors. How do I debug?

A: Follow this checklist:

  1. Clock enabled? Did you call sl_clock_manager_enable_bus_clock()?

  2. Peripheral enabled? Did you call sl_hal_<peripheral>_enable()?

  3. Operation started? For TIMER, did you call sl_hal_timer_start()?

  4. TX/RX enabled? For EUSART, did you call sl_hal_eusart_enable_tx() and/or sl_hal_eusart_enable_rx()?

  5. Interrupts configured? Did you enable and configure interrupts if needed?

  6. Pin routing? Did you configure pin routing (GPIO alternate functions)?

Q: I get a hard fault when accessing peripheral registers. Why?

A: Most likely, you didn't enable the bus clock before accessing the peripheral. Always call sl_clock_manager_enable_bus_clock() before any other peripheral operations.

Q: The TIMER peripheral is enabled but not counting. What's wrong?

A: You probably called sl_hal_timer_enable() but forgot sl_hal_timer_start(). Remember: enable() powers on the peripheral, start() begins the operation. See Pattern B: Init with Conditional Operation Start.

Q: EUSART transmits but doesn't receive (or vice versa). What happened?

A: Check that you called both sl_hal_eusart_enable_tx() and sl_hal_eusart_enable_rx(). You may have only enabled TX or only enabled RX. Both must be called separately if you need bidirectional communication. See Pattern C: Init with Feature Enable.

Q: How do I verify my migration is correct?

A: Use the debugging strategies:

  1. Register dump: Compare peripheral register values before/after migration

  2. Behavioral testing: Verify identical outputs for identical inputs

  3. Logic analyzer: Visual confirmation of signal timing and behavior