Sergey_Aleksandrovi4 0 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба Добрый день. С 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 бит оборачиваете в критические секции PS Возможно и не в этом дело. Попытался обострил проблему, включил таймер с интервалом 1 мкс и в прерывании (наивысший приоритет в XMEGA) вручную порчу SREG = 0xFF & ~CPU_I_bp; однако на частоту ошибки это не повлияло. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 119 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба 41 минуту назад, Sergey_Aleksandrovi4 сказал: Посмотрел ассемблерный листинг, AtmelStudio (GCC) в прологе/эпилоге обработчика прерывания не сохраняет SREG. Покажите, как вы описали обработчик. У меня все сохраняется. Покажите сам обработчик - может у вас там только асм-вставка с некорректным описанием и компилятор не знает, что эта вставка портит SREG. 41 минуту назад, Sergey_Aleksandrovi4 сказал: К тем, кто пользуется GCC. Есть какой-то общий шаблон для написания обработчиков прерываний? Может ключевые слова какие, настройки компилятора и т.п. Или пишете вручную ISR(TIMER0_OVF_vect) { //тут код } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sergey_Aleksandrovi4 0 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба Спасибо, что ткнули носом, действительно, компилятор следит за сохранением 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 "Эффект утёнка", пока не спросишь, не увидишь очевидной вещи. Сейчас все остальные (штатные) обработчики проверю. На всякий случай. Неделю с этой бедой воюю, не знаю на что ещё грешить. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 119 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба 2 часа назад, Sergey_Aleksandrovi4 сказал: Неделю с этой бедой воюю, не знаю на что ещё грешить. Есть какие-то 16 или 32-битные переменные, которые изменяются прерываниях, а читаются в основном цикле? Если да, то обращение к ним в основном цикле должно происходить атомарно, т.е. при запрещенных прерываниях. В противном случае возможна ситуация, когда прерывание меняет переменную между чтениями ее байтов в основном цикле. Или наоборот - прерывание может обращаться к переменной между записями разных ее байтов в основном цикле. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sergey_Aleksandrovi4 0 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба Всё намного сложнее: та 16-битная переменная локальная, ни в прерываниях, ни где-либо ещё она не используется. А прописные истины с атомарностью доступа к переменным разрядностью выше нативной я знаю, не первый год на кнопки жму :) В любом случае спасибо Вам за советы. По теме. Проверил все 10 обработчиков прерываний в своём проекте, каждый из них сохраняет содержимое SREG в прологе. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
NStorm 0 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба В avr-gcc если прерыванию не задать атрибут ISR_NAKED, то SREG точно должен сохраняться: https://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html А какого типа у вас cnt_16_bit? Точно локальная? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sergey_Aleksandrovi4 0 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба Помню в IAR подобный атрибут тоже был. Много лет назад тему здесь даже создавал с вопросом, когда в прерывании SPI из-за пролога не успевал предварительно подготовленную порцию данных загрузить. Переменная передаётся в качестве аргумента функции (хм, можно ли называть её "локальной"). Объявлена как uint16_t cnt_16_bit. В ассемблерном коде представлена регистровой парой R22:R23. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Baser 5 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба 7 часов назад, Sergey_Aleksandrovi4 сказал: В проекте в цикле используется 16-битный счётчик значения которого периодически портятся. Ошибка "плавающая", может вылезти через день-два непрерывного прогона. Пока грешу на прерывания. Другая самая распространенная возможная ошибка - выход за границы массива при косвенной адресации. Посмотрите в *.map файле линковщика, какие переменные он расположил до и после вашего счетчика. И проверьте работу с этими переменными. Все ли корректно. Пору раз по этим граблям проходил. Причем причина может быть даже и не в соседних переменных, просто они - самый вероятный случай. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
NStorm 0 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба 47 минут назад, Sergey_Aleksandrovi4 сказал: Переменная передаётся в качестве аргумента функции (хм, можно ли называть её "локальной"). Объявлена как uint16_t cnt_16_bit. А в вызове функции передается что? Какая-то другая переменная? Не может ли эта другая переменная меняться в прерывании? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sergey_Aleksandrovi4 0 1 июля, 2020 Опубликовано 1 июля, 2020 · Жалоба NStorm, аргумент в функция передаётся по значению: явно задано число. Т.е. две команды LDI и следом сразу вызов этой функции. Baser, нет переменной в map-файле. Она фактически существует только на регистрах во время работы функции, ОЗУ под неё не выделяется. Там вообще всё настолько примитивно, что даже смешно. Либо заблудился в 3-х соснах (что в последнее время частенько), либо на какую-то аппаратную аномалию наткнулся. В общем поставил запрет прерываний на функцию целиком и запустил тест. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
NStorm 0 2 июля, 2020 Опубликовано 2 июля, 2020 · Жалоба 11 часов назад, Sergey_Aleksandrovi4 сказал: аргумент в функция передаётся по значению: явно задано число. Т.е. две команды LDI и следом сразу вызов этой функции. Очень странно тогда. Тогда проблема наверное в другом месте. И может не в "порче" переменной дело вовсе. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Baser 5 2 июля, 2020 Опубликовано 2 июля, 2020 · Жалоба 17 часов назад, Sergey_Aleksandrovi4 сказал: та 16-битная переменная локальная Если это локальная переменная, то значит вы выявляете ошибку не напрямую, а уже впоследствии, по результатам неправильной работы функции. В начале топика вы дали данные только исходя из вашей первой версии ошибки (порча в прерывании), поэтому из них ничего не понятно, как ваша функция работает. А ошибка в косвенной адресации прекрасно может портить и локальные данные в стеке. Для проверки можете сделать этот счетчик статическим или глобальным и запустить тест. Если все равно будет портиться, то дело не в этом. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Sergey_Aleksandrovi4 0 2 июля, 2020 Опубликовано 2 июля, 2020 · Жалоба 10 hours ago, NStorm said: Очень странно тогда. Тогда проблема наверное в другом месте. И может не в "порче" переменной дело вовсе. Вы оказались правы, никакой порчи переменной не было. Суточный прогон с отключенными прерываниями это подтвердил. Я неверно истолковал свои отладочные данные. DMA всему виной, но пока не понял где и почему он "погибает". Спасибо всем вам за содействие, дальше я сам. А то сильно ушли от изначальной темы с SREG. Удачи, поменьше багов в коде ;) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться