Contents Menu Expand Light mode Dark mode Auto light/dark, in light mode Auto light/dark, in dark mode Skip to content
Embedded Linux on i.MX6ULL
Embedded Linux on i.MX6ULL
Download PDF

Part I — Foundations

  • Chapter 1: Preface and how to use this book
  • Chapter 2: What “Embedded Linux” actually is
  • Chapter 3: Host environment setup
  • Chapter 4: ARMv7-A and the Cortex-A7, for the MCU engineer
  • Chapter 5: A tour of the i.MX6ULL SoC
  • Chapter 6: The toolchain
  • Chapter 7: The Boot ROM, IVT, DCD, and BootData
  • Chapter 8: Hardware bring-up checklist

Part II — Bare-metal i.MX6ULL

  • Chapter 9: First LED, pure assembly
  • Chapter 10: C + startup.S + linker script
  • Chapter 11: Hand-building a Boot ROM-acceptable image
  • Chapter 12: UART driver and printf
  • Chapter 13: CCM clock tree bring-up
  • Chapter 14: DDR3 initialization with MMDC
  • Chapter 15: Exceptions and the GIC
  • Chapter 16: Timers, EPIT and GPT
  • Chapter 17: MMU and caches
  • Chapter 18: Optional bare-metal peripherals
  • Chapter 18A: Project organization
  • Chapter 18B: Button input and beep
  • Chapter 18C: Bare-metal RTC

Part III — U-Boot, deeply

  • Chapter 19: U-Boot from source, first boot
  • Chapter 20: U-Boot SPL: the missing link
  • Chapter 21: U-Boot internals
  • Chapter 22: Porting U-Boot to the MINI
  • Chapter 23: bootcmd, bootargs, FIT images
  • Chapter 24: Workflows, TFTP, NFS, USB-OTG
  • Chapter 24A: Building an i.MX6ULL teaching port from scratch
  • Chapter 24B: U-Boot board policy with GPIO and I2C
  • Chapter 24C: Ethernet fallback boot in U-Boot
  • Chapter 24D: Board identity and variant selection in U-Boot
  • Chapter 24E: Multi-variant FIT images and DT overlays
  • Chapter 24F: Watchdog, bootcount, and rollback in U-Boot
  • Chapter 24G: Factory and recovery modes in U-Boot
  • Chapter 24H: PMIC and power policy in U-Boot
  • Chapter 24I: U-Boot display and boot screen
  • Chapter 24J: SPI LCD and I2C OLED displays in U-Boot

Part IV — The Kernel

  • Chapter 25: Building mainline Linux for i.MX6ULL
  • Chapter 26: Booting the kernel from U-Boot
  • Chapter 26A: Kernel boot failure playbook
  • Chapter 27: Device Tree: the contract between firmware and kernel
  • Chapter 27A: DT bindings YAML and dt_binding_check
  • Chapter 27B: Device Tree for a custom i.MX6ULL board
  • Chapter 28: Kernel startup, traced
  • Chapter 29: Initramfs from scratch
  • Chapter 29A: Initramfs as a recovery system
  • Chapter 30: Kernel configuration deep-dive
  • Chapter 30A: Kernel lifecycle: mainline, stable, LTS, vendor BSPs
  • Chapter 30B: Product kernel configs

Part V — Root filesystem & user space

  • Chapter 31: A root filesystem, by hand
  • Chapter 32: /proc, /sys, devtmpfs
  • Chapter 33: Init systems
  • Chapter 34: libc, dynamic linking, and the loader
  • Chapter 35: Buildroot, after you can do it by hand
  • Chapter 35D: Bootable SD and eMMC image layout
  • Chapter 35I: Boot from SPI NOR flash
  • Chapter 35E: Buildroot product board directory
  • Chapter 35A: Ubuntu-base rootfs as a peer to BusyBox/Buildroot
  • Chapter 35F: Your application as a service
  • Chapter 35G: Networking, time, and logging
  • Chapter 35B: Read-only rootfs + overlayfs
  • Chapter 35H: Rootfs security basics
  • Chapter 35C: Container runtimes on embedded
  • Appendix — Userspace tooling reference

Part VI — Driver development (foundations)

  • Chapter 36: Your first kernel module
  • Chapter 37: A character driver, by hand
  • Chapter 38: Auto-creating /dev/ nodes
  • Chapter 39: Platform drivers + device tree
  • Chapter 40: The misc framework
  • Chapter 41: Concurrency in the kernel
  • Chapter 42: Sleeping, waiting, polling
  • Chapter 43: Interrupts

