0%

Cotex-M0 and plus

Cortex-M0 和 M0+ 的区别。

M0 和 M0+ 的区别

参考:Cortex® -M0 Versus Cortex-M0+ - KBA211306

The Cortex-M0+ processor builds on the Cortex-M0 processor, retaining the full instruction set and tool compatibility, while reducing energy consumption and increasing performance.

The following table lists the difference in the features of the two processors.

Features M0 M0+ Advantages of M0+ over M0
Pipeline Three-stage Two-stage Improved response time,improved efficiency
Performance Efficiency 2.33 CoreMark/MHz 2.46 CoreMark/MHz Lower power and higher performance
Memory Protection Not available Has optional Memory protection Unit Makes system more secure by: Separating processes – Preventing tasks from corrupting stack or data memory used by other tasks Preventing unprivileged tasks from accessing peripherals that can be critical to the system security
Relocatable vectortable Does not support Supports Allows relocating the interrupt vector table anywhere in the memory - enables different applications to use their own vector table.
Unprivileged/privileged mode execution Does not support Supports Allows a task, such as the system calling in an operating system, to execute with more privileges than the user task or an application.

指令集

Cortex-M0 使用了 ARMv6-M 指令集,该指令集由 2 部分组成:

  • 除 CBZ, CBNZ 和 IT 外的所有 ARMv7-M 指令。
  • 32-bit Thumb-2 指令:BL, DMB, DSB, ISB, MRS and MSR。

Reference < Cotex-M0 Technical Reference Manual > 3.3 Instruction set summary

所以在移植 Cortex-M3 的汇编代码到 Cortex-M0 时要注意两点:

  • CBZ 指令要用 CMP + BEQ 来代替。
  • STM 等多寄存器操作最大只能到 R7。

工作模式

Cortex-M0 有两种工作模式:

  • Thread mode - Normal code
  • Handler mode - Interrupt

两种模式的唯一区别是 thread mode 可以额外选择使用 PSP 作为栈指针寄存器。

Reference <The Definitive Guide to ARM Cortex-M0 and Cortex-M0+ Processors 2nd Edition> Chapter 3 - Programmer’s Modle - Operation Modes and States - page 26

用户等级

Cortex-M0+ 有两种用户等级:

  • Privileged
  • Nonprivileged

Privileged 等级可以访问所有的内存,相对的,Nonprivileged 等级不能访问部分系统控制寄存器。

但 Cortex-M0 没有用户等级的区别,换句话说,Cortex-M0 始终工作在 Privileged 等级。

Reference <Joseph Yiu - The Definitive Guide to ARM Cortex-M0 and Cortex-M0+ Processors 2nd Edition> Chapter 7.8 - Memory Attributes and Memory Access Permission - page 179

中断进/出操作和序列

中断进入:

  • 把 R0 to R3, R12, LR, PC 和 xPSR 压入中断前使用的栈
  • 取向量
  • 更新 LR, IPSR 和 NVIC

中断退出:

  • 把 R0 to R3, R12, LR, PC 和 xPSR 推出栈(根据 LR 的 EXC_RETURN bit 决定 MSP 还是 PSP)
  • 返回断点处执行

Reference < Joseph Yiu - The Definitive Guide to ARM Cortex-M0 and Cortex-M0+ Processors 2nd Edition > page 150

内核中断控制

Cortex-M0 的内核中断是在 SCB 寄存器组内控制的。

image-20201208214549837

Reference <The Definitive Guide to ARM Cortex-M0 and Cortex-M0+ Processors 2nd Edition> - Chapter 9.2.1

其中,SHPR2 和 SHPR3 是控制内核中断的优先级的寄存器,ICSR 是悬起/解悬中断的寄存器。

image-20201208214558944

Reference <[The Definitive Guide to ARM Cortex-M0 and Cortex-M0+ Processors 2nd Edition](../../resources/ARM/The Definitive Guide to ARM Cortex-M0 and Cortex-M0+ Processors 2nd Edition.pdf)> - Chapter 9.2.3

内核中断中,除 NMI, hardfault 外优先级都是可编程的,除 SYSTICK 外都是无法 disable 的。

image-20201208214649538

Reference <The Definitive Guide to ARM Cortex-M0 and Cortex-M0+ Processors 2nd Edition> - Chapter 10.3.1

ARMv6-M 和 ARMv7-M 的一些具体区别

LDMIA 指令

LDMIA <Rn>!, <registers>

