Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Оптимизация в Keil
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Arlleex
Всем привет!
Использую Keil 5 для программирования STM32.
Заметил интересную особенность. Выключаю оптимизацию (Level 0), запускаю код, в нем все прозрачно и безобидно - выделяется в функции локальная структура, и я ее заполняю. Выхожу из функции в main, захожу в другую функцию и отладчик отваливается с сообщением

Путем проб и ошибок выявил, что, закомментировав одно из заполнений полей структуры,
Код
struct.a = 0;
struct.b = 100;
struct.c = 45;
// struct.d = 50;
struct.e = 150;

все работает. Т.е. поймал багу прямо на строчке заполнения структуры. Если перед объявлением структуры объявить еще что-нибудь, баг тоже исчезает... Подобного никогда не видел. При оптимизации Level 3 все работает, даже при заполнении всех полей структуры. Что там натворил оптимизатор такого?
P.S. И еще вопрос. Почему при оптимизации Level 0
Код
volatile const unsigned int a = 100;

разместится в SRAM, а если она объявлена но не используется по коду - выкидывается вовсе из map-файла (ведь Level 0 предполагает ничего не оптимизировать)? Если просто
Код
const unsigned int a = 100;

то все хорошо - константа будет во Flash.
Для Level 3 вроде как оба случая разместят константу во Flash.
Объясните, пожалуйста, или направьте на нужную информацию.
Fedor
Больше похоже на то что в камень заливается не то что собралось. Попробуйте делать полный ребилд проекта после смены уровня оптимизации.
Иназвание переменной struct это реальное название или для примера?
Arlleex
Пересобирал, чего-только не пересобирал.
Думал, флеш битая, но сделал сравнение файлов в ST-Link Utility, файлы одинаковые.
struct - это название для примера, конечно же =)

Еще один момент. Стоит вместо цифры 42 (в реальной железке) в этом поле структуры поставить число 128 - как код не зависает. Но это не выход из положения =)
diwil
вне зависимости от volatile или нет, если символ не используется и размещен в своей секции, то он не линкуется.
более того, я не совсем понимаю как воспринимать константу, которая _изменяется_ неизвестным компилеру способом...
уверен, что компилер не понимает это тоже.

а размещение это не компилер, это линкер...
aaarrr
Цитата(Arlleex @ Jul 21 2015, 21:24) *
отладчик отваливается

Цитата(Arlleex @ Jul 21 2015, 21:49) *
Еще один момент. Стоит вместо цифры 42 (в реальной железке) в этом поле структуры поставить число 128 - как код не зависает. Но это не выход из положения =)

Так программа зависает, или отладчик отваливается? Это две большие разницы.

Цитата(Arlleex @ Jul 21 2015, 21:24) *
P.S. И еще вопрос. Почему при оптимизации Level 0
Код
volatile const unsigned int a = 100;

разместится в SRAM, а если она объявлена но не используется по коду - выкидывается вовсе из map-файла (ведь Level 0 предполагает ничего не оптимизировать)?

Выкидывает линкер, а он не в курсе уровня оптимизации.

Цитата(diwil @ Jul 21 2015, 21:54) *
более того, я не совсем понимаю как воспринимать константу, которая _изменяется_ неизвестным компилеру способом...

Например, RO-регистр периферии можно так объявить.
Arlleex
aaarrr, пардон. Отладчик отваливается с сообщением, внешний вид которого показан на рисунке моего первого сообщения. Поэтому узнать причину я не могу (ладно, если бы там, MemManage Fault был...).
scifi
Цитата(diwil @ Jul 21 2015, 21:54) *
не совсем понимаю как воспринимать константу, которая _изменяется_ неизвестным компилеру способом...

Слово "константа" здесь может ввести в заблуждение. Точнее будет "переменная, которой нельзя присвоить значение в программе". Значение всё равно может измениться: например, регистр датчика температуры.

Цитата(Arlleex @ Jul 21 2015, 21:24) *
Путем проб и ошибок выявил, что, закомментировав одно из заполнений полей структуры,
...
все работает.

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

Код
int main(void)
{
func1(); // эта функция выполняется
func2(); // при выполнении вот этой отладчик отваливается
}

func1()
{
struct s;

s.a = 0;
s.b = 10;
s.c = 42;
s.d = 50;
init_struct(&struct); // ну, просто настраиваем регистры периферии микроконтроллера этой структурой
}

func2()
{
// запись и чтение внешней памяти
}


Вот так - отладчик отваливается при попытке выполнения функции func2.
Стоит закомментировать в func1 строку s.c = 42 или присвоить не 42 а 128 - отладчик не отваливается, функция выполняется. Пробовали 42, 128 и несколько других чисел - с некоторыми отваливается, с некоторыми - нет.

Менял размер стека и кучи (ну, в отчаянии), не помогает.

Само отваливание происходит при попытке записи во внешнюю память (при выполнении строчки что-то типа *(base_addr + addr) = data). Думаю, это не важно, ведь по сути абсурдно, что, изменив лишь инициализатор переменной, совсем не имеющей отношение к этой операции, результат будет разный - STLink отваливается, или нет.

Кроме всего прочего. Если оставить так, что отладчик отваливается, и просто поменять порядок вызова функций (сначала func2, а потом func1) - отладчик не отваливается, память доступна и все счастливы =)
Повторюсь. Это на уровне оптимизации Level 0.
P.S. Отваливание отладчика еще и сопровождается "ввисанием" программы. Т.е. фактически я мониторю каждую выполненную функцию и после нее зажигаю светодиод. Так вот, если отладчик отваливается - не зажигается и светодиод. Когда я не под отладчиком, а просто прошил микроконтроллер и запустил на выполнение, все то же самое - светодиоды молчат. И где контроллер завис - не ясно.
http://www.keil.com/forum/20367/
Ссылка по теме. Правда у них ULINK.
Fedor
Если структуру
struct s;
объявить глобальной или static отваливается?
Arlleex
Fedor, глобальной - не отваливается. Статической - не пробовал.
scifi
Цитата(Arlleex @ Jul 21 2015, 22:32) *
Само отваливание происходит при попытке записи во внешнюю память (при выполнении строчки что-то типа *(base_addr + addr) = data). Думаю, это не важно...

Ошибаетесь. Это очень важно. Если "отваливание" происходит на конкретной инструкции, то дело практически в шляпе. А вы пляшете с бубном и меняете строчки исходника - это абсурд.
Arlleex
К сожалению, я шагал не по ассемблеровским инструкциям, а по Си-шным. Завтра попробую выловить =) Постараюсь отписаться.
Golikov A.
у меня глупый, возможно, вопрос.
А у вас какой кортекс? Вы случаем в не выровненную память никуда/никак не лезите? Строчку кода убрали, код изменился, выравнивание съехало... нет?
kolobok0
Цитата(Arlleex @ Jul 21 2015, 21:24) *
...Использую Keil 5 для программирования STM32....


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

осадочек остался sm.gif

PS
Да, год этак 2005 где-то...
Arlleex
Golikov A., вопросы не бывают глупыми, вопросы бывают недостаточно освещены =)
Процессорное ядро Cortex M4, у них поддержка невыровненного доступа полностью железная.
И да. Все-таки, в ядрах, в которых вылетало исключение при доступе по невыровненному адресу, компилятор сам выделял всем переменным выровненные адреса =)
kolobok0, в смысле изменялся асм на выходе компилятора? Ты имеешь в виду машинный код? Или прошедший оптимизацию входной асм?
Golikov A.
А никаких модулей защиты памяти и прочей байды случаем не включено?

Еще может обращение к какой-то периферии которая еще не вышла на режим, добавляете строку дольше где-то висите обращаетесь позже, убираете строку, обращаетесь раньше?

kolobok0
Цитата(Arlleex @ Jul 22 2015, 21:56) *
...в смысле изменялся асм на выходе компилятора? Ты имеешь в виду машинный код? Или прошедший оптимизацию входной асм?


после отработки компилятора на выходе получаем файлы с коментнутыми текстовыми инструкциями и машинный код
вот там видно пропадание нескольких команд. причём параметры среды одни и те-жи. исходник менялся незначительно - ну типа нопов дополнительных
и уже эффект проявлялся. Никакой оптимизацией это не объяснить. Чистый глюкало компилятора.
Да, и ощущение что именно от больших размеров компилируемой программы зависит. Т.е. это один раз всего было явно поймано за руку.
Кода в то время писал разное(под 51) на мелких замечено не было. только в одном проекте такое проявилось. Сначала как баг, который я не мог
понять природу. Далее методом контрольных точек было найден логический блок, ну и при дальнейшем скурпулёзном просмотре кода выявило
конкретное место и отсутствие команд. после научного метода тыка повлиять на ситуацию - была найдена закономерность.

где то так. давно было дело...
Booger
Добрый день!
Не стал создавать новую тему.
Вопрос по уровням оптимизации в Keil 5.14.

Предыстория такая.
Скачал библиотеку USB с сайта st.com (STSW-STM32092 STM32F0x2xx USB FS device library (UM1717)).
Запустил проект виртуального ком порта.
Убрал из кода файлы отладочной платы, поменял камень на STM32F042K6. Убрал USART. Сделал так, чтобы все что принимается с USB выплёвывалось туда же.
Все работает отлично, определятся на компе, как вирт ком порт, шлю данные из терминала и получаю их же.
В проекте стоит уровень оптимизации 3 (Level 3 (-O3)) по умолчанию.
Если выставить уровень оптимизации 0 (Level 0 (-O0)), то при попытке отправить данные из консоли в контроллер, сваливается в hardfault на 8-й строчке.
При всех других уровнях оптимизации все работает (Level 1 (-O1), Level 2 (-O2)).

Код
void PMAToUserBufferCopy(uint8_t *pbUsrBuf, uint16_t wPMABufAddr, uint16_t wNBytes)
{
  uint32_t n = (wNBytes + 1) >> 1;
  uint32_t i;
  uint16_t *pdwVal;
  pdwVal = (uint16_t *)(wPMABufAddr + PMAAddr);
  for (i = n; i != 0; i--)
  {
    *(uint16_t*)pbUsrBuf++ = *pdwVal++; // Тут падает
    pbUsrBuf++;
  }
}


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

Может есть нормальное описание уровней оптимизации keil'а? Или мысли по этому поводу? Или может быть, кто-нибудь сможет объяснить "на пальцах" как работает оптимизация в keil?
scifi
Цитата(Booger @ Feb 5 2016, 15:44) *
Может есть нормальное описание уровней оптимизации keil'а?

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

Цитата(Booger @ Feb 5 2016, 15:44) *
Или мысли по этому поводу?

Мысли есть, конечно. Заняться старой доброй отладкой. Вытащить из регистров причину Hard Fault. Ну и разбираться дальше.
vlad_new
Я не раз нарывался на проблеммы с оптимизатором в кейле. много всего было всего не упомниш. вот один пример: есть массив глобальный волятивный. Делаем какие то действия с массивом. После этого разрешаем прерывание и ждём флага окончания изменения данных в массиве. Так вот 1 элемент массива с оптимизатором не менялся. Почему то кейл заганял его в регистр и после обновления массива в прерывании, 1 элемент брал из этого же регистра. это было в версии 4.2 В версии 4.5 уже это было исправлено. Так же попадались примерчики кейловские, например с USB, которые работали только без оптимизатора.
Так что смотрите ассемблер и поймёте что не так.
ViKo
Я считаю, что работоспособность программы при любых уровнях оптимизации и есть тот тест, который отличает хороший код от сами_знаете_какого. У меня тоже бывали случаи, когда не работало при -О3, потом ошибка находилась.
К вопросу в сообщении 18 - возможно, не хватает быстродействия при -О0.
zltigo
QUOTE (ViKo @ Feb 7 2016, 07:58) *
Я считаю, что работоспособность программы при любых уровнях оптимизации и есть тот тест, который отличает хороший код от сами_знаете_какого.

Именно так. Более того, абсолютно всегда следует с самого начала использовать максимальные уровни оптимизации И НИКОГДА НЕ ОТКЛЮЧАТЬ, единственно, что в процессе работы можно, при жесткой необходимости сдвигать оптимизацию по скорости больше к оптимизации по размеру. Да и то, делать это не глобально, а для отдельных обьектов компиляции - каждому свое.
Даже если при работе со сразу реальной оптимизацией наступить, вдруг (уже много много лет для свежих компиляторов не наступал) на непонимание Вами написанного компилятором, то это легче локализовать и скоректировать в процессе написания в момент проявления проблемы и не постфактум.
scifi
Цитата(vlad_new @ Feb 7 2016, 03:24) *
Я не раз нарывался на проблеммы с оптимизатором в кейле. много всего было всего не упомниш.

