裸机中代码书写的细节总结
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的顺序,决定了第二阶段众多程序文件中,先执行哪些文件。
裸机中代码书写的细节总结相关推荐
- Python中代码书写规范与基本使用
Q:代码等于号两边要不要空格,有影响吗? A:没有影响,要习惯空格,这样更加规范美观,有辨识度,如: a = 0 b = input('输入你的问题') Q:单引号.双引号.三引号的区别 A:正常使用 ...
- 【php基础入门】PHP环境搭建与初识php代码书写及演示PHP和JS中遍历数组的区别、引入外部文件等知识点学习笔记
php是什么? PHP是PHP:HypertextPreprocessor(超文本预处理器)的首字母缩写,是一种跨平台的.开源的.免费的脚本语言,其语法吸收了C语言.Java 和 Perl 的特点,利 ...
- 天勤考研中数据结构的代码书写规范以及C与C++语言基础
考研综合应用题中算法设计部分的代码书写规范 头文件 头文件部分如果题目没有特殊说明可以去掉. 常量 如果题目中要用到一个常量,则在用的地方加上一句注释,说明某某常量已定义即可,不必在前面补上#defi ...
- 翻译-高质量JavaScript代码书写基本要点(转载)
by zhangxinxu from http://www.zhangxinxu.com 本文地址:http://www.zhangxinxu.com/wordpress/?p=1173 原文作者:S ...
- C++代码书写规范简介
C++代码书写规范简介 编码规范并不是必须的,写代码遵守一定的规范,会使阅读代码修改bug轻松一些. 作为代码初学者,了解掌握最基本的代码书写规范知识是必要的.由于代码不仅仅是让程序执行,代码更是给人 ...
- c#书写规范之---代码书写规范
代码书写规范 格式化使代码的逻辑结构很明显.花时间确保源代码以一致的逻辑方式进行格式化,这对于您和你的开发小组,以及以后维护源代码的其他开发人员都有很大的帮助. 以下几点是推荐的格式 ...
- php asp.net 代码量少,.NET_asp.net 反射减少代码书写量, 复制代码 代码如下:public b - phpStudy...
asp.net 反射减少代码书写量 public bool Add(Liuyan refmodel) { string sql = "insert into liuyan(name,phon ...
- javascript历史、作用、三大组成、javascript代码书写位置、注意事项、变量
JavaScript简介 JavaScript历史: JavaScript是一门解释型.动态类型.基于对象的脚本语言(不需要编译,直接执行,与之相对的是编译型语言),由美国网景公司的布兰登·艾奇发明, ...
- 面试官系统精讲Java源码及大厂真题 - 35 经验总结:各种锁在工作中使用场景和细节
35 经验总结:各种锁在工作中使用场景和细节 富贵必从勤苦得. 引导语 本章主要说一说锁在工作中的使用场景,主要以 synchronized 和 CountDownLatch 为例,会分别描述一下这两 ...
最新文章
- Kubernetes Ingress 日志分析与监控的最佳实践
- 极路由安全设计架构分析
- 在IOS XR上配置BFD
- ozf oracle,ORACLE EBS 简称大全
- JAVA泛型--待续
- [spring boot]idea中实现热部署的方法
- ea mysql建模_UML数据建模工具之Enterprise Architect(EA)实例-UML应用
- POSIX 线程详解
- Python刚刚尝试就遇:SyntaxError: invalid syntax
- GoEasy使用方法记录
- 8uftp工具,这款工具有什么作用?它的优势在哪里?
- Linux下显示IP地理位置信息的小工具-nali
- 仿微信语音输入页面(讯飞语音)
- 分布式系统的经典基础理论
- 腾讯云轻量应用服务器怎么用?配置搭建网站教程
- 创建table表格总有一些线比其他线粗
- web前端 js实现频域水印制作
- 电压放大和电流放大区分
- anthony1314的小笔记
- 数字经济是什么?如何发展数字经济?
热门文章
- HDU - 5934
- C++主要操作符重载的定义和总结
- java实体类如果不重写toString方法,会如何?
- 关于java assertion
- 无处不在的container_of
- centos常见错误 Failed to set locale, defaulting to C
- 用反卷积(Deconvnet)可视化理解卷积神经网络还有使用tensorboard
- VMware Horizon虚拟桌面工具箱2.0-审计,远程协助,控制台,电源
- 让数据中心变得更加友好
- [转载]SYSCALL_DEFINE宏定义