реклама на сайте
подробности

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> C++, мучения в освоении и JTAG отладке
Aprox
сообщение Mar 14 2011, 18:42
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 374
Регистрация: 7-11-07
Из: Moscow
Пользователь №: 32 131



Расскажу один назидательный случай, который случился со мной в освоении и отладке программ на С++. После того, как разобрался с помощью гуру, как разместить константы во Flash, а нев SRAM, со мной случилась другая проблема, связанная с загрузкой и JTAJ отладкой. Когда программа на С++ блистала своей красотой и лаконичностью, когда все прекрасно показывал симулятор IAR, настала пора грузить ее в кристалл. И вот тут-то началось! Отладчик JTAG упорно не хотел добегать до метки main, программа ускакивала в ловушку на векторе UndefinedHandler. Опускаю процесс поисков, сразу к сути. Оказывается, нельзя ни в коем случае в глобальных объектах заниматься инициализацией периферии кристалла. Вообще касаться периферии нельзя! Резонов два: -1. порядок вызовов конструкторов не определен, и -2 при JTAG отладке ARM после загрузки FLASH успевает промолотить еще достаточное количество инструкций прежде, чем сработает break по RESET и сдернет программу обратно в address_reset. Последнее меня и погубило. Дело в том, что я записал в конструктор куски инициализации Flash, а отладчик сдергивал кристалл как раз в момент этой инициализации. Возвращался по Reset он уже в испорченное содержимое сегмента-векторов. Победить удалось следующим приемом- из конструкторов глобальных объектов убрал любое обращение к периферии кристалла. Вместо этого, ввел в классы с периферией методы инициализации периферии типа obj.Start() с вызовом этих методов после входа в main(). Мне кажется, пережитые мучения пойдут на пользу всем, кто хочет переползти с С на С++.
Go to the top of the page
 
+Quote Post
sergeeff
сообщение Mar 14 2011, 19:11
Сообщение #2


Профессионал
*****

Группа: Свой
Сообщений: 1 481
Регистрация: 10-04-05
Пользователь №: 4 007



Цитата(Aprox @ Mar 14 2011, 22:42) *
Расскажу один назидательный случай


Если вы внимательно почитаете материалы форума, то про найденные вами "грабли" давно и внятно все было изложено.

Вам два совета:
- хороший тон - инициализация железа до входа в main();
- если уж хочется железо инициализировать в классах, никто не мешает объявить глобальные указатели на объекты, а в какой-либо low_level_init написать, что-нибудь типа:

Usart const *pUsart1;
....

void low_level_init(void)
{
pUsart1 = new Usart(1, 19200, 8, 1, 0); // пример чисто условный.
...
}

Есть еще варианты с singleton'ами, гарантирующими однократный вызов конструктора объекта.
Go to the top of the page
 
+Quote Post
Aprox
сообщение Mar 15 2011, 08:26
Сообщение #3


Местный
***

Группа: Участник
Сообщений: 374
Регистрация: 7-11-07
Из: Moscow
Пользователь №: 32 131



Цитата(sergeeff @ Mar 14 2011, 22:11) *
Вам два совета:
- хороший тон - инициализация железа до входа в main();
Почему? Я вспоминаю примеры профессиональных устройств c UCOS и С++, где задачи и железо при них создаются уже после входа в main(). Например, Ethernet модули фирмы NetBurner.
Цитата
- если уж хочется железо инициализировать в классах, никто не мешает объявить глобальные указатели на объекты, а в какой-либо low_level_init написать, что-нибудь типа:

Usart const *pUsart1;
....

void low_level_init(void)
{
pUsart1 = new Usart(1, 19200, 8, 1, 0); // пример чисто условный.
...
}

Есть еще варианты с singleton'ами, гарантирующими однократный вызов конструктора объекта.
Я пробовал с low_level_init. Для JTAG отладки программ ARM-ов - не годится. Проблема в том, что для JTAG отладки периферии требуется создать глобальные переменные-указатели на SFR-регистры. Иначе отладчик просто не видит периферию. По крайней мере, у меня с отладкой на кристаллах STR91xx именно так. И если в low_level_init произвести инициализацию этих указателей, то следующая за этим инициализация сегментов просто их стирает. В результате, отладчик улетает неизвестно куда.

