C to C++ Migration for Embedded Systems - PowerPoint PPT Presentation

1 / 28
About This Presentation
Title:

C to C++ Migration for Embedded Systems

Description:

C to C++ Migration for Embedded Systems A Step by Step tutorial with Pro s and Con s by Dirk Braun Contents General considerations What is OO? – PowerPoint PPT presentation

Number of Views:53
Avg rating:3.0/5.0
Slides: 29
Provided by: cleversof
Category:

less

Transcript and Presenter's Notes

Title: C to C++ Migration for Embedded Systems


1
C to C Migration for Embedded Systems
  • A Step by Step tutorial with Pros and Cons

by Dirk Braun
2
Contents
  • General considerations
  • What is OO? (very briefly, very quick)
  • Why is OO in embedded systems a special subject?
  • Tutorial Convert an example C module into a C
    class in 6 simple steps
  • Measurements comparison of code-size and speed

Not dealt with Inheritance and advanced OO.
3
General Considerations
  • What is meant by Object Orientation?

Code is in classes, Objects are instances of
classes. Analogy Alarm clock. The
generalization of the alarm clock is the
class. Real alarm clocks are instances of the
idea (the class). These would be called the
objects or class instances. Uses for
SW-projects Code for all objects of the same
class can be identical and exist only
once. Benefit save code size from the second
instance The instances differ by their states.
I.e. Each object has its own private copy of
memory that completely describes its
state. Benefit object instances are
independant of each other objects can be
created at run time
4
General Considerations
  • Why is OO in embedded systems a special subject?
  • Because assumption about available memory differ.

Embedded Reality
OO
Dynamic object- creation at runtime (usually)
  • Limited memory
  • Coding standards
  • Because many developers havent learned it during
    their professional training. OO trained from mid
    90s, many developers learned electronics rather
    than computing science. Combined studies emerging
    now.

5
General Considerations
Why is OO linked to dynamic memory allocation?
Compile Time
Run-Time
Object 1 with var
Class Code Data Structure
Object 2 with var
Object 3 with var
  • Dynamic memory allocation has to be treated with
    care because
  • We cannot easily test if the physically available
    memory will survive the worst case.
  • Memory can get fragmented and thereby seem to get
    used up.
  • Programmed Memory holes are not very easy to
    detect and may appear only after the system has
    been running for days or weeks.
  • When using object oriented programming
  • Each object has its own variable (of class
    type).
  • When the number of required objects is unknown
    at compile-time, the objects have to be
    instantiated at run-time and allocated
    dyamically.
  • With limited RAM I avoid permament dynamic
    object creation (i.e. Dynamic memory allocation)
    at run-time but I think its OK during startup or
    reconfiguration.

6
Tutorial Convert a C-Timer Module to C in 6
simple steps
  • 0. Get to know the initial C-Project
  • Switch from C to C compiler (no change of
    source code)
  • A simple class with Constructor and
    Initialization functions. What is the
    this-pointer? How to do static object memory
    allocation.
  • Convert all functions (except the ISR) to class
    member functions.
  • Convert the Interrupt-Service-Function to a class
    member-function.
  • Turn all global module-variables into protected
    member variables.
  • Create multiple class-instances that represent
    separate HW-entities.

7
Step 0 The initial C-Project
  • Standard on Chip HW-Timer
  • Provides SW-Timers for other modules
  • Wake up at intervals, count ticks, check if any
    SW-timer needs being called.
  • Similar to the work of a cyclic task scheduler
  • Additional features time base readjusts itself
    automatically to the greatest possible
    granularity to satisfy all SW timer cycles.

8
Working-Principle of Timer Module
App Module A
Timer Module
InitA
1
Timer Registration
TimerCallbackA
a
HW-Timer Event
Timer ISR works like a scheduler
x
2
App Module B
InitB
b
TimerCallbackB
  • SW-Timers register their on cyclic functions (1
    2)
  • Timer-Interrupts get counted (x)
  • SW-Timer-Callbacks get called at the right times
    (a b)

