Jump to content

    

STM32F7XX аналог bit-banding

В последние годы пришлось достаточно много писать для STM32F1XX и STM32F4XX.

Везде активно пользовался механизмом bit-banding для доступа к битовым переменным.

Сейчас заказал отладку с STM32F746ZG.

Читаю документацию и вижу, что там никаких упоминаний про bit-banding при работе с оперативной памятью...

Ткните носом, что на замену? Может появились неделимые операции "чтение-модификация-запись"?

Share this post


Link to post
Share on other sites
Читаю документацию и вижу, что там никаких упоминаний про bit-banding при работе с оперативной памятью...

Ткните носом, что на замену? Может появились неделимые операции "чтение-модификация-запись"?

Механизм LDREX/STREX есть уже в M3.

Share this post


Link to post
Share on other sites
Механизм LDREX/STREX есть уже в M3.

Это немного не то.

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

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

В какой то мере это танцы с бубном, которые в некоторых случаях ещё сложнее простого запрета прерываний.

Хотелось бы обойтись без всего этого.

Чтобы код на С выглядел как то так:

X &= mask; или X |= mask;

И при этом, разместив переменную в определенной секции памяти или объявив эту переменную особым образом, мы бы получили результат, когда компилятор генерирует непрерываемый код (в идеале из одной команды), который модифицирует переменную прямо в памяти.

Например IAR (всегда?) генерирует код из трёх отдельных этапов - чтение - модификация - запись.

Механизм bit-banding в Cortex-M3 и Cortex-M4 позволял достаточно красиво обойти эту проблему.

А в Cortex-M7 решили отказаться от этого без каких либо альтернатив?

 

Share this post


Link to post
Share on other sites

Идея bit-banding в том, что он делается не процессором, а контроллером памяти.

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

С другой это приводит к усложнению логики коммутатора шин.

Наиболее вероятная, на мой взгляд, причина - скорострельности ОЗУ стало недостаточно чтобы провести цикл чтение-модификация-запись за один такт, а за два делать - неоправданное усложнение контроллера и всего шинного хозяйства, которое в семёрке и так не маленькое.

 

Ну и со стороны компиляторов поддержки так и не дождались.

Share this post


Link to post
Share on other sites
Это немного не то.

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

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

... которая будет успешной только при эксклюзивном доступе.

И в чём разница?

Вы думаете классический битбандинг какие-то специальные операции делал по шине памяти предназначенные только для битов? ;)

Нет, процессор так же выполнял чтение-модификацию бита-запись для всей ячейки содержащей данный бит, но скрыто от программиста и в непрерывном режиме.

 

Вот моя реализация аналога команды CMPXCHG для x86:

PUBLIC   _Z13AtomicCmpSwapPVjjj
;АтомарнаЯ операциЯ "сравнение и обмен" длЯ типа u32.
;u32  AtomicCmpSwap(u32 volatile *ptr, u32 newVal, u32 cmpVal);
_Z13AtomicCmpSwapPVjjj:
aCmpSwap32_01: LDREX    R12, [R0]
              CMP      R12, R2
              ITT      EQ
              STREXEQ  R3, R1, [R0]
              CMPEQ    R3, #1
              BEQ      aCmpSwap32_01
              MOV      R0, R12
              BX       LR

На основе этой процедуры можно сделать и другие операции атомарной модификации памяти (хоть |=, хоть &=, ...).

 

Чтобы код на С выглядел как то так:

X &= mask; или X |= mask;

И при этом, разместив переменную в определенной секции памяти или объявив эту переменную особым образом, мы бы получили результат, когда компилятор генерирует непрерываемый код (в идеале из одной команды), который модифицирует переменную прямо в памяти.

Например IAR (всегда?) генерирует код из трёх отдельных этапов - чтение - модификация - запись.

Механизм bit-banding в Cortex-M3 и Cortex-M4 позволял достаточно красиво обойти эту проблему.

Святая наивность.... :biggrin::biggrin::biggrin::biggrin::biggrin:

Похоже Вы никогда не заглядывали, что генерит IAR при работе с bit-banding-ом. Там команд подчас ещё больше, чем в той процедуре, что я привёл выше.

И это не "IAR генерирует", это архитектура Cortex-M такова - у него нет команд непосредственно модифицирующих данные в памяти. Чтобы получить то, что Вы хотите, Вам нужно поменять процессор.

Share this post


Link to post
Share on other sites
Ну и со стороны компиляторов поддержки так и не дождались.

Да и со стороны пользователей тоже. Я, к примеру, не стал заморачиваться. Смысл какой? Если бы это было общее решение для всех процов, ну тогда да. Или подход.

Да и вообще, надо от головы плясать. Если бы язык С поддерживал бы какую то директиву, которая бы обеспечивала атомарные операции с данными, либо механизм, то тогда производители компиляторов вынуждены были бы реализовывать эти директивы средствами имеющимися в наличии у конкретного CPU. Вот тогда бы всё закрутилось. А так ... Пустое.

Share this post


Link to post
Share on other sites
Если бы язык С поддерживал бы какую то директиву, которая бы обеспечивала атомарные операции с данными, либо механизм, то тогда производители компиляторов вынуждены были бы реализовывать эти директивы средствами имеющимися в наличии у конкретного CPU. Вот тогда бы всё закрутилось. А так ... Пустое.

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

Share this post


Link to post
Share on other sites
А в Cortex-M7 решили отказаться от этого без каких либо альтернатив?

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

 

Share this post


Link to post
Share on other sites
Вы думаете классический битбандинг какие-то специальные операции делал по шине памяти предназначенные только для битов? ;)

Нет, не думаю.

 

Похоже Вы никогда не заглядывали, что генерит IAR при работе с bit-banding-ом. Там команд подчас ещё больше, чем в той процедуре, что я привёл выше.

Нет, заглядывал.

 

Я прекрасно знаю, как работать с разделяемыми переменными программными средствами.

И эти механизмы можно реализовать в любой архитектуре.

Чаще всего существует поддержка на уровне системы команд, т.е. есть специальные команды для упрощения реализации, в Cortex M это LDREX/STREX.

Если речь идёт о реализации семафоров, то bit-banding ничем не поможет,так как он не обеспечивает атомарный доступ (теперь уже к биту).

 

Позвольте уточню, для чего я использовал этот механизм:

Есть переменная, содержащая набор флагов. С помощью bit-banding я мог спокойно установить или сбросить любой бит в этой переменой, не заботясь о том, что одновременно другие процессы с разными приоритетами могут работать с другими битами этой переменной.

И похоже для Cortex M7 можно работать с битовыми структурами только через критические секции.

 

 

 

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

Спорить не буду, но я не занимался вычислением адресов.

Я создавал две секции памяти, одна в bit области, другая в band области памяти.

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

Может быть и коряво, но задачу решало, причём с минимумом накладных расходов.

 

 

 

Share this post


Link to post
Share on other sites
Есть переменная, содержащая набор флагов. С помощью bit-banding я мог спокойно установить или сбросить любой бит в этой переменой, не заботясь о том, что одновременно другие процессы с разными приоритетами могут работать с другими битами этой переменной.

При этом если работать с несколькими битами, то все становится сразу неудобным. При этом количество тактов на этот bit-banding это не один такт.

И похоже для Cortex M7 можно работать с битовыми структурами только через критические секции.

Разумнее с проверкой доступа к памяти.

Спорить не буду, но я не занимался вычислением адресов.

Я создавал две секции памяти, одна в bit области, другая в band области памяти.

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

битовая НЕ может быть "нужного размера" :(. Она только битовая.

вторая - структура из переменных int32 для доступа к отдельным битам.

В свою очередь 32bit обеспечивает доступ сразу ко всем 32 битам, а не отдельным.

Может быть и коряво, но задачу решало, причём с минимумом накладных расходов.

Минимума накладных расходов по производительности, увы, не было. Как то то, что решаемую задачу Вы тоже описали шиворот на выворот.

 

 

Share this post


Link to post
Share on other sites
При этом если работать с несколькими битами, то все становится сразу неудобным. При этом количество тактов на этот bit-banding это не один такт.

Ну так я с этим и не спорю.

 

битовая НЕ может быть "нужного размера" :(. Она только битовая.

В данном случае имелась в виду переменная, состоящая из нескольких битов (8, 16, 32, 64).

 

В свою очередь 32bit обеспечивает доступ сразу ко всем 32 битам, а не отдельным.

А разве смысл bit-banding не в том, что доступ к отдельному биту производится через полноразмерную 32-битную переменную из band области? Нет?

А у меня вроде работает именно так, и в документации так написано. Или опять вопрос в терминологии?

 

Минимума накладных расходов по производительности, увы, не было. Как то то, что решаемую задачу Вы тоже описали шиворот на выворот.

Да я вроде просто в Cortex M7 не обнаружил bit banding. Была мысль, что придумали что-то новое, более продвинутое, что я не увидел в документации.

Именно в это я попросил ткнуть меня носом.

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

 

Share this post


Link to post
Share on other sites
Позвольте уточню, для чего я использовал этот механизм:

Есть переменная, содержащая набор флагов. С помощью bit-banding я мог спокойно установить или сбросить любой бит в этой переменой, не заботясь о том, что одновременно другие процессы с разными приоритетами могут работать с другими битами этой переменной.

И похоже для Cortex M7 можно работать с битовыми структурами только через критические секции.

Блин... Похоже Вы ничего не поняли... :wacko:

Я же написал в первом сообщении: "На основе приведённой мной процедуры можно реализовать многие операции атомарного чтения-модификации-записи".

Пример - хотим "a |= b" атомарно:

u32 volatile a;
u32 i0, i1;
do i1 = (i0 = a) | b;
while (AtomicCmpSwap(&a, i1, i0) != i0);

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

Подобные процедуры у меня есть и для других типов данных: u8, u16.

Так можно реализовать и все прочие логические и арифметические операции атомарные.

А можно, при желании, написать отдельные функции, подобные этой, для каждой такой операции (чтобы уменьшить кол-во команд).

 

Да я вроде просто в Cortex M7 не обнаружил bit banding. Была мысль, что придумали что-то новое, более продвинутое, что я не увидел в документации.

Даже в M3/M4 bit-banding был опционален и в некоторых камнях его могло и не быть.

Так что может и для M7 то же самое? Может в том МК, который Вы смотрите, его решили не реализовывать? Не большая потеря....

Share this post


Link to post
Share on other sites

На AVR это делал. А теперь вообще так не делаю. Как правило этих флагов ну пусть 30 ... Если каждый поместить в 4-ёх байтное слово будет потрачено 120 байт. Это что критично для M7?

Вообще подходы к написанию поменял. Стараюсь флаги выставлять в одной задаче - забирать в другой... Короче стараюсь писать так, чтобы не возникало проблем. И задачи делаю строго асинхронные.

Выделяю задачу одну, следящего типа... Она диагностирует...

Несколько сложнее работать с общими ресурсами. Например флэш память. Записываются сообщения об ошибках с любой задачи. Можно делать конечно её обслуживание лишь в одной задаче, а туда слать лишь сообщения...

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

Ну это мой взгляд на вещи.

Share this post


Link to post
Share on other sites
На AVR это делал. А теперь вообще так не делаю. Как правило этих флагов ну пусть 30 ... Если каждый поместить в 4-ёх байтное слово будет потрачено 120 байт. Это что критично для M7?

Я думаю цель - не экономия памяти, а реализация механизма, когда доступ к объекту возможен как по частям (по битам) так и целиком (словом) из разных задач/ISR. В нескольких разных местах флаги ставятся (клиентами), а в одном месте - считываются-обнуляются другой задачей целиком всё слово.

 

Вообще подходы к написанию поменял. Стараюсь флаги выставлять в одной задаче - забирать в другой... Короче стараюсь писать так, чтобы не возникало проблем. И задачи делаю строго асинхронные.

Ну да, если бы всегда так можно было бы, то семафоров разных не придумывали бы.

Представьте случай:

Есть задача (сервер), выполняющая запросы нескольких других задач-клиентов. Соответственно она может быть занята. Клиенты когда хотят пнуть задачу, ставят её флажок, который она обработает или сразу или как только освободится.

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

Клиенты ставят флажки-биты в переменной запросов (опционально переходя в ожидание обслуживания) и пингуют задачу-сервер, пробуждая её. Задача, как только получает управление или освобождается от обработки предыдущего запроса, считывает сразу всю переменную флагов-запросов, в соответствии со своими правилами выбирает наиболее приоритетный запрос из имеющихся, сбрасывает этот флаг запроса, обрабатывает запрос.

Вот здесь нужен доступ к флагам-запросам как по отдельности и атомарно, так и сразу ко всем.

Share this post


Link to post
Share on other sites
Читаю документацию и вижу, что там никаких упоминаний про bit-banding при работе с оперативной памятью...

Может появились неделимые операции "чтение-модификация-запись"?

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

 

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

В общем у меня получатся так: глобальная структура с флагами ( ingl ) - цель, собрать в кучу мелкие имена флагов, строго 32б, ну и модификация с помощью стандартных команд __LDREXW и __STREXW.

Поместил функции f_ask_с и f_ask_d в разные задачи, они там в цикле просто меняют свой флаг. Смысла в функции нет, потому как я не придумал способ передать указатель члена структуры (оно ниже физического адреса). Возможно можно перечислениями сделать, но как потом не запутаться - не знаю.

 

Словом, если кто предложит решение - буду рад.

 

typedef union
{
 struct
 {
uint32_t stop:5;
uint32_t start:1;
uint32_t a:1;
uint32_t b:1;
uint32_t c:1;
uint32_t d:2;
uint32_t e:1;
uint32_t f:1;
uint32_t g:1;
uint32_t h:1;
uint32_t i:1;
uint32_t j:1;
uint32_t k:1;
uint32_t l:1;
uint32_t m:1;
uint32_t n:1;
uint32_t o:1;
uint32_t p:1;
uint32_t q:1;
uint32_t r:1;
uint32_t s:1;
uint32_t t:1;
uint32_t u:1;
uint32_t v:1;
uint32_t w:1;
uint32_t x:1;
uint32_t y:1;
uint32_t z:1;
 } Flags;
 uint32_t Flagi;
} Flag_Type;

Flag_Type ingl;


static void f_ask_c (void);
void f_ask_c (void)
{
Flag_Type tmp;
do
{
   tmp.Flagi = __LDREXW(&ingl.Flagi);
   tmp.Flags.c ^= 1;
}while ( __STREXW(tmp.Flagi, &ingl.Flagi));
__CLREX();
}

static void f_ask_d (void);
void f_ask_d (void)
{
Flag_Type tmp;
do
{
   tmp.Flagi = __LDREXW(&ingl.Flagi);
   tmp.Flags.d ^= 1;
}while ( __STREXW(tmp.Flagi, &ingl.Flagi));
__CLREX();
}

Edited by AVI-crak

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this