从刷机开始学ARM

1、什么是ARM

①ARM是一家公司:ARM前身为艾康电脑,于1978年在英国剑桥成立。1980年苹果与艾康合作,研究ARM。
1985年研究出ARM1(全球第一款商用RISC架构CPU)。1990年从艾康电脑中分离出Advanced RICS Machines(ARM),ARM公司正式成立。
②ARM同时又是一种芯片架构:ARM公司不生产芯片,他们只设计芯片,出售版权。

2、ARM的各种版本号

内核版本号SOC版本号芯片型号
ARMv4ARM7s3c44B0
ARMv4ARM9s3c2440 s3c2410
ARMv5ARM9+xscale
ARMv6ARM11s3c6410
ARMv7cortex-M(单片机)STM32
ARMv7cortex-A(嵌入式)cortex-A8(s5pv210)cortex-A9(exynos4412)
ARMv7cortex-R(工业实时性)
ARMv8Cortex-A35、Cortex-A50系列、Cortex-A72、Cortex-A73现在手机的芯片64位时代

SOC与CPU的区别
SOC=system on chip(片上系统)
ARM出售的内核就是CPU,SOC中包含外设,集成度高

3、RISC、CISC、统一编址、独立编址、哈佛结构、冯诺依曼结构

RISC 和CISC架构
Intel采用的就是CISC架构的CPU,可以用最少的指令完成任务,编译器也比较好设计,但是工艺相当复杂,功耗高,比较难做。
ARM采用的就是RISC架构,功耗低,芯片好做,精简指令集,指令少
统一编址、独立编址、哈佛结构、冯诺依曼结构
IO:input output 如uart LCD LED等外设都是IO,内存,CPU通过地址总线来访问,
统一编址:内存和IO都是通过总线地址来让CPU访问。
独立编址:IO用专用的指令集去访问特定的外设。
哈佛结构:程序和数据分开放,程序放在ROM中,数据放在RAM中。(单片机都是这样的)
冯诺依曼结构:数据和程序都放在内存中。

4、ARM的编程模式和7种工作模式

编程模式:
ARMv7之前的编程模式:
ARM模式:ARM指令集
thumb模式:thumb指令集
thumb2模式:thumb2指令集
ARMv8之后的编程模式:
AArch64/A64:64位
AArch32/A32:32位
T32(Thumb-2):16或32位
兼容ARMv7用户空间

工作模式:
user:非特权模式,大部分都在这种模式下工作
FIQ:快速中断模式 异常模式,特权模式
IRQ:普通中断模式 异常模式,特权模式
Supervisor:复位或者软中断模式 异常模式,特权模式
Abort:存取异常 异常模式,特权模式
undef:未定义模式 异常模式,特权模式
system:系统模式 特权模式

5、ARM的通用寄存器

ARM的通用寄存器共有37个。

user模式下可用的寄存器是r0~r15和cpsr寄存器,其中r13又叫sp(栈)寄存器,r14又叫lr寄存器。r15又叫pc寄存器。pc(程序控制寄存器)中的地址是哪里,CPU就执行在哪里。
FIQ:其中的寄存器r0~r7和r15以及cpsr寄存器和user一样,但是有自己的r8~r14,以及spsr
IRQ:其中的寄存器r0~r12和r15以及cpsr寄存器和user一样,但是有自己的r13~r14,以及spsr
Supervisor:其中的寄存器r0~r12和r15以及cpsr寄存器和user一样,但是有自己的r13~r14,以及spsr
Abort:其中的寄存器r0~r12和r15以及cpsr寄存器和user一样,但是有自己的r13~r14,以及spsr
undef:其中的寄存器r0~r12和r15以及cpsr寄存器和user一样,但是有自己的r13~r14,以及spsr
system:和user的寄存器一模一样,但是这是特权模式下的。
这里所有的cpsr又叫程序状态寄存器,其中bit0~bit4位工作模位,bit5是ARM编程模式位,bit6和bit7是FIQ和IRQ的中断使能位,bit28~bit31是条件位。

6、ARM异常处理方式

异常向量表

发生异常时需要:
①拷贝cpsr到spsr中
②设置cpsr
③保存返回地址到lr
④设置pc到异常处
⑤返回:从spsr到cpsr,从lr到pc。

7、S5PV210的介绍

上面说的都是ARM通用的,毕竟要落实到具体的一款芯片,我学的是三星的这款S5PV210,我就用它来说喽。
正常我们用的大容量的内存(DDR),刚通上电是不可以用的,需要初始化,这里我简单罗列一些硬件物质的特性:
内存(RAM):
SRAM:上电即可运行,不需要初始化。
SDRAM:同步动态随机存储器。需要初始化才可以使用
DDR:双向倍速SDRAM。需要初始化才能使用。有DDR1、DDR2、DDR3、DDR4。

