Перейти к содержанию
    

AVR GCC: SREG в прерываниях

Добрый день. С AVR в последний раз работал 8 лет назад, успел всё позабыть. Надеюсь на форуме остались инженеры кто с этими процессорами работает.

Проблема: В проекте в цикле используется 16-битный счётчик значения которого периодически портятся. Ошибка "плавающая", может вылезти через день-два непрерывного прогона. Пока грешу на прерывания.

while (cnt_16_bit > 1)
{
	//...do something...
	cnt_16_bit--;
}

   334: 			cnt_16_bit--;
0000AE87  SUBI R22,0x01		Subtract immediate 
0000AE88  SBC R23,R1		Subtract with carry 
   326: 		while (cnt_16_bit > 1)
0000AE89  CPI R22,0x01		Compare with immediate 
0000AE8A  CPC R23,R1		Compare with carry 

Т.е. если прерывание возникнет между двух команд работы с 16-битной переменной и повлияет на флаг C, результат операции окажется неверным. Посмотрел ассемблерный листинг, AtmelStudio (GCC) в прологе/эпилоге обработчика прерывания не сохраняет SREG. При этом процессор сам не умеет на аппаратном уровне этого делать, данная процедура отдана на откуп программисту.

Quote

The status register (SREG) is not saved automatically upon an interrupt request.

Посмотрел свои старые исходники 8-10 летней давности. Проекты на ассемблере: в прологе-эпилоге обработчика прерывания всегда явно сохраняется SREG. Посмотрел проекты на Си в IAR, там явного сохранения SREG нет, но вроде бы помню, что IAR в прологе/эпилоге делал сохранение регистра.

Вопросы:

1. К тем, кто пользуется IAR, посмотрите пожалуйста, сохраняется или нет SREG в прерываниях? 

2. К тем, кто пользуется GCC. Есть какой-то общий шаблон для написания обработчиков прерываний? Может ключевые слова какие, настройки компилятора и т.п. Или пишете вручную

flag_t sreg = SREG;

//....

SREG = sreg;

2.1. Или все операции с переменными разрядностью больше нативной 8 бит оборачиваете в критические секции :crazy:

PS Возможно и не в этом дело. Попытался обострил проблему, включил таймер с интервалом 1 мкс и в прерывании (наивысший приоритет в XMEGA) вручную порчу SREG = 0xFF & ~CPU_I_bp; однако на частоту ошибки это не повлияло.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

41 минуту назад, Sergey_Aleksandrovi4 сказал:

Посмотрел ассемблерный листинг, AtmelStudio (GCC) в прологе/эпилоге обработчика прерывания не сохраняет SREG.

Покажите, как вы описали обработчик. У меня все сохраняется. Покажите сам обработчик - может у вас там только асм-вставка с некорректным описанием и компилятор не знает, что эта вставка портит SREG.

41 минуту назад, Sergey_Aleksandrovi4 сказал:

К тем, кто пользуется GCC. Есть какой-то общий шаблон для написания обработчиков прерываний? Может ключевые слова какие, настройки компилятора и т.п. Или пишете вручную

ISR(TIMER0_OVF_vect)
{
   //тут код
}

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо, что ткнули носом, действительно, компилятор следит за сохранением SREG (поэтому мой стрессовый тест и не усугубил проблему). Искал в ассемблерном листинге мнемонику "SREG", а там "голый" адрес регистра 0x003F...

ISR(TCE0_OVF_vect)
{
	asm("nop");			//  <--- Set breakpoint here
		
	TEST_PIN_HIGH();
	SREG = 0xFF & ~CPU_I_bp;
	TEST_PIN_LOW();
}
Quote

  289: {
0000CBEC  PUSH R1        Push register on stack 
0000CBED  PUSH R0        Push register on stack 
0000CBEE  IN R0,0x3F        In from I/O location 
0000CBEF  PUSH R0        Push register on stack 

0000CBF0  CLR R1        Clear Register 
0000CBF1  IN R0,0x3B        In from I/O location 
0000CBF2  PUSH R0        Push register on stack 
0000CBF3  PUSH R24        Push register on stack 
0000CBF4  PUSH R25        Push register on stack 
0000CBF5  PUSH R30        Push register on stack 
0000CBF6  PUSH R31        Push register on stack 
   290:     asm("nop");            //  <--- Set breakpoint here
0000CBF7  NOP         No operation 
   292:     TEST_PIN_HIGH();
0000CBF8  LDI R30,0xE0        Load immediate 
0000CBF9  LDI R31,0x07        Load immediate 
0000CBFA  LDI R24,0x02        Load immediate 
0000CBFB  STD Z+5,R24        Store indirect with displacement 
   293:     SREG = 0xFF & ~CPU_I_bp;
0000CBFC  LDI R25,0xF8        Load immediate 
0000CBFD  OUT 0x3F,R25        Out to I/O location 
   294:     TEST_PIN_LOW();
0000CBFE  STD Z+6,R24        Store indirect with displacement 
   295: }
0000CBFF  POP R31        Pop register from stack 
0000CC00  POP R30        Pop register from stack 
0000CC01  POP R25        Pop register from stack 
0000CC02  POP R24        Pop register from stack 
0000CC03  POP R0        Pop register from stack 
0000CC04  OUT 0x3B,R0        Out to I/O location 
0000CC05  POP R0        Pop register from stack 
0000CC06  OUT 0x3F,R0        Out to I/O location 

0000CC07  POP R0        Pop register from stack 
0000CC08  POP R1        Pop register from stack 
0000CC09  RETI         Interrupt return 

"Эффект утёнка", пока не спросишь, не увидишь очевидной вещи. Сейчас все остальные (штатные) обработчики проверю. На всякий случай. Неделю с этой бедой воюю, не знаю на что ещё грешить.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, Sergey_Aleksandrovi4 сказал:

Неделю с этой бедой воюю, не знаю на что ещё грешить.

Есть какие-то 16 или 32-битные переменные, которые изменяются прерываниях, а читаются в основном цикле? Если да, то обращение к ним в основном цикле должно происходить атомарно, т.е. при запрещенных прерываниях. В противном случае возможна ситуация, когда прерывание меняет переменную между чтениями ее байтов в основном цикле. Или наоборот - прерывание может обращаться к переменной между записями разных ее байтов в основном цикле.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

По теме. Проверил все 10 обработчиков прерываний в своём проекте, каждый из них сохраняет содержимое SREG в прологе.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В avr-gcc если прерыванию не задать атрибут ISR_NAKED, то SREG точно должен сохраняться: https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

А какого типа у вас cnt_16_bit? Точно локальная?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Помню в IAR подобный атрибут тоже был. Много лет назад тему здесь даже создавал с вопросом, когда в прерывании SPI из-за пролога не успевал предварительно подготовленную порцию данных загрузить.

Переменная передаётся в качестве аргумента функции (хм, можно ли называть её "локальной"). Объявлена как uint16_t cnt_16_bit. В ассемблерном коде представлена регистровой парой R22:R23.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 часов назад, Sergey_Aleksandrovi4 сказал:

В проекте в цикле используется 16-битный счётчик значения которого периодически портятся. Ошибка "плавающая", может вылезти через день-два непрерывного прогона. Пока грешу на прерывания.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

47 минут назад, Sergey_Aleksandrovi4 сказал:

Переменная передаётся в качестве аргумента функции (хм, можно ли называть её "локальной"). Объявлена как uint16_t cnt_16_bit.

А в вызове функции передается что? Какая-то другая переменная? Не может ли эта другая переменная меняться в прерывании?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

NStorm, аргумент в функция передаётся по значению: явно задано число. Т.е. две команды LDI и следом сразу вызов этой функции.

Baser, нет переменной в map-файле. Она фактически существует только на регистрах во время работы функции, ОЗУ под неё не выделяется.

Там вообще всё настолько примитивно, что даже смешно. Либо заблудился в 3-х соснах (что в последнее время частенько), либо на какую-то аппаратную аномалию наткнулся. В общем поставил запрет прерываний на функцию целиком и запустил тест.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 часов назад, Sergey_Aleksandrovi4 сказал:

аргумент в функция передаётся по значению: явно задано число. Т.е. две команды LDI и следом сразу вызов этой функции.

Очень странно тогда. Тогда проблема наверное в другом месте. И может не в "порче" переменной дело вовсе.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

17 часов назад, Sergey_Aleksandrovi4 сказал:

та 16-битная переменная локальная

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

А ошибка в косвенной адресации прекрасно может портить и локальные данные в стеке.
Для проверки можете сделать этот счетчик статическим или глобальным и запустить тест.
Если все равно будет портиться, то дело не в этом.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 hours ago, NStorm said:

Очень странно тогда. Тогда проблема наверное в другом месте. И может не в "порче" переменной дело вовсе.

Вы оказались правы, никакой порчи переменной не было. Суточный прогон с отключенными прерываниями это подтвердил. Я неверно истолковал свои отладочные данные. DMA всему виной, но пока не понял где и почему он "погибает". Спасибо всем вам за содействие, дальше я сам. А то сильно ушли от изначальной темы с SREG. Удачи, поменьше багов в коде ;)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...