Home > Enterprise >  A preferred approach on encapsulating a variable in pure C
A preferred approach on encapsulating a variable in pure C

Time:10-01

We're a small team working on an embedded automation device. There is a core firmware dealing with all logic, and there're peripheral tasks dealing with statistics gathering, performing communication, telemetry, changing settings and so on. The idea is to provide these secondary tasks with limited (say, read only) access to critical variables. The simplest approach comes to mind first is to declare all core functions static and then to provide a handler to them for external access with no writing capability. But could there be some fittest/more elegant solution that just escaped us? Thanks.

CodePudding user response:

FreeRTOS has a queue mechanism (xQueue*), which could be employed to have your peripheral functions send updates to the core functions like "update setpoint", etc...

Although freeRTOS has a single address space model, on some architectures, you can restrict memory accessibility based on task. For example, the ARM Memory Protection Unit can be employed for this task. It would require logically grouping data to be protected within some ld-script definable segment, and setting alignment and granularity as appropriate.

While there is work involved in getting this right; the resulting confidence is typically worth the effort.

CodePudding user response:

A simple technique that I have used for allowing one module to modify data while providing read-only access to that data is as follows. If the module that owns the data is called samp1, then

In the header file samp1.h:

typedef struct {
    int    field1;
    double field2;
} Samp1_DataType;
extern const Samp1_DataType * const samp1_var;

In the C source file samp1.c

static Samp1_DataType samp1Var;
const Samp1_DataType * const samp1_var = &samp1Var;

This allows code within samp1.c to modify the fields of samp1Var, but all other modules cannot see samp1Var. But they can see the pointer samp1_var, which provides read access to the fields, but which prevents any modification.

(Note that this is not a substitute for the use of critical sections and mutexes to control access to data where that is required, it is just a way of preventing different modules fighting each other to set the values of global variables.)

Your compiler must be enabled to check for violations of const usage to enforce this. If your compiler is a gcc-derivative, use the -Wcast-qual option. If you are aiming for some kind of MISRA compliance, this is MISRA-2004 rule 11.5. (I am sorry, I don't know the equivalent MISRA-2012 rule.)

CodePudding user response:

But could there be some fittest/more elegant solution that just escaped us?

Yeah. well... that would be thinking about this at the early analysis stage of the project and writing it down with your requirements before you pick a MCU... Half-ways through the project when you've already picked a MCU, you have likely "painted yourself into a corner" and now you are stuck with whatever you picked.

For pure software/C programming-based protection, then indeed use static variables and functions as much as you can. This works just fine as long as you don't need re-entrancy or multiple instances of the same object. If you need such, then you can use opaque types - see How to do private encapsulation in C?

For actual write protection, microcontrollers typically have that for flash and eeprom only, though some mid-range and higher parts come with a MMU, which usually has various protection features such as setting up virtual memory. Many parts also have protection against executing code from data sections.

And finally, every semi-modern MCU has piracy protection against copy cats reading your binary.

  • Related