9
Use debug the Timer Demo Project
1. Initialize Register SW-Timer-callbacks
2. Set breakpoints inside the callbacks.
int main (void) ... TimerInit() // erster
TestTimer TimerCreate(25, On1stTimer3)
TimerCreate(30, On2ndTimer3) TimerCreate(35,
On3rdTimer3) ... while (TRUE) / Loop
forever /
3. Observe the times at which the breakpoints are
approached (watch the timing repoted by the
simulator).
10
Step 1 Switch from C to C compiler
  • Rename timer.c to timer.cpp. This causes the
    µVision IDE to use the c compiler.
  • Errors show stricter type checking -gt do type
    casts
  • New linker errors Undefined symbol TimerCreate
  • The reason for this is name decoration that is
    used in C. Further examination on next page.

11
Decorated Names
  • Searching object files reveals hello.o wants
    TimerCreate but timer.o exports
  • _Z11TimerCreatejPFvvE
  • _Z11TimerDeleteiPFvvE
  • _Z15TimerIntDisablev
  • _Z11TimerTx_ISRv
  • _Z10TimerStartv
  • _Z9TimerInitv
  • int TimerCreate(WORD,TimerCallbackPtr)
  • BOOL TimerDelete(int, TimerCallbackPtr)
  • void TimerIntDisable(void)
  • void TimerTx_ISR (void) __irq
  • void TimerStart(void)
  • void TimerInit(void)

Reason for name decoration C permits identical
function names that differ only by their
parameter lists. Name decoration codes the
parameter lists into the exported function
names. Different compilers very like decorate in
different ways. So using a C library compiled
wih a different compiler very likely wont
work. -gt To us this means all code module that
use C functions have to be compiled with a C
Compiler, too. -gt Rename hello.c to hello.cpp
12
Call C-functions from CPP works
  • Now the linker reports undefinded symbols
    ienable and idisable. These functions are
    implemented in a module (utilities) that is still
    compiled by the C compiler (and shall remain so).
  • The C compiler assumed these are also C
    functions and hence tells the linker to look for
    decorated function names. We have to tell the C
    compiler that these are C functions by use of
    extern C.

At the beginning and end of utilities.h insert
the statements shown on the right. These
conditionals ensure that the same file can freely
be included by C and C modules.
ifdef __cplusplus extern "C" endif ... ifdef
__cplusplus // extern "C" endif
Finally the project should compile. Debug and
check that it still works.
13
Step 2 Create a simple class classes,
code-reuse, object allocation
From Step 2 to step 6 use mixture of C and C
using only a single instance (object) of the new
timer class. Add a very simple class to the
class.
14
Step 2 Create a simple class classes,
code-reuse, object allocation
1. Add a class declaration to the header
class Timer protected public Timer(BYTE
timerNo) void Init()
void TimerInit() becomes TimerTimer(BYTE
timerNo) Add new (empty function) function void
TimerInit()
2. Change implementation in timer.cpp The static
initialization creates the timer before main gets
called. The intialization (for static object
instantiation) is split into two parts. The
constructor that gets called automatically and
the self-made Init-function.
Timer myTimer Timer(0)
3. Declare a timer object statically in hello.c
(similar to a global variable). To avoid dynamic
memory allocation I used a static declaration.
Space for the class gets reserved at compile time.
4. Compile and check for errors.
15
Step 3 Convert all Functions to class methods
(except ISRs)
  • class Timer
  • protected
  • WORD Gcd(WORD a, WORD b)
  • void IntEnable(void)
  • void IntDisable(void)
  • void Start()
  • void Stop()
  • void CalculateInternals(void)
  • public
  • Timer(BYTE timerNo)
  • void Init()
  • int Create(WORD ms_interval,
  • TimerCallbackPtr pCallback)
  • BOOL Delete(int timerNo,
  • TimerCallbackPtr pCallback)
  • Turn all forward declared functions (used in .c
    file only) into protected member functions
    (except ISR).
  • Turn all the remaining publicly declared
    functions (those in the header) into public
    members.
  • Change the implementation of these functions by
    prefixing the functions with Timer.
  • Change all calls to these functions from outside
    of the class (i.e. in hello.c) to class-method
    calls.
  • Compile and check for errors.

void TimerIntEnable(void) becomes void
TimerIntEnable(void)
TimerCreate(25, On1stTimer3)
becomes myTimer.Create(25, On1stTimer3)
16
Step 3 Convert all Functions to class methods
(except ISRs)
Detecting the this pointer
  • We have to get a better understanding of what
    happens in the background.
  • Set a breakpoint at the first call to
    myTimer.Create and let the debugger run up to
    there.
  • Open the disassembly view.
  • See how 3 parameters (R0, R1 R2) in the call to
    TimerCreate().
  • Close the disassembly view and step into the
    function.
  • Add two variables myTimer (the address of a
    global variable) and this to the watch window.
    The addresses conicide (also with the content of
    R0 used for parameter passing into the
    function).

17
Step 3 Convert all Functions to class methods
(except ISRs)
The purpose of the this pointer
As mentioned earlier OO is about code reuse. Code
exists only once, but we can have many instances
of objects. Every method call gets a hidden
first parameter a pointer to the object the
method shall be applied to. The object then is a
variable containing the objects state. In our
case at this stage of conversion the class
does not have any properties (i.e. variables)
yet, and hence this points to en empty
structure. This knowledge is important in order
to understand the next step.
18
Step 4 Turn the ISR into a class member
We just learned that every class method receives
a hidden this pointer. So if we want to change
the Interrupt Service Routine (ISR) into a class
method were going to run into trouble. An ISR is
just an interrupt vector. The interrupt-controller
wont be kind enough to provide a suitable this
pointer. In C a method can be static. This
simply means that the function doesnt receive a
this pointer and will not be able to know which
object its working on. For now were dealing
with one object only, so we should get along.
Lets just convert it and see how far we get.
19
Step 4 Turn the ISR into a class member
  • Remove the forward declaration of the ISR in
    timer.cpp
  • In timer.h declare
  • static void Tx_ISR (void) __irq
  • in the protected section of the class
    declaration.
  • 3. Change the function name in the .cpp file
    accordingly
  • void TimerTx_ISR (void) __irq
  • Then adjust the interrupt vector to point to the
    new function
  • SET_VIC_VECT_ADDR(TIMER_ILVL, Tx_ISR)
  • in the constructor.
  • 5. Compiler, Debug, Check.
  • Dont worry if you do not quite understand why
    this works. Step 5 makes it clear.

20
Step 5 The objects get a state global variable
become protected members
  • Cut paste all globally declared variables in
    timer.c to the protected section in timer.h.
  • Try to compile.
  • Get loads of errors, but all from inside the ISR
    !?
  • In the previous step the ISR refered to global
    variables. These have just been moved into the
    class (or in C-talk into a structure). All the
    other class functions reference into this
    structure by use of the hidden this pointer. The
    ISR doesnt have one.

21
Step 5 The objects get a state global variable
become protected members
Idea how to provide a this pointer for the
static ISR
  • Timer pTimer0 // somehow globally stored
  • void TimerT0_ISR (void) __irq
  • pTimer0-gtTx_ISR()
  • void TimerTx_ISR (void)
  • ... // as before

