Leave "my registers" alone.

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

I reserved some registers that are used in an Asm file with:

register unsigned char reform_cntr asm("r4");
register unsigned char rownum asm("r5");
register unsigned char row asm("r6");

which should be safe according to the manual.

As the project grew I moved the above into a separate file along with other C functions that related to the ASM file.

Things were working well but slowly deteriorated with absolutely crazy results at time...which I put down to me forgetting a comma, a semicolon or other, which of course wasn't.

As it turned out winAvr was taking so many liberties with R6 that I had to get a pregnancy test for it.... :?

Moving the registers reservation back into main fixes the problem for now. I can still see R6 being used by vprintf and maybe other places by I hope it is being pushed and popped as required.

The question is: Do I need to add the registers reservation lines to all C files in the project or just leave it in main? It is possible that other functions in other files start to mess around with reserved registers I suppose without it or I may get double definitions or other issues.

Tried to add those line to the header file of the module that uses the registers but winAvr complained.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

The "register" keyword is just a hint to the compiler, and falls down in the face of avr-libc, which has been precompiled for you with no knowledge of the reservation, so it will use those registers freely.

Unless you want to recompile a version of avr-libc with the register reservations in place, you should code assuming that all registers can and will be overwritten by other functions.

Don't forget your AVR's GPIOR registers if you need to store state across multiple calls to your ASM code.

- Dean :twisted:

Make Atmel Studio better with my free extensions. Open source and feedback welcome!

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

That's NOT what I want to hear.. :evil:

As I said it all works for now and I have no problem with precompiled functions using "my registers" as long as they are pushed and popped (not pooped) before and after use as they should be.

I can change the ASM file to use 3 ram locations instead of the 3 registers but that means 2 more instructions every time I need to use any of them and that would happen every 2.5ms. Maybe not a big deal.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

Last Edited: Wed. Sep 15, 2010 - 11:05 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

As it turned out winAvr was taking so many liberties with R6 that I had to get a pregnancy test for it.... Confused


Quote:

forgetting a comma, a semicolon or other,

The pregnancy test would be related to missing a period, versus the comma or semicolon.

Lee

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

Quote:
The "register" keyword is just a hint to the compiler, and falls down in the face of avr-libc, which has been precompiled for you with no knowledge of the reservation, so it will use those registers freely.
Does this not also applies to your own code? If the register reservation is done in just one file, the other files don't know about it either.

Regards,
Steve A.

The Board helps those that help themselves.

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

I don't know where your asm file gets executed, and I may be wrong, but in addition to you expecting the compiler to be saving 'call-saved' registers, it also expects you to do the same (which defeats the reason for using registers). So it could be that you are screwing up the comnpiler, instead of it screwing up you :)

My thinking could be wrong, but lets say your asm code gets 'called' while the compiler already has used 'your' call-saved registers previously in the same c function. When returning from your asm code, the compiler thinks all call-saved registers are unchanged, and uses these (cached) registers again. If you didn't save/restore them in your asm code, the compiler will then be working with incorrect values in those registers.

Ok, I'm probably confusing myself and everyone else.

For 2 instructions every 2.5ms, I would ditch the registers.

(I think if the asm code is in an irq, you are out of luck as you would have to push/pop those registers, but if it is a called function, you may be able to call it via inline asm using a 'memory' clobber to inform the compiler. But I don't know.)

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

Quote:
it could be that you are screwing up the comnpiler
I'm playing nice with the compiler, I push and pop all registers used in the ASM file.

The function is called by the main code once the timer interrupt signals the 2.5ms time flag, currently nothing else happens during the execution and the ASM function takes maybe 300us to execute at 7.32MHz.

Unless I have other feedback I'll ditch the registers and be wastefull like a C compiler. :)

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

theusch wrote:

The pregnancy test would be related to missing a period, versus the comma or semicolon.

that was so bad it made me laugh :P

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

abcminiuser wrote:
The "register" keyword is just a hint to the compiler...

When used as a storage class qualifier for local (automatic) variables, that's true. When used on a global context as John appears to have done, the compiler really does have no choice but to map those registers into your variables. The problem you mentioned of potential interactions with precompiled avr-libc functions still stands though.

1) Yes, in general your code will be less prone to bugs if your register reservation is visible in every single C module you write, regardless of whether or not the reserved variables are actually used by any particular module. You might get more optimal results if you limit the set of modules in which the reservation is visible, but you must take care not to take it too far or you'll run into problems.

2) As long as you are not using preemptive task switching, and as long as your reserved variables are never accessed within any interrupt context, and as long as you are limiting your reserved register variables to the call-saved set of registers, any interactions with precompiled avr-libc functions should be benign.

3) The direct call-return path between any functions which use your reserved register variables and main(), must never take any intermediate side-trips outside the source module(s) in which you've made your register reservation visible.

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

Remind me again what's gained by tying the variables to registers in this particular example anyway? Presumably a few LDS and STS? But are the variable usages THAT time critical? If they're global they are presumably used in more than one function - could you not get more benefit by inlining the smaller functions into the larger anyway?

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

theusch wrote:
Quote:

As it turned out winAvr was taking so many liberties with R6 that I had to get a pregnancy test for it.... Confused


Quote:

forgetting a comma, a semicolon or other,

The pregnancy test would be related to missing a period, versus the comma or semicolon.
He did say the problem was *NOT* a missing comma or semicolon.

The reserved registers are in the call-saved list.
That means that the callee will restore them if necessary.

"SCSI is NOT magic. There are *fundamental technical
reasons* why it is necessary to sacrifice a young
goat to your SCSI chain now and then." -- John Woods

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

Quote:

that was so bad it made me laugh Razz

Thank you, Geoff--the other readers were smart enough to ignore that one, for fear it would just encourage me.

Lee

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

Quote:
But are the variable usages THAT time critical?

Quote:
but that means 2 more instructions every time I need to use any of them and that would happen every 2.5ms.
I would say no. It sounds more like an old assembly programmer trying to assert his masculinity ;)

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:
what's gained by tying the variables to registers in this particular example anyway
That's working ASM code and that's how it was written. Pretty much all variables were registers with ram being just used for buffers.

No point reading a variable from ram, modifying/using it, put it back into ram if it is not necessary.

The code is driving a dot matrix led display and is doing a lot of bit manipulations to generate row info every 2.5ms for up to a 25 digit display.

I have stripped down pretty much all asm code but the bare core with some simple functions being re-written in C. I may attempt to rewrite the core code one day, but it' all working well for now if one "takes precautions" as in other aspects of life... :?

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Use this options for compile each file:

-ffixed-r4 -ffixed-r5 -ffixed-r6

Thish does not work for registers r1, r2 and from r18 upto r31. Registers from r8 upto r17 may be used in avr-libc. In any case check .lst output if you use registers.

Ilya

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

Quote:

Thish does not work for registers r1, r2 and from r18 upto r31.

But this does not help with the fact that libgcc.a, libc.a and libm.a were not built with these options. It'd be necessary to either rebuild the library code or avoid using library functions.
Quote:

No point reading a variable from ram, modifying/using it, put it back into ram if it is not necessary.

The optimiser already knows this and will keep variables in registers only when it can. Ironically this is why we see so many complaints here about the "watch window in Studio doesn't work" because variables in SRAM are not being updated within a routine.

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

Quote:
I have stripped down pretty much all asm code but the bare core with some simple functions being re-written in C. I may attempt to rewrite the core code one day, but it' all working well for now if one "takes precautions" as in other aspects of life..

If your function works ok in ASM, leave it that way.
i.e. save all your registers, call the ASM, restore the registers.

If you start mixing C in with your ASM, you will have to do extra register preservation. C knows that it can trash some registers.

Personally, I would re-write the ASM in C. I doubt very much that efficiency is an issue. If it is, I bet you can reduce the ASM down to twenty odd lines and still get good performance.

I really dislike the idea of forcing the Compiler into a corner. Reserving registers globally may be advantageous, but I doubt that it makes much difference to your overall performance. And as you have discovered, it gives you lots of extra grief.

David.

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

Quote:
libgcc.a, libc.a and libm.a were not built with these options.
So let's forget about what I'm doing for a second and think about in line asm, would not the same problems occur? Surely any library function would preserve any registers used, otherwise one would have a complete mess.
Quote:
dislike the idea of forcing the Compiler into a corner.
Just showing it who's the boss!! :lol:
Quote:
it gives you lots of extra grief.
Waddaumean grief? This has been fun. :wink: the main challenge has been to keep the main C loop very fast so that I don't get any flicker on the display, no time to slow down and smell the roses.

I need to absorb what this thread (and others) https://www.avrfreaks.net/index.p... say about the carry bit before I convert it all to C. I have 25 bytes of row info that carries 6 bits of real data per byte to turn into 19 bytes of data, ready for the SPI, by shifting/rotating registers.

John Samperi

Ampertronics Pty. Ltd.

www.ampertronics.com.au

* Electronic Design * Custom Products * Contract Assembly

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

Quote:
Surely any library function would preserve any registers used, otherwise one would have a complete mess.

That is the key part, because the answer often is NO.
The compiler has some defined scrash(spell ?) registers that anyone freely can use, and don't make any push and pop needed.
It's only when we talk about ISR that you need to store everything (and reentrant code) that everything needs to be saved. And that is often just a push pop around the function (Win AVR seems to push all legal reg where other toolchains try to keep track of what is needed).

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

Quote:
That is the key part, because the answer often is NO.
This is not true. There are well defined responsibilities for both the calling function and the called function as to which registers each has to preserve. The library functions surely save the registers that they are responsible for. If the calling function needs to preserve registers other than that, it is its responsibility, not the called function.
Quote:
Win AVR seems to push all legal reg where other toolchains try to keep track of what is needed
It pushes only what it knows about. The problem can come when you make a function call within the ISR. In that situation the compiler may have no knowledge of what it needs to save, so it must save all that might be used.

Regards,
Steve A.

The Board helps those that help themselves.

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

Quote:
I have 25 bytes of row info that carries 6 bits of real data per byte to turn into 19 bytes of data, ready for the SPI, by shifting/rotating registers.

I would write it in C. See what the Compiler produces. Hand optimise the generated ASM if necessary.

Job done. Most importantly I have a portable C function, and a specific AVR (ASM) function.

Now, since I am a suspicious so-and-so, I would then compare with your original ASM performance.

By the sound of it, your time critical sequence is minimal. It is often the case that only the inside loop is critical. 99% of the code is insensitive, principally because it is only executed a small number of times.

Yes. You can mix library, C, ASM as much as you like. You just have to do an awful lot of reading. i.e. you must obey all the calling conventions, register use etc.
I am quite happy to use the 'hand-optimise' method with a completely foreign CPU. i.e. you do not need to be an ASM expert, just need a list of CPU opcodes.

David.