Например TDA7294

Форум РадиоКот :: Просмотр темы - Как установить бит в памяти на ARM?
Форум РадиоКот
https://radiokot.ru/forum/

Как установить бит в памяти на ARM?
https://radiokot.ru/forum/viewtopic.php?f=59&t=135071
Страница 1 из 1

Автор:  themaster [ Ср авг 31, 2016 09:04:21 ]
Заголовок сообщения:  Как установить бит в памяти на ARM?

Наигравшись с PIC'ами, решил поковырять ARM в редакции STM. Столкнулся с непонятным моментом. Для работы с периферией контроллера необходимо то и дело снимать/ставить разные биты в ячейках памяти. В ассемблере PIC'ов есть замечательные команды BSF addr, bit / BCF addr, bit. Они берут бит bit по адресу addr и, соответственно, ставят его или снимают. Разумеется, с некоторыми оговорками, но сейчас это не важно.
Смысл в том, что у ПИКов для этого - отдельная команда, одна, которая выполняется за один такт. У АВРов - по-другому, и я в нескольких местах (например, здесь) видел упоминание, что аналогичная операция требует не менее трёх инструкций и, соответственно, трёх тактов: прочитать слово из памяти в регистр, изменить его (поставить/снять бит логической операцией), записать обратно.
Пример на сях:
Код:
*(unsigned long*)(0x40023830) |= 0x8;

Я решил глянуть, а как это будет на ассемблере? GCC выдал мне следующий результат обработки приведённой строчки:
Код:
ldr r2, .L3 /*.L3 - это адрес 32-разрядного слова в памяти*/
ldr r3, .L3 /*зачем это здесь - непонятно... адрес больше нигде в программе не используется.*/
ldr r3, [r3] /*загружаю слово из памяти в регистр*/
orr r3, r3, #8 /*меняю нужный бит*/
str r3, [r2] /*сохраняю в память*/

Тут целых пять строчек, и пять тактов!
Порывшись по учебникам, я прикинул, как бы я написал такой код - и получил не менее ЧЕТЫРЁХ строк:
Код:
ldr r1, addr_of_work /*загружаю адресв регистр*/
ldr r2, [r1] /*загружаю слово из памяти в регистр*/
orr r2, r2, #8 /*меняю нужный бит*/
str r2, [r1] /*сохраняю в память*/

Я не знаю, как сделать этот код ещё меньше. Правильно ли я понимаю, что он будет выполняться 4 такта? А про три такта имеется в виду случай, когда адрес нужной ячейки уже в регистре?
Или я чего-то не знаю?

Автор:  Z_h_e [ Ср авг 31, 2016 10:10:19 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Почитайте про bit banding. Для портов ввода/вывода есть еще специальный регистр BSRR.

Автор:  SII [ Ср авг 31, 2016 16:58:48 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

themaster писал(а):
Правильно ли я понимаю, что он будет выполняться 4 такта? А про три такта имеется в виду случай, когда адрес нужной ячейки уже в регистре?
Или я чего-то не знаю?


В общем случае -- да. Здесь сразу две причины:

1) ARM умеет обрабатывать данные только и исключительно в регистрах процессора; соответственно, необходимо сначала загрузить означенные данные, потом выполнить операцию и в конце записать результат обратно, что даёт нам три команды.

2) ARM для обращения к памяти всегда использует адреса в регистрах, а соответственно, эти адреса надо предварительно загрузить -- вот и лишняя команда.

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

