itop4412 裸机

ARM裸机

笔记来源《朱老师物联网大讲堂》、《迅为电子提供的资料》、网上资料、基于itop4412 scp开发板

想搭建HomeAssistant环境,但迅为提供的uboot2020.10只有1G内存的版本,所以有了这篇笔记

需要代码资料可留言

1. ARM那些你得知道的事儿

1.1 ARM的成长史

ARM发展的里程碑

  • ARM的前身为艾康电脑(Acorn),于1978年,于英国剑桥创立

  • 在1980年代晚期,苹果电脑开始与艾康电脑合作开发新版的ARM核心

  • 1985年开发出全球第一款商用RISC处理器,即ARM1

  • 1990年艾康电脑财务危机,受苹果和VLSI的投资,分割出独立子公司Advanced RISC Machines (ARM) ,ARM公司正式成立面世

  • 1991年,ARM推出第一款嵌入式RISC处理器,即ARM6

  • 1993年,发布ARM7

  • 1997年,发布ARM9TDMI

  • 1999年,发布ARM9E

  • 2001年,发布ARMv6架构

  • 2002年,发布ARM11微架构

  • 2004年,发布ARMv7架构的Cortex系列处理器,同时推出Cortex-M3

  • 2005年,发布Cortex-A8处理器

  • 2007年,发布Cortex-M1和Cortex-A9

  • 2009年,实现Cortex-A9、发布Cortex-M0

  • 2010年,推出Cortex-M4、成立Linaro,推出Cortex-A15 MPcore高性能处理器

  • 2011年,推出Cortex-A7,ARMv8发布

  • 2012年,开始64位处理器进程

......

1.2 ARM的版本

内核(架构)版本处理器版本
ARMv1ARM1
ARMv2ARM2、ARM3
ARMv3ARM6、ARM7
ARMv4StrongARM、ARM7TDMI、ARM9TDMI
ARMv5ARM7EJ、ARM9E、ARM10E、XScale
ARMv6ARM11、ARM Cortex-M
ARMv7ARM Cortex-A、ARM Cortex-M、ARM Cortex-R
ARMv8ARM Cortex-A30、ARM Cortex-A50、ARM Cortex-A70

链接:汽车软件工程师基础知识 - ARM发展史及各时期内核(ARM1 ~ ARM11 / Cortex)介绍 - 知乎 (zhihu.com)

1.3 CPU、MPU、MCU、SOC

  • CPU:中央处理单元(Central Processing Unit),由运算器、控制器和寄存器及相应的总线构成。由CPU发展出来三个分支,一个是DSP(Digital Signal Processing/Processor,数字信号处理),另外两个是MCU(Micro Control Unit,微控制器单元)和MPU(Micro Processor Unit,微处理器单元)

  • MPU:微处理器(Micro Processor Unit),是由计算机中的CPU演变而来。 与CPU的区别在于,它只保留了与嵌入式应用紧密相关的功能硬件。目前主要的嵌入式处理器类型有ARM、MIPS、PowerPC、68000系列等。(摘自《ARM&Linux嵌入式系统教程》),我们经常说ARM,却忘了它是MPU。

  • MCU:微控制器(micro control unit),即平常所说的单片机(MCU一般以一种MPU为核心),是随着大规模集成电路的发展,将计算机CPU、RAM、ROM、定时器、计数器和多种I/O接口集成在一片芯片上,形成芯片级的芯片,比如51,AVR、STM这些芯片,相当于内部集成了整个计算机系统,可以加一些简单的外围器件(电阻、电容等)即可运行代码,其有处理器,有外围接口,基于已有的系统架构进行开发,应用者主要工作是添加外围设备和开发软件程序。我们说的ARM(Cortex-M、Cortex-A系列)直接放代码运行不了,因为本质上是cpu,必须添加响应RAM或ROM才能运行代码。

  • SOC:指的是片上系统(System on Chip)。可以这样对比来看:MCU只是芯片级的芯片,而SOC是系统级的芯片,它集成了MCU和MPU的优点,即拥有内置RAM和ROM的同时又像MPU那样强大,它可以存放并运行系统级别的代码,也就是说可以运行操作系统(以Linux OS为主)

2. ARM体系结构与汇编指令

2.1 指令集、编址方式、存储结构

2.1.1 RISC和CISC

  • CISC

    • complex instruction set computer复杂指令集CPU

    • CISC体系的设计理念是用最少的指令来完成任务(譬如计算乘法只需要一条MUL指令即可),因此CISC的CPU本身设计复杂、工艺复杂,但好处是编译器好设计。CISC出现较早,至今Intel还一直采用CISC设计

  • RISC

    • Reduced Instruction-Set Computer精简指令集CPU

    • RISC的设计理念是让软件来完成具体的任务,CPU本身仅提供基本功能指令集。因此RISC CPU的指令集中只有很少的指令,这种设计相对于CISC,CPU的设计和工艺简单了,但是编译器的设计变难了

2.1.2 统一编址&独立编址&哈佛结构&冯诺依曼结构

内存与IO

  • 内存是程序的运行场所,内存和CPU之间通过总线连接,CPU通过一定的地址来访问具体内存单元。

  • IO(input and output)是输入输出接口,是CPU和其他外部设备(如串口、LCD、触摸屏、LED等)之间通信的道路。一般的,IO就是指CPU的各种内部或外部外设。

内存的访问方式

  • 内存通过CPU的地址总线来寻址定位,然后通过CPU数据总线来读写。

  • CPU的地址总线的位数是CPU设计时确定的,因此一款CPU所能寻址的范围是一定的,而内存是需要占用CPU的寻址空间的。

  • 内存与CPU的这种总线式连接方式是一种直接连接,优点是效率高访问快,缺点是资源有限,扩展性差。

IO的访问方式

  • IO指的是与CPU连接的各种外设

  • CPU访问各种外设有2种方式:一种是类似于访问内存的方式,即把外设的寄存器当作一个内存地址来读写,从而以访问内存相同的方式来操作外设,叫IO与内存统一编址方式;另一种是使用专用的CPU指令来访问某种特定外设,叫IO与内存独立编址

