Advertisement

Nucleus SE RTOS initialization and start-up

March 12, 2019

Colin Walls-March 12, 2019

Initializing Partition Pools

Here is the complete code for NUSE_Init_Partition_Pool():

void NUSE_Init_Partition_Pool(NUSE_PARTITION_POOL pool)
{
      NUSE_Partition_Pool_Partition_Used[pool] = 0;
      #if NUSE_BLOCKING_ENABLE
         NUSE_Partition_Pool_Blocking_Count[pool] = 0;
      #endif
}

Drag the corner of the box to expand as needed. ↑

The partition pool’s “used” counter (NUSE_Partition_Pool_Partition_Used[pool]) is set to zero.

If task blocking is enabled, the partition pool’s blocked task counter (NUSE_Partition_Pool_Blocking_Count[pool]) is set to zero.

Initializing Mailboxes

Here is the complete code for NUSE_Init_Mailbox():

void NUSE_Init_Mailbox(NUSE_MAILBOX mailbox)
{
      NUSE_Mailbox_Data[mailbox] = 0;
      NUSE_Mailbox_Status[mailbox] = 0;
      #if NUSE_BLOCKING_ENABLE
         NUSE_Mailbox_Blocking_Count[mailbox] = 0;
      #endif
}

Drag the corner of the box to expand as needed. ↑

The mailbox’s data store (NUSE_Mailbox_Data[mailbox]) is set to zero and its status (NUSE_Mailbox_Status[mailbox]) is set to “unused” (i.e. zero).

If task blocking is enabled, the mailbox’s blocked task counter (NUSE_Mailbox_Blocking_Count[mailbox]) is set to zero.

Initializing Queues

Here is the complete code for NUSE_Init_Queue():

void NUSE_Init_Queue(NUSE_QUEUE queue)
{
      NUSE_Queue_Head[queue] = 0;
      NUSE_Queue_Tail[queue] = 0;
      NUSE_Queue_Items[queue] = 0;
      #if NUSE_BLOCKING_ENABLE
         NUSE_Queue_Blocking_Count[queue] = 0;
      #endif
}

Drag the corner of the box to expand as needed. ↑

The queue’s head and tail pointers (actually, they are indexes – NUSE_Queue_Head[queue] and NUSE_Queue_Tail[queue]) are set to point to the start of the queue data area (i.e. given the value zero). The queue’s item counter (NUSE_Queue_Items[queue]) is also set to zero.

If task blocking is enabled, the queue’s blocked task counter (NUSE_Queue_Blocking_Count[queue]) is set to zero.

Initializing Pipes

Here is the complete code for NUSE_Init_Pipe():

void NUSE_Init_Pipe(NUSE_PIPE pipe)
{
      NUSE_Pipe_Head[pipe] = 0;
      NUSE_Pipe_Tail[pipe] = 0;
      NUSE_Pipe_Items[pipe] = 0;
      #if NUSE_BLOCKING_ENABLE
         NUSE_Pipe_Blocking_Count[pipe] = 0;
      #endif
}

Drag the corner of the box to expand as needed. ↑

The pipe’s head and tail pointers (actually, they are indexes – NUSE_Pipe_Head[pipe] and NUSE_Pipe_Tail[pipe]) are set to point to the start of the pipe data area (i.e. given the value zero). The pipe’s item counter (NUSE_Pipe_Items[pipe]) is also set to zero.

If task blocking is enabled, the pipe’s blocked task counter (NUSE_Pipe_Blocking_Count[pipe]) is set to zero.

Initializing Semaphores

Here is the complete code for NUSE_Init_Semaphore():

void NUSE_Init_Semaphore(NUSE_SEMAPHORE semaphore)
{
      NUSE_Semaphore_Counter[semaphore] =
         NUSE_Semaphore_Initial_Value[semaphore];
      #if NUSE_BLOCKING_ENABLE
         NUSE_Semaphore_Blocking_Count[semaphore] = 0;
      #endif
}

Drag the corner of the box to expand as needed. ↑

The semaphore’s counter (NUSE_Semaphore_Counter[semaphore]) is initialized to the user-specified value (NUSE_Semaphore_Initial_Value[semaphore]).

If task blocking is enabled, the semaphore’s blocked task counter (NUSE_Semaphore_Blocking_Count[semaphore]) is set to zero.

Initializing Event Groups

Here is the complete code for NUSE_Init_Event_Group():

void NUSE_Init_Event_Group(NUSE_EVENT_GROUP group)
{
      NUSE_Event_Group_Data[group] = 0;
      #if NUSE_BLOCKING_ENABLE
         NUSE_Event_Group_Blocking_Count[group] = 0;
      #endif
}

Drag the corner of the box to expand as needed. ↑

The event group’s flags are cleared; i.e. NUSE_Event_Group_Data[group] is set to zero.

If task blocking is enabled, the event group’s blocked task counter (NUSE_Event_Group_Blocking_Count[group]) is set to zero.

Initializing Timers

Here is the complete code for NUSE_Init_Timer():

void NUSE_Init_Timer(NUSE_TIMER timer)
{
      NUSE_Timer_Status[timer] = FALSE;
      NUSE_Timer_Value[timer] = NUSE_Timer_Initial_Time[timer];
      NUSE_Timer_Expirations_Counter[timer] = 0;
}

Drag the corner of the box to expand as needed. ↑

The timer’s status (NUSE_Timer_Status[timer]) is set to “unused”; i.e. FALSE.

Its count-down value (NUSE_Timer_Value[timer]) is initialized to the user-specified value (NUSE_Timer_Initial_Time[timer]).

Its expiration counter (NUSE_Timer_Expirations_Counter[timer]) is set to zero.

Application Code Initialization

Once the Nucleus SE data structures have been initialized, there is the opportunity for code to be executed that performs initialization of the application prior to task execution. There are many possible uses for this capability:

  • Initialization of application data structures. Explicit assignments are easier to understand and debug than allowing automatic initialization of static variables.

  • Kernel object assignments. Given that all kernel objects are created statically at build time and are identified by index values, it may be useful to assign “ownership” or define the usage of these objects. This might be done using #define symbols, but, if there are multiple instances of tasks, the object indexes may be best assigned through global arrays (indexed by the task’s ID).

  • Device initialization. This may be a good opportunity to set up any peripheral devices.

Obviously, many of these things could have been achieved before Nucleus SE initialization has been performed, but the advantage of locating the application initialization code here is that kernel services (API calls) may now be employed. For example, a queue or a mailbox may be pre-loaded with data to be processed when a task starts.

There is a limitation on which API calls are permitted: no action may be taken that would normally result in invocation of the scheduler – e.g. task suspension/blocking. The global variable NUSE_Task_State has been set to NUSE_STARTUP_CONTEXT to reflect this restriction.

Starting the Scheduler

Once initialization has been completed, it only remains to start the scheduler in order to commence execution of the application code – the tasks. The scheduler options and operation of the various types of scheduler were covered in detail in an earlier article, so only a brief summery is required here.

The key points in the sequence are:

  • Set the global variable NUSE_Task_State to NUSE_TASK_CONTEXT.

  • Select the index of the first task to be run. If support for initial task state is enabled, a search is performed for the first ready task; otherwise the value 0 is used.

  • The scheduler – NUSE_Scheduler() – is called.

Exactly what occurs in the last step depends on which scheduler type has been selected. For Run to Completion, the scheduling loop is entered and tasks are called in sequence. For other scheduler types, the context for the first task is loaded and control passed to the task.

The next article in this series will take a look at diagnostics and error checking.


Colin Walls has nearly forty years’ experience in the electronics industry, largely dedicated to embedded software. A frequent presenter at conferences and seminars and author of numerous technical articles and two books on embedded software, Colin is an embedded software technologist with Mentor, a Siemens business, and is based in the UK. His regular blog is located at: http://blogs.mentor.com/colinwalls. He may be reached by email at colin_walls@mentor.com

< Previous
Page 2 of 2
Next >

Loading comments...