Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: __LDREX __STREX в STM32F407
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
Страницы: 1, 2, 3
Alechek
Цитата(jcxz @ Jun 7 2017, 14:29) *
В смысле нет?
Если у Вас есть пара LDREX_1/STREX_1 и Вы внутри этой пары, между LDREX_1 и STREX_1 поставите другую STREX_2, то STREX_1 вернёт статус - нарушение эксклюзивности.
Потому что триггер эксклюзивного доступа был сброшен STREX_2.

В смысле, если между LDREX_1/STREX_1 сделать простой STR (в этот регион), то будет нарушение эксклюзивности.
scifi
Цитата(Alechek @ Jun 7 2017, 13:21) *
В смысле, если между LDREX_1/STREX_1 сделать простой STR (в этот регион), то будет нарушение эксклюзивности.

Это называется "глюк в софте". Систему можно и проще поломать - просто забейте весь стек мусором biggrin.gif
jcxz
Цитата(Alechek @ Jun 7 2017, 12:21) *
В смысле, если между LDREX_1/STREX_1 сделать простой STR (в этот регион), то будет нарушение эксклюзивности.

А зачем так делать??
LightElf
QUOTE (jcxz @ Jun 7 2017, 00:36) *
Я писал про другие bus-masters типа DMA, Ethernet-контроллеров и прочего, которое не выполняет программы, задачи, не выполняет какой-то код, задачи которого не нужно синхронизировать с другими задачами.

На жирных камнях с многоуровневыми кэшами - вполне может проверяться активность других bus-masters. На Cortex-M точно нет.
Вообще про LDREX/STREX лучше читать не у ARM, а у авторов - Hitachi SH. А правильное применение есть в сорцах Keil RTX

QUOTE (Alechek @ Jun 7 2017, 13:21) *
В смысле, если между LDREX_1/STREX_1 сделать простой STR (в этот регион), то будет нарушение эксклюзивности.

Нет, не будет.
Alechek
Цитата(scifi @ Jun 7 2017, 15:27) *
Это называется "глюк в софте". Систему можно и проще поломать - просто забейте весь стек мусором biggrin.gif

Цитата(jcxz @ Jun 7 2017, 16:23) *
А зачем так делать??

Господа, оставьте эти риторические вопросы. Здесь не обсуждается вопрос ЗАЧЕМ.
Нет ничего невозможного.
Arduino уже добрался до Cortex! Сделать вызов функции между LDREX/STREX - как раз плюнуть!

От неправильного применения может спасти только чтение правильной документации.

Цитата(LightElf @ Jun 7 2017, 16:52) *
Нет, не будет.


А поподробней? Практика или документация?
Пока что из того, что я привел, должно быть нарушение. Ошибка в ней или я что-то недочитал?
Forger
Цитата(LightElf @ Jun 7 2017, 14:52) *
А правильное применение есть в сорцах Keil RTX


Действительно - готовое решение. Спасибо за наводку )))

Причем в примере ниже два способа - через запрет /разрешения всех прерываний и через ldrex/strex.

..\Keil 5\ARM\Pack\ARM\CMSIS\5.0.0\CMSIS\RTOS\RTX\SRC\rt_MemBox.c:

CODE
/*--------------------------- rt_alloc_box ----------------------------------*/

void *rt_alloc_box (void *box_mem) {
/* Allocate a memory block and return start address. */
void **free;
#ifndef __USE_EXCLUSIVE_ACCESS
U32 irq_mask;

irq_mask = (U32)__disable_irq ();
free = ((P_BM) box_mem)->free;
if (free) {
((P_BM) box_mem)->free = *free;
}
if (irq_mask == 0U) { __enable_irq (); }
#else
do {
if ((free = (void **)__ldrex(&((P_BM) box_mem)->free)) == 0U) {
__clrex();
break;
}
} while (__strex((U32)*free, &((P_BM) box_mem)->free));
#endif
return (free);
}


и тут: rt_HAL_CM.h

Код
/* Functions */
#ifdef __USE_EXCLUSIVE_ACCESS
#define rt_inc(p)     while(__strex((__ldrex(p)+1U),p))
#define rt_dec(p)     while(__strex((__ldrex(p)-1U),p))
#else
#define rt_inc(p)     __disable_irq();(*p)++;__enable_irq();
#define rt_dec(p)     __disable_irq();(*p)--;__enable_irq();
#endif

scifi
Цитата(Alechek @ Jun 7 2017, 15:22) *
Сделать вызов функции между LDREX/STREX - как раз плюнуть!

И эта функция называется SelfDestruct(!Now) biggrin.gif
Хватит вы думывать небылицы wacko.gif
jcxz
Цитата(Forger @ Jun 7 2017, 14:39) *
Причем в примере ниже два способа - через запрет /разрешения всех прерываний и через ldrex/strex.
#define rt_inc(p) __disable_irq();(*p)++;__enable_irq();
#define rt_dec(p) __disable_irq();(*p)--;__enable_irq();

В uCOS сделано лучше:
typedef unsigned int OS_CPU_SR;
#define OS_CPU_SR_ALLOCATE() OS_CPU_SR cpu_sr //Allocate storage for CPU status register
#define OS_ENTER_CRITICAL() {cpu_sr = __get_PRIMASK(); __disable_interrupt();}
#define OS_EXIT_CRITICAL() {__set_PRIMASK(cpu_sr);}

чтобы, если перед OS_ENTER_CRITICAL() были запрещены, то после OS_EXIT_CRITICAL() так и остались.
Forger
Цитата(jcxz @ Jun 7 2017, 16:13) *
чтобы, если перед OS_ENTER_CRITICAL() были запрещены, то после OS_EXIT_CRITICAL() так и остались.

Оно так и сделано в приведенном выше примере. см. void *rt_alloc_box (void *box_mem) в файле rt_MemBox.c

Но мне гораздо нравится больше как это сделано во freertos, там юзается basepri (кроме CM0), т. е. запрещаются не все прерывания.
Точнее, никогда не запрещаются прерывания, обладающие приоритетом выше, чем заданный в настройках порта freertos - configMAX_SYSCALL_INTERRUPT_PRIORITY
С одним ограничением - в таких lockfree прерываниях категорически нельзя использовать сервисы rtos.
Чтобы это все правильно работало, NVIC нужно настраивать исключительно на полностью вытесняющие прерывания:

Код
NVIC_SetPriorityGrouping(0); // 4 bits for pre-emption priority, 0 bits for subpriority



CODE

#define portDISABLE_INTERRUPTS() vPortRaiseBASEPRI()
#define portENABLE_INTERRUPTS() vPortSetBASEPRI( 0 )
#define portENTER_CRITICAL() vPortEnterCritical()
#define portEXIT_CRITICAL() vPortExitCritical()

.....

static portFORCE_INLINE void vPortRaiseBASEPRI( void )
{
uint32_t ulNewBASEPRI = configMAX_SYSCALL_INTERRUPT_PRIORITY;

__asm
{
/* Set BASEPRI to the max syscall priority to effect a critical
section. */
msr basepri, ulNewBASEPRI
dsb
isb
}
}
/*-----------------------------------------------------------*/

static portFORCE_INLINE void vPortClearBASEPRIFromISR( void )
{
__asm
{
/* Set BASEPRI to 0 so no interrupts are masked. This function is only
used to lower the mask in an interrupt, so memory barriers are not
used. */
msr basepri, #0
}
}
AHTOXA
Цитата(Alechek @ Jun 7 2017, 17:22) *
А поподробней? Практика или документация?
Пока что из того, что я привел, должно быть нарушение. Ошибка в ней или я что-то недочитал?

Практика. Выше я уже приводил ссылку на предыдущее обсуждение LDREX/STREX. Там тесты разных вариантов.

Видимо, в документации под словом "more recent store of address x" подразумевается всё же STREX.
Alechek
AHTOXA, спасибо, ознакомился.
Но, опять противоречивые результаты:
AVI-crak говорит, что флаг сбрасывает любая запись по адресу, хоть DMA.
ArtDenis в своих тестах показывает, что даже явная запись не сбрасывает.
AlexandrY утверждает, что у него явная запись сбрасывает флаг, а неявная (PUSH) - не меняет состояние.

Походу, собака порылась в
Цитата(ARM ® v7-M Architecture Reference Manual)
A3.4.5 Load-Exclusive and Store-Exclusive usage restrictions
The Load-Exclusive and Store-Exclusive instructions are designed to work together, as a pair, for example a
LDREX/STREX pair or a LDREXB/STREXB pair. As mentioned in Context switch support, ARM recommends that the
Store-Exclusive instruction always follows within a few instructions of its associated Load-Exclusive instructions.
In order to support different implementations of these functions, software must follow the notes and restrictions
given here
.
...

В частности, по поводу простой записи:
Цитата
• An explicit store to memory can cause the clearing of exclusive monitors associated with other processors,
therefore, performing a store between the LDREX and the STREX can result in a livelock situation. As a result,
code must avoid placing an explicit store between an LDREX and an STREX in a single code sequence.
• LDREX and STREX operations must be performed only on memory with the Normal memory attribute.


То есть:
- LDREX/STREX применять только в паре, они для этого сделаны. Все остальное - от лукавого. Может сработать, а может и нет, зависит от конкретной реализации ядра
- LDREX/STREX применять только на Normal памяти:
Цитата(ARM ® v7-M Architecture Reference Manual)
A3.5.1 Memory types
For each memory region, the most significant memory attribute specifies the memory type. There are three mutually
exclusive memory types:
• Normal.
• Device.
• Strongly-ordered.

- вроде как сброс локального монитора текущего ядра может происходить при ЛЮБОЙ записи (хоть DMA), при ЯВНОЙ записи могут сбросится только мониторы других ядер. Но по факту может быть что угодно в зависимости от конкретной реализации. Извращения проверять обязательно!
Цитата(ARM ® v7-M Architecture Reference Manual)
A3.4.1 Exclusive access instructions and Non-shareable memory regions
When a processor writes using any instruction other than a Store-Exclusive:
• If the write is to a physical address that is covered by its local monitor it is IMPLEMENTATION DEFINED
whether the write affects the state of the local monitor.

- локальный монитор сбрасывается при возникновении исключения. Т.е. для одноядерной системы неважно, была ли в другом процессе запись(эксклюзивная) - все равно по входу и выходу из исключения происходит сброс.
Цитата
A3.4.4 Context switch support
In ARMv7-M, the local monitor is changed to Open Access automatically as part of an exception entry or exit sequence.
jcxz
Цитата(Alechek @ Jun 8 2017, 07:01) *
- LDREX/STREX применять только в паре, они для этого сделаны. Все остальное - от лукавого.

И правильно! Через DMA и пр. - ЗАЧЕМ???
AHTOXA
Цитата(Alechek @ Jun 8 2017, 10:01) *
- LDREX/STREX применять только в паре, они для этого сделаны. Все остальное - от лукавого. Может сработать, а может и нет, зависит от конкретной реализации ядра

Именно. Различия в результатах тестов вызваны, скорее всего, различиями в реализации. (Ну или кто-то накосячил в тестахsm.gif)
Способ использования от этого не меняется - только в паре.
Добавьте в ваш список выводов, что монитор может срабатывать на любое изменение уровня прерываний (вход в прерывание или выход из него).

ЗЫ. Хотя насчёт DMA - я лично не верю. В этом случае при непрерывно идущих быстрых транзакциях DMA можно наглухо зависнуть в LDREX/STREX.
Alechek
Цитата(jcxz @ Jun 8 2017, 11:17) *
И правильно! Через DMA и пр. - ЗАЧЕМ???

Импульсивный Вы наш, по делу бы что сказали!

Цитата(AHTOXA @ Jun 8 2017, 11:21) *
Добавьте в ваш список выводов, что монитор может срабатывать на любое изменение уровня прерываний

Добавил, куда можно было. Весь этот раздел A3.4 - сплошные выводы. Читать и читать.

Цитата(AHTOXA @ Jun 8 2017, 11:21) *
ЗЫ. Хотя насчёт DMA - я лично не верю. В этом случае при непрерывно идущих быстрых транзакциях DMA можно наглухо зависнуть в LDREX/STREX.

Мне тоже идея с DMA не очень нравится.
Вроде как не обязательно Store от ядра должно исходить.... В одном месте так написано, в другом - по другому...
Вобщем, все на откуп реализации:
Нажмите для просмотра прикрепленного файлаНажмите для просмотра прикрепленного файла
Опыты, только опыты... Ни в одной докуметации на MCU не видел таких тонкостей.
AVI-crak
Проверять нужно кодом на асме, чтобы gcc не наоптимизировал записи до нуля.
Тестовую запись нужно сбрасывать в память принудительно, с ожиданием завершения физической записи.
Неявная запись (PUSH) - является отложенным событием (как и любая запись кучи регистров), код выполняется параллельно, сбросить физику с подтверждением не получится, но можно немного подождать в пустом цикле или сбросить конвейер.

И наверное главное, давайте указывать явно - какой чип в тесте на данный момент.
AHTOXA
Цитата(Alechek @ Jun 8 2017, 16:54) *
Вобщем, все на откуп реализации:

Хорошие картинки, теперь понятны отличия в реализации. По идее, получается, могут существовать реализации, где DMA сбрасывает монитор. Надо будет учесть.

Цитата(Alechek @ Jun 8 2017, 16:54) *
Опыты, только опыты... Ни в одной докуметации на MCU не видел таких тонкостей.


Думаю, это плата за универсальность ядра. АРМу пришлось при проектировании закладывать разные варианты реализации.
LightElf
QUOTE (AHTOXA @ Jun 8 2017, 09:21) *
Именно. Различия в результатах тестов вызваны, скорее всего, различиями в реализации. (Ну или кто-то накосячил в тестахsm.gif)
Способ использования от этого не меняется - только в паре.
Добавьте в ваш список выводов, что монитор может срабатывать на любое изменение уровня прерываний (вход в прерывание или выход из него).

ЗЫ. Хотя насчёт DMA - я лично не верю. В этом случае при непрерывно идущих быстрых транзакциях DMA можно наглухо зависнуть в LDREX/STREX.

Есть архитектура, есть реализация.
На Cortex-M, которые по определению одноядерные, монитор как таковой отсутствует. Есть просто один-единственный триггер. LDREX его взводит, STREX проверяет что установлен, выполняет запись и сбрасывает. Любое исключение сбрасывает бит. Принудительно можно сбросить через CLREX.
На на навороченных многоядерных камнях с MMU, многоуровневыми кэшами, множеством bus-master устройств реализация может быть совсем другой.
AHTOXA
Цитата(LightElf @ Jun 8 2017, 18:44) *
Есть архитектура, есть реализация.
На Cortex-M, которые по определению одноядерные, монитор как таковой отсутствует. Есть просто один-единственный триггер. LDREX его взводит, STREX проверяет что установлен, выполняет запись и сбрасывает. Любое исключение сбрасывает бит. Принудительно можно сбросить через CLREX.

Не вижу в вашем высказывании противоречия с моим. (Или вы не возражали, а дополняли?)
И на Cortex-M есть нюансы. Где-то обычная запись сбрасывает монитор, а где-то -- нет. Где-то DMA сбрасывает монитор, а где-то -- нет.
jcxz
Цитата(AVI-crak @ Jun 8 2017, 15:21) *
Неявная запись (PUSH) - является отложенным событием (как и любая запись кучи регистров), код выполняется параллельно, сбросить физику с подтверждением не получится, но можно немного подождать

Сколько? Сколько вешать в граммах?

Цитата(AVI-crak @ Jun 8 2017, 15:21) *
в пустом цикле или сбросить конвейер.

Это что такое? И как оно поможет?

Цитата(AVI-crak @ Jun 8 2017, 15:21) *
И наверное главное, давайте указывать явно - какой чип в тесте на данный момент.

DMB/DSB/и прочие B - спасут отца русской демократии laughing.gif

Цитата(LightElf @ Jun 8 2017, 15:44) *
На Cortex-M, которые по определению одноядерные

Вы это расскажите NXP с их линейкой LPC43xx. Про свои определения. cool.gif
Или Ваш мир Cortex-M ограничен STM32?
Курица - не птица, Cortex-M - не STM! Рифма одна-ко. Надо в подпись внести © biggrin.gif
Forger
Коли SVC синхронное и его не получиться использовать нормально в прерываниях (для lockfree решений), то может быть для этой цели подойдет pendSV?

А, чтобы разделить переключатель контекста rtos и юзерские вызовы этого самого pendSV, скажем, в R0 кладем адрес функции, а в остальные регистры ее параметры, а уже внутри pendSV все это разбираем.
Приоритет pendSV самый низкий (ниже лишь systick).
Тогда другой вызов pendSV прервать его не сможет, а лишь поставить в очередь. Т.е. обеспечивается атомарность действий как в прерываниях, так и в основном коде.
Вижу одно ограничение - передавать в такие функции указатели нежелательно, а можно лишь готовые данные (прямо в регистры R1...), т.е. ограничен объем передаваемых данных.
В противном случае нет никакой гарантии, что к моменту вызова соотв. функции (внутри pendSV) данные по указателям будут актуальны.

Или тут есть какие-то подводные камни?
зы. Предположительно вангую грабли со вложенными вызовами (вытесняющие прерывания), где оба обработчика вызывают pendSV...
jcxz
Цитата(Forger @ Jun 9 2017, 09:50) *
зы. Предположительно вангую грабли со вложенными вызовами (вытесняющие прерывания), где оба обработчика вызывают pendSV...

PendSV не "вызывают", а лишь возбуждают запрос на него. В том числе и в ISR-ах, где вход в PendSV будет отложен до завершения всех других ISR, а из фонового процесса - вход в PendSV будет сразу, подобно синхронному SVC.
Forger
Цитата(jcxz @ Jun 9 2017, 13:33) *
PendSV не "вызывают" ...
Да, это-то понятно, это все - придирки к словам, а речь о другом:
как сделать так, чтобы pendSV могли вызывать взводить разные обработчики, особенно вложенные друг в друга, но так, чтобы в стеке сформировалась очередь данных, которые позволят этот pendSV обработать несколько раз подряд и не потерять ни одного запроса и не разрушить стек?
Понятно, что pendSV не для этого был задуман, но тем не менее ))

Размышляя вслух дальше - выполняемый в данный момент pendSV может прервать другое более приоритетное прерывание и опять накидать в стек новых данных перед взводом pendSV,
Или использовать внутри pendSV вызов соотв. SVC, который имеет приоритет выше pendSV...
Заумно как-то выходит. Но возможно ли осуществить что-то подобное?
Цель - избежать критических секций с запретом прерываний и "капризного" LDREX/STREX.
Шаманъ
Цитата(Forger @ Jun 9 2017, 13:58) *
как сделать так, чтобы pendSV могли вызывать взводить разные обработчики, особенно вложенные друг в друга, но так, чтобы в стеке сформировалась очередь данных, которые позволят этот pendSV обработать несколько раз подряд и не потерять ни одного запроса и не разрушить стек?

Очередь данных в стеке не формируется - формируйте ее отдельно, а потом обрабатывайте в PendSV. Но для данной задачи это неэффективно и не имеет смысла - есть LDREX/STREX, если его не хватает, то есть запрет части или всех прерываний.
Forger
Цитата(Шаманъ @ Jun 9 2017, 16:02) *
Очередь данных в стеке не формируется - формируйте ее отдельно, а потом обрабатывайте в PendSV.
А все равно грабли - процесс формирования очереди должен быть защищен критической секцией, масло масляное (((

Цитата
Но для данной задачи это неэффективно и не имеет смысла - есть LDREX/STREX, если его не хватает, то есть запрет части или всех прерываний.

Согласен, но хочется ведь гипотетического универсального решения, вот и спрашиваю )))

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

Сейчас сделано просто и логично - в конкретном экземпляре очереди запрещаются прерывания лишь от того, кто использует эту самую очередь - от USART.



Шаманъ
Цитата(Forger @ Jun 9 2017, 16:20) *
хочется ведь гипотетического универсального решения

Если пишите под GCC, то можно посмотреть на atomic built-in функции https://gcc.gnu.org/onlinedocs/gcc-4.4.3/gc...c-Builtins.html .

Цитата
Короче, задача довольно тривиальная:
программная очередь, набиваемая в обработчике (скажем, очередь байтов), там же сигналим счетным семафором, переходим к соотв. задаче, которая это семафор ждет и выгребает из этой же очереди байты.
Нужно защищать указатели (голова/хвост) очереди от прерывания.

Интересно зачем? Если это что-то типа кольцевого буфера приделанного к UART, то один указатель изменяется только в задаче, а второй только в прерывании. Пересечения между ними не происходит. Семафор это головная боль ОС. Вот и все. У меня сделано похожим образом.
Forger
Цитата(Шаманъ @ Jun 9 2017, 17:14) *
Интересно зачем? Если это что-то типа кольцевого буфера приделанного к UART, то один указатель изменяется только в задаче, а второй только в прерывании. Пересечения между ними не происходит.

Еще как происходит - я контролирую переполнение и исчерпание очереди (соотв. ловушки/логи)!
Если в задаче в процессе извлечения свежего байта из очереди пришел еще один новый байт,
который нужно быстро положить в ту же очередь, то без соотв. критических секций получим что угодно, обычно получаем HF.
Поэтому эти две вещи нужно изолировать - пока достаем байт из очереди (в задаче), временно запрещаем прерывания от того, кто наполняет эту очередь.

Именно это я и описал выше.
У меня очередь сделана в виде шаблона, всего один параметр - тип данных.
При создании экземпляра очереди ей передается ссылка на класс Interrupt, в котором есть два обязательным метода disable и enable.
Для каждой очереди он может быть свой, поэтому запрещаются лишь те прерывания, которые конфликтуют с конкретным экземпляром очереди.

Цитата
Семафор это головная боль ОС.
При чем тут это?
Я лишь привел пример с осью, как наиболее универсальный.
Но кто не пользуется осью, могут просто поллить некий флажок в основном коде, а в прерываниях его взводить или инкрементировать.
Способ оповещения основного кода в данном случае не имеет никакого значения. Отклоняемся от темы )))
AVI-crak
Защищённый режим: стек для потоков psp, стек для прерываний msp.
Поток не имеет прямого доступа к части системных регистров.
Приоритет SVC - 0.
Пользовательские прерывания 1~14.
Системный таймер, и таймер реального времени =15.
Любой поток и прерывание может вызвать SVC без разрушения стека. Системный таймер и таймер реального может прервать любое прерывание, все изменения при этом будут скидываться стек прерываний, и восстанавливаться до первоначального вида совершенно автоматически.
Кукла для всех прерываний не требуется!!!

Проблема половины бесплатных ос - в наследии от пиков да авр, где защищённый режим просто не существовал. Переписывать всю ось нет смысла - вот и продолжают пользоваться кактусом.

Шаманъ
Цитата(Forger @ Jun 9 2017, 17:30) *
При чем тут это?
Я лишь привел пример с осью, как наиболее универсальный.
Но кто не пользуется осью, могут просто поллить некий флажок в основном коде, а в прерываниях его взводить или инкрементировать.
Способ оповещения основного кода в данном случае не имеет никакого значения. Отклоняемся от темы )))

Это как раз причина по которой у меня с ОС не происходит, а с "неким флажком" происходит. Поясняю:

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

Теперь про голову - данные из UART выгребаем в прерывании, в этом же прерывании увеличиваем счетчик семафора и добавляем в очередь. Чтобы избежать переполнения очереди достаточно проверить счетчик семафора. Вот собственно и все. Счетчик семафора это проблема ОС, остальное (очередь и указатели на голову/хвост) в полной безопасности.

Если интересно, как сделана у меня в ОС работа с семафорами, то через те же LDREX/STREX sm.gif Правда это если не касаться вопросов переключения задач и системных вызовов, но то уже точно другая тема.
Forger
Цитата(Шаманъ @ Jun 9 2017, 18:40) *
Это как раз причина по которой у меня с ОС не происходит, а с "неким флажком" происходит. Поясняю:

Да при чем тут семафор, ось, флажки и т.п.???
Я говорю про совсем другое - манипулирование индексами головы, хвоста и изменяемых данных которые должны меняться в едином непрерывном блоке кода.
Если обработчик прерываний разорвет эту цепочку в самом неподходящем месте, то рано или поздно произойдет HF.


Цитата(AVI-crak @ Jun 9 2017, 17:38) *
Приоритет SVC - 0.
Пользовательские прерывания 1~14.
Системный таймер, и таймер реального времени =15.

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

А должно быть совсем иначе - срочные и архиважные аппаратные прерывания должны отработаться предельно быстро, куда надо просемафорить и тут же освободить процессор.
Шаманъ
Цитата(Forger @ Jun 9 2017, 19:27) *
Я говорю про совсем другое - манипулирование индексами головы, хвоста и изменяемых данных которые должны меняться в едином непрерывном блоке кода.
Если обработчик прерываний разорвет эту цепочку в самом неподходящем месте, то рано или поздно произойдет HF.

От же ж...ну как Вам еще объяснить. Читайте внимательно и думайте:
1. Указатель на голову (куда добавляются данные) он используется только в прерывании которое добавляет данные, только там, и больше нигде.
2. Указатель на хвост (откуда вычитываются данные) он используется только в задаче которая читает данные, только там, и больше нигде.

Синхронизация делается семафором, как и защита от переполнения/недополнения. К семафору есть доступ и у задачи, и у прерывания, но он делается через ОС.

Теперь понятно? Если нет, то перечитайте пожалуйста еще раз, ну или несколько раз - не знаю как объяснить иначе...
Forger
Цитата(Шаманъ @ Jun 9 2017, 20:08) *
От же ж...ну как Вам еще объяснить. Читайте внимательно и думайте:

Я про Фому, а он - про Ерему ...

Ясен пень, что голова и хвост изменяются в разным местах - один в фоне задач, другой - в перрывании, но !
Чтобы проверить переполнение/исчерпание, нужно контроллировать в непрерывном куске кода ОБА указателя/индекса.
Это можно делать только там, где производите изменение этих самых указателей/индексов - в прерывании и в фоне задач.
В прерывании это не проблема - защищать ничего не нужно, достаточно лишь не класть в буфер новые данные и кинуть соотв. сообщение/исключение.
А в фоне задачи нужно защищать обращение к ОБОИМ указателям в некой критической секции, иначе невозможно контроллировать подобные аварийные события.
Если этого не делать, то буфер может быть переполнен/исчерпан так, что об этом невозможно будет узнать.

Также реализуется обратная ситуация - задача кладет данные в буфер, взводит нужное прерывание и уже в соотв. обработчике буфер выгребается и отправляется во вне.
Чтобы положить данные в буфер в фоне задачи, нужно обращаться в непрерывном куске кода к ОБОИМ указателям. Поэтому тут нужна критическая секция или что-то подобное.

Цитата
Синхронизация делается семафором...

Точно, я про Фому, а он - про Ерему )))
Шаманъ
Цитата(Forger @ Jun 9 2017, 20:23) *
Чтобы проверить переполнение/исчерпание, нужно контроллировать в непрерывном куске кода ОБА указателя/индекса.

Нет. Достаточно проверить состояние семафора.

Цитата
А в фоне задачи нужно защищать обращение к ОБОИМ указателям в некой критической секции, иначе невозможно контроллировать подобные аварийные события.
Если этого не делать, то буфер может быть переполнен/исчерпан так, что об этом невозможно будет узнать.

Если напрячься и таки подумать, то в задаче буфер переполнен не модет быть вообще, ибо она данные только читает. А исчерпан не может быть т.к. задача синхронизирована семафором значение которого дает кол-во данных в очереди.

Про прерывание Вы и сами написали. Так кто там про Фому laughing.gif

Цитата
Также реализуется обратная ситуация - задача кладет данные в буфер, взводит нужное прерывание и уже в соотв. обработчике буфер выгребается и отправляется во вне.
Чтобы положить данные в буфер в фоне задачи, нужно обращаться в непрерывном куске кода к ОБОИМ указателям. Поэтому тут нужна критическая секция или что-то подобное.

Да не нужно - достаточно семафора.

P.S. Может быть Вы не так понимаете что такое семафор. У меня это объект синхронизации задач, который может принимать значения от 0 до некоторого MAX. Счетчик семафора показывает доступность ресурса (в данном случае кол-во данных в очереди). Задача может запросить конкретное кол-во данных или диапазон (от и до) - если нужное кол-во доступно, то задача получит запрошенное кол-во данных, если нет, то будет приостановлена, пока не будет доступно запрошенное кол-во данных.
Forger
Цитата(Шаманъ @ Jun 9 2017, 20:40) *
Если напрячься и таки подумать, то в задаче буфер переполнен не модет быть вообще, ибо она данные только читает. ...
Не, это - уже бесполезный спор.
Шаманъ
Цитата(Forger @ Jun 9 2017, 20:48) *
Не, это - уже бесполезный спор.

Конечно, ибо то, о чем я пишу работает sm.gif

ОК поставим вопрос по-другому, каким образом может быть переполнен буфер в задаче, которая из него только читает?
Forger
Цитата(Шаманъ @ Jun 9 2017, 20:56) *
Конечно, ибо то, о чем я пишу работает sm.gif

И самолеты летают. Но иногда, увы, падают sad.gif

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

Если она только читает, то буфер со временем просто переполнится, чтобы избежать этого, из буфера нужно не просто читать данные, но и изменять соотв. указатель.
При заполнении буфера (в прерывании) нужно сравнивать ОБА указателя.
Если это прерывание возникнет между чтением данных в задаче и изменением соотв. указателя, то будет беда. Обнаружить ее крайне сложно.


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

Я делал это несколько иначе, использовал счетчик свободного места, вообще не сравнивая указатели. Это получается несколько быстрее, но отъедает немного места в памяти.
Обращение к этому счетчику и изменение соотв. указателей, должно быть непрерывным. Иначе - HF
Шаманъ
Цитата(Forger @ Jun 9 2017, 21:04) *
И самолеты летают. Но иногда, увы, падают sad.gif

Я тоже могу подколоть - если Вам хочется позаниматься словоблудием, то это не ко мне.

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

Гениально. Для меня чтение из очереди это всегда было как получение данных, так и коррекция указателя.

Цитата
При заполнении буфера (в прерывании) нужно сравнивать ОБА указателя.
Если это прерывание возникнет между чтением данных в задаче и изменением соотв. указателя, то будет беда.

И в чем беда? Прочитали данные, потом произошло прерывание (в нем сравнили указатели и изменили указатель головы, кстати, в некоторых случаях можно указатели и вообще не сравнивать - достаточно кол-ва данных в очереди = значения семафора), потом (когда возвратились в задачу) изменили указатель хвоста - что мы имеем - в прерывании размер очереди был на 1 ячейку меньше, все. Никаких проблем нет. Сами же выше написали про это, а теперь "надумали" проблему там, где ее нет.

Цитата
При обратной ситуации - задача набивает буфер для передачи через прерывание

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

Цитата
Я делал это несколько иначе, использовал счетчик свободного места, вообще не сравнивая указатели. Это получается несколько быстрее, но отъедает немного места в памяти.
Обращение к этому счетчику и изменение соотв. указателей, должно быть непрерывным. Иначе - HF

Свободное место или занятое это одно и то же (свободное = размер - занятое sm.gif), т.е. Вы говорите +- о том же, что и я писал выше и сейчас. Какой HF? Отчего? Выше я написал реальный пример, объясните где там будет HF? Даже если накосячить, то все, что может быть это потеря данных в очереди из-за переполнения.
jcxz
Цитата(Forger @ Jun 9 2017, 19:23) *
А в фоне задачи нужно защищать обращение к ОБОИМ указателям в некой критической секции, иначе невозможно контроллировать подобные аварийные события.

И в фоновом процессе это тоже не нужно. Так как измениться в этот момент может ТОЛЬКО ОДИН указатель.
Подумайте ещё раз.

Цитата(Forger @ Jun 9 2017, 19:23) *
Если этого не делать, то буфер может быть переполнен/исчерпан так, что об этом невозможно будет узнать.

Вы похоже просто не понимаете как работают подобные кольцевые буфера... laughing.gif
Инфа для размышления:
Кольцевые буфера, построенные подобным образом вполне можно использовать даже в тех случаях, когда критические секции в принципе невозможны, например - кольцевой буфер обмена между двумя ядрами/процессорами.

Цитата(Forger @ Jun 9 2017, 19:48) *
Не, это - уже бесполезный спор.

Конечно бесполезный, если Вы не хотите понимать очевидных вещей. laughing.gif
Шаманъ Вам дело говорит.
Forger
Цитата(jcxz @ Jun 9 2017, 22:12) *
И в фоновом процессе это тоже не нужно. Так как измениться в этот момент может ТОЛЬКО ОДИН указатель.

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

Если переполнение/исчерпание очереди не имеет значения для конкретного проекта, вообще не контролируется, то, конечно, можно забить на все эти критические участки кода.
jcxz
Цитата(Forger @ Jun 9 2017, 20:04) *
Я делал это несколько иначе, использовал счетчик свободного места, вообще не сравнивая указатели. Это получается несколько быстрее, но отъедает немного места в памяти.
Обращение к этому счетчику и изменение соотв. указателей, должно быть непрерывным. Иначе - HF

Вот как раз со счётчиком - это плохая реализация, так как обязательно требует критической секции.
Реализация кольцевого буфера на двух указателях (один - чтения, другой - записи) для своей работы не требует критических секций при условии, что есть только один процесс/ядро- писатель и только один процесс/ядро- читатель. И это справедливо только для этих процессов. Т.е. - не требуется ни для чего - ни для вычисления свободного места ни для вычисления уровня заполнения и т.п.
Если состояние кольца читается 3-ей стороной (процессом/ядром отличным от читателя и писателя), то для этого критическая секция нужна.
А в читателе и писателе - нет. Так как каждый из них модифицирует только один указатель, а второй указатель использует только на чтение.

Цитата(Forger @ Jun 9 2017, 21:18) *
Если в этот момент мы начали сравнивать два указателя (или читать некий счетчик свободного/занятого места), то к моменту сравнения он будет неактуальным - ведь в прерывании выше он уже изменился.

Ну и что что станет неактуальным?
Допустим пишущий процесс считал указатель чтения и, пока он его анализировал, произошло прерывание чтения, которое убавило данных из буфера. Ну и что? От этого работа кольца не разрушится. Просто пишущий процесс в этот момент будет думать что данных в кольце больше чем уже есть. Ну и что? Это не приведёт ни к каким сбоям. Вот если-б он думал что данных меньше чем там есть - вот это бы привело к разрушению работы. Но это невозможно по алгоритму.
Forger
Цитата(jcxz @ Jun 9 2017, 22:31) *
Вот как раз со счётчиком - это плохая реализация, так как обязательно требует критической секции.

Счетчик нужен для анализа переполнения/исчерпания очереди.
Если подобные события не представляют угрозы конкретному коду, то и счетчик не нужен.
И критические секции не нужны. Я с этим не спорю.
Я же говорю про код, где нужно контролировать такие события, нужно реагировать на такие вещи и предпринимать конкретные действия.
Особенно, если устройство всегда включено и работает далеко от пользователя/обслуги.
В этом случае контроль вынуждает защищать подобные куски кода.
Самое простое - критическими секциями.
jcxz
Цитата(Forger @ Jun 9 2017, 21:39) *
Самое простое - критическими секциями.

Да... похоже Вы не понимаете.... smile3046.gif
Ниже привожу классическую реализацию кольцевого буфера.
Данная реализация позволяет:
вызывать функцию read() в читающем процессе/ядре;
вызывать функцию write() в пишущем процессе/ядре;
вызывать функции cnt() и free() в читающем и пишущем процессе/ядре.
Никаких дополнительных критических секций или запрещений прерываний или LDREX/STREX или другого шаманства - не требуется.
Данную реализацию кольцевого буфера можно использовать даже если читающий и пишущий процессы находятся в разных ядрах (процессорах).
Теперь покажите пожалуйста нам всем и объясните - где и зачем по Вашему в приведённом ниже коде нужны критические секции???
CODE
class Ring {
int volatile rpos, wpos;
u8 volatile buf[SIZE];
public:
void init() { wpos = rpos = 0; }
int read();
int write(int);
int cnt() const;
int free() const;
};

//Чтение байта данных из buf.
//return: <0 - buf пуст; >=0 - считанное значение.
int Ring::read()
{
int i, j = rpos;
if (j == wpos) return -1;
i = buf[j];
if (--j < 0) j = sizeof(buf) - 1;
rpos = j;
return i;
}

//Запись байта данных c в buf.
//return: !=0-запись успешна; ==0 - запись неуспешна (buf полон).
int Ring::write(int c)
{
int j = wpos;
u8 volatile *p = &buf[j];
if (--j < 0) j = sizeof(buf) - 1;
if (j == rpos) return 0;
*p = c;
wpos = j;
return 1;
}

//return: кол-во данных в buf.
int Ring::cnt() const
{
int i = rpos;
if ((i -= wpos) < 0) i += sizeof(buf);
return i;
}

//return: кол-во свободного места в buf.
int Ring::free() const
{
int i = wpos;
if ((i -= rpos) <= 0) i += sizeof(buf);
return i - 1;
}


PS: Я много раз не случайно подчёркивал - процесса или ядра. Так как процесс, выполняющийся на одном ядре, не может загнать процесс другого ядра в критическую секцию. Тогда следуя Вашей логике - кольцевые буфера для межъядерного обмена невозможно использовать. А их используют! laughing.gif

PSS: Приведённый код можно использовать, модифицировать и распространять совершенно свободно. Разрешаю cool.gif
Forger
Цитата(jcxz @ Jun 9 2017, 22:37) *
Допустим пишущий процесс считал указатель чтения и, пока он его анализировал, произошло прерывание чтения, которое убавило данных из буфера.

Все будет хорошо, пока очередь не исчерпается - совпадут указатели головы и хвоста.

Поясню.
Скажем в очереди остался всего один элемент.
Нужно туда положить еще один, он последний в отправляемом пакете.
Кладем его в голову очереди, далее должны сдвинуть указатель головы, но не успели - произошло прерывание передатчика.
В нем мы видим, что очередь непустая (сравнили два указателя), еще есть один элемент. Но всего один.
Отправляем его, сдвигаем хвост. Видим, что хвост и голова совпали - прекращаем передачу и формируем соотв. сообщение.
Возвращаемся из прерывания, сдвигаем голову вперед на один.
Ждем сообщения об окончании всей передачи, в том числе и этого последнего байта.
Успешно его дожидаемся, ведь передача была только что прекращена.
В итоге последний элемент данных не был отправлен.

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

Понимаю, ситуация редкая и очень трудноуловимая, но она вполне возможна.
Именно для исключения таких ситуаций запись в очередь и сдвиг соотв. указателя должны быть атомарными.
Если это реализуется атомарно на аппаратном уровне, то действительно никакие критические секции тут ни к чему.
jcxz
Цитата(Forger @ Jun 9 2017, 22:44) *
Понимаю, ситуация редкая и очень трудноуловимая, но она вполне возможна.

Это не редкая ситуация. Это вообще не имеет никакого отношения к работе кольцевого буфера.
Кольцевой буфер - это только кольцевой буфер, а Вы описываете случай его использования для некоей задачи.
Эта задача реализована неправильно. К работе кольца это отношения не имеет.
Так можно предположить что и в процессе передачи вашего пакета, пишущий процесс не успеет дописать достаточно данных в буфер в любом месте, а не только в последнем байте. И в любом месте будет сбой.
Это просто неправильная реализация процесса передачи, а не кольцевого буфера.
Forger
Кстати, при очистке очереди изменяются оба указателя (приравниваются либо друг к другу, либо к нулю).
Чистку очереди в реальном коде приходится делать, хотя бы в случае сбоев передачи по каналу связи.
Так вот эта самая чистка уж однозначно должна быть атомарной.
Или же перед ней нужно в обязательном порядке остановить все связанные с ней прерывания, на всякий случай сбросить соотв. флаги и очистить их аппаратные буферы.
jcxz
Цитата(Forger @ Jun 9 2017, 22:59) *
Кстати, при очистке очереди изменяются оба указателя (приравниваются либо друг к другу, либо к нулю).
Чистку очереди в реальном коде приходится делать, хотя бы в случае сбоев передачи по каналу связи.
Так вот эта самая чистка уж однозначно должна быть атомарной.

Опять нет rolleyes.gif
flush() в читающем процессе: Ring::flush() { rpos = wpos; } - однозначно очистка атомарна без всяких крит. секций
"Реальный код" не ограничивается только передачами по каналу связи. И далеко не везде нужен flush(). Даже скорей - редко где нужен. имха
Forger
Цитата(jcxz @ Jun 9 2017, 23:58) *
К работе кольца это отношения не имеет.
Да неужели?
Речь тут как раз про возвожность разорвать атомарную операцию записи в буфер обработчиком, который к этому же очереди и обращается.
Пусть заполнение буфера и передача обращаются к разным указателям, но как минимум факт исчерпания очереди требует чтения обоих указателей.
Поэтому операция записи по указателю и его инкремента должна быть атомарной.
Либо не использовать прерывания.

Цитата
Так можно предположить что и в процессе передачи вашего пакета, пишущий процесс не успеет дописать достаточно данных в буфер в любом месте, а не только в последнем байте. И в любом месте будет сбой.

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



Цитата(jcxz @ Jun 10 2017, 00:10) *
flush() в читающем процессе: Ring::flush() { rpos = wpos; } - однозначно очистка атомарна без всяких крит. секций

Нет, неатомарна - обычная операция чтение-модификация-запись. Может быть прервана прям посреди.
Или же прервать другое обращением к этим же полям в фоне задач.
Ее нельзя вызывать в фоне задач, пока активно и разрешено соотв. прерывание.

Цитата
"Реальный код" не ограничивается только передачами по каналу связи. И далеко не везде нужен flush(). Даже скорей - редко где нужен. имха

Он нужен лишь при аварийных ситуациях, в штатном коде он действительно не нужен.
Я описал устройство, которое работает долго и без обслуги. Если повиснет и не сможет себя привести в норму самостоятельно, то беда будет (((
jcxz
Цитата(Forger @ Jun 9 2017, 23:15) *
Пусть заполнение буфера и передача обращаются к разным указателям, но как минимум факт исчерпания очереди требует чтения обоих указателей.
Поэтому операция записи по указателю и его инкремента должна быть атомарной.
Либо не использовать прерывания.

Ещё раз: покажите в приведённом мной примере: прерывание в каком месте может разрушить его работу???
Ткните пальцем.

Цитата(Forger @ Jun 9 2017, 23:15) *
Нет, неатомарна - обычная операция чтение-модификация-запись. Может быть прервана прям посреди.
Или же прервать другое обращением к этим же полям в фоне задач.

И что? Работа кольца будет разрушена?
Да хоть 100500 обращений. На работу кольца это никак не повлияет.
Forger
Цитата(jcxz @ Jun 10 2017, 00:17) *
Ещё раз: покажите в приведённом мной примере: прерывание в каком месте может разрушить его работу???

Вычитали один указатель (положили например в R1), произошло прерывание, изменило значение указателя.
Вернулись, присвоили другому указателю старое значение, которое положили до этого в R1.
Теперь два указателя не одинаковые, а код предполагает, что одинаковые. Как после этого поведет себя код никто не знает.
jcxz
Цитата(Forger @ Jun 9 2017, 23:15) *
При чем тут не успеет дописать? В том примере все как раз и успевается и места в буфере полно.
Передача производится одновременно с заполнением буфера новыми данными. Вполне обычная ситуация.

тот пример, который Вы описали, вообще никак не говорит о правильности работы кольца. Это пример неправильного его использования.
Отвёрткой тоже можно болты закручивать, а можно её в ухо засунуть. И то, что кто-то после этого оглохнет, не означает, что отвёртка неправильная или неправильно сделана. smile3046.gif
Forger
Цитата(jcxz @ Jun 10 2017, 00:20) *
Да хоть 100500 обращений. На работу кольца это никак не повлияет.

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

Цитата(jcxz @ Jun 10 2017, 00:24) *
тот пример, который Вы описали, вообще никак не говорит о правильности работы кольца. Это пример неправильного его использования.

Очень убедительный довод. Оказывается, что кольцо еще и использовать нужно как-то по хитрому, чтобы оно правильно работало sm.gif
Нафик тогда нужно такое кольцо, вокруг которого нужно плясать с бубном?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.