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

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

Ох! Не смог удержаться!   ;) ;) ;)

=====НИЖЕ ЧИСТЫЙ ТРОЛЛИНГ================

На STM32, даже на f10x простой  и сложный и с блэкждеком и шлюхами - ногодрыг делается на аппаратном уровне. Просто задержки скидываешь в массив и грузишь массив по ДМА в таймер, Точность - сам понимаешь - 1/72 мкс= 14 нс. Гы! Сейчас "ssss" как выпрыгнет! ;) ;) ;) ;) и покажет вам Кузькину мать!

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

Приношу извинения за троллинг ув. "All"!

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

Проклятье! Он вызвал этого вонючего демона :( Тему можна удалять.

ПС. "нужна регулировка длительности импульса" - для накачки высоковольтного конденсатора совсем не обязательно, вполне частотно-импульсной модуляции хватает. Хотя пути неисповедимы ))

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

wdrakula пишет:
Но ЗАРАНЕЕ пишу, что если тебе нужна регулировка длительности импульса в рантайме, то писать нужно немного не так. Тоже все спокойно делается с точностью до 62,5 нс и начиная от 125 нс, но писать сложнее, там выравнивать нужно будет по вариантам. Код не будет сильно изящьным, но можно спокойно обойтись только С, без Асма.

Этот вариант написать?

Канечна! Это и будет ответом на вопрос топика: "есть решение?" :)

Ещё раз, требуется ногодрыг с рантайм регулировкой (через переменную) длительности импульса кратно 250нсек, начиная от 250нсек. То есть: 250, 500, 750 .. а не 312.5, 562.5 и т.д.

Скажем так:

#define MAX_ADCS 2
#define MAX_SAMPLES 10

int   adcs[MAX_ADCS*MAX_SAMPLES];
int * adcPtr;

for(int wait=1; wait<100; wait++){
  adcPtr = adcs;
  for(int i=0; i<MAX_SAMPLES; i++){
    cli();
    pwmOn();
    delay250us(wait); // функция задержки кратно 250мксек как чтобы строго 250*wait?
    pwmOf();
    adcReadFast( adcPtr++ );
    adcReadFast( adcPtr++ );
    sei();
  }
  // тут скажем "вывод на плоттер" того что получилось в adcs ..
}

Примерчики с константами у меня и работали всю жизнь .. загляни ещё в старый arhat.h оно там так и прописано.

 

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

Logik пишет:

Проклятье! Он вызвал этого вонючего демона :( Тему можна удалять.

ПС. "нужна регулировка длительности импульса" - для накачки высоковольтного конденсатора совсем не обязательно, вполне частотно-импульсной модуляции хватает. Хотя пути неисповедимы ))

Уже и сделано через таймер и прерывания .. вопрос уже чисто академический про контролируемый ногодрыг.

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

вот не зря я забивал на С и компилятор, в машинных кодах всё проще )))

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

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

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

DetSimen пишет:

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

А так нещитова! Вас ваще дисквалифицировать за доппинг надоть!

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

Так нужен батл! Дед & gcc! На большем проекте. В трех номинациях, скорость компиляции, обем кода и скорость исполнения. Применение стимуляторов с обеих сторон приветствуется.

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

Arhat109-2 пишет:

Уже и сделано через таймер и прерывания .. вопрос уже чисто академический про контролируемый ногодрыг.

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

void loop()
{
 int V=analogRead(A1);
 int t=micros();
...... 
   if(t-PulseT>700)
   {
    if(V<VALUE_ADC(Vhigh_))
    {
      PulseT=t;
        digitalWrite(PIN_DRV, HIGH);_delay_us(20);digitalWrite(PIN_DRV, LOW);
    }
   }

если от прошлого импульса прошло более 700мксек, а напряжение ниже порога - импульс 20мксек. Даже прерывания не запретил, всех делов))) Лоп предельно быстрый, других делеев кроме этого нет. Статистика напряжения на датчике -  Vmin=337V; Vmax=391V; Vs=353V; Вполне нормас. А импульс понятно по прерыванию,  на таймере 1 время нечувствительности после импульса.

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

Логик! 20мксек - это задержка в моих wait = 80 (по 250нсек). Меня интересовало поведение при ширине импульса накачки до 1-2мксек, ваще-то с целью понять можно ли регулировать напряжение на выходе шириной импульса накачки, ибо не известно "скока вешать в граммах" для самодельной трубки Гейгера из шприца. То-ли 300-400 вольт толи под пару киловольт, как показывает оценка. :)

И тут как раз "чем мельче шаг" - тем толще партизаны. С таймером вполне получается шагать по 62.5нсек.

P.S. В общем, это я - тормоз. Ответ №1 и пост №2 содержат решение с самого начала. :)

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();

6f8 - перенос из регистра в регистр, ибо переменная = 1 такт. 6fa - декремент = 2 такта, 6fc - сравнение .. и вот тут я подзабыл: переход выполняется 2 такта, если "НЕ РАВНО НУЛЮ" (взад, повтор цикла) и ОДИН ТАКТ если "равно нулю" .. итого имеем 4 такта при мин. задержке "1". Соответственно, все тут как надо: 250, 500, ..

:)

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

прости, брат, но ты это чисто аналитически считаешь, без логаналайзера? отползаю....

сорри, я что обещал утром... моим, московским, т.е. часв в 12 по мск. завтра напишу.... щаз я уже бухой

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

Arhat109-2 пишет:

Логик! 20мксек - это задержка в моих wait = 80 (по 250нсек). Меня интересовало поведение при ширине импульса накачки до 1-2мксек

Пробовал я на 1-2мксек работать. С заводским датчиком. Не получалось выйти на нужные напряжения. Но я больше не о том говорю. В данном случае ШИМ не нужен, ИМХО. Самое оно - фиксированая, экспериментально найденая длительность импульса, а стабилизация напряжения  за счет изменения паузы между импульсами. То, что на таймере шаг 62.5нсек получить можно никто наверно и не сомневался. А для самопального датчика думаю обязательно надо "плато" искать. От него собственно все и пляшет. Минимальное напряжение задает порог при проседании до которого пора делать импульс, а длительность импульса определяется разностю максимального и минимального. Если длительность делать больше, то превысим максимальное. По таким соображениям и цифра 20мксек вышла. 

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

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

Знать бы в каком диапазоне напряжений его искать, это "плато"..

P.S. По теме:

Как итого, перетасовки кода имеют место быть, управляются опциями они достаточно слабо и каждый ногодрыг надо смотреть в ассемблере чего туда натыкает оптимизатор. Или критические по исполнению последовательности оформлять asm() вставками с атрибутом volatile. Или устанавливать атрибутно опции оптимизатору на конкретно эту функцию, но тогда код придется заворачивать в функцию, ибо установка атрибутики на блок кода {..} не нашел "как".

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

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

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

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

ФИгню, как обычно написал - на блок кода постевить опции нельзя, #pragma тоже работает на единицы компиляции.

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

То, что вчера обещал, обкатал на логаналайзере.

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 void __attribute__ ((gnu_inline)) delayMicro16a(byte __count) 