冯诺依曼结构与哈佛结构

  • 程序和数据都放在内存中,且不彼此分离的结构称为冯诺依曼结构。譬如Intel的CPU均采用冯诺依曼结构。

  • 程序和数据分开独立放在不同的内存块中,彼此完全分离的结构称为哈佛结构。譬如大部分的单片机(MCS51、ARM9等)均采用哈佛结构。

  • 冯诺依曼结构中程序和数据不区分的放在一起,因此安全和稳定性是个问题,好处是处理起来简单。

  • 哈佛结构中程序(一般放在ROM、flash中)和数据(一般放在RAM中)独立分开存放,因此好处是安全和稳定性高,缺点是软件处理复杂一些(需要统一规划链接地址等)

2.2 寄存器

2.2.1 什么是寄存器

  • 寄存器属于CPU外设的硬件组成部分

  • CPU可以像访问内存一样访问寄存器

  • 寄存器是CPU的硬件设计者制定的,目的是留作外设被编程控制的“活动开关”

  • 正如汇编指令集是CPU的编程接口API一样,寄存器是外设硬件的软件编程接口API。使用软件编程控制某一硬件,其实就是编程读写该硬件的寄存器。

2.2.2 两类寄存器

  • SoC中有2类寄存器:通用寄存器和SFR

  • 通用寄存器(ARM中有37个)是CPU的组成部分,CPU的很多活动都需要通用寄存器的支持和参与。

  • SFR(special function register,特殊功能寄存器)不在CPU中,而存在于CPU的外设中,我们通过访问外设的SFR来编程操控这个外设,这就是硬件编程控制的方法。

2.3 ARM体系结构要点总结

2.3.1 ARM是RISC架构

  • 常用ARM汇编指令只有二三十条

  • ARM是低功耗CPU

2.3.2 ARM是统一编址的

  • 大部分ARM(M3 M4 M7 M0 ARM9 ARM11 A8 A9等)都是32位架构

  • 32位ARM CPU支持的内存少于4G,通过CPU地址总线来访问

  • SoC中的各种内部外设通过各自的SFR编程访问,这些SFR的访问方式类似于访问普通内存,这叫IO与内存统一编址

2.3.3 ARM是哈佛结构的

  • 常见ARM(除ARM7外)都是哈佛结构的

  • 哈佛结构保证了ARM CPU运行的稳定性和安全性,因此ARM适用于嵌入式领域

  • 哈佛结构也决定了ARM裸机程序(使用实地址即物理地址)的链接比较麻烦,必须使用复杂的链接脚本告知链接器如何组织程序;对于OS之上的应用(工作在虚拟地址之中)则不需考虑这么多

2.4 地址映射

2.4.1 什么是地址映射

  • Exynos4412属于ARM Cortex-A9架构,32位CPU,CPU设计时就有32根地址线&32根数据线。

  • 32根地址线决定了CPU的地址空间为4G,那么这4G空间如何分配使用?

2.4.2 一些术语

  • ROM:read only memory 只读存储器

  • RAM:ramdom access memory 随机访问存储器

  • IROM:internal rom 内部ROM,指的是集成到SoC内部的ROM

  • IRAM:internal ram 内部RAM,指的是集成到SoC内部的RAM

  • DRAM:dynamic ram 动态RAM

  • SRAM:static ram 静态RAM

  • SROM:static rom

  • SFR:special function register

2.5 内存和外存

2.5.1 概念

  • 内存:内部存储器,用来运行程序的,举例(DRAM SRAM DDR)

  • 外存:外部存储器,用来存储东西的,例(硬盘 Flash(Nand iNand···· U盘、SSD) 光盘)

  • CPU连接内存和外存的连接方式不同。

  • 内存需要直接地址访问,所以是通过地址总线&数据总线的总线式访问方式连接的(好处是直接访问,随机访问;坏处是占用CPU的地址空间,大小受限);

  • 外存是通过CPU的外存接口来连接的(好处是不占用CPU的地址空间,坏处是访问速度没有总线式快,访问时序较复杂)

2.5.2 SoC常用外存

  • NorFlash:总线式访问,接到SROM bank,优点是可以直接总线访问,一般用来启动。

  • NandFlash:分为SLC和MLC

  • eMMC:embeded MMC

  • iNand:SanDisk公司出产的eMMC

  • moviNand:三星公司出产的eMMC

  • oneNand:三星公司出的一种Nand

  • SD卡/TF卡/MMC卡

  • eSSD

2.6 ARM的编程模式和内核工作模式

2.6.1 ARM的基本设定

  • ARM 采用的是32位架构.

  • ARM 约定:

    • Byte :8 bits

    • Halfword :16 bits (2 byte)

    • Word : 32 bits (4 byte)

  • 大部分ARM core 提供:

    • ARM 指令集(32-bit)

    • Thumb 指令集(16-bit )

    • Thumb2指令集(16 & 32bit)

    • Jazelle cores 支持 Java bytecode

2.6.2 流水线

  • 传统的单片机(如8051)中,处理器只有完成一条指令的读取和执行后,才会开始下一条指令的处理,所以PC(程序计数器)总是指向正在执行的指令。而ARM体系架构中则引入了流水线的概念。

  • 到ARM7为止的ARM处理器使用了简单的三级流水线。三级流水线使用三个工位,将指令的处理分为三个阶段,分别为取指、译码和执行。取指:从存储器中装载;译码:识别将要被执行的指令;执行:处理指令并将结果写回寄存器。

  • 流水线机制 (1)在第1个周期,PC指向指令1,此时指令1进入三级流水线的取指阶段。 (2)在第2个周期,PC指向指令2,此时指令1进入三级流水线的译码阶段,同时取出指令2。 (3)在第3个周期,PC指向指令3,此时指令1进入三级流水线的执行阶段,指令2进入译码阶段,取出指令3。 (4)在第4个周期,指令1执行完成,指令2和指令3流水线推进一级,同时开始指令4的取指处理

    原文链接:ARM简介及Cortex-A9_arm cortex-a9-CSDN博客

2.6.3 Processor modes

ARMv7内核共支持9种处理器模式。当前程序状态寄存器CPSR的控制位M[4:0]可指示处理器正在执行的模式

  • User : 非特权模式,大部分任务执行在这种模式

  • FIQ : 当一个高优先级(fast) 中断产生时将会进入这种模式

  • IRQ : 当一个低优先级(normal) 中断产生时将会进入这种模式

  • Supervisor(SVC) :当复位或软中断指令执行时将会进入这种模式

  • Monitor(Mon):进行操作系统的高级处理

  • Hypervisor(Hyp):虚拟内存和存储器保护

  • Abort : 当存取异常时将会进入这种模式

  • Undef : 当执行未定义指令时会进入这种模式

  • System (Sys): 使用和User模式相同寄存器集的特权模式

注意

  • 除User(用户模式)是Normal(普通模式)外,其他8种都是Privilege(特权模式)。

  • Privilege中除Sys模式外,其余7种为异常模式。

  • 各种模式的切换,可以是程序员通过代码主动切换(通过写CPSR寄存器);也可以是CPU在某些情况下自动切换。

  • 各种模式下权限和可以访问的寄存器不同。

2.6.4 状态寄存器

在 armv7 中,状态寄存器为 CPSR,即 Current Program Status Register,该状态寄存器中保存了处理器运行时的状态信息:

CPSR 寄存器为 32 位,其中:

  • N:bit31,当运算结果为负且运算指令要求更新寄存器时,该位会被置位。

  • Z:bit30,当运算结果为0且运算指令要求更新寄存器时,该位会被置位。

  • C:bit29,当运算结果产生进位且指令要求更新寄存器时,该位会被置位,具体进位规则见下文。

  • V:bit28,当运算结果产生符号位溢出且指令要求更新寄存器时,该位会被置位,具体溢出规则见下文。

  • Q:bit27,累积饱和位,置为1表示某些指令中发生溢出或饱和,通常与数字信号处理(DSP)有关。

  • IT[1:0],bit[26:25],IT 位,同后续 IT[7:2] 相关。

  • J:bit24,指示处理器当前是否处于 Jazelle 状态。

  • bit[23:20],保留位。

  • GE[3:0]:bit[19:16],大于或者等于标志位,主要被 SIMD 指令使用,SIMD 全称为 single instruction multiple data,armv7 提供一条指令同时处理多个寄存器数据,属于扩展指令。

  • IT:bit[15:10],Thumb IT 指令集的 if-then 执行状态位。

  • E:bit9,指示当前处理器是运行于大端模式还是小端模式,同时,也可以通过设置该位来切换大小端模式。

  • mask bits,bit[8:6],屏蔽位 A、I、F,分别对应异步终止、快中断和中断,当对应的位为1时,相应的功能被屏蔽,当处理器需要屏蔽中断时,通常就是设置该屏蔽位。

  • T,bit5,指示处理器当前使用 thumb 指令集还是 arm 指令集,当前位和 J bit决定当前处理器的指令集,是 arm、Thumb、Jazelle 还是 ThumbEE 指令集。J、T的值对应指令集关系为:00-arm指令集,01-Thumb指令集,10-Jazelle指令集,11-ThumbEE指令集。

  • M,bit[4:0],模式位,指示处理器当前位于哪种运行模式下

条件位:

处理器状态:

模式位:

链接:armv7-A系列2-arm状态寄存器 - 知乎 (zhihu.com)

2.7 汇编指令集

2.7.1 关于汇编指令

  • (汇编)指令是CPU机器指令的助记符,经过编译后会得到一串10组成的机器码,可以由CPU读取执行。

  • (汇编)伪指令本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。

  • ARM官方的ARM汇编风格:指令一般用大写、Windows中IDE开发环境(如ADS、MDK等)常用。如: LDR R0, [R1]

  • GNU风格的ARM汇编:指令一般用小写字母、linux中常用。如:ldr r0, [r1]

2.7.2 汇编特点

(1)LDR/STR架构

  • ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。

  • ldr(load register)指令将内存内容加载入通用寄存器。

  • str(store register)指令将寄存器内容存入内存空间中。

  • ldr/str组合用来实现 ARM CPU和内存数据交换。

(2)8种寻址方式

  • 寄存器寻址

 @操作数在寄存器中,此句代码的功能就是将r2的数据加载到r1中。相当于C语言:r1 = r2
 move r1, r2
  • 立即数寻址

 @直接给出操作数,但是要受到合法立即数的限制。相当于c语言:r0=0xFF00
 mov r0, #0xFF00
  • 寄存器移位寻址

 @寄存器中的数进行移位后得到操作数。相当于C语言:r0=r1<<3
 mov r0, r1, lsl #3
  • 寄存器间接寻址

 @操作数不是在寄存器中,而是操作数的所在内存地址在寄存器中。在C语言中,就是r2中存的是一个指针,指针指向的内存地址存的操作数:r1=*r2
 ldr r1,[r2]
  • 基址变址寻址

 @操作数的地址等于寄存器中的值加上偏移量,访问该地址可以得到操作数。相当于C语言:r1=*(r2+4).
 ldr r1, [r2, #4]
  • 多寄存器寻址

 /*
 多寄存器寻址就是一次往多个寄存器里写数据,比如下面那条语句就是以r1里的数据为地址,读取地址里的数据依次写到后面的寄存器,每次操作完r1里的地址都+4,再去新的地址读取数据写到寄存器中。这对应于C语言里的数组,知道数组的首地址就可以每次数组首地址+4的方式来读取数组里的元素。常见用法:弹栈操作
 */
 ldmia sp!, {r2-r7, r12}
  • 堆栈寻址

 @堆栈寻址其实和多寄存寻址很像,几乎就是特殊的多寄存寻址。sp是栈指针,上面的语句就是把栈里的内容依次写到r2-r7还有lr寄存器中。常见用法:压栈操作
 stmfd sp!, {r2-r7, lr}
  • 相对寻址

 @pc当前值为基址,指令中值为偏移量,相加作为操作数的地址。地址分为相对地址和绝对地址,这里就是相对地址,跳转地址=当前地址+偏移量。此句是一个跳转指令,flag是汇编里的标号,相当于C语言中的函数名,eq是指令后缀,当上一句的执行结果为相等时才会执行本句,eq的作用相当于C语言的if条件语句。整句相当于c语言中的函数跳转,调用某个函数时,就会跳转到函数代码去执行,执行完毕后会返回。
 beq flag

(3)指令后缀

  • 同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:

    • B(byte)功能不变,操作长度变为8位,如 ldrb

    • H(half word)功能不变,长度变为16位,如 ldrh

    • S(signed)功能不变,操作数变为有符号,如 ldrsb ldrsh

    • S(S标志)功能不变,影响CPSR标志位,如 mov和movs,movs r0, #0

(4)条件执行后缀

(5)多级指令流水线

  • 为增加处理器指令流的速度,ARM使用多级流水线.,下图为3级流水线工作原理示意图。(Exynos4412为先进的推测型八级流水线)

–允许多个操作同时处理,而非顺序执行。

  • PC指向正被取指的指令,而非正在执行的指令

2.7.3 常用ARM指令

2.7.3.1 数据处理指令
  • 数据传输指令 mov mvn

  • 算术指令 add sub rsb adc sbc rsc

  • 逻辑指令 and orr eor bic

  • 比较指令 cmp cmn tst teq

  • 乘法指令 mvl mla umull umlal smull smlal

  • 前导零计数 clz

/*
 注:mov的两个操作数中不能有内存地址
 mvn和mov用法一样,区别是mov是原封不动的传递,而mvn是按位取反后传递
 按位取反的含义:譬如r1 = 0x000000ff,然后mov r0, r1 后,r0 = 0xff;但是我mvn r0, r1后,r0=0xffffff00。
 */
 mov r1, r0 @ 两个寄存器之间数据传递
 mov r1, #0xff @ 将立即数赋值给寄存器
 ​
 /*
 and逻辑与
 orr逻辑或
 eor逻辑异或
 bic位清除指令
 */
 bic r0,r1,#0x1f @ 将r1中的数的bit0到bit4清零后赋值给r0
 ​
 /*
 比较指令用来比较2个寄存器中的数
 注意:比较指令不用后加s后缀就可以影响cpsr中的标志位。
 */
 cmp r0, r1 @等价于 sub r2, r0, r1 (r2 = r0 - r1)
 cmn r0, r1 @等价于 add r0, r1
 tst r0, #0xf @测试r0的bit0~bit3是否全为0
 teq x0, x1 @用于比较操作数1和操作数2是否相等
 ​

2.7.3.2 cpsr访问指令
  • mrs用来读psr,msr用来写psr(指cpsr/spsr)

  • CPSR寄存器比较特殊,需要专门的指令访问,这就是mrs和msr

  • cpsr和spsr的区别和联系:cpsr是程序状态寄存器,整个SoC中只有1个;而spsr有7个,分别在5种异常模式下,作用是当从普通模式进入异常模式时,用来保存之前普通模式下的cpsr的,以在返回普通模式时恢复原来的cpsr。

2.7.3.3 跳转(分支)指令
  • b & bl & bx

  • b: 直接跳转(就没打开算返回)

  • bl :branch and link,跳转前把返回地址放入lr中,以便返回,以便用于函数调用

  • bx :跳转同时切换到ARM模式,一般用于异常处理的跳转。

2.7.3.4 访存指令
  • 单个字/半字/字节访问ldr/str

ldr,str 的第一操作数是目标寄存器,第二操作数是内存地址 ldrd 寄存器 <-- 内存 str 寄存器 --> 内存

  • 多字批量访问 ldm/stm

ldm,stm 的第一操作数是内存,第二操作数是寄存器列表,

注意第一操作数是寄存器里地址指向的内存,如ldmia r1, {r2-r7, r12}

可以理解为ldmia [r1], {r2-r7, r12}

ldm 内存 --> 寄存器

stm 内存 <-- 寄存器

  • swp

@ 将[r0]内存地址的内容放入r1寄存器中,将r2寄存器的值放入[r0]内存地址中,这两步同时进行。
swp r1, r2, [r0] 
@ 实现r1和[r0]内存地址中内容的互换
swp r1, r1, [r0] 
  • A合法立即数与非法立即数

    • ARM指令都是32位,除了指令标记和操作标记外,本身只能附带很少位数的立即数。因此立即数有合法和非法之分。

    • 合法立即数:经过任意位数的移位后非零部分可以用8位表示的即为合法立即数

    • 合法立即数: 0x000000ff0x00ff0000 0xf000000f

    • 非法立即数: 0x000001ff

2.7.3.5 软中断指令
  • swi(software interrupt)

  • 软中断指令用来实现操作系统中系统调用

2.7.3.6 协处理器cp15操作指令
  • mcr & mrc

  • mrc用于读取CP15中的寄存器

  • mcr用于写入CP15中的寄存器

  • 使用方法:

 opcode_1:对于cp15永远为0
 Rd:ARM的普通寄存器
 Crn:cp15的寄存器,合法值是c0~c15
 Crm:cp15的寄存器,一般均设为c0
 opcode_2:一般省略或为0
 ​
 mcr{<cond>}   p15, <opcode_1>, <Rd>,<Crn>, <Crm>, {<opcode_2>}
 mrc{<cond>}   p15, <Opcode_1>, <Rd>,<Crn>, <Crm>, {<Opcode_2>}
  • 什么是协处理器?

    • SoC内部另一处理核心,协助主CPU实现某些功能,被主CPU调用执行一定任务。

    • ARM设计上支持多达16个协处理器,但是一般SoC只实现其中的CP15.(cp:coprocessor)

    • 协处理器和MMU、cache、TLB等处理有关,功能上和操作系统的虚拟地址映射、cache管理等有关。

链接:Cortex-A 系列CP15协处理器简单解析 (ngui.cc)

2.7.3.7 ldm/stm与栈的处理

ldr/str每周期只能访问4字节内存,如果需要批量读取、写入内存时太慢,解决方案是stm/ldm

 /*
 ldm(load register mutiple)
 stm(store register mutiple)
 将r0存入sp指向的内存处(假设为0x30001000);然后地址+4(即指向0x30001004),将r1存入该地址;然后地址再+4(指向0x30001008),将r2存入该地址······直到r12内容放入(0x3001030),指令完成。
 一个访存周期同时完成13个寄存器的读写
 */
 stmia  sp,{r0 - r12}

8种后缀

 ia(increaseafter)先传输,再地址+4
 ib(increasebefore)先地址+4,再传输
 da(decreaseafter)先传输,再地址-4
 db(decreasebefore)先地址-4,再传输
 fd(full decrease)满递减堆栈
 ed(empty decrease)空递减堆栈
 fa(·······) 满递增堆栈
 ea(·······)空递增堆栈

四种栈

在ARM中,ATPCS(ARM-Thumb过程调用标准)要求使用满减栈

 空栈:栈指针指向空位,每次存入时可以直接存入然后栈指针移动一格;而取出时需要先移动一格才能取出
 满栈:栈指针指向栈中最后一格数据,每次存入时需要先移动栈指针一格再存入;取出时可以直接取出,然后再移动栈指针
 增栈:栈指针移动时向地址增加的方向移动的栈
 减栈:栈指针移动时向地址减小的方向移动的栈

!的作用:就是r0的值在ldm过程中发生的增加或者减少最后写回到r0去,也就是说ldm时会改变r0的值。

^的作用:

 ldmfd sp!,{r0 - r6, pc}
 ldmfd sp!,{r0 - r6, pc}^
 ^的作用:在目标寄存器中有pc时,会同时将spsr写入到cpsr,一般用于从异常模式返回。

2.7.3.8 ARM汇编伪指令
  • 伪指令的意义:

    • 伪指令不是指令,伪指令和指令的根本区别是经过编译后会不会生成机器码。

    • 伪指令的意义在于指导编译过程。

    • 伪指令是和具体的编译器相关的,我们使用gnu工具链,因此学习gnu环境下的汇编伪指令。

  • gnu汇编中的一些符号:

    • @ 用来做注释。可以在行首也可以在代码后面同一行直接跟,和C语言中//类似

    • :以冒号结尾的是标号

    • . 点号在gnu汇编中表示当前指令的地址

    • 立即数前面要加#或$,表示这是个立即数

  • 常用gnu伪指令:

@ 指定当前段为代码段
 .section .text 
 ​
  @ .ascii用于定义字符,.byte用于定义字节,.short用于定义半字,.word和.long是字
 .ascii .byte .short .long .word
 ​
 @ 定义数据 .quad用于定义双字,.float定义浮点型,.string用于定义字符串
 .quad .float .string 
 ​
 @以16字节对齐
 .align 4 
 ​
 @ 16字节对齐填充
 .balignl 16 0xabcdefgh 
 ​
 @ 类似于C中宏定义
 .equ 
  • 最重要的几个伪指令

    • ldr 大范围的地址加载指令

    • adr 小范围的地址加载指令

    • adrl 中等范围的地址加载指令

    • nop 空操作

    • ARM中有一个ldr指令,还有一个ldr伪指令

    • 一般都使用ldr伪指令而不用ldr指令,因为ldr伪指令可以不用考虑立即数是否合法,编译器会帮你转换为合法立即数

  • adr与ldr:

    • adr编译时会被1条sub或add指令替代,而ldr编译时会被一条mov指令替代或者文字池方式处理

    • adr总是以PC为基准来表示地址,因此指令本身和运行地址有关,可以用来检测程序当前的运行地址在哪里。

    • ldr加载的地址和链接时给定的地址有关,由链接脚本决定。

3.裸机实验说明

3.1 启动方式

(1)从哪里启动是由SoC的OM5:OM1这5个引脚的高低电平决定的。

(2)实际上在4412内部有一个寄存器(地址是0x10020000),这个寄存器中的值是硬件根据OM引脚的设置而自动设置值的。这个值反映的就是OM引脚的接法(电平高低),也就是真正的启动介质是谁。

(3)用户可以通过拨码开关来选择启动方式,itop4412支持上图红框两种启动方式,具体参考迅为的文档说明,也可以看iTop-4412 裸机教程(一)- 从启动方式开始4412教学Kilento的博客-CSDN博客

3.2 DNW方式进行裸机程序实验

(1)调整拨码开关,选择TF 卡启动模式,不插TF卡,让soc选择从USB启动,发现设备管理器中的连接总是在开机几秒后就丢失,原因应该是供电没锁存导致。

(2)没办法,只能用SD卡方式调试

3.3 SD卡方式进行裸机程序实验

(1)调整拨码开关,选择TF 卡启动模式,插入已烧录程序TF卡

4. GPIO和LED

源代码在代码目录下

(1)点亮led:1.leds_s

(2)流水灯:2.leds_s

注意

5.时钟

5.1 Exynos4412的时钟系统简介

5.1.1 时钟模块

(1)内部的时钟划分为5大模块 (2)CMU_CPU :CPU(Cortex-A9内核)、L2 cache controller、CoreSight (3)CMU_DMC:DMC、SSS、GIC (4)CMU_LEFTBUS 和CMU_RIGHTBUS: 全局数据总线、全局外设总线 (5)CMU_TOP :G3D, MFC, LCD0, ISP, CAM,TV, FSYS, MFC, GPS, MAUDIO ......

5.1.2 时钟来源

5.1.2.1 外部晶振

(1)Exynos4412外部有3个晶振接口:XRTCXTI、 XXTI、 XUSBXTI (2)itop4412使用24M外部晶振,接在XUSBXTI,不使用XXTI(被下拉接地),XRTCXTI由S5M8767A电源管理芯片提供

5.1.2.1 内部锁相环

内部四个PLL:APLL、MPLL、EPLL、VPLL

5.2 Exynos4412时钟详解

5.2.1 CMU_CPU

  • ARMCLK:给cpu内核工作的时钟,也就是所谓的主频

  • ACLK_COREM0:

  • ACLK_COREM1:

  • PERIPHCLK:

  • ATCLK:

  • PCLK_DBG:

5.2.2 CMU_DMC

  • SCLK_DMC:DMC时钟

  • ACLK_DMCD

  • ACLK_DMCP

  • ACLK_ACP

  • PCLK_ACP

  • SCLK_C2C

  • ACLK_C2C

5.2.3 CMU_LEFTBUS

  • ACLK_GDL

  • ACLK_GPL

5.2.4 CMU_RIGHTBUS

  • ACLK_GDR

  • ACLK_GPR

5.2.5 CMU_TOP

  • ACLK_400_MCUISP

  • ACLK_200

  • ACLK_100

  • ACLK_160

  • ACLK_133

  • SCLK_ONENAND

5.2.6 各时钟典型值(默认值,iROM中设置的值)

(1)刚上电时,默认是外部晶振+内部时钟发生器产生的24MHz频率的时钟直接给ARMCLK的,这时系统的主频就是24MHz,运行非常慢。 (2)iROM代码执行时初始化了时钟系统,这时给了系统一个默认推荐运行频率。这个时钟频率是三星推荐的4412工作性能和稳定性最佳的频率。 (3)高性能时钟典型值:

5.2.7 时钟体系框图

  • MUX:多路复用,即从多个输入源中选择一个

  • PLL:把低频率的输入时钟提高后输出

  • DIV:分频器,把高频率的输入时钟降频后输出

.global clock_init
 clock_init:
     push {lr}                   //保存返回地址,因为下面的代码还有函数调用
 ​
     ldr r0, =ELFIN_CLOCK_BASE   // 0x1003_0000
     
     // 1 设置CMU_CPU,暂时不使用PLL
     ldr r1, =0x0
     // APLL、MPLL
     ldr r2, =CLK_SRC_CPU_OFFSET
     str r1, [r0, r2]
     //  EPLL、VPLL
     ldr r2, =CLK_SRC_TOP0_OFFSET
     str r1, [r0, r2]
 ​
     // 2 设置锁定时间,使用默认值即可
     // 设置PLL后,时钟从Fin提升到目标频率时,需要一定的时间,即锁定时间
     ldr r1, =PLL_LOCK_VAL // 默认值0xffff
 ​
     // SET APLL LOCK TIME 
     ldr r2, =APLL_LOCK_OFFSET
     str r1, [r0, r2]
 ​
     // SET MPLL LOCK TIME 
     ldr r2, =MPLL_LOCK_OFFSET
     str r1, [r0, r2]
 ​
     // SET EPLL LOCK TIME 
     ldr r2, =EPLL_LOCK_OFFSET
     str r1, [r0, r2]
 ​
     // SET VPLL LOCK TIME 
     ldr r2, =VPLL_LOCK_OFFSET
     str r1, [r0, r2]
 ​
     // 3 设置PLL
     // PDIV:VCO分频因子,PDIV,MDIV:VCO倍频因子,SDIV:系统时钟分频因子
     // PDIV = 3, MDIV = 175, SDIV = 0, Fvco_out = Fref
     // Fref = FIN / PDIV = 24MHz / 3 = 8MHz 
     // FAPLLOUT = MDIV * FIN / (PDIV + 2^SDIV) = 125 * 24Mhz / (3 * 2 ^ 0) = 1000MHz
     ldr r1, =APLL_CON0_VAL
     ldr r2, =APLL_CON0_OFFSET
     str r1, [r0, r2]
 ​
     // FMPLLOUT = 800MHz
     ldr r1, =MPLL_CON0_VAL
     ldr r2, =MPLL_CON0_OFFSET
     str r1, [r0, r2]
 ​
     // FEPLLOUT = 400MHz
     ldr r1, =EPLL_CON0_VAL
     ldr r2, =EPLL_CON0_OFFSET
     str r1, [r0, r2]
 ​
     // FVPLLOUT = 100MHz
     ldr r1, =VPLL_CON0_VAL
     ldr r2, =VPLL_CON0_OFFSET
     str r1, [r0, r2]
 ​
     // 4 设置时钟源和分频
 ​
     // MUX_MPLL_USER_SEL_C = FOUTMPLL,MUX_HPM_SEL = MOUTAPLL,MUX_CORE_SEL = MOUTAPLL,MUX_APLL_SEL = MOUTAPLLFOUT 
     ldr r1, =0x01000001
     ldr r2, =CLK_SRC_CPU_OFFSET
     str r1, [r0, r2]
 ​
     ldr r2, =CLK_MUX_STAT_CPU_OFFSET
     ldr r3, =0x02110002
     bl wait_mux_state
 ​
     // MUX_PWI_SEL = XusbXTI,MUX_MPLL_SEL = MOUTMPLLFOUT 
     ldr r1, =0x00011000
     ldr r2, =CLK_SRC_DMC_OFFSET
     str r1, [r0, r2]
 ​
     ldr r2, =CLK_MUX_STAT_DMC_OFFSET
     ldr r3, =0x11102111
     bl wait_mux_state
 ​
     ldr r1, =CLK_DIV_DMC0_VAL
     ldr r2, =CLK_DIV_DMC0_OFFSET
     str r1, [r0, r2]
 ​
     ldr r1, =CLK_DIV_DMC1_VAL
     ldr r2, =CLK_DIV_DMC1_OFFSET
     str r1, [r0, r2]
 ​
     // MUX_ONENAND_SEL = ACLK_133, MUX_ACLK_133_SEL = SCLKMPLL, MUX_ACLK_160_SEL = SCLKMPLL, MUX_ACLK_100_SEL = SCLKMPLL
     // MUX_ACLK_200_SEL = SCLKMPLL, MUX_EPLL_SEL = FOUTEPLL, MUX_EPLL_SEL = FOUTEPLL, MUX_ONENAND_1_SEL = MOUTONENAND
     ldr r1, =0x00000110
     ldr r2, =CLK_SRC_TOP0_OFFSET
     str r1, [r0, r2]
 ​
     ldr r2, =CLK_MUX_STAT_TOP_OFFSET
     ldr r3, =0x11111221
     bl wait_mux_state
 ​
     // MUX_MPLL_USER_SEL_T = SCLKMPLLL, MUX_ACLK_266_GPS_SUB_SEL = DIVOUT_ACLK_266_GPS
     ldr r1, =0x00011000
     ldr r2, =CLK_SRC_TOP1_OFFSET
     str r1, [r0, r2]
 ​
     ldr r2, =CLK_MUX_STAT_TOP1_OFFSET
     ldr r3, =0x01122110
     bl wait_mux_state
 ​
     ldr r1, =CLK_DIV_TOP_VAL
     ldr r2, =CLK_DIV_TOP_OFFSET
     str r1, [r0, r2]
 ​
     // MUX_MPLL_USER_SEL_L = FOUTMPLL, MUX_GDL_SEL = SCLKMPLL
     ldr r1, =0x00000010
     ldr r2, =CLK_SRC_LEFTBUS_OFFSET
     str r1, [r0, r2]
 ​
     ldr r2, =CLK_MUX_STAT_LEFTBUS_OFFSET
     ldr r3, =0x00000021
     bl wait_mux_state
 ​
     ldr r1, =CLK_DIV_LEFRBUS_VAL
     ldr r2, =CLK_DIV_LEFTBUS_OFFSET
     str r1, [r0, r2]
 ​
     // MUX_MPLL_USER_SEL_R = FOUTMPLL, MUX_GDR_SEL = SCLKMPLL
     ldr r1, =0x00000010
     ldr r2, =CLK_SRC_RIGHTBUS_OFFSET
     str r1, [r0, r2]
 ​
     ldr r2, =CLK_MUX_STAT_RIGHTBUS_OFFSET
     ldr r3, =0x00000021
     bl wait_mux_state
 ​
     ldr r1, =CLK_DIV_RIGHTBUS_VAL
     ldr r2, =CLK_DIV_RIGHTBUS_OFFSET
     str r1, [r0, r2]
 ​
     pop {pc}    // 返回
 ​
 wait_mux_state:
     ldr r1, [r0, r2]
     cmp r1, r3
     bne wait_mux_state
     mov pc, lr

6. 重定位与SDRAM

6.1 Exynos4412的DMC

Exynos4412共有2个内存端口(就好像有2个内存插槽)。再结合查阅数据手册中内存映射部分,可知:两个内存端口分别叫DRAM0和DRAM1: DRAM0:内存地址范围:0x40000000~0x9FFFFFFF(1.5G),对应引脚是Xm1xxxx DRAM1: 内存地址范围:0xA0000000~0xFFFFFFFF(1.5G),对应引脚是Xm2xxxx

结论:

(1)Exynos4412最多支持内存为3GB,如果给)Exynos4412更多的内存CPU就无法识别。

(2))Exynos4412最多支持3GB内存,但是实际开发板不一定要这么多,itop4412_scp_2G开发板就只有2G内存,连接方法是在DRAM0端口分布1GB,在DRAM1端口分布了1GB。

(3)开发板上理论上内存合法地址是:0x40000000~0x7FFFFFFF(1GB) + 0xA0000000~0xDFFFFFFF(1GB),但实际的内存分布是0x40000000~0xBFFFFFFF(2GB),这里没搞明白。

6.2 itop4412_scp_2G的内存

  • 使用的是K4B4G164EBYK0,规格是32Mbit x 16I/Os x 8banks ,也就是512M,共4片

  • DDR3-1600 (800MHz@CL=11, tRCD=11, tRP=11)

  • (2^15(Row Addr) * 2^11(Column Addr) * 2^3 (Bank) * 16(bits)) / 8 = 512MB

  • 在逻辑上可以把这4颗内存芯片看成是2颗芯片,所以初始化的时候Number of Memory Chips选 1 chip

  • 可以从片选引脚可以确定1 chip 和 2 chips,CSn0和CSn1都用的是2 chips

6.3 初始化代码

  • 重点在DMC0_MEMCONTROL、DMC0_MEMCONFIG_0、DMC0_MEMCONFIG_1

  • 其他时序相关可以查看对应的手册

#include "s5pc210.h"
 ​
 #define MCLK_400
 ​
 #define ENABLE_DRAM0        (1)
 #define MDC0_CHIP_CNT       (1)
 ​
 #define ENABLE_DRAM1        (1)
 #define MDC1_CHIP_CNT       (1)
 ​
 ​
 ​
 // DMC0
 #define DMC0_MEMCONTROL     0x00302600  // MemControl   BL=8, 1Chip, DDR3 Type, dynamic self refresh, force precharge, dynamic power down off
 #define DMC0_MEMCONFIG_0    0x40801333  // MemConfig0   1GB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
 #define DMC0_MEMCONFIG_1    0x28F81312  // MemConfig1   默认值
 #define DMC0_TIMINGA_REF    0x00000618  // TimingAref   7.8us*400MHz=3120(0xC30),小于3120就可以
 //#define DMC0_TIMINGA_REF  0x000000BB  // TimingAref   
 ​
 // DMC1
 #define DMC1_MEMCONTROL     0x00302600  // MemControl   BL=8, 1Chip, DDR3 Type, dynamic self refresh, force precharge, dynamic power down off
 #define DMC1_MEMCONFIG_0    0x40801333  // MemConfig0   1GB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
 #define DMC1_MEMCONFIG_1    0x28F81312  // MemConfig1
 #define DMC1_TIMINGA_REF    0x00000618  
 //#define DMC1_TIMINGA_REF  0x000000BB  // TimingAref   
 ​
 #define CONFIG_IV_SIZE      0x1F
 ​
 ​
 ​
 wait_phy_state:
     ldr r1, [r0, #DMC_PHYSTATUS]
     tst r1, #(1<<2)
     beq wait_phy_state
     mov pc, lr
 ​
 dmc_delay:
     push {lr}
 1:  subs r2, r2, #1
     bne 1b
     pop {pc}
 ​
 .global sdram_asm_init
 ​
 sdram_asm_init: 
     push {lr}
 ​
 /*****************************************************************/
 /*DREX0***********************************************************/
 /*****************************************************************/
 ​
 #if(ENABLE_DRAM0)
     ldr r0, =APB_DMC_0_BASE
 ​
     ldr r1, =0xe0000086
     str r1, [r0, #DMC_PHYCONTROL1]
 ​
     ldr r1, =0xE3854C03
     str r1, [r0, #DMC_PHYZQCONTROL]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0xe000008e
     str r1, [r0, #DMC_PHYCONTROL1]
     ldr r1, =0xe0000086
     str r1, [r0, #DMC_PHYCONTROL1]
 ​
     ldr r1, =0x71101008
     str r1, [r0, #DMC_PHYCONTROL0]
     ldr r1, =0x7110100A
     str r1, [r0, #DMC_PHYCONTROL0]
 ​
     ldr r1, =0xe0000086
     str r1, [r0, #DMC_PHYCONTROL1]
     ldr r1, =0x7110100B
     str r1, [r0, #DMC_PHYCONTROL0]
 ​
     ldr r1, =0x00000000
     str r1, [r0, #DMC_PHYCONTROL2]
 ​
     ldr r1, =0x0FFF301A
     str r1, [r0, #DMC_CONCONTROL]
     ldr r1, =DMC0_MEMCONTROL
     str r1, [r0, #DMC_MEMCONTROL]
 ​
     ldr r1, =DMC0_MEMCONFIG_0
     str r1, [r0, #DMC_MEMCONFIG0]
 ​
 #if(MDC0_CHIP_CNT>1)
     ldr r1, =DMC0_MEMCONFIG_1
     str r1, [r0, #DMC_MEMCONFIG1]
 #endif
 ​
 #ifdef CONFIG_IV_SIZE
     ldr r1, =(0x80000000 | CONFIG_IV_SIZE)
 #else
     ldr r1, =0x08
 #endif
     str r1, [r0, #DMC_IVCONTROL]
 ​
     ldr r1, =0xff000000
     str r1, [r0, #DMC_PRECHCONFIG]
 ​
     ldr r1, =DMC0_TIMINGA_REF
     str r1, [r0, #DMC_TIMINGAREF] @TimingAref
 ​
 #ifdef MCLK_330
     ldr r1, =0x3545548d
     str r1, [r0, #DMC_TIMINGROW]
     ldr r1, =0x45430506
     str r1, [r0, #DMC_TIMINGDATA]
     ldr r1, =0x46000A3c
     str r1, [r0, #DMC_TIMINGPOWER]
 #endif
 ​
 #ifdef MCLK_400
     ldr r1, =0x7846654F/*0x4046654F*/
     str r1, [r0, #DMC_TIMINGROW] @TimingRow
     ldr r1, =0x46400506
     str r1, [r0, #DMC_TIMINGDATA] @TimingData
     ldr r1, =0x52000a3c
     str r1, [r0, #DMC_TIMINGPOWER] @TimingPower
 #endif
 ​
     /* chip 0 */
     ldr r1, =0x07000000
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0x00020000
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00030000
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00010002
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00000328
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0x0a000000
     str r1, [r0, #DMC_DIRECTCMD]
     
     mov r2, #0x100000
     bl dmc_delay
 ​
 #if(MDC0_CHIP_CNT>1)
     /* chip 1 */
     ldr r1, =0x07100000
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0x00120000
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00130000
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00110002
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00100328
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0x0a100000
     str r1, [r0, #DMC_DIRECTCMD]
     
     mov r2, #0x100000
     bl dmc_delay
 #endif
 ​
     ldr r1, =0xe000008e
     str r1, [r0, #DMC_PHYCONTROL1]
     ldr r1, =0xe0000086
     str r1, [r0, #DMC_PHYCONTROL1]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
 #endif
 /*****************************************************************/
 /*DREX1***********************************************************/
 /*****************************************************************/
 #if(ENABLE_DRAM1)
     ldr r0, =APB_DMC_1_BASE
 ​
     ldr r1, =0xe0000086
     str r1, [r0, #DMC_PHYCONTROL1]
 ​
     ldr r1, =0xE3854C03
     str r1, [r0, #DMC_PHYZQCONTROL]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0xe000008e
     str r1, [r0, #DMC_PHYCONTROL1]
     ldr r1, =0xe0000086
     str r1, [r0, #DMC_PHYCONTROL1]
 ​
     ldr r1, =0x71101008
     str r1, [r0, #DMC_PHYCONTROL0]
     ldr r1, =0x7110100A
     str r1, [r0, #DMC_PHYCONTROL0]
 ​
     ldr r1, =0xe0000086
     str r1, [r0, #DMC_PHYCONTROL1]
     ldr r1, =0x7110100B
     str r1, [r0, #DMC_PHYCONTROL0]
 ​
     ldr r1, =0x00000000
     str r1, [r0, #DMC_PHYCONTROL2]
 ​
     ldr r1, =0x0FFF301A
     str r1, [r0, #DMC_CONCONTROL]
     ldr r1, =DMC0_MEMCONTROL
     str r1, [r0, #DMC_MEMCONTROL]
 ​
     ldr r1, =DMC1_MEMCONFIG_0           @Interleaved?
     str r1, [r0, #DMC_MEMCONFIG0]
 ​
 #if(MDC1_CHIP_CNT>1)
     ldr r1, =DMC1_MEMCONFIG_1
     str r1, [r0, #DMC_MEMCONFIG1]
 #endif
 ​
 #ifdef CONFIG_IV_SIZE
     ldr r1, =(0x80000000 | CONFIG_IV_SIZE)
 #else
     ldr r1, =0x08
 #endif
     str r1, [r0, #DMC_IVCONTROL]
 ​
     ldr r1, =0xff000000
     str r1, [r0, #DMC_PRECHCONFIG]
 ​
     ldr r1, =DMC1_TIMINGA_REF
     str r1, [r0, #DMC_TIMINGAREF] @TimingAref
 ​
 #ifdef MCLK_330
     ldr r1, =0x3545548d
     str r1, [r0, #DMC_TIMINGROW]
     ldr r1, =0x45430506
     str r1, [r0, #DMC_TIMINGDATA]
     ldr r1, =0x46000A3c
     str r1, [r0, #DMC_TIMINGPOWER]
 #endif
 ​
 #ifdef MCLK_400
     ldr r1, =0x7846654F/*0x4046654F*/
     str r1, [r0, #DMC_TIMINGROW] @TimingRow
     ldr r1, =0x46400506
     str r1, [r0, #DMC_TIMINGDATA] @TimingData
     ldr r1, =0x52000a3c
     str r1, [r0, #DMC_TIMINGPOWER] @TimingPower
 #endif
 ​
     /* chip 0 */
     ldr r1, =0x07000000
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0x00020000
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00030000
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00010002
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00000328
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0x0a000000
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
 #if(MDC0_CHIP_CNT>1)
     /* chip 1 */
     ldr r1, =0x07100000
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0x00120000
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00130000
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00110002
     str r1, [r0, #DMC_DIRECTCMD]
     ldr r1, =0x00100328
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
     ldr r1, =0x0a100000
     str r1, [r0, #DMC_DIRECTCMD]
 ​
     mov r2, #0x100000
     bl dmc_delay
 #endif
 ​
     ldr r1, =0xe000008e
     str r1, [r0, #DMC_PHYCONTROL1]
     ldr r1, =0xe0000086
     str r1, [r0, #DMC_PHYCONTROL1]
 ​
     mov r2, #0x100000
     bl dmc_delay
 ​
 #endif
 /*****************************************************************/
 /*Finalize********************************************************/
 /*****************************************************************/
 ​
     ldr r0, =APB_DMC_0_BASE
     ldr r1, =0x0FFF303A
     str r1, [r0, #DMC_CONCONTROL]
 ​
     ldr r0, =APB_DMC_1_BASE
     ldr r1, =0x0FFF303A
     str r1, [r0, #DMC_CONCONTROL]
 ​
     pop {pc}

6.4 关于DRAM初始化的疑问

测试条件:

DMC0_MEMCONTROL 0x00312600

DMC1_MEMCONTROL 0x00312600

启用内存交错

结论:好像只设置DMC1_MEMCONFIG_0就可以,设置DMC0_MEMCONFIG_0无意义?

 #define rTest1  *((volatile unsigned int *)(0x78000000))
 #define rTest2  *((volatile unsigned int *)(0x88000000))
 #define rTest3  *((volatile unsigned int *)(0xA0000000))
 #define rTest4  *((volatile unsigned int *)(0xB8000000))
 ​
 int uart_main(void)
 {
     uart_init();
     rTest1 = 145;
     printf("rTest1= %ld\n",rTest1);
     rTest2 = 11;
     printf("rTest2= %ld\n",rTest2);
     rTest3 = 45;
     printf("rTest3= %ld\n",rTest3);
     rTest4 = 35;
     printf("rTest4= %ld\n",rTest4);
 ​
     printf("hello world +++++\n");
 ​
     while(1);
 }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值