I am developing software for attiny88 with avr-gcc toolchain. This is a cheap microcontroller with 8kB program memory, 512B SRAM and 64B non-volatile EEPROM data memory. This is all sufficient for the task it is supposed to handle. I do not have a debugger for this chip.
The device acts as SPI slave and allows the master to read/program the EEPROM and to read the device state (state of some analog inputs and digital outputs). The protocol is simple - first databyte carries the instruction where highest two bits encode required action (00-nothing, 01-write eeprom, 10-read eeprom, 11-read state) and the rest is address. Second byte is always zero, third byte is the value to read or write, fourth byte is always zero.
Problem is that I am getting weird behavior from the compiler. It is difficult to put a finger on what is going on, so I will just give a few examples. Most striking is that without -Os optimization the device does not respond over SPI. This is not for any of the obvious reasons - the program fits the memory and the stack should not run into .bss/.data sections (the program has ~700B, SRAM is mapped between 0x100 and 0x2ff, where .bss_end is at 0x109; the heap is empty; there are no nested function calls, neither nested interrupts).
If I turn on the -Os optimization, then the program responds as intended. Here is the working code to handle the ISR:
unsigned char state[8];
volatile unsigned char data;
ISR(SPI_STC_vect)
{
switch(data>>6) {
case 0:
data = SPDR;
break;
case 1: /* write eeprom */
while(EECR & (1<<EEPE));
EECR = (0<<EEPM1)|(0<<EEPM0);
EEARL = 0;
EEDR = SPDR;
EECR |= (1<<EEMPE);
EECR |= (1<<EEPE);
data = 0;
break;
case 2: /* read eeprom */
EEARL = data & 0x1f; /* with 0x3f stops working (???) */
EECR |= (1<<EERE);
SPDR = EEDR;
data = 0;
break;
case 3: /* read state */
SPDR = state[data&7];
data = 0;
break;
}
}
However, the program gets broken when written in semantically different way:
If I change the line "EEARL = data & 0x1f;" to "EEARL = data & 0x3f;", which is desirable as it would allow to address whole EEPROM address space, the ERPROM writing/reading stops working (I do not need full 64B, so I left it as is)
State reading (case 3) gets broken if I replace the line "SPDR = state[data&7];" with switch-case construct that returns value of PORTD/PORTB register when address is 0 and 1 respectively (the current workaround is to keep state[0] and state[1] synchronized with PORTB/PORTD in the main loop).
Am I missing something important? It seems to me that the compiler messes up, but I haven't found any bug reports for avr-gcc or avr-libc (newlib?) that would fit the bill.
The toolchain was installed from current gcc-avr package in aptitude repositories. The makefile is pretty basic:
all: main.hex main.s
main.elf: main.o
avr-gcc -g -mmcu=attiny88 -o main.elf main.o
main.s: main.elf
avr-objdump -d main.elf > main.s
main.hex: main.elf
avr-objcopy -O ihex main.elf main.hex
main.o: main.c
avr-gcc -mmcu=attiny88 -Os -c main.c -Wall
UPDATE: Still the same result with latest binutils, gcc and avr-libc (recompiled from sources)
data
variable? Are you relying on static initialization to give you "zero-out"? That's always a bad idea, since microcontrollers often deviate from the C standard there, in order to give faster start-up. \$\endgroup\$