Linux-Based PLC for Industrial Control

What Linux is doing for the highly proprietary embedded systems market is what the PuffinPLC will do for industrial automation.
The PuffinPLC Library

The common PuffinPLC library is divided into several sections (see Figure 2):

  • Configuration memory manager (cmm)--manages the shared memory that stores configuration data.

  • Global memory manager (gmm)--anages the global memory used to store the state of the plc points.

  • Synch library--andles the synchronization between modules.

  • Configuration library--sed for parsing of the configuration files.

  • Log module-- group of functions that lets every module produce logs in a consistent manner.

A module starts by calling the plc_init() function to initialize access to the common resources that have been set up by the puffinplc utility. The argv and argc variables of the module are forwarded to the plc_init() function call. This allows the PuffinPLC library to be configured at runtime by the end user simply by passing the parameters interpreted by the PLC library (--PLCxxx). This allows the end user to identify the module instance being launched (--PLCmodule=xxx), but also allows the PuffinPLC library to be tailored for a specific purpose.

When a module finishes, it calls the plc_close() function. Both these functions call the relevant init() or close() functions of each section of the PuffinPLC library. Apart from the previous functions, the plc_setup() and plc_shutdown() functions do the actual initialization of the common resources and are called by the puffinplc utility.

The Configuration Memory Manager

The configuration memory area is where the current configuration of the PLC is stored. This guarantees every module uses the same configuration data. Every access to the configuration memory area is made through the cmm. At present, the cmm does not have a public interface, so the modules themselves never access the cmm directly but always through other sections of the PuffinPLC library.

Several PuffinPLCs can run simultaneously on the same system. Each is distinguished by the configuration memory area it uses, identified by a unique number. When a module is launched, the identity of the PuffinPLC to which it should attach is specified as a command-line parameter (--PLCplc_id=xxx).

The cmm views the configuration memory as a simple, linked list of memory blocks that have been allocated and another list of empty memory blocks. The memory blocks are allocated by the cmm when requested by other sections of the PuffinPLC library. This currently occurs only during resource setup. Each block has a type identifier (1 byte) and a name (31 character string). During each module initialization, the PuffinPLC library requests the relevant block of memory using these identifiers. The type identifier is used to identify the structure of the data stored within that memory block; the name allows several data structures of the same type to be stored.

The Global Memory Map

The gmm manages the global shared memory, which stores the state of the plc points (i.e., plc internal coils). Plc points, with a size between zero and 32 bits, are configured in the configuration file with a unique name. At setup, this configuration is copied into the configuration memory. Modules access these points solely by using handles through the gmm library. A handle to a point is obtained by calling a gmm function to which the name of the point is passed.

Each module has its own private memory map and a private map mask, both created upon module initialization and both independent from the global memory map. When a module accesses a plc point, it is actually accessing its private memory map. Private and global memory maps are synchronized by calling the plc_update() function of the gmm. Synchronization is controlled by a semaphore that provides atomic updates. During synchronization, only the plc points that the module has write access for are copied into the global memory map. All other plc points are copied from the global memory map into the private memory map. The map mask is used to determine whether a module has write access to a plc point. All three maps are exactly the same size and use the same locations to store the plc points, allowing the update to be made as a simple, bit-for-bit logical function. The update must be made quickly because it requires access to a common shared resource (the global memory map) and may therefore become a bottleneck. Because of the use of a common shared resource controlled by a semaphore, using the PuffinPLC in a hard real-time environment will require the use of a scheduling mechanism that bounds priority inversion.

The use of private memory maps allows modules to run asynchronously without interfering with one another. Although it is not mandatory, it is expected that modules will run in an infinite loop, synchronizing their private memory maps at the beginning and end of each loop iteration. This guarantees that while the module is running its logic, the state of the plc points will not be changed by other modules executing their own logic or doing I/O. Nevertheless, the PuffinPLC architecture is sufficiently flexible to support modules that do not execute in an infinite loop; these may synchronize their private maps (in whole or in part) when they see fit.

Currently the gmm provides two alternative strategies to achieve the described functionality. Either of these strategies may be chosen when a module is launched by including one of two command-line options, --PLClocal or --PLCisolate.

The default strategy (--PLClocal) places the private memory map and the memory map mask in the module's heap and accesses the global memory as shared memory mapped onto the module's virtual memory address space. This has the drawback that if the module is badly written, a stray pointer may access the PLC's global memory map without going through the global memory, potentially creating havoc.

In the second strategy (--PLCisolate), the module forks a second process. The first process executes the module logic and accesses the private memory map, but it never actually maps the gmm into its virtual address space. The second process uses the shared memory mechanism to access both the gmm and the private map of the first process. Whenever the first process requests a memory map synchronization, it sends the request through a socket to the second process. The second process actually performs the memory map synchronization. This strategy involves large delays for each memory synchronization, but it does allow a developer to isolate a module that has not been thoroughly tested, making debugging easier.