AVR GCC + Optimization + AVR Simulator = Fail.

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

I've been writing some code for some hardware I don't have in my possession at the moment. So I can't actually test the hardware at the moment.

I've been writing a general purpose SMBus driver.. So far I've got master transmitter/master receiver covered. I've got to then include ST/SR modes and handle arbitrations (multi-master bus)..

Anyways, I've been compiling on -02 and despite the fact the simulator doesn't do the TWI hardware I've fixed several bugs by modifying TW_STATUS directly in the simulator.

Next thing I change one small change and all hell breaks loose.

case TW_MT_SLA_ACK:							//SLA+W sent, ACK rcvd
		case TW_MT_DATA_ACK:						//Data sent, ACK rcvd
		{
		
			//If there is data to be written out in MT mode
			if (writeIndex < twig.writeLen)
			{

				//Update CRC value
				crc = pgm_read_byte_near(&(CRC8_CCITT_TABLE[crc^twig.txData[writeIndex]]));

				//Load byte into TWI Data Register, increment writeIndex
				TWDR = twig.txData[writeIndex++];

				//Configure TWI control register to signal ISR
				//when next event occurs
				TWI_Continue();

			}

			/*
			
			Otherwise we have finished sending application-provided
			data. Insert PEC if necessary and stop transmission or
			change modes (i.e. write then read)			

			*/

			//If the structure of this command is write-only
			else if ((twig.flags & TWIFL_WRITEONLY))
			{

				//If PEC is enabled
				if ((twig.flags & TWIFL_USEPEC) && (writeIndex == twig.writeLen))
				{
					//Load PEC into TWI Data Register
					TWDR = crc;

					//Increment writeIndex to prevent PEC being
					//sent over and over.
					writeIndex++;

					//Configure TWI control register to signal ISR
					//when next event occurs
					TWI_Continue();

					#warning "This will send PEC over & over"
					#warning "must fix"
				}

				//Otherwise, PEC disabled. Stop and signal success
				else

So, if the simulator/compiler were working correctly toghether (as they do if optimizations are turned off). Then under normal circumstances the first if statement:

if (writeIndex < twig.writeLen)

would execute the statements within its scope and then execution would pass beyond the if/if-else/else block to the end of the case {} statement and would be broken out (break;) to the end of the ISR.

This all works as expected as long as the line 'writeIndex++;' is removed from the else-if block.

What happens when writeIndex++ is present in (-02)?

As execution enters if (writeIndex < twig.writeLen)
the statements within it are executed.. As execution leaves this block it should jump to the end of the ISR via the break; statement at the end of the case {}.

Instead, it jumps to the beginning statement of the else-if below it, starting at:

TWDR = crc;

Does anyone know if this is a simulator-only issue or if this bug is likely to occur on hardware as well? Also, does anyone know how to avoid this kind of behavior (even though it's clearly a bug)..

etc.. etc.. Any help/tips/thoughts you guys may have for me would be appreciated.

Thanks. Brad.

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

did you declare the writeIndex as volatile?
From the tread i make-up that you try to increse it inside the ISR.... then it must be volatile. otherwise a local copy is used inside the isr that is discarted after exiting the ISR, thus effectively never incresing your variable at all....

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

It's static within the ISR.

i.e. static uint8_t writeIndex = 0;

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

danielson wrote:
It's static within the ISR.

i.e. static uint8_t writeIndex = 0;

If it is static in the ISR(), then nothing else in the world can see it.

i.e. the 'writeIndex' in your foreground code is a completely different variable.

'static' restricts the scope to the module or block it is defined in.

'volatile' indicates that a variable may change value outside of program control. e.g. a loose cannon.

David.

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

I'm aware.

writeIndex is used only within the ISR. It's not volatile global because I never need to use it outside the ISR.

I do have a struct of globals that are used to setup the TWI state, this struct is declared as a volatile, i.e.

volatile TWI_GLOBALS twit;

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

Have you verified this from the disassembly? Debuggers and simulators often have a really hard time mapping the execution to source rows when optimizations are on.

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

I haven't jumped into verification at the assembly level.

Fortunately, I was able to make a small change in the code to get a more predictable sequence of events in the simulator (unless I was making a mistake, it appeared the code wasn't executing as written before).

I just swapped the lines TWDR=crc;/writeIndex++, i.e.

					//Increment writeIndex to prevent PEC being
					//sent over and over.
					writeIndex++;

					//Load PEC into TWI Data Register
					TWDR = crc;

The code then executes as expected (though there are places where code that does exactly the same thing is reused, i.e. the simulator jumps to a non-linear point in a linear sequence). Does that make sense? Basically, code that does exactly the same thing & has the same exit point is reused, which is fine.

As the code matures to support multi-master & slave operation I'll try to diassemble it.