Raspberry Pi 2 Bare Metal Multicore JTAG Debugging

29 April, 2015

To get going with multicore development and JTAG debugging,
these code snippets and pieces of information should be useful.

1. JTAG debugging

jtag_debug

To enable JTAG on Raspberry Pi 2, some of the GPIO pins
needs to be configured to their “alternate” function.
Create a small application that calls the function below.
Build the application, and place the kernel.img file
on the SD-card. Now, when the Raspberry Pi 2 boots, JTAG
pins will be enabled.

/*--------------------------------------------------*/
static void jtag_enable(void)
{
    // http://sysprogs.com/VisualKernel/tutorials/raspberry/jtagsetup/
    /* -------- JTAG SETUP --------
    JTAG pin    JTAG sig    GPIO        Header pin
    1           VREF        N/A         1
    3           nTRST       GPIO22      15
    4           GND         N/A         9
    5           TDI         GPIO4       7
    7           TMS         GPIO27      13
    9           TCK         GPIO25      22
    11          RTCK        GPIO23      16
    13          TDO         GPIO24      18
    -------------------------------*/
    
    GPFSEL2_bit.FSEL22 = GPFSEL_ALT4;   // nTRST
    GPFSEL0_bit.FSEL4  = GPFSEL_ALT5;   // TDI
    GPFSEL2_bit.FSEL27 = GPFSEL_ALT4;   // TMS
    GPFSEL2_bit.FSEL25 = GPFSEL_ALT4;   // TCK
    GPFSEL2_bit.FSEL23 = GPFSEL_ALT4;   // RTCK
    GPFSEL2_bit.FSEL24 = GPFSEL_ALT4;   // TDO
}

2. Multicore JTAG debugging

raspberry_multicore

To get in contact with all 4 CPU cores, write the following in a bcm2836.ProbeConfig file:

 "A7_0" "Cortex-A7:0@0x80010000"
 +"A7_1" "Cortex-A7:0@0x80012000"
 +"A7_2" "Cortex-A7:0@0x80014000"
 +"A7_3" "Cortex-A7:0@0x80016000"

Specify the ProbeConfig file on:
Project > Options > I-jet > JTAG/SWD > Probe configuration file.

Enable Multicore with:
Project > Options > Debugger > Multicore > Number of cores : 4.

The Debug Log should say:

Connecting on DAP APB-AP port 0 to core Cortex-A7 r0p5 at 0x80010000. 
Connecting on DAP APB-AP port 0 to core Cortex-A7 r0p5 at 0x80012000. 
Connecting on DAP APB-AP port 0 to core Cortex-A7 r0p5 at 0x80014000. 
Connecting on DAP APB-AP port 0 to core Cortex-A7 r0p5 at 0x80016000. 

3. Multicore coding

To enable a specific core, use the function below:

#define CORE0_MBOX3_SET             0x4000008C

static void core_enable(uint32_t core, uint32_t addr)
{
    // http://www.raspberrypi.org/forums/viewtopic.php?f=72&t=98904&start=25
    volatile uint32_t *p;
    p = (uint32_t*)(CORE0_MBOX3_SET + 0x10 * core);
    
    *p = addr;
}

For example, to enable all CPU cores and make them start at address 0x8000:

  core_enable(1, 0x8000);
  core_enable(2, 0x8000);
  core_enable(3, 0x8000);

Another nice function to have is get_core_id:

uint32_t get_core_id(void)
{
    uint32_t core_id;
    asm volatile ("mrc p15, 0, %0, c0, c0,  5" : "=r" (core_id));
    return core_id & 0x3;
}

With this function, the running code can detect on which CPU core it is executing. This is useful for a switch-case, like this for example:

    volatile uint32_t core_id = get_core_id();

    switch (core_id) {
    case 0:
        // Code for Core 0
        core_enable(1, 0x8000);
        core_enable(2, 0x8000);
        core_enable(3, 0x8000);
        break;
    case 1:
        // Code for Core 1
        break;
    case 2:
        // Code for Core 2
        break;
    case 3:
        // Code for Core 3
        break;
    default:
        while (1) {}
        break;
    }

Raspberry Pi 2 Bare Metal with IAR Embedded Workbench

13 March, 2015

raspberry2
This is a quick step-by-step instruction on how to write a C application for Raspberry Pi 2 “bare metal”, i.e. running directly on the Cortex-A7 CPU without having any operating system. In this example, I use the free, size-limited version of IAR Embedded Workbench for ARM.

  1. Download and install IAR Embedded Workbench for ARM. www.iar.com/downloads
  2. Create a new Project, and set General Options > Target > Core to Cortex-A7:
    1_core_option
  3. Select Linker, and press the Edit… button to set the application’s start address (vector table) to 0x8000:2_linker_intvec
  4. Click on the Memory Regions tab, and configure IROM1 (the rest of the application) to start at 0x8000:
    3_linker_regions
  5. To create a binary file (kernel.img), set the following option:
    4_kernel_img
  6. Add a main.c file to the project with the following code:
    
    #include <stdint.h>
    
    #define GPFSEL2 *(volatile uint32_t*)0x3F200008
    #define GPSET0  *(volatile uint32_t*)0x3F20001C
    #define GPCLR0  *(volatile uint32_t*)0x3F200028
    #define DELAYCNT 2500000
    
    static void delay(uint32_t cnt);
    
    int main(void)
    {
        GPFSEL2 = 1<<3;     // GPIO pin 21 is output
        
        while (1) {
            GPSET0 = 1<<21; // Set GPIO pin 21
            delay(DELAYCNT);
            GPCLR0 = 1<<21; // Clear GPIO pin 21
            delay(DELAYCNT);
        }
    }
    
    /*-------------------------------------------*/
    static void delay(uint32_t cnt)
    {
        uint32_t i;
        volatile uint32_t a;
        
        for (i = 0; i < cnt; i++) {
            a = i;
        }
    }
    
    
  7. Done! You now have the skeleton for a complete application, written in C – with a complete C99 runtime library. Download start.elf and bootloader.bin from GitHub.
  8. Place your kernel.img together with start.elf and bootloader.bin on an empty SD-card (FAT32) and start the Raspberry Pi 2. It will now run the application, and GPIO pin 21 (pin 40 on the board header) should toggle between high (3.3V) and low (0V) – perfect for blinking a LED.

More reading / details / thanks to:
https://github.com/dwelch67/raspberrypi
http://www.valvers.com/open-software/raspberry-pi/step01-bare-metal-programming-in-cpt1/

Raspberry Pi 2 Bare Metal with interrupts

12 March, 2015

This is a more advanced example that shows how to configure a timer interrupt on Raspberry Pi 2 “bare metal”. You will need IAR Embedded Workbench for ARM, and follow the Project Options steps described in the Raspberry Pi 2 Bare Metal with IAR Embedded Workbench example.

This example blinks the two on-board LEDs, so no need for extra hardware.

This is main.c

#include <stdint.h>
#include <string.h>
#include <intrinsics.h>
#include "bcm2836.h"
 
#define DELAYCNT    2500000
#define TIMERCNT    0x400
 
static void delay(uint32_t cnt);
static void timer_config(void);
 
volatile uint32_t toggle;
 
/*--------------------------------------------------*/
int main(void)
{
    GPFSEL4_bit.FSEL47 = GPFSEL_OUT;    // GPIO pin 47 = Output
    GPFSEL3_bit.FSEL35 = GPFSEL_OUT;    // GPIO pin 35 = Output
 
    timer_config();
 
    while (1) {
        GPSET1 = GPSET_47;              // Set GPIO pin 47
        delay(DELAYCNT);
        GPCLR1 = GPCLR_47;              // Clear GPIO pin 47
        delay(DELAYCNT);
    }
}
 
/*--------------------------------------------------*/
static void delay(uint32_t cnt)
{
    uint32_t i;
    volatile uint32_t a;
     
    for (i = 0; i < cnt; i++) {
        a = i;
    }
}
 
/*--------------------------------------------------*/
static void timer_config(void)
{
    // Copy reset vector to address 0x0 for interrupts to work
    memcpy((char*)&Reset_vector, (char*)BCM_START_ADDR, 
           sizeof(struct reset_struct));
     
    // Enable timer and interrupts
    Base_IRQ_enable_bit.ARM_Timer = 1;
    Timer_load = TIMERCNT;
     
    Timer_ctrl_bit.count16_23 = TIMER_CNT_23BIT;
    Timer_ctrl_bit.enable     = 1;
    Timer_ctrl_bit.irq_enable = 1;
    Timer_ctrl_bit.prescale   = TIMER_PRESCALE_256;
     
    __enable_interrupt();
}
 
/*--------------------------------------------------*/
__irq __arm void IRQ_Handler(void)
{
    Timer_irq_clear = 1;
 
    if (toggle) {
        GPSET1 = GPSET_35;
        toggle = 0;
    } else {
        GPCLR1 = GPCLR_35;
        toggle = 1;
    }
}

This is the header file bcm2836.h

#ifndef _BCM2836_H_
#define _BCM2836_H_
 
/* GPIO Function Select */
#define GPFSEL_IN           0x0;
#define GPFSEL_OUT          0x1;
#define GPFSEL_ALT0         0x4;
#define GPFSEL_ALT1         0x5;
#define GPFSEL_ALT2         0x6;
#define GPFSEL_ALT3         0x7;
#define GPFSEL_ALT4         0x3;
#define GPFSEL_ALT5         0x2;
 
#define GPSET0_ADDR         0x3F20001C
#define GPSET1_ADDR         0x3F200020

#define GPFSEL0_ADDR        0x3F200000
#define GPFSEL1_ADDR        0x3F200004
#define GPFSEL2_ADDR        0x3F200008
#define GPFSEL3_ADDR        0x3F20000C
#define GPFSEL4_ADDR        0x3F200010

#define GPCLR0_ADDR         0x3F200028
#define GPCLR1_ADDR         0x3F20002C

/* GPIO Function Select 0 */
volatile __no_init union
{
 uint32_t GPFSEL0;
 struct GPFSEL0_bits {
 uint32_t FSEL0 : 3;
 uint32_t FSEL1 : 3;
 uint32_t FSEL2 : 3;
 uint32_t FSEL3 : 3;
 uint32_t FSEL4 : 3;
 uint32_t FSEL5 : 3;
 uint32_t FSEL6 : 3;
 uint32_t FSEL7 : 3;
 uint32_t FSEL8 : 3;
 uint32_t FSEL9 : 3;
 uint32_t reserved : 2;
 } GPFSEL0_bit;
} @ GPFSEL0_ADDR;

 
/* GPIO Function Select 2 */
volatile __no_init union
{
    uint32_t GPFSEL2;
    struct GPFSEL2_bits {
        uint32_t FSEL20     : 3;
        uint32_t FSEL21     : 3;
        uint32_t FSEL22     : 3;
        uint32_t FSEL23     : 3;
        uint32_t FSEL24     : 3;
        uint32_t FSEL25     : 3;
        uint32_t FSEL26     : 3;
        uint32_t FSEL27     : 3;
        uint32_t FSEL28     : 3;
        uint32_t FSEL29     : 3;
        uint32_t reserved   : 2;
    } GPFSEL2_bit;
} @ GPFSEL2_ADDR;
 
/* GPIO Function Select 3 */
volatile __no_init union
{
    uint32_t GPFSEL3;
    struct GPFSEL3_bits {
        uint32_t FSEL30     : 3;
        uint32_t FSEL31     : 3;
        uint32_t FSEL32     : 3;
        uint32_t FSEL33     : 3;
        uint32_t FSEL34     : 3;
        uint32_t FSEL35     : 3;
        uint32_t FSEL36     : 3;
        uint32_t FSEL37     : 3;
        uint32_t FSEL38     : 3;
        uint32_t FSEL39     : 3;
        uint32_t reserved   : 2;
    } GPFSEL3_bit;
} @ GPFSEL3_ADDR;
 
/* GPIO Function Select 4 */
volatile __no_init union
{
    uint32_t GPFSEL4;
    struct GPFSEL4_bits {
        uint32_t FSEL40     : 3;
        uint32_t FSEL41     : 3;
        uint32_t FSEL42     : 3;
        uint32_t FSEL43     : 3;
        uint32_t FSEL44     : 3;
        uint32_t FSEL45     : 3;
        uint32_t FSEL46     : 3;
        uint32_t FSEL47     : 3;
        uint32_t FSEL48     : 3;
        uint32_t FSEL49     : 3;
        uint32_t reserved   : 2;
    } GPFSEL4_bit;
} @ GPFSEL4_ADDR;


/* GPIO Pin Output Set 0 */
volatile __no_init uint32_t GPSET0 @ GPSET0_ADDR;

#define GPSET_0       (1<<0)
#define GPSET_1       (1<<1)
#define GPSET_2       (1<<2)
#define GPSET_3       (1<<3)
#define GPSET_4       (1<<4)
#define GPSET_5       (1<<5)
#define GPSET_6       (1<<6)
#define GPSET_7       (1<<7)
#define GPSET_8       (1<<8)
#define GPSET_9       (1<<9)
#define GPSET_10      (1<<10)
#define GPSET_11      (1<<11)
#define GPSET_12      (1<<12)
#define GPSET_13      (1<<13)
#define GPSET_14      (1<<14)
#define GPSET_15      (1<<15)
#define GPSET_16      (1<<16)
#define GPSET_17      (1<<17)
#define GPSET_18      (1<<18)
#define GPSET_19      (1<<19)
#define GPSET_20      (1<<20)
#define GPSET_21      (1<<21)
#define GPSET_22      (1<<22)
#define GPSET_23      (1<<23)
#define GPSET_24      (1<<24)
#define GPSET_25      (1<<25)
#define GPSET_26      (1<<26)
#define GPSET_27      (1<<27)
#define GPSET_28      (1<<28)
#define GPSET_29      (1<<29)
#define GPSET_30      (1<<30)
#define GPSET_31      (1<<31)

/* GPIO Pin Output Set 1 */
volatile __no_init uint32_t GPSET1 @ GPSET1_ADDR;

#define GPSET_32      (1<<0)
#define GPSET_33      (1<<1)
#define GPSET_34      (1<<2)
#define GPSET_35      (1<<3)
#define GPSET_36      (1<<4)
#define GPSET_37      (1<<5)
#define GPSET_38      (1<<6)
#define GPSET_39      (1<<7)
#define GPSET_40      (1<<8)
#define GPSET_41      (1<<9)
#define GPSET_42      (1<<10)
#define GPSET_43      (1<<11)
#define GPSET_44      (1<<12)
#define GPSET_45      (1<<13)
#define GPSET_46      (1<<14)
#define GPSET_47      (1<<15)
#define GPSET_48      (1<<16)
#define GPSET_49      (1<<17)
#define GPSET_50      (1<<18)
#define GPSET_51      (1<<19)
#define GPSET_52      (1<<20)
#define GPSET_53      (1<<21)

/* GPIO Pin Output Clear 0 */
volatile __no_init uint32_t GPCLR0 @ GPCLR0_ADDR;

#define GPCLR_0       (1<<0)
#define GPCLR_1       (1<<1)
#define GPCLR_2       (1<<2)
#define GPCLR_3       (1<<3)
#define GPCLR_4       (1<<4)
#define GPCLR_5       (1<<5)
#define GPCLR_6       (1<<6)
#define GPCLR_7       (1<<7)
#define GPCLR_8       (1<<8)
#define GPCLR_9       (1<<9)
#define GPCLR_10      (1<<10)
#define GPCLR_11      (1<<11)
#define GPCLR_12      (1<<12)
#define GPCLR_13      (1<<13)
#define GPCLR_14      (1<<14)
#define GPCLR_15      (1<<15)
#define GPCLR_16      (1<<16)
#define GPCLR_17      (1<<17)
#define GPCLR_18      (1<<18)
#define GPCLR_19      (1<<19)
#define GPCLR_20      (1<<20)
#define GPCLR_21      (1<<21)
#define GPCLR_22      (1<<22)
#define GPCLR_23      (1<<23)
#define GPCLR_24      (1<<24)
#define GPCLR_25      (1<<25)
#define GPCLR_26      (1<<26)
#define GPCLR_27      (1<<27)
#define GPCLR_28      (1<<28)
#define GPCLR_29      (1<<29)
#define GPCLR_30      (1<<30)
#define GPCLR_31      (1<<31)


/* GPIO Pin Output Clear 1 */
volatile __no_init uint32_t GPCLR1 @ GPCLR1_ADDR;

#define GPCLR_32      (1<<0)
#define GPCLR_33      (1<<1)
#define GPCLR_34      (1<<2)
#define GPCLR_35      (1<<3)
#define GPCLR_36      (1<<4)
#define GPCLR_37      (1<<5)
#define GPCLR_38      (1<<6)
#define GPCLR_39      (1<<7)
#define GPCLR_40      (1<<8)
#define GPCLR_41      (1<<9)
#define GPCLR_42      (1<<10)
#define GPCLR_43      (1<<11)
#define GPCLR_44      (1<<12)
#define GPCLR_45      (1<<13)
#define GPCLR_46      (1<<14)
#define GPCLR_47      (1<<15)
#define GPCLR_48      (1<<16)
#define GPCLR_49      (1<<17)
#define GPCLR_50      (1<<18)
#define GPCLR_51      (1<<19)
#define GPCLR_52      (1<<20)
#define GPCLR_53      (1<<21)


 
#define BASE_IRQ_ADDR       0x3F00B218
 
/* Base Interrupt enable register */
volatile __no_init union
{
    uint32_t Base_IRQ_enable;
    struct Base_IRQ_enable_bits {
        uint32_t ARM_Timer          : 1;
        uint32_t ARM_Mailbox        : 1;
        uint32_t ARM_Doorbell_0     : 1;
        uint32_t ARM_Doorbell_1     : 1;
        uint32_t GPU_0_Halted       : 1;
        uint32_t GPU_1_Halted       : 1;
        uint32_t Access_err_type_1  : 1;
        uint32_t Access_err_type_0  : 1;
    } Base_IRQ_enable_bit;
} @ BASE_IRQ_ADDR;
 
 
#define TIMER_CNT_23BIT             1
#define TIMER_CNT_16BIT             0
 
#define TIMER_PRESCALE_256          2
 
#define TIMER_LOAD_ADDR             0x3F00B400
#define TIMER_CLR_ADDR              0x3F00B40C
#define TIMER_CTRL_ADDR             0x3F00B408
 
/* Timer Load register */
volatile __no_init uint32_t Timer_load @ TIMER_LOAD_ADDR;
 
/* Timer IRQ clear register */
volatile __no_init uint32_t Timer_irq_clear @ TIMER_CLR_ADDR;
 
/* Timer control register */
volatile __no_init union
{
    uint32_t Timer_ctrl;
    struct Timer_ctrl_bits {
        uint32_t unused1            : 1;
        uint32_t count16_23         : 1;
        uint32_t prescale           : 2;
        uint32_t unused2            : 1;
        uint32_t irq_enable         : 1;
        uint32_t unused3            : 1;
        uint32_t enable             : 1;
        uint32_t debug_halt         : 1;
        uint32_t free_run           : 1;
        uint32_t unused4            : 6;
        uint32_t free_prescale      : 8;
    } Timer_ctrl_bit;
} @ TIMER_CTRL_ADDR;
 
 
#define BCM_START_ADDR 0x00008000
#define BCM_VECT_ADDR  0x00000000
 
volatile __no_init struct reset_struct
{
    uint32_t Reset;
    uint32_t Undefined;
    uint32_t SWI;
    uint32_t Prefetch;
    uint32_t Abort;
    uint32_t Reserved;
    uint32_t IRQ;
    uint32_t FIQ;
    uint32_t jump_table[7];
} Reset_vector @ BCM_VECT_ADDR;
 
 
#endif

More reading / details / thanks to:
https://github.com/dwelch67/raspberrypi
http://www.valvers.com/open-software/raspberry-pi/step01-bare-metal-programming-in-cpt1/

Object Oriented programming in C

26 October, 2011

This guide is meant to be a short introduction on very basic methods to achieve an “Object Oriented” design on your application written in the ANSI-C programming language.

The text is focused on Test Driven Embedded Development, but could be used for any area where C is used. In this example, I want to write an application for a hardware device containing:

  • A display
  • A keyboard
  • A motor

Classes
As usual, when starting an object oriented design – start identifying your objects. I.e. the “things” in your design. You have a display, a keyboard and a motor. You will also have some control logic. These are your classes.

In C, there is no “class” keyword. Ofcourse, there is the “struct” keyword, with which you can encapsule data in some way. We will look at this later on.

To begin with, in C, the object oriented design starts in the file system. For my example, create a directory structure for the classes as follows:

Classes = Directories

Classes = Directories

Class Methods

The class methods are implemented in separate C-files in your directory structure. For example, the keyboard class contains the public methods “readline”, “readchar” and “sleep”.

The structure of your project will now look like:

Class Methods = Files

Class Methods = Files

Class Data

The data in your classes should be encapsulated and private (accessed via get/set methods). In C, there is no “secure” way of hiding data, but the directory structure and using the keyword “static” are helpful.

For example, the keyboard “class” needs a buffer to store characters in. In each class, create a “class structure” that holds all data needed by the class. This structure will be passed to each class method (function), just as the “this” pointer in object oriented languages.

Class Data = Structures

Class Data = Structures

Each directory in your design contains a *_main.c. This file should contain any initialization and desctruction code (i.e. the constructor and desctructor). For example, the keyb_main.c file initializes the “class private” data:

Class private data

Always pass this structure as the first argument to the class methods, like this:

This pointer

Interfaces

To allow your application (the control logic) to talk with the objects in your design, they need public interfaces. In C, these interfaces are declared in header files.

Depending on the size of your project, you can now choose if the public interfaces should be put in separate directories, or just be a part of the existing directory structure (as above).

In my small example project, I choose to put the header files directly in the existing directory structure. We need (at least) two header files for each class:

  • One interface (header file) for the public methods (external functions).
  • One internal interface (header file) for internal communication between methods (static functions).

In the keyboard example, the directory now looks like:

Interfaces = Header files

Interfaces = Header files

The “keyb.h” file is the public interface for the keyboard class.
The “keyb_int.h” file is the internal interface between the methods.

For example, the “keyb.h” file contains the following public methods:

Public methods

The internal interface file “keyb_int.h” contains declarations of the internal data (structure) and “class internal” helper methods:

Internal interface

Objects

The encapsulation in objects consist of:

  • Separate C files for methods.
  • Structures for data.
  • Internal and external interfaces.

As you may have noted above, only the “class internal” and static methods (file internal functions) has the class private data structure as the first argument (the this pointer). The public methods (external functions) do not require the “class private” data as an argument. By designing this way, the class data is “hidden” from external users.

If you need more than one object of a class, the “class private” data needs to be dynamically allocated on the heap (instead of having it allocated once and for all as “static struct keyb_data” above). If you have followed the design above so far, this is a very simple change in the application. Instead of having the “static struct keyb”, just allocate the structure on the heap instead, by doing:

Heap allocation

Larger projects

For larger projects (many files, many developers), the interfaces between “classes” needs to be pretty fixed at an early stage of the project. For this reason, it is nice to separate the public interface totally from the actual implementation of the classes. The classes might also need further dividing into components.

For the example above, the structure could be extended to something like:

Larger project

An advantage of the above structure is that the application can be developed by many developers, working far apart.

The public interfaces in the *_i directories have been “fixed” from project start, so there will be no conflicts or incompatible method calls as the project evolves. Another advantage is that this structure is very easy to maintain in a revision control system (for example SubVersion, Git, CVS). By separating methods and interfaces in different files there will be minimal need for merging different code between developers.

Test Driven Development

When using a structure as above, missing (not yet implemented) code can easily be implemented as “test stubs”. From start, add a _stubs (or test) directory to each “class” directory structure. For example, for the keyboard class:

Test structure

At project start, the method “keyb_readchar” in file “keyb_readchar.c” might not be implemented.

To be able to test the keyb component anyway, just add “keyb_readchar” to keyb_stubs.c (with a simple implementation), to enable further testing (by the component test or system tests).
There is a very good test framework for doing these tests (and stubs):
CppUTest – http://www.cpputest.org/

Comments

What about inheritance?
Inheritance is missing from the C language. With a basic design as above, there is often no need for inheritance. In real-world applications, there is seldom a need for language supported inheritance of the type “a keyboard is a kind of input device” (and a base class InputDevice with (often) only empty methods). Instead, public interface header files can be used as a kind of replacement.

For example, if there would be more input devices in the example above, we might want to add a “generic” or “base” header file for input devices, with methods that should be implemented (Just like when doing “implements” interface in Java). For example:

input_device.h:

Inheritance

Then, all input devices should include and implement the function in this “base” interface. Note that since there is no such thing as function overloading in C, you will need to add a prefix to each device’s own function. There are ways to overcome this (using function pointers, but that is another story).

Reasons for prefixes

In the example above, there are prefixes such as “keyb_” on data, methods and defines in keyb_*.c. This is to avoid global name conflicts. There will never be a global define “SLEEP_TIME” or “READ” that confuses the project (they will be called KEYB_SLEEP_TIME or KEYB_READ). These prefixes also enables the inclusion of third-party libraries (and standard libraries) without problems.

Reasons for having short filenames and prefixes

The C standard recommends that the first 31 characters of names should be uniquely identifiable (this also enables shorter lines and more readable code).

General comments

Write short comments on what functions are meant to do (i.e. only what they are meant to do, not how they do it). Also write about the function’s arguments (in/out) etc. Especially in the public interfaces. It is recommended to have a code template that has an empty comment above every function. The code implementation inside functions should be self-explaining and easy to maintain.

I.e. no “smart” one-line constructions that needs extra comments. This only makes it difficult for (1) the compiler and (2) the person who is going to fix the bugs in the “smart” code later on.

Table-driven State Machine using function pointers in C

26 October, 2011

This short example shows how to implement a simple 4-state state-machine using function pointers in the C programming language.

In the example below, the state functions and their data is enclosed in a “State Table”. The control function (main) calls each function one-by-one in a loop.

This way, you ensure (force) that state functions:

  1. Will have the same prototype (arguments).
  2. Will be handled by similar error handling (the return value can be checked).
  3. Are called in correct order – there is no manually updated “list of function calls”.
  4. Can be dynamically added, removed or reordered in the state table.
Example code
#include <stdio.h>

/* Forward declaration of state structure */
struct state;

/* State function prototype */
typedef int (state_func)(const struct state *);

/* Structure containing state function and data */
struct state
{
    int         state;
    int         data;
    state_func  *func_p;
};

/* State function declarations */
static int state_1(const struct state *);
static int state_2(const struct state *);
static int state_3(const struct state *);
static int state_4(const struct state *);

/* State table */
static const struct state state_arr[] =
{
    { 1, 0, state_1 },
    { 2, 0, state_2 },
    { 3, 0, state_3 },
    { 4, 0, state_4 },
    { 0, 0, NULL }
};

/* Main control function */
int main(void)
{
    const struct state *s_p;

    s_p = &state_arr[0];

    /* Call each valid state one by one, in turn */
    while (s_p->func_p) {
        s_p->func_p(s_p);
        s_p++;
    }

    return 0;
}

static int state_1(const struct state *s_p)
{
    printf("Hello from state %d\n", s_p->state);
    return 0;
}

static int state_2(const struct state *s_p)
{
    return state_1(s_p);
}

static int state_3(const struct state *s_p)
{
    return state_1(s_p);
}

static int state_4(const struct state *s_p)
{
    return state_1(s_p);
}

Event handling
 
The table can also be used as a fail-safe way to handle events of different types. For example, lets say your application handles “signals” or “events” of some type, perhaps there is an OS (RTOS) sending signals between processes. To react on those signals, the function table can be used.
In the  example below, let’s pretend that one event with id “3” came in. Just call the handler for event “3”. Then another event with id “1” came in. The handler for event “1” is called. Note that the example below is hard-coded, you would probably use an event id variable to call handler “3” and “1”.
   
Example code
static void test(void)
{
    /* The table can also be used for reacting on events
       or signals, like for (stupid) example: */
    state_arr[3].func_p(&state_arr[3]);
    state_arr[1].func_p(&state_arr[1]);
}
 
Further reading
To read more about table-driven design, and how it can replace many over-designed inheritance solutions for simple problems, see this great article: http://www.codeproject.com/KB/architecture/table_driven.aspx
 

Follow

Get every new post delivered to your Inbox.