Запись области ОЗУ одной командой

Normas
Offline
Зарегистрирован: 17.05.2017

С ассемблером Atmel не знаком, но где-то читал  что можно одной стандартной командой 8-разрядного контроллера копировать область данных из ОЗУ в ОЗУ  в несколько десятков байт. Как это можно сделать  ?

Массивы большие и цель уменьшение времени исполнения программы.

strarbit
Offline
Зарегистрирован: 12.06.2016

Только побайтно

Normas
Offline
Зарегистрирован: 17.05.2017

Где бы посмотреть результат компиляции С++  в ассемблер для этой программы, неужели компилятор будет побайтно копировать  из области 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){}

 

 

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Да. Аналога цепочечных команд Intel-a у AVR нет. Единственное упрощение для программиста, есть команда пересылки с отоматическим инкрементом(декрементом) адреса.  И сё. 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

за то все команды за один такт?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

неть, каждая пересылка - такт.  

P.S.  Ошибся,  2 такта. 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Нет. Каждое чтение/запись SRAM занимают ДВА такта, если SRAM внутренняя и НЕ МЕНЕЕ 3-х тактов, если внешнее расширение SRAM (можно настроить до 5-6 уж не помню). Команда чтения/записи с автоинкрементом/декрементом кажется тоже требует 1 доп. такт на операцию инкремента/декремента, но это уже надо уточнять в мануале, лень лазить сейчас.

Так что, операция: считать байт по одному указателю--/++ , записать байт по другому--/++ занимает как миниум 4 такта. Плюсом проверка на длину блока и переход цикла .. итого "от 6 тактов" на каждый байт пачки. Другое дело, что наибольший объем памяти у дунек это .. 8 КИЛО байт "на все про все" .. какие там "большие объемы"?!?

negavoid
Offline
Зарегистрирован: 09.07.2016

Normas

Normas пишет:
Где бы посмотреть результат компиляции С++  в ассемблер для этой программы

c:\programfiles(x86)\arduino\hardware\tools\avr\bin\avr-objdump.exe -S c:\users\administrator\appdata\local\temp\build123.tmp\sketch.elf > c:\disassembled.txt

 

Normas
Offline
Зарегистрирован: 17.05.2017

negavoid пишет:
c:\programfiles(x86)\arduino\hardware\tools\avr\bin\avr-objdump.exe -S c:\users\administrator\appdata\local\temp\build123.tmp\sketch.elf > c:\disassembled.txt
Спасибо, но это наверное декомпиляция, в которой не будет оригинальных названий переменных (или ошибаюсь?). Мне желательна кросскомпиляция в ассемблер, или это она и есть ?

 

Normas
Offline
Зарегистрирован: 17.05.2017

Arhat109-2 пишет:
Другое дело, что наибольший объем памяти у дунек это .. 8 КИЛО байт "на все про все" .. какие там "большие объемы"?!?
На многократные перезаписи (копирования) массивов у программы уходит много времени.

negavoid
Offline
Зарегистрирован: 09.07.2016

Это дизассемблирование, но последние пару версий 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 много, мне просто лень искать нужный вам режим, может он и существует, но это уж сами.

Normas
Offline
Зарегистрирован: 17.05.2017

Arhat109-2 пишет:
Каждое чтение/запись SRAM занимают ДВА такта, если SRAM внутренняя и НЕ МЕНЕЕ 3-х тактов

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 мкС

 

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Normas пишет:

Где бы посмотреть результат компиляции С++  в ассемблер для этой программы

Я уже задавался этим вопросом, здесь подробно всё описано - Настройка компиляции в Arduino IDE

Normas
Offline
Зарегистрирован: 17.05.2017

negavoid пишет:
Это дизассемблирование, но последние пару версий ide собирает elf с символами, так что в некотором роде будут.
Я еще не освоился в IDE arduino, Вы не смогли бы дизассемблировать программу из одной строчки - копирования массива 128 байт

 

typedef struct  {volatile uint8_t   elem[128];} STRUCT128_T;
STRUCT128_T a128,b128; 

void setup(void) {a128=b128;} 
void loop(void){}

 

negavoid
Offline
Зарегистрирован: 09.07.2016
Normas
вот интересующий вас кусок кода:
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>

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Ну собственно об чем вам и написал: ld,st испльзуют по 2 такта, в т.ч. и с ++/--, декремент = 1 такт и переход = 2 такта. Итого имеем 7 тактов на каждый байт пересылки. Остальная неравномерность - обработка прерываний от таймера: millis(), micros() и т.д. Чуть около или больше 2 мегабайт в секунду .. где это "много времени" для 8килов в наибольшем варианте?

Logik
Offline
Зарегистрирован: 05.08.2014

Люди, читайте класику, все ж изложено в первоисточнике предельно просто. Клоки крайние справа.

 

 

И при желании экспериментально проверяется. Только правильно надо мерить, с учетом времени потраченого на измерение времени ;) Де там наша тема про время выполнения операций на разных контролерах завалялась?

Normas
Offline
Зарегистрирован: 17.05.2017

Arhat109-2 пишет:
Ну собственно об чем вам и написал: ld,st испльзуют по 2 такта, в т.ч. и с ++/--, декремент = 1 такт и переход = 2 такта. Итого имеем 7 тактов на каждый байт пересылки. Остальная неравномерность - обработка прерываний от таймера: millis(), micros() и т.д. Чуть около или больше 2 мегабайт в секунду .. где это "много времени" для 8килов в наибольшем варианте?

Контроллер  должен выполнять еще и текущие операции и  все вместе занимает  "много" по времени.

Кстати, сколько команд занимает вход-выход в функцию (пролог и эпилог), если не передавать параметры в стеке  то есть  void Myfunction(void) ?

 

Normas
Offline
Зарегистрирован: 17.05.2017

negavoid пишет:
вот интересующий вас кусок кода:
Спасибо

Normas
Offline
Зарегистрирован: 17.05.2017

Буду пытаться экономить время при помощи inline http://arduino.ru/forum/programmirovanie/inline-eto-prinuditelnyi-modifi...

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

5 тактов - вызов, 5 тактов возврат из функции. На каждый байт параметра надо добавить по 3 команды: считать параметр в регистр (2 такта), положить параметр на стек (2 такта), и внутри достать параметр со стека (2 такта). Это после первых 2-4 параметров, в зависимости от их разрядности. Смотрите описание соглашения о регистрах для avr-gcc, там все детально прописано. Соответственно, long параметр, передаваемый через стек это 4х6 = 24 такта "плюсом". Подпрограммы с 1-3 параметрами как правило хорошо оптимизируются и там происходит передача в регистрах.

Если подпрограмма имеет локальные переменные (особенно long, float), то там тоже сильно зависит от оптимизации, но можно легко словить локалы на стеке и избыточное сохранение регистров там же "до" и "после" работы подпрограммы даже для байтовых переменных. Как правило, относительно хорошо компилируются только простые подпрограммы с 0-6 байтами в локалах, независимо от размерности переменных.

Тут ЕвгенийП с год назад исследовал поверхностно этот вопрос .. вызовы простых подпрограмм снижают ногодрыг с 5Мгц примерно до 1, помнится..

Сказки про "однотактовые Risc" .. на практике всего лишь сказки.. увы.

Ну и ещё: вместо копирования массивов "туда-сюда-обратно", попробуйте освоить указательную арифметику. Не гарантирую, но возможно это то, что требуется.