ARM Error: invalid literal constant: pool nee

nobody wrote on Thursday, February 23, 2006:

Hi,
I’m experimenting an odd problem with a FreeRTOS macro as the code grows bigger:

I’m using an ATMEL EB40A ev. board and GCC 4.02 WinARM dev. environment, and my application runs fairly good. The problem is that my code has grown big (.text is about 0x12000 bytes large) and the portSAVE_CONTEXT() and restore() macros stopped working: the compiler stops reporting:
“Error: invalid literal constant: pool needs to be closer”
several times (one per far data reference).
It looks that the referred variables like:
asm volatile ( "LDR        R0, =ulCriticalNesting " );
are now too far to be directly referred (the compiler address them storing their address in local constants).
I hope to have clearly pointed out the problem but, sorry, my knowledge of the ARM assembly language is not enough to solve this problem by myself… Can anybody help me?
Thank you in advance.

nobody wrote on Thursday, February 23, 2006:

I would guess that the difference in address to the critical nesting function from where it is called is too great for encoding into the instruction but this is a guess.  Try searching on the net for the error message.

nobody wrote on Thursday, February 23, 2006:

Yes, you are almost right: ulCriticalNesting is a variable, that this ARM asm code addresses using an offset based on the current PC. ARM relative indirection tange is somewhat limited and the distance between my last ISR routine (referring the variable) and the variable itself is too large to be covered by this kind of address mode.
But I’m not an ARM asm coder and I don’t know how to work it out :frowning:

nobody wrote on Thursday, February 23, 2006:

Yes, I think this is the problem too: ulCriticalNesting is a variable, that this ARM asm code addresses using an offset based on the current PC. ARM relative indirection tange is somewhat limited and the distance between my last ISR routine (referring the variable) and the variable itself is too large to be covered by this kind of address mode.
But, sorry, I’m not an ARM asm coder and I don’t know how to work it out :frowning:

jra01 wrote on Friday, February 24, 2006:

The trick is to use another addressing mode. You put the value ulCriticalNesting in aplace close to the code, and then you refer to that location. You shall be able to see examples of this in the start-up code, where they have used this tecnique. In boot.s in one of the gcc example projects you find:
    ldr        r1, .LC3

    .LC3:
    .word   __data_beg__

From this you should be able to solve your problem.

Regards
Jokke

nobody wrote on Friday, February 24, 2006:

Hi Jokke,
sure, I considered using this workaround (this is the trick used by the C compiler to address it), but it doesn’t solve the main problem: I’d like to place this declaration INSIDE the macros themselves, by using something like the old “local” or “private” directive to avoid generating duplicate symbols… I don’t know if this kind of construct exists in this asm.
Using a location OUTSIDE of the macro implies that I have to change the location name inside the macro at any usage, so I cannot use the plain macro… (but perhaps one can write a C macro substituting the “__var_addr__” symbol automagically).
Does anybody know the ARM gas syntax well enough to manage this problem?
Thank you.

olit123 wrote on Wednesday, November 14, 2007:

Hi,

I have the same problem, as my code size increases.

I’m using FreeRTOS 4.6.0 with WinARM-20060606, compiling for a LPC2294.

Here is an example of the error message:
arm-elf-gcc -c  -mcpu=arm7tdmi-s  -I. -gdwarf-2 -DXRAM_RUN -D GCC_ARM7 -D__WinARM__ -I ./import -I ./serial -I ./candrv -I ./debugTask -I ./canTask -I ./eeprom  -O0 -Wall -Wextra -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-align -Wsign-compare -Waggregate-return -Wunused -I…/FreeRTOS/V4.6.0/Source/include -fomit-frame-pointer -fsigned-char -save-temps -MD -MP -MF .dep/main.o.d -std=gnu99 -Wmissing-prototypes  -Wstrict-prototypes -Wmissing-declarations main.c -o main.o
main.s: Assembler messages:
main.s:598: Error: invalid literal constant: pool needs to be closer

The assembler listing for this reads (Line 598 is the one refering to ulCriticalNesting):
    SUB    LR, LR, #60                                           
    MRS    R0, SPSR                                           
    STMDB    LR!, {R0}                                           
    LDR    R0, =ulCriticalNesting                               
    LDR    R0, [R0]                                           
    STMDB    LR!, {R0}                                           
    LDR    R0, =pxCurrentTCB                                   
    LDR    R0, [R0]                                           
    STR    LR, [R0]                                           

Has someone already improved the portSAVE_CONTEXT / portRESTORE_CONTEXT macros to be able to handle larger code sizes?

Regards,
Oliver

quantum-leaps wrote on Thursday, November 15, 2007:

Try the GCC-ARM option -mlong-calls. This option replaces BL instructions with BX r? instructions. The B/BL instruction can reach only +/-4MB in Thumb in +/-32MB in ARM. The BX instruciton has access to the whole 32-bit address space (4GB).

olit123 wrote on Thursday, November 15, 2007:

Hi,

thanks for your response. I tried -mlong-calls, but unfortunately it doesn’t make any difference.

I’m just beginning to learn ARM ASM but as far as I understand the code, it’s not a branch that fails but the instruction that’s trying to load the address of the C-variable ulCriticalNesting into R0.

Regards,
Oliver

m_a_d_i_s wrote on Thursday, November 15, 2007:

Hi!

I encountered that problem in a project coded entirely in asm. I solved it by inserting .ltorg to some places the problem occured. Try googling for it and try to insert it into your code somewhere. I don’t know how to insert it within c function so that arm wont execute your literals (I added it to the asm file, then it was really straightforward).

Try playing with asm() and check assembler output how it compiles. That .ltorg needs to get inserted between two functions, basically. Or you can try to branch over it within asm()… just few ideas.

Madis

olit123 wrote on Thursday, November 15, 2007:

Hi,
I had no luck with the .ltorg. Maybe because it is used inside an asm() statement.
I were able to work around it manually, but this requires a branch over the data definition.

I feel that there must be a more elegant solution using input and clobber lists to the asm statement.

This might not be optimal, but it works for now:

#define portSAVE_CONTEXT()                                             
{                                                                      
extern volatile void * volatile pxCurrentTCB;                          
extern volatile unsigned portLONG ulCriticalNesting;                   
                                                                       
    asm volatile (                                                     
    "b      2f              /* skip pool */                     \n\t"  
    "1:                                                         \n\t"  
    ".word  pxCurrentTCB                                        \n\t"  
    ".word  ulCriticalNesting                                   \n\t"  
    "2:                                                         \n\t"  
    /* Push R0 as we are going to use the register. */                 
    "STMDB  SP!, {R0}                                           \n\t"  
                                                                       
    /* Set R0 to point to the task stack pointer. */                   
    "STMDB  SP,{SP}^                                            \n\t"  
    "NOP                                                        \n\t"  
    "SUB    SP, SP, #4                                          \n\t"  
    "LDMIA  SP!,{R0}                                            \n\t"  
                                                                       
    /* Push the return address onto the stack. */                      
    "STMDB  R0!, {LR}                                           \n\t"  
                                                                       
    /* Now we have saved LR we can use it instead of R0. */            
    "MOV    LR, R0                                              \n\t"  
                                                                       
    /* Pop R0 so we can save it onto the system mode stack. */         
    "LDMIA  SP!, {R0}                                           \n\t"  
                                                                       
    /* Push all the system mode registers onto the task stack. */      
    "STMDB  LR,{R0-LR}^                                         \n\t"  
    "NOP                                                        \n\t"  
    "SUB    LR, LR, #60                                         \n\t"  
                                                                       
    /* Push the SPSR onto the task stack. */                           
    "MRS    R0, SPSR                                            \n\t"  
    "STMDB  LR!, {R0}                                           \n\t"  
                                                                       
    "LDR    R0, 1b+4        /* =ulCriticalNesting */            \n\t"  
    "LDR    R0, [R0]                                            \n\t"  
    "STMDB  LR!, {R0}                                           \n\t"  
                                                                       
    /* Store the new top of stack for the task. */                     
    "LDR    R0, 1b          /* =pxCurrentTCB */                 \n\t"  
    "LDR    R0, [R0]                                            \n\t"  
    "STR    LR, [R0]                                            \n\t"  
    );                                                                 
    ( void ) ulCriticalNesting;                                        
    ( void ) pxCurrentTCB;                                             
}

#define portRESTORE_CONTEXT()                                          
{                                                                      
extern volatile void * volatile pxCurrentTCB;                          
extern volatile unsigned portLONG ulCriticalNesting;                   
                                                                       
    asm volatile (                                                     
    "b      2f              /* skip pool */                     \n\t"  
    "1:                                                         \n\t"  
    ".word  pxCurrentTCB                                        \n\t"  
    ".word  ulCriticalNesting                                   \n\t"  
    "2:                                                         \n\t"  
    /* Set the LR to the task stack. */                                
    "LDR        R0, 1b      /* =pxCurrentTCB */                 \n\t"  
    "LDR        R0, [R0]                                        \n\t"  
    "LDR        LR, [R0]                                        \n\t"  
                                                                       
    /* The critical nesting depth is the first item on the stack. */   
    /* Load it into the ulCriticalNesting variable. */                 
    "LDR        R0, 1b+4    /* =ulCriticalNesting */            \n\t"  
    "LDMFD  LR!, {R1}                                           \n\t"  
    "STR        R1, [R0]                                        \n\t"  
                                                                       
    /* Get the SPSR from the stack. */                                 
    "LDMFD  LR!, {R0}                                           \n\t"  
    "MSR        SPSR, R0                                        \n\t"  
                                                                       
    /* Restore all system mode registers for the task. */              
    "LDMFD  LR, {R0-R14}^                                       \n\t"  
    "NOP                                                        \n\t"  
                                                                       
    /* Restore the return address. */                                  
    "LDR        LR, [LR, #+60]                                  \n\t"  
                                                                       
    /* And return - correcting the offset in the LR to obtain the */   
    /* correct address. */                                             
    "SUBS   PC, LR, #4                                          \n\t"  
    );                                                                 
    ( void ) ulCriticalNesting;                                        
    ( void ) pxCurrentTCB;                                             
}

Regards,
Oliver

olit123 wrote on Friday, April 17, 2009:

Hi,

Because the code is still the same in V5.2.0 I suggest again to include the following code in the LPC2000 port (portRESTORE_CONTEXT was slightly improved compared to my last post, i moved the data behing the code, eliminating the branch).
If somebody has a more elegant solution, I would be also very happy. At least this fixes the "Error: invalid literal constant: pool needs to be closer" error that can happen if the code grows bigger.

#define portRESTORE_CONTEXT()                                          
{                                                                      
extern volatile void * volatile pxCurrentTCB;                          
extern volatile unsigned portLONG ulCriticalNesting;                   
                                                                       
    asm volatile (                                                     
    /* Set the LR to the task stack. */                                
    "LDR    R0, 1f      /* =pxCurrentTCB */                     \n\t"  
    "LDR    R0, [R0]                                            \n\t"  
    "LDR    LR, [R0]                                            \n\t"  
                                                                       
    /* The critical nesting depth is the first item on the stack. */   
    /* Load it into the ulCriticalNesting variable. */                 
    "LDR    R0, 2f      /* =ulCriticalNesting */                \n\t"  
    "LDMFD  LR!, {R1}                                           \n\t"  
    "STR    R1, [R0]                                            \n\t"  
                                                                       
    /* Get the SPSR from the stack. */                                 
    "LDMFD  LR!, {R0}                                           \n\t"  
    "MSR    SPSR, R0                                            \n\t"  
                                                                       
    /* Restore all system mode registers for the task. */              
    "LDMFD  LR, {R0-R14}^                                       \n\t"  
    "NOP                                                        \n\t"  
                                                                       
    /* Restore the return address. */                                  
    "LDR    LR, [LR, #+60]                                      \n\t"  
                                                                       
    /* And return - correcting the offset in the LR to obtain the */   
    /* correct address. */                                             
    "SUBS   PC, LR, #4                                          \n\t"  
                                                                       
    /* Place the pool behind the code. This is never reached. */       
    "1: .word  pxCurrentTCB                                     \n\t"  
    "2: .word  ulCriticalNesting                                \n\t"  
    );                                                                 
    ( void ) ulCriticalNesting;                                        
    ( void ) pxCurrentTCB;                                             
}
/*-----------------------------------------------------------*/

#define portSAVE_CONTEXT()                                             
{                                                                      
extern volatile void * volatile pxCurrentTCB;                          
extern volatile unsigned portLONG ulCriticalNesting;                   
                                                                       
    asm volatile (                                                     
    "b      3f              /* skip data */                     \n\t"  
    "1: .word  pxCurrentTCB                                     \n\t"  
    "2: .word  ulCriticalNesting                                \n\t"  
    "3:                                                         \n\t"  
    /* Push R0 as we are going to use the register. */                 
    "STMDB  SP!, {R0}                                           \n\t"  
                                                                       
    /* Set R0 to point to the task stack pointer. */                   
    "STMDB  SP,{SP}^                                            \n\t"  
    "NOP                                                        \n\t"  
    "SUB    SP, SP, #4                                          \n\t"  
    "LDMIA  SP!,{R0}                                            \n\t"  
                                                                       
    /* Push the return address onto the stack. */                      
    "STMDB  R0!, {LR}                                           \n\t"  
                                                                       
    /* Now we have saved LR we can use it instead of R0. */            
    "MOV    LR, R0                                              \n\t"  
                                                                       
    /* Pop R0 so we can save it onto the system mode stack. */         
    "LDMIA  SP!, {R0}                                           \n\t"  
                                                                       
    /* Push all the system mode registers onto the task stack. */      
    "STMDB  LR,{R0-LR}^                                         \n\t"  
    "NOP                                                        \n\t"  
    "SUB    LR, LR, #60                                         \n\t"  
                                                                       
    /* Push the SPSR onto the task stack. */                           
    "MRS    R0, SPSR                                            \n\t"  
    "STMDB  LR!, {R0}                                           \n\t"  
                                                                       
    "LDR    R0, 2b          /* =ulCriticalNesting */            \n\t"  
    "LDR    R0, [R0]                                            \n\t"  
    "STMDB  LR!, {R0}                                           \n\t"  
                                                                       
    /* Store the new top of stack for the task. */                     
    "LDR    R0, 1b          /* =pxCurrentTCB */                 \n\t"  
    "LDR    R0, [R0]                                            \n\t"  
    "STR    LR, [R0]                                            \n\t"  
    );                                                                 
    ( void ) ulCriticalNesting;                                        
    ( void ) pxCurrentTCB;                                             
}

Regards,
Oliver

rtel wrote on Friday, April 17, 2009:

Hi Oliver - could you post that to a feature request please?  That way it won’t get lost in the crowd.  Thanks ;o)

Regards.

cynergizer wrote on Thursday, July 07, 2011:

Oliver, 
I ran into the exact same problem as my code size grew to 104Kbytes.  I used your solution and it fixed the problem!  Thank you so much for posting this (as well as what didn’t work). You saved me some hurt!