This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

NOAREG & AREG,

Hello,
I'm having trouble understanding the NOAREG, AREG & USING options. I'm also confused with R0-R7 & AR0-AR7.
I've read the support files and manual but i'm still confused.
May I know
a) How is it that using NOAREGs will allow me to disregard register bank currently used when writing function? ( will the registers be automatically saved to stack? )
b) What is the difference with R0 and AR0? How does the USING option affect R0 & AR0? (for example if i put the option "USING 2", have then use R4, does this mean that it is the actually R20 ie in bank 2, whiel AR4 is not affected by the using function)
c) How does USING help me to save stack space?
Best Regards,
AL

Parents
  • The 8051 family of processors have four register banks each of which contain eight 8-bit registers R0 through R7. At any instant, exactly one register bank is selected so that any instruction that references register Rn will access Rn in the currently selected register bank. i.e. the register that is accessed is affected by the currently selected register bank.

    However, all the registers in all the register banks also have unique direct addresses so that it is also possible to access any register Rn from any register bank – regardless of the currently selected register bank.

    8051 instruction have a fairly limited range of addressing modes. For example, it is not possible to move the contents of one register to another directly as in MOV R0,R1.

    If code is written with knowledge of which register bank is selected when the code is to be executed, then there is an alternative way to access a register – because its absolute address is know. For convenience, the absolute address of a register Rn is simply ARn. So, an instruction such as MOV R0,AR1 is now possible.

    The Assembler/Compiler knows the currently selected register bank required to work out the absolute address of a register by either using a default (usually register bank 0) or because it has been told otherwise by a C #pragma REGISTERBANK(n) or an Assembler USING control. Note that these controls do not themselves change the register bank selected, they only affect the calculation of the absolute addresses of the ARn registers.

    Generally, being able to address registers directly is an advantage and results in faster and more compact code. The generation of code that uses absolute register addressing is turned on by the C #pragma AREGS. However, it is possible that a function may be called and it is not known what the currently selected register bank is – the compiler must be told not to attempt to use instructions that use absolute register address because the absolute addresses cannot be reliably computed at compile time. This situation is indicated to the compiler by using #pragma NOAREGS.

    Sometimes, it is useful to be able to change the currently selected register bank. In Keil C this should be done using the using keyword. This is most usefully applied when the function is question is an interrupt. When an interrupt is generated and the interrupt service function is invoked, that function will need a set of working registers to get anything non-trivial done. Obviously, the interrupt service function must not change the value of any registers in the code that was interrupted. There are two ways of achieving this, the interrupt function can save the current value of all registers that it needs onto the stack and restore them before returning to the interrupted code; or it can switch to a register bank that <u>cannot</u> currently be in use and restore the register bank before returning to the interrupted code. The latter option is generally faster and is achieved by the using keyword.

    It is common practice to assign a register bank to each 'level' of execution in the code. For example, the base level code (everything called by main) uses register bank 0 (default), low-priority interrupt use register bank 1 and high priority interrupts use register bank 2.

    Everything should be fairly straightforward unless the interrupt function calls another function – the called function must have the correct register bank indicated by #pragma REGISTERBANK(n); otherwise, things will go very wrong.

    In general, a function should only be called from code from one 'level' of execution. This can be done, but it is a whole other subject.

Reply
  • The 8051 family of processors have four register banks each of which contain eight 8-bit registers R0 through R7. At any instant, exactly one register bank is selected so that any instruction that references register Rn will access Rn in the currently selected register bank. i.e. the register that is accessed is affected by the currently selected register bank.

    However, all the registers in all the register banks also have unique direct addresses so that it is also possible to access any register Rn from any register bank – regardless of the currently selected register bank.

    8051 instruction have a fairly limited range of addressing modes. For example, it is not possible to move the contents of one register to another directly as in MOV R0,R1.

    If code is written with knowledge of which register bank is selected when the code is to be executed, then there is an alternative way to access a register – because its absolute address is know. For convenience, the absolute address of a register Rn is simply ARn. So, an instruction such as MOV R0,AR1 is now possible.

    The Assembler/Compiler knows the currently selected register bank required to work out the absolute address of a register by either using a default (usually register bank 0) or because it has been told otherwise by a C #pragma REGISTERBANK(n) or an Assembler USING control. Note that these controls do not themselves change the register bank selected, they only affect the calculation of the absolute addresses of the ARn registers.

    Generally, being able to address registers directly is an advantage and results in faster and more compact code. The generation of code that uses absolute register addressing is turned on by the C #pragma AREGS. However, it is possible that a function may be called and it is not known what the currently selected register bank is – the compiler must be told not to attempt to use instructions that use absolute register address because the absolute addresses cannot be reliably computed at compile time. This situation is indicated to the compiler by using #pragma NOAREGS.

    Sometimes, it is useful to be able to change the currently selected register bank. In Keil C this should be done using the using keyword. This is most usefully applied when the function is question is an interrupt. When an interrupt is generated and the interrupt service function is invoked, that function will need a set of working registers to get anything non-trivial done. Obviously, the interrupt service function must not change the value of any registers in the code that was interrupted. There are two ways of achieving this, the interrupt function can save the current value of all registers that it needs onto the stack and restore them before returning to the interrupted code; or it can switch to a register bank that <u>cannot</u> currently be in use and restore the register bank before returning to the interrupted code. The latter option is generally faster and is achieved by the using keyword.

    It is common practice to assign a register bank to each 'level' of execution in the code. For example, the base level code (everything called by main) uses register bank 0 (default), low-priority interrupt use register bank 1 and high priority interrupts use register bank 2.

    Everything should be fairly straightforward unless the interrupt function calls another function – the called function must have the correct register bank indicated by #pragma REGISTERBANK(n); otherwise, things will go very wrong.

    In general, a function should only be called from code from one 'level' of execution. This can be done, but it is a whole other subject.

Children
  • 1)How do you set the register bank so that function parameters will be passed using a bank you choose (instead of the default bank 0)?
    2)You said that "in general, a function should only be called from code from one level of execution". May I know how this is done?
    Best Regards
    AL

  • Still I am confused with Cole's words because the absolute addresses cannot be reliably computed at compile time.The following word is from Chapter 2,Cx51 Compiler User's Guide:

    2
    stmt level source
    1 extern char func ();
    2 char k;
    3
    4 #pragma NOAREGS
    5 noaregfunc () {
    6 1 k = func () + func ();
    7 1 }
    8
    9 #pragma AREGS
    10 aregfunc () {
    11 1 k = func () + func ();
    12 1 }
    ; FUNCTION noaregfunc (BEGIN)
    ; SOURCE LINE # 6
    0000 120000 E LCALL func
    0003 EF MOV A,R7
    0004 C0E0 PUSH ACC
    0006 120000 E LCALL func
    0009 D0E0 POP ACC
    000B 2F ADD A,R7
    000C F500 R MOV k,A
    ; SOURCE LINE # 7
    000E 22 RET
    ; FUNCTION noaregfunc (END)
    ; FUNCTION aregfunc (BEGIN)
    ; SOURCE LINE # 11
    0000 120000 E LCALL func
    0003 C007 PUSH AR7
    0005 120000 E LCALL func
    0008 D0E0 POP ACC
    000A 2F ADD A,R7
    000B F500 R MOV k,A
    ; SOURCE LINE # 12
    000D 22 RET
    ; FUNCTION aregfunc (END)
    Do you mean that
    push AR7
    is not reliable because the AR7 isn't reliably computed at compile time?If it is true,why did you said that all the registers in all the register banks also have unique direct addresses?Is it right that the AREGS directive makes the compiler to generate code using absolute address that will results fatal errors in case that a function,not indicated by REGISTERBANK,is called by an interrupt using using 2 and NOAREGS generate code that always use the current selected register bank when running?

  • Do you mean that push AR7 is not reliable because the AR7 isn't reliably computed at compile time?

    AR7 is the absolute address of register R7 in one particular register bank - could be one of four possibilities depending on of x in the preceeding "USING x". Each possible direct address for R7 is unique.

    When a function is called, any of the four possible register banks could be selected at that instant and the compiler cannot predict which it will be.

  • Hello,
    First of all I wanted to thank Cole for his excellent explanation. It clears a lot of the fog around this issue, and I think that this explanation should be entered into the manual !

    I still have several questions regarding interrupt coding and relevant compiler directives:

    1) Assuming the following code:

    void UartIsr( void ) INTERRUPT(4) USING(2)
    {
       f1();
    }
    
    #pragma NOAREGS
    void f1(void)
    {
       …
       …
       f2();
       …
       …
    }
    #pragma AREGS
    
    void f2(void)
    {
       …
       …
       …
    }
    
    Does f2 need to have the NOAREG and AREG declarations at it's beginning and end?



    2) Can the use of REGISTERBANK(x) replace the NOAREG and AREG combined with the USING statement? Will this method of writing produce better code performance ? If not, then when should I use the REGISTERBANK(x) and when the AREG - NOAREG statements ?



    3) If I have a function that will be called from several code levels (for example the main application as well as the interrupt subroutine), will the NOAREG declaration allow this code to be correctly compiled?

    Thanks,
    Amit Alon.

  • And some more questions...

    Is it deafult compilation state to use AREG if no AREG/NOAREG pragma is written in the code ?

    Do I need to specify it in the beginning of main?

    If I specify any option (use AREG or NOAREG) at a specific place in the code, will this direction be relevant in all the following code (including called functions) untill a new direction will be issued ?

  • Will the direction #pragma REGISTERBANK(x) apply only for the function that follows this statement, or will it apply untill a new REGISTERBANK direction will be stated ?

    In the following example:

    #pragma REGISTERBANK(3)
    void func1 (void)
    {
     ...
     ...
    }
    
    
    void func2 (void)
    {
     ...
     ...
    }
    

    Will func2 use registerbank 3 as func1, or will it use the deafult register bank 0 ?

    Should I write #pragma REGISTERBANK(0) before func2 declaration in order to compile func2 using the deafult register bank (0) ?

  • See: http://www.keil.com/support/man/docs/c51/c51_registerbank.htm

    (The REGISTERBANK directive selects which register bank to use for subsequent functions).

  • Please start a new thread for such generic questions.