接前一篇文章:字节跳动社招面经 —— BSP驱动工程师(1)-优快云博客
本文内容参考:
特此致谢!
上一回开始,针对于微信公众号“嵌入式充电站”发的一篇文章字节跳动社招面经——BSP驱动工程师中的面试题进行解答。
讲解了第1个问题 —— bootrom的主要工作。但是至少讲解了能够应答面试的内容,本回讲解Boot ROM更为深入的内容。
1. bootrom的主要工作
(4)BootROM的具体工作
- 工作流程
1)系统启动
当系统上电或复位时,CPU首先执行BootROM中的代码。
2)硬件初始化
BootROM中的代码会进行硬件初始化操作,为后续的引导加载和操作系统启动做准备。
3)加载引导加载程序
一旦硬件初始化完成,BootROM会找到并加载引导加载程序到内存中,并将CPU控制权转交给它。
4)操作系统启动
引导加载程序接管后,会进一步初始化系统并加载操作系统。
- 详细步骤
1)系统启动及硬件初始化
BootROM在系统上电或复位时被执行,负责进行基本的硬件初始化,如设置时钟、复位外设、配置I/O引脚等 。由于需要直接与硬件打交道,因此BootROM代码通常是用汇编语言编写的。
下面以一个常见的arm芯片中启动代码的实例,讲解这一部分(例程引自什么是BootRom-优快云博客)。
.section .text
.global _start
_start:
/* 设置系统时钟 */
ldr r0, =0x12340000 /* 时钟控制器的基地址 */
ldr r1, =0x0001C000 /* 时钟设置值 */
str r1, [r0, #0x4] /* 设置系统时钟频率 */
/* 初始化内存控制器 */
ldr r0, =0x12345000 /* 内存控制器的基地址 */
ldr r1, =0x00000001 /* 内存控制器初始化值 */
str r1, [r0, #0x0] /* 初始化内存控制器 */
/* 初始化中断控制器 */
ldr r0, =0x12350000 /* 中断控制器的基地址 */
ldr r1, =0x00000002 /* 中断控制器初始化值 */
str r1, [r0, #0x4] /* 初始化中断控制器 */
/* 配置I/O引脚 */
ldr r0, =0x12360001 /* GPIO控制器的基地址 */
mov r1, #1 /* 设置引脚为输出模式 */
str r1, [r0] /* 配置引脚 */
/* 检测启动设备并加载引导加载程序 */
ldr r0, =0x12370000 /* 启动设备接口的基地址 */
ldr r1, =0x00000003 /* 启动设备检测命令 */
str r1, [r0] /* 发送检测命令 */
ldr r1, [r0] /* 读取检测结果 */
cmp r1, #0
beq fail /* 如果没有检测到启动设备,跳转到失败处理 */
/* 加载引导加载程序到RAM */
ldr r0, =0x20000000 /* 引导加载程序的加载地址 */
ldr r1, =0x12370004 /* 启动设备的数据端口 */
load_bootloader:
ldr r2, [r1] /* 从启动设备读取一个字 */
str r2, [r0] /* 将读取的数据写入RAM */
add r0, r0, #4 /* 更新加载地址 */
cmp r0, #0x20000100 /* 检查是否加载完成 */
ble load_bootloader
/* 跳转到引导加载程序执行 */
b _start_bootloader
fail:
/* 失败处理 */
b fail /* 进入死循环 */
_start_bootloader:
ldr r0, =0x20000000 /* 加载地址 */
bx r0 /* 跳转到引导加载程序 */
.pool
a)设置系统时钟
代码片段如下:
/* 设置系统时钟 */
ldr r0, =0x12340000 /* 时钟控制器的基地址 */
ldr r1, =0x0001C000 /* 时钟设置值 */
str r1, [r0, #0x4] /* 设置系统时钟频率 */
注:这里假设时钟控制器的基地址是 0x12340000
,通过设置特定的寄存器值来配置系统时钟频率。以下代码均情况类似。
b)初始化内存控制器
代码片段如下:
ldr r0, =0x12345000 /* 内存控制器的基地址 */
ldr r1, =0x00000001 /* 内存控制器初始化值 */
str r1, [r0, #0x0] /* 初始化内存控制器 */
c)初始化中断控制器
代码片段如下:
/* 初始化中断控制器 */
ldr r0, =0x12350000 /* 中断控制器的基地址 */
ldr r1, =0x00000002 /* 中断控制器初始化值 */
str r1, [r0, #0x4] /* 初始化中断控制器 */
d)配置I/O引脚
代码片段如下:
/* 配置I/O引脚 */
ldr r0, =0x12360001 /* GPIO控制器的基地址 */
mov r1, #1 /* 设置引脚为输出模式 */
str r1, [r0] /* 配置引脚 */
2)找到并加载引导加载程序
BootROM的主要任务是找到并加载引导加载程序(Bootloader)。它通常从固定位置读取引导加载程序,如内置存储(如eMMC、NAND、NOR闪存等)或外部存储(如SD卡、USB设备等) 。
a)检测启动设备并找到引导加载程序
代码片段如下:
/* 检测启动设备并加载引导加载程序 */
ldr r0, =0x12370000 /* 启动设备接口的基地址 */
ldr r1, =0x00000003 /* 启动设备检测命令 */
str r1, [r0] /* 发送检测命令 */
ldr r1, [r0] /* 读取检测结果 */
cmp r1, #0
beq fail /* 如果没有检测到启动设备,跳转到失败处理 */
b)加载引导加载程序
代码片段如下:
/* 加载引导加载程序到RAM */
ldr r0, =0x20000000 /* 引导加载程序的加载地址 */
ldr r1, =0x12370004 /* 启动设备的数据端口 */
load_bootloader:
ldr r2, [r1] /* 从启动设备读取一个字 */
str r2, [r0] /* 将读取的数据写入RAM */
add r0, r0, #4 /* 更新加载地址 */
cmp r0, #0x20000100 /* 检查是否加载完成 */
ble load_bootloader
c)跳转到引导加载程序执行
代码片段如下:
/* 跳转到引导加载程序执行 */
b _start_bootloader
如果能够再讲一下检测启动设备的机制和实现细节,那么估计这一道题就能基本通过面试了。
检测启动设备是BootROM加载Bootloader过程中的一个关键步骤。这个过程涉及到检查系统中可用的启动媒体,如内置存储(eMMC、NAND、NOR闪存)或外部存储(SD卡、USB设备),并确保系统能够从这些设备之一启动。
BootROM会检查连接的启动设备,如eMMC、NAND、NOR闪存或外部存储器(如SD卡、USB设备)。
检测启动设备的实现机制如下:
1)初始化启动设备接口
BootROM首先初始化与启动设备通信所需的接口。这可能包括设置正确的时钟频率、配置I/O引脚和启用相应的控制器(如SD/MMC控制器、USB主机控制器等)。
2)检测设备存在与否通过读取设备的ID寄存器或发送检测命令来验证设备的物理存在。例如,对于NAND闪存,可能需要发送一个读取ID命令;对于SD卡,可能需要检查卡检测引脚的状态。
3)读取启动设备信息一旦设备被检测到,BootROM会读取存储在设备上的启动信息。这可能包括启动扇区的内容、文件系统上的特定文件或启动参数。
4)加载BootloaderBootROM根据读取的信息加载Bootloader。这通常涉及到将Bootloader的二进制数据从启动设备传输到RAM或其它内存区域。
5)验证Bootloader在某些系统中,BootROM可能会对加载的Bootloader进行校验,以确保其完整性和安全性。这可以通过比较Bootloader的哈希值或检查数字签名来完成。
6)跳转到Bootloader一旦Bootloader被加载和验证,BootROM会跳转到Bootloader的入口点,将控制权交给Bootloader。
至此,字节跳动社招BSP驱动工程师一面的第1题 —— bootrom的主要工作就解答、解析完了。