4.1. Porting U-Boot

4.1.1. Overview

When porting U-Boot on a custom board, it is recommended to start with a TI EVM that is similar to the custom hardware design. For example, the boards may have similar DDR (type and size of memory), MMC (which module is used, is the MMC an eMMC or SD card), network setup (type and number of PHYs connected), and UART setup (which UART is used as the console?).

For custom AM64x boards, many base their design off the AM64x General-Purpose EVM, or the AM64x Starter Kit.

For more information on the Linux boot flow (including how the R5 core is used during a Linux boot), reference section Linux Boot Flow.

4.1.2. Porting Custom Board Steps

4.1.2.1. Starting with U-Boot on TI EVM

U-Boot uses the driver model. Driver binding is accomplished with Device Tree Source (DTS) Files. One DTS file is unique to the R5, and one DTS file is unique to the A53. The DTS File describes the hardware available on the board, and allows U-Boot and the Linux kernel to bind drivers to a board’s hardware.

Build defconfigs:
  • am64x_evm_r5_defconfig: includes the configuration flags for building R5 SPL

  • am64x_evm_a53_defconfig: includes the configuration flags for building A53 SPL and U-Boot

DTS files:
  • k3-am642-r5-evm.dts: main R5 DTS file which includes the nodes for R5 boot, Pinmux, DDR, etc.

  • k3-am642-evm.dts: main A53 DTS file which includes the nodes for A53 boot, Pinmux, etc.

Summary:
  • Use the U-Boot source in the Processor SDK from TI.

  • Start with the TI examples of the R5 and A53 DTS files.

  • The Board DTS defines what SOC components are necessary to run the custom board.

  • The Pinmux and DDR Config tool is used to fill out DTS nodes.

4.1.2.2. Porting Custom Board with Minimum Configuration

First, create a minimal configuration that can be used for the initial board bringup attempt. Once the basics are working, the minimal configuration is a good base to incrementally build on. Add features one by one, until the configuration fully supports all the desired features of the custom board.

Note that new features will probably not work correctly in the first attempt. Typically an iterative approach is taken: start simple. Make small changes, build the code, and commit the changes into a Git source tree (so changes can be traced, understood, and reverted if needed). Finally, test the changes on hardware. Adjust the code based on the hardware tests, and keep iterating until the code works as expected. Then move on to the next feature. Continue this process until a fully working and functional board port is achieved.

  • Port DDR configuration if your DDR setup (devices, clock speeds, etc.) differs from the original platform (EVM):

    • Follow the steps outlined in the DDR Board Design and Layout Guidelines. This application report also includes useful DDR bringup information.

    • To facilitate software configuration of the DDRSS, use the DDR Subsystem Configuration Tool in SysConfig to generate DDR configuration device tree files. For example,

      • u-boot/arch/arm/dts/k3-am64-evm-ddr4-1600MTs.dtsi: AM64x GP EVM DDR4 configuration

      • u-boot/arch/arm/dts/k3-am64-sk-lp4-1333MTs.dtsi: AM64x SK LPDDR4 configuration

    • When the DDR timings and parameters are setup correctly, U-Boot will automatically detect, verify, and configure the size of DDR during runtime in the architectural files by using get_ram_size().

  • Establish an initial minimal pinmux setup for the custom board:

    • A minimal pinmux setup is needed to avoid signal conflicts that may occur when running a TI EVM pinmux configuration on a custom board that has different pinmux requirements.

    • Pinmux performed in U-Boot is usually limited to:

      • the peripherals that are directly involved in the boot process (such as GPMC, DDR, MMC, SPI, etc.)

      • an I2C module used for PMIC connectivity

      • the console UART

    • The TI-provided Pin MUX Utility in SysConfig is the recommended way to generate pinmux settings for a custom board.

  • Update PMIC configuration:

    • TI AM64x SoCs are typically supplied by an external Power Management IC (PMIC) connected via the I2C interface. The PMIC supplies power to the SoC at voltages that meet datasheet requirements.

    • The actual PMIC configuration is dependent on the clock frequency configured for a given board. The PMIC configuration may be different for different silicon revisions. Review the original TI EVM’s code carefully, particularly the board_is_*() invocations.

  • Customize console UART settings:

    • Configure the desired console index using the Kconfig tool.

  • Deactivate all peripheral initializations from the <device>-<myboard>.dts device tree file, except for basic boot support (UART, MMC, etc.). Use one of the following methods:

    1. Remove all device tree nodes that are not applicable. When a device tree node is removed, also remove its references (such as clocks, power regulator, and pinmux settings).

    2. De-activate peripherals that are not needed by adding a status = "disabled"; property to their respective nodes.

With the customizations now made, try an initial boot of the custom hardware platform. The goal is to get to the U-Boot prompt.

4.1.2.3. Building out Full Support for Target Platform

Once the U-Boot prompt has been reached, the porting focus switches to (re-)adding U-Boot features that are needed to support the custom system. It is recommended to add features one by one, while using Git to track any changes. Test and validate features on actual hardware as they are added, until all desired features have been added, tested, and integrated. Once the U-Boot features have been ported, developers can progress to porting the Linux Kernel.

Customization steps can include, but are not limited to, adding…

  • Support for additional storage media

  • Support for additional boot modes

  • Support for network interface(s)

  • Support for extra U-Boot commands (CONFIG_CMD_*) to help debug or run the system

When adding features, it is usually a good idea to analyze other boards already present in U-Boot that use the same TI SoC. Then, port features over into the new custom board files, board specific header file, and defconfig.

It can also be helpful to inspect the most current upstream U-Boot tree for additional boards that may have become available since the last TI Processor SDK release. However, care must be taken when back porting code to an earlier version of U-Boot. Consider all the required dependencies and changes that may have affected a specific feature.

4.1.3. U-Boot Bringup Debugging Tips

Doing a U-Boot board port is usually an iterative process. It involves debugging and troubleshooting, especially on custom hardware platforms that differ substantially from the TI EVMs. The following list gives some ideas that could be helpful while debugging the U-Boot bringup.

  • The most efficient and powerful tool for board bringup is to have access to the SoC via JTAG debugger, and use a tool such as TI’s Code Composer Studio (CCS) or Lauterbach (T32) to inspect the device and code.

  • Performing basic printf()-style debugging:

    • Use printf()-style debugging when JTAG is not available or not practical.

    • Maximizing the usefulness of this approach usually requires the early (debug) UART to be configured and activated (which will happen as part of SPL’s board_init_f()). Most of the critical low-level code on current TI EVMs is executed while the regular console UART is not yet available. If the early UART is not activated, nothing would get printed during failures relating to boot, PMIC setup, clock setup, DDR setup, and other critical stages. This leads to “black screen” failures which provide no feedback as to what the problem may be and how to debug it.

    • Many U-Boot modules (source files) already contain various forms of debug() print statements which can be activated on a per-module basis by adding a #define DEBUG to the top of the source file.

    • Beyond that, it can be helpful to add print statements throughout the boot flow to trace program execution. For example, the printf statement shown below can be inserted into code to help debug: printf("%s: %d:\n", __func__, __LINE__);.