Миро Самек "Использование режимов малого энергопотребления в простых программных архитектурах" (перевод)


M16C

M16C - 16-разрядный процессор фирмы Renesas - поддерживает малопотребляющий режим ожидания, включаемый специальной инструкцией "WAIT". Техническая документация на M16C недвусмысленно сообщает, что перед исполнением "WAIT" необходимо разрешить прерывания. Из этого можно заключить, что атомарный переход в режим ожидания процессором не поддерживается [8]. Никаких примечаний, подобных особенностям выполнения пары "SLEEP-SEI" в AVR, в документации на M16C нет, а значит, запрет прерываний ("FCLR I") срабатывает сразу.

Так же, как в 8051, единственным путём для M16C будет нейтрализация каким-либо способом перехода в режим ожидания в обработчике прерывания, не позволяющая фоновому циклу засыпать. В отличие от MCS-51, M16C переводится в режим малого потребления не записью в регистр, а исполнением специальной инструкции, и, следовательно, вариант с теневым регистром неприменим. Идея отмены перехода в режим ожидания требует замены инструкции "WAIT" чем-нибудь нейтральным (таким как "NOP" или "RTS"). Да, речь идёт о самомодифицирующемся коде, но другие способы для M16C не просматриваются. К счастью, M16C имеет фон-неймановскую архитектуру и может выполнять код из оперативной памяти.

Кусок самомодифицирующегося кода может быть очень небольшим. Надо завести в оперативной памяти 4-байтовый массив, как показано в листинге 10. Этот машинный код представляет собой маленькую функцию, выполняющую инструкцию "WAIT", возвращающую управление вызывающей процедуре и пригодную для вызова средствами языка Си. Код модифицируется в фоновом цикле и вызывается через указатель на функцию, как показано в листинге 11.

Листинг 10. Создание 4-байтного массива в оперативной памяти

    usigned int volatile Wait_code[] = {
        0xF37D,                         /*  Инструкция "WAIT"               */
        0x04F3                          /*  Пара инструкций RTS и NOP       */    
    }

				

 

Листинг 11. Указатель на функцию, вызывающий код листинга 10

    _asm("FCLR I");                     /*  Запрещаем прерывания            */
    if (idle_condition()) {             /*  Состояние простоя ?             */
        Wait_code[0] = 0xF37D;          /*  Да: вставляем инструкцию "WAIT" */
        _asm("FSET I");                 /*  Разрешаем прерывания            */
        (*(void (*)(void))Wait_code)(); /*  Вызываем модифицированный код   */
    }
    else {
        _asm("FSET I");                 /*  Нет: разрешаем прерывания       */    
    }

				

 

В каждом обработчике прерываний переход в режим ожидания должен быть нейтрализован заменой кода инструкции "WAIT", расположенного в "Wait_code[0]", как показано в листинге 12. Такой подход допускает прерывания на любой машинной инструкции между "FSET I" (разрешением прерываний) и исполнением кода в области "Wait_code[0]". Любое такое прерывание заменит содержимое "Wait_code[0]" на пару инструкций "RTS, NOP", которая просто возвращает управление вызывающей процедуре, то есть фоновому циклу. В результате "WAIT" не срабатывает в тот момент, когда фоновый цикл соберётся, наконец, его выполнить. Итак, любое прерывание, перехватывающее управление у фонового цикла, отменяет переход в состояние ожидания, решая, тем самым, задачу безопасного перехода в режим сна.

Листинг 12. Запрет перехода в каждом прерывании заменой инструкции "WAIT" по адресу "Wait_code[0]"

    #pragma INTERRUPT my_ISR
    void my_ISR(void) {         /*  M16C входит в обработчик с ..       */
        ...                     /*  .. запрещёнными прерываниями        */
        Wait_code[0] = 0x04F3;  /*  Вставляем пару инструкций RTS и NOP */    
    }

				

 

ПредпросмотрAttachmentSize
low_power_modes_in_background.zip27.87 КБ