#51 2025-08-24 19:28:36

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

MikeM пише:

При формуванні порожнього екранного рядка визначаються 25 символів, які будуть відображатися в наступних N рядках і в змінні і0 ... і24 записуються стартові адреси описів відповідних символів в кодовій таблиці.

А, здається, зрозумів. Ви тримаєте таблицю заздалегідь порахованих зміщень. Ну, можна й так, якщо ресурсу вистачає. У мене був символьний буфер на весь екран, здається 25x8.5 при шрифті 8x14, і коди символів з нього вибирались на кожній ітерації.

MikeM пише:

Хвіст, що може тягнутися за символом враховано. Останній біт завжди нульовий

Якщо тільки цифри, то можна було б зробити ширину символа 4 пікселя. Тоді в рядок в два рази більше влізе. Та й висоту не обовʼязково кратну 8. Ну то таке, вам постановка задачі та пріорітети фіч видніше.

Неактивний

#52 2025-08-24 19:37:06

MikeM
Учасник
З Київ
Зареєстрований: 2017-11-03
Повідомлень: 182

Re: Регістрова змінна

dimich пише:

#pragma GCC unroll 25

Нічого не змінюється, включно з розміром скомпільованого файлу.

jokeer пише:

Тут прямо проситься stm32, в якій більше мегагерців. Чи Ch32v003.

Да багато чого напрошується, але я обрав UNO

Остання редакція MikeM (2025-08-24 19:39:06)

Неактивний

#53 2025-08-24 19:56:32

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

MikeM пише:
dimich пише:

#pragma GCC unroll 25

Нічого не змінюється, включно з розміром скомпільованого файлу.

Дивно. Ви ж це безпосередньо перед циклом "for" написали, а не десь на початку?
А, мабуть ви користуєтесь ардуінівським тулчейном з GCC 7.3.0. Ця прагма зʼявилась в GCC 8.
Просто я собі для platformio зробив пакунок atmelavr, який використовує avr-gcc, встановлений в системі (на даний момент 15.1.0), а не з їхнього репозиторію, який вони самі тягнуть з ардуїнівського з версією GCC часів Давньої Греції. Тоді вибачаюсь, не подумав перед тим як радити, що у людей це може не працювати.

Остання редакція dimich (2025-08-24 19:56:59)

Неактивний

#54 2025-08-24 20:01:58

jokeer
Гість

Re: Регістрова змінна

Нічого не змінюється,

Можливо компілятор і сам здогадатися розгорнути цикл,  без підказок.

обрав UNO

Досить старе і дороге рішення.  Якщо по ₴₴₴, так і по часу, який теж ₴₴₴. Ну, рішення прийняте.. wink

#55 2025-08-24 20:07:25

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

jokeer пише:

Можливо компілятор і сам здогадатися розгорнути цикл,  без підказок.

З дефолтовим -Os навряд чи.

Неактивний

#56 2025-08-26 17:16:59

MikeM
Учасник
З Київ
Зареєстрований: 2017-11-03
Повідомлень: 182

Re: Регістрова змінна

Намагаюся створити фрагмент програми, час виконання якого не буде залежати від оброблюваних значень. Зіштовхнувся з цікавим ефектом.

  if((serialBuf & 0x200) == 0) {i15 = 0;} else {i15 = 1; __asm__("nopnt");}

  if((serialBuf & 0x400) == 0) {i14 = 0; __asm__("nopnt""nopnt""nopnt");} else {i14 = 1;} 

Час виконання кожного з рядків стабільний, незалежно від значення serialBuf. Але для досягнення такої стабільності треба додавати різну кількість NOPів і, що ще цікавіше, в різні гілки.
Сиджу. Чухаю потилицю.
Доречі, на монітор таки вдалося вивести 30 рядків по 23 символи.

Остання редакція MikeM (2025-08-26 17:18:25)

Неактивний

#57 2025-08-26 19:26:39

jokeer
Гість

Re: Регістрова змінна

