三十二.C语言环境初始化

1.栈为何物
是一种数据组织形式,具有后进先出的特点。其中栈底是第一个进栈的数据所处的位置。栈顶是最后一个进栈数据所处的位置。

2.满栈:堆栈指针SP总是指向当前最后进栈的数据
空栈:SP总是指向下一个将要存放数据的空位置

3.升栈:(升降都是针对 数据的入栈形式)SP由低地址向高地址移动
降栈:SP由高地址向低地址移动

4.ARM采用满降栈形式

5.栈帧:
一个程序对应一个进程,一个进程对应一个栈空间,然后一个程序对应多个函数,每个函数都会在该栈空间分配到一个完全属于自己的子空间,该子空间就是栈帧。栈帧有边界,上边界是FP(r11),下边界是SP(r13),每一个主调函数的边界都会存放在她的被调函数的栈帧中。

6.栈的作用
(1)保存局部变量
(2)当参数大于四个时候可以传递参数
(3)可以保存寄存器的值

7.代码:

@初始化栈
init_stack:
	ldr	sp,	=0x5FFFFFFF
	mov	pc,	lr





8.BSS段的作用
(1)初始化的全局变量存放在data段
(2)局部变量存放在栈
(3)malloc出的变量存放在堆
(4)未初始化的全局变量存放在BSS段

9.代码(为避免直接使用未初始化的全局变量引起异常,将BSS清0):
@清除BSS段
clean_bss:
	ldr	r0,	=bss_start
	ldr	r1,	=bss_end
	
	cmp	r0,	r1
	moveq	pc,	lr
	
clean_loop:
	mov	r2,	#0x0
	str	r2,[r0],#4
	cmp	r0,	r1
	bne	clean_loop
	
	mov	pc,	lr
	


10.进入C语言开发(进入BL2)
(1)要用绝对跳转,因为相对跳转只能在同一个代码段进行
(2)210代码搬移的时候,要跳过16B的头部
(3)其实就是一条指令:
然后修改Makefile,在里面添加
main.o
all : start.o mem.o main.o
	arm-linux-ld -Tg-boot.lds -o g-boot.elf $^
	arm-linux-objcopy -O binary g-boot.elf g-boot.bin
	arm-linux-objdump -D -S g-boot.elf > g-boot.dis
	
%.o : %.S
	arm-linux-gcc -g -c $^ -o $@

%.o : %.c
	arm-linux-gcc -g -c $^ -o $@
	
.PHONY: clean
clean:
	rm *.o *.elf *.bin *.bak *.dis -rf 
 


11.混合编程
(1)汇编的效率高,直接转换为机器语言,而C要经过转换为汇编
(2)汇编直接控制硬件的寄存器
基于以上原因,有时候必须的用汇编。但大多数场合都是用C,因为编写方便简洁。
(3)汇编调用c函数:
     ldr      pc,=函数名
(4)C调用汇编函数(标号)
先在汇编里声明为全局属性
然后在c里面直接:
               标号名();

12.C内嵌汇编:
(1)格式:
     __asm__(
     
           汇编语句部分
               :输出部分
               :输入部分
               :破坏描述部分     
     );
(2)以关键字__asm__或者asm开头,后面是(),注意后面不能忘记分号。四个部分都要用到分号
(3)汇编语句部分要用双引号括起来,如果有多条语句,要用换行符‘\n’或者分号隔开,每条指令都要用一个双引号括起来。
(4)第一部分不可少,后面三部分可以是空的,但是冒号也不能少。
(5)输出部分是在汇编里要修改的C的变量组成的列表,在分号表示的变量后面用()写入要传回到c里的变量,如:“=r”(var),var就是在c里定义的变量,就是汇编的输出。用%数字表示变量顺序。一般从%0开始。对应的从输出到输入。
(6)输入部分和输入部分类似,只不过他是要将c里面变量的值传递到汇编用。如:“r”(var)。
(7)破坏描述,就是修改部分描述,描述了哪些地方,变量,寄存器被修改了。
(8)优化,在sam后面加上volatile可以避免编译器对代码的优化。在硬件操作中,有时候优化很危险。
(9)实例:




10完整代码
(1)start.s
@****************************
@File:start.S
@
@Tiny6410裸机上学期代码
@
@Author:小君君
@****************************

.text
.global _start


@异常向量表
_start:
	b	reset
	ldr 	pc,	_Undefined_instruction
	ldr 	pc,	_Software_interrupt
	ldr	pc,	_Command_abort
	ldr	pc,	_Data_abort
	ldr	pc,	_Nothing_used
	ldr	pc,	_IRQ
	ldr	pc,	_FIQ
	
	
	
	
@存放实际异常入口地址开辟的存储单元	
_Undefined_instruction:
	.word Undefined_instruction
	
_Software_interrupt:
	.word Software_interrupt
	
_Command_abort:
	.word Command_abort
	
_Data_abort:
	.word Data_abort
	
_Nothing_used:
	.word Nothing_used
	
_IRQ:
	.word IRQ
	
_FIQ:
	.word FIQ


@各种实际的异常处理函数
Undefined_instruction:
	nop

Software_interrupt:
	nop

Command_abort:
	nop
	
Data_abort:
	nop
	
Nothing_used:
	nop
	
IRQ:
	nop
	
FIQ:
	nop
	
	
	
@上电复位以后就执行这里
reset:
	bl	set_svc
	bl 	set_peri_port
	bl	disable_watchdog
	bl 	disable_interrupt
	bl	disable_mmu
	bl	clock_init
	bl	mem_init
	bl	copy_to_ddr
	bl	init_stack
	bl	clean_bss
	
	bl	light_led
	
	ldr	pc,	=main
	
halt:
	b halt
	
	

@通过读-修改-写的方式控制CPSR从而改变工作模式的svc模式
set_svc:
	
	mrs	r0,	cpsr
	bic	r0,r0,#0x1F
	orr	r0,r0,#0xD3
	msr	cpsr,	r0
	mov	pc,	lr


@关闭看门狗
#define	pWTCON 0x7E004000	
disable_watchdog:
	ldr	r0,	=pWTCON
	mov	r1,	#0x00
	str	r1,	[r0]
	mov 	pc,	lr
	


@屏蔽中断,要操作两个寄存器,目的是将使能中断寄存器的相关位清除
#define VIC0INTENCLEAR 0x71200014 
#define VIC1INTENCLEAR 0x71300014 
disable_interrupt:
	ldr	r0,	=VIC0INTENCLEAR
	mvn 	r1,	#0x0
	str	r1,	[r0]
	
	ldr	r0,	=VIC1INTENCLEAR
	str	r1,	[r0]
	
	mov 	pc,	lr	
	
@关闭MMU和Cache	
disable_mmu:
	mcr	p15,0,r0,c7,c7,0
	mrc	p15,0,r0,c1,c0,0
	bic	r0,r0,#0x07
	mcr 	p15,0,r0,c1,c0,0
	mov 	pc,	lr




@外设地址初始化,要放在进入svc模式以后的第一步操作
set_peri_port:		
	ldr r0, =0x70000000 					@对于6410来说,内存(0x00000000~0x60000000),外设(0x70000000-0x7fffffff)
	orr r0, r0, #0x13						@外设大小:256M
	mcr p15,0,r0,c15,c2,4       			 @把r0的值(包括了外设基地址+外设大小)告诉cpu
	mov	pc,	lr


@时钟初始化
#define APLL_LOCK 0x7E00F000
#define CLK_DIV0 0x7E00F020
#define OTHERS 0x7E00F900
#define MPLL_CON 0x7E00F010
#define APLL_CON 0x7E00F00C
#define CLK_SRC 0x7E00F01C