Не верю. Один-два раза за годы - может быть. Остальное - байки, просто не разобрался в ситуации.
ViKo
Цитата(zltigo @ Feb 7 2016, 09:42) *
Именно так. Более того, абсолютно всегда следует с самого начала использовать максимальные уровни оптимизации И НИКОГДА НЕ ОТКЛЮЧАТЬ, единственно, что в процессе работы можно, при жесткой необходимости сдвигать оптимизацию по скорости больше к оптимизации по размеру. Да и то, делать это не глобально, а для отдельных обьектов компиляции - каждому свое.
Даже если при работе со сразу реальной оптимизацией наступить, вдруг (уже много много лет для свежих компиляторов не наступал) на непонимание Вами написанного компилятором, то это легче локализовать и скоректировать в процессе написания в момент проявления проблемы и не постфактум.

Иногда включаю -O0, чтобы определить, правильно ли понял меня компилятор. rolleyes.gif При этом уровне гораздо легче разбираться по ассемблерным командам.
В Кейле задал 2 Target - Debug и Release - с уровнями -O0 и -O3, и когда не работает, легким движением мыши переключаюсь на Debug.
scifi
Цитата(ViKo @ Feb 7 2016, 11:38) *
Иногда включаю -O0, чтобы определить, правильно ли понял меня компилятор. rolleyes.gif При этом уровне гораздо легче разбираться по ассемблерным командам.

Начиная с версии 4.8 у gcc появился уровень оптимизации -Og. Код получается всего где-то на 15% больше, чем -Os, при этом в дизассемблере всё понятно. Красота!
zltigo
QUOTE (ViKo @ Feb 7 2016, 10:38) *
Иногда включаю -O0, чтобы определить, правильно ли понял меня компилятор. rolleyes.gif
При этом уровне гораздо легче разбираться по ассемблерным командам.

Понял/не понял, это по результату смотрится. И если не понял, то прежде всего надо смотреть Вы сами-то себя поняли, когда писали? Чего уж тут в ассемблерные команды лезть - сишный текст по любому читается легче. Нежели Вы поняли, что хотели и написали, то компиляторы современные тоже поймут.
QUOTE
Debug и Release - с уровнями -O0 и -O3

Кроме этих "готовых рецептов" есть еще масса ключиков.
RabidRabbit
На выравнивание pdwVal смотрите. Возможно при O0 компилятор не старается сложить всё по выравненным адресам... Налетал так со встроенным memcpy на Cortex-M0+ (хотя у Вас вроде без плюса).
Booger
В том то и дело, товарищи!
Работает на всех уровнях оптимизации, кроме Leavel - 0 (O0).
Я бы даже не сувался на форум, если было бы наоборот.
При Leavel - 3 (O3) неудобно пользоваться "дебагером". Остальными уровнями (Leavel - 1 (O1) и Leavel - 2 (O2)) еще ни разу не пользовался.
С одной стороны, конечно же хорошо, что всё отлично работает на 3-м уровне оптимизации, как все мы и добиваемся в конечном итоге, но хотелось бы докопаться до истины.
Пока в голову ничего не пришло, как начать переписывать USB-шный пример, а может быть придется и всю библиотеку.

ViKo, RabidRabbit, спасибо, буду смотреть.
Сергей Борщ
Смею предположить, что с включенной оптимизацией компилятор заменяет этот цикл на memcpy(), которая, в отличие от этого кода, корректно работает с невыровненными данными. При отключенной оптимизации, в зависимости от расположения pbUsrBuf, этот код может приводить к невыровненному доступу, который ядро M0 не поддерживает. За столько времени можно было эту маленькую функцию пройти в окне дизассемблера на любом уровне оптимизации, найти вызывающую падение ассемблерную команду и понять, откуда у нее берутся неправильные аргументы.
Booger
Спасибо всем за помощь!

Действительно, если использовать memcpy, то работает и на 0-м уровне оптимизации.
GetSmart
В 4-ом Кейле (точнее не скажу) для ARM7 таргета видел баг с memcpy если адрес записи больше адреса чтения когда области пересекаются.

Цитата(zltigo)
Именно так. Более того, абсолютно всегда следует с самого начала использовать максимальные уровни оптимизации И НИКОГДА НЕ ОТКЛЮЧАТЬ

По соседству аналогично жаловались, что устройство часто не проходит самодиагностику. Выход из ситуации тоже похож - самодиагностика есть зловред. Советы от проффесионалов.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.