|
__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) должна возвратить "не ноль", однако возвращает "ноль". Помогите, пожалуйста, разобраться, что я делаю не правильно?
|
|
|
|
|
Jun 4 2017, 18:39
|
Участник

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

|
Цитата(jcxz @ Jun 4 2017, 17:10)  STREX всё верно возвращает. если не верно задал вопрос - от __LDREX() + __STREX() ожидаю следующее: если выполнено__LDREX(&cur_mes.sync) тогда в случае выполнения в другой части кода "__LDREX(&cur_mes.sync) + __STREX(sync, &cur_mes.sync)" __STREX вернет "не ноль". однако возвращается ноль. Где-то ошибся! Не понятно только где. Поясните вкратце, куда смотреть.
|
|
|
|
|
Jun 4 2017, 19:34
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(_lexa_ @ Jun 4 2017, 19:39)  Где-то ошибся! Не понятно только где. Поясните вкратце, куда смотреть. Каждый раз вспоминаю и снова забываю эти проклятые LDREX/STREX, но суть в том, что если между LDREX и последующим STREX "что-то пошло не так, Карл", а именно - возникло некоторое событие, прерывание, которое СТАВИТ ПОД УГРОЗУ целостность переменной-флага, то STREX вернет типа "ай-ай". То есть, даже если саму переменную-флаг никто и трогать не собирался где-то в недрах прерываний, но прерывание нарушило непрерывное исполнение между LDREX и STREX, то ресурс считается занятым. Поэтому у меня глубокое сомнение, можно ли строить код, как у ТС, - захватывать ресурс в основном коде и выяснять отношения в прерывании: в прерывании по определению ресурс будет занят. Если хотите организовать всякие мъютексы и прочие разделяемые флаги на системе bare bone с наличествующими прерываниями, гляньте на команду SVC. Могу даже код кинуть, если есть интерес.
|
|
|
|
|
Jun 4 2017, 20:14
|
Участник

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

|
Цитата(KnightIgor @ Jun 4 2017, 19:34)  ... Могу даже код кинуть, если есть интерес. Интерес есть. Кидайте.
|
|
|
|
|
Jun 4 2017, 21:14
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(_lexa_ @ Jun 4 2017, 21:14)  Интерес есть. Кидайте. Обработчик прерывания 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 операция по сбросу флага является атомарной.
Сообщение отредактировал KnightIgor - Jun 4 2017, 21:14
|
|
|
|
|
Jun 5 2017, 07:49
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(_lexa_ @ Jun 4 2017, 20:39)  Где-то ошибся! Не понятно только где. Поясните вкратце, куда смотреть. Если между LDREX и STREX произошло прерывание или был выполнен другой эксклюзивный доступ, до будет нарушение эксклюзивности. Ну и где у вас между LDREX и STREX нарушение эксклюзивности? Цитата(KnightIgor @ Jun 4 2017, 23:14)  // SVC_Handler used for "atomic" operations based on the the fact, that // SVC handler cannot be interrupted by higher placed interrupts. Жесть какая. Вот уж действительно - одевание трусов через голову Чем способы: LDREX/STREX или запрет прерываний не угодили? Они в разы менее громоздки. А для SVC можно придумать гораздо более полезные применения.
|
|
|
|
|
Jun 5 2017, 08:17
|

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

|
Цитата(AHTOXA @ Jun 5 2017, 08:38)  А в чём выгода такого метода по сравнению с простым запретом/разрешением прерываний? Полагаю, что выгода в полным отсутствием этих самых запретов/разрешений. Дабы иметь возможность гарантировать стабильную скорость реакции на критичные высокоприоритетные прерывания. Причем, в независимости используется ось или нет. Сам поглядываю на подобные решения, но таких у меня проектов очень мало, поэтому пока вполне хватает "классического" запрет/разрешение прерываний. Хожу как "кот вокруг сметаны"  Цитата Я думаю, что по скорости это даже медленнее (тратится время на сохранение и восстановление контекста). Если производительность камня выбрана хотя бы с небольшим запасом (в случае применения оси загрузка cpu никогда не достигает допустимого предела), то это не имеет значения. Смысл svc - дать гарантию реакции на важные высокоприоритетные прерывания.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 5 2017, 08:24
|

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

|
Цитата(AHTOXA @ Jun 5 2017, 11:22)  Если, как пишет, KnightIgor, svc является непрерываемым, то чем это отличается от запрета остальных прерываний? Почему непрерывный? Любое более приоритетное прерывание может его прервать в любой момент, иначе от этого SVC почти нет никакого проку . Лишь нужно правильно настроить NVIC. Причем, это более "мощное" прерывание может вызвать это же самое SVC, а когда выйдет, продолжится обработка прерванного SVC, и после его завершения тут же опять вызовется новое, отложенное. Так по сути SVC дает гарантию целостности кусков кода. Безусловно, оверхед заметно растет, но цена этому - гарантия реакции на действительно важные события. Под SVC вообще идеально ложатся все сервисы RTOS, судя по всему прям для них SVC и задумывалось. Только вот мне не попадались готовые оси, где это используется ...
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 5 2017, 08:49
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Forger @ Jun 5 2017, 10:17)  Полагаю, что выгода в полным отсутствием этих самых запретов/разрешений. Дабы иметь возможность гарантировать стабильную скорость реакции на критичные высокоприоритетные прерывания. Так обработчик SVC задержит эти самые "высокоприоритетные прерывания" на ещё в разы большее время! Цитата(Forger @ Jun 5 2017, 10:24)  Почему непрерывный? Любое более приоритетное прерывание может его прервать в любой момент, иначе от этого SVC почти нет никакого проку . Лишь нужно правильно настроить NVIC. Потому что для работоспособности этого способа, обработчик SVC должен быть самым высокоприоритетным. Цитата(Forger @ Jun 5 2017, 10:24)  Безусловно, оверхед заметно растет, но цена этому - гарантия реакции на действительно важные события. Под SVC вообще идеально ложатся все сервисы RTOS, судя по всему прям для них SVC и задумывалось. Только вот мне не попадались готовые оси, где это используется ... SVC задумывалось совсем не для этого. Для этого задумывались LDREX/STREX. Именно поэтому и нет. Если у Вас другой обработчик может прерывать SVC, то никакой эксклюзивности уже нет. По определению. Цитата(Forger @ Jun 5 2017, 10:24)  Причем, это более "мощное" прерывание может вызвать это же самое SVC, а когда выйдет, продолжится обработка прерванного SVC ну-ну... учите матчасть Если это "мощное" прерывание попытается так сделать, то получит HardFault, а не SVC.
|
|
|
|
|
Jun 5 2017, 08:54
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(Forger @ Jun 5 2017, 13:24)  Почему непрерывный? Любое более приоритетное прерывание может его прервать в любой момент, иначе от этого SVC почти нет никакого проку . Ну вы хоть читайте, на что отвечаете... KnightIgor написал: Цитата(KnightIgor @ Jun 5 2017, 02:14)  Благодаря непрерываемости SVC операция по сбросу флага является атомарной. Видите, "благодаря непрерываемости SVC". Это значит, что у svc в его случае самый высокий приоритет. А значит, вход в обработчик SVC блокирует остальные прерывания. Вот я и поинтересовался: Цитата(AHTOXA @ Jun 5 2017, 10:38)  А в чём выгода такого метода по сравнению с простым запретом/разрешением прерываний? А теперь вы спрашиваете, "почему непрерывный". Теперь понимаете нить?
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Jun 5 2017, 09:11
|

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

|
Цитата(jcxz @ Jun 5 2017, 11:49)  Так обработчик SVC задержит эти самые "высокоприоритетные прерывания" на ещё в разы большее время! Потому что для работоспособности этого способа, обработчик SVC должен быть самым высокоприоритетным. Как раз наоборот - приоритет SVC должен быть самым низким ! (ну кроме разве что PendSV). Цитата SVC задумывалось совсем не для этого. Для этого задумывались LDREX/STREX. Именно поэтому и нет. Если у Вас другой обработчик может прерывать SVC, то никакой эксклюзивности уже нет. По определению. Не путайте вызов SVC с вызовом обычной функции! Цитата Если это "мощное" прерывание попытается так сделать, то получит HardFault, а не SVC. Да неужели? И по какой причине? Цитата(AHTOXA @ Jun 5 2017, 11:54)  Видите, "благодаря непрерываемости SVC". Это значит, что у svc в его случае самый высокий приоритет. Вовсе нет, непрерываемость реализуется благодаря тому, что один вызов SVC не может прервать другой на уровне ЯДРА. Другими словами все возникшие вызовы SVC становятся в очередь. Цитата А значит, вход в обработчик SVC блокирует остальные прерывания. Вот я и поинтересовался: Я смотрю, что суть SVC понятна далеко не всем. Это как раз тот самый случай - изучить матчасть.
--------------------
Кругозор некоторых людей - круг с нулевым радиусом. Они называют его "точкой зрения".
|
|
|
|
|
Jun 5 2017, 09:37
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Forger @ Jun 5 2017, 11:11)  Как раз наоборот - приоритет SVC должен быть самым низким ! (ну кроме разве что PendSV). Да неужели? И по какой причине? Если бы Вы открыли мануал на ядро, то узнали бы, что SVC является синхронным исключением. Т.е. - должно обработаться сразу же. А если это не возможно (например - запрещены исключения или, как Вы советуете, оно вызвано внутри более приоритетного ISR), то будет активирован механизм эскалации до Hard Fault. Цитата(Forger @ Jun 5 2017, 11:11)  Не путайте вызов SVC с вызовом обычной функции! Я то как раз не путаю, потому что у меня много где SVC используется. А вот Вам следовало бы хотя-бы ознакомиться с предметом, прежде чем советовать что-то. И вызов SVC - как раз очень похож на вызов обычной функции. Для вызывающего процесса. Только на другом уровне привилегий. Именно это и есть его главное предназначение - вызов функций с привилегиями системного уровня из прикладного уровня. Цитата(Forger @ Jun 5 2017, 11:11)  Другими словами все возникшие вызовы SVC становятся в очередь. Какая ОЧЕРЕДЬ??? Откройте наконец-то мануал! Хватит фантазировать. SVC - это синхронное прерывание, если оно не может быть активировано немедленно, то будет HF. Цитата(Forger @ Jun 5 2017, 11:11)  Я смотрю, что суть SVC понятна далеко не всем. Это как раз тот самый случай - изучить матчасть. Вот именно.... Хватит чушь нести. Здесь не одни чайники собрались. Я думаю - много кто реально использует SVC в проектах.
|
|
|
|
5 чел. читают эту тему (гостей: 5, скрытых пользователей: 0)
Пользователей: 0
|
|
|