Открытый проект файловой системы для внутренней  памяти STM32H

image-loader.svg

Зачем ставить внешнюю IC памяти или SD карту если в микроконтроллере осталось много свободной Flash памяти!  

Микроконтроллеры семейства STM32H снабжены двумя независимыми банками Flash памяти и это очень удобно. В одном банке можно расположить программный код, а в другом временные перезаписываемые данные.  

Как сделать из внутренней Flash подобие EEPROM сравнительно неплохо написано в этом апноуте от ST. Но с некоторого уровня комплексности встроенного ПО хранить данные в именованных файлах удобнее чем в жёстких структурах. Файлы упрощают реюзинг, облегчают поддержку преемственности версий, апгрейды и даунгрейды. Файлы освобождают от хлопот с планированием размещения во флэш и разруливанием конфликтов размещения, особенно если приложение модульное и модули разрабатываются отдельно. 

В такой файловой системе можно хранить разные версии наборов параметров устройства, сертификаты и ключи к разным облачным сервисам, диагностические или аварийные дампы, временные файлы при распаковке, стандартные ресурсы, такие как шрифты и пиктограммы для HMI или скрипты и стили WEB страниц и проч.

Здесь представлена линейная файловая система относящаяся к классу журналируемых. Такие файловые системы известны давно.  Быстротой они не блещут, потому эффективны на сравнительно небольших объемах памяти. Но им присуще крайне малая потребность в ОЗУ, малый размер кода и сравнительная простота. При этом по своей природе линейные файловые системы обеспечивают равномерный износ Flash и им не нужен дополнительный программный слой FTL, как например для FAT на базе NAND. 

Это продолжение развития открытого проекта платы контроллера резервного питания BACKPMAN v2.0. Вот тут и тут написано как началась разработка BACKPMAN v2.0. Движение идет по пути наращивания функциональности. Поэтому представленный здесь следующий проект с пафосным названием Example2 является продолжением проекта Example1 с некоторым рефакторингом и c демонстрацией линейной файловой системы STfs (не расшифровывается, просто префикс в сорсах).  

Потому что наш идол — реюзинг.

Истоки STfs

Выше упомянул апноут от ST. На самом деле там все довольно угловато. Главная функция записи там может записывать только 2-х байтными словами сопровождаемыми тэгом под называемым виртуальный адрес. Что делать если надо сохранять структуры или не выровненные блоки неясно.

Как вариант — просто записывать все это во Flash по фиксированным адресам, макросам обеспечивая бесконфликтную упаковку. Потом чуть усложнить и организовать циклический сдвиг всего по сектору при перезаписи, чтобы выровнять износ Flash. Тут появляются зачатки API. И естественным образом мысль приходит к файловой абстракции. Один шаг остается до реализации API имитирующего обычную файловую систему. 

Источником вдохновения послужила файловая система из среды Keil для SPI Flash. Но там все аскетично и нет поддержки RTOS.

В предыдущих статьях было описано портирование Azure RTOS, и STfs  использует сервисы этой RTOS. Как любая файловая система STfs не предоставляет жесткого детерминизма.  Поэтому чтобы внедрить ее в среду задач с жестким реальным временем приходится использовать RTOS. Однако количество сервисов требующихся файловой системе от RTOS невелико, и портирование её на другую операционку не должно вызывать проблем.

Почему всё не так просто с STM32Hxxx

Особенностью семейства STM32H является наличие механизма контроля и исправления целостности блоков внутренней Flash памяти и разбивка Flash на стираемые секторы сравнительно большого размера. Конкретно у STM32H753VIH цельные блоки имеют размер 32 байта, а минимальные стираемые секторы 128 Кбайт. Т.е. можно записывать за одну итерацию во Flash только блоками по 32 байта называемыми Flash word
, не больше и не меньше, только один раз в одно место после последнего стирания, но стирать можно только блоками по 128 Кбайт. Если попытаться повторно записать что-то во Flash, даже если это будет один байт и он просто поменяет один бит с 1 на 0, то 32-байтный блок, на который придется такая запись, будет с высокой вероятностью обозначен системой как сбойный и последующее чтение в границах этого блока вызовет ошибку доступа к шине и вызов исключения HardFault, которое нарушит ход выполнения программы. И такое поведение никак отключить нельзя. Т.е. после некорректной записи всего в один байт в системе станет недоступным блок Flash памяти размером в 32 байта и его невозможно будет читать ни внутренней программой, ни с помощью отладочного адаптера, пока не будет стерт весь сектор128 Кбайт содержащий этот блок.  Указанные ограничения препятствует использованию файловых систем типа SPIFFS или littlefs. Требуется более специфичный подход. 

Описание STfs.

Технология проста. В секторы памяти размером 128 Кб последовательно записываются данные файлов. Каждый раз при открытии файлов на запись, фиксации данных и закрытии этих файлов создаются дескрипторы и записываются в тот же сектор. Данные файлов пишутся от нижней границы сектора вверх, а дескрипторы пишутся от верхней границы сектора вниз, как показано на рисунке ниже    

Способ организации файловой системы на примере одного сектораСпособ организации файловой системы на примере одного сектора

Когда данные и дескрипторы сходятся в одной точке запись переносится в следующий сектор Flash.

Ниже описание структуры дескриптора

  // Размер структуры дескриптора фрагмента файла - 64 байта
  // Дескриптор состоит из двух 32 байтных частей.
  // Вторая часть дописывается когда в дескрипторе ставится тэг(штамп) стертого файла
  typedef struct
  {
      uint32_t   file_id;       // Идентификационный номер файла. Если здесь находится EMPTY_PTTRN, то это чистый блок без данных о фрагменте
      int32_t    start_addr;    // Абсолютный адрес начала фрагмента
      int32_t    size;          // Размер фрагмента. Запись ведется 32 байтными блоками поэтому этот размер должен быть кратным 32
      uint32_t   number;        // Порядковый номер фрагмента, начинается с 0. В фрагменте с номером 0 хранится имя файла
      int32_t    data_size;     // Количество данных реально записанных во фрагменте
      uint32_t   version;       // Номер версии файла. Используется для безопасного переименования файла. По номеру версии определяется какая версия актуальна.
      uint32_t   reserv1[2];

  } T_stfs_file_normal_descriptor;

  typedef struct
  {
      uint32_t   deleted;      // Тэг удаленного файла
      uint32_t   reserv2[7];
  } T_stfs_file_erase_descriptor;


  typedef  struct
  {
      T_stfs_file_erase_descriptor  e;
      T_stfs_file_normal_descriptor n;
  } T_stfs_file_descriptor;

Дескриптор имеет размер 64 байта и состоит из двух половин по 32 байта. Это позволяет записывать дескрипторы всегда выровненными по границе 32-байтного Flash word и ровно соответствующего размера.

Дескриптор создается всегда, когда записывается имя файла и закрывается файл. Но если требуется надежно зафиксировать данные файла, а закрывать его ещё долго не придётся, то вызывают функцию STfs_flush, и в этом случае также будет создан дескриптор. Т.е. дескриптор окончательно валидирует данные и они не будут потеряны после выключения питания.

При удалении файлов ничего не стирается, но только в дескрипторы файлов записывается тэг удаленных. Когда в секторе остаются только удаленные файла он автоматически стирается. Если сектора полностью заполнены и в них остались вперемешку удаленные и актуальные файлы, то существует функция дефрагментации, которая переносит и дефрагментирует актуальные файлы в один или группу секторов, стирая освободившиеся сектора.

Технические характеристики файловой системы STfs

  • 32-битная линейная многозадачная для среды Azure RTOS адаптированная под 32-батное Flash word

  • Размер и количество секторов произвольное. Назначается через массив секторов на этапе компиляции

  • Длина имени файла не более 31 символа и не менее 1

  • Количество дисков произвольное.

  • Поддержки директорий нет, поддержки даты создания нет (но можно добавить).

  • Есть поддержка поиска файлов по имени, по шаблону, по порядку создания.

  • Количество одновременно открытых файлов на чтение произвольное. Определяется на этапе компиляции.

  • Количество одновременно открытых файлов на запись не должно превышать количества секторов.

  • Нельзя одновременно открывать файлы на запись и на чтение.

    Как видно есть некие ограничения по сравнению с той же FAT32, но с другой стороны все что надо для встраиваемого софта малых систем все есть.

Описание API STfs

Название функции

Описание

STfs_init

Инициализация файловой системы. Инициализирует драйвер Flash, проверяет целостность, форматирует если целостность нарушена

STfs_find

Поиск файла на виртуальном диске по строковому шаблону и получение сведений о файле

STfs_find_first_file

Поиск самого раннего файла на виртуальном диске и получение сведений о файле

STfs_find_next_file

Продолжение поиска файлов от ранних к поздним

STfs_open

Открытие файла на запись или чтение

STfs_rename

Переименование файла

STfs_read

Чтение данных из файла

STfs_write

Запись данных в файл

STfs_setpos

Установка позиции чтения из файла от его начала

STfs_len

Получение размера файла

STfs_close

Закрытие файла

STfs_delete

Удаление файла

STfs_flush

Фиксация записанных данных во Flash

STfs_free_space

Получение информации о количестве свободного пространства на виртуальном диске

STfs_format

Форматирование виртуального диска. Физически просто стирание всех секторов

STfs_check

Проверка целостности файловой системы на виртуальном диске и получение информации о системе

STfs_defrag

Дефрагментация файловой системы

STfs_free_task_cbls

Освобождение захваченных задачей RTOS ресурсов у файловой системы.

Симулятор STfs на персональном компьютере

Тестировать на реальном железе логику файловой системы слишком долго и неудобно.
Поэтому была написана программа симулятор на C++ под Windows 10.
Программа включает те же исходники STfs что и рабочая версия для микроконтроллера, только слой драйвера Flash памяти был заменен на эмуляцию в оперативной памяти компьютера. Но при этом эмулятор строго проверяет все ограничения по размеру и выравниванию записываемых данных и чистоте целевой области записи. Был создана функция тестовой записи и считывания файлов с регулярной проверкой целостности и дефрагментацией по необходимости.
Все записи, считывания производятся на основе генератора случайных чисел. Каждый 10-й файл не удаляется для имитации фрагментации. Если дефрагментация не удаётся, то чтобы не прерывать тест производится выборочное удаление более старших файлов.

Внешний вид программы симулятора STfs Каждый пиксел в столбце отображает состояние байта в секторе. Каждый столбец это отдельный сектор. Адреса растут слева направо и снизу вверх.  Зелёным цветом обозначаются блоки данных актуальных файлов, желтым обозначаются дескрипторы актуальных файлов. Красным и тёмно-красным обозначаются блоки уделенных файлов и их дескрипторов. Серый цвет обозначает чистые области Flash, куда еще ничего не записывалось после последнего стирания сектора.Внешний вид программы симулятора STfs Каждый пиксел в столбце отображает состояние байта в секторе. Каждый столбец это отдельный сектор. Адреса растут слева направо и снизу вверх.

Зелёным цветом обозначаются блоки данных актуальных файлов, желтым обозначаются дескрипторы актуальных файлов. Красным и тёмно-красным обозначаются блоки уделенных файлов и их дескрипторов. Серый цвет обозначает чистые области Flash, куда еще ничего не записывалось после последнего стирания сектора.

Симулятор легко проводит миллион и больше итераций, что на обычной Flash уже вызвало бы неминуемые проблемы износа. Однако такое количество итераций оправдано при поиске глубоко закопанных ошибок логики файловой системы и это сильно помогло при отладке.
Симулятор накапливает лог в котором можно просмотреть всю историю прохождения тестов.

Симулятор написан в среде Embarcadero RAD Studio 11 Alexandria. Но легко скомпилируется и на более ранних версиях, поскольку использует только минимальный набор стандартных компонентов. Подойдет триальная версия.

И наконец скоростные характеристики STfs

Сначала об износе. Как уже писал выше выравнивание износа присуще линейным FS и это показала симуляция случайного потока записей, удалений и дефрагментаций.

Вот результат после 100 тыс. итераций теста:

Номер сектора

Количество стираний

0

2961

1

3442

2

3541

3

3941

4

4386

5

3653

6

3798

7

3166

Как видно цифры одного порядка. При том что предельный жизненный цикл у Flash очень размыт то более точного совпадения цифр и не требуется. Надо напомнить что в STM32H работает механизм исправления одиночных ошибок Flash и предупреждения о двойных ошибках. Т.е. пользователь имеет дополнительную возможность проактивно среагировать на износ в своем прикладном коде.

Теперь цифры измеренные на реальном микроконтроллере STM32H753VIH после 200 итераций

Тип операции

Минимальное время, мкс

Максимальное время, мкс

Стирание сектора

855691

930729

Открытие файла на запись

179

768

Время записи файла размером 10 Кбайт

27100

30742

Время чтения файла размером 10 Кбайт

25

203

Проверка целостности файловой системы

189

8983

Итог

Скорость записи в файл колеблется в пределах 325…369 Кбайт в секунду. Это типичная цифра для NOR памяти и тут лучшего ожидать не приходится. Но надо отметить неплохую детерминированность.

Ложку дёгтя вносит время стирания сектора. А значит дефрагментация может затянуться до 8 сек. Это надо учитывать.

Но чтение будет идти со скоростью не менее 40 Мбайт в секунду. И этого более чем достаточно.

Все проекты хранятся здесь — https://github.com/Indemsys/Backup-controller_BACKPMAN-v2.0/tree/main/Software

© Habrahabr.ru