Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Библиотека атомарных операций для STM32
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > ARM, 32bit
Страницы: 1, 2
ArtDenis
Кто-нибудь встречал в природе готовую библиотеку для атомарных операций над целочисленными значениями? Интересуют стандартные операции типа "сравнить и обменять", арифметические операции (сложить и вернуть предыдущее значение), битовые и т.д.
Gleb80
Атомарная, тоесть выполняющаяся за один машинный цикл? Может лучше использовать ассемблер для конкретного контроллера?
AlexandrY
Цитата(ArtDenis @ Jun 18 2015, 19:31) *
Кто-нибудь встречал в природе готовую библиотеку для атомарных операций над целочисленными значениями? Интересуют стандартные операции типа "сравнить и обменять", арифметические операции (сложить и вернуть предыдущее значение), битовые и т.д.


STM32 мало чем отличается от других чипов на архитектуре ARMv7-M
поэтому вам подойдет и такое - http://mintomic.github.io/

Кстати, спасибо что напомнили. Самому уже надоело запрещение прерываний расставлять по всем углам.
В том проекте только один файл и интересен. Это - https://github.com/mintomic/mintomic/blob/m.../mintomic_gcc.c
Там несколько примеров ассемблерных функций сравнения, сложения и логические, но и их хватает чтобы полностью понять тему.
Aner
QUOTE (AlexandrY @ Jun 18 2015, 23:27) *
STM32 мало чем отличается от других чипов на архитектуре ARMv7-M
поэтому вам подойдет и такое - http://mintomic.github.io/

Отличия то есть поскольку STM32 это Cortexы не совсем ARMv7-M. То что подойдет, это да.
AHTOXA
Цитата(ArtDenis @ Jun 18 2015, 21:31) *
Кто-нибудь встречал в природе готовую библиотеку для атомарных операций над целочисленными значениями? Интересуют стандартные операции типа "сравнить и обменять", арифметические операции (сложить и вернуть предыдущее значение), битовые и т.д.

Не то, чтобы библиотека, но часть примитивов там точно есть: тынц.
AlexandrY
Цитата(AHTOXA @ Jun 18 2015, 23:40) *
Не то, чтобы библиотека, но часть примитивов там точно есть: тынц.


Ну это уж совсем низкий уровень.
Там в SoC-ах на ARM Cortex столько нюансов, что писать это на C значит просто "прострелить себе ногу".
Лучше тогда дать ссылку на первоисточник: http://infocenter.arm.com/help/index.jsp?t...008a/index.html
Где выяснится, что реализация эксклюзивных мониторов зависит от производителя SoC-а.
От него же зависит и такая вещь как Exclusives Reservation Granule.
Совершенно темной остается тема конфликтов с DMA.
Помнить надо и о типе памяти (normal, device, ordered), т.е. совершенно четко его знать. И проч.
jcxz
Вот моя реализация инкремента/декремента, обмена и сравнения-обмена для разных типов:
CODE
PUBLIC _Z9AtomicIncPVh, _Z9AtomicDecPVh
PUBLIC _Z10AtomicSwapPVhj, _Z10AtomicSwapPVtj _Z10AtomicSwapPVjj
PUBLIC _Z13AtomicCmpSwapPVhjj, _Z13AtomicCmpSwapPVtjj, _Z13AtomicCmpSwapPVjjj

;uint AtomicInc(u8 volatile *);
_Z9AtomicIncPVh:
aInc8_01: LDREXB R3, [R0]
ADDS R1, R3, #1
STREXB R2, R1, [R0]
CMP R2, #0
BNE aInc8_01
MOV R0, R3
BX LR

;uint AtomicDec(u8 volatile *);
_Z9AtomicDecPVh:
aDec8_01: LDREXB R3, [R0]
SUBS R1, R3, #1
STREXB R2, R1, [R0]
CMP R2, #0
BNE aDec8_01
MOV R0, R3
BX LR

;АтомарнаЯ операциЯ "обмен" длЯ типа u8.
;uint AtomicSwap(u8 volatile *, uint);
_Z10AtomicSwapPVhj:
aSwap8_01: LDREXB R3, [R0]
STREXB R2, R1, [R0]
CMP R2, #0
BNE aSwap8_01
MOV R0, R3
BX LR

