bulat-icon  Articles  

[+]  Articles


Windows Embedded Compact 2013 - THUMB2 Porting Considerations

by Vinoth.R


As you know Windows Embedded Compact 2013 (WEC2013) is supporting only ARMv7 Thumb2 mode and earlier versions of ARM BSPs (ARMv7) are forced to support Thumb2 mode while porting. Although WEC2013 Development team has released an ARM porting guide, this article reveal some implicit information, real world issues and solutions required to make THUMB2 porting success. Let's step in to this one by one.

Jumping between THUMB2 and ARM

When the ARM core is powered on, it is running in Native mode (ARM Mode). But from the STARTUP entry of the assembly code onwards the opcode is generated for THUMB2 mode. So Processor must be switched to THUMB2 mode before executing the first instruction of the STARTUP code.

To switch to THUMB2 mode, T Bit of CPSR register has to be set and J bit should be cleared(by default J bit is cleared). This cannot be done directly by using the MSR instruction. If you do this, the processor enters an unpredictable state. But this can be done easily with the BLX or BX (branch) instruction with the LSB of the target address is set to 1. Since each instruction is either 16 or 32 bit in size, the LSB of the target address is always ignored during instruction fetching and it is used for setting or clearing the T bit of CPSR. Clearing the T bit will switch the core to ARM Mode. It is required for few cases and it is discussed later in this article.

Understanding ROMIMAGE changes on WEC2013

The boot loader and the OS binary is built using ROMIMAGE. There are few changes on ROMIMAGE to generate binaries for WEC2013.

Generating .nb0 file

While generating an .Nb0 file, ROMIMAGE will generate a jump page (The first 4K page with jump instruction followed by zeros filled for remaining page, in between contains the ROM SIGNATURE, TOC pointer and offset) in the very beginning of the .nb0 file. ROMIMAGE for WEC2013 generates the jump instruction which is an ARM encoding of BLX instruction which jumps to the StartUp (Entry point of the assembly file) in THUMB2 mode. Since from the STARTUP entry of the assembly code onwards the assembler generating THUMB2 code. Figure 1) shows the ARM encoding of BLX with the target address 0x000100D1 of the example.
Jump instruction (ARM encoding of BLX)

Figure 1) Jump instruction (ARM encoding of BLX)

Generating .bin file

As you know .bin file is a Record based file. Each record has start address, length and checksum and the last record length is the Starting address of the code. This starting address is the address of the StartUp entry point. ROMIMAGE sets the LSB of this starting address to 1 in order to make sure the branching should switch to THUMB2 mode in WEC2013. You can see the NK.bin records using Viewbin tool.

Viewbin -d nk.bin > nk.txt
Last record of the NK.bin

Figure 2) Last record of the NK.bin
Here, LSB of the start address is set but the actual start address is 0x880100D0. See the below figure for the start address 0x000100D0 in NK.nb0 file. Address 0x00000000 will be relocated to 0x88000000 while loading in this example.
Start address ( Address of the StartUp code)

Figure 3) Start address ( Address of the StartUp code)
See the Dumpbin tool output of oal.exe to find the StartUp code for matching the instruction with the address shown in the NK.nb0 file.

Dumpbin /DISASM oal.exe > oal.txt
Dumpbin tool output of oal.exe Entry point (StartUp)

Figure 4) Dumpbin tool output of oal.exe Entry point (StartUp)

Switching from THUMB2 to ARM

Commonly we are using separate binary for bootloader and OS. Even the bootloader is split in two or more parts for certain circumstances. Things to remember is, whenever we are launching the .nb0 files, the starting address contains a jump instruction which is an ARM instruction. So you have to switch to ARM mode by calling
     JumpTo (Launchaddress & ~ 1);     //Calling JumpTo function to Switch from THUMB2 mode to ARM mode.
Eventhough the LSB is 0 for start address this is just to make sure for a safe Jump. For Example, jumping from primary bootloader (XLDR, IPL etc..) to EBOOT.nb0

Switching from ARM to THUMB2

Suppose if you are directly jumping to StartUp Entry by skipping the jump page in some cases, you have to set the LSB of the jump address as shown below,
     JumpTo ( Launchaddress | 1 ) ;     //Calling JumpTo function to Switch from ARM mode to THUMB2 mode.
Simply passing the Start address is enough when parsing and launching from .bin file. As stated above the ROMIMAGE sets the LSB to 1 for the start address (address of the StartUP Entry point) when creating the .bin file.
A typical JumpTo function implementation for WEC2013 is shown below
JumpTo() function
Figure 5) JumpTo() function
Avoid using "mov pc, r0" kind of jump implementation which won't work.

Exiting from Suspend State


Nowadays processors supports extensive power management which allows the MCU (core) to off state. Whenever core enter in to the on state(by wakeup source) during resume, the MCU by default in ARM mode, so the next expected instruction for the core should be an ARM instruction, but WEC2013 assemblers generating opcode only in THUMB2 mode. So we need an opcode that should be an ARM opcode which should switch the processor to THUMB2 for further execution. Again BLX is the suitable instruction to switch from ARM mode to THUMB2. Here is the ARM encoding of BLX instruction which jump to pc+8. So we need a no-op instruction just to fill another 4 byte.

DCD     0xfa000000
After this the core switched to THUMB2 mode and here after the core executes the THUMB2 instructions and continue the resume sequence.

Beware of linker


A strange issue is happening when using the global variable declared and defined in assembly file and import it and used in C file. Linker set the LSB bit of the global variable location declared in assembly file when the variable is used in C function call. Which cause a wrong access and end with fatal.

A typical example is, "g_oalAddressTable" a global variable for address table declared in assembler .inc file. This is declared in RODATAAREA to indicate that it is data not a code and assembler won't set the LSB to 1.
Address Table
Figure 6) Address Table
But when the same variable is import in C file, the linker doesn't aware about the variable and while linking, it set the LSB of the address to 1. This address table is used for implementing OALPAtoVA and OALVAtoPA in eboot. Address of the g_oalAddressTable is saved as a DCD directive at the end of OALPAtoVA function. Here is the .cod file entry for the g_oalAddressTable used in the OALPAtoVA.
.COD file for OALPAtoVA()
Figure 7) .COD file for OALPAtoVA()
This is the first DCD entry at the end of the function. Actual address is found when we disassemble the eboot.exe using Dumpbin tool. Below figure shows the actual location of the g_oalAddressTable and the address stored in DCD entry of the OALPAtoVA function.
Starting Location of the g_oalAddressTable
Figure 8) Starting Location of the g_oalAddressTable
g_oalAddressTable location (00012DC9) stored in DCD of OALPAtoVA
Figure 9) g_oalAddressTable location (00012DC9) stored in DCD of OALPAtoVA
You can see the location of g_oalAddressTable stored in the DCD entry of OALPAtoVA with LSB set to 1. Which indicate that the address stored in the DCD has point the CODE area but it is actually pointing to DATA area. But the linker unware of this and set the LSB to 1. This is happening only for a global data variable is declared in assembly and link with C files. If you see the next DCD entry, which stores the address 00001A38 with LSB set to 0. This is the correct one for pointing the DATA.

To fix this issue:

Clear the LSB of the address table while assigning to the pointer variable as shown below in the OALPAtoVA and OALVAtoPA function which allocate the proper memory address.
Clearing LSB
Figure 10) Clearing LSB

Considerations for Code Relocation


There are some cases where the code needed to be executed in different location than it is originally allocated. Typical example is, relocating the some power management assembly code from DDR to SRAM in order to allow processor to lower power state. Normally a set of assembly function written sequentially to make the relocation easier by simply copying the code from Starting function address to End function address as shown in the below example. This code copies the code from OALCPUStart to OALCPUEnd from DDR to SRAM which has many function in between.

Sample Relocation code
Figure 11) Sample Relocation code
The above memcpy won't provide the expected results. Because the LSB is set to 1 for the address of the function call by the by the linker since it is in code section. Instead of coping from the actual start address of OALCPUStart, it starts copying from the next byte of this function since the LSB is set to 1. This will end with fatal while executing the code.

To fix this issue:

Clear the LSB of the function call in the memcpy() argument as shown below.
Fix for relocation
Figure 12) Fix for relocation
While relocating the code, it is copied to aligned address as shown in the figure 13.
relocated address of fnCpuStart
Figure 13) relocated address of fnCpuStart
Make sure that the LSB bit is set to 1 for the address of the function pointer as shown below otherwise branching (calling the function) cause the jump to ARM mode but the code is compiled to THUMB2 mode.
populate fnoalcpuidel function


Hope this article shades some more lights on WEC2013 THUMB2 porting. Please continue to read few more condsiderations on THUMB2 Porting specially on error "pre UAL syntax not allowed" and also take your time to read about the change in STARTUPTEXT macro and its effect on WEC2013.