Misc registers for GP?

Go To Last Post
27 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Beyond the GPIOR's... what's the consensus on using other registers for general purpose?  For example the OCR1x's if Timer/Counter1 isn't being utilized?  Are there side effects of storing data in such places?  This is not regarding any specific AVR.  Thank you.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

No problems as long as you choose your registers carefully. They must have full R/W access and contain no status bits that might change.

 

But, to be blunt, if you are that short of memory then you have chosen the wrong chip.

#1 This forum helps those that help themselves

#2 All grounds are not created equal

#3 How have you proved that your chip is running at xxMHz?

#4 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand." - Heater's ex-boss

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If it's only to get an extra RAM byte or two  : yes go for an other chip (if you can).

 

If its a register that have access via in an out it could make sense to make faster and smaller code.(lower 64 IO addr)

 

And if you also have bit access (lower 32 IO addr) it could be even better.

 

But you better make some VERY good comments in the code , else it will soon start to bite you ;)

Last Edited: Sun. Dec 30, 2018 - 04:23 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks.  Awhile back I read about EPROM, FLASH, and SRAM access.  The datasheets indicate lower registers can be accessed with bit specific instructions.  Besides that it made me curious if there are performance benefits in using other registers for some high traffic variables too, vs. SRAM.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On old AVRS TWAR is often a good choice if you aren't using TWI. You can cast eight 1 bit vars onto it for SBI/CBI access.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Taking this a little further, is it possible to create a union of values stored in multiple registers, similar to below?  I attempted this but got stuck when the compiler thew "expected identifier before '(' token" on the last line where I left out the register specification because I didn't know what to put there.

 

typedef
    union MyUnion
    {
        struct
        {
            unsigned char loByte:8;
            unsigned char hiByte:8;
        };
        uint16_t bothBytes;
    } MyValue;

#define low  ( (volatile MyValue*)(&GPIOR0) )->loByte
#define high ( (volatile MyValue*)(&GPIOR1) )->hiByte
#define all  ( (volatile MyValue*) )->bothBytes

This seems to work if the data all fits within the same register, similar to below...

 

typedef
    union MyUnion
    {
        struct
        {
            unsigned char loNib:4;
            unsigned char hiNib:4;
        };
        uint8_t bothNibs;
    } MyValue;

#define low  ( (volatile MyValue*)(&GPIOR0) )->loNib
#define high ( (volatile MyValue*)(&GPIOR0) )->hiNib
#define all  ( (volatile MyValue*)(&GPIOR0) )->bothNibs

This results in the byte being accessible by MyValue.all and the nibbles by MyValue.low abd MyValue.high.  I read a lot of warnings about unions and member accessibility but it seems to work okay so far in my testing with Atmel Studio 7.

Last Edited: Wed. Jan 2, 2019 - 05:26 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But GPIOR0 and GPIOR1 are not adjacent??

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Good thought... In this instance GPIOR0 and GPIOR1 would seem to be at 0x2A/0x2B respectively (Atmega 168).

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

An interesting assertion, as it is GPIOR1 and GPIOR2 at those addresses.

 

While those addresses are indeed within range of single-cycle IN/OUT, that has limited value without bit access.  For that, it is only GPIOR0 in range of SBI and friends.

 

Atmel didn't do us punters many favors when the "new and improved I/O register map" was introduced, which includes Mega88-family.  See all the gaps in the precious low address/bit-addressable space, to accommodate models with more ports? 

 

Summary IME:  Yes, GPIOR0 is useful.  Beyond that, except in isolated instances, there won't be much to gained with your quest. 

 

 

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

Last Edited: Wed. Jan 2, 2019 - 10:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rooney wrote:

Beyond the GPIOR's... what's the consensus on using other registers for general purpose?  For example the OCR1x's if Timer/Counter1 isn't being utilized?  Are there side effects of storing data in such places?  This is not regarding any specific AVR.  Thank you.

 

Be careful with 16 bit registers, accessing them can have some quirks.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

theusch wrote:

An interesting assertion, as it is GPIOR1 and GPIOR2 at those addresses.  While those addresses are indeed within range of single-cycle IN/OUT, that has limited value without bit access.  For that, it is only GPIOR0 in range of SBI and friends.

 

Looking through datasheets of the AVR's I've been tinkering with lately two have GPIOR1,2,3 at (Tiny861)0x0A,0x0B,0x0C and (Tiny1634)0x14,0x15,0x16.  

 

El Tangas wrote:

Be careful with 16 bit registers, accessing them can have some quirks.

 

Would you mind elaborating a bit or pointing me in the right direction?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The TCNTn, ICRn, and OCRnX registers of 16-bit timers

use a temporary register for the high byte when reading

and writing so the 16-bit read/write appears to be atomic.

 

This temporary register is shared so you need to disable

interrupts when reading or writing.  Otherwise an ISR

could possibly change the value in the temporary register.

 

See "Accessing 16-bit Registers" in the datasheet.

 

--Mike

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

May I ask why you want this ?

 

In general it would be better to dedicate some registers (like R2 and R3) for what you try to do.

 

And if you use a mega168 the registers are memory mapped so you can use a pointer.  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rooney wrote:
Would you mind elaborating a bit or pointing me in the right direction?

When you open the datasheet for your Mega168, what does it say in the section "Accessing 16-Bit Registers"?

 

As you are on this quest and mentioned register pairs, which ones are they?  What does the datasheet have to say about access in the register description?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

sparrow2 wrote:

May I ask why you want this ?

 

Sure.  This particular question is exploring the possibility of using a register pair to more efficiently work with values greater than 8-bit. The reason I'm here is because I'm fairly new to AVR's and although I have read the datasheet(s) it will take time, experience, and reinforcement for much of that information to snap for me.  Maybe what I'm wanting is a fruitless effort, if so, I would appreciate being told.

 

My first thought is that a Union would be a convenient method if I could get the the members to lock against registers (any two registers for now).  I think I managed that last night but might be having endian issues, I need to spend some more time looking at that.

 

sparrow2 wrote:

In general it would be better to dedicate some registers (like R2 and R3) for what you try to do.

And if you use a Mega168 the registers are memory mapped so you can use a pointer.  

 

I used the Mega168 as a generic reference as it's one of the more common target devices I used but the less popular Tiny 1634 seems to be what I am gravitating to now.  Until recently I've not coded against hardware in a sense of needing to be too considerate of resource limitations or accessing registers.   If this doesn't play out then at least it was a good exercise.  If it shows merit then I'll explore performance benefits to potentially work into a project that I've been trying to optimize.  Whether or not this helps with that project isn't relevant now, currently this is an exercise that I would just like to explore.

 

I've very much tried to do my homework and keep my questions to a minimum but I suppose that's what online communities like this are for, and I appreciate everyone's input.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why do you need a union? If you want to access a location wider than 8 bits you do... 

uint16_t * p16;
uint32_t * p32;

p16 = (uint16_t *) 123;
p32 = (uint32_t *) 456;

*p16 = 12345;
*p32 = 0xBABEFACE;

The compiler will generate efficient access code. I put my 16 bit variable at 123 but it could just as easily have been & GPIOR1 then the combined (adjacent) R1 and R2 locations would be used for it. 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

PS Oh and if I wanted to make byte wise access to one of those I could do something like...

uint8_t * p8;
p8 = (uint8_t *) p32;
p8[2] = 0xF0;

Now BABEFACE is BABEF0CE

 

It seems to me you are trying to outsmart the compiler before you understand what it is capable of natively. 

Last Edited: Thu. Jan 3, 2019 - 02:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Why do you need a union?

 

clawson wrote:
 It seems to me you are trying to outsmart the compiler before you understand what it is capable of natively. 

 

Thanks, I'll examine the methods you mentioned.  The reason I 'need a union' and am unintentionally 'trying to outsmart the compiler' is because I'm beyond my area of knowledge... which is kind of why I'm here.  I put in a lot of time doing due diligence prior to bugging you folks with questions and am working with the best info I have at the moment.  I busted my butt trying to avoid the ever so common 'go read your datasheet' response... but go ahead, I'm good for it!

 

Thanks again for that info, I'll check it out.

smiley

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Rooney wrote:

Beyond the GPIOR's... what's the consensus on using other registers for general purpose?  For example the OCR1x's if Timer/Counter1 isn't being utilized?  Are there side effects of storing data in such places?  This is not regarding any specific AVR.  Thank you.

You may not know you can ask the compiler to reserve general registers for a variable (not R0 or R1) - e.g.

register uint_fast32_t accumulator asm("r2");

reserves R2-R5 for the variable 'accumulator'. Anything more than a byte, the register mentioned has to be even numbered. I have trouble figuring out how far you can go with this, a simple program I wrote used R2 thru R9 no problem, but there are warnings that library routines may clash with this. Someone else may know the whole story.

AFAIK you can use any register that is designated R/W, is not part of a 16-bit operation, is not used for its intended purpose, and a read of the specifications satisfy you there are no unintended side effects.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ajcashin wrote:
Someone else may know the whole story.
Well the issue is this. When you invoke avr-gcc to build some code you can use:

 

avr-gcc -ffixed-2 -ffixed-3 -ffixed-4 -ffixed-5 ...

 

That says "don't generate any access to r2..r5" but the problem is this. When your copy of AVR-LibC was built and the compiler built strcpy() and printf() and stuff then at that time it was not told -ffixed-2 (etc) so it didn't know it was supposed to avoid using r2 etc. So there's every chance (especially in the more complex stuff like printf() and the float support for +-*/ and so on) that the generated code has use of some of the low registers. So if you built a program relying on setting aside 2/3/4/5/... but then that program goes on to call printf() or do a float divide or something and it links to code from the library that is using R2 then the contents will be corrupted.

 

For this reason I would always advocate using things like GPIOR0 first to hold "fast access" data. Only revert to asm("r2") when you have run out of other resources.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

So there's every chance (especially in the more complex stuff like printf() and the float support for +-*/ and so on) that the generated code has use of some of the low registers.

It's annoying that documentation is not clear. accurate and complete. I guess I'm a control freak, I write mainly in assembler because nothing is left to chance. If it doesn't work I know who created the problem. When I look at the gcc toolchain, I am awestruck that people are able to get such a complex beast to work relatively seamlessly. But then I look for documentation and it's a nightmare. The answers are mostly out there but there's no logical way to find them. One could go to the library source, compile it, read the assembler and answer the question but I can't fork an extra brain to run in the background so it goes in the too hard basket.

clawson wrote:

For this reason I would always advocate using things like GPIOR0 first to hold "fast access" data. Only revert to asm("r2") when you have run out of other resources.

Mostly agree. I would expect the asm("r2") method to be portable to any [most?] AVR 8-bit targets. The use of the other registers is processor dependent. Rock and a hard place.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ajcashin wrote:
But then I look for documentation and it's a nightmare.
I think you are being a little unfair - GCC has a comprehensive manual, so does AVR-LibC. What exactly is it you were hoping to find in the manuals anyway? The manuals tell you how the tools are invoked - it does not cover the infinite number of scenarios that they might then be used for. It's like when you buy a car and it comes with a book that tells you how to turn the ignition on and how to select 5 gears. What it doesn't tell you is the fastest route from Ipswich to Gretna Green !
ajcashin wrote:
. I would expect the asm("r2") method to be portable to any [most?] AVR 8-bit targets.
Well no - the so called "brain dead" Tiny AVR chips are "brain dead" in the sense that they only have R16..R31. There is no R0..R15.

 

To be honest I never really got the code portability thing (with micros) anyway. You pick a Tiny13 or an Xmega384A1  because you either wanted a micro with low pin count and limited resource just to cover some very niche task or a behemoth with tons of pins and peripherals aand other resources. You then write code to fit one or the other. Is there ever really a prospect of migrating your X384 project to a T13 at some stage (or the other way?). Of course in this case the Xmega user is the winner because that micro has sixteen GPIORn registers !!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

And I will add:

 

on a Xmega384A1 I would let the compiler do the work, and use all the registers, so libs etc. will work

 

On a tiny13 (other than I personally would write ASM code for a small chip) there are a limit of librarys that would work. And there I would reserve registers if needed (for code size, and/speed , and for chips with low RAM count also minimize RAM use).

 

I would personally like the GCC to be able to ADD two 32bit variables a byte of the time, but it will always use 8 registers for that function (I know it a general part of C that the result always should be available but the optimizer could/should have the option )  

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So if you built a program relying on setting aside 2/3/4/5/... but then that program goes on to call printf() or do a float divide or something and it links to code from the library that is using R2 then the contents will be corrupted.

If you restrict yourself to reserving call-saved registers only, then library code will preserve them when needed.

 

The 'gotcha' is when you use your reserved registers in an ISR.  If the ISR can break in during such a library call, it may find that the contents of it's reserved registers have been replaced with something else.  This will break both the ISR, and later if the ISR modifies those registers, when the ISR returns it will break the interrupted library code.  A solution is to make all calls to such library code atomic, but that's not always easy (or even possible...), and negates the usual reason for reserving registers i.e. ISR speed.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"Read a lot.  Write a lot."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

It's like when you buy a car and it comes with a book that tells you how to turn the ignition on and how to select 5 gears.

When you get the book with the car, and you want to change the rear left tail light, you go to the index and look up lights/tail/replacing and it says Pg 74. Try the same with software today and there is no comprehensive index so you go to Dr Google and get back 6287 hits many of which are totally off topic, many are duplicates of the off topic reference, many are people asking the same question and not getting an answer, but somewhere in there you might get the hit you want. As I said the information is out there somewhere but, short of reading all the documentation from beginning to end, it isn't easy to reference. I worked with DEC equipment for a while and there was a bookcase that could answer just about any question you wanted. There was whole books of error messages and what they mean. I get the logs from my router booting, it throws up heaps of messages and finding out what most of them mean is almost impossible. You could argue I don't need to know, but sometimes there are glitches and you want to know why. I worked for a large company for a while, managing 75 servers across the world. All the logs were collected daily, munged by scripts I wrote, condensed and sieved, and I got a report that basically said "all is well" or "here are some interesting anomalies". I could do that because each of the log messages was well described and their importance could be assessed. I've tried that with Linux, gave up.

 

Maybe I've had life too easy. Here endeth today's rant.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

ajcashin wrote:
You may not know you can ask the compiler to reserve general registers for a variable (not R0 or R1) - e.g.

register uint_fast32_t accumulator asm("r2");

reserves R2-R5...

 

THANK YOU!  This was very helpful to and solved my need with a slightly different approach.  Gold star for you!

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Note you should also be invoking the compiler with -ffixed-2