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

STM32 – вопросы – проблемы - решения.

Вот благодетель! Не пожалел времени и сил, накропал гениальной рукой десяток тривиальных гениальных строк, и поделился со всеми! Нате, считайте!

Спасибо, что-то не хочется. В чём заключались ваши "эксперименты", если ничего не посчитано? В выяснении возможности скопипастить кусок дизассемблерного текста в форум? :cranky:

Вообще-то, я не вам предлагал считать. Тот код, что я показал, содержит все строки исходного кода. Кому интересно, может скомпилировать и изучить самостоятельно. ALIAS_PTR(), например, кому-нибудь может пригодиться.

А что, байты посчитать тяжелая задача?

P.S. А грубить зачем?

P.P.S. А про инверсию бита я уже сказал - обхожусь без инверсии, задаю то, что мне нужно через BSRR и BRR.

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


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

P.S. А грубить зачем?

Ну что вы, какая грубость? Так, лёгкий сарказм.

Дело в том, что я привёл вариант, скажем так, выстраданный, выбранный после многих тестов (ещё раз респект ReAl-у). И по размеру кода, и по тактам. А вы, походя, совершенно безо всяких тестов, его осудили. (Те кусочки дизассемблера - они не в счёт, ибо во-первых, несерьёзны, во-вторых, без замеров тактов, ну и в-третьих, были сделаны уже после исходных утверждений).

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


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

Пусть бы ReAl показал результат не для

---- через GPIOx->BSRR = Latched() ? mask << 16 : mask;

а

if (Latched()) GPIOx->BSRR = mask;

else GPIOx->BRR = mask;

Я похож на человека, который такое не проверит?

ODR ^= просто было написано первым, так как «идеологически просто». Но за неатомарность было забанено. Затем первым из других вариантов было проверено как раз предлагаемое, читаем внимательно:

---- через Latched() ? Off() : On()

Полные глупости при компиляции, компилируется в условные переходы,

нестабильный и плохой результат.

Только после этого начался поиск других вариантов.

On и Off как раз и сделаны через BSRR = и BRR =. Причём совершенно параноидальная попытка вкопипастить прямой код, а не «вызов» готовых On/Off -- ничего не меняет.

В таком варианте оно иногда генерирует вполне приличные два str в разные смещения под ite, иногда жуткую муть с условными переходами и возвратами потом назад.

Сильно зависит от того, чем размыты операции. Между ними вставлялись и обращения к ОЗУ, и обращения к другим пинам -- пока этого не было сделано, вариант был кандидатом номер 1. А так -- может, со временем будет иметь право на жизнь.

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


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

Ну что вы, какая грубость? Так, лёгкий сарказм.

Дело в том, что я привёл вариант, скажем так, выстраданный, выбранный после многих тестов (ещё раз респект ReAl-у). И по размеру кода, и по тактам. А вы, походя, совершенно безо всяких тестов, его осудили. (Те кусочки дизассемблера - они не в счёт, ибо во-первых, несерьёзны, во-вторых, без замеров тактов, ну и в-третьих, были сделаны уже после исходных утверждений).

И сильно "стадали"? Рожали в муках, наверное? (вот это - легкий сарказм).

Дело в том, что я тоже поначалу загорелся этим bit-band, но попробовав, пришел к выводу, что для управления битами порта это не нужно, благодаря куче специально предназначенных регистров. И эту работу я провел не походя, и не вчера. Во всех случаях, кроме инверсии бита, работа через bit-band не дает ничего. А инверсия мне не нужна (может, именно поэтому и не нужна).

Хотите, "пострадаем" и "выберем после многих тестов" еще раз, вместе? Начнем со сброса бита?

 

Сильно зависит от того, чем размыты операции.

Я же и говорю, что трудно посчитать объективно.

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


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

Да вроде всё понятно уже. Чего страдать-то:).

Инверсия бита действительно не очень частое явление, но тем не менее эта функция must have, как говориться.

И bit-band в этом случае может быть весьма кстати.

Спасибо всем за поддержку дискуссии!

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


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

Я же и говорю, что трудно посчитать объективно.
Я говорю, что

обращение к BSRR или BRR в зависимости от текущего состояния ноги компилируется в код, вид которого (ITE или «обычный if» с условным и безусловным пеерходами) сильно зависит от остального кода.

Т.е. если в коде будет

PIN1.On();

PIN2.Cpl();

PIN3.Off();

PIN2.Cpl();

PIN4.On();

то может даже оказаться, что эти два Cpl() по варианту «пишем в BSRR или BRR» скомпилируются по разному (собственно, это и было замечено осциллографом). Тут не то, что объективно, тут никак посчитать нельзя.

 

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

 

