3.6 Module and Task Interfaces

Modules and tasks interface with each other using two types of interchange, data and control. Data is the information to be transmitted or received on an interface. This can include the actual payload packets like packets to be switched, control packets like STP packets, OSPF packets, and management packets like SNMP packets. Control relates to passing control or status information between modules. Control information is totally internal to the communications system-these frames are not transmitted out on the interface.

There are two common schemes of information interchange between tasks/modules:

  • Functional or procedural interfaces

  • Messaging or event interfaces

3.6.1 Functional/Procedural Interfaces

Procedures or functions are used when two modules are in the same task, or in the same memory context. Consider the Layer 2 switch in which the switching task calls a driver function SendFrame() to transmit a packet. The driver provides this routine as part of a driver interface library so that the higher layer can use it to request the driver to transmit a packet on the Ethernet interface. SendFrame() will execute the steps for queuing the packet to the controller, setting up the transmit registers of the controller, and so on.

The function returns a value indicating the status of the operation. If the function returns immediately after queuing the frame to the controller, the calling function may not know the status of the transmission. Queuing the frames does not mean the transmission was completed. If the function waits for the transmission to complete, the switching task will be held up by the controller transmission status. The function cannot return until the transmission is complete. The call is known as a synchronous call, since the function does not return or blocks until the complete action is performed.

A blocking API is not a desirable way of implementing the driver, since transmission by the controller can be delayed based on the physical interface, and cause the switching task to be held up. Ethernet's CSMA/CD protocol requires that the transmitter wait for a random period of time if it detects a collision before attempting to retransmit a frame. If the switching task blocks due to this condition, it can delay all other processing.

The alternative is an implementation in which the SendFrame() function returns immediately once the queuing has been done. The status of the transmission is not returned, as it is not complete. The switching task can poll the driver at a later time to find the status. If the transmission has been successful, the buffers can be released via the same polling routine. In fact, the routine is implemented as a driver library and called by the switching task.

The alternative to polling is an interrupt from the driver, but, as discussed earlier, this has its own set of issues. One way to overcome this is with a callback function. In this design, the switching task provides a function reference to the driver when it registers itself (see Figure 3.6). The driver calls this function when it receives a transmit completion interrupt from the driver. This is the asynchronous mode of operation, with the callback routine indication providing the final status of the transmit operation.

click to expand
Figure 3.6: Callback function.

The callback routine, while implemented in the switching task, is called from the Interrupt Service Routine (ISR) by the driver. Though this routine is 'owned' by the switching task, it is always called by the driver-a subtle distinction. The switching task's data is visible only inside this routine.

A key requirement of interrupt handlers is that they complete their work and return quickly. Since callback routines can be called from an interrupt handler, it is best to keep the callback routine very short. To fulfill this requirement, the callback routine sends an event or notification to the switching task and exits immediately.

The main loop of any task implemented in the communications system waits on events like message reception, timer expiration, inter-task communication, and so on. Each event is identified by its type and additional parameters. On event reception, the task will classify and process the events, as shown in Listing 3.2.

Listing 3.2: Main task loop.

start example
      Task Main Loop:           Do Forever {                  /* Hard Wait - call will block if there are no events */                   Wait on Specific Events;                    /* If we are here, one or more events have occurred */                    Process Events;           }
end example

The callback routine event is another type of event, and its parameters are specified by the callback routine when called from the ISR. Instead of one event type per callback function, there is just one callback event, with parameters specifying the type and related information. A callback event with a type indicating transmission completion may result in a list of buffers being released.

This model of notification of the task via events is very useful. It can also be applied to the second method of information interchange, which is the messaging/event interface

3.6.2 Messaging/Event Interfaces

This interface is useful when two tasks in separate memory areas need to communicate. A source task communicates with the destination task by constructing a message and sending it to the destination task. The sending is done with a library call, typically implemented in the operating system or a message library. The message can contain data or control information. The message library copies the data from the source task context into a common area and notifies the destination task-which, in turn, copies the message back into its own memory context.

Even if the two tasks are in the same memory area, this is a useful approach. One way of implementing messaging is with a task-based message queue (see Figure 3.7). Each task has its own message queue and can read and write messages to its own queue, while other tasks are only allowed to write to the queue. In Figure 3.7, a routine in Task 1 makes an interface call to pass some information to Task 2. This call translates into queuing of a message to Task 2. While the module which calls this routine sees only a procedural interface, the implementation of the routine results in a message being sent to Task 2. This is a common technique used in communications software.

click to expand
Figure 3.7: Implementing messaging with a message queue.

The nature of the interface (procedural or messaging) is hidden from the calling routine via an API. Only the implementation of the API will need to use the appropriate interface.

The messaging interface scheme fits in very well with both distributed and multi- board architectures. For example, a switch may be implemented with control tasks such as STP and GVRP on a control card, while the forwarding or switching is done on a line card. The same code from a multi-task environment can be used in a multi-board environment as long as messaging is used between the tasks or if the API is based on messaging.

Some RTOS vendors base their operating systems on message passing. A case in point is the OSE™ real-time operating system, which implements message passing as the only IPC mechanism. The OS also provides a software component called the Link Handler, which helps build distributed systems using messages. Other vendors provide messaging as one of several IPC mechanisms, allowing application engineers to make a choice depending upon their application.

An event can be considered a special type of message-it is sent from one task to another and can include a small amount of additional information which is used by the task. An event can also be an effect caused by message enqueuing or timer expiration. In this context, the main loop is modified to look like Listing 3.3.

Listing 3.3: Event processing in the main loop.

start example
While (1)          Wait for any of the events ( …..0           /* break out of the hard wait loop */           if (messageQueuing event)                 Process MessageQueue           if (timer event)                Process TimerEvents           if (callBack event)                Process CallBackEvents; /* based on type + params */           Perform Housekeeping functions;  /* Releasing buffers.. */      }
end example

3.6.3 Standard versus Proprietary Interfaces

Standard operating system calls can be used for Inter-Process Communication (IPC). IPC mechanisms may differ slightly between operating systems, but standards such as POSIX attempt to correct this problem. Software using POSIX-compliant API calls is generally portable across multiple operating systems. This is true with embedded real-time operating systems as well. General systems calls that originated in the UNIX world such as the socket API, are very popular in the embedded world.

Much of the original TCP/IP networking code came from the 4.2 UNIX BSD (Berkeley Systems Distribution) from the University of California, Berkeley. The socket library and API, in fact, was a 4.2 BSD creation. So applications like telnet and ftp, which came with the 4.2 BSD code, used this API. Some RTOSes used the BSD networking code baseline for their networking stack implementation. This helped them to get to market faster with proven code, thus increasing the prevalence of standard API calls in embedded systems. Also, software engineers moving into embedded software from the desktop communications world found this to be a familiar environment, increasing its use.

The advantage of standard API calls is that the code need not change when moving to another operating system providing the same APIs. A key factor in embedded communications systems is the portability of protocols as an application migrates from one platform to another. As long as the new OS supports the same set of system APIs, the system interface does not change.

We use the term proprietary interfaces to imply those interfaces which are specific to the system and which do not use standard interfaces such as POSIX. Proprietary is not necessarily a problem. Sometimes, developers do not want to use the standard system calls due to the performance impact. For example, due to the 4.2 BSD legacy, a send call in the socket API results in a copy into the implementation's buffers, even in those RTOSes where there is no concept of user and kernel spaces. Any data copy results in a performance hit and should be avoided.

Code Maintainability

When designing communications software, tradeoffs between performance and maintainability are weighed. However, on the first attempt, engineers should focus on code maintainability and functionality with some overall performance goals. Avoiding copying of messages, reducing the amount of time spent in interrupt handlers, or larger polling intervals, are some of the techniques used to increase performance. After these are followed, engineers should use optimizing compilers as much as possible before attempting to optimize specific sections of their code.

Code tends to survive long after a project has ended. Therefore, it is very important that code be standard and maintainable so that changes can be made easily during migrations to new hardware platforms. The maintainability helps successive generations of engineers understand how the application was designed.



Designing Embedded Communications Software
Designing Embedded Communications Software
ISBN: 157820125X
EAN: 2147483647
Year: 2003
Pages: 126
Authors: T. Sridhar

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net