перлы оптимизации GCC и ногодрыги..

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

! ПЕРЕИМЕНОВАЛ ТЕМУ. Можно будет дополнять, если кто-то столкнется ещё с чем !

Гоняю плату дозиметра

Исходник скетча:

/**
 * Тест накачки высокого обратноходовым импульсным преобразователем
 * В этом варианте показывает до 679 попугаев или 1384в на ВВ-трансфоматоре
 */
extern "C" {

#define pinPWM   5   /* PORTD PORTD5*/
#define pinHV    0   /* analog pin A0! */

#define MAX_ADCS     4
#define MAX_SAMPLES 10
int adcs[MAX_ADCS*MAX_SAMPLES];

#define pwmON()  (PORTD |= 0B00100000)
#define pwmOFF() (PORTD &= 0B11011111)

/**
 * INLINE: 16-bit counter: up to 65535*4 F_CPU for 16Mhz:[0.25 .. 16383.75] mcsec.
 * Короткие задержки по 4 цикла ЦПУ (кратно 250 нсек)
 * !!! Работает неверно !!!
 */
#define delayMicro16(__count) \
  __asm__ __volatile__ (      \
    "1: sbiw %0,1 \n\t"       \
       "brne 1b\n\t"          \
       :: "w" (__count)       \
  )
/** вроде работает: проверить время! ТОЖЕ НЕ РАБОТАЕТ */
#define delayUS(t)     \
{                      \
  do{;}while(--(t)>0); \
}

#define adcStart() (ADCSRA |= (1<<ADSC))
#define adcWait()  while(ADCSRA&(1<<ADSC))
/**
 * Однократное чтение АЦП 8-бит (младших)
 * Если замер завершен, то сначала запуск следующего.
 * Пока идет замер - работаем дальше
 */
#define adcReadFast(res)   \
{                          \
  adcStart();              \
  adcWait();               \
  (res)=ADC;               \
}

} // extern"C"

void setup()
{
  pinMode(pinPWM, OUTPUT);
  pwmOFF();

  Serial.begin(115200);

  ADCSRA = (1<<ADEN) | (2);               // включаем АЦП и устанавливаем делитель 1(8), 2(4), 3(2), 4(1), 5(0.5), 6(0.25), 7(0.125)
  ADMUX  = (0<<REFS1)|(1<<REFS0)|(pinHV); // 01 - опорное 5в, рабочий вход - 7
  ADCSRB = 0;                             // тут ничего не надо
  DIDR0  = 255;                           // отключаем от OUTPUT все входы АЦП, дабы не разбираться
  adcStart();                             // запуск и сразу
  adcWait();                              // пропуск первого чтения, ибо мусор и долго
}

void loop()
{
  int wait;
  int n, i;
  int *ptr;

  for(n=0; n<MAX_SAMPLES; n++){
    wait = 4;
    ptr = adcs + MAX_ADCS*n;

    cli();
      pwmON();
      delayMicro16(wait);
      pwmOFF();
      adcReadFast(*ptr++); // MAX_ADCS раз!
      adcReadFast(*ptr++);
      adcReadFast(*ptr++);
      adcReadFast(*ptr++);
    sei();
  }

  // вывод попыток накачки на плоттер
  for(n=0; n<MAX_SAMPLES; n++){
    for(i=0; i<5; i++){ Serial.println(n, DEC); }
    for(i=0; i<MAX_ADCS; i++){
      Serial.println( adcs[i+n*MAX_ADCS], DEC);
    }
  }
  delay(2000); // пауза для просмотра результатов
}

и, непонимая что периодически происходит (код работает странно), полез в ассемблер через обждамп и обнаруживаю следующее:

Часть 1: Накачка и чтение данных:

00000596 <main>: // исполнение начинается отсюда!

void init() -- упс. inline вставка вместо вызова
{
 596:	78 94       	sei
 // и далее стандартная настройка таймеров под Ардуино
 // + теперь настройка АЦП на 125кГц тут
 // + inline вставка pinMode(pinPWM, OUTPUT) ..
 // + inline вставка setup() в который впихнут inline Serial.begin()
 // + inline следом код из loop():

 6e6:	c4 e0       	ldi	r28, 0x04	; 4  // (Y) wait=4;
 6e8:	d0 e0       	ldi	r29, 0x00	; 0

 // СЮДА приходит inline вставка вызова loop() в main()!!! отсюда повтор всего loop()

 6ea:	9d e1       	ldi	r25, 0x1D	; 29 // Z = R12,13 = R24,25 = 0x11D (129)
 6ec:	c9 2e       	mov	r12, r25
 6ee:	91 e0       	ldi	r25, 0x01	; 1
 6f0:	d9 2e       	mov	r13, r25

 6f2:	f6 01       	movw	r30, r12

  for(n=0; n<MAX_SAMPLES; n++){
    wait = 4;
    ptr = adcs + MAX_ADCS*n;
// сюда возвращаемся при повторе цикла:
 6f4:	f8 94       	cli
 6f6:	5d 9a       	sbi	0x0b, 5	        ; 11                  // pwmON();

 6f8:	21 97       	sbiw	r28, 0x01	; 1                   // delayMicro16(wait);
 6fa:	f1 f7       	brne	.-4      	; 0x6f8               // цикл задержки
 6fc:	5d 98       	cbi	0x0b, 5	        ; 11                  // pwmOFF();

 6fe:	80 91 7a 00 	lds	r24, 0x007A	; adcReadFast(*ptr++);
 702:	80 64       	ori	r24, 0x40	;
 704:	80 93 7a 00 	sts	0x007A, r24	;
 708:	80 91 7a 00 	lds	r24, 0x007A	;
 70c:	86 fd       	sbrc	r24, 6
 70e:	fc cf       	rjmp	.-8      	;
 710:	80 91 78 00 	lds	r24, 0x0078	;
 714:	90 91 79 00 	lds	r25, 0x0079	;
 718:	91 83       	std	Z+1, r25	;
 71a:	80 83       	st	Z, r24
 71c:	80 91 7a 00 	lds	r24, 0x007A	; adcReadFast(*ptr++);
 720:	80 64       	ori	r24, 0x40	;
 722:	80 93 7a 00 	sts	0x007A, r24	;
 726:	80 91 7a 00 	lds	r24, 0x007A	;
 72a:	86 fd       	sbrc	r24, 6
 72c:	fc cf       	rjmp	.-8      	;
 72e:	80 91 78 00 	lds	r24, 0x0078	;
 732:	90 91 79 00 	lds	r25, 0x0079	;
 736:	93 83       	std	Z+3, r25	;
 738:	82 83       	std	Z+2, r24	;
 73a:	80 91 7a 00 	lds	r24, 0x007A	; adcReadFast(*ptr++);
 73e:	80 64       	ori	r24, 0x40	;
 740:	80 93 7a 00 	sts	0x007A, r24	;
 744:	80 91 7a 00 	lds	r24, 0x007A	;
 748:	86 fd       	sbrc	r24, 6
 74a:	fc cf       	rjmp	.-8      	;
 74c:	80 91 78 00 	lds	r24, 0x0078	;
 750:	90 91 79 00 	lds	r25, 0x0079	;
 754:	95 83       	std	Z+5, r25	;
 756:	84 83       	std	Z+4, r24	;
 758:	80 91 7a 00 	lds	r24, 0x007A	; adcReadFast(*ptr++);
 75c:	80 64       	ori	r24, 0x40	;
 75e:	80 93 7a 00 	sts	0x007A, r24	;
 762:	80 91 7a 00 	lds	r24, 0x007A	;
 766:	86 fd       	sbrc	r24, 6
 768:	fc cf       	rjmp	.-8      	;
 76a:	80 91 78 00 	lds	r24, 0x0078	;
 76e:	90 91 79 00 	lds	r25, 0x0079	;
 772:	97 83       	std	Z+7, r25	;
 774:	86 83       	std	Z+6, r24	;

 776:	78 94       	sei                     ; sei();
 778:	38 96       	adiw	r30, 0x08	; 8 // оптимизация ptr++ прибавляем 1 раз сразу всё!

 77a:	21 e0       	ldi	r18, 0x01	; 1   оптимизация повтора цикла: контроль смещения ptr (Z)
 77c:	ed 36       	cpi	r30, 0x6D	; 109
 77e:	f2 07       	cpc	r31, r18
 780:	09 f0       	breq	.+2      	; 0x784 выход
 782:	b8 cf       	rjmp	.-144    	; 0x6f4 возврат на повтор цикла

Внезапно, установка wait=4 вынесено за тело цикла и вообще фактически за тело loop()! И это несмотря на то, что в макросе переменная ассемблерной вставки указана как ИЗМЕНЯЕМАЯ ..

Вопрос: КАК указать уомпилятору, что параметр этого макроса .. возвращаемый?

Макрос, который "не работает" на чистом "С" - тупо вообще выбрасывается компилятором как ненужный (это понятно почему).

Добавление volatile к переменной wait  приводит к её выбросу в память и соответственно нарушает длительность задержки..

Есть какие-то решения?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Конечно, есть. Вот здесь в конце как раз про это (у меня всегда срабатывала передача переменных в asm по именам с явным указанием, что они out)

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

Так лучше,

/**
 * INLINE: 16-bit counter: up to 65535*4 F_CPU for 16Mhz:[0.25 .. 16383.75] mcsec.
 * Короткие задержки по 4 цикла ЦПУ (кратно 250 нсек)
 * !!! Работает неверно !!!
 */
#define delayMicro16(__count) \
  __asm__ __volatile__ (      \
    "1: sbiw %0,1 \n\t"       \
       "brne 1b\n\t"          \
       : "=w" (__count)       \
       : "0"  (__count)       \
)

но тогда возникает 2 мелких засады:

1. Нельзя вызвать с константой: delayMicro16(4); -- получаем сообщение компилятора "Lvalue required", что совершенно правомерно.

и второе: код транслируется все равно так:

// вместо этого
  for(n=0; n<MAX_SAMPLES; n++){
    wait=4;
    ptr = adcs + MAX_ADCS*n;

    cli();
      pwmON();
      _delay_loop_2(wait);
      pwmOFF();
      adcReadFast(*ptr++); // MAX_ADCS раз!
      adcReadFast(*ptr++);
      adcReadFast(*ptr++);
      adcReadFast(*ptr++);
    sei();
  }

// обратно переводя на "С" получаем:
  for(n=0; n<MAX_SAMPLES; n++){
    ptr = adcs + MAX_ADCS*n;

    cli();
      pwmON();

      wait=4;
      _delay_loop_2(wait);

      pwmOFF();
      adcReadFast(*ptr++); // MAX_ADCS раз!
      adcReadFast(*ptr++);
      adcReadFast(*ptr++);
      adcReadFast(*ptr++);
    sei();
  }

То есть, вместо восстановления значения wait (все переменные регистровые) вначале цикла, имеем втягивание этой операции внутрь блока закрытых прерываний и удлинение времени между pwmOn() и pwmOff() гарантировано на 1 такт. Для этого места - очень много..

 6f4:	f8 94   cli               ; cli();
 6f6:	5d 9a   sbi     0x0b, 5   ; pwmON();
 6f8:	ce 01   movw    r24, r28  ; ! wait=tmpReg=4; -- лишние 62.5нсек!?!
 6fa:	01 97   sbiw    r24, 0x01 ; delay..();
 6fc:	f1 f7   brne    .-4       ; 
 6fe:	5d 98   cbi     0x0b, 5   ; pwmOFF();

Тут, наверное, больше бы подошло какое-то указание что этот блок операторов не может быть переставлен компилятором .. есть такая возможность?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Т.е. Вы хотите одновременно иметь возможность и изменять параметр, и передавать константу? Ну, удачи. А чем Вас не устраивает delay_us стесняюсь спросить?

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

а чо отключение оптимизации говорит? 

#pragma GCC optimize "O0"

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

Конечно! Это же только тестовый скетч, где тупо подбираю задержку и смотрю чего получается. Соответственно, нужна пиременная, дабы не перекомпилять скетч, а сделать подбор в цикле. А вот в рабочем скетче надо будет иметь константу. Переписывать? Лениво.

Не устраивает большой и грубой задержкой. Там нужна кратность от 1 такта ЦПУ (и даже это - "многовато", хочется аккуратней).

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

Отключение оптимизации и возврат к версии 1.6.4 - говорят что все компиляется практически как надо, по крайней мере этот критический кусок кода. LTO - конечно полезная весчь, но вот получить асм из под него низзя, а обратная декомпиляция объектника показывает вот такую фигню.

Ну и конечно хочется сделать так, чтобы можно было компилять без правки опций в ИДЕ.

P.S. Оставил последний вариант, который "лучше". В целом, за это время качество оптимизатора сильно выросло, что радует. Но .. при этом вопрос времени исполнения отдельных участков кода стал практически не прогнозируемым, что для управляющего компилятора - откровенно плохо: "привет всем любителям ногодрыгов".

Получается что, теперь ни в одном месте нельзя ожидать что код будет работать строго так, как написано.

P.P.S. В рабочем варианте буду уходить на прямое задание ШИМ таймером и работу через прерывания, что исключает замер высокого напряжения силами АЦП (работа по прерываниям значительно тормозней).

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

Arhat109-2 пишет:

Получается что, теперь ни в одном месте нельзя ожидать что код будет работать строго так, как написано.

Это лжывый С++, брат.  Когда встаешь с утра, никогда нельзя сказать точно, что сегодня будет значить оператор присваивания... 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Arhat109-2 пишет:

Получается что, теперь ни в одном месте нельзя ожидать что код будет работать строго так, как написано.

Отнюдь.

Полностью отключите оптимизацию, и "код будет работать строго так, как написано". 

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

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

andriano, Вы странный оппонент: пишете ровно то же самое, но предваряете его "отнюдь". Какой в этом смысел .. даже не знаю как Вам ответить.. :)

1. "Полностью отключаете оптимизацию" - означает только то, что вопрос написания БИБЛИОТЕКИ для этого шилда (у меня дозиметр - это шилд к нашему "Ардуино как лего") - ИСКЛЮЧАЕТСЯ от слова "совсем".

2. Наличие оптимизации заложено в компиляцию скетчей "по умолчанию", что собственно и исключает первый пункт. И это утверждение выше жирным становится верным для любого программного ногодрыга, потому что нельзя гарантировать что между волатил "дрыгами" компилятор не насует или не уберет чего-то жизненно важного для тайминга "дрыга", что я и показал выше. Особенно это может быть прикольным из-за инлайн втягивания одноразовых функций, что может существенно повлиять на тайминг: время вызова и возврата - оно не такое и маленькое - около 10 тактов, даже без параметров и сохранения контекстов..

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

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

Впрочем, вот тут и в смежных статьях поднимается ровно та же самая проблема и вопросы: http://microsin.net/programming/avr/avr-gcc-code-ordering-with-optimisat...

Как понимаю, проблема - давняя и нигде толком не решена. То есть, программные ногодрыги, так любимые многими, хорошо работают только тогда, когда их тайминг на порядок меньше чем времена исполнения инструкций. Ну или "а мне повезло - пашет". Все остальное - при нововведениях в оптимизатор может легко "пойти лесом" и не может быть применено в библиотеках "широкого пользования эмулирующего типа" (привет, wire.h, servo.h и т.д.).

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Архат, Вы немного увлеклись и Вас понесло. Успокойтесь, может быть отложите это дело на день, а потом со спокойной головой проделйет следующее.

1. Оформите параметр не так, как Вы попытались, а так, как там написано - через параметры директивы asm. Она всё поймёт правильно. Параметры asm видны компилятору.

2. С передачей константы - ну подумайте сами, в этом ассемблерном куске вы изменяете параметр - какая нафиг константа? Вы константу менять собрались? Так что про константу забудьте или сделайте отдельную функцию с параметром-константой, которая скадыает константу во внутренний буффер, а потом вызывает эту функцию.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

andriano пишет:

Полностью отключите оптимизацию, и "код будет работать строго так, как написано". 

Сам не сталкивался, но в документации (я выше давал ссылку) прямо написано, что нифига. Даже с отключённой полностью оптимизацией может переставить код.

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

ЕвгенийП, да уже понятно что к чему .. поостыл. Библу буду делать на таймере, нафиг этот гемморой с ногодрыгами.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Удачи!

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

Arhat109-2 пишет:

ЕвгенийП, да уже понятно что к чему .. поостыл. Библу буду делать на таймере, нафиг этот гемморой с ногодрыгами.

Здесь на таймере оч. правильно. Не забудте, что еще со "звоном" боротся, там тоже временной интервал, таймер нужен будет. А про оптимизацию - то что компилятор несколько операторов вида *ptr++ компилирует в типа std Z+n,.. , с разными n а потом "оптом" один раз прибавляет adiw    r30,.. я и на младших версиях компилятора замечал, удивлялся, проверял, и таки да! так короче и ошибок в оптимизации не замечал. Что внутрь блока запрещенного прерывания может чего внести - так запроста. Даже вынести вроде может ))) Он помоему вобще в запретах/разрешениях не шарит, считая это обычными функциями и переставляя когда хочется. К счастью хочется ему не часто.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

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

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Arhat109-2 пишет:

andriano, Вы странный оппонент: пишете ровно то же самое, но предваряете его "отнюдь". Какой в этом смысел .. даже не знаю как Вам ответить.. :)

1. "Полностью отключаете оптимизацию" - означает только то, что вопрос написания БИБЛИОТЕКИ для этого шилда (у меня дозиметр - это шилд к нашему "Ардуино как лего") - ИСКЛЮЧАЕТСЯ от слова "совсем".

Еще раз: "наличие оптимизации" и "выполнять, как написано" - вещи взаимоисключающие.

Совместить их принципиально невозможно. От слова "совсем".

Arhat109-2 пишет:

Как понимаю, проблема - давняя и нигде толком не решена.

Это "проблема" принципиально неразрешима. Поэтому не только "толком" но и вообще никак решена быть не может.

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

ЕвгенийП пишет:

andriano пишет:

Полностью отключите оптимизацию, и "код будет работать строго так, как написано". 

Сам не сталкивался, но в документации (я выше давал ссылку) прямо написано, что нифига. Даже с отключённой полностью оптимизацией может переставить код.

Если может переставить, значит оптимизация полностью не отключается. Вне зависимости от того, что по этому поводу написано в документации.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Да, нет. Это ж не ассемблер, где транслируется "команда в команду". Задача компилятора сгенерировать код, адекватный исходному тексту. Он это делает. А что и где он размещает - его дело. Как и "что он называет оптимизацией, а что не называет".

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

Ворота пишет:

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

Отсюда поподробнее плиз.. Про какие "специальные средства" - речь? Очень интересно .. если про асм "memory" то оно эту конкретно проблему имхо не решает ни разу. Или можете показать "как"? Буду - признателен, код выложен..

P.S. я пока что нашел только 1 выход: критические к оптимизации участки кода делать исключительно asm() вставками "целиком". Но это же .. "костыль"! Как быть с "переносимостью" на иную платформу?!?

Просто как итого, вывод банален до мычания: оптимизация устраняет писание на ЯВУ кода ногодрыга, критичного ко времни исполнения и/или порядка команд. Что для управляющего кода в общем-то "сплошь и рядом". Понятно что "в большинстве случаев" оно получается терпимо .. но как факт имеет место быть: "ногодрыг не гарантирован к исполнению".

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

andriano пишет:
Еще раз: "наличие оптимизации" и "выполнять, как написано" - вещи взаимоисключающие. Совместить их принципиально невозможно. От слова "совсем".

Это "проблема" принципиально неразрешима. Поэтому не только "толком" но и вообще никак решена быть не может.

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

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Arhat109-2 пишет:
Про какие "специальные средства" - речь?
Я отвечал логику про вынос куска кода из зоны, где он прерывания запретил. Для этого есть atomic и всё, что там рядом с ним.

А про твою проблему - я её не понимаю, извини. Ты хочешь передавать константу, и при этом изменять параметр? Эта проблема не решаема в принципе. Если не константу, а переменную, так тебе уже сказали как это делается, только ты упорно делаешь не так (судя по коду). В чём собственно проблема? В константе или в том, что ты по каким-то причинам не хочешь делать так, как написано в документации? Если хочешь обсудить свою проблему, сформулируй её как следует, пожалуйста

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

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

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Так и решение изложено достаточно подробно. Потому мне и непонятно в чём оставшаяся проблема. В том, что ты не хочешь сделать как написано в документации? Или в том, что, что ты хочешь сохранить возможность изменения константы?

Чисто, чтобы не было недоразумений - то, что ты написал в посте №2 - это не так, как у микрочипа написано. А информации о том, что ты попробовал именно так, как там, в теме нет.

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

?!? Где "изложено решение" и тем более "достаточно подробно", не нашел .. вот правда. покажите пальцем.

что там в посте №2 "не так как у микрочипа" написано? Тоже поясните, мне опять непонятно что Вы хотели этим сказать.. там тупо выложен тестовый скетч .. что там "не так" и про какие рекомендации от микрочипа речь ..

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Arhat109-2 пишет:

про какие рекомендации от микрочипа речь ..

Опаньки! Ты, что, не читаешь, что тебе пишут? В посте №1 есть ссылка на микрочиповскую документацию, в конце страницы как раз описана ситуация - как заставить компилятор не выбрасывать то, что меняется в ассемблерной вставке. Ты этого не видел? Нужно просто тупо сделать как там (именно asm с параметрами) и всего делов. Я всегда так делаю и проблем не имел никогда.

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

Такое ощущение, что это как раз Вы не читали обсуждения. А между прочим, специально для Вас выложил ссылки на древнее состояние вопроса. И да, там ничего компилятором НЕ выброшено .. там вообще-то ДОБАВЛЕНО то, чего не должно быть в этом месте.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Понятно. Ну, я же говорил, что не понимаю проблемы. Удачи!

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

Ну и ещё:

/**
 * INLINE: 16-bit counter: up to 65535*4 F_CPU for 16Mhz:[0.25 .. 16383.75] mcsec.
 * Короткие задержки по 4 цикла ЦПУ (кратно 250 нсек)
 * !!! Работает неверно !!!
 */
#define delayMicro16(__count)   \
  __asm__ __volatile__ (        \
    "1: sbiw %[__count],1 \n\t" \
       "brne 1b\n\t"            \
       : "=w" (__count)         \
       : "0"  (__count)         \
  )

Приводит к сообщению об ошибке:

"exit status 1
undefined named operand '__count'"

что совершенно понятно, ибо это МАКРОС. Так что в моем ответе пост №2 - изменение макроса с указанием что параметр выходной - единственно верно, ибо он изменяется в нем. Но, при этом, присвоение переменной wait = 4 .. ВТЯГИВАЕТСЯ непосредственно к макросу, хотя в коде оно РАЗНЕСЕНО специально (вытащено из под запрета прерываний).

Ещё раз:

Вот это - изменение порядка следования операций "на ровном месте" (код инвариантен) и приводит к невозможности писать ногодрыги "в теории": точно также туда может быть развернуто, к примеру, инлайном вызов короткой процедуры, что может существенно (только вызов/возврат = 9 тактов) повлиять на время дерганья ногодрыгом пинов, что может оказаться существенным для тайминга. Или наоборот, туда может быть втянуто сколько угодно таких же "присваиваний" ..

и "отключение оптимизации" - не есть "решение", особенно для "библиотечного" кода. Потому что в таком разе к применению библиотеки требуется указывать "отключите оптимизацию".. что есть маразм.

В целом, что называется "привет любителям ногодрыгов". :)

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Архат! Вот эта модификация компилируется нормально и дает именно тот код, который ты ожидаешь.

Дурная привычка ругать инструмент, если не умеешь им пользоваться.

/**
 * Тест накачки высокого обратноходовым импульсным преобразователем
 * В этом варианте показывает до 679 попугаев или 1384в на ВВ-трансфоматоре
 */
extern "C" {

#define pinPWM   5   /* PORTD PORTD5*/
#define pinHV    0   /* analog pin A0! */

#define MAX_ADCS     4
#define MAX_SAMPLES 10
int adcs[MAX_ADCS*MAX_SAMPLES];

#define pwmON()  (PORTD |= 0B00100000)
#define pwmOFF() (PORTD &= 0B11011111)

/**
 * INLINE: 16-bit counter: up to 65535*4 F_CPU for 16Mhz:[0.25 .. 16383.75] mcsec.
 * Короткие задержки по 4 цикла ЦПУ (кратно 250 нсек)
 * !!! Работает неверно !!!
 */
#define delayMicro16(__count) \
  __asm__ __volatile__ (      \
    "1: sbiw %0,1 \n\t"       \
       "brne 1b\n\t"          \
       :"=w" (__count): "0" (__count)       \
  )
/** вроде работает: проверить время! ТОЖЕ НЕ РАБОТАЕТ */
#define delayUS(t)     \
{                      \
  do{;}while(--(t)>0); \
}


#define adcStart() (ADCSRA |= (1<<ADSC))
#define adcWait()  while(ADCSRA&(1<<ADSC))
/**
 * Однократное чтение АЦП 8-бит (младших)
 * Если замер завершен, то сначала запуск следующего.
 * Пока идет замер - работаем дальше
 */
#define adcReadFast(res)   \
{                          \
  adcStart();              \
  adcWait();               \
  (res)=ADC;               \
}

} // extern"C"

void setup()
{
  pinMode(pinPWM, OUTPUT);
  pwmOFF();

  Serial.begin(115200);

  ADCSRA = (1<<ADEN) | (2);               // включаем АЦП и устанавливаем делитель 1(8), 2(4), 3(2), 4(1), 5(0.5), 6(0.25), 7(0.125)
  ADMUX  = (0<<REFS1)|(1<<REFS0)|(pinHV); // 01 - опорное 5в, рабочий вход - 7
  ADCSRB = 0;                             // тут ничего не надо
  DIDR0  = 255;                           // отключаем от OUTPUT все входы АЦП, дабы не разбираться
  adcStart();                             // запуск и сразу
  adcWait();                              // пропуск первого чтения, ибо мусор и долго
}
void loop () __attribute__ ((noinline,optimize ("-fno-reorder-blocks") ));
void loop()
{
  int wait;
  int n, i;
  int *ptr;

  for(n=0; n<MAX_SAMPLES; n++){
    wait = 4;
    ptr = adcs + MAX_ADCS*n;

    cli();
      pwmON();
      delayMicro16(wait);
      pwmOFF();
      adcReadFast(*ptr++); // MAX_ADCS раз!
      adcReadFast(*ptr++);
      adcReadFast(*ptr++);
      adcReadFast(*ptr++);
    sei();
  }

  // вывод попыток накачки на плоттер
  for(n=0; n<MAX_SAMPLES; n++){
    for(i=0; i<5; i++){ Serial.println(n, DEC); }
    for(i=0; i<MAX_ADCS; i++){
      Serial.println( adcs[i+n*MAX_ADCS], DEC);
    }
  }
  delay(2000); // пауза для просмотра результатов
}

============================

кусок после компиляции

void loop () __attribute__ ((noinline,optimize ("-fno-reorder-blocks") ));
void loop()
{
 474:	8f 92       	push	r8
 476:	9f 92       	push	r9
 478:	af 92       	push	r10
 47a:	bf 92       	push	r11
 47c:	cf 92       	push	r12
 47e:	df 92       	push	r13
 480:	ef 92       	push	r14
 482:	ff 92       	push	r15
 484:	0f 93       	push	r16
 486:	1f 93       	push	r17
 488:	cf 93       	push	r28
 48a:	df 93       	push	r29
 48c:	06 e1       	ldi	r16, 0x16	; 22
 48e:	11 e0       	ldi	r17, 0x01	; 1
 490:	f8 01       	movw	r30, r16
    wait = 4;
    ptr = adcs + MAX_ADCS*n;

    cli();
      pwmON();
      delayMicro16(wait);
 492:	24 e0       	ldi	r18, 0x04	; 4
 494:	30 e0       	ldi	r19, 0x00	; 0

  for(n=0; n<MAX_SAMPLES; n++){
    wait = 4;
    ptr = adcs + MAX_ADCS*n;

    cli();
 496:	f8 94       	cli
      pwmON();
 498:	5d 9a       	sbi	0x0b, 5	; 11
      delayMicro16(wait);
 49a:	c9 01       	movw	r24, r18
 49c:	01 97       	sbiw	r24, 0x01	; 1
 49e:	f1 f7       	brne	.-4      	; 0x49c <loop+0x28>
      pwmOFF();
 4a0:	5d 98       	cbi	0x0b, 5	; 11
      adcReadFast(*ptr++); // MAX_ADCS раз!
 4a2:	80 91 7a 00 	lds	r24, 0x007A
 4a6:	80 64       	ori	r24, 0x40	; 64
 4a8:	80 93 7a 00 	sts	0x007A, r24
 4ac:	80 91 7a 00 	lds	r24, 0x007A
 4b0:	86 fd       	sbrc	r24, 6
 4b2:	fc cf       	rjmp	.-8      	; 0x4ac <loop+0x38>
 4b4:	80 91 78 00 	lds	r24, 0x0078
 4b8:	90 91 79 00 	lds	r25, 0x0079
 4bc:	91 83       	std	Z+1, r25	; 0x01
 4be:	80 83       	st	Z, r24
      adcReadFast(*ptr++);
 4c0:	80 91 7a 00 	lds	r24, 0x007A
 4c4:	80 64       	ori	r24, 0x40	; 64
 4c6:	80 93 7a 00 	sts	0x007A, r24
 4ca:	80 91 7a 00 	lds	r24, 0x007A
 4ce:	86 fd       	sbrc	r24, 6
 4d0:	fc cf       	rjmp	.-8      	; 0x4ca <loop+0x56>
 4d2:	80 91 78 00 	lds	r24, 0x0078
 4d6:	90 91 79 00 	lds	r25, 0x0079
 4da:	93 83       	std	Z+3, r25	; 0x03
 4dc:	82 83       	std	Z+2, r24	; 0x02
      adcReadFast(*ptr++);
 4de:	80 91 7a 00 	lds	r24, 0x007A
 4e2:	80 64       	ori	r24, 0x40	; 64
 4e4:	80 93 7a 00 	sts	0x007A, r24
 4e8:	80 91 7a 00 	lds	r24, 0x007A
 4ec:	86 fd       	sbrc	r24, 6
 4ee:	fc cf       	rjmp	.-8      	; 0x4e8 <loop+0x74>
 4f0:	80 91 78 00 	lds	r24, 0x0078
 4f4:	90 91 79 00 	lds	r25, 0x0079
 4f8:	95 83       	std	Z+5, r25	; 0x05
 4fa:	84 83       	std	Z+4, r24	; 0x04
      adcReadFast(*ptr++);
 4fc:	80 91 7a 00 	lds	r24, 0x007A
 500:	80 64       	ori	r24, 0x40	; 64
 502:	80 93 7a 00 	sts	0x007A, r24
 506:	80 91 7a 00 	lds	r24, 0x007A
 50a:	86 fd       	sbrc	r24, 6
 50c:	fc cf       	rjmp	.-8      	; 0x506 <loop+0x92>
 50e:	80 91 78 00 	lds	r24, 0x0078
 512:	90 91 79 00 	lds	r25, 0x0079
 516:	97 83       	std	Z+7, r25	; 0x07
 518:	86 83       	std	Z+6, r24	; 0x06
    sei();
 51a:	78 94       	sei

 

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

wdrakula пишет:

Архат! Вот эта модификация компилируется нормально и дает именно тот код, который ты ожидаешь.

Да, пасибки. Это и есть ответ на вопрос первого поста "есть решения?":

void loop () __attribute__ ((noinline,optimize ("-fno-reorder-blocks") ));

вопрос -закрыт, спасибо.

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

Ху! Меня забанили в гугле или загуглили в бане?

 

{

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

ты это к чему написал? Не компилируется? - Нет, компилируется.

Интересно откуда я это знаю? Скажу за 100 баксов. Кушать то надо что-то. ;) ;) ;) без обид.

