I'm working on a embedded system, where some calibration data is stored in the flash memory. The calibration data is stored in a struct which is placed in a special section that the linker knows to place in the flash:
struct data_block {
calibration_data mData;
uint16_t mCheckSum;
};
//Define to compile the fixed flash location for image data
const data_block __attribute__((section (".caldata"))) gCalibrationData{};
where calibration_data is another POD struct which contains the actual values.
The problem is that if I now simply write the following:
const data_block data{gCalibrationData};
if (CheckSum(&(data.mData)) == data.mCheckSum) {
//do stuff
} else {
//Error
}
this always goes to the error branch, even though the actual checksum in flash is absolutely correct (writing this a bit differently makes it work, see below).
This is of course understandable: the compiler sees a const global object, which is default-initialized, so it knows all the values, so I guess it actually optimizes out the whole if (if I debug-printf data via a uint16_t *, I actually get the correct values).
The way I think would be correct is to define
const volatile data_block __attribute__((section (".caldata"))) gCalibrationData{};
However, now I have the problem that I can't assign a volatile struct to non-volatile, i.e. const data{gCalibrationData}; does not compile. The same problem also appears if I try to access through a const volatile data_block *.
There's at least two or three ways I can make this work, and I don't like any of them:
- remove the
const(andvolatile) qualifier fromgCalibrationData. However, this is a bit of a hack based on the compiler not being clever enough to guarantee that gCalibrationData is never touched in my program, and on the other hand, I'd like to keep to const qualifier, since trying to write to gCalibrationData by assigning is a hard fault. - access
gCalibrationDataviaconst gCalibrationData * volatile pData(yes, the volatile is exactly where I mean it). Accessing through a pointer which is volatile forces the compiler to actually load the data. Again, this seems like a hack, since the pointer itself certainly isn't volatile. - give
data_blockandcalibration_dataan assignment operator takingconst volatile &, and assign field by field in them. This seems to be correct from the language point of view, but then whenevercalibration_datachanges I need to edit the assignment operator by hand. Failing to do so will produce hard-to-detect bugs.
My question: what would be the correct way to read the calibration data? My ideal criteria would be:
- the global object itself is
const, to catch unintended writes. - no undefined behaviour
- access by assigning the struct directly to another struct
- or at least so that I'm not required to remember to assign each variable of primitive type in
calibration_data, see option 3. above - bonus points for thread-safety, although in my specific case only a single thread ever reads or writes the flash (all other "threads" are interrupts).