;АтомарнаЯ операциЯ "обмен" длЯ типа u16.
;uint AtomicSwap(u16 volatile *, uint);
_Z10AtomicSwapPVtj:
aSwap16_01: LDREXH R3, [R0]
STREXH R2, R1, [R0]
CMP R2, #0
BNE aSwap16_01
MOV R0, R3
BX LR

;АтомарнаЯ операциЯ "обмен" длЯ типа u32.
;u32 AtomicSwap(u32 volatile *, u32);
_Z10AtomicSwapPVjj:
aSwap32_01: LDREX R3, [R0]
STREX R2, R1, [R0]
CMP R2, #0
BNE aSwap32_01
MOV R0, R3
BX LR

;АтомарнаЯ операциЯ "сравнение и обмен" длЯ типа u8.
;uint AtomicCmpSwap(u8 volatile *ptr, uint newVal, uint cmpVal);
_Z13AtomicCmpSwapPVhjj:
aCmpSwap8_01: LDREXB R12, [R0]
CMP R12, R2
ITT EQ
STREXBEQ R3, R1, [R0]
CMPEQ R3, #1
BEQ aCmpSwap8_01
MOV R0, R12
BX LR

;АтомарнаЯ операциЯ "сравнение и обмен" длЯ типа u16.
;uint AtomicCmpSwap(u16 volatile *ptr, uint newVal, uint cmpVal);
_Z13AtomicCmpSwapPVtjj:
aCmpSwap16_01: LDREXH R12, [R0]
CMP R12, R2
ITT EQ
STREXHEQ R3, R1, [R0]
CMPEQ R3, #1
BEQ aCmpSwap16_01
MOV R0, R12
BX LR

;АтомарнаЯ операциЯ "сравнение и обмен" длЯ типа u32.
;u32 AtomicCmpSwap(u32 volatile *ptr, u32 newVal, u32 cmpVal);
_Z13AtomicCmpSwapPVjjj:
aCmpSwap32_01: LDREX R12, [R0]
CMP R12, R2
ITT EQ
STREXEQ R3, R1, [R0]
CMPEQ R3, #1
BEQ aCmpSwap32_01
MOV R0, R12
BX LR

си++ - хидеры:
Код
uint AtomicInc(u8 volatile *);
uint AtomicDec(u8 volatile *);
uint AtomicSwap(u8 volatile *, uint);
uint AtomicSwap(u16 volatile *, uint);
u32  AtomicSwap(u32 volatile *, u32);
uint AtomicCmpSwap(u8 volatile *, uint newVal, uint cmpVal);
uint AtomicCmpSwap(u16 volatile *, uint newVal, uint cmpVal);
u32  AtomicCmpSwap(u32 volatile *, u32 newVal, u32 cmpVal);

Все остальные операции сможете сами реализовать если прочитаете доку о командах LDREX/STREX.
ViKo
Объясните, на какой диапазон памяти распространяется мониторинг LDREX/STREX - на то слово, с которым работаем, или на больший?
AlexandrY
Цитата(jcxz @ Jun 19 2015, 10:48) *
Вот моя реализация инкремента/декремента, обмена и сравнения-обмена для разных типов:


А оно работает?
Тут самое интересное как это тестировалось и на чем или ком. wink.gif
jcxz
Цитата(AlexandrY @ Jun 19 2015, 14:24) *
А оно работает?
Тут самое интересное как это тестировалось и на чем или ком. wink.gif

Работает. На LPC17xx в нескольких проектах, под uCOS. Хотя довольно редко использую.
А что там тестировать? Три строчки...
Совместно с DMA не использую конечно, да и не нужно это. А для взаимодействия между разными задачами ОС и ISR - вполне подходит. И тип памяти никакой роли не играет.
AlexandrY
Цитата(jcxz @ Jun 19 2015, 11:27) *
Работает. На LPC17xx в нескольких проектах, под uCOS. Хотя довольно редко использую.
А что там тестировать? Три строчки...
Совместно с DMA не использую конечно, да и не нужно это. А для взаимодействия между разными задачами ОС и ISR - вполне подходит. И тип памяти никакой роли не играет.


Т.е. даете честное слово? Без тестирования?
Но при этом ни Exclusives Reservation Granule, ни влияние DMA не знаете?

Смысл то этой штуки быть быстрой, а какой толк если задача на ней застрянет пока не исполнится все, что так или иначе использует RAM.
А если есть код в RAM? Вообще капец?
jcxz
Цитата(AlexandrY @ Jun 19 2015, 14:34) *
Но при этом ни Exclusives Reservation Granule, ни влияние DMA не знаете?

Зачем тут DMA??? Это средство синхронизации задач/ISR. Вы задачи между собой при помощи DMA синхронизируете???
Зачем к переменным, служащим для синхронизации задач, обращаться через DMA???

Цитата(AlexandrY @ Jun 19 2015, 14:34) *
Смысл то этой штуки быть быстрой, а какой толк если задача на ней застрянет пока не исполнится все, что так или иначе использует RAM.
А если есть код в RAM? Вообще капец?

Тогда используйте обычный запрет прерываний.
ArtDenis
Цитата(Gleb80 @ Jun 18 2015, 23:47) *
Атомарная, тоесть выполняющаяся за один машинный цикл? Может лучше использовать ассемблер для конкретного контроллера?

Атомарная - это "выглядящая" как одно изменение для кода, который придерживается правил использования атомарных операций.

В общем, судя по ответам, с этим довольно-таки грустно у STM32. Для начала попробую встроенные в gcc функции, которые тут посоветовали: https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gc...c-Builtins.html
Их кто-нибудь проверял для STM32? Насколько надёжно они работают?

Ещё в C++11 в стандартную библиотеку добавлена поддержка атомарности через шаблон std::atomic. Интересно есть ли его реализация для STM32?
AlexandrY
Цитата(ArtDenis @ Jun 19 2015, 15:40) *
Их кто-нибудь проверял для STM32? Насколько надёжно они работают?


Да, не все так гладко.
Недаром в RTOS этот финт не используют. Нет детерминизма.
Фича явно планировалаcь для мультипроцессорных систем, а не для RTOS.
ArtDenis
Может особо не париться с атомарными операциями, и по старинке на короткий промежуток запрещать прерывания? Как я понимаю, если какое-то одно или несколько прерываний разных типов не произошли во время запрета, то после разрешения прерываний, они должны выполнится?
jcxz
Цитата(ArtDenis @ Jun 20 2015, 11:07) *
Как я понимаю, если какое-то одно или несколько прерываний разных типов не произошли во время запрета, то после разрешения прерываний, они должны выполнится?

Конечно. Они будут висеть в регистре запросов не переходя в статус обслуживаемых
ArtDenis
Ковыряю std::atomic. Оказывается они работают для STM32 под GCC. Вот какой код генерируются при их использовании:

Код
; std::atomic<unsigned> lalala;
; unsigned to_test = 20;
; ........

; lalala = 10;
8000a8a:    f3bf 8f5f     dmb    sy
8000a8e:    4a0d          ldr    r2, [pc, #52]; (8000ac4 <main+0x3c>)
8000a90:    210a          movs    r1, #10
8000a92:    6011          str    r1, [r2, #0]
8000a94:    f3bf 8f5f     dmb    sy

; lalala.compare_exchange_strong(to_test, 10);
8000a98:    4b0b          ldr    r3, [pc, #44]; (8000ac8 <main+0x40>)
8000a9a:    681c          ldr    r4, [r3, #0]
8000a9c:    f3bf 8f5f     dmb    sy
8000aa0:    e852 0f00     ldrex    r0, [r2]
8000aa4:    42a0          cmp    r0, r4
8000aa6:    d104          bne.n    8000ab2 <main+0x2a>
8000aa8:    e842 1e00     strex    lr, r1, [r2]
8000aac:    f1be 0f00     cmp.w    lr, #0
8000ab0:    d1f6          bne.n    8000aa0 <main+0x18>
8000ab2:    f3bf 8f5f     dmb    sy
8000ab6:    bf18          it    ne
8000ab8:    6018          strne    r0, [r3, #0]


В ассемблере армов разбираюсь плохо. Насколько всё хорошо/плохо в данном случае?
AHTOXA
Цитата(ArtDenis @ Jun 22 2015, 09:01) *
В ассемблере армов разбираюсь плохо. Насколько всё хорошо/плохо в данном случае?

Очень даже неплохо, кмк. В отличие от приведённых выше вариантов с ldrex/strex - добавлены барьеры. Это правильно, так аккуратнее/надёжнее.
ArtDenis
Так непонятно, нужны ли там барьеры. У STM32 вроде нету кэша ОЗУ. Или я ошибаюсь?
AHTOXA
Насколько я понимаю, эти барьеры (DMB) нужны для многоядерных процессоров. Чтобы обеспечить консистентность содержимого памяти.
С другой стороны, накладные расходы на барьеры невелики, зато реализация более-менее проверенная. Так что я бы использовал её.
jcxz
Цитата(ArtDenis @ Jun 22 2015, 14:16) *
Так непонятно, нужны ли там барьеры. У STM32 вроде нету кэша ОЗУ. Или я ошибаюсь?

Барьеры там что мёртвому припарка. Кеш у STM32 есть, но при чём тут он? Вы что - при каждом переключении задачи ОС или вызове ISR кеш сбрасываете?
Кеш прозрачен для доступов от ядра CPU.
Барьеры нужны, имхо, если возможен доступ разных устройств (например: ядро CPU и контроллер DMA; или два разных ядра CPU) к одному адресу.
AlexandrY
Цитата(ArtDenis @ Jun 22 2015, 11:16) *
Так непонятно, нужны ли там барьеры. У STM32 вроде нету кэша ОЗУ. Или я ошибаюсь?


Измерения показывают, что длительность инструкции DMB на Cortex-M4 случайным образом колеблется от 2 до 5-и тактов частоты ядра.
Накладная операция. Но необходимая скорее всего.
Поскольку эксклюзивные мониторы реализуются изготовителем SoC-ов уже после кэшей ARM-а.
AlexandrY
Цитата(AlexandrY @ Jun 22 2015, 13:03) *
Измерения показывают, что длительность инструкции DMB на Cortex-M4 случайным образом колеблется от 2 до 5-и тактов частоты ядра.
Накладная операция. Но необходимая скорее всего.
Поскольку эксклюзивные мониторы реализуются изготовителем SoC-ов уже после кэшей ARM-а.


О кстати, сообразил поискать в информационном центре ARM-а - http://infocenter.arm.com/help/index.jsp?t...7175928887.html
Тему можно закрывать. Эксклюзивный монитор у Cortex-M4 всю память считает как одно целое.
Значит атомная операция будет длится до тех пор пока все не перестанут пользоваться памятью. Т.е. возможно вечно ! wink.gif
ArtDenis
Цитата(AlexandrY @ Jun 22 2015, 18:26) *
Тему можно закрывать. Эксклюзивный монитор у Cortex-M4 всю память считает как одно целое.
Значит атомная операция будет длится до тех пор пока все не перестанут пользоваться памятью. Т.е. возможно вечно ! wink.gif

А можно для тех кто в танке объяснить что это означает? И как обстоят дела у M3?
AHTOXA
Цитата(ArtDenis @ Jun 22 2015, 18:53) *
А можно для тех кто в танке объяснить что это означает? И как обстоят дела у M3?

Это означает, что любая запись в память в промежутке между LDREX и STREX приведёт к неудаче STREX и повтору попытки.
Другими словами, если между LDREX и STREX возникнет прерывание, то попытку атомарного доступа почти наверняка придётся повторить, даже если обработчик прерывания не обращался к защищаемой переменной. Это происходит потому, что монитор отслеживает не обращение к конкретному адресу, а обращение к памяти вообще.
Насчёт "возможно вечно" - это конечно гипербола, хотя при наличии, скажем, DMA, который непрерывно пишет в буфер в ОЗУ, проскочить будет довольно трудно.
Надо, кстати, попробовать...
ЗЫ. У M3 дела обстоят точно так же.
ArtDenis
Ну вообще это очень легко проверить. Завтра поэкспериментирую
jcxz
Цитата(AlexandrY @ Jun 22 2015, 19:26) *
Значит атомная операция будет длится до тех пор пока все не перестанут пользоваться памятью. Т.е. возможно вечно ! wink.gif

Расстояние между LDREX и STREX в моих функциях всего несколько тактов.
С какой частотой у Вас в системе должны идти прерывания чтобы обеспечивалось 100% попадание между LDREX и STREX эксклюзивного доступа другого процесса?

Цитата(AHTOXA @ Jun 23 2015, 00:40) *
Это означает, что любая запись в память в промежутке между LDREX и STREX приведёт к неудаче STREX и повтору попытки.

Даташит на эту тему не читал, но с выскокой вероятностью могу предположить, что скорей всего не любая, а только эксклюзивная (LDREX/STREX).

Цитата(AHTOXA @ Jun 23 2015, 00:40) *
Насчёт "возможно вечно" - это конечно гипербола, хотя при наличии, скажем, DMA, который непрерывно пишет в буфер в ОЗУ, проскочить будет довольно трудно.

Ещё раз оговорюсь, что даташит не читал, но логика подсказывает, что DMA никак не будет влиять на LDREX/STREX, так как скорей всего не является эксклюзивным доступом (не нужно это для DMA).
AHTOXA
Цитата(jcxz @ Jun 23 2015, 07:44) *
Даташит на эту тему не читал, но с выскокой вероятностью могу предположить, что скорей всего не любая, а только эксклюзивная (LDREX/STREX).

Да, так логичнее, но, когда я проводил эксперименты с LDREX/STREX, то, вроде бы, любое прерывание приводило к сбою STREX, вне зависимости от того, что было в обработчике этого прерывания. Хотя, я не углублялся, возможно, что-то было не так в условиях моего эксперимента.
Посмотрим, что получится у ArtDenis.
ViKo
Если бы эксклюзивные операции отслеживали только сами себя, толку от них было бы никакого. Так можно и обычным флагом обойтись, когда захотел - установил, когда надо - проверил... Смысл именно в том, что мониторится любое обращение к памяти (надо думать, запись). Типа, триггера. LDREX разрешает работу триггера, любая запись в память устанавливает его в 1, CLREX сбрасывает его. STREX проверяет триггер, и если он установлен, не пишет в память, и выдает 1 в регистр. И запрещает мониторинг (триггер).
AlexandrY
Цитата(ViKo @ Jun 23 2015, 08:55) *
Если бы эксклюзивные операции отслеживали только сами себя, толку от них было бы никакого. Так можно и обычным флагом обойтись, когда захотел - установил, когда надо - проверил... Смысл именно в том, что мониторится любое обращение к памяти (надо думать, запись). Типа, триггера. LDREX разрешает работу триггера, любая запись в память устанавливает его в 1, CLREX сбрасывает его. STREX проверяет триггер, и если он установлен, не пишет в память, и выдает 1 в регистр. И запрещает мониторинг (триггер).


Да, это проверенно.
В Cortex-M4 операция STREX даст отбой даже если в прерывании запись была сделана командой STR и совсем в другую ячейку памяти.
jcxz
Цитата(ViKo @ Jun 23 2015, 11:55) *
Если бы эксклюзивные операции отслеживали только сами себя, толку от них было бы никакого. Так можно и обычным флагом обойтись, когда захотел - установил, когда надо - проверил... Смысл именно в том, что мониторится любое обращение к памяти

А зачем Вам мониторить все обращения? Ведь нужны только те обращения, которые могут использоваться для доступа к переменным синхронизации.
Если Вам нужна чтение-модификация-запись переменной синхронизации, или неатомарное чтение (несколькиим командами) этой переменной, или запись (любая) в эту переменную,
то извольте все эти операции выполнять только инструкциями LDREX/STREX. Тогда всё будет нормально и ставить этот бит по каждому обращению к памяти совсем излишне
(но конечно возможно для упрощения реализации ядра CPU).
А просто чтение этой-же переменной синхронизации можно делать и вообще одной командой LDR (которая априори атомарна) пусть даже она и попадёт между LDREX и STREX
выполняющимися в другом потоке - это никак не повлияет на результат.


Цитата(AlexandrY @ Jun 23 2015, 12:45) *
В Cortex-M4 операция STREX даст отбой даже если в прерывании запись была сделана командой STR и совсем в другую ячейку памяти.

Вполне возможно для упрощения реализации механизма в ядре сделали срабатывание не только на эксклюзивный доступ.
AlexandrY
Цитата(jcxz @ Jun 23 2015, 10:21) *
Вполне возможно для упрощения реализации механизма в ядре сделали срабатывание не только на эксклюзивный доступ.


С LDREX/STREX все еще более запутанней.

Решил тут протестировать как там насчет работы из разных областей памяти.
Выяснилось, что LDREX/STREX правильно работает когда переменные находятся в RAM вблизи 0x1000_0000-0x3000_0000
То, что кэширование разное сверху и снизу границы 0x2000_0000 никак не влияет, барьеры памяти можно не ставить.
А вот если использовать память которая находится после границы 0x4000_0000 , то пара LDREX/STREX там никакого эффекта не дает, хотя и исключений не вызывает.
Т.е. можно спокойно испортить переменную, а STREX ничего не покажет.

Ну и как положительный момент можно отметить, что выполнение кода из RAM на работу LDREX/STREX тоже не влияет.

А с DMA вопрос еще темный.
Мутный момент также с областями внешней памяти.
AHTOXA
Цитата(AlexandrY @ Jun 23 2015, 22:24) *
Выяснилось, что LDREX/STREX правильно работает когда переменные находятся в RAM вблизи 0x1000_0000-0x3000_0000

А какой контроллер вы тестировали?
AlexandrY
Цитата(AHTOXA @ Jun 23 2015, 20:29) *
А какой контроллер вы тестировали?


MK60FN1M0VLQ12
ArtDenis
На моём STM32L151 получились такие результаты:

Монитор НЕ отлавливает нарушение эксклюзивного доступа при простом изменение области памяти для которой был вызван LDREX или любой другой области памяти

Монитор отлавливает нарушение эксклюзивного доступа если
1. Был вызван STREX на любую область ОЗУ (не обязательно на ту, для которой был вызван LDREX)
2. Произошло любое прерывание
jcxz
Цитата(AlexandrY @ Jun 23 2015, 23:24) *
Т.е. можно спокойно испортить переменную, а STREX ничего не покажет.

Вы портили при помощи другой STREX или просто STR? Если второе - эксперимент некорректный.
И надо посмотреть как классифицируется память находящаяся по этим адресам. Возможна это область памяти периферии, возможно с этим связано, лень заглядывать в карту памяти в описании М-ядра. :-)
AlexandrY
Цитата(ArtDenis @ Jun 23 2015, 20:45) *
Монитор отлавливает нарушение эксклюзивного доступа если
2. Произошло любое прерывание


Странно, у меня не на любое, а только такое где был STREX или STR во внутреннюю RAM , а вот на PUSH в ту же RAM не реагирует.


Цитата(jcxz @ Jun 23 2015, 21:14) *
Вы портили при помощи другой STREX или просто STR? Если второе - эксперимент некорректный.
И надо посмотреть как классифицируется память находящаяся по этим адресам. Возможна это область памяти периферии, возможно с этим связано, лень заглядывать в карту памяти в описании М-ядра. :-)


Конечно использовал STREX.
ArtDenis
Цитата(AlexandrY @ Jun 23 2015, 23:25) *
Странно, у меня не на любое, а только такое где был STREX или STR во внутреннюю RAM , а вот на PUSH в ту же RAM не реагирует

Вообще, в прерывании был STR. Не подумал когда тестировал, что это имеет значение
ArtDenis
Цитата(ArtDenis @ Jun 23 2015, 23:42) *
Вообще, в прерывании был STR. Не подумал когда тестировал, что это имеет значение

Проверил ещё раз. Достаточно любого прерывания.
ViKo
Программы - в студию.
И выводы, желательно, не противоречивые. rolleyes.gif
ArtDenis
Цитата(ViKo @ Jun 24 2015, 09:00) *
Программы - в студию.

К сожалению по мере тестирования программа менялась sad.gif Последний вариант - тестирование пустого прерывания. Устроит?

А выводы - вроде как работает, но не настолько эффективно как хотелось. Скорее всего буду использовать.
ViKo
Цитата(ArtDenis @ Jun 24 2015, 07:19) *
К сожалению по мере тестирования программа менялась sad.gif Последний вариант - тестирование пустого прерывания. Устроит?

А выводы - вроде как работает, но не настолько эффективно как хотелось. Скорее всего буду использовать.

Устроят любые программы, как образец. И тестировщиков прибавится. И выводы скорректируются.
ArtDenis
Цитата(ViKo @ Jun 24 2015, 12:44) *
Устроят любые программы, как образец. И тестировщиков прибавится. И выводы скорректируются.


Ну держи. Правда, используется собственная либа для работы с периферией. Если светодиод загорелся, то монитор не отловил нарушений, если не загорелся, то отловил. К сожалению, светодиод на плате один. Поскупился я на них ))
Код
#include <stdint.h>
#include "stm32_lib/stm32l1xx/hl_gpio.hpp"
#include "stm32_lib/stm32l1xx/hl_adc.hpp"
#include "stm32_lib/stm32l1xx/hl_rcc.hpp"

using namespace hl;

typedef PA8 LedPin;

static void init_led()
{
    PA::clock_on();
    PA::reset();
    LedPin::conf_out_push_pull();
    LedPin::off();
}

static void adc_init()
{
    rcc_enable_hsi();
    ADC_<1>::clock_on();
    ADC_<1>::reset();
    ADC_<1>::set_regular_len(1);
    ADC_<1>::set_regular_channel_pos(1, 0);
    ADC_<1>::enable_eoc_interrupt();
    ADC_<1>::enable();
    NVIC_EnableIRQ(ADC1_IRQn);
}

static void led_on(bool value)
{
    LedPin::set_out(value);
}

// Нулевой тест. Между LDREXW и STREXW почти ничего нету
// Монитор НЕ отлавливает нарушение
static void test0()
{
    volatile uint32_t var = 0;
    __LDREXW(&var);
    bool res = __STREXW(10, &var) == 0;
    led_on(res);
}

// Изменение памяти при помощи STR
// Монитор НЕ отлавливает нарушение
static void test1()
{
    volatile uint32_t var = 0;
    __LDREXW(&var);
    var = 20;
    bool res = __STREXW(10, &var) == 0;
    led_on(res);
}

// Изменение одного участка памяти при помощи STREX
// Монитор отлавливает нарушение
static void test2()
{
    volatile uint32_t var = 0;
    __LDREXW(&var);
    __STREXW(20, &var);
    bool res = __STREXW(10, &var) == 0;
    led_on(res);
}

// Изменение разных участков памяти при помощи STREX
// Монитор отлавливает нарушение
static void test3()
{
    volatile uint32_t var = 0;
    volatile uint32_t var2 = 0;
    __LDREXW(&var);
    __STREXW(20, &var2);
    bool res = __STREXW(10, &var) == 0;
    led_on(res);
}

// Прерывание между LDREX и STREX
// Монитор отлавливает нарушение
// volatile bool interrupt_executed = false;
static void test4()
{
    volatile uint32_t var = 0;
    __LDREXW(&var);

    // Вот таким вот нехитрым образом добиваемся прерывания ))
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    ADC_<1>::start_regular_conversion();
    // Тут прерывание точно произошло, т.к. мы работаем на частоте 2 МГц, а АЦП на 16 Мгц

    // Это осталось от предыдущего теста:
    // while (!interrupt_executed) {}

    bool res = __STREXW(10, &var) == 0;
    led_on(res);
}

extern "C" void ADC1_IRQHandler()
{
//    interrupt_executed = true;
    ADC_<1>::clear_eoc_flag();
}

int main()
{
    init_led();
    adc_init();
//    test0();
//    test1();
//    test2();
//    test3();
    test4();
}


Один из старых вариантов тестирования прерывания был таким:

Код
static void test4()
{
    volatile uint32_t var = 0;
    __LDREXW(&var);

    ADC_<1>::start_regular_conversion();
    while (!interrupt_executed) {}

    bool res = __STREXW(10, &var) == 0;
    led_on(res);
}

extern "C" void ADC1_IRQHandler()
{
    interrupt_executed = true;
    ADC_<1>::clear_eoc_flag();
}

Он тоже показывал что монитор отловил нарушение эксклюзивного доступа.

PS: критика крайне приветствуется ))
ArtDenis
Недокопипастил. Там в конце main() ещё
Код
for (;;) {}

стоит
AHTOXA
Ну, вроде всё вполне согласуется с моими опытами.
Единственный момент, ADC_<1>::clear_eoc_flag() - наверняка делает STR в область периферии. Для чистоты эксперимента хорошо бы сделать совсем пустой обработчик прерывания.
ArtDenis
Цитата(AHTOXA @ Jun 24 2015, 14:19) *
Ну, вроде всё вполне согласуется с моими опытами.
Единственный момент, ADC_<1>::clear_eoc_flag() - наверняка делает STR в область периферии. Для чистоты эксперимента хорошо бы сделать совсем пустой обработчик прерывания.

Так вроде как область адресов периферии не относится к ОЗУ, которое защищает монитор?

И без ADC_<1>::clear_eoc_flag() флаг не сбросится и прерывание будет происходить снова и снова как только оно завершиться. Убрал ADC_<1>::clear_eoc_flag(). Поток выполнения перестал доходить до
Код
bool res = __STREXW(10, &var) == 0;

Есть в STM32 такие прерывания для которых не надо сбрасывать флаг при выходе из обработчика?
jcxz
Цитата(ArtDenis @ Jun 24 2015, 14:00) *
PS: критика крайне приветствуется ))

Все тесты, кроме test4() бессмысленны. Непонятно - что там тестируется? LDREX/STREX призваны для обнаружения нарушения эксклюзивного доступа
(когда при работе одного процесса с переменной, происходит доступ к ней другого процесса). Везде кроме test4() имеется только работа одного процесса.

Цитата(ArtDenis @ Jun 24 2015, 15:31) *
Есть в STM32 такие прерывания для которых не надо сбрасывать флаг при выходе из обработчика?

Все прерывания M-ядра квитируются (переходят в разряд обслуживаемых со снятием запроса).
Путаете мух с котлетами. Сбрасывать возможно нужно флаги периферии, вызвавшие срабатывание этих прерываний.
Ничего не мешает возбудить эти прерывания программно через NVIC. Читайте мануал на M-ядро.
ArtDenis
Цитата(jcxz @ Jun 24 2015, 14:42) *
Все тесты, кроме test4() бессмысленны. Непонятно - что там тестируется? LDREX/STREX призваны для обнаружения нарушения эксклюзивного доступа
(когда при работе одного процесса с переменной, происходит доступ к ней другого процесса). Везде кроме test4() имеется только работа одного процесса.

Вообще тестируется работа монитора эксклюзивного доступа, которому все эти потоки и процессы по барабану. Он вообще не знает о таких вещах.

Цитата(jcxz @ Jun 24 2015, 14:42) *
Все прерывания M-ядра квитируются (переходят в разряд обслуживаемых со снятием запроса).
Путаете мух с котлетами. Сбрасывать возможно нужно флаги периферии, вызвавшие срабатывание этих прерываний.
Ничего не мешает возбудить эти прерывания программно через NVIC. Читайте мануал на M-ядро.

Так что мне сделать, чтобы был пустой обработчик прерывания, в котором не надо сбрасывать флаг? И кстати, я не понял что именно я путаю и в каком месте ))
AHTOXA
Цитата(ArtDenis @ Jun 24 2015, 14:31) *
Так вроде как область адресов периферии не относится к ОЗУ, которое защищает монитор?

Ну, для полноты охвата тестамиsm.gif
Цитата(ArtDenis @ Jun 24 2015, 14:31) *
Есть в STM32 такие прерывания для которых не надо сбрасывать флаг при выходе из обработчика?

Например, SysTick. Или, вот, PendSV. Взводится вот так:
Код
SCB->ICSR = SCB_ICSR_PENDSVSET_Msk;
jcxz
Цитата(ArtDenis @ Jun 24 2015, 15:49) *
Вообще тестируется работа монитора эксклюзивного доступа, которому все эти потоки и процессы по барабану. Он вообще не знает о таких вещах.

Вообще этот монитор как раз и создан для этих процессов и потоков. Если что.

Цитата(ArtDenis @ Jun 24 2015, 15:49) *
Так что мне сделать, чтобы был пустой обработчик прерывания, в котором не надо сбрасывать флаг? И кстати, я не понял что именно я путаю и в каком месте ))

Программно активировать любое прерывание через NVIC. Открываете мануал на M-ядро и читаете про "Interrupt Set-Pending Register".
Путаете NVIC с периферией посылающей запросы к NVIC.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.