AVR-GCC generating livelock

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using an Atmega1284 in my project. This project involves writing into EEPROM, which in this case is a 24AA512-I/P by Microchip. It works perfectly, however I am trying to make it so that everything is within specifications, so everything needs to be validated. The datasheet of the EEPROM specifies that byte or page write time is maximum 5ms, probably dependent on the amount of bytes we write. So what I want is to disable writing for the next 5ms after each write's transmission ends. I did this using interrupts, by having the write function wait for the previous one to finish.

 

bool mem_write_block(uint8_t device, uint16_t address, uint8_t length, void* data) {

    //This is the code that hangs the MCU
	while(EEPROM_FLAG & _BV(WRITE_PENDING));
	
	
	Wire.beginTransmission( EEPROM_BASE_ADDRESS | device );
	Wire.write(address >> 8);
	Wire.write(address & 0xFF);
	for(int i = 0; i < length;i++)
	{
		Wire.write(((uint8_t*)data)[i]);
	}
	Wire.endTransmission();
	begin_write();
	return true;
}

void begin_write()
{
	EEPROM_FLAG |= _BV(WRITE_PENDING);
	//sets up timer for 5ms
	OCR2A = 80;
	TIMSK2 |= _BV(OCIE2A);
	TCNT2 = 0;
	TCCR2B |= _BV(CS22) | _BV(CS20) | _BV(CS21);
}

ISR(TIMER2_COMPA_vect) {
	EEPROM_FLAG &= ~_BV(WRITE_PENDING);
	//disables timer
	TIMSK2 &= ~_BV(OCIE2A);
	TCCR2B &= ~(_BV(CS22) | _BV(CS20) | _BV(CS21));
}

 

The while statement is causing my code to hang, even though the flag is cleared. I've found the reason in the disassembler, though I don't know how to solve this without switching over to ASM.

 

	while(EEPROM_FLAG & _BV(IRAS_FOLYAMATBAN));
000001D7  LDS R25,0x018E		Load direct from data space 
000001D9  ANDI R25,0x01		Logical AND with immediate 
    --- No source file -------------------------------------------------------------
000001DA  CPSE R25,R1		Compare, skip if equal 
000001DB  RJMP PC-0x0001		Relative jump

It's very easy to understand what this ASM code does. It reads the EEPROM_FLAG from the RAM, then checks if the first bit is true, then skip is used to break out of the while and the RJMP is used to repeat. The problem is with RJMP, as the statement should read the value from RAM again and evaluate that, but it only jumps back to the skip and this keeps it in this locked state. So the PC-0x0001 should be PC-0x0003

 

What I basically want to have an interrupt tell me when it is safe to continue and block the write procedure otherwise. Of course something like a task scheduler would be better, but it would take up more CPU time.

 

This topic has a solution.
Last Edited: Sat. Jul 29, 2017 - 12:21 AM
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

variables set within ISR and shared with main needs to be declared volatile.

My guess is one or both of these should be volatile....

while(EEPROM_FLAG & _BV(IRAS_FOLYAMATBAN));

Jim

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How does a 'livelock' differ from a 'deadlock'?
I agree with Jim, EEPROM_FLAG needs to be declared volatile.
Note: normal convention is #defines are in uppercase, not variable names. This makes your code hard to read for the average C coder.

Why did you not just have a timer tick function at 1ms - then you can use this for other timing purposes?

Last Edited: Fri. Jul 28, 2017 - 11:56 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Livelock: code stuck in an infinite loop. live in this case means the code can actually still do something, if I have something in that "while" statement it would execute forever

Deadlock: threads waiting for another thread to finish.

 

Of course this is just the right terminology, and in reality they are both just stuck.

 

I know the convention, the code will get refactored and commented out, these were mainly for testing as there are still a lot of things to do.

 

I already have a timer for a 1ms use case and since it has multiple unused timers why not use another one?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your choice, but you probably want to minimise the number of interrupt sources.