Изменение приоритета текущего прерывания в NVIC
У меня есть загадка. Часть, которую я использую (NXP KL27, Cortex-M0+), имеет ошибки в периферийном устройстве I2C, так что во время приема отсутствует управление потоком. В результате это должно быть прерывание с высоким приоритетом. Я также использую UART, который по своей асинхронной природе не имеет управления потоком при получении. В результате это должно быть прерывание с высоким приоритетом.
Круговой приоритет
Прерывание I2C должно иметь более высокий приоритет, чем прерывание UART, в противном случае входной байт может быть уничтожен в регистре сдвига перед чтением. Это действительно не должно работать таким образом, но это опечатки, и поэтому он должен иметь более высокий приоритет.
Прерывание UART должно иметь более высокий приоритет, чем прерывание I2C, потому что для закрытия транзакции I2C драйвер (из KSDK NXP) должен установить флаг и дождаться бита состояния. Во время этого ожидания входящие символы в UART могут переполнять не сдвинутый регистр FIFO.
Пытаясь решить проблему с UART, я обнаружил эту круговую зависимость. Первоначальная проблема: символы исчезали из приема UART и был установлен флаг переполнения. При смене приоритетов UART был непоколебим, не пропуская ни одного символа, но транзакции I2C в конечном итоге зависали из-за переполнений.
Возможное решение
Решение, которое я придумал, включает в себя изменение приоритетов прерываний на лету. Когда драйвер I2C закрывает транзакцию, он не получает, что означает, что ошибки, из-за которых байты поступают неуправляемо, не являются проблемой. Я хотел бы понизить приоритет прерывания I2C в NVIC в течение этого времени, чтобы UART мог иметь приоритет над ним, тем самым делая UART счастливым (и не пропуская никаких символов).
Вопрос
Я не смог найти ничего из ARM, в котором указано, будет ли изменение приоритета прерывания во время выполнения этого прерывания вступать в силу немедленно, или если приоритет текущего прерывания был зафиксирован, когда он начал выполняться. Я надеюсь, что кто-то определенно может спасти от глубины своих знаний об архитектуре или из опыта, что изменение приоритета вступит в силу немедленно или нет.
Другие возможные решения
Существует ряд других возможных решений и причин, по которым они нежелательны. Рефакторинг драйвера I2C для обработки цикла в контексте процесса, а не в контексте прерывания, будет значительным усилием, копаясь в коде поставщика, и влияет на код приложения, который вызывает его. Использование DMA для любой из этих периферийных устройств расходует нетривиальное количество доступных каналов DMA и накладывает накладные расходы на настройку DMA для каждой транзакции (а также влияет на код приложения, который вызывает драйверы).
Я открыт для других решений, но не решаюсь идти по любому пути, который вызывает значительные изменения в коде поставщика.
Тестовое задание
У меня есть идея для эксперимента, чтобы проверить, как NVIC работает в этом отношении, но я подумал, что сначала проверю здесь. Если я доберусь до эксперимента, я опубликую ответ с результатами.
2 ответа
С архитектурной точки зрения это кажется НЕПРЕДВИДИМЫМ (изменение приоритета в настоящее время активного исключения). Кажется, что нет никакой логики для обеспечения более согласованного поведения (т. Е. Логика регистрации, о которой вы беспокоитесь, явно отсутствует в M0/M0+).
Это означает, что если вы проверяете эффективность своего обходного пути, он, вероятно, будет работать - и в вашем ограниченном сценарии он может быть эффективным. Тем не менее, нет никакой гарантии, что один и тот же код будет работать на M3 или что он работает надежно во всех сценариях (например, при любом взаимодействии с отладкой). Вы могли бы даже наблюдать какое-то совершенно непредсказуемое поведение углового случая, но ограниченное по площади
Это указано как непредсказуемое в разделе B1.5.4 ARM v6-M ARM.
Для v7-M (B1.5.4, Приоритеты исключений и приоритет)
Это определение приоритета выполнения означает, что обработчик исключений может выполняться с приоритетом, который выше, чем приоритет соответствующего исключения. В частности, если обработчик уменьшает приоритет соответствующего исключения, приоритет выполнения падает только до приоритета вытесненного исключения с наивысшим приоритетом. Следовательно, уменьшение приоритета текущего исключения никогда не разрешает:
Исключенное исключение для вытеснения текущего обработчика исключений.
Инверсия приоритета выгружаемых исключений.
Аспект v7-M проясняет некоторые из сложных сценариев, которых следует избегать, если вы пытаетесь использовать непредсказуемое поведение, которое вы определили как полезное для части M0 +.
Одним из альтернативных решений может быть передача другого прерывания с более низким приоритетом на вторую половину обработки. Хорошим кандидатом является прерывание PendSV (если оно еще не используется), которое может (только) запускаться из программного обеспечения.
Для более подробного объяснения см. Этот ответ на аналогичный вопрос и этот ответ о PendSV в целом.
эксперимент
Сегодня я написал небольшой эксперимент, чтобы проверить это на моем конкретном варианте Cortex M0+. Я оставляю это как неприемлемый ответ, и я считаю, что ответ @Sean Houlihane является наиболее правильным (то есть непредсказуемым). Я все еще хотел проверить поведение и сообщить об этом при определенных обстоятельствах, пока я его использую.
Эксперимент проводился на плате FRDM-KL43Z. Он имеет красный светодиод, зеленый светодиод и две кнопки. Приложение выполнило некоторую настройку GPIO и прерываний, а затем приняло бесконечный цикл.
Кнопка 1: обработчик прерываний кнопки 1 был инициализирован для приоритета среднего уровня (0x80). На каждом падающем фронте кнопки 1 ожидание прерывания. Это прерывание будет переключать состояние зеленого светодиода.
Кнопка 2: обработчик прерываний кнопки 2 был инициализирован с приоритетом среднего уровня (0x80), но будет изменен как часть выполнения. Обработчик прерываний кнопки 2 запускает цикл, который длится приблизительно 8 секунд (две фазы по четыре), повторяясь бесконечно. Он включит красный светодиод и уменьшит свой собственный приоритет ниже, чем у кнопки 1. Через четыре секунды он выключит красный светодиод и увеличит свой собственный приоритет выше, чем у кнопки 1. Через четыре секунды он повторится.
Ожидаемые результаты
Если гипотеза подтвердится, когда красный светодиод включен, нажатие кнопки 1 переключит зеленый светодиод, а когда красный светодиод выключится, нажатие кнопки 1 не будет действовать до тех пор, пока красный светодиод не погаснет. Прерывание кнопки 1 не будет выполняться до тех пор, пока непрерывное прерывание кнопки 2 не будет иметь более низкий приоритет.
Результаты
Это скучный раздел. Все, что я ожидал в предыдущем разделе, произошло.
Заключение
Для экспериментальной установки (NXP KL43Z Cortex M0+) изменение приоритета прерывания текущего выполняющегося прерывания вступает в силу, когда прерывание выполняется. В результате мой хакерский обходной путь понижения приоритета во время занятого ожидания и восстановления его после должен работать так, как мне нужно.
Изменить: болеепоздние результаты
Хотя эксперимент был успешным, проблемы начали возникать после того, как был обойден исходный вопрос. Взаимодействие между обработчиками UART и I2C было относительно непротиворечивым, но третье периферийное устройство стало очень странным в своем обработчике прерываний. Обратите внимание на предупреждение о непредсказуемости.