Cortex-M0 的 LDMIA 指令只能操作 R0 ~ R7,是因为其只支持 16 bit 的指令。机器码格式如下:

1
2
| 15 14 13 12 | 11 | 10 9 8 | 7 6 5 4 3 2 1 0 |
| 1 1 0 0 | 1 | Rn | register_list |

其中register_list的每个 bit 分别代表 R0 ~ R7 之间的一个寄存器。所以 LDMIA 只能操作到寄存器 R7。

如果出现Error: cannot honor width suffix这样的编译错误,多半是操作的寄存器超出了范围,比如 LDMIA R0!, {R4 ~ R8}。

Reference < ARMv6-M Architecture Reference Manual > A6.7.25 LDM, LDMIA, LDMFD

Cortex-M3 的 LDMIA 指令还支持 32 bit 格式:

1
2
| 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 | 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 |
| 1 1 1 0 1| 0 0|0 1 0|W 1| Rn | P | M|(0)| register_list |

所以 Cortex-M3 最大可以操作到寄存器 R11。

Reference < ARMv7-M Architecture Reference Manual > A7.7.40 LDM, LDMIA, LDMFD

通用寄存器出入栈的汇编代码

由于 Cortex-M0 的 LDMIA 指令最大只能操作到 R7,所以只对于 R8 ~ R11 的入栈,要通过先拷贝 R8 ~ R11 到 R4 ~ R7 后再入栈 R4 - R7 的方法来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
void xPortPendSVHandler( void )
{
/* This is a naked function. */

__asm volatile
(
" mrs r0, psp \n"
" \n"
" ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
" ldr r2, [r3] \n"
" \n"
" sub r0, r0, #32 \n" /* Make space for the remaining low registers. */
" str r0, [r2] \n" /* Save the new top of stack. */
" stmia r0!, {r4-r7} \n" /* Store the low registers that are not saved automatically. */
" mov r4, r8 \n" /* Store the high registers. */
" mov r5, r9 \n"
" mov r6, r10 \n"
" mov r7, r11 \n"
" stmia r0!, {r4-r7} \n"
" \n"
" push {r3, r14} \n"
" cpsid i \n"
" bl vTaskSwitchContext \n"
" cpsie i \n"
" pop {r2, r3} \n" /* lr goes in r3. r2 now holds tcb pointer. */
" \n"
" ldr r1, [r2] \n"
" ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */
" add r0, r0, #16 \n" /* Move to the high registers. */
" ldmia r0!, {r4-r7} \n" /* Pop the high registers. */
" mov r8, r4 \n"
" mov r9, r5 \n"
" mov r10, r6 \n"
" mov r11, r7 \n"
" \n"
" msr psp, r0 \n" /* Remember the new top of stack for the task. */
" \n"
" sub r0, r0, #32 \n" /* Go back for the low registers that are not automatically restored. */
" ldmia r0!, {r4-r7} \n" /* Pop low registers. */
" \n"
" bx r3 \n"
" \n"
" .align 4 \n"
"pxCurrentTCBConst: .word pxCurrentTCB "
);
}

而 Cortex-M3 则可以直接入栈 R4 ~ R11:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void xPortPendSVHandler( void )
{
/* This is a naked function. */

__asm volatile
(
" mrs r0, psp \n"
" isb \n"
" \n"
" ldr r3, pxCurrentTCBConst \n" /* Get the location of the current TCB. */
" ldr r2, [r3] \n"
" \n"
" stmdb r0!, {r4-r11} \n" /* Save the remaining registers. */
" str r0, [r2] \n" /* Save the new top of stack into the first member of the TCB. */
" \n"
" stmdb sp!, {r3, r14} \n"
" mov r0, %0 \n"
" msr basepri, r0 \n"
" bl vTaskSwitchContext \n"
" mov r0, #0 \n"
" msr basepri, r0 \n"
" ldmia sp!, {r3, r14} \n"
" \n" /* Restore the context, including the critical nesting count. */
" ldr r1, [r3] \n"
" ldr r0, [r1] \n" /* The first item in pxCurrentTCB is the task top of stack. */
" ldmia r0!, {r4-r11} \n" /* Pop the registers. */
" msr psp, r0 \n"
" isb \n"
" bx r14 \n"
" \n"
" .align 4 \n"
"pxCurrentTCBConst: .word pxCurrentTCB \n"
::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY)
);
}
坚持原创技术分享,您的支持将鼓励我继续创作!