Channels ▼
RSS

Design

Porting an RTOS to a New Hardware Platform

Source Code Accompanies This Article. Download It Now.


Byron is an independent firmware developer specializing in microprocessor and DSP design and development for data acquisition, control, and Internet appliances. He can be reached at bmiller2@isd.net.


Product development cycles are market driven, and market demands often require vendors to compress development schedules. One approach to this is to simultaneously develop similar products, yet with varying levels of product complexity. However, scheduling pressures coupled with increased product complexity can be a recipe for disaster, resulting in slipped schedules and missed opportunities. Consequently, vendors are always on the alert for silver bullets, yet as developers, we know that they don't exist. That said, it is still in our best interest to seek better ways of compressing development cycles, and one way to do this is to port existing products to new hardware platforms, adding new features along the way. This is the approach we used to demonstrate a proof-in-concept when porting a legacy security application to a new hardware platform.

Our firm was hired to make enhancements to the client's existing 6502-based product, and we quickly realized that this platform was running out of steam. Specifically, the proposed features would significantly impact performance. Consequently, we proposed three options for fixing this problem:

  • Completely rewriting the application on the current hardware.
  • Rewriting the application on a new, higher performance hardware.
  • Migrating portable portions of the application to the new hardware.

After considering the options, we decided to port to new hardware.

Overview

Like most firmware projects, this project consisted of software and hardware, specifically the Quadros System's RTXC Real-Time Operating System (http://www.quadros.com/) and test code that we ported to NorthPole Engineering's NPE-167 Single Board Computer (http://www.npe-inc.com/), which is based on the Infineon C167 processor.

The C167 supports a 16-channel, 10-bit A/D converter, two 16-channel capture and compare units, four-channel PWM, two general-purpose timer units, asynchronous/ synchronous serial channel (USART), high-speed synchronous serial channel, watchdog timer, bootstrap loader, 111 I/O lines, 2K internal RAM, 16 priority-level interrupt system, an eight-channel Peripheral Event Controller (PEC), and a Controller Area Network (CAN) 2.0B specification. In its basic configuration, the NPE-167 (see Figure 1) contains both 512K of Flash and SRAM. Memory is expandable to 17 MB of combined Flash and SRAM. It also supports a real-time clock, RS-232 serial port, and modified Serial Peripheral Interface (SPI) bus used to control four I/O cards.

To bring up a real-time operating system (RTOS) on a new board, most vendors provide Board Support Packages (BSPs)—software and documentation that provide guidance on getting the RTOS to execute on new hardware. RTXC's BSP for the C167 comes with the source code for the RTOS kernel, a system-generation utility (RTXCgen) for configuring the number of tasks, timers, and resources used for the specific application. It also includes documentation, some source-code examples, and makefiles for building the kernel and a sample application.

RTXC Overview

The Real-Time eXecutive kernel (RTXC) supports three kinds of priority-based task scheduling: preemptive (the default), round-robin, and time-slice.

RTXC is robust, supports hard deadlines, changeable task priorities, time and resource management, and intertask communication. It also has a small RAM/ROM code footprint, standard API interface, and is implemented in many processors.

RTXC is divided into nine basic components: tasks, mailboxes, messages, queues, semaphores, resources, memory partitions, timers, and Interrupt Service Routines (ISRs). These components are further subdivided into three groups that are used for intertask communication, synchronization, and resource management. Moreover, component functionality is accessed via the standard API interface.

Porting Activities Overview

Porting an RTOS to a new board requires four activities:

  • Determining the system's architecture.
  • Figuring out what files to change based on the architecture.
  • Making changes to the files; this includes writing the code.
  • Creating test code and exercising the board to ensure that the RTOS is working properly.

The first activity is design related, while the others are implementation related. Moreover, the last three activities require an understanding of the new hardware—knowing the specifics of what needs to happen to make the RTOS interact with the board.

System Architecture

The purpose of determining the system architecture requirements is to identify the hardware and software components that need modifying to get the RTOS up and running on the NPE-167 board.

For most porting projects, hardware components include I/O, memory, timers, and other unique peripherals. For this project, these components are no different. We had the I/O ports that control the LEDs, CAN bus, serial communication, memory selection, and card-slot selection. Memory had both Flash and SRAM. Memory is selected through the I/O component using the SPI bus. Therefore, I/O and memory selection are interrelated. For this project, we also had to identify the timer to run RTXC's real-time clock. The real-time clock is the master timer used for all RTOS-based time-keeping functions. Additionally, for this project, we were not going to use any other on-chip peripherals.

The best way to identify hardware components is to study the board's schematics. Examining the NPE-167 board revealed that the I/O ports would be key for this project. Why? Because this board used the processor's general-purpose ports to handle switches to control CAN bus operation, the board's operating mode, control LED outputs, and memory selection. I/O cards were controlled via the SPI bus, rather than I/O ports.

Ports can be configured as either inputs or outputs. Examination of the NPE-167 board showed that 17 ports are used. Eleven ports are used as switch inputs. From the schematic we saw that switches 1-7 were used to set the MAC address for the CAN device. CAN bus speed is controlled by switches 8-9, while the board operating mode is controlled by switches 11-12. Switch 10 is not used. Four ports control the LEDs. There are three in total. One LED is green, one red, and the third bicolor. Thus, four outputs are required to control the three LEDs. Finally, two output ports are used as page selection for extended memory.

Referring to the schematic, we saw that the NPE board addresses up to 512K of memory before having to make use of the page-selection ports. Although we would configure the page-selection ports for the porting process, we didn't have to use them because the total code footprint of the kernel, plus test code, is 107K. RTXC's kernel is about 76K, and the porting test code fits within another 31K. In short, we would only use about 1/5 of the default memory to validate the porting process.

The last necessary component for the port was to determine which timer to use as the master time base. Timers are internal on the C167 processor, so they don't show up on the schematic. So we had two options—choose a timer and write the code for that timer, or use the BSP default timer. RTXC's C167 BSP uses a timer in its configuration. A trick to simplify the initial porting process is to use the default timer that the BSP uses. Reviewing the BSP documentation, we discovered that it uses timer 6 for the master timer. Once we determined the components associated with the porting process, we could turn our attention to figuring out which files needed to be changed.

Changing Files

We knew from the previous step that 11 ports were used for input and six ports for output. Because these were general-purpose I/O ports, they needed to be initialized to work as either inputs or outputs. This gave us an idea of where NPE-specific initialization code needed to go—specifically, initialization code to set up these ports goes in the startup code. For this project, initialization code was located in the cstart.a66 file that is located in the Porting directory. Listing One is the code that configures the NPE-167 board I/O. Once configured, I/O can be used by higher level RTOS and API functions. Once we figured out where the I/O changes go, we needed to turn our attention to discovering and setting up the master timer.

BSP set up the master timer for us because we were using default timer 6. Setup code for this timer is located in cstart.a66 and rtxcmain.c. Listing Two is a snippet of the RTXC-specific code. After analyzing the architecture requirements, we discovered that the only file to change for porting the NPE-167 board was cstart.a66. Granted, we knew we would have to change other files as well, but those files are application specific.

Changing Files and Writing Code

This brought us to the third step, which was straightforward because we knew what needed to be changed and where. Recall that all changes for basic porting functionality occurred in cstart.a66. We also needed to write the code for initialization. We wrote code to initialize the switches to handle CAN—but no other code— to deal with it because it is not used in the basic port. For specifics, look at cstart.a66 and search for npe and rtxc labels to find code changes specific to this port. Keep in mind, when porting to new hardware you may want to adopt a similar strategy for partitioning the code for hardware- and RTOS-specific changes. That is because partitioning code through the use of labels helps with code maintainability.

Test Code

Finally, we needed to create some test code to test our port. Building the test code application was a two-step process:

  1. We compiled the RTXC kernel into a library object (rtxc.lib).
  2. We compiled the test code and link in rtxc.lib to create the executable.

There are two directories for generating the test code, and they are stored at the same level in the hierarchy. Moreover, all files for creating rtxc.lib are located in the kernel directory. Alternatively, test code-specific files are located in the Porting directory.

The RTXCgen utility creates a set of files corresponding to each RTOS component. For instance, application queues are defined in three files: cqueue.c, cqueue.h, and cqueue.def. The same holds true for tasks, timers, semaphores, mailboxes, and the rest. Changes to the number of RTOS components are handled by this utility. For example, if we wanted to change the number of tasks used by the test code, we use RTXCgen to do it. Figure 2 shows the contents of the task definition file for the test code application. Test code files created by RTXCgen are placed in the Porting directory. Once RTXCgen has defined the system resources, we are ready to build the project.

Creating the executable test code requires the build of two subprojects—the kernel and test code. We performed builds using the Keil Microvision IDE (http://www.keil.com/). Keil uses project files (*.prj files) to store its build information. RTXC kernel creation consists of building the code using the librtxc.prj file located in the kernel directory. Evoking the librtxc project compiles, links, and creates a librtxc object in the kernel directory. Building the test code is accomplished using the NpeEg.prj file stored in the Porting directory. Invoking the NpeEg project compiles and links files in the Porting directory, and links the librtxc object in the kernel directory. The resulting executable is then placed in the Porting directory as well. Once the test code was fully built, we were ready to test the board port.

The test code is a simple application used to validate the porting process. Most of the test code is located in main.c located in the Porting directory. The application works by starting five tasks—two user and three system. User tasks execute alternatively, while system tasks execute in the background. One user task begins running. It then outputs data via one of the system tasks to the console. Next, it signals the other to wake up, and it puts itself to sleep, thus waiting for the other task to signal it to wake up again. Figure 3 shows the executing test code.

Conclusion

That's pretty much it. Porting software to a new hardware board doesn't need to be hard. With a firm plan (and following this simple process), porting just got a lot easier.

Acknowledgments

Thanks to Joe Tretter and Rick Gibbs at Northpole Engineering for their assistance.

DDJ



Listing One

;  NPE specific code in cstart.a66
;
; NPE-167 Special Function Register Addresses
P2        DEFR      0FFC0H
DP2       DEFR      0FFC2H
ODP2    DEFR        0F1C2H
P7        DEFR      0FFD0H
DP7       DEFR      0FFD2H
ODP7    DEFR        0F1D2H
PICON   DEFR        0F1C4H
CC8IC   DEFR        0FF88H
EXICON  DEFR        0F1C0H
T6CON   DEFR        0FF48H
;----------------------------------------------------------------
;   HARDWARE INITIALIZATION FOR NPE-167 A
;       This code initializes the processor to work with the peripherials
;       on the NPE-167 A.
$IF (WATCHDOG = 1)
        SRVWDT              ; SERVICE WATCHDOG
$ENDIF
        BCLR    T6CON.6     ; Shut off timer.
;       Initialize Ports 2,3,7 and 8 as standard TTL levels.
        MOV     R5,0
        MOV     PICON,R5
;
;       Initialize Port 2.
;           Set Output  = P2.0:         IO Bus Reset
;                       = P2.1, P2.2:   System LED
;                       = P2.3, P2.4:   CAN LED
;                       = P2.6, p2.7:   Memory Page Select
;
;           Set Input   = P2.10, P2.11: CAN Speed Select (SW8, SW9)
;
        MOV     R5,#001Fh   ; Set outputs to off.
        MOV     P2,R5
        MOV     R5,#0001h   ; IO is open drain.
        MOV     ODP2,R5
        MOV     R5,#00DFh   ; Set output direction.
        MOV     DP2,R5
;
;       Initialize Port 7.
;           Set Output  = P7.0 - P7.3:  IO Bus Slot Select.
;
        MOV     R5,#000Fh   ; Set outputs to off.
        MOV     P7,R5
        MOV     R5,#000Fh   ; IO is open drain.
        MOV     ODP7,R5
        MOV     R5,#000Fh   ; Set output direction.
        MOV     DP7,R5
;
;       Setup IO Interrupt (EX0IN).
;           Disable external interrupts and set Interrupt level to 7,
;           group level to 3, negative edge.
;
        MOV     R5,#001Fh
        MOV     CC8IC,R5
        MOV     R5,#0001h
        OR      EXICON,R5
Back to article


Listing Two
;========================================================================
; ** Beginning of RTXC specific code **
$IF MICROVISION
;========================================================================
;                    NULL STACK SIZE DEFINITION
;
; define the size of the stack for the null task.
; NOTE: Ensure you modify the 'C' level constant of the same name in
;       the RTXCOPTS.H file.
;----------------------------------------------------------------
NULLSTKSZ EQU 80H
$ELSE
$INCLUDE(rtxcopts.inc)                  ; include kernel assembly definitions
$ENDIF
; ** END RTXC specific code **
;=========================================================================

;=========================================================================
; ** Beginning of RTXC specific code **
; This user stack is used only for startup and entry into main()

USTSZ   EQU     40H   ; set User Stack Size to 64 Bytes.

; ** END RTXC of RTXC specific code **
;=========================================================================

;=========================================================================
; ** Beginning of RTXC specific code **

   EXTRN nullstack:WORD

   EXTRN DPP3:DPP1_INITVALUE:WORD
   EXTRN DPP3:DPP2_INITVALUE:WORD

   ; Initialize the 'C' variables for task frame initialization
   MOV     DPP1_INITVALUE, DPP1
   MOV     DPP2_INITVALUE, DPP2
$IF NOT TINY
     MOV     R0, #POF nullstack      ; restore user stack pointer
     MOV     R5, #PAG nullstack
     MOV     DPP2,R5
     NOP
     BSET    R0.0FH                         ; User stack uses DPP1
$ELSE
     MOV     R0, #nullstack              ; restore user stack pointer
$ENDIF

   MOV     R10, #NULLSTKSZ
   ADD     R0, R10                           ; get to top of stack

; ** END RTXC of RTXC specific code **
;===============================================================
Back to article

Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video