Перейти к содержанию
    

Хранение данных на NOR flash (Кольцевой буфер)

14 hours ago, jcxz said:

В моём чипе стёртые =0. Если у Вас =0xFF - то просто проинвертировать всё перед записью (и после чтения).

Теперь с нахождением головы, стало все ясно.

14 hours ago, jcxz said:

Я же Вам схему рисовал - на ней как раз и идёт запись в сторону уменьшения адресов.

Да я не совсем понял скрытый смысл, почему запись идет с конца.   

14 hours ago, jcxz said:

Т.е. - перед кодированием блока исходных данных я, пользуясь тем, что размер кодированных данных в COBS не зависит от содержимого, вычисляю сразу положение 1-го байта (куда он должен быть записан во флешь), если необходимо - стираю предварительно перед ним сектор, так чтобы был как минимум один стёртый сектор, и начинаю писать блок в сторону увеличения адресов от 1-го байта до последнего. При декодировании - декодирую опять в том же порядке (так как кодирование и декодирование - потоковые, то должны идти в одном и том же порядке). И новые блоки - добавляются в сторону уменьшения адресов флешки. Опять-же - согласно ранее приведённой схеме.

В общем решил отрисовать это, получилось такое:

455622395_UntitledDiagram.png.1df57e750dfefd4e7159e01056f14a2e.png

Какакя ешё информация храниться в 4 байтах маркера сектора ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В 09.04.2020 в 13:25, pokk сказал:

Какакя ешё информация храниться в 4 байтах маркера сектора ?

Там хранится маркер занятости сектора. В свободном (стёртом) секторе он ==0, непосредственно перед тем как в чистый сектор залезет голова записи - в него записывается маркер занятости.

Он нужен для быстрого поиска дырки стёртого пространства при старте системы хранения. Чтобы не читать целиком сектор (все 256КБ) я сперва прочитываю маркеры секторов, входящих в КОХ, по их содержимому предварительно нахожу дырку (или её отсутствие) и проверяю всю КОХ в целом на валидность. Потом, если по этому предварительному тесту КОХ представляет из себя несколько (>=1) стёртых секторов за которыми идут сектора с маркерами валидных данных (количеством также >=1), проверяю дополнительно самый крайний стёртый сектор в дырке (смежный с первым сектором валидных данных) на то, что он действительно весь стёрт (вычитываю его целиком проверяя все слова на ==0). Если все проверки прошли на ок - КОХ в валидном состоянии и можно читать из неё или дописывать, если что-то не так - инициализирую КОХ (стираю, можно не всё, а так, чтобы при следующем старте инициализация не требовалась).

Такой алгоритм позволяет системе хранения стартовать с любого произвольного состояния флешки - хоть она стёрта хоть заполнена каким-то мусором (не требуется начальное стёртое состояние флешь). Так же алгоритм устойчив к сбоям питания например во время операции стирания сектора - если он окажется не полностью стёрт: ничего страшного - при следующем вкл.питания содержимое КОХ не потеряется, а при следующей операции записи данных в КОХ возобновится стиранием того сектора, стирание которого было прервано сбоем питания.

Ну и стартует система хранения при включении питания быстро - при старте вычитывается целиком только один сектор.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 4/10/2020 at 4:13 PM, jcxz said:

Там хранится маркер занятости сектора.

Там у вас 4 байта, маркер занятости все 4 байта занимает? Или есть что-то еще?
А то я так же заложил 4 байта и только в 1 поставил маркер, а остальные пока не используются, но периодически руки так и чешутся что бы что нибудь туда добавить(подумывал добавить информацию по битно, на записанные страницы, для более быстрого и простого поиска головы), но оказалось это лишнее, голова и так достаточно просто ищется.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

34 минуты назад, pokk сказал:

Там у вас 4 байта, маркер занятости все 4 байта занимает? Или есть что-то еще?

Да все 4. Вам жалко 4 байта на 256КБ??? :shok:  :unknw:

Тем более что - я же вам описывал алгоритм быстрого обнаружения стёртой "дырки". Он рассчитан на то, что начальное заполнение КОХ может быть произвольным мусором. А значит если взять маркер = 1 байт, то будет высока вероятность совпадения со случайными данными. Алгоритм при этом работать не перестанет, но будет медленнее находить дырку. Поэтому маркер у меня - некоторое значение, которое должно быть маловероятным в случайных данных.

 

Цитата

А то я так же заложил 4 байта и только в 1 поставил маркер, а остальные пока не используются, но периодически руки так и чешутся что бы что нибудь туда добавить(подумывал добавить информацию по битно, на записанные страницы, для более быстрого и простого поиска головы), но оказалось это лишнее, голова и так достаточно просто ищется.

