固件工程师可以从软件工程师那里学到什么?

25
根据我对固件工程工具、实践等历史知识的了解,它一直比软件工程领域落后数年。例如,据我所知,在固件世界中,仍然有相当多的争论关于是否值得在我们的应用程序中使用C++,并且一些C++编译器明显缺失(Microchip?!?)。我想这在很大程度上是由于固件和软件之间需求差异造成的。同样地,从历史上看,经过适当审查的工具和技术进入固件领域只是时间问题。
现代软件工程师经常使用的哪些方法、工具、最佳实践等,固件工程师也可以利用来改善他们的工艺呢?
具体而言,我考虑以下方面(但不要局限于此):
- 改善代码整洁性/可维护性 - 减少缺陷引入并改善检测 - 改善文档 - 需求管理 - 提高可重用性
我也很想看到嵌入式店回答或评论答案,以提供有关理论可行性或更好的个人经验反馈。
更新: 我特别感兴趣的是超前一点的东西。因此,相对较新的经过合理验证(大多数人都使用良好)的东西,如C++、TDD等。你们每天都在使用和喜欢什么?
更新2: 到目前为止,我得到了很多好的通用编程建议,这很棒,但我真正想要的是更不寻常的方法,它已被证明对人们成功。我试图找出敏捷实践者、TDDers以及那些尝试过并发现它收益丰厚或失败的人。作为一名软件工程师,在过去几年中是否有您采用的工具或实践对您产生了显着的积极或消极影响?

2
他们能学到什么?编程:p - Greg
1
松散相关:https://dev59.com/4HVC5IYBdhLWcg3woSpW - Steve Melnikoff
2
C++在微芯片上?我先从他们的C18库中实现一个malloc功能就心满意足了。 - Mark
9个回答

24

固件工程师可以从软件工程师那里学到什么?很多东西!

我惊讶于现在的固件开发和25年前我们开始使用C语言进行嵌入式开发时的实践方式有多么相似。C语言是从汇编语言迈出的一大步,但还有很多固件工程师可以并且应该从中学习的经验教训。是的,有些工具更好了,但许多做法仍停留在70年代和80年代。

嵌入式软件开发在非嵌入式开发者面临的挑战之上增加了一些额外的挑战。但是熟练的软件开发人员使用的所有原则和做法都适用于嵌入式开发。顺便说一句:不仅嵌入式软件开发人员没有掌握这些最先进的做法,许多非嵌入式软件开发人员也是如此。

我认识的和遇到的从事固件开发的人大多数都是非常有技能的人,致力于解决困难问题。不幸的是,由于某种原因,许多人没有跟上软件领域的发展。我认为这与固件工程师树立的一种想象中的障碍有关。

嵌入式和非嵌入式开发者使用不同的语言,但解决类似的问题。使嵌入式代码独立于硬件设备本质上与使应用程序代码独立于UI或数据库相同。底层原则是相同的。

以下是我认为嵌入式开发人员应该更加关注的一些事情。其中一些原则和做法可以直接使用,而其他一些可能需要进行一些调整以应对嵌入式挑战。如果你想在下面将软件替换为固件,那就去吧,我实际上不区分两者。

依赖管理

模块之间的依赖关系必须得到管理。从软件到硬件的依赖关系是一种特殊情况,嵌入式软件开发人员必须积极管理。如果您不管理依赖关系,它将管理您。

实际上,这意味着只有一小部分软件模块应了解底层硬件(和操作系统)。随着硬件的发展,投资于与硬件无关的代码可以被保留。看看我的啊哈!时刻。

Robert Martin在SOLID设计原则方面写了大量文章。嵌入式开发人员应该了解并将其应用于他们的设计中。

  • S-单一责任原则
  • O-开放封闭原则
  • L-里氏替换原则
  • I-接口隔离原则
  • D-依赖反转原则

这些原则导致更能经受时间考验的设计。 SOLID原则鼓励创建具有内聚性和独立性的模块。它们基于面向对象的思想构建,但可以应用于C语言。我们必须停止在嵌入式C代码中过于常见的函数调用数据结构自由混用。

C++和面向对象语言

为什么不能使用C++和面向对象?因为它们太慢或太大了。事实是什么?C++是一种庞大而神秘的语言,但您不必使用所有功能。看看你为什么还在使用C?

C++弥补了C语言无法很好解决的一些问题,例如:

  • 封装和信息隐藏
  • 面向接口编程
  • 可替换对象
  • 特定初始化

C++可以有效用于嵌入式开发。当然你需要一个C++编译器以及足够的空间。也许在你的世界中这是不可能的,或者这是做生意的成本。开始学习:

  • 类 - 这些是带有成员函数和成员数据的结构体。
  • 构造函数 - 这使得始终正确地进行初始化成为可能。
  • 析构函数 - 如果你学会了构造函数,那么你也必须学会析构函数来保持宇宙的平衡。
  • 继承 - 主要用于定义仅包含纯虚函数的接口。接口提供重要的依赖关系断点和灵活性点。这通常在嵌入式系统中被不公正地反对。这里应该没有任何神秘或偏见;虚函数在底层是函数指针。有效使用接口的替代方案是复杂的条件逻辑,这是嵌入式C程序通常存在过多的东西。

如果嵌入式开发人员使用C++的这些部分,他们可以构建更灵活的设计而不会产生高成本。

测试驱动开发

这可能是最大的游戏改变者。我很高兴看到其他帖子也提到了它。TDD可以帮助预防现在和未来的缺陷。要了解为什么TDD可能有所帮助,请查看TDD的物理学

Embedded做TDD确实存在一些独特的挑战。例如,TDD需要极快的增量编辑/编译/链接/运行循环。对于许多嵌入式开发者来说,这意味着需要仔细进行依赖管理,并首先在目标上运行单元测试。了解更多关于如何为嵌入式适配TDD
使用TDD,您正在创建经过彻底测试的代码。全面的自动化测试覆盖作为安全网,允许在需求变化时安全地更改代码。不良后果会立即被发现。
此外,拥有几乎免费获得的测试,使您可以毫不畏惧地重构代码...
持续重构
代码只写一次,但会被多次阅读。然后它会被修改和调整,导致设计随着时间的推移而退化。如果开发人员不断重构以保持代码清洁,它就会变成一团糟。 TDD的自动化测试可实现低成本、低风险的重构。
持续集成
自动化构建过程。每次工作区检入时运行它。由于通常需要使用异构工具集将编译代码放入目标中,因此这是一个挑战,但仍然是正确的目标。
嵌入式开发人员越早知道更改与其他工作不兼容,就可以更快地修复它,并减少在痛苦的合并中花费的时间。
增量交付

找到分割工作的方法,以避免大量痛苦的集成,并尽早尝试设计想法。避免沿着架构线分割,专注于交付可见功能切片。

协作

嵌入式开发者!走出你们的小天地一起工作。当你只看到自己的代码时,如何变得更好?当你是技术XXX的专家并掌握它时,如何在不同领域工作并提高自己的能力。

有很多东西需要学习。你会为实现最大化自我价值而负责。


谢谢James,这正是我在寻找的答案类型。希望这篇文章能为其他人提供参考。 - Gabe
4
以上列表也可以回答“一个软件工程师应该拥有哪些实践技能?”我没有在这个答案中读到任何说明为什么它特别适用于嵌入式软件工程师...除了詹姆斯的主观看法,即他认识的大多数嵌入式开发人员使用较早年代的实践。根据我的经验,以上答案列出的实践技能是常规的;唯一的例外是我所在团队明确知道我们为什么应该选择C而不是C ++。我们进行了调查、测量并得出结论,C是我们项目的更好选择。 - dwhall
很高兴听到你在做所有这些事情。尽管我很惊讶你认为这些实践是常态。我不认为这是常态。一些嵌入式开发人员在做这些实践,但是做这些实践的人远远不够多。顺便说一句:在嵌入式之外也不是常态。是的,我们处于观点的领域。我的观点基于我在嵌入式系统会议等地方找到了多少了解这些实践的人,以及多少文章和书籍描述了这些想法应用于嵌入式系统。我喜欢听到更多人在做这些事情,但在我看来,这肯定不是常态。 - James Grenning

16

我曾经担任过嵌入式软件工程师和软件开发人员的角色。在这两个领域中,我学到了一点:无论你的系统资源有多少,编程语言是什么,都有许多东西可以让你的生活变得更轻松。

第一件事是你使用的工具。在嵌入式软件中,你大部分时间只需要处理编译器/连接器。还有其他很多工具能够帮助你节省时间,例如差异工具、正则表达式、脚本语言、文档工具等。

另一个重要的方面是代码质量。我们需要遵循规范化的编码风格,定期进行重构,并且必须时刻牢记代码阅读的频率比代码编写的频率高,因此编写易读的代码非常重要。

在嵌入式软件中,有时候我们会忽略设计阶段。嵌入式项目通常不像桌面/服务器项目那样庞大,但这并不是不执行适当设计的借口。

软件需要单独测试,而不仅仅作为整个设备的一部分。为了测试软件是否符合所需的规格,构建一个系统软件模拟器真的可以节省很多时间。当整个系统,包括硬件和软件都准备好时再进行测试,则成本更高。


1
+1 for 编码规范。如果你认真对待编程,我几乎视其为理所当然。 - Eugene Yokota
“代码阅读比代码编写更常见”加1。 - Nate
+1 对于在固件和软件中工作。 - Janie

8
  • 源代码控制
  • 单元测试(TDD)
  • 持续集成(或夜间构建)
  • 缺陷跟踪

我曾经与的固件工程师没有做过这些。

对于所有类型的固件而言,单元测试可能并不适用。我想当物理硬件运行时,进行单元测试可能更加困难。这取决于是否有可用的仿真器。


5
我认为每晚构建固件可能会有点昂贵。 - MusiGenesis
2
@MusiGenesis:如果固件是你公司业务的一部分,你可以决定是否像通常的软件公司一样使用每夜构建。固件是软件。 - sharptooth
1
@MusiGenesis:我假设他们可以编译软件并在某种仿真器中运行单元测试。显然,我不是指每晚都要物理构建硬件 = :) - Simon P Stevens
每天早上在你的桌子上放一台新的iPod或手机,直接从现实打印机中取出来,这将非常酷。=) - MusiGenesis
@MusiGenesis:总有一天我们会到达那里 - http://reprap.org/bin/view/Main/WebHome - Simon P Stevens
关于单元测试:编写跨平台的代码,然后在PC上进行单元测试。 - Craig McQueen

7
假设“firmware engineers”是指“嵌入式软件工程师”,那么我的回答是:他们软件工程师,因此在可能的情况下,应该和其他软件工程师一样做相同的事情。显然,为嵌入式系统编写软件需要一些不同的技能,如对目标处理器的详细了解,以及能够处理有限的资源(与个人计算机或类似设备相比)。正如其他人所提到的,单元测试的复杂性在于必须在运行在个人计算机上的模拟器上进行,虽然非常有用,但从来不能取代对真实系统进行彻底测试 - 尤其是考虑到嵌入式系统受到异步事件的影响。我担心嵌入式软件可能看起来“落后于曲线”的原因之一是因为软件工程 - 作为其中的一部分,嵌入式软件 - 在电子工程学位中通常不会深入教授。考虑到现在电子工程师职业生涯中很大一部分时间都可能花费在编码上,这似乎是一个巨大的疏忽。

幸运的是,有些人正在努力弥补这一点。我强烈推荐阅读Jack Ganssle的文章(在他的网站上和在embedded.com上的常规专栏)。

此外,MISRA-C早已创建,旨在避免汽车行业C软件中常见的错误源,并已被许多嵌入式软件界人士采用。添加像PC-Lint这样的静态分析检查器,您已经在改善代码方面迈出了一大步。

工具供应商也没有帮助,很多情况下他们自己创建IDE,当然,也许更好的做法是集中精力于编译器和调试器,将IDE留给其他人,例如Eclipse。

顺便提一下,关于嵌入式系统中不使用C++的更多信息,请参阅 this question

最后:由于固件工程师也是软件工程师,我们面临着许多相同的问题、挑战和关注点,因此我们应该使用相同的资源;毕竟,有很多这样的资源可供使用,而您正在阅读其中之一!

我经常访问的其他网站包括:

编辑:针对Gabe的评论,“我们应该寻找哪些工具进行适应?”,有几个例子值得一提:

独立于编译器的IDE:很多C语言IDE可用,但据我所知,少数IDE在没有兼容的编译器的情况下无法发挥其全部潜力。我希望看到编译器开发人员和IDE开发人员汇聚起来,以便任何编译器都可以与任何IDE一起使用。

在没有兼容编译器的情况下,我希望能够使用PC-Lint(或等效物)作为我的“官方”编译器,并选择自己喜欢的IDE。

模拟和建模:Simulink这样的模拟工具允许对PC和一些高端嵌入式处理器的软件进行模拟、建模和测试。这样的工具对于更小的芯片同样有用,因此将它们扩展到该市场领域会很好。

PS:杰克·甘斯勒本周的专栏名为“嵌入式有何不同?”,与上述问题(宽泛地)相关。


谢谢链接,史蒂夫。我一直在寻找有关软件工程方面的见解性知识来源。我同意嵌入式软件工程师处理与软件工程世界其他领域相同的许多问题,但我认为约束条件混合真正区别于其他行业。 工具必须适应嵌入式世界,而不能仅仅是原始复制。 那么我的问题就变成了,我们应该寻找哪些工具进行适应? - Gabe
接受链接,链接到杰克关于不同领域的区别的文章和建议的工具来适应。 - Gabe

3

我不确定什么程度的软件被视为固件(BIOS、驱动程序或实用程序),但标准投诉是用户界面非标准化。认识到用户界面很重要,应该遵循一些标准或传统是好事。

就C++而言,有些人可能会犹豫进入它,因为与C相比存在很多开销。C几乎像一个具有libc的汇编语言,而在C++中,即使是一个简单的函数调用也可以是虚拟的。基于语言的复杂性,实现完整的C++运行时可能并不容易。

在软件开发的“最佳实践”方面,有太多的东西可列举了(我讨厌这个词)。为什么不从Joel测试:12步走向更好的代码开始呢?

Joel测试

  1. 您使用源代码控制吗?
  2. 您能够一步完成构建吗?
  3. 您每天都会进行构建吗?
  4. 您有错误数据库吗?
  5. 您在编写新代码之前修复错误吗?
  6. 您有最新的时间表吗?
  7. 您有规格说明书吗?
  8. 程序员是否拥有安静的工作环境?
  9. 您使用最好的工具吗?
  10. 您有测试人员吗?
  11. 新候选人在面试时是否编写代码?
  12. 您会进行走廊可用性测试吗?

关于C++,你必须清楚地知道自己在做什么,但是仔细书写,你可以在不失效率的情况下获得很多好处。我意识到列出软件开发“最佳实践”的详尽清单是不切实际的。我更关注那些相对较新的发展趋势,在嵌入式领域中可能证明有用。 - Gabe

3
让我先回答你问题中的一个假设。这个假设是嵌入式软件工程师(ESE)不知道或没有意识到现代软件工程实践,并需要学习新的实践。这个假设应该立即被摒弃。我相信你会发现,与非嵌入式SE一样,保持其技能和技术更新的ESE的统计分布相同。
因此,也许你的问题会变成一系列问题,例如:
1. 为什么ESE使用单独的代码编辑器和命令行编译器而不是IDE? 2. 为什么在大多数嵌入式项目中C比C++更受欢迎? 3. 为什么在嵌入式世界中没有那么多的编程实践实验?
以下段落回答了这些问题。
1. ESE通常使用特定的代码编辑器,因为这是个人喜好或他/她所在公司使用的工具。 IDE并不常见,因为ESE与芯片密切合作,而并非所有芯片制造商都为其系列芯片开发IDE。只有市场渗透率最高的芯片,如ARM,才有足够的动力来开发基于IDE的工具。此外,IDE对ESE提供的帮助不如对桌面开发人员提供的帮助那么多。IDE提供了帮助将GUI与代码粘合在一起或为大型API完成代码自动补全;这两个功能在嵌入式世界中都不常见或标准化。
2. 我相信有比我更好的写作说明,说明为什么在嵌入式系统中C优于C++。简短的答案是使用有效的工具。 C有效且更普遍(更多的程序员知道C而不是C ++)。另一个原因可能是OO方法论旨在通过将解决方案抽象成可管理的概念对象来帮助程序员解决大问题。在嵌入式世界中,问题通常被缩小到尽可能小的问题(和解决方案),以便通过具有较小代码库的嵌入式系统使其更可靠。
3. ESE进行的实验较少,因为总体而言,嵌入式产品必须比桌面程序更少出现错误并具有更高的可靠性。这意味着要严格执行经过充分验证的实践,并花费更多时间进行测试。为什么?因为通常没有可行的升级嵌入式设备固件的路径;这要么由于系统部署超出了范围而无法实现,要么由于更新数百万设备的成本而不切实际。
总之, ESE使用最适合他们需求的工具和实践,就像非嵌入式SE一样。作为一名实践ESE,我认为嵌入式软件学科与我的非ESE朋友所认为的迥然不同。因此,编程实践的表面差异不是ESE需要学习现代实践的问题,而是非ESE需要了解嵌入式编程有多么不同。

我认为你可能没有理解我的问题的重点。我并不是想说ESEs没有跟上他们的技能更新。我只是好奇地想听听一些成功的工具/方法等,这些SEs使用和喜爱,但还没有进入ESE世界的领域。这样做的动机是寻找可以被ESEs改编和利用的工具/方法等,以便我们可以利用SEs已经完成的广泛试验和错误。 - Gabe

3
自动化测试 不要通过目视扫描模拟输出来检查一切是否正常。你需要全面的自动化测试,因为在那些波形中你总会漏掉一些东西。
版本控制 你不会记得哪个是工作版本。使用版本控制软件,这样你就知道该用什么程序来编程板子了。
缺陷跟踪 你迟早会忘记。一个缺陷日志应包含问题首次被发现的版本(参见版本控制)和修复问题的版本。
糟糕 我以为你说的固件是指FPGA,但嵌入式软件也是如此。如果你已经有了这些流程,那太好了,否则,在把基础打好之前,请忘记“非传统方法”。

暂且假设事情已经确定下来了。我们如何做得更好? - Gabe

2
固件工程非常广泛。从PIC到DSP,它们都具有不同程度的物理资源。现在的DSP非常强大(可与5年前的CPU相比),可以支持大量的内存等等。但是,PICS只能处理几千字节。资源更加匮乏时,程序员必须使用巧妙的技巧来充分利用设备。重点是“让它运行起来”而不是“编写优雅的代码”。
我想要看到的是良好的项目管理工具,将代码和文档组合在一起,因为您必须参考大量散布在网络和电子邮件中的数据表和设计文档等。
我还希望看到更好的DSP开发工具(TI:请将CCS交给擅长制作IDE的人),以及更多使用C ++(ATEME,我在看着你)构建更好库的库制造商。
另外,希望硬件工程师更好地欣赏面向对象编程,而不是口胡“如果是C ++,就会很慢”。

2
对于更好的开发工具的评论点赞。你认为TI CCS很糟糕吗?与Microchip dsPICs编程所能获得的相比,它是一个美丽的开发环境。MPLAB很糟糕,而Microchip甚至没有提供C++编译器,只有C编译器。TI C++编译器至少对于28xx系列DSP来说非常好。但我认为他们都应该迁移到Eclipse……记住不同IDE功能很头疼,在Eclipse上重构非常棒。 - Jason S

1

这可能有点脱离上下文。

嵌入式的固件专栏中,简短提到:

我一直在嵌入式上找到了很多关于固件工程的好文章。也许对此感兴趣的人也是如此...


谢谢Nik,我也经常把它们作为资源使用。 - Gabe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接