AVR 16-bit register transfers - Page 1
Forum section: electronics / general

author: chris_leyson
author_rank: Super Contributor
timestamp: July 31, 2018, 10:52:51 pm
content: As I'm going to use an ATtiny with a 16-bit timer in a project there will be a few instances when I have to transfer 16-bit values between timer registers. The ATtiny datasheet says, for a 16-bit read, read the low byte followed by the high byte, the high byte is automatically read and stored in a "transfer" register when the low byte is read. Similarly for a 16-bit write, write the high byte first to the transfer register then write the low byte to the timer register and at the same time the high byte is copied from the transfer register.In short, you can do a 16-bit transfer just by reading and writing the low byte as the high byte is held in the "transfer" register. A 16-bit register move becomes an 8-bit read/write, for example, OCR1AL = TCNT1L works because OCR1AH and TCNT1H and stored in the transfer register. This only works if the two 16-bit registers share the same transfer register. If you are stuck for program space or speed it's a neat trick, nicely thought out, thanks Atmel.

author: T3sl4co1l
author_rank: Super Contributor
timestamp: July 31, 2018, 11:38:51 pm
content: ASM or C?It's done automatically in C.Tim

author: chris_leyson
author_rank: Super Contributor
timestamp: August 01, 2018, 12:31:06 am
content: QuoteI somehow don't expect that from the "Arduino" libraries. So I tried OCR1A = TCNT1 instead of OCR1AL = TCNT1L and it used an extra 12 bytes, sort of makes sense, that's and extra register to save and recall plus reading and writing the high byte, damn that doesn't add up to six instructions. I put something together quickly with the Arduino IDE just to check out the logic but I'm going to have do this in assembler anyway as it's an ATtiny.

author: westfw
author_rank: Super Contributor
timestamp: August 01, 2018, 02:24:21 am
content: C automatically writes the 16bit registers in the correct order.AFAIK, it doesn't optimize a 16bit transfer to an 8-bit on, as Chris describes.However, the xx8 datasheet says:QuoteSo beware...

author: sleemanj
author_rank: Super Contributor
timestamp: August 01, 2018, 06:01:33 am
content: Quote from: chris_leyson on August 01, 2018, 12:31:06 amIf you want.But you can code for ATTiny's using the Arduino IDE, myfork of ATTinyCore for x5/x4,13,10,9,5 and 4is pretty efficient space wise (constant pin operations etc compile to a single instruction where possible).OrATTinyCore itself for various others.

author: Siwastaja
author_rank: Super Contributor
timestamp: August 01, 2018, 07:33:11 am
content: Quote from: chris_leyson on August 01, 2018, 12:31:06 amIt's irrelevant whether you expect it or not - it just happens. This is because Arduino libraries have no part in it at all; avr-gcc is performing this. If the Arduino folks were at it, I'd guess it would be a kilobyte of code running for a millisecond in a 1000-line switch-case.I strongly recommend you access these 16-bit registers with their combined names (OCR1A etc.), since GCC has done the access to these registers correctly and reliably forvery long time, saving us from countless of bugs if you do the read or write sequence wrong - which easily happens, since we do make mistakes.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: August 01, 2018, 02:31:38 pm
content: Quote from: Siwastaja on August 01, 2018, 07:33:11 amDon't you mean avr-libc? It's the C standard library, not the compiler, doing that.

author: Siwastaja
author_rank: Super Contributor
timestamp: August 01, 2018, 02:47:37 pm
content: Quote from: HwAoRrDk on August 01, 2018, 02:31:38 pmI don't think so, clearly the library only defines the type, address and the name for the 16-bit registers, and can assign attributes to them to give information to the compiler - but that's it. With direct assignment operator and no operator overloading in C, the library even theoretically couldn't do much to ensure the correct reading sequence for these special registers.So, the compiler needs to understand how these types of registers are accessed correctly, and avr-libc documentation seems to confirm this:https://www.nongnu.org/avr-libc/user-manual/group__avr__sfr.html"The compiler will choose the correct instruction sequence to generate based on the address of the register being accessed."

author: HwAoRrDk
author_rank: Super Contributor
timestamp: August 01, 2018, 10:04:24 pm
content: Ah yes, you're right. I assumed it was entirely macro trickery.Now I trace the actual implementation, I see that 16-bit registers just evaluate to_SFR_MEM16, which is just an alias to_MMIO_WORD, which is simply(*(volatile uint16_t *)(mem_addr)).

author: westfw
author_rank: Super Contributor
timestamp: August 01, 2018, 10:29:23 pm
content: Y'all are missing the point.Yes, avr-gcc properly handles the 16-bit register access by reading low-byte first and writing high-byte first:Code:OCR1A = TCNT1;0: 80 91 84 00 lds r24, 0x00844: 90 91 85 00 lds r25, 0x00858: 90 93 89 00 sts 0x0089, r25c: 80 93 88 00 sts 0x0088, r24Chris's claim is that you can optimize this to:Code:lds r24,TCNT1L sts OCR1AL, r24because the load from the low register also sets the High "temp transfer register", and the store to the other low regsiter also sets the high byte FROM the temp transfer register. avr-gcc does NOT do that.I'm not sure that it actually works (seehttps://www.eevblog.com/forum/microcontrollers/avr-16-bit-register-transfers/msg1714901/#msg1714901), and I'm not sure I see when it would be useful, but it's a neat observation.