Треба писати асемблерну вставку. Бо займатись такто@@бством на С якось дивно.

serialBuf & 0x200

Якщо із 16 розрядної змінної треба перевірити тільки 1 біт - може є смисл перейти то 8-бітної змінної? а вас же 8-бітнй проц.

#58 2025-08-26 22:24:12

MikeM
Учасник
З Київ
Зареєстрований: 2017-11-03
Повідомлень: 182

Re: Регістрова змінна

Все ще цікавіше. Змінна 32-розрядна. Вхідна інформація надходить у вигляді 32-розрядних послідовних кодів трьох різних форматів. Ці слова мені треба прийняти, декодувати і вивести їх вміст у придатному для сприйняття людиною вигляді у відповідних позиціях екрану.
Звичайно, можна попередньо розібрати слово по байтах, але на це витрачається неприпустимо багато часу.

Остання редакція MikeM (2025-08-26 22:26:04)

Неактивний

#59 2025-08-26 22:54:48

jokeer
Гість

Re: Регістрова змінна

wink щоб розібрати слово на байти, часу потрібно приблизно 0 wink воно в пам'яті вже розібране на байти.

gcc вміє генерувати асемблерний код, можна подивитись що там вийшло і порахувати такти.

#60 2025-08-27 05:48:03

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

MikeM пише:

Намагаюся створити фрагмент програми, час виконання якого не буде залежати від оброблюваних значень. Зіштовхнувся з цікавим ефектом.

  if((serialBuf & 0x200) == 0) {i15 = 0;} else {i15 = 1; __asm__("nopnt");}
  if((serialBuf & 0x400) == 0) {i14 = 0; __asm__("nopnt""nopnt""nopnt");} else {i14 = 1;} 

Час виконання кожного з рядків стабільний, незалежно від значення serialBuf. Але для досягнення такої стабільності треба додавати різну кількість NOPів і, що ще цікавіше, в різні гілки.

По-хорошому, такі фрагменти потрібно одразу писати на асемблері, чи на вбудованому, чи в окремому файлі. Воно-то можна зкомпонувати вирази мовою високого рівня і підібрати необхідну кількість нопів. Але при подальших змінах в коді, чи при компіляції іншою версією, або з іншими опціями згенерований код може стати іншим, і все попливе.

Наприклад, такий код (еквівалентний рядку вашого):

uint32_t serialBuf;
uint16_t i14;

__attribute__((naked)) void foo()
{
    i14 = ((serialBuf & 0x400) == 0) ? 0 : 1;
}

Або, те ж саме:

i14 = !!(serialBuf & 0x400);

GCC версії 15.1.0 з опціями "-mmcu=atmega328p -Os" компілює в

00000000 <_Z3foov>:
   0:   80 91 00 00     lds     r24, 0x0000     ; 0x800000 <__SREG__+0x7fffc1>
   4:   82 fb           bst     r24, 2
   6:   88 27           eor     r24, r24
   8:   80 f9           bld     r24, 0
   a:   80 93 00 00     sts     0x0000, r24     ; 0x800000 <__SREG__+0x7fffc1>
   e:   10 92 00 00     sts     0x0000, r1      ; 0x800000 <__SREG__+0x7fffc1>

9 тактів, ніякої залежності від значень.

А GCC версія 7.3.0 з ардуінівського тулчейна з тими ж опціями компілює в

00000000 <_Z3foov>:
   0:   80 91 00 00     lds     r24, 0x0000     ; 0x800000 <__SREG__+0x7fffc1>
   4:   90 91 00 00     lds     r25, 0x0000     ; 0x800000 <__SREG__+0x7fffc1>
   8:   a0 91 00 00     lds     r26, 0x0000     ; 0x800000 <__SREG__+0x7fffc1>
   c:   b0 91 00 00     lds     r27, 0x0000     ; 0x800000 <__SREG__+0x7fffc1>
  10:   2a e0           ldi     r18, 0x0A       ; 10
  12:   b6 95           lsr     r27
  14:   a7 95           ror     r26
  16:   97 95           ror     r25
  18:   87 95           ror     r24
  1a:   2a 95           dec     r18
  1c:   01 f4           brne    .+0             ; 0x1e <_Z3foov+0x1e>
  1e:   81 70           andi    r24, 0x01       ; 1
  20:   99 27           eor     r25, r25
  22:   90 93 00 00     sts     0x0000, r25     ; 0x800000 <__SREG__+0x7fffc1>
  26:   80 93 00 00     sts     0x0000, r24     ; 0x800000 <__SREG__+0x7fffc1>

Дивіться дизассемблером, що генерується в кожному окремому випадку. Контекст виклику також істотно впливає на результат.
Можна за допомогою "avr-objdump -dz firmware.elf". Якщо збираєте власним Makefile, то генерацію асемблерного лістингу автоматизувати просто. В platformio також можна.
Опцією -S можна тимчасово вказати компілятору, щоб замість обʼєктного продукував асемблерний файл і на тому зупинявся.
Можна зберігати проміжні результати компіляції опцією -save-temps.
Для швидкого аналізу коду, що генерується, можна користустуватись godbolt.org. Тільки avr-gcc 7.3.0 чомусь там не знайшов.
Також майте на увазі, що link time optimization (опція -flto) може кардинально змінити код в результуючому .elf порівняно з тим, що в обʼєктному файлі. Тому кінцевий результат потрібно дивитись objdump'ом на .elf.

MikeM пише:

Звичайно, можна попередньо розібрати слово по байтах, але на це витрачається неприпустимо багато часу.

По суті, на обробку даних у вас є тільки час вертикального зворотнього "ходу променю".
Якщо горизонтальна синхронізація (HSync) генерується апаратно таймером по Output Compare Match, то це безперервний інтервал в 1.4 мс, тобто більше 22000 тактів. Плюс декілька десятків тактів під час кожного горизонтального синхроімпульсу та back porch.
Якщо HSync програмна на GPIO, то цей інтервал потрібно розбивати на безперервні фрагменти не більше як десь по 450 тактів.

Остання редакція dimich (2025-08-27 07:46:09)

Неактивний

#61 2025-08-27 08:31:53

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

Цікаво, що навіть GCC 15.1.0 такий фрагмент:

i14 = (serialBuf & 0x400) == 0 ? 0 : 1;

компілює в короткий 9-тактовий код, а еквівалентний йому

i14 = (serialBuf & 0x400) ? 1 : 0;

в довгий, схожий на той що генерує 7.3.0.

Остання редакція dimich (2025-08-27 08:37:38)

Неактивний

#62 2025-08-27 11:32:38

jokeR
Учасник
Зареєстрований: 2024-12-12
Повідомлень: 160

Re: Регістрова змінна

А можна трохи пояснень? wink
А то відкрив систему команд AVR і ні%%я не пойняв wink

lds     r24, 0x0000 ; завантажили в r24 те що лежало в RAM за адресою 0х0000? serialBuf? старший байт?
bst     r24, 2 ; поклали в Т біт 2, 2^2=0х4; ок
eor     r24, r24 ; обнулили r24; ок
bld     r24, 0 ; встановили молодший біт в значення, збережене в Т; 0 або 1; ок
sts     0x0000, r24 ; зберігаємо 0/1 в 0х0000? назад в serialBuf?
sts     0x0000, r1  ; а що в r1??

Трюк з bst-eor-bld зовсім неочевидний, я б не додумався. Компілятор оптимізує однозначно краще. А що робить lds-sts - незрозуміло.

Неактивний

#63 2025-08-27 11:53:59

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

jokeR пише:
lds     r24, 0x0000 ; завантажили в r24 те що лежало в RAM за адресою 0х0000? serialBuf? старший байт?

Це дизасемблинг обʼєктного файлу (.o) до релокації, в ньому всі адреси нульові. При лінковці вже лінкер підставить потрібні адреси. Там буде адреса другого байта

jokeR пише:
sts     0x0000, r24 ; зберігаємо 0/1 в 0х0000? назад в serialBuf?

Нє, там буде інша адреса. Тут має бути зрозуміліше: https://godbolt.org/z/zKqrK7szG

jokeR пише:
sts     0x0000, r1  ; а що в r1??

За конвенцією в r1 завжди нуль. Саме тому після інструкцій mul, а також в преамбулі ISR компілятор його обнуляє. Є сумніви щодо доцільності такого рішення, але вже так.

jokeR пише:

А що робить lds-sts - незрозуміло.

Просто завантажує (Load Direct from Data Space) та вивантажує (Store Direct to Data Space) значення регістрів з/в памʼять. Так, в нерелокованому коді нульові адреси збивають з пантелику.

Остання редакція dimich (2025-08-27 12:27:32)

Неактивний

#64 2025-08-27 12:54:35

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

Для демонстрації, мабуть, красивіше так:

$ avr-objdump -dzrwC --no-address --no-show-raw-insn --visualize-jumps test.o
...
<foo()>:
        lds     r24, 0x0000     ; 0x<__SREG__+0x7fffc1> R_AVR_16        serialBuf+0x1
        bst     r24, 2
        eor     r24, r24
        bld     r24, 0
        sts     0x0000, r24     ; 0x<__SREG__+0x7fffc1> R_AVR_16        i14
        sts     0x0000, r1      ; 0x<__SREG__+0x7fffc1> R_AVR_16        i14+0x1

Або просто продукувати лістинг компілятором

$ avr-g++ -mmcu=atmega328p -Os -S -o - test.cc

Але з -flto після лінковки кінцевий результат може бути зовсім іншим, так що це дуже приблизно.

Остання редакція dimich (2025-08-27 13:26:54)

Неактивний

#65 2025-08-27 14:22:16

jokeR
Учасник
Зареєстрований: 2024-12-12
Повідомлень: 160

Re: Регістрова змінна

Цікаві досліди wink

        sts     0x0000, r1      ; 0x<__SREG__+0x7fffc1> R_AVR_16        i14+0x1

Записуємо 0 після i14? наче в С коді не було. Чи це вже щось наступне?

Неактивний

#66 2025-08-27 14:46:00

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

jokeR пише:

Цікаві досліди winkЗаписуємо 0 після i14? наче в С коді не було. Чи це вже щось наступне?

Старший байт 16-бітної змінної. У автора там наче б то індекс для масиву зі шрифтом.

Остання редакція dimich (2025-08-27 14:49:41)

Неактивний

#67 2025-08-27 16:47:44

jokeR
Учасник
Зареєстрований: 2024-12-12
Повідомлень: 160

Re: Регістрова змінна

Точно wink Дякую.
Колись учили ассемблер x86, там все якось більш зрозуміло було wink

Неактивний

#68 2025-08-27 16:54:03

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

jokeR пише:

Колись учили ассемблер x86, там все якось більш зрозуміло було wink

x86 CISC, регістрів менше, а інструкцій більше. А тут RISC, все навпаки smile

Неактивний

#69 2025-08-27 22:25:52

MikeM
Учасник
З Київ
Зареєстрований: 2017-11-03
Повідомлень: 182

Re: Регістрова змінна

Закинув я подалі ідею підлаштовувати час виконання фрагментів програми і вирішив використати переривання від таймера. Працює, але не без несподіваного сюрприза. Раніше нормально виводилися 23 символи в рядку, з використанням переривання тільки 17. При спробі вивести 18й символ (а місце для нього є і на екрані, і на осциллограмі) таймер збивається. Таке враження, що, якщо обробка переривання не завершується за 4 мкс до наступного переривання, то це має негативний вплив. Хто-небудь може щось прояснити?

Неактивний

#70 2025-08-27 22:40:30

jokeer
Гість

Re: Регістрова змінна

Пояснити, як працює код, коли не показано жодного рядка - це вищий пілотаж wink
Подивіться, який код згенеровано для обробки переривання. Все іде до того,  що пора добряче упоротись в архітектуру avr.

#71 2025-08-27 23:30:40

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

MikeM пише:

При спробі вивести 18й символ (а місце для нього є і на екрані, і на осциллограмі) таймер збивається.

Що значить "збивається"?

MikeM пише:

Таке враження, що, якщо обробка переривання не завершується за 4 мкс до наступного переривання, то це має негативний вплив.

4 мкс - це 64 такта. Спробую вгадати, ви помістили в обробник чималий шматок коду, який потребує зберігання/відновлення всіх регістрів. 32 інструкції pop по 2 такта в епілозі обробника, от вам і 4 мкс.
По-хорошому, обробник має складатись з єдиної інструкції reti: тільки розбудити процесор зі сплячки і завершитись.

Покажіть же, як ініціалізуєте таймер, що виконується в обробнику, як синхронізуєте переривання з рештою коду.

MikeM пише:

Хто-небудь може щось прояснити?

При наявній на даний момент інформації - можуть хіба що екстрасенси-телепати.

Неактивний

#72 2025-08-27 23:58:34

MikeM
Учасник
З Київ
Зареєстрований: 2017-11-03
Повідомлень: 182

Re: Регістрова змінна

Дякую за оперативність. З ваших відповідей я зрозумів, чого не вистачає. Треба зупиняти процессор!
Нове питання: як це зробити з Arduino IDE?

Неактивний

#73 2025-08-28 04:38:09

jokeer
Гість

Re: Регістрова змінна

Треба зупиняти процессор!

Навіщо? Ви плануєте заодно і в енергоспоживання гратись?

#74 2025-08-28 05:44:59

dimich
Учасник
Зареєстрований: 2023-12-01
Повідомлень: 627

Re: Регістрова змінна

MikeM пише:

З ваших відповідей я зрозумів, чого не вистачає. Треба зупиняти процессор!

Так. Може це не єдиний, але, мабуть, найпростіший спосіб синхронізувати виконання коду з перериванням.

Обробник переривання не може почати виконуватись посередині 2- чи 3-тактової інструкції. А організувати цикл чи розгалуження без таких інструкцій неможливо. Щоб забезпечити незмінність затримки між виникненням запиту на переривання та виконанням коду, потрібно, щоб запит виникав, коли процесор знаходиться в режимі idle, тобто виконує інструкцію sleep.

MikeM пише:

Нове питання: як це зробити з Arduino IDE?

Що потрібно зробити, написано в даташиті, розділи 9.3 Idle Mode, 9.11.1 SMCR – Sleep Mode Control Register.
Як це можна зробити засобами avr-libc, написано в avr/sleep.h. Цей хідер для вашої версії avr-libc лежить у вас на диску в тулчейні.
Якщо коротко (по запису, не по виконанню):

// Один раз на початку:
set_sleep_mode(SLEEP_MODE_IDLE);
...
// Для входу в режим очікування:
sleep_mode();

Але це оверхед з перестраховкою. Можна простіше:

// Один раз на початку:
set_sleep_mode(SLEEP_MODE_IDLE);
sleep_enable();
...
// Для входу в режим очікування:
sleep_cpu();

IDE тут ні до чого, код писати можна хоч у блокноті. Чи ви маєте на увазі, зробити засобами фреймворка ардуіно? По-перше, зручно використовувати 16-бітний TIMER1 в режимі CTC, бо при 16 МГц одна горизонтальна лінія відповідає циклу таймера в 508 тактів. По-друге, потрібно як мінімум вимкнути переривання від TIMER0, який ардуіно фреймворк використовує для своїх потреб.

Остання редакція dimich (2025-08-28 05:55:18)

Неактивний

#75 2025-08-28 08:27:50

jokeer
Гість

Re: Регістрова змінна

Хитро придумано wink

Швидке повідомлення

Введіть повідомлення і натисніть Надіслати

Підвал форуму