Т.е. вариант «пишем в BSRR или BRR» однозначно хуже при текущем состоянии оптимизатора.

И в саоём «лучшем» варианте компиляции он выигрывает у принятого совсем немножко.

 

 

Во всех случаях, кроме инверсии бита, работа через bit-band не дает ничего. А инверсия мне не нужна (может, именно поэтому и не нужна).
А разве в предложенном исходнике для изменения состояния выходов bit-band применяется где-то кроме инверсии?

Или «bit-band не нужен вообще, потому что он нужен только в том, что не нужно Вам лично» ?

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


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

А разве в предложенном исходнике для изменения состояния выходов bit-band применяется где-то кроме инверсии?

Или «bit-band не нужен вообще, потому что он нужен только в том, что не нужно Вам лично» ?

Отвечу вопросом на вопрос - а разве я такое где-то сказал?

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

Хотя, я тоже стремлюсь использовать "совершенный" код. И применяю, например, такие конструкции:

typedef struct {
  int  HelpEna : 1; // Индикация помощи
  int  Lang : 2; // Язык
  int  Vect : 1; // Точки / Вектора
  int  Wide : 1; // WorkArea Wide
  int  Scale : 2; // Work Scale Form
  int  dummy : 1;
} DPY_t __attribute__((bitband));

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

Bit-band манипуляции полезны, в первую очередь, своей атомарностью. Но при работе с портами ее (атомарность) можно получить и иными способами. А такты-байтики считать - не главное. IMHO.

 

P.S. В моем сообщении №99 нужно переставить местами bit-band alias и bit-band. Bit-band - это область в 1MB, а bit-band alias - это то, что имеет размер 32MB, и отражается на bit-band. Поправить сообщение уже не могу.

 

P.P.S. А есть ли у кого мысли, как этот bit-band реализован физически? Теряюсь в смутных догадках...

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


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

И сильно "стадали"? Рожали в муках, наверное? (вот это - легкий сарказм).

Это уже не сарказм, это юродствование.

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

Дело в том, что вы это повторяете уже пятый раз. И все пять раз - мимо кассы. Ибо вопрос был конкретно про инверсию бита, и именно для этого случая (и только для него) в предложенном исходнике применён bit-band. Но на это вам наплевать, вам бы поумничать.

И эту работу я провел не походя, и не вчера.

Не верю, уж извините.

Хотите, "пострадаем" и "выберем после многих тестов" еще раз, вместе?

С вами? Увольте.

 

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


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

#include "stm32f10x.h"

#define ALIAS_PTR(VarPtr,BitNum) \
  *((__IO uint32_t *) (((uint32_t) &(VarPtr) & 0xf0000000) | 0x02000000 \
  + (((uint32_t) &(VarPtr) & 0xfffff)<<5) | (BitNum<<2)))

void Bit_toggle(void);

int32_t main(void)
{
  while (1) {
    Bit_toggle();
  }
}

void Bit_toggle()
{
//  GPIOC->ODR ^= 1UL<<8;
//  ALIAS_PTR(GPIOC->ODR,8) ^= 1;
//  ALIAS_PTR(GPIOC->ODR,8) = ~ALIAS_PTR(GPIOC->ODR,8);
}

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

А именно, для Keil 4.12 при оптимизации default программа занимает 18 байтов, плюс 4 байта константа адреса. Основной цикл while выполняется за 16 тактов в симуляторе. Вот листинг для одного из вариантов. Для других картина аналогичная.

; generated by ARM C/C++ Compiler, 4.1 [Build 481]
; commandline ArmCC [--debug -c --asm --interleave -o.\objects\bit_toggle.o --depend=.\objects\bit_toggle.d --cpu=Cortex-M3 --apcs=interwork -O3 -IC:\Programs\Keil\ARM\INC -IC:\Programs\Keil\ARM\INC\ST\STM32F10x -DSTM32F10X_HD --omf_browse=.\objects\bit_toggle.crf Source\Bit_toggle.c]
                          THUMB
                          AREA ||.text||, CODE, READONLY, ALIGN=2
                  Bit_toggle PROC
;;;25     
;;;26     void Bit_toggle()
000000  4804              LDR      r0,|L1.20|
;;;27     {
;;;28       GPIOC->ODR ^= 1UL<<8;
000002  68c1              LDR      r1,[r0,#0xc]
000004  f4817180          EOR      r1,r1,#0x100
000008  60c1              STR      r1,[r0,#0xc]
;;;29     //  ALIAS_PTR(GPIOC->ODR,8) ^= 1;
;;;30     //  ALIAS_PTR(GPIOC->ODR,8) = ~ALIAS_PTR(GPIOC->ODR,8);
;;;31     }
00000a  4770              BX       lr
                          ENDP
                  main PROC
                  |L1.12|
;;;18     
;;;19     int32_t main(void)
00000c  f7fffffe          BL       Bit_toggle
;;;20     {
;;;21       while (1) {
;;;22         Bit_toggle();
000010  e7fc              B        |L1.12|
;;;23       }
;;;24     }
;;;25     
                          ENDP
000012  0000              DCW      0x0000
                  |L1.20|
                          DCD      0x40011000
                  __ARM_use_no_argv EQU 0

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


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

Нет у меня кейла.

Но есть вопрос:

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

Прямой доступ к ODR, операция ^=

521 0012 DA68     ldr r2, [r3, #12]
522 0014 82F00202 eor r2, r2, #2; ну никуда не деться от константы, с которой XOR-ить, в Вашем листинге аналогично
523 0018 DA60     str r2, [r3, #12]

Доступ через бит-банд, запись инверсии прочитанного

525 0010 5A68 ldr r2, [r3, #4]
526 0012 D243 mvns r2, r2 ; константа не нужна, просто инвертируется весь регистр
527 0014 5A60 str r2, [r3, #4]

 

По одинаковости времени STR в «обычный» порт и в его bit-band область (скрытое RMW обращение) -- это симулятор какой-то турбированный. Реальный кристалл с осциллографом показывает разные результаты.

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


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

Как может одинаковое число байтов получаться для команд...

Доступ через бит-банд, запись инверсии прочитанного

Вот как компилирует Keil. Почему так, не знаю. Но уже замечал, что в IAR получается компактнее.

;;;30       ALIAS_PTR(GPIOC->ODR,8) = ~ALIAS_PTR(GPIOC->ODR,8);
000002  6801              LDR      r1,[r0,#0]
000004  ea6f0101          MVN      r1,r1
000008  6001              STR      r1,[r0,#0]

По одинаковости времени STR в «обычный» порт и в его bit-band область (скрытое RMW обращение) -- это симулятор какой-то турбированный. Реальный кристалл с осциллографом показывает разные результаты.

Если, как видите, команды для обычной пересылки и пересылки в bit-band, практически, одни и те же (а по тактам полностью одинаковые, EOR и MVN выполняются за одну команду), то с чего бы второй вариант был быстрее? Осциллограф показывает работу кода из flash. Попробуйте запустить из ОЗУ. Я делал, но точные цифры сейчас вряд ли найду.

 

P.S. У вас MVNS - с обновлением флагов, у меня MVN - без обновления. Кто здесь "правильнее" подошел, Keil или GCC, не могу сказать. С одной стороны, лишний раз флаги "портить" не желательно, вдруг они потом используются. С другой, с "порчей" флагов компактнее. В любом случае, это особенности компиляторов, а не достоинства bit-band.

Я, все же, останусь при своей точке зрения. Bit-band для манипуляции битами порта использовать не буду.

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


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

Если, как видите, команды для обычной пересылки и пересылки в bit-band, практически, одни и те же (а по тактам полностью одинаковые), то с чего бы второй вариант был быстрее?
Кто «второй»? Который с бит-банд? Так я везде говорю, что он медленнее, зато атомарный.

 

Полагаю, раз Ваш симулятор показывает одинаковое время для записи в какой-то адрес (не важно, это порт или ОЗУ) и в его bit-band-alias область (хотя это просто запись и RMW), то для следующих фрагментов кода:

    str r1,[r0, #16];  показывает даже не на bit-band, в обычную область IO,
    str r1,[r0, #20];  но _не_ на память, а в APB2

    str r1,[r0, #16]
    nop
    str r1,[r0, #20]

он покажет разное время?

 

Знаете, на старом добром AVR команда STS ADDR выглядит одинаково для записи во внутреннее ОЗУ и во внешнее. А время отличается, ещё и разное может быть для разных адресов внешнего ОЗУ, в зависимости от установки битов SRW* в XMCRA*.

А у кортекса всё ещё сложнее.

 

p.s. во всех рассмотренных примерах был stm32f103, ядро/AHB и APB2 работали на одинаковой частоте 72 Мгц, если APB2 присадить, то еще веселее должно быть с подсчётом времён.

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


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

Кто «второй»? Который с бит-банд? Так я везде говорю, что он медленнее, зато атомарный.

я забыл, перепутал

Полагаю, раз Ваш симулятор показывает одинаковое время для записи в какой-то адрес и в его bit-band-alias область (хотя это просто запись и RMW)...

Для манипуляций с битами ODR - и там, и там используется RMW, читаем-меняем-записываем.

Аналогично для регистров BSRR и BRR - и там, и там используется просто запись.

Разница между обычным доступом и bit-band только в том, что обращаемся к целому слову или к одному биту. Вот она и "атомарность".

На 100% достоверность не претендую. Однако симулятор показывает... Надо знать само устройство этого bit-band.

p.s. во всех рассмотренных примерах был stm32f103, ядро/AHB и APB2 работали на одинаковой частоте 72 Мгц

Аналогично.

 

он покажет разное время?

добавил __nop(); получил на такт длительнее цикл, 17 тактов. А разве должно было быть иначе?

P.S. Чтобы разобраться до конца, попробую запустить свои варианты из flash, из RAM и посмотрю осциллографом. Доложу, когда сделаю.

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


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

Разница между обычным доступом и bit-band только в том, что обращаемся к целому слову или к одному биту. Вот она и "атомарность".

На 100% достоверность не претендую. Однако симулятор показывает... Надо знать само устройство этого bit-band.

Ну я же да линк на сай ARM.

Там чётко написано одной строкой, что «System bus interface» _запись_ в bit-band выполняет как атомароне(_чтение_-модификация-_запись_), скрыто от програмиста. Делать аппаратный побитовый доступ к мегабайтному адресному пространству -- дороговато.

 

Кстати, так же работает SBI в старых AVR-ках, в результате чего при обращении по SBI/CBI к некоторым регистрам в них отлично сбрасыватся соседние биты флагов, котрые сбрасываются записью единички.

 

добавил __nop(); получил на такт длительнее цикл, 17 тактов. А разве должно было быть иначе?
Да. Время не меняется.

STR с точки зрения ядра выполняется может и один такт, запись ушла в AHB и досвидания. Там она по окну адресов перебрасывается на APB2, но ядро продолжает выполнять команды. Если следующей командой опять STR в ту же APB2 (вот в APB1 я не пробовал писать, интересно, что будет), то ядро начинает ждать готовности заброса данных в шину. Таким образом, в цепочке STR в APB2 каждый STR занимает два такта, такт сбоственно STR в IO и такт ожидания готовности для следующего STR.

Но если после STR стоит однотактовая команда, то она себе выполняется, а следующий STR уже никого не ждёт.

 

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

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


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

Там чётко написано одной строкой, что «System bus interface» _запись_ в bit-band выполняет как атомароне(_чтение_-модификация-_запись_), скрыто от програмиста. Делать аппаратный побитовый доступ к мегабайтному адресному пространству -- дороговато.

Да. Время не меняется.

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

Практика - критерий истины. Проверил на STM32VLDISCOVERY, на частоте 24MHz, код выполнялся из flash с 0 циклов ожидания (естественно).

Все три мои варианта (назовем P ^= 1UL<<8; BB ^= 0x1; BB = ~BB) работают одинаково.

 

Цикл выполняется за 15 тактов, а период полученной частоты, понятно, в 2 раза больше. Итого 24MHz/30 = 800kHz. Удивительно, что получилось именно 15, а не 16 тактов! Чудо.

 

Для проверки добавил код, разрешающий вывод через MCO 8MHz от кварцевого генератора HSE. Убедился, что 8MHz есть. Период же исследуемого сигнала стал 32 такта (16+16). Как и ожидалось изначально.

 

Добавил _nop();, ставил и перед изменением регистра, и после ставил. Период сигнала стал 34 такта (17+17). Как видите, NOP работает. Примечательно, что размер кода при этом увеличился на 4 байта, а не на 2. В-общем, работа компилятора - потемки.

 

Похоже, никаких лишних тактов обращение к bit-band alias не требует.

Несомненно, используется некий аппаратный способ.

 

Команда NOP может работать, как надо. Кроме шины AHB еще есть буфер предвыборки flash, который путает всю временную картину. Он состоит из 2-х 64-битовых регистров. Как туда лягут команды цикла - тайна, покрытая мраком.

 

Ради интереса попробовал работу с портом через BSRR, BRR. Вот такой код выдает на ножке 4MHz со скважностью 50%, 3 такта - 0, 3 такта - 1. Может быть, при другом раскладе кода будет иначе.

  while (1) {
    GPIOC->BRR  = 0x00000100;
    __nop();  __nop();
    GPIOC->BSRR = 0x00000100;
  }

Сравните с 800kHz через инверсию. Понятно, вещи разные. Но для меня, как не было нужды инвертировать бит, так и не появилось желания. Одно удобно - при инвертировании скважность частоты будет точно 50%.

Вот такой вам ответ от DISCOVERY и Осциллографа с Симулятором. :)

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


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

Гость
Эта тема закрыта для публикации ответов.
×
×
  • Создать...