Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Линкер: Расположение переменной
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК > IAR
Кирилл__
Господа, подскажите: как принудительно заставить IAR for ARM расположить локальную переменную функции в RAM-памяти, а не в регистре?
Заранее спасибо.
Сергей Борщ
Специальных средств для делания быстрой программы медленной не предусмотрено. Попробуйте дать этой переменной квалификатор volatile. И отвечает за это компилятор, линкер тут не при чем.

P.S. Но нафига?
IgorKossak
Квалификатор static не спасёт?
scifi
Уточняющий вопрос: зачем???
Кирилл__
Сергей, Игорь, спасибо за ответ.

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

Квалификатор volatile справляется с задачей, неудобно только, что он вызывает при билде Warning: undefined behavior. Да и само решение показалось мне хитростью. В глубине души я понимал, что есть более официальное решение, поэтому пришел сюда за советом.

Квалификатор static хорош, но он резервирует память за пределами кучи, поэтому вынужден от него отказаться.
shmur
Цитата(Кирилл__ @ Apr 6 2012, 10:16) *
Зачем это нужно: порой, когда в моей функции много локальных переменных, и присутствуют вызовы других функций, компилятор забывает, что в регистре, скажем, Р0, у меня расположена переменная Х, и помещает туда другие данные. В результате при чтении переменной Х я получаю совсем не то значение, которое должен был.


Жесть wacko.gif
На компилятор грешить - последнее дело, скорее всего ошибка у тебя, можешь код показать?
scifi
Цитата(Кирилл__ @ Apr 6 2012, 10:16) *
Зачем это нужно: порой, когда в моей функции много локальных переменных, и присутствуют вызовы других функций, компилятор забывает, что в регистре, скажем, Р0, у меня расположена переменная Х, и помещает туда другие данные. В результате при чтении переменной Х я получаю совсем не то значение, которое должен был.

Поддерживаю предыдущего оратора: жесть. С вероятностью 99% это ошибка в Вашем коде. Всякие volatile и static могут помочь скрыть ошибку, но она запросто может вылезти в другом месте.
Я бы попросил показать код, но после упоминания большого числа локальных переменных мне кажется, что разбираться в этом коде будет непросто.
IgorKossak
Цитата(Кирилл__ @ Apr 6 2012, 09:16) *
... порой, когда в моей функции много локальных переменных, и присутствуют вызовы других функций, компилятор забывает, что в регистре, скажем, Р0, у меня расположена переменная Х, и помещает туда другие данные. В результате при чтении переменной Х я получаю совсем не то значение, которое должен был.

Уж больно это мне напоминает переполнение стека.
Marto
Хм.
Кирилл__
Ну что ж, направление я увидел.
Поищу утечки в памяти, и с ключевыми словами всё ясно.
Спасибо за Ваши ответы. biggrin.gif

П.С. Модераторы, тему можно закрыть.
Сергей Борщ
QUOTE (Кирилл__ @ Apr 11 2012, 09:18) *
П.С. Модераторы, тему можно закрыть.
Закрывать можно будет (вы можете сделать это сами в левом нижнем углу) когда вы найдете и огласите здесь причину. Чтобы кто-то другой, столкнувшись с аналогичной проблемой и воспользовавшись поиском узнал не только что он не одинок, но и одну из возможных причин и метод решения.
Кирилл__
Ошибка действительно была в коде.
Смутило, что дебагер показывает не всегда верное значение переменной, если она лежит в регистре. Поэтому решение такое:
Для отладки я выставляю локальным переменным функции volatile, что помещает их в оперативную память и теперь может быть верно прочитано дебагером. После отладки я удаляю volatile, и работоспособность функции не изменяется.
mdmitry
Цитата(Кирилл__ @ Jun 20 2012, 16:43) *
После отладки я удаляю volatile, и работоспособность функции не изменяется.

И при использовании оптимизирующего компилятора код получаете другой! Оптимизатор может много чего сделать. Итог: отладили одну программу, отдали заказчику другую. laughing.gif
Кирилл__
Цитата(mdmitry @ Jun 20 2012, 18:33) *
И при использовании оптимизирующего компилятора код получаете другой! Оптимизатор может много чего сделать. Итог: отладили одну программу, отдали заказчику другую. laughing.gif

