Jump to content

    
pokk

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

Recommended Posts

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 байтах маркера сектора ?

Share this post


Link to post
Share on other sites
В 09.04.2020 в 13:25, pokk сказал:

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

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

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

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

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

Share this post


Link to post
Share on other sites
On 4/10/2020 at 4:13 PM, jcxz said:

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

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

Share this post


Link to post
Share on other sites
34 минуты назад, pokk сказал:

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

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

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

 

Цитата

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

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

 

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

Share this post


Link to post
Share on other sites

Подскажите, как ваш алгоритм на абстракции (модули) делиться.
Сначала у меня возникали трудности с адресацией страницы, секторов ...  но потом представил что  у флеша, есть буфер 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)

Share this post


Link to post
Share on other sites

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);
    //------------------------------------------------
}

 

 

Share this post


Link to post
Share on other sites
11 часов назад, pokk сказал:

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

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

Share this post


Link to post
Share on other sites

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

5 minutes ago, jcxz said:

А как ещё? 

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

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

 

 

 

 

Share this post


Link to post
Share on other sites
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 блока. 

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

Share this post


Link to post
Share on other sites
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 аварий, надо считывать с начала, записи а не продолжать, считывать из глубины.

Share this post


Link to post
Share on other sites
2 часа назад, pokk сказал:

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

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

Цитата

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

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.