RK0

The Real-Time Kernel '0'

About the Building System: How RK0 Gets Up and Running

This blog complements the earlier post About Processes, Tasks and Threads”. That explained what runs in RK0; this one explains how the toolchain arranges it.


In constrained systems such as those targeted by RK0, the boundary between software and hardware is blurry.

The linker decides exactly where every byte of code or data will reside, and the start‑up code initialises that memory before the kernel starts. Understanding how this is constructed is essential when you want to have a system under your control.

From Source Code to Execution Image

After compilation and linking the tool‑chain emits an execution image – an ELF file that contains only statically located information:

SectionRoleLives in
.textMachine instructions, constants in const space, vector tableFlash
.dataInitialised globals and static.Flash (load) ➜ RAM (run)
.bss Unitialised or zero-initialised globals and static RAM

Addresses for these objects are fixed at link‑time; no relocation happens later.

Stack and Heap

Each RK0 thread owns a private stack buffer that you declare yourself, typically:

RK_STACK stack1[STACKSIZE] RK_ALIGN(8);

Because these arrays are unitialised they reside in .bss. Two stacking notions therefore, coexist:

ConceptWhere it livesPurpose
System Stack (uses the Main Stack Pointer)Initialises on the top of RAMUsed by exceptions and during early start‑up. Size reserved via _Min_Stack_Size.
Task Stacks (uses the Process Stack Pointer)Inside .bss (e.g. stack1, stack2, …) Optionally one can create dedicated sections in the linker for the stacks.Assigned to each thread by RK0’s scheduler.

The heap (if enabled) begins just after .bss and grows upward.

Runtime Image: What Actually Exists in RAM

After the start‑up routine copies .data and zeroes .bss, the main memory for a typical RK0 build looks like this (low → high):

| .data | .bss (includes task stacks) | heap (optional) | free RAM | MSP initial value |

  • The task stacks already exist in RAM when main() begins; RK0 simply assigns each buffer to a thread’s context.
  • free RAM is the slack between the (ever‑growing) heap and the fixed MSP area – available for further dynamic allocations or extra static buffers in future builds.
  • The MSP (Main Stack Pointer) initial value sits at the very top of RAM
  • Once scheduler runs, each thread switches to PSP (Process Stack Pointer), leaving MSP strictly for faults/handler mode.

The Linker Script (linker.ld)

All of this is dictated by the linker script.

/* RK0 Linker Script for QEMU (lm3s6965evb) */

ENTRY(Reset_Handler)
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Define memory layout */
MEMORY
{
  FLASH (rx)  : ORIGIN = 0x00000000, LENGTH = 256K
  RAM   (rwx) : ORIGIN = 0x20000000, LENGTH = 64K
}

The MEMORY block names physical regions and their permissions; subsequent SECTIONS commands place .text, .data, .bss, task stacks, heap reservation, and finally the MSP area.

The script also emits symbols such as _sidata, _sdata, _edata, _sbss, _ebss, and _estack. These are link‑time constants referenced directly by the start‑up code (a subject for another blog).

The Makefile: Stitching Everything Together

A key excerpt from the Makefile regarding the linking the stage is as follows:

LDFLAGS := -nostartfiles -T $(LINKER_SCRIPT) $(MCU_FLAGS) \
           -Wl,-Map=$(MAP),--cref -Wl,--gc-sections \
           -specs=nano.specs -lc  

I mean it is key because it is a typical linking directive for an embedded (non-hosted) C image.

FlagWhy it matters
-nostartfilesWe provide our implementation of what happens before main(), instead of the generic GCC C Runtime Environment .
-T $(LINKER_SCRIPT)Imposes our custom memory map.
--gc-sections*Discards truly unused code and data. (‘gc’ stands for garbage collection).
nano.specsPulls in newlib‑nano, a slim libc ideal for small MCUs.

*.o files are generated with the flags -ffunction-sections and -fdata-sections; otherwise the compiler batches many symbols into one big .text or .data section; using this flags allows for gc‑sections flag effectively build them off.

The “final make rule” converts the ELF to the raw binary that a programmer (or an emulator as QEMU) expects.

So what?

RK0’s build chain is old gold: the Makefile turns source into an ELF execution image, the linker script pins every section into Flash or RAM – what you build is what you get.

Keep this map to hand when you tweak the linker script, alter stack sizes or enable the heap; a small change in one region can ripple through the entire runtime image. In the follow‑up post we’ll step through the C‑based start‑up routine – that fully uses the symbols we have defined on linker.

/* comment here */