Hello World on RTEMS for GR712RC¶
A complete walkthrough: write a minimal RTEMS app, cross-compile it with
rcc (sparc-gaisler-rtems5-gcc), wrap it into a PROM image with
mkprom2, and boot it on Lince — both via the hello-gr712rc example and
via the lince-emu CLI.
flowchart LR
A[main.c] -->|sparc-gaisler-rtems5-gcc| B[hello.elf]
B -->|mkprom2| C[hello.rom]
C -->|prom_image_path / --image| D[lince-emu / hello-gr712rc]
D -->|APBUART0 → stdout| E["Hello world!"]
Already have RTEMS images?
If you only want to run a pre-built image, skip to step 4 — or use
the bundled tests/guest-programs/rtems/hello-world/hello-world.elf
with lince-emu --image (see First RTEMS boot).
You need RCC + mkprom2 only to build the guest yourself.
A minimal RTEMS app needs two pieces: source code compiled with rcc
(sparc-gaisler-rtems5-gcc), then a PROM image produced with mkprom2.
1. Source (main.c)¶
#include <rtems.h>
#include <rtems/bspIo.h>
rtems_task Init(rtems_task_argument arg) {
while (1) {
printk("Hello world!\n");
rtems_task_wake_after(rtems_clock_get_ticks_per_second());
}
rtems_task_exit();
}
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
#define CONFIGURE_MAXIMUM_TASKS 4
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_INIT
#include <rtems/confdefs.h>
The confdefs.h macros declare what the kernel must include before linking;
Init is the entry point RTEMS calls after boot.
2. Compilation with rcc¶
rcc is Gaisler's RTEMS Cross-Compiler — sparc-gaisler-rtems5-gcc — a gcc
patched for SPARC LEON.
Flag breakdown:
-qbsp=gr712rc— selects the GR712RC Board Support Package (links the right BSP, startup, and linker script; default load address0x40000000in RAM).-mcpu=leon3— emit LEON3 SPARC V8 instructions (FPU, CASA, etc.).-mfix-gr712rc— apply errata workarounds specific to the GR712RC silicon (compiler avoids known buggy instruction patterns).-g— DWARF debug info (forgdb).-O2— standard optimization; safe with the errata fixes above.
The same flags are passed at link time, since the BSP selection drives the linker script and startup objects.
3. PROM image with mkprom2¶
mkprom2 wraps the ELF with a small bootstrap that initializes the memory
controller, copies .text/.data into RAM, and jumps to _start.
mkprom2 -ccprefix sparc-gaisler-rtems5 \
-leon3 -freq 80 -baud 38400 \
-nocomp -nomsg \
hello.elf -o hello.rom
Flag breakdown:
-ccprefix sparc-gaisler-rtems5— tellsmkprom2which cross-toolchain to invoke when assembling its bootstrap stub (it calls<prefix>-gcc,<prefix>-objcopy, etc.).-leon3— generate a LEON3-compatible bootstrap (memory controller init, trap table, register window setup match LEON3).-freq 80— system clock in MHz; used to program SDRAM timing and the UART divisor. GR712RC dev board typically runs at 80 MHz.-baud 38400— boot-message UART baudrate, derived from-freq.-nocomp— do not LZSS-compress the payload (faster boot, larger image).-nomsg— suppress the bootstrap's startup banner over UART.
4. Output¶
The resulting hello.rom is the bootable PROM image. Once running on the
board, Hello world! is printed on UART0 once per second.
5. SMP boot on Lince¶
GR712RC silicon is dual-core, but only CPU 0 starts executing at reset.
CPU 1 stays halted in a power-down state until software writes
IRQMP.MPSTAT.MPS[1] = 1. The mkprom2 bootstrap (and the RTEMS BSP startup
that follows) runs entirely on CPU 0; CPU 1 is only released later, by
the RTEMS SMP scheduler — or never, if the BSP is uniprocessor.
The Lince emulator mirrors this reset model in both the PROM path
(prom_image_path) and the ELF path (load_elf): on initialize(), all
cores are reset to cfg.reset_pc, and then CPU 1..N are explicitly set
to power-down. Running the example below boots cleanly with cores = 2
even though hello.rom is a uniprocessor app:
./build/examples/hello-gr712rc/hello-gr712rc hello.rom
# [INFO] [emulator] Initialized with 2 core(s), 67108864 bytes RAM at 0x40000000
# [apbuart0] Hello world!
# [apbuart0] Hello world!
# ...
If both cores entered the mkprom2 bootstrap from PC = 0 in parallel
they would collide on shared state, one would double-trap, and the
emulator would report HaltedMode after a single quantum (~1280
instructions). The parking step prevents that. The N=2 case is covered
by the hello-lince: PROM-boot RTEMS app prints via APBUart integration
test (sections uniprocessor (N=1) and SMP (N=2, GR712RC default)).
6. Boot it with the lince-emu CLI¶
The example program is one way to boot; the standalone CLI is another.
--image accepts the mkprom2 .rom directly (its PT_LOAD segments are
flattened into PROM by initialize()):
./build/src/app/lince-emu --image hello.rom # GR712RC, realtime pacing
./build/src/app/lince-emu --turbo --image hello.rom # as fast as the host can
UART0 output (Hello world!) appears on stdout once per simulated second;
in --turbo mode it appears as fast as the guest produces it. See the
CLI reference for every flag.
mkprom2 -freq must match the emulator clock
A mkprom2 image bakes the -freq value (here 80) into its
bootloader, which programs the GPTIMER scaler for that frequency. The
GR712RC recipe runs at 80 MHz, so -freq 80 matches. If you change the
emulator clock (--mhz / cfg.cpu_clock_hz), rebuild the .rom with
a matching -freq. Direct-ELF guests have no baked frequency —
initialize() re-derives the scaler — so they need no rebuild. See
Clock frequency.
Next steps¶
- Read the UART console output programmatically → UART and console.
- Debug the guest with GDB → Debugging with GDB.
- Understand the GR712RC peripheral/IRQ map → GR712RC reference.
- Embed the boot flow in your own program → Embedding as a library.