About the issue of functions in the framework

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

Hi, guys,

it looks like that all the simple stuff are done in functions in the framework, even the very simple IO access, can anyone tell me what's main reason for this?

Cheng

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

A simple job like:

void gpio_set_gpio_pin(unsigned int pin)

acutally took a lot of instructions to execute, so why not macro?

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

Because GCC isn't stupid; it will probably inline this function anyway. It will then have the same cost as a macro (ie nothing) but be a brazillian times easier to read.

-S.

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

ok, I can let GCC to inline this for me, but look at this:

void gpio_set_gpio_pin(unsigned int pin)
{
  volatile avr32_gpio_port_t *gpio_port = &GPIO.port[pin >> 5];

  gpio_port->ovrs  = 1 << (pin & 0x1F); // Value to be driven on the I/O line: 1.
  gpio_port->oders = 1 << (pin & 0x1F); // The GPIO output driver is enabled for that pin.
  gpio_port->gpers = 1 << (pin & 0x1F); // The GPIO module controls that pin.
}

The structure difinition "gpio_port->ovrs" will add extra insturctions to loading the IO address of OVRS, why not do it like the old AVR did?

And the same is to bit number in the register, why not give a special MACRO name, instead of shift it.

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

darthvader wrote:
The structure difinition "gpio_port->ovrs" will add extra insturctions to loading the IO address of OVRS, why not do it like the old AVR did?

And the same is to bit number in the register, why not give a special MACRO name, instead of shift it.

I've actually written a gpio framework for AVR myself which does something similar to this. The 'pin' token has encoded in it the port and the pin data. The (WinAVR) AVR framework has no support at all for such a generic pin token does it?

For example, I've got an SPI driver which takes as an argument the chip select pin to use. I guess I could have passed in PORTA and PA4 separately but shifting them together on one side and decoding on the other seems to me a better and more generic solution. Does WinAVR have something that I don't know about that would help this?

Sure it might take a few more cycles if pin is non-constant but if you ask gcc to optimize your code it should constant-roll all this for no penalty.

Oh, and you're right, I think there's an extra pointer dereference above but I can't immediately think of a nicer way to do this.

-S.

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

the AVRGCC had all the register adresses and their bit numbers mapped to special macro names, so there is no need to shift bit in the program, example code of ATmega1281 like:

	TCCR1B = _BV(ICES1) | _BV(CS11); // start Timer1 in normal mode, rising edge input capture, clk/8
 7d8:	82 e4       	ldi	r24, 0x42	; 66
 7da:	8e bd       	out	0x2e, r24	; 46
	
	TCCR3B = _BV(WGM32) | _BV(CS30); // start Timer3 in CTC mode, clk/1
 7dc:	89 e0       	ldi	r24, 0x09	; 9
 7de:	80 93 8a 00 	sts	0x008A, r24

But now on AVR32, with O3 as optimization level, I got

void gpio_set_gpio_pin(unsigned int pin)
{
  volatile avr32_gpio_port_t *gpio_port = &GPIO.port[pin >> 5];

  gpio_port->ovrs  = 1 << (pin & 0x1F); // Value to be driven on the I/O line: 1.
80002204:	30 18       	mov	r8,1
80002206:	f0 0c 09 48 	lsl	r8,r8,r12
8000220a:	a5 9c       	lsr	r12,0x5
8000220c:	a9 6c       	lsl	r12,0x8
8000220e:	e0 2c f0 00 	sub	r12,61440
80002212:	f9 48 00 54 	st.w	r12[84],r8
  gpio_port->oders = 1 << (pin & 0x1F); // The GPIO output driver is enabled for that pin.
80002216:	f9 48 00 44 	st.w	r12[68],r8
  gpio_port->gpers = 1 << (pin & 0x1F); // The GPIO module controls that pin.
8000221a:	99 18       	st.w	r12[0x4],r8
}
8000221c:	5e fc       	retal	r12

and in this code,

Quote:

80002206: f0 0c 09 48 lsl r8,r8,r12

is not necessary

and so is

Quote:

8000220a: a5 9c lsr r12,0x5
8000220c: a9 6c lsl r12,0x8
8000220e: e0 2c f0 00 sub r12,61440

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

Sure, there are many extra instructions there but there is also extra functionality. In AVRGCC if you want to change the state of a pin you must know the number of the pin but also *what port it's on*. One way you can do this is to pass both these macros (eg PORTA and PA0) to another macro which then performs the PORTA |= _BV(PA0) (which incidentally expands to PORTA |= (1<<PA0) anyway).

The way I do it myself when I'm on 8-bit AVRs as well as the way the Atmel boys have done it in the framework is to encode both 'PORTA' and 'PA0' (continuing the same example) in to *one cookie* which can be passed around at will. There's no chance of accidentally loosing track of which PORTx goes with which Pxn and setting the right pin on the wrong port or vica-versa.

More than this, the cookie doesn't just encode the PORTA register, it just keeps track of the 'A' bit so you can use the same pin cookie to change direction (DDRA) or read values (PINA) as well.

With this ease of use and security comes extra clock cycles decoding the cookie and selecting the appropriate action. It's a design decision; if you want to save a few cycles and keep track of all the PORTA/DDRA/PINA/PAn stuff separately then you can do so.

FWIW this single cookie approach is also how the Linux Kernel GPIO Framework works. In that case the cookie not only encodes what port and pin are in question but also which chip they're on (either internal to the SoC or on an IO Expander) and how to go about communicating with them.

Admittedly a few extra clock cycles is going to make less of a difference on a Linux-powered SoC which churns through at least hundreds of millions of instructions per second than a UC3. Nevertheless, it's a design decision which you're free to rewrite if you want.

-S.

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

ok, now i get a picture of this, thanks.