本文转载自:https://blog.csdn.net/talent_CYJ/article/details/50533153

今天在一个问题上折腾了又是半天。就是在学JZ2440串口通信的时候,在sdram初始化函数中有这么一句话
  • 1
  • 2
/** 设置存储控制器以使用SDRAM*/
void memsetup(void)
{volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; /* 这个函数之所以这样赋值,而不是像前面的实验那样将配置值 * 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到 * SDRAM之前就可以在steppingstone中运行 */ /* 存储控制器13个寄存器的值 */ p[0] = 0x22011110; //BWSCON p[1] = 0x00000700; //BANKCON0 p[2] = 0x00000700; //BANKCON1 p[3] = 0x00000700; //BANKCON2 p[4] = 0x00000700; //BANKCON3 p[5] = 0x00000700; //BANKCON4 p[6] = 0x00000700; //BANKCON5 p[7] = 0x00018005; //BANKCON6 p[8] = 0x00018005; //BANKCON7 p[9] = 0x008C04F4; //REFRESH p[10] = 0x000000B1; //BANKSIZE p[11] = 0x00000030; //MRSRB6 p[12] = 0x00000030; //MRSRB7 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

对于前面实验,sdram初始化函数是这么写的:

void memsetup(void)
{/* SDRAM 13个寄存器的值 */unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON 0x00000700, //BANKCON0 0x00000700, //BANKCON1 0x00000700, //BANKCON2 0x00000700, //BANKCON3 0x00000700, //BANKCON4 0x00000700, //BANKCON5 0x00018005, //BANKCON6 0x00018005, //BANKCON7 0x008C07A3, //REFRESH 0x000000B1, //BANKSIZE 0x00000030, //MRSRB6 0x00000030, //MRSRB7 }; int i = 0; volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; for(; i < 13; i++) p[i] = mem_cfg_val[i]; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

对于这个串口程序,它的链接文件如下:

SECTIONS {. = 0x30000000;.text          :   { *(.text) }.rodata ALIGN(4) : {*(.rodata)} .data ALIGN(4) : { *(.data) } .bss ALIGN(4) : { *(.bss) *(COMMON) } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

表示该程序“应该”放在0x30000000中执行。

在程序运行时,首先执行head.S中的代码。 
head.S代码如下:

.extern     main
.text
.global _start
_start:
Reset:                  ldr sp, =4096           @ 设置栈指针,以下都是C函数,调用前需要设好栈bl  disable_watch_dog   @ 关闭WATCHDOG,否则CPU会不断重启bl  clock_init          @ 设置MPLL,改变FCLK、HCLK、PCLK bl memsetup @ 设置存储控制器以使用SDRAM bl copy_steppingstone_to_sdram @ 复制代码到SDRAM中 ldr pc, =on_sdram @ 跳到SDRAM中继续执行 on_sdram: ldr sp, =0x34000000 @ 设置栈指针 ldr lr, =halt_loop @ 设置返回地址 ldr pc, =main @ 调用main函数 halt_loop: b halt_loop
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

程序运行时,对于NAND FLASH启动,首先NAND FLASH前4K代码会被复制到steppingstone片内内存中去执行,这个时候0x30000000(对于JZ2440来说该地址是SDRAM地址)还不能使用,因为SDRAM还没初始化,虽然链接的时候指出了,程序应该位于0x30000000执行,但是程序代码从NAND FLASH拷贝到片内内存之后,CPU默认从0地址开始执行,所以程序会被执行,当SDRAM没被初始化之前,用的都是bl跳转指令,这些指令都是位置无关指令。

对于反汇编文件,先看前面一部分:

30000000 <_start>:
30000000:   e3a0da01    mov sp, #4096   ; 0x1000
30000004: eb00000a bl 30000034 <disable_watch_dog> 30000008: eb00000d bl 30000044 <clock_init> 3000000c: eb000026 bl 300000ac <memsetup> 30000010: eb000040 bl 30000118 <copy_steppingstone_to_sdram> 30000014: e59ff00c ldr pc, [pc, #12] ; 30000028 <.text+0x28> 30000018 <on_sdram>: 30000018: e3a0d30d mov sp, #872415232 ; 0x34000000 3000001c: e59fe008 ldr lr, [pc, #8] ; 3000002c <.text+0x2c> 30000020: e59ff008 ldr pc, [pc, #8] ; 30000030 <.text+0x30> 30000024 <halt_loop>: 30000024: eafffffe b 30000024 <halt_loop> 30000028: 30000018 andcc r0, r0, r8, lsl r0 3000002c: 30000024 andcc r0, r0, r4, lsr #32 30000030: 30000200 andcc r0, r0, r0, lsl #4 30000034 <disable_watch_dog>: 30000034: e3a02000 mov r2, #0 ; 0x0 30000038: e3a03453 mov r3, #1392508928 ; 0x53000000 3000003c: e5832000 str r2, [r3] 30000040: e1a0f00e mov pc, lr
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

前面表示了程序链接的地址。但是程序在片内内存中还是会继续执行,当执行到

bl  disable_watch_dog 
  • 1

这个语句时,程序会跳转到disable_watch_dog去执行,由于bl是位置无关,所以跳转的时候只是PC(new) = PC + 偏移。 
而对于ldr pc, =on_sdram这些指令来说则是位置有关,即让PC赋值为一个确切值。对于 
=on_sdram来说,它则等于 30000018,所以程序会跳转到该位置中去执行,若SDRAM没初始化之前使用了这类位置有关的指令,则会出错。

所以在下列情况下:

void memsetup(void)
{/* SDRAM 13个寄存器的值 */unsigned long const mem_cfg_val[]={ 0x22011110, //BWSCON 0x00000700, //BANKCON0 0x00000700, //BANKCON1 0x00000700, //BANKCON2 0x00000700, //BANKCON3 0x00000700, //BANKCON4 0x00000700, //BANKCON5 0x00018005, //BANKCON6 0x00018005, //BANKCON7 0x008C07A3, //REFRESH 0x000000B1, //BANKSIZE 0x00000030, //MRSRB6 0x00000030, //MRSRB7 }; int i = 0; volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; for(; i < 13; i++) p[i] = mem_cfg_val[i]; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

mem_cfg_val的链接地址会大于0x30000000,这个时候SDRAM还没被初始化,当执行下列语句时:

        p[i] = mem_cfg_val[i];
  • 1

即程序会去 mem_cfg_val 所在位置取值,所以会出错。因为此时SDRAM没初始化。

但是对于下列这种情况则不会出错:

/** 设置存储控制器以使用SDRAM*/
void memsetup(void)
{volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE; /* 这个函数之所以这样赋值,而不是像前面的实验那样将配置值 * 写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到 * SDRAM之前就可以在steppingstone中运行 */ /* 存储控制器13个寄存器的值 */ p[0] = 0x22011110; //BWSCON p[1] = 0x00000700; //BANKCON0 p[2] = 0x00000700; //BANKCON1 p[3] = 0x00000700; //BANKCON2 p[4] = 0x00000700; //BANKCON3 p[5] = 0x00000700; //BANKCON4 p[6] = 0x00000700; //BANKCON5 p[7] = 0x00018005; //BANKCON6 p[8] = 0x00018005; //BANKCON7 p[9] = 0x008C04F4; //REFRESH p[10] = 0x000000B1; //BANKSIZE p[11] = 0x00000030; //MRSRB6 p[12] = 0x00000030; //MRSRB7 }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

虽然这段代码中p指针变量也是位于0x30000000之后,但是p[0],p[1],这些值会被编译器编译成与MEM_CTL_BASE相关的值,也就是与SDRAM初始化有关的寄存器的地址。所以此时不管p指针位于哪也与它的地址无关。

新手一枚还在进步中,有说错的地方希望各位大神能够为我指出,也欢迎各位和我一起交流讨论。

关于ARM指令中位置无关和位置相关代码的认识【转】相关推荐

  1. ARM指令中如何判断一个立即数是有效立即数

    ARM指令中如何判断一个立即数是有效立数 在ARM处理器的汇编语言中,对指令语法格式中的<shifter_operand>的常数表达式有这样的规定:"该常数必须对应8位位图,即常 ...

  2. ARM指令中如何判断一个立即数是 有效立即数

    ARM指令中如何判断一个立即数是有效立数 在ARM处理器的汇编语言中,对指令语法格式中的<shifter_operand>的常数表达式有这样的规定:"该常数必须对应8位位图,即常 ...

  3. 位置无关(PIC)代码原理剖析

    共享库的一个关键目的是为了使多个进程能够共享内存中的同一份代码拷贝,已达到节约内存资源的目的.如何做到呢?一种方法是预先为每一个共享库指定好加载的地址范围,然后要求加载器总是将共享库加载至指定的位置. ...

  4. ARM指令中STM和LDM的理解误区

    STM和LDM的主要用途是现场保护.数据复制.参数传递等. 其模式有8种,如下:(前面4种用于数据块的传输,后面4种用于堆栈操作) (1)IA  每次传送后地址加4 (2)IB  每次传送前地址加4 ...

  5. arm指令中mov和ldr及ldr伪指令的区别

    ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令.比如想把数据从内存中某处读取到寄存器中,只能使用ldr比如:ldr r0, 0x12345678就 ...

  6. KSO-在NETCore中RabbitMQ的使用以及相关代码

    安装与配置 下载地址 Erlang https://www.erlang-solutions.com/resources/download.html rabbitMQ http://www.rabbi ...

  7. MITK中窗宽窗位相关代码

    目录 1  基本数据结构Image 2  窗宽窗位mitkLevelWindow定义类 3 窗宽窗位自动设定算法 4  窗宽窗位的属性设置LevelWindowProperty 类 5  mitkDi ...

  8. ARM的位置无关程序设计在Bootloader中的应用

    http://www.mcuol.com/tech/107/26052.htm 引言 基于位置无关代码PIC(PositionIndependent Code)的程序设计在嵌入式应用系统开发中具有重 ...

  9. ARM指令和Thumb指令的区别

    一.现在先区分下ARM指令集与Thumb指令集        Thumb 指令可以看作是 ARM 指令压缩形式的子集,是针对代码密度的问题而提出的,它具有 16 位的代码密度但是它不如ARM指令的效率 ...

  10. 大脸猫讲逆向之ARM汇编中PC寄存器详解

    i春秋作家:v4ever 近日,在研究一些开源native层hook方案的实现方式,并据此对ARM汇编层中容易出问题的一些地方做了整理,以便后来人能有从中有所收获并应用于现实问题中.当然,文中许多介绍 ...

最新文章

  1. 《中国人工智能学会通讯》——11.52 基于直推式学习的异质人脸图像 合成
  2. 数据库授予用户增删改查的权限的语句_mysql创建本地用户及赋予数据库权限的方法示例...
  3. webstorm快捷键说明
  4. RabbitMQ 高可用集群搭建及电商平台使用经验总结
  5. c++远征之继承篇——多重继承,多继承,虚继承,多继承时的重复定义解决方法
  6. 服务器ssr进程启动怎么运行,要SSR? NUXT项目从初始化到部署服务器流程全记录
  7. Laravel核心解读--Database(二) 查询构建器
  8. 从0开始配置Win环境下VScode (VScode For C/C++)
  9. mysql三高讲解(二):2.3 InnoDB索引即数据
  10. 【 Element UI 】—Element UI 的基本使用
  11. sql分区表上创建索引_SQL Server中分区表和索引的选项
  12. 漫画:分布式缓存服务器扛不住了怎么办?| 技术头条
  13. java Map及其实现类的底层原理
  14. code block怎样导入整个文件夹_XRD分析软件Xpert HighScore Plus 3安装和导入pdf卡片图文教程...
  15. node.js——uploads上传文件(multer)
  16. 为什么域名还会被DNS污染?域名被污染清洗方法!
  17. Oracle数据库中,deprecate和desupport的区别,以及Oracle 12.2中的deprecate 的参数
  18. 正则表达式-验证QQ号
  19. AMD启用虚拟化(AMD-V)
  20. android模拟微信聊天功能,android仿微信聊天界面 语音录制功能

热门文章

  1. 507 LOJ 「LibreOJ NOI Round #1」接竹竿
  2. 关闭jtag保留swd
  3. [No0000DD]C# StringEx 扩展字符串类 类封装
  4. SqlDataReader.GetFloat出错,类型无法转换
  5. git中Bash基本操作命令
  6. Intellij IDEA 报错java.lang.NoClassDefFoundError
  7. 基于OpenSSL的HTTPS通信C++实现
  8. C++学习总结(1)
  9. Python工程师面试题目
  10. 【2017级面向对象程序设计】作业二