Skip to content

First RTEMS boot

This walkthrough takes you from zero to a real RTEMS hello-world running on the emulator, using the RCC 1.3.2 SPARC cross-toolchain.

Prerequisites

  • A working build of Lince (see Installation).
  • The RCC 1.3.2 toolchain installed under /opt/rcc-1.3.2-gcc/. The path is hard-coded in the test build scripts; if you install elsewhere, set RCC_PREFIX accordingly.
ls /opt/rcc-1.3.2-gcc/bin/sparc-rtems5-gcc   # should exist

1. Build or obtain the hello-world image

The repository includes a pre-built binary at tests/rtems/bin/hello-world.elf. If you want to rebuild it from source, use the RCC toolchain:

# From the repository root (requires sparc-gaisler-rtems5-gcc on PATH):
cmake --build build --target sparc_test_binaries
# The cross-compiled ELF ends up in the build directory.

You can also use your own RCC toolchain and link script to produce a compatible ELF — just place it at tests/rtems/bin/hello-world.elf for the integration tests to find it.

The RTEMS BSP leon3 is configured without MMU (default for the MVP) and without networking. The image links a single user task that calls printf("Hello World\n") and exits cleanly.

2. Run it

./build/src/app/lince-emu --image tests/rtems/bin/hello-world.elf

Expected output:

*** BEGIN OF TEST HELLO WORLD ***
*** TEST VERSION: 5.x.y
*** TEST STATE: EXPECTED-PASS
*** TEST BUILD:
*** TEST TOOLS: 7.5.0 20191114 (RCC 1.3.2 [bcc-2.2.2-gcc])
Hello World

*** END OF TEST HELLO WORLD ***

The CLI returns exit code 0 when the simulated time budget expires naturally. RTEMS itself never calls exit() in this configuration; it returns to the idle thread, which immediately enters power-down via asr19. The emulator's idle-time skip then fast-forwards simulated time to the deadline.

3. Run the SPTEST integration suite

The repository ships ten SPARC RTEMS sptests as pre-built ELFs (in tests/rtems/sptests/bin/, generated by a build script):

ctest --test-dir build -R rtems_sptests --output-on-failure

A sptest is recorded as PASS when its captured UART contains the exact string *** END OF TEST. As of the MVP all 10 of sp01, sp02, sp03, sp04, sp05, sp06, sp07, sp08, sp11, sp12 pass.

4. Capture the UART programmatically

If you are integrating Lince in a CI pipeline, replace the default StdoutCharDevice with the test-support CapturingCharDevice:

#include "lince/runtime/emulator.hpp"
#include "tests/support/capturing_char_device.hpp"

auto cap = std::make_unique<lince::test_support::CapturingCharDevice>();
auto* ref = cap.get();
emu->set_character_device(std::move(cap));
emu->initialize();
emu->load_elf("hello-world.elf");
emu->run_for(lince::SimTimeNs{2'000'000'000ULL});

if (ref->captured().find("*** END OF TEST") != std::string::npos) {
    /* PASS */
}

See UART and console for more capture strategies.

What is happening under the hood?

The boot sequence on real GR712RC and on Lince is byte-identical:

  1. _start runs from the ELF entry point (typically 0x40000000).
  2. The RTEMS bootstrap probes the MemCtrl (FTMCTRL) for memory layout, programs the GPTimer prescaler for a 1 ms tick, and unmasks IRQs at the IRQMP.
  3. The first user task is created and scheduled.
  4. printf writes through the APBUART data register; Lince's APBUart peripheral forwards every byte to the injected ICharacterDevice.
  5. The task ends, the idle thread halts each CPU via wr %g0, %asr19, and the emulator skips simulated time to the next pending event.

A trace comparator tool is planned for a future release to automate verification of instruction counts and MMIO sequences against a reference.