DASM 0 29 марта, 2007 Опубликовано 29 марта, 2007 · Жалоба Боюсь уже автора задолбать, поэтому спрашиваю у всех. Есть такая штука.. tn_switch_context Там такой код tn_sw_restore ldmfd sp!, {r0} ldmfd sp!, {r1} msr CPSR_cxsf, r1; с этой точки прерывания разрешены ! Комментарий мой msr SPSR_cxsf, r0 mrs r4, CPSR bic r4, r4, #NOINT msr CPSR_c, r4 ldmfd sp!, {r0-r12,lr,pc} То есть стек задачи еще не развернут, прерывания разрешены. Наступление прерывания, требующего переключения контекста в нем затребует у этой задачи еще 15*4=60 байт стека. (вернее надо будет 68 байт, но 8 мы уже отыграли назад) Вот дальше у меня сомнения - сколько раз такое может произойти... В реалии раза 3-4 я точно наблюдаю - хорошо видно по использованию стека. Как уменьшить тоже понятно - перенести восстановление CPSR после развертывания большей части стека через ldmfd sp!, {r0-r12,lr,pc}. Но вот по идее то все равно, сколько раз такое может произойти - вобщем-то определить невозможно, а значит и минимальный требуемый стек для потока должен стремиться к бесконечности ? Замена вышепреведенного кода на вот такой (жутко наивный, но все же) ldmfd sp!, {r0} ldr r1, =t_spsr str r0, [r1] ldmfd sp!, {r0} ldr r1, =t_cpsr str r0, [r1] ldmfd sp!, {r0-r12,lr} push {r1} ldr r1, =t_spsr ldr r1,[r1] msr SPSR_cxsf, r1 ldr r1, =t_cpsr ldr r1,[r1] msr CPSR_cxsf, r1 pop {r1} ldmfd sp!, {pc} уменьшила требуемый стек очень прилично. Вопрос возник не праздно, прокачивю через USB большой поток.. и в результате например банальная tn_idle_task благополучно через перваливала за 96 слов требуемого стека, при том что по умолчанию он 48. Но все равно как я понимаю не застрахован и в этом случае. Какие будут соображения ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
yuri_t 0 29 марта, 2007 Опубликовано 29 марта, 2007 · Жалоба Подумаю, поэксперементирую и обязательно отвечу. Если выяснится, что это существенно, изменения будут сделаны уже в следующей версии (2.4). В любом случае спасибо за тщательный анализ - вещи, кажущиеся банальными, открываются с новой стороны... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
yuri_t 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба Отвечаю по-порядку... Но вот по идее то все равно, сколько раз такое может произойти - вобщем-то определить невозможно, а значит и минимальный требуемый стек для потока должен стремиться к бесконечности ? Нет, стек не стремится к бесконечности. Здесь вот в чем соль - чтобы при прерывании стек задачи(task) использовался для сохранения регистров, эта задача должна прежде всего быть активной(curr_run). A когда задача становится активной, то ее стек выгружается в регистры и соответственно ОПУСТОШАЕТСЯ. Отсюда - не бесконечное увеличение стека, а лишь бесконечный цикл - заполнение/опустошение. Наступление прерывания, требующего переключения контекста в нем затребует у этой задачи еще 15*4=60 байт стека. (вернее надо будет 68 байт, но 8 мы уже отыграли назад) Здесь Вы, Дмитрий, совершенно правы. Существующий код выглядит так tn_sw_restore: ldmfd sp!, {r0} ldmfd sp!, {r1} msr CPSR_cxsf, r1 msr SPSR_cxsf, r0 /* Эти 3 сроки можно исключить, т.к. фактически прерывания уже разрешены после команды msr CPSR_cxsf, r1, как справедливо заметил DASM */ mrs r4, CPSR bic r4, r4, #NOINT msr CPSR_c, r4 /* stack опустошен */ ldmfd sp!, {r0-r12,lr,pc} Код, уменьшающий использование стека, выглядит так: tn_sw_restore: ldmfd sp!, {r0} ldmfd sp!, {r1} orr r1, r1, #NOINT /* disable int */ msr CPSR_cxsf, r1 /*restore CPSR, but int are off*/ msr SPSR_cxsf, r0 /*restore SPSR*/ ldmfd sp!, {r0-r12,lr}/* restore all regs, execept pc*/ /* Enable interrupts */ stmfd sp!,{r0} /* save r0- tmp*/ mrs r0, CPSR bic r0, r0, #NOINT msr CPSR_c, r0 /* точка разрешения прерываний */ ldmfd sp!,{r0} /* restore r0*/ ldmfd sp!, {pc} /* new pc*/ И тот, и другой варианты функционируют корректно. Здесь лишь вечная задача выбора ("Вчера по 3, но маленькие, сегодня по 5, но большие"(с)) - или занимать больше памяти (стека), но работать быстрее, или занимать меньше памяти, но работать медленнее. например банальная tn_idle_task благополучно через перваливала за 96 слов требуемого стека, при том что по умолчанию он 48. Ф-ция у задачи tn_idle_task выглядит так: static void tn_idle_task_func(void * par) { for(;;) { tn_idle_count++; } } После создания tn_idle_task стек занимает 17 слов, сама задача стек или не использует или использует максимум 2-3 слова(зависит от компилятора). В худшем варианте с прерываниями, который Вы рассмотрели, понадобится еще, скажем,17 слов - итого 34(37) и мы явно укладываемся в 48 слов стека этой задачи. Я запускал под отладчиком множество своих проектов и нигде не видел, чтобы стек в tn_idle_task неконтролируемо возрастал. Cовершенно не могу представить, откуда могут взятся здесь 96 слов ??? IMHO, здесь у Вас есть где-то проблемы в Вашем коде. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alex B._ 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба >> После создания tn_idle_task стек занимает 17 слов, сама задача >> стек или не использует или использует максимум 2-3 слова >> (зависит от компилятора). В худшем варианте с прерываниями, >> который Вы рассмотрели, понадобится еще, скажем,17 слов >> - итого 34(37) и мы явно укладываемся в 48 слов стека этой >> задачи. А вложенные прерывания? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
yuri_t 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба В авторском варианте TNKernel вложенные прерывания не поддерживаются Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DASM 0 30 марта, 2007 Опубликовано 30 марта, 2007 · Жалоба msr CPSR_c, r0 /* точка разрешения прерываний */ ldmfd sp!,{r0} /* restore r0*/ ldmfd sp!, {pc} /* Тот же вараиант, что я и привел выше по-сути. Прерывания разрешаются, когда еще в стеке задачи два int - а (R0 и PC). Далее ситуация - прерывания, требующее irq_context_int. Задача вытесняется на этой точке. Потом задача делается опять планируемой (через обычный switch_context уже ) Выполнение продолжается с точки ldmfd sp!,{r0}. НО ТУТ же задача опять вытесняется вышеназванным методом. И так далее. Я не вижу тут ограничения для бесконечного роста стека в вытесненной задаче, т.к. вытеснение происходит многократно в точке, где стек еще несбалансирован. Изучение стека задачи, кушающей так много стека это подтверждает. Там сплошные точки возврата в эти две команды. "В худшем варианте с прерываниями, который Вы рассмотрели, понадобится еще, скажем,17 слов - итого 34(37) и мы явно укладываемся в 48 слов стека этой задачи." Тут не понял... при вытеснении этой задачи в ей стек будут сохранены все R0-R12, PC, LR, SPSR и CPSR. Проверить это все просто. В примере USB_BULK через USBIO закидать допустим 2-ую endpoint мегабайтами траффика. Для нагляндности в USBIO размер буффера лучше поставить например 5-ть байт, так прерываний больше будет. ( Упростил код только до ожидания очереди от приема 2-ой endpoint и тут же release_mem, ьессмысленный но отладочный вариант). А потом поглядеть насколько опускался стек idle_task. И мы НЕ уложимся в стек задачи именно из-за множественного вытеснения. В исправленном варианте все намного лучше - стек будет увеличиваться не более чем на два слова. Но вот сколько раз это рекуррентно произойдет ? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Alex B._ 0 31 марта, 2007 Опубликовано 31 марта, 2007 · Жалоба >> В авторском варианте TNKernel вложенные прерывания >> не поддерживаются Тогда не понимаю, зачем нужен tn_int_counter и его проверка на >1 (я так думал, чтобы разрешить переключение контекста только из первого прервания - что логично): tn_cpu_irq_isr: stmfd sp!,{r0-r12,lr} /* Save ALL registers */ /*-- interrupt_counter++ */ ldr r2, =tn_int_counter ldr r3, [r2] /* interrupt_counter -> r3 */ add r0, r3,#1 /* interrupt_counter++ */ str r0, [r2] bl tn_cpu_irq_handler /* Actually Handle interrupt */ /* interrupt_counter-- */ ldr r2, =tn_int_counter ldr r1, [r2] sub r3, r1,#1 str r3, [r2] cmp r3, #0x00 /* if it is nested int - return */ /* ваш комментарий ^^^^^^^ */ bne exit_irq_int /* return from int*/ ldr r0, =tn_context_switch_request /* see if we need to do a context switch */ ldr r1, [R0] cmp r1, #0 /* if 0 - return */ beq exit_irq_int mov r1, #0 /* else - clear req flag and goto context switch */ str r1, [r0] b tn_int_ctx_switch Еще, Дмитрий, пока не забыл - В версии с сайта (2.3) есть небольшой баг - при возникновении любого прерывания, переключающего контекст во время выполнения tn_task_exit() задача по activate не восстанавливается. Мы с Юрием это обсуждали, он прислал код 2.3.1, но на сайт его почему-то не выложил... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DASM 0 31 марта, 2007 Опубликовано 31 марта, 2007 · Жалоба А я допишу пока для ясности.. "A когда задача становится активной, то ее стек выгружается в регистры и соответственно ОПУСТОШАЕТСЯ." Так в том то и дело, что он не успевает полностью выгрузиться в регистры, как задача снова вытесняется. Проблему полностью бы решила атомарная загрузка регистров +CPSR через {la-la} ^ Но пока я не очень представляю как это сделать, в CPSR надо не содержимое SPSR отнюдь загружать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 121 31 марта, 2007 Опубликовано 31 марта, 2007 · Жалоба Но пока я не очень представляю как это сделать, в CPSR надо не содержимое SPSR отнюдь загружать."Я Пастернака не читал...", в смысле исходники TNKernel не изучал, но если SPSR после восстановления не нужен, то значит в него и надо перед восстановлением всех регистров занести требуемое значение для CPSR. И при его восстановлении операцией ldmfd xx, {...}^ разрешать прерывания. Глянь мой порт scmRTOS, может там мысль почерпнешь. P.S. Там долго думал как организовать восстановление из SPSR когда смена контекста вызывается из User/System Mode, в которых SPSR нет. Не нашел ничего лучше чем принудительно одновременно с запретом прерываний irq переключиться в IRQ Mode и использовать неиспользуемый (по причине запрещенных прерываний) в этот момент SPSR_irq. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DASM 0 31 марта, 2007 Опубликовано 31 марта, 2007 · Жалоба "Я Пастернака не читал...", в смысле исходники TNKernel не изучал, но если SPSR после восстановления не нужен, то значит в него и надо перед восстановлением всех регистров занести требуемое значение для CPSR. И при его восстановлении операцией ldmfd xx, {...}^ разрешать прерывания. Глянь мой порт scmRTOS, может там мысль почерпнешь. Надо подумать.. Кстати по-моему в UCOS засада со стеком точно такая же сидит . Проверил ! Все замечательно работает, и стека не кушает :-) Немного тяжко думать, прав ли я теряя SPSR и записывая в него на самом деле CPSR, но раз работает - значит можно :-) Возможно Юрий лучше ответит на этот вопрос, я в АРМовском асме дня три три только копаться начал. Но со стеком теперь совсем хорошо - 18 байт скушал idle_task и больше ни словом больше, несмотря ни на какие вытеснения. Вопрос решен !!!!!!!!!!!!!!!!!!!!!!!!!!!! Вот итоговый код. tn_sw_restore add sp, sp, #4; easy adjust stack pointer, since we don't need restore original SPCR ldmfd sp!, {r1} msr SPSR_cxsf, r1; transfer saved CPSR to SPSR ldmfd sp!, {r0-r12,lr,pc}^ Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spf 0 31 марта, 2007 Опубликовано 31 марта, 2007 · Жалоба В авторском варианте TNKernel вложенные прерывания не поддерживаются Существенное ограничение. Это затрудняет появление портов операцинки на других МК, где имеется более развитый, чем у ARM7, контроллер прерываний. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DASM 0 31 марта, 2007 Опубликовано 31 марта, 2007 · Жалоба Существенное ограничение. Это затрудняет появление портов операцинки на других МК, где имеется более развитый, чем у ARM7, контроллер прерываний. да это ограничение обойти то не вопрос. Да и не нада нам ваших процев :tongue: Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
spf 0 31 марта, 2007 Опубликовано 31 марта, 2007 · Жалоба да это ограничение обойти то не вопрос. Это понятно, но зачем обходить, если можно взять другую, в которой об этом позаботились сразу. (отношение к "собственным" вариантам ОС уже высказывал) Почему это не реализовано сразу? Хотя в первой строке записано "TNKernel is a compact and very fast real-time kernel for the embedded 32/16 bits microprocessors." т.е. предлагается забыть об аппаратных возможностях большинства 32/16 МК? PS:Прошу отнестись к моему вопросу с пониманием, а не бравым отрицанием НЕarm'ов. Да и не нада нам ваших процев :tongue: Не горячись раньше времени ;) Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DASM 0 31 марта, 2007 Опубликовано 31 марта, 2007 · Жалоба Это понятно, но зачем обходить, если можно взять другую, в которой об этом позаботились сразу. (отношение к "собственным" вариантам ОС уже высказывал) Почему это не реализовано сразу? Хотя в первой строке записано "TNKernel is a compact and very fast real-time kernel for the embedded 32/16 bits microprocessors." т.е. предлагается забыть об аппаратных возможностях большинства 32/16 МК? PS:Прошу отнестись к моему вопросу с пониманием, а не бравым отрицанием НЕarm'ов. Не горячись раньше времени ;) Ну так заведи свою ветку как плох TNKernel и почему ты выбрал не её. Тут обсуждаем расход памяти потоком Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
yuri_t 0 31 марта, 2007 Опубликовано 31 марта, 2007 · Жалоба По материалам дискуссии буду делать v.2.4. Туда же войдет исправление бага в ф-ции tn_task_exit(), который нашел Alex_B. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться