Русский
Русский
English
Статистика
Реклама

Перевод Медленный CrossWorks for ARM?



На моей текущей работе мы используем CrossWorks for ARM IDE со встроенным GCC в качестве среды разработки приложений для встраиваемых систем. До недавнего времени никто не замечал проблем с этим, пока мы не начали работать над проектом у которого требования к выходу системы из спящего режима оказались выше обычного.


Упомянутая система работала под управлением процессора STM32L4 (ядро ARM Cortex-M4) и имела в качестве одного из источников пробуждения пьезо-кнопку. Кнопка подключена к линии MCU и пользовательское нажатие на неё генерирует прерывание, от которого происходит пробуждение. Необходимость в ускорении времени пробуждения системы возникла по нескольким причинам:


  • наша пьезо-кнопка притягивает сенсорную линию к уровню нуля на 100-200 мс в случае, когда пользователь делает естественное касание и не пытается её продавить. Если бы использовалась обычная механическая кнопка, то наверняка проблема, о которой я хочу рассказать, осталась бы незамеченной, так как те экземпляры, с которыми я работал, прижимали сенсорную линию на 500+ мс.
  • схема включения пьезо-кнопки не предусматривала какую-либо аппаратную защиту от антидребезга (причина сейчас уже не важна и к теме статьи не относится, поэтому эти детали опустим) и, как следствие, это привело к тому, что случались ложные пробуждения, которые нужно было отлавливать

О том, как мы обнаружили проблему и как с помощью небольших правок в реализации CRT (C Run-time) стандартной библиотеки CrossWorks for ARM добились ощутимого ускорения дальше.


Эта статья может быть особенна интересна тем, кто пользуется следующими средами разработки:


  • CrossWorks for ARM (очень вероятно, что все проекты для ARM Cortex-M ядер будут подвержены задержкам при старте)
  • Segger Embedded Studio (SES)

Когда приходит проблема


Наиболее точно проблему опишет осциллограмма:


Figure_with_issue


На ней можно увидеть два маркера:


  • Маркер A показывает примерное время генерации сигнала на пробуждение (если быть совсем точным, то этот сигнал будет сгенерирован, когда уровень напряжения на линии упадёт ниже $V_{IL} = 0.3 \cdot V_{DDIOx} \approx 1 \; V$)
  • Маркер B установлен на точку, когда прошла начальная инициализация MCU (память, GPIO, последовательные интерфейсы, АЦП, и т.д.) и логика основного приложения начинает выполняться

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


  • пользователь нажимает на кнопку
  • MCU пробуждается и начинается инициализация
  • на данном этапе мы хотим понять причину пробуждения и её корректность: запускается debounce-алгоритм для проверки события
  • debounce-алгоритм знает, что причиной пробуждения была кнопка, но она в данный момент уже не нажата Это дребезг, говорит алгоритм
  • система уходит в спящий режим
  • пользователь в недоумении

Это не совсем то, что ожидает пользователь.


Заметка


Эту проблему можно было бы игнорировать и просто предположить, что пользователи приспособятся и будут жать на кнопку дольше. Тактильность используемой пьезо-кнопки не совсем такая, какая вкладывается в это определение она не нажимается, как обычная механическая кнопка, и регулировать силу/длительность нажатия не совсем тривиальная задача. Тем более нельзя ожидать, что всем пользователям это придётся по вкусу.

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

Для локализации и устранения проблемы в первом приближении потребовалось несколько шагов.


Шаг 1. Найти периферию, которая долго инициализируется


Этот шаг был не очень эффективным:


  • инициализация GPIO, АЦП, последовательных интерфейсы, криптографические блоки и т.д. не требовали длительного времени на инициализацию
  • инициализация USB-стека от ST отнимала ~20 мс, но оптимизация вызова не позволяла существенно сократить время старта получить 280 мс вместо 300 приятно, но недостаточно

Шаг 2. Найти код, который так сильно влияет на время старта


После предыдущего шага стало очевидно, что причина столь длительного пробуждения находится за пределами того, что мы писали, так как инициализация периферии STM32L4 стояла в самом начале функции main(). Анализ файла startup.s из стандартной поставки CrossWorks for ARM ничего необычного не показал, кроме того, что по сравнению с бесплатным GNU GCC for ARM в коде пристутствуют вызовы к функциям memory_copy и memory_set, которые реализованы в CRT от CrossWorks (справедливо для версий 4.4.0 и 4.5.0):


Функция копирования памяти


memory_copy:    cmp r0, r1    beq 2f    subs r2, r2, r1    beq 2f1:    ldrb r3, [r0] ;ПОДОЗРИТЕЛЬНАЯ ЧАСТЬ    adds r0, r0, #1    strb r3, [r1]    adds r1, r1, #1    subs r2, r2, #1    bne 1b2:    bx lr

