Комментарии 24
Мне кажется, от такой абстракции больше вреда, чем пользы. Вот у нас есть интерфейс, который даёт доступ кратно dword. И мы понимаем, что наши структуры данных должны быть выровнены по 4 байта, и обновляться по 4 байта и вышележащие алгоритмы к этому готовы.
А вот нам дали интерфейс с доступом побайтно. И вот мы написали некую структуру, и теперь решили её положить в EEPROM. Получается, чтобы записать 4 байта, нам нужно сделать 4 вызова побайтного интефейса, и 4 раза переписать слово в EEPROM. А если эти 4 байта ещё и не ровно по границам слов EEPROM выровнены, то мы трижды перепишем одно слово и ещё один раз -- соседнее.
Дело в том что уровнем выше (с другой стороны) уже есть готовый программный компонент черный ящик, которому нужен именно byte API.
И HiLevel черный ящик уже никак не переделать, так как он задействован в других проектах.
Тогда возможны сюрпризы в виде "истёртой до дыр" EEPROM, ведь у неё ресурс конечен, в отличие от микросхемы SRAM, питаемой от батарейки. Так себе ситуация, да.
возможны сюрпризы в виде "истёртой до дыр" EEPROM, ведь у неё ресурс конечен
Об равномерном износе полей EEPROM у меня заботится как раз компонент уровнем выше.
https://habr.com/ru/articles/706972/
Если придётся этот интерфейс делать поверх флеш-памяти, то вы тоже будете на каждый байт читать страницу, стирать её и переписывать? Но это ещё ладно, если страница предварительно стёрта, не так всё плохо.
А вот стирание будет выглядеть, как изнасилование с особо тяжкими:
сначала вы читаете страницу, стираете, заменяете первый байт на 0xFF, записываете.
потом снова читаете страницу, стираете, заменяете второй байт на 0xFF, записываете,
и так всю страницу.
Второй момент: запись и стирание EEPROM - обычно не моментальные операции. У вас многозадачная среда выполнения? Внутриeeprom_dword_erase
и eeprom_dword_write
можно переключить контекст и выполнять другие действия?
У меня NoRTOS прошивка. Да и в Rtos прошивке можно просто отключить прерывания на время манипуляций с eeprom .
Этот алгоритм только для EEPROM. Причем специфического МСU.
С NOR Flash алгоритм кардинально другой.
Если у верхнего уровня есть API для работы с флеш, не проще ли было использовать его, а не заниматься перекладыванием байтов?
Стоп. А я тогда не понял, что в интерфейсе NVRAM делает byte_erase? Ведь вы же NVRAM эмулируете.
надо попробовать по другому: функция записи байта должна записывать в статическую переменную, а перед этим проверять если в этой статической переменной уже есть значение и запись происходит по адресу другой ячейки Епром, то сохраненное значение надо предварительно записать в Епром и это значт что надо хранить и адрес этой ячейки (в кэш) и количество байт перезаписанных в этой ячейке (хотя про это есть варианты), получается гораздо сложнее, но может быть гораздо эффективнее. Но надо проверять конечно, а чтобы проверять надо изобрести метод который работает для конкретной задачи (для готовый-ого программный-ого компонент-а черный ящик, которому нужен именно byte API. )
А вот нам дали интерфейс с доступом побайтно. И вот мы написали некую структуру, и теперь решили её положить в EEPROM. Получается, чтобы записать 4 байта, нам нужно сделать 4 вызова побайтного интефейса, и 4 раза переписать слово в EEPROM. А если эти 4 байта ещё и не ровно по границам слов EEPROM выровнены, то мы трижды перепишем одно слово и ещё один раз -- соседнее.
Что нам теперь программную cache память разворачивать что-ли?
И думать, что случится, когда данные уже записались в кэш, но ещё не записались в EEPROM, и внезапно отключилось питание.
Ну, в общем-то, решаемая задача идентична работе с памятью в CPU. Тот тоже общается в внешним миром в терминах кеш-линий или транзакций шины, а изнутри - в терминах байтов. Так что да, по хорошему стоило бы сделать что-то типа write combining. Надо смотреть, какие паттерны работы с NVRAM у вашего черного ящика. Если более-менее последовательный доступ к памяти, то может быть достаточным будет "кеша" на единственный dword (придерживать запись, пока не будет запись в другое слово, собирать все байты воедино и тогда уже писать). Минимальная доп. работа, и потенциально в 4 раза меньше стираний.
По-моему, в коде есть проблемы поболее, чем "затёртая до дыр" eeprom :(
Что с порядком байт: little-endian vs big_endian? (У вас little-endian);
Также, возможно, если предположить перенос кода на C++ (или вдруг в чистом Си расширят UB), то лучше не использовать запись в одно поле union и чтение из другого (type-pinning).
Что с порядком байт: little-endian vs big_endian? (У вас little-endian);
У меня little-endian
, если предположить перенос кода на C++ (или вдруг в чистом Си расширят UB), то лучше не использовать запись в одно поле union и чтение из другого (type-pinning).
Почему в C++ не любят объединения? Union в купе с битовыми полями - это же фундаментальная основа embedded программирования.
Потому что такое использование union - это UB в C++. А битовые поля - это до кучи еще полностью implementation-defined упаковка их по байтам (с какой стороны в байтики суем, как они между байтиками разбиваются).
Надо просто модульный тест написать на битовые поля и все ответы у вас будут.
А битовые поля - это до кучи еще полностью implementation-defined упаковка их по байтам (с какой стороны в байтики суем, как они между байтиками разбиваются).
Вот битовым полем взвели 13ы бит. И результат совпал с маской.
typedef union{
uint32_t dword;
struct{
uint8_t bit0 : 1;
uint8_t bit1 : 1;
uint8_t bit2 : 1;
uint8_t bit3 : 1;
uint8_t bit4 : 1;
uint8_t bit5 : 1;
uint8_t bit6 : 1;
uint8_t bit7 : 1;
uint8_t bit8 : 1;
uint8_t bit9 : 1;
uint8_t bit10 : 1;
uint8_t bit11 : 1;
uint8_t bit12 : 1;
uint8_t bit13 : 1;
uint8_t bit14 : 1;
uint8_t bit15 : 1;
uint8_t bit16 : 1;
uint8_t bit17 : 1;
uint8_t bit18 : 1;
uint8_t bit19 : 1;
uint8_t bit20 : 1;
uint8_t bit21 : 1;
uint8_t bit22 : 1;
uint8_t bit23 : 1;
uint8_t bit24 : 1;
uint8_t bit25 : 1;
uint8_t bit26 : 1;
uint8_t bit27 : 1;
uint8_t bit28 : 1;
uint8_t bit29 : 1;
uint8_t bit30 : 1;
uint8_t bit31 : 1;
};
} BitField32_t;
bool test_bit32_fields(void){
LOG_INFO(TEST, "%s():", __FUNCTION__);
bool res = true;
BitField32_t Field32;
Field32.dword = 0;
Field32.bit13 = 1;
EXPECT_EQ( 0x2000, Field32.dword);
return res;
}

Ваши опасения напрасны. Просто надо вести разработку с модульными тестами.
Я не особо в теме и поэтому вопрос. Для МК, где данные будут постоянно менятся real-time сутки напролет, хватит ли ресурса циклов "запись - чтение" EEPROM? Или я зря беспокоюсь и этих циклов хватит на годы эксплуатации?
Смотрите даташит на количество циклов записи, считаете для своей пиковой нагрузки реальный ресурс. Если ресурса не хватает, а памяти с избытком, то реализуете (или берете готовый, благо реализаций уже много под разные архитектуры написано) механизм выравнивания износа. Самый простой: пишете данные в виде пары "токен-значение", держите таблицу токенов и состояния страниц (активная, заполнена, пустая) для инициализации отдельно в памяти. Пишете данные в одну страницу, при считывании для получения актуальных данных ищете последнюю запись искомого токена в активной странице. После заполнения страницы переносите актуальные пары на другую страницу и продолжаете запись в нее, стираете старую страницу (желательно делать это не сразу, а когда израсходуете текущую, может помочь в случаях когда по какой-либо причине дропнулась запись в активной странице). Таким образом жонглируете несколькими страницами памяти, выравнивая износ и увеличивая общий ресурс. Как и любой другой вариант записи данных чувствителен к падениям питания в моменты записи, но тут если нужно, можно дублировать данные/пытаться прочитать с прошлой записанной страницы.
Как вариант - использовать FRAM. Больше 10 триллионов циклов перезаписи на ячейку точно хватит.
NVRAM из EEPROM