Как стать автором
Обновить

Комментарии 25

А ещё есть опция --double=32

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

Ещё есть такая вещь, как integer promotion, и например, если вы будете работать с int16, то без оптимизации, будут добавляться ассемблерные команды, которые осуществляют integer promotion, например ldrh.

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

Ну и статический анализатор в помощь.

Как раз недавно попадалась прошивка с управлением мотором под Cortex-М3, где целый FOC аккуратно реализован с фиксированной точкой, а рядом пустяковый термистор, единственная функция которого - уход в ошибку при превышении фиксированного порога температуры, пересчитали из отсчётов АЦП в градусы "в лоб" по формуле, содежащей логарифм с плавающей точкой, чтобы проверять порог в градусах.

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

Зачем вообще постоянно опрашивать термистор с фиксой? Железный триггер и сигнал прерывания решают проблему имхо.

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

У нас до на Cortex-M3, где еще не было FPU, тоже был алгоритм на фиксированной точке, по сложности подобный FOC. Основная проблема это баланс между переполнением и шумами дискретизации. Последние радостно переходят в акустические шумы и мотор начинает трещать. Первые приводят к резким сбоям.

Этот баланс можно было бы легко удерживать, если бы не большие диапазоны параметров системы. Ток от 10 до 3000 мА, напряжение от 2 до 48 В, сопротивление обмоток от 0.1 до 12 Ом. И нужно на фиксированной точке предусмотреть все математические действия, чтобы результат был не слишком большим или маленьким для всех комбинаций величин. Если ток меняется на 3 порядка, то выделяемая на сопротивлении мощность это уже I²R, то есть разброс на 8 порядков.

ошибка реализации библиотечной функции в arm_math.h из CMSIS, revision: v1.4.4

Справедливости ради, ARM поправил это место:
https://github.com/ARM-software/CMSIS-DSP/blob/80bf36c676570fdc078a34dd72aeeb1ecdf7a8ad/Include/dsp/controller_functions.h#L735-L746

Когда именно, искать лень - ваша CMSIS-DSP была выпущена, если не ошибаюсь, в 2015 году.

half_of_N = 0.5 * N;

Компилятор не заменит это на сдвиг на 1 бит вправо?

При -О0 нет.

Потыкал godbolt. gcc-arm заменяет N / 2 на сдвиг при любом уровне оптимизации и никогда НЕ заменяет N * 0.5 (что, в принципе, логично). Зато умеет менять N / 2.0f на N * 0.5f при -O1 и выше.

Очень жалею, что нет в С встроенного fixed point типа, приходится велосипедить. Обычно нам известно, какая точность нужна, и можно от fp отказаться.

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

.../// Обычно нам известно, какая точность нужна, и можно от fp отказаться. ///...

Именно так,

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

  • нужно задаться требуемой (физической / кинематической) динамикой, которую нужно выдать или получить от актуатора и измерителя;

  • после чего перевести (конструктивная реализация с экономическими ограничениями, мотор + инвертор и выборка-хранение + АЦП) динамику в набор электрических последовательностей "фазных обмоток итп", наилучшей или выбранной формы + периодичности;

  • в итоге семейства электрических последовательностей, отражающие динамику, на 95% определяют как сам Алгоритм управления, так и его разрядность (5% фактические глюки конкретного чипа и аналогичное на всех этапах разработки и компиляции от кода до бинерника).

printf и sprintf всегда преобразуют float в double в последней версии IAR.

Раньше в IAR такого не было.

На критичных участках по времени работы решается использованием кастомного ftoa с дальнейшей передачей в printf в виде строки, у себя в проекте так делал, хотя в первую очередь решал задачу унификации печати float по аналогии с .ToString() из C#, но проблема перевода float в double тоже решается.

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

double a = 0.2;
uint32_t b = 6;

double r1 = a * b; //=1.2
double r2 = b * a; //=0

Потому что он оценивал тип по первому множителю.

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

Да, прямое несоответствие, должно к самому широкому приводиться.

Хорошо, что в C++ можно использовать одну функцию для разных типов данных. Написал std::abs(), и знаешь, что будет вызван нужный вариант, будь то int, float, или double.

Это IAR и embedded система. Там ничего не знаешь пока сам не протестируешь.
Мало того, любую статью про IAR надо сопровождать номером версии о которой идёт речь.
И ещё нельзя забывать про ретаргетинг, он тоже может чудес добавить.

Да, я вижу. По поиску «IAR» первый результат «The IAR C/C++ Compiler is built by our compiler experts»

В версиях 8.xx по моему и выше, там clang, поэтому сюрпризы такие же как и в Clang.

Было дело, писал загрузчик. Собственный протокол, интерфейс USB, память под загрузчик 16 кб. С оптимизацией Os + LTO в указанную память загрузчик не влезал, не хватало буквально сотни байт. Изучая ассемблерный листинг обнаружил функции 64-битной арифметики. Сюрприз был спрятан в хал-библиотеке stm32f4xx_hal_rcc.c:

uint32_t HAL_RCC_GetSysClockFreq(void)
{
  ...
  pllvco = (uint32_t) ((((uint64_t) oscFreq * ((uint64_t) ((RCC->PLLCFGR & RCC_PLLCFGR_PLLN) >> RCC_PLLCFGR_PLLN_Pos)))) / (uint64_t)pllm);
  ...
}

Небольшая доработка кода, и вуаля - полкилобайта памяти свободно. В моём случае функция вызывается однократно, просадки производительности нет, но память - ценный ресурс. Поэтому читать и понимать map/asm/lst файлы действительно бывает очень полезно.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории