<commit_before>---
layout:     post
title:      "嵌入式Linux-存储管理器 "
subtitle:   "ARM裸机"
date:       2017-02-22 18:00:00
author:     "陈登龙"
header-img: "img/post-bg-unix-linux.jpg"
catalog: true
tags:
    - 嵌入式Linux
---

# 嵌入式Linux-存储管理器

**一，简介**

存储管理是嵌入式中重要的模块，在SOC中有一个存储管理器，它的功能是**管理外设的内存访问**。
如下图：
![存储控制器模型][1]

如何管理呢？我们先看看访问一个芯片需要的信息：
1.地址线

2.数据线，数据宽度8，16, 32

3.时钟/频率

4.芯片相关的要求，行地址，列地址，bank信息

所以，如果我们要**访问**一个内存芯片，我们就需要**为存储管理器配置这个芯片的配置信息**。

**二，Tiny210内存初始化**

这里只是简单介绍存储管理器的功能和用法，具体的配置会在后面的DDR内存专题中说明。
这里利用Tiny210的启动原理来做一个关于内存的实验：**利用BL1阶段来初始化SDRAM内存，然后将BL2阶段的C代码加载到内存中运行，来测试内存是否初始化成功**。至于内存的初始化步骤则非常麻烦，数据手册上有27个具体的步骤，而且前提是你必须了解DDR内存的原理，所以这里就不在一一配置SDRAM。

实验代码框架：
2个目录：BL1， BL2

**BL1：**

**start.s** 汇编启动文件
``` 
.text
.global _start
_start:
	#关闭看门狗
	LDR	R0, =0xE2700000
	MOV R1, #0
	STR R1, [R0]
	
	#设置栈
	LDR SP, =0xD0037D80
	
	#初始化内存
	BL mem_init
	
	#跳转到内存中运行
	BL copy_code_to_dram

loop:
	B loop
```

**sdram.lds** 链接脚本文件
``` 
SECTIONS
{
	. = 0x0;
	
	.text : {
		start.o
		*(.text)
	}

	.data : {
		*(.data)
	}

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

[memory.S][2] 内存初始化文件,配置这个文件很复杂

[s5pv210.h][3] 	需要的头文件


**mmc_relocate.c** 重定位BL2阶段的代码
``` c
/* 在S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf 手册中可以找到定义. */
typedef int (*copy_sd_mmc_to_mem) (unsigned int  channel, unsigned int  start_block, unsigned char block_size, unsigned int  *trg, unsigned int  init);

/*
 * 将SD卡的BL2阶段的代码拷贝到内存指定位置
 */
void copy_code_to_dram(void) {
	void (*BL2)(void);
	unsigned int ret = 0;
	/* 得到使用的通道. */
	unsigned long ch = (*((volatile unsigned int*)0xD0037488));
	/* 得到官方在IRAM中已经定义好的拷贝函数. */
	copy_sd_mmc_to_mem copy_bl2 = (copy_sd_mmc_to_mem)(*(unsigned int*)(0xD0037F98));
	
	/* BL1阶段默认使用通道0 */
	if (0xEB000000 == ch) {
		/*
		* 0：通道0
		* 49：从SD的第49个扇区开始拷贝
		* 32：一共拷贝32个扇区，一个扇区512KB，一共拷贝16KB
		* 0x23E00000：拷贝到的内存地址
		* 0：不初始化SD卡
		*/
		ret = copy_bl2(0, 49, 32, (unsigned int*)0x23E00000, 0);
	} else if (0xEB200000 == ch) {
		ret = copy_bl2(2, 49, 32, (unsigned int*)0x23E00000, 0);
	} else {
		return ;
	}
	/* 初始化BL2指针 */
	BL2 = (void*)0x23E00000;
	
	/* 调用BL2阶段 */
	(*BL2)();
}
```
0xD0037488和0xD0037F98在哪里呢？
他们都在S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf 手册中：


![通道][4]


![CopyBL2][5]


Makefile 编译工具
``` makefile
sdram.bin : start.o memory.o mmc_relocate.o
	#使用编写的lds文件进行链接
	arm-linux-ld -Tsdram.lds $^ -o sdram.elf
	arm-linux-objcopy -O binary sdram.elf sdram.bin
	arm-linux-objdump -D -S sdram.elf > sdram_elf.dis
	mkv210 sdram.bin BL1.bin

%.o : %.S
	arm-linux-gcc -c $< -o $@

%.o : %.c
	arm-linux-gcc -c $< -o $@

clean:
	rm -f *.o *.elf *.dis *.bin 
```


**BL2:**

**start.S:**

``` 
.text
.global _start
_start:
	#直接跳转到main执行
	LDR pc, =main

loop:
	B loop
```

**sdram.lds:**

``` 
SECTIONS
{
	#注意此时的链接地址是内存地址
	. = 0x23E00000;
	.text : {
		start.o
		*(.text)
	}

	.data : {
		*(.data)
	}	
	
	.bss : {
		*(.bss)
	}
	
}
```

**main.c:**

``` c
/*
 * 实现4个LED灯的闪烁
 */

#define 	GPJ2CON 	(*(volatile unsigned long *) 0xE0200280)
#define 	GPJ2DAT		(*(volatile unsigned long *) 0xE0200284)

// 延时函数
void delay(unsigned long count)
{
	volatile unsigned long i = count;
	while (i--);
}

//LED 闪烁
void main()				
{
	// 配置引脚
	GPJ2CON = 0x00001111;		
	while(1)					
	{
		GPJ2DAT = 0;			// LED on
		delay(0x50000);
		GPJ2DAT = 0xf;			// LED off
		delay(0x50000);
	}
}
```

**Makefile:**

``` makefile
BL2.bin : start.o main.o
	#注意使用自己编写的lds文件
	arm-linux-ld -Tsdram.lds $^ -o BL2.elf
	arm-linux-objcopy -O binary BL2.elf BL2.bin
	arm-linux-objdump -D -S BL2.elf > BL2_elf.dis

%.o : %.S
	arm-linux-gcc -c $< -o $@

%.o : %.c
	arm-linux-gcc -c $< -o $@

clean:
	rm -f *.o *.bin *.elf *.dis
```

再编写一个**与BL1,BL2同层的Makefile**：

``` makefile
#全部编译
all:
	make -C ./BL1
	make -C ./BL2

#全部清理
clean:
	make clean -C ./BL1
	make clean -C ./BL2
```


然后进行编译：

``` 
	make
```

因为现在有两个阶段，而且我们的重定位代码是从SD卡的第49个扇区开始的，所以我们需要将BL1.bin烧写到第一个扇区，将BL2.bin烧写到第49个扇区，在插入SD卡后，执行下面的命令：

``` 
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=49
```

烧写完成后，将SD卡插入开发板，**从SD卡启动，会发现4个LED不停的闪烁**。



**三，总结**

整个实验的执行过程如下：系统上电后,首先将 sd 卡扇区 1 处的 BL1.bin 拷贝到 IRAM 的 0xD0020000 地址处,然
后运行该部分代码,该部分代码首先会初始化 DRAM,然后把位于 sd 卡中扇区 49 处的 BL2.bin，拷贝到 DRAM的0x23E00000 地址处,最后跳转到该地址处运行main。












  [1]: https://cheng-zhi.github.io/img/post-2017-02-22-MemCtl.png
  [2]: https://cheng-zhi.github.io/code/MemCtl/memory.S
  [3]: https://cheng-zhi.github.io/code/MemCtl/s5pv210.h
  [4]: https://cheng-zhi.github.io/img/post-2017-02-22-channle.png
  [5]: https://cheng-zhi.github.io/img/post-2017-02-22-CopyBL2.png
<commit_msg>Modify<commit_after>---
layout:     post
title:      "嵌入式Linux-存储管理器 "
subtitle:   "ARM裸机"
date:       2017-02-22 18:00:00
author:     "陈登龙"
header-img: "img/post-bg-unix-linux.jpg"
catalog: true
tags:
    - 嵌入式Linux
---

# 嵌入式Linux-存储管理器

**一，简介**

存储管理是嵌入式中重要的模块，在SOC中有一个存储管理器，它的功能是**管理外设的内存访问**。
如下图：
![存储控制器模型][1]

如何管理呢？我们先看看访问一个芯片需要的信息：

1.地址线

2.数据线，数据宽度8，16, 32

3.时钟/频率

4.芯片相关的要求，行地址，列地址，bank信息

所以，如果我们要**访问**一个内存芯片，我们就需要**为存储管理器配置这个芯片的配置信息**。

**二，Tiny210内存初始化**

这里只是简单介绍存储管理器的功能和用法，具体的配置会在后面的DDR内存专题中说明。
这里利用Tiny210的启动原理来做一个关于内存的实验：**利用BL1阶段来初始化SDRAM内存，然后将BL2阶段的C代码加载到内存中运行，来测试内存是否初始化成功**。至于内存的初始化步骤则非常麻烦，数据手册上有27个具体的步骤，而且前提是你必须了解DDR内存的原理，所以这里就不在一一配置SDRAM。

实验代码框架：
2个目录：BL1， BL2

**BL1：**

**start.s** 汇编启动文件
``` 
.text
.global _start
_start:
	#关闭看门狗
	LDR	R0, =0xE2700000
	MOV R1, #0
	STR R1, [R0]
	
	#设置栈
	LDR SP, =0xD0037D80
	
	#初始化内存
	BL mem_init
	
	#跳转到内存中运行
	BL copy_code_to_dram

loop:
	B loop
```

**sdram.lds** 链接脚本文件
``` 
SECTIONS
{
	. = 0x0;
	
	.text : {
		start.o
		*(.text)
	}

	.data : {
		*(.data)
	}

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

[memory.S][2] 内存初始化文件,配置这个文件很复杂

[s5pv210.h][3] 	需要的头文件


**mmc_relocate.c** 重定位BL2阶段的代码
``` c
/* 在S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf 手册中可以找到定义. */
typedef int (*copy_sd_mmc_to_mem) (unsigned int  channel, unsigned int  start_block, unsigned char block_size, unsigned int  *trg, unsigned int  init);

/*
 * 将SD卡的BL2阶段的代码拷贝到内存指定位置
 */
void copy_code_to_dram(void) {
	void (*BL2)(void);
	unsigned int ret = 0;
	/* 得到使用的通道. */
	unsigned long ch = (*((volatile unsigned int*)0xD0037488));
	/* 得到官方在IRAM中已经定义好的拷贝函数. */
	copy_sd_mmc_to_mem copy_bl2 = (copy_sd_mmc_to_mem)(*(unsigned int*)(0xD0037F98));
	
	/* BL1阶段默认使用通道0 */
	if (0xEB000000 == ch) {
		/*
		* 0：通道0
		* 49：从SD的第49个扇区开始拷贝
		* 32：一共拷贝32个扇区，一个扇区512KB，一共拷贝16KB
		* 0x23E00000：拷贝到的内存地址
		* 0：不初始化SD卡
		*/
		ret = copy_bl2(0, 49, 32, (unsigned int*)0x23E00000, 0);
	} else if (0xEB200000 == ch) {
		ret = copy_bl2(2, 49, 32, (unsigned int*)0x23E00000, 0);
	} else {
		return ;
	}
	/* 初始化BL2指针 */
	BL2 = (void*)0x23E00000;
	
	/* 调用BL2阶段 */
	(*BL2)();
}
```
0xD0037488和0xD0037F98在哪里呢？
他们都在S5PV210_iROM_ApplicationNote_Preliminary_20091126.pdf 手册中：


![通道][4]


![CopyBL2][5]


Makefile 编译工具
``` makefile
sdram.bin : start.o memory.o mmc_relocate.o
	#使用编写的lds文件进行链接
	arm-linux-ld -Tsdram.lds $^ -o sdram.elf
	arm-linux-objcopy -O binary sdram.elf sdram.bin
	arm-linux-objdump -D -S sdram.elf > sdram_elf.dis
	mkv210 sdram.bin BL1.bin

%.o : %.S
	arm-linux-gcc -c $< -o $@

%.o : %.c
	arm-linux-gcc -c $< -o $@

clean:
	rm -f *.o *.elf *.dis *.bin 
```


**BL2:**

**start.S:**

``` 
.text
.global _start
_start:
	#直接跳转到main执行
	LDR pc, =main

loop:
	B loop
```

**sdram.lds:**

``` 
SECTIONS
{
	#注意此时的链接地址是内存地址
	. = 0x23E00000;
	.text : {
		start.o
		*(.text)
	}

	.data : {
		*(.data)
	}	
	
	.bss : {
		*(.bss)
	}
	
}
```

**main.c:**

``` c
/*
 * 实现4个LED灯的闪烁
 */

#define 	GPJ2CON 	(*(volatile unsigned long *) 0xE0200280)
#define 	GPJ2DAT		(*(volatile unsigned long *) 0xE0200284)

// 延时函数
void delay(unsigned long count)
{
	volatile unsigned long i = count;
	while (i--);
}

//LED 闪烁
void main()				
{
	// 配置引脚
	GPJ2CON = 0x00001111;		
	while(1)					
	{
		GPJ2DAT = 0;			// LED on
		delay(0x50000);
		GPJ2DAT = 0xf;			// LED off
		delay(0x50000);
	}
}
```

**Makefile:**

``` makefile
BL2.bin : start.o main.o
	#注意使用自己编写的lds文件
	arm-linux-ld -Tsdram.lds $^ -o BL2.elf
	arm-linux-objcopy -O binary BL2.elf BL2.bin
	arm-linux-objdump -D -S BL2.elf > BL2_elf.dis

%.o : %.S
	arm-linux-gcc -c $< -o $@

%.o : %.c
	arm-linux-gcc -c $< -o $@

clean:
	rm -f *.o *.bin *.elf *.dis
```

再编写一个**与BL1,BL2同层的Makefile**：

``` makefile
#全部编译
all:
	make -C ./BL1
	make -C ./BL2

#全部清理
clean:
	make clean -C ./BL1
	make clean -C ./BL2
```


然后进行编译：

``` 
	make
```

因为现在有两个阶段，而且我们的重定位代码是从SD卡的第49个扇区开始的，所以我们需要将BL1.bin烧写到第一个扇区，将BL2.bin烧写到第49个扇区，在插入SD卡后，执行下面的命令：

``` 
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=49
```

烧写完成后，将SD卡插入开发板，**从SD卡启动，会发现4个LED不停的闪烁**。



**三，总结**

整个实验的执行过程如下：系统上电后,首先将 sd 卡扇区 1 处的 BL1.bin 拷贝到 IRAM 的 0xD0020000 地址处,然
后运行该部分代码,该部分代码首先会初始化 DRAM,然后把位于 sd 卡中扇区 49 处的 BL2.bin，拷贝到 DRAM的0x23E00000 地址处,最后跳转到该地址处运行main。












  [1]: https://cheng-zhi.github.io/img/post-2017-02-22-MemCtl.png
  [2]: https://cheng-zhi.github.io/code/MemCtl/memory.S
  [3]: https://cheng-zhi.github.io/code/MemCtl/s5pv210.h
  [4]: https://cheng-zhi.github.io/img/post-2017-02-22-channle.png
  [5]: https://cheng-zhi.github.io/img/post-2017-02-22-CopyBL2.png

{'fraction_non_alphanumeric': 0.17575494242885076, 'fraction_numerical': 0.07603736693460786, 'mean_word_length': 3.426442307692308, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 7, 'https://': 10, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '969384', 'n_tokens_mistral': 6047, 'n_tokens_neox': 5776, 'n_words': 766}