CMSIS - Extra

Resources: none

Goal

To understand how the CMSIS defines evaluate to the same value at compile time.

Required hardware

  • None

Introduction

As seen in CMSIS, there are three definitions for each bitfield in each register in each module. These are called bit field masks.

As an example, let's look at the definitions of the CRST bitfield in the TCR register of a CTIMER module.

TCR register diagram

CMSIS defines

The CMSIS defines for this bit field are:

#define CTIMER_TCR_CRST_MASK   (0x2U)
#define CTIMER_TCR_CRST_SHIFT  (1U)
#define CTIMER_TCR_CRST(x)     (((uint32_t)(((uint32_t)(x)) << CTIMER_TCR_CRST_SHIFT)) & CTIMER_TCR_CRST_MASK)

These defines can be used as follows. Notice that all instructions have the same result!

// Set the CRST bit field in the TCR register in the CTIMER1 module and leave
// all other bits unchanged. Notice that all instructions have the same result!
CTIMER1->TCR |= 2U;                            // 1
CTIMER1->TCR |= 0b10U;                         // 2
CTIMER1->TCR |= 0x2U;                          // 3
CTIMER1->TCR |= CTIMER_TCR_CRST_MASK;          // 4
CTIMER1->TCR |= (1U << CTIMER_TCR_CRST_SHIFT); // 5
CTIMER1->TCR |= CTIMER_TCR_CRST(1);            // 6
CTIMER1->TCR |= CTIMER_TCR_CRST(0b1);          // 7
CTIMER1->TCR |= CTIMER_TCR_CRST(0x1);          // 8

Let's see why all instruction evaluate to the same value.

Instruction 1-3

CTIMER1->TCR |= 2U; // 1 - Decimal
CTIMER1->TCR |= 0b10U; // 2 - Binary
CTIMER1->TCR |= 0x2U; // 3 - Hexadecimal

The only difference in these instructions is that the operand is written in a different number system: decimal, binary, and hexadecimal. The capital 'U' is a postfix making sure the number is 32-bit unsigned. Writing these number in 32-bit hexadecimal gives:

   2U = 0x00000002
0b10U = 0x00000002
 0x2U = 0x00000002

Instruction 4

The evaluation of this instruction, which is done at compile time, is as follows:

CTIMER1->TCR |= CTIMER_TCR_CRST_MASK; // 4 - Initial expression
CTIMER1->TCR |= 0x2U; // Substituted define
CTIMER1->TCR |= 0x00000002; // Rewritten to 32-bit hexadecimal

Instruction 5

The evaluation of this instruction, which is done at compile time, is as follows:

CTIMER1->TCR |= (1U << CTIMER_TCR_CRST_SHIFT); // 5 - Initial expression
CTIMER1->TCR |= (1U << 1U); // Substituted define
CTIMER1->TCR |= (2U); // Bitwise left shifted
CTIMER1->TCR |= 0x00000002; // Rewritten to 32-bit hexadecimal

Instruction 6

The evaluation of this instruction, which is done at compile time, is as follows:

CTIMER1->TCR |= CTIMER_TCR_CRST(1); // 6 - Initial expression
CTIMER1->TCR |= (((uint32_t)(((uint32_t)(1)) << CTIMER_TCR_CRST_SHIFT)) & CTIMER_TCR_CRST_MASK); // Substituted define
CTIMER1->TCR |= (((uint32_t)(((uint32_t)(1)) << 1U)) & 0x2U); // Substituted defines
CTIMER1->TCR |= (((uint32_t)(1U << 1U)) & 0x2U); // Rewritten typecast
CTIMER1->TCR |= (((uint32_t)(2U)) & 0x2U); // Bitwise left shifted
CTIMER1->TCR |= ((2U) & 0x2U); // Rewritten typecast
CTIMER1->TCR |= (2U); // Bitwise and
CTIMER1->TCR |= 0x00000002; // Rewritten to 32-bit hexadecimal

Instruction 7-8

These are identical to instruction 6, because only the value given as an argument is written in another number system.

CTIMER1->TCR |= CTIMER_TCR_CRST(0b1); // 7
CTIMER1->TCR |= CTIMER_TCR_CRST(0x1); // 8

Final note

You might wonder why instruction 6 includes a bitwise-and operation. This bitwise-and is added to mask bits that otherwise might accidentally be set. For example, the following instruction is invalid, because the CRST bitfield consists of a single bit only:

CTIMER1->TCR |= CTIMER_TCR_CRST(0xF);

The compiler does not give an error or warning about such a statement, but it is clearly not possible to set four bits in a single bitfield. This mistake is prevented by the CMSIS compliant define as follows:

CTIMER1->TCR |= CTIMER_TCR_CRST(0xF); // 6 - Initial expression
CTIMER1->TCR |= (((uint32_t)(((uint32_t)(0xF)) << CTIMER_TCR_CRST_SHIFT)) & CTIMER_TCR_CRST_MASK); // Substituted define
CTIMER1->TCR |= (((uint32_t)(((uint32_t)(0xF)) << 1U)) & 0x2U); // Substituted defines
CTIMER1->TCR |= (((uint32_t)(0XFU << 1U)) & 0x2U); // Rewritten typecast
CTIMER1->TCR |= (((uint32_t)(0x1EU)) & 0x2U); // Bitwise left shifted
CTIMER1->TCR |= ((0x1EU) & 0x2U); // Rewritten typecast
CTIMER1->TCR |= (2U); // Bitwise and
CTIMER1->TCR |= 0x00000002; // Rewritten to 32-bit hexadecimal

In other words, this define only takes the first bit in the value you pass as a parameter into account.