1、用汇编写的函数,末尾应该添加mov pc,lr语句。

2、裸机程序的代码编写流程、文件的引用关系

3、关于链接地址和链接脚本的一些符号标识的理解

4、关于重定位的理解

(1)在sram内部重定位,因此不需要初始化DDR

链接脚本中链接地址是0xd0024000 在SRAM中

/** 文件名:  led.s   * 作者:    朱老师* 描述: 演示重定位(在SRAM内部重定位)*/#define WTCON      0xE2700000
#define SVC_STACK   0xd0037d80.global _start    // 把_start链接属性改为外部,这样其他文件就可以看见_start了_start:// 第1步:关看门狗(向WTCON的bit5写入0即可)ldr r0, =WTCONldr r1, =0x0str r1, [r0]// 第2步:设置SVC栈ldr sp, =SVC_STACK// 第3步:开/关icachemrc p15,0,r0,c1,c0,0;            // 读出cp15的c1到r0中//bic r0, r0, #(1<<12)            // bit12 置0  关icacheorr r0, r0, #(1<<12)          // bit12 置1  开icachemcr p15,0,r0,c1,c0,0;// 第4步:重定位。(这里的代码细节说明adr是与运行相关的,ldr是与链接相关的。)adr r0, _start     // adr指令用于加载_start当前运行地址             // adr加载时就叫短加载             ldr r1, =_start    // ldr指令用于加载_start的链接地址:0xd0024000    // ldr加载时如果目标寄存器是pc就叫长跳转,如果目标寄存器是r1等就叫长加载// bss段的起始地址ldr r2, =bss_start   // 就是我们重定位代码的结束地址,重定位只需重定位代码段和数据段即可cmp r0, r1            // 比较_start的运行时地址和链接地址是否相等beq clean_bss     // 如果相等说明不需要重定位,所以跳过copy_loop,直接到clean_bss// 如果不相等说明需要重定位,那么直接执行下面的copy_loop进行重定位// 重定位完成后继续执行clean_bss。// 用汇编来实现的一个while循环
copy_loop:ldr r3, [r0], #4    // 源str r3, [r1], #4  // 目的   这两句代码就完成了4个字节内容的拷贝cmp r1, r2            // r1和r2都是用ldr加载的,都是链接地址,所以r1不断+4总能等于r2bne copy_loop// 清bss段,其实就是在链接地址处把bss段全部清零
clean_bss:ldr r0, =bss_start                   ldr r1, =bss_endcmp r0, r1             // 如果r0等于r1,说明bss段为空(即不存在bss段),直接下去beq run_on_dram          // 清除bss完之后的地址mov r2, #0clear_loop:str r2, [r0], #4     // 先将r2中的值放入r0所指向的内存地址(r0中的值作为内存地址),cmp r0, r1             // 然后r0 = r0 + 4bne clear_looprun_on_dram:    // 长跳转到led_blink开始第二阶段ldr pc, =led_blink               // ldr指令实现长跳转//bl led_blink                 // bl指令实现短跳转// 汇编最后的这个死循环不能丢b .

(2)重定位至DDR,因此需要初始化DDR

链接脚本中的链接地址是0x2000 0000 在DDR中

/** 文件名:  led.s   * 作者:    朱老师* 描述: 演示重定位*/#define WTCON        0xE2700000
#define SVC_STACK   0xd0037d80.global _start
_start://..............// 第4步:初始化ddrbl sdram_asm_init //此函数末尾记得添加mov pc,lr// 第5步:重定位,后面的代码和之前的完全一样。故不写。

(3)由此可以看出,其实两者没有什么区别,只是多了一个内存初始化操作而已。

5、Makefile中用 -Ttext 0x0 来指定链接地址是0x0。这意味着我们认为这个程序将来会放在0x0这个内存地址去运行。但是实际上我们运行时的地址是0xd0020010(我们用dnw下载时指定的下载地址)。这两个地址看似不同,但是实际相同。这是因为S5PV210内部做了映射,把SRAM映射到了0x0地址去。

这段话的意思是,SRAM的0xd0020010地址,映射到了0x0地址,因此我们可以把程序的链接地址设为0x0。而0xd0020010这个地址,是三星这个开发板启动时,BL0执行完后自动运行开始的地址,它是由CPU的设计决定的。

6、问题,BL0判断启动介质为SD卡后,怎么知道拷贝SD卡的第几扇区?怎么知道拷贝到哪个地方?

我的猜想是,BL0内部的拷贝函数,它的参数之一肯定是SD卡的第一扇区,参数之二肯定是拷贝到哪个地方(这里肯定是0xd0020000),参数之三是拷贝的大小。

7、问题,如果文件太大,分为两部分了,这个拷贝的过程是如何的。

首先,两部分烧录到SD卡的哪个扇区位置是已知的,第一部分肯定是在第一扇区开始的位置,至于第二部分,合理即可,但必须知道是哪里(后面这个位置当作参数,传给第一部分中的拷贝函数)。

然后,第一部分被BL0拷贝到SRAM中运行,运行到第一部分中的拷贝函数时,拷贝函数从SD中拷贝第二部分内容。此函数之一肯定是第二部分在SD卡中的存储位置,参数二肯定是第二部分的大小,参数三肯定是拷贝到哪个位置。

最后,第一部分代码中有一个跳转语句,跳转到刚才拷贝的第二部分的位置,执行第二部分的代码。

8、关于7中的问题验证。

(1)此时write2sd中代码

可知,两部分分别烧录至第1扇区、第45扇区开始的地方。

#!/bin/sh
sudo dd iflag=dsync oflag=dsync if=./BL1/BL1.bin of=/dev/sdb seek=1
sudo dd iflag=dsync oflag=dsync if=./BL2/BL2.bin of=/dev/sdb seek=45

(2)第一部分的逻辑关系

首先,链接脚本的链接地址是0xd0020010。很好,这个地址就是BL1该呆在的地方,因为由CPU设计时规定的一开始运行的地址就是0xd0020010。

SECTIONS
{. = 0xd0020010;.text : {start.osdram_init.o* (.text)}.data : {* (.data)}bss_start = .; .bss : {* (.bss)}bss_end  = .;
}

其次,BL1的makefile中,还是要把BL1做16字节填充的,这也很合理。

接着,start.S中初始化DDR后,使用copy_bl2_2_ddr函数,把BL2复制到DDR中的某个位置,并跳转到该位置执行。

#define WTCON        0xE2700000#define SVC_STACK 0xd0037d80.global _start                    // 把_start链接属性改为外部,这样其他文件就可以看见_start了
_start:// 第1步:关看门狗(向WTCON的bit5写入0即可)ldr r0, =WTCONldr r1, =0x0str r1, [r0]// 第2步:设置SVC栈ldr sp, =SVC_STACK// 第3步:开/关icachemrc p15,0,r0,c1,c0,0;            // 读出cp15的c1到r0中//bic r0, r0, #(1<<12)            // bit12 置0  关icacheorr r0, r0, #(1<<12)          // bit12 置1  开icachemcr p15,0,r0,c1,c0,0;// 第4步:初始化ddrbl sdram_asm_init// 第5步:重定位,从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000bl copy_bl2_2_ddr// 汇编最后的这个死循环不能丢b .

copy_bl2_2_ddr函数。

关注下0xD0037F98,这个地址是BL0内置的SD卡拷贝函数地址。

#define SD_START_BLOCK   45
#define SD_BLOCK_CNT    32
#define DDR_START_ADDR  0x23E00000typedef unsigned int bool;// 通道号:0,或者2
// 开始扇区号:45
// 读取扇区个数:32
// 读取后放入内存地址:0x23E00000
// with_init:0
typedef bool(*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);typedef void (*pBL2Type)(void);// 从SD卡第45扇区开始,复制32个扇区内容到DDR的0x23E00000,然后跳转到23E00000去执行
void copy_bl2_2_ddr(void)
{// 第一步,读取SD卡扇区到DDR中pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)0xD0037F98);p1(2, SD_START_BLOCK, SD_BLOCK_CNT, (unsigned int *)DDR_START_ADDR, 0);     // 读取SD卡到DDR中// 第二步,跳转到DDR中的BL2去执行pBL2Type p2 = (pBL2Type)DDR_START_ADDR;p2();
}

(3)第二部分的逻辑关系

首先,链接地址应该由第一部分的拷贝函数拷贝将第二部分到哪里决定。由上面可知拷贝到0x23E00000,那么第二部分的连接地址也应该是0x23E00000。果真如此。

SECTIONS
{. = 0x23E00000;.text : {start.o* (.text)}.data : {* (.data)}bss_start = .; .bss : {* (.bss)}bss_end  = .;
}

其次,BL2的makefile中,不会再添加16字节填充的操作。果真如此。

然后,至于为什么会先执行start.S,是因为链接脚本中的.o文件的顺序决定的。start.S中的内容如下。

#define WTCON        0xE2700000
#define SVC_STACK   0xd0037d80.global _start    _start:ldr pc, =main               // ldr指令实现长跳转// 汇编最后的这个死循环不能丢b .

此时,会到有main函数的文件中,执行main函数。

总结:

(1)拷贝函数还是使用BL0中的拷贝函数。

(2)第二阶段的运行入口,是第二阶段的链接脚本指定的位置(该位置由第一阶段拷贝函数将内容拷贝到哪里决定)。

(3)第二阶段的脚本中.o的顺序,决定了第二阶段众多程序文件中,先执行哪些文件。

裸机中代码书写的细节总结相关推荐

  1. Python中代码书写规范与基本使用

    Q:代码等于号两边要不要空格,有影响吗? A:没有影响,要习惯空格,这样更加规范美观,有辨识度,如: a = 0 b = input('输入你的问题') Q:单引号.双引号.三引号的区别 A:正常使用 ...

  2. 【php基础入门】PHP环境搭建与初识php代码书写及演示PHP和JS中遍历数组的区别、引入外部文件等知识点学习笔记

    php是什么? PHP是PHP:HypertextPreprocessor(超文本预处理器)的首字母缩写,是一种跨平台的.开源的.免费的脚本语言,其语法吸收了C语言.Java 和 Perl 的特点,利 ...

  3. 天勤考研中数据结构的代码书写规范以及C与C++语言基础

    考研综合应用题中算法设计部分的代码书写规范 头文件 头文件部分如果题目没有特殊说明可以去掉. 常量 如果题目中要用到一个常量,则在用的地方加上一句注释,说明某某常量已定义即可,不必在前面补上#defi ...

  4. 翻译-高质量JavaScript代码书写基本要点(转载)

    by zhangxinxu from http://www.zhangxinxu.com 本文地址:http://www.zhangxinxu.com/wordpress/?p=1173 原文作者:S ...

  5. C++代码书写规范简介

    C++代码书写规范简介 编码规范并不是必须的,写代码遵守一定的规范,会使阅读代码修改bug轻松一些. 作为代码初学者,了解掌握最基本的代码书写规范知识是必要的.由于代码不仅仅是让程序执行,代码更是给人 ...

  6. c#书写规范之---代码书写规范

    代码书写规范          格式化使代码的逻辑结构很明显.花时间确保源代码以一致的逻辑方式进行格式化,这对于您和你的开发小组,以及以后维护源代码的其他开发人员都有很大的帮助. 以下几点是推荐的格式 ...

  7. php asp.net 代码量少,.NET_asp.net 反射减少代码书写量, 复制代码 代码如下:public b - phpStudy...

    asp.net 反射减少代码书写量 public bool Add(Liuyan refmodel) { string sql = "insert into liuyan(name,phon ...

  8. javascript历史、作用、三大组成、javascript代码书写位置、注意事项、变量

    JavaScript简介 JavaScript历史: JavaScript是一门解释型.动态类型.基于对象的脚本语言(不需要编译,直接执行,与之相对的是编译型语言),由美国网景公司的布兰登·艾奇发明, ...

  9. 面试官系统精讲Java源码及大厂真题 - 35 经验总结:各种锁在工作中使用场景和细节

    35 经验总结:各种锁在工作中使用场景和细节 富贵必从勤苦得. 引导语 本章主要说一说锁在工作中的使用场景,主要以 synchronized 和 CountDownLatch 为例,会分别描述一下这两 ...

最新文章

  1. Kubernetes Ingress 日志分析与监控的最佳实践
  2. 极路由安全设计架构分析
  3. 在IOS XR上配置BFD
  4. ozf oracle,ORACLE EBS 简称大全
  5. JAVA泛型--待续
  6. [spring boot]idea中实现热部署的方法
  7. ea mysql建模_UML数据建模工具之Enterprise Architect(EA)实例-UML应用
  8. POSIX 线程详解
  9. Python刚刚尝试就遇:SyntaxError: invalid syntax
  10. GoEasy使用方法记录
  11. 8uftp工具,这款工具有什么作用?它的优势在哪里?
  12. Linux下显示IP地理位置信息的小工具-nali
  13. 仿微信语音输入页面(讯飞语音)
  14. 分布式系统的经典基础理论
  15. 腾讯云轻量应用服务器怎么用?配置搭建网站教程
  16. 创建table表格总有一些线比其他线粗
  17. web前端 js实现频域水印制作
  18. 电压放大和电流放大区分
  19. anthony1314的小笔记
  20. 数字经济是什么?如何发展数字经济?

热门文章

  1. HDU - 5934
  2. C++主要操作符重载的定义和总结
  3. java实体类如果不重写toString方法,会如何?
  4. 关于java assertion
  5. 无处不在的container_of
  6. centos常见错误 Failed to set locale, defaulting to C
  7. 用反卷积(Deconvnet)可视化理解卷积神经网络还有使用tensorboard
  8. VMware Horizon虚拟桌面工具箱2.0-审计,远程协助,控制台,电源
  9. 让数据中心变得更加友好
  10. [转载]SYSCALL_DEFINE宏定义