首页 > 解决方案 > LTO 破坏代码是否正常,如何避免?

问题描述

我正在使用 Arduino IDE 和AttinyCore为 Attiny85 uC 开发一个声音生成程序,并偶然发现了一个我以前从未遇到过的问题。

定时器初始化打开两个定时器,Timer0 和 Timer1。第一个用于 PWM 输出,第二个用于定时采样率中断。简而言之,这给了我两个字节,OCR0A 和 OCR0B,通过改变我可以输出声音的方式,以及一个在大约 1 处调用的中断。15kHz。一切都在一个文件中完成:setup() 初始化计时器,loop() 将声音渲染到 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;
}

使 phase1, phase2, sample1, sample2 volatile 有帮助,但我的问题是 - 为什么?

问题是使用 LTO,-Os 这个代码不起作用。可以通过将计算移至中断,或将计算结果直接分配给综合()函数中的 OCR0A 和 OCR0B 字节来修复它,但是,这不是我想要的行为,因为我想让中断尽可能短。

但是,无论是使用 -O0(完全关闭优化)还是禁用 LTO,此代码都可以工作,但闪存大小会显着增加(使用 -O0 时为 2.3kb,使用 -Os 时为 700B,禁用 LTO,启用 LTO 时为 504b)。

我可以关闭 LTO,但对我来说这似乎是一个肮脏的黑客行为。我的问题是:

  1. 禁用 LTO 是不是很脏?

  2. 优化器如此剧烈地改变行为是否正常?为什么它在没有 LTO 的情况下工作?

  3. 为什么我需要使这些变量变为 volatile 以及如何确定我是否需要使变量变为 volatile(除了简单的反复试验或简单地使它们都变为 volatile?)

  4. 除了更改优化器设置和使某些东西变得易变之外,我还能做什么?

  5. 会不会是编码风格问题?

  6. 关于如何编写优化器不会破坏的代码的任何建议?

标签: c++optimizationarduinolto

解决方案


推荐阅读