Sat, 19 Mar 2005 00:24:28 +0300 Alexey V Bugrov wrote to Harry Zhurov:
AV>>> Hикак. Ядро (таскманагер) писать с нуля. Все остальное может быть AV>>> и на С написано, легко перенесется. HZ>> Похоже, у нас несколько разное восприятие понятия "ядро". Вот ISR, HZ>> где возможно перепланирование - это ядро?
AV> В твоих терминах не знаю. В моих терминах перепланирование в AV> пользовательском ISR невозможно, из него только посылаются AV> сигналы ядру, которое перхватывает вызов пользовательской ISR и по AV> окончании его работы производит перепланирование при AV> необходимости. Интересно, сколько раз я уже написал этот тезис за время AV> нашей дискуссии?
AV> Т.е. у меня ISR - это не ядро, оно может быть писано на чем угодно,
В моем понимании ISR - есть кусок кода, асинхронно и _аппаратно_ вызываемый из основной программы.
AV> только вот его вызов перехватывается ядром, которое AV> делает всю черную работу.
Как это "перехватывается"? Как вообще может перехватываться управление если только не с помощью прерывания? Как ни назови, но либо обработчик прерывания - подпрограмма, вызванная аппаратно при возникновении соответствующего события, - отбирает управление у одного куска (задачи/процесса) основной программы и отдает его другому куску (задаче/процессу), либо основная программа сама отдает управление ядру путем вызова ядреной функции (что имеет место внутри функций сервисов). В этом случае обработчик прерывания - есть часть ядра.
AV> У меня _внутри_ пользовательского ISR никакое перпланирование невозможно.
Поэтому у тебя реально обработчик прерывания является частью ядра - т.е. он обладает всеми полномочиями ядра, а из него вызывается пользовательский код, который ты называешь "пользовательским ISR".
AV>>> Даже для uC/OS Лябрус вполне прозрачно написал, что если AV>>> интересует результат, а не процесс, то самая главная часть ОС, в AV>>> том числе планировщик, должна быть написана на асме. Обвеска на C AV>>> - сколько угодно. HZ>> Hо сам-то он все на С делает.
AV> Note that OSSched() could be written entirely in assembly language to AV> reduce scheduling time. OSSched() was written in AV> C for readability, portability and also to minimize assembly language.
:) То, что может быть написано, никто не сомневается. Только сам он так не сделал по понятным причинам (что-то у меня большие сомнения, что его планировшик, переписанный на асме, даст хоть какое-то заметное преимущество. И я, также, не слышал, чтобы кто-то так делал.
AV> Вот если последнее не требуется (ты же не занимаешься написанием ос на AV> продажу и каждый день с платформы на платформу AV> не прыгаешь) то писание на асме полностью оправдано.
Я не прыгаю каждый день с платформы на платформу. И ничего не продаю. Но у меня есть две рабочих платформы. И еще две маячат впереди, одна вполне определенно, другая пока туманно. И я, напротив, не вижу никаких весомых причин использовать тут ассемблер. У меня есть определенная программная модель ядра и прерываний и ломать ее на пустом месте я не хочу.
[...]AV>>> Да. Обработчик прерывания - это функция. Какая разница кто ее AV>>> вызовет: внешнее событие или другой код? Единственная проблема - AV>>> это reti вместо ret на выходе. Hо это можно обойти.
HZ>> А вектора прерываний ты сам руками вставляешь?
AV> ;=============================================================== AV> OS_ISRV_SECT CODE 0x0018 ; адрес вектора прерывания. Сюда ядро AV> прибито гвоздями. AV> ;--------------------------------------------------------------- AV> ; save minimal context AV> ; AV> [skip, ибо не существенно] AV> ; AV> btfsc OS_CtxSwPending ; check OS request, переключение контекста AV> запрошено вне прывания. AV> bra _skip_user_isr AV> ;--------------------------------------------------------------- AV> ; proceed user interrupts AV> ; AV> incf OS_bIntLevel,F,ACCESS ; increment interrupt nest level AV> incf OS_bLockCount,F,ACCESS ; we are inside interrupt, so increment
AV> locker AV> ; AV> call OS_ISR ; call user ISR (это тот самый AV> обработчик, который сгенерил компилятор) AV> ; AV> decf OS_bLockCount,F,ACCESS AV> decfsz OS_bIntLevel,F,ACCESS ; decrement interrupt nest level AV> bra _os_exit ; if not zero skip task scheduler AV> ; AV> _skip_user_isr: AV> ;--------------------------------------------------------------- AV> ; тута смотрим надо ли запускать планировщик и т.д.
Т.е. этот ассемблерный кусок кода ты руками вставляешь каждый раз для каждого ISR? Или у тебя там где-то еще есть анализ, какой именно "пользовательский ISR" вызвать? А этот код, пользуясь тем, что вектор один, общий? Если так, то как быть, когда для каждого обработчика свой вектор? Дублировать руками кучу ассемблерного кода?
HZ>> Поясню, что имеется HZ>> в виду: в IAR'е, например, есть волшебное слово __interrupt, которое HZ>> придает функции особый статус - в частности, компилятор сохраняет все HZ>> используемые регистры, а не только local.
AV> Hу и что? Это даже замечательно. От этого сгенерированный код перестал быть AV> функцией, которая может быть вызвана AV> коммандой call?
Перестал - как ты знаешь, call обычно порождает на выходе ret. А функция, оформленная как __interrupt на выходе будет иметь reti.
HZ>> И еще там же предназначена к HZ>> использованию волшебная прагма: #pragma vector=..., где указываешь HZ>> адрес ISR.
AV> Хинт: в этой прагме указываешь не адрес вектора, а некий произвольный адрес AV> вне таблицы векторов.
Хе, дык компилятор-то этот левый адрес и вставит в таблицу векторов и будет вызывать что попало.
AV> Или вообще не указываешь эту прагму, пусть функция будет там где она есть, AV> а вектор останется не тронутым.
Это можно. Только компилятор начнет вопить, что, типа, вектор не указали. Можно, конечно, вопли подавить, но это как-то не очень кузяво.
AV> В таблице векторов располагаешь врапер на асме, который делает описанную AV> мной работу и вызывает этот самый обработчик (OS_ISR), AV> расположенный по AV> адресу, указанному в прагме.
Понятно. Я так и делал, когда не удавалось подружить __interrupt и __C_task в каких-то версиях EWAVR: сам обработчик делал обычной функцией, на входе и на выходе руками все обрамление, в таблицу векторов - адрес. Работало. Но геморройно это.
HZ>> Таким образом, компилятор делает всю остальную работу - HZ>> помещение адреса ISR в таблицу векторов прерываний, реализует HZ>> поведение внутри ISR как это полагается делать (все же HZ>> обработчик прерываний - непростая функция).
AV> Единственное отличие - это reti в конце вместо ret.
Это немало.
AV> Все остальное несущественно.
Кому как. Что-то мне не улыбается таскать руками пачки ассемблерного кода, не имеющего отношения к целевому проекту, особенно учитывая, что в рабочих проектах я уже и забыл, когда последний раз использовал ассемблер.
AV> [skip]
HZ>> Если будет требоваться скорость (либо какие-то другие причины HZ>> вынудят), то так и делаем. Что выбрать - второй вопрос. Hо пока по HZ>> скорости устраивает, можно и не дергаться, а решить все в лоб.
AV> Это вопрос для отдельной дискуссии, но все-таки я надеюсь, что обсуждаем AV> разные способы реализации планировщика и AV> пытаемся взвесить их плюсы и минусы. Для моего варианта я вижу пока минус AV> только один: меньшая (не очень значительно) AV> переносимость кода и чуть-чуть большие накладные расходы на вход/выход из AV> обработчика.
А таскание вручную низкоуровневого, не имеющего отношения к прикладной задаче, кода? И обслуживание его - вызовы-то самих "пользовательских ISR" надо тоже руками вписывать. Словом, удобство и автоматизация еще те! Я бы не отнес это к достоинствам.
AV> Зато мы получаем прозрачность написания пользовательских прерываний (не AV> делим их на осевые и неосевые),
Ой, а что уж там непрозрачного? В одном случае я пишу
__interrupt void ISR() { ... }
В другом:
OS_INTERRUPT void ISR() { TISR_Wrapper ISR_Wrp; ... }
Что уж тут сложного?
AV> не стесняясь использовать функции ОС по прямому назначению (т.е фукции AV> сигнализации в прерываниях)
То же самое - в осевых прерываниях смело сигналим флаги, мечем сообщения и т.д. На выходе - перепланирование.
AV> и увеличение скорости обработки прерываний, т.к.
Увеличение скорости имеет место _только_ в случае, если было осевое прерывание, где семафор не взведен (как в случае приема байтов в пакете). Когда семафор взводится, никакого выигрыша нет, все ровно так же.
А по сравнению с обычным (внеосевым) прерыванием тут только проигрыш по скорости. И не надо говорить, что такие прерывания не нужны. Нужны. Например, был у меня в проекте использован термодатчик TMP03 (АД), который цифровой и выдает результат в виде ШИМ (температура вычисляется из соотношения времен полупериодов). Я завел его на Input Capture, где только с буфер складировались значения полупериодов. А когда надо было получить температуру, прикладной процесс просто брал значения и вычислял. Зачем тут городить какие-то контексты и прочее. Пришел фронт - быстренько прыгнули, сложили значение в кольцевой буферок и все. Максимально быстро и просто. Это я к тому, что не всякое прерывание может служить источником событий для ОС.
AV>>> Hа асме?
HZ>> Естественно. Пишется макрос, который состоит из ассемблерных HZ>> вставок. Один на входе (сохранение), другой на выходе HZ>> (восстановление).
AV> Если это допустимо, то и вставить перед выходом из функции прерывания, AV> сгенерированной компилятором, _asm(ret) труда не AV> составит (это чтобы подменить reti компилятора).
Не подменить, а вставить перед. Чтобы уверенно сказать, надо пробовать.
AV> Значит мой вариант реализуем на 100%. Hикаких макросов для AV> сохранения/восстановления контекста в ISR не потребуется.
В общем, не вижу я, чтобы все это было лучше тех двух ISR с подменой вектора. И если уж отказываться от универсальности, то лично мне значительно более симпатичен вариант с софтовым прерыванием или его эмуляцией с помощью аппаратного. В этом случае все гораздо проще и прозрачнее: любой ISR, кроме того самого софтового, не является частью ядра и пишется как обычно (ну, с ма-аленьким добавлением), а управление именно _отбирается_ (с помощью софтового прерывания) при необходимости ядром после выхода из любого ISR - если внутри ISR взводится семафор, то там же тупо выставляется запрос на софтовое прерывание - тогда после выхода сразу же управление будет передано в это софтовое прерывание, где так же тупо выбирается наиболее приоритетный процесс и тупо переключаются контексты. Все. Сама идея мне нравится и я серьезно подумаю над реализацией (главным образом, над тем, как формализовать процесс "затачивания" свободного аппаратного прерывания под софтовое).