×
单片机 > 单片机程序设计 > 详情

cortex-a8 uboot系列:第七章 uboot源码分析3-启动第二阶段

发布时间:2022-08-12 发布时间:
|

一、start_armboot解析2

init_fnc_ptr是init_fnc_t函数类型的二重指针。


对于init_sequence,是一个函数指针数组。里面保存了多个函数指针,每个函数指针指向一个函数。不过函数的类型是固定的,为init_fnc_t函数类型(特征是输入参数为void,返回值为int)。

init_sequence在定义时就同时给了初始化,初始化的函数指针都是一些函数名。


init_fnc_ptr是一个二重函数指针,可以指向init_sequence这个函数指针数组。

这个地方的技巧就是将所有初始化函数都遍历依次执行。而不用单独的去执行初始化函数然后判断是否初始化成功。

遍历函数指针数组方法:

第一种:用下标去遍历,用数组元素个数来截止。

第二种:数组的有效元素末尾放一个标志(如NULL),依次遍历到标志处即可截止。(和字符串遍历思路一样)。

Uboot使用的第二种方法。因为数组中存的全是函数指针,因此选用NULL作为结束标志。遍历时从头依次进行,直到看到NULL标志截止。这种方法的优势是不用知道数组的大小个数是多少。

使用(*init_fnc_ptr)()执行对应的初始化函数,这里不能写成*init_fnc_ptr(),因为()优先级比*高,就会变成*(init_fnc_ptr()),执行就会未知。


函数执行成功,会返回0。否则会返回1。如果返回1。会去执行hang函数。该函数就是打印错误信息,然后进入一个死循环。

Init_sequence中的这些函数,都是board级别的各种硬件初始化。

初始化函数解析:

1.cpu_init

CPU内部的初始化。因为CPU相关的初始化在之前已经初始化了,所以这里为空。

2.board_init

该函数在boardsamsungx210X210.c文件中。X210开发板相关的初始化。

DECLARE_GLOBAL_DATA_PTR申明全局变量gd指针。而gd的声明定义为一个宏的原因就是程序会经常用到gd,因此就要到处进行申明,定义成宏比较方面。


判断是否要用到DM9000网卡,要用的话,先对DM9000进行初始化。


CONFIG_DRIVER_DM9000宏在x210_sd.h中定义。用来配置开发板的网卡。


dm9000_pre_init函数在boardsamsungx210X210.c程序中。

SROM_BW寄存器,DM9000使用的是通道1的SROM,对于这个通道要进行设置,如位宽,地址模式等等。

SROM_BC1寄存器是设置SROM通道1的时序。这个要根据DM9000的手册来设置。

MP01CON,设置MP0_1的GPIO,设置片选。

对于网卡,实际上是使用了的soc内部的SROM控制器来进行驱动的,因此对于网卡驱动,首先要对内部的SROM控制器进行初始化,使程序能通过SROM控制器来驱动外部的网卡,然后在对外部的网卡进行初始化。


这一步是对内部的SROM控制器进行初始化。


以后要移植uboot时,如果要移植网卡,主要的工作就是这里。这个函数主要是网卡的GPIO和SROM接口的配置,而不是驱动。因为网卡的驱动都是现成可用的,移植的时候驱动是不需要改动的,关键是这里的基本初始化。因为这些基本初始化和硬件相关的。

给bd的机器码bi_arch_number赋值,bi_arch_number是board_info中的一个元素,含义是:开发板的机器码。所谓机器码就是uboot给这个开发板定义的一个唯一编号。


机器码的主要作用就是在uboot和linux内核之间进行比对和适配。


嵌入式设备中每一个设备的硬件都是定制性的,不能通用。嵌入式设备的高度定制化导致硬件和软件不能随便适配使用。这就告诉我们:这个开发板移植的内核镜像不能下载到另一个开发板去,否则会工作不正常。因此linux做了个设置:给每个开发板做个唯一编号(机器码),然后再uboot、内核中都有一个软件维护的机器码编号。然后开发板、uboot、linux三者之间去比对这个机器码,如果机器码对上了就启动,否则就不启动(因为软件认为和硬件不适配)。


Uboot的机器码就是保存在变量bi_arch_number中。MACH_TYPE在x210_sd.c中定义,值没有特殊含义,只是当前开发板对应的编号。这个编号就代表了x210这个开发板的机器码。将来这个开发板上面移植的linux内核中的机器码也必须是MACH_TYPE。

Uboot中配置的这个机器码,会作为uboot给linux内核传参的一部分传给linux内核,内核启动过程中会比对这个接收到的机器码,和自己本身的机器码相比对,如果相等就启动,如果不等就不启动。


Uboot只是定义了机器码,而没有使用这个机器码。机器码的使用是在linux中使用的。


理论来说,一个开发板的机器码不能自己随便定(因为可能会有不同的开发板使用的机器码是一样的)。理论上来说有权利去发放机器码的只有uboot官方,所以做好一个开发板并且移植了uboot之后,理论上应该提交给uboot官方审核并发放机器码。

存放启动参数的地址bi_boot_params赋值。bi_boot_params也是bd_info中另一个主要元素。表示uboot给linux kernel启动时的传参的地址。


Uboot给linux内核传参过程:uboot实现将准备好的传参(字符串,就是bootargs)放在内存的一个地址处(就是bi_boot_params),然后uboot就启动了内核(uboot在启动内核时真正是通过寄存器r0,r1,r2来直接传递参数),其中有一个寄存器就是bi_boot_params参数值。


内核启动后,从寄存器中读取bi_boot_params,就知道了uboot给内核传递的参数保存在内存的什么地方,然后自己去内存的那个地方去找bootargs。

计算可得到启动参数存放的地址是0x3000_0100。也就是uboot会将启动参数保存在0x3000_0100开始的地址处。启动内核的时候,内核会从这个地址去读取需要的启动参数。


在uboot其他地方使用内存时,要注意不要对这块内存操作,以免破坏启动参数。

3.interrupt_init

在cpus5pc11xinterrupts.c程序中。


看函数名似乎和中断初始化有关,实际上不是。实际上这个函数是用来初始化定时器的。实际初始化的是pwm timer4。

210共有5个PWM timer,其中timer0-timer3可以由输出管脚,但是timer4没有输出管脚。

这个timer4用来做计时功能。会用到两个寄存器,TCNTB4、TCNTO4。TCNTB4决定定时时长,TCNTO4表示当前计数值。通过读取TCNTO4寄存器的值是否减到0,就可知道计时时间是否已到。


使用timer4来定时,因为没有中断支持,所以CPU要使用轮询方式来不断查看TCNTO寄存器才能知道定时时间是否到达。


Uboot中定时就是使用timer4来实现定时的,所以uboot中定时时,是不能做其他事(典型下,就是bootdelay,bootdelay中定时时并且检查用户输入时用轮询方式实现的)。


定义pwm timers指针timers,使用S5PC11X_GetBase_TIMERS()获取pwm timer的基地址,这个函数最终返回ELFIN_TIMER_BASE的值,也就是PWM timer的基地址。

而S5PC11X_TIMERS是一个结构体,包括了PWM的所有寄存器。

对于__attribute__((packed))

设置PWM的time4的分频值是16。所以此时timer4的时钟是PCLK/16。

这部分是计算定时10ms,timer4应该定时的值。使用get_PCLK()获取PCLK的时钟频率。

读取CLK_DIV0寄存器的值,得到PCLK对HCLK的分频值。然后再获取HCLK,就可以知道PCLK的频率。HCLK的获取也是同样的道理。

获取到PCLK的频率后,除以16得到timer4的计时时钟。根据该时钟及定时时长,就可以计算得到应该计数的值。

装载TCNTB4寄存器的值为刚刚计算得到的值。然后对TCON寄存器进行设置。更新TCNTB4值,然后开启timer4,并启动auto-reload模式。

这个interrupt_init函数,其实就是给pwm timer4设置为定时10ms。并开启该定时器。


4.env_init

和环境变量有关的初始化函数。commonenv_movi.c程序中。

这个函数在很多文件有定义。原因是uboot支持各种不同的启动介质(如norflash,nandflash,inand,sd卡……),从什么地方启动,就将环境变量env放到哪里。而各种介质存取操作env的方法是不一样的。因此uboot支持了各种不同介质中env的操作方法,所以有好多个env_xx.c文件。xx就是启动介质。


实际使用的是哪一个要根据自己开发板使用的存储介质来决定(这些env_xx.c同时只有1个会起作用,其他是不作用的,通过x210_sd.h中配置的宏来决定谁被包含使用),对于九鼎开发板,使用的是env_movi.c程序中的env_init。


这个函数只是对内存里维护的那一份uboot的env做了基本的初始化及判定,判断env中有没有能用的环境变量。因为此时还没有进行环境变量从SD卡到DDR中的relocate,因此当前环境变量时不能使用的。


在start_armboot函数中调用env_relocate后才进行环境变量从SD卡到DDR中的重定位。重定位之后需要环境变量时才可从DDR中去读取,重定位之前只能从SD卡中去读取环境变量。


5.init_baudrate

初始化串口通信的波特率,在lib_armBoard.c程序中。其实就是修改gd和gd下的bd的波特率变量。

getenv_r函数获取环境变量的值。


该函数接收3个参数,第一个参数是环境变量的名字,第二个是保存环境变量的值(字符串表示),第三个是保存值字符串的长度。


返回读取环境变量值的字符串的长度。

使用getenv_r读取baudrated值,读出来的值是以字符串形式保存在tmp字符数组中,然后使用simple_strtoul函数将字符串波特率转化为数字波特率值。然后将值赋值给gd的baudrate变量和gd下的bd下的baudrate变量。


如果读取不成功,就将x210_sd.h中的CONFIG_BAUDRATE的值作为波特率。因此环境变量

[1] [2]
cortex-a8uboot系列启动第二阶段

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
采用AT89C2051的数字可调稳压电源单片机源程序