From Hardware to Linux

This blog post is a collection of methods and ideas for doing hardware bringups. It is based on the presentation given at the Open Source Summit 2024.
Introduction
Bringing a new piece of custom hardware to life with an embedded Linux system is a challenging yet rewarding process known as “hardware bring-up.” “At its core, hardware bring-up is the process of bringing new hardware to life. When a hardware team finalizes a design and the first prototypes arrive, these are typically lifeless, and it’s the bring-up process that breathes life into them. This article aims to provide a practical guide for embedded software engineers, particularly those newer to the intricacies of embedded Linux bring-up, covering common challenges, debugging strategies, and best practices.
A crucial aspect of a smoother bring-up process is the early engagement of the software team in the hardware design phase. Ideally, bring-up preparations begin well before the physical hardware arrives, during component evaluation and schematic design. Early involvement is key because it allows for the selection of components with good Linux driver support and helps ensure the hardware design aligns well with software requirements, such as accessible debug interfaces. This proactive collaboration can identify and resolve potential software or driver issues when changes are less costly. The earlier the software team contributes, the more robust the foundation for the bring-up will be. We will now walk through the typical phases, from initial preparations to getting Linux up and running.
This article is using the Verdin AM62 from Toradex equipped with a AM62x SoC from TI as an example platform. However, the ideas and concepts should also work for other boards and SoC. This are just examples and experiences gained from previous bring-ups but do not represent an actual bring-up performed on the Verdin AM62.
Understanding the Bootflow
The boot process of the AM62x is a sequential loading and execution of several software components, starting from the on-chip BootROM and culminating in the Linux kernel. Here’s a step-by-step breakdown of this flow:
- BootROM Loads System Controller: The process initiates with the AM62x’s internal BootROM. Its first task is to load and start the system controller firmware, which manages low-level chip functions.
- BootROM Starts R5 SPL: Once the system controller is active, the BootROM proceeds to load and start a Secondary Program Loader (SPL) onto one of the R5F cores. This R5 SPL is a small, initial bootloader based on U-Boot.
- R5 SPL Loads Critical Components: The R5 SPL is responsible for loading the next set of essential software:
- Arm Trusted Firmware (ATF)
- OP-TEE (Open Portable Trusted Execution Environment)
- A53 SPL (a separate SPL specifically for the A53 cores)
- Jump to ATF: After these components are loaded into memory, the R5 SPL will start the A53 core and sets the program counter (jumps) to th Arm Trusted Firmware (ATF).
- ATF Starts OP-TEE: The ATF, now running, initializes and starts the OP-TEE to set up the trusted execution environment.
- OP-TEE Returns to ATF: Once OP-TEE has completed its initialization, it yields control back to the ATF.
- ATF Starts A53 SPL: With the trusted environment prepared, the ATF then starts the A53 SPL (which was loaded earlier by the R5 SPL).
- A53 SPL Loads and Starts U-Boot: The A53 SPL’s primary function is to load the main U-Boot bootloader from storage into RAM and then transfer execution to it.
- U-Boot Loads and Starts Linux Kernel: Finally, the fully-featured U-Boot takes control. It performs further hardware initialization, loads the Linux kernel image and its associated device tree, and then boots the Linux kernel, bringing the system to its operational state.
Hardware Setup
To effectively work with and debug the AM62x platform, the following hardware setup was chosen. The typical connections and their purposes are as follows:
- DFU USB: This connection is primarily used for the initial flashing of bootloader components. The R5 SPL, A53 SPL, and the main U-Boot image can be loaded onto the device using the Device Firmware Update (DFU) protocol over USB.
- USB Serial: Essential for visibility into the boot process, the USB Serial connection provides console access. This allows us to view boot messages from the SPL, U-Boot, and the Linux kernel, as well as interact with the bootloader command line.
- JTAG: For low-level debugging, especially during the very early stages of software execution (like the SPL or U-Boot), a JTAG interface via a JTAG debugger probe is used. It allows direct hardware-level access to the CPU cores and memory.
- Ethernet: While U-Boot can load the kernel from various sources, an Ethernet connection is commonly used (often with TFTP - Trivial File Transfer Protocol) to download the Linux kernel image and the root filesystem onto the target during development.
Besides these direct connections to the AM62x board, the following items should be readily available:
- A suitable Power Supply for the AM62x based custom hardware.
- The Reference Manuals for the AM62x SoC (System on Chip) and other key components integrated onto our specific board.
- Board Schematics to understand the electrical connections and component placements.
- Hardware Layout files (e.g., PCB layout) for identifying physical test points and component locations.
- A Multimeter for basic voltage and continuity checks.
- An Oscilloscope for observing signal integrity, clock signals, and timing characteristics.
- Optionally, a Logic Analyzer can be very helpful for debugging digital interfaces and protocols.
Early Stage Debugging
Early Stage Debugging is necessary when no output is visible on the serial console, allowing investigation before serial communication is active. While also useful for understanding later system behavior, this process fundamentally requires a JTAG debugger for direct hardware access. The following few section will shows an example when the R5 core is failing to start the SPL on the AM62x.
Loading R5 SPL
The initial step in booting the AM62x often involves loading the system firmware, tiboot3.bin
, which contains the R5 Secondary Program Loader (SPL) and other critical firmware components. This is typically accomplished using the USB DFU (Device Firmware Update) protocol.
To load tiboot3.bin
onto the device, execute the following command in our terminal:
dfu-util -D path/to//u-boot/r5/tiboot3.bin -a 0
Upon successful execution, we should see output similar to the following, indicating that the transfer completed without errors:
A successful load, as indicated by the output above, is a positive first sign. It confirms that basic communication with the SoC is established and that the USB interface used for DFU is functioning correctly.
However, if the dfu-util command fails or tiboot3.bin
cannot be loaded, it points to more fundamental hardware or connection issues. In such a scenario, we would need to systematically check the following:
- Power Supplies: Verify that all critical power rails for the SoC are present and at their correct voltage levels (e.g., VDD_CORE, VDD_RAM, etc.).
- Core Clocks: Ensure that the necessary clock signals for the CPU cores and peripherals involved in early boot are active and stable.
- USB Connection: Double-check the physical USB connection between our host machine and the AM62x device, including the cable and connectors, and ensure the device is in the correct DFU mode.
Debugging R5 SPL
If no output appears on the serial console after loading tiboot3.bin (containing the R5 SPL), first verify the serial connection, settings, and ensure the correct file was loaded. If these are confirmed and the console remains silent, the SPL is likely crashing very early, necessitating JTAG debugging.
To allow the JTAG debugger to connect before a potential crash, the R5 core must be halted at an early, predictable point. This is commonly achieved by inserting an infinite debug loop or a break instruction into the SPL’s initial startup code. For instance, we can modify arch/arm/cpu/armv7/start.S as follows:
diff --git a/arch/arm/cpu/armv7/start.S b/arch/arm/cpu/armv7/start.S
index 7730a16e512..ed3d72d6628 100644
--- a/arch/arm/cpu/armv7/start.S
+++ b/arch/arm/cpu/armv7/start.S
@@ -37,6 +37,8 @@
#endif
reset:
+debug:
+ b debug
/* Allow the board to save important registers */
b save_boot_params
save_boot_params_ret:
After recompiling the SPL with this change and reloading the updated tiboot3.bin via USB DFU, the R5 core will be caught in this debug loop. Connecting a JTAG debugger will now show the core halted at this loop, providing a stable state to begin our debugging session, inspect registers, and step through the code.
The following screenshot illustrates a JTAG debugger connected to the R5 core, halted at such a debug point:
Once halted at the debug loop or any other breakpoint we set, we can use the JTAG debugger to step through the code line by line, inspect memory and registers, or let the program continue execution until it potentially crashes. If a crash occurs, the debugger can often provide valuable information, such as a backtrace, to help pinpoint the cause. To continue execution, we type jump +1 to jump to the next line and skip the loop.
An example of what a crash or backtrace of a panic in the SPL might look like through the JTAG debugger is shown below:
Looking at the backtrace from such a crash, as shown in the example, reveals that the SPL crashed within the function serial_find_console_or_panic. The associated error message, “No serial driver found,” points us directly towards the root cause of the issue.
Further investigation of the serial driver reveals that the crash occurs in the serial_omap.c
file when the system attempts to get the UART clock.
This discovery narrows the problem down significantly, indicating that the next step is to investigate the clock configuration for the UART peripheral, likely within the device tree or board-specific clock initialization code within the SPL.We eventually determined that the failure to enable the base clock for the UART was causing the SPL to crash. The following patch, which ensures the necessary clock controller is not inadvertently disabled in the R5 device tree context, will resolve the issue:
diff --git a/arch/arm/dts/k3-am625-verdin-r5.dts b/arch/arm/dts/k3-am625-verdin-r5.dts
index e7350da4ff8..2b333e70f5c 100644
--- a/arch/arm/dts/k3-am625-verdin-r5.dts
+++ b/arch/arm/dts/k3-am625-verdin-r5.dts
@@ -73,7 +73,3 @@
/* We require this for boot handshake */
status = "okay";
};
-
-&k3_clks {
- status = "disabled";
-};
With this change applied, the SPL recompiled, and the updated tiboot3.bin reloaded, the serial console should now output the expected boot messages, confirming that the UART is functioning correctly and the initial issue has been resolved. We now see the following output:
Unfortunately, now U-Boot is crashing and we need to figure out what is going on there.
Debugging U-Boot
After successfully getting the initial SPL (Second Program Loader) running, the system proceeds to load the main U-Boot bootloader, running on the primary application cores. As seen in the previous video, U-Boot now crashes with an exception.
It’s important to identify where this exception message originates. The message “unhandled exception in EL3” is not printed by U-Boot itself but by the ARM Trusted Firmware (ATF). This indicates that U-Boot didn’t just encounter an error it could handle.
While printk
or puts
style debugging could theoretically be used, JTAG with GDB is far more powerful for these hard crashes.
Just as with the SPL, we can add a debug loop, but this time specifically for U-Boot, not the SPL (which is working correctly).
diff --git a/arch/arm/cpu/armv8/start.S b/arch/arm/cpu/armv8/start.S
index 74612802617..9374cdb1078 100644
--- a/arch/arm/cpu/armv8/start.S
+++ b/arch/arm/cpu/armv8/start.S
@@ -53,6 +53,10 @@ _bss_end_ofs:
.quad __bss_end - _start
reset:
+#if !defined(CONFIG_SPL_BUILD)
+debug:
+ b debug
+#endif
/* Allow the board to save important registers */
b save_boot_params
.globl save_boot_params_ret
A key characteristic of U-Boot is that it often relocates itself. It copies its own code from its initial location in memory where the SPL is loaded to a more suitable location, typically towards the end of RAM. This is done to consolidate memory or to get out of the way of where the Linux kernel might be loaded. While not always enabled, it’s a common default behavior.
If we set breakpoints based on the initial symbol table of U-Boot (before relocation), those breakpoints will become invalid once U-Boot moves its code. This means breakpoints are possibly never hit.
To overcome this issue we need to:
- Load Initial Symbols: Load the U-Boot ELF file with symbols as usual in GDB:
symbol-file u-boot
- Breakpoint at
relocate_code
: The function responsible for relocation is typically calledrelocate_code
. Set a breakpoint there:b relocate_code
. - Run to Relocation: Continue execution until this breakpoint is hit.
- Determine Relocation Address: Once the relocate_code breakpoint is hit, U-Boot has calculated its new address but has not yet fully relocated. The relocation address can be obtained via gd->relocaddr. This address is then used for the relocation.
- Load Symbols to New Address: Once we have the relocation address (let’s call it
NEW_ADDRESS
), we need to tell GDB to load the U-Boot symbols again, but this time at the new offset. Use theadd-symbol-file
command in GDB:add-symbol-file u-boot NEW_ADDRESS
This will look as follows in GDB:
We can then debug the U-Boot code as usual, setting breakpoints and stepping through the code. With further debugging, we might identify the problematic part:
If we attempt to continue from there, U-Boot will crash on the next line where the printf occurs:
The code attempts to access an address related to a USB controller. However, at this stage, the USB driver has not been probed or initialized, therefore, it crashes. This often happens if the power domain or the clock is not enabled.
The following patch will fix this specific issue by enabling the power domain manually before accessing the address of the USB controller:
diff --git a/board/toradex/verdin-am62/verdin-am62.c b/board/toradex/verdin-am62/verdin-am62.c
index a17fd28b5b3..6efee7ea4ba 100644
--- a/board/toradex/verdin-am62/verdin-am62.c
+++ b/board/toradex/verdin-am62/verdin-am62.c
@@ -15,6 +15,8 @@
#include <init.h>
#include <k3-ddrss.h>
#include <spl.h>
+#include <dm/device.h>
+#include <power-domain.h>
#include "../common/tdx-cfg-block.h"
@@ -23,8 +25,35 @@ DECLARE_GLOBAL_DATA_PTR;
int board_init(void)
{
u32 val;
+ int ret;
+ ofnode usbss_node;
+ struct udevice *usbss_dev;
+ struct power_domain *pd;
#define CRASH_ADDRESS 0x31100000
+ puts("Get USB device\n");
+ usbss_node = ofnode_path("/bus@f0000/dwc3-usb@f910000");
+ if (!ofnode_valid(usbss_node)) {
+ printf("Failed to get usb node\n");
+ return 0;
+ }
+ ret = device_find_global_by_ofnode(usbss_node, &usbss_dev);
+ if (ret) {
+ printf("Failed to get usb device: %d\n", ret);
+ return 0;
+ }
+ puts("Get power domain controller and turn it on\n");
+ ret = power_domain_get_by_index(usbss_dev, pd, 0);
+ if (ret) {
+ printf("Failed to get usb power domain: %d\n", ret);
+ return 0;
+ }
+ ret = power_domain_on(pd);
+ if (ret) {
+ printf("Failed to power on usb power domain: %d\n", ret);
+ return 0;
+ }
+
val = readl(CRASH_ADDRESS);
printf("Value at 0x%08x: 0x%08x\n", CRASH_ADDRESS, val);
After fixing this issue we can finally start U-Boot and see the expected output:
Once we have U-Boot up and running to the point where its command-line console is active, a wealth of built-in commands become available. If the console is up, it’s often more practical to use these commands rather than resorting to JTAG for many tasks. We can explore available commands by typing help
in the U-Boot console. Many of these can be enabled or disabled via U-Boot’s configuration system (Kconfig). Some useful commands are listed in the last section about Useful U-Boot Commands.
Debugging Linux
We will now load Linux and a ramdisk image using the following command. The ramdisk image is a small rootfs, zipped as cpio archive and with a U-Boot header attached. It was built with Yocto and contains a minimal set of tools to get started and to install a full rootfs to the eMMC later on.
setenv fdt_addr_r 0x80000000; \
setenv bootargs \
'root=/dev/ram waitroot console=ttyS2,115200n8'; \
setenv ipaddr 192.168.1.1; setenv serverip 192.168.1.254; \
tftp $kernel_addr_r verdin-am62/Image.gz; \
tftp $fdt_addr_r verdin-am62/k3-am625-verdin-wifi-dev.dtb; \
tftp $ramdisk_addr_r verdin-am62/ramdisk.u-boot; \
booti $kernel_addr_r $ramdisk_addr_r $fdt_addr_r
This will give us the following output:
We can see from the output that U-Boot tried to start the Linux kernel but the Linux kernel never shows any output. Before starting to setup the JTAG debugger or other debugging tools, we can try to get some output from the kernel by using an early console. Normally, the Linux kernel only starts outputting to the console after the serial driver is loaded. By using earlycon, a ’lightweight’ driver is employed, which allows us to get some output even if the main serial driver is not yet loaded.
We change the command to the following:
setenv fdt_addr_r 0x80000000; \
setenv bootargs \
'root=/dev/ram waitroot console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02800000'; \
setenv ipaddr 192.168.1.1; setenv serverip 192.168.1.254; \
tftp $kernel_addr_r verdin-am62/Image.gz; \
tftp $fdt_addr_r verdin-am62/k3-am625-verdin-wifi-dev.dtb; \
tftp $ramdisk_addr_r verdin-am62/ramdisk.u-boot; \
booti $kernel_addr_r $ramdisk_addr_r $fdt_addr_r
Now we see some output:
From this output we will eventually figure out that by loading the device tree to 0x80000000 we are overwriting the memory used by the PSCI. It is a common issue in U-Boot that by loading files to the wrong addresses we overwrite memory by accident. The following change will finally allow us to boot into Linux:
setenv bootargs \
'root=/dev/ram waitroot console=ttyS2,115200n8 earlycon=ns16550a,mmio32,0x02800000'; \
setenv ipaddr 192.168.1.1; setenv serverip 192.168.1.254; \
tftp $kernel_addr_r verdin-am62/Image.gz; \
tftp $fdt_addr_r verdin-am62/k3-am625-verdin-wifi-dev.dtb; \
tftp $ramdisk_addr_r verdin-am62/ramdisk.u-boot; \
booti $kernel_addr_r $ramdisk_addr_r $fdt_addr_r
This will give us the following output:
For further debugging we might use printk, kgdb, JRAG, ftrace, devmem, virtual filesystems and other tools. Some of these tools and filesystems are documented in the following sections.
Useful U-Boot Commands
U-Boot’s command line provides essential tools for hardware interaction, diagnostics, and booting. Below is a concise list of commonly used commands.
General
help [command]
: Lists all commands or shows detailed help for a specific[command]
.
Memory Access
md [.b, .w, .l, .q] address [#objects]
: Memory Display. Shows memory contents. Suffixes.b, .w, .l, .q
set byte, word, long, quad-word display.mw [.b, .w, .l, .q] address value [count]
: Memory Write. Modifies memory. Suffixes control data size.
Ethernet PHY
mii info|dump|read|write <args>
: Inspects and manages Ethernet PHY (Physical Layer) devices. For example,mii info <phyaddr>
shows PHY details.
MMC/SD Card
mmc info
: Displays information about the current MMC device.mmc dev [devnum [part]]
: Shows or sets the current MMC device and partition.mmc part
: Lists partitions on the current MMC device.mmc read|write <addr> <blk_start> <blk_count>
: Reads data from MMC to memory address<addr>
or writes data from memory to MMC.
Board & Device Tree
bdinfo
: Displays Board Information like memory layout and key addresses.fdt addr|print|list <args>
: Manages the Flattened Device Tree (DTB). Usefdt addr <dt_addr>
to set its location, thenfdt print /path/to/node
to inspect.
Driver Model
dm tree
: Shows the device tree with bound drivers.dm list
: Lists available drivers or devices.
File Loading
tftpboot [loadAddress] [filename]
: Loads a file from a TFTP server.load <interface> <dev[:part]> [addr] [filename]
: Loads a file from storage like MMC or USB (e.g.,load mmc 0:1 0x... file
).ls <interface> <dev[:part]> [directory]
: Lists files on a storage device (e.g.,ls mmc 0:1 /
).
Booting an Operating System
bootm [addr]
: Boots a legacy U-Boot uImage (kernel).bootz [kernel_addr] [initrd_addr] [fdt_addr]
: Boots a zImage format Linux kernel, commonly used for ARM.booti [kernel_addr] [initrd_addr] [fdt_addr]
: Boots an ARM64 Linux kernel (e.g.,Image
file).
Useful Linux Virtual Filesystems and Directories
Linux utilizes several pseudo/virtual filesystems to expose kernel information, provide interfaces to hardware, and allow for system control and debugging directly from the user space. These are not regular on-disk filesystems but rather dynamic views into the kernel’s world.
sysfs
- Kernel Subsystem & Device Information
-
Mount Point:
/sys/
-
Purpose: Provides a structured view of kernel subsystems, hardware devices, and their attributes. It allows both reading status and, in some cases, writing values to control devices or trigger actions.
-
Triggering Actions: Many device operations can be triggered by writing to specific files.
echo 1 > /sys/bus/pci/rescan
- This command, for example, instructs the kernel to rescan the PCI bus for new or removed devices.
-
Navigating the Device Tree: The live device tree, as seen by the kernel, is often exposed here.
/sys/firmware/devicetree/base/
- We can
cd
into this directory and usels
to navigate the device tree hierarchy, inspect node properties (which appear as files), and see the system’s hardware configuration.
- We can
-
Accessing Raw Device Tree Blob (DTB):
/sys/firmware/devicetree/fdt
- This file represents the raw Flattened Device Tree blob that the kernel is using. We can copy this file (e.g.,
cp /sys/firmware/devicetree/fdt my_board.dtb
) and then use tools likedtc
(Device Tree Compiler) to decompile it into a human-readable source format (dtc -I dtb -O dts my_board.dtb > my_board.dts
).
- This file represents the raw Flattened Device Tree blob that the kernel is using. We can copy this file (e.g.,
debugfs
- Kernel Debug Information
-
Mount Point: Typically
/sys/kernel/debug/
(requires appropriate kernel configuration and mounting). -
Purpose:
debugfs
is specifically designed for kernel developers to export debugging information. Its interfaces are not guaranteed to be stable across kernel versions, so it should not be relied upon by production applications. -
Viewing Clock Tree Summary:
cat /sys/kernel/debug/clk/clk_summary
- This often provides a detailed summary of all clocks in the system, their enable cout, rates, and parent/child relationships, which is invaluable for debugging clock-related issues.
-
Inspecting Pin Control (Pinmux) Status:
cat /sys/kernel/debug/pinctrl/<pinctrl-device-name>/pins
- For example,
cat /sys/kernel/debug/pinctrl/44e10800.pinmux/pins
(where44e10800.pinmux
is specific to a device) would show the current configuration and status of pins managed by that particular pin control device, including their muxing and electrical properties.
- For example,
procfs
- Process and System Information
-
Mount Point:
/proc/
-
Purpose:
procfs
provides information about running processes (each PID has a directory like/proc/<PID>/
) and other system-wide kernel information and statistics. -
Viewing I/O Memory Map:
cat /proc/iomem
- This file lists the I/O memory regions registered with the kernel, showing the physical address ranges and which device or subsystem has claimed each region. This is very useful for understanding hardware resource allocation.
These virtual filesystems are powerful tools for introspection and debugging on a running Linux system, providing direct insight into how the kernel perceives and manages hardware and software resources.
Useful Linux Commands
Beyond the virtual filesystems, a range of powerful user-space commands are available in Linux for direct hardware interaction, diagnostics, and performance testing, especially useful in embedded system development and debugging.
Low-Level Hardware Access
devmem2 <address> [type] [data]
Allows direct read and write access to physical memory addresses (and thus memory-mapped registers).[type]
can beb
(byte),h
(half-word/16-bit),w
(word/32-bit).- If
[data]
is provided, it performs a write; otherwise, it reads. - Caution: Requires root privileges and can easily crash or damage a system if used incorrectly.
- Example:
devmem2 0x44e10800 w
(reads a 32-bit word from the address).devmem2 0x44e10800 w 0x12345678
(write a 32-bit word from the address).
Network and PHY Access
-
phytool <read/write> <interface>/<addr>[:page]/<reg> [value]
A utility to read from and write to Ethernet PHY registers.<interface>
is the network interface (e.g.,eth0
).<addr>
is the PHY address on the MDIO bus.<reg>
is the register number.- Example:
phytool read eth0/0/0
(reads register 0 of PHY 0 on eth0).
-
ethtool <interface>
Queries or controls network driver and hardware settings for a given Ethernet<interface>
(e.g.,eth0
).- Used to display link status, speed, duplex, auto-negotiation settings, driver information, and statistics. Can also be used to change settings like speed and duplex.
- Example:
ethtool eth0
(shows current settings for eth0).
-
ping <ip_address_or_hostname>
Tests network connectivity to a specified host by sending ICMP ECHO_REQUEST packets and waiting for replies. Essential for basic network troubleshooting.- Example:
ping 192.168.1.1
- Example:
Display and Graphics
modetest
A utility (part oflibdrm
) to test Direct Rendering Manager (DRM) / Kernel Mode Setting (KMS) functionality.- It can enumerate available display connectors, modes, planes, and run test patterns on displays without needing a full X server or Wayland compositor. Useful for display output debugging.
Benchmarking and Stress Testing
-
iperf3 <-c client_ip | -s> [options]
A tool for measuring network bandwidth performance.- Run
iperf3 -s
on one machine (server) andiperf3 -c <server_ip>
on another (client) to test TCP/UDP throughput between them.
- Run
-
coremark
(or similar CPU benchmark executable likecoremark.sh
) Runs the EEMBC CoreMark® benchmark, a standardized test to measure the performance of CPU cores. The output is a single number score. -
stress-ng [options]
A versatile stress testing utility that can impose various types of load on CPU, memory, I/O, and other system components to test stability and thermal performance.- Example:
stress-ng --cpu 4 --timeout 60s
(stresses 4 CPU cores for 60 seconds).
- Example:
-
memtester <memory_size> [iterations]
Tests the stability and reliability of RAM by writing various patterns to a specified portion of memory and verifying them.<memory_size>
is the amount of memory to test (e.g.,128M
).[iterations]
is how many times to repeat the tests.- Example:
memtester 256M 1
(tests 256MB of RAM for one iteration).
These commands provide powerful capabilities for low-level interaction and system testing directly from the Linux command line.
Summary
In this article, we have explored the process of hardware bring-up, focusing on the AM62x platform. We discussed the boot process, hardware setup, and debugging techniques for the SPL, U-Boot and Linux stage. We also covered useful commands and tools for both U-Boot and Linux environments. This guide serves as a practical reference for embedded software engineers, especially those new to embedded Linux bring-up. By following these steps and utilizing the provided tools, we can effectively troubleshoot and resolve issues during the hardware bring-up process.