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 配置及处理;
- | 七. 主程序对异常的处理;
- \\\\\\\\\\\\\\\\\\\\///////////////////
- 一. 预热文章:
- . Make 命令教程
- url: http://www.ruanyifeng.com/blog/2015/02/make.html
- . ATPCS和内嵌汇编: arm处理器上函数调用寄存器的使用规则
- url: http://bog.csdn.net/yypony/article/details/17633323
- 二. C语言中插入ARM汇编:
- . cat > test.c << EOF
- #include <stdio.h>
- int main(void)
- {
- volatile unsigned int a ;
- int b ;
- __asm__ __volatile__ (
- "mov r0, #11 \n" // 如果立即数小于256直接附值
- "mov %0, r0 \n"
- "mov %1, #125 \n"
- :"=r"(a),"=r"(b) // 输出
- : // 输入
- :"r0" // 已经使用过的寄存器
- );
- printf("a:%d b:%d \n" , a , b);
- return ;
- }
- EOF
- . arm-linux-gcc test.c -o test
- . minicom(U-Boot)中运行编译好的test程序: ./test
- 三. U-Boot下汇编裸板开发基本流程:
- . 编译好U-Boot后,在其根目标录下会生成一个System.map文件,这是U-Boot中提供的
- 函数及其地址(符号表),我们可以把U-Boot当作一个函数库来使用.
- . 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\n"
- .align
- EOF
- . 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
- . make
- . 将test.bin烧入开发板,运行程序,得到结果.
- . 如果不使用默认的连接文件,采用自己编写的连接文件,操作如下:
- . 获取链接脚本模板: 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 = ;
- }
- . 修改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语言裸板开发基本流程:
- . 编译好U-Boot后,在其根目标录下会生成一个System.map文件,这是U-Boot中提供的
- 函数及其地址(符号表),我们可以把U-Boot当作一个函数库来使用.
- . cat > test.c << EOF
- int num = ;
- int array[] = {};
- //0x43e11434是U-Boot中printf地址,这个地址不是固定,如果修改了源码,地址可能会变
- int (*printf)(const char *fmt , ...) = (void *)0x43e11434;
- int _start(void) // 这里不能是main,因为裸板运行的其实函数是_start,和汇编一样
- {
- printf("num:%d \n" , num);
- int i ;
- for(i = ; i < ; i++)
- {
- printf("array[%d]: %d \n" , i , array[i]);
- }
- return ;
- }
- EOF
- . 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
- . make
- . 将test.bin烧入开发板,运行程序,得到结果.
- 五. MMU 配置流程:
- void memset(int *ttb , char ch , int size )
- {
- int i ;
- for(i = ; 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 >> ] = (pa & 0xfff00000) | ;
- }
- //SFR
- for(va = 0x10000000 ; va < 0x14000000 ; va+=0x100000) {
- pa = va;
- ttb[va >> ] = (pa & 0xfff00000) | ;
- }
- //DRAM 内存
- for(va = 0x40000000 ; va < 0x80000000 ; va+=0x100000) {
- pa = va;
- ttb[va >> ] = (pa & 0xfff00000) | ;
- }
- }
- void memory_map(int *ttb , unsigned int va , unsigned int pa)
- {
- ttb[va >> ] = (pa & 0xfff00000) | ;
- }
- void enable_mmu(unsigned int virtualaddress , unsigned int physicsaddress)
- {
- unsigned int systemctl = ;
- 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, , *);
- //2. IROM SFR DRAM
- default_map(ttb);
- //3. memmap
- memory_map(ttb , virtualaddress, physicsaddress);
- //4. enable_mmu();
- systemctl = | ( << ) | ( << ) | ( << ) ;
- __asm__ __volatile__ (
- //Domain Acess c3 c0
- "mvn r0 , #0 \n"
- "MCR p15, 0, r0, c3, c0, 0 \n"
- //write ttb
- "MCR p15, 0, %0, c2, c0, 0 \n"
- //enable mmu system control
- "MRC p15, 0, r0, c1, c0, 0 \n"
- "orr r0 , r0 , %1 \n"
- "MCR p15, 0, r0, c1, c0, 0 \n"
- :
- :"r"(ttb),"r"(systemctl) //外部传的参数
- :"r0"
- );
- }
- 六. Exception 配置及处理:
- . 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 , #-] @把发生指令异常指令对应的数字打印出来
- 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 , #-]
- 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 \n"
- .align
- 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 \n"
- "nop \n"
- ".word 0x12345678 \n" //正常的指令
- ".word 0x77777777 \n" //异常的指令
- "swi #0x1234 \n" //软件中断: 以前是swi,现在改成svc
- "svc #0x2345 \n"
- );
- //设置cpsr第I位,打开外部中断,要不然GIC无效
- __asm__ __volatile__ (
- "mrs r0 , cpsr \n"
- "bic r0 , r0 , #(1 << 7) \n"
- "msr cpsr , r0 \n"
- );
- //---------------------------cpu
- //指定哪个CPU接收
- ICCICR_CPU0 |= ;
- //配置CPU的优先级最低
- //ICCPMR_CPU0 &= ~0xff ;
- ICCPMR_CPU0 |= 0xff ; //数字越小,优先级越高
- //开启GIC enable
- ICDDCR |= ;
- //----------------------------
- //设置GIC 1号中断的优先级为0,也就是最高
- ICDIPR0_CPU0 &= ~(0xff << );
- //指定CPU处理中断
- ICDIPTR0_CPU0 |= ( << );
- //允许GIC 1号中断
- ICDISER0_CPU0 |= << ;
- //发1号内部GIC中断
- ICDSGIR = ( << ) | ;
- }
- void do_reset(void)
- {
- printf("this is in reset ... \n");
- }
- void do_undef(void)
- {
- printf("this is in do_undef... \n");
- }
- void do_svc((int SystemCallNo)
- {
- /* 软件中断的参数在Linux就是系统调用号的意思 */
- SystemCallNo &= 0xffffff ; //获取系统调用号
- printf("this is in svc...No:%p \n" , SystemCallNo);
- }
- void do_PrefetchAbt(void)
- {
- printf("this is in PrefetchAbt... \n");
- }
- void do_DataAbt(void)
- {
- printf("this is in DataAbt... \n");
- }
- void do_irq(void)
- {
- /* 经测试,不能和其他的中断一起使用,只能作为测试GIC 1号中断这样处理 */
- int ID = ICCIAR_CPU0 & 0x3ff ;
- int CPUID = ((ICCIAR_CPU0) >> ) & 0x7 ;
- printf("this is in irq...ID:%d CPUID:%d \n" , ID , CPUID);
- }
- void do_fiq(void)
- {
- printf("this is in fiq... \n");
- }
Samsung_tiny4412(驱动笔记02)----ASM with C,MMU,Exception,GIC的更多相关文章
- Samsung_tiny4412(驱动笔记01)----linux 3.5,U-Boot,Busybox,SD卡启动环境搭建
/*********************************************************************************** * * linux 3.5,U ...
- Samsung_tiny4412(驱动笔记06)----list_head,proc file system,GPIO,ioremap
/**************************************************************************** * * list_head,proc fil ...
- Samsung_tiny4412(驱动笔记05)----Makefile,open,read,write,lseek,poll,ioctl,fasync
/*********************************************************************************** * * Makefile,op ...
- Samsung_tiny4412(驱动笔记04)----volatile,container_of,file_operations,file,inode
/*********************************************************************************** * * volatile,co ...
- Samsung_tiny4412(驱动笔记03)----字符设备驱动基本操作及调用流程
/*********************************************************************************** * * 字符设备驱动基本操作及 ...
- Samsung_tiny4412(驱动笔记10)----mdev,bus,device,driver,platform
/*********************************************************************************** * * mdev,bus,de ...
- Samsung_tiny4412(驱动笔记09)----alloc_pages,kmalloc,vmalloc,kmem_cache,class
/*********************************************************************************** * * alloc_pages ...
- Samsung_tiny4412(驱动笔记07)----spinlock,semaphore,atomic,mutex,completion,interrupt
/*********************************************************************************** * * spinlock,se ...
- Samsung_tiny4412(驱动笔记08)----jiffies,timer,kthread,workqueue,tasklet
/*********************************************************************************** * * jiffies,tim ...
随机推荐
- [luogu P3628] [APIO2010]特别行动队
[luogu P3628] [APIO2010]特别行动队 题目描述 你有一支由 n 名预备役士兵组成的部队,士兵从 1 到 n 编号,要将他们拆分 成若干特别行动队调入战场.出于默契的考虑,同一支特 ...
- CSS:font-family常用字体中英文对照
CSS:font-family常用字体中英文对照如下: 推荐网址:https://www.cnblogs.com/EnSnail/p/6792853.html 微软雅黑: Microsoft YaHe ...
- JDBC、JNDI和DBCP的区别
JDBC:Java DataBase Connectivity,java连接数据库和执行SQL语句的API. 数据源:Data Source.就是将IP.数据库.用户名.密码封装起来对外只提供一个JN ...
- python 学习 模块
在计算机程序的开发过程中,随着程序代码越写越多,在一个文件里代码就越来越长,越来越不容易 维护, 为了编写可维护的代码,我们把很多函数分组,分别放到不同的文件里,这样,每个文件包含的代码就相对较少,很 ...
- awk 中 fieldwidths使用方法
AWK中的FIELDWIDTHS是一个很好用的变量,这个变量可以指定字符串按照怎么样的宽度进行展示 实例一: 要求: 032130 032131 146230 035048 222049 095070 ...
- ActiveMQ的安装与配置
ActiveMQ的安装与配置详情 (1)ActiveMQ的简介 MQ: (message queue) ,消息队列,也就是用来处理消息的,(处理JMS的).主要用于大型企业内部或与企业之间的传递数据信 ...
- Java遍历集合的几种方法分析(实现原理、算法性能、适用场合)
概述 Java语言中,提供了一套数据集合框架,其中定义了一些诸如List.Set等抽象数据类型,每个抽象数据类型的各个具体实现,底层又采用了不同的实现方式,比如ArrayList和LinkedList ...
- java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result异常的解决方法
今天在写一个JAVA程序的时候出现了异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact repr ...
- VCL界面控件DevExpress VCL Controls发布v18.2.4|附下载
DevExpress VCL Controls是 Devexpress公司旗下最老牌的用户界面套包.所包含的控件有:数据录入,图表,数据分析,导航,布局,网格,日程管理,样式,打印和工作流等,让您快速 ...
- 数据结构~trie树(字典树)
1.概述 Trie树,又称字典树,单词查找树或者前缀树,是一种用于快速检索的多叉树结构,如英文字母的字典树是一个26叉树,数字的字典树是一个10叉树. 我理解字典树是看了这位大佬博客.还不了解字典树的 ...