@#define ARM_RATIO  	0   						@ARMCLK 	= DOUTAPLL / (ARM_RATIO + 1)  	= 532/(0+1) = 532  MHz
@#define MPLL_RATIO	0   						@ DOUTMPLL = MOUTMPLL / (MPLL_RATIO + 1)   = 532/(0+1) = 532  MHz
@#define HCLKX2_RATIO 1   						@ HCLKX2 	= HCLKX2IN / (HCLKX2_RATIO + 1) = 532/(1+1) = 266  MHz
@#define HCLK_RATIO   1   						@ HCLK 	= HCLKX2   / (HCLK_RATIO + 1)   = 266/(1+1) = 133  MHz
@#define PCLK_RATIO   3   						@ PCLK   	= HCLKX2   / (PCLK_RATIO + 1)   = 266/(3+1) = 66.5 MHz

#define DIV_VAL  ((0 << 0)|(0 << 4)|(1 << 8)|(1 << 9)|(3 << 12))	@注意这里不能用#define DIV_VAL (ARM_RATIO) | (MPLL_RATIO << 4) | (HCLK_RATIO << 8) | (HCLKX2_RATIO << 9) | (PCLK_RATIO << 12)
									@原因暂时未查明,不知道是不是我电脑最近鼠标抽风,把字符编码格式改变了导致的结果
clock_init:
	@ 1. 设置各PLL的LOCK_TIME,使用默认值  
	ldr 	r0, 	=APLL_LOCK  						@ APLL_LOCK,供cpu使用 
	ldr 	r1, 	=0x0000FFFF
	str 	r1, 	[r0]
	
	str 	r1, 	[r0, #4]	 						@ MPLL_LOCK,供AHB(存储/中断/lcd等控制器)/APB(看门狗,定时器,SD等)总线上的设备使用 
	str 	r1, 	[r0, #8]	 						@ EPLL_LOCK,供UART,IIS,IIC使用 
	
	@ 2. 设置为异步模式 
	ldr 	r0, 	=OTHERS							@ OTHERS
												@ 《linux installation for u-boot》3.7中:用MPLL作为HCLK和PCLK的Source是异步(ASYNC)模式
												@ 用APLL是同步(SYNC)模式
	ldr 	r1, 	[r0]
	bic r1, r1, #0xc0							@ bit[6:7]清0,即SYNCMODE=0/SYNCMUXSEL=0
	str 	r1, 	[r0]
loop_clock:			
	ldr 	r0, 	=OTHERS
	ldr 	r1, 	[r0]
	and r1, r1, #0xf00					
	cmp 	r1, 	#0
	bne 	loop_clock

	@ 3. 设置分频系数  
	ldr 	r0, 	=CLK_DIV0  						@CLK_DIV0
	ldr 	r1,	=DIV_VAL
	str 	r1, 	[r0]
	
	@ 4. 设置PLL,放大时钟  
	@ 4.1 配置APLL  
#define APLL_CON_VAL  ((1<<31) | (266 << 16) | (3 << 8) | (1))
	ldr 	r0, 	=APLL_CON							@ APLL_CON
	ldr 	r1, 	=APLL_CON_VAL						@ FOUT = MDIV X FIN / (PDIV X 2SDIV) = 266*12/(3*2^1) = 532MHz
	str 	r1, 	[r0]		
	
	@ 4.2 配置MPLL  
#define MPLL_CON_VAL  ((1<<31) | (266 << 16) | (3 << 8) | (1))
	ldr 	r0, 	=MPLL_CON							@ MPLL_CON
	ldr 	r1, 	=MPLL_CON_VAL						@ FOUT = MDIV X FIN / (PDIV X 2SDIV) = 266*12/(3*2^1) = 532MHz
	str 	r1, 	[r0]		

#define MPLL_SEL 1
#define APLL_SEL 1	
	@ 5.选择PLL的输出作为时钟源  
	ldr 	r0, 	=CLK_SRC							@ CLK_SRC
	ldr 	r1, 	=(MPLL_SEL<<1) | (APLL_SEL<<0)
	str 	r1, 	[r0]
	
	mov 	pc, 	lr

@将bin文件从_start开始到bss_start结束的数据搬移到_start指定的链接地址(0x50008000)
copy_to_ddr:
	adr	r0,	_start
	ldr	r1,	=_start
	ldr	r2,	=bss_start
	
copy_loop:
	ldr	r3,[r0],#4
	str	r3,[r1],#4
	cmp	r1,r2
	bne	copy_loop
	
	mov	pc,	lr

@初始化栈
init_stack:
	ldr	sp,	=0x5FFFFFFF
	mov	pc,	lr
	

@清除BSS段
clean_bss:
	ldr	r0,	=bss_start
	ldr	r1,	=bss_end
	
	cmp	r0,	r1
	moveq	pc,	lr
	
clean_loop:
	mov	r2,	#0x0
	str	r2,[r0],#4
	cmp	r0,	r1
	bne	clean_loop
	
	mov	pc,	lr
	
	
		

@点亮LED
#define GPKCON0 0x7F008800
#define GPKDAT  0x7F008808
light_led:	
	@设置GPKCON0
	ldr r1, =GPKCON0 					
	ldr r0, =0x11110000																  
	str r0, [r1]
	
	
	@设置GPKDAT点亮4颗LED
	ldr r0, =GPKDAT
	ldr r1, =0x0
	str r1, [r0]
	
	
	@延时函数,将会使得开机的时候4颗led先亮一段时间,大约4秒钟左右,之后只亮两颗LED表明进入C语言的main函数
	ldr	r0,	=0xFFFFFF
loop_led:
	sub	r0,r0,#1
	cmp	r0,	#1
	bne	loop_led
	
	mov pc, lr
	


	
	
	

(2)mem.s
@****************************
@File:mem.S
@
@Tiny6410内存初始化
@
@Author:小君君
@****************************


.text
.global mem_init

#define MEMCCMD		0x7e001004
#define P1REFRESH	0x7e001010
#define P1CASLAT	0x7e001014
#define MEM_SYS_CFG	0x7e00f120
#define P1MEMCFG	0x7e00100c
#define P1T_DQSS	0x7e001018
#define P1T_MRD		0x7e00101c
#define P1T_RAS		0x7e001020
#define P1T_RC		0x7e001024
#define P1T_RCD		0x7e001028
#define P1T_RFC		0x7e00102c
#define P1T_RP		0x7e001030
#define P1T_RRD		0x7e001034
#define P1T_WR		0x7e001038
#define P1T_WTR		0x7e00103c
#define P1T_XP		0x7e001040
#define P1T_XSR		0x7e001044
#define P1T_ESR		0x7e001048
#define P1MEMCFG2	0X7e00104c
#define P1_chip_0_cfg	0x7e001200

#define P1MEMSTAT	0x7e001000
#define P1MEMCCMD	0x7e001004
#define P1DIRECTCMD	0x7e001008

#define refresh_val 	(( 7800 / ( 1000000000/133000000 ) + 1 )) 	@刷新周期:(7.8us)/((1/HCLK)s)=(7.8*10^3)/(1/133*10^6)

mem_init:
	@0. 将data[31:26]设置为data pins
	ldr 	r0,	=MEM_SYS_CFG
	mov	r1,	#0x0
	str	r1,	[r0]
	
	
	@1. 使dramc进入"config"状态
	ldr	r0,	=P1MEMCCMD
	mov	r1,	#0x4
	str	r1,	[r0]
	
	
	@2. 设置timing parameter, chip configuration,id configuration registers
	
	@2.1 刷新周期
	ldr	r0,	=P1REFRESH
	ldr	r1,	=refresh_val
	str	r1,	[r0]
	
	@2.2 时间参数,下列设置全都是取了最小值
	ldr	r0,	=P1CASLAT	@CAS Latency:指的是内存存取数据所需的延迟时间,
					@简单的说,就是内存接到CPU的指令后的反应速度。
					@一般的参数值是2和3两种。K4X1G163PQ的芯片手册上CAS Latency=3 
	mov	r1,	#6		@下列设置均在sdram手册中可查询到	
	str	r1,	[r0]
	
	ldr	r0,	=P1T_DQSS
	mov	r1,	#0x1
	str	r1,	[r0]
	
	ldr	r0,	=P1T_MRD
	mov	r1,	#0x2
	str	r1,	[r0]
	
	ldr	r0,	=P1T_RAS
	ldr	r1,	=(( 42 / ( 1000000000/133000000 ) + 1 ))
	str	r1,	[r0]
	
	ldr	r0,	=P1T_RC
	ldr	r1,	=(( 60 / ( 1000000000/133000000 ) + 1 ))
	str	r1,	[r0]


#define trcd	(( 18 / ( 1000000000/133000000 ) + 1 ))
#define	trcd_val	((trcd | ((trcd-3)*8)))
	ldr	r0,	=P1T_RCD
	ldr	r1,	=trcd_val
	str	r1,	[r0]
	
#define trfc	(( 72 / ( 1000000000/133000000 ) + 1 ))
#define	trfc_val	((trfc | ((trfc-3)*32)))
	ldr	r0,	=P1T_RFC
	ldr	r1,	=trfc_val
	str	r1,	[r0]
	
#define trp	(( 18 / ( 1000000000/133000000 ) + 1 ))
#define	trp_val	((trp | ((trp-3)*8)))
	ldr	r0,	=P1T_RP
	ldr	r1,	=trp_val
	str	r1,	[r0]
	

	ldr	r0,	=P1T_RRD
	ldr 	r1,	=(( 12 / ( 1000000000/133000000 ) + 1 ))
	str	r1,	[r0]
	
	ldr	r0,	=P1T_WR
	ldr 	r1,	=(( 12 / ( 1000000000/133000000 ) + 1 ))
	str	r1,	[r0]
	
	
	ldr	r0,	=P1T_WTR
	mov 	r1,	#0x1
	str	r1,	[r0]
	
	ldr	r0,	=P1T_XP
	mov 	r1,	#0x1
	str	r1,	[r0]
	
	ldr	r0,	=P1T_XSR
	ldr 	r1,	=(( 120 / ( 1000000000/133000000 ) + 1 ))
	str	r1,	[r0]
	
	ldr	r0,	=P1T_ESR
	ldr 	r1,	=(( 120 / ( 1000000000/133000000 ) + 1 ))
	str	r1,	[r0]


		
	@2.3 chip configuration
@****************************
@结合DDR的手册3. Address configuration这个部分
@1.column address(10):A0~A9列地址P1MEMCFG[2:0]=0x2
@2.row address(14):A0~A13,行地址P1MEMCFG[5:3]=0x3
@3.A10/AP 设置自动预充电地址P1MEMCFG[6]=0x0
@4.Burst Length (2, 4, 8, 16)设置内存突发长度P1MEMCFG[17:15]=0x2
@****************************

	ldr	r0,	=P1MEMCFG
	ldr	r1,	=0x0001001a
	str	r1,	[r0]
	
	
	
	
@****************************
@
@1.Sets the level for the cke outputs after reset. 设置reset以后的cke引脚的输出电平P1MEMCFG2[3],一般设置为0,P1MEMCFG2[0:2]用默认的
@所以P1MEMCFG2[3:0] = 0x5
@2.设置存储长度。6410是32位机器,所以就是01了P1MEMCFG2[7:6]=0x1
@3.设置DDR类型,6410用的是Mobile DDR SDRAMP1MEMCFG2[10:8]=0x3
@4.Read delay设置读取延时,针对
@用01 = Read delay 1 cycle (usually for DDR SDRAM and mobile DDR SDRAM)DDRP1MEMCFG2[12:11]=0x1

@****************************
	ldr	r0,	=P1MEMCFG2
	ldr	r1,	=0x00000B45
	str	r1,	[r0]
	
	
@****************************
@设置芯片(片选)	
@1.设定地址组织格式Bank-Row-Column organization。P1_chip_0_cfg[16]=0x1
@2.设定AXI_chip_base。因为6410的DDR从0x50000000开始,所以AXI_chip_base是0x50.P1_chip_0_cfg[15:8]=0x50
@3.设定AXI_chip_mask。要想让AXI_chip_mask和AXI_addr[31:24]按位与以后与AXI_chip_base相等,所以AXI_chip_mask选择0XF0.P1_chip_0_cfg[7:0]=0xF0
@****************************	

	
	ldr	r0,	=P1_chip_0_cfg
	ldr	r1,	=0x000150F0
	str	r1,	[r0]


	
	@3. 初始化sdram
	
	@NOP
	ldr	r0,	=P1DIRECTCMD
	ldr	r1,	=0xc0000
	str	r1,	[r0]
	
	@precharge
	ldr	r0,	=P1DIRECTCMD
	ldr	r1,	=0x000
	str	r1,	[r0]
	
	@auto refresh
	ldr	r0,	=P1DIRECTCMD
	ldr	r1,	=0x40000
	str	r1,	[r0]
	
	@auto refresh
	ldr	r0,	=P1DIRECTCMD
	ldr	r1,	=0x40000
	str	r1,	[r0]
	
	@auto refresh
	ldr	r0,	=P1DIRECTCMD
	ldr	r1,	=0xa0000
	str	r1,	[r0]
	
	@auto refresh
	ldr	r0,	=P1DIRECTCMD
	ldr	r1,	=0x80032
	str	r1,	[r0]
	
	
	@4. 使dramc进入"ready"状态
	
	ldr	r0,	=P1MEMCCMD
	mov 	r1, 	#0x0
    	str 	r1, 	[r0]
	@5. 等待dramc进入"ready"状态
wait_for_ready:
    
	ldr 	r0, 	=P1MEMSTAT 
	ldr 	r1, 	[r0]
	mov 	r2, 	#0x3
	and r1, r1, r2
	cmp 	r1, 	#0x1
	bne 	wait_for_ready
	nop	
	
	mov	pc,	lr


(3)main.c
/****************************
@File:main.c
@
@Tiny6410裸机上学期代码
@进入C语言以后只点亮两颗LED,在汇编阶段是点亮4颗LED,以此作对比表明进入C语言成功
@Author:小君君
@****************************/

#define LED_CON 0x7F008800
#define LED_DAT  0x7F008808
#define  vi   *( volatile unsigned int * ) 


int main(void)
{
	
	(vi LED_CON) = 0x11110000;
	
	(vi LED_DAT) = 0xa0;
	
	while(1)
		;
	
	return 0;
}

(4)Makefile
all : start.o mem.o main.o
	arm-linux-ld -Tg-boot.lds -o g-boot.elf $^
	arm-linux-objcopy -O binary g-boot.elf g-boot.bin
	arm-linux-objdump -D -S g-boot.elf > g-boot.dis
	
%.o : %.S
	arm-linux-gcc -g -c $^ -o $@

%.o : %.c
	arm-linux-gcc -g -c $^ -o $@
	
.PHONY: clean
clean:
	rm *.o *.elf *.bin *.bak *.dis -rf 
 

(5)链接器脚本
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS {
	. = 0x50008000;
	. = ALIGN(4);
	.text :
	{
		start.o(.text)
		*(.text)
	}
	
	. = ALIGN(4);
	.data :
	{
		*(.data)
	}
	
	. = ALIGN(4);
	bss_start = .;
	.bss :
	{
		*(.bss)
	}
	bss_end = .;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值