Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Кармин Новиелло - Освоение STM32.pdf
Скачиваний:
2743
Добавлен:
23.09.2021
Размер:
47.68 Mб
Скачать

Организация памяти

537

20.3. Как использовать CCM-память

Некоторые микроконтроллеры из семейств STM32F3/4/7 предоставляют дополнитель-

ную память SRAM, называемую памятью, связанную с ядром (Core Coupled Memory, CCM).

В отличие от обычной SRAM, эта память тесно связана с ядром Cortex-M. С этой областью памяти напрямую соединены как шина D-Bus, так и шина I-Bus (см. Рисунок 517), что позволяет выполнять состояние 0-ожиданий (0-wait state). И хотя вполне возможно хранить данные в этой памяти, такие как таблицы поиска и векторы инициализации, наилучшим использованием этой области является хранение критических и требующих большого объема вычислений процедур, которые могут выполняться в режиме реального времени. По этой причине в микроконтроллерах с CCM-памятью реализована

технология ускорения процедур (routine booster technology).

Рисунок 5: Прямое подключение ядра Cortex-M и SRAM CCM

Зачем использовать CCM-память для хранения кода вместо данных?

В Интернете довольно можно часто вычитать, что CCM-память может использоваться для хранения важных данных. Это гарантирует быстрый доступ к ним из ядра. Хоть в теории это и верно, но на практике это не дает преимуществ. Все микроконтроллеры STM32 с CCM-памятью также предоставляют SRAM, к которой можно обращаться при максимальной системной тактовой частоте с

17 Рисунок был составлен на основе того, что содержится в AN4296 от ST (http://www.st.com/web/en/resource/technical/document/application_note/DM00083249.pdf).

Организация памяти

538

состоянием 0-ожиданий18. Более того, SRAM может быть доступно как ЦПУ, так и контроллеру DMA, а CCM-память – только ядру Cortex. Вместо этого, когда код расположен в SRAM CCM, а данные хранятся в обычном SRAM, ядро Cortex находится в оптимальной конфигурации для Гарвардской архитектуры, поскольку обеспечивает доступ с состоянием 0-ожиданий (без ожидания) для шины I-Bus (осуществляющей доступ к CCM-памяти) и для шины D-Bus (доступной параллельно для SRAM)19.

Однако очевидно, что если детерминированная производительность не важна для вашего приложения, и вам требуется дополнительное хранилище SRAM, то CCM-память является хорошим резервом для памяти данных.

Во всех микроконтроллерах STM32 с этой дополнительной памятью SRAM CCM отображается, начиная с адреса 0x1000 000020. Опять же, чтобы использовать ее, нам нужно определить эту область памяти внутри скрипта компоновщика следующим образом21:

/* организация памяти для STM32F334R8 */ MEMORY

{

FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 64K SRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 12K

CCM (xrw) : ORIGIN = 0x10000000, LENGTH = 4K

}

Очевидно, что атрибут LENGTH должен отражать размер CCM-памяти для конкретного микроконтроллера STM32. Как только область памяти определена, мы должны создать специальную секцию в скрипте компоновщика:

.ccm : ALIGN(4) { *(.ccm .ccm*)

} >CCM

Чтобы переместить определенную процедуру в CCM-память, мы можем использовать ключевое слово GCC __attribute__, как было показано ранее для секции .isr_vector:

void __attribute__((section(".ccm"))) routine() {

...

}

18Некоторые микроконтроллеры STM32 предоставляют две памяти SRAM, одна из которых обеспечивает доступ с 0-ожиданием. Всегда сверяйтесь с техническим описанием по вашему микроконтроллеру.

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

20STM32F7 предоставляет специальный интерфейс тесно связанной памяти (Tightly Coupled Memory, TCM) с двумя отдельными шинами, которые соединяют ядро Cortex-M7 с Flash-памятью и SRAM. ITCM-RAM команд – это область только для чтения размером 16 КБ, доступная только ядром и отображаемая с адреса 0x0000 0000. DTCM-RAM данных представляет собой область размером 64 КБ, отображенную на адрес 0x2000 0000 и доступную всем ведущим устройствам на шине AHB из шинной матрицы AHB, но через

специальную ведомую шину AHB ядра. Обратитесь к справочному руководству по STM32F7 для получения дополнительной информации.

21 Конфигурация памяти относится к Nucleo-F334, которая вместе с Nucleo-F303 предоставляет CCM-память.

Организация памяти

539

Если, наоборот, мы хотим хранить данные в CCM-памяти, то нам также нужно инициализировать их, как это было сделано для областей .bss и .data в обычной памяти SRAM. В этом случае нам нужен более полный скрипт компоновщика:

/* Используется кодом запуска (startup) для инициализации данных в CCM */ _siccm = LOADADDR(.ccm.data);

/* Секция инициализированных данных в CCM */

.ccm.data : ALIGN(4)

{

_sccmd = .;

*(.ccm.data .ccm.data*)

. = ALIGN(4); _eccmd = .;

} >CCM AT>FLASH

/* Секция неинициализированных данных в CCM */

.ccm.bss (NOLOAD) : ALIGN(4)

{

_sccmb = .; *(ccm.bss ccm.bss*)

. = ALIGN(4); _eccmb = .;

} >CCM

Здесь мы определяем две секции: .ccm.data, которая будет использоваться для хранения глобальных инициализированных данных в CCM, и .ccm.bss, используемая для хранения глобальных неинициализированных данных. Как и для обычной SRAM, потребуется

вызвать процедуры __initialize_data() и __initialize_bss() из процедуры _start():

...

__initialize_data(&_siccm, &_sccmd, &_eccmd);

__initialize_bss(&_sccmb, &_eccmb);

...

Затем, чтобы поместить данные в CCM, мы должны указать компилятору использовать ключевое слово attribute:

uint8_t initdata[] __attribute__((section(".ccm.data"))) = {0x1, 0x2, 0x3, 0x4}; uint8_t uninitdata __attribute__((section(".ccm.bss")));

20.3.1. Перемещение таблицы векторов в CCM-память

CCM-память также может использоваться для хранения процедур ISR, если переместить в нее всю таблицу векторов. Это может быть в особенности полезно для ISR, которые должны быть обработаны в кратчайшие сроки. Однако перемещение таблицы векторов требует дополнительных шагов, поскольку архитектура Cortex-M разработана таким образом, что таблица векторов начинается с адреса 0x0000 0004 (который соответствует адресу 0x0800 0004 внутренней Flash-памяти). Шаги, которые необходимо проделать, следующие:

Организация памяти

540

определить размещаемую в SRAM CCM таблицу векторов с помощью ключевого

слова __attribute__((section(".isr_vector_ccm"));

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

__attribute__((section(".ccm"));

определить размещаемую во Flash-памяти минимальную таблицу векторов, состоящую из указателя MSP и адреса обработчика исключения сброса Reset и начинающуюся с адреса 0x0800 0000;

переместить таблицу векторов в обработчике исключения сброса Reset копированием содержимого секции .ccm из Flash-памяти в SRAM.

Давайте начнем с определения размещаемой в SRAM CCM таблицы векторов. Определим файл с именем ccm_vector.c со следующим содержимым:

 

Имя файла: src/ccm_vector.c

 

 

 

 

1

 

#include <stm32f3xx_hal.h>

2

 

 

 

3

 

#define GPIOA_ODR

((uint32_t*)(GPIOA_BASE + 0x14))

4

 

 

 

5

 

extern const uint32_t _estack;

6

 

 

 

7

 

void SysTick_Handler(void);

8

 

 

 

9

 

uint32_t *ccm_vector_table[] __attribute__((section(".isr_vector_ccm"))) = {

10

 

(uint32_t *)&_estack,

// указатель начала стека

11

 

(uint32_t *) 0,

// Reset_Handler не перемещаемый

12(uint32_t *) 0,

13(uint32_t *) 0,

14(uint32_t *) 0,

15(uint32_t *) 0,

16(uint32_t *) 0,

17(uint32_t *) 0,

18(uint32_t *) 0,

19(uint32_t *) 0,

20(uint32_t *) 0,

21(uint32_t *) 0,

22(uint32_t *) 0,

23(uint32_t *) 0,

24(uint32_t *) 0,

25(uint32_t *) SysTick_Handler

26};

27

28void __attribute__((section(".ccm"))) SysTick_Handler(void) {

29*GPIOA_ODR = *GPIOA_ODR ? 0x0 : 0x20; // Вызывает мигание светодиода LD2

30}

Файл содержит всего лишь таблицу векторов, которая находится в секции

.isr_vector_ccm, и обработчик исключения SysTick, который находится в секции .ccm. Далее нам нужно перестроить скрипт компоновщика следующим образом:

Организация памяти

541

Имя файла: src/ldscript6.ld

75/* Используется кодом запуска (startup) для загрузки ISR в CCM из FLASH */

76_slccm = LOADADDR(.ccm);

77

78.ccm : ALIGN(4)

79{

80_sccm = .;

81*(.isr_vector_ccm)

82*(.ccm)

83KEEP(*(.isr_vector_ccm .ccm))

85. = ALIGN(4);

86_eccm = .;

87} >CCM AT>FLASH

89/* Размер секции .ccm */

90_ccmsize = _eccm - _sccm;

Скрипт компоновщика не содержит ничего отличного от того, что мы уже видели. Определяется секция .ccm, и мы даем указание компоновщику сначала поместить в нее содержимое секции .isr_vector_ccm, а затем содержимое из секции .ccm, которая в нашем случае содержит всего лишь процедуру SysTick_Handler. Мы также даем указание компоновщику сохранить содержимое секции .ccm во Flash-памяти (используя директиву CCM AT>FLASH), в то время как адреса VMA секции .ccm привязываются к диапазону адресов CCM-памяти (то есть к начальному адресу 0x1000 0000).

Наконец, нам нужно вручную скопировать содержимое секции .ccm из Flash-памяти в CCM-память и переместить таблицу векторов. Эта работа снова выполняется в исключе-

нии Reset_Handler.

Имя файла: src/main-ex6.c

68/* Минимальная таблица векторов */

69uint32_t *vector_table[] __attribute__((section(".isr_vector"))) = {

70(uint32_t *)&_estack, // указатель начала стека

71(uint32_t *)_start // main в качестве Reset_Handler

72};

73

74void __attribute__ ((noreturn,weak))

75_start (void) {

76/* Копирование секции .ccm из FLASH-памяти (_slccm) в CCM-память */

77memcpy(&_sccm, &_slccm, (size_t)&_ccmsize);

78

79 __DMB(); // Это гарантирует, что запись в память завершена

80

81SCB->VTOR = (uint32_t)&_sccm; /* Перемещение таблицы векторов в 0x1000 0000 */

82SYSCFG->RCR = 0xF; /* Включение защиты от записи для CCM-памяти */

83

84 __DSB(); // Это гарантирует, что следующие команды используют новую конфигурацию

85

86 __initialize_data(&_sidata, &_sdata, &_edata);

Организация памяти

542

87__initialize_bss(&_sbss, &_ebss);

88main();

89

90for(;;);

91}

92

93int main() {

94/* Разрешение подачи тактирования на периферийное устройство GPIOA */

95*RCC_APB1ENR |= 0x1 << 17;

96*GPIOA_MODER |= 0x400; // Установка MODER[11:10] = 0x1

97

98SysTick_Config(4000000); // Истекает каждые 0,5 с

99}

100

101void delay(uint32_t count) {

102while(count--);

103}

Строки [69:72] определяют минимальную таблицу векторов, используемую при сбросе ЦПУ. Она состоит только из указателя MSP и адреса исключения Reset_Handler, предоставленного процедурой _start(). Когда микроконтроллер сбрасывается, в строке 77 мы копируем содержимое секции .ccm из Flash-памяти (базовый адрес хранится в переменной _slccm) в CCM-память, а затем перемещаем всю таблицу векторов, назначая позицию массива ccm_vector_table в CCM-памяти регистру VTOR в блоке управления системой (System Control Block, SCB) – строка 81. Далее мы включаем защиту от записи во всей CCM-памяти, чтобы избежать нежелательных операций записи, которые могут повредить код.

CCM-память поделена на страницы размером 1 КБ. Каждый бит в регистре

RCR контроллера системной конфигурации (System Configuration Controller, SYSCFG) используется для установки защиты от записи индивидуально каждой странице (бит 1 устанавливает защиту для первой страницы, бит 2 устанавливает защиту для второй страницы и т. д.). Здесь мы защищаем от записи всю CCM-память микроконтроллера STM32F334, которая имеет CCM-память, состоящую из четырех страниц размером 1 КБ.

Важно отметить, что, если мы отключим запись для всей CCM-памяти, мы не сможем поместить в нее глобальные или статически выделенные переменные, иначе произойдет отказ. С другой стороны, помещение как кода, так и данных в CCM-память приводит к тому, что мы теряем преимущества, которые дает CCM-память, из-за одновременного доступа к одной и той же памяти как по шине D-Bus, так и по шине I-Bus (рассмотрев рисунок 5 вы можете убедиться, что CCM-память подключена лишь к одному ведущему порту шинной матрицы – порту M3; таким образом доступ из D-Bus и I-Bus организуется шинной матрицей).

Перемещение таблицы векторов не ограничивается CCM-памятью. Как мы увидим в Главе 22, этот метод также используется, когда микроконтроллер загружается из других источников, отличных от внутренней Flash-памяти. В

Организация памяти

543

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

Перемещение таблицы векторов – возможность, недоступная в микроконтроллерах Cortex-M0, но доступная в Cortex-M0+. Как мы увидим в Главе 22, существует процедура, которая пытается справиться с этим ограничением.

20.4.Как использовать модуль MPU в микроконтроллерах STM32 на базе

Cortex-M0+/3/4/7

Помимо ядра Cortex-M0, все микроконтроллеры на базе Cortex-M могут дополнительно предоставлять модуль защиты памяти (Memory Protection Unit, MPU). И хорошая новость заключается в том, что все микроконтроллеры STM32 на базе этих ядер предоставляют его. MPU не следует путать с устройством управления памятью (Memory Management Unit, MMU) – продвинутым аппаратным компонентом, доступным в более производительных микропроцессорах, таких как Cortex-A, который в основном предназначен для преобразования адресов виртуальной памяти в физические.

MPU используется для защиты до восьми областей памяти, пронумерованных от 0 до 7. Они, в свою очередь, могут иметь восемь подобластей, если основная область составляет не менее 256 Байт. Подобласти имеют одинаковый размер и могут быть включены или отключены в соответствии с номером подобласти. MPU используется для того, чтобы сделать встроенную систему более надежной и безопасной, а в некоторых областях применения его использование является обязательным (например, в автомобильной и аэрокосмической промышленности). MPU может использоваться для:

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

Определения области памяти SRAM как неисполняемой для предотвращения атаки с внедрением кода.

Изменения атрибутов доступа к памяти.

Если ядро ЦПУ нарушает определения доступа к определенной области памяти (например, пытается выполнить код из неисполняемой области), возникает исключение тяжелого отказа HardFault (или более конкретный отказ памяти Memory Fault, как мы увидим в Главе 24).

Области MPU могут покрывать все адресное пространство 4 ГБ, при этом они также могут перекрываться между собой. Характеристики области определяются двумя параметрами: типом области и ее атрибутами. Существует три типа памяти:

Нормальная память: позволяет загрузку и сохранение байтов, полуслов и слов22 для их эффективной организации процессором (компилятор не знает о типах

22 Помните, что ядра Cortex-M0/0+ могут выполнять только выравнивание по словам.

Организация памяти

544

областей памяти). Для области нормальной памяти загрузка/сохранение выполняется ЦПУ не обязательно в порядке, указанном в программе. Памяти SRAM и FLASH – два примера нормальной памяти.

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

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

 

Таблица 2: Атрибуты области памяти

Атрибут области

Описание

 

 

XN

Никогда не исполнять

AP

Право доступа (см. таблицу 3)

TEX

Поле расширения типов (недоступно в Cortex-M0+)

S

Разделяемая

C

Кэшируемая

B

Буферизируемая

SRD

Включение/отключение подобласти

SIZE

Размер области памяти

Каждая область памяти имеет восемь атрибутов, представленных в таблице 2:

Никогда не исполнять (Execute never, XN): область памяти, отмеченная данным атрибутом, не позволяет исполнять программный код.

Право доступа (Access Permission, AP): определяет права доступа к области памяти. Права устанавливаются как для привилегированного (например, ядра ОСРВ), так и для непривилегированного кода (например, отдельного потока). В таблице 3 перечислены все возможные комбинации.

Поле расширения типов (Type Extension field, TEX), Кэшируемая (Cacheable, C) и Буферизируемая (Bufferable, B): эти поля используются для определения свойств кэширования области и, в некоторой степени, ее разделяемости (совместного использования). Они кодируются в соответствии с таблицей 4. Обратите внимание, что в ядрах Cortex-M0+ поле TEX всегда равно 0. Это потому, что ядра Cortex-M0+ поддерживают один уровень политики кэширования.

Разделяемая (Shareable, S): это поле конфигурирует область разделяемой памяти. Система памяти обеспечивает синхронизацию данных между ведущими устройствами шины в системе, например, процессором с контроллером DMA. Строго упорядоченная память всегда разделяемая. Если несколько ведущих устройств шины могут получить доступ к неразделяемой области памяти, программное обеспечение должно гарантировать согласованность данных между ведущими устройствами шины. Это поле не поддерживается в архитектуре ARMv6-M и поэтому всегда устанавливается в 0 в процессорах Cortex-M0+.

Включение/отключение подобласти (Subregion Enable/Disable, SRD): определяет,

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

Организация памяти

545

Размер области памяти (SIZE): задает размер области памяти. Размер не может быть произвольным, но он может принимать значение из хорошо известного пула размеров областей (зависит от конкретного семейства STM32).

Таблица 3: Права доступа к области

Привилегированный

Непривилегированный

Описание

 

 

 

Нет доступа

Нет доступа

Обращение к области приводит к отказу

 

 

разрешения доступа

RW (чтение/запись)

Нет доступа

Доступ только для привилегированного

 

 

программного обеспечения

RW (чтение/запись)

RO (только чтение)

Запись непривилегированным ПО вызовет

 

 

отказ разрешения доступа

RW (чтение/запись)

RW (чтение/запись)

Полный доступ к области

Непредсказуемо

Непредсказуемо

ЗАРЕЗЕРВИРОВАНО

RO (только чтение)

Нет доступа

Чтение только привилегированным ПО

RO (только чтение)

RO (только чтение)

Только чтение, привилегированным и не-

 

 

привилегированным ПО

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

Кэшируемая/некэшируемая: означает, что выделенная область может быть кэширована или нет.

Сквозная запись, без размещения записываемых данных (Write through with no write allocate): при совпадениях записывается в кэш и основную память, при несовпадениях обновляется блок в основной памяти без дублирования этого блока в кэш-памяти.

Обратная запись, без размещения записываемых данных (Write-back with no write allocate): при совпадениях записывается в кэш, устанавливая грязный бит (dirty bit) для блока, основная память не обновляется. При несовпадении обновляется блок в основной памяти без его дублирования в кэш-памяти.

Обратная запись, с размещением записываемых и считываемых данных

(Write-back with write and read allocate): при совпадениях записывается в кэш,

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

Таблица 4: Свойства кэширования и разделяемости областей

TEX

C

B

Тип памяти

Разделяемая

Описание

 

 

 

 

 

 

000

0

0

Строго упорядоченная

Да

Строго упорядоченная

000

0

1

Память устройства

Да

Разделяемая память устройства

000

1

0

Нормальная

Зависит от бита S

Сквозная запись, запись без выделения

000

1

1

Нормальная

Зависит от бита S

Отложенная запись, запись без выделения

001

0

0

Нормальная

Зависит от бита S

Не кэшируемая

001

0

1

Зарезервировано

Зарезервировано

Зарезервировано

001

1

0

Не определено

Не определено

Не определено

001

1

1

Нормальная

Зависит от бита S

Отложенная запись, запись и чтение

 

 

 

 

 

с выделением

010

0

0

Память устройства

Нет

Неразделяемая память устройства

010

0

1

Зарезервировано

Зарезервировано

Зарезервировано