Stm32 модуль защиты памяти

Обновлено: 07.07.2024

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

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

Электроны попадают на плавающий затвор в процессе записи данных, туннелируя через изолятор. Этот процесс наглядно показан в фильме Чародеи — главное хорошо разогнаться, видеть цель и не замечать препятствий. Разгоняются электроны при пропускании тока в канале.

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

Чтобы сэкономить время, стирают память большими блоками — секторами. В случае STM32 минимальный размер — 16 килобайт — имеют 4 сектора, расположенные по младшим адресам. Записывать наш STM32 умеет по одному байту, по два, или четыре. Стертый транзистор читается как логическая единица. Соответственно, при записи мы поселяем электроны на затворы тех транзисторов, которые соответствуют логическим нулям в записываемых данных. Отсюда интересное наблюдение — мы можем выставлять в ноль биты в одном и том же байте один за другим, а не все сразу. Обратная операция — выставить нулевой бит в единицу — невозможна без стирания. При записи единичного бита содержимое памяти не изменяется.

Проблема стабильности чтений

Что же произойдет, если мы выключим питание в момент записи данных? Понятно, что часть данных окажется незаписанной. А что призойдет с тем байтом или словом, которое мы записывали в момент выключения питания? При записи на плавающий затвор может попасть разное количество электронов. Много электронов читается как 0, мало — как 1, значит есть и некоторое пограничное количество электронов. Если до выключения питания на затвор попадет количество электронов, близкое к пограничному, то при чтении мы можем получать как 0, так и 1 в зависимости от совершенно случайных факторов. Со временем заряд будет стекать с затвора, так что вероятность прочитать 1 будет расти. Эта крайне неприятная особенность делает ненадежной даже схему с использованием контрольной суммы. Если питание отключилось в момент записи последнего слова нашего пакета с данными, которые мы можем сопроводить любым количеством проверочной информации, то мы можем сегодня прочитать наши данные, а завтра нет, или наоборот. Более того, нас ждут неприятности и при записи в область, которую мы считаем стертой, потому что она сегодня читается как все единицы — ведь завтра там могут проступить нули и испортить наши данные.

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

Реализация с гарантией целостности данных

Теперь мы готовы рассмотреть схему хранения данных, которая гарантирует целостность данных в смысле, обсуждавшемся выше. Поскольку STM не скупится на размер флэша, было решено упростить конструкцию, отказавшись от экономии, и использовать модель, где все данные объединены в единственную структуру фиксированного размера. При обновлении данных мы записываем всю структуру целиком. Разные версии данных записываются последовательно в предварительно стертую область флэш памяти. Актуальными считаются данные, записанные последними.

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

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

  • Не делать лишних записей
  • Иметь возможность понять, почему не совпадает контрольная сумма. Если при этом проверочный байт правильный, то несовпадение контрольной суммы однозначно указывает на то, что содержимое сектора повреждено или не до конца стерто.

Итак, мы можем гарантировать, что будучи однажды прочитанными, данные будут читаться и дальше. Однако, с прочими свойствами надежного хранилища данных все не так радужно. Очевидна проблема с необходимостью периодически стирать сектор, когда там заканчивается место. Если при этом выключится питание, мы не только не запишем новые данные, но и потеряем старые. Несколько менее очевидна проблема с неправильно записанными данными (в результате отключения питания во время записи). Мы не можем гарантировать, что со временем там не проступят отсутствующие биты и мы не начнем читать эти данные как правильные. Может показаться, что дополнительные статусные биты, маркирующие запись как неправильную, могут спасти положение, однако это не так. Ведь питание может пропасть и при записи этих дополнительных бит, и в итоге проблем станет только больше. Схема, описанная выше, успешно использует корректирующие записи только потому, что они записывают ровно те же данные, что и первоначальная запись, поэтому при любой последовательности отключений питания последняя успешная запись переводит флэш в стабильное состояние. Конечно, и в таком виде описанное хранилище может использоваться в приложениях не предъявляющих повышенные требования к надежности хранения. Но оказывается, что на базе двух хранилищ описанного типа можно создать более надежный вариант, лишенный описанных недостатков. Схема такого хранилища показана на следующем рисунке.

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

Тестовый проект

