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

Атомарная запись нескольких битовых полей структуры в Си

6 часов назад, Сергей Борщ сказал:

1 * (RCC_CFGR_PLLSRC & -RCC_CFGR_PLLSRC)

Спасибо Сергей !

Я не додумался до такого красивого способа сдвига значения до начала битовой маски !!!

Надо только наверное в макрос завернуть, чтобы не писать два раза одинаковую битовую маску ?

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


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

7 часов назад, amaora сказал:

Если в объявлении нет слов volatile, то компилятор может свернуть запись нескольких битовых полей в одно чтение-модификация-запись. Не проверяли?

Объявлять регистры периферии без volatile - это стрелять себе в ногу.

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


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

7 часов назад, AlexandrY сказал:

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

А у меня и есть по даташиту (только названия регистров изменены на адекватные).

 

7 часов назад, Сергей Борщ сказал:

Тоже вброшу свой вариант...

Вот мои макросы как раз делают то же самое

#define maskbits(src, mask)           ((src) & (mask))
...
#define presrv(src, sbits, rbits)     (maskbits((src) | (sbits), ~(rbits)))
...
#define fieldlsb(mask)                maskbits(mask, -(mask))
#define makefield(mask, val)          maskbits((val) * fieldlsb(mask), mask)
#define updatefield(reg, mask, val)   reg = maskbits(reg, ~(mask)) | makefield(mask, val)
...
#define assertpass(cond)              ((cond) ? 1 : 1 / (cond))
...
#define inrange(val, min, max)        ((val) >= (min) && (val) <= (max))
...

 

Пример макроса, устанавливающего в нужное поле периферийного регистра число от 1 до 16. Если число вне этого диапазона, компилятор сформирует предупреждение (или ошибку)

#define cpc_ClkOutDiv(x) updatefield(CPC->CLKOUTCR, CPC_CLKOUTCR_DIV, assertpass(inrange((x), 1UL, 16UL))*((x) - 1UL))

 

Пример макроса, устанавливающего или сбрасывающего флаг в периферийном регистре при выполнении или невыполнении условия, не используя оператора if() и связанного с ним инструкций перехода Bx

#define definebits(reg, bits, bcond) reg = maskbits(reg, ~(bits)) | maskbits(bits, -(bcond))

...

#define cpc_ClkOutCmd(s) definebits(CPC->CLKOUTCR, CPC_CLKOUTCR_OUTEN, (s))

...

cpc_ClkOutCmd(ENABLE); // установит бит CPC_CLKOUTCR_OUTEN в регистр CPC->CLKOUTCR

// а можно и в run-time
void CANCmd(const u32 cmd)
{
  definebits(CAN->CR1, CAN_CR1_EN, cmd);
  ...
}

 

Короче, выделил некоторое количество макросов (что-то около 50) для элементарных операций, зато не надо теперь каждый раз голову ломать и вспоминать, на сколько и куда мне чего надо сдвинуть, чтобы куда-то там попасть:blush:

 

Только что, jcxz сказал:

Объявлять регистры периферии без volatile - это стрелять себе в ногу.

+1.

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


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

1 minute ago, Arlleex said:

 зато не надо теперь каждый раз голову ломать и вспоминать.

Почему с ходу у меня сложилось как раз противоположное впечатление :mda:

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


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

Только что, Forger сказал:

Почему с ходу у меня сложилось как раз противоположное впечатление

Я уже как-то притерся ко всем этим макросам и пользую их на ура.

Но тут уж кому как удобно:smile:

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


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

6 hours ago, jcxz said:

Объявлять регистры периферии без volatile - это стрелять себе в ногу.

Можно несколько объявлений иметь. Можно барьеры расставлять. Локальные копии делать.

То, что хочет ТС не получится с volatile. На каждую операцию с битовым полем будет и чтение и запись.

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


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

Только что, amaora сказал:

То, что хочет ТС не получится с volatile. На каждую операцию с битовым полем будет и чтение и запись.

В случае работы с битовыми полями структуры - да. В случае работы с предопределенными битовыми масками - не обязательно. Да и ассемблерный выхлоп я смотрю на минимальной и максимальной оптимизации.

Я пишу под ARM Cortex-M3.

Под атомарностью в целом можно понимать несколько несвязанных между собой операций:

1. Атомарная работа с одним или несколькими регистрами (LDR/LDRD/STR/STRD).

2. Атомарная работа с переменными в разделяемой среде, например, в многопоточной вытесняющей ОС (команды LDREX/STREX и средства синхронизации самой ОС).

3. Атомарная работа с битовыми группами с точки зрения языка Си.

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

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


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

17 минут назад, Arlleex сказал:

я хотел одной строкой Си-кода инициализировать несколько битовых полей структуры

Видимо многие "инициализировать" восприняли как "изменить".

Такое голые си кушают?

sPLL0СR = 
{
  12345,	// M
  0,		// __RESERVED__
  67,		// N
  1,		// EN
  .............
};

Еще в сях можно было с указанием имен полей структуры заполнять (в плюсах нельзя, поэтому не помню точно синтаксис, что-то вроде { .N = 67, .M = 12345, .EN = 1 } )

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


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

Только что, Сергей Борщ сказал:

Видимо многие "инициализировать" восприняли как "изменить".

Еще в сях можно было с указанием имен полей структуры заполнять...

Конечно можно, насколько помню, такая инициализация называется инициализацией составным литералом (составные литералы были введены в C99).

Правда, перед инициализатором нужно привести тип

typedef struct
{
  u32 a : 10;
  u32 b : 15;
  u32   :  7;
}sREG;

...

sREG a;
a = (sREG){.a = 5UL, b = 3UL};

 

Однако все равно такой способ не подойдет, поскольку он пишет не просто структуру, а периферийный регистр, в котором есть и другие биты, состояние которых мне неведомо и трогать которые не нужно. Там же и резервные биты, о которых производитель может кричать "что бы там ни лежало, не трогайте и не меняйте их значение". В общем итоге, как я понял, одним выражением сделать чтение-модификацию-запись синтаксисом работы с битами структур не получится.

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


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

5 hours ago, Arlleex said:

Под атомарностью в целом можно понимать несколько несвязанных между собой операций:

То, что Вам нужно это называется оптимизация доступа к регистрам периферии, а не атомарность. У меня подобная задача возникала с обертками над GPIO. Несколько записей в один регистр можно свернуть в одну, но компилятор видя volatile в объявлении не может этого сделать. Но я пришел к тому, что вся эта возня не стоит затраченных усилий, лучше оставить неоптимальный но простой и надежный вариант. А при особой необходимости делать оптимизированные варианты кода на низком уровне, на примере GPIO это может быть чтение нескольких входов одной операцией.

Один из простых вариантов, уже предлагали выше, делать локальные копии. То же что у Вас с макросами, только можно скопировать в локальную структуру, изменить поля и записать обратно. Тогда количество операций перестанет зависеть от количества изменяемых полей. Во что это красиво завернуть в C, вот вопрос без ответа.

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


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

6 minutes ago, amaora said:

Во что это красиво завернуть в C, вот вопрос без ответа.

Да нефиг делать:

1. объявляем union, в которой объединяем структуру с битовыми полями и "сырое" (raw) значение регистра.

2. создаем локально экземпляр этого union

3. читаем нужный регистр в соотв. поле raw

4. меняем битовые поля в нашей структуре

5. пишем в регистр значение поля raw

Сам так делал, просто, эффективно, но требует описания всех этих union и структур.

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

 

 

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


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

On 7/30/2019 at 8:14 PM, Arlleex said:

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

Имеется, допустим,


// битовые представления регистров
typedef union
{
  struct
  {
    u32 M            : 15UL;
    u32 __RESERVE1__ :  1UL;
    u32 N            :  8UL;
    u32 EN           :  1UL;
    u32 SEL          :  1UL;
    u32 RDY          :  1UL;
    u32 __RESERVE2__ :  5UL;
  };
  u32 RAW;
}sPLL0СR;

...

// описание регистров периферии
typedef struct
{
  ...
  sPLL0СR СR;
  ...
}sPPL0;

 

Теперь я хочу одним махом каким-то образом сразу инициализировать поля M и N в структуре CR. Возможно ли такое средствами языка?

Ну, я бы не рекомендовал это для начинающих, но в IAR это вроде работает:

sPPL0 a;

a=(sPPL0){.CR.M=5,.CR.N=6};

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

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


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

artemkad, посмотрите мой ответ Сергею пару сообщениями выше. Повторюсь, этот код не сделает подобие a |= 5 << Mpos | 6 << Npos.

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


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

8 hours ago, Arlleex said:

artemkad, посмотрите мой ответ Сергею пару сообщениями выше. Повторюсь, этот код не сделает подобие a |= 5 << Mpos | 6 << Npos.

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

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


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

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

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

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

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

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

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

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

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

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