UPD:Любителям загадок WINAVR(решение связано с volatile)

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Закрыто
Вымогатель припоя
Аватара пользователя
Сообщения: 586
Зарегистрирован: Ср мар 07, 2007 22:58:02
Откуда: Черкассы, Украина

Сообщение Negor »

Конечно можно было и в ветку winavr записать это, но случай мне кажется,интересный. Захотел я себе в winavr uart с буферами чтоб можно было в буфер складывать данные а они пусть там через прерывания выводятся. Такой механизм генерит визард CodeVision если ставить там RX или TX прерывние. Вот я выдернул оттуда исходники, запихал их в библиотеку и... приём не работает. Весь проект я сложил в архив а тут приведу важные участки кода. Собственно проблема в таком вот моменте

Код: Выделить всё

while (rx_counter==0);
Ничего волшебного - ждём пока rx_counter перестанеть быть равным 0.
И затыкается отладчик в месте помеченном звёздочкой.

Код: Выделить всё

char getchar_u(void)
{
	char data;
	while (rx_counter==0);
	data=rx_buffer[rx_rd_index];
	if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
	asm("cli");
	--rx_counter;
	asm("sei");
	return data;
}*
Т.е. стрелочка становится там и никуда не двигается. При нажатии F10 студия запускает ход программы но долго останавливает. Смотрим в дизассемблер(для не знающих - в меню AvrStudio View->Dissassembler) и видем там вот такой код

Код: Выделить всё

@000001BE: getchar_u
38:       	while (rx_counter==0);
+000001BE:   91900073    LDS       R25,0x0073     Load direct from data space
+000001C0:   2399        TST       R25            Test for Zero or Minus
+000001C1:   F0A9        BREQ      PC+0x16        Branch if equal
39:       	data=rx_buffer[rx_rd_index];
+000001C2:   91800074    LDS       R24,0x0074     Load direct from data space
+000001C4:   2FE8        MOV       R30,R24        Copy register
+000001C5:   E0F0        LDI       R31,0x00       Load immediate
+000001C6:   59ED        SUBI      R30,0x9D       Subtract immediate
+000001C7:   4FFF        SBCI      R31,0xFF       Subtract immediate with carry
+000001C8:   81E0        LDD       R30,Z+0        Load indirect with displacement
40:       	if (++rx_rd_index == RX_BUFFER_SIZE) rx_rd_index=0;
+000001C9:   5F8F        SUBI      R24,0xFF       Subtract immediate
+000001CA:   93800074    STS       0x0074,R24     Store direct to data space
+000001CC:   3088        CPI       R24,0x08       Compare with immediate
+000001CD:   F411        BRNE      PC+0x03        Branch if not equal
+000001CE:   92100074    STS       0x0074,R1      Store direct to data space
41:       	asm("cli");
+000001D0:   94F8        CLI                      Global Interrupt Disable
42:       	--rx_counter;
+000001D1:   5091        SUBI      R25,0x01       Subtract immediate
+000001D2:   93900073    STS       0x0073,R25     Store direct to data space
43:       	asm("sei");
+000001D4:   9478        SEI                      Global Interrupt Enable
45:       }
+000001D5:   2F8E        MOV       R24,R30        Copy register
+000001D6:   9508        RET                      Subroutine return
+000001D7:   CFFF        RJMP      PC-0x0000      Relative jump
Что-же происходит? А вот что - если R25 равен 0 то выполнится breq PC+0x0016 а именно выполнение перейдёт в 0x000001C1+0x00000016=000001D7. И останется там до второго прешествия потому что rjmp PC-0x0000. На этом чудеса не заканчиваются. Меняю вот так

Код: Выделить всё

while (rx_counter==0)
{
	asm("nop");
};
А нефига. Опять смотрим в отладчик - там

Код: Выделить всё

@000001BE: getchar_u
38:       	while (rx_counter==0)
+000001BE:   91900073    LDS       R25,0x0073     Load direct from data space
+000001C0:   2399        TST       R25            Test for Zero or Minus
+000001C1:   F411        BRNE      PC+0x03        Branch if not equal
40:       		asm("nop");
+000001C2:   0000        NOP                      No operation
+000001C3:   CFFE        RJMP      PC-0x0001      Relative jump
Это кусочек си кода приведённого выше. Опять лажа. До NOP дойдёт но второй раз проверку не сделает. команда RJMP PC-0x0001 приведёт на NOP а не на проверку.
Так тоже до проверки не доходит

Код: Выделить всё

while (!(rx_counter|=0));

Код: Выделить всё

@000001BE: getchar_u
36:       {
+000001BE:   91900073    LDS       R25,0x0073     Load direct from data space
38:       	while (!(rx_counter|=0));
+000001C0:   2399        TST       R25            Test for Zero or Minus
+000001C1:   F3F1        BREQ      PC-0x01        Branch if equal
Точнее проверка есть но содержимое к25 не обновляется. Вобщем пока наверно напишу вставку на асме, но как же так? Все остальные while прекрасно работают... вроде...
Вложения
LTM16_fw1.rar
(28.81 КБ) 163 скачивания
Последний раз редактировалось Negor Вт янв 05, 2010 23:44:55, всего редактировалось 1 раз.
There is only 10 kind of people: those who understands binary code and those who dont!!!
Контактная информация:
Реклама
Вымогатель припоя
Аватара пользователя
Сообщения: 586
Зарегистрирован: Ср мар 07, 2007 22:58:02
Откуда: Черкассы, Украина

Сообщение Negor »

почитал - принял к сведению. Спасибо
There is only 10 kind of people: those who understands binary code and those who dont!!!
Контактная информация:
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18851
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

minime писал(а):volatile - это волшебное слово и оно лечит.
с возвращением! :)))

проблема в том, что мало соблюдать стандарты, которые, кстати, про volatile ничего такого особенного не говорят, надо понимать, что это слово означает - я имею ввиду volatile.

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

Код: Выделить всё

int tmp = 12;

void foo(void){
   while(tmp == 0);
}
в этом коде компилятор видит, что переменная tmp имеет значение 12 и внутри функции foo() не изменяется никак. он делает вывод, что цикл while - бесконечный. скажите, разве он не прав?

чтобы показать компилятору, что переменная в любой момент может изменить свое значение, надо использовать слово volatile - это заставит компилятор все время реально проверять значение переменной, не взирая на то, что внешне она не изменяется.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Открыл глаза
Сообщения: 42
Зарегистрирован: Пн янв 04, 2010 16:06:47

Сообщение minime »

ARV писал(а): компилятор видит, что переменная tmp имеет значение 12 и внутри функции foo() не изменяется никак. он делает вывод, что цикл while - бесконечный. скажите, разве он не прав?
Растолкуй тогда нафигаж он постоянно тестит регистр R25 если ДВАЖДЫ видит что он не может НУ НИ КАК изменится - он ведь сам АСМ код накалялкал.

Из твоего логического объяснения позиции компилятора ему бы не стоило тестить R25 бесконечно.
Вложения
1.png
(4.98 КБ) 485 скачиваний
Реклама
Эиком - электронные компоненты и радиодетали
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18851
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

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

к тому же volatile-подход к любой глобальной переменной очень и очень отрицаительно сказывается на быстродействии и объеме кода, а в некоторых случаях может привести к явно неверным результатам!

вот пример:

Код: Выделить всё

a = b*b + c*b + b/2;
при вычислении выражения компилятор имеет полное право, если ему покажется более оптимальным по скорости, сделать вычисление в таком порядке:

Код: Выделить всё

a = b * (b + с + 1/2)
то есть использовать вместо 2 умножений всего одно, а от деления избавиться совсем (1/2 это константа и будет вычислена на этапе компиляции).

если же переменная b описана как volatile, то компилятор будет обязан каждый раз при обращении к ней считывать из ячейки ее значение, т.е. никакой оптимизации и близко не будет! более того, если вместо b мы захотим использовать ADC (а эта переменная определена как volatile и на самом деле является обращением к регистрам АЦП), то результат вычисления будет вообще непредсказуем! а для аналогичного обращения к UDR (тоже самое - volatile) результат ВСЕГДА (почти) БУДЕТ РАВНЫМ НУЛЮ!!!

поэтому нельзя относиться ко всем переменным, как к volatile

программист сам решает, что компилятор должен делать, а что нет, не полагаясь на "интеллект" компилятора. это обычный подход для С-программ: компилятор исполняет, а программист думает. не наоборот.
Последний раз редактировалось ARV Вт янв 05, 2010 12:00:28, всего редактировалось 1 раз.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18851
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

minime писал(а):Растолкуй тогда нафигаж он постоянно тестит регистр R25 если ДВАЖДЫ видит что он не может НУ НИ КАК изменится - он ведь сам АСМ код накалялкал.

Из твоего логического объяснения позиции компилятора ему бы не стоило тестить R25 бесконечно.
я не знаю :) главное, что результат получается верный: выхода не будет из функции. а неоптимальности на уровне ассемблерного кода могут быть: все-таки AVR-GCC делается на энтузиазме :) и глюки там есть - никто и не спорит. но правильное поведение программы не есть глюк, хотя с точки зрения супероптимальности можно было бы стать в вечный цикл сразу в точке вызова этой функции и выкинуть весь код "ниже" :)

кстати, моя версия WinAVR именно так и поступает в большинстве случаев: если есть тупость в проге, то кроме этой тупости в проге не остается ничего :) хотел ступить - тупи на здоровье, вот как он поступает! а в ассемблерный код я смотрю очень редко :)
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Реклама
Открыл глаза
Сообщения: 42
Зарегистрирован: Пн янв 04, 2010 16:06:47

Сообщение minime »

ARV писал(а):компилятор исполняет, а программист думает. не наоборот.
А тут то как раз WinAVR думать взялся и расуждать.

Програмист написал дословно:

Пока результат вычисления выражения содержащего переменную ( о том что она переменная прогер известил компилер при ее объявлении ) есть истина ...

Компилятор вдруг стал думать и подумав ВДРУГ решил ПОЧЕМУ-ТО !
Что результат вычисления выражения содержащего ПЕРЕМЕННУЮ неизменен.

Почему ? мне например не понятно.

Но ДУМАЯ еще дальше компилятор стал ТУПО тестить зачем-то регистр.

=========

Вот cvavr как раз абсолютно не думает, а выполняет то что написано программистом.
Только он не лучше чем WinAVR - я просто его упомянул всуе. :lol:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18851
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

minime писал(а):Только он не лучше чем WinAVR - я просто его упомянул всуе. :lol:
никаких холиваров :))) я не утверждаю, что CVAVR хуже или WinAVR лучше (или наоборот) - мне до ламопчки это, как и то, кто чем будет пользоваться. я смотрю шире: лучше привыкать к тому, что де-факто (или тем более реально) стандартно, чем к тому, что отступает от стандарта. не дай бог придется переучиваться - вот тогда-то мы и похохочем :)))

тем более что все стандартные фичи CVAVR поддерживает нормально, просто зачем-то еще и свои собственные применяет... потому я бы советовал всем всегда придерживаться стандартных подходов, в частности того, что такое переменная и с чем ее едят. стандарт суров, но это стандарт :)))
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18851
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

minime писал(а):а ты в начале "стандарт ни при чем"
со мной этот номер не пройдет - где я говорил, что стандарт ни при чем? ;)

думаю, дискуссия закончена, все полезное сказано, остальное - лишнее.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18851
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

ок, продолжаем разговор :)

стандарт говорит, что volatile переменная должна быть считана из памяти всякий раз, когда она встретится в тексте программы. в общем, ничего более. про все прочие переменные стандарт этого не говорит.

то есть как раз та ситуация, что я и говорил: ничего особенного. :)))

кстати, вот абсолютно правильный способ сброса принятого в UART байта без использования его значения:как раз благодаря тому, что UDR есть volatile :) попробуйте написать такое для не-volatile переменной :))) минимум - получите warning
P.S. CVAVR хавает и без warning :(
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
ARV
Ум, честь и совесть. И скромность.
Аватара пользователя
Сообщения: 18851
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск

Сообщение ARV »

minime писал(а):У него хорший аппетит. И память хорошо кушает :lol:
самое главное, что хавает-то он ПРАВИЛЬНО, т.е. для обычной переменной ничего не делает, а для UDR - делает считывание... а вот WinAVR для обычной выдает warning: дескать, бессмысленное действие (и тоже, естественно, ничего не делает) :)
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
Контактная информация:
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»