QLJT移植u-boot-2010.09到s3c2440

QLJT移植u-boot-2010.09s3c2440

作者:梁桥江.

转载请注明出处

 

前言:本文针对对u-boot有一定了解的读者,可能注释比较少,如果想了解关于u-boot移植到s3c2440的注释,可以参考本人博客的《u-boot-1.3.4 移植到S3C2440 (带有某些解析)》一文。

 

本次移植与《u-boot-1.3.4 移植到S3C2440 (带有某些解析)》相比,具有以下不同:

1.       nand flash的读写采用标准的MTD模式。

2.       移植的各个层次阶段更为清晰。

3.       移植每一步都在SDRAM里验证过,直到最后一步完成才把u-boot.bin写到flash

 

一.         u-boot-2010.09原来基础上为自己的板子创建一个工程

[root@qljt u-boot-2010.09]# pwd

/ql_study/u-boot/u-boot-2010.09

[root@qljt u-boot-2010.09]# make distclean

[root@qljt u-boot-2010.09]# cd board

[root@qljt board]# mkdir qljt

[root@qljt board]# cp sbc2410x/ qljt/qljt2440

cp: omitting directory `sbc2410x/'

[root@qljt board]# cp sbc2410x/ qljt/qljt2440 -rf

[root@qljt board]# mv qljt /tmp

[root@qljt board]# rm -rf *

[root@qljt board]# mv /tmp/qljt/ ./

[root@qljt board]# cd qljt/qljt2440/

[root@qljt qljt2440]# mv sbc2410x.c qljt2440.c

[root@qljt qljt2440]# vi Makefile

COBJS   := sbc2410x.o flash.o

改为

COBJS   := qljt2440.o flash.o         

                                 

[root@qljt qljt2440]# vi config.mk

TEXT_BASE = 0x33F80000

改为

TEXT_BASE = 0x30100000

                                        

[root@qljt qljt2440]# cd -

/ql_study/u-boot/u-boot-2010.09/board

[root@qljt board]# cd ..

[root@qljt u-boot-2010.09]# cp include/configs/sbc2410x.h include/configs/qljt2440.h

[root@qljt u-boot-2010.09]# vi Makefile  

__OBJS := $(subst $(obj),,$(OBJS))

__LIBS := $(subst $(obj),,$(LIBS)) $(subst $(obj),,$(LIBBOARD))

改为

__OBJS := $(subst $(obj),,$(OBJS))

__LIBS := $(subst $(obj),,$(LIBBOARD)) $(subst $(obj),,$(LIBS))

 

添加:

qljt2440_config: unconfig

       @$(MKCONFIG) $(@:_config=) arm arm920t qljt2440 qljt s3c24x0

      

[root@qljt u-boot-2010.09]# make qljt2440_config

Configuring for qljt2440 board...

[root@qljt u-boot-2010.09]# make

最后生成

arm-linux-objcopy --gap-fill=0xff -O binary u-boot u-boot.bin

 

/*====到此针对我开发板的u-boot工程建立起来了,以后的所有操作都基于该工程====*/

 

二.   汇编部分移植

1.     arch/arm/cpu/arm920t/start.S

添加

#define GPIO_CTL_BASE_ADDR              0x56000000

#define GPIO_F_OFFSET                   0x50    /* offset of GPIO_CTL_BASE*/

#define GPIO_CON_OFFSET                 0x0     /* */

#define GPIO_DAT_OFFSET                 0x4     /* */

#define GPIO_UP_OFFSET                  0x8     /* */

gpio_led_init:

       mov    r1, #GPIO_CTL_BASE_ADDR    

       add    r1, r1, #GPIO_F_OFFSET

       ldr    r2, =0x5500 /*GPIO_F[7~4]分别设为输出,GPIO_F[3~0]设为输入*/

       str    r2, [r1, #GPIO_CON_OFFSET]

       mov    r2, #0xff

       str    r2, [r1, #GPIO_UP_OFFSET] 

       mov    pc, lr

 

led_all_off:     

       mov    r1, #GPIO_CTL_BASE_ADDR    

       add    r1, r1, #GPIO_F_OFFSET

       mov    r2, #0xff

       str    r2, [r1, #GPIO_DAT_OFFSET]

       mov    pc, lr

 

led_0_on:

       mov    r1, #GPIO_CTL_BASE_ADDR    

       add    r1, r1, #GPIO_F_OFFSET

       ldr      r2, [r1, #GPIO_DAT_OFFSET]

       bic      r2, r2,#0x10

       str    r2, [r1, #GPIO_DAT_OFFSET]

       mov    pc, lr

 

led_1_on:

       mov    r1, #GPIO_CTL_BASE_ADDR    

       add    r1, r1, #GPIO_F_OFFSET

       ldr      r2, [r1, #GPIO_DAT_OFFSET]

       bic      r2, r2,#0x20

       str    r2, [r1, #GPIO_DAT_OFFSET]

       mov    pc, lr

 

led_2_on:

       mov    r1, #GPIO_CTL_BASE_ADDR    

       add    r1, r1, #GPIO_F_OFFSET

       ldr      r2, [r1, #GPIO_DAT_OFFSET]

       bic      r2, r2,#0x40

       str    r2, [r1, #GPIO_DAT_OFFSET]

       mov    pc, lr

 

led_3_on:

       mov    r1, #GPIO_CTL_BASE_ADDR    

       add    r1, r1, #GPIO_F_OFFSET

       ldr      r2, [r1, #GPIO_DAT_OFFSET]

       bic      r2, r2,#0x80

       str    r2, [r1, #GPIO_DAT_OFFSET]

       mov    pc, lr

 

start_code:

       /*

        * set the cpu to SVC32 mode

        */

       mrs  r0, cpsr

       bic   r0, r0, #0x1f

       orr   r0, r0, #0xd3

       msr  cpsr, r0

后添加

       bl     gpio_led_init

       bl     led_all_off

       bl  led_0_on

       bl  led_1_on

       bl  led_2_on

 

#define pWTCON  0x53000000

#define INTMSK    0x4A000008   /* Interupt-Controller base addresses */

#define INTSUBMSK    0x4A00001C

#define CLKDIVN  0x4C000014   /* clock divisor register */

后面添加

#define CLK_CTL_BASE        0x4c000000 

#if defined(CONFIG_S3C2440)   

#define MDIV_405       0x7f << 12 

#define PSDIV_405       0x21   

#endif

 

 

# if defined(CONFIG_S3C2410)

ldr   r1, =0x3ff

ldr   r0, =INTSUBMSK

str   r1, [r0]

# endif

改为

# if defined(CONFIG_S3C2440)

ldr  r1, =0x7fff

ldr   r0, =INTSUBMSK

str   r1, [r0]

# endif

 

/* FCLK:HCLK:PCLK = 1:2:4 */

/* default FCLK is 405 MHz ! */

ldr   r0, =CLKDIVN

mov r1, #3

str   r1, [r0]

改为

/* FCLK:HCLK:PCLK = 1:4:8 */

/* default FCLK is 405 MHz ! */

ldr   r0, =CLKDIVN

mov r1, #5

str   r1, [r0]

然后接着添加

mrc    p15, 0, r1, c1, c0, 0      

orr    r1, r1, #0xc0000000     

mcr    p15, 0, r1, c1, c0, 0    

 

#if defined(CONFIG_S3C2440)     

mov    r1, #CLK_CTL_BASE                                                                        

mov    r2, #MDIV_405                                                                                        

add    r2, r2, #PSDIV_405                                                                                              

str    r2, [r1, #0x04]                                                                                                    

#endif

 

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

改为

#if 0 //ndef CONFIG_SKIP_RELOCATE_UBOOT

 

2include/configs/qljt2440.h

#define CONFIG_S3C2410   1     /* specifically a SAMSUNG S3C2410 SoC   */

后添加

#define CONFIG_S3C2440   1     /* specifically a SAMSUNG S3C2440 SoC   */

 

3.  board/qljt/qljt2440/lowlevel_init.S

#define Trp                   0x0  /* 2clk */

改为

#define Trp                   0x1  /* 3clk */

 

#define REFCNT                  0x0459

改为

#define REFCNT                  1259/*0x0459*/

 

.word 0xb2

改为

.word 0xb1

/*======================到此汇编部分移植完毕======================*/

 

. C语言部分移植

1arch/arm/lib/board.c

添加

#include <asm/arch/s3c24x0_cpu.h>

void led_close_all(void)

{

       struct s3c24x0_gpio *pGpio;

       char gpio_f_data;

 

       pGpio = s3c24x0_get_base_gpio();

       gpio_f_data = pGpio->GPFDAT;

       gpio_f_data |= 0xf0;

       pGpio->GPFDAT = gpio_f_data;

}

 

void led_open_one(int which)

{

       struct s3c24x0_gpio *pGpio;

       char gpio_f_data;

      

       if(which < 0 || which >3)

       {

              printf("led which(%d) if out of range!/r/n",which);

              return;

       }

       pGpio = s3c24x0_get_base_gpio();

       gpio_f_data = pGpio->GPFDAT;

       gpio_f_data &= ~(0x01<<(which +4));

       pGpio->GPFDAT = gpio_f_data;  

}

 

2.       board/qljt/qljt2440/qljt2440.c

#define M_MDIV   0x5c

#define M_PDIV    0x4

#define M_SDIV    0x0

改为

#define M_MDIV 0x7f

#define M_PDIV 0x2

#define M_SDIV 0x1

 

#define U_M_MDIV      0x48

#define U_M_PDIV       0x3

改为

#define U_M_MDIV 0x38

#define U_M_PDIV 0x2

 

gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;

改为

gd->bd->bi_arch_number = MACH_TYPE_S3C2440;

 

/*=================到此为止串口已经有东西输出,但是乱码=================*/

 

 

.  debug串口乱码的原因

串口输出乱码是因为串口参数设置不对,led灯进行对各个串口的参数配置进行debug,发现所用的串口参数都是默认的,而且跟PC机通信串口的参数一致,那么,就是设到串口寄存器中的值不对了。再看源码,除了波特率其他配置都是直接设寄存器,而对波特率的设置,是把波特率转化成串口参数的除法因子设进去的,在波特率转化成除法因子的时候用到get_pclk()函数,这就跟系统时钟拉上关系了,于是开始从系统时钟着手寻找串口输出乱码的原因。

通过重新阅读s3c2440datasheet后,对

1. u-boot-2010.09/arch/arm/cpu/arm920t/s3c24x0/ speed.c

get_PLLCLK()

m = ((r & 0xFF000) >> 12) + 8;

p = ((r & 0x003F0) >> 4) + 2;

s = r & 0x3;

后添加:

#if defined(CONFIG_S3C2440)

if (pllreg == MPLL)

        return((CONFIG_SYS_CLK_FREQ * m * 2) / (p << s)); /* CONFIG_SYS_CLK_FREQ qljt2440.h中定义*/

        else if (pllreg == UPLL)

#endif    

 

 

        get_HCLK()

              整个函数的内容改为:

              struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();     

              if (clk_power->CLKDIVN & 0x6)   

              {

                     if ((clk_power->CLKDIVN & 0x6)==2)       

                            return(get_FCLK()/2);

                     if ((clk_power->CLKDIVN & 0x6)==6)      

                            return((clk_power->CAMDIVN & 0x100) ? get_FCLK()/6 : get_FCLK()/3);        

                     if ((clk_power->CLKDIVN & 0x6)==4)      

                            return((clk_power->CAMDIVN & 0x200) ? get_FCLK()/8 : get_FCLK()/4);        

                     return(get_FCLK());

              }

              else 

              {

                     return(get_FCLK());

              }    

              //return (readl(&clk_power->CLKDIVN) & 2) ? get_FCLK() / 2 : get_FCLK();

 

3. u-boot-2010.09/arch/arm/include/asm/arch/ s3c24x0.h中的下面结构体添加蓝色变量

       struct s3c24x0_clock_power

 {

              u32  LOCKTIME;

              u32  MPLLCON;

              u32  UPLLCON;

              u32  CLKCON;

              u32  CLKSLOW;

              u32  CLKDIVN;

              u32 CAMDIVN;

};

/*===============到此为止串口输出正常,网卡(CS8900)工作正常===============*/

 

. 开始nandflashmtd移植, 我的板子上之后nandflash,没有norflash,所以顺便把norflash的支持也去掉。

1.      include/configs/qljt2440.h

#define CONFIG_BOOTP_GATEWAY

#define CONFIG_BOOTP_HOSTNAME

后添加

#define CONFIG_SYS_NO_FLASH 1

 

#define CONFIG_CMD_ELF

#define CONFIG_CMD_PING

后添加

#define CONFIG_CMD_NAND

 

#define     CONFIG_ENV_IS_IN_FLASH     1

改为

#define   CONFIG_ENV_IS_IN_NAND     1

 

#define CONFIG_ENV_SIZE        0x10000  /* Total Size of Environment Sector

后添加

#define CONFIG_ENV_OFFSET       0x40000

 

 

#endif      /* __CONFIG_H */

前添加

#if defined(CONFIG_CMD_NAND)

#define CONFIG_SYS_NAND_BASE     0x4E000000 //Nand配置寄存器基地址

#define CONFIG_SYS_MAX_NAND_DEVICE      1

#define CONFIG_MTD_NAND_VERIFY_WRITE    1

//#define NAND_SAMSUNG_LP_OPTIONS       1 //大于128M nand使用

#endif

 

2. drivers/mtd/nand/s3c2410_nand.c

#define S3C2410_NFCONF_nFCE        (1<<11)

#define S3C2410_NFCONF_TACLS(x)    ((x)<<8)

#define S3C2410_NFCONF_TWRPH0(x)   ((x)<<4)

#define S3C2410_NFCONF_TWRPH1(x)   ((x)<<0)

改为

#define S3C2410_NFCONF_nFCE        (1<<1)

#define S3C2410_NFCONF_TACLS(x)    ((x)<<12)

#define S3C2410_NFCONF_TWRPH0(x)   ((x)<<8)

#define S3C2410_NFCONF_TWRPH1(x)   ((x)<<4)

 

#define S3C2410_ADDR_NALE 4

#define S3C2410_ADDR_NCLE 8

改为

#define S3C2410_ADDR_NALE 0x08

#define S3C2410_ADDR_NCLE 0x0C

#define S3C2410_ADDR_DATA 0x10

 

if (!(ctrl & NAND_CLE))

IO_ADDR_W |= S3C2410_ADDR_NCLE;

if (!(ctrl & NAND_ALE))

IO_ADDR_W |= S3C2410_ADDR_NALE;

改为

if(!(ctrl & NAND_CLE) && !(ctrl & NAND_ALE))

{

       IO_ADDR_W |= S3C2410_ADDR_DATA;

}

else{

       if (!(ctrl & NAND_CLE))

              IO_ADDR_W |= S3C2410_ADDR_NCLE;                    

       if (!(ctrl & NAND_ALE))

              IO_ADDR_W |= S3C2410_ADDR_NALE;

}

/* initialize hardware */

twrph0 = 3;

twrph1 = 0;

tacls = 0;

 

cfg = S3C2410_NFCONF_EN;

cfg |= S3C2410_NFCONF_TACLS(tacls - 1);

cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);

cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);

writel(cfg, &nand_reg->NFCONF);

改为

/* initialize hardware */

twrph0 = 4;

twrph1 = 2;

tacls = 0;

 

//cfg = S3C2410_NFCONF_EN;

cfg = S3C2410_NFCONF_TACLS(tacls);

cfg |= S3C2410_NFCONF_TWRPH0(twrph0);

cfg |= S3C2410_NFCONF_TWRPH1(twrph1);

writel(cfg, &nand_reg->NFCONF);

 

cfg = ((0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(0<<1)|(1<<0));

writel(cfg, &nand_reg->NFCONT);

 

3.      board/qljt/qljt2440/qljt2440.c

#if defined(CONFIG_CMD_NAND)

改为

#if 0

 

4.      board/qljt/qljt2440/flash.c

#include <common.h>

后添加

#ifndef CONFIG_SYS_NO_FLASH

 

文件末尾添加

#endif

5.      include/asm/arch-s3c24x0/s3c24x0.h

 

/* NAND FLASH (see S3C2410 manual chapter 6) */

struct s3c2410_nand {

       u32  NFCONF;

       u32  NFCMD;

       u32  NFADDR;

       u32  NFDATA;

       u32  NFSTAT;

       u32  NFECC;

};

改为

/* NAND FLASH (see S3C2410 manual chapter 6) */

struct s3c2410_nand {

       u32  NFCONF;

       u32 NFCONT;

       u32  NFCMD;

       u32  NFADDR;

       u32  NFDATA;

      u32 NFMECCD0;

      u32 NFMECCD1;

      u32 NFSECCD;

      u32  NFSTAT;

      u32 NFESTAT0;

      u32 NFESTAT1;

      u32 NFMECC0;

      u32 NFMECC1;   

      u32 NFSECC;

      u32 NFSBLK;

      u32 NFEBLK;

       //u32       NFECC;

};

/*=========到此为止所有nandfalsh命令正常使用,包括保存环境变量=========*/

 

. 移植从nandflash引导部分

到目前为止, u-boot都只能够在SDRAM中运行。下面开始移植把u-boot 加载到SDRAM的代码, 这样S3C2440A复位后就会自动把u-bootnandfalsh加载到SDRAM,u-boot可以正式烧写到nandflash中执行了。

1.      arch/arm/cpu/arm920t/start.S

#endif     /* CONFIG_SKIP_RELOCATE_UBOOT */

后添加(这段代码基本上全世界都是一样的)

//下面添加2440u-bootNand Flash启动

#ifdef CONFIG_S3C2440_NAND_BOOT

    mov r1, #NAND_CTL_BASE   //复位Nand Flash

    ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )

    str r2, [r1, #oNFCONF]   //设置配置寄存器的初始值,参考s3c2440手册

    ldr r2, [r1, #oNFCONF]

 

    ldr r2, =( (1<<4)|(0<<1)|(1<<0) )

    str r2, [r1, #oNFCONT]   //设置控制寄存器

    ldr r2, [r1, #oNFCONT]

 

    ldr r2, =(0x6)           //RnB Clear

    str r2, [r1, #oNFSTAT]

    ldr r2, [r1, #oNFSTAT]

    mov r2, #0xff            //复位command

    strb r2, [r1, #oNFCMD]

 

    mov r3, #0               //等待

nand1:

    add r3, r3, #0x1

    cmp r3, #0xa

    blt nand1

 

nand2:

    ldr r2, [r1, #oNFSTAT]   //等待就绪

    tst r2, #0x4

    beq nand2

 

    ldr r2, [r1, #oNFCONT]

    orr r2, r2, #0x2         //取消片选

    str r2, [r1, #oNFCONT]

 

    //get read to call C functions (for nand_read())

    ldr sp, DW_STACK_START   //C代码准备堆栈,DW_STACK_START定义在下面

    mov fp, #0              

 

    //copy U-Boot to RAM

    ldr r0, =TEXT_BASE

    mov r1, #0x0     

    mov r2, #0x30000 

    bl nand_read_ll  

    tst r0, #0x0

    beq ok_nand_read

 

bad_nand_read:

    loop2: b loop2    //infinite loop

 

ok_nand_read:

    //检查搬移后的数据,如果前4k完全相同,表示搬移成功

    mov r0, #0

    ldr r1, =TEXT_BASE

    mov r2, #0x400           //4 bytes * 1024 = 4K-bytes

go_next:

    ldr r3, [r0], #4

    ldr r4, [r1], #4

    teq r3, r4

    bne notmatch

    subs r2, r2, #4

    beq stack_setup

    bne go_next

 

notmatch:

    loop3: b loop3           //infinite loop

 

#endif //CONFIG_S3C2440_NAND_BOOT

 

 

 

_start_armboot:      .word start_armboot

后添加

#if defined(CONFIG_S3C2440_NAND_BOOT)

.align          2      

DW_STACK_START:  .word      STACK_BASE+STACK_SIZE-4  

#endif

 

2.      添加文件board/qljt/qljt2440/ nand_read.c, 内容如下

#include <config.h>

 

#define NF_BASE   0x4E000000  //Nand Flash配置寄存器基地址

 

#define __REGb(x) (*(volatile unsigned char *)(x))

#define __REGi(x) (*(volatile unsigned int  *)(x))

 

#define NFCONF __REGi(NF_BASE + 0x0 ) 

#define NFCONT __REGi(NF_BASE + 0x4 ) 

#define NFCMD  __REGb(NF_BASE + 0x8 ) 

#define NFADDR __REGb(NF_BASE + 0xC ) 

#define NFDATA __REGb(NF_BASE + 0x10) 

#define NFSTAT __REGb(NF_BASE + 0x20) 

 

#define NAND_CHIP_ENABLE  (NFCONT &= ~(1<<1))  //Nand片选使能

#define NAND_CHIP_DISABLE (NFCONT |= (1<<1))   //取消Nand片选

#define NAND_CLEAR_RB     (NFSTAT |= (1<<2))

#define NAND_DETECT_RB    { while(! (NFSTAT&(1<<2)) );}

 

#define NAND_SECTOR_SIZE 512

#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)

 

/* low level nand read function */

int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)

{

    int i, j;

 

    if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK))

    {

        return -1; //地址或长度不对齐

    }

 

    NAND_CHIP_ENABLE; //选中Nand片选

 

    for(i=start_addr; i < (start_addr + size);)

    {

        //发出READ0指令

 

        NAND_CLEAR_RB;

        NFCMD = 0;

 

        //Nand进行寻址

        NFADDR = i & 0xFF;

 

        NFADDR = (i >> 9) & 0xFF;

        NFADDR = (i >> 17) & 0xFF;

        NFADDR = (i >> 25) & 0xFF;

 

        NAND_DETECT_RB;

 

        for(j=0; j < NAND_SECTOR_SIZE; j++, i++)

        {

            *buf = (NFDATA & 0xFF);

            buf++;

        }

    }

 

    NAND_CHIP_DISABLE; //取消片选信号

 

    return 0;

}

 

3.      board/qljt/qljt2440/Makefile

COBJS    := qljt2440.o flash.o

改为

COBJS    := qljt2440.o flash.o nand_read.o

 

4. include/configs/qljt2440.h

#endif  /* __CONFIG_H */前添加

#define STACK_BASE    0x33f00000

#define STACK_SIZE    0x8000


#define CONFIG_S3C2440_NAND_BOOT  1

#define NAND_CTL_BASE  0x4E000000  //Nand Flash配置寄存器基地址

#define oNFCONF  0x00

#define oNFCONT  0x04                      

#define oNFADDR  0x0c

#define oNFDATA  0x10

#define oNFCMD   0x08

#define oNFSTAT  0x20                      

#define oNFECC   0x2c

      

       5. board/qljt/qljt2440/config.mk

TEXT_BASE = 0x30100000

改回

TEXT_BASE = 0x33F80000

 

 

/*=到此u-boot的移植工作完成, 只支持nandfalsh读写、网络、串口打印等基本功能=*/

附录:

1.      movldr的区别:

Mov 是把立即数赋给一个寄存器,但对立即数的范围有要求。只能是由8bit连续有效位通过偶数次移位能得到的数。如果立即数超出这个范围,就没办法用一条MOV指令给寄存器赋值。

LDR除了普通的读数之外,也有给寄存器赋立即数的功能。

你只要写 LDR R0=0xabcdef

它没有立即数范围的限制。因为这是一条伪指令。如果立即数在MOV的要求内,那就用一条汇编来实现。如果不在Mov的范围内,就用其它方式实现,如变成两条指令,或从PC偏移地址读一个32位数给寄存器。

MOV是从一个寄存器或者移位的寄存器或者立即数的值传递到另外一个寄存器
从本质上是寄存器到寄存器的传递,为什么会有立即数,其实也是有限制的立即数,不是所有立即数都可以传递的
这个立即数要符合一个8位数循环右移偶数位的取值
原因是,MOV本身就是一条32bit指令,除了指令码本身,它不可能再带一个可以表示32bit的数字,所以用了其中的12bit来表示立即数,其中4bit表示移位的位数(循环右移,且数值x2)8bit用来表示要移位的一个基数。

还有一点是关于ldr的,其实ldr可以装载一个32bit立即数的说法并不确切,因为实际上并不是这一条语句装载了一个32bit立即数,比如
ldr r1
=0x12345678
其实真正的汇编代码是将某个地址的值传递给r1,就是说需要一个地址存放0x12345678这个立即数,实际上可以看作是一条伪指令
而且如果这个立即数可以用mov指令的形式来表达,会被编译器实际用mov来代替
比如:
ldr r1,=0x10
会变成
mov r1,#0x10

ARMRISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。想把数据从内存中某处读取到寄存器中,只能使用ldr。比如:

ldr r0, 0x12345678

就是把0x12345678这个地址中的值存放到r0中。

mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中,这个和x86这种CISC架构的芯片区别最大的地方。 x86中没有ldr这种指令,因为x86mov指令可以将数据从内存中移动到寄存器中。

另外还有一个就是ldr伪指令,虽然ldr伪指令和ARMldr指令很像,但是作用不太一样。ldr伪指令可以在立即数前加上=,以表示把一个地址写到某寄存器中,比如:

ldr r0, =0x12345678

这样,就把0x12345678这个地址写到r0中了。所以,ldr伪指令和mov是比较相似的。只不过mov指令限制了立即数的长度为8位,也就是不能超过512。而ldr伪指令没有这个限制。如果使用ldr伪指令时,后面跟的立即数没有超过 8位,那么在实际汇编的时候该ldr伪指令是被转换为mov指令的。

ldr伪指令和ldr指令不是一个同东西。

 

2.      跳转指令的理解

b      start_code

ldr    pc,=start_code

都可以实现跳转,但他们并不一定跳到同一个地址,

b      start_code是相对相对跳转,相当于:

{

        temp = start_code – start_addr;

        pc = pc + temp;

}

start_addr: u-boot编译后System.map中第一个向量的地址。

ldr    pc,=start_code 是绝对地址跳转。

 

举例:假设start_addr0x30100000, start_code 0x30100010,这两个地址在编译的时候已经被决定了。在跳转前pc的值为0x5,那么

(1) 如果执行 b   start_code pc的值为0x15;

否则

(2)执行ldr     pc,=start_code, pc的值为0x30100010

 

3.      文字池技术

伪指令LTORG

    LTORG用于声明一个数据缓冲池,(也称为文字池)的开始。在使用伪指令LDR时,常常需要在适当的地方加入LTORG声明数据缓冲池,LDR加载的数据暂时放于数据缓冲池。

 

语法 LTORG

 

使用说明:

当程序中使用LDR之类的指令时,数据缓冲池的使用可能越界。为防止越界发生,可使用LTONG伪操作定义数据缓冲池。通常大的代码段可以使用多个数据缓冲池。ARM汇编编译器一般把数据缓冲池放在代码段的最后面,即下一代码段开始之前,或者END伪操作之前。LTORG伪操作通常放在无条件跳转指令之后,或者子程序返回指令之后,这样处理器就不会错误的将数据缓冲池中的数据当作指令来执行。

 

    LDR伪指令在汇编时,如果立即数可以用<shifter_operand>表示的话就替换为MOVMVN指令;如果不能用其表示则需要将该立即数放到一个文字池中,并生成一条将该文字池内容加载到目标寄存器的LDR指令。而使用LDR指令必须保证文字池在其可以访问的地址范围之内,对于ARM指令集来说就是4KB,所以必须在LDR指令前后4KB的范围内用LTORG显式地在代码段中添加一个文字池。

 

LTORG是在此指令出现的地方放一个文本池(literal pool). ARM汇编中常用到

 

    ldr   r0, =instruction     将地址instruction载入r0.

 

此时编译器将ldr尽可能的转变成movmvn指令。

 

如果转变不成, 将产生一个ldr指令, 通过pc相对地址从一块保存常数的内存区读出instruction的值。此内存区既是文本池。一般的, 文本池放在END指令之后的地方。但是, 如果偏移地址大于4k空间, ldr指令会出错(因为ldr的相对偏移地址为12-bit的值). 此时使用LTORG放到会出错的ldr指令附近, 以解决此问题。编译器会收集没有分配的ldr的值放到此文本池中。

程序举例:

ltorg_test:

       ldr   r1, =0x55555555

       mov pc, lr

 

       //.ltorg

 

malloc_space:

       .SPACE   4200

 

程序(3.1

ltorg_test:

       ldr   r1, =0x55555555

       mov pc, lr

 

       //.ltorg

 

malloc_space:

       .SPACE   3200

 

程序(3.2

ltorg_test:

       ldr   r1, =0x55555555

       mov pc, lr

 

       .ltorg

 

malloc_space:

       .SPACE   4200

 

程序(3.3

上面程序3.1不能编译通过,程序3.23.3可以编译通过

对程序3.13.2都没有 ”.ltorg” 指令,那么文本池在 ”.SPACE ***” 后设置, 很明显程序3.1的文本池在ldr伪指令偏移4200(大于4Kbytes)个字节以后创建,所以编译器会报错。

 

4.       对于所有的ARM伪指令,当用arm-linux-xxx编译的时候都要加“.

例如伪指令SPACE 100 就要写成 .SPACE 100

 

5.       伪指令

.align     2 告诉编译器下一个地址要以4字节对齐

.align     5 告诉编译器下一个地址要以32字节对齐

 

6.      lowlevel_init.S特别提醒

设置SDRAM寄存器的时候取值是从SMRDATA地址开始取的,而且该地址是个相对于0地址的偏移,所以在调试的时候,

执行了

tftp 30100000 u-boot.bin

后一定要执行

cp 30100000 0 1000

然后无论是执行

go 30100000 还是go 0 都可以从lowlevel_init返回,否则进入lowlevel_init后不返回。

 

7.      全局变量gd

gd在文件global_data.h中定义。

 

8. 调试u-boot的时候可以利用u-boot的反汇编, System.map等二进制文件进行辅助。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值