Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Размещение "изменчивой" переменной в регистре
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > AVR
Snaky
Как добиться, чтобы WinAVR разместил переменную в регистре и не оптимизировал ее?
Т.е. есть маленькая программа, хотелось одну переменную поместить в регистр для ускорения доступа, причем изменение переменной возможно в прерывании. Если бы писал на асме, то просто разместил бы переменную в регистре по своему усмотрению и использовал бы всегда значение этого регистра, однако с WinAVR такой трюк не прошел sad.gif .

Поясню:
1) Пишу: volatile unsigned char Timer;
Результат: переменная размещена в ОЗУ (@ 0x0078), считывается каждый раз. Все логично.
Дизасм:
if (Timer > max) max = Timer; // внутри цикла
Код
    LDS     R24,0x0078      // в цикле
    CP      R25,R24         // в цикле
    BRCC    PC+0x03         // в цикле
    LDS     R25,0x0078      // в цикле

2) Пишу: register unsigned char Timer asm("r15");
Результат: переменная размещена в регистре R15, но считывание оптимизировано. Тоже логично - компилятор "не знает", что она может менятся в прерывании.
Дизасм:
if (Timer > max) max = Timer; // внутри цикла
Код
   MOV     R19,R15      // за циклом
   ...
   CP      R25,R19      // в цикле
   BRCC    PC+0x02      // в цикле
   MOV     R25,R19      // в цикле
   ...

3) Пишу оба модификатора: volatile register unsigned char Timer asm("r15");
Результат: практически тот же, что и в п.2. Компилятор "частично помнит" что переменная volatile и при копировании использует значение регистра R15, однако при сравнении в цикле все равно использует копию R15 в R19. В результате программа не работает как ожидалось.
Дизасм:
if (Timer > max) max = Timer; // внутри цикла
Код
   MOV     R19,R15      // за циклом
   ...
   CP      R25,R19      // в цикле
   BRCC    PC+0x02      // в цикле
   MOV     R25,R15      // в цикле
   ...

В результате пришлось использовать вариант 1. Расходы на чтение/запись ОЗУ в принципе небольшие - 1 команда LDS. Однако в цикле уже ощутимо, да и все же хочется иметь возможность "управлять" действиями компилятора.
makc
Посмотрите на опцию gcc -ffixed-reg:
Цитата
-ffixed-reg

Treat the register named reg as a fixed register; generated code should never refer to it (except perhaps as a stack pointer, frame pointer or in some other fixed role).

reg must be the name of a register. The register names accepted are machine-specific and are defined in the REGISTER_NAMES macro in the machine description macro file.

This flag does not have a negative form, because it specifies a three-way choice.
mdmitry
Если в прерывании не используется более ничего, кроме указанной переменной, то прерывание описать как
__attribute__ (naked),
и руками сохранить sreg для корректности.
Snaky
Цитата(mdmitry @ May 21 2007, 16:19) *
Если в прерывании не используется более ничего, кроме указанной переменной, то прерывание описать как
__attribute__ (naked),
и руками сохранить sreg для корректности.

Переменная оптимизируется в основной программе. При этом становится уже неважно как она меняется в прерывании. Конкретно в моем случае в прерывании как раз все в порядке.
mdmitry
Посмотрел свой код и в нем объявленные переменные по варианту 3 работают как надо. Используются несколько переменных в обработчике прерывания. Делалось это с Вашей целью: недопустить обращения к временным регистрам в прерывании для ускорения работы. В прерывании счетчик, маленький расчет и кое-какие присваивания.
Snaky
Цитата(mdmitry @ May 22 2007, 16:22) *
Посмотрел свой код и в нем объявленные переменные по варианту 3 работают как надо. Используются несколько переменных в обработчике прерывания. Делалось это с Вашей целью: недопустить обращения к временным регистрам в прерывании для ускорения работы. В прерывании счетчик, маленький расчет и кое-какие присваивания.

Я вроде понятно объяснил, но вижу что вы меня не поняли (или я недопонимаю). Повторюсь:
переменная которую я привязываю к регистру R15 в обработчике прерывания изменяется правильно (меняется содержимое R15), однако код основной программы (ака функция main()) обращается с ней не как с volatile переменной - т.е. копирует ее в другой регистр (R19) и оперирует с ним в цикле, несмотря на то, что в любой момент времени R15 может измениться в прерывании.
Цитата
Посмотрите на опцию gcc -ffixed-reg

Посмотрел. Из описания я понял что это средство для фиксации регистров для определенных целей (как например в winavr R1 == перманентный ноль), и недопущение их использования не по назначению. Имхо, это немного не то. По крайней мере ключики -ffixed-reg и -ffixed-reg-r15 не помогли 05.gif .
dxp
Цитата(Snaky @ May 23 2007, 09:43) *
Посмотрел. Из описания я понял что это средство для фиксации регистров для определенных целей (как например в winavr R1 == перманентный ноль), и недопущение их использования не по назначению.

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

Второй момент - при использовании этих ключей надо и стандартную библиотеку перекомпилировать с этой же опцией. Если этого не сделать, то в случае, если компилятор вызывает функцию из библиотеки, то эта функция, будучи скомпилирована без залочки регистров, может использовать эти регистры и тем самым нарушить целостность работы программы.
WHALE
Запретите в main во время работы с вашей переменной прерывание,где она может быть изменена.
Snaky
Цитата(WHALE @ May 23 2007, 11:55) *
Запретите в main во время работы с вашей переменной прерывание,где она может быть изменена.

Я даже не знаю что ответить wacko.gif
Давайте в третий раз попробую. Переменную я помечаю модификатором volatile потому что ее значение может измениться в обработчике прерываний, и программа должна это видеть. Для этого компилятору запрещено оптимизировать volatile переменные и положенно при любом обращении к переменной считывать заново ее значение. Проблема в том, что компилятор генерирует такой код, где в некоторых местах основной программы используется копия значения переменной, что неприемлемо и нарушает алгоритм работы программы.
Более того, мне непонятно зачем он это вообще делает, если оптимизацией тут даже и не пахнет: копирует один регистр в другой и использует копию теми же командами работы с регистрами.
forever failure
{
volatile register unsigned char eol_trans_uart asm ("r15");
if (PORTC & BIT (MODBUS_ENABLE_TRANS_BIT))
return;
/* ... */

}
avr-gcc --std=gnu99 -c -g -Os -ffixed-r15 -Wall -D_DEBUG -Wstrict-prototypes -mmcu=atmega8 main.c -o main.o
io.h: In function `main':
io.h:21: warning: volatile register variables don't work as you might wish

Может об этом и предупреждение ?
Snaky
Цитата(forever failure @ May 23 2007, 13:01) *
io.h:21: warning: volatile register variables don't work as you might wish
Может об этом и предупреждение ?

Очень похоже что именно об этом. Странно что у меня Build succeeded with 0 Warnings... (WinAVR 20070122).
aesok
Цитата(Snaky @ May 23 2007, 11:12) *
Очень похоже что именно об этом. Странно что у меня Build succeeded with 0 Warnings... (WinAVR 20070122).


Скорее всего причина в том что нет ключа "-Wall" в коммандной строке компилятора.

Анатолий.
Snaky
Цитата(aesok @ May 23 2007, 14:11) *
Скорее всего причина в том что нет ключа "-Wall" в коммандной строке компилятора.

Есть.
ReAl
Цитата(Snaky @ May 23 2007, 11:20) *
Есть.

Попробуй к нему добавить ключ -Wextra
aesok
Цитата(Snaky @ May 23 2007, 12:20) *
Есть.


Почитайте про volatile <<global>> register здесь:
http://gcc.gnu.org/ml/gcc-patches/2005-11/msg00628.html

Вывод что volatile <<global>> register в GCC не работает. Почему у вас нет варинга не знаю.

Анатолий.
mdmitry
>avr-gcc --std=gnu99 -c -g -Os -ffixed-r15 -Wall -D_DEBUG -Wstrict-prototypes -mmcu=atmega8 main.c -o >main.o
У Вас оптимизация по объему кода -Os.
В моем случае была оптимизация по скорости -O2. Возможно это влияет на результат. Попробуйте поменять режим оптимизации.
В дополнение к замечаниям по регистрам: регистры r2-r15 отданы пользователю в WinAVR, о чем есть в справке по библиотеке замечание, поэтому перекомпилировать библиотеку нет необходимости.
Snaky
Цитата(ReAl @ May 23 2007, 14:27) *
Попробуй к нему добавить ключ -Wextra

C ключом -Wextra 1 warning: 'register' is not at beginning of declaration
Поменял местами register и volatile: register volatile unsigned char Timer asm("r15");
Результат: Build succeeded with 0 Warnings... Генерируемый код от перестановки, как и следовало ожидать, не изменился.
singlskv
Цитата(Snaky @ May 23 2007, 12:32) *
C ключом -Wextra 1 warning: 'register' is not at beginning of declaration
Поменял местами register и volatile: register volatile unsigned char Timer asm("r15");
Результат: Build succeeded with 0 Warnings... Генерируемый код от перестановки, как и следовало ожидать, не изменился.

Попробуйте убрать volatile из определения Timer
используйте -ffixed
ИМХО, регистры лучше выбирать снизу (исключая r0,r1), то есть например asm("r2")

А вобще, приведите минимальный "рабочий" код в котором будет ошибка, будет проще
разобраться
aesok
Цитата(mdmitry @ May 23 2007, 12:32) *
В дополнение к замечаниям по регистрам: регистры r2-r15 отданы пользователю в WinAVR, о чем есть в справке по библиотеке замечание, поэтому перекомпилировать библиотеку нет необходимости.


В avr-libc-user-manual а FAQ#13: "8.3.14 What registers are used by the C compiler?" описано как GCC использует регистры. Регистры которые можно безопастно использовать для регистровых переменных это r2..r7.

Анатолий.
Snaky
Цитата(mdmitry @ May 23 2007, 14:32) *
У Вас оптимизация по объему кода -Os.
В моем случае была оптимизация по скорости -O2. Возможно это влияет на результат. Попробуйте поменять режим оптимизации.

Не повлияло.

Цитата(singlskv @ May 23 2007, 14:47) *
Попробуйте убрать volatile из определения Timer
используйте -ffixed
ИМХО, регистры лучше выбирать снизу (исключая r0,r1), то есть например asm("r2")
А вобще, приведите минимальный "рабочий" код в котором будет ошибка, будет проще
разобраться

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

Цитата(aesok @ May 23 2007, 14:50) *
В avr-libc-user-manual а FAQ#13: "8.3.14 What registers are used by the C compiler?" описано как GCC использует регистры. Регистры которые можно безопастно использовать для регистровых переменных это r2..r7.

R2..R17
mdmitry
>R2..R17
Из документации по Winavr:
AVR Libc 1.4.5
FAQ
3. How to permanently bind a variable to a register?

This can be done with

register unsigned char counter asm("r3");

Typically, it should be possible to use r2 through r15 that way.


Использую сейчас r2-r11.
Посмотрел свой код и обнаружил следующее:
1. Если в основной программе элементарные действия (инкремент) с регистровыми данными (вариант 3 описания), то идет прямое использование регистров.
2. Действия более сложные (инкремент и работа с массивами), то начинает использоваться копирование в рабочие регистры WINAVR.

IMHO: компилятор в зависимости от кода сам решает вопрос о использовании рабочих регистров и копировании данных. В моем коде без копирования все же не обойтись sad.gif
singlskv
Цитата(Snaky @ May 23 2007, 13:17) *
насчет порядка использования регистров - не согласен. где это сказано, что надо использовать снизу попорядку, а иначе будет неверный код? хотя все равно проверил - также копируется в R19.
Сейчас выдерну из проекта рабочий кусочек - выложу.
R2..R17

А вот потому что:
• Function call conventions:
Arguments - allocated left to right, r25 to r8. All arguments are aligned to start in
even-numbered registers (odd-sized arguments, including char, have one free
register above them). This allows making better use of the movw instruction on
the enhanced core.

Это из того же 8.3.14
Snaky
Выкладываю минимум где наблюдается. В коде помечено комментарием.
Параметры вызова видны в makefile.
aesok
Цитата(Snaky @ May 23 2007, 13:45) *
Выкладываю минимум где наблюдается. В коде помечено комментарием.
Параметры вызова видны в makefile.


Я же вам уже написал - GCC не поддерживает volatile register перемнные. Вы хотите доказать очевидный факт?

Вот почему у вас нет варинга это да, странно.

Анатолий.
Snaky
Цитата(aesok @ May 23 2007, 16:02) *
Я же вам уже написал - GCC не поддерживает volatile register перемнные. Вы хотите доказать очевидный факт?
Вот почему у вас нет варинга это да, странно.

Да нет, не пытаюсь доказать, просто выкладываю по просьбе.
aesok
Цитата(aesok @ May 23 2007, 14:02) *
Вот почему у вас нет варинга это да, странно.


Посмотрел в WinAVR-20060421.

demo.c
Код
register volatile unsigned char Timer asm("r15");


C:\work\avr>avr-gcc -Wall demo.c
demo.c:1: warning: volatile register variables don't work as you might wish

Кто нибудь проверте в WinAVR-2007.. (GCC 4.1.x)

Анатолий.
singlskv
Цитата(Snaky @ May 23 2007, 14:06) *
Да нет, не пытаюсь доказать, просто выкладываю по просьбе.


1. зачем
unsigned char tmp1, max;
сделаны глобальными ???
при переносе их в main скомпилированный код уменьшается с 296 до 270 байт

2.циклы в прерывании не есть хорошо

3. убрал volatile, warning исчез
вот код:
Код
78:           while(!Flag.Tick);
+0000006E:   91800060    LDS     R24,0x0060       Load direct from data space
+00000070:   FF80        SBRS    R24,0            Skip if bit in register set
+00000071:   CFFC        RJMP    PC-0x0003        Relative jump
79:           Flag.Tick = FALSE;
+00000072:   91800060    LDS     R24,0x0060       Load direct from data space
+00000074:   7F8E        ANDI    R24,0xFE         Logical AND with immediate
+00000075:   93800060    STS     0x0060,R24       Store direct to data space
80:           if (Timer > max) max = Timer;   // <-- LOOK HERE!
+00000077:   152F        CP      R18,R15          Compare
+00000078:   F408        BRCC    PC+0x02          Branch if carry cleared
+00000079:   2D2F        MOV     R18,R15          Copy register
81:           tmp1--;
+0000007A:   5091        SUBI    R25,0x01         Subtract immediate
82:         }
+0000007B:   F791        BRNE    PC-0x0D          Branch if not equal

с R15 вроде как все в порядке

P.S. Одно маленькое уточнение, версия компилятора у меня 20060421
Его Вам и порекомендую, особенно учитывая что проц у вас Tiny13

P.S.S Еще одно маленькое уточнение/вопрос

А нужен ли вобще volatile, в том случае, если переменная регистровая и мы
убедились в том что она дейсвительно разместилась в регистре (-ffixed) ???
singlskv
Да, и еще одно небольшое добавление
Если не хотите переползать на предыдущию версию
можете попробовать сделать еще так:

register unsigned char Timer asm("r15"); // без volatile
........
if ((volatile unsigned char)Timer > max) max = (volatile unsigned char)Timer; // <-- LOOK HERE!

Может быть поможет...
Snaky
Цитата(singlskv @ May 23 2007, 16:40) *
1. зачем
unsigned char tmp1, max;
сделаны глобальными ???

3. убрал volatile, warning исчез
с R15 вроде как все в порядке

1. Я же кусочек выдрал из всего проекта (повыкидывал другие функции) и не стал его вылизывать, переменные локализовывать.
3. Да и не было у меня warning-а изначально, про что сейчас речь и идет. И с R15 не в порядке было при любом объявлении.
Цитата
А нужен ли вобще volatile, в том случае, если переменная регистровая и мы
убедились в том что она дейсвительно разместилась в регистре (-ffixed) ???

На этот вопрос я отвечал уже выше.

Ладно, думаю тема исчерпана. Для себя сделал выводы, что лучше так не делать ибо gcc это не приветствует 05.gif , хотя, гад, и предупреждать отказывается. Ну и, естественно, новое не всегда лучше старого.
mdmitry
>хотя, гад, и предупреждать отказывается. Ну и, естественно, новое не всегда лучше старого.
При опции -Wall включены не все предупреждения, в частности, упомянутое ранее. Это описано в документации к gcc.
Ваш тест запустил под WINAVR-2007. Действительно есть копирование. Предупреждения нет, так как в опциях компилятора не указано его генерировать. В своем проекте на пробу включил smile.gif предупреждения, так сразу 5 штук получил crying.gif .
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.