_lexa_ 0 4 июня, 2017 Опубликовано 4 июня, 2017 · Жалоба Всем доброе время суток! IDE - IAR+плагин IAR для eclipse+eclipse. Решил проверить, как работает синхронизация с использованием __LDREX/__STREX. Пишем следующий код 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(); } в обработчике программного прерывания: 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) должна возвратить "не ноль", однако возвращает "ноль". Помогите, пожалуйста, разобраться, что я делаю не правильно? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 197 4 июня, 2017 Опубликовано 4 июня, 2017 · Жалоба что я делаю не правильно? Ожидаете "не ноль". STREX всё верно возвращает. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_lexa_ 0 4 июня, 2017 Опубликовано 4 июня, 2017 · Жалоба STREX всё верно возвращает. если не верно задал вопрос - от __LDREX() + __STREX() ожидаю следующее: если выполнено__LDREX(&cur_mes.sync) тогда в случае выполнения в другой части кода "__LDREX(&cur_mes.sync) + __STREX(sync, &cur_mes.sync)" __STREX вернет "не ноль". однако возвращается ноль. Где-то ошибся! Не понятно только где. Поясните вкратце, куда смотреть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 4 июня, 2017 Опубликовано 4 июня, 2017 · Жалоба Где-то ошибся! Не понятно только где. Поясните вкратце, куда смотреть. Каждый раз вспоминаю и снова забываю эти проклятые LDREX/STREX, но суть в том, что если между LDREX и последующим STREX "что-то пошло не так, Карл", а именно - возникло некоторое событие, прерывание, которое СТАВИТ ПОД УГРОЗУ целостность переменной-флага, то STREX вернет типа "ай-ай". То есть, даже если саму переменную-флаг никто и трогать не собирался где-то в недрах прерываний, но прерывание нарушило непрерывное исполнение между LDREX и STREX, то ресурс считается занятым. Поэтому у меня глубокое сомнение, можно ли строить код, как у ТС, - захватывать ресурс в основном коде и выяснять отношения в прерывании: в прерывании по определению ресурс будет занят. Если хотите организовать всякие мъютексы и прочие разделяемые флаги на системе bare bone с наличествующими прерываниями, гляньте на команду SVC. Могу даже код кинуть, если есть интерес. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
_lexa_ 0 4 июня, 2017 Опубликовано 4 июня, 2017 · Жалоба ... Могу даже код кинуть, если есть интерес. Интерес есть. Кидайте. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
KnightIgor 2 4 июня, 2017 Опубликовано 4 июня, 2017 (изменено) · Жалоба Интерес есть. Кидайте. Обработчик прерывания SVC выглядит у меня следующим образом. Много подсмотрено по ссылкам, указанным в комментариях. // --------------------------------------------------------------------------- // // SVC_Handler used for "atomic" operations based on the the fact, that // SVC handler cannot be interrupted by higher placed interrupts. // // Upon call to SVC vector the stack holds saved register: // xPSR 0x1C (7) // PC 0x18 (6) // R14(LR) 0x14 (5) // R12 0x10 (4) // R3 0x0C (3) // R2 0x08 (2) // R1 0x04 (1) // [SP]-> R0 0x00 (0) // // The registers will be restored upon leaving the handler. The handler // to return a result, a value in the stack must be modified. // // Via stacked R0..R3 the parameters can be passed through to the // handler. For this purpose the definition of the user SVC call can // be done as (the type 'int' is for example): // // __svc(n) myfunc(int [,int, int, int]); // // See Chapter 6.19, Figure 6.5 in: // http://infocenter.arm.com/help/topic/com.arm.doc.dui0471c/ \ // DUI0471C_developing_for_arm_processors.pdf // // and // // http://www.mikrocontroller.net/topic/295183 // http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471j/pge1358787038365.html // http://sites.fas.harvard.edu/~libe251/spring2014/CauseOfDefaultISR.txt // // To PRESERVE8 for stack 8 bytes alignment see // http://infocenter.arm.com/help/topic/com.arm.doc.faqs/ka4127.html // __asm void SVC_Handler(void) { PRESERVE8 EXPORT SVC_Handler ; get the pointer to the saved R0-R3 to pass it ; to SVC_Dispatcher() as the second parameter ; (via R1): #ifdef SVC_OS_MODE TST LR, #4 ; kernel mode? ITE EQ MRSEQ R1, MSP ; kernel stack MRSNE R1, PSP ; user stack ; get SVC n instruction code field and ; pass it to SVC_Dispatcher() as the first ; parameter (via R0): LDR R0, [R1, #6*4] ; PC #if defined(__CORE_CM0_H_GENERIC) || defined(__CORE_CM0PLUS_H_GENERIC) SUBS R0, R0, #2 LDRB R0, [R0, #0] ; SVC OPCODE low byte #else LDRB R0, [R0, #-2] ; SVC OPCODE low byte #endif PUSH {LR, R1} EXTERN SVC_Dispatcher BL SVC_Dispatcher ; return value in R0 POP {R1} ; return the result in R0 via stacked R0: STR R0, [R1] #else MOV R1, SP ; kernel=user stack (no OS) ; get SVC n instruction code field and ; pass it to SVC_Dispatcher() as the first ; parameter (via R0): LDR R0, [R1, #6*4] ; PC #if defined(__CORE_CM0_H_GENERIC) || defined(__CORE_CM0PLUS_H_GENERIC) SUBS R0, R0, #2 LDRB R0, [R0, #0] ; SVC OPCODE low byte M0/M0+ #else LDRB R0, [R0, #-2] ; SVC OPCODE low byte M3 and higher #endif PUSH {LR} ; preserve return address EXTERN SVC_Dispatcher BL SVC_Dispatcher ; return value in R0 ; return the result in R0 via stacked R0: STR R0, [SP, #4] ; #4 to skip LR in the stack #endif POP {PC} ; exit by LR } //------------------------------------------------------------------------------ Если исключить условные трансляции, которые разбирают варианты под OS, а также оставить универсальный вариант, работающий и на -M0 (не поддерживающих отрицательные смещения), то обработчик упростится для исполнения и понимания: __asm void SVC_Handler(void) { PRESERVE8 EXPORT SVC_Handler ; get the pointer to the saved R0-R3 to pass it ; to SVC_Dispatcher() as the second parameter ; (via R1): MOV R1, SP ; kernel=user stack (no OS) ; get SVC n instruction code field and ; pass it to SVC_Dispatcher() as the first ; parameter (via R0): LDR R0, [R1, #6*4] ; PC SUBS R0, R0, #2 LDRB R0, [R0, #0] ; SVC OPCODE low byte M0/M0+ PUSH {LR} ; preserve return address EXTERN SVC_Dispatcher BL SVC_Dispatcher ; return value in R0 ; return the result in R0 via stacked R0: STR R0, [SP, #4] ; #4 to skip LR in the stack POP {PC} ; exit by LR } //------------------------------------------------------------------------------ Как видно, все готовилось для вызова C-шной процедуры-обработчика ниже. Я выбросил многие мои специфические ветви, оставив те, что иллюстрируют идею: uint32_t SVC_Dispatcher(int svc, SVC_Param_TypeDef *ptr) { uint32_t res = UINT32_MAX; switch (svc) { case _SVC_ATOMIC_FLAG8: // atomic clear of an U8 flag res = *(uint8_t *)ptr->R0; // return the last state *(uint8_t *)ptr->R0 = 0; // clear it break; case _SVC_ATOMIC_FLAG16: // atomic clear of an U16 flag res = *(uint16_t *)ptr->R0; // return the last state *(uint16_t *)ptr->R0 = 0; // clear it break; case _SVC_ATOMIC_ADD32: // atomic add 32 res = *(uint32_t *)ptr->R0 += (int32_t)ptr->R1; break; case _SVC_ATOMIC_DEC8: res = *(uint8_t *)ptr->R0; if (res) { *(uint8_t *)ptr->R0 = --res; } break; case _SVC_MUTEX8: // mutex in an U8 variable res = !(*(uint8_t *)ptr->R0); // current mutex state *(uint8_t *)ptr->R0 = ptr->R1; // set the value break; } return res; } К этому пристегивается заголовок (существенный фрагмент): typedef struct svc_params_s { uint32_t R0, R1, R2, R3; } SVC_Param_TypeDef; #define _SVC_GETSYSCLOCKVALUE 4 #define _SVC_ATOMIC_FLAG8 8 #define _SVC_ATOMIC_FLAG16 9 #define _SVC_ATOMIC_FLAG32 10 #define _SVC_ATOMIC_ADD8 11 #define _SVC_ATOMIC_ADD16 12 #define _SVC_ATOMIC_ADD32 13 #define _SVC_ATOMIC_DEC8 14 #define _SVC_MUTEX8 16 #define _SVC_ATOMIC_SET8 21 #define _SVC_ATOMIC_SET16 22 #define _SVC_ATOMIC_SET32 23 #define _SVC_CALL_ME_PAR 25 //------------------------------------------------------------------------------ // // Clears an U8 flag pointed by 'pflag' but returns its latest value. // uint8_t __svc(_SVC_ATOMIC_FLAG8) Atomic_Flag8(uint8_t *pflag); //------------------------------------------------------------------------------ // // Adds an I32 value to the value pointed by 'pvalue' and returns the result // uint32_t __svc(_SVC_ATOMIC_ADD32) Atomic_Add32(uint32_t *pvalue, int32_t val); //------------------------------------------------------------------------------ // // Decrements a non zero value pointed by 'pvalue' and returns the result // uint8_t __svc(_SVC_ATOMIC_DEC8) Atomic_Dec8(uint8_t *pvalue); //------------------------------------------------------------------------------ // // Sets the mutex ('setit'=true) pointed by 'pflag' and returns true if success. // Otherwise the mutex has been busy. // // Clears ('setit'=false) the mutex unconditionally. It's up to the user to take // care, if the mutex is allowed to be cleared. The return result to be ignored. // // NOTE: THE MUTEX VALUE AND THE SETIT PARAMETER ARE EXPECTED TO BE ONLY 0 OR 1. // NO OTHER VALUES ARE ALLOWED, SHOULD FOR INSTANCE THE MUTEX VARIABLE BE // CHANGED ELSEWHERE. // int __svc(_SVC_MUTEX8) Mutex8(uint8_t *pflag, uint8_t setit); //------------------------------------------------------------------------------ extern uint32_t SVC_Dispatcher(int svc, SVC_Param_TypeDef *ptr); Теперь о вызове на примере флага: uint8_t sb = Atomic_Flag8(&flag); // atomic reset if (sb) { // ... } Там для некой переменной uint8_t flag, которая устанавливается в прерывании, функция возвращает мне последнее состояние флага и сбрасывает его. Благодаря непрерываемости SVC операция по сбросу флага является атомарной. Изменено 4 июня, 2017 пользователем KnightIgor Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 15 5 июня, 2017 Опубликовано 5 июня, 2017 · Жалоба Благодаря непрерываемости SVC операция по сбросу флага является атомарной. А в чём выгода такого метода по сравнению с простым запретом/разрешением прерываний? Я думаю, что по скорости это даже медленнее (тратится время на сохранение и восстановление контекста). Прелесть же LDREX/STREX как раз в том, что они не блокируют прерывания. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 197 5 июня, 2017 Опубликовано 5 июня, 2017 · Жалоба Где-то ошибся! Не понятно только где. Поясните вкратце, куда смотреть. Если между LDREX и STREX произошло прерывание или был выполнен другой эксклюзивный доступ, до будет нарушение эксклюзивности. Ну и где у вас между LDREX и STREX нарушение эксклюзивности? // SVC_Handler used for "atomic" operations based on the the fact, that // SVC handler cannot be interrupted by higher placed interrupts. Жесть какая. Вот уж действительно - одевание трусов через голову Чем способы: LDREX/STREX или запрет прерываний не угодили? Они в разы менее громоздки. А для SVC можно придумать гораздо более полезные применения. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 19 5 июня, 2017 Опубликовано 5 июня, 2017 · Жалоба А в чём выгода такого метода по сравнению с простым запретом/разрешением прерываний? Полагаю, что выгода в полным отсутствием этих самых запретов/разрешений. Дабы иметь возможность гарантировать стабильную скорость реакции на критичные высокоприоритетные прерывания. Причем, в независимости используется ось или нет. Сам поглядываю на подобные решения, но таких у меня проектов очень мало, поэтому пока вполне хватает "классического" запрет/разрешение прерываний. Хожу как "кот вокруг сметаны" :) Я думаю, что по скорости это даже медленнее (тратится время на сохранение и восстановление контекста). Если производительность камня выбрана хотя бы с небольшим запасом (в случае применения оси загрузка cpu никогда не достигает допустимого предела), то это не имеет значения. Смысл svc - дать гарантию реакции на важные высокоприоритетные прерывания. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 15 5 июня, 2017 Опубликовано 5 июня, 2017 · Жалоба Полагаю, что выгода в полным отсутствием этих самых запретов/разрешений. Если, как пишет, KnightIgor, svc является непрерываемым, то чем это отличается от запрета остальных прерываний? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 19 5 июня, 2017 Опубликовано 5 июня, 2017 · Жалоба Если, как пишет, KnightIgor, svc является непрерываемым, то чем это отличается от запрета остальных прерываний? Почему непрерывный? Любое более приоритетное прерывание может его прервать в любой момент, иначе от этого SVC почти нет никакого проку . Лишь нужно правильно настроить NVIC. Причем, это более "мощное" прерывание может вызвать это же самое SVC, а когда выйдет, продолжится обработка прерванного SVC, и после его завершения тут же опять вызовется новое, отложенное. Так по сути SVC дает гарантию целостности кусков кода. Безусловно, оверхед заметно растет, но цена этому - гарантия реакции на действительно важные события. Под SVC вообще идеально ложатся все сервисы RTOS, судя по всему прям для них SVC и задумывалось. Только вот мне не попадались готовые оси, где это используется ... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 197 5 июня, 2017 Опубликовано 5 июня, 2017 · Жалоба Полагаю, что выгода в полным отсутствием этих самых запретов/разрешений. Дабы иметь возможность гарантировать стабильную скорость реакции на критичные высокоприоритетные прерывания. Так обработчик SVC задержит эти самые "высокоприоритетные прерывания" на ещё в разы большее время! Почему непрерывный? Любое более приоритетное прерывание может его прервать в любой момент, иначе от этого SVC почти нет никакого проку . Лишь нужно правильно настроить NVIC. Потому что для работоспособности этого способа, обработчик SVC должен быть самым высокоприоритетным. Безусловно, оверхед заметно растет, но цена этому - гарантия реакции на действительно важные события. Под SVC вообще идеально ложатся все сервисы RTOS, судя по всему прям для них SVC и задумывалось. Только вот мне не попадались готовые оси, где это используется ... SVC задумывалось совсем не для этого. Для этого задумывались LDREX/STREX. Именно поэтому и нет. Если у Вас другой обработчик может прерывать SVC, то никакой эксклюзивности уже нет. По определению. Причем, это более "мощное" прерывание может вызвать это же самое SVC, а когда выйдет, продолжится обработка прерванного SVC ну-ну... учите матчасть Если это "мощное" прерывание попытается так сделать, то получит HardFault, а не SVC. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
AHTOXA 15 5 июня, 2017 Опубликовано 5 июня, 2017 · Жалоба Почему непрерывный? Любое более приоритетное прерывание может его прервать в любой момент, иначе от этого SVC почти нет никакого проку . Ну вы хоть читайте, на что отвечаете... KnightIgor написал: Благодаря непрерываемости SVC операция по сбросу флага является атомарной. Видите, "благодаря непрерываемости SVC". Это значит, что у svc в его случае самый высокий приоритет. А значит, вход в обработчик SVC блокирует остальные прерывания. Вот я и поинтересовался: А в чём выгода такого метода по сравнению с простым запретом/разрешением прерываний? А теперь вы спрашиваете, "почему непрерывный". Теперь понимаете нить? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Forger 19 5 июня, 2017 Опубликовано 5 июня, 2017 · Жалоба Так обработчик SVC задержит эти самые "высокоприоритетные прерывания" на ещё в разы большее время! Потому что для работоспособности этого способа, обработчик SVC должен быть самым высокоприоритетным. Как раз наоборот - приоритет SVC должен быть самым низким ! (ну кроме разве что PendSV). SVC задумывалось совсем не для этого. Для этого задумывались LDREX/STREX. Именно поэтому и нет. Если у Вас другой обработчик может прерывать SVC, то никакой эксклюзивности уже нет. По определению. Не путайте вызов SVC с вызовом обычной функции! Если это "мощное" прерывание попытается так сделать, то получит HardFault, а не SVC. Да неужели? И по какой причине? Видите, "благодаря непрерываемости SVC". Это значит, что у svc в его случае самый высокий приоритет. Вовсе нет, непрерываемость реализуется благодаря тому, что один вызов SVC не может прервать другой на уровне ЯДРА. Другими словами все возникшие вызовы SVC становятся в очередь. А значит, вход в обработчик SVC блокирует остальные прерывания. Вот я и поинтересовался: Я смотрю, что суть SVC понятна далеко не всем. Это как раз тот самый случай - изучить матчасть. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 197 5 июня, 2017 Опубликовано 5 июня, 2017 · Жалоба Как раз наоборот - приоритет SVC должен быть самым низким ! (ну кроме разве что PendSV). Да неужели? И по какой причине? Если бы Вы открыли мануал на ядро, то узнали бы, что SVC является синхронным исключением. Т.е. - должно обработаться сразу же. А если это не возможно (например - запрещены исключения или, как Вы советуете, оно вызвано внутри более приоритетного ISR), то будет активирован механизм эскалации до Hard Fault. Не путайте вызов SVC с вызовом обычной функции! Я то как раз не путаю, потому что у меня много где SVC используется. А вот Вам следовало бы хотя-бы ознакомиться с предметом, прежде чем советовать что-то. И вызов SVC - как раз очень похож на вызов обычной функции. Для вызывающего процесса. Только на другом уровне привилегий. Именно это и есть его главное предназначение - вызов функций с привилегиями системного уровня из прикладного уровня. Другими словами все возникшие вызовы SVC становятся в очередь. Какая ОЧЕРЕДЬ??? Откройте наконец-то мануал! Хватит фантазировать. SVC - это синхронное прерывание, если оно не может быть активировано немедленно, то будет HF. Я смотрю, что суть SVC понятна далеко не всем. Это как раз тот самый случай - изучить матчасть. Вот именно.... Хватит чушь нести. Здесь не одни чайники собрались. Я думаю - много кто реально использует SVC в проектах. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться