当执行到达最后一条return
语句时,嵌入式处理器中会发生什么?一切都照原样冻结吗?功耗等,天空中永远只有一个NOP?还是连续执行NOP,或者处理器将完全关闭?
我问的部分原因是,我想知道处理器是否需要在完成执行之前关闭电源,并且如果确实关闭了电源,那么它将如何完成执行?
当执行到达最后一条return
语句时,嵌入式处理器中会发生什么?一切都照原样冻结吗?功耗等,天空中永远只有一个NOP?还是连续执行NOP,或者处理器将完全关闭?
我问的部分原因是,我想知道处理器是否需要在完成执行之前关闭电源,并且如果确实关闭了电源,那么它将如何完成执行?
Answers:
这是我父亲一直问我的问题。“ 为什么不只执行所有指令并在最后停止? ”
让我们看一个病理例子。以下代码是在Microchip的PIC18的C18编译器中编译的:
void main(void)
{
}
它产生以下汇编输出:
addr opco instruction
---- ---- -----------
0000 EF63 GOTO 0xc6
0002 F000 NOP
0004 0012 RETURN 0
.
. some instructions removed for brevity
.
00C6 EE15 LFSR 0x1, 0x500
00C8 F000 NOP
00CA EE25 LFSR 0x2, 0x500
00CC F000 NOP
.
. some instructions removed for brevity
.
00D6 EC72 CALL 0xe4, 0 // Call the initialisation code
00D8 F000 NOP //
00DA EC71 CALL 0xe2, 0 // Here we call main()
00DC F000 NOP //
00DE D7FB BRA 0xd6 // Jump back to address 00D6
.
. some instructions removed for brevity
.
00E2 0012 RETURN 0 // This is main()
00E4 0012 RETURN 0 // This is the initialisation code
如您所见,main()被调用,并且最后包含一个return语句,尽管我们自己没有明确地将它放在那里。当main返回时,CPU执行下一条指令,该指令只是GOTO以返回到代码的开头。main()被简单地反复调用。
话虽如此,这不是人们通常会做的事情。我从未写过任何允许main()退出的嵌入式代码。通常,我的代码如下所示:
void main(void)
{
while(1)
{
wait_timer();
do_some_task();
}
}
因此,我通常不会让main()退出。
你说“好吧好”。所有这些非常有趣,编译器确保永远不会有最后一个return语句。但是,如果我们强制执行该问题会怎样?如果我手动编码了我的汇编程序,但又没有跳回开头怎么办?
好吧,很明显,CPU只会继续执行下一条指令。这些看起来像这样:
addr opco instruction
---- ---- -----------
00E6 FFFF NOP
00E8 FFFF NOP
00EA FFFF NOP
00EB FFFF NOP
.
. some instructions removed for brevity
.
7EE8 FFFF NOP
7FFA FFFF NOP
7FFC FFFF NOP
7FFE FFFF NOP
main()中最后一条指令之后的下一个内存地址为空。在具有闪存的微控制器上,空指令包含值0xFFFF。至少在PIC上,该操作码被解释为“ nop”或“ no operation”。它根本什么也没做。CPU将从内存一直一直执行到所有结束。
那是什么
在最后一条指令中,CPU的指令指针为0x7FFe。当CPU将2加到其指令指针时,它得到0x8000,这在只有32k FLASH的PIC上被认为是溢出,因此它回绕回0x0000,CPU高兴地在代码开始处继续执行指令,就好像它已被重置一样。
您还询问了是否需要关闭电源。基本上,您可以做任何您想做的事,这取决于您的应用程序。
如果您确实有一个应用程序,该应用程序在开机后只需要做一件事,然后什么也不做,那么您可以放一会儿(1); 在main()的末尾,以便CPU停止执行任何明显的操作。
如果应用程序要求CPU断电,则取决于CPU,可能会有各种可用的睡眠模式。但是,CPU有重新唤醒的习惯,因此您必须确保睡眠没有时间限制,并且没有活动的Watch Dog Timer等。
您甚至可以组织一些外部电路,这些电路将允许CPU在完成后完全切断自身的电源。看到以下问题:使用瞬时按钮作为闩锁开/关拨动开关。
这里有两点要指出:
在嵌入式环境中,通常不存在程序关闭的概念。在较低级别上,CPU将在可能的情况下执行指令。没有“最终回报声明”之类的东西。如果CPU遇到不可恢复的错误或显式暂停(进入睡眠模式,低功耗模式等),则可能会停止执行,但是请注意,即使是睡眠模式或不可恢复的错误也通常不能保证不再有更多的代码要执行。被执行。您可以从睡眠模式中唤醒(这是它们通常的使用方式),甚至锁定的CPU仍可以执行NMI处理程序(Cortex-M就是这种情况)。看门狗也仍然会运行,并且一旦启用,您可能无法在某些微控制器上将其禁用。架构之间的细节差异很大。
如果固件是用C或C ++等语言编写的,则main()退出时发生的情况由启动代码确定。例如,这是STM32标准外设库中启动代码的相关部分(对于GNU工具链,注释是我的):
Reset_Handler:
/* ... */
bl main ; call main(), lr points to next instruction
bx lr ; infinite loop
尽管main()返回时,此代码将进入无限循环,尽管以一种非显而易见的方式(bl main
加载lr
下一条指令的地址,实际上是对其自身的跳转)。不会尝试停止CPU或使其进入低功耗模式,等等。如果您在应用程序中有对此的合法需要,您必须自己做。
请注意,如ARMv7-M ARM A2.3.1中所指定,链接寄存器在复位时设置为0xFFFFFFFF,并且分支到该地址将触发故障。因此,Cortex-M的设计师决定将重置处理程序的返回视为异常,因此很难与他们争论。
谈到在固件完成后停止CPU的合法需求,很难想象有任何方法可以通过关闭设备电源来解决。(如果您确实永久禁用了CPU,则只能对设备执行电源循环或外部硬件复位操作。)您可以取消DC / DC转换器的ENABLE信号置为有效,或者关闭电源。其他方式,例如ATX PC。
loop: b loop
。我不知道他们是否真的打算退货却忘了保存lr
。
当询问时return
,您认为水平过高。C代码被翻译成机器代码。因此,如果您反而想到处理器盲目地将指令从内存中拉出并执行它们,则不知道哪一个是“最终”指令return
。因此,处理器没有固有的目的,而是由程序员来处理最终情况。正如Leon在他的答案中指出的那样,编译器已编写了默认行为,但程序员常常可能希望自己关闭程序(我做了很多事情,例如进入低功耗模式并暂停,或者等待USB电缆插入进入,然后重新启动)。
许多微处理器都有停止指令,这会使处理器停止运行而不会影响特性。其他处理器可以通过简单地重复跳到同一地址来依靠“停止”。有可能的选择,但这取决于程序员,因为处理器即使不打算将内存用作指令,也只会从内存中读取指令。
这个问题不是嵌入式的(嵌入式系统可以运行Linux甚至是Windows,也可以是Windows),而是独立的或裸机的:(编译的)应用程序是计算机上唯一运行的东西(如果它是微控制器或微处理器)。
对于大多数语言,该语言未定义当“主”终止且没有操作系统可返回时发生的情况。对于C,它取决于启动文件中的内容(通常为crt0.s)。在大多数情况下,用户可以(甚至必须)提供启动代码,所以最终的答案是:您编写的是启动代码,或者指定的启动代码中发生了什么。
实际上,有3种方法:
无需采取特殊措施。未确定主要收益时会发生什么。
跳到0,或使用任何其他方式重新启动应用程序。
进入紧密循环(或禁用中断并执行暂停指令),从而永远锁定处理器。
合适的取决于应用程序。精美的贺卡和制动控制系统(仅提及两个嵌入式系统)可能应该重新启动。重新启动的不利之处在于该问题可能不会引起注意。
通常,编译后的main
代码随后与启动代码链接(它可能集成到工具链中,由芯片供应商提供,由您自己编写,等等)。
然后,链接程序将所有应用程序代码和启动代码放置在内存段中,因此对您的问题的答案取决于:1.启动代码,因为它可以例如:
bl lr
或b .
)结束,这类似于“程序结束”,但先前启用的中断和外设仍将运行,main
)结束。只需在调用main
return 之后忽略“下一步将是什么” 。
main
行为返回后程序计数器仅增加时,将取决于您的链接器(和/或链接期间使用的链接器脚本)。如果其他函数/代码放在您的后面main
,则将使用无效/未定义的参数值执行该代码,
如果启用了看门狗,尽管您处于所有无限循环中,它最终仍将重置MCU(当然,如果不重新加载它)。
停止嵌入式设备的最佳方法是永远等待NOP指令。
第二种方法是使用设备本身来关闭设备。如果可以按照说明控制继电器,则只需打开为嵌入式设备供电的开关即可,而嵌入式设备却不消耗任何电能。
手册中对此进行了明确说明。通常,CPU将引发一般异常,因为它将访问堆栈段外部的内存位置。[内存保护异常]。
您所说的嵌入式系统是什么意思?微处理器还是微控制器?无论哪种方式,都在手册中定义。
在x86 CPU中,我们通过将命令发送到ACIP控制器来关闭计算机。进入系统管理模式。因此,该控制器是I / O芯片,您无需手动将其关闭。
阅读ACPI规范以获取更多信息。