ARM体系结构


前言

ARM体系结构与汇编 底层知识点集 杂乱的底层知识,帮助理解程序运行,以s5pv210来叙述,其他ARM通用,有些地方没头没尾难以阅读

1.ARM的模式、寄存器、异常向量表

ARM的两种状态:thumb态,ARM态
thumb态的寄存器如图,只有r0~r7:
在这里插入图片描述

  1. User:非特权模式,大部分任务执行在这一模式(除此模式外都是特权模式)
  2. FIQ:当一个高优先级(fast)中断产生时将会进入这种模式
  3. IRQ:当一个低优先级(normal)中断产生时将会进入这种模式
  4. Supervisor:当复位或软中断指令执行时将会进入这种模式
  5. Abort:存取异常时….
  6. Undef:当执行未定义指令时…
  7. System:使用和User模式相同寄存器集的特权模式(23456是异常模式, 7是系统模式)

ARM总共有37个寄存器
访问这37个寄存器:访问他的名字。但是在每种模式下最多只能看到18个寄存器,其他寄存器虽然名字相同,但是在当前模式不可见。(如对r13这个名字来说,在ARM中共有6个名叫r13(也叫sp)的寄存器,但是在每种特定处理模式下,只有一个r13是可见的,这种设计叫影子寄存器(banked register))system模式和user模式使用一样的寄存器集。User模式没有spsr寄存器。

ARM 37个寄存器都是32位长度。30个为“通用”型,1个固定用作PC,一个固定用作CPSR,5个固定用作5种异常模式下的SPSR。

CPSR(r16)程序状态寄存器(用来记录当前CPU所处的状态)
在这里插入图片描述

1.条件位(最重要的4位,在执行程序后会自动给出值)
N = Negative result from ALU (当结果为负时,N值输出1)
Z = Zero result from ALU (当结果为0时,Z值输出1)
C = ALU operation Carried out (当有进位时为1)
V = ALU operation oVerflowed (当有溢出时为1

2.Q 位:
仅ARM 5TE/J架构支持
指示饱和状态
3.J 位
仅ARM 5TE/J架构支持
J = 1: 处理器处于Jazelle状态

4.中断禁止位:
I = 1: 禁止 IRQ.
F = 1: 禁止 FIQ.
5.T Bit
仅ARM xT架构支持
T = 0: 处理器处于 ARM 状态(我们基本在ARM状态写程序)
T = 1: 处理器处于 Thumb 状态
6.Mode位:
处理器模式位
CPSR中各个bit位表明了CPU的某些状态信息,这些信息非常重要,和后面学到的汇编指令息息相关(譬如BLE指令中的E就和CPSR中的Z标志位有关)
CPSR中的I、F位和开中断、关中断有关
CPSR中的mode位(bit4~bit0共5位)决定了CPU的工作模式,可写,在uboot代码中会使用汇编进行设置。
SPSR(r17)用来保存CPSR
当切换用户时,spsr会保存当前用户的cpsr,以便在下次返回此用户时使用。cpsr和spsr的区别和联系:cpsr是程序状态寄存器,整个SoC中只有1个;而spsr有5个,分别在5种异常模式下,作用是当从普通模式进入异常模式时,用来保存之前普通模式下的cpsr的,以在返回普通模式时恢复原来的cpsr。
PC(r15)程序控制寄存器
PC(Program control register)为程序指针,PC指向哪里,CPU就会执行哪条指令(所以程序跳转时就是把目标地址代码放到PC中)
整个CPU中只有一个PC。
sp(r13)堆栈指针
在做函数调用或者是用户切换时,转去做其他事情之前,需要将当前状态保护起来,C语言工作需要堆栈环境,堆栈相当于C语言的工作现场
lr(r14)返回控制
在切换模式时用来存返回地址。

异常处理

1.异常向量表
所有的CPU都有异常向量表,这是CPU设计时就设定好的,是硬件决定的。
当异常发生时,CPU会自动动作(PC跳转到异常向量处处理异常,有时伴有一些辅助动作)
异常向量表是硬件向软件提供的处理异常的支持。
2. ARM的异常处理机制

在这里插入图片描述

v 异常向量表
当异常产生时, ARM core:(CPU自动动作)
– 拷贝CPSR到 SPSR_< mode >
– 设置适当的 CPSR 位:
改变处理器状态进入ARM态(cpsr的T位置0,Thum态不能做异常处理)
改变处理器模式进入相应的异常模式(cpsr的mode 位改变)
设置中断禁止位禁止相应中断 (如果需要)
– 保存返回地址到 LR_< mode >
– 设置 PC 为相应的异常向量(以上3步都为保护现场,PC设置为相应的异常向量,如在IRQ模式下,pc=0x18)
返回时, 异常处理需要:
– 从 SPSR_< mode >恢复CPSR(对应上面的CPSR->SPSR,若之前处理器状态为Thum态,则这个在SPSR中保存的状态会反会到CPSR中)
– 从LR_<mode>恢复PC
Note:这些操作只能在 ARM 态执行.
异常处理中有一些是硬件自动做的,有一些是程序员需要自己做的。需要搞清楚哪些是需要自己做的,才知道如何写代码。
以上说的是CPU设计时提供的异常向量表,一般称为一级向量表。有些CPU为了支持多个中断,还会提供二级中断向量表,处理思路类似于这里说的一级中断向量表。

2.SRAM空间

BL0+BL1+BL2
重启后会在svc模式,这时只有sram可以使用,也就是裸机下没有去初始化ddr的话,只有96kb的内存可以使用,我们需要为c程序提供运行环境,就要设置栈,在sram空间中,栈是被规定在特定位置的:我们设置时需要设置svc的头地址,并且栈为满减栈(arm要求实现为满减栈)所以从尾地址开始:0xd0037D80

3.汇编程序和C程序互相调用

汇编跳转到C语言:b、bx、bl Cfunction等
C语言内嵌汇编:_asm_、_asm、#asm和#endasm
:

//1.预处理指令
#asm
       mov ax,word ptr 8[bp]
       imul ax word ptr 10[bp]
#endasm
//2.asm语句
asm   mov ax,word ptr 8[bp]
//3._asm、_asm_

Linux及uboot常用的方法:_ asm _
__asm__(汇编语句模板 : 输出部分 : 输入部分 : 破坏描述部分);

其中包括四个部分,每个部分之间使用":“分开
汇编语句模板是汇编命令的字符串,输出部分是需要输出到C变量参数列表,输入部分是需要从C变量输入到ASM汇编的参数列表,破坏描述部分是指执行汇编指令会破坏的寄存器描述。
第一部分是必须写的,后面三部分是可以省略,但是分号”:“不能省略!
一个嵌套汇编块里面可以写多条汇编指令,指令之间需要换行符隔开 “\n” 或分号" ; "隔开

例子:

unsigned long x; 
            unsigned long temp; 
            (void) (&temp == &x); 
            __asm__ __volatile__( 
                                               "mrs %0, cpsr \n" 
                                               "orr %1, %0, #128 \n"
                                               "msr cpsr_c, %1\n"  
                                              : "=r" (x), "=r" (temp)	//输出的列表,每个参数是以逗号","隔开的,"=r"(x)表示asm中第一个参数的值保存到变量x中;"=r"(temp)表示asm中第二个参数的值保存到变量temp中.
                                              : "r" (x) 	//输入参数,不带=
                                              : "memory", "cc");	//"memory"描述符 表示
        															//1.将不重新排序该段内嵌汇编指令与前面的指令。
       															 	//2.不使用寄存器作为缓存。

C调用汇编函数
知道以下规则,调用规则就一目了然了:

1.对于参数个数不多于 4 的函数,编译器必须按参数在列表中的顺序,自左向右 为它们分配寄存器 R0~R3

2.其中函数返回时,R0 还被用来存放函数的返回值

3.如果函数的参数多于 4 个,那么多余的参数则按自右向左的顺序压入数据堆栈(参数入栈顺序与参数顺序相反)

4.一个浮点数可能几个整数型的寄存器进行传送

5.双精度和long long类型的参数通过两个连续的寄存器来传递,返回值通过R0和R1返回(大端系统下,R0包含高位有效字)

6.BL0做的部分事情

设置栈,设置SVC模式,关看门狗,开icache

#define WTCON		0xE2700000
#define SVC_STACK	0xd0037d80
//观看门狗
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]

// 设置SVC栈
ldr sp, =SVC_STACK
	
mrc p15,0,r0,c1,c0,0;			// 读出cp15的c1到r0中
bic r0, r0, #(1<<12)				// bit12 置0  关icache
orr r0, r0, #(1<<12)				// bit12 置1  开icache
mcr p15,0,r0,c1,c0,0;

7.重定位代码

由于有位置有关码和位置无关码,位置有关码必须在指定位置执行。
Makefile中-Ttext 0x0就是链接到0地址
重定位:代码复制到新的内存中
跳转,pc指向新地址(重定位位置)
长跳转:ldr pc, =led_blink
这句长跳转直接从0xd0020010处代码跳转到0xd0024000开头的那一份代码的led_blink函数处去执行
短跳转:bl led_blink
加载:目的:将链接地址和符号地址的偏移量计算出来,就可以知道跳转的大小。
长加载: ldr,加载的是链接地址,原理:将地址读进来
短加载: adr,加载的是运行时地址,原理:相对pc的地址偏移
ldr加载时如果目标寄存器是pc就叫长跳转,如果目标寄存器是r1等就叫长加载
.S文件代码:(重定位代码运行地址和链接地址都是读到的,此代码可移植,很有用)

//重定位
    //加载当前运行地址
    adr r0,_start
    //加载链接地址
    ldr r1,=_start
    //如果不一样,则开始拷贝,一样,就跳过
    ldr r2,=bss_start
    cmp r0,r1
    beq clean_bss

    //拷贝:
copy_loop:
    ldr r3,[r0],#4
    str r3,[r1],#4
    cmp r1,r2
    bne copy_loop

    //清bss
clean_bss:
    ldr r0, =bss_start	//细节:ldr 长加载,加载的是链接地址后的bss_start地址,所以这里在链接地址=运行地址时清的是当前bss段,在链接地址!=运行地址时清的是重定位的bss段
    ldr r1, =bss_end
    cmp r0,r1
    beq run_on_dram
    mov r2, #0
clear_loop:
    str r2,[r0],#4
    cmp r0,r1
    bne clear_loop

run_on_dram:
    ldr pc, =led_blink

如上拷贝到bss_start就停止了,也就是拷贝长度为bss_start-_start,因为bss段不需要拷贝(都是0),只需要拷贝代码段和数据段
清除bss段是为了满足C语言的运行时要求(C语言要求显式初始化为0的全局变量,或者未显式初始化的全局变量的值为0,实际上C语言编译器就是通过清bss段来实现C语言的这个特性的)。一般情况下我们的程序是不需要负责清零bss段的(C语言编译器和链接器会帮我们的程序自动添加一段头程序,这段程序会在main函数之前运行)。但是在代码重定位了之后,因为编译器附加的代码只是清除了运行地址那一份代码中的bss,而未清除重定位地址处开头的那一份代码的bss,所以重定位之后需要自己去清除bss。
重定位时不能让新代码把旧代码给覆盖了

8.链接脚本.lds文件

告诉代码的链接地址
Makefile的-T参数变为:-Tlink.lds
链接地址(代码在的位置)改变后,重定位地址(代码运行的位置)也必须改变。

SECTIONS
{
    . = 0xd0024000;

    .text : {
        start.o
        * (.text)
    }

    .data : {
        *(.data)
    }

    bss_start = .;
    .bss : {
        *(.bss)
    }
    bss_end = .;
}

9.usb下载和sd烧录

从第一张图可以看出,BL1start address是在0xd0020010,前面空了16个字节,是用来做校验头的(sd、inand等块设备中存储的数据可能会出错,所以要校验,usb下载是不需要校验的),所以sd的bin文件都需要加校验头。没有超过BL1数据范围,也就是16k,都可以直接运行在0xd0020010上,就是把逻辑代码当成BL1去执行。
超过16k怎么办?
那就要在ddr中运行了,那就要去初始化ddr
对于usb:在0xd0020010下载x210_usb.bin,这个是初始化ddr的代码,然后将程序下载到23e00000(ddr地址中)
对于sd:就需要自己写初始化sdram的代码,然后重定位代码到sdram,然后跳转执行。(上面讲了重定位)

以下是sd启动的两个操作:加16字节校验头;初始化sdram

那么上面讲了16字节校验头,我们要做什么,其实就是BL0中最后需要做的事情:注意此文件是在pc上运行的,c的标准库文件都可以使用
1.申请16kbuf,用随便16字节的char*内容填前16字节
2.将原bin读到buf中(从16字节开始)
3.计算校验和,将校验和写到前16字节的第8个字节开始处
4.将校验和+bin内容的内容写到新文件中
传两个参数,第一个是原始bin文件,第二个是加了校验头的新文件

mkv210_image.c:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define BUFSIZE                 (16*1024)
#define IMG_SIZE                (16*1024)
#define SPL_HEADER_SIZE         16
//#define SPL_HEADER              "S5PC110 HEADER  "
#define SPL_HEADER              "****************"

int main (int argc, char *argv[])
{
	FILE		*fp;
	char		*Buf, *a;
	int		BufLen;
	int		nbytes, fileLen;
	unsigned int	checksum, count;
	int		i;
	// 1. 3个参数
	if (argc != 3)
	{
		printf("Usage: %s <source file> <destination file>\n", argv[0]);
		return -1;
	}
	// 2. 分配16K的buffer
	BufLen = BUFSIZE;
	Buf = (char *)malloc(BufLen);
	if (!Buf)
	{
		printf("Alloc buffer failed!\n");
		return -1;
	}

	memset(Buf, 0x00, BufLen);

	// 3. 读源bin到buffer
	// 3.1 打开源bin
	fp = fopen(argv[1], "rb");
	if( fp == NULL)
	{
		printf("source file open error\n");
		free(Buf);
		return -1;
	}
	// 3.2 获取源bin长度
	fseek(fp, 0L, SEEK_END);								// 定位到文件尾
	fileLen = ftell(fp);									// 得到文件长度
	fseek(fp, 0L, SEEK_SET);								// 再次定位到文件头
	// 3.3 源bin长度不得超过16K-16byte
	count = (fileLen < (IMG_SIZE - SPL_HEADER_SIZE))
		? fileLen : (IMG_SIZE - SPL_HEADER_SIZE);
	// 3.4 buffer[0~15]存放"S5PC110 HEADER  "
	memcpy(&Buf[0], SPL_HEADER, SPL_HEADER_SIZE);
	// 3.5 读源bin到buffer[16]
	nbytes = fread(Buf + SPL_HEADER_SIZE, 1, count, fp);
	if ( nbytes != count )
	{
		printf("source file read error\n");
		free(Buf);
		fclose(fp);
		return -1;
	}
	fclose(fp);

	// 4. 计算校验和
 	// 4.1 从第16byte开始统计buffer中共有几个1
	// 4.1 从第16byte开始计算,把buffer中所有的字节数据加和起来得到的结果
	a = Buf + SPL_HEADER_SIZE;
	for(i = 0, checksum = 0; i < IMG_SIZE - SPL_HEADER_SIZE; i++)
		checksum += (0x000000FF) & *a++;
	// 4.2 将校验和保存在buffer[8~15]
	a = Buf + 8;							// Buf是210.bin的起始地址,+8表示向后位移2个字,也就是说写入到第3个字
	*( (unsigned int *)a ) = checksum;

	// 5. 拷贝buffer中的内容到目的bin
	// 5.1 打开目的bin
	fp = fopen(argv[2], "wb");
	if (fp == NULL)
	{
		printf("destination file open error\n");
		free(Buf);
		return -1;
	}
	// 5.2 将16k的buffer拷贝到目的bin中
	a = Buf;
	nbytes	= fwrite( a, 1, BufLen, fp);
	if ( nbytes != BufLen )
	{
		printf("destination file write error\n");
		free(Buf);
		fclose(fp);
		return -1;
	}

	free(Buf);
	fclose(fp);

	return 0;
}

初始化sdram是一段汇编代码(太长了还是不放了)10.28
想想还是放吧,有利于理解MP引脚、DRAM启动初始化过程(10.31更新)
步骤:

Initialization sequence for DDR2 memory type:

  1. To provide stable power for controller and memory device, the controller must assert and hold CKE to a logic low level. Then apply
    stable clock. Note: XDDR2SEL should be High level to hold CKE to low.
  2. Set the PhyControl0.ctrl_start_point and PhyControl0.ctrl_inc bit-fields to correct value according to clock frequency. Set the
    PhyControl0.ctrl_dll_on bit-field to ‘1’ to turn on the PHY DLL.
  3. DQS Cleaning: Set the PhyControl1.ctrl_shiftc and PhyControl1.ctrl_offsetc bit-fields to correct value according to
    clock frequency and memory tAC parameters.
  4. Set the PhyControl0.ctrl_start bit-field to ‘1’.
  5. Set the ConControl. At this moment, an auto refresh counter should be off.
  6. Set the MemControl. At this moment, all power down modes should be off.
  7. Set the MemConfig0 register. If there are two external memory chips, set the MemConfig1 register.
  8. Set the PrechConfig and PwrdnConfig registers.
  9. Set the TimingAref, TimingRow, TimingData and TimingPower registers according to memory AC parameters.
  10. If QoS scheme is required, set the QosControl0~15 and QosConfig0~15 registers.
  11. Wait for the PhyStatus0.ctrl_locked bit-fields to change to ‘1’. Check whether PHY DLL is locked.
  12. PHY DLL compensates the changes of delay amount caused by Process, Voltage and Temperature (PVT) variation during memory operation.
    Therefore, PHY DLL should not be off for reliable operation. It can be
    off except runs at low frequency. If off mode is used, set the
    PhyControl0.ctrl_force bit-field to correct value according to the
    PhyStatus0.ctrl_lock_value[9:2] bit-field to fix delay amount. Clear
    the PhyControl0.ctrl_dll_on bit-field to turn off PHY DLL.
  13. Confirm whether stable clock is issued minimum 200us after power on
  14. Issue a NOP command using the DirectCmd register to assert and to hold CKE to a logic high level.
  15. Wait for minimum 400ns.
  16. Issue a PALL command using the DirectCmd register.
  17. Issue an EMRS2 command using the DirectCmd register to program the operating parameters.
  18. Issue an EMRS3 command using the DirectCmd register to program the operating parameters.
  19. Issue an EMRS command using the DirectCmd register to enable the memory DLLs.
  20. Issue a MRS command using the DirectCmd register to reset the memory DLL.
  21. Issue a PALL command using the DirectCmd register.
  22. Issue two Auto Refresh commands using the DirectCmd register.
  23. Issue a MRS command using the DirectCmd register to program the operating parameters without resetting the memory DLL.
  24. Wait for minimum 200 clock cycles.
  25. Issue an EMRS command using the DirectCmd register to program the operating parameters. If OCD calibration is not used, issue an EMRS
    command to set OCD Calibration Default. After that, issue an EMRS
    command to exit OCD Calibration Mode and to program the operating
    parameters.
  26. If there are two external memory chips, perform steps 14~25 for chip1 memory device.
  27. Set the ConControl to turn on an auto refresh counter. 28. If power down modes is required, set the MemControl registers.

首先来看一下电路图:
DDR引脚图:

CPU引脚图:

代码如下:(s5pv210.h在:https://blog.csdn.net/weixin_44705391/article/details/121061661
代码感觉有点问题:MP1_0DRV_SR_OFFSET这里设置驱动强度,但是设置的并不是ddr引脚,而是存储器引脚,有知道的给我解个惑。

#include "s5pv210.h"

#if 1
#define DMC0_MEMCONTROL		0x00202400 	// MemControl	BL=4, 1Chip, DDR2 Type, dynamic self refresh, force precharge, dynamic power down off

#define DMC0_MEMCONFIG_0	0x20F01323	// MemConfig0	256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
#define DMC0_MEMCONFIG_1	0x30F00312	// MemConfig1		默认值

#define DMC0_TIMINGA_REF	0x00000618	// TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)
#define DMC0_TIMING_ROW		0x28233287	// TimingRow	for @200MHz
#define DMC0_TIMING_DATA	0x23240304	// TimingData	CL=3
#define	DMC0_TIMING_PWR		0x09C80232	// TimingPower

#define	DMC1_MEMCONTROL		0x00202400	// MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off

#define DMC1_MEMCONFIG_0	0x40F01323	// MemConfig0	512MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
#define DMC1_MEMCONFIG_1	0x60E00312	// MemConfig1

#define DMC1_TIMINGA_REF	0x00000618	// TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4
#define DMC1_TIMING_ROW		0x28233289	// TimingRow	for @200MHz
#define DMC1_TIMING_DATA	0x23240304	// TimingData	CL=3
#define	DMC1_TIMING_PWR		0x08280232	// TimingPower

#endif

#if 0

#define DMC0_MEMCONTROL		0x00212400 	// MemControl	BL=4, 1Chip, DDR2 Type, dynamic self refresh, force precharge, dynamic power down off

#define DMC0_MEMCONFIG_0	0x20E01323	// MemConfig0	512MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
#define DMC0_MEMCONFIG_1	0x40F01323	// MemConfig1

#define DMC0_TIMINGA_REF	0x00000618	// TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)
#define DMC0_TIMING_ROW		0x28233287	// TimingRow	for @200MHz
#define DMC0_TIMING_DATA	0x23240304	// TimingData	CL=3
#define	DMC0_TIMING_PWR		0x09C80232	// TimingPower

#define	DMC1_MEMCONTROL		0x00202400	// MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off

#define DMC1_MEMCONFIG_0	0x40C01323	// MemConfig0	512MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
#define DMC1_MEMCONFIG_1	0x00E01323	// MemConfig1

#define DMC1_TIMINGA_REF	0x00000618	// TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4
#define DMC1_TIMING_ROW		0x28233289	// TimingRow	for @200MHz
#define DMC1_TIMING_DATA	0x23240304	// TimingData	CL=3
#define	DMC1_TIMING_PWR		0x08280232	// TimingPower


#endif



.global sdram_asm_init

sdram_asm_init:	
	ldr	r0, =0xf1e00000
	ldr	r1, =0x0
	str	r1, [r0, #0x0]

	/* DMC0 Drive Strength (Setting 2X) */
	
	ldr	r0, =ELFIN_GPIO_BASE

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_0DRV_SR_OFFSET]		// 寄存器中对应0b10,就是2X

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_1DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_2DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_3DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_4DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_5DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_6DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP1_7DRV_SR_OFFSET]

	ldr	r1, =0x00002AAA
	str	r1, [r0, #MP1_8DRV_SR_OFFSET]

	
	/* DMC1 Drive Strength (Setting 2X) */
	
	ldr	r0, =ELFIN_GPIO_BASE
	
	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_0DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_1DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_2DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_3DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_4DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_5DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_6DRV_SR_OFFSET]

	ldr	r1, =0x0000AAAA
	str	r1, [r0, #MP2_7DRV_SR_OFFSET]

	ldr	r1, =0x00002AAA
	str	r1, [r0, #MP2_8DRV_SR_OFFSET]
	
	/* DMC0 initialization at single Type*/
	ldr	r0, =APB_DMC_0_BASE

	ldr	r1, =0x00101000				@PhyControl0 DLL parameter setting, manual 0x00101000
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00000086				@PhyControl1 DLL parameter setting, LPDDR/LPDDR2 Case
	str	r1, [r0, #DMC_PHYCONTROL1]

	ldr	r1, =0x00101002				@PhyControl0 DLL on
	str	r1, [r0, #DMC_PHYCONTROL0]

	ldr	r1, =0x00101003				@PhyControl0 DLL start
	str	r1, [r0, #DMC_PHYCONTROL0]

find_lock_val:
	ldr	r1, [r0, #DMC_PHYSTATUS]		@Load Phystatus register value
	and	r2, r1, #0x7
	cmp	r2, #0x7				@Loop until DLL is locked
	bne	find_lock_val
	
	and	r1, #0x3fc0 
	mov	r2, r1, LSL #18
	orr	r2, r2, #0x100000
	orr	r2 ,r2, #0x1000	
		
	orr	r1, r2, #0x3				@Force Value locking
	str	r1, [r0, #DMC_PHYCONTROL0]
	
#if 0	/* Memory margin test 10.01.05 */
	orr	r1, r2, #0x1				@DLL off
	str	r1, [r0, #DMC_PHYCONTROL0]
#endif
	/* setting DDR2 */
	ldr	r1, =0x0FFF2010				@ConControl auto refresh off
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r1, =DMC0_MEMCONTROL			@MemControl BL=4, 1 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]
	
	ldr	r1, =DMC0_MEMCONFIG_0			@MemConfig0 256MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
	str	r1, [r0, #DMC_MEMCONFIG0]

	ldr	r1, =DMC0_MEMCONFIG_1			@MemConfig1
	str	r1, [r0, #DMC_MEMCONFIG1]

	ldr	r1, =0xFF000000				@PrechConfig
	str	r1, [r0, #DMC_PRECHCONFIG]
	
	ldr	r1, =DMC0_TIMINGA_REF			@TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4E)
	str	r1, [r0, #DMC_TIMINGAREF]
	
	ldr	r1, =DMC0_TIMING_ROW			@TimingRow	for @200MHz
	str	r1, [r0, #DMC_TIMINGROW]

	ldr	r1, =DMC0_TIMING_DATA			@TimingData	CL=3
	str	r1, [r0, #DMC_TIMINGDATA]
	
	ldr	r1, =DMC0_TIMING_PWR			@TimingPower
	str	r1, [r0, #DMC_TIMINGPOWER]

	ldr	r1, =0x07000000				@DirectCmd	chip0 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00020000				@DirectCmd	chip0 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00030000				@DirectCmd	chip0 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000542				@DirectCmd	chip0 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000442				@DirectCmd	chip0 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010780				@DirectCmd	chip0 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x07100000				@DirectCmd	chip1 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00120000				@DirectCmd	chip1 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00130000				@DirectCmd	chip1 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00110400				@DirectCmd	chip1 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00100542				@DirectCmd	chip1 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00100442				@DirectCmd	chip1 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]
	
	ldr	r1, =0x00110780				@DirectCmd	chip1 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]
		
	ldr	r1, =0x00110400				@DirectCmd	chip1 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]
		
	ldr	r1, =0x0FF02030				@ConControl	auto refresh on
	str	r1, [r0, #DMC_CONCONTROL]
		
	ldr	r1, =0xFFFF00FF				@PwrdnConfig
	str	r1, [r0, #DMC_PWRDNCONFIG]
		
	ldr	r1, =0x00202400				@MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]
	
// 上面是DRAM0初始化步骤
/*******************************************************************************************/	
// 下面是DRAM1初始化步骤,两者没有联系,是并列的。

	/* DMC1 initialization */
	ldr	r0, =APB_DMC_1_BASE
	ldr	r1, =0x00101000				@Phycontrol0 DLL parameter setting
	str	r1, [r0, #DMC_PHYCONTROL0]

	
	ldr	r1, =0x00000086				@Phycontrol1 DLL parameter setting
	str	r1, [r0, #DMC_PHYCONTROL1]
	ldr	r1, =0x00101002				@PhyControl0 DLL on
	str	r1, [r0, #DMC_PHYCONTROL0]
	ldr	r1, =0x00101003				@PhyControl0 DLL start
	str	r1, [r0, #DMC_PHYCONTROL0]
	
	
	
find_lock_val1:
	ldr	r1, [r0, #DMC_PHYSTATUS]		@Load Phystatus register value
	and	r2, r1, #0x7
	cmp	r2, #0x7				@Loop until DLL is locked
	bne	find_lock_val1
	
	and	r1, #0x3fc0 
	mov	r2, r1, LSL #18
	orr	r2, r2, #0x100000
	orr	r2, r2, #0x1000
		
	orr	r1, r2, #0x3				@Force Value locking
	str	r1, [r0, #DMC_PHYCONTROL0]
	
#if 0	/* Memory margin test 10.01.05 */
	orr	r1, r2, #0x1				@DLL off
	str	r1, [r0, #DMC_PHYCONTROL0]
#endif

	/* settinf fot DDR2 */
	ldr	r0, =APB_DMC_1_BASE

	ldr	r1, =0x0FFF2010				@auto refresh off
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r1, =DMC1_MEMCONTROL			@MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]

	ldr	r1, =DMC1_MEMCONFIG_0			@MemConfig0	512MB config, 8 banks,Mapping Method[12:15]0:linear, 1:linterleaved, 2:Mixed
	str	r1, [r0, #DMC_MEMCONFIG0]

	ldr	r1, =DMC1_MEMCONFIG_1			@MemConfig1
	str	r1, [r0, #DMC_MEMCONFIG1]

	ldr	r1, =0xFF000000
	str	r1, [r0, #DMC_PRECHCONFIG]

	ldr	r1, =DMC1_TIMINGA_REF			@TimingAref	7.8us*133MHz=1038(0x40E), 100MHz=780(0x30C), 20MHz=156(0x9C), 10MHz=78(0x4
	str	r1, [r0, #DMC_TIMINGAREF]

	ldr	r1, =DMC1_TIMING_ROW			@TimingRow	for @200MHz
	str	r1, [r0, #DMC_TIMINGROW]

	ldr	r1, =DMC1_TIMING_DATA			@TimingData	CL=3
	str	r1, [r0, #DMC_TIMINGDATA]

	ldr	r1, =DMC1_TIMING_PWR			@TimingPower
	str	r1, [r0, #DMC_TIMINGPOWER]


	ldr	r1, =0x07000000				@DirectCmd	chip0 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00020000				@DirectCmd	chip0 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00030000				@DirectCmd	chip0 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000542				@DirectCmd	chip0 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01000000				@DirectCmd	chip0 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05000000				@DirectCmd	chip0 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00000442				@DirectCmd	chip0 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010780				@DirectCmd	chip0 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00010400				@DirectCmd	chip0 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x07100000				@DirectCmd	chip1 Deselect
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00120000				@DirectCmd	chip1 EMRS2
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00130000				@DirectCmd	chip1 EMRS3
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00110440				@DirectCmd	chip1 EMRS1 (MEM DLL on, DQS# disable)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00100542				@DirectCmd	chip1 MRS (MEM DLL reset) CL=4, BL=4
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x01100000				@DirectCmd	chip1 PALL
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x05100000				@DirectCmd	chip1 REFA
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00100442				@DirectCmd	chip1 MRS (MEM DLL unreset)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00110780				@DirectCmd	chip1 EMRS1 (OCD default)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x00110400				@DirectCmd	chip1 EMRS1 (OCD exit)
	str	r1, [r0, #DMC_DIRECTCMD]

	ldr	r1, =0x0FF02030				@ConControl	auto refresh on
	str	r1, [r0, #DMC_CONCONTROL]

	ldr	r1, =0xFFFF00FF				@PwrdnConfig	
	str	r1, [r0, #DMC_PWRDNCONFIG]

	ldr	r1, =DMC1_MEMCONTROL			@MemControl	BL=4, 2 chip, DDR2 type, dynamic self refresh, force precharge, dynamic power down off
	str	r1, [r0, #DMC_MEMCONTROL]
	// 函数返回
	mov pc, lr

跳转执行

启动

210启动首先执行内部的iROM(也就是BL0),BL0会判断OMpin来决定从哪个设备启动,如果启动设备是SD卡,则BL0会从SD卡读取前16KB(不一定是16,反正16是工作的)到SRAM中去启动执行(这部分就是BL1,这就是steppingstone技术)
NorFlash读取时可以总线式访问
而SD是通过协议来访问的
那么启动的时候NorFlash启动很简单,从存储器使用device copy function来直接拷贝BL1,但是SD启动怎么办,也是一样使用device copy function,但是这个是针对SD的块设备复制函数,其中包括了初始化SD和使用SD协议和SD通信,之后复制SD中的BL1这些过程。我们来看一下block device copy function:
Global Variable
If the MMC device is used to boot up, the information of MMC card must be saved in the special area.
在这里插入图片描述
SROM中存有的block device copy function如下:
在这里插入图片描述
在这里插入图片描述
SD块设备复制函数用法:
External source clock parameter is used to fit EPLL source clock at 20MHz.

  • This Function copy MMC(MoviNAND/iNand) Card Data to memory.
  • Always use EPLL source clock.
  • This function works at 20Mhz.
  • @param u32 StartBlkAddress : Source card(MoviNAND/iNand MMC)) Address.(It must block address.)
  • @param u16 blockSize : Number of blocks to copy.
  • @param u32* memoryPtr : Buffer to copy from.
  • @param bool with_init : determined card initialization.
  • @return bool(u8) - Success or failure.

用函数指针方式调用device copy function

typedef unsigned int bool;
// 第一种方法:宏定义,好处是简单方便,坏处是编译器不能帮做参数的静态类型检查。
#define CopySDMMCtoMem(z,a,b,c,e)((( bool(*)(int, unsigned int, unsigned short, unsigned int*, bool))(*((unsigned int *)0xD0037F98)))(z,a,b,c,e))bool(*)(int, unsigned int, unsigned short, unsigned int*, bool)是函数指针的类型
*((unsigned int *)0xD0037F98)))(z,a,b,c,e)是函数指针)
// 第二种方法:用函数指针方式调用
typedef bool(*pCopySDMMC2Mem)(int, unsigned int, unsigned short, unsigned int*, bool);
// 实际使用时
pCopySDMMC2Mem p1 = (pCopySDMMC2Mem)(*(unsigned int *)0xD0037F98);// 
p1(x,x,x,x,x); 			//第一种调用方法
(*p1)(x,x,x,x,x);		//第二种调用方法

为什么需要*(unsigned int *):
1.如果地址就是函数首地址,则直接执行,像pBL2Type p2=(pBL2Type)DDR_START_ADDR;
2而上面的情况是d0037f98地址是一个指针指向真正的函数地址,而真正的函数地址我们不知道,相当于一个二重指针,所以在这里必须对他进行解引用,用*(unsigned int *)解引用

任务:实现大于16KB的bin文件使用SD卡启动

(1)总体思路:将我们的代码分为2部分:第一部分BL1小于等于16KB,第二部分为任意大小,iROM代码执行完成后从SD卡启动会自动读取BL1到SRAM中执行;BL1执行时负责初始化DDR,然后手动将BL2从SD卡copy到DDR中正确位置,然后BL1远跳转到BL2中执行BL2.
(2)细节1:程序怎么安排?程序整个分为2个文件夹BL1和BL2,各自管理各自的项目。
(3)细节2:BL1中要完成:关看门狗、设置栈、开iCache、初始化DDR、从SD卡复制BL2到DDR中特定位置,跳转执行BL2。

在这里插入图片描述
(4)细节3:BL1在SD卡中必须从Block1开始(Block0不能用,这个是三星官方规定的),长度为16KB内,我们就定为16KB(也就是32个block);BL2理论上可以从33扇区开始,但是实际上为了安全都会留一些空扇区作为隔离,譬如可以从45扇区开始,长度由自己定(实际根据自己的BL2大小来分配长度,(我们实验时BL2非常小,因此我们定义BL2长度为16KB,也就是32扇区))。(我们用的九鼎SD烧录软件其实隐含着这些处理的细节,我们还可以在Linux下烧录,用的是write2sd脚本:dd命令烧录(见dd命令详解)
使用iflag来控制输入(读取数据)时的行为特征。dsync:使用同步I/O 存取模式
#!/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
seek=1:block1,seek=45:block45)
(5)细节4:DDR初始化好之后,整个DDR都可以使用了,这时在其中选择一段长度足够BL2的DDR空间即可。我们选0x23E00000(因为uboot经常选这个地址,所以我们也选了这个地址)(因为我们BL1中只初始化了DDR1,地址空间范围是0x20000000~0x2FFFFFFF)。

代码划分为2部分(BL1和BL2)
用一个总makefile管理工程,(当make的目标为all时,-C ( K D I R ) 指 明 跳 转 到 源 码 目 录 下 读 取 那 里 的 M a k e f i l e ; M = (KDIR) 指明跳转到源码目录下读取那里的Makefile;M= (KDIR)MakefileM=(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile。-C是进入到子目录下。)
BL1中的重定位
BL2远跳转(不用加头文件了,因为是BL2,所以makefile中拿掉mkv210_image.c)
(1)因为我们BL1和BL2其实是2个独立的程序,链接时也是独立分开链接的,所以不能像以前一样使用ldr pc, =main这种方式来通过链接地址实现远跳转到BL2.
(2)我们的解决方案是使用地址进行强制跳转。因为我们知道BL2在内存地址0x23E00000处,所以直接去执行这个地址即可。
bl1和bl2完成的事情:
在这里插入图片描述
烧录用dd命令,将BL2烧到45扇区,然后放到开发板,SD卡启动,BL1将BL2下载到ddr中(细节是从SD的45扇区下载,为什么可以这么做是因为dd命令将BL2烧到了45扇区),然后BL2代码在内存的0x23e00000地址中,所以我们长跳转到此地址执行。
BL1中:void copy_bl2_2_ddr(void)
{
//跳转到DDR中执行
pBL2Type p2 = (pBL2Type)DDR_START_ADDR;
p2();
}
BL2的start.S中ldr pc, =main
烧录启动实验
在Linux中一切设备都是文件,sd卡在Linux中就是/dev/sdb,我们ls /dev/sd*就可以看到Linux的块设备,有sda(硬盘),sda1,sda2…是分区
./write2sd执行脚本。
代码分为2部分启动的缺陷
(1)代码分为2部分,这种技术叫分散加载。
(2)分散加载的缺陷:第一,代码完全分2部分,完全独立,代码编写和组织上麻烦;第二,无法让工程项目兼容SD卡启动、Nand启动、NorFlash启动等各种启动方式。
uboot中的做法
(1)第二种思路:程序代码仍然包括BL1和BL2两部分,但是组织形式上不分为2部分而是作为一个整体来组织。它的实现方式是:iROM启动然后从SD卡的扇区1开始读取16KB的BL1然后去执行BL1,BL1负责初始化DDR,然后从SD卡中读取整个程序(BL1+BL2)到DDR中,然后从DDR中执行(利用ldr pc, =main这种方式以远跳转从SRAM中运行的BL1跳转到DDR中运行的BL2)。
再来分析uboot的SD卡启动细节
(1)uboot编译好之后有200多KB,超出了16KB。uboot的组织方式就是前面16KB为BL1,剩下的部分为BL2.
(2)uboot在烧录到SD卡的时候,先截取uboot.bin的前16KB(实际脚本截取的是8KB)烧录到SD卡的block1~bolck32;然后将整个uboot烧录到SD卡的某个扇区中(譬如49扇区)
(3)实际uboot从SD卡启动时是这样的:iROM先执行,根据OMpin判断出启动设备是SD卡,然后从S卡的block1开始读取16KB(8KB)到SRAM中执行BL1,BL1执行时负责初始化DDR,并且从SD卡的49扇区开始复制整个uboot到DDR中指定位置(0x23E00000)去备用;然后BL1继续执行直到ldr pc, =main时BL1跳转到DDR上的BL2中接着执行uboot的第二阶段。
总结:uboot中的这种启动方式比上节讲的分散加载的好处在于:能够兼容各种启动方式。

10.图集合

IO与内存统一编址,可以看到SFR地址在0xE0000000开始。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值