The ISR uses a global pointer to call into a
method of a real object instance. This method
contains the original ISR code.
22
Step 5 The objects get a state global variable
become protected members
Use a global class-pointer to call back into the
class
  • Declare a global pointer variable pTimer0 of
    type Timer
  • In the constructor save the this pointer to the
    global pointer.
  • Create a new protected method (Tx_ISR) that does
    what the ISR did so far.
  • From the real static ISR call the new Tx_Isr with
    the new global class-pointer.
  • Compile, debug and check for errors.
  • Set a breakpoint in T0_ISR and see how the code
    calls back into the class.
  • Timer pTimer0
  • TimerTimer(BYTE timerNo)
  • ...
  • switch (timerNo)
  • case 0
  • pTimer0 this
  • break
  • default
  • // todo show error
  • break
  • ...
  • void TimerT0_ISR (void) __irq
  • pTimer0-gtTx_ISR()

23
Step 6 - Multiple Object Instances
  • An ordinary class would be finished now and could
    be instantiated many times.
  • This class is special because it is linked to
    HW-resources, namely Timer-Peripherals. As a
    consequence each object instance needs its own
  • set of pointers to registers
  • Interrupt Service Routine

protected ... WORD m_timerChannel
volatile unsigned long m_pTxMR0 volatile
unsigned long m_pTxTCR volatile unsigned
long m_pTxIR ... static void T1_ISR (void)
__irq
  • Create pointer vars to timer registers
  • Create a second ISR for Timer 1

24
Step 6 - Multiple Object Instances
TimerTimer(BYTE timerNo) ... switch
(timerNo) case 0 pTimer0 this
T0MCR 3 // Interrupt and Reset on MR0
T0TCR 0 // Timer0 Enable m_pTxMR0
T0MR0 // set pointers to SFRs m_pTxTCR
T0TCR m_pTxIR T0IR m_timerChannel
TIMER0_CHANNEL SET_VIC_VECT_ADDR(TIMER0_ILVL,
T0_ISR) SET_VIC_VECT_CNTL(TIMER0_ILVL,
m_timerChannel) break case 1 pTimer1
this T1MCR 3 // Interrupt and Reset on
MR1 T1TCR 0 // Timer1 Enable m_pTxMR0
T1MR0 // set pointers to SFRs m_pTxTCR
T1TCR m_pTxIR T1IR m_timerChannel
TIMER1_CHANNEL SET_VIC_VECT_ADDR(TIMER1_ILVL,
T1_ISR) SET_VIC_VECT_CNTL(TIMER1_ILVL,
m_timerChannel) break ...
  • In timer.cpp create global pointer to timer 1.
  • Store global object pointer to timer 1 in
    constructor.
  • Store register pointers in constructor.

Timer pTimer1
25
Step 6 - Multiple Object Instances
  • Access registers via these pointers. (Changes
    shown for one example)
  • Create ISR for Timer1 peripheral.
  • Instantiate use the second HW-timer object in
    hello.c (note no of HW-Timer passed in
    constructor)
  • Compile and check for errors.

void TimerStop() m_pTxTCR 0 // Timer X
Disable void TimerT1_ISR (void) __irq
pTimer1-gtTx_ISR()
Timer myOtherTimer Timer(1) // static
allocation of timer object int main (void)
... myOtherTimer.Init() ... myOtherTimer.
Create(35, On3rdTimer3) // cycle time 35
ms ...
26
Measurements
Comparison of two systems A code of step 0
(C-only) B code of step 0 duplicated
(C-only) C equal to step 6 (OO)
  • Interpreting measurements
  • Code size
  • C much less than B. Expected.
  • C slightly more than A. May be assigned to added
    indirection and storing of pointers etc.
  • RAM
  • B and C very similar. Expected.
  • Performance
  • C greater than A and B. Expected. Attributed to
    indirection and additional function call.

27
Summary
  • Pros
  • Smaller Code for more than one object
  • Automatic code maintenance as code exists only
    once.
  • Cons
  • Small performance overhead
  • Automatic code maintenance as code exists only
    once.
  • Uses
  • Bus couplers
  • Time synchronous machines e.g. multi axis
    control

Contact Dirk Braun Email dbraun_at_cleversoftware.de
28
Thanksfor yourattention!
Write a Comment
User Comments (0)
About PowerShow.com