Код-то другой, но отличается лишь позиционированием некоторых локальных переменных. Я верю, что компилятор правильно делает свое дело. wink.gif
scifi
Цитата(mdmitry @ Jun 20 2012, 18:33) *
И при использовании оптимизирующего компилятора код получаете другой! Оптимизатор может много чего сделать. Итог: отладили одну программу, отдали заказчику другую. laughing.gif

Справедливо, конечно: этот подход не выявляет все баги. Но он помогает выявить баги, не зависящие от уровня оптимизации, а это уже очень хорошо.
mdmitry
Цитата(Кирилл__ @ Jun 22 2012, 10:27) *
Код-то другой, но отличается лишь позиционированием некоторых локальных переменных. Я верю, что компилятор правильно делает свое дело. wink.gif

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

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

Конечно лучше чем ничего, но остануться неочевидные баги.
Sergey_Aleksandrovi4
Столкнулся с аналогичной проблемой: IAR 6.30.4, STM32F1xx платформа. На Medium уровне оптимизации переменная из R0
CODE

void RunUpdatePackParser(void)
{
bool isSoftwareError = false;
unsigned long CRC32;
/*static*/ unsigned int ResetAddr = 0;

// Явно привести передающий буфер к структуре принятого пакета
tRun_Update_Pack_Struct * pPack = (tRun_Update_Pack_Struct *)Wiz_RxBuf;

// Поле контрольной суммы
if (pPack->Mask[0] & (1<<7))
{
// Вычислить контрольную сумму все буферной области Flash-памяти
CRC32 = CalcCRC32((unsigned char*)LOADER_BUF_START, LOADER_BUF_SIZE);
if (pPack->CheckSum != CRC32)
isSoftwareError = true;

// Проверить байты [4..7] новой прошивки. Это адрес ResetVector +1(особенности Thumb). Должен попадать в ROM
/*unsigned int*/ ResetAddr = *(unsigned int*)(LOADER_BUF_START+4);
if ( (ResetAddr < ROM_START) || (ResetAddr > ROM_END) )
isSoftwareError = true;

if ( (ResetAddr & 0x00000003) != 1) //Адрес кратен 4, +1, т.е. 2 младших бита 0b0..01.
isSoftwareError = true;


if (isSoftwareError)
StatusReg |= RUN_UPDATE_SW_ERROR;
else
{
// Установить в Option Byte Data1 ключ "Переписать прошивку" (учесть мизерную вероятность аппаратной ошибки)
if (WriteOptionByte((unsigned int)&OB->Data0, RUN_UPDATE_KEY) != 0)
{
StatusReg |= RUN_UPDATE_HW_ERROR;
}
// Запустить механизм входа в загрузчик
else
{
StatusReg |= UPDATER_RUN;
DoSomething();
}
}

}
// Сформировать пакет-ответ со статусной информацией
TransmitStPack();
}



Программа ведёт себя некорректно. Пошагал отладчиком, строку "unsigned int ResetAddr = *(unsigned int*)(LOADER_BUF_START+4);" пропускает (привык я переменные объявлять по месту их первого использования). Переменная в отладчике значится как <unavailable>.
Унёс объявление ResetAddr в начало фунции, вручную проинициализировал 0. Результат - переменная помещается в R0. Отладчик всё-равно не шагает на строку "ResetAddr = *(unsigned int*)(LOADER_BUF_START+4);".
Пометил её как static. При этом отладка падает на строке "if ( (ResetAddr < ROM_START) || (ResetAddr > ROM_END) )", где макросы ROM_START и ROM_END - числа объявленные в одном из заголовочных файлов. Стек раздул с 1кБайта до 4. Вот сижу и голову ломаю: поставить более свежий IAR или всё-таки моя ошибка в коде.
Сергей Борщ
QUOTE (Sergey_Aleksandrovi4 @ Sep 28 2012, 11:15) *
Отладчик всё-равно не шагает на строку "ResetAddr = *(unsigned int*)(LOADER_BUF_START+4);".
Возможно потому что он встроил эту операцию в следующие команды? Надо смотреть ассемблерный код и шагать отладчиком по нему. Вполе возможно что он знает значение, которое должно грузиться в ResetAddr на этапе компиляции и в нужный момент загружает в регистр константу?. А если вы хотите, чтобы он таки считывал его несмотря ни на что - пришите ResetAddr = *(unsigned int volatile *)(LOADER_BUF_START+4), а еще лучше ResetAddr = *(unsigned int volatile const *)(LOADER_BUF_START+4);

Что значит "отладка падает на строке"?
Sergey_Aleksandrovi4
Под "отладка падает на строке" имел в виду то, находясь на строке Х и вызывая "Step Into" отладчик переходил не к следующей команде, а улетал неизвестно куда. Расставив брэкпойнты выяснил, что со строки "ResetAddr = *(unsigned int*)(LOADER_BUF_START+4)" отладчик перескакивал сразу к условному оператору "if (isSoftwareError)". Но, открыл окно дизасемблера, стал шагать по асм-командам. Все они исправно выполняются. Видимо есть нюансы при отладке при ненулевом уровне оптимизации.
"ResetAddr = *(unsigned int*)(LOADER_BUF_START+4);" выполняется правильно (LOADER_BUF_START находится за границами указанными линкеру), volatile добавлю для надёжности, спасибо за совет.
Глюк ушёл и не появляется даже если я теперь убираю квалификатор static при объявлении переменной ResetAddr. Не понятно из-за чего возникла ошибка, почему она пропала и, ГЛАВНОЕ, ожидать ли её появление вновь.
asm для STM32 не знаю, вот листинг на всякий случай.
CODE

ResetAddr = *(unsigned int*)(LOADER_BUF_START+4);
if ( (ResetAddr < ROM_START) || (ResetAddr > ROM_END) )
isSoftwareError = true;

if ( (ResetAddr & 0x00000003) != 1) //Адрес кратен 4, +1, т.е. 2 младших бита 0b0..01.
isSoftwareError = true;


if (isSoftwareError)
StatusReg |= RUN_UPDATE_SW_ERROR;

.......................
ResetAddr = *(unsigned int*)(LOADER_BUF_START+4);
??RunUpdatePackParser_2:
0x8000a14: 0x6870 LDR R0, [R6, #0x4]
if ( (ResetAddr < ROM_START) || (ResetAddr > ROM_END) )
0x8000a16: 0x4932 LDR.N R1, ??DataTable4_14 ; 0xf7ffe000 (-134225920)
0x8000a18: 0x1809 ADDS R1, R1, R0
0x8000a1a: 0xf5b1 0x3ff0 CMP.W R1, #122880 ; 0x1e000
0x8000a1e: 0xd300 BCC.N ??RunUpdatePackParser_3 ; 0x8000a22
isSoftwareError = true;
0x8000a20: 0x2501 MOVS R5, #1
if ( (ResetAddr & 0x00000003) != 1) //дрес кратен 4, +1, т.е. 2 младших бита 0b0..01.
??RunUpdatePackParser_3:
0x8000a22: 0xf000 0x0003 AND.W R0, R0, #3
0x8000a26: 0x2801 CMP R0, #1
0x8000a28: 0xd000 BEQ.N ??RunUpdatePackParser_4 ; 0x8000a2c
isSoftwareError = true;
0x8000a2a: 0x2501 MOVS R5, #1
if (isSoftwareError)
??RunUpdatePackParser_4:
0x8000a2c: 0x2d00 CMP R5, #0
0x8000a2e: 0xd004 BEQ.N ??RunUpdatePackParser_5 ; 0x8000a3a
.......................
scifi
Цитата(Sergey_Aleksandrovi4 @ Sep 28 2012, 13:23) *
Видимо есть нюансы при отладке при ненулевом уровне оптимизации.

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

Цитата(Sergey_Aleksandrovi4 @ Sep 28 2012, 13:23) *
Глюк ушёл и не появляется даже если я теперь убираю квалификатор static при объявлении переменной ResetAddr. Не понятно из-за чего возникла ошибка, почему она пропала и, ГЛАВНОЕ, ожидать ли её появление вновь.

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