В семействе STM8 заложена очень полезная возможность экономии энергии в случае, когда быстрые и критичные ко времени обработки выполняются по прерываниям, а низкоприоритетные задачи работают в фоновом режиме. Для этого используется бит AL в регистре GCR и машинная команда WFI. Однако здесь был обнаружен подводный камень, не описанный в текущей версии errata на кристалл.

Данная проблема была обнаружена на кристалле stm8l152c6t6, установленном на STM8L-Discovery board.
В основном процессе был инициализирован таймер TIM4 для работы по прерываниям. Обработчик прерывания для него практически пустой (ну за исключением процедуры сброса бита TIM_SR1_UIF в регистре TIM4->SR1). Далее в основном процессе была разрешена запись в EEPROM путем разблокировки MASS и инициирована процедура записи байта с генерацией IRQ по ее окончании. После чего в регистре GCR был установлен бит AL и выполнена команда WFI.
В обработчике прерываний по завершению операции записи в EEPROM после чтения содержимого FLASH->IAPSR понижается приоритет выполняемого кода командой RIM или комбинацией PUSH #val/POP CC. Т.е. внутри EEPROM ISR разрешаются все остальные прерывания. И было обнаружено следующее: если EEPROM ISR была прервана другим прерыванием, то после возврата из вложенного прерывания выполнение обработки EEPROM ISR прекращается (т.е. такое впечатление, что CPU core переходит в состояние WFI, выполненное основным процессом).
Данная ошибка проявляется только при наличии следующих условий:
  1. Перед выполнением WFI в основном процессе бит AL в регистре GCR был установлен
  2. Приоритет EEPROM IRQ оставлен по умолчанию (т.е. содержимое регистра ITC->ISPR1 равно 0xFF)