Код:
    LDR   R3, =базовый адрес

    LDR   R0, [R3, #смещение регистра]
    LDR   R1, [R3, #смещение другого регистра]
    и так далее


Z_h_e писал(а):
Почитайте про bit banding

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

Z_h_e писал(а):
Для портов ввода/вывода есть еще специальный регистр BSRR.

Я с STM32 практически не сталкивался, но про этот регистр помню. Но это опять-таки специфический случай -- хорош для ногодрыга, но и только. Если, например, необходимо сбрасывать-устанавливать отдельные разряды регистров USARTа или её какой периферии, там, надо полагать, придётся-таки считывать их в регистр проца, выполнять операцию и записывать на место.

Автор:  Ser60 [ Ср авг 31, 2016 17:53:05 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

В некоторых реализациях ARM, например в семействе Kinetis, имеется BME (Bit Manipulation Engine) модуль. Он позволяет производить 2-цикловые атомарные 8/16/32-битные операции непосредственно с регистрами периферии. В число операций входит AND, OR, XOR, и вставка битов. Для этого к адресу регистра следует добавить определённую константу в зависимости от операции. Например, для операции OR значения data с регистром по адресу addr код будет таким (здесь бит 27 отвечает за операцию OR):
Код:
   ldr   R0, =(addr + (1<<27))
   ldr   R1, =data
   str   R1, [R0]

Для оперативной работы с GPIO в этом семействе для каждого порта имеются специальные регистры для установки/сброса битов (как BSRR/BRR в STM32), а также для их инвертирования (toggle).

Автор:  Z_h_e [ Ср авг 31, 2016 18:02:41 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Кстати о bit banding. Это аппаратный |=, выяснилось при записи в регистр с битами типа togle. Наверняка это указано в каком-то документе, но я не видел.

Автор:  menzoda [ Ср авг 31, 2016 23:06:53 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Чтобы совсем быть точным

themaster писал(а):
Правильно ли я понимаю, что он будет выполняться 4 такта?

Нет, ldr/str в общем случае выполняется два такта. Иногда N последовательных ldr/str могут быть выполнены за N+1 такт. Кроме того, первый ldr может загружать адрес из константы во flash памяти, это может добавить еще несколько тактов так как flash медленная. Правда, он может быть заменен на mov32, который всегда выполняется ровно два такта. Ну и напоследок надо добавить, что при исполнении данного кода из flash, количество тактов еще увеличится (как я уже говорил - flash медленная).

Автор:  Z_h_e [ Чт сен 01, 2016 05:54:18 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

menzoda писал(а):
что при исполнении данного кода из flash, количество тактов еще увеличится (как я уже говорил - flash медленная)
Наоборот, код расположенный в ОЗУ выполняется медленнее. Если Ваш код расположен во флеш, то выборка инструкций из флеш будет производится через шину ICODE, а данных через DCODE. Если код разместить в RAM, то все ляжет на шину DCODE.

Лучше конечно проверить это, записав некий тестовый код и туда и туда, затем сравнить.

Автор:  menzoda [ Чт сен 01, 2016 08:27:42 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Я открыл даташит на первый попавшийся STM32f407, так вот там ICode имеет доступ и к SRAM.
Цитата:
I-bus
This bus connects the Instruction bus of the Cortex®-M4 with FPU core to the BusMatrix.
This bus is used by the core to fetch instructions. The target of this bus is a memory
containing code (internal Flash memory/SRAM or external memories through the
FSMC/FMC).


Не знаю какой МК у топикстартера, но думаю что у всех STM32 на Cortex-M3 и Cortex-M4 именно такое устройство. Вот с Cortex-M0 уже может быть отличие, там же все обрезано. Но это все не важно, так как в любом случае медленность flash все перебьет. Ну пускай добавит эта коллизия шин по одному-два такта к некоторым инструкциям при исполнении из SRAM, зато исполнение из flash добавит к каждой инструкции от 3 тактов (типичное значение, на высокой частоте может быть и больше)! Акселераторы flash есть не у всех, а быстрая flash вообще у единиц.

Автор:  Z_h_e [ Чт сен 01, 2016 12:47:27 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Смотрим RM008. Это на STM32F10x. Ядро Cortex-M3.
СпойлерИзображение

Читаем оттуда же.
Цитата:
ICode bus This bus connects the Instruction bus of the Cortex®-M3 core to the Flash memory instruction interface. Prefetching is performed on this bus.
Т.е. у таких МК шина Icode не имеет доступ к SRAM.
Обращаем внимание, что чтение флеша происходит через буфер FLITF, который читает сразу 2х64 бит, т.е. четыре 32 битных слова. Это должно ускорять процесс чтения.

Цитата из книги Trevor Martin_The Insiders Guide to the STM32 ARM Based Microcontroller
Цитата:
Несмотря на то, что программный код может загружаться и исполняться изSRAM, команды в таком случае будут извлекаться с использованием системной шины, что приводит к дополнительным задержкам. И вероятнее всего код будет исполняться медленнее изSRAM, чем из встроеннойFlash памяти, расположенной в области для программного кода.
Это он про Cortex-M3.

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

----------

Вы привели в пример STM32f407. Я с ним не работал. Это уже Cortex-M4.
Гляжу RM009.
Цитату Вашу дублировать не буду. Смотрим сразу картинку.
СпойлерИзображение
Действительно получается I-bus тут "достает" до ОЗУ.
НО!!! Шина ICode и Dcode подключены ко флеш через некий акселератор. Если ничего не напутал, то вот цитата про него
Цитата:
To release the processor full performance, the accelerator implements an instruction
prefetch queue and branch cache which increases program execution speed from the 128-bit Flash memory. Based on CoreMark benchmark, the performance achieved thanks to the ART accelerator is equivalent to 0 wait state program execution from Flash memory at a
CPU frequency up to 180 MHz.
Т.е. ноль времени простоя при чтении флеша. Куда еще быстрее?

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

Сегодня может попробую сделать тестовый код и поробовать его на STM32F103 (другого у меня нет). Если сделаю, то результат выложу само собой.

Если есть желание, то можете и Вы попробовать на своем МК.

Автор:  menzoda [ Чт сен 01, 2016 13:18:18 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Так я и не против, что в частных случаях из flash может быть даже быстрее. Я говорю про большинство случаев, а в большинстве случаев, как я уже говорил, в МК нет акселератора. Например, у ST он есть только в некоторых продвинутых линейках. А без акселератора, как я тоже уже говорил, flash настолько медленная, что сведет на нет всю ту небольшую выгоду, полученную от использования раздельных шин. Это я уже гарантирую, потому что не так давно специально искал ARM МК с быстрой flash или акселератором. Так вот, их можно сосчитать по пальцам одной руки! Это несколько STM32 (есть акселератор) и вроде что-то было у Infineon (быстрая flash). У остальных, на номинальной частоте, по 3-5 циклов ожидания, Карл!

Кстати пресловутый акселератор тоже не панацея, он как и любой кэш может ошибаться. Если код выполняет частое ветвление, то этот акселератор быстренько сдуется. А вот SRAM гарантированно будет обеспечивать 0 циклов ожидания в любом случае. Пусть даже при этом LDR и STR будут немного медленнее, зато весь остальной код будет гораздо быстрее.

Конечно лучше провести тест, чтобы убедиться. Можно попробовать следующие тесты:
1. линейный код из flash с выключенным акселератором
2. линейный код из flash с включенным акселератором
3. код с частым ветвлением (дальше чем 4 слова) из флеш с включенным акселератором (чтобы убедиться, что он сдуется)
4. линейный код из SRAM
5. код с частым ветвлением из SRAM.

Автор:  Z_h_e [ Чт сен 01, 2016 21:36:48 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Ну что же. Сделал простенький эксперимент. Вы, menzoda, были абсолютно правы.

Тестовый код просто дрочит порт А0. МК STM32F103C8
Код:
int main(void)
{
   RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
   GPIOA->CRL=GPIO_CRL_MODE0|GPIO_CRL_MODE1;
    while(1)
    {
       GPIOA->ODR=1;//Инвертируем выход A0
       GPIOA->ODR=0;
    }
}


Справедливости ради, я проверил, во что он был скомпилирован для ОЗУ.
Код:
GPIOA->ODR=1;//Инвертируем выход A0
200002fa:   ldr r3, [pc, #16]       ; (0x2000030c <main+40>)
200002fc:   movs r2, #1
200002fe:   str r2, [r3, #12]
GPIOA->ODR=0;
20000300:   ldr r3, [pc, #8]        ; (0x2000030c <main+40>)
20000302:   movs r2, #0
20000304:   str r2, [r3, #12]

20000306:   b.n 0x200002fa <main+22>


и для Флеша.
Код:
GPIOA->ODR=1;//Инвертируем выход A0
080002fa:   ldr r3, [pc, #16]       ; (0x800030c <main+40>)
080002fc:   movs r2, #1
080002fe:   str r2, [r3, #12]
GPIOA->ODR=0;
08000300:   ldr r3, [pc, #8]        ; (0x800030c <main+40>)
08000302:   movs r2, #0
08000304:   str r2, [r3, #12]

08000306:   b.n 0x80002fa <main+22>

Коды идентичны.


Подключаем осциллограф к выходу и наблюдаем.

----------
Код запущен из ОЗУ
Изображение
Частота: 4.000 МГц
Длит. имп+ 109нс
Длит.имп- 141 нс

Небольшой перекос скважности объясняется затратами времени на выполнение команды безусловного перехода.

----------
Код запущен из Флеша
Изображение
Частота: 2.881 МГц
Длит. имп+ 123нс
Длит.имп- 224 нс

Частота значительно ниже. При том, что положительный импульс где-то похож по длительности на предыдущий тест, а вот отрицательный чуть не в два раза длиннее. Я думаю это легко объяснить работой буфера FLITF. Когда код линеен он успевает кэшировать данные. А вот предсказать ветвления он не может совсем, даже безусловные.

Ну вот, как то вот так.

----------
Если бы кто сделал что-то подобное для контроллера с акселератором и выложил, думаю была бы полезная инфа.


----------
З.Ы. Есть у меня STM8. Еще не разу не игрался с ним и почти ничего не читал. Но русской статейке видел, что если там выполнять код из ОЗУ, то точно будет медленнее ибо шина значительно уже по разрядности для ОЗУ. Если руки дойдут до изучения этого МК, постараюсь сделать и для него аналогичный тест.

Автор:  menzoda [ Чт сен 01, 2016 22:13:37 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Я может попробую с акселератором, если найду свою платку discovery.

Автор:  Z_h_e [ Пт сен 02, 2016 18:22:40 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Сегодня провел еще один тест. На выполнение одного и того же кода во Флеш и в ОЗУ. Была мысль создать отдельную тему, так как данные тесты по сути офтоп.

Из предыдущего теста, можно было предположить, что линейный код выполняется практически с одинаковой скоростью и во флеш и в ОЗУ. Тормозит все у данного МК переходы.

Сегодня протестировал вот такой код.
Спойлер
Код:
#define Mac0 {a=b+c;c=a;}
#define Mac1 {Mac0 Mac0 Mac0 Mac0}
#define Mac2 {Mac1 Mac1 Mac1 Mac1}
#define Mac3 {Mac2 Mac2 Mac2 Mac2}
#define Mac4 {Mac3 Mac3 Mac3 Mac3}

uint32_t a;
int32_t b=-88123456;
int64_t c=0xFF00FF00FF00FF00;

int main(void)
{
   RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
   GPIOA->CRL=GPIO_CRL_MODE0|GPIO_CRL_MODE1;

    while(1)
    {
       Mac4;
       GPIOA->ODR^=1;//Инвертируем выход A0
    }
}
Такой исходник компильнулся почти в 14КБ кода и практически весь линейный.

Скрины не буду прикладывать.
Частота при выполнении кода из ОЗУ - 4,655 кГц.
Частота при выполнении кода из Флеш - 3,394 кГц.
Так что буфер FLITF на 128 бит и тут не помогает для данного МК.

----------

Думаю, если продолжать подобные тесты, для разных МК, нужно создать отдельный топик со ссылкой на этот. Наверное данный МК тестировать уже нет необходимости, вроде все понятно.

Автор:  menzoda [ Пт сен 02, 2016 18:29:21 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

С другими еще хуже будет. Не у всех есть этот буфер, который может хоть как-то помогает. Могу сказать, что на TMS320F2810 (это не ARM) на номинальной частоте 150 МГц код из SRAM выполняется примерно в два-три раза быстрее чем из flash.

Автор:  Z_h_e [ Пт сен 02, 2016 18:30:48 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

Ну может STM8 наоборот себя поведет. :)

Автор:  Z_h_e [ Вс сен 04, 2016 17:25:57 ]
Заголовок сообщения:  Re: Как установить бит в памяти на ARM?

На STM8 код из ОЗУ выполняется в 2.5 раза медленнее чем из флеш. Тест аналогичный первому с STM32.

Страница 1 из 1 Часовой пояс: UTC + 3 часа
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/