Я по-прежнему считаю, что самый простой и надежный прием написания программ на C++ для ARMов- это вообще не касаться периферии до входа в main().


Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Mar 15 2011, 10:04
Сообщение #4


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Вставьте в low_level_init задержку чтобы отладчик успел остановить проц во время исполнения этой задержки.
QUOTE (Aprox @ Mar 15 2011, 10:26) *
Я пробовал с low_level_init. Для JTAG отладки программ ARM-ов - не годится. Проблема в том, что для JTAG отладки периферии требуется создать глобальные переменные-указатели на SFR-регистры. Иначе отладчик просто не видит периферию. По крайней мере, у меня с отладкой на кристаллах STR91xx именно так. И если в low_level_init произвести инициализацию этих указателей, то следующая за этим инициализация сегментов просто их стирает. В результате, отладчик улетает неизвестно куда.
Да ну, ерунда какая-то. Если речь идет об IAR. С STR91x не работал, а 71х прекрасно отлаживался без всяких указателей. И с чего бы отладчику куда-то улетать из-за неправильных указателей. Ну хотите указатели - инициализируйте их в main(). Периферия-то при чем?

QUOTE
Я по-прежнему считаю, что самый простой и надежный прием написания программ на C++ для ARMов- это вообще не касаться периферии до входа в main().
Ага, особенно если до входа в main инициализируются глобальные переменные, которые находятся во внешней S(D)RAM или создаются глобальные объекты, которые используют S(D)RAM, а она еще не включена.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Andy Mozzhevilov
сообщение Mar 15 2011, 10:10
Сообщение #5


Знающий
****

Группа: Свой
Сообщений: 877
Регистрация: 26-01-05
Из: Екатеринбург
Пользователь №: 2 206



Цитата(Aprox @ Mar 15 2011, 11:26) *
Я пробовал с low_level_init. Для JTAG отладки программ ARM-ов - не годится. Проблема в том, что для JTAG отладки периферии требуется создать глобальные переменные-указатели на SFR-регистры.

Создать именно в программе? Зачем там лишние сущности? Подключаете ddf файл для вашего контроллера, и смотрите всю периферию.
Цитата
Иначе отладчик просто не видит периферию. По крайней мере, у меня с отладкой на кристаллах STR91xx именно так.

По моему вы придумали для себя проблему, а потом ее решили, причем далеко не лучшим способом.
Во всяком случае сейчас посмотрел установленный у себя IAR и тут:
C:\Program Files\IAR Systems\Embedded Workbench 6.0\arm\config\debugger\ST\
находятся ddf файлы в том числе для STR91xx
Причем это все автоматически подключается, если вы в качестве целевого контроллера выберите именно ваш тип контроллера, а не generiс типа ARM7 или ARM9

Цитата
И если в low_level_init произвести инициализацию этих указателей, то следующая за этим инициализация сегментов просто их стирает. В результате, отладчик улетает неизвестно куда.

При желании и эту проблему можно обойти, но в вашем случае она надумана.

Цитата
Я по-прежнему считаю, что самый простой и надежный прием написания программ на C++ для ARMов- это вообще не касаться периферии до входа в main().

Это только один из способов, причем не факт, что самый надежный. Вы лишаете себя возможности создавать глобальные объекты классов с использованием периферии, при этом вручную вынуждены в определенной последовательности вызывать функции дополнительной инициализации объектов из main или откуда то еще, то есть совершаете дополнительное действие, которое можно забыть сделать.


--------------------
Пасу котов...
Go to the top of the page
 
+Quote Post
Aprox
сообщение Mar 15 2011, 11:20
Сообщение #6


Местный
***

Группа: Участник
Сообщений: 374
Регистрация: 7-11-07
Из: Moscow
Пользователь №: 32 131



