Чтение структуры из постоянной памяти - PullRequest
0 голосов
/ 10 сентября 2018

Я работаю над встроенной системой, где некоторые данные калибровки хранятся во флэш-памяти. Данные калибровки хранятся в структуре, которая помещается в специальный раздел, который компоновщик знает для помещения во флэш-память:

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{};

, где calibration_data - другая структура POD, которая содержит фактические значения.

Проблема в том, что если я сейчас просто напишу следующее:

const data_block data{gCalibrationData};

if (CheckSum(&(data.mData)) == data.mCheckSum) {
    //do stuff
} else {
    //Error
}

это всегда относится к ветке ошибок, даже если фактическая контрольная сумма во флэш-памяти абсолютно верна (запись немного по-другому заставляет его работать, см. Ниже).

Это, конечно, понятно: компилятор видит глобальный объект const, который инициализирован по умолчанию, поэтому он знает все значения, поэтому я предполагаю, что он фактически оптимизирует весь if (если я отлаживаю данные printf через uint16_t *, я на самом деле получаю правильные значения).

Я думаю, что было бы правильно определить

const volatile data_block __attribute__((section (".caldata"))) gCalibrationData{};

Однако теперь у меня проблема в том, что я не могу назначить энергонезависимую структуру для энергонезависимой, то есть const data{gCalibrationData}; не компилируется. Та же проблема также появляется, если я пытаюсь получить доступ через const volatile data_block *.

Есть как минимум два или три способа сделать эту работу, и мне не нравится ни один из них:

  1. удалить квалификатор constvolatile) из gCalibrationData. Тем не менее, это что-то вроде хака, основанного на том, что компилятор недостаточно умен, чтобы гарантировать, что gCalibrationData никогда не затрагивается в моей программе, и, с другой стороны, я хотел бы сохранить квалификатор const, так как пытается писать в gCalibrationData по присваиванию это серьезная ошибка.
  2. доступ gCalibrationData через const gCalibrationData * volatile pData (да, изменчивый - именно то, что я имею в виду). Доступ через указатель, который является изменчивым, заставляет компилятор фактически загружать данные. Опять же, это похоже на взлом, поскольку сам указатель, конечно, не является изменчивым.
  3. дают data_block и calibration_data оператор присваивания, принимающий const volatile &, и присваивают им поле за полем. Это кажется правильным с языковой точки зрения, но тогда, когда меняется calibration_data, мне нужно вручную редактировать оператор присваивания. Несоблюдение этого требования приведет к трудно обнаруживаемым ошибкам.

Мой вопрос: Как правильно считывать данные калибровки? Моими идеальными критериями были бы:

  • сам глобальный объект - const, чтобы перехватывать непреднамеренные записи.
  • нет неопределенного поведения
  • доступ путем присвоения структуры непосредственно другой структуре
  • или, по крайней мере, чтобы мне не нужно было присваивать каждую переменную примитивного типа в calibration_data, см. Вариант 3. выше
  • бонусных баллов за безопасность потоков, хотя в моем конкретном случае флэш-память читает или записывает только один поток (все остальные «потоки» являются прерываниями).

Ответы [ 2 ]

0 голосов
/ 10 сентября 2018

Одним из решений может быть объявление буфера в отдельном исходном файле, информирование компоновщика о размере data_block, а затем определение gCalibrationData как символа, значением которого является начало этого буфера:

data_block.cpp :

//no initialization performed here, just used to
//transmit to the linker the information of the size
//and alignment of data_block
extern "C"{//simpler name mangling
[[gnu::section(".caldata")]] volatile
aligned_storage<sizeof(data_block),alignof(data_block)> datablock_buffer;
}

//then we specify that gCalibrationData refers to this buffer
extern const volatile data_block
gCalibrationData [[gnu::alias("datablock_buffer")]];

В качестве альтернативы определение символа gCalibrationData можно выполнить с помощью сценария компоновщика:

SECTIONS{
  .caldata : {
    gCalibrationData = . ;
    data_block.o(.caldata)
    }
  }

gCalibrationData isпсевдоним data_block_buffer.Это не приведет к неопределенному поведению , поскольку такой псевдоним разрешен языком: data_block_buffer предоставляет хранилище для gCalibrationData.

Семантически, спецификатор externиспользуется, чтобы сказать, что это объявление не является определением значения gCalibrationData.Тем не менее атрибут alias является определением символа для компоновщика.

data_block.hpp

extern const volatile data_block gCalibrationData;

//and copy must be enabled for volatile:
struct data_block{
  /*...*/
  data_block(const data_block&) =default; 

  data_block& operator=(const data_block&) =default;

  data_block(const volatile data_block& other){
    //the const cast means: you are responsible not to 
    //perform this operation while performing a rom update.
    memcpy(this,const_cast<const data_block*>(&other);
    }

  data_block& operator=(const volatile data_block& other){
    memmove(this,const_cast<const data_block*>(&other);
    //or memcpy if you are sure a self assignment will never happen.
    return *this;
    }
  };
0 голосов
/ 10 сентября 2018

Наиболее практичным подходом было бы потерять const. При строгом прочтении стандарта gCalibrationData не должно быть позволенным быть постоянным, поскольку запись в объект const - независимо от того, кто это делает - ведет к неопределенному поведению.

Если это не так, просто определите его как extern const (и, при необходимости, чтобы успокоить компоновщик, поместите определение не-extern в его собственную единицу перевода. Это даст вам проверку на константную корректность, разрешите например, компилятор выполняет оптимизацию подъема на основе начальных значений калибровочных данных, в то же время не позволяя ему делать какие-либо определенные предположения об этих значениях во время компиляции.

...