CS452 - Real-Time Programming - Winter 2018

Lecture 4 - Tasks & Kernels

Public Service Annoucements

  1. Marking of a0.
  2. Due date for kernel 1: 26 January, 2018
  3. Change to combination for the lab.

A few interesting things about RedBoot

What's in the flash ROM of the board is a partial implementation of Redboot. Redboot is a product of the ecos project, initiated in the distant past by Cygnus to create a set of components from which a real-time OS could be instantiated. The boot loader parts became RedBoot when Red Hat was acquired by Cygnus. What Technologic calls RedBoot is a collection of components that they put together that offers a particular set of operations at the RedBoot prompt. The "useful" ones are

You can, for example, send a byte through a UART by reading the status register, checking the ready bit, then writing the data register.

The program you wrote for a0 puts its local variables on a stack?

This enables you to get a clean return to RedBoot when your program terminates.

When your program crashes it's because an exception has been generated because of, for example,

Redboot catches the exception, and puts out some information onto which gcc might hook. (ecos and RedHat are strongly connected to the GNU project. (Don't bother trying to use gdb!) You see the information on the bottom two lines of the terminal window. It's useless except for one 8 digit number starting 00218 or 00219. This is the address of the last instruction the CPU attempted to execute. Using the load map and the assembly code you can find exactly how far the program got before crashing. Unfortunately, the error may have occurred long before execution stopped. The amount a program runs after the error occurred increases as the term goes on.


Kernel of a Real-time Operating System

Introduction

The base unit of a polling loop is

      if ( condition ) action;
    
We generalize this structure only a little in constructing the kernel of our operating system: the kernel determines when the condition is true, then readies the task that executes the action, which terminates by informing the kernel of the condition for its next execution.

It cannot be emphasized too strongly that the kernel may -- but is NOT guaranteed to -- run a task immediately after it is made ready. The kernel's task handling code must be correct whether the task is run immediately or only after other tasks have run. The bugs created when this is wrong are neither subtle nor easy to repair. They should de designed out.

In the next few weeks we will build, from scratch, a micro-kernel. The micro-kernel supervises the execution of tasks, which are the elements of our systems. A task consists of

  1. a set of instructions that it executes,
  2. memory in which it stores data, and
  3. state, such as processor state, kernel state, and so on.

The kernel provides support for a running task (the active task), allowing it to

  1. create a new task,
  2. send a message to another task,
  3. receive a message from another task, and
  4. wait for an interrupt to occur.

These basic needs are provided by the kernel of every operating system. The kernel we create in cs452 is a microkernel, because it provides these capabilities and nothing more.

We build the microkernel in four assignments

  1. task creation and scheduling
  2. inter-task communication
  3. the interrupt primitive
  4. complex servers


Microkernels

The real-time operating system we build consists of

  1. an uninterruptible microkernel, plus
  2. interruptible device-handling server tasks that run in user-space.

What Does a Microkernel Provide?

Tasks

A program is conceived as a collection of co-operating tasks. Tasks provide applications with modularity. Task structure as a method of program organization will be discussed about the time you are finishing the OS.

A task comprises

Every task instance has its own local state. Two tasks can have the same set of instructions but no two tasks share local state.

Why are tasks important?

The programmer can then separate reasoning about his or her program into reasoning about single-threaded task execution (easy, provided that ...) and inter-task communication (easy, provided that ...).

Communication

Communication has two aspects

  1. sharing information and
  2. synchronization.

We use Send/Receive/Reply (SRR) to do both.

  1. Send blocks
  2. Receive blocks: synchronizes with the call to send
  3. Reply doesn't block: synchronizes its return with the return from send.

Sharing data

Data is usually shared in the form of a struct, which requires a common name space of types, but removes parsing and formating. The kernel copies the data from the memory of one task to the memory of another.

Two transfers of data occur between the call to send and the return from it. (The idea is that Send requests something without which it cannot continue computing.) Send does transfers both ways at once. Receive and Reply both have one transfer of data between the call and the return.

Synchronization

  1. Between tasks
  2. With internal events
  3. With external events

Interrupts

In the polling loop you polled a register

Almost every test failed, which wasted CPU. (Exercise for the reader. For each condition/action pair in a0, what proportion of the condition tests evaluated to false?) Even worse, it increases the response time, the time from when the event occurs until the first instruction of user code responding to it occurs.

You can get rid of the polling tests if you program the hardware to send the CPU a signal when the condition for which you are waiting occurs. Such a signal is called an exception and the CPU goes into a privileged state when the interrupt occurs.

The kernel responds to the exception by reading volatile data and readying the task that is waiting on the interrupt. This task is essentially the action that was conditioned on the event in the polling loop.


Tasks

The first part of the kernel requires you to create, schedule and run a collection of tasks. What is a task?

  1. A set of instructions
  2. The current state, which is changed by executing instructions. The state of a task includes which includes When a task enters the kernel its state must be saved completely, so that when it begins executing again, the kernel can restore its state exactly. The analogy to how a function is called and returned from is important to understand.

Two tasks can use the same set of instructions, but every task has its own local state, which is maintained on its private stack. (To provide each task with a private stack the kernel must split up memory among tasks.) There is some memory that is shared among tasks with the same set of instructions.

Static variables may be useful but they are dangerous. Good programming practice says that they should be written once and after that only read.

The kernel keeps track of every task's state

The TD normally contains

  1. The task's stack pointer, which points to a private stack, in the memory of the task, containing all ready to be reloaded whenever the task next runs.
  2. Possibly the return value for when the task is next activated
  3. The task's parent
  4. The task's state
  5. Links to queues on which the task is located

Possible states of the task

  1. Active: If the system is in user space, the active task is in the CPU, running. If the system is in the kernel the active task has either just finished running or is about to run. You are in control of when it changes from one to the other.
  2. Ready: Will be made act
  3. Blocked: waiting for something to happen
The single blocked, zombie, state will differentiate into several different blocked states as the kernel develops.


Kernel Structure

The kernel is just a function (with no arguments and no return value like a task), and which runs forever. It runs forever because like most real programs it terminates when the user decides it should terminate.

      void main( ) {
        initialize( );  // includes starting the first user task
        FOREVER {
  	active = schedule( );
  	request = activate( active );
  	handle( request );
        }
      }
    

Where is the OS?

Where are the user tasks?

What's inside activate( active )?

  1. transfer of control to the active task
  2. execution to completion of the active task
  3. transfer of control back to the kernel
  4. getting the request

The hard part to get right is "transfer of control" which we call a context switch.


The Hardware/Software Provided in the Undergraduate Environment

TS-7200

Specific documentation from Technologic

System on Chip (SoC)

EP9302, designed and manufactured by Cirrus semiconductor

Memory

Byte addressable, word size 32 bits

Hardware configuration created by RedBoot

Separate instruction and data caches

`COM' ports

Connected to two UARTs implemented on the SoC

Reset switch

The reset switch may be red or black, even though documentation says black.

EP-9302

Specific documentation from Cirrus

System on chip

ARM 920T core

Specific documentation from ARM, which covers


Return to: