完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
说一下u-boot,它是一个在嵌入式设备中相当于电脑bootloader的一个东西,能干啥:1.初始化硬件 2.启动内核
只有内核启动了才能让一个系统在各种不一样的硬件上跑起来,这样才能扔给那些不懂底层硬件事情的程序员编写应用程序app.所以,系统工程师才是真正江湖明白人啊,嘻嘻嘻。这是链接脚本(u-boot.lds),内存是这么放的。Makefile在编译时根据u-boot.lds去放的各个段,下面的代码我都写了相关的注释,有问题请指点,看不懂的话就只能对不起你自己了,更对不起我,好,话不多说 SECtiONS { . = 0x33f80000; .text : { *(.text) } //在链接脚本里 .text表示代码内存的代码段。代码段放在0x33f80000,也就是0x33f80000开始执行程序 . = ALIGN(4); .rodata : {*(.rodata*)} //只读数据段 代码段的结束地址按4个对齐接下来就放只读数据段 . = ALIGN(4); .data : { *(.data) } //数据段 . = ALIGN(4); __bss_start = .; .bss : { *(.bss) *(COMMON) } //bss段,是未初始化的全局变量先放这里,这些数据没用,程序会清掉的 __bss_end = .; } 为什么需要这样的一个链接脚本:如果不这么放没有这些代码段数据段的环境的话程序是无法运行的,要知道我们正处于0阶段,c程序还不能运行,这个环境还只够汇编程序运行,代码的第一个入口程序文件一定得用汇编写,这个问价是设备一开机就会运行的那种,现在的所有的嵌入式设备也好PC机也好入口程序肯定是汇编这种比较直接的机器语言。 首先需要编写汇编程序start.S (底层初始化) start.S必要干的事情有以下(百问科技的JZ2440平台为例): 1. 关闭看门狗 2.设置始终分频比例,频率 3.内存控制器初始化 45.设置栈(想调用c函数先设置栈) 5.nand初始化并重定位(nand_init是c函数) 6.进入main start.S源代码如下: #define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01)) #define MEM_CTL_BASE 0x48000000 .text .global _start //_start标识符=0x33f80000 第一个指令挂载到0x33f80000 _start: /* 1. 关看门狗 */ ldr r0, =0x53000000 mov r1, #0 str r1, [r0] /* 2. 设置时钟 */ ldr r0, =0x4c000014 mov r1, #0x05; // PCLK:HCLK:FCLK=1:2:8 str r1, [r0] /* 如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode” */ mrc p15, 0, r1, c1, c0, 0 /* 读出控制寄存器 */ orr r1, r1, #0xc0000000 /* 设置为“asynchronous bus mode” */ mcr p15, 0, r1, c1, c0, 0 /* 写入控制寄存器 */ ldr r0, =0x4c000004 ldr r1, =S3C2440_MPLL_400MHZ //MPLL=400MHZ str r1, [r0] /* 启动ICACHE和DCACHE 会更快 */ mrc p15, 0, r0, c1, c0, 0 @ read control reg //启动I/D CACHE内存,这样很快。 可以去掉这段代码看看,然后发现设备启动起来比毛驴车还慢 orr r0, r0, #(1<<12) //cache威廉希尔官方网站 可以查资料自己看,也就是一个内部的很快很高贵的高速内存。 mcr p15, 0, r0, c1, c0, 0 @ write it back /* 3. 初始化内存控制器,就可以读写sdram了 */ ldr r0, =MEM_CTL_BASE adr r1, sdram_config /* sdram_config的当前地址 */ add r3, r0, #(13*4) /*r3是结束地址*/ 1: ldr r2, [r1], #4 str r2, [r0], #4 cmp r0, r3 bne 1b /* 4. 重定位 : 把bootloader本身的代码从flash复制到它的链接地址去 */ ldr sp, =0x34000000 //设置栈 内存的最顶 //从0x33f80000(在.lds链接脚本里的链接地址)到0x34000000总共是512kB空间 bl nand_init //nand初始化 mov r0, #0 ldr r1, =_start ldr r2, =__bss_start sub r2, r2, r1 bl copy_code_to_sdram //重定位函数 参数r0=0,r1=_start(0x33f80000), r2=u-boot_size bl clear_bss /* 5. 执行main */ ldr lr, =halt ldr pc, =main halt: b halt sdram_config: .long 0x22011110 //BWSCON .long 0x00000700 //BANKCON0 .long 0x00000700 //BANKCON1 .long 0x00000700 //BANKCON2 .long 0x00000700 //BANKCON3 .long 0x00000700 //BANKCON4 .long 0x00000700 //BANKCON5 .long 0x00018005 //BANKCON6 .long 0x00018005 //BANKCON7 .long 0x008C04F4 // REFRESH .long 0x000000B1 //BANKSIZE .long 0x00000030 //MRSRB6 .long 0x00000030 //MRSRB7 设置好栈以后可以运行C程序,nand flash要初始化才能在往后的程序里读写,这样才能重定位。有一个问题一直困扰我,机器一运行就从nand flash拷贝代码到内部sram,我觉得应该是芯片内部有固化的代码,拷贝到内部sram后就得重新编写nand flash的读写程序,固化的copy程序已经不能用了,不在代码区内。我猜的,芯片手册上nand flash那个页面有一个框图,根据这个去猜的,也挺合逻辑的,不过理解上要不能这么虚,还请各位路过朋友们指点。那,继续说,第一个C程序就是nand flash的读写,因为要为重定位做准备。 初始化nand flash 设置栈后第一个程序是nand_init函数 void nand_init(void) { #define TACLS 0 /* 设置时序 */ //看2440芯片手册根据读写flash接口的时序配置 #define TWRPH0 1 #define TWRPH1 0 NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4); NFCONT = (1<<4)|(1<<1)|(1<<0); /* 使能NAND Flash控制器, 初始化ECC, 禁止片选 */ } TACLS,TWRPH0,TWRPH1这三个时序参数设置好了就可以完成nand芯片初始化工作,能读写了。根据时序图和K9F2G08U0M(nand存储器)的时序要求去配置,下面是2440和nand芯片datasheet里的截图,根据这个去配置: 可以发现 TWRPH1对应talh/tclh>=5n TWRPH0对应twp>=15ns TWRPH0: twp>=15ns(第二个图查表)=此处duration的值 HCLK x (TWRPH0 + 1) -> HCLK=100MHZ(10ns) -> 15ns<=10ns * (TWRPH0 +1 ) =0.5->Duaration=1 ->[10:8]=001 TWRPH1: talh/tclh>=5ns(查表) 同理 HCLK x (TWRPH1 + 1) 5ns<=HCLK x (TWRPH1 + 1) -> Duration=0时成立 [6:4]=000 TACLS: =tcls-twp tcls>=15ns twp>=15 TACLS=15-15=0 Duration=HCLK x TACLS = 0 [13:12]=00 其他的读写功能函数,在init.c文件里放着,已上传工程文件 flash的事情已经搞定。最后一部从flash里读出内核加载启动。这都在main函数里实现。调用c程序前,汇编程序中设置栈。 int main(void) { void (*theKernel)(int zero, int arch, unsigned int params); //设定函数指针 volatile unsigned int *p = (volatile unsigned int *)0x30008000; //0x30008000是读出内核后加载到sdram内存的地址 uart0_init(); /* 帮内核设置串口: 内核启动的开始部分会从串口打印一些信息,但是内核一开始没有初始化串口 */ puts("Copy kernel from nandnr"); /* 从NAND FLASH里把内核读入内存 */ nand_read(0x60000+64, (unsigned char *)0x30008000, 0x200000); //0x60000+64是nand flash里内核存放地址,0x30008000目标目标加载地址,0x200000=size /* 2. 设置参数 */ puts("Set boot paramsnr"); setup_start_tag(); setup_memory_tags(); setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0"); setup_end_tag(); /* 3. 跳转执行 */ puts("Boot kernelnr"); theKernel = (void (*)(int, int, unsigned int))0x30008000; //设置0x30008000=theKernel函数的入口地址。 theKernel(0, 362, 0x30000100); //调用theKernel启动内核命令。362是机器ID,0x30000100是有参数的地方 /* * mov r0, #0 * ldr r1, =362 * ldr r2, =0x30000100 * mov pc, #0x30008000 */ puts("Error!nr"); /* 如果一切正常, 不会执行到这里 */ return -1; } 完结,这里摆出来的代码不全,只强调了一些重要的代码过程。 已附代码,请放心下载自己可以分析。
|
|
相关推荐
|
|
迅为RK3568开发板篇OpenHarmony配置HDF驱动控制LED-配置创建私有配置文件
628 浏览 0 评论
飞凌嵌入式ElfBoard ELF 1板卡-初识设备树之Makefile修改
732 浏览 0 评论
飞凌嵌入式-ELFBOARD-ELF 2硬件知识分享之Debug
940 浏览 1 评论
飞凌嵌入式ElfBoard ELF 1板卡-烧录流程介绍之单独更新内核
2479 浏览 1 评论
飞凌嵌入式ElfBoard ELF 1板卡-TF卡烧录流程之烧写过程
1061 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-12 04:11 , Processed in 0.524638 second(s), Total 76, Slave 55 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号