Difference between revisions of "Zephyr"

From Wiki at Neela Nurseries
Jump to: navigation, search
m (^ Anatomy of a Zephyr thread: - refining list of elements needed to create a Zephyr application thread)
m (^ Anatomy of a Zephyr thread: - minor formatting edit to code excerpt)
Line 65: Line 65:
 
     int rstatus = 0;
 
     int rstatus = 0;
  
     k_tid_t task_led_tid = k_thread_create(&thread_led_data, thread_led_stack_area,
+
     k_tid_t task_led_tid = k_thread_create(&thread_led_data,
                                            K_THREAD_STACK_SIZEOF(thread_led_stack_area),
+
                                          thread_led_stack_area,
                                            thread_led_entry_point,
+
                                          K_THREAD_STACK_SIZEOF(thread_led_stack_area),
                                            NULL, NULL, NULL,
+
                                          thread_led_entry_point,
                                            THREAD_LED_PRIORITY,
+
                                          NULL, NULL, NULL,
                                            0,
+
                                          THREAD_LED_PRIORITY,
                                            K_MSEC(1000)); // K_NO_WAIT);
+
                                          0,
 +
                                          K_MSEC(1000)); // K_NO_WAIT);
  
 
// REF https://docs.zephyrproject.org/2.6.0/reference/kernel/threads/index.html?highlight=k_thread_create#c.k_thread_name_set
 
// REF https://docs.zephyrproject.org/2.6.0/reference/kernel/threads/index.html?highlight=k_thread_create#c.k_thread_name_set
 
// int k_thread_name_set(k_tid_t thread, const char *str)
 
// int k_thread_name_set(k_tid_t thread, const char *str)
 +
 
     rstatus = k_thread_name_set(task_led_tid, MODULE_ID__THREAD_LED);
 
     rstatus = k_thread_name_set(task_led_tid, MODULE_ID__THREAD_LED);
 
     if ( rstatus == 0 ) { } // avoid compiler warning about unused variable - TMH
 
     if ( rstatus == 0 ) { } // avoid compiler warning about unused variable - TMH

Revision as of 21:49, 10 May 2023

Zephyr RTOS :: Zephyr Drivers


Overview

This NN page a holding point for notes and links to external references regarding Zephyr RTOS, and Zephyr RTOS Project. Some key features of Zephyr RTOS include its designers' incorporation of Device Tree Source and Kconfig frameworks in this real time operating system.

As of 2023 April this page under reorganization, pulling multiple local pages into this page and removing older and defunct content - TMH

For device tree and Kconfig see also:


^ Zephyr Releases

Zephyr RTOS project itself, one of some one hundred twenty projects on Zephyr's Github page, has stable releases which become available once or twice a year. The Zephyr RTOS repository itself is generally at a cutting edge of development. It is `git tagged` or identified as a release candidate, until that point where the work has been finished to review all open issues and developments and create and tag a new stable release.

Nordic Semi forks a recent, but not necessarily most recent stable release of Zephyr RTOS Project as part of its NCS Software Development Kit.

A local NN wiki page with some early Zephyr release notes is Zephyr RTOS releases. There may not be much to save from this article, but contributor Ted to review it.


^ Zephyr Threads

An important executing element of a Zephyr based app is an application defined thread. Zephyr based applications written in C will normally have at least one app side thread , which Zephyr will default to giving the name `main`. This name reflects that application code begins executing from a function named `main()`, as is a long standing convention of C language.

Zephyr based applications can define threads of their own. This is a multi-step action in the code, both at time of development and at run time. Application defined threads are given numeric identifiers by Zephyr at run time; only "main line" code of the app gets a named thread by default. Developers however may optionally call a _Zephyr_API_ to give additional threads human meaningful names.

^ Anatomy of a Zephyr thread

To create a Zephyr thread in an app involves declarations of certain thread elements, a call to Zephyr's thread_create API function, and at minimum an entry point function for the given thread. The following pieces:

    • thread data structure
    • thread priority
    • thread stack size
    • call to Zephyr "create thread" API
    • thread entry point function

A Zephyr thread's data structure, priority and stack size are programmed in a declarative way. Stack size and thread data are typically declared as follows, the first of these involves a function like macro:

K_THREAD_STACK_DEFINE(thread_led_stack_area, THREAD_LED_STACK_SIZE);

struct k_thread thread_led_data;

The call to `thread_create` <- REVIEW looks like this:

