u-boot代码层次uboot需要支持众多的硬件,并且具有良好的可扩展性、可移植性和可维护性,因此必须要有一个设计良好的代码架构。代码架构的设计总是与软硬件架构密不可分的,在硬件层面嵌入式系统的核心一般包括以下层次:
(1)目标板:它包含了系统运行所需的所有组件,如soc芯片、ddr、flash/emmc存储器、各种外设以及时钟源、电源管理芯片等
(2)soc:它包含了cpu、总线控制器、集成在片内的rom、sram、dma控制器、硬件加速器、异构核,以及片内时钟、电源控制模块等
(3)处理器架构:它一般指处理器体系结构的大版本,不同的体系结构之间可能存在不同的指令集、异常模型以及内存模型等。例如对于arm系列架构,armv8和armv7就属于不同的处理器架构
(4)cpu型号:它是指处理器的具体型号,如cortex-a53或cortex-a72等
一般cpu型号和处理器架构数量相对较少,如对于arm架构来说一般就是arm官方发布的这些型号。而soc型号就要多一些,它主要是各芯片公司基于特定cpu架构以及其它ip模块,设计的专用或通用芯片,如高通或海思设计的手机芯片。最后就是以soc芯片为核心设计的目标板了,在目标板上基本上集成了一款产品所需的所有组件,如一款手机的电路板。它们之间的简单关系如下图:
uboot的代码设计也遵循以上层次,arch目录包含了处理器架构相关代码,arch/cpu目录包含了特定cpu代码,而board目录则包含了特定目标板的代码。因此当我们新增加一款目标板时,主要的工作就可以集中在board相关的代码,只要不是太新的cpu型号,arch和cpu相关代码在uboot官方版本中都已经被支持。因此可以直接复用这部分实现,我们唯一要做的就是选择正确的配置选项。
如何添加board添加board的基本步骤当我们开始一个全新的项目时,总是希望能先让系统能运行起来,然后再在此基础上为其添加更多的feature,这个只包含能让系统运行所需模块的系统,叫做最小系统。cpu能正常运行包含以下几个条件:
(1)具有合适的电源和时钟
(2)程序代码被加载到合适的位置,cpu能够正常获取指令
(3)具有cpu用于数据操作的可读写内存
(4)cpu被release reset 当然对于需要支持中断的系统,则还需要包含中断控制器,而对于像操作系统这种需要通过定时器驱动进程切换的系统,则显然还需要timer定时器。为了达到以上目的,我们添加board的基本步骤大概如下: (1)在board目录下为新board添加一个目录,用于存放board特定的代码
(2)为新目录添加kconfig配置选项和makefile编译选项,将其添加到编译系统中
(3)在kconfig中为该board定义一个配置项,并为该配置项添加其所支持的特性,如cpu架构、cpu型号等
(4)为新board增加一个配置相关的头文件和编译所需的defconfig文件,用于该board相关的选项配置
(5)在board目录下添加适当的文件,并实现必要的接口
test board添加示例添加target配置选项在arch/arm/kconfig的board select菜单下新增如下的target_testboard配置选项:config target_testboard bool qemu test board select arm64 select dm select dm_serial select pl01x_serial select support_spl select spl if support_spl select spl_framework_board_init_f if spl select spl_serial_support select pl011_serial if spl select spl_libgeneric_support if spl select spl_libcommon_support if spl它将在后面的configs/testboard_defconfig中通过config_ target_testboard =y选择
在arch/arm/kconfig文件中添加以下内容,以包含board的kconfig文件source board/mars/test/kconfig添加config头文件在include/configs目录下添加config头文件testboard.h,并添加以下内容:
#ifndef __config_h#define __config_h#include #define config_sys_sdram_base 0x40000000/* the dtb generated by qemu is placed at start of ram, stay away from there */#define config_sys_init_sp_addr (config_sys_sdram_base + sz_2m)#define config_sys_load_addr (config_sys_sdram_base + sz_2m)#define config_sys_malloc_len sz_16m#define config_sys_bootm_len sz_64m#define config_sys_hz 1000#define config_pl01x_ports {(void *)(0x9000000)}#define config_pl011_clock 1#define config_sys_uboot_start 0x40300000#define boot_target_devices(func) \\ func(virtio, virtio, 0)#define config_extra_env_settings \\ fdt_addr=0x43000000\\0 \\ kernel_addr_r=0x40000000\\0 \\ bootargs=earlycon root=/dev/vda\\0 \\ bootcmd=smhload /home/lgj/work/linux/arch/arm64/boot/image ${kernel_addr_r}; \\ smhload /home/lgj/work/linux/arch/arm64/boot/dts/qemu/test-board-smc.dtb ${fdt_addr}; \\ booti ${kernel_addr_r} - ${fdt_addr}\\0#define config_sys_cbsize 512#define config_sys_monitor_base config_sys_text_base#define config_sys_max_flash_banks_detect 2#define config_sys_max_flash_sect 256 /* sector: 256k, bank: 64m */#define config_cfi_flash_use_weak_accessors#define config_spl_max_size 0x25000#define config_spl_stack (config_sys_sdram_base + sz_1m)#define config_spl_bss_start_addr (config_sys_sdram_base + sz_1m)#define config_spl_bss_max_size 0x1000#endif添加dtb文件使用qemu模拟器启动uboot时,模拟器会提供一个默认的dtb文件,但是我们也可以使用自己自定义的dtb文件,以下是自定义dtb文件的方法:
在arch/arm/dts/目录下添加dts文件test-board-minimal.dts,并在目录的makefile中添加以下编译选项dtb-$(config_target_testboard) += test-board-minimal.dtb在配置文件configs/testboard_defconfig中指定该dtb为默认dtb文件,并使能uboot的设备树支持config_default_device_tree=test-board-minimalconfig_of_control=yconfig_of_separate=y添加board文件在board目录下创建mars/test目录在board/mars/test目录下创建kconfig文件,并添加如下内容if target_testboardconfig sys_vendor default marsconfig sys_board default testconfig sys_config_name default testboardendif其中:
sys_vendor:用于指定该board的vendor名,它与sys_board一起确定会被编译的board代码路径。即board//common和board//的路径下的makefile会被执行,在我们的例子中该目录为board/mars/common/和board/mars/test/sys_board:用于指定在board/下需要编译的board路径,如当前配置下该目录为board/mars/test/
sys_config_name用于指定include/configs目录下的头文件名,如当前配置该文件即为include/configs/testboard.h
在board/mars/test目录下创建maintainers文件,并添加如下内容qemu qemu test boardm: xxx@yyy.coms: maintainedf: board/mars/testf: include/configs/testboard.hf: configs/testboard.h在board/mars/test目录下创建makefile文件,并添加如下内容obj-y += testboard.o创建board/mars/test目录下创建testboard.c文件,并添加如下内容include #include #include #include #include #ifdef config_arm64#include static struct mm_region testboard_mem_map[] = { { /* flash */ .virt = 0x00000000ul, .phys = 0x00000000ul, .size = 0x08000000ul, .attrs = pte_block_memtype(mt_normal) | pte_block_inner_share }, { /* lowmem peripherals */ .virt = 0x08000000ul, .phys = 0x08000000ul, .size = 0x38000000, .attrs = pte_block_memtype(mt_device_ngnrne) | pte_block_non_share | pte_block_pxn | pte_block_uxn }, { /* ram */ .virt = 0x40000000ul, .phys = 0x40000000ul, .size = 255ul * sz_1g, .attrs = pte_block_memtype(mt_normal) | pte_block_inner_share }, { /* highmem pci-e ecam memory area */ .virt = 0x4010000000ull, .phys = 0x4010000000ull, .size = 0x10000000, .attrs = pte_block_memtype(mt_device_ngnrne) | pte_block_non_share | pte_block_pxn | pte_block_uxn }, { /* highmem pci-e mmio memory area */ .virt = 0x8000000000ull, .phys = 0x8000000000ull, .size = 0x8000000000ull, .attrs = pte_block_memtype(mt_device_ngnrne) | pte_block_non_share | pte_block_pxn | pte_block_uxn }, { /* list terminator */ 0, }};struct mm_region *mem_map = testboard_mem_map;#endifint board_init(void){ return 0;}int dram_init(void){ if (fdtdec_setup_mem_size_base() != 0) return -einval; return 0;}int dram_init_banksize(void){ fdtdec_setup_memory_banksize(); return 0;}void *board_fdt_blob_setup(void){ /* qemu loads a generated dtb for us at the start of ram. */ return (void *)config_sys_sdram_base;}void enable_caches(void){ icache_enable(); dcache_enable();}#ifdef config_splu32 spl_boot_device(void){ return boot_device_semihosting;}#endif创建defconfig配置文件在configs目录下为testboard创建配置文件testboard_defconfig,并添加如下内容
config_arm=yconfig_target_testboard=yconfig_position_independent=yconfig_nr_dram_banks=1config_env_size=0x40000config_env_sect_size=0x40000config_default_device_tree=test-board-minimalconfig_env_addr=0x4000000config_of_control=yconfig_of_separate=yconfig_dm_serial=yconfig_dm_eth=yconfig_sysreset=yconfig_semihosting=y# add boot stage info to fdtconfig_of_fdt=yconfig_spl_sys_malloc_f_len=0x1000config_spl_text_base=0x40000000config_cons_index=0config_sys_text_base=0x40300000# config_display_cpuinfo is not setspl支持semihost启动将arch/arm/lib/semihosting.c中smh_load_file导出,即去掉下面定义中的staticstatic int smh_load_file(const char * const name, ulong load_addr, ulong *end_addr){ …}将arch/arm/lib/semihosting.c中的do_smhload文件修改为只有uboot编译,即将其修改为:#ifndef config_spl_buildstatic int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) {}#endif在arch/arm/include/asm/spl.h的boot device枚举中添加对semihosting的支持enum { … boot_device_semihosting, boot_device_none};在common/spl/目录下添加文件spl_semihosting.c,并添加以下内容:#include #include extern int smh_load_file(const char * const name, ulong load_addr, ulong *end_addr);static int spl_sh_load_image(struct spl_image_info *spl_image, struct spl_boot_device *bootdev){ int rc; ulong uboot_load_addr = 0x40300000, uboot_end_addr; rc = smh_load_file(u-boot.bin, uboot_load_addr, &uboot_end_addr); if (rc load_addr = uboot_load_addr; spl_image->entry_point = uboot_load_addr; spl_image->os = ih_os_u_boot; return 0;}spl_load_image_method(semihosting, 0, boot_device_semihosting, spl_sh_load_image);
特斯拉Model3原型车路跑视频曝光 今年7月将开始量产
PWM驱动有刷电机时的电流再生方法及其区别
光学系统热效应之无热化分析
珞石科技与华睿科技合作将共同推动机器人领域的应用发展
开关调光调色温恒流icSM2213EM无频闪三段应用替换CYT调色
U-boot如何添加board
语音识别电路设计图集锦 —电路图天天读(71)
5G时代,鸿蒙系统的生态战!鸿蒙系统有多牛?
三星Note 10再曝正面渲染图 居中前置摄像头更顺眼
精谱测控无纺布在线污点检测系统实时监测表面缺陷
从IoT 到 AIoT,物联网行业发生的变化分析
功放之间有什么差异?
网络安全巨头SolarWinds被俄罗斯黑客攻击 美国情报界正在紧急调查
中国研发海底光缆自主制造技术 最大长度可达245km
拆解一款电动新风口罩,顺藤摸瓜浅析一下电机驱动芯片
LED显示屏技术的四大问题分析
在那科技基于LoRa® 打造无界位置管理系统和智能体内生物胶囊
如何在INCA的测量文件中记录数据集、ECU描述文件等额外信息?
田间小气候监测站的作用及配置的介绍
MOV-压敏电阻的五处常见应用范围及三点选型注意事项