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

Добрый день.

 

Перенес проект в Eclipse на CDT с таким набором: scmRTOS+STM32L15x+OpenSTM(+CUBE).

Столкнулся с проблемой переключения контекста (после обновления Eclipse до Mars, всех плагинов и самой порты ОС):

Выполняется только задача с максимальным приоритетом, остально время висим тут:

 {...} 
       while(CurProcPriority != SchedProcPriority); // until context switch done

 

В отладчике вижу что CurProcPriority = SchedProcPriority, проблема знакомая с AVR8 и оптимизатором в IAR.

Я грешил на что оставил stdlib при переносе и откатился в далекое прошлое, когда в проекте не было sdtlib был родной (порты OC) startup и все работало, поиграть с оптимизацией, собрать yagarto'ой, но это не изменило ситуацию.

Решилось так:

 

volatile uint_fast8_t          CurProcPriority;

 

Хочется узнать почему она изначально не объявлена volatile как и SchedProcPriority?

 

 

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


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

День добрый.

 

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

Думаю, что проблема всё-таки в чём-то другом.

Обновление Eclipse и плагинов вряд ли могло повлиять. А вот обновление sdtlib (это что? SrdPeriphLib?) и особенно замена стартап-файла - повлиять могли.

Такие симптомы бывают, когда не вызывается прерывание переключателя контекста или прерывание системного таймера.

Проверьте имена обработчиков прерываний в стартап-файле. Они должны называться

PendSVC_ISR()

и

SystemTimer_ISR().

 

Кроме того, насколько я знаю, CUBE захватывает системный таймер. Как вы решили эту проблему?

 

ЗЫ. Напишите версию компилятора и scmRTOS, а также какой порт вы используете (GCC/Cortex-M3 или GCC/Cortex-Mx).

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


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

для CurProcPriority volatile не нужен, потому что она локальная для каждого процесса. Эта переменная не меняется в течении всего времени выполнения программы. Поскольку при переключении процессов контекст также переключается полностью, то и сравнение всегда присходит с CurProcPriority текущего процесса вне зависимости от того, где она размещена - в ОЗУ или регистре. Согласен с Антоном, надо смотреть по какой причине не вызывается обработчик прерывания переключения контекста.

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


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

для CurProcPriority volatile не нужен, потому что она локальная для каждого процесса. Эта переменная не меняется в течении всего времени выполнения программы. Поскольку при переключении процессов контекст также переключается полностью, то и сравнение всегда присходит с CurProcPriority текущего процесса вне зависимости от того, где она размещена - в ОЗУ или регистре. Согласен с Антоном, надо смотреть по какой причине не вызывается обработчик прерывания переключения контекста.

Погоди, CurProcPriority - это же переменная ядра. Может ты попутал с const TPriority TBaseProcess::Priority? CurProcPriority - это приоритет текущего процесса, значение этой переменной меняется при переключении контекста. Думается, Антон прав, что формально компилятор может засунуть эту переменную в регистр и там держать, не перечитывать каждый раз из памяти. Вот бы и посмотреть по листингу, что там реально. Хотя тоже склоняюсь, что причина, скорее всего, не в этом.

 

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

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


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

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

Значит так, появилась ясность в вопросе про волатильность, Сергей наставил на путь истинный. :)

 

Суть в следующем. Главная тонкость состоит в том, что (цитата Сергея):

 

enable_context_switch() происходит в одном потоке, а disable_context_switch() - уже в другом

 

void TKernel::sched()
{
    uint_fast8_t NextPrty = highest_priority(ReadyProcessMap);
    if(NextPrty != CurProcPriority)
    {
        SchedProcPriority = NextPrty;
    
        raise_context_switch();
        do
        {
            enable_context_switch();
            DUMMY_INSTR();
            disable_context_switch();
        } 
        while(CurProcPriority != SchedProcPriority); // until context switch done
    }
}

 

Переменная CurProcPriority в контексте каждого процесса фактически константа, точнее, не константа, конечно, но в условнии

 

while(CurProcPriority != SchedProcPriority);

 

происходит её чтение и с этого момента уже не важно, из памяти читать или из регистра, в данном процессе эта переменная не меняется

 

Переменная SchedProcPriority является по сути признаком (меткой, тегом) следующего процесса. Если переключение контекстов произошло, т.е. поток управления прошёл через функцию

 

OS::TKernel::context_switch_hook(stack_item_t* sp)

где перед выходом выполняется

 

CurProcPriority = SchedProcPriority;

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

 