Лежит тут. Проект создан с помощью STM32CubeMX под компилятор IAR EWARM для платы STM32-H405. Использование STM32CubeMX для компиляции проекта оставило только положительные эмоции. Особенно радует дерево клоков — та часть, которая раньше была для меня областью магии, теперь упростилась до нескольких кликов мышью. Проект легко адаптировать под другие процессоры STM32 или компиляторы просто перегенерив его с помощью STM32CubeMX. Код хранилища данных легко адаптировать и под другие архитектуры, поскольку работа с флэшом вынесена в отдельный модуль с абстрактным интерфейсом. В составе пректа есть автоматический тест хранилища данных, который использует сторожевой таймер для сброса процессора в случайный момент времени. Кроме того, в проекте есть тестовая реализация USB CDC протокола, которая просто отсылает назад все принятые строки. Я добавил ее, поскольку меня интересовали 2 вопроса. Во-первых, что происходит с известными мне проблемами в реализации USB стэка. Оказалось, что ничего — старые проблемы не исправляются, новых не появляется. Видимо такова политика кампании — кто знает про ZLP — сделает сам, кто не знает — заплатит за поддержку. Во-вторых, было интересно, как стирание флэша влияет на работу USB, ведь при этом процессор может останавливать выборку команд из флэша. Оказалось, что не влияет.

[Учебник по STM32H7] Глава 23 Блок защиты памяти STM32H7 MPU (важно)

В этой главе рассказывается о важном элементе знания о MPU (блок защиты памяти) при изучении STM32H7, который также имеет эту функцию на микросхемах STM32F1 и F4, но в основном он не используется. Но в H7 вы должны использовать его, потому что вам нужно установить кеш.

23.1 Важные советы для начинающих

23.2 Введение в MPU

23.3 Функция реализации MPU

23.4. Три типа памяти, которые может настраивать MPU

23,5 регистров MPU и соответствующие параметры библиотеки

23.6 Функции конфигурации MPU

23.1 Важные советы для начинающих

  1. Эта глава предназначена главным образом для того, чтобы проложить путь к объяснению следующей главы «Кэш». Новичкам необходимо освоить базовые знания, связанные с этой главой.
  2. При первом изучении MPU могут быть некоторые моменты знания, которые нельзя понять сразу. После различных применений в последующих главах будет более глубокое понимание. Эта точка знаний будет в основном проходить через весь учебник.

23.2 Введение в MPU

MPU может различать карту памяти карты памяти на несколько областей с определенными правилами доступа, которые могут выполнять следующие функции:

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

Проще говоря, это защита памяти, защита периферийных устройств и защита доступа к коду.

Карта памяти - это общее адресуемое пространство 32-битного ядра CM7, которое может быть адресовано от 0 до 2 ^ 32 -1, всего 4 ГБ. Эти адреса могут иметь доступ к RAM, Flash, периферийным устройствам и т. Д. Ниже приведена схема отображения памяти: когда производители микросхем используют ее, они будут разделены для добавления соответствующих аппаратных функций.


23.3 Функция реализации MPU

MPU может быть сконфигурирован для защиты 16 областей памяти (16 областей памяти конфигурируются независимо). Каждая область требует минимум 256 байтов. Каждая область также может быть сконфигурирована как 8 подобластей. Поскольку субрегионы обычно имеют одинаковый размер, размер каждого субрегиона составляет 32 байта, что в точности соответствует размеру строки кэша.

16 областей памяти, которые могут быть сконфигурированы MPU, имеют серийные номера в диапазоне от 0 до 15 и область по умолчанию, также известную как фоновая область, с серийным номером -1. Поскольку эти области памяти могут быть вложенными и перекрываться, возникает проблема приоритета, когда эти области являются вложенными или перекрываются. Серийный номер 15 имеет самый высокий приоритет, поэтому он уменьшается, а серийный номер -1, то есть фоновая область имеет самый низкий приоритет. Эти приоритеты фиксированы.

Вот конкретный пример, чтобы помочь всем понять. Как показано ниже, есть 7 областей, фоновая область и области с номерами 0-5. Область памяти 4 перекрывается с областями памяти 0 и 1, затем область перекрытия будет выполняться в соответствии с правилами конфигурации области памяти 4, область памяти 5 полностью содержится в области памяти 3, затем эта часть области памяти будет настроена в соответствии с областью памяти 5. Правило исполнения.


23.4. Три типа памяти, которые может настраивать MPU

Три типа памяти, которые может настраивать MPU:

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

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

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

23,5 регистров MPU и соответствующие параметры библиотеки

Что касается введения регистров MPU, в руководстве по программированию STM32H7 есть специальное объяснение. Здесь мы сосредоточимся на регистре MPU_RASR и регистре управления. Определение этого регистра следующее:


23.5.1. XN бит регистра RASR

XN = 0 означает, что выбор инструкций включен, то есть эта область памяти может выполнять программный код, XN = 1 означает, что выбор инструкций запрещен, то есть этой области памяти запрещено выполнение программного кода.

Соответствующие параметры MPU библиотеки HAL следующие:

23.5.2. Бит AP в регистре RASR

Конкретное определение AP заключается в следующем:


Параметры MPU библиотеки HAL, соответствующие этим параметрам, следующие:

23.5.3. Биты TEX, C, B и S регистра RASR

(Обратите внимание, эти биты очень важны. Вам нужно только знать текущие знания, и в следующей главе будет конкретно объяснена конкретная роль)

Ниже приведены определения TEX, C, B и S. Это касается только TEX = 0b000 и 0b001. Другие конфигурации TEX в основном не используются.


TEX используется для настройки политики кэширования и поддерживает следующие четыре ситуации, которые могут быть достигнуты только путем настройки битов C и B.


Даны три параметра MPU для библиотеки HAL, соответствующей TEX.На практике используются только первые два MPU_TEX_LEVEL0 и MPU_TEX_LEVEL1.

Параметры MPU библиотеки HAL, соответствующие биту C, являются следующими, которые используются для включения или отключения кэша.

Параметры MPU библиотеки HAL, соответствующие биту B, следующие, используются для взаимодействия с битом C для реализации необходимости использования буферизации в режиме кэширования.

Параметры MPU библиотеки HAL, соответствующие биту S, являются следующими, которые используются для решения проблемы совместного использования доступа с несколькими шинами или с несколькими ядрами.

23.5.4. Бит SRD в регистре RASR

Этот бит используется для управления подобластью области памяти. Используется бит [15: 8], всего 8 бит. Один бит управляет одной подобластью.

В общем, функция запрета субрегиона в основном не используется, поэтому, когда настроен параметр SubRegionDisable библиотеки HAL, он может быть непосредственно установлен равным 0x00, что указывает на то, что все 8 субрегионов включены.

23.5.5 Бит РАЗМЕРА в регистре РАСР

Биты РАЗМЕРА используют бит [5: 1], всего 5 битов, которые могут представлять 2 ^ 5 = 32 размера. Соответствующая библиотека HAL дает 28 параметров, которые можно настроить:

23.5.6 Биты регистра CTRL

Биты регистра управления определены следующим образом:


23.6 Функции конфигурации MPU

Файл stm32h7xx_hal_cortex.c библиотеки HAL предоставляет три функции для конфигурации MPU:

  • HAL_MPU_Disable
  • HAL_MPU_Enable
  • HAL_MPU_ConfigRegion

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

23.6.1 Функция HAL_MPU_Disable

Функциональный прототип:

Описание функции:

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

23.6.2 Функция HAL_MPU_Enable

Функциональный прототип:

Описание функции:

Эта функция используется для включения MPU. Обычно используется параметр MPU_PRIVILEGED_DEFAULT.

Параметры функции:

Эта функция поддерживает следующие параметры:

1、 MPU_HFNMI_PRIVDEF_NONE ((uint32_t)0x00000000)

  • Этот параметр устанавливает бит PRIVDEFENA регистра управления CTL MPU на 0.

Указывает, что фоновая область отключена, и доступ к любой области, где MPU не включен, вызовет исключение памяти MemFault.

  • Этот параметр устанавливает бит HFNMIENA регистра управления CTL MPU на 0.

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

2、 MPU_HARDFAULT_NMI ((uint32_t)0x00000002)

  • Этот параметр устанавливает бит PRIVDEFENA регистра управления CTL MPU на 0.

Указывает, что фоновая область отключена, и доступ к любой области, где MPU не включен, вызовет исключение памяти MemFault.

  • Этот параметр устанавливает бит HFNMIENA регистра управления CTL MPU на 1.

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

3、 MPU_PRIVILEGED_DEFAULT ((uint32_t)0x00000004)

  • Этот параметр устанавливает бит PRIVDEFENA регистра управления CTL MPU на 1.

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

  • Этот параметр устанавливает бит HFNMIENA регистра управления CTL MPU на 0.

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

4、 MPU_HFNMI_PRIVDEF ((uint32_t)0x00000006)

  • Этот параметр устанавливает бит PRIVDEFENA регистра управления CTL MPU на 1.

Указывает, что фоновая область отключена, и доступ к любой области, где MPU не включен, вызовет исключение памяти MemFault.

  • Этот параметр устанавливает бит HFNMIENA регистра управления CTL MPU на 1.

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

23.6.3. Функция HAL_MPU_ConfigRegion

Функциональный прототип:

Описание функции:

Эта функция используется для настройки MPU.

Параметры функции:

Формальным параметром этой функции является структурная переменная типа MPU_Region_InitTypeDef, которая определяется следующим образом:

В дополнение к параметрам Number и BaseAddress, некоторые другие параметры описаны в Разделе 23.5 этой главы.

Этот элемент используется для установки серийного номера области памяти.При настройке рекомендуется начинать с номера 0, до номера 15, всего 16. Соответствующие параметры библиотеки HAL следующие:

Этот элемент структуры используется для установки первого адреса области памяти. Этот параметр более элегантен с конфигурацией размера элемента структуры. Вы должны убедиться, что первый адрес выровнен с размером области памяти. Например, если настроена область памяти 64 КБ, первый адрес должен быть выровнен с 64 КБ, то есть Пара адресов составляет 64 КБ, то есть остаток 0x00010000 равен 0, запомните.

Пример использования:

Две группы настроены ниже.

23.7 Резюме

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

Это перевод апноута AN4838 [1] (автор перевода Marat Galyamov), который описывает управление модулем защиты памяти (memory protection unit, MPU) в архитектуре STM32. Блок MPU является опциональным (программно подключаемым) компонентом для защиты памяти. Включение модуля MPU в STM32 делает работу процессора более устойчивой и надежной. MPU должен быть настроен и включен перед использованием. Если модуль MPU не включен, то нет никаких изменений в поведении системы памяти. Этот апноут касается все продуктов STM32, включая Cortex®-M0+/M3/M4 и M7 варианты, которые поддерживают MPU.

• STM32F7 Series Cortex®-M7 processor programming manual (PM0253)
• STM32F3 and STM32F4 Series Cortex®-M4 programming manual (PM0214)
• STM32F10xxx/20xxx/21xxx/L1xxxx Cortex®-M3 programming manual (PM0056)
• STM32L0 Series Cortex®-M0+ programming manual (PM0223)

Таблица 1. Процессоры STM32, которые имеют блок MPU.

[Обзор системы MPU]

MPU может быть использован для повышения устойчивости и надежности системы, предоставляя следующие возможности:

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

MPU может быть использован для защиты 8 областей памяти. В свою очередь, они могут иметь 8 подобластей, если размер области не менее 256 байт. Подобласти всегда имеют равный размер, и могут быть включены или отключены с использованием номера подобласти. Поскольку минимальный размер области зависит от длины строки кэша (32 байта), 8 подобластей по 32 байта соответствует 256 байтам.

Области пронумерованы от 0 до 7. В дополнение есть другая область, названная областью по умолчанию, с идентификатором -1. Все области памяти в диапазоне от 0 до 7 имеют приоритет над областью по умолчанию.

Области могут перекрываться, и могут быть вложенными. Область 7 имеет более высокий приоритет, область 0 - низкий, и это определяет поведение перекрывающихся областей. Приоритеты фиксированы и не могут быть изменены.

Рис. 1 показывает пример с 6-ю областями. Этот пример показывает, что область 4 перекрывает области 0 и 1. Область 5 включена полностью в область 3. Так как приоритет идет в порядке возрастания, у областей перекрытия (желтые) есть приоритет над другими областями. Таким образом, если область 0 определена как доступная для записи, а область 4 наоборот, недоступная, то адрес, попадающий в зону перекрытия областей, не будет доступен для записи.

Рис. 1. Пример перекрытия областей.

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

MPU может быть использован для определения других атрибутов памяти, таких как кэширование, которые могут быть экспортированы (применены) в системном блоке кэш-памяти или в контроллере памяти.

Управление кэшем происходит глобально посредством регистра команд кэша, но MPU может определить политики кэша, и будет ли область кэшируемой или нет. MPU позволяет установить атрибуты кэша для области кэшем уровня L1 (доступно только для серии STM32F7, у которой есть кэш L1).

Модель памяти. В архитектуре STM32 процессор имеет по умолчанию фиксированную карту распределения памяти, которая предоставляет до 4 гигабайт адресуемой памяти. На рис. 2 показана карта распределения памяти.

Рис. 2. Распределение адресного пространства процессора.

[Типы памяти, регистры и атрибуты]

Карта распределения памяти и настройка MPU разделяют карту на области. Каждая область имеет определенный тип памяти и её атрибуты.

Типы памяти. Есть три общих типа памяти:

• Память Normal: позволяет загружать и хранить байты, полуслова и слова, которые будут эффективно расположены процессором (компилятору неизвестно о типах областей памяти). Для этой области загрузка/хранение не обязательно выполняется для процессора в том порядке, который определен в исходном коде программы.

• Память Device: в этом регионе загрузка и хранение совершаются в строго упорядоченном виде. Это должно гарантировать, что последовательность загрузки регистров выполнена в надлежащем порядке.

• Строго упорядоченная память: всегда выполняется в порядке, написанном в программе, ожидает завершения инструкции записи/хранения (эффективный доступ к шине) прежде, чем выполнить следующую инструкцию в потоке программы. Это может серьезно увеличить производительность.

Регистры MPU. Регистры MPU расположены по адресу 0xE000ED90. Есть 5 основных регистров MPU и несколько регистров-псевдонимов (alias registers) для каждой области. Следующие регистры используются для установки областей в MPU:

MPU_TYPE регистр только для чтения, используется для детекции наличия MPU.
MPU_CTRL регистр управления.
MPU_RNR номер области, используется для определения допустимых операций к ней.
MPU_RBAR базовый адрес области.
MPU_RASR атрибуты области и размер.
MPU_RBAR_An n-й псевдоним MPU_RBAR, где n в диапазоне от 1 до 3 (a) .
MPU_RASR_An n-й псевдоним MPU_RASR, где n в диапазоне от 1 до 3 (a) .

Примечание (a): В Cortex®-M0+ эти регистры не реализованы.

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

Атрибуты памяти. В регистре атрибутов и размера (MPU_RASR) устанавливаются все атрибуты памяти. Таблица 2 показывает краткое описание регистра атрибутов и размера.

Таблица 2. Регистр атрибутов и размера области - MPU_RASR.

XN. Этот флаг XN управляет выполнением кода. В случае выполнения команд в области должен быть разрешен доступ на чтение на уровне привилегий и XN должен быть 0. Иначе будет сгенерирована ошибка управления памятью (MemManage).
AP. Это поле разрешения доступа к данным (см. таблицу 3).

Таблица 3. Права доступа к регионам памяти.

S. Поле S для области совместно используемой памяти: система обеспечивает синхронизацию данных между устройствами управления шиной в системе с многократными устройствами управления шиной, например, процессор с контроллером DMA.
TEX, C, B. Биты TEX, C и B используются для определения свойств кэша области и возможности его совместного использования. Варианты представлены в таблице 4.

Таблица 4. Свойства кэша и его совместное использование.

SRD. Это биты отключения подобласти, которые определяют, включена ли определенная подобласть или нет. Отключение подобласти означает, что другая область, перекрывающая отключенный диапазон, соответствует ей вместо этого. Если никакая другая включенная область не перекрывает отключенную подобласть, MPU генерирует ошибку.

Для моделей с реализованным кэшем (только для серии STM32F7, у которой есть кэш L1) существуют дополнительные атрибуты памяти:

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

[Сравнение характеристик MPU у различных моделей процессоров]

Есть несколько различий между уровнями MPU Cortex®-M0+, Cortex®-M3/M4 и Cortex®-M7, поэтому программист должен знать о них, если в программном обеспечении будет использоваться конфигурирование MPU. Таблица 5 показывает различия характеристик MPU между Cortex®-M0+, Cortex®-M3/M4 и Cortex®-M7.

Таблица 5. Сравнение характеристик MPU STM32.

Примечание (1). Cortex®-M0+ поддерживает один уровень политики кэша, поэтому поле TEX недоступно в cortex®-M0+.

Таблица 6 описывает пример настройки MPU со следующими областями памяти: Внутренняя память SRAM, Флэш-память и периферийные устройства. По умолчанию карта распределения памяти используется для привилегированного доступа в качестве фоновой области памяти, MPU не включен для обработчика отказа HARD и NMI.

Внутренняя память SRAM: 8 Kбайт статической памяти (SRAM) будут сконфигурированы как Region0. Атрибуты памяти: общедоступная память, прямая запись без выделения записи, права полного доступа и включено выполнение кода.

