The Boot Stage Record library provides a standardized way to measure and track boot time performance across different components of an embedded system. Inspired by U-Boot's bootstage record, this lightweight profiling tool allows developers to instrument their boot process with consistent timing measurements, helping identify bottlenecks and track performance improvements.
- Standardized data structure across all system components
- Memory efficient with no dynamic allocation
- Platform-independent timestamp mechanism
- Single boot stage with multiple measurement points
- Simple API with minimal function calls
This structure stores information about a single profile measurement:
typedef struct
{
/* Name of the record profile */
char name[24];
/* Time measurement for this profile */
uint64_t time;
} boot_record_profile_t;name[24]: A null-terminated string identifier for the profile pointtime: A timestamp value in microseconds, captured at the profile point
This structure represents a boot stage with its associated profile records:
typedef struct
{
/* Unique identifier for this record */
uint32_t record_id;
/* Count of profile records in this boot stage */
uint32_t record_count;
/* Start time of this boot stage */
uint64_t start_time;
/* Array of profile records */
boot_record_profile_t profiles[0];
} boot_stage_record_t;record_id: A unique identifier for this boot stagerecord_count: Number of profile records currently storedstart_time: Timestamp when this boot stage beganprofiles[0]: Flexible array member storing variable number of profile records
This internal structure manages the overall boot record configuration:
typedef struct
{
/* Base address of allocated memory */
void *memory_base;
/* Size of allocated memory */
uint32_t memory_size;
/* Number of possible record count */
uint32_t possible_records;
/* Array of boot stage records */
boot_stage_record_t *records;
} boot_records_t;memory_base: Base address of the allocated memory blockmemory_size: Total size of the allocated memory blockpossible_records: Maximum number of profile records that can be storedrecords: Pointer to the boot stage record structure
Initializes the boot record library with a specified memory area.
boot_record_status_t boot_record_init(uint32_t stage_id, void *memory_addr, uint32_t size);Parameters:
stage_id: Unique identifier for this boot stagememory_addr: Pointer to pre-allocated memory for storing recordssize: Size of the allocated memory in bytes
Returns:
BOOT_RECORD_SUCCESS: Initialization successfulBOOT_RECORD_ERR_INVALID_PARAMS: Invalid parametersBOOT_RECORD_ERR_INSUFFICIENT_MEM: Not enough memory
Records a profile point with the current timestamp.
boot_record_status_t boot_record_log_profile(const char *name);Parameters:
name: Name of the profile point to record
Returns:
BOOT_RECORD_SUCCESS: Profile recorded successfullyBOOT_RECORD_ERR_INVALID_PARAMS: Invalid parametersBOOT_RECORD_ERR_OVERFLOW: Profile record limit exceeded
A weak function that should be implemented by the user to provide platform-specific timestamp functionality.
__attribute__((weak)) uint64_t boot_record_get_timestamp(void);Returns:
- Current timestamp in microseconds
Add the following to your Makefile:
# Include Boot Record Library
BOOT_RECORD_DIR := path/to/drivers/boot-stage-record
INCLUDES += -I$(BOOT_RECORD_DIR)
SRCS += $(BOOT_RECORD_DIR)/bootrecord.c# Add Boot Record Library
add_subdirectory(path/to/drivers/boot-stage-record)
target_link_libraries(your_target bootrecord)/* Define in your platform code */
uint64_t boot_record_get_timestamp(void)
{
/* Example for ARM Cortex-M based platforms */
uint32_t cycles = DWT->CYCCNT;
uint32_t freq = SystemCoreClock;
/* Convert cycles to microseconds */
return ((uint64_t)cycles * 1000000ULL) / freq;
}For bootloader integration:
/* In your bootloader's early initialization */
#define BOOT_RECORD_SIZE 4096 /* Adjust based on your needs */
static uint8_t boot_record_memory[BOOT_RECORD_SIZE] __attribute__((aligned(8), section(".boot_record")));
void early_init(void)
{
/* Initialize with stage ID 1 for bootloader */
boot_record_init(1, boot_record_memory, BOOT_RECORD_SIZE);
boot_record_log_profile("Bootloader_Start");
/* Continue bootloader initialization */
}For component-level integration:
/* In your component initialization */
void component_init(void)
{
/* Access pre-allocated memory region for boot records */
extern uint8_t __boot_record_section_start[];
extern uint8_t __boot_record_section_size[];
/* Initialize with unique component ID */
uint32_t component_id = 0x100; /* Choose a unique ID scheme */
boot_record_init(component_id, __boot_record_section_start,
(uint32_t)__boot_record_section_size);
boot_record_log_profile("Component_Init_Start");
/* Component initialization logic */
boot_record_log_profile("Component_Init_Complete");
}Add to your linker script:
SECTIONS {
/* Other sections... */
.boot_record (NOLOAD) : {
__boot_record_section_start = .;
. = ALIGN(8);
KEEP(*(.boot_record))
. = ALIGN(8);
__boot_record_section_end = .;
} > RAM
__boot_record_section_size = __boot_record_section_end - __boot_record_section_start;
}
The library uses a contiguous block of memory organized as follows:
boot_stage_record_tstructure at the beginning of the memory block- Followed by an array of
boot_record_profile_tstructures (size determined at initialization)
#include "bootrecord.h"
/* Example for tracking bootloader initialization */
void bootloader_main(void)
{
/* Allocate memory for boot records */
static uint8_t boot_memory[1024] __attribute__((aligned(8)));
/* Initialize boot record system with stage ID 1 */
boot_record_init(1, boot_memory, sizeof(boot_memory));
/* Record first profile point */
boot_record_log_profile("Bootloader_Start");
/* Hardware initialization */
init_clocks();
boot_record_log_profile("Clocks_Initialized");
init_memory_controller();
boot_record_log_profile("Memory_Controller_Ready");
load_os_image();
boot_record_log_profile("OS_Image_Loaded");
/* Boot complete */
boot_record_log_profile("Bootloader_Complete");
/* Boot record data remains in memory for OS to analyze */
jump_to_os();
}- The library uses minimal CPU resources during recording
- Memory usage is predictable and defined at initialization
- No dynamic memory allocation occurs during operation
- Profile names are limited to 23 characters plus null terminator
Copyright (C) 2025 Texas Instruments Incorporated
This library is provided under the terms of the BSD 3-Clause license.