Нужно хорошо знать GCC. Про него целые книги написаны. Я же их читал не бесплатно... ;)

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

wdrakula пишет:

ты это к чему написал? ....

 Скажу за 100 баксов. Кушать то надо что-то. ;) ;) ;) без обид.

Написал я чтоб твое жлобство продемонстрироваит ;) Без обид разумеется )))

ПС. По этой фигне и остальным скрытым кормушкам сторонников открытого кода гуглить по "-fno-reorder-blocks". Так вот, в кавычечках.

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

А забавно. Полистал gcc.gnu.org .. нету там такой опции в документации на компилятор. Можно конечно и догадаться .. но, не факт что она поможет во всех случаях .. там ещё вагон + маленькая тележка опций, приводящая как к перестановке блоков кода, так и к свертке/развертке и даже копипасту.. так что,

получаем в очередной раз вариант "мне повезло, тут пашет так".

Не могу признать такой подход "решением в целом" .. частичное - да. И как вывод модифицируем: "Каждый ногодрыг, критичный к исполнению кода, должен проверяться на доп. указивки компилятору в каждом конктретном случае". :)

И написание критических участков кода (а это 80% управления) надо делать исключительно asm() вставками целиком. То есть "ну его нафиг этот си и тем более си++ для микроконтроллеров" :)

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Нет, Архат, получается не так.

Может ли  домохозяйка (с Ардуино напервес) написать корректный "ногодрыг" с таймингами порядка 1-2 мкс и меньше?

Нет не может. С дефолтными для ИДЕ настройками компилятора - не может совсем.

Например цикл с 4 NOP-ами оптимизатор просто развернет в 4 NOP-а и все твои расчеты времени пойдудт по 3.14зде.

Для этого есть опции настройки оптимизации, они ВСЕ - на самом деле - есть в полной документиции на GCC. У меня это файл gcc.pdf, аж на 1000 страниц. И да, его нужно знать, пусть не наизусть, но уметь пользоваться как справочником. Я, конечно, не помнил точно опцию, но помнил где ее искать, и да, можно было убрать оптимизацию, выключить только блок-реордер было чистым позерством, признаю.

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

Где-то в детстве в тебе поселился комплекс, что "стыдно" не знать чего-либо. Так вот это полная херня! Никто не всеведущ, ну кроме сам понимаешь кого ;) ;) ;).

--------------

ЗЫ: gcc.pdf гуглится вот прямо так, по имени файла, на gnu.org ведет первая же ссылка в яндексе, и в этом файле есть все про эту опцию, в том числе.

Похоже и тебя и Логика и правда в гугле забанили. ;)

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

Пилять .. старею, недосмотрел, виноват .. :)

Твоя опция на самом деле ничего не изменила!

"код после компиляции":

    wait = 4;
    ptr = adcs + MAX_ADCS*n;

    cli();
      pwmON();
      delayMicro16(wait);
 492:	24 e0       	ldi	r18, 0x04	; 4
 494:	30 e0       	ldi	r19, 0x00	; 0

  for(n=0; n<MAX_SAMPLES; n++){
    wait = 4;
    ptr = adcs + MAX_ADCS*n;

    cli();
 496:	f8 94       	cli
      pwmON();
 498:	5d 9a       	sbi	0x0b, 5	; 11
      delayMicro16(wait);
 49a:	c9 01       	movw	r24, r18
 49c:	01 97       	sbiw	r24, 0x01	; 1
 49e:	f1 f7       	brne	.-4      	; 0x49c <loop+0x28>
      pwmOFF();
 4a0:	5d 98       	cbi	0x0b, 5	; 11

 Строка 19, 49а: r24 = r18, а ведь wait=4 вынесено ДО cli() и pwmOn() .. промеж pwmOn() и pwmOff() не должно быть НИЧЕГО, кроме цикла паузы .. иначе она искажается на те самые 62.5нсек для 16Мгц.. как було так и осталось. :(

Ну и это твой макрос:

#define delayMicro16(__count) \
  __asm__ __volatile__ (      \
    "1: sbiw %0,1 \n\t"       \
       "brne 1b\n\t"          \
       :"=w" (__count): "0" (__count)       \
  )

А это мой исправленный после коммента от Евгения:

#define delayMicro16(__count) \
  __asm__ __volatile__ (      \
    "1: sbiw %0,1 \n\t"       \
       "brne 1b\n\t"          \
       : "=w" (__count)       \
       : "0"  (__count)       \
)

Что называется "найди 2 отличия" .. зато гонору .. "ты научишся" .. думаю ты тоже научишся хотя бы читать написанное .. (тоже не упрек, ежели вчё) :)

gcc.gnu.org - ваще-то веб-документ как понимаю и есть. Так вот в нем - данная опция - отсутствует. Присутствует обратная, по которой и можно догадаться. Может в печатном ПДФ есть и это .. не знаю, у меня его нет. Пользуюсь этим веб-вариантом, пока что.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Дурдом :)

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

Зато весело! Имеем недокументированую опцию  к которой даже претензию что она неработает предявить нельзя. Ибо несчего взять что она должна делать именно то что нужно. И вобще несчего взять что она вобще чего делает. И даже нельзя твердо утверждать что она существует, а не ошибочное отсутствие сообщения о ошибке при данном сочитании букв в коде. Писец усиливает что, это не  левое творение обезумевшего форумчанина, а сам gcc - мэйнстрим IT. Сегодня вечером забухаю с горя.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Logik пишет:

Зато весело!

Ты правда не нашел документацию? -f<any option> - описано, -fno<any option> отключение  опции. Приятно дурака из себя строить? Не знал за тобой такого ранее... Умеешь удивить!

-freorder-blocks
Reorder basic blocks in the compiled function in order to reduce number of
taken branches and improve code locality.
Enabled at levels ‘-O’, ‘-O2’, ‘-O3’, ‘-Os’.

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Arhat109-2 пишет:

Пилять .. старею, недосмотрел, виноват .. :)

Извини, Архат. Планировал топать ногами, показывать на ошибки программирования... но подумал, что на твой поток сознания просто нет ответов. Это как баба в истерике: ей не нужно "по полочкам" раскладывать решение ситуации, её нужно просто обнять и пожалеть... сорри - считай, что я тебя обнял. ;) ;) ;)

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

Бабу тут ты из себя изображаешь, облажавшись.

Не надо меня жалеть, мне надо чтобы було вот так:

 48c:	06 e1       	ldi	r16, 0x16	; 22       //     ptr = adcs + MAX_ADCS*n;
 48e:	11 e0       	ldi	r17, 0x01	; 1 // пофиг, пусть будет вынесено
 490:	f8 01       	movw	r30, r16

  for(n=0; n<MAX_SAMPLES; n++){
 492:	24 e0       	ldi	r18, 0x04	; 4 //     wait = 4;
 494:	30 e0       	ldi	r19, 0x00	; 0 // пофиг, пусть даже через временный регистр
 49a:	c9 01       	movw	r24, r18        ;   // НО ДО cli() и задержки!!!

 496:	f8 94       	cli
 498:	5d 9a           sbi	0x0b, 5	        ; 11 //       pwmON();
 49c:	01 97       	sbiw	r24, 0x01	; 1  //       delayMicro16(wait);
 49e:	f1 f7       	brne	.-4      	; 0x49c <loop+0x28>
 4a0:	5d 98           cbi	0x0b, 5	        ; 11 //       pwmOFF();

 4a2:	80 91 7a 00 	lds	r24, 0x007A     ; //       adcReadFast(*ptr++); // MAX_ADCS раз!
 4a6:	80 64       	ori	r24, 0x40	; 64
 4a8:	80 93 7a 00 	sts	0x007A, r24
 4ac:	80 91 7a 00 	lds	r24, 0x007A
 4b0:	86 fd       	sbrc	r24, 6
 4b2:	fc cf       	rjmp	.-8      	; 0x4ac <loop+0x38>
 4b4:	80 91 78 00 	lds	r24, 0x0078
 4b8:	90 91 79 00 	lds	r25, 0x0079
 4bc:	91 83       	std	Z+1, r25	; 0x01
 4be:	80 83       	st	Z, r24
...
 51a:	78 94       	sei

строки 496..4а0 содержат РОВНО 4 оператора: задрал ногу, сделал дело, опустил. Этот блок кода должен получиться именно таким, как он написан на Си, а не таким как его решил сделать оптимизатор!

Прикол в том, что преобразования оптимизатора - ИНВАРИАНТНЫ или УХУДШАЮТ качество этого участка кода, но никак не улучшают.. (на асме, я весь этот участок оптимизирую лучше).

P.S. Для тех, кто в Таньке, поясняю ещё раз: компилятору явно указано присвоение делать ДО закрытия прерываний и работы цикла. Втягивание присваивания (пусть даже во временный регистр) приводит к нарушению тайминга работы ногодрыга. В данном конкрретном случае - критическому. Минимальное исполнение 2-х команд = 4 такта или 250нсек. Внесение команды внутрь увеличивает длительность исполнения на 62.5нсек или на ЧЕТВЕРТЬ. Это - много.

А если макрос задержки (мне просто надо было кратно 250нсек) заменить на delay_loop1() где задержка .. байтовая, то искажение времени задержки составит уже ТРЕТЬ от заданной со всеми последствиями.

Есть решение, кроме рисования полной asm() вставки? Пока не вижу и это - принципиально: никакой ногодрыг НЕ гарантирует точное преобразование своего кода, а стало быть или должен писаться только асм вставками или ногодрыг не может быть писан критичным ко времени исполнения +- лапоть. Ибо сколько в него захочет втянуть оптимизатор - вилами по воде.

P.P.S. Редкий случай, когда Логик - прав. Ибо в документации есть прямые описания для "обратных опций" -fno-..., но .. КРОМЕ ЭТОЙ. То есть опция - недокументированная, но как и писал "догадаться можно". К сожалению не работает точно также.

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

Logik пишет:
Зато весело! Имеем недокументированую опцию  к которой даже претензию что она неработает предявить нельзя ...

Она может и работает. Там претензия не столько к перестановке блоков в реальности, сколько к тупости оптимизатора, который уже какой год всё лучшеет и лучшеет, но как не умел переназначать оптимально переменные на регистровое поле так и не умеет.

Все там понятно что происходит: сказали не баловаться с порядком, ну и ладушки. Завел ЕЩЁ ОДНУ (!) регистровую переменную, запихал в неё значение, а когда надо считать макрос задержки ВНЕЗАПНО использовал типовой регистровый блок ВРЕМЕННОГО назначения .. только он оказался не присвоен к моменту .. ну так не беда, ща переприсвоим.

.. и пофиг, что итоговая оптимизация ушла в откровенный минус .. сами же сказали "не балуй" ..

Аналогичное поведение можно увидеть и на присваивании значения указателю, что оказалось вынесенным (у меня) далеко за цикл, где он используется .. там временная перетасовка промеж регистров аж .. дважды. И пофиг, во многих иных местах, что такие перетасовки увеличивают код (и порой критично - см. обработчик таймера 0 - время ардуино). Ну не умеет оптимизатор помнить в какой регистр чего он нафигачил и что прямо оттуда и можно использовать. А ежели низзя (набор команд не позволяет), то можно фигачить сразу туда где оно будет использовано .. не, мы лучше ещё регистр-пару задействуем .. их многа.

Писал это уже им в службу поддержки .. в ответ "ага, мы понимаем, классно .. сделаем .. " 2 года - воз похоже и ныне там жеж.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Понеслась душа в рай :)))

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

Столько постов и только один попробовал помочь, да и то облажался.. по вопросу - добавить нечего? :)

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Облажался ты, когда собрался константу изменять. А помочь тебе невозможно, т.к. ты сам не понимаешь чего хочешь.

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

Писец. Ты похоже вообще не читатель. Написано и ПОКАЗАНО в каких строчках результата проблема у оптимизатора. Когда в следующий раз будешь поражаться с какого фига твой ногодрыг пашет не так - перечитай топик.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Arhat109-2 пишет:

Когда в следующий раз будешь поражаться с какого фига твой ногодрыг пашет не так

Мои ногодрыги пашут так! Наверное, потому, что я константы не модифицирую

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

Архат! Еще раз.

Вот код, он из твоего, комментарии и лишее выкинул для читаемости. Не сворачивал, так как маленький.

extern "C" {

#define pinPWM   5   /* PORTD PORTD5*/
#define pinHV    0   /* analog pin A0! */

#define MAX_ADCS     4
#define MAX_SAMPLES 10
int adcs[MAX_ADCS*MAX_SAMPLES];

#define pwmON()  (PORTD |= 0B00100000)
#define pwmOFF() (PORTD &= 0B11011111)


#define delayMicro16a(__count) \
  __asm__ __volatile__ (      \
    "1: sbiw %0,1   \n\t"       \
       "brne 1b     \n\t"          \
       : : "w" (__count)       \
  )


inline volatile __attribute__ (( gnu_inline, optimize ("--param loop-unroll-jam-size=7") )) void delayMicro16c(uint8_t __count) 
{do {_NOP();} while (--__count);} 
  

} // extern"C"

void setup()
{
  pinMode(pinPWM, OUTPUT);
  pwmOFF();
}
void loop () __attribute__ ((noinline,optimize ("-fno-reorder-blocks") ));
void loop()
{
  int wait;
  int n, i;
  int *ptr;

  for(n=0; n<MAX_SAMPLES; n++){
    wait = 6;
    //ptr = adcs + MAX_ADCS*n;

    cli();

    pwmON();
    delayMicro16c(2);
    pwmOFF();
    delayMicro16c(4);
    
    pwmON();
    delayMicro16a(1);
    pwmOFF();  
    delayMicro16c(6);
    
    pwmON();
    delayMicro16a(2);
    pwmOFF();  
    delayMicro16c(6);
    
    pwmON();
    delayMicro16a(3);
    pwmOFF();  
    delayMicro16c(6);
    
    pwmON();
    delayMicro16a(4);
    pwmOFF();  
    delayMicro16c(6);
    
    pwmON();
    delayMicro16a(5);
    pwmOFF();  
    delayMicro16c(6);
    
    pwmON();
    delayMicro16a(7);
    pwmOFF();  
    delayMicro16c(6);
    
    sei();
    delay(1);
  }
}

Все видно: delayMicro16a  и c - асм и С варианты, соответственно.

С вариант разворачивается в НОП без цикла, если их меньше 7 штук.

Этим и пользуемся.

С вариант, конечно с константой! Присвоение регистру, которое тебе не нравится не "глупость" оптимизатора, а следствие того, что в АСМ вставе есть и ВХОДНЫЕ и ВЫХОДНЫЕ регистры. Убери выход и все станет красиво. Запрет введения loop( ) инлайном, плюс запрет реордеринга - дают правильное расположение всех инициализаций. Как правильно тут заметели - компилятор НЕ МОЖЕТ знать, что для тебя важны cli/sti скобки.

Нужно разговаривать с компилятором на ЕГО языке, а не на "Ну ты чо, не понял?!"

я написал тебе пример использования ОБОИХ вариантов, с учетом особенностей АВР.

Вот картинка с логаналайзера:

И вот тайминги с него же. Третья колонка  - это я для тебя в ЛибреКалке... ну для тебя понятнее - в Екселе - длительности посчитал.

Снимал от 575 до 590 мкс.

Time[s] Channel 5  
0,000575 0  
0,0005796667 1 0,25
0,0005799167 0 0,375
0,0005802917 1 0,375
0,0005806667 0 0,5
0,0005811667 1 0,625
0,0005817917 0 0,5
0,0005822917 1 0,875
0,0005831667 0 0,5
0,0005836667 1 1,125
0,0005847917 0 0,5
0,0005852917 1 1,375
0,0005866667 0 0,5
0,0005871667 1 1,875
0,0005890417 0  

То есть видишь: два НОПа дают 250 нс импульс, потому что 125 и еще 125 на сами команды записи в порт.

4 Нопа (второй 0) - 250+125, как мы и ждали.

6 нопов - это все паузы - дают по 500нс.

далее в деле твоя ассемблерная задержка, Она дает от 375 и далее с шагом в 250, что мы от нее и ждали.

=========================

Ну так в чем сложность писать "ногодрыг"?

wdrakula
wdrakula аватар
Offline
Зарегистрирован: 15.03.2016

И да. Я понимаю, что у тебя в Новосибе вечер уже, так что ты, вернее всего, только завтра разберешься.

Но ЗАРАНЕЕ пишу, что если тебе нужна регулировка длительности импульса в рантайме, то писать нужно немного не так.

Тоже все спокойно делается с точностью до 62,5 нс и начиная от 125 нс, но писать сложнее, там выравнивать нужно будет по вариантам.

Код не будет сильно изящьным, но можно спокойно обойтись только С, без Асма.

Этот вариант написать? - Я понимаю, что ты из гордости откажешься, а зря. Я просто уже ходил по этим граблям и первый раз бился над неправильными таймингами долго. Я же и написал тебе, что правильный ногодрыг - это не просто и с наскока не решается. А ты готов всех "шашками порубать" и компилятор проклясть. Нормально?