Jump to content
    

Сбои при многопоточном обращении к Flash

Четверть века назад я столкнулся с проблемой, которая выражалась в том, что через несколько минут нормальной работы, портится последний байт, передаваемый по UART, всегда 0xff. Тогда я не сумел докопаться до истины. Сделал программу как-то по-другому и решил для себя, что проблему я решил. И вот недавно эта проблема мне опять выстрелила. Докопаться до сути вещей я так и не сумел. Было замечено, что проблема проявляется, когда длина передаваемых данных по обоим UART превышает некоторое значение. Когда работает только один UART, такой проблемы не возникало. Путём последовательного исключения нашёл заковырку в организации цикла

for(ct = 0; ct < (Sectocol_rom[ct_Sectocol_trans].amount * 2); src.ch++, ct++)

 

Sectocol_rom – массив во Flash. Каждый проход цикла происходило обращение ко Flash.

 

Сделал вот так. То есть один раз присваиваю счётчику значение из Flash и произвожу его декремент с проверкой на ноль.

for(ct = (Sectocol_rom[ct_Sectocol_trans].amount * 2); ct--; src.ch++)

 

И произошло чудо! Гонял несколько часов, всё хорошо.

Казалось бы, оба объявления цикла корректны. Видать, частые обращения ко Flash из разных потоков приводя к конфликтам. А каким образом этот могло повлиять на UART, я так и не понял.

 

Мораль – красивый код, красиво работает. Не пытайтесь проблему обойти, нерешённая проблема имеет свойство возвращаться через много лет.

Share this post


Link to post
Share on other sites

В правильно написанном коде ошибок не возникает.

Про циклы сказать ничего нельзя, т.к. отсутствует куча сопутствующей информации.

Share this post


Link to post
Share on other sites

47 минут назад, Дерищев сказал:

Казалось бы, оба объявления цикла корректны. Видать, частые обращения ко Flash из разных потоков приводя к конфликтам.

Нет, никакие "частые обращения к флешь" не при чём. Проблема у вас где-то в другом. Где - вы не нашли.

47 минут назад, Дерищев сказал:

А каким образом этот могло повлиять на UART, я так и не понял.

Ищите не там где светло. Под фонарём (как правило) искать бесполезно - давно известная истина.

47 минут назад, Дерищев сказал:

Не пытайтесь проблему обойти, нерешённая проблема имеет свойство возвращаться через много лет.

А вот здесь вы совершенно правы. И эта, так и не найденная проблема, к вам когда-нить снова вернётся.  :unknw:

Share this post


Link to post
Share on other sites

1 hour ago, Дерищев said:

Четверть века назад я столкнулся с проблемой, которая выражалась в том, что через несколько минут нормальной работы, портится последний байт, передаваемый по UART, всегда 0xff. Тогда я не сумел докопаться до истины. Сделал программу как-то по-другому и решил для себя, что проблему я решил. И вот недавно эта проблема мне опять выстрелила. Докопаться до сути вещей я так и не сумел. Было замечено, что проблема проявляется, когда длина передаваемых данных по обоим UART превышает некоторое значение. Когда работает только один UART, такой проблемы не возникало. Путём последовательного исключения нашёл заковырку в организации цикла

for(ct = 0; ct < (Sectocol_rom[ct_Sectocol_trans].amount * 2); src.ch++, ct++)

 

Sectocol_rom – массив во Flash. Каждый проход цикла происходило обращение ко Flash.

 

Сделал вот так. То есть один раз присваиваю счётчику значение из Flash и произвожу его декремент с проверкой на ноль.

for(ct = (Sectocol_rom[ct_Sectocol_trans].amount * 2); ct--; src.ch++)

 

И произошло чудо! Гонял несколько часов, всё хорошо.

Казалось бы, оба объявления цикла корректны. Видать, частые обращения ко Flash из разных потоков приводя к конфликтам. А каким образом этот могло повлиять на UART, я так и не понял.

 

Мораль – красивый код, красиво работает. Не пытайтесь проблему обойти, нерешённая проблема имеет свойство возвращаться через много лет.

Просто где-то наговнокодили. Делов то.

Share this post


Link to post
Share on other sites

Вероятно ваше "многопоточное" приложение где-то портит сами переменные, либо лезет в отведенную для них память. М.б. отключаете UART не вовремя.
А код в AVR (Tiny, Mega) исполняется из Flash, поэтому в случае ошибок при "частом чтении" изменение цикла ничем бы не помогло.

Share this post


Link to post
Share on other sites

17 часов назад, Дерищев сказал:

 

