2

I have some old code for STM32F4 in which a critical section looks like

uint32_t primask;
primask = __get_PRIMASK();
__disable_irq();

/* ... Critical code ... */

__set_PRIMASK(primask);

I read in this article that the right way to restore interrupt mask after a critical section is instead:

if (!primask) {
     __enable_irq();
}

The old code "seems to works", but I couldn't find any similar example, and am concerned about possible side effects. The documentation is quite ambiguous about PRIMASK register. Here an excerpt:

enter image description here

There are two things here that concern me:

  1. Bits 1-31 are "reserved", is it safe to assume that they are zeroes? If not, we should rather write if ((primask & 1) == 0).
  2. Writing 0 in Bit 0 is labeled as "No effect". It does not seem to be true (indeed previos interrupt mask is restored), but it does explain why the suggested way to leave the critical section is by __enable_irq().

How should I interpret that "No effect"?

Are there any drawbacks in using __set_PRIMASK() to leave a critical section?

Giuseppe Guerrini
  • 4,274
  • 17
  • 32
  • 0: "No effect" in this context means "interrupt masking doesn't happen". Unlike 1: "interrupts are masked". It is indeed a poor choice of words. – Ilya Feb 23 '23 at 18:04

2 Answers2

1

These two methods are completely equivalent.

__set_PRIMASK(value) uses one 32-bit instruction (MSR).

if(!value){__enable_irq();} probably uses three 16-bit instructions (CMP,IT,CPSIE).

On older architectures maybe only the second option was available, but if both are available then the result is always identical.

In my opinion __set_PRIMASK looks better paired with __get_PRIMASK.

I think that when the ST manual says "no effect" they mean having a zero there causes no change in the usual handling of interrupts. As you have tested, writing a zero will have the effect of making the value in the register be zero.

You should refer to the ARMv7M Architecture Reference Manual for the authoritative documentation.

You will also find in that document that it says explicitly that PRIMASK is a "one bit register". You don't need to worry about masking off the other bits.

Tom V
  • 4,827
  • 2
  • 5
  • 22
1

Cortex M4 is ARMv7-M, so Architecture Reference Manual from ARM applies.

Here

Page 519

enter image description here

It explicitly says "1-bit register". Which can be understood as "other bits are not implemented".

PRIMASK value 0 means no masking happens. No effect = No masking.
PRIMASK value 1 means interrupts with configurable priority are masked.

Now for the greatest part: your main question - to write vs to read-modify - the answer is you can definitely set it straight with new value, but not for the reason you probably expect. You set the value not by writing to that register. When you set PRIMASK, you never actually write to that register. You execute instruction __asm volatile("cpsid i"); to set PRIMASK to 1, and you execute __asm volatile("cpsie i"); to set PRIMASK to 0. This is a special instruction that exists specifically to do this. You can paste it right into your C/C++. From the same page of the document:

enter image description here

Ilya
  • 992
  • 7
  • 14