Флэш-память : вся флэш-память будет сконфигурирована как Region1. Атрибуты памяти : не общедоступная память, прямая запись без выделения записи, права полного доступа и включено выполнение кода.

Область периферии: будет сконфигурирована как Region2. Атрибуты памяти: общедоступное устройство, права полного доступа и выключено выполнение кода.

Таблица. 6. Пример настройки MPU.

[Заключение]

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

У микроконтроллеров собственной памяти мало, даже если говорить о каком-нибудь жирном прежирном Corteх, все равно: как волка ни корми, а у медведя, т.е. полноценного компьютера, толще. Поэтому практически все микроконтроллеры, в своем жирном исполнении так или иначе позволяют подцеплять к себе внешнюю параллельную память. Даже древний, как говно мамонта, АТ89С51 это умел. Что уж говорить про AVR и STM32.

▌Параллельная память


Фактически же ,контроллера там два. Один отвечает за работу с типом NOR/PSRAM, другой с NAND/PC Card у этих интерфейсов несколько различается логика работы, но они довольно похожи. При этом у них те сигналы, что совпадают для каждого типа тут общие, ну там шина данных, шина адреса, линии Write-Read, а какие то специфичные линии для каждого типа памяти раздельно свои. Что позволяет одновременно цепляться на одну шину разные девайсы и они могут работать вместе, разумеется попеременно.

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



Выводы той части контроллера, что NOR/PSRAM там в даташите расписаны прям отдельно.


▌Ошибки
Также этот контроллер может генерировать ошибки, которые приводят к исключению Hard Fault.

Ошибки возникают в следующих случаях:

  • Когда мы пишем или читаем из адресного пространства FSMC, но оно не включено. Т.е. мы пытаемся обращаться в пустоту. Это, кстати, характерно не только для FSMC, но и для многих других блоков.
  • Когда мы обращаемся к NOR Flash памяти, но бит FACCEN регистра FSMC_BCRx для конкретного банка сброшен. Напомню, что банок там четыре.
  • Когда мы обращаемся к PC Card, а пин ее физического наличия не выставлен.

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

Выводы? С выводами все довольно просто, ищем спецификацию интерфейса и узнаем, что у нас тут есть:

Так как у нас интерфейс i8080, то подключаться мы будем как NOR/PSRAM устройство к соответствующему контроллеру.

Подключается это все следующим образом.

Но тут есть одно очень и очень неоднозначное западло.
Дело в том, что внешняя адресация FMSC зависит от разрядности шины данных (RM00088 стр. 511 табл. 101). Если у нас разрядность шины данных составляет 8 бит, то все нормально, адрес в адресном пространстве равен адресу на линиях данных. Т.е. если банк начинается с адреса 0x60000000, то при записи в 0x60000000 А0=0, а запись в 0x60000001 сделает линию А0 = 1. Т.е. младший бит адресной шины соответствует младшему биту адреса.

Но если мы переводим шину в 16 разрядный режим, то у нас включается внешняя адресация в словах.

Выглядит это как сдвиг адреса вправо перед тем как он попадет на шину адреса Ах. Т.е. адрес делится на два, оно и логично, данные то у нас уже словами пошли. И тогда для того, чтобы поднять А0 надо записать не по адресу 0x60000001 (это будет всего лишь адрес второго байта первого слова, что был на шине данных при адресе 0x60000000), а по адресу следующего слова, т.е. 0x60000002 и тогда у нас выйдет бит на А0. То же касается, естественно, и других линий А0 в 16 разрядном режиме.

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

Тут все довльно просто, сразу указано как настроена нога, поясню только что что OUT_APP, означает режим альтернативной функции (AFIO PushPull), с подтяжкой в LOW. Настраивайте это как хотите у себя, хоть через HAL, хоть через spl, хоть LL.

Также, надо настроить тактирование периферии:

RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // Тактирование портов RCC->APB2ENR |= RCC_APB2ENR_IOPBEN; //RCC->APB2ENR |= RCC_APB2ENR_IOPCEN; RCC->APB2ENR |= RCC_APB2ENR_IOPDEN; RCC->APB2ENR |= RCC_APB2ENR_IOPEEN; //RCC->APB2ENR |= RCC_APB2ENR_IOPFEN; //RCC->APB2ENR |= RCC_APB2ENR_IOPGEN; RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; // Включаем AFIO RCC->AHBENR |= RCC_AHBENR_FSMCEN; // Включаем FSMC AFIO->MAPR &=

AFIO_MAPR_SWJ_CFG_Msk; // Делаем ремап линий JTAG, точнее включаем JTAG, оставляем только SWD AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_JTAGDISABLE; // Иначе он попадает на ноги.

Теперь определяем наш базовый адрес. Для удобства сведено все в структуру. Сидеть все будет так, CS на NE1, нога RS на А16

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

Теперь немного поясню. Т.к. контроллер у нас NORSRAM используется, то настраивать и включать будем его. Банк у нас 1, без вариантов. В CMSIS все это определено как то коряво, как массив, где регистры FSMC_BTR и FSMC_ BCR никак не разделены, а слиты в кучу, словно они поленились. Приходится их индексным методом доставать, благо они парами идут в памяти, друг за другом. поэтому для BCR1 адрес от базового FSMC_Bank1_NORSRAM1, а для BTR адрес FSMC_Bank1_NORSRAM1+1.

Дальше там биты, сделал как в старом добром AVR, я так люблю :))))

Начинаем настраивать. Сначала определимся в каком режиме все должно работать. Смотрим времянки на интерфейс и на то, что контроллер нам предлагает. В нашем случае это NOR Flash или Mode 2.

Смотрим табличку 114 в RM00008


Определиться надо всего с парой бит. Все остальное или предрешено или не имеет значения :)

Теперь надо определиться с таймингами записи и чтения. У нас за них отвечает регистр FSMC_BTR1. И нам в Mode 2 доступны только две настройки:





Смотрим табличку 115 в RM00008

  • BUSTURN нам не нужен, это для пакетной записи в PSRAM.
  • DATAST время за которое данные устаканятся на шине данных и дисплей готов будет их считать.
  • ADDSET время за которое установится и будет воспринят адрес на адресной шине. Т.к. у нас адресная шина рулит линией RS, то надо смотреть время от RS до готовности считать данные.

Смотрим даташит на дисплей:




Картинку конечно рисовал наркоман. Все в кашу. У них там чтоль вообще нормоконтроля нет? Наша Геннадьевна бы из за такой чертеж выдрала без вазелина шваброй.

Время от активации чипа через CS до готовности ловить строб совпадает со временем установки RS, его тут назвали А0, видимо подразумевая, что эта линия всегда будет на адресной линии А0. Говорю же, наркоманы. Это время tAS8 и табличка гласит, что оно может быть от 0. Т.е. на запись ADDSET можно смело ставить ноль.

А вот время от начала движухи на линии DB0..7 и до готовности, на перегибе строба WR обозначено как tDS8 И оно у нас тут 10ns для записи.

Для чтения же время от того как у нас упадет линия строба RD, и тем самым даст понять дисплею, что с него хотят читать равно tACC8 и составляет от 0 до 20ns. Берем по максимуму 20ns.

Чтобы не заморачиваться с настройкой разных таймингов для чтения и записи через EXTMOD примем их по максимальному значению 20ns. А теперь давайте думать сколько записать в DATAST. Из графика 191 узнаем, что длина этих циклов это DATAST+1 тик HCLK

FSMC тактуется от шины HCLK, которая сидит после предделителя AHB. У меня частота настраивается вручную и я точно знаю, что у меня там 72МГц. Вам же этот вопрос надо будет выяснить. А раз так, то один тик HCLK = 1/72000000 = 1.4Е-8 т.е. около 14ns т.е. уже больше тайминга. А нам надо 20ns. Т.е. в DATAST запишем 1, будет даже немного с запасом.

Теперь код инициализации FSMC будет предельно понятен.

Осталось написать функции записи команды и данных. МЫ просто берем ранее сделанную структуру с адресами и просто пишем туда данные:

void LCD_CmdWrite(const uint16_t Command) < LCD->LCD_REG = Command; >void LCD_DataWrite(const uint16_t Data) < LCD->LCD_RAM = Data; >void LCD_WriteReg(const uint8_t LCD_Reg, const uint16_t LCD_RegValue)

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

IO_SetLine(io_TFT_Reset, HIGH); delay_ms(5); LCD_WriteReg(0x01, 0x01); //PWRR: LCD on, sleep off, reset ON delay_ms(1); LCD_WriteReg(0x01, 0x00); //PWRR: LCD on, sleep off, reset OFF delay_ms(1); LCD_WriteReg(0x88, 0x0B); //PLL Control Register 1 delay_ms(1); LCD_WriteReg(0x89, 0x02); //PLL Cntrol Register 2 бла бла бла

Собранный проект на FreeRTOS который ничего не делает, только инициализирует дисплей и заливает его туда сюда разными цветами прилагаю. Там все проще некуда.

Спасибо. Вы потрясающие! Всего за месяц мы собрали нужную сумму в 500000 на хоккейную коробку для детского дома Аистенок. Из которых 125000+ было от вас, читателей EasyElectronics. Были даже переводы на 25000+ и просто поток платежей на 251 рубль. Это невероятно круто. Сейчас идет заключение договора и подготовка к строительству!

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

39 thoughts on “STM32. Контроллер внешней параллельной памяти FSMC”


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

Обновления «по воздуху» и многочисленные отладочные интерфейсы умных устройств могут оказаться серьезной дырой в безопасности, которая позволит опытному злоумышленнику подменить заводскую прошивку своей собственной, — не говоря уж о банальной краже чужой интеллектуальной собственности. Глупый, глупый современный IoT!

Для чего нужен Secure Boot

В общем случае разработчику, который хочет защитить свое устройство, предстоит решить две ключевые задачи.

  • В первую очередь следует реализовать механизм подтверждения подлинности прошивки (аутентификации). Для этого используются различные криптографические алгоритмы (например, SHA-256 и NIST P256). Они позволяют убедиться, что на устройстве будет исполняться только доверенный код.
  • Кроме того, необходимо защитить память от внешних атак и лишить злоумышленника доступа к критичным регионам, который он может в теории получить с помощью уязвимостей ПО либо при использовании отладочных интерфейсов (например, JTAG) или логического анализатора.

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

Как это работает



Базовую безопасность при этом обеспечивают следующие меры: во-первых, исключается возможность альтернативных методов загрузки. Для этого применяется подтвержденный Secure Boot, который формирует root of trust в нашей системе. Во-вторых, приватные ключи шифрования должны храниться в прошивке устройства и быть индивидуальными.



Кроме того, конкретную реализацию криптографического алгоритма следует проверять на устойчивость к АВК (атака по второстепенным каналам, side-channel attack) или АМИС (атака методом индуцированных сбоев, fault injection attack). К этому мы еще вернемся.

Наконец, следует озаботиться защитой от нежелательного внешнего доступа. К счастью, многие разработчики уже научились отключать JTAG — самый желанный подарок для злоумышленника. Однако производители не стоят на месте и предлагают сегодня дополнительные средства обнаружить воздействие, такие как Anti-Tamper. Ими пользуются пока не так часто, как хотелось бы.

Аппаратные средства

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

Стоит заметить, что набор доступных средств защиты зависит от конкретного семейства МК (F, G, L и H). Демонстрационные примеры в пакете X-CUBE-SBSFU охватывают большую часть из этого набора, но за полной информацией в любом случае следует обращаться к документации. Конкретно сегодня нас интересуют:

    — ключевой материал о безопасности микроконтроллеров STM32; — руководство по фреймворку SBSFU в пакете XCUBE; — апноут для MPU (Memory Protection Unit); — мануал по механизмам защиты для ядра Cortex-M7; — даташит на МК H743; — референс на МК H743.

Все ссылки — на PDF.

Защита от чтения, RDP

Это базовый механизм безопасности, который предотвращает доступ к содержимому памяти микроконтроллера различными отладочными средствами (JTAG, SWV и ETM). Его применение рекомендуется во всех случаях на готовых серийных устройствах. Отключение RDP возможно только для первого уровня защиты и приводит к стиранию содержимого флеш-памяти. Включение второго уровня — необратимая операция для микросхемы.

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

На H743 за эту функцию отвечают биты RDP [15:8] в паре регистров FLASH_OPTSR_CUR и FLASH_OPTSR_PRG из области Option Bytes. При этом значение 0xAA соответствует нулевому уровню защиты (по умолчанию), значение 0xCC — первому, а любое другое — второму (максимальному) уровню.

Формально на диаграммах STMicroelectronics Option Bytes относятся к внутренней флеш-памяти, однако непосредственный доступ к ним невозможен. Для взаимодействия и внесения изменений пользователю нужно обращаться к регистрам и следовать определенной процедуре (подробнее см. раздел Option Bytes Modification на с. 157 RM0433).

Продолжение доступно только участникам

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

Вариант 2. Открой один материал

Александр Бурага

Инженер-конструктор радиоэлектронной техники. С вниманием следит за прогрессом IoT и носимой электроники.

Читайте также: