SVC call causing hardfault when there is no pre_emption priority

I met a problem now.On starting the scheduler, the SVC 0 instruction at the end of vPortStartFirstTask causes a Hardfault exception.And i found out that the reason for this error is because i set the priority group to 7 (0 bits for pre-emption priority and 4 bits for subpriority).When i set the priority group to 3 (4 bits for pre-emption priority and 0 bits for subpriority) according to the document ,the task can run normal.But i wonder why it(SVC 0) cause a Hardfault exception when i set priority group to 7.
Dose anyone know anything about this?

The SVC instruction must preempt the current execution priority or it will cause a hard fault. Thus you must configure at least one priority bit for preemption priority to use SVC.

In FreeRTOS specifically, you are expected to configure all priority bits for preemption priority.

This page contains the details - RTOS for ARM Cortex-M.

It is recommended to assign all the priority bits to be preempt priority bits, leaving no priority bits as subpriority bits. 

Yes, i know that the SVC call must be executed immediately,otherwise,it will cause hard exception.But if i set priority group to no bits for pre-emption,that means there are no high-priority exceptions to preempt SVC,because all exceptions have preempt priority 0.So why does SVC still cause hardfault?That’s what i don’t understand.

Yes,I’ve seen it.But it said ‘recommended’ not ‘must’.I can follow the page to set all the priority bits to be preempt priority bits,that’s no problem.But i also want to know why the SVC instruction will cause hardfault when i set no bits to preempt priority.Is that a property of Cortex-M?

Lets first understand the priority grouping. Priority grouping splits exception priority into two parts, the Group Priority and the Subgroup Priority. The Group Priority field defines the priority for preemption i.e. an exception can only preempt another exception if its Group Priority is higher (numerically lower).

Consider the following example -

  1. Total number of priority bits is 4.
  2. PRIGROUP is configured to treat 2 bits for Group Priority and 2 bits for Subgroup Priority.
  3. Exception1’s priority is 13 and Exception2’s priority is 15.
             Group      |   Subgroup   
            Priority    |   Priority   
           |            |             |
Exception1 |  1    1    |    0    1   |
           |            |             |
           |            |             |
Exception2 |  1    1    |    1    1   |
           |            |             |

Even though Exception1’s priority is higher (numerically lower), it cannot preempt Exception2 because their Group Priorities (b11) are equal.

When you configure PRIGROUP to leave no bits for Group Priority, no exception can preempt any other because each has the same Group Priority.

With that, the following is from the ARMv7-M Architecture Reference Manual:

When the group priority of a Supervisor Call (SVC) is lower than or equal to the
currently executing priority, inhibiting normal preemption, the processor escalates the exception priority to HardFault.

So, if you configure PRIGROUP to leave no bits for Group Priority, SVC call will not be able to preempt and will escalate to hard fault.

Yes,thank you.I got it.Now,i still don’t understand that if i set no bits to preempt priority bits.Does the SVC 0 will cause hardfault whether or not even if there are no exceptions pending current?Or if there are no exceptions created when executing the SVC 0 instruction,does it cause fault?

The explanation for that is - When you call a FreeRTOS API (such that xTaskCreate) before starting the scheduler, interrupts upto the priority configMAX_SYSCALL_INTERRUPT_PRIORITY are disabled and are only enabled when the scheduler is started. If you configure PRIGROUP to leave no bits for Group Priority, disabling interrupts upto configMAX_SYSCALL_INTERRUPT_PRIORITY would disable all the interrupts as there is no preemption priority. As a result, SVC will not be able to preempt the execution and would escalate to hard fault.

I‘m sorry that i still don’t understand.I tried to break away from the rtos and run the program on bare metal.The SVC call also cause hardfault when i set no bits to preempt priority bits.Is this the hardware design of Contex-M?You must set preempt priority bits if you want to use SVC call.

That should not happen. I ran the following code on a Cortex-M7:

  /* This configures the PRIGROUP to leave no bits for Group Priority. */
   __asm volatile ("svc 0");

And the code branched to SVC_Handler. Which Cortex-M part are you using? Can you check the value of BASEPRI and PRIMASK registers to ensures that no interrupts are masked?

The simplest answer is yes. Otherwise the hardware operates in a grey area. If you want the hardware to support preemption, then you should not give it zero bits for managing preemption priorities. If you use the SVC instruction, then you are asking the hardware to support preemption.

Despite the grey area, I was interested in the test @aggarg did. I duplicated it on Cortex-M4. Turns out BASEPRI can mask SVC in this case, which is evidence of being in a gray area. BASEPRI is not supposed to be able to mask priority-0 interrupts. And BASEPRI bits outside the priority grouping bits are not supposed to mask any interrupts. From the ARMv7-M Architecture Reference Manual:

BASEPRI: A non-zero value, qualified by the value of the AIRCR.PRIGROUP field, acts as a priority mask.

So what should a non-zero BASEPRI do when none of its bits are priority grouping bits? Seems it should do nothing, but instead it masks all interrupts.

Okay,thanks for your test.I have do it again.I set no bits for group priority at the beginning of the main function, and then called SVC 0.The code branched to SVC_Handler normaly.Maybe i set something incorrectly in the previous experiment.
In fact,it shows that the SVC call is not directly related to priority groups,isn’t it?

A possible explanation in this grey area is that when you configure PRIGROUP to leave no bits for Group Priority, all exceptions have the same preemption priority. So a non-zero BASEPRI ends up masking all of them.

It may play a role in combination with BASEPRI as @jefftenney also found in his experiments. The main point is - if SVC cannot preempt the execution immediately for any reason, it escalates to hard fault.

1 Like

May be.I tyied many times.I found that when i set no bits to preempt priority,the code branched to SVC_Handler normaly only at the BASEPRI register value is 0x00 ~ 0x0F. If i set the BASEPRI register value larger than 0x0F,like 0x10,then it will cause hardfault execution.

Most likely your hardware implements 4 priority bits. So values 0x00 ~ 0x0F are essentially 0. Anything bigger than 0x0F is a non-zero value for BASEPRI which results in masking interrupts.

Yes,it is,i think so.And when i set BASEPRI register is 0x10,i will cause hardfault execution even there are no other executions pending.That means the SVC call will be executed immediately and no other executions influenced SVC.But it still cause hardfault.

As I mentioned before, when PRIGROUP is configured to leave no bits for preemption priority, non-zero BASEPRI masks all exceptions which includes SVC. This is the reason that SVC cannot preempt execution and escalates to hard fault.

Okay,i got it.Thank you.