|
__LDREX __STREX в STM32F407 |
|
|
|
Jun 4 2017, 13:18
|
Участник

Группа: Участник
Сообщений: 23
Регистрация: 23-03-15
Пользователь №: 85 852

|
Всем доброе время суток! IDE - IAR+плагин IAR для eclipse+eclipse. Решил проверить, как работает синхронизация с использованием __LDREX/__STREX. Пишем следующий код CODE typedef struct { ... volatile unsigned long sync; //переменная для синхонизации доступа к данному элементу } burst_measur;
burst_measur cur_mes;
void mpu_cfg_test() { //настраиваем область внутренней RAM, как разделяемую __DMB(); SCB->SHCSR &= ~SCB_SHCSR_MEMFAULTENA_Msk; MPU->CTRL = 0U;
MPU->RNR = 0UL; MPU->RBAR = 0x20000000UL; MPU->RASR = (0x10UL << MPU_RASR_SIZE_Pos) | MPU_RASR_C_Msk | MPU_RASR_S_Msk | (0x3 << MPU_RASR_AP_Pos) | MPU_RASR_ENABLE_Msk;
MPU->CTRL = MPU_CTRL_PRIVDEFENA_Msk | MPU_CTRL_ENABLE_Msk; SCB->SHCSR |= SCB_SHCSR_MEMFAULTENA_Msk; __DSB(); __ISB();
//выполняем запрос эксклюзивного доступа к переменной cur_mes.sync DWORD sync=0; sync = __LDREX(&cur_mes.sync); __DMB(); soft_int_ini(); //настойка программного прерывания soft_int_on(); //вызов программного прерывания __WFI(); } в обработчике программного прерывания: CODE DWORD sync; do { sync = __LDREX(&cur_mes.sync); sync++; sync = __STREX(sync, &cur_mes.sync); } while (sync); Т.е. сначала выполняется __LDREX(&cur_mes.sync), потом происходит прерывание и выполняется __LDREX(&cur_mes.sync) + __STREX(sync, &cur_mes.sync). По всем документациям, как я их понял, __STREX(sync, &cur_mes.sync) должна возвратить "не ноль", однако возвращает "ноль". Помогите, пожалуйста, разобраться, что я делаю не правильно?
|
|
|
|
|
 |
Ответов
(90 - 104)
|
Jun 9 2017, 20:27
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Forger @ Jun 9 2017, 21:39)  Самое простое - критическими секциями. Да... похоже Вы не понимаете.... Ниже привожу классическую реализацию кольцевого буфера. Данная реализация позволяет: вызывать функцию 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: Я много раз не случайно подчёркивал - процесса или ядра. Так как процесс, выполняющийся на одном ядре, не может загнать процесс другого ядра в критическую секцию. Тогда следуя Вашей логике - кольцевые буфера для межъядерного обмена невозможно использовать. А их используют!  PSS: Приведённый код можно использовать, модифицировать и распространять совершенно свободно. Разрешаю
|
|
|
|
|
Jun 9 2017, 20:44
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(jcxz @ Jun 9 2017, 22:37)  Допустим пишущий процесс считал указатель чтения и, пока он его анализировал, произошло прерывание чтения, которое убавило данных из буфера. Все будет хорошо, пока очередь не исчерпается - совпадут указатели головы и хвоста. Поясню. Скажем в очереди остался всего один элемент. Нужно туда положить еще один, он последний в отправляемом пакете. Кладем его в голову очереди, далее должны сдвинуть указатель головы, но не успели - произошло прерывание передатчика. В нем мы видим, что очередь непустая (сравнили два указателя), еще есть один элемент. Но всего один. Отправляем его, сдвигаем хвост. Видим, что хвост и голова совпали - прекращаем передачу и формируем соотв. сообщение. Возвращаемся из прерывания, сдвигаем голову вперед на один. Ждем сообщения об окончании всей передачи, в том числе и этого последнего байта. Успешно его дожидаемся, ведь передача была только что прекращена. В итоге последний элемент данных не был отправлен. В следующем пакете он будет отправлен, но он попадет в начало след. пакета. В пакетных протоколах эта ситуация недопустима - нарушается содержимое пакетов, если, конечно, имеют значения интервалы между пакетами и между элементами данных. Простой пример - пакетная передача по USART обычными байтами. Понимаю, ситуация редкая и очень трудноуловимая, но она вполне возможна. Именно для исключения таких ситуаций запись в очередь и сдвиг соотв. указателя должны быть атомарными. Если это реализуется атомарно на аппаратном уровне, то действительно никакие критические секции тут ни к чему.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 9 2017, 20:58
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

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

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(jcxz @ Jun 9 2017, 23:58)  К работе кольца это отношения не имеет. Да неужели? Речь тут как раз про возвожность разорвать атомарную операцию записи в буфер обработчиком, который к этому же очереди и обращается. Пусть заполнение буфера и передача обращаются к разным указателям, но как минимум факт исчерпания очереди требует чтения обоих указателей. Поэтому операция записи по указателю и его инкремента должна быть атомарной. Либо не использовать прерывания. Цитата Так можно предположить что и в процессе передачи вашего пакета, пишущий процесс не успеет дописать достаточно данных в буфер в любом месте, а не только в последнем байте. И в любом месте будет сбой. При чем тут не успеет дописать? В том примере все как раз и успевается и места в буфере полно. Передача производится одновременно с заполнением буфера новыми данными. Вполне обычная ситуация. Иначе хватило бы обычного массива, который заранее набивается данными, запускается передача и пока массив не будет весь передан, не будет новой передачи. Тогда тут действительно нафик не нужен кольцевой буфер. Цитата(jcxz @ Jun 10 2017, 00:10)  flush() в читающем процессе: Ring::flush() { rpos = wpos; } - однозначно очистка атомарна без всяких крит. секций Нет, неатомарна - обычная операция чтение-модификация-запись. Может быть прервана прям посреди. Или же прервать другое обращением к этим же полям в фоне задач. Ее нельзя вызывать в фоне задач, пока активно и разрешено соотв. прерывание. Цитата "Реальный код" не ограничивается только передачами по каналу связи. И далеко не везде нужен flush(). Даже скорей - редко где нужен. имха Он нужен лишь при аварийных ситуациях, в штатном коде он действительно не нужен. Я описал устройство, которое работает долго и без обслуги. Если повиснет и не сможет себя привести в норму самостоятельно, то беда будет (((
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 9 2017, 21:20
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Forger @ Jun 9 2017, 23:15)  Пусть заполнение буфера и передача обращаются к разным указателям, но как минимум факт исчерпания очереди требует чтения обоих указателей. Поэтому операция записи по указателю и его инкремента должна быть атомарной. Либо не использовать прерывания. Ещё раз: покажите в приведённом мной примере: прерывание в каком месте может разрушить его работу??? Ткните пальцем. Цитата(Forger @ Jun 9 2017, 23:15)  Нет, неатомарна - обычная операция чтение-модификация-запись. Может быть прервана прям посреди. Или же прервать другое обращением к этим же полям в фоне задач. И что? Работа кольца будет разрушена? Да хоть 100500 обращений. На работу кольца это никак не повлияет.
|
|
|
|
|
Jun 9 2017, 21:20
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(jcxz @ Jun 10 2017, 00:17)  Ещё раз: покажите в приведённом мной примере: прерывание в каком месте может разрушить его работу??? Вычитали один указатель (положили например в R1), произошло прерывание, изменило значение указателя. Вернулись, присвоили другому указателю старое значение, которое положили до этого в R1. Теперь два указателя не одинаковые, а код предполагает, что одинаковые. Как после этого поведет себя код никто не знает.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 9 2017, 21:27
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(jcxz @ Jun 10 2017, 00:20)  Да хоть 100500 обращений. На работу кольца это никак не повлияет. Я ж говорю, ни при чем тут кольцо как таковое, речь про ситуации исчерпания и переполнения буфера, которые обращаются к обоим указателям. А эти операции неатомарны. Пока читаем один указатель, прерывание (там изменяется один из указателей), выходим, и сравниваем с другим указателем старое неактуальное значение. Цитата(jcxz @ Jun 10 2017, 00:24)  тот пример, который Вы описали, вообще никак не говорит о правильности работы кольца. Это пример неправильного его использования. Очень убедительный довод. Оказывается, что кольцо еще и использовать нужно как-то по хитрому, чтобы оно правильно работало  Нафик тогда нужно такое кольцо, вокруг которого нужно плясать с бубном?
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 9 2017, 21:32
|

Профессионал
    
Группа: Свой
Сообщений: 1 215
Регистрация: 22-02-05
Пользователь №: 2 831

|
Цитата(jcxz @ Jun 10 2017, 00:29)  Ничего не понял....  Вот именно! Один про Фому, а второй - про Ерёму )) Цитата Я же просил - покажите на приведённом мной примере где, в каком месте прерывание (или что там ещё) нарушит его работу??? Например, внутри flush. Прямо посреди операции чтения-модификации-запись произойдет прерывание, которое в данный момент работает с этой же очередью.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 9 2017, 21:35
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Forger @ Jun 9 2017, 23:27)  Я ж говорю, ни при чем тут кольцо как таковое, речь про ситуации исчерпания и переполнения буфера, которые обращаются к обоим указателям. Может хватит наконец-то пустословия? Покажите на моём примере ГДЕ ПРОБЛЕМА??? Цитата(Forger @ Jun 9 2017, 23:27)  Очень убедительный довод. Оказывается, что кольцо еще и использовать нужно как-то по хитрому, чтобы оно правильно работало  Нафик тогда нужно такое кольцо, вокруг которого нужно плясать с бубном? Прочитайте выше ещё раз про отвёртку. Или нафик такая отвёртка, которой ухо сломать можно?  Цитата(Forger @ Jun 9 2017, 23:32)  Например, внутри flush. Прямо посреди операции чтения-модификации-запись произойдет прерывание, которое в данный момент работает с этой же очередью. Произойдёт прерывание, которое внутри вызовет write()? И что? Это никак не нарушит кольцо. Подумайте.
|
|
|
|
|
Jun 9 2017, 22:12
|
Частый гость
 
Группа: Участник
Сообщений: 182
Регистрация: 16-10-15
Пользователь №: 88 894

|
Цитата(Forger @ Jun 9 2017, 22:27)  Поменять шило на мыло? По вашей логике получится, что пока этот приоритетный SVC не отработает, ни одно более важное аппаратное прерывание не пройдет. SVC с таким высоким приоритетом, вызываемые из других менее приоритетных прерываний по сути временно задирает их собственный приоритет, который был "ниже плинтуса", отбирая "право голоса" у более приоритетных прерываний. На лицо - инверсия приоритетов. С чем боролись на то и напоролись. Ни чем не лучше глобальной критической секции (запрет/разрешение всех прерываний).
А должно быть совсем иначе - срочные и архиважные аппаратные прерывания должны отработаться предельно быстро, куда надо просемафорить и тут же освободить процессор. Цель - сохранить стек без использования мази от геморроя. Запреты прерываний - вот настоящий геморрой. Насчёт кольцевого буфера (знатный срачь): На сам буфер натравлено два указателя, или лучше два смещения. Один для приёмника другой для передатчика. Всё что между приёмником и передатчиком - читает приёмник, как прочитает - переписывает приёмник. Всё что перед передатчиком и позади приёмника - пишет передатчик, когда записал корректно - переписал передатчик. Когда приёмник с передатчиком складывается - читать нечего. Когда передатчик догоняет приёмник на расстояние одного символа - место для записи заканчивается. Это тупо означает что записывать в буфер нельзя, процесс за это отвечающий - должен ожидать. Складывается ощущение, что не каждый понимает о чём тут разговор. Но читать приятно.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|