Если вы туда ещё какие-то биты писать будете, непонятно как потом быстро дырку искать при старте? Вы уверены что поняли мой алгоритм работы?

 

PS: Пытаясь экономить на пуговицах, так и целиком надёжность работы можете угробить. Если уж сильно хочется ускорить старт системы хранения, то идти нужно в сторону распараллеливания чтения очередной страницы данных из флешь и поиска головы (или проверки на чистоту) в предыдущей странице. Это даст значительно бОльший эффект.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

void PutDataInFlashCache(uint8_t name,uint8_t data);   
uint8_t GetDataFromFlashCache(uint8_t name);

Так вот теперь встал вопрос, где должен находиться алгоритм с кольцевым буфером ? перед кэш  ? или  работать на основе кэш ?
Если перед кэшом, то как-то все мудрено получается, совсем не представляю как это должно выглядеть.
Если алгоритм с кольцевого буфера работает на основе кэша, то тут как бы проще,  тогда  у кольца получается своя байтова линейная адресация  допустим от 0 до 409600 (100 секторов по 4096) байтов (естественно с перебросом адреса в 0 при увеличении ядреса > 409600 ).  Но тогда где разместить весь алгоритм стирания сектора дырка, и вообще как кэш завернуть по кольцу.

В общем подразумеваю структуру модулей примерно такую:

Физические адреса Flash -> кэш -> кольцевой буфер -> файловая система (проверка CRC, шифрование COBS)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

jcxz, Можете подсказать как у вас идет кодирование CRC ? Сделал кодирование данных в COBS и запись, считывание и декодирование.

 

Кодирование COBS, сделал следующим образом, так как все данные известны, то при начальном вхождении пробегаюсь по массиву данных и нахожу позицию 0, и его выдаю из функции кодирования. Когда начил добавлять CRC возникла проблема что его нету в данных по которым идет кодирование, соответственно COBS  отрабатывает не совсем корректно.
Решение есть простое, перед тем как кодировать, посчитать в цикле CRC и добавить в конец данных, но не хотелось бы заморачиваться на увеличение данных под CRC когда закидываешь данные на запись.

Вот что получилось:

// массив данных TestDataWrite50 который надо записать, количество байт 50, суда совсем не хотелось влазить что бы выделять место под CRC
StreamWriteDataFlash(TEST1,TestDataWrite50,50);

Вот как я планировал сделать, но в данном случае CRC не правильно добавляется (Шапка COBS указывает на конец блока в 50 байт, без учета CRC)

//==================================================================================================
/*
    * @Описание:
    * @Параметр:
    * @Возврат:
*/
void StreamWriteDataFlash(uint8_t name,uint8_t *data,uint16_t lenght){
uint8_t Temp;
uint8_t LenBalance;
uint8_t Tempdata;
uint32_t CRC32=0;
uint16_t LenCobs=0;
    //------------------------------------------------
    ResetCrc();
    //------------------------------------------------
    LenCobs=InitCodeCOBS(data,lenght);
    //------------------------------------------------
    CalcShiftHead(name,LenCobs);
    //------------------------------------------------
    for(int16_t i = 0; i<lenght;i++){
        //------------------------------------------------
        Tempdata=data[i];
        Tread_CRC(Tempdata);    // CRC
        //------------------------------------------------
        Cobs_encode(Tempdata);  // COBS
        //------------------------------------------------
        if(IsCobsCacheFull()){
            Temp=PopCobsCache(); Temp=~Temp; PutDataInFlashCache(name,Temp);
            Temp=PopCobsCache(); Temp=~Temp; PutDataInFlashCache(name,Temp);
        }
    }
    //------------------------------------------------
    LenBalance=GetByteInCobsCache();
    if(LenBalance>0){
        for(uint8_t i=0;i<LenBalance;i++){
            Temp=PopCobsCache(); Temp=~Temp; PutDataInFlashCache(name,Temp);
        }
    }
    //------------------------------------------------
    //CRC32=GetCRC();
    CRC32=~0xAABBCCEE;
    //------------------------------------------------
    //----------В COBS буфер на 4 байта---------------
    //------------------------------------------------
    Tempdata=CRC32>>24; Cobs_encode(Tempdata);
    Tempdata=CRC32>>16; Cobs_encode(Tempdata);
    Tempdata=CRC32>>8;  Cobs_encode(Tempdata);
    Tempdata=CRC32;     Cobs_encode(Tempdata);
    if(IsCobsCacheFull()){
        Temp=PopCobsCache(); Temp=~Temp; PutDataInFlashCache(name,Temp);
        Temp=PopCobsCache(); Temp=~Temp; PutDataInFlashCache(name,Temp);
        Temp=PopCobsCache(); Temp=~Temp; PutDataInFlashCache(name,Temp);
        Temp=PopCobsCache(); Temp=~Temp; PutDataInFlashCache(name,Temp);
    }
    //------------------------------------------------
    PutDataInFlashCache(name,(uint8_t)~0); //  Разделитель !!!!!!!!
    WriteCache_sync(name);
    //------------------------------------------------
}

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 часов назад, pokk сказал:

jcxz, Можете подсказать как у вас идет кодирование CRC ? Сделал кодирование данных в COBS и запись, считывание и декодирование.

Не понимаю вопроса... :unknw:  Что значит "как"? Как обычно: считаю CRC, кладу её в кадр, кодирую его. А как ещё?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А как добавить CRC в кадр с минимальным, копирование данных ? (без промежуточного буфера)

5 minutes ago, jcxz said:

А как ещё? 

Да походу только так, а я губу раскатал, что так как работаем с потоком байт, то и высчитывать CRC по байтно , в момент кодирование.

PS:  А функцию считывание с какими параметрами запускаете ? Интересует момент, как сделан выбор продолжить считывания 2-3 блока или надо сбросить и начать с 1 блока. 

 

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

18 минут назад, pokk сказал:

А как добавить CRC в кадр с минимальным, копирование данных ? (без промежуточного буфера)

Да походу только так, а я губу раскатал, что так как работаем с потоком байт, то и высчитывать CRC по байтно , в момент кодирование.

У меня работа идёт со структурой, представляющей из себя конфиг устройства. Этот конфиг считывается в соответствующую переменную (структуру ) в ОЗУ, там с ним идёт работа (отображение, редактирование), потом он прям оттуда же и записывается обратно во флешь. Без промежуточных копирований. Правда создаются 2 копии структуры конфига, но это - для редактирования, точнее - возможности отката при редактировании.

Перед записью подсчитывается CRC (и сохраняется в саму структуру), добавляется к ней номер версии, затем кодируется COBS. Кодируется тоже - опять прямо в эту же область памяти, т.е.- поверх. Кодируется в один проход, потоково.

Так:

...
  shmem.cstor.payload.bound = shmem.cstor.payload.cfg.version;
  shmem.cstor.payload.crc = HCRC32(&shmem.cstor.payload.cfg, sizeof(shmem.cstor.payload.cfg), ~0);
  u8 *s = (u8 *)&shmem.cstor.payload, *s1 = &shmem.cstor.over[0];
  uint c, i = 0, n = sizeof(shmem.cstor.payload) - 1;
  u32 j;
  do {
    s1++;
    if (++i == 255) {
      *(s1++ - 255) = 0;
      i = 1;
    }
    if (!(c = *s++)) {
      *(s1 - i) = ~i;
      i = 0;
    }
    *s1 = ~c;
  } while ((int)--n >= 0);
  s1[1] = n;
  *(s1 - i) = ~(i + 1);
  n = s1 + 2 - &shmem.cstor.over[0];
...
  
union ShareMem { //разделяемая память
  ...
  struct { //для службы хранения конфигурации
    __packed struct Payload {
      CfgMain cfg;
      u32 crc;
      u8 bound;
    };
    u64 page[DFLASH_PAGE_SIZE / 8]; //для службы хранения конфигурации
    u8 over[CobsCodedOver(sizeof(Payload))];
    Payload payload;
    u8 split;
  } cstor;  
  ...
} shmem;

 

18 минут назад, pokk сказал:

PS:  А функцию считывание с какими параметрами запускаете ? Интересует момент, как сделан выбор продолжить считывания 2-3 блока или надо сбросить и начать с 1 блока. 

Читаю из флешь по-байтно. Ну т.е. - при пересечении границы страницы, она читается в кеш, а дальше уже оттуда функции декодирования и читает по-байтно. Дошла до следующей страницы - читает в кеш следующую.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 hours ago, jcxz said:

У меня работа идёт со структурой, представляющей из себя конфиг устройства.

Все понял, да походу к данным проще добавить CRC, чем внутри записи его реализовывать

10 hours ago, jcxz said:

u64 page[DFLASH_PAGE_SIZE / 8]; //для службы хранения конфигурации

А что тут находиться ? Как то тут много типов на увеличенный размер шифрованных данных.

10 hours ago, jcxz said:

Читаю из флешь по-байтно. Ну т.е. - при пересечении границы страницы, она читается в кеш, а дальше уже оттуда функции декодирования и читает по-байтно. Дошла до следующей страницы - читает в кеш следующую.

Нет нет, я про блок данных (конфигурации/журнала), допустим что было записано  5 аварий(разными блоками COBS) и теперь их надо прочитать, т.е с начала надо прочитать последнюю, аварию(блок COBS), и потом продолжить считать в глубь. И где производить сброс, что при следующий итерации считывание всех 5 аварий, надо считывать с начала, записи а не продолжать, считывать из глубины.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, pokk сказал:

А что тут находиться ? Как то тут много типов на увеличенный размер шифрованных данных.

кеш страницы.

Цитата

Нет нет, я про блок данных (конфигурации/журнала), допустим что было записано  5 аварий(разными блоками COBS) и теперь их надо прочитать, т.е с начала надо прочитать последнюю, аварию(блок COBS), и потом продолжить считать в глубь. И где производить сброс, что при следующий итерации считывание всех 5 аварий, надо считывать с начала, записи а не продолжать, считывать из глубины.

У меня читается только последняя валидная запись. Так как у меня конфиг. Он один. Если последняя запись не валидна - декодирую дальше, пока не найдётся валидная, ограничив весь процесс некоторым количеством байт (на тот редкий случай, если много-много раз был сбой питания при записи конфига; или на случай если конфига вообще не было и это первое включение девайса с мусором во флешь).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 3/19/2020 at 8:32 AM, jcxz said:

Также в каждом секторе для записи данных я использую только (РАЗМЕР_СЕКТОРА-4_БАЙТА) байт. А в 4 байтах (в конце сектора) храню маркер занятости сектора. Это для ускорения процедуры поиска стёртой дырки при старте системы хранения (чтобы уменьшать операции чтения). Т.е. - сначала я читаю эти 4 байта и по ним определю предварительное состояние стёртости сектора. И только когда по маркеру занятости сектора обнаружу границу перехода СТЁРТ/НЕ_СТЁРТ - только для такого стёртого сектора полностью его считываю и проверяю содержимое на равенство всех байт ==0. Также этот маркер я использую как маркер стёртого сектора. Процедура стирания сектора у меня 2-этапная: сперва в маркер занятости сектора (поверх старого значения занятости сектора) пишется спец. значение "сектор стирается", и только потом даётся команда на стирание сектора. Таким образом, если вдруг во время операции стирания (длительной) произойдёт сбой питания, то при следующем запуске системы хранения она обнаружит маркер стирания сектора и дотрёт его до конца.

Система хранения не требует никакого предварительного специального "форматирования" чипа и может начать работу с чипа заполненного произвольным мусором (если стёртая дырка не будет найдена при старте - считается что КОХ не инициализирована - тогда она инициализируется автоматически созданием этой самой дырки из одного сектора).

Добрый день, заинтересовал ваш алгоримт, сразу извините за "некропостинг", если тут это неприемлемо.

 

Что делать если этот стертый сектор окажется испорченным (не все стерто в нем и маркер занятости тоже с мусором). В этом случае потеряется положение головы (а значит и потеряем все сохраненные данные) и надо начинать с нуля?

 

Насколько надежна идентификация головы лишь по одному сектору? Ведь в случае проблем с этим сектором мы теряем положение головы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, turnon сказал:

Что делать если этот стертый сектор окажется испорченным (не все стерто в нем и маркер занятости тоже с мусором).

Так я же там описывал "как":

19.03.2020 в 08:32, jcxz сказал:

Процедура стирания сектора у меня 2-этапная: сперва в маркер занятости сектора (поверх старого значения занятости сектора) пишется спец. значение "сектор стирается", и только потом даётся команда на стирание сектора. Таким образом, если вдруг во время операции стирания (длительной) произойдёт сбой питания, то при следующем запуске системы хранения она обнаружит маркер стирания сектора и дотрёт его до конца.

И что значит "маркер занятости тоже с мусором"? Такого не может быть в моём алгоритме.

Цитата

Насколько надежна идентификация головы лишь по одному сектору? Ведь в случае проблем с этим сектором мы теряем положение головы.

Какие именно проблемы вы имеете в виду? Не понял.... :wacko2:

Пока флешь работоспособна (позволяет полностью стереть сектор) - нахождение головы абсолютно надёжно. Ну или я не вижу сценария работы, могущего привести к неисправности системы хранения.

Опишите конкретную последовательность событий, которая приведёт к сбою по Вашему мнению.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

5 hours ago, jcxz said:

Процедура стирания сектора у меня 2-этапная: сперва в маркер занятости сектора (поверх старого значения занятости сектора) пишется спец. значение "сектор стирается", и только потом даётся команда на стирание сектора. Таким образом, если вдруг во время операции стирания (длительной) произойдёт сбой питания, то при следующем запуске системы хранения она обнаружит маркер стирания сектора и дотрёт его до конца.

Например: записали маркер "сектор стирается", дали команду стирания, начало стирать, затерло маркер, но до конца сектор не стерло.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

48 минут назад, turnon сказал:

Например: записали маркер "сектор стирается", дали команду стирания, начало стирать, затерло маркер, но до конца сектор не стерло.

И что? При след. запуске проверка сектора покажет, что он не стёрт при маркере не равном корректному -> запустится стирание сектора заново.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...