2015-01-17 3 views
1

В настоящее время я портирую свой DCF77 library (вы можете найти source code at GitHub) от Arduino (на базе AVR) до Arduino Due (ARM Cortex M3). Я абсолютно новичок с платформой ARM.Атомный блок для чтения против ARM SysTicks

С помощью Arduino, основанного на AVR, я могу использовать avr-libc для получения атомных блоков. В основном это блокирует все прерывания во время блока и позволяет прерываниям позже снова. Для AVR это было хорошо. Теперь для ARM Cortex вещи начинают усложняться.

Прежде всего: для использования в настоящее время библиотеки этот подход будет работать. Итак, мой первый вопрос: есть ли аналогичные макросы «ATOMIC» avr-libc для ARM? Очевидно, другие люди думали о something in this directions. Поскольку я использую gcc, я мог бы улучшить эти макросы, чтобы работать почти так же, как avr-libv ATOMIC macors. Я уже нашел некоторый CMSIS documentation, но это похоже только на макрос «enable_irq» вместо макроса «restore_irq».

Вопрос 1: есть ли там библиотека (для gcc), которая уже делает это?

Поскольку ARM имеет разные приоритетные прерывания, я мог бы установить атомарность по-разному. В моем случае «атомные» блоки должны только удостовериться, что они не прерываются прерыванием systick. Так что на самом деле мне не нужно было бы блокировать все, чтобы мои блоки были «достаточно атомными». В дальнейшем я нашел ARM synchronization primitives article in the developer infocenter. Особенно есть намек на lockless programming. Согласно этой статье, это передовая концепция и что на ней много публикаций. Поиск в сети я нашел только общие объяснения концепции, например. here. Я предполагаю, что беззаконная реализация будет очень крутой, но в это время я чувствую себя недостаточно уверенно в ARM, чтобы реализовать это с нуля.

Вопрос 2: Есть ли у кого-нибудь какие-то подсказки для меня на блокированных чтениях блоков памяти на ARM Cortex M3?

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

Вопрос 3: Есть ли хороший способ блокировки прерываний sysTick без потери тиков?

Я также нашел CMSIS documentation for semaphores. Однако я несколько ошеломлен. Особенно мне интересно, следует ли мне использовать CMSIS и как это сделать на Arduino Due.

Вопрос 4: Какой будет мой лучший вариант? Или где я должен продолжать читать?

Частичный ответ: с намеком из Notlikethat я реализовал

#if defined(ARDUINO_ARCH_AVR) 
    #include <util/atomic.h> 
    #define CRITICAL_SECTION ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 

#elif defined(ARDUINO_ARCH_SAM) 
    // Workaround as suggested by Stackoverflow user "Notlikethat" 
    // http://stackoverflow.com/questions/27998059/atomic-block-for-reading-vs-arm-systicks 

    static inline int __int_disable_irq(void) { 
     int primask; 
     asm volatile("mrs %0, PRIMASK\n" : "=r"(primask)); 
     asm volatile("cpsid i\n"); 
     return primask & 1; 
    } 

    static inline void __int_restore_irq(int *primask) { 
     if (!(*primask)) { 
      asm volatile ("" ::: "memory"); 
      asm volatile("cpsie i\n"); 
     } 
    } 
    // This critical section macro borrows heavily from 
    // avr-libc util/atomic.h 
    // --> http://www.nongnu.org/avr-libc/user-manual/atomic_8h_source.html 
    #define CRITICAL_SECTION for (int primask_save __attribute__((__cleanup__(__int_restore_irq))) = __int_disable_irq(), __ToDo = 1; __ToDo; __ToDo = 0) 

#else 
    #error Unsupported controller architecture 
#endif 

Этот макрос делает более или менее то, что мне нужно. Однако я нахожу, что есть место для улучшения, поскольку это блокирует все прерывания, хотя было бы достаточно блокировать только систолы. Так что вопрос 3 по-прежнему открыт.

+1

Я уверен, что 'asm volatile (" mrs% 0, PRIMASK \ n "::" r "(primask));' Плохая сборка, потому что она отмечает переменную primask как вход вместо вывода, что приведет к тому, что GCC будет повторно использовать регистр. Я видел серьезные недостатки. Исправлено: 'asm volatile (" mrs% 0, PRIMASK \ n ":" = r "(primask));' –

ответ

4

В большинстве случаев вы ссылаетесь на синхронизацию доступа к памяти между несколькими процессорами или предварительно запланированные потоки на одном CPU, что кажется совершенно неуместным с учетом заявленной ситуации.«Атомность» в этом смысле относится к тому, чтобы гарантировать, что когда один наблюдатель , обновляющий память, любой наблюдатель память считывания видит либо начальное состояние, либо обновленное состояние, но между ними ничего не происходит.

«атомарность» по отношению к прерываниям по тому же принципу - то есть, обеспечивая, что если происходит прерывание, последовательность кода либо не работает вообще, или работать полностью - но это концептуально разные вещи , Есть только две вещи, гарантированные атомным w.r.t. прерывания: одна команда или последовательность инструкций, выполняемых с отключенными прерываниями.

«Право» способ добиться того, что на самом деле с помощью CPSID/CPSIE инструкции, которые обернуты в __disable_irq()/__enable_irq() встроенных функций. Обратите внимание, что в системе есть две «стадии» обработки прерываний: сам ядро ​​M3 имеет только один сигнал IRQ - это внешнее задание NVIC выполнять всю маршрутизацию/мультиплексирование/приоритизацию IRQ системы в эту одну строку. Когда ЦП хочет войти в критический раздел, все, что ему нужно сделать, это замаскировать свой собственный вход IRQ с помощью CPSID, выполнить то, что ему нужно, а затем разоблачить CPSIE, после чего будет немедленно отправлено любое ожидающее прерывание от NVIC.

В случае критических разделов с вложенными/повторными входами внутренняя среда предоставляет удобную форму int __disable_irq(void), которая возвращает предыдущее состояние, поэтому вы можете условно разоблачить это.

Для других компиляторов, которые не предлагают такие встроенные функции, достаточно просто свернуть свой собственный, например:

static inline int disable_irq(void) { 
    int primask; 
    asm volatile("mrs %0, PRIMASK\n" 
       "cpsid i\n" : "=r"(primask)); 
    return primask & 1; 
} 

static inline void enable_irq(int primask) { 
    if (primask) 
     asm volatile("cpsie i\n"); 
} 

[1] Один запутанным перекрытие последнего смысла часто используется для добиться первого в однопроцессорной многозадачности - если прерывания отключены, ни один другой поток не может быть запланирован до тех пор, пока вы не закончите, поэтому никогда не увидите частично обновленную память.

[2] За возможным исключением загрузки/сохранения-кратно инструкций - в конфигурации с низкой задержкой прерываний, эти может быть прервана, и либо перезапустить, либо продолжить после возвращения.

+0

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

+0

Это было то, что я, очевидно, отсутствовал. Большое спасибо! –

+0

К сожалению, я не пропустил этот момент. Если я пытаюсь получить доступ к __disable_irq(), я получаю сообщение об ошибке: значение void не игнорируется, поскольку оно должно быть для (uint8_t was_masked __attribute __ ((__ cleanup __ (__ iRestore))) = __disable_irq(), __ToDo = 1; __ToDo; __ToDo = 0) Очевидно, компилятор предполагает, что __disable_irq() вернет void. –

Смежные вопросы