Ленивое объединение FPU в Cortex-M4F

Я пишу код многопоточности для Cortex M4F. Все работает, и теперь я пытаюсь сделать переключение контекста FPU более эффективным с помощью ленивого стекирования.

Я прочитал AN298 и I. реализовал альтернативный подход, основанный на отключении FPU и обработке UsageFault, но нижние (S0-S15) регистры не сохраняются / не восстанавливаются правильно аппаратными средствами. Я думаю, что проблема заключается в рисунке 11:

Рисунок 11: переключение контекста

В соответствии с этим, когда PendSV запускается, FPCAR должен указывать на пространство, зарезервированное в стеке задачи A. Но, как я понимаю, поскольку CONTROL.FPCA находится на высоком уровне в задаче C, FPCAR будет обновлен, чтобы указывать на стек задачи C при входе в PendSV. Если это так, S0-S15 и FPSCR будут сохранены в стеке задачи C, а не задачи A, что, конечно, неверно.

Я что-то упустил или неправильно написано?

Кстати, я проверил несколько ОСРВ с открытым исходным кодом. FreeRTOS и mbed RTOS всегда складываются S16-S31 во время переключения контекста, что приводит к автоматическому S0-S15 стеку, то есть они используют отложенное стекание только для уменьшения задержки прерывания, но обеспечивают полное сохранение состояния для задач (как в первом подходе, описанном в приложении). Порт TNKernel для M4F использует подход UsageFault, но полностью сохраняет / восстанавливает S0-S31 с помощью программного обеспечения, эффективно обходя любую проблему с FPCAR (за счет 48 загрузок / сохранений вместо 32, 16 аппаратных перезаписываются при восстановлении). Кажется, что никто не использует подход UsageFault, сохраняя только S16-S31.

(Кстати, это также размещено в сообществе ARM, но многие вопросы, кажется, остаться без ответа там. Если я получу там ответ, я воспроизведу его и здесь)


person Andrea Biondo    schedule 27.07.2016    source источник
comment
Из приложения Примечание: регистр FPCAR указывает на часть пространства стека в текущем стеке ... Таким образом, он должен указывать на стек предварительно очищенной задачи.   -  person D Krueger    schedule 27.07.2016
comment
@DKrueger Совершенно верно. Но рисунок подразумевает, что он все еще указывает на кадр задачи A, когда задача C вытесняется PendSV. Вот почему я запутался, я не знаю, неправильно ли я это понял или что-то не так в заявке.   -  person Andrea Biondo    schedule 27.07.2016
comment
Я думаю, это указывает на то, что FPCAR не будет обновляться, когда FPU отключен. Вот почему он продолжает указывать на стек задачи A до тех пор, пока задача C не будет прервана с включенным FPU. Сохранение контекста FPU может быть отложено до тех пор, пока другая задача не потребует FPU.   -  person D Krueger    schedule 27.07.2016
comment
@DKrueger У вас очень хорошая гипотеза. В приложении говорится, что обновление FPCAR зависит от FPCA, без упоминания о состоянии FPU (логика тоже была бы немного сложной, учитывая, что ее также можно включить только для привилегированного кода и т. Д.). Но я попробую утром.   -  person Andrea Biondo    schedule 27.07.2016
comment
@DKrueger FPCAR обновляется даже при отключенном FPU, я это тестировал. Я был в отпуске и не ответил, но теперь понял, как это делать правильно.   -  person Andrea Biondo    schedule 08.08.2016


Ответы (1)


На это потребовалось время, но в итоге я понял, как это сделать максимально эффективно.

Во-первых, это неверное приложение. Мое первоначальное объяснение того, как обновляется FPCAR, верно. Обратите внимание, что FPCAR обновляется, даже когда FPU отключен. Кроме того, путем тестирования я определил, что FPCAR действительно всегда указывает на прерванный стек.

Мой первый подход заключался в манипулировании FPCAR, LSPACT и EXC_RETURN вместе с ожидающим PendSV UsageFault. Конечно, для этого важно, чтобы FPCAR манипуляции не считались операцией FPU с точки зрения ленивого стекирования. Когда документации нет, мы можем только взломать ответы из ЦП ...

LDR  R2, =0xE000EF38
LDR  R3, =0xDEADBEEF
STR  R3, [R2]
VSTM R1, {S16-S31}
UDF

FPCAR находится на 0xE000EF38. VSTM является частью процедуры сохранения контекста. Идея состоит в том, что если FPCAR манипуляция является операцией FPU, ленивое стекирование остановит FPCAR хранилище и будет успешным, поскольку FPCAR все еще действует. Это приведет к ошибке UDF. В противном случае на VSTM с поврежденным FPCAR произойдет отложенное стекирование, что приведет к сбою шины.

Действительно, у меня неисправность автобуса. Ура! Я повторил тест с действующим адресом: неисправностей нет, работает отлично. Так что экономить достаточно просто. Для восстановления требуется ожидающий PendSV и манипулирование FPCAR, LSPACT и EXC_RETURN внутри него, чтобы вызвать S0-S15 восстановление текущего потока при возврате исключения. Проблема здесь в том, что вы не можете сохранить состояние текущего потока в его стеке, так как он будет отключен. Копирование неэффективно, поэтому лучше всего указать FPCAR на постоянное состояние TCB вместо сохранения состояния, созданного ЦП.

Это становится довольно сложным, это требует выполнения PendSV после UsageFault, и у него довольно много угловых случаев и гонок. Есть способ получше.

Подход, который я в итоге использовал, полностью работает внутри UsageFault и обходит аппаратное стекание, не теряя при этом эффективности. После включения FPU и определения необходимости переключения контекста FPU, я:

  1. Установите LSPACT в ноль;
  2. Сохранение / восстановление полного S0-S31 состояния в / из TCB;
  3. Установите LSPACT обратно в единицу.

Делая это, я могу работать со всем S0-S31 состоянием, не отвлекаясь от ленивого стекирования, потому что ЦП считает, что уже сложил контекст, поскольку LSPACT равно нулю. Это, конечно, зависит от того, что обработчик UsageFault не использует операции FPU за пределами сохранения / восстановления и не вытесняется обработчиками ISR, использующими FPU, что является довольно тривиальным предположением, учитывая, что его ASM с ручным кодированием и обработчики ошибок не могут быть вытеснены ISR. Я также попытался отключить ленивое накопление с помощью _28 _ / _ 29_ вместо работы с LSPACT, но, похоже, он не работает (он по-прежнему вызывает ленивое накопление, что подтверждается установкой недопустимого FPCAR).

С точки зрения эффективности это так же эффективно, как и аппаратное стекирование. Если бы я хотел придираться, это экономит один цикл, так как мне не нужно записывать увеличенный указатель.

Кстати, я включил первый подход, хотя я не стал его использовать, потому что я думаю, что в нем есть некоторая полезная информация, если кто-то еще ее ищет.

person Andrea Biondo    schedule 08.08.2016