int initialize_thread_led(void)
{
    int rstatus = 0;

    k_tid_t task_led_tid = k_thread_create(&thread_led_data,
                                           thread_led_stack_area,
                                           K_THREAD_STACK_SIZEOF(thread_led_stack_area),
                                           thread_led_entry_point,
                                           NULL, NULL, NULL,
                                           THREAD_LED_PRIORITY,
                                           0,
                                           K_MSEC(1000)); // K_NO_WAIT);

// REF https://docs.zephyrproject.org/2.6.0/reference/kernel/threads/index.html?highlight=k_thread_create#c.k_thread_name_set
// int k_thread_name_set(k_tid_t thread, const char *str)

    rstatus = k_thread_name_set(task_led_tid, MODULE_ID__THREAD_LED);
    if ( rstatus == 0 ) { } // avoid compiler warning about unused variable - TMH

    return (int)task_led_tid;
}




^ Some Zephyr thread API references


^ Zephyr Drivers

Zephyr documentation and some Zephyr app developers speak of "in tree" and "out of tree" drivers, in the context of Zephyr RTOS project and Zephyr based apps. In-tree drivers exist within the source code of Zephyr RTOS proper, and development to these drivers is captured in commits to Zephyr RTOS repository. In contrast out-of-tree drivers which work with Zephyr are factored into their own respective code repositories, not Zephyr RTOS' source tree. It is even possible an out-of-tree driver is not version controlled, but this would be a driver that's hard to share and likely not widely used.

To some extent Zephyr in-tree drivers cover the set of peripherals which are on the silicon of a given target microcontroller or MCU that Zephyr RTOS supports. This is not a hard and fast rule, but it is less common that an in-tree Zephyr driver covers the driver needs of a device or peripheral that is off chip, or off SoC or SiP.


^ Some specific Zephyr in-tree drivers


^ Driver Code Factoring

In-tree Zephyr drivers exist as part of Zephyr's source code tree, in other words, the code repo that entails Zephyr RTOS itself. Out-of-tree drivers written to cooperate with Zephyr exist outside of Zephyr's source code tree and are often their own independent code projects.

Example Zephyr app and supporting code base which entails an out-of-tree driver factored along side app code:


^ Zephyr RTOS Logger and Backend Features

Zephyr's thread_analyzer when not configured to dump reports to printk() instead sends thread data to LOG_INF(). Excerpt from Zephyr file `./zephyr/subsys/debug/thread_analyzer.c`:

 11 #include <kernel.h>
 12 #include <debug/thread_analyzer.h>
 13 #include <debug/stack.h>
 14 #include <kernel.h>
 15 #include <logging/log.h>
 16 #include <stdio.h>
 17 
 18 LOG_MODULE_REGISTER(thread_analyzer, CONFIG_THREAD_ANALYZER_LOG_LEVEL);
 19 
 20 #if IS_ENABLED(CONFIG_THREAD_ANALYZER_USE_PRINTK)
 21 #define THREAD_ANALYZER_PRINT(...) printk(__VA_ARGS__)
 22 #define THREAD_ANALYZER_FMT(str)   str "\n"
 23 #define THREAD_ANALYZER_VSTR(str)  (str)
 24 #else
 25 #define THREAD_ANALYZER_PRINT(...) LOG_INF(__VA_ARGS__)
 26 #define THREAD_ANALYZER_FMT(str)   str
 27 #define THREAD_ANALYZER_VSTR(str)  log_strdup(str)
 28 #endif

Zephyr macro `LOG_INF` is in turn defined:

ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/zephyr$ grep -nr LOG_INF ./* | grep define

./include/logging/log.h:61:#define LOG_INF(...)   Z_LOG(LOG_LEVEL_INF, __VA_ARGS__)

Define of Z_LOG:

ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/zephyr$ grep -nr 'Z_LOG[^_]' ./*

./include/logging/log_core.h:295:#define Z_LOG2(_level, _source, _dsource, ...) do { \
./include/logging/log_core.h:329:#define Z_LOG(_level, ...) \


$ grep -nr 'Z_LOG[^_]' ./*

$ vi ./include/logging/log_core.h

329 #define Z_LOG(_level, ...) \
330         Z_LOG2(_level, __log_current_const_data, __log_current_dynamic_data, __VA_ARGS__)


A Zephyr RTOS sample to check out:

`ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/zephyr/samples/subsys/logging/logger$`


- 2022-10-05 -

Zephyr v2.6.0 source file `log_core.h` may also define `log_strdup()`:

./subsys/logging/log_core.c:1017:char *z_log_strdup(const char *str)

But it looks like the routine is statically in-lined in Zephyr header file `log.h`:

./include/logging/log.h:290:static inline char *log_strdup(const char *str)


^ Zephyr thread_analyzer_cb And Related

Zephyr 2.6.0's thread_analyzer_cb() routine gathers and populates a structure with all pertinent, reported run-time thread statistics. This is however a static routine so we cannot call it directly. Nor does it return the summary thread resource use data to its direct calling routine. There is yet hope, and to understand a path forward here excerpted is the definition of this important thread reporting routine from file `./zephyr/subsys/debug/thread_analyzer.c`:

 59 static void thread_analyze_cb(const struct k_thread *cthread, void *user_data)
 60 {
 61         struct k_thread *thread = (struct k_thread *)cthread;
 62 #ifdef CONFIG_THREAD_RUNTIME_STATS
 63         k_thread_runtime_stats_t rt_stats_all;
 64         k_thread_runtime_stats_t rt_stats_thread;
 65         int ret;
 66 #endif
 67         size_t size = thread->stack_info.size;
 68         thread_analyzer_cb cb = user_data;
 69         struct thread_analyzer_info info;
 70         char hexname[PTR_STR_MAXLEN + 1];
 71         const char *name;
 72         size_t unused;
 73         int err;
 74 
 75 
 76 
 77         name = k_thread_name_get((k_tid_t)thread);
 78         if (!name || name[0] == '\0') {
 79                 name = hexname;
 80                 snprintk(hexname, sizeof(hexname), "%p", (void *)thread);
 81         }
 82 
 83         err = k_thread_stack_space_get(thread, &unused);
 84         if (err) {
 85                 THREAD_ANALYZER_PRINT(
 86                         THREAD_ANALYZER_FMT(
 87                                 " %-20s: unable to get stack space (%d)"),
 88                         name, err);

 88                         name, err);
 89 
 90                 unused = 0;
 91         }
 92 
 93         info.name = name;
 94         info.stack_size = size;
 95         info.stack_used = size - unused;
 96 
 97 #ifdef CONFIG_THREAD_RUNTIME_STATS
 98         ret = 0;
 99 
100         if (k_thread_runtime_stats_get(thread, &rt_stats_thread) != 0) {
101                 ret++;
102         }
103 
104         if (k_thread_runtime_stats_all_get(&rt_stats_all) != 0) {
105                 ret++;
106         }
107         if (ret == 0) {
108                 info.utilization = (rt_stats_thread.execution_cycles * 100U) /
109                         rt_stats_all.execution_cycles;
110         }
111 #endif
112         cb(&info);
113 }

The routine which calls this is:

115 void thread_analyzer_run(thread_analyzer_cb cb)
116 {
117         if (IS_ENABLED(CONFIG_THREAD_ANALYZER_RUN_UNLOCKED)) {
118                 k_thread_foreach_unlocked(thread_analyze_cb, cb);
119         } else {
120                 k_thread_foreach(thread_analyze_cb, cb);
121         }
122 }

We should be able to directly call thread_analyzer_run(thread_analyzer_cb cb). If yes, and if we can learn how &info is defined, we should be able to redirect thread_analyzer reports to an arbitrary UART . . .


^ struct thread_analyzer_info

ted@localhost:~/projects/zephyr-based/z4-sandbox-kionix-work/zephyr$ grep -nr 'struct thread_analyzer_info' ./*
./include/debug/thread_analyzer.h:23:struct thread_analyzer_info {
./include/debug/thread_analyzer.h:44:typedef void (*thread_analyzer_cb)(struct thread_analyzer_info *info);

Structure definition for &info passed to thread_analyzer_cb() function above:

/** @defgroup thread_analyzer Thread analyzer
 *  @brief Module for analyzing threads
 *
 *  This module implements functions and the configuration that simplifies
 *  thread analysis.
 *  @{
 */

struct thread_analyzer_info {
        /** The name of the thread or stringified address of the thread handle
         * if name is not set.
         */
        const char *name;
        /** The total size of the stack*/
        size_t stack_size;
        /** Stack size in used */
        size_t stack_used;

#ifdef CONFIG_THREAD_RUNTIME_STATS
        unsigned int utilization;
#endif
};


----- ----- ----- ----- ----- ----- ----- ----- -----

Zephyr Memory Management


^ Zephyr Kernel Timing

Following link at Zephyr Project documentation pages has valuable info on various Zephyr timing and work queueing facilities:

Related:

Important to follow up on this bug report relating to above mentioned Zephyr timing features:


^ Zephyr Application Code Relocation

!-- comentario -->