Перейти к содержанию
    

Всем доброе время суток!

 

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) должна возвратить "не ноль", однако возвращает "ноль".

 

Помогите, пожалуйста, разобраться, что я делаю не правильно?

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

что я делаю не правильно?

Ожидаете "не ноль".

STREX всё верно возвращает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

STREX всё верно возвращает.

если не верно задал вопрос -

от __LDREX() + __STREX() ожидаю следующее:

если выполнено__LDREX(&cur_mes.sync)

тогда в случае выполнения в другой части кода "__LDREX(&cur_mes.sync) + __STREX(sync, &cur_mes.sync)" __STREX вернет "не ноль".

однако возвращается ноль.

 

Где-то ошибся! Не понятно только где. Поясните вкратце, куда смотреть.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Где-то ошибся! Не понятно только где. Поясните вкратце, куда смотреть.

Каждый раз вспоминаю и снова забываю эти проклятые LDREX/STREX, но суть в том, что если между LDREX и последующим STREX "что-то пошло не так, Карл", а именно - возникло некоторое событие, прерывание, которое СТАВИТ ПОД УГРОЗУ целостность переменной-флага, то STREX вернет типа "ай-ай". То есть, даже если саму переменную-флаг никто и трогать не собирался где-то в недрах прерываний, но прерывание нарушило непрерывное исполнение между LDREX и STREX, то ресурс считается занятым. Поэтому у меня глубокое сомнение, можно ли строить код, как у ТС, - захватывать ресурс в основном коде и выяснять отношения в прерывании: в прерывании по определению ресурс будет занят.

Если хотите организовать всякие мъютексы и прочие разделяемые флаги на системе bare bone с наличествующими прерываниями, гляньте на команду SVC. Могу даже код кинуть, если есть интерес.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Интерес есть. Кидайте.

Обработчик прерывания 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Благодаря непрерываемости SVC операция по сбросу флага является атомарной.

А в чём выгода такого метода по сравнению с простым запретом/разрешением прерываний? Я думаю, что по скорости это даже медленнее (тратится время на сохранение и восстановление контекста).

Прелесть же LDREX/STREX как раз в том, что они не блокируют прерывания.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Где-то ошибся! Не понятно только где. Поясните вкратце, куда смотреть.

Если между 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.

Жесть какая. Вот уж действительно - одевание трусов через голову :biggrin:

Чем способы: LDREX/STREX или запрет прерываний не угодили? Они в разы менее громоздки.

А для SVC можно придумать гораздо более полезные применения.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А в чём выгода такого метода по сравнению с простым запретом/разрешением прерываний?

Полагаю, что выгода в полным отсутствием этих самых запретов/разрешений.

Дабы иметь возможность гарантировать стабильную скорость реакции на критичные высокоприоритетные прерывания.

Причем, в независимости используется ось или нет.

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

Хожу как "кот вокруг сметаны" :)

 

Я думаю, что по скорости это даже медленнее (тратится время на сохранение и восстановление контекста).

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

Смысл svc - дать гарантию реакции на важные высокоприоритетные прерывания.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Полагаю, что выгода в полным отсутствием этих самых запретов/разрешений.

Если, как пишет, KnightIgor, svc является непрерываемым, то чем это отличается от запрета остальных прерываний?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если, как пишет, KnightIgor, svc является непрерываемым, то чем это отличается от запрета остальных прерываний?

Почему непрерывный? Любое более приоритетное прерывание может его прервать в любой момент, иначе от этого SVC почти нет никакого проку .

Лишь нужно правильно настроить NVIC.

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

Так по сути SVC дает гарантию целостности кусков кода.

Безусловно, оверхед заметно растет, но цена этому - гарантия реакции на действительно важные события.

Под SVC вообще идеально ложатся все сервисы RTOS, судя по всему прям для них SVC и задумывалось.

Только вот мне не попадались готовые оси, где это используется ...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Полагаю, что выгода в полным отсутствием этих самых запретов/разрешений.

Дабы иметь возможность гарантировать стабильную скорость реакции на критичные высокоприоритетные прерывания.

Так обработчик SVC задержит эти самые "высокоприоритетные прерывания" на ещё в разы большее время!

 

Почему непрерывный? Любое более приоритетное прерывание может его прервать в любой момент, иначе от этого SVC почти нет никакого проку .

Лишь нужно правильно настроить NVIC.

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

 

Безусловно, оверхед заметно растет, но цена этому - гарантия реакции на действительно важные события.

Под SVC вообще идеально ложатся все сервисы RTOS, судя по всему прям для них SVC и задумывалось.

Только вот мне не попадались готовые оси, где это используется ...

SVC задумывалось совсем не для этого. Для этого задумывались LDREX/STREX. Именно поэтому и нет.

Если у Вас другой обработчик может прерывать SVC, то никакой эксклюзивности уже нет. По определению.

 

Причем, это более "мощное" прерывание может вызвать это же самое SVC, а когда выйдет, продолжится обработка прерванного SVC

ну-ну... учите матчасть :biggrin:

Если это "мощное" прерывание попытается так сделать, то получит HardFault, а не SVC.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Почему непрерывный? Любое более приоритетное прерывание может его прервать в любой момент, иначе от этого SVC почти нет никакого проку .

Ну вы хоть читайте, на что отвечаете...

 

KnightIgor написал:

Благодаря непрерываемости SVC операция по сбросу флага является атомарной.

Видите, "благодаря непрерываемости SVC". Это значит, что у svc в его случае самый высокий приоритет. А значит, вход в обработчик SVC блокирует остальные прерывания. Вот я и поинтересовался:

А в чём выгода такого метода по сравнению с простым запретом/разрешением прерываний?

А теперь вы спрашиваете, "почему непрерывный".

Теперь понимаете нить?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так обработчик SVC задержит эти самые "высокоприоритетные прерывания" на ещё в разы большее время!

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

Как раз наоборот - приоритет SVC должен быть самым низким ! (ну кроме разве что PendSV).

 

SVC задумывалось совсем не для этого. Для этого задумывались LDREX/STREX. Именно поэтому и нет.

Если у Вас другой обработчик может прерывать SVC, то никакой эксклюзивности уже нет. По определению.

Не путайте вызов SVC с вызовом обычной функции!

 

Если это "мощное" прерывание попытается так сделать, то получит HardFault, а не SVC.

Да неужели? И по какой причине?

 

 

Видите, "благодаря непрерываемости SVC". Это значит, что у svc в его случае самый высокий приоритет.

Вовсе нет, непрерываемость реализуется благодаря тому, что один вызов SVC не может прервать другой на уровне ЯДРА.

Другими словами все возникшие вызовы SVC становятся в очередь.

 

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

Я смотрю, что суть SVC понятна далеко не всем. Это как раз тот самый случай - изучить матчасть.

 

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Как раз наоборот - приоритет SVC должен быть самым низким ! (ну кроме разве что PendSV).

Да неужели? И по какой причине?

Если бы Вы открыли мануал на ядро, то узнали бы, что SVC является синхронным исключением. Т.е. - должно обработаться сразу же.

А если это не возможно (например - запрещены исключения или, как Вы советуете, оно вызвано внутри более приоритетного ISR), то будет активирован механизм эскалации до Hard Fault.

 

Не путайте вызов SVC с вызовом обычной функции!

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

И вызов SVC - как раз очень похож на вызов обычной функции. Для вызывающего процесса. Только на другом уровне привилегий.

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

 

Другими словами все возникшие вызовы SVC становятся в очередь.

Какая ОЧЕРЕДЬ??? Откройте наконец-то мануал! Хватит фантазировать.

SVC - это синхронное прерывание, если оно не может быть активировано немедленно, то будет HF.

 

Я смотрю, что суть SVC понятна далеко не всем. Это как раз тот самый случай - изучить матчасть.

Вот именно.... Хватит чушь нести. Здесь не одни чайники собрались. Я думаю - много кто реально использует SVC в проектах.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...