IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> 静态映射和动态映射 -> 正文阅读

[嵌入式]静态映射和动态映射

1、为什么需要映射

在内核启动过程中会开启MMU,建立虚拟映射表,以后内核使用的都是虚拟地址。但是我们查询数据手册得到I/O寄存器地址都是物理地址,于是需要将物理地址转换到虚拟地址,这样才能在内核空间去访问I/O寄存器。物理地址转换到虚拟地址就叫做映射,映射分为静态映射和动态映射。

2、动态映射和静态映射的比较

(1)建立映射的时机:静态映射是在内核启动过程中就建立好,并且在整个内核的生命周期都存在;动态映射是在需要的时候才进行,使用完后要解除映射;
(2)占用资源:静态映射是在内核启动就建立,所以会一直占用地址资源;动态映射只在映射的时候才占用地址资源,解除映射后就不再占用地址资源;
(3)访问速度:静态映射因为是一开始就映射好的,所以访问速度快;动态映射使用的时候要向申请再映射,所以访问速度稍慢一点;
(4)一般会把常访问的I/O地址设置成静态映射,因为访问速度更快;但是不能把太多物理地址设置成静态映射,因为静态映射会一直占用资源;

3、静态映射

3.1、struct machine_desc结构体

每个开发板都对应一个struct machine_desc结构体,因为嵌入式设备都是高度定制的,结构体里是针对该开发板的描述。其中成员变量map_io是个函数指针,每个开发板的静态映射就是该函数指针对应的函数完成的。struct machine_desc结构体的详细介绍见博客:《内核启动过程中机器码的确定》

3.2、静态映射的建立过程

	start_kernel
		setup_arch
			paging_init
				devicemaps_init
					if (mdesc->map_io)	// Ask the machine support to map in the statically mapped devices.s
						mdesc->map_io();

在devicemaps_init函数里去调用struct machine_desc结构体的map_io函数指针,也就是每个开发板特定的静态映射建立函数。从上面的代码还可以知道,静态映射不是必须的,在添加开发板对应的struct machine_desc结构体时,可以将map_io函数指针赋值为NULL,表示此开发板不需要静态映射。

3.3、struct map_desc结构体

	{
		unsigned long virtual;	 /* 映射后的虚拟地址 */
		unsigned long pfn;		 /* I/O资源物理地址所在的页帧号 */
		unsigned long length;	 /* I/O资源长度 */
		unsigned int type;		 /* I/O资源类型 */
	};

struct map_desc结构体是用来描述如何建立静态映射的,需要指明物理地址、映射后的虚拟地址、要映射的长度、映射的资源类型;其中物理地址传的是页帧号,因为内核建立的虚拟映射是以叶为单位的。

3.4、以X210开发板的map_io函数为例

#define S3C_ADDR_BASE	(0xFD000000)

#define S3C_ADDR(x)	(S3C_ADDR_BASE + (x))

#define S5P_VA_CHIPID		S3C_ADDR(0x00700000)
#define S5P_VA_GPIO			S3C_ADDR(0x00500000)

static struct map_desc s5p_iodesc[] __initdata = {
	{
		.virtual	= (unsigned long)S5P_VA_CHIPID,
		.pfn		= __phys_to_pfn(S5P_PA_CHIPID),
		.length		= SZ_4K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S3C_VA_SYS,
		.pfn		= __phys_to_pfn(S5P_PA_SYSCON),
		.length		= SZ_64K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S3C_VA_UART,
		.pfn		= __phys_to_pfn(S3C_PA_UART),
		.length		= SZ_4K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)VA_VIC0,
		.pfn		= __phys_to_pfn(S5P_PA_VIC0),
		.length		= SZ_16K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)VA_VIC1,
		.pfn		= __phys_to_pfn(S5P_PA_VIC1),
		.length		= SZ_16K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S3C_VA_TIMER,
		.pfn		= __phys_to_pfn(S5P_PA_TIMER),
		.length		= SZ_16K,
		.type		= MT_DEVICE,
	}, {
		.virtual	= (unsigned long)S5P_VA_GPIO, //开发板的GPIO映射后的虚拟地址的基地址
		.pfn		= __phys_to_pfn(S5P_PA_GPIO), //开发板GPIO的物理地址基地址
		.length		= SZ_4K,	//要映射的长度					
		.type		= MT_DEVICE,	//资源类型
	},
};

void __init iotable_init(struct map_desc *io_desc, int nr)
{
	int i;

	for (i = 0; i < nr; i++)
		create_mapping(io_desc + i);
}

void __init s5p_init_io(struct map_desc *mach_desc,
			int size, void __iomem *cpuid_addr)
{
	unsigned long idcode;

	/* initialize the io descriptors we need for initialization */
	iotable_init(s5p_iodesc, ARRAY_SIZE(s5p_iodesc));
	if (mach_desc)
		iotable_init(mach_desc, size);

	idcode = __raw_readl(cpuid_addr);
	s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids));
}

static void __init smdkv210_map_io(void)
{
	s5p_init_io(NULL, 0, S5P_VA_CHIPID);
	s3c24xx_init_clocks(24000000);
	s3c24xx_init_uarts(smdkv210_uartcfgs, ARRAY_SIZE(smdkv210_uartcfgs));
}

3.4.1、函数调用顺序:

	smdkv210_map_io
		s5p_init_io
			iotable_init
				create_mapping

3.4.2、函数调用说明

(1)X210开发板的map_io函数是smdkv210_map_io,这是在构建struct machine_desc结构体时指定的,见博客:《内核启动过程中机器码的确定》
(2)s5p_iodesc是struct map_desc类型的数组,每个struct map_desc类型的元素代表要进行静态映射的虚拟地址、物理地址已经映射的长度;
(3)create_mapping函数是真正构建静态映射的函数,将对s5p_iodesc数组里的每个元素进行解析,构建静态映射;

3.4.3、如何添加静态映射

 {
		.virtual	= (unsigned long)S5P_VA_GPIO, //开发板的GPIO映射后的虚拟地址的基地址
		.pfn		= __phys_to_pfn(S5P_PA_GPIO), //开发板GPIO的物理地址基地址
		.length		= SZ_4K,	//要映射的长度					
		.type		= MT_DEVICE,	//资源类型
	},

(1)在s5p_iodesc结构体数组中多添加一个struct map_desc 结构体成员,里面说明要映射的物理地址和虚拟地址;
(2)以s5p_iodesc结构体数组的最后一个成员为例,就是将物理地址S5P_PA_GPIO(0xE0200000)映射到虚拟地址S5P_VA_GPIO(0xFD500000)。物理地址就是从数据手册里查到的地址,映射后就可以在操作系统中用虚拟地址去访问那些物理地址。

4、动态映射

4.1、涉及的函数

//参数:物理地址、地址长度、名字
//返回值:成功、失败
#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0)
//参数:要映射的物理地址、地址长度
//返回值:映射得到的虚拟地址
#define ioremap(cookie,size)           __ioremap(cookie,size,0) 
//参数:之前映射得到的虚拟地址
#define iounmap(cookie)			__iounmap(cookie)
//参数:之前申请的物理地址、地址长度
#define release_mem_region(start,n)	__release_region(&iomem_resource, (start), (n))

4.2、函数的调用次序

(1)request_mem_region:向内核申请物理地址资源;
(2)ioremap:将物理地址映射成虚拟地址;
(3)iounmap:解除物理地址到虚拟地址的映射;
(4)release_mem_region:通知内核释放物理地址;

4.3、示例代码

#define GPJ0CON_PA	0xe0200240
#define GPJ0DAT_PA 	0xe0200244

unsigned int *pGPJ0CON;
unsigned int *pGPJ0DAT;

// 申请物理地址
if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))
	return -EINVAL;
if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))
	return -EINVAL;
	
//进行虚拟地址映射
pGPJ0CON = ioremap(GPJ0CON_PA, 4);
pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);

// 解除映射
iounmap(pGPJ0CON);
iounmap(pGPJ0DAT);
release_mem_region(GPJ0CON_PA, 4);
release_mem_region(GPJ0DAT_PA, 4);
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:41:35  更:2022-03-30 18:43:58 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:23:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码