SoC
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
  • Arm Cortex M3
  • Contact
Reading: Ensuring Thumb Mode Stays Enabled in Cortex-M3
SUBSCRIBE
SoCSoC
Font ResizerAa
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Search
  • Home
  • Arm
  • Arm Cortex M0/M0+
  • Arm Cortex M4
Have an existing account? Sign In
Follow US
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
© S-O-C.ORG, All Rights Reserved.
Arm

Ensuring Thumb Mode Stays Enabled in Cortex-M3

Holly Lindsey
Last updated: October 5, 2023 9:55 am
Holly Lindsey 12 Min Read
Share
SHARE

When programming for the Cortex-M3 processor, it is important to keep the processor in Thumb mode rather than ARM mode. Thumb mode uses 16-bit Thumb instructions which take up less memory space than 32-bit ARM instructions. Staying in Thumb mode ensures your code remains compact and efficient. Here are some techniques to keep the Cortex-M3 locked in Thumb mode.

Contents
Set the CPSR T-Bit on ResetUse BLX Instructions for Function CallsAvoid BX Instructions Outside VeneersUse Assembler Directives to Lock SectionsConfigure Linker Script Memory Regions as ThumbValidate T-Bit Settings During DebuggingUse Inline Assembly to Set T-BitConfigure LOC Counter for Thumb ModeValidate Thumb-2 Support in ToolsReserve Some ARM Space in Memory MapUse UAL Syntax in Assembly CodeOrder Code Sections from ARM to ThumbUse Assembly Wrappers Around ARM FunctionsDebug Faulty Veneers Causing Mode SwitchingLimit Inline Assembly Code SectionsUse Compiler Optimization PragmasReserve unused PC Bits to Validate Thumb ModeSummary

Set the CPSR T-Bit on Reset

The simplest way to keep the Cortex-M3 in Thumb mode is to set the T-bit in the CPSR (Current Program Status Register) during processor reset. The T-bit controls whether the processor is in Thumb (T=1) or ARM (T=0) state. By default, the T-bit resets to 0 on power on for Cortex-M3. To change this, update your reset handler code to set the T-bit to 1 early in the reset process. For example: void Reset_Handler() { // Set T-bit to enable Thumb mode __set_CONTROL(0x00000001); // Rest of reset code }

Setting the T-bit on reset locks the processor into Thumb state from the start, preventing accidental switching to ARM mode later on. This is the recommended approach for Cortex-M3 programming to maximize code density.

Use BLX Instructions for Function Calls

The BL and BLX instructions are used for function calls in ARM code. BL performs a branch with link and always stays in the same instruction set state. BLX performs a branch with link, and can switch modes. When calling between Thumb and ARM code, you must use BLX rather than BL to change processor states automatically. Using BLX for all function calls ensures the T-bit remains set properly throughout a program. // Calling a Thumb function from ARM code BLX ThumbFunc // Calling an ARM function from Thumb code BLX ARMFunc

This avoids accidentally dropping into ARM mode by using BL, which can happen if libraries contain mixed ARM and Thumb code. Getting into ARM mode can be disastrous for code size, so strictly using BLX is good practice.

Avoid BX Instructions Outside Veneers

The BX instruction can explicitly change the instruction set state from ARM to Thumb or vice versa. Avoid using BX outside of interworking veneers, as this can unintentionally switch modes. Interworking veneers handle transitions between states properly, whereas arbitrary BX instructions can corrupt the T-bit. // In Thumb code BX ARMFunc ; DON’T DO THIS! // Use veneers instead BLX ARMFuncStub ; Thumb to ARM veneer

Veneers contain a small stub that performs a proper BLX when switching instruction sets. This protects the T-bit throughout the rest of the code. Avoiding extraneous BX instructions ensures Thumb mode remains unchanged.

Use Assembler Directives to Lock Sections

Most assemblers and compilers provide directives to lock sections into Thumb or ARM state. For Cortex-M3, add .thumb or .thumb_func to force functions into Thumb mode. For example: .thumb .thumb_func void MyThumbFunc(void) { // Thumb instructions }

This prevents a function from accidentally ending up in ARM state if the linker mixes code sections. Apply .thumb directives to source files and key functions to robustly stay in Thumb state.

Configure Linker Script Memory Regions as Thumb

Linker scripts define how code and data sections are placed into memory. To keep regions Thumb only, add a “.thumb” prefix to the region name. For example: .thumb.text : { *(.thumb.text*) }

This forces all code linked into the “text” region to contain only Thumb instructions. Having Thumb-only memory regions prevents ARM code from being mixed in.

Validate T-Bit Settings During Debugging

Debuggers allow you to view and manipulate processor registers like the CPSR during execution. When halted, check that the T-bit is set to confirm Thumb mode is active. The debugger can also set breakpoints if the T-bit gets improperly cleared to catch errors early. Watching the CPSR ensures the processor stays in Thumb state as expected. (gdb) print $cpsr $1 = 0x1000000 (T-bit is set)

Proper use of BLX, BX avoidance, assembler directives, linker regions, and debugger validation combines to create robust, Thumb mode only applications on Cortex-M3.

Use Inline Assembly to Set T-Bit

For C code, inline assembly can explicitly set the T-bit when necessary. Insert asm code to change the CPSR control register and lock in Thumb mode. asm volatile ( “mov r0, #0x1” “\n” “msr CONTROL, r0” “\n” );

This is useful if transitioning from higher level ARM code down to Thumb functions. The inline asm ensures Thumb mode is set properly before hitting any Thumb instructions.

Configure LOC Counter for Thumb Mode

The LOC (Length of Code) counter tracks code size during JTAG debug. Make sure LOC counts bytes as Thumb (2-byte) instructions rather than 4-byte ARM instructions. For example, in GDB: monitor LOC 2

This sets the multiplier to 2 for Thumb mode. Confirming LOC counts properly ensures BREAK and other debug commands work as intended in Thumb state.

Validate Thumb-2 Support in Tools

Thumb-2 extended Thumb with some 16- and 32-bit instructions on Cortex-M3. Make sure your toolchain – assembler, compiler, linker, debugger – fully supports Thumb-2 extensions. Using legacy Thumb-only tools can result in incorrect code generation and T-bit handling. Check documentation and confirm Thumb-2 compatibility is explicitly listed.

Reserve Some ARM Space in Memory Map

While your Cortex-M3 code will be Thumb, reserve space in the memory map for some ARM mode exception handlers. The low level ARM exception code helps transition to Thumb cleanly. Locate this ARM code in the memory map by using linker region directives. .arm.text : { *(.arm.text*) }

Put these handlers in a known ARM-only region. The rest of the app code can safely stay in Thumb mode.

Use UAL Syntax in Assembly Code

UAL (Unified Assembly Language) provides syntax compatible with both ARM and Thumb assembly. By using UAL conventions like using R registers instead of RN, assembly code can be assembled into either mode. This makes code reuse easier when using both Thumb and ARM. ADD R0, R1, R2 ; UAL syntax

The assembler will convert UAL instructions into the proper Thumb or ARM encodings based on section directives. UAL allows writing generic code sections that can be used in either mode.

Order Code Sections from ARM to Thumb

When organizing code sections, list ARM first followed by Thumb during linking. Linker section placement order matters for proper interworking veneers. Listing ARM code first simplifies transitioning into Thumb mode when sections get linked together. ARM_Code1 ARM_Code2 … Thumb_Code1 Thumb_Code2 …

Following this order guarantees ARM code appears at lower addresses than Thumb code in the final binary. This convention produces the shortest interworking veneers.

Use Assembly Wrappers Around ARM Functions

For legacy ARM library functions, create a thin wrapper in assembly to transition modes properly: .thumb .thumb_func .global ThumbFunc ThumbFunc: bx ARMFunc ; switch to ARM nop .arm ARMFunc: // ARM instructions

This guarantees Thumb mode leading into and out of ARM code segments. The wrappers isolate ARM so it does not corrupt surrounding Thumb areas.

Debug Faulty Veneers Causing Mode Switching

If switching between Thumb and ARM code, debug any interworking veneers that may be buggy. Faulty veneers are a common source of improperly changed T-bit values. Use breakpoint debugging or log trace records to ensure veneers operate correctly.

Veneers should follow this proper sequence: Thumb: BLX ARMStub BX LR ARMStub: BX ThumbFunc

Confirm this flow is followed to rule out veneer issues when tracking down mode switching problems.

Limit Inline Assembly Code Sections

Excessive use of inline assembly code can inadvertently toggle the T-bit. Try to minimize inline asm statements and consolidate them into named functions. This contained approach reduces the likelihood of inline asm accidentally enabling ARM mode. // Avoid littering code with asm blocks asm { ARM instructions… } // Instead use contained asm functions void asmFunc() { asm { ARM instructions … } }

Proper encapsulation limits the reach of inline asm, making the code easier to manage in Thumb mode.

Use Compiler Optimization Pragmas

Compiler optimizers can generate incorrect code that improperly changes modes. Use pragmas or command line options to selectively disable optimizations that could impact the T-bit: #pragma O0 // disable optimization for function -O0 // disable optimization for file

Diagnosing flags that introduce erroneous BX or BL instructions will ensure the compiler preserves Thumb mode correctly.

Reserve unused PC Bits to Validate Thumb Mode

When returning from an exception the PC is checked for the bit pattern 0bXXXX0000. Since bits 4:0 are all zero in Thumb, we can use bits 14:5 as a signature to verify Thumb mode on exception return. Set your own signature value in the unused bits. ldr pc, [sp], #4 // Exception return // Check reserved PC bits ands pc, #0x7FE0 bne goodThumbPc // Invalid PC, handle error

This technique provides runtime validation of the PC value and Thumb state integrity. Having your own signature encodes an extra level of protection.

Summary

In summary, keeping Cortex-M3 locked in Thumb mode requires utilizing:

  • T-bit configuration in reset handler and linker scripts
  • BLX instructions for function calls
  • Thumb assembly directives on code
  • Validator debugger checks on T-bit
  • Proper veneer sequences when transitioning
  • Reserved PC bit patterns to check mode

Following ARM recommended programming practices strictly, along with robust debugging, ensures Thumb mode remains enabled reliably in Cortex-M3 applications. The result is compact, efficient code that maximizes the performance and capability of the Cortex-M3 processor.

Newsletter Form (#3)

More ARM insights right in your inbox

 


Share This Article
Facebook Twitter Email Copy Link Print
Previous Article Why is there rotate right but not rotate left instruction in cortex m3?
Next Article Using BX and BLX Instructions Correctly in Thumb Code
Leave a comment Leave a comment

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

2k Followers Like
3k Followers Follow
10.1k Followers Pin
- Sponsored-
Ad image

You Might Also Like

Are ARM processors more powerful?

ARM processors have become increasingly popular in recent years, powering…

6 Min Read

What is unaligned memory access?

Unaligned memory access refers to reading data from or writing…

8 Min Read

Scatter Load File Best Practices for ARM Cortex-M Applications

A scatter load file is an essential component when building…

8 Min Read

Debugging Multi-Core ARM Designs with SWD

ARM processors are extremely popular in embedded systems due to…

7 Min Read
SoCSoC
  • Looking for Something?
  • Privacy Policy
  • About Us
  • Sitemap
  • Contact Us
Welcome Back!

Sign in to your account