Кто любит RISC в жизни, заходим, не стесняемся.
Вс окт 24, 2021 16:21:02
Уверены? Что-то многовато как-то...
Спойлер
- Код:
uint32_t divu10(uint32_t val) { return val * (((1ULL << 35) + 5) / 10) >> 35; }
volatile uint32_t val = 4294967295;
for (uint32_t i = 100; i > 0; i--)
{
__BKPT();
volatile uint32_t res = divu10(val);
__NOP();
}
0x20002330 bkpt 0x0000
0x20002332 movs r5, #0
0x20002334 ldr r3, [sp, #84] ; 0x54
0x20002336 movs r4, r3
0x20002338 movs r2, r3
0x2000233a movs r3, r5
0x2000233c adds r2, r2, r4
0x2000233e adcs r3, r5
0x20002340 adds r2, r2, r4
0x20002342 adcs r3, r5
0x20002344 lsrs r1, r2, #28
0x20002346 mov r12, r1
0x20002348 mov r0, r12
0x2000234a lsls r1, r3, #4
0x2000234c orrs r1, r0
0x2000234e str r1, [sp, #52] ; 0x34
0x20002350 lsls r1, r2, #4
0x20002352 str r1, [sp, #48] ; 0x30
0x20002354 ldr r0, [sp, #48] ; 0x30
0x20002356 ldr r1, [sp, #52] ; 0x34
0x20002358 adds r2, r2, r0
0x2000235a adcs r3, r1
0x2000235c lsrs r1, r2, #24
0x2000235e mov r12, r1
0x20002360 mov r0, r12
0x20002362 lsls r1, r3, #8
0x20002364 orrs r1, r0
0x20002366 str r1, [sp, #60] ; 0x3c
0x20002368 lsls r1, r2, #8
0x2000236a str r1, [sp, #56] ; 0x38
0x2000236c ldr r0, [sp, #56] ; 0x38
0x2000236e ldr r1, [sp, #60] ; 0x3c
0x20002370 adds r2, r2, r0
0x20002372 adcs r3, r1
0x20002374 lsrs r1, r2, #16
0x20002376 mov r12, r1
0x20002378 mov r0, r12
0x2000237a lsls r1, r3, #16
0x2000237c orrs r1, r0
0x2000237e str r1, [sp, #68] ; 0x44
0x20002380 lsls r1, r2, #16
0x20002382 str r1, [sp, #64] ; 0x40
0x20002384 ldr r0, [sp, #64] ; 0x40
0x20002386 ldr r1, [sp, #68] ; 0x44
0x20002388 adds r2, r2, r0
0x2000238a adcs r3, r1
0x2000238c lsrs r1, r2, #30
0x2000238e mov r12, r1
0x20002390 lsls r1, r3, #2
0x20002392 str r1, [sp, #76] ; 0x4c
0x20002394 mov r1, r12
0x20002396 ldr r0, [sp, #76] ; 0x4c
0x20002398 lsls r3, r2, #2
0x2000239a orrs r0, r1
0x2000239c str r0, [sp, #76] ; 0x4c
0x2000239e str r3, [sp, #72] ; 0x48
0x200023a0 ldr r2, [sp, #72] ; 0x48
0x200023a2 ldr r3, [sp, #76] ; 0x4c
0x200023a4 adds r2, r2, r4
0x200023a6 adcs r3, r5
0x200023a8 lsrs r3, r3, #3
0x200023aa str r3, [sp, #92] ; 0x5c
0x200023ac nop ; (mov r8, r8)
Вс окт 24, 2021 18:50:25
https://github.com/bobbl/libaeabi-cortexm0Человечеще спецом заморачивался в оптимизации cortexm0, и мне кажется этот код уже включён в GCC. Потому как на выхлопе очень похожее получается.
Так легко можно увидеть вложенные циклы на делении __aeabi_uidiv ( u32=u32/u32).
И отсутствие циклов на умножении __aeabi_lmul (u64=u64*u64).
Чт окт 28, 2021 00:02:35
Вот вы тут все про оптимизацию, а я уже второй вечер под пивасик выдумываю, как бы реализовать рамп шаговика! Точней, у меня на одном STM32F072 контроллер трех шаговых двигателей. Они могут быть как с обратной связью (квадратурный оптический или магнитный энкодер), так и без нее. И самое неприятное в том, что каждые несколько миллисекунд нужно будет в состоянии разгона или торможения заново вычислять ARR пинающего драйвер ШД таймера! И там, естественно, есть деление на некоторое число: ARR=FREQ/V, а V вычисляется каждый раз как V+=dV (или V-=dV на стадии торможения). Причем, если есть энкодер, на стадии разгона нужно еще проверять, не застрял ли вал вообще (если застрял - снижаем скорость, если снизили до минимальной и все равно кирдык — сигналим: "ахтунг") и нет ли проскальзываний (если есть, то скорость пока не увеличиваем)... Ну и в каждом прерывании таймера ШД нужно проверять концевики.
Трындец, сколько вычислений.
Хотя, с другой стороны, это будет от силы тактов 200-300. Т.е. меньше десятка микросекунд. И лень говорит: "а может, нафиг оптимизации? И так сойдет!"
Добавлено after 1 hour 15 minutes 24 seconds:
Японский городовой! Проблеме построения рампа столько статей посвящено, что задолбаешься читать!!! И у всех более-менее одно и то же (сам подобный алгоритм и нарисовал себе), разве что некоторые особо упорные (или упоротые?) китайцы такой математики накидали, что никакой Cortex-M0 не осилит!
Пт ноя 05, 2021 14:56:44
Как сократить операцию сравнения? cmp
https://godbolt.org/z/eT5j7nP3W
Вт ноя 09, 2021 01:20:40
https://godbolt.org/z/sx7ernxjjДеление uint64_t на 10 за 68 тиков, __aeabi_uldivmod из GCC жуёт цифры аж 269 тиков.
- Код:
#define MAGICDIV 3435973837UL
const uint32_t divu64_dat[]={
0x00000000, 0x19999999, 0x33333333, 0x4CCCCCCC, 0x66666666,
0x80000000, 0x99999999, 0xB3333333, 0xCCCCCCCC, 0xE6666666,};
union divrev {uint64_t w64;uint32_t w32[2];}rev;
/// 68 cpu clock (DWT->CYCCNT)
uint64_t divu64_10 (uint64_t value, uint8_t *residue)
{
uint32_t nex;
union divrev rev;
union divrev res;
rev.w64 = value;
res.w32[1] = ((uint64_t)rev.w32[1] * MAGICDIV >> 35);
nex = rev.w32[1] - res.w32[1] * 10;
rev.w32[1] = nex;
rev.w64 -= (uint64_t)divu64_dat[nex] * 10;
res.w32[0] = divu64_dat[nex];
if (rev.w32[1] != 0) {
rev.w64 -= (uint64_t)divu64_dat[1] * 10;
res.w64 += (uint64_t)divu64_dat[1];
};
nex = ((uint64_t)rev.w32[0] * MAGICDIV >> 35);
res.w64 += (uint64_t)nex;
*residue = rev.w32[0] - nex * 10;
return res.w64 ;
};
Последний раз редактировалось
AVI-crak Вт ноя 09, 2021 16:27:46, всего редактировалось 1 раз.
Вт ноя 09, 2021 10:39:59
А какие проблемы на M7?
https://godbolt.org/z/71jsT3xM6На M0 что получится?
Вт ноя 09, 2021 10:43:33
Деление uint64_t на 10 за 68 тиков
- Код:
for(uint64_t i = -1; i > 0xFFFF'FFFE'0000'0000; i--)
{
if (i / 10 != divu64_10(i))
printf("Error: %llu, %llu, %llu\n", i, i / 10, divu64_10(i));
}
>> Error: 18446744069414584319, 1844674406941458431, 1844674406511961702
>> Error: 18446744069414584318, 1844674406941458431, 1844674406511961702
>> Error: 18446744069414584317, 1844674406941458431, 1844674406511961702
>> Error: 18446744069414584316, 1844674406941458431, 1844674406511961702
Вт ноя 09, 2021 10:51:10
Reflector, естественно, там переполнение будет. Чтобы с uint64_t работать, нужно промежуточные результаты в uint128_t хранить. А это уж совсем оверхед для микроконтроллера.
Подозреваю, в оригинале все-таки был пример деления uint32_t, а uint64 случайно скопипастился.
Но я бы и с uint32_t проверил: нет ли переполнения. Помнится, у кого-то в алгоритме был сдвиг вправо на 35 — это стопудово работать не будет!
Вт ноя 09, 2021 10:55:27
Помнится, у кого-то в алгоритме был сдвиг вправо на 35 — это стопудово работать не будет!
Я приводил код с делением на 10/100/1000/1М, со сдвигами на 35 и т.д.. Быстро подобрал и проверил, потом оказалось gcc делает точно так же
Длинное умножение на ассме для M0 тоже естественно проверил, пришлось STM32G0 разогнать до 140MHz и подождать минут 20...
Вт ноя 09, 2021 12:13:04
Reflector, а, дошло: при сдвиге на 35 теряются три младших бита, но они нам и так не нужны, т.к. мы же делим не по правилам округления, а отбрасывая всю дробную часть!
Вт ноя 09, 2021 12:25:37
при сдвиге на 35 теряются три младших бита, но они нам и так не нужны, т.к. мы же делим не по правилам округления, а отбрасывая всю дробную часть!
При делении на 10(или умножении на 0.1) результат поместится в 29 бит, т.к. это будет наблюдаться уже при делении на 8, потому 3 дополнительных бита можно использовать для более точных расчетов. Аналогично при делении на 1000 можно дополнительно задействовать 9 бит(2^9 = 512).
Вт ноя 09, 2021 16:30:14
>> Error: 18446744069414584319
Упс, GCC усиленно сокращает (uint64_t) операции до (uint32_t), подправил.
Вт ноя 16, 2021 01:03:36
Вот вы тут все про оптимизацию, а я уже второй вечер под пивасик выдумываю, как бы реализовать рамп шаговика!
Позавчера таки пришли разъемы, я их запаял вчера и сегодня
пре-пре-альфу сделал. Еще полным-полно косяков, но без энкодера свободно крутит шаговик: ускоряет/замедляет. С энкодером чуть похуже: надо отладить алгоритм определения заклинивания, а то пока чуть пальцем нажал - и оно встало (вместо 24В подключил 12, да и вместо 1.7А на движок выдаю 600мА)!
Вот только с делениями оптимизировать нормально не вышло: каждые 10мс при разгоне/торможении нужно новое значение скорости вычислять. v=v₀ + a(t-t₀)/1000 (т.к. t измеряется в миллисекундах). А потом вычислять новое значение ARR: ARR = (((PCLK/(MOTORTIM_PSC+1)) / curspeed[i]) >> ustepsshift[i]) - 1; и проверять его на вхождение в интервал [99, 65535]. Поначалу я сделал минимальным ARR двойку, но процесс ускорения/торможения на высоких скоростях уж очень слух резал (таки получается, что вначале скорость падает вообще в 2 раза), а здесь квант в 1% — более плавно.
Как разберусь с движками, надо будет все остальное отладить по-человечески (особенно кучу сеттеров/геттеров всяких второстепенных). Да и протокол CAN я еще не проверял.
Вт ноя 23, 2021 18:01:52
Мама дорогая! printf на микроконтроллере!..
Вы туда еще malloc запихайте ради полного "Щассья".
А чем вам, собственно, не угодил printf? Стандартная СИшная функция, может быть реализована по разному для разных МК или приложений, но по-моему достаточно удобно использовать. Обеспечивает простую переносимость кода.
Да и malloc иногда полезен. Были у меня задачи, когда без него просто не получалось справиться.
Вт ноя 23, 2021 19:50:59
А чем вам, собственно, не угодил printf?
Оверхедом. Жутким оверхедом.
Стандартная СИшная функция
Ничуть она не сишная, она из glibc! Не существует "стандартной сишной функции printf"! Если что, вообще не существует стандартных сишных функций! Зато существует стандартная библиотека glibc, вот из нее-то эти функции и берутся.
Вот только никто не линкует прошивки микроконтроллеров с glibc! Для этого даже флаги специальные выставляются...
Да и malloc иногда полезен
Разве что если на МК есть MMU. Иначе malloc нафиг не нужен. Изврат это - вручную заниматься разделением памяти. Всегда есть шанс, что куча наткнется на стек, и получится незапланированный кирдык. А если резервировать для стека достаточно много, то приличный объем памяти вообще никогда не будет использован!
Ср ноя 24, 2021 05:17:42
Изврат это - вручную заниматься разделением памяти. Всегда есть шанс, что куча наткнется на стек, и получится незапланированный кирдык.
Ну, если в режиме обезьяны запускать все жирные задачи одновременно(даже те -которые сейчас нафиг не нужны) - то обязательно кончится память, и верхушка malloc налетит на ось. Вот только вопрос, зачем так делать?
Ср ноя 24, 2021 09:39:46
А зачем тогда вообще аллокаторы рукожопить, если и так все хорошо работает?
Что до printf, я против printf из glibc, т.к. он полон оверхеда (например, на кой черт разбирать аргументы uint64_t, float и double на 32-битном МК без FPU?). Для вывода целых чисел можно нарисовать свой printf. Я же ограничиваюсь функциями void bufputchar(char c), добавляющей в буфер отправки 1 символ, void addtobuf(char *s), добавляющей в буфер строку, а также char *printu(uint32_t u), char *printi(int32_t i) и char *printuhex(uint32_t u), которые выдают строку с числом.
Ср ноя 24, 2021 09:40:36
Ничуть она не сишная, она из glibc!
Только не из glibc (гнутого), а из libc (любого). Но семейство printf в стандарт Си входит, от этого никуда не деться. Другое дело что ее реализацию можно не подключать и соответственно сэкономить килобайт флеша и сколько-то тактов.
Разве что если на МК есть MMU.
При чем здесь MMU? Я бы еще понял MPU - модуль защиты памяти. Но преобразование адресов как поможет?
Кстати, без MMU может быть даже безопаснее: софт может самостоятельно проверять допустимость полученного адреса даже если выделение прошло успешно.
Ср ноя 24, 2021 10:00:34
При чем здесь MMU?
При том, что без него вообще руки надо отрывать за использование аллокаторов!
Ср ноя 24, 2021 10:13:13
От вас я ждал хоть какой-то аргументации, а не фанатичных воплей.
Powered by phpBB © phpBB Group.
phpBB Mobile / SEO by Artodia.