System Practice Learning ARMv8 Assembly – Course 1

Course 1: Stage 1 – Basic Preparation (Week 1)

Topic: Bare-metal program development, PL011 UART communication, ARMv8 boot process

1.1 Basics of Bare-metal Programming

Core Concepts:

  1. Bare-metal Program: Runs directly on hardware without operating system support.

  2. Boot Process:

  • The CPU starts executing instructions from the reset address (usually <span>0x0</span> or <span>0x8000</span>).

  • Hardware (such as memory and peripherals) needs to be manually initialized.

  • PL011 UART:

    • ARM’s serial controller for input and output.

    • Base address of registers:<span>0x9000000</span> (default address for QEMU <span>virt</span> machine).

    1.2 Development Environment Setup

    Toolchain Installation:

    # Install cross-compiler and QEMU (Linux example)
    sudo apt install gcc-aarch64-linux-gnu qemu-system-arm

    Verify Toolchain:

    aarch64-linux-gnu-gcc --version
    qemu-system-aarch64 --version

    1.3 Writing a Bare-metal Program

    Code Example (<span>uart_hello.s</span>):

    // Define UART register addresses
    .equ UART0_BASE, 0x9000000   // Base address of PL011 UART
    .equ UARTFR, 0x18            // Offset for flag register
    .equ UARTFR_TXFF, (1 << 5)   // Transmit FIFO full flag
    .equ UARTDR, 0x0             // Offset for data register
    .section .text
    .global _start
    _start:
        // Initialize stack pointer (optional, not used in this simple example)
        ldr x0, =stack_top
        mov sp, x0
        // Print string
        ldr x1, =message         // Load string address
    loop:
        ldrb w2, [x1], #1        // Read one byte into w2, increment address x1 by 1
        cbz w2, halt             // If read 0 (end of string), jump to halt
        bl uart_putc             // Call UART send function
        b loop                   // Continue loop
    // UART send single character function
    uart_putc:
        ldr x3, =UART0_BASE      // Load UART base address into x3
        tx_wait:
        ldr w4, [x3, UARTFR]     // Read UARTFR register
        tst w4, UARTFR_TXFF      // Check if transmit FIFO is full
        b.ne tx_wait             // If full, wait
        str w2, [x3, UARTDR]     // Write character to UARTDR register
        ret                      // Return
    halt:
        b halt                   // Infinite loop (halt)
    .section .data
    message:
        .asciz "Hello, ARMv8 Bare-metal!\n" // Define stack space (1KB)
    .align 12
    stack_bottom:
        .space 1024
    stack_top:

    1.4 Compile and Run

    Steps:

    1. Compile Assembly File:

      aarch64-linux-gnu-as -o uart_hello.o uart_hello.s
    2. Link to Generate ELF File (specifying entry point and memory layout):

      aarch64-linux-gnu-ld -nostdlib -o uart_hello.elf uart_hello.o -Ttext=0x80000
    3. Run with QEMU:

      qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -kernel uart_hello.elf
    • <span>-M virt</span>: Select QEMU’s <span>virt</span> machine (default includes PL011 UART).

    • <span>-nographic</span>: Disable graphical interface, output directly to terminal.

    • <span>-kernel</span>: Specify the ELF file to load.

  • Expected Output:

    Hello, ARMv8 Bare-metal!
  • 1.5 Key Code Analysis

    1. UART Sending Logic:

    • Check Transmit FIFO: Ensure the buffer is not full by reading the <span>UARTFR</span> register’s <span>TXFF</span> bit.

    • Write Character: Write the character to the <span>UARTDR</span> register, hardware automatically sends it.

  • Boot Process:

    • The program starts executing from <span>_start</span>, initializing the stack pointer (this is just an example, actual complex programs need this).

    • Characters are sent one by one in a loop until the terminator <span>0</span> is encountered.

    1.6 Hands-on Experiment

    1. Modify String Content: Change the text in <span>message</span> to something else, recompile and run.

    2. Debug the Program: Try enabling GDB debugging in QEMU:

      qemu-system-aarch64 -M virt -cpu cortex-a53 -nographic -kernel uart_hello.elf -S -s

      Connect GDB in another terminal:

      gdb-multiarch -ex "target remote localhost:1234" -ex "file uart_hello.elf"

    Practical results show:System Practice Learning ARMv8 Assembly - Course 1System Practice Learning ARMv8 Assembly - Course 1

    Leave a Comment