页次: 1
xiaoci 真是一个努力的朋友,挖坑网见证小白到大神的过程!
谢谢晕哥!最近的事情有点忙,耽误太多时间,前同事离职,几个烂尾项目都压在我手上,老板一直也在催进度!大女儿刚幼儿园报名要适应节奏,小孩要适应,家长也要适应。二孩还有一个月左右出生,老婆的脾气也是被现实生活折磨的非常敏感易怒,我也非常能理解她的心情,感觉自己在感情上成熟了不少(怕老婆了!!)。总之一言难尽,生活本就如此,有苦有甜吧。然后就是自己时间也安排的不是太合理,学习被一些乱七八糟的事情打乱,有点浮躁。但我不会放弃,最近可能会优先清理手头的烂尾工作,晚上抽时间出来看书学习,论坛进度更新。死磕到底,不会放弃。
u-boot 的主要的目的是启动内核
内核的主要目的是挂载文件系统并启动应用程序。
内核怎么启动第一个应用程序:
分析代码:
asmlinkage void __init start_kernel(void)
static void noinline __init_refok rest_init(void)
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND); // 启动 kernel_init 内核线程
prepare_namespace(); // 挂载根文件系统
init_post(); // 启动应用程序
分析 init_post() 代码:
-------------------------------------------------
static int noinline init_post(void)
{
free_initmem();
unlock_kernel();
mark_rodata_ro();
system_state = SYSTEM_RUNNING;
numa_default_policy();
if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) // 打开标准控制台 对应 printf
printk(KERN_WARNING "Warning: unable to open an initial console.\n");
(void) sys_dup(0); // 拷贝标准控制台"/dev/console" 对应 scanf
(void) sys_dup(0); // 拷贝标准控制台"/dev/console" 对应 err
// 我们这里控制台用的是串口0 也可以是其他,比如显示屏和键盘的组合
if (ramdisk_execute_command) {
run_init_process(ramdisk_execute_command);
printk(KERN_WARNING "Failed to execute %s\n",
ramdisk_execute_command);
}
/*
* We try each of these until one succeeds.
*
* The Bourne shell can be used instead of init if we are
* trying to recover a really broken machine.
*/
if (execute_command) { // 搜索 execute_command 它对应的是 "init=" 后面的参数
run_init_process(execute_command); // 执行 u-boot 传入的参数 init=/linuxrc run_init_process 函数,成功执行便不会再返回
printk(KERN_WARNING "Failed to execute %s. Attempting "
"defaults...\n", execute_command);
}
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
panic("No init found. Try passing init= option to kernel.");
}
-------------------------------------------------
想要知道这个文件系统有哪些东西,我们就得跟着 /sbin/init 进程一路分析下去,看些这个进程需要哪些东西。
根文件系统中许多命令 ls cp ... 这些命令都是应用程序,但是Linux中有许多命令,我们不可能都要把这些命令重新写一遍,
在嵌入式系统里面,我们有一个东西叫做 busybox 它是 ls cp cd 这些命令的合集,我们去编译 busybox ,
会得到一个 busybox 应用程序,ls cp cd 这些命令都是 busybox 的一个链接,到我们执行这些命令时实际运行的是 busybox 应用程序。
我们可以执行如下命令来验证:
ls -l /bin/ls
ls -l /bin/cp
busybox ls 也能实现 ls 的功能
在嵌入式系统中,/sbin/init 也是到 busybox 的链接
ls -l /sbin/init
所以我们想要知道 /sbin/init 进程做了哪些事情,我们需要分析 busybox 的源码。
busybox 中每一个命令都对应一个 .c 文件,我们来分析 init.c
busybox -> init_main
parse_inittab(); // 解析 init 进程 表
file = fopen(INITTAB, "r"); //打开文件INITTAB 表 "/etc/inittab" /* inittab file location */ inittab 的格式请查看文档 /examples/inittab <id>:<runlevels>:<action>:<process>
文件打开失败,解析默认项
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
文件打开成功,解析配置文件
new_init_action // 解析配置文件 把解析的内容放到一个链表中
run_actions(SYSINIT); // 运行这一类动作
run_actions(WAIT);
run_actions(ONCE);
分析:
new_init_action(ASKFIRST, bb_default_login_shell, VC_2);
替换宏:
const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL;
#define LIBBB_DEFAULT_LOGIN_SHELL "-/bin/sh"
#define ASKFIRST 0x004
# define VC_2 "/dev/tty2"
new_init_action(ASKFIRST, "-/bin/sh", /dev/tty2);
------------------------
static void new_init_action(int action, const char *command, const char *cons) // 参数 执行时机,命令或脚本,终端
{
struct init_action *new_action, *a, *last;
if (strcmp(cons, bb_dev_null) == 0 && (action & ASKFIRST))
return;
/* Append to the end of the list */
for (a = last = init_action_list; a; a = a->next) {
/* don't enter action if it's already in the list,
* but do overwrite existing actions */
if ((strcmp(a->command, command) == 0)
&& (strcmp(a->terminal, cons) == 0)
) {
a->action = action;
return;
}
last = a;
}
new_action = xzalloc(sizeof(struct init_action));
if (last) {
last->next = new_action;
} else {
init_action_list = new_action;
}
strcpy(new_action->command, command);
new_action->action = action;
strcpy(new_action->terminal, cons);
messageD(L_LOG | L_CONSOLE, "command='%s' action=%d tty='%s'\n",
new_action->command, new_action->action, new_action->terminal);
}
struct init_action {
struct init_action *next; // 链表指针
int action; // 执行时机
pid_t pid; // process ID 进程ID
char command[INIT_BUFFS_SIZE]; // 命令或脚本
char terminal[CONSOLE_NAME_SIZE]; // 终端
};
------------------------
inittab 格式: 详见源码下文档 /examples/inittab
<id>:<runlevels>:<action>:<process>
id : id=/dev/id,用做终端:stdin, stdout, stderr :printf , scanf , err 等
runlevels : 忽略
action : 执行时机 sysinit, respawn, askfirst, wait, once,restart, ctrlaltdel, and shutdown.
process : 应用程序或脚本
最小根文件系统需要:
1. dev/console、 dev/null // 控制台
2. init 应用程序 -> busybox // 第一个应用程序
3. 配置文件 /etc/inittab // 指定需要启动的程序表 指定ID 启动方式 和 时机
4. 配置文件中的应用程序 // 应用程序
5. 应用程序所需要的 C 库 // 库
配置和编译busybox
解压 tar xjf busybox-1.7.0.tar.bz2
查看 源文件目录下的 INSTALL 文件 查看编译方法
make menuconfig # This creates a file called ".config"
make # This creates the "busybox" executable
make install # or make CONFIG_PREFIX=/path/from/root install
问题 : make menuconfig 报错
Makefile:405: *** mixed implicit and normal rules: deprecated syntax
Makefile:1242: *** mixed implicit and normal rules: deprecated syntax
方法:
在makefile中将405行代码
config %config: scripts_basic outputmakefile FORCE
改为
%config: scripts_basic outputmakefile FORCE
在makefile中将1242行代码
/ %/: prepare scripts FORCE
改为
%/: prepare scripts FORCE
配置:参考韦老师书本 17.2.3 章
Busybox Setting -->
Busybox Library Tuning -->
[*、] Tab completion // 命令行 tab 补全
Build Options --->
[ ] Build BusyBox as static binary (no shared libs) // 静态链接, 我们用动态库
Archival Utilities --> // 压缩命令
// 保持默认
Linux Module Utilities ---> // 模块加载命令
// 保持默认
Linux System Utilities ---> // 支持mdev , 这可以很方便的构造 /dev 目录
// 保持默认
Networking Utilities ---> // 网络
// 保持默认
保存退出
make CROSS_COMPILE=arm-linux- // 指定工具链 编译 或者在 Makefile 中定义。
安装
mkdir -p /work/first_fs
// make CONFIG_PREFIX=/work/first_fs install //安装到指定目录 如果没有修改 Makefile 文件 的编译工具链 请使用下面的命令
make CROSS_COMPILE=arm-linux- CONFIG_PREFIX=/work/first_fs install
查看
cd /work/first_fs
ls -l
drwxrwxr-x 2 book book 4096 6月 18 08:20 bin
lrwxrwxrwx 1 book book 11 6月 18 08:20 linuxrc -> bin/busybox // u-boot 传递进来第一运行的程序 Linuxrc
drwxrwxr-x 2 book book 4096 6月 18 08:20 sbin
drwxrwxr-x 4 book book 4096 6月 18 08:20 usr
ls -l bin/ // 这些命令都是指向 busybox 的链接
构造最小根文件系统
创建设备节点:
先查看本机系统的控制台
ls /dev/console /dev/null -l
crw------- 1 root root 5, 1 6月 18 07:26 /dev/console
crw-rw-rw- 1 root root 1, 3 6月 18 07:26 /dev/null
c 字符设备 5 主设备号 1 次设备号
mkdir dev
cd dev/
sudo mknod console c 5 1
sudo mknod null c 1 3
ls -l
创建 inittab 表:
cd .. // 返回根目录
mkdir etc
vi etc/inittab 添加 console::askfirst:-/bin/sh 项 启动一个 -/bin/sh 并显示到终端 console
安装 C 库:
创建 lib 目录
mkdir lib
cd /work/tools/gcc-3.4.5-glibc-2.3.6/arm-linux/lib
cp *.so* /work/first_fs/lib/ -d // -d 参数表示保持链接 不加会把链接也拷贝成链接对应的文件,这样体积会很大
最小根文件系统已经做好了,怎么烧写到开发板?
我们需要做一个映像文件,怎么制作?
参考书本 17.4.4
编译 yaffs 工具 ,用编译后的 工具 打包制作根文件系统
cd /work/systems/
tar xjf yaffs_source_util_larger_small_page_nand.tar.bz2
cd Development_util_ok/yaffs2/utils/
make
sudo cp mkyaffs2image /usr/local/bin/
sudo chmod +x /usr/local/bin/mkyaffs2image
打包根文件系统
cd /work/
mkyaffs2image first_fs first_fs.yaffs2
烧写 first_fs.yaffs2
发现 不能执行 ps 命令
ps: can't open '/proc': No such file or directory
系统的当前状态保存在一个虚拟的 proc 文件系统,并且挂载在 /proc 目录
在单板上运行
mkdir proc
mount -t proc none /proc
然后再执行 ps 就可以查看进程了
proc 是一个虚拟的文件系统,由内核提供的
cd proc
cd 1
ls -l fd
lrwx------ 1 0 0 64 Jan 1 00:19 0 -> /dev/console // 标准输入
lrwx------ 1 0 0 64 Jan 1 00:19 1 -> /dev/console // 标准输出
lrwx------ 1 0 0 64 Jan 1 00:19 2 -> /dev/console // 标准错误
完善文件系统
mkdir proc
添加自动挂载功能
vi etc/inittab
添加启动项
::sysinit:/etc/init.d/rcS
创建
mkdir etc/init.d
vi etc/init.d/rcS
添加
mound -t proc none /proc
添加可执行权限
chmod +x etc/init.d/rcS
或者
vi etc/init.d/rcS
# mound -t proc none /proc
mount -a // 读取 /etc/fstab 中的内容
根据 etc/fstab 中的内容来挂载
vi etc/fstab
# <file system> <mount point> <type> <options> <dump> <pass>
proc /proc proc defaults 0 0
cat /proc/mounts // 查看系统已经挂在的文件系统
继续完善文件系统
在 /dev 目录下有两个设备,这是我们手动创建的
# ls dev/ -l
crw-r--r-- 1 0 0 5, 1 Jan 1 00:01 console
crw-r--r-- 1 0 0 1, 3 Jun 18 2018 null
如果一个系统里面有成千上万个设备,我们不可能全部手工来创建,这里介绍 udev
udev : 自动创建 /dev 下的设备节点,busybox 中有一个 udev 的简化版本 mdev
我们来介绍 mdev 的用法,查看文档 busybox-1.7.0\docs\mdev.txt 或书本 17.4.2.2
mkdir sys
vi /etc/fstab
添加
sysfs /sys sysfs defaults 0 0
tmpfs /dev tmpfs defaults 0 0
vi /etc/init.d/rcS
添加
mkdir /dev/pts
mount -t devpts devpts /dev/pts
echo /sbin/mdev > /proc/sys/kernel/hotplug // 热拔插 指向 /sbin/mdev 当系统设备有变化时,会调用 /sbin/mdev
mdev -s // 创建现有设备节点
重新烧录
ls dev/ 查看设备节点发现自动创建了很多
cat /proc/mounts // 查看系统已经挂载的文件系统
到此我们已经制作了一个较为完善的最小根文件系统了!
扩展知识:
果我们想使用其他的文件系统 如 jffs2 韦老师书本17.4.5
jffs2 文件系统适用于 NOR Flash ,也可以使用在 NAND Flash 上
cd work/GUI/xwindow/X/deps/
tar xzvf zlib-1.2.3.tar.gz
cd zlib-1.2.3/
./configure --shared --prefix=/usr // --shared 表示动态库 --prefix=/usr 表示安装目录
make
sudo make install
zlib 是一个压缩库,编译 jffs2 工具要用到
编译 mkfs.jffs2
cd /work/tools/
tar vxjf mtd-utils-05.07.23.tar.bz2
cd mtd-utils-05.07.23/util/
make
sudo make install
制作文件系统镜像
cd /work/
mkfs.jffs2 -n -s 2048 -e 128KiB -d first_fs -o first_fs.jffs2 // -s 页大小 -e 块大小 -d 目录 -o 输出文件名
烧写后不能正常启动,还是以yaffs2 方式识别文件系统,我们可以修改启动参数 强制以 jffs2 方式启动
set bootargs noinitrd root=/dev/mtdblock3 rootfstype=jffs2 init=/linuxrc console=ttySAC0
save
boot
我们每次修改文件系统都要重新烧写,有没有更好的办法?
答:有,NFS 网络文件系统
ifconfig eth0 up
ifconfig eth0 192.168.1.12 //同一网段
NFS:
a. 从 Flash 上启动根文件系统,在用命令挂接NFS
b. 直接从NFS 启动
挂接 NFS 条件:
1. 服务器允许那个目录可以被挂接
指定被挂接的目录
修改 sudo vi /etc/exports
/work/first_fs *(rw,sync,no_root_squash)
重启启动NFS服务
sudo /etc/init.d/nfs-kernel-server restart
本机挂接测试
sudo mount -t nfs 192.168.100.101:/work/first_fs /mnt
-----------------------------
a. 首先创建挂载点: mkdir /mnt/test1
b. 然后挂载nfs: mount -t nfs 192.168.1.3:/nfs_test /mnt/test1
c. 挂载成功之后通过 df -h 可以查看挂载的情况,nfs可用空间就是服务端/nfs_test目录所能使用的最大空间
d. 卸载nfs和普通文件系统一样,使用: umount /mnt/test1
-----------------------------
开发版挂接
mkdir mnt
mount -t nfs -o nolock 192.168.100.101:/work/first_fs /mnt
------------------------------------------------------------
直接从NFS 启动文件系统:
1. 服务器IP 根文件目录
2. 裸板IP
修改启动参数,参考内核源码\Documenttation\nfsroot.txt
bootargs=noinitrd root=/dev/mtdblock3 rootftype=jffs2 init=/linuxrc console=ttySAC0
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.123.200:/work/first_fs ip=192.168.123.201:192.168.123.200:192.168.123.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0
save
也可以开启路由器的NFS
http://www.right.com.cn/forum/thread-182695-1-1.html
我的路由器可以直接在 USB应用程序 -> NFS 服务器
将ubuntu 下的busybox 创建的根文件系统拷贝到路由器的U盘上
sudo cp -r /work/first_fs/* first_fs/ -d
登陆路由器添加共享的文件夹
vi /etc/exports
/media/upan/first_fs 192.168.123.0/24(rw,async,insecure,no_root_squash,no_subtree_check)
串口登陆开发板,修改保存参数
set bootargs noinitrd root=/dev/nfs nfsroot=192.168.123.1:/media/upan/first_fs ip=192.168.123.201:192.168.123.1:192.168.123.1:255.255.255.0::eth0:off init=/linuxrc console=ttySAC0
save
这样路由器下的电脑都可以把这个网络文件系统挂载在自己的电脑上,然后进行驱动调试,只需要把开发板网线连接路由器上然后通过telnet登陆开发板即可。
--------------------------------------------------------------
测试:在主机上编译文件,在开发板上运行。
vi hello.c
#include <stdio.h>
int main()
{
printf("xiaoci,hello!\n");
return 0;
}
arm-linux-gcc -o hello hello.c
在开发板对应目录下执行
./hello
xiaoci,hello!
文件系统更详细配置请看:http://bbs.100ask.net/forum.php?mod=viewthread&tid=17389
修改密码出现:
passwd:unknown uid 0
解决方法:
自动生成是使用了busybox提供的adduser工具和passwd工具。
在文件系统正常运行起来后,使用adduser命令,使用方法为:
#adduser root
然后就会在etc目录下自动生成passwd 、group和shadow3个文件。但是运行该命令后会打印出如下消息:
passwd:unknown uid 0
这表示不能为该用户设置密码,此时你会发现要passwd命令也无法使用。
解决的办法是,打开passwd文件,其内容为:
root:x:1000:1000:Linux User…:/home/root:/bin/sh
将用户ID和组ID均更改为0
打开group文件,其内容为:
root:x:1000:
同样将组ID改为0
然后,passwd命令就可以正常使用了。这时为root用户设置口令:
#passwd root
根据提示输入密码。其中,root用户登陆后的目录可以手动进行更改。
参考:https://www.cnblogs.com/liangwode/p/5710343.html?utm_source=itdadao&utm_medium=referral
telnet 远程登陆
/etc/init.d/rcS
中加上
telnetd& // 开机启动 telnetd 服务
就可以用 telnet IP 命令进行登陆 输入账号 密码
这样就不需要串口了
但是 telnet 无法显示内核打印信息
方法1:
可以通过 cat /proc/kmsg& 来查看 内核输出信息
ps
kill 11021
方法2:
dmesg -c //显示最新的信息 并清除
内核启动流程分析
1. 内核打补丁、配置、编译、烧写、实验
a. 解压缩、打补丁
tar xjf linux-2.6.22.6.tar.bz2
cd linux-2.6.22.6
patch -p1 <../linux-2.6.22.6_jz2440.patch
b. 配置
. make menuconfig // 自己根据菜单配置
配置项很多,比较麻烦。
. 使用默认配置,并在上面修改
find -name "*defconfig*" // 查找所有默认配置文件
cd ./arch/arm/configs // ARM 架构
找到相似的配置文件 s3c2410_defconfig
cd linux-2.6.22.6/
make s3c2410_defconfig //所有的配置项被写入 .config 文件中去
make menuconfig // 读取 .config 以菜单形式显示
教程中用的Ubuntu9.10
实际用的ubuntu 16.04 make s3c2410_defconfig 报错
Makefile:416: *** mixed implicit and normal rules: deprecated syntax
Makefile:1449: *** mixed implicit and normal rules: deprecated syntax
make: *** No rule to make target ‘s3c2410_defconfig’。 停止。
https://blog.csdn.net/u013944565/article/details/77686569
. 使用厂家提供的配置文件
将厂家提供的配置文件 命名为 .config
make menuconfig
对应开发板 cp config_ok .config 即可
make menuconfig 报错
`Symbol ‘acs_map’ has different size in shared object, consider re-linking
解决方法:
sudo apt-get install libncurses5-dev libncursesw5-dev
https://blog.csdn.net/czg13548930186/article/details/79851149
make menuconfig 菜单操作
上下左右移动光标。
回车进入子菜单
Y 选中,编译进内核
N 不选中
M 编译为模块
加粗字体为相应的快捷键
? 为帮助
/ 搜索
esc 两次退出
c. 编译
make uImage
1> 根据.config 生成 autoconfig.h 文件,供原代码使用
2> 根据.config 生成 auto.config 文件,供子目录 Makefile 使用,在顶层Makefile中包含。
make uImage时出现错误:
UIMAGE arch/arm/boot/uImage
“mkimage” command not found - U-Boot images will not be built
Image arch/arm/boot/uImage is ready
解决方法:
安装u-boot-tools软件包:
sudo apt-get install u-boot-tools
d. 下载内核
在uboot菜单选择k
在电脑上用dnw.exe -> USB Poart -> Transmit 选择编译好的内核文件 uImage
生成的内核文件在 /work/kernel/linux-2.6.22.6/arch/arm/boot 文件下
根文件系统的删除:
在uboot 下执行 nand erase root
没有根文件时,内核初始化后就卡死了。
2. 内核功能、结构,结合Makefile、Kconfig 进行分析
因为配置完后会生成一个.config 文件,所以我们分析 .config 文件中去
.config 里面都是配置项,我们以CONFIG_DM9000 为例分析。
grep "CONFIG_DM9000" * -nwR
1> arch/arm/plat-s3c24xx/common-smdk.c 源码用到 来自头文件 autoconfig.h
2> drivers/net/Makefile 子目录 Makefile 用到 // y 或 m 在 Makefile 体现 obj-$(CONFIG_DM9000) += dm9dev9000c.o Makefile中的CONFIG_DM9000 来自于 auto.config
如果是 y 则被编译进内核
如果是 m 则被编译成 .ko 模块
3> include/config/auto.conf // 来自.config自动生成 CONFIG_DM9000=y auto.conf 中的内容要被别的Makefile使用,所以它被包含在顶层的Makefile中
在顶层Makefile中查找 /auto.conf
-include include/config/auto.conf Makefile:443
4> include/linux/autoconfig.h // 来自.config自动生成 CONFIG_DM9000 = 1 不管是y 或 m 在头文件里都是1
分析Makefile和链接脚本
Makefile的介绍看韦老师书本 16.2.2 表 16.3
关于Makefile的文档请看: 源码下的Documentation/kbuid/Makefile.txt
子目录下的Makefile很简单
obj-y += a.o
obj-m += b.o
a.c b.c
怎么将两个源文件编译进内核
obj-y += a.o b.o
怎么将两个文件编译成模块 Makefile.txt->Line:190
obj-m += ab.o
ab-obj := a.o b.o
我们编译内核是使用 make uImage
所以我们查找 uImage
grep "uImage" * -nwR
uImage 在架构相关的文件中定义
arch/arm/Makefile:227:zImage Image xipImage bootpImage uImage: vmlinux
uImage 并没有在顶层Makefile中定义,所以 顶层文件应该包含 arch/arm/Makefile 文件
在顶层文件中搜索 include
/include n 下一个
Makefile:413
include $(srctree)/arch/$(ARCH)/Makefile
arch/arm/Makefile:227:zImage Image xipImage bootpImage uImage: vmlinux
继续分析 uImage 依赖于 vmlinux uImage = 头 + 真正的内核(vmlinux)
继续搜索 vmlinux 不在架构 Makefile 中,在顶层 Makefile 中有定义
grep "vmlinux" Makefile -nwR
745:vmlinux: $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) $(kallsyms.o) FORCE
展开:
$(vmlinux-lds) 是 链接脚本 arch/arm/kernel/vmlinux.lds
608:vmlinux-init := $(head-y) $(init-y)
/* 第一个文件 */
grep "head-y" arch/arm/Makefile -nwR // 架构 Makefile 中
94:head-y := arch/arm/kernel/head$(MMUEXT).o arch/arm/kernel/init_task.o // 最初始的代码 第一个链接的应该是 arch/arm/kernel/head$(MMUEXT).o
/* 初始化 */
grep "init-y" Makefile -nwR // 顶层 Makefile 中
434:init-y := init/
573:init-y := $(patsubst %/, %/built-in.o, $(init-y)) // init-y = init/built-in.o 最终init 目录下的文件会被编译成一个built-in.o文件 (patsubst 是一个Makefile 函数)
609:vmlinux-main := $(core-y) $(libs-y) $(drivers-y) $(net-y)
/* 核心 */
grep "core-y" Makefile -nwR
438:core-y := usr/
562:core-y += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
574:core-y := $(patsubst %/, %/built-in.o, $(core-y))
最终 core-y := kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o
/* 库 */
grep "libs-y" Makefile -nwR
437:libs-y := lib/
577:libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
578:libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
579:libs-y := $(libs-y1) $(libs-y2)
最终 libs-y := lib/lib.a lib/built-in.o
/* 驱动 */
grep "drivers-y" Makefile -nwR
435:drivers-y := drivers/ sound/
575:drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
最终 drivers-y := drivers/built-in.o sound/built-in.o
/* 网络 */
grep "net-y" Makefile -nwR
436:net-y := net/
576:net-y := $(patsubst %/, %/built-in.o, $(net-y))
最终 net-y := net/built-in.o
610:vmlinux-all := $(vmlinux-init) $(vmlinux-main)
611:vmlinux-lds := arch/$(ARCH)/kernel/vmlinux.lds
手工展开太过复杂,所以我查看编译的最后一条信息:
rm vmlinux // 删除 vmlinux 重新编译
make uImage V=1 // V=1 显示详细的编译信息
arm-linux-ld -EL -p --no-undefined -X -o vmlinux -T arch/arm/kernel/vmlinux.lds arch/arm/kernel/head.o arch/arm/kernel/init_task.o init/built-in.o --start-group usr/built-in.o arch/arm/kernel/built-in.o arch/arm/mm/built-in.o arch/arm/common/built-in.o arch/arm/mach-s3c2410/built-in.o arch/arm/mach-s3c2400/built-in.o arch/arm/mach-s3c2412/built-in.o arch/arm/mach-s3c2440/built-in.o arch/arm/mach-s3c2442/built-in.o arch/arm/mach-s3c2443/built-in.o arch/arm/nwfpe/built-in.o arch/arm/plat-s3c24xx/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o ipc/built-in.o security/built-in.o crypto/built-in.o block/built-in.o arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o drivers/built-in.o sound/built-in.o net/built-in.o --end-group .tmp_kallsyms2.o
通过Makefile的分析我们大概能知道内核的结构,看韦老师书本 16.2.2 表 16.2
确定链接脚本:arch/arm/kernel/vmlinux.lds // 根据 vmlinux.lds.S 生成的 链接脚本中的链接地址是虚拟地址
确定第一个文件:arch/arm/kernel/head.o
然后就可以根据第一个文件,一路跟踪下去了。
3. 内核的启动流程分析 详细请看 韦老师书本 16.3.1 图 16.7
a. __lookup_processor_type 确定内核是否支持该架构
b. __lookup_machine_type 确定内核时候支持该单板 u-boot 传入的第二个参数
c. __create_page_tables 建立一级页表
d. _arm920_setup 禁止ICache、DCache
e. __enable_mmu 使能MMU
f. __mmap_switched 复制数据段,清除BSS段,设置栈指针,保存CPU ID 到processor_id 变量、保存机器类型ID到_machine_arch_type变量,调用start_kernel
分析 arch/arm/kernel/head.S 文件
bl __lookup_processor_type @ r5=procinfo r9=cpuid // 协处理器获取处理器类型
bl __lookup_machine_type @ r5=machinfo // 判断传入的机器ID 在head-common.S 文件中去
__lookup_machine_type: @ 查找机器类型 u-boot 传入的di 2 个参数
adr r3, 3b @ r3 = address of 3b ,real address, phy address 3: .long . qian mian
ldmia r3, {r4, r5, r6} @ r4 ="." virtual address of 3b, r5 = __arch_info_begin , r6 = __arch_info_end r5 r6 在链接脚本中定义,中间是架构信息初始化段 .arch.info.init
sub r3, r3, r4 @ get offset between virt&phys
add r5, r5, r3 @ convert virt addresses to
add r6, r6, r3 @ physical address space 转换虚拟地址到物理地址(当前的虚拟地址只是链接时候用的虚拟地址,因为MMU还没有启动)
1: ldr r3, [r5, #MACHINFO_TYPE] @ get machine type
teq r3, r1 @ matches loader number? r1 di 2 个参数
beq 2f @ found 2f hou mian 2: mov pc, lr
add r5, r5, #SIZEOF_MACHINE_DESC @ next machine_desc
cmp r5, r6
blo 1b
mov r5, #0 @ unknown machine
2: mov pc, lr
在链接脚本中:
arch/arm/kernel/vmlinux.lds
__arch_info_begin = .;
*(.arch.info.init)
__arch_info_end = .;
搜索 .arch.info.init
grep ".arch.info.init" * -nR
Binary file arch/arm/mach-s3c2412/mach-smdk2413.o matches
Binary file arch/arm/mach-s3c2412/built-in.o matches
Binary file arch/arm/mach-s3c2412/mach-vstms.o matches
Binary file arch/arm/mach-s3c2410/mach-qt2410.o matches
Binary file arch/arm/mach-s3c2410/mach-smdk2410.o matches
Binary file arch/arm/mach-s3c2410/built-in.o matches
Binary file arch/arm/mach-s3c2440/mach-smdk2440.o matches
Binary file arch/arm/mach-s3c2440/built-in.o matches
Binary file arch/arm/mach-s3c2443/mach-smdk2443.o matches
Binary file arch/arm/mach-s3c2443/built-in.o matches // 内核也支持上面这些单板
arch/arm/kernel/vmlinux.lds.S:39: *(.arch.info.init) // 生成的链接脚本
arch/arm/kernel/vmlinux.lds:306: *(.arch.info.init) // 真实的链接脚本
include/asm-arm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = { \ // 文件夹链接
include/asm/mach/arch.h:53: __attribute__((__section__(".arch.info.init"))) = {\ // 真实的文件
arch.h 找到定义
#define MACHINE_START(_type,_name) \
static const struct machine_desc __mach_desc_##_type \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_##_type, \
.name = _name,
#define MACHINE_END \
};
搜索这个宏 MACHINE_START 谁在使用 ,选择其中一个单板 Mach-smdk2440.c (arch\arm\mach-s3c2440):MACHINE_START(S3C2440, "SMDK2440")
MACHINE_START(S3C2440, "SMDK2440")
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
MACHINE_END
展开:
static const struct machine_desc __mach_desc_S3C2440 \
__used \
__attribute__((__section__(".arch.info.init"))) = { \
.nr = MACH_TYPE_S3C2440, \
.name "SMDK2440",
/* Maintainer: Ben Dooks <ben@fluff.org> */
.phys_io = S3C2410_PA_UART,
.io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
.boot_params = S3C2410_SDRAM_PA + 0x100,
.init_irq = s3c24xx_init_irq,
.map_io = smdk2440_map_io,
.init_machine = smdk2440_machine_init,
.timer = &s3c24xx_timer,
};
相当于定义了一个结构体 machine_desc 这个结构体被放在 .arch.info.init 段,查看结构体 machine_desc
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head-armv.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};
继续分析 :arch/arm/kernel/head.S
bl __create_page_tables // 创建一级页表
ldr r13, __switch_data @ address to jump to after mmu has been enabled 当MMU使能之后 跳转到 __mmap_switched
adr lr, __enable_mmu @ return (PIC) address 使能MMU
ldr r13, __switch_data 的意思:
r13 = __switch_data的地址 查看 __switch_data:head-common.S (arch\arm\kernel):__switch_data:
__switch_data:
.long __mmap_switched // 相当于 r13 中保存了 __mmap_switched 函数的入口地址,这个地址是运行时地址 (虚拟地址,因为链接脚本中用的是虚拟地址)
.long __data_loc @ r4
.long __data_start @ r5
.long __bss_start @ r6
.long _end @ r7
.long processor_id @ r4
.long __machine_arch_type @ r5
.long cr_alignment @ r6
.long init_thread_union + THREAD_START_SP @ sp
adr lr, __enable_mmu 的意思:
相对地址adr 当前地址 与链接位置无关 。查看 __enable_mmu
__enable_mmu:
#ifdef CONFIG_ALIGNMENT_TRAP
orr r0, r0, #CR_A
#else
bic r0, r0, #CR_A
#endif
#ifdef CONFIG_CPU_DCACHE_DISABLE
bic r0, r0, #CR_C
#endif
#ifdef CONFIG_CPU_BPREDICT_DISABLE
bic r0, r0, #CR_Z
#endif
#ifdef CONFIG_CPU_ICACHE_DISABLE
bic r0, r0, #CR_I
#endif
mov r5, #(domain_val(DOMAIN_USER, DOMAIN_MANAGER) | \
domain_val(DOMAIN_KERNEL, DOMAIN_MANAGER) | \
domain_val(DOMAIN_TABLE, DOMAIN_MANAGER) | \
domain_val(DOMAIN_IO, DOMAIN_CLIENT))
mcr p15, 0, r5, c3, c0, 0 @ load domain access register
mcr p15, 0, r4, c2, c0, 0 @ load page table pointer
b __turn_mmu_on // MMU结尾调用了 __turn_mmu_on
分析 __turn_mmu_on 代码:
__turn_mmu_on:
mov r0, r0
mcr p15, 0, r0, c1, c0, 0 @ write control reg
mrc p15, 0, r3, c0, c0, 0 @ read id reg
mov r3, r3
mov r3, r3
mov pc, r13 // 将PC 指针指向 R13 的运行地址 即 __mmap_switched 函数的入口地址
再分析__mmap_switched 代码:
__mmap_switched:
adr r3, __switch_data + 4
ldmia r3!, {r4, r5, r6, r7}
cmp r4, r5 @ Copy data segment if needed
1: cmpne r5, r6
ldrne fp, [r4], #4
strne fp, [r5], #4
bne 1b
mov fp, #0 @ Clear BSS (and zero fp)
1: cmp r6, r7
strcc fp, [r6],#4
bcc 1b
ldmia r3, {r4, r5, r6, sp}
str r9, [r4] @ Save processor ID
str r1, [r5] @ Save machine type
bic r4, r0, #CR_A @ Clear 'A' bit
stmia r6, {r0, r4} @ Save control register values
b start_kernel // 最终调用 start_kernel C 函数 到这个位置,第一阶段代码就算完毕了,更复杂的功能在C语言中实现。
第一阶段并没有使用到u-boot 传递进来的参数,所以应该是在C函数中实现的,我们继续分析 start_kernel
一些初始化函数
printk(linux_banner); // 打印内核信息
setup_arch(&command_line);// u-boot 传入进来的命令行
setup_command_line(command_line);
分析 setup_arch(&command_line) 函数:
struct tag *tags = (struct tag *)&init_tags; // 标签
struct machine_desc *mdesc; // 架构信息的结构 machine_desc
char *from = default_command_line; // 默认命令行参数 , 如果u-boot 没有传入参数会执行默认命令行参数。
// 物理地址转虚拟地址
tags = phys_to_virt(mdesc->boot_params); // mdesc->boot_params = S3C2410_SDRAM_PA + 0x100 = 物理地址+0x100 刚好等于 u-boot 参数的存放地址 0x30000100 theKernel (0, bd->bi_arch_number, bd->bi_boot_params)
parse_tags(tags); // 解析 tags
parse_cmdline(cmdline_p, from); // 单独解析命令行
....
返回 start_kernel 函数继续分析:
start_kernel()
...
setup_arch(&command_line); // 解析 u-boot 传入进来的参数
setup_command_line(command_line); // 解析 u-boot 传入进来的参数
...
parse_early_param();
do_early_param
从 __setup_start 扫描到 __setup_end 调用 early 函数
unknown_bootoption
obsolete_checksetup
从 __setup_start 扫描到 __setup_end 调用 非 early 函数
...
rest_init();
创建 kernel_init 线程 :kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
prepare_namespace();
mount_root();// 我们的最终目的是挂接根文件系统
init_post();
sys_open((const char __user *) "/dev/console", O_RDWR, 0)// 打开控制台
// 执行应用程序
run_init_process("/sbin/init");
run_init_process("/etc/init");
run_init_process("/bin/init");
run_init_process("/bin/sh");
挂载根文件系统是哪个盘/分区?
传递进来的参数是 char *commandline = getenv ("bootargs");// 来自于环境变量 bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
root=/dev/mtdblock3 根文件放在第四个分区上
prepare_namespace(); 函数中 根文件挂载 mount_root(); 之前肯定要确定挂接的是哪个文件系统
进入 prepare_namespace
if (saved_root_name[0]) {
root_device_name = saved_root_name; // 设备名字在 saved_root_name[0] 数组中
if (!strncmp(root_device_name, "mtd", 3)) {
mount_block_root(root_device_name, root_mountflags);
goto out;
}
ROOT_DEV = name_to_dev_t(root_device_name); // ROOT_DEV 根文件设备 root_device_name 设备名字
if (strncmp(root_device_name, "/dev/", 5) == 0)
root_device_name += 5;
}
搜索 saved_root_name
Do_mounts.c (init):static char __initdata saved_root_name[64];
Do_mounts.c (init): strlcpy(saved_root_name, line, sizeof(saved_root_name));
Do_mounts.c 中 下面这两段代码与 u-boot 中的命令行相似 一个结构对应一个函数,我们可以猜测,先检测到 "root=" 这个字符串,然后执行对应的函数 root_dev_setup
static int __init root_dev_setup(char *line)
{
strlcpy(saved_root_name, line, sizeof(saved_root_name));// root_dev_setup 把/dev/mtdblock3 保存到 saved_root_name 中去
return 1;
}
__setup("root=", root_dev_setup); // 这里有 "root="
分析 __setup 宏,这个宏是一个结构体变量,它被强制定义在某一个段内,然后会有其他程序从这个段的起始地址轮训到结束地址去执行这些 __setup 命令。
搜索 __setup 搜索结果中搜索 define
Init.h (include\linux):#define __setup(str, fn) \
#define __setup(str, fn) \
__setup_param(str, fn, fn, 0)
查找 __setup_param 的定义
#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_##unique_id[] __initdata = str; \
static struct obs_kernel_param __setup_##unique_id \
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_##unique_id, fn, early }
根据上面的信息对 __setup("root=", root_dev_setup) 进行展开
#define __setup_param(str, unique_id, fn, early) \
static char __setup_str_root_dev_setup[] __initdata = "root="; \ // 定义一个字符串 __setup_str_root_dev_setup
static struct obs_kernel_param __setup_root_dev_setup \
__attribute_used__ \
__attribute__((__section__(".init.setup"))) \
__attribute__((aligned((sizeof(long))))) \
= { __setup_str_root_dev_setup, root_dev_setup, 0 } // __setup_str_root_dev_setup 对应的函数 root_dev_setup
.init.setup 在链接脚本的定义 :
__setup_start = .;
*(.init.setup)
__setup_end = .;
搜索 __setup_start __setup_end 看谁调用扫描了这些段,以及执行了这些对应函数
Main.c (init):extern struct obs_kernel_param __setup_start[], __setup_end[]; // 链接脚本地址声明
Main.c (init): p = __setup_start; // static int __init obsolete_checksetup(char *line) 函数
Main.c (init): for (p = __setup_start; p < __setup_end; p++) { // static int __init do_early_param(char *param, char *val) 函数
分析这两个函数
--------------------------------------------------
static int __init obsolete_checksetup(char *line)
{
struct obs_kernel_param *p;
int had_early_param = 0;
p = __setup_start;
do {
int n = strlen(p->str);
if (!strncmp(line, p->str, n)) {
if (p->early) { // p->early = 1
/* Already done in parse_early_param?
* (Needs exact match on param part).
* Keep iterating, as we can have early
* params and __setups of same names 8( */
if (line[n] == '\0' || line[n] == '=')
had_early_param = 1;
} else if (!p->setup_func) { // 对应的函数为空
printk(KERN_WARNING "Parameter %s is obsolete,"
" ignored\n", p->str);
return 1;
} else if (p->setup_func(line + n)) // 执行 __setup 中字符串对应的函数 p->early = 0 且 对应的函数不为空 上面 __setup("root=", root_dev_setup) 展开中 p->early = 0 所以 __setup("root=", root_dev_setup) 在这里面调用
return 1;
}
p++;
} while (p < __setup_end);
return had_early_param;
}
obsolete_checksetup 谁来调用的
static int __init unknown_bootoption(char *param, char *val)
{
/* Change NUL term back to "=", to make "param" the whole string. */
if (val) {
/* param=val or param="val"? */
if (val == param+strlen(param)+1)
val[-1] = '=';
else if (val == param+strlen(param)+2) {
val[-2] = '=';
memmove(val-1, val, strlen(val)+1);
val--;
} else
BUG();
}
/* Handle obsolete-style parameters */
if (obsolete_checksetup(param)) // 传入参数,调用 obsolete_checksetup
return 0;
...... // 略
......
}
unknown_bootoption 由 C 入口函数 asmlinkage void __init start_kernel(void) 调用
parse_args("Booting kernel", static_command_line, __start___param,
__stop___param - __start___param,
&unknown_bootoption); // 调用 unknown_bootoption parse_args 不在细分 应该是给 unknown_bootoption 传递参数 并调用
--------------------------------------------------
/* Check for early params. */
static int __init do_early_param(char *param, char *val)
{
struct obs_kernel_param *p;
for (p = __setup_start; p < __setup_end; p++) {
if (p->early && strcmp(param, p->str) == 0) { // 判断传入的参数 param 是否 等于 p->str 等于是在.init.setup 段里面扫描匹配字符串 且 p->early = 1 执行 上面 __setup("root=", root_dev_setup) 展开中 p->early = 0 所以 __setup("root=", root_dev_setup) 并不在这里面调用
if (p->setup_func(val) != 0) // p->setup_func(val) 执行对应的函数
printk(KERN_WARNING
"Malformed early option '%s'\n", param);
}
}
/* We accept everything at this stage. */
return 0;
}
do_early_param 谁来调用的
void __init parse_early_param(void)
{
static __initdata int done = 0;
static __initdata char tmp_cmdline[COMMAND_LINE_SIZE];
if (done)
return;
/* All fall through to do_early_param. */
strlcpy(tmp_cmdline, boot_command_line, COMMAND_LINE_SIZE);
parse_args("early options", tmp_cmdline, NULL, 0, do_early_param); // 这里调用 do_early_param parse_args 有时间自己分析 应该是给 do_early_param 传递参数并调用
done = 1;
}
parse_early_param 由 C 入口函数 asmlinkage void __init start_kernel(void) 调用
parse_early_param();
-------------------------------------------------------------------
分区的概念:
root=/dev/mtdblock3 根文件放在第四个分区上,我们的NAND Flash 没有分区表,所以怎么确认第四个分区的?
由于没有分区表,所以只能在代码里面写死了
以u-boot 为例
bootloader||环境变量(参数)||kernel||文件系统(root)
可以输入 u-boot 命令 mtd 查看
#: name size offset mask_flags
0: boot_loader 0x00040000 0x00000000 0
1: params 0x00020000 0x00040000 0
2: kernel 0x00200000 0x00060000 0
3: root 0x0fda0000 0x00260000 0
怎么修改分区,参考韦老师书本 16.3.3 MTD 分区
如果换一个单板,或内核,不知道这些分区信息怎么办?
可以查看内核启动时的分区打印信息
然后在代码中搜索对应的字符串如:
grep "\"bootloader\"" * -nR
arch/arm/plat-s3c24xx/common-smdk.c:120: .name = "bootloader",
static struct mtd_partition smdk_default_nand_part[] = {
[0] = {
.name = "bootloader",
.size = 0x00040000,
.offset = 0,
},
[1] = {
.name = "params",
.offset = MTDPART_OFS_APPEND, // 表示接着上一个分区
.size = 0x00020000,
},
[2] = {
.name = "kernel",
.offset = MTDPART_OFS_APPEND,
.size = 0x00200000,
},
[3] = {
.name = "root",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL,
}
};
更多请看 韦老师文本第 16 章
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
------------------------------
u-boot 命令是怎么实现的?
1. 输入字符串
2. 调用对应的函数
根据字符串调用对应的函数。
------------------------------
自定义一个 u-boot 下的 hello 命令
在 common 目录下添加 cmd_hello.c ,并在common/Makefile 中添加 cmd_hello.o
--------------------
#include <common.h>
#include <watchdog.h>
#include <command.h>
#include <image.h>
#include <malloc.h>
#include <zlib.h>
#include <bzlib.h>
#include <environment.h>
#include <asm/byteorder.h>
int do_hello (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
printf ("hello word ! argc = %d\n", argc);
for(i=0;i<argc;i++)
{
printf("argv[%d]: %s\n",i,argv[i]);
}
return 0;
}
U_BOOT_CMD(
hello, CFG_MAXARGS, 1, do_hello,
"hello - just for test \n",
"hello,long help ........................\n"
);
测试:
在u-boot 下
输入 help 查看是否有 hello 命令及简单帮助信息
输入 help hello 查看长帮助信息。
执行hello aaa bbb 查看执行结果。
链接脚本中
.u_boot_cmd 段理解,
命令结构存放段
搜索 u_boot_cmd
uboot/include/command.h
#define Struct_Section __attribute__ ((unused,section (".u_boot_cmd")))
print 命令打印含有 bootm ,以bootm为线索
搜索 bootm -> U_BOOT_CMD
U_BOOT_CMD(
bootm, CFG_MAXARGS, 1, do_bootm,
"bootm - boot application image from memory\n",
"[addr [arg ...]]\n - boot application image stored in memory\n"
"\tpassing arguments 'arg ...'; when booting a Linux kernel,\n"
"\t'arg' can be the address of an initrd image\n"
#ifdef CONFIG_OF_FLAT_TREE
"\tWhen booting a Linux kernel which requires a flat device-tree\n"
"\ta third argument is required which is the address of the of the\n"
"\tdevice-tree blob. To boot that kernel without an initrd image,\n"
"\tuse a '-' for the second argument. If you do not pass a third\n"
"\ta bd_info struct will be passed instead\n"
#endif
);
搜索 U_BOOT_CMD
#define U_BOOT_CMD(name,maxargs,rep,cmd,usage,help) \
cmd_tbl_t __u_boot_cmd_##name Struct_Section = {#name, maxargs, rep, cmd, usage, help}
U_BOOT_CMD 展开
# 把参数字符串化
## 参数字符串化后粘贴在当前位置
cmd_tbl_t __u_boot_cmd_bootm __attribute__ ((unused,section (".u_boot_cmd"))) = {"bootm", CFG_MAXARGS, 1, do_bootm, "短帮助", "长帮助"}
由此可见 U_BOOT_CMD 将 cmd_tbl_t 结构保存在 .u_boot_cmd 段
cmd_tbl_t 结构
struct cmd_tbl_s {
char *name; /* Command Name 命令对应的字符串 */
int maxargs; /* maximum number of arguments 参数长度 */
int repeatable; /* autorepeat allowed? 是否重复 */
/* Implementation function */
int (*cmd)(struct cmd_tbl_s *, int, int, char *[]); /* 函数指针 */
char *usage; /* Usage message (short) 短的帮助信息,输入help */
#ifdef CFG_LONGHELP
char *help; /* Help message (long) 长的帮助信息,输入help 命令 */
#endif
#ifdef CONFIG_AUTO_COMPLETE
/* do auto completion on the arguments */
int (*complete)(int argc, char *argv[], char last_char, int maxv, char *cmdv[]);
#endif
};
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
牢记 u-boot 的本质作用
1. 将内核从Flash读出内核到SDRAM
2. 启动内核
其它都是附加功能,分析第二阶段代码之前我们要明确目标,由于代码量比较庞大,不要花费很多精力去分析不是太重要的代码。
第二阶段代码从 start_armboot 开始
gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); // gd 是一个结构体指针,指向内存 CFG_GBL_DATA_SIZE 128 字节(韦老师书本 P260) 全局变量,保存各种参数,比如 内存大小、内存起始地址
flash_init (); // Flash 初始化
nand_init(); /* go init the NAND */
....... /* 其它一系列的初始化 */
for (;;) {//进入死循环
main_loop ();
}
分析 main_loop()
1. s = getenv ("bootdelay"); // 读取环境变量,bootdelay u-boot 启动延时时间
2. 倒计时 倒计时没有按下空格
a. s = getenv ("bootcmd"); 读出环境变量 bootcmd = nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0
b. run_command (s, 0);//启动内核s = getenv ("bootcmd")
3. 如果倒计时按下空格
a. len = readline (CFG_PROMPT);//读取串口的命令
b. run_command (lastcommand, flag);
所以u-boot 的核心是 run_command() 各种命令的集合
关于怎么自定义命令,请看我的另一个帖子,这里不重要,可跳过。
------------------------------------------------------------------
自定义u-boot命令 及u-boot 链接脚本 .u_boot_cmd 段的理解
https://whycan.cn/t_1295.html
------------------------------------------------------------------
我们继续分析代码
怎么将内核拷贝到SDRAM
s = getenv ("bootcmd")//获取环境变量 bootcmd
run_command (s, 0) //执行环境变量对应的命令 nand read.jffs2 0x30007FC0 kernel;bootm 0x30007FC0
相当于执行了nand read.jffs2 0x30007FC0 kernel 和 bootm 0x30007FC0 两条命令
分析第一条命令前先分析一下什么是环境变量?
在u-boot 命令行下 输入 print 打印出来的就是一些常用的环境变量
pc 机每个硬盘上都有一个分区表 C 盘 D 盘 ...
嵌入式Linux 没有分区表,需要我们自己约定每个分区,一般NAND Flash 上的划分为:
bootloader||环境变量(参数)||kernel||文件系统(root)
由于没有分区表,所以这些分区在源码里写死的。在配置文件 100ask24x0.h 中
#define MTDPARTS_DEFAULT "mtdparts=nandflash0:256k@0(bootloader)," \ // 从0开始的256K是bootloader
"128k(params)," \ // 128K 是 params
"2m(kernel)," \ // 2M 是内核
"-(root)" // 剩下的实际 root 文件系统
可以输入 u-boot 命令 mtd 查看
#: name size offset mask_flags
0: boot_loader 0x00040000 0x00000000 0
1: params 0x00020000 0x00040000 0
2: kernel 0x00200000 0x00060000 0
3: root 0x0fda0000 0x00260000 0
active partition: nand0,0 - (bootloader) 0x00040000 @ 0x00000000
defaults:
mtdids : nand0=nandflash0
mtdparts: mtdparts=nandflash0:256k@0(bootloader),128k(params),2m(kernel),-(root
在命令 nand read.jffs2 0x30007FC0 kernel 中 kernel也是一个环境变量 0x00200000 0x00060000
所以
nand read.jffs2 0x30007FC0 kernel
等价于
nand read.jffs2 0x30007FC0 0x00200000 0x00060000
意思是:从NAND Flash 的 0x00060000 位置读取 0x00200000(2M) 的数据到 SDRAM 的 0x30007FC0
名字kernel 不重要 重要的是他对应的地址
nand read.jffs2 0x30007FC0 kernel
nand read.jffs2 0x30007FC0 0x00200000 0x00060000
执行,是一样的效果。
由命令 nand read.jffs2 0x30007FC0 kernel 可以知道 是 nand 命令实现的烧写,所以我们分析 nand 命令对应的函数 do_nand
do_nand 函数里面的 !strcmp(s, ".jffs2") || !strcmp(s, ".e") 进而调用 ret = nand_read_opts(nand, &opts); 拷贝 内核到 SDRAM
jffs2 是一种文件格式,这里跟文件格式没有关系,只是用这个命令,对页对齐 块对齐没有限制
怎么调用内核?
bootm 0x30007FC0
分析 do_bootm
先来说明一下内核的格式
uimage: 头 + 真正的内核
typedef struct image_header {
uint32_t ih_magic; /* Image Header Magic Number */
uint32_t ih_hcrc; /* Image Header CRC Checksum */
uint32_t ih_time; /* Image Creation Timestamp */
uint32_t ih_size; /* Image Data Size */
uint32_t ih_load; /* Data Load Address 数据加载地址,如果当前地址不等于加载地址,则把内核搬运到加载地址 */
uint32_t ih_ep; /* Entry Point Address 入口地址,搬运完毕内核后,跳到入口地址启动内核 */
uint32_t ih_dcrc; /* Image Data CRC Checksum */
uint8_t ih_os; /* Operating System */
uint8_t ih_arch; /* CPU architecture */
uint8_t ih_type; /* Image Type */
uint8_t ih_comp; /* Compression Type */
uint8_t ih_name[IH_NMLEN]; /* Image Name */
} image_header_t;
bootm 启动内核分析内核当前地址是否等于加载地址,如果不等于加载地址,则把内核移动到加载地址,然后跳到内核入口去启动内核。
我们所用内核的加载地址是0x30008000
0x30008000 - 0x30007FC0 = 64 字节
64 字节刚好等于 内核头接结构 image_header_t 的长度,所以内核不用从新搬运,从而提高启动速度。
if(ntohl(hdr->ih_load) == data) ,位置相同,不搬运
继续讲怎么启动内核
1. uboot 通过特定数据格式(tag)告诉内核一些参数
setup_serial_tag (¶ms);//起始标签
setup_memory_tags (bd);//内存标签 告诉内核可用内存个数和大小
setup_commandline_tag (bd, commandline);//命令标签
setup_end_tag (bd);//结束标签
2. 跳转到内核入口地址
void (*theKernel)(int zero, int arch, uint params);//定义函数指针
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);//获取内核入口
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//传递参数 启动内核
一些分析过程和对应的文件
----------------------------------------------------------
lib_arm.c
调用 do_bootm_linux (cmdtp, flag, argc, argv, addr, len_ptr, verify);
查看 Armlinux.c 下的 do_bootm_linux 函数
设置启动参数:
#if defined (CONFIG_SETUP_MEMORY_TAGS) || \
defined (CONFIG_CMDLINE_TAG) || \
defined (CONFIG_INITRD_TAG) || \
defined (CONFIG_SERIAL_TAG) || \
defined (CONFIG_REVISION_TAG) || \
defined (CONFIG_LCD) || \
defined (CONFIG_VFD)
setup_start_tag (bd);
#ifdef CONFIG_SERIAL_TAG
setup_serial_tag (¶ms);
#endif
#ifdef CONFIG_REVISION_TAG
setup_revision_tag (¶ms);
#endif
#ifdef CONFIG_SETUP_MEMORY_TAGS
setup_memory_tags (bd);
#endif
#ifdef CONFIG_CMDLINE_TAG
setup_commandline_tag (bd, commandline);
#endif
#ifdef CONFIG_INITRD_TAG
if (initrd_start && initrd_end)
setup_initrd_tag (bd, initrd_start, initrd_end);
#endif
#if defined (CONFIG_VFD) || defined (CONFIG_LCD)
setup_videolfb_tag ((gd_t *) gd);
#endif
setup_end_tag (bd);
#endif
启动内核
void (*theKernel)(int zero, int arch, uint params);//定义函数指针
theKernel = (void (*)(int, int, uint))ntohl(hdr->ih_ep);//获取内核入口
theKernel (0, bd->bi_arch_number, bd->bi_boot_params);//传递参数 启动内核
uboot 启动完内核就退出了,怎么传递参数呢?
1. 在某个地址(开发版:0x30000100)按照双方约定的某种格式tag保存数据。
setup_start_tag (bd); //起始标签
setup_memory_tags (bd); // 内存的块数起始地址和大小
setup_commandline_tag (bd, commandline); //传递命令行
setup_end_tag (bd); //结束标签
setup_start_tag lib_arm.c
static void setup_start_tag (bd_t *bd)
{
params = (struct tag *) bd->bi_boot_params; // 100ask24x0.c (board\100ask24x0): gd->bd->bi_boot_params = 0x30000100;
params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params); // #define tag_next(t) ((struct tag *)((u32 *)(t) + (t)->hdr.size))
}
tag结构
struct tag {
struct tag_header hdr;
union {
struct tag_core core;
struct tag_mem32 mem;
struct tag_videotext videotext;
struct tag_ramdisk ramdisk;
struct tag_initrd initrd;
struct tag_serialnr serialnr;
struct tag_revision revision;
struct tag_videolfb videolfb;
struct tag_cmdline cmdline;
/*
* Acorn specific
*/
struct tag_acorn acorn;
/*
* DC21285 specific
*/
struct tag_memclk memclk;
} u;
};
struct tag_core {
u32 flags; /* bit 0 = read-only */
u32 pagesize;
u32 rootdev;
};
struct tag_header {
u32 size;
u32 tag;
};
联合体与结构体的区别:
联合体内的成员共用同一个内存,占用内存空间为最大成员所占用的空间。同一时间联合体内部只能存放其中一个成员。
结构体每个成员都占用一定的内存空间。同一时间结构体内部可以存放多个成员。
typedef struct bd_info {
int bi_baudrate; /* serial console baudrate */
unsigned long bi_ip_addr; /* IP Address */
unsigned char bi_enetaddr[6]; /* Ethernet adress */
struct environment_s *bi_env;
ulong bi_arch_number; /* unique id for this board */
ulong bi_boot_params; /* where this board expects params */
struct /* RAM configuration */
{
ulong start;
ulong size;
} bi_dram[CONFIG_NR_DRAM_BANKS];
#ifdef CONFIG_HAS_ETH1
/* second onboard ethernet port */
unsigned char bi_enet1addr[6];
#endif
} bd_t;
关于好多地方用到的gd 全局变量 搜索 gd_t *gd
#define DECLARE_GLOBAL_DATA_PTR register volatile gd_t *gd asm ("r8")
typedef struct global_data {
bd_t *bd;
unsigned long flags;
unsigned long baudrate;
unsigned long have_console; /* serial_init() was called */
unsigned long reloc_off; /* Relocation Offset */
unsigned long env_addr; /* Address of Environment struct */
unsigned long env_valid; /* Checksum of Environment valid? */
unsigned long fb_base; /* base address of frame buffer */
#ifdef CONFIG_VFD
unsigned char vfd_type; /* display type */
#endif
#if 0
unsigned long cpu_clk; /* CPU clock in Hz! */
unsigned long bus_clk;
unsigned long ram_size; /* RAM size */
unsigned long reset_status; /* reset status register at boot */
#endif
void **jt; /* jump table */
} gd_t;
setup_start_tag 在内存中的排列
U32 u.core.rootdev = 0 0x30000110
U32 u.core.pagesize = 0 0x3000010C
U32 u.core.flags=0 0x30000108
U32 hdr.size = tag_size (tag_core) = 5 0x30000104 #define tag_size(type) ((sizeof(struct tag_header) + sizeof(struct type)) >> 2) // 右移2 等于 /4
U32 hdr.tag = ATAG_CORE=(0x54410001) 0x30000100
setup_memory_tags (bd);
获取内存块数、起始地址、大小
setup_commandline_tag (bd, commandline)
char *commandline = getenv ("bootargs");// 来自于环境变量 bootargs=noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0
----------------------------------------------------------
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
分析 cpu/arm920t/start.s 文件
第一阶段都是硬件相关的初始化以及将u-boot代码拷贝到SDRAM:
1. 设置为管理模式
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
2. 关闭看门狗
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
3. 屏蔽所有中断
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
3. SDRAM初始化
blne cpu_init_crit
4. 是设置栈
sub sp, r0, #12
5. 时钟
bl clock_init
6. 重定位
bl CopyCode2Ram
7. 清除bss段
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
8. 调用 start_armboot 函数
ldr pc, _start_armboot
cpu/arm920t/start.s 简易注释
/*
* armboot - Startup Code for ARM920 CPU-core
*
* Copyright (c) 2001 Marius Gr鰃er <mag@sysgo.de>
* Copyright (c) 2002 Alex Z黳ke <azu@sysgo.de>
* Copyright (c) 2002 Gary Jennejohn <gj@denx.de>
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <config.h>
#include <version.h>
/*
*************************************************************************
*
* Jump vector table as in table 3.1 in [1]
*
*************************************************************************
*/
.globl _start
_start: b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
.balignl 16,0xdeadbeef
/*
*************************************************************************
*
* Startup Code (reset vector)
*
* do important init only if we don't start from memory!
* relocate armboot to ram
* setup stack
* jump to second stage
*
*************************************************************************
*/
_TEXT_BASE:
.word TEXT_BASE /* EXT_BASE是代码执行的起始地址 由-Ttext $(TEXT_BASE) 传递进来 */
.globl _armboot_start
_armboot_start:
.word _start
/*
* These are defined in the board-specific linker script.
*/
.globl _bss_start
_bss_start:
.word __bss_start
.globl _bss_end
_bss_end:
.word _end
.globl FREE_RAM_END
FREE_RAM_END:
.word 0x0badc0de
.globl FREE_RAM_SIZE
FREE_RAM_SIZE:
.word 0x0badc0de
.globl PreLoadedONRAM
PreLoadedONRAM:
.word 0
#ifdef CONFIG_USE_IRQ
/* IRQ stack memory (calculated at run-time) */
.globl IRQ_STACK_START
IRQ_STACK_START:
.word 0x0badc0de
/* IRQ stack memory (calculated at run-time) */
.globl FIQ_STACK_START
FIQ_STACK_START:
.word 0x0badc0de
#endif
/*
* the actual reset code
*/
reset:
/*
* set the cpu to SVC32 mode
* 设置CPU为管理模式
*/
mrs r0,cpsr
bic r0,r0,#0x1f
orr r0,r0,#0xd3
msr cpsr,r0
/* turn off the watchdog */
/* 关闭看门狗 */
#if defined(CONFIG_S3C2400)
# define pWTCON 0x15300000
# define INTMSK 0x14400008 /* Interupt-Controller base addresses */
# define CLKDIVN 0x14800014 /* clock divisor register */
#elif defined(CONFIG_S3C2410)
# define pWTCON 0x53000000
# define INTMOD 0X4A000004
# define INTMSK 0x4A000008 /* Interupt-Controller base addresses */
# define INTSUBMSK 0x4A00001C
# define CLKDIVN 0x4C000014 /* clock divisor register */
#endif
#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
* 关闭所有中断
*/
mov r1, #0xffffffff
ldr r0, =INTMSK
str r1, [r0]
# if defined(CONFIG_S3C2410)
ldr r1, =0x3ff
ldr r0, =INTSUBMSK
str r1, [r0]
# endif
#if 0
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
#endif
#endif /* CONFIG_S3C2400 || CONFIG_S3C2410 */
/*
* we do sys-critical inits only at reboot,
* not when booting from ram!
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
adr r0, _start /* r0 <- current position of code 当前位置 */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM 链接位置 */
cmp r0, r1 /* don't reloc during debug 是否相等 */
blne cpu_init_crit /* 不相等 执行内存控制器初始化 */
#endif
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot _TEXT_BASE 0x33f80000 前面的是 uboot代码存放位置 */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area 减去 CFG_MALLOC_LEN 段 动态内存 */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo 减去 CFG_GBL_DATA_SIZE 段 全局的系统初始化参数 */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)/* 减去 中断和快速中断的栈 */
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack 设置栈 sp= r0 - 12 */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl clock_init /* 调用 c 函数初始化时钟 */
#endif
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
/* 拷贝代码到内存 */
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
beq clear_bss
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
#if 1
bl CopyCode2Ram /* r0: source, r1: dest, r2: size */
#else
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop /* r0 <r2 */
#endif
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */
/* 清除bss 段 */
clear_bss:
ldr r0, _bss_start /* find start of bss segment */
ldr r1, _bss_end /* stop here */
mov r2, #0x00000000 /* clear */
clbss_l:str r2, [r0] /* clear loop... */
add r0, r0, #4
cmp r0, r1
ble clbss_l
SetLoadFlag:
/* Set a global flag, PreLoadedONRAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* don't reloc during debug */
ldr r2, =PreLoadedONRAM
mov r3, #1
streq r3, [r2]
#if 0
/* try doing this stuff after the relocation */
ldr r0, =pWTCON
mov r1, #0x0
str r1, [r0]
/*
* mask all IRQs by setting all bits in the INTMR - default
*/
mov r1, #0xffffffff
ldr r0, =INTMR
str r1, [r0]
/* FCLK:HCLK:PCLK = 1:2:4 */
/* default FCLK is 120 MHz ! */
ldr r0, =CLKDIVN
mov r1, #3
str r1, [r0]
/* END stuff after relocation */
#endif
ldr pc, _start_armboot /* 调用start_armboot 函数 */
_start_armboot: .word start_armboot
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
cpu_init_crit:
/*
* flush v4 I/D caches
* 清空缓存
*/
mov r0, #0
mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */
mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */
/*
* disable MMU stuff and caches
* 关闭MMU 和 缓存
*/
mrc p15, 0, r0, c1, c0, 0
bic r0, r0, #0x00002300 @ clear bits 13, 9:8 (--V- --RS)
bic r0, r0, #0x00000087 @ clear bits 7, 2:0 (B--- -CAM)
orr r0, r0, #0x00000002 @ set bit 2 (A) Align
orr r0, r0, #0x00001000 @ set bit 12 (I) I-Cache
mcr p15, 0, r0, c1, c0, 0
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a lowlevel_init.S in your board directory.
*/
mov ip, lr
bl lowlevel_init // board\100ask24x0 初始化SDRAM lowlevel_init.s
mov lr, ip
mov pc, lr
#endif /* CONFIG_SKIP_LOWLEVEL_INIT */
/*
*************************************************************************
*
* Interrupt handling
*
*************************************************************************
*/
@
@ IRQ stack frame.
@
#define S_FRAME_SIZE 72
#define S_OLD_R0 68
#define S_PSR 64
#define S_PC 60
#define S_LR 56
#define S_SP 52
#define S_IP 48
#define S_FP 44
#define S_R10 40
#define S_R9 36
#define S_R8 32
#define S_R7 28
#define S_R6 24
#define S_R5 20
#define S_R4 16
#define S_R3 12
#define S_R2 8
#define S_R1 4
#define S_R0 0
#define MODE_SVC 0x13
#define I_BIT 0x80
/*
* use bad_save_user_regs for abort/prefetch/undef/swi ...
* use irq_save_user_regs / irq_restore_user_regs for IRQ/FIQ handling
*/
.macro bad_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
ldr r2, _armboot_start
sub r2, r2, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r2, r2, #(CFG_GBL_DATA_SIZE+8) @ set base 2 words into abort stack
ldmia r2, {r2 - r3} @ get pc, cpsr
add r0, sp, #S_FRAME_SIZE @ restore sp_SVC
add r5, sp, #S_SP
mov r1, lr
stmia r5, {r0 - r3} @ save sp_SVC, lr_SVC, pc, cpsr
mov r0, sp
.endm
.macro irq_save_user_regs
sub sp, sp, #S_FRAME_SIZE
stmia sp, {r0 - r12} @ Calling r0-r12
add r8, sp, #S_PC
stmdb r8, {sp, lr}^ @ Calling SP, LR
str lr, [r8, #0] @ Save calling PC
mrs r6, spsr
str r6, [r8, #4] @ Save CPSR
str r0, [r8, #8] @ Save OLD_R0
mov r0, sp
.endm
.macro irq_restore_user_regs
ldmia sp, {r0 - lr}^ @ Calling r0 - lr
mov r0, r0
ldr lr, [sp, #S_PC] @ Get PC
add sp, sp, #S_FRAME_SIZE
subs pc, lr, #4 @ return & move spsr_svc into cpsr
.endm
.macro get_bad_stack
ldr r13, _armboot_start @ setup our mode stack
sub r13, r13, #(CONFIG_STACKSIZE+CFG_MALLOC_LEN)
sub r13, r13, #(CFG_GBL_DATA_SIZE+8) @ reserved a couple spots in abort stack
str lr, [r13] @ save caller lr / spsr
mrs lr, spsr
str lr, [r13, #4]
mov r13, #MODE_SVC @ prepare SVC-Mode
@ msr spsr_c, r13
msr spsr, r13
mov lr, pc
movs pc, lr
.endm
.macro get_irq_stack @ setup IRQ stack
ldr sp, IRQ_STACK_START
.endm
.macro get_fiq_stack @ setup FIQ stack
ldr sp, FIQ_STACK_START
.endm
/*
* exception handlers
*/
.align 5
undefined_instruction:
get_bad_stack
bad_save_user_regs
bl do_undefined_instruction
.align 5
software_interrupt:
get_bad_stack
bad_save_user_regs
bl do_software_interrupt
.align 5
prefetch_abort:
get_bad_stack
bad_save_user_regs
bl do_prefetch_abort
.align 5
data_abort:
get_bad_stack
bad_save_user_regs
bl do_data_abort
.align 5
not_used:
get_bad_stack
bad_save_user_regs
bl do_not_used
@ thisway.diy, 2006.06.24
.globl Launch
.align 4
Launch:
mov r7, r0
@ diable interrupt
@ disable watch dog timer
mov r1, #0x53000000
mov r2, #0x0
str r2, [r1]
ldr r1,=INTMSK
ldr r2,=0xffffffff @ all interrupt disable
str r2,[r1]
ldr r1,=INTSUBMSK
ldr r2,=0x7ff @ all sub interrupt disable
str r2,[r1]
ldr r1, = INTMOD
mov r2, #0x0 @ set all interrupt as IRQ (not FIQ)
str r2, [r1]
@
mov ip, #0
mcr p15, 0, ip, c13, c0, 0 @ /* zero PID */
mcr p15, 0, ip, c7, c7, 0 @ /* invalidate I,D caches */
mcr p15, 0, ip, c7, c10, 4 @ /* drain write buffer */
mcr p15, 0, ip, c8, c7, 0 @ /* invalidate I,D TLBs */
mrc p15, 0, ip, c1, c0, 0 @ /* get control register */
bic ip, ip, #0x0001 @ /* disable MMU */
mcr p15, 0, ip, c1, c0, 0 @ /* write control register */
@ MMU_EnableICache
@mrc p15,0,r1,c1,c0,0
@orr r1,r1,#(1<<12)
@mcr p15,0,r1,c1,c0,0
@ clear SDRAM: the end of free mem(has wince on it now) to the end of SDRAM
ldr r3, FREE_RAM_END
ldr r4, =PHYS_SDRAM_1+PHYS_SDRAM_1_SIZE @ must clear all the memory unused to zero
mov r5, #0
ldr r1, _armboot_start
ldr r2, =On_Steppingstone
sub r2, r2, r1
mov pc, r2
On_Steppingstone:
2: stmia r3!, {r5}
cmp r3, r4
bne 2b
@ set sp = 0 on sys mode
mov sp, #0
@ add by thisway.diy 2006.06.26, switch to SVC mode
msr cpsr_c, #0xdf @ set the I-bit = 1, diable the IRQ interrupt
msr cpsr_c, #0xd3 @ set the I-bit = 1, diable the IRQ interrupt
ldr sp, =0x31ff5800
nop
nop
nop
nop
mov pc, r7 @ Jump to PhysicalAddress
nop
mov pc, lr
#ifdef CONFIG_USE_IRQ
.align 5
irq:
/* add by www.100ask.net to use IRQ for USB and DMA */
sub lr, lr, #4 @ the return address
ldr sp, IRQ_STACK_START @ the stack for irq
stmdb sp!, { r0-r12,lr } @ save registers
ldr lr, =int_return @ set the return addr
ldr pc, =IRQ_Handle @ call the isr
int_return:
ldmia sp!, { r0-r12,pc }^ @ return from interrupt
.align 5
fiq:
get_fiq_stack
/* someone ought to write a more effiction fiq_save_user_regs */
irq_save_user_regs
bl do_fiq
irq_restore_user_regs
#else
.align 5
irq:
get_bad_stack
bad_save_user_regs
bl do_irq
.align 5
fiq:
get_bad_stack
bad_save_user_regs
bl do_fiq
#endif
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
u-boot 代码结构
board : 单板相关
cpu : 芯片相关
common : 通用
lib_XX : 各种架构相关的库
net : 网络相关
drivers : 驱动相关
怎么确定这些文件的组织关系?
最简单的就是分析MAkefile文件
u-boot 配置过程分析
make 100ask24x0_config 配置做了哪些事情?
make 100ask24x0_config 实际上是执行了
./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
传递参数给./mkconfig 文件,所以我们来分析 mkconfig 文件
mkconfig 主要做了
1. 创建开发版相关的头文件链接
cd ./include
rm -f asm
ln -s asm-arm asm
rm -f asm-arm/arch
ln -s arch-s3c24x0 asm-arm/arch
rm -f asm-arm/proc
ln -s proc-armv asm-arm/proc
2. 创建 include/config.mk 文件,保存一些参数,供顶层 Makefile 包含
ARCH = arm
CPU = arm920t
BOARD = 100ask24x0
SOC = s3c24x0
3. 创建开发版相关的头文件 include/config.h
/* Automatically generated - do not edit */
#include <configs/100ask24x0.h>
所以如果要在board目录下创建一个开发板<board_name>的目录,则在include/config 目录下也要建立一个<board_name>.h头文件,里面存放<board_name>的配置信息
mkconfig 分析及注释
#!/bin/sh -e
# Script to create header files and links to configure
# U-Boot for a specific board.
#
# Parameters: Target Architecture CPU Board [VENDOR] [SOC]
#
# (C) 2002-2006 DENX Software Engineering, Wolfgang Denk <wd@denx.de>
#
APPEND=no # Default: Create new config file
BOARD_NAME="" # Name to print in make output
# ./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
# $0 $1 $2 $3 $4 $5 $6
# 判断传入的参数是否包含 -- -a -n *
# 没有传入,所以跳过
while [ $# -gt 0 ] ; do
case "$1" in
--) shift ; break ;;
-a) shift ; APPEND=yes ;;
-n) shift ; BOARD_NAME="${1%%_config}" ; shift ;;
*) break ;;
esac
done
# BOARD_NAME 没有定义,所以 BOARD_NAME = $1 = 100ask24x0
[ "${BOARD_NAME}" ] || BOARD_NAME="$1"
# $# 表示参数的个数
[ $# -lt 4 ] && exit 1 # -lt 小于 4 退出
[ $# -gt 6 ] && exit 1 # -gt 大于 6 退出
# 打印信息
echo "Configuring for ${BOARD_NAME} board..."
#
# Create link to architecture specific headers
# 创建与特定体系结构的连接头文件
# "$SRCTREE" != "$OBJTREE" 执行 else 代码
if [ "$SRCTREE" != "$OBJTREE" ] ; then
mkdir -p ${OBJTREE}/include
mkdir -p ${OBJTREE}/include2
cd ${OBJTREE}/include2
rm -f asm
ln -s ${SRCTREE}/include/asm-$2 asm
LNPREFIX="../../include2/asm/"
cd ../include
rm -rf asm-$2
rm -f asm
mkdir asm-$2
ln -s asm-$2 asm
else
cd ./include
rm -f asm
ln -s asm-$2 asm # ln -s asm-arm asm 创建一个asm链接文件夹 指向 asm-arm架构 ,还有其他的架构 asm-i386 asm-avr32 等
fi
rm -f asm-$2/arch # rm -f asm-arm/arch
# 如果 $6 为空(-zero) 或者(-or) $6 等于字符串"NULL" 不成立 执行 else
if [ -z "$6" -o "$6" = "NULL" ] ; then
ln -s ${LNPREFIX}arch-$3 asm-$2/arch
else
ln -s ${LNPREFIX}arch-$6 asm-$2/arch
# LNPREFIX 没有定义 ln -s arch-s3c24x0 asm-arm/arch
# asm-arm/arch -> arch-s3c24x0 软连接
# 查看软连接 ls -l asm-arm/arch
# 结果 asm-arm/arch -> arch-s3c24x0
fi
# "$2" = "arm" 条件成立
if [ "$2" = "arm" ] ; then
rm -f asm-$2/proc # 删除 rm -f asm-arm/proc
ln -s ${LNPREFIX}proc-armv asm-$2/proc
# ln -s proc-armv asm-arm/proc
# asm-arm/proc -> proc-armv
fi
#
# Create include file for Make
# 创建适用于make的头文件
#
# > : 表示创建或覆盖一个文件 >> : 表示追加
# ./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
# $0 $1 $2 $3 $4 $5 $6
# 执行 cat config.mk 查看内容
echo "ARCH = $2" > config.mk
echo "CPU = $3" >> config.mk
echo "BOARD = $4" >> config.mk
[ "$5" ] && [ "$5" != "NULL" ] && echo "VENDOR = $5" >> config.mk
[ "$6" ] && [ "$6" != "NULL" ] && echo "SOC = $6" >> config.mk
#
# Create board specific header file
# 创建一个单板相关的头文件
# "$APPEND" = "no" 不成立
#
if [ "$APPEND" = "yes" ] # Append to existing config file
then
echo >> config.h
else
> config.h # Create new config file 创建新的配置文件
fi
echo "/* Automatically generated - do not edit */" >>config.h # 提示信息
echo "#include <configs/$1.h>" >>config.h
# "#include <configs/100ask24x0.h>"
exit 0
分析编译过程
# 这一步感觉展开太麻烦,可以查看 查看 make 的编译输出信息
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
----------------------------------------------------
UNDEF_SYM=`arm-linux-objdump -x lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd /work/test2/u-boot-1.1.6
&& arm-linux-ld -Bstatic -T /work/test2/u-boot-1.1.6/board/100ask24x0/u-boot.lds -Ttext 0x33F80000 $UNDEF_SYM cpu/arm920t/start.o \
--start-group lib_generic/libgeneric.a board/100ask24x0/lib100ask24x0.a cpu/arm920t/libarm920t.a cpu/arm920t/s3c24x0/libs3c24x0.a lib_arm/libarm.a fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a net/libnet.a disk/libdisk.a rtc/librtc.a dtt/libdtt.a drivers/libdrivers.a drivers/nand/libnand.a drivers/nand_legacy/libnand_legacy.a drivers/usb/libusb.a drivers/sk98lin/libsk98lin.a common/libcommon.a --end-group -L /work/tools/gcc-3.4.5-glibc-2.3.6/lib/gcc/arm-linux/3.4.5 -lgcc \
-Map u-boot.map -o u-boot
----------------------------------------------------
根据Makefile获得的信息
1. 第一个文件是:cpu/arm920t/start.o 以后可以根据这个文件来分析u-boot 执行过程
2. 链接脚本 board/100ask24x0/u-boot.lds
3. 链接地址 0x33F80000 64M SDRAM 0x30000000 ~ 0x34000000 uboot 是放置在SDRAM的最后512k位置
查找命令
grep "33F80000" * -nR
board/100ask24x0/config.mk:25:TEXT_BASE = 0x33F80000
grep "LDFLAGS" * -nR
config.mk:189:LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)
Makefile 分析注释
#
# (C) Copyright 2000-2006
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# See file CREDITS for list of people who contributed to this
# project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundatio; either version 2 of
# the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307 USA
#
VERSION = 1
PATCHLEVEL = 1
SUBLEVEL = 6
EXTRAVERSION =
U_BOOT_VERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
VERSION_FILE = $(obj)include/version_autogenerated.h
HOSTARCH := $(shell uname -m | \
sed -e s/i.86/i386/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ \
-e s/sa110/arm/ \
-e s/powerpc/ppc/ \
-e s/macppc/ppc/)
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]' | \
sed -e 's/\(cygwin\).*/cygwin/')
export HOSTARCH HOSTOS
# Deal with colliding definitions from tcsh etc.
VENDOR=
#########################################################################
#
# U-boot build supports producing a object files to the separate external
# directory. Two use cases are supported:
#
# 1) Add O= to the make command line
# 'make O=/tmp/build all'
#
# 2) Set environement variable BUILD_DIR to point to the desired location
# 'export BUILD_DIR=/tmp/build'
# 'make'
#
# The second approach can also be used with a MAKEALL script
# 'export BUILD_DIR=/tmp/build'
# './MAKEALL'
#
# Command line 'O=' setting overrides BUILD_DIR environent variable.
#
# When none of the above methods is used the local build is performed and
# the object files are placed in the source directory.
#
ifdef O
ifeq ("$(origin O)", "command line")
BUILD_DIR := $(O)
endif
endif
ifneq ($(BUILD_DIR),)
saved-output := $(BUILD_DIR)
# Attempt to create a output directory.
$(shell [ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR})
# Verify if it was successful.
BUILD_DIR := $(shell cd $(BUILD_DIR) && /bin/pwd)
$(if $(BUILD_DIR),,$(error output directory "$(saved-output)" does not exist))
endif # ifneq ($(BUILD_DIR),)
# 如果定义了 BUILD_DIR 则 OBJTREE = BUILD_DIR 否则 OBJTREE = CURDIR
OBJTREE := $(if $(BUILD_DIR),$(BUILD_DIR),$(CURDIR))
SRCTREE := $(CURDIR)
TOPDIR := $(SRCTREE)
LNDIR := $(OBJTREE)
export TOPDIR SRCTREE OBJTREE
MKCONFIG := $(SRCTREE)/mkconfig
export MKCONFIG
ifneq ($(OBJTREE),$(SRCTREE))
REMOTE_BUILD := 1
export REMOTE_BUILD
endif
# $(obj) and (src) are defined in config.mk but here in main Makefile
# we also need them before config.mk is included which is the case for
# some targets like unconfig, clean, clobber, distclean, etc.
ifneq ($(OBJTREE),$(SRCTREE))
obj := $(OBJTREE)/
src := $(SRCTREE)/
else
obj :=
src :=
endif
export obj src
#########################################################################
ifeq ($(OBJTREE)/include/config.mk,$(wildcard $(OBJTREE)/include/config.mk))
# load ARCH, BOARD, and CPU configuration
# 包含配置过程生成的 config.mk
# ARCH = arm
# CPU = arm920t
# BOARD = 100ask24x0
# SOC = s3c24x0
include $(OBJTREE)/include/config.mk
export ARCH CPU BOARD VENDOR SOC
ifndef CROSS_COMPILE
ifeq ($(HOSTARCH),ppc)
CROSS_COMPILE =
else
ifeq ($(ARCH),ppc)
CROSS_COMPILE = powerpc-linux-
endif
# 如果是 arm 架构,则使用 arm-linux- 工具链
ifeq ($(ARCH),arm)
CROSS_COMPILE = arm-linux-
endif
ifeq ($(ARCH),i386)
ifeq ($(HOSTARCH),i386)
CROSS_COMPILE =
else
CROSS_COMPILE = i386-linux-
endif
endif
ifeq ($(ARCH),mips)
CROSS_COMPILE = mips_4KC-
endif
ifeq ($(ARCH),nios)
CROSS_COMPILE = nios-elf-
endif
ifeq ($(ARCH),nios2)
CROSS_COMPILE = nios2-elf-
endif
ifeq ($(ARCH),m68k)
CROSS_COMPILE = m68k-elf-
endif
ifeq ($(ARCH),microblaze)
CROSS_COMPILE = mb-
endif
ifeq ($(ARCH),blackfin)
CROSS_COMPILE = bfin-elf-
endif
ifeq ($(ARCH),avr32)
CROSS_COMPILE = avr32-
endif
endif
endif
export CROSS_COMPILE
# load other configuration
include $(TOPDIR)/config.mk
#########################################################################
# U-Boot objects....order is important (i.e. start must be first)
# U-Boot 项目代码排序很重要 例如 start 必须排在第一个
# OBJS = cpu/arm920t/start.o
OBJS = cpu/$(CPU)/start.o
ifeq ($(CPU),i386)
OBJS += cpu/$(CPU)/start16.o
OBJS += cpu/$(CPU)/reset.o
endif
ifeq ($(CPU),ppc4xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc83xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc85xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),mpc86xx)
OBJS += cpu/$(CPU)/resetvec.o
endif
ifeq ($(CPU),bf533)
OBJS += cpu/$(CPU)/start1.o cpu/$(CPU)/interrupt.o cpu/$(CPU)/cache.o
OBJS += cpu/$(CPU)/cplbhdlr.o cpu/$(CPU)/cplbmgr.o cpu/$(CPU)/flush.o
endif
OBJS := $(addprefix $(obj),$(OBJS))
LIBS = lib_generic/libgeneric.a
# LIBS += board/100ask24x0/lib100ask24x0.a
LIBS += board/$(BOARDDIR)/lib$(BOARD).a
# LIBS += cpu/arm920t/libarm920t.a
LIBS += cpu/$(CPU)/lib$(CPU).a
ifdef SOC
# LIBS += cpu/arm920t/s3c24x0/libs3c24x0.a
LIBS += cpu/$(CPU)/$(SOC)/lib$(SOC).a
endif
# LIBS += lib_arm/libarm.a
LIBS += lib_$(ARCH)/lib$(ARCH).a
LIBS += fs/cramfs/libcramfs.a fs/fat/libfat.a fs/fdos/libfdos.a fs/jffs2/libjffs2.a \
fs/reiserfs/libreiserfs.a fs/ext2/libext2fs.a
LIBS += net/libnet.a
LIBS += disk/libdisk.a
LIBS += rtc/librtc.a
LIBS += dtt/libdtt.a
LIBS += drivers/libdrivers.a
LIBS += drivers/nand/libnand.a
LIBS += drivers/nand_legacy/libnand_legacy.a
LIBS += drivers/usb/libusb.a
LIBS += drivers/sk98lin/libsk98lin.a
LIBS += common/libcommon.a
LIBS += $(BOARDLIBS)
LIBS := $(addprefix $(obj),$(LIBS))
.PHONY : $(LIBS)
# Add GCC lib
PLATFORM_LIBS += -L $(shell dirname `$(CC) $(CFLAGS) -print-libgcc-file-name`) -lgcc
# The "tools" are needed early, so put this first
# Don't include stuff already done in $(LIBS)
SUBDIRS = tools \
examples \
post \
post/cpu
.PHONY : $(SUBDIRS)
ifeq ($(CONFIG_NAND_U_BOOT),y)
NAND_SPL = nand_spl
U_BOOT_NAND = $(obj)u-boot-nand.bin
endif
__OBJS := $(subst $(obj),,$(OBJS))
__LIBS := $(subst $(obj),,$(LIBS))
#########################################################################
#########################################################################
ALL = $(obj)u-boot.srec $(obj)u-boot.bin $(obj)System.map $(U_BOOT_NAND)
# 如果不指定ALL 则编译生成所有文件 u-boot.srec u-boot.bin System.map
all: $(ALL)
$(obj)u-boot.hex: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O ihex $< $@
$(obj)u-boot.srec: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O srec $< $@
# u-boot.bin 是二进制格式的 u-boot 是 elf 格式的
$(obj)u-boot.bin: $(obj)u-boot
$(OBJCOPY) ${OBJCFLAGS} -O binary $< $@
$(obj)u-boot.img: $(obj)u-boot.bin
./tools/mkimage -A $(ARCH) -T firmware -C none \
-a $(TEXT_BASE) -e 0 \
-n $(shell sed -n -e 's/.*U_BOOT_VERSION//p' $(VERSION_FILE) | \
sed -e 's/"[ ]*$$/ for $(BOARD) board"/') \
-d $< $@
$(obj)u-boot.dis: $(obj)u-boot
$(OBJDUMP) -d $< > $@
# 这一步感觉展开太麻烦,可以查看 查看 make 的编译输出信息
$(obj)u-boot: depend version $(SUBDIRS) $(OBJS) $(LIBS) $(LDSCRIPT)
UNDEF_SYM=`$(OBJDUMP) -x $(LIBS) |sed -n -e 's/.*\(__u_boot_cmd_.*\)/-u\1/p'|sort|uniq`;\
cd $(LNDIR) && $(LD) $(LDFLAGS) $$UNDEF_SYM $(__OBJS) \
--start-group $(__LIBS) --end-group $(PLATFORM_LIBS) \
-Map u-boot.map -o u-boot
$(OBJS):
echo $(OBJS)
$(MAKE) -C cpu/$(CPU) $(if $(REMOTE_BUILD),$@,$(notdir $@))
$(LIBS):
$(MAKE) -C $(dir $(subst $(obj),,$@))
usb:
$(MAKE) -C drivers/usb
$(SUBDIRS):
$(MAKE) -C $@ all
$(NAND_SPL): version
$(MAKE) -C nand_spl/board/$(BOARDDIR) all
$(U_BOOT_NAND): $(NAND_SPL) $(obj)u-boot.bin
cat $(obj)nand_spl/u-boot-spl-16k.bin $(obj)u-boot.bin > $(obj)u-boot-nand.bin
version:
@echo -n "#define U_BOOT_VERSION \"U-Boot " > $(VERSION_FILE); \
echo -n "$(U_BOOT_VERSION)" >> $(VERSION_FILE); \
echo -n $(shell $(CONFIG_SHELL) $(TOPDIR)/tools/setlocalversion \
$(TOPDIR)) >> $(VERSION_FILE); \
echo "\"" >> $(VERSION_FILE)
gdbtools:
$(MAKE) -C tools/gdb all || exit 1
updater:
$(MAKE) -C tools/updater all || exit 1
env:
$(MAKE) -C tools/env all || exit 1
depend dep:
for dir in $(SUBDIRS) ; do $(MAKE) -C $$dir _depend ; done
tags ctags:
ctags -w -o $(OBJTREE)/ctags `find $(SUBDIRS) include \
lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
fs/cramfs fs/fat fs/fdos fs/jffs2 \
net disk rtc dtt drivers drivers/sk98lin common \
\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
etags:
etags -a -o $(OBJTREE)/etags `find $(SUBDIRS) include \
lib_generic board/$(BOARDDIR) cpu/$(CPU) lib_$(ARCH) \
fs/cramfs fs/fat fs/fdos fs/jffs2 \
net disk rtc dtt drivers drivers/sk98lin common \
\( -name CVS -prune \) -o \( -name '*.[ch]' -print \)`
$(obj)System.map: $(obj)u-boot
@$(NM) $< | \
grep -v '\(compiled\)\|\(\.o$$\)\|\( [aUw] \)\|\(\.\.ng$$\)\|\(LASH[RL]DI\)' | \
sort > $(obj)System.map
#########################################################################
else
all $(obj)u-boot.hex $(obj)u-boot.srec $(obj)u-boot.bin \
$(obj)u-boot.img $(obj)u-boot.dis $(obj)u-boot \
$(SUBDIRS) version gdbtools updater env depend \
dep tags ctags etags $(obj)System.map:
@echo "System not configured - see README" >&2
@ exit 1
endif
.PHONY : CHANGELOG
CHANGELOG:
git log --no-merges U-Boot-1_1_5.. | \
unexpand -a | sed -e 's/\s\s*$$//' > $@
#########################################################################
unconfig:
@rm -f $(obj)include/config.h $(obj)include/config.mk \
$(obj)board/*/config.tmp $(obj)board/*/*/config.tmp
#========================================================================
# PowerPC
#========================================================================
#########################################################################
## MPC5xx Systems
#########################################################################
canmb_config: unconfig
@$(MKCONFIG) -a canmb ppc mpc5xxx canmb
cmi_mpc5xx_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc5xx cmi
PATI_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc5xx pati mpl
#########################################################################
## MPC5xxx Systems
#########################################################################
aev_config: unconfig
@$(MKCONFIG) -a aev ppc mpc5xxx tqm5200
BC3450_config: unconfig
@$(MKCONFIG) -a BC3450 ppc mpc5xxx bc3450
cpci5200_config: unconfig
@$(MKCONFIG) -a cpci5200 ppc mpc5xxx cpci5200 esd
hmi1001_config: unconfig
@$(MKCONFIG) hmi1001 ppc mpc5xxx hmi1001
Lite5200_config \
Lite5200_LOWBOOT_config \
Lite5200_LOWBOOT08_config \
icecube_5200_config \
icecube_5200_LOWBOOT_config \
icecube_5200_LOWBOOT08_config \
icecube_5200_DDR_config \
icecube_5200_DDR_LOWBOOT_config \
icecube_5200_DDR_LOWBOOT08_config \
icecube_5100_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/icecube
@ >$(obj)include/config.h
@[ -z "$(findstring LOWBOOT_,$@)" ] || \
{ if [ "$(findstring DDR,$@)" ] ; \
then echo "TEXT_BASE = 0xFF800000" >$(obj)board/icecube/config.tmp ; \
else echo "TEXT_BASE = 0xFF000000" >$(obj)board/icecube/config.tmp ; \
fi ; \
echo "... with LOWBOOT configuration" ; \
}
@[ -z "$(findstring LOWBOOT08,$@)" ] || \
{ echo "TEXT_BASE = 0xFF800000" >$(obj)board/icecube/config.tmp ; \
echo "... with 8 MB flash only" ; \
echo "... with LOWBOOT configuration" ; \
}
@[ -z "$(findstring DDR,$@)" ] || \
{ echo "#define CONFIG_MPC5200_DDR" >>$(obj)include/config.h ; \
echo "... DDR memory revision" ; \
}
@[ -z "$(findstring 5200,$@)" ] || \
{ echo "#define CONFIG_MPC5200" >>$(obj)include/config.h ; \
echo "... with MPC5200 processor" ; \
}
@[ -z "$(findstring 5100,$@)" ] || \
{ echo "#define CONFIG_MGT5100" >>$(obj)include/config.h ; \
echo "... with MGT5100 processor" ; \
}
@$(MKCONFIG) -a IceCube ppc mpc5xxx icecube
v38b_config: unconfig
@./mkconfig -a V38B ppc mpc5xxx v38b
inka4x0_config: unconfig
@$(MKCONFIG) inka4x0 ppc mpc5xxx inka4x0
lite5200b_config \
lite5200b_LOWBOOT_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/icecube
@ >$(obj)include/config.h
@ echo "#define CONFIG_MPC5200_DDR" >>$(obj)include/config.h
@ echo "... DDR memory revision"
@ echo "#define CONFIG_MPC5200" >>$(obj)include/config.h
@ echo "#define CONFIG_LITE5200B" >>$(obj)include/config.h
@[ -z "$(findstring LOWBOOT_,$@)" ] || \
{ echo "TEXT_BASE = 0xFF000000" >$(obj)board/icecube/config.tmp ; \
echo "... with LOWBOOT configuration" ; \
}
@ echo "... with MPC5200B processor"
@$(MKCONFIG) -a IceCube ppc mpc5xxx icecube
mcc200_config \
mcc200_SDRAM_config \
mcc200_highboot_config \
mcc200_COM12_config \
mcc200_COM12_SDRAM_config \
mcc200_COM12_highboot_config \
mcc200_COM12_highboot_SDRAM_config \
mcc200_highboot_SDRAM_config \
prs200_config \
prs200_DDR_config \
prs200_highboot_config \
prs200_highboot_DDR_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/mcc200
@ >$(obj)include/config.h
@[ -n "$(findstring highboot,$@)" ] || \
{ echo "... with lowboot configuration" ; \
}
@[ -z "$(findstring highboot,$@)" ] || \
{ echo "TEXT_BASE = 0xFFF00000" >$(obj)board/mcc200/config.tmp ; \
echo "... with highboot configuration" ; \
}
@[ -n "$(findstring _SDRAM,$@)" ] || \
{ if [ -n "$(findstring mcc200,$@)" ]; \
then \
echo "... with DDR" ; \
else \
if [ -n "$(findstring _DDR,$@)" ];\
then \
echo "... with DDR" ; \
else \
echo "#define CONFIG_MCC200_SDRAM" >>$(obj)include/config.h ;\
echo "... with SDRAM" ; \
fi; \
fi; \
}
@[ -z "$(findstring _SDRAM,$@)" ] || \
{ echo "#define CONFIG_MCC200_SDRAM" >>$(obj)include/config.h ; \
echo "... with SDRAM" ; \
}
@[ -z "$(findstring COM12,$@)" ] || \
{ echo "#define CONFIG_CONSOLE_COM12" >>$(obj)include/config.h ; \
echo "... with console on COM12" ; \
}
@[ -z "$(findstring prs200,$@)" ] || \
{ echo "#define CONFIG_PRS200" >>$(obj)include/config.h ;\
}
@$(MKCONFIG) -n $@ -a mcc200 ppc mpc5xxx mcc200
o2dnt_config:
@$(MKCONFIG) o2dnt ppc mpc5xxx o2dnt
pf5200_config: unconfig
@$(MKCONFIG) pf5200 ppc mpc5xxx pf5200 esd
PM520_config \
PM520_DDR_config \
PM520_ROMBOOT_config \
PM520_ROMBOOT_DDR_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring DDR,$@)" ] || \
{ echo "#define CONFIG_MPC5200_DDR" >>$(obj)include/config.h ; \
echo "... DDR memory revision" ; \
}
@[ -z "$(findstring ROMBOOT,$@)" ] || \
{ echo "#define CONFIG_BOOT_ROM" >>$(obj)include/config.h ; \
echo "... booting from 8-bit flash" ; \
}
@$(MKCONFIG) -a PM520 ppc mpc5xxx pm520
smmaco4_config: unconfig
@$(MKCONFIG) -a smmaco4 ppc mpc5xxx tqm5200
spieval_config: unconfig
@$(MKCONFIG) -a spieval ppc mpc5xxx tqm5200
TB5200_B_config \
TB5200_config: unconfig
@mkdir -p $(obj)include
@[ -z "$(findstring _B,$@)" ] || \
{ echo "#define CONFIG_TQM5200_B" >>$(obj)include/config.h ; \
echo "... with MPC5200B processor" ; \
}
@$(MKCONFIG) -n $@ -a TB5200 ppc mpc5xxx tqm5200
MINI5200_config \
EVAL5200_config \
TOP5200_config: unconfig
@mkdir -p $(obj)include
@ echo "#define CONFIG_$(@:_config=) 1" >$(obj)include/config.h
@$(MKCONFIG) -n $@ -a TOP5200 ppc mpc5xxx top5200 emk
Total5100_config \
Total5200_config \
Total5200_lowboot_config \
Total5200_Rev2_config \
Total5200_Rev2_lowboot_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/total5200
@ >$(obj)include/config.h
@[ -z "$(findstring 5100,$@)" ] || \
{ echo "#define CONFIG_MGT5100" >>$(obj)include/config.h ; \
echo "... with MGT5100 processor" ; \
}
@[ -z "$(findstring 5200,$@)" ] || \
{ echo "#define CONFIG_MPC5200" >>$(obj)include/config.h ; \
echo "... with MPC5200 processor" ; \
}
@[ -n "$(findstring Rev,$@)" ] || \
{ echo "#define CONFIG_TOTAL5200_REV 1" >>$(obj)include/config.h ; \
echo "... revision 1 board" ; \
}
@[ -z "$(findstring Rev2_,$@)" ] || \
{ echo "#define CONFIG_TOTAL5200_REV 2" >>$(obj)include/config.h ; \
echo "... revision 2 board" ; \
}
@[ -z "$(findstring lowboot_,$@)" ] || \
{ echo "TEXT_BASE = 0xFE000000" >$(obj)board/total5200/config.tmp ; \
echo "... with lowboot configuration" ; \
}
@$(MKCONFIG) -a Total5200 ppc mpc5xxx total5200
cam5200_config \
fo300_config \
MiniFAP_config \
TQM5200S_config \
TQM5200S_HIGHBOOT_config \
TQM5200_B_config \
TQM5200_B_HIGHBOOT_config \
TQM5200_config \
TQM5200_STK100_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/tqm5200
@ >$(obj)include/config.h
@[ -z "$(findstring cam5200,$@)" ] || \
{ echo "#define CONFIG_CAM5200" >>$(obj)include/config.h ; \
echo "#define CONFIG_TQM5200S" >>$(obj)include/config.h ; \
echo "#define CONFIG_TQM5200_B" >>$(obj)include/config.h ; \
echo "... TQM5200S on Cam5200" ; \
}
@[ -z "$(findstring fo300,$@)" ] || \
{ echo "#define CONFIG_FO300" >>$(obj)include/config.h ; \
echo "... TQM5200 on FO300" ; \
}
@[ -z "$(findstring MiniFAP,$@)" ] || \
{ echo "#define CONFIG_MINIFAP" >>$(obj)include/config.h ; \
echo "... TQM5200_AC on MiniFAP" ; \
}
@[ -z "$(findstring STK100,$@)" ] || \
{ echo "#define CONFIG_STK52XX_REV100" >>$(obj)include/config.h ; \
echo "... on a STK52XX.100 base board" ; \
}
@[ -z "$(findstring TQM5200_B,$@)" ] || \
{ echo "#define CONFIG_TQM5200_B" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring TQM5200S,$@)" ] || \
{ echo "#define CONFIG_TQM5200S" >>$(obj)include/config.h ; \
echo "#define CONFIG_TQM5200_B" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring HIGHBOOT,$@)" ] || \
{ echo "TEXT_BASE = 0xFFF00000" >$(obj)board/tqm5200/config.tmp ; \
}
@$(MKCONFIG) -n $@ -a TQM5200 ppc mpc5xxx tqm5200
#########################################################################
## MPC8xx Systems
#########################################################################
Adder_config \
Adder87x_config \
AdderII_config \
: unconfig
@mkdir -p $(obj)include
$(if $(findstring AdderII,$@), \
@echo "#define CONFIG_MPC852T" > $(obj)include/config.h)
@$(MKCONFIG) -a Adder ppc mpc8xx adder
ADS860_config \
FADS823_config \
FADS850SAR_config \
MPC86xADS_config \
MPC885ADS_config \
FADS860T_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx fads
AMX860_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx amx860 westel
c2mon_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx c2mon
CCM_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx CCM siemens
cogent_mpc8xx_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx cogent
ELPT860_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx elpt860 LEOX
EP88x_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx ep88x
ESTEEM192E_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx esteem192e
ETX094_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx etx094
FLAGADM_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx flagadm
xtract_GEN860T = $(subst _SC,,$(subst _config,,$1))
GEN860T_SC_config \
GEN860T_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring _SC,$@)" ] || \
{ echo "#define CONFIG_SC" >>$(obj)include/config.h ; \
echo "With reduced H/W feature set (SC)..." ; \
}
@$(MKCONFIG) -a $(call xtract_GEN860T,$@) ppc mpc8xx gen860t
GENIETV_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx genietv
GTH_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx gth
hermes_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx hermes
HMI10_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx tqm8xx
IAD210_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx IAD210 siemens
xtract_ICU862 = $(subst _100MHz,,$(subst _config,,$1))
ICU862_100MHz_config \
ICU862_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring _100MHz,$@)" ] || \
{ echo "#define CONFIG_100MHz" >>$(obj)include/config.h ; \
echo "... with 100MHz system clock" ; \
}
@$(MKCONFIG) -a $(call xtract_ICU862,$@) ppc mpc8xx icu862
IP860_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx ip860
IVML24_256_config \
IVML24_128_config \
IVML24_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring IVML24_config,$@)" ] || \
{ echo "#define CONFIG_IVML24_16M" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring IVML24_128_config,$@)" ] || \
{ echo "#define CONFIG_IVML24_32M" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring IVML24_256_config,$@)" ] || \
{ echo "#define CONFIG_IVML24_64M" >>$(obj)include/config.h ; \
}
@$(MKCONFIG) -a IVML24 ppc mpc8xx ivm
IVMS8_256_config \
IVMS8_128_config \
IVMS8_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring IVMS8_config,$@)" ] || \
{ echo "#define CONFIG_IVMS8_16M" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring IVMS8_128_config,$@)" ] || \
{ echo "#define CONFIG_IVMS8_32M" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring IVMS8_256_config,$@)" ] || \
{ echo "#define CONFIG_IVMS8_64M" >>$(obj)include/config.h ; \
}
@$(MKCONFIG) -a IVMS8 ppc mpc8xx ivm
KUP4K_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx kup4k kup
KUP4X_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx kup4x kup
LANTEC_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx lantec
lwmon_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx lwmon
MBX_config \
MBX860T_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx mbx8xx
MHPC_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx mhpc eltec
MVS1_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx mvs1
xtract_NETVIA = $(subst _V2,,$(subst _config,,$1))
NETVIA_V2_config \
NETVIA_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring NETVIA_config,$@)" ] || \
{ echo "#define CONFIG_NETVIA_VERSION 1" >>$(obj)include/config.h ; \
echo "... Version 1" ; \
}
@[ -z "$(findstring NETVIA_V2_config,$@)" ] || \
{ echo "#define CONFIG_NETVIA_VERSION 2" >>$(obj)include/config.h ; \
echo "... Version 2" ; \
}
@$(MKCONFIG) -a $(call xtract_NETVIA,$@) ppc mpc8xx netvia
xtract_NETPHONE = $(subst _V2,,$(subst _config,,$1))
NETPHONE_V2_config \
NETPHONE_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring NETPHONE_config,$@)" ] || \
{ echo "#define CONFIG_NETPHONE_VERSION 1" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring NETPHONE_V2_config,$@)" ] || \
{ echo "#define CONFIG_NETPHONE_VERSION 2" >>$(obj)include/config.h ; \
}
@$(MKCONFIG) -a $(call xtract_NETPHONE,$@) ppc mpc8xx netphone
xtract_NETTA = $(subst _SWAPHOOK,,$(subst _6412,,$(subst _ISDN,,$(subst _config,,$1))))
NETTA_ISDN_6412_SWAPHOOK_config \
NETTA_ISDN_SWAPHOOK_config \
NETTA_6412_SWAPHOOK_config \
NETTA_SWAPHOOK_config \
NETTA_ISDN_6412_config \
NETTA_ISDN_config \
NETTA_6412_config \
NETTA_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring ISDN_,$@)" ] || \
{ echo "#define CONFIG_NETTA_ISDN 1" >>$(obj)include/config.h ; \
}
@[ -n "$(findstring ISDN_,$@)" ] || \
{ echo "#undef CONFIG_NETTA_ISDN" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring 6412_,$@)" ] || \
{ echo "#define CONFIG_NETTA_6412 1" >>$(obj)include/config.h ; \
}
@[ -n "$(findstring 6412_,$@)" ] || \
{ echo "#undef CONFIG_NETTA_6412" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring SWAPHOOK_,$@)" ] || \
{ echo "#define CONFIG_NETTA_SWAPHOOK 1" >>$(obj)include/config.h ; \
}
@[ -n "$(findstring SWAPHOOK_,$@)" ] || \
{ echo "#undef CONFIG_NETTA_SWAPHOOK" >>$(obj)include/config.h ; \
}
@$(MKCONFIG) -a $(call xtract_NETTA,$@) ppc mpc8xx netta
xtract_NETTA2 = $(subst _V2,,$(subst _config,,$1))
NETTA2_V2_config \
NETTA2_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring NETTA2_config,$@)" ] || \
{ echo "#define CONFIG_NETTA2_VERSION 1" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring NETTA2_V2_config,$@)" ] || \
{ echo "#define CONFIG_NETTA2_VERSION 2" >>$(obj)include/config.h ; \
}
@$(MKCONFIG) -a $(call xtract_NETTA2,$@) ppc mpc8xx netta2
NC650_Rev1_config \
NC650_Rev2_config \
CP850_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring CP850,$@)" ] || \
{ echo "#define CONFIG_CP850 1" >>$(obj)include/config.h ; \
echo "#define CONFIG_IDS852_REV2 1" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring Rev1,$@)" ] || \
{ echo "#define CONFIG_IDS852_REV1 1" >>$(obj)include/config.h ; \
}
@[ -z "$(findstring Rev2,$@)" ] || \
{ echo "#define CONFIG_IDS852_REV2 1" >>$(obj)include/config.h ; \
}
@$(MKCONFIG) -a NC650 ppc mpc8xx nc650
NX823_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx nx823
pcu_e_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx pcu_e siemens
QS850_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx qs850 snmc
QS823_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx qs850 snmc
QS860T_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx qs860t snmc
quantum_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx quantum
R360MPI_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx r360mpi
RBC823_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx rbc823
RPXClassic_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx RPXClassic
RPXlite_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx RPXlite
RPXlite_DW_64_config \
RPXlite_DW_LCD_config \
RPXlite_DW_64_LCD_config \
RPXlite_DW_NVRAM_config \
RPXlite_DW_NVRAM_64_config \
RPXlite_DW_NVRAM_LCD_config \
RPXlite_DW_NVRAM_64_LCD_config \
RPXlite_DW_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring _64,$@)" ] || \
{ echo "#define RPXlite_64MHz" >>$(obj)include/config.h ; \
echo "... with 64MHz system clock ..."; \
}
@[ -z "$(findstring _LCD,$@)" ] || \
{ echo "#define CONFIG_LCD" >>$(obj)include/config.h ; \
echo "#define CONFIG_NEC_NL6448BC20" >>$(obj)include/config.h ; \
echo "... with LCD display ..."; \
}
@[ -z "$(findstring _NVRAM,$@)" ] || \
{ echo "#define CFG_ENV_IS_IN_NVRAM" >>$(obj)include/config.h ; \
echo "... with ENV in NVRAM ..."; \
}
@$(MKCONFIG) -a RPXlite_DW ppc mpc8xx RPXlite_dw
rmu_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx rmu
RRvision_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx RRvision
RRvision_LCD_config: unconfig
@mkdir -p $(obj)include
@echo "#define CONFIG_LCD" >$(obj)include/config.h
@echo "#define CONFIG_SHARP_LQ104V7DS01" >>$(obj)include/config.h
@$(MKCONFIG) -a RRvision ppc mpc8xx RRvision
SM850_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx tqm8xx
spc1920_config:
@$(MKCONFIG) $(@:_config=) ppc mpc8xx spc1920
SPD823TS_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx spd8xx
stxxtc_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx stxxtc
svm_sc8xx_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx svm_sc8xx
SXNI855T_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx sixnet
# EMK MPC8xx based modules
TOP860_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx top860 emk
# Play some tricks for configuration selection
# Only 855 and 860 boards may come with FEC
# and only 823 boards may have LCD support
xtract_8xx = $(subst _LCD,,$(subst _config,,$1))
FPS850L_config \
FPS860L_config \
NSCU_config \
TQM823L_config \
TQM823L_LCD_config \
TQM850L_config \
TQM855L_config \
TQM860L_config \
TQM862L_config \
TQM823M_config \
TQM850M_config \
TQM855M_config \
TQM860M_config \
TQM862M_config \
TQM866M_config \
TQM885D_config \
virtlab2_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring _LCD,$@)" ] || \
{ echo "#define CONFIG_LCD" >>$(obj)include/config.h ; \
echo "#define CONFIG_NEC_NL6448BC20" >>$(obj)include/config.h ; \
echo "... with LCD display" ; \
}
@$(MKCONFIG) -a $(call xtract_8xx,$@) ppc mpc8xx tqm8xx
TTTech_config: unconfig
@mkdir -p $(obj)include
@echo "#define CONFIG_LCD" >$(obj)include/config.h
@echo "#define CONFIG_SHARP_LQ104V7DS01" >>$(obj)include/config.h
@$(MKCONFIG) -a TQM823L ppc mpc8xx tqm8xx
uc100_config : unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8xx uc100
v37_config: unconfig
@mkdir -p $(obj)include
@echo "#define CONFIG_LCD" >$(obj)include/config.h
@echo "#define CONFIG_SHARP_LQ084V1DG21" >>$(obj)include/config.h
@$(MKCONFIG) $(@:_config=) ppc mpc8xx v37
wtk_config: unconfig
@mkdir -p $(obj)include
@echo "#define CONFIG_LCD" >$(obj)include/config.h
@echo "#define CONFIG_SHARP_LQ065T9DR51U" >>$(obj)include/config.h
@$(MKCONFIG) -a TQM823L ppc mpc8xx tqm8xx
#########################################################################
## PPC4xx Systems
#########################################################################
xtract_4xx = $(subst _25,,$(subst _33,,$(subst _BA,,$(subst _ME,,$(subst _HI,,$(subst _config,,$1))))))
ADCIOP_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx adciop esd
AP1000_config:unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx ap1000 amirix
APC405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx apc405 esd
AR405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx ar405 esd
ASH405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx ash405 esd
bamboo_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx bamboo amcc
bubinga_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx bubinga amcc
CANBT_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx canbt esd
CATcenter_config \
CATcenter_25_config \
CATcenter_33_config: unconfig
@mkdir -p $(obj)include
@ echo "/* CATcenter uses PPChameleon Model ME */" > $(obj)include/config.h
@ echo "#define CONFIG_PPCHAMELEON_MODULE_MODEL 1" >> $(obj)include/config.h
@[ -z "$(findstring _25,$@)" ] || \
{ echo "#define CONFIG_PPCHAMELEON_CLK_25" >> $(obj)include/config.h ; \
echo "SysClk = 25MHz" ; \
}
@[ -z "$(findstring _33,$@)" ] || \
{ echo "#define CONFIG_PPCHAMELEON_CLK_33" >> $(obj)include/config.h ; \
echo "SysClk = 33MHz" ; \
}
@$(MKCONFIG) -a $(call xtract_4xx,$@) ppc ppc4xx PPChameleonEVB dave
CPCI2DP_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx cpci2dp esd
CPCI405_config \
CPCI4052_config \
CPCI405DT_config \
CPCI405AB_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx cpci405 esd
@echo "BOARD_REVISION = $(@:_config=)" >> $(obj)include/config.mk
CPCI440_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx cpci440 esd
CPCIISER4_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx cpciiser4 esd
CRAYL1_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx L1 cray
csb272_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx csb272
csb472_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx csb472
DASA_SIM_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx dasa_sim esd
DP405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx dp405 esd
DU405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx du405 esd
ebony_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx ebony amcc
ERIC_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx eric
EXBITGEN_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx exbitgen
G2000_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx g2000
HH405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx hh405 esd
HUB405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx hub405 esd
JSE_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx jse
KAREF_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx karef sandburst
luan_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx luan amcc
METROBOX_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx metrobox sandburst
MIP405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx mip405 mpl
MIP405T_config: unconfig
@mkdir -p $(obj)include
@echo "#define CONFIG_MIP405T" >$(obj)include/config.h
@echo "Enable subset config for MIP405T"
@$(MKCONFIG) -a MIP405 ppc ppc4xx mip405 mpl
ML2_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx ml2
ml300_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx ml300 xilinx
ocotea_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx ocotea amcc
OCRTC_config \
ORSG_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx ocrtc esd
p3p440_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx p3p440 prodrive
PCI405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx pci405 esd
pcs440ep_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx pcs440ep
PIP405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx pip405 mpl
PLU405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx plu405 esd
PMC405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx pmc405 esd
PPChameleonEVB_config \
PPChameleonEVB_BA_25_config \
PPChameleonEVB_ME_25_config \
PPChameleonEVB_HI_25_config \
PPChameleonEVB_BA_33_config \
PPChameleonEVB_ME_33_config \
PPChameleonEVB_HI_33_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring EVB_BA,$@)" ] || \
{ echo "#define CONFIG_PPCHAMELEON_MODULE_MODEL 0" >>$(obj)include/config.h ; \
echo "... BASIC model" ; \
}
@[ -z "$(findstring EVB_ME,$@)" ] || \
{ echo "#define CONFIG_PPCHAMELEON_MODULE_MODEL 1" >>$(obj)include/config.h ; \
echo "... MEDIUM model" ; \
}
@[ -z "$(findstring EVB_HI,$@)" ] || \
{ echo "#define CONFIG_PPCHAMELEON_MODULE_MODEL 2" >>$(obj)include/config.h ; \
echo "... HIGH-END model" ; \
}
@[ -z "$(findstring _25,$@)" ] || \
{ echo "#define CONFIG_PPCHAMELEON_CLK_25" >>$(obj)include/config.h ; \
echo "SysClk = 25MHz" ; \
}
@[ -z "$(findstring _33,$@)" ] || \
{ echo "#define CONFIG_PPCHAMELEON_CLK_33" >>$(obj)include/config.h ; \
echo "SysClk = 33MHz" ; \
}
@$(MKCONFIG) -a $(call xtract_4xx,$@) ppc ppc4xx PPChameleonEVB dave
rainier_config: unconfig
@mkdir -p $(obj)include
@echo "#define CONFIG_RAINIER" > $(obj)include/config.h
@echo "Configuring for rainier board as subset of sequoia..."
@$(MKCONFIG) -a sequoia ppc ppc4xx sequoia amcc
rainier_nand_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)nand_spl
@mkdir -p $(obj)board/amcc/sequoia
@echo "#define CONFIG_RAINIER" > $(obj)include/config.h
@echo "Configuring for rainier board as subset of sequoia..."
@echo "#define CONFIG_NAND_U_BOOT" >> $(obj)include/config.h
@echo "Compile NAND boot image for sequoia"
@$(MKCONFIG) -a sequoia ppc ppc4xx sequoia amcc
@echo "TEXT_BASE = 0x01000000" > $(obj)board/amcc/sequoia/config.tmp
@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
sbc405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx sbc405
sequoia_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx sequoia amcc
sequoia_nand_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)nand_spl
@mkdir -p $(obj)board/amcc/sequoia
@echo "#define CONFIG_NAND_U_BOOT" > $(obj)include/config.h
@echo "Compile NAND boot image for sequoia"
@$(MKCONFIG) -a sequoia ppc ppc4xx sequoia amcc
@echo "TEXT_BASE = 0x01000000" > $(obj)board/amcc/sequoia/config.tmp
@echo "CONFIG_NAND_U_BOOT = y" >> $(obj)include/config.mk
sycamore_config: unconfig
@echo "Configuring for sycamore board as subset of walnut..."
@$(MKCONFIG) -a walnut ppc ppc4xx walnut amcc
VOH405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx voh405 esd
VOM405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx vom405 esd
CMS700_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx cms700 esd
W7OLMC_config \
W7OLMG_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx w7o
walnut_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx walnut amcc
WUH405_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx wuh405 esd
XPEDITE1K_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx xpedite1k
yosemite_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx yosemite amcc
yellowstone_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx yellowstone amcc
yucca_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc ppc4xx yucca amcc
#########################################################################
## MPC8220 Systems
#########################################################################
Alaska8220_config \
Yukon8220_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8220 alaska
sorcery_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8220 sorcery
#########################################################################
## MPC824x Systems
#########################################################################
xtract_82xx = $(subst _BIGFLASH,,$(subst _ROMBOOT,,$(subst _L2,,$(subst _266MHz,,$(subst _300MHz,,$(subst _config,,$1))))))
A3000_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x a3000
barco_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x barco
BMW_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x bmw
CPC45_config \
CPC45_ROMBOOT_config: unconfig
@$(MKCONFIG) $(call xtract_82xx,$@) ppc mpc824x cpc45
@cd $(obj)include ; \
if [ "$(findstring _ROMBOOT_,$@)" ] ; then \
echo "CONFIG_BOOT_ROM = y" >> config.mk ; \
echo "... booting from 8-bit flash" ; \
else \
echo "CONFIG_BOOT_ROM = n" >> config.mk ; \
echo "... booting from 64-bit flash" ; \
fi; \
echo "export CONFIG_BOOT_ROM" >> config.mk;
CU824_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x cu824
debris_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x debris etin
eXalion_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x eXalion
HIDDEN_DRAGON_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x hidden_dragon
kvme080_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x kvme080 etin
MOUSSE_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x mousse
MUSENKI_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x musenki
MVBLUE_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x mvblue
OXC_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x oxc
PN62_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x pn62
Sandpoint8240_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x sandpoint
Sandpoint8245_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x sandpoint
sbc8240_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x sbc8240
SL8245_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x sl8245
utx8245_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc824x utx8245
#########################################################################
## MPC8260 Systems
#########################################################################
atc_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 atc
cogent_mpc8260_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 cogent
CPU86_config \
CPU86_ROMBOOT_config: unconfig
@$(MKCONFIG) $(call xtract_82xx,$@) ppc mpc8260 cpu86
@cd $(obj)include ; \
if [ "$(findstring _ROMBOOT_,$@)" ] ; then \
echo "CONFIG_BOOT_ROM = y" >> config.mk ; \
echo "... booting from 8-bit flash" ; \
else \
echo "CONFIG_BOOT_ROM = n" >> config.mk ; \
echo "... booting from 64-bit flash" ; \
fi; \
echo "export CONFIG_BOOT_ROM" >> config.mk;
CPU87_config \
CPU87_ROMBOOT_config: unconfig
@$(MKCONFIG) $(call xtract_82xx,$@) ppc mpc8260 cpu87
@cd $(obj)include ; \
if [ "$(findstring _ROMBOOT_,$@)" ] ; then \
echo "CONFIG_BOOT_ROM = y" >> config.mk ; \
echo "... booting from 8-bit flash" ; \
else \
echo "CONFIG_BOOT_ROM = n" >> config.mk ; \
echo "... booting from 64-bit flash" ; \
fi; \
echo "export CONFIG_BOOT_ROM" >> config.mk;
ep8248_config \
ep8248E_config : unconfig
@$(MKCONFIG) ep8248 ppc mpc8260 ep8248
ep8260_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 ep8260
ep82xxm_config: unconfig
@./mkconfig $(@:_config=) ppc mpc8260 ep82xxm
gw8260_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 gw8260
hymod_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 hymod
IDS8247_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 ids8247
IPHASE4539_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 iphase4539
ISPAN_config \
ISPAN_REVB_config: unconfig
@mkdir -p $(obj)include
@if [ "$(findstring _REVB_,$@)" ] ; then \
echo "#define CFG_REV_B" > $(obj)include/config.h ; \
fi
@$(MKCONFIG) -a ISPAN ppc mpc8260 ispan
MPC8260ADS_config \
MPC8260ADS_lowboot_config \
MPC8260ADS_33MHz_config \
MPC8260ADS_33MHz_lowboot_config \
MPC8260ADS_40MHz_config \
MPC8260ADS_40MHz_lowboot_config \
MPC8272ADS_config \
MPC8272ADS_lowboot_config \
PQ2FADS_config \
PQ2FADS_lowboot_config \
PQ2FADS-VR_config \
PQ2FADS-VR_lowboot_config \
PQ2FADS-ZU_config \
PQ2FADS-ZU_lowboot_config \
PQ2FADS-ZU_66MHz_config \
PQ2FADS-ZU_66MHz_lowboot_config \
: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/mpc8260ads
$(if $(findstring PQ2FADS,$@), \
@echo "#define CONFIG_ADSTYPE CFG_PQ2FADS" > $(obj)include/config.h, \
@echo "#define CONFIG_ADSTYPE CFG_"$(subst MPC,,$(word 1,$(subst _, ,$@))) > $(obj)include/config.h)
$(if $(findstring MHz,$@), \
@echo "#define CONFIG_8260_CLKIN" $(subst MHz,,$(word 2,$(subst _, ,$@)))"000000" >> $(obj)include/config.h, \
$(if $(findstring VR,$@), \
@echo "#define CONFIG_8260_CLKIN 66000000" >> $(obj)include/config.h))
@[ -z "$(findstring lowboot_,$@)" ] || \
{ echo "TEXT_BASE = 0xFF800000" >$(obj)board/mpc8260ads/config.tmp ; \
echo "... with lowboot configuration" ; \
}
@$(MKCONFIG) -a MPC8260ADS ppc mpc8260 mpc8260ads
MPC8266ADS_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 mpc8266ads
# PM825/PM826 default configuration: small (= 8 MB) Flash / boot from 64-bit flash
PM825_config \
PM825_ROMBOOT_config \
PM825_BIGFLASH_config \
PM825_ROMBOOT_BIGFLASH_config \
PM826_config \
PM826_ROMBOOT_config \
PM826_BIGFLASH_config \
PM826_ROMBOOT_BIGFLASH_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/pm826
@if [ "$(findstring PM825_,$@)" ] ; then \
echo "#define CONFIG_PCI" >$(obj)include/config.h ; \
else \
>$(obj)include/config.h ; \
fi
@if [ "$(findstring _ROMBOOT_,$@)" ] ; then \
echo "... booting from 8-bit flash" ; \
echo "#define CONFIG_BOOT_ROM" >>$(obj)include/config.h ; \
echo "TEXT_BASE = 0xFF800000" >$(obj)board/pm826/config.tmp ; \
if [ "$(findstring _BIGFLASH_,$@)" ] ; then \
echo "... with 32 MB Flash" ; \
echo "#define CONFIG_FLASH_32MB" >>$(obj)include/config.h ; \
fi; \
else \
echo "... booting from 64-bit flash" ; \
if [ "$(findstring _BIGFLASH_,$@)" ] ; then \
echo "... with 32 MB Flash" ; \
echo "#define CONFIG_FLASH_32MB" >>$(obj)include/config.h ; \
echo "TEXT_BASE = 0x40000000" >$(obj)board/pm826/config.tmp ; \
else \
echo "TEXT_BASE = 0xFF000000" >$(obj)board/pm826/config.tmp ; \
fi; \
fi
@$(MKCONFIG) -a PM826 ppc mpc8260 pm826
PM828_config \
PM828_PCI_config \
PM828_ROMBOOT_config \
PM828_ROMBOOT_PCI_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/pm826
@if [ "$(findstring _PCI_,$@)" ] ; then \
echo "#define CONFIG_PCI" >>$(obj)include/config.h ; \
echo "... with PCI enabled" ; \
else \
>$(obj)include/config.h ; \
fi
@if [ "$(findstring _ROMBOOT_,$@)" ] ; then \
echo "... booting from 8-bit flash" ; \
echo "#define CONFIG_BOOT_ROM" >>$(obj)include/config.h ; \
echo "TEXT_BASE = 0xFF800000" >$(obj)board/pm826/config.tmp ; \
fi
@$(MKCONFIG) -a PM828 ppc mpc8260 pm828
ppmc8260_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 ppmc8260
Rattler8248_config \
Rattler_config: unconfig
@mkdir -p $(obj)include
$(if $(findstring 8248,$@), \
@echo "#define CONFIG_MPC8248" > $(obj)include/config.h)
@$(MKCONFIG) -a Rattler ppc mpc8260 rattler
RPXsuper_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 rpxsuper
rsdproto_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 rsdproto
sacsng_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 sacsng
sbc8260_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 sbc8260
SCM_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 SCM siemens
TQM8255_AA_config \
TQM8260_AA_config \
TQM8260_AB_config \
TQM8260_AC_config \
TQM8260_AD_config \
TQM8260_AE_config \
TQM8260_AF_config \
TQM8260_AG_config \
TQM8260_AH_config \
TQM8260_AI_config \
TQM8265_AA_config: unconfig
@mkdir -p $(obj)include
@case "$@" in \
TQM8255_AA_config) CTYPE=MPC8255; CFREQ=300; CACHE=no; BMODE=8260;; \
TQM8260_AA_config) CTYPE=MPC8260; CFREQ=200; CACHE=no; BMODE=8260;; \
TQM8260_AB_config) CTYPE=MPC8260; CFREQ=200; CACHE=yes; BMODE=60x;; \
TQM8260_AC_config) CTYPE=MPC8260; CFREQ=200; CACHE=yes; BMODE=60x;; \
TQM8260_AD_config) CTYPE=MPC8260; CFREQ=300; CACHE=no; BMODE=60x;; \
TQM8260_AE_config) CTYPE=MPC8260; CFREQ=266; CACHE=no; BMODE=8260;; \
TQM8260_AF_config) CTYPE=MPC8260; CFREQ=300; CACHE=no; BMODE=60x;; \
TQM8260_AG_config) CTYPE=MPC8260; CFREQ=300; CACHE=no; BMODE=8260;; \
TQM8260_AH_config) CTYPE=MPC8260; CFREQ=300; CACHE=yes; BMODE=60x;; \
TQM8260_AI_config) CTYPE=MPC8260; CFREQ=300; CACHE=no; BMODE=60x;; \
TQM8265_AA_config) CTYPE=MPC8265; CFREQ=300; CACHE=no; BMODE=60x;; \
esac; \
>$(obj)include/config.h ; \
if [ "$${CTYPE}" != "MPC8260" ] ; then \
echo "#define CONFIG_$${CTYPE}" >>$(obj)include/config.h ; \
fi; \
echo "#define CONFIG_$${CFREQ}MHz" >>$(obj)include/config.h ; \
echo "... with $${CFREQ}MHz system clock" ; \
if [ "$${CACHE}" == "yes" ] ; then \
echo "#define CONFIG_L2_CACHE" >>$(obj)include/config.h ; \
echo "... with L2 Cache support" ; \
else \
echo "#undef CONFIG_L2_CACHE" >>$(obj)include/config.h ; \
echo "... without L2 Cache support" ; \
fi; \
if [ "$${BMODE}" == "60x" ] ; then \
echo "#define CONFIG_BUSMODE_60x" >>$(obj)include/config.h ; \
echo "... with 60x Bus Mode" ; \
else \
echo "#undef CONFIG_BUSMODE_60x" >>$(obj)include/config.h ; \
echo "... without 60x Bus Mode" ; \
fi
@$(MKCONFIG) -a TQM8260 ppc mpc8260 tqm8260
VoVPN-GW_66MHz_config \
VoVPN-GW_100MHz_config: unconfig
@mkdir -p $(obj)include
@echo "#define CONFIG_CLKIN_$(word 2,$(subst _, ,$@))" > $(obj)include/config.h
@$(MKCONFIG) -a VoVPN-GW ppc mpc8260 vovpn-gw funkwerk
ZPC1900_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc8260 zpc1900
#########################################################################
## Coldfire
#########################################################################
cobra5272_config : unconfig
@$(MKCONFIG) $(@:_config=) m68k mcf52x2 cobra5272
EB+MCF-EV123_config : unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/BuS/EB+MCF-EV123
@ >$(obj)include/config.h
@echo "TEXT_BASE = 0xFFE00000"|tee $(obj)board/BuS/EB+MCF-EV123/textbase.mk
@$(MKCONFIG) EB+MCF-EV123 m68k mcf52x2 EB+MCF-EV123 BuS
EB+MCF-EV123_internal_config : unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/BuS/EB+MCF-EV123
@ >$(obj)include/config.h
@echo "TEXT_BASE = 0xF0000000"|tee $(obj)board/BuS/EB+MCF-EV123/textbase.mk
@$(MKCONFIG) EB+MCF-EV123 m68k mcf52x2 EB+MCF-EV123 BuS
M5271EVB_config : unconfig
@$(MKCONFIG) $(@:_config=) m68k mcf52x2 m5271evb
M5272C3_config : unconfig
@$(MKCONFIG) $(@:_config=) m68k mcf52x2 m5272c3
M5282EVB_config : unconfig
@$(MKCONFIG) $(@:_config=) m68k mcf52x2 m5282evb
TASREG_config : unconfig
@$(MKCONFIG) $(@:_config=) m68k mcf52x2 tasreg esd
r5200_config : unconfig
@$(MKCONFIG) $(@:_config=) m68k mcf52x2 r5200
#########################################################################
## MPC83xx Systems
#########################################################################
MPC8349ADS_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc83xx mpc8349ads
TQM834x_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc83xx tqm834x
MPC8349EMDS_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc83xx mpc8349emds
#########################################################################
## MPC85xx Systems
#########################################################################
MPC8540ADS_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc85xx mpc8540ads
MPC8540EVAL_config \
MPC8540EVAL_33_config \
MPC8540EVAL_66_config \
MPC8540EVAL_33_slave_config \
MPC8540EVAL_66_slave_config: unconfig
@mkdir -p $(obj)include
@echo "" >$(obj)include/config.h ; \
if [ "$(findstring _33_,$@)" ] ; then \
echo -n "... 33 MHz PCI" ; \
else \
echo "#define CONFIG_SYSCLK_66M" >>$(obj)include/config.h ; \
echo -n "... 66 MHz PCI" ; \
fi ; \
if [ "$(findstring _slave_,$@)" ] ; then \
echo "#define CONFIG_PCI_SLAVE" >>$(obj)include/config.h ; \
echo " slave" ; \
else \
echo " host" ; \
fi
@$(MKCONFIG) -a MPC8540EVAL ppc mpc85xx mpc8540eval
MPC8560ADS_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc85xx mpc8560ads
MPC8541CDS_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc85xx mpc8541cds cds
MPC8548CDS_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc85xx mpc8548cds cds
MPC8555CDS_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc85xx mpc8555cds cds
PM854_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc85xx pm854
PM856_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc85xx pm856
sbc8540_config \
sbc8540_33_config \
sbc8540_66_config: unconfig
@mkdir -p $(obj)include
@if [ "$(findstring _66_,$@)" ] ; then \
echo "#define CONFIG_PCI_66" >>$(obj)include/config.h ; \
echo "... 66 MHz PCI" ; \
else \
>$(obj)include/config.h ; \
echo "... 33 MHz PCI" ; \
fi
@$(MKCONFIG) -a SBC8540 ppc mpc85xx sbc8560
sbc8560_config \
sbc8560_33_config \
sbc8560_66_config: unconfig
@mkdir -p $(obj)include
@if [ "$(findstring _66_,$@)" ] ; then \
echo "#define CONFIG_PCI_66" >>$(obj)include/config.h ; \
echo "... 66 MHz PCI" ; \
else \
>$(obj)include/config.h ; \
echo "... 33 MHz PCI" ; \
fi
@$(MKCONFIG) -a sbc8560 ppc mpc85xx sbc8560
stxgp3_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc mpc85xx stxgp3
TQM8540_config \
TQM8541_config \
TQM8555_config \
TQM8560_config: unconfig
@mkdir -p $(obj)include
@CTYPE=$(subst TQM,,$(@:_config=)); \
>$(obj)include/config.h ; \
echo "... TQM"$${CTYPE}; \
echo "#define CONFIG_MPC$${CTYPE}">>$(obj)include/config.h; \
echo "#define CONFIG_TQM$${CTYPE}">>$(obj)include/config.h; \
echo "#define CONFIG_HOSTNAME tqm$${CTYPE}">>$(obj)include/config.h; \
echo "#define CONFIG_BOARDNAME \"TQM$${CTYPE}\"">>$(obj)include/config.h; \
echo "#define CFG_BOOTFILE \"bootfile=/tftpboot/tqm$${CTYPE}/uImage\0\"">>$(obj)include/config.h
@$(MKCONFIG) -a TQM85xx ppc mpc85xx tqm85xx
#########################################################################
## MPC86xx Systems
#########################################################################
MPC8641HPCN_config: unconfig
@./mkconfig $(@:_config=) ppc mpc86xx mpc8641hpcn
#########################################################################
## 74xx/7xx Systems
#########################################################################
AmigaOneG3SE_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc 74xx_7xx AmigaOneG3SE MAI
BAB7xx_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc 74xx_7xx bab7xx eltec
CPCI750_config: unconfig
@$(MKCONFIG) CPCI750 ppc 74xx_7xx cpci750 esd
DB64360_config: unconfig
@$(MKCONFIG) DB64360 ppc 74xx_7xx db64360 Marvell
DB64460_config: unconfig
@$(MKCONFIG) DB64460 ppc 74xx_7xx db64460 Marvell
ELPPC_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc 74xx_7xx elppc eltec
EVB64260_config \
EVB64260_750CX_config: unconfig
@$(MKCONFIG) EVB64260 ppc 74xx_7xx evb64260
P3G4_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc 74xx_7xx evb64260
PCIPPC2_config \
PCIPPC6_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc 74xx_7xx pcippc2
ZUMA_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc 74xx_7xx evb64260
ppmc7xx_config: unconfig
@$(MKCONFIG) $(@:_config=) ppc 74xx_7xx ppmc7xx
#========================================================================
# ARM
#========================================================================
#########################################################################
## StrongARM Systems
#########################################################################
assabet_config : unconfig
@$(MKCONFIG) $(@:_config=) arm sa1100 assabet
dnp1110_config : unconfig
@$(MKCONFIG) $(@:_config=) arm sa1100 dnp1110
gcplus_config : unconfig
@$(MKCONFIG) $(@:_config=) arm sa1100 gcplus
lart_config : unconfig
@$(MKCONFIG) $(@:_config=) arm sa1100 lart
shannon_config : unconfig
@$(MKCONFIG) $(@:_config=) arm sa1100 shannon
#########################################################################
## ARM92xT Systems
#########################################################################
xtract_trab = $(subst _bigram,,$(subst _bigflash,,$(subst _old,,$(subst _config,,$1))))
xtract_omap1610xxx = $(subst _cs0boot,,$(subst _cs3boot,,$(subst _cs_autoboot,,$(subst _config,,$1))))
xtract_omap730p2 = $(subst _cs0boot,,$(subst _cs3boot,, $(subst _config,,$1)))
at91rm9200dk_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t at91rm9200dk NULL at91rm9200
cmc_pu2_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t cmc_pu2 NULL at91rm9200
csb637_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t csb637 NULL at91rm9200
mp2usb_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t mp2usb NULL at91rm9200
########################################################################
## ARM Integrator boards - see doc/README-integrator for more info.
integratorap_config \
ap_config \
ap966_config \
ap922_config \
ap922_XA10_config \
ap7_config \
ap720t_config \
ap920t_config \
ap926ejs_config \
ap946es_config: unconfig
@board/integratorap/split_by_variant.sh $@
integratorcp_config \
cp_config \
cp920t_config \
cp926ejs_config \
cp946es_config \
cp1136_config \
cp966_config \
cp922_config \
cp922_XA10_config \
cp1026_config: unconfig
@board/integratorcp/split_by_variant.sh $@
kb9202_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t kb9202 NULL at91rm9200
lpd7a400_config \
lpd7a404_config: unconfig
@$(MKCONFIG) $(@:_config=) arm lh7a40x lpd7a40x
mx1ads_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t mx1ads NULL imx
mx1fs2_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t mx1fs2 NULL imx
netstar_32_config \
netstar_config: unconfig
@mkdir -p $(obj)include
@if [ "$(findstring _32_,$@)" ] ; then \
echo "... 32MB SDRAM" ; \
echo "#define PHYS_SDRAM_1_SIZE SZ_32M" >>$(obj)include/config.h ; \
else \
echo "... 64MB SDRAM" ; \
echo "#define PHYS_SDRAM_1_SIZE SZ_64M" >>$(obj)include/config.h ; \
fi
@$(MKCONFIG) -a netstar arm arm925t netstar
omap1510inn_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm925t omap1510inn
omap5912osk_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm926ejs omap5912osk NULL omap
omap1610inn_config \
omap1610inn_cs0boot_config \
omap1610inn_cs3boot_config \
omap1610inn_cs_autoboot_config \
omap1610h2_config \
omap1610h2_cs0boot_config \
omap1610h2_cs3boot_config \
omap1610h2_cs_autoboot_config: unconfig
@mkdir -p $(obj)include
@if [ "$(findstring _cs0boot_, $@)" ] ; then \
echo "#define CONFIG_CS0_BOOT" >> .$(obj)/include/config.h ; \
echo "... configured for CS0 boot"; \
elif [ "$(findstring _cs_autoboot_, $@)" ] ; then \
echo "#define CONFIG_CS_AUTOBOOT" >> $(obj)./include/config.h ; \
echo "... configured for CS_AUTO boot"; \
else \
echo "#define CONFIG_CS3_BOOT" >> $(obj)./include/config.h ; \
echo "... configured for CS3 boot"; \
fi;
@$(MKCONFIG) -a $(call xtract_omap1610xxx,$@) arm arm926ejs omap1610inn NULL omap
omap730p2_config \
omap730p2_cs0boot_config \
omap730p2_cs3boot_config : unconfig
@mkdir -p $(obj)include
@if [ "$(findstring _cs0boot_, $@)" ] ; then \
echo "#define CONFIG_CS0_BOOT" >> $(obj)include/config.h ; \
echo "... configured for CS0 boot"; \
else \
echo "#define CONFIG_CS3_BOOT" >> $(obj)include/config.h ; \
echo "... configured for CS3 boot"; \
fi;
@$(MKCONFIG) -a $(call xtract_omap730p2,$@) arm arm926ejs omap730p2 NULL omap
sbc2410x_config: unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t sbc2410x NULL s3c24x0
scb9328_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t scb9328 NULL imx
smdk2400_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2400 NULL s3c24x0
smdk2410_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t smdk2410 NULL s3c24x0
100ask24x0_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t 100ask24x0 NULL s3c24x0
# make 100ask24x0_config 实际执行的是
# ./mkconfig 100ask24x0 arm arm920t 100ask24x0 NULL s3c24x0
# @$(MKCONFIG) = ./mkconfig
# $(@:_config=) = 100ask24x0
#
SX1_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm925t sx1
# TRAB default configuration: 8 MB Flash, 32 MB RAM
trab_config \
trab_bigram_config \
trab_bigflash_config \
trab_old_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/trab
@ >$(obj)include/config.h
@[ -z "$(findstring _bigram,$@)" ] || \
{ echo "#define CONFIG_FLASH_8MB" >>$(obj)include/config.h ; \
echo "#define CONFIG_RAM_32MB" >>$(obj)include/config.h ; \
echo "... with 8 MB Flash, 32 MB RAM" ; \
}
@[ -z "$(findstring _bigflash,$@)" ] || \
{ echo "#define CONFIG_FLASH_16MB" >>$(obj)include/config.h ; \
echo "#define CONFIG_RAM_16MB" >>$(obj)include/config.h ; \
echo "... with 16 MB Flash, 16 MB RAM" ; \
echo "TEXT_BASE = 0x0CF40000" >$(obj)board/trab/config.tmp ; \
}
@[ -z "$(findstring _old,$@)" ] || \
{ echo "#define CONFIG_FLASH_8MB" >>$(obj)include/config.h ; \
echo "#define CONFIG_RAM_16MB" >>$(obj)include/config.h ; \
echo "... with 8 MB Flash, 16 MB RAM" ; \
echo "TEXT_BASE = 0x0CF40000" >$(obj)board/trab/config.tmp ; \
}
@$(MKCONFIG) -a $(call xtract_trab,$@) arm arm920t trab NULL s3c24x0
VCMA9_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t vcma9 mpl s3c24x0
#========================================================================
# ARM supplied Versatile development boards
#========================================================================
versatile_config \
versatileab_config \
versatilepb_config : unconfig
@board/versatile/split_by_variant.sh $@
voiceblue_smallflash_config \
voiceblue_config: unconfig
@mkdir -p $(obj)include
@mkdir -p $(obj)board/voiceblue
@if [ "$(findstring _smallflash_,$@)" ] ; then \
echo "... boot from lower flash bank" ; \
echo "#define VOICEBLUE_SMALL_FLASH" >>$(obj)include/config.h ; \
echo "VOICEBLUE_SMALL_FLASH=y" >$(obj)board/voiceblue/config.tmp ; \
else \
echo "... boot from upper flash bank" ; \
>$(obj)include/config.h ; \
echo "VOICEBLUE_SMALL_FLASH=n" >$(obj)board/voiceblue/config.tmp ; \
fi
@$(MKCONFIG) -a voiceblue arm arm925t voiceblue
cm4008_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t cm4008 NULL ks8695
cm41xx_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm920t cm41xx NULL ks8695
gth2_config : unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@echo "#define CONFIG_GTH2 1" >>$(obj)include/config.h
@$(MKCONFIG) -a gth2 mips mips gth2
#########################################################################
## S3C44B0 Systems
#########################################################################
B2_config : unconfig
@$(MKCONFIG) $(@:_config=) arm s3c44b0 B2 dave
#########################################################################
## ARM720T Systems
#########################################################################
armadillo_config: unconfig
@$(MKCONFIG) $(@:_config=) arm arm720t armadillo
ep7312_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm720t ep7312
impa7_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm720t impa7
modnet50_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm720t modnet50
evb4510_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm720t evb4510
#########################################################################
## XScale Systems
#########################################################################
adsvix_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa adsvix
cerf250_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa cerf250
cradle_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa cradle
csb226_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa csb226
delta_config :
@$(MKCONFIG) $(@:_config=) arm pxa delta
innokom_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa innokom
ixdp425_config : unconfig
@$(MKCONFIG) $(@:_config=) arm ixp ixdp425
ixdpg425_config : unconfig
@$(MKCONFIG) $(@:_config=) arm ixp ixdp425
lubbock_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa lubbock
pleb2_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa pleb2
logodl_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa logodl
pdnb3_config : unconfig
@$(MKCONFIG) $(@:_config=) arm ixp pdnb3 prodrive
pxa255_idp_config: unconfig
@$(MKCONFIG) $(@:_config=) arm pxa pxa255_idp
wepep250_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa wepep250
xaeniax_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa xaeniax
xm250_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa xm250
xsengine_config : unconfig
@$(MKCONFIG) $(@:_config=) arm pxa xsengine
zylonite_config :
@$(MKCONFIG) $(@:_config=) arm pxa zylonite
#########################################################################
## ARM1136 Systems
#########################################################################
omap2420h4_config : unconfig
@$(MKCONFIG) $(@:_config=) arm arm1136 omap2420h4
#========================================================================
# i386
#========================================================================
#########################################################################
## AMD SC520 CDP
#########################################################################
sc520_cdp_config : unconfig
@$(MKCONFIG) $(@:_config=) i386 i386 sc520_cdp
sc520_spunk_config : unconfig
@$(MKCONFIG) $(@:_config=) i386 i386 sc520_spunk
sc520_spunk_rel_config : unconfig
@$(MKCONFIG) $(@:_config=) i386 i386 sc520_spunk
#========================================================================
# MIPS
#========================================================================
#########################################################################
## MIPS32 4Kc
#########################################################################
xtract_incaip = $(subst _100MHz,,$(subst _133MHz,,$(subst _150MHz,,$(subst _config,,$1))))
incaip_100MHz_config \
incaip_133MHz_config \
incaip_150MHz_config \
incaip_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring _100MHz,$@)" ] || \
{ echo "#define CPU_CLOCK_RATE 100000000" >>$(obj)include/config.h ; \
echo "... with 100MHz system clock" ; \
}
@[ -z "$(findstring _133MHz,$@)" ] || \
{ echo "#define CPU_CLOCK_RATE 133000000" >>$(obj)include/config.h ; \
echo "... with 133MHz system clock" ; \
}
@[ -z "$(findstring _150MHz,$@)" ] || \
{ echo "#define CPU_CLOCK_RATE 150000000" >>$(obj)include/config.h ; \
echo "... with 150MHz system clock" ; \
}
@$(MKCONFIG) -a $(call xtract_incaip,$@) mips mips incaip
tb0229_config: unconfig
@$(MKCONFIG) $(@:_config=) mips mips tb0229
#########################################################################
## MIPS32 AU1X00
#########################################################################
dbau1000_config : unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@echo "#define CONFIG_DBAU1000 1" >>$(obj)include/config.h
@$(MKCONFIG) -a dbau1x00 mips mips dbau1x00
dbau1100_config : unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@echo "#define CONFIG_DBAU1100 1" >>$(obj)include/config.h
@$(MKCONFIG) -a dbau1x00 mips mips dbau1x00
dbau1500_config : unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@echo "#define CONFIG_DBAU1500 1" >>$(obj)include/config.h
@$(MKCONFIG) -a dbau1x00 mips mips dbau1x00
dbau1550_config : unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@echo "#define CONFIG_DBAU1550 1" >>$(obj)include/config.h
@$(MKCONFIG) -a dbau1x00 mips mips dbau1x00
dbau1550_el_config : unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@echo "#define CONFIG_DBAU1550 1" >>$(obj)include/config.h
@$(MKCONFIG) -a dbau1x00 mips mips dbau1x00
pb1000_config : unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@echo "#define CONFIG_PB1000 1" >>$(obj)include/config.h
@$(MKCONFIG) -a pb1x00 mips mips pb1x00
#########################################################################
## MIPS64 5Kc
#########################################################################
purple_config : unconfig
@$(MKCONFIG) $(@:_config=) mips mips purple
#========================================================================
# Nios
#========================================================================
#########################################################################
## Nios32
#########################################################################
DK1C20_safe_32_config \
DK1C20_standard_32_config \
DK1C20_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring _safe_32,$@)" ] || \
{ echo "#define CONFIG_NIOS_SAFE_32 1" >>$(obj)include/config.h ; \
echo "... NIOS 'safe_32' configuration" ; \
}
@[ -z "$(findstring _standard_32,$@)" ] || \
{ echo "#define CONFIG_NIOS_STANDARD_32 1" >>$(obj)include/config.h ; \
echo "... NIOS 'standard_32' configuration" ; \
}
@[ -z "$(findstring DK1C20_config,$@)" ] || \
{ echo "#define CONFIG_NIOS_STANDARD_32 1" >>$(obj)include/config.h ; \
echo "... NIOS 'standard_32' configuration (DEFAULT)" ; \
}
@$(MKCONFIG) -a DK1C20 nios nios dk1c20 altera
DK1S10_safe_32_config \
DK1S10_standard_32_config \
DK1S10_mtx_ldk_20_config \
DK1S10_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring _safe_32,$@)" ] || \
{ echo "#define CONFIG_NIOS_SAFE_32 1" >>$(obj)include/config.h ; \
echo "... NIOS 'safe_32' configuration" ; \
}
@[ -z "$(findstring _standard_32,$@)" ] || \
{ echo "#define CONFIG_NIOS_STANDARD_32 1" >>$(obj)include/config.h ; \
echo "... NIOS 'standard_32' configuration" ; \
}
@[ -z "$(findstring _mtx_ldk_20,$@)" ] || \
{ echo "#define CONFIG_NIOS_MTX_LDK_20 1" >>$(obj)include/config.h ; \
echo "... NIOS 'mtx_ldk_20' configuration" ; \
}
@[ -z "$(findstring DK1S10_config,$@)" ] || \
{ echo "#define CONFIG_NIOS_STANDARD_32 1" >>$(obj)include/config.h ; \
echo "... NIOS 'standard_32' configuration (DEFAULT)" ; \
}
@$(MKCONFIG) -a DK1S10 nios nios dk1s10 altera
ADNPESC1_DNPEVA2_base_32_config \
ADNPESC1_base_32_config \
ADNPESC1_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@[ -z "$(findstring _DNPEVA2,$@)" ] || \
{ echo "#define CONFIG_DNPEVA2 1" >>$(obj)include/config.h ; \
echo "... DNP/EVA2 configuration" ; \
}
@[ -z "$(findstring _base_32,$@)" ] || \
{ echo "#define CONFIG_NIOS_BASE_32 1" >>$(obj)include/config.h ; \
echo "... NIOS 'base_32' configuration" ; \
}
@[ -z "$(findstring ADNPESC1_config,$@)" ] || \
{ echo "#define CONFIG_NIOS_BASE_32 1" >>$(obj)include/config.h ; \
echo "... NIOS 'base_32' configuration (DEFAULT)" ; \
}
@$(MKCONFIG) -a ADNPESC1 nios nios adnpesc1 ssv
#########################################################################
## Nios-II
#########################################################################
EP1C20_config : unconfig
@$(MKCONFIG) EP1C20 nios2 nios2 ep1c20 altera
EP1S10_config : unconfig
@$(MKCONFIG) EP1S10 nios2 nios2 ep1s10 altera
EP1S40_config : unconfig
@$(MKCONFIG) EP1S40 nios2 nios2 ep1s40 altera
PK1C20_config : unconfig
@$(MKCONFIG) PK1C20 nios2 nios2 pk1c20 psyent
PCI5441_config : unconfig
@$(MKCONFIG) PCI5441 nios2 nios2 pci5441 psyent
#========================================================================
# MicroBlaze
#========================================================================
#########################################################################
## Microblaze
#########################################################################
suzaku_config: unconfig
@mkdir -p $(obj)include
@ >$(obj)include/config.h
@echo "#define CONFIG_SUZAKU 1" >> $(obj)include/config.h
@$(MKCONFIG) -a $(@:_config=) microblaze microblaze suzaku AtmarkTechno
#########################################################################
## Blackfin
#########################################################################
ezkit533_config : unconfig
@$(MKCONFIG) $(@:_config=) blackfin bf533 ezkit533
stamp_config : unconfig
@$(MKCONFIG) $(@:_config=) blackfin bf533 stamp
dspstamp_config : unconfig
@$(MKCONFIG) $(@:_config=) blackfin bf533 dsp_stamp
#========================================================================
# AVR32
#========================================================================
#########################################################################
## AT32AP7xxx
#########################################################################
atstk1002_config : unconfig
@./mkconfig $(@:_config=) avr32 at32ap atstk1000 atmel at32ap7000
#########################################################################
#########################################################################
#########################################################################
clean:
find $(OBJTREE) -type f \
\( -name 'core' -o -name '*.bak' -o -name '*~' \
-o -name '*.o' -o -name '*.a' \) -print \
| xargs rm -f
rm -f $(obj)examples/hello_world $(obj)examples/timer \
$(obj)examples/eepro100_eeprom $(obj)examples/sched \
$(obj)examples/mem_to_mem_idma2intr $(obj)examples/82559_eeprom \
$(obj)examples/smc91111_eeprom $(obj)examples/interrupt \
$(obj)examples/test_burst
rm -f $(obj)tools/img2srec $(obj)tools/mkimage $(obj)tools/envcrc \
$(obj)tools/gen_eth_addr
rm -f $(obj)tools/mpc86x_clk $(obj)tools/ncb
rm -f $(obj)tools/easylogo/easylogo $(obj)tools/bmp_logo
rm -f $(obj)tools/gdb/astest $(obj)tools/gdb/gdbcont $(obj)tools/gdb/gdbsend
rm -f $(obj)tools/env/fw_printenv $(obj)tools/env/fw_setenv
rm -f $(obj)board/cray/L1/bootscript.c $(obj)board/cray/L1/bootscript.image
rm -f $(obj)board/netstar/eeprom $(obj)board/netstar/crcek $(obj)board/netstar/crcit
rm -f $(obj)board/netstar/*.srec $(obj)board/netstar/*.bin
rm -f $(obj)board/trab/trab_fkt $(obj)board/voiceblue/eeprom
rm -f $(obj)board/integratorap/u-boot.lds $(obj)board/integratorcp/u-boot.lds
rm -f $(obj)include/bmp_logo.h
rm -f $(obj)nand_spl/u-boot-spl $(obj)nand_spl/u-boot-spl.map
clobber: clean
find $(OBJTREE) -type f \( -name .depend \
-o -name '*.srec' -o -name '*.bin' -o -name u-boot.img \) \
-print0 \
| xargs -0 rm -f
rm -f $(OBJS) $(obj)*.bak $(obj)ctags $(obj)etags $(obj)TAGS $(obj)include/version_autogenerated.h
rm -fr $(obj)*.*~
rm -f $(obj)u-boot $(obj)u-boot.map $(obj)u-boot.hex $(ALL)
rm -f $(obj)tools/crc32.c $(obj)tools/environment.c $(obj)tools/env/crc32.c
rm -f $(obj)tools/inca-swap-bytes $(obj)cpu/mpc824x/bedbug_603e.c
rm -f $(obj)include/asm/proc $(obj)include/asm/arch $(obj)include/asm
[ ! -d $(OBJTREE)/nand_spl ] || find $(obj)nand_spl -lname "*" -print | xargs rm -f
ifeq ($(OBJTREE),$(SRCTREE))
mrproper \
distclean: clobber unconfig
else
mrproper \
distclean: clobber unconfig
rm -rf $(OBJTREE)/*
endif
backup:
F=`basename $(TOPDIR)` ; cd .. ; \
gtar --force-local -zcvf `date "+$$F-%Y-%m-%d-%T.tar.gz"` $$F
#########################################################################
写的比较简陋,个人只是打算简单的了解下,不打算深入分析,有兴趣深入理解的小伙伴可以百度。
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
PC机的启动过程:
BIOS -> 引导操作系统windows -> 识别C/D盘 -> 运行应用程序
嵌入式系统启动过程:
bootloader -> linux内核 -> 挂接根文件系统 -> 运行应用程序
u-boot 属于 bootloader 的一种
--------------------------
u-boot 编译步骤
1. 解压缩
tar xjf u-boot-1.1.6.tar.bz2
2. 打补丁
cd u-boot-1.1.6
patch -p1 <../u-boot-1.1.6_jz2440.patch // p1 表示忽略补丁文件信息中 路径的 第一个"/"之前的内容
3. 配置
make 100ask24x0_config
4. 编译
make
u-boot 简单操作
> help // 查看所有命令
> ? 命令 // 查看命令的使用方法
> print // 查看环境变量
怎么设置环境变量?
> set bootdelay 10 // 修改启动倒计时
> save // 保存
> reset // 重启
u-boot 的最终目的是为了引导操作系统。
1. 从Flash 读出内核到 SDRAM
2. 启动内核
所以u-boot需要实现的功能:
1. 硬件初始化
a. 关闭看门狗
b. 初始化时钟
c. 初始化SDRAM
d. 初始化Flash控制器
2. 从Flash上读取内核到SDRAM
3. 启动内核
为了开发方便,我们还需要增加一些功能:
1. Flash烧写
a. 通过网络
b. 通过USB
……
2. 串口 打印/控制
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
地址映射 MMU
当N个应用APP运行时
1. 它们同时保存在SDRAM 中
2. 它们的地址各不行同
链接地址 = 程序运行时所处的地址
所以在编写某个APP时要需要单独的指定他的链接地址,对于一两个应用,我们还可以手动去指定链接地址,但是对于一个开放的操作系统,有成百上千的应用,由不同的开发者编写,我们是不可能预测和重定位所有的APP位置,所以要引入虚拟地址,让所有的APP都位于相同的虚拟地址来执行。
MMU负责虚拟地址与物理地址之间的转换
CPU -> MMU -> 地址总线 -> SDRAM/GPIO
MMU 的作用:
1. 让不同的APP以相同的链接地址进行编译和运行
2. 让大于SDRAM容量的APP可以运行,根据APP运行的局部性分段执行
3. 权限管理,APP1 只能访问APP1 对应的地址,保证应用程序不越界,及整个系统给的正常运行。
MMU 的使用:
1. 在内存中创建页表
2. 把页表基址告诉MMU
3. 设置CP15 启动 MMU
以一级也表为例
一级页表的中的每个条目对应 1M 的内存空间,这个条目称之为描述符(段描述符、粗页表描述符、细页表描述符)。
一级页表描述详见 s3c2410 P560
段描述符为例:
base address AP Domain C B
base address: 对应物理地址
AP 和 Domain: 对应权限管理
C 和 B : 对应CACHE 和 write buffer
权限管理:
a. 完全不允许访问
b. 允许系统模式访问,不允许用户模式访问
c. 用户模式下根据描述符的AP值决定怎么访问
域 的概念:
CP15 -> C3 32bit 每两位控制一个域 共 0-15 16个域
表示4中权限:
00 无访问权限 如果想让一段内存无法访问,将页表描述符中domain 设置为对应的域号 0-15 然后将对应的域设置为00
01 客户模式 使用页表描述符中的 AP + S + R 来确定权限,具体见韦老师书本7.1.3 最后一个表格。
10 保留
11 管理模式 可以访问,不进行 AP S R 权限检查
注:AP来自描述符 S R 来自 CP15 -> C1
进程切换时需要切换到对应的虚拟地址,由于系统有众多APP进程,所以这个切换开销非常大,由硬件来实现 CP15 -> C13
CPU -> 虚拟地址(VA) -> CP15 : C13 -> 变换后的虚拟地址(MVA) -> MMU -> 得到物理地址(PA)
我们常说的虚拟地址一般都是指CP15:C13进程变换后的虚拟地址 MVA
CP15:C13 的变换关系:
if(VA<32M)
MVA = VA|(PID<<25) ;
else
MVA = VA ;
解决了频繁构造页表的问题。
假如有两个进程 APP1 APP2 链接地址都是 0x80b4 (链接地址即运行时地址 0x80b4 是虚拟地址VA),PID 分别为 1 和 2;运行地址一般都小于32M。
1. 当CPU运行APP1时,发出VA 0x80b4
MVA = VA|(1<<25) 对应的页表指向 APP1所在的实际物理地址。
2. 当CPU运行APP2时,发出VA 0x80b4
MVA = VA|(2<<25) 对应的页表指向 APP2所在的实际物理地址。
这样进程1 切换到进程2 只需要切换PID即可,不需要重新创建页表,从而提升切换效率。
MMU、CACHE等看到的都是MVA(变换后的虚拟地址),而不是编译链接地址(或运行时地址) VA(链接虚拟地址),我们提到的虚拟地址默认都是MVA 变换后的虚拟地址。
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
CACHE 高速缓存
S3C2440 内部有
16k 指令缓存 16K 数据缓存
通过 CP15 协处理器来控制
程序局部性
a. 时间局部性 在程序执行时,在一段时间内有极大的概率访问同一地址的指令/数据。
b. 空间局部性 在程序执行时,在一段时间内有极大的概率访问相邻地址的指令/数据。
对于高速缓存中的数据存储方式:
地址 : 数据
地址 : 数据
地址 : 数据
地址 : 数据
地址 : 数据
对于高速缓存中的指令存储方式:
地址 : 指令
地址 : 指令
地址 : 指令
地址 : 指令
地址 : 指令
Data CACHE 与 Instruction CACHE 在读取时类似。
以 Data CACHE 为例:地址A 地址B 相邻
1. 程序要读取地址A的数据
a. cpu以地址A查找CACHE,一开始CACHE中无数据 : CACHE MISS
b. cpu把地址A发送到SDRAM,SDRAM返回的不只是地址A的数据, 而是一系列的数据 CACHE LINE = 8 WORD = 32 byte
把返回的数据读入 CACHE : CACHE FILL 并返回A地址对应的数据给 CPU
2. 程序再次读取地址A的数据
a. cpu以地址A查找CACHE,CACHE 有对应的地址,直接读取CACHE中的数据给CPU : CACHE HIT 命中
3. 程序读取地址B的数据
a. cpu以地址B查找CACHE,CACHE 有对应的地址,直接读取CACHE中的数据给CPU : CACHE HIT 命中
......
......
4. 16k CACHE满
a. 将老数据置换出去
b. 填充新数据
write buffer 写入缓冲,由于CPU运行的比较快,写外部硬件比较慢,所以 CPU 可以直接把要写的数据扔给 write buffer 然后去执行下一条指令,write buffer 去和硬件打交道,从而提高CPU运行速度。
Data CACHE 和 write buffer 可以后四种组合方式:s3c2410 585
1. NCNB 不启动 Data CACHE 和 write buffer CPU直接操作外部硬件,适用于外设寄存器操作。
2. NCB 不启动 Data CACHE 启动 write buffer , CPU把要写的数据给 write buffer ,write buffer再把数据写给硬件。
3. WT 启动 Data CACHE 不启动 write buffer , CPU把数据写给 Data CACHE ,Data CACHE 再把数据写给硬件。写通
4. WB 启动 Data CACHE 和 write buffer ,写回 分为两种情况:
miss 未命中: CPU -> write buffer -> 硬件
hit 命中: CPU -> Data CACHE(标记 dirty 脏) -> 合适的时机 -> write buffer -> 硬件
合适的时机:
a. Data CACHE 缓存满需要替换时 会把标记 dirty 的数据 -> write buffer -> 硬件
b. 强刷 flush ,CACHE -> write buffer -> 硬件
--------------------------------------------------------------------
协处理器CP15 管理CACHE 和 MMU
主CPU:r0 - r15
协处理器: c0 - c15 c7 有多个专用寄存器 类似于主CPU的专用寄存器。
协处理器操作:S3C2440 P142
<MCR|MRC>{cond} p#,<expression1>,Rd,cn,cm{,<expression2>}
mcr p15,0,r1,c1,c0,0
mrc : mov register coprocessor 将协处理器寄存器数据 写入 主CPU寄存器
p15 : CP15 协处理器
r1 : 主CPU寄存器
c1 : 协处理器寄存器
c0,0: 区分是哪一个c1 有可能一个寄存器对应多个专用寄存器
mcr p15,0,r1,c1,c0,0 将主CPU 寄存器数据写入 写协处理器 寄存器
演示:
由于 Data CACHE 只能通过地址映射后才能使用。演示 Instruction CACHE
使能 Instruction CACHE 需要 设置协处理器CP15 C1-> 12bit 置 1 启动 Instruction CACHE 。 C1 : Control Register 1 参考S3C2410数据手册 附录 2-11 描述
代码:
在start.s 中添加
-----------------------
mrc p15, 0, r0, c1, c0, 0
orr r0, r0, #(1<<12) /* r0 = r0 | (1<<12) */
mcr p15, 0, r0, c1, c0, 0
-----------------------
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
NAND Flash : K9F2G08U0C
- Memory Cell Array : (256M + 8M) x 8bit
- Data Register : (2K + 64) x 8bit
• Automatic Program and Erase
- Page Program : (2K + 64)Byte
- Block Erase : (128K + 4K)Byte
• Page Read Operation
- Page Size : (2K + 64)Byte
--------------------
NAND Flash 引脚说明 K9F2G08U0C.pdf P8 P22
CE 片选 低电平选中芯片 空闲为高电平-> 未选中
I/O 0~7 数据输入输出引脚 接CPU的数据线 LDATA0~7
R/B 就绪/忙碌状态 READY/BUSY OUTPUT 空闲为高电平->就绪
CLE 命令使能 高电平 I/O 0~7 上放置的是命令 空闲为低电平
ALE 地址使能 高电平高电平 I/O 0~7 上放置的是地址 空闲为低电平
WE 写使能 低电平有效,低到高跳边沿将I/O 0~7 上的数据锁存
RE 读使能 低电平有效,高到低跳变通知NAND放置数据,低到高跳变,通知NAND数据读取完毕
CLE 为高 数据线上放置的是命令
ALE 为高 数据线上放置的是地址
CLE、ALE 都为低 数据线上放置的是数据。
具体请看时序图
-------------------
根据NAND FLASH的芯片手册时序图,NAND Flash一般的操作过程:
发出命令
发出地址
发出数据/读数据
NAND FLASH S3C2440
发命令 选中芯片
CLE设为高电平 NFCMMD=命令值
在DATA0~DATA7上输出命令值
发出一个写脉冲
发地址 选中芯片 NFADDR=地址值
ALE设为高电平
在DATA0~DATA7上输出地址值
发出一个写脉冲
发数据 选中芯片 NFDATA=数据值
ALE,CLE设为低电平
在DATA0~DATA7上输出数据值
发出一个写脉冲
读数据 选中芯片 val=NFDATA
发出读脉冲
读DATA0~DATA7的数据
---------------------------------------------------------
s3c2440 NAND 控制器 部分寄存器
NFCONF 0x4E000000 :配置寄存器 访问时序周期
NFCONT 0x4E000004 :bit0 = 1 使能NAND 控制器 bit1 = 0 片选引脚
NFCMMD 0x4E000008 :命令寄存器 发送指令
NFADDR 0x4E00000C :地址寄存器 发送地址
NFDATA 0x4E000010 :数据寄存器 读/写数据
---------------------------------------------------------
通过u-boot体验 NAND Flash 操作
1. 读ID
S3C2440 u-boot
选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1
发出命令0x90 NFCMMD=0x90 mw.b 0x4E000008 0x90
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00
读数据得到0xEC val=NFDATA md.b 0x4E000010 1
读数据得到device code val=NFDATA md.b 0x4E000010 1
0xda
退出读ID的状态 NFCMMD=0xff mw.b 0x4E000008 0xff
2. 读内容: 读0地址的数据
使用UBOOT命令:
nand dump [addr] 最少读取1页 page
使用寄存器读取
S3C2440 u-boot
选中 NFCONT的bit1设为0 md.l 0x4E000004 1; mw.l 0x4E000004 1
发出命令0x00 NFCMMD=0x00 mw.b 0x4E000008 0x00
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 // 列地址 低8位
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 // 列地址 高8位
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 // 行地址 低8位
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 // 行地址 中间8位
发出地址0x00 NFADDR=0x00 mw.b 0x4E00000C 0x00 // 行地址 高8位
发出命令0x30 NFCMMD=0x30 mw.b 0x4E000008 0x30
读数据得到0x17 val=NFDATA md.b 0x4E000010 1
读数据得到0x00 val=NFDATA md.b 0x4E000010 1
读数据得到0x00 val=NFDATA md.b 0x4E000010 1
读数据得到0xea val=NFDATA md.b 0x4E000010 1
退出读状态 NFCMMD=0xff mw.b 0x4E000008 0xff
3. 代码
#include "s3c2440.h"
#include "my_printf.h"
void nand_init(void)
{
/*设置NAND FLASH的时序*/
NFCONF = (0<<12) | (1<<8) | (0<<4);
/*使能NAND FLASH控制器,初始化ECC,禁止片选*/
NFCONT = (1<<4) | (1<<1) | (1<<0);
}
/* 读状态 */
void read_state(void)
{
while (!(NFSTAT & 1));
}
/*使能片选*/
void nand_select(void)
{
NFCONT &=~(1<<1);
}
/*禁止片选*/
void nand_deselect(void)
{
NFCONT |= (1<<1);
}
/* 发命令 */
void nand_cmd(unsigned char cmd)
{
volatile int i;
NFCMMD = cmd;
for(i=0; i<10; i++);
//read_state();
}
/* 发送地址 */
void nand_addr_byte(unsigned char addr)
{
volatile int i;
NFADDR = addr;
for(i=0; i<10; i++);
//read_state();
}
/* 读数据 */
unsigned char nand_read_data(void)
{
return NFDATA;
}
/* 写数据 */
void nand_write_data(unsigned char val)
{
NFDATA = val;
}
void nand_chip_id(void)
{
unsigned char buf[5]={0};
nand_select();
nand_cmd(0x90);
nand_addr_byte(0x00);
buf[0] = nand_read_data();
buf[1] = nand_read_data();
buf[2] = nand_read_data();
buf[3] = nand_read_data();
buf[4] = nand_read_data();
nand_deselect();
printf("maker id = 0x%x\n\r",buf[0]);
printf("device id = 0x%x\n\r",buf[1]);
printf("3rd byte = 0x%x\n\r",buf[2]);
printf("4th byte = 0x%x\n\r",buf[3]);
printf("page size = %d kb\n\r",1 << (buf[3] & 0x03));
printf("block size = %d kb\n\r",64 << ((buf[3] >> 4) & 0x03));
printf("5th byte = 0x%x\n\r",buf[4]);
}
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int i = 0;
int page = addr / 2048; // 行/页
int col = addr & (2048 - 1); // 列地址
nand_select();
while (i < len)
{
/* 发出00h命令 */
nand_cmd(00);
/* 发出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 发出30h命令 */
nand_cmd(0x30);
/* 等待就绪 */
read_state();
/* 读数据 */
for (; (col < 2048) && (i < len); col++)
{
buf[i++] = nand_read_data();
}
if (i == len)
break;
col = 0;
page++;
}
nand_deselect();
}
int nand_erase(unsigned int addr, unsigned int len)
{
int page = addr / 2048;
if (addr & (0x1FFFF))
{
printf("nand_erase err, addr is not block align\n\r");
return -1;
}
if (len & (0x1FFFF))
{
printf("nand_erase err, len is not block align\n\r");
return -1;
}
nand_select();
while (1)
{
page = addr / 2048;
nand_cmd(0x60);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
nand_cmd(0xD0);
read_state();
len -= (128*1024);
if (len == 0)
break;
addr += (128*1024);
}
nand_deselect();
return 0;
}
void nand_write(unsigned int addr, unsigned char *buf, unsigned int len)
{
int page = addr / 2048;
int col = addr & (2048 - 1);
int i = 0;
nand_select();
while (1)
{
nand_cmd(0x80);
/* 发出地址 */
/* col addr */
nand_addr_byte(col & 0xff);
nand_addr_byte((col>>8) & 0xff);
/* row/page addr */
nand_addr_byte(page & 0xff);
nand_addr_byte((page>>8) & 0xff);
nand_addr_byte((page>>16) & 0xff);
/* 发出数据 */
for (; (col < 2048) && (i < len); )
{
nand_write_data(buf[i++]);
}
nand_cmd(0x10);
read_state();
if (i == len)
break;
else
{
/* 开始下一个循环page */
col = 0;
page++;
}
}
nand_deselect();
}
void do_read_nand_flash(void)
{
unsigned int addr;
volatile unsigned char *p;
int i, j;
unsigned char c;
unsigned char str[16];
unsigned char buf[64];
/* 获得地址 */
printf("Enter the address to read: ");
addr = get_uint();
nand_read(addr, buf, 64);
p = (volatile unsigned char *)buf;
printf("Data : \n\r");
/* 长度固定为64 */
for (i = 0; i < 4; i++)
{
/* 每行打印16个数据 */
for (j = 0; j < 16; j++)
{
/* 先打印数值 */
c = *p++;
str[j] = c;
printf("%02x ", c);
}
printf(" ; ");
for (j = 0; j < 16; j++)
{
/* 后打印字符 */
if (str[j] < 0x20 || str[j] > 0x7e) /* 不可视字符 */
putchar('.');
else
putchar(str[j]);
}
printf("\n\r");
}
}
void do_erase_nand_flash(void)
{
unsigned int addr;
/* 获得地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
printf("erasing ...\n\r");
nand_erase(addr, 128*1024);
}
void do_write_nand_flash(void)
{
unsigned int addr;
unsigned char str[100];
int i, j;
unsigned int val;
/* 获得地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();
printf("Enter the string to write: ");
gets(str);
printf("writing ...\n\r");
nand_write(addr, str, strlen(str)+1);
}
void nand_flash_test(void)
{
char c;
while (1)
{
/* 打印菜单, 供我们选择测试内容 */
printf("[s] Scan nand flash\n\r");
printf("[e] Erase nand flash\n\r");
printf("[w] Write nand flash\n\r");
printf("[r] Read nand flash\n\r");
printf("[q] quit\n\r");
printf("Enter selection: ");
c = getchar();
printf("%c\n\r", c);
/* 测试内容:
* 1. 识别nand flash
* 2. 擦除nand flash某个扇区
* 3. 编写某个地址
* 4. 读某个地址
*/
switch (c)
{
case 'q':
case 'Q':
return;
break;
case 's':
case 'S':
nand_chip_id();
break;
case 'e':
case 'E':
do_erase_nand_flash();
break;
case 'w':
case 'W':
do_write_nand_flash();
break;
case 'r':
case 'R':
do_read_nand_flash();
break;
default:
break;
}
}
}
4. 设置NAND启动注意事项
NAND 启动时会把代码的前4k拷贝至内部4k RAM 运行,如果编译的代码大于4k 我们需要把NAND代码搬运至 外部SDRAM 运行。
如果重定位前一个调用函数的代码段不在4k以内,调用时不要用bl 调用。bl是 相对跳转指令,能调用正负32M的范围,但如果函数
所在的代码段地址超过4k 范围,此时代码将不在内部 4k RAM 中,bl 跳转将指向一个不存在或不正确的地址。所用我们的正确做法是移
动pc至SDRAM中去执行从NAND拷贝过去的代码。
将NAND代码搬运至 SDRAM执行,支持nor flash 和 nand flash 启动
#include "nand_flash.h"
/* 判断启动方式 */
int isBootFromNorFlash(void)
{
volatile unsigned int *p = (volatile unsigned int *)0;
unsigned int val = *p;
*p = 0x12345678;
if (*p == 0x12345678)
{
/* 写成功, 对应nand启动 */
*p = val;
return 0;
}
else
{
return 1;
}
}
/* 根据不同的启动方式,从不同的位置拷贝代码到SDRAM */
void copy2sdram(void)
{
/* 要从lds文件中获得 __code_start, __bss_start
* 然后从0地址把数据复制到__code_start
*/
extern int __code_start, __bss_start;
volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
volatile unsigned int *src = (volatile unsigned int *)0;
int len;
len = ((int)&__bss_start) - ((int)&__code_start);
if (isBootFromNorFlash())
{
while (dest < end)
{
*dest++ = *src++;
}
}
else
{
nand_init();
nand_read(src, dest, len);
}
}
/* 清除.bss段 */
void clean_bss(void)
{
/* 要从lds文件中获得 __bss_start, _end
*/
extern int _end, __bss_start;
volatile unsigned int *start = (volatile unsigned int *)&__bss_start;
volatile unsigned int *end = (volatile unsigned int *)&_end;
while (start <= end)
{
*start++ = 0;
}
}
----------------------------------------------------------
扩展知识
ECC: Error Checking and Correction
OOB: out of band
BBT: bad block table
https://blog.csdn.net/seasonyrq/article/details/51510965
----------------------------------------------------------
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
NOR Flash 原理及硬件操作
NOR Flash 是内存类接口,可以像读内存一样读,但不能像内存一样写数据。
写数据需要发送对应的解锁指令才能进行操作,写的时候必须保证当前地址是0xff,每次写都是以16位数据为一次操作进行。
如果要写的地址不为空,需要擦除,擦除是以块为单位进行的操作
相同区域内的块大小相同,NOR flash 内有不同的区域。具体要看芯片手册 或 CFI 接口读取。
--------------------------------
uboot 体验 NOR Flash的读写擦除
1.先烧写uboot到NOR Flash , 并设置为NOR 启动
uboot启动时按空格进入uboot,然后输入q 进入命令模式
md.b 0 40 从0地址开始读取64字节数据
mw.w 0 1234 往0地址写入0x1234 0地址为NOR Flash的地址
md.b 0 40 从0地址开始读取64字节数据
md.b 30000000 40 从0x30000000地址开始读取64字节数据
mw.w 30000000 1234 往0x30000000地址写入0x1234 0x30000000 地址为 SDRAM的地址
md.b 30000000 40 从0x30000000地址开始读取64字节数据
对比发现,NOR可以像内存一样“读”,但不可以像内存一样“写”入。
-------------------------------
2. 读取NOR Flash ID:
参考资料:MX29LV160DBTI-70G(NOR FLASH).pdf
NOR Flash 指令操作方式:
解锁->发送操作指令->进行相关操作->退出
芯片手册 P20
Addr Data word
0x555 0xAA //解锁
0x2AA 0x55
0x555 0x90 //读ID命令
X00 //从X00读取厂商ID
X01 //从X01读取设备ID
XXX 0xF0 //任意地址写入0xF0 复位退出 解锁模式
注意事项:
a. 操作过程中解锁,读ID 这些操作的Addr 对应的是NOR Flash的看到的地址 由于CPU是16bit访问,CPU地址线与NOR Flash的地址线相错1位,所以我们要左移一位。
b. 对扇区的擦除,烧写,对应的Addr是内存地址(CPU看到的地址),所以发出的Addr不变。
所以我们正确的读ID方式是:
往地址AAAH写AAH mw.w aaa aa 0x555<<1 = 0xAAA 下同
往地址554写55H mw.w 554 55
往地址AAAH写90H mw.w aaa 90
读0地址得到厂家ID: C2H md.w 0 1
读2地址得到设备ID: 22DAH或225BH md.w 2 1
退出读ID状态: mw.w 0 f0
3. NOR有两种规范, jedec, cfi(common flash interface)
参考资料:CFI publication 100
https://wenku.baidu.com/view/cd1c1e22482fb4daa58d4b42.html
CFI 命令查看芯片手册 P27
进入CFI模式 往AAH写入98H mw.w AA 98
读数据: 读20H得到0051 md.w 20 1
读22H得到0052 md.w 22 1
读24H得到0059 md.w 24 1
读4EH得到0015 md.w 4E 1 2^21=2MB
读58H得到0004 md.w 58 1 说明有4个区域 Number of erase regions within device 说明:每个区域内的BANK块大小相等
//区域1
读5AH得到0000 md.w 5A 1 //获取区域1 的块个数 0x0000+1=1 BANk 低位在低地址(读出的16bit数据低8位有效)
读5CH得到0000 md.w 5C 1
读5EH得到0040 md.w 5E 1 //每个BANk的容量 0x0040 = 64*256 = 16k
读60H得到0000 md.w 60 1
....
//区域4
读72H得到001E md.w 72 1 //获取区域4 的块个数 0x001E+1=31 BANk 低位在低地址
读74H得到0000 md.w 74 1
读76H得到0000 md.w 76 1 //每个BANk的容量 0x0100 = 256*256 = 64k 低位在低地址(读出的16bit数据低8位有效)
读78H得到0001 md.w 78 1
退出CFI模式 mw.w 0 f0
4. 写数据
写数据操作步骤:解锁->发出写数据指令->写数据->复位
写入的数据必须是以16bit;且起始地址必须是偶数16bit对齐,否则重启。
往地址AAAH写AAH mw.w aaa aa 解锁
往地址554H写55H mw.w 554 55
往地址AAAH写A0H mw.w aaa a0 写数据指令
往地址0x100000写1234h mw.w 100000 1234 写数据 写数据 16bit对齐 当一个地址的数据不是0xFF 时需要重新擦除才能再写
5. 数据擦除
擦除操作步骤:解锁->发出擦除指令->解锁->发出扇区地址->启动擦除
mw.w aaa aa
mw.w 554 55
mw.w aaa 80
mw.w aaa aa
mw.w 554 55
mw.w 100000 30
------------------------------------------------
C 程序编写
写之前需要修改Makefile文件,添加编译选项 -march=armv4 ,否则对NOR的一些操作指令会被拆分成两条,导致访问出错。
%.o : %.c
arm-linux-gcc -march=armv4 -c -o $@ $<
%.o : %.S
arm-linux-gcc -march=armv4 -c -o $@ $<
擦除块时,发出的地址只要在某一个块的范围内就可以。
对于nor flash 启动, 由于中断向量表是存在于NOR flash 的起始地址部分, 而对nor flash 进行解锁操作寄存器时,cpu 操作对应的 nor flash 地址段是访问不到的,导致出错! 所以在对nor flash 进行解锁操作编程时,应当关闭所有中断。
#include "norflash.h"
#include "my_printf.h"
#include "string_utils.h"
#define NOR_FLASH_BASE 0 /* jz2440, nor-->cs0, base addr = 0 */
/* Nor 初始化 */
void norflash_init(void)
{
BANKCON0 = 0x0500; //Tacc 0b101 80ns>70ns 访问时钟
}
/*
* 操作 nor flash 内部寄存器地址对应的是 nor flash 芯片上对应的地址线 bit0 - bit15 而不是 CPU的 地址线
* 而我们只能通过CPU的地址线来访问,由于物理接线是 CPU -> bit1 == nor flash -> bit0 详看原理图
* 所以我们需要 (offset << 1) 这是访问的才是 nor flash 内部 寄存器
*
* 当我们要读写擦除数据时,对应的是CPU的地址线,不用左移,由于我们调用的是一个字函数,所以CPU地址需要右移一位,抵消子函数中的左移
*/
void nor_write_word(unsigned int base, unsigned int offset, unsigned int val)
{
volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
*p = val;
}
/* offset是基于NOR的角度看到 */
void nor_cmd(unsigned int offset, unsigned int cmd)
{
nor_write_word(NOR_FLASH_BASE, offset, cmd);
}
unsigned int nor_read_word(unsigned int base, unsigned int offset)
{
volatile unsigned short *p = (volatile unsigned short *)(base + (offset << 1));
return *p;
}
unsigned int nor_dat(unsigned int offset)
{
return nor_read_word(NOR_FLASH_BASE, offset);
}
void wait_ready(unsigned int addr)
{
unsigned int val;
unsigned int pre;
pre = nor_dat(addr>>1);
val = nor_dat(addr>>1);
while ((val & (1<<6)) != (pre & (1<<6)))
{
pre = val;
val = nor_dat(addr>>1);
}
}
/* 进入NOR FLASH的CFI模式
* 读取各类信息
*/
void do_scan_nor_flash(void)
{
char str[4];
unsigned int size;
int regions, i;
int region_info_base;
int block_addr, blocks, block_size, j;
int cnt;
int vendor, device;
/* 打印厂家ID、设备ID */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0x90); /* read id */
vendor = nor_dat(0);
device = nor_dat(1);
nor_cmd(0, 0xf0); /* reset */
nor_cmd(0x55, 0x98); /* 进入cfi模式 */
str[0] = nor_dat(0x10);
str[1] = nor_dat(0x11);
str[2] = nor_dat(0x12);
str[3] = '\0';
printf("str = %s\n\r", str);
/* 打印容量 */
size = 1<<(nor_dat(0x27));
printf("vendor id = 0x%x, device id = 0x%x, nor size = 0x%x, %dM\n\r", vendor, device, size, size/(1024*1024));
/* 打印各个扇区的起始地址 */
/* 名词解释:
* erase block region : 里面含有1个或多个block, 它们的大小一样
* 一个nor flash含有1个或多个region
* 一个region含有1个或多个block(扇区)
* Erase block region information:
* 前2字节+1 : 表示该region有多少个block
* 后2字节*256 : 表示block的大小
*/
regions = nor_dat(0x2c);
region_info_base = 0x2d;
block_addr = 0;
printf("Block/Sector start Address:\n\r");
cnt = 0;
for (i = 0; i < regions; i++)
{
blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8);
block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));
region_info_base += 4;
// printf("\n\rregion %d, blocks = %d, block_size = 0x%x, block_addr = 0x%x\n\r", i, blocks, block_size, block_addr);
for (j = 0; j < blocks; j++)
{
/* 打印每个block的起始地址 */
//printf("0x%08x ", block_addr);
printHex(block_addr);
putchar(' ');
cnt++;
block_addr += block_size;
if (cnt % 5 == 0)
printf("\n\r");
}
}
printf("\n\r");
/* 退出CFI模式 */
nor_cmd(0, 0xf0);
}
void do_erase_nor_flash(void)
{
unsigned int addr;
/* 获得地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
printf("erasing ...\n\r");
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0x80); /* erase sector */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(addr>>1, 0x30); /* 发出扇区地址 */
wait_ready(addr);
}
void do_write_nor_flash(void)
{
unsigned int addr;
unsigned char str[100];
int i, j;
unsigned int val;
/* 获得地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();
printf("Enter the string to write: ");
gets(str);
printf("writing ...\n\r");
/* str[0],str[1]==>16bit
* str[2],str[3]==>16bit
*/
i = 0;
j = 1;
while (str[i] && str[j])
{
val = str[i] + (str[j]<<8);
/* 烧写 */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0xa0); /* program */
nor_cmd(addr>>1, val);
/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
wait_ready(addr);
i += 2;
j += 2;
addr += 2;
}
val = str[i];
/* 烧写 */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0xa0); /* program */
nor_cmd(addr>>1, val);
/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
wait_ready(addr);
}
void do_read_nor_flash(void)
{
unsigned int addr;
volatile unsigned char *p;
int i, j;
unsigned char c;
unsigned char str[16];
/* 获得地址 */
printf("Enter the address to read: ");
addr = get_uint();
p = (volatile unsigned char *)addr;
printf("Data : \n\r");
/* 长度固定为64 */
for (i = 0; i < 4; i++)
{
/* 每行打印16个数据 */
for (j = 0; j < 16; j++)
{
/* 先打印数值 */
c = *p++;
str[j] = c;
printf("%02x ", c);
}
printf(" ; ");
for (j = 0; j < 16; j++)
{
/* 后打印字符 */
if (str[j] < 0x20 || str[j] > 0x7e) /* 不可视字符 */
putchar('.');
else
putchar(str[j]);
}
printf("\n\r");
}
}
void nor_flash_test(void)
{
char c;
while (1)
{
/* 打印菜单, 供我们选择测试内容 */
printf("[s] Scan nor flash\n\r");
printf("[e] Erase nor flash\n\r");
printf("[w] Write nor flash\n\r");
printf("[r] Read nor flash\n\r");
printf("[q] quit\n\r");
printf("Enter selection: ");
c = getchar();
printf("%c\n\r", c);
/* 测试内容:
* 1. 识别nor flash
* 2. 擦除nor flash某个扇区
* 3. 编写某个地址
* 4. 读某个地址
*/
switch (c)
{
case 'q':
case 'Q':
return;
break;
case 's':
case 'S':
do_scan_nor_flash();
break;
case 'e':
case 'E':
do_erase_nor_flash();
break;
case 'w':
case 'W':
do_write_nor_flash();
break;
case 'r':
case 'R':
do_read_nor_flash();
break;
default:
break;
}
}
}
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
定时器中断搞定后就可以像单片一样写一个前后台框架撸代码了。
关于中断的一些开关寄存器请看上一章
中断异常--外部中断配置
定时器时钟框图 看手册 P313
定时器0的相关的寄存器 更详细说明请阅读手册
TCFG0[7:0]->Prescaler 0 : 定时器0和1 的预分频器 0-255
TCFG1[3:0]->MUX 0 : 选择分频的线
TCNTB0[15:0] 装载计数值
TCON 定时器控制寄存器
TCON[1] -> Timer 0 manual update : 手动装计数值,启动自动装载之前要先手动装载一次。并且自动装载之前要把它清除。
TCON[0] -> Timer 0 start/stop :启动/停止 定时器
TCON[3] -> Timer 0 auto reload on/off :自动装载模式
中断开关
INTMSK[10] -> INT_TIMER0 : 打开中断
-------------------------------------------------
代码
typedef void(*irq_func)(int);
irq_func irq_array[32]; //函数指针数组
/* 注册函数 */
void register_irq(int irq, irq_func fp)
{
irq_array[irq] = fp;//注册函数
INTMSK &= ~(1<<irq);//使能中断
}
void handle_irq(void)
{
/* 分辨中断源 */
int bit = INTOFFSET;
/* 调用对应的处理函数 */
irq_array[bit](bit);
/* 清中断 : 从源头开始清 */
SRCPND = (1<<bit);
INTPND = (1<<bit);
}
void timer_irq(void)
{
/* 中断处理函数 */
}
/* 定时器0初始化 */
void timer_init(void)
{
/* 设置TIMER0的时钟 */
/* Timer clk = PCLK / {prescaler value+1} / {divider value}
= 50000000/(99+1)/16
= 31250
*/
TCFG0 = 99; /* Prescaler 0 = 99, 用于timer0,1 */
TCFG1 &= ~0xf;
TCFG1 |= 3; /* MUX0 : 1/16 */
/* 设置TIMER0的初值 */
TCNTB0 = 15625; /* 0.5s中断一次 */
/* 加载初值, 启动timer0 */
TCON |= (1<<1); /* Update from TCNTB0 & TCMPB0 */
/* 设置为自动加载并启动 */
TCON &= ~(1<<1);
TCON |= (1<<0) | (1<<3); /* bit0: start, bit3: auto reload */
/* 设置中断 */
register_irq(10, timer_irq);
}
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
中断框图
Request sources : 中断请求源 发起中断信号
SUBSRCPND :子中断源挂起标记
SUBMASK:子中断屏蔽寄存器 相当于中断开关
SRCPND:中断源挂起标记 有些中断源单独占据一位,有些中断源需要经过子中断源,子中断源汇总后占据SRCPND上的一位。
MASK:中断屏蔽寄存器 功能类似于SUBMASK ,它是总的一个开关
Priority: 优先级配置 暂时不用 保持默认值,与兴趣可以自己研究
INTPND: 发生的中断 通过优先级筛选后的中断
MODE:中断模式,IRQ、FIQ
CPSR: bit7 IRQ 总开关
-----------------------------------------------------
下面介绍 外部中断需要配置的寄存器
GPFCON : IO工作模式配置为中断模式 P292
EXTINT0:配置外部中断的边沿触发方式 P301
EINTMASK:外部中断屏蔽,我们把对应的位设置为不屏蔽,打开中断源开关。属于上图中的SUBMASK 一级开关
INTMSK:中断屏蔽,我们把对应的位设置为不屏蔽,打开中断源开关。属于上图中的MASK 二级开关
CPSR: bit7 打开中断总开关
修改start.S文件 在中断向量表指定的位置添加IRQ入口及对应的现场保存,栈指针设置,中断函数调用,恢复现场,具体可以看我的上一遍文章。
通过这些配置,就可以触发中断。
然后在中断函数中通过分析对应的寄存器,来判断是什么触发的中断,然后再调用特定的函数。
EINTPEND: 外部中断挂起标志,写1清除中断;用于指示哪些外部引脚触发的中断(1个或多个),属于上图中的 SUBSRCPND
SRCPND:中断挂起标志,写1清除中断;用于指示哪些类型的中断(1个或多个);属于上图中的 SRCPND
INTPND:当前发生的中断,写1清除标志,经过中断优先级筛选出来的中断(唯一);用于判断当前发生的是什么中断
INTOFFSET:当前发生中断的偏移,是INTPND对应位的下标,用于快速判断中断源,不用手动清除,清除INTPND时,INTOFFSET也会清除;
下面是代码
----------------------------------------------------------------
start.S
.text
.global _start
_start:
/* 中断向量表 对应工作模式 参考 P82 */
b reset /* vector 0x00 : reset svc 复位 管理模式*/
ldr pc, _undefined_instruction /* vector 0x04 : und 无定义指令模式 */
ldr pc, _software_interrupt /* vector 0x08 : swi svc 软中断 管理模式 */
b reset /* vector 0x0C : abt(prefetch) 终止模式 预取终止 */
b reset /* vector 0x10 : abt(data) 终止模式 数据终止 */
b reset /* vector 0x14 : Reserved 保留 */
ldr pc, _irq /* vector 0x18 : IRQ 中断模式 */
b reset /* vector 0x1C : FIQ 快速中断模式 */
/* 将函数的入口放置在前面,如果不定义默认放置在.text 段的
* 末尾 ,start.S 汇编后超过4k 在NAND启动时CPU有可能无法
* 读取到入口
*/
_undefined_instruction:
.word undefined_instruction /* 保存入口地址。运行时地址,会跳转到SDRAM执行 */
_software_interrupt:
.word software_interrupt
_irq:
.word irq
undefined_instruction:
/* 执行到这里之前:
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_und保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式
* 4. 跳到0x04的地方执行程序
*/
/* sp_und未设置, 先设置它 */
ldr sp, =0x34000000
/* 保存现场 */
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 处理und异常 */
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
software_interrupt:
/* 执行到这里之前:
* 1. lr_swi保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_swi保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为10011, 进入到swi模式
* 4. 跳到0x08的地方执行程序
*/
/* sp_swi未设置, 先设置它 */
ldr sp, =0x33f00000
/* 保存现场 */
/* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 处理swi异常 */
/* C函数调用时会用到 r4 - r11 所以在函数调用时会被保存,函
* 数返回时恢复,所以我们可以用r4来获取lr的值 参考第8章内
* 容 ATPCS 寄存器使用规则
*/
mov r4, lr // 将lr 返回地址 保存到 r4 sub r0, r4, #4 获取触发SWI异常的指令 ,根据指令获取传递进来的软中断号
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState
/* 获取软中断号 */
sub r0, r4, #4
bl getSWIVal
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
irq:
/* 执行到这里之前:
* 1. lr_irq保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_irq保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为10010, 进入到irq模式
* 4. 跳到0x18的地方执行程序
*/
/* sp_irq未设置, 先设置它 */
ldr sp, =0x33d00000
/* 保存现场 */
/* 在irq异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr-4是异常处理完后的返回地址, 也要保存 参考 P80 */
sub lr, lr, #4
stmdb sp!, {r0-r12, lr}
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState
/* 处理irq异常 */
bl handle_irq_c
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr_irq的值恢复到cpsr里 */
/* 上面打印字符串长度有可能不是4字节对齐,所以这里要强
* 制4字节对齐,否则程序可能会出错
*/
.align 4
reset:
/* 关闭看门狗 */
ldr r0,=0x53000000
ldr r1,=0
str r1,[r0]
/* PLL配置---------------------------- */
/* 设置MPLL,FCLK:HCLK:PCLK=400M:100M:50M */
/* 设置lock time 详见手册P255
* LCOKTIME(0x4c000000)=0xFFFFFFFF
*/
ldr r0,=0x4c000000
ldr r1,=0xFFFFFFFF
str r1,[r0]
/* 设置HCLK、PCLK 相对于FCLK的分频系数 详见手册P260
* CLKDIVN(0x4c000014)=0x05 tFCLK:tHCLK:tPCLK=1:4:8
*/
ldr r0,=0x4c000014
ldr r1,=0x05
str r1,[r0]
/* 设置CPU工作于异步模式
* 详见手册P244
*/
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 配置MPLLCON 来设置FCLK频率 详见手册P255~256
* 设置MPLLCON(0x4c000004)=(92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8 =100
* p = PDIV+2 =1+2 =3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s)=2*100*12/(3*2^1)=400MHz
*/
ldr r0,=0x4c000004
ldr r1,=(92<<12)|(1<<4)|(1<<0)
str r1,[r0]
/* 一旦设置PLL,就会锁定 lock time 直到PLL输出稳定 */
/* PLL配置--END-------------------------- */
/* 设置内存:sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址,再读出来;如果读出来是0,表示
* 操作是内部RAM,是nand启动;否则就是nor启动
* 因为nor flash 不向内存那样能直接读写
*/
mov r1,#0
ldr r0,[r1] /* 备份0地址数据 */
str r1,[r1] /* 将0写入0地址 */
ldr r2,[r1] /* 读取0地址数据到r2 */
cmp r1,r2 /* 判断r1 r2是否相等 */
ldr sp,=0x40000000+4096 /* 假设nor启动 */
moveq sp,#4096 /* 如果r1==r2 nand启动 */
streq r0,[r1] /* 恢复原来的值 */
/* 整个程序重定位 */
bl sdram_init /* 初始化SDRAM 否则下面无法搬运数据 */
bl copy2sdram /* .text .rodata .data 段数据拷贝 */
bl clean_bss /* .bss 段清除 */
/* 复位之后, cpu处于svc模式
* 现在, 切换到usr模式
*/
mrs r0, cpsr /* 读出cpsr */
bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */
bic r0, r0, #(1<<7) /* 清除I位, 使能中断 开总中断 */
msr cpsr, r0
/* 设置 sp_usr */
ldr sp, =0x33e00000
bl uart0_init /* 故障代码中调用了串口打印,所以先初始化串口 */
und_code:
.word 0xeeadc0de /* 未定义指令 P86 指令集格式 原代码 0xdeadc0de 有误 参考:http://www.100ask.org/bbs/forum.php?mod=viewthread&tid=20299&highlight=und%D2%EC%B3%A3 */
/* 获取当前状态的CPSR */
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState // 打印当前状态信息
swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */
.align 4
/* 调用main函数 */
ldr pc, =main /* 绝对跳转, 跳到SDRAM 运行 */
/* 死循环 */
halt:
b halt
interrupt.c
#include "interrupt.h"
/* 初始化按键, 设为中断源 */
void key_eint_init(void)
{
/* 配置GPIO为中断引脚 */
GPFCON &= ~((3<<0) | (3<<4));
GPFCON |= ((2<<0) | (2<<4)); /* S2,S3被配置为中断引脚 */
GPGCON &= ~((3<<6) | (3<<22));
GPGCON |= ((2<<6) | (2<<22)); /* S4,S5被配置为中断引脚 */
/* 设置GPFCON让GPF4/5/6配置为输出引脚 */
GPFCON &= ~((3<<8) | (3<<10) | (3<<12));
GPFCON |= ((1<<8) | (1<<10) | (1<<12));
/* 设置中断触发方式: 双边沿触发 */
EXTINT0 |= (7<<0) | (7<<8); /* S2,S3 */
EXTINT1 |= (7<<12); /* S4 */
EXTINT2 |= (7<<12); /* S5 */
/* 设置EINTMASK使能eint11,19 */
EINTMASK &= ~((1<<11) | (1<<19));
INTMSK &= ~((1<<0) | (1<<2) | (1<<5));
}
/* 读EINTPEND分辨率哪个EINT产生(eint4~23)
* 清除中断时, 写EINTPEND的相应位
*/
void key_eint_irq(int irq)
{
unsigned int val = EINTPEND;
unsigned int val1 = GPFDAT;
unsigned int val2 = GPGDAT;
if (irq == 0) /* eint0 : s2 控制 D12 */
{
if (val1 & (1<<0)) /* s2 --> gpf6 */
{
/* 松开 */
GPFDAT |= (1<<6);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<6);
}
}
else if (irq == 2) /* eint2 : s3 控制 D11 */
{
if (val1 & (1<<2)) /* s3 --> gpf5 */
{
/* 松开 */
GPFDAT |= (1<<5);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<5);
}
}
else if (irq == 5) /* eint8_23, eint11--s4 控制 D10, eint19---s5 控制所有LED */
{
if (val & (1<<11)) /* eint11 */
{
if (val2 & (1<<3)) /* s4 --> gpf4 */
{
/* 松开 */
GPFDAT |= (1<<4);
}
else
{
/* 按下 */
GPFDAT &= ~(1<<4);
}
}
else if (val & (1<<19)) /* eint19 */
{
if (val2 & (1<<11))
{
/* 松开 */
/* 熄灭所有LED */
GPFDAT |= ((1<<4) | (1<<5) | (1<<6));
}
else
{
/* 按下: 点亮所有LED */
GPFDAT &= ~((1<<4) | (1<<5) | (1<<6));
}
}
}
EINTPEND = val;
}
void handle_irq_c(void)
{
/* 分辨中断源 */
int bit = INTOFFSET;
/* 调用对应的处理函数 */
if (bit == 0 || bit == 2 || bit == 5) /* eint0,2,eint8_23 */
{
key_eint_irq(bit); /* 处理中断, 清中断源EINTPEND */
}
/* 清中断 : 从源头开始清 */
SRCPND = (1<<bit);
INTPND = (1<<bit);
}
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
英文参考:ARM Architecture Reference Manual
中文参考:<ARM体系结构与编程> 杜春雷
CPU模式(Mode) 状态(State)与寄存器
7种Mode: P72
usr/sys 用户模式/系统模式
undefined(und) 无定义指令模式,CPU遇到不认识的指令时会进入该模式
Supervisor(svc) 管理模式
Abort(abt) 终止模式 1. 指令预取终止 流水线预取出错 2. 数据访问终止 数据读取过程中产生的异常。
IRQ(irq) 中断模式
FIQ(fiq) 快速中断模式 有多个专用寄存器,所以中断速度比较快
2种状态:
ARM state 4字节指令
Thumb state 2字节指令 用于压缩程序代码 对小容量芯片非常有用,S3C2440芯片一般不用
寄存器:
通用寄存器
分组寄存器(banked register) 特权模式的专用寄存器 P73
当前程序状态寄存器 CPSR : (Current Program State Register) 属于通用寄存器
CPSR的备份寄存器 SPSR: (Saved Program State Register) 特权模式专用寄存器,用于保存被中断模式的CPSR 程序状态
除了 usr 其它模式均为特权模式(privileged mode)。特权模式之间可以通过修改CPSR 低5位来切换。普通模式无法修改CPSR进入特权模式。 CPSR 详见 P76
-------------------------------------------------------------------
中断属于异常的一种。异常对应不同的CPU工作模式,不同的工作模式,有不同的专有寄存器 lr、sp、spsr。
lr 保存被中断模式中的下一条即将执行的指令的地址 进入异常之前硬件自动保存
sp 当前异常模式专用的栈指针, 进入异常后先设置栈指针
spsr 备份被中断状态的程序状态 进入异常之前硬件自动保存
CPU每执行完一条指令都会去检查是否有异常产生,如果有异常,就开始去处理异常。
对于不同的异常,跳去不同的地址去执行。这整个过程是有硬件决定的,这个跳转的
地址也是固定的。不同的异常跳转地址组成了异常向量表,如下:
_start:
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
如果是中断异常,则硬件会自动跳到 0x18(ldr pc, _irq),跳转到0x18位置是由硬件决定的,0x18这个地址放什么指令是由软件决定。
我们一般在这个位置放置一条跳转命令,跳转到更复杂的程序(中断处理程序)
我们要在中断处理程序中做什么呢?
1. 保存现场 (保存寄存器数据)
2. 调用处理函数
3. 恢复现场 (恢复寄存器数据)
2 中调用的处理函数,判断中断类型,根据不同的中断类型调用不同中断理函数。
进入异常硬件动作: P79
1. 保存被中断模式的 下一条指令地址到 对应异常模式的LR专用控制器
2. 保存被中断模式的CPSR程序状态寄存器到 对应异常模式的 SPSR (状态备份 )
3. 修改CPSR中的低5位 切换到对应的异常模式。
4. 修改PC指针指向异常向量表对应的异常入口
退出异常软件操作:
1. 如果是中断产生的异常,清除中断源
2. 恢复程序状态寄存器 CPSR = SPSR(对应异常模式的程序状态备份寄存器)
3. 修改PC指针 = _LR - offset(4 or 8) 参考 P80 table 2-2
und 未定义指令异常
当CPU发现未定义指令时,会强制跳转到 0x04 地址,同时会将异常前的下一条指令地址保存在 lr_und CPSR 保存在 SPSR_und保存有被中断模式的CPSR
所以我们保存现场时要保存 lr_und 到栈中,保存现场之前要设置异常模式的栈地址。
恢复现场的时候将lr恢复到pc 将 SPSR_und恢复到CPSR
我们对start.S文件进行修改
----------------------------
_start:
b reset /* vector 0 : reset */
b do_und /* vector 4 : und */
do_und:
/* 执行到这里之前:
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_und保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式
* 4. 跳到0x4的地方执行程序
*/
/* sp_und未设置, 先设置它 */
ldr sp, =0x34000000
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 保存现场 */
/* 处理und异常 */
mrs r0, cpsr
ldr r1, =und_string
bl printException
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
und_string:
.string "undefined instruction exception"
.align 4 /* 这里要加上4k 对齐 否则上面und_string数据段的长度会影响程序的正确执行 */
reset:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
略……
----------------------------
上面代码并不完善,我们要进行一些细节修改:
1. b do_und 函数中的 bl printException 函数有可能超出4K 范围,所以我们需要进行强制跳转到runtime addr 地址(SDRAM执行)
2. 经过反汇编代码分析知道,ldr pc,=do_und 这条指令中是将 do_und的入口地址放置在.data数据段中,数据段默认是放置在start.S .text代码段结尾,如果代码长度超过4K 并且代码是在NAND上时,ldr pc,=do_und将无法获取到do_und的入口,所以我们需要人为将入口地址放置在前面,保证程序能正确获取到入口地址。
3. 数据段 后面的代码要保持 4字节对齐,如:und_string: 中的数据长度如果有变化,会导致代码运行出错。
修改后的 start.S
----------------------------
.text
.global _start
_start:
b reset /* vector 0 : reset */
ldr pc, und_addr /* vector 4 : und */
und_addr:
.word do_und
do_und:
/* 执行到这里之前:
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_und保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式
* 4. 跳到0x4的地方执行程序
*/
/* sp_und未设置, 先设置它 */
ldr sp, =0x34000000
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 保存现场 */
/* 处理und异常 */
mrs r0, cpsr
ldr r1, =und_string
bl printException
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
und_string:
.string "undefined instruction exception"
.align 4
reset:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFF
str r1, [r0]
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */
ldr r0, =0x4C000014
ldr r1, =0x5
str r1, [r0]
/* 设置CPU工作于异步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8=100
* p = PDIV+2 = 1+2 = 3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
*/
ldr r0, =0x4C000004
ldr r1, =(92<<12)|(1<<4)|(1<<0)
str r1, [r0]
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
* 然后CPU工作于新的频率FCLK
*/
/* 设置内存: sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址, 再读出来
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
* 否则就是nor启动
*/
mov r1, #0
ldr r0, [r1] /* 读出原来的值备份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
moveq sp, #4096 /* nand启动 */
streq r0, [r1] /* 恢复原来的值 */
bl sdram_init
//bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */
/* 重定位text, rodata, data段整个程序 */
bl copy2sdram
/* 清除BSS段 */
bl clean_bss
ldr pc, =sdram
sdram:
bl uart0_init
/* 故意加入一条未定义指令 */
und_code:
.word 0xeeadc0de
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
ldr pc, =main /* 绝对跳转, 跳到SDRAM */
halt:
b halt
----------------------------
SVC异常 (SWI软中断)
一般情况下APP应该运行于usr mode 。usr受限不可访问硬件。APP想要访问硬件,必须切换mode ,通过异常(未定义异常、中断异常可遇不可求),所以我们APP需要调用swi #val 进入svc异常。然后系统根据不同的异常标号,执行相应的操作。
怎么获取异常标号。
进入异常时 lr_svc 保存了被中断模式的下一条指令地址 ,通过lr_svc - 4 即可获取上一条指令的地址:SWI #val ,然后读取指令并对指令进行解析,即可得到VAL值 参考 P86
注释:C函数调用时会用到 r4 - r11 所以在函数调用时会被保存,函数返回时恢复,所以我们可以用r4来获取lr的值 参考第8章内容 ATPCS 寄存器使用规则
--------------------------
start.S代码
.text
.global _start
_start:
/* 中断向量表 对应工作模式 参考 P82 */
b reset /* vector 0x00 : reset svc 复位 管理模式*/
ldr pc, _undefined_instruction /* vector 0x04 : und 无定义指令模式 */
ldr pc, _software_interrupt /* vector 0x08 : swi svc 软中断 管理模式 */
b reset /* vector 0x0C : abt(prefetch) 终止模式 预取终止 */
b reset /* vector 0x10 : abt(data) 终止模式 数据终止 */
b reset /* vector 0x14 : Reserved 保留 */
b reset /* vector 0x18 : IRQ 中断模式 */
b reset /* vector 0x1C : FIQ 快速中断模式 */
/* 将函数的入口放置在前面,如果不定义默认放置在.text 段的
* 末尾 ,start.S 汇编后超过4k 在NAND启动时CPU有可能无法
* 读取到入口
*/
_undefined_instruction:
.word undefined_instruction /* 保存入口地址。运行时地址,会跳转到SDRAM执行 */
_software_interrupt:
.word software_interrupt
undefined_instruction:
/* 执行到这里之前:
* 1. lr_und保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_und保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为11011, 进入到und模式
* 4. 跳到0x04的地方执行程序
*/
/* sp_und未设置, 先设置它 */
ldr sp, =0x34000000
/* 保存现场 */
/* 在und异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 处理und异常 */
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
software_interrupt:
/* 执行到这里之前:
* 1. lr_swi保存有被中断模式中的下一条即将执行的指令的地址
* 2. SPSR_swi保存有被中断模式的CPSR
* 3. CPSR中的M4-M0被设置为10011, 进入到swi模式
* 4. 跳到0x08的地方执行程序
*/
/* sp_swi未设置, 先设置它 */
ldr sp, =0x33f00000
/* 保存现场 */
/* 在swi异常处理函数中有可能会修改r0-r12, 所以先保存 */
/* lr是异常处理完后的返回地址, 也要保存 */
stmdb sp!, {r0-r12, lr}
/* 处理swi异常 */
/* C函数调用时会用到 r4 - r11 所以在函数调用时会被保存,函
* 数返回时恢复,所以我们可以用r4来获取lr的值 参考第8章内
* 容 ATPCS 寄存器使用规则
*/
mov r4, lr // 将lr 返回地址 保存到 r4 sub r0, r4, #4 获取触发SWI异常的指令 ,根据指令获取传递进来的软中断号
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState
/* 获取软中断号 */
sub r0, r4, #4
bl getSWIVal
/* 恢复现场 */
ldmia sp!, {r0-r12, pc}^ /* ^会把spsr的值恢复到cpsr里 */
/* 上面打印字符串长度有可能不是4字节对齐,所以这里要强
* 制4字节对齐,否则程序可能会出错
*/
.align 4
reset:
/* 关闭看门狗 */
ldr r0,=0x53000000
ldr r1,=0
str r1,[r0]
/* PLL配置---------------------------- */
/* 设置MPLL,FCLK:HCLK:PCLK=400M:100M:50M */
/* 设置lock time 详见手册P255
* LCOKTIME(0x4c000000)=0xFFFFFFFF
*/
ldr r0,=0x4c000000
ldr r1,=0xFFFFFFFF
str r1,[r0]
/* 设置HCLK、PCLK 相对于FCLK的分频系数 详见手册P260
* CLKDIVN(0x4c000014)=0x05 tFCLK:tHCLK:tPCLK=1:4:8
*/
ldr r0,=0x4c000014
ldr r1,=0x05
str r1,[r0]
/* 设置CPU工作于异步模式
* 详见手册P244
*/
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 配置MPLLCON 来设置FCLK频率 详见手册P255~256
* 设置MPLLCON(0x4c000004)=(92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8 =100
* p = PDIV+2 =1+2 =3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s)=2*100*12/(3*2^1)=400MHz
*/
ldr r0,=0x4c000004
ldr r1,=(92<<12)|(1<<4)|(1<<0)
str r1,[r0]
/* 一旦设置PLL,就会锁定 lock time 直到PLL输出稳定 */
/* PLL配置--END-------------------------- */
/* 设置内存:sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址,再读出来;如果读出来是0,表示
* 操作是内部RAM,是nand启动;否则就是nor启动
* 因为nor flash 不向内存那样能直接读写
*/
mov r1,#0
ldr r0,[r1] /* 备份0地址数据 */
str r1,[r1] /* 将0写入0地址 */
ldr r2,[r1] /* 读取0地址数据到r2 */
cmp r1,r2 /* 判断r1 r2是否相等 */
ldr sp,=0x40000000+4096 /* 假设nor启动 */
moveq sp,#4096 /* 如果r1==r2 nand启动 */
streq r0,[r1] /* 恢复原来的值 */
/* 整个程序重定位 */
bl sdram_init /* 初始化SDRAM 否则下面无法搬运数据 */
bl copy2sdram /* .text .rodata .data 段数据拷贝 */
bl clean_bss /* .bss 段清除 */
/* 复位之后, cpu处于svc模式
* 现在, 切换到usr模式
*/
mrs r0, cpsr /* 读出cpsr */
bic r0, r0, #0xf /* 修改M4-M0为0b10000, 进入usr模式 */
msr cpsr, r0
/* 设置 sp_usr */
ldr sp, =0x33e00000
bl uart0_init /* 故障代码中调用了串口打印,所以先初始化串口 */
und_code:
.word 0xeeadc0de /* 未定义指令 P86 指令集格式 原代码 0xdeadc0de 有误 参考:http://www.100ask.org/bbs/forum.php?mod=viewthread&tid=20299&highlight=und%D2%EC%B3%A3 */
/* 获取当前状态的CPSR */
mrs r0, cpsr // 获取程序状态寄存器 传递给 printState
bl printState
swi 0x123 /* 执行此命令, 触发SWI异常, 进入0x8执行 */
.align 4
/* 调用main函数 */
ldr pc, =main /* 绝对跳转, 跳到SDRAM 运行 */
/* 死循环 */
halt:
b halt
相关 C 函数代码
/* 当前状态打印 P78 */
void printState(unsigned int cpsr)
/* 获取SWI 异常参数 */
void getSWIVal(unsigned int *pSWI)
串口打印相关的函数请参考我前面的文章
/* 当前状态打印 P78 */
void printState(unsigned int cpsr)
{
puts("cpsr = ");
printHex(cpsr);
puts(" ");
switch(cpsr&0x1F) // M[4:0]
{
case 0x10: puts(" User State !\n\r"); break;
case 0x11: puts(" FIQ State !\n\r"); break;
case 0x12: puts(" IRQ State !\n\r"); break;
case 0x13: puts(" Supervisor State !\n\r"); break;
case 0x17: puts(" Abort State !\n\r"); break;
case 0x1B: puts(" Undefined State !\n\r"); break;
case 0x1F: puts(" System State !\n\r"); break;
default: puts(" error State !\n\r");break;
}
}
/* 获取SWI 异常参数 */
void getSWIVal(unsigned int *pSWI)
{
puts("SWI val = ");
printHex(*pSWI & ~0xff000000);
puts("\n\r");
}
-----------------------------------------------------------------
# gcc Thumb 指令集 编译 不重要可以跳过
由于Thumb 是压缩的指令集,所以代码体积比较小
.c 代码编译
修改makeflie 添加编译选项 -mthumb
------------
%.o : %.c
arm-linux-gcc -mthumb -c -o $@ $<
-----------
.s 代码编译
.cdoe 32 表示使用arm 指令集编译
-----------
.text
.global _start
.code 32 //使用ARM
_start:
……
-----------
.code 16 表示使用thumb 指令集编译
-----------
……
.code 16
thumb_func:
bl sdram_init
……
-----------
怎么从 arm state 切换到 thumb state 状态运行
用bx 跳转 如果跳转地址的bit0 =1 则切换到 thumb state 运行
-----------
……
adr r0,thumb_fun
add r0,r0,#1
bx r0
……
-----------
thumb 编译事项
1. 不能用 ldr pc,=main 跳转到main函数,需要:
ldr r0,=main
mov pc,r0
2. cpy2mem 数组 前添加 const static 修饰 防止编译出错。
-----------------------------------------
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
-------------------------------------------------
A. NAND flash中的程序小于4K可以直接运行,如果超过4K则需要将代码从NAND flash中读取到SDRAM执行,这个过程需要代码重定位。
B. Nor flash 可以像内存一样读数据,但不能像内存一样直接写数据。
所以在Nor flash 上的程序中如果包含 全局/静态变量,将无法修改。因为全局变量也是保存在.bin 文件中。需要代码重定位,将代码放入SDRAM中。
把程序从一个位置移动到另一个位置,叫做代码的重定位,可以只重定位数据段,也可以重定位整个程序。
程序包含的段:
.text 代码段 程序代码
.data 数据段 存放全局变量和静态变量
.rodata 只读数据段 只读数据const
.bss 初值为0 或 无初值的全局变量
.comment 注释段 编译器描述信息
.bss和.commen 不保存在.bin 文件中
--------------------------------------------------
链接脚本简单测试
参考文档
http://ftp.gnu.org/old-gnu/Manuals/ld-2.9.1/html_mono/ld.html
arm-linux-ld -Ttext 0 -Tdata 0x30000000 start.o led.o uart.o init.o main.o -o sdram.elf
直接指定数据段,将全局变量定义在SDRAM中,会导致编译后的.bin 文件非常大,不符合我们的要求
处理方法,将代码段写在数据段后面不远的位置,如0x800 ,然后在程序运行时,将数据段拷贝至SDRAM,重定位。需要引入链接脚本 .lds
Makefile:
arm-linux-ld -T sdram.lds start.o led.o uart.o init.o main.o -o sdram.elf
sdram.lds:
SECTIONS {
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x800)
{
data_load_addr = LOADADDR(.data);
data_start = . ;
*(.data)
data_end = . ;
}
.bss : { *(.bss) *(.COMMON) }
}
start.s main函数前添加如下代码
bl sdram_init /* 初始化SDRAM 否则下面无法搬运数据 */
/* 重定位data段 */
ldr r1, =data_load_addr /* data段在bin文件中的地址, 加载地址 */
ldr r2, =data_start /* data段在重定位地址, 运行时的地址 */
ldr r3, =data_end /* data段结束地址 */
cpy:
ldrb r4, [r1]
strb r4, [r2]
add r1, r1, #1
add r2, r2, #1
cmp r2, r3
bne cpy
----------------------------------------------------------------------------
链接脚本解析:
runtime addr :运行时地址
relocate addr:重定位地址
链接地址
load addr :加载地址
程序运行时应该位于 runtime addr。如果load addr!=runtime addr 需要重定位:把load addr位置代码拷贝到对应的runtime addr.
SECTIONS {
.text 0 : { *(.text) } // 所有文件的代码段 runtime addr = load addr 无需重定位
.rodata : { *(.rodata) } // 所有文件的.rodata 段
.data 0x30000000 : AT(0x800) // runtime addr = 0x30000000 也可以叫做链接地址 load addr = 0x800
{ // load addr!=runtime addr 需要重定位
data_load_addr = LOADADDR(.data); // data_load_addr 相当于一个变量 保存的是当前运行时的地址,传递给其他参数使用。
data_start = . ; // . 表示当前地址
*(.data)
data_end = . ;
}
.bss : { *(.bss) *(.COMMON) } //.elf 和 .bin 文件中都不存在 .bss .COMMON 段。
}
1.链接得到.elf格式的文件,含有(load addr)地址信息
2.使用加载器把.elf文件读入内存,加载到指定位置(加载器:a.JTAG b.加载程序APP)
3.运行程序
4.如果load addr!=runtime addr 程序本身需要重定位
a. elf -> bin
b. 没有加载器,硬件机制启动。
c. 如果.bin 中指令所在的位置!= runtime addr 那么程序本身需要实现重定位功能。
---------------------------------------------
.elf 和 .bin 文件中都不存在 .bss .COMMON 段。
所以我们要清空.bss段对应地址中的数值,使其初始值等于0;
SECTIONS
{
.text 0 : { *(.text) }
.rodata : { *(.rodata) }
.data 0x30000000 : AT(0x800)
{
data_load_addr = LOADADDR(.data);
data_start = . ;
*(.data)
data_end = . ;
}
.bss :
{
bss_start = .;
*(.bss) *(.COMMON)
bss_end = .;
}
}
start.s 在重定位后面加上如下代码
/* 清除 .bss 段 */
ldr r1,=bss_start
ldr r2,=bss_end
mov r3,#0
clean:
strb r3,[r1]
add r1,r1,#1
cmp r1,r2
bne clean
-------------------------------------------------
链接脚本的改进
目的:提高代码拷贝的效率,减少对外部芯片的读写次数
a. 将数据的读写改为 32bit 位宽 4字节对齐
ldrb r4, [r1] -> ldr r4, [r1]
strb r4, [r2] -> str r4, [r2]
b. 由于将读写修改为 32 bit宽 4字节对齐,所以在清除.bss段时有可能 .bss的起始地址不是4字节对齐,会导致清除掉.data段的数据。所以要修改链接脚本使其 bss_start 地址 4字节对齐
. = ALIGN(4); 当前地址4字节对齐
bss_start = .; 将当前地址传递给 bss_start
由于链接脚本中 .data 段我们指定的地址类型是4字节对齐,所以不需要修改,如果不放心,也可以在data_start = .; 前添加 . = ALIGN(4);
-------------------------------------------------
整个程序的重定位
数据和代码分离的重定位常用在单片机中或有可执行代码的器件中如nor,如果没有外接nor设备,我们就需要将整个程序重定位。
步骤:
1. 在连接脚本中指定runtime addr 为 SDRAM
2.重定位之前的代码用位置无关码编写。这样程序在Nor和SDRAM中都可执行。
位置无关代码不能使用 :绝对地址、全局变量、静态变量、有初值的数组,具体查看反汇编代码。
链接脚本可以参考 u-boot 重定位代码进行修改。
知识点:
b/bl 是相对跳转 = 当前pc值 + offset(偏移地址)
反汇编代码中的 bl _0x30000000<sdram_init> 并不是跳转到 0x30000000 位置。 _0x30000000<sdram_init>只是为了便于分析代码才这样写的。
怎么写位置无关吗程序:
a. 调用程序时用B/BL相对跳转指令
b. 重定位之前不可以使用绝对地址:全局变量、静态变量、由初值的数组,具体要看反汇编结果。
c. 重定位之后,使用绝对跳转指令跳转到Runtime Addr : ldr pc,=main 如果使用bl main 程序还是在Nor或片内SRAM执行。
有初值的数组:数组保存在栈中,数组中的初值保存在 .rodata 段中。
修改后的链接脚本 sdram.lds
SECTIONS
{
. = 0x30000000;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
_end = .;
}
修改后的start.s
.text
.global _start
_start:
/* 关闭看门狗 */
ldr r0, =0x53000000
ldr r1, =0
str r1, [r0]
/* 设置MPLL, FCLK : HCLK : PCLK = 400m : 100m : 50m */
/* LOCKTIME(0x4C000000) = 0xFFFFFFFF */
ldr r0, =0x4C000000
ldr r1, =0xFFFFFFFF
str r1, [r0]
/* CLKDIVN(0x4C000014) = 0X5, tFCLK:tHCLK:tPCLK = 1:4:8 */
ldr r0, =0x4C000014
ldr r1, =0x5
str r1, [r0]
/* 设置CPU工作于异步模式 */
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 设置MPLLCON(0x4C000004) = (92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8=100
* p = PDIV+2 = 1+2 = 3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s) = 2*100*12/(3*2^1)=400M
*/
ldr r0, =0x4C000004
ldr r1, =(92<<12)|(1<<4)|(1<<0)
str r1, [r0]
/* 一旦设置PLL, 就会锁定lock time直到PLL输出稳定
* 然后CPU工作于新的频率FCLK
*/
/* 设置内存: sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址, 再读出来
* 如果得到0, 表示0地址上的内容被修改了, 它对应ram, 这就是nand启动
* 否则就是nor启动
*/
mov r1, #0
ldr r0, [r1] /* 读出原来的值备份 */
str r1, [r1] /* 0->[0] */
ldr r2, [r1] /* r2=[0] */
cmp r1, r2 /* r1==r2? 如果相等表示是NAND启动 */
ldr sp, =0x40000000+4096 /* 先假设是nor启动 */
moveq sp, #4096 /* nand启动 */
streq r0, [r1] /* 恢复原来的值 */
bl sdram_init
//bl sdram_init2 /* 用到有初始值的数组, 不是位置无关码 */
/* 重定位text, rodata, data段整个程序 */
mov r1, #0
ldr r2, =_start /* 第1条指令运行时的地址 */
ldr r3, =__bss_start /* bss段的起始地址 */
cpy:
ldr r4, [r1]
str r4, [r2]
add r1, r1, #4
add r2, r2, #4
cmp r2, r3
ble cpy
/* 清除BSS段 */
ldr r1, =__bss_start
ldr r2, =_end
mov r3, #0
clean:
str r3, [r1]
add r1, r1, #4
cmp r1, r2
ble clean
//bl main /* 使用BL命令相对跳转, 程序仍然在NOR/sram执行 */
ldr pc, =main /* 绝对跳转, 跳到SDRAM */
halt:
b halt
------------------------------------------------------
用C语言替换代码重定义和.bss 段清除
a. 汇编向C函数传递运行时地址,调用c函数
start.s 改进
/* 重定位text, rodata, data段整个程序 */
mov r0, #0
ldr r1, =_start /* 第1条指令运行时的地址 */
ldr r2, =__bss_start /* bss段的起始地址 */
sub r2, r2, r1
bl copy2sdram /* src, dest, len */
/* 清除BSS段 */
ldr r0, =__bss_start
ldr r1, =_end
bl clean_bss /* start, end */
c语言代码
void copy2sdram(volatile unsigned int *src, volatile unsigned int *dest, unsigned int len) /* src, dest, len */
{
unsigned int i = 0;
while (i < len)
{
*dest++ = *src++;
i += 4;
}
}
void clean_bss(volatile unsigned int *start, volatile unsigned int *end) /* start, end */
{
while (start <= end)
{
*start++ = 0;
}
}
b. 直接从链接脚本中获取 对应的地址
starts.s 改进
/* 重定位text, rodata, data段整个程序 */
bl copy2sdram
/* 清除BSS段 */
bl clean_bss
sdram.lds 改进 传递起始地址
SECTIONS
{
. = 0x30000000;
__code_start = .;
. = ALIGN(4);
.text :
{
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(.COMMON) }
_end = .;
}
C代码改进
void copy2sdram(void)
{
/* 要从lds文件中获得 __code_start, __bss_start
* 然后从0地址把数据复制到__code_start
*/
extern int __code_start, __bss_start;
volatile unsigned int *dest = (volatile unsigned int *)&__code_start;
volatile unsigned int *end = (volatile unsigned int *)&__bss_start;
volatile unsigned int *src = (volatile unsigned int *)0;
while (dest < end)
{
*dest++ = *src++;
}
}
void clean_bss(void)
{
/* 要从lds文件中获得 __bss_start, _end
*/
extern int _end, __bss_start;
volatile unsigned int *start = (volatile unsigned int *)&__bss_start;
volatile unsigned int *end = (volatile unsigned int *)&_end;
while (start <= end)
{
*start++ = 0;
}
}
原理:
文件编译时有一个 symbol table 符号表
存放各种变量
name1:global_a:addr1
name2:global_b:addr2
....
....
__code_start::addr
c语言中用取址符号 & 即可获取对应的地址
使用方法:
1. 声明为外部变量
2. 用取值符号得到地址
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
CPU 通过地址线/数据线访问 -> GPIO控制器
-> UART/I2C/I2S/SPI控制器
-> NAND控制器
-> 内存控制器 NOR flash/SDRAM/网卡
所有的外设控制器和内存设备都属于CPU统一编址,NAND控制属于内存编址,NAND flash 不属于CPU统一编址。
NOR flash/SDRAM/网卡 共用地址线/数据线,如何互不干扰?
答:通过片选信号来区分,根据访问地址范围来使能不同的片选信号。每个地址范围128MB = 2^27 所以地址线有A0~A26 27条线。片选用来使能不同的内存设备,LnOE 控制读信号,LnWE控制写信号。SDRAM只用LnWE一条线来控制读/写,低电平写,高电平读。
------------------------------------------------------------
CPU发出32位内存地址 -> 内存控制器 ->A[26:0]->外部内存设备
数据位宽与地址线的接线方式
8位宽 A0 -> A0
16位宽 A1 -> A0 内存控制器根据未发出位A0的值来判断是高8位还是第8位
32位宽 A2 -> A0 内存控制器根据未发出位A0,A1的值来组装数据返回给CPU
高位宽在访问大量数据时,内存控制器需要发送1/2(16bit) 或1/4(32bit)次读写即可,提高访问速度。
怎么确定内存设备的地址范围?
答:1.根据片选信号确定基址。
2.根据地址线确定范围。
----------------------------------------------------------
Nor flash
时序图 MX29LV160DBTI-70G(NOR FLASH).pdf P32-33
CPU 可编程时序 P205
接线
LnOE 读信号
LnWE 写信号
nGCS0 片选
nREST 复位
LDATA[15:0] 数据线 16位宽
LADDR[20:1] 地址线 2^21 = 2MB 容量
寄存器设置
BANKCON0(0x48000004) = 0x0500
Tacc 0b101 80ns>70ns 访问时钟
BWSON(0x48000000) 不用设置
DW0[2:1] 只读不需要设置,根据OM[1:0]电平选择位宽 0b01 16bit 0b10 32bit
通过修改BANKCON0[10:8]可以修改norflash的读取速度。只有在Nor启动下才能感觉到程序的差异。
------------------------------------------------------
SDRAM
步骤
1.内存控制器根据地址发出nGCS6片选
2.根据内存类型(SDRAM)拆分地址
a.L_BANK地址
b.行地址
c.列地址
-> 怎么拆分地址,通过寄存器来配置
3.读写数据
BANK6 寄存器配置 SDRAM
BWSON(0x48000000) = 0x22000000 P207 Bus width & wait status control register
ST6[27] SRAM UB/LB专用引脚 SDRAM不用
WS6[26] 等待信号未用到
DW6[25:24] 0b10 32位宽 -> 0b10 32bit
BANK7不用 配置成与BANK6 一样
BANKCON6(0x4800001C) = 0x00018001 P210
BANKCON7(0x48000020) = 0x00018001
MT[16:15] 0b11 Sync.DRAM 确定内存类型
Trcd[3:2] 0b00 2clock=20ns RAS to CAS delay 行地址到列地址延时 在芯片手册中搜索 Trcd P18 最大21ns
SCAN[1:0] 0b01 9bit 列地址内存块数量
REFRESH(0x48000024) = 0x008404F5 SDRAM refresh control register
REFEN[23] 1 SDRAM 刷新使能
TREFMD[22] 0 SDRAM 刷新模式 0自动刷新
Trp[21:20] 0b00 20ns RAS行访问预充电时间 手册21ns
Tsrc[19:18] 0b01 50ns SDRAM Semi Row cycle time Trc=Tsrc+Trp 行地址保持时间 = 时钟周期-充电时间
Refresh Counter[10:0] 0x4F5(1269) 64ms/8k=7.8us 刷新计数时间
BANKSIZE(0x48000028) = 0xb1 Flexible bank size register 灵活的块大小配置寄存器
BURST_EN[7] 1 突发操作使能
SCKE_EN[5] 1 休眠模式使能
SCLK_EN[4] 1 SCLK 仅在访问时活跃
BK76MAP[2:0] 0b001 BANKCON6/BANK7 64M/64M 内存映射大小
MRSRB6(0x4800002C) = 0x20
MRSRB7(0x48000030) = 0x20
WBL[9] 0 写突发长度 默认值
TM[8:7] 0b00 测试模式 默认值
CL[6:4] 0b010 CAS 列访问后延时 2或3时钟周期 根据芯片手册
BT[3] 0 突发类型 固定值
BL[2:0] 0b000 突发长度 固定值
测试代码
#define BANKCON0 (*(volatile unsigned int *)0x48000004) //volatile 防止编译器优化
#define BWSON (*(volatile unsigned int *)0x48000000) //位宽控制
#define BANKCON6 (*(volatile unsigned int *)0x4800001C)
#define BANKCON7 (*(volatile unsigned int *)0x48000020)
#define REFRESH (*(volatile unsigned int *)0x48000024)
#define BANKSIZE (*(volatile unsigned int *)0x48000028)
#define MRSRB6 (*(volatile unsigned int *)0x4800002C)
#define MRSRB7 (*(volatile unsigned int *)0x48000030)
/* Nor 初始化 */
void norflash_init(void)
{
BANKCON0 = 0x0500; //Tacc 0b101 80ns>70ns 访问时钟
}
/* SDRAM 初始化 */
void sdram_init(void)
{
BWSON = 0x22000000;
BANKCON6 = 0x18001;
BANKCON7 = 0x18001;
REFRESH = 0x8404F5;
BANKSIZE = 0xb1;
MRSRB6 = 0x20;
MRSRB7 = 0x20;
}
/* SDRAM 测试 */
int sdram_test(void)
{
volatile unsigned int *p = (volatile unsigned int *)0x30000000; //BANK6 基址
int i;
// write sdram
for (i = 0; i < 10000; i++)
p[i] = i;
// read sdram
for (i = 0; i < 10000; i++)
if (p[i] != i)
return -1;
return 0;
}
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
波特率115200,8n1
波特率计算公式:UBRDIVn = (int)(UART clock / (buad rate*16))-1 手册P338
uart_0.h
#ifndef __UART0_H__
#define __UART0_H__
#define GPHCON (*(volatile unsigned int *)0x56000070) //volatile 防止编译器优化
#define GPHDAT (*(volatile unsigned int *)0x56000074)
#define GPHUP (*(volatile unsigned int *)0x56000078)
#define UCON0 (*(volatile unsigned int *)0x50000004) // P342 串口0 控制寄存器
#define UBRDIV0 (*(volatile unsigned int *)0x50000028) // P352 波特率
#define ULCON0 (*(volatile unsigned int *)0x50000000) // P341 数据格式
#define UTRSTAT0 (*(volatile unsigned int *)0x50000010)// P347 收发状态寄存器
#define UTXH0 (*(volatile unsigned char *)0x50000020) // P351 发送数据寄存器 CPU小端模式
#define URXH0 (*(volatile unsigned char *)0x50000024) // P351 接收数据寄存器 CPU小端模式
void uart0_init(); /* 115200,8n1 */
int putchar(unsigned char c); //发送字符
unsigned char getchar(void); //接收字符
int puts(const char *s); //发送字符串
#endif
uart_0.c
#include "uart_0.h"
/* 115200,8n1 */
void uart0_init()
{
/* 复用引脚配置
* TXD0 -> GPH2
* RXD0 -> GPH3
*/
GPHCON &= ~((3<<4)|(3<<6)); //清零 P295
GPHCON |= ((2<<4)|(2<<6)); //配置为串口模式
GPHUP &= ~((1<<2)|(1<<3)); //使能内部上拉
/* 设置波特率 */
/* UBRDIVn = (int)(UART clock / (buad rate*16))-1 P338
* UBRDIVn = (int)(50000000 / (115200*16))-1 =26
* UART clock 时钟源 P342 UCON0 -> Clock Selection 00
* 发送接收模式:中断或查询 UCON0-> Transmit/Receie Mode 01
*/
UCON0 = 0x00000005; //PCLK,中断/查询模式 PCLK = 50M
UBRDIV0 = 26; //根据时钟频率计算波特率
/* 设置数据格式 */
ULCON0 = 0x00000003; //8n1: 8个数据位,无校验位,一个停止位
}
/* 发送数据 */
int putchar(unsigned char c)
{
while(!(UTRSTAT0 & (1<<2))); // 0 上一次数据没有发送完毕 Not empty
UTXH0 = c;
return 0;
}
/* 接收数据 */
unsigned char getchar(void)
{
while(!(UTRSTAT0 & (1<<0))); // 0 没有接收到数据 Empty
return URXH0;
}
/* 发送字符串 */
int puts(const char *s)
{
while(*s)
{
putchar(*s);
s++;
}
}
main.c
#include "uart_0.h"
int main(void)
{
unsigned char c;
uart0_init(); /* 115200,8n1 */
puts("hellow ARM!\r\n"); // \r 回到行首 \n 新行
while(1)
{
c = getchar();
if(c=='\r') putchar('\n');
if(c=='\n') putchar('\r');
putchar(c);
}
return 0;
}
Makefile
all:
arm-linux-gcc -c -o start.o start.S
arm-linux-gcc -c -o uart_0.o uart_0.c
arm-linux-gcc -c -o main.o main.c
arm-linux-ld -Ttext 0 start.o uart_0.o main.o -o uart0.elf
arm-linux-objcopy -O binary -S uart0.elf uart0.bin
arm-linux-objdump -D uart0.elf > uart0.dis
clean:
rm *.bin *.o *.elf *.dis
xinxiaoci 说:大神,你玩过飞思卡尔K21?
没玩过,我等晕哥玩的差不多了,我在入手这个8块的板子
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
芯片手册P237
JZ2440 V2 原理图 P12
OM2、OM3=0 引脚接地,MPLL(Main CLK)、UPLL(USB CLK)时钟源为晶振Crystal。
芯片手册P238
MPLL ->FCLK->CPU 最高400MHz
->HCLK ->AHB高速外设总线 最高136MHz
->PCLK->APB 低速外设总线 最高68MHz
UPLL->UCLK->USB设备使用
芯片手册P35
下面是代码,自行验证,启动PLL与不启用PLL差别,LED延时闪烁频率
start.S 代码
.text
.global _start
_start:
/* 关闭看门狗 */
ldr r0,=0x53000000
ldr r1,=0
str r1,[r0]
/* PLL配置---------------------------- */
/* 设置MPLL,FCLK:HCLK:PCLK=400M:100M:50M */
/* 设置lock time 详见手册P255
* LCOKTIME(0x4c000000)=0xFFFFFFFF
*/
ldr r0,=0x4c000000
ldr r1,=0xFFFFFFFF
str r1,[r0]
/* 设置HCLK、PCLK 相对于FCLK的分频系数 详见手册P260
* CLKDIVN(0x4c000014)=0x05 tFCLK:tHCLK:tPCLK=1:4:8
*/
ldr r0,=0x4c000014
ldr r1,=0x05
str r1,[r0]
/* 设置CPU工作于异步模式
* 详见手册P244
*/
mrc p15,0,r0,c1,c0,0
orr r0,r0,#0xc0000000 //R1_nF:OR:R1_iA
mcr p15,0,r0,c1,c0,0
/* 配置MPLLCON 来设置FCLK频率 详见手册P255~256
* 设置MPLLCON(0x4c000004)=(92<<12)|(1<<4)|(1<<0)
* m = MDIV+8 = 92+8 =100
* p = PDIV+2 =1+2 =3
* s = SDIV = 1
* FCLK = 2*m*Fin/(p*2^s)=2*100*12/(3*2^1)=400MHz
*/
ldr r0,=0x4c000004
ldr r1,=(92<<12)|(1<<4)|(1<<0)
str r1,[r0]
/* 一旦设置PLL,就会锁定 lock time 直到PLL输出稳定 */
/* PLL配置--END-------------------------- */
/* 设置内存:sp 栈 */
/* 分辨是nor/nand启动
* 写0到0地址,再读出来;如果读出来是0,表示
* 操作是内部RAM,是nand启动;否则就是nor启动
* 因为nor flash 不向内存那样能直接读写
*/
mov r1,#0
ldr r0,[r1] /* 备份0地址数据 */
str r1,[r1] /* 将0写入0地址 */
ldr r2,[r1] /* 读取0地址数据到r2 */
cmp r1,r2 /* 判断r1 r2是否相等 */
ldr sp,=0x40000000+4096 /* 假设nor启动 */
moveq sp,#4096 /* 如果r1==r2 nand启动 */
streq r0,[r1] /* 恢复原来的值 */
/* 调用main函数 */
bl main
/* 死循环 */
halt:
b halt
led.c
#define GPFCON (*(volatile unsigned int *)0x56000050) //volatile 防止编译器优化
#define GPFDAT (*(volatile unsigned int *)0x56000054)
#define LED1_On GPFDAT&=~(1<<4)//GPF4拉低
#define LED1_Off GPFDAT|=(1<<4)////GPF4拉高
#define LED2_On GPFDAT&=~(1<<5)//GPF5拉低
#define LED2_Off GPFDAT|=(1<<5)////GPF5拉高
#define LED4_On GPFDAT&=~(1<<6)//GPF6拉低
#define LED4_Off GPFDAT|=(1<<6)////GPF6拉高
void delay(volatile unsigned int cnt)
{
while(cnt--);
}
int main()
{
unsigned int temp=0;
/* 初始化LED熄灭 */
LED1_Off;
LED2_Off;
LED4_Off;
/* 配置GPF4/5/6为输出引脚 */
GPFCON&=~((3<<8)|(3<<10)|(3<<12));//清除位
GPFCON|=((1<<8)|(1<<10)|(1<<12));//GPF4/5/6配置为输出
while(1)
{
if(++temp>2) temp=0;
delay(100000);
if(temp==0) LED1_On; else LED1_Off;
if(temp==1) LED2_On; else LED2_Off;
if(temp==2) LED4_On; else LED4_Off;
}
return 0;
}
xinxiaoci 说:https://blog.csdn.net/l_backkom/article/details/41512675
是不是跟这个一样主芯片 M21G9VMC -> MK21DN512VMC5
http://cache.freescale.com/files/32bit/doc/data_sheet/K21P80M50SF4.pdf
第5页Original part number Alternate part number
MK21DX128VMC5 M21GGVMC
MK21DX256VMC5 M21GHVMC
MK21DN512VMC5 M21G9VMC开机自毁,怕怕,估计已经爆掉一个了。
我们的口号是:挖坑行,填坑你更行!晕哥,你把它填上
https://blog.csdn.net/l_backkom/article/details/41512675
是不是跟这个一样
主芯片 M21G9VMC -> MK21DN512VMC5
http://cache.freescale.com/files/32bit/doc/data_sheet/K21P80M50SF4.pdf
第5页
Original part number Alternate part number
MK21DX128VMC5 M21GGVMC
MK21DX256VMC5 M21GHVMC
MK21DN512VMC5 M21G9VMC
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
预处理->编译->汇编->链接
预处理(-E *.i):处理源码代码中的宏定义,#开头的文件,预处理为 *.i文件
gcc -E -o hello.i hello.c
编译(-S *.S):将预处理后的文件*.i编译为汇编代码文件*.S
gcc -S -o hello.S hello.c
汇编(-c *.o):编译和汇编。将汇编代码问价*.S 汇编为(OBJ) *.o文件
gcc -c -o hello.o hello.S
链接:将*.o文件链接为一个可执行文件
gcc -o hello hello.o
----------------------------------------------------
文件后缀与编译器默认动作关系
.c c源程序 预处理、编译、汇编
.C c++源程序 预处理、编译、汇编
.cc c++源程序 预处理、编译、汇编
.cxx c++源程序 预处理、编译、汇编
.m Objective-C源文件 预处理、编译、汇编
.i 预处理后的c文件 编译、汇编
.ii 预处理后的c++文件 编译、汇编
.s 汇编语言源程序 汇编
.S 汇编语言源程序 预处理、汇编
.h 预处理器文件 通常不出现在命令行上
----------------------------------------------------
ldd hello //显示所链接的动态库
gcc -0 -static hello hello.o //静态链接,把库直接链接在程序内,所以程序体积较大
-----------------------------
gnu make 中文手册
https://blog.csdn.net/Sun_Jianhua/article/details/494002
-----------------------------
Makefile
test:a.o b.o
gcc -o test a.o b.o
a.o:a.c
gcc -c -o a.o a.c
b.o:b.c
gcc -c -o b.o b.c
-----------------------------
Makefile 格式
目标文件:依赖文件1 依赖文件2 ...
[TAB]命令
编译命令执行条件:
1.当依赖文件比目标文件新
2.依赖文件不存在
-----------------------------
特殊符号
test:a.o b.o c.o
gcc -o test $^
%.o:%c
gcc -c -o $@ $<
%.o %c : %号表示通配符
$^ 表示所有的依赖 a.o b.o c.o
$@ 表示目标文件
$< 表示第一个依赖文件
--------------------------
假想目标
clean:
rm *.o test
.PHONY:clean
如果不定义 .PHONY:clean 当编译目录有clean文件名时,make clean 命令将不会被执行
-------------------------
变量:简单变量(即时变量)、延时变量、export
A:=xxx # A的值在定义时即确定
B = xxx # B的值用到是才确定
举例
----------------
A :=$(C)
B =$(C)
C =abc
all:
@echo A=$(A) # A=
@echo B=$(B) # B=abc123456
C +=123456
----------------
:= #即时变量
= #延时变量
?= #延时变量,第一次定义才有效,如果前面已有该变量,则不执行
+= #附加,它是即时变量还是延时变量取决于前面的定义
可以在make时传入变量值
make a=ssss
---------------------------------------
Makefile 函数
a. $(foreach var,list,text)
b. $(filter pattern...,text) # 在text中提取符合pattern格式的值
$(filter-out pattern...,text) # 在text中提取不符合pattern格式的值
c. $(wildcard pattern) # pattern定义了文件名的格式
# wildcard取出其中存在的文件
d. $(patsubst pattern,replacement,$(var)) # 从列表中取出每一个值
# 如果符合pattern则替换为replacement
例 a>
--------------
A = a b c
B =$(foreach f,$(A),$(f).o)
all:
@echo B = $(B)
# 执行结果:B = a.o b.o c.o
--------------
例 b>
--------------
A = a b/ c d/
B =$(filter %/,$(A)) # 匹配的字符串
C =$(filter-out %/,$(A)) # 不匹配的字符串
all:
@echo B = $(B)
@echo C = $(C)
# 执行结果:
# B = b/ d/
# C = a c
--------------
例 c>
--------------
file =$(wildcard *.c) # 获得当前文件夹下以 .c 结尾的文件
A = b.c c.c e.c d.c
file2 = $(wildcard $(A)) # 以A变量中的值,去匹配所在文件夹下的文件名
all:
@file = $(file)
# 假设Makefile所在文件夹下有 a.c b.c c.c
# 执行结果:
# file = a.c b.c c.c
# file2 = b.c c.c
--------------
例 d>
--------------
A = a.c b.x c.c e.c d.c
rep = $(patsubst %.c,%.o,$(A))
all:
@echo rep = $(rep)
# 执行结果:
# rep = a.o b.x c.o e.o d.o
--------------
自动生成依赖
https://blog.csdn.net/qq1452008/article/details/50855810
gcc -M c.c # 查看c.c的依赖文件
gcc -M -MF c.d c.c # 生成c.c的依赖文件 c.d
gcc -c -o c.o c.c -MD -MF c.d # 编译同时生成依赖文件 c.d
---------------------------------------
Makefile综合练习
SRCS = $(wildcard *.c) # 获取当前目录下所有的 .c文件名列表
OBJS = $(patsubst %.c,%.o,$(SRCS)) # 生成目标所依赖的 .o文件名列表
DEPS = $(patsubst %,.%.d,$(OBJS)) # 生成依赖文件 .%.d 文件名列表
CFLAGS = -Werror -Iinclude # 所有警告都当做错误来处理 去./include目录查找头文件
.PHONY:clean # 假想目标,防止有clean同名文件而无法执行clean命令
all: main
-include $(DEPS) # 加载所有依赖.%.d文件 '-'号的作用:加载错误时,会继续执行 make,主要是考虑到首次 make 时,目录中若不存在 '.*.d' 文件时,加载便会产生错误而停止 make 的执行
%.o:%.c
gcc $(CFLAGS) -c -o $@ $< -MD -MF .$@.d -MP # $@ 目标文件 $< 第一个依赖文件.c 并将.c的依赖(头文件)输出到 .*.o.d
main: $(OBJS)
gcc -o $@ $^ #注释:$^:表示所有的依赖文件$(OBJS) $@:表示目标文件
clean:
rm -f *.d *.o main
------------
我来检查作业,不错!100分!
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
低位存放在低地址:小字节序 小端 little endian
高位存放在低地址:大字节序 大端 big endian
一般ARM芯片都是小字节顺序
编写c程序控制led
a.谁来调用main函数
b.main函数中的局部变量保存在内存中,这个内存地址是多少?
答:我们需要写一个汇编代码,给main函数设置内存(栈),然后调用main函数;局部变量保存在栈中,栈对应一块内存。
----------------------------------------------
start.S 汇编代码
.text
.global _start
_start:
/* 设置内存:sp 栈 */
ldr sp,=4096 /* nand启动设置在内部ram的顶部4k位置 */
//ldr sp,=0x40000000+4096 /* nor启动 */
/* 调用main函数 */
bl main
/* 死循环 */
halt:
b halt
-----------------------------------------------
led.c c代码
int main()
{
unsigned int *pGPFCON = (unsigned int *)0x56000050;//控制寄存器
unsigned int *pGPFDAT = (unsigned int *)0x56000054;//数据寄存器
/* 配置GPF5为输出引脚 */
*pGPFCON=0x400;
/* 设置GPF5输出0 */
*pGPFDAT=0;
return 0;
}
----------------------------------------------
Makefile 文件
all:
arm-linux-gcc -c -o start.o start.S
arm-linux-gcc -c -o led.o led.c
arm-linux-ld -Ttext 0 start.o led.o -o led.elf
arm-linux-objcopy -O binary -S led.elf led.bin
arm-linux-objdump -D led.elf > led.dis
clean:
rm *.bin *.o *.elf *.dis
---------------------------------------------
led.dis反汇编
led.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e3a0da01 mov sp, #4096 ; 0x1000
4: eb000000 bl c <main> ;调用main函数 并把返回地址保存在lr中 lr=0x08
00000008 <halt>:
8: eafffffe b 8 <halt>
0000000c <main>:
c: e1a0c00d mov ip, sp ;ip保存了栈顶的值 4096
10: e92dd800 stmdb sp!, {fp, ip, lr, pc} ;保存fp,ip,lr,pc 更新sp的值 sp=4096-4*4=4080
14: e24cb004 sub fp, ip, #4 ; 0x4 ;fp=ip-4=4096-4=4092
18: e24dd008 sub sp, sp, #8 ; 0x8 ;sp=sp-8=4080-8=4072
1c: e3a03456 mov r3, #1442840576 ; 0x56000000 ;r3=0x56000000
20: e2833050 add r3, r3, #80 ; 0x50 ;r3=r3+0x50=0x56000050 pGPFCON寄存器
24: e50b3010 str r3, [fp, #-16] ;将r3的值保存在fp-16的内存(栈)中 fp-16=4092-16=4076 (*(int *)4076)=0x56000050
28: e3a03456 mov r3, #1442840576 ; 0x56000000 ;r3=0x56000000
2c: e2833054 add r3, r3, #84 ; 0x54 ;r3=r3+0x54=0x56000054
30: e50b3014 str r3, [fp, #-20] ;将r3的值写入fp-20的内存(栈)中 fp-20=4092-20=4072 (*(int *)4072)=0x56000054
34: e51b2010 ldr r2, [fp, #-16] ;读取fp-16内存中的值到r2 r2=(*(int *)4076)=0x56000050
38: e3a03b01 mov r3, #1024 ; 0x400 ;r3=0x400 GPF5寄存器
3c: e5823000 str r3, [r2] ;将r3的值写入r2指向的内存中 (*(int *)0x56000050)=0x400 GPF5为输出模式
40: e51b2014 ldr r2, [fp, #-20] ;同上面三行代码(*(int *)0x56000054)=0 输出低电平 点亮LED
44: e3a03000 mov r3, #0 ; 0x0
48: e5823000 str r3, [r2]
4c: e3a03000 mov r3, #0 ; 0x0 ;r3=0
50: e1a00003 mov r0, r3 ;r0=r3=0 r0-r3 用于调用者与被调用者之间传递参数
54: e24bd00c sub sp, fp, #12 ; 0xc ;sp=fp-12=4092-12=4080
58: e89da800 ldmia sp, {fp, sp, pc} ;从4080位置开始还原进入函数之前的fp=fp,sp=ip,pc=lr=0x08 所以程序跳转至0x08位置执行
Disassembly of section .comment:
00000000 <.comment>:
0: 43434700 cmpmi r3, #0 ; 0x0
4: 4728203a undefined
8: 2029554e eorcs r5, r9, lr, asr #10
c: 2e342e33 mrccs 14, 1, r2, cr4, cr3, {1}
10: Address 0x10 is out of bounds.
-----------------------------------------------------
汇编指令
add,sub
举例:
add r0,r1,#4 ;r0=r1+4
add r0,r1,r2 ;r0=r1+r2
sub r0,r1,#4 ;r0=r1-4
sub r0,r1,r2 ;r0=r1-r2
bl break link 跳转并保存返回地址
举例:
bl c <main> ;跳转到内存0x0c 并保存下一条指令的地址,用于调用返回
ldm ;many 读内存,写入多个cpu寄存器
stm ;把多个cpu寄存器的值写入内存
ldmia ;ia 先读后蹭
stmdb ;db 先减后存
举例:
stmdb sp!,{fp,ip,lr,pc} ;1> 大括号中的cpu寄存器的存取顺序与,书写顺序无关。高编号cpu寄存器存
在高地址。
;2> sp! : ! 表示sp=sp最终被修改后的值,假如sp初始值是4096 那么运行该
条汇编后sp的值为4080
执行流程:
1.sp=4096-4=4092
2.将pc寄存器的值存入4092指向的内存(栈)中
3.sp=4092-4=4088
4.将lr寄存器的值存入4088指向的内存(栈)中
5.sp=4088-4=4084
6.将ip寄存器的值存入4084指向的内存(栈)中
7.sp=4084-4=4080
8.将fp寄存器的值存入4080指向的内存(栈)中
9.将当前的栈指针更新
->4096
pc值
->4092
lr值
->4088
ip值
->4084
fp值
->4080
ldmia sp,{fp,sp,pc} ; sp 无 ! 号,表示sp修改后的值不存入sp
; 先读后增,假如sp的初始值为4080
执行流程:
1.fp=内存(栈)中fp的值(4080内存指向的值)
2.sp=4080+4=4084
3.sp=内存(栈)中ip的值(4084内存指向的值)
4.sp=4084+4=4088
5.pc=内存(栈)中lr的值(4088内存指向的值)
6.sp=4088+4=4092
《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》
---------------------------------------
GPIO
General Purpose intput output 通用输入输出
---------------------------------------
网络标号:nLED_1
点亮条件:低电平点亮
对应IO:GPF4
准备知识:
S3C2440A芯片手册
9.I/O Ports
PORT F CONTROL REGISTERS 292页
GPFCON 0x56000050 //GPIO F 的配置寄存器 配置输入/输出/外部中断
GPFDAT 0x56000054 //GPIO F 数据寄存器 控制电平的高低
-----------------------------------------
cpu启动过程
Nor 启动
Nor Flash 基地址为0地址,cpu片内4K RAM基地址为0x4000 0000。
CPU从Nor Flash上读取第一条指令。
Nand Flash 启动
片内4K RAM 基地址为地址线上的0地址,Nor Flash 不可用。
Nand控制器把Nand Flash 前4K 数据复制到片内4K RAM ,然后CPU从片内4K RAM 执行第一条命令。
CPU都是0地址开始执行。
-----------------------------------------
一些简单的ARM 汇编
1.LDR load 读内存,32位 4字节
LDR R0,[R1] ;设R1的值为0x000000EF 读取 0x000000EF 地址上的数据 4字节 保存到R0中
2.STR store 写内存指令
STR R0,[R1] ;设R1的值为0x000000EF 将R0寄存器的值写到 0x000000EF 地址指向的内存中。
3.B break 跳转
4.mov move 移动
mov R0,#0x100 ;R0 = 0x100 0x100 是立即数一条机器码能放下
mov R0,#0x12345678 ;这是一条错误指令,一条ARM指令是32位,0x12345678已经占用了32位,一条机器码放不下这条指令,以后遇到再说
5.伪指令
LDR R0,=#0x12345678 ;可以放置任意数 而 mov R0,#0x100 只能处理立即数
----------------------------------------
代码文件:led_on.S
.text
.global _start
_start:
/* 配置GPF4为输出模式 */
ldr r1,=0x56000050
ldr r0,=0x100 /* mov r0,#0x100 */
str r0,[R1]
/* GPF4为输出低,点亮led */
ldr r1,=0x56000054
ldr r0,=0 /* mov r0,#0 */
str r0,[R1]
/* 死循环 */
halt:
b halt
------------------------------------------
gcc 编译
1.命令行
arm-linux-gcc -c -o led_on.o led_on.S //预编译
arm-linux-ld -Ttext 0 led_on.o -o led_on.elf //链接
arm-linux-objcopy -o binary -S led_on.elf led_on.bin //生成文件
arm-linux-objdump -D led_on.elf > led_on.dis //翻译成汇编
2.Makefile 文件
all:
arm-linux-gcc -c -o led_on.o led_on.S
arm-linux-ld -Ttext 0 led_on.o -o led_on.elf
arm-linux-objcopy -O binary -S led_on.elf led_on.bin
clean:
rm *.bin *.o *.elf
然后执行
make 或 make clean
-----------------------------------------
led_on.dis 反汇编文件
led_on.elf: file format elf32-littlearm
Disassembly of section .text:
00000000 <_start>:
0: e59f1014 ldr r1, [pc, #20] ; 1c <.text+0x1c>
4: e3a00c01 mov r0, #256 ; 0x100
8: e5810000 str r0, [r1]
c: e59f100c ldr r1, [pc, #12] ; 20 <.text+0x20>
10: e3a00000 mov r0, #0 ; 0x0
14: e5810000 str r0, [r1]
00000018 <halt>:
18: eafffffe b 18 <halt>
1c: 56000050 undefined
20: 56000054 undefined
-----------------------------------------
扩展知识:
MOV立即数:
立即数保存在32位机器码的后12位,在这12位数中,设低8位为immed_8,高4位为rotate,那么 立即数=immed_8循环右移(2*rotate)位 如0x100 在机器码后12位的表示为 1100 0000 0001 如:上面反汇编的内存4 机器码为 e3a00c01 后12位 c01即为立即数0x100在机器码中的表示
流水线:
当前执行地址A的指令时,已经对A+4地址指令进行了译码,已经在读取地址A+8的指令,所以 PC = A+8 (PC:Program counter 程序计数器)
如:上面反汇编 内存0 ldr r1, [pc, #20] r1=[0+8+20]=[0x1c] 将0x1c内存中的数值0x56000050 传递给r1
寄存器对于CPU来说,也是内存,与普通内存没什么区别。
寄存器别名对应关系:
R1 a1
R2 a2
R3 a3
R4 a4
R5 v1
r6 v2
r7 v3
r8 v4
r9 v5
r10 v6
r11 fp
r12 ip
r13 sp stack pointer 栈指针
r14 lr link register 返回地址
r15 pc program counter 程序计数器
find 目录名 [选项] 查找条件
1.find /home/ -name “*.txt” //查找/home/目录下的所有*.txt文件
2.find /home/ -mtime -2 //两天内改变过的文件
3.find /home/ -name “temp” //查看是否存在temp目录
———————————————–
grep [选项] [查找模式] [文件名]
1.grep -rn “字符串” 文件名 递归查询文件中字符串的,并显示行号
-r recursive 递归
-n number 显示行号
-w 全匹配
———————————————–
file 识别文件类型 Linux下皆文件
file ~/.bashrc 为ASCII 编码的text类型
file ~/.vimrc 为UTF-8 Unicode 编码的text类型
file ~/Pictures/* 如图形文件JPEG/PNG/BMP格式
file ~/100ask/ 为directory表明这是一个目录
file /bin/pwd 出现 ELF 64-bit LSB executable,即为ELF格式的可执行文件
file /dev/* 出现character special(字符设备文件)、 block special(块设备文件)等
———————————————
压缩命令
gzip .gz
bzip2 .bz2
压缩
1.gzip/bzip2 文件名 压缩后删除源文件
2.gzip/bzip2 -k 文件名 压缩后保存文件
3.不能压缩目录
解压缩
1.gzip/bzip2 -d 文件名 解压缩后删除压缩包文件
2.gzip/bzip2 -kd 文件名 解压缩后保留压缩包文件
———————————————–
文件打包命令
tar 常用选项
-c (create)表示创建,用来生成文件包
-x 表示提取,从包中提取文件
-t 查看压缩的文件
-z 使用gzip方式进行处理,与c结合表示压缩,与x结合表示解压
-j 使用bzip2方式进行处理,与c结合表示压缩,与x结合表示解压
-v (verbose)详细报告,tar处理的信息
-f 表示一个文件,后面接着一个文件名
-C 指定目录,解压到指定目录
举例
1.tar打包、gzip压缩
1)压缩
tar -czvf 压缩文件名 目录名
如:tar czvf dira.tar.gz dira
注意:
tar -czvf 与 tar czvf 效果一样,后面统一取消 –
2)查看
tar tvf 压缩文件名
如:
tar tvf dira.tar.gz
3)解压
tar xzvf 压缩文件名
tar xzvf 压缩文件名 -C 指定目录
如:
tar xzvf dira.tar.gz 解压到当前目录
tar xzvf dira.tar.gz -C /home/book 解压到 home/book
2.tar打包、bzip2压缩
同上,只需将 z 换成 j
vim编辑器配置
命令行执行如下操作
cd /etc/vim
cp vimrc ~/.vimrc
cd ~
gedit .vimrc
在文本结尾粘贴如下代码,便于我们操作vim
“关闭兼容功能
set nocompatible
“显示行号
set number
“编辑时 backspace 键设置为2个空格
set backspace=2
“编辑时 tab 键设置为4个空格
set tabstop=4
“设置自动对齐为4个空格
set shiftwidth=4
“搜索时不区分大小写
set ignorecase
“搜索时高亮显示
set hlsearch
===========================================
vi的三种模式
1.一般模式 光标移动、复制、粘贴、删除
2.编辑模式 编辑文本
3.命令行模式 查找、替换、保存、退出
常用的vi命令,不需要几太多,够用就行
——————————————-
光标快速定位
h j k l 左 下 上 右光标移动
ngg 第n行 行首
G 文本最后一行
0 当前行行首 数字0
$ 当前行行尾
fx 跳转到当前行x字母所在的位置
——————————————–
删除、复制、粘贴
yy 复制当前行
nyy 从当前行开始,复制n行
x 删除光标所在位置的字符
dd 删除当前行
ndd 从当前行开始删除n行
p 粘贴
粘贴系统剪切板内容编辑模式按鼠标中键
u 撤销操作
————————————
查找、替换
/字符串 从光标所在位置向下查找 n next 下一个
:%s/p1/p2/g 将文件中所有的p1替换为p2
:%s/p1/p2/gc 替换将进行询问
s substitute 替换
g global 全局
c cofirm 确认
————————————-
编辑模式
i 在光标位置前插入
a 在光标位置后插入
o 插入新行
————————————-
保存退出
:w 保存
:wq 保存退出
:q 退出
:q! 退出不保存
我现在直接买VPS撸
嗯,看到了!晕哥是有钱人,收不收小弟!罩着我,/斜眼笑
这些命令算是熟悉下Linux操作系统吧
————————————————–
帮助命令
man 显示的比较详细,多用这个帮助命令
举例
man man
man 1 ls shell
man 1 gcc 可执行程序
man 2 open 系统调用
man手册
1.可执行程序或shell命令
2.系统调用
3.库调用
4.特殊文件,在/dev下的设备文件
5.文件格式合约定
6.游戏程序
7.杂项
8.系统管理员使用的管理命令
9.内核相关
info
help
—————————————————
pwd print working directory 打印当前路径
—————————————————
cd change directory 改变路径、切换路径
简化输入
cd ~ 用户家目录 /home/XXX/ /home/下对应用户名目录
cd . 当前目录
cd .. 上一级目录
cd ../.. 切换到上上级目录
cd – 前一次目录
—————————————————
ls list 列出目录内容
ls -l (list) 显示目录下文件更详细的信息
-a (all) 显示隐藏文件
-h (human-able) 以M、G为单位显示文件大小
—————————————————
mkdir make directory 创建目录
mkdir -p dir1/dir2 parents 创建多级目录父目录和子目录,如果父目录不存在,需要加-p参数
—————————————————
rmdir remove directory 删除目录
rmdir 不能删除非空目录
=====================================================
文件的操作
touch 新建文件
同一目录不能创建同名的文件
—————————————————-
mv 修改文件名、移动文件
mv 旧文件名 新文件名 ->修改文件名
mv 旧文件夹 新文件夹 ->修改文件夹名
mv 文件名 目录名 ->移动文件
—————————————————-
cp copy 复制文件或目录
cp tex1 tex2 将文本tex1的内容拷贝到tex2
cp -r dir1 dir2 将dir1目录下的所有文件拷贝到dir2下 -r 递归复制
cp -i dir1 dir2 如果有重名的文件提示是否覆盖
cp 常用的参数有: -i,-r,-f,d等
————————————————–
rm remove 删除文件(目录)
常用选项:
-i (interactive)交互的缩写,删除文件(目录)之前,要求确认
-r (recursie)递归的缩写,递归删除指定目录下的子目录和文件
-f (force)强制的缩写,强制删除
rm的常用参数有:-i,-r,-f,-d等。
————————————————–
文件查看
cat 查看文件内容
cat file1 将file1的内容打印到标准输出中
cat file1 file2 依次将file1 file2的内容打印到标准输出中
cat -n file1 打印时显示行号
类似查看命令:more、less、tail等
也可以用 vi、gedit工具
————————————————–
清屏
clear 将屏幕翻页,并没有清除
reset 重新初始化屏幕
虚拟机软件版本:VMware 14
下载地址:VMware-workstation-full-14.0.0-6661328.exe
序列号,自己百度下。
开发环境用韦老师的 Ubuntu16.04,暂时不研究这个环境的安装配置过程,不想自己过多的分心。
下载地址: https://eyun.baidu.com/s/3b1UtLc 企业网盘
位置:\005_ARM裸机1期加强版\虚拟机环境ubuntu16.04\ubuntu-16.04.2-x64-100ask.rar
解压,添加进vmware即可
学习线路:
单片机-> bootloader -> Linux/驱动 -> Qt
统启动过程: bootloader -> linux内核 -> 挂载根文件系统 -> 运行APP
1.由于 bootloader 需要读取引导Linux内核,所以bootloader要有读取flash/SD的能力
2.要显示信息,所以要操作LCD
3.要提高运行速度,所以要初始化时钟,和内存
3.要远程下载内核,要设置网卡
所以有些外设的硬件操作在bootloader和内核驱动中应该都有。
--------------------------------------------------------------
bootloader相对于电脑的bios
根文件系统相当于windows的C盘,系统盘。
--------------------------------------------------------------
Linux内核也需要有操作硬件的能力,所以需要驱动程序。
驱动程序=软件框架+硬件操作
硬件操作就需要看硬件原理图和芯片手册及寄存器的读写,这部分应该类似于单片开发。
U-BOOT
u-boot 编译烧写体验
https://whycan.cn/t_1266.html
u-boot Makefile 简单理解分析
https://whycan.cn/t_1267.html
u-boot 源码分析 第一阶段源码分析
https://whycan.cn/t_1272.html
u-boot 源码分析 第二阶段源码分析
https://whycan.cn/t_1273.html
自定义u-boot命令 及u-boot 链接脚本 .u_boot_cmd 段的理解
https://whycan.cn/t_1295.html
Linux_kernel 简单跟踪分析001
https://whycan.cn/t_1539.html
Linux_kernel 简单跟踪分析002 最小根文件系统的制作(网络文件系统挂载)
https://whycan.cn/t_1540.html
最近一段时间工作进度有点紧张,总之烦心的事情挺多,产品正式生产,撸了两个私活。下班之后只想睡觉,有点力不从心。是不是自己变懒了,还是时间没有合理规划。请问小伙伴们是怎么保持精力充沛的?我怎么晚上8点多就困的不行。
一直从事单片机的开发工作,最近想更进一步学习,所以打算入坑嵌入式Linux。对于一个搞单片机的菜鸟来说整个Linux体系过于庞大,无从下手,或许是没有找到对入门的方法,或许是过于浮躁。与其自己盲目的挣扎,不如先随便选个方向走下去;今天买了韦老师的2440开发版和教程,打算看下去。准备把自己的学习过程记录下来,供和我一样从单片机转过来搞Linux的朋友一个参考。由于平时要上班,全是业余时间来看这些,所以进度不会太快!中间有理解不对的地方,请路过的兄弟能及时纠正我错误的概念。
开发准备
学习线路和一些基本概念
https://whycan.cn/t_1082.html
安装虚拟机和开发环境
https://whycan.cn/t_1089.html
入门shell命令
https://whycan.cn/t_1090.html
vim编辑器配置和常用命令
https://whycan.cn/t_1106.html
Linux进阶指令
https://whycan.cn/t_1107.html
裸机开发
点亮led_汇编
https://whycan.cn/t_1120.html
c程序控制led灯及反汇编代码分析
https://whycan.cn/t_1128.html
GCC常用编译指令及Makefile文件组织编译
https://whycan.cn/t_1140.html
S3C2440内部时钟及高速时钟配置
https://whycan.cn/t_1145.html
S3C2440-裸机-串口
https://whycan.cn/t_1154.html
内存控制器配置 nor/SDRAM
https://whycan.cn/t_1165.html
将程序搬运至SDRAM运行--链接脚本与代码重定位
https://whycan.cn/t_1170.html
异常与中断概述 UND SVC
https://whycan.cn/t_1197.html
中断异常--外部中断配置
https://whycan.cn/t_1202.html
中断异常--定时器中断
https://whycan.cn/t_1218.html
NOR Flash 原理及读写擦除操作
https://whycan.cn/t_1255.html
NAND Flash 读写擦除操作
https://whycan.cn/t_1258.html
CACHE 缓存的理解
https://whycan.cn/t_1262.html
MMU 的概念及原理
https://whycan.cn/t_1263.html
裸机部分暂时就更新到这里了,其他的都是一些外设的配置操作了,与单片区别不大。
发表下自己的学习感受,可能不是太靠谱,主要说一下与单片机的一些区别:
1. 从start.s 中的汇编一步一步实现 C 语言的调用,设置栈
2. Makefile 文件的组织编译
3. 代码的搬运和重定位
4. 异常向量表,异常的现场保护与恢复
5. 高速缓存 和 MMU 单片机应该没有,最大的区别应该就是这个,其他几条只是加深了一些概念的理解
标题即为诉求:
先说一下我自身的状况,由于工作和学习经历,我一直从事单片机开发工作,一直用的stm8/stm32f103系列的芯片做产品的开发。
现在想更进一步学习Linux操作系统,主要是想做一些嵌入式触摸界面程序,通过485或CAN网络来采集MCU小模块的数据进行远程读取或参数设置。
目前正在自学Qt,主要在windows桌面练习。
现在手上的有荔枝派 zero 的小板子,仅仅是能把各位大神编译好的系统烧写进板子,然后板子就开始吃灰了。感觉整个系统过于庞大感觉无从下手。
希望能基于lichee_zero 进行常用的外设(SPI、IIC、串口) 驱动编写、外设的操作、Qt移植。我应该看哪些书籍才能一步步实现自己的技术小目标,求推荐。
我应该先学什么,在学什么,然后学什么?希望过来的小哥哥小姐姐指点迷津!
页次: 1