Цитата(Сергей Борщ @ Mar 15 2011, 13:04) *
Вставьте в low_level_init задержку чтобы отладчик успел остановить проц во время исполнения этой задержки.
А в боевом варианте задержку вручную потом убирать?
Цитата
Да ну, ерунда какая-то. Если речь идет об IAR. С STR91x не работал, а 71х прекрасно отлаживался без всяких указателей. И с чего бы отладчику куда-то улетать из-за неправильных указателей. Ну хотите указатели - инициализируйте их в main(). Периферия-то при чем?
Кристаллы STR91xx имеют уже готовую фирменную библиотеку драйверов периферийных устройств. Библиотека написана на C и требует запуска инициализации собственных внутренних переменных. Как понимаете, вызывать инициализацию такой библиотеки в low_level_init нельзя, потому что следующая за этим инициализация сегментов данных основной программы затрет внутренние переменные слинкованной библиотеки. Поведение программы становится непредсказуемым. Единственный выход- заниматься периферией только после входа в main().
Цитата
Ага, особенно если до входа в main инициализируются глобальные переменные, которые находятся во внешней S(D)RAM или создаются глобальные объекты, которые используют S(D)RAM, а она еще не включена.
Зачем нарываться на приключения? Создавайте объекты, взаимодействующие с периферией, во внутренней SRAM кристалла. Этой SRAM вполне достаточно.


Цитата(Andy Mozzhevilov @ Mar 15 2011, 13:10) *
Создать именно в программе? Зачем там лишние сущности? Подключаете ddf файл для вашего контроллера, и смотрите всю периферию.
По моему вы придумали для себя проблему, а потом ее решили, причем далеко не лучшим способом.
Во всяком случае сейчас посмотрел установленный у себя IAR и тут:
C:\Program Files\IAR Systems\Embedded Workbench 6.0\arm\config\debugger\ST\
находятся ddf файлы в том числе для STR91xx
Причем это все автоматически подключается, если вы в качестве целевого контроллера выберите именно ваш тип контроллера, а не generiс типа ARM7 или ARM9
Я работаю с IAR v4.30A. Там нет ddf заготовок для STR91xx. Использую установку Taget =ARM9663-E. Предложения типа перейти на новые версии IAR пока не рассматриваю, поскольку создана инфраструктура разработки, привязанная типом отладчика именно к указанной версии. Иными словами придется менять и отладчик, поскольку старый не понимает отладочной информации новых версий.
Цитата
Это только один из способов, причем не факт, что самый надежный. Вы лишаете себя возможности создавать глобальные объекты классов с использованием периферии, при этом вручную вынуждены в определенной последовательности вызывать функции дополнительной инициализации объектов из main или откуда то еще, то есть совершаете дополнительное действие, которое можно забыть сделать.
Я так не считаю. В моем варианте объекты могут использовать периферию. Надо только в этих объектах предусмотреть метод Старт_Периферии() и вызывать его сразу по входу в main(). Поскольку этот метод есть неотъемлемая часть класса, то забыть чего-либо в периферии вряд ли получиться. Кроме того, это пожалуй единственный прием работать с готовыми библиотеками драйверов периферии, написанными на C.
Go to the top of the page
 
+Quote Post
Andy Mozzhevilov
сообщение Mar 15 2011, 12:19
Сообщение #7


Знающий
****

Группа: Свой
Сообщений: 877
Регистрация: 26-01-05
Из: Екатеринбург
Пользователь №: 2 206



Цитата(Aprox @ Mar 15 2011, 14:20) *
Я работаю с IAR v4.30A.

Сочувствую.

Цитата
Там нет ddf заготовок для STR91xx.

Это текстовый файл, его можно сделать самостоятельно. А возможно он просто подойдет от более свежих версий.

Цитата
Предложения типа перейти на новые версии IAR пока не рассматриваю, поскольку создана инфраструктура разработки, привязанная типом отладчика именно к указанной версии. Иными словами придется менять и отладчик, поскольку старый не понимает отладочной информации новых версий.