Функция установки значения памяти


memory_set:    cmp r0, r1    beq 1f    strb r2, [r0] ;ПОДОЗРИТЕЛЬНАЯ ЧАСТЬ    adds r0, r0, #1    b memory_set1:    bx lr

Подозрительные части отмечены и в них видно, что копирование и установка значения памяти реализована с помощью побайтной операции установки. Соответственно, количество необходимых итераций возрастает в 4 раза, так как инструкции STRB (установка 1 байта) и STR (установка машинного слова для ARM32 это 4 байта) имеют одинаковое время исполнения 1 такт. _Это странно потому, что данные должны быть выровнены по границе машинного слова (исключения могут быть, но без использования явных атрибутов/опций компилятора, такого не происходит)_.


Если компилятор CrossWorks for ARM или их стандартная библиотека отходит от этого правила, то, на мой взгляд, это должно быть явно указано, почему memory_copy и memory_set реализованы именно так. И уж точно это не то, за что вы захотите платить.


Что касается нашего приложения, реализация CRT выше повлияла на систему и мы это заметили по той причине, что объем данных в RAM на момент нахождения проблемы уже был достаточно велик ~265 Кбайт (сторонние библиотеки в виде TCP/IP-стека, TLS/SSL-библиотека, RTOS + статические буферы в нашем коде сделали своё дело):


  • секция .data занимала ~3 Кбайт
  • секция .bss занимала ~262 Кбайт

Как известно, секция .bss обнуляется в процессе инициализации приложения, а данные из секции .data во Flash-памяти MCU копируются в RAM.


Немного расчётов для MCU STM32L4A6VG


Просто чтобы показать разницу в скорости реализации функций копирования/установки памяти по словам и по байту я покажу расчёт для MCU, используемого в системе. STM32L4A6VG на старте работает от встроенных часов MSI (multispeed internal RC oscillator), которые по умолчанию настроены на частоту 4 МГц. Это значит, что время выполнения одной инструкции до переключения на более высокую частоту составляет:
$ t_{instruction} = \frac{1}{f_{CPU}} = \frac{1}{4 \cdot 10^6} = 250 \cdot 10^{-9} \; s = 250 \; ns $
Для простоты вычислений предположим, что мы используем только функцию memory_set, содержащую 5 инструкций, которые исполняются за 1 такт процессора (beq, cmp, strb, adds, b). При записи 1 байта за итерацию общее число тактов, необходимых для очистки 265 Кбайт = 271360 байт составит:
$ t_{set} = C_{MEM} \cdot N = 271360 \cdot 5 = 1356800 \; cycles$
Итого, общее время выполнения:
$ T_{set} = t_{instruction} \cdot t_{set} = 250 \cdot 10^{-9} \; s \cdot 1356800 \; cycles = 0.3392 \; s \approx 0.34 \; s = 340 \; ms $

Это время очень близко к тому, что мы увидели на осциллограмме в самом начале

Ожидаемое время исполнения аналогичных действий при использовании инструкции установки значения, оперирующей машинными словами:
$ t_{set} = C_{MEM} \cdot N = \frac{271360}{4} \cdot 5 = 339200 \; cycles \rightarrow \\ \rightarrow T_{set} = t_{instruction} \cdot t_{set} = 250 \cdot 10^{-9} \; s \cdot 339200 \; cycles = 0.0848 \; s \approx 0.085 \; s = 85 \; ms$

Простое обновление реализации, которое ускоряет время копирования и очистки памяти:


Функция копирования памяти


memory_copy:    cmp r1, r2    itte lt    ldrlt r3, [r0], #4    strlt r3, [r1], #4    bge 1f    b memory_copy1:    bx lr

Функция установки значения памяти


memory_set:    cmp r0, r1    ite lt    strlt r2, [r0], #4    bge 1f    b memory_set1:    bx lr

Как и ожидалось, ускорение времени старта составило ~4 раз, что также соответствует расчётам:


Figure_optimized_boot_time


Заключение


Очень печально, что в платном продукте нашлась не совсем оптимальная реализация startup-кода. Повторюсь, описанная выше реализация встречалась для всех ARM Cortex-M процессоров в CrossWorks for ARM v4.4 и v4.5.
Вероятнее всего пользователи Segger Embedded Studio также могут столкнуться с указанной проблемой, так как реализации CRT и стандартной библиотеки идентичны.


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

Источник: habr.com
К списку статей
Опубликовано: 12.11.2020 16:17:09
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

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

Разработка для интернета вещей

Stm32

Arm

Crossworks for arm

Это нормально?

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru