引自:http://blog.csdn.net/gooogleman/archive/2008/08/06/2776564.aspx
一般在ARM架构的CPU上,物理地址都是统一编址的,寻址空间为4GB(32Bit CPU)。也就是说,针对一个ARM的处理器,它可以访问的物理空间是4GB。在WinCE中,ARM中的4GB物理地址空间将被映射为512MB的虚拟内存空间。 OEMAddressTable就是一个4GB物理地址空间到WinCE Kernel中的512MB虚拟地址空间的映射表。
在BSP中,会定义OEMAddressTable来描述系统中可访问的物理空间及对应的虚拟地址空间,还有大小。这个表会在WinCE系统开始启动的时候传给MMU,具体到BSP中应该是在OAL中的startup.s中,OEMAddressTable的起始地址会被放到r0寄存器中,然后就跳转到KernelStart里面,KernelStart会用OEMAddressTable完成MMU得初始化。当WinCE启动以后,就只能访问虚拟地址空间了。
举个例子,比如我们要开发一个Flash的驱动程序,那么首先我们知道这个flash所接的片选对应的物理起始地址是多少(假如是0x60000000),大小是多少(假如是0x2000000)。如果我们要在WinCE中访问它,就必须为它定义一个虚拟地址(假如是0x80000000),并添加到OEMAddressTable中,这样,我们才能在我们的驱动里面通过这个虚拟地址访问到flash。
虚拟地址不是随便定义的,WinCE中有规定,必须在0x80000000---0x9FFFFFFF。实际上WinCE创建了两套虚拟地址空间,一个是0x80000000---0x9FFFFFFF,是Cache Enabled。另一个是0xA0000000---0xBFFFFFFF,是Cache Disabled。有啥区别呢:
如果我们访问的这个空间只是一段内存空间(比如SDRAM),那么就可以用Cache Enabled的空间来访问,这样存取数据的速度会比较快,因为数据被保存在Cache中。
如果我们访问的这个空间是一个外设的地址,那么我们就要使用Cached Disabled的空间来访问,这样才能使CPU与外设同步。
可能说得有点绕,我的经验就是:只要是SDRAM,可以用Cache Enabled空间访问。如果是寄存器,就用Cache Disabled空间访问。
如何定义OEMAddressTable呢,如果安装了WinCE5.0或者6.0,那么提供的参考BSP中都已经有定义了,在BSP目录下搜索“OEMAddressTable”,一看代码就明白了,这里重复一下,格式如下:
虚拟地址 物理地址 大小
比如:
OEMAddressTable:
dd 0x80000000 0x60000000 0x2000000
dd 0 0 0
上面这个表定义了一个flash的物理地址到虚拟地址的映射,物理地址是0x60000000,虚拟地址是0x80000000,大小是32MB。OEMAddressTable最后必须以0结尾,表示OEMAddressTable结束。
总之,说白了就是一张物理地址/虚拟地址映射表,当我们要在WinCE中要访问相关硬件的时候,查查这张表,然后通过虚拟地址就可以访问了。如果没有定义,自己添加一个物理地址到虚拟地址的映射就好了
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gooogleman/archive/2008/08/06/2776564.aspx
; This module contains the OEM memory map for the S3c2410
;
;--
;
; OEMAddressTable defines the mapping from the 4GB physical address space
; to the kernel's 512MB "un-mapped" spaces. The kernel will create two ranges
; of virtual addresses from this table. One from 0x80000000 to 0x9FFFFFFF which
; has caching & buffering enabled and one from 0xA0000000 to 0xBFFFFFFF which
; has the cache & buffering disabled.
;
; Each entry in the table consists of the Virtual Base Address to map to,
; the Physical Base Address to map from, and the number of megabytes to map.
;
; The order of the entries is arbitrary, but DRAM should be placed first for
; optimal performance. The table is zero-terminated, so the last entry MUST
; be all zeroes.
;
; Mapped for S3C2400X01
EXPORT OEMAddressTable[DATA]
OEMAddressTable
;;;-------------------------------------------------------------
;;; Virt Addr Phys Addr MB
;;;-------------------------------------------------------------
DCD 0x80000000, 0x02000000, 30 ; 30 MB SROM(SRAM/ROM) BANK 0
DCD 0x82000000, 0x08000000, 32 ; 32 MB SROM(SRAM/ROM) BANK 1
DCD 0x84000000, 0x10000000, 32 ; 32 MB SROM(SRAM/ROM) BANK 2
DCD 0x86000000, 0x18000000, 32 ; 32 MB SROM(SRAM/ROM) BANK 3
DCD 0x88000000, 0x20000000, 32 ; 32 MB SROM(SRAM/ROM) BANK 4
DCD 0x8A000000, 0x28000000, 32 ; 32 MB SROM(SRAM/ROM) BANK 5
DCD 0x8C000000, 0x30000000, 64 ; 64 MB DRAM BANK 0,1
DCD 0x90800000, 0x48000000, 1 ; Memory control register
DCD 0x90900000, 0x49000000, 1 ; USB Host register
DCD 0x90A00000, 0x4A000000, 1 ; Interrupt Control register
DCD 0x90B00000, 0x4B000000, 1 ; DMA control register
DCD 0x90C00000, 0x4C000000, 1 ; Clock & Power register
DCD 0x90D00000, 0x4D000000, 1 ; LCD control register
DCD 0x90E00000, 0x4E000000, 1 ; NAND flash control register
DCD 0x91000000, 0x50000000, 1 ; UART control register
DCD 0x91100000, 0x51000000, 1 ; PWM timer register
DCD 0x91200000, 0x52000000, 1 ; USB device register
DCD 0x91300000, 0x53000000, 1 ; Watchdog Timer register
DCD 0x91400000, 0x54000000, 1 ; IIC control register
DCD 0x91500000, 0x55000000, 1 ; IIS control register
DCD 0x91600000, 0x56000000, 1 ; I/O Port register
DCD 0x91700000, 0x57000000, 1 ; RTC control register
DCD 0x91800000, 0x58000000, 1 ; A/D convert register
DCD 0x91900000, 0x59000000, 1 ; SPI register
DCD 0x91A00000, 0x5A000000, 1 ; SD Interface register
DCD 0x92000000, 0x00000000, 2 ; 2 MB SROM(SRAM/ROM) BANK 0
DCD 0x00000000, 0x00000000, 0 ; End of Table (MB MUST BE ZERO!)
END
Config.bib
MEMORY // 定义可用的物理内存,包括起始地址大小和内存类型.
NK 80001000 01E00000 RAMIMAGE
RAM 8C200000 01E00000 RAM
FLASH 92000000 00100000 RESERVED
; Common RAM areas
AUD_DMA 8c002000 00000800 RESERVED
SDIO_DMA 8c010000 00010000 RESERVED
ARGS 8C020800 00000800 RESERVED
DBGSER_DMA 8c022000 00002000 RESERVED
SER_DMA 8c024000 00002000 RESERVED
IR_DMA 8c026000 00002000 RESERVED
EDBG 8c030000 00020000 RESERVED
DISPLAY 8c100000 00100000 RESERVED
Boot.bib
MEMORY
; Name Start Size Type
; ------- -------- -------- ----
ARGS 8c020800 00000800 RESERVED
RAM 8c026000 00006000 RAM
STACK 8c02c000 00004000 RESERVED
EBOOT 8c038000 00040000 RAMIMAGE
; Area used to cache nk.bin while programming flash
FLSCACHE 8c200000 01400000 RESERVED
DISPLAY 8c100000 00100000 RESERVED
OEMAddressTable里定义的映射关系是给ARM MMU用的,是在KernelStart(source code参考wince420private目录)时建立的,只要WINCE还在跑,就不会解除. OEMAddressTable里的Virtual Addr和Physical Addr是对ARM来说的. 其实对于WINCE,就只能访问到它的Virtual address. 也就是说,OEMAddressTable里的Virtual address对WINCE 系统来说才是Physical Address. 经过OEMAddressTable映射后的系统的物理地址,在0x80000000~0x9fffffff之间.是caching and buffering的地址,这个地址加上0x20000000,就是它的cache & buffering disabled地址.所有的硬件寄存器的地址都在这个地址段上,受MMU保护的.
上面讲的系统的物理地址,从0x80000000~0xbfffffff,在Kernel Mode下都可以直接访问. ISR是在KERNEL里,也就可以直接访问这些系统的物理地址.无所谓"因为ISR只能访问静态映射的虚拟地址".
上面说过,对于ARM来说,有虚拟地址和物理地址之分,对于WINCE来说,也有虚拟地址和物理地址之分. 可以这么说,ARM的虚拟地址就是WINCE系统的物理地址.
32位的OS总共有4G的虚拟地址空间,WINCE也不例外. 其中,0x00000000~0x80000000是Application Space; 0x80000000~0xffffffff是System Reserved. 系统的物理地址就在System Reserved的这段,只能在KERNEL MODE访问. 那么,当APPLICATION和DRIVER(都是运行在USER MODE)要访问这些在System Reserved地址段的硬件寄存器或MEMORY怎么办呢? 只好再建立一层映射关系,在Application Space里分配一段空间,把它映射到System Reserved里的地址上,这就是VirtualAlloc/Copy和MmMapIoSpace干的事情.(居然这讲的是错的 )
如果你的地址是这样声明的:
#define RTC_COUNTER *((volatile unsigned *)0x91000000)
那么直接读写就可以了,比如:
int nRtc = RTC_COUNTER;
RTC_COUNTER = nRtc;
否则,可以用:
int nRtc = READ_REGISTER_ULONG(0x91000000);
WRITE_REGISTER_ULONG(0X91000000, nRtc);
其实这两种方式的本质是一样的,都是把地址声明成某个数据类型,然后就可以直接读写了.下面是READ_REGISTER_ULONG()和WRITE_REGISTER_ULONG()的定义:
#define READ_REGISTER_ULONG(reg) (*(volatile unsigned long * const)(reg))
#define WRITE_REGISTER_ULONG(reg, val) (*(volatile unsigned long * const)(reg)) = (val)
嵌入式设备与桌面PC的一个显著不同是它的应用程序中通常需要直接访问某一段物理内存,这在驱动程序中对物理内存的访问尤为重要,尤其是像ARM体系结构下,I/O端口也被映射成某一个物理内存地址。因此,与桌面版本Windows相比,Windows CE提供了相对简单的物理内存访问方式。无论是驱动程序还是应用程序都可以通过API访问某一段物理内存。
Windows CE的有些函数中需要用到物理内存结构体PHYSICAL_ADDRESS, Windows CE在ceddk.h中定义了PHYSICAL_ADDRESS,它其实是LARGE_INTEGER类型,其定义如下:
// in ceddk.h
typedef LARGE_INTEGER PHYSICAL_ADDRESS, *PPHYSICAL_ADDRESS;
// in winnt.h
typedef union _LARGE_INTEGER{
struct{
DWORD LowPart;
LONG HighPart;
};
LONGLONG QuadPart;
} LARGE_INTEGER;
可见,Windows CE中用64个Bit来代表物理地址,对于大多数32位的CPU而言,只需要把它的HighPart设置为0就可以了。
如果要直接访问某一个地址的物理内存,Windows CE提供了VirtualAlloc()和VirtualCopy()函数,VirtualAlloc负责在虚拟内存空间内保留一段虚拟内存,而VirtualCopy负责把一段物理内存和虚拟内存绑定,这样,最终对物理内存的访问还是通过虚拟地址进行。它们的声明如下:
// 申请虚拟内存
LPVOID VirtualAlloc(
LPVOID lpAddress, // 希望的虚拟内存起始地址
DWORD dwSize, // 以字节为单位的大小
DWORD flAllocationType, // 申请类型,分为Reserve和Commit
DWORD flProtect // 访问权限
);
// 把物理内存绑定到虚拟地址空间
BOOL VirtualCopy(
LPVOID lpvDest, // 虚拟内存的目标地址
LPVOID lpvSrc, // 物理内存地址
DWORD cbSize, // 要绑定的大小
DWORD fdwProtect // 访问权限
);
VirtualAlloc对虚拟内存的申请分为两步,保留MEM_RESERVE和提交MEM_COMMIT。其中MEM_RESERVE只是在进程的虚拟地址空间内保留一段,并不分配实际的物理内存,因此保留的虚拟内存并不能被应用程序直接使用。MEM_COMMIT阶段才真正的为虚拟内存分配物理内存。
下面的代码显示了如何使用VirtualAlloc和VirtualCopy来访问物理内存。因为VirtualCopy负责把一段物理内存和虚拟内存绑定,所以VirtualAlloc的时候只需要对内存保留,没有必要提交。
FpDriverGlobals =
(PDRIVER_GLOBALS) VirtualAlloc(
0,
DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
MEM_RESERVE,
PAGE_NOACCESS);
if (FpDriverGlobals == NULL) {
ERRORMSG(DRIVER_ERROR_MSG, (TEXT(" VirtualAlloc failed!/r/n")));
return;
}
else {
if (!VirtualCopy(
(PVOID)FpDriverGlobals,
(PVOID)(DRIVER_GLOBALS_PHYSICAL_MEMORY_START),
DRIVER_GLOBALS_PHYSICAL_MEMORY_SIZE,
(PAGE_READWRITE | PAGE_NOCACHE))) {
ERRORMSG(DRIVER_ERROR_MSG, (TEXT("VirtualCopy failed!/r/n")));
return;
}
}
CEDDK还提供了函数MmMapIoSpace用来把一段物理内存直接映射到虚拟内存。用MmMapIoSpace申请的内存要用MmUnmapIoSpace释放,此函数的原形如下:
PVOID MmMapIoSpace(
PHYSICAL_ADDRESS PhysicalAddress, // 起始物理地址
ULONG NumberOfBytes, // 要映射的字节数
BOOLEAN CacheEnable // 是否缓存
);
VOID MmUnmapIoSpace(
PVOID BaseAddress, // MmMapIoSpace返回的起始虚拟地址
ULONG NumberOfBytes //
);
其实,MmMapIoSpace函数内部也是调用VirtualAlloc和VirtualCopy函数来实现物理地址到虚拟地址的映射的。MmMapIoSpace函数的原代码是公开的,我们可以从%_WINCEROOT%/PUBLIC/COMMON/OAK/DRIVERS/CEDDK/DDK_MAP/ddk_map.c得到。从MmMapIoSpace的实现我们也可以看出VirtualAlloc和VirtualCopy的用法:
PVOID MmMapIoSpace (
IN PHYSICAL_ADDRESS PhysicalAddress,
IN ULONG NumberOfBytes,
IN BOOLEAN CacheEnable
)
{
PVOID pVirtualAddress; ULONGLONG SourcePhys;
ULONG SourceSize; BOOL bSuccess;
SourcePhys = PhysicalAddress.QuadPart & ~(PAGE_SIZE - 1);
SourceSize = NumberOfBytes + (PhysicalAddress.LowPart & (PAGE_SIZE - 1));
pVirtualAddress = VirtualAlloc(0, SourceSize, MEM_RESERVE, PAGE_NOACCESS);
if (pVirtualAddress != NULL)
{
bSuccess = VirtualCopy(
pVirtualAddress, (PVOID)(SourcePhys >> 8), SourceSize,
PAGE_PHYSICAL | PAGE_READWRITE | (CacheEnable ? 0 : PAGE_NOCACHE));
if (bSuccess) {
(ULONG)pVirtualAddress += PhysicalAddress.LowPart & (PAGE_SIZE - 1);
}
else {
VirtualFree(pVirtualAddress, 0, MEM_RELEASE);
pVirtualAddress = NULL;
}
}
return pVirtualAddress;
}
此外,Windows CE还供了AllocPhysMem函数和FreePhysMem函数,用来申请和释放一段连续的物理内存。函数可以保证申请的物理内存是连续的,如果函数成功,会返回虚拟内存的句柄和物理内存的起始地址。这对于DMA设备尤为有用。在这里就不详细介绍了,读者可以参考Windows CE的联机文档。