Как то печально совсем у вас все. Что за такая уникальная инфраструктура, которая работает только с одной версией отладчика? Вы себя вяжете сами по рукам и ногам, работая на версии 4-летней давности.

Цитата(Aprox @ Mar 15 2011, 14:20) *
Библиотека написана на C и требует запуска инициализации собственных внутренних переменных. Как понимаете, вызывать инициализацию такой библиотеки в low_level_init нельзя, потому что следующая за этим инициализация сегментов данных основной программы затрет внутренние переменные слинкованной библиотеки.

Для этого есть такой способ для low_level_init():
1. Зайти в low_level_init
2. Вызвать из нее функцию статической инициализации iar
3. Вызвать функцию инициализации библиотек периферии
4. Выйти из low_level_init с кодом 0 - return 0;
Код возврата 0 означает, что startup должен пропустить статическую инициализацию (во всяком случае до версии IAR 5.50.5)
После этого вы в конструкторах глобальных объектах получаете доступ к уже проинициализированным библиотекам.



--------------------
Пасу котов...
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Mar 15 2011, 14:01
Сообщение #8


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



QUOTE (Aprox @ Mar 15 2011, 13:20) *
А в боевом варианте задержку вручную потом убирать?
обрамить в #ifdef NDEBUG.
QUOTE (Aprox @ Mar 15 2011, 13:20) *
Кристаллы STR91xx имеют уже готовую фирменную библиотеку драйверов периферийных устройств. Библиотека написана на C и требует запуска инициализации собственных внутренних переменных.
Сочувствую.
QUOTE (Aprox @ Mar 15 2011, 13:20) *
Создавайте объекты, взаимодействующие с периферией, во внутренней SRAM кристалла. Этой SRAM вполне достаточно.
"Отучаемся говорить за других". Вам достаточно. Если внешнюю память предусмотрели - она кому-то нужна.
QUOTE (Aprox @ Mar 15 2011, 13:20) *
Кроме того, это пожалуй единственный прием работать с готовыми библиотеками драйверов периферии, написанными на C.
Нет, не единственный. Можно переписать cstartup.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
Aprox
сообщение Mar 15 2011, 14:49
Сообщение #9


Местный
***

Группа: Участник
Сообщений: 374
Регистрация: 7-11-07
Из: Moscow
Пользователь №: 32 131



Цитата(Andy Mozzhevilov @ Mar 15 2011, 15:19) *
Как то печально совсем у вас все. Что за такая уникальная инфраструктура, которая работает только с одной версией отладчика? Вы себя вяжете сами по рукам и ногам, работая на версии 4-летней давности.
Да, я понимаю. Но заняться обновлением инфраструктуры пока совершенно нет времени.
Цитата
Для этого есть такой способ для low_level_init():
1. Зайти в low_level_init
2. Вызвать из нее функцию статической инициализации iar
3. Вызвать функцию инициализации библиотек периферии
4. Выйти из low_level_init с кодом 0 - return 0;
Код возврата 0 означает, что startup должен пропустить статическую инициализацию (во всяком случае до версии IAR 5.50.5)
После этого вы в конструкторах глобальных объектах получаете доступ к уже проинициализированным библиотекам.
Плохо понимаю, если отключить статическую инициализацию сегмента данных, то в самом приложении на СPP писать какую либо инициализацию глобальных переменных станет уже бесполезно? Как же тогда будут созданы глобальные объекты?

Вообще, мне кажется, что проблема упирается в случай, когда проект состоит из модулей частью на СPP и частью на С.


Цитата(Сергей Борщ @ Mar 15 2011, 17:01) *
Нет, не единственный. Можно переписать cstartup.

Хорошо, не единственный. Но зато самый простой и ненапряжный. Потому, что просто используешь куски программы на С, которая раньше стопроцентно работала, а не вязнешь в тонкостях low_level_init и прочих cstartup.
Go to the top of the page
 
+Quote Post
Andy Mozzhevilov
сообщение Mar 15 2011, 16:08
Сообщение #10


Знающий
****

Группа: Свой
Сообщений: 877
Регистрация: 26-01-05
Из: Екатеринбург
Пользователь №: 2 206



Цитата(Aprox @ Mar 15 2011, 17:49) *
Да, я понимаю. Но заняться обновлением инфраструктуры пока совершенно нет времени.
Плохо понимаю, если отключить статическую инициализацию сегмента данных, то в самом приложении на СPP писать какую либо инициализацию глобальных переменных станет уже бесполезно? Как же тогда будут созданы глобальные объекты?

Ее не надо отключать, ее вызов надо переместить. То есть вы ее можете вызвать из своей low_level_init() (п.2 моих пояснений). Потом вы вызываете инициализацию своей библиотеки, и поскольку вызов инициализации библиотеки происходит после статической инициализации, то все корректно. Это все равно что вызвать init библиотеки в первой строчке main() программы на Си. После выхода из low_level_init() с кодом возврата 0 стандартный startup iar пропустит вызов статической инициализации и вызовет сразу динамическую инициализацию глобальных объектов С++ (во всяком случае до 5.50.5, но ведь у вас вообще 4.30).
При этом вы в конструкторах этих объектов уже спокойно можете использовать вызов функций вашей библиотеки.

Цитата
Вообще, мне кажется, что проблема упирается в случай, когда проект состоит из модулей частью на СPP и частью на С.

Здесь нет никакой проблемы, модули С++ совершенно спокойно могут вызывать функции на Си. У меня в проектах на С++ используется куча сторонних библиотек на Си (для примера - lwip, fat_fs, ucos-ii, tlsf), и это не является проблемой.

Цитата
Хорошо, не единственный. Но зато самый простой и ненапряжный. Потому, что просто используешь куски программы на С, которая раньше стопроцентно работала, а не вязнешь в тонкостях low_level_init и прочих cstartup.

В тонкостях то нужно разобраться один раз, сделать и отладить - цена вопроса - один рабочий день.
А вводить в объектах функции startObj и вызывать их - нужно повседневно.
По мне так лучше - полдня потерять, потом за 5 минут долететь. Не люблю рутинную ручную работу, изо дня в день наворачивать лишние строки кода только лишь потому, что библиотека неинициализирована до вызова конструкторов глобальных объектов.


--------------------
Пасу котов...
Go to the top of the page
 
+Quote Post
sergeeff
сообщение Mar 15 2011, 22:01
Сообщение #11


Профессионал
*****

Группа: Свой
Сообщений: 1 481
Регистрация: 10-04-05
Пользователь №: 4 007



Цитата(Andy Mozzhevilov @ Mar 15 2011, 20:08) *
В тонкостях то нужно разобраться один раз...


Абсолютно согласен. Никто не мешает startup файл организовать так, как тебе удобно и нужно, а не ребятам из отдела сопровождения компилятора X для процессора Y.
Go to the top of the page
 
+Quote Post
Aprox
сообщение Mar 16 2011, 07:23
Сообщение #12


Местный
***

Группа: Участник
Сообщений: 374
Регистрация: 7-11-07
Из: Moscow
Пользователь №: 32 131



Цитата(Andy Mozzhevilov @ Mar 15 2011, 19:08) *
Ее не надо отключать, ее вызов надо переместить. То есть вы ее можете вызвать из своей low_level_init() (п.2 моих пояснений). Потом вы вызываете инициализацию своей библиотеки, и поскольку вызов инициализации библиотеки происходит после статической инициализации, то все корректно. Это все равно что вызвать init библиотеки в первой строчке main() программы на Си. После выхода из low_level_init() с кодом возврата 0 стандартный startup iar пропустит вызов статической инициализации и вызовет сразу динамическую инициализацию глобальных объектов С++ (во всяком случае до 5.50.5, но ведь у вас вообще 4.30).
При этом вы в конструкторах этих объектов уже спокойно можете использовать вызов функций вашей библиотеки.
Спасибо, я попробую.
Цитата
В тонкостях то нужно разобраться один раз, сделать и отладить - цена вопроса - один рабочий день.
А вводить в объектах функции startObj и вызывать их - нужно повседневно.
По мне так лучше - полдня потерять, потом за 5 минут долететь. Не люблю рутинную ручную работу, изо дня в день наворачивать лишние строки кода только лишь потому, что библиотека неинициализирована до вызова конструкторов глобальных объектов.
Резон конечно есть,- лишняя сущность только во вред. Еще раз спасибо, буду пробовать.
Go to the top of the page
 
+Quote Post
Andy Mozzhevilov
сообщение Mar 16 2011, 08:36
Сообщение #13


Знающий
****

Группа: Свой
Сообщений: 877
Регистрация: 26-01-05
Из: Екатеринбург
Пользователь №: 2 206



Обращайтесь, если что. Можно в аську.


--------------------
Пасу котов...
Go to the top of the page
 
+Quote Post
Aprox
сообщение Mar 17 2011, 17:28
Сообщение #14


Местный
***

Группа: Участник
Сообщений: 374
Регистрация: 7-11-07
Из: Moscow
Пользователь №: 32 131



Цитата(Andy Mozzhevilov @ Mar 16 2011, 11:36) *
Обращайтесь, если что. Можно в аську.
Спасибо. Потыркался целый день, искал кого куда и в какой момент вызывать - сильно утомился. Решил, как обычно- "лучшее враг хорошего" и остановился на своем варианте- инициализировать периферию и C-библиотеки после входа в main(). Так проще прикладнику, которому важнее сделать работающую вещь, чем погрязнуть в тонкостях стартапов. У меня работает, а больше ничего и не надо.

Вы говорили , что ucos инициализируют в low_leve_init. Я вспоминаю, когда года четыре назад возился с модулями Ethernet фирмы NetBurner, - там ucos инициализировалась вызовом Start_UCOS() сразу после входа в main(). Также инициализировался и TCP/IP стек вызовом Start_Enet(), тоже после main(). Все они написаны на C. А приложение пользователя писалось на C++. Компилятор был GCC. Собственно, те четырехлетние воспоминания и спасли меня от мучений с реал-тайм JTAG отладкой на IAR.
Go to the top of the page
 
+Quote Post
Andy Mozzhevilov
сообщение Mar 18 2011, 04:37
Сообщение #15


Знающий
****

Группа: Свой
Сообщений: 877
Регистрация: 26-01-05
Из: Екатеринбург
Пользователь №: 2 206



Цитата(Aprox @ Mar 17 2011, 20:28) *
Вы говорили , что ucos инициализируют в low_leve_init. Я вспоминаю, когда года четыре назад возился с модулями Ethernet фирмы NetBurner, - там ucos инициализировалась вызовом Start_UCOS() сразу после входа в main().


Я говорил, что я так делаю. Как делают другие, не знаю.
Если uCOS инициализировать стандартным способом, т.е. вызовом из main(), то в ПО на С++ теряется возможность создавать объекты, использующие сервисы uCOS в конструкторах объектов, объявленных глобально, т.е. необходимо каждый раз прибегать к вызову дополнительной функции инициализации.
С моей точки зрения, если внутри объекта к примеру используется семафор, логично, чтобы этот семафор создавался (выделялся) в конструкторе объекта. В этом случае после создания объекта он готов к использованию без дополнительных вызовов. Это как раз и решается нужной организацией стартапа (в части low_level_init()).
Поэтому способ, который я описал, он полностью рабочий и проверенный на практике.
Конечно, если ПО написано полностью на Си, то эти заморочки ни к чему.


--------------------
Пасу котов...
Go to the top of the page
 
+Quote Post

2 страниц V   1 2 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 26th July 2025 - 00:37
Рейтинг@Mail.ru


Страница сгенерированна за 0.0159 секунд с 7
ELECTRONIX ©2004-2016