AndreyS 0 10 декабря, 2008 Опубликовано 10 декабря, 2008 · Жалоба Люди!!! Помогите. Пока перебросил переменные одной процедуры в XDATA, чтобы они ушли из перекрестка. Вот настройки проекта: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
barabek 0 10 декабря, 2008 Опубликовано 10 декабря, 2008 · Жалоба Суть проблеммы В рабочем режиме иногда прошивка виснет. Выяснил, что зависает (зацикливается) она по причине того что локальные переменные процедуры кто-то запорол. Нашел в карте памяти что данные ячейки относятся еще к двум процедурам (причем там эти переменные тоже локальные). Одна из которых вызывается в прерывании. Как мне теперь заставить Keil убрать из перекрестных ссылок эти переменные (чтобы они лежали в разных адресах DATA)? Делать их глобальными внутри одного файла желания нет. Пока я проверил только локальные переменные в области DATA, быть может такая петрушка есть еще с переменными в области XDATA. Почему Keil их так расположил? Ведь при компиляции он знает какие процедуры попадают в прерывание и по идее должен этим процедурам для локальных переменных выдилять свои адреса. Может нужно что-то указать?? Спасибо. Как я понял, у Вас есть функция которая вызывается из функции обработки прерывания? Если так, то это очень не гуд :). Для этого нужны очень веские основания. Только в том случае, если Вы эту функцию вызываете еще из другого места. Но эту ситуацию линкер возможно и не отслеживает и нужно в этой функции переменные объявить static, хотя это равносильно глобальной переменной. Да и тем более возможен повторный вход в функцию со всеми вытекающими. Этот вариант отметаем :( Если же вызов только из прерывания, тогда лучше вобще обойтись без дополнительной функции, все стащить в одно место, в функцию-обработчик. Вы же наверняка пользуетесь using-ами, может быть линкер эти переменные через регистры протащит, будет маленькое счастье :). А нельзя вобще обойтись в прерывании без обработки, выставив соответствующий флажок и обработать в другом месте? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AndreyS 0 10 декабря, 2008 Опубликовано 10 декабря, 2008 (изменено) · Жалоба Как я понял, у Вас есть функция которая вызывается из функции обработки прерывания? Если так, то это очень не гуд :). Для этого нужны очень веские основания. Только в том случае, если Вы эту функцию вызываете еще из другого места. Но эту ситуацию линкер возможно и не отслеживает и нужно в этой функции переменные объявить static, хотя это равносильно глобальной переменной. Да и тем более возможен повторный вход в функцию со всеми вытекающими. Этот вариант отметаем :( Если же вызов только из прерывания, тогда лучше вобще обойтись без дополнительной функции, все стащить в одно место, в функцию-обработчик. Вы же наверняка пользуетесь using-ами, может быть линкер эти переменные через регистры протащит, будет маленькое счастье :). А нельзя вобще обойтись в прерывании без обработки, выставив соответствующий флажок и обработать в другом месте? Огромное спасибо за содержательный ответ. В прерывании использую процедуру которая вызывается еще и по коду. Суть процедуры сформировать (подготовить запрос на SPI шину и поставить его в очередь). Т.е. по сути - это и флажок и короткий обработчик этого флажка. Да. Переосмыслил сейчас еще раз свой код и чую буду переделывать обработчик прерывания. :) Да, тут я упустил из внимания возможность повторного вызова процедуры в неблагоприятный момент. :) Вы подняли мне веки :) Правда это не отменяет объединения (отображения части переменных по одним и тем же адресам) локальных переменных двух разных процедур (я пока просто перебросил переменные в одной процедуре в XRAM и таким образом развязал эти процедуры). Буду переписывать обработчик. Изменено 10 декабря, 2008 пользователем AndreyS Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Axel 1 10 декабря, 2008 Опубликовано 10 декабря, 2008 · Жалоба Если функция не написана, как реентерабельная (слово не я придумал!) и при этом может вызываться из основной петли и из прерываний, то это, вобщем, конкретные грабли. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
barabek 0 11 декабря, 2008 Опубликовано 11 декабря, 2008 · Жалоба Правда это не отменяет объединения (отображения части переменных по одним и тем же адресам) локальных переменных двух разных процедур (я пока просто перебросил переменные в одной процедуре в XRAM и таким образом развязал эти процедуры). Если две функции не могут вызываться одновременно (случай с прерыванием) - то конечно же пусть они используют одни и те же адреса для локальных переменных. В этом и соль этих переменных в отличии от глобальных. Если посмотреть на листинг то почти в каждой функции для локальных переменных используются регистры. И это очень хорошо, это только плюс компилятору-линкеру. Ведь памяти очень немного :) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tag 0 11 декабря, 2008 Опубликовано 11 декабря, 2008 · Жалоба В этом и соль этих переменных в отличии от глобальных. Уверяю вас соль не в этом. Как известно памяти у 51 немного, плюс компилятор разработан так что локальные переменные сохраняет не на стеке, а либо в регистрах, либо в памяти (в этом вы можете убедится просмотрев руководство). В случае когда переменные располагаются в памяти линковщик может перекрывать область памяти локальных переменных одной подпрограммы и такую же область другой подпрограммы. Видимо так сделано с целью экономии памяти. Тогда, конечно подразумевается что эти фукции к примеру, не могут вызывать друг друга из своего тела. Так линковщик работает если ему не указать опцию nooverlay. Когда данная опция указана области локальных переменных подпрограмм не пересекаются и следовательно проблема порчи локальных переменных исчезает. nooverlay.bmp Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
barabek 0 11 декабря, 2008 Опубликовано 11 декабря, 2008 · Жалоба Уверяю вас соль не в этом. Как известно памяти у 51 немного, плюс компилятор разработан так что локальные переменные сохраняет не на стеке, а либо в регистрах, либо в памяти (в этом вы можете убедится просмотрев руководство). В случае когда переменные располагаются в памяти линковщик может перекрывать область памяти локальных переменных одной подпрограммы и такую же область другой подпрограммы... А я разве сказал не тоже самое?:-D Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AndreyS 0 11 декабря, 2008 Опубликовано 11 декабря, 2008 (изменено) · Жалоба Если две функции не могут вызываться одновременно (случай с прерыванием) - то конечно же пусть они используют одни и те же адреса для локальных переменных. В этом и соль этих переменных в отличии от глобальных. Если посмотреть на листинг то почти в каждой функции для локальных переменных используются регистры. И это очень хорошо, это только плюс компилятору-линкеру. Ведь памяти очень немного :) Добрый день. Спасибо всем за ответы. Все что Вы ранее высказывали, я прекрасно понимаю. Но тут вещь в следующем. Компилятор никогда не отображает локальные переменные на одни и теже адреса, если они пересекаются при вызове процедур (РАЗНЫХ ПРОЦЕДУР). Если переменные пересекаются (отображаются на одни и теже адреса) (РЕГИСТРЫ НАПРИМЕР), то компилятор при входе в такую процедуру сохраняет все эти регистры в стек. Если компилятор этого не делает - то это плохой компилятор. Анализирую код Keil'а он (Кейл) именно так и делает. Если я в перерывании (или процедуре) использую вызов процедуры, то в прерывании, перед ее выполнением, сохряняются все регистры и PSW в стек. Если я неиспользую каких либо регистров (ну компилятор в процессе компиляции ничего не задействовал), то на входе он ничего не сохраняет. Для Кейла я это проверял. Правда весь листинг виден только в самом проце, в выходных файлах этого нет. В данном примере происходит все именно так. Если у меня есть ряд процедур, которые могут вызваться из прерывания и у них есть локальные переменные, то компилятор не отражает на адреса этих переменных ничего из других процедур. В данном примере я заметил пересекающиеся области (две ячейки наложились. Две ячейки локальных переменных разных процедур). Причем ячеки входящих переменных (извиняюсь за корявость, но переменных через которые я передаю данные в процедуру). Есть функция Spi_RD в которую я передаю ряд параметров. Она вызывается в прерывании. Есть функция Filtrate_long в которую я передаю одно число и в нутри нее есть переменные счетчики. Так пересеклись две переменные из входящих переменных функции Spi_RD и внутренние переменные-счетчики процедуры Filtrate_long. На примере других функций я вижу, что используемые регистры для локальных переменных в процедурах, компилятор сохраняет в стек, при вызове внутри одной процедуры другой (с такими же регистрами). Иначе я вообще никогда не смог бы вызывать внутри процедур другие процедуры. Именно так происходит и тут. Просто количество параметров передаваемых в процедуру Spi_RD наверное большое и они вышли за регистры и компилятор о них забыл (мое предположение). Вот вызовы процедур с которыми я нашел пересечение переменных: bit Spi_RD(byte * ptr, byte * complete, word LEN, byte type, byte FIRST_SENT, bit cs); signed long filtrate_long(signed long input_data); Пересеклись адреса следующих переменных type, FIRST_SENT с локальными переменными (счетчиками) внутри функции Filtrate_long. В данный момент локальные переменные функции Filtrate_long я объявил в области XDATA дабы они попросту ушли из карты памяти в другую область (остальные вроде как ен пересекаются). И все работает без глюков. Без наездов. Просто пытаюсь разобраться, что я делаю не так. И что мне следует сделать, чтобы такого не происходило впредь. Ведь я не исключаю того что такое же может произойти в области XDATA. Как известно памяти у 51 немного, плюс компилятор разработан так что локальные переменные сохраняет не на стеке, а либо в регистрах, либо в памяти (в этом вы можете убедится просмотрев руководство). В случае когда переменные располагаются в памяти линковщик может перекрывать область памяти локальных переменных одной подпрограммы и такую же область другой подпрограммы. Видимо так сделано с целью экономии памяти. Тогда, конечно подразумевается что эти фукции к примеру, не могут вызывать друг друга из своего тела. Так линковщик работает если ему не указать опцию nooverlay. Когда данная опция указана области локальных переменных подпрограмм не пересекаются и следовательно проблема порчи локальных переменных исчезает. Добрый день. Огромное вам спасибо. Мне кажется - это есть лекарство моей болезни. Так как симптомы вы описали точно. Черт, а ведь эта директива относится ко всему проекту. С ней области data не хватает :) А для лечения локально, нужно делать глобальбные переменные. А что же делать с входыми данными процедур??? Тоже глобально?? Изменено 11 декабря, 2008 пользователем AndreyS Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
barabek 0 11 декабря, 2008 Опубликовано 11 декабря, 2008 (изменено) · Жалоба Компилятор никогда не отображает локальные переменные на одни и теже адреса, если они пересекаются при вызове процедур (РАЗНЫХ ПРОЦЕДУР). Если переменные пересекаются (отображаются на одни и теже адреса) (РЕГИСТРЫ НАПРИМЕР), то компилятор при входе в такую процедуру сохраняет все эти регистры в стек. Если компилятор этого не делает - то это плохой компилятор. Анализирую код Keil'а он (Кейл) именно так и делает. Здесь позвольте с Вами не согласиться. Это относится не конкретно к Keil, а вообще к языку C. Локальные переменные, кроме того что они видны только внутри функции (блока), они еще имеют время жизни только на протяжении выполнения этой функции (блока). Ни в коем случае не гарантируется, что при очередном вызове функции локальная переменная будет иметь тоже значение, что было раньше. Если я в перерывании (или процедуре) использую вызов процедуры, то в прерывании, перед ее выполнением, сохряняются все регистры и PSW в стек. Если я неиспользую каких либо регистров (ну компилятор в процессе компиляции ничего не задействовал), то на входе он ничего не сохраняет. Для Кейла я это проверял. ,,, ....Если у меня есть ряд процедур, которые могут вызваться из прерывания и у них есть локальные переменные, то компилятор не отражает на адреса этих переменных ничего из других процедур. В данном примере я заметил пересекающиеся области... Чтобы не сохранять регистры лучше использовать using. А вот для этого случая - просто функции и функции вызываемой из прерывания, Keil действительно подкачал :), видимо у него нет такой проверки на перекрытие переменных :(. Я когда-то тоже обжигался на этом. Теперь прерывания только наиболее короткие, без вызовов, с использованием отдельного банка памяти для прерываний одинакового приоритета. Правда весь листинг виден только в самом проце, в выходных файлах этого нет. А в *.LST разве в конце не расписаны адреса под переменные (при соответствующих галочках в настройках), или это не то? bit Spi_RD(byte * ptr, byte * complete, word LEN, byte type, byte FIRST_SENT, bit cs); Меня, честно говоря, пугают такие функции с огромным количеством входных переменных :). Не воспринимайте мои слова близко к сердцу. Это конечно не ошибка, просто я бы использовал структуру и передавал бы указатель на нее. Изменено 11 декабря, 2008 пользователем barabek Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AndreyS 0 11 декабря, 2008 Опубликовано 11 декабря, 2008 · Жалоба Здесь позвольте с Вами не согласиться. Это относится не конкретно к Keil, а вообще к языку C. Локальные переменные, кроме того что они видны только внутри функции (блока), они еще имеют время жизни только на протяжении выполнения этой функции (блока). Ни в коем случае не гарантируется, что при очередном вызове функции локальная переменная будет иметь тоже значение, что было раньше. Меня, честно говоря, пугают такие функции с огромным количеством входных переменных :). Не воспринимайте мои слова близко к сердцу. Это конечно не ошибка, просто я бы использовал структуру и передавал бы указатель на нее. Я согласен что локальные переменные имеют время жизни только на протяжении функции. Но при этом регистры то он в стек сохраняет (т.е. если я из функци вызову другую, то локальные переменные первой функции он сохранит в стек или будет использовать локальные переменные второй функции в другой области), а вот локальные переменные, что вылезли за диапазон регистров он в стек не сохраняет. Т.е. если я передаю в функцию большое количество параметров есть вероятность пересечения. Тут же у меня была проблемма с изменением ячейки. Я не понял почему это происходит (оставил для разбирательств на потом) просто завел внутри процедуры дублирующую локальную переменную (она видно отразилась в регистры) и в нее перебрасывал срузу при входе в процедуру данные из ихменяющейся переменной. Все заработало номарльно. Тогда я не врубился кто виноват (думал сам куда за диапазон выхожу), а теперь понял. Надо шерстить весь проект на предмет большого числа передаваемых параметров. Делать либо как у вас (заполнять структуру и передавать указатель. Просто я хотел унивирсальности, а так прийдется везде еще таскать обписание этой структуры.) либо урезать часть передаваемых параметров в функцию и переносить их например в глобальные. Обидно все это. Регистры сохраняет (так как знает что их портит), а часть используемых ячеек не сохраняет. :( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AndreyS 0 12 декабря, 2008 Опубликовано 12 декабря, 2008 · Жалоба Добрый день. Написал для прерывания специальную функцию Spi_RD, но без передачи параметров. Все параметры передал через глобальную структуру. Пересекающихся адресов теперь нет. Все работает нормально. Печально это :( И печально именно то, что оказалось что Keil не сохраняет все локальные переменные при входе в процедуры. Сохраняет он только регистры. :( А еще более печально то, что прошло столько времени, а подобное в нем досих пор существует. Вердикт один. Если в прерывании используется процедура с количество передаваемых в нее параметров по размеру информации больше чем количество регистров, то такую процедуру в прерывании применять нельзя. Нужно или урезать передаваемые параметры или выводить их в глобальные. Мда. Что еще может подстерегать??? По сему заинтересовал вопрос перехода с Keil на другой компилятор и линковщик. Или просто другой линковщик (вроде как именно он раскидывает переменые по адресам???). Как себя ведут другие компиляторы касательно данной проблеммы?? Не ну быть может это просто некрасиво конечно с точки зрения написания кода, передавать в функцию такое количество параметров как у меня. Но я надеялся, что компилятору это позубам. А иначе накой хрен переходить с Asm, когда мне и тут приходится заботиться о том чтобы небыло пересекающихся адресов?? На Си ведь переходят для удобства и быстроты написания программы (частный случай и лично мое мнение), быстрой модификации программы. В общем чтобы не иметь в частности такой головной боли как раскидывание по адресам локальных переменных. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 123 12 декабря, 2008 Опубликовано 12 декабря, 2008 · Жалоба И печально именно то, что оказалось что Keil не сохраняет все локальные переменные при входе в процедуры. Сохраняет он только регистры. :(Смею предположить, что Кейл все делает правильно. Для цепочки вызовов он строит компилируемый стек и вы сами заметили, что локальные переменные разных функций в такой цепочке не пересекаются. Axel в сообщении №19 сказал вам, что ваш случай отличается от обычного, что вам надо найти способ указать компилятору, что функция является реентерабельной. Штудируйте документацию компилятора на предмет ключевого слова reentrant или подобного. Если бы компилятор каждую функцию делал реентерабельной - вы бы первый взвыли, что он сохраняет кучу лишнего и генерит неэффективный код. Кейлом и x51 уже очень давно не пользуюсь, поэтому не могу подсказать конкретнее, но на 99% уверен, что в нем предусмотрен какой-то атрибут вроде reentrant для функций. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
silabs 0 12 декабря, 2008 Опубликовано 12 декабря, 2008 (изменено) · Жалоба Смею предположить, что Кейл все делает правильно. ...вам надо найти способ указать компилятору, что функция является реентерабельной. Штудируйте документацию компилятора на предмет ключевого слова reentrant или подобного. c51.pdf page 129 Reentrant functions can be called recursively and can be called simultaneously by two or more processes. Reentrant functions are often required in real-time applications or in situations where interrupt code and non-interrupt code must share a function. или в ситуациях, когда код прерывания и код непрерывания должны совместно использовать функцию. Изменено 12 декабря, 2008 пользователем c8051 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 123 12 декабря, 2008 Опубликовано 12 декабря, 2008 · Жалоба c51.pdf page 129 Фух... Кейла оправдали. Очередной "глюк" оказался просто ленью прочитать документацию. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AndreyS 0 13 декабря, 2008 Опубликовано 13 декабря, 2008 (изменено) · Жалоба Фух... Кейла оправдали. Очередной "глюк" оказался просто ленью прочитать документацию. Добрый день. Я и не называл это глюком. В самом первом своем сообщении я сразу просил подсказать что мне сделать. Позже, когда все свелось к изменению способа вызова процедуры, я просто сделал вывод, что Кейл просто не сохраняет в стек ту часть переменных, которая вылезла за регистры. Большое спасибо за наставление. Пойду читать документацию (просто читать ее всю подряд, от корки до корки, я не хотел. В этом я действительно поленился :). Спасибо. PS. Да а на сообщение Axel я не обратил внимания :laughing: Изменено 13 декабря, 2008 пользователем AndreyS Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться