Modules: Keeping Things Loose

A recurrent theme in this blog has been the concept of modular programming. The systematic deconstruction of large complex applications into more manageable chunks know as modules. Each module operates by being an abstraction of a key concept in the application. These in turn interact with each other to create the desired total application. It is the interaction of these modules that is the topic of this discussion.

Whenever modules interact, there is a sharing of information that must happen. For example, if module “A” calls a function in module “B”, then module “B” must share with module “A” the signature of the function in question. In addition, the purpose of the function must be clear so that it may be called appropriately. Another example would be module “A” accessing a variable in module”B”.  This causes a great deal of shared information, some of which are, the signature of the variable, the meaning of the data it contains, how mode “B” reads the variable, how module “B” updates the variable, how module “A” and all other modules read and update the variable, what actions are based on the variable, what events affect the variable, and list list goes, seemingly without end.

In the first case, the function call, where little information is shared, is referred to as loosely bound or coupled. The analogy being that of an arms length business relationship. The second case, where a great deal on information is shared is called tightly bound or coupled. The human analogy being that of a romantic couple.

Now, before the orthodox view is examined, it must be pointed out, that tightly coupled  modules do have their good points. When modules work very closely together, a great deal of “protocol” overhead may be omitted and high levels of performance can be achieved. This is especially so in highly resource constrained systems. The down side is clear in the above statement though. By working as one, the two modules have in fact become one big module. Taken to the limit, the application becomes one huge mass of code where changes to one area have undesirable side effects in seemingly unrelated areas. Again, highly resource constrained systems are often quite small, and thus have smaller/simpler applications. In these cases this complexity may be manageable.

The predominant view is that loose coupling is better. Most systems are NOT hyper-constrained and applications are large and complex. By keeping the inner-working of modules hidden and private, many benefits are observed.

  • Clarity; when modules interact along clear, well defined lines lines, the code becomes easier to understand, write, and debug. Further, loose coupling generally results in simpler module interfaces are easier (and thus more likely) to document clearly. Tightly coupled modules require an extensive documentation effort that is often never done.
  • Maintainability; by limiting access, the odds of a change in one module have an undesired side-effect on an other are great reduced.
  • Reuse: When modules are loosely bound, it is possible to reuse modules in different applications without having to “fix” all of the “back-door” interactions.
  • Abstraction; having a limited, published interface means that the module is able to provide an abstract view of the services provided, shielding the consumer modules from the details of operations.
  • Team Programming; when modules have well defined interactions, it becomes easier to divide the work among a number of coders. Since each need only concern themselves on the published interfaces they use, it is no longer necessary to memorize the entire application and programmers are less likely to “step” on each others work.

The downside of loosely coupled systems is that some performance is lost “going through channels” rather than taking short-cuts. In my career, I have sometimes seen this argument put forth as a reason to abandon the discipline of loosely coupled modules with well defined interfaces. These factors will all have to be considered in the design of each interface, still, the overwhelming advantages of loose coupling should make it the default choice that is only set aside when circumstances leave no choice.

This article has dealt with module interaction in general. An important special case is where one module (the source) needs to transmit data or an event to another module (the sink). That is the topic of the upcoming post “Modules: Push or Pull”.

After a long hiatus, this web site is back! I want to take this time to apologize to my readers for the long pause in postings. A lot of water has gone under the bridge (and some even over it) in my life, leaving no time for my duties here. Hopefully things will be better now. As always your comments, thoughts and suggestions are most welcomed.

Peter Camilleri (aka Squidly Jones)

Bus Management

One side of the M25P80 abstraction that has been omitted until now is the interface layer between the processor and the device, namely the Serial (or sometime Synchronous) Peripheral Interconnect (SPI) bus. SPI is a common serial peripheral bus whose use in embedded systems can be traced back to the 1980s.

To the left is a much more complete architecture diagram. Added to the picture are the lower levels, in particular the SPI bus, peripheral and driver levels.

Starting from the bottom, it must be made clear that the SPI bus supports one controlling node (sometimes called the master node) and one or more peripheral nodes (sometimes called slave nodes). In general, the controller selects a peripheral and then the two exchange data. When the exchange is completed, the controller deselects the peripheral at which time another peripheral node may be selected.

In connecting the M25P80 to the PIC, some decisions need to be made. In particular, which of the two available SPI ports is to be used and to which I/O pin will the chip select be connect. In this case, both of those decisions were made by the engineers at MikroElektronika who designed the MMB32 board being used in these examples. Of course the M25P80 driver software has to comply with these design specs. Now the required selections could just be hard coded but that would make the code hard to re-use, so instead, all of the hardware dependent “settings” are brought into a single header file “MikroeMMB32.h”. The following little bit of that file deals with the M25P80:

The first define is used to specify the bit used as the device chip select, the next two define the registers needed to control the direction of that bit, and the two after that for actually controlling the state of the chip select bit; active (low) or inactive (high). Note that in each case, rather than specify the appropriate TRIS or LATCH register directly, the code specifies the respective clear and set registers so that these operations may be performed “atomically”. Now, this code is still a work in progress, so I know the I/O control could stand a great deal more in the abstraction area, but for now, this works OK. (See the note at the end)

Skipping to the last line, the FLASH_SPI_CHANNEL define specifies which SPI port the device will be using. Simple. So what of the rest? They exist to handle the fact that SPI is potentially a multi-device bus, that only supports one operation at a time. This rule can be enforced a number of ways; for example only one task might be allowed to access the SPI port, preventing any possible conflict.

If multiple devices need to be accessed by multiple tasks (as is illustrated), what is needed is called a Mutual Exclusion or Mutex semaphore. For now, a Mutex is used to prevent multiple tasks from accessing a resource out of turn. (See also below)

In order to protect the SPI port from inappropriate accesses, it is first needed to define how that port will be accessed. This is done in the header file “Confiig.h” The following little bit of that file specifies the use of the various system resources:

Note how RES_SPI_2 is specified as RES_SHARED whereas others are specified as RES_UN– USED. This prevents wasting resources by not creating Mutexes for unused or unmanaged peripherals. This is then used back in the header file “MikroeMMB32.h” to create macros for managing the Mutex and device setup if it is shared, or doing nothing, if the device is NOT shared. This way, the code is not cluttered with even more #if  … #endif blocks than needed and again, one file contains the definitions if they need to be updated. The result of this is two simple macros: emTakeFlashSPI and emGiveFlashSPI. To transparently share the SPI port, the following code template is employed:

emTakeFlashSPI();
// code use the Flash's SPI port here.
emGiveFlashSPI();

Finally, it is necessary to account for the fact that different tasks may use different settings of the SPI port. This is handled by the emQryFlashSPI and emSetFlashSPI macros. These query and set a configuration byte that tracks the setting of the SPI port. emQryFlashSPI returns true if the configuration does not match the one specified, indicating that the port needs to be set up. An example of this in action is:

if (emQryFlashSPI(emSPI2_M25P80))   // Confirm the configuration.
{
    // Close any previous configuration.
    if (emQryFlashSPI(emSPI2_NONE))
        SpiChnClose(FLASH_SPI_CHANNEL);

    // Set up the M25P80 configuration.
    // SPI2 setup
    SpiChnOpen(FLASH_SPI_CHANNEL,
               SPI_OPEN_MSTEN | SPI_OPEN_MODE8,
               PB_FREQ/bitRate);

    // Set the config indicator.
    emSetFlashSPI(emSPI2_M25P80);
}

The crucial thing about these macros is that they only result in code being generated if the SPI port is actually shared. If the port is not shared, they translate to no code at all, or “non-executed code” like if (0) { … } that is easy for the compiler to eliminate. Thus the burden at run time is minimized without hard coding system details into the “driver” code.

This is perhaps one of the most complex postings to date, and I must admit, it has left a lot of loose ends that will need to tied up in future posts. (See below once more)

As always, comments and suggestions are welcomed and invited.

Peter Camilleri (aka Squidly Jones)

PS: I should also add that another reason that the I/O are not as abstracted as they could be is that I am waiting to see what Microchip does in the next generation of peripheral libraries for the PIC32 “C” compiler. These were promised at the 2011 Masters Conference and to be honest, I’ve not heard anything more on that front since.

PPS: A future article looking at RTOS issues called FreeRTOS Impressions is planned. Please bear with me while it is being worked out.

PPPS: All of this software exists within a framework dubbed emSystem, hinted at in the article About the MMB-32 development project. A future article will begin a more formal look at the emSystem, its design and architecture.

The TFS in Action

In a recent posting, an abstraction for the M25P80 Serial Memory Device was examined. In a later posting, the application programming interface for the Trivial File System (TFS) was explained. This article will delve into some examples of the TFS in action, showing how, even a file system as simple as TFS can be used successfully in an embedded system. A future posting will detail the internals of the TFS.

The first step in using the TFS is to configure the file system for use. this is accomplished with a few simple definitions:

//**********************************************************
// Platform Specific Control - the M25P80 SPI Flash Device.
//**********************************************************
#define -- USE_M25P80_SPI_FLASH            1
#define M25P80_THREAD_SAFE              0

#if -- USE_M25P80_SPI_FLASH
#define -- USE_TRIVIAL_FILE_SYSTEM         1
#define TFS_THREAD_SAFE                 1
#define TFS_HANDLES                     3
#endif

The first define enable support for the M25P80. The second bypasses thread safe mutex semaphores. This is possible since the TFS is the only user of the M25P90. Next, the TFS is enabled followed by thread safe semaphores at the file system level. Finally, three file handles are allocated, so up to three files can be opened/acted upon at one time. This is similar to the file handles setting in old MS-DOS™.

The first bit of code that is needed is to open the file system form use. An example is shown below:

switch (emTFSOpenFS())
{
    case emOK:
    case emWRN_WRITE_PROTECT:
        break;

    case emERR_UNFORMATTED:
    case emERR_UNKNOWN_VERSION:
        // Warn the user about this slow operation.
        // ... some code deleted ...
        emTFSFormatMedia(16384);
        break;

    case emERR_NO_RESPONSE:
    default:
        // This should never happen.
        // Tell the user of the disaster
        // ... some code deleted ... 
        while (1) {}  // Spin loop
        break;
}

This code is a bit paranoid; the M25P80 is soldered onto the development board so emERR_NO_RESPONSE should be impossible, still the code tests and reacts to this calamity anyway. Overall the code is clear, efficient and simple. Next is an example of touch screen parameters being retrieved. Note that if the file cannot be read, this simple code assumes that the file was not found and calls the screen calibration routine to supply the needed parameters.

h = emTFSOpenFile((PU8)"TSP");

if (h >= 0)
{
    if (emTFSReadFile(h, 
                      0, 
                      sizeof(emTOUCHPARMS), 
                      (PU8)&touchParms) == emOK)
        emSetTouchLimits(&touchParms);

    emTFSCloseFile(h);
}
else
{
    setupTouchScreen();
}

Finally, an example of touch screen parameters being saved out to non-volatile storage.

// Write out the touch screen calibration data.
if ((h = emTFSCreateFile((PU8)"TSP", sizeof(emTOUCHPARMS))) >= 0)
{
    emTFSWriteFile(h, 0, sizeof(emTOUCHPARMS), (PU8)&tp);
    emTFSCloseFile(h);
}

Note that when the file is created, we need to tell the TFS about the size of the data that will be stored. In embedded applications this is often simply the “sizezof” the data structure that holds the required information.

In conclusion, the TFS simplifies many of the tasks encountered in embedded systems  without the high overhead of more capable, full-featured, file systems. As always,  your comments and suggestions are welcomed and invited.

Peter Camilleri (aka Squidly Jones)

A Brief Time Out

In an embedded system, there are many occasions when a delay in execution is required. For most of these, I prefer to make use of the task sleep APIs of most RTOS packages. There are times however when no RTOS is present, or the time interval is too short for an RTOS. In those cases, a dedicated delay library comes in handy.

The quick and dirty approach is just to spin loop, like this:

#define DELAY_1MS 16000/5 // for 16MIPS
void DelayMs(WORD time)
{
   unsigned delay;
   while(time--)
      for(delay=0; delay<DELAY_1MS; delay++);
}

This code leaves a lot to be desired. For starters, it’s sensitive to compiler optimization, caching, pre-fetching or other system speed ups that can throw off the time or even eliminate the delay. Another defect is that is is hard to slip in other duties without throwing off the delay accuracy quite severely.

Better solutions usually revolve around the use of a timer peripheral. The PIC32 includes the MIPS 4K core timer. This 32 bit timer runs at the peripheral bus speed (typically 40MHz) and its value can be read. The are many other capabilities too, but for this delay library, reading the counter value will suffice.

To start our timer, we need to record the current value of the core timer. This is the starting point or “now” in our delay:

#define emStartDelay() ReadCoreTimer()

Not much here, just a macro around a read to the core timer. This returns a 32 bit value that is the starting point of the delay. Now as time goes by, the core timer will increment. The amount of time that has passed is simply: Current Timer Value – Start Timer Value. This holds true, even if the core timer should overflow. Now, if this elapsed time is greater than the desired delay, the timer has expired.

if ((ReadCoreTimer() - start) >= duration)
{
   // Stuff here
}

Note that a timer can be started and its end detected all without modifying the steady counting of the core timer, or using up any limited resources. Thus a large number of software timers can be created without any difficulty. This simple bit of code is the heart of the following delay library interface:

#define emStartDelay() ReadCoreTimer()  // Get a starting point.
B8 emIsFinishedRaw(U32 start, U32 duration);  // Finished yet?
void emFinishRaw(U32 start, U32 duration);  // Finish the delay!
void emDelayRaw(U32 duration); // Start and finish a delay!

The astute reader will have noted that most of these functions end with the word “raw”; why is that? Simply these functions deal with time only in terms of core timer ticks. For my API, I want to deal in abstract, standard time units. I chose nanoseconds because they are a standard time unit and second, they hint at the sorts of time scales I’m aiming for. To help with the transition from nanoseconds to ticks, a few macros help out:

#define NSPT_NUM (1)  // NSPT_NUM / NSPT_DEN equals the number
#define NSPT_DEN (25) // of PB tics per nanosecond.

These two integers form a rational number that represents tics per nanosecond. No floating point required or desired! At 40MHz, the core timer step period is 25ns, so each nanosecond is 1/25 of a tic. Nanoseconds are converted to tics in the next macro:

// Convert nanoseconds to core timer tics.
#if NSPT_NUM == (1)
#define CVT_NS_2_TICS(ns) ((ns)/NSPT_DEN)
#else
#define CVT_NS_2_TICS(ns) ((NSPT_NUM *(ns))/NSPT_DEN)
#endif

Note the slight optimization for the (common) case where the numerator is 1. This leads to the actual library interface:

#define emIsFinishedNS(start, ns) \
    (emIsFinishedRaw(start, CVT_NS_2_TICS(ns)))
#define emFinishNS(start, ns)     \
    (emFinishRaw(start, CVT_NS_2_TICS(ns)))
#define emDelayNS(ns)             \
    (emDelayRaw(CVT_NS_2_TICS(ns)))

Which is a flexible delay library using standardized units. Of course, Other units, like microseconds or milliseconds could be used and creating those routines is left as an exercise for the reader.

Now, let’s see this code in action. Example 1, a simple delay is needed in the code:

emDelay(200); // Delay 200ns

Example 2, a sequence of code must not complete before a minimum time:

start = emStartDelay();
// Other code goes here . . .
emFinishNS(1000); // Make sure at least 1000ns has elapsed.
// Something crucial and time sensitive here

Example 3, a state machine must remain in a state for a certain length of time:

switch (state)
{
    // other states removed
    case s_0010:
        timer = emStartDelay;
        state = s_0011;
        break;
    case s_0011:
        if (emIsFinishedNS(timer, 2000))
            state = s_0012;
        break;
    // other states removed
}

It’s assumed that the switch is executed frequently so that after 2 microseconds, state s_0011 will transition to state s_0012.

So there it is: a flexible delay library with just a core timer and a few scant lines of code. As always, comments and suggestions are welcomed.

Peter Camilleri (aka Squidly Jones)

The Trivial File System

This posting is the third in a series that began with a look at Non-volatile Options and then moved on to an examination of the M25P80 serial memory device. With this posting we look at the Trivial File System or TFS created to exploit the M25P80 as a storage medium in an embedded development system (The mikromedia for PIC32).

The TFS is a very rudimentary file system for storing parameters, settings and infrequently changed data for an embedded system. The TFS is not designed to compete or be compatible with any “real” big computer file systems. It is not designed to hold “user” data that may need to be updated frequently. Rather, the TFS is best thought of as a combination of EEPROM for parameters and an extension to flash program memory for data.

In examining the TFS, three questions would seem to be of the greatest relevance:

  1. What does the TFS do?
  2. How is the TFS used?
  3. How does the TFS work?

The TFS lives up to its name by doing as little as possible and providing only basic services with ten functions. These functions tell the story of what the TFS is all about:

emRESULT emTFSOpenFS(void)
void emTFSCloseFS(void)

These simple functions open and close the file system. The open function can return some interesting values:

emOK – the file system is opened and available.
emWRN_WRITE_PROTECT – a warning that the device is write protected.
emERR_NO_RESPONSE – can’t initialize the device.
emERR_UNFORMATTED – unformatted media.
emERR_UNKNOWN_VERSION – the file system version is unknown.

 emRESULT emTFSFormatMedia(U32 dirSize)

The format function erases all data on the M25P80 and prepares the media to receive data. There are two reasons to call the format function. 1) The open function has returned emERR_UNFORMATTED or emERR_UNKNOWN_VERSION or 2) there is a need to reclaim file space in the media. The format function is the only function that calls the erase routines of the flash memory, and because of this, it can be extremely slow, up to 20 seconds!

Note that the format function takes one argument, the directory size in bytes. Memory space for the directory must be pre-allocated and cannot be expanded. Each file  directory entry consumes 4 bytes plus one byte for each character in the name. Every time a file is replaced with a new version, a new directory entry is required. It is important that the directory area be large enough for all the files it needs to hold and yet not so large that it eats into the space needed to store the file data.

emRESULT emTFSOpenFile(PC8 name)

This function is used to open an existing file with the given name. The file name is a null terminated string of one to 15 characters and is case sensitive. Except for null, there are no reserved characters. This function returns:

A handle value if successful.
emERR_FILE_NOT_FOUND – the file could not be found.
emERR_OUT_OF_HANDLES – the supply of handles is exhausted.

emRESULT emTFSCreateFile(PC8 name, U32 size)

This function is used to create new files and replace existing ones. The name is a null terminated string, the size is the amount of file space reserved for the file. The total space used must be known when the file is created. If a file of the same name exists, it is marked as pending erase. The new file is created as write in progress. Possible return values include:

A handle value if successful.
emERR_OUT_OF_HANDLES – the supply of handles is exhausted.
emERR_INVALID_FILE_NAME – the name is not valid
emERR_OUT_OF_HANDLES – the supply of handles is exhausted.
emERR_OUT_OF_SPACE – there is not enough space for the file.
emERR_WRITE_PROTECT – the media is write protected.

emRESULT emTFSCloseFile(emTFSHANDLE hnd)

This function closes a file, freeing up the handle used to access it. If the file was newly created, its status is changed from write in progress to valid. If an older file was being replaced, its status is changed from pending erase to erased. Possible return values include:

emOK – no errors, all OK
emERR_INVALID_HANDLE – the handle was not of an opened file.

emRESULT emTFSDeleteFile(PC8 name)

This simple function deletes the file of the given name. Note however that this function does NOT free up space on the media. Only a format command can do that. The delete command merely makes the file inaccessible. Possible return values are:

emOK – all OK!
emERR_WRITE_PROTECT – the media is write protected.
emERR_FILE_NOT_FOUND – no file of that name found to delete.

emRESULT emTFSReadFile(emTFSHANDLE hnd, U32 adr, U16 len, PU8 buf)

This function is used to read data from a file where hnd is the handle of the file, adr is the offset in bytes from the start of the file (0 is the start of the file), len is the number of bytes to be read, and buf is a pointer to a buffer to hold the data. Possible return values include:

emOK – all OK
emERR_INVALID_HANDLE – the handle was not of an opened file.
emERR_INVALID_POSN – the read was not within the scope of the file.

emRESULT emTFSWriteFile(emTFSHANDLE hnd, U32 adr, U16 len, PU8 buf)

This function is used to write data to a file, sort of. The actual operation performed is to bitwise AND the new data with the data already on the media. Fresh media  has the value 0xFF so this emulates a write operation the first time it is performed. Subsequent writes to the same section of media only AND the old and new values. To get a clean slate, a new file must be created (see above).

The parameters are hnd, the handle of the file, adr is the offset in bytes from the start of the file (0 is the start of the file), len is the number of bytes to be written, and buf is a pointer to a buffer to that holds the data to be written. Possible return values are:

emOK – all OK
emERR_INVALID_HANDLE – the handle was not of an opened file.
emERR_WRITE_PROTECT – the media is write protected.
emERR_INVALID_POSN – the write was not within the scope of the file.

 U32 emTFSFileSize(emTFSHANDLE hnd)

The function returns the size of the file attached to the handle in bytes or zero if there is an error.

There. A whole file system in only ten modest functions. In future postings, the usage and internals of the TFS shall be examined. Until then, please take the time to comment or make suggestions.

Peter Camilleri (aka  Squidly Jones)

Spiro Graphic Memories

[Update] If you are looking for information on the Spirograph toy, click here.

In examining the PIC 32 C compiler in C32 Impressions, there was a pressing need for several programs to stand in as benchmarks. One of those programs was a graphics applet that drew images inspired by the old Spirograph toy. As requested by a reader, this posting takes a look at the story behind the pretty picture.

When I was a kid, one of my favorite toys was my  Super Spirograph. This toy consisted of a number of gear toothed wheels, disks and inter-lockable shapes that when meshed together and driven by hand with a pen, would draw really cool pictures on paper. It was prone to having the little teeth slip, or the gears wobbling and ruining the picture, but I loved the toy all the same.

Years later in High School Math class, I was introduced to graphing trigonometric functions using polar coordinates. As difficult as it was to create those plots by hand (back then, fire was still a novelty so this was way before computers in the classroom) I still noted the similarity to the images created by my Spirograph.

A few years later in college, I was working as a research assistant at York University for Dr Keith Aldridge. I was given the task of writing device control software for a Calcomp pen plotter connected to an old Interdata 5/16 mini-computer. The purpose of this software was to help produce camera ready artwork for the graphs in the various papers and thesis of the post graduate students. The scaling, data manipulation, and string processing were all done in FORTRAN. To test my routines, sample data was needed. Thinking back to my childhood toy, I wrote a program to generate data sets based on a simple model of the Spirograph. The results were quite striking and beautiful. Regrettably, those prints are long lost.

It turns out that my code was not really simulating a Spirograph toy, but instead was producing a set of plots closely related to a family of curves called Rose plots. The basic formula for a Rose plot is:

\text{For } \phi \text{ in } 0 \dots 2D\pi: r = cos( \frac{N}{D} \phi)

Rose plots for various values of N and D, are pictured to the left.

While Rose plots are pretty close, they are not quite what I came up with. Rose plots all pass through the center point. In a pen plotter that is not a good thing as the center of the page would become saturated with ink and tear. I needed a way to simulate the wider radius of the Spirograph drawing wheel. I did this by adding a unit circle to my polar plot data like this:

