Callgraph generation using gcc, egypt & graphviz

In this post, we will see how to use gcc, egypt & graphviz to generate call graphs of any C-based source code / multiple source codes. This technique will be helpful if you are working on legacy code and want to have a view of the code architecture at first.



Among the three tools mentioned above, GCC is generally pre-installed in Linux. So You need to install the other two. 


Follow the following :


1. Download Egypt from here egypt download and then unzip it


~/Downloads$ tar -xzvf egypt-1.10.tar.gz
~/Downloads$ sudo mv egypt-1.10 /opt


go to the egypt directory then install it by these commands


/opt/egypt-1.10$ perl Makefile.PL
/opt/egypt-1.10$ make
/opt/egypt-1.10$ sudo make install


2. then install graphViz by


$sudo apt-get install graphviz


Alright then tools are installed So now let's go to the basics 


In order to generate call graph, you basically need the following steps 


1. compile and generate object files using gcc

2. generate RTL from the object files using gcc

3. generate the call graphs using egypt & graphviz 


RTL means Register transfer Language. It is used to describe the data flow at the register transfer level. It is actually a kind of intermediate representation that is very close to assembly language. 


First of all compile and run a simple C program with an additional file 


$mkdir test
$cd test
$notepadqq hello.h
$notepadqq hello.c
$notepadqq main.c


#include <stdio.h>
#include "hello.h"

void main(int argc, char const *argv[])
{
    printf("I am the Main Function\n");
    hello_world();

}

main.c


#ifndef HELLO_H
#define HELLO_H

void hello_world(void);

#endif  

hello.h


#include <stdio.h>
#include "hello.h"

void hello_world(void)
{
    printf("hello world\n");
}

hello.c 


Now let’s compile using GCC 



$gcc -Wall main.c hello.c -o hello
$./hello


Now generate the RTL expands and run egypt command on the output to get a callgrqaph picture 


$gcc -fdump-rtl-expand main.c hello.c
$egypt main.c.192r.expand hello.c.192r.expand | dot -Grankdir=LR -Tsvg -o hello.svg


now open the svg file and you will see the pictorial call graph of the hello application that you made




ARM Cortex-M0 : Introduction

Well we designers come to a point when we want to go to the next level of embedded designing and we start to think about ARM based microcontrollers. So what is ARM ?

Actually ARM is a processor and the processor core is  sold by the company also named ARM to different vendors like STM , Texas Instruments , Atmel and so on . So these vendors do what ?
well instead of re designing the processor these compnaies make microcontrollers around the ARM processor . & we developers choose microcontroller from these vendors and start using it.

So why would all the vendors choose ARM ?

Because ARM ( advanced RISC machine) is a family of reduced instruction set computing architectures and requires less number of transistors than typical complex instructions set computing architecture s (CISC) . This approach reduces cost , power consumption and heat . for example ARM cortex M0 has 12,000 logic gates & even a 32 bit multiplication can be completed in 1 cycle. some of the important features are below:

Features: 
  • Cortex- M processors are extremely C friendly
  • increased performance and efficiency 
  • Low cost
  • extended battery life
  • floating point 
  • DSP capability 
  • Lower operating frequency means : lower active power & lower EMI

More information on  ARM cortex M processor: 
  • Remember ARM cortex M0 is a processor, Cortex M0 is designed as an ideal C target  
  • Cortex M0 has 56 instructions 
  • user can choose devices with smaller flash memory sizes for the the same application 
  • The ARM Cortex-M1 processor is the first ARM processor designed specifically for implementation in FPGAs. 
  • Cortex M0 processor is a 32 bit RISC (reduced instruction set computing) processor with Von neuman Architecture 
  • clock frequency for the cortex M0 processor can be reduced significantly to achieve lower power consumption 
  • Cortex M0 processor has implemented the ARMv6-m architecture 

Some Abbreviations:  

ASSP: Application Specific Standard products
SOC: System on chip
ARM:  originally Acorn RISC Machine , later Advanced RISC Machine
NVIC: Nested Vector Interrupt Controller
MDK: Microcontroller development Kit
AMBA: Advanced Microcontroller BUS architecture
AHB: Advanced High performane BUS
APB: Advanced peripheral Bus


Instruction sets : 

ARM cortex M processors supports 2 types of instructions sets

  • 32 bit instruction set ----------- called ARM instruction set 
  • 16 bit instruction set ----------- called Thumb instruction set 
for example ARM7TDMI supports both types of instruction sets 

Most instructions generated by the C compiler use the 16 bit instructions & 32 bit instructions are used when the 16-bit version cannot carry out the required operations. 



Interrupt types: 

Maskable Interrupt: The interrupts which can be ignored by the processor 

NMI(Non maskable interrupt): the interrupts that can not be ignored by the processor



Cortex M0 block diagram: 


Processor Core elements :

  • Register Banks
  • ALU(arithmetic logic unit)
  • Control Logic 


AHB Lite Bus interface: is a on chip bus protocol used in many ARM processor . it is part of the AMBA ( Advanced Microcontroller BUS architecture) specification . it is 32 bits wide .




  • APB means Advanced Peripheral Bus and you can see peripherals are attched with this BUS 
  • AHB Lite supports 32,16,8 bit transfers
Unlike AHB lite APB does not support different sized transfers So APB should be accessed using word sized transfers 


                                           In ARM Word is 32 bits wide 

Event Driven Embedded Programming

Introduction
I guess most of us as beginners in embedded systems programming in C++, probably start writing firmware with a super loop like architecture where
  • all our statements/function calls reside in the main loop 
  • we start including arduino like libraries 
  • we create global objects & start using the objects in the main loop.  
  • being a bit more mature & in order to manage evolving large code base we bring hierarchical models  to subgroup our code across different classes 
Fundamentally the code executes in a loop but the code is more manageable now.
The problem with the super loop is that it is not a very good approach to attain hard real-time performance and is not suited for the embedded applications where you need hard/soft real-time response. So what could be the solution?

The concept of RTOS
The firmware industry has a long history of being written in C with the RTOS approach, for example, VxWorks , freeRtos are very popular and robust. Obviously, RTOS is better than Super Loop considering real-time operation
RTOS types
RTOS is of two types: cooperative and preemptive
In Co-operative RTOS each task must finish before the time allotted for the completion of the task gets over. Meaning there should not be any blocking part in the code. There is an excellent book named “Patterns for Time-triggered Embedded Systems” by Michael J point where many design patterns have been shown to design embedded software using Co-operative RTOS. The author also suggests why a cooperative scheduler can be more effective and reliable than a preemptive scheduler if properly implemented.
On the other hand In preemptive RTOS Tasks can have blocking parts and in this part, context switching i.e. task switching can occur. Task switching can also occur in the middle of a task execution if a higher priority task is needed to be executed. This is called preemption.   When a preemption occurs the RTOS saves the current tasks states and then switches to another task. So usage of RAM is an issue here and also other complexity arises. Free RTOS is a well popular preemptive RTOS.
Remember CO-operative RTOS has also priority-based operation capability.

The concept of Active Object
As a firmware engineer, I always wanted to have a combination of all the best options out there. For instance i want to use some of the C++ & OOP features , use arduino ready made libraries & also use RTOS like scheduler to achieve soft/hard real time operation. 
Looking around i found Event-driven programming & active object concepts from Miro Samek, he is renowned in the embedded software industry. He created the Active object framework.
Active object has the following properties:
  • Encapsulated 
  • Non-blocking 
  • HSM (hierarchical state machine) 
  • Event queues 
  • Event Publish and subscribe capability 
Miro samek’s quantum leaps offers Active object framework solution for different microprocessor architectures. They also offer modelling tools to generate code from UML diagrams. Currently these are the frameworks provided by quantum leaps:
  • QP-nano
  • QP-C
  • QP-C++
These frameworks can work with 2 types of kernels underneath these are:
  • Cooperative kernel (QV) (much like co-operative RTOS) 
  • Preemptive Non-Blocking Kernel (QK) (much like preemptive RTOS but with non-blocking code)
There is an excellent document by miro samek named “beyond the RTOS” to explain the concepts
A minimum Event driven framework looks like this


  1. void loop()
  2. {
  3.     if( event1() ) { Event_1_handler(); }
  4.     else if( event1() ) { Event_1_handler(); }
  5.     else if( event1() ) { Event_1_handler(); }
  6.     else
  7.     {
  8.         goTo_idleState();
  9.     }
  10. }
Here the framework/ Supervisory event driven infrastructure waits for events
and dispatches them to application
Some important notes
  • Calls to functions that poll internally (like delay()) are not allowed, because they would slow down the main loop and defeat the main purpose of event-driven programming
  • The Event handler code MUST consist of Linear code that quickly returns control to the framework after handling each event 
  • The event handler code must be designed in a way so that the event handler can pick up where it left for the last event. But to do this you need if-else branches and code is likely to become “spaghetti code “. The solution is concept of state machine 
  • Qp –nano framework allows to combine event driven programming with state machines 
  • In qp-nano framework there is multiple event queues  with priority property 
  • There is a  state machine corresponding to each event 
  • These Event queues are constantly monitored by a scheduler 
  • Scheduler picks up the highest priority not-empty queue , after finding it > the scheduler extracts the event from the queue and sends it to the state machine associated with it . This is called dispatching of an event to the state machine 
  • The design guarantees that the dispatch() operation for each state machine always runs to completion and returns to the main Arduino loop before any other event can be processed
The event queue + state machine + a unique priority is collectively called an Active Object
  • When all event queues are empty the situation is idle condition . in this case QV_onIdle() is called , here processor can go to a low power sleep mode 
Conclusion

I think event driven programming is good for battery powered applications
I wish to work with this Active object framework at near future and write a blog post describing my hands on experience

Encapsulation & Accessibility in C++

Encapsulation : 

Encapsulation is another major component of OOP . the process of restricting access to data is called encapsulation. Encapsulation can be used to hide data members and members' functions. 

note: Class members are actually Class variables(data) and functions 

Access modifiers: 

Access modifiers are keywords in OOP that set the accessibility of classes, methods, and other members. Access modifiers are a specific part of programming language syntax used to facilitate encapsulation. in C++ there are 3 access modifiers 
  1. public 
  2. protected
  3. private 

Explanation:

Suppose you have created a class A. 
Then The private, public and protected members of class A can be accessed within the class. Now say you have created an object of this class then the object can only access the public members of the class. 

Similarly, if you derive a class B from the base class A then the derived class B can access the protected & public members of the base class A

now if you create an object of type Class B then the object can only access the public members of class B. now whether this object can access the members of the Base class A depends on the inheritance type. if it's public then this object of type class B can access public members of class A . on the other hand if the inheritance type is private then this object of type class B won't be able to access public members of class A.   

Now see the following diagram and you will understand everything 

Access specifiers without inheritance: 

Access specifiers with an inheritance: 










C++ in Arduino : Inheritance

What is Inheritance?

Inheritance is the process of creating new classes called "derived classes" from existing classes called "base classes". the derived class inherits all the capabilities of the base class & can add refinements of its own.

But why implement inheritance?

The answer is inheritance permits code re-usability. Suppose that you have made or got a class that is working perfectly but you want to add your own features to that class . Now instead of modifying the codes of Class A you can create a new class and inherit all the properties of Class A . in this way if any bug occurs you will be sure that it is from your newly made class thereby simplifying development by a lot. Inheritance is a major element of OOP.

the derived class is also called the child class
the base class is also called the Parent class

In some languages, the base class is called the superclass and the derived class is called the
subclass.

you will also hear the term child class extending parent class which is actually the same thing i.e. child class inheriting properties of the base/parent class.

let's see inheritance in UML with a very simple example

         
Here Animal is the Base class And Dog is the Derived Class . as Dog is an animal too so it will have all the capabilities of an animal. 

In UML inheritance is called generalization. In UML class diagrams, generalization is indicated by a triangular arrowhead on the line connecting the Derived and Base classes. The Arrow means derived from. The direction of the arrow tells that the derived class refers to functions and data in the base class, while the base class has no access to the derived class. 

Code in Action 

Lots of theory now Let's jump into Arduino and write some practical codes.

Returning to the Sensor Class that we made in our previous post  https://whileinthisloop.blogspot.com/2016/05/c-in-arduino-isr-in-class.html We have a proper working Sensor Class . and let's say we want to use this class For temperature measurement and we want to add functions like 
  • readCelsius() 
  • readFahrenheit() 
Now we can add these extension of ours using inheritance and by doing so we won't have to make any modification to the Sensor class, So to do that let's make a New Class called LM35 which will extend Sensor Class . in UML : 

here are the code snippets for the Sensor Class : 

Sensor.h 

#ifndef _SENSOR_H      //tells compiler to compile this class only once 
#define _SENSOR_H 

#include <Arduino.h>

class Sensor
{
   public:
          Sensor();
          void init(void);
          uint16_t readRawAdcValue(void);
          float readmv(void);
};
#endif

Sensor.cpp

#include "Sensor.h"

volatile static uint16_t adcValue = 0;

Sensor::Sensor()                                             
{}

void Sensor::init(void)
{
  ADMUX|=(1<<REFS0);    //AVCC with external capacitor at AREF pin 
  ADMUX|=(1<<MUX2)|(1<<MUX0); //0101adc channel 5 selected for adc input
  ADCSRA|=(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);   //prescalar 128 selected 
  ADCSRA|=(1<<ADEN);    //adc enabled
  ADCSRA|=(1<<ADATE);   // ADC Auto Trigger Enable
  ADCSRA|=(1<<ADIE);   //adc interrupt enable such that interrupt will happen after each conversion
  ADCSRA|=(1<<ADSC);   //conversion process begins
  sei();
}

uint16_t Sensor::readRawAdcValue(void)
{
  return adcValue;
}

float Sensor::readmv(void)
{
  return((readRawAdcValue()/1024.0)*5000);
}

ISR(ADC_vect)       //adc interrupt service routine /tasks which will be executed by microprocessor after each conversion
{
adcValue=ADCL;        //ADCL data register read 
adcValue+=(ADCH<<8);  //ADCH data register read
}

LM35.h

#ifndef _LM35_H      //tells compiler to compile this class only once 
#define _LM35_H 

#include <Arduino.h>
#include "Sensor.h"

class LM35:public Sensor     //LM35 class inherits base class Sensor
{
   public:
          LM35();
          float readCelsius(void);
          float readFahrenheit(void);
};
#endif

Notice that after colon : the base class name is written. this sets up the relationship between the parent and child class.

The keyword public after the colon 
: specifies that objects of the derived class LM35 are able to access public member functions of the base class Sensor. The alternative is the keyword private. When this keyword is used, objects of the derived class LM35 cannot access public member functions of the base class Sensor.

LM35.cpp

#include "LM35.h"

LM35::LM35()                                             
{}

float LM35::readCelsius(void)
{
  float mv = readmv();
  float cls = mv/10.0;
  return cls;
}

float LM35::readFahrenheit(void)
{
  float cls = readCelsius();
  float fhr = (cls*9)/5 + 32;
  return fhr;
}

Now let's write the sketch which will create an object of LM35. 

the sketch 

/*
 * this example shows the inheritance of c++ 
 * author: Hassin Ayaz
 * http://whileinthisloop.blogspot.com/
*/
#include "LM35.h"

LM35 myTempSensor;

void setup() 
{
  Serial.begin(9600);
  myTempSensor.init();
}

void loop() 
{
  Serial.print("raw ADC value: ");
  Serial.println(myTempSensor.readRawAdcValue());

  Serial.print("in mv: ");
  Serial.println(myTempSensor.readmv());

  Serial.print("in celsius: ");
  Serial.println(myTempSensor.readCelsius());

  Serial.print("in fahrenheit: ");
  Serial.println(myTempSensor.readFahrenheit());
  Serial.println("-----------------------------");
  delay(1000);
}

here you can see that the LM35 type object myTempSensor can call functions of both the base & derived classes.

In this post we have seen how inheritance of OOP can increase the usability of our codes :)

All the sources are available in this link https://github.com/hassin23ayz/ArduinoCpp/tree/master/T37_inheritance

C++ in arduino : ISR() in a class

In the last post https://whileinthisloop.blogspot.com/2016/04/c-in-arduino-interrupted-adc-with-free.html I showed you how to run the ADC in free-running mode. In this post I am going to show you how to wrap up the ISR() in a Class

I don't know if you have tried it or not but implementing ISR() in a class is a bit tricky ;)

Handling interrupts is not only a job for C or assembly language actually  C++ can also handle it. In a later post, I will show how to wrap up the ISR() with the help of some advanced concepts of c++ like virtual function. 

But in this post, I am going to show you a quick & simple way that may help you if you don't understand advanced concepts of C++. So if you are willing to design and maintain OOP concepts and want to implement your  ISR() in a class in a simple way then this post may help you.

Note: The way I am going to show is kind of mixing C & C++ concepts.

To keep our embedded context relevant, we are going to make a Sensor Class from the last post's codes,

First Let us make the declaration file (sensor.h)


#ifndef SENSOR_H_     //tells compiler to compile this class only once 
#define SENSOR_H_

#include <Arduino.h>
#include <avr/interrupt.h>

class Sensor
{
    //variables
  public:
  private:

    //functions
  public:
    Sensor();
    ~Sensor();
    void init(void);
    unsigned int readAdcValue(void);
};

#endif

you can see that there is no ISR(ADC_vect) function declaration in the header file.

now let's make the implementation file (sensor.cpp)

#include "Sensor.h"

static unsigned int adcValue; //this variable's scope is only this file 

//constructor
Sensor::Sensor()
{}

//destructor
Sensor::~Sensor()
{}
void Sensor::init(void)
{
  //initiating variables
  adcValue = 0;

  //initiating registers
  ADMUX |= (1 << REFS0); //AVCC with external capacitor at AREF pin
  ADMUX |= (1 << MUX2) | (1 << MUX0); //0101adc channel 5 selected for adc input
  ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); //prescalar 128 selected ADC clk = 125KHz
  ADCSRA |= (1 << ADEN); //adc enabled
  ADCSRA |= (1 << ADATE); // ADC Auto Trigger Enable
  ADCSRA |= (1 << ADIE); //adc interrupt enable such that interrupt will happen after each conversion
  ADCSRA |= (1 << ADSC); //conversion process begins
  sei();                 //global interrupt enabled
}

unsigned int Sensor::readAdcValue(void)
{
  return adcValue;
}


ISR(ADC_vect)       //adc interrupt service routine /tasks which will be executed by microprocessor after each conversion
{
  adcValue = ADCL;      //ADCL data register read
  adcValue += (ADCH << 8); //ADCH data register read
}

Note that the adcValue variable is a  static unsigned int type global variable & this variable gets updated in the interrupt service routine but why I have not declared this variable as a Class member variable? the answer is as 
the ISR() function is not a Class member function you won't be able to access any Class member variable in the ISR() .  

Also, note that the adcValue is a static variable which means its scope is limited to the cpp implementation file only. This pattern provides a data abstraction like private variables in a class. This is kind of implementing a C++ feature in C.

As per language pattern One may argue on the above approach but I found this simple solution working without any runtime / compile time issue but obvisously there may be better ways to implement.

Categories

Pages

Firmware Engineer

My photo
Works on Firmware, Embedded Linux, Smart Metering, RTOS, IoT backend

Contact Form

Name

Email *

Message *

Copyrighted by Hassin. Powered by Blogger.