Инструменты пользователя

Инструменты сайта


prog:spec:elpower_dualcore

Запускаем микроконтроллер для управления двигателями, "Электросила", в режиме DUALCORE | IDE Keil

Микроконтроллер для управления двигателями выполнен на базе двух ядер ARM Cortex-M4F. По сбросу два ядра МК работают в режиме LOCKSTEP. При этом первое ядро выполняет инструкции программы, а второе ядро только дублирует все операции первого ядра с задержкой в два такта. Данный режим применяется для обнаружения сбоев в работе МК путём сравнения поведения первого и второго ядра.
При необходимости режим LOCKSTEP может быть отключен, и тогда два ядра становятся независимыми (режим DUALCORE) и могут исполнять различные программы, что увеличивает общую производительность МК.

В данной статье мы рассмотрим пример перехода МК из режима LOCKSTEP в режим DUALCORE (для среды Keil), а также разберём некоторые особенности двухъядерной отладки.

Проект для работы МК в двухъядерном режиме

На примере проекта SMP (Symmetric Multi-Processing) мы разберём процесс запуска МК в режиме DUALCORE, а также посмотрим, как осуществляется отладка сразу двух ядер. Проект для Keil можно скачать по этой ссылке.

В статье я опирался на презентацию Миландра с выставки «ЭкспоЭлектроника» под номером 02, в которой приведены подробные настройки сред Keil и IAR для работы с МК в режиме DUALCORE.

Для одновременной отладки сразу двух ядер в среде Keil подходят следующие отладчики:

  • ULINK2,
  • ULINKPro,
  • CMSIS-DAP совместимые.

У меня в наличии имеется отладчик ULINK2, его и будем использовать.

Настройка среды Keil для двухъядерной отладки

Запускаем проект SMP и смотрим как настроена среда Keil. Подробное описание настроек для загрузки программы во Flash и ОЗУ память приведено в статье Создаем проект “Hello, World” на микроконтроллер для управления электроприводами “Электросила”, поэтому остановимся только на настройках, характерных для двухъядерной отладки.

Переходим в настройки отладчика Options for Target→Debug→Settings. Здесь в поле "AP" осуществляется выбор ядра, к которому будет подключаться отладчик.

Так как после сброса МК работает в режиме LOCKSTEP, то отладчик имеет доступ только к ядру 0, поэтому в "AP" выбираем 0.

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

Но пока не будем запускать отладку, а разберём процесс перехода из режима LOCKSTEP в режим DUALCORE.

Процесс перехода из режим LOCKSTEP в режим DUALCORE

Согласно презентации Миландра, переход в режим DUALCORE и обратно должен быть следующим.

Рекомендуемая процедура переключения из режима LOCKSTEP в режим DUALCORE:

Процедура выполняется при выключенных DMA, кэш и прерываниях.
1. Разблокировать запись в регистры батарейного домена, записав ключ в регистр BKP_KEY.
2. Установить бит LOCKSTEP_EN регистра REG_60_SYS.
3. Очистить конвейер инструкцией DSB.
4. В зависимости от выполнения условия CPUID==0 передать управление на разные
   участки кода, загрузить разные значения в указатели стека.

Рекомендуемая процедура переключения из режима DUALCORE в режим LOCKSTEP:

1. Разблокировать запись в регистры батарейного домена, записав ключ в регистр BKP_KEY.
2. Сбросить бит LOCKSTEP_EN регистра REG_60_SYS.
3. Очистить конвейер инструкцией DSB.
4. Выполнить сброс процессора, установив бит SYSRESETREQ регистра AIRCR.

В проекте SMP переход в режим DUALCORE осуществляется в файле startup.s, обработчик Reset_Handler().

LDR     R0, =BKP_BASE_KEY ; Записываем ключ в регистр KEY блока BKP
LDR     R1, =BKP_KEY
STR     R1, [R0]    

LDR     R0, =BKP_RG60_0 ; Считываем текущее значение регистра REG60 и устанавливаем бит 9 LOCKSTEP
LDR     R1,[R0]
AND     R1,#~0x1600             
ORR     R1,R1,#0x0200 ; DUALCORE
                
DMB.W 
DSB.W 
ISB.W 
STR     R1,[R0,#0x00]  ; Записываем полученное значение в регистр REG60_SYS
DMB.W                  ; Очищаем конвейер инструкций
DSB.W 
ISB.W

После исполнения данного кода произойдёт переход в режим DUALCORE, при этом ядро 0 и ядро 1 продолжат выполнять программу уже по отдельности.

Чтобы два ядра могли совместно исполнять программу, нам необходимо указать для каждого из них отдельный стек и кучу. В проекте это сделано в файле startup.s, функция __user_setup_stackheap, которая вызывается библиотекой C во время запуска программы, т.е. после перехода в __main. Подробнее про это описано на официальном сайте Keil.

__user_setup_stackheap
  LDR     R0, =SMP_REG
  LDR     R0,[R0]
  TST     R0,#1         ; Если значение по адресу SMP_REG == 1, тогда перейти в функцию setup_core1
  BNE     setup_core1
setup_core0                
  LDR     R0, =(Heap_Mem0             )
  LDR     R2, =(Heap_Mem0 + Heap_Size )
  LDR     sp, =(Stack_Mem             )
  BX      LR
                
setup_core1
  LDR     R0, =(Heap_Mem1             )
  LDR     R2, =(Heap_Mem1 + Heap_Size )
  LDR     sp, =(Stack_Mem1            )
                
  BX      LR

Здесь и далее "SMP_REG" обозначает регистр идентификации ядра, который содержит один бит: при чтении данного регистра первое ядро получает значение 0, а второе ядро - значение 1. С помощью данного регистра можно легко передать управление на разные участки кода.

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

Запускаем двухъядерную отладку

В проекте SMP показан пример взаимодействия двух ядер с использованием мьютексов. При этом ядро 0 мигает синим светодиодом (PC19), а ядро 1 - красным (PC21). Ничего сложного в работе программы нет, поэтому перейдём сразу к отладке.

В зависимости от номера версии платы могут отличаться выводы микроконтроллера, отвечающие за диоды указанных цветов. Например, для платы V2, это были выводы PC18 и PC20 соответственно. В таком случае, необходимо внести изменения в программный код. Пример работает без изменений для V3.

Программу будем запускать из памяти ОЗУ, поэтому в настройках Keil выбираем конфигурацию "RAM ULINK2 @0", которая запускает отладку 0-го ядра.

За настройку запуска режима отладки отвечает файл инициализации "0x02000000.ini", в котором выполняются две строки:

Setup(0x02000000);  // Установить значения SP (указатель стека), PC (счётчик команд), VT (начало таблицы векторов)
g ,main             // Запустить выполнение программы с адреса, указанного в PC, и остановится в main

Собираем проект и запускаем режим отладки. МК автоматически останавливается в функции main(), так как в настройках проекта, вкладка Debug, установлена опция "Run to main()". Слева внизу в окне "Command" отображается лог запуска отладки, в котором видно, что на момент запуска отладки режим DUALCORE был выключен (равен 0), а отлаживаемое ядро имеет номер 0.

Переход в режим DUALCORE и установка различных стеков производится в файле startup.s до входа в main(), поэтому на момент остановки в функции main() два ядра уже работают независимо.

Если после запуска отладки выполнение программы не было остановлено в начале функции main, то в этом случае необходимо выйти из отладки, перейти в опции проекта, вкладка Debug, и снять галочку "Run to main()", после чего можно снова запускать отладку.

Казалось бы, мы подключились к ядру 0 и остановили его работу, при этом ядро 1 должно продолжать исполнять программу в своём "ритме". Однако, здесь есть нюанс. Так как программа исполняется из ОЗУ, то отладчик может ставить программные точки останова, т.е. подменять инструкции, на которых мы хотели остановиться, на инструкции остановки типа "BKPT #0x00".

При установке опции "Run to main()" (или "g ,main" в файле инициализации для старых версий Keil) такая программная точка останова ставится в начале main(), а так как оба ядра проходят по этому участку кода, то и остановлены они будут тоже оба. Нам такая ситуация даже удобна, так как мы точно знаем, что оба ядра остановлены, и запустив отладку второго ядра, можно смотреть за ходом выполнения программы. Однако, если необходимо чтобы второе ядро не останавливалось, достаточно снять галочку с опции "Run to main()" (или "g ,main") и при запуске отладки МК будет останавливаться в функции Reset_Handler.

Сейчас два ядра остановлены, и мы можем в окне отладки запустить выполнение программы (F5), при этом будет запущено только ядро 0, а на демоплате замигает синий светодиод. Теперь запустим отладку второго ядра. Для этого переходим в папку проекта SMP и ещё раз запускаем пример.

В настройках Keil выбираем конфигурацию "RAM ULINK2 @1", которая настроена для подключения к 1 ядру. Здесь необходимо обратить внимание, что в настройках отладчика выставлены не совсем типичные настройки:

В поле Connect я установил параметр "without Stop", чтобы при подключении мы не сбросили ядро 1, а подключились к нему без остановки, хотя в нашем случае оно уже остановлено в main(). Также в файле инициализации изменена опция LOAD, чтобы мы не записывали заново программу в память ОЗУ, а загрузили только отладочную информацию. Это необходимо для Keil, иначе отладка будет только в дизассемблере.

load %L incremental NOCODE // Загружает только отладочную информацию

Собираем проект, запускаем отладку и видим, что ядро остановлено в начале функции main():

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

Особенности двухъядерной отладки

1. Как уже было отмечено ранее, если программа запускается из ОЗУ, то отладчик ULINK2 может ставить программные точки останова, которые, в случае попадания на общие участки кода, будут останавливать оба ядра.

2. После сброса МК не по питанию, настройки режима DUALCORE сохраняются, так как блок BKP не сбрасывается. Это приводит к тому, что, если программа будет загружена во FLASH память, а затем произойдёт Reset, оба ядра начнут исполнять загрузочную программу, в которой нет разделения стеков для двух ядер. Это обстоятельство может приводить к ошибкам при определении режима загрузки, и МК "свалится" в бесконечный цикл ожидания. Например, в режиме загрузки FLASH+JA при установленном бите LOCKSTEP МК не стартует. При этом для режима FLASH+JB данная ошибка не наблюдается.

3. При работе из FLASH-памяти необходимо учитывать ошибку Errata 1001, при которой возникают ошибки ECC, особенно часто в двухъядерном режиме работы.

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

prog/spec/elpower_dualcore.txt · Последние изменения: 2019/08/16 14:48 — vova