Part VI — Driver development (common subsystems)

  • Chapter 44: GPIO subsystem + pinctrl
  • Chapter 45: Input subsystem
  • Chapter 46: I²C drivers
  • Chapter 47: SPI drivers
  • Chapter 48: PWM and RTC subsystems
  • Chapter 49: IIO subsystem (ADCs, sensors)
  • Chapter 50: regmap

Part VI — Driver development (advanced + insertions)

  • Chapter 51: DMA
  • Chapter 51A: Watchdog
  • Chapter 51B: Power management
  • Chapter 52: Network driver: FEC + KSZ8081
  • Chapter 52A: PREEMPT_RT
  • Chapter 53: Sound: ALSA and ASoC
  • Chapter 54: LCD framebuffer and DRM/KMS
  • Chapter 54A: MTD / UBI for raw NAND
  • Chapter 54B: V4L2 + GStreamer
  • Chapter 55: USB gadget
  • Chapter 55A: Kernel timers and hrtimers
  • Chapter 55B: Async notification (SIGIO)
  • Chapter 55C: CAN bus (SocketCAN + FlexCAN)
  • Chapter 55D: Block device drivers
  • Chapter 55E: WiFi + wpa_supplicant
  • Chapter 55F: Cellular modems
  • Chapter 55G: Multi-touch (GT911)
  • Chapter 55H: RGB-to-HDMI bridge (sii902x)
  • Chapter 55I: Rust for Linux

Part VII — Device cookbook (Storage)

  • Chapter 64: QSPI NOR flash
  • Chapter 65: I²C / SPI EEPROM
  • Chapter 66: SD card and eMMC deep dive

Part VII — Device cookbook (Environmental sensors)

  • Chapter 67: Temperature / humidity / pressure
  • Chapter 68: Light & color sensors
  • Chapter 69: Air quality, gas, particulate matter

Part VII — Device cookbook (Motion sensors)

  • Chapter 70: I²C IMUs
  • Chapter 71: SPI IMUs

Part VII — Device cookbook (Position & distance)

  • Chapter 72: Distance & proximity sensors
  • Chapter 73: Magnetometer / compass
  • Chapter 74: Hall-effect & rotary position sensors

Part VII — Device cookbook (Power & current)

  • Chapter 75: Current & power monitoring
  • Chapter 76: Battery fuel gauge + charger

Part VII — Device cookbook (Specialty sensors)

  • Chapter 77: 1-Wire sensors
  • Chapter 78: MEMS microphones
  • Chapter 79: Health sensors

Part VII — Device cookbook (ADC / DAC / clocks)

  • Chapter 80: External ADCs
  • Chapter 81: External DACs + clock generators

Part VII — Device cookbook (Displays)

  • Chapter 82: RGB parallel LCD on LCDIF
  • Chapter 83: SPI LCD
  • Chapter 84: QSPI LCD
  • Chapter 85: OLED & e-paper
  • Chapter 86: Touch input ICs

Part VII — Device cookbook (Cameras)

  • Chapter 87: Parallel CSI cameras
  • Chapter 88: USB UVC cameras

Part VII — Device cookbook (Audio)

  • Chapter 89: I²S audio codecs
  • Chapter 90: Digital class-D amplifiers

Part VII — Device cookbook (WiFi)

  • Chapter 91: SDIO WiFi
  • Chapter 92: USB WiFi
  • Chapter 93: Hosted WiFi via ESP32 / ESP8266
  • Chapter 94: WiFi+BT combo modules

Part VII — Device cookbook (Bluetooth)

  • Chapter 95: HCI Bluetooth over UART/USB
  • Chapter 96: AT-command BLE modules
  • Chapter 97: BLE Mesh

Part VII — Device cookbook (Long-range & specialty wireless)

  • Chapter 98: LoRa
  • Chapter 99: Sub-GHz proprietary
  • Chapter 100: ZigBee / Thread / 802.15.4
  • Chapter 101: UWB ranging

Part VII — Device cookbook (Cellular)

  • Chapter 102: USB 4G LTE modems
  • Chapter 103: UART AT-command modems
  • Chapter 104: NB-IoT / Cat-M1

Part VII — Device cookbook (Identification)

  • Chapter 105: RFID / NFC
  • Chapter 106: Fingerprint sensors

Part VII — Device cookbook (Positioning)

  • Chapter 107: GPS / GNSS + PPS

Part VII — Device cookbook (Industrial buses)

  • Chapter 108: RS-485 + Modbus RTU
  • Chapter 109: LIN bus
  • Chapter 110: CAN deep dive

Part VII — Device cookbook (Motors & encoders)

  • Chapter 111: Quadrature encoders & rotary
  • Chapter 112: Stepper & DC motor drivers

Part VII — Device cookbook (Indicators & actuators)

  • Chapter 113: WS2812 / SK6812 / APA102 addressable LEDs
  • Chapter 114: Beepers, relays, SSRs

Part VII — Device cookbook (Network & system power)

  • Chapter 115: Dual FEC + hosted Ethernet
  • Chapter 116: PMICs and regulator framework
  • Chapter 117: External RTC

Part VIII — Debug, production, advanced

  • Chapter 118: JTAG, OpenOCD, GDB at every layer
  • Chapter 119: Kernel debugging without JTAG
  • Chapter 120: User-space debugging
  • Chapter 120A: Mainline patch submission workflow
  • Chapter 121: Custom board port as a system design
  • Chapter 121A: CI/CD for embedded Linux
  • Chapter 122: Build your own cross-toolchain
  • Chapter 122A: BSP → mainline migration playbook
  • Chapter 123: Yocto vs Buildroot, an honest comparison
  • Chapter 123A: Yocto layer development in depth
  • Chapter 124: Secure boot (HAB) and OP-TEE
  • Chapter 125: Field updates
  • Chapter 125A: VSCode + gdbserver remote-debug workflow
  • Chapter 126: Closing: what to read next

Part IX — Applied virtualization

  • Chapter 127: Why embedded products use hypervisors
  • Chapter 128: QEMU as a virtual hardware model
  • Chapter 129: Build and boot tiny Linux in QEMU
  • Chapter 130: U-Boot in QEMU
  • Chapter 131: HYP mode, stage-2 MMU, and virtual interrupts
  • Chapter 132: Xen in QEMU
  • Chapter 133: First DomU Linux
  • Chapter 134: Xen on i.MX6ULL
  • Chapter 135: DomU Linux on i.MX6ULL
  • Chapter 136: Devices, memory, and DMA boundaries
  • Chapter 137: Jailhouse in QEMU ARM64
  • Chapter 138: Bare-metal or Zephyr inmate cell
  • Chapter 139: STM32MP1 Linux plus RTOS, the production pattern

Front matter

  • Table of contents
  • Status
Back to top
View this page
Edit this page
Download PDF

Chapter 20: U-Boot SPL: the missing link¶

Chapter 19 left us with a large U-Boot image and a small question: how could the ROM load it into DDR before U-Boot’s C code had initialized that DDR?

For our MINI vendor configuration, the answer was the DCD. The ROM interpreted its register writes first. Another board can give that job to a small program instead. That program is SPL, the Secondary Program Loader.

SPL is an alternative boot stage, not a file missing from our MINI build. Knowing which route you have is more useful than memorizing a universal “ROM, SPL, U-Boot” diagram. We will read an actual SPL implementation, then compare its responsibilities with the MINI’s ROM-driven setup. The comparison uses another board’s source on the host. It does not replace our MINI firmware.

20.1 Why two stages¶

DDR cannot hold a useful stack or executable image until its controller, pads, clocks, and memory device have been configured. The startup program must therefore begin in memory that is already usable.

Two routes solve that dependency on this SoC family:

The ROM and DCD route used by MINI and the reference EVK prepares DDR before full U-Boot. An alternative SPL route initializes DDR from a small program in OCRAM.

Our MINI uses the upper route too. The EVK label names another example of ROM-interpreted DDR setup, not the board we need to buy. The lower route assigns that work to SPL code.¶

Route

Who prepares DDR?

Where the first application code runs

MINI ROM + DCD

The Boot ROM executes the image’s DDR setup data

Full U-Boot begins in DDR

ROM + SPL

A small executable runs its board-specific DDR setup

SPL begins in internal RAM, then loads full U-Boot into DDR

Both routes still need correct board-specific DDR values. Moving the writes from a DCD into C does not remove the electrical or calibration work from Chapter 14. It changes where that knowledge is expressed and how much software can run before the large image is loaded.

Some SPL configurations can select among memory arrangements, perform more complex initialization, authenticate a payload, or boot an OS directly. Those are configured features, not promises made by the name SPL.

20.2 What SPL is responsible for¶

For a typical i.MX6-family SPL loading full U-Boot, the responsibilities are:

  1. Establish a usable CPU mode, early stack, and global-data area.

  2. Prepare the clock and pad paths needed by its console, DDR, and boot medium.

  3. Initialize the fitted DDR arrangement.

  4. Initialize the selected SD, eMMC, SPI, or other loader path.

  5. Read and interpret the configured payload format.

  6. Place that payload at its load address and transfer control to its entry.

A console is often made available before DDR setup so that an early failure can be reported. The exact order belongs to the board implementation. Do not reorder the calls just to make them resemble a generic diagram.

Likewise, “SPL cannot use networking” is too broad. Some builds include it. The usual engineering question is whether a feature is needed before the larger runtime is available, and whether its memory and dependencies fit.

20.3 The size budget¶

The i.MX6ULL has 128 KiB of OCRAM. That physical capacity is not a promise that all 128 KiB are available to a loaded program. ROM activity, image placement, the early stack, global data, and any early allocation pool also need space. Use the selected image format, ROM constraints, configuration, and linker map together. Do not infer a free window by subtracting an approximate reservation quoted for another board.

For the reference SPL used below, CONFIG_SPL_MAX_SIZE is 0x10000, or 64 KiB. The Kconfig help describes this as the image limit excluding BSS. That is different from a limit on text + data + bss + stack.

In this family, an SPL may place BSS in DDR and clear it only after DDR setup. The early code must not use that BSS while DDR is unavailable. Its OCRAM code and stack budget and its later DDR budget are separate checks.

Record these fields from the generated configuration rather than copying another build’s size report:

Field or file

Check

CONFIG_SPL_TEXT_BASE

Where SPL code is linked

CONFIG_SPL_MAX_SIZE

Which loadable image limit the linker checks

SPL stack selection

Where the early stack begins and what lies below it

CONFIG_SPL_BSS_START_ADDR and CONFIG_SPL_BSS_MAX_SIZE

Where BSS lives and when that memory becomes usable

spl/u-boot-spl.map

Actual linked ranges and reservations

spl/u-boot-spl

ELF section sizes and addresses

SPL

The ROM-facing packaged file, including its header

Not every feature has a one-to-one CONFIG_SPL_FOO switch. Dependencies, shared framework code, and stage-specific defaults matter too. Inspect the expanded configuration after changing a feature.

20.4 Where SPL lives in the source¶

Our MINI baseline has no SPL to inspect. For this comparison and the modern internals examples that follow, create a separate upstream v2026.04 study tree. Keep the vendor tree and its MINI image from Chapter 19 unchanged:

$ cd ~/imx6ull/src
$ git clone --branch v2026.04 https://source.denx.de/u-boot/u-boot.git u-boot
$ cd u-boot
$ git rev-parse HEAD
88dc2788777babfd6322fa655df549a019aa1e69

If that directory already exists, inspect its revision and preserve local changes instead of cloning over it. This fixed release gives Chapters 20-24J one set of modern APIs to discuss. Their C patches belong here, not in the 2016 vendor tree. Chapter 22 maps the MINI’s hardware into this newer framework.

We also need a full-U-Boot reference for Chapter 21’s map and command example:

$ . ~/imx6ull/scripts/env.sh
$ make O="$HOME/imx6ull/build/uboot-evk" mx6ull_14x14_evk_defconfig
$ make O="$HOME/imx6ull/build/uboot-evk" -j4

This is a host-side comparison build, not an image to flash to MINI. Its host tools may need libgnutls28-dev in the Chapter 3 lab Ubuntu environment. That dependency does not change either project-local Arm toolchain.

Now use a real SPL-enabled configuration. In this v2026.04 tree, use pico-imx6ul_defconfig as a separate host-side reference. It describes a TechNexion PICO i.MX6UL platform, not the i.MX6ULL EVK and not our MINI. Do not flash its outputs to either board.

$ . ~/imx6ull/scripts/env.sh
$ cd ~/imx6ull/src/u-boot
$ make O="$HOME/imx6ull/build/uboot-pico-study" pico-imx6ul_defconfig
$ make O="$HOME/imx6ull/build/uboot-pico-study" -j4
$ arm-none-linux-gnueabihf-size \
    ~/imx6ull/build/uboot-pico-study/spl/u-boot-spl

The different O= directory keeps this configuration out of both the EVK study build and the MINI vendor build. The Arm-prefixed size program reads the target ELF on the host. It does not execute it. Its BSS count is useful, but does not describe stack usage or all packaging overhead. We are borrowing a source example, not changing the MINI’s documented boot route.

Read these source files with the generated .config beside them:

Path

Responsibility

board/technexion/pico-imx6ul/spl.c

This board’s early setup, DDR data, and MMC setup

arch/arm/cpu/armv7/start.S

Shared ARMv7 CPU startup

arch/arm/lib/crt0.S

Early C-runtime arrangements and stage-dependent paths

common/spl/spl.c

SPL framework and boot-device selection

common/spl/spl_mmc.c

MMC payload loading

arch/arm/mach-imx/mx6/ddr.c

Shared MMDC configuration routines

common/spl/Kconfig

Stage limits, formats, and loader defaults

An SPL file appears only when the build includes that stage. Looking for TI’s MLO, or guessing an .imx filename, is not a way to repair a no-SPL EVK build.

20.5 Reading start.S¶

Open arch/arm/cpu/armv7/start.S, then follow its branch into _main in arch/arm/lib/crt0.S. The names are familiar from Part II, but the framework must accommodate more than our one experiment.

Startup work

What to look for

Preserve incoming boot information

save_boot_params and its return label

Select or respect CPU mode, mask IRQ/FIQ

The CPSR manipulation and conditional mode check

Establish architecture state

cpu_init_cp15 and the selected critical-init path

Make early C calls possible

Initial stack selection, alignment, and global-data reservation in _main

Enter board setup

The call to board_init_f

The generic mode check is not evidence that this board normally enters in HYP mode. The handoff policy depends on the ROM or preceding firmware and the selected platform. Similarly, masking IRQ and FIQ does not make bad memory accesses harmless.

Notice the explicit r9 global-data setup in crt0.S. A stack alone is not the whole early environment. The framework gives early code a place to retain state even before its normal runtime is ready.

In v2026.04, assembly and shared code use CONFIG_XPL_BUILD for these small loader stages. Older excerpts may say CONFIG_SPL_BUILD. Read the pinned tree’s conditions, not just a function with a familiar name.

20.6 board_init_f, the “before relocation” stage¶

The PICO implementation’s sequence can be summarized as:

clock gates
    -> architecture and board early setup
    -> timer
    -> preloader console
    -> board DDR configuration
    -> BSS clear
    -> SPL board_init_r

This is a reading map, not replacement code. Open the actual function to see the calls and the DDR data they use.

The order explains something important: this implementation clears BSS after DDR setup because its BSS location is in DDR. That region is not available for an early timeout counter merely because the C declaration compiled.

Also notice that this board’s board_init_f calls board_init_r directly. Other SPL implementations return to crt0.S, which completes the stage’s runtime setup. The chosen board path decides. A direct call is not a universal rule that every function named board_init_f never returns.

Find the PICO DDR geometry and calibration structures, then follow the call to mx6_dram_cfg. They are examples of how one supported board supplies data to common MMDC code. They are not calibration results for our MINI.

20.7 board_init_r, the “after relocation” stage¶

Full U-Boot uses this name for its post-relocation runtime. SPL keeps the name for its later framework phase, even when its code has not relocated.

Follow board_init_r in common/spl/spl.c into boot-order selection, registered loaders, and the selected image-loading function. The framework is not simply a hard-coded call with BOOT_DEVICE_MMC1 on every board.

For the PICO raw-MMC reference, inspect CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR. The family default is 0x8A:

0x8A = 138 sectors
138 * 512 bytes = 70656 bytes = 69 KiB

That explains an offset you may have seen in other i.MX6 SPL instructions. It does not change the MINI image placement in Chapter 19. The loader’s expected payload format matters as well: a legacy u-boot.img, a FIT, and a ROM-facing DCD image are not interchangeable just because they contain U-Boot.

The loader records a load address, entry address, and size in struct spl_image_info. On success, the selected handoff enters that payload. On failure, the configured boot order may try another supported loader or stop. Find the actual error path before interpreting a later boot message.

20.8 Comparing SPL to your Ch 11 image-builder¶

Our Chapter 11 file included a leading pad, an IVT at file offset 0x400, BootData, a header gap, and an OCRAM payload. U-Boot’s packaged SPL has its own placement convention. Inspect the artifact rather than assuming that its name implies the same leading pad.

$ xxd -l 32 ~/imx6ull/build/uboot-pico-study/SPL
$ ~/imx6ull/build/uboot-pico-study/tools/mkimage -l \
    ~/imx6ull/build/uboot-pico-study/SPL

For this packaged SPL, examine the IVT at file byte zero. Its entry and self pointers refer to internal RAM rather than the DDR entry of the EVK image. Use the image tool and selected configuration to interpret the remaining fields. Do not substitute the entry from a full-U-Boot .imx dump.

The tool may print Mode: DCD for both files. In this implementation that label distinguishes the non-plugin header mode, not proof that a DDR setup table is present. The PICO wrapper selected by arch/arm/mach-imx/spl_sd.cfg does not supply the EVK’s DDR writes. Check the actual DCD pointer and table before deciding who initialized memory.

An OCRAM-loaded SPL does not need the ROM to initialize DDR just to load the SPL itself. Its own board code can do that afterward. A DDR-loaded full image does need DDR before it can execute, which is why the EVK supplies a DCD.

The meaningful comparison is the dependency, not an identical filename or an identical amount of padding:

Which memory must already work before the next instruction can be fetched?

20.9 The SPL-to-U-Boot handshake¶

spl_image_info is the SPL loader’s record. It tells the handoff where an image was placed and where to enter it. It is not automatically an argument that full U-Boot reads from the SPL’s old stack.

The payload also depends on established state:

  • Its code and required data must actually be present in usable memory.

  • Its load and entry addresses must agree with the selected format and build.

  • Memory writes must be visible to the instruction-fetch path when needed.

  • CPU mode, caches, MMU, and device state must satisfy the selected entry path.

The exact cache-maintenance and jump behavior belongs to the selected loader, architecture, and configuration. Do not infer that every SPL automatically performs cleanup_before_linux() before every possible handoff. Read the selected jump_to_image_no_args implementation and any board override.

Some configurations provide additional handoff records through facilities such as a blob list. Those are separate interfaces. For our reading example, trace the normal full-U-Boot entry into crt0.S; it establishes its own runtime instead of treating the preceding program’s stack as its permanent stack.

20.10 Key Notes¶

  • DCD and SPL solve the same early-memory dependency with different owners: ROM interprets a table, while SPL executes board-specific initialization code.

  • The PICO SPL and EVK full-U-Boot sources are comparison cases, not MINI firmware. Their payload formats and raw-MMC offsets do not replace Chapter 19’s layout.

  • A loadable-image size limit excludes other runtime consumers. OCRAM code, stack and early allocation, plus any later DDR-resident BSS, have separate lifetimes and budgets.

  • The PICO path clears its DDR-resident BSS only after DDR initialization. A compiled C variable is not usable before its backing memory exists.

  • board_init_f and board_init_r names do not impose universal return or relocation behavior. The selected board and stage determine the actual handoff.

  • spl_image_info records placement and entry information. The next stage also depends on CPU, cache and memory state, not merely a function-pointer jump.

20.11 Pitfalls¶

  • Looking for SPL in every build. Check CONFIG_SPL first. Our EVK does not use it, and a missing file there is expected.

  • Treating 64 KiB as the whole runtime budget. The loadable image limit, BSS location, early stack, header, and allocation pool are different facts.

  • Accessing DDR-resident BSS before DDR setup. Successful compilation says nothing about whether that address is usable at that moment.

  • Copying another board’s DDR structures. They describe its memory and calibration, not a generic i.MX6UL/ULL memory device.

  • Using full-U-Boot DT assumptions for SPL. Stage filtering and available drivers determine what an SPL can bind and probe. Inspect the stage’s output.

  • Guessing handoff cleanup. Follow the actual path and its ownership rules. A function-pointer call does not by itself synchronize cached instructions.

20.12 Going deeper¶

  • SPL development documentation.

  • Pinned PICO configuration.

  • PICO board SPL.

  • SPL Kconfig limits and loader settings.

  • The common ARM runtime entry.

Next we return to full U-Boot. Whether the ROM or an SPL made DDR usable, the large program still has to arrange its own stack, relocation, devices, and shell.

Next
Chapter 21: U-Boot internals
Previous
Chapter 19: U-Boot from source, first boot
Copyright © 2026, DangViTin
Made with Furo
On this page
  • Chapter 20: U-Boot SPL: the missing link
    • 20.1 Why two stages
    • 20.2 What SPL is responsible for
    • 20.3 The size budget
    • 20.4 Where SPL lives in the source
    • 20.5 Reading start.S
    • 20.6 board_init_f, the “before relocation” stage
    • 20.7 board_init_r, the “after relocation” stage
    • 20.8 Comparing SPL to your Ch 11 image-builder
    • 20.9 The SPL-to-U-Boot handshake
    • 20.10 Key Notes
    • 20.11 Pitfalls
    • 20.12 Going deeper