Application developers do not often need to create a Board Support Package (BSP). However, Original Equipment Manufacturers (OEMs) face this requirement when porting Microsoft® Windows® Embedded CE 6.0 R2 to a new hardware platform. To help OEMs accomplish this task efficiently, Windows Embedded CE features a production-quality OEM adaptation layer (PQOAL) architecture that promotes code reuse based on a collection of OAL libraries organized by processor model and OAL function. Microsoft encourages OEM developers to clone and customize an existing BSP to meet their specific requirements and take full advantage of tested and proven production features for power management, performance optimizations, and input/ output controls (IOCTL). This chapter covers the PQOAL architecture, explains how to clone BSPs, and lists the functions that OEM developers must implement in order to adapt Windows Embedded CE to new hardware architectures and models. It is advantageous to understand the various aspects of customizing a BSP even if you do not intend to develop your own. This chapter will provide an overview of the aspects of BSP customization, ranging from modifications of the startup process and implementing kernel initialization routines, to adding device drivers, power management capabilities, and support for performance optimization.
Exam objectives in this chapter:
■ Understanding the BSP architecture of Windows Embedded CE
■ Modifying and adapting BSPs and boot loaders for specific target devices
■ Understanding memory management and layout
■ Enabling power management in a BSP
To complete the lessons in this chapter, you must have the following:
■ At least some basic knowledge about Windows Embedded CE software development.
■ A thorough understanding of hardware architectures for embedded devices.
■ Basic knowledge about power management and how to implement it in drivers and applications.
■ A development computer with Microsoft Visual Studio® 2005 Service Pack 1 and Platform Builder for Windows Embedded CE 6.0 R2 installed.
The BSP development process for a new hardware platform typically begins after performing functional tests of the hardware by using a ROM monitor, by cloning an appropriate reference BSP, followed by implementing a boot loader and the core OAL functions to support the kernel. The goal is to create a bootable system with the least possible amount of custom code. You can then add device drivers to the BSP to support integrated and peripheral hardware and expand the system by implementing power management and other advanced operating system (OS) features according to the capabilities of the target device.
After this lesson, you will be able to:
■ Identify and locate the content of a PQOAL-based Board Support Package.
■ Identify hardware-specific and common-code libraries.
■ Understand how to clone a BSP.
■ Adapt a boot loader, OAL, and device drivers.
Estimated lesson time: 40 minutes.
A BSP contains all the source code for the boot loader, OAL, and device drivers for a given platform. In addition to these components, the BSP also contains build and system configuration files, as illustrated in Figure 5-1. The configuration files are not included in the actual run-time image, yet they are part of the BSP package to specify source code files, memory layout, registry settings, and other aspects to compile and build the run-time image, as explained in Chapter 2, "Building and Deploying a Run-Time Image."
Figure 5-1 Components of a BSP in relationship to the remaining elements of Windows Embedded CE 6.0
According to Figure 5-1, BSP development includes the following main components:
■ Boot loader Runs when powering up or resetting the device. The boot loader is responsible for initializing the hardware platform and passing execution to the operating system.
■ OEM adaptation layer (OAL) Represents the core of the BSP and is the interface between the kernel and the hardware. Because it is linked directly to the kernel, it becomes part of the kernel in a CE run-time image. Some of the core kernel components are directly dependent on the OAL for hardware initialization, such as the interrupt handling and timer handling for the thread scheduler.
■ Device drivers Manage the functionality of a particular peripheral and provide an interface between the device hardware and the operating system. Windows Embedded CE supports a variety of driver architectures based on the interfaces they expose, as explained in Chapter 6, "Developing Device Drivers."
■ Configuration files Provide the necessary information to control the build process and plays a key role in the design of a platform's operating system. Typical configuration files included in BSPs are Sources files, Dirs files, Config.bib, Platform.bib, Platform.reg, Platform.db, Platform.dat, and catalog files (*.pbcxml).
It is generally a good idea to jump start the BSP development process by cloning an existing reference BSP instead of creating a BSP from scratch. Even if you must develop a BSP for an entirely new platform with an entirely new CPU, it is still recommended to clone a BSP based on a similar processor architecture. In this way, you can reduce BSP development time by reusing hardware-independent code from the existing BSP and shorten future migration cycles to new Windows Embedded versions as they become available on the market. Migrating a proprietary BSP design is generally much harder to do than migrating a PQOAL-based design because the proprietary BSP cannot benefit from those PQOAL code portions that Microsoft implicitly migrates and tests as part of the new operating system version.
Adapting a board support package includes the following sequence of steps:
■ Cloning a reference BSP.
■ Implementing a boot loader.
■ Adapting the OAL functions.
4. Modifying the run-time image configuration files.
5. Developing device drivers.
Platform Builder includes a wizard that facilitates cloning a reference BSP. This wizard copies the entire source code of the selected reference BSP to a new folder structure so that you can customize the BSP for the new hardware without affecting the reference BSP or other BSPs in the %_WINCEROOT% folder hierarchy. Figure 5-2 illustrates how to start the BSP Cloning Wizard in Microsoft Visual Studio 2005 with Platform Builder for Windows Embedded CE 6.0.
Figure 5-2 Cloning a BSP in Visual Studio 2005
When cloning a BSP, you have to choose a new name for this new set of files. The name that you choose for the platform must match the name of the folder on your hard drive. As mentioned in the previous chapter, the build engine is based on a command-line script and is not compatible with spaces in folder names. Therefore, the BSP's name must not include white spaces. You can use the underscore (_) character instead.
To increase code reusability, PQOAL-based BSPs feature a common architecture and corresponding folder structure that is consistent across processor families. Due to this common architecture, large portions of the source code can be reused regardless of hardware-specific BSP requirements. Figure 5-3 shows the typical BSP folder structure and Table 5-1 summarizes the most important BSP folders.
Figure 5-3 Folder structure of a typical BSP
You can use the environment variable %_TARGETPLATROOT% in the build window to locate the path of the BSP being used in the current OS design (Open Release Directory in Build Window option on the Build menu in Visual Studio).
Table 5-1 Important BSP folders
| Folder | Description |
|---|---|
| Root Folder | Contains configuration and batch files. The two most important files for developers are as follows: |
| ■ Sources.cmn Contains macro definitions that are common across the entire BSP. | |
| ■ <BSP Name>.bat Sets the default BSP environment variables. | |
| CATALOG | Contains the BSP catalog file in which all the components of the BSP are defined. This file is used in the OS design stage to add or remove BSP features. Chapter 1, "Customizing the Operating System Design," discusses how to manage catalog items. |
| CESYSGEN | Contains the Makefile for the Sysgen tool. Configuring a BSP does requires no changes to this directory. |
| FILES | Contains the build configuration files, such as .bib, .reg, .db, and .dat files. |
| SRC | Contains the platform-specific source code that you must adapt according to the PQOAL model, which divides the code between platform-specific and common components per CPU type. |
| COMMON | Exists under the Platform directory and contains most of the BSP source code. It consists of a common set of processor specific components. The BSP links to libraries in this folder, generated during the build process. These are libraries for processor-based peripherals as well as processor-specific OAL parts. If the hardware uses a CPU from the family of supported processors, then most of these libraries can be reused without modification. |
The most important platform-specific source code that you must adapt as part of your BSP is for the boot loader, the OAL, and the device drivers. You can find the corresponding source code underneath the Src folder in the following subdirectories:
■ Src\Boot loader Contains the boot loader code. However, if the boot loader relies on BLCOMMON and related libraries, then only the basic hardware- specific part of the boot loader is located in this directory. The reusable boot loader code is available in the Public folder (%_WINCEROOT%\Public \Common\Oak\Drivers\Ethdbg) and linked as libraries to the BSP part. Chapter 4, "Debugging and Testing the System," introduces the static libraries that facilitate boot loader development.
■ Src\Oal Contains the bare minimal amount of code that is specific to the hardware platform. The majority of the OAL code is located in %_WINCEROOT%\Platform\Common, divided into hardware-independent, processor-family-related, chip-set-specific and platform-specific groups. These code groups provide most of the OAL functionality and are linked to the platform-specific parts as libraries.
■ Src\Common and Src\Drivers Contains the driver source code, organized in different categories to facilitate maintenance and portability. These categories are typically processor-specific and platform-specific. The processor-specific component is located in the Src\Common directory and requires no modifications when adapted to new hardware based on the same processor family.
Several aspects have to be considered when adapting a boot loader for a new platform, including:
■ Changes in the processor architecture.
■ Location of the boot loader code on the target device.
■ Memory architecture of the platform.
■ Tasks to perform during the boot process.
■ Supported transports for downloading the run-time image.
■ Additional features to be supported.
The first important adaptation task revolves around the definition of memory mappings for the boot loader. The standard BSPs included in Windows Embedded CE define the memory configuration in a .bib file, located in a boot loader subdirectory, such as %_WINCEROOT%\Platform\Arubaboard\Src\Boot loader\Eboot\Eboot.bib. The following listing shows an example of an Eboot.bib file, which you can customize to meet your specific requirements.
MEMORY
; Name Start Size Type
; ------- -------- -------- ----
; Reserve some RAM before Eboot.
; This memory will be used later.
DRV_GLB A0008000 00001000 RESERVED ; Driver globals; 4 KB is sufficient.
EBOOT A0030000 00020000 RAMIMAGE ; Set aside 128 KB for loader; finalize later.
RAM A0050000 00010000 RAM ; Free RAM; finalize later.
CONFIG
COMPRESSION=OFF
PROFILE=OFF
KERNELFIXUPS=ON
; These configuration options cause the .nb0 file to be created.
; An .nb0 file may be directly written to flash memory and then
; booted. Because the loader is linked to execute from RAM,
; the following configuration options
; must match the RAMIMAGE section.
ROMSTART=A0030000
ROMWIDTH=32
ROMSIZE=20000
MODULES
; Name Path Memory Type
; ----------- --------------------------------------------- -----------
nk.exe $(_TARGETPLATROOT)\target\$(_TGTCPU)\$(WINCEDEBUG)\EBOOT.exe EBOOT
Among other things you can use the Eboot.bib file to reserve a memory section for the boot loader to pass information to the operating system during the startup process. This information might reflect the current state of initialized hardware, network communication capabilities if the boot loader supports Ethernet downloads, user and system flags for the operating system, such as to enable Kernel Independent Transport Layer (KITL), and so on. To enable this communication, the boot loader and operating system must share a common region of physical memory, which is referred to as driver globals (DRV_GLB). The above Eboot.bib listing includes a DRV_GLB mapping. The data that the boot loader passes to the operating system in the DRV_GLB region must adhere to a BOOT_ARGS structure that you can define according to your specific requirements.
The following procedure illustrates how to pass Ethernet and IP configuration information from the boot loader to the operating system through a DRV_GLB region. To do this, create a header file in the %_WINCEROOT%\Platform\<BSP Name>\Src\Inc folder, such as Drv_glob.h, with the following content:
#include
// Debug Ethernet parameters.
typedef struct _ETH_HARDWARE_SETTINGS {
EDBG_ADAPTER Adapter; // The NIC to communicate with Platform Builder.
UCHAR ucEdbgAdapterType; // Type of debug Ethernet adapter.
UCHAR ucEdbgIRQ; // IRQ line to use for debug Ethernet adapter.
DWORD dwEdbgBaseAddr; // Base I/O address for debug Ethernet adapter.
DWORD dwEdbgDebugZone; // EDBG debug zones to be enabled.
// Base for creating a device name.
// This will be combined with the EDBG MAC address
// to generate a unique device name to identify
// the device to Platform Builder.
char szPlatformStri ng[EDBG_MAX_DEV_NAMELEN];
UCHAR ucCpuId; // Type of CPU.
} ETH_HARDWARE_SETTINGS, *PETH_HARDWARE_SETTINGS;
// BootArgs - Parameters passed from the boot loader to the OS.
#define BOOTARG_SIG 0x544F4F42 // "BOOT"
typedef struct BOOT_ARGS {
DWORD dwSig;
DWORD dwLen; // Total length of BootArgs struct.
UCHAR ucLoaderFlags; // Flags set by boot loader.
UCHAR ucEshellFlags; // Flags from Eshell.
DWORD dwEdbgDebugZone; // Which debug messages are enabled?
// The following addresses are only valid if LDRFL_JUMPIMG is set and
// the corresponding bit in ucEshellFlags is set (configured by Eshell, bit
// definitions in Ethdbg.h).
EDBG_ADDR EshellHostAddr; // IP/Ethernet addr and UDP port of host
// running Eshell.
EDBG_ADDR DbgHostAddr; // IP/Ethernet address and UDP port of host
// receiving debug messages.
EDBG_ADDR CeshHostAddr; // IP/Ethernet addr and UDP port of host
// running Ethernet text shell.
EDBG_ADDR KdbgHostAddr; // IP/Ethernet addr and UDP port of host
// running kernel debugger.
ETH_HARDWARE_SETTINGS Edbg; // The debug Ethernet controller.
} BOOT_ARGS, *PBOOT_ARGS;
// Definitions for flags set by the boot loader.
#define LDRFL_USE_EDBG 0x0001 // Set to attempt to use debug Ethernet.
// The following two flags are only looked at if LDRFL_USE_EDBG is set.
#define LDRFL_ADDR_VALID 0x0002 // Set if EdbgAddr member is valid.
#define LDRFL_JUMPIMG 0x0004 // If set, do not communicate with Eshell
// to get configuration information,
// use ucEshellFlags member instead.
typedef struct _DRIVER_GLOBALS {
//
// TODO: Later, fill in this area with shared information between
// drivers and the OS.
//
BOOT_ARGS bootargs;
} DRIVER_GLOBALS, *PDRIVER_GLOBALS;
The StartUp entry point of the boot loader must be located in linear memory at the address where the CPU begins fetching code for execution because this routine carries out the initialization of the hardware. If the adaptation is based on a reference BSP for the same processor chipset, then most of the CPU-related and memory controller-related code can remain unchanged. On the other hand, if the CPU architecture is different, you must adapt the startup routine to perform the following tasks:
1. Put the CPU in the right mode.
2. Disable all interrupts.
3. Initialize the memory controller.
4. Setup caches, Translation Lookaside Buffers (TLBs), and Memory Management Unit (MMU).
5. Copy the boot loader from flash memory into RAM for faster execution.
6. Jump to the C code in the main function.
The StartUp routine eventually calls the main function of the boot loader, and if the boot loader is based on BLCOMMON, then this function in turn calls BootLoaderMain, which initializes the download transport by calling OEM platform functions. The advantage of using the standard libraries provided by Microsoft is that the modifications required to adapt a BSP to a new hardware platform are componentized, isolated, and minimized.
The next step in the boot loader adaptation is the initialization of the serial debug output. This is an important part of the boot process because it enables the user to interact with the boot loader and the developer to analyze debug messages, as discussed in Chapter 4, "Debugging and Testing the System."
Table 5-2 lists the OEM platform functions required to support serial debug output in the boot loader.
Table 5-2 Serial debug output functions
| Function | Description |
|---|---|
| OEMDebugInit | Initializes the UART on the platform. |
| OEMWriteDebugString | Writes a string to the debug UART. |
| OEMWriteDebugByte | Writes a byte to the debug UART, used by OEMWriteDebugString. |
| OEMReadDebugByte | Reads a byte from the debug UART. |
Once the CPU and the debug serial output are initialized, you can turn your attention to the remaining hardware initialization tasks. The OEMPlatformInit routine performs these remaining tasks, including:
■ Initializing the real-time clock.
■ Setting up external memory, particularly flash memory.
■ Initializing the network controller.
If the hardware platform includes a network controller, then the boot loader can download the run-time image over Ethernet. Table 5-3 lists the functions that you must implement to support Ethernet-based communication.
Table 5-3 Ethernet support functions
| Function | Description |
|---|---|
| OEMReadData | Reads data from the transport for downloading. |
| OEMEthGetFrame | Reads data from the NIC using function pointer pfnEDbgGetFrame. |
| OEMEthSendFrame | Writes data to the NIC using function pointer pfnEDbfSendFrame. |
| OEMEthGetSecs | Returns number of seconds passed relative to a fixed time. |
The Ethernet support functions use callbacks into network controller-specific routines. This means that you must implement additional routines and set up appropriate function pointers in the OEMPlatformInit function if you want to support a different network controller, as demonstrated in the following sample code:
cAdaptType=pBootArgs->ucEdbgAdapterType;
// Set up EDBG driver callbacks based on
// Ethernet controller type.
switch (cAdaptType) {
case EDBG_ADAPTER_NE2000:
pfnEDbgInit = NE2000Init;
pfnEDbgInitDMABuffer = NULL;
pfnEDbgGetFrame = NE2000GetFrame;
pfnEDbgSendFrame = NE2000SendFrame;
break;
case EDBG_ADAPTER_DP83815:
pfnEDbgInit = DP83815Init;
pfnEDbgInitDMABuffer = DP83815InitDMABuffer;
pfnEDbgGetFrame = DP83815GetFrame;
...
}
Having implemented network communication capabilities, you also must enable the boot loader to download run-time image onto the new hardware platform and pass control to it. Alternately, you can save the run-time image to flash memory. Table 5-4 lists the download and flash memory support functions that you must implement for this purpose if the reference BSP's boot loader does not already support these features.
Table 5-4 Functions for supporting download and flash memory
| Function | Description |
|---|---|
| OEMPreDownload | Sets up the necessary download protocol supported by platform builder. |
| OEMIsFlashAddr | Checks if the image is for flash or RAM. |
| OEMMapMemAddr | Performs temporary remapping of the image to RAM. |
| OEMStartEraseFlash | Prepares to erase flash of enough size to fit the OS image. |
| OEMContinueEraseFlash | Continue erasing flash based on download progress. |
| OEMFinishEraseFlash | Complete the flash erasing once the download is done. |
| OEMWriteFlash | Write OS image to flash. |
Boot loaders can support user interaction based on a menu that provides the user with different options to start the platform, which can be helpful during the development process and later on for maintenance and software updates. Figure 5-4 shows a standard boot loader menu. For sample source code, check out the Menu.c file located in the Src\Bootloader\Eboot directory of a reference BSP or in the %_WINCEROOT%\Platform\Common\Src\Common\Boot\Blmenu folder.
Figure 5-4 An example of a boot loader menu
Beyond the core functionality, you can also add convenience features, such as download progress indication, support for downloading multiple .bin files during the same download session (multi-bin image notification), or downloading only trusted images. Additionally, you can implement support for downloading run-time images directly from Platform Builder. To accomplish this task, the boot loader must prepare a BOOTME packet with details about the target device and send it over the underlying transport. If the transport is Ethernet then this packet is broadcasted over the network. The libraries provided by Microsoft support these features, and you can customize them to suit your needs.
For detailed information about required and optional boot loader functions as well as boot loader structures, see the section "Boot Loader Reference" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN® Web site at http://msdn2.microsoft.com/en-us/library/aa908395.aspx.
A significant portion of the BSP adaptation revolves around the platform-specific part of the OAL. If the new platform uses a CPU that is not currently supported, then the OAL adaptation requires you to modify most of the OAL code to support the new processor architecture. On the other hand, if the new hardware is very similar to the reference BSP's platform, you might be able to reuse most of the existing code base.
The kernel performs specialized tasks, such as initializing virtual memory, and cannot rely on a boot loader for this because the kernel must be entirely self-contained. Otherwise, the operating system would depend on the presence of a boot loader and it would not be possible to bootstrap the run-time image directly. Yet, to establish virtual-to-physical address mappings through the Memory Management Unit (MMU), the kernel must know the memory layout of the underlying hardware platform. To obtain this information, the kernel uses an important table called OEMAddressTable (or g_oalAddressTable) that defines static virtual memory regions. The OAL includes a declaration of OEMAddressTable as a read-only section and one of the first actions taken by the kernel is to read this section, set up corresponding virtual memory mapping tables, and then transition to the virtual address where the kernel can execute code. The kernel can determine the physical address of the OEMAddressTable in linear memory based on the address information available in the run-time image.
You must indicate any differences in the memory configuration of a new hardware platform by modifying the OEMAddressTable. The following sample code illustrates how to declare the OEMAddressTable section.
;--------------------------------------------------------------
public _OEMAddressTable
_OEMAddressTable:
; OEMAddressTable defines the mapping between Physical and Virtual Address
; o MUST be in a READONLY Section
; o First Entry MUST be RAM, mapping from 0x80000000 -> 0x00000000
; o each entry is of the format ( VA, PA, cbSize )
; o cbSize must be multiple of 4M
; o last entry must be (0, 0, 0)
; o must have at least one non-zero entry
; RAM 0x80000000 -> 0x00000000, size 64M
dd 80000000h, 0, 04000000h
; FLASH and other memory, if any
; dd FlashVA, FlashPA, FlashSize
; Last entry, all zeros
dd 0 0 0
Similar to the boot loader, the OAL contains a StartUp entry point to which the boot loader or system can jump in order to start kernel execution and initialize the system. For example, the assembly code for putting the processor in the correct state is usually the same as the code used in the boot loader. In fact, code sharing between the boot loader and the OAL is a common practice to minimize code duplication in the BSP. Yet not all code runs twice. For example, on hardware platforms that start from a boot loader, StartUp directly jumps to the KernelStart function, as the boot loader has already performed the intialization groundwork.
The KernelStart function initializes the memory-mapping tables as discussed in the previous section and loads the kernel library to run Microsoft kernel code. The Microsoft kernel code now calls the OEMInitGlobals function to pass a pointer to a static NKGLOBALS structure to the OAL and retrieve a pointer to an OEMGLOBALS structure in the form of a return value from the OAL. NKGLOBALS contains pointers to all the functions and variables used by KITL and the Microsoft kernel code. OEMGLOBALS has pointers to all the functions and variables implemented in the OAL for the BSP. By exchanging pointers to these global structures, Oal.exe and Kernel.dll have access to each other's functions and data, and can continue with architecture-generic and platform-specific startup tasks.
The architecture-generic tasks include setting up page tables and cache information, flushing TLBs, initializing architecture-specific buses and components, setting up the interlocked API code, loading KITL to support kernel communication for debugging purposes, and initializing the kernel debug output. The kernel then proceeds by calling the OEMInit function through the function pointer in the OEMGLOBALS structure to perform platform-specific initialization.
Table 5-5 lists the platform-specific funtions that Kernel.dll calls and that you might have to modify in your BSP to run Windows Embedded CE on a new hardware platform.
Table 5-5 Kernel startup support functions
| Function | Description |
|---|---|
| OEMInitGlobals | Exchanges global pointers between Oal.exe and Kernel.dll. |
| OEMInit | Initializes the hardware interfaces for the platform. |
| OEMGetExtensionDRAM | Provides information about additional RAM, if available. |
| OEMGetRealTime | Retrieves time from RTC. |
| OEMSetAlarmTime | Sets the RTC alarm. |
| OEMSetRealTime | Set the time in the RTC. |
| OEMIdle | Puts CPU in idle state when no threads are running. |
| OEMInterruptDisable | Disables particular hardware interrupt. |
| OEMInterruptEnable | Enables particular hardware interrupt. |
| OEMInterruptDone | Signals completion of interrupt processing. |
| OEMInterruptHandler | Handles interrupts (is different for SHx processors). |
| OEMInterruptHandler | Handles FIQ (specific for ARM processors). |
| OEMIoControl | IO control code for OEM information. |
| OEMNMI | Supports a non maskable interrupt (specific to SHx processor). |
| OEMNMIHandler | Handler for non maskable interrupt (specific to SHx processor). |
| OEMPowerOff | Puts CPU in suspend state and takes care of final power down operations. |
The OEMInit function is the main OAL routine that initializes board-specific peripherals, sets up the kernel variables, and starts KITL by passing a KITL IOCTL to the kernel. If you added and enabled KITL in the run-time image, the kernel starts KITL for debugging over different transport layers, as discussed in Chapter 4, "Debugging and Testing the System."
Table 5-6 lists the functions that the OAL must include to enable KITL support on a new platform.
Table 5-6 KITL support functions
| Function | Description |
|---|---|
| OEMKitlInit | Initializes KITL. |
| OEMKitlGetSecs | Returns the current time in seconds. |
| TransportDecode | Decodes recevied frames. |
| TransportEnableInt | Enables or disables KITL interrupt if it is interrupt based. |
| TransportEncode | Encodes data accroding the transport's required frame structure. |
| TransportGetDevCfg | Retrieves the device's KITL transport configuration. |
| TransportReceive | Receives a frame from the transport. |
| TransportSend | Sends a frame using the transport. |
| KitlInit | Initializes KITL system. |
| KitlSendRawData | Sends raw data using the transport bypassing the protocol. |
| KitlSetTimerCallback | Registers a callback that is called after a specified amount of time. |
| KitlStopTimerCallback | Disables a timer used by the above routine. |
Located at the core of the operating system, the OAL is a perfect choice for mechanisms to measure the performance of the system and support performance optimization. As discussed in Chapter 3, "Performing System Programming," you can use the Interrupt Latency Timing (ILTiming) tool to measure the time it takes to invoke an interrupt service routine (ISR) after an interrupt occurred (ISR latency) and the time between when the ISR exits and the interrupt service thread (IST) actually starts (IST latency). However, this tool requires a system hardware tick timer or alternative high-resolution timer that is not available on all hardware platforms. If the new hardware platform supports a high-resolution hardware timer, you can support ILTiming and similar tools by implementing the functions listed in Table 5-7.
Table 5-7 Profile timer support functions
| Function | Description |
|---|---|
| OEMProfileTimerEnable | Enables a profiler timer. |
| OEMProfileTimerDisable | Disables a profiler timer. |
The OAL must also support interrupt handling and the kernel scheduler. The scheduler is independent of the processor type, yet interrupt handling must be optimized for different types of processors.
Apart from the core system functions, the BSP also contains device drivers for peripherals. These peripheral devices can be components on the processor chip or external components. Even when separate from the processor, they remain an integral part of the hardware platform.
Table 5-8 lists the source code locations for device drivers according to the PQOAL model. If your BSP is based on the same processor as the reference BSP, then the adaptation of device drivers mainly requires modification to the source code in the %TGTPLATROOT% folder. It is also possible to add new drivers to the BSP if the new platform includes peripherals that are not present in the reference platform. For more information about developing device drivers, see Chapter 6, "Developing Device Drivers."
Table 5-8 Source code folders for device drivers
| Folder | Description |
|---|---|
| %_WINCEROOT%\Platform\%_TGTPLAT% | Contains platform dependent drivers. |
| %_WINCEROOT%\Platform\Common\Src\Soc | Contains drivers for processor-native peripherals. |
| %_WINCEROOT%\Public\Common\Oak\Drivers | Contains drivers for non-native peripherals that include external controllers. |
If you cloned your BSP from an existing BSP, all configuration files are already in place. However, it is important that you review the memory layout in the Config.bib file, as explained in detail in Lesson 2. The other configuration files require modifications only if you added new drivers or modified components in the BSP, as explained in Chapter 2, "Building and Deploying a Run-Time Image."
It is advantageous to start the BSP development process by cloning an appropriate reference BSP. Ideally, this BSP should be based on the same or similar hardware platform because this makes the most of tested and proven production features. Windows Embedded CE features a PQOAL architecture and Platform Builder tools that facilitate the cloning process. The goal is to create a bootable system with minimum customizations and then add additional features and support for peripheral devices as necessary.
The first component of a BSP that you might have to adapt is the boot loader, which is responsible for initializing the hardware platform and passing execution to the kernel. The second component is the OAL, which contains the platform-specific code that the kernel needs for hardware initialization, interrupt handling, and timer handling for the thread scheduler, KITL, and kernel debug output. The third part of the BSP you must adapt is the device drivers for peripheral devices. The fourth part of the BSP requiring adaptation is the configuration files that control the build process, determine the memory layout, and specify system configuration settings. If the BSP adaption is based on a reference BSP for the same processor architecture, then most of the CPU-related and memory controller-related BSP code can remain unchanged. You only need to address platform-specific code portions that focus on bringing up the hardware, rather than creating the necessary setup for the BSP.
Memory management in Windows Embedded CE has changed significantly from previous versions. In past versions, all processes shared the same 4 GB address space. With CE 6.0, each process has its own unique address space. The new system of managing virtual memory enables CE 6.0 to run up to 32,000 processes in contrast to the previous 32 processes limitation. This lesson covers the details of the new memory architecture and management, so that you can map virtual memory regions to correct physical memory addresses on the platform.
After this lesson, you will be able to:
■ Describe how Windows Embedded CE manages virtual memory.
■ Configure static memory mappings for a hardware platform.
■ Map noncontiguous physical memory to virtual memory on the system.
■ Share resources between OAL and device drivers.
Estimated lesson time: 15 minutes.
Windows Embedded CE uses a paged virtual memory management system with a 32-bit virtual address space, mapped to physical memory by using the MMU. With 32 bits, the system can address a total of 4 GB of virtual memory, which CE 6.0 divides into the following two areas (see Figure 5-5):
■ Kernel space Located in the upper 2 GB of virtual memory and shared between all application processes running on the target device.
■ User space Located in the lower 2 GB of virtual memory and used exclusively by each individual process. Each process has its own unique address space. The kernel manages this mapping of the process address space when a process switch occurs. Processes cannot access the kernel address space directly.
Figure 5-5 Virtual memory space in Windows Embedded CE 6.0
Windows Embedded CE 6.0 divides the kernel address space further into several regions for specific purposes, as illustrated in Figure 5-6. The lower two regions of 512 MB each statically map physical memory into cached and non-cached virtual memory. The middle two regions for kernel execute in place (XIP) DLLs and Object Store are important for the OS design. The remaining space is for kernel modules and CPU-specific purposes.
Figure 5-6 Kernel space in Windows Embedded CE 6.0
Table 5-9 summarizes the kernel virtual memory regions with start and end addresses.
Table 5-9 Kernel memory regions
| Start Address | End Address | Description |
|---|---|---|
| 0xF0000000 | 0xFFFFFFFF | Used for CPU specific system trap and kernel data pages. |
| 0xE0000000 | 0xEFFFFFFF | Kernel virtual machine, it is CPU dependent, for example this space is not available for SHx. |
| 0xD0000000 | 0xDFFFFFFF | Used for all kernel mode modules of the OS. |
| 0xC8000000 | 0xCFFFFFFF | Object Store used for RAM file system, database and registry. |
| 0xC0000000 | 0xC7FFFFFF | XIP DLLs. |
| 0xA0000000 | 0xBFFFFFFF | Non-cached mapping of physical memory. |
| 0x80000000 | 0x9FFFFFFF | Cached mapping of physical memory. |
The process address space ranges from 0x00000000 through 0x7FFFFFFF, and is divided into a CPU-dependent kernel data section, four main process regions, and a 1 MB buffer between user and kernel spaces. Figure 5-7 illustrates the main regions. The first process region of 1 GB is for application code and data. The next process region of 512 MB is for the DLLs and read-only data. The next two regions of 256 MB and 255 MB are for memory-mapped objects and the shared system heap. The shared system heap is read-only for the application process, but readable and writable for the kernel.
Figure 5-7 Process space in Windows Embedded CE 6.0
Table 5-10 summarizes the virtual memory regions in user space with start and end addresses.
Table 5-10 Process memory regions
| Start Address | End Address | Description |
|---|---|---|
| 0x7FF00000 | 0x7FFFFFFF | An unmapped buffer for protection between user and kernel spaces. |
| 0x70000000 | 0x7FEFFFFF | Shared heap between the kernel and processes. |
| 0x60000000 | 0x6FFFFFFF | Memory-mapped file objects that do not correspond to an actual physical file, mainly for backward compatibility with applications that used RAM-backed map files for inter-process communication. |
| 0x40000000 | 0x5FFFFFFF | DLLs loaded into the process and read-only data. |
| 0x00010000 | 0x3FFFFFFF | Application code and data. |
| 0x00000000 | 0x00010000 | CPU-dependent user kernel data (read-only for user processes). |
Windows Embedded CE 6.0 requires the processor to provide a memory mapping mechanism to associate physical memory with virtual memory, up to a maximum of 512 MB of mapped physical memory. Figure 5-8 shows an example with 32 MB of flash memory and 64 MB of RAM mapped into the cached and non-cached static mapping regions of the kernel. On ARM-based and x86-based platforms, the memory mapping relies on a user-defined OEMAddressTable, whereas on the SHx-based and MIPS-based platforms, the mapping is directly defined by the CPU. The Memory Management Unit (MMU) is responsible for managing the physical-to-virtual address mappings.
Figure 5-8 Physical-to-virtual memory mapping example
The kernel initializes the MMU and creates the necessary page tables during system startup. This processor-specific part of the kernel depends on the architecture of the hardware platform. For implementation details, refer to the Windows Embedded CE private code, located in subdirectories per processor type under %_PRIVATEROOT%\Winceos\Coreos\Kernel.
The virtual memory regions depicted in Figure 5-8 are statically mapped virtual addresses to emphasize the fact that they are defined at startup time and the mapping does not change. Statically mapped virtual addresses are always available and directly accessible in kernel mode. It is noteworthy, however, that Windows Embedded CE also supports static mapping at runtime by means of CreateStaticMapping and NKCreateStaticMapping APIs. These functions return a non-cached virtual address mapped to the specified physical address.
The kernel can also manage the mapping of physical-to-virtual addresses dynamically, which is the technique used for all non-static mappings. A driver or DLL loaded into the kernel through LoadKernelLibrary can reserve a region of virtual memory in the kernel address space by calling VirtualAlloc and then map the virtual address to a physical address by creating a new page-table entry through a call to VirtualCopy. This is a common technique to map virtual addresses to the registers or frame buffers of peripheral devices in order to perform input/output operations. If the mapped buffer is no longer needed, the device driver or DLL can call VirtualFree to remove page-table entry and free the allocated virtual memory.
You must customize two elements to include information about static memory mappings in a BSP:
■ Config.bib file Provides information about how the system should use the different memory regions on the platform. For example, you can specify how much memory is available for the OS, how much can be used as free RAM and also reserve memory for specific needs.
■ OEMAddressTable Provides information about the memory layout of the underlying platform, as discussed in Lesson 1. The memory specified in Config.bib should also be mapped in the OEMAddressTable.
As mentioned in Chapter 2, "Building and Deploying a Run-Time Image," you must define a single contiguous region in the RAMIMAGE memory region for the operating system in the MEMORY section of the Config.bib file. The system uses this definition to load the kernel image and any modules you specified in the MODULES and FILES sections. You cannot define multiple RAMIMAGE regions, yet the OAL can extend the RAMIMAGE region and provide additional noncontiguous memory sections at runtime.
Table 5-11 summarizes important variables and functions to extend the RAM region.
Table 5-11 Variables and functions to extend the RAM region
| Variable/Function | Description |
|---|---|
| MainMemoryEndAddress | This variable indicates the end of the RAM region. The kernel sets this variable initially according to the size reserved for the operating system in the Config.bib file. The OAL OEMInit function can update this variable if additional contiguous memory is available. |
| OEMGetExtensionDRAM | The OAL can use this function to report the presence of an additional region of noncontiguous memory to the kernel. OEMGetExtensionDRAM returns the start address and length of the second region of memory. |
| pNKEnumExtensionDRAM | The OAL can use this function pointer to report the presence of more than one additional region of memory to the kernel. This mechanism supports up to 15 different noncontiguous memory regions. If you implement the pNKEnumExtensionDRAM function pointer, then OEMGetExtensionDRAM is not called during the startup process |
Device drivers often need access to physical resources, such as memory-mapped registers or DMA buffers, yet drivers cannot directly access physical memory because the system only works with virtual addresses. For device drivers to gain access to physical memory, the physical addresses must be mapped to virtual addresses.
If a driver requires physically contiguous memory, as in the case of buffers required for DMA operations, the driver can allocate contiguous physical memory by using the AllocPhysMem function. If the allocation is successful, AllocPhysMem returns a pointer to the virtual address that corresponds to the specified physical address. Because the system allocates memory, it is important to free the memory later on when it is no longer needed by calling FreePhysMem.
On the other hand, if a driver requires non-paged access to a physical memory region defined in Config.bib, you can use the MmMapIoSpace function. MmMapIoSpace returns a non-paged virtual address that is directly mapped to the specified physical address. This function is typically used to access device registers.
Occasionally, it may be necessary to share a common region of physical memory between drivers or between a driver and the OAL (such as between an IST and an ISR). Similar to sharing a memory region for boot arguments between boot loader and kernel, you can reserve a shared memory region for driver communication purposes in the Config.bib file. A standard practice is to use the DRIVER_GLOBALS structure defined in Drv_glob.h, as mentioned in Lesson 1.
In addition to the standard set of IOCTLs required by the kernel, drivers can communicate with the OAL through custom IOCTLs implemented in OEMIoControl. Kernel-mode drivers call OEMIoControl indirectly through KernelIoControl, passing in the custom IOCTL. The kernel does no processing, other than passing the parameters straight through to OEMIoControl. However, user-mode drivers cannot directly call custom OAL IOCTLs by default. The KernelIOControl calls from user-mode drivers or processes are passed to OEMIoControl through a kernel-mode component (Oalioctl.dll), which maintains a list of user-accessible OAL IOCTL codes. The call is rejected if the requested IOCTL code is not listed in this module, but you can customize this list by modifying the Oalioctl.cpp file that is located in the %_WINCEROOT%\Public\Common\Oak\Oalioctl folder.
A good understanding of the Windows Embedded CE 6.0 memory architecture is a must for every CE developer. Specifically for BSP developers, it is important to know how CE 6.0 maps available physical memory into the virtual memory address space. Accessing memory from OAL, kernel-mode modules, and user-mode drivers and applications requires a detailed understanding of static and dynamic mapping techniques that are available in kernel mode or user mode. For more information about the communication between kernel-mode and user-mode components, refer to Chapter 6, "Developing Device Drivers."
As discussed in Chapter 3, "Performing System Programming," Windows Embedded CE 6.0 provides a comprehensive set of power management features based on a Power Manager component that OEM developers can customize to implement system power state definitions as appropriate for their hardware platforms. In relationship to the OAL, implementing power management capabilities is a twofold task. You need to enable the operating system to control the power state of the hardware components and you need to enable the hardware platform to inform the operating system about power state changes. Most embedded devices require at least basic power management support to reduce power consumption and prolong battery life.
After this lesson, you will be able to:
■ Describe how to reduce processor power consumption.
■ Identify the transition paths to suspend and resume the system.
Estimated lesson time: 15 minutes.
Embedded devices that are not constantly in use, such as personal digital assistants (PDAs), operate for extended periods of time in an idle state, thus providing an opportunity to preserve energy by switching from full-power mode to a reduced-power mode or suspend state. Most embedded processors available on the market today support these transitions, as illustrated in Figure 5-9.
Figure 5-9 Power state transitions
Windows Embedded CE can respond to power-related events in the following ways:
■ Battery critically low The system switches into Critical Off state in response to a nonmaskable interrupt (NMI) that a voltage comparator on the board triggers, so that the user can replace the battery and resume.
■ Idle The system switches the CPU into reduced-power mode if the CPU has no worker threads to run and wakes up when an interrupt occurs.
■ Suspend The system switches the device into Suspend state when the user presses the Off button or in response to an inactivity timeout and resumes in response to a wakeup event, such as the user pressing the power button again. On some embedded devices, the Suspend state corresponds to a true power-off state, in which case the system resumes with a cold boot.
To switch the device into reduced-power mode, Windows Embedded CE relies on the OEMIdle function, which the kernel calls when the scheduler has no threads to run. The OEMIdle function is a hardware-specific routine that depends on the capabilities of the platform. For example, if the system timer uses a fixed interval, then the OEMIdle function cannot really provide the expected power saving functionality because the system wakes up every time a timer interrupt occurs. On the other hand, if the processor supports programmable interval timers, you can use the kernel's dwReschedTime variable to specify the amount of time spent in reduced-power mode.
On waking up from reduced-power mode, the system must update the kernel global variables used by the scheduler. This is particularly important for the CurMSec variable, which the system uses to keep track of the number of milliseconds since the last system boot. The wakeup source can be either the system timer or another interrupt. If the wakeup source is the system timer then the CurMSec variable is already updated before execution is passed back to the OEMIdle function. In other cases, the CurMSec does not contain an updated value. To learn more about the OEMIdle implementation details, refer to the Idle.c source code file, located in the %_WINCEROOT%\Platform\Common\Src\Common\Timer\Idle folder.
For detailed information about global variables that the kernel exports for scheduling, see the section "Kernel Global Variables for Scheduling" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn.microsoft.com/en-us/library/aa915099.aspx.
The maximum power saving state that a Windows Embedded CE device can support is the Power Off or Suspend state. The system can request the device to enter the Suspend state by calling GwesPowerOffSystem directly or SetSystemPowerState. Both functions eventually call the OEMPowerOff routine.
The OEMPowerOff routine is part of the OAL and responsible for switching the CPU into Suspend state. OEMPowerOff should also put the RAM into self-refresh mode if the processor does not automatically do so when it enters the Suspend state. You can also set up the interrupts to wake up the device. In handheld devices, this is typically the power-button interrupt, but you may use any wakeup event source that is appropriate for your target platform.
When entering the Suspend state, Windows Embedded CE performs the following sequence of steps:
1. GWES notifies the Taskbar about the power down event.
2. The system aborts calibration if in the calibration screen.
3. The system stops the Windows message queues. After step 3, the system enters single-thread mode, which prevents function calls that rely on blocking operations.
4. The system checks if the startup user interface (UI) must appear on resume.
5. The system saves video memory to RAM.
6. The system calls SetSystemPowerState (NULL, POWER_STATE_SUSPEND, POWER_FORCE).
7. Power Manager:
a. Calls the FileSystemPowerFunction to power off the drivers related to the file system.
b. Calls PowerOffSystem to inform the kernel to do the final power down.
c. Calls Sleep(0) to invoke the scheduler.
If the OS design does not include Power Manager or GWES, then the OEM must explicitly call FileSystemPowerFunction and PowerOffSystem.
8. Kernel:
a. Unloads GWES process.
b. Unloads Filesys.exe.
c. Calls OEMPowerOff.
9. OEMPowerOff configures the interrupts and puts the CPU in Suspend state.
When a pre-configured interrupt wakes up the system, the associated ISR runs and returns to the OEMPowerOff routine. On returning from this function, the system goes through the resume sequence, which includes the following steps:
1. OEMPowerOff re-configures interrupts to original state and returns.
2. Kernel:
a. Calls InitClock to re-initialize the system timer.
b. Starts Filesys.exe with power on notification.
c. Starts GWES with power on notification.
d. Re-initializes KITL interrupt if it was in use.
3. Power Manager calls FileSystemPowerFunction with power on notification.
4. GWES:
a. Restores video memory from RAM.
b. Powers on Windows Manager.
c. Sets the display contrast.
d. Shows startup UI if required.
e. Notifies Taskbar of resume.
f. Notifies User Subsystem.
g. Triggers applications as required.
If the OAL supports the kernel IOCTL_HAL_ENABLE_WAKE, applications can register wake up sources. For detailed information, see the section "IOCTL_HAL_ENABLE_WAKE" in the Windows Embedded CE 6.0 Documentation, available on the Microsoft MSDN Web site at http://msdn2.microsoft.com/en-us/library/aa914884.aspx.
On hardware platforms equipped with a voltage comparator that triggers NMI, you can implement support for the Critical Off state to protect the user from data loss in low-battery conditions. On x86 hardware, the kernel exports the OEMNMIHandler function to capture critical events in the system. On other systems, you might have to implement a custom IST that calls SetSystemPowerState to turn off the system gracefully with the help of Power Manager. The Critical Off state typically corresponds to the Suspend state with dynamic RAM refresh enabled.
When implementing Critical Off state support, make sure you trigger the NMI at a point when the system still has time to perform all power down tasks , such as powering down peripherals, putting RAM into self-refresh, perhaps setting a wakeup condition, and suspending the CPU.
Power management is an important Windows Embedded CE feature that ensures efficient power consumption on target devices. OEMs should implement power management features in the OAL to enable transitions from full-power mode to Idle and Suspend modes and Critical Off state for battery-powered devices. Implementing power management support involves re-synchronizing timer-related kernel variables, powering down peripherals, putting RAM into self-refresh mode, setting wakeup conditions, and suspending the CPU. It is not trivial to implement these low-level routines, yet Microsoft provides sufficient reference code in the sample BSPs to get a better understanding of the implementation details.
In this lab you clone a reference BSP in Visual Studio 2005 with Platform Builder and use it to build a run-time image. As the underlying platform, this lab uses the Device Emulator because this platform can run on the Windows Embedded CE development computer. Microsoft included the Device Emulator BSP in Platform Builder as a reference BSP.
To help you successfully master the procedures presented in this Lab, see the document "Detailed Step-by-Step Instructions for Lab 5" in the companion material for this book.
1. In Visual Studio 2005, open the Tools menu, click Platform Builder For CE 6.0, and then click Clone BSP.
2. In the Clone Board Support Package window select Device Emulator: ARMV4I as the Source BSP from the drop-down list.
3. Under New BSP Information enter the information shown in Table 5-12 (see also Figure 5-10):
Table 5-12 New BSP details
| Parameter | Value |
|---|---|
| Name | DeviceEmulatorClone |
| Description | Clone of the Device Emulator BSP |
| Platform Directory | DeviceEmulatorClone |
| Vendor | Contoso Ltd. |
| Version | 0.0 |
4. Select Open New BSP Catalog File In Catalog Editor check box and then click Clone.
5. Verify that Platform Builder clones the Device Emulator BSP successfully, and then in the corresponding Clone BSP dialog box, click OK.
6. Verify that Visual Studio automatically opens the DeviceEmulatorClone.pbcxml catalog file. Close the catalog editor without making any changes.
Figure 5-10 BSP cloning information
1. In order to validate our cloned BSP, create a new OS design based on the DeviceEmulatorClone BSP. Call the OS design DeviceEmulatorCloneTest, as illustrated in Figure 5-11 (see also Lab 1 in Chapter 1 for details on how to accomplish this step).
2. Choose Industrial Device in the Design Templates and Industrial Controller in the Design Template Variants. Accept the default options in the subsequent steps of the wizard.
3. After Platform Builder generates the DeviceEmulatorCloneTest project, verify the OS design by examining the catalog items in Catalog Items View.
4. Verify that the Debug build configuration is enabled by opening Configuration Manager on the Build menu and seeing if the Active Solution Configuration list box displays DeviceEmulatorClone ARMV4I Debug.
5. On the Build menu, click Build Solution.
6. After the build is completed, configure the Connectivity Options to use the Device Emulator.
7. Open the Target menu and click Attach Device to download the run-time image to the Device Emulator and start Windows Embedded CE. Notice the debug messages in the Output window of Visual Studio 2005. Wait until the device has started up completely.
Figure 5-11 A new OS design based on the DeviceEmulatorClone BSP
Device Emulator emulates the same hardware platform for both the reference BSP and the cloned BSP. For this reason, the new run-time image runs on Device Emulator without further adaption. In practice, however, the underlying hardware is different in most cases, requiring BSP adaptations to start CE successfully.
1. Detach from the target device and close Device Emulator.
2. In Visual Studio, open the init.c source code file that you can find in the %_PLATFORMROOT%\DeviceEmulatorClone\Src\Oal\Oallib folder, as illustrated in Figure 5-12.
3. Search for the OAL function OEMGetExtensionDRAM and add the following line of code to print a debug message in the Output window of Visual Studio during system startup.
BOOL OEMGetExtensionDRAM(LPDWORD lpMemStart, LPDWORD lpMemLen) {
...
OALMSG(OAL_FUNC, L"++OEMGetExtensionDRAM\r\n"));
// Test message to confirm that our modifications are part of run-time image.
OALMSG(1,(TEXT("This modification is part of the run-time image.\r\n")));
...
}
4. Rebuild the run-time image to includes the changes, and then attach to the device again in order to download and start the new run-time image in Device Emulator. Verify that Windows Embedded CE prints the debug message in the Output window.
Figure 5-12 DeviceEmulatorClone BSP customization
The adaptation of a BSP is one of the most complicated and critical development tasks that OEMs face when porting Windows Embedded CE 6.0 to a new hardware platform. To facilitate this undertaking, Microsoft provides reference BSPs with Platform Builder and encourages OEMs to start the development process by cloning the most suitable BSP. The PQOAL-based BSPs follow a well-organized folder and file structure to separate platform-agnostic and platform-specific code by processor type and OAL function so that OEMs can focus on platform-specific implementation details without getting side tracked by general aspects of the kernel or operating system.
OEM developers should consider the following recommendations to ensure a successful adaptation of a BSP:
■ Study the Windows Embedded CE reference BSPs Windows Embedded CE BSPs follow a well-defined architecture with close relationships to the kernel. This makes it necessary to implement numerous APIs that the kernel requires to run the operating system. Knowing these APIs and their purpose is very important. The PQOAL-based architecture is continually evolving.
■ Clone a BSP Avoid writing a new BSP completely from scratch. Instead, clone a BSP to jump start the adaptation process. By reusing as much code as possible from a reference BSP, you not only shorten development time, but also increase the quality of your solution and provide a solid foundation for efficient handling of future upgrades.
■ Boot loader and BLCOMMON Use BLCOMMON and related libraries when implementing a boot loader because these libraries provide useful hardware-independent features for downloading run-time images and enabling users to interact with the target device during the startup process.
■ Memory and BSPs Make sure you thoroughly understand how Windows Embedded CE 6.0 deals with physical and virtual memory. Configure <Boot loader>.bib and Config.bib files to provide accurate information about available memory to the operating system and adjust the entries in the OEMAddressTable, if necessary. Keep in mind that you cannot directly access physical memory in Windows Embedded CE. Use the correct memory-mapping APIs to map physical memory addresses to virtual memory addresses.
■ Implement power management Implement the OEMIdle function to enable the system to switch the CPU into Idle mode. Consider implementing OEMPowerOff as well, if your platform supports power state transitions into Suspend mode in response to user actions or critical battery levels.
Do you know what these key terms mean? You can check your answers by looking up the terms in the glossary at the end of the book.
■ PQOAL
■ Boot loader
■ KernelIoControl
■ Driver globals
To help you successfully master the exam objectives presented in this chapter, complete the following tasks.
Implement a device driver for peripheral hardware and access the hardware registers by using the MmMapIoSpace API to interact with the device. Note that it is not possible to call MmMapIoSpace from an application.
Because Device Emulator emulates an ARM processor in software, you cannot access hardware devices. You must use a genuine hardware platform to perform this suggested practice.
By modifying the Config.bib file of the cloned Device Emulator BSB, you can increasingly reduce the available RAM on the system and study the impact in terms of available memory on the system by using the memory information APIs or Platform Builder tools.