Functions in Multiple Files Sharing Variables

Remember from Multicore Example that a function launched into another cog using the CMM or LMM memory model needs to exchange information with volatile global variables.  Sometimes a library launches functions into other cogs, and the same rules will apply.  Let's recap some global variable vocabulary, and look at some rules for using them in library source files.

 

Global Variables in Library Source Files

A global variable is accessible to all functions in every source file where it is declared.  To avoid problems:

Initialization — if a global variable is declared in more than one source file in a library, it should be initialized in only one place or you will get a compiler error. 

Static — use the static keyword to make a global variable visible only to functions within the same source file whenever possible. This removes any potential for conflict with variables of the same name in any other library source files or user application code. You can use static and volatile together.

Volatile — Use the volatile keyword for global variables that need to be used by functions running in different cogs. This keeps the C compiler's size optimizer from removing code that affects other functions' ability to read or write to that variable from another cog.  You can use static and volatile together.

Naming — if a programmer happens to give a global variable in their application code the same name as a non-static global variable in a library, the names will conflict and give unexpected results.  To help avoid this, name your variables in a  libName_varName format. If you run into a mystery bug when writing an application, it is worth checking the documentation for the libraries you are using to see if you have a variable name conflict.

 

More Functions for the Awesome Library

This activity starts by adding a new set of functions to the awesome library, in a source file named timer.c.   Since all the variables are in the same file, they are declared static to prevent other files using the same variable names from corrupting this file’s data.     Later, we'll move some of the functions to separate source files, with care to set up the global variables for multi-cog use.

  • Open … Documents\SimpleIDE\Learn\Simple Libraries\My Libraries\libawesome\libawesome.side.
  • Click Project and Select Add Tab to Project.
  • Set the File name to timer.c, paste in the code below, and save.
/*
  timer.c

  Track time since the last awesome call.
*/

#include "simpletools.h"
#include "awesome.h"

static int cog = 0;
static int stack[60];
static volatile int seconds = 0;

void secondCtr(void *par);
int awesome_startTimer(void)
{
  if(cog == 0)
    cog = 1 + cogstart(&secondCtr, NULL, stack, sizeof stack);
}

void awesome_stopTimer(void)
{
  if(cog > 0)
  {
    cogstop(cog - 1);
    cog = 0;
  }
}

int awesome_secondsSince(void)
{
  return seconds;    
}
int awesome_secondsReset(void)
{
  seconds = 0;
}

void secondCtr(void *par)
{
  int dt = CLKFREQ;
  int t = CNT;
  while(1)
  {
    waitcnt(t += dt);
    seconds++;
  }
}

You have added more functions to the library, so:

  • Click the Show Project Manager button in the bottom-left corner of SimpleIDE.
  • Click awesome.h and update as shown here.

 

After adding this to the library, make sure to test it.  The easiest way to do this is to modify the test harness so that it uses the new functions, and verify that each one runs as expected. 

In the example below, we want to verify that start awesome_startTimer returns nonzero, and awesome_secondsSince() reports the correct seconds count after the start function was called. 

The modified test harness also checks to make sure that awesome_secondsReset can reset the seconds counter to zero, and awesome_stopTimer stops the counting process.  There are two calls to awesome_secondsSince that follow the stop call to make sure it really has stopped counting.

  • Click the libawesome.c tab and update your test harness as shown here.
  • Run and verify that the application correctly displays the time since the last call (2 seconds in this case).
/*
libawesome.c

Test harness for libawesome library.
*/

#include "simpletools.h"                      // Include simple tools
#include "awesome.h"                          // Include awesome header

int main()                                    // Main function
{
  int cog = awesome_startTimer();             // Test the start
  print("cog = %d\n", cog);                   // Print cog value

  for(int n = 1; n <= 10; n++)                // Call awesome certain number of times
  {                                          
    print("Nick");                            // Print a name
    awesome();                                // Append with " is awesome!\n"
  }                                          
  int callCount = awesome_getCount();         // Test awesome_getcount
  print("callCount = %d\n", callCount);      
 
  pause(2000);
  int seconds = awesome_secondsSince();       // Test seconds since
  print("seconds = %d\n", seconds);
 
  awesome_secondsReset();                     // Test seconds reset
  seconds = awesome_secondsSince();
  print("seconds = %d\n", seconds);
 
  print("Jessica");                           // Print another name
  epic();                                     // Append with " is epic!\n

  pause(2000);                                
  awesome_stopTimer();                        // Test stop timer
  seconds = awesome_secondsSince();

  print("seconds = %d\n", seconds);           // Make sure timer stopped counting
  pause(1000);
  seconds = awesome_secondsSince();
  print("seconds = %d\n", seconds);
}

Here's what your SimpleIDE Terminal output should look like if all went well:

 


Did You Know?

Library-Only Functions — Forward declarations for all the functions were added to awesome.h with one exception: secondCtr.  This function is not one that we want the user's application to call, so we didn’t advertise it in the header file.   Instead, the timer.c file just has a forward declaration letting the compiler know that it will find the code for it eventually. 


 

Try This - Move Functions to Other Files and Give them Global Variable Access

Now, imagine that there are 26 more functions in the timer.c source file (yikes!).  In that case, it would be better to move functions to separate files whenever possible so your application code does not get stuffed with code for unneeded functions if you only call one or two.  Not all functions would have to be separated.  For example, if two or more functions always work together, or if the functions are small and interrelated, it’s okay to leave them in the same file.

With these guidelines in mind, we’ll keep awesome_startTimer and awesome_stopTimer functions in the same file with secondCtr.  An advantage here is that the cog and stack variables can remain static and keep their generic names. 

In contrast, awesome_secondsSince and awesome_secondsReset should be moved to separate files.  Since they share the seconds variable, it should be made global to the application (no static modifier).  Its name should also be changed from seconds to something like awesome_cog_seconds.  That way if a user makes a global variable named seconds in their application code, it won't interfere with this library’s seconds value.

The result is the three files shown below.  All three files have to share the awesome_cog_seconds variable, so all three declare it without the static modifier.  Remember, only one source file should optionally initialize the variable, and that is already done in timer.c. 

  • Modify timer.c as shown here.  We we removed the static keyword from the the seconds variable declaration, and renamed it to awesome_cog_seconds in both the declaration and in the secondCtr function. We also removed awesome_secondsSince and awesome_secondsReset — those are going into their own source files.
/*
  timer.c

  Track time since the last awesome call.
*/

#include "simpletools.h"
#include "awesome.h"

static int cog = 0;

static int stack[60];

volatile int awesome_cog_seconds = 0;         

void secondCtr(void *par);

int awesome_startTimer(void)
{
  if(cog == 0)
    cog = 1 + cogstart(&secondCtr, NULL, stack, sizeof stack);
}

void awesome_stopTimer(void)
{
  if(cog > 0)
  {
    cogstop(cog - 1);
    cog = 0;
  }
}

void secondCtr(void *par)
{
  int dt = CLKFREQ;
  int t = CNT;
  while(1)
  {
    waitcnt(t += dt);
    awesome_cog_seconds++;
  }
}

Next, we need to make a source file for the awesome_secondsSince function removed from timer.c   Note that the awesome_cog_seconds variable that is still in timer.c is also needed here.   So, it needs to be declared (but not initialized!) as a global volatile variable in this source file as well. Don't forget to update the variable name in the function!

/*
  seconds_since.c

  Track time since the last awesome call.
*/

#include "simpletools.h"
#include "awesome.h"

volatile int awesome_cog_seconds;

int awesome_secondsSince(void)
{
  return awesome_cog_seconds;    
}

A source file for awesome_secondsReset also needs to be added to the library project.  It too accesses the awesome_timer_seconds variable. So, so again we will need to declare the variable, and update the variable name used in the function call.

  • Repeat the Add Tab to Project steps with seconds_reset.c.
/*
  seconds_reset.c

  Track time since the last awesome call.
*/

#include "simpletools.h"
#include "awesome.h"

volatile int awesome_cog_seconds;

int awesome_secondsReset(void)
{
  awesome_cog_seconds = 0;
}

 

Your Turn

At this point, the test harness code in libawesome.c should run the same as it did the last time.  The only difference is that the library is arranged so that calling seconds_since will not bring in the code for seconds_reset if the application never calls it.

  • Re-run the libawesome test harness code, and verify that it behaves the same as before.
  • If needed, review the steps for building a library in How to Create a Simple Library.
  • Build the modified library for the CMM and LMM memory models.