6

我正在为 ARM CPU 编写启动代码。没有内部 RAM,但有 1GB 的 DDRAM 连接到 CPU,在初始化之前无法直接访问。代码存储在闪存中,初始化 RAM,然后将自身和数据段复制到 RAM 并在那里继续执行。我的程序是:

#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;
}

目前我的程序无法正常工作,因为寄存器值最终被链接到 RAM,而此时程序试图访问它们只有闪存可用。

我怎样才能更改我的链接描述文件或我的程序,以便这些值在闪存中有它们的地址?有没有办法可以在文本段中包含这些值?

当它们在文件范围内声明时,这些定义的值实际上是全局数据还是静态数据?

编辑:

目标文件与以下链接描述文件链接:

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
...
}

从寄存器分配反汇编:

  *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
4

2 回答 2

3

直接回答您的问题 -#defined值不会存储在程序中的任何地方(可能在调试部分除外)。宏在编译时会扩展,就像您在函数中键入它们一样,例如:

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

作为编译代码的一部分,这些值最终会出现在文本段中。

这里更有可能的是你做错了其他事情。在我的脑海中,我的第一个猜测是您的堆栈未正确初始化,或者位于尚不可用的内存区域中。

于 2013-04-07T22:44:01.897 回答
1

有几个选项可以解决这个问题。

  1. 使用PC相对数据访问。
  2. 使用自定义linker script.
  3. 使用汇编程序。

使用PC相对数据访问

这种方法的问题是你必须知道编译器如何生成代码的细节。#define register1 (volatile unsigned int *)0xd0010000UL是它被存储为从链接的 SDRAM 地址加载的静态变量。

  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

你必须这样做,

 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;
 }

或者您可能更喜欢在汇编程序中实现它,因为对于您可以在这里做什么和不能做什么可能很迟钝。上面C代码的主要作用是每一件事都是计算的或PC相对的。如果您选择使用链接描述文件,则必须如此。正如Duskwuff指出的那样,您也可能遇到堆栈问题。如果您没有可以用作临时堆栈的 ETB 内存等,那么最好在汇编程序中对其进行编码。

链接器脚本

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.

于 2013-04-07T22:44:48.767 回答