Критические разделы на Cortex-M3

Мне интересно немного о реализации критических разделов кода на Cortex-M3, где исключения не допускаются из-за ограничений времени или проблем с параллелизмом.

В моем случае я запускаю LPC1758, и у меня есть трансивер TI CC2500 на борту. CC2500 имеет контакты, которые могут использоваться в качестве линий прерываний для данных в буфере RX и в свободном пространстве в буфере TX.

В качестве примера, я хочу иметь буфер TX в SRAM моего MCU и когда в буфере TX трансивера есть свободное пространство, я хочу записать эти данные там. Но процедура, которая помещает данные в буфер SRAM, очевидно, не может быть прервана прерыванием свободного пробела в TX. Поэтому я хочу временно отключить прерывания при выполнении этой процедуры заполнения этого буфера, но при этом все прерывания, возникающие во время этой процедуры, выполняются после завершения.

Как это делается лучше всего на Cortex-M3?

10 голосов | спросил Emil Eriksson 26 Jpm1000000pmThu, 26 Jan 2012 13:08:46 +040012 2012, 13:08:46

3 ответа


11

Cortex M3 поддерживает полезную пару операций (обычную для многих других машин), называемую Load-Exclusive (LDREX) и «Store-Exclusive» (STREX). Понятно, что операция LDREX выполняет нагрузку, а также устанавливает специальное оборудование для проверки того, может ли загруженное место быть записано чем-то другим. Выполнение STREX по адресу, используемому последним LDREX, приведет к тому, что этот адрес будет записан , только если ничего не написал ранее . Инструкция STREX загрузит регистр с 0, если хранилище произошло, или 1, если оно было прервано.

Обратите внимание, что STREX часто пессимистичен. Существует множество ситуаций, когда он может решить не выполнять работу магазина, даже если рассматриваемое местоположение на самом деле не было затронуто. Например, прерывание между LDREX и STREX приведет к тому, что STREX будет считать, что наблюдаемое местоположение может быть поражено. По этой причине обычно рекомендуется минимизировать количество кода между LDREX и STREX. Например, рассмотрим следующее:

inline void safe_increment (uint32_t * addr)
{
  uint32_t new_value;
  делать
  {
    new_value = __ldrex (addr) + 1;
  } while (__ strex (new_value, addr));
}

, который компилируется примерно так:

; Предположим, что R0 содержит указанный адрес; r1 trashed
LP:
  ldrex r1, [r0]
  добавить r1, r1, # 1
  strex r1, r1, [r0]
  cmp r1, # 0; Проверьте, нет ли ненулевой
  bne lp
  .. код продолжается

В подавляющем большинстве случаев код выполняется, между LDREX и STREX ничего не произойдет, чтобы «потревожить» их, поэтому STREX преуспеет без дальнейших церемоний. Если, однако, происходит прерывание сразу после инструкции LDREX или ADD, STREX не будет выполнять сохранение, но вместо этого код вернется, чтобы прочитать (возможно обновленное) значение [r0] и вычислить новое добавочное значение основываясь на этом.

Использование LDREX /STREX для создания таких операций, как safe_increment, позволяет не только управлять критическими разделамиm, но также и во многих случаях, чтобы избежать необходимости в них.

ответил supercat 26 Jpm1000000pmThu, 26 Jan 2012 20:25:47 +040012 2012, 20:25:47
4

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

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

Кодирование фона (обработка прерываний) потребляет данные от указателя чтения и увеличивает указатель чтения.

Когда указатели чтения и записи равны, буфер пуст, а фоновый процесс не отправляет данные. Когда буфер заполнен, процесс переднего плана отказывается писать больше (или может перезаписывать старые данные, в зависимости от ваших потребностей).

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

ответил Toby Jaffey 26 Jpm1000000pmThu, 26 Jan 2012 17:12:35 +040012 2012, 17:12:35
2

Я не могу вспомнить точное местоположение, но в библиотеках, которые поступают из ARM (Not TI, ARM, это должно быть в CMSIS или что-то в этом роде, я использую ST, но я помню, где-то читал, что этот файл поступает из ARM, поэтому вы должны иметь это также) существует глобальная опция отключения прерывания. Это вызов функции. (Я не работаю, но завтра буду искать точную функцию). Я бы обернул это с красивым именем в вашей системе и отключил прерывания, снова выполнил вашу задачу и включил. Сказав это, лучшим вариантом будет реализация семафора или структуры очереди вместо глобального отключения прерываний.

ответил Ktc 26 Jpm1000000pmThu, 26 Jan 2012 19:41:56 +040012 2012, 19:41:56

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132