您尚未登录。

#1 Re: S3C2440/S3C2416/S3C6410/S5PV210 » Linux_kernel 简单跟踪分析001 » 2018-08-22 08:28:21

晕哥 说:

https://whycan.cn/t_1075.html

xiaoci 真是一个努力的朋友,挖坑网见证小白到大神的过程!

谢谢晕哥!最近的事情有点忙,耽误太多时间,前同事离职,几个烂尾项目都压在我手上,老板一直也在催进度!大女儿刚幼儿园报名要适应节奏,小孩要适应,家长也要适应。二孩还有一个月左右出生,老婆的脾气也是被现实生活折磨的非常敏感易怒,我也非常能理解她的心情,感觉自己在感情上成熟了不少(怕老婆了!!)。总之一言难尽,生活本就如此,有苦有甜吧。然后就是自己时间也安排的不是太合理,学习被一些乱七八糟的事情打乱,有点浮躁。但我不会放弃,最近可能会优先清理手头的烂尾工作,晚上抽时间出来看书学习,论坛进度更新。死磕到底,不会放弃。

#2 S3C2440/S3C2416/S3C6410/S5PV210 » Linux_kernel 简单跟踪分析002 最小根文件系统的制作(网络文件系统挂载) » 2018-08-21 20:28:27

xinxiaoci
回复: 3

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 //显示最新的信息 并清除

#3 S3C2440/S3C2416/S3C6410/S5PV210 » Linux_kernel 简单跟踪分析001 » 2018-08-21 20:22:10

xinxiaoci
回复: 8

内核启动流程分析
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 章

#5 Re: 技术人生/软件使用技巧/破解经验/技术吐槽/灌水 » 天猫精灵 AI音箱〔方糖〕开放购买了 » 2018-06-12 19:50:21

看到你的帖子我也去拍了一个,每次唤醒必须的先叫它的名字“天猫精灵”,对话太僵硬,对它不能期望太高。但这个价格,拿来听听音乐还不错。

#6 S3C2440/S3C2416/S3C6410/S5PV210 » 自定义u-boot命令 及u-boot 链接脚本 .u_boot_cmd 段的理解 » 2018-06-12 19:32:17

xinxiaoci
回复: 1

《单片机小白转嵌入式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
};

#7 S3C2440/S3C2416/S3C6410/S5PV210 » u-boot 源码分析 第二阶段源码分析 » 2018-06-08 16:52:29

xinxiaoci
回复: 1

《单片机小白转嵌入式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 (&params);//起始标签
     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 (&params);
#endif
#ifdef CONFIG_REVISION_TAG
    setup_revision_tag (&params);
#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

----------------------------------------------------------

#8 S3C2440/S3C2416/S3C6410/S5PV210 » u-boot 源码分析 第一阶段源码分析 » 2018-06-08 16:15:53

xinxiaoci
回复: 2

《单片机小白转嵌入式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

#9 S3C2440/S3C2416/S3C6410/S5PV210 » u-boot Makefile 简单理解分析 » 2018-06-06 18:07:10

xinxiaoci
回复: 0

《单片机小白转嵌入式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

#########################################################################

写的比较简陋,个人只是打算简单的了解下,不打算深入分析,有兴趣深入理解的小伙伴可以百度。

#10 S3C2440/S3C2416/S3C6410/S5PV210 » u-boot 编译烧写体验 » 2018-06-06 17:48:41

xinxiaoci
回复: 0

《单片机小白转嵌入式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. 串口 打印/控制

#11 S3C2440/S3C2416/S3C6410/S5PV210 » MMU 的概念及原理 » 2018-06-05 18:09:29

xinxiaoci
回复: 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 变换后的虚拟地址。

#12 S3C2440/S3C2416/S3C6410/S5PV210 » CACHE 的概念及原理 » 2018-06-05 18:04:59

xinxiaoci
回复: 1

《单片机小白转嵌入式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   

-----------------------

#13 Re: 计算机图形/GUI/RTOS/FileSystem/OpenGL/DirectX/SDL2 » step by step 把 LittleVGL porting到新唐N32903上去裸奔 » 2018-06-04 19:54:24

晕哥 说:
xinxiaoci 说:
晕哥 说:

本站网友100积分以上,也就是说发100个帖子,不限主题与回复,可以免费获取整个工程源码,
如果您是土豪,也可以支付人民币100元用于小站建设。

100积分有点多啊 ! 这要到什么时候了

你有板子吗?
我发给你.
QQ 516333132

谢谢了,没有板子,现在精力有限,等我把s3c2440玩通了在找你要,嘿嘿!

#14 Re: 计算机图形/GUI/RTOS/FileSystem/OpenGL/DirectX/SDL2 » step by step 把 LittleVGL porting到新唐N32903上去裸奔 » 2018-06-04 15:28:53

晕哥 说:

本站网友100积分以上,也就是说发100个帖子,不限主题与回复,可以免费获取整个工程源码,
如果您是土豪,也可以支付人民币100元用于小站建设。

100积分有点多啊 ! 这要到什么时候了

#15 S3C2440/S3C2416/S3C6410/S5PV210 » NAND Flash 读写擦除操作 » 2018-06-04 15:07:42

xinxiaoci
回复: 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
----------------------------------------------------------

#16 Re: S3C2440/S3C2416/S3C6410/S5PV210 » NOR Flash 原理及读写擦除操作 » 2018-06-04 05:57:19

晕哥 说:

这种并行NOR FLASH已经是过去式了,新的SOC基本已经绝迹了,现在都是 spi/qspi flash(nor, nand)
比如w25q128/256, mx25l128/256等.

嗯嗯,谢谢晕哥提醒,以后知道该侧重哪些地方了

#17 S3C2440/S3C2416/S3C6410/S5PV210 » NOR Flash 原理及读写擦除操作 » 2018-06-03 16:43:43

xinxiaoci
回复: 2

《单片机小白转嵌入式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;
		}
	}
}

#18 Re: S3C2440/S3C2416/S3C6410/S5PV210 » 中断异常--定时器中断 » 2018-05-23 12:41:58

晕哥 说:

S3C2440已经不适合做产品了, BOM成本太高。

等你这个系列做完, 希望来一波 《新唐的ARM9(NUC972, N32926)的从入门到放弃》.

新唐的裸奔例程非常丰富, Keil的工程。

晕哥,我真有这个想法!s3c2440 作为入门,下一个准备撸新唐的这款芯片。

#19 S3C2440/S3C2416/S3C6410/S5PV210 » 中断异常--定时器中断 » 2018-05-23 09:12:11

xinxiaoci
回复: 5

《单片机小白转嵌入式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); 
}

#20 S3C2440/S3C2416/S3C6410/S5PV210 » 中断异常--外部中断配置 » 2018-05-17 09:18:27

xinxiaoci
回复: 0

《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》



中断框图
1.jpg

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);	
}

#21 S3C2440/S3C2416/S3C6410/S5PV210 » 异常与中断概述 UND SVC » 2018-05-16 14:34:41

xinxiaoci
回复: 0

《单片机小白转嵌入式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 修饰 防止编译出错。

-----------------------------------------

#22 Re: S3C2440/S3C2416/S3C6410/S5PV210 » ARM linux 在处理IRQ中断的时候 为什么先切换到SVC模式,而不是直接在IRQ模式下处理中断? » 2018-05-14 08:59:26

因为IRQ中断不可预测什么时候发生。让IRQ的中断处理时间尽可能短,这样可以快速响应下一次IRQ中断。
发生IRQ中断时,在IRQ中断中将要处理的任务转交给SVC就退出IRQ中断。

SVC可以通过软中断进入,也能打断正在运行的程序,起到中断作用。

我是刚开始学习这些,理解不对的地方,还请路过的大神纠正。

#23 S3C2440/S3C2416/S3C6410/S5PV210 » 将程序搬运至SDRAM运行--链接脚本与代码重定位 » 2018-05-10 19:23:55

xinxiaoci
回复: 3

《单片机小白转嵌入式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. 用取值符号得到地址

#24 S3C2440/S3C2416/S3C6410/S5PV210 » 内存控制器配置 nor/SDRAM » 2018-05-10 10:23:56

xinxiaoci
回复: 0

《单片机小白转嵌入式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;
}

#25 S3C2440/S3C2416/S3C6410/S5PV210 » S3C2440-裸机-串口 » 2018-05-08 18:08:20

xinxiaoci
回复: 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

#28 S3C2440/S3C2416/S3C6410/S5PV210 » S3C2440内部时钟及高速时钟配置 » 2018-05-06 14:37:20

xinxiaoci
回复: 0

《单片机小白转嵌入式Linux学习记录,基于S3C2440----目录》


芯片手册P237
01.jpg
JZ2440 V2 原理图 P12
02.jpg
OM2、OM3=0 引脚接地,MPLL(Main CLK)、UPLL(USB CLK)时钟源为晶振Crystal。

芯片手册P238
03.jpg
MPLL ->FCLK->CPU        最高400MHz
    ->HCLK ->AHB高速外设总线    最高136MHz
    ->PCLK->APB 低速外设总线    最高68MHz

UPLL->UCLK->USB设备使用

芯片手册P35
04.jpg

下面是代码,自行验证,启动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;
}

#29 Re: ESP32/ESP8266 » 推荐一个好玩的板子,8元,二手, 飞思卡尔 K21 + AP6210B + 12864液晶屏蓝牙POS机, 可以用作Cortex-M4开发板 » 2018-05-06 14:13:32

晕哥 说:
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

开机自毁,怕怕,估计已经爆掉一个了。

我们的口号是:挖坑行,填坑你更行!晕哥,你把它填上

#30 Re: ESP32/ESP8266 » 推荐一个好玩的板子,8元,二手, 飞思卡尔 K21 + AP6210B + 12864液晶屏蓝牙POS机, 可以用作Cortex-M4开发板 » 2018-05-06 09:38:48

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

#32 Re: S3C2440/S3C2416/S3C6410/S5PV210 » GCC常用编译指令及Makefile文件组织编译 » 2018-05-05 20:25:03

晕哥 说:

.C        c++源程序            预处理、编译、汇编

这个坑见识过了,
gcc默认当c++导致链接失败。
当时这个问题找了挺久。

谢谢晕哥提醒!我记下来!

#34 S3C2440/S3C2416/S3C6410/S5PV210 » GCC常用编译指令及Makefile文件组织编译 » 2018-05-05 19:38:20

xinxiaoci
回复: 3

《单片机小白转嵌入式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
------------

#36 S3C2440/S3C2416/S3C6410/S5PV210 » c程序控制led灯及反汇编代码分析 » 2018-05-03 18:47:35

xinxiaoci
回复: 1

《单片机小白转嵌入式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

#37 S3C2440/S3C2416/S3C6410/S5PV210 » 点亮LED——汇编 » 2018-05-02 10:25:22

xinxiaoci
回复: 1

《单片机小白转嵌入式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     程序计数器

led.jpeg

#39 Re: S3C2440/S3C2416/S3C6410/S5PV210 » Linux进阶指令 » 2018-04-30 12:26:18

晕哥 说:

经常用才不会忘记,
长期不用很快又忘记。
就像我以前同事做过培训班的五笔打字老师,
现在一个字也不会打了。

嗯嗯,我会勤加练习的,Ubuntu用的多应该会熟悉的

#40 S3C2440/S3C2416/S3C6410/S5PV210 » Linux进阶指令 » 2018-04-30 10:25:28

xinxiaoci
回复: 2

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

#41 S3C2440/S3C2416/S3C6410/S5PV210 » vim编辑器配置和常用命令 » 2018-04-30 10:23:34

xinxiaoci
回复: 2

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! 退出不保存

#43 Re: S3C2440/S3C2416/S3C6410/S5PV210 » 单片机小白转嵌入式Linux学习记录,基于S3C2440 » 2018-04-27 09:40:34

晕哥 说:

前排见证未来大神的修炼过程。

晕哥,你才是真正的大神!我的信仰。

#44 Re: S3C2440/S3C2416/S3C6410/S5PV210 » 单片机小白转嵌入式Linux学习记录,基于S3C2440 » 2018-04-27 09:39:04

Lvy 说:

握个手,最近全职在家里搞NUC972.共勉

我一边上班一边学,一起努力,我还是单片机层次

#45 Re: S3C2440/S3C2416/S3C6410/S5PV210 » 安装虚拟机和开发环境 » 2018-04-27 09:32:57

晕哥 说:

我现在直接买VPS撸

https://whycan.cn/t_1071.html

嗯,看到了!晕哥是有钱人,收不收小弟!罩着我,/斜眼笑

#46 S3C2440/S3C2416/S3C6410/S5PV210 » 入门shell命令 » 2018-04-27 09:30:36

xinxiaoci
回复: 1

这些命令算是熟悉下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 重新初始化屏幕

#47 S3C2440/S3C2416/S3C6410/S5PV210 » 安装虚拟机和开发环境 » 2018-04-27 09:19:09

xinxiaoci
回复: 2

虚拟机软件版本: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即可

#48 S3C2440/S3C2416/S3C6410/S5PV210 » 学习路线和一些基本概念 » 2018-04-26 14:25:50

xinxiaoci
回复: 2

学习线路:

单片机-> bootloader -> Linux/驱动 -> Qt



统启动过程: bootloader -> linux内核 -> 挂载根文件系统 -> 运行APP


1.由于 bootloader 需要读取引导Linux内核,所以bootloader要有读取flash/SD的能力
2.要显示信息,所以要操作LCD
3.要提高运行速度,所以要初始化时钟,和内存
3.要远程下载内核,要设置网卡

所以有些外设的硬件操作在bootloader和内核驱动中应该都有。

--------------------------------------------------------------
bootloader相对于电脑的bios
根文件系统相当于windows的C盘,系统盘。
--------------------------------------------------------------

Linux内核也需要有操作硬件的能力,所以需要驱动程序。

驱动程序=软件框架+硬件操作

硬件操作就需要看硬件原理图和芯片手册及寄存器的读写,这部分应该类似于单片开发。

#50 Re: S3C2440/S3C2416/S3C6410/S5PV210 » 单片机小白转嵌入式Linux学习记录,基于S3C2440 » 2018-04-26 13:58:44

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点多就困的不行。

#51 S3C2440/S3C2416/S3C6410/S5PV210 » 单片机小白转嵌入式Linux学习记录,基于S3C2440 » 2018-04-26 13:57:44

xinxiaoci
回复: 17

一直从事单片机的开发工作,最近想更进一步学习,所以打算入坑嵌入式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 单片机应该没有,最大的区别应该就是这个,其他几条只是加深了一些概念的理解

#52 Re: 全志 SOC » 单片机小白,求教学习路线,希望过来的哥哥姐姐给我指明方向! » 2018-04-26 10:31:18

晕哥 说:
xinxiaoci 说:

没人回复吗?自己顶一顶!刚才拍下了韦东山的2440和一二期视频,不知道这个选择对吗,总要有一个方向吧

选择没有问题, 只要不让视频吃灰了。
我也买了很多开发板, 也是跑个demo玩几天就吃灰了。
人的占有欲是很强的,
没有的时候非常期待,当真正拥有了觉得索然无味.
不过我仍然很享受这个花钱的感觉.
这个如同小孩的玩具、女人的衣服哈!

争取努力不让视频吃灰,以后我会把自己的学习过程在2440的板块给像我一样的小白分享分享!

#53 Re: 全志 SOC » 单片机小白,求教学习路线,希望过来的哥哥姐姐给我指明方向! » 2018-04-26 10:29:01

晕哥 说:

你真要学习Linux或者V3s开发板裸奔,本站都有大量资料,搜一下就有结果了, 实在没有的可以提问哦,我解决不了的可以帮你去搬救兵。

嗯嗯,我会经常关注本论坛的,可能是自己平时玩的都是单片机这些,思维模式不一样。看到大神们的帖子,其实写的已经很详细了,但我还是看看的懵懵懂懂,可能欠缺哪些基础知识。

#54 Re: Php/Nodejs/Web/HTML5/Javascript/微信开发/Python » 手机浏览器登录论坛,回复帖子提交按钮被遮挡。 » 2018-04-26 10:05:54

嗯嗯,知道了!晕哥棒棒的!怎么把昵称改为中文?我的还是拼音

xinxiaoci 说:

用手机回复别人的帖子,在回复框里打字后,回复框的样式会发生变化,遮挡提交按钮

#55 Re: 全志 SOC » 单片机小白,求教学习路线,希望过来的哥哥姐姐给我指明方向! » 2018-04-26 10:01:34

没人回复吗?自己顶一顶!刚才拍下了韦东山的2440和一二期视频,不知道这个选择对吗,总要有一个方向吧

#57 Re: 全志 SOC » step by step 给ts_calibrate 触摸屏校正程序加上汉字显示. » 2018-04-26 06:14:06

厉害了晕哥,你这精力跟充沛啊!赶不上,只能膜拜

#58 Php/Nodejs/Web/HTML5/Javascript/微信开发/Python » 手机浏览器登录论坛,回复帖子提交按钮被遮挡。 » 2018-04-26 06:03:18

xinxiaoci
回复: 6

用手机回复别人的帖子,在回复框里打字后,回复框的样式会发生变化,遮挡提交按钮

#61 Re: Cortex M0/M3/M4/M7 » 各位大神,有没易上手的STM32文件系统推荐一个 » 2018-04-25 19:16:29

如果是移动设备,内存卡、U盘之类的还是用fatfs好点

#62 Re: Cortex M0/M3/M4/M7 » 请问你们写stm32时,用寄存器,还是库函数? » 2018-04-25 19:13:30

一般操作都是用库,同一个配置,百度出来的大部分也是库函数,所以还是跟随大众好点

#63 全志 SOC » 单片机小白,求教学习路线,希望过来的哥哥姐姐给我指明方向! » 2018-04-25 16:48:29

xinxiaoci
回复: 6

标题即为诉求:

先说一下我自身的状况,由于工作和学习经历,我一直从事单片机开发工作,一直用的stm8/stm32f103系列的芯片做产品的开发。

现在想更进一步学习Linux操作系统,主要是想做一些嵌入式触摸界面程序,通过485或CAN网络来采集MCU小模块的数据进行远程读取或参数设置。
目前正在自学Qt,主要在windows桌面练习。

现在手上的有荔枝派 zero 的小板子,仅仅是能把各位大神编译好的系统烧写进板子,然后板子就开始吃灰了。感觉整个系统过于庞大感觉无从下手。

希望能基于lichee_zero 进行常用的外设(SPI、IIC、串口) 驱动编写、外设的操作、Qt移植。我应该看哪些书籍才能一步步实现自己的技术小目标,求推荐。

我应该先学什么,在学什么,然后学什么?希望过来的小哥哥小姐姐指点迷津!

页脚

工信部备案:粤ICP备20025096号 Powered by FluxBB

感谢为中文互联网持续输出优质内容的各位老铁们。 QQ: 516333132, 微信(wechat): whycan_cn (哇酷网/挖坑网/填坑网) service@whycan.cn