zoukankan      html  css  js  c++  java
  • Samsung_tiny4412(驱动笔记02)----ASM with C,MMU,Exception,GIC

    /****************************************************************************
     *
     *                        ASM with C,MMU,Exception,GIC
     *
     *  声明:
     *      1. 本系列文档是在vim下编辑,请尽量是用vim来阅读,在其它编辑器下可能会
     *         不对齐,从而影响阅读.
     *      2. 以下所有的shell命令都是在root权限下运行的;
     *      3. 文中在需要往文件中写入内容的时候使用了如下2方式:
     *          1.如果文件不存在,创建文件;如果存在,以覆盖的方式往文件中添加内容:
     *              cat > 文件名 << EOF (结束符)
     *              ...
     *              文件内容...
     *              ...
     *              EOF (输入遇到EOF,cat指令结束,内容将保存在前面指定的文件中)
     *          2.如果文件不存在,创建文件;如果存在,将内容追加到文件尾:
     *              cat >> 文件名 << EOF (结束符)
     *              ...
     *              文件内容...
     *              ...
     *              EOF 
     *
     *                                          2015-3-7 阴 深圳 尚观 Sbin 曾剑锋
     ****************************************************************************/
    
                        \\\\\\\--*目录*--//////////////
                        |  一. 预热文章;                      
                        |  二. C语言中插入ARM汇编;            
                        |  三. U-Boot下汇编裸板开发基本流程;  
                        |  四. U-Boot下C语言裸板开发基本流程; 
                        |  五. MMU 配置流程;                  
                        |  六. Exception 配置及处理;          
                        |  七. 主程序对异常的处理;            
                        \\\\\\\\\\///////////////////
    
    一. 预热文章:
        1. Make 命令教程
            url: http://www.ruanyifeng.com/blog/2015/02/make.html
        2. ATPCS和内嵌汇编: arm处理器上函数调用寄存器的使用规则
            url: http://bog.csdn.net/yypony/article/details/17633323
    
    二. C语言中插入ARM汇编:
        1. cat > test.c << EOF
             #include <stdio.h>
             int main(void)
             {
                 volatile  unsigned int a ;
                 int b ;
                 __asm__  __volatile__ (
                 "mov r0, #11  
    "      // 如果立即数小于256直接附值
                 "mov %0, r0   
    "
                 "mov %1, #125 
    "
                 :"=r"(a),"=r"(b)       // 输出
                 :                      // 输入
                 :"r0"                  // 已经使用过的寄存器
                 );
                 printf("a:%d b:%d 
    " , a , b);
                 return 0 ;
             }
             EOF
         2. arm-linux-gcc test.c -o test
         3. minicom(U-Boot)中运行编译好的test程序: ./test
    
    三. U-Boot下汇编裸板开发基本流程:
        1. 编译好U-Boot后,在其根目标录下会生成一个System.map文件,这是U-Boot中提供的
            函数及其地址(符号表),我们可以把U-Boot当作一个函数库来使用.
        2. cat > test.S << EOF
             .global _start
             _start:
                 stmfd sp! , {r0-r12 , lr} @寄存器入栈
    
                 @ 0x43e11434是U-Boot中printf地址,这个地址不是固定,这是我编译的U-Boot中
                 @ printf的地址, 因为如果修改了U-Boot的源码,printf地址会变,U-Boot其他
                 @ 函数地址也会变,所以大家以各自编译U-Boot后产生的System.map文件中的
                 @ 地址为准.
                 ldr r1 , =0x43e11434 
                 ldr r0 , =str
                 mov lr , pc
                 mov pc , r1
             
                 ldmfd sp! , {r0-r12 , pc} @寄存器出栈
             str:
                 .string    "hello world
    "
                 .align     5
             EOF
        3. cat > Makefile << EOF 
             all:
                 arm-linux-gcc -c test.S -o test.o
                 arm-linux-ld -Ttext=0x40008000 test.o -o test # 0x40008000是加载代码的起始地址
                 arm-linux-objcopy -O binary test test.bin     # 获取二进制可运行文件
             
             clean:
                 rm -rf  test.o  test test.bin
             EOF
        4. make 
        5. 将test.bin烧入开发板,运行程序,得到结果.
        6. 如果不使用默认的连接文件,采用自己编写的连接文件,操作如下:
            1. 获取链接脚本模板: arm-linux-ld --verbose > test.lds ,修改模板文件为如下文件内容:
                =============================================================================
                /* Script for -z combreloc: combine and sort reloc sections */
                OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
                          "elf32-littlearm")
                OUTPUT_ARCH(arm)
                ENTRY(_start)
                SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib");
                SECTIONS
                {
                    . = 0x40008000 ; /* 运行代码的起始地址 */
                    .text :
                    {
                        test.o(.text) ;  /* _start标号在这个文件里 */
                        *(.text) ;
                    }
                    align = 4 ; 
                }
            2. 修改Makefile如下: cat > Makefile << EOF 
                all:
                    arm-linux-gcc -c test.S -o test.o 
                    arm-linux-ld -T test.lds *.o -o test
                    arm-linux-objcopy -O binary test  test.bin
                clean:
                    rm -rf test.o test test.bin 
                EOF
    
    四. U-Boot下C语言裸板开发基本流程:
        1. 编译好U-Boot后,在其根目标录下会生成一个System.map文件,这是U-Boot中提供的
            函数及其地址(符号表),我们可以把U-Boot当作一个函数库来使用.
        2. cat > test.c << EOF
            int num = 1;
            int array[10] = {0}; 
            //0x43e11434是U-Boot中printf地址,这个地址不是固定,如果修改了源码,地址可能会变
            int (*printf)(const char *fmt , ...) = (void *)0x43e11434; 
            int _start(void) // 这里不能是main,因为裸板运行的其实函数是_start,和汇编一样
            {
                printf("num:%d 
    " , num);
                int i ; 
                for(i = 0 ; i < 10; i++)
                {
                    printf("array[%d]: %d 
    " , i , array[i]);   
                }
            
                return 0 ; 
            }
            EOF
        3. cat > Makefile << EOF 
            all:
                arm-linux-gcc -c  test.c  -o  test.o -fno-builtin
                arm-linux-ld  -T test.lds  *.o  -o  test #采用第三部分的lds文件
                arm-linux-objcopy -O binary test  test.bin
            clean:
                rm -rf test  test.bin   *.o 
            EOF
        4. make
        5. 将test.bin烧入开发板,运行程序,得到结果.
    
    五. MMU 配置流程:
        void memset(int *ttb , char ch , int size )
        {
            int i ; 
            for(i = 0 ; i < size ; i++) {
                ((char *)ttb)[i] = ch; 
            }
        }
        void default_map(int *ttb)
        {
            unsigned int va , pa; 
            //IROM  RAM
            for(va = 0x00000000 ; va < 0x10000000 ; va+=0x100000) {
                pa = va; 
                ttb[va >> 20] = (pa & 0xfff00000) | 2; 
            }
            //SFR
            for(va = 0x10000000 ; va < 0x14000000 ; va+=0x100000) {
                pa = va; 
                ttb[va >> 20] = (pa & 0xfff00000) | 2; 
            }
            //DRAM  内存
            for(va = 0x40000000 ; va < 0x80000000 ; va+=0x100000) {
                pa = va; 
                ttb[va >> 20] = (pa & 0xfff00000) | 2; 
            }
        }
        void memory_map(int *ttb , unsigned int va , unsigned int pa)
        {
                ttb[va >> 20] = (pa & 0xfff00000) | 2 ; 
        }
        void enable_mmu(unsigned int virtualaddress , unsigned int physicsaddress)
        {
            unsigned int systemctl = 0; 
            unsigned int *ttb = (void *)0x73000000 ; 
            unsigned int *va  = (void *)virtualaddress; 
            unsigned int *pa  = (void *)physicsaddress;     
            //1. 清空ttb所在的地址  16K = 4G/1M*4(最后乘以4是因为每个地址占用4个字节)
            memset(ttb, 0, 16*1024);
            //2. IROM SFR DRAM
            default_map(ttb);
            //3. memmap
            memory_map(ttb , virtualaddress, physicsaddress);
            //4. enable_mmu();
            systemctl = 1 | (1 << 11) | (1 << 13) | ( 1 << 28) ; 
    
            __asm__ __volatile__ (
            //Domain Acess c3  c0
            "mvn  r0 , #0     
    "
            "MCR p15, 0, r0, c3, c0, 0     
    "
            
            //write ttb
            "MCR p15, 0, %0, c2, c0, 0     
    "
    
            //enable mmu system control 
            "MRC p15, 0, r0, c1, c0, 0     
    "
            "orr    r0 , r0 , %1           
    "
            "MCR p15, 0, r0, c1, c0, 0     
    "
            :
            :"r"(ttb),"r"(systemctl)   //外部传的参数 
            :"r0"
            );
        }
    
    六. Exception 配置及处理:
        1. cat > vector.S << EOF
            .global _start
            _start:
                b    reset       @复位异常
                b    undef       @指令未定义异常
                b    svc            @软件中断
                b    PrefetchAbt @取指令异常
                b    DataAbt     @取数据异常
                nop             @保留
                b    irq         @外部普通中断
                b    fiq         @外部快速中断
                
            reset: @执行指令的时候触发的异常,但因为是复位,返回pc指针无效
                stmfd sp! , {r0-r12 , lr}   
    
                ldr    r0 , =0x60000000
                @保存当前执行位置下+8的地址,也就是下2行ldmfd sp! , {r0-r12 , pc}^地址
                @所以当执行完r0代表的函数返回时,接着到这个位置执行---
                mov    lr , pc                                         |
                ldr    pc , [r0]                                        |
                                                                    V
                ldmfd sp! , {r0-r12 , pc}^ @"^"的意思是指令完成后,把SPSR拷贝到CPSR
    
            undef:  @指令编译的时候触发的异常,此时的pc指针正好指向异常指令的后面一条指令
                stmfd sp! , {r0-r12 , lr}
    
                @-------------------test
                ldr r0 , =str           @获取字符串,第一个参数保存在r0中
                ldr r2 , =printf        @获取printf符号的地址
                ldr r1 , [lr , #-4]     @把发生指令异常指令对应的数字打印出来
    
                mov lr , pc             
                ldr pc , [r2]           @获取printf符号地址里的值,并调用对应值的函数(调用printf)
                @-------------------test
                            
                ldr    r0 , =0x60000004
                mov    lr , pc
                ldr    pc , [r0]    
    
                ldmfd sp! , {r0-r12 , pc}^
    
            svc:    @指令编译的时候触发的异常
                stmfd sp! , {r0-r12 , lr}
    
                @处理函数需要知道SVC指令的调用号,把整条指令当传输传给C函数处理
                ldr r0 , [lr , #-4]
                ldr r2 , =0x60000008
                mov lr , pc
                ldr pc , [r2]
    
                ldmfd sp! , {r0-r12 , pc}^
    
            PrefetchAbt:    @取指令的时候引发的异常
                stmfd sp! , {r0-r12 , lr}
    
                ldr    r0 , =0x6000000C
                mov    lr , pc
                ldr    pc , [r0]    
                
                ldmfd sp! , {r0-r12 , pc}^
    
            DataAbt:    @取数据的时候引发的异常
                stmfd sp! , {r0-r12 , lr}
    
                ldr    r0 , =0x60000010
                mov    lr , pc
                ldr    pc , [r0]    
                
                ldmfd sp! , {r0-r12 , pc}^
    
            irq:    @会执行完当前正在编译的指令,再去处理异常
                stmfd sp! , {r0-r12 , lr}
    
                ldr    r0 , =0x60000014
                mov    lr , pc
                ldr    pc , [r0]    
                
                ldmfd sp! , {r0-r12 , pc}^
    
            fiq:    @会执行完当前正在编译的指令,再去处理异常
                stmfd sp! , {r0-r12 , lr}
    
                ldr    r0 , =0x60000018
                mov    lr , pc
                ldr    pc , [r0]    
                
                ldmfd sp! , {r0-r12 , pc}^
    
            str:
                .string  "hello world 
    "
                .align    5
    
            printf:
                .word  0x43e11434
            EOF
    
    七. 主程序对异常的处理:
        int (*printf)(const char *fmt , ...) = (void *)0x43e11434 ; 
        void do_reset(void);
        void do_undef(void);
        void do_svc(void);
        void do_PrefetchAbt(void);
        void do_DataAbt(void);
        void do_irq(void);
        void do_fiq(void);
    
        int _start(void) {
            unsigned int *va  = (void *)0xfff00000 ; 
            unsigned int *pa  = (void *)0x50000000 ; //这里决定异常向量表从0x500f0000开始
            
            /* 对应vector.S中的地址调用 */
            *(U32 *)0x60000000 = (U32)do_reset; 
            *(U32 *)0x60000004 = (U32)do_undef ; 
            *(U32 *)0x60000008 = (U32)do_svc; 
            *(U32 *)0x6000000C = (U32)do_PrefetchAbt; 
            *(U32 *)0x60000010 = (U32)do_DataAbt ; 
            *(U32 *)0x60000014 = (U32)do_irq ; 
            *(U32 *)0x60000018 = (U32)do_fiq ; 
    
            //开启mmu
            enable_mmu((int)va , (int)pa);
    
            __asm__ __volatile__ (
            "mov    r0 , r0   
    "
            "nop    
    "
            ".word  0x12345678   
    "    //正常的指令
            ".word  0x77777777   
    "    //异常的指令
    
            "swi    #0x1234      
    "    //软件中断: 以前是swi,现在改成svc
            "svc    #0x2345      
    "
            );
    
            //设置cpsr第I位,打开外部中断,要不然GIC无效
            __asm__ __volatile__ (
            "mrs    r0 , cpsr             
    "
            "bic    r0 , r0 , #(1 << 7)   
    "
            "msr    cpsr , r0             
    "
            );
         
            //---------------------------cpu
            //指定哪个CPU接收
            ICCICR_CPU0 |= 1 ; 
             
            //配置CPU的优先级最低
            //ICCPMR_CPU0 &= ~0xff ; 
            ICCPMR_CPU0 |= 0xff ; //数字越小,优先级越高
            //开启GIC  enable
            ICDDCR |= 1 ; 
            //----------------------------
            //设置GIC 1号中断的优先级为0,也就是最高
            ICDIPR0_CPU0 &= ~(0xff << 8);
            //指定CPU处理中断
            ICDIPTR0_CPU0 |= (1 << 8);
            //允许GIC 1号中断
            ICDISER0_CPU0 |= 1  << 1;
            //发1号内部GIC中断
            ICDSGIR = (1 << 16) | 1 ;
    
        }
        void do_reset(void)
        {
            printf("this is in reset ... 
    ");
        }
        void do_undef(void)
        {
            printf("this is in do_undef... 
    ");
        }
        void do_svc((int SystemCallNo)
        {
            /* 软件中断的参数在Linux就是系统调用号的意思 */
            SystemCallNo &= 0xffffff ;  //获取系统调用号
            printf("this is in svc...No:%p  
    " , SystemCallNo);
        }
        void do_PrefetchAbt(void)
        {
            printf("this is in PrefetchAbt... 
    ");
        }
        void do_DataAbt(void)
        {
            printf("this is in DataAbt... 
    ");
        }
        void do_irq(void)
        {
            /* 经测试,不能和其他的中断一起使用,只能作为测试GIC 1号中断这样处理 */
            int ID = ICCIAR_CPU0 & 0x3ff ;
            int CPUID = ((ICCIAR_CPU0) >> 10) & 0x7  ;
            printf("this is in irq...ID:%d  CPUID:%d  
    " , ID , CPUID);
    
        }
        void do_fiq(void)
        {
            printf("this is in fiq... 
    ");
        }
  • 相关阅读:
    Erlang 杂记 IV
    ASP.NET MVC的View是如何被呈现出来的?[设计篇]
    面向对象—在线文件管理模块
    软件开发中个人在团队里的效绩评定
    用go语言遍历文件夹
    磁盘缓存的算法:写算法
    一种Lua到C的封装
    从信息系统界面设计引发的思考
    基于Backbone.js的JavaScript MVC示例程序
    C和C++
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4320885.html
Copyright © 2011-2022 走看看