Hardware XOR For Output Pins On AVR Microcontrollers

Did you know that most AVR chips have a type of hardware exclusive OR (XOR) option when it comes to the logic levels of the output pins? If you look in the datasheet (the image above is a screenshot from an ATtiny13 datasheet) you’ll find a section on Toggling the Pin. It turns out that if you set a PORT as an output, writing logic one to the corresponding PIN register will toggle the logic levels of that out. This is really easy to overlook if you’re writing in C, but I’ve been working on learning a bit of assembler language and found this to be very useful. Keep reading after the break and I’ll tell you how I happened upon this info and what it’s good for.

So first off, let’s talk about why this doesn’t matter very much if you’re writing in C code. Normally if you want to toggle some output pins you’ll just write a one-liner that XOR’s with a bitmask:

PORTB ^= 0xFF;

This is a bit of C shorthand (learn more about that from my tutorial series) that performs the XOR on the current output levels and writes the result back to the port. But the same thing can be done in hardware by writing the bitmask to the PINB register:

PINB = 0xFF;

You don’t really care, because it’s just one line of code. In fact it’s probably easier to XOR the PORTB because it makes more sense conceptually. But the compiler might end up using more cycles than if you had written to the PIN register.

I happened across this feature because I was blinking some LEDs as a way to learn assembler. I had this jumble of code in an Interrupt Service Routine:

ldi myReg, 0xFF
in intReg, PORTB
eor intReg, myReg
out PORTB, intReg

It loads a bitmask into one register, loads in the current logic from PORTB to another register, performs an XOR of the two, and writes the result back to PORTB. This takes four cycles and requires two registers. Toggling bits is such a rudimentary operation I was surprised there wasn’t a command to XOR bits directly so I started searching around. I came across this article over at AVR Freaks which clued me into the bit toggle feature. Now I was able to reduce my assembler code as follows:

ldi intReg2, 0xFF	;temporarity use intReg2 as a bit mask
out PINB, intReg2	;writing to PINB effectivley does an Exclusive OR on PORTB

This results in the same toggling effect, but takes just two cycles and requires the use of only one register.

What I found most interesting is that no matter how much I use AVR chips, there ‘s never a shortage of surprises waiting to be found in the datasheet.

30 thoughts on “Hardware XOR For Output Pins On AVR Microcontrollers

  1. On the same note: Did you know something like
    PORTB|=(1<<LEDA)|(1<<LEDB);
    generates more code than
    PORTB|=(1<<LEDA);
    PORTB|=(1<<LEDB);
    ?

    The first translates to an IN instruction, an OR instruction and an OUT instruction, making for 3 instructions in total. The second one gets optimized to two SBI instructions and is one instruction shorter.

  2. @Alex Parting:
    Datasheets are close to 500 pages for some chips. It’s really easy to overlook these things if you’re just skimming through, and reading the document cover-to-cover probably isn’t very productive at all.

  3. jc,
    It appears to be the same on at least the mega328, mega644, and mega2560 and their respective families as well as the tiny4. Based on the datasheets I have in front of me.

  4. Tricks like this are what separate people like me who think in C and expect the compiler to know what to do, and the folks who know their brand of chip inside-out.

    And the real pros know which of those instructions have erratas for when certain values are undefined.

  5. That’s a cool trick, nice to know.

    Note however that this does not work for every AVR. For example on ATMega16 the PIN* registers are read-only and the datasheet for not mention this behaviour. So keep this in mind if writing portable libraries.

  6. The language is called assembly. Only the program which translates it to machine code is called AN assembler. Unless you also want to tell people that you’re not learning C, you’re learning C compiler linker. /petpeeve
    Otherwise nice article.

  7. There are two types of programmers: those who count cycles and write very obfuscated code; and then there are those that just write clearly documented code and don’t bother counting cycles.

    I’m clearly in the latter category. There are *VERY* few instances where I would care about saving one or two cycles. The XOR hardware will never get used because it makes defining whether the pin is a 0 or 1 excessively vague. In my opinion, this is much more likely to result in a bug in the firmware than it is to be useful.

  8. I’ve read several PIC mcu datasheets nearly cover to cover I ignore the dimension diagrams etc and most block diagrams.

    They are long but they stand you in good stead for ever. In terms of becoming idiomatic, picking up tricks like this but just understanding the architecture.

  9. @anti-fanboi:
    So I searched for Assembler – 1209 results.
    Then I searched for Assembly – 37982 results.

    From the results, it seems “Assembler” is an outdated term.

    Thanks for proving Gordos point. What is next, Wikipedia?

  10. Outdated? perhaps. what’s next, are you going to make the point that “hackers” are those that cheat in online games? The point was that Assembler can refer to the language not the popularity of the term. The fact you found more than a few proves my point. The number of results as a method of “proof” is obviously/demonstrably infantile. Pft! kids! *roll eyes*

  11. @anti-fanboi:
    Gordo: “The language is called assembly”
    anti-fanboi: “You are wrong, search Amazon for ‘assembler'”
    Me: *points out ‘assembly’ is much more common than ‘assembler’ on Amazon*
    anti-fanboi: “Using Amazon that way is infantile’

    By the way, the language is called assembly. Not disputing that assembler has been used.

  12. Using XOR to flip bits and to zero out registers was a commonly used idiom in IBM System 360 Assembly language and right through to System z. You usually learned this by reading others’ code.

    As Gordo points out, an assembler is a program that translates assembly language into machine code. The term is still very much in use as defined. If you go looking for an assembler manual and think it will teach you assembly language, you will be very disappointed. An assembler manual is a reference on how to use the assembler.

    I made that mistake when I first started learning 360 assembler.

  13. @hpux735: The suggested optimization (replacing IN…OR…OUT… with SBI…SBI…) is not valid. A compiler can’t optimize in a way that sets a volatile register in an unexpected state.

    Regarding XOR: Another common use of xor is to obtain zero (xor regn regn) because embedding constants in machine code is often more expensive. I don’t know if this applies to AVRs.

  14. @Ken

    It really depends on how the AVR is defined in the context of the C compiler. If the two operations are truly equivalent then the compiler can optimize.

    As an aside, that is one thing that I don’t like about the AVR. I don’t want a pinx and portx register… Just give me a portx like the PIC and do what I want based upon whether I’m reading of writing to it.

  15. hpux: Sorry, but the ability to trick your compiler into making unsafe IO optimizations is very different from saying that a good compiler should do this by default. Since the compiler can’t predict who will be watching the IO pins, it must not allow them to enter an inconsistent state, even for a cycle or two.

    Ken (compiler writer)

  16. I haven’t tried this but why can’t you just re-write the original value, rather than 0xFF, to XOR?

    E.g.

    out PORTB, intReg

    out PORTB, intReg

    rather than:

    out PORTB, intReg

    ldi intReg2, 0xFF
    out PORTB, intReg2

    ?

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.