Возможные workarounds:
  1. В основном процессе до выполнения инструкции WFI сбросить бит AL в GCR. При этом после каждого прерывания основной процесс будет возобновлять свое выполнение, что не очень хорошо скажется на энергопотреблении.
  2. Перед понижением приоритета внутри EEPROM ISR (т.е. до команд RIM или PUSH #val/POP CC) также сбросить бит AL в GCR. Опять-таки, в этом случае после завершения EEPROM ISR основной процесс продолжит свое выполнение, что не очень хорошо скажется на энергопотреблении.
  3. Изменить приоритет EEPROM IRQ в регистре ITC->ISPR1, например записав в него значение 0b11110111. Что самое непонятное, после этого начинают нормально работать команды RIM или PUSH #val/POP CC внутри EEPROM ISR (т.е. после возврата из вложенного прерывания обработка EEPROM ISR возобновляется).


Господа, у кого есть желание/возможность, попробуйте воспроизвести этот баг на других кристаллах семейства STM8/STM8L. Ибо если этот баг действительно воспроизводим, то надо пнуть STM на предмет его исправления или хотя бы внесения в errata sheet.
Поделиться с друзьями
-->

Комментарии (20)


  1. armature_current
    25.03.2017 10:18

    Я не понял, у Вас обработчик прерывания окончания записи EEPROM вызывается в состоянии WFI? Если так, то вполне документированная работа, т.е. возврат в состояние WFI после вложенного прерывания.


    you can set the AL bit before entering Low power mode (by executing WFI/HALT instruction). Consequently, the interrupt routine causes the device to return to low power mode., then the interrupt routine returns directly to Low power mode. The run time/ISR execution is reduced due to the fact that the register context is saved only on the first interrupt


    1. Vedga
      25.03.2017 10:24

      Произошло прерывание от EEPROM, началась обработка, она была прервана таймером, и после выхода из таймерной ISR продолжения обработки _прерывания_ EEPROM не происходит. Это нормально!?


      1. armature_current
        25.03.2017 14:35

        1. Так Вы же сами перевели контроллер на самый низкий приоритет в обработчике EEPROM ISR, тем самым позволили другим прерываниям вмешиваться в ход обработки первого прерывания. При входе во вложенное прерывание от таймера, в стеке сохраняется регистровый контекст, по которому контроллер находится в основном процессе. При выходе из вложенного прерывания в контроллере установлен бит AL и RETI, что приводит к тому, что и описано в документации, т.е. вход в WFI.


        2. Вопрос, зачем Вам понижать приоритет в обработчике окончания записи EEPROM, но при этом пытаться остаться в обработчике? Нестандартное решение -> нестандартное поведение.


        3. Workarounds #3. Вы уверены, что у Вас осталось вложенное прерывание? Может просто оно остается висячим до окончания EEPROM ISR?


        1. Vedga
          27.03.2017 09:19

          По пункту (1): такое поведение наблюдается при понижении до любого приоритета, отличного от I1 == I0 == 1. Таким образом при выходе из вложенного прерывания приоритет кода может отличаться от приоритета, на который мы попали при выполнении WFI. Однако зависание имеет место быть.
          2. После окончания записи байта/слова возможно нам понадобится записать следующий байт/слово. Это надо определить и инициировать следующую операцию. Несколько команд.
          Для уменьшения времени пребывания в режиме запрещения прерываний вполне законно изменить текущий приоритет. И в этот момент произошло вложенное прерывание. Что тут не стандартного?
          3. Проверяется, допустим, изменением состояния внешнего пина с просмотром результата на осциллографе. Либо изменением какой-либо ячейки памяти. Да, я уверен: вложенное прерывание остается.


          1. armature_current
            27.03.2017 14:58

            По первому отписался ниже.
            По второму, стандартно, т.е. "условно стандартно", перешли по вектору прерывания, обработали, вышли из прерывания соответствующей инструкцией. Если нужно раскидать обработку прерываний от разных источников, то выставляем соответствующие программные приоритеты. И совсем другая ситуация "условно не стандартно" когда понижают приоритет в самом прерывании. С какой целью, словить другое прерывание и обмануть логику микроконтроллера? Во всяком случае, без понимания контекста задачи трудно понять зачем такие выкрутасы. Если нужно выполнить еще какие-то операции, например инициировать запись следующего байта/слова и при этом дать возможность другим прерываниям на приоритетную обработку, то тут, как мне кажется, проще в самом прерывании окончания записи байта разрешить более приоритетные прерывания путем воздействия на механизмы запрета самих же прерываний, но никак не на статусные биты контроллера. Опять же, такой вариант исходит из того, что у Вас изначально приоритет EEPROM IRQ был самым высоким по каким-то причинам.


            По третьему. Вы можете однозначно сказать, когда происходит переход по вектору вложенного прерывания, до или после "ручного" изменения битов приоритета CC? Это событие нужно отследить при разных программных приоритетах EEPROM IRQ.


            Мне кажется, я начал улавливать причину такого поведения. Пока Вы разруливаете приоритет прерываний программным способом, а восстановлением состояния CC занимается IRET, то все происходит как положено. Но как только начинается несанкционированное изменение статусных битов, выполнение инструкции IRET приводит к неоднозначному поведению.


            1. Vedga
              27.03.2017 15:19

              По второму. Во многих ОС РВ используется концепция отложенного прерывания. Да и вообще, живой пример: внутри прерывания нужно выполнить атомарную последовательность инструкций (захват разделяемого ресурса?) — делаем с приоритетом I1 == I0 == 1, затем выполнить работу с конкретным оборудованием (ex. «записали данные в порт» — делаем с приоритетом оборудования), затем выполняем обработку в режиме «отложенного прерывания» (ex. «проверяем условие на завершение блочной операции, устанавливаем параметры пробуждения основного процесса и т.п.» — приоритет основного кода или любой другой, в котором все прочие прерывания являются разрешенными). Чем меньше мы сидим в режиме, когда хоть какие-нибудь прерывания запрещены, тем лучше будет реакция системы на события.

              По третьему. Для своей задачи со своим конкретным камнем я нашел конкретные workarounds. Но этого нет в errata/reference/datasheet. Что стоило мне 2 недели рабочего времени. Это не нормально. Статья для того, чтобы другие не наступали на те же самые грабли. Плюс просьба проверить описанное поведение на других кристаллах семейства.

              А что такое «несанкционированное изменение статусных битов»? Где в reference есть прямой запрет на изменение I1/I0 внутри ISR? Может быть мне еще запретят завершать ISR любым другим способом, кроме как по команде IRET?


              1. armature_current
                27.03.2017 18:59

                Ваш живой пример прекрасно работает при указанном workaround #3, т.е. через настройку программных приоритетов. Если я правильно понял, контекст задачи заключается как раз в том, что сначала нужно выполнить очень приоритетные инструкции в прерывании, а потом идет череда второстепенных. Вот эти второстепенные, но все в том же прерывании, могут быть "отложены" на время работы с оборудованием. При этом программный приоритет прерывания настроен на самый высший уровень. Верно?


                Где в reference есть прямой запрет на изменение I1/I0 внутри ISR? Может быть мне еще запретят завершать ISR любым другим способом, кроме как по команде IRET?

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


                Тем не менее, спасибо Вам за занимательные грабли. Будем иметь в виду. Могу по случаю проверить на STM8S003 или STM8L052C6, тем более что намечается разработка с пониженным энергопотреблением. До осциллографа надо только донести


  1. amphasis
    25.03.2017 10:25

    IMHO, на самом деле контроллер ведет себя в соответствии с текущими настройками: установлен бит AL? переходим в WFI сразу после RETI.
    А если попробовать сбросить AL перед включением вложенных прерываний и установить его перед выходом из прерывания, чисто ради эксперимента?


    1. Vedga
      25.03.2017 10:29

      Но это не нормально, т.к. WFI находится не в обработчике ISR EEPROM, а в основной программе (см. workaround #2 и #1). И почему тогда работает workaround #3? По-моему либо баг, либо слабо документированная фича ;'(.


      1. amphasis
        25.03.2017 10:40

        Тут у меня параллельно возникли вопросы:
        1. Для чего вообще используете WFI, задача не позволяет использовать Active-Halt?
        2. Почему не хотите возвращаться к исполнению основного кода? По моему опыту это экономия на спичках в плане энергосбережения.

        Из собственного опыта, я сравнивал работу одного и того-же устройства на STM8L с разными вариантами реализации энергосбережения, в том числе low power run. Так вот, наименьшее потребление показала прошивка, работающая на максимальной частоте контроллера, уходящая в Active-Halt для ожидания прерываний. При чем разница по потреблению даже с LPR была в разы!


        1. olartamonov
          25.03.2017 11:43

          Так а что тут удивительного? В Halt (Stop у кортексов) у контроллера останавливается тактовая, потребляет он почти нисколько. В активном режиме потребление от частоты зависит нелинейно, поэтому, если там активные вычисления, а не тупое ожидание какой-нибудь периферии, максимум энергоэффективности обычно и попадает на досаточно высокие частоты, а не на LPR.

          Вот только на этой неделе занимались измерением потребления на задачке «раз в 100 мс просыпаемся, немного работаем, засыпаем». У STM32L1 оптимум оказался на частоте 2 или 4 МГц MSI.

          Wait (WFI/WFE) же на STM8L нужен, если нужна работа периферии — она в Wait тактируется, в Halt нет.


          1. amphasis
            25.03.2017 12:43

            Ну, в таком случае остается два варианта:
            1. Сохранять GCR.AL при входе в обработчик перед включением вложенных прерываний и восстанавливать его перед выходом (не забыв перед этим прерывания выключить).
            2. Возвращаться в основной цикл и снова делать там WFI

            Что будет лучше, сложно сказать, не зная конкретной задачи


            1. olartamonov
              25.03.2017 14:01

              Ну, я не автор, задача не моя, у нас вообще ничего на STM8 в продакшене нету (хотя зачем-то я его смотрел недавно, но хоть убей, не помню, зачем).

              У нас вон тоже недавно было весело. STM32L, уходим в STOP по WFI, выходим, инициализируем тактовые, восстанавливаем периферию, идём пересчитывать частоту USART — потому как есть вариант, при котором в сон уходили на одной частоте, а по выходу перестроились на другую.

              И вот если у USART ножки выведены в один порт, то всё хорошо. А если в другой — то при записи в BRR тут же кровь, кишки, хардфолт.

              Умом понять трудно, но выключить USART перед записью в BRR и включить сразу после помогает.

              Так что в контроллерах трудно чем-то удивляться. Непродуктивно.


        1. Vedga
          27.03.2017 09:22

          (1) Задача не позволяет использовать active-halt.
          (2) Да фиг с этим исполнением основного кода. Просто обнаруженное поведение не описано ни в reference, ни в errata. Вот я и спросил, «это у одного меня такие глюки, или сие есть баг/фича камня?».


          1. amphasis
            27.03.2017 13:14

            не описано ни в reference, ни в errata

            Тут согласен, использование Activation Level и Nested Interrupts описываются в разных подразделах и их совместное использование нигде не упоминается. А чем вам не понравился Workaround #3?


            1. Vedga
              27.03.2017 13:40

              Ну так я сейчас его и использую. Вот только обидно за убитые 2 недели по отлову этой ситуации.

              P.S. olartamonov, а ты еще не любишь «arduino» в коммерческих проектах ;). Для atmel (имеется ввиду именно работа с голым железом, а не C++ обертка для чайников) пишешь, заглядывая одним глазком в datasheet, и все работает с первого раза. Когда у них крайний раз было обнаружено несоответствие описания функционированию реального железа? А STM-железо все-таки очень сырое. Несмотря на большое количество аппаратных плюшек.


      1. armature_current
        25.03.2017 14:46

        WFI находится не в обработчике ISR EEPROM, а в основной программе

        А какая разница, при каком значение PC и GCR была выполнена последовательность AL=1 и WFI? У вас выполняется IRET во вложенном прерывании, восстанавливается CC с I1 = 1 и I0 = 0, что при установленном AL приводит к спячке


        1. Vedga
          27.03.2017 09:27

          Аналогичная ситуация происходит и в случае понижения приоритета кода в ISR EEPROM до любого другого значения, отличного от I1 == I0 == 1.
          Про безусловное засыпание при IRET с учетом только приоритета кода и без учета PC я уж вообще молчу. Просыпаться-то как будем? Восстановим регистры из стека и с таким набором продолжим обработку прерванной ISR?


          1. armature_current
            27.03.2017 14:32

            Аналогичная ситуация происходит и в случае понижения приоритета кода в ISR EEPROM до любого другого значения, отличного от I1 == I0 == 1.

            Ну вот теперь уже понятнее. То есть при выходе из вложенного прерывания в состояние с любым меньшим приоритетом происходит возврат в сон. Такое поведение выглядит действительно, мягко говоря, "странноватым" и как минимум должно быть обоснованным в документации. Поймите правильно, мне здесь не нравится только оговорка про "любое меньшее". Но мне вполне логичным кажется такое поведение при возврате в состояние с наименьшим приоритетом.


            А по поводу PC, что Вас смущает? В спячку контроллер возвращается после выполнения RETI (при установленном AL), а значит PC должен выгрузиться из стека до остановки тактирования. При возобновлении тактирования содержимое PC вполне определенное, и именно то, которое было до входа в последнее прерывание.


  1. armature_current
    27.03.2017 14:32

    del