From: ChrisQ on
David Brown wrote:

> Code that uses pointers to structs will see particular benefits of
> having Y available for general use.
>

More good info - I suspect avr is a far better architecture than 8051.
Memories of legacy 8051 hw platforms, multiple code banks, not enough
common area and hard work trying to ensure that all the correct data
appeared in the selected bank at the right time suggests that there must
be a better way. The impact on development timescales can be significant
and outweighs any device cost advantage for small to medium volume products.

As you suggest, more than 2 arguments and the best way is to package up
into a structure and pass a pointer to it. Such a structure can also aid
encapsulation as common variables can also be declared within it. Object
oriented methods for 8 bit micros indeed :-)...

Regards,

Chris
From: David Brown on
ChrisQ wrote:
> David Brown wrote:
>
>> Code that uses pointers to structs will see particular benefits of
>> having Y available for general use.
>>
>
> More good info - I suspect avr is a far better architecture than 8051.
> Memories of legacy 8051 hw platforms, multiple code banks, not enough
> common area and hard work trying to ensure that all the correct data
> appeared in the selected bank at the right time suggests that there must
> be a better way. The impact on development timescales can be significant
> and outweighs any device cost advantage for small to medium volume
> products.
>
> As you suggest, more than 2 arguments and the best way is to package up
> into a structure and pass a pointer to it. Such a structure can also aid
> encapsulation as common variables can also be declared within it. Object
> oriented methods for 8 bit micros indeed :-)...
>

No, no - I did not suggest packing function call arguments in a struct!
How did you manage to read that from my post? You only need to use
such tricks for braindead architectures like the 8051, where you have a
hopeless stack and almost no registers, and thus need to pass data via
globals or extra structs. (A good compiler will hide these messy
implementation details from you, and do a better job that using these
tricks manually.)

The AVR has plenty of registers - you pass arguments in these registers
by using normal C function calls. If you have so many parameters (or
such large parameters) that passing by stack is needed, the compiler
handles that fine - there is a minor overhead, but any code that needs
it will already be large.

What I said about pointers to structs is that the AVR has two pointer
registers that work well with structs - Y and Z (since there are Y+index
and Z+index addressing modes). If your compiler dedicates Y to a data
stack pointer, it's going to be inefficient at code that could otherwise
take advantage of two pointer-to-struct registers.
From: David Brown on
ChrisQ wrote:
> Niklas Holsti wrote:
>> A small addition to my own posting, sorry for omitting it initially:
>>
>> Niklas Holsti wrote:
>> (I elide most of the context):
>>
>>> However, sometimes the IAR compiler generates code that adds or
>>> subtracts a larger number (> 1) to/from Y, and then it must use two
>>> 8-bit operations, and must disable interrupts just as gcc does.
>>
>> Some AVR models do provide instructions (ADIW, SBIW) that can
>> atomically add/subtract an immediate number (0..63) to/from the 16-bit
>> Y register. I assume, but haven't checked, that IAR uses these
>> instructions when possible, rather than two 8-bit operations in an
>> interrupt-disabled region.
>>
>
> A very good explanation and thanks. It's the intricacies of architecture
> that is sometimes hard to get a big picture of when choosing a processor
> for a project. I've never used avr for any project and info like this
> would tend to keep me in the 8051 world for small logic replacement
> tasks, no matter how constrained it is. AVR32 looks much better though.
>

I'm guessing you wrote this before reading my other post? Remember,
these are details that are hidden by the compiler, and the AVR will have
executed the necessary pushes, stack pointer manipulation, interrupt
disable and whatever before the average 8051 device has managed to push
the A register onto the stack. The discussion is about whether gcc's
stack arrangement or IAR's stack arrangement is best for producing
optimal interrupt code on the AVR - no one would seriously compare it to
the 8051.

The AVR32 is a different beast entirely. It shares the same developer
(Atmel), and some tools, but other than that it is a totally different
processor.

> In summary then, it looks like the 8 bit avr's need special compiler
> support to get best results, which I wouldn't necessarily expect gcc to

The AVR needs an AVR compiler - just like any other cpu needs its own
compiler. It doesn't need any "special" support or tricks here - every
target has it's own way of handling function prologues and epilogues.

gcc is best suited to RISC-type architectures with plenty of registers
and an orthogonal instruction set. The AVR comes fairly close to that,
but with two big exceptions - it is 8-bit (most gcc targets are 32-bit),
and it has a separate memory space for flash. avr-gcc does a good job
in working around these "non-standard" features, but is occasionally
sub-optimal in that regard.

This is hugely different from cores like the 8051 or the COP8, which
need much more specialised compilers to generate good code.

> provide. I'm quite happy to accept that IAR would produce better code,
> in much the same way as Keil is arguably the best solution for 8051.

It's a different world entirely. IAR produces better code than gcc (at
least, according to popular opinion - I have not yet compared it myself,
or seen any independent comparisons) because they have more resources to
use in the development of their compiler, and their compiler
architecture is probably also more suited to optimising 8-bit code.
They have also been working with the AVR developers since before the
core was fully specified.

