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

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

4 часа назад, x893 сказал:

Всё это прекрасно, но что произойдет при плохом секторе ?

Что это такое "плохой"?

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

Наоборот, 2-3 сектора за глаза хватает, по этому и удивлялся что перетираем сразу 60% информации которая отображается, но хотя если взять десяток секторов то это 60% будет "100 летней" давности.

На 2-х секторах алгоритм работать не будет. На 3-х - не знаю, надо смотреть. А зачем такая экономия при нынешних объёмах флеши?

Я писал данный алгоритм из расчёта уменьшения износа флеши при частой записи, поэтому у меня много секторов в кольце. Когда мало - даже не рассматривал.

 

Кроме того: я создавал этот алгоритм для хранения главным образом структуры конфигурации прибора; формат и размер которой со временем к новым версиям может измениться и заранее не известны. Этот алгоритм позволяет такое без проблем.

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


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

On 3/20/2020 at 3:00 AM, jcxz said:

Кроме того: я создавал этот алгоритм для хранения главным образом структуры конфигурации прибора

А как у вас происходит перезапись, одного параметра в конфигурации ? Записью всей структуры данных(конфигурации) как новый блок данных?
Добавление аварии журнала аварий и лог хотел сделать "отложенной" операцией (установкой данных в очередь), с последующим считыванием из задачи на запись, а вот как быть с файлом конфигурации, он может быть достаточно большой что бы при каждом изменение его в очередь пихать (жалко мне 300-900 байт).  Хотя запись конфигурации можно сделать напрямую и не откладывать (естественно дождавшись), но тогда пользовательский интерфейс будет подтормаживать на момент записи (максимум 0.5с не заметно же ? )

Кстати разделение на несколько файлов как лучше сделать ?
Разбить всю FLASH на 3 области и в каждом, запустить свой кольцевой буфер или же все в одном с пометкой в блоке данных к какому файлу относился записанный блок данных ?

Изменено пользователем pokk

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


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

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

А как у вас происходит перезапись, одного параметра в конфигурации ? Записью всей структуры данных(конфигурации) как новый блок данных?

да

Цитата

Добавление аварии журнала аварий и лог хотел сделать "отложенной" операцией (установкой данных в очередь), с последующим считыванием из задачи на запись, а вот как быть с файлом конфигурации, он может быть достаточно большой что бы при каждом изменение его в очередь пихать (жалко мне 300-900 байт).

Жалко чего? Не понятно...

У меня объём конфига примерно такой-же. Объём флеша у меня 512 секторов по 256К каждый. Допустим выделим 16 секторов под кольцо с конфигом (16 секторов - ерунда ведь по сравнению со всем объёмом в 512 секторов?). 1e+5 - число циклов перезаписи. Считаем: 0x40000*16/900*100000=~466e+6 - столько циклов записи конфига выдержит кольцевой буфер флешки. У Вас реально может быть больше и боитесь что протрётся???  :shok:

Цитата

Хотя запись конфигурации можно сделать напрямую и не откладывать (естественно дождавшись), но тогда пользовательский интерфейс будет подтормаживать на момент записи (максимум 0.5с не заметно же ? )

Почему должен "подтормаживать"? Тоже непонятно...

Цитата

Кстати разделение на несколько файлов как лучше сделать ?
Разбить всю FLASH на 3 области и в каждом, запустить свой кольцевой буфер

Конечно так.

Цитата

или же все в одном с пометкой в блоке данных к какому файлу относился записанный блок данных ?

Если у Вас при каждом записи лога например, будет одновременно переписываться остальные 2 файла данных - можно. Иначе - не будет работать. Сами подумайте почему.

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


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

11 hours ago, jcxz said:

Жалко чего? Не понятно...

Не flash а озу, для организации очереди, если всю конфигурацию передавать через очередь задаче которая будет записывать.
Если же передавать указатель на конфигурацию, то надо блокировать задачу из которой вызывается функция на запись, что бы данные в момент записи не менялись.
Соответственно от суда и "притормаживания" интерфейса, на время записи.

Кстати, как правильнее передавать данные разного размера в очередь (FreeRTOS)? (т.е одна задача принимает данные, а другие здачи передают не фиксированный размер данных, к примеру строки)
Адаптировать функцию под любой тип  через union ?
Или создать несколько очередей по свой тип данных и в них записывать, а в задаче на запись ожидать данные из любой очереди (как ?).

 

 

 

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


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

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

Не flash а озу, для организации очереди, если всю конфигурацию передавать через очередь задаче которая будет записывать.
Если же передавать указатель на конфигурацию, то надо блокировать задачу из которой вызывается функция на запись, что бы данные в момент записи не менялись.
Соответственно от суда и "притормаживания" интерфейса, на время записи.

Вы хотя-бы откройте мануал на ваш flash и посмотрите время записи страницы. А то имхо - печётесь о каких-то высосанных из пальца проблемах.

Например в моём чипе (S70FL01GS) время записи страницы == 340мкс. Т.е. на запись 900 байт потребуется 340мкс + время пересылки по SPI (десятки мкс). Вы реально сможете заметить такую задержку???

Зачем какие-то очереди для этого - тоже не понятно. Зачем усложнять?

Если всё-таки и задержка в сотни мкс критична - так можно придумать целую кучу способов чтобы не блокировать задачу юзер-интерфейса на время записи или стирания, например: делать это в отдельной задаче, или вообще в ISR. Стирать секторы в "дырке" кольца - для этого тоже не нужно блокировать никакую задачу: дал команду, а дальше в ISR периодически опрашивать регистр статуса на предмет завершения операции. И связывать с задачей пользовательского интерфейса это тоже не нужно. Зачем?

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

а в задаче на запись ожидать данные из любой очереди (как ?).

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

Да ещё тысячу способов можно придумать - зависит от вашего алгоритма работы всей системы.

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


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

11 hours ago, jcxz said:

Вы хотя-бы откройте мануал на ваш flash и посмотрите время записи страницы

Я ориентировался на самое большое время, это стирание страницы 80-200ms, когда происходит переход с одного сектора на другой, причем стирать надо 2 сектора для дырки и новый куда буду записываться данные, отсюда и задержка около 400 ms.

11 hours ago, jcxz said:

Если всё-таки и задержка в сотни мкс критична - так можно придумать целую кучу способов чтобы не блокировать задачу юзер-интерфейса на время записи или стирания

У меня задача юзер-интерфейса выглядит как в web странице, вводятся данные и нажимается кнопка сохранить, после чего формируется xml запрос с веденным данными и отправляется в процессор. В процессоре, вызывается  Callback функция соответственной кнопке web. Внутри функции происходит последовательное модификация конфигурация, запись, верификация(что данные корректно записаны), и возврат статуса записи в web(ответ на запрос) по которому отображается сообщение как произошла запись. В данном случае через 13 сохранений, сообщение об записи отобразиться  с задержкой( 400ms), вот это я и назвал "подтормаживание". Ну и задача  Callback блокируется на запись, что бы потом отправить подтверждение + зашита от модификации конфигурации, из другого  Callback.
Особо от этого ни куда не денешься(и буферы тут да не помогают) , по факту стирание странице будет быстрее, и надеюсь это будет не так заметно.

PS: а время стирания страници, случайно не зависит, количеста записанной информации в старнице? Пока писал функцию заметил что она выполняеться в 40ms  в 2 раза меньше чем указанно в даташите, но там у меня пару 10 байт записанно только было. 

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


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

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

Я ориентировался на самое большое время, это стирание страницы 80-200ms, когда происходит переход с одного сектора на другой, причем стирать надо 2 сектора для дырки и новый куда буду записываться данные, отсюда и задержка около 400 ms.

Так зачем берёте такой алгоритм где нужно 2 стирать? Я же вам описывал свой - там всего один за раз нужно стирать.

 

Цитата

В данном случае через 13 сохранений, сообщение об записи отобразиться  с задержкой( 400ms), вот это я и назвал "подтормаживание".

И опять - зачем берёте такой алгоритм, где нужны 13 стираний???

Я Вас не понимаю: сами же выбираете МК, в с урезанной периферией, а потом думаете как это обойти костылями (хотя есть куча МК с нормальной периферией); выбираете алгоритм хранения в котором требуется 13 сохранений чего-то куда и 2 стирания сектора - и жалуетесь на это. Вы же сами такой выбрали! Или кто-то вас заставлял?  :unknw:

 

PS: Прям как в анекдоте: "Доктор, почему мне больно когда я так делаю?" "Пациент, так не делайте так и не будет больно".

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


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

1 hour ago, jcxz said:

Я же вам описывал свой - там всего один за раз нужно стирать.

Упс мой косяк, сектор с дыркой хотел стерать, а не записывать. 

1 hour ago, jcxz said:

И опять - зачем берёте такой алгоритм, где нужны 13 стираний??? 

Откуда 13 стираний ??? Я описывал ситуацию что каждая 13 итерация сохраненя будет происходит в новый сектор  (размер сектор у меня 4096, размер конфигурации примерно 300 байт 4096/300=13)

Изменено пользователем pokk

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


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

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

Откуда 13 стираний ??? Я описывал ситуацию что каждая 13 итерация сохраненя будет происходит в новый сектор  (размер сектор у меня 4096, размер конфигурации примерно 300 байт 4096/300=13)

Опечатался. Хотел написать "сохранений". Если беспокоитесь о задержке каждые 13 записей страниц, то стирайте дополнительный сектор в дырке заранее, в фоне, например пока конфиг редактируется юзером. Или сразу после того как сохранили очередной конфиг и оказалось, что при следующем сохранении может потребоваться стирание. Мой алгоритм требует только чтобы дырка была >= 1 сектора, максимальный её размер ничем не ограничен (ну т.е. ограничен только минимальным требуемым размером записанной области).

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


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

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

On 3/19/2020 at 1:32 PM, jcxz said:

Данные перед записью в КОХ, сперва дополняются CRC, обрамляются маркерами начала и конца блока данных (не равными 0), затем кодируются по протоколу COBS и пишутся во флешь закодированными COBS

Для чего используються маркер начала и конца блока данных? И как его макеры правильно добавлять что бы они не совпали а данными?
Если декодировать блок данных из COBS , то получаем сразу данные + CRC

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


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

Я добавил для упрощения алгоритма кодирования/декодирования. Да - только маркер конца добавил, в начале - нет. Маркер - любое число не равное 0.

Может можно и обойтись, но 1 байт на фоне КБ данных роли не играет.

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


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

On 3/19/2020 at 1:32 PM, jcxz said:

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

А как у вас происходит поиск точного положения головы (смешение внутри сектора)? Как правильно детектировать что после N-го нуля дальше идут не данные а свободная область памяти.
1) Если искать переход  0/0xFF то, 0xFF может быть началом данных следующего блока COBS.
2) Если добавить начальный маркер(отличный от нуля и 0xFF) блока COBS, то тут как бы да переход между 0 и 0xFF, уже говорит о том что это не данные, а свободное место, но положение головы все равно по нему не получается найти, так как такие "хвосты" свободной памяти  могут быть в нескольких страницах (когда блок COBS не влез в страницу, и полностью перенеся на следующую страницу).

Запись блока COBS собирался делать по странично, если не влезает, на текущую страницу, то записываем на следующую, что бы не разбивать блок COBS и не усложнять алгоритм.

PS: Придумал что хвосты можно затереть нулем(разделителем).

А если блок COBS, больше размера страницы..., походу придется с разбивкой блока COBS.

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


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

В 06.04.2020 в 12:46, pokk сказал:

А как у вас происходит поиск точного положения головы (смешение внутри сектора)? Как правильно детектировать что после N-го нуля дальше идут не данные а свободная область памяти.
1) Если искать переход  0/0xFF то, 0xFF может быть началом данных следующего блока COBS.

Почему "0/0xFF"? Ищу переход "==0/!=0" естественно. Это следует из метода кодирования.

В 06.04.2020 в 12:46, pokk сказал:

2) Если добавить начальный маркер(отличный от нуля и 0xFF) блока COBS, то тут как бы да переход между 0 и 0xFF,

Что-то мне кажется Вы не поняли смысла применения COBS... :unknw:  Да и самого COBS. Зачем там что-то добавлять, если сам COBS гарантирует что в кодированных данных не будет байтов ==0. Соответствеено - стёртые области ==0, заполненные данными !=0. Два блока COBS разделяются байтом стёртых данных (==0). Соответственно - когда хотим записать новый блок после последнего, то отступаем от него 1 байт (который останется с исходным заполнением ==0) и пишем новый блок.

В 06.04.2020 в 12:46, pokk сказал:

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

Опять не поняли моего метода записи и смысла применения COBS.... :unknw:

У меня нет никаких "хвостов" и переносов на след.страницу. Зачем это делать??? COBS образует байтовый поток со значением всех байт отличным от 0. Соответственно и пишется кодированный блок именно как байтовый поток (аналогично передаче в интерфейс связи). И кодирование/декодирование COBS-блоков у меня тоже делается в потоковом режиме.

Именно поэтому я и применил COBS.

Я же вам выше схемы приводил. Где Вы там нашли какие-то "хвосты" и "переносы"???

В 06.04.2020 в 12:46, pokk сказал:

Запись блока COBS собирался делать по странично, если не влезает, на текущую страницу, то записываем на следующую, что бы не разбивать блок COBS и не усложнять алгоритм.

Этим Вы наоборот всё зачем-то усложняете. Не понятно зачем... COBS обеспечивает кодирование/декодирование в потоковом режиме, по-байтно. Зачем что-то переносить - не понятно....

В 06.04.2020 в 12:46, pokk сказал:

А если блок COBS, больше размера страницы..., походу придется с разбивкой блока COBS.

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

Всё просто!

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


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

5 hours ago, jcxz said:

Зачем там что-то добавлять, если сам COBS гарантирует что в кодированных данных не будет байтов ==0. Соответствеено - стёртые области ==0, заполненные данными !=0.

Почему стертые области ==0 ? После стирания страницы flash вся страница становиться ==0xFF, и записью сбрасываем биты.

5 hours ago, jcxz said:

Почему "0/0xFF"? Ищу переход "==0/!=0" естественно. Это следует из метода кодирования.

Расположение блоков COBS внутри сектора сделал так(похоже не много не правильно, но пока не суть)(запись идет от 0 адреса сектора к максимальному):

|Data1|0|data2|0|FFFFFF|MARKER|

Где data1/2 - блок данных COBS;  0- разделитель между блоками; FFFFFF - свободное место для записи; MARKER- 4 байта в конце сектора
Соответственно если искать переход между "==0/!=0", то находим  блок с данным Data2, а надо найти свободное место, для записи нового блока данных(COBS).

Но походу надо поменять порядок записи, от начала маркера и вниз, с SWAP данными (нулевой элемент массива COBS, у MARKER с дальнейшим уменьшением адреса к 0), тем самым декодирование данных будет проще (сразу получаем указатель на конец блока COBS и его декодируем).

Но в любом случае по переходу  "==0/!=0" находиться  следующий блок данных, а не последний.

5 hours ago, jcxz said:

У меня нет никаких "хвостов" и переносов на след.страницу. Зачем это делать???

Да, сделала именно так, байтовый поток пишется в кэш, а дальше по заполнению кэша записываем в flash (либо по завершению функции записи в кэш, если данные < размера кэш ), тем самым полностью абстрагировавшись, от страничной разметки сектора и не важно, какой длины блок записи.
При пересечении сектора1/сектора2 так же ? Пока я планировал, перенести блок COBS в другой сектор, если он не влезает,а в том секторе где был хвост забить нулями, тем самым при стирании сектора у нас не возникнет ломаного блока COBS, хотя это самый давнишний блок,можно и потерять его.

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


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

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

Почему стертые области ==0 ? После стирания страницы flash вся страница становиться ==0xFF, и записью сбрасываем биты.

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

Цитата

Но походу надо поменять порядок записи, от начала маркера и вниз, с SWAP данными (нулевой элемент массива COBS, у MARKER с дальнейшим уменьшением адреса к 0), тем самым декодирование данных будет проще (сразу получаем указатель на конец блока COBS и его декодируем).

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

Цитата

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

При пересечении сектора1/сектора2 так же ?

Пересечение границы сектора отличается от пересечения границы страницы только тем, что нужно учитывать что последняя страница сектора на 4 байта короче остальных, и что перед пересечением (именно - перед!) нужно обеспечить требование минимальной дырки стёртых секторов. Впрочем - если у вас не может быть внезапного отключения питания или перезагрузки, то на порядок можно забить и стирать после.

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

Цитата

Пока я планировал, перенести блок COBS в другой сектор, если он не влезает,а в том секторе где был хвост забить нулями, тем самым при стирании сектора у нас не возникнет ломаного блока COBS, хотя это самый давнишний блок,можно и потерять его.

Во-первых: в чём проблема что записываемых блок пересекает границу сектора? Ну и пусть. Кодирование и декодирование - потоковые, им без разницы где граница. Она отслеживается на другом уровне - на уровне записи страницы.

Во-вторых: Зачем сперва писать, а потом что-то "забивать"? Ведь в COBS можно сразу вычислить размер кодированных данных - это одно из его преимуществ. Да и как Вы собираетесь забивать нулями ? Ведь это будет у вас инвертированное 0xFF - а это значение может быть в блоке данных. У меня в моей системе хранения есть только стёртое значение (==0 - свободное место или межблочный интервал) и нестёртое (!=0 - данные блока). Никаких других, каких-то маркеров - нет, ибо не нужно.

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


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

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

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

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

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

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

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

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

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

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