TWI problems with compiler optimization enabled

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

I have a project using the AT32UC3C that works great with compiler optimization turned off.  But with it turned on, the TWI hardware (in slave mode) doesn't function properly.  I am able to get a few TWI transfers out but that is it.  BTW, I'm using the PDCA along with the TWI hardware.

 

Things work fine if I add at least two break points and one is prior to my function that initiates the TWI transfers.  Any other break point configurations or free-running execution cause the TWI hardware to become unresponsive until things are re-initialized.

 

Does anyone have an idea how optimization might effect operation of the TWI hardware?  

 

Thanks in advance.

Walt

Last Edited: Tue. Dec 2, 2014 - 05:47 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Assuming that you are using the AVR32 GCC compiler in Atmel Studio6.x then it has 5 optimisation levels available.

Which optimisation level have you tried ? -O1, -O2, -O3 or -Os ?

A brief snippet of the problem code might be helpful.

Last Edited: Tue. Dec 2, 2014 - 08:42 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've tried all of the optimization levels.  Same behavior with any optimization.  I'll try to simplify my code and provide a snippet.

Walt

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

I can get things working if I do the following each time I initiate a TWI PDCA transfer:

 

  1. Disable the PDCA transfer complete interrupts (in red below).
  2. Fix an invalid function pointer that was invalid.

 

void twimDMA_write_read( uint8_t addr, volatile void *bufTx, uint8_t sizeTx, volatile void *bufRx, uint8_t sizeRx )
{
	pdca_disable_interrupt_transfer_complete(PDCA_CH_TWIM_RX);
	pdca_disable_interrupt_reload_counter_zero(PDCA_CH_TWIM_RX);

	pdca_load_channel(PDCA_CH_TWIM_RX, bufRx, sizeRx);
	pdca_load_channel(PDCA_CH_TWIM_TX, bufTx, sizeTx);
	
	// Write address and register number
	TWIM->cmdr  = (addr << AVR32_TWIM_CMDR_SADR_OFFSET)
				| (sizeTx << AVR32_TWIM_CMDR_NBYTES_OFFSET)
                | (AVR32_TWIM_CMDR_VALID_MASK)
                | (AVR32_TWIM_CMDR_START_MASK)
                | (0 << AVR32_TWIM_CMDR_READ_OFFSET);
	
	// Read back "size" number of data bytes
	TWIM->ncmdr = (addr << AVR32_TWIM_NCMDR_SADR_OFFSET) 
				| (1 << AVR32_TWIM_CMDR_READ_OFFSET)				// Read bit
				| (sizeRx << AVR32_TWIM_NCMDR_NBYTES_OFFSET)
				| (AVR32_TWIM_NCMDR_VALID_MASK)
 				| (AVR32_TWIM_NCMDR_START_MASK)
				| (AVR32_TWIM_NCMDR_STOP_MASK)
				| (AVR32_TWIM_NCMDR_READ_MASK);
	
	pdca_enable_interrupt_transfer_complete(PDCA_CH_TWIM_RX);
	pdca_enable(PDCA_CH_TWIM_RX);
	pdca_enable(PDCA_CH_TWIM_TX);
}

 

Somehow I'm getting corrupted memory.  I do get a compiler error which may relate to this problem, but I'm not sure how to fix it:

Error    118    PROJECT.elf: section .ddalign lma 0x8003c1b0 adjusted to 0x8003c71c

 

 

 

Walt

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

Does your pdca interrupt-handler disable the transfer-complete interrupt ?
(The PDCA will generate continuous interrupts until you disable them or their conditions are not satisfied.)

Is the TERR flag in the ISR register set ?

Last Edited: Wed. Dec 3, 2014 - 07:07 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes, the PDCA interrupt handler disables the transfer complete interrupt.  No, TERR flag in the TWI PDCA ISR register is not set, so no PDCA error.

 

I believe my problem arises from memory misuse problem.  In my TWI PDCA library, if I make PDCA function calls inline, things break.  However, things work fine if I leave them as function calls.  For example, with optimization on, the following function works with INLINE_OPTIMIZE=0 but not with INLINE_OPTIMIZE=1.  It's probably not that the function works or doesn't, but that I'm changing the size and location of code memory.

 

void twimDMA_write_read( uint8_t addr, volatile void *bufTx, uint8_t sizeTx, volatile void *bufRx, uint8_t sizeRx )
{
	// Setup PDCA
#if INLINE_OPTIMIZE==1	// We can only use this optimization w/ optimization off because of some sort of memory alignment error.
	volatile avr32_pdca_channel_t *pdca_channel_rx = &AVR32_PDCA.channel[PDCA_CH_TWIM_RX];
	volatile avr32_pdca_channel_t *pdca_channel_tx = &AVR32_PDCA.channel[PDCA_CH_TWIM_TX];

	irqflags_t flags = cpu_irq_save();

	pdca_channel_rx->idr = AVR32_PDCA_TRC_MASK | AVR32_PDCA_RCZ_MASK;
	pdca_channel_rx->mar = (uint32_t)bufRx;
	pdca_channel_rx->tcr = sizeRx;
	pdca_channel_rx->cr = AVR32_PDCA_ECLR_MASK;
	pdca_channel_rx->isr;

	pdca_channel_tx->idr = AVR32_PDCA_TRC_MASK | AVR32_PDCA_RCZ_MASK;
	pdca_channel_tx->mar = (uint32_t)bufTx;
	pdca_channel_tx->tcr = sizeTx;
	pdca_channel_tx->cr = AVR32_PDCA_ECLR_MASK;
	pdca_channel_tx->isr;

	cpu_irq_restore(flags);
#else		
	pdca_disable_interrupt_transfer_complete(PDCA_CH_TWIM_RX);
	pdca_disable_interrupt_reload_counter_zero(PDCA_CH_TWIM_RX);
	
    pdca_load_channel(PDCA_CH_TWIM_RX, bufRx, sizeRx);
    pdca_load_channel(PDCA_CH_TWIM_TX, bufTx, sizeTx);
#endif

	// Setup TWI
	
	// Write address and register number
	TWIM->cmdr  = (addr << AVR32_TWIM_CMDR_SADR_OFFSET)
				| (sizeTx << AVR32_TWIM_CMDR_NBYTES_OFFSET)
                | (AVR32_TWIM_CMDR_VALID_MASK)
                | (AVR32_TWIM_CMDR_START_MASK)
                | (0 << AVR32_TWIM_CMDR_READ_OFFSET);
	
	// Read back "size" number of data bytes
	TWIM->ncmdr = (addr << AVR32_TWIM_NCMDR_SADR_OFFSET) 
				| (1 << AVR32_TWIM_CMDR_READ_OFFSET)				// Read bit
				| (sizeRx << AVR32_TWIM_NCMDR_NBYTES_OFFSET)
				| (AVR32_TWIM_NCMDR_VALID_MASK)
 				| (AVR32_TWIM_NCMDR_START_MASK)
				| (AVR32_TWIM_NCMDR_STOP_MASK)
				| (AVR32_TWIM_NCMDR_READ_MASK);	

#if INLINE_OPTIMIZE==1
	// Enable Rx PDCA transfer complete interrupt
	pdca_channel_rx->ier = AVR32_PDCA_TRC_MASK;

	// Enable PDCA
	pdca_channel_rx->cr = AVR32_PDCA_TEN_MASK;
	pdca_channel_tx->cr = AVR32_PDCA_TEN_MASK;
#else
	// Enable Rx PDCA transfer complete interrupt
	pdca_enable_interrupt_transfer_complete(PDCA_CH_TWIM_RX);

	// Enable PDCA
    pdca_enable(PDCA_CH_TWIM_RX);
    pdca_enable(PDCA_CH_TWIM_TX);
#endif
}

 

 

Walt

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

What PID values do you set into the PSR register for each PDCA channel ?
I'm assuming that the PDCA channels PDCA_CH_TWIM_RX and PDCA_CH_TWIM_TX are different, in the range 0-15 and only used for the TWIS.

I would disable the PDCA channels before doing anything with them. If a channel is still enabled then putting a count into the TCR will start a transfer.
Enable the channels after they have been fully configured to your liking.

Last Edited: Wed. Dec 3, 2014 - 08:09 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The PID values are different and unique:

#define PDCA_CH_TWIM_TX                0
#define PDCA_CH_TWIM_RX                1

I disabled the PDCA channels before configuring them.  Now it runs with -O1 but not -O3 (most optimization).  It will run if I add a break point before each transfer, causing the processor to halt between transfers.  Now I'm thinking this is more along the lines of a timing issue.  Adding or removing dummy instructions in the code changes the faulty behavior.

 

 

Walt

Last Edited: Wed. Dec 3, 2014 - 09:52 PM