Это нормально, что LTO нарушает код и как этого избежать? - PullRequest
0 голосов
/ 18 июня 2020

Я разрабатываю программу генерации звука для Attiny85 u C с использованием Arduino IDE и AttinyCore и наткнулся на проблему, с которой никогда раньше не сталкивался.

Инициализация таймеров включает два таймера, Timer0 и Timer1. Первый используется для вывода ШИМ, второй - для временного прерывания по частоте дискретизации. Короче говоря, это дает мне два байта, OCR0A и OCR0B, изменяя которые, я могу выводить звук, и прерывание, которое вызывается прибл. 15 кГц. Все делается в одном файле: setup () инициализирует таймеры, l oop () отображает звук в sample1 и sample2, а ISR выводит звук на OCR0A и OCR0B.

#define F_CPU 8000000UL
#include "Arduino.h"

#include <avr/interrupt.h>
#include <stdlib.h>
#include <avr/io.h>


uint8_t sample1 = 0, sample2 = 0;
uint16_t phase1 = 0, phase2 = 0;


void setup() {
  digitalWrite(5, HIGH); // Immedeately turn on this pull-up resistor for the reset pin
  pinMode(0, OUTPUT); // Two outputs
  pinMode(1, OUTPUT);
  cli();
  // Turn on PLL and wait while it stabilizes
  bitSet(PLLCSR, PLLE);
  delayMicroseconds(100);
  while(!bitRead(PLLCSR, PLOCK));
  bitSet(PLLCSR, PCKE);
  bitSet(PLLCSR, LSM);  //low speed mode, PLL @ 32MHz.

  //Set up the TIM0 timer
  TCCR0A = bit(WGM00) | bit(WGM01); //Mode: 011, fast PWM. IMHO regular CTC PWM is superior
  TCCR0B = 0; //WGM02 is zero
  TCCR0A |= bit(COM0A1); // Channel A: Non-inverting PWM mode (10)
  TCCR0A |= bit(COM0B1); // Channel B: Non-inverting PWM mode (10)
  bitSet(TCCR0B, CS00); // Timer frequency: CLKio/1 = 8MHz

  //Set up the TIM1 timer
  TCCR1 = 0; TCNT1 = 0; // Stop and clear the timer
  GTCCR = bit(PSR1); //Reset the timer
  OCR1A = 255; OCR1C = 255;
  TIMSK = bit(OCIE1A); // Interrupt enable
  TCCR1 = bit(CTC1) | bit(CS12);  //Clock: PLL Clock / 8 = 32MHz / 8 = 4 MHz = CPU Clock / 2
                                  //Final Timer1 config: reset on OCR1C compare
                                  //Interrupt on OCR1A compare
                                  //Frequency: 4MHz / 256 = 15625 Hz
  sei();
}

ISR(TIMER1_COMPA_vect)
{
  OCR0A = sample1;
  OCR0B = sample2;

  //Internal oscillators 1, 2
  phase1 += 3*255;
  phase2 += 3*400;
}


void loop() {
  sample1 = phase1 >> 8;
  sample2 = phase2 >> 8;
}

Making phase1, phase2, sample1, sample2 volatile помогает, но мой вопрос - почему?

Проблема в том, что при использовании LTO, -Os этот код не работает. Это можно исправить, либо переместив вычисление в прерывание, либо назначив результат вычисления прямо байтам OCR0A и OCR0B в функции Synthesis (), однако это не то поведение, которое я желаю, поскольку я хочу, чтобы прерывание было как можно короче.

Но, либо с -O0 (оптимизация полностью отключена), либо с отключенным LTO, этот код действительно работает, но размер fla sh значительно увеличился (2.3kb с -O0, 700B с -Os, LTO disable, 504b с включенным LTO).

Я мог бы отключить LTO, но мне это кажется грязным хаком. Мои вопросы:

  1. Это грязный хакер для отключения LTO?

  2. Нормально ли оптимизатор так резко менять поведение? Почему он работает без LTO?

  3. Почему мне нужно сделать эти переменные изменчивыми и как определить, нужно ли мне делать переменные изменчивыми (кроме простого метода проб и ошибок или просто сделать их все энергозависимыми?)

  4. Что еще я могу сделать, кроме изменения настроек оптимизатора и создания чего-то нестабильного?

  5. Может быть, это кодировка проблема стиля?

  6. Есть какие-нибудь советы по написанию кода, который оптимизатор не нарушает?

...