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

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> LPC2888, Data Abort exception, при выполнении кода из Flash
artymen
сообщение Jan 10 2011, 07:27
Сообщение #1


Участник
*

Группа: Участник
Сообщений: 66
Регистрация: 6-11-09
Из: г. Омск
Пользователь №: 53 464



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

Проблема возникла, когда размер проекта достиг порядка 2000 строк кода. Причем "охват" использования ресурсов МК небольшой: прерывания не используются, код линейный, из периферии используется только GPIO, LCD контроллер, ну и CGU для тактирования последнего. Компилятор GCC (Yagarto). Оптимизация кода отключена. Отлаживаю J-Link'ом (через GDB Server), драйвера и прошивка последние. Cache и memory remap не используются, в j-linke также отключено всяческое кеширование.
Из RAM код выполняется замечательно, но из Flash вдруг где-то посередине (на дисплее половина интерфейса уже отрисовалась) процессор вываливается в Data Abort. Причем происходит это даже не в какой то ответсвенный момент работы с регистрами периферии или еще чего-то там, а в в процессе выполнения безобидной функции. Вот она:
Код
10401878 <_Z14get_menu_childP18menu_item_header_th>:
menu_item_header_t* get_menu_child(menu_item_header_t* menu, uint8_t i) {
10401878:    e52db004     push    {fp}; (str fp, [sp, #-4]!)
1040187c:    e28db000     add    fp, sp, #0
10401880:    e24dd00c     sub    sp, sp, #12
10401884:    e50b0008     str    r0, [fp, #-8]
10401888:    e1a03001     mov    r3, r1
1040188c:    e54b3009     strb    r3, [fp, #-9]
    return (menu_item_header_t *)((uint8_t *)(menu) + *((uint16_t *)((uint8_t *)(menu) + sizeof(menu_item_header_t) + 2*(i))));
10401890:    e51b2008     ldr    r2, [fp, #-8]
10401894:    e55b3009     ldrb    r3, [fp, #-9]
10401898:    e2833001     add    r3, r3, #1
1040189c:    e1a03083     lsl    r3, r3, #1
104018a0:    e0823003     add    r3, r2, r3
104018a4:    e1d330b0     ldrh    r3, [r3] <--- выполнение этой инструкции приводит к возникновению data abort exception
104018a8:    e51b2008     ldr    r2, [fp, #-8]
104018ac:    e0823003     add    r3, r2, r3
}
104018b0:    e1a00003     mov    r0, r3
104018b4:    e28bd000     add    sp, fp, #0
104018b8:    e8bd0800     pop    {fp}
104018bc:    e12fff1e     bx    lr
Я поставил брикпойнт на эту инструкцию. В регистре R3 на этот момент находится вполне валидный адрес из области flash. Просматриваю память по этому адресу, все шикарно читается, значение показывает то самое, которое и должно быть там (это значение я узнал, выполнив до этого код в RAM'е). Ну что делать ? Наверно, надо как-то вмешаться, возможно я сброшу какой-нить там кеш или конвейр комманд своим действием. Беру и меняю адрес в R3 на другой произвольно взятый из области flash. Делаю шаг, все равно вываливается, ну и ладно, чуда не произошло. Начинаю все сначала, и на этот раз меняю его на адрес из RAM. Вуаля, инструкция выполняется успешно ! Но мне то надо, чтобы код выполнялся так, как он написан. И пришлось мне оказать помощь процу, взяв выполнение инструкции "ldrh r3, [r3]" на себя, раз уж он не справляется, благо это меня не сильно обременило, потому как эта функция всего два раза вызывается. Итак, что я сделал. Проц останавливается про брикпоинту на этой инструкции. Я ручками беру адрес из R3, по этому адресу из памяти запрашиваю значение, запихиваю это значение в R3, в регистр PC запихиваю значение 0x104018a8 (перепрыгиваю через злосчастную инструкцию), говорю отладчику продолжить выполнять код дальше. Остановка происходит на втором вызове, я повторяю те же действия. В результате код выполняется до конца, на дисплее тот же результат, какой был при выполнении из RAM.

Какие есть соображения по этому поводу ?

P.S. За startup-код и линкер-скрипты ручаюсь.
P.S.2. Я портировал код на IAR, и то же самое повторилось в этой же функции, но уже при выполнении из RAM.

Сообщение отредактировал artymen - Jan 10 2011, 07:32


--------------------
"Сознание своего несовершенства приближает к совершенству"
Гёте
Go to the top of the page
 
+Quote Post
Chameleon
сообщение Jan 10 2011, 09:52
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 50
Регистрация: 5-07-08
Пользователь №: 38 757



Выравнивание? Чему равен младший бит регистра R3 в команде, которая вызывает Data Abort?
Go to the top of the page
 
+Quote Post
artymen
сообщение Jan 11 2011, 01:27
Сообщение #3


Участник
*

Группа: Участник
Сообщений: 66
Регистрация: 6-11-09
Из: г. Омск
Пользователь №: 53 464



Ну точно ! Сейчас прочитал, что архитектура требует: "Halfwords must be aligned to 2-byte boundaries". В обоих случаях у меня невыровненный адрес ! Я так понимаю, поведение процессора тут зависит от реализации. Видимо, в других МК самое худшее к чему это приводит, это к усложеннной процедуре доступа к памяти и уменьшению производительности, однако здесь это обрубили абортом.
Выходит, я выявил весьма скрытый баг компилятора. Он должен был оценить, что не может узнать заранее, какое выравнивание получится в этом сложном выражении, а потому использовать побайтовый доступ (LDRB), даже несмотря на политику оптимизации (которая, вобщем-то, отключена, но я склонен полагать, что он все равно на каком то минимальном уровне оптимизирует).

И что можно сделать в такой ситуации ? sm.gif

P.S. Об этом, оказывается, уже написали здесь: http://gcc.gnu.org/ml/gcc/2000-05/msg00683.html Еще в 2000 году. Видимо, вопрос либо остался без внимания разработчиков, либо они его победили, но спустя много лет баг вернулся sm.gif

Сообщение отредактировал artymen - Jan 11 2011, 01:30


--------------------
"Сознание своего несовершенства приближает к совершенству"
Гёте
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Jan 11 2011, 02:00
Сообщение #4


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(artymen @ Jan 11 2011, 07:27) *
Он должен был оценить...

Не должен. И это не баг компилятора.
Go to the top of the page
 
+Quote Post
artymen
сообщение Jan 11 2011, 02:45
Сообщение #5


Участник
*

Группа: Участник
Сообщений: 66
Регистрация: 6-11-09
Из: г. Омск
Пользователь №: 53 464



А чей же ? Неужто это я должен заботиться об архитектурно-специфичном выравнивании, создавая программу на языке высокого уровня ?! Я считаю, что компилятор должен брать эту ответственность на себя. Я же, как программист, не отказываюсь, например, от ответственности за выход за пределы памяти, которым грозит использование указателей.


--------------------
"Сознание своего несовершенства приближает к совершенству"
Гёте
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Jan 11 2011, 03:30
Сообщение #6


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Цитата(artymen @ Jan 11 2011, 08:45) *
Неужто это я должен заботиться об архитектурно-специфичном выравнивании, создавая программу на языке высокого уровня ?!

Да, должны. Если уж используете явное преобразование типов, то должны и представлять себе последствия на выбранной архитектуре.
Go to the top of the page
 
+Quote Post
artymen
сообщение Jan 11 2011, 05:07
Сообщение #7


Участник
*

Группа: Участник
Сообщений: 66
Регистрация: 6-11-09
Из: г. Омск
Пользователь №: 53 464



Нет, не должен ! Вот хотя бы одна объективная причина (а их немало в эмбеддед кодинге, я думаю). Памяти в МК мало, поэтому я напрямую работаю с файлом шрифта, загруженным во флеш при программировании. А все внутренние структуры шрифта упакованы, а таблицы 8битные нечетного размера и т.д., то есть выравнивания никакого и в помине нету. Я пыхтел, разрабатывая свой собственный ультракомпактный формат шрифта, чтобы запихать его в мизерную память. И что в итоге получилось ? Капризный компилятор, видите ли, забил на проверку выравнивания. Ему, видите ли, вломы было разбить 16-битный доступ на два 8-битных. И теперь я должен переделывать формат файла, выравнивая данные в нем, то есть переделывать и усложнять код генератора шрифта, да еще и размер файла увеличивать ! И все для того, чтобы программа заработала конкретно на LPC2888. А, к примеру, на LPC2106 она уже будет не так работать. Согласитесь, это бред. Разработчики gcc, я думаю, согласились, и предусмотрели это, введя опции -m(no-)short-load-bytes, однако позже концепция была переделана в -m(no-)alignment-traps. Но мне несказанно повезло с текущей сборкой Yagarto. Версия gcc как раз пришлась ровно на этот перелом. В ней нету ни первой (уже устаревшей) опции, и новой (еще не появившейся) опции biggrin.gif
UPDATE: Похоже ни той, ни другой опции больше вообще нету и не будет. Замечательно, что тут еще сказать.

Сообщение отредактировал artymen - Jan 11 2011, 05:24


--------------------
"Сознание своего несовершенства приближает к совершенству"
Гёте
Go to the top of the page
 
+Quote Post
IgorKossak
сообщение Jan 11 2011, 05:40
Сообщение #8


Шаман
******

Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221



artymen, не кипятитесь. Послушайте ещё раз и внимательно.
Компилятор поступает абсолютно правильно в плане выравнивания, когда сам располагает переменные в памяти и обращается к ним сообразно их типу. В Вашем же случае Вы его насильно просите рассматривать байтовые переменные (которые он вполне по праву разместил в памяти без выравнивания) как словные (полусловные). Обращение к словным переменным компилятор осуществляет как к выровненным, ибо на момент компиляции их выравнивание неизвестно. Раз так, то именно на Ваши плечи ложится задача обеспечить выравнивание в рантайме. Кроме выравнивания в Вашем фрагменте есть ещё один нюанс непереносимости. Вы полагаетесь на вполне определённое расположение байтов в полуслове. Поэтому Ваше выражение (uint16_t *)((uint8_t *)(menu) может вызвать вполне справедливые глюки при переносе на платформу с другой эндианностью.
Как правило, следование хорошему стилю программирования не вызывает нареканий на компилятор.

PS Простейшее, что можно сделать в Вашем случае, это сделать длину объекта menu_item_header_t кратной двум. Если же Вы озабочены компактностью данных, то лучше работать через промежуточные переменные нужной длины.
Go to the top of the page
 
+Quote Post
artymen
сообщение Jan 11 2011, 06:20
Сообщение #9


Участник
*

Группа: Участник
Сообщений: 66
Регистрация: 6-11-09
Из: г. Омск
Пользователь №: 53 464



Я прекрасно это понимаю. И, как я сейчас выяснил, компилятор тоже прекрасно это понимает и выдает предупреждение, включаемое опцией -Wcast-align (у меня почему то не выдавал несмотря на -Wall, пока я не указал опцию явно). Просто я всего-навсего говорю о том, что компилятору следует не только предупреждать, но и проявлять инициативу, а именно разбить 16-битный доступ на два 8-битных. Да, это будет немного медленнее, но зато это будет 100% РАБОТАТЬ. Или указание из ARM Architecture Reference Manual на UNPREDICTABLE memory access (прям так заглавными буквами в мануале и написано) звучит недостаточно убедительно для разработчиков gcc ? Для них приоритетнее производительность в угоду 50% вероятной неработоспособности кода ?
А эндианность я предусмотрел еще в самом начале проектирования, и в общем загловочном файле даже поместил
Код
#ifndef __ARMEL__
#error This file should only be compiled in little endian mode
#endif
Тут, я должен признать, справедливый упрек, но мы не расчитываем перенести когда-либо этот проект с арма на что-нить другое.
Пожалуй, мне и остается только сделать длину menu_item_header_t кратной двум, тобишь выровнять элементы структуры. К счастью, формат шрифта эти изменения не затронут, и в целом не так уж много придется вещей выравнивать, но все же очень и очень неприятно...

Сообщение отредактировал artymen - Jan 11 2011, 06:21


--------------------
"Сознание своего несовершенства приближает к совершенству"
Гёте
Go to the top of the page
 
+Quote Post
IgorKossak
сообщение Jan 11 2011, 06:34
Сообщение #10


Шаман
******

Группа: Модераторы
Сообщений: 3 064
Регистрация: 30-06-04
Из: Киев, Украина
Пользователь №: 221



QUOTE (artymen @ Jan 11 2011, 11:20) *
... что компилятору следует не только предупреждать, но и проявлять инициативу, а именно разбить 16-битный доступ на два 8-битных. Да, это будет немного медленнее ...

А другой программист может возразить: "Нафига мне такая инициатива компилятора? Чего это он умничает? Мне не нужно никакого разбиения на побайтовый доступ, а нужна максимальная производительность!" И тоже будет прав. На всех не угодишь. rolleyes.gif

QUOTE (artymen @ Jan 11 2011, 11:20) *
...но мы не расчитываем перенести когда-либо этот проект с арма на что-нить другое.
... но все же очень и очень неприятно...

Неприятно, согласен, но раз уж Вы подсели на армы, то придётся считаться с особенностями архитектуры.
Тем не менее, кортексы - в первом приближении те же армы и с выравниванием данных у них проблем нет. И перенести не сложно.
Go to the top of the page
 
+Quote Post
xelax
сообщение Jan 11 2011, 06:44
Сообщение #11


Местный
***

Группа: Свой
Сообщений: 370
Регистрация: 7-11-06
Пользователь №: 22 035



Цитата(artymen @ Jan 11 2011, 11:07) *
Нет, не должен ! Вот хотя бы одна объективная причина (а их немало в эмбеддед кодинге, я думаю). Памяти в МК мало, поэтому я напрямую работаю с файлом шрифта, загруженным во флеш при программировании. А все внутренние структуры шрифта упакованы, а таблицы 8битные нечетного размера и т.д., то есть выравнивания никакого и в помине нету. Я пыхтел, разрабатывая свой собственный ультракомпактный формат шрифта, чтобы запихать его в мизерную память. И что в итоге получилось ? ...


А вот с пакованными структурами вообще надо быть очень осторожным. Компилятор генерит специальный код для доступа к полям таких структур, обращааясь к ним побайтно (к стати тут интересный момент, пакуешь структуры, чтобы сэкономить память, но из-за специфической кодогенерации объём всё равно растёт). И стоит только где-нибудь в сишном коде свалиться к обращениям по нетипизированным указателям к этим полям дата аборты полезут, как клопы из-зо всех щелей. Поэтому программист на С ОБЯЗАН отслеживать все подобные моменты в своём коде. НЕ ВЫКРУЧИВАТЬ РУКИ компилятору преобразованием типов и адресной арифметикой при работе с пакованными типами и уж точно потом не жаловаться на кривизну компилятора, если не соблюдает этих элементарных правил.

Всё вышеописанное справедливо для GCC, IAR а также ARM7, CortexM3, AVR32. То что на собственной шкуре испытал.

З.Ы. К стати когда то над моим постом, о том что внутри memcpy происходит дата аборт, достопочтимые гуру просто надругались. Мы докопали тему и нашли когда именно в gсс могут пойти дата аборты из memcpy. Без опции no-builtin-memcpy компимлятор не вставлял свою функцию memcpy, а каждый раз генерировал новый код, где если нет явных указаний на пакованность типов, может возникать обращение по невыровненным адресам.

Цитата(artymen @ Jan 11 2011, 12:20) *
Просто я всего-навсего говорю о том, что компилятору следует не только предупреждать, но и проявлять инициативу, а именно разбить 16-битный доступ на два 8-битных.


Собственно говоря компилятор так и делает когда необходимо, например при пакованных структурах. А если структура обыкновенная, то компилятор СПРАВЕДЛИВО считает, что всё находится по выровненным адресам и делает чтение\запись с максимальной возможной разрядностью.

И вообще баги надо начинать искать со своего кода, а не огульно обвинять компилятор в том что программа не работает. 1111493779.gif
То что код работал из RAM, судя по всему простая случайность.
Go to the top of the page
 
+Quote Post
sonycman
сообщение Jan 11 2011, 07:22
Сообщение #12


Любитель
*****

Группа: Свой
Сообщений: 1 864
Регистрация: 20-08-06
Из: Тольятти
Пользователь №: 19 695



Цитата(IgorKossak @ Jan 11 2011, 12:34) *
Тем не менее, кортексы - в первом приближении те же армы и с выравниванием данных у них проблем нет.

Есть, на самом деле, но поменьше, конечно.
Инструкции типа LDRD или LDM всё так же требуют выровненных данных.
Go to the top of the page
 
+Quote Post
artymen
сообщение Jan 11 2011, 07:40
Сообщение #13


Участник
*

Группа: Участник
Сообщений: 66
Регистрация: 6-11-09
Из: г. Омск
Пользователь №: 53 464



Эх... Значит проблема у меня на уровне системного проектирвоания. Хорошо, а как бы вы разрулили эту ситуацию, когда надо сразу с данными работать, не перекидывая из памяти в память, чтобы подвести выравнивание (а также, опционально, эндианность и т.п.) под общий знаменатель ?

Цитата
А другой программист может возразить: "Нафига мне такая инициатива компилятора? Чего это он умничает? Мне не нужно никакого разбиения на побайтовый доступ, а нужна максимальная производительность!" И тоже будет прав. На всех не угодишь.
Я предусмотрел такой ответ sm.gif Поясню свою позицию. Эта инициатива очень и очень скромная, как невинное деяние монашки, по сравнению с многочисленными другими инициативами, которые компилятор творит в процессе оптимизации: режет переменные, рубит функции, раскидывает операции и прочий ужас, против которого даже грозный volatile не всегда спасает. Так что если "другие программисты" предпочитают производительность неработоспособности, то я склонен рассматривать это как извращение, а извращение это меньшинство, следовательно следует предусмотреть явную опцию, которая отключает инициативу, а по умолчанию (тобишь для большинства) инициатива должна быть.

P.S. Структуры я запаковал затем, чтобы "унифицировать" тем самым выравнивание (большинство компиляторов, я думаю, умеет упаковывывать до 1 байта, а до произвольного числа - не факт), ну и размер сократить заодно.

Цитата
Собственно говоря компилятор так и делает когда необходимо, например при пакованных структурах. А если структура обыкновенная, то компилятор СПРАВЕДЛИВО считает, что всё находится по выровненным адресам и делает чтение\запись с максимальной возможной разрядностью.
Я понимаю, но у меня компилятор все знает. Я не подсовываю ему непакованную структуру вместо пакованной, или наоборот. Ошибка возникла в том месте, где арифметика указателей.

Цитата
То что код работал из RAM, судя по всему простая случайность.
Там тоже был невыровненный доступ sm.gif Но мне кажется, это недоработка со стороны аппаратной поддержки невыравниваемого доступа. Полагаю, она есть несмотря на директиву в архитектуре. По всей видимости, контроллер памяти успел провернуть трюк с двойным доступом и ротацией в RAM, а вот с флешем этот трюк уже не уложился во флешевые wait-таймы, и потому провалился по таймауту, вызвав аборт. Это всего лишь предположение, точнее очередное мое злорадство в сторону LPC2888 sm.gif

Сообщение отредактировал artymen - Jan 11 2011, 08:25


--------------------
"Сознание своего несовершенства приближает к совершенству"
Гёте
Go to the top of the page
 
+Quote Post
GetSmart
сообщение Jan 11 2011, 08:04
Сообщение #14


.
******

Группа: Участник
Сообщений: 4 005
Регистрация: 3-05-06
Из: Россия
Пользователь №: 16 753



Цитата(artymen @ Jan 11 2011, 15:40) *
Хорошо, а как бы вы разрулили эту ситуацию, когда надо сразу с данными работать, не перекидывая из памяти в память, чтобы подвести выравнивание (а также, опционально, эндианность и т.п.) под общий знаменатель ?

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

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

Побайтовый доступ в IAR например делается очень красиво. Это просто пакованная (по желанию побайтно или по 16 бит если достаточно по-half доступ) структура с любыми типами, даже с одним LONG. И в коде обычное обращение к полю структуры. Чистый Си, без извратов. Вместо структуры можно использовать пакованный юнион со всеми его свойствами и побайтовым доступом.

Сообщение отредактировал GetSmart - Jan 11 2011, 08:12


--------------------
Заблуждаться - Ваше законное право :-)
Go to the top of the page
 
+Quote Post
sergeeff
сообщение Jan 11 2011, 08:17
Сообщение #15


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

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



Цитата(artymen @ Jan 11 2011, 14:40) *
Ошибка возникла в том месте, где арифметика указателей.


Ошибка возникла, когда вы заставляете процессор читать данные, размерностью больше 1 байта с нечетных адресов. А он этого делать не может. Поэтому вам надо, с учетом этой процессорной специфики, поменять свою концепцию хранения данных.
Go to the top of the page
 
+Quote Post

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

 


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


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