\text{For } \phi \text{ in } 0 \dots 2D\pi: r = 1 + A cos( \frac{N}{D} \phi)

where A is typically 0.7. and N and D are relatively prime whole numbers.

So we come to the end. Just for the record, the benchmark sample used A=0.8, N=31 and D=30 to produce the figure on the left. Pretty, isn’t it?

I hope you have enjoyed reading about this little trip down memory lane as much as I’ve had writing about it. As always, comments and suggestions are welcomed and encouraged.

Peter Camilleri (aka Squidly Jones)

Some Gnarly Bits

Given recent posts on Abstraction and its application in making devices (like the M25P80) easier to deal with, the reader might be under the impression that the workings of the system underneath the covers are not a real concern; A sort of “Out of sight, out of mind” attitude. Well the reality is much more like “What you don’t know can hurt you!”

A very common example of this sort of thing in embedded systems is the attachment of various devices to General Purpose Input/Output (GPIO) pins. In the MMB32 development system for example, the LCD CS pin is connected to Port F, bit number 12. Conveniently, the PIC32 “C” Peripheral Libraries define an easy-to-use variable for this called _LATF12. It is very easy to use a macro to give this bit a more meaningful name:

#define LCD_CS_LAT_BIT      _LATF12

 Then in my code, to deselect the LCD I simply write:

LCD_CS_LAT_BIT = 1;

And that would appear to be the end of that! Of course, it is not; the above code is a “time-bomb” that could go off with serious consequences. A very nice feature of MPLAB-X is a dis-assembly view of the generated code with source code interspersed at appropriate points. Below is the above code and its implementation in MIPS assembly language:

420:                    LCD_CS_LAT_BIT = 1;
9D008958 3C03BF88   LUI V1, -16504
9D00895C 8C626160   LW V0, 24928(V1)
9D008960 24040001   ADDIU A0, ZERO, 1
9D008964 7C826304   INS V0, A0, 12, 1
9D008968 AC626160   SW V0, 24928(V1)

It’s not necessary to be an expert in MIPS assembler, the key points here are that the value of Port F is read using an LW instruction, modified in the next two instructions, and the modified value is written back to Port F using an SW instruction. How can this code be harmful? The fault is that it is not an  “Atomic Operation“!

Consider that Port F, having many bits, might be connected to more than one device besides the LCD. Further consider that this other device might be controlled by code in another task or perhaps an interrupt service routine. So now, contemplate the following scenario with an LCD and a Grunge Master 6000 (The 6000 is less costly than the 9000 but has more features than the 3000) both on Port F:

  1. The LW instruction reads the current value of Port F.
  2. The code modifies this value, setting bit number 12.
  3. An interrupt occurs. While the interrupt is being processed, some other part of Port F is modified to send a command to the Grunge Master 6000.
  4. The interrupt completes and normal execution resumes.
  5. The SW instruction writes it’s modified value to Port F without the changes made during the interrupt! A BUG!

This nasty little bug would take the form of the Grunge Master 6000 command being “randomly” truncated. The odds of catching it with a break-point are extremely low. This is the sort of bug that has people saying things like “HELP! My Grunge Master 6000 code works perfectly until I add my LCD driver code.”

So how can this problem be circumvented? The answer is that the operations on Port F need to be indivisible or atomic. There cannot be a middle part where an interrupt could cause trouble. Some common answers include:

  • Disable all interrupts while modifying the Port bits. This way the sensitive area of code is protected. The downside is that means turning interrupts off a lot and adding complexity to the code and increases interrupt latency. Further, it is easy to miss a spot and leave a vulnerability in the code.
  • The CPU could have built in, atomic, instructions for setting, clearing and toggling bits. This is actually the case of 8 and 16 bit PICs. By performing the bit manipulation in a single, uninterruptable instruction, there is not a middle section that needs to be protected. Alas, the 32 bit PICs do not have these instructions.
  • Microchip gave the 32 the PICs something else. Most peripheral registers have 3 “shadow” registers called the CLR, SET and INV registers. Writes to these registers perform Boolean operations on the register that they shadow. These are shown below:
CLR:  Register = Register & ~(Value)
SET:  Register = Register | Value
INV:  Register = Register ^ Value

These Boolean operations are atomic making them safe for use on shared resources without disabling interrupts.

To the left is a snippet from the PIC32 datasheet regarding these shadow registers. Note that reads from the shadow registers yield undefined values. Further note that the atomic nature of the operations applies to all three operations, not just INV or toggle as seems to be implied in the datasheet.

So, how does using these registers change the code? For starters, we must stop using the pre-packaged bit definition and switch to a new model. This means new definitions:

// Definitions for the LCD Chip Selct pin.
#define LCD_CS_BIT_MASK   (1 << 12)
#define LCD_CS_TRIS_SET   TRISFSET
#define LCD_CS_TRIS_CLR   TRISFCLR
#define LCD_CS_LAT_SET    LATFSET
#define LCD_CS_LAT_CLR    LATFCLR

And the code changes as well:

419:                    // Disable LCD
420:                    LCD_CS_LAT_SET = LCD_CS_BIT_MASK;
9D008958 3C02BF88    LUI V0, -16504
9D00895C 24031000    ADDIU V1, ZERO, 4096
9D008960 AC436168    SW V1, 24936(V0)

The code is now safe. The update to Port F is accomplished atomically by the SW instruction and the sequence is eight bytes shorter than before as a bonus. The real benefit here though is that Port F is now safe to use for other devices without fear of mind busting interaction bugs ruining your day!

Sometimes, it really does pay to check carefully under the covers. It’s what good engineers do!

As always, comments and suggestions are welcomed and invited.

Peter Camilleri (aka Squidly Jones)

PS: Astute readers will have recognized the Grunge Master 6000 as being somewhat similar to the Grunt Master 6000 introduced in Scott Adams’ Dilbert universe. While similar in a lot of respects, only the Grunge Master is suitable for microprocessor interfacing.

MMB-32 I/O Workseheets

As I mentioned in my article About the MMB-32 development project I am working with the mikromedia for PIC32 development board for various tutorials, exercises and projects. One slight issue with this board is that there a lot of resources to keep track of and further the boards changed quite a bit from version 1.0 to version 1.1.

Keeping track of all the I/O resources can be daunting and creating projects for a mixed population of 1.0 and 1.1 boards means I need to find common ground. Rather than the error prone process of pouring over tiny, hard-to-read schematics, I created I/O worksheets for each of the types of MMB-32 boards available.

Since they may be of use to other as well, here they are in PDF format:

MMB32 V1_0 Port Worksheet and MMB32 V1_1 Port Worksheet

or if you prefer, both worksheets in the form of a XLSX format spreadsheet:

Port Usage Map

I hope you find them as useful and time saving as I do. As always, your comments and suggestions are invited and welcomed.

Peter Camilleri (aka Squidly Jones)

The M25P80 Serial Memory Device

This posting will bring together two threads; Non-Volatile Options for parameter storage and the Abstraction of hardware through the use of modular “C” programming techniques.

At the conclusion of the non-volatile options posting, all proposals save one had been rejected. The sole remaining avenue was to store parameter data the the M25P80 SPI Flash Memory device installed in the MMB32 development board. As stated in the that posting, this device is not at all ideal for the task. So let’s take this opportunity to take a closer look at the requirements compared to the device and determine what makes it problematic.

Capacity

The capacity requirements for parameter storage are very modest. Typically a few hundred or at most a few thousand bytes is more than enough for most applications.

(+) The M25P80 exceeds this need by a large margin with a capacity of 1,048,576 bytes, commonly referred to as one mega-byte. This capacity is so large that it opens up the possibility of other potential uses for the device.

Reading

The device should allow arbitrary bytes to be read from the media.

(+) The M25P80 meets this requirement.

Writing

The device should allow arbitrary bytes to be written to the media.

(–) The M25P80 does not allow the writing of data! Instead data must be written using a two step process in which the media is erased to the value 0xFF and then data is “laid down” using the bit-wise AND of the current media value and the new data. If data is written without an erase, the result is the bit-wise AND of the old and new data.

Granularity

The device should allow operations to take place on arbitrary locations without concern for alignment or other requirements.

(–) The M25P80 does not allow arbitrary locations. Instead:

  • Erase commands operate on either sectors of 65.535 bytes or the entire device.
  • The bit-wise AND operation may start on any byte, but a sequence of these bytes may not cross a page (256 byte) boundary.

Speed

The device should perform all commands at a reasonable speed compared with the expected data update rate and general user interface requirements (< 100 milliseconds).

(–) The M25P80 execution times may be summarized as:

  • Read: quick; as fast as the SPI interface. (< 1 microsecond per byte)
  • Bit-wise AND: modest; as slow as 5 milliseconds, 0.8 milliseconds typical.
  • Erase: very very slow!; up to 20 seconds and only 0.6 seconds typical.

Endurance

The device should allow 100,000 write cycles.

(+) The M25P80 is rated for at least an endurance of 100,000 erase/write cycles and a data retention specification of 20 years at 55°C.

Bridging the Gap

At this point, the next step is to use modular programming and abstraction to bridge the gap between the requirements and the actual device. While the gap is rather larger large, potential “deal breakers” of inadequate capacity or endurance are not issues. Instead, the major areas needing attention are the bit-wise AND “writing” of data, erase granularity, slow erase cycles, and write page boundaries.

Figure 1

The semantic gap between the application and the hardware is large enough that it cannot be easily bridged by a single abstraction. That’s why this design uses two: A low level, device driver abstraction and a high level but very simple, file system that presents applications with an easy to use interface to the the storage device. This is illustrated in figure 1.

The low level driver is a fairly simple piece of code that abstracts out the SPI interface, the M25P80 command set and reworks them into a small set of callable “C” functions. This layer does deal with one issue though; the function that writes a data block automatically handles the cases where the block crosses page boundaries and performs the correct sequence of device commands. Thus at least that gap is closed. The function prototypes are listed below:

The balance of the gap is left to higher layers of software to deal with. These will be examined in an upcoming post on the Trivial File System.

As always, comments are invited and welcome!

Peter Camilleri (aka Squidly Jones)


Non-Volatile Options

Don’t worry. I am not about to branch into offering investment advice; Especially not the dubious world of the derivatives marketplace. I am instead looking at one of the unresolved issues in my Touch Screen Calibration series; Namely, where do the parameters go when we’ve gathered them? They need to be stored in some sort of non-volatile storage. What are the ideal characteristics of this storage and what do I have on hand in my Mikroe MMB32 development board? Ideally, the storage:

  • is non-volatile so that data is not lost when power is removed.
  • is permanently attached so that system parameters are always available.
  • supports atomic update so that parameters cannot be half written.
  • is simple to program so that a lot of program storage is not wasted.

Plan A

Many microcontrollers have on chip non-volatile resources. A wide selection of PIC chips have on board EEPROM that is an excellent choice for this sort of parameter data. Regrettably the PIC32MX460F512L used in my development board has no on chip EEPROM storage, so this easy option is not available. Still, the part has flash memory that could be pressed into service as EEPROM emulation. I did not look into this option too deeply at this time due to the complexity of the software needed and some doubts as to how it would affect real-time performance by turning off interrupts for long periods of time. I may revisit this at a later time.

Plan B

If your microcontroller lacks non-volatile storage, an easy, inexpensive option is to utilize a low cost serial EEPROM device. They are  is easy to use and come in I2C bus and SPI bus versions. This is a great idea, except that Mikroe didn’t put one on the development board I’m using. I could easily add an EEPROM but that would be cheating! A sub-variant of this plan are all the modern FRAM and MRAM non-volatile serial RAM devices that are on the market. These offer greater write speed and higher write endurance than EEPROM at a premium price but are still cheating! I wanted to only use resources already on board!

Plan C

Another approach is to use a battery backed up calendar/clock with ram. Indeed the  PIC32MX460F512L chip has an RTCC module and a plethora of power savings modes. The spec sheet has over 60 line items detailing power consumption so this is not a simple matter. In fact getting the chip into a low power mode is very complex and difficult. Further the PIC32 is not a member of Microchip’s XLP family of low power chips designed for battery powered operation, so the low power modes are not that low. Finally the back-light control circuit for the MMB32 display consumes about 5ma of current even when the display is off. This would drain any battery dead in short order so this option won’t work either.

Plan D

OK, if the RTCC in the PIC is no help to us, we can add an external RTCC/RAM module. There are many to choose from. Mikroe makes a very nice RTC PROTO Board that has it’s own battery, RTCC and 240 bytes of non-volatile RAM. Perfect, and since it is separate from the PIC32, life is a lot easier code wise since the switch-over to low power mode is handled by the RTCC boards hardware and I shouldn’t have to worry about power thief circuits. This too is cheating, but I purchased one of the Mikroe boards so I will be revisiting this option again at a later time.

Plan E

OK, so enough of what the MMB32 does not have. What about some non-volatile resources that it does have? Well there are two. The first is an SD memory card slot. This allows access to up to 2 giga-bytes of storage, which is staggering. The downsides are that this is removable storage, so the user can easily remove the memory device with all the essential parameters in it. In order to utilize an SD card, a low level driver is required, and a FAT32 file system to organize the data on that card. That is a lot off complex code that will be difficult to use and will devour resources. Now if a large file system is already needed, the incremental expense is minor to store parameters there. However if you were not already planning in that direction, it is a huge waste of flash memory.

Plan F

OK we are at the end, and still no closer to our answer. What’s left on our MMB32? Well there is a M25P80 SPI flash memory. It is NOT an EEPROM, it’s not all that flexible, and it’s not an easy chip to use well. Why do I say that? Yes, the full megabyte capacity is great and the SPI interface is simple and straight forward, but while data can be written (well it’s not a write so much as a bit-wise AND operation) one byte at a time, the device needs to be erased before any space can be overwritten. The bad news? The smallest unit that can be erased is an enormous 64K bytes and the erase time is up to 3 seconds! There is a command to erase the whole device but that can take up to 20 seconds! To use this device we need to create for it a file system that is small, simple and works well on this very primitive storage system. That file system is called the Trivial File System and is the subject of a my next posting on this topic.

Peter Camilleri (aka Squidly Jones)