当前位置:网站首页>聊聊SOC启动(九) 为uboot 添加新的board
聊聊SOC启动(九) 为uboot 添加新的board
2022-07-07 09:30:00 【lgjjeff】
本文基于以下软硬件假定:
架构:AARCH64
软件:Uboot 2021.10-rc1
1 Uboot代码层次
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官方版本中都已经被支持。因此可以直接复用这部分实现,我们唯一要做的就是选择正确的配置选项
2 如何添加board
2.1 添加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目录下添加适当的文件,并实现必要的接口
2.2 test board添加示例
接下来我们将自定义一款目标板test,该board以armv8架构的qemu虚拟机virt machine作为硬件,并且在board添加完成后通过qemu执行相关的功能测试。该目标板的命名如下:
vendor:mars
board:test
2.2.1 添加target配置选项
(1)在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选择
(2)在arch/arm/Kconfig文件中添加以下内容,以包含board的Kconfig文件
source "board/mars/test/Kconfig"
2.2.2 添加config头文件
在include/configs目录下添加config头文件testboard.h,并添加以下内容:
#ifndef __CONFIG_H
#define __CONFIG_H
#include <linux/sizes.h>
#define CONFIG_SYS_SDRAM_BASE 0x40000000
#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
2.2.3 添加dtb文件
使用qemu模拟器启动uboot时,模拟器会提供一个默认的dtb文件,但是我们也可以使用自己自定义的dtb文件,以下是自定义dtb文件的方法:
(1)在arch/arm/dts/目录下添加dts文件test-board-minimal.dts,并在目录的Makefile中添加以下编译选项
dtb-$(CONFIG_TARGET_TESTBOARD) += test-board-minimal.dtb
(2)在编译配文件configs/testboard_defconfig中指定该dtb为默认dtb文件,并使能uboot的设备树支持
CONFIG_DEFAULT_DEVICE_TREE="test-board-minimal"
CONFIG_OF_CONTROL=y
CONFIG_OF_SEPARATE=y
2.2.4 添加board文件
(1)在board目录下创建mars/test目录
(2)在board/mars/test目录下创建Kconfig文件,并添加如下内容
if TARGET_TESTBOARD
config SYS_VENDOR
default "mars"
config SYS_BOARD
default "test"
config SYS_CONFIG_NAME
default "testboard"
endif
其中:
SYS_VENDOR:用于指定该board的vendor名,它与SYS_BOARD一起确定会被编译的board代码路径。
即board/< SYS_VENDOR >/common和board/< SYS_VENDOR >/< SYS_BOARD >的路径下的Makefile会被执行,在我们的例子中该目录为board/mars/common/和board/mars/test/
SYS_BOARD:用于指定在board/< SYS_VENDOR >下需要编译的board路径,如当前配置下该目录为board/mars/test/
SYS_CONFIG_NAME用于指定include/configs目录下的头文件名,如当前配置该文件即为include/configs/testboard.h
(3)在board/mars/test目录下创建MAINTAINERS文件,并添加如下内容
QEMU QEMU TEST BOARD
M: [email protected].com
S: Maintained
F: board/mars/test
F: include/configs/testboard.h
F: configs/testboard.h
(4)在board/mars/test目录下创建Makefile文件,并添加如下内容
obj-y += testboard.o
(5)创建board/mars/test目录下创建testboard.c文件,并添加如下内容
#include <common’s>
#include <cpu_func.h>
#include <fdtdec.h>
#include <init.h>
#include <spl.h>
#ifdef CONFIG_ARM64
#include <asm/armv8/mmu.h>
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;
#endif
int 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_SPL
u32 spl_boot_device(void)
{
return BOOT_DEVICE_SEMIHOSTING;
}
#endif
2.2.5 创建defconfig配置文件
在configs目录下为testboard创建配置文件testboard_defconfig,并添加如下内容
CONFIG_ARM=y
CONFIG_TARGET_TESTBOARD=y
CONFIG_POSITION_INDEPENDENT=y
CONFIG_NR_DRAM_BANKS=1
CONFIG_ENV_SIZE=0x40000
CONFIG_ENV_SECT_SIZE=0x40000
CONFIG_DEFAULT_DEVICE_TREE="test-board-minimal"
CONFIG_ENV_ADDR=0x4000000
CONFIG_OF_CONTROL=y
CONFIG_OF_SEPARATE=y
CONFIG_DM_SERIAL=y
CONFIG_DM_ETH=y
CONFIG_SYSRESET=y
CONFIG_SEMIHOSTING=y
# add boot stage info to fdt
CONFIG_OF_FDT=y
CONFIG_SPL_SYS_MALLOC_F_LEN=0x1000
CONFIG_SPL_TEXT_BASE=0x40000000
CONFIG_CONS_INDEX=0
CONFIG_SYS_TEXT_BASE=0x40300000
# CONFIG_DISPLAY_CPUINFO is not set
2.2.6 spl支持semihost启动
(1)将arch/arm/lib/semihosting.c中smh_load_file导出,即去掉下面定义中的static
static int smh_load_file(const char * const name, ulong load_addr,
ulong *end_addr)
{
…
}
(2)将arch/arm/lib/semihosting.c中的do_smhload文件修改为只有uboot编译,即将其修改为:
#ifndef CONFIG_SPL_BUILD
static int do_smhload(struct cmd_tbl *cmdtp, int flag, int argc,
char *const argv[]) {
}
#endif
(3)在arch/arm/include/asm/spl.h的BOOT DEVICE枚举中添加对semihosting的支持
enum {
…
BOOT_DEVICE_SEMIHOSTING,
BOOT_DEVICE_NONE
};
(4)在common/spl/目录下添加文件spl_semihosting.c,并添加以下内容:
#include <image.h>
#include <spl.h>
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 < 0) {
if (CONFIG_IS_ENABLED(SHOW_ERRORS) &&
CONFIG_IS_ENABLED(LIBCOMMON_SUPPORT))
printf(SPL_TPL_PROMPT "load file u-boot.bin failed (err=%d)\n",
rc);
else
puts(SPL_TPL_PROMPT "load file u-boot.bin failed\n");
return -1;
}
spl_image->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);
附录:影响编译目录的配置选项说明
(1)定义CONFIG_SYS_CPU="cpu"
用于编译arch/<arch>/cpu/<cpu>
(2)定义CONFIG_SYS_SOC="soc"
用于编译arch/<arch>/cpu/<cpu>/<soc>
(3)定义CONFIG_SYS_VENDOR="vendor"
用于编译board/<vendor>/common/*和board/<vendor>/<board>/*
(4)定义CONFIG_SYS_BOARD="board"
用于编译board/<board>/*
若定义了CONFIG_SYS_VENDOR,则编译:board/<vendor>/<board>/*
(5)CONFIG_SYS_CONFIG_NAME="target"
用于包含头文件include/configs/<target>.h
边栏推荐
- Eth trunk link switching delay is too high
- 滚动踩坑--UNI_APP(八)
- Deeply understand the characteristics of database transaction isolation
- 2021-04-08
- Cmake learning manual
- Antd select selector drop-down box follows the scroll bar to scroll through the solution
- Learning notes | data Xiaobai uses dataease to make a large data screen
- Unity downloads files through the server address
- 使用MeterSphere让你的测试工作持续高效
- 从色情直播到直播电商
猜你喜欢
About the application of writing shell script JSON in JMeter
数据库同步工具 DBSync 新增对MongoDB、ES的支持
Learning notes | data Xiaobai uses dataease to make a large data screen
Mpx 插件
[untitled]
JS add spaces to the string
verilog设计抢答器【附源码】
2021-04-08
The concept, implementation and analysis of binary search tree (BST)
普通测试年薪15w,测试开发年薪30w+,二者差距在哪?
随机推荐
Mpx 插件
0.96 inch IIC LCD driver based on stc8g1k08
Ffmpeg record a video command from RTSP
[untitled]
高考作文,高频提及科技那些事儿……
Web端自动化测试失败的原因
关于在云服务器上(这里用腾讯云)安装mysql8.0并使本地可以远程连接的方法
Go-Redis 中间件
Input type= "password" how to solve the problem of password automatically brought in
学习笔记|数据小白使用DataEase制作数据大屏
[untitled]
uniCloud
Go Slice 比较
Vuthink正确安装过程
基于STC8G1K08的0.96寸IIC液晶屏驱动程序
什么是高内聚、低耦合?
Kitex 重试机制
2021-04-23
Process control (creation, termination, waiting, program replacement)
electron添加SQLite数据库