Wed, 16 Mar 2005 20:33:53 +0300 Alexey V Bugrov wrote to Harry Zhurov:
[...]
AV>>> Hет. Происходит сохранение контекста прерванной задачи, обработка AV>>> прерывания, восстановление контекста. HZ>> Как это нет? Именно щелканье контекстами на каждый байт. Вопрос HZ>> только в размере этого контекста.
AV> Что-то я не понимаю о чем тогда вообще разговор. У тебя получается тоже AV> самоей щелканье контекстами, только без твоего AV> участия и в неизвестном тебе объеме, т.к. оставляешь это на откуп AV> компилятору.
Нет. С моим участием, в известном объеме и никакого откупа компилятору - вообще без его участия. Просто на входе сохраняется сразу весь контекст. Понимаю, что не всегда это эффективно. Эффективно это только тогда, когда прерывание является источником событий. Но это универсальный способ, он работает всегда и везде без завязок на конкретные нюансы целевой платформы и проекта.
AV>>> как ты говоришь, "внеосевое".
HZ>> Э, нет, это далеко не то же самое. При внеосевом прерывании HZ>> компилятор (или я сам, если на асме пишу) сохраняет только то, что HZ>> использует. Hапример, использует два регистра - их и сохраняет.
AV> OR уже сказал про убогость этого компилятора. Если ему нельзя объяснить AV> какие регистры можно пользовать какие нет и AV> какие регистры он будет использовать.
Я бы не стал обзывать лучший компилятор убогим. Качество компилятора определяется в первую очередь качеством кодогенерации, и она на высоте. Указывать компилятору, как ему рулить регистрами, конечно, хорошо, но это имеет обратную сторону именно в кодогенерации, т.к. схемы распределения регистров при кодогенерации определяют многое (если не все). Как сказали в саппорте, register allocation при кодогенерации есть очень нетривиальная задача сама по себе. Компилятор дает возможность залочить часть регистров, т.е. он их не будет в этом случае использовать.
AV> Если нельзя явно разрешить, то нужно знать как оно работает.
Это да, это знать можно, и такое знание есть. Только где гарантия, что в следующей версии компилятор не будет что-то делать по-другому? Как быть в случае использования ОС для другой платформы? Все эти завязки на конкретный компилятор конкретной платформы, конечно, дают некую свободу в конкретном случае, но решение получается частным со всеми вытекающими. Если тебя это не волнует, то тебе хорошо. Как только начнет волновать, появятся все эти обсуждаемые вопросы.
[...]
HZ>> иметь другой вид!? Я уже не говорю про переход на другую версию HZ>> компилятора.
AV> Hе, ну немного нестандартно мыслить можно? Если нет средства досохранить AV> контекст, нехай компилятор это делает по AV> своему усмотрению. Сам же говоришь, что два-три регистра. Обработчик пусть AV> сохранит/восстановит то, что ему надо, а при AV> необходимости переключения задачи шедулер сохраняет контекст целиком.
AV> 1. Вошли в прерывание. AV> 2. Сохранили то, что требуется для инкремента ISR_Level и проверки флага AV> need_run_scheduler. AV> 3. Инкремент ISR_Level AV> 4. Вызов "внеосевого прерывания" с его собственным сохранением AV> восстановлением "контекста", тех самых двух регистров.
Как это "вызов"? Оно уже вызвано. Аппаратно. Мы в нем уже находимся.
Если ты подразумеваешь именно вызов функции, то тут, как только ты поставишь внутри обработчика прерывания вызов функции, компилятор вставит сохранение всех local регистров - ведь он не знает, какие регистры юзаются внутри этой функции, поэтому сохраняет все.
Если ты подразумеваешь передачу управления обработчику прерывания путем инициирования аппаратного прерывания, то тут я не понимаю, как это сделать - ведь мы уже находимся в обработчике прерывания, куда попали по вектору текущего прерывания. Если инициировать еще одно (например, путем взвода флага прерывания), и разрешить вложенные прерывания, то опять попадем сюда же. Если использовать другой вектор, то это будет нечто вроде софтового прерывания, которое проще использовать по-другому - т.е. после завершения текущего. И в этом случае проблема остается ровно та же - хорошо, если софтовое прерывание есть. Но его на деле часто нет (к примеру, в AVR, MSP430 его нет).
AV> 5. if (!need_run_scheduler || ISR_Level) AV> 6. восстановили то, что сохранили в п.2 далее reti AV> 7. сохраняем весь контекст AV> 8. вызываем шедулер AV> 9. восстанавливаем контекст новой задачи. AV> 10. reti в новую задачу.
С этим понятно.
AV> Hу что, запредельные накладные расходы?
Нет. Только я пока не вижу, как это можно реализовать, не привязываясь к нюансам МК и используемого компилятора. Будь софтовое прерывание штатной вещью в любом МК, тогда проблемы бы не было. А так приходится сразу делить прерывания на осевые и внеосевые. Осевые - это которые однозначно являются источниками событий (т.е. в них взводятся семафоры). Внеосевые - остальные.
[...]
HZ>> Как? Hа асме написать? Hаучи, как это сделать, например, на AVR? В HZ>> самом проце ничего особенного нет: 32 РОH, 2 байта - Stack Pointer, HZ>> один байт - Status Reg. Как сделать, чтобы сохранять полный контекст HZ>> только когда требуется перепланирование? ISR пишется на С.
AV> см. выше. :)
Нипалучицца. :)
[...]
HZ>> Вроде нет. Или под компилятор HZ>> подстраиваешься?
AV> Hет. С компилятором завзяки только на runtime model, т.е. как расположен (в AV> каких регистрах) и как обслуживается AV> стек и еще кое что по мелочи. Без этих данных невозможно корректное AV> сохранение контекста вообще.
Вот это, имхо, как раз, не самое лучшее решение: компиляторы меняются. Меняются версии, меняются производители. Тут ты привязан крепко. И нет гарантий, что даже в текущем состоянии компилятор в какой-то ситуации не сделает что-то "нестандартно". Документированы, как правило, соглашения о вызове. А поведение компилятора, например, в прерываниях - это внутреннее дело компилятора (и его разработчиков). Закладываться на это - это хак.
Более надежным решением, имхо, является просто отобрать у компилятора возможность рулить процессом сохранения/восстановления регистров в прерывании (осевом). Т.е. чтобы он вообще к этому не касался. Тогда пишем на входе свой код сохранения, на выходе свой код восстановления. И от компилятора не зависим. Главное, чтобы компилятор позволял это делать. Это самое слабое место оного подхода. Но пока на практике все хорошо. IAR для MSP430 имеет специальное слово __raw, для AVR - __task, GCC - __attribute__ ((naked)). И от компилятора тут вообще ничего не зависит - сохранение/восстановление контекста вообще не его ума дело.
HZ>> что есть. В TMS320F28xx тоже все это есть. Hеплохо было бы, если бы HZ>> что-то подобное было и в более мелких. Hу хотя бы в старших моделях HZ>> AVR, MSP430, PIC18 и других их "одноклассников".
AV> Hекто в здравом уме их не нагружает задачами высокоскоростой обработки AV> потоками данных под управлением (RT)OS. Hалицо AV> невостребованность ресурса.
"Налицо" - категория субъективная. При 115200 - период 87 мкс, что есть довольно небольшое время вообще, безотносительно к RTOS. Довольно глупо при приеме пакета прыгать туда-сюда за каждым байтом только для того, чтобы сложить его куда-то в буфер. Такую тупую работу вполне можно поручить периферийному автомату. В MSP430, кстати, для модуля АЦП реализовано пакетное АЦПирование, т.е. зарядить ему очередь, он и собирает. До 16 измерений за раз. Учитывая, что там скорость до 200 киловыборок в секунду может доходить, то прыгать за каждым отсчетом не очень хорошая идея. При этом, он для каждого измерения берет индивидуальные настройки (номер канала, вид опоры и т.д.), т.е. это дело куда как сложнее простого пересылания байтов. И дело даже не в том, что большие объемы надо пересылать, а в скорости. Пусть даже пакеты приходят редко, зато скорость высокая. Эта скорость на момент поступления пакета может просто парализовать МК - прикинь, если там не 115200, а больше?
Все это, конечно, умозрительно, в реальности все знают ограничения и выходят из положения так или иначе. Но, имхо, наличие простейшего FIFO решило бы проблему, а цена за него невелика. Кстати, проблема усугубляется в случае, например, синхронных интерфейсов типа SPI, где скорости выше, а времянки жестче. SPI Slave на МК получается не очень здорово - ведь надо успеть прыгнуть в прерывание и пихнуть следующий байт до прихода следующего бита. Это накладывает ограничения на режим работы Master'а. При наличие FIFO, можно было бы сложить пачку байт в него, и пусть себе отправляются сами. Вот в текущем проекте мне приходится из-за описанного обстоятельства сильно менять режим работы программы, рулящей передачей через SPI: там два устройства-слейва сидят, одно ПЛИС, другое МК. С ПЛИС проблем нет, она все успевает со свистом, что понятно, а вот для МК приходится паузы между передачами байтов вводить, чтобы он гарантированно успевал обменивать байты на своем конце.