ARM Cortex-M Thumb Mode B Instruction PC Calculation Discrepancy

The ARM Cortex-M architecture, particularly in Thumb mode, exhibits a discrepancy in the Program Counter (PC) calculation for the B (Branch) instruction. According to the ARMv7-M Architecture Reference Manual (ARM DDI 0403E.e), the expected behavior is that the PC should be incremented by the immediate value specified in the instruction. However, in practice, the PC is observed to be incremented by an additional 4 bytes. This discrepancy can lead to unexpected behavior in embedded systems, particularly when precise control over program flow is required.

The issue manifests when executing a B instruction in Thumb mode on an ARM Cortex-M processor, such as the nrf52840dk. The instruction encoding and the expected PC calculation are well-documented, but the actual behavior on the hardware deviates from the documented behavior. This discrepancy is particularly problematic for developers who rely on the manual for precise control over program flow and timing.

Thumb Mode PC Calculation and Instruction Encoding

The ARM Cortex-M architecture operates in Thumb mode by default, which uses a 16-bit instruction set. The B instruction in Thumb mode is encoded as a conditional branch with an 8-bit immediate value. The encoding format for the B instruction is as follows:

1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0
5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
--------------------------------
|1 1 0 1| cond | imm8 |
--------------------------------

In this encoding, the cond field specifies the condition for the branch, and the imm8 field specifies the immediate value used to calculate the branch target. The immediate value is sign-extended to 32 bits and added to the current PC to determine the branch target.

According to the ARMv7-M Architecture Reference Manual, the PC calculation for the B instruction should be as follows:

imm32 = SignExtend(imm8:'0', 32);
BranchWritePC(PC + imm32);

The BranchWritePC function is defined as:

BranchWritePC(bits(32) address)
    BranchTo(address<31:1>:’0’);

And the BranchTo function is defined as:

BranchTo(bits(32) address)
    _R[RName_PC] = address;
    return;

Based on this documentation, the expected behavior is that the PC should be incremented by the sign-extended immediate value. However, in practice, the PC is observed to be incremented by an additional 4 bytes.

Thumb Mode PC Calculation and Instruction Execution

The discrepancy in PC calculation arises from the way the ARM Cortex-M architecture handles the PC in Thumb mode. In Thumb mode, the PC is always aligned to a 4-byte boundary, and the value of the PC used in calculations is the address of the current instruction plus 4 bytes. This is due to the pipeline nature of the ARM architecture, where the PC points to the next instruction to be fetched, not the currently executing instruction.

The ARMv7-M Architecture Reference Manual provides a description of the PC calculation in Thumb mode:

Calculate the PC or Align(PC,4) value of the instruction. The PC value of an instruction is its address plus 4 for a Thumb instruction. The Align(PC,4) value of an instruction is its PC value ANDed with 0xFFFFFFFC to force it to be word-aligned. (A4-104)

This means that when the B instruction is executed, the PC value used in the calculation is the address of the current instruction plus 4 bytes. This results in the observed behavior where the branch target is calculated as PC + imm32 + 4.

Implementing Correct PC Calculation and Branch Target Adjustment

To address the discrepancy between the documented behavior and the observed behavior, developers must adjust their understanding of the PC calculation in Thumb mode. When writing assembly code or analyzing disassembled code, it is important to account for the additional 4-byte offset in the PC calculation.

For example, consider the following assembly code snippet:

0x20000200: db01        blt.n   0x20000206
0x20000202: f240 0b0b   movw    r11, #11
0x20000206: f8cc b006   str.w   r11, [r12, #6]

In this example, the blt.n instruction at address 0x20000200 is expected to branch to address 0x20000206 if the condition is met. However, due to the additional 4-byte offset in the PC calculation, the branch target is effectively 0x20000206.

To ensure correct behavior, developers should adjust their branch target calculations to account for the additional 4-byte offset. This can be done by subtracting 4 bytes from the intended branch target when encoding the B instruction.

For example, if the intended branch target is 0x20000206, the immediate value in the B instruction should be calculated as follows:

imm8 = (target_address - (current_address + 4)) >> 1;

This adjustment ensures that the branch target is correctly calculated, taking into account the additional 4-byte offset in the PC calculation.

Conclusion

The discrepancy between the documented behavior and the observed behavior of the B instruction in Thumb mode on ARM Cortex-M processors is due to the way the PC is calculated in Thumb mode. The PC value used in the calculation is the address of the current instruction plus 4 bytes, which results in an additional 4-byte offset in the branch target calculation. Developers must account for this offset when writing assembly code or analyzing disassembled code to ensure correct program flow and timing.

By understanding the underlying mechanics of the PC calculation in Thumb mode and adjusting branch target calculations accordingly, developers can avoid unexpected behavior and ensure reliable operation of their embedded systems.

Similar Posts

Leave a Reply

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