CurProcPriority = SchedProcPriority;

это значение в регистр не заносилось. Иными словами, любой процесс, прошедший через

 

while(CurProcPriority != SchedProcPriority);

имеет в регистре это значение, которое у каждого процесса своё. Т.е. тут можно было бы вообще не CurProcPriority использовать, а просто Priority процесса, но CurProcPriority в функциях ядра технически использовать проще (члены одного класса).

 

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

 

 

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


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

Попробую еще большую ясность внести:

Перед передачей управления мы присваиваем SchedProcPriority номер того процесса, которому хотим отдать управление. И если в конце цикла SchedProcPriority стала снова равной CurProcPriority, значит управление нам вернули и мы можем выходить из этой функции.

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


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

Ага, идея понятна. Каждый процесс, засыпая, читает текущее значение CurProcPriority в какой-то регистр, потом проходит через строчку

enable_context_switch();

, потом спит, потом, просыпаясь, уже имеет в нужном регистре правильное значение CurProcPriority.

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

 

В этой логике есть изъян. Проблема в том, что если процесс ещё не засыпал, то у него в регистре нет нужного значения!

То есть, в начале работы оси, при переключении с наиболее приоритетного процесса на менее приоритетный, могут возникнуть проблемы. И, судя по описанию, возникли...

В этой связи ещё прошу автора темы дать информацию о ключах компиляции.

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


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

В этой логике есть изъян. Проблема в том, что если процесс ещё не засыпал, то у него в регистре нет нужного значения!

То есть, в начале работы оси, при переключении с наиболее приоритетного процесса на менее приоритетный, могут возникнуть проблемы. И, судя по описанию, возникли...

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

 

В этой связи ещё прошу автора темы дать информацию о ключах компиляции.

Да, хотелось бы воспроизвести ситуацию, дабы убедиться, что так всё и есть. А заодно и workaround применить и проверить более эффективный, нежели простое volatile CurProcPriority.

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


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

Да, действительно, в самом начале все процессы, кроме самого приоритетного получают управление, "выныривая" из переключателя контекста, т.е. не проходя весь код функции,
Нет. В самом начале все процессы "выныривают" в начало TProcess::exec. А уже потом, в процессе работы, попадают в sched() и выныривают из него. При старте все присходит совершенно аналогично прерыванию менее приоритетного процесса - его прерывают где угодно и после возврата управления он продолжает исполнение с той точки, где управление у него отобрали. Выходить из sched() процесс будет только в том случае, если сам добровольно отдал управление в каком-нибудь из сервисов ОС. И это бует происходить только тогда, когда процесс перед этим в sched() зашел. Так что все здесь с логикой хорошо и правильно.

 

 

Ага, идея понятна. Каждый процесс, засыпая, читает текущее значение CurProcPriority в какой-то регистр, потом проходит через строчку

enable_context_switch();

, потом спит, потом, просыпаясь, уже имеет в нужном регистре правильное значение CurProcPriority.

Я думаю, логика была бы более прозрачна, если бы каждый процесс имел собственную функцию sched(). Тогда бы было ясно видно, что программа уходит в sched() одного процесса, а выскакивает из sched() другого. А из первого вынырнет, когда первый процесс снова получит управление. У нас же код этой функции один на все процессы и неочевидно, что по ходу выполнения программы enable_context_switch() делает один процесс, а следующий за ним disable_context_switch() будет делать уже другой. Но это должно быть хорошо заметно при первых отдачах управления, когда процесс делает enable_context_switch() и после прерывания выполнение продолжается с точки входа в exec() следующего процесса. Очень легко это увидеть в программе из двух-трех процессов, состоящих только из sleep(1).

 

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

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


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

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

Вот смотри. Запускается ось, CurProcPriority = 0. Процесс 0 отработал, хочет отдать управление. Вызывает sleep(), оттуда вызывается TKernel::sched():

Там у нас вот что (см. комментарии):

{
    uint_fast8_t NextPrty = highest_priority(ReadyProcessMap);
    if(NextPrty != CurProcPriority)  /// [1] <<-Вот в этой строчке компилятор засовывает CurProcPriority в R1. Это R1 от процесса 0.
    {
        SchedProcPriority = NextPrty;
    
        raise_context_switch();
        do
        {
            enable_context_switch();  // [2]-- здесь процесс 0 засыпает.
            DUMMY_INSTR();
            disable_context_switch(); // [3]-- здесь просыпается процесс 1. Но в его R1 нет значения CurProcPriority! Он ещё не был в точке [1],
        } 
        while(CurProcPriority != SchedProcPriority); // until context switch done
    }
}

