详细的结构模式分析甚至确定了系统将如何振动,从而避免了残酷的发射阶段引起的破坏性共振。尽管他们创建的产品可以在恶劣且经常未知的环境中工作,但他们的详细计算却描述了系统的运行方式。
想想土木工程师。今天,没有人不做数学就架起一座桥梁。支撑薄薄的舞蹈之路的那条细腻的电缆只是在起作用。期。计算证明了承包商开始浇筑混凝土的时间。
飞机设计师还使用定量方法来预测性能。您最后一次听说不会飞的新飞机设计是什么时候?然而,机翼的形状很复杂,并且极难采用分析方法。在缺乏适当理论的情况下,工程师们依赖于数十年来风洞实验获得的大量表格。在弯曲金属之前,工程师仍然可以理解其产品的工作原理。
将此与我们的领域进行比较。尽管进行了数十年的研究,但是证明软件正确性的形式方法对于实际系统仍然不切实际。我们的嵌入式工程师在构建并测试之后,并没有真正的证据证明我们的产品能够正常工作。当我们选择一个CPU,时钟速度,内存大小时,我们押注我们的即席猜测就足够了,一年后,我们开始测试100,000多行代码。
经验在正确解决资源需求方面起着重要作用。通常,运气更加重要。但是,希望是我们的主要工具,而有了足够的英雄学知识,我们通常就能克服大多数挑战。
在我作为嵌入式工程师的职位上,研究了成千上万个项目,我认为仅由于使用不足的资源而造成的总失败率为10“ 15%。8051根本无法处理这些数据。PowerPC部分是一个不错的选择,但是该程序增长了两倍,是可用Flash的大小,并且采用新的成本模型,该产品不可行。
最近,我已经看到很多关于使我们的嵌入式系统更可预测的方法的文章,以确保它们对外部刺激足够快地做出反应,以确保过程按时完成。不知所措,没有实际有用的方法来计算可预测性。在大多数情况下,如果系统运行速度太慢,我们会构建系统并开始更改内容。
与航空航天和土木工程师相比,我们在黑暗中工作。异步活动改变程序流程时,很难预测行为。多任务处理和中断都会导致无法分析的问题。
USENET上的最新主题以及嵌入式系统大会上的一些讨论都建议完全禁止中断!艾格斯(Iguess)认为,这确实导致了一个更易于分析的系统,但是这个解决方案使我感到非常激进。我已经建立了轮询系统。
更糟糕的是,必须在不使用多任务的情况下或多或少地并发处理几种不同的事情。两种情况下的软件始终都是一团糟。
大约20年前,我天真地建造了没有RTOS的钢测厚仪,后来又不得不穿上鞋拔钉子。发生了太多的异步事情,内联代码变得异常复杂。我仍在设法弄清楚向圣保罗解释那个特殊的罪过 彼得
一个特别令人烦恼的问题是确保系统将及时响应外部输入。我们如何保证中断能够被足够快地识别和处理以保持系统的可靠性?
让我们详细了解第一个要求:及时识别中断。看起来很简单。翻阅处理器的数据手册,您会发现一个称为“延迟”的规范,该规范始终以亚微秒级别列出(请参见下面的图9.3)。毫无疑问,脚注将延迟定义为发生中断与CPU暂停当前处理上下文之间的最长时间。这似乎是中断响应时间,但实际上并非如此。
图9.3:延迟是从中断信号出现直到ISR开始的时间
CPU供应商定义的等待时间从零(处理器准备好立即处理中断)到指定的最大时间不等,它是正在执行哪种指令的乘积。显然,在执行指令的中间更改上下文是个坏主意,因此处理器通常要等到当前指令完成后才能对中断输入进行采样。
现在,如果它正在执行一个简单的寄存器到寄存器的移动(可能只是一个时钟周期),则零等待状态的20 MHz处理器仅需50 ns。完全没有延迟。
其他说明要慢得多。乘法可能需要数十个时钟。读-修改-写指令(例如“增量存储器”)本质上也是关键。最大延迟数来自于这些最慢的指令。
许多CPU包含循环结构,这些结构可能要花费数百毫秒的时间。例如,由一条指令启动的块内存到内存传输可能会运行很长时间,从而使等待时间变得不明显。
我知道的所有处理器都将在这些长循环中间接受一个中断,以使中断响应合理。块移动将被挂起,但是将保留足够的上下文,以允许在ISR(中断服务程序)完成时恢复传输。
因此,数据表中的等待时间数据告诉我们处理器不能处理中断的最长时间。该编号对于固件工程师完全没有用。好的,如果您要构建一个极端周期计数,纳秒级的,灰白头发诱人的系统,那么也许300 ns的延迟时间确实是系统性能的关键部分。
对于我们其他人来说,真正的延迟(即中断响应的99%组成部分)不是来自CPU的工作,而是来自我们自己的软件设计。而且,我的朋友,在设计时很难预测。没有正式的方法,我们需要经验方法来管理延迟。
如果延迟是从获取中断到进入ISR之间的时间,那么肯定会发生大多数,因为我们已禁用了中断!这是因为我们编写了织补代码。即使中断几条C语句,中断也可能长达数百微秒,远远超过CPU供应商所引用的那几纳秒。
无论您多么仔细地构建应用程序,您都将经常关闭中断。实际上,即使是从不发出“ disableinterrupt”指令的代码也确实经常禁用它们。因为,每次硬件事件发出中断请求时,处理器本身都会执行自动禁用,直到您在ISR内部显式重新启用它们后,该禁用才会生效。结果,暴涨的等待时间就可以指望了。
当然,在许多处理器上,我们不会像更改优先级那样将中断关闭。在第5级收到68 K的中断将禁止该级别及更低级别的所有中断,直到我们的代码在ISR中明确启用它们为止。优先级较高的设备仍然可以运行,但是所有1到5级设备的等待时间都是无限的,直到代码执行该操作为止。
因此,在ISR中,尽快重新启用中断。在阅读代码时,我的“经验法则”之一是在返回之前可能启用的代码可能有缺陷。
我们大多数人都被教导将中断使能推迟到ISR结束。但这会延长延迟时间,这是无法接受的。所有其他中断(至少等于或低于该优先级)都将关闭,直到ISR完成。更好的方法是,进入例程,执行所有不可重入的操作(例如处理硬件),然后启用中断。运行带有中断的ISR的其余部分,该ISR管理可重入变量等。您将减少延迟并提高系统性能。
如果该中断可以重新调用自身,则可能需要更多堆栈空间。在设计合理且可重入的ISR中,这没有任何问题,但是堆栈会不断增长,直到所有待处理的中断得到处理为止。
延迟的第二大原因是过度使用disableinterrupts指令。当两个异步活动尝试同时访问它们时,共享资源(全局变量,硬件等)将导致不稳定的崩溃。
我们需要通过使所有此类访问保持原子性或通过限制一次访问单个任务来保持代码可重入。经典方法是禁用此类访问周围的中断。尽管是一种简单的解决方案,但它以增加延迟为代价。
收集您将需要的数据
那么系统的延迟是多少?你知道吗?为什么不?令人震惊的是,我们中的许多人都以“如果愚蠢的东西行之有效,就出货”的理念来构建系统。在我看来,为了正确开发和维护产品,必须理解某些关键参数。像,有没有可用的ROM空间?系统是否已加载20%。。。还是99%?最大延迟有多糟糕?
延迟很容易测量,有时这些测量会产生令人惊讶和可怕的结果。获得中断响应感觉的最简单方法可能是,通过在例程启动时将并行输出位切换为高电平的指令来对每个ISR进行检测。退出时将其拉低。将此位连接到示波器的一个输入,将另一输入连接到中断信号本身。
这个简单的设置所提供的信息量令人叹为观止。测量从发出中断直到并行位变高的时间。那就是等待时间,减去管理仪器位的开销。调整示波器的时基以将其测量到所需的任何精度水平。
该位保持高电平的时间是ISR的总执行时间。厌倦了猜测您的代码运行速度有多快?这是定量的,便宜的和准确的。在实际系统中,中断经常发生。延迟会根据其他情况而变化。
在存储模式下使用数字示波器。断言中断输入后,您将看到一个空白空间-这是此输入的最小系统延迟。然后会有哈希值,因为仪器位在相对于中断输入的不同时间变高,因此会产生模糊。这些代表延迟的变化。当模糊将自身分解为稳定的高电平时,这就是最大延迟。
所有这些,仅需花费一个未使用的并行位。
如果您有备用计时器通道,那么还有另一种方法既不需要额外的位,也不需要范围。构建ISR仅用于测量目的,以服务于计时器的中断。
初始化时,启动计时器递增计数,将其编程为在计数溢出时中断。让它尽可能快地计数。使ISR完全简单,开销最小。用汇编语言编写代码以减少不必要的代码是一件好事。太多的Ccompiler将所有内容推送到中断处理程序中。
ISR本身读取计时器的计数寄存器,然后将数字求和成一个长变量,可能称为total_time。还增加一个计数器(迭代)。清理并返回。
这里的窍门是,尽管定时器在抛出溢出中断时读取零,但即使CPU忙于准备调用ISR,定时器寄存器也继续计数。如果系统忙于处理另一个中断,或者可能停留在禁止中断的状态,则计数器将继续递增。
没有延迟的无限快速的CPU将以计数器寄存器等于0的方式启动仪器ISR。经常发生延迟问题的实时处理器会发现计数器处于某个非零值,该值指示系统停止执行其他操作的时间。
因此,平均等待时间就是累计到总时间(标准化为微秒)中的时间除以ISR运行的次数(迭代次数)。扩展想法以提供更多信息很容易。关于中断我们可能知道的最重要的事情是最长的延迟。添加几行代码以进行比较并记录最大时间。
方法完美吗?当然不是。数据有些统计,因此可能会遗漏单点外围事件。速度非常快的处理器可能比计时器的滴答速度快得多,以至于它们总是记录为零的延迟,尽管这可能表明从所有实际目的来看,延迟都足够短而不重要。
关键是,一旦我们了解了丢失中断的延迟原因,知识就是力量。
尝试在购买的软件组件上运行这些实验。运行于100 MHz 486上的嵌入式DOS产生的延迟只有几十毫秒!