当前位置:网站首页>聊聊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
边栏推荐
- 【pyqt】tableWidget里的cellWidget使用信号与槽机制
- Activity lifecycle
- Unity script generates configurable files and loads
- 【C#】WinForm运行缩放(变糊)的解决方法
- 自律,提升自制力原来也有方法
- CentOS系统下Redis安装和自启动配置的步骤
- The post-90s resigned and started a business, saying they would kill cloud database
- JS add spaces to the string
- 测试开发基础,教你做一个完整功能的Web平台之环境准备
- 技术分享 | 抓包分析 TCP 协议
猜你喜欢
Input type= "password" how to solve the problem of password automatically brought in
JS add spaces to the string
Still cannot find RPC dispatcher table failed to connect in virtual KD
electron添加SQLite数据库
Socket socket programming
2021-04-23
The database synchronization tool dbsync adds support for mongodb and es
数据库同步工具 DBSync 新增对MongoDB、ES的支持
基于华为云IOT设计智能称重系统(STM32)
关于jmeter中编写shell脚本json的应用
随机推荐
关于测试人生的一站式发展建议
Go Slice 比较
Go redis Middleware
When initializing 'float', what is the difference between converting to 'float' and adding 'f' as a suffix?
uniCloud
RationalDMIS2022阵列工件测量
Verilog realizes nixie tube display driver [with source code]
Apprentissage comparatif non supervisé des caractéristiques visuelles par les assignations de groupes de contrôle
Add a self incrementing sequence number to the antd table component
The sixth training assignment
Leetcode - interview question 17.24 maximum submatrix
0.96 inch IIC LCD driver based on stc8g1k08
Case study of Jinshan API translation function based on retrofit framework
Process control (creation, termination, waiting, program replacement)
Force buckle 1002 Find common characters
[untitled]
在我有限的软件测试经历里,一段专职的自动化测试经验总结
[STM32] actual combat 3.1 - drive 42 stepper motors with STM32 and tb6600 drivers (I)
mif 文件格式记录
The use of list and Its Simulation Implementation