历史上的今天
今天是:2025年02月22日(星期六)
2021年02月22日 | cortex-a8 uboot系列:第十三章 uboot命令体系
2021-02-22 来源:eefocus
一、uboot命令实现代码
Uboot命令体系的实现代码在common/cmd_xxx.c中。有若干个.c文件和命令体系有关。(还有command.c main.c)
每个命令对应一个函数
每一个uboot的命令背后都对应一个函数。这就是uboot实现命令体系的一种思路和方法。
命令参数以argc&argv传给函数
有些uboot的命令还支持传递参数。也就是命令对应的函数接收的参数列表中有argc和argv,然后命令体系会把执行执行命令时的命令+参数(如md 0x30000000 10)以argc(3)和argv(argv[0]=”md” argv[1]=”0x30000000” argv[2]=”10”)的方式传递给执行命令的函数
二、Help命令为例
使用U_BOOT_CMD宏定义。

对于help命令,长说明有6行,分别用6行字符串表示,中间不能有逗号,gcc编译会自动将6行字符串合并为一个。
help命令背后对应的函数名叫:do_help函数(common/command.c)

1.不带参数

首先对参数进行判断,如果只有1个参数,说明输入是help,要打印所有的命令信息。
定义一个命令数组指针,然后循环uboot所有命令,依次赋值给数组指针cmd_array。

对于这段代码,调试打印出下列信息。

&__u_boot_cmd_end - &__u_boot_cmd_start是一个指针运算,结果为(0xc3e58f88 – 0xc3e58959) / sizeof(cmd_tbl_t) = 1584 / 24 = 66。也就是共有66个命令。
__u_boot_cmd_end和__u_boot_cmd_start两个变量是定义在链接脚本中的。对于汇编代码,或者c代码,是可以引用链接脚本中的变量的。
所以新建了66个大小的数组指针cmd_array,因为每一个cmd_array大小是4个字节(只要是指针,大小都是4个自己),所以cmd_array的总大小就是66*4 = 264

cmd_tbl_s(include/command.h)是uboot命令的结构体,里面有多个参数。

使用extern 从外部引入两个变量__u_boot_cmd_start和__u_boot_cmd_end。这两个变量是在链接脚本中定义的。

其实__u_boot_cmd_start和__u_boot_cmd_end是两个标号,分别对应两个地址,一个uboot命令集的起始地址,另一个对应uboot命令集的结束地址。
C语言中将两个标号引入为两个cmd_tbl_s结构体的变量。在后面的程序中就可以使用这两个变量。

执行结果:

反汇编程序中,autoscr命令的执行函数确实是在地址0xc3e09244。


使用冒泡排序,对数组指针cmd_array,按照命令的名字进行排序。

最后打印所有命令的用法。
2.带参数
help命令可以带参数,表示打印执行命令的help信息。

对于参数超过1个,说明是要打印第二个参数的具体用法。
使用find_cmd函数(common/command.c中)查找指定命令,找到后,返回命令的指针。利用该指针,就可以知道命令的名字,命令的帮助,命令的用法,将之打印。
如果没有找到,就打印未知命令。

三、Uboot命令解析和执行过程分析
从main_loop函数(start_armboot函数的最后)开始。main_loop函数在common/main.c中。


使用readline函数从串口接收一行数据,接收数据在console_buffer。然后将该数据复制到lastcommand,使用run_command函数去执行命令。


clear_ctrlc函数清除上一次输入的ctrl+c,uboot中支持输入ctrl+c打断操作。所以在命令之前之前,要将这个清除。
后面判断命令是否为空以及命令是否太长。
将命令拷贝到cmdbuf中。

parse_line函数把”md 0x30000000 10”解析成argv[0]=”md”, argv[1]=”0x30000000” argv[2] = ”10”
find_cmd函数去uboot命令函数集合中搜索有没有argv[0]命令。在这里是找寻md命令。
检查参数对应命令是否正确。

使用函数指针执行命令函数。cmdtp->cmd。
四、Uboot的命令体系机制
1.Uboot对命令集的管理
可能的管理方式
数组: 结构体数组,数组中每一个结构体成员就是一个命令的所有信息。
链表: 链表的每个节点data段就是一个命令结构体,所有的命令都放在一条链表上。这样解决了数组方式的不灵活。坏处是需要额外的内存开销,然后各种算法(遍历、插入、删除等)需要一定复杂度的代码执行。
Uboot没有使用数组或者链表,而是使用了一种新的方式来实现这个功能。
命令结构体:cmd_tbl_s

name:命令的名字,字符串格式
maxargs:命令的最大接收参数个数
repeatable:命令是否可重复执行。重复执行是uboot命令行的一种工作机制,就是直接按回车则执行上一条执行的命令
cmd:命令执行函数指针,指向命令对应的执行函数,将来执行这个命令的函数时使用这个函数指针来调用
usage: 命令的短帮助信息。对命令的简单描述
help:命令的长帮助信息。命令的细节帮助信息,可以配置没有。
complete:自动补全函数指针。指向命令的自动补全函数,可以配置没有。
总结
Uboot的命令体系在工作时,一个命令对应一个cmd_tbl_s结构体的一个实例,然后uboot支持多少个命令,就需要多少个结构体实例。Uboot的命令体系把这些结构体实例管理起来,当用户输入了一个命令时,uboot会去这些结构体实例中查找(查找方法和存储管理的方法有关)。如果找到则执行命令,如果未找到则提示命令未知。
2.Uboot实现命令管理的思路
1)填充1个结构体实例构成一个命令
2)给命令结构体实例附加特定段属性(用户自定义段),链接时将带有该段属性的内容链接在一起排列(挨着的,不会夹杂其他东西,也不会丢掉一个带有这种段属性的)。
3)Uboot重定位时将该段整体加载到DDR中。加载到DDR中的uboot镜像中带有特定段属性的这一段其实就是命令结构体的集合,有点像一个命令结构体数组。
4)段起始地址和段结束地址(链接地址、定义在u-boot.lds中)决定了这些命令的开始地址和结束地址
3.U_BOOT_CMD宏
uboot使用U_BOOT_CMD宏来进行uboot命令的定义。这个宏在common/command.h中。

##在宏定义中是连字符。将__u_boot_cmd_和name连在一起作为一个整体。
#a是将a转化为字符串
如下面这个uboot命令version:

U_BOOT_CMD展开后,就成为了
cmd_tbl_t __u_boot_cmd_version __attribute__ ((unused,section (".u_boot_cmd"))) =
{“version”, 1, 1 , do_version, "version - print monitor versionn",NULL};
将结构体变量__u_boot_cmd_version,赋了初始值,然后放入到最终bin文件的.u_boot_cmd段中。
这样就定义了一个命令version,和函数do_version关联。最大参数为1,可重复执行,用法是version - print monitor version,没有帮助信息。
.u_boot_cmd段定义在链接脚本中。

总结:
这个U_BOOT_CMD宏的理解,关键在于结构体变量的名字和段属性。变量名使用##连字符来生成。指定变量存放的段,这样链接器链接时将该变量放入到.u_boot_cmd段中。最后对该变量结构体进行赋值。
4.find_cmd函数
uboot使用find_cmd函数(common/command.c)来查找uboot命令。

函数的任务就是从当前uboot的命令集中查找是否有某个命令,如果找到则返回命令的结构体指针,没有找到返回NULL。

定义cmd_tbl_t类型的指针cmdtp_temp,指向__u_boot_cmd_start的地址处。__u_boot_cmd_start的地址为.u_boot_cmd的起始地址。
![]()
从命令中找寻 . 的位置,因为有的命令是有带 . 的。 如md.b命令
最终len得到.之前的字符长度
如md.b 0x30000000 len = 2
如md 0x30000000 len = 2 + 4 + 10 = 16

循环从.u_boot_cmd段中的第一个cmd_tbl_t变量开始寻找,一直找到.u_boot_cmd段中的最后一个cmd_tbl_t。
通过strncmp (cmd, cmdtp->name, len),判断输入的命令是否和uboot的命令是否一样。返回0表示一样,即找到命令。
![]()
如果之前计算得到的长度和uboot的命令长度一致,说明命令匹配,返回uboot命令的cmd_tbl_t类型指针。
![]()
如果长度不一致,将命令指针保存到cmdtp_temp命令临时指针。
以上的情况是解决md.b 这种类型的命令。

当是简短命令,即md.b这种类型,就把cmdtp_temp命令临时指针返回,否则返回NULL。
总结:
查找命令的思路其实就是for循环遍历数组的思路,不同的是数组的起始位置和结束地址是用地址值来给定的,数组中的元素类型是结构体cmd_tbl_t类型。
5.U_BOOT_CMD宏详解
这个宏其实就是定义了一个命令对应的结构体变量,这个变量名和宏的第一个参数有关,因此只要调用时传参的第一个参数不同则定义的结构体变量不会重名。并且链接器会将该变量放入到.u_boot_cmd段中。
一个简单的C测试。

使用gcc –E 预编译。查看,宏被替换为以下。

五、Uboot中增加自定义命令
在已有的c文件中直接添加命令
1)在common/command.c中添加一个命令
2)直接使用U_BOOT_CMD_宏皆可添加命令,给命令提供一个do_xxx对应的函数就可对这个命令进行使用
3)添加完成后要重新编译编译工程,生成的u-boot.bin即可。
下面以实现一个自定义命令为例:
自建一个c文件并添加命令
1) 在common目录下新建一个命令文件,名字如cmd_weiqi.c(相应的命名就叫weiqi,对应的函数就叫do_weiqi函数),然后在c文件中添加命令所对应的U_BOOT_CMD宏和函数。

2)在common里的makefile中添加上对应的cmd_weiqi.o,目的是让make在编译时能够把cmd_weiqi.c编译进去

对于COBJS-$(CONFIG_CMD_AMBAPP) += cmd_ambapp.o
如果宏CONFIG_CMD_AMBAPP定义,COBJS-y就加cmd_ambapp.o
宏CONFIG_CMD_AMBAPP如果定义,定义为y。没有定义,就是n。
3)重新编译烧录执行
史海拾趣
|
问: ASIC的中文翻译是专用集成电路,为什么说它是一种嵌入式微处理器呢?FPGA也是一种嵌入式微处理器吗?我听有人说FPGA指的是一种封装结构,这是正确的吗? 答: 用于专用应用场合的处理器就是嵌入式处理器,不管它是可编程的还是不可编程的。 ...… 查看全部问答> |
|
瑞士LEM互感器、LEM电流互感器、LEM电涌保护器、LEM传感器、LEM电压互感器、LEM电量传感器、LEM无线电能表 LEM 是全球电量传感器的知名制造者,也是电量传感器领域的领导者。 LEM集团全球拥有多家公司,销售办事处遍布全球。其中瑞士日内瓦、中 ...… 查看全部问答> |
|
1 概述 WK283R3S-33M电源模块为非密封灌封系列,其主要技术参数:VOUT=3.3 V,VIN=18 V~36 V,POUT=33 W,SI/Sv=0.5%,η=85%,VP-P=100 mV。该电源模块的外形为1/4砖,其主要特点为低压大电流输出模块、高效率和低纹波电压值、体积为1/4砖可 ...… 查看全部问答> |
|
RealView MDK-ARM Version:3.24 CPU是LPC2101 H-JTAG 0.8 软件仿真正常。 现象是仿真器连接都正常。 硬件仿真 在Startup.s中还正常,转向main()函数就死机。 查了好多天了。今天忽然注意到软件仿真的程序地址0x000041A4(比16K还多) 而LPC2 ...… 查看全部问答> |
|
目录: 第一部分 贴片元器件基础知识 1.1片状电阻器 1.1.1片状电阻器的阻值和允差标注方法 1.1.2常见片状电阻器介绍 1.1.3片状电阻器的使用 1.2片状电容器 1.2.1片状电容器容量和允差标注方法 1.2.2常见片状电容器介绍 1.2 ...… 查看全部问答> |
|
本帖最后由 paulhyde 于 2014-9-15 09:21 编辑 在78**、79**系列三端稳压器中最常应用的是TO-220和TO-202两种封装。这两种封装的图形以及引脚序号、引脚功能如附图所示。 图中的引脚号标注方法是按照引脚电位从高到底的顺序标注的。这样 ...… 查看全部问答> |
|
S3C2440平台WinCE5.0中串口发送堵塞现象的疑惑: 本人开发一个软件,使用TQ公司的SKY2440开发板为模板,使用的是从4.2移植到wince5.0使用的BSP包。 该软件需要用到2个串口(COM0和COM1),TCP/UDP编程;修改MDD层的mdd ...… 查看全部问答> |
|
大家好,我请教一个关于2440的问题。我用2440连接一个ITU656接口时序的摄相头,为什么一直接不到同步信号?模块出来的同步开始是(FF,00,00,80),结束是(FF,00,00,90)。2440的ForthWord中的F,V,H是不是同步信号线上的电平?… 查看全部问答> |




