Legacy Applications Porting Guide¶
注解
This document is still work in progress.
This guide will help you move your applications from the nanokernel/microkernel model to the unified kernel. The unified kernel was introduced with Zephyr Kernel 1.6.0 which was released late 2016.
A list of the major changes that came with the unified kernel can be found in the section 与第 1 版内核的改动.
API Changes¶
As described in the section 内核 API the kernel now has one unified and consistent API with new naming.
An application using the old APIs can still be compiled using a legacy interface
that translates old APIs to the new APIs. This legacy interface maintained in
include/legacy.h
can be used as a guide when porting a legacy
application to the new kernel.
Same Arguments¶
In many cases, a simple search and replace is enough to move from the legacy to the new APIs, for example:
task_abort()
->k_thread_abort()
task_sem_count_get()
->k_sem_count_get()
Additional Arguments¶
The number of arguments to some APIs have changed,
nano_sem_init()
->k_sem_init()
This function now accepts 2 additional arguments:
- Initial semaphore count
- Permitted semaphore count
When porting your application, make sure you have set the right arguments. For example, calls to the old API:
nano_sem_init(sem)
depending on the usage becomes in most cases:
k_sem_init(sem, 0, UINT_MAX);
Return Codes¶
Many kernel APIs now return 0 to indicate success and a non-zero error code to indicate the reason for failure. You should pay special attention to this change when checking for return codes from kernel APIs, for example:
k_sem_take()
now returns 0 on on success, in the legacy APInano_sem_take()
returned 1 when a semaphore is available.
Application Porting¶
The existing synchronization_sample from the Zephyr tree will be used to guide you with porting a legacy application to the new kernel.
The code has been ported to the new kernel and is shown below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 | #include <zephyr.h>
#include <misc/printk.h>
/*
* The hello world demo has two threads that utilize semaphores and sleeping
* to take turns printing a greeting message at a controlled rate. The demo
* shows both the static and dynamic approaches for spawning a thread; a real
* world application would likely use the static approach for both threads.
*/
/* size of stack area used by each thread */
#define STACKSIZE 1024
/* scheduling priority used by each thread */
#define PRIORITY 7
/* delay between greetings (in ms) */
#define SLEEPTIME 500
/*
* @param my_name thread identification string
* @param my_sem thread's own semaphore
* @param other_sem other thread's semaphore
*/
void helloLoop(const char *my_name,
struct k_sem *my_sem, struct k_sem *other_sem)
{
while (1) {
/* take my semaphore */
k_sem_take(my_sem, K_FOREVER);
/* say "hello" */
printk("%s: Hello World from %s!\n", my_name, CONFIG_ARCH);
/* wait a while, then let other thread have a turn */
k_sleep(SLEEPTIME);
k_sem_give(other_sem);
}
}
/* define semaphores */
K_SEM_DEFINE(threadA_sem, 1, 1); /* starts off "available" */
K_SEM_DEFINE(threadB_sem, 0, 1); /* starts off "not available" */
/* threadB is a dynamic thread that is spawned by threadA */
void threadB(void *dummy1, void *dummy2, void *dummy3)
{
ARG_UNUSED(dummy1);
ARG_UNUSED(dummy2);
ARG_UNUSED(dummy3);
/* invoke routine to ping-pong hello messages with threadA */
helloLoop(__func__, &threadB_sem, &threadA_sem);
}
char __noinit __stack threadB_stack_area[STACKSIZE];
/* threadA is a static thread that is spawned automatically */
void threadA(void *dummy1, void *dummy2, void *dummy3)
{
ARG_UNUSED(dummy1);
ARG_UNUSED(dummy2);
ARG_UNUSED(dummy3);
/* spawn threadB */
k_thread_spawn(threadB_stack_area, STACKSIZE, threadB, NULL, NULL, NULL,
PRIORITY, 0, K_NO_WAIT);
/* invoke routine to ping-pong hello messages with threadB */
helloLoop(__func__, &threadA_sem, &threadB_sem);
}
K_THREAD_DEFINE(threadA_id, STACKSIZE, threadA, NULL, NULL, NULL,
PRIORITY, 0, K_NO_WAIT);
|
Porting a Nanokernel Application¶
Below is the code for the application using the legacy kernel:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | #include <zephyr.h>
#include <misc/printk.h>
/*
* Nanokernel version of hello world demo has a task and a fiber that utilize
* semaphores and timers to take turns printing a greeting message at
* a controlled rate.
*/
/* specify delay between greetings (in ms); compute equivalent in ticks */
#define SLEEPTIME 500
#define SLEEPTICKS (SLEEPTIME * sys_clock_ticks_per_sec / 1000)
#define STACKSIZE 2000
char __stack fiberStack[STACKSIZE];
struct nano_sem nanoSemTask;
struct nano_sem nanoSemFiber;
void fiberEntry(void)
{
struct nano_timer timer;
uint32_t data[2] = {0, 0};
nano_sem_init(&nanoSemFiber);
nano_timer_init(&timer, data);
while (1) {
/* wait for task to let us have a turn */
nano_fiber_sem_take(&nanoSemFiber, TICKS_UNLIMITED);
/* say "hello" */
printk("%s: Hello World!\n", __func__);
/* wait a while, then let task have a turn */
nano_fiber_timer_start(&timer, SLEEPTICKS);
nano_fiber_timer_test(&timer, TICKS_UNLIMITED);
nano_fiber_sem_give(&nanoSemTask);
}
}
void main(void)
{
struct nano_timer timer;
uint32_t data[2] = {0, 0};
task_fiber_start(&fiberStack[0], STACKSIZE,
(nano_fiber_entry_t) fiberEntry, 0, 0, 7, 0);
nano_sem_init(&nanoSemTask);
nano_timer_init(&timer, data);
while (1) {
/* say "hello" */
printk("%s: Hello World!\n", __func__);
/* wait a while, then let fiber have a turn */
nano_task_timer_start(&timer, SLEEPTICKS);
nano_task_timer_test(&timer, TICKS_UNLIMITED);
nano_task_sem_give(&nanoSemFiber);
/* now wait for fiber to let us have a turn */
nano_task_sem_take(&nanoSemTask, TICKS_UNLIMITED);
}
}
|
Porting a Microkernel Application¶
The MDEF feature of the legacy kernel has been eliminated. Consequently, all kernel objects are now defined directly in code.
Below is the code for the application using the legacy kernel:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include <zephyr.h>
#include <misc/printk.h>
/*
* Microkernel version of hello world demo has two tasks that utilize
* semaphores and sleeps to take turns printing a greeting message at
* a controlled rate.
*/
/* specify delay between greetings (in ms); compute equivalent in ticks */
#define SLEEPTIME 500
#define SLEEPTICKS (SLEEPTIME * sys_clock_ticks_per_sec / 1000)
/*
*
* @param taskname task identification string
* @param mySem task's own semaphore
* @param otherSem other task's semaphore
*
*/
void helloLoop(const char *taskname, ksem_t mySem, ksem_t otherSem)
{
while (1) {
task_sem_take(mySem, TICKS_UNLIMITED);
/* say "hello" */
printk("%s: Hello World from %s!\n", taskname, CONFIG_ARCH);
/* wait a while, then let other task have a turn */
task_sleep(SLEEPTICKS);
task_sem_give(otherSem);
}
}
void taskA(void)
{
/* taskA gives its own semaphore, allowing it to say hello right away */
task_sem_give(TASKASEM);
/* invoke routine that allows task to ping-pong hello messages with taskB */
helloLoop(__func__, TASKASEM, TASKBSEM);
}
void taskB(void)
{
/* invoke routine that allows task to ping-pong hello messages with taskA */
helloLoop(__func__, TASKBSEM, TASKASEM);
}
|
A microkernel application defines the used objects in an MDEF file, for this porting sample using the synchronization_sample, the file is shown below:
1 2 3 4 5 6 7 8 9 10 11 | % Application : Hello demo
% TASK NAME PRIO ENTRY STACK GROUPS
% ==================================
TASK TASKA 7 taskA 1024 [EXE]
TASK TASKB 7 taskB 1024 [EXE]
% SEMA NAME
% =============
SEMA TASKASEM
SEMA TASKBSEM
|
In the unified kernel the semaphore will be defined in the code as follows:
1 2 3 4 | /* define semaphores */
K_SEM_DEFINE(threadA_sem, 1, 1); /* starts off "available" */
K_SEM_DEFINE(threadB_sem, 0, 1); /* starts off "not available" */
|
The threads (previously named tasks) are defined in the code as follows, for thread A:
1 2 | K_THREAD_DEFINE(threadA_id, STACKSIZE, threadA, NULL, NULL, NULL,
PRIORITY, 0, K_NO_WAIT);
|
Thread B (taskB in the microkernel) will be spawned dynamically from thread A (See 线程的创建 section):
1 2 3 | /* spawn threadB */
k_thread_spawn(threadB_stack_area, STACKSIZE, threadB, NULL, NULL, NULL,
PRIORITY, 0, K_NO_WAIT);
|