Not to belittle the work of either the avr-gcc or IAR development teams,
but writing a solid AVR compiler that produces small and fast code is a
fraction of the work needed to make a close-to-optimal 8051 compiler.
And if you've got a working multi-target compiler to start with (as both
avr-gcc and IAR had), then porting it to the AVR is a practical task.
For the 8051, you have to start almost from scratch.

> Both are 8 bit legacy architectures, designed before the days of general
> hll development. I think if I were trying to find a low end micro now,

I think you should read a little about the AVR before making such
ignorant and incorrect statements. The AVR was specifically designed as
a small and low power core that worked well with C - it was developed in
cooperation with IAR. The 8051 is legacy, even though there are modern
implementations. But the AVR, while not perfect, is about as close to
modern cpu design as you get in 8 bits.

> msp430 would be the first point of call, as it is a much more compiler
> friendly 16 bit architecture. Stuff like this does matter as it can have
> a significant impact on software development timescales and quality...
>

The msp430 is certainly very compiler friendly - even more so than the
AVR (16-bit registers, plenty of flexible pointers, and a single address
space). But they too have their "special issues". For example, the
multiplier is implemented as a peripheral and the state of the
multiplier cannot be properly saved by an interrupt. Thus either
interrupts must avoid using the multiplier, or main code must disable
interrupts when using the multiplier. /Every/ cpu core has it issues.

And the newer msp430 cores with their 20-bit registers totally buggers
up their C compiler friendliness.
From: Niklas Holsti on
David Brown wrote:

> The AVR has plenty of registers - you pass arguments in these registers
> by using normal C function calls. If you have so many parameters (or
> such large parameters) that passing by stack is needed, the compiler
> handles that fine - there is a minor overhead, but any code that needs
> it will already be large.
>
> What I said about pointers to structs is that the AVR has two pointer
> registers that work well with structs - Y and Z (since there are Y+index
> and Z+index addressing modes). If your compiler dedicates Y to a data
> stack pointer, it's going to be inefficient at code that could otherwise
> take advantage of two pointer-to-struct registers.

.... which is a drawback of the two-stack solution. On the other hand,
since the AVR provides no SP-relative addressing, single-stack code must
often use one of the Y or Z pointers as a frame pointer, and there we
are again.

Although the AVR is register-rich, it is "pointer-poor". Some other
architectures, such as the H8/300, have more flexible interplay of 8-bit
and 16-bit computations.

--
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
. @ .
From: ChrisQ on
David Brown wrote:

>
> I'm guessing you wrote this before reading my other post? Remember,
> these are details that are hidden by the compiler, and the AVR will have
> executed the necessary pushes, stack pointer manipulation, interrupt
> disable and whatever before the average 8051 device has managed to push
> the A register onto the stack. The discussion is about whether gcc's
> stack arrangement or IAR's stack arrangement is best for producing
> optimal interrupt code on the AVR - no one would seriously compare it to
> the 8051.

Stuff like that may be hidden by the compiler, much as one might stick a
bandaid on a sore to hide it, but the inefficiency is still there. C on
8 bit micros only gets away with it in most cases because the later cpu
performance is far greater than cpu's used in the old assembler days.
This is not a critique of avr either. Its a long time since I looked at
the output from Keil C, but remember being horrified by a function with
a simple switch statement + a few other lines of code producing over 2
A4 pages of 8051 assembler. We get away with it, but let's not pretend
that it's anything like elegant - it's a compromise made to make
software development easier, portable and more maintainable using cheap
micros. The downside is that such limitations can make it much more
difficult to produce structured, robust and portable code.

>
> I think you should read a little about the AVR before making such
> ignorant and incorrect statements. The AVR was specifically designed as
> a small and low power core that worked well with C - it was developed in
> cooperation with IAR. The 8051 is legacy, even though there are modern
> implementations. But the AVR, while not perfect, is about as close to
> modern cpu design as you get in 8 bits.
>

As I said, I haven't used avr, but why not 16 bit registers instead of 8
?. Sorry, but I don't see that as being particularly compiler friendly.
Compiler friendly to me means n * 16 bit registers at a minimum and
addressing modes to allow use for vars or addresses.

>
> The msp430 is certainly very compiler friendly - even more so than the
> AVR (16-bit registers, plenty of flexible pointers, and a single address
> space). But they too have their "special issues". For example, the
> multiplier is implemented as a peripheral and the state of the
> multiplier cannot be properly saved by an interrupt. Thus either
> interrupts must avoid using the multiplier, or main code must disable
> interrupts when using the multiplier. /Every/ cpu core has it issues.
>

That's a bit of a red herring. Not all versions of the 430 have the
multiplier in any case and it looks like it was intended as a fast dsp
type 'peripheral'. The compiler must provide support via shift and add
or similar to get multiply and divide in C.

> And the newer msp430 cores with their 20-bit registers totally buggers
> up their C compiler friendliness.

Agreed, that could be a problem, but no doubt the compilers will be
updated to make use of the added address space. They manage fine on the
24 bit 80C87 series and probably others as well...

Regards,

Chris