[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[avr-libc-dev] Supporting AVR_TINY, draft HOWTO

From: George Spelvin
Subject: [avr-libc-dev] Supporting AVR_TINY, draft HOWTO
Date: 9 Dec 2016 22:03:14 -0500

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

(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

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

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

reply via email to

[Prev in Thread] Current Thread [Next in Thread]