外存:
Norflash:上电就可以使用,是总线式访问的,接到SROM bank上。
Nandflash:分为MLC和SLC。MLC常用便宜但是不稳定,SLC贵,但是稳定,需要初始化才能用
eMMC:分为iNand(SanDisk)和movinand(三星设计),需要初始化才能用
onenand:三星设计的一种存储物质,贼贵,需要初始化才能用
SD/TF/MMC卡:存储卡,需要初始化才能用
ESSD:固态硬盘,需要初始化才能用
SATA:机械硬盘,需要初始化才能用

那么既然如此,我们开机系统怎么就能启动起来了呢?是这样的:s5pv210有内置的64KB的IROM(类似于norflash,可以上电直接使用。IROM中的代码干了什么官方文档有写)和96KB的IRAM(是SRAM可以上电直接使用的)。其中的IROM是由内置代码的,固化在上面,由三星公司烧进去的,未公开的代码。IROM中有各种外存的设备初始化的代码,因为一共也就只有这几种物质,但是没有DDR初始化的代码,因为生产DDR的生产商很多。

s5pv210的启动方式详解
①三星推荐的启动方式:
三星公司要求所使用的BootLoader必须大于16KB,又必须小于96KB,我们先假设我们的BootLoader为80KB,开机,首先BL0(也就是IROM上的代码)最开始运行,BL0会根据判断我们的启动方式,选择外部的存储介质。,加载BootLoader中的前16KB到SRAM中运行,这个前16KB的代码就是BL1。BL1运行会加载BL2(剩余的64KB的代码)到SARM中运行。BL2会将DDR初始化。并将内核加载到DDR中去运行,BootLoader结束。

②uboot的启动方式:
uboot可以任意大小,无所谓。首先BL0(也就是IROM上的代码)最开始运行,BL0会根据判断我们的启动方式,选择外部的存储介质。,加载BootLoader中的前8KB或者16KB到SRAM中运行,这个前16KB的代码就是BL1。这个BL1就会去初始化DDR。并且把整个uboot加载到DDR中去执行,然后用一句长跳转(从SRAM到DDR中),指令直接从SRAM跳转到DDR中继续执行跳转到的那个地方继续执行uboot知到uboot完全启动,uboot启动之后会在命令行或者默认启动OS(操作系统)
实际在uboot中,会有一个脚本将uboot截成一个8KB或者16KB大小的文件,这就是BL1 ,放在SD卡中的第一个扇区。整个uboot作为BL2放在第49个扇区。(放在哪个扇区都是自己设计的,放在哪里就去哪里找),执行完BL1之后,BL1会重定位将BL2加载到DDR,然后长跳转执行后面的程序。

我用的板子可以用USB方式和SD卡的方式启动,其中文档上写着USB方式启动的程序不需要16字节校验头,而SD卡需要。如果是SD卡启动,先从板载内置SD卡0通道的inand启动,启动失败会从外置的SD卡2通道启动

8、刷机

我之前说过,我不想用虚拟机,所以我在Linux下面进行刷机,在Linux下刷机还不用装fastboot驱动,很方便。步骤如下:
①Linux中安装fastboot,sudo apt-get install fastboot,Windows中自行找软件并在命令行下使用。fastboot是谷歌发行的一种协议,刷机很方便。如果inand中有系统,要先擦除,打开串口minicom,见上一篇文章的配置,开机,进入Linux系统控制台依次执行busybox dd if=/dev/zero of=/dev/block/mmcblk0 bs=512 seek=1 count=1 conv=sync​ sync,接着重启
②准备一张刷了uboot的SD卡,可以使用Windows工具刷,也可以使用Linux脚本刷。比如我用的uboot是三星移植好的,在uboot源码目录根目录下/sd_fusing中,要重新make clean ,再执行make,编译一下,使用方法sudo ./sd_fusing.sh /dev/sdc.我这里SD卡对应sdc,根据自己情况调整
③连接USB线,选择SD卡启动,打开串口minicom,在uboot倒数三秒进入uboot控制台,输入fastboot.
④在终端中执行fastboot devices 命令用来查看当前连接的设备。
⑤准备好要刷的镜像,放到自定的位置,我放到了android4.0目录下,顺序执行下面命令:

fastboot flash bootloader android4.0/uboot.bin               //烧uboot
fastboot flash kernel android4.0/zImage-android            //烧linux kernel 
fastboot flash system android4.0/x210.img                         //烧android rom

刷机完毕,fastboot reboot 重启即可。

9、Linux中用SD卡烧裸机代码

if=xxx.bin中的xxx.bin为自己要烧的程序,of=/dev/sdc为SD卡对应的设备文件,seek=1是因为芯片手册中规定了使用SD卡的话,前面要空出一个扇区,这句话就是空出一个扇区的作用

sudo dd iflag=dsync oflag=dsync if=xxx.bin of=/dev/sdc seek=1

10、安装交叉编译工具链

因为各个平台的CPU的指令各不相同,要想编写出在对应平台上能运行的程序,就要用对应的编译器,这就是为什么装交叉编译工具链。交叉编译工具链是根据开源gcc进行进一步的配置即可得到对应的平台的编译器,由芯片原厂来进行配置,然后打包发出去。

交叉编译工具链的安装

在/usr/local/下创建/usr/local/arm文件夹,解压工具链tar -jxvf arm-2009q3.tar.bz2到此相当于程序已经安装完毕,真正的应用程序安装在/usr/local/arm/arm-2009q3/bin目录下我们装软一般都在/usr目录下。我们安装arm-linux-gcc,就在/usr/local/底下创建一个arm文件夹,然后装到里面。到真正的应用程序的安装目录下(也就是/usr/local/arm/arm-2009q3/bin),去执行arm-linux-gcc -v执行方法是:./arm-none-linux-gnueabi-gcc -v执行后可以得到一长串输出,其中有“gcc version 4.4.1 ”字样,即表示安装成功。

将工具链导出到环境变量
export PATH=/usr/local/arm/arm-2009q3/bin:$PATH
​ 在一个终端中执行以上命令后,该终端中就可以直接使用arm-linux-gcc了,但是只要关掉这个终端再另外打开一个立马就不行了。原因是我们本次终端中执行时的操作只是针对本终端,以后再打开的终端并未被执行过这个命令所以没导出。

​ 解决方案是在~/.bashrc中,添加export PATH=/usr/local/arm/arm-2009q3/bin:$PATH 即可。注意:我们导出这个环境变量是在当前用户,如果你登录时在其他用户下是没用的。我用的是zsh,所以要在~/.zshrc中执行刚刚的操作。

为工具链创建arm-linux-xxx符号链接
​ ln arm-none-linux-gnueabi-addr2line -s arm-linux-addr2line
其他的照样进行符号链接即可。可以用shell脚本来完成这个工作

11、Makefile

window的程序员大部分 都不知道Makefile这个东西,因为Windows已经把这个东西封装了,但是UNIX或者linux程序员就肯定要跟这个东西打交道了。但是浅尝辄止即可,会用,会改就行。深入理解Makefile可以看这两本书《跟我一起学Makefile》、《GNUmake手册》。
我放上一段通用的Makefile,以后使用Makefile只需要在下面的代码中稍加改动就行了:

通用Makefile

CROSS_COMPILE =                                                                 #注释:这里以后要写上交叉编译工具链的名字
AS        = $(CROSS_COMPILE)as
LD        = $(CROSS_COMPILE)ld
CC        = $(CROSS_COMPILE)gcc
CPP        = $(CC) -E
AR        = $(CROSS_COMPILE)ar
NM        = $(CROSS_COMPILE)nm

STRIP        = $(CROSS_COMPILE)strip
OBJCOPY        = $(CROSS_COMPILE)objcopy
OBJDUMP        = $(CROSS_COMPILE)objdump

export AS LD CC CPP AR NM
export STRIP OBJCOPY OBJDUMP

CFLAGS := -Wall -O2 -g
CFLAGS += -I $(shell pwd)/include

LDFLAGS := 

export CFLAGS LDFLAGS

TOPDIR := $(shell pwd)
export TOPDIR

TARGET := test

obj-y += main.o                 #这里是写的程序,main.c对应main.o,如果还有别的程序依次添加即可
obj-y += sub.o
obj-y += a/                           #包含文件夹,一部分程序存在子文件夹a中也要包含,自己改文件夹名,不可省略/
                                                 #同时还要在a文件夹下创建子Makefile,里面用obj-y +=添加子文件夹的程序即可


all : start_recursive_build $(TARGET)        #all为目标,冒号之后为依赖
    @echo $(TARGET) has been built!      #命令前面放的是一个tab按键,不可为空格

start_recursive_build:
    make -C ./ -f $(TOPDIR)/Makefile.build

$(TARGET) : built-in.o
    $(CC) $(LDFLAGS) -o $(TARGET) built-in.o

clean:
    rm -f $(shell find -name "*.o")
    rm -f $(TARGET)

distclean:
    rm -f $(shell find -name "*.o")
    rm -f $(shell find -name "*.d")
    rm -f $(TARGET)

我们编译程序的时候,需要在程序根目录创建一个名为Makefile文件,然后在文件中,编辑上内容,然后退出,就可以在命令行上使用make命令来执行了。只用一个make默认就会执行第一个目标。

12、链接脚本

链接脚本其实是个规则文件,他是程序员用来指挥链接器工作的。链接器会参考链接脚本,并且使用其中规定的规则来处理.o文件中那些段,将其链接成一个可执行程序。​ 链接脚本的关键内容有2部分:段名 + 地址(作为链接地址的内存地址)
链接脚本的理解:
​ SECTIONS {} 这个是整个链接脚本
. 点号在链接脚本中代表当前位置。
= 等号代表赋值

13、ARM汇编

汇编分为多种,这里指的是ARM汇编,不是什么Intel8086汇编,要区分。学汇编主要就是学那些指令集。
待补充……

Last modification:November 5th, 2019 at 10:10 pm
如果觉得我的文章对你有用,请随意赞赏

Leave a Comment