QLJT移植u-boot-2010.09到s3c2440
作者:梁桥江.
转载请注明出处
前言:本文针对对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
2.include/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语言部分移植
1.arch/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()函数,这就跟系统时钟拉上关系了,于是开始从系统时钟着手寻找串口输出乱码的原因。
通过重新阅读s3c2440的datasheet后,对
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)工作正常===============*/
四. 开始nandflash的mtd移植, 我的板子上之后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-boot从nandfalsh加载到SDRAM中,u-boot可以正式烧写到nandflash中执行了。
1. arch/arm/cpu/arm920t/start.S
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
后添加(这段代码基本上全世界都是一样的)
//下面添加2440中u-boot从Nand 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. mov与ldr的区别:
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
ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。想把数据从内存中某处读取到寄存器中,只能使用ldr。比如:
ldr r0, 0x12345678
就是把0x12345678这个地址中的值存放到r0中。
而mov不能干这个活,mov只能在寄存器之间移动数据,或者把立即数移动到寄存器中,这个和x86这种CISC架构的芯片区别最大的地方。 x86中没有ldr这种指令,因为x86的mov指令可以将数据从内存中移动到寄存器中。
另外还有一个就是ldr伪指令,虽然ldr伪指令和ARM的ldr指令很像,但是作用不太一样。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_addr为0x30100000, 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>表示的话就替换为MOV或MVN指令;如果不能用其表示则需要将该立即数放到一个文字池中,并生成一条将该文字池内容加载到目标寄存器的LDR指令。而使用LDR指令必须保证文字池在其可以访问的地址范围之内,对于ARM指令集来说就是4KB,所以必须在LDR指令前后4KB的范围内用LTORG显式地在代码段中添加一个文字池。
LTORG是在此指令出现的地方放一个文本池(literal pool). 在ARM汇编中常用到
ldr r0, =instruction 将地址instruction载入r0.
此时编译器将ldr尽可能的转变成mov或mvn指令。
如果转变不成, 将产生一个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.2和3.3可以编译通过
对程序3.1和3.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等二进制文件进行辅助。