Вот так и висим. А работало до этого у нас всё потому, что компилятор, по всей видимости, не догадывался кешировать CurProcPriority в регистре.

 

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


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

{
    uint_fast8_t NextPrty = highest_priority(ReadyProcessMap);
    if(NextPrty != CurProcPriority)  /// [1] <<-Вот в этой строчке компилятор засовывает CurProcPriority в R1. Это R1 от процесса 0.
    {
        SchedProcPriority = NextPrty;
    
        raise_context_switch();
        do
        {
            enable_context_switch();  // [2]-- здесь процесс 0 засыпает.
            DUMMY_INSTR();
            disable_context_switch(); // [3]-- здесь просыпается процесс 1. Но в его R1 нет значения CurProcPriority! Он ещё не был в точке [1],
        } 
        while(CurProcPriority != SchedProcPriority); // until context switch done
    }
}

Вот так и висим. А работало до этого у нас всё потому, что компилятор, по всей видимости, не догадывался кешировать CurProcPriority в регистре.

С чего ты взял, что процесс 1 выныривает в точке [3]? Первый раз он выныривает, как заметил Сергей, в начале своей exeс.

 

 

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


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

Семён Семёныч! :)

Это я тормознул, да. Тогда возвращаюсь к своей первоначальной версии.

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


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

Семён Семёныч! :)

Это я тормознул, да. Тогда возвращаюсь к своей первоначальной версии.

Но она как-то не очень объясняет, почему квалификатор volatile в объявлении CurProcPriority порулил проблему у автора темы.

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


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

Добрый день.

 

Я проковырялся пару дней с разными версиями развития событий, хотя не первый год использовал scmRTOS на разных процах, в т.ч. и на STM32L15x (и F1xx).

Пробовал обе порты, пробовал откатиться к версии когда был родной startup и старая версия порты (проект давний и он на старых прошивках живет).

Вызов raise_context_switch() проходит, PendSVC_ISR() вызывается, контекст переключается (!), но из while(...) он выходит только в процесс с максимальным приоритетом, вот только он один исправно и живет.

SystemTimer_ISR() тоже зовется, что касается "кубовского" обработчика, то он вызывается из хука таймера (там только счетчик миллисекунд, раньше так жил DMAшный драйвер I2C в какой-то версии CPAL).

А, что интересно, "кубовская" часть живет себе исправно и scmRTOS-ные обертки для дров исправно работают - события и сообщения исправно ходят из ISR'ных callback'ов куба. В приступе паранои, я отключил все дрова и пробовал просто подергать один поток событиями из другого или еще проще поморгать светодиодами из разных потоков по sleep.

Еще думал, что может дебаг портит все - попробовал отключить его и посмотреть времянки анализатором дергая ножками из raise_context_switch(), PendSVC_ISR() и SystemTimer_ISR() - все зовется, while(...) висит)

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

 

Пока живу вторую неделю с volatile - работает.

Одно не попробовал пройтись там дизасмом и посмотреть разницу - обрадовался, что заработало)

 

ЗЫ Ac6 версии 1.4, GNU Tools 1.3, ставил как плагин Eclipse.

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


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

Глянул дизасмом:

 

Вариант без volatile (нерабочий):

08002206:   beq.n 0x8002220 <OS::TKernel::sched()+40>
84               SchedProcPriority = NextPrty;
>0800220a:   str r2, [r0, #12]
90                   DUMMY_INSTR();
>08002216:   nop 
93               while(CurProcPriority != SchedProcPriority); // until context switch done
>0800221a:   ldr r3, [r0, #0]
87               do
>0800221c:   cmp r3, r2
0800221e:   bne.n 0x8002214 <OS::TKernel::sched()+28>

 

Вариант с volatile (рабочий):

 

08002206:   beq.n 0x8002222 <OS::TKernel::sched()+42>
84               SchedProcPriority = NextPrty;
0800220a:   str r3, [r0, #12]
90                   DUMMY_INSTR();
08002216:   nop 
93               while(CurProcPriority != SchedProcPriority); // until context switch done
0800221a:   ldr r2, [r0, #0]
0800221c:   ldr r3, [r0, #12]
87               do
0800221e:   cmp r2, r3
08002220:   bne.n 0x8002214 <OS::TKernel::sched()+28>

 

Все-таки регистр r2 перед сравнением не обновляется. Оптимизатор?

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


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

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

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

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

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

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

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

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

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

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