for(ct = 0; ct < (Sectocol_rom[ct_Sectocol_trans].amount * 2); src.ch++, ct++)

Sectocol_rom – массив во Flash. Каждый проход цикла происходило обращение ко Flash.

 

Если член структуры .amount, массив структур Sectocol_rom[] или индексная переменная ct_Sectocol_trans объявлены без квалификатора volatile, то чтение из Flash, по идее, происходит единожды перед входом в цикл. Листинг ассемблера смотрели?

Переменная ct_Sectocol_trans асинхронно по отношению к циклу где-то в коде изменяется? В прерываниях, например.

Компилятор IAR EWAVR, надо полагать?

Share this post


Link to post
Share on other sites

17 часов назад, Дерищев сказал:

Казалось бы, оба объявления цикла корректны. Видать, частые обращения ко Flash из разных потоков приводя к конфликтам. А каким образом этот могло повлиять на UART, я так и не понял.

В первом приближении, для проверки, я бы побайтно писал данные в RAM,  потом одним блоком скидывал во flash.

Share this post


Link to post
Share on other sites

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

Если член структуры .amount, массив структур Sectocol_rom[] или индексная переменная ct_Sectocol_trans объявлены без квалификатора volatile, то чтение из Flash, по идее, происходит единожды перед входом в цикл.

Не обязательно. Чтения из флешь может вообще не происходить. Если сonst-данные не имеют volatile, то оптимизирующий компилятор вправе создать копию таких данных и поместить их прямо в команду CPU (если таковая возможность имеется в системе команд CPU). А сами const-данные вообще удалить из программы, как неиспользуемые.

PS: Вообще - угадывать что там у ТС - дело неблагодарное. Всё равно никто не угадает.

Share this post


Link to post
Share on other sites

Приветствую!

Спасибо большое за поддержку! Да, действительно, проблема была не во flash. Интереса ради, массив перенёс в RAM, эффект прежний. Было установлено, что проблема во многопоточности. Использую диспетчер кооперативной многозадачности, базирующийся на механизме псевдо программного прерывания - средний уровень приоритетности между фоновой задачей и аппаратным прерыванием. Было задумано для уменьшения времени нахождения в обработчике аппаратного прерывания. В частности, в псевдо программном прерывании осуществляется вычисление CRC UART. В обработчике аппаратного прерывания производится только сохранение регистров и инициализация псевдо программного прерывания. Механизм псевдо программного прерывания базируется на аппаратном прерывании INT0. Активация производится путём установки в ноль вывода INT0 и занесением в массив указателей адреса обработчика. Если не разрешать прерывания в диспетчере псевдо программного прерывания, всё устойчиво работает, но тогда теряется смысл в этом механизме. Вывод, что сбои происходят тогда, когда из обработчика прерывания (при разрёшённом прерывании) происходит вызов другого прерывания. Режим INT0 - при нуле (когда ноль, постоянного генерируются прерывания). Вот листинг обработчика псевдо программного прерывания:

#pragma vector=SW_Int_Vector // As Soft Ware interrupt
__interrupt void SWI_mng (void)
{
unsigned char ct;
void(*ptr)(void) = NULL;

// Сохранение в стек всех регистров без исключения, в дополнение к тем, которые сохраняет транслятор

asm("ST      -Y, R24");// Сохранение в стек SREG
asm("ST      -Y, R25");// Сохранение в стек RAMPZ

asm("ST      -Y, R4");
asm("ST      -Y, R5");
asm("ST      -Y, R6");
asm("ST      -Y, R7");
asm("ST      -Y, R8");
asm("ST      -Y, R9");
asm("ST      -Y, R10");
asm("ST      -Y, R11");
asm("ST      -Y, R12");
asm("ST      -Y, R13");
asm("ST      -Y, R14");
asm("ST      -Y, R15");

for(ct = 0; ct < qty_SWI_executors; ct++)// Поиск в массиве указателей активированного запроса
    if(SWI_exec[ct])// если указатель отличен от нуля
        {
        ptr = SWI_exec[ct]; // копирование в местный указатель
        SWI_exec[ct] = NULL; // Deactivate further execution очистка указателя в массиве, чтобы не выполнялся постоянно
        SW_Int = 1; // Stop interrupt Перевод порта в единицу, чтобы не было прерываний.
        EIMSK &= ~(1<< SW_Int_Vect); // External interrupt disable as SoftWare interrupt Запрет аппаратного прерывания INT0
        GI_enable// Запрет глобального прерывания
        (*ptr)(); // Индексный вызов обработчика, косвенно, через адрес в указателе
        GI_disable // Разрешение глобального прерывания
        EIMSK |= (1<< SW_Int_Vect); // External interrupt enable as SoftWare interrupt Разрешение аппаратного прерывания INT0
        break;
        }

for(ct = 0; ct < qty_SWI_executors; ct++) // Поиск в массиве указателей необработанных запросов на псевдо программное прерывание
    if(SWI_exec[ct]) // Если был найден
        {
        SW_Int = 0; // Start interrupt установить в ноль порт INT0 для активации прерывания. После выхода из обработчика, тут же произойдёт запрос на новое прерывание.
        break;
        }

asm("LD      R15, Y+");
asm("LD      R14, Y+");
asm("LD      R13, Y+");
asm("LD      R12, Y+");
asm("LD      R11, Y+");
asm("LD      R10, Y+");
asm("LD      R9, Y+");
asm("LD      R8, Y+");
asm("LD      R7, Y+");
asm("LD      R6, Y+");
asm("LD      R5, Y+");
asm("LD      R4, Y+");

asm("LD      R25, Y+");
asm("LD      R24, Y+");
}
 

Вроде бы всё правильно, но где-то в фундаменте скрылась ошибка.

Заранее благодарен за любые идеи, даже те, которые кажутся абсурдными! 

Share this post


Link to post
Share on other sites

1 час назад, Дерищев сказал:

Вывод, что сбои происходят тогда, когда из обработчика прерывания (при разрёшённом прерывании) происходит вызов другого прерывания.

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

Share this post


Link to post
Share on other sites

8 минут назад, r_dot сказал:

Ну так сделайте одноуровневые прерывания. Настройками или тупо глобальным запретом прерываний на время INT0.
Ну подождёт другое прерывание немного. Ничего страшного. Ничего от этого не изменится, кроме скорости реакции на прерывание. В сумме-то время вычислений всё равно то же самое будет. Только в другом порядке, не вперемешку, а по очереди.

Спасибо! Так оно уже и сделано, работает. Пугает другое, что я сумел зацепиться за верхушку айсберга, а под водой скрываются фундаментальные ошибки, которые могут выстрелить опять. Пытаюсь через неработоспособность двухуровневых прерываний докопаться до предполагаемых ошибок.

Share this post


Link to post
Share on other sites

21 минуту назад, Дерищев сказал:

под водой скрываются фундаментальные ошибки

Когда пишешь на ЯВУ, да с использованием чужих библиотек, ещё не то может вылезти. :biggrin:

Когда пишу, всегда представляю, как это будет выглядеть на низком уровне. Не ленюсь и ассемблерный текст иногда посмотреть. Ну а последовательность вычислений и распределение прерываний по времени, вообще на бумажке сначала рисую. Прикинуть времена их выполнения, посмотреть, сколько и в какие моменты остаётся на основной цикл...
Рекомендую.

А многоуровневые прерывания - они для абсолютно независимых потоков применяются.
Особого смысла в них нет - общее время счёта всё равно такое же. А вот на "затыки" и нехватку производительности наступить - запросто. В коде этого ж не видно.
Типовое - пропуск прерываний. Это можно очень долго искать.

Share this post


Link to post
Share on other sites

        GI_enable// Запрет глобального прерывания
        (*ptr)(); // Индексный вызов обработчика, косвенно, через адрес в указателе
        GI_disable // Разрешение глобального прерывания

enable - запрет; disable - разрешение. Где ошибка: в тексте комментариев или в логике?

GI_enable во внешнем коде как и когда вызывается?

Share this post


Link to post
Share on other sites

50 минут назад, Sergey_Aleksandrovi4 сказал:
        GI_enable// Запрет глобального прерывания
        (*ptr)(); // Индексный вызов обработчика, косвенно, через адрес в указателе
        GI_disable // Разрешение глобального прерывания

enable - запрет; disable - разрешение. Где ошибка: в тексте комментариев или в логике?

GI_enable во внешнем коде как и когда вызывается?

Ошибка в комментарии - Разрешаем глобальное прерывание, передаём управление обработчику, запрещаем прерывание.

Share this post


Link to post
Share on other sites

У вас система довольно продолжительно время находится в состоянии запрета прерываний:

* сохранение регистров в стек прологе обработчика прерывания INT0 (пролог формируется компилятором)

* ручное сохранение регистров в стек

* перебор массива указателей

* повторный перебор массива указателей

* ручное восстановление регистров из стека

* восстановление регистров из стека в эпилоге обработчика INT0

... спустя какое-то время ...

где-то во внешнем коде вы разрешаете прерывания GI_enable

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

Т.е. своего рода критическая секция с большим и недетерминированным временем.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...