Как составить алгоритм флэш памяти

Твердотельные накопители. Внутреннее устройство и принципы их построения

Время на прочтение
8 мин

Количество просмотров 17K

Наши клиенты часто интересуются внутренним устройством твердотельных накопителей. Чем определяется надежность накопителей? Почему нельзя хранить важные данные на флешках, и бывают ли надежные флешки? Зачем в контроллеры SSD-накопителей ставят многоядерные процессоры? Почему флешки больших объемов пишут быстро, а малых объемов медленно? Многие вопросы требуют длительного погружения в тему, а некоторые отпадают сами собой после небольшого ликбеза по внутреннему устройству твердотельных накопителей, о том и пойдет речь.

image

USB-флеш-накопитель, карта памяти microSD/SD, SSD-накопитель представляют собой, на первый взгляд, разные устройства, хотя на самом деле являются «близкими родственниками». Все три типа устройств относятся к твердотельным накопителям на основе NAND флеш-памяти. Забавный факт, именуемые в быту «SSD-диски» не являются дисками в прямом смысле. Название «диск» исторически унаследовано от жестких магнитных дисков (Hard Disk Drive — HDD).

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

В состав твердотельных накопителей входят:

  • Контроллер – основной элемент твердотельного накопителя, выполняющий функции чтения, записи, контроля целостности данных и исправления битовых ошибок, возникающих в структуре NAND флеш-памяти. С одной стороны, контроллер подключается к хосту через внешний интерфейс SATA / USB / SD / PCIe, с другой – к микросхемам NAND флеш-памяти через интерфейс подключения ONFI / Toggle
  • NAND флеш-память – массив микросхем, формирующих объем памяти накопителя

image

Для понимания задач, выполняемых контроллером, необходимо иметь базовые представления об организации NAND флеш-памяти. Микросхемы NAND флеш-памяти достаточно специфичны в использовании, начиная от интерфейса подключения и заканчивая достоверностью хранения информации.

NAND флеш-память

Микросхема NAND флеш-памяти – хранилище для информации пользователя (фотографии, фильмы, документы, системные файлы операционной системы и т.п.).

Остановимся на вопросах интерфейса доступа к данным и актуальных для твердотельных накопителей проблемах сохранности информации в NAND флеш-памяти.

Микросхему NAND флеш-памяти можно сравнить с архивом бумажных документов. Наподобие того, как бумаги хранятся в архиве, также электронные документы хранятся в памяти микросхем.

image

Важнейшими функциями любой системы хранения данных и архива являются:

  • Хранение данных — архив должен иметь условия, обеспечивающие сохранность бумаг
  • Доступ к информации — библиотекарь должен иметь возможность поработать с нужным документом, иначе архив бесполезен

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

Однако система хранения данных во флеш-памяти имеет следующие особенности:

  1. Заряд с затвора способен «утекать» со временем, что рано или поздно приведет к изменению данных. Например, как чернила на архивных бумагах со временем выцветают или растекаются, превращаясь в неразборчивые пятна. Чем дольше хранятся данные, тем меньше вероятность их потом прочитать.
  2. После записи одними и теми же цепями одинакового логического уровня заряда в разные ячейки из-за технологического разброса параметров транзисторов появится вероятность прочитать оттуда разные по величине значения заряда. (Бумага может иметь разные свойства впитывания и растекания чернил. Мелкий текст, написанный фломастером, не на каждой бумаге удастся прочитать.)
  3. Цепи записи и чтения заряда также не идеальны и имеют технологический разброс уровней напряжения программирования и порогов чтения логических уровней. (Похоже на то, как на бумажных носителях разные библиотекари по-разному могут разобрать текст, записанный разными авторами, потому что почерк у всех разный.)

image

Целью технологии разработки микросхем памяти является создание флеш-памяти с максимальным соотношением «качество/цена». Для хранения данных в накопителях
NAND флеш-память является весьма достойным решением по этому соотношению, о чем говорит рост рынка накопителей. Но соотношение «качество/цена» не то же самое, что «качество». От площади кристалла микросхемы прямым образом зависит стоимость микросхемы. Поэтому производители флеш-памяти постоянно стремятся увеличить плотность хранения данных в микросхемах памяти. Увеличение плотности ячеек памяти достигается за счет уменьшения размера самих ячеек, так и за счет объединения цепей записи и чтения зарядов ячеек. Причем, второе, в свою очередь, создаёт некоторые сложности в доступе к хранимым данным.

Понятия интерфейса доступа и правила доступа к NAND флеш-памяти:

  • Блок. Весь объем микросхемы разбит на блоки. Объем блока составляет порядка единиц мегабайт. (Например, в архиве, это книга или блокнот в переплете с прошитыми листами)
  • Стирание блока. Блок может быть стёрт, при этом каждый бит информации в нем будет установлен в «1». Нельзя стереть только часть блока. (Например, книгу можно выкинуть, но нельзя вырвать из книги лист, не нарушив целостность книги)
  • Страница. Блок разделен на страницы размером порядка десятков килобайт. (Например, книги и блокноты тоже имеют страницы)
  • Программирование страницы. В NAND флеш-память могут быть записаны (запрограммированы) одновременно данные всей страницы, биты устанавливаются при программировании в значения «0» или «1». (Например, чистые страницы книги или блокнота можно заполнить информацией только единожды)
  • Порядок программирования страниц. Страницы в пределах блока должны программироваться строго в порядке возрастания их номеров. (Например, последовательная запись информации в книгу или блокнот)
  • Порядок перезаписи. Каждая страница может быть запрограммирована только один раз. Для повторного программирования страницы необходимо стереть полностью блок. (Например, если в уже готовом издании книги необходимо заменить страницу, то придется перепечатать всю книгу и заново сделать переплет)

Перечисленные выше особенности NAND флеш-памяти выглядят довольно безобидно, на первый взгляд, но множество вопросов возникает при первой же попытке перезаписать часть данных (например, один сектор). Задачи записи и чтения непосредственно NAND флеш-памяти решает контроллер.

Контроллер

Контроллер обеспечивает подключение к хосту, и, собственно, представляется накопителем. В общем виде архитектура любого контроллера любого накопителя выглядит типично: имеются аппаратные блоки интерфейсов для обмена данными с хостом (EXT_IF) и NAND-памятью (FLASH_IF). Между блоками интерфейсов в обязательном порядке присутствует буферная оперативная память (MEM_BUF), предназначенная для оперативного кэширования данных и сглаживания потока записи/чтения данных. В контроллерах USB-накопителей буферная память составляет десятки килобайт и размещается непосредственно в самом контроллере. В высокопроизводительных системах, таких как SSD-накопители, используются внешние микросхемы памяти. Данные между интерфейсными блоками и буферной памятью передаются без непосредственного участия процессора по каналам прямого доступа к памяти (DMA, direct memory access). Участие процессора в передаче данных заключается в настройке каналов DMA и синхронизации работы блоков.

image

Любой из подключаемых накопителей является «блочным устройством». Блочное устройство – это устройство, в котором данные хранятся в виде последовательной цепочки логических блоков, обращение к которым возможно по адресу Logical Block Address (LBA). Большинство устройств поддерживает размер блока в 512 байт, который называется «сектор». Сектор является минимальной дискретной информацией, которая может быть перезаписана на блочном устройстве. То есть для замены одного байта хост должен передать целиком сектор на устройство хранения.

Для программистов, работающих с накопителями на физическом уровне (запись/чтение по физическим адресам), вполне очевидно, что запись и чтение одного сектора не должны затрагивать ни коим образом данные других секторов. Также это очевидно для пользователя блочного устройства, но NAND флеш-память, как было сказано ранее, не предоставляет такой возможности. Для того, чтобы заменить один сектор в массиве памяти NAND, необходимо перезаписать весь блок NAND флеш-памяти, размер которого составляет мегабайты. Такой метод решения задачи крайне неэффективен, так как приводит к недопустимому снижению скорости записи в NAND флеш-память относительно ее потенциальных возможностей. К тому же так как операционная система часто пишет в одни и те же адреса устройства (например, записи FAT), то блоки NAND флеш-памяти быстро придут в негодность из-за ограниченного ресурса на стирание. Чтобы увеличить скорость записи/чтения данных и продлить срок службы NAND флеш-памяти, применяются более хитрые методы адресации, переводящие логические адреса (LBA) накопителя в физические адреса NAND флеш-памяти. Алгоритм трансляции адресов NAND флеш-памяти в зарубежной литературе называется Flash Translation Layer (FTL). Если посмотреть описание контроллеров SSD (например, компании Marvell), то можно увидеть, что в состав контроллера входит до 4 процессорных ядер. Такая высокопроизводительная система в SSD необходима в первую очередь для расчета адресов трансляции.

FTL, логическая и физическая адресация

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

  • скорость записи/чтения данных по последовательным адресам
  • скорость записи/чтения данных по случайным адресам
  • срок службы накопителя
  • надежность хранения данных
  • объем и тип применяемой памяти

Алгоритмы FTL могут отличаться как для разных типов накопителей (SD/USB/SSD), так и для одного типа. Например, существует разделение SSD по назначению: для серверов, для кэширования в персональном компьютере (ПК), для ноутбуков, для настольных ПК. При этом для каждого применения существуют свои приоритеты в требованиях и свои варианты реализации FTL. Каждый вариант FTL представляет собой компромисс параметров с учетом приоритетов конкретного применения накопителя.

Суть FTL – преобразование логических адресов устройства в физичеcкие адреса NAND флеш-памяти.

image

Каждому логическому LBA ставится область памяти в NAND. Это называется Logical Unit Number Table (LUN table). Для перезаписи логического блока стираются данные свободного физического блока, после чего происходит замена в LUN table, что создает для пользователя видимость перезаписи. От того, какого размера выбираются логические блоки, зависит размер LUN table (не во всех устройствах целесообразно иметь большие объемы памяти). Существуют два принципиально разных подхода в реализации FTL: блочная адресация и страничная адресация.

Блочная адресация

В блочной адресации размер логического блока соответствует размеру физического блока. В свою очередь в массиве LUN table с индексом, равным адресу логического блока, указывается значение, соответствующее адресу физического блока. Для изменения части блока необходимо переписать весь блок целиком.

Преимуществом блочной адресации является малый размер LUN table, что актуально для устройств с малым объемом оперативной памяти, таких как USB-флеш-накопитель или карта памяти microSD. Недостатком является то, что размер блока довольно большой (порядка мегабайтов), и для перезаписи малых объемов данных (например, 512 байт) приходится переписывать весь блок.

Страничная адресация

В страничной адресации в LUN table сохраняются адреса физических страниц. Размер логического блока при этом составляет порядка десятков килобайт.

Преимуществом страничной адресации является высокая скорость перезаписи данных, как последовательно, так и в случайном порядке. Недостатком является большой размер LUN table. Поэтому в состав большинства SSD входит микросхема оперативной памяти, объемом более 100 Мбайт.

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


Такова структура и принципы работы накопителей на NAND флеш-памяти. О методах адресации FTL написано большое количество научных статьей и запатентовано множество решений. Производители контроллеров непрерывно работают над совершенствованием ПО даже после выхода продукта на рынок. В целом, программное обеспечение является неотъемлемой частью контроллера, и его описание заслуживает отдельной статьи.

Микроконтроллеры
AVR
имеют от 1кБ до 128Кб перепрограммируемой
флэш памяти программ и от 128 байт до 4
Кб энергонезависимой памяти данных.
При поставке флэш память и память данных
стерты (содержат $FF) и готовы к
программированию. Микросхемы поддерживают
высоковольтный (12В) параллельный режим
программирования и низковольтный режим
последовательного программирования.
Напряжение +12В используется только для
разрешения программирования, этот вывод
не потребляет тока. Последовательный
режим программирования предусмотрен
для загрузки программы и данных в
микроконтроллер в системе пользователя
(внутрисистемное программирование).

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

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

Программирование
битов блокировки памяти

Микроконтроллер
AT90S2313 имеет два бита блокировки, которые
могут быть оставлены незапрограммированными
(1) или программироваться (0), при этом
достигаются свойства приведенные в
таблице 5.1.

Таблица
5.1 Назначение битов защиты

Биты
блокировки

Тип
защиты

Режим

LB1

LB2

1

1

1

защита
не установлена

2

0

1

дальнейшее
программирование флэш памяти и EEPROM
запрещено

3

0

0

как
режим 2, но запрещено и чтение

Примечание:
биты блокировки стираются только при
полном стирании памяти

Код
устройства

Все
микроконтроллеры фирмы Atmel имеют
3-байтовый сигнатурный код, по которому
идентифицируется устройство. Этот код
может быть прочитан в параллельном и
последовательном режимах. Эти три байта
размещены в отдельном адресном
пространстве и для AT90S2313 имеют следующие
значения:

1.
$000: $1E — код производителя — Atmel

2.
$001: $91 — 2 кБ флэш памяти

3.
$002: $01 — при $01=$91 — м/сх AT90S2313

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

ПОСЛЕДОВАТЕЛЬНАЯ
ЗАГРУЗКА

Как
память программ, так и память данных
могут быть запрограммированы с
использованием последовательной шины
SPI, при этом вывод RESET должен быть подключен
к земле. Последовательный интерфейс
работает с выводами SCK, MOSI (вход) и MISO
(выход). После подачи низкого уровня на
RESET перепрограммированием/стиранием
необходимо исполнить команду разрешения
программирования.

При
программировании памяти данных, во
внутренний алгоритм программирования
встроен цикл стирания (только при
последовательном программировании),
поэтому нет необходимости в выполнении
команды стирания памяти. Команда стирания
микросхемы переводит все ячейки памяти
программ и данных в состояние $FF.

Флэш
память программ и энергонезависимая
память данных имеют отдельное адресное
пространство: $0000 — $хххх для памяти
программ и $000- $ххх для памяти данных.

При
программировании необходимо подавать
внешнюю тактовую частоту на вывод XTAL1
или подключить внешний тактовый генератор
к выводам XTAL1 и XTAL2. Минимальные длительности
низкого и высокого уровня сигнала SCK
определены следующим образом:

Низкий:
> 2 периодов XTAL1

Высокий:
> 2 периодов XTAL1

ПОДТВЕРЖДЕНИЕ
ДАННЫХ

До
завершения программировании нового
байта, при чтении из памяти будет читаться
значение $7F. Когда микросхема будет
готова для записи следующего байта,
будет выдаваться записанное значение.
Это используется для определения
момента, когда можно записывать следующий
байт. Этот способ не будет работать для
байта $7F, поэтому для записи этого числа
перед программированием следующего
байта придется выждать по крайней мере
4mS. Поскольку после стирания ячейки
всегда устанавливаются в $FF, при
программировании ячейки, содержащие
$FF можно пропускать. Это не применимо
при перезаписи EEPROM без стирания памяти
программ. В этом случае подтверждение
не работает для данных $7F и $FF, для этих
значений перед программированием
следующего байта необходимо выждать
4mS.

АЛГОРИТМ
ПОСЛЕДОВАТЕЛЬНОГО ПРОГРАММИРОВАНИЯ

Для
программирования и проверки микросхемыв
режиме последовательного программирования
рекомендуется следующая последовательность
действий :

1.
Последовательность включения питания:

Подать
напряжение питания между VCC и GND, при
этом RESET и SCK должны быть установлены в
‘0’. (Если программатор не гарантирует
установки SCK в ‘0’ при подаче питания,
после того как на SCK будет установлен
‘0’, на RESET необходимо подать положительный
импульс. Если кварцевый резонатор не
подключен к выводам XTAL1 и XTAL2, подайте
частоту от 0 до 16 MHz на вывод XTAL1.

2.
Подождать 20 mS и разрешить последовательное
программирование послав команду
разрешения на вывод MOSI/PB5.

3.
При посылке трехбайтовой последовательности
разрешения программирования, второй
байт последовательности ($53) будет
возвращен при посылке третьего байта.
В любом случае, должны быть посланы все
четыре байта команды. Если число $53 не
получено обратно, необходимо подать
положительный импульс на SCK и повторить
команду разрешения программирования.
Если после 32 попыток не будет получено
число $53, микросхема неисправна.

4.
При подаче команды стирания стирать
микросхему (всегда при программировании
памяти программ), подождать 10 mS, выдать
положительный импульс на RESET и повторить
шаг 2.

5.
Память программ и память данных
программируются по одному байту выдачей
адреса и данных в команде записи. Перед
записью новых данных в EEPROM ячейки памяти
перед записью новых данных автоматически
стираются. Чтобы определить время, когда
можно записывать следующий байт,
используется подтверждение данных. При
записи предварительно стертой микросхемы
записывать ячейки содержащие $FF не
обязательно. При записи в ячейку числа
$7F, перед программированием следующего
адреса необходимо выждать 4 mS.

6.
Любую ячейку памяти можно проверить
используя команду чтения, которая выдает
содержимое указанной ячейки на
последовательный вывод MISO/PB6.

7.
По окончанию программирования вывод
RESET может быть установлен в ‘1’ для
возобновления нормальной работы схемы.

6.
Последовательность выключения питания
(если необходимо) Установить XTAL1 в ‘0’
(если не используется кварцевый резонатор)

Установить
RESET в ‘1’.

Отключить
питание

Возможность
внутрисхемного программирования
микроконтроллеров AVR по последовательному
синхронному интерфейсу SPI позволяет
создавать простые и недорогие
программаторы. Такие программаторы
называют внутрисхемными (In-System Programmer,
ISP).

При
выполнении лабораторного практикума
для программирования микроконтроллеров
используется на наш взгляд наиболее
простая и доступная программа AVReAL,
которая поддерживает выходной формат
файла AVRStudio
и некоторым недостатком которой является
то, что она не входит в число утилит
AVRStudio
и запускается из системной оболочки.
Для более подробного изучения способов
и средств программирования микроконтроллеров
AVR
рекомендуем обратиться на
http://www.atmel.com/atmel/acrobat/doc0943.pdf.45

Схема
переходника программирования «Altera
ByteBlaster»

Данный
переходник разработан фирмой Altera для
программирования и конфигурирования
микросхем ПЛИС через интерфейсы JTAG и
Passive Serial 45Схему
бластера несколько модифицирована —
добавлены светодиоды для индикации
наличия питания и режима программирования,
а также выведены дополнительный сигнал
на вывод 8 разъема X2 через свободный
канал шинного формирователя. Эта линия
используется при программной генерации
сигнала XTAL1 микроконтроллера.45Таблица
5.1Назначение контактов разъема X2
байт-бластера в режиме программирования
AVR.

Таблица
5.1Назначение контактов разъема X2
байт-бластера в режиме программирования
AVR.

X2 pin

AVR signal

X2 pin

AVR signal

1

SCK

2

GND

3

MISO

4

VCC

5

nRESET

6

ключ

7

не
используется

8

XTAL1

9

MOSI

10

GND

Рисунок
5.1 Принципиальная схема программатора
Altera ByteBlaster

При
использовании Altera ByteBlaster-II используется
такое же подключение, доработка для
генерации XTAL не требуется.

AVReAl
— описание программы

Программа
компилируется в следующих вариантах

  • 16-битном
    DOS, для работы на процессорах начиная
    с 286.

  • 32-битном
    WIN32

  • Также
    существует вариант для LINUX, но он еще
    недостаточно оттестирован.

Для
работы WIN32 версии необходимы

W95/W98 DLportIO.DLL

NT4,W2000 DLportIO.DLL
и
DLportIO.sys

Применение
WIN32 версии фактически имеет смысл только
под NT, когда DOS-программа не имеет доступа
к портам. Под W95/W98 нормально работает
DOS-версия.

Программа
AVReAl предназначена для программирования
микроконтроллеров серии AVR фирмы Atmel в
режиме ISP (in-cirсuit serial programming, последовательное
программирование в схеме). Т.е. на плату
устройства устанавливаются штыри, к
которым впоследствии подключается
шлейф от программатора (См выше). По
окончании процесса программирования
снимается сигнал сброса и процессор
начинает выполнять зашитую в него
программу.

В
режиме «fbprg» незадействованные
выходы данных LPT могут быть использованы
как питание чипа (ключ -ap).

При
использовании буферированных адаптеров
(Altera ByteBlaster, Atmel STK*00) программа рассчитана
на прошивку в плате с «родными»
питанием и кварцем.

Игнорируются
пустые строки и строки, начинающиеся с
символа ‘#’, последнее предназначено для
помещения комментариев о версии
непосредственно в HEX-файлы.

Проверяется
верхний адрес в HEX-файле, если не умещается
в опознанный чип, то никаких действий
не осуществляется. Проверяется перекрытие
адресов записей в HEX файле. При нахождении
первого перекрытия производится выход
из программы с указанием диапазона
адресов перекрывающихся данных.

Для
mega103 необходим расширенный формат
HEX-файла (обычный не поддерживает объем
больше 64Kb). Запись прочитанных данных
в файл всегда производится с дополнительными
записями тип ’04’ — Extended Linear Address Record, как
не имеющим разночтений. Чтение из
HEX-файла записей типа ’02’ Extended Segment Address
Record производится в соответствии с
формированием их в AVRASM by Atmel (без
предусмотренного документацией
Intel
сворачивания адреса смещения по модулю
64Kb).

Командная
строка
вызова
программы прошивки имеет вид:

avreal
[ключи] [[-c] имя_файла_кода [[-d]
имя_файла_данных]]

или

avreal
[ключи] -d имя_файла_данных (когда код
программы остаётся неизменным)

Если
есть только имена файлов, но нет
относящихся к ним ключей (-r
-w -v
), то файлы
игнорируются. Подробнее про работу с
файлами и ключами -c,
-d

рассказано после описания остальных
ключей.

Ключи

-?
(-h)
— выдать
ключи и перечень поддерживаемых
кристаллов. Команда avreal +name -? (+name должен
быть задан раньше
ключа -?) выдает список fuses , поддерживаемых
в режиме ISP для чипа name. Fuses
(фузы) – внутренние программные ключи
данного типа микроконтроллера (чипа).
Фузы в основном поддерживают чипы класса
mega.

+имя
— установить тип кристалла, <имя>
допускается задавать как с префиксом
‘AT’, так и без него, в том виде, в котором
имена выдаются по -h.
Регистр букв не важен. Например, at90s1200,
attiny22l, atmega103
либо 90s1200,
tiny22l, mega103.
Обязательный ключ, никаких действий по
умолчанию не производится. Если задан
только чип (больше нет ни ключей, ни имен
файлов), то чип будет сброшен и выдана
информация о нем (наличие, если есть
доступные по ISP fuses — их состояние).

-p<число1>[,<число2>]

«число1»,
шестнадцатеричное установливает порт
1,2,3 — номер LPT порта, >0x100 — базовый адрес
порта если задан номер — адрес из BIOS
MEMORY (не работает для win32 и linux версий, где
принято, что LPT1/2/3 это 278/378/3BC). Адрес и в
виде -p3BC,
и в виде -p0x3BC
воспринимается как HEX число. если задан
адрес – по запросу по умолчанию -p2
(LPT2)

«число2»,
с плавающей точкой,
необязательный параметр если указан,
то не производится настройку на скорость
порта, время обращения принимается
равным <число2>
микросекунд. В win32 версии настройка не
производится никогда, если не указано
— считается, что обращение к порту не
быстрее 1мкс

-a<буква>

группа
ключей для выбора типа адаптера

-ab
работа
через Altera ByteBlaster (включая ByteBlasterMV,
ByteBlaster-II)

-as
работа
через адаптер Atmel STK*00

-aa
автоматический
выбор
ByteBlaster/STK

-ax
XILINX
parallel download cable

az
пытаться
переводить выходы LPT в Z-состояние для
«отключения» программатора от
схемы («byte-blaster для ленивых», работает
в «FBPRG»
режиме)

-ar
инверсия
полярности сброса (например, для подачи
его через резистор в базу присутствующего
в схеме транзистора)

-ai#
установить
время неактивного Reset после стирания
равным #
mS. Необходимо устанавливать большим
времени задержки снятия сброса
присутствующим в схеме супервизором.

-ap
— подавать питание чипа через свободные
выходы данных LPT (несовместимо с -ab,-as)
Рекомендуется поближе к чипу установить
блокировочный конденсатор по питанию.

-o<число>

Задать
частоту установленного кварца для
настройки скорости SPI. Частота допускается
с плавающей точкой и единицами измерения
частоты Hz,
kHz, MHz,
например,

-o3.686MHZ

-o14745600hz

При
отсутствии единиц измерения для
совместимости с предыдущими версиями
считается, что частота в килогерцах.
При отсутствии ключа частота по умолчанию
800 (0.8MHz)

-o0
означает необходимость генерации XTAL
из программы. У STK*00 для генерации XTAL
используется линия LED

-n[<число>]

Использовать
последние 2 байта кода как счетчик
стираний. Если указано <число>,
то для записи счетчика используется
оно, а не инкрементированное прочитанное
значение. При -ewn
если последние 2 байта заняты кодом то
-n
игнорируется

-e[-]

Стереть
чип; ‘-‘ задает сохранение содержимого
EEPROM способом прочесть/стереть/записать.
Для кристаллов, имеющих fuse EESAVE, по -e-
также применяется этот способ. Если же
хочется пользоваться имеющимся fuse
EESAVE, следует установить его (-feesv=0)
отдельным запуском avreal и применять
обычную форму ключа -e.

-1,-2,-3,-4,-5

Увеличить
задержки на программирование в 1.5, 2, 3,
4, 5 раз соответственно (для программирования
при пониженном напряжении)

-b

Проверить
на чистоту

-r

Прочитать
из чипа в файл[ы]

-w[+][p]

Записать
в чип файлы кода/данных, заданные в
командной строке fuses. ‘+
— если чип в этом задании не стирался и
задана запись в EEPROM, то прописать FF в
ячейки EEPROM, не заданные в HEX. Верификация
этих ячеек на значение FF будет производится
даже, если был задан просто -v,
а не -v+.
Автоматическое стирание чипа не
производится по -w,
используйте -ew
для нестертого чипа. ‘p
— использовать поллинг для определения
конца записи. Если в течении времени,
равного восьмикратному номинальному
времени записи поллинг не даёт
положительного результата — выдаётся
сообщение об ошибке и завершение
программы.

-v[+]

Верифицировать
(только то, что есть в hex) дополнительный
‘+’ вызывает проверку на FF «свободных»
с точки зрения HEX-файла позиций

-l[+]#

Установить
уровень защиты # = 1 or 2 при наличии ‘+
lock биты прошиваются до зашивки fuses
(необходимо для включения BODEN и залочки
90s4433 в одном цикле подачи питания)

-f<fuselist>

список
fuses для тех чипов, в которых они шьются
по ISP. <fuselist>
выглядит как fusename=value,
fusename=value, bytename=value…
где value — ШЕСТНАДЦАТЕРИЧНОЕ значение
без префиксов и суффиксов. Для однобитовых
fuse добавлены значения ON и OFF (прошить и
стереть, 0 и 1 соответственно). Начиная
с версии 1.23 НЕ ПОДДЕРЖИВАЮТСЯ однобуквенные
синонимы для fuses.
При наличии
«пересекающихся» name=val
выдается сообщение об ошибке. Перечень
fusename для конкретного чипа с диапазоном
value и комментарием можно получить при
помощи — avreal +chipname -?

Для
сокращения строки возможно задание
полного байта fuses при помощи специальных
имен bytename, представляющие собой названия
байтов по документации с лидирующим
подчерком: _low,_high,_ext,_lock
Нереализованные в данном байте биты
рекомендуется устанавливать в ‘1’. Имя
_lock
относится к байту lock-битов и BLB, однако
на состояние lock-битов не
влияет
. Для
блокировки кристалла по прежнему надо
использовать ключ -l.
Допускается комбинация отдельных fuse и
байтов, однако они при этом не должны
пересекаться, т.е. каждый байт fuses должен
быть задан либо только через специальное
имя, либо только через имена отдельных
fuse. Например,

допустимо:
-f_low=C3,_high=F4,blb2=1

не допустимо:
-f_lock=F7,blb2=1

Если
работа с fuses по SPI поддерживается, то их
состояние сообщается при любой операции
с чипом, задание ключа -f
необязательно. Fuses программируются или
верифицируются указанием ключа -w
или -v
совместно с ключом -f
(в том числе в одном сеансе с
программированием/верификацией кода
или данных). Неупомянутые fuse остаются
неизменными (кроме битов защиты boot-блока
BLB, так как они могут быть стерты по -E
независимо от ключа -F). Группа BLB относится
скорее к lock-битам, записать 1 поверх уже
запрограммированной в 0 fuse невозможно.
Если запрошена такая операция и чип не
стирался, то программа завершает работу
с соответствующим предупреждением.
AVReAl обеспечивает также расширенную
поддержку
osccal

Для
tiny12, tiny15 поддерживается программирование
SPIEN и RSTDSBL. Будьте осторожны, SPIEN по
умолчанию запрограммировано, ключ
-fspien=1
(-fspien=off)
сотрет SPIEN и запретит дальнейшее
низковольтное программирование. То же
произойдет и при программировании
RSTDSBL (-frstdsbl=0
или -frstdsbl=on).

-%

Выдавать
по ходу программирования дополнительную
информацию (производимые действия и
ответы чипа). Внутри .bat файлов следует
писать -%%.

-!

Делать,
что указано ранее, даже если чип не
распознан (или распознан «не так»,
как задано в +<имя
чипа>
) а
также если не обнаружен указанный в
командной строке адаптер. Попытка
стереть нераспознанный (возможно просто
закрытый) чип при наличии команды
стирания производится и без -!
после чего проводится повторное
детектирование чипа.

Возможны
осмысленные комбинации:

-v
– w
записать,
проверить

-e
– w -v+
стереть
с сохранением EEPROM, записать, верифицировать
с проверкой на чистоту свободных
участков.

-b
–w
проверить
на чистоту, если грязная — выход, иначе
писать

После
ключа -d идет имя файла данных, после -c
имя кода, в этом случае порядок файлов
не важен. Пробел между ключами -c/-d и
именами не обязателен, т.е. допустимо
как -cfoo.hex так и -c foo.hex. Если дано два имени
файла без ключей -c и -d, то первое имя —
файл кода, второе — файл данных (EEPROM).
Если имя одно и нет ключей -c/-d, то этот
файл содержит данные для кода и, если
он содержит информацию после верхнего
адреса FLASH для заданного кристалла, то
эта информация используется для
программирования EEPROM данных. Т.е. если,
например, для 90s2313 hex-файл содержал данные
в адресах от 0x800 до 0x87F, то эти данные
будут записаны в EEPROM по адресам 0x00-0x7F.

Для
занесения в код и/или данные специальных
параметров применяются особые формы
ключей -c и -d следующего вида:

-d*name=offset,ext
— как байты в EEPROM данных

-c*name=offset,ext
— как байты во FLASH кода (под команду LPM)

-с?name=offset,ext
— как часть последовательных команд LDI
начиная с offset где:

name
— имя специального параметра, может
состоять из собственно имени и уточняющего
числа.

offset
— адрес байта, начиная с которого
производится запись специального
значения, шестнадцатеричное значение
без префиксов и суффиксов. Используется
адрес байта,
а не слова, в том числе и при обращении
ко flash кода.

ext
— дополнительный аргумент

Байты,
необходимые для записи, должны существовать
в исходном HEX-файле. В качестве специальных
параметров могут выступать: Значение
калибровочного байта RC-генератора В
качестве имени используется слово
osccal
с номером калибровочного байта. При
использовании нулевого байта OSCCAL
указание номера 0 не обязательно.
Дополнительный аргумент ext
— корректирующее значение к прочитанной
из кристалла величине OSCCAL (может быть
необходимо при напряжении питания,
отличающемся от напряжения калибровки
на производстве). При выходе корректированного
значения за пределы [0..255] происходит
ограничение значения и выдача
предупреждения.

Примеры:

1.
Необходимо записать значение 3-го (считая
с 0) байта osccal как байт по адресу 0x3F в
EEPROM данных. Используется ключ

-d*osccal3=3F

2.
Необходимо записать значение 0-го байта
osccal, уменьшенное на 3, как часть команды
LDI.

public
osccal_ldi

osccal_ldi:

ldi
R16,0xFF

out
OSCCAL,R16

Далее
смотрим по MAP-файлу значение osccal_ldi,
например оно получилось 0x120. Команда
примет вид:

avreal
+tiny12 -ewv -c foo.hex -c?osccal=120,-3

Запись
серийного номера прибора предназначена
для присвоения последовательных серийных
номеров программируемым изделиям. В
качестве имени параметра используется
слово serno с числом — длиной номера в
байтах от 1 до 4. Серийный номер записывается
в двоичном виде, младший байт записывается
первым. Дополнительный аргумент ext
— имя текстового файла, в котором записан
номер в десятичном виде. После успешной
записи в кристалл номер увеличивается
на 1 и записывается назад в файл. Файл
при этом переписывается полностью, если
после серийного номера в нём была другая
информация — она теряется. При сбое
верификации при команде -w
-v
номер не
меняется. Если в командной строке задана
только верификация (без записи), то
значение серийного номера из файла
используется, но не инкрементируется,
файл остаётся без изменений.

Примеры.

1.
Необходимо записать значение 2-байтного
серийного номера как 2 байта начиная с
адреса 0x12 в EEPROM данных. Сам номер записан
в файле serno.txt Используется ключ

-d*serno2=12,serno.txt

2.
Необходимо записать значение 3-байтового
серийного номера как часть команд LDI.
Номер
записан
в
файле
proj.serno

public
serno_load

serno_load:

ldi
R16,0xFF ; младший байт

ldi
R17,0xFF ; средний байт

ldi
R18,0xFF ; старший байт

Пусть
значение serno_load
по map-файлу вышло 0x120. Используется ключ:

-c?serno3=120,proj.serno

Порядок
ключей не важен, выполнение производится
в порядке E B W V L. Если проверка (B, V) дала
отрицательный результат, то дальнейшая
работа не производится. Т.е. -e -b -w -v -l2
означает: стереть, если стерлась — писать,
если верифицировалась — закрыть.

Возвращаемые
КОДЫ ОШИБОК

0
– программирование выполнено без ошибок

10
— not blank при -b,
несовпадение при -v[+]

20
— Невозможность осуществить запрошенную
операцию HEX кода или объём данных слишком
большой для распознанного чипа.
Запрошенное для записи состояние BLB
нельзя записать, не очистив чип (при
этом команды стирания не было).

30
— не распознан конкретный чип (бывает
при защищенном чипе, поэтому при -e
все равно делается попытка стереть и
если после этого тоже не распознан —
отказ)

40
— не подключен шнур программирования
(«Altera
ByteBlaster»
)
или
не удалось
войти в программирование по алгортиму
для At90s

50
— ошибка при чтении (не найден, «не те»
символы, не совпала контрольная сумма,
ошибка чтения, …) или записи (есть
защищенный от перезаписи с таким же
именем участок, …) HEX-файла.

60
— недостаточно памяти для списков
кода/данных


  1. недопустимая командная строка (в т.ч.
    задан номер отсутствующего LPT)

Программирование
может осуществляться из командной
строки или командного файла (*.bat).
Ниже приведен текст командного файла
для прошивки микроконтроллера AT90S2313.

avreal
+90S2313 -p1 -ab -o8.192MHz -e -b -w -v -cproba.hex

rem
| | | | | |
| | | |___ hex файл
proba.hex

rem
| | | | | |
| | |__ Верификация

rem
| | | | | |
| |__ Запись

rem
| | | | | |
|__ Проверить
на
чистоту

rem
| | | | |
|__ Очистить

rem
| | | | |__ Тактовая
частота

rem
| | | |__ Программатор
Altera ByteBlaster

rem
| | |__ Порт
LPT 1

rem
| |__ Тип
контроллера

rem
|__ Имя исполняемой прграммы

Флэш-память типа Boot Block служит для хранения обновляемых программ и данных в самых разных системах, включая сотовые телефоны, модемы, BIOS, системы управления автомобильными двигателями и многое другое. Используя флэш-память вместо EEPROM для хранения параметрических данных, разработчики добиваются снижения стоимости и повышения надежности своих систем.

Например, в разработках сотовых телефонов параметрические блоки флэш-памяти используются для хранения телефонных номеров, учета времени использования и идентификатора пользователя (SIM-карта). Производители автомобилей используют параметрические блоки флэш-памяти в системах управления двигателями для хранения кодов ошибок и параметров оптимальных режимов работы. В каждом из подобных примеров изготовители экономят как на ненужной микросхеме EEPROM, так и на расходах, связанных с необходимостью содержания складского запаса «прошитых» разными программами EEPROM, используя флэш-память Boot Block Flash Memory не только для хранения прикладных программ, но и параметров. Загрузка кода в чистую память может производиться в составе готовой системы на финальной стадии изготовления изделия. Кроме того, за счет снижения числа комплектующих и внешних контактов достигается более высокая надежность автомобильных систем в целом. И, наконец, повышается объем хранимых параметров и частота их изменения.

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

Основы технологии

Флэш-технология позволяет оснастить системную память уникальными свойствами. Подобно ОЗУ, флэш-память модифицируется электрически внутрисистемно, но подобно ПЗУ, флэш энергонезависима и хранит данные даже после отключения питания. Однако, в отличие от ОЗУ, флэш нельзя переписывать побайтно. Флэш-память читается и записывается байт за байтом и предъявляет новое требование: ее нужно стереть перед тем, как записывать новые данные.

Операции над флэш-памятью

Операция Минимальный сегмент Типичное время Максимальное время
Чтение Byte 60 нс 60 нс
Запись Byte 9 мкс не более 100 мкс
Стирание 8KB-Block 0.6 с 4.3 с
Примечание: по спецификации на ИС SmartVoltage 4Мbit Boot Block в 8-bit режиме при VCC=5.0V и VPP=5.0V

Запись (программирование) флэш-памяти — это процесс замены «1» на «0». Стирание — это процесс замены «0» на «1», где флэш стирается блок за блоком. Блоки — это области с фиксированными адресами, как показано на карте 4Мbit Boot Block микросхемы.

Карта памяти Boot Block

16KByte BOOT BLOCK
8KByte PARAMETER BLOCK
8KByte PARAMETER BLOCK
96KByte PARAMETER BLOCK
128KByte MAIN BLOCK
128KByte MAIN BLOCK
128KByte MAIN BLOCK

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

Микросхемы Flash Memory Boot Block должны выдерживать не менее 100 тысяч циклов стирания при напряжении питания VCC=5V. Цикл считается законченным, если 8КВ одного из параметрических блоков успешно запрограммировано и после этого стерто. Этот параметр очень важный, так как от него зависит то, какой объем данных можно хранить и как часто их можно обновлять.

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

Функционирование в системе

Кроме хранения параметрических данных, блочную флэш-память часто используют под хранение сменного кода программ. Часто в системах в заблокированном Boot-блоке хранится ядро кода, необходимого для инициализации системы и загрузки подпрограммы восстановления, на случай разрушения программы. В «бутовом» блоке обычно хранится также программа для программирования и стирания флэш-памяти. Так, флэш-память не допускает одновременное чтение ячейки с одним адресом и запись в ячейку с другим адресом в пределах одной микросхемы. Это означает, что любой код программы записи во флэш должен перегружаться в ОЗУ.

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

Структура параметрических данных

Структура данных в форме связных списков обеспечивает организацию данных, очень удобную для флэш-памяти. Например, предположим, что нужно сохранить 3 параметра, которые будут изменяться при условии, что каждый параметр хранится в виде записи. Каждая запись состоит из двух полей: Parameter_Value и Next_Record. В первом поле хранится значение параметра. Второе поле — это указатель, содержащий адрес следующей записи для этого параметра. ParameterX — это переменный указатель, содержащий адрес первой записи для этого параметра, поэтому Parameter1 представляет адрес. В ячейке с этим адресом хранится адрес первой записи параметра Parameter1, которая содержит первое значение Parameter1 и адрес второй записи Parameter1. Вторая запись содержит последнее значение этого параметра и адрес третьей записи, и т.д.. В последней записи в поле Next_Record содержится код FFh, указание на то, что записей больше нет. Код FFh выбран для указания, что записей больше нет, из-за того, что именно этот код представляет собой значение стертого байта флэш-памяти по умолчанию. При каждом изменении параметров программа ищет первую доступную ячейку в параметрическом блоке, записывает новое значение в поле значения новой записи, а потом обновляет поле Next_Record в предыдущей записи. Итак, каждая запись содержит значение и указатель, или связь со следующей записью. Такие структуры данных хорошо известны программистам, и называются связными списками, пользуясь которыми система может быстро найти последнее значение данного параметра.

Пример структуры связного списка

Адрес

Значение

Параметр

Parameter1 01H Parameter 1 Pointer Variable
Parameter2 03H Parameter 2 Pointer Variable
Parameter3 05H Parameter 3 Pointer Variable
01H F8H Parameter 1 Value = F8H
02H 07H Parameter 1 Next_Record = 07H
03H 22H Parameter 2 Value = 22H
04H 09H Parameter 2 Next_Record = 09H
05H 44H Parameter 3 Value = 44H
06H FFH Parameter 3 Next_Record = FFH = latest
07H 55H Parameter 1 Value = 55H
08H 0BH Parameter 1 Next_Record = 0BH
09H F2H Parameter 2 Value = F2H
0AH FFH Parameter 2 Next_Record = FFH = latest
0BH F4H Parameter 1 Value = F4H
0CH FFH Parameter 1 Next_Record = FFH = latest

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

Альтернативный подход к использованию связного списка состоит в применении поля parameter ID и поля статуса параметра, которое указывает, является ли текущая запись параметра самой поздней. В альтернативной схеме для того, чтобы получить последнее значение параметра, система считывает каждый параметр до тех пор, пока не найдет последнее значение данного параметра.

Запоминание параметров продолжается до тех пор, пока параметрический блок не заполнится или пока в параметрическом блоке хватает места для целой следующей записи. По достижении этой точки последние значения каждого параметра передаются во второй параметрический блок, а связный список продолжает формироваться во втором блоке параметров. Запись заголовка (Block_Header) в начале каждого параметрического блока показывает состояние блока. Состояние — это информация, например, о том, что параметрический блок активен, т.е. либо передает данные, либо стирается. Таким образом и осуществляются блочные передачи.

Стирание параметрического блока

После передачи действительных значений параметров из первого блока во второй, первый блок стирается. Вспомним, что стирание флэш-памяти занимает примерно 0,5s на каждый параметрический блок. Поскольку так много времени во время работы системы может не оказаться, во флэш-памяти используется команда приостановки стирания (Erase Suspend). По этой команде операцию стирания можно приостановить, чтобы система смогла считать данные из другого блока данного прибора памяти. Когда команда Erase Suspend поступает в микросхему, операция стирания останавливается, а память входит в «подвешенное» состояние, и тогда можно прочесть данные из другого блока. Когда снова будет можно стирать, команда Erase Resume прикажет прибору продолжить стирание с того места, где оно было прервано, что позволяет реализовать операцию стирания в пределах конечного программного цикла, используя несколько вызовов (Call). После полного стирания первого блока он снова готов к записи параметров, когда заполнится второй блок. Важно то, что никакие новые параметры нельзя записать, пока не закончится операция стирания блока. Текущие версии флэш-памяти Boot Block не допускают запись в моменты, когда стирание приостановлено.

Эмуляция побайтного обновления

Ступень 1. Резервирование параметрических записей в параметрическом блоке 1 (Parameter Block 1)
PARAMETER BLOCK 1
block_status record
parameter records
 
PARAMETER BLOCK 2
block_status record
erased
Ступень 2. Когда Parameter Block 1 заполнен, осуществляется передача последнего параметра записи в Parameter Block 2 и изменение block_status record.
PARAMETER BLOCK 1
block_status record
parameter records
 
 
PARAMETER BLOCK 2
block_status record
 
Ступень 3. Резервирование параметра записи в Parameter Block 2. Стирание Parameter Block 1, используя команду приостановки стирания (Erase Suspend) для возврата в фазу чтения флэша, когда это необходимо.
PARAMETER BLOCK 1
block_status record
erased
 
PARAMETER BLOCK 2
block_status record
parameter records

Требования к системе

Как отмечено выше, для исполнения программы в моменты программирования и стирания флэш-памяти требуется ОЗУ. Необходимый объем ОЗУ зависит от сложности базы данных параметров. Программа, которая должна быть загружена в ОЗУ, включает подпрограммы чтения, записи и стирания флэш-памяти. Размер ее кода лежит в диапазоне от 512 байт до одного килобайта. Кроме того, для хранения этой программы потребуется место внутри самой флэш-памяти. Образец программы занимает около 15KB, но только небольшая ее часть (около 1KB) выгружается в ОЗУ.

Другое системное требование — адекватное напряжение программирования (VPP) для записи и стирания. Большинство современных микросхем флэш-памяти требует подачи 12V для внутрисистемной записи и стирания. Например, микросхемы семейства SmartVoltage позволяют использовать напряжение 5 В для операций записи и стирания, если источник 12 В в системе отсутствует.

Кроме 12V и 5V SmartVoltage стандартов существует технология 3.3 В SmartVoltage — микросхемы Flash-памяти емкостью 4 Mбит, имеющие архитектуру Boot Block. Эти микросхемы дополняют существующий ряд и позволяют разработчикам оптимизировать производительность и энергопотребление запоминающих устройств, пользуясь только одним типом памяти. SmartVoltage — технология, соединяющая в себе свойства низкой потребляемой мощности, самого быстрого программирования и единственного напряжения питания в одном приборе. Архитектура Boot Block позволяет совместить функции ROM, Flash или EPROM и EEPROM памяти в одной микросхеме.

Данная память позволяет эффективно удовлетворить противоречивые требования к разработке изделия, используя напряжения программирования VPP уровней 5 В или 12 В, и VCC со значениями 3.3 В или 5 В в любой комбинации. Это позволяет оптимизировать время записи, выбирая напряжение VPP=l2 В, или цену устройства, выбрав единственное напряжение питания VСС=VPP=5 В. Данное семейство имеет самое низкое потребление энергии без потерь производительности. При потреблении 150 мВт на 6 Mгц, 3V-read режим на 40% более эффективен, чем 5V-only. Дополнительно, SmartVoltage Flash в 3V-read режиме обеспечивает доступ за 110 нс, что вдвое быстрее, чем лучшие 3V-EEPROM. Теперь же еще микросхемы имеют реконфигурируемую шину данных, поэтому их можно применять как с 16bit, так и с 8bit микропроцессорами.

Так, если соединить выводы VPP и VCC, то память предлагает самую высокую производительность при единственном напряжении питания. При питании от 5 В SmartVortage обеспечивает чтение данных за 60 нс, и запись за 13 мкс, что превосходит те же параметры у сопоставимых 5V-only изделий другой технологии. Кроме этого, 3.3 В SmartVoltage позволяет переходить от режима с единственным напряжением питания к более гибкому режиму 3V-read/5V-write в портативной аппаратуре без дополнительных затрат на сертификацию Flash-микросхем. Для максимальной скорости программирования в процессе производства, приборы SmartVoltage могу быть запрограммированы при VPP=12 В, что вдвое сокращает время записи и снижает затраты при изготовлении больших партий аппаратуры.

Так, «новые» микросхемы выпускаются в 44-выводных пластиковых корпусах (PSOP — Plastic Small Outline Package) и 48-выводных корпусах с уменьшенной толщиной (TSOP — Thin Small Outline Package), и имеют разводку выводов в соответствии со стандартом JEDEC, что позволяет разрабатывать платы, на которые можно установить микросхемы 2-8 Mбит. В настоящее время доступны микросхемы с типами упаковки, начиная «обычным» пластиковым вариантом двухстороннего расположения выводов (PDIP — Plastic Dual In-line Package) и заканчивая современным «масштабируемым» вариантом (SCP — Chip Scale Package), комбинируя для разных ситуаций степень упаковки, расстояние между выводами, габаритные размеры и, наконец, условия эксплуатации. Эти микросхемы имеют ту же проверенную длительной эксплуатацией технологию запоминающих матриц на транзисторах с плавающим затвором (технология ЕТОХ IV — EPROM Tunnel OXide), и поэтому имеют те же характеристики надежности и времени жизни, как и другие микросхемы Flash-памяти. Они предназначены для широкого круга применений, включая BIOS, сотовые телефоны, приводы жестких дисков, point-of-sale терминалы, а также блоки управления двигателем и другими автомобильными системами.

Интересным моментом является вопрос пропадания питания во время стирания или в процессе обновления значений параметров. С ситуацией исчезновения питания можно надежно справиться, добавив дополнительные поля как к параметрическим, так и к блочным записям. Например, в дополнение к полям Parameter_Value и Next_Record, которые ввели для параметрической записи, можно установить поле статуса (Parameter_Status). Один бит поля состояния указывает, что обновление параметра началось, а другой бит — что обновление параметра завершилось. Таким образом, если питание исчезнет в процессе модификации параметра, то когда питание восстановится, можно узнать состояние каждого параметра. К примеру, если питание появилось, и видно по битам состояния, что обновление параметра начато, но не закончено, то отсюда следует, что запись испорчена и должна быть исправлена. Эту же концепцию можно применить в отношении записи Block_Status, чтобы обрабатывать ошибки при стирании, вследствие прерывания процесса стирания из-за отказа питания, либо из-за пересылки параметров между блоками.

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

Ранее обсуждалось, как читать и программировать флэш-память на побайтной основе. Флэш-память, в действительности, допускает программирование на уровне битов (или группы битов) за один раз. Надо помнить, что программирование флэш-памяти — это процесс замены логических «1» на «0». Одиночные биты можно запрограммировать путем маскирования остальных битов в байте или слове «единицами». Пользуясь такой удобной особенностью, можно минимизировать затраты памяти, отводимой под разные поля состояния.

Пример 1
1111 1111
0111 1111
0111 1111
Содержание памяти
Программируемые данные
Результирующее состояние памяти
Пример 2
0111 1111
1011 1111
0011 1111
Содержание памяти
Программируемые данные
Результирующее состояние памяти
Пример 3
0011 1111
0001 1111
0001 1111
Содержание памяти
Программируемые данные
Результирующее состояние памяти

Временная диаграмма работы

Динамический анализ работы системы необходим, чтобы определить длительность времени, необходимого для чтения параметров, выгрузки кода программы Write/Erase в ОЗУ, записи параметров, передачи параметров в новый блок и стирания параметрического блока.

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

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

При выполнении каждой операции записи или стирания (Write/Erase) флэш-памяти, нужно перегрузить из нее в ОЗУ код программы, содержащий драйверы программирования и стирания. Время, необходимое для этой перегрузки кода в ОЗУ, зависит от объема кода (обычно 1 Kбайт или меньше). Умножив размер кода на длительность цикла записи, определим длительность загрузки кода в ОЗУ.

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

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

Как и в случае передачи параметров, стирание блока можно рассматривать как задачу переднего плана или как фоновую. В случае фоновой задачи, общее время стирания зависит от величины временного «окна» в рамках программного цикла. Число необходимых вызовов определяется путем деления общего времени стирания на длительность времени, доступного в пределах каждого цикла. Умножив число вызовов цикла на длительность цикла, получим полное время стирания параметрического блока.

Так, для микросхем флэш-памяти типа Boot Block в спецификации гарантируется не менее 100 тысяч циклов стирания. Как это влияет на хранение параметров (цикличность), легко рассчитать, воспользовавшись выражением:
100000 Cycles=[8KB-(Block_Record size)/Parameter_Record size]*number of Parameter_Record

Это уравнение можно решить как для искомого числа записей параметров, так и для длины поля Parameter_Record, в зависимости от того, что известно. По сравнению с EEPROM устойчивость флэш-памяти к ре-программированию значительно выше.

Заключение

В настоящей статье были описаны основные моменты программных методов эмуляции побайтовой работы с использованием двух параметрических блоков флэш-памяти. Разработчики систем, используя для хранения параметров вместо EEPROM параметрические блоки микросхем Boot Block, могут снизить стоимость и повысить надежность своих систем. Так, уже давно осознали преимущества перехода от микросхем, стираемых целиком, к приборам, основанным на блочной архитектуре. Флэш-память типа Bulk Erase, изготавливаемая по устаревшей технологии, перестала модернизироваться и уже давно вытеснена более современными семействами.

				
	
	
	
Дата: 9 Октября 2014. Автор: Алексей

	
	

Понадобилось мне тут мнооого памяти) В общем внешнее EEPROM типа 24LC512 оказалась
мелковато для нужд мегадевайса. Блуждая по интернету понял что курить мне придется
самую обычную флешку. Но не ту что мы привыкли вставлять в USB порт ПК, а самую что
не на есть микросхему. Называется это чудо AT45DB041. Ну съездил в магаз, купил.
По даташиту сваял по быренькому отладочную платку.


Отладочная плата AT45DB041


Далее читаю принцип руления этой штуковиной. Какие-то буфера, страницы. Жуть.
Нифига не понял. Ладно думаю, пойду спрошу у Яндекса, Гугла. Ну сказали они мне:
«Вот те пару библиотек, одна для AVR другая для STM32». Первая оказалась фигней,
ничего не заработало, а искать ошибки в чужом коде, бррр. По STM32 вроде ничего,
да зараза под микросхему AT45DB161 и там что-то стоко всего накрутили. Видать с той
библой можно микросхему аж за регистры пощупать руками))) Ну да ладно, короче решил
я сам разузнать что по чем, так как в просторах великой паутины кроме как урывок с
разных форумов я толком ничего не нашел.


Нус начнем. Пишу как я понял. Если где ошибусь, прошу знающих поправить.
Начнем с того как устроена память в микросхеме. Как оказалась обратиться к памяти
напрямую не так просто. Вот тут и всплыли буфера да страницы. Основная память
устроена не сплошняком, а разбита на так называемые страницы, коих в данной
микросхеме аж 2048 штуков. Каждая страница несет на своих плечах либо 256 байт,
либо 264. Да я тут тоже репу почесал. Как я понял по умолчанию доступны 264 байта,
а если ее перенастроить (я даже не заморачивался как это сделать) то на странице
будет только 256 байт. Причем, опять как я понял, эта операция безвозвратна. То
есть обрезал и радуйся. Чтобы это понять, можно представить память как например,
кто помнит была в свое время фотопленка «Свема64», 36 кадров. Вот вся пленка и есть
память, а кадры это страницы. То есть работать нужно не со всем массивом, а с
одной выбранной страницей. Но к ней напрямую тоже нельзя обратиться. Для того
чтобы писать/читать нужен посредник. Вот этой целью и занимаются два буфера.
Зовут их Буфер1 и Буфер2. Эти два буфера располагаются в оперативке микросхемы.
Да, у флеш памяти есть оперативка и даже больше. Запись в основную память из
буфера и чтение из памяти в буфер происходят автоматически. Просто кидаем команду
записать и микросхема сама пишет. Говорим читай и она читает. Отсюда появляется
алгоритм записи и чтения данных. Давайте ка мы его рассмотрим.


Алгоритм записи, а потом чтения.


  • 1. Записать данные в Буфер1/2
  • 2. Дать команду на запись данных из буфера1/2 в страницу основной памяти.
  • 3. Дать команду на запись данных из страницы в основной памяти в Буфер1/2
  • 4. Прочитать данные из Буфера1/2

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


  • 1. df_WriteBuffer Запись в буфер
  • 2. df_ReadBuffer Чтение буфера
  • 3. df_WriteBufferToFlash Записать данные из буфера в память
  • 4. df_ReadFlashToBuffer Записать данные из памяти в буфер

Но перед тем как начать писать далее код, давайте договоримся. Я писал все под
МК STM32F103C8T6. Отсюда я не буду расписывать как сконфигурировать SPI. Так же
нам понадобится функция приема/передачи данных по SPI. Я ее тоже описывать не
буду, а просто представим что она работает и называется DF_SPI_RW. Почему
я не буду их описывать. Функции которые мы напишем для записи и чтение в память
фактически платформонезависимые и их можно использовать как в STM32 так и в AVR и
даже в я думаю в PIC. Хотя с последними я вообще никогда не работал, но думаю
там не сильно будет отличаться. Для пользователей STM32 бонус. В конце статьи
будут лежать два файла под Кокос, которые подключив к проекту можно сразу
использовать. По мимо основных функций в этих файлах прописано инициализация SPI1
и порта В который удачно расположен с выводами SPI. То есть эти файлы практически
библиотека для флеш-памяти.


Поехали дальше. Вся передача данных в микросхему происходит в полнодуплексном
режиме. То есть передача от МК до Флеш и обратно происходит одновременно. По этой
причине есть небольшие сложности в общении с флешкой. Но все по порядку. Для
выполнения той или иной команды, микросхеме нужно об этом сообщить. Для этих
целей есть набор команд. Я выпишу те что нам нужны, а если понадобятся другие,
то милости просим в документацию.


  • 1. Запись в Буфер 1 код 0x84
  • 2. Запись в Буфер 2 код 0x87
  • 3. Запись Буфера 1 в память с предварительным стиранием страницы код 0x83
  • 4. Запись Буфера 2 в память с предварительным стиранием страницы код 0x86

То есть если нам надо произвести запись в Буфер1, то надо подать команду 0x84.
Важно помнить еще то, что передача должна идти всегда старшим битом вперед.
Теперь давайте посмотрим как нам собрать функцию для записи данных в буфер.
Для этого нам понадобится передать функции несколько аргументов. Первый это номер
буфера. Ведь у нас два буфера, вот и надо сказать в какой записываем данные. Для
простоты так и назовем BufferNo. Следующий аргумент это адрес байта в буфере. Ведь
в буфере 264 байта, значит нужно сказать куда писать. А точнее с какого адреса
писать в буфер. Это связано с тем, что команда запись в буфер принимает адрес
ячейки памяти, а записать можно хоть все 264 байта сразу. То есть микросхема сама
будет увеличивать адрес на единицу при каждой записи байта. Причем нужно самому
следить за переполнением. Если количество передаваемых байт превысит объем памяти,
то микросхема даже не пикнув продолжит писать но уже с нулевого адреса, переписывая
предыдущие данные. Назовем его Addr. И последний аргумент это сам байт данных.
Назовем его Data. Вот что у нас получилось.

 
df_ReadFlashToBuffer(uint8_t BufferNo, uint16_t Addr, uint8_t Data)
{

}
 

Теперь давайте почитаем про команду записи в буфер. А пишут следующее: Для того
чтобы записать данные в буфер, нужно подать команду 0x84, за которым следуют 15
незначащих бита и 9 адресных бит. Что сия абра-кадабра означает. Незначащие биты
это просто нули в порт. Но что делать с 9 битами адреса? Байт-то у нас 8 бит. Вот
тут умные люди нв Атмеле и приделали 15 незначащих бит. 15+9=24. А, 24/8=3. Да,
три байта. То есть сначала кидаем в порт байт команды, затем байт с нулями. Потом
старший байт адреса(он может принимать два значения, либо 0x00, либо 0x01). И
добиваем все это дело младшим байтом адреса. Давайте все это теперь рассмотрим в
коде.

 
void df_ReadFlashToBuffer(uint8_t BufferNo, uint16_t Addr, uint8_t Data)
{

    uint8_t adr1 = (unsigned char)(Addr>>8);
    uint8_t adr2 = (unsigned char)Addr;

    SELECT();

    if(BufferNo == 1) { DF_SPI_RW(Buf1Write); }
    else if (BufferNo == 2) { DF_SPI_RW(Buf2Write); }
    else {DESELECT(); return; }

    DF_SPI_RW(0x00);
    DF_SPI_RW(adr1);
    DF_SPI_RW(adr2);
    DF_SPI_RW(Data);

    DESELECT();
}
 

Все по порядку. В первой строке мы переменной adr1 присваиваем старший байт адрес
методом сдвига вправо на 8 бит. В следующей строке мы присваиваем переменной adr2
младший байт адреса. Далее следует строка SELECT(); Я совсем забыл про внешние
ноги. Так как микросхема общается по SPI и всегда является ведомой, то на борту
имеет ножку выбора микросхемы CS. Если нужно что-то сказать или получить от
микросхемы, то нужно просто прижать ножку CS к земле, а когда общаться перестали,
то подтягиваем ее к плюсу. Собственно SELECT(); и прижимает с земле. Тем самым мы
говорим, что хотим общатся с микросхемой. Дальше идет условие выбора Буфера. Если
мы выбрали первый буфер то запишем в микросхему 0x84, а если второй, то 0x87.
Buf1Write = 0x84
Buf2Write = 0x87
Третье условие нужно для того чтобы не промахнуться и не пихать данные в буфер 3,
4, 5 и т.д. После условия выбора буфера идет строка с передачей нулевого байта.
Затем старший байт адреса, а за ним младший. Все, микросхема с этого момента готова
принимать байты данных. В данной функции мы передаем лишь один байт. И в конце
поднимаем CS к плюсу питания говоря что больше нам микросхема не нужна. После того
как мы записали данные в буфер, нужно записать в основную память этот буфер. Для
этого существует две команды. Первая записывает данные из буфера в страницу
памяти с предварительным стиранием страницы, а вторая просто записывает. В чем
разница. Все дело в том что запись в память происходит всего буфера, а он равен
264 байтам. То есть если мы записали всего один байт, то все равно запишем все
264. Почему я так и не понял, но перед записью в память нужно сначала стереть
всю страницу в которую будет идти запись. Отсюда и две команды. Первая сама все
за нас сделает, а вот перед второй соизвольте сами стереть страницу. Разобрались,
теперь к делу. Аргументы функции. Первый опять номер буфера. Второй адрес страницы
в которую будем записывать буфер.

 
void df_WriteBufferToFlash(uint8_t BufferNo, uint16_t PageAdr)
{
    uint8_t adr1 = (unsigned char)(PageAdr>>7);
    uint8_t adr2 = (unsigned char)(PageAdr<<1);

    SELECT();

    if(BufferNo == 1) { DF_SPI_RW(DF_BUF1_TO_FLASH_WITH_ERASE); }
    else if (BufferNo == 2) { DF_SPI_RW(DF_BUF2_TO_FLASH_WITH_ERASE); }
    else {DESELECT(); return; }

    DF_SPI_RW(adr1);
    DF_SPI_RW(adr2);
    DF_SPI_RW(0x00);

    DESELECT();

    Delay(MS*20);
}
 

В первой строке мы опять записываем старший байт адреса, а во второй младший. Если
вы заметили, то сдвиг в первой строке вправо на 7 бит, в во второй влево на 1 бит.
В чем тут шаманство. Опять идем смотреть мануал. Для того чтобы записать буфер в
страницу с предварительным стирание последней, нужно: Подать команду 0x83 или 0x86
в зависимости от номера буфера. Далее передать 3 зарезервированных бита, затем 12
бит адреса страницы и 9 незначащих бита. Во какая опа. Но тут опять все просто.
3+12+9=24. Опять наши 3 байта. Только тут немного по сложнее. Смотрите.


хххААААААААААААННННННННН я написал последовательность бит, х-зарезервированные,
А-адресные и Н-незначащие. Теперь давайте разобьем по байтам.

 
    хххААААА
    АААААААН
    НННННННН
 

Надеюсь теперь прояснилось) Мы адрес передаем двубайтовой переменной. То есть у
нас адрес может принять максимальное значение 2048 -> 0b0000100000000000. Теперь
если мы сдвинем значение вправо на 7 бит и присвоим это значение однобайтовой
переменной, то получим 0b00010000. Вот они наши первые три зарезервированных бита,
а за ними 5 бит адреса. Дальше нам нужно передать оставшиеся 7 бит адреса. Так
как адрес в двубайтовои переменной записан с младшего бита, а нам нужно только 7,
то просто сдвигаем биты влево на 1 бит. Вот и вся премудрость. Дальше в функции
выполняем тоже самое что и при записи в буфер, за двумя но… Первое это нулевой
байт шлем после адреса.(Помним 9 незначащих бит), а второй это пауза после того
как подняли CS. Она нужна для того чтобы микросхема смогла сама стереть страницу,
а потом записать туда буфер. Для справки, смотрим документацию tEP Время
стирания и записи страницы 20 мс.
Следующая функция записывает в буфер выбранную страницу. Аргументы функции такие же
как и при записи буфера в страницу.

 
void df_ReadFlashToBuffer(uint8_t BufferNo, uint16_t PageAdr)
{
    uint8_t adr1 = (unsigned char)(PageAdr>>7);
    uint8_t adr2 = (unsigned char)(PageAdr<<1);
	
    SELECT();
	
    if(BufferNo == 1) { DF_SPI_RW(DF_FLASH_TO_BUF1); }
    else if (BufferNo == 2) { DF_SPI_RW(DF_FLASH_TO_BUF2); }
    else {DESELECT(); return; }
	
    DF_SPI_RW(adr1);
    DF_SPI_RW(adr2);
    DF_SPI_RW(0x00);
	
    DESELECT();
	
    Delay(MS*20);
}
 

Собственно в этой функции поменялась лишь команда с записи в страницу на запись в
буфер и все. И последняя функция чтение байта из буфера. Аргументы: Номер буфера
и адрес байта.

 
uint8_t df_ReadBuffer(uint8_t BufferNo, uint16_t Addr)
{

	uint8_t data = 0;

	uint8_t adr1 = (unsigned char)(Addr>>8);
	uint8_t adr2 = (unsigned char)Addr;

	SELECT();


	if(BufferNo == 1) { DF_SPI_RW(Buf1Read); }
	else if(BufferNo == 2) { DF_SPI_RW(Buf2Read); }
	else { DERESET(); return 0; }

	DF_SPI_RW(0x00);
	DF_SPI_RW(adr1);
	DF_SPI_RW(adr2);

	data = DF_SPI_RW(0x00);

	DESELECT();

	return data;
}
 

Здесь мы сначала создаем переменную data. Так как мы читаем байт, то функция
должна нам его вернуть. Вот для этих целей и создаем эту переменную. Далее мы
подготавливаем адрес, выбираем микросхему(помним CS), выбираем буфер, кидаем
незначащие 8 бит, адрес. А вот для получения байта нам нужно послать в порт любой
байт. Шлем ноль, а в ответ получаем байт из буфера. Отваливаемся от микросхемы CS.
И возвращаем наш байт. Вот и все. Для старта этого вполне достаточно.


Библа которую я наваял для STM32 под CooCox
Мануал на AT45DB081B на русском языке. Любезно переведен http://piclist.ru.
Собственно та же микра, только на 8Mb

Алексей    24.06.17 11:27

Кто нибудь проверял код? собираю с этого кода библиотеку для AVR вопрос встал уже на первой функции:
void df_ReadFlashToBuffer(uint8_t BufferNo, uint16_t Addr, uint8_t Data)
{

uint8_t adr1 = (unsigned char)(Addr>>8);
uint8_t adr2 = (unsigned char)Addr;

SELECT();

if(BufferNo == 1) { DF_SPI_RW(Buf1Write); }
else if (BufferNo == 2) { DF_SPI_RW(Buf2Write); }
else {DESELECT(); return; }

DF_SPI_RW(0x00);
DF_SPI_RW(adr1);
DF_SPI_RW(adr2);
DF_SPI_RW(Data);

DESELECT();
}
— разве это функция не df_WriteBuffer?

в статье так же не нашел значения этих переменных:
DF_BUF1_TO_FLASH_WITH_ERASE
DF_BUF2_TO_FLASH_WITH_ERASE

DF_FLASH_TO_BUF1
DF_FLASH_TO_BUF2

Buf1Read
Buf2Read

может я не внимательно читал или что то не понимаю, но может вы пересмотрите статью?!

Алексей    24.06.17 18:07

Код рабочий 100% так как я его запускал на живом МК. Если нужны функции под AVR, то у меня в проекте добавить эту память в axlib.

Алексей    25.06.17 13:52

А еще вопрос такой, кажется я его неправильно развязал, WP если не подтянуть к +5 то первые 256 страниц нельзя трогать?
А еще, питание микросхемы 2.5-3.6В?Если так то высокий уровень в SPI не критичен? и если я питал его от 5В то скорее всего сжег?

Алексей    25.06.17 16:51

WP это блокировка записи. То есть если подтянуть к земле, то можно будет только читать. Если подтянуть к плюсу, то можно читать и писать. Напряжение питание микросхемы от 2,5в до 3,6в. От 5в я боюсь ей поплохеет.

Игорь    25.06.19 17:53

Так что там с планами по переводу функций под AVR :)
Есть уже библиотека чтобы из AVR контроллеров можно было с этой памятью работать?

Алексей    25.06.19 21:32

Можно, но нет времени.

Игорь    26.06.19 13:56

Очень жаль :(

Александр    08.10.19 12:06

df_ReadFlashToBufferТеперь давайте почитаем про команду записи в буфер.

Самая первая функция дословно означает «считать флэш в буффер», хотя вы ее называете «записать в буфер». В тексте нет ни слова про проверку перед отправкой битов регистра spi. К тому же ставить паузы в коде — очень топорно. Надо либо использовать таймер, либо перед следующим запросом прочитать регистр статуса самой микросхемы, в частности бит RDY. Если он установлен, значет флэш готов к принятию/чтению данных




Введение

Классическим периферийным блоком современного микроконтроллера является FLASH-память, которая позволяет хранить данные при отсутствии питания. Механизм её записи более сложный, чем ОЗУ-памяти, где можно просто по необходимому адресу выставить значения на шину данных, а по сигналу записи они защелкнутся памятью на хранение. Для записи данных во FLASH нужен определенный алгоритм программирования. Обычно такие программы называют загрузчиками. Эти файлы как раз и являются готовыми программами-прошивальщиками, которые позволяют скомпилированный образ программы загрузить во FLASH-память. Причем, стоит отметить, что они могут реализовывать абсолютно любой механизм для программирования любого типа памяти. Например, можно создать загрузчик для записи данных во внешнюю FLASH-память, подключенную по SPI или параллельному интерфейсу, или даже для записи данных во внешнюю OTP. Но самый распространенный вариант – это загрузчики для программирования внутренней-FLASH памяти. Посмотрим на две самые популярные среды разработки для микроконтроллеров с ядром Cortex-M – Keil uVision и IAR Workbench. Каждая из них имеет свой формат загрузчиков и примеры их реализации. У Keil uVision – FLM файлы, у IAR – файлы формата *.out (по сути ELF). Благодаря этим файлам, работая в среде, имеется возможность нажать «Download» и программа будет «прошита» в микросхему. Рассмотрим, как сделать реализацию такого файла загрузчика FLM для среды Keil, а именно сделаем такие файлы для прошивки внутренней FLASH-памяти для всей актуальной линейки микроконтроллеров Миландра.

Структура FLM

FLM – он же Flash Programming Algorithm (именно так именуется официальной документацией CMSIS) по сути представляет из себя программу, которая загружается в ОЗУ контроллера, а затем получает данные для записи в память, то есть взаимодействует с тем или иным периферийным блоком. Получается, что, когда «прошивается» память контроллера, на самом деле загружается специальный алгоритм в ОЗУ-память микросхемы, который, взаимодействуя с контроллером FLASH-памяти, фактически организует процесс программирования микросхемы самой себя.

Рисунок 1 — Окно выбора алгоритма программирования FLM в среде Keil

В среде Keil есть шаблонный проект для реализации FLM. Его можно найти в директории, где установлена среда, по пути: Keil_v5ARMFlash_Template. Сама же директория Flash хранит в себе папки с исходниками FLM от популярных производителей микроконтроллеров и их производные – готовые файлы FLM. Шаблонный проект содержит следующие основные файлы:

-FlashDev.c

-FlashPrg.c

-директория Test,

а также файлы проекта среды. Для того, чтобы происходила генерация FLM-файла, в настройках проекта в окне USER, после сборки/компиляции (After Build/Rebuild) указывается следующая команда:


cmd.exe /C copy "Objects%L" ".@L.FLM"

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

Приведём список микроконтроллеров Миландр, в которых присутствует внутренняя FLASH-память, поскольку именно для них будем подготавливать новые FLM.

K1901ВЦ1QI;

К1986ВЕ1QI;

К1986ВЕ92QI;

К1986ВК214;

К1986ВК234.

FlashDev.c

В файле FlashDev.c содержатся структуры, описывающие информацию о памяти и самом микроконтроллере. Структуры проинициализированы для каждого контроллера и выбираются с помощью макросов условной компиляции. Объявление структуры содержится в FlashOS.H

Имеет следующий вид:

struct FlashDevice  {

   unsigned short     Vers;    // Version Number and Architecture

   char       DevName[128];    // Device Name and Description

   unsigned short  DevType;    // Device Type: ONCHIP, EXT8BIT, EXT16BIT, ...

   unsigned long    DevAdr;    // Default Device Start Address

   unsigned long     szDev;    // Total Size of Device

   unsigned long    szPage;    // Programming Page Size

   unsigned long       Res;    // Reserved for future Extension

   unsigned char  valEmpty;    // Content of Erased Memory

   unsigned long    toProg;    // Time Out of Program Page Function

   unsigned long   toErase;    // Time Out of Erase Sector Function

   struct FlashSectors sectors[SECTOR_NUM];

}; 

Все поля уже прокомментированы и сразу, в качестве примера, приведём инициализированную структуру для микроконтроллеров К1986ВЕ1QI:

struct FlashDevice const FlashDevice  =  {

   FLASH_DRV_VERS,             // Driver Version, do not modify!

   "1986VE1/VE3 IAP 128kB Flash Rev_3",   // Device Name

   ONCHIP,                     // Device Type

   0x00000000,                 // Device Start Address

   0x00020000,                 // Device Size in Bytes (128kB)

   0x1000,                     // Programming Page Size

   0,                          // Reserved, must be 0

   0xFF,                       // Initial Content of Erased Memory

   5000,                       // Program Page Timeout 5 Sec

   5000,                       // Erase Sector Timeout 5 Sec

// Specify Size and Address of Sectors

   0x00001000, 0x00000000,     // Sector Size  4kB (32 Pages)

   SECTOR_END

};

FlashPrg.c

FlashPrg.c – это основной файл, где описаны функции для программирования. В шаблоне их представлено 7:

extern          int  Init        (unsigned long adr,   // Initialize Flash

                                  unsigned long clk,

                                  unsigned long fnc);

extern          int  UnInit      (unsigned long fnc);  // De-initialize Flash

extern          int  BlankCheck  (unsigned long adr,   // Blank Check

                                  unsigned long sz,

                                  unsigned char pat);

extern          int  EraseChip   (void);               // Erase complete Device

extern          int  EraseSector (unsigned long adr);  // Erase Sector Function

extern          int  ProgramPage (unsigned long adr,   // Program Page Function

                                  unsigned long sz,

                                  unsigned char *buf);

extern unsigned long Verify      (unsigned long adr,   // Verify Function

                                  unsigned long sz,

                                  unsigned char *buf);

По рекомендациям CMSIS обязательны к реализации 4, а именно:

int  EraseSector (unsigned long adr);

int  ProgramPage (unsigned long adr, unsigned long sz, unsigned char *buf);

int  Init (unsigned long adr,  unsigned long clk, unsigned long fnc);

int  UnInit (unsigned long fnc);

А опциональны (не обязательны):

int  EraseChip   (void);  

int  BlankCheck  (unsigned long adr,  unsigned long sz, unsigned char pat);

unsigned long Verify      (unsigned long adr,  unsigned long sz, unsigned char *buf);

Стоит обратить внимание на обязательность EraseSector (…) и необязательность EraseChip (…). К сожалению, в текущих официальных FLM для микроконтроллеров «Миландр» всегда был реализован EraseChip, а при выборе Erase Sectors сыпались ошибки, потому что данная функция просто не содержала какого-либо описания, хотя и обязательна (ранее функция просто сразу возвращала нужное значение).

Алгоритмы операций

Erase

Рассмотрим алгоритм использования FLM-файла средой Keil при операции стирания, которая может быть стиранием по секторам или стиранием всей микросхемы.
Когда в опциях отладчика (Рисунок 1) выбрано стирание по секторам, то выполняется определенный алгоритм, в котором, очевидно, из 7 функций задействованы не только функции, относящиеся к стиранию.  Согласно документации, CMSIS:

Рисунок 2 — Алгоритм операции стирания

FLM загружается в RAM-память. Далее исполняется функция Init() – процедура инициализации. Она необходима для отключения прерываний и настройки тактирования. Важно отметить, что программирование внутренней памяти выполняется при тактировании ядра от внутреннего HSI-генератора. Это позволяет всегда программировать микросхему независимо от того, установлен ли внешний источник частоты на плате или нет. Для микросхем К1986ВК214/234 у данной функции есть дополнительный функционал. Его рассмотрим отдельно. В зависимости от того, что выбрано в окне отладчика — Рисунок 1, поле Download Function (выбрать можно одну из 3-х опций: Erase Chip, Erase Sectors, Do not Erase) происходит стирание полное или по секторам.

При выполнении полного стирания вызывается функция EraseChip (), внутри которой происходит вызов библиотечной EEPROM_EraseAllPages() (драйвер MDR32F9Qx_eeprom.c официальной библиотеки), которая сотрет весь чип. Аппаратно микросхемы К1901ВЦ1QI, К1986ВЕ1QI, К1986ВЕ92QI поддерживают полное стирание всей памяти или стирание страницы. (К1986ВК214/234 вновь отличается – вернёмся к нему отдельно)

При процессе стирания по секторам, как видно из диаграммы рисунка 2, для начала вызывается функция BlankCheck (), которая выполняет роль проверки стертой страницы, сравнивая данные памяти по каждому адресу сектора со значением стертой ячейки. Если ячейка не пуста, то тогда уже вызывается функционал EraseSector(), внутри которого реализована библиотечная функция EEPROM_ErasePage(), потом проверяется следующая страница. Условимся, что в терминах документации на микроконтроллеры, стереть можно страницу, а в терминах функций FLM стирается сектор, поэтому пускай это будет одно и тоже. Если BlankCheck () сразу дает информацию о том, что страница пуста, то понятно, что EraseSector() не вызывается, что позволяет лишний раз не стирать микросхему, тем самым продлевая ресурс FLASH-памяти, который ограничен определенным количеством циклов перезаписи.

После следует вызов UnInit (), в которой происходит процедура деинициализации: возвращение настроек частоты, разрешение прерываний.

Programm/Verify

В алгоритме программирования данных, как и в случае стирания, также запускаются функции Init() и UnInit ().  

Рисунок 3 — Алгоритм операции программирования

По сути, они являются сопутствующими для всех операций с памятью. В зависимости от того, в связке с какой процедурой — стиранием или программированием – вызываются, они могу давать разный функционал. Для этого на их вход и подается третий аргумент:

/*

 *  Initialize Flash Programming Functions

*    Parameter:      adr:  Device Base Address

 *                    clk:  Clock Frequency (Hz)

 *                    fnc:  Function Code (1 - Erase, 2 - Program, 3 - Verify)

 *    Return Value:   0 - OK,  1 - Failed

 */

int Init (unsigned long adr, unsigned long clk, unsigned long fnc) {…}

Далее на рисунке 4 приведён алгоритм верификации, как он реализован и какие функции вызываются.

Рисунок 4 — Алгоритм операции верификации

Особенности работы с К1986ВК214/234

Как было подмечено ранее, у  микросхем для электросчетчиков К1986ВК214/234 есть свои особенности. Уделим им особое внимание.

1.       Контроллер FLASH-памяти, в отличие от микроконтроллеров К1901ВЦ1QI, К1986ВЕ1QI, К1986ВЕ92QI, не умеет стирать всю память, а только блок размером 32 Кбайта или страницу (которую принимаем за сектор) размером 512 байт. Поэтому для стирания всей памяти организован цикл последовательного стирания всех блоков:

for (i = 0; i < BLOCK_SIZE * CNT_BLOCK; i += BLOCK_SIZE)

EEPROM_EraseBlock(i, EEPROM_Main_Bank_Select);

Каждый блок имеет размер по 32 КБ. У контроллера К1986ВК234 4 блока, у К1986ВК214 всего 2.

2.       Второе важное примечание заключается в том, что загрузчик всех 2-х номиналов микросхем расположен в информационной FLASH-памяти и его можно «затереть», работая с микросхемой. В связи с тем, что таблица векторов непереносима и всегда расположена с адреса 0x0, то с данного адреса может отображаться две области внутренней FLASH-памяти:

  • EEPROM_Info_Bank (0x0000_0000 — 0x0000_1FFF) – область информационной FLASH-памяти с загрузочной программой объёмом 8 Кбайт;
  • EEPROM_Main_Bank (0x0000_0000 — 0x0001_FFFF) – область основной FLASH-памяти программ объёмом 128 Кбайт. (64 Кбайта в случае, если это микросхема К1986ВК214)

Область памяти, которая отображается с адреса 0х0, определяется значением бита FPOR регистра BKP_REG_0E. А именно:

FPOR = 0 — EEPROM_Info_Bank

FPOR = 1 — EEPROM_Main_Bank.

Таким образом, при старте по питанию микросхемы всегда начинают выполнять программу с информационной памяти, где располагается загрузчик. Загрузчик считывает комбинацию на выводах MODE и, если она равна b’00, то устанавливается бит FPOR=1, происходит сброс и переключение памяти на основную. Подробнее об этом можно прочитать в статье про старт микроконтроллеров.

В связи с этими особенностями функционал FLM функции Init (…) при процедуре стирания памяти для микроконтроллеров К1986ВК234/214 расширен:

if (fnc == 1)

    {         

        if ((MDR_BKP->REG_0E & BKP_REG_0E_FPOR) == 0x00000000)

        {

            for (i = 0; i < PAGE_SIZE * PAGES_TO_BOOT; i += PAGE_SIZE)

                EEPROM_ErasePage(i, EEPROM_Info_Bank_Select);

            for (i = 0; i < BOOT_SIZE; i++)

                EEPROM_ProgramWord (i*4, EEPROM_Info_Bank_Select, BOOT_LOADER[i]);

        }

     }

Тут происходит проверка, установлен ли бит FPOR, и если нет, то стирается информационная память и в неё записывается стандартный бутлоадер, режимы которого указаны спецификации.

3. Последнее важное, на чем стоит застроить внимание — это процедура верификации. В виду того, что память отображается в адресном пространстве в зависимости от значения бита FPOR, выполняя процедуру верификации (то есть чтение и сравнение), можно получить ошибки. Например, если FPOR сброшен и загрузчик стартовый удален, то будет выполнено программирование основной памяти, а чтение данных для верификации будет происходить из информационной памяти, поскольку, как уже отмечено ранее, FPOR=0. Поэтому для микросхем К1986ВК234/214 реализован механизм верификации с помощью чтения регистровым доступом:

#define READ_BY_EEPROM

uint32_t ReadWord(uint32_t Address)

{

#ifndef READ_BY_EEPROM

    return *((volatile uint32_t *)(Address));

#else

    return EEPROM_ReadWord(Address, BANK_SELECTOR);

#endif

Тестирование

Для каждого микроконтроллера реализованы проекты тестирования разработанных FLM, которые проверяют функционал реализованных функций. Сам алгоритм использован тот, который предложен в шаблонном примере — FlashTest.c (шаблон от Keil).

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

Исходные файлы алгоритмов FLM и файлы их тестирования для рассмотренных в статье микросхем могут быть предоставлены по запросу на адрес технической поддержки support@milandr.ru

Сохранить статью в PDF

Понравилась статья? Поделить с друзьями:

Не пропустите также:

  • Как найти информацию по электронной почте
  • Маленькие клопы как их найти
  • Как найти ценных работников
  • Как найти серую кошку
  • Как найти сайты где ссылки на вас

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии