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

Уменьшение минимального кол-ва стека

Боюсь уже автора задолбать, поэтому спрашиваю у всех.

Есть такая штука.. 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. Но все равно как я понимаю не застрахован и в этом случае. Какие будут соображения ?

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


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

Подумаю, поэксперементирую и обязательно отвечу. Если выяснится, что это

существенно, изменения будут сделаны уже в следующей версии (2.4).

В любом случае спасибо за тщательный анализ - вещи, кажущиеся банальными,

открываются с новой стороны...

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


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

Отвечаю по-порядку...

 

Но вот по идее то все равно, сколько раз такое может произойти - вобщем-то

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

стремиться к бесконечности ?

 

Нет, стек не стремится к бесконечности. Здесь вот в чем соль - чтобы при прерывании

стек задачи(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, здесь у Вас есть где-то проблемы в Вашем коде.

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


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

>> После создания tn_idle_task стек занимает 17 слов, сама задача

>> стек или не использует или использует максимум 2-3 слова

>> (зависит от компилятора). В худшем варианте с прерываниями, >> который Вы рассмотрели, понадобится еще, скажем,17 слов

>> - итого 34(37) и мы явно укладываемся в 48 слов стека этой

>> задачи.

 

А вложенные прерывания?

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


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

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. И мы НЕ уложимся в стек задачи именно из-за множественного вытеснения. В исправленном варианте все намного лучше - стек будет увеличиваться не более чем на два слова. Но вот сколько раз это рекуррентно произойдет ?

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


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

>> В авторском варианте 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, но на сайт его почему-то не выложил...

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


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

А я допишу пока для ясности..

"A когда задача становится активной,

то ее стек выгружается в регистры и соответственно ОПУСТОШАЕТСЯ."

Так в том то и дело, что он не успевает полностью выгрузиться в регистры, как задача снова вытесняется. Проблему полностью бы решила атомарная загрузка регистров +CPSR через {la-la} ^

Но пока я не очень представляю как это сделать, в CPSR надо не содержимое SPSR отнюдь загружать.

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


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

Но пока я не очень представляю как это сделать, в CPSR надо не содержимое SPSR отнюдь загружать.
"Я Пастернака не читал...", в смысле исходники TNKernel не изучал, но если SPSR после восстановления не нужен, то значит в него и надо перед восстановлением всех регистров занести требуемое значение для CPSR. И при его восстановлении операцией ldmfd xx, {...}^ разрешать прерывания. Глянь мой порт scmRTOS, может там мысль почерпнешь.

 

P.S. Там долго думал как организовать восстановление из SPSR когда смена контекста вызывается из User/System Mode, в которых SPSR нет. Не нашел ничего лучше чем принудительно одновременно с запретом прерываний irq переключиться в IRQ Mode и использовать неиспользуемый (по причине запрещенных прерываний) в этот момент SPSR_irq.

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


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

"Я Пастернака не читал...", в смысле исходники 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}^

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


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

В авторском варианте TNKernel вложенные прерывания не поддерживаются

Существенное ограничение. Это затрудняет появление портов операцинки на других МК, где имеется более развитый, чем у ARM7, контроллер прерываний.

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


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

Существенное ограничение. Это затрудняет появление портов операцинки на других МК, где имеется более развитый, чем у ARM7, контроллер прерываний.

да это ограничение обойти то не вопрос. Да и не нада нам ваших процев :tongue:

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


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

да это ограничение обойти то не вопрос.

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

(отношение к "собственным" вариантам ОС уже высказывал)

Почему это не реализовано сразу? Хотя в первой строке записано

"TNKernel is a compact and very fast real-time kernel for the embedded 32/16 bits microprocessors."

т.е. предлагается забыть об аппаратных возможностях большинства 32/16 МК?

 

PS:Прошу отнестись к моему вопросу с пониманием, а не бравым отрицанием НЕarm'ов.

 

Да и не нада нам ваших процев :tongue:

Не горячись раньше времени ;)

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


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

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

(отношение к "собственным" вариантам ОС уже высказывал)

Почему это не реализовано сразу? Хотя в первой строке записано

"TNKernel is a compact and very fast real-time kernel for the embedded 32/16 bits microprocessors."

т.е. предлагается забыть об аппаратных возможностях большинства 32/16 МК?

 

PS:Прошу отнестись к моему вопросу с пониманием, а не бравым отрицанием НЕarm'ов.

Не горячись раньше времени ;)

Ну так заведи свою ветку как плох TNKernel и почему ты выбрал не её. Тут обсуждаем расход памяти потоком

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


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

По материалам дискуссии буду делать v.2.4. Туда же войдет исправление

бага в ф-ции tn_task_exit(), который нашел Alex_B.

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


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

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

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

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

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

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

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

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

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

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