完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
` 中奖名单见: https://bbs.elecfans.com/jishu_422412_1_1.html 感谢大家的热情支持,更加要感谢作者朱兆祺(@zzq宁静致远 )的大力支持,慷慨拿出20本《嵌入式Linux开发实用教程》送给大家。 嵌入式Linux开发实用教程 如何让这本充满情怀的书到你碗里去? 规则一: 致亲爱的发烧友们: ★你是不是常常看到单片机“裸奔”,想给MCU/处理器穿上一层“衣服(操作系统)” ★你是不是正在寻找嵌入式Linux修炼秘诀? ★你是不是期待着到自己在嵌入式linux领域有更大的进步? 期待着自己能从Linux的野战军变成正规军甚至王牌军吗? 我们,等你来! 征集时间 2014-02.19 至 2014-03-16 参与规则 下载附件《嵌入式linux开发实用教程》 嵌入式Linux开发实用教程(试用).pdf (705.32 KB, 下载次数: 0) ,阅读《嵌入式linux开发实用教程》试读文章,在本帖中回答3个小问题或者在嵌入式板块单独开帖分析问题,就有机会获得《嵌入式linux开发实用教程》赠书1本,越详细能得到书本的机会越大。 3个问题 2.DDR的内存是如何分配? 3.Main_loop()函数的作用是什么? 规则二: 以“我与linux XXX的段子”为题,在本板块[Linux论坛]里发表新帖,分享你在linux开发过程中的逸闻趣事,或者开发过程中遇到的大大小小问题,又或者分享你是如何开始你的linux之旅的,也许还因为linux结下某段不解之缘,又可能因为linux成功卖身,还可能忘linux于身后,入他行而谋生。数之不尽,道之不截,总之是你与linux的故事,大小均可,重在分享,任你发挥,引发讨论火热的同学将获得赠书喔! PS:“XXX”可以是最囧,最坑,最惊悚,最开心,最浪漫,最可爱,最蛋疼、...... 公布获奖名单的时间: 3月3日、3月17日,每次公布10人,一共20本送出! 想获得特别赠言的朋友还可以再此回帖留下你的赠言! 另外朱兆祺正在开展 新年学生大礼包,凭借学生证购买开发板可免费领取书本、蓝牙模块:
嵌入式linux开发实用教程 目录.doc
(22.5 KB, 下载次数: 177
)
` |
|
相关推荐
83 个讨论
|
|
|
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
答:包括了 led 灯配置,关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤醒复位。 2.DDR的内存是如何分配? 答:1. SDRAM的最后64K分配给TLB,所分配的地址为:0x57FF 0000~0x57FF FFFF。 2.给u-boot分配BSS、数据段、代码段,分配地址为:0x57F7 5000~0x57FE FFFF。 3.malloc空间,分配的地址为:0x57E6 D000~0x57E7 4FFF。 4.bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 5.gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7. 6.分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 3.Main_loop()函数的作用是什么? 答:1.HUSH的相关初始化 2.bootdelay的初始化 3.启动次数 4.Modem功能 5.设置U-Boot版本号 6.启动tftp功能 7.打印启动菜单 |
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
函数的工作是进行与单板相关的初始化工作这个初始化仅仅是最低限度(lowlevel)的,包括led灯配置(便于观察现象)、关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤醒复位。 2.DDR的内存是如何分配? SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为0x5000 0000~0x57FF FFFF SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB, 所分配的地址为:0x57FF 0000~0x57FF FFFF。 SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为:0x57F7 5000~0x57FE FFFF。 一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF。 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7。 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 说明SDRAM的起始地址是:0x5000 0000。 3.Main_loop()函数的作用是什么? 1) HUSH的相关初始化 2) bootdelay的初始化 3) 启动次数 4) Modem功能 5) 设置U-Boot版本号 6) 启动tftp功能 7) 打印启动菜单 main_loop()的主要作用即是U-Boot启动管理。 3.Main_loop()函数的作用是什么? 答:Main_loop()函数的作用: 1.HUSH的相关初始化 2.bootdelay的初始化 3.启动次数 4.Modem功能 5.设置U-Boot版本号 6.启动tftp功能 7.打印启动菜单 |
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
lowlevel_init函数的工作是进行与单板相关的初始化工作,包括led灯配置、关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤醒复位。 2.DDR的内存是如何分配? 1) SDRAM的最后64K分配给TLB,所分配的地址为:0x57FF 0000~0x57FF FFFF。 2)给u-boot分配BSS、数据段、代码段,分配地址为:0x57F7 5000~0x57FE FFFF。 3)malloc空间,分配的地址为:0x57E6 D000~0x57E7 4FFF。 4)bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 5)gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7. 6)分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 3.Main_loop()函数的作用是什么? 1) HUSH的相关初始化 2) bootdelay的初始化 3) 启动次数 4) Modem功能 5) 设置U-Boot版本号 6) 启动tftp功能 7) 打印启动菜单 |
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
接下来是执行带返回跳转指令“bl lowlevel_init”。调用lowlevel_init 函数(位于 boardsamsungsmdk6410lowlevel_init.s)。lowlevel_init 函数的工作是进行与单板相关的 初始化工作,故名思议,这个初始化仅仅是最低限度(lowlevel)的,包括led 灯配置(便于 观察现象)、关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤 醒复位。 1) 配置led ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x55540000 str r1, [r0, #GPNCON_OFFSET] ldr r1, =0x55555555 str r1, [r0, #GPNPUD_OFFSET] ldr r1, =0xf000 str r1, [r0, #GPNDAT_OFFSET] 这里应该改成与s3c6410相适应的配置,单板使用GPM0-GPM3管脚驱动led。根据 s3c6410用户手册中的端口M控制寄存器章节可以对程序作出如下修改。 /* LED on only #8 */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x00111111 str r1, [r0, #GPMCON_OFFSET] ldr r1, =0x00000555 str r1, [r0, #GPMPUD_OFFSET] /* all of LEDs are power on */ ldr r1, =0x000f str r1, [r0, #GPMDAT_OFFSET] 根据需要,LED测试自行修改: /* LED test */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x0003 str r1, [r0, #GPMDAT_OFFSET] 2) 关闭看门狗 ldr r0, =0x7e000000 @0x7e004000 orr r0, r0, #0x4000 mov r1, #0 str r1, [r0] 大多数微处理器都带有看门狗,当看门狗没有被定时清零(喂狗)时,将引起复位,这 可防止程序跑飞,也可以防止程序运行时候出现死循环。设计者必须清楚看门狗的溢出时间 以决定在合适的时候清除看门狗。在内核中通常用于防止出现死循环,U-Boot直接关闭看 门狗。 3) 设置中断 /* External interrupt pending clear */ ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/ ldr r1, [r0] str r1, [r0] ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000 ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000 /* Disable all interrupts (VIC0 and VIC1) */ mvn r3, #0x0 str r3, [r0, #oINTMSK] str r3, [r1, #oINTMSK] /* Set all interrupts as IRQ */ mov r3, #0x0 str r3, [r0, #oINTMOD] str r3, [r1, #oINTMOD] /* Pending Interrupt Clear */ mov r3, #0x0 str r3, [r0, #oVECTADDR] str r3, [r1, #oVECTADDR] 4) 配置系统时钟 S3C6410有三个PLL(锁相环),分别为APLL、MPLL和EPLL。其中APLL产生ACLK, 给CPU使用,MPLL产生HCLKX2、HCLK和PCLK,HCLKX2主要提供时钟给DDR使用, 最大可以到266MHz。HCLK用作AXIAHB总线时钟,PCLK用作APB总线时钟。接AXI和 AHB总线的外设最大时钟为133MHz,接APB总线的外设最大时钟为66MHz。UART的时 钟可以由MPLL或者EPLL提供。 系统时钟初始化起始于: system_clock_init: ldr r0, =ELFIN_CLOCK_POWER_BASE /* 0x7e00f000 */ S3C6400的时钟系统与S3C6410有所差异,其中将 /* FOUT of EPLL is 96MHz */ ldr r1, =0x200203 修改成: ldr r1, =0x80200203 5) 串口初始化 uart_asm_init: /* set GPIO to enable UART */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x220022 str r1, [r0, #GPACON_OFFSET] mov pc, lr 6) NAND Flash控制器初始化 nand_asm_init: ldr r0, =ELFIN_NAND_BASE ldr r1, [r0, #NFCONF_OFFSET] orr r1, r1, #0x70 orr r1, r1, #0x7700 str r1, [r0, #NFCONF_OFFSET] ldr r1, [r0, #NFCONT_OFFSET] orr r1, r1, #0x07 str r1, [r0, #NFCONT_OFFSET] mov pc, lr 简单地对NAND Flash主机控制器的时间参数初始化。 7) 内存初始化 调用mem_ctrl_asm_init函数,跳入到arch/arm/cpu/arm1176/s3c64xx/ mem_ctrl_asm_init.s 中。系统上电,在利用内存控制器访问外部内存之前,需要进行一系列初始化工作,如图2. 3。主要做两件事情:配置内存控制器和初始化外部内存设备。配置内存控制器包括时间参数、位宽、片选和ID配置等。初始化外部内存设备,通过操P1DIRECTCMD寄存器,发出初始化系列:“nop”命令,Prechargeall命令,Autorefresh命令,Autorefresh命令,EMRS 命令,MRS命令。 S3C6410的DRAM控制器是基于 ARM PrimeCell CP003 AXI DMC(PL340),S3C6410的存储器端口0并不支持DRAM,所以只能选用存储器端口1(DMC1)。S3C6410的DMC1基址ELFIN_DMC1_BASE的值为0x7e00_1000。当DMC1使用32位数据线DRAM时,需要配置MEM_SYS_CFG寄存器,将芯片管脚Xm1DATA[31:16]设置为DMC1的数据域。单板利用两块64M×16的DDR SDRAM芯片K4X1G163PC组合成一块大小为64M×32的芯片,此时,MEM_SYS_CFG[7]必须清零。 DDR时间参数根据K4X1G163PC手册得到,并定义在s3c6410.h头文件中,利用宏NS_TO_CLK(t)将时间参数转化成时钟周期,再写入相应的寄存器中。一块K4X1G163PC行地址为A0 - A13,列地址为A0 - A9,BANK地址为B0-B1。寻址范围为128Mb。特别注意的是,片选寄存器DMC1_CHIP0_CFG的值:P1_chip_0_cfg[16] = 1,选择Bank-Row-Column组织结构。地址匹配值为0x50,地址屏蔽位0xF0,屏蔽了总线的高八位。因此寻址范围0x5xxxx_xxxx(0x5000_0000~0x5ff_ffff 即256 MiB)。 8) 唤醒复位初始化 /* Wakeup support. Don't know if it's going to be used, untested. */ ldr r0, =(ELFIN_CLOCK_POWER_BASE + RST_STAT_OFFSET) ldr r1, [r0] bic r1, r1, #0xfffffff7 cmp r1, #0x8 beq wakeup_reset 2.DDR的内存是如何分配? 这行代码告诉我们SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为 0x5000 0000~0x57FF FFFF。说明SDRAM一共256MB的空间。 接下来的代码程序就是对这256MB内存进行划分。 #ifdef CONFIG_PRAM /* * reserve protected RAM */ reg = getenv_ulong("pram", 10, CONFIG_PRAM); addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lxn", reg, addr); #endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug("TLB table at: %08lxn", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lxn", addr); 这里告诉我们是将SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB,所分配 的地址为:0x57FF 0000~0x57FF FFFF。 #ifdef CONFIG_LCD #ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; #else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_FB_ADDR */ #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lxn", gd->mon_len >> 10, addr); 这段代码是在SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为: 0x57F7 5000~0x57FE FFFF。 /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lxn", TOTAL_MALLOC_LEN >> 10, addr_sp); 从后往前紧挨着代码段开辟一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF。 /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lxn", sizeof (bd_t), addr_sp); 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lxn", sizeof (gd_t), addr_sp); 这是给gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7。 /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp; #ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lxn", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); #endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* leave 32 words for abort-stack */ gd->irq_sp = addr_sp; #endif debug("New Stack Pointer is: %08lxn", addr_sp); 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 综合上面SDRAM的分配,那么内存分配即为如图1. 16所示。SDRAM的空间大小为 256MB。 3.Main_loop()函数的作用是什么? main_loop()函数位于/commom目录下的main.c文件中。如下: void main_loop (void) main_loop()函数既无入口参数也无返回值。Main_loop()函数的主要实现作用是: 1) HUSH的相关初始化 #ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif …… #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif 2) bootdelay的初始化 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif 3) 启动次数 #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); 上面这行代码的作用是加载保存的启动次数。 bootcount++; 启动次数加1。 bootcount_store(bootcount); 更新启动次数。 sprintf (bcs_set, "%lu", bootcount); 将启动次数通过串口输出。 setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ 这段代码蕴含的东西较多。启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。 4) Modem功能 #ifdef CONFIG_MODEM_SUPPORT debug ("DEBUG: main_loop: do_mdm_init=%dn", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd")); setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ 如果系统中有Modem功能,打开其功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用 5) 设置U-Boot版本号 #ifdef CONFIG_VERSION_VARIABLE { setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ 打开动态版本支持功能后,u-boot在启动的时候会显示最新的版本号。 6) 启动tftp功能 #if defined(CONFIG_UPDATE_TFTP) update_tftp (0UL); #endif /* CONFIG_UPDATE_TFTP */ 7) 打印启动菜单 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%dnn", bootdelay); 在进入主循环之前,如果配置了启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。 #if defined(CONFIG_MENU_SHOW) bootdelay = menu_show(bootdelay); #endif 向终端打印出一个启动菜单。 # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ 初始化命令行超时机制。 #ifdef CONFIG_POST if (gd->flags & GD_FLG_POSTFAIL) { s = getenv("failbootcmd"); } else #endif /* CONFIG_POST */ #ifdef CONFIG_BOOTCOUNT_LIMIT if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.n", (unsigned)bootlimit); 检测是否超出启动次数限制。 s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); 获取启动命令参数。 main_loop()的主要作用即是U-Boot启动管理。 |
|
|
|
|
|
|
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化? 接下来是执行带返回跳转指令“bl lowlevel_init”。调用lowlevel_init 函数(位于 boardsamsungsmdk6410lowlevel_init.s)。lowlevel_init 函数的工作是进行与单板相关的 初始化工作,故名思议,这个初始化仅仅是最低限度(lowlevel)的,包括led 灯配置(便于 观察现象)、关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤 醒复位。 1) 配置led ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x55540000 str r1, [r0, #GPNCON_OFFSET] ldr r1, =0x55555555 str r1, [r0, #GPNPUD_OFFSET] ldr r1, =0xf000 str r1, [r0, #GPNDAT_OFFSET] 这里应该改成与s3c6410相适应的配置,单板使用GPM0-GPM3管脚驱动led。根据 s3c6410用户手册中的端口M控制寄存器章节可以对程序作出如下修改。 /* LED on only #8 */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x00111111 str r1, [r0, #GPMCON_OFFSET] ldr r1, =0x00000555 str r1, [r0, #GPMPUD_OFFSET] /* all of LEDs are power on */ ldr r1, =0x000f str r1, [r0, #GPMDAT_OFFSET] 根据需要,LED测试自行修改: /* LED test */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x0003 str r1, [r0, #GPMDAT_OFFSET] 2) 关闭看门狗 ldr r0, =0x7e000000 @0x7e004000 orr r0, r0, #0x4000 mov r1, #0 str r1, [r0] 大多数微处理器都带有看门狗,当看门狗没有被定时清零(喂狗)时,将引起复位,这 可防止程序跑飞,也可以防止程序运行时候出现死循环。设计者必须清楚看门狗的溢出时间 以决定在合适的时候清除看门狗。在内核中通常用于防止出现死循环,U-Boot直接关闭看 门狗。 3) 设置中断 /* External interrupt pending clear */ ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/ ldr r1, [r0] str r1, [r0] ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000 ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000 /* Disable all interrupts (VIC0 and VIC1) */ mvn r3, #0x0 str r3, [r0, #oINTMSK] str r3, [r1, #oINTMSK] /* Set all interrupts as IRQ */ mov r3, #0x0 str r3, [r0, #oINTMOD] str r3, [r1, #oINTMOD] /* Pending Interrupt Clear */ mov r3, #0x0 str r3, [r0, #oVECTADDR] str r3, [r1, #oVECTADDR] 4) 配置系统时钟 S3C6410有三个PLL(锁相环),分别为APLL、MPLL和EPLL。其中APLL产生ACLK, 给CPU使用,MPLL产生HCLKX2、HCLK和PCLK,HCLKX2主要提供时钟给DDR使用, 最大可以到266MHz。HCLK用作AXIAHB总线时钟,PCLK用作APB总线时钟。接AXI和 AHB总线的外设最大时钟为133MHz,接APB总线的外设最大时钟为66MHz。UART的时 钟可以由MPLL或者EPLL提供。 系统时钟初始化起始于: system_clock_init: ldr r0, =ELFIN_CLOCK_POWER_BASE /* 0x7e00f000 */ S3C6400的时钟系统与S3C6410有所差异,其中将 /* FOUT of EPLL is 96MHz */ ldr r1, =0x200203 修改成: ldr r1, =0x80200203 5) 串口初始化 uart_asm_init: /* set GPIO to enable UART */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x220022 str r1, [r0, #GPACON_OFFSET] mov pc, lr 6) NAND Flash控制器初始化 nand_asm_init: ldr r0, =ELFIN_NAND_BASE ldr r1, [r0, #NFCONF_OFFSET] orr r1, r1, #0x70 orr r1, r1, #0x7700 str r1, [r0, #NFCONF_OFFSET] ldr r1, [r0, #NFCONT_OFFSET] orr r1, r1, #0x07 str r1, [r0, #NFCONT_OFFSET] mov pc, lr 简单地对NAND Flash主机控制器的时间参数初始化。 7) 内存初始化 调用mem_ctrl_asm_init函数,跳入到arch/arm/cpu/arm1176/s3c64xx/ mem_ctrl_asm_init.s 中。系统上电,在利用内存控制器访问外部内存之前,需要进行一系列初始化工作,如图2. 3。主要做两件事情:配置内存控制器和初始化外部内存设备。配置内存控制器包括时间参数、位宽、片选和ID配置等。初始化外部内存设备,通过操P1DIRECTCMD寄存器,发出初始化系列:“nop”命令,Prechargeall命令,Autorefresh命令,Autorefresh命令,EMRS 命令,MRS命令。 S3C6410的DRAM控制器是基于 ARM PrimeCell CP003 AXI DMC(PL340),S3C6410的存储器端口0并不支持DRAM,所以只能选用存储器端口1(DMC1)。S3C6410的DMC1基址ELFIN_DMC1_BASE的值为0x7e00_1000。当DMC1使用32位数据线DRAM时,需要配置MEM_SYS_CFG寄存器,将芯片管脚Xm1DATA[31:16]设置为DMC1的数据域。单板利用两块64M×16的DDR SDRAM芯片K4X1G163PC组合成一块大小为64M×32的芯片,此时,MEM_SYS_CFG[7]必须清零。 DDR时间参数根据K4X1G163PC手册得到,并定义在s3c6410.h头文件中,利用宏NS_TO_CLK(t)将时间参数转化成时钟周期,再写入相应的寄存器中。一块K4X1G163PC行地址为A0 - A13,列地址为A0 - A9,BANK地址为B0-B1。寻址范围为128Mb。特别注意的是,片选寄存器DMC1_CHIP0_CFG的值:P1_chip_0_cfg[16] = 1,选择Bank-Row-Column组织结构。地址匹配值为0x50,地址屏蔽位0xF0,屏蔽了总线的高八位。因此寻址范围0x5xxxx_xxxx(0x5000_0000~0x5ff_ffff 即256 MiB)。 8) 唤醒复位初始化 /* Wakeup support. Don't know if it's going to be used, untested. */ ldr r0, =(ELFIN_CLOCK_POWER_BASE + RST_STAT_OFFSET) ldr r1, [r0] bic r1, r1, #0xfffffff7 cmp r1, #0x8 beq wakeup_reset 2.DDR的内存是如何分配? 这行代码告诉我们SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为 0x5000 0000~0x57FF FFFF。说明SDRAM一共256MB的空间。 接下来的代码程序就是对这256MB内存进行划分。 #ifdef CONFIG_PRAM /* * reserve protected RAM */ reg = getenv_ulong("pram", 10, CONFIG_PRAM); addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lxn", reg, addr); #endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug("TLB table at: %08lxn", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lxn", addr); 这里告诉我们是将SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB,所分配 的地址为:0x57FF 0000~0x57FF FFFF。 #ifdef CONFIG_LCD #ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; #else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_FB_ADDR */ #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lxn", gd->mon_len >> 10, addr); 这段代码是在SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为: 0x57F7 5000~0x57FE FFFF。 /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lxn", TOTAL_MALLOC_LEN >> 10, addr_sp); 从后往前紧挨着代码段开辟一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF。 /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lxn", sizeof (bd_t), addr_sp); 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lxn", sizeof (gd_t), addr_sp); 这是给gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7。 /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp; #ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lxn", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); #endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* leave 32 words for abort-stack */ gd->irq_sp = addr_sp; #endif debug("New Stack Pointer is: %08lxn", addr_sp); 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 综合上面SDRAM的分配,那么内存分配即为如图1. 16所示。SDRAM的空间大小为 256MB。 3.Main_loop()函数的作用是什么? main_loop()函数位于/commom目录下的main.c文件中。如下: void main_loop (void) main_loop()函数既无入口参数也无返回值。Main_loop()函数的主要实现作用是: 1) HUSH的相关初始化 #ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif …… #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif 2) bootdelay的初始化 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif 3) 启动次数 #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); 上面这行代码的作用是加载保存的启动次数。 bootcount++; 启动次数加1。 bootcount_store(bootcount); 更新启动次数。 sprintf (bcs_set, "%lu", bootcount); 将启动次数通过串口输出。 setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ 这段代码蕴含的东西较多。启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。 4) Modem功能 #ifdef CONFIG_MODEM_SUPPORT debug ("DEBUG: main_loop: do_mdm_init=%dn", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd")); setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ 如果系统中有Modem功能,打开其功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用 5) 设置U-Boot版本号 #ifdef CONFIG_VERSION_VARIABLE { setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ 打开动态版本支持功能后,u-boot在启动的时候会显示最新的版本号。 6) 启动tftp功能 #if defined(CONFIG_UPDATE_TFTP) update_tftp (0UL); #endif /* CONFIG_UPDATE_TFTP */ 7) 打印启动菜单 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%dnn", bootdelay); 在进入主循环之前,如果配置了启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。 #if defined(CONFIG_MENU_SHOW) bootdelay = menu_show(bootdelay); #endif 向终端打印出一个启动菜单。 # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ 初始化命令行超时机制。 #ifdef CONFIG_POST if (gd->flags & GD_FLG_POSTFAIL) { s = getenv("failbootcmd"); } else #endif /* CONFIG_POST */ #ifdef CONFIG_BOOTCOUNT_LIMIT if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.n", (unsigned)bootlimit); 检测是否超出启动次数限制。 s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); 获取启动命令参数。 main_loop()的主要作用即是U-Boot启动管理。 |
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
接下来是执行带返回跳转指令“bl lowlevel_init”。调用lowlevel_init 函数(位于 boardsamsungsmdk6410lowlevel_init.s)。lowlevel_init 函数的工作是进行与单板相关的 初始化工作,故名思议,这个初始化仅仅是最低限度(lowlevel)的,包括led 灯配置(便于 观察现象)、关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤 醒复位。 1) 配置led ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x55540000 str r1, [r0, #GPNCON_OFFSET] ldr r1, =0x55555555 str r1, [r0, #GPNPUD_OFFSET] ldr r1, =0xf000 str r1, [r0, #GPNDAT_OFFSET] 这里应该改成与s3c6410相适应的配置,单板使用GPM0-GPM3管脚驱动led。根据 s3c6410用户手册中的端口M控制寄存器章节可以对程序作出如下修改。 /* LED on only #8 */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x00111111 str r1, [r0, #GPMCON_OFFSET] ldr r1, =0x00000555 str r1, [r0, #GPMPUD_OFFSET] /* all of LEDs are power on */ ldr r1, =0x000f str r1, [r0, #GPMDAT_OFFSET] 根据需要,LED测试自行修改: /* LED test */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x0003 str r1, [r0, #GPMDAT_OFFSET] 2) 关闭看门狗 ldr r0, =0x7e000000 @0x7e004000 orr r0, r0, #0x4000 mov r1, #0 str r1, [r0] 大多数微处理器都带有看门狗,当看门狗没有被定时清零(喂狗)时,将引起复位,这 可防止程序跑飞,也可以防止程序运行时候出现死循环。设计者必须清楚看门狗的溢出时间 以决定在合适的时候清除看门狗。在内核中通常用于防止出现死循环,U-Boot直接关闭看 门狗。 3) 设置中断 /* External interrupt pending clear */ ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/ ldr r1, [r0] str r1, [r0] ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000 ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000 /* Disable all interrupts (VIC0 and VIC1) */ mvn r3, #0x0 str r3, [r0, #oINTMSK] str r3, [r1, #oINTMSK] /* Set all interrupts as IRQ */ mov r3, #0x0 str r3, [r0, #oINTMOD] str r3, [r1, #oINTMOD] /* Pending Interrupt Clear */ mov r3, #0x0 str r3, [r0, #oVECTADDR] str r3, [r1, #oVECTADDR] 4) 配置系统时钟 S3C6410有三个PLL(锁相环),分别为APLL、MPLL和EPLL。其中APLL产生ACLK, 给CPU使用,MPLL产生HCLKX2、HCLK和PCLK,HCLKX2主要提供时钟给DDR使用, 最大可以到266MHz。HCLK用作AXIAHB总线时钟,PCLK用作APB总线时钟。接AXI和 AHB总线的外设最大时钟为133MHz,接APB总线的外设最大时钟为66MHz。UART的时 钟可以由MPLL或者EPLL提供。 系统时钟初始化起始于: system_clock_init: ldr r0, =ELFIN_CLOCK_POWER_BASE /* 0x7e00f000 */ S3C6400的时钟系统与S3C6410有所差异,其中将 /* FOUT of EPLL is 96MHz */ ldr r1, =0x200203 修改成: ldr r1, =0x80200203 5) 串口初始化 uart_asm_init: /* set GPIO to enable UART */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x220022 str r1, [r0, #GPACON_OFFSET] mov pc, lr 6) NAND Flash控制器初始化 nand_asm_init: ldr r0, =ELFIN_NAND_BASE ldr r1, [r0, #NFCONF_OFFSET] orr r1, r1, #0x70 orr r1, r1, #0x7700 str r1, [r0, #NFCONF_OFFSET] ldr r1, [r0, #NFCONT_OFFSET] orr r1, r1, #0x07 str r1, [r0, #NFCONT_OFFSET] mov pc, lr 简单地对NAND Flash主机控制器的时间参数初始化。 7) 内存初始化 调用mem_ctrl_asm_init函数,跳入到arch/arm/cpu/arm1176/s3c64xx/ mem_ctrl_asm_init.s 中。系统上电,在利用内存控制器访问外部内存之前,需要进行一系列初始化工作,如图2. 3。主要做两件事情:配置内存控制器和初始化外部内存设备。配置内存控制器包括时间参数、位宽、片选和ID配置等。初始化外部内存设备,通过操P1DIRECTCMD寄存器,发出初始化系列:“nop”命令,Prechargeall命令,Autorefresh命令,Autorefresh命令,EMRS 命令,MRS命令。 S3C6410的DRAM控制器是基于 ARM PrimeCell CP003 AXI DMC(PL340),S3C6410的存储器端口0并不支持DRAM,所以只能选用存储器端口1(DMC1)。S3C6410的DMC1基址ELFIN_DMC1_BASE的值为0x7e00_1000。当DMC1使用32位数据线DRAM时,需要配置MEM_SYS_CFG寄存器,将芯片管脚Xm1DATA[31:16]设置为DMC1的数据域。单板利用两块64M×16的DDR SDRAM芯片K4X1G163PC组合成一块大小为64M×32的芯片,此时,MEM_SYS_CFG[7]必须清零。 DDR时间参数根据K4X1G163PC手册得到,并定义在s3c6410.h头文件中,利用宏NS_TO_CLK(t)将时间参数转化成时钟周期,再写入相应的寄存器中。一块K4X1G163PC行地址为A0 - A13,列地址为A0 - A9,BANK地址为B0-B1。寻址范围为128Mb。特别注意的是,片选寄存器DMC1_CHIP0_CFG的值:P1_chip_0_cfg[16] = 1,选择Bank-Row-Column组织结构。地址匹配值为0x50,地址屏蔽位0xF0,屏蔽了总线的高八位。因此寻址范围0x5xxxx_xxxx(0x5000_0000~0x5ff_ffff 即256 MiB)。 8) 唤醒复位初始化 /* Wakeup support. Don't know if it's going to be used, untested. */ ldr r0, =(ELFIN_CLOCK_POWER_BASE + RST_STAT_OFFSET) ldr r1, [r0] bic r1, r1, #0xfffffff7 cmp r1, #0x8 beq wakeup_reset 2.DDR的内存是如何分配? 这行代码告诉我们SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为 0x5000 0000~0x57FF FFFF。说明SDRAM一共256MB的空间。 接下来的代码程序就是对这256MB内存进行划分。 #ifdef CONFIG_PRAM /* * reserve protected RAM */ reg = getenv_ulong("pram", 10, CONFIG_PRAM); addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lxn", reg, addr); #endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug("TLB table at: %08lxn", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lxn", addr); 这里告诉我们是将SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB,所分配 的地址为:0x57FF 0000~0x57FF FFFF。 #ifdef CONFIG_LCD #ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; #else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_FB_ADDR */ #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lxn", gd->mon_len >> 10, addr); 这段代码是在SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为: 0x57F7 5000~0x57FE FFFF。 /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lxn", TOTAL_MALLOC_LEN >> 10, addr_sp); 从后往前紧挨着代码段开辟一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF。 /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lxn", sizeof (bd_t), addr_sp); 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lxn", sizeof (gd_t), addr_sp); 这是给gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7。 /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp; #ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lxn", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); #endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* leave 32 words for abort-stack */ gd->irq_sp = addr_sp; #endif debug("New Stack Pointer is: %08lxn", addr_sp); 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 综合上面SDRAM的分配,那么内存分配即为如图1. 16所示。SDRAM的空间大小为 256MB。 3.Main_loop()函数的作用是什么? main_loop()函数位于/commom目录下的main.c文件中。如下: void main_loop (void) main_loop()函数既无入口参数也无返回值。Main_loop()函数的主要实现作用是: 1) HUSH的相关初始化 #ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif …… #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif 2) bootdelay的初始化 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif 3) 启动次数 #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); 上面这行代码的作用是加载保存的启动次数。 bootcount++; 启动次数加1。 bootcount_store(bootcount); 更新启动次数。 sprintf (bcs_set, "%lu", bootcount); 将启动次数通过串口输出。 setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ 这段代码蕴含的东西较多。启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。 4) Modem功能 #ifdef CONFIG_MODEM_SUPPORT debug ("DEBUG: main_loop: do_mdm_init=%dn", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd")); setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ 如果系统中有Modem功能,打开其功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用 5) 设置U-Boot版本号 #ifdef CONFIG_VERSION_VARIABLE { setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ 打开动态版本支持功能后,u-boot在启动的时候会显示最新的版本号。 6) 启动tftp功能 #if defined(CONFIG_UPDATE_TFTP) update_tftp (0UL); #endif /* CONFIG_UPDATE_TFTP */ 7) 打印启动菜单 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%dnn", bootdelay); 在进入主循环之前,如果配置了启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。 #if defined(CONFIG_MENU_SHOW) bootdelay = menu_show(bootdelay); #endif 向终端打印出一个启动菜单。 # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ 初始化命令行超时机制。 #ifdef CONFIG_POST if (gd->flags & GD_FLG_POSTFAIL) { s = getenv("failbootcmd"); } else #endif /* CONFIG_POST */ #ifdef CONFIG_BOOTCOUNT_LIMIT if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.n", (unsigned)bootlimit); 检测是否超出启动次数限制。 s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); 获取启动命令参数。 main_loop()的主要作用即是U-Boot启动管理。 |
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
接下来是执行带返回跳转指令“bl lowlevel_init”。调用lowlevel_init 函数(位于 boardsamsungsmdk6410lowlevel_init.s)。lowlevel_init 函数的工作是进行与单板相关的 初始化工作,故名思议,这个初始化仅仅是最低限度(lowlevel)的,包括led 灯配置(便于 观察现象)、关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤 醒复位。 1) 配置led ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x55540000 str r1, [r0, #GPNCON_OFFSET] ldr r1, =0x55555555 str r1, [r0, #GPNPUD_OFFSET] ldr r1, =0xf000 str r1, [r0, #GPNDAT_OFFSET] 这里应该改成与s3c6410相适应的配置,单板使用GPM0-GPM3管脚驱动led。根据 s3c6410用户手册中的端口M控制寄存器章节可以对程序作出如下修改。 /* LED on only #8 */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x00111111 str r1, [r0, #GPMCON_OFFSET] ldr r1, =0x00000555 str r1, [r0, #GPMPUD_OFFSET] /* all of LEDs are power on */ ldr r1, =0x000f str r1, [r0, #GPMDAT_OFFSET] 根据需要,LED测试自行修改: /* LED test */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x0003 str r1, [r0, #GPMDAT_OFFSET] 2) 关闭看门狗 ldr r0, =0x7e000000 @0x7e004000 orr r0, r0, #0x4000 mov r1, #0 str r1, [r0] 大多数微处理器都带有看门狗,当看门狗没有被定时清零(喂狗)时,将引起复位,这 可防止程序跑飞,也可以防止程序运行时候出现死循环。设计者必须清楚看门狗的溢出时间 以决定在合适的时候清除看门狗。在内核中通常用于防止出现死循环,U-Boot直接关闭看 门狗。 3) 设置中断 /* External interrupt pending clear */ ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/ ldr r1, [r0] str r1, [r0] ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000 ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000 /* Disable all interrupts (VIC0 and VIC1) */ mvn r3, #0x0 str r3, [r0, #oINTMSK] str r3, [r1, #oINTMSK] /* Set all interrupts as IRQ */ mov r3, #0x0 str r3, [r0, #oINTMOD] str r3, [r1, #oINTMOD] /* Pending Interrupt Clear */ mov r3, #0x0 str r3, [r0, #oVECTADDR] str r3, [r1, #oVECTADDR] 4) 配置系统时钟 S3C6410有三个PLL(锁相环),分别为APLL、MPLL和EPLL。其中APLL产生ACLK, 给CPU使用,MPLL产生HCLKX2、HCLK和PCLK,HCLKX2主要提供时钟给DDR使用, 最大可以到266MHz。HCLK用作AXIAHB总线时钟,PCLK用作APB总线时钟。接AXI和 AHB总线的外设最大时钟为133MHz,接APB总线的外设最大时钟为66MHz。UART的时 钟可以由MPLL或者EPLL提供。 系统时钟初始化起始于: system_clock_init: ldr r0, =ELFIN_CLOCK_POWER_BASE /* 0x7e00f000 */ S3C6400的时钟系统与S3C6410有所差异,其中将 /* FOUT of EPLL is 96MHz */ ldr r1, =0x200203 修改成: ldr r1, =0x80200203 5) 串口初始化 uart_asm_init: /* set GPIO to enable UART */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x220022 str r1, [r0, #GPACON_OFFSET] mov pc, lr 6) NAND Flash控制器初始化 nand_asm_init: ldr r0, =ELFIN_NAND_BASE ldr r1, [r0, #NFCONF_OFFSET] orr r1, r1, #0x70 orr r1, r1, #0x7700 str r1, [r0, #NFCONF_OFFSET] ldr r1, [r0, #NFCONT_OFFSET] orr r1, r1, #0x07 str r1, [r0, #NFCONT_OFFSET] mov pc, lr 简单地对NAND Flash主机控制器的时间参数初始化。 7) 内存初始化 调用mem_ctrl_asm_init函数,跳入到arch/arm/cpu/arm1176/s3c64xx/ mem_ctrl_asm_init.s 中。系统上电,在利用内存控制器访问外部内存之前,需要进行一系列初始化工作,如图2. 3。主要做两件事情:配置内存控制器和初始化外部内存设备。配置内存控制器包括时间参数、位宽、片选和ID配置等。初始化外部内存设备,通过操P1DIRECTCMD寄存器,发出初始化系列:“nop”命令,Prechargeall命令,Autorefresh命令,Autorefresh命令,EMRS 命令,MRS命令。 S3C6410的DRAM控制器是基于 ARM PrimeCell CP003 AXI DMC(PL340),S3C6410的存储器端口0并不支持DRAM,所以只能选用存储器端口1(DMC1)。S3C6410的DMC1基址ELFIN_DMC1_BASE的值为0x7e00_1000。当DMC1使用32位数据线DRAM时,需要配置MEM_SYS_CFG寄存器,将芯片管脚Xm1DATA[31:16]设置为DMC1的数据域。单板利用两块64M×16的DDR SDRAM芯片K4X1G163PC组合成一块大小为64M×32的芯片,此时,MEM_SYS_CFG[7]必须清零。 DDR时间参数根据K4X1G163PC手册得到,并定义在s3c6410.h头文件中,利用宏NS_TO_CLK(t)将时间参数转化成时钟周期,再写入相应的寄存器中。一块K4X1G163PC行地址为A0 - A13,列地址为A0 - A9,BANK地址为B0-B1。寻址范围为128Mb。特别注意的是,片选寄存器DMC1_CHIP0_CFG的值:P1_chip_0_cfg[16] = 1,选择Bank-Row-Column组织结构。地址匹配值为0x50,地址屏蔽位0xF0,屏蔽了总线的高八位。因此寻址范围0x5xxxx_xxxx(0x5000_0000~0x5ff_ffff 即256 MiB)。 8) 唤醒复位初始化 /* Wakeup support. Don't know if it's going to be used, untested. */ ldr r0, =(ELFIN_CLOCK_POWER_BASE + RST_STAT_OFFSET) ldr r1, [r0] bic r1, r1, #0xfffffff7 cmp r1, #0x8 beq wakeup_reset 2.DDR的内存是如何分配? 这行代码告诉我们SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为 0x5000 0000~0x57FF FFFF。说明SDRAM一共256MB的空间。 接下来的代码程序就是对这256MB内存进行划分。 #ifdef CONFIG_PRAM /* * reserve protected RAM */ reg = getenv_ulong("pram", 10, CONFIG_PRAM); addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lxn", reg, addr); #endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug("TLB table at: %08lxn", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lxn", addr); 这里告诉我们是将SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB,所分配 的地址为:0x57FF 0000~0x57FF FFFF。 #ifdef CONFIG_LCD #ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; #else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_FB_ADDR */ #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lxn", gd->mon_len >> 10, addr); 这段代码是在SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为: 0x57F7 5000~0x57FE FFFF。 /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lxn", TOTAL_MALLOC_LEN >> 10, addr_sp); 从后往前紧挨着代码段开辟一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF。 /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lxn", sizeof (bd_t), addr_sp); 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lxn", sizeof (gd_t), addr_sp); 这是给gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7。 /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp; #ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lxn", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); #endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* leave 32 words for abort-stack */ gd->irq_sp = addr_sp; #endif debug("New Stack Pointer is: %08lxn", addr_sp); 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 综合上面SDRAM的分配,那么内存分配即为如图1. 16所示。SDRAM的空间大小为 256MB。 3.Main_loop()函数的作用是什么? main_loop()函数位于/commom目录下的main.c文件中。如下: void main_loop (void) main_loop()函数既无入口参数也无返回值。Main_loop()函数的主要实现作用是: 1) HUSH的相关初始化 #ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif …… #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif 2) bootdelay的初始化 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif 3) 启动次数 #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); 上面这行代码的作用是加载保存的启动次数。 bootcount++; 启动次数加1。 bootcount_store(bootcount); 更新启动次数。 sprintf (bcs_set, "%lu", bootcount); 将启动次数通过串口输出。 setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ 这段代码蕴含的东西较多。启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。 4) Modem功能 #ifdef CONFIG_MODEM_SUPPORT debug ("DEBUG: main_loop: do_mdm_init=%dn", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd")); setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ 如果系统中有Modem功能,打开其功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用 5) 设置U-Boot版本号 #ifdef CONFIG_VERSION_VARIABLE { setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ 打开动态版本支持功能后,u-boot在启动的时候会显示最新的版本号。 6) 启动tftp功能 #if defined(CONFIG_UPDATE_TFTP) update_tftp (0UL); #endif /* CONFIG_UPDATE_TFTP */ 7) 打印启动菜单 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%dnn", bootdelay); 在进入主循环之前,如果配置了启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。 #if defined(CONFIG_MENU_SHOW) bootdelay = menu_show(bootdelay); #endif 向终端打印出一个启动菜单。 # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ 初始化命令行超时机制。 #ifdef CONFIG_POST if (gd->flags & GD_FLG_POSTFAIL) { s = getenv("failbootcmd"); } else #endif /* CONFIG_POST */ #ifdef CONFIG_BOOTCOUNT_LIMIT if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.n", (unsigned)bootlimit); 检测是否超出启动次数限制。 s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); 获取启动命令参数。 main_loop()的主要作用即是U-Boot启动管理。 |
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
接下来是执行带返回跳转指令“bl lowlevel_init”。调用lowlevel_init 函数(位于 boardsamsungsmdk6410lowlevel_init.s)。lowlevel_init 函数的工作是进行与单板相关的 初始化工作,故名思议,这个初始化仅仅是最低限度(lowlevel)的,包括led 灯配置(便于 观察现象)、关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤 醒复位。 1) 配置led ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x55540000 str r1, [r0, #GPNCON_OFFSET] ldr r1, =0x55555555 str r1, [r0, #GPNPUD_OFFSET] ldr r1, =0xf000 str r1, [r0, #GPNDAT_OFFSET] 这里应该改成与s3c6410相适应的配置,单板使用GPM0-GPM3管脚驱动led。根据 s3c6410用户手册中的端口M控制寄存器章节可以对程序作出如下修改。 /* LED on only #8 */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x00111111 str r1, [r0, #GPMCON_OFFSET] ldr r1, =0x00000555 str r1, [r0, #GPMPUD_OFFSET] /* all of LEDs are power on */ ldr r1, =0x000f str r1, [r0, #GPMDAT_OFFSET] 根据需要,LED测试自行修改: /* LED test */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x0003 str r1, [r0, #GPMDAT_OFFSET] 2) 关闭看门狗 ldr r0, =0x7e000000 @0x7e004000 orr r0, r0, #0x4000 mov r1, #0 str r1, [r0] 大多数微处理器都带有看门狗,当看门狗没有被定时清零(喂狗)时,将引起复位,这 可防止程序跑飞,也可以防止程序运行时候出现死循环。设计者必须清楚看门狗的溢出时间 以决定在合适的时候清除看门狗。在内核中通常用于防止出现死循环,U-Boot直接关闭看 门狗。 3) 设置中断 /* External interrupt pending clear */ ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/ ldr r1, [r0] str r1, [r0] ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000 ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000 /* Disable all interrupts (VIC0 and VIC1) */ mvn r3, #0x0 str r3, [r0, #oINTMSK] str r3, [r1, #oINTMSK] /* Set all interrupts as IRQ */ mov r3, #0x0 str r3, [r0, #oINTMOD] str r3, [r1, #oINTMOD] /* Pending Interrupt Clear */ mov r3, #0x0 str r3, [r0, #oVECTADDR] str r3, [r1, #oVECTADDR] 4) 配置系统时钟 S3C6410有三个PLL(锁相环),分别为APLL、MPLL和EPLL。其中APLL产生ACLK, 给CPU使用,MPLL产生HCLKX2、HCLK和PCLK,HCLKX2主要提供时钟给DDR使用, 最大可以到266MHz。HCLK用作AXIAHB总线时钟,PCLK用作APB总线时钟。接AXI和 AHB总线的外设最大时钟为133MHz,接APB总线的外设最大时钟为66MHz。UART的时 钟可以由MPLL或者EPLL提供。 系统时钟初始化起始于: system_clock_init: ldr r0, =ELFIN_CLOCK_POWER_BASE /* 0x7e00f000 */ S3C6400的时钟系统与S3C6410有所差异,其中将 /* FOUT of EPLL is 96MHz */ ldr r1, =0x200203 修改成: ldr r1, =0x80200203 5) 串口初始化 uart_asm_init: /* set GPIO to enable UART */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x220022 str r1, [r0, #GPACON_OFFSET] mov pc, lr 6) NAND Flash控制器初始化 nand_asm_init: ldr r0, =ELFIN_NAND_BASE ldr r1, [r0, #NFCONF_OFFSET] orr r1, r1, #0x70 orr r1, r1, #0x7700 str r1, [r0, #NFCONF_OFFSET] ldr r1, [r0, #NFCONT_OFFSET] orr r1, r1, #0x07 str r1, [r0, #NFCONT_OFFSET] mov pc, lr 简单地对NAND Flash主机控制器的时间参数初始化。 7) 内存初始化 调用mem_ctrl_asm_init函数,跳入到arch/arm/cpu/arm1176/s3c64xx/ mem_ctrl_asm_init.s 中。系统上电,在利用内存控制器访问外部内存之前,需要进行一系列初始化工作,如图2. 3。主要做两件事情:配置内存控制器和初始化外部内存设备。配置内存控制器包括时间参数、位宽、片选和ID配置等。初始化外部内存设备,通过操P1DIRECTCMD寄存器,发出初始化系列:“nop”命令,Prechargeall命令,Autorefresh命令,Autorefresh命令,EMRS 命令,MRS命令。 S3C6410的DRAM控制器是基于 ARM PrimeCell CP003 AXI DMC(PL340),S3C6410的存储器端口0并不支持DRAM,所以只能选用存储器端口1(DMC1)。S3C6410的DMC1基址ELFIN_DMC1_BASE的值为0x7e00_1000。当DMC1使用32位数据线DRAM时,需要配置MEM_SYS_CFG寄存器,将芯片管脚Xm1DATA[31:16]设置为DMC1的数据域。单板利用两块64M×16的DDR SDRAM芯片K4X1G163PC组合成一块大小为64M×32的芯片,此时,MEM_SYS_CFG[7]必须清零。 DDR时间参数根据K4X1G163PC手册得到,并定义在s3c6410.h头文件中,利用宏NS_TO_CLK(t)将时间参数转化成时钟周期,再写入相应的寄存器中。一块K4X1G163PC行地址为A0 - A13,列地址为A0 - A9,BANK地址为B0-B1。寻址范围为128Mb。特别注意的是,片选寄存器DMC1_CHIP0_CFG的值:P1_chip_0_cfg[16] = 1,选择Bank-Row-Column组织结构。地址匹配值为0x50,地址屏蔽位0xF0,屏蔽了总线的高八位。因此寻址范围0x5xxxx_xxxx(0x5000_0000~0x5ff_ffff 即256 MiB)。 8) 唤醒复位初始化 /* Wakeup support. Don't know if it's going to be used, untested. */ ldr r0, =(ELFIN_CLOCK_POWER_BASE + RST_STAT_OFFSET) ldr r1, [r0] bic r1, r1, #0xfffffff7 cmp r1, #0x8 beq wakeup_reset 2.DDR的内存是如何分配? 这行代码告诉我们SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为 0x5000 0000~0x57FF FFFF。说明SDRAM一共256MB的空间。 接下来的代码程序就是对这256MB内存进行划分。 #ifdef CONFIG_PRAM /* * reserve protected RAM */ reg = getenv_ulong("pram", 10, CONFIG_PRAM); addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lxn", reg, addr); #endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug("TLB table at: %08lxn", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lxn", addr); 这里告诉我们是将SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB,所分配 的地址为:0x57FF 0000~0x57FF FFFF。 #ifdef CONFIG_LCD #ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; #else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_FB_ADDR */ #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lxn", gd->mon_len >> 10, addr); 这段代码是在SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为: 0x57F7 5000~0x57FE FFFF。 /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lxn", TOTAL_MALLOC_LEN >> 10, addr_sp); 从后往前紧挨着代码段开辟一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF。 /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lxn", sizeof (bd_t), addr_sp); 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lxn", sizeof (gd_t), addr_sp); 这是给gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7。 /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp; #ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lxn", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); #endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* leave 32 words for abort-stack */ gd->irq_sp = addr_sp; #endif debug("New Stack Pointer is: %08lxn", addr_sp); 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 综合上面SDRAM的分配,那么内存分配即为如图1. 16所示。SDRAM的空间大小为 256MB。 3.Main_loop()函数的作用是什么? main_loop()函数位于/commom目录下的main.c文件中。如下: void main_loop (void) main_loop()函数既无入口参数也无返回值。Main_loop()函数的主要实现作用是: 1) HUSH的相关初始化 #ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif …… #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif 2) bootdelay的初始化 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif 3) 启动次数 #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); 上面这行代码的作用是加载保存的启动次数。 bootcount++; 启动次数加1。 bootcount_store(bootcount); 更新启动次数。 sprintf (bcs_set, "%lu", bootcount); 将启动次数通过串口输出。 setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ 这段代码蕴含的东西较多。启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。 4) Modem功能 #ifdef CONFIG_MODEM_SUPPORT debug ("DEBUG: main_loop: do_mdm_init=%dn", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd")); setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ 如果系统中有Modem功能,打开其功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用 5) 设置U-Boot版本号 #ifdef CONFIG_VERSION_VARIABLE { setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ 打开动态版本支持功能后,u-boot在启动的时候会显示最新的版本号。 6) 启动tftp功能 #if defined(CONFIG_UPDATE_TFTP) update_tftp (0UL); #endif /* CONFIG_UPDATE_TFTP */ 7) 打印启动菜单 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%dnn", bootdelay); 在进入主循环之前,如果配置了启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。 #if defined(CONFIG_MENU_SHOW) bootdelay = menu_show(bootdelay); #endif 向终端打印出一个启动菜单。 # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ 初始化命令行超时机制。 #ifdef CONFIG_POST if (gd->flags & GD_FLG_POSTFAIL) { s = getenv("failbootcmd"); } else #endif /* CONFIG_POST */ #ifdef CONFIG_BOOTCOUNT_LIMIT if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.n", (unsigned)bootlimit); 检测是否超出启动次数限制。 s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); 获取启动命令参数。 main_loop()的主要作用即是U-Boot启动管理。 |
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
接下来是执行带返回跳转指令“bl lowlevel_init”。调用lowlevel_init 函数(位于 boardsamsungsmdk6410lowlevel_init.s)。lowlevel_init 函数的工作是进行与单板相关的 初始化工作,故名思议,这个初始化仅仅是最低限度(lowlevel)的,包括led 灯配置(便于 观察现象)、关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤 醒复位。 1) 配置led ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x55540000 str r1, [r0, #GPNCON_OFFSET] ldr r1, =0x55555555 str r1, [r0, #GPNPUD_OFFSET] ldr r1, =0xf000 str r1, [r0, #GPNDAT_OFFSET] 这里应该改成与s3c6410相适应的配置,单板使用GPM0-GPM3管脚驱动led。根据 s3c6410用户手册中的端口M控制寄存器章节可以对程序作出如下修改。 /* LED on only #8 */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x00111111 str r1, [r0, #GPMCON_OFFSET] ldr r1, =0x00000555 str r1, [r0, #GPMPUD_OFFSET] /* all of LEDs are power on */ ldr r1, =0x000f str r1, [r0, #GPMDAT_OFFSET] 根据需要,LED测试自行修改: /* LED test */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x0003 str r1, [r0, #GPMDAT_OFFSET] 2) 关闭看门狗 ldr r0, =0x7e000000 @0x7e004000 orr r0, r0, #0x4000 mov r1, #0 str r1, [r0] 大多数微处理器都带有看门狗,当看门狗没有被定时清零(喂狗)时,将引起复位,这 可防止程序跑飞,也可以防止程序运行时候出现死循环。设计者必须清楚看门狗的溢出时间 以决定在合适的时候清除看门狗。在内核中通常用于防止出现死循环,U-Boot直接关闭看 门狗。 3) 设置中断 /* External interrupt pending clear */ ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/ ldr r1, [r0] str r1, [r0] ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000 ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000 /* Disable all interrupts (VIC0 and VIC1) */ mvn r3, #0x0 str r3, [r0, #oINTMSK] str r3, [r1, #oINTMSK] /* Set all interrupts as IRQ */ mov r3, #0x0 str r3, [r0, #oINTMOD] str r3, [r1, #oINTMOD] /* Pending Interrupt Clear */ mov r3, #0x0 str r3, [r0, #oVECTADDR] str r3, [r1, #oVECTADDR] 4) 配置系统时钟 S3C6410有三个PLL(锁相环),分别为APLL、MPLL和EPLL。其中APLL产生ACLK, 给CPU使用,MPLL产生HCLKX2、HCLK和PCLK,HCLKX2主要提供时钟给DDR使用, 最大可以到266MHz。HCLK用作AXIAHB总线时钟,PCLK用作APB总线时钟。接AXI和 AHB总线的外设最大时钟为133MHz,接APB总线的外设最大时钟为66MHz。UART的时 钟可以由MPLL或者EPLL提供。 系统时钟初始化起始于: system_clock_init: ldr r0, =ELFIN_CLOCK_POWER_BASE /* 0x7e00f000 */ S3C6400的时钟系统与S3C6410有所差异,其中将 /* FOUT of EPLL is 96MHz */ ldr r1, =0x200203 修改成: ldr r1, =0x80200203 5) 串口初始化 uart_asm_init: /* set GPIO to enable UART */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x220022 str r1, [r0, #GPACON_OFFSET] mov pc, lr 6) NAND Flash控制器初始化 nand_asm_init: ldr r0, =ELFIN_NAND_BASE ldr r1, [r0, #NFCONF_OFFSET] orr r1, r1, #0x70 orr r1, r1, #0x7700 str r1, [r0, #NFCONF_OFFSET] ldr r1, [r0, #NFCONT_OFFSET] orr r1, r1, #0x07 str r1, [r0, #NFCONT_OFFSET] mov pc, lr 简单地对NAND Flash主机控制器的时间参数初始化。 7) 内存初始化 调用mem_ctrl_asm_init函数,跳入到arch/arm/cpu/arm1176/s3c64xx/ mem_ctrl_asm_init.s 中。系统上电,在利用内存控制器访问外部内存之前,需要进行一系列初始化工作,如图2. 3。主要做两件事情:配置内存控制器和初始化外部内存设备。配置内存控制器包括时间参数、位宽、片选和ID配置等。初始化外部内存设备,通过操P1DIRECTCMD寄存器,发出初始化系列:“nop”命令,Prechargeall命令,Autorefresh命令,Autorefresh命令,EMRS 命令,MRS命令。 S3C6410的DRAM控制器是基于 ARM PrimeCell CP003 AXI DMC(PL340),S3C6410的存储器端口0并不支持DRAM,所以只能选用存储器端口1(DMC1)。S3C6410的DMC1基址ELFIN_DMC1_BASE的值为0x7e00_1000。当DMC1使用32位数据线DRAM时,需要配置MEM_SYS_CFG寄存器,将芯片管脚Xm1DATA[31:16]设置为DMC1的数据域。单板利用两块64M×16的DDR SDRAM芯片K4X1G163PC组合成一块大小为64M×32的芯片,此时,MEM_SYS_CFG[7]必须清零。 DDR时间参数根据K4X1G163PC手册得到,并定义在s3c6410.h头文件中,利用宏NS_TO_CLK(t)将时间参数转化成时钟周期,再写入相应的寄存器中。一块K4X1G163PC行地址为A0 - A13,列地址为A0 - A9,BANK地址为B0-B1。寻址范围为128Mb。特别注意的是,片选寄存器DMC1_CHIP0_CFG的值:P1_chip_0_cfg[16] = 1,选择Bank-Row-Column组织结构。地址匹配值为0x50,地址屏蔽位0xF0,屏蔽了总线的高八位。因此寻址范围0x5xxxx_xxxx(0x5000_0000~0x5ff_ffff 即256 MiB)。 8) 唤醒复位初始化 /* Wakeup support. Don't know if it's going to be used, untested. */ ldr r0, =(ELFIN_CLOCK_POWER_BASE + RST_STAT_OFFSET) ldr r1, [r0] bic r1, r1, #0xfffffff7 cmp r1, #0x8 beq wakeup_reset 2.DDR的内存是如何分配? 这行代码告诉我们SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为 0x5000 0000~0x57FF FFFF。说明SDRAM一共256MB的空间。 接下来的代码程序就是对这256MB内存进行划分。 #ifdef CONFIG_PRAM /* * reserve protected RAM */ reg = getenv_ulong("pram", 10, CONFIG_PRAM); addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lxn", reg, addr); #endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug("TLB table at: %08lxn", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lxn", addr); 这里告诉我们是将SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB,所分配 的地址为:0x57FF 0000~0x57FF FFFF。 #ifdef CONFIG_LCD #ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; #else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_FB_ADDR */ #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lxn", gd->mon_len >> 10, addr); 这段代码是在SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为: 0x57F7 5000~0x57FE FFFF。 /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lxn", TOTAL_MALLOC_LEN >> 10, addr_sp); 从后往前紧挨着代码段开辟一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF。 /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lxn", sizeof (bd_t), addr_sp); 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lxn", sizeof (gd_t), addr_sp); 这是给gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7。 /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp; #ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lxn", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); #endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* leave 32 words for abort-stack */ gd->irq_sp = addr_sp; #endif debug("New Stack Pointer is: %08lxn", addr_sp); 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 综合上面SDRAM的分配,那么内存分配即为如图1. 16所示。SDRAM的空间大小为 256MB。 3.Main_loop()函数的作用是什么? main_loop()函数位于/commom目录下的main.c文件中。如下: void main_loop (void) main_loop()函数既无入口参数也无返回值。Main_loop()函数的主要实现作用是: 1) HUSH的相关初始化 #ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif …… #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif 2) bootdelay的初始化 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif 3) 启动次数 #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); 上面这行代码的作用是加载保存的启动次数。 bootcount++; 启动次数加1。 bootcount_store(bootcount); 更新启动次数。 sprintf (bcs_set, "%lu", bootcount); 将启动次数通过串口输出。 setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ 这段代码蕴含的东西较多。启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。 4) Modem功能 #ifdef CONFIG_MODEM_SUPPORT debug ("DEBUG: main_loop: do_mdm_init=%dn", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd")); setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ 如果系统中有Modem功能,打开其功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用 5) 设置U-Boot版本号 #ifdef CONFIG_VERSION_VARIABLE { setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ 打开动态版本支持功能后,u-boot在启动的时候会显示最新的版本号。 6) 启动tftp功能 #if defined(CONFIG_UPDATE_TFTP) update_tftp (0UL); #endif /* CONFIG_UPDATE_TFTP */ 7) 打印启动菜单 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%dnn", bootdelay); 在进入主循环之前,如果配置了启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。 #if defined(CONFIG_MENU_SHOW) bootdelay = menu_show(bootdelay); #endif 向终端打印出一个启动菜单。 # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ 初始化命令行超时机制。 #ifdef CONFIG_POST if (gd->flags & GD_FLG_POSTFAIL) { s = getenv("failbootcmd"); } else #endif /* CONFIG_POST */ #ifdef CONFIG_BOOTCOUNT_LIMIT if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.n", (unsigned)bootlimit); 检测是否超出启动次数限制。 s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); 获取启动命令参数。 main_loop()的主要作用即是U-Boot启动管理。 |
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
接下来是执行带返回跳转指令“bl lowlevel_init”。调用lowlevel_init 函数(位于 boardsamsungsmdk6410lowlevel_init.s)。lowlevel_init 函数的工作是进行与单板相关的 初始化工作,故名思议,这个初始化仅仅是最低限度(lowlevel)的,包括led 灯配置(便于 观察现象)、关闭看门狗、设置中断、配置系统时钟、初始化串口、初始化内存和初始化唤 醒复位。 1) 配置led ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x55540000 str r1, [r0, #GPNCON_OFFSET] ldr r1, =0x55555555 str r1, [r0, #GPNPUD_OFFSET] ldr r1, =0xf000 str r1, [r0, #GPNDAT_OFFSET] 这里应该改成与s3c6410相适应的配置,单板使用GPM0-GPM3管脚驱动led。根据 s3c6410用户手册中的端口M控制寄存器章节可以对程序作出如下修改。 /* LED on only #8 */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x00111111 str r1, [r0, #GPMCON_OFFSET] ldr r1, =0x00000555 str r1, [r0, #GPMPUD_OFFSET] /* all of LEDs are power on */ ldr r1, =0x000f str r1, [r0, #GPMDAT_OFFSET] 根据需要,LED测试自行修改: /* LED test */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x0003 str r1, [r0, #GPMDAT_OFFSET] 2) 关闭看门狗 ldr r0, =0x7e000000 @0x7e004000 orr r0, r0, #0x4000 mov r1, #0 str r1, [r0] 大多数微处理器都带有看门狗,当看门狗没有被定时清零(喂狗)时,将引起复位,这 可防止程序跑飞,也可以防止程序运行时候出现死循环。设计者必须清楚看门狗的溢出时间 以决定在合适的时候清除看门狗。在内核中通常用于防止出现死循环,U-Boot直接关闭看 门狗。 3) 设置中断 /* External interrupt pending clear */ ldr r0, =(ELFIN_GPIO_BASE+EINTPEND_OFFSET) /*EINTPEND*/ ldr r1, [r0] str r1, [r0] ldr r0, =ELFIN_VIC0_BASE_ADDR @0x71200000 ldr r1, =ELFIN_VIC1_BASE_ADDR @0x71300000 /* Disable all interrupts (VIC0 and VIC1) */ mvn r3, #0x0 str r3, [r0, #oINTMSK] str r3, [r1, #oINTMSK] /* Set all interrupts as IRQ */ mov r3, #0x0 str r3, [r0, #oINTMOD] str r3, [r1, #oINTMOD] /* Pending Interrupt Clear */ mov r3, #0x0 str r3, [r0, #oVECTADDR] str r3, [r1, #oVECTADDR] 4) 配置系统时钟 S3C6410有三个PLL(锁相环),分别为APLL、MPLL和EPLL。其中APLL产生ACLK, 给CPU使用,MPLL产生HCLKX2、HCLK和PCLK,HCLKX2主要提供时钟给DDR使用, 最大可以到266MHz。HCLK用作AXIAHB总线时钟,PCLK用作APB总线时钟。接AXI和 AHB总线的外设最大时钟为133MHz,接APB总线的外设最大时钟为66MHz。UART的时 钟可以由MPLL或者EPLL提供。 系统时钟初始化起始于: system_clock_init: ldr r0, =ELFIN_CLOCK_POWER_BASE /* 0x7e00f000 */ S3C6400的时钟系统与S3C6410有所差异,其中将 /* FOUT of EPLL is 96MHz */ ldr r1, =0x200203 修改成: ldr r1, =0x80200203 5) 串口初始化 uart_asm_init: /* set GPIO to enable UART */ ldr r0, =ELFIN_GPIO_BASE ldr r1, =0x220022 str r1, [r0, #GPACON_OFFSET] mov pc, lr 6) NAND Flash控制器初始化 nand_asm_init: ldr r0, =ELFIN_NAND_BASE ldr r1, [r0, #NFCONF_OFFSET] orr r1, r1, #0x70 orr r1, r1, #0x7700 str r1, [r0, #NFCONF_OFFSET] ldr r1, [r0, #NFCONT_OFFSET] orr r1, r1, #0x07 str r1, [r0, #NFCONT_OFFSET] mov pc, lr 简单地对NAND Flash主机控制器的时间参数初始化。 7) 内存初始化 调用mem_ctrl_asm_init函数,跳入到arch/arm/cpu/arm1176/s3c64xx/ mem_ctrl_asm_init.s 中。系统上电,在利用内存控制器访问外部内存之前,需要进行一系列初始化工作,如图2. 3。主要做两件事情:配置内存控制器和初始化外部内存设备。配置内存控制器包括时间参数、位宽、片选和ID配置等。初始化外部内存设备,通过操P1DIRECTCMD寄存器,发出初始化系列:“nop”命令,Prechargeall命令,Autorefresh命令,Autorefresh命令,EMRS 命令,MRS命令。 S3C6410的DRAM控制器是基于 ARM PrimeCell CP003 AXI DMC(PL340),S3C6410的存储器端口0并不支持DRAM,所以只能选用存储器端口1(DMC1)。S3C6410的DMC1基址ELFIN_DMC1_BASE的值为0x7e00_1000。当DMC1使用32位数据线DRAM时,需要配置MEM_SYS_CFG寄存器,将芯片管脚Xm1DATA[31:16]设置为DMC1的数据域。单板利用两块64M×16的DDR SDRAM芯片K4X1G163PC组合成一块大小为64M×32的芯片,此时,MEM_SYS_CFG[7]必须清零。 DDR时间参数根据K4X1G163PC手册得到,并定义在s3c6410.h头文件中,利用宏NS_TO_CLK(t)将时间参数转化成时钟周期,再写入相应的寄存器中。一块K4X1G163PC行地址为A0 - A13,列地址为A0 - A9,BANK地址为B0-B1。寻址范围为128Mb。特别注意的是,片选寄存器DMC1_CHIP0_CFG的值:P1_chip_0_cfg[16] = 1,选择Bank-Row-Column组织结构。地址匹配值为0x50,地址屏蔽位0xF0,屏蔽了总线的高八位。因此寻址范围0x5xxxx_xxxx(0x5000_0000~0x5ff_ffff 即256 MiB)。 8) 唤醒复位初始化 /* Wakeup support. Don't know if it's going to be used, untested. */ ldr r0, =(ELFIN_CLOCK_POWER_BASE + RST_STAT_OFFSET) ldr r1, [r0] bic r1, r1, #0xfffffff7 cmp r1, #0x8 beq wakeup_reset 2.DDR的内存是如何分配? 这行代码告诉我们SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为 0x5000 0000~0x57FF FFFF。说明SDRAM一共256MB的空间。 接下来的代码程序就是对这256MB内存进行划分。 #ifdef CONFIG_PRAM /* * reserve protected RAM */ reg = getenv_ulong("pram", 10, CONFIG_PRAM); addr -= (reg << 10); /* size is in kB */ debug("Reserving %ldk for protected RAM at %08lxn", reg, addr); #endif /* CONFIG_PRAM */ #if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF)) /* reserve TLB table */ addr -= (4096 * 4); /* round down to next 64 kB limit */ addr &= ~(0x10000 - 1); gd->tlb_addr = addr; debug("TLB table at: %08lxn", addr); #endif /* round down to next 4 kB limit */ addr &= ~(4096 - 1); debug("Top of RAM usable for U-Boot at: %08lxn", addr); 这里告诉我们是将SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB,所分配 的地址为:0x57FF 0000~0x57FF FFFF。 #ifdef CONFIG_LCD #ifdef CONFIG_FB_ADDR gd->fb_base = CONFIG_FB_ADDR; #else /* reserve memory for LCD display (always full pages) */ addr = lcd_setmem(addr); gd->fb_base = addr; #endif /* CONFIG_FB_ADDR */ #endif /* CONFIG_LCD */ /* * reserve memory for U-Boot code, data & bss * round down to next 4 kB limit */ addr -= gd->mon_len; addr &= ~(4096 - 1); debug("Reserving %ldk for U-Boot at: %08lxn", gd->mon_len >> 10, addr); 这段代码是在SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为: 0x57F7 5000~0x57FE FFFF。 /* * reserve memory for malloc() arena */ addr_sp = addr - TOTAL_MALLOC_LEN; debug("Reserving %dk for malloc() at: %08lxn", TOTAL_MALLOC_LEN >> 10, addr_sp); 从后往前紧挨着代码段开辟一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF。 /* * (permanently) allocate a Board Info struct * and a permanent copy of the "global" data */ addr_sp -= sizeof (bd_t); bd = (bd_t *) addr_sp; gd->bd = bd; debug("Reserving %zu Bytes for Board Info at: %08lxn", sizeof (bd_t), addr_sp); 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF。 addr_sp -= sizeof (gd_t); id = (gd_t *) addr_sp; debug("Reserving %zu Bytes for Global Data at: %08lxn", sizeof (gd_t), addr_sp); 这是给gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7。 /* setup stackpointer for exeptions */ gd->irq_sp = addr_sp; #ifdef CONFIG_USE_IRQ addr_sp -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ); debug("Reserving %zu Bytes for IRQ stack at: %08lxn", CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ, addr_sp); #endif /* leave 3 words for abort-stack */ addr_sp -= 12; /* 8-byte alignment for ABI compliance */ addr_sp &= ~0x07; #else addr_sp += 128; /* leave 32 words for abort-stack */ gd->irq_sp = addr_sp; #endif debug("New Stack Pointer is: %08lxn", addr_sp); 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F。 综合上面SDRAM的分配,那么内存分配即为如图1. 16所示。SDRAM的空间大小为 256MB。 3.Main_loop()函数的作用是什么? main_loop()函数位于/commom目录下的main.c文件中。如下: void main_loop (void) main_loop()函数既无入口参数也无返回值。Main_loop()函数的主要实现作用是: 1) HUSH的相关初始化 #ifndef CONFIG_SYS_HUSH_PARSER static char lastcommand[CONFIG_SYS_CBSIZE] = { 0, }; int len; int rc = 1; int flag; #endif …… #ifdef CONFIG_SYS_HUSH_PARSER u_boot_hush_start (); #endif #if defined(CONFIG_HUSH_INIT_VAR) hush_init_var (); #endif 2) bootdelay的初始化 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) char *s; int bootdelay; #endif 3) 启动次数 #ifdef CONFIG_BOOTCOUNT_LIMIT bootcount = bootcount_load(); 上面这行代码的作用是加载保存的启动次数。 bootcount++; 启动次数加1。 bootcount_store(bootcount); 更新启动次数。 sprintf (bcs_set, "%lu", bootcount); 将启动次数通过串口输出。 setenv ("bootcount", bcs_set); bcs = getenv ("bootlimit"); bootlimit = bcs ? simple_strtoul (bcs, NULL, 10) : 0; #endif /* CONFIG_BOOTCOUNT_LIMIT */ 这段代码蕴含的东西较多。启动次数限制功能,启动次数限制可以被用户设置一个启动次数,然后保存在Flash存储器的特定位置,当到达启动次数后,U-Boot无法启动。该功能适合一些商业产品,通过配置不同的License限制用户重新启动系统。 4) Modem功能 #ifdef CONFIG_MODEM_SUPPORT debug ("DEBUG: main_loop: do_mdm_init=%dn", do_mdm_init); if (do_mdm_init) { char *str = strdup(getenv("mdm_cmd")); setenv ("preboot", str); /* set or delete definition */ if (str != NULL) free (str); mdm_init(); /* wait for modem connection */ } #endif /* CONFIG_MODEM_SUPPORT */ 如果系统中有Modem功能,打开其功能可以接受其他用户通过电话网络的拨号请求。Modem功能通常供一些远程控制的系统使用 5) 设置U-Boot版本号 #ifdef CONFIG_VERSION_VARIABLE { setenv ("ver", version_string); /* set version variable */ } #endif /* CONFIG_VERSION_VARIABLE */ 打开动态版本支持功能后,u-boot在启动的时候会显示最新的版本号。 6) 启动tftp功能 #if defined(CONFIG_UPDATE_TFTP) update_tftp (0UL); #endif /* CONFIG_UPDATE_TFTP */ 7) 打印启动菜单 #if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0) s = getenv ("bootdelay"); bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY; debug ("### main_loop entered: bootdelay=%dnn", bootdelay); 在进入主循环之前,如果配置了启动延迟功能,需要等待用户从串口或者网络接口输入。如果用户按下任意键打断,启动流程,会向终端打印出一个启动菜单。 #if defined(CONFIG_MENU_SHOW) bootdelay = menu_show(bootdelay); #endif 向终端打印出一个启动菜单。 # ifdef CONFIG_BOOT_RETRY_TIME init_cmd_timeout (); # endif /* CONFIG_BOOT_RETRY_TIME */ 初始化命令行超时机制。 #ifdef CONFIG_POST if (gd->flags & GD_FLG_POSTFAIL) { s = getenv("failbootcmd"); } else #endif /* CONFIG_POST */ #ifdef CONFIG_BOOTCOUNT_LIMIT if (bootlimit && (bootcount > bootlimit)) { printf ("Warning: Bootlimit (%u) exceeded. Using altbootcmd.n", (unsigned)bootlimit); 检测是否超出启动次数限制。 s = getenv ("altbootcmd"); } else #endif /* CONFIG_BOOTCOUNT_LIMIT */ s = getenv ("bootcmd"); 获取启动命令参数。 main_loop()的主要作用即是U-Boot启动管理。 |
|
|
|
|
|
1.lowlevel_init函数完成了哪些初始化?
答:此函数在试读文档中BUG二首次出现,经过查阅得知,lowlevel_init函数主要是进行与开发板相关的初始化工作,如看门狗、中断设置、配置系统时钟,初始化串口、初始化内存和初始化唤醒复位等等。 2.DDR的内存是如何分配? 答: SDRAM的末位物理地址为0x5800 0000,即SDRAM的空间分布为0x5000 0000~0x57FF FFFF SDRAM的最后64K(addr &= ~(0x10000 - 1))分配给TLB,所分配的地址为:0x57FF 0000~0x57FF FFFF。因此SDRAM一共是256MB的空间。 SDRAM中从后往前给u-boot分配BSS、数据段、代码段,分配地址为:0x57F7 5000~0x57FE FFFF 从后往前紧挨着代码段开辟一块了malloc空间,给予的地址为:0x57E6 D000~0x57E7 4FFF 为bd结构体分配空间,地址为:0x57E6 CFD8~0x57E6 CFFF 给gd结构体分配空间,地址为:0x57E6 CF60~0x57E6 CFD7 分配异常中断空间,地址:0x57E6 CF50~0x57E6 CF5F 3.Main_loop()函数的作用是什么? 答:Main_loop()函数的主要作用就是实现U-Boot启动管理,同时也可以完成其目的操作比如:1)HUSH的相关初始化 2)bootdelay的初始化 3)Modem功能 4)启动次数 5)设置U-Boot版本号 6)启动tftp功能 7)打印启动菜单。 PS,纯属入门菜鸟,每天上来逛帖子,有幸碰到,仔细阅读《试读》后经过上网以及楼上一些参考。重在参与 |
|
|
|
|
|
《具身智能机器人系统》第1-6章阅读心得之具身智能机器人系统背景知识与基础模块
854 浏览 0 评论
961 浏览 11 评论
849 浏览 0 评论
【「嵌入式系统设计与实现」阅读体验】“基于车牌识别的自动地锁”案例学习
913 浏览 0 评论
【「HarmonyOS NEXT启程:零基础构建纯血鸿蒙应用」阅读体验】+ 8-10章有感
621 浏览 0 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 01:03 , Processed in 1.069178 second(s), Total 94, Slave 78 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号