Question

I am writing the boot-up code for an ARM CPU. There is no internal RAM, but there is 1GB of DDRAM connected to the CPU, which is not directly accessible before initialisation. The code is stored in flash, initialises RAM, then copies itself and the data segment to RAM and continue execution there. My program is:

#define REG_BASE_BOOTUP 0xD0000000
#define INTER_REGS_BASE REG_BASE_BOOTUP

#define SDRAM_FTDLL_REG_DEFAULT_LEFT            0x887000

#define DRAM_BASE               0x0
#define SDRAM_FTDLL_CONFIG_LEFT_REG (DRAM_BASE+ 0x1484)
... //a lot of registers

void sdram_init() __attribute__((section(".text_sdram_init")));

void ram_init()
{
  static volatile unsigned int* const sdram_ftdll_config_left_reg = (unsigned int*)(INTER_REGS_BASE + SDRAM_FTDLL_CONFIG_LEFT_REG);
  ... //a lot of registers assignments

  *sdram_ftdll_config_left_reg = SDRAM_FTDLL_REG_DEFAULT_LEFT;
}

At the moment my program is not working correctly because the register values end up being linked to RAM, and at the moment the program tries to access them only the flash is usable.

How could I change my linker script or my program so that those values have their address in flash? Is there a way I can have those values in the text segment?

And actually are those defined values global or static data when they are declared at file scope?

Edit:

The object file is linked with the following linker script:

MEMORY 
{                                                                                                             
RAM (rw)    : ORIGIN = 0x00001000, LENGTH = 12M-4K                                
ROM (rx)    : ORIGIN = 0x007f1000, LENGTH = 60K
VECTOR (rx) : ORIGIN = 0x007f0000, LENGTH = 4K   
}
SECTIONS
{
    .startup :
    {
    KEEP((.text.vectors))
    sdram_init.o(.sdram_init)
    } > VECTOR
...
}

Disassembly from the register assignment:

  *sdram_ftdll_config_left_reg = SDRAM_FTDLL_REG_DEFAULT_LEFT;
  7f0068:       e59f3204        ldr     r3, [pc, #516]  ; 7f0274 <sdram_init+0x254>
  7f006c:       e5932000        ldr     r2, [r3]
  7f0070:       e59f3200        ldr     r3, [pc, #512]  ; 7f0278 <sdram_init+0x258>
  7f0074:       e5823000        str     r3, [r2]
  ...
  7f0274:       007f2304        .word   0x007f2304
  7f0278:       00887000        .word   0x00887000
Was it helpful?

Solution

To answer your question directly -- #defined values are not stored in the program anywhere (besides possibly in debug sections). Macros are expanded at compile time as if you'd typed them out in the function, something like:

*((unsigned int *) 0xd0010000) = 0x800f800f;

The values do end up in the text segment, as part of your compiled code.

What's much more likely here is that there's something else you're doing wrong. Off the top of my head, my first guess would be that your stack isn't initialized properly, or is located in a memory region that isn't available yet.

OTHER TIPS

There are a few options to solve this problem.

  1. Use PC relative data access.
  2. Use a custom linker script.
  3. Use assembler.

Use PC relative data access

The trouble you have with this method is you must know details of how the compiler will generate code. #define register1 (volatile unsigned int *)0xd0010000UL is that this is being stored as a static variable which is loaded from the linked SDRAM address.

  7f0068:      ldr     r3, [pc, #516]  ; 7f0274 <sdram_init+0x254>
  7f006c:      ldr     r2, [r3]  ; !! This is a problem !!
  7f0070:      ldr     r3, [pc, #512]  ; 7f0278 <sdram_init+0x258>
  7f0074:      str     r3, [r2]
  ...
  7f0274:     .word   0x007f2304  ; !! This memory doesn't exist.
  7f0278:     .word   0x00887000

You must do this,

 void ram_init()
 {
     /* NO 'static', you can not do that. */
     /* static */ volatile unsigned int* const sdram_reg = 
         (unsigned int*)(INTER_REGS_BASE + SDRAM_FTDLL_CONFIG_LEFT_REG);
     *sdram_ftdll_config_left_reg = SDRAM_FTDLL_REG_DEFAULT_LEFT;
 }

Or you may prefer to implement this in assembler as it is probably pretty obtuse as to what you can and can't do here. The main effect of the above C code is that every thing is calculated or PC relative. If you opt not to use a linker script, this must be the case. As Duskwuff points out, you also can have stack issues. If you have no ETB memory, etc, that you can use as a temporary stack then it probably best to code this in assembler.

Linker script

See gnu linker map... and many other question on using a linker script in this case. If you want specifics, you need to give actual addresses use by the processor. With this option you can annotate your function to specify which section it will live in. For instance,

void ram_init() __attribute__((section("FLASH")));

In this case, you would use the Gnu Linkers MEMORY statement and AT statements to put this code at the flash address where you desire it to run from.

Use assembler

Assembler gives you full control over memory use. You can garentee that no stack is used, that no non-PC relative code is generated and it will probably be faster to boot. Here is some table driven ARM assembler I have used for the case you describe, initializing an SDRAM controller.

 /* Macro for table of register writes. */
 .macro DCDGEN,type,addr,data
 .long \type
 .long \addr
 .long \data
 .endm
 .set FTDLL_CONFIG_LEFT, 0xD0001484

 sdram_init:
 DCDGEN 4, FTDLL_CONFIG_LEFT, 0x887000
 1:

 init_sdram_bank:
         adr     r0,sdram_init
         adr     r1,1b
 1:
         /* Delay. */
         mov     r5,#0x100
 2:      subs    r5,r5,#1
         bne     2b

         ldmia   r0!, {r2,r3,r4} /* Load DCD entry. */
         cmp     r2,#1           /* byte? */
         streqb  r4,[r3]         /* Store byte... */
         strne   r4,[r3]         /* Store word. */

         cmp     r0,r1           /* table done? */
         blo     1b

         bx lr

         /* Dump literal pool. */
        .ltorg

Assembler has many benefits. You can also clear the bss section and setup the stack with simple routines. There are many on the Internet and I think you can probably code one yourself. The gnu ld script is also beneficial with assembler as you can ensure that sections like bss are aligned and a multiple of 4,8,etc. so that the clearing routine doesn't need special cases. Also, you will have to copy the code from flash to SDRAM after it is initialized. This is a fairly expensive/long running task and you can speed it up with some short assembler.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top