Запись области ОЗУ одной командой
- Войдите на сайт для отправки комментариев
Ср, 17/05/2017 - 01:35
С ассемблером Atmel не знаком, но где-то читал что можно одной стандартной командой 8-разрядного контроллера копировать область данных из ОЗУ в ОЗУ в несколько десятков байт. Как это можно сделать ?
Массивы большие и цель уменьшение времени исполнения программы.
Только побайтно
Где бы посмотреть результат компиляции С++ в ассемблер для этой программы, неужели компилятор будет побайтно копировать из области ff в область ee и не знает способа лучше ?
typedef struct {uint8_t aa; // uint8_t m[100]; uint16_t bb; // uint32_t cc; // } AA_BB_CC; AA_BB_CC ee,ff; void setup(void) { Serial.begin(9600); ee.aa=9; ee.bb=19; ee.cc=199; ff.aa=1; ff.bb=2; ff.cc=3; ee=ff; // неужели будет копировать побайтно ? Serial.println(ee.aa); Serial.println(ee.bb); Serial.println(ee.cc); } void loop(void){}Да. Аналога цепочечных команд Intel-a у AVR нет. Единственное упрощение для программиста, есть команда пересылки с отоматическим инкрементом(декрементом) адреса. И сё.
за то все команды за один такт?
неть, каждая пересылка - такт.
P.S. Ошибся, 2 такта.
Нет. Каждое чтение/запись SRAM занимают ДВА такта, если SRAM внутренняя и НЕ МЕНЕЕ 3-х тактов, если внешнее расширение SRAM (можно настроить до 5-6 уж не помню). Команда чтения/записи с автоинкрементом/декрементом кажется тоже требует 1 доп. такт на операцию инкремента/декремента, но это уже надо уточнять в мануале, лень лазить сейчас.
Так что, операция: считать байт по одному указателю--/++ , записать байт по другому--/++ занимает как миниум 4 такта. Плюсом проверка на длину блока и переход цикла .. итого "от 6 тактов" на каждый байт пачки. Другое дело, что наибольший объем памяти у дунек это .. 8 КИЛО байт "на все про все" .. какие там "большие объемы"?!?
Normas
Это дизассемблирование, но последние пару версий ide собирает elf с символами, так что в некотором роде будут.
last_measure_time = millis(); 455a: 0e 94 c1 19 call 0x3382 ; 0x3382 <millis> 455e: 60 93 0e 05 sts 0x050E, r22 4562: 70 93 0f 05 sts 0x050F, r23 4566: 80 93 10 05 sts 0x0510, r24 456a: 90 93 11 05 sts 0x0511, r25где last_measure_time была определена, как UL, что мы и видим - результат millis [r22:r25] запихивается в 4 байта UL.
Ключей у avr-tools много, мне просто лень искать нужный вам режим, может он и существует, но это уж сами.
typedef struct {volatile uint8_t elem[32];} STRUCT32_T; typedef struct {volatile uint8_t elem[64];} STRUCT64_T; typedef struct {volatile uint8_t elem[128];} STRUCT128_T; typedef struct {volatile uint8_t elem[256];} STRUCT256_T; typedef struct {volatile uint8_t elem[512];} STRUCT512_T; // 1984 байт STRUCT32_T a32,b32; // 64=32+32 байт RAM STRUCT64_T a64,b64; // 128=64+64 байт RAM STRUCT128_T a128,b128; // 256=128+128 байт RAM STRUCT256_T a256,b256; // 512=256+256 байт, // для AVR младше mega256 удалить эту строку STRUCT512_T a512,b512; // 512=256+256 байт, // для AVR младше mega256 удалить эту строку uint16_t tstart,tstop; // время начала и завершения копирования void setup(void) { int i; Serial.begin(115200); // ХЗ как оптимизирует компилятор, поэтому // предотвращаю исключение компилятором кода с неиспользуемыми перемеными: for(i=0; i<32; i++) b32.elem[i]=i; for(i=0; i<64; i++) b64.elem[i]=i; for(i=0; i<128; i++) b128.elem[i]=i; for(i=0; i<256; i++) b256.elem[i]=i; for(i=0; i<512; i++) b512.elem[i]=min(i,255); tstart=micros(); a32=b32; tstop=micros(); Serial.print("size="); Serial.print(sizeof(STRUCT32_T)); Serial.print(" t=");Serial.println(tstop-tstart); tstart=micros(); a64=b64; tstop=micros(); Serial.print("size="); Serial.print(sizeof(STRUCT64_T)); Serial.print(" t=");Serial.println(tstop-tstart); tstart=micros(); a128=b128; tstop=micros(); Serial.print("size="); Serial.print(sizeof(STRUCT128_T)); Serial.print(" t=");Serial.println(tstop-tstart); tstart=micros(); a256=b256; tstop=micros(); Serial.print("size="); Serial.print(sizeof(STRUCT256_T)); Serial.print(" t=");Serial.println(tstop-tstart); // для AVR младше mega256 удалить эту строку tstart=micros(); a512=b512; tstop=micros(); Serial.print("size="); Serial.print(sizeof(STRUCT512_T)); Serial.print(" t=");Serial.println(tstop-tstart); // для AVR младше mega256 удалить эту строку // ХЗ как оптимизирует компилятор, поэтому // предотвращаю исключение компилятором кода с неиспользуемыми перемеными: Serial.println("--"); for(i=0; i<32; i++) {Serial.print(";"); Serial.print(a32.elem[i]); } Serial.println("\n\r--"); for(i=0; i<64; i++) {Serial.print(";"); Serial.print(a64.elem[i]); } Serial.println("\n\r--"); for(i=0; i<128; i++) {Serial.print(";"); Serial.print(a128.elem[i]); } Serial.println("\n\r--"); for(i=0; i<256; i++) {Serial.print(";"); Serial.print(a256.elem[i]); } // для AVR с малой памятью удалить эту строку Serial.println("\n\r--"); for(i=0; i<512; i++) {Serial.print(";"); Serial.print(a512.elem[i]); } // для AVR с малой памятью удалить эту строку: } void loop(void){}Получил нелинейную зависимость времени копирования от размера массива. Причина не понятна, ничем кроме работы сторонних прерываний объяснить это не могу :-(((
размер=32 байт, t=16 мкС
размер=64 байт, t=28 мкС
размер=128 байт, t=60 мкС
размер=256 байт, t=144 мкС
размер=512 байт, t=280 мкС
Где бы посмотреть результат компиляции С++ в ассемблер для этой программы
Я уже задавался этим вопросом, здесь подробно всё описано - Настройка компиляции в Arduino IDE
typedef struct {volatile uint8_t elem[128];} STRUCT128_T; STRUCT128_T a128,b128; void setup(void) {a128=b128;} void loop(void){}typedef struct {volatile uint8_t elem[128];} STRUCT128_T; STRUCT128_T a128,b128; void setup(void) {a128=b128;} 1aa: 80 e8 ldi r24, 0x80 ; 128 1ac: e0 e0 ldi r30, 0x00 ; 0 1ae: f1 e0 ldi r31, 0x01 ; 1 1b0: a0 e8 ldi r26, 0x80 ; 128 1b2: b1 e0 ldi r27, 0x01 ; 1 1b4: 01 90 ld r0, Z+ 1b6: 0d 92 st X+, r0 1b8: 8a 95 dec r24 1ba: e1 f7 brne .-8 ; 0x1b4 <main+0x90>Ну собственно об чем вам и написал: ld,st испльзуют по 2 такта, в т.ч. и с ++/--, декремент = 1 такт и переход = 2 такта. Итого имеем 7 тактов на каждый байт пересылки. Остальная неравномерность - обработка прерываний от таймера: millis(), micros() и т.д. Чуть около или больше 2 мегабайт в секунду .. где это "много времени" для 8килов в наибольшем варианте?
Люди, читайте класику, все ж изложено в первоисточнике предельно просто. Клоки крайние справа.
И при желании экспериментально проверяется. Только правильно надо мерить, с учетом времени потраченого на измерение времени ;) Де там наша тема про время выполнения операций на разных контролерах завалялась?
Контроллер должен выполнять еще и текущие операции и все вместе занимает "много" по времени.
Кстати, сколько команд занимает вход-выход в функцию (пролог и эпилог), если не передавать параметры в стеке то есть void Myfunction(void) ?
Буду пытаться экономить время при помощи inline http://arduino.ru/forum/programmirovanie/inline-eto-prinuditelnyi-modifi...
5 тактов - вызов, 5 тактов возврат из функции. На каждый байт параметра надо добавить по 3 команды: считать параметр в регистр (2 такта), положить параметр на стек (2 такта), и внутри достать параметр со стека (2 такта). Это после первых 2-4 параметров, в зависимости от их разрядности. Смотрите описание соглашения о регистрах для avr-gcc, там все детально прописано. Соответственно, long параметр, передаваемый через стек это 4х6 = 24 такта "плюсом". Подпрограммы с 1-3 параметрами как правило хорошо оптимизируются и там происходит передача в регистрах.
Если подпрограмма имеет локальные переменные (особенно long, float), то там тоже сильно зависит от оптимизации, но можно легко словить локалы на стеке и избыточное сохранение регистров там же "до" и "после" работы подпрограммы даже для байтовых переменных. Как правило, относительно хорошо компилируются только простые подпрограммы с 0-6 байтами в локалах, независимо от размерности переменных.
Тут ЕвгенийП с год назад исследовал поверхностно этот вопрос .. вызовы простых подпрограмм снижают ногодрыг с 5Мгц примерно до 1, помнится..
Сказки про "однотактовые Risc" .. на практике всего лишь сказки.. увы.
Ну и ещё: вместо копирования массивов "туда-сюда-обратно", попробуйте освоить указательную арифметику. Не гарантирую, но возможно это то, что требуется.