RT-Thread RTOS Auto-Initialization Mechanism Analysis
RT-Thread is an open-source embedded real-time operating system, with rich middle-tier components and an excellent hardware and software ecosystem, all delivering fantastic support for the Internet of Things industry. Since its inception in 2006, RT-Thread has powered 1.5 billion devices, including Wearables, Smart Home Appliances, Automotive Electronics, Medical Electronics, Consumer Electronics, Energy, and many other industries.
On March 15, RT-Thread is partnered with NXP Semiconductors, STMicroelectronics, and WCHElectronics to bring an IoT Webinar: Power the IoT Devices. Free Register Here.
Let’s go back to the Auto-Initialization Mechanism.
#General initialization
During the embedded development, we’re mainly taking this approach to initialize a peripheral
int main(int argc, char *argv[])
{
clk_init();
led_init();
beep_init();
key_init();
.....
while(1)
{
...
}
}
The order of this initialization is relatively clear, and it is quite easy to figure out the peripherals that have been initialized and the order in which they are initialized. However, the main
function is particularly cumbersome, especially when there are a lot of peripherals that need to be initialized.
#Auto-Initialization
Programming C on the computer, to print a hello world
#include <stdio.h>
int main(int argc, char *argv[])
{
printf("hello world\r\n");
return 1;
}
Here we can directly use printf
to print without any initialization steps, this idea leads to the RT-Thread
automatic initialization mechanism.
RT-Thread
Auto-Initialization
int led_init()
{
...
}
INIT_APP_EXPORT(led_init);
int main(int argc, char *argv[])
{
led_on();
rt_kprintf("hello rt thread\r\n");
return 1;
}
The central thought of automatic initialization is that the initialization of each peripheral is completed before executing to the main
function and all the peripherals can be used directly in the main
function. For example, the above program directly uses rt_kprintf
for output, and lit the LED
.
#Auto-initialized API
The auto-initialized API
intercepted from the RT-Thread
source code, as shown as follows:
/* board init routines will be called in board_init() function */
#define INIT_BOARD_EXPORT(fn) INIT_EXPORT(fn, "1")
/* pre/device/component/env/app init routines will be called in init_thread */
/* components pre-initialization (pure software initilization) */
#define INIT_PREV_EXPORT(fn) INIT_EXPORT(fn, "2")
/* device initialization */
#define INIT_DEVICE_EXPORT(fn) INIT_EXPORT(fn, "3")
/* components initialization (dfs, lwip, ...) */
#define INIT_COMPONENT_EXPORT(fn) INIT_EXPORT(fn, "4")
/* environment initialization (mount disk, ...) */
#define INIT_ENV_EXPORT(fn) INIT_EXPORT(fn, "5")
/* appliation initialization (rtgui application etc ...) */
#define INIT_APP_EXPORT(fn) INIT_EXPORT(fn, "6")
API
functions list is shown in the following table
#Mechanism Analysis
#INIT_EXPORT Function
Seeing from the initialization functions, we’re getting to know that their final call is the INIT_EXPORT
function, only the entered parameters are different. Let's take a look at the definition of this function
#define INIT_EXPORT(fn, level) \
RT_USED const init_fn_t __rt_init_##fn SECTION(".rti_fn." level) = fn
The INIT_EXPORT()
function has two parameters, the first parameter indicates which function needs to be initialized, delivering the function pointer(the function name), and the second parameter indicates which segment to place the function pointer into. Let's get into the Macro next, and there are several prerequisites required to know before we jump to the macro.
RT_USED
#define RT_USED attribute((used))
The attribute__(used)
function has been marked in the object file to prevent the linker from deleting unused sections.
init_fn_t type
typedef int (*init_fn_t)(void);
Here a return value of int
is defined, a function pointer type with the function parameter void
and renamed to init_fn_t
.
##
##
belongs to the C language, and its role is to combine two language symbols into a single language symbol
SECTION
#define SECTION(x) attribute((section(x)))
__attribute__((section(name)))
puts the functional functions or data into an input segment specified named name
With the above preliminary backup, let’s analyze the following INIT_EXPORT
macro. Expand the macro to as below
RT_USED const init_fn_t __rt_init_fn SECTION(".rti_fn." level) = fn
The function of this macro is to assign the pointer to the function fn
to the variable __rt_init_fn
, this variable type is RT_USED const init_fn_t
, and it is stored in the specified segment .rti_fn.level
. So after the function is exported using an automatic initialization macro, pointers to each initialization function will be stored in these data segments. When we're dereferencing these pointers will be taken as we're executing the corresponding function.
#Division of segments
The segments are divided in component.c
, and the source code is as follows
static int rti_start(void)
{
return 0;
}
INIT_EXPORT(rti_start, "0");
static int rti_board_start(void)
{
return 0;
}
INIT_EXPORT(rti_board_start, "0.end");
static int rti_board_end(void)
{
return 0;
}
INIT_EXPORT(rti_board_end, "1.end");
static int rti_end(void)
{
return 0;
}
INIT_EXPORT(rti_end, "6.end");
The distribution of the segments exported above using the INIT_EXPORT
macro is shown in the following table
After adding the six segments that are exported after auto-initialization, the distribution of each segment is shown in the following table
#rt_components_board_init function
Head to check about the implementation of the rt_components_board_init
function
void rt_components_board_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
for (desc = &__rt_init_desc_rti_board_start; desc < &__rt_init_desc_rti_board_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
#endif
}
If not consider about the RT_DEBUG_INIT
, it's clear to find that the rt_components_board_init
is executing the following
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_start; fn_ptr < &__rt_init_rti_board_end; fn_ptr++)
{
(*fn_ptr)();
}
The above code defines a fn_ptr
pointer, which is dereferenced when the range of the pointer is within the range of __rt_init_rti_board_start
and rt_init_rti_board_end
, where the pointer is the function pointer put during automatic initialization, so it is quite the execution of the function. That is, the function exported by INIT_BOARD_EXPORT(fn)
is executed
#rt_components_init function
source code:
void rt_components_init(void)
{
#if RT_DEBUG_INIT
int result;
const struct rt_init_desc *desc;
rt_kprintf("do components initialization.\n");
for (desc = &__rt_init_desc_rti_board_end; desc < &__rt_init_desc_rti_end; desc ++)
{
rt_kprintf("initialize %s", desc->fn_name);
result = desc->fn();
rt_kprintf(":%d done\n", result);
}
#else
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
#endif
}
If not consider about the RT_DEBUG_INIT
, it's clear to find that the rt_components_init
is executing the following
volatile const init_fn_t *fn_ptr;
for (fn_ptr = &__rt_init_rti_board_end; fn_ptr < &__rt_init_rti_end; fn_ptr ++)
{
(*fn_ptr)();
}
The code also defines a fn_ptr
pointer, which is dereferenced when the range of the pointer is within the range of __rt_init_rti_board_end
and __rt_init_rti_end
, where the pointer is the function pointer put during automatic initialization, again, we're getting this function executed. That is, the function derived between the INIT_PREV_EXPORT(fn)
to INIT_APP_EXPORT(fn)
segments is executed
#Execution of auto-initialized functions
The startup process of RT-Thread
:
The rt_components_board_init()
function and the rt_componenets_init()
function are executed.
#Sample
Add the following test code to the main.c
function
int led_init(void)
{
return 1;
}
INIT_APP_EXPORT(led_init);
The compiled .map
file is shown as follows:
The function pointer __rt_init_led_init
is located in the .rti_fn.6
segment, and the function rt_components_init()
will dereference this pointer when it is executed, that is, execute the led_init
function
Connect with RT-Thread: Github | Facebook | Twitter| Linkedin | Youtube
Subscribe to my newsletter
Read articles from Abby directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Abby
Abby
Hi, I am Abby. This account is set to share everything new about OpenSource RT-Thread IoTOS.