Supporting AVR_TINY, draft HOWTO

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Supporting AVR_TINY, draft HOWTO

George Spelvin
Having seen Georg-Johann's patch to enable more code on AVR-TINY, I'd
like to know how to do it myself.  So I've written the following draft
of a document explaining how, which might eventually be useful for
the project, but currently is for people to point out the error is.

One thing that jumps out at me is that the X_lpm macro should be extended
to invoke LD in case of __AVR_TINY__.  It looks very simpe.  Is there
a reason that nobody has done this yet?


The smallest microcontrollers in the AVR family are the "AVR-tiny" group.

They implement a subset of the AVR architecture, and receive limited
support.

(Note that most processors sold as "ATtiny" are *not* the AVR-tiny
architecture.  Even the ATtiny13 is a full AVR core.  As of this writing,
the list of AVR-tiny processors is ATtiny 4, 5, 9, 10, 20 and 40)

Unlike the different "avr1" subset, the avrtiny *is* supported by GCC.
However, avr-libc has limited support.

Compilation for an AVR-tiny processor is identified by the __AVR_TINY__
symbol.  Large parts of the library are wrapped in "#ifndef __AVR_TINY__"
and generate an empty object file.  This is the basic requirement for
AVR-tiny non-support.

However, if not too difficult, and makes sense, it's nice to make your
code usable on AVR-tiny.  This document explains how to do that.

The biggest issue is the fact that registers r0-r15 are not implemented.
Only registers r16-r31 exist.  Since these registers are used more heavily
anyway (they're the call-used registers available to leaf functions, and
can be used with the immediate instructions), many functions fit into
this limit already.  If your function can't fit, it's a good candidate
for #ifndef __AVR_TINY__.

The AVR-tiny calling convention is almost the same as the regular one,
except that r16 is used for the temporary register instead of r0, and
r17 is used for the zero register instead of r1.  Note that this means
that the only call-saved registers are r28 and r29 (the Y pointer).

To make this easier in assembly code, the macros __tmp_reg__ and
__zero_reg__ are defined and map to r0/r1 on non-tiny processors, and
r16/r17 on tiny cores.

A second large difference is that tiny cores have only one address space.
They do not have the LPM instruction, but map flash to the data address
space starting at 0x4000.  Thus, all of avr-libc's *_P() functions
(which take program memory pointers) are unwanted, and just alias the
non-suffixed functions.

(TODO: Update the X_lpm macro to use LD in case of __AVR_TINY__)

Another difference in the address space is that the registers are not
memory-mapped.  This is unlikely to be an issue; this feature is not
used much on avr-libc.  The address space looks like:

0x0000-0x003f: I/O registers (instead of 0x20-0x5f on other AVR)
0x0040-0x005f: SRAM (yes, 32 bytes!)
...       unused
0x3f00-0x3f01: NVM lock bits
0x3f40-0x3f41: Configuration bits
0x3f80-0x3f81: Calibration bits
0x3fc0-0x3fc1: Device ID
0x4000-0x41ff/0x43ff: Program memory


Third, there are the instruction set differences.

AVR-tiny lacks all the usual extensions you might think of (multiply, DES
round, XMEGA load-and-update extensions), but also:

- LDD Y+offset and LDD Z+offset.  Only X, X+, -X, Y, Y+, -Y, Z, Z+ and -Z
  addressing modes are allowed.
- MOVW.  The existing macro X_movw can handle this, however.
- ADIW/SBIW.  The *only* 16-bit operation supported by AVR-tiny is the
  address register auto-inc/decrement on LD/ST.  (PUSH/POP are also
  supported, but the stack pointer is only 8 bits!)  These can usually
  be replaced by an SUBI/SBCI pair.
- Any "extended" instructions, ELPR, EIJMP, EICALL.

AVR-tiny *does* have LDS and STS instructions for load/store to absolute
addresses, but they're different from other AVRs.  They are 1-word
instructions (no worrying about the skip bug!) which specify a 7-bit
address which is offset by 0x40 between 0x40 and 0xbf.  (This is
not a limit because available SRAM is much less than 128 bytes.)

It's the lack of base+offset addressing modes which are the biggest
problem.  Sometimes it's possible to make creative use of the
auto-inc/decrement instructions.  For example, instead of

        ld b0,Z+0
        ld b1,Z+1
        ld b2,Z+2
        ld b3,Z+3
        /* Compute */
        st Z+0,b0
        st Z+1,b1
        st Z+2,b2
        st Z+3,b3

you might be able to do:

        ld b0,Z+
        ld b1,Z+
        ld b2,Z+
        ld b3,Z
        /* Compute */
        st Z,b3
        st -Z,b2
        st -Z,b1
        st -Z,b0

The lack of adiw can also be a nuisance.  It can be replaced with an
subi/sbci pair to subtract the negative, but that has the opposite effect
on the carry flag.


Like other AVR processors with less than 256 bytes of RAM, AVR-tiny only
implements 8 bits of stack pointer.

Unlike other such AVR procssors, AVR-tiny does *not* ignore the high
byte of the pointer registers.  (That's because they can also address
the program memory.)



== What about avr1? ==

avr1 is a *different* subset and is *not* supported byh GCC or avr-libc.
(Although some people have managed to use gcc by examining its output for
unimplemented instructions and changing any code that generates them.)

avr1 is included in the attiny11, 12, 15 and 28 processors, and
the at90s1200.

Although it does include the full 32 registers, in every other respect
it's a smaller subset than avr-tiny.

To begin with, The registers are mapped to memory addresses 0-31 and
that's all the RAM you have!  There is no other RAM!

The I/O registers are not memory-mapped; they're in their own
address space accessible via the IN and OUT instructions.  The low
32 I/O registers are also accessible via the SBI, CBI, SBIC and SBIC
instructions.

There's not much need for rmemory addressing, and so the only addressing
mode implemented for LD or ST is "Z".  No Z+offset, Z+, -Z, or anything
using X or Y.  Also, when accessing RAM, only the low byte of Z (r30)
matters.  r31 is ignored.  (It *is* used by LPM, however.)

Another difference is that it has a 3-level hardware stack (in addition
to the current PC).  There is no visible stack pointer, and no "push"
or "pop" instructions.  (If you underflow the stack, the top entry gets
duplicated.)

Needless to say, there is no MOVW, ADIW or SBIW.  There is also no LDS
or STS at all.  (Use the MOV instruction instead!)

_______________________________________________
AVR-libc-dev mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/avr-libc-dev
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Supporting AVR_TINY, draft HOWTO

Georg-Johann Lay-2
George Spelvin schrieb:
> Having seen Georg-Johann's patch to enable more code on AVR-TINY, I'd
> like to know how to do it myself.  So I've written the following draft
> of a document explaining how, which might eventually be useful for
> the project, but currently is for people to point out the error is.
>
> One thing that jumps out at me is that the X_lpm macro should be extended
> to invoke LD in case of __AVR_TINY__.  It looks very simpe.  Is there
> a reason that nobody has done this yet?

Presumably because nobody has put effort in this :-)

And mybe because X_lpm is not needed on Tiny because you can avoid
progmem gobbledegook altogether.  All you need is an appropriate
linker description file, cf. "progmem" + "Reduced AVR Tiny cores" in

http://gcc.gnu.org/onlinedocs/gcc/AVR-Variable-Attributes.html

I also filed https://sourceware.org/PR20849

But as it appears nobody is picking this up...

> The smallest microcontrollers in the AVR family are the "AVR-tiny" group.
>
> They implement a subset of the AVR architecture, and receive limited
> support.
>
> (Note that most processors sold as "ATtiny" are *not* the AVR-tiny
> architecture.  Even the ATtiny13 is a full AVR core.  As of this writing,
> the list of AVR-tiny processors is ATtiny 4, 5, 9, 10, 20 and 40)

There is yet another family of such Tinys like ATtiny104 (not yet
supported)

* 16 GPRs
* Support for SBIW, ADIW, MOVW
* Support for Z+q and Y+q addressing modes.

Hence the current "#ifdef __AVR_TINY__" idiom is not very robust
w.r.t. future extensions...  The ATtiny104 class of controllers
will require a new multilib (same for ATtiny817 class cores which
are 32-GPR cores also not yet supported and requiring multilib
extension).

> Unlike the different "avr1" subset, the avrtiny *is* supported by GCC.
> However, avr-libc has limited support.
>
> Compilation for an AVR-tiny processor is identified by the __AVR_TINY__
> symbol.  Large parts of the library are wrapped in "#ifndef __AVR_TINY__"
> and generate an empty object file.  This is the basic requirement for
> AVR-tiny non-support.

Presumably, this was a quick fix in order to get avr-libc built
and to be sure that no wrong code is generated as the 16-GPR cores
come with a different ABI w.r.t argument passing and call-used
registers.

> However, if not too difficult, and makes sense, it's nice to make your
> code usable on AVR-tiny.  This document explains how to do that.
>
> The biggest issue is the fact that registers r0-r15 are not implemented.
> Only registers r16-r31 exist.  Since these registers are used more heavily
> anyway (they're the call-used registers available to leaf functions, and
> can be used with the immediate instructions), many functions fit into
> this limit already.  If your function can't fit, it's a good candidate
> for #ifndef __AVR_TINY__.

Actually only R18-R19, R28-R28 are callee-saved.  R16 (tmp_reg) and
R17 (zero_reg) are fixed registers, i.e. never allocated by the
compiler.  Registers used for argument passing are R25..R18 instead
of R25..R8 for classical AVRs.

> The AVR-tiny calling convention is almost the same as the regular one,
> except that r16 is used for the temporary register instead of r0, and
> r17 is used for the zero register instead of r1.  Note that this means
> that the only call-saved registers are r28 and r29 (the Y pointer).

R18 and R19 are also callee-saved.

> To make this easier in assembly code, the macros __tmp_reg__ and
> __zero_reg__ are defined and map to r0/r1 on non-tiny processors, and
> r16/r17 on tiny cores.
>
> A second large difference is that tiny cores have only one address space.
> They do not have the LPM instruction, but map flash to the data address
> space starting at 0x4000.  Thus, all of avr-libc's *_P() functions
> (which take program memory pointers) are unwanted, and just alias the
> non-suffixed functions.
>
> (TODO: Update the X_lpm macro to use LD in case of __AVR_TINY__)

Maybe the _P versions should not be provided at all; this would only
cause confusion.  Appears that after almost 20 years of progmem,
developers have problems to no more think in those terms ;-) as
everything is fine with a grain of salt in the linker script...

> Another difference in the address space is that the registers are not
> memory-mapped.  This is unlikely to be an issue; this feature is not
> used much on avr-libc.  The address space looks like:
>
> 0x0000-0x003f: I/O registers (instead of 0x20-0x5f on other AVR)
> 0x0040-0x005f: SRAM (yes, 32 bytes!)
> ...       unused
> 0x3f00-0x3f01: NVM lock bits
> 0x3f40-0x3f41: Configuration bits
> 0x3f80-0x3f81: Calibration bits
> 0x3fc0-0x3fc1: Device ID
> 0x4000-0x41ff/0x43ff: Program memory
>
>
> Third, there are the instruction set differences.
>
> AVR-tiny lacks all the usual extensions you might think of (multiply, DES
> round, XMEGA load-and-update extensions), but also:
>
> - LDD Y+offset and LDD Z+offset.  Only X, X+, -X, Y, Y+, -Y, Z, Z+ and -Z
>   addressing modes are allowed.
> - MOVW.  The existing macro X_movw can handle this, however.
> - ADIW/SBIW.  The *only* 16-bit operation supported by AVR-tiny is the
>   address register auto-inc/decrement on LD/ST.  (PUSH/POP are also
>   supported, but the stack pointer is only 8 bits!)  These can usually
>   be replaced by an SUBI/SBCI pair.
> - Any "extended" instructions, ELPR, EIJMP, EICALL.

As stated above, there are (currently unsupported) AVR_TINY that *are*
supporting SBIW at al. and Z+q, Z+q addressing...

> AVR-tiny *does* have LDS and STS instructions for load/store to absolute
> addresses, but they're different from other AVRs.  They are 1-word
> instructions (no worrying about the skip bug!) which specify a 7-bit
> address which is offset by 0x40 between 0x40 and 0xbf.  (This is
> not a limit because available SRAM is much less than 128 bytes.)

There is a limitation for ATtiny40 which has more RAM and where not all
of RAM is covered by LDS / STS.  One way to cope with that limitation
is using -mabsdata option (or absdata variable attribute) to assert
that all RAM (the attributed object) can be accessed by LDS / STS.

A full fledged absolute sections suppport like .zdata, .zbss, .zrodata
would be kind of overkill because that required extra startup code
and more hacking in the compiler.

> It's the lack of base+offset addressing modes which are the biggest
> problem.  Sometimes it's possible to make creative use of the
> auto-inc/decrement instructions.  For example, instead of
>
> ld b0,Z+0
> ld b1,Z+1
> ld b2,Z+2
> ld b3,Z+3
> /* Compute */
> st Z+0,b0
> st Z+1,b1
> st Z+2,b2
> st Z+3,b3
>
> you might be able to do:
>
> ld b0,Z+
> ld b1,Z+
> ld b2,Z+
> ld b3,Z
> /* Compute */
> st Z,b3
> st -Z,b2
> st -Z,b1
> st -Z,b0

This is only possible in assembler.  It will be very hard to teach this
to gcc.

> The lack of adiw can also be a nuisance.  It can be replaced with an
> subi/sbci pair to subtract the negative, but that has the opposite effect
> on the carry flag.
>
> Like other AVR processors with less than 256 bytes of RAM, AVR-tiny only
> implements 8 bits of stack pointer.

FYI, the compiler will still have to maintain a 16-bit frame pointer.

> Unlike other such AVR procssors, AVR-tiny does *not* ignore the high
> byte of the pointer registers.  (That's because they can also address
> the program memory.)
>
>
> == What about avr1? ==
>
> avr1 is a *different* subset and is *not* supported by GCC or avr-libc.
> (Although some people have managed to use gcc by examining its output for
> unimplemented instructions and changing any code that generates them.)
>
> avr1 is included in the attiny11, 12, 15 and 28 processors, and
> the at90s1200.
>
> Although it does include the full 32 registers, in every other respect
> it's a smaller subset than avr-tiny.
>
> To begin with, The registers are mapped to memory addresses 0-31 and
> that's all the RAM you have!  There is no other RAM!
>
> The I/O registers are not memory-mapped; they're in their own
> address space accessible via the IN and OUT instructions.  The low
> 32 I/O registers are also accessible via the SBI, CBI, SBIC and SBIC
> instructions.
>
> There's not much need for rmemory addressing, and so the only addressing
> mode implemented for LD or ST is "Z".  No Z+offset, Z+, -Z, or anything
> using X or Y.  Also, when accessing RAM, only the low byte of Z (r30)
> matters.  r31 is ignored.  (It *is* used by LPM, however.)
>
> Another difference is that it has a 3-level hardware stack (in addition
> to the current PC).  There is no visible stack pointer, and no "push"
> or "pop" instructions.  (If you underflow the stack, the top entry gets
> duplicated.)
>
> Needless to say, there is no MOVW, ADIW or SBIW.  There is also no LDS
> or STS at all.  (Use the MOV instruction instead!)

avr1 is so antique -- no need to support is now :-)  Except you want to
do it for the purpose of completeness.  But as there's no RAM,
applications for these devices will likely use fixed GPRs as with
-ffixed-N to take it away from the compiler; hence it's likely we
see project-specific ABI, hence no real support by gcc possible.

Johann




_______________________________________________
AVR-libc-dev mailing list
[hidden email]
https://lists.nongnu.org/mailman/listinfo/avr-libc-dev
Loading...