引自: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的聯機文檔。