{  __asm__ __volatile__ (      \
      "1: subi %0,1   \n\t"    \
         "nop         \n\t"    \
         "brne 1b     \n\t"    \
         "nop         \n\t"    \
         "nop         \n\t"    \
       : "=d" (__count): "0" (__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 () __attribute__ ((noinline,optimize ("-O1") ));
void loop()
{
  byte wait;
  int n, i;
  int *ptr;
  cli();
  //ВСЕ ПОДБОРЫ - КОНЕЧНО НА ЛОГАНАЛАЙЗЕРЕ!!!!!!!! Без него и без осцила делать в цифровой электронике нечего.
    pwmON();
    //первый импульс 250нс
    delayMicro16c(2);
    pwmOFF();  
    //подбор паузы 625 нс для красоты.
    delayMicro16a(1);
    delayMicro16c(1);
  //далее импульсы как ты хотел от 500 и далее с шагом в 250нс, пауза 625 нс
  for(wait=1; wait<50; wait++){
    
    pwmON();
    delayMicro16a(wait);
    pwmOFF();  
    delayMicro16c(4);
  }
  sei();
delayMicro16a(200);
}

Тайминги:

Time[s]	 Channel 5	
0,0001	0	
0,0001081667	1	250
0,0001084167	0	625
0,0001090417	1	500
0,0001095417	0	625
0,0001101667	1	750
0,0001109167	0	625
0,0001115417	1	1000
0,0001125417	0	625
0,0001131667	1	1250
0,0001144167	0	625
0,0001150417	1	1500
0,0001165417	0	625
0,0001171667	1	1750
0,0001189167	0	625
0,0001195417	1	2000
0,0001215417	0	625
0,0001221667	1	2250
0,0001244167	0	625
0,0001250417	1	2500
0,0001275417	0	625
0,0001281667	1	2750
0,0001309167	0	625
0,0001315417	1	3000
0,0001345417	0	625
0,0001351667	1	3250
0,0001384167	0	625
0,0001390417	1	3500
0,0001425417	0	625
0,0001431667	1	3750
0,0001469167	0	625
0,0001475417	1	4000
0,0001515417	0	625
0,0001521667	1	4250
0,0001564167	0	625
0,0001570417	1	4500
0,0001615417	0	625
0,0001621667	1	4750
0,0001669167	0	625
0,0001675417	1	5000
0,0001725417	0	625
0,0001731667	1	5250
0,0001784167	0	625
0,0001790417	1	5500
0,0001845417	0	625
0,0001851667	1	5750
0,0001909167	0	625
0,0001915417	1	6000
0,0001975417	0	625
0,0001981667	1	6250
0,0002044167	0	625
0,0002050417	1	6500
0,0002115417	0	625
0,0002121667	1	6750
0,0002189167	0	625
0,0002195417	1	7000
0,0002265417	0	625
0,0002271667	1	7250
0,0002344167	0	625
0,0002350417	1	7500
0,0002425417	0	625
0,0002431667	1	7750
0,0002509167	0	625
0,0002515417	1	8000
0,0002595417	0	625
0,0002601667	1	8250
0,0002684167	0	625
0,0002690417	1	8500
0,0002775417	0	625
0,0002781667	1	8750
0,0002869167	0	625
0,0002875417	1	9000
0,0002965417	0	625
0,0002971667	1	9250
0,0003064167	0	625
0,0003070417	1	9500
0,0003165417	0	625
0,0003171667	1	9750,0000000001
0,0003269167	0	625
0,0003275417	1	10000
0,0003375417	0	625
0,0003381667	1	10250
0,0003484167	0	625
0,0003490417	1	10500
0,0003595417	0	625
0,0003601667	1	10750
0,0003709167	0	625
0,0003715417	1	11000,0000000001
0,0003825417	0	625
0,0003831667	1	11250
0,0003944167	0	625
0,0003950417	1	11500
0,0004065417	0	625
0,0004071667	1	11750
0,0004189167	0	625
0,0004195417	1	12000
0,0004315417	0	625
0,0004321667	1	12250
0,0004444167	0	625
0,0004450417	1	12500
0,0004575417	0	

 

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

Спасибо конечно за труд, но Вы решили ИНУЮ задачу .. :) Решение того что было надо содержится в посте №2, на самом деле работает как надо.

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

Arhat109-2 пишет:

Спасибо конечно за труд, но Вы решили ИНУЮ задачу .. :) Решение того что было надо содержится в посте №2, на самом деле работает как надо.

а тайминги можно показать?

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

Arhat109-2 пишет:

Ещё раз, требуется ногодрыг с рантайм регулировкой (через переменную) длительности импульса кратно 250нсек, начиная от 250нсек. То есть: 250, 500, 750 .. а не 312.5, 562.5 и т.д.

Arhat109-2 пишет:

Спасибо конечно за труд, но Вы решили ИНУЮ задачу .. :) Решение того что было надо содержится в посте №2, на самом деле работает как надо.

я стесняюсь спросить, как в твоей башне склеиваются эти две цитаты? Принимая во внимание тайминги?

Хотя твое дело... Компилятор все равно во всем виновотым окажется... ну и я , до кучи.

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

ua6em пишет:
а тайминги можно показать?

Ну только вот так:

/**
 * Тест накачки высокого обратноходовым импульсным преобразователем
 * В этом варианте показывает до 679 попугаев или 1384в на ВВ-трансфоматоре (№1)
 * Делитель 100мОм + 240кОм: = 100.24 / 0,24 * (5в/1024) = 2,04 вольта на 1 попугай АЦП.
 * при импульсе длительностью от 8.25 до 10.25 мксек.
 *
 * АЦП НАНО:                      4Мгц - 5бит, 2Мгц - 6-7бит, 1Мгц - 8бит.
 * АЦП МУРК-2560: 8Мгц - 5..6бит, 4Мгц - 6..7бит, 2Мгц - 7..8бит, 1Мгц - >8бит.
 * все кривые младшие значения результата обнулены! (ошибку можно располовинить)
 * Время замера строго 13.5тактов АЦП. Не забывать про последний "запуск" в adcReadFast()!
 * МУРК-2560: AREF - 100нф, AVCC - (20мкГн + 2х4.7мкф)
 */
#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 adcStart() (ADCSRA |= (1<<ADSC))
#define adcWait()  while(ADCSRA&(1<<ADSC))
/**
 * Однократное чтение АЦП 8-бит (младших)
 * Если замер завершен, то сначала запуск следующего. Пока идет замер - работаем дальше
 */
#define adcReadFast(res)   \
{                          \
  adcWait();               \
  adcStart();              \
  (res)=ADC;               \
}

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в, рабочий вход - pinHV
  ADCSRB = 0;                             // тут ничего не надо
  DIDR0  = 255;                           // отключаем от OUTPUT все входы АЦП, дабы не разбираться
  adcStart();                             // запуск и сразу
  adcWait();                              // пропуск первого чтения, ибо мусор и долго
}

void loop()
{
  int *ptr;

  for(int wait=1; wait<100; wait++){
    ptr = adcs;
    for(uint8_t n=0; n<MAX_SAMPLES; n++){
      int _w = wait;
      cli();
        pwmON();
        delayMicro16(_w);
        pwmOFF();
        adcStart();
        adcReadFast(*ptr++); // MAX_ADCS раз!
        adcReadFast(*ptr++);
        adcReadFast(*ptr++);
        adcReadFast(*ptr++);
        adcWait();           // пропуск последнего запуска АЦП
      sei();
    }
    // вывод попыток накачки на плоттер
    for(uint8_t n=0; n<MAX_SAMPLES; n++){
      for(uint8_t i=0; i<5; i++){ Serial.println(n, DEC); }
      for(uint8_t i=0; i<MAX_ADCS; i++){
        Serial.println( adcs[i+n*MAX_ADCS], DEC);
      }
    }
  }
  delay(2000); // пауза для просмотра результатов
}

Это код теста, как оно и предполагалось, переменная wait - есть счетчик цикла по размеру задержки. Вывод на плоттер по 10 импульсов накачки на посмотреть докудова докачивает измерялкой на АЦП. Измерялка сделана как можно шустрее, задержка промеж импульсов накачки регулировалась как количеством чтений АЦП, так и изменением частоты оцифровки. Тут стоит макс. в 4Мгц, поскольку гонялось на НАНО, а она 8Мгц не тянет.

Результат компиляции блока ногодрыга на ИДЕ 1.6.9.:

 121 0012 E0E0      		ldi r30,lo8(adcs) ; *2* ptr = adcs;
 122 0014 F0E0      		ldi r31,hi8(adcs) ; *2*
 124               	.L23:                     ; *** for(uint8_t n=0; n<MAX_SAMPLES; n++){
 132 0016 F894      		cli
 136 0018 5D9A      		sbi 0xb,5         ; *1*   pwmON();
 138 001a CE01      		movw r24,r28      ; *1*   // установка параметра __count
 141 001c 0197           1: sbiw r24,1        ; *2*   delayMicro16(_w);
 142 001e 01F4      		brne 1b           ; *2/1* -- вот тут я и "подзабыл" :)
 148 0020 5D98      		cbi 0xb,5         ; *1*   pwmOFF();
 

В звездочках - тайминг операций согласно AVR-ASM. Итого, цикл задержки: 1+2+4*(wait-1)+1 .. строго кратно 250нсек, начиная с самой первой задержки.

Забавно то, что в коде необходимость оператора int _w = wait; обязательна, но в итоге оно оказывается редуцировано оптимизатором. Операция строки 138 - неустранима, ибо параметр макроса в асм-вставке указан как "возвращаемая переменная".

То, что писал про "константу" Евгению: это - тестовый код, в котором длительность wait - подбирается под оптимум. В окончательном коде ожидалось применение delayMicro16(4), с указанием конкретно подобранной константы. Однако ТАК с этим вариантом макроса - не получится, будет сгенерена ошибка: "Lvalue required", что верно.

Как видно, никаких специальных опций компилятору указывать не пришлось вовсе. :)